diff --git a/DEPS b/DEPS
index c8172ca..d73f36e 100644
--- a/DEPS
+++ b/DEPS
@@ -195,7 +195,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '18f6a54fc3cea32dd92a28e8808e4525c5180782',
+  'skia_revision': 'bbe69951b416913c1a0564caf82beb9e02a7dcae',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -207,7 +207,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': '1bd71b48fb5cd5bf393f730d3f542e493a1e324f',
+  'angle_revision': '0c8f66c7169c8433e5dab0c9c6a804aa02658831',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -215,7 +215,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': 'b6525ec96573078b680538b7c4e12acce59d4574',
+  'pdfium_revision': 'a163e26c830518c9a92c09c7f6011f5b517b20c1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -266,7 +266,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': '0c37bf0f193b3541eb8b0006e75d6254ecfdc217',
+  'devtools_frontend_revision': '2157bf8b002de7e11ab30a046ba6a57fafea5dbc',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -318,11 +318,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '9427c97d9c2917d66e79e49ffaa718aac2e7d6ad',
+  'dawn_revision': '948b3a05550b88519e5f92baee5292790a6ec1e1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'quiche_revision': 'edf934d71e960173dcb1f257b8d228ff6c4f9607',
+  'quiche_revision': '5c52ad843f8a33546cc17f4e7f05e6ab0df92c43',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ios_webkit
   # and whatever else without interference from each other.
@@ -545,7 +545,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '3aace45a832e030d2e7812e310ffb4488e0bf77b',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '6878fc63446902f4d78c73f4a486ad28ef6caf72',
       'condition': 'checkout_ios',
   },
 
@@ -1249,7 +1249,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '3b19b9d3347ff6b5510160f64edb34c036b241bc',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '617d3f67c3d963d2bd8b1f70cc60b660123e8153',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1285,7 +1285,7 @@
   },
 
   'src/third_party/quic_trace/src':
-    Var('chromium_git') + '/external/github.com/google/quic-trace.git' + '@' + 'a3f901c6a99e786ed0fa58a89927f630af17a90c',
+    Var('chromium_git') + '/external/github.com/google/quic-trace.git' + '@' + '2dccbe8e996d43c6d93b69e4f6d024f015e8c488',
 
   'src/third_party/pywebsocket3/src':
     Var('chromium_git') + '/external/github.com/GoogleChromeLabs/pywebsocket3.git' + '@' + 'faf478a0453ceae78f2a3bc11c6c21e13362628f',
@@ -1471,7 +1471,7 @@
   },
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'fc83cdc8198b0609f8986313ef48e565b558e8a7',
+    Var('webrtc_git') + '/src.git' + '@' + '60c25a303fac85dccb2ccdd9e8d6d71be13b7541',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1543,7 +1543,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@0ba5cb6e38f7e81e3458c333026db7aa0e18edd3',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@4bb03bb4b22dd262b712cd3a6bab6d8049f77746',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/DEPS b/android_webview/DEPS
index 51e7b8c3..872e493 100644
--- a/android_webview/DEPS
+++ b/android_webview/DEPS
@@ -36,8 +36,9 @@
   "+services/service_manager/public",
   "+services/viz/public/mojom",
   "+skia",
-  "+third_party/skia/include",
+  "+third_party/blink/public/common/web_preferences",
   "+third_party/boringssl/src/include",
+  "+third_party/skia/include",
   "+ui/android",
   "+ui/base",
   "+ui/gfx",
diff --git a/android_webview/browser/aw_content_browser_client.cc b/android_webview/browser/aw_content_browser_client.cc
index 35d04777..58f8fd2 100644
--- a/android_webview/browser/aw_content_browser_client.cc
+++ b/android_webview/browser/aw_content_browser_client.cc
@@ -85,7 +85,6 @@
 #include "content/public/common/service_names.mojom.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/common/user_agent.h"
-#include "content/public/common/web_preferences.h"
 #include "mojo/public/cpp/bindings/pending_associated_receiver.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
@@ -100,6 +99,7 @@
 #include "services/service_manager/public/cpp/binder_registry.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "third_party/blink/public/common/loader/url_loader_throttle.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "third_party/blink/public/mojom/loader/resource_load_info.mojom-shared.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/base/resource/resource_bundle_android.h"
@@ -598,7 +598,7 @@
 
 void AwContentBrowserClient::OverrideWebkitPrefs(
     content::RenderViewHost* rvh,
-    content::WebPreferences* web_prefs) {
+    blink::web_pref::WebPreferences* web_prefs) {
   AwSettings* aw_settings = AwSettings::FromWebContents(
       content::WebContents::FromRenderViewHost(rvh));
   if (aw_settings) {
diff --git a/android_webview/browser/aw_content_browser_client.h b/android_webview/browser/aw_content_browser_client.h
index 97a3e62e..363bec9d 100644
--- a/android_webview/browser/aw_content_browser_client.h
+++ b/android_webview/browser/aw_content_browser_client.h
@@ -130,7 +130,7 @@
       int child_process_id,
       content::PosixFileDescriptorInfo* mappings) override;
   void OverrideWebkitPrefs(content::RenderViewHost* rvh,
-                           content::WebPreferences* web_prefs) override;
+                           blink::web_pref::WebPreferences* web_prefs) override;
   std::vector<std::unique_ptr<content::NavigationThrottle>>
   CreateThrottlesForNavigation(
       content::NavigationHandle* navigation_handle) override;
diff --git a/android_webview/browser/aw_settings.cc b/android_webview/browser/aw_settings.cc
index c30942c2..92c26c74 100644
--- a/android_webview/browser/aw_settings.cc
+++ b/android_webview/browser/aw_settings.cc
@@ -23,15 +23,15 @@
 #include "content/public/browser/renderer_preferences_util.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/web_contents.h"
-#include "content/public/common/web_preferences.h"
 #include "net/http/http_util.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "third_party/blink/public/mojom/renderer_preferences.mojom.h"
 
 using base::android::ConvertJavaStringToUTF16;
 using base::android::ConvertUTF8ToJavaString;
 using base::android::JavaParamRef;
 using base::android::ScopedJavaLocalRef;
-using content::WebPreferences;
+using blink::web_pref::WebPreferences;
 
 namespace android_webview {
 
@@ -344,27 +344,27 @@
     render_view_host_ext->SetTextZoomFactor(text_size_percent / 100.0f);
   }
 
-  web_prefs->standard_font_family_map[content::kCommonScript] =
+  web_prefs->standard_font_family_map[blink::web_pref::kCommonScript] =
       ConvertJavaStringToUTF16(
           Java_AwSettings_getStandardFontFamilyLocked(env, obj));
 
-  web_prefs->fixed_font_family_map[content::kCommonScript] =
+  web_prefs->fixed_font_family_map[blink::web_pref::kCommonScript] =
       ConvertJavaStringToUTF16(
           Java_AwSettings_getFixedFontFamilyLocked(env, obj));
 
-  web_prefs->sans_serif_font_family_map[content::kCommonScript] =
+  web_prefs->sans_serif_font_family_map[blink::web_pref::kCommonScript] =
       ConvertJavaStringToUTF16(
           Java_AwSettings_getSansSerifFontFamilyLocked(env, obj));
 
-  web_prefs->serif_font_family_map[content::kCommonScript] =
+  web_prefs->serif_font_family_map[blink::web_pref::kCommonScript] =
       ConvertJavaStringToUTF16(
           Java_AwSettings_getSerifFontFamilyLocked(env, obj));
 
-  web_prefs->cursive_font_family_map[content::kCommonScript] =
+  web_prefs->cursive_font_family_map[blink::web_pref::kCommonScript] =
       ConvertJavaStringToUTF16(
           Java_AwSettings_getCursiveFontFamilyLocked(env, obj));
 
-  web_prefs->fantasy_font_family_map[content::kCommonScript] =
+  web_prefs->fantasy_font_family_map[blink::web_pref::kCommonScript] =
       ConvertJavaStringToUTF16(
           Java_AwSettings_getFantasyFontFamilyLocked(env, obj));
 
@@ -438,8 +438,8 @@
 
   web_prefs->autoplay_policy =
       Java_AwSettings_getMediaPlaybackRequiresUserGestureLocked(env, obj)
-          ? content::AutoplayPolicy::kUserGestureRequired
-          : content::AutoplayPolicy::kNoUserGestureRequired;
+          ? blink::web_pref::AutoplayPolicy::kUserGestureRequired
+          : blink::web_pref::AutoplayPolicy::kNoUserGestureRequired;
 
   ScopedJavaLocalRef<jstring> url =
       Java_AwSettings_getDefaultVideoPosterURLLocked(env, obj);
diff --git a/android_webview/browser/aw_settings.h b/android_webview/browser/aw_settings.h
index ffbc8c53..cd65c21 100644
--- a/android_webview/browser/aw_settings.h
+++ b/android_webview/browser/aw_settings.h
@@ -11,9 +11,11 @@
 #include "base/android/scoped_java_ref.h"
 #include "content/public/browser/web_contents_observer.h"
 
-namespace content {
+namespace blink {
+namespace web_pref {
 struct WebPreferences;
 }
+}  // namespace blink
 
 namespace android_webview {
 
@@ -83,7 +85,7 @@
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj);
 
-  void PopulateWebPreferences(content::WebPreferences* web_prefs);
+  void PopulateWebPreferences(blink::web_pref::WebPreferences* web_prefs);
   bool GetAllowFileAccess();
   bool IsDarkMode(JNIEnv* env,
                        const base::android::JavaParamRef<jobject>& obj);
diff --git a/android_webview/renderer/aw_content_settings_client.cc b/android_webview/renderer/aw_content_settings_client.cc
index a9c16b7..125b3a31 100644
--- a/android_webview/renderer/aw_content_settings_client.cc
+++ b/android_webview/renderer/aw_content_settings_client.cc
@@ -5,8 +5,8 @@
 #include "android_webview/renderer/aw_content_settings_client.h"
 
 #include "content/public/common/url_constants.h"
-#include "content/public/common/web_preferences.h"
 #include "content/public/renderer/render_frame.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "third_party/blink/public/platform/web_url.h"
 #include "third_party/blink/public/web/web_local_frame.h"
 #include "url/gurl.h"
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 1212d41..2e5a3df0 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1091,12 +1091,16 @@
     "system/palette/tools/magnifier_mode.h",
     "system/palette/tools/metalayer_mode.cc",
     "system/palette/tools/metalayer_mode.h",
+    "system/phonehub/continue_browsing_chip.cc",
+    "system/phonehub/continue_browsing_chip.h",
     "system/phonehub/phone_hub_tray.cc",
     "system/phonehub/phone_hub_tray.h",
     "system/phonehub/phone_status_view.cc",
     "system/phonehub/phone_status_view.h",
     "system/phonehub/quick_actions_view.cc",
     "system/phonehub/quick_actions_view.h",
+    "system/phonehub/task_continuation_view.cc",
+    "system/phonehub/task_continuation_view.h",
     "system/power/backlights_forced_off_setter.cc",
     "system/power/backlights_forced_off_setter.h",
     "system/power/battery_notification.cc",
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index e77c073..f7a463f4 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -972,6 +972,10 @@
       <message name="IDS_ASH_PHONE_HUB_QUICK_ACTIONS_ON_STATE" desc="Quick actions state in phone hub bubble." meaning="Quick actions feature is on.">
         On
       </message>
+      <message name="IDS_ASH_PHONE_HUB_TASK_CONTINUATION_TITLE" desc="Title of the task continuation section (where users pick up unfinished task left off from their phone, currently only support web browsing) in phone hub bubble.">
+        Continue browsing
+      </message>
+
 
       <message name="IDS_ASH_STYLUS_TOOLS_CAPTURE_REGION_ACTION" desc="Title of the capture region action in the stylus tools (a pop-up panel next to the status tray). This causes a partial screenshot to be taken (the user selects an area of the screen to take a screenshot of).">
         Capture region
diff --git a/ash/ash_strings_grd/IDS_ASH_PHONE_HUB_TASK_CONTINUATION_TITLE.png.sha1 b/ash/ash_strings_grd/IDS_ASH_PHONE_HUB_TASK_CONTINUATION_TITLE.png.sha1
new file mode 100644
index 0000000..b766c98
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_PHONE_HUB_TASK_CONTINUATION_TITLE.png.sha1
@@ -0,0 +1 @@
+1ac32cceb9bdf23779cf4be7bb55d52652062a5a
\ No newline at end of file
diff --git a/ash/power/hfp_battery_listener.cc b/ash/power/hfp_battery_listener.cc
index 97f5736..745d95f9 100644
--- a/ash/power/hfp_battery_listener.cc
+++ b/ash/power/hfp_battery_listener.cc
@@ -4,7 +4,6 @@
 
 #include "ash/power/hfp_battery_listener.h"
 
-#include "device/bluetooth/bluetooth_adapter.h"
 #include "device/bluetooth/bluetooth_device.h"
 
 namespace ash {
@@ -14,10 +13,17 @@
     : adapter_(adapter) {
   DCHECK(adapter);
   chromeos::CrasAudioHandler::Get()->AddAudioObserver(this);
+  adapter_->AddObserver(this);
+
+  // We may be late for DeviceAdded notifications. So for the already added
+  // devices, simulate DeviceAdded events.
+  for (auto* const device : adapter_->GetDevices())
+    DeviceAdded(adapter_.get(), device);
 }
 
 HfpBatteryListener::~HfpBatteryListener() {
   chromeos::CrasAudioHandler::Get()->RemoveAudioObserver(this);
+  adapter_->RemoveObserver(this);
 }
 
 void HfpBatteryListener::OnBluetoothBatteryChanged(const std::string& address,
@@ -32,4 +38,9 @@
     device->SetBatteryPercentage(level);
 }
 
+void HfpBatteryListener::DeviceAdded(device::BluetoothAdapter* adapter,
+                                     device::BluetoothDevice* device) {
+  chromeos::CrasAudioHandler::Get()->ResendBluetoothBattery();
+}
+
 }  // namespace ash
diff --git a/ash/power/hfp_battery_listener.h b/ash/power/hfp_battery_listener.h
index cdf7bff..2c3b9a3 100644
--- a/ash/power/hfp_battery_listener.h
+++ b/ash/power/hfp_battery_listener.h
@@ -9,6 +9,7 @@
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
 #include "chromeos/audio/cras_audio_handler.h"
+#include "device/bluetooth/bluetooth_adapter.h"
 
 namespace device {
 class BluetoothAdapter;
@@ -18,8 +19,10 @@
 
 // Listens to changes in battery level for devices compatible with the Hands
 // Free Profile, updating the corresponding device::BluetoothDevice.
+// TODO(b/166543531): Remove after migrated to BlueZ Battery Provider API.
 class ASH_EXPORT HfpBatteryListener
-    : public chromeos::CrasAudioHandler::AudioObserver {
+    : public chromeos::CrasAudioHandler::AudioObserver,
+      public device::BluetoothAdapter::Observer {
  public:
   explicit HfpBatteryListener(scoped_refptr<device::BluetoothAdapter> adapter);
   ~HfpBatteryListener() override;
@@ -31,6 +34,10 @@
   void OnBluetoothBatteryChanged(const std::string& address,
                                  uint32_t level) override;
 
+  // device::BluetoothAdapter::Observer:
+  void DeviceAdded(device::BluetoothAdapter* adapter,
+                   device::BluetoothDevice* device) override;
+
   scoped_refptr<device::BluetoothAdapter> adapter_;
 
   DISALLOW_COPY_AND_ASSIGN(HfpBatteryListener);
diff --git a/ash/power/hfp_battery_listener_unittest.cc b/ash/power/hfp_battery_listener_unittest.cc
index 7853cff..05f78b7 100644
--- a/ash/power/hfp_battery_listener_unittest.cc
+++ b/ash/power/hfp_battery_listener_unittest.cc
@@ -43,6 +43,10 @@
     ON_CALL(*mock_adapter_, GetDevice(kAddress))
         .WillByDefault(Return(mock_device_.get()));
 
+    // Check that HfpBatteryListener subscribes to be adapter observer.
+    EXPECT_CALL(*mock_adapter_, AddObserver(_))
+        .WillOnce(testing::SaveArg<0>(&adapter_observer_));
+
     listener_ = std::make_unique<HfpBatteryListener>(mock_adapter_);
   }
 
@@ -60,6 +64,7 @@
   scoped_refptr<NiceMock<device::MockBluetoothAdapter>> mock_adapter_;
   std::unique_ptr<device::MockBluetoothDevice> mock_device_;
   std::unique_ptr<HfpBatteryListener> listener_;
+  device::BluetoothAdapter::Observer* adapter_observer_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(HfpBatteryListenerTest);
diff --git a/ash/resources/vector_icons/BUILD.gn b/ash/resources/vector_icons/BUILD.gn
index cb21752..6aabf7d 100644
--- a/ash/resources/vector_icons/BUILD.gn
+++ b/ash/resources/vector_icons/BUILD.gn
@@ -48,6 +48,7 @@
     "dictation_on_newui.icon",
     "dogfood.icon",
     "global_media_controls.icon",
+    "holding_space.icon",
     "hollow_check_circle.icon",
     "ime_menu_emoticon.icon",
     "ime_menu_microphone.icon",
diff --git a/ash/resources/vector_icons/holding_space.icon b/ash/resources/vector_icons/holding_space.icon
new file mode 100644
index 0000000..56c370a1
--- /dev/null
+++ b/ash/resources/vector_icons/holding_space.icon
@@ -0,0 +1,46 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+CANVAS_DIMENSIONS, 20,
+MOVE_TO, 15.61f, 7.92f,
+LINE_TO, 12.27f, 11.25f,
+LINE_TO, 8.94f, 7.92f,
+LINE_TO, 10.12f, 6.74f,
+LINE_TO, 11.24f, 7.86f,
+V_LINE_TO, 5,
+H_LINE_TO, 13.24f,
+V_LINE_TO, 7.92f,
+LINE_TO, 14.43f, 6.73f,
+LINE_TO, 15.61f, 7.92f,
+CLOSE,
+MOVE_TO, 7.24f, 1,
+CUBIC_TO, 6.14f, 1, 5.24f, 1.9f, 5.24f, 3,
+V_LINE_TO, 13,
+CUBIC_TO, 5.24f, 14.1f, 6.14f, 15, 7.24f, 15,
+H_LINE_TO, 17.24f,
+CUBIC_TO, 18.35f, 15, 19.24f, 14.1f, 19.24f, 13,
+V_LINE_TO, 3,
+CUBIC_TO, 19.24f, 1.9f, 18.35f, 1, 17.24f, 1,
+H_LINE_TO, 7.24f,
+CLOSE,
+MOVE_TO, 17.24f, 3,
+H_LINE_TO, 7.24f,
+V_LINE_TO, 13,
+H_LINE_TO, 17.24f,
+V_LINE_TO, 3,
+CLOSE,
+MOVE_TO, 2.72f, 5.52f,
+LINE_TO, 4.24f, 5.31f,
+V_LINE_TO, 7.33f,
+LINE_TO, 3, 7.5f,
+LINE_TO, 4.39f, 17.4f,
+LINE_TO, 14.29f, 16.01f,
+LINE_TO, 14.29f, 16,
+H_LINE_TO, 16.29f,
+CUBIC_TO, 16.3f, 16.99f, 15.58f, 17.85f, 14.57f, 17.99f,
+LINE_TO, 4.67f, 19.39f,
+CUBIC_TO, 3.58f, 19.54f, 2.57f, 18.78f, 2.41f, 17.68f,
+LINE_TO, 1.02f, 7.78f,
+CUBIC_TO, 0.87f, 6.69f, 1.63f, 5.68f, 2.72f, 5.52f,
+CLOSE
diff --git a/ash/system/holding_space/holding_space_tray.cc b/ash/system/holding_space/holding_space_tray.cc
index a7dff6a..0bfa8cec 100644
--- a/ash/system/holding_space/holding_space_tray.cc
+++ b/ash/system/holding_space/holding_space_tray.cc
@@ -56,7 +56,7 @@
   icon_->set_tooltip_text(
       l10n_util::GetStringUTF16(IDS_ASH_HOLDING_SPACE_SCREENSHOTS_TITLE));
 
-  icon_->SetImage(CreateVectorIcon(kSystemMenuArrowBackIcon,
+  icon_->SetImage(CreateVectorIcon(kHoldingSpaceIcon,
                                    ShelfConfig::Get()->shelf_icon_color()));
 
   tray_container()->SetMargin(kTrayIconMainAxisInset, 0);
diff --git a/ash/system/phonehub/continue_browsing_chip.cc b/ash/system/phonehub/continue_browsing_chip.cc
new file mode 100644
index 0000000..9700982
--- /dev/null
+++ b/ash/system/phonehub/continue_browsing_chip.cc
@@ -0,0 +1,64 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/phonehub/continue_browsing_chip.h"
+#include "ash/strings/grit/ash_strings.h"
+#include "ash/style/ash_color_provider.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/layout/box_layout.h"
+
+namespace ash {
+
+namespace {
+
+constexpr gfx::Insets kContinueBrowsingChipPadding(8, 8);
+constexpr int kTaskContinuationChipRadius = 10;
+
+}  // namespace
+
+ContinueBrowsingChip::ContinueBrowsingChip() : views::Button(this) {
+  auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
+      views::BoxLayout::Orientation::kVertical, kContinueBrowsingChipPadding));
+  layout->set_cross_axis_alignment(
+      views::BoxLayout::CrossAxisAlignment::kStart);
+
+  // TODO(leandre): Update chip with phone data and updated design.
+  auto* header_label = AddChildView(std::make_unique<views::Label>(
+      l10n_util::GetStringUTF16(IDS_ASH_PHONE_HUB_TASK_CONTINUATION_TITLE)));
+  header_label->SetAutoColorReadabilityEnabled(false);
+  header_label->SetSubpixelRenderingEnabled(false);
+  header_label->SetEnabledColor(AshColorProvider::Get()->GetContentLayerColor(
+      AshColorProvider::ContentLayerType::kTextColorPrimary));
+
+  auto* sub_label = AddChildView(std::make_unique<views::Label>(
+      l10n_util::GetStringUTF16(IDS_ASH_PHONE_HUB_TASK_CONTINUATION_TITLE)));
+  sub_label->SetAutoColorReadabilityEnabled(false);
+  sub_label->SetSubpixelRenderingEnabled(false);
+  sub_label->SetEnabledColor(AshColorProvider::Get()->GetContentLayerColor(
+      AshColorProvider::ContentLayerType::kTextColorSecondary));
+}
+
+void ContinueBrowsingChip::OnPaintBackground(gfx::Canvas* canvas) {
+  cc::PaintFlags flags;
+  flags.setAntiAlias(true);
+  flags.setColor(AshColorProvider::Get()->GetControlsLayerColor(
+      AshColorProvider::ControlsLayerType::kControlBackgroundColorInactive));
+  gfx::Rect bounds = GetContentsBounds();
+  canvas->DrawRoundRect(bounds, kTaskContinuationChipRadius, flags);
+  views::View::OnPaintBackground(canvas);
+}
+
+void ContinueBrowsingChip::ButtonPressed(views::Button* sender,
+                                         const ui::Event& event) {
+  // TODO(leandre): Open browser when button pressed.
+}
+
+ContinueBrowsingChip::~ContinueBrowsingChip() = default;
+
+const char* ContinueBrowsingChip::GetClassName() const {
+  return "ContinueBrowsingChip";
+}
+
+}  // namespace ash
diff --git a/ash/system/phonehub/continue_browsing_chip.h b/ash/system/phonehub/continue_browsing_chip.h
new file mode 100644
index 0000000..6118e13
--- /dev/null
+++ b/ash/system/phonehub/continue_browsing_chip.h
@@ -0,0 +1,32 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_SYSTEM_PHONEHUB_CONTINUE_BROWSING_CHIP_H_
+#define ASH_SYSTEM_PHONEHUB_CONTINUE_BROWSING_CHIP_H_
+
+#include "ash/ash_export.h"
+#include "ui/gfx/canvas.h"
+#include "ui/views/controls/button/button.h"
+
+namespace ash {
+
+// A chip containing a web page info (title, web URL, etc.) that users left off
+// from their phone.
+class ASH_EXPORT ContinueBrowsingChip : public views::Button,
+                                        public views::ButtonListener {
+ public:
+  ContinueBrowsingChip();
+  ~ContinueBrowsingChip() override;
+  ContinueBrowsingChip(ContinueBrowsingChip&) = delete;
+  ContinueBrowsingChip operator=(ContinueBrowsingChip&) = delete;
+
+  // views::ButtonListener:
+  void OnPaintBackground(gfx::Canvas* canvas) override;
+  void ButtonPressed(views::Button* sender, const ui::Event& event) override;
+  const char* GetClassName() const override;
+};
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_PHONEHUB_CONTINUE_BROWSING_CHIP_H_
diff --git a/ash/system/phonehub/phone_hub_tray.cc b/ash/system/phonehub/phone_hub_tray.cc
index 2620c5d..e133aa2 100644
--- a/ash/system/phonehub/phone_hub_tray.cc
+++ b/ash/system/phonehub/phone_hub_tray.cc
@@ -12,6 +12,7 @@
 #include "ash/style/ash_color_provider.h"
 #include "ash/system/phonehub/phone_status_view.h"
 #include "ash/system/phonehub/quick_actions_view.h"
+#include "ash/system/phonehub/task_continuation_view.h"
 #include "ash/system/tray/system_menu_button.h"
 #include "ash/system/tray/tray_bubble_wrapper.h"
 #include "ash/system/tray/tray_constants.h"
@@ -45,31 +46,45 @@
 // such as phone status, task continuation, etc.
 class PhoneHubView : public views ::View {
  public:
-  explicit PhoneHubView(TrayBubbleView* bubble_view) {
+  explicit PhoneHubView(TrayBubbleView* bubble_view)
+      : bubble_view_(bubble_view) {
     auto setup_layered_view = [](views::View* view) {
       view->SetPaintToLayer();
       view->layer()->SetFillsBoundsOpaquely(false);
     };
 
     setup_layered_view(
-        bubble_view->AddChildView(std::make_unique<PhoneStatusView>()));
+        bubble_view_->AddChildView(std::make_unique<PhoneStatusView>()));
 
-    auto* separator =
-        bubble_view->AddChildView(std::make_unique<views::Separator>());
-    setup_layered_view(separator);
-    separator->SetColor(AshColorProvider::Get()->GetContentLayerColor(
-        AshColorProvider::ContentLayerType::kSeparatorColor));
-    separator->SetBorder(views::CreateEmptyBorder(
-        gfx::Insets(kPaddingBetweenTitleAndSeparator, 0,
-                    kMenuSeparatorVerticalPadding, 0)));
+    AddSeparator();
 
     setup_layered_view(
-        bubble_view->AddChildView(std::make_unique<QuickActionsView>()));
+        bubble_view_->AddChildView(std::make_unique<QuickActionsView>()));
+
+    AddSeparator();
+
+    setup_layered_view(
+        bubble_view_->AddChildView(std::make_unique<TaskContinuationView>()));
   }
   ~PhoneHubView() override = default;
 
   // views::View:
   const char* GetClassName() const override { return "PhoneHubView"; }
+
+ private:
+  void AddSeparator() {
+    auto* separator =
+        bubble_view_->AddChildView(std::make_unique<views::Separator>());
+    separator->SetPaintToLayer();
+    separator->layer()->SetFillsBoundsOpaquely(false);
+    separator->SetColor(AshColorProvider::Get()->GetContentLayerColor(
+        AshColorProvider::ContentLayerType::kSeparatorColor));
+    separator->SetBorder(views::CreateEmptyBorder(
+        gfx::Insets(kPaddingBetweenTitleAndSeparator, 0,
+                    kMenuSeparatorVerticalPadding, 0)));
+  }
+
+  TrayBubbleView* bubble_view_ = nullptr;
 };
 
 }  // namespace
diff --git a/ash/system/phonehub/task_continuation_view.cc b/ash/system/phonehub/task_continuation_view.cc
new file mode 100644
index 0000000..c15043c
--- /dev/null
+++ b/ash/system/phonehub/task_continuation_view.cc
@@ -0,0 +1,136 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/phonehub/task_continuation_view.h"
+
+#include "ash/strings/grit/ash_strings.h"
+#include "ash/style/ash_color_provider.h"
+#include "ash/system/phonehub/continue_browsing_chip.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/geometry/insets.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/layout/box_layout.h"
+#include "ui/views/view_model.h"
+
+namespace ash {
+
+namespace {
+
+constexpr int kTaskContinuationHeaderSpacing = 8;
+constexpr gfx::Insets kTaskContinuationViewPadding(12, 4);
+constexpr gfx::Insets kPhoneHubSubHeaderPadding(4, 32);
+constexpr gfx::Size kTaskContinuationChipSize(170, 50);
+constexpr int kTaskContinuationChipsInRow = 2;
+constexpr int kTaskContinuationChipSpacing = 8;
+constexpr int kTaskContinuationChipVerticalPadding = 5;
+
+class HeaderView : public views::View {
+ public:
+  HeaderView() {
+    SetLayoutManager(std::make_unique<views::BoxLayout>(
+        views::BoxLayout::Orientation::kVertical, kPhoneHubSubHeaderPadding));
+    auto* header_label = AddChildView(std::make_unique<views::Label>(
+        l10n_util::GetStringUTF16(IDS_ASH_PHONE_HUB_TASK_CONTINUATION_TITLE)));
+    header_label->SetAutoColorReadabilityEnabled(false);
+    header_label->SetSubpixelRenderingEnabled(false);
+    header_label->SetEnabledColor(AshColorProvider::Get()->GetContentLayerColor(
+        AshColorProvider::ContentLayerType::kTextColorPrimary));
+  }
+
+  ~HeaderView() override = default;
+  HeaderView(HeaderView&) = delete;
+  HeaderView operator=(HeaderView&) = delete;
+
+  // views::View:
+  const char* GetClassName() const override { return "HeaderView"; }
+};
+
+class ChipsView : public views::View {
+ public:
+  ChipsView() {
+    // TODO(leandre): Add task chip to bubble using real data from phone.
+    AddTaskChip(new ContinueBrowsingChip());
+    AddTaskChip(new ContinueBrowsingChip());
+    AddTaskChip(new ContinueBrowsingChip());
+    AddTaskChip(new ContinueBrowsingChip());
+  }
+
+  ~ChipsView() override = default;
+  ChipsView(ChipsView&) = delete;
+  ChipsView operator=(ChipsView&) = delete;
+
+  // views::View:
+  gfx::Size CalculatePreferredSize() const override {
+    int width =
+        kTaskContinuationChipSize.width() * kTaskContinuationChipsInRow +
+        kTaskContinuationChipSpacing;
+    int rows_num = std::ceil(
+        (double)(task_chips_.view_size() / kTaskContinuationChipsInRow));
+    int height = (kTaskContinuationChipSize.height() +
+                  kTaskContinuationChipVerticalPadding) *
+                     std::max(0, rows_num - 1) +
+                 kTaskContinuationChipSize.height();
+    return gfx::Size(width, height);
+  }
+
+  void Layout() override {
+    views::View::Layout();
+    CalculateIdealBounds();
+    for (int i = 0; i < task_chips_.view_size(); ++i) {
+      auto* button = task_chips_.view_at(i);
+      button->SetBoundsRect(task_chips_.ideal_bounds(i));
+    }
+  }
+
+  const char* GetClassName() const override { return "ChipsView"; }
+
+ private:
+  gfx::Point GetButtonPosition(int index) {
+    int row = index / kTaskContinuationChipsInRow;
+    int column = index % kTaskContinuationChipsInRow;
+    int x = (kTaskContinuationChipSize.width() + kTaskContinuationChipSpacing) *
+            column;
+    int y = (kTaskContinuationChipSize.height() +
+             kTaskContinuationChipVerticalPadding) *
+            row;
+    return gfx::Point(x, y);
+  }
+
+  void CalculateIdealBounds() {
+    for (int i = 0; i < task_chips_.view_size(); ++i) {
+      gfx::Rect tile_bounds =
+          gfx::Rect(GetButtonPosition(i), kTaskContinuationChipSize);
+      task_chips_.set_ideal_bounds(i, tile_bounds);
+    }
+  }
+
+  void AddTaskChip(views::View* task_chip) {
+    int view_size = task_chips_.view_size();
+    task_chips_.Add(task_chip, view_size);
+    AddChildView(task_chip);
+  }
+
+  views::ViewModelT<views::View> task_chips_;
+};
+
+}  // namespace
+
+TaskContinuationView::TaskContinuationView() {
+  auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
+      views::BoxLayout::Orientation::kVertical, kTaskContinuationViewPadding,
+      kTaskContinuationHeaderSpacing));
+  layout->set_cross_axis_alignment(
+      views::BoxLayout::CrossAxisAlignment::kStart);
+
+  AddChildView(std::make_unique<HeaderView>());
+  AddChildView(std::make_unique<ChipsView>());
+}
+
+TaskContinuationView::~TaskContinuationView() = default;
+
+const char* TaskContinuationView::GetClassName() const {
+  return "TaskContinuationView";
+}
+
+}  // namespace ash
diff --git a/ash/system/phonehub/task_continuation_view.h b/ash/system/phonehub/task_continuation_view.h
new file mode 100644
index 0000000..ac9dbca
--- /dev/null
+++ b/ash/system/phonehub/task_continuation_view.h
@@ -0,0 +1,28 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_SYSTEM_PHONEHUB_TASK_CONTINUATION_VIEW_H_
+#define ASH_SYSTEM_PHONEHUB_TASK_CONTINUATION_VIEW_H_
+
+#include "ash/ash_export.h"
+#include "ui/views/view.h"
+
+namespace ash {
+
+// A view in Phone Hub bubble that allows user to pick up unfinished task left
+// off from their phone, currently only support web browsing.
+class ASH_EXPORT TaskContinuationView : public views::View {
+ public:
+  TaskContinuationView();
+  ~TaskContinuationView() override;
+  TaskContinuationView(TaskContinuationView&) = delete;
+  TaskContinuationView operator=(TaskContinuationView&) = delete;
+
+  // views::View:
+  const char* GetClassName() const override;
+};
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_PHONEHUB_TASK_CONTINUATION_VIEW_H_
diff --git a/ash/wm/overview/overview_grid.cc b/ash/wm/overview/overview_grid.cc
index 02caf25..0f12f1f 100644
--- a/ash/wm/overview/overview_grid.cc
+++ b/ash/wm/overview/overview_grid.cc
@@ -60,7 +60,6 @@
 #include "ui/compositor/throughput_tracker.h"
 #include "ui/gfx/geometry/vector2d_f.h"
 #include "ui/gfx/transform_util.h"
-#include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/view.h"
 #include "ui/views/widget/widget.h"
 #include "ui/wm/core/coordinate_conversion.h"
@@ -338,62 +337,6 @@
   DISALLOW_COPY_AND_ASSIGN(TargetWindowObserver);
 };
 
-// Class that updates the focusable overview widgets so that the point to the
-// correct next and previous widgets for a11y purposes. Needs to be updated when
-// an overview item is added or removed. It is expected that the desk widget
-// does not get altered for the duration of overview.
-class OverviewGrid::AccessibilityFocusAnnotator {
- public:
-  explicit AccessibilityFocusAnnotator(OverviewGrid* grid) : grid_(grid) {}
-  AccessibilityFocusAnnotator(const AccessibilityFocusAnnotator&) = delete;
-  AccessibilityFocusAnnotator& operator=(const AccessibilityFocusAnnotator&) =
-      delete;
-  ~AccessibilityFocusAnnotator() = default;
-
-  void UpdateAccessibilityFocus() {
-    // Construct the list of accessible widgets, these are the desk bar widget
-    // and all the item widgets of each item on this grid.
-    std::vector<views::Widget*> a11y_widgets;
-    if (grid_->desks_widget())
-      a11y_widgets.push_back(const_cast<views::Widget*>(grid_->desks_widget()));
-    for (const auto& item : grid_->window_list())
-      a11y_widgets.push_back(item->item_widget());
-
-    if (a11y_widgets.empty())
-      return;
-
-    auto get_view_a11y =
-        [&a11y_widgets](int index) -> views::ViewAccessibility& {
-      return a11y_widgets[index]->GetContentsView()->GetViewAccessibility();
-    };
-
-    // If there is only one widget left, clear the focus overrides so that they
-    // do not point to deleted objects.
-    if (a11y_widgets.size() == 1) {
-      get_view_a11y(/*index=*/0).OverridePreviousFocus(nullptr);
-      get_view_a11y(/*index=*/0).OverrideNextFocus(nullptr);
-      a11y_widgets[0]->GetContentsView()->NotifyAccessibilityEvent(
-          ax::mojom::Event::kTreeChanged, true);
-      return;
-    }
-
-    int size = a11y_widgets.size();
-    for (int i = 0; i < size; ++i) {
-      int previous_index = (i + size - 1) % size;
-      int next_index = (i + 1) % size;
-      get_view_a11y(i).OverridePreviousFocus(a11y_widgets[previous_index]);
-      get_view_a11y(i).OverrideNextFocus(a11y_widgets[next_index]);
-      a11y_widgets[i]->GetContentsView()->NotifyAccessibilityEvent(
-          ax::mojom::Event::kTreeChanged, true);
-    }
-  }
-
- private:
-  // The associated overview grid. Guaranteed to be non null for the duration of
-  // |this|.
-  OverviewGrid* grid_;
-};
-
 OverviewGrid::OverviewGrid(aura::Window* root_window,
                            const std::vector<aura::Window*>& windows,
                            OverviewSession* overview_session)
@@ -624,9 +567,6 @@
   }
   if (reposition)
     PositionWindows(animate, ignored_items);
-
-  if (accessibility_focus_annotator_)
-    accessibility_focus_annotator_->UpdateAccessibilityFocus();
 }
 
 void OverviewGrid::AppendItem(aura::Window* window,
@@ -668,16 +608,6 @@
   // iterating through the |window_list_|.
   std::unique_ptr<OverviewItem> tmp = std::move(*iter);
   window_list_.erase(std::next(iter).base());
-
-  // This can also be called when shutting down |this|, at which the item will
-  // be cleaning up and its associated view may be nullptr. |overview_item|
-  // needs to be removed from |window_list_| so we recompute the accessibility
-  // widget pointers.
-  if (overview_session_ && accessibility_focus_annotator_ &&
-      tmp->overview_item_view()) {
-    accessibility_focus_annotator_->UpdateAccessibilityFocus();
-  }
-
   tmp.reset();
 
   UpdateFrameThrottling();
@@ -1082,9 +1012,6 @@
   for (auto& window : window_list())
     window->OnStartingAnimationComplete();
 
-  accessibility_focus_annotator_ =
-      std::make_unique<AccessibilityFocusAnnotator>(this);
-  accessibility_focus_annotator_->UpdateAccessibilityFocus();
 }
 
 void OverviewGrid::CalculateWindowListAnimationStates(
diff --git a/ash/wm/overview/overview_grid.h b/ash/wm/overview/overview_grid.h
index f9b46ea2f..955ffe2 100644
--- a/ash/wm/overview/overview_grid.h
+++ b/ash/wm/overview/overview_grid.h
@@ -102,6 +102,9 @@
   // will be adjusted after the animation. If |restack| is true but at least one
   // of |reposition| and |animate| is false, the stacking order will be adjusted
   // immediately.
+  // Note: OverviewSession has versions of the Add/Remove items. Those are
+  // preferred as they update other things like the overview accessibility
+  // annotator.
   void AddItem(aura::Window* window,
                bool reposition,
                bool animate,
@@ -374,7 +377,6 @@
   }
 
  private:
-  class AccessibilityFocusAnnotator;
   class TargetWindowObserver;
   friend class OverviewSessionTest;
 
@@ -521,10 +523,6 @@
   // Records the presentation time of scrolling the grid in overview mode.
   std::unique_ptr<PresentationTimeRecorder> presentation_time_recorder_;
 
-  // Updates accessibility with the correct focus order among all overview
-  // widgets.
-  std::unique_ptr<AccessibilityFocusAnnotator> accessibility_focus_annotator_;
-
   // Weak pointer to the window that is being dragged from the top, if there is
   // one.
   aura::Window* dragged_window_ = nullptr;
diff --git a/ash/wm/overview/overview_item.cc b/ash/wm/overview/overview_item.cc
index d5180f57..31b3edac 100644
--- a/ash/wm/overview/overview_item.cc
+++ b/ash/wm/overview/overview_item.cc
@@ -1050,8 +1050,16 @@
     overview_session_->window_drag_controller()->ResetGesture();
   }
 
-  overview_grid_->RemoveItem(this, /*item_destroying=*/true,
-                             /*reposition=*/!animating_to_close_);
+  // Remove the item from the session which will remove it from the grid in
+  // addition to updating accessibility states. If the session is not available
+  // then remove it from the grid directly.
+  if (overview_session_) {
+    overview_session_->RemoveItem(this, /*item_destroying=*/true,
+                                  /*reposition=*/!animating_to_close_);
+  } else {
+    overview_grid()->RemoveItem(this, /*item_destroying=*/true,
+                                /*reposition=*/!animating_to_close_);
+  }
 
   Shell::Get()
       ->accessibility_controller()
diff --git a/ash/wm/overview/overview_item_view.cc b/ash/wm/overview/overview_item_view.cc
index 597a4c5..455da9f 100644
--- a/ash/wm/overview/overview_item_view.cc
+++ b/ash/wm/overview/overview_item_view.cc
@@ -436,6 +436,10 @@
 
 void OverviewItemView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
   WindowMiniView::GetAccessibleNodeData(node_data);
+
+  // TODO: This doesn't allow |this| to be navigated by ChromeVox, find a way
+  // to allow |this| as well as the title and close button.
+  node_data->role = ax::mojom::Role::kGenericContainer;
   node_data->AddStringAttribute(
       ax::mojom::StringAttribute::kDescription,
       l10n_util::GetStringUTF8(
diff --git a/ash/wm/overview/overview_session.cc b/ash/wm/overview/overview_session.cc
index 937ae190..b290222 100644
--- a/ash/wm/overview/overview_session.cc
+++ b/ash/wm/overview/overview_session.cc
@@ -49,6 +49,7 @@
 #include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/display/screen.h"
 #include "ui/events/event.h"
+#include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/widget/widget.h"
 #include "ui/wm/core/coordinate_conversion.h"
 
@@ -77,8 +78,6 @@
   return Shell::Get()->overview_controller()->EndOverview();
 }
 
-}  // namespace
-
 // A self-deleting window state observer that runs the given callback when its
 // associated window state has been changed.
 class AsyncWindowStateChangeObserver : public WindowStateObserver,
@@ -123,6 +122,93 @@
   base::OnceCallback<void(WindowState*)> on_post_window_state_changed_;
 };
 
+// Simple override of views::Button. Allows to use a element of accessibility
+// role button as the overview focus widget's contents.
+class OverviewFocusButton : public views::Button {
+ public:
+  OverviewFocusButton() : views::Button(/*listener=*/nullptr) {
+    // Make this not focusable to avoid accessibility error since this view has
+    // no accessible name.
+    SetFocusBehavior(FocusBehavior::NEVER);
+  }
+  OverviewFocusButton(const OverviewFocusButton&) = delete;
+  OverviewFocusButton& operator=(const OverviewFocusButton&) = delete;
+  ~OverviewFocusButton() override = default;
+};
+
+}  // namespace
+
+// Class that updates the focusable overview widgets so that the point to the
+// correct next and previous widgets for a11y purposes. Needs to be updated when
+// an overview item is added or removed. It is expected that the desk widget
+// does not get altered for the duration of overview.
+class OverviewSession::AccessibilityFocusAnnotator {
+ public:
+  explicit AccessibilityFocusAnnotator(OverviewSession* session)
+      : session_(session) {}
+  AccessibilityFocusAnnotator(const AccessibilityFocusAnnotator&) = delete;
+  AccessibilityFocusAnnotator& operator=(const AccessibilityFocusAnnotator&) =
+      delete;
+  ~AccessibilityFocusAnnotator() = default;
+
+  void UpdateAccessibilityFocus() {
+    if (session_->is_shutting_down())
+      return;
+
+    // Construct the list of accessible widgets, these are the overview focus
+    // widget, desk bar widget, all the item widgets and the no window indicator
+    // widget, if available.
+    std::vector<views::Widget*> a11y_widgets;
+    if (session_->overview_focus_widget_)
+      a11y_widgets.push_back(session_->overview_focus_widget_.get());
+
+    for (aura::Window* root : Shell::GetAllRootWindows()) {
+      OverviewGrid* grid = session_->GetGridWithRootWindow(root);
+      DCHECK(grid);
+      if (grid->desks_widget())
+        a11y_widgets.push_back(
+            const_cast<views::Widget*>(grid->desks_widget()));
+      for (const auto& item : grid->window_list())
+        a11y_widgets.push_back(item->item_widget());
+    }
+    if (session_->no_windows_widget_.get())
+      a11y_widgets.push_back(session_->no_windows_widget_.get());
+
+    if (a11y_widgets.empty())
+      return;
+
+    auto get_view_a11y =
+        [&a11y_widgets](int index) -> views::ViewAccessibility& {
+      return a11y_widgets[index]->GetContentsView()->GetViewAccessibility();
+    };
+
+    // If there is only one widget left, clear the focus overrides so that they
+    // do not point to deleted objects.
+    if (a11y_widgets.size() == 1) {
+      get_view_a11y(/*index=*/0).OverridePreviousFocus(nullptr);
+      get_view_a11y(/*index=*/0).OverrideNextFocus(nullptr);
+      a11y_widgets[0]->GetContentsView()->NotifyAccessibilityEvent(
+          ax::mojom::Event::kTreeChanged, true);
+      return;
+    }
+
+    int size = a11y_widgets.size();
+    for (int i = 0; i < size; ++i) {
+      int previous_index = (i + size - 1) % size;
+      int next_index = (i + 1) % size;
+      get_view_a11y(i).OverridePreviousFocus(a11y_widgets[previous_index]);
+      get_view_a11y(i).OverrideNextFocus(a11y_widgets[next_index]);
+      a11y_widgets[i]->GetContentsView()->NotifyAccessibilityEvent(
+          ax::mojom::Event::kTreeChanged, true);
+    }
+  }
+
+ private:
+  // The associated overview session. Guaranteed to be non null for the lifetime
+  // of |this|.
+  OverviewSession* session_;
+};
+
 OverviewSession::OverviewSession(OverviewDelegate* delegate)
     : delegate_(delegate),
       restore_focus_window_(window_util::GetFocusedWindow()),
@@ -217,7 +303,9 @@
   UpdateNoWindowsWidget();
 
   // Create the widget that will receive focus while in overview mode for
-  // accessibility purposes.
+  // accessibility purposes. Add a button as the contents so that
+  // |accessibility_focus_annotator_| can put it on the accessibility focus
+  // cycler.
   overview_focus_widget_ = std::make_unique<views::Widget>();
   views::Widget::InitParams params;
   params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS;
@@ -226,10 +314,12 @@
   params.accept_events = false;
   params.bounds = gfx::Rect(0, 0, 2, 2);
   params.layer_type = ui::LAYER_NOT_DRAWN;
-  params.name = "OverviewModeFocusedWidget";
+  params.name = "OverviewModeFocusWidget";
   params.z_order = ui::ZOrderLevel::kFloatingWindow;
   params.init_properties_container.SetProperty(ash::kExcludeInMruKey, true);
   overview_focus_widget_->Init(std::move(params));
+  overview_focus_widget_->SetContentsView(
+      std::make_unique<OverviewFocusButton>());
 
   UMA_HISTOGRAM_COUNTS_100("Ash.WindowSelector.Items", num_items_);
 
@@ -499,6 +589,12 @@
 }
 
 void OverviewSession::RemoveItem(OverviewItem* overview_item) {
+  RemoveItem(overview_item, /*item_destroying=*/false, /*reposition=*/false);
+}
+
+void OverviewSession::RemoveItem(OverviewItem* overview_item,
+                                 bool item_destroying,
+                                 bool reposition) {
   if (overview_item->GetWindow()->HasObserver(this)) {
     overview_item->GetWindow()->RemoveObserver(this);
     observed_windows_.erase(overview_item->GetWindow());
@@ -506,11 +602,14 @@
       restore_focus_window_ = nullptr;
   }
 
-  overview_item->overview_grid()->RemoveItem(
-      overview_item, /*item_destroying=*/false, /*reposition=*/false);
+  overview_item->overview_grid()->RemoveItem(overview_item, item_destroying,
+                                             reposition);
   --num_items_;
 
   UpdateNoWindowsWidget();
+
+  if (accessibility_focus_annotator_)
+    accessibility_focus_annotator_->UpdateAccessibilityFocus();
 }
 
 void OverviewSession::RemoveDropTargets() {
@@ -709,6 +808,7 @@
 
   if (canceled)
     return;
+
   if (overview_focus_widget_) {
     if (should_focus_overview) {
       overview_focus_widget_->Show();
@@ -729,6 +829,11 @@
       }
     }
   }
+
+  accessibility_focus_annotator_ =
+      std::make_unique<AccessibilityFocusAnnotator>(this);
+  accessibility_focus_annotator_->UpdateAccessibilityFocus();
+
   Shell::Get()->overview_controller()->DelayedUpdateRoundedCornersAndShadow();
 }
 
@@ -1225,6 +1330,9 @@
   // called before OnStartingAnimationComplete() is called, so use Show()
   // instead of ActivateWindow() to show and activate the widget.
   overview_focus_widget_->Show();
+
+  if (accessibility_focus_annotator_)
+    accessibility_focus_annotator_->UpdateAccessibilityFocus();
 }
 
 }  // namespace ash
diff --git a/ash/wm/overview/overview_session.h b/ash/wm/overview/overview_session.h
index 72ba560..5098a8a 100644
--- a/ash/wm/overview/overview_session.h
+++ b/ash/wm/overview/overview_session.h
@@ -141,9 +141,11 @@
                          bool animate,
                          bool restack);
 
-  // Removes |overview_item| from the corresponding grid. No items are
-  // repositioned.
+  // Removes |overview_item| from the corresponding grid.
   void RemoveItem(OverviewItem* overview_item);
+  void RemoveItem(OverviewItem* overview_item,
+                  bool item_destroying,
+                  bool reposition);
 
   void RemoveDropTargets();
 
@@ -330,9 +332,10 @@
  private:
   friend class DesksAcceleratorsTest;
   friend class OverviewSessionTest;
+  class AccessibilityFocusAnnotator;
 
-  // Helper function that moves the selection widget to forward or backward on
-  // the corresponding window grid.
+  // Helper function that moves the highlight forward or backward on the
+  // corresponding window grid.
   void Move(bool reverse);
 
   // Helper function that processes a key event and maybe scrolls the overview
@@ -356,19 +359,17 @@
   // is made.
   OverviewDelegate* delegate_;
 
-  // A weak pointer to the window which was focused on beginning window
-  // selection. If window selection is canceled the focus should be restored to
-  // this window.
+  // A weak pointer to the window which was focused on starting overview. If
+  // overview is canceled the focus should be restored to this window.
   aura::Window* restore_focus_window_ = nullptr;
 
   // A hidden window that receives focus while in overview mode. It is needed
   // because accessibility needs something focused for it to work and we cannot
   // use one of the overview windows otherwise wm::ActivateWindow will not
   // work.
-  // TODO(sammiequon): Investigate if we can focus the |selection_widget_| in
-  // OverviewGrid when it is created, or if we can focus a widget from the
-  // virtual desks UI when that is complete, or we may be able to add some
-  // mechanism to trigger accessibility events without a focused window.
+  // TODO(sammiequon): Focus the grid desks widget if it is always available, or
+  // we may be able to add some mechanism to trigger accessibility events
+  // without a focused window.
   std::unique_ptr<views::Widget> overview_focus_widget_;
 
   // A widget that is shown if we entered overview without any windows opened.
@@ -416,6 +417,10 @@
 
   std::unique_ptr<OverviewHighlightController> highlight_controller_;
 
+  // Updates accessibility with the correct focus order among all overview
+  // widgets.
+  std::unique_ptr<AccessibilityFocusAnnotator> accessibility_focus_annotator_;
+
   DISALLOW_COPY_AND_ASSIGN(OverviewSession);
 };
 
diff --git a/ash/wm/overview/overview_session_unittest.cc b/ash/wm/overview/overview_session_unittest.cc
index 92728364..89135c4 100644
--- a/ash/wm/overview/overview_session_unittest.cc
+++ b/ash/wm/overview/overview_session_unittest.cc
@@ -3160,6 +3160,10 @@
   ToggleOverview();
   WaitForOverviewEnterAnimation();
 
+  auto* focus_widget = views::Widget::GetWidgetForNativeWindow(
+      GetOverviewSession()->GetOverviewFocusWindow());
+  DCHECK(focus_widget);
+
   OverviewGrid* grid = GetOverviewSession()->grid_list()[0].get();
   auto* desk_widget = const_cast<views::Widget*>(grid->desks_widget());
   DCHECK(desk_widget);
@@ -3183,18 +3187,21 @@
     EXPECT_EQ(expected_next, view_accessibility.GetNextFocus());
   };
 
-  // Order should be [desk_widget, item_widget1, item_widget2, item_widget3].
-  check_a11y_overrides("desk", desk_widget, item_widget3, item_widget1);
+  // Order should be [focus_widget, desk_widget, item_widget1, item_widget2,
+  // item_widget3].
+  check_a11y_overrides("focus", focus_widget, item_widget3, desk_widget);
+  check_a11y_overrides("desk", desk_widget, focus_widget, item_widget1);
   check_a11y_overrides("item1", item_widget1, desk_widget, item_widget2);
   check_a11y_overrides("item2", item_widget2, item_widget1, item_widget3);
-  check_a11y_overrides("item3", item_widget3, item_widget2, desk_widget);
+  check_a11y_overrides("item3", item_widget3, item_widget2, focus_widget);
 
-  // Remove |window2|. The new order should be [desk_widget, item_widget1,
-  // item_widget3].
+  // Remove |window2|. The new order should be [focus_widget, desk_widget,
+  // item_widget1, item_widget3].
   window2.reset();
-  check_a11y_overrides("desk", desk_widget, item_widget3, item_widget1);
+  check_a11y_overrides("focus", focus_widget, item_widget3, desk_widget);
+  check_a11y_overrides("desk", desk_widget, focus_widget, item_widget1);
   check_a11y_overrides("item1", item_widget1, desk_widget, item_widget3);
-  check_a11y_overrides("item3", item_widget3, item_widget1, desk_widget);
+  check_a11y_overrides("item3", item_widget3, item_widget1, focus_widget);
 }
 
 class TabletModeOverviewSessionTest : public OverviewSessionTest {
diff --git a/ash/wm/overview/overview_window_drag_controller.cc b/ash/wm/overview/overview_window_drag_controller.cc
index 9ef20f24..4916887 100644
--- a/ash/wm/overview/overview_window_drag_controller.cc
+++ b/ash/wm/overview/overview_window_drag_controller.cc
@@ -666,10 +666,10 @@
     // Get the window and bounds from |item_| before removing it from its grid.
     aura::Window* window = item_->GetWindow();
     const gfx::RectF target_item_bounds = item_->target_bounds();
-    // Remove |item_| from its grid. Leave the repositioning to the
+    // Remove |item_| from overview. Leave the repositioning to the
     // |OverviewItemMoveHelper|.
-    item_overview_grid->RemoveItem(item_, /*item_destroying=*/false,
-                                   /*reposition=*/false);
+    overview_session_->RemoveItem(item_, /*item_destroying=*/false,
+                                  /*reposition=*/false);
     item_ = nullptr;
     // The |OverviewItemMoveHelper| will self destruct when we move |window| to
     // |target_root|.
diff --git a/base/debug/stack_trace.cc b/base/debug/stack_trace.cc
index 1daf640..f5e2dbb 100644
--- a/base/debug/stack_trace.cc
+++ b/base/debug/stack_trace.cc
@@ -10,6 +10,7 @@
 #include <sstream>
 
 #include "base/check_op.h"
+#include "base/optional.h"
 #include "base/stl_util.h"
 
 #if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
@@ -95,17 +96,9 @@
 //   <more frames from Chrome>
 //   __libc_start_main
 //
-// For stack scanning to be efficient it's very important for the thread to
-// be started by Chrome. In that case we naturally terminate unwinding once
-// we reach the origin of the stack (i.e. GetStackEnd()). If the thread is
-// not started by Chrome (e.g. Android's main thread), then we end up always
-// scanning area at the origin of the stack, wasting time and not finding any
-// frames (since Android libraries don't have frame pointers).
-//
 // ScanStackForNextFrame() returns 0 if it couldn't find a valid frame
 // (or if stack scanning is not supported on the current platform).
 uintptr_t ScanStackForNextFrame(uintptr_t fp, uintptr_t stack_end) {
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
   // Enough to resume almost all prematurely terminated traces.
   constexpr size_t kMaxStackScanArea = 8192;
 
@@ -130,7 +123,6 @@
       }
     }
   }
-#endif  // defined(OS_LINUX) || defined(OS_CHROMEOS)
 
   return 0;
 }
@@ -250,44 +242,122 @@
 
 #if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
 
-size_t TraceStackFramePointers(const void** out_trace,
-                               size_t max_depth,
-                               size_t skip_initial) {
-  // Usage of __builtin_frame_address() enables frame pointers in this
-  // function even if they are not enabled globally. So 'fp' will always
-  // be valid.
-  uintptr_t fp = reinterpret_cast<uintptr_t>(__builtin_frame_address(0)) -
-                    kStackFrameAdjustment;
+struct AddressRange {
+  uintptr_t start;
+  uintptr_t end;
+};
 
-  uintptr_t stack_end = GetStackEnd();
+bool IsWithinRange(uintptr_t address, const AddressRange& range) {
+  return address >= range.start && address <= range.end;
+}
+
+size_t TraceStackFramePointersInternal(
+    base::Optional<uintptr_t> fp,
+    uintptr_t stack_end,
+    size_t max_depth,
+    size_t skip_initial,
+    bool enable_scanning,
+    base::Optional<AddressRange> caller_function_range,
+    const void** out_trace) {
+  // If |fp| is not provided then try to unwind the current stack. In this case
+  // the caller function cannot pass in it's own frame pointer to unwind
+  // because the frame pointer may not be valid here. The compiler can optimize
+  // the tail function call from the caller to skip to the previous frame of the
+  // caller directly, making it's frame pointer invalid when we reach this
+  // function.
+  if (!fp) {
+    // Usage of __builtin_frame_address() enables frame pointers in this
+    // function even if they are not enabled globally. So 'fp' will always
+    // be valid.
+    fp = reinterpret_cast<uintptr_t>(__builtin_frame_address(0)) -
+         kStackFrameAdjustment;
+  }
 
   size_t depth = 0;
   while (depth < max_depth) {
-    if (skip_initial != 0) {
-      skip_initial--;
-    } else {
-      out_trace[depth++] = reinterpret_cast<const void*>(GetStackFramePC(fp));
+    uintptr_t pc = GetStackFramePC(*fp);
+    // Case 1: If we are unwinding on a copied stack, then
+    // |caller_function_range| will not exist.
+    //
+    // Case 2: If we are unwinding the current stack from this function's frame,
+    // the next frame could be either the caller (TraceStackFramePointers()) or
+    // the function that called TraceStackFramePointers() (say Fn()).
+    //
+    // 2a. If the current function (depending on optimization of the build) is
+    // inlined, or the tail call to this function from TraceStackFramePointers()
+    // causes the frame pointer to skip directly to Fn(), the stack will look
+    // like this:
+    //    1st Frame: TraceStackFramePointersInternal()
+    //               TraceStackFramePointers() has no frame
+    //    2nd Frame: Fn()
+    //    ...
+    //  In this case we do not want to skip the caller from the output.
+    //
+    //  2b. Otherwise the stack will look like this:
+    //    1st Frame: TraceStackFramePointersInternal()
+    //    2nd Frame: <stack space of TraceStackFramePointers()>   <- Skip
+    //    3rd Frame: Fn()
+    //  In this case, the next pc will be within the caller function's
+    //  addresses, so skip the frame.
+    if (!caller_function_range || !IsWithinRange(pc, *caller_function_range)) {
+      if (skip_initial != 0) {
+        skip_initial--;
+      } else {
+        out_trace[depth++] = reinterpret_cast<const void*>(pc);
+      }
     }
 
-    uintptr_t next_fp = GetNextStackFrame(fp);
-    if (IsStackFrameValid(next_fp, fp, stack_end)) {
+    uintptr_t next_fp = GetNextStackFrame(*fp);
+    if (IsStackFrameValid(next_fp, *fp, stack_end)) {
       fp = next_fp;
       continue;
     }
 
-    next_fp = ScanStackForNextFrame(fp, stack_end);
+    if (!enable_scanning)
+      break;
+
+    next_fp = ScanStackForNextFrame(*fp, stack_end);
     if (next_fp) {
       fp = next_fp;
-      continue;
+    } else {
+      break;
     }
-
-    // Failed to find next frame.
-    break;
   }
 
   return depth;
 }
 
+size_t TraceStackFramePointers(const void** out_trace,
+                               size_t max_depth,
+                               size_t skip_initial,
+                               bool enable_scanning) {
+  // This function's frame can be skipped by the compiler since the callee
+  // function can jump to caller of this function directly while execution.
+  // Since there is no way to guarantee that the first frame the trace stack
+  // function finds will be this function or the previous function, skip the
+  // current function if it is found.
+TraceStackFramePointers_start:
+  AddressRange current_fn_range = {
+      reinterpret_cast<uintptr_t>(&&TraceStackFramePointers_start),
+      reinterpret_cast<uintptr_t>(&&TraceStackFramePointers_end)};
+  size_t depth = TraceStackFramePointersInternal(
+      /*fp=*/base::nullopt, GetStackEnd(), max_depth, skip_initial,
+      enable_scanning, current_fn_range, out_trace);
+TraceStackFramePointers_end:
+  return depth;
+}
+
+size_t TraceStackFramePointersFromBuffer(uintptr_t fp,
+                                         uintptr_t stack_end,
+                                         const void** out_trace,
+                                         size_t max_depth,
+                                         size_t skip_initial,
+                                         bool enable_scanning) {
+  return TraceStackFramePointersInternal(fp, stack_end, max_depth, skip_initial,
+                                         enable_scanning, base::nullopt,
+                                         out_trace);
+}
+
 ScopedStackFrameLinker::ScopedStackFrameLinker(void* fp, void* parent_fp)
     : fp_(fp),
       parent_fp_(parent_fp),
diff --git a/base/debug/stack_trace.h b/base/debug/stack_trace.h
index a516861..b8d6487 100644
--- a/base/debug/stack_trace.h
+++ b/base/debug/stack_trace.h
@@ -144,6 +144,20 @@
 BASE_EXPORT size_t CollectStackTrace(void** trace, size_t count);
 
 #if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
+
+// For stack scanning to be efficient it's very important for the thread to
+// be started by Chrome. In that case we naturally terminate unwinding once
+// we reach the origin of the stack (i.e. GetStackEnd()). If the thread is
+// not started by Chrome (e.g. Android's main thread), then we end up always
+// scanning area at the origin of the stack, wasting time and not finding any
+// frames (since Android libraries don't have frame pointers). Scanning is not
+// enabled on other posix platforms due to legacy reasons.
+#if defined(OS_LINUX)
+constexpr bool kEnableScanningByDefault = true;
+#else
+constexpr bool kEnableScanningByDefault = false;
+#endif
+
 // Traces the stack by using frame pointers. This function is faster but less
 // reliable than StackTrace. It should work for debug and profiling builds,
 // but not for release builds (although there are some exceptions).
@@ -151,10 +165,25 @@
 // Writes at most |max_depth| frames (instruction pointers) into |out_trace|
 // after skipping |skip_initial| frames. Note that the function itself is not
 // added to the trace so |skip_initial| should be 0 in most cases.
-// Returns number of frames written.
-BASE_EXPORT size_t TraceStackFramePointers(const void** out_trace,
-                                           size_t max_depth,
-                                           size_t skip_initial);
+// Returns number of frames written. |enable_scanning| enables scanning on
+// platforms that do not enable scanning by default.
+BASE_EXPORT size_t
+TraceStackFramePointers(const void** out_trace,
+                        size_t max_depth,
+                        size_t skip_initial,
+                        bool enable_scanning = kEnableScanningByDefault);
+
+// Same as above function, but allows to pass in frame pointer and stack end
+// address for unwinding. This is useful when unwinding based on a copied stack
+// segment. Note that the client has to take care of rewriting all the pointers
+// in the stack pointing within the stack to point to the copied addresses.
+BASE_EXPORT size_t TraceStackFramePointersFromBuffer(
+    uintptr_t fp,
+    uintptr_t stack_end,
+    const void** out_trace,
+    size_t max_depth,
+    size_t skip_initial,
+    bool enable_scanning = kEnableScanningByDefault);
 
 // Links stack frame |fp| to |parent_fp|, so that during stack unwinding
 // TraceStackFramePointers() visits |parent_fp| after visiting |fp|.
diff --git a/base/debug/stack_trace_unittest.cc b/base/debug/stack_trace_unittest.cc
index 3c7c06b..34e8ea35 100644
--- a/base/debug/stack_trace_unittest.cc
+++ b/base/debug/stack_trace_unittest.cc
@@ -13,6 +13,8 @@
 #include "base/logging.h"
 #include "base/process/kill.h"
 #include "base/process/process_handle.h"
+#include "base/profiler/stack_buffer.h"
+#include "base/profiler/stack_copier.h"
 #include "base/test/test_timeouts.h"
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -274,30 +276,63 @@
 
 #if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
 
+class CopyFunction : public StackCopier {
+ public:
+  using StackCopier::CopyStackContentsAndRewritePointers;
+};
+
+// Copies the current stack segment, starting from the frame pointer of the
+// caller frame. Also fills in |stack_end| for the copied stack.
+static std::unique_ptr<StackBuffer> NOINLINE
+CopyCurrentStackAndRewritePointers(uintptr_t* out_fp, uintptr_t* stack_end) {
+  const uint8_t* fp =
+      reinterpret_cast<const uint8_t*>(__builtin_frame_address(0));
+  uintptr_t original_stack_end = GetStackEnd();
+  size_t stack_size = original_stack_end - reinterpret_cast<uintptr_t>(fp);
+  auto buffer = std::make_unique<StackBuffer>(stack_size);
+  *out_fp = reinterpret_cast<uintptr_t>(
+      CopyFunction::CopyStackContentsAndRewritePointers(
+          fp, reinterpret_cast<const uintptr_t*>(original_stack_end),
+          StackBuffer::kPlatformStackAlignment, buffer->buffer()));
+  *stack_end = *out_fp + stack_size;
+  return buffer;
+}
+
 template <size_t Depth>
 void NOINLINE ExpectStackFramePointers(const void** frames,
-                                       size_t max_depth) {
+                                       size_t max_depth,
+                                       bool copy_stack) {
   code_start:
   // Calling __builtin_frame_address() forces compiler to emit
   // frame pointers, even if they are not enabled.
   EXPECT_NE(nullptr, __builtin_frame_address(0));
-  ExpectStackFramePointers<Depth - 1>(frames, max_depth);
+  ExpectStackFramePointers<Depth - 1>(frames, max_depth, copy_stack);
 
   constexpr size_t frame_index = Depth - 1;
   const void* frame = frames[frame_index];
   EXPECT_GE(frame, &&code_start) << "For frame at index " << frame_index;
   EXPECT_LE(frame, &&code_end) << "For frame at index " << frame_index;
   code_end: return;
-}
+  }
 
-template <>
-void NOINLINE ExpectStackFramePointers<1>(const void** frames,
-                                          size_t max_depth) {
+  template <>
+  void NOINLINE ExpectStackFramePointers<1>(const void** frames,
+                                            size_t max_depth,
+                                            bool copy_stack) {
   code_start:
   // Calling __builtin_frame_address() forces compiler to emit
   // frame pointers, even if they are not enabled.
   EXPECT_NE(nullptr, __builtin_frame_address(0));
-  size_t count = TraceStackFramePointers(frames, max_depth, 0);
+  size_t count = 0;
+  if (copy_stack) {
+    uintptr_t stack_end = 0, fp = 0;
+    std::unique_ptr<StackBuffer> copy =
+        CopyCurrentStackAndRewritePointers(&fp, &stack_end);
+    count =
+        TraceStackFramePointersFromBuffer(fp, stack_end, frames, max_depth, 0);
+  } else {
+    count = TraceStackFramePointers(frames, max_depth, 0);
+  }
   ASSERT_EQ(max_depth, count);
 
   const void* frame = frames[0];
@@ -318,7 +353,24 @@
 TEST_F(StackTraceTest, MAYBE_TraceStackFramePointers) {
   constexpr size_t kDepth = 5;
   const void* frames[kDepth];
-  ExpectStackFramePointers<kDepth>(frames, kDepth);
+  ExpectStackFramePointers<kDepth>(frames, kDepth, /*copy_stack=*/false);
+}
+
+#if defined(MEMORY_SANITIZER)
+// The test triggers use-of-uninitialized-value errors on MSan bots.
+// This is expected because we're walking and reading the stack, and
+// sometimes we read fp / pc from the place that previously held
+// uninitialized value.
+#define MAYBE_TraceStackFramePointersFromBuffer \
+  DISABLED_TraceStackFramePointersFromBuffer
+#else
+#define MAYBE_TraceStackFramePointersFromBuffer \
+  TraceStackFramePointersFromBuffer
+#endif
+TEST_F(StackTraceTest, MAYBE_TraceStackFramePointersFromBuffer) {
+  constexpr size_t kDepth = 5;
+  const void* frames[kDepth];
+  ExpectStackFramePointers<kDepth>(frames, kDepth, /*copy_stack=*/true);
 }
 
 #if defined(OS_ANDROID) || defined(OS_APPLE)
diff --git a/base/feature_list.cc b/base/feature_list.cc
index 3910734..c5c8145c 100644
--- a/base/feature_list.cc
+++ b/base/feature_list.cc
@@ -11,6 +11,7 @@
 
 #include "base/base_switches.h"
 #include "base/debug/alias.h"
+#include "base/debug/stack_trace.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/field_trial.h"
diff --git a/base/profiler/stack_sampler_android.cc b/base/profiler/stack_sampler_android.cc
index f7ca615..75cf149 100644
--- a/base/profiler/stack_sampler_android.cc
+++ b/base/profiler/stack_sampler_android.cc
@@ -21,9 +21,6 @@
     std::vector<std::unique_ptr<Unwinder>> core_unwinders,
     RepeatingClosure record_sample_callback,
     StackSamplerTestDelegate* test_delegate) {
-  // |core_unwinders| must contain NativeUnwinderAndroid and
-  // ChromeUnwinderAndroid, respectively.
-  DCHECK_EQ(2U, core_unwinders.size());
   return std::make_unique<StackSamplerImpl>(
       std::make_unique<StackCopierSignal>(
           std::make_unique<ThreadDelegatePosix>(thread_token)),
diff --git a/base/threading/platform_thread.h b/base/threading/platform_thread.h
index 39baf182..e52189a 100644
--- a/base/threading/platform_thread.h
+++ b/base/threading/platform_thread.h
@@ -231,10 +231,19 @@
   // to change the priority of sandboxed threads for improved performance.
   // Warning: Don't use this for a main thread because that will change the
   // whole thread group's (i.e. process) priority.
-  static void SetThreadPriority(PlatformThreadId thread_id,
+  static void SetThreadPriority(PlatformThreadId process_id,
+                                PlatformThreadId thread_id,
                                 ThreadPriority priority);
 #endif
 
+#if defined(OS_CHROMEOS)
+  // Signals that the feature list has been initialized which allows to check
+  // the feature's value now and initialize state. This prevents race
+  // conditions where the feature is being checked while it is being
+  // initialized, which can cause a crash.
+  static void InitThreadPostFieldTrial();
+#endif
+
   // Returns the default thread stack size set by chrome. If we do not
   // explicitly set default size then returns 0.
   static size_t GetDefaultThreadStackSize();
diff --git a/base/threading/platform_thread_linux.cc b/base/threading/platform_thread_linux.cc
index 095c49b..74a01ad 100644
--- a/base/threading/platform_thread_linux.cc
+++ b/base/threading/platform_thread_linux.cc
@@ -7,11 +7,17 @@
 #include <errno.h>
 #include <sched.h>
 #include <stddef.h>
+#include <cstdint>
+#include <atomic>
 
+#include "base/compiler_specific.h"
+#include "base/feature_list.h"
 #include "base/files/file_util.h"
 #include "base/lazy_instance.h"
 #include "base/logging.h"
+#include "base/process/internal_linux.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
 #include "base/threading/platform_thread_internal_posix.h"
 #include "base/threading/thread_id_name_manager.h"
 #include "build/build_config.h"
@@ -26,7 +32,73 @@
 #endif
 
 namespace base {
+
+#if defined(OS_CHROMEOS)
+const Feature kSchedUtilHints{"SchedUtilHints", base::FEATURE_ENABLED_BY_DEFAULT};
+#endif
+
 namespace {
+
+#if defined(OS_CHROMEOS)
+std::atomic<bool> g_use_sched_util(true);
+std::atomic<bool> g_feature_checked(false);
+
+// sched_attr is used to set scheduler attributes for Linux. It is not a POSIX
+// struct and glibc does not expose it.
+struct sched_attr {
+  uint32_t size;
+
+  uint32_t sched_policy;
+  uint64_t sched_flags;
+
+  /* SCHED_NORMAL, SCHED_BATCH */
+  __s32 sched_nice;
+
+  /* SCHED_FIFO, SCHED_RR */
+  uint32_t sched_priority;
+
+  /* SCHED_DEADLINE */
+  uint64_t sched_runtime;
+  uint64_t sched_deadline;
+  uint64_t sched_period;
+
+  /* Utilization hints */
+  uint32_t sched_util_min;
+  uint32_t sched_util_max;
+};
+
+#if !defined(__NR_sched_setattr)
+#if defined(__x86_64__)
+#define __NR_sched_setattr 314
+#define __NR_sched_getattr 315
+#elif defined(__i386__)
+#define __NR_sched_setattr 351
+#define __NR_sched_getattr 352
+#elif defined(__arm__)
+#define __NR_sched_setattr 380
+#define __NR_sched_getattr 381
+#elif defined(__aarch64__)
+#define __NR_sched_setattr 274
+#define __NR_sched_getattr 275
+#else
+#error "We don't have an __NR_sched_setattr for this architecture."
+#endif
+#endif
+
+int sched_getattr(pid_t pid,
+                  const struct sched_attr* attr,
+                  unsigned int size,
+                  unsigned int flags) {
+  return syscall(__NR_sched_getattr, pid, attr, size, flags);
+}
+
+int sched_setattr(pid_t pid,
+                  const struct sched_attr* attr,
+                  unsigned int flags) {
+  return syscall(__NR_sched_setattr, pid, attr, flags);
+}
+#endif  // OS_CHROMEOS
+
 #if !defined(OS_NACL)
 const FilePath::CharType kCgroupDirectory[] =
     FILE_PATH_LITERAL("/sys/fs/cgroup");
@@ -39,6 +111,7 @@
     case ThreadPriority::BACKGROUND:
       return cgroup_filepath.Append(FILE_PATH_LITERAL("non-urgent"));
     case ThreadPriority::DISPLAY:
+      FALLTHROUGH;
     case ThreadPriority::REALTIME_AUDIO:
       return cgroup_filepath.Append(FILE_PATH_LITERAL("urgent"));
   }
@@ -70,6 +143,98 @@
   SetThreadCgroup(thread_id, cgroup_directory);
 }
 
+#if defined(OS_CHROMEOS)
+// thread_id should always be the value in the root PID namespace (see
+// FindThreadID).
+void SetThreadLatencySensitivity(ProcessId process_id,
+                                 PlatformThreadId thread_id,
+                                 ThreadPriority priority) {
+  struct sched_attr attr;
+  bool is_urgent = false;
+  int uclamp_min_urgent, uclamp_max_non_urgent, latency_sensitive_urgent;
+
+  // Scheduler boost defaults to true unless disabled.
+  if (!g_use_sched_util.load())
+    return;
+
+  // FieldTrial API can be called only once features were parsed.
+  if (g_feature_checked.load()) {
+    uclamp_min_urgent =
+      GetFieldTrialParamByFeatureAsInt(kSchedUtilHints, "MinUrgent", 0);
+    uclamp_max_non_urgent = GetFieldTrialParamByFeatureAsInt(
+        kSchedUtilHints, "MaxNonUrgent", 100);
+    latency_sensitive_urgent = GetFieldTrialParamByFeatureAsBool(
+        kSchedUtilHints, "LatencySensitive", true);
+  } else {
+    // Use defaults if features were not parsed yet...
+    uclamp_min_urgent = 0;
+    uclamp_max_non_urgent = 100;
+    latency_sensitive_urgent = true;
+  }
+
+  // The thread_id passed in here is either 0 (in which case we ste for current
+  // thread), or is a tid that is not the NS tid but the global one. The
+  // conversion from NS tid to global tid is done by the callers using
+  // FindThreadID().
+  std::string thread_dir;
+  if (thread_id)
+    thread_dir = base::StringPrintf("/proc/%d/task/%d/", process_id, thread_id);
+  else
+    thread_dir = "/proc/thread-self/";
+
+  // Silently ignore request if thread directory doesn't exist.
+  if (!DirectoryExists(FilePath(thread_dir)))
+    return;
+
+  FilePath latency_sensitive_file = FilePath(thread_dir + "latency_sensitive");
+
+  if (!PathExists(latency_sensitive_file))
+    return;
+
+  // Silently ignore if getattr fails due to sandboxing.
+  if (sched_getattr(thread_id, &attr, sizeof(attr), 0) == -1 ||
+      attr.size != sizeof(attr))
+    return;
+
+  switch (priority) {
+    case ThreadPriority::NORMAL:
+      FALLTHROUGH;
+    case ThreadPriority::BACKGROUND:
+      break;
+    case ThreadPriority::DISPLAY:
+      // Display needs a boost for consistent 60 fps compositing.
+      FALLTHROUGH;
+    case ThreadPriority::REALTIME_AUDIO:
+      is_urgent = true;
+      break;
+  }
+
+  if (is_urgent && latency_sensitive_urgent) {
+    PLOG_IF(ERROR, !WriteFile(latency_sensitive_file, "1", 1))
+        << "Failed to write latency file.\n";
+  } else {
+    PLOG_IF(ERROR, !WriteFile(latency_sensitive_file, "0", 1))
+        << "Failed to write latency file.\n";
+  }
+
+  if (is_urgent) {
+    attr.sched_util_min = uclamp_min_urgent;
+    attr.sched_util_max = 100;
+  } else {
+    attr.sched_util_min = 0;
+    attr.sched_util_max = uclamp_max_non_urgent;
+  }
+
+  attr.size = sizeof(struct sched_attr);
+  if (sched_setattr(thread_id, &attr, 0) == -1) {
+    // We log it as an error because, if the PathExists above succeeded, we
+    // expect this syscall to also work since the kernel is new'ish.
+    PLOG_IF(ERROR, errno != E2BIG)
+        << "Failed to set sched_util_min, performance may be effected.\n";
+  }
+}
+#endif
+
 void SetThreadCgroupsForThreadPriority(PlatformThreadId thread_id,
                                        ThreadPriority priority) {
   FilePath cgroup_filepath(kCgroupDirectory);
@@ -113,7 +278,15 @@
 
 bool SetCurrentThreadPriorityForPlatform(ThreadPriority priority) {
 #if !defined(OS_NACL)
+  // For legacy schedtune interface
   SetThreadCgroupsForThreadPriority(PlatformThread::CurrentId(), priority);
+
+#if defined(OS_CHROMEOS)
+  // For upstream uclamp interface. We try both legacy (schedtune, as done
+  // earlier) and upstream (uclamp) interfaces, and whichever succeeds wins.
+  SetThreadLatencySensitivity(0 /* ignore */, 0 /* thread-self */, priority);
+#endif
+
   return priority == ThreadPriority::REALTIME_AUDIO &&
          pthread_setschedparam(pthread_self(), SCHED_RR, &kRealTimePrio) == 0;
 #else
@@ -163,15 +336,23 @@
 
 #if !defined(OS_NACL) && !defined(OS_AIX)
 // static
-void PlatformThread::SetThreadPriority(PlatformThreadId thread_id,
+void PlatformThread::SetThreadPriority(ProcessId process_id,
+                                       PlatformThreadId thread_id,
                                        ThreadPriority priority) {
   // Changing current main threads' priority is not permitted in favor of
   // security, this interface is restricted to change only non-main thread
   // priority.
-  CHECK_NE(thread_id, getpid());
+  CHECK_NE(thread_id, process_id);
 
+  // For legacy schedtune interface
   SetThreadCgroupsForThreadPriority(thread_id, priority);
 
+#if defined(OS_CHROMEOS)
+  // For upstream uclamp interface. We try both legacy (schedtune, as done
+  // earlier) and upstream (uclamp) interfaces, and whichever succeeds wins.
+  SetThreadLatencySensitivity(process_id, thread_id, priority);
+#endif
+
   const int nice_setting = internal::ThreadPriorityToNiceValue(priority);
   if (setpriority(PRIO_PROCESS, thread_id, nice_setting)) {
     DVPLOG(1) << "Failed to set nice value of thread (" << thread_id << ") to "
@@ -180,6 +361,16 @@
 }
 #endif  //  !defined(OS_NACL) && !defined(OS_AIX)
 
+#if defined(OS_CHROMEOS)
+void PlatformThread::InitThreadPostFieldTrial() {
+  DCHECK(FeatureList::GetInstance());
+  if (!FeatureList::IsEnabled(kSchedUtilHints)) {
+    g_use_sched_util.store(false);
+  }
+  g_feature_checked.store(true);
+}
+#endif
+
 void InitThreading() {}
 
 void TerminateOnThread() {}
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index b2b7375..6bbf2b3 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-0.20200910.2.2
+0.20200911.0.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index b2b7375..6bbf2b3 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-0.20200910.2.2
+0.20200911.0.1
diff --git a/cc/paint/display_item_list.cc b/cc/paint/display_item_list.cc
index deedb9c..aa047ab 100644
--- a/cc/paint/display_item_list.cc
+++ b/cc/paint/display_item_list.cc
@@ -119,22 +119,26 @@
 }
 
 double DisplayItemList::AreaOfDrawText(const gfx::Rect& rect) const {
-  double area = 0;
   if (!paint_op_buffer_.has_draw_text_ops())
-    return area;
+    return 0;
   std::vector<size_t> offsets;
   std::vector<gfx::Rect> rects;
   rtree_.Search(rect, &offsets, &rects);
-  IterateTextContentByOffsets(
-      paint_op_buffer_, offsets, rects,
-      [&area](const DrawTextBlobOp* op, const gfx::Rect& rect) {
-        // TODO(wangxianzhu) : crbug.com/1126582 use the visual_rect from
-        // callback.
-        // This is not fully accurate, e.g. when there is transform operations,
-        // but is good for statistics purpose.
-        SkRect bounds = op->blob->bounds();
-        area += static_cast<double>(bounds.width()) * bounds.height();
-      });
+  DCHECK_EQ(offsets.size(), rects.size());
+
+  double area = 0;
+  size_t index = 0;
+  for (auto* op : PaintOpBuffer::OffsetIterator(&paint_op_buffer_, &offsets)) {
+    if (op->GetType() == PaintOpType::DrawTextBlob ||
+        // Don't walk into the record because the visual rect is already the
+        // bounding box of the sub paint operations. This works for most paint
+        // results for text generated by blink.
+        (op->GetType() == PaintOpType::DrawRecord &&
+         static_cast<DrawRecordOp*>(op)->record->has_draw_text_ops())) {
+      area += static_cast<double>(rects[index].width()) * rects[index].height();
+    }
+    ++index;
+  }
   return area;
 }
 
diff --git a/cc/paint/display_item_list_unittest.cc b/cc/paint/display_item_list_unittest.cc
index a823078..2a3889b 100644
--- a/cc/paint/display_item_list_unittest.cc
+++ b/cc/paint/display_item_list_unittest.cc
@@ -1138,14 +1138,15 @@
   auto sub_list = base::MakeRefCounted<DisplayItemList>();
 
   auto text_blob1 = SkTextBlob::MakeFromString("ABCD", SkFont());
-  auto text_blob1_bounds = text_blob1->bounds();
-  auto text_blob1_area = text_blob1_bounds.width() * text_blob1_bounds.height();
+  gfx::Size text_blob1_size(ceilf(text_blob1->bounds().width()),
+                            ceilf(text_blob1->bounds().height()));
+  auto text_blob1_area = text_blob1_size.width() * text_blob1_size.height();
   auto text_blob2 = SkTextBlob::MakeFromString("EFG", SkFont());
-  auto text_blob2_bounds = text_blob2->bounds();
-  auto text_blob2_area = text_blob2_bounds.width() * text_blob2_bounds.height();
+  gfx::Size text_blob2_size(ceilf(text_blob2->bounds().width()),
+                            ceilf(text_blob2->bounds().height()));
+  auto text_blob2_area = text_blob2_size.width() * text_blob2_size.height();
 
   sub_list->StartPaint();
-  sub_list->push<DrawRectOp>(SkRect::MakeWH(100, 200), PaintFlags());
   sub_list->push<DrawTextBlobOp>(text_blob1, 0, 0, PaintFlags());
   sub_list->EndPaintOfUnpaired(gfx::Rect());
   auto record = sub_list->ReleaseAsRecord();
@@ -1155,22 +1156,24 @@
   list->push<TranslateOp>(100, 100);
   list->push<DrawRecordOp>(record);
   list->push<RestoreOp>();
-  list->EndPaintOfUnpaired(gfx::Rect(100, 100, 100, 200));
+  list->EndPaintOfUnpaired(gfx::Rect(gfx::Point(100, 100), text_blob1_size));
 
   list->StartPaint();
   list->push<SaveOp>();
   list->push<TranslateOp>(100, 400);
   list->push<DrawRecordOp>(record);
   list->push<RestoreOp>();
-  list->EndPaintOfUnpaired(gfx::Rect(100, 400, 100, 200));
+  list->EndPaintOfUnpaired(gfx::Rect(gfx::Point(100, 400), text_blob1_size));
 
   list->StartPaint();
-  list->push<DrawRectOp>(SkRect::MakeWH(100, 100), PaintFlags());
   list->push<DrawTextBlobOp>(text_blob2, 10, 20, PaintFlags());
-  list->EndPaintOfUnpaired(gfx::Rect(0, 0, 100, 100));
+  list->EndPaintOfUnpaired(gfx::Rect(text_blob2_size));
 
   list->StartPaint();
   list->push<DrawTextBlobOp>(text_blob2, 400, 100, PaintFlags());
+  list->EndPaintOfUnpaired(gfx::Rect(gfx::Point(400, 100), text_blob2_size));
+
+  list->StartPaint();
   list->push<DrawRectOp>(SkRect::MakeXYWH(400, 100, 100, 100), PaintFlags());
   list->EndPaintOfUnpaired(gfx::Rect(400, 100, 100, 100));
 
@@ -1178,10 +1181,10 @@
   // This includes the DrawTextBlobOp in the first DrawRecordOp the the first
   // direct DrawTextBlobOp.
   EXPECT_EQ(static_cast<int>(text_blob1_area + text_blob2_area),
-            static_cast<int>(list->AreaOfDrawText(gfx::Rect(0, 0, 200, 200))));
+            static_cast<int>(list->AreaOfDrawText(gfx::Rect(200, 200))));
   // This includes all DrawTextBlobOps.
   EXPECT_EQ(static_cast<int>(text_blob1_area * 2 + text_blob2_area * 2),
-            static_cast<int>(list->AreaOfDrawText(gfx::Rect(0, 0, 500, 500))));
+            static_cast<int>(list->AreaOfDrawText(gfx::Rect(500, 500))));
 }
 
 }  // namespace cc
diff --git a/chrome/VERSION b/chrome/VERSION
index 9822dc75..79dde5a 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=87
 MINOR=0
-BUILD=4261
+BUILD=4262
 PATCH=0
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 4d0f5a4..c32bbad 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -2816,6 +2816,15 @@
   }
 }
 
+# Convenient target to build all Trichrome APK / bundles for testing.
+group("trichrome") {
+  deps = [
+    ":trichrome_chrome_bundle",
+    ":trichrome_library_apk",
+    "//android_webview:trichrome_webview_bundle",
+  ]
+}
+
 if (is_official_build) {
   _trichrome_library_basename = "TrichromeLibrary.apk"
   _trichrome_chrome_basename = "TrichromeChrome.minimal.apks"
@@ -2834,16 +2843,20 @@
     bundle_path = "$root_build_dir/apks/TrichromeChrome.aab"
   }
 
+  group("trichrome_minimal_apks") {
+    deps = [
+      ":trichrome_chrome_minimal_apks",
+      ":trichrome_library_apk",
+      "//android_webview:trichrome_webview_minimal_apks",
+    ]
+  }
+
   android_resource_sizes_test("resource_sizes_trichrome") {
     apk_name = "Trichrome"
     trichrome_library_path = "$root_build_dir/apks/$_trichrome_library_basename"
     trichrome_chrome_path = "$root_build_dir/apks/$_trichrome_chrome_basename"
     trichrome_webview_path = "$root_build_dir/apks/$_trichrome_webview_basename"
-    data_deps = [
-      ":trichrome_chrome_minimal_apks",
-      ":trichrome_library_apk",
-      "//android_webview:trichrome_webview_minimal_apks",
-    ]
+    data_deps = [ ":trichrome_minimal_apks" ]
   }
 }
 
diff --git a/chrome/android/chrome_java_resources.gni b/chrome/android/chrome_java_resources.gni
index 7f92bd2..31ec2aa 100644
--- a/chrome/android/chrome_java_resources.gni
+++ b/chrome/android/chrome_java_resources.gni
@@ -64,7 +64,6 @@
   "java/res/drawable-hdpi/help_outline.png",
   "java/res/drawable-hdpi/ic_account_child_20dp.png",
   "java/res/drawable-hdpi/ic_chrome.png",
-  "java/res/drawable-hdpi/ic_close_all_tabs.png",
   "java/res/drawable-hdpi/ic_devices_16dp.png",
   "java/res/drawable-hdpi/ic_devices_48dp.png",
   "java/res/drawable-hdpi/ic_dialer_icon_blue_40dp.png",
@@ -212,7 +211,6 @@
   "java/res/drawable-mdpi/help_outline.png",
   "java/res/drawable-mdpi/ic_account_child_20dp.png",
   "java/res/drawable-mdpi/ic_chrome.png",
-  "java/res/drawable-mdpi/ic_close_all_tabs.png",
   "java/res/drawable-mdpi/ic_devices_16dp.png",
   "java/res/drawable-mdpi/ic_devices_48dp.png",
   "java/res/drawable-mdpi/ic_dialer_icon_blue_40dp.png",
@@ -344,7 +342,6 @@
   "java/res/drawable-xhdpi/help_outline.png",
   "java/res/drawable-xhdpi/ic_account_child_20dp.png",
   "java/res/drawable-xhdpi/ic_chrome.png",
-  "java/res/drawable-xhdpi/ic_close_all_tabs.png",
   "java/res/drawable-xhdpi/ic_devices_16dp.png",
   "java/res/drawable-xhdpi/ic_devices_48dp.png",
   "java/res/drawable-xhdpi/ic_dialer_icon_blue_40dp.png",
@@ -455,7 +452,6 @@
   "java/res/drawable-xxhdpi/help_outline.png",
   "java/res/drawable-xxhdpi/ic_account_child_20dp.png",
   "java/res/drawable-xxhdpi/ic_chrome.png",
-  "java/res/drawable-xxhdpi/ic_close_all_tabs.png",
   "java/res/drawable-xxhdpi/ic_devices_16dp.png",
   "java/res/drawable-xxhdpi/ic_devices_48dp.png",
   "java/res/drawable-xxhdpi/ic_dialer_icon_blue_40dp.png",
@@ -565,7 +561,6 @@
   "java/res/drawable-xxxhdpi/help_outline.png",
   "java/res/drawable-xxxhdpi/ic_account_child_20dp.png",
   "java/res/drawable-xxxhdpi/ic_chrome.png",
-  "java/res/drawable-xxxhdpi/ic_close_all_tabs.png",
   "java/res/drawable-xxxhdpi/ic_devices_16dp.png",
   "java/res/drawable-xxxhdpi/ic_devices_48dp.png",
   "java/res/drawable-xxxhdpi/ic_dialer_icon_blue_40dp.png",
@@ -819,10 +814,6 @@
   "java/res/layout/bookmark_widget_icons_only.xml",
   "java/res/layout/bookmark_widget_item.xml",
   "java/res/layout/bottom_control_container.xml",
-  "java/res/layout/bottom_toolbar.xml",
-  "java/res/layout/bottom_toolbar_browsing.xml",
-  "java/res/layout/bottom_toolbar_menu_button.xml",
-  "java/res/layout/bottom_toolbar_tab_switcher.xml",
   "java/res/layout/clear_browsing_data_button.xml",
   "java/res/layout/clear_browsing_data_tabs.xml",
   "java/res/layout/clear_browsing_important_dialog_listview.xml",
@@ -882,7 +873,6 @@
   "java/res/layout/history_privacy_disclaimer_header.xml",
   "java/res/layout/history_toolbar.xml",
   "java/res/layout/homepage_editor.xml",
-  "java/res/layout/icon_row_menu_footer.xml",
   "java/res/layout/incognito_description_layout.xml",
   "java/res/layout/incognito_interstitial_bottom_sheet_view.xml",
   "java/res/layout/incognito_menu_item.xml",
@@ -997,7 +987,6 @@
   "java/res/layout/tile_view_modern.xml",
   "java/res/layout/tile_view_modern_condensed.xml",
   "java/res/layout/toolbar_phone.xml",
-  "java/res/layout/toolbar_space.xml",
   "java/res/layout/toolbar_tablet.xml",
   "java/res/layout/top_sites_tile_view.xml",
   "java/res/layout/top_sites_tile_view_condensed.xml",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 01b02780..b0f7d47 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -693,7 +693,6 @@
   "java/src/org/chromium/chrome/browser/feedback/ConnectivityFeedbackSource.java",
   "java/src/org/chromium/chrome/browser/feedback/ConnectivityTask.java",
   "java/src/org/chromium/chrome/browser/feedback/DataReductionProxyFeedbackSource.java",
-  "java/src/org/chromium/chrome/browser/feedback/DuetFeedbackSource.java",
   "java/src/org/chromium/chrome/browser/feedback/FeedFeedbackCollector.java",
   "java/src/org/chromium/chrome/browser/feedback/FeedbackCollector.java",
   "java/src/org/chromium/chrome/browser/feedback/FeedbackContextFeedbackSource.java",
@@ -1584,24 +1583,7 @@
   "java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsMediator.java",
   "java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsProperties.java",
   "java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsViewBinder.java",
-  "java/src/org/chromium/chrome/browser/toolbar/bottom/BottomTabSwitcherActionMenuCoordinator.java",
-  "java/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarConfiguration.java",
-  "java/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarCoordinator.java",
-  "java/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarNewTabButton.java",
-  "java/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarVariationManager.java",
-  "java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarCoordinator.java",
-  "java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarLinearLayout.java",
-  "java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarMediator.java",
-  "java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarModel.java",
-  "java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarViewBinder.java",
-  "java/src/org/chromium/chrome/browser/toolbar/bottom/CloseAllTabsButton.java",
   "java/src/org/chromium/chrome/browser/toolbar/bottom/ScrollingBottomViewResourceFrameLayout.java",
-  "java/src/org/chromium/chrome/browser/toolbar/bottom/SearchAccelerator.java",
-  "java/src/org/chromium/chrome/browser/toolbar/bottom/ShareButton.java",
-  "java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarCoordinator.java",
-  "java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarMediator.java",
-  "java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarModel.java",
-  "java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarViewBinder.java",
   "java/src/org/chromium/chrome/browser/toolbar/load_progress/LoadProgressCoordinator.java",
   "java/src/org/chromium/chrome/browser/toolbar/load_progress/LoadProgressMediator.java",
   "java/src/org/chromium/chrome/browser/toolbar/load_progress/LoadProgressProperties.java",
diff --git a/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceCoordinator.java b/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceCoordinator.java
index d25be37..c91baf8 100644
--- a/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceCoordinator.java
+++ b/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceCoordinator.java
@@ -17,7 +17,6 @@
 import org.chromium.chrome.browser.tasks.tab_management.TabManagementDelegate.TabSwitcherType;
 import org.chromium.chrome.browser.tasks.tab_management.TabManagementModuleProvider;
 import org.chromium.chrome.browser.tasks.tab_management.TabSwitcher;
-import org.chromium.chrome.browser.toolbar.bottom.BottomToolbarConfiguration;
 import org.chromium.chrome.features.start_surface.StartSurfaceMediator.SurfaceMode;
 import org.chromium.chrome.start_surface.R;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
@@ -283,12 +282,7 @@
 
         String feature = StartSurfaceConfiguration.START_SURFACE_VARIATION.getValue();
 
-        if (feature.equals("twopanes")) {
-            // Do not enable two panes when the bottom bar is enabled since it will
-            // overlap the two panes' bottom bar.
-            return BottomToolbarConfiguration.isBottomToolbarEnabled() ? SurfaceMode.SINGLE_PANE
-                                                                       : SurfaceMode.TWO_PANES;
-        }
+        if (feature.equals("twopanes")) return SurfaceMode.TWO_PANES;
 
         // TODO(crbug.com/982018): Remove isStartSurfaceSinglePaneEnabled check after
         // removing ChromePreferenceKeys.START_SURFACE_SINGLE_PANE_ENABLED_KEY.
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java
index f249f22..cab57d6 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java
@@ -41,7 +41,6 @@
 import org.chromium.chrome.browser.tasks.tab_groups.EmptyTabGroupModelFilterObserver;
 import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter;
 import org.chromium.chrome.browser.toolbar.bottom.BottomControlsCoordinator;
-import org.chromium.chrome.browser.toolbar.bottom.BottomToolbarConfiguration;
 import org.chromium.chrome.browser.ui.messages.infobar.SimpleConfirmInfoBarBuilder;
 import org.chromium.chrome.browser.ui.messages.snackbar.Snackbar;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
@@ -468,11 +467,6 @@
                 ConditionalTabStripUtils.updateLastShownTimeStamp();
             }
         }
-        boolean isDuetTabStripIntegrationEnabled =
-                TabUiFeatureUtilities.isDuetTabStripIntegrationAndroidEnabled()
-                && BottomToolbarConfiguration.isBottomToolbarEnabled();
-        assert (mVisibilityController == null) == isDuetTabStripIntegrationEnabled;
-        if (isDuetTabStripIntegrationEnabled) return;
         if (mIsTabGroupUiVisible) {
             // Post to make sure that the recyclerView already knows how many visible items it has.
             // This is to make sure that we can scroll to a state where the selected tab is in the
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
index bf8f240..e7ac4c7 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
@@ -33,7 +33,6 @@
 import org.chromium.chrome.browser.tasks.pseudotab.PseudoTab;
 import org.chromium.chrome.browser.tasks.tab_groups.TabGroupUtils;
 import org.chromium.chrome.browser.tasks.tab_management.TabProperties.UiType;
-import org.chromium.chrome.browser.toolbar.bottom.BottomToolbarConfiguration;
 import org.chromium.chrome.features.start_surface.StartSurfaceConfiguration;
 import org.chromium.chrome.tab_ui.R;
 import org.chromium.components.feature_engagement.FeatureConstants;
@@ -222,13 +221,6 @@
 
         mRecyclerView.setAdapter(mAdapter);
         mRecyclerView.setHasFixedSize(true);
-        if (mMode == TabListMode.STRIP
-                && TabUiFeatureUtilities.isDuetTabStripIntegrationAndroidEnabled()
-                && BottomToolbarConfiguration.isBottomToolbarEnabled()) {
-            // TODO(crbug.com/1045944): Disable item animation for now for Duet-TabStrip Integration
-            //  to avoid crash.
-            mRecyclerView.setItemAnimator(null);
-        }
         if (recyclerListener != null) mRecyclerView.setRecyclerListener(recyclerListener);
 
         // TODO (https://crbug.com/1048632): Use the current profile (i.e., regular profile or
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java
index 3846b5dd..27b921f 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java
@@ -132,13 +132,6 @@
     }
 
     /**
-     * @return Whether the tab strip and duet integration feature is enabled and available for use.
-     */
-    public static boolean isDuetTabStripIntegrationAndroidEnabled() {
-        return false;
-    }
-
-    /**
      * @return Whether the tab group continuation feature is enabled and available for use.
      */
     public static boolean isTabGroupsAndroidContinuationEnabled() {
diff --git a/chrome/android/java/res/drawable-hdpi/ic_close_all_tabs.png b/chrome/android/java/res/drawable-hdpi/ic_close_all_tabs.png
deleted file mode 100644
index 473cf0c..0000000
--- a/chrome/android/java/res/drawable-hdpi/ic_close_all_tabs.png
+++ /dev/null
Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/ic_close_all_tabs.png b/chrome/android/java/res/drawable-mdpi/ic_close_all_tabs.png
deleted file mode 100644
index 847a239..0000000
--- a/chrome/android/java/res/drawable-mdpi/ic_close_all_tabs.png
+++ /dev/null
Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/ic_close_all_tabs.png b/chrome/android/java/res/drawable-xhdpi/ic_close_all_tabs.png
deleted file mode 100644
index 47ca228..0000000
--- a/chrome/android/java/res/drawable-xhdpi/ic_close_all_tabs.png
+++ /dev/null
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/ic_close_all_tabs.png b/chrome/android/java/res/drawable-xxhdpi/ic_close_all_tabs.png
deleted file mode 100644
index d4426ad..0000000
--- a/chrome/android/java/res/drawable-xxhdpi/ic_close_all_tabs.png
+++ /dev/null
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/ic_close_all_tabs.png b/chrome/android/java/res/drawable-xxxhdpi/ic_close_all_tabs.png
deleted file mode 100644
index 4384d6a..0000000
--- a/chrome/android/java/res/drawable-xxxhdpi/ic_close_all_tabs.png
+++ /dev/null
Binary files differ
diff --git a/chrome/android/java/res/layout/bottom_control_container.xml b/chrome/android/java/res/layout/bottom_control_container.xml
index 03f19cff..a19b661 100644
--- a/chrome/android/java/res/layout/bottom_control_container.xml
+++ b/chrome/android/java/res/layout/bottom_control_container.xml
@@ -27,17 +27,7 @@
         <FrameLayout
             android:layout_width="match_parent"
             android:layout_height="match_parent"
-            android:id="@+id/bottom_container_slot" >
-
-            <ViewStub
-                android:id="@+id/bottom_toolbar_stub"
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:layout_gravity="start|bottom"
-                android:inflatedId="@+id/bottom_toolbar"
-                android:layout="@layout/bottom_toolbar" />
-
-        </FrameLayout>
+            android:id="@+id/bottom_container_slot" />
 
     </LinearLayout>
 
diff --git a/chrome/android/java/res/layout/bottom_toolbar.xml b/chrome/android/java/res/layout/bottom_toolbar.xml
deleted file mode 100644
index df816b8..0000000
--- a/chrome/android/java/res/layout/bottom_toolbar.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2018 The Chromium Authors. All rights reserved.
-     Use of this source code is governed by a BSD-style license that can be
-     found in the LICENSE file. -->
-
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent" >
-
-  <include layout="@layout/bottom_toolbar_browsing" />
-
-  <ViewStub
-      android:id="@+id/bottom_toolbar_tab_switcher_mode_stub"
-      android:layout_width="match_parent"
-      android:layout_height="match_parent"
-      android:inflatedId="@+id/bottom_toolbar_tab_switcher_mode"
-      android:layout="@layout/bottom_toolbar_tab_switcher" />
-
-</FrameLayout>
diff --git a/chrome/android/java/res/layout/bottom_toolbar_browsing.xml b/chrome/android/java/res/layout/bottom_toolbar_browsing.xml
deleted file mode 100644
index 341f1563..0000000
--- a/chrome/android/java/res/layout/bottom_toolbar_browsing.xml
+++ /dev/null
@@ -1,76 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2018 The Chromium Authors. All rights reserved.
-     Use of this source code is governed by a BSD-style license that can be
-     found in the LICENSE file. -->
-
-
-<org.chromium.chrome.browser.toolbar.bottom.BrowsingModeBottomToolbarLinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:id="@+id/bottom_toolbar_browsing"
-    android:orientation="horizontal"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:background="@color/toolbar_background_primary"
-    android:layout_gravity="top|center_horizontal"
-    android:paddingStart="@dimen/bottom_toolbar_padding"
-    android:paddingEnd="@dimen/bottom_toolbar_padding" >
-
-    <include layout="@layout/toolbar_space" />
-
-    <org.chromium.chrome.browser.toolbar.HomeButton
-        android:id="@+id/bottom_home_button"
-        app:tint="@color/default_icon_color_tint_list"
-        android:background="?attr/selectableItemBackgroundBorderless"
-        android:contentDescription="@string/accessibility_toolbar_btn_home"
-        android:visibility="gone"
-        style="@style/SplitToolbarButton" />
-
-    <org.chromium.chrome.browser.toolbar.bottom.BottomToolbarNewTabButton
-        android:id="@+id/bottom_new_tab_button"
-        android:contentDescription="@string/accessibility_toolbar_btn_new_tab"
-        app:tint="@color/default_icon_color_tint_list"
-        android:background="?attr/selectableItemBackgroundBorderless"
-        app:srcCompat="@drawable/new_tab_icon"
-        android:visibility="gone"
-        style="@style/SplitToolbarButton" />
-
-    <include layout="@layout/toolbar_space" />
-    <include layout="@layout/toolbar_space" />
-
-    <org.chromium.chrome.browser.toolbar.bottom.SearchAccelerator
-        android:id="@+id/search_accelerator"
-        android:layout_width="@dimen/search_accelerator_width"
-        android:layout_height="@dimen/search_accelerator_height"
-        android:layout_gravity="center"
-        android:layout_marginStart="@dimen/search_accelerator_width_margin"
-        android:layout_marginEnd="@dimen/search_accelerator_width_margin"
-        android:paddingTop="@dimen/search_accelerator_height_padding"
-        android:paddingBottom="@dimen/search_accelerator_height_padding"
-        android:contentDescription="@string/accessibility_toolbar_btn_search_accelerator"
-        android:src="@drawable/ic_search"/>
-
-    <include layout="@layout/toolbar_space" />
-    <include layout="@layout/toolbar_space" />
-
-    <org.chromium.chrome.browser.toolbar.TabSwitcherButtonView
-        android:id="@+id/bottom_tab_switcher_button"
-        style="@style/SplitToolbarButton"
-        app:menuMaxWidth="@dimen/tab_switcher_menu_width"
-        android:visibility="gone"
-        android:background="?attr/selectableItemBackgroundBorderless"
-        android:contentDescription="@string/accessibility_toolbar_btn_tabswitcher_toggle_default"
-        app:menuVerticalOverlapAnchor="false" />
-
-    <org.chromium.chrome.browser.toolbar.bottom.ShareButton
-        android:id="@+id/bottom_share_button"
-        android:src="@drawable/ic_share_white_24dp"
-        app:tint="@color/default_icon_color_tint_list"
-        android:visibility="gone"
-        android:background="?attr/selectableItemBackgroundBorderless"
-        android:contentDescription="@string/share"
-        style="@style/SplitToolbarButton" />
-
-    <include layout="@layout/toolbar_space" />
-
-</org.chromium.chrome.browser.toolbar.bottom.BrowsingModeBottomToolbarLinearLayout>
diff --git a/chrome/android/java/res/layout/bottom_toolbar_menu_button.xml b/chrome/android/java/res/layout/bottom_toolbar_menu_button.xml
deleted file mode 100644
index a4badfa..0000000
--- a/chrome/android/java/res/layout/bottom_toolbar_menu_button.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2019 The Chromium Authors. All rights reserved.
-     Use of this source code is governed by a BSD-style license that can be
-     found in the LICENSE file. -->
-<org.chromium.chrome.browser.toolbar.menu_button.MenuButton
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:layout_gravity="center"
-    android:clickable="true"
-    android:contentDescription="@string/accessibility_toolbar_btn_menu"
-    android:id="@+id/menu_button_wrapper">
-
-    <org.chromium.ui.widget.ChromeImageButton
-        android:id="@+id/menu_button"
-        style="@style/SplitToolbarButton"
-        android:src="@drawable/ic_more_vert_24dp"
-        android:importantForAccessibility="no"
-        android:layout_gravity="center"
-        android:background="?attr/selectableItemBackgroundBorderless"
-        app:tint="@color/default_icon_color_tint_list" />
-
-    <ImageView
-        android:id="@+id/menu_badge"
-        style="@style/SplitToolbarButton"
-        android:src="@drawable/badge_update_dark"
-        tools:ignore="ContentDescription"
-        android:background="?attr/selectableItemBackgroundBorderless"
-        android:importantForAccessibility="no"
-        android:layout_gravity="center"
-        android:visibility="invisible" />
-
-</org.chromium.chrome.browser.toolbar.menu_button.MenuButton>
-
diff --git a/chrome/android/java/res/layout/bottom_toolbar_tab_switcher.xml b/chrome/android/java/res/layout/bottom_toolbar_tab_switcher.xml
deleted file mode 100644
index b52337f..0000000
--- a/chrome/android/java/res/layout/bottom_toolbar_tab_switcher.xml
+++ /dev/null
@@ -1,66 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2018 The Chromium Authors. All rights reserved.
-     Use of this source code is governed by a BSD-style license that can be
-     found in the LICENSE file. -->
-
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:id="@+id/bottom_toolbar_tab_switcher"
-    android:orientation="vertical"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:visibility="gone"
-    android:clickable="true" >
-
-    <FrameLayout
-        android:id="@+id/bottom_toolbar_buttons"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:background="@color/default_bg_color" >
-
-        <LinearLayout
-            android:orientation="horizontal"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:paddingStart="@dimen/bottom_toolbar_padding"
-            android:paddingEnd="@dimen/bottom_toolbar_padding" >
-
-            <org.chromium.chrome.browser.toolbar.bottom.CloseAllTabsButton
-                android:id="@+id/close_all_tabs_button"
-                style="@style/BottomToolbarButtonWrapper"
-                android:src="@drawable/ic_close_all_tabs"
-                android:contentDescription="@string/accessibility_toolbar_btn_close_all_tabs"
-                app:tint="@color/default_icon_color_tint_list" />
-
-            <include layout="@layout/toolbar_space" />
-
-            <org.chromium.chrome.browser.toolbar.bottom.BottomToolbarNewTabButton
-                android:id="@+id/tab_switcher_new_tab_button"
-                android:layout_width="@dimen/search_accelerator_height"
-                android:layout_height="@dimen/search_accelerator_height"
-                android:layout_gravity="center"
-                android:contentDescription="@string/accessibility_toolbar_btn_new_tab"
-                android:paddingTop="@dimen/search_accelerator_height_padding"
-                android:paddingBottom="@dimen/search_accelerator_height_padding" />
-
-            <include layout="@layout/toolbar_space" />
-
-            <include layout="@layout/bottom_toolbar_menu_button"
-                style="@style/SplitToolbarButton" />
-
-        </LinearLayout>
-
-    </FrameLayout>
-
-    <ImageView
-        android:id="@+id/bottom_toolbar_bottom_shadow"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/toolbar_shadow_height"
-        android:src="@drawable/modern_toolbar_shadow"
-        android:scaleType="fitXY"
-        android:visibility="gone"
-        tools:ignore="ContentDescription" />
-
-</LinearLayout>
diff --git a/chrome/android/java/res/layout/icon_row_menu_footer.xml b/chrome/android/java/res/layout/icon_row_menu_footer.xml
deleted file mode 100644
index 2382b933..0000000
--- a/chrome/android/java/res/layout/icon_row_menu_footer.xml
+++ /dev/null
@@ -1,53 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2018 The Chromium Authors. All rights reserved.
-
-     Use of this source code is governed by a BSD-style license that can be
-     found in the LICENSE file.
--->
-<org.chromium.chrome.browser.app.appmenu.AppMenuIconRowFooter
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:orientation="vertical" >
-
-    <View style="@style/HorizontalDivider" />
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="?android:attr/listPreferredItemHeightSmall"
-        android:orientation="horizontal"
-        android:id="@+id/menu_items" >
-
-        <org.chromium.ui.widget.ChromeImageButton
-            android:id="@+id/forward_menu_id"
-            style="@style/OverflowMenuButton"
-            android:src="@drawable/btn_forward"
-            android:contentDescription="@string/accessibility_menu_forward" />
-
-        <org.chromium.ui.widget.ChromeImageButton
-            android:id="@+id/bookmark_this_page_id"
-            style="@style/OverflowMenuButton"
-            android:src="@drawable/btn_star"
-            android:contentDescription="@string/accessibility_menu_bookmark" />
-
-        <org.chromium.ui.widget.ChromeImageButton
-            android:id="@+id/offline_page_id"
-            style="@style/OverflowMenuButton"
-            android:src="@drawable/ic_file_download_white_24dp"
-            android:contentDescription="@string/download_page" />
-
-        <org.chromium.ui.widget.ChromeImageButton
-            android:id="@+id/info_menu_id"
-            style="@style/OverflowMenuButton"
-            android:src="@drawable/btn_info"
-            android:contentDescription="@string/accessibility_menu_info" />
-
-        <!-- The src will be set in onFinishInflate. -->
-        <org.chromium.ui.widget.ChromeImageButton
-            android:id="@+id/reload_menu_id"
-            style="@style/OverflowMenuButton"
-            android:contentDescription="@string/accessibility_btn_refresh"
-            tools:src="@drawable/btn_reload_stop" />
-    </LinearLayout>
-</org.chromium.chrome.browser.app.appmenu.AppMenuIconRowFooter>
diff --git a/chrome/android/java/res/layout/toolbar_space.xml b/chrome/android/java/res/layout/toolbar_space.xml
deleted file mode 100644
index b16e283..0000000
--- a/chrome/android/java/res/layout/toolbar_space.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2018 The Chromium Authors. All rights reserved.
-     Use of this source code is governed by a BSD-style license that can be
-     found in the LICENSE file. -->
-<merge xmlns:android="http://schemas.android.com/apk/res/android" >
-
-    <Space
-        android:layout_width="0dp"
-        android:layout_height="1dp"
-        android:layout_weight="1" />
-
-</merge>
-
-
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml
index 04937c1..5320f02 100644
--- a/chrome/android/java/res/values/dimens.xml
+++ b/chrome/android/java/res/values/dimens.xml
@@ -268,16 +268,8 @@
     <dimen name="tablet_toolbar_end_padding">6dp</dimen>
     <dimen name="toolbar_optional_button_animation_translation">10dp</dimen>
 
-    <!-- Bottom toolbar dimensions -->
-    <dimen name="bottom_toolbar_height">@dimen/min_touch_target_size</dimen>
-    <dimen name="bottom_toolbar_height_with_shadow">56dp</dimen>
-    <dimen name="labeled_bottom_toolbar_height">68dp</dimen>
-    <dimen name="labeled_bottom_toolbar_height_with_shadow">76dp</dimen>
-    <dimen name="bottom_toolbar_padding">12dp</dimen>
-    <dimen name="search_accelerator_width">64dp</dimen>
-    <dimen name="search_accelerator_height">36dp</dimen>
-    <dimen name="search_accelerator_height_padding">6dp</dimen>
-    <dimen name="search_accelerator_width_margin">4dp</dimen>
+    <!-- Bottom controls dimensions -->
+    <dimen name="bottom_controls_height">@dimen/min_touch_target_size</dimen>
 
     <!-- Start surface toolbar dimensions -->
     <dimen name="start_surface_toolbar_button_padding_to_button">8dp</dimen>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index 5208e21..1dbb48f9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -609,15 +609,11 @@
                     return;
                 }
 
-                if (getToolbarManager().isBottomToolbarVisible()) {
-                    RecordUserAction.record("MobileBottomToolbarTabSwitcherButtonInBrowsingView");
-                } else {
-                    Layout activeLayout = mLayoutManager.getActiveLayout();
-                    if (activeLayout instanceof StackLayout && !activeLayout.isHiding()) {
-                        RecordUserAction.record("MobileToolbarStackViewButtonInStackView");
-                    } else if (!isInOverviewMode()) {
-                        RecordUserAction.record("MobileToolbarStackViewButtonInBrowsingView");
-                    }
+                Layout activeLayout = mLayoutManager.getActiveLayout();
+                if (activeLayout instanceof StackLayout && !activeLayout.isHiding()) {
+                    RecordUserAction.record("MobileToolbarStackViewButtonInStackView");
+                } else if (!isInOverviewMode()) {
+                    RecordUserAction.record("MobileToolbarStackViewButtonInBrowsingView");
                 }
 
                 if (isInOverviewMode() && !StartSurfaceConfiguration.isStartSurfaceEnabled()) {
@@ -637,11 +633,7 @@
                 } else {
                     RecordUserAction.record("MobileToolbarStackViewNewTab");
                 }
-                if (getToolbarManager().isBottomToolbarVisible()) {
-                    RecordUserAction.record("MobileBottomToolbarNewTabButton");
-                } else {
-                    RecordUserAction.record("MobileTopToolbarNewTabButton");
-                }
+                RecordUserAction.record("MobileTopToolbarNewTabButton");
 
                 RecordUserAction.record("MobileNewTabOpened");
             };
@@ -1474,11 +1466,11 @@
         if (TabUiFeatureUtilities.isTabGroupsAndroidEnabled()) {
             dialogVisibilitySupplier = () -> {
                 assert mStartSurface != null;
-                assert getToolbarManager().getBottomToolbarCoordinator() != null;
+                assert getToolbarManager().getBottomControlsCoordinator() != null;
                 // Return true if dialog from either tab switcher or tab strip is visible.
                 Supplier<Boolean> tabGroupUiDialogVisibilitySupplier =
                         getToolbarManager()
-                                .getBottomToolbarCoordinator()
+                                .getBottomControlsCoordinator()
                                 .getTabGridDialogVisibilitySupplier();
                 Supplier<Boolean> tabSwitcherDialogVisibilitySupplier =
                         mStartSurface.getTabGridDialogVisibilitySupplier();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java
index 4db83c9..73d85c3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java
@@ -618,8 +618,7 @@
                 || mDecorView.getWidth()
                         < DeviceFormFactor.getNonMultiDisplayMinimumTabletWidthPx(mContext);
 
-        final boolean isMenuButtonOnTop =
-                mToolbarManager != null && !mToolbarManager.isMenuFromBottom();
+        final boolean isMenuButtonOnTop = mToolbarManager != null;
         shouldShowIconRow &= isMenuButtonOnTop;
         return shouldShowIconRow;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayoutBase.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayoutBase.java
index 31d5726..4465def 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayoutBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayoutBase.java
@@ -23,7 +23,6 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.base.supplier.ObservableSupplier;
-import org.chromium.chrome.R;
 import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider;
 import org.chromium.chrome.browser.browser_controls.BrowserControlsUtils;
 import org.chromium.chrome.browser.compositor.LayerTitleCache;
@@ -51,8 +50,6 @@
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabModelObserver;
 import org.chromium.chrome.browser.tabmodel.TabModelUtils;
-import org.chromium.chrome.browser.toolbar.bottom.BottomToolbarConfiguration;
-import org.chromium.ui.UiUtils;
 import org.chromium.ui.base.LocalizationUtils;
 import org.chromium.ui.resources.ResourceManager;
 
@@ -649,21 +646,11 @@
 
     @Override
     public void attachViews(ViewGroup container) {
-        if (BottomToolbarConfiguration.isBottomToolbarEnabled()) {
-            // In practice, the "container view" is used for animation. When Duet is enabled, the
-            // container is placed behind the bottom toolbar since it is persistent.
-            ViewGroup compositorViewHolder = container.findViewById(R.id.compositor_view_holder);
-            UiUtils.insertAfter((ViewGroup) compositorViewHolder.getParent(), mViewContainer,
-                    compositorViewHolder);
-            mViewContainer.getLayoutParams().width = LayoutParams.MATCH_PARENT;
-            mViewContainer.getLayoutParams().height = LayoutParams.MATCH_PARENT;
-        } else {
-            // TODO(dtrainor): This is a hack.  We're attaching to the parent of the view container
-            // which is the content container of the Activity.
-            ((ViewGroup) container.getParent())
-                    .addView(mViewContainer,
-                            new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
-        }
+        // TODO(dtrainor): This is a hack.  We're attaching to the parent of the view container
+        // which is the content container of the Activity.
+        ((ViewGroup) container.getParent())
+                .addView(mViewContainer,
+                        new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/feedback/ChromeFeedbackCollector.java b/chrome/android/java/src/org/chromium/chrome/browser/feedback/ChromeFeedbackCollector.java
index 58b2e10..fe1ada9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/feedback/ChromeFeedbackCollector.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/feedback/ChromeFeedbackCollector.java
@@ -56,7 +56,6 @@
         sources.add(new IMEFeedbackSource());
         sources.add(new PermissionFeedbackSource());
         sources.add(new FeedbackContextFeedbackSource(initParams.feedbackContext));
-        sources.add(new DuetFeedbackSource());
 
         return sources;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/feedback/DuetFeedbackSource.java b/chrome/android/java/src/org/chromium/chrome/browser/feedback/DuetFeedbackSource.java
deleted file mode 100644
index e0f0b23..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/feedback/DuetFeedbackSource.java
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.feedback;
-
-import org.chromium.chrome.browser.toolbar.bottom.BottomToolbarConfiguration;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/** Provides whether Duet is enabled for feedback reports. */
-public class DuetFeedbackSource implements FeedbackSource {
-    private static final String DUET_KEY = "Duet";
-    private static final String ENABLED_VALUE = "Enabled";
-    private static final String DISABLED_VALUE = "Disabled";
-
-    private final HashMap<String, String> mMap;
-
-    DuetFeedbackSource() {
-        mMap = new HashMap<>(1);
-        mMap.put(DUET_KEY,
-                BottomToolbarConfiguration.isBottomToolbarEnabled() ? ENABLED_VALUE
-                                                                    : DISABLED_VALUE);
-    }
-
-    @Override
-    public Map<String, String> getFeedback() {
-        return mMap;
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/homepage/settings/HomepageSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/homepage/settings/HomepageSettings.java
index 4e2976d..156eea0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/homepage/settings/HomepageSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/homepage/settings/HomepageSettings.java
@@ -20,7 +20,6 @@
 import org.chromium.chrome.browser.homepage.settings.RadioButtonGroupHomepagePreference.PreferenceValues;
 import org.chromium.chrome.browser.ntp.NewTabPage;
 import org.chromium.chrome.browser.settings.ChromeManagedPreferenceDelegate;
-import org.chromium.chrome.browser.toolbar.bottom.BottomToolbarVariationManager;
 import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
 import org.chromium.components.browser_ui.settings.SettingsUtils;
 import org.chromium.components.browser_ui.settings.TextMessagePreference;
@@ -51,8 +50,6 @@
         }
     }
 
-    private static boolean sIsHomeButtonOnBottomToolbar;
-
     private HomepageManager mHomepageManager;
     private Preference mHomepageEdit;
     private RadioButtonGroupHomepagePreference mRadioButtons;
@@ -80,16 +77,12 @@
         setupPreferenceVisibility();
 
         // Set up listeners and update the page.
-        if (isHomeButtonOnBottomToolbar()) {
-            homepageSwitch.setVisible(false);
-        } else {
-            boolean isHomepageEnabled = HomepageManager.isHomepageEnabled();
-            homepageSwitch.setChecked(isHomepageEnabled);
-            homepageSwitch.setOnPreferenceChangeListener((preference, newValue) -> {
-                onSwitchPreferenceChange((boolean) newValue);
-                return true;
-            });
-        }
+        boolean isHomepageEnabled = HomepageManager.isHomepageEnabled();
+        homepageSwitch.setChecked(isHomepageEnabled);
+        homepageSwitch.setOnPreferenceChangeListener((preference, newValue) -> {
+            onSwitchPreferenceChange((boolean) newValue);
+            return true;
+        });
 
         RecordUserAction.record("Settings.Homepage.Opened");
 
@@ -116,7 +109,7 @@
      */
     private void updatePreferenceState() {
         boolean isManagedByPolicy = HomepagePolicyManager.isHomepageManagedByPolicy();
-        mTextManaged.setVisible(isManagedByPolicy && isHomeButtonOnBottomToolbar());
+        mTextManaged.setVisible(false);
 
         if (isHomepageSettingsUIConversionEnabled()) {
             if (mRadioButtons != null) {
@@ -132,15 +125,6 @@
         return ChromeFeatureList.isEnabled(ChromeFeatureList.HOMEPAGE_SETTINGS_UI_CONVERSION);
     }
 
-    private boolean isHomeButtonOnBottomToolbar() {
-        return sIsHomeButtonOnBottomToolbar || BottomToolbarVariationManager.isHomeButtonOnBottom();
-    }
-
-    @VisibleForTesting
-    public static void setIsHomeButtonOnBottomToolbar(boolean isOnBottom) {
-        sIsHomeButtonOnBottomToolbar = isOnBottom;
-    }
-
     @Override
     public void onResume() {
         super.onResume();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/identity_disc/IdentityDiscController.java b/chrome/android/java/src/org/chromium/chrome/browser/identity_disc/IdentityDiscController.java
index 62c154b..941f9204 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/identity_disc/IdentityDiscController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/identity_disc/IdentityDiscController.java
@@ -9,7 +9,6 @@
 
 import androidx.annotation.DimenRes;
 import androidx.annotation.IntDef;
-import androidx.annotation.Nullable;
 
 import org.chromium.base.Callback;
 import org.chromium.base.ObserverList;
@@ -30,7 +29,6 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.toolbar.ButtonData;
 import org.chromium.chrome.browser.toolbar.ButtonDataProvider;
-import org.chromium.chrome.browser.toolbar.bottom.BottomToolbarVariationManager;
 import org.chromium.chrome.browser.user_education.IPHCommandBuilder;
 import org.chromium.components.feature_engagement.EventConstants;
 import org.chromium.components.feature_engagement.FeatureConstants;
@@ -67,12 +65,9 @@
     // Context is used for fetching resources and launching preferences page.
     private final Context mContext;
     private ActivityLifecycleDispatcher mActivityLifecycleDispatcher;
-    private final ObservableSupplier<Boolean> mBottomToolbarVisibilitySupplier;
     private final ObservableSupplier<Profile> mProfileSupplier;
     private final Callback<Profile> mProfileSupplierObserver = this::setProfile;
 
-    private @Nullable Callback<Boolean> mBottomToolbarVisibilityObserver;
-
     // We observe IdentityManager to receive primary account state change notifications.
     private IdentityManager mIdentityManager;
 
@@ -95,16 +90,12 @@
      * @param context The Context for retrieving resources, launching preference activiy, etc.
      * @param activityLifecycleDispatcher Dispatcher for activity lifecycle events, e.g. native
      *         initialization completing.
-     * @param bottomToolbarVisibilitySupplier Supplier that queries and updates the visibility of
-     *         the bottom toolbar.
      */
     public IdentityDiscController(Context context,
             ActivityLifecycleDispatcher activityLifecycleDispatcher,
-            ObservableSupplier<Boolean> bottomToolbarVisibilitySupplier,
             ObservableSupplier<Profile> profileSupplier) {
         mContext = context;
         mActivityLifecycleDispatcher = activityLifecycleDispatcher;
-        mBottomToolbarVisibilitySupplier = bottomToolbarVisibilitySupplier;
         mProfileSupplier = profileSupplier;
         mActivityLifecycleDispatcher.register(this);
 
@@ -133,10 +124,6 @@
         mNativeIsInitialized = true;
 
         mProfileSupplier.addObserver(mProfileSupplierObserver);
-
-        mBottomToolbarVisibilityObserver =
-                (bottomToolbarIsVisible) -> notifyObservers(getSyncAccountInfo() != null);
-        mBottomToolbarVisibilitySupplier.addObserver(mBottomToolbarVisibilityObserver);
     }
 
     @Override
@@ -157,7 +144,7 @@
             return mButtonData;
         }
 
-        calculateButtonData(mBottomToolbarVisibilitySupplier.get());
+        calculateButtonData();
         return mButtonData;
     }
 
@@ -167,11 +154,11 @@
             return mButtonData;
         }
 
-        calculateButtonData(false);
+        calculateButtonData();
         return mButtonData;
     }
 
-    private void calculateButtonData(boolean bottomToolbarVisible) {
+    private void calculateButtonData() {
         if (!mNativeIsInitialized) {
             assert !mButtonData.canShow;
             return;
@@ -179,12 +166,8 @@
 
         String email = CoreAccountInfo.getEmailFrom(getSyncAccountInfo());
         boolean canShowIdentityDisc = email != null;
-        boolean menuBottomOnBottom =
-                bottomToolbarVisible && BottomToolbarVariationManager.isMenuButtonOnBottom();
 
-        mState = !canShowIdentityDisc
-                ? IdentityDiscState.NONE
-                : menuBottomOnBottom ? IdentityDiscState.LARGE : IdentityDiscState.SMALL;
+        mState = !canShowIdentityDisc ? IdentityDiscState.NONE : IdentityDiscState.SMALL;
         ensureProfileDataCache(email, mState);
 
         if (mState != IdentityDiscState.NONE) {
@@ -288,11 +271,6 @@
             mIdentityManager = null;
         }
 
-        if (mBottomToolbarVisibilityObserver != null) {
-            mBottomToolbarVisibilitySupplier.removeObserver(mBottomToolbarVisibilityObserver);
-            mBottomToolbarVisibilityObserver = null;
-        }
-
         if (mNativeIsInitialized) {
             mProfileSupplier.removeObserver(mProfileSupplierObserver);
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
index ffc7e4f..039187a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
@@ -31,7 +31,6 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ActivityTabProvider;
 import org.chromium.chrome.browser.ActivityTabProvider.ActivityTabTabObserver;
-import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.browser.compositor.layouts.EmptyOverviewModeObserver;
 import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior;
 import org.chromium.chrome.browser.document.ChromeIntentUtil;
@@ -52,7 +51,9 @@
 import org.chromium.chrome.browser.share.ShareDelegate;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabSelectionType;
+import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelUtils;
+import org.chromium.chrome.browser.tabmodel.TabWindowManager;
 import org.chromium.chrome.browser.toolbar.ToolbarDataProvider;
 import org.chromium.components.embedder_support.util.UrlConstants;
 import org.chromium.components.query_tiles.QueryTile;
@@ -579,7 +580,8 @@
     @Override
     public void onSwitchToTab(OmniboxSuggestion suggestion, int position) {
         Tab tab = mAutocomplete.findMatchingTabWithUrl(suggestion.getUrl());
-        if (tab == null) {
+        TabWindowManager tabWindowManager = TabWindowManager.getInstance();
+        if (tab == null || tabWindowManager == null) {
             onSelection(suggestion, position);
             return;
         }
@@ -589,16 +591,11 @@
         // animation since Android will show the animation for switching apps.
         if (tab.getWindowAndroid().getActivityState() != ActivityState.STOPPED
                 && tab.getWindowAndroid().getActivityState() != ActivityState.DESTROYED) {
-            // TODO(1097292):  Do not use Activity to get TabModelSelector.
-            assert tab.getWindowAndroid().getActivity().get() instanceof ChromeActivity;
+            TabModel tabModel = tabWindowManager.getTabModelForTab(tab);
+            assert tabModel != null;
 
-            ChromeActivity chromeActivity =
-                    (ChromeActivity) tab.getWindowAndroid().getActivity().get();
-            int tabIndex = TabModelUtils.getTabIndexById(
-                    chromeActivity.getTabModelSelector().getModel(tab.isIncognito()), tab.getId());
-            chromeActivity.getTabModelSelector()
-                    .getModel(tab.isIncognito())
-                    .setIndex(tabIndex, TabSelectionType.FROM_OMNIBOX);
+            int tabIndex = TabModelUtils.getTabIndexById(tabModel, tab.getId());
+            tabModel.setIndex(tabIndex, TabSelectionType.FROM_OMNIBOX);
         } else {
             // Browser is in background, bring to to foreground and switch to the tab.
             Intent newIntent = ChromeIntentUtil.createBringTabToFrontIntent(tab.getId());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/share/ShareButtonController.java b/chrome/android/java/src/org/chromium/chrome/browser/share/ShareButtonController.java
index 0ce4329..b7fbdae 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/share/ShareButtonController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/share/ShareButtonController.java
@@ -8,10 +8,8 @@
 import android.content.res.Configuration;
 import android.view.View.OnClickListener;
 
-import androidx.annotation.Nullable;
 import androidx.appcompat.content.res.AppCompatResources;
 
-import org.chromium.base.Callback;
 import org.chromium.base.ObserverList;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.base.supplier.ObservableSupplier;
@@ -23,7 +21,6 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.toolbar.ButtonData;
 import org.chromium.chrome.browser.toolbar.ButtonDataProvider;
-import org.chromium.chrome.browser.toolbar.bottom.BottomToolbarVariationManager;
 import org.chromium.ui.modaldialog.ModalDialogManager;
 import org.chromium.ui.modaldialog.ModalDialogManager.ModalDialogManagerObserver;
 import org.chromium.ui.modelutil.PropertyModel;
@@ -52,7 +49,6 @@
 
     private ButtonData mButtonData;
     private ObserverList<ButtonDataObserver> mObservers = new ObserverList<>();
-    private final ObservableSupplier<Boolean> mBottomToolbarVisibilitySupplier;
     private OnClickListener mOnClickListener;
 
     private ModalDialogManager mModalDialogManager;
@@ -63,31 +59,21 @@
 
     private int mCurrentOrientation;
 
-    private @Nullable Callback<Boolean> mBottomToolbarVisibilityObserver;
-
     /**
      * Creates ShareButtonController object.
      * @param context The Context for retrieving resources, etc.
      * @param tabProvider The {@link ActivityTabProvider} used for accessing the tab.
      * @param shareDelegateSupplier The supplier to get a handle on the share delegate.
      * @param shareUtils The share utility functions used by this class.
-     * @param bottomToolbarVisibilitySupplier Supplier that queries and updates the visibility of
-     * the bottom toolbar.
      * @param activityLifecycleDispatcher Dispatcher for activity lifecycle events, e.g.
      * configuration changes.
      * @param modalDialogManager dispatcher for modal lifecycles events
      */
     public ShareButtonController(Context context, ActivityTabProvider tabProvider,
             ObservableSupplier<ShareDelegate> shareDelegateSupplier, ShareUtils shareUtils,
-            ObservableSupplier<Boolean> bottomToolbarVisibilitySupplier,
             ActivityLifecycleDispatcher activityLifecycleDispatcher,
             ModalDialogManager modalDialogManager) {
         mContext = context;
-        mBottomToolbarVisibilitySupplier = bottomToolbarVisibilitySupplier;
-        mBottomToolbarVisibilityObserver = (bottomToolbarIsVisible)
-                -> notifyObservers(!(bottomToolbarIsVisible
-                        && BottomToolbarVariationManager.isShareButtonOnBottom()));
-        mBottomToolbarVisibilitySupplier.addObserver(mBottomToolbarVisibilityObserver);
 
         mActivityLifecycleDispatcher = activityLifecycleDispatcher;
         mActivityLifecycleDispatcher.register(this);
@@ -146,10 +132,6 @@
             mActivityLifecycleDispatcher.unregister(this);
             mActivityLifecycleDispatcher = null;
         }
-        if (mBottomToolbarVisibilityObserver != null) {
-            mBottomToolbarVisibilitySupplier.removeObserver(mBottomToolbarVisibilityObserver);
-            mBottomToolbarVisibilityObserver = null;
-        }
         if (mModalDialogManagerObserver != null && mModalDialogManager != null) {
             mModalDialogManager.removeObserver(mModalDialogManagerObserver);
             mModalDialogManagerObserver = null;
@@ -188,9 +170,7 @@
 
         boolean isDeviceWideEnough = mScreenWidthDp > mMinimumWidthDp;
 
-        if ((mBottomToolbarVisibilitySupplier.get()
-                    && BottomToolbarVariationManager.isShareButtonOnBottom())
-                || mShareDelegateSupplier.get() == null || !isDeviceWideEnough) {
+        if (mShareDelegateSupplier.get() == null || !isDeviceWideEnough) {
             mButtonData.canShow = false;
             return;
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegate.java
index f214587b..2540e554 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegate.java
@@ -11,7 +11,6 @@
 import androidx.annotation.Nullable;
 
 import org.chromium.chrome.browser.device.DeviceClassManager;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.util.ChromeAccessibilityUtil;
 import org.chromium.components.browser_ui.util.BrowserControlsVisibilityDelegate;
 import org.chromium.components.dom_distiller.core.DomDistillerUrlUtils;
@@ -171,12 +170,6 @@
     }
 
     private boolean enableHidingBrowserControls() {
-        if (ChromeFeatureList.isEnabled(ChromeFeatureList.DONT_AUTO_HIDE_BROWSER_CONTROLS)
-                && mTab.getActivity() != null && mTab.getActivity().getToolbarManager() != null
-                && mTab.getActivity().getToolbarManager().getBottomToolbarCoordinator() != null) {
-            return false;
-        }
-
         WebContents webContents = mTab.getWebContents();
         if (webContents == null || webContents.isDestroyed()) return false;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegate.java
index caf0d329..082fcb0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegate.java
@@ -44,10 +44,6 @@
         mAppMenuDelegate = appMenuDelegate;
     }
 
-    private boolean isMenuButtonInBottomToolbar() {
-        return mToolbarManager != null && mToolbarManager.isMenuFromBottom();
-    }
-
     private boolean shouldShowDataSaverMenuItem() {
         return (mOverviewModeBehavior == null || !mOverviewModeBehavior.overviewVisible())
                 && DataReductionProxySettings.getInstance().shouldUseDataReductionMainMenuItem();
@@ -55,9 +51,6 @@
 
     @Override
     public int getFooterResourceId() {
-        if (isMenuButtonInBottomToolbar()) {
-            return this.shouldShowPageMenu() ? R.layout.icon_row_menu_footer : 0;
-        }
         return shouldShowDataSaverMenuItem() ? R.layout.data_reduction_main_menu_item : 0;
     }
 
@@ -72,9 +65,6 @@
 
     @Override
     public int getHeaderResourceId() {
-        if (isMenuButtonInBottomToolbar()) {
-            return shouldShowDataSaverMenuItem() ? R.layout.data_reduction_main_menu_item : 0;
-        }
         return 0;
     }
 
@@ -87,7 +77,6 @@
 
     @Override
     public boolean shouldShowFooter(int maxMenuHeight) {
-        if (isMenuButtonInBottomToolbar()) return true;
         if (shouldShowDataSaverMenuItem()) {
             return canShowDataReductionItem(maxMenuHeight);
         }
@@ -95,19 +84,6 @@
     }
 
     @Override
-    public boolean shouldShowHeader(int maxMenuHeight) {
-        if (!isMenuButtonInBottomToolbar()) {
-            return super.shouldShowHeader(maxMenuHeight);
-        }
-
-        if (DataReductionProxySettings.getInstance().shouldUseDataReductionMainMenuItem()) {
-            return canShowDataReductionItem(maxMenuHeight);
-        }
-
-        return super.shouldShowHeader(maxMenuHeight);
-    }
-
-    @Override
     protected boolean shouldShowManagedByMenuItem(Tab currentTab) {
         return CachedFeatureFlags.isEnabled(ChromeFeatureList.ANDROID_MANAGED_BY_MENU_ITEM)
                 && ManagedBrowserUtils.hasBrowserPoliciesApplied(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
index 12e10ba4..60c7f801 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
@@ -47,7 +47,6 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tasks.tab_management.TabUiFeatureUtilities;
 import org.chromium.chrome.browser.toolbar.ToolbarButtonInProductHelpController;
-import org.chromium.chrome.browser.toolbar.bottom.BottomToolbarConfiguration;
 import org.chromium.chrome.browser.ui.RootUiCoordinator;
 import org.chromium.chrome.browser.ui.appmenu.AppMenuHandler;
 import org.chromium.chrome.browser.ui.default_browser_promo.DefaultBrowserPromoUtils;
@@ -248,10 +247,9 @@
     protected void initializeToolbar() {
         super.initializeToolbar();
         if (!mActivity.isTablet()
-                && (BottomToolbarConfiguration.isBottomToolbarEnabled()
-                        || TabUiFeatureUtilities.isTabGroupsAndroidEnabled()
+                && (TabUiFeatureUtilities.isTabGroupsAndroidEnabled()
                         || TabUiFeatureUtilities.isConditionalTabStripEnabled())) {
-            getToolbarManager().enableBottomToolbar();
+            getToolbarManager().enableBottomControls();
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabWindowManager.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabWindowManager.java
index 6b53eb2..f8d08f5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabWindowManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabWindowManager.java
@@ -171,6 +171,24 @@
     }
 
     /**
+     * @param tab The tab to look for in each model.
+     * @return The TabModel containing the given Tab or null if one doesn't exist.
+     **/
+    public TabModel getTabModelForTab(Tab tab) {
+        if (tab == null) return null;
+
+        for (int i = 0; i < mSelectors.size(); i++) {
+            TabModelSelector selector = mSelectors.get(i);
+            if (selector != null) {
+                TabModel tabModel = selector.getModelForTabId(tab.getId());
+                if (tabModel != null) return tabModel;
+            }
+        }
+
+        return null;
+    }
+
+    /**
      * @param tabId The ID of the tab in question.
      * @return Specified {@link Tab} or {@code null} if the {@link Tab} is not found.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/HomeButton.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/HomeButton.java
index eec0ac33..3a08a2e0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/HomeButton.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/HomeButton.java
@@ -35,7 +35,6 @@
 import org.chromium.chrome.browser.settings.SettingsLauncher;
 import org.chromium.chrome.browser.settings.SettingsLauncherImpl;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.toolbar.bottom.BottomToolbarConfiguration;
 import org.chromium.ui.widget.ChromeImageButton;
 
 /**
@@ -247,7 +246,7 @@
     }
 
     private void updateContextMenuListener() {
-        if (!BottomToolbarConfiguration.isBottomToolbarEnabled() && !isManagedByPolicy()) {
+        if (!isManagedByPolicy()) {
             setOnCreateContextMenuListener(this);
         } else {
             setOnCreateContextMenuListener(null);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonCoordinator.java
index 84c254c..9280207 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonCoordinator.java
@@ -9,9 +9,6 @@
 
 import org.chromium.chrome.browser.ThemeColorProvider;
 import org.chromium.chrome.browser.ThemeColorProvider.TintObserver;
-import org.chromium.chrome.browser.compositor.layouts.EmptyOverviewModeObserver;
-import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior;
-import org.chromium.chrome.browser.compositor.layouts.OverviewModeState;
 import org.chromium.chrome.browser.toolbar.TabCountProvider.TabCountObserver;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
@@ -35,15 +32,6 @@
     private TabCountProvider mTabCountProvider;
     private TabCountObserver mTabCountObserver;
 
-    /** The {@link OverviewModeBehavior} used to observe overview state changes.  */
-    private OverviewModeBehavior mOverviewModeBehavior;
-
-    /** The {@link OvervieModeObserver} observing the OverviewModeBehavior  */
-    private OverviewModeBehavior.OverviewModeObserver mOverviewModeObserver;
-
-    @OverviewModeState
-    private int mOverviewModeState;
-
     /**
      * Build the controller that manages the tab switcher button.
      * @param view The {@link TabSwitcherButtonView} the controller manages.
@@ -51,16 +39,6 @@
     public TabSwitcherButtonCoordinator(TabSwitcherButtonView view) {
         PropertyModelChangeProcessor.create(
                 mTabSwitcherButtonModel, view, new TabSwitcherButtonViewBinder());
-        mOverviewModeObserver = new EmptyOverviewModeObserver() {
-            @Override
-            public void onOverviewModeStateChanged(
-                    @OverviewModeState int overviewModeState, boolean showTabSwitcherToolbar) {
-                mOverviewModeState = overviewModeState;
-                updateButtonState();
-            }
-        };
-
-        mOverviewModeState = OverviewModeState.NOT_SHOWN;
     }
 
     /**
@@ -96,12 +74,6 @@
         mTabCountProvider.addObserverAndTrigger(mTabCountObserver);
     }
 
-    public void setOverviewModeBehavior(OverviewModeBehavior overviewModeBehavior) {
-        assert overviewModeBehavior != null;
-        mOverviewModeBehavior = overviewModeBehavior;
-        mOverviewModeBehavior.addOverviewModeObserver(mOverviewModeObserver);
-    }
-
     public void destroy() {
         if (mThemeColorProvider != null) {
             mThemeColorProvider.removeTintObserver(mTintObserver);
@@ -111,20 +83,11 @@
             mTabCountProvider.removeObserver(mTabCountObserver);
             mTabCountProvider = null;
         }
-        if (mOverviewModeBehavior != null) {
-            mOverviewModeBehavior.removeOverviewModeObserver(mOverviewModeObserver);
-            mOverviewModeObserver = null;
-        }
     }
 
     private void updateButtonState() {
-        // TODO(crbug.com/1039997): match SHOWN_HOMEPAGE instead.
-        boolean shouldEnable = mOverviewModeState != OverviewModeState.SHOWN_TABSWITCHER
-                && mOverviewModeState != OverviewModeState.SHOWN_TABSWITCHER_TASKS_ONLY
-                && mOverviewModeState != OverviewModeState.SHOWN_TABSWITCHER_OMNIBOX_ONLY
-                && mOverviewModeState != OverviewModeState.SHOWN_TABSWITCHER_TWO_PANES
-                && mOverviewModeState != OverviewModeState.SHOWN_TABSWITCHER_TRENDY_TERMS
-                && mTabSwitcherButtonModel.get(TabSwitcherButtonProperties.NUMBER_OF_TABS) >= 1;
+        boolean shouldEnable =
+                mTabSwitcherButtonModel.get(TabSwitcherButtonProperties.NUMBER_OF_TABS) >= 1;
         mTabSwitcherButtonModel.set(TabSwitcherButtonProperties.IS_ENABLED, shouldEnable);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarButtonInProductHelpController.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarButtonInProductHelpController.java
index 7407ffb..9464932 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarButtonInProductHelpController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarButtonInProductHelpController.java
@@ -24,7 +24,6 @@
 import org.chromium.chrome.browser.previews.Previews;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.toolbar.bottom.BottomToolbarConfiguration;
 import org.chromium.chrome.browser.translate.TranslateBridge;
 import org.chromium.chrome.browser.translate.TranslateUtils;
 import org.chromium.chrome.browser.ui.appmenu.AppMenuCoordinator;
@@ -191,8 +190,7 @@
 
     // Private methods.
     private static int getDataReductionMenuItemHighlight() {
-        return BottomToolbarConfiguration.isBottomToolbarEnabled() ? R.id.data_reduction_menu_item
-                                                                   : R.id.app_menu_footer;
+        return R.id.app_menu_footer;
     }
 
     // Attempts to show an IPH text bubble for data saver detail.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
index 108320b5..81827c7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -20,11 +20,9 @@
 import androidx.annotation.VisibleForTesting;
 import androidx.appcompat.app.ActionBar;
 
-import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.Callback;
 import org.chromium.base.TraceEvent;
 import org.chromium.base.supplier.ObservableSupplier;
-import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.base.supplier.Supplier;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ActivityTabProvider;
@@ -72,13 +70,7 @@
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver;
-import org.chromium.chrome.browser.tasks.tab_management.TabGroupPopupUi;
-import org.chromium.chrome.browser.tasks.tab_management.TabManagementModuleProvider;
-import org.chromium.chrome.browser.tasks.tab_management.TabUiFeatureUtilities;
 import org.chromium.chrome.browser.toolbar.bottom.BottomControlsCoordinator;
-import org.chromium.chrome.browser.toolbar.bottom.BottomTabSwitcherActionMenuCoordinator;
-import org.chromium.chrome.browser.toolbar.bottom.BottomToolbarConfiguration;
-import org.chromium.chrome.browser.toolbar.bottom.BottomToolbarVariationManager;
 import org.chromium.chrome.browser.toolbar.load_progress.LoadProgressCoordinator;
 import org.chromium.chrome.browser.toolbar.menu_button.MenuButtonCoordinator;
 import org.chromium.chrome.browser.toolbar.top.ActionModeController;
@@ -142,8 +134,6 @@
 
     private LayoutManager mLayoutManager;
     private final ObservableSupplier<ShareDelegate> mShareDelegateSupplier;
-    private ObservableSupplierImpl<View> mTabGroupPopUiParentSupplier;
-    private @Nullable TabGroupPopupUi mTabGroupPopupUi;
 
     private TabObserver mTabObserver;
     private BookmarkBridge.BookmarkModelObserver mBookmarksObserver;
@@ -183,9 +173,6 @@
     private boolean mShouldUpdateToolbarPrimaryColor = true;
     private int mCurrentThemeColor;
 
-    private boolean mIsBottomToolbarVisible;
-
-
     private int mCurrentOrientation;
 
     private final Supplier<Boolean> mCanAnimateNativeBrowserControls;
@@ -194,9 +181,6 @@
      * Runnable for the home and search accelerator button when Start Surface home page is enabled.
      */
     private Supplier<Boolean> mShowStartSurfaceSupplier;
-    // TODO(https://crbug.com/865801): Consolidate isBottomToolbarVisible(),
-    // onBottomToolbarVisibilityChanged, etc. to all use mBottomToolbarVisibilitySupplier.
-    private final ObservableSupplierImpl<Boolean> mBottomToolbarVisibilitySupplier;
     private final ScrimCoordinator mScrimCoordinator;
 
     /**
@@ -209,7 +193,6 @@
      * @param themeColorProvider The ThemeColorProvider object.
      * @param tabObscuringHandler Delegate object handling obscuring views.
      * @param shareDelegateSupplier Supplier for ShareDelegate.
-     * @param bottomToolbarVisibilitySupplier
      * @param identityDiscController The controller that coordinates the state of the identity disc
      * @param buttonDataProviders The list of button data providers for the optional toolbar button
      *         in the browsing mode toolbar, given in precedence order.
@@ -229,7 +212,6 @@
             Invalidator invalidator, Callback<Boolean> urlFocusChangedCallback,
             ThemeColorProvider themeColorProvider, TabObscuringHandler tabObscuringHandler,
             ObservableSupplier<ShareDelegate> shareDelegateSupplier,
-            ObservableSupplierImpl<Boolean> bottomToolbarVisibilitySupplier,
             IdentityDiscController identityDiscController,
             List<ButtonDataProvider> buttonDataProviders, ActivityTabProvider tabProvider,
             ScrimCoordinator scrimCoordinator, ToolbarActionModeCallback toolbarActionModeCallback,
@@ -245,7 +227,6 @@
         mFullscreenManager = fullscreenManager;
         mActionBarDelegate = new ViewShiftingActionBarDelegate(activity, controlContainer);
         mShareDelegateSupplier = shareDelegateSupplier;
-        mBottomToolbarVisibilitySupplier = bottomToolbarVisibilitySupplier;
         mCanAnimateNativeBrowserControls = canAnimateNativeBrowserControls;
         mScrimCoordinator = scrimCoordinator;
 
@@ -296,7 +277,6 @@
         mActivityTabProvider = tabProvider;
         mToolbarTabController = new ToolbarTabControllerImpl(mLocationBarModel::getTab,
                 // clang-format off
-                this::isBottomToolbarVisible,
                 () -> mShowStartSurfaceSupplier != null && mShowStartSurfaceSupplier.get(),
                 mProfileSupplier, () -> mBottomControlsCoordinator, this::updateButtonStatus);
         // clang-format on
@@ -571,17 +551,6 @@
             public void onOverviewModeStartedShowing(boolean showToolbar) {
                 mToolbar.setTabSwitcherMode(true, showToolbar, false);
                 updateButtonStatus();
-                if (BottomToolbarConfiguration.isBottomToolbarEnabled()
-                        && !BottomToolbarVariationManager
-                                    .shouldBottomToolbarBeVisibleInOverviewMode()) {
-                    // TODO(crbug.com/1036474): don't tell mToolbar the visibility of bottom toolbar
-                    // has been changed when entering overview, so it won't draw extra animations
-                    // or buttons during the transition. Before, bottom toolbar was always visible
-                    // in portrait mode, so the visibility was equivalent to the orientation change.
-                    // We may have to tell mToolbar extra information, like orientation, in future.
-                    // mToolbar.onBottomToolbarVisibilityChanged(false);
-                    mBottomControlsCoordinator.setBottomControlsVisible(false);
-                }
             }
 
             @Override
@@ -595,17 +564,6 @@
             public void onOverviewModeStartedHiding(boolean showToolbar, boolean delayAnimation) {
                 mToolbar.setTabSwitcherMode(false, showToolbar, delayAnimation);
                 updateButtonStatus();
-                if (BottomToolbarConfiguration.isBottomToolbarEnabled()
-                        && !BottomToolbarVariationManager
-                                    .shouldBottomToolbarBeVisibleInOverviewMode()) {
-                    // User may enter overview mode in landscape mode but exit in portrait mode.
-
-                    boolean isBottomToolbarVisible =
-                            !BottomToolbarConfiguration.isAdaptiveToolbarEnabled()
-                            || mActivity.getResources().getConfiguration().orientation
-                                    != Configuration.ORIENTATION_LANDSCAPE;
-                    setBottomToolbarVisible(isBottomToolbarVisible);
-                }
             }
 
             @Override
@@ -669,48 +627,24 @@
     }
 
     /**
-     * Enable the bottom toolbar.
+     * Enable the bottom controls.
      */
-    public void enableBottomToolbar() {
-        if (TabUiFeatureUtilities.isDuetTabStripIntegrationAndroidEnabled()
-                && BottomToolbarConfiguration.isBottomToolbarEnabled()) {
-            mTabGroupPopUiParentSupplier = new ObservableSupplierImpl<>();
-            mTabGroupPopupUi = TabManagementModuleProvider.getDelegate().createTabGroupPopUi(
-                    mAppThemeColorProvider, mTabGroupPopUiParentSupplier);
-        }
-
+    public void enableBottomControls() {
         mBottomControlsCoordinator =
                 new BottomControlsCoordinator(mBrowserControlsSizer, mFullscreenManager,
                         mActivity.findViewById(R.id.bottom_controls_stub), mActivityTabProvider,
-                        mTabGroupPopupUi != null
-                                ? mTabGroupPopupUi.getLongClickListenerForTriggering()
-                                : BottomTabSwitcherActionMenuCoordinator.createOnLongClickListener(
-                                        id -> mActivity.onOptionsItemSelected(id, null)),
                         mAppThemeColorProvider, mShareDelegateSupplier,
                         mMenuButtonCoordinator.getMenuButtonHelperSupplier(),
                         mShowStartSurfaceSupplier, mToolbarTabController::openHomepage,
                         (reason)
                                 -> setUrlBarFocus(true, reason),
                         mOverviewModeBehaviorSupplier, mScrimCoordinator);
-
-        boolean isBottomToolbarVisible = BottomToolbarConfiguration.isBottomToolbarEnabled()
-                && (!BottomToolbarConfiguration.isAdaptiveToolbarEnabled()
-                        || mActivity.getResources().getConfiguration().orientation
-                                != Configuration.ORIENTATION_LANDSCAPE);
-        setBottomToolbarVisible(isBottomToolbarVisible);
     }
 
     /**
-     * @return Whether the bottom toolbar is visible.
+     * @return The coordinator for the bottom controls if it exists.
      */
-    public boolean isBottomToolbarVisible() {
-        return mIsBottomToolbarVisible;
-    }
-
-    /**
-     * @return The coordinator for the bottom toolbar if it exists.
-     */
-    public BottomControlsCoordinator getBottomToolbarCoordinator() {
+    public BottomControlsCoordinator getBottomControlsCoordinator() {
         return mBottomControlsCoordinator;
     }
 
@@ -734,15 +668,9 @@
 
         mShowStartSurfaceSupplier = showStartSurfaceSupplier;
 
-        OnLongClickListener tabSwitcherLongClickHandler = null;
-
-        if (mTabGroupPopupUi != null) {
-            tabSwitcherLongClickHandler = mTabGroupPopupUi.getLongClickListenerForTriggering();
-        } else {
-            tabSwitcherLongClickHandler =
-                    TabSwitcherActionMenuCoordinator.createOnLongClickListener(
-                            (id) -> mActivity.onOptionsItemSelected(id, null));
-        }
+        OnLongClickListener tabSwitcherLongClickHandler =
+                TabSwitcherActionMenuCoordinator.createOnLongClickListener(
+                        (id) -> mActivity.onOptionsItemSelected(id, null));
 
         mToolbar.initializeWithNative(tabModelSelector, layoutManager, tabSwitcherClickHandler,
                 tabSwitcherLongClickHandler, newTabClickHandler, bookmarkClickHandler,
@@ -761,14 +689,6 @@
             }
         });
 
-        if (mTabGroupPopupUi != null) {
-            mTabGroupPopUiParentSupplier.set(
-                    mIsBottomToolbarVisible && BottomToolbarVariationManager.isTabSwitcherOnBottom()
-                            ? mActivity.findViewById(R.id.bottom_controls)
-                            : mActivity.findViewById(R.id.toolbar));
-            mTabGroupPopupUi.initializeWithNative(mActivity);
-        }
-
         mLocationBarModel.initializeWithNative();
 
 
@@ -792,10 +712,6 @@
                     newTabClickHandler, mActivity.getWindowAndroid(), mTabCountProvider,
                     mIncognitoStateProvider, mActivity.findViewById(R.id.control_container),
                     closeAllTabsAction);
-
-            // Allow the bottom toolbar to be focused in accessibility after the top toolbar.
-            ApiCompatibilityUtils.setAccessibilityTraversalBefore(
-                    mLocationBar.getContainerView(), R.id.bottom_toolbar);
         }
 
         TemplateUrlServiceFactory.get().runWhenLoaded(this::registerTemplateUrlObserver);
@@ -836,11 +752,6 @@
         return mToolbar.getMenuButton();
     }
 
-    @Override
-    public boolean isMenuFromBottom() {
-        return mIsBottomToolbarVisible && BottomToolbarVariationManager.isMenuButtonOnBottom();
-    }
-
     /**
      * TODO(twellington): Try to remove this method. It's only used to return an in-product help
      *                    bubble anchor view... which should be moved out of tab and perhaps into
@@ -921,11 +832,6 @@
             mLocationBar.removeUrlFocusChangeListener(this);
         }
 
-        if (mTabGroupPopupUi != null) {
-            mTabGroupPopupUi.destroy();
-            mTabGroupPopupUi = null;
-        }
-
         mToolbar.removeUrlExpansionObserver(mActivity.getStatusBarColorController());
         mToolbar.destroy();
 
@@ -984,24 +890,6 @@
      */
     private void onOrientationChange(int newOrientation) {
         if (mActionModeController != null) mActionModeController.showControlsOnOrientationChange();
-
-        if (mBottomControlsCoordinator != null
-                && BottomToolbarConfiguration.isBottomToolbarEnabled()
-                && BottomToolbarConfiguration.isAdaptiveToolbarEnabled()) {
-            boolean isBottomToolbarVisible = newOrientation != Configuration.ORIENTATION_LANDSCAPE;
-            if (!BottomToolbarVariationManager.shouldBottomToolbarBeVisibleInOverviewMode()
-                    && isBottomToolbarVisible) {
-                isBottomToolbarVisible = !mActivity.isInOverviewMode();
-            }
-            setBottomToolbarVisible(isBottomToolbarVisible);
-
-            if (mTabGroupPopupUi != null) {
-                mTabGroupPopUiParentSupplier.set(mIsBottomToolbarVisible
-                                        && BottomToolbarVariationManager.isTabSwitcherOnBottom()
-                                ? mActivity.findViewById(R.id.bottom_controls)
-                                : mActivity.findViewById(R.id.toolbar));
-            }
-        }
     }
 
     @Override
@@ -1247,7 +1135,7 @@
         mToolbar.updateForwardButtonVisibility(currentTab != null && currentTab.canGoForward());
         updateReloadState(tabCrashed);
         updateBookmarkButtonStatus();
-        if (mToolbar.getMenuButtonWrapper() != null && !isBottomToolbarVisible()) {
+        if (mToolbar.getMenuButtonWrapper() != null) {
             mToolbar.getMenuButtonWrapper().setVisibility(View.VISIBLE);
         }
     }
@@ -1356,13 +1244,6 @@
         if (updateUrl) updateButtonStatus();
     }
 
-    private void setBottomToolbarVisible(boolean visible) {
-        mIsBottomToolbarVisible = visible;
-        mToolbar.onBottomToolbarVisibilityChanged(visible);
-        mBottomToolbarVisibilitySupplier.set(visible);
-        mBottomControlsCoordinator.setBottomControlsVisible(visible);
-    }
-
     /**
      * @param enabled Whether the progress bar is enabled.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarTabControllerImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarTabControllerImpl.java
index 9ad544c..6a9508f5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarTabControllerImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarTabControllerImpl.java
@@ -27,7 +27,6 @@
  */
 public class ToolbarTabControllerImpl implements ToolbarTabController {
     private final Supplier<Tab> mTabSupplier;
-    private final Supplier<Boolean> mBottomToolbarVisibilityPredicate;
     private final Supplier<Boolean> mOverrideHomePageSupplier;
     private final Supplier<Profile> mProfileSupplier;
     private final Supplier<BottomControlsCoordinator> mBottomControlsCoordinatorSupplier;
@@ -36,8 +35,6 @@
     /**
      *
      * @param tabSupplier Supplier for the currently active tab.
-     * @param bottomToolbarVisibilityPredicate Predicate that tells us if the bottom toolbar is
-     *         visible.
      * @param overrideHomePageSupplier Supplier that returns true if it overrides the default
      *         homepage behavior.
      * @param profileSupplier Supplier for the current profile.
@@ -46,12 +43,10 @@
      *         perform the action or for openHompage.
      */
     public ToolbarTabControllerImpl(Supplier<Tab> tabSupplier,
-            Supplier<Boolean> bottomToolbarVisibilityPredicate,
             Supplier<Boolean> overrideHomePageSupplier, Supplier<Profile> profileSupplier,
             Supplier<BottomControlsCoordinator> bottomControlsCoordinatorSupplier,
             Runnable onSuccessRunnable) {
         mTabSupplier = tabSupplier;
-        mBottomToolbarVisibilityPredicate = bottomToolbarVisibilityPredicate;
         mOverrideHomePageSupplier = overrideHomePageSupplier;
         mProfileSupplier = profileSupplier;
         mBottomControlsCoordinatorSupplier = bottomControlsCoordinatorSupplier;
@@ -103,16 +98,7 @@
     @Override
     public void openHomepage() {
         RecordUserAction.record("Home");
-
-        if (mBottomToolbarVisibilityPredicate.get()) {
-            RecordUserAction.record("MobileBottomToolbarHomeButton");
-        } else {
-            RecordUserAction.record("MobileTopToolbarHomeButton");
-        }
-
-        if (mOverrideHomePageSupplier.get()) {
-            return;
-        }
+        if (mOverrideHomePageSupplier.get()) return;
         Tab currentTab = mTabSupplier.get();
         if (currentTab == null) return;
         String homePageUrl = HomepageManager.getHomepageUri();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsCoordinator.java
index fdca805..b59b3b2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsCoordinator.java
@@ -7,7 +7,6 @@
 import android.annotation.SuppressLint;
 import android.view.View;
 import android.view.View.OnClickListener;
-import android.view.View.OnLongClickListener;
 import android.view.ViewGroup;
 import android.view.ViewStub;
 
@@ -58,7 +57,6 @@
     private final BottomControlsMediator mMediator;
 
     /** The coordinator for the split toolbar's bottom toolbar component. */
-    private @Nullable BottomToolbarCoordinator mBottomToolbarCoordinator;
     private @Nullable TabGroupUi mTabGroupUi;
 
     /**
@@ -68,7 +66,6 @@
      * @param fullscreenManager A {@link FullscreenManager} to listen for fullscreen changes.
      * @param stub The bottom controls {@link ViewStub} to inflate.
      * @param tabProvider
-     * @param tabSwitcherLongclickListener
      * @param themeColorProvider The {@link ThemeColorProvider} for the bottom toolbar.
      * @param shareDelegateSupplier The supplier for the {@link ShareDelegate} the bottom controls
      *         should use to share content.
@@ -83,7 +80,7 @@
     @SuppressLint("CutPasteId") // Not actually cut and paste since it's View vs ViewGroup.
     public BottomControlsCoordinator(BrowserControlsSizer controlsSizer,
             FullscreenManager fullscreenManager, ViewStub stub, ActivityTabProvider tabProvider,
-            OnLongClickListener tabSwitcherLongclickListener, ThemeColorProvider themeColorProvider,
+            ThemeColorProvider themeColorProvider,
             ObservableSupplier<ShareDelegate> shareDelegateSupplier,
             ObservableSupplier<AppMenuButtonHelper> menuButtonHelperSupplier,
             Supplier<Boolean> showStartSurfaceCallable, Runnable openHomepageAction,
@@ -98,39 +95,22 @@
         PropertyModelChangeProcessor.create(
                 model, new ViewHolder(root), BottomControlsViewBinder::bind);
 
-        int bottomToolbarHeightId;
+        int bottomControlsHeightId = R.dimen.bottom_controls_height;
 
-        if (BottomToolbarConfiguration.isLabeledBottomToolbarEnabled()) {
-            bottomToolbarHeightId = R.dimen.labeled_bottom_toolbar_height;
-        } else {
-            bottomToolbarHeightId = R.dimen.bottom_toolbar_height;
-        }
-
-        View toolbar = root.findViewById(R.id.bottom_container_slot);
-        ViewGroup.LayoutParams params = toolbar.getLayoutParams();
-        params.height = root.getResources().getDimensionPixelOffset(bottomToolbarHeightId);
+        View container = root.findViewById(R.id.bottom_container_slot);
+        ViewGroup.LayoutParams params = container.getLayoutParams();
+        params.height = root.getResources().getDimensionPixelOffset(bottomControlsHeightId);
         mMediator = new BottomControlsMediator(model, controlsSizer, fullscreenManager,
-                root.getResources().getDimensionPixelOffset(bottomToolbarHeightId));
+                root.getResources().getDimensionPixelOffset(bottomControlsHeightId));
 
-        if ((TabUiFeatureUtilities.isTabGroupsAndroidEnabled()
-                    && !(TabUiFeatureUtilities.isDuetTabStripIntegrationAndroidEnabled()
-                            && BottomToolbarConfiguration.isBottomToolbarEnabled()))
+        if (TabUiFeatureUtilities.isTabGroupsAndroidEnabled()
                 || TabUiFeatureUtilities.isConditionalTabStripEnabled()) {
             mTabGroupUi = TabManagementModuleProvider.getDelegate().createTabGroupUi(
                     root.findViewById(R.id.bottom_container_slot), themeColorProvider,
                     scrimCoordinator);
-        } else {
-            mBottomToolbarCoordinator = new BottomToolbarCoordinator(
-                    root.findViewById(R.id.bottom_toolbar_stub), tabProvider,
-                    tabSwitcherLongclickListener, themeColorProvider, shareDelegateSupplier,
-                    showStartSurfaceCallable, openHomepageAction, setUrlBarFocusAction,
-                    overviewModeBehaviorSupplier, menuButtonHelperSupplier);
         }
-
-        Toast.setGlobalExtraYOffset(root.getResources().getDimensionPixelSize(
-                BottomToolbarConfiguration.isLabeledBottomToolbarEnabled()
-                        ? R.dimen.labeled_bottom_toolbar_height
-                        : R.dimen.bottom_toolbar_height));
+        Toast.setGlobalExtraYOffset(
+                root.getResources().getDimensionPixelSize(bottomControlsHeightId));
     }
 
     /**
@@ -161,11 +141,6 @@
         mMediator.setResourceManager(resourceManager);
         mMediator.setWindowAndroid(windowAndroid);
 
-        if (mBottomToolbarCoordinator != null) {
-            mBottomToolbarCoordinator.initializeWithNative(tabSwitcherListener, newTabClickListener,
-                    tabCountProvider, incognitoStateProvider, topToolbarRoot, closeAllTabsAction);
-        }
-
         if (mTabGroupUi != null) {
             mTabGroupUi.initializeWithNative(chromeActivity, mMediator::setBottomControlsVisible);
         }
@@ -176,9 +151,6 @@
      */
     public void setBottomControlsVisible(boolean isVisible) {
         mMediator.setBottomControlsVisible(isVisible);
-        if (mBottomToolbarCoordinator != null) {
-            mBottomToolbarCoordinator.setBottomToolbarVisible(isVisible);
-        }
     }
 
     /**
@@ -193,7 +165,6 @@
      * Clean up any state when the bottom controls component is destroyed.
      */
     public void destroy() {
-        if (mBottomToolbarCoordinator != null) mBottomToolbarCoordinator.destroy();
         if (mTabGroupUi != null) mTabGroupUi.destroy();
         mMediator.destroy();
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsProperties.java
index 64d5b84..38498fc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsProperties.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsProperties.java
@@ -16,9 +16,6 @@
     static final WritableIntPropertyKey BOTTOM_CONTROLS_CONTAINER_HEIGHT_PX =
             new WritableIntPropertyKey();
 
-    /** The height of the bottom toolbar view in px. */
-    static final WritableIntPropertyKey BOTTOM_CONTROLS_HEIGHT_PX = new WritableIntPropertyKey();
-
     /** The Y offset of the view in px. */
     static final WritableIntPropertyKey Y_OFFSET = new WritableIntPropertyKey();
 
@@ -37,7 +34,7 @@
     static final WritableObjectPropertyKey<ResourceManager> RESOURCE_MANAGER =
             new WritableObjectPropertyKey<>();
 
-    static final PropertyKey[] ALL_KEYS = new PropertyKey[] {BOTTOM_CONTROLS_CONTAINER_HEIGHT_PX,
-            BOTTOM_CONTROLS_HEIGHT_PX, Y_OFFSET, ANDROID_VIEW_VISIBLE, COMPOSITED_VIEW_VISIBLE,
-            LAYOUT_MANAGER, RESOURCE_MANAGER};
+    static final PropertyKey[] ALL_KEYS =
+            new PropertyKey[] {BOTTOM_CONTROLS_CONTAINER_HEIGHT_PX, Y_OFFSET, ANDROID_VIEW_VISIBLE,
+                    COMPOSITED_VIEW_VISIBLE, LAYOUT_MANAGER, RESOURCE_MANAGER};
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsViewBinder.java
index 3ad3f7f..2e8ce71 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsViewBinder.java
@@ -36,10 +36,6 @@
             View bottomControlsWrapper = view.root.findViewById(R.id.bottom_controls_wrapper);
             bottomControlsWrapper.getLayoutParams().height =
                     model.get(BottomControlsProperties.BOTTOM_CONTROLS_CONTAINER_HEIGHT_PX);
-        } else if (BottomControlsProperties.BOTTOM_CONTROLS_HEIGHT_PX == propertyKey) {
-            View bottomContainerSlot = view.root.findViewById(R.id.bottom_toolbar);
-            bottomContainerSlot.getLayoutParams().height =
-                    model.get(BottomControlsProperties.BOTTOM_CONTROLS_HEIGHT_PX);
         } else if (BottomControlsProperties.Y_OFFSET == propertyKey) {
             // Native may not have completely initialized by the time this is set.
             if (view.sceneLayer == null) return;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomTabSwitcherActionMenuCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomTabSwitcherActionMenuCoordinator.java
deleted file mode 100644
index 8f4ee18..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomTabSwitcherActionMenuCoordinator.java
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.toolbar.bottom;
-
-import android.view.View;
-import android.view.View.OnLongClickListener;
-
-import org.chromium.base.Callback;
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.toolbar.top.TabSwitcherActionMenuCoordinator;
-import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
-import org.chromium.ui.widget.RectProvider;
-import org.chromium.ui.widget.ViewRectProvider;
-
-/**
- * The main coordinator for the Tab Switcher Action Menu on the bottom toolbar,
- * responsible for creating the popup menu and building a list of menu items.
- */
-public class BottomTabSwitcherActionMenuCoordinator extends TabSwitcherActionMenuCoordinator {
-    public static OnLongClickListener createOnLongClickListener(Callback<Integer> onItemClicked) {
-        return createOnLongClickListener(
-                new BottomTabSwitcherActionMenuCoordinator(), onItemClicked);
-    }
-
-    @Override
-    public ModelList buildMenuItems() {
-        ModelList itemList = new ModelList();
-        itemList.add(buildListItemByMenuItemType(MenuItemType.NEW_TAB));
-        itemList.add(buildListItemByMenuItemType(MenuItemType.NEW_INCOGNITO_TAB));
-        itemList.add(buildListItemByMenuItemType(MenuItemType.DIVIDER));
-        itemList.add(buildListItemByMenuItemType(MenuItemType.CLOSE_TAB));
-        return itemList;
-    }
-
-    @Override
-    protected RectProvider getRectProvider(View anchorView) {
-        ViewRectProvider rectProvider = new ViewRectProvider(anchorView);
-        rectProvider.setIncludePadding(true);
-
-        // space between the icon and the border of the wrapper
-        int toolbarHeight = anchorView.getHeight();
-        int iconHeight =
-                anchorView.getResources().getDimensionPixelSize(R.dimen.toolbar_icon_height);
-        int padding = (toolbarHeight - iconHeight) / 2;
-        rectProvider.setInsetPx(0, padding, padding / 2, 0);
-
-        return rectProvider;
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarConfiguration.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarConfiguration.java
deleted file mode 100644
index 9f61423f..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarConfiguration.java
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.toolbar.bottom;
-
-/**
- * Allows querying bottom toolbar related configurations.
- */
-public class BottomToolbarConfiguration {
-    /**
-     * @return Whether or not the bottom toolbar is enabled.
-     */
-    public static boolean isBottomToolbarEnabled() {
-        return false;
-    }
-
-    /**
-     * @return Whether or not the adaptive toolbar is enabled.
-     */
-    public static boolean isAdaptiveToolbarEnabled() {
-        return false;
-    }
-
-    /**
-     * @return Whether or not the labeled bottom toolbar is enabled.
-     */
-    public static boolean isLabeledBottomToolbarEnabled() {
-        return false;
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarCoordinator.java
deleted file mode 100644
index 2c58d30..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarCoordinator.java
+++ /dev/null
@@ -1,283 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.toolbar.bottom;
-
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.View.OnLongClickListener;
-import android.view.ViewGroup;
-import android.view.ViewStub;
-
-import org.chromium.base.Callback;
-import org.chromium.base.metrics.RecordUserAction;
-import org.chromium.base.supplier.ObservableSupplier;
-import org.chromium.base.supplier.ObservableSupplierImpl;
-import org.chromium.base.supplier.OneShotCallback;
-import org.chromium.base.supplier.Supplier;
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ActivityTabProvider;
-import org.chromium.chrome.browser.ThemeColorProvider;
-import org.chromium.chrome.browser.compositor.layouts.EmptyOverviewModeObserver;
-import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior;
-import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior.OverviewModeObserver;
-import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
-import org.chromium.chrome.browser.omnibox.LocationBar;
-import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.share.ShareDelegate;
-import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.tasks.ReturnToChromeExperimentsUtil;
-import org.chromium.chrome.browser.toolbar.IncognitoStateProvider;
-import org.chromium.chrome.browser.toolbar.TabCountProvider;
-import org.chromium.chrome.browser.ui.appmenu.AppMenuButtonHelper;
-import org.chromium.components.feature_engagement.EventConstants;
-import org.chromium.components.feature_engagement.Tracker;
-
-/**
- * The root coordinator for the bottom toolbar. It has two sub-components: the browsing mode bottom
- * toolbar and the tab switcher mode bottom toolbar.
- */
-class BottomToolbarCoordinator {
-    /** The browsing mode bottom toolbar component */
-    private final BrowsingModeBottomToolbarCoordinator mBrowsingModeCoordinator;
-
-    /** The tab switcher mode bottom toolbar component */
-    private TabSwitcherBottomToolbarCoordinator mTabSwitcherModeCoordinator;
-
-    /** The tab switcher mode bottom toolbar stub that will be inflated when native is ready. */
-    private final ViewStub mTabSwitcherModeStub;
-
-    /** A provider that notifies components when the theme color changes.*/
-    private final ThemeColorProvider mThemeColorProvider;
-
-    /** The overview mode manager. */
-    private OverviewModeBehavior mOverviewModeBehavior;
-    private OverviewModeObserver mOverviewModeObserver;
-
-    /** The activity tab provider. */
-    private ActivityTabProvider mTabProvider;
-
-    private final ObservableSupplier<ShareDelegate> mShareDelegateSupplier;
-    private final Callback<ShareDelegate> mShareDelegateSupplierCallback;
-    private ObservableSupplierImpl<OnClickListener> mShareButtonListenerSupplier =
-            new ObservableSupplierImpl<>();
-    private final Supplier<Boolean> mShowStartSurfaceCallable;
-    private ObservableSupplier<OverviewModeBehavior> mOverviewModeBehaviorSupplier;
-    private Callback<OverviewModeBehavior> mOverviewModeBehaviorSupplierObserver;
-    private AppMenuButtonHelper mMenuButtonHelper;
-
-    /**
-     * Build the coordinator that manages the bottom toolbar.
-     * @param stub The bottom toolbar {@link ViewStub} to inflate.
-     * @param tabProvider The {@link ActivityTabProvider} used for making the IPH.
-     * @param themeColorProvider The {@link ThemeColorProvider} for the bottom toolbar.
-     * @param shareDelegateSupplier The supplier for the {@link ShareDelegate} the bottom controls
-     *         should use to share content.
-     * @param showStartSurfaceCallable The action that opens the start surface, returning true if
-     * the start surface is shown.
-     * @param openHomepageAction The action that opens the homepage.
-     * @param setUrlBarFocusAction The function that sets Url bar focus. The first argument is
-     * @param overviewModeBehaviorSupplier Supplier for the overview mode manager.
-     * @param menuButtonHelperSupplier
-     */
-    BottomToolbarCoordinator(ViewStub stub, ActivityTabProvider tabProvider,
-            OnLongClickListener tabsSwitcherLongClickListner, ThemeColorProvider themeColorProvider,
-            ObservableSupplier<ShareDelegate> shareDelegateSupplier,
-            Supplier<Boolean> showStartSurfaceCallable, Runnable openHomepageAction,
-            Callback<Integer> setUrlBarFocusAction,
-            ObservableSupplier<OverviewModeBehavior> overviewModeBehaviorSupplier,
-            ObservableSupplier<AppMenuButtonHelper> menuButtonHelperSupplier) {
-        View root = stub.inflate();
-
-        mOverviewModeBehaviorSupplierObserver = this::setOverviewModeBehavior;
-        mOverviewModeBehaviorSupplier = overviewModeBehaviorSupplier;
-
-        mShowStartSurfaceCallable = showStartSurfaceCallable;
-        final OnClickListener homeButtonListener = v -> {
-            recordBottomToolbarUseForIPH();
-            openHomepageAction.run();
-        };
-
-        final OnClickListener searchAcceleratorListener = v -> {
-            recordBottomToolbarUseForIPH();
-            RecordUserAction.record("MobileToolbarOmniboxAcceleratorTap");
-
-            // Only switch to HomePage when overview is showing.
-            if (mOverviewModeBehavior != null && mOverviewModeBehavior.overviewVisible()) {
-                mShowStartSurfaceCallable.get();
-            }
-            setUrlBarFocusAction.onResult(LocationBar.OmniboxFocusReason.ACCELERATOR_TAP);
-        };
-
-        mBrowsingModeCoordinator = new BrowsingModeBottomToolbarCoordinator(root, tabProvider,
-                homeButtonListener, searchAcceleratorListener, mShareButtonListenerSupplier,
-                tabsSwitcherLongClickListner, mOverviewModeBehaviorSupplier);
-
-        mTabSwitcherModeStub = root.findViewById(R.id.bottom_toolbar_tab_switcher_mode_stub);
-
-        mThemeColorProvider = themeColorProvider;
-        mTabProvider = tabProvider;
-
-        mShareDelegateSupplier = shareDelegateSupplier;
-        mShareDelegateSupplierCallback = this::onShareDelegateAvailable;
-        mShareDelegateSupplier.addObserver(mShareDelegateSupplierCallback);
-
-        new OneShotCallback<>(menuButtonHelperSupplier, (menuButtonHelper) -> {
-            if (menuButtonHelper != null) {
-                mMenuButtonHelper = menuButtonHelper;
-                mMenuButtonHelper.setOnClickRunnable(() -> recordBottomToolbarUseForIPH());
-            }
-        });
-    }
-
-    /**
-     * Initialize the bottom toolbar with the components that had native initialization
-     * dependencies.
-     * <p>
-     * Calling this must occur after the native library have completely loaded.
-     * @param tabSwitcherListener An {@link OnClickListener} that is triggered when the
-     *                            tab switcher button is clicked.
-     * @param newTabClickListener An {@link OnClickListener} that is triggered when the
-     *                            new tab button is clicked.
-     * @param tabCountProvider Updates the tab count number in the tab switcher button and in the
-     *                         incognito toggle tab layout.
-     * @param incognitoStateProvider Notifies components when incognito mode is entered or exited.
-     * @param topToolbarRoot The root {@link ViewGroup} of the top toolbar.
-     * @param closeAllTabsAction The runnable that closes all tabs in the current tab model.
-     */
-    void initializeWithNative(OnClickListener tabSwitcherListener,
-            OnClickListener newTabClickListener, TabCountProvider tabCountProvider,
-            IncognitoStateProvider incognitoStateProvider, ViewGroup topToolbarRoot,
-            Runnable closeAllTabsAction) {
-        final OnClickListener closeTabsClickListener = v -> {
-            recordBottomToolbarUseForIPH();
-            final boolean isIncognito = incognitoStateProvider.isIncognitoSelected();
-            if (isIncognito) {
-                RecordUserAction.record("MobileToolbarCloseAllIncognitoTabsButtonTap");
-            } else {
-                RecordUserAction.record("MobileToolbarCloseAllRegularTabsButtonTap");
-            }
-
-            closeAllTabsAction.run();
-        };
-
-
-        newTabClickListener = wrapBottomToolbarClickListenerForIPH(newTabClickListener);
-        tabSwitcherListener = wrapBottomToolbarClickListenerForIPH(tabSwitcherListener);
-        mBrowsingModeCoordinator.initializeWithNative(newTabClickListener, tabSwitcherListener,
-                mMenuButtonHelper, tabCountProvider, mThemeColorProvider, incognitoStateProvider);
-        mTabSwitcherModeCoordinator = new TabSwitcherBottomToolbarCoordinator(mTabSwitcherModeStub,
-                topToolbarRoot, incognitoStateProvider, mThemeColorProvider, newTabClickListener,
-                closeTabsClickListener, mMenuButtonHelper, tabCountProvider);
-
-        // Do not change bottom bar if StartSurface Single Pane is enabled and HomePage is not
-        // customized.
-        if (!ReturnToChromeExperimentsUtil.shouldShowStartSurfaceAsTheHomePage()
-                && BottomToolbarVariationManager.shouldBottomToolbarBeVisibleInOverviewMode()) {
-            mOverviewModeObserver = new EmptyOverviewModeObserver() {
-                @Override
-                public void onOverviewModeStartedShowing(boolean showToolbar) {
-                    mBrowsingModeCoordinator.getSearchAccelerator().setEnabled(false);
-                    if (BottomToolbarVariationManager.isShareButtonOnBottom()) {
-                        mBrowsingModeCoordinator.getShareButton().setEnabled(false);
-                    }
-                    if (BottomToolbarVariationManager.isHomeButtonOnBottom()) {
-                        mBrowsingModeCoordinator.getHomeButton().setEnabled(false);
-                    }
-                }
-
-                @Override
-                public void onOverviewModeStartedHiding(
-                        boolean showToolbar, boolean delayAnimation) {
-                    mBrowsingModeCoordinator.getSearchAccelerator().setEnabled(true);
-                    if (BottomToolbarVariationManager.isShareButtonOnBottom()) {
-                        mBrowsingModeCoordinator.getShareButton().updateButtonEnabledState(
-                                mTabProvider.get());
-                    }
-                    if (BottomToolbarVariationManager.isHomeButtonOnBottom()) {
-                        mBrowsingModeCoordinator.getHomeButton().updateButtonEnabledState(
-                                mTabProvider.get());
-                    }
-                }
-            };
-            mOverviewModeBehaviorSupplier.addObserver(mOverviewModeBehaviorSupplierObserver);
-        }
-    }
-
-    /**
-     * @param isVisible Whether the bottom toolbar is visible.
-     */
-    void setBottomToolbarVisible(boolean isVisible) {
-        if (mTabSwitcherModeCoordinator != null) {
-            mTabSwitcherModeCoordinator.showToolbarOnTop(!isVisible);
-        }
-        mBrowsingModeCoordinator.onVisibilityChanged(isVisible);
-    }
-
-    /**
-     * Clean up any state when the bottom toolbar is destroyed.
-     */
-    void destroy() {
-        mBrowsingModeCoordinator.destroy();
-        if (mTabSwitcherModeCoordinator != null) {
-            mTabSwitcherModeCoordinator.destroy();
-            mTabSwitcherModeCoordinator = null;
-        }
-        if (mOverviewModeBehavior != null) {
-            mOverviewModeBehavior.removeOverviewModeObserver(mOverviewModeObserver);
-            mOverviewModeBehavior = null;
-        }
-        if (mOverviewModeBehaviorSupplier != null) {
-            mOverviewModeBehaviorSupplier.removeObserver(mOverviewModeBehaviorSupplierObserver);
-            mOverviewModeBehaviorSupplier = null;
-            mOverviewModeBehaviorSupplierObserver = null;
-        }
-        mThemeColorProvider.destroy();
-        mShareDelegateSupplier.removeObserver(mShareDelegateSupplierCallback);
-    }
-
-    private void onShareDelegateAvailable(ShareDelegate shareDelegate) {
-        final OnClickListener shareButtonListener = v -> {
-            if (BottomToolbarVariationManager.isShareButtonOnBottom()) {
-                recordBottomToolbarUseForIPH();
-                RecordUserAction.record("MobileBottomToolbarShareButton");
-            }
-
-            Tab tab = mTabProvider.get();
-            shareDelegate.share(tab, /*shareDirectly=*/false);
-        };
-
-        mShareButtonListenerSupplier.set(shareButtonListener);
-    }
-
-    private void setOverviewModeBehavior(OverviewModeBehavior overviewModeBehavior) {
-        assert overviewModeBehavior != null;
-        assert mOverviewModeBehavior
-                == null
-            : "TODO(https://crbug.com/1084528): the overview mode manager should set at most once.";
-        mOverviewModeBehavior = overviewModeBehavior;
-        mOverviewModeBehavior.addOverviewModeObserver(mOverviewModeObserver);
-    }
-
-    /** Record that the bottom toolbar was used for IPH reasons. */
-    private void recordBottomToolbarUseForIPH() {
-        Tab tab = mTabProvider.get();
-        if (tab == null) return;
-
-        Tracker tracker =
-                TrackerFactory.getTrackerForProfile(Profile.fromWebContents(tab.getWebContents()));
-        tracker.notifyEvent(EventConstants.CHROME_DUET_USED_BOTTOM_TOOLBAR);
-    }
-
-    /**
-     * Add bottom toolbar IPH tracking to an existing click listener.
-     * @param listener The listener to add bottom toolbar tracking to.
-     */
-    private OnClickListener wrapBottomToolbarClickListenerForIPH(OnClickListener listener) {
-        return (v) -> {
-            recordBottomToolbarUseForIPH();
-            listener.onClick(v);
-        };
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarNewTabButton.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarNewTabButton.java
deleted file mode 100644
index a501280..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarNewTabButton.java
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.toolbar.bottom;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-
-import androidx.annotation.StringRes;
-
-import org.chromium.base.ApiCompatibilityUtils;
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ThemeColorProvider;
-import org.chromium.chrome.browser.ThemeColorProvider.ThemeColorObserver;
-import org.chromium.chrome.browser.ThemeColorProvider.TintObserver;
-import org.chromium.chrome.browser.toolbar.IncognitoStateProvider;
-import org.chromium.chrome.browser.toolbar.IncognitoStateProvider.IncognitoStateObserver;
-import org.chromium.chrome.browser.toolbar.ToolbarColors;
-import org.chromium.ui.widget.ChromeImageButton;
-
-/**
- * The tab switcher new tab button.
- */
-class BottomToolbarNewTabButton extends ChromeImageButton
-        implements IncognitoStateObserver, ThemeColorObserver, TintObserver {
-    /** The gray pill background behind the plus icon. */
-    private Drawable mBackground;
-
-    /** The {@link Resources} used to compute the background color. */
-    private final Resources mResources;
-
-    /** A provider that notifies when incognito mode is entered or exited. */
-    private IncognitoStateProvider mIncognitoStateProvider;
-
-    /** A provider that notifies when the theme color changes.*/
-    private ThemeColorProvider mThemeColorProvider;
-
-    public BottomToolbarNewTabButton(Context context, AttributeSet attrs) {
-        super(context, attrs);
-
-        mResources = context.getResources();
-    }
-
-    @Override
-    public void setBackground(Drawable background) {
-        super.setBackground(background);
-        mBackground = background;
-    }
-
-    /**
-     * Clean up any state when the new tab button is destroyed.
-     */
-    void destroy() {
-        if (mIncognitoStateProvider != null) {
-            mIncognitoStateProvider.removeObserver(this);
-            mIncognitoStateProvider = null;
-        }
-        if (mThemeColorProvider != null) {
-            mThemeColorProvider.removeThemeColorObserver(this);
-            mThemeColorProvider.removeTintObserver(this);
-            mThemeColorProvider = null;
-        }
-    }
-
-    void setIncognitoStateProvider(IncognitoStateProvider incognitoStateProvider) {
-        mIncognitoStateProvider = incognitoStateProvider;
-        mIncognitoStateProvider.addIncognitoStateObserverAndTrigger(this);
-    }
-
-    @Override
-    public void onIncognitoStateChanged(boolean isIncognito) {
-        @StringRes
-        int resId = isIncognito ? R.string.accessibility_toolbar_btn_new_incognito_tab
-                                : R.string.accessibility_toolbar_btn_new_tab;
-        setContentDescription(getResources().getText(resId));
-        updateBackground();
-    }
-
-    void setThemeColorProvider(ThemeColorProvider themeColorProvider) {
-        mThemeColorProvider = themeColorProvider;
-        mThemeColorProvider.addThemeColorObserver(this);
-        mThemeColorProvider.addTintObserver(this);
-    }
-
-    @Override
-    public void onThemeColorChanged(int primaryColor, boolean shouldAnimate) {
-        updateBackground();
-    }
-
-    @Override
-    public void onTintChanged(ColorStateList tint, boolean useLight) {
-        ApiCompatibilityUtils.setImageTintList(this, tint);
-        updateBackground();
-    }
-
-    private void updateBackground() {
-        if (mThemeColorProvider == null || mIncognitoStateProvider == null || mBackground == null) {
-            return;
-        }
-        mBackground.setColorFilter(
-                ToolbarColors.getTextBoxColorForToolbarBackgroundInNonNativePage(mResources,
-                        mThemeColorProvider.getThemeColor(),
-                        mThemeColorProvider.useLight()
-                                && mIncognitoStateProvider.isIncognitoSelected()),
-                PorterDuff.Mode.SRC_IN);
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarVariationManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarVariationManager.java
deleted file mode 100644
index 9bef97de..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarVariationManager.java
+++ /dev/null
@@ -1,117 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.toolbar.bottom;
-
-import androidx.annotation.StringDef;
-import androidx.annotation.VisibleForTesting;
-
-import org.chromium.chrome.browser.incognito.IncognitoUtils;
-import org.chromium.chrome.browser.tasks.tab_management.TabUiFeatureUtilities;
-import org.chromium.chrome.features.start_surface.StartSurfaceConfiguration;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * The variation manager helps figure out the current variation and the visibility of buttons on
- * bottom toolbar. Every operation related to the variation, e.g. getting variation value, should be
- * through {@link BottomToolbarVariationManager} rather than calling {@link CachedFeatureFlags}.
- */
-public class BottomToolbarVariationManager {
-    @StringDef({Variations.NONE, Variations.HOME_SEARCH_TAB_SWITCHER, Variations.HOME_SEARCH_SHARE,
-            Variations.NEW_TAB_SEARCH_SHARE})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface Variations {
-        String NONE = "";
-        String HOME_SEARCH_TAB_SWITCHER = "HomeSearchTabSwitcher";
-        String HOME_SEARCH_SHARE = "HomeSearchShare";
-        String NEW_TAB_SEARCH_SHARE = "NewTabSearchShare";
-    }
-
-    private static String sVariation;
-
-    /**
-     * @return The currently enabled bottom toolbar variation.
-     *         Should be called after {@link BottomToolbarConfiguration#isBottomToolbarEnabled()}.
-     */
-    private static @Variations String getVariation() {
-        if (sVariation != null) return sVariation;
-        if (!BottomToolbarConfiguration.isBottomToolbarEnabled()) {
-            return Variations.HOME_SEARCH_TAB_SWITCHER;
-        }
-        sVariation = Variations.NONE;
-        return sVariation;
-    }
-
-    /**
-     * @return Whether or not share button should be visible on the top toolbar in portrait mode
-     *         in the current variation.
-     */
-    public static boolean isShareButtonOnBottom() {
-        return BottomToolbarConfiguration.isBottomToolbarEnabled()
-                && !getVariation().equals(Variations.HOME_SEARCH_TAB_SWITCHER);
-    }
-
-    /**
-     * @return Whether or not new tab button should be visible on the bottom toolbar
-     *         in portrait mode in the current variation.
-     */
-    public static boolean isNewTabButtonOnBottom() {
-        return BottomToolbarConfiguration.isBottomToolbarEnabled()
-                && getVariation().equals(Variations.NEW_TAB_SEARCH_SHARE);
-    }
-
-    /**
-     * @return Whether or not menu button should be visible on the top toolbar
-     *         in portrait mode in the current variation.
-     */
-    public static boolean isMenuButtonOnBottom() {
-        // If we don't have variations that put menu on bottom in the future,
-        // then this method can be removed.
-        return false;
-    }
-
-    /**
-     * @return Whether or not bottom toolbar should be visible in overview mode of portrait mode
-     *         in the current variation.
-     */
-    public static boolean shouldBottomToolbarBeVisibleInOverviewMode() {
-        return (getVariation().equals(Variations.NEW_TAB_SEARCH_SHARE)
-                       && !StartSurfaceConfiguration.isStartSurfaceEnabled())
-                || ((!TabUiFeatureUtilities.isGridTabSwitcherEnabled()
-                            || !IncognitoUtils.isIncognitoModeEnabled())
-                        && getVariation().equals(Variations.HOME_SEARCH_TAB_SWITCHER));
-    }
-
-    /**
-     * @return Whether or not home button should be visible in top toolbar of portrait mode
-     *         in current variation.
-     */
-    public static boolean isHomeButtonOnBottom() {
-        return BottomToolbarConfiguration.isBottomToolbarEnabled()
-                && !getVariation().equals(Variations.NEW_TAB_SEARCH_SHARE);
-    }
-
-    /**
-     * @return Whether or not tab switcher button should be visible in bottom toolbar
-     *         of portrait mode in current variation.
-     */
-    public static boolean isTabSwitcherOnBottom() {
-        return BottomToolbarConfiguration.isBottomToolbarEnabled()
-                && getVariation().equals(Variations.HOME_SEARCH_TAB_SWITCHER);
-    }
-
-    /**
-     * @return Name of the variation parameter of bottom toolbar.
-     */
-    public static String getVariationParamName() {
-        return "chrome_duet_variation";
-    }
-
-    @VisibleForTesting
-    public static void setVariation(String variation) {
-        sVariation = variation;
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarCoordinator.java
deleted file mode 100644
index b0e0905..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarCoordinator.java
+++ /dev/null
@@ -1,260 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.toolbar.bottom;
-
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.View.OnLongClickListener;
-
-import org.chromium.base.Callback;
-import org.chromium.base.supplier.ObservableSupplier;
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ActivityTabProvider;
-import org.chromium.chrome.browser.ThemeColorProvider;
-import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior;
-import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.tab.TabUtils;
-import org.chromium.chrome.browser.tasks.ReturnToChromeExperimentsUtil;
-import org.chromium.chrome.browser.toolbar.HomeButton;
-import org.chromium.chrome.browser.toolbar.IncognitoStateProvider;
-import org.chromium.chrome.browser.toolbar.TabCountProvider;
-import org.chromium.chrome.browser.toolbar.TabSwitcherButtonCoordinator;
-import org.chromium.chrome.browser.toolbar.TabSwitcherButtonView;
-import org.chromium.chrome.browser.ui.appmenu.AppMenuButtonHelper;
-import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
-
-/**
- * The coordinator for the browsing mode bottom toolbar. This class has two primary components,
- * an Android view that handles user actions and a composited texture that draws when the controls
- * are being scrolled off-screen. The Android version does not draw unless the controls offset is 0.
- */
-public class BrowsingModeBottomToolbarCoordinator {
-    /** The mediator that handles events from outside the browsing mode bottom toolbar. */
-    private final BrowsingModeBottomToolbarMediator mMediator;
-
-    /** The home button that lives in the bottom toolbar. */
-    private final HomeButton mHomeButton;
-
-    /** The share button that lives in the bottom toolbar. */
-    private final ShareButton mShareButton;
-
-    /** The new tab button that lives in the bottom toolbar. */
-    private final BottomToolbarNewTabButton mNewTabButton;
-
-    /** The search accelerator that lives in the bottom toolbar. */
-    private final SearchAccelerator mSearchAccelerator;
-
-    /** The tab switcher button component that lives in the bottom toolbar. */
-    private final TabSwitcherButtonCoordinator mTabSwitcherButtonCoordinator;
-
-    /** The tab switcher button view that lives in the bottom toolbar. */
-    private final TabSwitcherButtonView mTabSwitcherButtonView;
-
-    /** The view group that includes all views shown on browsing mode */
-    private final BrowsingModeBottomToolbarLinearLayout mToolbarRoot;
-
-    /** The model for the browsing mode bottom toolbar that holds all of its state. */
-    private final BrowsingModeBottomToolbarModel mModel;
-
-    /** The callback to be exectured when the share button on click listener is available. */
-    private Callback<OnClickListener> mShareButtonListenerSupplierCallback;
-
-    /** The supplier for the share button on click listener. */
-    private ObservableSupplier<OnClickListener> mShareButtonListenerSupplier;
-
-    /** The activity tab provider that used for making the IPH. */
-    private final ActivityTabProvider mTabProvider;
-
-    private Callback<OverviewModeBehavior> mOverviewModeBehaviorSupplierObserver;
-    private ObservableSupplier<OverviewModeBehavior> mOverviewModeBehaviorSupplier;
-
-    /**
-     * Build the coordinator that manages the browsing mode bottom toolbar.
-     * @param root The root {@link View} for locating the views to inflate.
-     * @param tabProvider The {@link ActivityTabProvider} used for making the IPH.
-     * @param homeButtonListener The {@link OnClickListener} for the home button.
-     * @param searchAcceleratorListener The {@link OnClickListener} for the search accelerator.
-     * @param shareButtonListener The {@link OnClickListener} for the share button.
-     * @param overviewModeBehaviorSupplier Supplier for the overview mode manager.
-     */
-    BrowsingModeBottomToolbarCoordinator(View root, ActivityTabProvider tabProvider,
-            OnClickListener homeButtonListener, OnClickListener searchAcceleratorListener,
-            ObservableSupplier<OnClickListener> shareButtonListenerSupplier,
-            OnLongClickListener tabSwitcherLongClickListener,
-            ObservableSupplier<OverviewModeBehavior> overviewModeBehaviorSupplier) {
-        mModel = new BrowsingModeBottomToolbarModel();
-        mToolbarRoot = root.findViewById(R.id.bottom_toolbar_browsing);
-        mTabProvider = tabProvider;
-
-        PropertyModelChangeProcessor.create(
-                mModel, mToolbarRoot, new BrowsingModeBottomToolbarViewBinder());
-
-        mMediator = new BrowsingModeBottomToolbarMediator(mModel);
-
-        mHomeButton = mToolbarRoot.findViewById(R.id.bottom_home_button);
-        mHomeButton.setOnClickListener(homeButtonListener);
-        mHomeButton.setActivityTabProvider(mTabProvider);
-
-        mNewTabButton = mToolbarRoot.findViewById(R.id.bottom_new_tab_button);
-
-        mShareButton = mToolbarRoot.findViewById(R.id.bottom_share_button);
-
-        mSearchAccelerator = mToolbarRoot.findViewById(R.id.search_accelerator);
-        mSearchAccelerator.setOnClickListener(searchAcceleratorListener);
-
-        // TODO(amaralp): Make this adhere to MVC framework.
-        mTabSwitcherButtonView = mToolbarRoot.findViewById(R.id.bottom_tab_switcher_button);
-        mTabSwitcherButtonCoordinator = new TabSwitcherButtonCoordinator(mTabSwitcherButtonView);
-
-        mTabSwitcherButtonView.setOnLongClickListener(tabSwitcherLongClickListener);
-        if (BottomToolbarVariationManager.isNewTabButtonOnBottom()) {
-            mNewTabButton.setVisibility(View.VISIBLE);
-        }
-        if (BottomToolbarVariationManager.isHomeButtonOnBottom()) {
-            mHomeButton.setVisibility(View.VISIBLE);
-        }
-
-        if (BottomToolbarVariationManager.isTabSwitcherOnBottom()) {
-            mTabSwitcherButtonView.setVisibility(View.VISIBLE);
-        }
-        if (BottomToolbarVariationManager.isShareButtonOnBottom()) {
-            mShareButton.setVisibility(View.VISIBLE);
-            mShareButtonListenerSupplierCallback = shareButtonListener -> {
-                mShareButton.setOnClickListener(shareButtonListener);
-            };
-            mShareButtonListenerSupplier = shareButtonListenerSupplier;
-            mShareButton.setActivityTabProvider(mTabProvider);
-            mShareButtonListenerSupplier.addObserver(mShareButtonListenerSupplierCallback);
-        }
-
-        mOverviewModeBehaviorSupplier = overviewModeBehaviorSupplier;
-        mOverviewModeBehaviorSupplierObserver = this::setOverviewModeBehavior;
-        mOverviewModeBehaviorSupplier.addObserver(mOverviewModeBehaviorSupplierObserver);
-    }
-
-    /**
-     * @param isVisible Whether the bottom toolbar is visible.
-     */
-    void onVisibilityChanged(boolean isVisible) {
-        if (isVisible) return;
-        Tab tab = mTabProvider.get();
-        if (tab != null) mMediator.dismissIPH(TabUtils.getActivity(tab));
-    }
-
-    /**
-     * Initialize the bottom toolbar with the components that had native initialization
-     * dependencies.
-     * <p>
-     * Calling this must occur after the native library have completely loaded.
-     * @param tabSwitcherListener An {@link OnClickListener} that is triggered when the
-     *                            tab switcher button is clicked.
-     * @param menuButtonHelper An {@link AppMenuButtonHelper} that is triggered when the
-     *                         menu button is clicked.
-     * @param tabCountProvider Updates the tab count number in the tab switcher button.
-     * @param themeColorProvider Notifies components when theme color changes.
-     * @param incognitoStateProvider Notifies components when incognito state changes.
-     */
-    void initializeWithNative(OnClickListener newTabListener, OnClickListener tabSwitcherListener,
-            AppMenuButtonHelper menuButtonHelper, TabCountProvider tabCountProvider,
-            ThemeColorProvider themeColorProvider, IncognitoStateProvider incognitoStateProvider) {
-        mMediator.setThemeColorProvider(themeColorProvider);
-        if (BottomToolbarVariationManager.isNewTabButtonOnBottom()) {
-            mNewTabButton.setOnClickListener(newTabListener);
-            mNewTabButton.setThemeColorProvider(themeColorProvider);
-            mNewTabButton.setIncognitoStateProvider(incognitoStateProvider);
-        }
-        if (BottomToolbarVariationManager.isHomeButtonOnBottom()) {
-            mHomeButton.setThemeColorProvider(themeColorProvider);
-        }
-
-        if (BottomToolbarVariationManager.isShareButtonOnBottom()) {
-            mShareButton.setThemeColorProvider(themeColorProvider);
-        }
-
-        mSearchAccelerator.setThemeColorProvider(themeColorProvider);
-        mSearchAccelerator.setIncognitoStateProvider(incognitoStateProvider);
-
-        if (BottomToolbarVariationManager.isTabSwitcherOnBottom()) {
-            mTabSwitcherButtonCoordinator.setTabSwitcherListener(tabSwitcherListener);
-            mTabSwitcherButtonCoordinator.setThemeColorProvider(themeColorProvider);
-            mTabSwitcherButtonCoordinator.setTabCountProvider(tabCountProvider);
-        }
-    }
-
-    private void setOverviewModeBehavior(OverviewModeBehavior overviewModeBehavior) {
-        assert overviewModeBehavior != null;
-
-        // If StartSurface is HomePage, BrowsingModeBottomToolbar is shown in browsing mode and in
-        // overview mode. We need to pass the OverviewModeBehavior to the buttons so they are
-        // disabled based on the overview state.
-        if (ReturnToChromeExperimentsUtil.shouldShowStartSurfaceAsTheHomePage()) {
-            mShareButton.setOverviewModeBehavior(overviewModeBehavior);
-            mTabSwitcherButtonCoordinator.setOverviewModeBehavior(overviewModeBehavior);
-            mHomeButton.setOverviewModeBehavior(overviewModeBehavior);
-        }
-    }
-
-    /**
-     * @param enabled Whether to disable click events on the bottom toolbar. Setting true can also
-     *                prevent from all click events on toolbar and all children views on toolbar.
-     */
-    void setTouchEnabled(boolean enabled) {
-        mToolbarRoot.setTouchEnabled(enabled);
-    }
-
-    /**
-     * @param visible Whether to hide the tab switcher bottom toolbar
-     */
-    void setVisible(boolean visible) {
-        mModel.set(BrowsingModeBottomToolbarModel.IS_VISIBLE, visible);
-    }
-
-    /**
-     * @return The browsing mode bottom toolbar's share button.
-     */
-    ShareButton getShareButton() {
-        return mShareButton;
-    }
-
-    /**
-     * @return The browsing mode bottom toolbar's tab switcher button.
-     */
-    TabSwitcherButtonView getTabSwitcherButtonView() {
-        return mTabSwitcherButtonView;
-    }
-
-    /**
-     * @return The browsing mode bottom toolbar's search button.
-     */
-    SearchAccelerator getSearchAccelerator() {
-        return mSearchAccelerator;
-    }
-
-    /**
-     * @return The browsing mode bottom toolbar's home button.
-     */
-    HomeButton getHomeButton() {
-        return mHomeButton;
-    }
-
-    /**
-     * Clean up any state when the browsing mode bottom toolbar is destroyed.
-     */
-    public void destroy() {
-        if (mShareButtonListenerSupplier != null) {
-            mShareButtonListenerSupplier.removeObserver(mShareButtonListenerSupplierCallback);
-        }
-        if (mOverviewModeBehaviorSupplier != null) {
-            mOverviewModeBehaviorSupplier.removeObserver(mOverviewModeBehaviorSupplierObserver);
-            mOverviewModeBehaviorSupplier = null;
-            mOverviewModeBehaviorSupplierObserver = null;
-        }
-        mMediator.destroy();
-        mHomeButton.destroy();
-        mShareButton.destroy();
-        mSearchAccelerator.destroy();
-        mTabSwitcherButtonCoordinator.destroy();
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarLinearLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarLinearLayout.java
deleted file mode 100644
index cf6fb2b..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarLinearLayout.java
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.toolbar.bottom;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.widget.LinearLayout;
-
-import androidx.annotation.Nullable;
-
-/**
- * A linear layout which can intercept touch events to prevent from invoking click listeners on
- * children views.
- */
-public class BrowsingModeBottomToolbarLinearLayout extends LinearLayout {
-    private boolean mTouchEnabled = true;
-
-    public BrowsingModeBottomToolbarLinearLayout(Context context) {
-        super(context);
-    }
-
-    public BrowsingModeBottomToolbarLinearLayout(Context context, @Nullable AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public BrowsingModeBottomToolbarLinearLayout(
-            Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
-
-    /**
-     * @param enabled True if all touch events will be intercepted.
-     */
-    public void setTouchEnabled(boolean enabled) {
-        mTouchEnabled = enabled;
-    }
-
-    @Override
-    public boolean onInterceptTouchEvent(MotionEvent ev) {
-        if (!mTouchEnabled) return true;
-        return super.onInterceptTouchEvent(ev);
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        if (!mTouchEnabled) return true;
-        return super.onTouchEvent(event);
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarMediator.java
deleted file mode 100644
index 505d43e..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarMediator.java
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.toolbar.bottom;
-
-import android.app.Activity;
-
-import androidx.annotation.ColorInt;
-import androidx.appcompat.app.AppCompatActivity;
-
-import org.chromium.chrome.browser.AppHooks;
-import org.chromium.chrome.browser.ThemeColorProvider;
-import org.chromium.chrome.browser.ThemeColorProvider.ThemeColorObserver;
-import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior;
-import org.chromium.components.browser_ui.widget.FeatureHighlightProvider;
-
-/**
- * This class is responsible for reacting to events from the outside world, interacting with other
- * coordinators, running most of the business logic associated with the browsing mode bottom
- * toolbar, and updating the model accordingly.
- */
-class BrowsingModeBottomToolbarMediator implements ThemeColorObserver {
-    /** The transparency fraction of the IPH bubble. */
-    private static final float DUET_IPH_BUBBLE_ALPHA_FRACTION = 0.9f;
-
-    /** The transparency fraction of the IPH background. */
-    private static final float DUET_IPH_BACKGROUND_ALPHA_FRACTION = 0.3f;
-
-    /** The dismissable parameter name of the IPH. */
-    static final String DUET_IPH_TAP_TO_DISMISS_PARAM_NAME = "duet_iph_tap_to_dismiss_enabled";
-
-    /** The model for the browsing mode bottom toolbar that holds all of its state. */
-    private final BrowsingModeBottomToolbarModel mModel;
-
-    /** The overview mode manager. */
-    private OverviewModeBehavior mOverviewModeBehavior;
-
-    /** A provider that notifies components when the theme color changes.*/
-    private ThemeColorProvider mThemeColorProvider;
-
-    private FeatureHighlightProvider mFeatureHighlightProvider;
-
-    /**
-     * Build a new mediator that handles events from outside the bottom toolbar.
-     * @param model The {@link BrowsingModeBottomToolbarModel} that holds all the state for the
-     *              browsing mode  bottom toolbar.
-     */
-    BrowsingModeBottomToolbarMediator(BrowsingModeBottomToolbarModel model) {
-        mModel = model;
-        mFeatureHighlightProvider = AppHooks.get().createFeatureHighlightProvider();
-    }
-
-    void setThemeColorProvider(ThemeColorProvider themeColorProvider) {
-        mThemeColorProvider = themeColorProvider;
-        mThemeColorProvider.addThemeColorObserver(this);
-    }
-
-    /**
-     * Dismiss the IPH bubble for Chrome Duet.
-     * @param activity An activity to attach the IPH to.
-     */
-    void dismissIPH(Activity activity) {
-        mFeatureHighlightProvider.dismiss((AppCompatActivity) activity);
-    }
-
-    /**
-     * Clean up anything that needs to be when the bottom toolbar is destroyed.
-     */
-    void destroy() {
-        if (mThemeColorProvider != null) {
-            mThemeColorProvider.removeThemeColorObserver(this);
-            mThemeColorProvider = null;
-        }
-    }
-
-    @Override
-    public void onThemeColorChanged(int primaryColor, boolean shouldAnimate) {
-        mModel.set(BrowsingModeBottomToolbarModel.PRIMARY_COLOR, primaryColor);
-    }
-
-    /**
-     * Set the alpha for the color.
-     * @param baseColor The color which alpha will apply to.
-     * @param alpha The desired alpha for the color. The value should between 0 to 1. 0 means total
-     *         transparency, 1 means total non-transparency.
-     */
-    private @ColorInt int applyCustomAlphaToColor(@ColorInt int baseColor, float alpha) {
-        return (baseColor & 0x00FFFFFF) | ((int) (alpha * 255) << 24);
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarModel.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarModel.java
deleted file mode 100644
index 0d86662..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarModel.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.toolbar.bottom;
-
-import org.chromium.ui.modelutil.PropertyModel;
-
-/**
- * All of the state for the bottom toolbar, updated by the {@link
- * BrowsingModeBottomToolbarCoordinator}.
- */
-public class BrowsingModeBottomToolbarModel extends PropertyModel {
-    /** Primary color of bottom toolbar. */
-    static final WritableIntPropertyKey PRIMARY_COLOR = new WritableIntPropertyKey();
-
-    /** Whether the browsing mode bottom toolbar is visible */
-    static final WritableBooleanPropertyKey IS_VISIBLE = new WritableBooleanPropertyKey();
-
-    /** Default constructor. */
-    BrowsingModeBottomToolbarModel() {
-        super(IS_VISIBLE, PRIMARY_COLOR);
-        set(IS_VISIBLE, true);
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarViewBinder.java
deleted file mode 100644
index 74c1f61..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarViewBinder.java
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.toolbar.bottom;
-
-import android.view.View;
-
-import org.chromium.ui.modelutil.PropertyKey;
-import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
-
-/**
- * This class is responsible for pushing updates to both the Android view and the compositor
- * component of the browsing mode bottom toolbar. These updates are pulled from the
- * {@link BrowsingModeBottomToolbarModel} when a notification of an update is received.
- */
-public class BrowsingModeBottomToolbarViewBinder
-        implements PropertyModelChangeProcessor
-                           .ViewBinder<BrowsingModeBottomToolbarModel, View, PropertyKey> {
-    /**
-     * Build a binder that handles interaction between the model and the views that make up the
-     * browsing mode bottom toolbar.
-     */
-    BrowsingModeBottomToolbarViewBinder() {}
-
-    @Override
-    public final void bind(
-            BrowsingModeBottomToolbarModel model, View view, PropertyKey propertyKey) {
-        if (BrowsingModeBottomToolbarModel.PRIMARY_COLOR == propertyKey) {
-            view.setBackgroundColor(model.get(BrowsingModeBottomToolbarModel.PRIMARY_COLOR));
-        } else if (BrowsingModeBottomToolbarModel.IS_VISIBLE == propertyKey) {
-            view.setVisibility(model.get(BrowsingModeBottomToolbarModel.IS_VISIBLE) ? View.VISIBLE
-                                                                                    : View.GONE);
-        } else {
-            assert false : "Unhandled property detected in BrowsingModeBottomToolbarViewBinder!";
-        }
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/CloseAllTabsButton.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/CloseAllTabsButton.java
deleted file mode 100644
index b824be7..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/CloseAllTabsButton.java
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.toolbar.bottom;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.util.AttributeSet;
-
-import androidx.annotation.StringRes;
-
-import org.chromium.base.ApiCompatibilityUtils;
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ThemeColorProvider;
-import org.chromium.chrome.browser.ThemeColorProvider.TintObserver;
-import org.chromium.chrome.browser.toolbar.IncognitoStateProvider;
-import org.chromium.chrome.browser.toolbar.IncognitoStateProvider.IncognitoStateObserver;
-import org.chromium.chrome.browser.toolbar.TabCountProvider;
-import org.chromium.chrome.browser.toolbar.TabCountProvider.TabCountObserver;
-import org.chromium.ui.widget.ChromeImageButton;
-
-/**
- * The close all tabs button.
- */
-class CloseAllTabsButton extends ChromeImageButton
-        implements TintObserver, IncognitoStateObserver, TabCountObserver {
-    /** A provider that notifies when the theme color changes.*/
-    private ThemeColorProvider mThemeColorProvider;
-
-    /** A provider that notifies when incognito mode is entered or exited. */
-    private IncognitoStateProvider mIncognitoStateProvider;
-
-    /** A provider that notifies when the number of tabs changes. */
-    private TabCountProvider mTabCountProvider;
-
-    /** Whether the close all tabs button should be enabled. */
-    private boolean mIsEnabled = true;
-
-    public CloseAllTabsButton(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    void destroy() {
-        if (mThemeColorProvider != null) {
-            mThemeColorProvider.removeTintObserver(this);
-            mThemeColorProvider = null;
-        }
-        if (mIncognitoStateProvider != null) {
-            mIncognitoStateProvider.removeObserver((IncognitoStateObserver) this);
-            mIncognitoStateProvider = null;
-        }
-        if (mTabCountProvider != null) {
-            mTabCountProvider.removeObserver(this);
-            mTabCountProvider = null;
-        }
-    }
-
-    void setThemeColorProvider(ThemeColorProvider themeColorProvider) {
-        mThemeColorProvider = themeColorProvider;
-        mThemeColorProvider.addTintObserver(this);
-    }
-
-    @Override
-    public void onTintChanged(ColorStateList tint, boolean useLight) {
-        ApiCompatibilityUtils.setImageTintList(this, tint);
-    }
-
-    void setIncognitoStateProvider(IncognitoStateProvider incognitoStateProvider) {
-        mIncognitoStateProvider = incognitoStateProvider;
-        mIncognitoStateProvider.addIncognitoStateObserverAndTrigger(this);
-    }
-
-    @Override
-    public void onIncognitoStateChanged(boolean isIncognito) {
-        @StringRes
-        int resId = isIncognito ? R.string.accessibility_toolbar_btn_close_all_incognito_tabs
-                                : R.string.accessibility_toolbar_btn_close_all_tabs;
-        setContentDescription(getResources().getText(resId));
-    }
-
-    void setTabCountProvider(TabCountProvider provider) {
-        mTabCountProvider = provider;
-        mTabCountProvider.addObserver(this);
-    }
-
-    @Override
-    public void onTabCountChanged(int tabCount, boolean isIncognito) {
-        final boolean shouldBeEnabled = tabCount > 0;
-        if (shouldBeEnabled == mIsEnabled) return;
-
-        mIsEnabled = shouldBeEnabled;
-        setEnabled(mIsEnabled);
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/SearchAccelerator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/SearchAccelerator.java
deleted file mode 100644
index e4bacfa..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/SearchAccelerator.java
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.toolbar.bottom;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-
-import org.chromium.base.ApiCompatibilityUtils;
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ThemeColorProvider;
-import org.chromium.chrome.browser.ThemeColorProvider.ThemeColorObserver;
-import org.chromium.chrome.browser.ThemeColorProvider.TintObserver;
-import org.chromium.chrome.browser.toolbar.IncognitoStateProvider;
-import org.chromium.chrome.browser.toolbar.IncognitoStateProvider.IncognitoStateObserver;
-import org.chromium.chrome.browser.toolbar.ToolbarColors;
-import org.chromium.ui.widget.ChromeImageButton;
-
-/**
- * The search accelerator.
- */
-class SearchAccelerator extends ChromeImageButton
-        implements ThemeColorObserver, TintObserver, IncognitoStateObserver {
-    /** The gray pill background behind the search icon. */
-    private final Drawable mBackground;
-
-    /** The {@link Resources} used to compute the background color. */
-    private final Resources mResources;
-
-    /** A provider that notifies components when the theme color changes.*/
-    private ThemeColorProvider mThemeColorProvider;
-
-    /** A provider that notifies when incognito mode is entered or exited. */
-    private IncognitoStateProvider mIncognitoStateProvider;
-
-    public SearchAccelerator(Context context, AttributeSet attrs) {
-        super(context, attrs);
-
-        mResources = context.getResources();
-
-        mBackground = ApiCompatibilityUtils.getDrawable(mResources, R.drawable.ntp_search_box);
-        mBackground.mutate();
-        setBackground(mBackground);
-    }
-
-    void setThemeColorProvider(ThemeColorProvider themeColorProvider) {
-        mThemeColorProvider = themeColorProvider;
-        mThemeColorProvider.addThemeColorObserver(this);
-        mThemeColorProvider.addTintObserver(this);
-    }
-
-    void setIncognitoStateProvider(IncognitoStateProvider provider) {
-        mIncognitoStateProvider = provider;
-        mIncognitoStateProvider.addIncognitoStateObserverAndTrigger(this);
-    }
-
-    void destroy() {
-        if (mThemeColorProvider != null) {
-            mThemeColorProvider.removeThemeColorObserver(this);
-            mThemeColorProvider.removeTintObserver(this);
-            mThemeColorProvider = null;
-        }
-
-        if (mIncognitoStateProvider != null) {
-            mIncognitoStateProvider.removeObserver(this);
-            mIncognitoStateProvider = null;
-        }
-    }
-
-    @Override
-    public void onThemeColorChanged(int color, boolean shouldAnimate) {
-        updateBackground();
-    }
-
-    @Override
-    public void onTintChanged(ColorStateList tint, boolean useLight) {
-        ApiCompatibilityUtils.setImageTintList(this, tint);
-        updateBackground();
-    }
-
-    @Override
-    public void onIncognitoStateChanged(boolean isIncognito) {
-        updateBackground();
-    }
-
-    private void updateBackground() {
-        if (mThemeColorProvider == null || mIncognitoStateProvider == null) return;
-
-        mBackground.setColorFilter(ToolbarColors.getTextBoxColorForToolbarBackgroundInNonNativePage(
-                                           mResources, mThemeColorProvider.getThemeColor(),
-                                           mIncognitoStateProvider.isIncognitoSelected()
-                                                   && mThemeColorProvider.useLight()),
-                PorterDuff.Mode.SRC_IN);
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/ShareButton.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/ShareButton.java
deleted file mode 100644
index b54faf1..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/ShareButton.java
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.toolbar.bottom;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.util.AttributeSet;
-
-import org.chromium.base.ApiCompatibilityUtils;
-import org.chromium.chrome.browser.ActivityTabProvider;
-import org.chromium.chrome.browser.ActivityTabProvider.ActivityTabTabObserver;
-import org.chromium.chrome.browser.ThemeColorProvider;
-import org.chromium.chrome.browser.ThemeColorProvider.TintObserver;
-import org.chromium.chrome.browser.compositor.layouts.EmptyOverviewModeObserver;
-import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior;
-import org.chromium.chrome.browser.share.ShareUtils;
-import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.ui.widget.ChromeImageButton;
-
-/**
- * The share button.
- */
-class ShareButton extends ChromeImageButton implements TintObserver {
-    /** A provider that notifies components when the theme color changes.*/
-    private ThemeColorProvider mThemeColorProvider;
-
-    /** The {@link ActivityTabTabObserver} used to know when the active page changed. */
-    private ActivityTabTabObserver mActivityTabTabObserver;
-
-    /** The {@link OverviewModeBehavior} used to observe overview state changes.  */
-    private OverviewModeBehavior mOverviewModeBehavior;
-
-    /** The {@link OvervieModeObserver} observing the OverviewModeBehavior  */
-    private OverviewModeBehavior.OverviewModeObserver mOverviewModeObserver;
-
-    /** A collection of sharing utility functions.*/
-    private ShareUtils mShareUtils;
-
-    public ShareButton(Context context, AttributeSet attrs) {
-        super(context, attrs);
-
-        mOverviewModeObserver = new EmptyOverviewModeObserver() {
-            @Override
-            public void onOverviewModeStartedShowing(boolean showTabSwitcherToolbar) {
-                setEnabled(false);
-            }
-        };
-
-        mShareUtils = new ShareUtils();
-    }
-
-    void setThemeColorProvider(ThemeColorProvider themeColorProvider) {
-        mThemeColorProvider = themeColorProvider;
-        mThemeColorProvider.addTintObserver(this);
-    }
-
-    void setActivityTabProvider(ActivityTabProvider activityTabProvider) {
-        mActivityTabTabObserver = new ActivityTabTabObserver(activityTabProvider) {
-            @Override
-            public void onObservingDifferentTab(Tab tab, boolean hint) {
-                updateButtonEnabledState(tab);
-            }
-
-            @Override
-            public void onUpdateUrl(Tab tab, String url) {
-                updateButtonEnabledState(tab);
-            }
-        };
-    }
-
-    public void setOverviewModeBehavior(OverviewModeBehavior overviewModeBehavior) {
-        assert overviewModeBehavior != null;
-        mOverviewModeBehavior = overviewModeBehavior;
-        mOverviewModeBehavior.addOverviewModeObserver(mOverviewModeObserver);
-    }
-
-    void destroy() {
-        if (mThemeColorProvider != null) {
-            mThemeColorProvider.removeTintObserver(this);
-            mThemeColorProvider = null;
-        }
-        if (mActivityTabTabObserver != null) {
-            mActivityTabTabObserver.destroy();
-            mActivityTabTabObserver = null;
-        }
-
-        if (mOverviewModeBehavior != null) {
-            mOverviewModeBehavior.removeOverviewModeObserver(mOverviewModeObserver);
-            mOverviewModeObserver = null;
-        }
-    }
-
-    public void updateButtonEnabledState(Tab tab) {
-        final boolean isEnabled = mShareUtils.shouldEnableShare(tab);
-        setEnabled(isEnabled);
-    }
-
-    @Override
-    public void onTintChanged(ColorStateList tint, boolean useLight) {
-        ApiCompatibilityUtils.setImageTintList(this, tint);
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarCoordinator.java
deleted file mode 100644
index 6ea60ac..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarCoordinator.java
+++ /dev/null
@@ -1,125 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.toolbar.bottom;
-
-import android.graphics.drawable.Drawable;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.view.ViewStub;
-
-import org.chromium.base.ApiCompatibilityUtils;
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ThemeColorProvider;
-import org.chromium.chrome.browser.toolbar.IncognitoStateProvider;
-import org.chromium.chrome.browser.toolbar.TabCountProvider;
-import org.chromium.chrome.browser.toolbar.menu_button.MenuButton;
-import org.chromium.chrome.browser.ui.appmenu.AppMenuButtonHelper;
-import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
-
-/**
- * The coordinator for the tab switcher mode bottom toolbar. This class handles all interactions
- * that the tab switcher bottom toolbar has with the outside world.
- * TODO(crbug.com/1036474): This coordinator is not used currently and can be removed if the final
- *                          duet design doesn't need a stand-alone toolbar in tab switcher mode.
- */
-public class TabSwitcherBottomToolbarCoordinator {
-    /** The mediator that handles events from outside the tab switcher bottom toolbar. */
-    private final TabSwitcherBottomToolbarMediator mMediator;
-
-    /** The close all tabs button that lives in the tab switcher bottom bar. */
-    private final CloseAllTabsButton mCloseAllTabsButton;
-
-    /** The new tab button that lives in the tab switcher bottom toolbar. */
-    private final BottomToolbarNewTabButton mNewTabButton;
-
-    /** The menu button that lives in the tab switcher bottom toolbar. */
-    private final MenuButton mMenuButton;
-
-    /** The model for the tab switcher bottom toolbar that holds all of its state. */
-    private final TabSwitcherBottomToolbarModel mModel;
-
-    /**
-     * Build the coordinator that manages the tab switcher bottom toolbar.
-     * @param stub The tab switcher bottom toolbar {@link ViewStub} to inflate.
-     * @param topToolbarRoot The root {@link ViewGroup} of the top toolbar.
-     * @param incognitoStateProvider Notifies components when incognito mode is entered or exited.
-     * @param themeColorProvider Notifies components when the theme color changes.
-     * @param newTabClickListener An {@link OnClickListener} that is triggered when the
-     *                            new tab button is clicked.
-     * @param closeTabsClickListener An {@link OnClickListener} that is triggered when the
-     *                               close all tabs button is clicked.
-     * @param menuButtonHelper An {@link AppMenuButtonHelper} that is triggered when the
-     *                         menu button is clicked.
-     * @param tabCountProvider Updates the tab count number in the tab switcher button and in the
-     *                         incognito toggle tab layout.
-     */
-    TabSwitcherBottomToolbarCoordinator(ViewStub stub, ViewGroup topToolbarRoot,
-            IncognitoStateProvider incognitoStateProvider, ThemeColorProvider themeColorProvider,
-            OnClickListener newTabClickListener, OnClickListener closeTabsClickListener,
-            AppMenuButtonHelper menuButtonHelper, TabCountProvider tabCountProvider) {
-        final ViewGroup root = (ViewGroup) stub.inflate();
-
-        View toolbar = root.findViewById(R.id.bottom_toolbar_buttons);
-        ViewGroup.LayoutParams params = toolbar.getLayoutParams();
-        params.height = root.getResources().getDimensionPixelOffset(
-                BottomToolbarConfiguration.isLabeledBottomToolbarEnabled()
-                        ? R.dimen.labeled_bottom_toolbar_height
-                        : R.dimen.bottom_toolbar_height);
-
-        mModel = new TabSwitcherBottomToolbarModel();
-
-        PropertyModelChangeProcessor.create(mModel, root,
-                new TabSwitcherBottomToolbarViewBinder(
-                        topToolbarRoot, (ViewGroup) root.getParent()));
-
-        mMediator = new TabSwitcherBottomToolbarMediator(mModel, themeColorProvider);
-
-        mCloseAllTabsButton = root.findViewById(R.id.close_all_tabs_button);
-        mCloseAllTabsButton.setOnClickListener(closeTabsClickListener);
-        mCloseAllTabsButton.setIncognitoStateProvider(incognitoStateProvider);
-        mCloseAllTabsButton.setThemeColorProvider(themeColorProvider);
-        mCloseAllTabsButton.setTabCountProvider(tabCountProvider);
-        mCloseAllTabsButton.setVisibility(View.INVISIBLE);
-
-        mNewTabButton = root.findViewById(R.id.tab_switcher_new_tab_button);
-        Drawable background =
-                ApiCompatibilityUtils.getDrawable(root.getResources(), R.drawable.ntp_search_box);
-        background.mutate();
-        mNewTabButton.setBackground(background);
-        mNewTabButton.setOnClickListener(newTabClickListener);
-        mNewTabButton.setIncognitoStateProvider(incognitoStateProvider);
-        mNewTabButton.setThemeColorProvider(themeColorProvider);
-
-        assert menuButtonHelper != null;
-        mMenuButton = root.findViewById(R.id.menu_button_wrapper);
-        mMenuButton.setThemeColorProvider(themeColorProvider);
-        mMenuButton.setAppMenuButtonHelper(menuButtonHelper);
-    }
-
-    /**
-     * @param showOnTop Whether to show the tab switcher bottom toolbar on the top of the screen.
-     */
-    void showToolbarOnTop(boolean showOnTop) {
-        mMediator.showToolbarOnTop(showOnTop);
-    }
-
-    /**
-     * @param visible Whether to hide the tab switcher bottom toolbar
-     */
-    void setVisible(boolean visible) {
-        mModel.set(TabSwitcherBottomToolbarModel.IS_VISIBLE, visible);
-    }
-
-    /**
-     * Clean up any state when the bottom toolbar is destroyed.
-     */
-    public void destroy() {
-        mMediator.destroy();
-        mCloseAllTabsButton.destroy();
-        mNewTabButton.destroy();
-        mMenuButton.destroy();
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarMediator.java
deleted file mode 100644
index fdbbd0e..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarMediator.java
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.toolbar.bottom;
-
-import org.chromium.chrome.browser.ThemeColorProvider;
-import org.chromium.chrome.browser.ThemeColorProvider.ThemeColorObserver;
-import org.chromium.chrome.browser.tasks.tab_management.TabUiFeatureUtilities;
-
-/**
- * This class is responsible for reacting to events from the outside world, interacting with other
- * coordinators, running most of the business logic associated with the tab switcher bottom toolbar,
- * and updating the model accordingly.
- */
-class TabSwitcherBottomToolbarMediator implements ThemeColorObserver {
-    /** The model for the tab switcher bottom toolbar that holds all of its state. */
-    private final TabSwitcherBottomToolbarModel mModel;
-
-    /** A provider that notifies components when the theme color changes.*/
-    private final ThemeColorProvider mThemeColorProvider;
-
-    /**
-     * Build a new mediator that handles events from outside the tab switcher bottom toolbar.
-     * @param model The {@link TabSwitcherBottomToolbarModel} that holds all the state for the
-     *              tab switcher bottom toolbar.
-     * @param themeColorProvider Notifies components when the theme color changes.
-     */
-    TabSwitcherBottomToolbarMediator(
-            TabSwitcherBottomToolbarModel model, ThemeColorProvider themeColorProvider) {
-        mModel = model;
-
-        mThemeColorProvider = themeColorProvider;
-        mThemeColorProvider.addThemeColorObserver(this);
-    }
-
-    /**
-     * @param showOnTop Whether to show the tab switcher bottom toolbar on the top of the screen.
-     */
-    void showToolbarOnTop(boolean showOnTop) {
-        // TODO(crbug.com/1012014): Resolve how to manage the toolbar position in tab switcher in
-        // landscape mode. Probably remove code about showing bottom toolbar on top.
-        // When GridTabSwitcher is enabled, show the original top toolbar instead of showing the
-        // bottom toolbar on top.
-        mModel.set(TabSwitcherBottomToolbarModel.SHOW_ON_TOP,
-                showOnTop && !TabUiFeatureUtilities.isGridTabSwitcherEnabled());
-    }
-
-    /**
-     * Clean up anything that needs to be when the tab switcher bottom toolbar is destroyed.
-     */
-    void destroy() {
-        mThemeColorProvider.removeThemeColorObserver(this);
-    }
-
-    @Override
-    public void onThemeColorChanged(int primaryColor, boolean shouldAnimate) {
-        mModel.set(TabSwitcherBottomToolbarModel.PRIMARY_COLOR, primaryColor);
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarModel.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarModel.java
deleted file mode 100644
index 03a43a3..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarModel.java
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.toolbar.bottom;
-
-import org.chromium.ui.modelutil.PropertyModel;
-
-/**
- * All of the state for the tab switcher bottom toolbar, updated by the
- * {@link TabSwitcherBottomToolbarCoordinator}.
- */
-public class TabSwitcherBottomToolbarModel extends PropertyModel {
-    /** Primary color of tab switcher bottom toolbar. */
-    public static final WritableIntPropertyKey PRIMARY_COLOR = new WritableIntPropertyKey();
-
-    /** Whether the tab switcher bottom toolbar is visible */
-    public static final WritableBooleanPropertyKey IS_VISIBLE = new WritableBooleanPropertyKey();
-
-    /** Whether the tab switcher bottom toolbar shows on top of the screen. */
-    public static final WritableBooleanPropertyKey SHOW_ON_TOP = new WritableBooleanPropertyKey();
-
-    /** Default constructor. */
-    public TabSwitcherBottomToolbarModel() {
-        super(PRIMARY_COLOR, IS_VISIBLE, SHOW_ON_TOP);
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarViewBinder.java
deleted file mode 100644
index c262a53..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarViewBinder.java
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.toolbar.bottom;
-
-import android.view.View;
-import android.view.ViewGroup;
-
-import org.chromium.chrome.R;
-import org.chromium.ui.UiUtils;
-import org.chromium.ui.modelutil.PropertyKey;
-import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
-
-/**
- * This class is responsible for pushing updates the view of the tab switcher bottom toolbar. These
- * updates are pulled from the {@link TabSwitcherBottomToolbarModel} when a notification of an
- * update is received.
- */
-public class TabSwitcherBottomToolbarViewBinder
-        implements PropertyModelChangeProcessor
-                           .ViewBinder<TabSwitcherBottomToolbarModel, View, PropertyKey> {
-    private final ViewGroup mTopRoot;
-    private final ViewGroup mBottomRoot;
-
-    /**
-     * Build a binder that handles interaction between the model and the tab switcher bottom toolbar
-     * view.
-     */
-    TabSwitcherBottomToolbarViewBinder(ViewGroup topRoot, ViewGroup bottomRoot) {
-        mTopRoot = topRoot;
-        mBottomRoot = bottomRoot;
-    }
-
-    @Override
-    public final void bind(
-            TabSwitcherBottomToolbarModel model, View view, PropertyKey propertyKey) {
-        if (TabSwitcherBottomToolbarModel.IS_VISIBLE == propertyKey) {
-            view.setVisibility(
-                    model.get(TabSwitcherBottomToolbarModel.IS_VISIBLE) ? View.VISIBLE : View.GONE);
-        } else if (TabSwitcherBottomToolbarModel.PRIMARY_COLOR == propertyKey) {
-            view.findViewById(R.id.bottom_toolbar_buttons)
-                    .setBackgroundColor(model.get(TabSwitcherBottomToolbarModel.PRIMARY_COLOR));
-        } else if (TabSwitcherBottomToolbarModel.SHOW_ON_TOP == propertyKey) {
-            final boolean showOnTop = model.get(TabSwitcherBottomToolbarModel.SHOW_ON_TOP);
-            view.findViewById(R.id.bottom_toolbar_bottom_shadow)
-                    .setVisibility(showOnTop ? View.VISIBLE : View.GONE);
-            // When shown on the bottom, the layout should match_parent so that it fills its
-            // parent container. When the layout is shown on the top, it should wrap_content
-            // so that the toolbar shadow is visible.
-            view.getLayoutParams().height = showOnTop ? ViewGroup.LayoutParams.WRAP_CONTENT
-                                                      : ViewGroup.LayoutParams.MATCH_PARENT;
-            reparentView(view, showOnTop ? mTopRoot : mBottomRoot);
-        } else {
-            assert false : "Unhandled property detected in TabSwitcherBottomToolbarViewBinder!";
-        }
-    }
-
-    private static void reparentView(View v, ViewGroup newParent) {
-        UiUtils.removeViewFromParent(v);
-        newParent.addView(v);
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/StartSurfaceToolbarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/StartSurfaceToolbarCoordinator.java
index 29963e2..9000659 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/StartSurfaceToolbarCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/StartSurfaceToolbarCoordinator.java
@@ -171,13 +171,6 @@
     }
 
     /**
-     * @param isVisible Whether the bottom toolbar is visible.
-     */
-    void onBottomToolbarVisibilityChanged(boolean isVisible) {
-        mToolbarMediator.onBottomToolbarVisibilityChanged(isVisible);
-    }
-
-    /**
      * @param overviewModeBehavior The {@link OverviewModeBehavior} to observe overview state
      *         changes.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/StartSurfaceToolbarMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/StartSurfaceToolbarMediator.java
index cce7145..8dd27c9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/StartSurfaceToolbarMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/StartSurfaceToolbarMediator.java
@@ -159,8 +159,6 @@
         updateNewTabButtonVisibility();
     }
 
-    void onBottomToolbarVisibilityChanged(boolean isVisible) {}
-
     void setOverviewModeBehavior(OverviewModeBehavior overviewModeBehavior) {
         assert overviewModeBehavior != null;
         assert mOverviewModeBehavior
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherModeTTCoordinatorPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherModeTTCoordinatorPhone.java
index b0bb41c..276e724 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherModeTTCoordinatorPhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherModeTTCoordinatorPhone.java
@@ -41,7 +41,6 @@
     private TabModelSelector mTabModelSelector;
     private IncognitoStateProvider mIncognitoStateProvider;
     private boolean mAccessibilityEnabled;
-    private boolean mIsBottomToolbarVisible;
 
     private TabSwitcherModeTTPhone mTabSwitcherModeToolbar;
 
@@ -184,16 +183,6 @@
     /**
      * @param isVisible Whether the bottom toolbar is visible.
      */
-    void onBottomToolbarVisibilityChanged(boolean isVisible) {
-        if (mIsBottomToolbarVisible == isVisible) {
-            return;
-        }
-        mIsBottomToolbarVisible = isVisible;
-        if (mTabSwitcherModeToolbar != null) {
-            mTabSwitcherModeToolbar.onBottomToolbarVisibilityChanged(isVisible);
-        }
-    }
-
     private void initializeTabSwitcherToolbar() {
         mTabSwitcherModeToolbar = (TabSwitcherModeTTPhone) mTabSwitcherToolbarStub.inflate();
 
@@ -248,7 +237,6 @@
         if (mAccessibilityEnabled) {
             mTabSwitcherModeToolbar.onAccessibilityStatusChanged(mAccessibilityEnabled);
         }
-        mTabSwitcherModeToolbar.onBottomToolbarVisibilityChanged(mIsBottomToolbarVisible);
     }
 
     private boolean isNewTabVariationEnabled() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherModeTTPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherModeTTPhone.java
index 5a7b93b..8677a7e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherModeTTPhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherModeTTPhone.java
@@ -27,8 +27,6 @@
 import org.chromium.chrome.browser.toolbar.IncognitoToggleTabLayout;
 import org.chromium.chrome.browser.toolbar.NewTabButton;
 import org.chromium.chrome.browser.toolbar.TabCountProvider;
-import org.chromium.chrome.browser.toolbar.bottom.BottomToolbarConfiguration;
-import org.chromium.chrome.browser.toolbar.bottom.BottomToolbarVariationManager;
 import org.chromium.chrome.browser.toolbar.menu_button.MenuButton;
 import org.chromium.chrome.browser.ui.appmenu.AppMenuButtonHelper;
 import org.chromium.components.browser_ui.styles.ChromeColors;
@@ -62,7 +60,6 @@
     private ColorStateList mDarkIconTint;
 
     private boolean mIsIncognito;
-    private boolean mShouldShowNewTabButton;
     private boolean mShouldShowNewTabVariation;
 
     private ObjectAnimator mVisiblityAnimator;
@@ -89,6 +86,7 @@
         mNewTabViewButton.setOnClickListener(this);
 
         updateTabSwitchingElements(shouldShowIncognitoToggle());
+        updateNewTabButtonVisibility();
     }
 
     @Override
@@ -276,31 +274,15 @@
         // Show new tab variation when there are no incognito tabs.
         assert mIncognitoToggleTabLayout != null;
         mIncognitoToggleTabLayout.setVisibility(mShouldShowNewTabVariation ? GONE : VISIBLE);
-        setNewTabButtonVisibility(mShouldShowNewTabButton);
+        updateNewTabButtonVisibility();
     }
 
-    /**
-     * @param isVisible Whether the bottom toolbar is visible.
-     */
-    void onBottomToolbarVisibilityChanged(boolean isVisible) {
-        mShouldShowNewTabButton = !isVisible
-                || (BottomToolbarConfiguration.isBottomToolbarEnabled()
-                        && !BottomToolbarVariationManager.isNewTabButtonOnBottom());
-        setNewTabButtonVisibility(mShouldShowNewTabButton);
-        // show tab switcher button on the top in landscape mode.
-        if (BottomToolbarVariationManager.isTabSwitcherOnBottom() && !shouldShowIncognitoToggle()) {
-            mToggleTabStackButton.setVisibility(isVisible ? GONE : VISIBLE);
-        }
-    }
-
-    private void setNewTabButtonVisibility(boolean isButtonVisible) {
+    private void updateNewTabButtonVisibility() {
         if (mNewTabViewButton != null) {
-            mNewTabViewButton.setVisibility(
-                    mShouldShowNewTabVariation && isButtonVisible ? VISIBLE : GONE);
+            mNewTabViewButton.setVisibility(mShouldShowNewTabVariation ? VISIBLE : GONE);
         }
         if (mNewTabImageButton != null) {
-            mNewTabImageButton.setVisibility(
-                    !mShouldShowNewTabVariation && isButtonVisible ? VISIBLE : GONE);
+            mNewTabImageButton.setVisibility(!mShouldShowNewTabVariation ? VISIBLE : GONE);
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java
index 843b59f..e5eb984 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java
@@ -202,11 +202,6 @@
     }
 
     /**
-     * @param isVisible Whether the bottom toolbar is visible.
-     */
-    void onBottomToolbarVisibilityChanged(boolean isVisible) {}
-
-    /**
      * TODO comment
      */
     @CallSuper
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
index 884eada6..25c6879 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
@@ -69,8 +69,6 @@
 import org.chromium.chrome.browser.toolbar.TabCountProvider.TabCountObserver;
 import org.chromium.chrome.browser.toolbar.TabSwitcherDrawable;
 import org.chromium.chrome.browser.toolbar.ToolbarColors;
-import org.chromium.chrome.browser.toolbar.bottom.BottomToolbarConfiguration;
-import org.chromium.chrome.browser.toolbar.bottom.BottomToolbarVariationManager;
 import org.chromium.chrome.browser.toolbar.menu_button.MenuButton;
 import org.chromium.chrome.browser.toolbar.menu_button.MenuButtonCoordinator;
 import org.chromium.chrome.browser.toolbar.top.TopToolbarCoordinator.UrlExpansionObserver;
@@ -250,15 +248,6 @@
     private int mCurrentLocationBarColor;
 
     /**
-     * Whether the bottom toolbar is visible. If it is visible then the top toolbar's home,
-     *  tab switcher, and menu buttons should be hidden.
-     */
-    private boolean mIsBottomToolbarVisible;
-
-    /** Whether the bottom toolbar was visible for the last texture capture. */
-    private boolean mWasBottomToolbarVisibleForLastTextureCapture;
-
-    /**
      * Used to specify the visual state of the toolbar.
      */
     @IntDef({VisualState.NORMAL, VisualState.INCOGNITO, VisualState.BRAND_COLOR,
@@ -495,8 +484,7 @@
                 }
             });
         }
-        onHomeButtonUpdate(HomepageManager.isHomepageEnabled()
-                || BottomToolbarConfiguration.isBottomToolbarEnabled());
+        onHomeButtonUpdate(HomepageManager.isHomepageEnabled());
 
         updateVisualsForLocationBarState();
     }
@@ -1291,7 +1279,7 @@
 
         // Draw the tab stack button and associated text if necessary.
         if (mTabSwitcherAnimationTabStackDrawable != null && mToggleTabStackButton != null
-                && mUrlExpansionPercent != 1f && !isTabSwitcherOnBottom()) {
+                && mUrlExpansionPercent != 1f) {
             // Draw the tab stack button image.
             canvas.save();
             translateCanvasToView(mToolbarButtonsContainer, mToggleTabStackButton, canvas);
@@ -1327,10 +1315,8 @@
         }
 
         mLightDrawablesUsedForLastTextureCapture = useLight();
-        mWasBottomToolbarVisibleForLastTextureCapture = mIsBottomToolbarVisible;
 
-        if (mTabSwitcherAnimationTabStackDrawable != null && mToggleTabStackButton != null
-                && !mIsBottomToolbarVisible) {
+        if (mTabSwitcherAnimationTabStackDrawable != null && mToggleTabStackButton != null) {
             mTabCountForLastTextureCapture = mTabSwitcherAnimationTabStackDrawable.getTabCount();
         }
 
@@ -1575,8 +1561,7 @@
         if (forceTextureCapture) {
             // Only force a texture capture if the tint for the toolbar drawables is changing or
             // if the tab count has changed since the last texture capture.
-            mForceTextureCapture = mLightDrawablesUsedForLastTextureCapture != useLight()
-                    || mWasBottomToolbarVisibleForLastTextureCapture != mIsBottomToolbarVisible;
+            mForceTextureCapture = mLightDrawablesUsedForLastTextureCapture != useLight();
 
             if (mTabSwitcherAnimationTabStackDrawable != null && mToggleTabStackButton != null) {
                 mForceTextureCapture = mForceTextureCapture
@@ -1640,9 +1625,7 @@
     public void updateButtonVisibility() {
         if (mHomeButton == null) return;
 
-        boolean hideHomeButton = !mIsHomeButtonEnabled
-                || (mIsBottomToolbarVisible
-                        && BottomToolbarVariationManager.isHomeButtonOnBottom());
+        boolean hideHomeButton = !mIsHomeButtonEnabled;
         if (hideHomeButton) {
             removeHomeButton();
         } else {
@@ -1893,7 +1876,7 @@
         if (!getToolbarDataProvider().shouldShowLocationBarInOverviewMode()) return false;
 
         if (mToggleTabStackButton != null) {
-            boolean isGone = inTabSwitcherMode || isTabSwitcherOnBottom();
+            boolean isGone = inTabSwitcherMode;
             mToggleTabStackButton.setVisibility(isGone ? GONE : VISIBLE);
         }
 
@@ -2029,7 +2012,7 @@
             animators.add(animator);
         }
 
-        if (mToggleTabStackButton != null && !isTabSwitcherOnBottom()) {
+        if (mToggleTabStackButton != null) {
             animator = ObjectAnimator.ofFloat(
                     mToggleTabStackButton, TRANSLATION_X, toolbarButtonTranslationX);
             animator.setDuration(URL_FOCUS_TOOLBAR_BUTTONS_DURATION_MS);
@@ -2085,7 +2068,7 @@
             animators.add(animator);
         }
 
-        if (mToggleTabStackButton != null && !isTabSwitcherOnBottom()) {
+        if (mToggleTabStackButton != null) {
             animator = ObjectAnimator.ofFloat(mToggleTabStackButton, TRANSLATION_X, 0);
             animator.setDuration(URL_FOCUS_TOOLBAR_BUTTONS_DURATION_MS);
             animator.setStartDelay(URL_CLEAR_FOCUS_TABSTACK_DELAY_MS);
@@ -2429,14 +2412,6 @@
         return getToolbarDataProvider().getPrimaryColor();
     }
 
-    /**
-     * @return Whether tab switcher is shown on the bottom toolbar.
-     *         Return false when bottom toolbar is not visible.
-     */
-    private boolean isTabSwitcherOnBottom() {
-        return mIsBottomToolbarVisible && BottomToolbarVariationManager.isTabSwitcherOnBottom();
-    }
-
     private void updateVisualsForLocationBarState() {
         TraceEvent.begin("ToolbarPhone.updateVisualsForLocationBarState");
         // These are used to skip setting state unnecessarily while in the tab switcher.
@@ -2536,7 +2511,7 @@
         }
 
         getMenuButtonCoordinator().setMenuButtonHighlightDrawable();
-        if (!mIsBottomToolbarVisible) getMenuButtonCoordinator().setVisibility(View.VISIBLE);
+        getMenuButtonCoordinator().setVisibility(View.VISIBLE);
 
         DrawableCompat.setTint(mLocationBarBackground,
                 isIncognito() ? Color.WHITE
@@ -2820,15 +2795,6 @@
         }
     }
 
-    @Override
-    public void onBottomToolbarVisibilityChanged(boolean isVisible) {
-        mIsBottomToolbarVisible = isVisible;
-
-        mToggleTabStackButton.setVisibility(isTabSwitcherOnBottom() ? GONE : VISIBLE);
-        updateButtonVisibility();
-        mToolbarButtonsContainer.requestLayout();
-    }
-
     private void cancelAnimations() {
         if (mUrlFocusLayoutAnimator != null && mUrlFocusLayoutAnimator.isRunning()) {
             mUrlFocusLayoutAnimator.cancel();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java
index 8a042a1..9587189 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java
@@ -605,19 +605,6 @@
         return mToolbarLayout.getLocationBar();
     }
 
-    /**
-     * @param isVisible Whether the bottom toolbar is visible.
-     */
-    public void onBottomToolbarVisibilityChanged(boolean isVisible) {
-        mToolbarLayout.onBottomToolbarVisibilityChanged(isVisible);
-        if (mTabSwitcherModeCoordinatorPhone != null) {
-            mTabSwitcherModeCoordinatorPhone.onBottomToolbarVisibilityChanged(isVisible);
-        } else if (mStartSurfaceToolbarCoordinator != null) {
-            mStartSurfaceToolbarCoordinator.onBottomToolbarVisibilityChanged(isVisible);
-        }
-        mOptionalButtonController.updateButtonVisibility();
-    }
-
     @Override
     public int getHeight() {
         return mToolbarLayout.getHeight();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
index 981813b..fbecdc2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
@@ -492,27 +492,20 @@
                 mOmniboxFocusStateSupplier.set(hasFocus);
             };
 
-            ObservableSupplierImpl<Boolean> bottomToolbarVisibilitySupplier =
-                    new ObservableSupplierImpl<>();
-            bottomToolbarVisibilitySupplier.set(false);
-
-            mIdentityDiscController =
-                    new IdentityDiscController(mActivity, mActivity.getLifecycleDispatcher(),
-                            bottomToolbarVisibilitySupplier, mProfileSupplier);
+            mIdentityDiscController = new IdentityDiscController(
+                    mActivity, mActivity.getLifecycleDispatcher(), mProfileSupplier);
             ShareButtonController shareButtonController = new ShareButtonController(mActivity,
                     mActivityTabProvider, mShareDelegateSupplier, new ShareUtils(),
-                    bottomToolbarVisibilitySupplier, mActivity.getLifecycleDispatcher(),
-                    mActivity.getModalDialogManager());
+                    mActivity.getLifecycleDispatcher(), mActivity.getModalDialogManager());
             mButtonDataProviders = Arrays.asList(mIdentityDiscController, shareButtonController);
             mToolbarManager = new ToolbarManager(mActivity, mActivity.getBrowserControlsManager(),
                     mActivity.getFullscreenManager(), toolbarContainer,
                     mActivity.getCompositorViewHolder().getInvalidator(), urlFocusChangedCallback,
                     mTabThemeColorProvider, mTabObscuringHandler, mShareDelegateSupplier,
-                    bottomToolbarVisibilitySupplier, mIdentityDiscController, mButtonDataProviders,
-                    mActivityTabProvider, mScrimCoordinator, mActionModeControllerCallback,
-                    mFindToolbarManager, mProfileSupplier, mBookmarkBridgeSupplier,
-                    mCanAnimateBrowserControls, mOverviewModeBehaviorSupplier, mAppMenuSupplier,
-                    shouldShowMenuUpdateBadge());
+                    mIdentityDiscController, mButtonDataProviders, mActivityTabProvider,
+                    mScrimCoordinator, mActionModeControllerCallback, mFindToolbarManager,
+                    mProfileSupplier, mBookmarkBridgeSupplier, mCanAnimateBrowserControls,
+                    mOverviewModeBehaviorSupplier, mAppMenuSupplier, shouldShowMenuUpdateBadge());
             if (!mActivity.supportsAppMenu()) {
                 mToolbarManager.getToolbar().disableMenuButton();
             }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/OverviewAppMenuTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/OverviewAppMenuTest.java
index 0ac121a..18af3eb 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/OverviewAppMenuTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/OverviewAppMenuTest.java
@@ -60,8 +60,7 @@
     public void testAllMenuItemsWithoutStartSurface() throws Exception {
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             CachedFeatureFlags.setForTesting(ChromeFeatureList.START_SURFACE_ANDROID, false);
-            AppMenuTestSupport.showAppMenu(
-                    mActivityTestRule.getAppMenuCoordinator(), null, false, false);
+            AppMenuTestSupport.showAppMenu(mActivityTestRule.getAppMenuCoordinator(), null, false);
         });
 
         int checkedMenuItems = 0;
@@ -95,8 +94,7 @@
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             mActivityTestRule.getActivity().getTabModelSelector().selectModel(true);
             CachedFeatureFlags.setForTesting(ChromeFeatureList.START_SURFACE_ANDROID, false);
-            AppMenuTestSupport.showAppMenu(
-                    mActivityTestRule.getAppMenuCoordinator(), null, false, false);
+            AppMenuTestSupport.showAppMenu(mActivityTestRule.getAppMenuCoordinator(), null, false);
         });
 
         int checkedMenuItems = 0;
@@ -129,8 +127,7 @@
     public void testAllMenuItemsWithStartSurface() throws Exception {
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             CachedFeatureFlags.setForTesting(ChromeFeatureList.START_SURFACE_ANDROID, true);
-            AppMenuTestSupport.showAppMenu(
-                    mActivityTestRule.getAppMenuCoordinator(), null, false, false);
+            AppMenuTestSupport.showAppMenu(mActivityTestRule.getAppMenuCoordinator(), null, false);
         });
 
         int checkedMenuItems = 0;
@@ -166,8 +163,7 @@
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             mActivityTestRule.getActivity().getTabModelSelector().selectModel(true);
             CachedFeatureFlags.setForTesting(ChromeFeatureList.START_SURFACE_ANDROID, true);
-            AppMenuTestSupport.showAppMenu(
-                    mActivityTestRule.getAppMenuCoordinator(), null, false, false);
+            AppMenuTestSupport.showAppMenu(mActivityTestRule.getAppMenuCoordinator(), null, false);
         });
 
         int checkedMenuItems = 0;
@@ -202,8 +198,7 @@
     public void testGroupTabsIsDisabled() throws Exception {
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             CachedFeatureFlags.setForTesting(ChromeFeatureList.START_SURFACE_ANDROID, false);
-            AppMenuTestSupport.showAppMenu(
-                    mActivityTestRule.getAppMenuCoordinator(), null, false, false);
+            AppMenuTestSupport.showAppMenu(mActivityTestRule.getAppMenuCoordinator(), null, false);
         });
 
         int checkedMenuItems = 0;
@@ -225,8 +220,7 @@
     public void testGroupTabsIsEnabled() throws Exception {
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             CachedFeatureFlags.setForTesting(ChromeFeatureList.START_SURFACE_ANDROID, false);
-            AppMenuTestSupport.showAppMenu(
-                    mActivityTestRule.getAppMenuCoordinator(), null, false, false);
+            AppMenuTestSupport.showAppMenu(mActivityTestRule.getAppMenuCoordinator(), null, false);
         });
 
         int checkedMenuItems = 0;
@@ -254,8 +248,7 @@
     public void testGroupTabsIsDisabledWithStartSurface() throws Exception {
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             CachedFeatureFlags.setForTesting(ChromeFeatureList.START_SURFACE_ANDROID, true);
-            AppMenuTestSupport.showAppMenu(
-                    mActivityTestRule.getAppMenuCoordinator(), null, false, false);
+            AppMenuTestSupport.showAppMenu(mActivityTestRule.getAppMenuCoordinator(), null, false);
         });
 
         int checkedMenuItems = 0;
@@ -277,8 +270,7 @@
     public void testGroupTabsIsEnabledWithStartSurface() throws Exception {
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             CachedFeatureFlags.setForTesting(ChromeFeatureList.START_SURFACE_ANDROID, true);
-            AppMenuTestSupport.showAppMenu(
-                    mActivityTestRule.getAppMenuCoordinator(), null, false, false);
+            AppMenuTestSupport.showAppMenu(mActivityTestRule.getAppMenuCoordinator(), null, false);
         });
 
         int checkedMenuItems = 0;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/TabbedAppMenuTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/TabbedAppMenuTest.java
index 064d17c..e0293f3 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/TabbedAppMenuTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/TabbedAppMenuTest.java
@@ -373,7 +373,7 @@
         PostTask.runOrPostTask(UiThreadTaskTraits.DEFAULT,
                 ()
                         -> AppMenuTestSupport.showAppMenu(
-                                mActivityTestRule.getAppMenuCoordinator(), null, false, false));
+                                mActivityTestRule.getAppMenuCoordinator(), null, false));
         CriteriaHelper.pollInstrumentationThread(
                 () -> mAppMenuHandler.isAppMenuShowing(), "AppMenu did not show");
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/homepage/settings/HomepageSettingsFragmentTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/homepage/settings/HomepageSettingsFragmentTest.java
index 0f511e0..3d918af 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/homepage/settings/HomepageSettingsFragmentTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/homepage/settings/HomepageSettingsFragmentTest.java
@@ -15,7 +15,6 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.util.Feature;
-import org.chromium.base.test.util.Restriction;
 import org.chromium.base.test.util.UserActionTester;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.homepage.HomepageManager;
@@ -34,7 +33,6 @@
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.content_public.browser.test.util.TouchCommon;
-import org.chromium.ui.test.util.UiRestriction;
 
 /**
  * Test for {@link HomepageSettings}.
@@ -73,8 +71,7 @@
     private static final String ASSERT_HOMEPAGE_MANAGER_SETTINGS =
             "HomepageManager#getHomepageUri is different than test homepage settings.";
 
-    private static final String ASSERT_MESSAGE_DUET_SWITCH_INVISIBLE =
-            "Switch should not be visible when duet is enabled.";
+    private static final String ASSERT_MESSAGE_SWITCH_INVISIBLE = "Switch should not be visible.";
 
     private static final String ASSERT_HOMEPAGE_LOCATION_HISTOGRAM_COUNT =
             "Count for user action <Settings.Homepage.LocationChanged_V2> is different.";
@@ -187,36 +184,6 @@
     @Test
     @SmallTest
     @Feature({"Homepage"})
-    @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
-    public void testStartUp_ChromeNTP_BottomToolbar() {
-        mHomepageTestRule.useCustomizedHomepageForTest(TEST_URL_BAR);
-        mHomepageTestRule.useChromeNTPForTest();
-
-        HomepageSettings.setIsHomeButtonOnBottomToolbar(true);
-
-        launchSettingsActivity();
-
-        Assert.assertFalse(ASSERT_MESSAGE_DUET_SWITCH_INVISIBLE, mSwitch.isVisible());
-
-        Assert.assertTrue(ASSERT_MESSAGE_RADIO_BUTTON_ENABLED, mChromeNtpRadioButton.isEnabled());
-        Assert.assertTrue(ASSERT_MESSAGE_RADIO_BUTTON_ENABLED, mCustomUriRadioButton.isEnabled());
-        Assert.assertTrue(ASSERT_MESSAGE_TITLE_ENABLED, mTitleTextView.isEnabled());
-
-        Assert.assertTrue(ASSERT_MESSAGE_RADIO_BUTTON_NTP_CHECK, mChromeNtpRadioButton.isChecked());
-        Assert.assertFalse(
-                ASSERT_MESSAGE_RADIO_BUTTON_CUSTOMIZED_CHECK, mCustomUriRadioButton.isChecked());
-        Assert.assertEquals(ASSERT_MESSAGE_EDIT_TEXT, TEST_URL_BAR,
-                mCustomUriRadioButton.getPrimaryText().toString());
-        Assert.assertEquals(ASSERT_HOMEPAGE_LOCATION_TYPE_MISMATCH,
-                HomepageLocationType.USER_CUSTOMIZED_NTP,
-                HomepageManager.getInstance().getHomepageLocationType());
-
-        HomepageSettings.setIsHomeButtonOnBottomToolbar(false);
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Homepage"})
     public void testStartUp_Customized() {
         mHomepageTestRule.useCustomizedHomepageForTest(TEST_URL_BAR);
 
@@ -305,44 +272,6 @@
     @Test
     @SmallTest
     @Feature({"Homepage"})
-    @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
-    @Features.EnableFeatures(ChromeFeatureList.HOMEPAGE_LOCATION_POLICY)
-    public void testStartUp_Policies_Customized_BottomToolbar() {
-        // Set mock policies
-        mHomepageTestRule.setHomepagePolicyForTest(TEST_URL_BAR);
-
-        HomepageSettings.setIsHomeButtonOnBottomToolbar(true);
-
-        launchSettingsActivity();
-
-        Assert.assertFalse(ASSERT_MESSAGE_DUET_SWITCH_INVISIBLE, mSwitch.isVisible());
-
-        Assert.assertFalse(ASSERT_MESSAGE_RADIO_BUTTON_DISABLED, mChromeNtpRadioButton.isEnabled());
-        Assert.assertFalse(ASSERT_MESSAGE_RADIO_BUTTON_DISABLED, mCustomUriRadioButton.isEnabled());
-        Assert.assertFalse(ASSERT_MESSAGE_TITLE_DISABLED, mTitleTextView.isEnabled());
-
-        Assert.assertTrue(ASSERT_MESSAGE_RADIO_BUTTON_NTP_CHECK, mCustomUriRadioButton.isChecked());
-
-        // Additional verification - managed text should be visible
-        Assert.assertEquals("NTP Button should not be visible.", View.GONE,
-                mChromeNtpRadioButton.getVisibility());
-        Assert.assertEquals("Customized Button should not be visible.", View.VISIBLE,
-                mCustomUriRadioButton.getVisibility());
-        Assert.assertTrue("Managed text message preference should be visible when duet enabled.",
-                mManagedText.isVisible());
-        Assert.assertFalse(
-                "Managed text message preference should be disabled.", mManagedText.isEnabled());
-        Assert.assertEquals(ASSERT_HOMEPAGE_LOCATION_TYPE_MISMATCH,
-                HomepageLocationType.POLICY_OTHER,
-                HomepageManager.getInstance().getHomepageLocationType());
-
-        // Reset policy
-        HomepageSettings.setIsHomeButtonOnBottomToolbar(false);
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Homepage"})
     public void testStartUp_DefaultToPartner() {
         PartnerBrowserCustomizations.getInstance().setHomepageForTests(TEST_URL_FOO);
         mHomepageTestRule.useDefaultHomepageForTest();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/homepage/settings/HomepageSettingsFragmentWithEditorTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/homepage/settings/HomepageSettingsFragmentWithEditorTest.java
index 2edf59cc..2a12eb4 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/homepage/settings/HomepageSettingsFragmentWithEditorTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/homepage/settings/HomepageSettingsFragmentWithEditorTest.java
@@ -46,8 +46,7 @@
             "HomepageEditor should be enabled when homepage is enabled.";
     private static final String ASSERT_SWITCH_CHECKED =
             "Switch checked state when homepage is enabled.";
-    private static final String ASSERT_SWITCH_VISIBLE_WITHOUT_DUET =
-            "Switch should be visible without bottom toolbar.";
+    private static final String ASSERT_SWITCH_VISIBLE = "Switch should be visible.";
 
     public static final String TEST_URL = "http://127.0.0.1:8000/foo.html";
     public static final String CHROME_NTP = UrlConstants.NTP_NON_NATIVE_URL;
@@ -90,7 +89,7 @@
 
         launchSettingsActivity();
 
-        Assert.assertTrue(ASSERT_SWITCH_VISIBLE_WITHOUT_DUET, mSwitch.isVisible());
+        Assert.assertTrue(ASSERT_SWITCH_VISIBLE, mSwitch.isVisible());
         Assert.assertEquals(ASSERT_HOMEPAGE_MISMATCH, TEST_URL, mHomepageEditor.getSummary());
 
         Assert.assertTrue(ASSERT_SWITCH_CHECKED, mSwitch.isChecked());
@@ -105,7 +104,7 @@
 
         launchSettingsActivity();
 
-        Assert.assertTrue(ASSERT_SWITCH_VISIBLE_WITHOUT_DUET, mSwitch.isVisible());
+        Assert.assertTrue(ASSERT_SWITCH_VISIBLE, mSwitch.isVisible());
         Assert.assertEquals(ASSERT_HOMEPAGE_MISMATCH, CHROME_NTP, mHomepageEditor.getSummary());
 
         Assert.assertTrue(ASSERT_SWITCH_CHECKED, mSwitch.isChecked());
@@ -121,7 +120,7 @@
 
         launchSettingsActivity();
 
-        Assert.assertTrue(ASSERT_SWITCH_VISIBLE_WITHOUT_DUET, mSwitch.isVisible());
+        Assert.assertTrue(ASSERT_SWITCH_VISIBLE, mSwitch.isVisible());
         Assert.assertEquals(ASSERT_HOMEPAGE_MISMATCH, TEST_URL, mHomepageEditor.getSummary());
 
         Assert.assertFalse("Homepage should be disabled", HomepageManager.isHomepageEnabled());
@@ -137,7 +136,7 @@
 
         launchSettingsActivity();
 
-        Assert.assertTrue(ASSERT_SWITCH_VISIBLE_WITHOUT_DUET, mSwitch.isVisible());
+        Assert.assertTrue(ASSERT_SWITCH_VISIBLE, mSwitch.isVisible());
         Assert.assertEquals(ASSERT_HOMEPAGE_MISMATCH, TEST_URL, mHomepageEditor.getSummary());
 
         // Homepage should be enabled when start up.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/UpdateMenuItemHelperTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/UpdateMenuItemHelperTest.java
index 011bf7f..192cac1 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/UpdateMenuItemHelperTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/UpdateMenuItemHelperTest.java
@@ -288,8 +288,7 @@
     private void showAppMenuAndAssertMenuShown() throws TimeoutException {
         int currentCallCount = mMenuObserver.menuShownCallback.getCallCount();
         TestThreadUtils.runOnUiThreadBlocking(() -> {
-            AppMenuTestSupport.showAppMenu(
-                    mActivityTestRule.getAppMenuCoordinator(), null, false, false);
+            AppMenuTestSupport.showAppMenu(mActivityTestRule.getAppMenuCoordinator(), null, false);
         });
         mMenuObserver.menuShownCallback.waitForCallback(currentCallCount);
     }
@@ -324,4 +323,3 @@
         });
     }
 }
-
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/AccountPickerBottomSheetRenderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/AccountPickerBottomSheetRenderTest.java
index 2bd5d369a..12c94976 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/AccountPickerBottomSheetRenderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/AccountPickerBottomSheetRenderTest.java
@@ -20,6 +20,7 @@
 
 import androidx.test.filters.MediumTest;
 
+import org.junit.After;
 import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.BeforeClass;
@@ -31,6 +32,7 @@
 import org.chromium.base.Callback;
 import org.chromium.base.test.params.ParameterAnnotations;
 import org.chromium.base.test.params.ParameterizedRunner;
+import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.R;
@@ -42,6 +44,7 @@
 import org.chromium.chrome.browser.signin.account_picker.AccountPickerDelegate;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate;
+import org.chromium.chrome.test.util.ApplicationTestUtils;
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.chrome.test.util.browser.signin.AccountManagerTestRule;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
@@ -64,6 +67,7 @@
 @ParameterAnnotations.UseRunnerDelegate(ChromeJUnit4RunnerDelegate.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
 @Features.EnableFeatures({ChromeFeatureList.MOBILE_IDENTITY_CONSISTENCY})
+@Batch(Batch.PER_CLASS)
 public class AccountPickerBottomSheetRenderTest {
     private static final ProfileDataSource.ProfileData PROFILE_DATA1 =
             new ProfileDataSource.ProfileData(
@@ -92,7 +96,9 @@
 
     @ParameterAnnotations.UseMethodParameterBefore(NightModeTestUtils.NightModeParams.class)
     public void setupNightMode(boolean nightModeEnabled) {
-        ChromeNightModeTestUtils.setUpNightModeForChromeActivity(nightModeEnabled);
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            ChromeNightModeTestUtils.setUpNightModeForChromeActivity(nightModeEnabled);
+        });
         mRenderTestRule.setNightModeEnabled(nightModeEnabled);
     }
 
@@ -107,6 +113,11 @@
         mActivityTestRule.startMainActivityOnBlankPage();
     }
 
+    @After
+    public void tearDown() throws Exception {
+        ApplicationTestUtils.finishActivity(mActivityTestRule.getActivity());
+    }
+
     @AfterClass
     public static void tearDownAfterActivityDestroyed() {
         ChromeNightModeTestUtils.tearDownNightModeAfterChromeActivityDestroyed();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabWindowManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabWindowManagerTest.java
index 26c0f19..8a1bf17 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabWindowManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabWindowManagerTest.java
@@ -343,4 +343,33 @@
             asyncTabParamsManager.getAsyncTabParams().clear();
         }
     }
+
+    /**
+     * Tests that getTabModelForTab(...) functions properly.
+     */
+    @Test
+    @SmallTest
+    @Feature({"Multiwindow"})
+    @UiThreadTest
+    public void getTabModelForTab() {
+        final TabWindowManager manager = TabWindowManager.getInstance();
+
+        ChromeActivity activity0 = buildActivity();
+        ChromeActivity activity1 = buildActivity();
+        MockTabModelSelector selector0 = requestSelector(activity0, 0);
+        MockTabModelSelector selector1 = requestSelector(activity1, 1);
+        Tab tab1 = selector0.addMockTab();
+        Tab tab2 = selector1.addMockTab();
+        Tab tab3 = selector0.addMockIncognitoTab();
+        Tab tab4 = selector1.addMockIncognitoTab();
+
+        Assert.assertEquals(
+                selector0.getModel(/* incognito= */ false), manager.getTabModelForTab(tab1));
+        Assert.assertEquals(
+                selector1.getModel(/* incognito= */ false), manager.getTabModelForTab(tab2));
+        Assert.assertEquals(
+                selector0.getModel(/* incognito= */ true), manager.getTabModelForTab(tab3));
+        Assert.assertEquals(
+                selector1.getModel(/* incognito= */ true), manager.getTabModelForTab(tab4));
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarTest.java
deleted file mode 100644
index de0ff7d4..0000000
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarTest.java
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.toolbar.bottom;
-
-import static org.chromium.chrome.test.util.ToolbarTestUtils.BOTTOM_TOOLBAR;
-import static org.chromium.chrome.test.util.ToolbarTestUtils.BOTTOM_TOOLBAR_HOME;
-import static org.chromium.chrome.test.util.ToolbarTestUtils.BOTTOM_TOOLBAR_NEW_TAB;
-import static org.chromium.chrome.test.util.ToolbarTestUtils.BOTTOM_TOOLBAR_SEARCH;
-import static org.chromium.chrome.test.util.ToolbarTestUtils.BOTTOM_TOOLBAR_SHARE;
-import static org.chromium.chrome.test.util.ToolbarTestUtils.BOTTOM_TOOLBAR_TAB_SWITCHER;
-import static org.chromium.chrome.test.util.ToolbarTestUtils.checkToolbarButtonVisibility;
-import static org.chromium.chrome.test.util.ToolbarTestUtils.checkToolbarVisibility;
-
-import android.view.View;
-import android.view.ViewGroup;
-
-import androidx.test.filters.MediumTest;
-
-import org.junit.Assert;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.Restriction;
-import org.chromium.chrome.browser.app.ChromeActivity;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
-import org.chromium.chrome.browser.flags.ChromeSwitches;
-import org.chromium.chrome.browser.toolbar.bottom.BottomToolbarVariationManager.Variations;
-import org.chromium.chrome.test.ChromeActivityTestRule;
-import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.chrome.test.util.OverviewModeBehaviorWatcher;
-import org.chromium.chrome.test.util.browser.Features;
-import org.chromium.content_public.browser.test.util.TestThreadUtils;
-import org.chromium.ui.test.util.UiRestriction;
-
-import java.util.concurrent.ExecutionException;
-
-/**
- * Integration tests for the bottom toolbar.
- */
-@RunWith(ChromeJUnit4ClassRunner.class)
-// clang-format off
-@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
-@Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
-@Features.EnableFeatures({ChromeFeatureList.CHROME_DUET})
-public class BottomToolbarTest {
-    //clang-format on
-    @Rule
-    public ChromeActivityTestRule<ChromeActivity> mActivityTestRule =
-            new ChromeActivityTestRule<>(ChromeActivity.class);
-
-
-    @Test
-    @MediumTest
-    public void testBottomToolbar_Home_Search_Tab_Switcher() throws ExecutionException {
-        BottomToolbarVariationManager.setVariation(Variations.HOME_SEARCH_TAB_SWITCHER);
-        mActivityTestRule.startMainActivityOnBlankPage();
-
-        checkToolbarVisibility(BOTTOM_TOOLBAR, true);
-        checkToolbarButtonVisibility(BOTTOM_TOOLBAR, BOTTOM_TOOLBAR_HOME, true);
-        checkToolbarButtonVisibility(BOTTOM_TOOLBAR, BOTTOM_TOOLBAR_SEARCH, true);
-        checkToolbarButtonVisibility(BOTTOM_TOOLBAR, BOTTOM_TOOLBAR_TAB_SWITCHER, true);
-        checkToolbarButtonVisibility(BOTTOM_TOOLBAR, BOTTOM_TOOLBAR_NEW_TAB, false);
-        checkToolbarButtonVisibility(BOTTOM_TOOLBAR, BOTTOM_TOOLBAR_SHARE, false);
-
-        ViewGroup bottomToolbar = mActivityTestRule.getActivity().findViewById(BOTTOM_TOOLBAR);
-        View tabSwitcherButton = bottomToolbar.findViewById(BOTTOM_TOOLBAR_TAB_SWITCHER);
-
-        OverviewModeBehaviorWatcher overviewModeWatcher = new OverviewModeBehaviorWatcher(
-                mActivityTestRule.getActivity().getOverviewModeBehavior(), true, false);
-        TestThreadUtils.runOnUiThreadBlocking(() -> tabSwitcherButton.callOnClick());
-        overviewModeWatcher.waitForBehavior();
-
-        Assert.assertTrue("Tab switcher should be visible.",
-                mActivityTestRule.getActivity().getOverviewModeBehavior().overviewVisible());
-    }
-
-    @Test
-    @MediumTest
-    public void testBottomToolbar_New_Tab_Search_Share() throws ExecutionException {
-        BottomToolbarVariationManager.setVariation(Variations.NEW_TAB_SEARCH_SHARE);
-        mActivityTestRule.startMainActivityOnBlankPage();
-
-        checkToolbarVisibility(BOTTOM_TOOLBAR, true);
-        checkToolbarButtonVisibility(BOTTOM_TOOLBAR, BOTTOM_TOOLBAR_NEW_TAB, true);
-        checkToolbarButtonVisibility(BOTTOM_TOOLBAR, BOTTOM_TOOLBAR_SEARCH, true);
-        checkToolbarButtonVisibility(BOTTOM_TOOLBAR, BOTTOM_TOOLBAR_SHARE, true);
-        checkToolbarButtonVisibility(BOTTOM_TOOLBAR, BOTTOM_TOOLBAR_HOME, false);
-        checkToolbarButtonVisibility(BOTTOM_TOOLBAR, BOTTOM_TOOLBAR_TAB_SWITCHER, false);
-
-        int tabCount = mActivityTestRule.getActivity().getCurrentTabModel().getCount();
-        ViewGroup bottomToolbar = mActivityTestRule.getActivity().findViewById(BOTTOM_TOOLBAR);
-        View newTabButton = bottomToolbar.findViewById(BOTTOM_TOOLBAR_NEW_TAB);
-        TestThreadUtils.runOnUiThreadBlocking(() -> newTabButton.callOnClick());
-
-        Assert.assertEquals(
-                tabCount + 1, mActivityTestRule.getActivity().getCurrentTabModel().getCount());
-    }
-
-    @Test
-    @MediumTest
-    public void testBottomToolbar_Home_Search_Share() {
-        BottomToolbarVariationManager.setVariation(Variations.HOME_SEARCH_SHARE);
-        mActivityTestRule.startMainActivityOnBlankPage();
-
-        checkToolbarVisibility(BOTTOM_TOOLBAR, true);
-        checkToolbarButtonVisibility(BOTTOM_TOOLBAR, BOTTOM_TOOLBAR_HOME, true);
-        checkToolbarButtonVisibility(BOTTOM_TOOLBAR, BOTTOM_TOOLBAR_SEARCH, true);
-        checkToolbarButtonVisibility(BOTTOM_TOOLBAR, BOTTOM_TOOLBAR_SHARE, true);
-        checkToolbarButtonVisibility(BOTTOM_TOOLBAR, BOTTOM_TOOLBAR_NEW_TAB, false);
-        checkToolbarButtonVisibility(BOTTOM_TOOLBAR, BOTTOM_TOOLBAR_TAB_SWITCHER, false);
-    }
-}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarWithStartSurfaceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarWithStartSurfaceTest.java
deleted file mode 100644
index c972be3..0000000
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarWithStartSurfaceTest.java
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.toolbar.bottom;
-
-import static org.chromium.chrome.test.util.ToolbarTestUtils.BOTTOM_TOOLBAR;
-import static org.chromium.chrome.test.util.ToolbarTestUtils.checkToolbarVisibility;
-
-import androidx.test.filters.MediumTest;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.Restriction;
-import org.chromium.chrome.browser.compositor.layouts.OverviewModeState;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
-import org.chromium.chrome.browser.flags.ChromeSwitches;
-import org.chromium.chrome.browser.toolbar.bottom.BottomToolbarVariationManager.Variations;
-import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
-import org.chromium.chrome.test.util.browser.Features;
-import org.chromium.content_public.browser.test.util.TestThreadUtils;
-import org.chromium.ui.test.util.UiRestriction;
-
-/**
- * Test bottom toolbar when start surface is enabled.
- */
-@RunWith(ChromeJUnit4ClassRunner.class)
-// clang-format off
-@Restriction(
-        {UiRestriction.RESTRICTION_TYPE_PHONE, Restriction.RESTRICTION_TYPE_NON_LOW_END_DEVICE})
-@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
-        "enable-features=" + ChromeFeatureList.START_SURFACE_ANDROID + "<Study",
-        "force-fieldtrials=Study/Group",
-        "force-fieldtrial-params=Study.Group:start_surface_variation/single"})
-@Features.EnableFeatures({ChromeFeatureList.CHROME_DUET, ChromeFeatureList.START_SURFACE_ANDROID})
-public class BottomToolbarWithStartSurfaceTest {
-    //clang-format on
-    @Rule
-    public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
-
-    @Before
-    public void setUp() {
-        BottomToolbarVariationManager.setVariation(Variations.HOME_SEARCH_SHARE);
-
-        // TODO(crbug.com/1051226): Test start activity with startMainActivityFromLauncher, so there
-        // is no tab will be created, then the single start surface should be shown if it is
-        // enabled.
-        mActivityTestRule.startMainActivityOnBlankPage();
-    }
-
-    @Test
-    @MediumTest
-    public void testShowAndHideSingleSurface() {
-        checkToolbarVisibility(BOTTOM_TOOLBAR, true);
-        TestThreadUtils.runOnUiThreadBlocking(
-                ()
-                        -> mActivityTestRule.getActivity()
-                                   .getStartSurface()
-                                   .getController()
-                                   .setOverviewState(OverviewModeState.SHOWING_HOMEPAGE));
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> mActivityTestRule.getActivity().getLayoutManager().showOverview(false));
-        checkToolbarVisibility(BOTTOM_TOOLBAR, false);
-        TestThreadUtils.runOnUiThreadBlocking(
-                ()
-                        -> mActivityTestRule.getActivity()
-                                   .getStartSurface()
-                                   .getController()
-                                   .setOverviewState(OverviewModeState.SHOWING_TABSWITCHER));
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> mActivityTestRule.getActivity().getLayoutManager().showOverview(false));
-        checkToolbarVisibility(BOTTOM_TOOLBAR, false);
-        TestThreadUtils.runOnUiThreadBlocking(
-                ()
-                        -> mActivityTestRule.getActivity()
-                                   .getStartSurface()
-                                   .getController()
-                                   .setOverviewState(OverviewModeState.NOT_SHOWN));
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> mActivityTestRule.getActivity().getLayoutManager().hideOverview(false));
-        checkToolbarVisibility(BOTTOM_TOOLBAR, true);
-    }
-}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/top/AdaptiveToolbarTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/top/AdaptiveToolbarTest.java
index e1155054..4f89bb6d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/top/AdaptiveToolbarTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/top/AdaptiveToolbarTest.java
@@ -64,8 +64,7 @@
                 ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
     }
 
-    private void setupFlagsAndLaunchActivity(
-            boolean isBottomToolbarEnabled, boolean isGridTabSwitcherEnabled) {
+    private void setupFlagsAndLaunchActivity(boolean isGridTabSwitcherEnabled) {
         CachedFeatureFlags.setForTesting(
                 ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID, isGridTabSwitcherEnabled);
         mActivityTestRule.startMainActivityOnBlankPage();
@@ -78,9 +77,9 @@
     // clang-format off
     @CommandLineFlags.Add({"enable-features=" + ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID +
             "<Study", "force-fieldtrials=Study/Group", NO_NEW_TAB_VARIATION_PARAMS})
-    public void testTopToolbar_WithGTS_WithoutBottomToolbar() throws InterruptedException {
+    public void testTopToolbar_WithGTS() throws InterruptedException {
         // clang-format on
-        setupFlagsAndLaunchActivity(false, true);
+        setupFlagsAndLaunchActivity(true);
         final ChromeTabbedActivity cta = mActivityTestRule.getActivity();
         Layout layout = cta.getLayoutManager().getOverviewLayout();
         assertTrue(layout instanceof StartSurfaceLayout);
@@ -96,7 +95,6 @@
         checkToolbarButtonVisibility(TAB_SWITCHER_TOOLBAR, TAB_SWITCHER_TOOLBAR_NEW_TAB, true);
     }
 
-
     @Test
     @MediumTest
     // clang-format off
@@ -104,7 +102,7 @@
             "<Study", "force-fieldtrials=Study/Group", NEW_TAB_VARIATION_PARAMS})
     public void testTopToolbar_NewTabVariation() {
         // clang-format on
-        setupFlagsAndLaunchActivity(false, true);
+        setupFlagsAndLaunchActivity(true);
         final ChromeTabbedActivity cta = mActivityTestRule.getActivity();
         Layout layout = cta.getLayoutManager().getOverviewLayout();
         assertTrue(layout instanceof StartSurfaceLayout);
@@ -132,7 +130,7 @@
     public void testTopToolbar_NewTabVariation_IncognitoDisabled() {
         // clang-format on
         IncognitoUtils.setEnabledForTesting(false);
-        setupFlagsAndLaunchActivity(false, true);
+        setupFlagsAndLaunchActivity(true);
         final ChromeTabbedActivity cta = mActivityTestRule.getActivity();
         Layout layout = cta.getLayoutManager().getOverviewLayout();
         assertTrue(layout instanceof StartSurfaceLayout);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateUnitTest.java
index 8a0f616..8e7942c 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateUnitTest.java
@@ -100,7 +100,6 @@
         when(mTab.getWebContents()).thenReturn(mWebContents);
         when(mWebContents.getNavigationController()).thenReturn(mNavigationController);
         when(mNavigationController.getUseDesktopUserAgent()).thenReturn(false);
-        when(mToolbarManager.isMenuFromBottom()).thenReturn(false);
         when(mTabModelSelector.getCurrentModel()).thenReturn(mTabModel);
         when(mTabModelSelector.getModel(false)).thenReturn((mTabModel));
         when(mTabModelSelector.getModel(true)).thenReturn((mIncognitoTabModel));
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegateUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegateUnitTest.java
index 032ec97..7d7edc1 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegateUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegateUnitTest.java
@@ -107,7 +107,6 @@
         when(mTab.getWebContents()).thenReturn(mWebContents);
         when(mWebContents.getNavigationController()).thenReturn(mNavigationController);
         when(mNavigationController.getUseDesktopUserAgent()).thenReturn(false);
-        when(mToolbarManager.isMenuFromBottom()).thenReturn(false);
         when(mTabModelSelector.getCurrentModel()).thenReturn(mTabModel);
         when(mTabModelSelector.getModel(false)).thenReturn((mTabModel));
         when(mTabModel.isIncognito()).thenReturn(false);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/toolbar/ToolbarTabControllerImplTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/toolbar/ToolbarTabControllerImplTest.java
index 96f9ece7..5f9e6ef 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/toolbar/ToolbarTabControllerImplTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/toolbar/ToolbarTabControllerImplTest.java
@@ -57,8 +57,6 @@
     @Mock
     private Tab mTab;
     @Mock
-    private Supplier<Boolean> mBottomToolbarVisibilitySupplier;
-    @Mock
     private Supplier<Boolean> mOverrideHomePageSupplier;
     @Mock
     private Supplier<Profile> mProfileSupplier;
@@ -77,12 +75,11 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         doReturn(mTab).when(mTabSupplier).get();
-        doReturn(false).when(mBottomToolbarVisibilitySupplier).get();
         doReturn(false).when(mOverrideHomePageSupplier).get();
         TrackerFactory.setTrackerForTests(mTracker);
-        mToolbarTabController = new ToolbarTabControllerImpl(mTabSupplier,
-                mBottomToolbarVisibilitySupplier, mOverrideHomePageSupplier, mProfileSupplier,
-                mBottomControlsCoordinatorSupplier, mRunnable);
+        mToolbarTabController =
+                new ToolbarTabControllerImpl(mTabSupplier, mOverrideHomePageSupplier,
+                        mProfileSupplier, mBottomControlsCoordinatorSupplier, mRunnable);
     }
 
     @Test
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index 55705c03..53455280 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-87.0.4258.0_rc-r1-merged.afdo.bz2
+chromeos-chrome-amd64-87.0.4260.0_rc-r1-merged.afdo.bz2
diff --git a/chrome/app/chrome_main_delegate.cc b/chrome/app/chrome_main_delegate.cc
index e8121085..2a25e584 100644
--- a/chrome/app/chrome_main_delegate.cc
+++ b/chrome/app/chrome_main_delegate.cc
@@ -600,6 +600,11 @@
   chrome::InitializeCpuAffinityExperiments();
 #endif
 
+#if defined(OS_CHROMEOS)
+  // Threading features.
+  base::PlatformThread::InitThreadPostFieldTrial();
+#endif
+
 #if BUILDFLAG(ENABLE_GWP_ASAN_MALLOC)
   {
     version_info::Channel channel = chrome::GetChannel();
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 7fcf978c..687e771 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -3680,7 +3680,7 @@
       <message name="IDS_EXTENSION_DISABLED_ERROR_LABEL" desc="Text displayed when an extension was disabled due to a new upgrade requiring an explicit permission check from the user.">
         To re-enable it, accept the new permissions:
       </message>
-      <message name="IDS_EXTENSION_IS_BLACKLISTED" desc="Text displayed in an infobar when an extension is blacklisted and prevented from being installed.">
+      <message name="IDS_EXTENSION_IS_BLOCKLISTED" desc="Text displayed in an infobar when an extension is blocklisted and prevented from being installed.">
         Google has flagged "<ph name="EXTENSION_NAME">$1<ex>Google Talk</ex></ph>" as malicious and installation has been prevented
       </message>
       <message name="IDS_EXTENSION_DISABLED_REMOTE_INSTALL_ERROR_TITLE" desc="Title of the notification that an extension or app was disabled due to it being installed server side, requiring an explicit permission check from the user.">
@@ -3805,13 +3805,13 @@
        <message name="IDS_EXTENSION_AND_APP_ALERT_TITLE" desc="Titlebar of the extension and app notification alert">
         These items may be dangerous
       </message>
-      <message name="IDS_EXTENSION_ALERT_ITEM_BLACKLISTED_OTHER" desc="A statement that an extension has been newly blacklisted. https://support.google.com/chrome/answer/1210215 contains the language on which we're basing the phrasing.">
+      <message name="IDS_EXTENSION_ALERT_ITEM_BLOCKLISTED_OTHER" desc="A statement that an extension has been newly blocklisted. https://support.google.com/chrome/answer/1210215 contains the language on which we're basing the phrasing.">
         The extension "<ph name="EXTENSION_NAME">$1<ex>Gmail Checker</ex></ph>" was automatically disabled.
       </message>
-      <message name="IDS_EXTENSION_ALERT_ITEM_BLACKLISTED_MALWARE" desc="A statement that an extension has been newly blacklisted for malware.">
+      <message name="IDS_EXTENSION_ALERT_ITEM_BLOCKLISTED_MALWARE" desc="A statement that an extension has been newly blocklisted for malware.">
         "<ph name="EXTENSION_NAME">$1<ex>Gmail Checker</ex></ph>" has been disabled because it contains malware
       </message>
-      <message name="IDS_APP_ALERT_ITEM_BLACKLISTED_OTHER" desc="A statement that a packaged app has been newly blacklisted. https://support.google.com/chrome/answer/1210215 contains the language on which we're basing the phrasing.">
+      <message name="IDS_APP_ALERT_ITEM_BLOCKLISTED_OTHER" desc="A statement that a packaged app has been newly blocklisted. https://support.google.com/chrome/answer/1210215 contains the language on which we're basing the phrasing.">
         The app "<ph name="EXTENSION_NAME">$1<ex>Gmail</ex></ph>" was automatically removed.
       </message>
       <message name="IDS_EXTENSION_ALERT_ITEM_OK" desc="The title of the default button acknowledging the information presented.">
@@ -4170,7 +4170,7 @@
           Read and change saved password settings
         </message>
         <message name="IDS_EXTENSION_PROMPT_WARNING_USERS_PRIVATE" desc="Permission string for access to user accounts.">
-          Read and change whitelisted users
+          Read and change allowlisted users
         </message>
         <message name="IDS_EXTENSION_PROMPT_WARNING_DISPLAY_SOURCE" desc="Permission string for access to Display Source API.">
           Send audio and video to displays on the local network
@@ -4234,7 +4234,7 @@
       <message name="IDS_EXTENSION_INSTALL_DEPENDENCY_NOT_SHARED_MODULE" desc="Error displayed during installation of an extension which tries to imports resources from an extension which is not a shared module.">
         Unable to import extension "<ph name="IMPORT_NAME">$1<ex>Gmail</ex></ph>" because it is not a shared module
       </message>
-      <message name="IDS_EXTENSION_INSTALL_DEPENDENCY_NOT_WHITELISTED" desc="Error displayed during installation of an extension which tries to imports resources from an extension, but it is not whitelisted to do so.">
+      <message name="IDS_EXTENSION_INSTALL_DEPENDENCY_NOT_ALLOWLISTED" desc="Error displayed during installation of an extension which tries to imports resources from an extension, but it is not allowlisted to do so.">
         Unable to install "<ph name="APP_NAME">$1<ex>Google Play Movies &amp; TV</ex></ph>" because it is not allowed by "<ph name="IMPORT_NAME">$2<ex>Google Cast API</ex></ph>"
       </message>
       <message name="IDS_EXTENSION_INSTALL_GALLERY_ONLY" desc="Error displayed when an app or extension that has an update URL used by the gallery is installed when not directly downloaded from the gallery.">
@@ -7020,7 +7020,7 @@
           Installed because of dependent extension(s).
         </message>
 
-        <!-- Extension blacklist state -->
+        <!-- Extension blocklist state -->
         <message name="IDS_EXTENSIONS_BLOCKLISTED_MALWARE" desc="The text explaining the reason for disabling extension or app. The extension in question contains malware.">
           This extension contains malware.
         </message>
diff --git a/chrome/app/generated_resources_grd/IDS_APP_ALERT_ITEM_BLOCKLISTED_OTHER.png.sha1 b/chrome/app/generated_resources_grd/IDS_APP_ALERT_ITEM_BLOCKLISTED_OTHER.png.sha1
new file mode 100644
index 0000000..91e7921
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_APP_ALERT_ITEM_BLOCKLISTED_OTHER.png.sha1
@@ -0,0 +1 @@
+a38339762b672d9fa366278b7cc4d7d0910d44c7
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_EXTENSION_ALERT_ITEM_BLACKLISTED_MALWARE.png.sha1 b/chrome/app/generated_resources_grd/IDS_EXTENSION_ALERT_ITEM_BLACKLISTED_MALWARE.png.sha1
deleted file mode 100644
index a0b2752..0000000
--- a/chrome/app/generated_resources_grd/IDS_EXTENSION_ALERT_ITEM_BLACKLISTED_MALWARE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-6d5c915c70316c01acf1e48f0d6704f8def440ca
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_EXTENSION_ALERT_ITEM_BLOCKLISTED_MALWARE.png.sha1 b/chrome/app/generated_resources_grd/IDS_EXTENSION_ALERT_ITEM_BLOCKLISTED_MALWARE.png.sha1
new file mode 100644
index 0000000..2f19627
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_EXTENSION_ALERT_ITEM_BLOCKLISTED_MALWARE.png.sha1
@@ -0,0 +1 @@
+a7450f2679a1fee0eb30c2d8ec3bd9ee7af6932c
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_EXTENSION_ALERT_ITEM_BLOCKLISTED_OTHER.png.sha1 b/chrome/app/generated_resources_grd/IDS_EXTENSION_ALERT_ITEM_BLOCKLISTED_OTHER.png.sha1
new file mode 100644
index 0000000..64c39c7
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_EXTENSION_ALERT_ITEM_BLOCKLISTED_OTHER.png.sha1
@@ -0,0 +1 @@
+de2721ea976cb34ca020f4ddaeaa6b82541a4d60
\ No newline at end of file
diff --git a/chrome/app/vector_icons/user_account_avatar.icon b/chrome/app/vector_icons/user_account_avatar.icon
index 006dec9a..18953f2 100644
--- a/chrome/app/vector_icons/user_account_avatar.icon
+++ b/chrome/app/vector_icons/user_account_avatar.icon
@@ -2,22 +2,22 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-CANVAS_DIMENSIONS, 40,
-MOVE_TO, 20.52f, 3,
-CUBIC_TO, 10.62f, 3, 3, 10.62f, 3, 20.52f,
-CUBIC_TO, 3, 29.39f, 10.62f, 37, 20.52f, 37,
-CUBIC_TO, 29.39f, 37, 37, 29.38f, 37, 20.52f,
-CUBIC_TO, 37, 10.62f, 29.38f, 3, 20.52f, 3,
+CANVAS_DIMENSIONS, 64,
+MOVE_TO, 32, 0,
+CUBIC_TO, 14.34f, 0, 0, 14.34f, 0, 32,
+CUBIC_TO, 0, 49.66f, 14.34f, 64, 32, 64,
+CUBIC_TO, 49.66f, 64, 64, 49.66f, 64, 32,
+CUBIC_TO, 64, 14.34f, 49.66f, 0, 32, 0,
 CLOSE,
-R_MOVE_TO, 0, 5.15f,
-R_CUBIC_TO, 2.85f, 0, 5.15f, 2.3f, 5.15f, 5.15f,
-R_CUBIC_TO, 0, 2.85f, -2.3f, 5.15f, -5.15f, 5.15f,
-R_CUBIC_TO, -2.85f, 0, -5.15f, -2.3f, -5.15f, -5.15f,
-R_CUBIC_TO, 0, -2.85f, 2.3f, -5.15f, 5.15f, -5.15f,
+MOVE_TO, 32, 9.6f,
+CUBIC_TO, 37.31f, 9.6f, 41.6f, 13.89f, 41.6f, 19.2f,
+CUBIC_TO, 41.6f, 24.51f, 37.31f, 28.8f, 32, 28.8f,
+CUBIC_TO, 26.69f, 28.8f, 22.4f, 24.51f, 22.4f, 19.2f,
+CUBIC_TO, 22.4f, 13.89f, 26.69f, 9.6f, 32, 9.6f,
 CLOSE,
-R_MOVE_TO, 0, 23.7f,
-R_CUBIC_TO, -4.29f, 0, -8.09f, -2.09f, -10.3f, -5.15f,
-R_CUBIC_TO, 0.05f, -3.37f, 6.87f, -5.15f, 10.3f, -5.15f,
-R_CUBIC_TO, 3.42f, 0, 10.25f, 1.78f, 10.3f, 5.15f,
-R_CUBIC_TO, -2.21f, 3.06f, -6.01f, 5.15f, -10.3f, 5.15f,
+MOVE_TO, 32, 55.04f,
+CUBIC_TO, 24, 55.04f, 16.93f, 50.94f, 12.8f, 44.74f,
+CUBIC_TO, 12.9f, 38.37f, 25.6f, 34.88f, 32, 34.88f,
+CUBIC_TO, 38.37f, 34.88f, 51.1f, 38.37f, 51.2f, 44.74f,
+CUBIC_TO, 47.07f, 50.94f, 40, 55.04f, 32, 55.04f,
 CLOSE
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index ab327752..c15789d 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1514,6 +1514,7 @@
     "sessions/session_tab_helper_factory.h",
     "sessions/tab_restore_service_factory.cc",
     "sessions/tab_restore_service_factory.h",
+    "sharesheet/share_action.cc",
     "sharesheet/share_action.h",
     "sharesheet/sharesheet_action_cache.cc",
     "sharesheet/sharesheet_action_cache.h",
@@ -4033,6 +4034,8 @@
       "renderer_context_menu/quick_answers_menu_observer.h",
       "resource_coordinator/tab_manager_delegate_chromeos.cc",
       "resource_coordinator/tab_manager_delegate_chromeos.h",
+      "sharesheet/drive_share_action.cc",
+      "sharesheet/drive_share_action.h",
       "shell_integration_chromeos.cc",
       "signin/signin_status_metrics_provider_chromeos.cc",
       "signin/signin_status_metrics_provider_chromeos.h",
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index eb0a5bf9..789b541d 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -312,7 +312,6 @@
 #include "content/public/common/url_constants.h"
 #include "content/public/common/url_utils.h"
 #include "content/public/common/user_agent.h"
-#include "content/public/common/web_preferences.h"
 #include "content/public/common/window_container_type.mojom-shared.h"
 #include "device/vr/buildflags/buildflags.h"
 #include "extensions/browser/process_map.h"
@@ -356,6 +355,7 @@
 #include "third_party/blink/public/common/loader/referrer_utils.h"
 #include "third_party/blink/public/common/loader/url_loader_throttle.h"
 #include "third_party/blink/public/common/switches.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "third_party/blink/public/mojom/renderer_preferences.mojom.h"
 #include "third_party/blink/public/mojom/site_engagement/site_engagement.mojom.h"
 #include "third_party/blink/public/mojom/user_agent/user_agent_metadata.mojom.h"
@@ -635,6 +635,7 @@
 #endif
 
 using base::FileDescriptor;
+using blink::web_pref::WebPreferences;
 using content::BrowserThread;
 using content::BrowserURLHandler;
 using content::BrowsingDataFilterBuilder;
@@ -644,7 +645,6 @@
 using content::RenderViewHost;
 using content::SiteInstance;
 using content::WebContents;
-using content::WebPreferences;
 using message_center::NotifierId;
 
 #if defined(OS_POSIX)
@@ -3303,11 +3303,13 @@
       prefs->GetString(prefs::kAnimationPolicy);
   if (image_animation_policy == kAnimationPolicyOnce)
     web_prefs->animation_policy =
-        content::IMAGE_ANIMATION_POLICY_ANIMATION_ONCE;
+        blink::web_pref::IMAGE_ANIMATION_POLICY_ANIMATION_ONCE;
   else if (image_animation_policy == kAnimationPolicyNone)
-    web_prefs->animation_policy = content::IMAGE_ANIMATION_POLICY_NO_ANIMATION;
+    web_prefs->animation_policy =
+        blink::web_pref::IMAGE_ANIMATION_POLICY_NO_ANIMATION;
   else
-    web_prefs->animation_policy = content::IMAGE_ANIMATION_POLICY_ALLOWED;
+    web_prefs->animation_policy =
+        blink::web_pref::IMAGE_ANIMATION_POLICY_ALLOWED;
 #endif
 
   // Make sure we will set the default_encoding with canonical encoding name.
@@ -3487,17 +3489,18 @@
     // If autoplay is allowed by policy then force the no user gesture required
     // autoplay policy.
     web_prefs->autoplay_policy =
-        content::AutoplayPolicy::kNoUserGestureRequired;
+        blink::web_pref::AutoplayPolicy::kNoUserGestureRequired;
   } else if (base::FeatureList::IsEnabled(media::kAutoplayDisableSettings) &&
              web_prefs->autoplay_policy ==
-                 content::AutoplayPolicy::kDocumentUserActivationRequired) {
+                 blink::web_pref::AutoplayPolicy::
+                     kDocumentUserActivationRequired) {
     // If the autoplay disable settings feature is enabled and the autoplay
     // policy is set to using the unified policy then set the default autoplay
     // policy based on user preference.
     web_prefs->autoplay_policy =
         UnifiedAutoplayConfig::ShouldBlockAutoplay(profile)
-            ? content::AutoplayPolicy::kDocumentUserActivationRequired
-            : content::AutoplayPolicy::kNoUserGestureRequired;
+            ? blink::web_pref::AutoplayPolicy::kDocumentUserActivationRequired
+            : blink::web_pref::AutoplayPolicy::kNoUserGestureRequired;
   }
 
   auto* native_theme = GetWebTheme();
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index 972aff1..1f90198 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -42,6 +42,9 @@
 class WindowFeatures;
 class WebUsbService;
 }  // namespace mojom
+namespace web_pref {
+struct WebPreferences;
+}  // namespace web_pref
 class URLLoaderThrottle;
 }  // namespace blink
 
@@ -336,10 +339,10 @@
 #endif
   content::TtsPlatform* GetTtsPlatform() override;
   void OverrideWebkitPrefs(content::RenderViewHost* rvh,
-                           content::WebPreferences* prefs) override;
+                           blink::web_pref::WebPreferences* prefs) override;
   bool OverrideWebPreferencesAfterNavigation(
       content::WebContents* web_contents,
-      content::WebPreferences* prefs) override;
+      blink::web_pref::WebPreferences* prefs) override;
   void BrowserURLHandlerCreated(content::BrowserURLHandler* handler) override;
   base::FilePath GetDefaultDownloadDirectory() override;
   std::string GetDefaultDownloadName() override;
diff --git a/chrome/browser/chrome_content_browser_client_parts.h b/chrome/browser/chrome_content_browser_client_parts.h
index 8add63d..00e2a85 100644
--- a/chrome/browser/chrome_content_browser_client_parts.h
+++ b/chrome/browser/chrome_content_browser_client_parts.h
@@ -17,6 +17,9 @@
 }
 
 namespace blink {
+namespace web_pref {
+struct WebPreferences;
+}  // namespace web_pref
 class AssociatedInterfaceRegistry;
 }
 
@@ -26,7 +29,6 @@
 class RenderProcessHost;
 class RenderViewHost;
 class SiteInstance;
-struct WebPreferences;
 }
 
 namespace storage {
@@ -46,7 +48,8 @@
   virtual void SiteInstanceGotProcess(content::SiteInstance* site_instance) {}
   virtual void SiteInstanceDeleting(content::SiteInstance* site_instance) {}
   virtual void OverrideWebkitPrefs(content::RenderViewHost* rvh,
-                                   content::WebPreferences* web_prefs) {}
+                                   blink::web_pref::WebPreferences* web_prefs) {
+  }
   virtual void BrowserURLHandlerCreated(content::BrowserURLHandler* handler) {}
   virtual void GetAdditionalAllowedSchemesForFileSystem(
       std::vector<std::string>* additional_allowed_schemes) {}
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 3296ef2..ae5d1bae 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -957,6 +957,8 @@
     "crosapi/screen_manager_ash.h",
     "crosapi/select_file_ash.cc",
     "crosapi/select_file_ash.h",
+    "crosapi/test_mojo_connection_manager.cc",
+    "crosapi/test_mojo_connection_manager.h",
     "crostini/ansible/ansible_management_service.cc",
     "crostini/ansible/ansible_management_service.h",
     "crostini/ansible/ansible_management_service_factory.cc",
@@ -2680,6 +2682,8 @@
     "web_applications/system_web_app_install_utils.h",
     "web_applications/terminal_source.cc",
     "web_applications/terminal_source.h",
+    "web_applications/terminal_system_web_app_info.cc",
+    "web_applications/terminal_system_web_app_info.h",
     "wilco_dtc_supportd/fake_wilco_dtc_supportd_client.cc",
     "wilco_dtc_supportd/fake_wilco_dtc_supportd_client.h",
     "wilco_dtc_supportd/wilco_dtc_supportd_bridge.cc",
@@ -3156,6 +3160,7 @@
     "concierge_helper_service_unittest.cc",
     "crosapi/browser_util_unittest.cc",
     "crosapi/message_center_ash_unittest.cc",
+    "crosapi/test_mojo_connection_manager_unittest.cc",
     "crostini/ansible/ansible_management_service_unittest.cc",
     "crostini/crostini_disk_unittest.cc",
     "crostini/crostini_export_import_unittest.cc",
diff --git a/chrome/browser/chromeos/accessibility/spoken_feedback_browsertest.cc b/chrome/browser/chromeos/accessibility/spoken_feedback_browsertest.cc
index 1f5b279..b27d786 100644
--- a/chrome/browser/chromeos/accessibility/spoken_feedback_browsertest.cc
+++ b/chrome/browser/chromeos/accessibility/spoken_feedback_browsertest.cc
@@ -537,8 +537,9 @@
   sm_.Call([this]() { SendKeyPressWithShift(ui::VKEY_TAB); });
   sm_.ExpectSpeechPattern(
       "Chrom* - data:text slash html;charset equal utf-8, less than button "
-      "autofocus greater than Click me less than slash button greater than , "
-      "window");
+      "autofocus greater than Click me less than slash button greater than");
+  sm_.ExpectSpeechPattern("Press Ctrl plus W to close.");
+  sm_.ExpectSpeechPattern(", window");
 
   sm_.Replay();
 }
diff --git a/chrome/browser/chromeos/chrome_content_browser_client_chromeos_part.cc b/chrome/browser/chromeos/chrome_content_browser_client_chromeos_part.cc
index 951f986f..f986c2ba 100644
--- a/chrome/browser/chromeos/chrome_content_browser_client_chromeos_part.cc
+++ b/chrome/browser/chromeos/chrome_content_browser_client_chromeos_part.cc
@@ -22,8 +22,8 @@
 #include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/url_constants.h"
-#include "content/public/common/web_preferences.h"
 #include "extensions/common/constants.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 
 namespace {
 
@@ -69,8 +69,9 @@
   return false;
 }
 
-void OverrideWebkitPrefsForTabletMode(content::WebContents* contents,
-                                      content::WebPreferences* web_prefs) {
+void OverrideWebkitPrefsForTabletMode(
+    content::WebContents* contents,
+    blink::web_pref::WebPreferences* web_prefs) {
   // Enable some mobile-like behaviors when in tablet mode on Chrome OS.
   if (!ash::TabletMode::Get() || !ash::TabletMode::Get()->InTabletMode())
     return;
@@ -95,7 +96,7 @@
 }
 
 void OverrideFontSize(content::WebContents* contents,
-                      content::WebPreferences* web_prefs) {
+                      blink::web_pref::WebPreferences* web_prefs) {
   DCHECK(contents);
   // Check the URL because |contents| may not yet be associated with a window,
   // SettingsWindowManager, etc.
@@ -103,7 +104,7 @@
   if (!url.is_empty() && UseDefaultFontSize(url)) {
     // System dialogs are considered native UI, so they do not follow the
     // browser's web-page font sizes. Reset fonts to the base sizes.
-    content::WebPreferences base_prefs;
+    blink::web_pref::WebPreferences base_prefs;
     web_prefs->default_font_size = base_prefs.default_font_size;
     web_prefs->default_fixed_font_size = base_prefs.default_fixed_font_size;
   }
@@ -119,7 +120,7 @@
 
 void ChromeContentBrowserClientChromeOsPart::OverrideWebkitPrefs(
     content::RenderViewHost* rvh,
-    content::WebPreferences* web_prefs) {
+    blink::web_pref::WebPreferences* web_prefs) {
   content::WebContents* contents =
       content::WebContents::FromRenderViewHost(rvh);
 
diff --git a/chrome/browser/chromeos/chrome_content_browser_client_chromeos_part.h b/chrome/browser/chromeos/chrome_content_browser_client_chromeos_part.h
index 02ca28ed..4a6345f 100644
--- a/chrome/browser/chromeos/chrome_content_browser_client_chromeos_part.h
+++ b/chrome/browser/chromeos/chrome_content_browser_client_chromeos_part.h
@@ -18,7 +18,7 @@
 
   // ChromeContentBrowserClientParts:
   void OverrideWebkitPrefs(content::RenderViewHost* rvh,
-                           content::WebPreferences* web_prefs) override;
+                           blink::web_pref::WebPreferences* web_prefs) override;
 
   static bool UseDefaultFontSizeForTest(const GURL& url);
 
diff --git a/chrome/browser/chromeos/chrome_content_browser_client_chromeos_part_browsertest.cc b/chrome/browser/chromeos/chrome_content_browser_client_chromeos_part_browsertest.cc
index b6aefb7..39f55f8 100644
--- a/chrome/browser/chromeos/chrome_content_browser_client_chromeos_part_browsertest.cc
+++ b/chrome/browser/chromeos/chrome_content_browser_client_chromeos_part_browsertest.cc
@@ -12,8 +12,8 @@
 #include "chrome/test/base/in_process_browser_test.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/render_view_host.h"
-#include "content/public/common/web_preferences.h"
 #include "content/public/test/browser_test.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 
 using ChromeContentBrowserClientChromeOsPartTest = InProcessBrowserTest;
 
@@ -24,7 +24,7 @@
       ->system_web_app_manager()
       .InstallSystemAppsForTesting();
 
-  const content::WebPreferences kDefaultPrefs;
+  const blink::web_pref::WebPreferences kDefaultPrefs;
   const int kDefaultFontSize = kDefaultPrefs.default_font_size;
   const int kDefaultFixedFontSize = kDefaultPrefs.default_fixed_font_size;
 
@@ -43,7 +43,7 @@
   // The OS settings window still uses the default font sizes.
   Browser* browser = settings->FindBrowserForProfile(profile);
   auto* web_contents = browser->tab_strip_model()->GetActiveWebContents();
-  content::WebPreferences window_prefs =
+  blink::web_pref::WebPreferences window_prefs =
       web_contents->GetOrCreateWebPreferences();
   EXPECT_EQ(kDefaultFontSize, window_prefs.default_font_size);
   EXPECT_EQ(kDefaultFixedFontSize, window_prefs.default_fixed_font_size);
diff --git a/chrome/browser/chromeos/crosapi/browser_manager.cc b/chrome/browser/chromeos/crosapi/browser_manager.cc
index ddf5e0fdc..c21dc31 100644
--- a/chrome/browser/chromeos/crosapi/browser_manager.cc
+++ b/chrome/browser/chromeos/crosapi/browser_manager.cc
@@ -30,6 +30,7 @@
 #include "chrome/browser/chromeos/crosapi/ash_chrome_service_impl.h"
 #include "chrome/browser/chromeos/crosapi/browser_loader.h"
 #include "chrome/browser/chromeos/crosapi/browser_util.h"
+#include "chrome/browser/chromeos/crosapi/test_mojo_connection_manager.h"
 #include "chrome/browser/component_updater/cros_component_manager.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chromeos/constants/chromeos_features.h"
@@ -39,7 +40,6 @@
 #include "google_apis/google_api_keys.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/platform/platform_channel.h"
-#include "mojo/public/cpp/system/invitation.h"
 
 // TODO(crbug.com/1101667): Currently, this source has log spamming
 // by LOG(WARNING) for non critical errors to make it easy
@@ -146,6 +146,15 @@
   // devices restart Chrome during login to apply flags. We don't want to run
   // the flag-off cleanup logic until we know we have the final flag state.
   session_manager::SessionManager::Get()->AddObserver(this);
+
+  std::string socket_path =
+      base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+          chromeos::switches::kLacrosMojoSocketForTesting);
+  if (!socket_path.empty()) {
+    test_mojo_connection_manager_ =
+        std::make_unique<crosapi::TestMojoConnectionManager>(
+            base::FilePath(socket_path));
+  }
 }
 
 BrowserManager::~BrowserManager() {
@@ -282,19 +291,11 @@
   mojo::PlatformChannel channel;
   channel.PrepareToPassRemoteEndpoint(&options, &command_line);
 
-  // Queue messages to establish the mojo connection,
-  // so that the passed IPC is available already when lacros-chrome accepts
-  // the invitation.
-  // TODO(crbug.com/1115092): Pass the initialization parameter over
-  // mojo connection.
-  mojo::OutgoingInvitation invitation;
-  lacros_chrome_service_.Bind(
-      mojo::PendingRemote<crosapi::mojom::LacrosChromeService>(
-          invitation.AttachMessagePipe(0), /*version=*/0));
-  lacros_chrome_service_.set_disconnect_handler(base::BindOnce(
-      &BrowserManager::OnMojoDisconnected, weak_factory_.GetWeakPtr()));
-  lacros_chrome_service_->Init(crosapi::mojom::LacrosInitParams::New());
-  lacros_chrome_service_->RequestAshChromeServiceReceiver(
+  // TODO(crbug.com/1124490): Support multiple mojo connections from lacros.
+  lacros_chrome_service_ = browser_util::SendMojoInvitationToLacrosChrome(
+      channel.TakeLocalEndpoint(),
+      base::BindOnce(&BrowserManager::OnMojoDisconnected,
+                     weak_factory_.GetWeakPtr()),
       base::BindOnce(&BrowserManager::OnAshChromeServiceReceiverReceived,
                      weak_factory_.GetWeakPtr()));
 
@@ -310,12 +311,7 @@
   }
   state_ = State::STARTING;
   LOG(WARNING) << "Launched lacros-chrome with pid " << lacros_process_.Pid();
-
-  // Invite the lacros-chrome to the mojo universe.
   channel.RemoteProcessLaunchAttempted();
-  mojo::OutgoingInvitation::Send(std::move(invitation),
-                                 lacros_process_.Handle(),
-                                 channel.TakeLocalEndpoint());
 }
 
 void BrowserManager::OnAshChromeServiceReceiverReceived(
diff --git a/chrome/browser/chromeos/crosapi/browser_manager.h b/chrome/browser/chromeos/crosapi/browser_manager.h
index 72fb310..a5234ee 100644
--- a/chrome/browser/chromeos/crosapi/browser_manager.h
+++ b/chrome/browser/chromeos/crosapi/browser_manager.h
@@ -24,6 +24,7 @@
 
 class AshChromeServiceImpl;
 class BrowserLoader;
+class TestMojoConnectionManager;
 
 // Manages the lifetime of lacros-chrome, and its loading status. This class is
 // a part of ash-chrome.
@@ -150,6 +151,11 @@
   // Instantiated on receiving the PendingReceiver from lacros-chrome.
   std::unique_ptr<AshChromeServiceImpl> ash_chrome_service_;
 
+  // Helps set up and manage the mojo connections between lacros-chrome and
+  // ash-chrome in testing environment. Only applicable when
+  // '--lacros-mojo-socket-for-testing' is present in the command line.
+  std::unique_ptr<TestMojoConnectionManager> test_mojo_connection_manager_;
+
   base::WeakPtrFactory<BrowserManager> weak_factory_{this};
 };
 
diff --git a/chrome/browser/chromeos/crosapi/browser_util.cc b/chrome/browser/chromeos/crosapi/browser_util.cc
index e7f530e1..8a8c1d6 100644
--- a/chrome/browser/chromeos/crosapi/browser_util.cc
+++ b/chrome/browser/chromeos/crosapi/browser_util.cc
@@ -4,8 +4,12 @@
 
 #include "chrome/browser/chromeos/crosapi/browser_util.h"
 
+#include <utility>
+
+#include "base/callback.h"
 #include "base/files/file_path.h"
 #include "base/path_service.h"
+#include "base/process/process_handle.h"
 #include "base/strings/string_util.h"
 #include "base/system/sys_info.h"
 #include "chrome/common/channel_info.h"
@@ -15,6 +19,8 @@
 #include "components/user_manager/user_manager.h"
 #include "components/user_manager/user_type.h"
 #include "components/version_info/channel.h"
+#include "mojo/public/cpp/platform/platform_channel.h"
+#include "mojo/public/cpp/system/invitation.h"
 
 using user_manager::User;
 using version_info::Channel;
@@ -89,5 +95,28 @@
   }
 }
 
+mojo::Remote<crosapi::mojom::LacrosChromeService>
+SendMojoInvitationToLacrosChrome(
+    mojo::PlatformChannelEndpoint local_endpoint,
+    base::OnceClosure mojo_disconnected_callback,
+    base::OnceCallback<
+        void(mojo::PendingReceiver<crosapi::mojom::AshChromeService>)>
+        ash_chrome_service_callback) {
+  mojo::OutgoingInvitation invitation;
+  mojo::Remote<crosapi::mojom::LacrosChromeService> lacros_chrome_service;
+  lacros_chrome_service.Bind(
+      mojo::PendingRemote<crosapi::mojom::LacrosChromeService>(
+          invitation.AttachMessagePipe(0 /* token */), /*version=*/0));
+  lacros_chrome_service.set_disconnect_handler(
+      std::move(mojo_disconnected_callback));
+  lacros_chrome_service->Init(crosapi::mojom::LacrosInitParams::New());
+  lacros_chrome_service->RequestAshChromeServiceReceiver(
+      std::move(ash_chrome_service_callback));
+  mojo::OutgoingInvitation::Send(std::move(invitation),
+                                 base::kNullProcessHandle,
+                                 std::move(local_endpoint));
+  return lacros_chrome_service;
+}
+
 }  // namespace browser_util
 }  // namespace crosapi
diff --git a/chrome/browser/chromeos/crosapi/browser_util.h b/chrome/browser/chromeos/crosapi/browser_util.h
index 148c7c7..abde75cd 100644
--- a/chrome/browser/chromeos/crosapi/browser_util.h
+++ b/chrome/browser/chromeos/crosapi/browser_util.h
@@ -5,12 +5,21 @@
 #ifndef CHROME_BROWSER_CHROMEOS_CROSAPI_BROWSER_UTIL_H_
 #define CHROME_BROWSER_CHROMEOS_CROSAPI_BROWSER_UTIL_H_
 
+#include "base/callback_forward.h"
+#include "chromeos/crosapi/mojom/crosapi.mojom.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
+
 class PrefRegistrySimple;
 
 namespace base {
 class FilePath;
 }  // namespace base
 
+namespace mojo {
+class PlatformChannelEndpoint;
+}  // namespace mojo
+
 namespace version_info {
 enum class Channel;
 }  // namespace version_info
@@ -35,6 +44,19 @@
 // As above, but takes a channel. Exposed for testing.
 bool IsLacrosAllowed(version_info::Channel channel);
 
+// Invite the lacros-chrome to the mojo universe.
+// Queue messages to establish the mojo connection, so that the passed IPC is
+// available already when lacros-chrome accepts the invitation.
+// TODO(crbug.com/1115092): Pass the initialization parameter over mojo
+// connection.
+mojo::Remote<crosapi::mojom::LacrosChromeService>
+SendMojoInvitationToLacrosChrome(
+    mojo::PlatformChannelEndpoint local_endpoint,
+    base::OnceClosure mojo_disconnected_callback,
+    base::OnceCallback<
+        void(mojo::PendingReceiver<crosapi::mojom::AshChromeService>)>
+        ash_chrome_service_callback);
+
 }  // namespace browser_util
 }  // namespace crosapi
 
diff --git a/chrome/browser/chromeos/crosapi/test_mojo_connection_manager.cc b/chrome/browser/chromeos/crosapi/test_mojo_connection_manager.cc
new file mode 100644
index 0000000..3e00073
--- /dev/null
+++ b/chrome/browser/chromeos/crosapi/test_mojo_connection_manager.cc
@@ -0,0 +1,116 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/crosapi/test_mojo_connection_manager.h"
+
+#include <fcntl.h>
+
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/task/post_task.h"
+#include "base/task/task_traits.h"
+#include "base/task/thread_pool.h"
+#include "chrome/browser/chromeos/crosapi/ash_chrome_service_impl.h"
+#include "chrome/browser/chromeos/crosapi/browser_util.h"
+#include "chromeos/constants/chromeos_switches.h"
+#include "mojo/public/cpp/platform/named_platform_channel.h"
+#include "mojo/public/cpp/platform/platform_channel.h"
+#include "mojo/public/cpp/platform/socket_utils_posix.h"
+#include "mojo/public/cpp/system/invitation.h"
+
+namespace crosapi {
+
+namespace {
+
+// TODO(crbug.com/1124494): Refactor the code to share with ARC.
+base::ScopedFD CreateSocketForTesting(const base::FilePath& socket_path) {
+  auto endpoint = mojo::NamedPlatformChannel({socket_path.value()});
+  base::ScopedFD socket_fd =
+      endpoint.TakeServerEndpoint().TakePlatformHandle().TakeFD();
+  if (!socket_fd.is_valid()) {
+    PLOG(ERROR) << "Failed to create socket file: " << socket_path;
+    return socket_fd;
+  }
+
+  if (!base::SetPosixFilePermissions(socket_path, 0660)) {
+    PLOG(ERROR) << "Could not set permissions on socket file: " << socket_path;
+    return base::ScopedFD();
+  }
+
+  return socket_fd;
+}
+
+}  // namespace
+
+TestMojoConnectionManager::TestMojoConnectionManager(
+    const base::FilePath& socket_path) {
+  base::ThreadPool::PostTaskAndReplyWithResult(
+      FROM_HERE, {base::MayBlock()},
+      base::BindOnce(&CreateSocketForTesting, socket_path),
+      base::BindOnce(&TestMojoConnectionManager::OnTestingSocketCreated,
+                     weak_factory_.GetWeakPtr()));
+}
+
+TestMojoConnectionManager::~TestMojoConnectionManager() = default;
+
+void TestMojoConnectionManager::OnTestingSocketCreated(
+    base::ScopedFD socket_fd) {
+  if (!socket_fd.is_valid())
+    return;
+
+  testing_socket_ = std::move(socket_fd);
+  testing_socket_watcher_ = base::FileDescriptorWatcher::WatchReadable(
+      testing_socket_.get(),
+      base::BindRepeating(&TestMojoConnectionManager::OnTestingSocketAvailable,
+                          weak_factory_.GetWeakPtr()));
+}
+
+void TestMojoConnectionManager::OnTestingSocketAvailable() {
+  base::ScopedFD connection_fd;
+  if (!mojo::AcceptSocketConnection(testing_socket_.get(), &connection_fd,
+                                    /* check_peer_user = */ false) ||
+      !connection_fd.is_valid()) {
+    LOG(ERROR) << "Failed to Accept the socket";
+    return;
+  }
+
+  // TODO(crbug.com/1124490): Support multiple mojo connections from lacros.
+  mojo::PlatformChannel channel;
+  lacros_chrome_service_ = browser_util::SendMojoInvitationToLacrosChrome(
+      channel.TakeLocalEndpoint(),
+      base::BindOnce(&TestMojoConnectionManager::OnMojoDisconnected,
+                     weak_factory_.GetWeakPtr()),
+      base::BindOnce(
+          &TestMojoConnectionManager::OnAshChromeServiceReceiverReceived,
+          weak_factory_.GetWeakPtr()));
+
+  std::vector<base::ScopedFD> fds;
+  fds.emplace_back(channel.TakeRemoteEndpoint().TakePlatformHandle().TakeFD());
+
+  // Version of protocol Chrome is using.
+  uint8_t protocol_version = 0;
+  struct iovec iov[] = {{&protocol_version, sizeof(protocol_version)}};
+  ssize_t result = mojo::SendmsgWithHandles(connection_fd.get(), iov,
+                                            sizeof(iov) / sizeof(iov[0]), fds);
+  if (result == -1) {
+    PLOG(ERROR) << "Failed to send file descriptors to the socket";
+    return;
+  }
+}
+
+void TestMojoConnectionManager::OnAshChromeServiceReceiverReceived(
+    mojo::PendingReceiver<crosapi::mojom::AshChromeService> pending_receiver) {
+  ash_chrome_service_ =
+      std::make_unique<AshChromeServiceImpl>(std::move(pending_receiver));
+  LOG(INFO) << "Connection to lacros-chrome is established.";
+}
+
+void TestMojoConnectionManager::OnMojoDisconnected() {
+  lacros_chrome_service_.reset();
+  ash_chrome_service_ = nullptr;
+  LOG(WARNING) << "Mojo to lacros-chrome is disconnected.";
+}
+
+}  // namespace crosapi
diff --git a/chrome/browser/chromeos/crosapi/test_mojo_connection_manager.h b/chrome/browser/chromeos/crosapi/test_mojo_connection_manager.h
new file mode 100644
index 0000000..f52bf516
--- /dev/null
+++ b/chrome/browser/chromeos/crosapi/test_mojo_connection_manager.h
@@ -0,0 +1,84 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_CROSAPI_TEST_MOJO_CONNECTION_MANAGER_H_
+#define CHROME_BROWSER_CHROMEOS_CROSAPI_TEST_MOJO_CONNECTION_MANAGER_H_
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/files/file_descriptor_watcher_posix.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_file.h"
+#include "base/memory/weak_ptr.h"
+#include "chromeos/crosapi/mojom/crosapi.mojom.h"
+#include "mojo/public/cpp/bindings/remote.h"
+
+namespace crosapi {
+
+class AshChromeServiceImpl;
+
+// An extension of BrowserManager to help set up and manage the mojo connections
+// between the test executable and ash-chrome in testing environment.
+//
+// In testing environment, the workflow is as following:
+// - Ash-chrome creates a Unix domain socket.
+// - Test executable connects to the Unix domain socket.
+// - When ash-chrome accepts the connection, it creates a |PlatformChannel| and
+//   sends one end of it (as a FD) over the socket.
+// - Test executable reads the FD from the socket and passes it to lacros-chrome
+//   when launching a test.
+//
+// The workflow works for debugging as well, a wapper script can play the role
+// of the test executable above to obtain the FD, and passes it to lacros-chrome
+// when launching it inside gdb.
+class TestMojoConnectionManager {
+ public:
+  explicit TestMojoConnectionManager(const base::FilePath& socket_path);
+
+  TestMojoConnectionManager(const TestMojoConnectionManager&) = delete;
+  TestMojoConnectionManager& operator=(const TestMojoConnectionManager&) =
+      delete;
+
+  ~TestMojoConnectionManager();
+
+ private:
+  // Called when the testing socket is created.
+  void OnTestingSocketCreated(base::ScopedFD socket_fd);
+
+  // Called when a client, such as a test launcher, attempts to connect.
+  void OnTestingSocketAvailable();
+
+  // Called when PendingReceiver of AshChromeService is passed from
+  // lacros-chrome.
+  void OnAshChromeServiceReceiverReceived(
+      mojo::PendingReceiver<crosapi::mojom::AshChromeService> pending_receiver);
+
+  // Called when the Mojo connection to lacros-chrome is disconnected.
+  // It may be "just a Mojo error" or "test is finished".
+  void OnMojoDisconnected();
+
+  // Proxy to LacrosChromeService mojo service in lacros-chrome.
+  // Available during lacros-chrome is running.
+  mojo::Remote<crosapi::mojom::LacrosChromeService> lacros_chrome_service_;
+
+  // Implementation of AshChromeService Mojo APIs.
+  // Instantiated on receiving the PendingReceiver from lacros-chrome.
+  std::unique_ptr<AshChromeServiceImpl> ash_chrome_service_;
+
+  // A socket for a client, such as a test launcher, to connect to.
+  base::ScopedFD testing_socket_;
+
+  // A watcher that watches |testing_scoket_| and invokes
+  // |OnTestingSocketAvailable| when it becomes readable.
+  std::unique_ptr<base::FileDescriptorWatcher::Controller>
+      testing_socket_watcher_;
+
+  base::WeakPtrFactory<TestMojoConnectionManager> weak_factory_{this};
+};
+
+}  // namespace crosapi
+
+#endif  // CHROME_BROWSER_CHROMEOS_CROSAPI_TEST_MOJO_CONNECTION_MANAGER_H_
diff --git a/chrome/browser/chromeos/crosapi/test_mojo_connection_manager_unittest.cc b/chrome/browser/chromeos/crosapi/test_mojo_connection_manager_unittest.cc
new file mode 100644
index 0000000..79d76098
--- /dev/null
+++ b/chrome/browser/chromeos/crosapi/test_mojo_connection_manager_unittest.cc
@@ -0,0 +1,166 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/crosapi/test_mojo_connection_manager.h"
+
+#include <fcntl.h>
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/files/file_path_watcher.h"
+#include "base/files/scoped_file.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/process/launch.h"
+#include "base/run_loop.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/task/thread_pool.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/multiprocess_test.h"
+#include "base/test/task_environment.h"
+#include "base/test/test_timeouts.h"
+#include "chromeos/crosapi/mojom/crosapi.mojom.h"
+#include "mojo/public/cpp/platform/named_platform_channel.h"
+#include "mojo/public/cpp/platform/platform_channel.h"
+#include "mojo/public/cpp/platform/socket_utils_posix.h"
+#include "mojo/public/cpp/system/invitation.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/multiprocess_func_list.h"
+
+namespace crosapi {
+
+class TestLacrosChromeService : public crosapi::mojom::LacrosChromeService {
+ public:
+  TestLacrosChromeService(
+      mojo::PendingReceiver<mojom::LacrosChromeService> receiver,
+      base::RunLoop& run_loop)
+      : receiver_(this, std::move(receiver)), run_loop_(run_loop) {}
+
+  ~TestLacrosChromeService() override = default;
+
+  void Init(crosapi::mojom::LacrosInitParamsPtr params) override {
+    init_is_called_ = true;
+  }
+
+  void RequestAshChromeServiceReceiver(
+      RequestAshChromeServiceReceiverCallback callback) override {
+    EXPECT_TRUE(init_is_called_);
+    std::move(callback).Run(ash_chrome_service_.BindNewPipeAndPassReceiver());
+    request_ash_chrome_service_is_called_ = true;
+    run_loop_.Quit();
+  }
+
+  void NewWindow(NewWindowCallback callback) override {}
+
+  bool init_is_called() { return init_is_called_; }
+
+  bool request_ash_chrome_service_is_called() {
+    return request_ash_chrome_service_is_called_;
+  }
+
+ private:
+  mojo::Receiver<mojom::LacrosChromeService> receiver_;
+
+  bool init_is_called_ = false;
+  bool request_ash_chrome_service_is_called_ = false;
+
+  base::RunLoop& run_loop_;
+
+  mojo::Remote<crosapi::mojom::AshChromeService> ash_chrome_service_;
+};
+
+class TestMojoConnectionManagerTest : public testing::Test {
+ public:
+  TestMojoConnectionManagerTest() = default;
+  ~TestMojoConnectionManagerTest() override = default;
+};
+
+TEST_F(TestMojoConnectionManagerTest, ConnectWithLacrosChrome) {
+  // Use IO type to support the FileDescriptorWatcher API on POSIX.
+  base::test::TaskEnvironment task_environment{
+      base::test::TaskEnvironment::MainThreadType::IO};
+
+  // Ash-chrome queues an invitation, drop a socket and wait for connection.
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+  std::string socket_path =
+      temp_dir.GetPath().MaybeAsASCII() + "/lacros.socket";
+
+  // Sets up watcher to Wait for the socket to be created by
+  // |TestMojoConnectionManager| before attempting to connect. There is no
+  // garanteen that |OnTestingSocketCreated| has run after the run loop is done,
+  // so this test should NOT depend on the assumption.
+  base::FilePathWatcher watcher;
+  base::RunLoop run_loop1;
+  watcher.Watch(base::FilePath(socket_path), false,
+                base::BindRepeating(base::BindLambdaForTesting(
+                    [&run_loop1](const base::FilePath& path, bool error) {
+                      EXPECT_FALSE(error);
+                      run_loop1.Quit();
+                    })));
+  TestMojoConnectionManager test_mojo_connection_manager{
+      base::FilePath(socket_path)};
+  run_loop1.Run();
+
+  // Test connects with ash-chrome via the socket.
+  auto channel = mojo::NamedPlatformChannel::ConnectToServer(socket_path);
+  ASSERT_TRUE(channel.is_valid());
+  base::ScopedFD socket_fd = channel.TakePlatformHandle().TakeFD();
+
+  uint8_t buf[32];
+  std::vector<base::ScopedFD> descriptors;
+  ssize_t size;
+  base::RunLoop run_loop2;
+  base::ThreadPool::PostTaskAndReply(
+      FROM_HERE, {base::MayBlock()},
+      base::BindOnce(base::BindLambdaForTesting([&]() {
+        // Mark the channel as blocking.
+        int flags = fcntl(socket_fd.get(), F_GETFL);
+        PCHECK(flags != -1);
+        fcntl(socket_fd.get(), F_SETFL, flags & ~O_NONBLOCK);
+        size = mojo::SocketRecvmsg(socket_fd.get(), buf, sizeof(buf),
+                                   &descriptors, true /*block*/);
+      })),
+      run_loop2.QuitClosure());
+  run_loop2.Run();
+  EXPECT_EQ(1, size);
+  EXPECT_EQ(0u, buf[0]);
+  ASSERT_EQ(1u, descriptors.size());
+
+  // Test launches lacros-chrome as child process.
+  base::LaunchOptions options;
+  options.fds_to_remap.emplace_back(descriptors[0].get(), descriptors[0].get());
+  base::CommandLine lacros_cmd(base::GetMultiProcessTestChildBaseCommandLine());
+  lacros_cmd.AppendSwitchASCII(mojo::PlatformChannel::kHandleSwitch,
+                               base::NumberToString(descriptors[0].release()));
+  base::Process lacros_process =
+      base::SpawnMultiProcessTestChild("LacrosMain", lacros_cmd, options);
+
+  // lacros-chrome accepts the invitation to establish mojo connection with
+  // ash-chrome.
+  int rv = -1;
+  ASSERT_TRUE(base::WaitForMultiprocessTestChildExit(
+      lacros_process, TestTimeouts::action_timeout(), &rv));
+  lacros_process.Close();
+  EXPECT_EQ(0, rv);
+}
+
+// Another process that emulates the behavior of lacros-chrome.
+MULTIPROCESS_TEST_MAIN(LacrosMain) {
+  base::test::SingleThreadTaskEnvironment task_environment;
+  mojo::IncomingInvitation invitation = mojo::IncomingInvitation::Accept(
+      mojo::PlatformChannel::RecoverPassedEndpointFromCommandLine(
+          *base::CommandLine::ForCurrentProcess()));
+  base::RunLoop run_loop;
+  TestLacrosChromeService test_lacros_chrome_service(
+      mojo::PendingReceiver<crosapi::mojom::LacrosChromeService>(
+          invitation.ExtractMessagePipe(0)),
+      run_loop);
+  run_loop.Run();
+  DCHECK(test_lacros_chrome_service.init_is_called());
+  DCHECK(test_lacros_chrome_service.request_ash_chrome_service_is_called());
+  return 0;
+}
+
+}  // namespace crosapi
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc
index aab114d8..b00ae2f 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc
@@ -88,22 +88,6 @@
 constexpr base::TimeDelta kDriveVerySlowOperationThreshold =
     base::TimeDelta::FromMinutes(1);
 
-std::unique_ptr<std::string> GetShareUrlFromAlternateUrl(
-    const GURL& alternate_url) {
-  // Set |share_url| to a modified version of |alternate_url| that opens the
-  // sharing dialog for files and folders (add ?userstoinvite="" to the URL).
-  // TODO(sashab): Add an endpoint to the Drive API that generates this URL,
-  // instead of manually modifying it here.
-  GURL::Replacements replacements;
-  std::string new_query =
-      (alternate_url.has_query() ? alternate_url.query() + "&" : "") +
-      "userstoinvite=%22%22";
-  replacements.SetQueryStr(new_query);
-
-  return std::make_unique<std::string>(
-      alternate_url.ReplaceComponents(replacements).spec());
-}
-
 class SingleEntryPropertiesGetterForFileSystemProvider {
  public:
   typedef base::OnceCallback<void(std::unique_ptr<EntryProperties> properties,
@@ -241,184 +225,6 @@
       weak_ptr_factory_{this};
 };  // class SingleEntryPropertiesGetterForFileSystemProvider
 
-class SingleEntryPropertiesGetterForDriveFs {
- public:
-  using ResultCallback =
-      base::OnceCallback<void(std::unique_ptr<EntryProperties> properties,
-                              base::File::Error error)>;
-
-  // Creates an instance and starts the process.
-  static void Start(const storage::FileSystemURL& file_system_url,
-                    Profile* const profile,
-                    ResultCallback callback) {
-    DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-    SingleEntryPropertiesGetterForDriveFs* instance =
-        new SingleEntryPropertiesGetterForDriveFs(file_system_url, profile,
-                                                  std::move(callback));
-    instance->StartProcess();
-
-    // The instance will be destroyed by itself.
-  }
-
- private:
-  SingleEntryPropertiesGetterForDriveFs(
-      const storage::FileSystemURL& file_system_url,
-      Profile* const profile,
-      ResultCallback callback)
-      : callback_(std::move(callback)),
-        file_system_url_(file_system_url),
-        running_profile_(profile),
-        properties_(std::make_unique<EntryProperties>()) {
-    DCHECK(callback_);
-    DCHECK(profile);
-  }
-
-  void StartProcess() {
-    DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-    drive::DriveIntegrationService* integration_service =
-        drive::DriveIntegrationServiceFactory::FindForProfile(running_profile_);
-    if (!integration_service || !integration_service->IsMounted()) {
-      CompleteGetEntryProperties(drive::FILE_ERROR_SERVICE_UNAVAILABLE);
-      return;
-    }
-    base::FilePath path;
-    if (!integration_service->GetRelativeDrivePath(file_system_url_.path(),
-                                                   &path)) {
-      CompleteGetEntryProperties(drive::FILE_ERROR_INVALID_OPERATION);
-      return;
-    }
-
-    auto* drivefs_interface = integration_service->GetDriveFsInterface();
-    if (!drivefs_interface) {
-      CompleteGetEntryProperties(drive::FILE_ERROR_SERVICE_UNAVAILABLE);
-      return;
-    }
-
-    drivefs_interface->GetMetadata(
-        path, mojo::WrapCallbackWithDefaultInvokeIfNotRun(
-                  base::BindOnce(
-                      &SingleEntryPropertiesGetterForDriveFs::OnGetFileInfo,
-                      weak_ptr_factory_.GetWeakPtr()),
-                  drive::FILE_ERROR_SERVICE_UNAVAILABLE, nullptr));
-  }
-
-  void OnGetFileInfo(drive::FileError error,
-                     drivefs::mojom::FileMetadataPtr metadata) {
-    DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-    if (!metadata) {
-      CompleteGetEntryProperties(error);
-      return;
-    }
-
-    properties_->size = std::make_unique<double>(metadata->size);
-    properties_->present = std::make_unique<bool>(metadata->available_offline);
-    properties_->dirty = std::make_unique<bool>(metadata->dirty);
-    properties_->hosted =
-        std::make_unique<bool>(drivefs::IsHosted(metadata->type));
-    properties_->available_offline = std::make_unique<bool>(
-        metadata->available_offline || *properties_->hosted);
-    properties_->available_when_metered = std::make_unique<bool>(
-        metadata->available_offline || *properties_->hosted);
-    properties_->pinned = std::make_unique<bool>(metadata->pinned);
-    properties_->shared = std::make_unique<bool>(metadata->shared);
-    properties_->starred = std::make_unique<bool>(metadata->starred);
-
-    if (metadata->modification_time != base::Time()) {
-      properties_->modification_time =
-          std::make_unique<double>(metadata->modification_time.ToJsTime());
-    }
-    if (metadata->last_viewed_by_me_time != base::Time()) {
-      properties_->modification_by_me_time =
-          std::make_unique<double>(metadata->last_viewed_by_me_time.ToJsTime());
-    }
-    if (!metadata->content_mime_type.empty()) {
-      properties_->content_mime_type =
-          std::make_unique<std::string>(metadata->content_mime_type);
-    }
-    if (!metadata->custom_icon_url.empty()) {
-      properties_->custom_icon_url =
-          std::make_unique<std::string>(std::move(metadata->custom_icon_url));
-    }
-    if (!metadata->alternate_url.empty()) {
-      properties_->alternate_url =
-          std::make_unique<std::string>(std::move(metadata->alternate_url));
-      properties_->share_url =
-          GetShareUrlFromAlternateUrl(GURL(*properties_->alternate_url));
-    }
-    if (metadata->image_metadata) {
-      if (metadata->image_metadata->height) {
-        properties_->image_height =
-            std::make_unique<int32_t>(metadata->image_metadata->height);
-      }
-      if (metadata->image_metadata->width) {
-        properties_->image_width =
-            std::make_unique<int32_t>(metadata->image_metadata->width);
-      }
-      if (metadata->image_metadata->rotation) {
-        properties_->image_rotation =
-            std::make_unique<int32_t>(metadata->image_metadata->rotation);
-      }
-    }
-
-    properties_->can_delete =
-        std::make_unique<bool>(metadata->capabilities->can_delete);
-    properties_->can_rename =
-        std::make_unique<bool>(metadata->capabilities->can_rename);
-    properties_->can_add_children =
-        std::make_unique<bool>(metadata->capabilities->can_add_children);
-
-    // Only set the |can_copy| capability for hosted documents; for other files,
-    // we must have read access, so |can_copy| is implicitly true.
-    properties_->can_copy = std::make_unique<bool>(
-        !*properties_->hosted || metadata->capabilities->can_copy);
-    properties_->can_share =
-        std::make_unique<bool>(metadata->capabilities->can_share);
-
-    if (drivefs::IsAFile(metadata->type)) {
-      properties_->thumbnail_url = std::make_unique<std::string>(
-          base::StrCat({"drivefs:", file_system_url_.ToGURL().spec()}));
-      properties_->cropped_thumbnail_url =
-          std::make_unique<std::string>(*properties_->thumbnail_url);
-    }
-
-    if (metadata->folder_feature) {
-      properties_->is_machine_root =
-          std::make_unique<bool>(metadata->folder_feature->is_machine_root);
-      properties_->is_external_media =
-          std::make_unique<bool>(metadata->folder_feature->is_external_media);
-      properties_->is_arbitrary_sync_folder = std::make_unique<bool>(
-          metadata->folder_feature->is_arbitrary_sync_folder);
-    }
-
-    CompleteGetEntryProperties(drive::FILE_ERROR_OK);
-  }
-
-  void CompleteGetEntryProperties(drive::FileError error) {
-    DCHECK_CURRENTLY_ON(BrowserThread::UI);
-    DCHECK(callback_);
-
-    std::move(callback_).Run(std::move(properties_),
-                             drive::FileErrorToBaseFileError(error));
-    content::GetUIThreadTaskRunner({})->DeleteSoon(FROM_HERE, this);
-  }
-
-  // Given parameters.
-  ResultCallback callback_;
-  const storage::FileSystemURL file_system_url_;
-  Profile* const running_profile_;
-
-  // Values used in the process.
-  std::unique_ptr<EntryProperties> properties_;
-
-  base::WeakPtrFactory<SingleEntryPropertiesGetterForDriveFs> weak_ptr_factory_{
-      this};
-
-  DISALLOW_COPY_AND_ASSIGN(SingleEntryPropertiesGetterForDriveFs);
-};
-
 class SingleEntryPropertiesGetterForDocumentsProvider {
  public:
   typedef base::OnceCallback<void(std::unique_ptr<EntryProperties> properties,
@@ -645,7 +451,7 @@
                 this, i, file_system_url));
         break;
       case storage::kFileSystemTypeDriveFs:
-        SingleEntryPropertiesGetterForDriveFs::Start(
+        file_manager::util::SingleEntryPropertiesGetterForDriveFs::Start(
             file_system_url, chrome_details.GetProfile(),
             base::BindOnce(
                 &FileManagerPrivateInternalGetEntryPropertiesFunction::
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_sharesheet.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_sharesheet.cc
index 03096797..92c6bb4 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_sharesheet.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_sharesheet.cc
@@ -5,6 +5,8 @@
 #include "chrome/browser/chromeos/extensions/file_manager/private_api_sharesheet.h"
 
 #include "base/bind.h"
+#include "chrome/browser/chromeos/drive/file_system_util.h"
+#include "chrome/browser/chromeos/extensions/file_manager/private_api_util.h"
 #include "chrome/browser/chromeos/file_manager/fileapi_util.h"
 #include "chrome/browser/chromeos/fileapi/file_system_backend.h"
 #include "chrome/browser/profiles/profile.h"
@@ -12,6 +14,7 @@
 #include "chrome/browser/sharesheet/sharesheet_service_factory.h"
 #include "chrome/common/extensions/api/file_manager_private.h"
 #include "chrome/common/extensions/api/file_manager_private_internal.h"
+#include "components/drive/drive_api_util.h"
 #include "components/services/app_service/public/cpp/intent_util.h"
 #include "content/public/browser/web_contents.h"
 #include "extensions/browser/api/file_handlers/directory_util.h"
@@ -44,23 +47,25 @@
       file_manager::util::GetFileSystemContextForRenderFrameHost(
           chrome_details_.GetProfile(), render_frame_host());
 
-  std::vector<storage::FileSystemURL> file_system_urls;
   // Collect all the URLs, convert them to GURLs, and crack all the urls into
   // file paths.
   for (size_t i = 0; i < params->urls.size(); ++i) {
     const GURL url(params->urls[i]);
     storage::FileSystemURL file_system_url(file_system_context->CrackURL(url));
+    if (drive::util::HasHostedDocumentExtension(file_system_url.path())) {
+      contains_hosted_document_ = true;
+    }
     if (!chromeos::FileSystemBackend::CanHandleURL(file_system_url))
       continue;
     urls_.push_back(url);
-    file_system_urls.push_back(file_system_url);
+    file_system_urls_.push_back(file_system_url);
   }
 
   mime_type_collector_ =
       std::make_unique<app_file_handler_util::MimeTypeCollector>(
           chrome_details_.GetProfile());
   mime_type_collector_->CollectForURLs(
-      file_system_urls,
+      file_system_urls_,
       base::BindOnce(&FileManagerPrivateInternalSharesheetHasTargetsFunction::
                          OnMimeTypesCollected,
                      this));
@@ -79,11 +84,59 @@
     LOG(ERROR) << "Couldn't get Sharesheet Service for profile";
     Respond(ArgumentList(extensions::api::file_manager_private_internal::
                              SharesheetHasTargets::Results::Create(result)));
+    return;
   }
 
-  result = sharesheet_service->HasShareTargets(
-      apps_util::CreateShareIntentFromFiles(urls_, *mime_types));
+  if (file_system_urls_.size() == 1 &&
+      file_system_urls_[0].type() == storage::kFileSystemTypeDriveFs) {
+    auto connection_status = drive::util::GetDriveConnectionStatus(
+        Profile::FromBrowserContext(browser_context()));
 
+    if (connection_status == drive::util::DRIVE_CONNECTED_METERED ||
+        connection_status == drive::util::DRIVE_CONNECTED) {
+      file_manager::util::SingleEntryPropertiesGetterForDriveFs::Start(
+          file_system_urls_[0], chrome_details_.GetProfile(),
+          base::BindOnce(
+              &FileManagerPrivateInternalSharesheetHasTargetsFunction::
+                  OnDrivePropertyCollected,
+              this, std::move(mime_types)));
+      return;
+    }
+  }
+  result = sharesheet_service->HasShareTargets(
+      apps_util::CreateShareIntentFromFiles(urls_, *mime_types),
+      contains_hosted_document_);
+  Respond(ArgumentList(extensions::api::file_manager_private_internal::
+                           SharesheetHasTargets::Results::Create(result)));
+}
+
+void FileManagerPrivateInternalSharesheetHasTargetsFunction::
+    OnDrivePropertyCollected(
+        std::unique_ptr<std::vector<std::string>> mime_types,
+        std::unique_ptr<api::file_manager_private::EntryProperties> properties,
+        base::File::Error error) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  bool result = false;
+
+  if (error != base::File::FILE_OK) {
+    LOG(ERROR) << "Error reading file properties in Drive: " << error;
+    Respond(ArgumentList(extensions::api::file_manager_private_internal::
+                             SharesheetHasTargets::Results::Create(result)));
+    return;
+  }
+
+  sharesheet::SharesheetService* sharesheet_service =
+      sharesheet::SharesheetServiceFactory::GetForProfile(
+          chrome_details_.GetProfile());
+  GURL share_url =
+      (properties->can_share && *properties->can_share && properties->share_url)
+          ? GURL(*properties->share_url)
+          : GURL();
+  result = sharesheet_service->HasShareTargets(
+      apps_util::CreateShareIntentFromDriveFile(urls_[0], (*mime_types)[0],
+                                                share_url),
+      contains_hosted_document_);
   Respond(ArgumentList(extensions::api::file_manager_private_internal::
                            SharesheetHasTargets::Results::Create(result)));
 }
@@ -109,23 +162,24 @@
       file_manager::util::GetFileSystemContextForRenderFrameHost(
           chrome_details_.GetProfile(), render_frame_host());
 
-  std::vector<storage::FileSystemURL> file_system_urls;
   // Collect all the URLs, convert them to GURLs, and crack all the urls into
   // file paths.
   for (size_t i = 0; i < params->urls.size(); ++i) {
     const GURL url(params->urls[i]);
     storage::FileSystemURL file_system_url(file_system_context->CrackURL(url));
+    if (drive::util::HasHostedDocumentExtension(file_system_url.path()))
+      contains_hosted_document_ = true;
     if (!chromeos::FileSystemBackend::CanHandleURL(file_system_url))
       continue;
     urls_.push_back(url);
-    file_system_urls.push_back(file_system_url);
+    file_system_urls_.push_back(file_system_url);
   }
 
   mime_type_collector_ =
       std::make_unique<app_file_handler_util::MimeTypeCollector>(
           chrome_details_.GetProfile());
   mime_type_collector_->CollectForURLs(
-      file_system_urls,
+      file_system_urls_,
       base::BindOnce(&FileManagerPrivateInternalInvokeSharesheetFunction::
                          OnMimeTypesCollected,
                      this));
@@ -143,10 +197,58 @@
     Respond(Error("Cannot find sharesheet service"));
     return;
   }
+
+  if (file_system_urls_.size() == 1 &&
+      file_system_urls_[0].type() == storage::kFileSystemTypeDriveFs) {
+    auto connection_status = drive::util::GetDriveConnectionStatus(
+        Profile::FromBrowserContext(browser_context()));
+
+    if (connection_status == drive::util::DRIVE_CONNECTED_METERED ||
+        connection_status == drive::util::DRIVE_CONNECTED) {
+      file_manager::util::SingleEntryPropertiesGetterForDriveFs::Start(
+          file_system_urls_[0], chrome_details_.GetProfile(),
+          base::BindOnce(&FileManagerPrivateInternalInvokeSharesheetFunction::
+                             OnDrivePropertyCollected,
+                         this, std::move(mime_types)));
+      return;
+    }
+  }
+
   sharesheet_service->ShowBubble(
       GetSenderWebContents(),
-      apps_util::CreateShareIntentFromFiles(urls_, *mime_types));
+      apps_util::CreateShareIntentFromFiles(urls_, *mime_types),
+      contains_hosted_document_);
+  Respond(NoArguments());
+}
 
+void FileManagerPrivateInternalInvokeSharesheetFunction::
+    OnDrivePropertyCollected(
+        std::unique_ptr<std::vector<std::string>> mime_types,
+        std::unique_ptr<api::file_manager_private::EntryProperties> properties,
+        base::File::Error error) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  if (error != base::File::FILE_OK) {
+    Respond(Error("Drive File Error"));
+    return;
+  }
+
+  auto* profile = chrome_details_.GetProfile();
+  sharesheet::SharesheetService* sharesheet_service =
+      sharesheet::SharesheetServiceFactory::GetForProfile(profile);
+  if (!sharesheet_service) {
+    Respond(Error("Cannot find sharesheet service"));
+    return;
+  }
+
+  GURL share_url =
+      (properties->can_share && *properties->can_share && properties->share_url)
+          ? GURL(*properties->share_url)
+          : GURL();
+  sharesheet_service->ShowBubble(GetSenderWebContents(),
+                                 apps_util::CreateShareIntentFromDriveFile(
+                                     urls_[0], (*mime_types)[0], share_url),
+                                 contains_hosted_document_);
   Respond(NoArguments());
 }
 
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_sharesheet.h b/chrome/browser/chromeos/extensions/file_manager/private_api_sharesheet.h
index 4611706c..7483eb52 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_sharesheet.h
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_sharesheet.h
@@ -14,9 +14,20 @@
 
 #include "chrome/browser/chromeos/extensions/file_manager/private_api_base.h"
 #include "chrome/browser/extensions/chrome_extension_function_details.h"
+#include "storage/browser/file_system/file_system_url.h"
+
+namespace base {
+class File;
+}
 
 namespace extensions {
 
+namespace api {
+namespace file_manager_private {
+struct EntryProperties;
+}
+}  // namespace api
+
 namespace app_file_handler_util {
 class MimeTypeCollector;
 }  // namespace app_file_handler_util
@@ -41,10 +52,17 @@
   void OnMimeTypesCollected(
       std::unique_ptr<std::vector<std::string>> mime_types);
 
+  void OnDrivePropertyCollected(
+      std::unique_ptr<std::vector<std::string>> mime_types,
+      std::unique_ptr<api::file_manager_private::EntryProperties> properties,
+      base::File::Error error);
+
   std::unique_ptr<app_file_handler_util::MimeTypeCollector>
       mime_type_collector_;
   std::vector<GURL> urls_;
   const ChromeExtensionFunctionDetails chrome_details_;
+  std::vector<storage::FileSystemURL> file_system_urls_;
+  bool contains_hosted_document_ = false;
 };
 
 // Implements the chrome.fileManagerPrivateInternal.invokeSharesheet method.
@@ -66,10 +84,17 @@
   void OnMimeTypesCollected(
       std::unique_ptr<std::vector<std::string>> mime_types);
 
+  void OnDrivePropertyCollected(
+      std::unique_ptr<std::vector<std::string>> mime_types,
+      std::unique_ptr<api::file_manager_private::EntryProperties> properties,
+      base::File::Error error);
+
   std::unique_ptr<app_file_handler_util::MimeTypeCollector>
       mime_type_collector_;
   std::vector<GURL> urls_;
   const ChromeExtensionFunctionDetails chrome_details_;
+  std::vector<storage::FileSystemURL> file_system_urls_;
+  bool contains_hosted_document_ = false;
 };
 
 }  // namespace extensions
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_util.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_util.cc
index 89aad71a..901608c 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_util.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_util.cc
@@ -30,7 +30,9 @@
 #include "chromeos/components/drivefs/drivefs_util.h"
 #include "components/drive/drive_api_util.h"
 #include "components/drive/file_errors.h"
+#include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/child_process_security_policy.h"
+#include "mojo/public/cpp/bindings/callback_helpers.h"
 #include "storage/browser/file_system/file_system_context.h"
 #include "storage/browser/file_system/file_system_url.h"
 #include "ui/shell_dialogs/selected_file_info.h"
@@ -180,8 +182,186 @@
   GetSelectedFileInfoInternal(profile, std::move(params));
 }
 
+std::unique_ptr<std::string> GetShareUrlFromAlternateUrl(
+    const GURL& alternate_url) {
+  // Set |share_url| to a modified version of |alternate_url| that opens the
+  // sharing dialog for files and folders (add ?userstoinvite="" to the URL).
+  GURL::Replacements replacements;
+  std::string new_query =
+      (alternate_url.has_query() ? alternate_url.query() + "&" : "") +
+      "userstoinvite=%22%22";
+  replacements.SetQueryStr(new_query);
+
+  return std::make_unique<std::string>(
+      alternate_url.ReplaceComponents(replacements).spec());
+}
+
 }  // namespace
 
+// Creates an instance and starts the process.
+void SingleEntryPropertiesGetterForDriveFs::Start(
+    const storage::FileSystemURL& file_system_url,
+    Profile* const profile,
+    ResultCallback callback) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  SingleEntryPropertiesGetterForDriveFs* instance =
+      new SingleEntryPropertiesGetterForDriveFs(file_system_url, profile,
+                                                std::move(callback));
+  instance->StartProcess();
+
+  // The instance will be destroyed by itself.
+}
+
+SingleEntryPropertiesGetterForDriveFs::SingleEntryPropertiesGetterForDriveFs(
+    const storage::FileSystemURL& file_system_url,
+    Profile* const profile,
+    ResultCallback callback)
+    : callback_(std::move(callback)),
+      file_system_url_(file_system_url),
+      running_profile_(profile),
+      properties_(std::make_unique<
+                  extensions::api::file_manager_private::EntryProperties>()) {
+  DCHECK(callback_);
+  DCHECK(profile);
+}
+
+SingleEntryPropertiesGetterForDriveFs::
+    ~SingleEntryPropertiesGetterForDriveFs() = default;
+
+void SingleEntryPropertiesGetterForDriveFs::StartProcess() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  drive::DriveIntegrationService* integration_service =
+      drive::DriveIntegrationServiceFactory::FindForProfile(running_profile_);
+  if (!integration_service || !integration_service->IsMounted()) {
+    CompleteGetEntryProperties(drive::FILE_ERROR_SERVICE_UNAVAILABLE);
+    return;
+  }
+  base::FilePath path;
+  if (!integration_service->GetRelativeDrivePath(file_system_url_.path(),
+                                                 &path)) {
+    CompleteGetEntryProperties(drive::FILE_ERROR_INVALID_OPERATION);
+    return;
+  }
+
+  auto* drivefs_interface = integration_service->GetDriveFsInterface();
+  if (!drivefs_interface) {
+    CompleteGetEntryProperties(drive::FILE_ERROR_SERVICE_UNAVAILABLE);
+    return;
+  }
+
+  drivefs_interface->GetMetadata(
+      path,
+      mojo::WrapCallbackWithDefaultInvokeIfNotRun(
+          base::BindOnce(&SingleEntryPropertiesGetterForDriveFs::OnGetFileInfo,
+                         weak_ptr_factory_.GetWeakPtr()),
+          drive::FILE_ERROR_SERVICE_UNAVAILABLE, nullptr));
+}
+
+void SingleEntryPropertiesGetterForDriveFs::OnGetFileInfo(
+    drive::FileError error,
+    drivefs::mojom::FileMetadataPtr metadata) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  if (!metadata) {
+    CompleteGetEntryProperties(error);
+    return;
+  }
+
+  properties_->size = std::make_unique<double>(metadata->size);
+  properties_->present = std::make_unique<bool>(metadata->available_offline);
+  properties_->dirty = std::make_unique<bool>(metadata->dirty);
+  properties_->hosted =
+      std::make_unique<bool>(drivefs::IsHosted(metadata->type));
+  properties_->available_offline = std::make_unique<bool>(
+      metadata->available_offline || *properties_->hosted);
+  properties_->available_when_metered = std::make_unique<bool>(
+      metadata->available_offline || *properties_->hosted);
+  properties_->pinned = std::make_unique<bool>(metadata->pinned);
+  properties_->shared = std::make_unique<bool>(metadata->shared);
+  properties_->starred = std::make_unique<bool>(metadata->starred);
+
+  if (metadata->modification_time != base::Time()) {
+    properties_->modification_time =
+        std::make_unique<double>(metadata->modification_time.ToJsTime());
+  }
+  if (metadata->last_viewed_by_me_time != base::Time()) {
+    properties_->modification_by_me_time =
+        std::make_unique<double>(metadata->last_viewed_by_me_time.ToJsTime());
+  }
+  if (!metadata->content_mime_type.empty()) {
+    properties_->content_mime_type =
+        std::make_unique<std::string>(metadata->content_mime_type);
+  }
+  if (!metadata->custom_icon_url.empty()) {
+    properties_->custom_icon_url =
+        std::make_unique<std::string>(std::move(metadata->custom_icon_url));
+  }
+  if (!metadata->alternate_url.empty()) {
+    properties_->alternate_url =
+        std::make_unique<std::string>(std::move(metadata->alternate_url));
+    properties_->share_url =
+        GetShareUrlFromAlternateUrl(GURL(*properties_->alternate_url));
+  }
+  if (metadata->image_metadata) {
+    if (metadata->image_metadata->height) {
+      properties_->image_height =
+          std::make_unique<int32_t>(metadata->image_metadata->height);
+    }
+    if (metadata->image_metadata->width) {
+      properties_->image_width =
+          std::make_unique<int32_t>(metadata->image_metadata->width);
+    }
+    if (metadata->image_metadata->rotation) {
+      properties_->image_rotation =
+          std::make_unique<int32_t>(metadata->image_metadata->rotation);
+    }
+  }
+
+  properties_->can_delete =
+      std::make_unique<bool>(metadata->capabilities->can_delete);
+  properties_->can_rename =
+      std::make_unique<bool>(metadata->capabilities->can_rename);
+  properties_->can_add_children =
+      std::make_unique<bool>(metadata->capabilities->can_add_children);
+
+  // Only set the |can_copy| capability for hosted documents; for other files,
+  // we must have read access, so |can_copy| is implicitly true.
+  properties_->can_copy = std::make_unique<bool>(
+      !*properties_->hosted || metadata->capabilities->can_copy);
+  properties_->can_share =
+      std::make_unique<bool>(metadata->capabilities->can_share);
+
+  if (drivefs::IsAFile(metadata->type)) {
+    properties_->thumbnail_url = std::make_unique<std::string>(
+        base::StrCat({"drivefs:", file_system_url_.ToGURL().spec()}));
+    properties_->cropped_thumbnail_url =
+        std::make_unique<std::string>(*properties_->thumbnail_url);
+  }
+
+  if (metadata->folder_feature) {
+    properties_->is_machine_root =
+        std::make_unique<bool>(metadata->folder_feature->is_machine_root);
+    properties_->is_external_media =
+        std::make_unique<bool>(metadata->folder_feature->is_external_media);
+    properties_->is_arbitrary_sync_folder = std::make_unique<bool>(
+        metadata->folder_feature->is_arbitrary_sync_folder);
+  }
+
+  CompleteGetEntryProperties(drive::FILE_ERROR_OK);
+}
+
+void SingleEntryPropertiesGetterForDriveFs::CompleteGetEntryProperties(
+    drive::FileError error) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DCHECK(callback_);
+
+  std::move(callback_).Run(std::move(properties_),
+                           drive::FileErrorToBaseFileError(error));
+  content::GetUIThreadTaskRunner({})->DeleteSoon(FROM_HERE, this);
+}
+
 void FillIconSet(file_manager_private::IconSet* output,
                  const chromeos::file_system_provider::IconSet& input) {
   DCHECK(output);
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_util.h b/chrome/browser/chromeos/extensions/file_manager/private_api_util.h
index 202092b0..2dcfa8c0 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_util.h
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_util.h
@@ -7,15 +7,23 @@
 #ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_UTIL_H_
 #define CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_UTIL_H_
 
+#include <memory>
 #include <vector>
 
+#include "base/callback.h"
 #include "base/callback_forward.h"
+#include "base/memory/weak_ptr.h"
+#include "base/strings/strcat.h"
 #include "chrome/browser/chromeos/file_system_provider/icon_set.h"
+#include "chromeos/components/drivefs/mojom/drivefs.mojom-forward.h"
+#include "components/drive/file_errors.h"
+#include "storage/browser/file_system/file_system_url.h"
 
 class GURL;
 class Profile;
 
 namespace base {
+class File;
 class FilePath;
 }
 
@@ -30,6 +38,7 @@
 namespace extensions {
 namespace api {
 namespace file_manager_private {
+struct EntryProperties;
 struct IconSet;
 struct VolumeMetadata;
 }
@@ -46,6 +55,47 @@
 
 namespace util {
 
+class SingleEntryPropertiesGetterForDriveFs {
+ public:
+  using ResultCallback = base::OnceCallback<void(
+      std::unique_ptr<extensions::api::file_manager_private::EntryProperties>
+          properties,
+      base::File::Error error)>;
+
+  // Creates an instance and starts the process.
+  static void Start(const storage::FileSystemURL& file_system_url,
+                    Profile* const profile,
+                    ResultCallback callback);
+  ~SingleEntryPropertiesGetterForDriveFs();
+
+  SingleEntryPropertiesGetterForDriveFs(
+      const SingleEntryPropertiesGetterForDriveFs&) = delete;
+  SingleEntryPropertiesGetterForDriveFs& operator=(
+      const SingleEntryPropertiesGetterForDriveFs&) = delete;
+
+ private:
+  SingleEntryPropertiesGetterForDriveFs(
+      const storage::FileSystemURL& file_system_url,
+      Profile* const profile,
+      ResultCallback callback);
+  void StartProcess();
+  void OnGetFileInfo(drive::FileError error,
+                     drivefs::mojom::FileMetadataPtr metadata);
+  void CompleteGetEntryProperties(drive::FileError error);
+
+  // Given parameters.
+  ResultCallback callback_;
+  const storage::FileSystemURL file_system_url_;
+  Profile* const running_profile_;
+
+  // Values used in the process.
+  std::unique_ptr<extensions::api::file_manager_private::EntryProperties>
+      properties_;
+
+  base::WeakPtrFactory<SingleEntryPropertiesGetterForDriveFs> weak_ptr_factory_{
+      this};
+};
+
 // Fills out IDL IconSet struct with the provided icon set.
 void FillIconSet(extensions::api::file_manager_private::IconSet* output,
                  const chromeos::file_system_provider::IconSet& input);
diff --git a/chrome/browser/chromeos/file_manager/file_manager_uitest.cc b/chrome/browser/chromeos/file_manager/file_manager_uitest.cc
index f7a1d549..fb5483a 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_uitest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_uitest.cc
@@ -9,10 +9,10 @@
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/common/content_switches.h"
-#include "content/public/common/web_preferences.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "net/base/filename_util.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 
 namespace file_manager {
 
diff --git a/chrome/browser/chromeos/web_applications/terminal_source.cc b/chrome/browser/chromeos/web_applications/terminal_source.cc
index 4f0a2bf..abe68f9 100644
--- a/chrome/browser/chromeos/web_applications/terminal_source.cc
+++ b/chrome/browser/chromeos/web_applications/terminal_source.cc
@@ -49,16 +49,6 @@
   if (!result) {
     static const base::NoDestructor<base::flat_map<std::string, std::string>>
         kTestFiles({
-            {"html/pwa.html",
-             "<html><head><link rel='manifest' "
-             "href='/manifest.json'></head></html>"},
-            {"manifest.json", R"({
-               "name": "Test Terminal",
-               "icons": [{ "src": "/icon.svg", "sizes": "any" }],
-               "start_url": "/html/terminal.html"})"},
-            {"icon.svg",
-             "<svg xmlns='http://www.w3.org/2000/svg'><rect "
-             "fill='red'/></svg>"},
             {"html/terminal.html", "<script src='/js/terminal.js'></script>"},
             {"js/terminal.js",
              "chrome.terminalPrivate.openVmshellProcess([], () => {})"},
diff --git a/chrome/browser/chromeos/web_applications/terminal_system_web_app_info.cc b/chrome/browser/chromeos/web_applications/terminal_system_web_app_info.cc
new file mode 100644
index 0000000..ace01a1
--- /dev/null
+++ b/chrome/browser/chromeos/web_applications/terminal_system_web_app_info.cc
@@ -0,0 +1,34 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/web_applications/terminal_system_web_app_info.h"
+
+#include <memory>
+
+#include "base/feature_list.h"
+#include "chrome/browser/chromeos/web_applications/system_web_app_install_utils.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/common/web_application_info.h"
+#include "chrome/common/webui_url_constants.h"
+#include "chrome/grit/chrome_unscaled_resources.h"
+#include "chrome/grit/generated_resources.h"
+#include "third_party/blink/public/mojom/manifest/display_mode.mojom.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "url/gurl.h"
+
+std::unique_ptr<WebApplicationInfo> CreateWebAppInfoForTerminalSystemWebApp() {
+  std::unique_ptr<WebApplicationInfo> info =
+      std::make_unique<WebApplicationInfo>();
+  // URL used for crostini::kCrostiniTerminalSystemAppId.
+  info->app_url = GURL("chrome-untrusted://terminal/html/terminal.html");
+  if (base::FeatureList::IsEnabled(features::kDesktopPWAsWithoutExtensions))
+    info->scope = GURL(chrome::kChromeUIUntrustedTerminalURL);
+  info->title = l10n_util::GetStringUTF16(IDS_CROSTINI_TERMINAL_APP_NAME);
+  web_app::CreateIconInfoForSystemWebApp(
+      info->app_url, {{"app_icon_192.png", 192, IDR_LOGO_CROSTINI_TERMINAL}},
+      *info);
+  info->background_color = 0xFF202124;
+  info->display_mode = blink::mojom::DisplayMode::kStandalone;
+  return info;
+}
diff --git a/chrome/browser/chromeos/web_applications/terminal_system_web_app_info.h b/chrome/browser/chromeos/web_applications/terminal_system_web_app_info.h
new file mode 100644
index 0000000..788b118
--- /dev/null
+++ b/chrome/browser/chromeos/web_applications/terminal_system_web_app_info.h
@@ -0,0 +1,15 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_WEB_APPLICATIONS_TERMINAL_SYSTEM_WEB_APP_INFO_H_
+#define CHROME_BROWSER_CHROMEOS_WEB_APPLICATIONS_TERMINAL_SYSTEM_WEB_APP_INFO_H_
+
+#include <memory>
+
+struct WebApplicationInfo;
+
+// Returns a WebApplicationInfo used to install the app.
+std::unique_ptr<WebApplicationInfo> CreateWebAppInfoForTerminalSystemWebApp();
+
+#endif  // CHROME_BROWSER_CHROMEOS_WEB_APPLICATIONS_TERMINAL_SYSTEM_WEB_APP_INFO_H_
diff --git a/chrome/browser/client_hints/client_hints_browsertest.cc b/chrome/browser/client_hints/client_hints_browsertest.cc
index 3ea2395..6cbb135 100644
--- a/chrome/browser/client_hints/client_hints_browsertest.cc
+++ b/chrome/browser/client_hints/client_hints_browsertest.cc
@@ -37,7 +37,6 @@
 #include "content/public/browser/render_view_host.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
-#include "content/public/common/web_preferences.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_utils.h"
@@ -52,6 +51,7 @@
 #include "services/network/public/cpp/features.h"
 #include "services/network/public/cpp/network_switches.h"
 #include "third_party/blink/public/common/client_hints/client_hints.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 
 namespace {
 
@@ -314,7 +314,8 @@
   void SetJsEnabledForActiveView(bool enabled) {
     content::WebContents* web_contents =
         browser()->tab_strip_model()->GetActiveWebContents();
-    content::WebPreferences prefs = web_contents->GetOrCreateWebPreferences();
+    blink::web_pref::WebPreferences prefs =
+        web_contents->GetOrCreateWebPreferences();
     prefs.javascript_enabled = enabled;
     web_contents->SetWebPreferences(prefs);
   }
diff --git a/chrome/browser/devtools/protocol/cast_handler.h b/chrome/browser/devtools/protocol/cast_handler.h
index cb14c6fa0..357d94e 100644
--- a/chrome/browser/devtools/protocol/cast_handler.h
+++ b/chrome/browser/devtools/protocol/cast_handler.h
@@ -9,11 +9,12 @@
 #include <string>
 #include <vector>
 
+#include "base/containers/flat_set.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/devtools/protocol/cast.h"
 #include "chrome/browser/media/router/issues_observer.h"
-#include "chrome/browser/media/router/presentation/presentation_service_delegate_impl.h"
 #include "chrome/browser/ui/media_router/query_result_manager.h"
+#include "components/media_router/common/mojom/media_router.mojom.h"
 
 namespace content {
 class WebContents;
@@ -21,6 +22,7 @@
 
 namespace media_router {
 class MediaRouter;
+class StartPresentationContext;
 }  // namespace media_router
 
 class CastHandler : public protocol::Cast::Backend,
diff --git a/chrome/browser/devtools/protocol/cast_handler_unittest.cc b/chrome/browser/devtools/protocol/cast_handler_unittest.cc
index d8e7eaa..4e678d8 100644
--- a/chrome/browser/devtools/protocol/cast_handler_unittest.cc
+++ b/chrome/browser/devtools/protocol/cast_handler_unittest.cc
@@ -8,11 +8,13 @@
 #include "base/bind_helpers.h"
 #include "chrome/browser/media/router/media_router_factory.h"
 #include "chrome/browser/media/router/media_sinks_observer.h"
+#include "chrome/browser/media/router/presentation/presentation_service_delegate_impl.h"
 #include "chrome/browser/media/router/test/mock_media_router.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "components/media_router/common/media_sink.h"
 #include "components/media_router/common/media_source.h"
 #include "components/sessions/content/session_tab_helper.h"
+#include "content/public/browser/presentation_request.h"
 
 using testing::_;
 using testing::DoAll;
diff --git a/chrome/browser/dom_distiller/tab_utils_browsertest.cc b/chrome/browser/dom_distiller/tab_utils_browsertest.cc
index 15151e8..0d2ee04 100644
--- a/chrome/browser/dom_distiller/tab_utils_browsertest.cc
+++ b/chrome/browser/dom_distiller/tab_utils_browsertest.cc
@@ -40,7 +40,6 @@
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/isolated_world_ids.h"
-#include "content/public/common/web_preferences.h"
 #include "content/public/test/back_forward_cache_util.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
@@ -49,6 +48,7 @@
 #include "net/test/embedded_test_server/request_handler_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/features.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "ui/gfx/image/image_unittest_util.h"
 
 namespace dom_distiller {
diff --git a/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc b/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc
index 881d58e..3d227b9 100644
--- a/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc
+++ b/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc
@@ -77,13 +77,13 @@
 #include "extensions/browser/api/vpn_provider/vpn_service_factory.h"
 #endif  // defined(OS_CHROMEOS)
 
+using blink::web_pref::WebPreferences;
 using content::BrowserContext;
 using content::BrowserThread;
 using content::BrowserURLHandler;
 using content::RenderViewHost;
 using content::SiteInstance;
 using content::WebContents;
-using content::WebPreferences;
 
 namespace extensions {
 
diff --git a/chrome/browser/extensions/chrome_content_browser_client_extensions_part.h b/chrome/browser/extensions/chrome_content_browser_client_extensions_part.h
index c1be3e0..49d32ca 100644
--- a/chrome/browser/extensions/chrome_content_browser_client_extensions_part.h
+++ b/chrome/browser/extensions/chrome_content_browser_client_extensions_part.h
@@ -103,7 +103,7 @@
   void SiteInstanceGotProcess(content::SiteInstance* site_instance) override;
   void SiteInstanceDeleting(content::SiteInstance* site_instance) override;
   void OverrideWebkitPrefs(content::RenderViewHost* rvh,
-                           content::WebPreferences* web_prefs) override;
+                           blink::web_pref::WebPreferences* web_prefs) override;
   void BrowserURLHandlerCreated(content::BrowserURLHandler* handler) override;
   void GetAdditionalAllowedSchemesForFileSystem(
       std::vector<std::string>* additional_allowed_schemes) override;
diff --git a/chrome/browser/extensions/crx_installer.cc b/chrome/browser/extensions/crx_installer.cc
index d5fe0ef..8b094bec5 100644
--- a/chrome/browser/extensions/crx_installer.cc
+++ b/chrome/browser/extensions/crx_installer.cc
@@ -650,7 +650,7 @@
             CrxInstallErrorType::DECLINED,
             CrxInstallErrorDetail::DEPENDENCY_NOT_ALLOWLISTED,
             l10n_util::GetStringFUTF16(
-                IDS_EXTENSION_INSTALL_DEPENDENCY_NOT_WHITELISTED,
+                IDS_EXTENSION_INSTALL_DEPENDENCY_NOT_ALLOWLISTED,
                 base::UTF8ToUTF16(extension()->name()),
                 base::UTF8ToUTF16(imported_module->name()))));
         return;
@@ -717,7 +717,7 @@
       ReportFailureFromUIThread(CrxInstallError(
           CrxInstallErrorType::DECLINED,
           CrxInstallErrorDetail::EXTENSION_IS_BLOCKLISTED,
-          l10n_util::GetStringFUTF16(IDS_EXTENSION_IS_BLACKLISTED,
+          l10n_util::GetStringFUTF16(IDS_EXTENSION_IS_BLOCKLISTED,
                                      base::UTF8ToUTF16(extension()->name()))));
       UMA_HISTOGRAM_ENUMERATION("ExtensionBlacklist.BlockCRX",
                                 extension()->location(),
diff --git a/chrome/browser/extensions/extension_error_ui_default.cc b/chrome/browser/extensions/extension_error_ui_default.cc
index 7c84d33..cf94a67 100644
--- a/chrome/browser/extensions/extension_error_ui_default.cc
+++ b/chrome/browser/extensions/extension_error_ui_default.cc
@@ -58,10 +58,10 @@
     int id = 0;
     if (disable_remotely_for_malware ||
         (blocklist_state == BlocklistState::BLOCKLISTED_MALWARE)) {
-      id = IDS_EXTENSION_ALERT_ITEM_BLACKLISTED_MALWARE;
+      id = IDS_EXTENSION_ALERT_ITEM_BLOCKLISTED_MALWARE;
     } else {
-      id = extension->is_app() ? IDS_APP_ALERT_ITEM_BLACKLISTED_OTHER
-                               : IDS_EXTENSION_ALERT_ITEM_BLACKLISTED_OTHER;
+      id = extension->is_app() ? IDS_APP_ALERT_ITEM_BLOCKLISTED_OTHER
+                               : IDS_EXTENSION_ALERT_ITEM_BLOCKLISTED_OTHER;
     }
     message.push_back(
         l10n_util::GetStringFUTF16(id, base::UTF8ToUTF16(extension->name())));
diff --git a/chrome/browser/extensions/extension_error_ui_default_unittest.cc b/chrome/browser/extensions/extension_error_ui_default_unittest.cc
index 6038aca..5c35016 100644
--- a/chrome/browser/extensions/extension_error_ui_default_unittest.cc
+++ b/chrome/browser/extensions/extension_error_ui_default_unittest.cc
@@ -59,9 +59,9 @@
   EXPECT_THAT(
       messages,
       testing::ElementsAre(
-          l10n_util::GetStringFUTF16(IDS_EXTENSION_ALERT_ITEM_BLACKLISTED_OTHER,
+          l10n_util::GetStringFUTF16(IDS_EXTENSION_ALERT_ITEM_BLOCKLISTED_OTHER,
                                      base::UTF8ToUTF16("Bar")),
-          l10n_util::GetStringFUTF16(IDS_EXTENSION_ALERT_ITEM_BLACKLISTED_OTHER,
+          l10n_util::GetStringFUTF16(IDS_EXTENSION_ALERT_ITEM_BLOCKLISTED_OTHER,
                                      base::UTF8ToUTF16("Baz"))));
 }
 
@@ -82,7 +82,7 @@
   std::vector<base::string16> messages = bubble->GetBubbleViewMessages();
 
   EXPECT_THAT(messages, testing::ElementsAre(l10n_util::GetStringFUTF16(
-                            IDS_APP_ALERT_ITEM_BLACKLISTED_OTHER,
+                            IDS_APP_ALERT_ITEM_BLOCKLISTED_OTHER,
                             base::UTF8ToUTF16("Bar"))));
 }
 
@@ -107,6 +107,6 @@
   std::vector<base::string16> messages = bubble->GetBubbleViewMessages();
 
   EXPECT_THAT(messages, testing::ElementsAre(l10n_util::GetStringFUTF16(
-                            IDS_EXTENSION_ALERT_ITEM_BLACKLISTED_MALWARE,
+                            IDS_EXTENSION_ALERT_ITEM_BLOCKLISTED_MALWARE,
                             base::UTF8ToUTF16(extension->name()))));
 }
diff --git a/chrome/browser/extensions/extension_webkit_preferences.cc b/chrome/browser/extensions/extension_webkit_preferences.cc
index 73ce8c3..4155812 100644
--- a/chrome/browser/extensions/extension_webkit_preferences.cc
+++ b/chrome/browser/extensions/extension_webkit_preferences.cc
@@ -6,15 +6,15 @@
 
 #include "base/command_line.h"
 #include "chrome/common/chrome_switches.h"
-#include "content/public/common/web_preferences.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/manifest.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 
 namespace extension_webkit_preferences {
 
 void SetPreferences(const extensions::Extension* extension,
                     extensions::ViewType render_view_type,
-                    content::WebPreferences* webkit_prefs) {
+                    blink::web_pref::WebPreferences* webkit_prefs) {
   if (!extension)
     return;
 
diff --git a/chrome/browser/extensions/extension_webkit_preferences.h b/chrome/browser/extensions/extension_webkit_preferences.h
index 0675287..85a5242 100644
--- a/chrome/browser/extensions/extension_webkit_preferences.h
+++ b/chrome/browser/extensions/extension_webkit_preferences.h
@@ -7,9 +7,11 @@
 
 #include "extensions/common/view_type.h"
 
-namespace content {
+namespace blink {
+namespace web_pref {
 struct WebPreferences;
 }
+}  // namespace blink
 
 namespace extensions {
 class Extension;
@@ -19,7 +21,7 @@
 
 void SetPreferences(const extensions::Extension* extension,
                     extensions::ViewType render_view_type,
-                    content::WebPreferences* webkit_prefs);
+                    blink::web_pref::WebPreferences* webkit_prefs);
 
 }  // namespace extension_webkit_preferences
 
diff --git a/chrome/browser/extensions/gpu_browsertest.cc b/chrome/browser/extensions/gpu_browsertest.cc
index c0733300..5db1eb0 100644
--- a/chrome/browser/extensions/gpu_browsertest.cc
+++ b/chrome/browser/extensions/gpu_browsertest.cc
@@ -5,7 +5,6 @@
 #include "chrome/browser/extensions/extension_browsertest.h"
 #include "chrome/browser/ui/browser.h"
 #include "content/public/browser/render_view_host.h"
-#include "content/public/common/web_preferences.h"
 #include "content/public/test/browser_test.h"
 #include "extensions/browser/extension_host.h"
 #include "extensions/browser/process_manager.h"
diff --git a/chrome/browser/font_family_cache.cc b/chrome/browser/font_family_cache.cc
index 41b3c73..b134e46 100644
--- a/chrome/browser/font_family_cache.cc
+++ b/chrome/browser/font_family_cache.cc
@@ -31,9 +31,10 @@
 
 FontFamilyCache::~FontFamilyCache() = default;
 
-void FontFamilyCache::FillFontFamilyMap(Profile* profile,
-                                        const char* map_name,
-                                        content::ScriptFontFamilyMap* map) {
+void FontFamilyCache::FillFontFamilyMap(
+    Profile* profile,
+    const char* map_name,
+    blink::web_pref::ScriptFontFamilyMap* map) {
   FontFamilyCache* cache =
       static_cast<FontFamilyCache*>(profile->GetUserData(&kFontFamilyCacheKey));
   if (!cache) {
@@ -44,8 +45,9 @@
   cache->FillFontFamilyMap(map_name, map);
 }
 
-void FontFamilyCache::FillFontFamilyMap(const char* map_name,
-                                        content::ScriptFontFamilyMap* map) {
+void FontFamilyCache::FillFontFamilyMap(
+    const char* map_name,
+    blink::web_pref::ScriptFontFamilyMap* map) {
   // TODO(falken): Get rid of the brute-force scan over possible
   // (font family / script) combinations - see http://crbug.com/308095.
   for (size_t i = 0; i < prefs::kWebKitScriptsForFontFamilyMapsLength; ++i) {
diff --git a/chrome/browser/font_family_cache.h b/chrome/browser/font_family_cache.h
index a99372e..3e15bc5 100644
--- a/chrome/browser/font_family_cache.h
+++ b/chrome/browser/font_family_cache.h
@@ -12,7 +12,7 @@
 #include "base/strings/string16.h"
 #include "base/supports_user_data.h"
 #include "chrome/browser/font_pref_change_notifier.h"
-#include "content/public/common/web_preferences.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 
 class PrefService;
 class Profile;
@@ -24,9 +24,9 @@
 // is a unique string. It also relies on the assumption that the (const char*)
 // keys used in both inner and outer maps are compile time constants.
 // This class caches the strings necessary to update
-// "content::ScriptFontFamilyMap". This is necessary since Chrome attempts to
-// update content::ScriptFontFamilyMap 20000 times at startup. See
-// https://crbug.com/308095.
+// "blink::web_pref::ScriptFontFamilyMap". This is necessary since Chrome
+// attempts to update blink::web_pref::ScriptFontFamilyMap 20000 times at
+// startup. See https://crbug.com/308095.
 class FontFamilyCache : public base::SupportsUserData::Data {
  public:
   explicit FontFamilyCache(Profile* profile);
@@ -35,11 +35,11 @@
   // Gets or creates the relevant FontFamilyCache, and then fills |map|.
   static void FillFontFamilyMap(Profile* profile,
                                 const char* map_name,
-                                content::ScriptFontFamilyMap* map);
+                                blink::web_pref::ScriptFontFamilyMap* map);
 
   // Fills |map| with font family preferences.
   void FillFontFamilyMap(const char* map_name,
-                         content::ScriptFontFamilyMap* map);
+                         blink::web_pref::ScriptFontFamilyMap* map);
 
  protected:
   // Exposed and virtual for testing.
diff --git a/chrome/browser/media/android/router/media_router_dialog_controller_android.cc b/chrome/browser/media/android/router/media_router_dialog_controller_android.cc
index ec46080a..657f8315 100644
--- a/chrome/browser/media/android/router/media_router_dialog_controller_android.cc
+++ b/chrome/browser/media/android/router/media_router_dialog_controller_android.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/media/android/router/media_router_android.h"
 #include "chrome/browser/media/router/media_router.h"
 #include "chrome/browser/media/router/media_router_factory.h"
+#include "chrome/browser/media/router/presentation/start_presentation_context.h"
 #include "components/media_router/browser/android/jni_headers/BrowserMediaRouterDialogController_jni.h"
 #include "components/media_router/common/media_source.h"
 #include "content/public/browser/browser_context.h"
diff --git a/chrome/browser/media/kaleidoscope/kaleidoscope_service_unittest.cc b/chrome/browser/media/kaleidoscope/kaleidoscope_service_unittest.cc
index d4af97f..88176e7 100644
--- a/chrome/browser/media/kaleidoscope/kaleidoscope_service_unittest.cc
+++ b/chrome/browser/media/kaleidoscope/kaleidoscope_service_unittest.cc
@@ -50,6 +50,8 @@
     GetService()->test_url_loader_factory_for_fetcher_ =
         base::MakeRefCounted<::network::WeakWrapperSharedURLLoaderFactory>(
             &url_loader_factory_);
+
+    feature_list_.InitWithFeatures({}, {media::kKaleidoscopeModuleCacheOnly});
   }
 
   ::network::TestURLLoaderFactory* url_loader_factory() {
@@ -109,6 +111,8 @@
   }
 
   ::network::TestURLLoaderFactory url_loader_factory_;
+
+  base::test::ScopedFeatureList feature_list_;
 };
 
 TEST_F(KaleidoscopeServiceTest, Success) {
diff --git a/chrome/browser/media/router/BUILD.gn b/chrome/browser/media/router/BUILD.gn
index 2bd7ff9b73..d397ae27 100644
--- a/chrome/browser/media/router/BUILD.gn
+++ b/chrome/browser/media/router/BUILD.gn
@@ -62,6 +62,8 @@
     "presentation/presentation_service_delegate_observers.h",
     "presentation/receiver_presentation_service_delegate_impl.cc",
     "presentation/receiver_presentation_service_delegate_impl.h",
+    "presentation/start_presentation_context.cc",
+    "presentation/start_presentation_context.h",
     "presentation/web_contents_presentation_manager.cc",
     "presentation/web_contents_presentation_manager.h",
     "route_message_observer.cc",
diff --git a/chrome/browser/media/router/media_router_dialog_controller.cc b/chrome/browser/media/router/media_router_dialog_controller.cc
index fc6a9cc..49a3358 100644
--- a/chrome/browser/media/router/media_router_dialog_controller.cc
+++ b/chrome/browser/media/router/media_router_dialog_controller.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "chrome/browser/media/router/media_router_metrics.h"
+#include "chrome/browser/media/router/presentation/presentation_service_delegate_impl.h"
 #include "components/media_router/common/media_route.h"
 #include "components/media_router/common/route_request_result.h"
 #include "content/public/browser/browser_thread.h"
diff --git a/chrome/browser/media/router/media_router_dialog_controller.h b/chrome/browser/media/router/media_router_dialog_controller.h
index 550d45c..0df76656 100644
--- a/chrome/browser/media/router/media_router_dialog_controller.h
+++ b/chrome/browser/media/router/media_router_dialog_controller.h
@@ -9,7 +9,6 @@
 #include <string>
 
 #include "base/macros.h"
-#include "chrome/browser/media/router/presentation/presentation_service_delegate_impl.h"
 #include "components/media_router/common/mojom/media_router.mojom.h"
 #include "content/public/browser/presentation_request.h"
 #include "content/public/browser/presentation_service_delegate.h"
@@ -22,6 +21,7 @@
 
 namespace media_router {
 
+class StartPresentationContext;
 enum class MediaRouterDialogOpenOrigin;
 
 // An abstract base class for Media Router dialog controllers. Tied to a
diff --git a/chrome/browser/media/router/media_router_dialog_controller_unittest.cc b/chrome/browser/media/router/media_router_dialog_controller_unittest.cc
index 3b538bf..68792f6 100644
--- a/chrome/browser/media/router/media_router_dialog_controller_unittest.cc
+++ b/chrome/browser/media/router/media_router_dialog_controller_unittest.cc
@@ -9,6 +9,7 @@
 
 #include "base/bind.h"
 #include "chrome/browser/media/router/media_router_metrics.h"
+#include "chrome/browser/media/router/presentation/presentation_service_delegate_impl.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "components/media_router/common/media_route.h"
 #include "components/media_router/common/media_source.h"
diff --git a/chrome/browser/media/router/presentation/presentation_service_delegate_impl.cc b/chrome/browser/media/router/presentation/presentation_service_delegate_impl.cc
index 27eb4ee..4dcddee7 100644
--- a/chrome/browser/media/router/presentation/presentation_service_delegate_impl.cc
+++ b/chrome/browser/media/router/presentation/presentation_service_delegate_impl.cc
@@ -26,7 +26,6 @@
 #include "components/media_router/common/media_route.h"
 #include "components/media_router/common/media_sink.h"
 #include "components/media_router/common/media_source.h"
-#include "components/media_router/common/route_request_result.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/presentation_request.h"
 #include "content/public/browser/presentation_screen_availability_listener.h"
@@ -295,55 +294,6 @@
                     route_id, state_changed_cb));
 }
 
-StartPresentationContext::StartPresentationContext(
-    const content::PresentationRequest& presentation_request,
-    PresentationConnectionCallback success_cb,
-    PresentationConnectionErrorCallback error_cb)
-    : presentation_request_(presentation_request),
-      success_cb_(std::move(success_cb)),
-      error_cb_(std::move(error_cb)) {
-  DCHECK(success_cb_);
-  DCHECK(error_cb_);
-}
-
-StartPresentationContext::~StartPresentationContext() {
-  if (success_cb_ && error_cb_) {
-    std::move(error_cb_).Run(blink::mojom::PresentationError(
-        blink::mojom::PresentationErrorType::UNKNOWN, "Unknown error."));
-  }
-}
-
-void StartPresentationContext::InvokeSuccessCallback(
-    const std::string& presentation_id,
-    const GURL& presentation_url,
-    const MediaRoute& route,
-    mojom::RoutePresentationConnectionPtr connection) {
-  if (success_cb_ && error_cb_) {
-    std::move(success_cb_)
-        .Run(blink::mojom::PresentationInfo(presentation_url, presentation_id),
-             std::move(connection), route);
-  }
-}
-
-void StartPresentationContext::InvokeErrorCallback(
-    const blink::mojom::PresentationError& error) {
-  if (success_cb_ && error_cb_) {
-    std::move(error_cb_).Run(error);
-  }
-}
-
-void StartPresentationContext::HandleRouteResponse(
-    mojom::RoutePresentationConnectionPtr connection,
-    const RouteRequestResult& result) {
-  if (!result.route()) {
-    InvokeErrorCallback(blink::mojom::PresentationError(
-        blink::mojom::PresentationErrorType::UNKNOWN, result.error()));
-  } else {
-    InvokeSuccessCallback(result.presentation_id(), result.presentation_url(),
-                          *result.route(), std::move(connection));
-  }
-}
-
 PresentationServiceDelegateImpl*
 PresentationServiceDelegateImpl::GetOrCreateForWebContents(
     content::WebContents* web_contents) {
diff --git a/chrome/browser/media/router/presentation/presentation_service_delegate_impl.h b/chrome/browser/media/router/presentation/presentation_service_delegate_impl.h
index 6a70548..35ae370 100644
--- a/chrome/browser/media/router/presentation/presentation_service_delegate_impl.h
+++ b/chrome/browser/media/router/presentation/presentation_service_delegate_impl.h
@@ -19,6 +19,7 @@
 #include "build/build_config.h"
 #include "chrome/browser/media/router/media_router.h"
 #include "chrome/browser/media/router/presentation/presentation_service_delegate_observers.h"
+#include "chrome/browser/media/router/presentation/start_presentation_context.h"
 #include "chrome/browser/media/router/presentation/web_contents_presentation_manager.h"
 #include "components/media_router/common/media_source.h"
 #include "components/media_router/common/mojom/media_router.mojom.h"
@@ -42,46 +43,6 @@
 class PresentationFrame;
 class RouteRequestResult;
 
-// Helper data structure to hold information for a request from the
-// Presentation API. Contains information on the PresentationRequest, and
-// success / error callbacks. Depending on the route creation outcome,
-// only one of the callbacks will be invoked exactly once.
-class StartPresentationContext {
- public:
-  using PresentationConnectionCallback =
-      base::OnceCallback<void(const blink::mojom::PresentationInfo&,
-                              mojom::RoutePresentationConnectionPtr,
-                              const MediaRoute&)>;
-  using PresentationConnectionErrorCallback =
-      content::PresentationConnectionErrorCallback;
-
-  StartPresentationContext(
-      const content::PresentationRequest& presentation_request,
-      PresentationConnectionCallback success_cb,
-      PresentationConnectionErrorCallback error_cb);
-  ~StartPresentationContext();
-
-  const content::PresentationRequest& presentation_request() const {
-    return presentation_request_;
-  }
-
-  // Invokes |success_cb_| or |error_cb_| with the given arguments.
-  void InvokeSuccessCallback(const std::string& presentation_id,
-                             const GURL& presentation_url,
-                             const MediaRoute& route,
-                             mojom::RoutePresentationConnectionPtr connection);
-  void InvokeErrorCallback(const blink::mojom::PresentationError& error);
-
-  // Handle route creation/joining response by invoking the right callback.
-  void HandleRouteResponse(mojom::RoutePresentationConnectionPtr connection,
-                           const RouteRequestResult& result);
-
- private:
-  content::PresentationRequest presentation_request_;
-  PresentationConnectionCallback success_cb_;
-  PresentationConnectionErrorCallback error_cb_;
-};
-
 // Implementation of PresentationServiceDelegate that interfaces an instance of
 // WebContents with the Chrome Media Router. It uses the Media Router to handle
 // presentation API calls forwarded from PresentationServiceImpl. In addition,
@@ -261,7 +222,8 @@
   MediaRouter* router_;
 
   // References to the observers listening for changes to the default
-  // presentation and presentation MediaRoutes associated with the WebContents.
+  // presentation and presentation MediaRoutes associated with the
+  // WebContents.
   base::ObserverList<WebContentsPresentationManager::Observer>
       presentation_observers_;
 
@@ -272,8 +234,8 @@
   content::DefaultPresentationConnectionCallback
       default_presentation_started_callback_;
 
-  // If this callback is set when a request to start a presentation is made, it
-  // is called instead of showing the Media Router dialog.
+  // If this callback is set when a request to start a presentation is made,
+  // it is called instead of showing the Media Router dialog.
   base::RepeatingCallback<void(std::unique_ptr<StartPresentationContext>)>
       start_presentation_cb_;
 
diff --git a/chrome/browser/media/router/presentation/presentation_service_delegate_impl_unittest.cc b/chrome/browser/media/router/presentation/presentation_service_delegate_impl_unittest.cc
index fcbf8877b..b8f4905f 100644
--- a/chrome/browser/media/router/presentation/presentation_service_delegate_impl_unittest.cc
+++ b/chrome/browser/media/router/presentation/presentation_service_delegate_impl_unittest.cc
@@ -555,8 +555,8 @@
   EXPECT_CALL(success_cb, Run(_));
 
   delegate_impl_->OnStartPresentationSucceeded(
-      rfh_id, success_cb.Get(), presentation_info, /** connection */ nullptr,
-      media_route);
+      rfh_id, success_cb.Get(), presentation_info,
+      /** connection */ nullptr, media_route);
 
   EXPECT_CALL(mock_local_manager,
               UnregisterLocalPresentationController(kPresentationId, rfh_id))
diff --git a/chrome/browser/media/router/presentation/receiver_presentation_service_delegate_impl.cc b/chrome/browser/media/router/presentation/receiver_presentation_service_delegate_impl.cc
index 52e4907..18f68f8 100644
--- a/chrome/browser/media/router/presentation/receiver_presentation_service_delegate_impl.cc
+++ b/chrome/browser/media/router/presentation/receiver_presentation_service_delegate_impl.cc
@@ -9,7 +9,7 @@
 #include "chrome/browser/media/router/presentation/local_presentation_manager_factory.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.h"
-#include "content/public/common/web_preferences.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 
 using content::RenderFrameHost;
 
diff --git a/chrome/browser/media/router/presentation/start_presentation_context.cc b/chrome/browser/media/router/presentation/start_presentation_context.cc
new file mode 100644
index 0000000..7a4708f
--- /dev/null
+++ b/chrome/browser/media/router/presentation/start_presentation_context.cc
@@ -0,0 +1,61 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/media/router/presentation/start_presentation_context.h"
+
+#include "components/media_router/common/mojom/media_router.mojom.h"
+#include "components/media_router/common/route_request_result.h"
+
+namespace media_router {
+
+StartPresentationContext::StartPresentationContext(
+    const content::PresentationRequest& presentation_request,
+    PresentationConnectionCallback success_cb,
+    PresentationConnectionErrorCallback error_cb)
+    : presentation_request_(presentation_request),
+      success_cb_(std::move(success_cb)),
+      error_cb_(std::move(error_cb)) {
+  DCHECK(success_cb_);
+  DCHECK(error_cb_);
+}
+
+StartPresentationContext::~StartPresentationContext() {
+  if (success_cb_ && error_cb_) {
+    std::move(error_cb_).Run(blink::mojom::PresentationError(
+        blink::mojom::PresentationErrorType::UNKNOWN, "Unknown error."));
+  }
+}
+
+void StartPresentationContext::InvokeSuccessCallback(
+    const std::string& presentation_id,
+    const GURL& presentation_url,
+    const MediaRoute& route,
+    mojom::RoutePresentationConnectionPtr connection) {
+  if (success_cb_ && error_cb_) {
+    std::move(success_cb_)
+        .Run(blink::mojom::PresentationInfo(presentation_url, presentation_id),
+             std::move(connection), route);
+  }
+}
+
+void StartPresentationContext::InvokeErrorCallback(
+    const blink::mojom::PresentationError& error) {
+  if (success_cb_ && error_cb_) {
+    std::move(error_cb_).Run(error);
+  }
+}
+
+void StartPresentationContext::HandleRouteResponse(
+    mojom::RoutePresentationConnectionPtr connection,
+    const RouteRequestResult& result) {
+  if (!result.route()) {
+    InvokeErrorCallback(blink::mojom::PresentationError(
+        blink::mojom::PresentationErrorType::UNKNOWN, result.error()));
+  } else {
+    InvokeSuccessCallback(result.presentation_id(), result.presentation_url(),
+                          *result.route(), std::move(connection));
+  }
+}
+
+}  // namespace media_router
diff --git a/chrome/browser/media/router/presentation/start_presentation_context.h b/chrome/browser/media/router/presentation/start_presentation_context.h
new file mode 100644
index 0000000..a60c4e5
--- /dev/null
+++ b/chrome/browser/media/router/presentation/start_presentation_context.h
@@ -0,0 +1,65 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_MEDIA_ROUTER_PRESENTATION_START_PRESENTATION_CONTEXT_H_
+#define CHROME_BROWSER_MEDIA_ROUTER_PRESENTATION_START_PRESENTATION_CONTEXT_H_
+
+#include "base/callback.h"
+#include "components/media_router/common/mojom/media_router.mojom-forward.h"
+#include "content/public/browser/presentation_request.h"
+#include "content/public/browser/presentation_service_delegate.h"
+#include "third_party/blink/public/mojom/presentation/presentation.mojom-forward.h"
+
+namespace content {
+struct PresentationRequest;
+}  // namespace content
+
+namespace media_router {
+
+class MediaRoute;
+class RouteRequestResult;
+
+// Helper data structure to hold information for a request from the
+// Presentation API. Contains information on the PresentationRequest, and
+// success / error callbacks. Depending on the route creation outcome,
+// only one of the callbacks will be invoked exactly once.
+class StartPresentationContext {
+ public:
+  using PresentationConnectionCallback =
+      base::OnceCallback<void(const blink::mojom::PresentationInfo&,
+                              mojom::RoutePresentationConnectionPtr,
+                              const MediaRoute&)>;
+  using PresentationConnectionErrorCallback =
+      content::PresentationConnectionErrorCallback;
+
+  StartPresentationContext(
+      const content::PresentationRequest& presentation_request,
+      PresentationConnectionCallback success_cb,
+      PresentationConnectionErrorCallback error_cb);
+  ~StartPresentationContext();
+
+  const content::PresentationRequest& presentation_request() const {
+    return presentation_request_;
+  }
+
+  // Invokes |success_cb_| or |error_cb_| with the given arguments.
+  void InvokeSuccessCallback(const std::string& presentation_id,
+                             const GURL& presentation_url,
+                             const MediaRoute& route,
+                             mojom::RoutePresentationConnectionPtr connection);
+  void InvokeErrorCallback(const blink::mojom::PresentationError& error);
+
+  // Handle route creation/joining response by invoking the right callback.
+  void HandleRouteResponse(mojom::RoutePresentationConnectionPtr connection,
+                           const RouteRequestResult& result);
+
+ private:
+  const content::PresentationRequest presentation_request_;
+  PresentationConnectionCallback success_cb_;
+  PresentationConnectionErrorCallback error_cb_;
+};
+
+}  // namespace media_router
+
+#endif  // CHROME_BROWSER_MEDIA_ROUTER_PRESENTATION_START_PRESENTATION_CONTEXT_H_
diff --git a/chrome/browser/media/router/presentation/web_contents_presentation_manager.h b/chrome/browser/media/router/presentation/web_contents_presentation_manager.h
index d491a2d0..2a85030 100644
--- a/chrome/browser/media/router/presentation/web_contents_presentation_manager.h
+++ b/chrome/browser/media/router/presentation/web_contents_presentation_manager.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_MEDIA_ROUTER_PRESENTATION_WEB_CONTENTS_PRESENTATION_MANAGER_H_
 #define CHROME_BROWSER_MEDIA_ROUTER_PRESENTATION_WEB_CONTENTS_PRESENTATION_MANAGER_H_
 
+#include <memory>
 #include <vector>
 
 #include "base/containers/flat_map.h"
diff --git a/chrome/browser/media/unified_autoplay_browsertest.cc b/chrome/browser/media/unified_autoplay_browsertest.cc
index 9611715..c274fc9 100644
--- a/chrome/browser/media/unified_autoplay_browsertest.cc
+++ b/chrome/browser/media/unified_autoplay_browsertest.cc
@@ -16,7 +16,6 @@
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/common/content_client.h"
-#include "content/public/common/web_preferences.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_frame_navigation_observer.h"
@@ -25,6 +24,7 @@
 #include "mojo/public/cpp/bindings/associated_remote.h"
 #include "net/dns/mock_host_resolver.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "third_party/blink/public/mojom/autoplay/autoplay.mojom.h"
 
 namespace {
@@ -40,8 +40,9 @@
   ChromeContentBrowserClientOverrideWebAppScope() = default;
   ~ChromeContentBrowserClientOverrideWebAppScope() override = default;
 
-  void OverrideWebkitPrefs(content::RenderViewHost* rvh,
-                           content::WebPreferences* web_prefs) override {
+  void OverrideWebkitPrefs(
+      content::RenderViewHost* rvh,
+      blink::web_pref::WebPreferences* web_prefs) override {
     ChromeContentBrowserClient::OverrideWebkitPrefs(rvh, web_prefs);
 
     web_prefs->web_app_scope = web_app_scope_;
diff --git a/chrome/browser/media/unified_autoplay_config_unittest.cc b/chrome/browser/media/unified_autoplay_config_unittest.cc
index fbc2813..4632eed8 100644
--- a/chrome/browser/media/unified_autoplay_config_unittest.cc
+++ b/chrome/browser/media/unified_autoplay_config_unittest.cc
@@ -13,9 +13,9 @@
 #include "chrome/test/base/testing_profile.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/prefs/pref_service.h"
-#include "content/public/common/web_preferences.h"
 #include "content/public/test/web_contents_tester.h"
 #include "media/base/media_switches.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 
 // Unit tests for the unified autoplay policy with the unified sound settings
 // UI enabled.
@@ -50,7 +50,7 @@
     return UnifiedAutoplayConfig::ShouldBlockAutoplay(profile());
   }
 
-  content::AutoplayPolicy GetAppliedAutoplayPolicy() {
+  blink::web_pref::AutoplayPolicy GetAppliedAutoplayPolicy() {
     return web_contents()->GetOrCreateWebPreferences().autoplay_policy;
   }
 
@@ -72,7 +72,7 @@
   EXPECT_FALSE(ShouldBlockAutoplay());
 
   NavigateToTestPage();
-  EXPECT_EQ(content::AutoplayPolicy::kNoUserGestureRequired,
+  EXPECT_EQ(blink::web_pref::AutoplayPolicy::kNoUserGestureRequired,
             GetAppliedAutoplayPolicy());
 }
 
@@ -83,7 +83,7 @@
   EXPECT_TRUE(ShouldBlockAutoplay());
 
   NavigateToTestPage();
-  EXPECT_EQ(content::AutoplayPolicy::kDocumentUserActivationRequired,
+  EXPECT_EQ(blink::web_pref::AutoplayPolicy::kDocumentUserActivationRequired,
             GetAppliedAutoplayPolicy());
 
   // Set back to ALLOW to ensure that the policy is updated on the next
@@ -92,7 +92,7 @@
   EXPECT_FALSE(ShouldBlockAutoplay());
 
   NavigateToTestPage();
-  EXPECT_EQ(content::AutoplayPolicy::kNoUserGestureRequired,
+  EXPECT_EQ(blink::web_pref::AutoplayPolicy::kNoUserGestureRequired,
             GetAppliedAutoplayPolicy());
 }
 
@@ -106,7 +106,7 @@
   EXPECT_FALSE(ShouldBlockAutoplay());
 
   NavigateToTestPage();
-  EXPECT_EQ(content::AutoplayPolicy::kDocumentUserActivationRequired,
+  EXPECT_EQ(blink::web_pref::AutoplayPolicy::kDocumentUserActivationRequired,
             GetAppliedAutoplayPolicy());
 }
 
@@ -114,7 +114,7 @@
   EXPECT_TRUE(ShouldBlockAutoplay());
 
   NavigateToTestPage();
-  EXPECT_EQ(content::AutoplayPolicy::kDocumentUserActivationRequired,
+  EXPECT_EQ(blink::web_pref::AutoplayPolicy::kDocumentUserActivationRequired,
             GetAppliedAutoplayPolicy());
 }
 
@@ -123,7 +123,7 @@
   EXPECT_FALSE(ShouldBlockAutoplay());
 
   NavigateToTestPage();
-  EXPECT_EQ(content::AutoplayPolicy::kNoUserGestureRequired,
+  EXPECT_EQ(blink::web_pref::AutoplayPolicy::kNoUserGestureRequired,
             GetAppliedAutoplayPolicy());
 
   // Now update the pref and make sure we apply it on the next navigation.
@@ -131,7 +131,7 @@
   EXPECT_TRUE(ShouldBlockAutoplay());
 
   NavigateToTestPage();
-  EXPECT_EQ(content::AutoplayPolicy::kDocumentUserActivationRequired,
+  EXPECT_EQ(blink::web_pref::AutoplayPolicy::kDocumentUserActivationRequired,
             GetAppliedAutoplayPolicy());
 }
 
@@ -159,6 +159,6 @@
   EXPECT_TRUE(ShouldBlockAutoplay());
 
   NavigateToTestPage();
-  EXPECT_EQ(content::AutoplayPolicy::kUserGestureRequired,
+  EXPECT_EQ(blink::web_pref::AutoplayPolicy::kUserGestureRequired,
             GetAppliedAutoplayPolicy());
 }
diff --git a/chrome/browser/nearby_sharing/nearby_sharing_service.h b/chrome/browser/nearby_sharing/nearby_sharing_service.h
index 0265f62..c0643be 100644
--- a/chrome/browser/nearby_sharing/nearby_sharing_service.h
+++ b/chrome/browser/nearby_sharing/nearby_sharing_service.h
@@ -65,6 +65,10 @@
   class Observer : public base::CheckedObserver {
    public:
     virtual void OnHighVisibilityChanged(bool in_high_visibility) = 0;
+
+    // Called during the |KeyedService| shutdown, but before everything has been
+    // cleaned up. It is safe to remove any observers on this event.
+    virtual void OnShutdown() = 0;
   };
 
   using StatusCodesCallback =
diff --git a/chrome/browser/nearby_sharing/nearby_sharing_service_impl.cc b/chrome/browser/nearby_sharing/nearby_sharing_service_impl.cc
index 4c12740..ebc7331b 100644
--- a/chrome/browser/nearby_sharing/nearby_sharing_service_impl.cc
+++ b/chrome/browser/nearby_sharing/nearby_sharing_service_impl.cc
@@ -252,6 +252,12 @@
 }
 
 void NearbySharingServiceImpl::Shutdown() {
+  // Before we clean up, lets give observers a heads up we are shutting down.
+  for (auto& observer : observers_) {
+    observer.OnShutdown();
+  }
+  observers_.Clear();
+
   // Clear in-progress transfers.
   ClearOutgoingShareTargetInfoMap();
   incoming_share_target_info_map_.clear();
diff --git a/chrome/browser/nearby_sharing/nearby_sharing_service_impl_unittest.cc b/chrome/browser/nearby_sharing/nearby_sharing_service_impl_unittest.cc
index e87bef8..49efdd0 100644
--- a/chrome/browser/nearby_sharing/nearby_sharing_service_impl_unittest.cc
+++ b/chrome/browser/nearby_sharing/nearby_sharing_service_impl_unittest.cc
@@ -922,11 +922,22 @@
 
 class TestObserver : public NearbySharingService::Observer {
  public:
+  explicit TestObserver(NearbySharingService* service) : service_(service) {
+    service_->AddObserver(this);
+  }
+
   void OnHighVisibilityChanged(bool in_high_visibility) override {
     in_high_visibility_ = in_high_visibility;
   }
 
+  void OnShutdown() override {
+    shutdown_called_ = true;
+    service_->RemoveObserver(this);
+  }
+
   bool in_high_visibility_ = false;
+  bool shutdown_called_ = false;
+  NearbySharingService* service_;
 };
 
 }  // namespace
@@ -3432,14 +3443,13 @@
 
 TEST_F(NearbySharingServiceImplTest,
        RegisterForegroundReceiveSurfaceEntersHighVisibility) {
-  TestObserver observer;
+  TestObserver observer(service_.get());
   NiceMock<MockTransferUpdateCallback> callback;
   ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
 
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   SetVisibility(nearby_share::mojom::Visibility::kAllContacts);
   local_device_data_manager()->SetDeviceName(kDeviceName);
-  service_->AddObserver(&observer);
 
   // To start, we should not be in high visibility state.
   EXPECT_FALSE(service_->IsInHighVisibility());
@@ -3460,5 +3470,19 @@
   EXPECT_FALSE(service_->IsInHighVisibility());
   EXPECT_FALSE(observer.in_high_visibility_);
 
+  // Remove the observer before it goes out of scope.
   service_->RemoveObserver(&observer);
 }
+
+TEST_F(NearbySharingServiceImplTest, ShutdownCallsObservers) {
+  TestObserver observer(service_.get());
+
+  EXPECT_FALSE(observer.shutdown_called_);
+
+  service_->Shutdown();
+
+  EXPECT_TRUE(observer.shutdown_called_);
+
+  // Prevent a double shutdown.
+  service_.reset();
+}
diff --git a/chrome/browser/net/network_context_configuration_browsertest.cc b/chrome/browser/net/network_context_configuration_browsertest.cc
index d9a6c766..6a65824 100644
--- a/chrome/browser/net/network_context_configuration_browsertest.cc
+++ b/chrome/browser/net/network_context_configuration_browsertest.cc
@@ -1390,35 +1390,44 @@
   ASSERT_TRUE(FetchHeaderEcho("user-agent", &user_agent));
   EXPECT_EQ(::GetUserAgent(), user_agent);
 
-  // Now change the profile a different language, and see if the headers
-  // get updated.
+  // Change AcceptLanguages preferences, and check that headers are updated.
+  // First, A single language.
   browser()->profile()->GetPrefs()->SetString(language::prefs::kAcceptLanguages,
-                                              "uk");
+                                              "zu");
   FlushNetworkInterface();
   std::string accept_language2, user_agent2;
   ASSERT_TRUE(FetchHeaderEcho("accept-language", &accept_language2));
-  if (GetProfile()->IsOffTheRecord()) {
-    EXPECT_EQ(system ? kNoAcceptLanguage : "en-US,en;q=0.9", accept_language2);
-  } else {
-    EXPECT_EQ(system ? kNoAcceptLanguage : "uk", accept_language2);
-  }
+  EXPECT_EQ(system ? kNoAcceptLanguage : "zu", accept_language2);
   ASSERT_TRUE(FetchHeaderEcho("user-agent", &user_agent2));
   EXPECT_EQ(::GetUserAgent(), user_agent2);
 
-  // Try a more complicated one, with multiple languages.
+  // Second, a single language with locale.
   browser()->profile()->GetPrefs()->SetString(language::prefs::kAcceptLanguages,
-                                              "uk, en-US");
+                                              "zu-ZA");
   FlushNetworkInterface();
   std::string accept_language3, user_agent3;
   ASSERT_TRUE(FetchHeaderEcho("accept-language", &accept_language3));
-  if (GetProfile()->IsOffTheRecord()) {
-    EXPECT_EQ(system ? kNoAcceptLanguage : "en-US,en;q=0.9", accept_language3);
-  } else {
-    EXPECT_EQ(system ? kNoAcceptLanguage : "uk,en-US;q=0.9,en;q=0.8",
-              accept_language3);
-  }
+  EXPECT_EQ(system ? kNoAcceptLanguage : "zu-ZA,zu;q=0.9", accept_language3);
   ASSERT_TRUE(FetchHeaderEcho("user-agent", &user_agent3));
   EXPECT_EQ(::GetUserAgent(), user_agent3);
+
+  // Third, a list with multiple languages. Incognito mode should return only
+  // the first.
+  browser()->profile()->GetPrefs()->SetString(language::prefs::kAcceptLanguages,
+                                              "ar,am,en-GB,ru,zu");
+  FlushNetworkInterface();
+  std::string accept_language4;
+  std::string user_agent4;
+  ASSERT_TRUE(FetchHeaderEcho("accept-language", &accept_language4));
+  if (GetProfile()->IsOffTheRecord()) {
+    EXPECT_EQ(system ? kNoAcceptLanguage : "ar", accept_language4);
+  } else {
+    EXPECT_EQ(system ? kNoAcceptLanguage
+                     : "ar,am;q=0.9,en-GB;q=0.8,en;q=0.7,ru;q=0.6,zu;q=0.5",
+              accept_language4);
+  }
+  ASSERT_TRUE(FetchHeaderEcho("user-agent", &user_agent4));
+  EXPECT_EQ(::GetUserAgent(), user_agent4);
 }
 
 // First part of testing enable referrers. Check that referrers are enabled by
diff --git a/chrome/browser/net/profile_network_context_service.cc b/chrome/browser/net/profile_network_context_service.cc
index 2a07bb8..fa3161f3 100644
--- a/chrome/browser/net/profile_network_context_service.cc
+++ b/chrome/browser/net/profile_network_context_service.cc
@@ -36,8 +36,8 @@
 #include "components/certificate_transparency/pref_names.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/embedder_support/pref_names.h"
+#include "components/language/core/browser/language_prefs.h"
 #include "components/language/core/browser/pref_names.h"
-#include "components/language/core/common/locale_util.h"
 #include "components/metrics/metrics_pref_names.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_registry_simple.h"
@@ -378,8 +378,9 @@
 
 std::string ProfileNetworkContextService::ComputeAcceptLanguage() const {
   if (profile_->IsOffTheRecord()) {
+    // In incognito mode return only the first language.
     return ComputeAcceptLanguageFromPref(
-        g_browser_process->GetApplicationLocale());
+        language::GetFirstLanguage(pref_accept_language_.GetValue()));
   }
   return ComputeAcceptLanguageFromPref(pref_accept_language_.GetValue());
 }
diff --git a/chrome/browser/pdf/pdf_extension_test.cc b/chrome/browser/pdf/pdf_extension_test.cc
index 33485299..b48ad165 100644
--- a/chrome/browser/pdf/pdf_extension_test.cc
+++ b/chrome/browser/pdf/pdf_extension_test.cc
@@ -1336,11 +1336,13 @@
   ASSERT_TRUE(guest_contents);
   WebContents* web_contents = GetActiveWebContents();
 
-  // The PDF viewer always starts at default zoom, which for tests is 100% or
-  // zoom level 0.0. Here we look at the presets to find the next zoom level
-  // above 0. Ideally we should look at the zoom levels from the PDF viewer
-  // javascript, but we assume they'll always match the browser presets, which
-  // are easier to access.
+  // The PDF viewer, when the update is disabled, always starts at default zoom,
+  // which for tests is 100% or zoom level 0.0. Here we look at the presets to
+  // find the next zoom level above 0. Ideally we should look at the zoom levels
+  // from the PDF viewer javascript, but we assume they'll always match the
+  // browser presets, which are easier to access. When the update is enabled,
+  // the presence of the sidenav causes the default zoom to be just under 90%.
+  // In this case, we add 2 additional zoom calls to match the final result.
   std::vector<double> preset_zoom_levels = zoom::PageZoom::PresetZoomLevels(0);
   auto it = std::find(preset_zoom_levels.begin(), preset_zoom_levels.end(), 0);
   ASSERT_TRUE(it != preset_zoom_levels.end());
@@ -1363,6 +1365,17 @@
   ASSERT_TRUE(
       content::ExecuteScript(guest_contents, "viewer.viewport.zoomIn();"));
 
+  // Two extra calls - the first zoomIn() takes zoom to 90%, the second to 100%,
+  // and the third goes to the next zoom level above 100%, which is the desired
+  // result for this test.
+  ASSERT_TRUE(
+      content::ExecuteScript(guest_contents,
+                             "if (document.documentElement.hasAttribute("
+                             "        'pdf-viewer-update-enabled')) {"
+                             "  viewer.viewport.zoomIn();"
+                             "  viewer.viewport.zoomIn();"
+                             "}"));
+
   watcher.Wait();
 #if defined(TOOLKIT_VIEWS) && !defined(OS_MAC)
   EXPECT_EQ(nullptr, ZoomBubbleView::GetZoomBubble());
@@ -2560,7 +2573,7 @@
 #if defined(TOOLKIT_VIEWS) && defined(USE_AURA)
 // On text selection, a touch selection menu should pop up. On clicking ellipsis
 // icon on the menu, the context menu should open up.
-IN_PROC_BROWSER_TEST_F(PDFExtensionTest,
+IN_PROC_BROWSER_TEST_P(PDFExtensionTestWithParam,
                        ContextMenuOpensFromTouchSelectionMenu) {
   const GURL url = embedded_test_server()->GetURL("/pdf/text_large.pdf");
   WebContents* const guest_contents = LoadPdfGetGuestContents(url);
@@ -2568,7 +2581,7 @@
 
   views::NamedWidgetShownWaiter waiter(views::test::AnyWidgetTestPasskey{},
                                        "TouchSelectionMenuViews");
-  gfx::Point text_selection_position(10, 10);
+  gfx::Point text_selection_position(12, 12);
   ConvertPageCoordToScreenCoord(guest_contents, &text_selection_position);
   content::SimulateTouchEventAt(GetActiveWebContents(), ui::ET_TOUCH_PRESSED,
                                 text_selection_position);
diff --git a/chrome/browser/picture_in_picture/picture_in_picture_window_controller_browsertest.cc b/chrome/browser/picture_in_picture/picture_in_picture_window_controller_browsertest.cc
index 7ab8491..84d858d 100644
--- a/chrome/browser/picture_in_picture/picture_in_picture_window_controller_browsertest.cc
+++ b/chrome/browser/picture_in_picture/picture_in_picture_window_controller_browsertest.cc
@@ -38,7 +38,6 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_switches.h"
-#include "content/public/common/web_preferences.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_navigation_observer.h"
@@ -48,6 +47,7 @@
 #include "services/media_session/public/cpp/features.h"
 #include "skia/ext/image_operations.h"
 #include "testing/gmock/include/gmock/gmock.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "ui/events/base_event_utils.h"
 #include "ui/gfx/codec/png_codec.h"
 #include "ui/views/controls/button/image_button.h"
@@ -2487,8 +2487,9 @@
   ChromeContentBrowserClientOverrideWebAppScope() = default;
   ~ChromeContentBrowserClientOverrideWebAppScope() override = default;
 
-  void OverrideWebkitPrefs(content::RenderViewHost* rvh,
-                           content::WebPreferences* web_prefs) override {
+  void OverrideWebkitPrefs(
+      content::RenderViewHost* rvh,
+      blink::web_pref::WebPreferences* web_prefs) override {
     ChromeContentBrowserClient::OverrideWebkitPrefs(rvh, web_prefs);
 
     web_prefs->web_app_scope = web_app_scope_;
diff --git a/chrome/browser/policy/policy_browsertest.cc b/chrome/browser/policy/policy_browsertest.cc
index 1bc91e8..83b900ad 100644
--- a/chrome/browser/policy/policy_browsertest.cc
+++ b/chrome/browser/policy/policy_browsertest.cc
@@ -160,7 +160,6 @@
 #include "content/public/common/content_paths.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/url_constants.h"
-#include "content/public/common/web_preferences.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/download_test_observer.h"
@@ -193,6 +192,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/input/web_input_event.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/page_transition_types.h"
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
index 46e21fa4..ea76986 100644
--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
+++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
@@ -363,11 +363,6 @@
     public static final String FLAGS_CACHED_ADAPTIVE_TOOLBAR_ENABLED = "adaptive_toolbar_enabled";
 
     /**
-     * Whether or not the bottom toolbar is enabled.
-     * Default value is false.
-     */
-    public static final String FLAGS_CACHED_BOTTOM_TOOLBAR_ENABLED = "bottom_toolbar_enabled";
-    /**
      * Whether or not command line on non-rooted devices is enabled.
      * Default value is false.
      */
@@ -391,12 +386,6 @@
     public static final String FLAGS_CACHED_INTEREST_FEED_CONTENT_SUGGESTIONS =
             "interest_feed_content_suggestions";
     /**
-     * Whether or not the labeled bottom toolbar is enabled.
-     * Default value is false.
-     */
-    public static final String FLAGS_CACHED_LABELED_BOTTOM_TOOLBAR_ENABLED =
-            "labeled_bottom_toolbar_enabled";
-    /**
      * Whether warming up network service is enabled.
      * Default value is false.
      */
@@ -541,11 +530,6 @@
     public static final KeyPrefix PROMO_TIMES_SEEN = new KeyPrefix("Chrome.PromoCard.TimesSeen.*");
 
     /**
-     * Key to cache the enabled bottom toolbar parameter.
-     */
-    public static final String VARIATION_CACHED_BOTTOM_TOOLBAR = "bottom_toolbar_variation";
-
-    /**
      * Whether the promotion for data reduction has been skipped on first invocation.
      * Default value is false.
      */
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/DeprecatedChromePreferenceKeys.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/DeprecatedChromePreferenceKeys.java
index e33f936..153950b 100644
--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/DeprecatedChromePreferenceKeys.java
+++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/DeprecatedChromePreferenceKeys.java
@@ -41,6 +41,8 @@
                 "allow_low_end_device_ui",
                 "allow_starting_service_manager_only",
                 "bookmark_search_history",
+                "bottom_toolbar_enabled",
+                "bottom_toolbar_variation",
                 "cellular_experiment",
                 "chrome_home_enabled_date",
                 "chrome_home_info_promo_shown",
@@ -54,6 +56,7 @@
                 "home_page_button_force_enabled",
                 "homepage_tile_enabled",
                 "inflate_toolbar_on_background_thread",
+                "labeled_bottom_toolbar_enabled",
                 "night_mode_available",
                 "night_mode_cct_available",
                 "night_mode_default_to_light",
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/GrandfatheredChromePreferenceKeys.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/GrandfatheredChromePreferenceKeys.java
index 74f6e581..eef9918a 100644
--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/GrandfatheredChromePreferenceKeys.java
+++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/GrandfatheredChromePreferenceKeys.java
@@ -88,13 +88,11 @@
                 ChromePreferenceKeys.FIRST_RUN_LIGHTWEIGHT_FLOW_COMPLETE,
                 ChromePreferenceKeys.FIRST_RUN_SKIP_WELCOME_PAGE,
                 ChromePreferenceKeys.FLAGS_CACHED_ADAPTIVE_TOOLBAR_ENABLED,
-                ChromePreferenceKeys.FLAGS_CACHED_BOTTOM_TOOLBAR_ENABLED,
                 ChromePreferenceKeys.FLAGS_CACHED_COMMAND_LINE_ON_NON_ROOTED_ENABLED,
                 ChromePreferenceKeys.FLAGS_CACHED_DOWNLOAD_AUTO_RESUMPTION_IN_NATIVE,
                 ChromePreferenceKeys.FLAGS_CACHED_GRID_TAB_SWITCHER_ENABLED,
                 ChromePreferenceKeys.FLAGS_CACHED_IMMERSIVE_UI_MODE_ENABLED,
                 ChromePreferenceKeys.FLAGS_CACHED_INTEREST_FEED_CONTENT_SUGGESTIONS,
-                ChromePreferenceKeys.FLAGS_CACHED_LABELED_BOTTOM_TOOLBAR_ENABLED,
                 ChromePreferenceKeys.FLAGS_CACHED_NETWORK_SERVICE_WARM_UP_ENABLED,
                 ChromePreferenceKeys.FLAGS_CACHED_PRIORITIZE_BOOTSTRAP_TASKS,
                 ChromePreferenceKeys.FLAGS_CACHED_SERVICE_MANAGER_FOR_BACKGROUND_PREFETCH,
@@ -186,7 +184,6 @@
                 ChromePreferenceKeys.TWA_DISCLOSURE_ACCEPTED_PACKAGES,
                 ChromePreferenceKeys.UI_THEME_DARKEN_WEBSITES_ENABLED,
                 ChromePreferenceKeys.UI_THEME_SETTING,
-                ChromePreferenceKeys.VARIATION_CACHED_BOTTOM_TOOLBAR,
                 ChromePreferenceKeys.VERIFIED_DIGITAL_ASSET_LINKS,
                 ChromePreferenceKeys.VR_EXIT_TO_2D_COUNT,
                 ChromePreferenceKeys.VR_FEEDBACK_OPT_OUT,
diff --git a/chrome/browser/prefs/chrome_pref_service_unittest.cc b/chrome/browser/prefs/chrome_pref_service_unittest.cc
index 60dd36a..7bd596f 100644
--- a/chrome/browser/prefs/chrome_pref_service_unittest.cc
+++ b/chrome/browser/prefs/chrome_pref_service_unittest.cc
@@ -12,11 +12,11 @@
 #include "chrome/test/base/testing_profile.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
-#include "content/public/common/web_preferences.h"
 #include "content/public/test/test_renderer_host.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 
+using blink::web_pref::WebPreferences;
 using content::RenderViewHostTester;
-using content::WebPreferences;
 
 TEST(ChromePrefServiceTest, UpdateCommandLinePrefStore) {
   TestingPrefServiceSimple prefs;
diff --git a/chrome/browser/profiles/profile_manager.h b/chrome/browser/profiles/profile_manager.h
index 1bb2d7a..f27f425 100644
--- a/chrome/browser/profiles/profile_manager.h
+++ b/chrome/browser/profiles/profile_manager.h
@@ -73,16 +73,10 @@
 
   // Get the profile for the user which created the current session.
   // Note that in case of a guest account this will return a 'suitable' profile.
-  // This function is temporary and will soon be moved to ash. As such avoid
-  // using it at all cost.
-  // TODO(skuhne): Move into ash's new user management function.
   static Profile* GetPrimaryUserProfile();
 
   // Get the profile for the currently active user.
   // Note that in case of a guest account this will return a 'suitable' profile.
-  // This function is temporary and will soon be moved to ash. As such avoid
-  // using it at all cost.
-  // TODO(skuhne): Move into ash's new user management function.
   static Profile* GetActiveUserProfile();
 
   // Load and return the initial profile for browser. On ChromeOS, this returns
diff --git a/chrome/browser/renderer_preferences_util.cc b/chrome/browser/renderer_preferences_util.cc
index 71fdef9..7ba5b3d 100644
--- a/chrome/browser/renderer_preferences_util.cc
+++ b/chrome/browser/renderer_preferences_util.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/common/pref_names.h"
+#include "components/language/core/browser/language_prefs.h"
 #include "components/language/core/browser/pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/browser_accessibility_state.h"
@@ -97,7 +98,9 @@
                               Profile* profile) {
   const PrefService* pref_service = profile->GetPrefs();
   if (profile->IsOffTheRecord()) {
-    prefs->accept_languages = g_browser_process->GetApplicationLocale();
+    // In incognito mode return only the first language.
+    prefs->accept_languages = language::GetFirstLanguage(
+        pref_service->GetString(language::prefs::kAcceptLanguages));
   } else {
     prefs->accept_languages =
         pref_service->GetString(language::prefs::kAcceptLanguages);
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js
index 609366b..f1b647c 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js
@@ -2921,8 +2921,8 @@
         assertNotNullNorUndefined(audio);
         assertNotNullNorUndefined(video);
 
-        assertEquals('', audio.name);
-        assertEquals('', video.name);
+        assertEquals(undefined, audio.name);
+        assertEquals(undefined, video.name);
         assertEquals(undefined, audio.firstChild);
         assertEquals(undefined, video.firstChild);
 
@@ -3034,3 +3034,69 @@
             .replay();
       });
 });
+
+TEST_F('ChromeVoxBackgroundTest', 'DialogAutoSummaryTextContent', function() {
+  // This was overridden in setUp() for most all tests, but we want the
+  // production behavior here.
+  Output.ROLE_INFO_[RoleType.DIALOG]['outputContextFirst'] = true;
+  const mockFeedback = this.createMockFeedback();
+  this.runWithLoadedTree(
+      `
+    <p>start</p>
+    <div role="dialog" aria-label="Setup">
+      <h1>Welcome</h1>
+      <p>This is some introductory text<p>
+      <button>Exit</button>
+      <button>Let's go</button>
+    </div>
+  `,
+      function(root) {
+        mockFeedback.call(doCmd('nextObject'))
+            .expectSpeech('Setup', 'Dialog')
+            .expectSpeech(
+                `Welcome This is some introductory text Exit Let's go`)
+            .expectSpeech('Welcome')
+            .expectSpeech('Heading 1')
+            .replay();
+      });
+});
+
+TEST_F('ChromeVoxBackgroundTest', 'ImageAnnotations', function() {
+  const mockFeedback = this.createMockFeedback();
+  this.runWithLoadedTree(
+      `
+    <p>start</p>
+    <img alt="bar" src="data:image/png;base64,iVBORw0KGgoAAAANS">
+    <img src="data:image/png;base64,iVBORw0KGgoAAAANS">
+  `,
+      function(root) {
+        const [namedImg, unnamedImg] = root.findAll({role: RoleType.IMAGE});
+
+        assertNotNullNorUndefined(namedImg);
+        assertNotNullNorUndefined(unnamedImg);
+
+        assertEquals('bar', namedImg.name);
+        assertEquals(undefined, unnamedImg.name);
+
+        // Fake the image annotation.
+        Object.defineProperty(namedImg, 'imageAnnotation', {
+          get() {
+            return 'foo';
+          }
+        });
+        Object.defineProperty(unnamedImg, 'imageAnnotation', {
+          get() {
+            return 'foo';
+          }
+        });
+
+        mockFeedback.call(doCmd('nextObject'))
+            .expectSpeech('start')
+            .expectNextSpeechUtteranceIsNot('foo')
+            .expectSpeech('bar', 'Image')
+            .call(doCmd('nextObject'))
+            .expectNextSpeechUtteranceIsNot('bar')
+            .expectSpeech('foo', 'Image')
+            .replay();
+      });
+});
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing.js
index 5c42123..564c0719a 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing.js
@@ -1049,14 +1049,16 @@
     if (!AutomationPredicate.text(startNode) ||
         (this.start_.node != startNode &&
          this.start_.node.parent != startNode)) {
-      startIndex = this.start_.index == cursors.NODE_INDEX ?
+      startIndex =
+          (this.start_.index == cursors.NODE_INDEX && this.start_.node.name) ?
           this.start_.node.name.length :
           this.start_.index;
     }
 
     if (!AutomationPredicate.text(endNode) ||
         (this.end_.node != endNode && this.end_.node.parent != endNode)) {
-      endIndex = this.end_.index == cursors.NODE_INDEX ?
+      endIndex =
+          (this.end_.index == cursors.NODE_INDEX && this.end_.node.name) ?
           this.end_.node.name.length :
           this.end_.index;
     }
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output.js
index 5aa021a3..4515fce 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output.js
@@ -1067,8 +1067,8 @@
             this.node_(
                 related, related, Output.EventType.NAVIGATE, buff, ruleStr);
           }
-        } else if (token == 'nameOrTextContent') {
-          if (node.name) {
+        } else if (token == 'nameOrTextContent' || token == 'textContent') {
+          if (node.name && token == 'nameOrTextContent') {
             ruleStr.writeToken(token);
             this.format_({
               node,
@@ -2437,7 +2437,7 @@
           $state`
     },
     alertDialog: {
-      enter: `$earcon(ALERT_MODAL) $name $state $description`,
+      enter: `$earcon(ALERT_MODAL) $name $state $description $textContent`,
       speak: `$earcon(ALERT_MODAL) $name $nameOrTextContent $description $state
           $role`
     },
@@ -2468,7 +2468,7 @@
           $state $restriction $role $description`,
     },
     date: {enter: `$nameFromNode $role $state $restriction $description`},
-    dialog: {enter: `$nameFromNode $role $description`},
+    dialog: {enter: `$nameFromNode $role $description $textContent`},
     genericContainer: {
       enter: `$nameFromNode $description $state`,
       speak: `$nameOrTextContent $description $state`
diff --git a/chrome/browser/resources/chromeos/accessibility/common/automation_predicate.js b/chrome/browser/resources/chromeos/accessibility/common/automation_predicate.js
index 403ddea..f255b80 100644
--- a/chrome/browser/resources/chromeos/accessibility/common/automation_predicate.js
+++ b/chrome/browser/resources/chromeos/accessibility/common/automation_predicate.js
@@ -49,7 +49,7 @@
 const nodeNameContainedInStaticTextChildren = function(node) {
   const name = node.name;
   let child = node.firstChild;
-  if (!child) {
+  if (name === undefined || !child) {
     return false;
   }
   let nameIndex = 0;
diff --git a/chrome/browser/resources/settings/chromeos/ambient_mode_page/topic_source_list.html b/chrome/browser/resources/settings/chromeos/ambient_mode_page/topic_source_list.html
index 888dc9d..c1b2937 100644
--- a/chrome/browser/resources/settings/chromeos/ambient_mode_page/topic_source_list.html
+++ b/chrome/browser/resources/settings/chromeos/ambient_mode_page/topic_source_list.html
@@ -18,7 +18,7 @@
 
       topic-source-item {
         align-items: center;
-        height: 48px;
+        height: 64px;
       }
 
       iron-list > *:not(:first-of-type) {
diff --git a/chrome/browser/sharesheet/drive_share_action.cc b/chrome/browser/sharesheet/drive_share_action.cc
new file mode 100644
index 0000000..f1495678
--- /dev/null
+++ b/chrome/browser/sharesheet/drive_share_action.cc
@@ -0,0 +1,54 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/sharesheet/drive_share_action.h"
+
+#include <memory>
+#include <vector>
+
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/sharesheet/sharesheet_types.h"
+#include "chrome/browser/ui/browser_navigator.h"
+#include "chrome/browser/ui/browser_navigator_params.h"
+#include "components/services/app_service/public/mojom/types.mojom.h"
+#include "ui/base/page_transition_types.h"
+#include "ui/base/window_open_disposition.h"
+#include "ui/gfx/image/image_skia.h"
+#include "url/gurl.h"
+
+DriveShareAction::DriveShareAction() = default;
+
+DriveShareAction::~DriveShareAction() = default;
+
+const base::string16 DriveShareAction::GetActionName() {
+  // TODO(crbug.com/1097623): Get the Action name from files app strings.
+  return base::UTF8ToUTF16("ShareWithOthers");
+}
+
+const gfx::ImageSkia DriveShareAction::GetActionIcon() {
+  // TODO(crbug.com/1097623): Get the icon.
+  return gfx::ImageSkia();
+}
+
+void DriveShareAction::LaunchAction(
+    sharesheet::SharesheetController* controller,
+    views::View* root_view,
+    apps::mojom::IntentPtr intent) {
+  DCHECK(intent->drive_share_url.has_value());
+  NavigateParams params(controller->GetProfile(),
+                        intent->drive_share_url.value(),
+                        ui::PAGE_TRANSITION_LINK);
+  params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
+  Navigate(&params);
+}
+
+void DriveShareAction::OnClosing(sharesheet::SharesheetController* controller) {
+  controller_ = nullptr;
+}
+
+bool DriveShareAction::ShouldShowAction(const apps::mojom::IntentPtr& intent,
+                                        bool contains_hosted_document) {
+  return intent->drive_share_url.has_value() &&
+         !intent->drive_share_url->is_empty();
+}
diff --git a/chrome/browser/sharesheet/drive_share_action.h b/chrome/browser/sharesheet/drive_share_action.h
new file mode 100644
index 0000000..b56dbb9
--- /dev/null
+++ b/chrome/browser/sharesheet/drive_share_action.h
@@ -0,0 +1,31 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SHARESHEET_DRIVE_SHARE_ACTION_H_
+#define CHROME_BROWSER_SHARESHEET_DRIVE_SHARE_ACTION_H_
+
+#include "chrome/browser/sharesheet/share_action.h"
+
+class DriveShareAction : public sharesheet::ShareAction {
+ public:
+  DriveShareAction();
+  ~DriveShareAction() override;
+  DriveShareAction(const DriveShareAction&) = delete;
+  DriveShareAction& operator=(const DriveShareAction&) = delete;
+
+  // sharesheet::ShareAction:
+  const base::string16 GetActionName() override;
+  const gfx::ImageSkia GetActionIcon() override;
+  void LaunchAction(sharesheet::SharesheetController* controller,
+                    views::View* root_view,
+                    apps::mojom::IntentPtr intent) override;
+  void OnClosing(sharesheet::SharesheetController* controller) override;
+  bool ShouldShowAction(const apps::mojom::IntentPtr& intent,
+                        bool contains_hosted_document) override;
+
+ private:
+  sharesheet::SharesheetController* controller_ = nullptr;
+};
+
+#endif  // CHROME_BROWSER_SHARESHEET_DRIVE_SHARE_ACTION_H_
diff --git a/chrome/browser/sharesheet/share_action.cc b/chrome/browser/sharesheet/share_action.cc
new file mode 100644
index 0000000..66b3715
--- /dev/null
+++ b/chrome/browser/sharesheet/share_action.cc
@@ -0,0 +1,14 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/sharesheet/share_action.h"
+
+namespace sharesheet {
+
+bool ShareAction::ShouldShowAction(const apps::mojom::IntentPtr& intent,
+                                   bool contains_hosted_document) {
+  return !contains_hosted_document;
+}
+
+}  // namespace sharesheet
diff --git a/chrome/browser/sharesheet/share_action.h b/chrome/browser/sharesheet/share_action.h
index 37119d41..c6397b0 100644
--- a/chrome/browser/sharesheet/share_action.h
+++ b/chrome/browser/sharesheet/share_action.h
@@ -45,6 +45,12 @@
   // shutdown when OnClosing is called, and not use |root_view| or |controller|
   // once the method completes as they will be destroyed.
   virtual void OnClosing(SharesheetController* controller) = 0;
+
+  // Return true if the action should be shown on the sharesheet. By default,
+  // the actions are only visible if the files don't contain a Google Drive
+  // hosted document.
+  virtual bool ShouldShowAction(const apps::mojom::IntentPtr& intent,
+                                bool contains_hosted_document);
 };
 
 }  // namespace sharesheet
diff --git a/chrome/browser/sharesheet/sharesheet_action_cache.cc b/chrome/browser/sharesheet/sharesheet_action_cache.cc
index f7dc43f..66bc2568 100644
--- a/chrome/browser/sharesheet/sharesheet_action_cache.cc
+++ b/chrome/browser/sharesheet/sharesheet_action_cache.cc
@@ -10,6 +10,7 @@
 
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/nearby_sharing/sharesheet/nearby_share_action.h"
+#include "chrome/browser/sharesheet/drive_share_action.h"
 #endif
 
 namespace sharesheet {
@@ -20,6 +21,7 @@
   if (base::FeatureList::IsEnabled(features::kNearbySharing)) {
     AddShareAction(std::make_unique<NearbyShareAction>());
   }
+  AddShareAction(std::make_unique<DriveShareAction>());
 #endif
 }
 
@@ -43,6 +45,17 @@
   return nullptr;
 }
 
+bool SharesheetActionCache::HasVisibleActions(
+    const apps::mojom::IntentPtr& intent,
+    bool contains_google_document) {
+  for (auto& action : share_actions_) {
+    if (action->ShouldShowAction(intent, contains_google_document)) {
+      return true;
+    }
+  }
+  return false;
+}
+
 void SharesheetActionCache::AddShareAction(
     std::unique_ptr<ShareAction> action) {
   DCHECK_EQ(action->GetActionIcon().size(), gfx::Size(kIconSize, kIconSize));
diff --git a/chrome/browser/sharesheet/sharesheet_action_cache.h b/chrome/browser/sharesheet/sharesheet_action_cache.h
index 40fea14..86cc741 100644
--- a/chrome/browser/sharesheet/sharesheet_action_cache.h
+++ b/chrome/browser/sharesheet/sharesheet_action_cache.h
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/strings/string16.h"
+#include "components/services/app_service/public/mojom/types.mojom.h"
 
 namespace sharesheet {
 
@@ -28,6 +29,9 @@
 
   const std::vector<std::unique_ptr<ShareAction>>& GetShareActions();
 
+  bool HasVisibleActions(const apps::mojom::IntentPtr& intent,
+                         bool contains_google_document);
+
  private:
   void AddShareAction(std::unique_ptr<ShareAction> action);
 
diff --git a/chrome/browser/sharesheet/sharesheet_service.cc b/chrome/browser/sharesheet/sharesheet_service.cc
index 8144942..e06f3d69 100644
--- a/chrome/browser/sharesheet/sharesheet_service.cc
+++ b/chrome/browser/sharesheet/sharesheet_service.cc
@@ -40,18 +40,25 @@
       std::make_unique<SharesheetServiceDelegate>(
           delegate_counter_++, std::move(bubble_anchor_view), this);
   ShowBubbleWithDelegate(std::move(sharesheet_service_delegate),
-                         std::move(intent));
+                         std::move(intent), /*contains_hosted_document=*/false);
 }
 
 void SharesheetService::ShowBubble(content::WebContents* web_contents,
                                    apps::mojom::IntentPtr intent) {
+  ShowBubble(web_contents, std::move(intent),
+             /*contains_hosted_document=*/false);
+}
+
+void SharesheetService::ShowBubble(content::WebContents* web_contents,
+                                   apps::mojom::IntentPtr intent,
+                                   bool contains_hosted_document) {
   DCHECK(intent->action == apps_util::kIntentActionSend ||
          intent->action == apps_util::kIntentActionSendMultiple);
   auto sharesheet_service_delegate =
       std::make_unique<SharesheetServiceDelegate>(delegate_counter_++,
                                                   web_contents, this);
   ShowBubbleWithDelegate(std::move(sharesheet_service_delegate),
-                         std::move(intent));
+                         std::move(intent), contains_hosted_document);
 }
 
 // Cleanup delegate when bubble closes.
@@ -117,12 +124,14 @@
   return nullptr;
 }
 
-bool SharesheetService::HasShareTargets(const apps::mojom::IntentPtr& intent) {
-  auto& actions = sharesheet_action_cache_->GetShareActions();
+bool SharesheetService::HasShareTargets(const apps::mojom::IntentPtr& intent,
+                                        bool contains_hosted_document) {
   std::vector<apps::IntentLaunchInfo> intent_launch_info =
       app_service_proxy_->GetAppsForIntent(intent);
 
-  return !actions.empty() || !intent_launch_info.empty();
+  return sharesheet_action_cache_->HasVisibleActions(
+             intent, contains_hosted_document) ||
+         (!contains_hosted_document && !intent_launch_info.empty());
 }
 
 Profile* SharesheetService::GetProfile() {
@@ -185,19 +194,23 @@
 
 void SharesheetService::ShowBubbleWithDelegate(
     std::unique_ptr<SharesheetServiceDelegate> delegate,
-    apps::mojom::IntentPtr intent) {
+    apps::mojom::IntentPtr intent,
+    bool contains_hosted_document) {
   std::vector<TargetInfo> targets;
   auto& actions = sharesheet_action_cache_->GetShareActions();
   auto iter = actions.begin();
   while (iter != actions.end()) {
-    targets.emplace_back(TargetType::kAction, (*iter)->GetActionIcon(),
-                         (*iter)->GetActionName(), (*iter)->GetActionName(),
-                         base::nullopt, base::nullopt);
+    if ((*iter)->ShouldShowAction(intent, contains_hosted_document)) {
+      targets.emplace_back(TargetType::kAction, (*iter)->GetActionIcon(),
+                           (*iter)->GetActionName(), (*iter)->GetActionName(),
+                           base::nullopt, base::nullopt);
+    }
     ++iter;
   }
 
   std::vector<apps::IntentLaunchInfo> intent_launch_info =
-      app_service_proxy_->GetAppsForIntent(intent);
+      contains_hosted_document ? std::vector<apps::IntentLaunchInfo>()
+                               : app_service_proxy_->GetAppsForIntent(intent);
   sharesheet::SharesheetMetrics::RecordSharesheetAppCount(
       intent_launch_info.size());
   LoadAppIcons(std::move(intent_launch_info), std::move(targets), 0,
diff --git a/chrome/browser/sharesheet/sharesheet_service.h b/chrome/browser/sharesheet/sharesheet_service.h
index cd56d51..a0e2f86 100644
--- a/chrome/browser/sharesheet/sharesheet_service.h
+++ b/chrome/browser/sharesheet/sharesheet_service.h
@@ -47,11 +47,15 @@
 
   // Displays the dialog (aka bubble) for sharing content (or files) with
   // other applications and targets. |intent| contains the list of the
-  // files/content to be shared.
+  // files/content to be shared. If the files to share contains Google
+  // Drive hosted document, only drive share action will be shown.
   void ShowBubble(views::View* bubble_anchor_view,
                   apps::mojom::IntentPtr intent);
   void ShowBubble(content::WebContents* web_contents,
                   apps::mojom::IntentPtr intent);
+  void ShowBubble(content::WebContents* web_contents,
+                  apps::mojom::IntentPtr intent,
+                  bool contains_hosted_document);
   void OnBubbleClosed(uint32_t id, const base::string16& active_action);
   void OnTargetSelected(uint32_t delegate_id,
                         const base::string16& target_name,
@@ -59,7 +63,11 @@
                         apps::mojom::IntentPtr intent,
                         views::View* share_action_view);
   SharesheetServiceDelegate* GetDelegate(uint32_t delegate_id);
-  bool HasShareTargets(const apps::mojom::IntentPtr& intent);
+
+  // If the files to share contains a Google Drive hosted document, only the
+  // drive share action will be shown.
+  bool HasShareTargets(const apps::mojom::IntentPtr& intent,
+                       bool contains_hosted_document);
   Profile* GetProfile();
 
  private:
@@ -83,7 +91,8 @@
 
   void ShowBubbleWithDelegate(
       std::unique_ptr<SharesheetServiceDelegate> delegate,
-      apps::mojom::IntentPtr intent);
+      apps::mojom::IntentPtr intent,
+      bool contains_hosted_document);
 
   uint32_t delegate_counter_ = 0;
   Profile* profile_;
diff --git a/chrome/browser/ssl/ssl_browsertest.cc b/chrome/browser/ssl/ssl_browsertest.cc
index 591966b6..fbccb35e 100644
--- a/chrome/browser/ssl/ssl_browsertest.cc
+++ b/chrome/browser/ssl/ssl_browsertest.cc
@@ -140,7 +140,6 @@
 #include "content/public/common/network_service_util.h"
 #include "content/public/common/page_state.h"
 #include "content/public/common/url_constants.h"
-#include "content/public/common/web_preferences.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_mock_cert_verifier.h"
@@ -188,6 +187,7 @@
 #include "services/network/public/mojom/network_service.mojom.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
 #include "third_party/blink/public/common/features.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "ui/base/l10n/l10n_util.h"
 
 #if defined(USE_NSS_CERTS)
@@ -283,8 +283,9 @@
     : public ChromeContentBrowserClient {
  public:
   ChromeContentBrowserClientForMixedContentTest() {}
-  void OverrideWebkitPrefs(content::RenderViewHost* rvh,
-                           content::WebPreferences* web_prefs) override {
+  void OverrideWebkitPrefs(
+      content::RenderViewHost* rvh,
+      blink::web_pref::WebPreferences* web_prefs) override {
     web_prefs->allow_running_insecure_content = allow_running_insecure_content_;
     web_prefs->strict_mixed_content_checking = strict_mixed_content_checking_;
     web_prefs->strictly_block_blockable_mixed_content =
@@ -3573,7 +3574,8 @@
                                  bool strict_mixed_content_checking,
                                  bool strictly_block_blockable_mixed_content) {
     WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
-    const content::WebPreferences& prefs = tab->GetOrCreateWebPreferences();
+    const blink::web_pref::WebPreferences& prefs =
+        tab->GetOrCreateWebPreferences();
     ASSERT_EQ(prefs.strictly_block_blockable_mixed_content,
               strictly_block_blockable_mixed_content);
     ASSERT_EQ(prefs.allow_running_insecure_content,
diff --git a/chrome/browser/sync/test/integration/single_client_extension_apps_sync_test.cc b/chrome/browser/sync/test/integration/single_client_extension_apps_sync_test.cc
index 1214913b..b2a87168 100644
--- a/chrome/browser/sync/test/integration/single_client_extension_apps_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_extension_apps_sync_test.cc
@@ -46,15 +46,6 @@
   DISALLOW_COPY_AND_ASSIGN(SingleClientExtensionAppsSyncTest);
 };
 
-// crbug.com/1001437
-#if defined(ADDRESS_SANITIZER)
-#define MAYBE_InstallSomePlatformApps DISABLED_InstallSomePlatformApps
-#define MAYBE_InstallSomeApps DISABLED_InstallSomeApps
-#else
-#define MAYBE_InstallSomePlatformApps InstallSomePlatformApps
-#define MAYBE_InstallSomeApps InstallSomeApps
-#endif
-
 IN_PROC_BROWSER_TEST_P(SingleClientExtensionAppsSyncTest, StartWithNoApps) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AllProfilesHaveSameApps());
@@ -64,7 +55,7 @@
                        StartWithSomeLegacyApps) {
   ASSERT_TRUE(SetupClients());
 
-  const int kNumApps = 5;
+  const int kNumApps = 2;
   for (int i = 0; i < kNumApps; ++i) {
     InstallHostedApp(GetProfile(0), i);
     InstallHostedApp(verifier(), i);
@@ -78,7 +69,7 @@
                        StartWithSomePlatformApps) {
   ASSERT_TRUE(SetupClients());
 
-  const int kNumApps = 5;
+  const int kNumApps = 2;
   for (int i = 0; i < kNumApps; ++i) {
     InstallPlatformApp(GetProfile(0), i);
     InstallPlatformApp(verifier(), i);
@@ -92,7 +83,7 @@
                        InstallSomeLegacyApps) {
   ASSERT_TRUE(SetupSync());
 
-  const int kNumApps = 5;
+  const int kNumApps = 2;
   for (int i = 0; i < kNumApps; ++i) {
     InstallHostedApp(GetProfile(0), i);
     InstallHostedApp(verifier(), i);
@@ -103,10 +94,10 @@
 }
 
 IN_PROC_BROWSER_TEST_P(SingleClientExtensionAppsSyncTest,
-                       MAYBE_InstallSomePlatformApps) {
+                       InstallSomePlatformApps) {
   ASSERT_TRUE(SetupSync());
 
-  const int kNumApps = 5;
+  const int kNumApps = 2;
   for (int i = 0; i < kNumApps; ++i) {
     InstallPlatformApp(GetProfile(0), i);
     InstallPlatformApp(verifier(), i);
@@ -116,19 +107,21 @@
   ASSERT_TRUE(AllProfilesHaveSameApps());
 }
 
-IN_PROC_BROWSER_TEST_P(SingleClientExtensionAppsSyncTest,
-                       MAYBE_InstallSomeApps) {
+IN_PROC_BROWSER_TEST_P(SingleClientExtensionAppsSyncTest, InstallSomeApps) {
   ASSERT_TRUE(SetupSync());
 
+  // TODO(crbug.com/1124986): Determine if these values
+  // can be raised without introducing flakiness.
+  const int kNumApps = 1;
+  const int kNumPlatformApps = 1;
+
   int i = 0;
 
-  const int kNumApps = 5;
   for (int j = 0; j < kNumApps; ++i, ++j) {
     InstallHostedApp(GetProfile(0), i);
     InstallHostedApp(verifier(), i);
   }
 
-  const int kNumPlatformApps = 5;
   for (int j = 0; j < kNumPlatformApps; ++i, ++j) {
     InstallPlatformApp(GetProfile(0), i);
     InstallPlatformApp(verifier(), i);
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index e697612..2bf4865f 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -999,6 +999,10 @@
       "global_media_controls/overlay_media_notifications_manager.h",
       "global_media_controls/overlay_media_notifications_manager_impl.cc",
       "global_media_controls/overlay_media_notifications_manager_impl.h",
+      "global_media_controls/presentation_request_notification_item.cc",
+      "global_media_controls/presentation_request_notification_item.h",
+      "global_media_controls/presentation_request_notification_provider.cc",
+      "global_media_controls/presentation_request_notification_provider.h",
       "hats/hats_helper.cc",
       "hats/hats_helper.h",
       "hats/hats_service.cc",
diff --git a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenu.java b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenu.java
index 29becf7..12a4e3b5 100644
--- a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenu.java
+++ b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenu.java
@@ -165,15 +165,13 @@
      *         determine the number of dividers that appear in the menu.
      * @param circleHighlightItem   Whether the highlighted item should use a circle highlight or
      *                              not.
-     * @param showFromBottom        Whether the appearance animation should run from the bottom up.
      * @param customViewBinders     See {@link AppMenuPropertiesDelegate#getCustomViewBinders()}.
      */
     void show(Context context, final View anchorView, boolean isByPermanentButton,
             int screenRotation, Rect visibleDisplayFrame, int screenHeight,
             @IdRes int footerResourceId, @IdRes int headerResourceId,
             @IdRes int groupDividerResourceId, Integer highlightedItemId,
-            boolean circleHighlightItem, boolean showFromBottom,
-            @Nullable List<CustomViewBinder> customViewBinders) {
+            boolean circleHighlightItem, @Nullable List<CustomViewBinder> customViewBinders) {
         mPopup = new PopupWindow(context);
         mPopup.setFocusable(true);
         mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
@@ -210,10 +208,7 @@
         // an incorrectly drawn background.
         mPopup.setBackgroundDrawable(ApiCompatibilityUtils.getDrawable(
                 context.getResources(), R.drawable.popup_bg_tinted));
-        if (!isByPermanentButton) {
-            mPopup.setAnimationStyle(
-                    showFromBottom ? R.style.OverflowMenuAnimBottom : R.style.OverflowMenuAnim);
-        }
+        if (!isByPermanentButton) mPopup.setAnimationStyle(R.style.OverflowMenuAnim);
 
         // Turn off window animations for low end devices.
         if (SysUtils.isLowEndDevice()) mPopup.setAnimationStyle(0);
@@ -274,7 +269,7 @@
         int[] popupPosition = getPopupPosition(mTempLocation, mIsByPermanentButton,
                 mNegativeSoftwareVerticalOffset, mNegativeVerticalOffsetNotTopAnchored,
                 mCurrentScreenRotation, visibleDisplayFrame, sizingPadding, anchorView, popupWidth,
-                popupHeight, showFromBottom, anchorView.getRootView().getLayoutDirection());
+                popupHeight, anchorView.getRootView().getLayoutDirection());
 
         mPopup.setContentView(contentView);
         mPopup.showAtLocation(
@@ -310,7 +305,7 @@
     static int[] getPopupPosition(int[] tempLocation, boolean isByPermanentButton,
             int negativeSoftwareVerticalOffset, int negativeVerticalOffsetNotTopAnchored,
             int screenRotation, Rect appRect, Rect padding, View anchorView, int popupWidth,
-            int popupHeight, boolean isAnchorAtBottom, int viewLayoutDirection) {
+            int popupHeight, int viewLayoutDirection) {
         anchorView.getLocationInWindow(tempLocation);
         int anchorViewX = tempLocation[0];
         int anchorViewY = tempLocation[1];
@@ -340,19 +335,6 @@
             offsets[1] = -padding.bottom;
         } else {
             offsets[1] = -negativeSoftwareVerticalOffset;
-
-            // If the anchor is at the bottom of the screen, align the popup with the bottom of the
-            // anchor. The anchor may not be fully visible, so
-            // (appRect.bottom - anchorViewLocationOnScreenY) is used to determine the visible
-            // bottom edge of the anchor view.
-            if (isAnchorAtBottom) {
-                anchorView.getLocationOnScreen(tempLocation);
-                int anchorViewLocationOnScreenY = tempLocation[1];
-                offsets[1] += appRect.bottom - anchorViewLocationOnScreenY - popupHeight;
-                offsets[1] -= negativeVerticalOffsetNotTopAnchored;
-                offsets[1] += padding.bottom;
-            }
-
             if (viewLayoutDirection != View.LAYOUT_DIRECTION_RTL) {
                 offsets[0] = anchorView.getWidth() - popupWidth;
             }
diff --git a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuButtonHelperImpl.java b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuButtonHelperImpl.java
index 7e1443c..caaa109 100644
--- a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuButtonHelperImpl.java
+++ b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuButtonHelperImpl.java
@@ -26,7 +26,6 @@
     private final AppMenuHandlerImpl mMenuHandler;
     private Runnable mOnAppMenuShownListener;
     private boolean mIsTouchEventsBeingProcessed;
-    private boolean mMenuShowsFromBottom;
     private Runnable mOnClickRunnable;
 
     /**
@@ -39,11 +38,6 @@
     // AppMenuButtonHelper implementation.
 
     @Override
-    public void setMenuShowsFromBottom(boolean showsFromBottom) {
-        mMenuShowsFromBottom = showsFromBottom;
-    }
-
-    @Override
     public void setOnAppMenuShownListener(Runnable onAppMenuShownListener) {
         mOnAppMenuShownListener = onAppMenuShownListener;
     }
@@ -125,8 +119,7 @@
      * @return Whether or not if the app menu is successfully shown.
      */
     private boolean showAppMenu(View view, boolean startDragging) {
-        if (!mMenuHandler.isAppMenuShowing()
-                && mMenuHandler.showAppMenu(view, startDragging, mMenuShowsFromBottom)) {
+        if (!mMenuHandler.isAppMenuShowing() && mMenuHandler.showAppMenu(view, startDragging)) {
             // Initial start dragging can be canceled in case if it was just single tap.
             // So we only record non-dragging here, and will deal with those dragging cases in
             // AppMenuDragHelper class.
diff --git a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuCoordinatorImpl.java b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuCoordinatorImpl.java
index 50d32ed..df38b113 100644
--- a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuCoordinatorImpl.java
+++ b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuCoordinatorImpl.java
@@ -86,8 +86,7 @@
                 ? sHasPermanentMenuKeyForTesting.booleanValue()
                 : ViewConfiguration.get(mContext).hasPermanentMenuKey();
         mAppMenuHandler.showAppMenu(
-                hasPermanentMenuKey ? null : mButtonDelegate.getMenuButtonView(), false,
-                mButtonDelegate.isMenuFromBottom());
+                hasPermanentMenuKey ? null : mButtonDelegate.getMenuButtonView(), false);
     }
 
     @Override
diff --git a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuHandlerImpl.java b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuHandlerImpl.java
index c8b36b77..63913bb2 100644
--- a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuHandlerImpl.java
+++ b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuHandlerImpl.java
@@ -136,14 +136,13 @@
      *                      dragging down on the menu button, this should be true. Note that if
      *                      anchorView is null, this must be false since we no longer support
      *                      hardware menu button dragging.
-     * @param showFromBottom Whether the menu should be shown from the bottom up.
      * @return              True, if the menu is shown, false, if menu is not shown, example
      *                      reasons: the menu is not yet available to be shown, or the menu is
      *                      already showing.
      */
     // TODO(crbug.com/635567): Fix this properly.
     @SuppressLint("ResourceType")
-    boolean showAppMenu(View anchorView, boolean startDragging, boolean showFromBottom) {
+    boolean showAppMenu(View anchorView, boolean startDragging) {
         if (!shouldShowAppMenu() || isAppMenuShowing()) return false;
 
         TextBubble.dismissBubbles();
@@ -223,7 +222,7 @@
         }
         mAppMenu.show(wrapper, anchorView, isByPermanentButton, rotation, appRect, pt.y,
                 footerResourceId, headerResourceId, mDelegate.getGroupDividerId(), mHighlightMenuId,
-                mCircleHighlight, showFromBottom, mDelegate.getCustomViewBinders());
+                mCircleHighlight, mDelegate.getCustomViewBinders());
         mAppMenuDragHelper.onShow(startDragging);
         clearMenuHighlight();
         RecordUserAction.record("MobileMenuShow");
diff --git a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuPopupPositionTest.java b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuPopupPositionTest.java
index 6eeedcc..f92980e 100644
--- a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuPopupPositionTest.java
+++ b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuPopupPositionTest.java
@@ -68,20 +68,18 @@
         int expectedX = (mAppWidth - mPopupWidth) / 2;
         int expectedY = mAnchorY - mBgPadding;
 
-        int[] results =
-                getPopupPosition(true, Surface.ROTATION_0, false, View.LAYOUT_DIRECTION_LTR);
+        int[] results = getPopupPosition(true, Surface.ROTATION_0, View.LAYOUT_DIRECTION_LTR);
         Assert.assertEquals("Incorrect popup x", expectedX, results[0]);
         Assert.assertEquals("Incorrect popup y", expectedY, results[1]);
 
-        results = getPopupPosition(true, Surface.ROTATION_180, false, View.LAYOUT_DIRECTION_LTR);
+        results = getPopupPosition(true, Surface.ROTATION_180, View.LAYOUT_DIRECTION_LTR);
         Assert.assertEquals("Incorrect popup x for rotation 180", expectedX, results[0]);
         Assert.assertEquals("Incorrect popup y for rotation 180", expectedY, results[1]);
     }
 
     @Test
     public void testPermanentButton_Landscape() {
-        int[] results =
-                getPopupPosition(true, Surface.ROTATION_90, false, View.LAYOUT_DIRECTION_LTR);
+        int[] results = getPopupPosition(true, Surface.ROTATION_90, View.LAYOUT_DIRECTION_LTR);
 
         // Popup should be positioned toward the right edge of the screen, anchored near the anchor
         // view.
@@ -93,15 +91,14 @@
         // Popup should be positioned toward the left edge of the screen, anchored near the anchor
         // view.
         expectedX = 0;
-        results = getPopupPosition(true, Surface.ROTATION_270, false, View.LAYOUT_DIRECTION_LTR);
+        results = getPopupPosition(true, Surface.ROTATION_270, View.LAYOUT_DIRECTION_LTR);
         Assert.assertEquals("Incorrect popup x for rotation 180", expectedX, results[0]);
         Assert.assertEquals("Incorrect popup y for rotation 180", expectedY, results[1]);
     }
 
     @Test
     public void testTopButton_LTR() {
-        int[] results =
-                getPopupPosition(false, Surface.ROTATION_0, false, View.LAYOUT_DIRECTION_LTR);
+        int[] results = getPopupPosition(false, Surface.ROTATION_0, View.LAYOUT_DIRECTION_LTR);
 
         // The top right edge of the popup should be aligned with the top right edge of the button.
         int expectedX = mAnchorX + mAnchorWidth - mPopupWidth;
@@ -112,8 +109,7 @@
 
     @Test
     public void testTopButton_RTL() {
-        int[] results =
-                getPopupPosition(false, Surface.ROTATION_0, false, View.LAYOUT_DIRECTION_RTL);
+        int[] results = getPopupPosition(false, Surface.ROTATION_0, View.LAYOUT_DIRECTION_RTL);
 
         // The top left edge of the popup should be aligned with the top left edge of the button.
         int expectedX = mAnchorX;
@@ -122,39 +118,10 @@
         Assert.assertEquals("Incorrect popup y", expectedY, results[1]);
     }
 
-    @Test
-    public void testBottomButton_LTR() {
-        int[] results =
-                getPopupPosition(false, Surface.ROTATION_0, true, View.LAYOUT_DIRECTION_LTR);
-
-        // The bottom popup menu positioning assumes the anchor is at the bottom of the screen. It
-        // aligns the popup based on the bottom of the screen plus offsets.
-        int expectedX = mAnchorX + mAnchorWidth - mPopupWidth;
-        int expectedY = mAppHeight - mPopupHeight - mNegativeSoftwareVerticalOffset
-                - mNegativeSoftwareVerticalOffsetNotTopAnchored + mBgPadding;
-        Assert.assertEquals("Incorrect popup x", expectedX, results[0]);
-        Assert.assertEquals("Incorrect popup y", expectedY, results[1]);
-    }
-
-    @Test
-    public void testBottomButton_RTL() {
-        int[] results =
-                getPopupPosition(false, Surface.ROTATION_0, true, View.LAYOUT_DIRECTION_RTL);
-
-        // The bottom popup menu positioning assumes the anchor is at the bottom of the screen. It
-        // aligns the popup based on the bottom of the screen plus offsets.
-        int expectedX = mAnchorX;
-        int expectedY = mAppHeight - mPopupHeight - mNegativeSoftwareVerticalOffset
-                - mNegativeSoftwareVerticalOffsetNotTopAnchored + mBgPadding;
-        Assert.assertEquals("Incorrect popup x", expectedX, results[0]);
-        Assert.assertEquals("Incorrect popup y", expectedY, results[1]);
-    }
-
-    private int[] getPopupPosition(boolean isByPermanentButton, int rotation,
-            boolean isAnchorAtBottom, int layoutDirection) {
+    private int[] getPopupPosition(boolean isByPermanentButton, int rotation, int layoutDirection) {
         return AppMenu.getPopupPosition(mTempLocation, isByPermanentButton,
                 mNegativeSoftwareVerticalOffset, mNegativeSoftwareVerticalOffsetNotTopAnchored,
                 rotation, mAppRect, mBgPaddingRect, mAnchorView, mPopupWidth, mPopupHeight,
-                isAnchorAtBottom, layoutDirection);
+                layoutDirection);
     }
 }
diff --git a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuTest.java b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuTest.java
index f2676f8..4f3a254 100644
--- a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuTest.java
+++ b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuTest.java
@@ -148,30 +148,6 @@
 
     @Test
     @MediumTest
-    public void testShowAppMenu_AnchorBottom() throws TimeoutException {
-        AppMenuCoordinatorImpl.setHasPermanentMenuKeyForTesting(false);
-        mTestMenuButtonDelegate.useBottomAnchor = true;
-        showMenuAndAssert();
-
-        View bottomAnchor = getActivity().findViewById(R.id.bottom_button);
-        Rect viewRect = getViewLocationRect(bottomAnchor);
-        Rect popupRect = getPopupLocationRect();
-
-        // Check that bottom right corner of app menu aligns with bottom right corner of the anchor.
-        int alignmentSlop = viewRect.bottom - viewRect.top;
-        Assert.assertEquals("Popup should be overlap bottom anchor. Anchor rect: " + viewRect
-                        + ", popup rect: " + popupRect,
-                viewRect.bottom, popupRect.bottom, alignmentSlop);
-        Assert.assertTrue("Popup should overlap bottom anchor. Anchor rect: " + viewRect
-                        + ", popup rect: " + popupRect,
-                viewRect.bottom >= popupRect.bottom);
-        Assert.assertEquals("Popup should be aligned with right of anchor. Anchor rect: " + viewRect
-                        + ", popup rect: " + popupRect,
-                viewRect.right, popupRect.right);
-    }
-
-    @Test
-    @MediumTest
     public void testShowAppMenu_PermanentButton() throws TimeoutException {
         AppMenuCoordinatorImpl.setHasPermanentMenuKeyForTesting(true);
         showMenuAndAssert();
@@ -792,18 +768,10 @@
     }
 
     private class TestMenuButtonDelegate implements MenuButtonDelegate {
-        public boolean useBottomAnchor;
-
         @Nullable
         @Override
         public View getMenuButtonView() {
-            return getActivity().findViewById(
-                    useBottomAnchor ? R.id.bottom_button : R.id.top_button);
-        }
-
-        @Override
-        public boolean isMenuFromBottom() {
-            return useBottomAnchor;
+            return getActivity().findViewById(R.id.top_button);
         }
     }
 
diff --git a/chrome/browser/ui/android/appmenu/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuButtonHelper.java b/chrome/browser/ui/android/appmenu/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuButtonHelper.java
index c54e172d..6204d20 100644
--- a/chrome/browser/ui/android/appmenu/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuButtonHelper.java
+++ b/chrome/browser/ui/android/appmenu/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuButtonHelper.java
@@ -14,11 +14,6 @@
  */
 public interface AppMenuButtonHelper extends View.OnTouchListener {
     /**
-     * @param showsFromBottom Whether the menu shows from the bottom by default.
-     */
-    void setMenuShowsFromBottom(boolean showsFromBottom);
-
-    /**
      * @return Whether app menu is active. That is, AppMenu is showing or menu button is consuming
      *         touch events to prepare AppMenu showing.
      */
diff --git a/chrome/browser/ui/android/appmenu/java/src/org/chromium/chrome/browser/ui/appmenu/MenuButtonDelegate.java b/chrome/browser/ui/android/appmenu/java/src/org/chromium/chrome/browser/ui/appmenu/MenuButtonDelegate.java
index 2083e958..0471bbe 100644
--- a/chrome/browser/ui/android/appmenu/java/src/org/chromium/chrome/browser/ui/appmenu/MenuButtonDelegate.java
+++ b/chrome/browser/ui/android/appmenu/java/src/org/chromium/chrome/browser/ui/appmenu/MenuButtonDelegate.java
@@ -15,9 +15,4 @@
      */
     @Nullable
     View getMenuButtonView();
-
-    /**
-     * @return Whether the menu is shown from the bottom of the screen.
-     */
-    boolean isMenuFromBottom();
-}
\ No newline at end of file
+}
diff --git a/chrome/browser/ui/android/appmenu/test/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuTestSupport.java b/chrome/browser/ui/android/appmenu/test/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuTestSupport.java
index f6e1c3d..29dac94 100644
--- a/chrome/browser/ui/android/appmenu/test/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuTestSupport.java
+++ b/chrome/browser/ui/android/appmenu/test/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuTestSupport.java
@@ -69,16 +69,15 @@
      *         dragging down on the menu button, this should be true. Note that if
      *         anchorView is null, this must be false since we no longer support
      *         hardware menu button dragging.
-     * @param showFromBottom Whether the menu should be shown from the bottom up.
      * @return True, if the menu is shown, false, if menu is not shown, example
      *         reasons: the menu is not yet available to be shown, or the menu is
      *         already showing.
      */
-    public static boolean showAppMenu(AppMenuCoordinator coordinator, View anchorView,
-            boolean startDragging, boolean showFromBottom) {
+    public static boolean showAppMenu(
+            AppMenuCoordinator coordinator, View anchorView, boolean startDragging) {
         return ((AppMenuCoordinatorImpl) coordinator)
                 .getAppMenuHandlerImplForTesting()
-                .showAppMenu(anchorView, startDragging, showFromBottom);
+                .showAppMenu(anchorView, startDragging);
     }
 
     /**
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index 6294de4..fc0a52b 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -2946,9 +2946,6 @@
       <message name="IDS_ACCESSIBILITY_TOOLBAR_BTN_MENU_OS_VERSION_UNSUPPORTED" desc="Content description for the menu button when it is covered by the warning icon that is displayed when the current Android OS version is unsupported.">
         Chrome can’t update. More options
       </message>
-      <message name="IDS_ACCESSIBILITY_TOOLBAR_BTN_SEARCH_ACCELERATOR" desc="Content description for the search accelerator button">
-        Search
-      </message>
       <message name="IDS_ACCESSIBILITY_TOOLBAR_BTN_SITE_INFO" desc="Content description for the page icon that gives more site information when clicked. The icon can be a magnifier for search result pages, or other icons representing the page state.">
         Site information
       </message>
@@ -2967,12 +2964,6 @@
       <message name="IDS_ACCESSIBILITY_TOOLBAR_BTN_NEW_INCOGNITO_TAB" desc="Content description for the new incognito tab button.">
         New incognito tab
       </message>
-      <message name="IDS_ACCESSIBILITY_TOOLBAR_BTN_CLOSE_ALL_TABS" desc="Content description for the close all tabs button.">
-        Close all tabs
-      </message>
-      <message name="IDS_ACCESSIBILITY_TOOLBAR_BTN_CLOSE_ALL_INCOGNITO_TABS" desc="Content description for the close all incognito tabs button.">
-        Close all incognito tabs
-      </message>
       <message name="IDS_ACCESSIBILITY_INCOGNITO_BADGE" desc="Content description for the badge indicating that the user is in Incognito mode.">
         Incognito mode
       </message>
diff --git a/chrome/browser/ui/app_list/search/launcher_search/launcher_search_provider.cc b/chrome/browser/ui/app_list/search/launcher_search/launcher_search_provider.cc
index 52355b9..d212ce1 100644
--- a/chrome/browser/ui/app_list/search/launcher_search/launcher_search_provider.cc
+++ b/chrome/browser/ui/app_list/search/launcher_search/launcher_search_provider.cc
@@ -10,6 +10,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/chromeos/launcher_search_provider/launcher_search_provider_service.h"
+#include "chromeos/components/string_matching/fuzzy_tokenized_string_match.h"
 
 using chromeos::launcher_search_provider::Service;
 
@@ -17,9 +18,33 @@
 
 namespace {
 
+using TokenizedString = chromeos::string_matching::TokenizedString;
+using FuzzyTokenizedStringMatch =
+    chromeos::string_matching::FuzzyTokenizedStringMatch;
+
 constexpr int kLauncherSearchProviderQueryDelayInMs = 100;
 constexpr int kLauncherSearchProviderMaxResults = 6;
 
+constexpr double kDefaultRelevance = 0.5;
+
+// Parameters for FuzzyTokenizedStringMatch. Note that the underlying file
+// search uses an exact substring match to retrieve file results, so using edit
+// distance here doesn't provide any benefit.
+constexpr bool kUsePrefixOnly = false;
+constexpr bool kUseWeightedRatio = true;
+constexpr bool kUseEditDistance = false;
+constexpr double kRelevanceThreshold = 0.0;
+constexpr double kPartialMatchPenaltyRate = 0.9;
+
+double FuzzyMatchRelevance(const TokenizedString& title,
+                           const TokenizedString& query) {
+  FuzzyTokenizedStringMatch match;
+  match.IsRelevant(query, title, kRelevanceThreshold, kUsePrefixOnly,
+                   kUseWeightedRatio, kUseEditDistance,
+                   kPartialMatchPenaltyRate);
+  return match.relevance();
+}
+
 }  // namespace
 
 LauncherSearchProvider::LauncherSearchProvider(Profile* profile)
@@ -49,6 +74,7 @@
   // Clear previously added search results.
   ClearResults();
 
+  last_tokenized_query_.emplace(query, TokenizedString::Mode::kWords);
   DelayQuery(base::Bind(&LauncherSearchProvider::StartInternal,
                         weak_ptr_factory_.GetWeakPtr(), query));
 }
@@ -64,12 +90,25 @@
 
   // Add this extension's results (erasing any existing results).
   extension_results_[extension_id] = std::move(results);
+  DCHECK_LE(extension_results_.size(), 1);
 
   // Update results with other extension results.
   SearchProvider::Results new_results;
   for (const auto& item : extension_results_) {
-    for (const auto& result : item.second)
-      new_results.emplace_back(result->Duplicate());
+    for (const auto& result : item.second) {
+      std::unique_ptr<LauncherSearchResult> new_result = result->Duplicate();
+
+      double relevance = kDefaultRelevance;
+      if (last_tokenized_query_) {
+        const TokenizedString tokenized_title(new_result->title(),
+                                              TokenizedString::Mode::kWords);
+        relevance =
+            FuzzyMatchRelevance(tokenized_title, last_tokenized_query_.value());
+      }
+      new_result->set_relevance(relevance);
+
+      new_results.push_back(std::move(new_result));
+    }
   }
   SwapResults(&new_results);
 }
diff --git a/chrome/browser/ui/app_list/search/launcher_search/launcher_search_provider.h b/chrome/browser/ui/app_list/search/launcher_search/launcher_search_provider.h
index 12896468..fe4f4a1d 100644
--- a/chrome/browser/ui/app_list/search/launcher_search/launcher_search_provider.h
+++ b/chrome/browser/ui/app_list/search/launcher_search/launcher_search_provider.h
@@ -16,6 +16,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/search/launcher_search/launcher_search_result.h"
 #include "chrome/browser/ui/app_list/search/search_provider.h"
+#include "chromeos/components/string_matching/tokenized_string.h"
 #include "extensions/common/extension_id.h"
 
 namespace app_list {
@@ -54,6 +55,9 @@
 
   base::TimeTicks query_start_time_;
 
+  base::Optional<chromeos::string_matching::TokenizedString>
+      last_tokenized_query_;
+
   // The reference to profile to get LauncherSearchProvider service.
   Profile* profile_;
 
diff --git a/chrome/browser/ui/ash/media_notification_provider_impl.h b/chrome/browser/ui/ash/media_notification_provider_impl.h
index a1ee801..83fe6390 100644
--- a/chrome/browser/ui/ash/media_notification_provider_impl.h
+++ b/chrome/browser/ui/ash/media_notification_provider_impl.h
@@ -31,7 +31,8 @@
 
   // MediaNotificationServiceObserver implementations.
   void OnNotificationListChanged() override;
-  void OnMediaDialogOpenedOrClosed() override {}
+  void OnMediaDialogOpened() override {}
+  void OnMediaDialogClosed() override {}
 
   // SessionManagerobserver implementation.
   void OnUserProfileLoaded(const AccountId& account_id) override;
diff --git a/chrome/browser/ui/ash/tablet_mode_page_behavior_browsertest.cc b/chrome/browser/ui/ash/tablet_mode_page_behavior_browsertest.cc
index 78d78ac..65534b28 100644
--- a/chrome/browser/ui/ash/tablet_mode_page_behavior_browsertest.cc
+++ b/chrome/browser/ui/ash/tablet_mode_page_behavior_browsertest.cc
@@ -17,9 +17,9 @@
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/render_widget_host.h"
 #include "content/public/browser/web_contents.h"
-#include "content/public/common/web_preferences.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 
 namespace {
 
@@ -53,14 +53,14 @@
     return browser->tab_strip_model()->GetActiveWebContents();
   }
 
-  content::WebPreferences GetWebKitPreferences(
+  blink::web_pref::WebPreferences GetWebKitPreferences(
       content::WebContents* web_contents) const {
     return web_contents->GetOrCreateWebPreferences();
   }
 
   void ValidateWebPrefs(content::WebContents* web_contents,
                         bool tablet_mode_enabled) const {
-    const content::WebPreferences web_prefs =
+    const blink::web_pref::WebPreferences web_prefs =
         GetWebKitPreferences(web_contents);
     if (tablet_mode_enabled) {
       EXPECT_TRUE(web_prefs.double_tap_to_zoom_enabled);
diff --git a/chrome/browser/ui/extensions/hosted_app_browser_controller.cc b/chrome/browser/ui/extensions/hosted_app_browser_controller.cc
index 1991d42..c29196be 100644
--- a/chrome/browser/ui/extensions/hosted_app_browser_controller.cc
+++ b/chrome/browser/ui/extensions/hosted_app_browser_controller.cc
@@ -29,12 +29,12 @@
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/url_constants.h"
-#include "content/public/common/web_preferences.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_system.h"
 #include "extensions/browser/management_policy.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "third_party/blink/public/mojom/manifest/display_mode.mojom.h"
 #include "third_party/blink/public/mojom/renderer_preferences.mojom.h"
 #include "ui/gfx/image/image_skia.h"
diff --git a/chrome/browser/ui/global_media_controls/cast_media_notification_provider.cc b/chrome/browser/ui/global_media_controls/cast_media_notification_provider.cc
index 42158ea8..73e41e4 100644
--- a/chrome/browser/ui/global_media_controls/cast_media_notification_provider.cc
+++ b/chrome/browser/ui/global_media_controls/cast_media_notification_provider.cc
@@ -7,6 +7,7 @@
 #include "chrome/browser/media/router/media_router.h"
 #include "chrome/browser/media/router/media_router_factory.h"
 #include "chrome/browser/profiles/profile.h"
+#include "components/media_message_center/media_notification_controller.h"
 #include "components/media_router/common/providers/cast/cast_media_source.h"
 
 namespace {
diff --git a/chrome/browser/ui/global_media_controls/cast_media_notification_provider.h b/chrome/browser/ui/global_media_controls/cast_media_notification_provider.h
index 918867e..afcf769 100644
--- a/chrome/browser/ui/global_media_controls/cast_media_notification_provider.h
+++ b/chrome/browser/ui/global_media_controls/cast_media_notification_provider.h
@@ -20,10 +20,6 @@
 class MediaNotificationController;
 }  // namespace media_message_center
 
-namespace media_router {
-class MediaRouter;
-}  // namespace media_router
-
 // Manages media notifications shown in the Global Media Controls dialog for
 // active Cast sessions.
 class CastMediaNotificationProvider : public media_router::MediaRoutesObserver {
diff --git a/chrome/browser/ui/global_media_controls/media_notification_service.cc b/chrome/browser/ui/global_media_controls/media_notification_service.cc
index 4475971..c886f7da 100644
--- a/chrome/browser/ui/global_media_controls/media_notification_service.cc
+++ b/chrome/browser/ui/global_media_controls/media_notification_service.cc
@@ -9,6 +9,7 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/util/ranges/algorithm.h"
 #include "chrome/browser/media/router/media_router_feature.h"
+#include "chrome/browser/media/router/presentation/start_presentation_context.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
@@ -312,6 +313,10 @@
                 &MediaNotificationService::OnCastNotificationsChanged,
                 base::Unretained(this)));
   }
+  if (media_router::GlobalMediaControlsCastStartStopEnabled()) {
+    presentation_request_notification_provider_ =
+        std::make_unique<PresentationRequestNotificationProvider>(this);
+  }
 
   // Connect to the controller manager so we can create media controllers for
   // media sessions.
@@ -544,22 +549,17 @@
     return;
   }
 
-  auto it = sessions_.find(id);
-  if (it == sessions_.end()) {
-    if (!cast_notification_provider_)
-      return;
-
-    base::WeakPtr<media_message_center::MediaNotificationItem> cast_item =
-        cast_notification_provider_->GetNotificationItem(id);
-    if (cast_item)
-      cast_item->Dismiss();
-
+  Session* session = GetSession(id);
+  if (!session) {
+    auto item = GetNonSessionNotificationItem(id);
+    if (item)
+      item->Dismiss();
     return;
   }
 
-  it->second.set_dismiss_reason(
+  session->set_dismiss_reason(
       GlobalMediaControlsDismissReason::kUserDismissedNotification);
-  it->second.item()->Dismiss();
+  session->item()->Dismiss();
 }
 
 void MediaNotificationService::OnContainerDestroyed(const std::string& id) {
@@ -606,9 +606,11 @@
 }
 
 void MediaNotificationService::Shutdown() {
-  // |cast_notification_provider_| depends on MediaRouter, which is another
-  // keyed service.
+  // |cast_notification_provider_| and
+  // |presentation_request_notification_provider_| depend on MediaRouter, which
+  // is another keyed service.
   cast_notification_provider_.reset();
+  presentation_request_notification_provider_.reset();
 }
 
 void MediaNotificationService::OnOverlayNotificationClosed(
@@ -660,8 +662,13 @@
   DCHECK(!delegate || !dialog_delegate_);
   dialog_delegate_ = delegate;
 
-  for (auto& observer : observers_)
-    observer.OnMediaDialogOpenedOrClosed();
+  if (dialog_delegate_) {
+    for (auto& observer : observers_)
+      observer.OnMediaDialogOpened();
+  } else {
+    for (auto& observer : observers_)
+      observer.OnMediaDialogClosed();
+  }
 
   if (!dialog_delegate_)
     return;
@@ -689,6 +696,7 @@
 
   media_message_center::RecordConcurrentNotificationCount(
       active_controllable_session_ids_.size());
+
   if (cast_notification_provider_) {
     media_message_center::RecordConcurrentCastNotificationCount(
         cast_notification_provider_->GetItemCount());
@@ -769,6 +777,14 @@
       std::move(callback));
 }
 
+void MediaNotificationService::OnStartPresentationContextCreated(
+    std::unique_ptr<media_router::StartPresentationContext> context) {
+  if (presentation_request_notification_provider_) {
+    presentation_request_notification_provider_
+        ->OnStartPresentationContextCreated(std::move(context));
+  }
+}
+
 void MediaNotificationService::set_device_provider_for_testing(
     std::unique_ptr<MediaNotificationDeviceProvider> device_provider) {
   device_provider_ = std::move(device_provider);
@@ -792,11 +808,32 @@
 
 base::WeakPtr<media_message_center::MediaNotificationItem>
 MediaNotificationService::GetNotificationItem(const std::string& id) {
+  Session* session = GetSession(id);
+  if (session)
+    return session->item()->GetWeakPtr();
+  return GetNonSessionNotificationItem(id);
+}
+
+MediaNotificationService::Session* MediaNotificationService::GetSession(
+    const std::string& id) {
   auto it = sessions_.find(id);
-  if (it != sessions_.end()) {
-    return it->second.item()->GetWeakPtr();
-  } else if (cast_notification_provider_) {
-    return cast_notification_provider_->GetNotificationItem(id);
+  return it == sessions_.end() ? nullptr : &it->second;
+}
+
+base::WeakPtr<media_message_center::MediaNotificationItem>
+MediaNotificationService::GetNonSessionNotificationItem(const std::string& id) {
+  if (cast_notification_provider_) {
+    auto item = cast_notification_provider_->GetNotificationItem(id);
+    if (item)
+      return item;
   }
+
+  if (presentation_request_notification_provider_) {
+    auto item =
+        presentation_request_notification_provider_->GetNotificationItem(id);
+    if (item)
+      return item;
+  }
+
   return nullptr;
 }
diff --git a/chrome/browser/ui/global_media_controls/media_notification_service.h b/chrome/browser/ui/global_media_controls/media_notification_service.h
index 4e27ccf..346fd10 100644
--- a/chrome/browser/ui/global_media_controls/media_notification_service.h
+++ b/chrome/browser/ui/global_media_controls/media_notification_service.h
@@ -19,6 +19,7 @@
 #include "chrome/browser/ui/global_media_controls/media_notification_container_observer.h"
 #include "chrome/browser/ui/global_media_controls/media_notification_device_provider.h"
 #include "chrome/browser/ui/global_media_controls/overlay_media_notifications_manager_impl.h"
+#include "chrome/browser/ui/global_media_controls/presentation_request_notification_provider.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/media_message_center/media_notification_controller.h"
 #include "content/public/browser/web_contents_observer.h"
@@ -30,6 +31,7 @@
 #include "services/metrics/public/cpp/ukm_source_id.h"
 
 namespace content {
+class StartPresentationContext;
 class WebContents;
 }  // namespace content
 
@@ -123,6 +125,9 @@
       const std::string& id,
       base::RepeatingCallback<void(bool)> callback);
 
+  void OnStartPresentationContextCreated(
+      std::unique_ptr<media_router::StartPresentationContext> context);
+
   void set_device_provider_for_testing(
       std::unique_ptr<MediaNotificationDeviceProvider> device_provider);
 
@@ -268,9 +273,20 @@
   void OnReceivedAudioFocusRequests(
       std::vector<media_session::mojom::AudioFocusRequestStatePtr> sessions);
 
+  // Looks up a notification from any source.  Returns null if not found.
   base::WeakPtr<media_message_center::MediaNotificationItem>
   GetNotificationItem(const std::string& id);
 
+  // Looks up a Session object by its ID.  Returns null if not found.
+  Session* GetSession(const std::string& id);
+
+  // Looks up a notification item not associated with a Session object.  Returns
+  // null if not found.
+  //
+  // TODO(crbug.com/1021643): Treat audio sessions the same way we treat others.
+  base::WeakPtr<media_message_center::MediaNotificationItem>
+  GetNonSessionNotificationItem(const std::string& id);
+
   MediaDialogDelegate* dialog_delegate_ = nullptr;
 
   OverlayMediaNotificationsManagerImpl overlay_media_notifications_manager_;
@@ -311,6 +327,8 @@
       audio_focus_observer_receiver_{this};
 
   std::unique_ptr<CastMediaNotificationProvider> cast_notification_provider_;
+  std::unique_ptr<PresentationRequestNotificationProvider>
+      presentation_request_notification_provider_;
 
   base::ObserverList<MediaNotificationServiceObserver> observers_;
 
diff --git a/chrome/browser/ui/global_media_controls/media_notification_service_observer.h b/chrome/browser/ui/global_media_controls/media_notification_service_observer.h
index c0f30b1..da68e51 100644
--- a/chrome/browser/ui/global_media_controls/media_notification_service_observer.h
+++ b/chrome/browser/ui/global_media_controls/media_notification_service_observer.h
@@ -13,9 +13,9 @@
   // changes.
   virtual void OnNotificationListChanged() = 0;
 
-  // Called when a media dialog associated with the service is either opened or
-  // closed.
-  virtual void OnMediaDialogOpenedOrClosed() = 0;
+  // Called when a media dialog associated with the service is opened or closed.
+  virtual void OnMediaDialogOpened() = 0;
+  virtual void OnMediaDialogClosed() = 0;
 
  protected:
   ~MediaNotificationServiceObserver() override = default;
diff --git a/chrome/browser/ui/global_media_controls/media_notification_service_unittest.cc b/chrome/browser/ui/global_media_controls/media_notification_service_unittest.cc
index 97ecd06f..e3229f7 100644
--- a/chrome/browser/ui/global_media_controls/media_notification_service_unittest.cc
+++ b/chrome/browser/ui/global_media_controls/media_notification_service_unittest.cc
@@ -51,8 +51,9 @@
   ~MockMediaNotificationServiceObserver() override = default;
 
   // MediaNotificationServiceObserver implementation.
-  MOCK_METHOD0(OnNotificationListChanged, void());
-  MOCK_METHOD0(OnMediaDialogOpenedOrClosed, void());
+  MOCK_METHOD(void, OnNotificationListChanged, ());
+  MOCK_METHOD(void, OnMediaDialogOpened, ());
+  MOCK_METHOD(void, OnMediaDialogClosed, ());
 };
 
 class MockMediaDialogDelegate : public MediaDialogDelegate {
@@ -381,7 +382,7 @@
   // Simulate opening a MediaDialogView.
   MockMediaDialogDelegate dialog_delegate;
   EXPECT_CALL(dialog_delegate, ShowMediaSession(id.ToString(), _));
-  EXPECT_CALL(observer(), OnMediaDialogOpenedOrClosed());
+  EXPECT_CALL(observer(), OnMediaDialogOpened());
   EXPECT_FALSE(HasOpenDialog());
   SimulateDialogOpened(&dialog_delegate);
 
diff --git a/chrome/browser/ui/global_media_controls/media_toolbar_button_controller.cc b/chrome/browser/ui/global_media_controls/media_toolbar_button_controller.cc
index 4a6374e..f5f5746 100644
--- a/chrome/browser/ui/global_media_controls/media_toolbar_button_controller.cc
+++ b/chrome/browser/ui/global_media_controls/media_toolbar_button_controller.cc
@@ -24,7 +24,11 @@
   UpdateToolbarButtonState();
 }
 
-void MediaToolbarButtonController::OnMediaDialogOpenedOrClosed() {
+void MediaToolbarButtonController::OnMediaDialogOpened() {
+  UpdateToolbarButtonState();
+}
+
+void MediaToolbarButtonController::OnMediaDialogClosed() {
   UpdateToolbarButtonState();
 }
 
diff --git a/chrome/browser/ui/global_media_controls/media_toolbar_button_controller.h b/chrome/browser/ui/global_media_controls/media_toolbar_button_controller.h
index f59ab2e..53f98629 100644
--- a/chrome/browser/ui/global_media_controls/media_toolbar_button_controller.h
+++ b/chrome/browser/ui/global_media_controls/media_toolbar_button_controller.h
@@ -23,7 +23,8 @@
 
   // MediaNotificationServiceObserver implementation.
   void OnNotificationListChanged() override;
-  void OnMediaDialogOpenedOrClosed() override;
+  void OnMediaDialogOpened() override;
+  void OnMediaDialogClosed() override;
 
  private:
   // Tracks the current display state of the toolbar button delegate.
diff --git a/chrome/browser/ui/global_media_controls/presentation_request_notification_item.cc b/chrome/browser/ui/global_media_controls/presentation_request_notification_item.cc
new file mode 100644
index 0000000..2c72f47
--- /dev/null
+++ b/chrome/browser/ui/global_media_controls/presentation_request_notification_item.cc
@@ -0,0 +1,39 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/global_media_controls/presentation_request_notification_item.h"
+
+#include <utility>
+
+#include "chrome/browser/media/router/presentation/presentation_service_delegate_impl.h"
+#include "chrome/browser/ui/global_media_controls/media_notification_service.h"
+
+PresentationRequestNotificationItem::PresentationRequestNotificationItem(
+    const std::string& id,
+    MediaNotificationService* notification_service,
+    const content::PresentationRequest& request,
+    std::unique_ptr<media_router::StartPresentationContext> context)
+    : id_(id),
+      notification_service_(notification_service),
+      context_(std::move(context)) {
+  // TODO(jrw): Save a copy of |request| once it is actually used.
+  DCHECK(!context || request == context->presentation_request());
+  notification_service_->ShowNotification(id_);
+}
+
+PresentationRequestNotificationItem::~PresentationRequestNotificationItem() {
+  notification_service_->RemoveItem(id_);
+}
+
+void PresentationRequestNotificationItem::SetView(
+    media_message_center::MediaNotificationView* view) {}
+
+void PresentationRequestNotificationItem::OnMediaSessionActionButtonPressed(
+    media_session::mojom::MediaSessionAction action) {}
+
+void PresentationRequestNotificationItem::Dismiss() {}
+
+bool PresentationRequestNotificationItem::SourceIsCast() {
+  return false;
+}
diff --git a/chrome/browser/ui/global_media_controls/presentation_request_notification_item.h b/chrome/browser/ui/global_media_controls/presentation_request_notification_item.h
new file mode 100644
index 0000000..dd097b98
--- /dev/null
+++ b/chrome/browser/ui/global_media_controls/presentation_request_notification_item.h
@@ -0,0 +1,60 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_GLOBAL_MEDIA_CONTROLS_PRESENTATION_REQUEST_NOTIFICATION_ITEM_H_
+#define CHROME_BROWSER_UI_GLOBAL_MEDIA_CONTROLS_PRESENTATION_REQUEST_NOTIFICATION_ITEM_H_
+
+#include <memory>
+#include <string>
+
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/ui/global_media_controls/media_notification_service_observer.h"
+#include "components/media_message_center/media_notification_item.h"
+#include "content/public/browser/presentation_request.h"
+
+class MediaNotificationService;
+
+namespace media_router {
+class StartPresentationContext;
+}  // namespace media_router
+
+class PresentationRequestNotificationItem
+    : public media_message_center::MediaNotificationItem {
+ public:
+  PresentationRequestNotificationItem(
+      const std::string& id,
+      MediaNotificationService* notification_service,
+      const content::PresentationRequest& request,
+      std::unique_ptr<media_router::StartPresentationContext> context);
+  PresentationRequestNotificationItem(
+      const PresentationRequestNotificationItem&) = delete;
+  PresentationRequestNotificationItem& operator=(
+      const PresentationRequestNotificationItem&) = delete;
+  ~PresentationRequestNotificationItem() final;
+
+  base::WeakPtr<PresentationRequestNotificationItem> GetWeakPtr() {
+    return weak_ptr_factory_.GetWeakPtr();
+  }
+
+  const std::string& id() const { return id_; }
+  media_router::StartPresentationContext* context() const {
+    return context_.get();
+  }
+
+ private:
+  // media_message_center::MediaNotificationItem
+  void SetView(media_message_center::MediaNotificationView* view) final;
+  void OnMediaSessionActionButtonPressed(
+      media_session::mojom::MediaSessionAction action) final;
+  void Dismiss() final;
+  bool SourceIsCast() final;
+
+  const std::string id_;
+  MediaNotificationService* const notification_service_;
+  std::unique_ptr<media_router::StartPresentationContext> context_;
+  base::WeakPtrFactory<PresentationRequestNotificationItem> weak_ptr_factory_{
+      this};
+};
+
+#endif  // CHROME_BROWSER_UI_GLOBAL_MEDIA_CONTROLS_PRESENTATION_REQUEST_NOTIFICATION_ITEM_H_
diff --git a/chrome/browser/ui/global_media_controls/presentation_request_notification_provider.cc b/chrome/browser/ui/global_media_controls/presentation_request_notification_provider.cc
new file mode 100644
index 0000000..0afdd4f3
--- /dev/null
+++ b/chrome/browser/ui/global_media_controls/presentation_request_notification_provider.cc
@@ -0,0 +1,142 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/global_media_controls/presentation_request_notification_provider.h"
+
+#include <utility>
+
+#include "base/unguessable_token.h"
+#include "chrome/browser/media/router/media_router.h"
+#include "chrome/browser/media/router/media_router_factory.h"
+#include "chrome/browser/media/router/presentation/presentation_service_delegate_impl.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/global_media_controls/media_notification_service.h"
+#include "components/media_router/common/providers/cast/cast_media_source.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace {
+
+base::WeakPtr<media_router::WebContentsPresentationManager>
+GetActiveWebContentsPresentationManager() {
+  auto* browser = chrome::FindLastActive();
+  if (!browser)
+    return nullptr;
+  auto* tab_strip = browser->tab_strip_model();
+  if (!tab_strip)
+    return nullptr;
+  auto* web_contents = tab_strip->GetActiveWebContents();
+  if (!web_contents)
+    return nullptr;
+  return media_router::WebContentsPresentationManager::Get(web_contents);
+}
+
+}  // namespace
+
+PresentationRequestNotificationProvider::
+    PresentationRequestNotificationProvider(
+        MediaNotificationService* notification_service)
+    : notification_service_(notification_service) {
+  notification_service_->AddObserver(this);
+}
+
+PresentationRequestNotificationProvider::
+    ~PresentationRequestNotificationProvider() {
+  notification_service_->RemoveObserver(this);
+}
+
+base::WeakPtr<media_message_center::MediaNotificationItem>
+PresentationRequestNotificationProvider::GetNotificationItem(
+    const std::string& id) {
+  if (item_) {
+    DCHECK_EQ(item_->id(), id);
+    return item_->GetWeakPtr();
+  }
+  return nullptr;
+}
+
+void PresentationRequestNotificationProvider::OnStartPresentationContextCreated(
+    std::unique_ptr<media_router::StartPresentationContext> context) {
+  DCHECK(context);
+  const auto& request = context->presentation_request();
+  CreateItemForPresentationRequest(request, std::move(context));
+}
+
+void PresentationRequestNotificationProvider::OnNotificationListChanged() {}
+
+void PresentationRequestNotificationProvider::OnMediaDialogOpened() {
+  // At the point where this method is called, MediaNotificationService is in
+  // a state where it can't accept new notifications.  As a workaround, we
+  // simply defer the handling of the event.
+  content::GetUIThreadTaskRunner({})->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          &PresentationRequestNotificationProvider::AfterMediaDialogOpened,
+          base::Unretained(this), GetActiveWebContentsPresentationManager()));
+}
+
+void PresentationRequestNotificationProvider::OnMediaDialogClosed() {
+  // This event needs to be handled asynchronously the be absolutely certain
+  // it's handled later than a prior call to OnMediaDialogOpened().
+  content::GetUIThreadTaskRunner({})->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          &PresentationRequestNotificationProvider::AfterMediaDialogClosed,
+          base::Unretained(this), GetActiveWebContentsPresentationManager()));
+}
+
+void PresentationRequestNotificationProvider::AfterMediaDialogOpened(
+    base::WeakPtr<media_router::WebContentsPresentationManager>
+        presentation_manager) {
+  // It's possible the presentation manager was deleted since the call to this
+  // method was scheduled.
+  if (!presentation_manager)
+    return;
+
+  presentation_manager->AddObserver(this);
+
+  // Handle any request that was created while we weren't watching, first making
+  // sure the dialog hasn't been closed since the we found out it was opening.
+  // This is the normal way notifications are created for a default presentation
+  // request.
+  if (presentation_manager->HasDefaultPresentationRequest() &&
+      notification_service_->HasOpenDialog()) {
+    CreateItemForPresentationRequest(
+        presentation_manager->GetDefaultPresentationRequest(), nullptr);
+  }
+}
+
+void PresentationRequestNotificationProvider::AfterMediaDialogClosed(
+    base::WeakPtr<media_router::WebContentsPresentationManager>
+        presentation_manager) {
+  item_.reset();
+  if (presentation_manager)
+    presentation_manager->RemoveObserver(this);
+}
+
+void PresentationRequestNotificationProvider::OnDefaultPresentationChanged(
+    const content::PresentationRequest* presentation_request) {
+  // NOTE: We only observe the presentation manager while the media control
+  // dialog is open, so this method is only handling the unusual case where the
+  // default presentation request is changed while the dialog is open.  In the
+  // even more unusual case where the dialog is already open with a notification
+  // for a non-default request, we ignored changes in the default request.
+  if (!HasItemForNonDefaultRequest()) {
+    if (presentation_request) {
+      CreateItemForPresentationRequest(*presentation_request, nullptr);
+    } else {
+      item_.reset();
+    }
+  }
+}
+
+void PresentationRequestNotificationProvider::CreateItemForPresentationRequest(
+    const content::PresentationRequest& request,
+    std::unique_ptr<media_router::StartPresentationContext> context) {
+  // This may replace an existing item, which is the right thing to do if we've
+  // reached this point.
+  item_.emplace(base::UnguessableToken().ToString(), notification_service_,
+                request, std::move(context));
+}
diff --git a/chrome/browser/ui/global_media_controls/presentation_request_notification_provider.h b/chrome/browser/ui/global_media_controls/presentation_request_notification_provider.h
new file mode 100644
index 0000000..465527e
--- /dev/null
+++ b/chrome/browser/ui/global_media_controls/presentation_request_notification_provider.h
@@ -0,0 +1,90 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_GLOBAL_MEDIA_CONTROLS_PRESENTATION_REQUEST_NOTIFICATION_PROVIDER_H_
+#define CHROME_BROWSER_UI_GLOBAL_MEDIA_CONTROLS_PRESENTATION_REQUEST_NOTIFICATION_PROVIDER_H_
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "chrome/browser/media/router/presentation/web_contents_presentation_manager.h"
+#include "chrome/browser/ui/global_media_controls/media_notification_service_observer.h"
+#include "chrome/browser/ui/global_media_controls/presentation_request_notification_item.h"
+
+// An object that creates and manages media notifications related to
+// presentation requests.
+//
+// The purpose of this class is somewhat subtle.  When a page uses the Cast API
+// or Presentation API to make itself castable, we want there to be a
+// notification for it in the global media controls dialog.  Most of the time,
+// there will already be a notification because the active tab will be playing
+// media that causes a notification to be created, but in some circumstances
+// (e.g. a YouTube page loaded with the audio muted), this class is the only
+// mechanism that will cause a notification to be shown.
+//
+// This class can only ever manage one notification at a time.  The notification
+// correponds either to the default presentation request created in the active
+// tab, or to a non-default presentation request being started (typically by the
+// user clicking a Cast button in the active tab).
+//
+// The notification managed by this object only exists when the media control
+// dialog is showing.  This is to prevent presentation requests from causing the
+// media control button to become visible when it would otherwise be hidden.
+//
+// Once a Cast/Presentation session has been created, this class is no longer
+// involved; at that point CastMediaNotificationProvider become responsible for
+// managing the notification for an active session.
+class PresentationRequestNotificationProvider
+    : public media_router::WebContentsPresentationManager::Observer,
+      public MediaNotificationServiceObserver {
+ public:
+  explicit PresentationRequestNotificationProvider(
+      MediaNotificationService* notification_service);
+  PresentationRequestNotificationProvider(
+      const PresentationRequestNotificationProvider&) = delete;
+  PresentationRequestNotificationProvider& operator=(
+      const PresentationRequestNotificationProvider&) = delete;
+  ~PresentationRequestNotificationProvider() final;
+
+  base::WeakPtr<media_message_center::MediaNotificationItem>
+  GetNotificationItem(const std::string& id);
+
+  void OnStartPresentationContextCreated(
+      std::unique_ptr<media_router::StartPresentationContext> context);
+
+ private:
+  // MediaNotificationServiceObserver
+  void OnNotificationListChanged() final;
+  void OnMediaDialogOpened() final;
+  void OnMediaDialogClosed() final;
+
+  void AfterMediaDialogOpened(
+      base::WeakPtr<media_router::WebContentsPresentationManager>
+          presentation_manager);
+  void AfterMediaDialogClosed(
+      base::WeakPtr<media_router::WebContentsPresentationManager>
+          presentation_manager);
+
+  // WebContentsPresentationManager::Observer
+  void OnDefaultPresentationChanged(
+      const content::PresentationRequest* presentation_request) final;
+
+  void CreateItemForPresentationRequest(
+      const content::PresentationRequest& request,
+      std::unique_ptr<media_router::StartPresentationContext> context);
+
+  // Returns true if there is an item, and the item is for a non-default
+  // presentation request.
+  bool HasItemForNonDefaultRequest() const { return item_ && item_->context(); }
+
+  MediaNotificationService* const notification_service_;
+
+  // The notification managed by this provider, if there is one.
+  base::Optional<PresentationRequestNotificationItem> item_;
+};
+
+#endif  // CHROME_BROWSER_UI_GLOBAL_MEDIA_CONTROLS_PRESENTATION_REQUEST_NOTIFICATION_PROVIDER_H_
diff --git a/chrome/browser/ui/media_router/media_router_ui.cc b/chrome/browser/ui/media_router/media_router_ui.cc
index 09fe332..12c174d 100644
--- a/chrome/browser/ui/media_router/media_router_ui.cc
+++ b/chrome/browser/ui/media_router/media_router_ui.cc
@@ -28,6 +28,7 @@
 #include "chrome/browser/media/router/media_router_factory.h"
 #include "chrome/browser/media/router/media_router_metrics.h"
 #include "chrome/browser/media/router/media_routes_observer.h"
+#include "chrome/browser/media/router/presentation/presentation_service_delegate_impl.h"
 #include "chrome/browser/media/router/providers/wired_display/wired_display_media_route_provider.h"
 #include "chrome/browser/media/webrtc/desktop_media_picker_controller.h"
 #include "chrome/browser/profiles/profile.h"
@@ -758,8 +759,6 @@
                      sink_id, cast_mode, GetPresentationRequestSourceName()));
   if (for_presentation_source) {
     if (start_presentation_context_) {
-      // |start_presentation_context_| will be nullptr after this call, as the
-      // object will be transferred to the callback.
       params.presentation_callback =
           base::BindOnce(&StartPresentationContext::HandleRouteResponse,
                          std::move(start_presentation_context_));
diff --git a/chrome/browser/ui/media_router/media_router_ui.h b/chrome/browser/ui/media_router/media_router_ui.h
index f8415cc7..de2e65e 100644
--- a/chrome/browser/ui/media_router/media_router_ui.h
+++ b/chrome/browser/ui/media_router/media_router_ui.h
@@ -19,6 +19,7 @@
 #include "build/build_config.h"
 #include "chrome/browser/media/router/issues_observer.h"
 #include "chrome/browser/media/router/media_router_dialog_controller.h"
+#include "chrome/browser/media/router/presentation/start_presentation_context.h"
 #include "chrome/browser/media/router/presentation/web_contents_presentation_manager.h"
 #include "chrome/browser/ui/media_router/cast_dialog_controller.h"
 #include "chrome/browser/ui/media_router/cast_dialog_model.h"
diff --git a/chrome/browser/ui/media_router/media_router_ui_unittest.cc b/chrome/browser/ui/media_router/media_router_ui_unittest.cc
index 95a2e3e7..a2c38ed 100644
--- a/chrome/browser/ui/media_router/media_router_ui_unittest.cc
+++ b/chrome/browser/ui/media_router/media_router_ui_unittest.cc
@@ -13,6 +13,7 @@
 #include "build/build_config.h"
 #include "chrome/browser/media/router/media_router_factory.h"
 #include "chrome/browser/media/router/media_sinks_observer.h"
+#include "chrome/browser/media/router/presentation/presentation_service_delegate_impl.h"
 #include "chrome/browser/media/router/providers/wired_display/wired_display_media_route_provider.h"
 #include "chrome/browser/media/router/test/mock_media_router.h"
 #include "chrome/browser/media/router/test/test_helper.h"
@@ -236,14 +237,15 @@
     blink::mojom::PresentationError expected_error(error_type, error_message);
     auto request_callbacks =
         std::make_unique<PresentationRequestCallbacks>(expected_error);
-    auto context = std::make_unique<StartPresentationContext>(
+    start_presentation_context_ = std::make_unique<StartPresentationContext>(
         presentation_request_,
         base::Bind(&PresentationRequestCallbacks::Success,
                    base::Unretained(request_callbacks.get())),
         base::Bind(&PresentationRequestCallbacks::Error,
                    base::Unretained(request_callbacks.get())));
-    StartPresentationContext* context_ptr = context.get();
-    ui_->set_start_presentation_context_for_test(std::move(context));
+    StartPresentationContext* context_ptr = start_presentation_context_.get();
+    ui_->set_start_presentation_context_for_test(
+        std::move(start_presentation_context_));
     ui_->OnDefaultPresentationChanged(&context_ptr->presentation_request());
     return request_callbacks;
   }
diff --git a/chrome/browser/ui/prefs/prefs_tab_helper.cc b/chrome/browser/ui/prefs/prefs_tab_helper.cc
index f3e4a7b..97d147c 100644
--- a/chrome/browser/ui/prefs/prefs_tab_helper.cc
+++ b/chrome/browser/ui/prefs/prefs_tab_helper.cc
@@ -40,10 +40,10 @@
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.h"
-#include "content/public/common/web_preferences.h"
 #include "extensions/buildflags/buildflags.h"
 #include "media/media_buildflags.h"
 #include "third_party/blink/public/common/features.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "third_party/blink/public/mojom/renderer_preferences.mojom.h"
 #include "third_party/icu/source/common/unicode/uchar.h"
 #include "third_party/icu/source/common/unicode/uscript.h"
@@ -60,8 +60,8 @@
 #include "chrome/browser/themes/theme_service_factory.h"
 #endif
 
+using blink::web_pref::WebPreferences;
 using content::WebContents;
-using content::WebPreferences;
 
 namespace {
 
@@ -246,11 +246,11 @@
 }
 
 // Sets a font family pref in |prefs| to |pref_value|.
-void OverrideFontFamily(WebPreferences* prefs,
+void OverrideFontFamily(blink::web_pref::WebPreferences* prefs,
                         const std::string& generic_family,
                         const std::string& script,
                         const std::string& pref_value) {
-  content::ScriptFontFamilyMap* map = NULL;
+  blink::web_pref::ScriptFontFamilyMap* map = nullptr;
   if (generic_family == "standard")
     map = &prefs->standard_font_family_map;
   else if (generic_family == "fixed")
@@ -475,7 +475,8 @@
     PrefService* prefs = profile_->GetPrefs();
     std::string pref_value = prefs->GetString(pref_name);
     if (pref_value.empty()) {
-      WebPreferences web_prefs = web_contents_->GetOrCreateWebPreferences();
+      blink::web_pref::WebPreferences web_prefs =
+          web_contents_->GetOrCreateWebPreferences();
       OverrideFontFamily(&web_prefs, generic_family, script, std::string());
       web_contents_->SetWebPreferences(web_prefs);
       return;
diff --git a/chrome/browser/ui/signin_reauth_view_controller.cc b/chrome/browser/ui/signin_reauth_view_controller.cc
index 9dfc069..99d37e6 100644
--- a/chrome/browser/ui/signin_reauth_view_controller.cc
+++ b/chrome/browser/ui/signin_reauth_view_controller.cc
@@ -28,9 +28,9 @@
 #include "content/public/browser/site_instance.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
-#include "content/public/common/web_preferences.h"
 #include "google_apis/gaia/gaia_urls.h"
 #include "third_party/blink/public/common/css/preferred_color_scheme.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 
 namespace {
 
diff --git a/chrome/browser/ui/views/global_media_controls/media_notification_container_impl_view.cc b/chrome/browser/ui/views/global_media_controls/media_notification_container_impl_view.cc
index 4d51627a..d2afd4d 100644
--- a/chrome/browser/ui/views/global_media_controls/media_notification_container_impl_view.cc
+++ b/chrome/browser/ui/views/global_media_controls/media_notification_container_impl_view.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/views/global_media_controls/media_notification_container_impl_view.h"
 
 #include "base/feature_list.h"
+#include "base/metrics/field_trial_params.h"
 #include "chrome/browser/ui/global_media_controls/media_notification_container_impl.h"
 #include "chrome/browser/ui/global_media_controls/media_notification_container_observer.h"
 #include "chrome/browser/ui/global_media_controls/media_notification_service.h"
@@ -31,10 +32,10 @@
 
 // TODO(steimel): We need to decide on the correct values here.
 constexpr int kWidth = 400;
-// TODO(noahrose): Should these sizes include the height of the audio
-// device selector view?
+constexpr int kModernUIWidth = 350;
 constexpr gfx::Size kNormalSize = gfx::Size(kWidth, 100);
 constexpr gfx::Size kExpandedSize = gfx::Size(kWidth, 150);
+constexpr gfx::Size kModernUISize = gfx::Size(kModernUIWidth, 100);
 constexpr gfx::Size kDismissButtonSize = gfx::Size(30, 30);
 constexpr int kDismissButtonIconSize = 20;
 constexpr int kDismissButtonBackgroundRadius = 15;
@@ -118,12 +119,16 @@
 
   std::unique_ptr<media_message_center::MediaNotificationView> view;
   if (base::FeatureList::IsEnabled(media::kGlobalMediaControlsModernUI)) {
-    view = std::make_unique<
-        media_message_center::MediaNotificationViewModernImpl>();
+    view =
+        std::make_unique<media_message_center::MediaNotificationViewModernImpl>(
+            this, std::move(item), std::move(dismiss_button_placeholder),
+            kModernUIWidth);
+    SetPreferredSize(kModernUISize);
   } else {
     view = std::make_unique<media_message_center::MediaNotificationViewImpl>(
         this, std::move(item), std::move(dismiss_button_placeholder),
         base::string16(), kWidth, /*should_show_icon=*/false);
+    SetPreferredSize(kNormalSize);
   }
   view_ = swipeable_container_->AddChildView(std::move(view));
 
@@ -496,7 +501,12 @@
 }
 
 void MediaNotificationContainerImplView::OnSizeChanged() {
-  gfx::Size new_size = is_expanded_ ? kExpandedSize : kNormalSize;
+  gfx::Size new_size;
+  if (base::FeatureList::IsEnabled(media::kGlobalMediaControlsModernUI)) {
+    new_size = kModernUISize;
+  } else {
+    new_size = is_expanded_ ? kExpandedSize : kNormalSize;
+  }
 
   // |new_size| does not contain the height for the audio device selector view.
   // If this view is present, we should query it for its preferred height and
diff --git a/chrome/browser/ui/views/media_router/media_router_dialog_controller_views.cc b/chrome/browser/ui/views/media_router/media_router_dialog_controller_views.cc
index 33dd75d..8b0821f 100644
--- a/chrome/browser/ui/views/media_router/media_router_dialog_controller_views.cc
+++ b/chrome/browser/ui/views/media_router/media_router_dialog_controller_views.cc
@@ -8,6 +8,7 @@
 
 #include "build/build_config.h"
 #include "chrome/browser/media/router/media_router_feature.h"
+#include "chrome/browser/media/router/presentation/start_presentation_context.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/global_media_controls/media_notification_service.h"
 #include "chrome/browser/ui/global_media_controls/media_notification_service_factory.h"
@@ -57,6 +58,7 @@
         Profile::FromBrowserContext(initiator()->GetBrowserContext());
     MediaNotificationService* const service =
         MediaNotificationServiceFactory::GetForProfile(profile);
+    service->OnStartPresentationContextCreated(std::move(context));
     Browser* const browser = chrome::FindBrowserWithWebContents(initiator());
     BrowserView* const browser_view =
         browser ? BrowserView::GetBrowserViewForBrowser(browser) : nullptr;
diff --git a/chrome/browser/ui/views/media_router/media_router_dialog_controller_views.h b/chrome/browser/ui/views/media_router/media_router_dialog_controller_views.h
index 792783e..1795d9c 100644
--- a/chrome/browser/ui/views/media_router/media_router_dialog_controller_views.h
+++ b/chrome/browser/ui/views/media_router/media_router_dialog_controller_views.h
@@ -12,12 +12,14 @@
 #include "base/scoped_observer.h"
 #include "chrome/browser/media/router/media_router_dialog_controller.h"
 #include "chrome/browser/ui/media_router/media_router_ui_service.h"
+#include "content/public/browser/web_contents_user_data.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_observer.h"
 
 namespace media_router {
 
 class MediaRouterUI;
+class StartPresentationContext;
 
 // A Views implementation of MediaRouterDialogController.
 class MediaRouterDialogControllerViews
diff --git a/chrome/browser/ui/views/media_router/media_router_ui_browsertest.cc b/chrome/browser/ui/views/media_router/media_router_ui_browsertest.cc
index b6aadc1..fefde00 100644
--- a/chrome/browser/ui/views/media_router/media_router_ui_browsertest.cc
+++ b/chrome/browser/ui/views/media_router/media_router_ui_browsertest.cc
@@ -39,8 +39,8 @@
 
 namespace media_router {
 
-// Base class containing setup code and test cases shared between WebUI and
-// Views dialog tests.
+// Base class containing setup code and test cases shared between Views dialog
+// tests.
 class MediaRouterUIBrowserTest : public InProcessBrowserTest {
  public:
   MediaRouterUIBrowserTest()
@@ -78,6 +78,7 @@
     GetCastIcon()->OnMousePressed(
         ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::Point(0, 0), gfx::Point(0, 0),
                        ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, 0));
+    base::RunLoop().RunUntilIdle();
   }
 
   bool ToolbarIconExists() {
diff --git a/chrome/browser/ui/webui/chromeos/system_web_dialog_browsertest.cc b/chrome/browser/ui/webui/chromeos/system_web_dialog_browsertest.cc
index 234b0c8..46e2836 100644
--- a/chrome/browser/ui/webui/chromeos/system_web_dialog_browsertest.cc
+++ b/chrome/browser/ui/webui/chromeos/system_web_dialog_browsertest.cc
@@ -20,9 +20,9 @@
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_ui.h"
-#include "content/public/common/web_preferences.h"
 #include "content/public/test/browser_test.h"
 #include "third_party/blink/public/common/page/page_zoom.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "ui/aura/client/aura_constants.h"
 #include "url/gurl.h"
 
@@ -99,7 +99,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(SystemWebDialogTest, FontSize) {
-  const content::WebPreferences kDefaultPrefs;
+  const blink::web_pref::WebPreferences kDefaultPrefs;
   const int kDefaultFontSize = kDefaultPrefs.default_font_size;
   const int kDefaultFixedFontSize = kDefaultPrefs.default_fixed_font_size;
 
@@ -115,7 +115,7 @@
   dialog->ShowSystemDialog();
 
   // Dialog font sizes are still the default values.
-  content::WebPreferences dialog_prefs =
+  blink::web_pref::WebPreferences dialog_prefs =
       dialog->GetWebUIForTest()->GetWebContents()->GetOrCreateWebPreferences();
   EXPECT_EQ(kDefaultFontSize, dialog_prefs.default_font_size);
   EXPECT_EQ(kDefaultFixedFontSize, dialog_prefs.default_fixed_font_size);
diff --git a/chrome/browser/ui/webui/media/media_engagement_ui.cc b/chrome/browser/ui/webui/media/media_engagement_ui.cc
index 04bf259..7031f4e 100644
--- a/chrome/browser/ui/webui/media/media_engagement_ui.cc
+++ b/chrome/browser/ui/webui/media/media_engagement_ui.cc
@@ -25,10 +25,10 @@
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_controller.h"
 #include "content/public/browser/web_ui_data_source.h"
-#include "content/public/common/web_preferences.h"
 #include "media/base/media_switches.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 
 #if !defined(OS_ANDROID)
 #include "chrome/common/pref_names.h"
@@ -95,11 +95,11 @@
     switch (web_ui_->GetWebContents()
                 ->GetOrCreateWebPreferences()
                 .autoplay_policy) {
-      case content::AutoplayPolicy::kNoUserGestureRequired:
+      case blink::web_pref::AutoplayPolicy::kNoUserGestureRequired:
         return "no-user-gesture-required";
-      case content::AutoplayPolicy::kUserGestureRequired:
+      case blink::web_pref::AutoplayPolicy::kUserGestureRequired:
         return "user-gesture-required";
-      case content::AutoplayPolicy::kDocumentUserActivationRequired:
+      case blink::web_pref::AutoplayPolicy::kDocumentUserActivationRequired:
         return "document-user-activation-required";
     }
   }
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
index 773748f..a9aedac 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
@@ -548,6 +548,7 @@
   if (!id_)
     return;
 
+  receiver_.reset();
   PrintPreviewDataService::GetInstance()->RemoveEntry(*id_);
   g_print_preview_request_id_map.Get().Erase(*id_);
   g_print_preview_ui_id_map.Get().Remove(*id_);
diff --git a/chrome/browser/ui/webui/tab_search/BUILD.gn b/chrome/browser/ui/webui/tab_search/BUILD.gn
index 092303f..ec9b1c3 100644
--- a/chrome/browser/ui/webui/tab_search/BUILD.gn
+++ b/chrome/browser/ui/webui/tab_search/BUILD.gn
@@ -6,4 +6,5 @@
 
 mojom("mojo_bindings") {
   sources = [ "tab_search.mojom" ]
+  public_deps = [ "//mojo/public/mojom/base" ]
 }
diff --git a/chrome/browser/ui/webui/tab_search/tab_search.mojom b/chrome/browser/ui/webui/tab_search/tab_search.mojom
index 0c03b500..d5b163a 100644
--- a/chrome/browser/ui/webui/tab_search/tab_search.mojom
+++ b/chrome/browser/ui/webui/tab_search/tab_search.mojom
@@ -4,6 +4,8 @@
 
 module tab_search.mojom;
 
+import "mojo/public/mojom/base/time.mojom";
+
 // Collection of WindowTabs of a profile.
 struct ProfileTabs {
   array<WindowTabs> windows;
@@ -27,6 +29,7 @@
   string? fav_icon_url;
   bool is_default_favicon;
   bool show_icon;
+  mojo_base.mojom.TimeTicks last_active_time_ticks;
 };
 
 // Collection of tab groups.
diff --git a/chrome/browser/ui/webui/tab_search/tab_search_page_handler.cc b/chrome/browser/ui/webui/tab_search/tab_search_page_handler.cc
index ea1e0ec3..fd370c94 100644
--- a/chrome/browser/ui/webui/tab_search/tab_search_page_handler.cc
+++ b/chrome/browser/ui/webui/tab_search/tab_search_page_handler.cc
@@ -185,6 +185,7 @@
             favicon::GetDefaultFavicon().AsImageSkia());
   }
   tab_data->show_icon = tab_renderer_data.show_icon;
+  tab_data->last_active_time_ticks = contents->GetLastActiveTime();
 
   return tab_data;
 }
diff --git a/chrome/browser/ui/webui/tab_search/tab_search_page_handler_unittest.cc b/chrome/browser/ui/webui/tab_search/tab_search_page_handler_unittest.cc
index 33c90da2..35970d01 100644
--- a/chrome/browser/ui/webui/tab_search/tab_search_page_handler_unittest.cc
+++ b/chrome/browser/ui/webui/tab_search/tab_search_page_handler_unittest.cc
@@ -63,6 +63,7 @@
   EXPECT_TRUE(tab->fav_icon_url.has_value());
   EXPECT_TRUE(tab->is_default_favicon);
   EXPECT_TRUE(tab->show_icon);
+  EXPECT_GT(tab->last_active_time_ticks, base::TimeTicks());
 }
 
 void ExpectProfileTabs(tab_search::mojom::ProfileTabs* profile_tabs) {
diff --git a/chrome/browser/ui/webui/tab_strip/chrome_content_browser_client_tab_strip_part.cc b/chrome/browser/ui/webui/tab_strip/chrome_content_browser_client_tab_strip_part.cc
index 0891024..30aa4e5 100644
--- a/chrome/browser/ui/webui/tab_strip/chrome_content_browser_client_tab_strip_part.cc
+++ b/chrome/browser/ui/webui/tab_strip/chrome_content_browser_client_tab_strip_part.cc
@@ -7,7 +7,7 @@
 #include "chrome/common/webui_url_constants.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/web_contents.h"
-#include "content/public/common/web_preferences.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 
 ChromeContentBrowserClientTabStripPart::
     ChromeContentBrowserClientTabStripPart() = default;
@@ -16,7 +16,7 @@
 
 void ChromeContentBrowserClientTabStripPart::OverrideWebkitPrefs(
     content::RenderViewHost* rvh,
-    content::WebPreferences* web_prefs) {
+    blink::web_pref::WebPreferences* web_prefs) {
   content::WebContents* contents =
       content::WebContents::FromRenderViewHost(rvh);
 
@@ -30,7 +30,7 @@
     return;
   }
 
-  content::WebPreferences default_prefs;
+  blink::web_pref::WebPreferences default_prefs;
   web_prefs->default_font_size = default_prefs.default_font_size;
   web_prefs->default_fixed_font_size = default_prefs.default_fixed_font_size;
   web_prefs->minimum_font_size = default_prefs.minimum_font_size;
diff --git a/chrome/browser/ui/webui/tab_strip/chrome_content_browser_client_tab_strip_part.h b/chrome/browser/ui/webui/tab_strip/chrome_content_browser_client_tab_strip_part.h
index 63af9321..2edc7d3 100644
--- a/chrome/browser/ui/webui/tab_strip/chrome_content_browser_client_tab_strip_part.h
+++ b/chrome/browser/ui/webui/tab_strip/chrome_content_browser_client_tab_strip_part.h
@@ -8,7 +8,7 @@
 #include "base/macros.h"
 #include "chrome/browser/chrome_content_browser_client_parts.h"
 #include "content/public/browser/render_view_host.h"
-#include "content/public/common/web_preferences.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 
 class ChromeContentBrowserClientTabStripPart
     : public ChromeContentBrowserClientParts {
@@ -18,7 +18,7 @@
 
   // ChromeContentBrowserClientParts:
   void OverrideWebkitPrefs(content::RenderViewHost* rvh,
-                           content::WebPreferences* web_prefs) override;
+                           blink::web_pref::WebPreferences* web_prefs) override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(ChromeContentBrowserClientTabStripPart);
diff --git a/chrome/browser/ui/webui/tab_strip/chrome_content_browser_client_tab_strip_part_browsertest.cc b/chrome/browser/ui/webui/tab_strip/chrome_content_browser_client_tab_strip_part_browsertest.cc
index 7767380..c3dadea 100644
--- a/chrome/browser/ui/webui/tab_strip/chrome_content_browser_client_tab_strip_part_browsertest.cc
+++ b/chrome/browser/ui/webui/tab_strip/chrome_content_browser_client_tab_strip_part_browsertest.cc
@@ -12,8 +12,8 @@
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/render_view_host.h"
-#include "content/public/common/web_preferences.h"
 #include "content/public/test/browser_test.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "ui/base/ui_base_switches.h"
 
 class ChromeContentBrowserClientTabStripPartTest : public InProcessBrowserTest {
@@ -34,14 +34,14 @@
 
 IN_PROC_BROWSER_TEST_F(ChromeContentBrowserClientTabStripPartTest,
                        TabStripHasDefaultFontSizes) {
-  const content::WebPreferences default_prefs;
+  const blink::web_pref::WebPreferences default_prefs;
   const int kDefaultFontSize = default_prefs.default_font_size;
   const int kDefaultFixedFontSize = default_prefs.default_fixed_font_size;
   const int kDefaultMinimumFontSize = default_prefs.minimum_font_size;
   const int kDefaultMinimumLogicalFontSize =
       default_prefs.minimum_logical_font_size;
 
-  content::WebPreferences preexisting_tab_strip_prefs =
+  blink::web_pref::WebPreferences preexisting_tab_strip_prefs =
       CreateTabStripWebContents()->GetOrCreateWebPreferences();
 
   Profile* profile = browser()->profile();
@@ -63,7 +63,7 @@
   EXPECT_EQ(kDefaultMinimumLogicalFontSize,
             preexisting_tab_strip_prefs.minimum_logical_font_size);
 
-  content::WebPreferences new_tab_strip_prefs =
+  blink::web_pref::WebPreferences new_tab_strip_prefs =
       CreateTabStripWebContents()->GetOrCreateWebPreferences();
   EXPECT_EQ(kDefaultFontSize, new_tab_strip_prefs.default_font_size);
   EXPECT_EQ(kDefaultFixedFontSize, new_tab_strip_prefs.default_fixed_font_size);
diff --git a/chrome/browser/vr/vr_tab_helper.cc b/chrome/browser/vr/vr_tab_helper.cc
index 5f66636..0fcc15a 100644
--- a/chrome/browser/vr/vr_tab_helper.cc
+++ b/chrome/browser/vr/vr_tab_helper.cc
@@ -7,8 +7,8 @@
 #include "build/build_config.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/xr_runtime_manager.h"
-#include "content/public/common/web_preferences.h"
 #include "device/vr/buildflags/buildflags.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 
 #if defined(OS_ANDROID)
 #include "base/feature_list.h"
@@ -18,8 +18,8 @@
 #include "chrome/browser/ui/browser_finder.h"
 #endif
 
+using blink::web_pref::WebPreferences;
 using content::WebContents;
-using content::WebPreferences;
 
 namespace vr {
 
@@ -34,7 +34,8 @@
 
   is_in_vr_ = is_in_vr;
 
-  WebPreferences web_prefs = web_contents_->GetOrCreateWebPreferences();
+  blink::web_pref::WebPreferences web_prefs =
+      web_contents_->GetOrCreateWebPreferences();
   web_prefs.immersive_mode_enabled = is_in_vr_;
   web_contents_->SetWebPreferences(web_prefs);
 }
diff --git a/chrome/browser/web_applications/BUILD.gn b/chrome/browser/web_applications/BUILD.gn
index 4243e8b..65b715d 100644
--- a/chrome/browser/web_applications/BUILD.gn
+++ b/chrome/browser/web_applications/BUILD.gn
@@ -84,6 +84,8 @@
     "web_app_install_manager.h",
     "web_app_install_task.cc",
     "web_app_install_task.h",
+    "web_app_installation_utils.cc",
+    "web_app_installation_utils.h",
     "web_app_proto_utils.cc",
     "web_app_proto_utils.h",
     "web_app_registrar.cc",
@@ -204,6 +206,7 @@
     "web_app_icon_manager_unittest.cc",
     "web_app_install_manager_unittest.cc",
     "web_app_install_task_unittest.cc",
+    "web_app_installation_utils_unittest.cc",
     "web_app_proto_utils_unittest.cc",
     "web_app_registrar_unittest.cc",
     "web_app_sync_bridge_unittest.cc",
diff --git a/chrome/browser/web_applications/components/app_shortcut_manager.cc b/chrome/browser/web_applications/components/app_shortcut_manager.cc
index bf165042..8478717 100644
--- a/chrome/browser/web_applications/components/app_shortcut_manager.cc
+++ b/chrome/browser/web_applications/components/app_shortcut_manager.cc
@@ -8,6 +8,7 @@
 
 #include "base/callback.h"
 #include "base/feature_list.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/no_destructor.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
@@ -21,6 +22,19 @@
 
 namespace {
 
+// UMA metric name for shortcuts creation result.
+constexpr const char* kCreationResultMetric =
+    "WebApp.Shortcuts.Creation.Result";
+
+// Result of shortcuts creation process.
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class CreationResult {
+  kSuccess = 0,
+  kFailToCreateShortcut = 1,
+  kMaxValue = kFailToCreateShortcut
+};
+
 AppShortcutManager::ShortcutCallback& GetShortcutUpdateCallbackForTesting() {
   static base::NoDestructor<AppShortcutManager::ShortcutCallback> callback;
   return *callback;
@@ -134,6 +148,9 @@
                                             CreateShortcutsCallback callback,
                                             bool success) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  UMA_HISTOGRAM_ENUMERATION(kCreationResultMetric,
+                            success ? CreationResult::kSuccess
+                                    : CreationResult::kFailToCreateShortcut);
   std::move(callback).Run(success);
 }
 
diff --git a/chrome/browser/web_applications/components/file_handler_manager.cc b/chrome/browser/web_applications/components/file_handler_manager.cc
index 31c4b752..530c4a32 100644
--- a/chrome/browser/web_applications/components/file_handler_manager.cc
+++ b/chrome/browser/web_applications/components/file_handler_manager.cc
@@ -7,6 +7,7 @@
 #include "base/bind.h"
 #include "base/feature_list.h"
 #include "base/time/time.h"
+#include "build/build_config.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/web_applications/components/web_app_file_handler_registration.h"
 #include "chrome/browser/web_applications/components/web_app_prefs_utils.h"
@@ -85,10 +86,17 @@
     return;
   }
 
+// File handler registration is done via shortcuts creation on MacOS,
+// WebAppShortcutManager::BuildShortcutInfoForWebApp collects file handler
+// information to shortcut_info->file_handler_extensions, then used by MacOS
+// implementation of |internals::CreatePlatformShortcuts|. So we avoid creating
+// shortcuts twice here.
+#if !defined(OS_MAC)
   std::string app_name = registrar_->GetAppShortName(app_id);
   const apps::FileHandlers* file_handlers = GetAllFileHandlers(app_id);
   if (file_handlers)
     RegisterFileHandlersWithOs(app_id, app_name, profile(), *file_handlers);
+#endif
 }
 
 void FileHandlerManager::DisableAndUnregisterOsFileHandlers(
@@ -106,7 +114,13 @@
     return;
   }
 
+  // File handler information is embedded in the shortcut, when
+  // |DeleteSharedAppShims| is called in
+  // |OsIntegrationManager::UninstallOsHooks|, file handlers are also
+  // unregistered./
+#if !defined(OS_MAC)
   UnregisterFileHandlersWithOs(app_id, profile());
+#endif
 }
 
 void FileHandlerManager::MaybeUpdateFileHandlingOriginTrialExpiry(
diff --git a/chrome/browser/web_applications/components/os_integration_manager.cc b/chrome/browser/web_applications/components/os_integration_manager.cc
index fa654353..b7d8edf 100644
--- a/chrome/browser/web_applications/components/os_integration_manager.cc
+++ b/chrome/browser/web_applications/components/os_integration_manager.cc
@@ -203,14 +203,6 @@
   return shortcut_manager_->CanCreateShortcuts();
 }
 
-void OsIntegrationManager::CreateShortcuts(const AppId& app_id,
-                                           bool add_to_desktop,
-                                           CreateShortcutsCallback callback) {
-  DCHECK(shortcut_manager_);
-  return shortcut_manager_->CreateShortcuts(app_id, add_to_desktop,
-                                            std::move(callback));
-}
-
 void OsIntegrationManager::GetShortcutInfoForApp(
     const AppId& app_id,
     AppShortcutManager::GetShortcutInfoCallback callback) {
diff --git a/chrome/browser/web_applications/components/os_integration_manager.h b/chrome/browser/web_applications/components/os_integration_manager.h
index b2e2918..e0adf76 100644
--- a/chrome/browser/web_applications/components/os_integration_manager.h
+++ b/chrome/browser/web_applications/components/os_integration_manager.h
@@ -93,11 +93,6 @@
   void GetShortcutInfoForApp(
       const AppId& app_id,
       AppShortcutManager::GetShortcutInfoCallback callback);
-  // TODO(https://crbug.com/1123201): remove exposing this from
-  // OsIntegrationManager.
-  void CreateShortcuts(const AppId& app_id,
-                       bool add_to_desktop,
-                       CreateShortcutsCallback callback);
 
   // Proxy calls for FileHandlerManager.
   bool IsFileHandlingAPIAvailable(const AppId& app_id);
diff --git a/chrome/browser/web_applications/components/web_app_file_handler_registration_mac.cc b/chrome/browser/web_applications/components/web_app_file_handler_registration_mac.cc
index 1929e39..dd5fb9c 100644
--- a/chrome/browser/web_applications/components/web_app_file_handler_registration_mac.cc
+++ b/chrome/browser/web_applications/components/web_app_file_handler_registration_mac.cc
@@ -4,49 +4,7 @@
 
 #include "chrome/browser/web_applications/components/web_app_file_handler_registration.h"
 
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/metrics/histogram_macros.h"
-#include "chrome/browser/web_applications/components/app_registrar.h"
-#include "chrome/browser/web_applications/components/os_integration_manager.h"
-#include "chrome/browser/web_applications/components/web_app_provider_base.h"
-#include "chrome/browser/web_applications/components/web_app_shortcut.h"
-
 namespace web_app {
-
-namespace {
-
-// UMA metric name for file handler registration result.
-constexpr const char* kRegistrationResultMetric =
-    "Apps.FileHandler.Registration.Mac.Result";
-
-// Result of file handler registration process.
-// These values are persisted to logs. Entries should not be renumbered and
-// numeric values should never be reused.
-enum class RegistrationResult {
-  kSuccess = 0,
-  kFailToCreateShortcut = 1,
-  kMaxValue = kFailToCreateShortcut
-};
-
-void UpdateFileHandlerRegistrationInOs(const AppId& app_id, Profile* profile) {
-  // On OSX, file associations are managed through app shims in the Applications
-  // directory, so after enabling or disabling file handling for an app its shim
-  // needs to be updated.
-  OsIntegrationManager& os_integration_manager =
-      WebAppProviderBase::GetProviderBase(profile)->os_integration_manager();
-  auto onCreateShortcut = [](bool shortcut_created) {
-    UMA_HISTOGRAM_ENUMERATION(kRegistrationResultMetric,
-                              shortcut_created
-                                  ? RegistrationResult::kSuccess
-                                  : RegistrationResult::kFailToCreateShortcut);
-  };
-  os_integration_manager.CreateShortcuts(app_id, /*add_to_desktop=*/false,
-                                         base::BindOnce(onCreateShortcut));
-}
-
-}  // namespace
-
 bool ShouldRegisterFileHandlersWithOs() {
   return true;
 }
@@ -55,18 +13,17 @@
                                 const std::string& app_name,
                                 Profile* profile,
                                 const apps::FileHandlers& file_handlers) {
-  UpdateFileHandlerRegistrationInOs(app_id, profile);
+  // On MacOS, file associations are managed through app shims in the
+  // Applications directory. File handler registration is handled via shortcuts
+  // creation.
+  NOTREACHED();
 }
 
 void UnregisterFileHandlersWithOs(const AppId& app_id, Profile* profile) {
-  // If this was triggered as part of the uninstallation process, nothing more
-  // is needed. Uninstalling already cleans up app shims (and thus, file
-  // handlers).
-  auto* provider = WebAppProviderBase::GetProviderBase(profile);
-  if (!provider->registrar().IsInstalled(app_id))
-    return;
-
-  UpdateFileHandlerRegistrationInOs(app_id, profile);
+  // On MacOS, file associations are managed through app shims in the
+  // Applications directory. File handler unregistration is handled via
+  // shortcuts deletion on MacOS.
+  NOTREACHED();
 }
 
 }  // namespace web_app
\ No newline at end of file
diff --git a/chrome/browser/web_applications/components/web_app_install_utils.cc b/chrome/browser/web_applications/components/web_app_install_utils.cc
index 8a35dbb..a6734ab 100644
--- a/chrome/browser/web_applications/components/web_app_install_utils.cc
+++ b/chrome/browser/web_applications/components/web_app_install_utils.cc
@@ -8,6 +8,7 @@
 #include <string>
 #include <utility>
 
+#include "base/feature_list.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/optional.h"
 #include "base/stl_util.h"
@@ -210,6 +211,8 @@
 
   web_app_info->file_handlers = manifest.file_handlers;
 
+  web_app_info->share_target = manifest.share_target;
+
   web_app_info->protocol_handlers = manifest.protocol_handlers;
 
   // If any shortcuts are specified in the manifest, they take precedence over
diff --git a/chrome/browser/web_applications/components/web_app_install_utils_unittest.cc b/chrome/browser/web_applications/components/web_app_install_utils_unittest.cc
index f7e806a..dc42f25 100644
--- a/chrome/browser/web_applications/components/web_app_install_utils_unittest.cc
+++ b/chrome/browser/web_applications/components/web_app_install_utils_unittest.cc
@@ -638,4 +638,87 @@
   }
 }
 
+TEST(WebAppInstallUtils, UpdateShareTargetFromManifest) {
+  const base::string16 kTitle = base::ASCIIToUTF16("kTitle");
+  const base::string16 kText = base::ASCIIToUTF16("kText");
+  const base::string16 kUrl = base::ASCIIToUTF16("kUrl");
+  const base::string16 kImages = base::ASCIIToUTF16("kImages");
+  const base::string16 kExtension = base::ASCIIToUTF16(".png");
+  const base::string16 kContentType = base::ASCIIToUTF16("image/png");
+
+  WebApplicationInfo web_app_info;
+
+  blink::Manifest manifest;
+  manifest.start_url = AppUrl();
+  manifest.scope = AppUrl().GetWithoutFilename();
+  manifest.short_name = base::ASCIIToUTF16(kAppShortName);
+
+  {
+    blink::Manifest::ShareTarget share_target;
+    share_target.action = GURL("http://example.com/share1");
+    share_target.method = blink::Manifest::ShareTarget::Method::kPost;
+    share_target.enctype =
+        blink::Manifest::ShareTarget::Enctype::kMultipartFormData;
+    share_target.params.title = kTitle;
+    share_target.params.text = kText;
+
+    blink::Manifest::FileFilter file_filter;
+    file_filter.name = kImages;
+    file_filter.accept.push_back(kExtension);
+    file_filter.accept.push_back(kContentType);
+    share_target.params.files.push_back(std::move(file_filter));
+
+    manifest.share_target = std::move(share_target);
+  }
+
+  UpdateWebAppInfoFromManifest(manifest, &web_app_info);
+
+  {
+    EXPECT_TRUE(web_app_info.share_target.has_value());
+    auto share_target = *web_app_info.share_target;
+    EXPECT_EQ(share_target.action, GURL("http://example.com/share1"));
+    EXPECT_EQ(share_target.method, blink::Manifest::ShareTarget::Method::kPost);
+    EXPECT_EQ(share_target.enctype,
+              blink::Manifest::ShareTarget::Enctype::kMultipartFormData);
+    EXPECT_EQ(share_target.params.title, kTitle);
+    EXPECT_EQ(share_target.params.text, kText);
+    EXPECT_FALSE(share_target.params.url.has_value());
+    EXPECT_EQ(share_target.params.files.size(), 1U);
+    EXPECT_EQ(share_target.params.files[0].name, kImages);
+    EXPECT_EQ(share_target.params.files[0].accept.size(), 2U);
+    EXPECT_EQ(share_target.params.files[0].accept[0], kExtension);
+    EXPECT_EQ(share_target.params.files[0].accept[1], kContentType);
+  }
+
+  {
+    blink::Manifest::ShareTarget share_target;
+    share_target.action = GURL("http://example.com/share2");
+    share_target.method = blink::Manifest::ShareTarget::Method::kGet;
+    share_target.enctype =
+        blink::Manifest::ShareTarget::Enctype::kFormUrlEncoded;
+    share_target.params.text = kText;
+    share_target.params.url = kUrl;
+    manifest.share_target = std::move(share_target);
+  }
+
+  UpdateWebAppInfoFromManifest(manifest, &web_app_info);
+
+  {
+    EXPECT_TRUE(web_app_info.share_target.has_value());
+    auto share_target = *web_app_info.share_target;
+    EXPECT_EQ(share_target.action, GURL("http://example.com/share2"));
+    EXPECT_EQ(share_target.method, blink::Manifest::ShareTarget::Method::kGet);
+    EXPECT_EQ(share_target.enctype,
+              blink::Manifest::ShareTarget::Enctype::kFormUrlEncoded);
+    EXPECT_FALSE(share_target.params.title.has_value());
+    EXPECT_EQ(share_target.params.text, kText);
+    EXPECT_EQ(share_target.params.url, kUrl);
+    EXPECT_TRUE(share_target.params.files.empty());
+  }
+
+  manifest.share_target = base::nullopt;
+  UpdateWebAppInfoFromManifest(manifest, &web_app_info);
+  EXPECT_FALSE(web_app_info.share_target.has_value());
+}
+
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/system_web_app_manager.cc b/chrome/browser/web_applications/system_web_app_manager.cc
index 02bff1a..d368a83 100644
--- a/chrome/browser/web_applications/system_web_app_manager.cc
+++ b/chrome/browser/web_applications/system_web_app_manager.cc
@@ -50,6 +50,7 @@
 #include "chrome/browser/chromeos/web_applications/media_web_app_info.h"
 #include "chrome/browser/chromeos/web_applications/scanning_system_web_app_info.h"
 #include "chrome/browser/chromeos/web_applications/terminal_source.h"
+#include "chrome/browser/chromeos/web_applications/terminal_system_web_app_info.h"
 #include "chromeos/components/help_app_ui/url_constants.h"
 #include "chromeos/components/media_app_ui/url_constants.h"
 #include "chromeos/constants/chromeos_features.h"
@@ -140,8 +141,9 @@
   if (SystemWebAppManager::IsAppEnabled(SystemAppType::TERMINAL)) {
     infos.emplace(
         SystemAppType::TERMINAL,
-        SystemAppInfo("Terminal",
-                      GURL("chrome-untrusted://terminal/html/pwa.html")));
+        SystemAppInfo(
+            "Terminal", GURL(chrome::kChromeUIUntrustedTerminalURL),
+            base::BindRepeating(&CreateWebAppInfoForTerminalSystemWebApp)));
     infos.at(SystemAppType::TERMINAL).single_window = false;
   }
 
diff --git a/chrome/browser/web_applications/web_app_install_finalizer.cc b/chrome/browser/web_applications/web_app_install_finalizer.cc
index b1561871..d3c7cdc 100644
--- a/chrome/browser/web_applications/web_app_install_finalizer.cc
+++ b/chrome/browser/web_applications/web_app_install_finalizer.cc
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "chrome/browser/web_applications/web_app_install_finalizer.h"
+
 #include <utility>
 #include <map>
 #include <vector>
 
-#include "chrome/browser/web_applications/web_app_install_finalizer.h"
-
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/logging.h"
@@ -28,12 +28,11 @@
 #include "chrome/browser/web_applications/components/web_app_shortcuts_menu.h"
 #include "chrome/browser/web_applications/web_app.h"
 #include "chrome/browser/web_applications/web_app_icon_manager.h"
+#include "chrome/browser/web_applications/web_app_installation_utils.h"
 #include "chrome/browser/web_applications/web_app_registry_update.h"
 #include "chrome/browser/web_applications/web_app_sync_bridge.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/web_application_info.h"
-#include "components/services/app_service/public/cpp/file_handler.h"
-#include "components/services/app_service/public/cpp/protocol_handler_info.h"
 #include "content/public/browser/browser_thread.h"
 #include "third_party/skia/include/core/SkColor.h"
 
@@ -97,64 +96,6 @@
   }
 }
 
-std::vector<SquareSizePx> GetSquareSizePxs(
-    const std::map<SquareSizePx, SkBitmap>& icon_bitmaps) {
-  std::vector<SquareSizePx> sizes;
-  sizes.reserve(icon_bitmaps.size());
-  for (const std::pair<const SquareSizePx, SkBitmap>& item : icon_bitmaps)
-    sizes.push_back(item.first);
-  return sizes;
-}
-
-std::vector<std::vector<SquareSizePx>> GetDownloadedShortcutsMenuIconsSizes(
-    const ShortcutsMenuIconsBitmaps& shortcuts_menu_icons_bitmaps) {
-  std::vector<std::vector<SquareSizePx>> shortcuts_menu_icons_sizes;
-  shortcuts_menu_icons_sizes.reserve(shortcuts_menu_icons_bitmaps.size());
-  for (const auto& shortcut_icon_bitmaps : shortcuts_menu_icons_bitmaps) {
-    shortcuts_menu_icons_sizes.emplace_back(
-        GetSquareSizePxs(shortcut_icon_bitmaps));
-  }
-  return shortcuts_menu_icons_sizes;
-}
-
-void SetWebAppFileHandlers(
-    const std::vector<blink::Manifest::FileHandler>& manifest_file_handlers,
-    WebApp* web_app) {
-  apps::FileHandlers web_app_file_handlers;
-
-  for (const auto& manifest_file_handler : manifest_file_handlers) {
-    apps::FileHandler web_app_file_handler;
-    web_app_file_handler.action = manifest_file_handler.action;
-
-    for (const auto& it : manifest_file_handler.accept) {
-      apps::FileHandler::AcceptEntry web_app_accept_entry;
-      web_app_accept_entry.mime_type = base::UTF16ToUTF8(it.first);
-      for (const auto& manifest_file_extension : it.second)
-        web_app_accept_entry.file_extensions.insert(
-            base::UTF16ToUTF8(manifest_file_extension));
-      web_app_file_handler.accept.push_back(std::move(web_app_accept_entry));
-    }
-
-    web_app_file_handlers.push_back(std::move(web_app_file_handler));
-  }
-
-  web_app->SetFileHandlers(std::move(web_app_file_handlers));
-}
-
-void SetWebAppProtocolHandlers(
-    const std::vector<blink::Manifest::ProtocolHandler>& protocol_handlers,
-    WebApp* web_app) {
-  std::vector<apps::ProtocolHandlerInfo> web_app_protocol_handlers;
-  for (const auto& handler : protocol_handlers) {
-    apps::ProtocolHandlerInfo protocol_handler_info;
-    protocol_handler_info.protocol = base::UTF16ToUTF8(handler.protocol);
-    protocol_handler_info.url = handler.url;
-    web_app_protocol_handlers.push_back(std::move(protocol_handler_info));
-  }
-
-  web_app->SetProtocolHandlers(web_app_protocol_handlers);
-}
-
 }  // namespace
 
 WebAppInstallFinalizer::WebAppInstallFinalizer(
@@ -425,50 +366,7 @@
     const WebApplicationInfo& web_app_info,
     std::unique_ptr<WebApp> web_app,
     CommitCallback commit_callback) {
-  DCHECK(!web_app_info.title.empty());
-  web_app->SetName(base::UTF16ToUTF8(web_app_info.title));
-
-  web_app->SetDisplayMode(web_app_info.display_mode);
-  web_app->SetDisplayModeOverride(web_app_info.display_override);
-
-  web_app->SetDescription(base::UTF16ToUTF8(web_app_info.description));
-  web_app->SetScope(web_app_info.scope);
-  DCHECK(!web_app_info.theme_color.has_value() ||
-         SkColorGetA(*web_app_info.theme_color) == SK_AlphaOPAQUE);
-  web_app->SetThemeColor(web_app_info.theme_color);
-  DCHECK(!web_app_info.background_color.has_value() ||
-         SkColorGetA(*web_app_info.background_color) == SK_AlphaOPAQUE);
-  web_app->SetBackgroundColor(web_app_info.background_color);
-
-  WebApp::SyncFallbackData sync_fallback_data;
-  sync_fallback_data.name = base::UTF16ToUTF8(web_app_info.title);
-  sync_fallback_data.theme_color = web_app_info.theme_color;
-  sync_fallback_data.scope = web_app_info.scope;
-  sync_fallback_data.icon_infos = web_app_info.icon_infos;
-  web_app->SetSyncFallbackData(std::move(sync_fallback_data));
-
-  web_app->SetIconInfos(web_app_info.icon_infos);
-  web_app->SetDownloadedIconSizes(
-      IconPurpose::ANY, GetSquareSizePxs(web_app_info.icon_bitmaps_any));
-  web_app->SetDownloadedIconSizes(
-      IconPurpose::MASKABLE,
-      GetSquareSizePxs(web_app_info.icon_bitmaps_maskable));
-  web_app->SetIsGeneratedIcon(web_app_info.is_generated_icon);
-
-  web_app->SetShortcutsMenuItemInfos(web_app_info.shortcuts_menu_item_infos);
-  web_app->SetDownloadedShortcutsMenuIconsSizes(
-      GetDownloadedShortcutsMenuIconsSizes(
-          web_app_info.shortcuts_menu_icons_bitmaps));
-
-  SetWebAppFileHandlers(web_app_info.file_handlers, web_app.get());
-  SetWebAppProtocolHandlers(web_app_info.protocol_handlers, web_app.get());
-
-  if (base::FeatureList::IsEnabled(features::kDesktopPWAsRunOnOsLogin) &&
-      web_app_info.run_on_os_login) {
-    // TODO(crbug.com/1091964): Obtain actual mode, currently set to the
-    // default (windowed).
-    web_app->SetRunOnOsLoginMode(RunOnOsLoginMode::kWindowed);
-  }
+  SetWebAppManifestFields(web_app_info, *web_app);
 
   AppId app_id = web_app->app_id();
   IconBitmaps icon_bitmaps;
diff --git a/chrome/browser/web_applications/web_app_installation_utils.cc b/chrome/browser/web_applications/web_app_installation_utils.cc
new file mode 100644
index 0000000..24edd08
--- /dev/null
+++ b/chrome/browser/web_applications/web_app_installation_utils.cc
@@ -0,0 +1,200 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/web_applications/web_app_installation_utils.h"
+
+#include <map>
+#include <utility>
+#include <vector>
+
+#include "base/check.h"
+#include "base/feature_list.h"
+#include "base/notreached.h"
+#include "base/optional.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/web_applications/web_app.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/common/web_application_info.h"
+#include "components/services/app_service/public/cpp/file_handler.h"
+#include "components/services/app_service/public/cpp/protocol_handler_info.h"
+#include "components/services/app_service/public/cpp/share_target.h"
+#include "third_party/blink/public/common/manifest/manifest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkColor.h"
+
+namespace web_app {
+
+namespace {
+
+std::vector<SquareSizePx> GetSquareSizePxs(
+    const std::map<SquareSizePx, SkBitmap>& icon_bitmaps) {
+  std::vector<SquareSizePx> sizes;
+  sizes.reserve(icon_bitmaps.size());
+  for (const std::pair<const SquareSizePx, SkBitmap>& item : icon_bitmaps)
+    sizes.push_back(item.first);
+  return sizes;
+}
+
+std::vector<std::vector<SquareSizePx>> GetDownloadedShortcutsMenuIconsSizes(
+    const ShortcutsMenuIconsBitmaps& shortcuts_menu_icons_bitmaps) {
+  std::vector<std::vector<SquareSizePx>> shortcuts_menu_icons_sizes;
+  shortcuts_menu_icons_sizes.reserve(shortcuts_menu_icons_bitmaps.size());
+  for (const auto& shortcut_icon_bitmaps : shortcuts_menu_icons_bitmaps) {
+    shortcuts_menu_icons_sizes.emplace_back(
+        GetSquareSizePxs(shortcut_icon_bitmaps));
+  }
+  return shortcuts_menu_icons_sizes;
+}
+
+apps::ShareTarget::Method ToAppsShareTargetMethod(
+    blink::Manifest::ShareTarget::Method method) {
+  switch (method) {
+    case blink::Manifest::ShareTarget::Method::kGet:
+      return apps::ShareTarget::Method::kGet;
+    case blink::Manifest::ShareTarget::Method::kPost:
+      return apps::ShareTarget::Method::kPost;
+  }
+  NOTREACHED();
+}
+
+apps::ShareTarget::Enctype ToAppsShareTargetEnctype(
+    blink::Manifest::ShareTarget::Enctype enctype) {
+  switch (enctype) {
+    case blink::Manifest::ShareTarget::Enctype::kFormUrlEncoded:
+      return apps::ShareTarget::Enctype::kFormUrlEncoded;
+    case blink::Manifest::ShareTarget::Enctype::kMultipartFormData:
+      return apps::ShareTarget::Enctype::kMultipartFormData;
+  }
+  NOTREACHED();
+}
+
+void SetWebAppFileHandlers(
+    const std::vector<blink::Manifest::FileHandler>& manifest_file_handlers,
+    WebApp& web_app) {
+  apps::FileHandlers web_app_file_handlers;
+
+  for (const auto& manifest_file_handler : manifest_file_handlers) {
+    apps::FileHandler web_app_file_handler;
+    web_app_file_handler.action = manifest_file_handler.action;
+
+    for (const auto& it : manifest_file_handler.accept) {
+      apps::FileHandler::AcceptEntry web_app_accept_entry;
+      web_app_accept_entry.mime_type = base::UTF16ToUTF8(it.first);
+      for (const auto& manifest_file_extension : it.second)
+        web_app_accept_entry.file_extensions.insert(
+            base::UTF16ToUTF8(manifest_file_extension));
+      web_app_file_handler.accept.push_back(std::move(web_app_accept_entry));
+    }
+
+    web_app_file_handlers.push_back(std::move(web_app_file_handler));
+  }
+
+  web_app.SetFileHandlers(std::move(web_app_file_handlers));
+}
+
+void SetWebAppShareTarget(
+    const base::Optional<blink::Manifest::ShareTarget>& share_target,
+    WebApp& web_app) {
+  if (!share_target) {
+    web_app.SetShareTarget(base::nullopt);
+    return;
+  }
+  apps::ShareTarget apps_share_target;
+  apps_share_target.action = share_target->action;
+  apps_share_target.method = ToAppsShareTargetMethod(share_target->method);
+  apps_share_target.enctype = ToAppsShareTargetEnctype(share_target->enctype);
+
+  if (share_target->params.title.has_value()) {
+    apps_share_target.params.title =
+        base::UTF16ToUTF8(*share_target->params.title);
+  }
+  if (share_target->params.text.has_value()) {
+    apps_share_target.params.text =
+        base::UTF16ToUTF8(*share_target->params.text);
+  }
+  if (share_target->params.url.has_value()) {
+    apps_share_target.params.url = base::UTF16ToUTF8(*share_target->params.url);
+  }
+
+  for (const auto& file_filter : share_target->params.files) {
+    apps::ShareTarget::Files apps_share_target_files;
+    apps_share_target_files.name = base::UTF16ToUTF8(file_filter.name);
+
+    for (const auto& file_type : file_filter.accept) {
+      apps_share_target_files.accept.push_back(base::UTF16ToUTF8(file_type));
+    }
+
+    apps_share_target.params.files.push_back(
+        std::move(apps_share_target_files));
+  }
+
+  web_app.SetShareTarget(std::move(apps_share_target));
+}
+
+void SetWebAppProtocolHandlers(
+    const std::vector<blink::Manifest::ProtocolHandler>& protocol_handlers,
+    WebApp& web_app) {
+  std::vector<apps::ProtocolHandlerInfo> web_app_protocol_handlers;
+  for (const auto& handler : protocol_handlers) {
+    apps::ProtocolHandlerInfo protocol_handler_info;
+    protocol_handler_info.protocol = base::UTF16ToUTF8(handler.protocol);
+    protocol_handler_info.url = handler.url;
+    web_app_protocol_handlers.push_back(std::move(protocol_handler_info));
+  }
+
+  web_app.SetProtocolHandlers(web_app_protocol_handlers);
+}
+
+}  // namespace
+
+void SetWebAppManifestFields(const WebApplicationInfo& web_app_info,
+                             WebApp& web_app) {
+  DCHECK(!web_app_info.title.empty());
+  web_app.SetName(base::UTF16ToUTF8(web_app_info.title));
+
+  web_app.SetDisplayMode(web_app_info.display_mode);
+  web_app.SetDisplayModeOverride(web_app_info.display_override);
+
+  web_app.SetDescription(base::UTF16ToUTF8(web_app_info.description));
+  web_app.SetScope(web_app_info.scope);
+  DCHECK(!web_app_info.theme_color.has_value() ||
+         SkColorGetA(*web_app_info.theme_color) == SK_AlphaOPAQUE);
+  web_app.SetThemeColor(web_app_info.theme_color);
+  DCHECK(!web_app_info.background_color.has_value() ||
+         SkColorGetA(*web_app_info.background_color) == SK_AlphaOPAQUE);
+  web_app.SetBackgroundColor(web_app_info.background_color);
+
+  WebApp::SyncFallbackData sync_fallback_data;
+  sync_fallback_data.name = base::UTF16ToUTF8(web_app_info.title);
+  sync_fallback_data.theme_color = web_app_info.theme_color;
+  sync_fallback_data.scope = web_app_info.scope;
+  sync_fallback_data.icon_infos = web_app_info.icon_infos;
+  web_app.SetSyncFallbackData(std::move(sync_fallback_data));
+
+  web_app.SetIconInfos(web_app_info.icon_infos);
+  web_app.SetDownloadedIconSizes(
+      IconPurpose::ANY, GetSquareSizePxs(web_app_info.icon_bitmaps_any));
+  web_app.SetDownloadedIconSizes(
+      IconPurpose::MASKABLE,
+      GetSquareSizePxs(web_app_info.icon_bitmaps_maskable));
+  web_app.SetIsGeneratedIcon(web_app_info.is_generated_icon);
+
+  web_app.SetShortcutsMenuItemInfos(web_app_info.shortcuts_menu_item_infos);
+  web_app.SetDownloadedShortcutsMenuIconsSizes(
+      GetDownloadedShortcutsMenuIconsSizes(
+          web_app_info.shortcuts_menu_icons_bitmaps));
+
+  SetWebAppFileHandlers(web_app_info.file_handlers, web_app);
+  SetWebAppShareTarget(web_app_info.share_target, web_app);
+  SetWebAppProtocolHandlers(web_app_info.protocol_handlers, web_app);
+
+  if (base::FeatureList::IsEnabled(features::kDesktopPWAsRunOnOsLogin) &&
+      web_app_info.run_on_os_login) {
+    // TODO(crbug.com/1091964): Obtain actual mode, currently set to the
+    // default (windowed).
+    web_app.SetRunOnOsLoginMode(RunOnOsLoginMode::kWindowed);
+  }
+}
+
+}  // namespace web_app
diff --git a/chrome/browser/web_applications/web_app_installation_utils.h b/chrome/browser/web_applications/web_app_installation_utils.h
new file mode 100644
index 0000000..8c28c6a
--- /dev/null
+++ b/chrome/browser/web_applications/web_app_installation_utils.h
@@ -0,0 +1,20 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_INSTALLATION_UTILS_H_
+#define CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_INSTALLATION_UTILS_H_
+
+struct WebApplicationInfo;
+
+namespace web_app {
+
+class WebApp;
+
+// Updates |web_app| using |web_app_info|
+void SetWebAppManifestFields(const WebApplicationInfo& web_app_info,
+                             WebApp& web_app);
+
+}  // namespace web_app
+
+#endif  // CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_INSTALLATION_UTILS_H_
diff --git a/chrome/browser/web_applications/web_app_installation_utils_unittest.cc b/chrome/browser/web_applications/web_app_installation_utils_unittest.cc
new file mode 100644
index 0000000..2494e6e
--- /dev/null
+++ b/chrome/browser/web_applications/web_app_installation_utils_unittest.cc
@@ -0,0 +1,100 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/web_applications/web_app_installation_utils.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/optional.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/web_applications/components/web_app_helpers.h"
+#include "chrome/browser/web_applications/web_app.h"
+#include "chrome/common/web_application_info.h"
+#include "components/services/app_service/public/cpp/share_target.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/manifest/manifest.h"
+#include "url/gurl.h"
+
+namespace web_app {
+
+TEST(WebAppInstallationUtils, SetWebAppManifestFields_ShareTarget) {
+  WebApplicationInfo web_app_info;
+  web_app_info.app_url = GURL("https://www.chromium.org/index.html");
+  web_app_info.scope = web_app_info.app_url.GetWithoutFilename();
+  web_app_info.title = base::ASCIIToUTF16("App Name");
+
+  const AppId app_id = GenerateAppIdFromURL(web_app_info.app_url);
+  auto web_app = std::make_unique<WebApp>(app_id);
+
+  {
+    blink::Manifest::ShareTarget share_target;
+    share_target.action = GURL("http://example.com/share1");
+    share_target.method = blink::Manifest::ShareTarget::Method::kPost;
+    share_target.enctype =
+        blink::Manifest::ShareTarget::Enctype::kMultipartFormData;
+    share_target.params.title = base::ASCIIToUTF16("kTitle");
+    share_target.params.text = base::ASCIIToUTF16("kText");
+
+    blink::Manifest::FileFilter file_filter;
+    file_filter.name = base::ASCIIToUTF16("kImages");
+    file_filter.accept.push_back(base::ASCIIToUTF16(".png"));
+    file_filter.accept.push_back(base::ASCIIToUTF16("image/png"));
+    share_target.params.files.push_back(std::move(file_filter));
+
+    web_app_info.share_target = std::move(share_target);
+  }
+
+  SetWebAppManifestFields(web_app_info, *web_app);
+
+  {
+    EXPECT_TRUE(web_app->share_target().has_value());
+    auto share_target = *web_app->share_target();
+    EXPECT_EQ(share_target.action, GURL("http://example.com/share1"));
+    EXPECT_EQ(share_target.method, apps::ShareTarget::Method::kPost);
+    EXPECT_EQ(share_target.enctype,
+              apps::ShareTarget::Enctype::kMultipartFormData);
+    EXPECT_EQ(share_target.params.title, "kTitle");
+    EXPECT_EQ(share_target.params.text, "kText");
+    EXPECT_TRUE(share_target.params.url.empty());
+    EXPECT_EQ(share_target.params.files.size(), 1U);
+    EXPECT_EQ(share_target.params.files[0].name, "kImages");
+    EXPECT_EQ(share_target.params.files[0].accept.size(), 2U);
+    EXPECT_EQ(share_target.params.files[0].accept[0], ".png");
+    EXPECT_EQ(share_target.params.files[0].accept[1], "image/png");
+  }
+
+  {
+    blink::Manifest::ShareTarget share_target;
+    share_target.action = GURL("http://example.com/share2");
+    share_target.method = blink::Manifest::ShareTarget::Method::kGet;
+    share_target.enctype =
+        blink::Manifest::ShareTarget::Enctype::kFormUrlEncoded;
+    share_target.params.text = base::ASCIIToUTF16("kText");
+    share_target.params.url = base::ASCIIToUTF16("kUrl");
+    web_app_info.share_target = std::move(share_target);
+  }
+
+  SetWebAppManifestFields(web_app_info, *web_app);
+
+  {
+    EXPECT_TRUE(web_app->share_target().has_value());
+    auto share_target = *web_app->share_target();
+    EXPECT_EQ(share_target.action, GURL("http://example.com/share2"));
+    EXPECT_EQ(share_target.method, apps::ShareTarget::Method::kGet);
+    EXPECT_EQ(share_target.enctype,
+              apps::ShareTarget::Enctype::kFormUrlEncoded);
+    EXPECT_TRUE(share_target.params.title.empty());
+    EXPECT_EQ(share_target.params.text, "kText");
+    EXPECT_EQ(share_target.params.url, "kUrl");
+    EXPECT_TRUE(share_target.params.files.empty());
+  }
+
+  web_app_info.share_target = base::nullopt;
+  SetWebAppManifestFields(web_app_info, *web_app);
+  EXPECT_FALSE(web_app->share_target().has_value());
+}
+
+}  // namespace web_app
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 840deeac2..7cc528f 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-master-1599760756-c4035dc0b0474f5a6f2e7d19fc7457941683bae1.profdata
+chrome-linux-master-1599803297-c5d0441082dd5a8fa2a2c84cdf40fe1c77138c5e.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 08ff6d3..8330ac0 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-master-1599760756-5eeaad3a0935fac8a24777486ae90bd2f43c056f.profdata
+chrome-mac-master-1599781911-9c360a18426c47cd39d0c24797835d08f90d685a.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 35f8b78..cf67af9 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-master-1599738815-40f6ae90536e6d0f415f1d4a37e068148427b479.profdata
+chrome-win64-master-1599760756-ff576999efc14edb97330a2a91d8abce13fe0e29.profdata
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index ddc5624d..8d697a8 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -323,7 +323,7 @@
 // The plan is for this to go away once tagged PDFs become the default.
 // See https://crbug.com/607777
 const base::Feature kExportTaggedPDF{"ExportTaggedPDF",
-                                     base::FEATURE_DISABLED_BY_DEFAULT};
+                                     base::FEATURE_ENABLED_BY_DEFAULT};
 
 // If enabled, this feature's |kExternalInstallDefaultButtonKey| field trial
 // parameter value controls which |ExternalInstallBubbleAlert| button is the
diff --git a/chrome/common/web_application_info.h b/chrome/common/web_application_info.h
index b915ebe..a9d99bb 100644
--- a/chrome/common/web_application_info.h
+++ b/chrome/common/web_application_info.h
@@ -152,6 +152,9 @@
   // The extensions and mime types the app can handle.
   std::vector<blink::Manifest::FileHandler> file_handlers;
 
+  // File types the app accepts as a Web Share Target.
+  base::Optional<blink::Manifest::ShareTarget> share_target;
+
   // Additional search terms that users can use to find the app.
   std::vector<std::string> additional_search_terms;
 
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/tabswitcher/TabSwitcherController.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/tabswitcher/TabSwitcherController.java
index 2971f533..36ef29b4 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/tabswitcher/TabSwitcherController.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/tabswitcher/TabSwitcherController.java
@@ -19,10 +19,8 @@
  */
 public class TabSwitcherController extends PageController {
     private static final Pattern PATTERN_NUMBER_OF_OPEN_TABS = Pattern.compile("^(\\d+) .*");
-    private static final IUi2Locator LOCATOR_CLOSE_ALL_TABS =
-            Ui2Locators.withAnyResEntry(R.id.close_all_tabs_button);
     private static final IUi2Locator LOCATOR_NEW_TAB =
-            Ui2Locators.withAnyResEntry(R.id.tab_switcher_new_tab_button, R.id.new_tab_button);
+            Ui2Locators.withAnyResEntry(R.id.new_tab_button);
     private static final IUi2Locator LOCATOR_TAB_SWITCHER_BUTTON = Ui2Locators.withAnyResEntry(
             R.id.tab_switcher_button, R.id.tab_switcher_mode_tab_switcher_button);
     private static final IUi2Locator LOCATOR_MENU = Ui2Locators.withAnyResEntry(R.id.menu_button);
@@ -36,13 +34,7 @@
     private TabSwitcherController() {}
 
     public void clickCloseAllTabs() {
-        // Default to  use the close all tabs button.
-        if (mLocatorHelper.isOnScreen(LOCATOR_CLOSE_ALL_TABS)) {
-            mUtils.click(LOCATOR_CLOSE_ALL_TABS);
-        } else {
-            // If it's not found for whatever reason, then do it through the menu.
-            clickMenu().clickCloseAllTabs();
-        }
+        clickMenu().clickCloseAllTabs();
     }
 
     public void clickTabSwitcher() {
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ToolbarTestUtils.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ToolbarTestUtils.java
index 2407c9b9..2ea73fb6 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ToolbarTestUtils.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ToolbarTestUtils.java
@@ -25,7 +25,6 @@
     // Res ids of views being tested.
     public static final @IdRes int TOP_TOOLBAR = R.id.toolbar;
     public static final @IdRes int TAB_SWITCHER_TOOLBAR = R.id.tab_switcher_toolbar;
-    public static final @IdRes int BOTTOM_TOOLBAR = R.id.bottom_toolbar;
 
     public static final @IdRes int TOP_TOOLBAR_MENU = R.id.menu_button_wrapper;
     public static final @IdRes int TOP_TOOLBAR_HOME = R.id.home_button;
@@ -37,12 +36,6 @@
     public static final @IdRes int TAB_SWITCHER_TOOLBAR_TAB_SWITCHER_BUTTON =
             R.id.tab_switcher_mode_tab_switcher_button;
 
-    public static final @IdRes int BOTTOM_TOOLBAR_HOME = R.id.bottom_home_button;
-    public static final @IdRes int BOTTOM_TOOLBAR_SEARCH = R.id.search_accelerator;
-    public static final @IdRes int BOTTOM_TOOLBAR_SHARE = R.id.bottom_share_button;
-    public static final @IdRes int BOTTOM_TOOLBAR_NEW_TAB = R.id.bottom_new_tab_button;
-    public static final @IdRes int BOTTOM_TOOLBAR_TAB_SWITCHER = R.id.bottom_tab_switcher_button;
-
     public static void checkToolbarVisibility(@IdRes int toolbarId, boolean isVisible) {
         onView(withId(toolbarId)).check(matches(isVisible ? isDisplayed() : not(isDisplayed())));
     }
diff --git a/chrome/test/data/webui/settings/chromeos/ambient_mode_page_test.js b/chrome/test/data/webui/settings/chromeos/ambient_mode_page_test.js
index 1806b7ac..34cb64a 100644
--- a/chrome/test/data/webui/settings/chromeos/ambient_mode_page_test.js
+++ b/chrome/test/data/webui/settings/chromeos/ambient_mode_page_test.js
@@ -147,6 +147,16 @@
     assertEquals(2, topicSourceItems.length);
   });
 
+  test('topicSourceItemHasCorrectRowHeight', function() {
+    const topicSourceListElement = ambientModePage.$$('topic-source-list');
+    const ironList = topicSourceListElement.$$('iron-list');
+    const topicSourceItems = ironList.querySelectorAll('topic-source-item');
+
+    topicSourceItems.forEach((row) => {
+      assertEquals(64, row.offsetHeight);
+    });
+  });
+
   test('Deep link to topic sources', async () => {
     loadTimeData.overrideValues({isDeepLinkingEnabled: true});
     assertTrue(loadTimeData.getBoolean('isDeepLinkingEnabled'));
diff --git a/chromecast/browser/accessibility/flutter/BUILD.gn b/chromecast/browser/accessibility/flutter/BUILD.gn
index 88815d5c..b9043f10 100644
--- a/chromecast/browser/accessibility/flutter/BUILD.gn
+++ b/chromecast/browser/accessibility/flutter/BUILD.gn
@@ -38,6 +38,7 @@
     ":accessibility",
     "//base",
     "//base/test:run_all_unittests",
+    "//chromecast:cast_shell_lib",
     "//chromecast/browser",
     "//extensions/browser/api",
     "//skia",
diff --git a/chromecast/browser/cast_content_browser_client.cc b/chromecast/browser/cast_content_browser_client.cc
index 0c97882..9099ed3cf 100644
--- a/chromecast/browser/cast_content_browser_client.cc
+++ b/chromecast/browser/cast_content_browser_client.cc
@@ -80,7 +80,6 @@
 #include "content/public/common/content_switches.h"
 #include "content/public/common/service_names.mojom.h"
 #include "content/public/common/url_constants.h"
-#include "content/public/common/web_preferences.h"
 #include "media/audio/audio_thread_impl.h"
 #include "media/base/media_switches.h"
 #include "media/mojo/services/mojo_renderer_service.h"
@@ -90,6 +89,7 @@
 #include "services/network/public/cpp/network_switches.h"
 #include "services/service_manager/embedder/descriptors.h"
 #include "third_party/blink/public/common/features.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
 #include "ui/gl/gl_switches.h"
@@ -527,7 +527,7 @@
 
 void CastContentBrowserClient::OverrideWebkitPrefs(
     content::RenderViewHost* render_view_host,
-    content::WebPreferences* prefs) {
+    blink::web_pref::WebPreferences* prefs) {
   prefs->allow_scripts_to_close_windows = true;
   // TODO(halliwell): http://crbug.com/391089. This pref defaults to to true
   // because some content providers such as YouTube use plain http requests
@@ -550,7 +550,7 @@
   // 1280px wide layout viewport by default.
   DCHECK(prefs->viewport_enabled);
   DCHECK(prefs->viewport_meta_enabled);
-  prefs->viewport_style = content::ViewportStyle::TELEVISION;
+  prefs->viewport_style = blink::web_pref::ViewportStyle::TELEVISION;
 #endif  // defined(OS_ANDROID)
 
   // Disable WebSQL databases by default.
diff --git a/chromecast/browser/cast_content_browser_client.h b/chromecast/browser/cast_content_browser_client.h
index c8e7a19..ebf2f2a 100644
--- a/chromecast/browser/cast_content_browser_client.h
+++ b/chromecast/browser/cast_content_browser_client.h
@@ -162,7 +162,7 @@
   std::string GetAcceptLangs(content::BrowserContext* context) override;
   network::mojom::NetworkContext* GetSystemNetworkContext() override;
   void OverrideWebkitPrefs(content::RenderViewHost* render_view_host,
-                           content::WebPreferences* prefs) override;
+                           blink::web_pref::WebPreferences* prefs) override;
   std::string GetApplicationLocale() override;
   scoped_refptr<content::QuotaPermissionContext> CreateQuotaPermissionContext()
       override;
diff --git a/chromecast/browser/cast_web_preferences.cc b/chromecast/browser/cast_web_preferences.cc
index dd598e3c..2d9d59b 100644
--- a/chromecast/browser/cast_web_preferences.cc
+++ b/chromecast/browser/cast_web_preferences.cc
@@ -13,7 +13,7 @@
 
 CastWebPreferences::CastWebPreferences() = default;
 
-void CastWebPreferences::Update(content::WebPreferences* prefs) {
+void CastWebPreferences::Update(blink::web_pref::WebPreferences* prefs) {
   if (preferences_.autoplay_policy)
     prefs->autoplay_policy = preferences_.autoplay_policy.value();
 
diff --git a/chromecast/browser/cast_web_preferences.h b/chromecast/browser/cast_web_preferences.h
index 18c8876..fc5a21d 100644
--- a/chromecast/browser/cast_web_preferences.h
+++ b/chromecast/browser/cast_web_preferences.h
@@ -7,7 +7,7 @@
 
 #include <base/optional.h>
 #include <base/supports_user_data.h>
-#include <content/public/common/web_preferences.h>
+#include <third_party/blink/public/common/web_preferences/web_preferences.h>
 
 namespace chromecast {
 
@@ -18,7 +18,7 @@
   struct Preferences {
     Preferences();
 
-    base::Optional<content::AutoplayPolicy> autoplay_policy;
+    base::Optional<blink::web_pref::AutoplayPolicy> autoplay_policy;
     base::Optional<bool> hide_scrollbars;
     base::Optional<bool> javascript_enabled;
   };
@@ -34,7 +34,7 @@
   Preferences* preferences() { return &preferences_; }
 
   // Overrides |prefs| with any locally stored preferences.
-  void Update(content::WebPreferences* prefs);
+  void Update(blink::web_pref::WebPreferences* prefs);
 
  private:
   Preferences preferences_;
diff --git a/chromecast/browser/webview/webview_controller.cc b/chromecast/browser/webview/webview_controller.cc
index 8398339..7cb14ba8 100644
--- a/chromecast/browser/webview/webview_controller.cc
+++ b/chromecast/browser/webview/webview_controller.cc
@@ -23,7 +23,7 @@
 #include "content/public/browser/render_widget_host.h"
 #include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/web_contents.h"
-#include "content/public/common/web_preferences.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 
 namespace chromecast {
 
@@ -51,7 +51,8 @@
 
 void UpdateWebkitPreferences(content::WebContents* web_contents,
                              CastWebPreferences* cast_prefs) {
-  content::WebPreferences prefs = web_contents->GetOrCreateWebPreferences();
+  blink::web_pref::WebPreferences prefs =
+      web_contents->GetOrCreateWebPreferences();
   cast_prefs->Update(&prefs);
   web_contents->SetWebPreferences(prefs);
 }
@@ -196,8 +197,8 @@
 
   cast_prefs->preferences()->autoplay_policy =
       request.require_user_gesture()
-          ? content::AutoplayPolicy::kUserGestureRequired
-          : content::AutoplayPolicy::kNoUserGestureRequired;
+          ? blink::web_pref::AutoplayPolicy::kUserGestureRequired
+          : blink::web_pref::AutoplayPolicy::kNoUserGestureRequired;
   UpdateWebkitPreferences(contents, cast_prefs);
 }
 
diff --git a/chromecast/media/DEPS b/chromecast/media/DEPS
index ee32283..4ba981c 100644
--- a/chromecast/media/DEPS
+++ b/chromecast/media/DEPS
@@ -14,6 +14,7 @@
   "+mojo/public/cpp/bindings",
   "+ui/gfx/geometry",
   "+ui/gfx/overlay_transform.h",
+  "+ui/gl/hdr_metadata.h",
   "+services/service_manager/public",
   "+third_party/blink/public/common/tokens/tokens.h",
   "+third_party/blink/public/platform/audio/web_audio_device_source_type.h",
diff --git a/chromecast/media/cma/base/decoder_config_adapter.cc b/chromecast/media/cma/base/decoder_config_adapter.cc
index 7131b43..bcbf82e5 100644
--- a/chromecast/media/cma/base/decoder_config_adapter.cc
+++ b/chromecast/media/cma/base/decoder_config_adapter.cc
@@ -10,6 +10,7 @@
 #include "media/base/channel_layout.h"
 #include "media/base/encryption_pattern.h"
 #include "media/base/encryption_scheme.h"
+#include "ui/gl/hdr_metadata.h"
 
 namespace chromecast {
 namespace media {
@@ -331,7 +332,7 @@
   video_config.matrix = static_cast<MatrixID>(config.color_space_info().matrix);
   video_config.range = static_cast<RangeID>(config.color_space_info().range);
 
-  base::Optional<::media::HDRMetadata> hdr_metadata = config.hdr_metadata();
+  base::Optional<::gl::HDRMetadata> hdr_metadata = config.hdr_metadata();
   if (hdr_metadata) {
     video_config.have_hdr_metadata = true;
     video_config.hdr_metadata.max_content_light_level =
diff --git a/chromecast/renderer/cast_content_renderer_client.cc b/chromecast/renderer/cast_content_renderer_client.cc
index ec3047d..6edd2c8e 100644
--- a/chromecast/renderer/cast_content_renderer_client.cc
+++ b/chromecast/renderer/cast_content_renderer_client.cc
@@ -313,10 +313,10 @@
 // TODO(servolk): make use of eotf.
 
   // TODO(1066567): Check attached screen for support of type.hdr_metadata_type.
-  if (type.hdr_metadata_type != ::media::HdrMetadataType::kNone) {
-    NOTIMPLEMENTED() << "HdrMetadataType support signaling not implemented.";
-    return false;
-  }
+if (type.hdr_metadata_type != ::gl::HdrMetadataType::kNone) {
+  NOTIMPLEMENTED() << "HdrMetadataType support signaling not implemented.";
+  return false;
+}
 
 #if defined(OS_ANDROID)
   return supported_profiles_->IsSupportedVideoConfig(
diff --git a/chromeos/BUILD.gn b/chromeos/BUILD.gn
index 3e963b16..5f7121c 100644
--- a/chromeos/BUILD.gn
+++ b/chromeos/BUILD.gn
@@ -24,11 +24,6 @@
   is_printing_ppd_provider_v3 = false
 }
 
-buildflag_header("chromeos_buildflags") {
-  header = "chromeos_buildflags.h"
-  flags = [ "IS_CHROMEOS_DEVICE=$is_chromeos_device" ]
-}
-
 component("chromeos") {
   configs += [
     ":chromeos_implementation",
@@ -161,7 +156,6 @@
   ]
 
   deps = [
-    ":chromeos_buildflags",
     ":test_support",
     "//base/test:test_support",
     "//build:chromeos_buildflags",
diff --git a/chromeos/audio/cras_audio_handler.cc b/chromeos/audio/cras_audio_handler.cc
index ea610ea..9be1d19 100644
--- a/chromeos/audio/cras_audio_handler.cc
+++ b/chromeos/audio/cras_audio_handler.cc
@@ -859,6 +859,10 @@
     observer.OnBluetoothBatteryChanged(address, level);
 }
 
+void CrasAudioHandler::ResendBluetoothBattery() {
+  CrasAudioClient::Get()->ResendBluetoothBattery();
+}
+
 void CrasAudioHandler::OnAudioPolicyPrefChanged() {
   ApplyAudioPolicy();
 }
diff --git a/chromeos/audio/cras_audio_handler.h b/chromeos/audio/cras_audio_handler.h
index 7f1bea0..4325b9b 100644
--- a/chromeos/audio/cras_audio_handler.h
+++ b/chromeos/audio/cras_audio_handler.h
@@ -322,6 +322,11 @@
   // returned.
   int32_t system_aec_group_id() const;
 
+  // Asks  CRAS to resend BluetoothBatteryChanged signal, used in cases when
+  // Chrome cleans up the stored battery information but still has the device
+  // connected afterward. For example: User logout.
+  void ResendBluetoothBattery();
+
  protected:
   CrasAudioHandler(
       mojo::PendingRemote<media_session::mojom::MediaControllerManager>
diff --git a/chromeos/components/diagnostics_ui/resources/BUILD.gn b/chromeos/components/diagnostics_ui/resources/BUILD.gn
index 5528602..142d021 100644
--- a/chromeos/components/diagnostics_ui/resources/BUILD.gn
+++ b/chromeos/components/diagnostics_ui/resources/BUILD.gn
@@ -9,11 +9,21 @@
 
 js_type_check("closure_compile_module") {
   is_polymer3 = true
-  deps = [ ":diagnostics_app" ]
+  deps = [
+    ":diagnostics_app",
+    ":diagnostics_card",
+  ]
 }
 
 js_library("diagnostics_app") {
   deps = [
+    ":diagnostics_card",
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+  ]
+}
+
+js_library("diagnostics_card") {
+  deps = [
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
   ]
 }
@@ -21,6 +31,7 @@
 html_to_js("web_components") {
   js_files = [
     "diagnostics_app.js",
+    "diagnostics_card.js",
     "diagnostics_shared_css.js",
   ]
 }
diff --git a/chromeos/components/diagnostics_ui/resources/diagnostics_card.html b/chromeos/components/diagnostics_ui/resources/diagnostics_card.html
new file mode 100644
index 0000000..9b742f1
--- /dev/null
+++ b/chromeos/components/diagnostics_ui/resources/diagnostics_card.html
@@ -0,0 +1,12 @@
+<style include="diagnostics-shared">
+</style>
+
+<div id="card-wrapper">
+  <div id="title" class="title-container">
+    <slot name="title"></slot>
+  </div>
+
+  <div id="body" class="body-container">
+    <slot name="body"></slot>
+  </div>
+</div>
diff --git a/chromeos/components/diagnostics_ui/resources/diagnostics_card.js b/chromeos/components/diagnostics_ui/resources/diagnostics_card.js
new file mode 100644
index 0000000..0f982fc
--- /dev/null
+++ b/chromeos/components/diagnostics_ui/resources/diagnostics_card.js
@@ -0,0 +1,19 @@
+// Copyright 2020 The Chromium 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 './diagnostics_shared_css.js';
+
+import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+/**
+ * @fileoverview
+ * 'diagnostics-card' is a styling wrapper for each component's diagnostic
+ * card.
+ */
+Polymer({
+  is: 'diagnostics-card',
+
+  _template: html`{__html_template__}`,
+
+});
diff --git a/chromeos/constants/chromeos_switches.cc b/chromeos/constants/chromeos_switches.cc
index 93b4cc8..173dc2c 100644
--- a/chromeos/constants/chromeos_switches.cc
+++ b/chromeos/constants/chromeos_switches.cc
@@ -431,6 +431,15 @@
 // path should be to a directory that contains a binary named 'chrome'.
 const char kLacrosChromePath[] = "lacros-chrome-path";
 
+// If set, ash-chrome will drop a Unix domain socket to wait for a process to
+// connect to it, and the connection will be used to request file descriptors
+// from ash-chrome, and when the process forks to start a lacros-chrome, the
+// obtained file descriptor will be used by lacros-chrome to set up the mojo
+// connection with ash-chrome. There are mainly two use cases:
+// 1. Test launcher to run browser tests in testing environment.
+// 2. A terminal to start lacros-chrome with a debugger.
+const char kLacrosMojoSocketForTesting[] = "lacros-mojo-socket-for-testing";
+
 // Enables Chrome-as-a-login-manager behavior.
 const char kLoginManager[] = "login-manager";
 
diff --git a/chromeos/constants/chromeos_switches.h b/chromeos/constants/chromeos_switches.h
index 1496faa..f12f1e0 100644
--- a/chromeos/constants/chromeos_switches.h
+++ b/chromeos/constants/chromeos_switches.h
@@ -173,6 +173,8 @@
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const char kLacrosChromeAdditionalArgs[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kLacrosChromePath[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kLacrosMojoSocketForTesting[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kLoginManager[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kLoginProfile[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kLoginUser[];
diff --git a/chromeos/dbus/audio/cras_audio_client.cc b/chromeos/dbus/audio/cras_audio_client.cc
index 30ebe722..be0408bd 100644
--- a/chromeos/dbus/audio/cras_audio_client.cc
+++ b/chromeos/dbus/audio/cras_audio_client.cc
@@ -419,6 +419,14 @@
                             base::DoNothing());
   }
 
+  void ResendBluetoothBattery() override {
+    dbus::MethodCall method_call(cras::kCrasControlInterface,
+                                 cras::kResendBluetoothBattery);
+    cras_proxy_->CallMethod(&method_call,
+                            dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+                            base::DoNothing());
+  }
+
   void WaitForServiceToBeAvailable(
       WaitForServiceToBeAvailableCallback callback) override {
     cras_proxy_->WaitForServiceToBeAvailable(std::move(callback));
diff --git a/chromeos/dbus/audio/cras_audio_client.h b/chromeos/dbus/audio/cras_audio_client.h
index e2c4a5e..ef1e4c73 100644
--- a/chromeos/dbus/audio/cras_audio_client.h
+++ b/chromeos/dbus/audio/cras_audio_client.h
@@ -187,6 +187,9 @@
   virtual void SetPlayerMetadata(
       const std::map<std::string, std::string>& metadata) = 0;
 
+  // Asks Cras to resend battery level for Bluetooth device if exists .
+  virtual void ResendBluetoothBattery() = 0;
+
   // Runs the callback as soon as the service becomes available.
   virtual void WaitForServiceToBeAvailable(
       WaitForServiceToBeAvailableCallback callback) = 0;
diff --git a/chromeos/dbus/audio/cras_audio_client_unittest.cc b/chromeos/dbus/audio/cras_audio_client_unittest.cc
index 105ab83..ccb4b3b 100644
--- a/chromeos/dbus/audio/cras_audio_client_unittest.cc
+++ b/chromeos/dbus/audio/cras_audio_client_unittest.cc
@@ -1262,4 +1262,18 @@
   base::RunLoop().RunUntilIdle();
 }
 
+TEST_F(CrasAudioClientTest, ResendBluetoothBattery) {
+  // Create response.
+  std::unique_ptr<dbus::Response> response(dbus::Response::CreateEmpty());
+
+  // Set expectations.
+  PrepareForMethodCall(cras::kResendBluetoothBattery,
+                       base::BindRepeating(&ExpectNoArgument), response.get());
+
+  // Call method.
+  client()->ResendBluetoothBattery();
+  // Run the message loop.
+  base::RunLoop().RunUntilIdle();
+}
+
 }  // namespace chromeos
diff --git a/chromeos/dbus/audio/fake_cras_audio_client.cc b/chromeos/dbus/audio/fake_cras_audio_client.cc
index 8f88190..5209d111 100644
--- a/chromeos/dbus/audio/fake_cras_audio_client.cc
+++ b/chromeos/dbus/audio/fake_cras_audio_client.cc
@@ -247,6 +247,11 @@
   }
 }
 
+void FakeCrasAudioClient::ResendBluetoothBattery() {
+  for (auto& observer : observers_)
+    observer.BluetoothBatteryChanged("11:22:33:44:55:66", battery_level_);
+}
+
 void FakeCrasAudioClient::WaitForServiceToBeAvailable(
     WaitForServiceToBeAvailableCallback callback) {
   std::move(callback).Run(true);
@@ -303,6 +308,10 @@
     observer.HotwordTriggered(tv_sec, tv_nsec);
 }
 
+void FakeCrasAudioClient::SetBluetoothBattteryLevelForTesting(uint32_t level) {
+  battery_level_ = level;
+}
+
 AudioNodeList::iterator FakeCrasAudioClient::FindNode(uint64_t node_id) {
   return std::find_if(
       node_list_.begin(), node_list_.end(),
diff --git a/chromeos/dbus/audio/fake_cras_audio_client.h b/chromeos/dbus/audio/fake_cras_audio_client.h
index a505117e..59f69eb 100644
--- a/chromeos/dbus/audio/fake_cras_audio_client.h
+++ b/chromeos/dbus/audio/fake_cras_audio_client.h
@@ -59,6 +59,7 @@
   void SetPlayerDuration(const int64_t& duration) override;
   void SetPlayerMetadata(
       const std::map<std::string, std::string>& metadata) override;
+  void ResendBluetoothBattery() override;
   void WaitForServiceToBeAvailable(
       WaitForServiceToBeAvailableCallback callback) override;
 
@@ -83,6 +84,9 @@
   // Generates fake hotword signal for HotwordTriggered.
   void NotifyHotwordTriggeredForTesting(uint64_t tv_sec, uint64_t tv_nsec);
 
+  // Set a mock battery level for ResendBatteryLevel.
+  void SetBluetoothBattteryLevelForTesting(uint32_t level);
+
   const AudioNodeList& node_list() const { return node_list_; }
   const uint64_t& active_input_node_id() const { return active_input_node_id_; }
   const uint64_t& active_output_node_id() const {
@@ -103,6 +107,7 @@
   // By default, immediately sends OutputNodeVolumeChange signal following the
   // SetOutputNodeVolume fake dbus call.
   bool notify_volume_change_with_delay_ = false;
+  uint32_t battery_level_ = 0;
   base::ObserverList<Observer>::Unchecked observers_;
 
   DISALLOW_COPY_AND_ASSIGN(FakeCrasAudioClient);
diff --git a/chromeos/services/machine_learning/public/cpp/BUILD.gn b/chromeos/services/machine_learning/public/cpp/BUILD.gn
index 883f056..9edcd92 100644
--- a/chromeos/services/machine_learning/public/cpp/BUILD.gn
+++ b/chromeos/services/machine_learning/public/cpp/BUILD.gn
@@ -6,6 +6,8 @@
 
 source_set("cpp") {
   sources = [
+    "handwriting_model_loader.cc",
+    "handwriting_model_loader.h",
     "handwriting_recognizer_manager.cc",
     "handwriting_recognizer_manager.h",
     "service_connection.cc",
@@ -13,6 +15,7 @@
   ]
   deps = [
     "//base",
+    "//chromeos/dbus/dlcservice",
     "//chromeos/dbus/machine_learning",
     "//chromeos/services/machine_learning/public/mojom",
   ]
@@ -34,11 +37,15 @@
 
 source_set("unit_tests") {
   testonly = true
-  sources = [ "service_connection_unittest.cc" ]
+  sources = [
+    "handwriting_model_loader_unittest.cc",
+    "service_connection_unittest.cc",
+  ]
   deps = [
     ":cpp",
     ":test_support",
     "//base/test:test_support",
+    "//chromeos/dbus/dlcservice:test_support",
     "//chromeos/dbus/machine_learning",
     "//chromeos/services/machine_learning/public/mojom",
     "//mojo/core/embedder",
diff --git a/chromeos/services/machine_learning/public/cpp/handwriting_model_loader.cc b/chromeos/services/machine_learning/public/cpp/handwriting_model_loader.cc
new file mode 100644
index 0000000..4ca29fb
--- /dev/null
+++ b/chromeos/services/machine_learning/public/cpp/handwriting_model_loader.cc
@@ -0,0 +1,138 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/services/machine_learning/public/cpp/handwriting_model_loader.h"
+#include "base/command_line.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/no_destructor.h"
+#include "chromeos/services/machine_learning/public/cpp/service_connection.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+
+namespace chromeos {
+namespace machine_learning {
+namespace {
+
+using chromeos::machine_learning::mojom::LoadHandwritingModelResult;
+
+// Records CrOSActionRecorder event.
+void RecordLoadHandwritingModelResult(const LoadHandwritingModelResult val) {
+  UMA_HISTOGRAM_ENUMERATION(
+      "MachineLearningService.HandwritingModel.LoadModelResult.Event", val,
+      LoadHandwritingModelResult::LOAD_MODEL_FILES_ERROR);
+}
+
+// A list of supported language code.
+constexpr char kLanguageCodeEn[] = "en";
+constexpr char kLanguageCodeGesture[] = "gesture_in_context";
+
+// Returns whether the `value` is set for command line switch
+// kOndeviceHandwritingSwitch.
+bool HandwritingSwitchHasValue(const std::string& value) {
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  return command_line->HasSwitch(
+             HandwritingModelLoader::kOndeviceHandwritingSwitch) &&
+         command_line->GetSwitchValueASCII(
+             HandwritingModelLoader::kOndeviceHandwritingSwitch) == value;
+}
+
+// Returns true if switch kOndeviceHandwritingSwitch is set to use_rootfs.
+bool IsLibHandwritingRootfsEnabled() {
+  return HandwritingSwitchHasValue("use_rootfs");
+}
+
+// Returns true if switch kOndeviceHandwritingSwitch is set to use_dlc.
+bool IsLibHandwritingDlcEnabled() {
+  return HandwritingSwitchHasValue("use_dlc");
+}
+
+}  // namespace
+
+constexpr char HandwritingModelLoader::kOndeviceHandwritingSwitch[];
+constexpr char HandwritingModelLoader::kLibHandwritingDlcId[];
+
+HandwritingModelLoader::HandwritingModelLoader(
+    mojom::HandwritingRecognizerSpecPtr spec,
+    mojo::PendingReceiver<mojom::HandwritingRecognizer> receiver,
+    mojom::MachineLearningService::LoadHandwritingModelCallback callback)
+    : dlc_client_(chromeos::DlcserviceClient::Get()),
+      spec_(std::move(spec)),
+      receiver_(std::move(receiver)),
+      callback_(std::move(callback)),
+      weak_ptr_factory_(this) {}
+
+HandwritingModelLoader::~HandwritingModelLoader() = default;
+
+void HandwritingModelLoader::Load() {
+  // Returns FEATURE_NOT_SUPPORTED_ERROR if both rootfs and dlc are not enabled.
+  if (!IsLibHandwritingRootfsEnabled() && !IsLibHandwritingDlcEnabled()) {
+    RecordLoadHandwritingModelResult(
+        LoadHandwritingModelResult::FEATURE_NOT_SUPPORTED_ERROR);
+    std::move(callback_).Run(
+        LoadHandwritingModelResult::FEATURE_NOT_SUPPORTED_ERROR);
+    return;
+  }
+
+  // Returns LANGUAGE_NOT_SUPPORTED_ERROR if the language is not supported yet.
+  if (spec_->language != kLanguageCodeEn &&
+      spec_->language != kLanguageCodeGesture) {
+    RecordLoadHandwritingModelResult(
+        LoadHandwritingModelResult::LANGUAGE_NOT_SUPPORTED_ERROR);
+    std::move(callback_).Run(
+        LoadHandwritingModelResult::LANGUAGE_NOT_SUPPORTED_ERROR);
+    return;
+  }
+
+  // Load from rootfs if enabled.
+  if (IsLibHandwritingRootfsEnabled()) {
+    ServiceConnection::GetInstance()->LoadHandwritingModel(
+        std::move(spec_), std::move(receiver_), std::move(callback_));
+    return;
+  }
+
+  // Gets existing dlc list and based on the presence of libhandwriting
+  // either returns an error or installs the libhandwriting dlc.
+  dlc_client_->GetExistingDlcs(
+      base::BindOnce(&HandwritingModelLoader::OnGetExistingDlcsComplete,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void HandwritingModelLoader::OnGetExistingDlcsComplete(
+    const std::string& err,
+    const dlcservice::DlcsWithContent& dlcs_with_content) {
+  // Loop over dlcs_with_content, and installs libhandwriting if already exists.
+  // Since we don't want to trigger downloading here, we only install(mount)
+  // the handwriting dlc if it is already on device.
+  for (const auto& dlc_info : dlcs_with_content.dlc_infos()) {
+    if (dlc_info.id() == HandwritingModelLoader::kLibHandwritingDlcId) {
+      dlc_client_->Install(
+          kLibHandwritingDlcId,
+          base::BindOnce(&HandwritingModelLoader::OnInstallDlcComplete,
+                         weak_ptr_factory_.GetWeakPtr()),
+          chromeos::DlcserviceClient::IgnoreProgress);
+      return;
+    }
+  }
+
+  // Returns error if the handwriting dlc is not on the device.
+  RecordLoadHandwritingModelResult(
+      LoadHandwritingModelResult::DLC_DOES_NOT_EXIST);
+  std::move(callback_).Run(LoadHandwritingModelResult::DLC_DOES_NOT_EXIST);
+}
+
+void HandwritingModelLoader::OnInstallDlcComplete(
+    const chromeos::DlcserviceClient::InstallResult& result) {
+  // Call LoadHandwritingModelWithSpec if no error was found.
+  if (result.error == dlcservice::kErrorNone) {
+    ServiceConnection::GetInstance()->LoadHandwritingModel(
+        std::move(spec_), std::move(receiver_), std::move(callback_));
+    return;
+  }
+
+  RecordLoadHandwritingModelResult(
+      LoadHandwritingModelResult::DLC_INSTALL_ERROR);
+  std::move(callback_).Run(LoadHandwritingModelResult::DLC_INSTALL_ERROR);
+}
+
+}  // namespace machine_learning
+}  // namespace chromeos
diff --git a/chromeos/services/machine_learning/public/cpp/handwriting_model_loader.h b/chromeos/services/machine_learning/public/cpp/handwriting_model_loader.h
new file mode 100644
index 0000000..aaf915c
--- /dev/null
+++ b/chromeos/services/machine_learning/public/cpp/handwriting_model_loader.h
@@ -0,0 +1,79 @@
+// Copyright 2020 The Chromium 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 CHROMEOS_SERVICES_MACHINE_LEARNING_PUBLIC_CPP_HANDWRITING_MODEL_LOADER_H_
+#define CHROMEOS_SERVICES_MACHINE_LEARNING_PUBLIC_CPP_HANDWRITING_MODEL_LOADER_H_
+
+#include "base/memory/weak_ptr.h"
+#include "chromeos/dbus/dlcservice/dlcservice_client.h"
+#include "chromeos/services/machine_learning/public/mojom/handwriting_recognizer.mojom.h"
+#include "chromeos/services/machine_learning/public/mojom/machine_learning_service.mojom.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+
+namespace chromeos {
+namespace machine_learning {
+
+// Class that decides either to load handwriting model from rootfs or dlc.
+// New Handwriting clients should call this helper instead of calling
+// ServiceConnection::GetInstance()->LoadHandwritingModel.
+// Three typical examples of the callstack are:
+// Case 1: handwriting in enabled on rootfs.
+//   client calls HandwritingModelLoader("en", receiver, callback).Load()
+//   which calls LoadHandwritingModel -> handwriting model loaded from rootfs.
+// Case 2: handwriting is enabled for dlc and dlc is already on the device.
+//   client calls HandwritingModelLoader("en", receiver, callback).Load()
+//   which calls -> GetExistingDlcs -> libhandwriting dlc already exists
+//               -> InstallDlc -> LoadHandwritingModel
+//   The correct handwriting model will be loaded and bond to the receiver.
+// Case 3: handwriting is enabled for dlc and dlc is not on the device yet.
+//   client calls HandwritingModelLoader("en", receiver, callback).Load()
+//   which calls -> GetExistingDlcs -> NO libhandwriting dlc exists
+//               -> Return error DLC_NOT_EXISTED.
+//   Then it will be the client's duty to install the dlc and then calls
+//   HandwritingModelLoader("en", receiver, callback).Load() again.
+class HandwritingModelLoader {
+ public:
+  HandwritingModelLoader(
+      mojom::HandwritingRecognizerSpecPtr spec,
+      mojo::PendingReceiver<mojom::HandwritingRecognizer> receiver,
+      mojom::MachineLearningService::LoadHandwritingModelCallback callback);
+
+  ~HandwritingModelLoader();
+
+  // Load handwriting model based on the comandline flag and language.
+  void Load();
+
+  static constexpr char kOndeviceHandwritingSwitch[] = "ondevice_handwriting";
+  static constexpr char kLibHandwritingDlcId[] = "libhandwriting";
+
+ private:
+  friend class HandwritingModelLoaderTest;
+
+  // Called when the existing-dlc-list is returned.
+  // Returns an error if libhandwriting is not in the existing-dlc-list.
+  // Calls InstallDlc otherwise.
+  void OnGetExistingDlcsComplete(
+      const std::string& err,
+      const dlcservice::DlcsWithContent& dlcs_with_content);
+
+  // Called when InstallDlc completes.
+  // Returns an error if the `result.error` is not dlcservice::kErrorNone.
+  // Calls mlservice to LoadHandwritingModel otherwise.
+  void OnInstallDlcComplete(
+      const chromeos::DlcserviceClient::InstallResult& result);
+
+  DlcserviceClient* dlc_client_;
+  mojom::HandwritingRecognizerSpecPtr spec_;
+  mojo::PendingReceiver<mojom::HandwritingRecognizer> receiver_;
+  mojom::MachineLearningService::LoadHandwritingModelCallback callback_;
+
+  base::WeakPtrFactory<HandwritingModelLoader> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(HandwritingModelLoader);
+};
+
+}  // namespace machine_learning
+}  // namespace chromeos
+
+#endif  // CHROMEOS_SERVICES_MACHINE_LEARNING_PUBLIC_CPP_HANDWRITING_MODEL_LOADER_H_
diff --git a/chromeos/services/machine_learning/public/cpp/handwriting_model_loader_unittest.cc b/chromeos/services/machine_learning/public/cpp/handwriting_model_loader_unittest.cc
new file mode 100644
index 0000000..d3fcc6a
--- /dev/null
+++ b/chromeos/services/machine_learning/public/cpp/handwriting_model_loader_unittest.cc
@@ -0,0 +1,144 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/services/machine_learning/public/cpp/handwriting_model_loader.h"
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/run_loop.h"
+#include "base/test/scoped_command_line.h"
+#include "base/test/task_environment.h"
+#include "chromeos/dbus/dlcservice/fake_dlcservice_client.h"
+#include "chromeos/services/machine_learning/public/cpp/fake_service_connection.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+
+namespace chromeos {
+namespace machine_learning {
+using chromeos::machine_learning::mojom::LoadHandwritingModelResult;
+
+class HandwritingModelLoaderTest : public testing::Test {
+ protected:
+  void SetUp() override {
+    ServiceConnection::UseFakeServiceConnectionForTesting(
+        &fake_service_connection_);
+    result_ = LoadHandwritingModelResult::DEPRECATED_MODEL_SPEC_ERROR;
+
+    loader_ = std::make_unique<HandwritingModelLoader>(
+        mojom::HandwritingRecognizerSpec::New("en"),
+        recognizer_.BindNewPipeAndPassReceiver(),
+        base::BindOnce(
+            &HandwritingModelLoaderTest::OnHandwritingModelLoaderComplete,
+            base::Unretained(this)));
+    loader_->dlc_client_ = &fake_client_;
+  }
+
+  // Callback that called when loader_->Load() is over to save the returned
+  // result.
+  void OnHandwritingModelLoaderComplete(
+      const LoadHandwritingModelResult result) {
+    result_ = result;
+  }
+
+  // Runs loader_->Load() and check the returned result as expected.
+  void ExpectLoadHandwritingModelResult(
+      const LoadHandwritingModelResult expected_result) {
+    loader_->Load();
+    base::RunLoop().RunUntilIdle();
+    EXPECT_EQ(result_, expected_result);
+  }
+
+  void SetLanguage(const std::string& language) {
+    loader_->spec_->language = language;
+  }
+
+  // Creates a dlc list with one dlc inside.
+  void AddDlcsWithContent(const std::string& dlc_id) {
+    dlcservice::DlcsWithContent dlcs_with_content;
+    dlcs_with_content.add_dlc_infos()->set_id(dlc_id);
+    fake_client_.set_dlcs_with_content(dlcs_with_content);
+  }
+
+  // Sets InstallDlc error.
+  void SetInstallError(const std::string& error) {
+    fake_client_.set_install_error(error);
+    fake_client_.set_install_root_path("/any-path");
+  }
+
+  // Sets "ondevice_handwriting" value.
+  void SetSwitchValue(const std::string& switch_value) {
+    base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+        HandwritingModelLoader::kOndeviceHandwritingSwitch, switch_value);
+  }
+
+ private:
+  base::test::TaskEnvironment task_environment_{
+      base::test::TaskEnvironment::MainThreadType::DEFAULT,
+      base::test::TaskEnvironment::ThreadPoolExecutionMode::QUEUED};
+  base::test::ScopedCommandLine scoped_command_line_;
+  FakeDlcserviceClient fake_client_;
+  FakeServiceConnectionImpl fake_service_connection_;
+  LoadHandwritingModelResult result_;
+  mojo::Remote<mojom::HandwritingRecognizer> recognizer_;
+  std::unique_ptr<HandwritingModelLoader> loader_;
+};
+
+TEST_F(HandwritingModelLoaderTest, HandwritingNotEnabled) {
+  SetSwitchValue("random_string");
+
+  // Random switch value should return FEATURE_NOT_SUPPORTED_ERROR.
+  ExpectLoadHandwritingModelResult(
+      LoadHandwritingModelResult::FEATURE_NOT_SUPPORTED_ERROR);
+}
+
+TEST_F(HandwritingModelLoaderTest, LoadingWithInvalidLanguage) {
+  SetSwitchValue("use_rootfs");
+
+  SetLanguage("random string as language");
+
+  // Random language code should return LANGUAGE_NOT_SUPPORTED_ERROR.
+  ExpectLoadHandwritingModelResult(
+      LoadHandwritingModelResult::LANGUAGE_NOT_SUPPORTED_ERROR);
+}
+
+TEST_F(HandwritingModelLoaderTest, LoadingWithUseRootfs) {
+  SetSwitchValue("use_rootfs");
+
+  // Load from rootfs should return success.
+  ExpectLoadHandwritingModelResult(LoadHandwritingModelResult::OK);
+}
+
+TEST_F(HandwritingModelLoaderTest, LoadingWithoutDlcOnDevice) {
+  SetSwitchValue("use_dlc");
+
+  AddDlcsWithContent("random dlc-id");
+
+  // Random dlc id should return DLC_DOES_NOT_EXIST.
+  ExpectLoadHandwritingModelResult(
+      LoadHandwritingModelResult::DLC_DOES_NOT_EXIST);
+}
+
+TEST_F(HandwritingModelLoaderTest, DlcInstalledWithError) {
+  SetSwitchValue("use_dlc");
+
+  AddDlcsWithContent(HandwritingModelLoader::kLibHandwritingDlcId);
+  SetInstallError("random error");
+
+  // InstallDlc error should return DLC_INSTALL_ERROR.
+  ExpectLoadHandwritingModelResult(
+      LoadHandwritingModelResult::DLC_INSTALL_ERROR);
+}
+
+TEST_F(HandwritingModelLoaderTest, DlcInstalledWithoutError) {
+  SetSwitchValue("use_dlc");
+
+  AddDlcsWithContent(HandwritingModelLoader::kLibHandwritingDlcId);
+  SetInstallError(dlcservice::kErrorNone);
+
+  // InstallDlc without an error should return success.
+  ExpectLoadHandwritingModelResult(LoadHandwritingModelResult::OK);
+}
+
+}  // namespace machine_learning
+}  // namespace chromeos
diff --git a/components/dom_distiller/content/DEPS b/components/dom_distiller/content/DEPS
index 4688a57..2323b1c9 100644
--- a/components/dom_distiller/content/DEPS
+++ b/components/dom_distiller/content/DEPS
@@ -5,4 +5,5 @@
   "+ipc",
   "+net/base",
   "+net/test",
+  "+third_party/blink/public/common/web_preferences",
 ]
diff --git a/components/dom_distiller/content/browser/dom_distiller_viewer_source.cc b/components/dom_distiller/content/browser/dom_distiller_viewer_source.cc
index b70f967..6fd8226 100644
--- a/components/dom_distiller/content/browser/dom_distiller_viewer_source.cc
+++ b/components/dom_distiller/content/browser/dom_distiller_viewer_source.cc
@@ -37,11 +37,11 @@
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
-#include "content/public/common/web_preferences.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "net/base/url_util.h"
 #include "services/network/public/mojom/content_security_policy.mojom.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "ui/base/l10n/l10n_util.h"
 
 namespace dom_distiller {
@@ -211,7 +211,8 @@
     return;
 #if !defined(OS_ANDROID)
   // Don't allow loading of mixed content on Reader Mode pages.
-  content::WebPreferences prefs = web_contents->GetOrCreateWebPreferences();
+  blink::web_pref::WebPreferences prefs =
+      web_contents->GetOrCreateWebPreferences();
   prefs.strict_mixed_content_checking = true;
   web_contents->SetWebPreferences(prefs);
 #endif  // !defined(OS_ANDROID)
diff --git a/components/download/internal/common/download_stats.cc b/components/download/internal/common/download_stats.cc
index b7f0ebf..9ee6aa4 100644
--- a/components/download/internal/common/download_stats.cc
+++ b/components/download/internal/common/download_stats.cc
@@ -650,7 +650,6 @@
   if (!uses_parallel_requests)
     return;
 
-  base::TimeDelta time_saved;
   if (bytes_downloaded_with_parallel_streams > 0) {
     int64_t bandwidth_with_parallel_streams = CalculateBandwidthBytesPerSecond(
         bytes_downloaded_with_parallel_streams, time_with_parallel_streams);
@@ -658,20 +657,6 @@
         "Download.ParallelizableDownloadBandwidth."
         "WithParallelRequestsMultipleStreams",
         bandwidth_with_parallel_streams);
-    if (bandwidth_without_parallel_streams > 0) {
-      time_saved = base::TimeDelta::FromMilliseconds(
-                       1000.0 * bytes_downloaded_with_parallel_streams /
-                       bandwidth_without_parallel_streams) -
-                   time_with_parallel_streams;
-    }
-  }
-
-  int kMillisecondsPerHour =
-      base::checked_cast<int>(base::Time::kMillisecondsPerSecond * 60 * 60);
-  if (time_saved >= base::TimeDelta()) {
-    UMA_HISTOGRAM_CUSTOM_COUNTS(
-        "Download.EstimatedTimeSavedWithParallelDownload",
-        time_saved.InMilliseconds(), 0, kMillisecondsPerHour, 50);
   }
 }
 
diff --git a/components/exo/client_controlled_shell_surface.cc b/components/exo/client_controlled_shell_surface.cc
index 2c598109..41cb88f 100644
--- a/components/exo/client_controlled_shell_surface.cc
+++ b/components/exo/client_controlled_shell_surface.cc
@@ -52,6 +52,8 @@
 #include "ui/compositor/compositor_lock.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/size.h"
 #include "ui/views/widget/widget.h"
 #include "ui/wm/core/coordinate_conversion.h"
 #include "ui/wm/core/window_util.h"
@@ -321,8 +323,7 @@
     bool default_scale_cancellation)
     : ShellSurfaceBase(surface, gfx::Point(), true, can_minimize, container),
       current_pin_(ash::WindowPinType::kNone),
-      use_default_scale_cancellation_(default_scale_cancellation) {
-}
+      use_default_scale_cancellation_(default_scale_cancellation) {}
 
 ClientControlledShellSurface::~ClientControlledShellSurface() {
   // Reset the window delegate here so that we won't try to do any dragging
@@ -346,31 +347,11 @@
     return;
   }
 
-  // Handle the case where we receive bounds from the client before the initial
-  // scale has been set.
-  if (pending_scale_ == 0.0) {
-    DCHECK(!use_default_scale_cancellation_);
-    display::Display display;
-    if (display::Screen::GetScreen()->GetDisplayWithDisplayId(display_id,
-                                                              &display)) {
-      SetScale(display.device_scale_factor());
-      CommitPendingScale();
-    }
-  }
-
-  float client_to_dp_scale = 1.f;
-  // When the client is scale-aware, we expect that it will resize windows when
-  // reacting to scale changes. Since we do not commit the scale until the
-  // buffer size changes, any bounds sent after a scale change and before the
-  // scale commit will result in mismatched sizes between widget and the buffer.
-  // To work around this, we use pending_scale_ to calculate bounds in DP
-  // instead of GetClientToDpScale().
-  if (!use_default_scale_cancellation_)
-    client_to_dp_scale = 1.f / pending_scale_;
-
-  gfx::Rect bounds_dp = gfx::ScaleToRoundedRect(bounds, client_to_dp_scale);
-
   SetDisplay(display_id);
+  EnsurePendingScale();
+
+  const gfx::Rect bounds_dp =
+      gfx::ScaleToRoundedRect(bounds, GetClientToDpPendingScale());
   SetGeometry(bounds_dp);
 }
 
@@ -378,7 +359,10 @@
   TRACE_EVENT1("exo", "ClientControlledShellSurface::SetBoundsOrigin", "origin",
                origin.ToString());
 
-  pending_geometry_.set_origin(origin);
+  EnsurePendingScale();
+  const gfx::Point origin_dp =
+      gfx::ScaleToRoundedPoint(origin, GetClientToDpPendingScale());
+  pending_geometry_.set_origin(origin_dp);
 }
 
 void ClientControlledShellSurface::SetBoundsSize(const gfx::Size& size) {
@@ -390,8 +374,12 @@
     return;
   }
 
-  pending_geometry_.set_size(size);
+  EnsurePendingScale();
+  const gfx::Size size_dp =
+      gfx::ScaleToRoundedSize(size, GetClientToDpPendingScale());
+  pending_geometry_.set_size(size_dp);
 }
+
 void ClientControlledShellSurface::SetMaximized() {
   TRACE_EVENT0("exo", "ClientControlledShellSurface::SetMaximized");
   pending_window_state_ = ash::WindowStateType::kMaximized;
@@ -1394,6 +1382,30 @@
       widget_->non_client_view()->frame_view());
 }
 
+void ClientControlledShellSurface::EnsurePendingScale() {
+  // Handle the case where we receive bounds from the client before the initial
+  // scale has been set.
+  if (pending_scale_ == 0.0) {
+    DCHECK(!use_default_scale_cancellation_);
+    display::Display display;
+    if (display::Screen::GetScreen()->GetDisplayWithDisplayId(
+            pending_display_id_, &display)) {
+      SetScale(display.device_scale_factor());
+      CommitPendingScale();
+    }
+  }
+}
+
+float ClientControlledShellSurface::GetClientToDpPendingScale() const {
+  // When the client is scale-aware, we expect that it will resize windows when
+  // reacting to scale changes. Since we do not commit the scale until the
+  // buffer size changes, any bounds sent after a scale change and before the
+  // scale commit will result in mismatched sizes between widget and the buffer.
+  // To work around this, we use pending_scale_ to calculate bounds in DP
+  // instead of GetClientToDpScale().
+  return use_default_scale_cancellation_ ? 1.f : 1.f / pending_scale_;
+}
+
 // static
 void ClientControlledShellSurface::
     SetClientControlledStateDelegateFactoryForTest(
diff --git a/components/exo/client_controlled_shell_surface.h b/components/exo/client_controlled_shell_surface.h
index cb8d1ab0..008ba08d 100644
--- a/components/exo/client_controlled_shell_surface.h
+++ b/components/exo/client_controlled_shell_surface.h
@@ -311,6 +311,9 @@
   ash::NonClientFrameViewAsh* GetFrameView();
   const ash::NonClientFrameViewAsh* GetFrameView() const;
 
+  void EnsurePendingScale();
+  float GetClientToDpPendingScale() const;
+
   GeometryChangedCallback geometry_changed_callback_;
 
   int top_inset_height_ = 0;
diff --git a/components/exo/client_controlled_shell_surface_unittest.cc b/components/exo/client_controlled_shell_surface_unittest.cc
index 6fc8508..79728d5 100644
--- a/components/exo/client_controlled_shell_surface_unittest.cc
+++ b/components/exo/client_controlled_shell_surface_unittest.cc
@@ -1820,6 +1820,58 @@
   EXPECT_EQ(secondary_display.id(), display.id());
 }
 
+// Test if the surface bounds is correctly set when default scale cancellation
+// is enabled or disabled.
+TEST_F(ClientControlledShellSurfaceTest,
+       SetBoundsWithAndWithoutDefaultScaleCancellation) {
+  UpdateDisplay("800x600*2");
+
+  const auto primary_display_id =
+      display::Screen::GetScreen()->GetPrimaryDisplay().id();
+
+  const gfx::Size buffer_size(64, 64);
+  std::unique_ptr<Buffer> buffer(
+      new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size)));
+
+  const gfx::Rect bounds(64, 64, 128, 128);
+  const gfx::Rect bounds_dp = gfx::ScaleToRoundedRect(bounds, 1.f / 2.f);
+
+  for (const auto default_scale_cancellation : {true, false}) {
+    const auto bounds_to_set = default_scale_cancellation ? bounds_dp : bounds;
+
+    {
+      // Set display id, bounds origin, bounds size at the same time via
+      // SetBounds method.
+      std::unique_ptr<Surface> surface(new Surface);
+      auto shell_surface(exo_test_helper()->CreateClientControlledShellSurface(
+          surface.get(), /*is_modal=*/false, default_scale_cancellation));
+
+      shell_surface->SetBounds(primary_display_id, bounds_to_set);
+      surface->Attach(buffer.get());
+      surface->Commit();
+
+      EXPECT_EQ(bounds_dp,
+                shell_surface->GetWidget()->GetWindowBoundsInScreen());
+    }
+
+    {
+      // Set display id, bounds origin, bounds size separately.
+      std::unique_ptr<Surface> surface(new Surface);
+      auto shell_surface(exo_test_helper()->CreateClientControlledShellSurface(
+          surface.get(), /*is_modal=*/false, default_scale_cancellation));
+
+      shell_surface->SetDisplay(primary_display_id);
+      shell_surface->SetBoundsOrigin(bounds_to_set.origin());
+      shell_surface->SetBoundsSize(bounds_to_set.size());
+      surface->Attach(buffer.get());
+      surface->Commit();
+
+      EXPECT_EQ(bounds_dp,
+                shell_surface->GetWidget()->GetWindowBoundsInScreen());
+    }
+  }
+}
+
 // Set orientation lock to a window.
 TEST_F(ClientControlledShellSurfaceTest, SetOrientationLock) {
   display::test::DisplayManagerTestApi(ash::Shell::Get()->display_manager())
diff --git a/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/EventConstants.java b/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/EventConstants.java
index 8e84001..5fe8d5e 100644
--- a/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/EventConstants.java
+++ b/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/EventConstants.java
@@ -133,9 +133,6 @@
     public static final String PARTNER_HOME_PAGE_BUTTON_PRESSED =
             "partner_home_page_button_pressed";
 
-    /** The user used a button in the bottom toolbar. */
-    public static final String CHROME_DUET_USED_BOTTOM_TOOLBAR = "chrome_duet_used_bottom_toolbar";
-
     /** The homepage button in the toolbar was clicked. */
     public static final String HOMEPAGE_BUTTON_CLICKED = "homepage_button_clicked";
 
diff --git a/components/javascript_dialogs/views/app_modal_dialog_view_views.cc b/components/javascript_dialogs/views/app_modal_dialog_view_views.cc
index 9bb183f..a40f389 100644
--- a/components/javascript_dialogs/views/app_modal_dialog_view_views.cc
+++ b/components/javascript_dialogs/views/app_modal_dialog_view_views.cc
@@ -5,6 +5,7 @@
 #include "components/javascript_dialogs/views/app_modal_dialog_view_views.h"
 
 #include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
 #include "components/javascript_dialogs/app_modal_dialog_controller.h"
 #include "components/strings/grit/components_strings.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -106,7 +107,13 @@
 }
 
 ui::ModalType AppModalDialogViewViews::GetModalType() const {
+#if defined(OS_CHROMEOS)
+  // TODO(https://crbug.com/1127133): Remove this hack. This works around the
+  // linked bug. This dialog should be window-modal on ChromeOS as well.
   return ui::MODAL_TYPE_SYSTEM;
+#else
+  return ui::MODAL_TYPE_WINDOW;
+#endif
 }
 
 views::View* AppModalDialogViewViews::GetContentsView() {
diff --git a/components/language/core/browser/language_prefs.cc b/components/language/core/browser/language_prefs.cc
index 9223b19..75dcb9e5 100644
--- a/components/language/core/browser/language_prefs.cc
+++ b/components/language/core/browser/language_prefs.cc
@@ -142,4 +142,9 @@
 #endif
 }
 
+std::string GetFirstLanguage(base::StringPiece language_list) {
+  auto end = language_list.find(",");
+  return std::string(language_list.substr(0, end));
+}
+
 }  // namespace language
diff --git a/components/language/core/browser/language_prefs.h b/components/language/core/browser/language_prefs.h
index a954b1d5..c9de856 100644
--- a/components/language/core/browser/language_prefs.h
+++ b/components/language/core/browser/language_prefs.h
@@ -5,6 +5,8 @@
 #ifndef COMPONENTS_LANGUAGE_CORE_BROWSER_LANGUAGE_PREFS_H_
 #define COMPONENTS_LANGUAGE_CORE_BROWSER_LANGUAGE_PREFS_H_
 
+#include <string>
+
 #include "base/macros.h"
 #include "base/strings/string_piece.h"
 
@@ -57,6 +59,9 @@
 
 void ResetLanguagePrefs(PrefService* prefs);
 
+// Given a comma separated list of locales, return the first.
+std::string GetFirstLanguage(base::StringPiece language_list);
+
 }  // namespace language
 
 #endif  // COMPONENTS_LANGUAGE_CORE_BROWSER_LANGUAGE_PREFS_H_
diff --git a/components/language/core/browser/language_prefs_unittest.cc b/components/language/core/browser/language_prefs_unittest.cc
index dcf321d..3484aa1 100644
--- a/components/language/core/browser/language_prefs_unittest.cc
+++ b/components/language/core/browser/language_prefs_unittest.cc
@@ -152,4 +152,11 @@
   ExpectFluentLanguageListContent({"en"});
 }
 
+TEST_F(LanguagePrefsTest, GetFirstLanguageTest) {
+  EXPECT_EQ("a", language::GetFirstLanguage("a,b,c"));
+  EXPECT_EQ("en-US", language::GetFirstLanguage("en-US,en,en-GB"));
+  EXPECT_EQ("en-US", language::GetFirstLanguage("en-US"));
+  EXPECT_EQ("", language::GetFirstLanguage(""));
+}
+
 }  // namespace language
diff --git a/components/media_message_center/BUILD.gn b/components/media_message_center/BUILD.gn
index 118ef5a..e44451d 100644
--- a/components/media_message_center/BUILD.gn
+++ b/components/media_message_center/BUILD.gn
@@ -49,6 +49,7 @@
     "media_controls_progress_view_unittest.cc",
     "media_notification_background_unittest.cc",
     "media_notification_view_impl_unittest.cc",
+    "media_notification_view_modern_impl_unittest.cc",
   ]
 
   deps = [
diff --git a/components/media_message_center/media_notification_background.h b/components/media_message_center/media_notification_background.h
index db69d6ac..9df64bf 100644
--- a/components/media_message_center/media_notification_background.h
+++ b/components/media_message_center/media_notification_background.h
@@ -49,6 +49,7 @@
  private:
   friend class MediaNotificationBackgroundTest;
   friend class MediaNotificationViewImplTest;
+  friend class MediaNotificationViewModernImplTest;
   FRIEND_TEST_ALL_PREFIXES(MediaNotificationBackgroundRTLTest,
                            BoundsSanityCheck);
 
diff --git a/components/media_message_center/media_notification_view_modern_impl.cc b/components/media_message_center/media_notification_view_modern_impl.cc
index f0a37541..2f429b8 100644
--- a/components/media_message_center/media_notification_view_modern_impl.cc
+++ b/components/media_message_center/media_notification_view_modern_impl.cc
@@ -3,3 +3,593 @@
 // found in the LICENSE file.
 
 #include "components/media_message_center/media_notification_view_modern_impl.h"
+
+#include "base/metrics/histogram_macros.h"
+#include "base/stl_util.h"
+#include "components/media_message_center/media_notification_background.h"
+#include "components/media_message_center/media_notification_constants.h"
+#include "components/media_message_center/media_notification_container.h"
+#include "components/media_message_center/media_notification_item.h"
+#include "components/media_message_center/media_notification_util.h"
+#include "components/media_message_center/vector_icons/vector_icons.h"
+#include "components/strings/grit/components_strings.h"
+#include "services/media_session/public/mojom/media_session.mojom.h"
+#include "ui/accessibility/ax_enums.mojom.h"
+#include "ui/accessibility/ax_node_data.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/font.h"
+#include "ui/gfx/font_list.h"
+#include "ui/gfx/paint_vector_icon.h"
+#include "ui/message_center/public/cpp/message_center_constants.h"
+#include "ui/views/border.h"
+#include "ui/views/controls/button/image_button_factory.h"
+#include "ui/views/layout/box_layout.h"
+#include "ui/views/style/typography.h"
+#include "ui/views/view_class_properties.h"
+
+namespace media_message_center {
+
+using media_session::mojom::MediaSessionAction;
+
+namespace {
+
+constexpr gfx::Size kMediaNotificationViewBaseSize = {350, 175};
+constexpr gfx::Size kInfoContainerSize = {
+    kMediaNotificationViewBaseSize.width(), 100};
+constexpr gfx::Insets kArtworkContainerBorderInsets = {15, 10, 15, 0};
+constexpr gfx::Size kArtworkContainerSize = {100, kInfoContainerSize.height()};
+constexpr int kArtworkVignetteCornerRadius = 5;
+constexpr gfx::Size kLabelsContainerBaseSize = {
+    kMediaNotificationViewBaseSize.width() - kArtworkContainerSize.width(),
+    kInfoContainerSize.height()};
+constexpr gfx::Insets kLabelsContainerBorderInsets = {15, 10, 0, 0};
+constexpr gfx::Size kPipSeparatorSize = {1, 14};
+constexpr gfx::Size kPipButtonSize = {20, 20};
+constexpr int kPipButtonIconSize = 18;
+constexpr gfx::Size kNotificationControlsSpacerSize = {100, 1};
+constexpr gfx::Insets kNotificationControlsInsets = {5, 0, 0, 5};
+constexpr gfx::Size kMediaControlsContainerSize = {350, 75};
+constexpr int kMediaControlsButtonSpacing = 16;
+constexpr gfx::Insets kMediaControlsBorderInsets = {0, 0, 10, 0};
+
+constexpr int kTitleArtistLineHeight = 20;
+constexpr gfx::Size kMediaButtonSize = gfx::Size(36, 36);
+constexpr int kMediaButtonIconSize = 20;
+
+// An image view with a rounded rectangle vignette
+class MediaArtworkView : public views::ImageView {
+ public:
+  explicit MediaArtworkView(float corner_radius)
+      : corner_radius_(corner_radius) {}
+
+  void SetVignetteColor(const SkColor& vignette_color) {
+    vignette_color_ = vignette_color;
+  }
+
+  // ImageView
+  void OnPaint(gfx::Canvas* canvas) override;
+
+ private:
+  SkColor vignette_color_;
+  float corner_radius_;
+};
+
+void MediaArtworkView::OnPaint(gfx::Canvas* canvas) {
+  views::ImageView::OnPaint(canvas);
+  auto path = SkPath().addRoundRect(RectToSkRect(GetLocalBounds()),
+                                    corner_radius_, corner_radius_);
+  path.toggleInverseFillType();
+  cc::PaintFlags paint_flags;
+  paint_flags.setStyle(cc::PaintFlags::kFill_Style);
+  paint_flags.setAntiAlias(true);
+  paint_flags.setColor(vignette_color_);
+  canvas->DrawPath(path, paint_flags);
+}
+
+void RecordMetadataHistogram(
+    MediaNotificationViewModernImpl::Metadata metadata) {
+  UMA_HISTOGRAM_ENUMERATION(
+      MediaNotificationViewModernImpl::kMetadataHistogramName, metadata);
+}
+
+const gfx::VectorIcon* GetVectorIconForMediaAction(MediaSessionAction action) {
+  switch (action) {
+    case MediaSessionAction::kPreviousTrack:
+      return &kMediaPreviousTrackIcon;
+    case MediaSessionAction::kSeekBackward:
+      return &kMediaSeekBackwardIcon;
+    case MediaSessionAction::kPlay:
+      return &kPlayArrowIcon;
+    case MediaSessionAction::kPause:
+      return &kPauseIcon;
+    case MediaSessionAction::kSeekForward:
+      return &kMediaSeekForwardIcon;
+    case MediaSessionAction::kNextTrack:
+      return &kMediaNextTrackIcon;
+    case MediaSessionAction::kEnterPictureInPicture:
+      return &kMediaEnterPipIcon;
+    case MediaSessionAction::kExitPictureInPicture:
+      return &kMediaExitPipIcon;
+    case MediaSessionAction::kStop:
+    case MediaSessionAction::kSkipAd:
+    case MediaSessionAction::kSeekTo:
+    case MediaSessionAction::kScrubTo:
+    case MediaSessionAction::kSwitchAudioDevice:
+      NOTREACHED();
+      break;
+  }
+
+  return nullptr;
+}
+
+}  // anonymous namespace
+
+// static
+const char MediaNotificationViewModernImpl::kArtworkHistogramName[] =
+    "Media.Notification.ArtworkPresent";
+
+// static
+const char MediaNotificationViewModernImpl::kMetadataHistogramName[] =
+    "Media.Notification.MetadataPresent";
+
+MediaNotificationViewModernImpl::MediaNotificationViewModernImpl(
+    MediaNotificationContainer* container,
+    base::WeakPtr<MediaNotificationItem> item,
+    std::unique_ptr<views::View> notification_controls_view,
+    int notification_width)
+    : container_(container), item_(std::move(item)) {
+  DCHECK(container_);
+
+  SetPreferredSize(kMediaNotificationViewBaseSize);
+
+  DCHECK(notification_width >= kMediaNotificationViewBaseSize.width())
+      << "MediaNotificationViewModernImpl expects a width of at least "
+      << kMediaNotificationViewBaseSize.width();
+  auto border_insets = gfx::Insets(
+      0, (kMediaNotificationViewBaseSize.width() - notification_width) / 2);
+  SetBorder(views::CreateEmptyBorder(border_insets));
+
+  SetLayoutManager(std::make_unique<views::BoxLayout>(
+      views::BoxLayout::Orientation::kVertical, gfx::Insets(), 0));
+
+  SetBackground(std::make_unique<MediaNotificationBackground>(
+      message_center::kNotificationCornerRadius,
+      message_center::kNotificationCornerRadius, 0));
+
+  UpdateCornerRadius(message_center::kNotificationCornerRadius,
+                     message_center::kNotificationCornerRadius);
+
+  {
+    // The info container contains the notification artwork, the labels for the
+    // title and artist text, the picture in picture button, and the dismiss
+    // button.
+    auto info_container = std::make_unique<views::View>();
+    info_container->SetPreferredSize(kInfoContainerSize);
+
+    auto* info_container_layout =
+        info_container->SetLayoutManager(std::make_unique<views::BoxLayout>(
+            views::BoxLayout::Orientation::kHorizontal, gfx::Insets(), 0));
+    info_container_layout->set_cross_axis_alignment(
+        views::BoxLayout::CrossAxisAlignment::kStart);
+
+    {
+      auto artwork_container = std::make_unique<views::View>();
+      artwork_container->SetBorder(
+          views::CreateEmptyBorder(kArtworkContainerBorderInsets));
+      artwork_container->SetPreferredSize(kArtworkContainerSize);
+
+      // The artwork container will become visible once artwork has been set in
+      // UpdateWithMediaArtwork
+      artwork_container->SetVisible(false);
+
+      auto* artwork_container_layout = artwork_container->SetLayoutManager(
+          std::make_unique<views::BoxLayout>(
+              views::BoxLayout::Orientation::kHorizontal, gfx::Insets(), 0));
+      artwork_container_layout->set_main_axis_alignment(
+          views::BoxLayout::MainAxisAlignment::kCenter);
+      artwork_container_layout->set_cross_axis_alignment(
+          views::BoxLayout::CrossAxisAlignment::kCenter);
+
+      {
+        auto artwork =
+            std::make_unique<MediaArtworkView>(kArtworkVignetteCornerRadius);
+        artwork_ = artwork_container->AddChildView(std::move(artwork));
+      }
+
+      artwork_container_ =
+          info_container->AddChildView(std::move(artwork_container));
+    }
+
+    {
+      auto labels_container = std::make_unique<views::View>();
+
+      labels_container->SetPreferredSize(
+          {kLabelsContainerBaseSize.width() -
+               (notification_controls_view->GetPreferredSize().width() +
+                kNotificationControlsInsets.width()),
+           kLabelsContainerBaseSize.height()});
+      labels_container->SetBorder(
+          views::CreateEmptyBorder(kLabelsContainerBorderInsets));
+
+      auto* labels_container_layout_manager =
+          labels_container->SetLayoutManager(std::make_unique<views::BoxLayout>(
+              views::BoxLayout::Orientation::kVertical, gfx::Insets(), 0));
+      labels_container_layout_manager->set_main_axis_alignment(
+          views::BoxLayout::MainAxisAlignment::kStart);
+      labels_container_layout_manager->set_cross_axis_alignment(
+          views::BoxLayout::CrossAxisAlignment::kStart);
+
+      {
+        auto title_label = std::make_unique<views::Label>(
+            base::EmptyString16(), views::style::CONTEXT_LABEL,
+            views::style::STYLE_PRIMARY);
+        title_label->SetLineHeight(kTitleArtistLineHeight);
+        title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+        title_label_ = labels_container->AddChildView(std::move(title_label));
+      }
+
+      {
+        auto subtitle_label = std::make_unique<views::Label>(
+            base::EmptyString16(), views::style::CONTEXT_LABEL,
+            views::style::STYLE_SECONDARY);
+        subtitle_label->SetLineHeight(18);
+        subtitle_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+        subtitle_label_ =
+            labels_container->AddChildView(std::move(subtitle_label));
+      }
+
+      {
+        // Put a vertical spacer between the labels and the pip button.
+        auto spacer = std::make_unique<views::View>();
+        spacer->SetPreferredSize(kPipSeparatorSize);
+        labels_container->AddChildView(std::move(spacer));
+      }
+
+      {
+        // The picture-in-picture button appears directly under the media
+        // labels.
+        auto picture_in_picture_button =
+            views::CreateVectorToggleImageButton(this);
+        picture_in_picture_button->set_tag(
+            static_cast<int>(MediaSessionAction::kEnterPictureInPicture));
+        picture_in_picture_button->SetPreferredSize(kPipButtonSize);
+        picture_in_picture_button->SetFocusBehavior(
+            views::View::FocusBehavior::ALWAYS);
+        picture_in_picture_button->SetTooltipText(l10n_util::GetStringUTF16(
+            IDS_MEDIA_MESSAGE_CENTER_MEDIA_NOTIFICATION_ACTION_ENTER_PIP));
+        picture_in_picture_button->SetToggledTooltipText(
+            l10n_util::GetStringUTF16(
+                IDS_MEDIA_MESSAGE_CENTER_MEDIA_NOTIFICATION_ACTION_EXIT_PIP));
+        picture_in_picture_button->EnableCanvasFlippingForRTLUI(false);
+        views::SetImageFromVectorIconWithColor(
+            picture_in_picture_button.get(),
+            *GetVectorIconForMediaAction(
+                MediaSessionAction::kEnterPictureInPicture),
+            kPipButtonIconSize, SK_ColorBLACK);
+        picture_in_picture_button_ = labels_container->AddChildView(
+            std::move(picture_in_picture_button));
+      }
+
+      info_container->AddChildView(std::move(labels_container));
+    }
+
+    {
+      // If there is no artwork to display, a vertical spacer should be added
+      // between the labels container and the dismiss button.
+      auto notification_controls_spacer = std::make_unique<views::View>();
+      notification_controls_spacer->SetPreferredSize(
+          kNotificationControlsSpacerSize);
+      notification_controls_spacer_ =
+          info_container->AddChildView(std::move(notification_controls_spacer));
+    }
+
+    notification_controls_view->SetProperty(views::kMarginsKey,
+                                            kNotificationControlsInsets);
+    info_container->AddChildView(std::move(notification_controls_view));
+
+    AddChildView(std::move(info_container));
+  }
+
+  {
+    // The media controls container contains buttons for media playback. This
+    // includes play/pause, fast-forward/rewind, and skip controls.
+    auto media_controls_container = std::make_unique<views::View>();
+    media_controls_container->SetPreferredSize(kMediaControlsContainerSize);
+    auto* media_controls_layout = media_controls_container->SetLayoutManager(
+        std::make_unique<views::BoxLayout>(
+            views::BoxLayout::Orientation::kHorizontal, gfx::Insets(),
+            kMediaControlsButtonSpacing));
+    media_controls_layout->set_cross_axis_alignment(
+        views::BoxLayout::CrossAxisAlignment::kCenter);
+    media_controls_layout->set_main_axis_alignment(
+        views::BoxLayout::MainAxisAlignment::kCenter);
+    media_controls_container->SetBorder(
+        views::CreateEmptyBorder(kMediaControlsBorderInsets));
+
+    // Media controls should always be presented left-to-right,
+    // regardless of the local UI direction.
+    media_controls_container->SetMirrored(false);
+
+    CreateMediaButton(
+        media_controls_container.get(), MediaSessionAction::kPreviousTrack,
+        l10n_util::GetStringUTF16(
+            IDS_MEDIA_MESSAGE_CENTER_MEDIA_NOTIFICATION_ACTION_PREVIOUS_TRACK));
+    CreateMediaButton(
+        media_controls_container.get(), MediaSessionAction::kSeekBackward,
+        l10n_util::GetStringUTF16(
+            IDS_MEDIA_MESSAGE_CENTER_MEDIA_NOTIFICATION_ACTION_SEEK_BACKWARD));
+
+    {
+      auto play_pause_button = views::CreateVectorToggleImageButton(this);
+      play_pause_button->set_tag(static_cast<int>(MediaSessionAction::kPlay));
+      play_pause_button->SetPreferredSize(kMediaButtonSize);
+      play_pause_button->SetFocusBehavior(views::View::FocusBehavior::ALWAYS);
+      play_pause_button->SetTooltipText(l10n_util::GetStringUTF16(
+          IDS_MEDIA_MESSAGE_CENTER_MEDIA_NOTIFICATION_ACTION_PLAY));
+      play_pause_button->SetToggledTooltipText(l10n_util::GetStringUTF16(
+          IDS_MEDIA_MESSAGE_CENTER_MEDIA_NOTIFICATION_ACTION_PAUSE));
+      play_pause_button->EnableCanvasFlippingForRTLUI(false);
+      play_pause_button_ =
+          media_controls_container->AddChildView(std::move(play_pause_button));
+    }
+
+    CreateMediaButton(
+        media_controls_container.get(), MediaSessionAction::kSeekForward,
+        l10n_util::GetStringUTF16(
+            IDS_MEDIA_MESSAGE_CENTER_MEDIA_NOTIFICATION_ACTION_SEEK_FORWARD));
+    CreateMediaButton(
+        media_controls_container.get(), MediaSessionAction::kNextTrack,
+        l10n_util::GetStringUTF16(
+            IDS_MEDIA_MESSAGE_CENTER_MEDIA_NOTIFICATION_ACTION_NEXT_TRACK));
+
+    media_controls_container_ =
+        AddChildView(std::move(media_controls_container));
+  }
+
+  if (item_)
+    item_->SetView(this);
+}
+
+MediaNotificationViewModernImpl::~MediaNotificationViewModernImpl() {
+  if (item_)
+    item_->SetView(nullptr);
+}
+
+void MediaNotificationViewModernImpl::UpdateCornerRadius(int top_radius,
+                                                         int bottom_radius) {
+  if (GetMediaNotificationBackground()->UpdateCornerRadius(top_radius,
+                                                           bottom_radius)) {
+    SchedulePaint();
+  }
+}
+
+void MediaNotificationViewModernImpl::GetAccessibleNodeData(
+    ui::AXNodeData* node_data) {
+  node_data->role = ax::mojom::Role::kListItem;
+  node_data->AddStringAttribute(
+      ax::mojom::StringAttribute::kRoleDescription,
+      l10n_util::GetStringUTF8(
+          IDS_MEDIA_MESSAGE_CENTER_MEDIA_NOTIFICATION_ACCESSIBLE_NAME));
+
+  if (!accessible_name_.empty())
+    node_data->SetName(accessible_name_);
+}
+
+void MediaNotificationViewModernImpl::ButtonPressed(views::Button* sender,
+                                                    const ui::Event& event) {
+  if (item_) {
+    item_->OnMediaSessionActionButtonPressed(GetActionFromButtonTag(*sender));
+  }
+}
+
+void MediaNotificationViewModernImpl::UpdateWithMediaSessionInfo(
+    const media_session::mojom::MediaSessionInfoPtr& session_info) {
+  bool playing =
+      session_info && session_info->playback_state ==
+                          media_session::mojom::MediaPlaybackState::kPlaying;
+  play_pause_button_->SetToggled(playing);
+
+  MediaSessionAction action =
+      playing ? MediaSessionAction::kPause : MediaSessionAction::kPlay;
+  play_pause_button_->set_tag(static_cast<int>(action));
+
+  bool in_picture_in_picture =
+      session_info &&
+      session_info->picture_in_picture_state ==
+          media_session::mojom::MediaPictureInPictureState::kInPictureInPicture;
+  picture_in_picture_button_->SetToggled(in_picture_in_picture);
+
+  action = in_picture_in_picture ? MediaSessionAction::kExitPictureInPicture
+                                 : MediaSessionAction::kEnterPictureInPicture;
+  picture_in_picture_button_->set_tag(static_cast<int>(action));
+
+  UpdateActionButtonsVisibility();
+
+  container_->OnMediaSessionInfoChanged(session_info);
+
+  PreferredSizeChanged();
+  Layout();
+  SchedulePaint();
+}
+
+void MediaNotificationViewModernImpl::UpdateWithMediaMetadata(
+    const media_session::MediaMetadata& metadata) {
+  title_label_->SetText(metadata.title);
+  subtitle_label_->SetText(metadata.source_title);
+
+  accessible_name_ = GetAccessibleNameFromMetadata(metadata);
+
+  // The title label should only be a11y-focusable when there is text to be
+  // read.
+  if (metadata.title.empty()) {
+    title_label_->SetFocusBehavior(FocusBehavior::NEVER);
+  } else {
+    title_label_->SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
+    RecordMetadataHistogram(Metadata::kTitle);
+  }
+
+  // The subtitle label should only be a11y-focusable when there is text to be
+  // read.
+  if (metadata.source_title.empty()) {
+    subtitle_label_->SetFocusBehavior(FocusBehavior::NEVER);
+  } else {
+    subtitle_label_->SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
+    RecordMetadataHistogram(Metadata::kSource);
+  }
+
+  RecordMetadataHistogram(Metadata::kCount);
+
+  container_->OnMediaSessionMetadataChanged(metadata);
+
+  PreferredSizeChanged();
+  Layout();
+  SchedulePaint();
+}
+
+void MediaNotificationViewModernImpl::UpdateWithMediaActions(
+    const base::flat_set<media_session::mojom::MediaSessionAction>& actions) {
+  enabled_actions_ = actions;
+
+  UpdateActionButtonsVisibility();
+
+  PreferredSizeChanged();
+  Layout();
+  SchedulePaint();
+}
+
+void MediaNotificationViewModernImpl::UpdateWithMediaArtwork(
+    const gfx::ImageSkia& image) {
+  GetMediaNotificationBackground()->UpdateArtwork(image);
+
+  UMA_HISTOGRAM_BOOLEAN(kArtworkHistogramName, !image.isNull());
+
+  if (!image.isNull()) {
+    artwork_container_->SetVisible(true);
+    // When there is artwork to display, this spacer is no logner needed
+    notification_controls_spacer_->SetVisible(false);
+  }
+
+  artwork_->SetImage(image);
+  artwork_->SetPreferredSize({70, 70});
+  artwork_->SetVignetteColor(
+      GetMediaNotificationBackground()->GetBackgroundColor(*this));
+
+  UpdateForegroundColor();
+
+  container_->OnMediaArtworkChanged(image);
+
+  PreferredSizeChanged();
+  Layout();
+  SchedulePaint();
+}
+
+void MediaNotificationViewModernImpl::UpdateWithFavicon(
+    const gfx::ImageSkia& icon) {
+  GetMediaNotificationBackground()->UpdateFavicon(icon);
+
+  UpdateForegroundColor();
+  SchedulePaint();
+}
+
+void MediaNotificationViewModernImpl::OnThemeChanged() {
+  MediaNotificationView::OnThemeChanged();
+  UpdateForegroundColor();
+}
+
+void MediaNotificationViewModernImpl::UpdateDeviceSelectorAvailability(
+    bool availability) {
+  GetMediaNotificationBackground()->UpdateDeviceSelectorAvailability(
+      availability);
+}
+
+void MediaNotificationViewModernImpl::UpdateActionButtonsVisibility() {
+  for (auto* view : media_controls_container_->children()) {
+    views::Button* action_button = views::Button::AsButton(view);
+    bool should_show = base::Contains(enabled_actions_,
+                                      GetActionFromButtonTag(*action_button));
+    bool should_invalidate = should_show != action_button->GetVisible();
+
+    action_button->SetVisible(should_show);
+
+    if (should_invalidate)
+      action_button->InvalidateLayout();
+  }
+
+  container_->OnVisibleActionsChanged(enabled_actions_);
+}
+
+void MediaNotificationViewModernImpl::CreateMediaButton(
+    views::View* parent_view,
+    MediaSessionAction action,
+    const base::string16& accessible_name) {
+  auto button = views::CreateVectorImageButton(this);
+  button->set_tag(static_cast<int>(action));
+  button->SetPreferredSize(kMediaButtonSize);
+  button->SetAccessibleName(accessible_name);
+  button->SetTooltipText(accessible_name);
+  button->SetFocusBehavior(views::View::FocusBehavior::ALWAYS);
+  button->EnableCanvasFlippingForRTLUI(false);
+  parent_view->AddChildView(std::move(button));
+}
+
+MediaNotificationBackground*
+MediaNotificationViewModernImpl::GetMediaNotificationBackground() {
+  return static_cast<MediaNotificationBackground*>(background());
+}
+
+void MediaNotificationViewModernImpl::UpdateForegroundColor() {
+  const SkColor background =
+      GetMediaNotificationBackground()->GetBackgroundColor(*this);
+  const SkColor foreground =
+      GetMediaNotificationBackground()->GetForegroundColor(*this);
+  const SkColor disabled_icon_color =
+      SkColorSetA(foreground, gfx::kDisabledControlAlpha);
+
+  // Update the colors for the labels
+  title_label_->SetEnabledColor(foreground);
+  subtitle_label_->SetEnabledColor(disabled_icon_color);
+
+  title_label_->SetBackgroundColor(background);
+  subtitle_label_->SetBackgroundColor(background);
+
+  // Update the colors for the toggle buttons (play/pause and
+  // picture-in-picture)
+  views::SetImageFromVectorIconWithColor(
+      play_pause_button_,
+      *GetVectorIconForMediaAction(MediaSessionAction::kPlay),
+      kMediaButtonIconSize, foreground);
+  views::SetToggledImageFromVectorIconWithColor(
+      play_pause_button_,
+      *GetVectorIconForMediaAction(MediaSessionAction::kPause),
+      kMediaButtonIconSize, foreground, disabled_icon_color);
+
+  views::SetImageFromVectorIconWithColor(
+      picture_in_picture_button_,
+      *GetVectorIconForMediaAction(MediaSessionAction::kEnterPictureInPicture),
+      kPipButtonIconSize, foreground);
+  views::SetToggledImageFromVectorIconWithColor(
+      picture_in_picture_button_,
+      *GetVectorIconForMediaAction(MediaSessionAction::kExitPictureInPicture),
+      kPipButtonIconSize, foreground, disabled_icon_color);
+
+  // Update the colors for the media control buttons.
+  for (views::View* child : media_controls_container_->children()) {
+    // Skip the play pause button since it is a special case.
+    if (child == play_pause_button_)
+      continue;
+
+    views::ImageButton* button = static_cast<views::ImageButton*>(child);
+
+    views::SetImageFromVectorIconWithColor(
+        button, *GetVectorIconForMediaAction(GetActionFromButtonTag(*button)),
+        kMediaButtonIconSize, foreground);
+
+    button->SchedulePaint();
+  }
+
+  SchedulePaint();
+  container_->OnColorsChanged(foreground, background);
+}
+
+}  // namespace media_message_center
diff --git a/components/media_message_center/media_notification_view_modern_impl.h b/components/media_message_center/media_notification_view_modern_impl.h
index e3332f4..96b0291 100644
--- a/components/media_message_center/media_notification_view_modern_impl.h
+++ b/components/media_message_center/media_notification_view_modern_impl.h
@@ -7,27 +7,146 @@
 
 #include "components/media_message_center/media_notification_view.h"
 
+#include "base/component_export.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "components/media_message_center/media_notification_view.h"
+#include "services/media_session/public/mojom/media_session.mojom.h"
+#include "ui/views/controls/button/button.h"
+#include "ui/views/controls/button/image_button.h"
+#include "ui/views/controls/image_view.h"
+#include "ui/views/controls/label.h"
+
+namespace views {
+class ToggleImageButton;
+}  // namespace views
+
 namespace media_message_center {
+
+namespace {
+class MediaArtworkView;
+}  // anonymous namespace
+
+class MediaNotificationBackground;
+class MediaNotificationContainer;
+class MediaNotificationItem;
+
 class COMPONENT_EXPORT(MEDIA_MESSAGE_CENTER) MediaNotificationViewModernImpl
-    : public MediaNotificationView {
+    : public MediaNotificationView,
+      public views::ButtonListener {
  public:
-  MediaNotificationViewModernImpl() = default;
+  // The name of the histogram used when recording whether the artwork was
+  // present.
+  static const char kArtworkHistogramName[];
+
+  // The name of the histogram used when recording the type of metadata that was
+  // displayed.
+  static const char kMetadataHistogramName[];
+
+  // The type of metadata that was displayed. This is used in metrics so new
+  // values must only be added to the end.
+  enum class Metadata {
+    kTitle,
+    kArtist,
+    kAlbum,
+    kCount,
+    kSource,
+    kMaxValue = kSource,
+  };
+
+  MediaNotificationViewModernImpl(
+      MediaNotificationContainer* container,
+      base::WeakPtr<MediaNotificationItem> item,
+      std::unique_ptr<views::View> notification_controls_view,
+      int notification_width);
+  MediaNotificationViewModernImpl(const MediaNotificationViewModernImpl&) =
+      delete;
+  MediaNotificationViewModernImpl& operator=(
+      const MediaNotificationViewModernImpl&) = delete;
+  ~MediaNotificationViewModernImpl() override;
+
+  // views::View:
+  void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
+  void OnThemeChanged() override;
+
+  // views::ButtonListener:
+  void ButtonPressed(views::Button* sender, const ui::Event& event) override;
 
   // MediaNotificationView
   void SetForcedExpandedState(bool* forced_expanded_state) override {}
   void SetExpanded(bool expanded) override {}
-  void UpdateCornerRadius(int top_radius, int bottom_radius) override {}
+  void UpdateCornerRadius(int top_radius, int bottom_radius) override;
   void UpdateWithMediaSessionInfo(
-      const media_session::mojom::MediaSessionInfoPtr& session_info) override {}
+      const media_session::mojom::MediaSessionInfoPtr& session_info) override;
   void UpdateWithMediaMetadata(
-      const media_session::MediaMetadata& metadata) override {}
+      const media_session::MediaMetadata& metadata) override;
   void UpdateWithMediaActions(
       const base::flat_set<media_session::mojom::MediaSessionAction>& actions)
-      override {}
-  void UpdateWithMediaArtwork(const gfx::ImageSkia& image) override {}
-  void UpdateWithFavicon(const gfx::ImageSkia& icon) override {}
+      override;
+  void UpdateWithMediaArtwork(const gfx::ImageSkia& image) override;
+  void UpdateWithFavicon(const gfx::ImageSkia& icon) override;
   void UpdateWithVectorIcon(const gfx::VectorIcon& vector_icon) override {}
-  void UpdateDeviceSelectorAvailability(bool availability) override {}
+  void UpdateDeviceSelectorAvailability(bool availability) override;
+
+  // Testing methods
+  const views::Label* title_label_for_testing() const { return title_label_; }
+
+  const views::Label* subtitle_label_for_testing() const {
+    return subtitle_label_;
+  }
+
+  const views::Button* picture_in_picture_button_for_testing() const {
+    return picture_in_picture_button_;
+  }
+
+  const views::View* media_controls_container_for_testing() const {
+    return media_controls_container_;
+  }
+
+ private:
+  friend class MediaNotificationViewModernImplTest;
+
+  // Creates an image button with an icon that matches |action| and adds it
+  // to |parent_view|. When clicked it will trigger |action| on the session.
+  // |accessible_name| is the text used for screen readers and the
+  // button's tooltip.
+  void CreateMediaButton(views::View* parent_view,
+                         media_session::mojom::MediaSessionAction action,
+                         const base::string16& accessible_name);
+
+  void UpdateActionButtonsVisibility();
+
+  MediaNotificationBackground* GetMediaNotificationBackground();
+
+  void UpdateForegroundColor();
+
+  // Container that receives events.
+  MediaNotificationContainer* const container_;
+
+  // Keeps track of media metadata and controls the session when buttons are
+  // clicked.
+  base::WeakPtr<MediaNotificationItem> item_;
+
+  bool has_artwork_ = false;
+
+  // Set of enabled actions.
+  base::flat_set<media_session::mojom::MediaSessionAction> enabled_actions_;
+
+  // Stores the text to be read by screen readers describing the notification.
+  // Contains the title, artist and album separated by hyphens.
+  base::string16 accessible_name_;
+
+  MediaNotificationBackground* background_;
+
+  // Container views directly attached to this view.
+  views::View* artwork_container_ = nullptr;
+  MediaArtworkView* artwork_ = nullptr;
+  views::Label* title_label_ = nullptr;
+  views::Label* subtitle_label_ = nullptr;
+  views::ToggleImageButton* picture_in_picture_button_ = nullptr;
+  views::View* notification_controls_spacer_ = nullptr;
+  views::View* media_controls_container_ = nullptr;
+  views::ToggleImageButton* play_pause_button_ = nullptr;
 };
 
 }  // namespace media_message_center
diff --git a/components/media_message_center/media_notification_view_modern_impl_unittest.cc b/components/media_message_center/media_notification_view_modern_impl_unittest.cc
new file mode 100644
index 0000000..024c2ba9
--- /dev/null
+++ b/components/media_message_center/media_notification_view_modern_impl_unittest.cc
@@ -0,0 +1,1072 @@
+// Copyright 2020 The Chromium 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/media_message_center/media_notification_view_modern_impl.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/containers/flat_set.h"
+#include "base/macros.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/mock_callback.h"
+#include "base/test/task_environment.h"
+#include "base/unguessable_token.h"
+#include "build/build_config.h"
+#include "components/media_message_center/media_notification_background.h"
+#include "components/media_message_center/media_notification_constants.h"
+#include "components/media_message_center/media_notification_container.h"
+#include "components/media_message_center/media_notification_controller.h"
+#include "components/media_message_center/media_notification_util.h"
+#include "components/media_message_center/media_session_notification_item.h"
+#include "services/media_session/public/cpp/test/test_media_controller.h"
+#include "services/media_session/public/mojom/audio_focus.mojom.h"
+#include "services/media_session/public/mojom/media_session.mojom.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "ui/accessibility/ax_enums.mojom.h"
+#include "ui/accessibility/ax_node_data.h"
+#include "ui/events/base_event_utils.h"
+#include "ui/message_center/message_center.h"
+#include "ui/message_center/public/cpp/message_center_constants.h"
+#include "ui/message_center/views/message_view_factory.h"
+#include "ui/message_center/views/notification_control_buttons_view.h"
+#include "ui/message_center/views/notification_header_view.h"
+#include "ui/views/controls/image_view.h"
+#include "ui/views/test/views_test_base.h"
+
+namespace media_message_center {
+
+using media_session::mojom::MediaSessionAction;
+using media_session::test::TestMediaController;
+using testing::_;
+using testing::Expectation;
+using testing::Invoke;
+
+namespace {
+
+const int kMediaButtonIconSize = 20;
+const int kPipButtonIconSize = 18;
+
+const gfx::Size kWidgetSize(500, 500);
+
+constexpr int kViewWidth = 350;
+const gfx::Size kViewSize(kViewWidth, 400);
+
+class MockMediaNotificationController : public MediaNotificationController {
+ public:
+  MockMediaNotificationController() = default;
+  ~MockMediaNotificationController() override = default;
+
+  // MediaNotificationController implementation.
+  MOCK_METHOD1(ShowNotification, void(const std::string& id));
+  MOCK_METHOD1(HideNotification, void(const std::string& id));
+  MOCK_METHOD1(RemoveItem, void(const std::string& id));
+  scoped_refptr<base::SequencedTaskRunner> GetTaskRunner() const override {
+    return nullptr;
+  }
+  MOCK_METHOD2(LogMediaSessionActionButtonPressed,
+               void(const std::string& id,
+                    media_session::mojom::MediaSessionAction action));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockMediaNotificationController);
+};
+
+class MockMediaNotificationContainer : public MediaNotificationContainer {
+ public:
+  MockMediaNotificationContainer() = default;
+  ~MockMediaNotificationContainer() override = default;
+
+  // MediaNotificationContainer implementation.
+  MOCK_METHOD1(OnExpanded, void(bool expanded));
+  MOCK_METHOD1(
+      OnMediaSessionInfoChanged,
+      void(const media_session::mojom::MediaSessionInfoPtr& session_info));
+  MOCK_METHOD1(OnMediaSessionMetadataChanged,
+               void(const media_session::MediaMetadata& metadata));
+  MOCK_METHOD1(OnVisibleActionsChanged,
+               void(const base::flat_set<MediaSessionAction>& actions));
+  MOCK_METHOD1(OnMediaArtworkChanged, void(const gfx::ImageSkia& image));
+  MOCK_METHOD2(OnColorsChanged, void(SkColor foreground, SkColor background));
+  MOCK_METHOD0(OnHeaderClicked, void());
+
+  MediaNotificationViewModernImpl* view() const { return view_; }
+  void SetView(MediaNotificationViewModernImpl* view) { view_ = view; }
+
+ private:
+  MediaNotificationViewModernImpl* view_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockMediaNotificationContainer);
+};
+
+}  // namespace
+
+class MediaNotificationViewModernImplTest : public views::ViewsTestBase {
+ public:
+  MediaNotificationViewModernImplTest()
+      : views::ViewsTestBase(
+            base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
+  ~MediaNotificationViewModernImplTest() override = default;
+
+  void SetUp() override {
+    views::ViewsTestBase::SetUp();
+
+    request_id_ = base::UnguessableToken::Create();
+
+    // Create a new MediaNotificationViewModernImpl whenever the
+    // MediaSessionNotificationItem says to show the notification.
+    EXPECT_CALL(controller_, ShowNotification(request_id_.ToString()))
+        .WillRepeatedly(InvokeWithoutArgs(
+            this, &MediaNotificationViewModernImplTest::CreateView));
+
+    // Create a widget to show on the screen for testing screen coordinates and
+    // focus.
+    widget_ = std::make_unique<views::Widget>();
+    views::Widget::InitParams params =
+        CreateParams(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
+    params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+    params.bounds = gfx::Rect(kWidgetSize);
+    widget_->Init(std::move(params));
+    widget_->Show();
+
+    CreateViewFromMediaSessionInfo(
+        media_session::mojom::MediaSessionInfo::New());
+  }
+
+  void CreateViewFromMediaSessionInfo(
+      media_session::mojom::MediaSessionInfoPtr session_info) {
+    session_info->is_controllable = true;
+    mojo::Remote<media_session::mojom::MediaController> controller;
+    item_ = std::make_unique<MediaSessionNotificationItem>(
+        &controller_, request_id_.ToString(), std::string(),
+        std::move(controller), std::move(session_info));
+
+    // Update the metadata.
+    media_session::MediaMetadata metadata;
+    metadata.title = base::ASCIIToUTF16("title");
+    metadata.artist = base::ASCIIToUTF16("artist");
+    metadata.source_title = base::ASCIIToUTF16("source title");
+    item_->MediaSessionMetadataChanged(metadata);
+
+    // Inject the test media controller into the item.
+    media_controller_ = std::make_unique<TestMediaController>();
+    item_->SetMediaControllerForTesting(
+        media_controller_->CreateMediaControllerRemote());
+  }
+
+  void TearDown() override {
+    container_.SetView(nullptr);
+    widget_.reset();
+
+    actions_.clear();
+
+    views::ViewsTestBase::TearDown();
+  }
+
+  void EnableAllActions() {
+    actions_.insert(MediaSessionAction::kPlay);
+    actions_.insert(MediaSessionAction::kPause);
+    actions_.insert(MediaSessionAction::kPreviousTrack);
+    actions_.insert(MediaSessionAction::kNextTrack);
+    actions_.insert(MediaSessionAction::kSeekBackward);
+    actions_.insert(MediaSessionAction::kSeekForward);
+    actions_.insert(MediaSessionAction::kStop);
+    actions_.insert(MediaSessionAction::kEnterPictureInPicture);
+    actions_.insert(MediaSessionAction::kExitPictureInPicture);
+
+    NotifyUpdatedActions();
+  }
+
+  void EnableAction(MediaSessionAction action) {
+    actions_.insert(action);
+    NotifyUpdatedActions();
+  }
+
+  void DisableAction(MediaSessionAction action) {
+    actions_.erase(action);
+    NotifyUpdatedActions();
+  }
+
+  MockMediaNotificationContainer& container() { return container_; }
+
+  MockMediaNotificationController& controller() { return controller_; }
+
+  MediaNotificationViewModernImpl* view() const { return container_.view(); }
+
+  TestMediaController* media_controller() const {
+    return media_controller_.get();
+  }
+
+  const base::string16& accessible_name() const {
+    return view()->accessible_name_;
+  }
+
+  views::Label* title_label() const { return view()->title_label_; }
+
+  views::Label* subtitle_label() const { return view()->subtitle_label_; }
+
+  views::View* artwork_container() const { return view()->artwork_container_; }
+
+  views::View* media_controls_container() const {
+    return view()->media_controls_container_;
+  }
+
+  std::vector<views::Button*> media_control_buttons() const {
+    std::vector<views::Button*> buttons;
+    auto children = view()->media_controls_container_->children();
+    std::transform(
+        children.begin(), children.end(), std::back_inserter(buttons),
+        [](views::View* child) { return views::Button::AsButton(child); });
+    buttons.push_back(
+        views::Button::AsButton(view()->picture_in_picture_button_));
+    return buttons;
+  }
+
+  views::Button* picture_in_picture_button() const {
+    return view()->picture_in_picture_button_;
+  }
+
+  views::Button* GetButtonForAction(MediaSessionAction action) const {
+    auto buttons = media_control_buttons();
+    const auto i = std::find_if(
+        buttons.begin(), buttons.end(), [action](const views::Button* button) {
+          return button->tag() == static_cast<int>(action);
+        });
+    return (i == buttons.end()) ? nullptr : *i;
+  }
+
+  bool IsActionButtonVisible(MediaSessionAction action) const {
+    return GetButtonForAction(action)->GetVisible();
+  }
+
+  MediaSessionNotificationItem* GetItem() const { return item_.get(); }
+
+  const gfx::ImageSkia& GetArtworkImage() const {
+    return view()->GetMediaNotificationBackground()->artwork_;
+  }
+
+  void SimulateButtonClick(MediaSessionAction action) {
+    views::Button* button = GetButtonForAction(action);
+    EXPECT_TRUE(button->GetVisible());
+
+    view()->ButtonPressed(
+        button, ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
+                               ui::EventTimeForNow(), 0, 0));
+  }
+
+  void SimulateTab() {
+    ui::KeyEvent pressed_tab(ui::ET_KEY_PRESSED, ui::VKEY_TAB, ui::EF_NONE);
+    view()->GetFocusManager()->OnKeyEvent(pressed_tab);
+  }
+
+  void ExpectHistogramActionRecorded(MediaSessionAction action) {
+    histogram_tester_.ExpectUniqueSample(
+        MediaSessionNotificationItem::kUserActionHistogramName,
+        static_cast<base::HistogramBase::Sample>(action), 1);
+  }
+
+  void ExpectHistogramArtworkRecorded(bool present, int count) {
+    histogram_tester_.ExpectBucketCount(
+        MediaNotificationViewModernImpl::kArtworkHistogramName,
+        static_cast<base::HistogramBase::Sample>(present), count);
+  }
+
+  void ExpectHistogramMetadataRecorded(
+      MediaNotificationViewModernImpl::Metadata metadata,
+      int count) {
+    histogram_tester_.ExpectBucketCount(
+        MediaNotificationViewModernImpl::kMetadataHistogramName,
+        static_cast<base::HistogramBase::Sample>(metadata), count);
+  }
+
+  void AdvanceClockMilliseconds(int milliseconds) {
+    task_environment()->FastForwardBy(
+        base::TimeDelta::FromMilliseconds(milliseconds));
+  }
+
+ private:
+  void NotifyUpdatedActions() {
+    item_->MediaSessionActionsChanged(
+        std::vector<MediaSessionAction>(actions_.begin(), actions_.end()));
+  }
+
+  void CreateView() {
+    // Create a MediaNotificationViewModernImpl.
+    auto view = std::make_unique<MediaNotificationViewModernImpl>(
+        &container_, item_->GetWeakPtr(), std::make_unique<views::View>(),
+        kViewWidth);
+    view->SetSize(kViewSize);
+
+    // Display it in |widget_|. Widget now owns |view|.
+    // And associate it with |container_|.
+    container_.SetView(widget_->SetContentsView(std::move(view)));
+  }
+
+  base::UnguessableToken request_id_;
+
+  base::HistogramTester histogram_tester_;
+
+  base::flat_set<MediaSessionAction> actions_;
+
+  std::unique_ptr<TestMediaController> media_controller_;
+  MockMediaNotificationContainer container_;
+  MockMediaNotificationController controller_;
+  std::unique_ptr<MediaSessionNotificationItem> item_;
+  std::unique_ptr<views::Widget> widget_;
+
+  DISALLOW_COPY_AND_ASSIGN(MediaNotificationViewModernImplTest);
+};
+
+// TODO(crbug.com/1009287): many of these tests are failing on TSan builds.
+#if defined(THREAD_SANITIZER)
+#define MAYBE_MediaNotificationViewModernImplTest \
+  DISABLED_MediaNotificationViewModernImplTest
+class DISABLED_MediaNotificationViewModernImplTest
+    : public MediaNotificationViewModernImplTest {};
+#else
+#define MAYBE_MediaNotificationViewModernImplTest \
+  MediaNotificationViewModernImplTest
+#endif
+
+TEST_F(MAYBE_MediaNotificationViewModernImplTest, ButtonsSanityCheck) {
+  EnableAllActions();
+
+  EXPECT_TRUE(media_controls_container()->GetVisible());
+  EXPECT_GT(media_controls_container()->width(), 0);
+  EXPECT_GT(media_controls_container()->height(), 0);
+
+  auto buttons = media_control_buttons();
+  EXPECT_EQ(6u, buttons.size());
+
+  for (auto* button : buttons) {
+    EXPECT_TRUE(button->GetVisible());
+    if (button == picture_in_picture_button()) {
+      EXPECT_LT(kPipButtonIconSize, button->width());
+      EXPECT_LT(kPipButtonIconSize, button->height());
+    } else {
+      EXPECT_LT(kMediaButtonIconSize, button->width());
+      EXPECT_LT(kMediaButtonIconSize, button->height());
+    }
+    EXPECT_FALSE(views::Button::AsButton(button)->GetAccessibleName().empty());
+  }
+
+  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
+  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPreviousTrack));
+  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kNextTrack));
+  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kSeekBackward));
+  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kSeekForward));
+  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kEnterPictureInPicture));
+
+  // |kPause| cannot be present if |kPlay| is.
+  EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
+  EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kExitPictureInPicture));
+}
+
+#if defined(OS_WIN)
+#define MAYBE_ButtonsFocusCheck DISABLED_ButtonsFocusCheck
+#else
+#define MAYBE_ButtonsFocusCheck ButtonsFocusCheck
+#endif
+TEST_F(MAYBE_MediaNotificationViewModernImplTest, MAYBE_ButtonsFocusCheck) {
+  // Expand and enable all actions to show all buttons.
+  EnableAllActions();
+
+  views::FocusManager* focus_manager = view()->GetFocusManager();
+
+  {
+    // Focus the first action button.
+    auto* button = GetButtonForAction(MediaSessionAction::kPreviousTrack);
+    focus_manager->SetFocusedView(button);
+    EXPECT_EQ(button, focus_manager->GetFocusedView());
+  }
+
+  SimulateTab();
+  EXPECT_EQ(GetButtonForAction(MediaSessionAction::kSeekBackward),
+            focus_manager->GetFocusedView());
+
+  SimulateTab();
+  EXPECT_EQ(GetButtonForAction(MediaSessionAction::kPlay),
+            focus_manager->GetFocusedView());
+
+  SimulateTab();
+  EXPECT_EQ(GetButtonForAction(MediaSessionAction::kSeekForward),
+            focus_manager->GetFocusedView());
+
+  SimulateTab();
+  EXPECT_EQ(GetButtonForAction(MediaSessionAction::kNextTrack),
+            focus_manager->GetFocusedView());
+}
+
+TEST_F(MAYBE_MediaNotificationViewModernImplTest, PlayPauseButtonTooltipCheck) {
+  EnableAction(MediaSessionAction::kPlay);
+  EnableAction(MediaSessionAction::kPause);
+
+  EXPECT_CALL(container(), OnMediaSessionInfoChanged(_));
+
+  auto* button = GetButtonForAction(MediaSessionAction::kPlay);
+  base::string16 tooltip = button->GetTooltipText(gfx::Point());
+  EXPECT_FALSE(tooltip.empty());
+
+  media_session::mojom::MediaSessionInfoPtr session_info(
+      media_session::mojom::MediaSessionInfo::New());
+  session_info->playback_state =
+      media_session::mojom::MediaPlaybackState::kPlaying;
+  session_info->is_controllable = true;
+  GetItem()->MediaSessionInfoChanged(session_info.Clone());
+
+  base::string16 new_tooltip = button->GetTooltipText(gfx::Point());
+  EXPECT_FALSE(new_tooltip.empty());
+  EXPECT_NE(tooltip, new_tooltip);
+}
+
+TEST_F(MAYBE_MediaNotificationViewModernImplTest, NextTrackButtonClick) {
+  EXPECT_CALL(controller(), LogMediaSessionActionButtonPressed(
+                                _, MediaSessionAction::kNextTrack));
+  EnableAction(MediaSessionAction::kNextTrack);
+
+  EXPECT_EQ(0, media_controller()->next_track_count());
+
+  SimulateButtonClick(MediaSessionAction::kNextTrack);
+  GetItem()->FlushForTesting();
+
+  EXPECT_EQ(1, media_controller()->next_track_count());
+  ExpectHistogramActionRecorded(MediaSessionAction::kNextTrack);
+}
+
+TEST_F(MAYBE_MediaNotificationViewModernImplTest, PlayButtonClick) {
+  EXPECT_CALL(controller(),
+              LogMediaSessionActionButtonPressed(_, MediaSessionAction::kPlay));
+  EnableAction(MediaSessionAction::kPlay);
+
+  EXPECT_EQ(0, media_controller()->resume_count());
+
+  SimulateButtonClick(MediaSessionAction::kPlay);
+  GetItem()->FlushForTesting();
+
+  EXPECT_EQ(1, media_controller()->resume_count());
+  ExpectHistogramActionRecorded(MediaSessionAction::kPlay);
+}
+
+TEST_F(MAYBE_MediaNotificationViewModernImplTest, PauseButtonClick) {
+  EXPECT_CALL(controller(), LogMediaSessionActionButtonPressed(
+                                _, MediaSessionAction::kPause));
+  EnableAction(MediaSessionAction::kPause);
+  EXPECT_CALL(container(), OnMediaSessionInfoChanged(_));
+
+  EXPECT_EQ(0, media_controller()->suspend_count());
+
+  media_session::mojom::MediaSessionInfoPtr session_info(
+      media_session::mojom::MediaSessionInfo::New());
+  session_info->playback_state =
+      media_session::mojom::MediaPlaybackState::kPlaying;
+  session_info->is_controllable = true;
+  GetItem()->MediaSessionInfoChanged(session_info.Clone());
+
+  SimulateButtonClick(MediaSessionAction::kPause);
+  GetItem()->FlushForTesting();
+
+  EXPECT_EQ(1, media_controller()->suspend_count());
+  ExpectHistogramActionRecorded(MediaSessionAction::kPause);
+}
+
+TEST_F(MAYBE_MediaNotificationViewModernImplTest, PreviousTrackButtonClick) {
+  EXPECT_CALL(controller(), LogMediaSessionActionButtonPressed(
+                                _, MediaSessionAction::kPreviousTrack));
+  EnableAction(MediaSessionAction::kPreviousTrack);
+
+  EXPECT_EQ(0, media_controller()->previous_track_count());
+
+  SimulateButtonClick(MediaSessionAction::kPreviousTrack);
+  GetItem()->FlushForTesting();
+
+  EXPECT_EQ(1, media_controller()->previous_track_count());
+  ExpectHistogramActionRecorded(MediaSessionAction::kPreviousTrack);
+}
+
+TEST_F(MAYBE_MediaNotificationViewModernImplTest, SeekBackwardButtonClick) {
+  EXPECT_CALL(controller(), LogMediaSessionActionButtonPressed(
+                                _, MediaSessionAction::kSeekBackward));
+  EnableAction(MediaSessionAction::kSeekBackward);
+
+  EXPECT_EQ(0, media_controller()->seek_backward_count());
+
+  SimulateButtonClick(MediaSessionAction::kSeekBackward);
+  GetItem()->FlushForTesting();
+
+  EXPECT_EQ(1, media_controller()->seek_backward_count());
+  ExpectHistogramActionRecorded(MediaSessionAction::kSeekBackward);
+}
+
+TEST_F(MAYBE_MediaNotificationViewModernImplTest, SeekForwardButtonClick) {
+  EXPECT_CALL(controller(), LogMediaSessionActionButtonPressed(
+                                _, MediaSessionAction::kSeekForward));
+  EnableAction(MediaSessionAction::kSeekForward);
+
+  EXPECT_EQ(0, media_controller()->seek_forward_count());
+
+  SimulateButtonClick(MediaSessionAction::kSeekForward);
+  GetItem()->FlushForTesting();
+
+  EXPECT_EQ(1, media_controller()->seek_forward_count());
+  ExpectHistogramActionRecorded(MediaSessionAction::kSeekForward);
+}
+
+TEST_F(MAYBE_MediaNotificationViewModernImplTest,
+       PlayToggle_FromObserver_Empty) {
+  EnableAction(MediaSessionAction::kPlay);
+
+  {
+    views::ToggleImageButton* button = static_cast<views::ToggleImageButton*>(
+        GetButtonForAction(MediaSessionAction::kPlay));
+    ASSERT_EQ(views::ToggleImageButton::kViewClassName, button->GetClassName());
+    EXPECT_FALSE(button->toggled());
+  }
+
+  view()->UpdateWithMediaSessionInfo(
+      media_session::mojom::MediaSessionInfo::New());
+
+  {
+    views::ToggleImageButton* button = static_cast<views::ToggleImageButton*>(
+        GetButtonForAction(MediaSessionAction::kPlay));
+    ASSERT_EQ(views::ToggleImageButton::kViewClassName, button->GetClassName());
+    EXPECT_FALSE(button->toggled());
+  }
+}
+
+TEST_F(MAYBE_MediaNotificationViewModernImplTest,
+       PlayToggle_FromObserver_PlaybackState) {
+  EnableAction(MediaSessionAction::kPlay);
+  EnableAction(MediaSessionAction::kPause);
+
+  {
+    views::ToggleImageButton* button = static_cast<views::ToggleImageButton*>(
+        GetButtonForAction(MediaSessionAction::kPlay));
+    ASSERT_EQ(views::ToggleImageButton::kViewClassName, button->GetClassName());
+    EXPECT_FALSE(button->toggled());
+  }
+
+  media_session::mojom::MediaSessionInfoPtr session_info(
+      media_session::mojom::MediaSessionInfo::New());
+
+  session_info->playback_state =
+      media_session::mojom::MediaPlaybackState::kPlaying;
+  view()->UpdateWithMediaSessionInfo(session_info.Clone());
+
+  {
+    views::ToggleImageButton* button = static_cast<views::ToggleImageButton*>(
+        GetButtonForAction(MediaSessionAction::kPause));
+    ASSERT_EQ(views::ToggleImageButton::kViewClassName, button->GetClassName());
+    EXPECT_TRUE(button->toggled());
+  }
+
+  session_info->playback_state =
+      media_session::mojom::MediaPlaybackState::kPaused;
+  view()->UpdateWithMediaSessionInfo(session_info.Clone());
+
+  {
+    views::ToggleImageButton* button = static_cast<views::ToggleImageButton*>(
+        GetButtonForAction(MediaSessionAction::kPlay));
+    ASSERT_EQ(views::ToggleImageButton::kViewClassName, button->GetClassName());
+    EXPECT_FALSE(button->toggled());
+  }
+}
+
+TEST_F(MAYBE_MediaNotificationViewModernImplTest, MetadataIsDisplayed) {
+  EnableAllActions();
+
+  EXPECT_TRUE(title_label()->GetVisible());
+  EXPECT_TRUE(subtitle_label()->GetVisible());
+
+  EXPECT_EQ(base::ASCIIToUTF16("title"), title_label()->GetText());
+  EXPECT_EQ(base::ASCIIToUTF16("source title"), subtitle_label()->GetText());
+}
+
+TEST_F(MAYBE_MediaNotificationViewModernImplTest, UpdateMetadata_FromObserver) {
+  EnableAllActions();
+
+  ExpectHistogramMetadataRecorded(
+      MediaNotificationViewModernImpl::Metadata::kTitle, 1);
+  ExpectHistogramMetadataRecorded(
+      MediaNotificationViewModernImpl::Metadata::kSource, 1);
+  ExpectHistogramMetadataRecorded(
+      MediaNotificationViewModernImpl::Metadata::kCount, 1);
+
+  media_session::MediaMetadata metadata;
+  metadata.title = base::ASCIIToUTF16("title2");
+  metadata.source_title = base::ASCIIToUTF16("source title2");
+  metadata.artist = base::ASCIIToUTF16("artist2");
+  metadata.album = base::ASCIIToUTF16("album");
+
+  EXPECT_CALL(container(), OnMediaSessionMetadataChanged(_));
+  GetItem()->MediaSessionMetadataChanged(metadata);
+
+  EXPECT_TRUE(title_label()->GetVisible());
+  EXPECT_TRUE(subtitle_label()->GetVisible());
+
+  EXPECT_EQ(metadata.title, title_label()->GetText());
+  EXPECT_EQ(metadata.source_title, subtitle_label()->GetText());
+
+  EXPECT_EQ(base::ASCIIToUTF16("title2 - artist2 - album"), accessible_name());
+
+  ExpectHistogramMetadataRecorded(
+      MediaNotificationViewModernImpl::Metadata::kTitle, 2);
+  ExpectHistogramMetadataRecorded(
+      MediaNotificationViewModernImpl::Metadata::kSource, 2);
+  ExpectHistogramMetadataRecorded(
+      MediaNotificationViewModernImpl::Metadata::kCount, 2);
+}
+
+TEST_F(MAYBE_MediaNotificationViewModernImplTest,
+       ActionButtonsHiddenByDefault) {
+  EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kPlay));
+  EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kNextTrack));
+  EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kPreviousTrack));
+  EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kSeekForward));
+  EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kSeekBackward));
+}
+
+TEST_F(MAYBE_MediaNotificationViewModernImplTest,
+       ActionButtonsToggleVisibility) {
+  EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kNextTrack));
+
+  EnableAction(MediaSessionAction::kNextTrack);
+
+  EXPECT_TRUE(IsActionButtonVisible(MediaSessionAction::kNextTrack));
+
+  DisableAction(MediaSessionAction::kNextTrack);
+
+  EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kNextTrack));
+}
+
+TEST_F(MAYBE_MediaNotificationViewModernImplTest, UpdateArtworkFromItem) {
+  int labels_container_width = title_label()->parent()->width();
+  gfx::Size size = view()->size();
+  EXPECT_CALL(container(), OnMediaArtworkChanged(_)).Times(2);
+  EXPECT_CALL(container(), OnColorsChanged(_, _)).Times(2);
+
+  SkBitmap image;
+  image.allocN32Pixels(10, 10);
+  image.eraseColor(SK_ColorGREEN);
+
+  EXPECT_TRUE(GetArtworkImage().isNull());
+
+  GetItem()->MediaControllerImageChanged(
+      media_session::mojom::MediaSessionImageType::kArtwork, image);
+
+  ExpectHistogramArtworkRecorded(true, 1);
+
+  // The size of the labels container should not change when there is artwork.
+  EXPECT_EQ(labels_container_width, title_label()->parent()->width());
+
+  // Ensure that the labels container does not extend into the artwork bounds.
+  EXPECT_FALSE(artwork_container()->bounds().Intersects(
+      title_label()->parent()->bounds()));
+
+  // Ensure that when the image is displayed that the size of the notification
+  // was not affected.
+  EXPECT_FALSE(GetArtworkImage().isNull());
+  EXPECT_EQ(gfx::Size(10, 10), GetArtworkImage().size());
+  EXPECT_EQ(size, view()->size());
+
+  GetItem()->MediaControllerImageChanged(
+      media_session::mojom::MediaSessionImageType::kArtwork, SkBitmap());
+
+  ExpectHistogramArtworkRecorded(false, 1);
+
+  // Ensure the labels container goes back to the original width now that we
+  // do not have any artwork.
+  EXPECT_EQ(labels_container_width, title_label()->parent()->width());
+
+  // Ensure that the artwork was reset and the size was still not
+  // affected.
+  EXPECT_TRUE(GetArtworkImage().isNull());
+  EXPECT_EQ(size, view()->size());
+}
+
+TEST_F(MAYBE_MediaNotificationViewModernImplTest, AccessibleNodeData) {
+  ui::AXNodeData data;
+  view()->GetAccessibleNodeData(&data);
+
+  EXPECT_TRUE(
+      data.HasStringAttribute(ax::mojom::StringAttribute::kRoleDescription));
+  EXPECT_EQ(base::ASCIIToUTF16("title - artist"), accessible_name());
+}
+
+TEST_F(MAYBE_MediaNotificationViewModernImplTest,
+       Freezing_DoNotUpdateMetadata) {
+  media_session::MediaMetadata metadata;
+  metadata.title = base::ASCIIToUTF16("title2");
+  metadata.artist = base::ASCIIToUTF16("artist2");
+  metadata.album = base::ASCIIToUTF16("album");
+
+  EXPECT_CALL(container(), OnMediaSessionMetadataChanged(_)).Times(0);
+  GetItem()->Freeze(base::DoNothing());
+  GetItem()->MediaSessionMetadataChanged(metadata);
+
+  EXPECT_EQ(base::ASCIIToUTF16("title"), title_label()->GetText());
+  EXPECT_EQ(base::ASCIIToUTF16("source title"), subtitle_label()->GetText());
+}
+
+TEST_F(MAYBE_MediaNotificationViewModernImplTest, Freezing_DoNotUpdateImage) {
+  SkBitmap image;
+  image.allocN32Pixels(10, 10);
+  image.eraseColor(SK_ColorMAGENTA);
+  EXPECT_CALL(container(), OnMediaArtworkChanged(_)).Times(0);
+  EXPECT_CALL(container(), OnColorsChanged(_, _)).Times(0);
+
+  GetItem()->Freeze(base::DoNothing());
+  GetItem()->MediaControllerImageChanged(
+      media_session::mojom::MediaSessionImageType::kArtwork, image);
+
+  EXPECT_TRUE(GetArtworkImage().isNull());
+}
+
+TEST_F(MAYBE_MediaNotificationViewModernImplTest,
+       Freezing_DoNotUpdatePlaybackState) {
+  EnableAction(MediaSessionAction::kPlay);
+  EnableAction(MediaSessionAction::kPause);
+
+  EXPECT_CALL(container(), OnMediaSessionInfoChanged(_)).Times(0);
+
+  GetItem()->Freeze(base::DoNothing());
+
+  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
+  EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
+
+  media_session::mojom::MediaSessionInfoPtr session_info(
+      media_session::mojom::MediaSessionInfo::New());
+  session_info->playback_state =
+      media_session::mojom::MediaPlaybackState::kPlaying;
+  GetItem()->MediaSessionInfoChanged(session_info.Clone());
+
+  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
+  EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
+}
+
+TEST_F(MAYBE_MediaNotificationViewModernImplTest, Freezing_DoNotUpdateActions) {
+  EXPECT_FALSE(
+      GetButtonForAction(MediaSessionAction::kSeekForward)->GetVisible());
+
+  GetItem()->Freeze(base::DoNothing());
+  EnableAction(MediaSessionAction::kSeekForward);
+
+  EXPECT_FALSE(
+      GetButtonForAction(MediaSessionAction::kSeekForward)->GetVisible());
+}
+
+TEST_F(MAYBE_MediaNotificationViewModernImplTest, Freezing_DisableInteraction) {
+  EnableAllActions();
+
+  EXPECT_EQ(0, media_controller()->next_track_count());
+
+  GetItem()->Freeze(base::DoNothing());
+
+  SimulateButtonClick(MediaSessionAction::kNextTrack);
+  GetItem()->FlushForTesting();
+
+  EXPECT_EQ(0, media_controller()->next_track_count());
+}
+
+TEST_F(MAYBE_MediaNotificationViewModernImplTest, UnfreezingDoesntMissUpdates) {
+  EnableAction(MediaSessionAction::kPlay);
+  EnableAction(MediaSessionAction::kPause);
+
+  // Freeze the item and clear the metadata.
+  base::MockOnceClosure unfrozen_callback;
+  EXPECT_CALL(unfrozen_callback, Run).Times(0);
+  GetItem()->Freeze(unfrozen_callback.Get());
+  GetItem()->MediaSessionInfoChanged(nullptr);
+  GetItem()->MediaSessionMetadataChanged(base::nullopt);
+
+  // The item should be frozen and the view should contain the old data.
+  EXPECT_TRUE(GetItem()->frozen());
+  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
+  EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
+  EXPECT_EQ(base::ASCIIToUTF16("title"), title_label()->GetText());
+  EXPECT_EQ(base::ASCIIToUTF16("source title"), subtitle_label()->GetText());
+
+  // Bind the item to a new controller that's playing instead of paused.
+  auto new_media_controller = std::make_unique<TestMediaController>();
+  media_session::mojom::MediaSessionInfoPtr session_info(
+      media_session::mojom::MediaSessionInfo::New());
+  session_info->playback_state =
+      media_session::mojom::MediaPlaybackState::kPlaying;
+  session_info->is_controllable = true;
+  GetItem()->SetController(new_media_controller->CreateMediaControllerRemote(),
+                           session_info.Clone());
+
+  // The item will receive a MediaSessionInfoChanged.
+  GetItem()->MediaSessionInfoChanged(session_info.Clone());
+
+  // The item should still be frozen, and the view should contain the old data.
+  EXPECT_TRUE(GetItem()->frozen());
+  testing::Mock::VerifyAndClearExpectations(&unfrozen_callback);
+  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
+  EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
+  EXPECT_EQ(base::ASCIIToUTF16("title"), title_label()->GetText());
+  EXPECT_EQ(base::ASCIIToUTF16("source title"), subtitle_label()->GetText());
+
+  // Update the metadata.
+  EXPECT_CALL(unfrozen_callback, Run);
+  media_session::MediaMetadata metadata;
+  metadata.title = base::ASCIIToUTF16("title2");
+  metadata.source_title = base::ASCIIToUTF16("source title 2");
+  GetItem()->MediaSessionMetadataChanged(metadata);
+
+  // The item should no longer be frozen, and we should see the updated data.
+  EXPECT_FALSE(GetItem()->frozen());
+  testing::Mock::VerifyAndClearExpectations(&unfrozen_callback);
+  EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPlay));
+  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPause));
+  EXPECT_EQ(base::ASCIIToUTF16("title2"), title_label()->GetText());
+  EXPECT_EQ(base::ASCIIToUTF16("source title 2"), subtitle_label()->GetText());
+}
+
+TEST_F(MAYBE_MediaNotificationViewModernImplTest,
+       UnfreezingWaitsForArtwork_Timeout) {
+  EnableAction(MediaSessionAction::kPlay);
+  EnableAction(MediaSessionAction::kPause);
+
+  // Set an image before freezing.
+  SkBitmap initial_image;
+  initial_image.allocN32Pixels(10, 10);
+  initial_image.eraseColor(SK_ColorMAGENTA);
+  GetItem()->MediaControllerImageChanged(
+      media_session::mojom::MediaSessionImageType::kArtwork, initial_image);
+  EXPECT_FALSE(GetArtworkImage().isNull());
+
+  // Freeze the item and clear the metadata.
+  base::MockOnceClosure unfrozen_callback;
+  EXPECT_CALL(unfrozen_callback, Run).Times(0);
+  GetItem()->Freeze(unfrozen_callback.Get());
+  GetItem()->MediaSessionInfoChanged(nullptr);
+  GetItem()->MediaSessionMetadataChanged(base::nullopt);
+  GetItem()->MediaControllerImageChanged(
+      media_session::mojom::MediaSessionImageType::kArtwork, SkBitmap());
+
+  // The item should be frozen and the view should contain the old data.
+  EXPECT_TRUE(GetItem()->frozen());
+  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
+  EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
+  EXPECT_EQ(base::ASCIIToUTF16("title"), title_label()->GetText());
+  EXPECT_EQ(base::ASCIIToUTF16("source title"), subtitle_label()->GetText());
+  EXPECT_FALSE(GetArtworkImage().isNull());
+
+  // Bind the item to a new controller that's playing instead of paused.
+  auto new_media_controller = std::make_unique<TestMediaController>();
+  media_session::mojom::MediaSessionInfoPtr session_info(
+      media_session::mojom::MediaSessionInfo::New());
+  session_info->playback_state =
+      media_session::mojom::MediaPlaybackState::kPlaying;
+  session_info->is_controllable = true;
+  GetItem()->SetController(new_media_controller->CreateMediaControllerRemote(),
+                           session_info.Clone());
+
+  // The item will receive a MediaSessionInfoChanged.
+  GetItem()->MediaSessionInfoChanged(session_info.Clone());
+
+  // The item should still be frozen, and the view should contain the old data.
+  EXPECT_TRUE(GetItem()->frozen());
+  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
+  EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
+  EXPECT_EQ(base::ASCIIToUTF16("title"), title_label()->GetText());
+  EXPECT_EQ(base::ASCIIToUTF16("source title"), subtitle_label()->GetText());
+  EXPECT_FALSE(GetArtworkImage().isNull());
+
+  // Update the metadata.
+  media_session::MediaMetadata metadata;
+  metadata.title = base::ASCIIToUTF16("title2");
+  metadata.source_title = base::ASCIIToUTF16("source title 2");
+  GetItem()->MediaSessionMetadataChanged(metadata);
+
+  // The item should still be frozen, and waiting for a new image.
+  EXPECT_TRUE(GetItem()->frozen());
+  testing::Mock::VerifyAndClearExpectations(&unfrozen_callback);
+  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
+  EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
+  EXPECT_EQ(base::ASCIIToUTF16("title"), title_label()->GetText());
+  EXPECT_EQ(base::ASCIIToUTF16("source title"), subtitle_label()->GetText());
+  EXPECT_FALSE(GetArtworkImage().isNull());
+
+  // Once the freeze timer fires, the item should unfreeze even if there's no
+  // artwork.
+  EXPECT_CALL(unfrozen_callback, Run);
+  AdvanceClockMilliseconds(2600);
+
+  EXPECT_FALSE(GetItem()->frozen());
+  testing::Mock::VerifyAndClearExpectations(&unfrozen_callback);
+  EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPlay));
+  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPause));
+  EXPECT_EQ(base::ASCIIToUTF16("title2"), title_label()->GetText());
+  EXPECT_EQ(base::ASCIIToUTF16("source title 2"), subtitle_label()->GetText());
+  EXPECT_TRUE(GetArtworkImage().isNull());
+}
+
+TEST_F(MAYBE_MediaNotificationViewModernImplTest, UnfreezingWaitsForActions) {
+  EnableAction(MediaSessionAction::kPlay);
+  EnableAction(MediaSessionAction::kPause);
+  EnableAction(MediaSessionAction::kNextTrack);
+  EnableAction(MediaSessionAction::kPreviousTrack);
+
+  // Freeze the item and clear the metadata and actions.
+  base::MockOnceClosure unfrozen_callback;
+  EXPECT_CALL(unfrozen_callback, Run).Times(0);
+  GetItem()->Freeze(unfrozen_callback.Get());
+  GetItem()->MediaSessionInfoChanged(nullptr);
+  GetItem()->MediaSessionMetadataChanged(base::nullopt);
+  DisableAction(MediaSessionAction::kPlay);
+  DisableAction(MediaSessionAction::kPause);
+  DisableAction(MediaSessionAction::kNextTrack);
+  DisableAction(MediaSessionAction::kPreviousTrack);
+
+  // The item should be frozen and the view should contain the old data.
+  EXPECT_TRUE(GetItem()->frozen());
+  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
+  EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
+  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kNextTrack));
+  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPreviousTrack));
+  EXPECT_EQ(base::ASCIIToUTF16("title"), title_label()->GetText());
+  EXPECT_EQ(base::ASCIIToUTF16("source title"), subtitle_label()->GetText());
+
+  // Bind the item to a new controller that's playing instead of paused.
+  auto new_media_controller = std::make_unique<TestMediaController>();
+  media_session::mojom::MediaSessionInfoPtr session_info(
+      media_session::mojom::MediaSessionInfo::New());
+  session_info->playback_state =
+      media_session::mojom::MediaPlaybackState::kPlaying;
+  session_info->is_controllable = true;
+  GetItem()->SetController(new_media_controller->CreateMediaControllerRemote(),
+                           session_info.Clone());
+
+  // The item will receive a MediaSessionInfoChanged.
+  GetItem()->MediaSessionInfoChanged(session_info.Clone());
+
+  // The item should still be frozen, and the view should contain the old data.
+  EXPECT_TRUE(GetItem()->frozen());
+  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
+  EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
+  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kNextTrack));
+  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPreviousTrack));
+  EXPECT_EQ(base::ASCIIToUTF16("title"), title_label()->GetText());
+  EXPECT_EQ(base::ASCIIToUTF16("source title"), subtitle_label()->GetText());
+
+  // Update the metadata.
+  media_session::MediaMetadata metadata;
+  metadata.title = base::ASCIIToUTF16("title2");
+  metadata.source_title = base::ASCIIToUTF16("source title 2");
+  GetItem()->MediaSessionMetadataChanged(metadata);
+
+  // The item should still be frozen, and waiting for new actions.
+  EXPECT_TRUE(GetItem()->frozen());
+  testing::Mock::VerifyAndClearExpectations(&unfrozen_callback);
+  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
+  EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
+  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kNextTrack));
+  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPreviousTrack));
+  EXPECT_EQ(base::ASCIIToUTF16("title"), title_label()->GetText());
+  EXPECT_EQ(base::ASCIIToUTF16("source title"), subtitle_label()->GetText());
+
+  // Once we receive actions, the item should unfreeze.
+  EXPECT_CALL(unfrozen_callback, Run);
+  EnableAction(MediaSessionAction::kPlay);
+  EnableAction(MediaSessionAction::kPause);
+  EnableAction(MediaSessionAction::kSeekForward);
+  EnableAction(MediaSessionAction::kSeekBackward);
+
+  EXPECT_FALSE(GetItem()->frozen());
+  testing::Mock::VerifyAndClearExpectations(&unfrozen_callback);
+  EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPlay));
+  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPause));
+  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kSeekForward));
+  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kSeekBackward));
+  EXPECT_EQ(base::ASCIIToUTF16("title2"), title_label()->GetText());
+  EXPECT_EQ(base::ASCIIToUTF16("source title 2"), subtitle_label()->GetText());
+}
+
+TEST_F(MAYBE_MediaNotificationViewModernImplTest,
+       UnfreezingWaitsForArtwork_ReceiveArtwork) {
+  EnableAction(MediaSessionAction::kPlay);
+  EnableAction(MediaSessionAction::kPause);
+
+  // Set an image before freezing.
+  SkBitmap initial_image;
+  initial_image.allocN32Pixels(10, 10);
+  initial_image.eraseColor(SK_ColorMAGENTA);
+  GetItem()->MediaControllerImageChanged(
+      media_session::mojom::MediaSessionImageType::kArtwork, initial_image);
+  EXPECT_FALSE(GetArtworkImage().isNull());
+
+  // Freeze the item and clear the metadata.
+  base::MockOnceClosure unfrozen_callback;
+  EXPECT_CALL(unfrozen_callback, Run).Times(0);
+  GetItem()->Freeze(unfrozen_callback.Get());
+  GetItem()->MediaSessionInfoChanged(nullptr);
+  GetItem()->MediaSessionMetadataChanged(base::nullopt);
+  GetItem()->MediaControllerImageChanged(
+      media_session::mojom::MediaSessionImageType::kArtwork, SkBitmap());
+
+  // The item should be frozen and the view should contain the old data.
+  EXPECT_TRUE(GetItem()->frozen());
+  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
+  EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
+  EXPECT_EQ(base::ASCIIToUTF16("title"), title_label()->GetText());
+  EXPECT_EQ(base::ASCIIToUTF16("source title"), subtitle_label()->GetText());
+  EXPECT_FALSE(GetArtworkImage().isNull());
+
+  // Bind the item to a new controller that's playing instead of paused.
+  auto new_media_controller = std::make_unique<TestMediaController>();
+  media_session::mojom::MediaSessionInfoPtr session_info(
+      media_session::mojom::MediaSessionInfo::New());
+  session_info->playback_state =
+      media_session::mojom::MediaPlaybackState::kPlaying;
+  session_info->is_controllable = true;
+  GetItem()->SetController(new_media_controller->CreateMediaControllerRemote(),
+                           session_info.Clone());
+
+  // The item will receive a MediaSessionInfoChanged.
+  GetItem()->MediaSessionInfoChanged(session_info.Clone());
+
+  // The item should still be frozen, and the view should contain the old data.
+  EXPECT_TRUE(GetItem()->frozen());
+  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
+  EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
+  EXPECT_EQ(base::ASCIIToUTF16("title"), title_label()->GetText());
+  EXPECT_EQ(base::ASCIIToUTF16("source title"), subtitle_label()->GetText());
+  EXPECT_FALSE(GetArtworkImage().isNull());
+
+  // Update the metadata.
+  media_session::MediaMetadata metadata;
+  metadata.title = base::ASCIIToUTF16("title2");
+  metadata.source_title = base::ASCIIToUTF16("source title 2");
+  GetItem()->MediaSessionMetadataChanged(metadata);
+
+  // The item should still be frozen, and waiting for a new image.
+  EXPECT_TRUE(GetItem()->frozen());
+  testing::Mock::VerifyAndClearExpectations(&unfrozen_callback);
+  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
+  EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
+  EXPECT_EQ(base::ASCIIToUTF16("title"), title_label()->GetText());
+  EXPECT_EQ(base::ASCIIToUTF16("source title"), subtitle_label()->GetText());
+  EXPECT_FALSE(GetArtworkImage().isNull());
+
+  // Once we receive artwork, the item should unfreeze.
+  EXPECT_CALL(unfrozen_callback, Run);
+  SkBitmap new_image;
+  new_image.allocN32Pixels(10, 10);
+  new_image.eraseColor(SK_ColorYELLOW);
+  GetItem()->MediaControllerImageChanged(
+      media_session::mojom::MediaSessionImageType::kArtwork, new_image);
+
+  EXPECT_FALSE(GetItem()->frozen());
+  testing::Mock::VerifyAndClearExpectations(&unfrozen_callback);
+  EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPlay));
+  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPause));
+  EXPECT_EQ(base::ASCIIToUTF16("title2"), title_label()->GetText());
+  EXPECT_EQ(base::ASCIIToUTF16("source title 2"), subtitle_label()->GetText());
+  EXPECT_FALSE(GetArtworkImage().isNull());
+}
+
+}  // namespace media_message_center
diff --git a/components/plugins/renderer/plugin_placeholder.cc b/components/plugins/renderer/plugin_placeholder.cc
index 93db78a..a5d3009 100644
--- a/components/plugins/renderer/plugin_placeholder.cc
+++ b/components/plugins/renderer/plugin_placeholder.cc
@@ -5,11 +5,11 @@
 #include "components/plugins/renderer/plugin_placeholder.h"
 
 #include "base/strings/string_util.h"
-#include "content/public/common/web_preferences.h"
 #include "content/public/renderer/render_frame.h"
 #include "content/public/renderer/render_thread.h"
 #include "content/public/renderer/v8_value_converter.h"
 #include "gin/object_template_builder.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "third_party/blink/public/web/blink.h"
 #include "third_party/blink/public/web/web_dom_message_event.h"
 #include "third_party/blink/public/web/web_element.h"
@@ -35,7 +35,7 @@
                                     this,
                                     render_frame
                                         ? render_frame->GetWebkitPreferences()
-                                        : content::WebPreferences(),
+                                        : blink::web_pref::WebPreferences(),
                                     html_data,
                                     GURL(kPluginPlaceholderDataURL))),
       hidden_(false) {}
diff --git a/components/plugins/renderer/webview_plugin.cc b/components/plugins/renderer/webview_plugin.cc
index fee253b2..f4a3b27 100644
--- a/components/plugins/renderer/webview_plugin.cc
+++ b/components/plugins/renderer/webview_plugin.cc
@@ -16,13 +16,13 @@
 #include "base/numerics/safe_conversions.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "content/public/common/web_preferences.h"
 #include "content/public/renderer/render_view.h"
 #include "gin/converter.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "skia/ext/platform_canvas.h"
 #include "third_party/blink/public/common/input/web_coalesced_input_event.h"
 #include "third_party/blink/public/common/page/page_zoom.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "third_party/blink/public/mojom/input/focus_type.mojom.h"
 #include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h"
 #include "third_party/blink/public/platform/web_url.h"
@@ -48,7 +48,7 @@
 using blink::WebURLResponse;
 using blink::WebVector;
 using blink::WebView;
-using content::WebPreferences;
+using blink::web_pref::WebPreferences;
 
 WebViewPlugin::WebViewPlugin(content::RenderView* render_view,
                              WebViewPlugin::Delegate* delegate,
diff --git a/components/plugins/renderer/webview_plugin.h b/components/plugins/renderer/webview_plugin.h
index 19977150..4bd59663 100644
--- a/components/plugins/renderer/webview_plugin.h
+++ b/components/plugins/renderer/webview_plugin.h
@@ -27,13 +27,15 @@
 #include "ui/base/ime/mojom/text_input_state.mojom.h"
 
 namespace blink {
+namespace web_pref {
+struct WebPreferences;
+}  // namespace web_pref
 class WebLocalFrame;
 class WebMouseEvent;
 }
 
 namespace content {
 class RenderView;
-struct WebPreferences;
 }
 
 // This class implements the WebPlugin interface by forwarding drawing and
@@ -70,11 +72,12 @@
   // Convenience method to set up a new WebViewPlugin using |preferences|
   // and displaying |html_data|. |url| should be a (fake) data:text/html URL;
   // it is only used for navigation and never actually resolved.
-  static WebViewPlugin* Create(content::RenderView* render_view,
-                               Delegate* delegate,
-                               const content::WebPreferences& preferences,
-                               const std::string& html_data,
-                               const GURL& url);
+  static WebViewPlugin* Create(
+      content::RenderView* render_view,
+      Delegate* delegate,
+      const blink::web_pref::WebPreferences& preferences,
+      const std::string& html_data,
+      const GURL& url);
 
   blink::WebLocalFrame* main_frame() { return web_view_helper_.main_frame(); }
 
@@ -121,7 +124,7 @@
   friend class base::DeleteHelper<WebViewPlugin>;
   WebViewPlugin(content::RenderView* render_view,
                 Delegate* delegate,
-                const content::WebPreferences& preferences);
+                const blink::web_pref::WebPreferences& preferences);
   ~WebViewPlugin() override;
 
   blink::WebView* web_view() { return web_view_helper_.web_view(); }
@@ -162,7 +165,7 @@
                         public blink::mojom::WidgetHost {
    public:
     WebViewHelper(WebViewPlugin* plugin,
-                  const content::WebPreferences& preferences);
+                  const blink::web_pref::WebPreferences& preferences);
     ~WebViewHelper() override;
 
     blink::WebView* web_view() { return web_view_; }
diff --git a/components/printing/renderer/print_render_frame_helper.cc b/components/printing/renderer/print_render_frame_helper.cc
index a8b7c6d..f2a1f49 100644
--- a/components/printing/renderer/print_render_frame_helper.cc
+++ b/components/printing/renderer/print_render_frame_helper.cc
@@ -29,7 +29,6 @@
 #include "build/build_config.h"
 #include "components/grit/components_resources.h"
 #include "components/printing/common/print_messages.h"
-#include "content/public/common/web_preferences.h"
 #include "content/public/renderer/render_frame.h"
 #include "content/public/renderer/render_thread.h"
 #include "content/public/renderer/render_view.h"
@@ -43,6 +42,7 @@
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
 #include "third_party/blink/public/common/css/page_orientation.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "third_party/blink/public/mojom/frame/frame_owner_element_type.mojom.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_data.h"
@@ -75,7 +75,7 @@
 #include "ui/accessibility/ax_tree_update.h"
 #endif
 
-using content::WebPreferences;
+using blink::web_pref::WebPreferences;
 
 namespace printing {
 
diff --git a/components/safe_browsing/core/verdict_cache_manager.cc b/components/safe_browsing/core/verdict_cache_manager.cc
index 95570f0..5a4c392 100644
--- a/components/safe_browsing/core/verdict_cache_manager.cc
+++ b/components/safe_browsing/core/verdict_cache_manager.cc
@@ -617,6 +617,9 @@
 }
 
 void VerdictCacheManager::CleanUpExpiredRealTimeUrlCheckVerdicts() {
+  if (stored_verdict_count_real_time_url_check_ == 0) {
+    return;
+  }
   ContentSettingsForOneType safe_browsing_url_check_data_settings;
   content_settings_->GetSettingsForOneType(
       ContentSettingsType::SAFE_BROWSING_URL_CHECK_DATA, std::string(),
diff --git a/components/safe_browsing/core/verdict_cache_manager.h b/components/safe_browsing/core/verdict_cache_manager.h
index f1f0ee0..0c2acde 100644
--- a/components/safe_browsing/core/verdict_cache_manager.h
+++ b/components/safe_browsing/core/verdict_cache_manager.h
@@ -149,7 +149,7 @@
   base::Optional<size_t> stored_verdict_count_password_entry_;
 
   // Number of verdict stored for this profile for real time url check pings.
-  // This is only used for testing and logging metrics.
+  // This is used for testing, logging metrics and cleaning up during shutdown.
   int stored_verdict_count_real_time_url_check_ = 0;
 
   ScopedObserver<history::HistoryService, history::HistoryServiceObserver>
diff --git a/components/safe_browsing/core/verdict_cache_manager_unittest.cc b/components/safe_browsing/core/verdict_cache_manager_unittest.cc
index 598bc97a..495e6656 100644
--- a/components/safe_browsing/core/verdict_cache_manager_unittest.cc
+++ b/components/safe_browsing/core/verdict_cache_manager_unittest.cc
@@ -326,6 +326,9 @@
   ASSERT_EQ(2u, cache_manager_->GetStoredPhishGuardVerdictCount(
                     LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
 
+  // Prepare 2 verdicts for SAFE_BROWSING_URL_CHECK_DATA:
+  // (1) "www.example.com/" expired
+  // (2) "www.example.com/path" valid
   RTLookupResponse response;
   AddThreatInfoToResponse(response, RTLookupResponse::ThreatInfo::DANGEROUS,
                           RTLookupResponse::ThreatInfo::SOCIAL_ENGINEERING, 0,
@@ -388,6 +391,18 @@
                 GURL("https://bar.com/xyz/index.jsp"),
                 LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
                 password_type, &actual_verdict));
+
+  RTLookupResponse::ThreatInfo actual_real_time_threat_info;
+  // No cached SAFE_BROWSING_URL_CHECK_DATA verdict for www.example.com/.
+  EXPECT_EQ(
+      RTLookupResponse::ThreatInfo::VERDICT_TYPE_UNSPECIFIED,
+      cache_manager_->GetCachedRealTimeUrlVerdict(
+          GURL("https://www.example.com/"), &actual_real_time_threat_info));
+  // Has cached SAFE_BROWSING_URL_CHECK_DATA verdict for www.example.com/path.
+  EXPECT_EQ(
+      RTLookupResponse::ThreatInfo::DANGEROUS,
+      cache_manager_->GetCachedRealTimeUrlVerdict(
+          GURL("https://www.example.com/path"), &actual_real_time_threat_info));
 }
 
 TEST_F(VerdictCacheManagerTest, TestCleanUpExpiredVerdictWithInvalidEntry) {
diff --git a/components/services/app_service/public/cpp/intent_util.cc b/components/services/app_service/public/cpp/intent_util.cc
index 01a1dd7..2be1e951 100644
--- a/components/services/app_service/public/cpp/intent_util.cc
+++ b/components/services/app_service/public/cpp/intent_util.cc
@@ -153,6 +153,20 @@
   return intent;
 }
 
+apps::mojom::IntentPtr CreateShareIntentFromDriveFile(
+    const GURL& filesystem_url,
+    const std::string& mime_type,
+    const GURL& drive_share_url) {
+  auto intent = apps::mojom::Intent::New();
+  intent->action = kIntentActionSend;
+  intent->mime_type = mime_type;
+  intent->file_urls = std::vector<GURL>{filesystem_url};
+  if (!drive_share_url.is_empty()) {
+    intent->drive_share_url = drive_share_url;
+  }
+  return intent;
+}
+
 bool ConditionValueMatches(
     const std::string& value,
     const apps::mojom::ConditionValuePtr& condition_value) {
diff --git a/components/services/app_service/public/cpp/intent_util.h b/components/services/app_service/public/cpp/intent_util.h
index d003fd2..753ba06 100644
--- a/components/services/app_service/public/cpp/intent_util.h
+++ b/components/services/app_service/public/cpp/intent_util.h
@@ -28,6 +28,13 @@
     const std::vector<GURL>& filesystem_urls,
     const std::vector<std::string>& mime_types);
 
+// Create an intent struct from the filesystem url, mime type
+// and the drive share url for a Google Drive file.
+apps::mojom::IntentPtr CreateShareIntentFromDriveFile(
+    const GURL& filesystem_url,
+    const std::string& mime_type,
+    const GURL& drive_share_url);
+
 // Return true if |value| matches with the |condition_value|, based on the
 // pattern match type in the |condition_value|.
 bool ConditionValueMatches(
diff --git a/components/services/app_service/public/mojom/types.mojom b/components/services/app_service/public/mojom/types.mojom
index 2dae49b..d8381d0 100644
--- a/components/services/app_service/public/mojom/types.mojom
+++ b/components/services/app_service/public/mojom/types.mojom
@@ -325,6 +325,10 @@
   string? mime_type; // MIME type. e.g. text/plain, image/*.
   array<url.mojom.Url>? file_urls; // The URLs of the files to share.
   string? activity_name; // The activity for the app to launch.
+
+  // The Drive share URL, this is only filled if the intent contains a file
+  // from Google Drive.
+  url.mojom.Url? drive_share_url;
 };
 
 // Represents a group of |app_ids| that is no longer preferred app of their
diff --git a/components/services/storage/public/mojom/service_worker_storage_control.mojom b/components/services/storage/public/mojom/service_worker_storage_control.mojom
index 342e911c..e37f953 100644
--- a/components/services/storage/public/mojom/service_worker_storage_control.mojom
+++ b/components/services/storage/public/mojom/service_worker_storage_control.mojom
@@ -123,7 +123,7 @@
        ServiceWorkerFindRegistrationResult? result);
   // Reads a stored registration for |registration_id|. |origin| is to
   // be used as a hint to look up the registration faster.
-  FindRegistrationForId(int64 registration_id, url.mojom.Url? origin) =>
+  FindRegistrationForId(int64 registration_id, url.mojom.Origin? origin) =>
       (ServiceWorkerDatabaseStatus status,
        ServiceWorkerFindRegistrationResult? result);
 
@@ -220,7 +220,7 @@
       (ServiceWorkerDatabaseStatus status, array<string> values);
   // Stores |user_data| on persistent storage.
   StoreUserData(int64 registration_id,
-                url.mojom.Url origin,
+                url.mojom.Origin origin,
                 array<ServiceWorkerUserData> user_data) =>
       (ServiceWorkerDatabaseStatus status);
   // Clears user data specified by |registration_id| and |keys|.
diff --git a/components/translate/content/browser/content_translate_driver.cc b/components/translate/content/browser/content_translate_driver.cc
index 156cd948..2e8622b 100644
--- a/components/translate/content/browser/content_translate_driver.cc
+++ b/components/translate/content/browser/content_translate_driver.cc
@@ -34,7 +34,6 @@
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/referrer.h"
-#include "content/public/common/web_preferences.h"
 #include "net/http/http_status_code.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
 #include "url/gurl.h"
diff --git a/components/translate/content/browser/per_frame_content_translate_driver.cc b/components/translate/content/browser/per_frame_content_translate_driver.cc
index 4ee98b36..3fc4b85 100644
--- a/components/translate/content/browser/per_frame_content_translate_driver.cc
+++ b/components/translate/content/browser/per_frame_content_translate_driver.cc
@@ -37,10 +37,10 @@
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/referrer.h"
-#include "content/public/common/web_preferences.h"
 #include "net/http/http_status_code.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "ui/accessibility/ax_node.h"
 #include "ui/accessibility/ax_tree.h"
 #include "url/gurl.h"
diff --git a/components/viz/service/display/surface_aggregator.cc b/components/viz/service/display/surface_aggregator.cc
index 84e2cd2b..dc436ce 100644
--- a/components/viz/service/display/surface_aggregator.cc
+++ b/components/viz/service/display/surface_aggregator.cc
@@ -283,12 +283,12 @@
 // before this.
 void SurfaceAggregator::UnionSurfaceDamageRectsOnTop(
     const gfx::Rect& surface_rect,
-    const gfx::Transform& quad_to_root_target_transform) {
+    const gfx::Transform& parent_quad_to_root_target_transform) {
   DCHECK(!surface_rect.IsEmpty());
 
   gfx::Rect damage_rect_in_root_target_space =
-      cc::MathUtil::MapEnclosingClippedRect(quad_to_root_target_transform,
-                                            surface_rect);
+      cc::MathUtil::MapEnclosingClippedRect(
+          parent_quad_to_root_target_transform, surface_rect);
   damage_rects_union_of_surfaces_on_top_.Union(
       damage_rect_in_root_target_space);
 }
@@ -303,61 +303,34 @@
 // surface to which the optimization can be applied, this function returns
 // that quad and sets the occluding_damage_rect out parameter to the
 // appropriate rectangle.
-DrawQuad* SurfaceAggregator::ProcessSurfaceOccludingDamage(
-    const Surface* surface,
-    const CompositorRenderPassList& render_pass_list,
-    const gfx::Transform& parent_target_transform,
-    const CompositorRenderPass* render_pass,
-    gfx::Rect* occluding_damage_rect) {
-  auto aggregated_id =
-      pass_id_remapper_.Remap(render_pass->id, surface->surface_id());
-  return ProcessSurfaceOccludingDamageInternal(
-      surface, render_pass_list, parent_target_transform,
-      render_pass->transform_to_root_target, aggregated_id,
-      render_pass->cache_render_pass, occluding_damage_rect);
-}
 
-DrawQuad* SurfaceAggregator::ProcessSurfaceOccludingDamage(
-    const Surface* surface,
-    const CompositorRenderPassList& render_pass_list,
+const DrawQuad* SurfaceAggregator::ProcessSurfaceOccludingDamage(
+    const CompositorRenderPass& source_pass,
+    AggregatedRenderPass* dest_pass,
     const gfx::Transform& parent_target_transform,
-    const AggregatedRenderPass* render_pass,
+    Surface* surface,
+    bool is_last_pass_in_src_surface,
     gfx::Rect* occluding_damage_rect) {
-  return ProcessSurfaceOccludingDamageInternal(
-      surface, render_pass_list, parent_target_transform,
-      render_pass->transform_to_root_target, render_pass->id,
-      render_pass->cache_render_pass, occluding_damage_rect);
-}
-
-DrawQuad* SurfaceAggregator::ProcessSurfaceOccludingDamageInternal(
-    const Surface* surface,
-    const CompositorRenderPassList& render_pass_list,
-    const gfx::Transform& parent_target_transform,
-    const gfx::Transform& dest_transform_to_root_target,
-    const AggregatedRenderPassId& dest_pass_id,
-    bool dest_pass_cached,
-    gfx::Rect* occluding_damage_rect) {
-  if (!needs_surface_occluding_damage_rect_)
+  if (!needs_surface_occluding_damage_rect_ || !is_last_pass_in_src_surface)
     return nullptr;
 
-  CompositorRenderPass* last_render_pass = render_pass_list.back().get();
-  gfx::Transform quad_to_root_target_transform =
-      gfx::Transform(dest_transform_to_root_target, parent_target_transform);
+  gfx::Transform parent_quad_to_root_target_transform = gfx::Transform(
+      dest_pass->transform_to_root_target, parent_target_transform);
 
   // The occluding damage optimization currently relies on two things - there
   // can't be any damage above the quad within the surface, and the quad needs
   // its own SQS for the occluding_damage_rect metadata.
-  DrawQuad* target_quad = nullptr;
-  if (last_render_pass->quad_list.size() == 1) {
+  const DrawQuad* target_quad = nullptr;
+  if (source_pass.quad_list.size() == 1) {
     // If there's only one quad in the root render pass, then the conditions
     // are clearly satisfied.
-    target_quad = last_render_pass->quad_list.back();
+    target_quad = source_pass.quad_list.back();
   } else {
     // If there are multiple quads in the surface, if exactly one quad is
     // marked as having damage, then we know that quad doesn't have damage
     // above it, and we know that it has its own SQS (because its
     // sqs->no_damage is unique).
-    for (auto* quad : last_render_pass->quad_list) {
+    for (auto* quad : source_pass.quad_list) {
       if (quad->shared_quad_state->no_damage) {
         continue;
       }
@@ -373,15 +346,15 @@
 
   if (target_quad) {
     *occluding_damage_rect = CalculateOccludingSurfaceDamageRect(
-        target_quad, quad_to_root_target_transform);
+        target_quad, parent_quad_to_root_target_transform);
   }
 
   gfx::Rect surface_damage_rect;
-  if (RenderPassNeedsFullDamage(dest_pass_id, dest_pass_cached)) {
-    surface_damage_rect = last_render_pass->output_rect;
+  if (RenderPassNeedsFullDamage(dest_pass->id, dest_pass->cache_render_pass)) {
+    surface_damage_rect = source_pass.output_rect;
   } else {
-    surface_damage_rect = DamageRectForSurface(surface, *last_render_pass,
-                                               last_render_pass->output_rect);
+    surface_damage_rect =
+        DamageRectForSurface(surface, source_pass, source_pass.output_rect);
   }
 
   // Add the current surface to the damage rect union if there is any damage.
@@ -389,7 +362,7 @@
   // on top should not include its own surface.
   if (!surface_damage_rect.IsEmpty()) {
     UnionSurfaceDamageRectsOnTop(surface_damage_rect,
-                                 quad_to_root_target_transform);
+                                 parent_quad_to_root_target_transform);
   }
   return target_quad;
 }
@@ -579,11 +552,6 @@
         frame.metadata.delegated_ink_metadata.get());
   }
 
-  gfx::Rect occluding_damage_rect;
-  DrawQuad* quad_with_occluding_damage_rect = ProcessSurfaceOccludingDamage(
-      surface, render_pass_list, combined_transform, dest_pass,
-      &occluding_damage_rect);
-
   const CompositorRenderPassList& referenced_passes = render_pass_list;
   // TODO(fsamuel): Move this to a separate helper function.
   size_t passes_to_copy =
@@ -623,11 +591,10 @@
     copy_pass->transform_to_root_target.ConcatTransform(
         dest_pass->transform_to_root_target);
 
-    CopyQuadsToPass(source.quad_list, source.shared_quad_state_list,
-                    surface->GetActiveFrame().device_scale_factor(),
-                    child_to_parent_map, gfx::Transform(), {}, copy_pass.get(),
-                    surface_id, RoundedCornerInfo(), occluding_damage_rect,
-                    quad_with_occluding_damage_rect);
+    CopyQuadsToPass(source, copy_pass.get(), frame.device_scale_factor(),
+                    child_to_parent_map, gfx::Transform(), {}, surface_id,
+                    RoundedCornerInfo(), surface,
+                    /*is_last_pass*/ j == (render_pass_list.size() - 1));
 
     // If the render pass has copy requests, or should be cached, or has
     // moving-pixel filters, or in a moving-pixel surface, we should damage the
@@ -659,9 +626,6 @@
            .IsEmpty();
 
   if (merge_pass) {
-    // TODO(jamesr): Clean up last pass special casing.
-    const QuadList& quads = last_pass.quad_list;
-
     // Intersect the transformed visible rect and the clip rect to create a
     // smaller cliprect for the quad.
     ClipData surface_quad_clip_rect = {
@@ -674,11 +638,10 @@
     ClipData quads_clip =
         CalculateClipRect(clip_rect, surface_quad_clip_rect, target_transform);
 
-    CopyQuadsToPass(quads, last_pass.shared_quad_state_list,
-                    surface->GetActiveFrame().device_scale_factor(),
+    CopyQuadsToPass(last_pass, dest_pass, frame.device_scale_factor(),
                     child_to_parent_map, combined_transform, quads_clip,
-                    dest_pass, surface_id, rounded_corner_info,
-                    occluding_damage_rect, quad_with_occluding_damage_rect);
+                    surface_id, rounded_corner_info, surface,
+                    /*is_last_pass*/ true);
   } else {
     auto* shared_quad_state = CopyAndScaleSharedQuadState(
         source_sqs, scaled_quad_to_target_transform, target_transform,
@@ -688,8 +651,8 @@
         gfx::ScaleToEnclosingRect(source_sqs->visible_quad_layer_rect,
                                   inverse_extra_content_scale_x,
                                   inverse_extra_content_scale_y),
-        clip_rect, dest_pass, rounded_corner_info, occluding_damage_rect,
-        /* occluding_damage_rect_valid */ false);
+        clip_rect, dest_pass, rounded_corner_info, /*occluding_damage_rect*/
+        gfx::Rect(), /* occluding_damage_rect_valid */ false);
 
     // At this point, we need to calculate three values in order to construct
     // the CompositorRenderPassDrawQuad:
@@ -1005,18 +968,19 @@
 }
 
 void SurfaceAggregator::CopyQuadsToPass(
-    const QuadList& source_quad_list,
-    const SharedQuadStateList& source_shared_quad_state_list,
+    const CompositorRenderPass& source_pass,
+    AggregatedRenderPass* dest_pass,
     float parent_device_scale_factor,
     const std::unordered_map<ResourceId, ResourceId>& child_to_parent_map,
     const gfx::Transform& target_transform,
     const ClipData& clip_rect,
-    AggregatedRenderPass* dest_pass,
     const SurfaceId& surface_id,
     const RoundedCornerInfo& parent_rounded_corner_info,
-    const gfx::Rect& occluding_damage_rect,
-    DrawQuad* quad_with_occluding_damage_rect) {
+    Surface* surface,
+    bool is_last_pass) {
+  const QuadList& source_quad_list = source_pass.quad_list;
   const SharedQuadState* last_copied_source_shared_quad_state = nullptr;
+
   // If the current frame has copy requests or cached render passes, then
   // aggregate the entire thing, as otherwise parts of the copy requests may be
   // ignored and we could cache partially drawn render pass.
@@ -1034,6 +998,8 @@
   bool damage_rect_in_quad_space_valid = false;
 
 #if DCHECK_IS_ON()
+  const SharedQuadStateList& source_shared_quad_state_list =
+      source_pass.shared_quad_state_list;
   // If quads have come in with SharedQuadState out of order, or when quads have
   // invalid SharedQuadState pointer, it should DCHECK.
   auto sqs_iter = source_shared_quad_state_list.cbegin();
@@ -1046,6 +1012,12 @@
   }
 #endif
 
+  gfx::Rect occluding_damage_rect;
+  const DrawQuad* quad_with_occluding_damage_rect =
+      ProcessSurfaceOccludingDamage(source_pass, dest_pass, target_transform,
+                                    surface, is_last_pass,
+                                    &occluding_damage_rect);
+
   RoundedCornerInfo new_rounded_corner_info = parent_rounded_corner_info;
   for (auto* quad : source_quad_list) {
     // Both cannot be set at once. If this happens then a surface is being
@@ -1184,11 +1156,6 @@
         frame.metadata.delegated_ink_metadata.get());
   }
 
-  gfx::Rect occluding_damage_rect;
-  DrawQuad* quad_with_occluding_damage_rect = ProcessSurfaceOccludingDamage(
-      surface, source_pass_list, surface_transform,
-      source_pass_list.back().get(), &occluding_damage_rect);
-
   bool apply_surface_transform_to_root_pass = true;
   for (size_t i = 0; i < source_pass_list.size(); ++i) {
     const auto& source = *source_pass_list[i];
@@ -1232,13 +1199,12 @@
         source.cache_render_pass, source.has_damage_from_contributing_content,
         source.generate_mipmap);
 
-    CopyQuadsToPass(source.quad_list, source.shared_quad_state_list,
-                    frame.device_scale_factor(), child_to_parent_map,
+    CopyQuadsToPass(source, copy_pass.get(), frame.device_scale_factor(),
+                    child_to_parent_map,
                     apply_surface_transform_to_root_pass ? surface_transform
                                                          : gfx::Transform(),
-                    {}, copy_pass.get(), surface->surface_id(),
-                    RoundedCornerInfo(), occluding_damage_rect,
-                    quad_with_occluding_damage_rect);
+                    {}, surface->surface_id(), RoundedCornerInfo(), surface,
+                    is_root_pass);
 
     // If the render pass has copy requests, or should be cached, or has
     // moving-pixel filters, or in a moving-pixel surface, we should damage the
diff --git a/components/viz/service/display/surface_aggregator.h b/components/viz/service/display/surface_aggregator.h
index 57af4095..ea6e8f7 100644
--- a/components/viz/service/display/surface_aggregator.h
+++ b/components/viz/service/display/surface_aggregator.h
@@ -171,17 +171,16 @@
       bool occluding_damage_rect_valid);
 
   void CopyQuadsToPass(
-      const QuadList& source_quad_list,
-      const SharedQuadStateList& source_shared_quad_state_list,
+      const CompositorRenderPass& source_pass,
+      AggregatedRenderPass* dest_pass,
       float parent_device_scale_factor,
       const std::unordered_map<ResourceId, ResourceId>& resource_to_child_map,
       const gfx::Transform& target_transform,
       const ClipData& clip_rect,
-      AggregatedRenderPass* dest_pass,
       const SurfaceId& surface_id,
       const RoundedCornerInfo& rounded_corner_info,
-      const gfx::Rect& occluding_damage_rect,
-      DrawQuad* quad_with_occluding_damage_rect);
+      Surface* surface,
+      bool is_last_pass);
 
   // Recursively walks through the render pass and updates the
   // |can_use_backdrop_filter_cache| flag on all RenderPassDrawQuads(RPDQ).
@@ -261,23 +260,12 @@
   void UnionSurfaceDamageRectsOnTop(const gfx::Rect& surface_rect,
                                     const gfx::Transform& target_transform);
 
-  // Determines occluding damage. Note that there is two version of the
-  // function, differing on what type of RenderPass is given (aggregated or from
-  // the compositor). If the compositor pass is given, it is assumed that it
-  // will be transformed into an aggregated pass later, so the values (with the
-  // exception of the id) will be the same. The id is remapped to an aggregated
-  // id.
-  DrawQuad* ProcessSurfaceOccludingDamage(
-      const Surface* surface,
-      const CompositorRenderPassList& render_pass_list,
-      const gfx::Transform& target_transform,
-      const CompositorRenderPass* dest_pass,
-      gfx::Rect* occluding_damage_rect);
-  DrawQuad* ProcessSurfaceOccludingDamage(
-      const Surface* surface,
-      const CompositorRenderPassList& render_pass_list,
-      const gfx::Transform& target_transform,
-      const AggregatedRenderPass* dest_pass,
+  const DrawQuad* ProcessSurfaceOccludingDamage(
+      const CompositorRenderPass& source_pass,
+      AggregatedRenderPass* dest_pass,
+      const gfx::Transform& parent_target_transform,
+      Surface* surface,
+      bool is_last_pass_in_src_surface,
       gfx::Rect* occluding_damage_rect);
 
   // Returns true if the render pass with the given id and cache_render_pass
@@ -339,16 +327,6 @@
   // Update |last_frame_had_jelly_|, should be called once per frame.
   void SetLastFrameHadJelly(bool had_jelly);
 
-  // An internal helper for the `ProcessSurfaceOccludingDamage()` functions.
-  DrawQuad* ProcessSurfaceOccludingDamageInternal(
-      const Surface* surface,
-      const CompositorRenderPassList& render_pass_list,
-      const gfx::Transform& parent_target_transform,
-      const gfx::Transform& dest_transform_to_root_target,
-      const AggregatedRenderPassId& dest_pass_id,
-      bool dest_pass_cached,
-      gfx::Rect* occluding_damage_rect);
-
   SurfaceManager* manager_;
   DisplayResourceProvider* provider_;
 
diff --git a/content/browser/accessibility/captioning_controller.cc b/content/browser/accessibility/captioning_controller.cc
index f9a5f7e..19fe0176 100644
--- a/content/browser/accessibility/captioning_controller.cc
+++ b/content/browser/accessibility/captioning_controller.cc
@@ -8,7 +8,7 @@
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/common/frame_messages.h"
 #include "content/public/android/content_jni_headers/CaptioningController_jni.h"
-#include "content/public/common/web_preferences.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 
 using base::android::AttachCurrentThread;
 using base::android::ConvertJavaStringToUTF8;
diff --git a/content/browser/back_forward_cache_browsertest.cc b/content/browser/back_forward_cache_browsertest.cc
index 4655cd6..82e8bfc 100644
--- a/content/browser/back_forward_cache_browsertest.cc
+++ b/content/browser/back_forward_cache_browsertest.cc
@@ -4713,7 +4713,8 @@
   EXPECT_TRUE(rfh_a->IsInBackForwardCache());
   EXPECT_NE(rfh_a, rfh_b);
 
-  WebPreferences prefs = web_contents()->GetOrCreateWebPreferences();
+  blink::web_pref::WebPreferences prefs =
+      web_contents()->GetOrCreateWebPreferences();
   prefs.preferred_color_scheme = blink::PreferredColorScheme::kDark;
   web_contents()->SetWebPreferences(prefs);
 
diff --git a/content/browser/background_fetch/background_fetch_data_manager_unittest.cc b/content/browser/background_fetch/background_fetch_data_manager_unittest.cc
index d868509fd..7ad9f56 100644
--- a/content/browser/background_fetch/background_fetch_data_manager_unittest.cc
+++ b/content/browser/background_fetch/background_fetch_data_manager_unittest.cc
@@ -453,7 +453,7 @@
 
     base::RunLoop run_loop;
     embedded_worker_test_helper()->context_wrapper()->StoreRegistrationUserData(
-        service_worker_registration_id, origin().GetURL(), {{key, value}},
+        service_worker_registration_id, origin(), {{key, value}},
         base::BindOnce(&DidStoreUserData, run_loop.QuitClosure()));
     run_loop.Run();
   }
diff --git a/content/browser/background_fetch/background_fetch_event_dispatcher.cc b/content/browser/background_fetch/background_fetch_event_dispatcher.cc
index ebbf1d6..12f24dd 100644
--- a/content/browser/background_fetch/background_fetch_event_dispatcher.cc
+++ b/content/browser/background_fetch/background_fetch_event_dispatcher.cc
@@ -273,7 +273,7 @@
     ServiceWorkerLoadedCallback loaded_callback) {
   service_worker_context_->FindReadyRegistrationForId(
       registration_id.service_worker_registration_id(),
-      registration_id.origin().GetURL(),
+      registration_id.origin(),
       base::BindOnce(
           &BackgroundFetchEventDispatcher::StartActiveWorkerForDispatch, event,
           std::move(finished_closure), std::move(loaded_callback)));
diff --git a/content/browser/background_fetch/background_fetch_service_impl.cc b/content/browser/background_fetch/background_fetch_service_impl.cc
index fac7450..714b65e5 100644
--- a/content/browser/background_fetch/background_fetch_service_impl.cc
+++ b/content/browser/background_fetch/background_fetch_service_impl.cc
@@ -51,7 +51,7 @@
           WrapRefCounted(static_cast<StoragePartitionImpl*>(
                              render_process_host->GetStoragePartition())
                              ->GetBackgroundFetchContext()),
-          info.script_origin,
+          info.origin,
           /* render_frame_tree_node_id= */ 0,
           /* wc_getter= */ base::NullCallback(), std::move(receiver)));
 }
diff --git a/content/browser/background_fetch/background_fetch_test_base.cc b/content/browser/background_fetch/background_fetch_test_base.cc
index ab80bc5..97f1be5 100644
--- a/content/browser/background_fetch/background_fetch_test_base.cc
+++ b/content/browser/background_fetch/background_fetch_test_base.cc
@@ -131,7 +131,7 @@
   {
     base::RunLoop run_loop;
     embedded_worker_test_helper_.context()->registry()->FindRegistrationForId(
-        service_worker_registration_id, origin.GetURL(),
+        service_worker_registration_id, origin,
         base::BindOnce(&DidFindServiceWorkerRegistration,
                        &service_worker_registration, run_loop.QuitClosure()));
 
diff --git a/content/browser/background_fetch/storage/create_metadata_task.cc b/content/browser/background_fetch/storage/create_metadata_task.cc
index 56a9325e..6e8fac48 100644
--- a/content/browser/background_fetch/storage/create_metadata_task.cc
+++ b/content/browser/background_fetch/storage/create_metadata_task.cc
@@ -344,7 +344,7 @@
 
   service_worker_context()->StoreRegistrationUserData(
       registration_id_.service_worker_registration_id(),
-      registration_id_.origin().GetURL(), entries,
+      registration_id_.origin(), entries,
       base::BindOnce(&CreateMetadataTask::DidStoreMetadata,
                      weak_factory_.GetWeakPtr()));
 }
diff --git a/content/browser/background_fetch/storage/mark_request_complete_task.cc b/content/browser/background_fetch/storage/mark_request_complete_task.cc
index 51a4c236..4f97e78 100644
--- a/content/browser/background_fetch/storage/mark_request_complete_task.cc
+++ b/content/browser/background_fetch/storage/mark_request_complete_task.cc
@@ -222,7 +222,7 @@
 
   service_worker_context()->StoreRegistrationUserData(
       registration_id_.service_worker_registration_id(),
-      registration_id_.origin().GetURL(),
+      registration_id_.origin(),
       {{CompletedRequestKey(completed_request_.unique_id(),
                             completed_request_.request_index()),
         completed_request_.SerializeAsString()}},
@@ -299,7 +299,7 @@
 
   service_worker_context()->StoreRegistrationUserData(
       registration_id_.service_worker_registration_id(),
-      registration_id_.origin().GetURL(),
+      registration_id_.origin(),
       {{RegistrationKey(registration_id_.unique_id()),
         metadata->SerializeAsString()}},
       base::BindOnce(&MarkRequestCompleteTask::DidStoreMetadata,
diff --git a/content/browser/background_fetch/storage/start_next_pending_request_task.cc b/content/browser/background_fetch/storage/start_next_pending_request_task.cc
index 8d4ea453..d9146c8 100644
--- a/content/browser/background_fetch/storage/start_next_pending_request_task.cc
+++ b/content/browser/background_fetch/storage/start_next_pending_request_task.cc
@@ -75,7 +75,7 @@
 
   service_worker_context()->StoreRegistrationUserData(
       registration_id_.service_worker_registration_id(),
-      registration_id_.origin().GetURL(),
+      registration_id_.origin(),
       {{ActiveRequestKey(active_request_.unique_id(),
                          active_request_.request_index()),
         active_request_.SerializeAsString()}},
diff --git a/content/browser/background_sync/background_sync_manager.cc b/content/browser/background_sync/background_sync_manager.cc
index 7a7bf9f..745affc0 100644
--- a/content/browser/background_sync/background_sync_manager.cc
+++ b/content/browser/background_sync/background_sync_manager.cc
@@ -1419,8 +1419,7 @@
   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
 
   service_worker_context_->StoreRegistrationUserData(
-      sw_registration_id, origin.GetURL(), {{backend_key, data}},
-      std::move(callback));
+      sw_registration_id, origin, {{backend_key, data}}, std::move(callback));
 }
 
 void BackgroundSyncManager::GetDataFromBackend(
@@ -1467,7 +1466,7 @@
   if (devtools_context_->IsRecording(
           DevToolsBackgroundService::kBackgroundSync)) {
     devtools_context_->LogBackgroundServiceEventOnCoreThread(
-        active_version->registration_id(), active_version->script_origin(),
+        active_version->registration_id(), active_version->origin(),
         DevToolsBackgroundService::kBackgroundSync,
         /* event_name= */ "Dispatched sync event",
         /* instance_id= */ tag,
@@ -1510,7 +1509,7 @@
   if (devtools_context_->IsRecording(
           DevToolsBackgroundService::kPeriodicBackgroundSync)) {
     devtools_context_->LogBackgroundServiceEventOnCoreThread(
-        active_version->registration_id(), active_version->script_origin(),
+        active_version->registration_id(), active_version->origin(),
         DevToolsBackgroundService::kPeriodicBackgroundSync,
         /* event_name= */ "Dispatched periodicsync event",
         /* instance_id= */ tag,
@@ -2087,7 +2086,7 @@
         registration_info->service_worker_registration_id;
     service_worker_context_->FindReadyRegistrationForId(
         service_worker_registration_id,
-        active_registrations_[service_worker_registration_id].origin.GetURL(),
+        active_registrations_[service_worker_registration_id].origin,
         base::BindOnce(
             &BackgroundSyncManager::FireReadyEventsDidFindRegistration,
             weak_ptr_factory_.GetWeakPtr(), std::move(registration_info),
diff --git a/content/browser/background_sync/background_sync_manager_unittest.cc b/content/browser/background_sync/background_sync_manager_unittest.cc
index df81eeb..4cbefce3 100644
--- a/content/browser/background_sync/background_sync_manager_unittest.cc
+++ b/content/browser/background_sync/background_sync_manager_unittest.cc
@@ -183,12 +183,12 @@
     // Hang onto the registrations as they need to be "live" when
     // calling BackgroundSyncManager::Register.
     helper_->context_wrapper()->FindReadyRegistrationForId(
-        sw_registration_id_1_, GURL(kScope1).GetOrigin(),
+        sw_registration_id_1_, url::Origin::Create(GURL(kScope1)),
         base::BindOnce(FindServiceWorkerRegistrationCallback,
                        &sw_registration_1_));
 
     helper_->context_wrapper()->FindReadyRegistrationForId(
-        sw_registration_id_2_, GURL(kScope1).GetOrigin(),
+        sw_registration_id_2_, url::Origin::Create(GURL(kScope1)),
         base::BindOnce(FindServiceWorkerRegistrationCallback,
                        &sw_registration_2_));
     base::RunLoop().RunUntilIdle();
diff --git a/content/browser/background_sync/background_sync_service_impl_test_harness.cc b/content/browser/background_sync/background_sync_service_impl_test_harness.cc
index 2889d7cd..3564de7 100644
--- a/content/browser/background_sync/background_sync_service_impl_test_harness.cc
+++ b/content/browser/background_sync/background_sync_service_impl_test_harness.cc
@@ -184,7 +184,7 @@
   ASSERT_TRUE(called);
 
   embedded_worker_helper_->context_wrapper()->FindReadyRegistrationForId(
-      sw_registration_id_, GURL(kServiceWorkerScope).GetOrigin(),
+      sw_registration_id_, url::Origin::Create(GURL(kServiceWorkerScope)),
       base::BindOnce(FindServiceWorkerRegistrationCallback, &sw_registration_));
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(sw_registration_);
diff --git a/content/browser/browser_interface_binders.cc b/content/browser/browser_interface_binders.cc
index 3f2a75b..573d9f8 100644
--- a/content/browser/browser_interface_binders.cc
+++ b/content/browser/browser_interface_binders.cc
@@ -438,7 +438,7 @@
              const url::Origin&, mojo::PendingReceiver<Interface>),
          const ServiceWorkerVersionInfo& info,
          mojo::PendingReceiver<Interface> receiver) {
-        auto origin = info.script_origin;
+        auto origin = info.origin;
         RunOrPostTaskToBindServiceWorkerReceiver<
             const url::Origin&, mojo::PendingReceiver<Interface>>(
             host, method, origin, std::move(receiver));
@@ -460,7 +460,7 @@
              int, const url::Origin&, mojo::PendingReceiver<Interface>),
          const ServiceWorkerVersionInfo& info,
          mojo::PendingReceiver<Interface> receiver) {
-        auto origin = info.script_origin;
+        auto origin = info.origin;
         RunOrPostTaskToBindServiceWorkerReceiver<
             int, const url::Origin&, mojo::PendingReceiver<Interface>>(
             host, method, MSG_ROUTING_NONE, origin, std::move(receiver));
diff --git a/content/browser/browser_plugin/browser_plugin_guest.cc b/content/browser/browser_plugin/browser_plugin_guest.cc
index e07f79be..b9e803a 100644
--- a/content/browser/browser_plugin/browser_plugin_guest.cc
+++ b/content/browser/browser_plugin/browser_plugin_guest.cc
@@ -137,7 +137,8 @@
 
   // TODO(chrishtr): this code is wrong. The navigate_on_drag_drop field will
   // be reset again the next time preferences are updated.
-  WebPreferences prefs = GetWebContents()->GetOrCreateWebPreferences();
+  blink::web_pref::WebPreferences prefs =
+      GetWebContents()->GetOrCreateWebPreferences();
   prefs.navigate_on_drag_drop = false;
   GetWebContents()->SetWebPreferences(prefs);
 }
diff --git a/content/browser/content_index/content_index_database.cc b/content/browser/content_index/content_index_database.cc
index 27a2f4f..2ce59c4 100644
--- a/content/browser/content_index/content_index_database.cc
+++ b/content/browser/content_index/content_index_database.cc
@@ -225,7 +225,7 @@
                           std::move(description), launch_url, entry_time);
 
   service_worker_context_->StoreRegistrationUserData(
-      service_worker_registration_id, origin.GetURL(),
+      service_worker_registration_id, origin,
       {{std::move(entry_key), std::move(entry_value)},
        {std::move(icon_key), std::move(icons_value)}},
       base::BindOnce(&ContentIndexDatabase::DidAddEntry,
@@ -656,7 +656,7 @@
     return;
 
   service_worker_context_->FindReadyRegistrationForId(
-      service_worker_registration_id, origin.GetURL(),
+      service_worker_registration_id, origin,
       base::BindOnce(&ContentIndexDatabase::StartActiveWorkerForDispatch,
                      weak_ptr_factory_core_.GetWeakPtr(), description_id));
 }
@@ -697,13 +697,13 @@
 
   // Don't allow DB operations while the `contentdelete` event is firing.
   // This is to prevent re-registering the deleted content within the event.
-  BlockOrigin(service_worker->script_origin());
+  BlockOrigin(service_worker->origin());
 
   int request_id = service_worker->StartRequest(
       ServiceWorkerMetrics::EventType::CONTENT_DELETE,
       base::BindOnce(&ContentIndexDatabase::DidDispatchEvent,
                      weak_ptr_factory_core_.GetWeakPtr(),
-                     service_worker->script_origin()));
+                     service_worker->origin()));
 
   service_worker->endpoint()->DispatchContentDeleteEvent(
       description_id, service_worker->CreateSimpleEventCallback(request_id));
diff --git a/content/browser/content_index/content_index_database_unittest.cc b/content/browser/content_index/content_index_database_unittest.cc
index ab1f4cb..8739e76 100644
--- a/content/browser/content_index/content_index_database_unittest.cc
+++ b/content/browser/content_index/content_index_database_unittest.cc
@@ -258,7 +258,7 @@
     {
       base::RunLoop run_loop;
       embedded_worker_test_helper_.context()->registry()->FindRegistrationForId(
-          service_worker_registration_id, origin_.GetURL(),
+          service_worker_registration_id, origin_,
           base::BindOnce(&DidFindServiceWorkerRegistration,
                          &service_worker_registration_,
                          run_loop.QuitClosure()));
diff --git a/content/browser/content_index/content_index_service_impl.cc b/content/browser/content_index/content_index_service_impl.cc
index 314dc6a..2946893 100644
--- a/content/browser/content_index/content_index_service_impl.cc
+++ b/content/browser/content_index/content_index_service_impl.cc
@@ -52,7 +52,7 @@
 
   mojo::MakeSelfOwnedReceiver(
       std::make_unique<ContentIndexServiceImpl>(
-          info.script_origin, storage_partition->GetContentIndexContext()),
+          info.origin, storage_partition->GetContentIndexContext()),
       std::move(receiver));
 }
 
diff --git a/content/browser/cookie_store/cookie_store_context.cc b/content/browser/cookie_store/cookie_store_context.cc
index 1e3776b..2c71c18 100644
--- a/content/browser/cookie_store/cookie_store_context.cc
+++ b/content/browser/cookie_store/cookie_store_context.cc
@@ -107,7 +107,7 @@
   StoragePartitionImpl* storage_partition = static_cast<StoragePartitionImpl*>(
       render_process_host->GetStoragePartition());
   storage_partition->GetCookieStoreContext()->CreateServiceForTesting(
-      info.script_origin, std::move(receiver));
+      info.origin, std::move(receiver));
 }
 
 void CookieStoreContext::CreateServiceForTesting(
diff --git a/content/browser/cookie_store/cookie_store_manager.cc b/content/browser/cookie_store/cookie_store_manager.cc
index 498034b..c62e7b30 100644
--- a/content/browser/cookie_store/cookie_store_manager.cc
+++ b/content/browser/cookie_store/cookie_store_manager.cc
@@ -412,7 +412,8 @@
       << "Failed to create cookie change subscriptions protobuf";
 
   service_worker_context_->StoreRegistrationUserData(
-      service_worker_registration_id, service_worker_origin,
+      service_worker_registration_id,
+      url::Origin::Create(service_worker_origin),
       std::vector<std::pair<std::string, std::string>>(
           {{registration_user_data_key_, subscriptions_data}}),
       base::BindOnce(
diff --git a/content/browser/devtools/devtools_background_services_context_impl.cc b/content/browser/devtools/devtools_background_services_context_impl.cc
index 81117f4..db9f9ea 100644
--- a/content/browser/devtools/devtools_background_services_context_impl.cc
+++ b/content/browser/devtools/devtools_background_services_context_impl.cc
@@ -278,7 +278,7 @@
                                          event_metadata.end());
 
   service_worker_context_->StoreRegistrationUserData(
-      service_worker_registration_id, origin.GetURL(),
+      service_worker_registration_id, origin,
       {{CreateEntryKey(event.background_service()), event.SerializeAsString()}},
       base::BindOnce(&DidLogServiceEvent));
 
diff --git a/content/browser/devtools/devtools_background_services_context_impl_unittest.cc b/content/browser/devtools/devtools_background_services_context_impl_unittest.cc
index d93be3b..ebafb3c 100644
--- a/content/browser/devtools/devtools_background_services_context_impl_unittest.cc
+++ b/content/browser/devtools/devtools_background_services_context_impl_unittest.cc
@@ -231,7 +231,7 @@
     {
       base::RunLoop run_loop;
       embedded_worker_test_helper_.context()->registry()->FindRegistrationForId(
-          service_worker_registration_id, origin_.GetURL(),
+          service_worker_registration_id, origin_,
           base::BindOnce(&DidFindServiceWorkerRegistration,
                          &service_worker_registration_,
                          run_loop.QuitClosure()));
diff --git a/content/browser/devtools/protocol/service_worker_handler.cc b/content/browser/devtools/protocol/service_worker_handler.cc
index 662e7c8a..f228c8e 100644
--- a/content/browser/devtools/protocol/service_worker_handler.cc
+++ b/content/browser/devtools/protocol/service_worker_handler.cc
@@ -154,7 +154,7 @@
 void DispatchSyncEventOnCoreThread(
     scoped_refptr<ServiceWorkerContextWrapper> context,
     scoped_refptr<BackgroundSyncContextImpl> sync_context,
-    const GURL& origin,
+    const url::Origin& origin,
     int64_t registration_id,
     const std::string& tag,
     bool last_chance) {
@@ -167,7 +167,7 @@
 void DispatchPeriodicSyncEventOnCoreThread(
     scoped_refptr<ServiceWorkerContextWrapper> context,
     scoped_refptr<BackgroundSyncContextImpl> sync_context,
-    const GURL& origin,
+    const url::Origin& origin,
     int64_t registration_id,
     const std::string& tag) {
   context->FindReadyRegistrationForId(
@@ -373,10 +373,11 @@
   BackgroundSyncContextImpl* sync_context =
       storage_partition_->GetBackgroundSyncContext();
 
-  RunOrPostTaskOnThread(FROM_HERE, ServiceWorkerContext::GetCoreThreadId(),
-                        base::BindOnce(&DispatchSyncEventOnCoreThread, context_,
-                                       base::WrapRefCounted(sync_context),
-                                       GURL(origin), id, tag, last_chance));
+  RunOrPostTaskOnThread(
+      FROM_HERE, ServiceWorkerContext::GetCoreThreadId(),
+      base::BindOnce(&DispatchSyncEventOnCoreThread, context_,
+                     base::WrapRefCounted(sync_context),
+                     url::Origin::Create(GURL(origin)), id, tag, last_chance));
   return Response::Success();
 }
 
@@ -398,8 +399,8 @@
   RunOrPostTaskOnThread(
       FROM_HERE, ServiceWorkerContext::GetCoreThreadId(),
       base::BindOnce(&DispatchPeriodicSyncEventOnCoreThread, context_,
-                     base::WrapRefCounted(sync_context), GURL(origin), id,
-                     tag));
+                     base::WrapRefCounted(sync_context),
+                     url::Origin::Create(GURL(origin)), id, tag));
   return Response::Success();
 }
 
diff --git a/content/browser/download/download_manager_impl.cc b/content/browser/download/download_manager_impl.cc
index d7b1e94..f6b8e53 100644
--- a/content/browser/download/download_manager_impl.cc
+++ b/content/browser/download/download_manager_impl.cc
@@ -1316,7 +1316,7 @@
                                                        site_url));
 
     pending_url_loader_factory =
-        CreatePendingSharedURLLoaderFactoryFromURLLoaderFactory(
+        std::make_unique<network::WrapperPendingSharedURLLoaderFactory>(
             CreateFileSystemURLLoaderFactory(
                 rfh->GetProcess()->GetID(), rfh->GetFrameTreeNodeId(),
                 storage_partition->GetFileSystemContext(),
diff --git a/content/browser/download/save_file_manager.cc b/content/browser/download/save_file_manager.cc
index aed0ee3..a4d1228 100644
--- a/content/browser/download/save_file_manager.cc
+++ b/content/browser/download/save_file_manager.cc
@@ -274,10 +274,10 @@
           static_cast<StoragePartitionImpl*>(storage_partition);
       auto partition_domain =
           rfh->GetSiteInstance()->GetPartitionDomain(storage_partition_impl);
-      url_loader_factory = CreateFileSystemURLLoaderFactory(
+      factory_remote.Bind(CreateFileSystemURLLoaderFactory(
           rfh->GetProcess()->GetID(), rfh->GetFrameTreeNodeId(),
-          storage_partition->GetFileSystemContext(), partition_domain);
-      factory = url_loader_factory.get();
+          storage_partition->GetFileSystemContext(), partition_domain));
+      factory = factory_remote.get();
     } else if (rfh && url.SchemeIs(content::kChromeUIScheme)) {
       url_loader_factory = CreateWebUIURLLoader(rfh, url.scheme(),
                                                 base::flat_set<std::string>());
diff --git a/content/browser/file_system/file_system_url_loader_factory.cc b/content/browser/file_system/file_system_url_loader_factory.cc
index 9a93b72b..7a28f39 100644
--- a/content/browser/file_system/file_system_url_loader_factory.cc
+++ b/content/browser/file_system/file_system_url_loader_factory.cc
@@ -22,6 +22,7 @@
 #include "build/build_config.h"
 #include "components/services/filesystem/public/mojom/types.mojom.h"
 #include "content/browser/child_process_security_policy_impl.h"
+#include "content/browser/loader/non_network_url_loader_factory_base.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_frame_host.h"
@@ -29,7 +30,6 @@
 #include "content/public/common/child_process_host.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"
 #include "mojo/public/cpp/system/data_pipe_producer.h"
 #include "mojo/public/cpp/system/string_data_source.h"
@@ -600,12 +600,15 @@
 
 // A URLLoaderFactory used for the filesystem:// scheme used when the Network
 // Service is enabled.
-class FileSystemURLLoaderFactory : public network::mojom::URLLoaderFactory {
+class FileSystemURLLoaderFactory : public NonNetworkURLLoaderFactoryBase {
  public:
   FileSystemURLLoaderFactory(
       FactoryParams params,
-      scoped_refptr<base::SequencedTaskRunner> io_task_runner)
-      : params_(std::move(params)), io_task_runner_(io_task_runner) {}
+      scoped_refptr<base::SequencedTaskRunner> io_task_runner,
+      mojo::PendingReceiver<network::mojom::URLLoaderFactory> factory_receiver)
+      : NonNetworkURLLoaderFactoryBase(std::move(factory_receiver)),
+        params_(std::move(params)),
+        io_task_runner_(io_task_runner) {}
 
   ~FileSystemURLLoaderFactory() override = default;
 
@@ -638,13 +641,7 @@
                                             io_task_runner_);
   }
 
-  void Clone(
-      mojo::PendingReceiver<network::mojom::URLLoaderFactory> loader) override {
-    receivers_.Add(this, std::move(loader));
-  }
-
   const FactoryParams params_;
-  mojo::ReceiverSet<network::mojom::URLLoaderFactory> receivers_;
   scoped_refptr<base::SequencedTaskRunner> io_task_runner_;
 
   DISALLOW_COPY_AND_ASSIGN(FileSystemURLLoaderFactory);
@@ -652,16 +649,23 @@
 
 }  // anonymous namespace
 
-std::unique_ptr<network::mojom::URLLoaderFactory>
+mojo::PendingRemote<network::mojom::URLLoaderFactory>
 CreateFileSystemURLLoaderFactory(
     int render_process_host_id,
     int frame_tree_node_id,
     scoped_refptr<FileSystemContext> file_system_context,
     const std::string& storage_domain) {
+  mojo::PendingRemote<network::mojom::URLLoaderFactory> pending_remote;
   FactoryParams params = {render_process_host_id, frame_tree_node_id,
                           file_system_context, storage_domain};
-  return std::make_unique<FileSystemURLLoaderFactory>(
-      std::move(params), GetIOThreadTaskRunner({}));
+
+  // The FileSystemURLLoaderFactory will delete itself when there are no more
+  // receivers - see the NonNetworkURLLoaderFactoryBase::OnDisconnect method.
+  new FileSystemURLLoaderFactory(
+      std::move(params), GetIOThreadTaskRunner({}),
+      pending_remote.InitWithNewPipeAndPassReceiver());
+
+  return pending_remote;
 }
 
 }  // namespace content
diff --git a/content/browser/file_system/file_system_url_loader_factory.h b/content/browser/file_system/file_system_url_loader_factory.h
index 318e2e4..1f6cf6d 100644
--- a/content/browser/file_system/file_system_url_loader_factory.h
+++ b/content/browser/file_system/file_system_url_loader_factory.h
@@ -5,11 +5,11 @@
 #ifndef CONTENT_BROWSER_FILE_SYSTEM_FILE_SYSTEM_URL_LOADER_FACTORY_H_
 #define CONTENT_BROWSER_FILE_SYSTEM_FILE_SYSTEM_URL_LOADER_FACTORY_H_
 
-#include <memory>
 #include <string>
 
 #include "base/memory/ref_counted.h"
 #include "content/common/content_export.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
 #include "storage/browser/file_system/file_system_context.h"
 
@@ -17,9 +17,13 @@
 
 class RenderFrameHost;
 
-// Create a URLLoaderFactory to serve filesystem: requests from the given
+// Creates a URLLoaderFactory to serve filesystem: requests from the given
 // |file_system_context| and |storage_domain|.
 //
+// The factory is self-owned - it will delete itself once there are no more
+// receivers (including the receiver associated with the returned
+// mojo::PendingRemote and the receivers bound by the Clone method).
+//
 // |render_process_host_id| is the ID of the RenderProcessHost where the
 // requests are issued.
 // - For a factory created for a browser-initiated navigation request:
@@ -39,7 +43,8 @@
 //   factory created for subresource requests from the frame: that frame's ID.
 // - For a factory created for workers (which don't have frames):
 //   RenderFrameHost::kNoFrameTreeNodeId.
-CONTENT_EXPORT std::unique_ptr<network::mojom::URLLoaderFactory>
+CONTENT_EXPORT
+mojo::PendingRemote<network::mojom::URLLoaderFactory>
 CreateFileSystemURLLoaderFactory(
     int render_process_host_id,
     int frame_tree_node_id,
diff --git a/content/browser/file_system/file_system_url_loader_factory_browsertest.cc b/content/browser/file_system/file_system_url_loader_factory_browsertest.cc
index 7da8896..d6718f6 100644
--- a/content/browser/file_system/file_system_url_loader_factory_browsertest.cc
+++ b/content/browser/file_system/file_system_url_loader_factory_browsertest.cc
@@ -464,10 +464,11 @@
       request.headers.MergeFrom(*extra_headers);
     const std::string storage_domain = url.GetOrigin().host();
 
-    auto factory = content::CreateFileSystemURLLoaderFactory(
-        render_frame_host()->GetProcess()->GetID(),
-        render_frame_host()->GetFrameTreeNodeId(), file_system_context,
-        storage_domain);
+    mojo::Remote<network::mojom::URLLoaderFactory> factory(
+        CreateFileSystemURLLoaderFactory(
+            render_frame_host()->GetProcess()->GetID(),
+            render_frame_host()->GetFrameTreeNodeId(), file_system_context,
+            storage_domain));
 
     auto client = std::make_unique<network::TestURLLoaderClient>();
     loader_.reset();
diff --git a/content/browser/find_request_manager_browsertest.cc b/content/browser/find_request_manager_browsertest.cc
index 0a281e7..9b734d5 100644
--- a/content/browser/find_request_manager_browsertest.cc
+++ b/content/browser/find_request_manager_browsertest.cc
@@ -24,6 +24,7 @@
 #include "content/test/content_browser_test_utils_internal.h"
 #include "net/dns/mock_host_resolver.h"
 #include "third_party/blink/public/common/features.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "third_party/blink/public/mojom/page/widget.mojom-test-utils.h"
 
 namespace content {
@@ -228,7 +229,8 @@
 IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, ScrollAndZoomIntoView) {
   WebContentsImpl* web_contents =
       static_cast<WebContentsImpl*>(shell()->web_contents());
-  WebPreferences prefs = web_contents->GetOrCreateWebPreferences();
+  blink::web_pref::WebPreferences prefs =
+      web_contents->GetOrCreateWebPreferences();
   prefs.smooth_scroll_for_find_enabled = false;
   web_contents->SetWebPreferences(prefs);
 
diff --git a/content/browser/loader/cors_file_origin_browsertest.cc b/content/browser/loader/cors_file_origin_browsertest.cc
index 8739ca790..c88e241e 100644
--- a/content/browser/loader/cors_file_origin_browsertest.cc
+++ b/content/browser/loader/cors_file_origin_browsertest.cc
@@ -18,7 +18,6 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/content_paths.h"
 #include "content/public/common/content_switches.h"
-#include "content/public/common/web_preferences.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_browser_test.h"
@@ -33,6 +32,7 @@
 #include "services/network/public/cpp/cors/cors.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "url/gurl.h"
 #include "url/url_constants.h"
 
@@ -261,7 +261,8 @@
       JsReplace(kScript, embedded_test_server()->GetURL("/title2.html"));
 
   // Activate the preference to allow universal access from file URLs.
-  WebPreferences prefs = shell()->web_contents()->GetOrCreateWebPreferences();
+  blink::web_pref::WebPreferences prefs =
+      shell()->web_contents()->GetOrCreateWebPreferences();
   prefs.allow_universal_access_from_file_urls = true;
   shell()->web_contents()->SetWebPreferences(prefs);
 
diff --git a/content/browser/loader/cross_site_document_blocking_browsertest.cc b/content/browser/loader/cross_site_document_blocking_browsertest.cc
index 4bd5891..4d8a7f9 100644
--- a/content/browser/loader/cross_site_document_blocking_browsertest.cc
+++ b/content/browser/loader/cross_site_document_blocking_browsertest.cc
@@ -30,7 +30,6 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
-#include "content/public/common/web_preferences.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_browser_test.h"
@@ -54,6 +53,7 @@
 #include "services/network/public/cpp/network_switches.h"
 #include "services/network/test/test_url_loader_client.h"
 #include "testing/gmock/include/gmock/gmock.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "third_party/blink/public/mojom/loader/resource_load_info.mojom-shared.h"
 
 namespace content {
diff --git a/content/browser/loader/navigation_url_loader_impl.cc b/content/browser/loader/navigation_url_loader_impl.cc
index 16b8bbd..1e572be 100644
--- a/content/browser/loader/navigation_url_loader_impl.cc
+++ b/content/browser/loader/navigation_url_loader_impl.cc
@@ -1201,7 +1201,7 @@
     }
 
     const std::string storage_domain;
-    non_network_uniquely_owned_factories_.emplace(
+    non_network_url_loader_factories_.emplace(
         url::kFileSystemScheme,
         CreateFileSystemURLLoaderFactory(
             ChildProcessHost::kInvalidUniqueID,
diff --git a/content/browser/notifications/blink_notification_service_impl_unittest.cc b/content/browser/notifications/blink_notification_service_impl_unittest.cc
index a3deeb4..8465aa1 100644
--- a/content/browser/notifications/blink_notification_service_impl_unittest.cc
+++ b/content/browser/notifications/blink_notification_service_impl_unittest.cc
@@ -190,7 +190,8 @@
     {
       base::RunLoop run_loop;
       embedded_worker_helper_->context()->registry()->FindRegistrationForId(
-          service_worker_registration_id, GURL(kTestOrigin),
+          service_worker_registration_id,
+          url::Origin::Create(GURL(kTestOrigin)),
           base::BindOnce(&BlinkNotificationServiceImplTest::
                              DidFindServiceWorkerRegistration,
                          base::Unretained(this), service_worker_registration,
diff --git a/content/browser/notifications/notification_event_dispatcher_impl.cc b/content/browser/notifications/notification_event_dispatcher_impl.cc
index 9b770a80..ccef290 100644
--- a/content/browser/notifications/notification_event_dispatcher_impl.cc
+++ b/content/browser/notifications/notification_event_dispatcher_impl.cc
@@ -155,7 +155,7 @@
 // Finds the ServiceWorkerRegistration associated with the |origin| and
 // |service_worker_registration_id|. Must be called on the UI thread.
 void FindServiceWorkerRegistration(
-    const GURL& origin,
+    const url::Origin& origin,
     const scoped_refptr<ServiceWorkerContextWrapper>& service_worker_context,
     NotificationOperationCallback notification_action_callback,
     NotificationDispatchCompleteCallback dispatch_complete_callback,
@@ -199,8 +199,8 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   notification_context->ReadNotificationDataAndRecordInteraction(
       notification_id, origin, interaction,
-      base::BindOnce(&FindServiceWorkerRegistration, origin,
-                     service_worker_context,
+      base::BindOnce(&FindServiceWorkerRegistration,
+                     url::Origin::Create(origin), service_worker_context,
                      std::move(notification_read_callback),
                      std::move(dispatch_complete_callback)));
 }
diff --git a/content/browser/notifications/notification_storage.cc b/content/browser/notifications/notification_storage.cc
index d60536c..f7caccdd4 100644
--- a/content/browser/notifications/notification_storage.cc
+++ b/content/browser/notifications/notification_storage.cc
@@ -57,7 +57,7 @@
   }
 
   service_worker_context_->StoreRegistrationUserData(
-      data.service_worker_registration_id, data.origin,
+      data.service_worker_registration_id, url::Origin::Create(data.origin),
       {{CreateDataKey(data.notification_id), std::move(serialized_data)}},
       base::BindOnce(&NotificationStorage::OnWriteComplete,
                      weak_ptr_factory_.GetWeakPtr(), data,
@@ -133,7 +133,7 @@
     return;
   }
 
-  GURL origin = data->origin;
+  url::Origin origin = url::Origin::Create(data->origin);
   std::string notification_id = data->notification_id;
   service_worker_context_->StoreRegistrationUserData(
       service_worker_registration_id, origin,
diff --git a/content/browser/notifications/notification_storage_unittest.cc b/content/browser/notifications/notification_storage_unittest.cc
index 9d6eda7..1582d7b 100644
--- a/content/browser/notifications/notification_storage_unittest.cc
+++ b/content/browser/notifications/notification_storage_unittest.cc
@@ -21,7 +21,8 @@
  public:
   NotificationStorageTest()
       : task_environment_(BrowserTaskEnvironment::IO_MAINLOOP),
-        origin_(GURL("https://example.com")),
+        url_(GURL("https://example.com")),
+        origin_(url::Origin::Create(url_)),
         success_(false),
         service_worker_registration_id_(
             blink::mojom::kInvalidServiceWorkerRegistrationId) {
@@ -61,11 +62,11 @@
   // blink::mojom::kInvalidServiceWorkerRegistrationId. The
   // ServiceWorkerRegistration will be kept alive for the test's lifetime.
   int64_t RegisterServiceWorker() {
-    GURL script_url = origin_;
+    GURL script_url = url_;
 
     {
       blink::mojom::ServiceWorkerRegistrationOptions options;
-      options.scope = origin_;
+      options.scope = url_;
       base::RunLoop run_loop;
       helper_->context()->RegisterServiceWorker(
           script_url, options, blink::mojom::FetchClientSettingsObject::New(),
@@ -154,7 +155,8 @@
  protected:
   BrowserTaskEnvironment task_environment_;  // Must be first member
   std::unique_ptr<EmbeddedWorkerTestHelper> helper_;
-  GURL origin_;
+  GURL url_;
+  url::Origin origin_;
   TestBrowserContext browser_context_;
   bool success_;
   int64_t service_worker_registration_id_;
@@ -173,7 +175,7 @@
 TEST_F(NotificationStorageTest, WriteReadNotification) {
   NotificationDatabaseData data;
   data.notification_id = GenerateNotificationId();
-  data.origin = origin_;
+  data.origin = url_;
   data.service_worker_registration_id = RegisterServiceWorker();
   ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId,
             data.service_worker_registration_id);
@@ -204,7 +206,7 @@
 TEST_F(NotificationStorageTest, ReadAndUpdateInteraction) {
   NotificationDatabaseData data, read_data;
   data.notification_id = GenerateNotificationId();
-  data.origin = origin_;
+  data.origin = url_;
   data.service_worker_registration_id = RegisterServiceWorker();
   ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId,
             data.service_worker_registration_id);
diff --git a/content/browser/notifications/platform_notification_service_proxy.cc b/content/browser/notifications/platform_notification_service_proxy.cc
index 56d40d3b..fe806d8 100644
--- a/content/browser/notifications/platform_notification_service_proxy.cc
+++ b/content/browser/notifications/platform_notification_service_proxy.cc
@@ -102,7 +102,7 @@
       base::BindOnce(
           &ServiceWorkerContextWrapper::FindReadyRegistrationForId,
           service_worker_context_, data.service_worker_registration_id,
-          data.origin,
+          url::Origin::Create(data.origin),
           base::BindOnce(
               &PlatformNotificationServiceProxy::VerifyServiceWorkerScope,
               weak_ptr_factory_io_.GetWeakPtr(), data, std::move(callback))));
diff --git a/content/browser/payments/payment_app_database.cc b/content/browser/payments/payment_app_database.cc
index ad6d1e7..fb108835 100644
--- a/content/browser/payments/payment_app_database.cc
+++ b/content/browser/payments/payment_app_database.cc
@@ -348,7 +348,7 @@
   DCHECK(success);
 
   service_worker_context_->StoreRegistrationUserData(
-      registration->id(), registration->scope().GetOrigin(),
+      registration->id(), registration->origin(),
       {{CreatePaymentAppKey(registration->scope().spec()),
         serialized_payment_app}},
       base::BindOnce(&PaymentAppDatabase::DidUpdatePaymentApp,
@@ -472,7 +472,7 @@
   DCHECK(success);
 
   service_worker_context_->StoreRegistrationUserData(
-      registration_id, pattern.GetOrigin(),
+      registration_id, url::Origin::Create(pattern),
       {{CreatePaymentAppKey(pattern.spec()), serialized_payment_app}},
       base::BindOnce(&PaymentAppDatabase::DidEnablePaymentAppDelegations,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
@@ -525,7 +525,7 @@
   DCHECK(success);
 
   service_worker_context_->StoreRegistrationUserData(
-      registration_id, pattern.GetOrigin(),
+      registration_id, url::Origin::Create(pattern),
       {{CreatePaymentAppKey(pattern.spec()), serialized_payment_app}},
       base::BindOnce(&PaymentAppDatabase::DidSetPaymentAppUserHint,
                      weak_ptr_factory_.GetWeakPtr()));
@@ -596,7 +596,7 @@
   // Constructing registration_id, registration_origin and storage_key before
   // moving registration.
   int64_t registration_id = registration->id();
-  GURL registration_origin = registration->scope().GetOrigin();
+  url::Origin registration_origin = registration->origin();
   std::string storage_key = CreatePaymentAppKey(registration->scope().spec());
   service_worker_context_->StoreRegistrationUserData(
       registration_id, registration_origin,
@@ -639,7 +639,7 @@
   DCHECK(success);
 
   service_worker_context_->StoreRegistrationUserData(
-      registration->id(), registration->scope().GetOrigin(),
+      registration->id(), registration->origin(),
       {{CreatePaymentInstrumentKey(instrument_key), serialized_instrument},
        {CreatePaymentInstrumentKeyInfoKey(instrument_key),
         serialized_key_info}},
@@ -920,7 +920,7 @@
   DCHECK(success);
 
   service_worker_context_->StoreRegistrationUserData(
-      registration->id(), registration->scope().GetOrigin(),
+      registration->id(), registration->origin(),
       {{CreatePaymentInstrumentKey(instrument_key), serialized_instrument},
        {CreatePaymentInstrumentKeyInfoKey(instrument_key),
         serialized_key_info}},
diff --git a/content/browser/portal/portal_browsertest.cc b/content/browser/portal/portal_browsertest.cc
index 0cbc319..5679781 100644
--- a/content/browser/portal/portal_browsertest.cc
+++ b/content/browser/portal/portal_browsertest.cc
@@ -2377,13 +2377,13 @@
 #endif
 IN_PROC_BROWSER_TEST_F(PortalPixelBrowserTest, MAYBE_PageScaleRaster) {
   ShellContentBrowserClient::Get()->set_override_web_preferences_callback(
-      base::BindRepeating([](WebPreferences* prefs) {
+      base::BindRepeating([](blink::web_pref::WebPreferences* prefs) {
         // Enable processing of the viewport <meta> tag in the same way the
         // Android browser would.
         prefs->viewport_enabled = true;
         prefs->viewport_meta_enabled = true;
         prefs->shrinks_viewport_contents_to_fit = true;
-        prefs->viewport_style = content::ViewportStyle::MOBILE;
+        prefs->viewport_style = blink::web_pref::ViewportStyle::MOBILE;
 
         // Hide scrollbars to make pixel testing more robust.
         prefs->hide_scrollbars = true;
diff --git a/content/browser/push_messaging/push_messaging_manager.cc b/content/browser/push_messaging/push_messaging_manager.cc
index c6279d4a..7cea192 100644
--- a/content/browser/push_messaging/push_messaging_manager.cc
+++ b/content/browser/push_messaging/push_messaging_manager.cc
@@ -563,7 +563,7 @@
     const std::vector<uint8_t>& auth,
     blink::mojom::PushRegistrationStatus status) {
   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
-  GURL requesting_origin = data.requesting_origin;
+  url::Origin requesting_origin = url::Origin::Create(data.requesting_origin);
   int64_t registration_id = data.service_worker_registration_id;
   std::string application_server_key(
       std::string(data.options->application_server_key.begin(),
diff --git a/content/browser/push_messaging/push_messaging_router.cc b/content/browser/push_messaging/push_messaging_router.cc
index fbd7122..7468d43 100644
--- a/content/browser/push_messaging/push_messaging_router.cc
+++ b/content/browser/push_messaging/push_messaging_router.cc
@@ -77,7 +77,7 @@
     ServiceWorkerMetrics::EventType event_type,
     scoped_refptr<ServiceWorkerContextWrapper> service_worker_context,
     scoped_refptr<DevToolsBackgroundServicesContextImpl> devtools_context,
-    const GURL& origin,
+    const url::Origin& origin,
     int64_t service_worker_registration_id,
     ServiceWorkerStartCallback callback) {
   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
@@ -112,7 +112,7 @@
       FROM_HERE, ServiceWorkerContext::GetCoreThreadId(),
       base::BindOnce(&FindServiceWorkerRegistration, event_type,
                      std::move(service_worker_context),
-                     std::move(devtools_context), origin,
+                     std::move(devtools_context), url::Origin::Create(origin),
                      service_worker_registration_id, std::move(callback)));
 }
 
@@ -179,7 +179,7 @@
     if (payload)
       event_metadata["Payload"] = *payload;
     devtools_context->LogBackgroundServiceEventOnCoreThread(
-        service_worker->registration_id(), service_worker->script_origin(),
+        service_worker->registration_id(), service_worker->origin(),
         DevToolsBackgroundService::kPushMessaging, "Push event dispatched",
         message_id, event_metadata);
   }
@@ -244,7 +244,7 @@
       push_event_status !=
           blink::mojom::PushEventStatus::SERVICE_WORKER_ERROR) {
     devtools_context->LogBackgroundServiceEventOnCoreThread(
-        service_worker->registration_id(), service_worker->script_origin(),
+        service_worker->registration_id(), service_worker->origin(),
         DevToolsBackgroundService::kPushMessaging, "Push event completed",
         message_id, {{"Status", status_description}});
   }
diff --git a/content/browser/renderer_host/frame_tree.cc b/content/browser/renderer_host/frame_tree.cc
index 91f8362..5ad1a889 100644
--- a/content/browser/renderer_host/frame_tree.cc
+++ b/content/browser/renderer_host/frame_tree.cc
@@ -50,7 +50,7 @@
 
 FrameTree::NodeIterator::NodeIterator(const NodeIterator& other) = default;
 
-FrameTree::NodeIterator::~NodeIterator() {}
+FrameTree::NodeIterator::~NodeIterator() = default;
 
 FrameTree::NodeIterator& FrameTree::NodeIterator::operator++() {
   if (current_node_ != root_of_subtree_to_skip_) {
diff --git a/content/browser/renderer_host/input/input_device_change_observer.cc b/content/browser/renderer_host/input/input_device_change_observer.cc
index 867c40e..e05bf26 100644
--- a/content/browser/renderer_host/input/input_device_change_observer.cc
+++ b/content/browser/renderer_host/input/input_device_change_observer.cc
@@ -6,7 +6,6 @@
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
-#include "content/public/common/web_preferences.h"
 
 #if defined(OS_WIN)
 #include "ui/events/devices/input_device_observer_win.h"
diff --git a/content/browser/renderer_host/input/touchpad_pinch_browsertest.cc b/content/browser/renderer_host/input/touchpad_pinch_browsertest.cc
index 5f13285..fded55f 100644
--- a/content/browser/renderer_host/input/touchpad_pinch_browsertest.cc
+++ b/content/browser/renderer_host/input/touchpad_pinch_browsertest.cc
@@ -11,7 +11,6 @@
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/content_features.h"
-#include "content/public/common/web_preferences.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_browser_test.h"
@@ -19,6 +18,7 @@
 #include "content/public/test/hit_test_region_observer.h"
 #include "content/public/test/test_utils.h"
 #include "content/shell/browser/shell.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 
 namespace content {
 
@@ -252,7 +252,8 @@
                        WheelListenerPreventingDoubleTap) {
   LoadURL();
 
-  WebPreferences prefs = shell()->web_contents()->GetOrCreateWebPreferences();
+  blink::web_pref::WebPreferences prefs =
+      shell()->web_contents()->GetOrCreateWebPreferences();
   prefs.double_tap_to_zoom_enabled = true;
   shell()->web_contents()->SetWebPreferences(prefs);
 
diff --git a/content/browser/renderer_host/media/media_stream_ui_proxy_unittest.cc b/content/browser/renderer_host/media/media_stream_ui_proxy_unittest.cc
index d5182ec5..f3507a0 100644
--- a/content/browser/renderer_host/media/media_stream_ui_proxy_unittest.cc
+++ b/content/browser/renderer_host/media/media_stream_ui_proxy_unittest.cc
@@ -20,6 +20,7 @@
 #include "content/test/test_render_frame_host.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "ui/gfx/geometry/rect.h"
 #include "url/gurl.h"
 #include "url/origin.h"
@@ -37,7 +38,7 @@
                                     MediaResponseCallback callback) {
     return RequestMediaAccessPermission(request, &callback);
   }
-  const WebPreferences& GetOrCreateWebPreferences() override {
+  const blink::web_pref::WebPreferences& GetOrCreateWebPreferences() override {
     return mock_web_preferences_;
   }
   MOCK_METHOD2(RequestMediaAccessPermission,
@@ -49,7 +50,7 @@
                     blink::mojom::MediaStreamType type));
 
  private:
-  WebPreferences mock_web_preferences_;
+  blink::web_pref::WebPreferences mock_web_preferences_;
 };
 
 class MockResponseCallback {
@@ -443,12 +444,13 @@
           devices, blink::mojom::MediaStreamRequestResult::OK, std::move(ui));
     }
 
-    const WebPreferences& GetOrCreateWebPreferences() override {
+    const blink::web_pref::WebPreferences& GetOrCreateWebPreferences()
+        override {
       return mock_web_preferences_;
     }
 
    private:
-    WebPreferences mock_web_preferences_;
+    blink::web_pref::WebPreferences mock_web_preferences_;
   };
 
   void GetResultForRequestOnIOThread(
diff --git a/content/browser/renderer_host/mixed_content_navigation_throttle.cc b/content/browser/renderer_host/mixed_content_navigation_throttle.cc
index 3a2573701..e94a78e 100644
--- a/content/browser/renderer_host/mixed_content_navigation_throttle.cc
+++ b/content/browser/renderer_host/mixed_content_navigation_throttle.cc
@@ -18,10 +18,10 @@
 #include "content/public/common/content_client.h"
 #include "content/public/common/navigation_policy.h"
 #include "content/public/common/origin_util.h"
-#include "content/public/common/web_preferences.h"
 #include "net/base/url_util.h"
 #include "third_party/blink/public/common/loader/network_utils.h"
 #include "third_party/blink/public/common/security_context/insecure_request_policy.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom.h"
 #include "third_party/blink/public/mojom/security_context/insecure_request_policy.mojom.h"
 
@@ -158,8 +158,7 @@
            .insecure_request_policy &
        blink::mojom::InsecureRequestPolicy::kBlockAllMixedContent) !=
       blink::mojom::InsecureRequestPolicy::kLeaveInsecureRequestsAlone;
-  const WebPreferences& prefs =
-      mixed_content_frame->GetOrCreateWebPreferences();
+  const auto& prefs = mixed_content_frame->GetOrCreateWebPreferences();
   bool strict_mode =
       prefs.strict_mixed_content_checking || block_all_mixed_content;
 
diff --git a/content/browser/renderer_host/navigation_controller_impl.cc b/content/browser/renderer_host/navigation_controller_impl.cc
index d4fd1aa..6bdd9cb 100644
--- a/content/browser/renderer_host/navigation_controller_impl.cc
+++ b/content/browser/renderer_host/navigation_controller_impl.cc
@@ -1992,7 +1992,7 @@
     last_committed_url = last_committed->GetURL();
   }
 
-  WebPreferences prefs = rfhi->GetOrCreateWebPreferences();
+  auto prefs = rfhi->GetOrCreateWebPreferences();
   const url::Origin& committed_origin =
       rfhi->frame_tree_node()->current_origin();
   bool is_same_origin = last_committed_url.is_empty() ||
diff --git a/content/browser/renderer_host/navigation_controller_impl_unittest.cc b/content/browser/renderer_host/navigation_controller_impl_unittest.cc
index 26784bc..437669e 100644
--- a/content/browser/renderer_host/navigation_controller_impl_unittest.cc
+++ b/content/browser/renderer_host/navigation_controller_impl_unittest.cc
@@ -2963,8 +2963,7 @@
   // Test allow_universal_access_from_file_urls flag.
   const GURL different_origin_url("http://www.example.com");
   MockRenderProcessHost* rph = main_test_rfh()->GetProcess();
-  WebPreferences prefs =
-      controller.GetWebContents()->GetOrCreateWebPreferences();
+  auto prefs = controller.GetWebContents()->GetOrCreateWebPreferences();
   prefs.allow_universal_access_from_file_urls = true;
   controller.GetWebContents()->SetWebPreferences(prefs);
   prefs = controller.GetWebContents()->GetOrCreateWebPreferences();
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc
index 68c2b4fd..3712fae 100644
--- a/content/browser/renderer_host/navigation_request.cc
+++ b/content/browser/renderer_host/navigation_request.cc
@@ -89,7 +89,6 @@
 #include "content/public/common/origin_util.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/common/url_utils.h"
-#include "content/public/common/web_preferences.h"
 #include "mojo/public/cpp/system/data_pipe.h"
 #include "net/base/filename_util.h"
 #include "net/base/ip_endpoint.h"
@@ -116,6 +115,7 @@
 #include "third_party/blink/public/common/blob/blob_utils.h"
 #include "third_party/blink/public/common/client_hints/client_hints.h"
 #include "third_party/blink/public/common/origin_trials/trial_token_validator.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "third_party/blink/public/mojom/appcache/appcache.mojom.h"
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom.h"
 #include "third_party/blink/public/mojom/renderer_preferences.mojom.h"
diff --git a/content/browser/renderer_host/render_frame_host_delegate.h b/content/browser/renderer_host/render_frame_host_delegate.h
index 014ac859..da5a5a6 100644
--- a/content/browser/renderer_host/render_frame_host_delegate.h
+++ b/content/browser/renderer_host/render_frame_host_delegate.h
@@ -72,6 +72,9 @@
 namespace mojom {
 class FullscreenOptions;
 }
+namespace web_pref {
+struct WebPreferences;
+}
 }  // namespace blink
 
 namespace ui {
@@ -89,7 +92,6 @@
 struct AXLocationChangeNotificationDetails;
 struct ContextMenuParams;
 struct GlobalRequestID;
-struct WebPreferences;
 
 namespace mojom {
 class CreateNewWindowParams;
@@ -474,7 +476,8 @@
   // WebPreferences. If we want to guarantee that the value reflects the current
   // state of the WebContents, NotifyPreferencesChanged() should be called
   // before calling this.
-  virtual const WebPreferences& GetOrCreateWebPreferences() = 0;
+  virtual const blink::web_pref::WebPreferences&
+  GetOrCreateWebPreferences() = 0;
 
   // Returns the visibility of the delegate.
   virtual Visibility GetVisibility();
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 8b1d217..1f5ae2cd 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -979,9 +979,9 @@
   unload_event_monitor_timeout_ =
       std::make_unique<TimeoutMonitor>(base::BindRepeating(
           &RenderFrameHostImpl::OnUnloaded, weak_ptr_factory_.GetWeakPtr()));
-  beforeunload_timeout_.reset(new TimeoutMonitor(
+  beforeunload_timeout_ = std::make_unique<TimeoutMonitor>(
       base::BindRepeating(&RenderFrameHostImpl::BeforeUnloadTimeout,
-                          weak_ptr_factory_.GetWeakPtr())));
+                          weak_ptr_factory_.GetWeakPtr()));
 
   // Local roots are:
   // - main frames; or
@@ -1571,7 +1571,8 @@
   return static_cast<int>(active_sandbox_flags_) & static_cast<int>(flags);
 }
 
-WebPreferences RenderFrameHostImpl::GetOrCreateWebPreferences() {
+blink::web_pref::WebPreferences
+RenderFrameHostImpl::GetOrCreateWebPreferences() {
   return delegate()->GetOrCreateWebPreferences();
 }
 
@@ -1581,7 +1582,7 @@
     const base::flat_set<url::Origin>& isolated_world_origins,
     network::mojom::ClientSecurityStatePtr client_security_state,
     network::mojom::TrustTokenRedemptionPolicy trust_token_redemption_policy) {
-  WebPreferences preferences = GetOrCreateWebPreferences();
+  auto preferences = GetOrCreateWebPreferences();
 
   blink::PendingURLLoaderFactoryBundle::OriginMap result;
   for (const url::Origin& isolated_world_origin : isolated_world_origins) {
@@ -1760,7 +1761,6 @@
   IPC_BEGIN_MESSAGE_MAP(RenderFrameHostImpl, msg)
     IPC_MESSAGE_HANDLER(FrameHostMsg_Unload_ACK, OnUnloadACK)
     IPC_MESSAGE_HANDLER(FrameHostMsg_ContextMenu, OnContextMenu)
-    IPC_MESSAGE_HANDLER(FrameHostMsg_SelectionChanged, OnSelectionChanged)
   IPC_END_MESSAGE_MAP()
 
   // No further actions here, since we may have been deleted.
@@ -4494,13 +4494,6 @@
   delegate_->DOMContentLoaded(this);
 }
 
-void RenderFrameHostImpl::OnSelectionChanged(const base::string16& text,
-                                             uint32_t offset,
-                                             const gfx::Range& range) {
-  has_selection_ = !text.empty();
-  GetRenderWidgetHost()->SelectionChanged(text, offset, range);
-}
-
 void RenderFrameHostImpl::FocusedElementChanged(
     bool is_editable_element,
     const gfx::Rect& bounds_in_frame_widget,
@@ -4518,6 +4511,13 @@
       focus_type);
 }
 
+void RenderFrameHostImpl::TextSelectionChanged(const base::string16& text,
+                                               uint32_t offset,
+                                               const gfx::Range& range) {
+  has_selection_ = !text.empty();
+  GetRenderWidgetHost()->SelectionChanged(text, offset, range);
+}
+
 void RenderFrameHostImpl::DidReceiveFirstUserActivation() {
   delegate_->DidReceiveFirstUserActivation(this);
 }
@@ -6234,8 +6234,8 @@
     auto* partition =
         static_cast<StoragePartitionImpl*>(BrowserContext::GetStoragePartition(
             browser_context, GetSiteInstance()));
-    non_network_uniquely_owned_factories_.emplace(
-        url::kFileSystemScheme, content::CreateFileSystemURLLoaderFactory(
+    non_network_factories.emplace(
+        url::kFileSystemScheme, CreateFileSystemURLLoaderFactory(
                                     GetProcess()->GetID(), GetFrameTreeNodeId(),
                                     partition->GetFileSystemContext(),
                                     partition->GetPartitionDomain()));
@@ -6626,7 +6626,7 @@
       remote_interfaces;
   frame_->GetInterfaceProvider(
       remote_interfaces.InitWithNewPipeAndPassReceiver());
-  remote_interfaces_.reset(new service_manager::InterfaceProvider);
+  remote_interfaces_ = std::make_unique<service_manager::InterfaceProvider>();
   remote_interfaces_->Bind(std::move(remote_interfaces));
 
   // Called to bind the receiver for this interface to the local frame. We need
@@ -7544,7 +7544,7 @@
 void RenderFrameHostImpl::BindAuthenticatorReceiver(
     mojo::PendingReceiver<blink::mojom::Authenticator> receiver) {
   if (!authenticator_impl_)
-    authenticator_impl_.reset(new AuthenticatorImpl(this));
+    authenticator_impl_ = std::make_unique<AuthenticatorImpl>(this);
 
   authenticator_impl_->Bind(std::move(receiver));
 }
@@ -7603,10 +7603,10 @@
 void RenderFrameHostImpl::GetSensorProvider(
     mojo::PendingReceiver<device::mojom::SensorProvider> receiver) {
   if (!sensor_provider_proxy_) {
-    sensor_provider_proxy_.reset(new SensorProviderProxyImpl(
+    sensor_provider_proxy_ = std::make_unique<SensorProviderProxyImpl>(
         PermissionControllerImpl::FromBrowserContext(
             GetProcess()->GetBrowserContext()),
-        this));
+        this);
   }
   sensor_provider_proxy_->Bind(std::move(receiver));
 }
@@ -7792,7 +7792,8 @@
 void RenderFrameHostImpl::CreatePermissionService(
     mojo::PendingReceiver<blink::mojom::PermissionService> receiver) {
   if (!permission_service_context_)
-    permission_service_context_.reset(new PermissionServiceContext(this));
+    permission_service_context_ =
+        std::make_unique<PermissionServiceContext>(this);
 
   permission_service_context_->CreateService(std::move(receiver));
 }
@@ -8104,7 +8105,7 @@
   // file: URLs can be allowed to access any other origin, based on settings.
   bool bypass_checks_for_file_scheme = false;
   if (params->origin.scheme() == url::kFileScheme) {
-    WebPreferences prefs = GetOrCreateWebPreferences();
+    auto prefs = GetOrCreateWebPreferences();
     if (prefs.allow_universal_access_from_file_urls)
       bypass_checks_for_file_scheme = true;
   }
@@ -8176,9 +8177,8 @@
   // banned URLs into the navigation controller in the first place.
   process->FilterURL(false, &params->url);
   process->FilterURL(true, &params->referrer.url);
-  for (auto it(params->redirects.begin()); it != params->redirects.end();
-       ++it) {
-    process->FilterURL(false, &(*it));
+  for (auto& redirect : params->redirects) {
+    process->FilterURL(false, &redirect);
   }
 
   // Without this check, the renderer can trick the browser into using
diff --git a/content/browser/renderer_host/render_frame_host_impl.h b/content/browser/renderer_host/render_frame_host_impl.h
index 4cc9f6ef5..b8e0478 100644
--- a/content/browser/renderer_host/render_frame_host_impl.h
+++ b/content/browser/renderer_host/render_frame_host_impl.h
@@ -409,7 +409,7 @@
   // Returns the current WebPreferences for the WebContents associated with this
   // RenderFrameHost. Will create one if it does not exist (and update all the
   // renderers with the newly computed value).
-  WebPreferences GetOrCreateWebPreferences();
+  blink::web_pref::WebPreferences GetOrCreateWebPreferences();
 
   // IPC::Sender
   bool Send(IPC::Message* msg) override;
@@ -1626,6 +1626,9 @@
   void FocusedElementChanged(bool is_editable_element,
                              const gfx::Rect& bounds_in_frame_widget,
                              blink::mojom::FocusType focus_type) override;
+  void TextSelectionChanged(const base::string16& text,
+                            uint32_t offset,
+                            const gfx::Range& range) override;
   void ShowPopupMenu(
       mojo::PendingRemote<blink::mojom::PopupMenuClient> popup_client,
       const gfx::Rect& bounds,
@@ -1963,9 +1966,6 @@
   void OnContextMenu(const UntrustworthyContextMenuParams& params);
   void OnForwardResourceTimingToParent(
       const ResourceTimingInfo& resource_timing);
-  void OnSelectionChanged(const base::string16& text,
-                          uint32_t offset,
-                          const gfx::Range& range);
   void OnSetNeedsOcclusionTracking(bool needs_tracking);
   void OnSaveImageFromDataURL(const std::string& url_str);
 
diff --git a/content/browser/renderer_host/render_frame_host_manager.h b/content/browser/renderer_host/render_frame_host_manager.h
index ea7832e0b..bead5656 100644
--- a/content/browser/renderer_host/render_frame_host_manager.h
+++ b/content/browser/renderer_host/render_frame_host_manager.h
@@ -164,7 +164,7 @@
     virtual int GetOuterDelegateFrameTreeNodeId() = 0;
 
    protected:
-    virtual ~Delegate() {}
+    virtual ~Delegate() = default;
   };
 
   // The delegate pointer must be non-null and is not owned by this class. It
diff --git a/content/browser/renderer_host/render_frame_host_manager_browsertest.cc b/content/browser/renderer_host/render_frame_host_manager_browsertest.cc
index ea0e569c..3f246d1a 100644
--- a/content/browser/renderer_host/render_frame_host_manager_browsertest.cc
+++ b/content/browser/renderer_host/render_frame_host_manager_browsertest.cc
@@ -56,7 +56,6 @@
 #include "content/public/common/content_switches.h"
 #include "content/public/common/page_state.h"
 #include "content/public/common/url_constants.h"
-#include "content/public/common/web_preferences.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_browser_test.h"
@@ -82,6 +81,7 @@
 #include "services/network/public/cpp/features.h"
 #include "testing/gmock/include/gmock/gmock-matchers.h"
 #include "third_party/blink/public/common/action_after_pagehide.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 
 using base::ASCIIToUTF16;
 using blink::ActionAfterPagehide;
@@ -3727,7 +3727,7 @@
       static_cast<WebContentsImpl*>(shell()->web_contents());
   FrameTreeNode* root = web_contents->GetFrameTree()->root();
 
-  WebPreferences prefs = web_contents->GetOrCreateWebPreferences();
+  auto prefs = web_contents->GetOrCreateWebPreferences();
   prefs.allow_universal_access_from_file_urls = true;
   web_contents->SetWebPreferences(prefs);
 
diff --git a/content/browser/renderer_host/render_message_filter.cc b/content/browser/renderer_host/render_message_filter.cc
index 3a908956..758ed53 100644
--- a/content/browser/renderer_host/render_message_filter.cc
+++ b/content/browser/renderer_host/render_message_filter.cc
@@ -134,7 +134,7 @@
     return;
   }
 
-  base::PlatformThread::SetThreadPriority(peer_tid, priority);
+  base::PlatformThread::SetThreadPriority(peer_pid(), peer_tid, priority);
 }
 #endif
 
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 6f7d900..e45dbb3 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -178,7 +178,6 @@
 #include "content/public/common/sandboxed_process_launcher_delegate.h"
 #include "content/public/common/service_names.mojom.h"
 #include "content/public/common/url_constants.h"
-#include "content/public/common/web_preferences.h"
 #include "content/public/common/zygote/zygote_buildflags.h"
 #include "device/gamepad/gamepad_haptics_manager.h"
 #include "google_apis/gaia/gaia_switches.h"
diff --git a/content/browser/renderer_host/render_view_host_delegate.cc b/content/browser/renderer_host/render_view_host_delegate.cc
index 2b0a773..39544f7 100644
--- a/content/browser/renderer_host/render_view_host_delegate.cc
+++ b/content/browser/renderer_host/render_view_host_delegate.cc
@@ -4,8 +4,6 @@
 
 #include "content/browser/renderer_host/render_view_host_delegate.h"
 
-#include "content/public/common/web_preferences.h"
-
 namespace content {
 
 RenderViewHostDelegateView* RenderViewHostDelegate::GetDelegateView() {
diff --git a/content/browser/renderer_host/render_view_host_delegate.h b/content/browser/renderer_host/render_view_host_delegate.h
index 361e214..352a257 100644
--- a/content/browser/renderer_host/render_view_host_delegate.h
+++ b/content/browser/renderer_host/render_view_host_delegate.h
@@ -26,6 +26,9 @@
 namespace mojom {
 class RendererPreferences;
 }
+namespace web_pref {
+struct WebPreferences;
+}
 }  // namespace blink
 
 namespace gfx {
@@ -43,7 +46,6 @@
 class SessionStorageNamespace;
 class SiteInstance;
 class WebContents;
-struct WebPreferences;
 
 //
 // RenderViewHostDelegate
@@ -150,7 +152,8 @@
   // WebPreferences. If we want to guarantee that the value reflects the current
   // state of the WebContents, NotifyPreferencesChanged() should be called
   // before calling this.
-  virtual const WebPreferences& GetOrCreateWebPreferences() = 0;
+  virtual const blink::web_pref::WebPreferences&
+  GetOrCreateWebPreferences() = 0;
 
   // Returns true if the WebPreferences for this RenderViewHost is not null.
   virtual bool IsWebPreferencesSet() const;
@@ -158,7 +161,8 @@
   // Sets the WebPreferences for the WebContents associated with this
   // RenderViewHost to |prefs| and send the new value to all renderers in the
   // WebContents.
-  virtual void SetWebPreferences(const WebPreferences& prefs) {}
+  virtual void SetWebPreferences(const blink::web_pref::WebPreferences& prefs) {
+  }
 
   // Triggers a total recomputation of WebPreferences by resetting the current
   // cached WebPreferences to null and triggering the recomputation path for
diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc
index 415a3dc..d7fb93c 100644
--- a/content/browser/renderer_host/render_view_host_impl.cc
+++ b/content/browser/renderer_host/render_view_host_impl.cc
@@ -85,6 +85,7 @@
 #include "net/base/url_util.h"
 #include "services/network/public/cpp/features.h"
 #include "third_party/blink/public/common/features.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/base/clipboard/clipboard.h"
 #include "ui/base/device_form_factor.h"
@@ -532,9 +533,10 @@
   return GetDelegate()->IsNeverComposited();
 }
 
-WebPreferences RenderViewHostImpl::GetWebkitPreferencesForWidget() {
+blink::web_pref::WebPreferences
+RenderViewHostImpl::GetWebkitPreferencesForWidget() {
   if (!delegate_)
-    return WebPreferences();
+    return blink::web_pref::WebPreferences();
   return delegate_->GetOrCreateWebPreferences();
 }
 
diff --git a/content/browser/renderer_host/render_view_host_impl.h b/content/browser/renderer_host/render_view_host_impl.h
index 3a53906..c5a1410 100644
--- a/content/browser/renderer_host/render_view_host_impl.h
+++ b/content/browser/renderer_host/render_view_host_impl.h
@@ -41,6 +41,12 @@
 #include "ui/gl/gpu_preference.h"
 #include "ui/gl/gpu_switching_observer.h"
 
+namespace blink {
+namespace web_pref {
+struct WebPreferences;
+}
+}  // namespace blink
+
 namespace content {
 
 class TimeoutMonitor;
@@ -299,7 +305,7 @@
   void SetBackgroundOpaque(bool opaque) override;
   bool IsMainFrameActive() override;
   bool IsNeverComposited() override;
-  WebPreferences GetWebkitPreferencesForWidget() override;
+  blink::web_pref::WebPreferences GetWebkitPreferencesForWidget() override;
 
   // IPC message handlers.
   void OnShowView(int route_id,
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index 5105cca..29c6cecd 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -95,7 +95,6 @@
 #include "content/public/common/content_switches.h"
 #include "content/public/common/result_codes.h"
 #include "content/public/common/use_zoom_for_dsf_policy.h"
-#include "content/public/common/web_preferences.h"
 #include "gpu/GLES2/gl2extchromium.h"
 #include "gpu/command_buffer/service/gpu_switches.h"
 #include "gpu/ipc/common/gpu_messages.h"
@@ -108,6 +107,7 @@
 #include "skia/ext/platform_canvas.h"
 #include "storage/browser/file_system/isolated_context.h"
 #include "third_party/blink/public/common/input/synthetic_web_input_event_builders.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "third_party/blink/public/common/widget/visual_properties.h"
 #include "third_party/blink/public/mojom/input/touch_event.mojom.h"
 #include "third_party/blink/public/mojom/native_file_system/native_file_system_drag_drop_token.mojom.h"
diff --git a/content/browser/renderer_host/render_widget_host_owner_delegate.h b/content/browser/renderer_host/render_widget_host_owner_delegate.h
index 3b907a2..d1e4f8f 100644
--- a/content/browser/renderer_host/render_widget_host_owner_delegate.h
+++ b/content/browser/renderer_host/render_widget_host_owner_delegate.h
@@ -10,6 +10,9 @@
 #include "third_party/blink/public/common/widget/visual_properties.h"
 
 namespace blink {
+namespace web_pref {
+struct WebPreferences;
+}
 class WebMouseEvent;
 }
 
@@ -19,7 +22,6 @@
 
 namespace content {
 struct NativeWebKeyboardEvent;
-struct WebPreferences;
 
 //
 // RenderWidgetHostOwnerDelegate
@@ -75,7 +77,7 @@
 
   // Returns the WebkitPreferences for the page. The preferences are shared
   // between all widgets for the page.
-  virtual WebPreferences GetWebkitPreferencesForWidget() = 0;
+  virtual blink::web_pref::WebPreferences GetWebkitPreferencesForWidget() = 0;
 
  protected:
   virtual ~RenderWidgetHostOwnerDelegate() {}
diff --git a/content/browser/service_worker/embedded_worker_instance.cc b/content/browser/service_worker/embedded_worker_instance.cc
index 3fcf618c..e0bf8839 100644
--- a/content/browser/service_worker/embedded_worker_instance.cc
+++ b/content/browser/service_worker/embedded_worker_instance.cc
@@ -1525,7 +1525,7 @@
     RunOrPostTaskOnThread(
         FROM_HERE, BrowserThread::UI,
         base::BindOnce(content::BindCacheStorageOnUIThread, process_id(),
-                       owner_version_->script_origin(), coep,
+                       owner_version_->origin(), coep,
                        std::move(coep_reporter_remote), std::move(receiver)));
   }
   pending_cache_storage_receivers_.clear();
diff --git a/content/browser/service_worker/service_worker_browsertest.cc b/content/browser/service_worker/service_worker_browsertest.cc
index 909fde9..7a8f516 100644
--- a/content/browser/service_worker/service_worker_browsertest.cc
+++ b/content/browser/service_worker/service_worker_browsertest.cc
@@ -61,7 +61,6 @@
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
-#include "content/public/common/web_preferences.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_browser_test.h"
@@ -84,6 +83,7 @@
 #include "third_party/blink/public/common/loader/url_loader_throttle.h"
 #include "third_party/blink/public/common/service_worker/service_worker_status_code.h"
 #include "third_party/blink/public/common/service_worker/service_worker_utils.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "third_party/blink/public/mojom/loader/code_cache.mojom-test-utils.h"
 
 using blink::mojom::CacheStorageError;
@@ -438,7 +438,7 @@
   }
 
   void OverrideWebkitPrefs(RenderViewHost* render_view_host,
-                           WebPreferences* prefs) override {
+                           blink::web_pref::WebPreferences* prefs) override {
     prefs->data_saver_enabled = data_saver_enabled_;
   }
 
diff --git a/content/browser/service_worker/service_worker_container_host.h b/content/browser/service_worker/service_worker_container_host.h
index 3cd1c8d..c8536743 100644
--- a/content/browser/service_worker/service_worker_container_host.h
+++ b/content/browser/service_worker/service_worker_container_host.h
@@ -361,7 +361,7 @@
 
   // The URL representing the first-party site for this context.
   // For service worker execution contexts, top_frame_origin() always
-  // returns the origin of the service worker script URL.
+  // returns the origin of the service worker scope's URL.
   // For shared worker it is the origin of the document that created the worker.
   // For dedicated worker it is the top-frame origin of the document that owns
   // the worker.
diff --git a/content/browser/service_worker/service_worker_context_unittest.cc b/content/browser/service_worker/service_worker_context_unittest.cc
index 2c4d065a..bdb550d 100644
--- a/content/browser/service_worker/service_worker_context_unittest.cc
+++ b/content/browser/service_worker/service_worker_context_unittest.cc
@@ -637,7 +637,7 @@
   EXPECT_EQ(registration_id, notifications_[1].registration_id);
 
   context()->registry()->FindRegistrationForId(
-      registration_id, scope.GetOrigin(),
+      registration_id, url::Origin::Create(scope),
       base::BindOnce(&ExpectRegisteredWorkers,
                      blink::ServiceWorkerStatusCode::kOk,
                      false /* expect_waiting */, true /* expect_active */));
@@ -687,7 +687,7 @@
   EXPECT_EQ(registration_id, notifications_[0].registration_id);
 
   context()->registry()->FindRegistrationForId(
-      registration_id, scope.GetOrigin(),
+      registration_id, url::Origin::Create(scope),
       base::BindOnce(&ExpectRegisteredWorkers,
                      blink::ServiceWorkerStatusCode::kErrorNotFound,
                      false /* expect_waiting */, false /* expect_active */));
@@ -738,7 +738,7 @@
   EXPECT_EQ(registration_id, notifications_[1].registration_id);
 
   context()->registry()->FindRegistrationForId(
-      registration_id, scope.GetOrigin(),
+      registration_id, url::Origin::Create(scope),
       base::BindOnce(&ExpectRegisteredWorkers,
                      blink::ServiceWorkerStatusCode::kOk,
                      false /* expect_waiting */, true /* expect_active */));
@@ -772,7 +772,7 @@
   ASSERT_TRUE(called);
 
   context()->registry()->FindRegistrationForId(
-      registration_id, scope.GetOrigin(),
+      registration_id, url::Origin::Create(scope),
       base::BindOnce(&ExpectRegisteredWorkers,
                      blink::ServiceWorkerStatusCode::kErrorNotFound,
                      false /* expect_waiting */, false /* expect_active */));
@@ -871,23 +871,23 @@
   ASSERT_TRUE(called);
 
   context()->registry()->FindRegistrationForId(
-      registration_id1, origin1_s1.GetOrigin(),
+      registration_id1, url::Origin::Create(origin1_s1),
       base::BindOnce(&ExpectRegisteredWorkers,
                      blink::ServiceWorkerStatusCode::kErrorNotFound,
                      false /* expect_waiting */, false /* expect_active */));
   context()->registry()->FindRegistrationForId(
-      registration_id2, origin1_s2.GetOrigin(),
+      registration_id2, url::Origin::Create(origin1_s2),
       base::BindOnce(&ExpectRegisteredWorkers,
                      blink::ServiceWorkerStatusCode::kErrorNotFound,
                      false /* expect_waiting */, false /* expect_active */));
   context()->registry()->FindRegistrationForId(
-      registration_id3, origin2_s1.GetOrigin(),
+      registration_id3, url::Origin::Create(origin2_s1),
       base::BindOnce(&ExpectRegisteredWorkers,
                      blink::ServiceWorkerStatusCode::kOk,
                      false /* expect_waiting */, true /* expect_active */));
 
   context()->registry()->FindRegistrationForId(
-      registration_id4, origin3_s1.GetOrigin(),
+      registration_id4, url::Origin::Create(origin3_s1),
       base::BindOnce(&ExpectRegisteredWorkers,
                      blink::ServiceWorkerStatusCode::kOk,
                      false /* expect_waiting */, true /* expect_active */));
@@ -1144,7 +1144,7 @@
   EXPECT_TRUE(called);
 
   context()->registry()->FindRegistrationForId(
-      registration_id, scope.GetOrigin(),
+      registration_id, url::Origin::Create(scope),
       base::BindOnce(&ExpectRegisteredWorkers,
                      blink::ServiceWorkerStatusCode::kOk,
                      false /* expect_waiting */, true /* expect_active */));
@@ -1155,7 +1155,7 @@
   // The storage is disabled while the recovery process is running, so the
   // operation should be aborted.
   context()->registry()->FindRegistrationForId(
-      registration_id, scope.GetOrigin(),
+      registration_id, url::Origin::Create(scope),
       base::BindOnce(&ExpectRegisteredWorkers,
                      blink::ServiceWorkerStatusCode::kErrorAbort,
                      false /* expect_waiting */, true /* expect_active */));
@@ -1164,7 +1164,7 @@
   // The context started over and the storage was re-initialized, so the
   // registration should not be found.
   context()->registry()->FindRegistrationForId(
-      registration_id, scope.GetOrigin(),
+      registration_id, url::Origin::Create(scope),
       base::BindOnce(&ExpectRegisteredWorkers,
                      blink::ServiceWorkerStatusCode::kErrorNotFound,
                      false /* expect_waiting */, true /* expect_active */));
@@ -1180,7 +1180,7 @@
   EXPECT_TRUE(called);
 
   context()->registry()->FindRegistrationForId(
-      registration_id, scope.GetOrigin(),
+      registration_id, url::Origin::Create(scope),
       base::BindOnce(&ExpectRegisteredWorkers,
                      blink::ServiceWorkerStatusCode::kOk,
                      false /* expect_waiting */, true /* expect_active */));
diff --git a/content/browser/service_worker/service_worker_context_wrapper.cc b/content/browser/service_worker/service_worker_context_wrapper.cc
index d5d7619e..f63a930 100644
--- a/content/browser/service_worker/service_worker_context_wrapper.cc
+++ b/content/browser/service_worker/service_worker_context_wrapper.cc
@@ -1070,7 +1070,7 @@
 
 void ServiceWorkerContextWrapper::FindReadyRegistrationForId(
     int64_t registration_id,
-    const GURL& origin,
+    const url::Origin& origin,
     FindRegistrationCallback callback) {
   RunOrPostTaskOnCoreThread(
       FROM_HERE,
@@ -1082,7 +1082,7 @@
 
 void ServiceWorkerContextWrapper::FindReadyRegistrationForIdOnCoreThread(
     int64_t registration_id,
-    const GURL& origin,
+    const url::Origin& origin,
     FindRegistrationCallback callback,
     scoped_refptr<base::TaskRunner> callback_runner) {
   DCHECK_CURRENTLY_ON(GetCoreThreadId());
@@ -1094,7 +1094,7 @@
     return;
   }
   context_core_->registry()->FindRegistrationForId(
-      registration_id, origin.GetOrigin(),
+      registration_id, origin,
       base::BindOnce(
           &ServiceWorkerContextWrapper::DidFindRegistrationForFindReady, this,
           std::move(callback), std::move(callback_runner)));
@@ -1317,7 +1317,7 @@
 
 void ServiceWorkerContextWrapper::StoreRegistrationUserData(
     int64_t registration_id,
-    const GURL& origin,
+    const url::Origin& origin,
     const std::vector<std::pair<std::string, std::string>>& key_value_pairs,
     StatusCallback callback) {
   RunOrPostTaskOnCoreThread(
@@ -1337,7 +1337,7 @@
 
 void ServiceWorkerContextWrapper::StoreRegistrationUserDataOnCoreThread(
     int64_t registration_id,
-    const GURL& origin,
+    const url::Origin& origin,
     const std::vector<std::pair<std::string, std::string>>& key_value_pairs,
     StatusCallback callback) {
   DCHECK_CURRENTLY_ON(GetCoreThreadId());
@@ -1345,9 +1345,8 @@
     std::move(callback).Run(blink::ServiceWorkerStatusCode::kErrorAbort);
     return;
   }
-  context_core_->registry()->StoreUserData(registration_id, origin.GetOrigin(),
-                                           key_value_pairs,
-                                           std::move(callback));
+  context_core_->registry()->StoreUserData(
+      registration_id, origin, key_value_pairs, std::move(callback));
 }
 
 void ServiceWorkerContextWrapper::ClearRegistrationUserData(
diff --git a/content/browser/service_worker/service_worker_context_wrapper.h b/content/browser/service_worker/service_worker_context_wrapper.h
index f29bab9..1fb22fda 100644
--- a/content/browser/service_worker/service_worker_context_wrapper.h
+++ b/content/browser/service_worker/service_worker_context_wrapper.h
@@ -263,7 +263,7 @@
   //
   // Can be called from any thread, and the callback is called on that thread.
   void FindReadyRegistrationForId(int64_t registration_id,
-                                  const GURL& origin,
+                                  const url::Origin& origin,
                                   FindRegistrationCallback callback);
 
   // Returns the registration for |registration_id|. It is guaranteed that the
@@ -299,7 +299,7 @@
       GetUserKeysAndDataCallback callback);
   void StoreRegistrationUserData(
       int64_t registration_id,
-      const GURL& origin,
+      const url::Origin& origin,
       const std::vector<std::pair<std::string, std::string>>& key_value_pairs,
       StatusCallback callback);
   void ClearRegistrationUserData(int64_t registration_id,
@@ -516,7 +516,7 @@
       scoped_refptr<base::TaskRunner> callback_runner);
   void FindReadyRegistrationForIdOnCoreThread(
       int64_t registration_id,
-      const GURL& origin,
+      const url::Origin& origin,
       FindRegistrationCallback callback,
       scoped_refptr<base::TaskRunner> callback_runner);
   void FindReadyRegistrationForIdOnlyOnCoreThread(
@@ -554,7 +554,7 @@
       GetUserKeysAndDataCallback callback);
   void StoreRegistrationUserDataOnCoreThread(
       int64_t registration_id,
-      const GURL& origin,
+      const url::Origin& origin,
       const std::vector<std::pair<std::string, std::string>>& key_value_pairs,
       StatusCallback callback);
   void ClearRegistrationUserDataOnCoreThread(
diff --git a/content/browser/service_worker/service_worker_database.cc b/content/browser/service_worker/service_worker_database.cc
index 01a2eaf..5a72ac7 100644
--- a/content/browser/service_worker/service_worker_database.cc
+++ b/content/browser/service_worker/service_worker_database.cc
@@ -335,7 +335,8 @@
 }
 
 ServiceWorkerDatabase::Status
-ServiceWorkerDatabase::GetOriginsWithRegistrations(std::set<GURL>* origins) {
+ServiceWorkerDatabase::GetOriginsWithRegistrations(
+    std::set<url::Origin>* origins) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(origins->empty());
 
@@ -369,7 +370,7 @@
         break;
       }
 
-      origins->insert(origin);
+      origins->insert(url::Origin::Create(origin));
     }
   }
 
@@ -983,7 +984,7 @@
 
 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteUserData(
     int64_t registration_id,
-    const GURL& origin,
+    const url::Origin& origin,
     const std::vector<storage::mojom::ServiceWorkerUserDataPtr>& user_data) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, registration_id);
@@ -997,8 +998,7 @@
 
   // There should be the registration specified by |registration_id|.
   storage::mojom::ServiceWorkerRegistrationDataPtr registration;
-  status = ReadRegistrationData(registration_id, url::Origin::Create(origin),
-                                &registration);
+  status = ReadRegistrationData(registration_id, origin, &registration);
   if (status != Status::kOk)
     return status;
 
diff --git a/content/browser/service_worker/service_worker_database.h b/content/browser/service_worker/service_worker_database.h
index 49f9cd9..10120ba4 100644
--- a/content/browser/service_worker/service_worker_database.h
+++ b/content/browser/service_worker/service_worker_database.h
@@ -85,7 +85,7 @@
   // Reads origins that have one or more than one registration from the
   // database. Returns OK if they are successfully read or not found.
   // Otherwise, returns an error.
-  Status GetOriginsWithRegistrations(std::set<GURL>* origins);
+  Status GetOriginsWithRegistrations(std::set<url::Origin>* origins);
 
   // Reads registrations for |origin| from the database. Returns OK if they are
   // successfully read or not found. Otherwise, returns an error.
@@ -194,7 +194,7 @@
   // registration specified by |registration_id| does not exist in the database.
   Status WriteUserData(
       int64_t registration_id,
-      const GURL& origin,
+      const url::Origin& origin,
       const std::vector<storage::mojom::ServiceWorkerUserDataPtr>& user_data);
 
   // Deletes user data for |registration_id| and |user_data_names| from the
diff --git a/content/browser/service_worker/service_worker_database_unittest.cc b/content/browser/service_worker/service_worker_database_unittest.cc
index 0fa08fe..4aea30eb 100644
--- a/content/browser/service_worker/service_worker_database_unittest.cc
+++ b/content/browser/service_worker/service_worker_database_unittest.cc
@@ -352,18 +352,18 @@
 TEST(ServiceWorkerDatabaseTest, GetOriginsWithRegistrations) {
   std::unique_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory());
 
-  std::set<GURL> origins;
+  std::set<url::Origin> origins;
   EXPECT_EQ(ServiceWorkerDatabase::Status::kOk,
             database->GetOriginsWithRegistrations(&origins));
   EXPECT_TRUE(origins.empty());
 
   ServiceWorkerDatabase::DeletedVersion deleted_version;
 
-  GURL origin1("https://example.com");
+  url::Origin origin1 = url::Origin::Create(GURL("https://example.com"));
   RegistrationData data1;
   data1.registration_id = 123;
-  data1.scope = URL(origin1, "/foo");
-  data1.script = URL(origin1, "/script1.js");
+  data1.scope = URL(origin1.GetURL(), "/foo");
+  data1.script = URL(origin1.GetURL(), "/script1.js");
   data1.version_id = 456;
   data1.resources_total_size_bytes = 100;
   std::vector<ResourceRecordPtr> resources1;
@@ -371,11 +371,11 @@
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
             database->WriteRegistration(data1, resources1, &deleted_version));
 
-  GURL origin2("https://www.example.com");
+  url::Origin origin2 = url::Origin::Create(GURL("https://www.example.com"));
   RegistrationData data2;
   data2.registration_id = 234;
-  data2.scope = URL(origin2, "/bar");
-  data2.script = URL(origin2, "/script2.js");
+  data2.scope = URL(origin2.GetURL(), "/bar");
+  data2.script = URL(origin2.GetURL(), "/script2.js");
   data2.version_id = 567;
   data2.resources_total_size_bytes = 200;
   std::vector<ResourceRecordPtr> resources2;
@@ -383,11 +383,11 @@
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
             database->WriteRegistration(data2, resources2, &deleted_version));
 
-  GURL origin3("https://example.org");
+  url::Origin origin3 = url::Origin::Create(GURL("https://example.org"));
   RegistrationData data3;
   data3.registration_id = 345;
-  data3.scope = URL(origin3, "/hoge");
-  data3.script = URL(origin3, "/script3.js");
+  data3.scope = URL(origin3.GetURL(), "/hoge");
+  data3.script = URL(origin3.GetURL(), "/script3.js");
   data3.version_id = 678;
   data3.resources_total_size_bytes = 300;
   std::vector<ResourceRecordPtr> resources3;
@@ -398,8 +398,8 @@
   // |origin3| has two registrations.
   RegistrationData data4;
   data4.registration_id = 456;
-  data4.scope = URL(origin3, "/fuga");
-  data4.script = URL(origin3, "/script4.js");
+  data4.scope = URL(origin3.GetURL(), "/fuga");
+  data4.script = URL(origin3.GetURL(), "/script4.js");
   data4.version_id = 789;
   data4.resources_total_size_bytes = 400;
   std::vector<ResourceRecordPtr> resources4;
@@ -418,8 +418,8 @@
   // |origin3| has another registration, so should not remove it from the
   // unique origin list.
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
-            database->DeleteRegistration(data4.registration_id, origin3,
-                                         &deleted_version));
+            database->DeleteRegistration(data4.registration_id,
+                                         origin3.GetURL(), &deleted_version));
   EXPECT_EQ(data4.registration_id, deleted_version.registration_id);
 
   origins.clear();
@@ -432,8 +432,8 @@
 
   // |origin3| should be removed from the unique origin list.
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
-            database->DeleteRegistration(data3.registration_id, origin3,
-                                         &deleted_version));
+            database->DeleteRegistration(data3.registration_id,
+                                         origin3.GetURL(), &deleted_version));
   EXPECT_EQ(data3.registration_id, deleted_version.registration_id);
 
   origins.clear();
@@ -1066,13 +1066,13 @@
 
 TEST(ServiceWorkerDatabaseTest, UserData_Basic) {
   std::unique_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory());
-  const GURL kOrigin("https://example.com");
+  const url::Origin kOrigin = url::Origin::Create(GURL("https://example.com"));
 
   // Add a registration.
   RegistrationData data;
   data.registration_id = 100;
-  data.scope = URL(kOrigin, "/foo");
-  data.script = URL(kOrigin, "/script.js");
+  data.scope = URL(kOrigin.GetURL(), "/foo");
+  data.script = URL(kOrigin.GetURL(), "/script.js");
   data.version_id = 200;
   data.resources_total_size_bytes = 100;
   std::vector<ResourceRecordPtr> resources;
@@ -1186,13 +1186,13 @@
 TEST(ServiceWorkerDatabaseTest,
      UserData_ReadUserDataForAllRegistrationsByKeyPrefix) {
   std::unique_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory());
-  const GURL kOrigin("https://example.com");
+  const url::Origin kOrigin = url::Origin::Create(GURL("https://example.com"));
 
   // Add registration 1.
   RegistrationData data1;
   data1.registration_id = 100;
-  data1.scope = URL(kOrigin, "/foo");
-  data1.script = URL(kOrigin, "/script1.js");
+  data1.scope = URL(kOrigin.GetURL(), "/foo");
+  data1.script = URL(kOrigin.GetURL(), "/script1.js");
   data1.version_id = 200;
   data1.resources_total_size_bytes = 100;
   std::vector<ResourceRecordPtr> resources1;
@@ -1201,8 +1201,8 @@
   // Add registration 2.
   RegistrationData data2;
   data2.registration_id = 101;
-  data2.scope = URL(kOrigin, "/bar");
-  data2.script = URL(kOrigin, "/script2.js");
+  data2.scope = URL(kOrigin.GetURL(), "/bar");
+  data2.script = URL(kOrigin.GetURL(), "/script2.js");
   data2.version_id = 201;
   data2.resources_total_size_bytes = 200;
   std::vector<ResourceRecordPtr> resources2;
@@ -1279,13 +1279,13 @@
 
 TEST(ServiceWorkerDatabaseTest, ReadUserDataByKeyPrefix) {
   std::unique_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory());
-  const GURL kOrigin("https://example.com");
+  const url::Origin kOrigin = url::Origin::Create(GURL("https://example.com"));
 
   // Add a registration.
   RegistrationData data;
   data.registration_id = 100;
-  data.scope = URL(kOrigin, "/foo");
-  data.script = URL(kOrigin, "/script.js");
+  data.scope = URL(kOrigin.GetURL(), "/foo");
+  data.script = URL(kOrigin.GetURL(), "/script.js");
   data.version_id = 200;
   data.resources_total_size_bytes = 100;
   std::vector<ResourceRecordPtr> resources;
@@ -1325,13 +1325,13 @@
 
 TEST(ServiceWorkerDatabaseTest, ReadUserKeysAndDataByKeyPrefix) {
   std::unique_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory());
-  const GURL kOrigin("https://example.com");
+  const url::Origin kOrigin = url::Origin::Create(GURL("https://example.com"));
 
   // Add a registration.
   RegistrationData data;
   data.registration_id = 100;
-  data.scope = URL(kOrigin, "/foo");
-  data.script = URL(kOrigin, "/script.js");
+  data.scope = URL(kOrigin.GetURL(), "/foo");
+  data.script = URL(kOrigin.GetURL(), "/script.js");
   data.version_id = 200;
   data.resources_total_size_bytes = 100;
   std::vector<ResourceRecordPtr> resources;
@@ -1375,13 +1375,13 @@
 
 TEST(ServiceWorkerDatabaseTest, UserData_DeleteUserDataByKeyPrefixes) {
   std::unique_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory());
-  const GURL kOrigin("https://example.com");
+  const url::Origin kOrigin = url::Origin::Create(GURL("https://example.com"));
 
   // Add registration 1.
   RegistrationData data1;
   data1.registration_id = 100;
-  data1.scope = URL(kOrigin, "/foo");
-  data1.script = URL(kOrigin, "/script1.js");
+  data1.scope = URL(kOrigin.GetURL(), "/foo");
+  data1.script = URL(kOrigin.GetURL(), "/script1.js");
   data1.version_id = 200;
   data1.resources_total_size_bytes = 100;
   std::vector<ResourceRecordPtr> resources1;
@@ -1390,8 +1390,8 @@
   // Add registration 2.
   RegistrationData data2;
   data2.registration_id = 101;
-  data2.scope = URL(kOrigin, "/bar");
-  data2.script = URL(kOrigin, "/script2.js");
+  data2.scope = URL(kOrigin.GetURL(), "/bar");
+  data2.script = URL(kOrigin.GetURL(), "/script2.js");
   data2.version_id = 201;
   data2.resources_total_size_bytes = 200;
   std::vector<ResourceRecordPtr> resources2;
@@ -1483,13 +1483,13 @@
 TEST(ServiceWorkerDatabaseTest,
      UserData_DeleteUserDataForAllRegistrationsByKeyPrefix) {
   std::unique_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory());
-  const GURL kOrigin("https://example.com");
+  const url::Origin kOrigin = url::Origin::Create(GURL("https://example.com"));
 
   // Add registration 1.
   RegistrationData data1;
   data1.registration_id = 100;
-  data1.scope = URL(kOrigin, "/foo");
-  data1.script = URL(kOrigin, "/script1.js");
+  data1.scope = URL(kOrigin.GetURL(), "/foo");
+  data1.script = URL(kOrigin.GetURL(), "/script1.js");
   data1.version_id = 200;
   data1.resources_total_size_bytes = 100;
   std::vector<ResourceRecordPtr> resources1;
@@ -1498,8 +1498,8 @@
   // Add registration 2.
   RegistrationData data2;
   data2.registration_id = 101;
-  data2.scope = URL(kOrigin, "/bar");
-  data2.script = URL(kOrigin, "/script2.js");
+  data2.scope = URL(kOrigin.GetURL(), "/bar");
+  data2.script = URL(kOrigin.GetURL(), "/script2.js");
   data2.version_id = 201;
   data2.resources_total_size_bytes = 200;
   std::vector<ResourceRecordPtr> resources2;
@@ -1570,13 +1570,13 @@
 
 TEST(ServiceWorkerDatabaseTest, UserData_DataIsolation) {
   std::unique_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory());
-  const GURL kOrigin("https://example.com");
+  const url::Origin kOrigin = url::Origin::Create(GURL("https://example.com"));
 
   // Add registration 1.
   RegistrationData data1;
   data1.registration_id = 100;
-  data1.scope = URL(kOrigin, "/foo");
-  data1.script = URL(kOrigin, "/script1.js");
+  data1.scope = URL(kOrigin.GetURL(), "/foo");
+  data1.script = URL(kOrigin.GetURL(), "/script1.js");
   data1.version_id = 200;
   data1.resources_total_size_bytes = 100;
   std::vector<ResourceRecordPtr> resources1;
@@ -1585,8 +1585,8 @@
   // Add registration 2.
   RegistrationData data2;
   data2.registration_id = 101;
-  data2.scope = URL(kOrigin, "/bar");
-  data2.script = URL(kOrigin, "/script2.js");
+  data2.scope = URL(kOrigin.GetURL(), "/bar");
+  data2.script = URL(kOrigin.GetURL(), "/script2.js");
   data2.version_id = 201;
   data2.resources_total_size_bytes = 200;
   data2.update_via_cache = blink::mojom::ServiceWorkerUpdateViaCache::kImports;
@@ -1668,13 +1668,13 @@
 
 TEST(ServiceWorkerDatabaseTest, UserData_DeleteRegistration) {
   std::unique_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory());
-  const GURL kOrigin("https://example.com");
+  const url::Origin kOrigin = url::Origin::Create(GURL("https://example.com"));
 
   // Add registration 1.
   RegistrationData data1;
   data1.registration_id = 100;
-  data1.scope = URL(kOrigin, "/foo");
-  data1.script = URL(kOrigin, "/script1.js");
+  data1.scope = URL(kOrigin.GetURL(), "/foo");
+  data1.script = URL(kOrigin.GetURL(), "/script1.js");
   data1.version_id = 200;
   data1.resources_total_size_bytes = 100;
   std::vector<ResourceRecordPtr> resources1;
@@ -1683,8 +1683,8 @@
   // Add registration 2.
   RegistrationData data2;
   data2.registration_id = 101;
-  data2.scope = URL(kOrigin, "/bar");
-  data2.script = URL(kOrigin, "/script2.js");
+  data2.scope = URL(kOrigin.GetURL(), "/bar");
+  data2.script = URL(kOrigin.GetURL(), "/script2.js");
   data2.version_id = 201;
   data2.resources_total_size_bytes = 200;
   std::vector<ResourceRecordPtr> resources2;
@@ -1731,8 +1731,8 @@
   // Delete all data associated with the registration1. This shouldn't delete
   // the data associated with registration2.
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
-            database->DeleteRegistration(data1.registration_id, kOrigin,
-                                         &deleted_version));
+            database->DeleteRegistration(data1.registration_id,
+                                         kOrigin.GetURL(), &deleted_version));
   EXPECT_EQ(
       ServiceWorkerDatabase::Status::kErrorNotFound,
       database->ReadUserData(data1.registration_id, {"key1"}, &user_data_out));
@@ -1748,7 +1748,7 @@
 
 TEST(ServiceWorkerDatabaseTest, UserData_UninitializedDatabase) {
   std::unique_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory());
-  const GURL kOrigin("https://example.com");
+  const url::Origin kOrigin = url::Origin::Create(GURL("https://example.com"));
 
   // Should be failed because the database does not exist.
   std::vector<std::string> user_data_out;
@@ -1950,20 +1950,22 @@
   ServiceWorkerDatabase::DeletedVersion deleted_version;
 
   // Data associated with |origin1| will be removed.
-  GURL origin1("https://example.com");
-  GURL origin2("https://example.org");
+  GURL url1("https://example.com");
+  GURL url2("https://example.org");
+  url::Origin origin1 = url::Origin::Create(url1);
+  url::Origin origin2 = url::Origin::Create(url2);
 
   // |origin1| has two registrations (registration1 and registration2).
   RegistrationData data1;
   data1.registration_id = 10;
-  data1.scope = URL(origin1, "/foo");
-  data1.script = URL(origin1, "/resource1");
+  data1.scope = URL(url1, "/foo");
+  data1.script = URL(url1, "/resource1");
   data1.version_id = 100;
   data1.resources_total_size_bytes = 2013 + 512;
 
   std::vector<ResourceRecordPtr> resources1;
-  resources1.push_back(CreateResource(1, URL(origin1, "/resource1"), 2013));
-  resources1.push_back(CreateResource(2, URL(origin1, "/resource2"), 512));
+  resources1.push_back(CreateResource(1, URL(url1, "/resource1"), 2013));
+  resources1.push_back(CreateResource(2, URL(url1, "/resource2"), 512));
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
             database->WriteRegistration(data1, resources1, &deleted_version));
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
@@ -1977,14 +1979,14 @@
 
   RegistrationData data2;
   data2.registration_id = 11;
-  data2.scope = URL(origin1, "/bar");
-  data2.script = URL(origin1, "/resource3");
+  data2.scope = URL(url1, "/bar");
+  data2.script = URL(url1, "/resource3");
   data2.version_id = 101;
   data2.resources_total_size_bytes = 4 + 5;
 
   std::vector<ResourceRecordPtr> resources2;
-  resources2.push_back(CreateResource(3, URL(origin1, "/resource3"), 4));
-  resources2.push_back(CreateResource(4, URL(origin1, "/resource4"), 5));
+  resources2.push_back(CreateResource(3, URL(url1, "/resource3"), 4));
+  resources2.push_back(CreateResource(4, URL(url1, "/resource4"), 5));
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
             database->WriteRegistration(data2, resources2, &deleted_version));
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
@@ -1999,14 +2001,14 @@
   // |origin2| has one registration (registration3).
   RegistrationData data3;
   data3.registration_id = 12;
-  data3.scope = URL(origin2, "/hoge");
-  data3.script = URL(origin2, "/resource5");
+  data3.scope = URL(url2, "/hoge");
+  data3.script = URL(url2, "/resource5");
   data3.version_id = 102;
   data3.resources_total_size_bytes = 6 + 7;
 
   std::vector<ResourceRecordPtr> resources3;
-  resources3.push_back(CreateResource(5, URL(origin2, "/resource5"), 6));
-  resources3.push_back(CreateResource(6, URL(origin2, "/resource6"), 7));
+  resources3.push_back(CreateResource(5, URL(url2, "/resource5"), 6));
+  resources3.push_back(CreateResource(6, URL(url2, "/resource6"), 7));
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
             database->WriteRegistration(data3, resources3, &deleted_version));
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
@@ -2020,13 +2022,13 @@
 
   std::set<GURL> origins_to_delete;
   std::vector<int64_t> newly_purgeable_resources;
-  origins_to_delete.insert(origin1);
+  origins_to_delete.insert(url1);
   EXPECT_EQ(ServiceWorkerDatabase::Status::kOk,
             database->DeleteAllDataForOrigins(origins_to_delete,
                                               &newly_purgeable_resources));
 
   // |origin1| should be removed from the unique origin list.
-  std::set<GURL> unique_origins;
+  std::set<url::Origin> unique_origins;
   EXPECT_EQ(ServiceWorkerDatabase::Status::kOk,
             database->GetOriginsWithRegistrations(&unique_origins));
   EXPECT_EQ(1u, unique_origins.size());
@@ -2034,9 +2036,9 @@
 
   // The registrations for |origin1| should be removed.
   std::vector<storage::mojom::ServiceWorkerRegistrationDataPtr> registrations;
-  EXPECT_EQ(ServiceWorkerDatabase::Status::kOk,
-            database->GetRegistrationsForOrigin(url::Origin::Create(origin1),
-                                                &registrations, nullptr));
+  EXPECT_EQ(
+      ServiceWorkerDatabase::Status::kOk,
+      database->GetRegistrationsForOrigin(origin1, &registrations, nullptr));
   EXPECT_TRUE(registrations.empty());
   GURL origin_out;
   EXPECT_EQ(
@@ -2047,14 +2049,14 @@
   RegistrationDataPtr data_out;
   std::vector<ResourceRecordPtr> resources_out;
   EXPECT_EQ(ServiceWorkerDatabase::Status::kOk,
-            database->ReadRegistration(data3.registration_id, origin2,
-                                       &data_out, &resources_out));
+            database->ReadRegistration(data3.registration_id, url2, &data_out,
+                                       &resources_out));
   VerifyRegistrationData(data3, *data_out);
   VerifyResourceRecords(resources3, resources_out);
   EXPECT_EQ(
       ServiceWorkerDatabase::Status::kOk,
       database->ReadRegistrationOrigin(data3.registration_id, &origin_out));
-  EXPECT_EQ(origin2, origin_out);
+  EXPECT_EQ(url2, origin_out);
 
   // The resources associated with |origin1| should be purgeable.
   std::vector<int64_t> purgeable_ids_out;
diff --git a/content/browser/service_worker/service_worker_host.cc b/content/browser/service_worker/service_worker_host.cc
index 1882734..49091676 100644
--- a/content/browser/service_worker/service_worker_host.cc
+++ b/content/browser/service_worker/service_worker_host.cc
@@ -62,8 +62,7 @@
   container_host_->set_service_worker_host(this);
   container_host_->UpdateUrls(
       version_->script_url(),
-      net::SiteForCookies::FromUrl(version_->script_url()),
-      version_->script_origin());
+      net::SiteForCookies::FromUrl(version_->script_url()), version_->origin());
 }
 
 ServiceWorkerHost::~ServiceWorkerHost() {
@@ -96,7 +95,7 @@
   RunOrPostTaskOnThread(
       FROM_HERE, BrowserThread::UI,
       base::BindOnce(&CreateQuicTransportConnectorImpl, worker_process_id_,
-                     version_->script_origin(), std::move(receiver)));
+                     version_->origin(), std::move(receiver)));
 }
 
 void ServiceWorkerHost::BindCacheStorage(
diff --git a/content/browser/service_worker/service_worker_info.cc b/content/browser/service_worker/service_worker_info.cc
index 4289aa5..22e1f627 100644
--- a/content/browser/service_worker/service_worker_info.cc
+++ b/content/browser/service_worker/service_worker_info.cc
@@ -29,7 +29,7 @@
     ServiceWorkerVersion::Status status,
     ServiceWorkerVersion::FetchHandlerExistence fetch_handler_existence,
     const GURL& script_url,
-    const url::Origin& script_origin,
+    const url::Origin& origin,
     int64_t registration_id,
     int64_t version_id,
     int process_id,
@@ -39,7 +39,7 @@
       status(status),
       fetch_handler_existence(fetch_handler_existence),
       script_url(script_url),
-      script_origin(script_origin),
+      origin(origin),
       registration_id(registration_id),
       version_id(version_id),
       process_id(process_id),
diff --git a/content/browser/service_worker/service_worker_info.h b/content/browser/service_worker/service_worker_info.h
index 78af740..e4746be 100644
--- a/content/browser/service_worker/service_worker_info.h
+++ b/content/browser/service_worker/service_worker_info.h
@@ -45,7 +45,7 @@
   ServiceWorkerVersion::FetchHandlerExistence fetch_handler_existence;
   blink::mojom::NavigationPreloadState navigation_preload_state;
   GURL script_url;
-  url::Origin script_origin;
+  url::Origin origin;
   int64_t registration_id;
   int64_t version_id;
   int process_id;
diff --git a/content/browser/service_worker/service_worker_registration.cc b/content/browser/service_worker/service_worker_registration.cc
index 5375e02..658c45c 100644
--- a/content/browser/service_worker/service_worker_registration.cc
+++ b/content/browser/service_worker/service_worker_registration.cc
@@ -46,6 +46,9 @@
     int64_t registration_id,
     base::WeakPtr<ServiceWorkerContextCore> context)
     : scope_(options.scope),
+      // Safe to convert GURL to Origin because service workers are restricted
+      // to secure contexts.
+      origin_(url::Origin::Create(options.scope)),
       update_via_cache_(options.update_via_cache),
       registration_id_(registration_id),
       status_(Status::kIntact),
diff --git a/content/browser/service_worker/service_worker_registration.h b/content/browser/service_worker/service_worker_registration.h
index c59f1fe3..6201927 100644
--- a/content/browser/service_worker/service_worker_registration.h
+++ b/content/browser/service_worker/service_worker_registration.h
@@ -76,6 +76,7 @@
 
   int64_t id() const { return registration_id_; }
   const GURL& scope() const { return scope_; }
+  const url::Origin& origin() const { return origin_; }
   blink::mojom::ServiceWorkerUpdateViaCache update_via_cache() const {
     return update_via_cache_;
   }
@@ -255,6 +256,7 @@
   };
 
   const GURL scope_;
+  const url::Origin origin_;
   blink::mojom::ServiceWorkerUpdateViaCache update_via_cache_;
   const int64_t registration_id_;
   Status status_;
diff --git a/content/browser/service_worker/service_worker_registration_unittest.cc b/content/browser/service_worker/service_worker_registration_unittest.cc
index 0d0c662d..69c54c1 100644
--- a/content/browser/service_worker/service_worker_registration_unittest.cc
+++ b/content/browser/service_worker/service_worker_registration_unittest.cc
@@ -865,7 +865,7 @@
       const GURL& scope) {
     base::Optional<blink::ServiceWorkerStatusCode> status;
     registry()->FindRegistrationForId(
-        registration_id, scope,
+        registration_id, url::Origin::Create(scope),
         base::AdaptCallbackForRepeating(base::BindOnce(
             [](base::Optional<blink::ServiceWorkerStatusCode>* out_status,
                blink::ServiceWorkerStatusCode status,
diff --git a/content/browser/service_worker/service_worker_registry.cc b/content/browser/service_worker/service_worker_registry.cc
index 1addc3b..88ba014 100644
--- a/content/browser/service_worker/service_worker_registry.cc
+++ b/content/browser/service_worker/service_worker_registry.cc
@@ -217,7 +217,7 @@
 
 void ServiceWorkerRegistry::FindRegistrationForId(
     int64_t registration_id,
-    const GURL& origin,
+    const url::Origin& origin,
     FindRegistrationCallback callback) {
   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
   // Registration lookup is expected to abort when storage is disabled.
@@ -594,7 +594,7 @@
 
 void ServiceWorkerRegistry::StoreUserData(
     int64_t registration_id,
-    const GURL& origin,
+    const url::Origin& origin,
     const std::vector<std::pair<std::string, std::string>>& key_value_pairs,
     StatusCallback callback) {
   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
diff --git a/content/browser/service_worker/service_worker_registry.h b/content/browser/service_worker/service_worker_registry.h
index f2098e7..51244da 100644
--- a/content/browser/service_worker/service_worker_registry.h
+++ b/content/browser/service_worker/service_worker_registry.h
@@ -123,7 +123,7 @@
   // is considered as "findable" when the registration is stored or in the
   // installing state.
   void FindRegistrationForId(int64_t registration_id,
-                             const GURL& origin,
+                             const url::Origin& origin,
                              FindRegistrationCallback callback);
   // Generally |FindRegistrationForId| should be used to look up a registration
   // by |registration_id| since it's more efficient. But if a |registration_id|
@@ -213,7 +213,7 @@
                                      GetUserKeysAndDataCallback callback);
   void StoreUserData(
       int64_t registration_id,
-      const GURL& origin,
+      const url::Origin& origin,
       const std::vector<std::pair<std::string, std::string>>& key_value_pairs,
       StatusCallback callback);
   void ClearUserData(int64_t registration_id,
diff --git a/content/browser/service_worker/service_worker_storage.cc b/content/browser/service_worker/service_worker_storage.cc
index da3b984..fd8e49f 100644
--- a/content/browser/service_worker/service_worker_storage.cc
+++ b/content/browser/service_worker/service_worker_storage.cc
@@ -136,7 +136,7 @@
 
   std::vector<url::Origin> origins;
   for (const auto& origin : registered_origins_)
-    origins.push_back(url::Origin::Create(origin));
+    origins.push_back(origin);
   std::move(callback).Run(std::move(origins));
 }
 
@@ -165,7 +165,7 @@
   }
 
   // Bypass database lookup when there is no stored registration.
-  if (!base::Contains(registered_origins_, client_url.GetOrigin())) {
+  if (!base::Contains(registered_origins_, url::Origin::Create(client_url))) {
     std::move(callback).Run(
         /*data=*/nullptr, /*resources=*/nullptr,
         ServiceWorkerDatabase::Status::kErrorNotFound);
@@ -199,7 +199,7 @@
   }
 
   // Bypass database lookup when there is no stored registration.
-  if (!base::Contains(registered_origins_, scope.GetOrigin())) {
+  if (!base::Contains(registered_origins_, url::Origin::Create(scope))) {
     RunSoon(FROM_HERE,
             base::BindOnce(std::move(callback),
                            /*data=*/nullptr, /*resources=*/nullptr,
@@ -215,7 +215,7 @@
 
 void ServiceWorkerStorage::FindRegistrationForId(
     int64_t registration_id,
-    const GURL& origin,
+    const url::Origin& origin,
     FindRegistrationDataCallback callback) {
   switch (state_) {
     case STORAGE_STATE_DISABLED:
@@ -596,7 +596,7 @@
 
 void ServiceWorkerStorage::StoreUserData(
     int64_t registration_id,
-    const GURL& origin,
+    const url::Origin& origin,
     std::vector<storage::mojom::ServiceWorkerUserDataPtr> user_data,
     DatabaseStatusCallback callback) {
   switch (state_) {
@@ -1124,7 +1124,7 @@
                             deleted_version.newly_purgeable_resources);
     return;
   }
-  registered_origins_.insert(origin);
+  registered_origins_.insert(url::Origin::Create(origin));
 
   if (quota_manager_proxy_) {
     // Can be nullptr in tests.
@@ -1170,7 +1170,7 @@
   }
 
   if (origin_state == OriginState::kDelete)
-    registered_origins_.erase(params->origin);
+    registered_origins_.erase(url::Origin::Create(params->origin));
 
   std::move(params->callback)
       .Run(ServiceWorkerDatabase::Status::kOk, origin_state,
@@ -1197,9 +1197,9 @@
 
 void ServiceWorkerStorage::DidStoreUserData(
     DatabaseStatusCallback callback,
-    const GURL& origin,
+    const url::Origin& origin,
     ServiceWorkerDatabase::Status status) {
-  MaybeNotifyWriteFailed(quota_manager_proxy_, status, origin);
+  MaybeNotifyWriteFailed(quota_manager_proxy_, status, origin.GetURL());
   std::move(callback).Run(status);
 }
 
@@ -1536,12 +1536,12 @@
     ServiceWorkerDatabase* database,
     scoped_refptr<base::SequencedTaskRunner> original_task_runner,
     int64_t registration_id,
-    const GURL& origin,
+    const url::Origin& origin,
     FindInDBCallback callback) {
   storage::mojom::ServiceWorkerRegistrationDataPtr data;
   auto resources = std::make_unique<ResourceList>();
   ServiceWorkerDatabase::Status status = database->ReadRegistration(
-      registration_id, origin, &data, resources.get());
+      registration_id, origin.GetURL(), &data, resources.get());
   original_task_runner->PostTask(
       FROM_HERE, base::BindOnce(std::move(callback), std::move(data),
                                 std::move(resources), status));
@@ -1563,8 +1563,8 @@
                                   /*resources=*/nullptr, status));
     return;
   }
-  FindForIdInDB(database, original_task_runner, registration_id, origin,
-                std::move(callback));
+  FindForIdInDB(database, original_task_runner, registration_id,
+                url::Origin::Create(origin), std::move(callback));
 }
 
 // static
diff --git a/content/browser/service_worker/service_worker_storage.h b/content/browser/service_worker/service_worker_storage.h
index b25541b..6ede4b45 100644
--- a/content/browser/service_worker/service_worker_storage.h
+++ b/content/browser/service_worker/service_worker_storage.h
@@ -133,7 +133,7 @@
   void FindRegistrationForScope(const GURL& scope,
                                 FindRegistrationDataCallback callback);
   void FindRegistrationForId(int64_t registration_id,
-                             const GURL& origin,
+                             const url::Origin& origin,
                              FindRegistrationDataCallback callback);
   void FindRegistrationForIdOnly(int64_t registration_id,
                                  FindRegistrationDataCallback callback);
@@ -229,7 +229,7 @@
   // Stored data is deleted when the associated registraton is deleted.
   void StoreUserData(
       int64_t registration_id,
-      const GURL& origin,
+      const url::Origin& origin,
       std::vector<storage::mojom::ServiceWorkerUserDataPtr> user_data,
       DatabaseStatusCallback callback);
   // Responds OK if all are successfully deleted or not found in the database.
@@ -320,7 +320,7 @@
     int64_t next_registration_id;
     int64_t next_version_id;
     int64_t next_resource_id;
-    std::set<GURL> origins;
+    std::set<url::Origin> origins;
 
     InitialData();
     ~InitialData();
@@ -398,7 +398,7 @@
                                      DatabaseStatusCallback callback,
                                      ServiceWorkerDatabase::Status status);
   void DidStoreUserData(DatabaseStatusCallback callback,
-                        const GURL& origin,
+                        const url::Origin& origin,
                         ServiceWorkerDatabase::Status status);
 
   // Lazy disk_cache getter.
@@ -458,7 +458,7 @@
       ServiceWorkerDatabase* database,
       scoped_refptr<base::SequencedTaskRunner> original_task_runner,
       int64_t registration_id,
-      const GURL& origin,
+      const url::Origin& origin,
       FindInDBCallback callback);
   static void FindForIdOnlyInDB(
       ServiceWorkerDatabase* database,
@@ -511,7 +511,7 @@
   void DidDeleteDiskCache(DatabaseStatusCallback callback, bool result);
 
   // Origins having registations.
-  std::set<GURL> registered_origins_;
+  std::set<url::Origin> registered_origins_;
   // The set of origins whose storage should be cleaned on shutdown.
   std::set<GURL> origins_to_purge_on_shutdown_;
 
diff --git a/content/browser/service_worker/service_worker_storage_control_impl.cc b/content/browser/service_worker/service_worker_storage_control_impl.cc
index b30a32e..2c37fddb 100644
--- a/content/browser/service_worker/service_worker_storage_control_impl.cc
+++ b/content/browser/service_worker/service_worker_storage_control_impl.cc
@@ -142,7 +142,7 @@
 
 void ServiceWorkerStorageControlImpl::FindRegistrationForId(
     int64_t registration_id,
-    const base::Optional<GURL>& origin,
+    const base::Optional<url::Origin>& origin,
     FindRegistrationForClientUrlCallback callback) {
   if (origin.has_value()) {
     storage_->FindRegistrationForId(
@@ -302,7 +302,7 @@
 
 void ServiceWorkerStorageControlImpl::StoreUserData(
     int64_t registration_id,
-    const GURL& origin,
+    const url::Origin& origin,
     std::vector<storage::mojom::ServiceWorkerUserDataPtr> user_data,
     StoreUserDataCallback callback) {
   storage_->StoreUserData(registration_id, origin, std::move(user_data),
diff --git a/content/browser/service_worker/service_worker_storage_control_impl.h b/content/browser/service_worker/service_worker_storage_control_impl.h
index 32dda3f..ee857f8 100644
--- a/content/browser/service_worker/service_worker_storage_control_impl.h
+++ b/content/browser/service_worker/service_worker_storage_control_impl.h
@@ -59,7 +59,7 @@
       const GURL& scope,
       FindRegistrationForScopeCallback callback) override;
   void FindRegistrationForId(int64_t registration_id,
-                             const base::Optional<GURL>& origin,
+                             const base::Optional<url::Origin>& origin,
                              FindRegistrationForIdCallback callback) override;
   void GetRegistrationsForOrigin(
       const url::Origin& origin,
@@ -120,7 +120,7 @@
                    GetUserDataCallback callback) override;
   void StoreUserData(
       int64_t registration_id,
-      const GURL& origin,
+      const url::Origin& origin,
       std::vector<storage::mojom::ServiceWorkerUserDataPtr> user_data,
       StoreUserDataCallback callback) override;
   void ClearUserData(int64_t registration_id,
diff --git a/content/browser/service_worker/service_worker_storage_control_impl_unittest.cc b/content/browser/service_worker/service_worker_storage_control_impl_unittest.cc
index 85d88fcd..b872f1c 100644
--- a/content/browser/service_worker/service_worker_storage_control_impl_unittest.cc
+++ b/content/browser/service_worker/service_worker_storage_control_impl_unittest.cc
@@ -239,7 +239,7 @@
 
   FindRegistrationResult FindRegistrationForId(
       int64_t registration_id,
-      const base::Optional<GURL>& origin) {
+      const base::Optional<url::Origin>& origin) {
     FindRegistrationResult return_value;
     base::RunLoop loop;
     storage()->FindRegistrationForId(
@@ -449,7 +449,7 @@
 
   DatabaseStatus StoreUserData(
       int64_t registration_id,
-      const GURL& origin,
+      const url::Origin& origin,
       std::vector<storage::mojom::ServiceWorkerUserDataPtr> user_data) {
     DatabaseStatus return_value;
     base::RunLoop loop;
@@ -710,7 +710,7 @@
 
   {
     FindRegistrationResult result =
-        FindRegistrationForId(kRegistrationId, kScope.GetOrigin());
+        FindRegistrationForId(kRegistrationId, url::Origin::Create(kScope));
     EXPECT_EQ(result.status, DatabaseStatus::kErrorNotFound);
   }
 
@@ -772,7 +772,8 @@
 
     result = FindRegistrationForScope(kScope);
     EXPECT_EQ(result.status, DatabaseStatus::kOk);
-    result = FindRegistrationForId(kRegistrationId, kScope.GetOrigin());
+    result =
+        FindRegistrationForId(kRegistrationId, url::Origin::Create(kScope));
     EXPECT_EQ(result.status, DatabaseStatus::kOk);
     result = FindRegistrationForId(kRegistrationId, base::nullopt);
     EXPECT_EQ(result.status, DatabaseStatus::kOk);
@@ -794,7 +795,8 @@
     EXPECT_EQ(result.status, DatabaseStatus::kErrorNotFound);
     result = FindRegistrationForScope(kScope);
     EXPECT_EQ(result.status, DatabaseStatus::kErrorNotFound);
-    result = FindRegistrationForId(kRegistrationId, kScope.GetOrigin());
+    result =
+        FindRegistrationForId(kRegistrationId, url::Origin::Create(kScope));
     EXPECT_EQ(result.status, DatabaseStatus::kErrorNotFound);
   }
 }
@@ -818,7 +820,7 @@
   // The stored registration shouldn't be activated yet.
   {
     FindRegistrationResult result =
-        FindRegistrationForId(registration_id, kScope.GetOrigin());
+        FindRegistrationForId(registration_id, url::Origin::Create(kScope));
     ASSERT_EQ(result.status, DatabaseStatus::kOk);
     EXPECT_FALSE(result.entry->registration->is_active);
   }
@@ -830,7 +832,7 @@
   // Now the stored registration should be active.
   {
     FindRegistrationResult result =
-        FindRegistrationForId(registration_id, kScope.GetOrigin());
+        FindRegistrationForId(registration_id, url::Origin::Create(kScope));
     ASSERT_EQ(result.status, DatabaseStatus::kOk);
     EXPECT_TRUE(result.entry->registration->is_active);
   }
@@ -855,7 +857,7 @@
   // The stored registration shouldn't have the last update check time yet.
   {
     FindRegistrationResult result =
-        FindRegistrationForId(registration_id, kScope.GetOrigin());
+        FindRegistrationForId(registration_id, url::Origin::Create(kScope));
     ASSERT_EQ(result.status, DatabaseStatus::kOk);
     EXPECT_EQ(result.entry->registration->last_update_check, base::Time());
   }
@@ -868,7 +870,7 @@
   // Now the stored registration should be active.
   {
     FindRegistrationResult result =
-        FindRegistrationForId(registration_id, kScope.GetOrigin());
+        FindRegistrationForId(registration_id, url::Origin::Create(kScope));
     ASSERT_EQ(result.status, DatabaseStatus::kOk);
     EXPECT_EQ(result.entry->registration->last_update_check, now);
   }
@@ -893,7 +895,7 @@
   // Check the stored registration has default navigation preload fields.
   {
     FindRegistrationResult result =
-        FindRegistrationForId(registration_id, kScope.GetOrigin());
+        FindRegistrationForId(registration_id, url::Origin::Create(kScope));
     ASSERT_EQ(result.status, DatabaseStatus::kOk);
     EXPECT_FALSE(result.entry->registration->navigation_preload_state->enabled);
     EXPECT_EQ(result.entry->registration->navigation_preload_state->header,
@@ -912,7 +914,7 @@
   // Check navigation preload fields are updated.
   {
     FindRegistrationResult result =
-        FindRegistrationForId(registration_id, kScope.GetOrigin());
+        FindRegistrationForId(registration_id, url::Origin::Create(kScope));
     ASSERT_EQ(result.status, DatabaseStatus::kOk);
     EXPECT_TRUE(result.entry->registration->navigation_preload_state->enabled);
     EXPECT_EQ(result.entry->registration->navigation_preload_state->header,
@@ -1164,7 +1166,7 @@
     user_data.push_back(storage::mojom::ServiceWorkerUserData::New(
         registration_id, "key2", "value2"));
 
-    status = StoreUserData(registration_id, kScope.GetOrigin(),
+    status = StoreUserData(registration_id, url::Origin::Create(kScope),
                            std::move(user_data));
     ASSERT_EQ(status, DatabaseStatus::kOk);
   }
@@ -1257,8 +1259,8 @@
       registration_id, "prefixB", "value3"));
   user_data.push_back(storage::mojom::ServiceWorkerUserData::New(
       registration_id, "prefixC", "value4"));
-  status =
-      StoreUserData(registration_id, kScope.GetOrigin(), std::move(user_data));
+  status = StoreUserData(registration_id, url::Origin::Create(kScope),
+                         std::move(user_data));
   ASSERT_EQ(status, DatabaseStatus::kOk);
 
   {
@@ -1342,7 +1344,7 @@
         registration_id1, "key2", "registration1_value2"));
     user_data.push_back(storage::mojom::ServiceWorkerUserData::New(
         registration_id1, "prefix1", "registration1_prefix_value1"));
-    status = StoreUserData(registration_id1, kScope1.GetOrigin(),
+    status = StoreUserData(registration_id1, url::Origin::Create(kScope1),
                            std::move(user_data));
     ASSERT_EQ(status, DatabaseStatus::kOk);
   }
@@ -1354,7 +1356,7 @@
         registration_id1, "key3", "registration2_value3"));
     user_data.push_back(storage::mojom::ServiceWorkerUserData::New(
         registration_id1, "prefix2", "registration2_prefix_value2"));
-    status = StoreUserData(registration_id2, kScope2.GetOrigin(),
+    status = StoreUserData(registration_id2, url::Origin::Create(kScope2),
                            std::move(user_data));
     ASSERT_EQ(status, DatabaseStatus::kOk);
   }
@@ -1507,7 +1509,7 @@
   mojo::Remote<storage::mojom::ServiceWorkerLiveVersionRef> reference2;
   {
     FindRegistrationResult result =
-        FindRegistrationForId(registration_id, kScope.GetOrigin());
+        FindRegistrationForId(registration_id, url::Origin::Create(kScope));
     ASSERT_EQ(result.status, DatabaseStatus::kOk);
     ASSERT_TRUE(result.entry->version_reference);
     reference2.Bind(std::move(result.entry->version_reference));
diff --git a/content/browser/service_worker/service_worker_storage_unittest.cc b/content/browser/service_worker/service_worker_storage_unittest.cc
index 51b22be5..583b72b 100644
--- a/content/browser/service_worker/service_worker_storage_unittest.cc
+++ b/content/browser/service_worker/service_worker_storage_unittest.cc
@@ -315,7 +315,7 @@
   ServiceWorkerDatabase* database() { return storage()->database_.get(); }
 
  protected:
-  const std::set<GURL>& registered_origins() {
+  const std::set<url::Origin>& registered_origins() {
     return storage()->registered_origins_;
   }
 
@@ -449,7 +449,7 @@
 
   blink::ServiceWorkerStatusCode StoreUserData(
       int64_t registration_id,
-      const GURL& origin,
+      const url::Origin& origin,
       const std::vector<std::pair<std::string, std::string>>& key_value_pairs) {
     base::RunLoop loop;
     base::Optional<blink::ServiceWorkerStatusCode> result;
@@ -569,7 +569,7 @@
 
   blink::ServiceWorkerStatusCode FindRegistrationForId(
       int64_t registration_id,
-      const GURL& origin,
+      const url::Origin& origin,
       scoped_refptr<ServiceWorkerRegistration>* registration) {
     base::Optional<blink::ServiceWorkerStatusCode> result;
     base::RunLoop loop;
@@ -686,6 +686,7 @@
 
 TEST_F(ServiceWorkerStorageTest, DisabledStorage) {
   const GURL kScope("http://www.example.com/scope/");
+  const url::Origin kOrigin = url::Origin::Create(kScope);
   const GURL kScript("http://www.example.com/script.js");
   const GURL kDocumentUrl("http://www.example.com/scope/document.html");
   const int64_t kRegistrationId = 0;
@@ -702,7 +703,7 @@
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorAbort,
             FindRegistrationForScope(kScope, &found_registration));
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorAbort,
-            FindRegistrationForId(kRegistrationId, kScope.GetOrigin(),
+            FindRegistrationForId(kRegistrationId, url::Origin::Create(kScope),
                                   &found_registration));
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorAbort,
             FindRegistrationForIdOnly(kRegistrationId, &found_registration));
@@ -752,8 +753,7 @@
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorAbort,
             GetUserDataByKeyPrefix(kRegistrationId, "prefix", &user_data_out));
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorAbort,
-            StoreUserData(kRegistrationId, kScope.GetOrigin(),
-                          {{kUserDataKey, "foo"}}));
+            StoreUserData(kRegistrationId, kOrigin, {{kUserDataKey, "foo"}}));
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorAbort,
             ClearUserData(kRegistrationId, {kUserDataKey}));
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorAbort,
@@ -799,7 +799,7 @@
   EXPECT_FALSE(found_registration.get());
 
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorNotFound,
-            FindRegistrationForId(kRegistrationId, kScope.GetOrigin(),
+            FindRegistrationForId(kRegistrationId, url::Origin::Create(kScope),
                                   &found_registration));
   EXPECT_FALSE(found_registration.get());
 
@@ -856,7 +856,7 @@
 
   // Can be found by id too.
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
-            FindRegistrationForId(kRegistrationId, kScope.GetOrigin(),
+            FindRegistrationForId(kRegistrationId, url::Origin::Create(kScope),
                                   &found_registration));
   ASSERT_TRUE(found_registration.get());
   EXPECT_EQ(kRegistrationId, found_registration->id());
@@ -987,7 +987,7 @@
 
   // Should not be findable, including by GetAllRegistrationsInfos.
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorNotFound,
-            FindRegistrationForId(kRegistrationId, kScope.GetOrigin(),
+            FindRegistrationForId(kRegistrationId, url::Origin::Create(kScope),
                                   &found_registration));
   EXPECT_FALSE(found_registration.get());
 
@@ -1026,7 +1026,7 @@
 
   // Now should be findable.
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
-            FindRegistrationForId(kRegistrationId, kScope.GetOrigin(),
+            FindRegistrationForId(kRegistrationId, url::Origin::Create(kScope),
                                   &found_registration));
   EXPECT_EQ(live_registration, found_registration);
   found_registration = nullptr;
@@ -1070,7 +1070,7 @@
 
   // Once again, should not be findable.
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorNotFound,
-            FindRegistrationForId(kRegistrationId, kScope.GetOrigin(),
+            FindRegistrationForId(kRegistrationId, url::Origin::Create(kScope),
                                   &found_registration));
   EXPECT_FALSE(found_registration.get());
 
@@ -1104,6 +1104,7 @@
 
 TEST_F(ServiceWorkerStorageTest, StoreUserData) {
   const GURL kScope("http://www.test.not/scope/");
+  const url::Origin kOrigin = url::Origin::Create(kScope);
   const GURL kScript("http://www.test.not/script.js");
   LazyInitialize();
 
@@ -1118,9 +1119,8 @@
 
   // Store user data associated with the registration.
   std::vector<std::string> data_out;
-  EXPECT_EQ(
-      blink::ServiceWorkerStatusCode::kOk,
-      StoreUserData(kRegistrationId, kScope.GetOrigin(), {{"key", "data"}}));
+  EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
+            StoreUserData(kRegistrationId, kOrigin, {{"key", "data"}}));
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
             GetUserData(kRegistrationId, {"key"}, &data_out));
   ASSERT_EQ(1u, data_out.size());
@@ -1145,7 +1145,7 @@
   // Write/overwrite multiple user data keys.
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
             StoreUserData(
-                kRegistrationId, kScope.GetOrigin(),
+                kRegistrationId, kOrigin,
                 {{"key", "overwrite"}, {"key3", "data3"}, {"key4", "data4"}}));
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorNotFound,
             GetUserData(kRegistrationId, {"key2"}, &data_out));
@@ -1177,7 +1177,7 @@
 
   // Get/delete multiple user data keys by prefixes.
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
-            StoreUserData(kRegistrationId, kScope.GetOrigin(),
+            StoreUserData(kRegistrationId, kOrigin,
                           {{"prefixA", "data1"},
                            {"prefixA2", "data2"},
                            {"prefixB", "data3"},
@@ -1204,9 +1204,8 @@
   EXPECT_TRUE(data_out.empty());
 
   // User data should be deleted when the associated registration is deleted.
-  ASSERT_EQ(
-      blink::ServiceWorkerStatusCode::kOk,
-      StoreUserData(kRegistrationId, kScope.GetOrigin(), {{"key", "data"}}));
+  ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk,
+            StoreUserData(kRegistrationId, kOrigin, {{"key", "data"}}));
   ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk,
             GetUserData(kRegistrationId, {"key"}, &data_out));
   ASSERT_EQ(1u, data_out.size());
@@ -1224,7 +1223,7 @@
   // Data access with an invalid registration id should be failed.
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorFailed,
             StoreUserData(blink::mojom::kInvalidServiceWorkerRegistrationId,
-                          kScope.GetOrigin(), {{"key", "data"}}));
+                          kOrigin, {{"key", "data"}}));
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorFailed,
             GetUserData(blink::mojom::kInvalidServiceWorkerRegistrationId,
                         {"key"}, &data_out));
@@ -1241,13 +1240,12 @@
 
   // Data access with an empty key should be failed.
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorFailed,
-            StoreUserData(kRegistrationId, kScope.GetOrigin(),
+            StoreUserData(kRegistrationId, kOrigin,
                           std::vector<std::pair<std::string, std::string>>()));
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorFailed,
-            StoreUserData(kRegistrationId, kScope.GetOrigin(),
-                          {{std::string(), "data"}}));
+            StoreUserData(kRegistrationId, kOrigin, {{std::string(), "data"}}));
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorFailed,
-            StoreUserData(kRegistrationId, kScope.GetOrigin(),
+            StoreUserData(kRegistrationId, kOrigin,
                           {{std::string(), "data"}, {"key", "data"}}));
   EXPECT_EQ(
       blink::ServiceWorkerStatusCode::kErrorFailed,
@@ -1280,7 +1278,8 @@
 TEST_F(ServiceWorkerStorageTest, StoreUserData_BeforeInitialize) {
   const int kRegistrationId = 0;
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorNotFound,
-            StoreUserData(kRegistrationId, GURL("https://example.com"),
+            StoreUserData(kRegistrationId,
+                          url::Origin::Create(GURL("https://example.com")),
                           {{"key", "data"}}));
 }
 
diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc
index dbbd57e..fd0522c 100644
--- a/content/browser/service_worker/service_worker_version.cc
+++ b/content/browser/service_worker/service_worker_version.cc
@@ -255,9 +255,7 @@
     : version_id_(version_id),
       registration_id_(registration->id()),
       script_url_(script_url),
-      // Safe to convert GURL to Origin because service workers are restricted
-      // to secure contexts.
-      script_origin_(url::Origin::Create(script_url_)),
+      origin_(registration->origin()),
       scope_(registration->scope()),
       script_type_(script_type),
       fetch_handler_existence_(FetchHandlerExistence::UNKNOWN),
@@ -390,7 +388,7 @@
   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
   ServiceWorkerVersionInfo info(
       running_status(), status(), fetch_handler_existence(), script_url(),
-      script_origin(), registration_id(), version_id(),
+      origin(), registration_id(), version_id(),
       embedded_worker()->process_id(), embedded_worker()->thread_id(),
       embedded_worker()->worker_devtools_agent_route_id());
   for (const auto& controllee : controllee_map_) {
@@ -465,7 +463,7 @@
   // get associated with it in
   // ServiceWorkerHost::CompleteStartWorkerPreparation.
   context_->registry()->FindRegistrationForId(
-      registration_id_, scope_.GetOrigin(),
+      registration_id_, origin_,
       base::BindOnce(
           &ServiceWorkerVersion::DidEnsureLiveRegistrationForStartWorker,
           weak_factory_.GetWeakPtr(), purpose, status_,
@@ -576,7 +574,7 @@
   if (!context_)
     return;
   context_->registry()->FindRegistrationForId(
-      registration_id_, scope_.GetOrigin(),
+      registration_id_, origin_,
       base::BindOnce(&ServiceWorkerVersion::FoundRegistrationForUpdate,
                      weak_factory_.GetWeakPtr()));
 }
@@ -1387,8 +1385,7 @@
     return;
   }
 
-  if (!url.is_valid() ||
-      !url::Origin::Create(url).IsSameOriginWith(script_origin_)) {
+  if (!url.is_valid() || !url::Origin::Create(url).IsSameOriginWith(origin_)) {
     mojo::ReportBadMessage(
         "Received PaymentRequestEvent#openWindow() request for a cross-origin "
         "URL.");
diff --git a/content/browser/service_worker/service_worker_version.h b/content/browser/service_worker/service_worker_version.h
index 71867e7..f813b9a 100644
--- a/content/browser/service_worker/service_worker_version.h
+++ b/content/browser/service_worker/service_worker_version.h
@@ -205,7 +205,7 @@
   int64_t version_id() const { return version_id_; }
   int64_t registration_id() const { return registration_id_; }
   const GURL& script_url() const { return script_url_; }
-  const url::Origin& script_origin() const { return script_origin_; }
+  const url::Origin& origin() const { return origin_; }
   const GURL& scope() const { return scope_; }
   blink::mojom::ScriptType script_type() const { return script_type_; }
   EmbeddedWorkerStatus running_status() const {
@@ -886,7 +886,10 @@
   const int64_t version_id_;
   const int64_t registration_id_;
   const GURL script_url_;
-  const url::Origin script_origin_;
+  // |origin_| is computed from |scope_|. Warning: The |script_url_|'s origin
+  // and |origin_| may be different in some scenarios e.g.
+  // --disable-web-security.
+  const url::Origin origin_;
   const GURL scope_;
   // A service worker has an associated type which is either
   // "classic" or "module". Unless stated otherwise, it is "classic".
diff --git a/content/browser/service_worker/service_worker_version_browsertest.cc b/content/browser/service_worker/service_worker_version_browsertest.cc
index 13d65e0..165ad68c 100644
--- a/content/browser/service_worker/service_worker_version_browsertest.cc
+++ b/content/browser/service_worker/service_worker_version_browsertest.cc
@@ -47,7 +47,6 @@
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/result_codes.h"
-#include "content/public/common/web_preferences.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_browser_test.h"
@@ -63,6 +62,7 @@
 #include "storage/browser/test/blob_test_utils.h"
 #include "third_party/blink/public/common/service_worker/service_worker_status_code.h"
 #include "third_party/blink/public/common/service_worker/service_worker_type_converters.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "third_party/blink/public/mojom/loader/resource_load_info.mojom-shared.h"
 
 using blink::mojom::CacheStorageError;
@@ -552,7 +552,7 @@
   }
 
   void FindRegistrationForId(int64_t id,
-                             const GURL& origin,
+                             const url::Origin& origin,
                              blink::ServiceWorkerStatusCode expected_status) {
     ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI));
     blink::ServiceWorkerStatusCode status =
@@ -570,7 +570,7 @@
   void FindRegistrationForIdOnCoreThread(base::OnceClosure done,
                                          blink::ServiceWorkerStatusCode* result,
                                          int64_t id,
-                                         const GURL& origin) {
+                                         const url::Origin& origin) {
     ASSERT_TRUE(
         BrowserThread::CurrentlyOn(ServiceWorkerContext::GetCoreThreadId()));
     wrapper()->context()->registry()->FindRegistrationForId(
@@ -876,7 +876,7 @@
   }
 
   void OverrideWebkitPrefs(RenderViewHost* render_view_host,
-                           WebPreferences* prefs) override {
+                           blink::web_pref::WebPreferences* prefs) override {
     prefs->data_saver_enabled = data_saver_enabled_;
   }
 
@@ -1002,7 +1002,7 @@
   EXPECT_EQ(ServiceWorkerVersion::REDUNDANT, version_->status());
 
   // The registration should be deleted from storage.
-  FindRegistrationForId(registration_->id(), registration_->scope().GetOrigin(),
+  FindRegistrationForId(registration_->id(), registration_->origin(),
                         blink::ServiceWorkerStatusCode::kErrorNotFound);
   EXPECT_TRUE(registration_->is_uninstalled());
 }
@@ -1053,7 +1053,7 @@
 
   // The whole registration should be deleted from storage even though the
   // waiting version was not the broken one.
-  FindRegistrationForId(registration_->id(), registration_->scope().GetOrigin(),
+  FindRegistrationForId(registration_->id(), registration_->origin(),
                         blink::ServiceWorkerStatusCode::kErrorNotFound);
   EXPECT_TRUE(registration_->is_uninstalled());
 }
diff --git a/content/browser/service_worker/service_worker_version_unittest.cc b/content/browser/service_worker/service_worker_version_unittest.cc
index 052cbb95..5463015 100644
--- a/content/browser/service_worker/service_worker_version_unittest.cc
+++ b/content/browser/service_worker/service_worker_version_unittest.cc
@@ -98,7 +98,7 @@
         helper_->context()->registry(), registration_.get(),
         GURL("https://www.example.com/test/service_worker.js"),
         blink::mojom::ScriptType::kClassic);
-    EXPECT_EQ(url::Origin::Create(scope_), version_->script_origin());
+    EXPECT_EQ(url::Origin::Create(scope_), version_->origin());
     std::vector<storage::mojom::ServiceWorkerResourceRecordPtr> records;
     records.push_back(WriteToDiskCacheWithIdSync(
         helper_->context()->GetStorageControl(), version_->script_url(), 10,
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index 5208648..4ab086f 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -1159,7 +1159,8 @@
 IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, TextAutosizerPageInfo) {
   UpdateTextAutosizerInfoProxyObserver update_text_autosizer_info_observer;
 
-  WebPreferences prefs = web_contents()->GetOrCreateWebPreferences();
+  blink::web_pref::WebPreferences prefs =
+      web_contents()->GetOrCreateWebPreferences();
   prefs.text_autosizing_enabled = true;
 
   GURL main_url(embedded_test_server()->GetURL(
@@ -13089,7 +13090,7 @@
   EnableForceZoomContentClient() = default;
 
   void OverrideWebkitPrefs(RenderViewHost* render_view_host,
-                           WebPreferences* prefs) override {
+                           blink::web_pref::WebPreferences* prefs) override {
     DCHECK(old_client_);
     old_client_->OverrideWebkitPrefs(render_view_host, prefs);
     prefs->force_enable_zoom = true;
@@ -14403,8 +14404,9 @@
  public:
   DoubleTapZoomContentBrowserClient() = default;
 
-  void OverrideWebkitPrefs(content::RenderViewHost* rvh,
-                           content::WebPreferences* web_prefs) override {
+  void OverrideWebkitPrefs(
+      content::RenderViewHost* rvh,
+      blink::web_pref::WebPreferences* web_prefs) override {
     web_prefs->double_tap_to_zoom_enabled = true;
   }
 
diff --git a/content/browser/site_per_process_hit_test_browsertest.cc b/content/browser/site_per_process_hit_test_browsertest.cc
index 68f0f3d..75e009de 100644
--- a/content/browser/site_per_process_hit_test_browsertest.cc
+++ b/content/browser/site_per_process_hit_test_browsertest.cc
@@ -36,7 +36,6 @@
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/use_zoom_for_dsf_policy.h"
-#include "content/public/common/web_preferences.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_browser_test_utils.h"
@@ -46,6 +45,7 @@
 #include "content/shell/common/shell_switches.h"
 #include "content/test/mock_overscroll_observer.h"
 #include "third_party/blink/public/common/switches.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "third_party/blink/public/mojom/frame/user_activation_update_types.mojom.h"
 #include "third_party/blink/public/mojom/input/input_handler.mojom-test-utils.h"
 #include "third_party/blink/public/mojom/page/widget.mojom-test-utils.h"
@@ -5591,7 +5591,7 @@
 
   WebContentsImpl* contents = web_contents();
 
-  WebPreferences prefs = contents->GetOrCreateWebPreferences();
+  blink::web_pref::WebPreferences prefs = contents->GetOrCreateWebPreferences();
   prefs.double_tap_to_zoom_enabled = true;
   contents->SetWebPreferences(prefs);
 
diff --git a/content/browser/url_loader_factory_params_helper.cc b/content/browser/url_loader_factory_params_helper.cc
index c4a80d51..96c3c889 100644
--- a/content/browser/url_loader_factory_params_helper.cc
+++ b/content/browser/url_loader_factory_params_helper.cc
@@ -15,12 +15,12 @@
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/url_constants.h"
-#include "content/public/common/web_preferences.h"
 #include "ipc/ipc_message.h"
 #include "net/base/isolation_info.h"
 #include "services/network/public/mojom/cross_origin_embedder_policy.mojom.h"
 #include "services/network/public/mojom/network_context.mojom.h"
 #include "services/network/public/mojom/url_loader.mojom-shared.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 
 namespace content {
 
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 88d4426..d76fe04 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -150,7 +150,6 @@
 #include "content/public/common/result_codes.h"
 #include "content/public/common/url_utils.h"
 #include "content/public/common/use_zoom_for_dsf_policy.h"
-#include "content/public/common/web_preferences.h"
 #include "media/base/media_switches.h"
 #include "media/base/user_input_monitor.h"
 #include "net/base/url_util.h"
@@ -168,6 +167,7 @@
 #include "third_party/blink/public/common/mime_util/mime_util.h"
 #include "third_party/blink/public/common/page/page_zoom.h"
 #include "third_party/blink/public/common/security/security_style.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "third_party/blink/public/mojom/frame/frame.mojom.h"
 #include "third_party/blink/public/mojom/frame/fullscreen.mojom.h"
 #include "third_party/blink/public/mojom/loader/pause_subresource_loading_handle.mojom.h"
@@ -298,7 +298,7 @@
 
  private:
   friend class base::RefCountedThreadSafe<CloseDialogCallbackWrapper>;
-  ~CloseDialogCallbackWrapper() {}
+  ~CloseDialogCallbackWrapper() = default;
 
   CloseCallback callback_;
 };
@@ -529,10 +529,9 @@
     opener = opener_rfh->frame_tree_node();
   new_contents->SetOpenerForNewContents(opener, params.opener_suppressed);
 
-  for (auto it = session_storage_namespace_map.begin();
-       it != session_storage_namespace_map.end(); ++it) {
-    new_contents->GetController().SetSessionStorageNamespace(it->first,
-                                                             it->second.get());
+  for (const auto& it : session_storage_namespace_map) {
+    new_contents->GetController().SetSessionStorageNamespace(it.first,
+                                                             it.second.get());
   }
 
   WebContentsImpl* outer_web_contents = nullptr;
@@ -675,7 +674,7 @@
           FrameTreeNode::kFrameTreeNodeInvalidId),
       focused_web_contents_(current_web_contents) {}
 
-WebContentsImpl::WebContentsTreeNode::~WebContentsTreeNode() {}
+WebContentsImpl::WebContentsTreeNode::~WebContentsTreeNode() = default;
 
 std::unique_ptr<WebContents>
 WebContentsImpl::WebContentsTreeNode::DisconnectFromOuterWebContents() {
@@ -845,7 +844,7 @@
   frame_tree_.SetFrameRemoveListener(base::BindRepeating(
       &WebContentsImpl::OnFrameRemoved, base::Unretained(this)));
 #if BUILDFLAG(ENABLE_PLUGINS)
-  pepper_playback_observer_.reset(new PepperPlaybackObserver(this));
+  pepper_playback_observer_ = std::make_unique<PepperPlaybackObserver>(this);
 #endif
 
 #if defined(OS_ANDROID)
@@ -2188,11 +2187,11 @@
 
   // When attaching a WebContents as an inner WebContents, we need to replace
   // the Webcontents' view with a WebContentsViewChildFrame.
-  inner_web_contents_impl->view_.reset(new WebContentsViewChildFrame(
+  inner_web_contents_impl->view_ = std::make_unique<WebContentsViewChildFrame>(
       inner_web_contents_impl,
       GetContentClient()->browser()->GetWebContentsViewDelegate(
           inner_web_contents_impl),
-      &inner_web_contents_impl->render_view_host_delegate_view_));
+      &inner_web_contents_impl->render_view_host_delegate_view_);
 
   // When the WebContents being initialized has an opener, the  browser side
   // Render{View,Frame}Host must be initialized and the RenderWidgetHostView
@@ -2383,10 +2382,10 @@
   });
 }
 
-const WebPreferences WebContentsImpl::ComputeWebPreferences() {
+const blink::web_pref::WebPreferences WebContentsImpl::ComputeWebPreferences() {
   OPTIONAL_TRACE_EVENT0("browser", "WebContentsImpl::ComputeWebPreferences");
 
-  WebPreferences prefs;
+  blink::web_pref::WebPreferences prefs;
 
   const base::CommandLine& command_line =
       *base::CommandLine::ForCurrentProcess();
@@ -2441,13 +2440,16 @@
 
   std::string autoplay_policy = media::GetEffectiveAutoplayPolicy(command_line);
   if (autoplay_policy == switches::autoplay::kNoUserGestureRequiredPolicy) {
-    prefs.autoplay_policy = AutoplayPolicy::kNoUserGestureRequired;
+    prefs.autoplay_policy =
+        blink::web_pref::AutoplayPolicy::kNoUserGestureRequired;
   } else if (autoplay_policy ==
              switches::autoplay::kUserGestureRequiredPolicy) {
-    prefs.autoplay_policy = AutoplayPolicy::kUserGestureRequired;
+    prefs.autoplay_policy =
+        blink::web_pref::AutoplayPolicy::kUserGestureRequired;
   } else if (autoplay_policy ==
              switches::autoplay::kDocumentUserActivationRequiredPolicy) {
-    prefs.autoplay_policy = AutoplayPolicy::kDocumentUserActivationRequired;
+    prefs.autoplay_policy =
+        blink::web_pref::AutoplayPolicy::kDocumentUserActivationRequired;
   } else {
     NOTREACHED();
   }
@@ -2535,7 +2537,7 @@
 
 void WebContentsImpl::SetSlowWebPreferences(
     const base::CommandLine& command_line,
-    WebPreferences* prefs) {
+    blink::web_pref::WebPreferences* prefs) {
   OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::SetSlowWebPreferences");
 
   if (web_preferences_.get()) {
@@ -2765,8 +2767,8 @@
       GetContentClient()->browser()->GetWebContentsViewDelegate(this);
 
   if (browser_plugin_guest_) {
-    view_.reset(new WebContentsViewChildFrame(
-        this, delegate, &render_view_host_delegate_view_));
+    view_ = std::make_unique<WebContentsViewChildFrame>(
+        this, delegate, &render_view_host_delegate_view_);
   } else {
     view_.reset(CreateWebContentsView(this, delegate,
                                       &render_view_host_delegate_view_));
@@ -2777,15 +2779,16 @@
   view_->CreateView(params.context);
 
 #if BUILDFLAG(ENABLE_PLUGINS)
-  plugin_content_origin_allowlist_.reset(
-      new PluginContentOriginAllowlist(this));
+  plugin_content_origin_allowlist_ =
+      std::make_unique<PluginContentOriginAllowlist>(this);
 #endif
 
   registrar_.Add(this,
                  NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED,
                  NotificationService::AllBrowserContextsAndSources());
 
-  screen_orientation_provider_.reset(new ScreenOrientationProvider(this));
+  screen_orientation_provider_ =
+      std::make_unique<ScreenOrientationProvider>(this);
 
 #if defined(OS_ANDROID)
   DateTimeChooserAndroid::CreateForWebContents(this);
@@ -2796,8 +2799,8 @@
   if (browser_plugin_guest_)
     browser_plugin_guest_->Init();
 
-  for (size_t i = 0; i < g_created_callbacks.Get().size(); i++)
-    g_created_callbacks.Get().at(i).Run(this);
+  for (auto& i : g_created_callbacks.Get())
+    i.Run(this);
 
   // If the WebContents creation was renderer-initiated, it means that the
   // corresponding RenderView and main RenderFrame have already been created.
@@ -3124,7 +3127,8 @@
     return GetOuterWebContents()->GetInputEventRouter();
 
   if (!rwh_input_event_router_.get() && !is_being_destroyed_)
-    rwh_input_event_router_.reset(new RenderWidgetHostInputEventRouter);
+    rwh_input_event_router_ =
+        std::make_unique<RenderWidgetHostInputEventRouter>();
   return rwh_input_event_router_.get();
 }
 
@@ -4210,7 +4214,7 @@
 
 device::mojom::WakeLockContext* WebContentsImpl::GetWakeLockContext() {
   if (!wake_lock_context_host_)
-    wake_lock_context_host_.reset(new WakeLockContextHost(this));
+    wake_lock_context_host_ = std::make_unique<WakeLockContextHost>(this);
   return wake_lock_context_host_->GetWakeLockContext();
 }
 
@@ -4250,9 +4254,9 @@
     return GetOuterWebContents()->GetTextInputManager();
 
   if (!text_input_manager_ && !browser_plugin_guest_) {
-    text_input_manager_.reset(new TextInputManager(
+    text_input_manager_ = std::make_unique<TextInputManager>(
         GetBrowserContext() &&
-        !GetBrowserContext()->IsOffTheRecord()) /* should_do_learning */);
+        !GetBrowserContext()->IsOffTheRecord() /* should_do_learning */);
   }
 
   return text_input_manager_.get();
@@ -5742,7 +5746,8 @@
   });
 }
 
-const WebPreferences& WebContentsImpl::GetOrCreateWebPreferences() {
+const blink::web_pref::WebPreferences&
+WebContentsImpl::GetOrCreateWebPreferences() {
   OPTIONAL_TRACE_EVENT0("content",
                         "WebContentsImpl::GetOrCreateWebPreferences");
   // Compute WebPreferences based on the current state if it's null.
@@ -5755,9 +5760,10 @@
   return web_preferences_.get();
 }
 
-void WebContentsImpl::SetWebPreferences(const WebPreferences& prefs) {
+void WebContentsImpl::SetWebPreferences(
+    const blink::web_pref::WebPreferences& prefs) {
   OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::SetWebPreferences");
-  web_preferences_ = std::make_unique<WebPreferences>(prefs);
+  web_preferences_ = std::make_unique<blink::web_pref::WebPreferences>(prefs);
   // Get all the RenderViewHosts (except the ones for currently back-forward
   // cached pages), and make them send the current WebPreferences
   // to the renderer. WebPreferences updates for back-forward cached pages will
@@ -7208,10 +7214,9 @@
   // An entry may not exist for a stop when loading an initial blank page or
   // if an iframe injected by script into a blank page finishes loading.
   if (entry) {
-    details.reset(new LoadNotificationDetails(
-        entry->GetVirtualURL(),
-        &controller_,
-        controller_.GetCurrentEntryIndex()));
+    details = std::make_unique<LoadNotificationDetails>(
+        entry->GetVirtualURL(), &controller_,
+        controller_.GetCurrentEntryIndex());
   }
 
   LoadingStateChanged(true, details.get());
@@ -8224,7 +8229,7 @@
   DCHECK(!browser_plugin_guest_ || GetOuterWebContents());
 
   // No existing FindRequestManager found, so one must be created.
-  find_request_manager_.reset(new FindRequestManager(this));
+  find_request_manager_ = std::make_unique<FindRequestManager>(this);
 
   // Concurrent find sessions must not overlap, so destroy any existing
   // FindRequestManagers in any inner WebContentses.
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 9f87c39d..f4ec726 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -55,7 +55,6 @@
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_receiver_set.h"
 #include "content/public/common/three_d_api_types.h"
-#include "content/public/common/web_preferences.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
@@ -67,6 +66,7 @@
 #include "services/metrics/public/cpp/ukm_recorder.h"
 #include "services/network/public/mojom/fetch_api.mojom-forward.h"
 #include "third_party/blink/public/common/page/web_drag_operation.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "third_party/blink/public/mojom/choosers/color_chooser.mojom.h"
 #include "third_party/blink/public/mojom/choosers/popup_menu.mojom.h"
 #include "third_party/blink/public/mojom/frame/blocked_navigation_types.mojom.h"
@@ -543,9 +543,9 @@
   // Implementation of PageNavigator.
   WebContents* OpenURL(const OpenURLParams& params) override;
 
-  const WebPreferences& GetOrCreateWebPreferences() override;
+  const blink::web_pref::WebPreferences& GetOrCreateWebPreferences() override;
   void NotifyPreferencesChanged() override;
-  void SetWebPreferences(const WebPreferences& prefs) override;
+  void SetWebPreferences(const blink::web_pref::WebPreferences& prefs) override;
   void OnWebPreferencesChanged() override;
 
   // RenderFrameHostDelegate ---------------------------------------------------
@@ -1245,7 +1245,7 @@
   // Recomputes only the "fast" preferences (those not requiring slow
   // platform/device polling); the remaining "slow" ones are recomputed only if
   // the preference cache is empty.
-  const WebPreferences ComputeWebPreferences();
+  const blink::web_pref::WebPreferences ComputeWebPreferences();
 
  private:
   friend class WebContentsObserver;
@@ -1723,7 +1723,7 @@
   // Sets the hardware-related fields in |prefs| that are slow to compute.  The
   // fields are set from cache if available, otherwise recomputed.
   void SetSlowWebPreferences(const base::CommandLine& command_line,
-                             WebPreferences* prefs);
+                             blink::web_pref::WebPreferences* prefs);
 
   // Data for core operation ---------------------------------------------------
 
@@ -1978,7 +1978,7 @@
   // The last set/computed value of WebPreferences for this WebContents, either
   // set directly through SetWebPreferences, or set after recomputing values
   // from ComputeWebPreferences.
-  std::unique_ptr<WebPreferences> web_preferences_;
+  std::unique_ptr<blink::web_pref::WebPreferences> web_preferences_;
 
   bool updating_web_preferences_ = false;
 
diff --git a/content/browser/web_contents/web_contents_impl_browsertest.cc b/content/browser/web_contents/web_contents_impl_browsertest.cc
index ecc58b8f..06b7c9f 100644
--- a/content/browser/web_contents/web_contents_impl_browsertest.cc
+++ b/content/browser/web_contents/web_contents_impl_browsertest.cc
@@ -118,7 +118,7 @@
   NotifyPreferencesChangedTestContentBrowserClient() = default;
 
   void OverrideWebkitPrefs(RenderViewHost* render_view_host,
-                           WebPreferences* prefs) override {
+                           blink::web_pref::WebPreferences* prefs) override {
     override_webkit_prefs_rvh_set_.insert(render_view_host);
   }
 
diff --git a/content/browser/worker_host/worker_script_fetch_initiator.cc b/content/browser/worker_host/worker_script_fetch_initiator.cc
index a0585ae1..af9e4632 100644
--- a/content/browser/worker_host/worker_script_fetch_initiator.cc
+++ b/content/browser/worker_host/worker_script_fetch_initiator.cc
@@ -204,10 +204,11 @@
     // TODO(https://crbug.com/986188): Pass ChildProcessHost::kInvalidUniqueID
     // instead of valid |worker_process_id| for |factory_bundle_for_browser|
     // once CanCommitURL-like check is implemented in PlzWorker.
-    non_network_uniquely_owned_factories[url::kFileSystemScheme] =
+    non_network_factories.emplace(
+        url::kFileSystemScheme,
         CreateFileSystemURLLoaderFactory(
             worker_process_id, RenderFrameHost::kNoFrameTreeNodeId,
-            storage_partition->GetFileSystemContext(), storage_domain);
+            storage_partition->GetFileSystemContext(), storage_domain));
   }
   if (file_support) {
     // USER_VISIBLE because worker script fetch may affect the UI.
diff --git a/content/child/browser_font_resource_trusted.cc b/content/child/browser_font_resource_trusted.cc
index 3a64779..1bafbe0 100644
--- a/content/child/browser_font_resource_trusted.cc
+++ b/content/child/browser_font_resource_trusted.cc
@@ -11,7 +11,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "cc/paint/paint_canvas.h"
 #include "cc/paint/skia_paint_canvas.h"
-#include "content/public/common/web_preferences.h"
 #include "ppapi/proxy/connection.h"
 #include "ppapi/shared_impl/ppapi_preferences.h"
 #include "ppapi/shared_impl/var.h"
@@ -19,6 +18,7 @@
 #include "ppapi/thunk/ppb_image_data_api.h"
 #include "ppapi/thunk/thunk.h"
 #include "skia/ext/platform_canvas.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "third_party/blink/public/platform/web_float_rect.h"
 #include "third_party/blink/public/platform/web_font.h"
 #include "third_party/blink/public/platform/web_font_description.h"
@@ -44,9 +44,9 @@
 // undefined reference linker error.
 const char kCommonScript[] = "Zyyy";
 
-base::string16 GetFontFromMap(const ScriptFontFamilyMap& map,
+base::string16 GetFontFromMap(const blink::web_pref::ScriptFontFamilyMap& map,
                               const std::string& script) {
-  ScriptFontFamilyMap::const_iterator it = map.find(script);
+  blink::web_pref::ScriptFontFamilyMap::const_iterator it = map.find(script);
   if (it != map.end())
     return it->second;
   return base::string16();
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index 011d3619..5f0c4350 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -376,6 +376,7 @@
           {"CustomElementsV0", blink::features::kWebComponentsV0, kDefault},
           {"FeaturePolicyForClientHints",
            features::kFeaturePolicyForClientHints, kDefault},
+          {"EditingNG", blink::features::kEditingNG, kDefault},
           {"FontAccess", blink::features::kFontAccess, kDefault},
           {"FontSrcLocalMatching", features::kFontSrcLocalMatching, kDefault},
           {"ForceSynchronousHTMLParsing",
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index e95a3d0..8f47984 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -557,7 +557,7 @@
         "//content/common/input/synthetic_smooth_scroll_gesture_params.h",
         "//content/common/input/synthetic_tap_gesture_params.h",
         "//third_party/blink/public/common/widget/visual_properties.h",
-        "//content/public/common/web_preferences.h",
+        "//third_party/blink/public/common/web_preferences/web_preferences.h",
         "//net/base/network_change_notifier.h",
         "//third_party/blink/public/common/input/web_coalesced_input_event_mojom_traits.h",
         "//third_party/blink/public/common/input/web_input_event.h",
diff --git a/content/common/content_switches_internal.h b/content/common/content_switches_internal.h
index 886bdf0..57072bf 100644
--- a/content/common/content_switches_internal.h
+++ b/content/common/content_switches_internal.h
@@ -7,7 +7,7 @@
 
 #include "build/build_config.h"
 #include "content/common/content_export.h"
-#include "content/public/common/web_preferences.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 
 namespace base {
 class CommandLine;
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h
index 7d026daa..b718e46a 100644
--- a/content/common/frame_messages.h
+++ b/content/common/frame_messages.h
@@ -74,7 +74,6 @@
 #include "ui/gfx/geometry/rect_f.h"
 #include "ui/gfx/ipc/gfx_param_traits.h"
 #include "ui/gfx/ipc/skia/gfx_skia_param_traits.h"
-#include "ui/gfx/range/range.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 
@@ -622,14 +621,6 @@
 IPC_MESSAGE_ROUTED1(FrameHostMsg_ContextMenu,
                     content::UntrustworthyContextMenuParams)
 
-// Notification that the text selection has changed.
-// Note: The second parameter is the character based offset of the
-// base::string16 text in the document.
-IPC_MESSAGE_ROUTED3(FrameHostMsg_SelectionChanged,
-                    base::string16 /* text covers the selection range */,
-                    uint32_t /* the offset of the text in the document */,
-                    gfx::Range /* selection range in the document */)
-
 // Adding a new message? Stick to the sort order above: first platform
 // independent FrameMsg, then ifdefs for platform specific FrameMsg, then
 // platform independent FrameHostMsg, then ifdefs for platform specific
diff --git a/content/common/view_messages.h b/content/common/view_messages.h
index f62ff600..3f7794f8 100644
--- a/content/common/view_messages.h
+++ b/content/common/view_messages.h
@@ -96,7 +96,7 @@
 
 // This passes a set of webkit preferences down to the renderer.
 IPC_MESSAGE_ROUTED1(ViewMsg_UpdateWebPreferences,
-                    content::WebPreferences)
+                    blink::web_pref::WebPreferences)
 
 // Notification that a move or resize renderer's containing window has
 // started.
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index a79acda..2d2bfc7e 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -370,7 +370,7 @@
 
 bool ContentBrowserClient::OverrideWebPreferencesAfterNavigation(
     WebContents* web_contents,
-    WebPreferences* prefs) {
+    blink::web_pref::WebPreferences* prefs) {
   return false;
 }
 
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index 754d7efd..718fc09 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -91,6 +91,9 @@
 class WindowFeatures;
 enum class WebFeature : int32_t;
 }  // namespace mojom
+namespace web_pref {
+struct WebPreferences;
+}  // namespace web_pref
 class AssociatedInterfaceRegistry;
 class URLLoaderThrottle;
 }  // namespace blink
@@ -223,7 +226,6 @@
 struct PepperPluginInfo;
 struct Referrer;
 struct SocketPermissionRequest;
-struct WebPreferences;
 
 #if defined(OS_ANDROID)
 class TtsEnvironmentAndroid;
@@ -891,14 +893,15 @@
   // the renderer. The content layer will add its own settings, and then it's up
   // to the embedder to update it if it wants.
   virtual void OverrideWebkitPrefs(RenderViewHost* render_view_host,
-                                   WebPreferences* prefs) {}
+                                   blink::web_pref::WebPreferences* prefs) {}
 
   // Similar to OverrideWebkitPrefs, but is only called after navigations. Some
   // attributes in WebPreferences might need its value updated after navigation,
   // and this method will give the opportunity for embedder to update them.
   // Returns true if some values |prefs| changed due to embedder override.
-  virtual bool OverrideWebPreferencesAfterNavigation(WebContents* web_contents,
-                                                     WebPreferences* prefs);
+  virtual bool OverrideWebPreferencesAfterNavigation(
+      WebContents* web_contents,
+      blink::web_pref::WebPreferences* prefs);
 
   // Notifies that BrowserURLHandler has been created, so that the embedder can
   // optionally add their own handlers.
diff --git a/content/public/browser/notification_database_data.h b/content/public/browser/notification_database_data.h
index 4cb6253..f3708799 100644
--- a/content/public/browser/notification_database_data.h
+++ b/content/public/browser/notification_database_data.h
@@ -46,6 +46,8 @@
   std::string notification_id;
 
   // Origin of the website this notification is associated with.
+  // TODO(https://crbug.com/1095896): Consider making |origin| a url::Origin
+  // field.
   GURL origin;
 
   // Id of the Service Worker registration this notification is associated with.
diff --git a/content/public/browser/presentation_request.cc b/content/public/browser/presentation_request.cc
index b8df3bc..eda1361 100644
--- a/content/public/browser/presentation_request.cc
+++ b/content/public/browser/presentation_request.cc
@@ -22,4 +22,10 @@
 PresentationRequest& PresentationRequest::operator=(
     const PresentationRequest& other) = default;
 
+bool PresentationRequest::operator==(const PresentationRequest& other) const {
+  return render_frame_host_id == other.render_frame_host_id &&
+         presentation_urls == other.presentation_urls &&
+         frame_origin == other.frame_origin;
+}
+
 }  // namespace content
diff --git a/content/public/browser/presentation_request.h b/content/public/browser/presentation_request.h
index 224095a..2ec45e0 100644
--- a/content/public/browser/presentation_request.h
+++ b/content/public/browser/presentation_request.h
@@ -28,6 +28,11 @@
   PresentationRequest(const PresentationRequest& other);
   PresentationRequest& operator=(const PresentationRequest& other);
 
+  bool operator==(const PresentationRequest& other) const;
+  bool operator!=(const PresentationRequest& other) const {
+    return !(*this == other);
+  }
+
   // ID of RenderFrameHost that initiated the request.
   GlobalFrameRoutingId render_frame_host_id;
 
diff --git a/content/public/browser/push_messaging_service.cc b/content/public/browser/push_messaging_service.cc
index 1ef27ec..aeec023 100644
--- a/content/public/browser/push_messaging_service.cc
+++ b/content/public/browser/push_messaging_service.cc
@@ -64,7 +64,7 @@
     base::OnceClosure callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   service_worker_context->StoreRegistrationUserData(
-      service_worker_registration_id, origin,
+      service_worker_registration_id, url::Origin::Create(origin),
       {{kPushRegistrationIdServiceWorkerKey, subscription_id}},
       base::BindOnce(&CallClosureFromIO, std::move(callback)));
 }
@@ -79,7 +79,7 @@
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   service_worker_context->StoreRegistrationUserData(
-      service_worker_registration_id, origin,
+      service_worker_registration_id, url::Origin::Create(origin),
       {{kPushRegistrationIdServiceWorkerKey, subscription_id},
        {kPushSenderIdServiceWorkerKey, sender_id}},
       base::BindOnce(&CallClosureFromIO, std::move(callback)));
diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h
index 54e5573..7a4675d8 100644
--- a/content/public/browser/web_contents.h
+++ b/content/public/browser/web_contents.h
@@ -55,6 +55,9 @@
 namespace mojom {
 class RendererPreferences;
 }
+namespace web_pref {
+struct WebPreferences;
+}
 struct Manifest;
 struct UserAgentOverride;
 }  // namespace blink
@@ -89,7 +92,6 @@
 struct CustomContextMenuContext;
 struct DropData;
 struct MHTMLGenerationParams;
-struct WebPreferences;
 
 // WebContents is the core class in content/. A WebContents renders web content
 // (usually HTML) in a rectangular area.
@@ -978,7 +980,8 @@
   // WebPreferences. If we want to guarantee that the value reflects the current
   // state of the WebContents, NotifyPreferencesChanged() should be called
   // before calling this.
-  virtual const WebPreferences& GetOrCreateWebPreferences() = 0;
+  virtual const blink::web_pref::WebPreferences&
+  GetOrCreateWebPreferences() = 0;
 
   // Notify this WebContents that the preferences have changed, so it needs to
   // recompute the current WebPreferences based on the current state of the
@@ -1008,7 +1011,8 @@
   // recomputation logic in either of those functions.
   // TODO(rakina): Try to make values set through this function stick even after
   // recomputations.
-  virtual void SetWebPreferences(const WebPreferences& prefs) = 0;
+  virtual void SetWebPreferences(
+      const blink::web_pref::WebPreferences& prefs) = 0;
 
   // Passes current web preferences to all renderer in this WebContents after
   // possibly recomputing them as follows: all "fast" preferences (those not
diff --git a/content/public/common/BUILD.gn b/content/public/common/BUILD.gn
index 3933b8d..9c024d45 100644
--- a/content/public/common/BUILD.gn
+++ b/content/public/common/BUILD.gn
@@ -177,8 +177,6 @@
     "use_zoom_for_dsf_policy.cc",
     "use_zoom_for_dsf_policy.h",
     "user_agent.h",
-    "web_preferences.cc",
-    "web_preferences.h",
     "webplugininfo.cc",
     "webplugininfo.h",
     "webplugininfo_param_traits.h",
@@ -297,9 +295,9 @@
   # Must depend on //content/public/common/ instead, for component build.
   visibility = [
     ":common_sources",
-    "//content/common/*",
     "//chrome/common:mojo_bindings",
     "//chrome/common:mojo_bindings_blink",
+    "//content/common/*",
     "//extensions/common:mojom",
     "//extensions/common:mojom_blink",
   ]
diff --git a/content/public/common/common_param_traits_macros.h b/content/public/common/common_param_traits_macros.h
index 8e81087..e73aeb50 100644
--- a/content/public/common/common_param_traits_macros.h
+++ b/content/public/common/common_param_traits_macros.h
@@ -12,13 +12,13 @@
 #include "content/public/common/browser_controls_state.h"
 #include "content/public/common/drop_data.h"
 #include "content/public/common/referrer.h"
-#include "content/public/common/web_preferences.h"
 #include "content/public/common/webplugininfo_param_traits.h"
 #include "ipc/ipc_message_macros.h"
 #include "services/network/public/cpp/network_ipc_param_traits.h"
 #include "services/network/public/mojom/referrer_policy.mojom.h"
 #include "third_party/blink/public/common/page/web_drag_operation.h"
 #include "third_party/blink/public/common/security/security_style.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "third_party/blink/public/mojom/devtools/console_message.mojom.h"
 #include "third_party/blink/public/mojom/permissions/permission_status.mojom.h"
 #include "third_party/blink/public/mojom/renderer_preferences.mojom.h"
@@ -53,8 +53,8 @@
 IPC_ENUM_TRAITS_MAX_VALUE(blink::SecurityStyle, blink::SecurityStyle::kMaxValue)
 IPC_ENUM_TRAITS_MAX_VALUE(blink::mojom::PermissionStatus,
                           blink::mojom::PermissionStatus::LAST)
-IPC_ENUM_TRAITS_MAX_VALUE(content::EditingBehavior,
-                          content::EDITING_BEHAVIOR_LAST)
+IPC_ENUM_TRAITS_MAX_VALUE(blink::web_pref::EditingBehavior,
+                          blink::web_pref::EDITING_BEHAVIOR_LAST)
 IPC_ENUM_TRAITS_MAX_VALUE(WindowOpenDisposition,
                           WindowOpenDisposition::MAX_VALUE)
 IPC_ENUM_TRAITS_MAX_VALUE(blink::mojom::V8CacheOptions,
@@ -65,16 +65,17 @@
 IPC_ENUM_TRAITS_MIN_MAX_VALUE(ui::HoverType,
                               ui::HOVER_TYPE_FIRST,
                               ui::HOVER_TYPE_LAST)
-IPC_ENUM_TRAITS_MIN_MAX_VALUE(content::ImageAnimationPolicy,
-                              content::IMAGE_ANIMATION_POLICY_ALLOWED,
-                              content::IMAGE_ANIMATION_POLICY_NO_ANIMATION)
-IPC_ENUM_TRAITS_MIN_MAX_VALUE(content::ViewportStyle,
-                              content::ViewportStyle::DEFAULT,
-                              content::ViewportStyle::LAST)
 IPC_ENUM_TRAITS_MIN_MAX_VALUE(
-    content::AutoplayPolicy,
-    content::AutoplayPolicy::kNoUserGestureRequired,
-    content::AutoplayPolicy::kDocumentUserActivationRequired)
+    blink::web_pref::ImageAnimationPolicy,
+    blink::web_pref::IMAGE_ANIMATION_POLICY_ALLOWED,
+    blink::web_pref::IMAGE_ANIMATION_POLICY_NO_ANIMATION)
+IPC_ENUM_TRAITS_MIN_MAX_VALUE(blink::web_pref::ViewportStyle,
+                              blink::web_pref::ViewportStyle::DEFAULT,
+                              blink::web_pref::ViewportStyle::LAST)
+IPC_ENUM_TRAITS_MIN_MAX_VALUE(
+    blink::web_pref::AutoplayPolicy,
+    blink::web_pref::AutoplayPolicy::kNoUserGestureRequired,
+    blink::web_pref::AutoplayPolicy::kDocumentUserActivationRequired)
 IPC_ENUM_TRAITS_MIN_MAX_VALUE(blink::PreferredColorScheme,
                               blink::PreferredColorScheme::kDark,
                               blink::PreferredColorScheme::kMaxValue)
@@ -91,7 +92,7 @@
   IPC_STRUCT_TRAITS_MEMBER(policy)
 IPC_STRUCT_TRAITS_END()
 
-IPC_STRUCT_TRAITS_BEGIN(content::WebPreferences)
+IPC_STRUCT_TRAITS_BEGIN(blink::web_pref::WebPreferences)
   IPC_STRUCT_TRAITS_MEMBER(standard_font_family_map)
   IPC_STRUCT_TRAITS_MEMBER(fixed_font_family_map)
   IPC_STRUCT_TRAITS_MEMBER(serif_font_family_map)
diff --git a/content/public/common/web_preferences.typemap b/content/public/common/web_preferences.typemap
index df582b90..8e67242 100644
--- a/content/public/common/web_preferences.typemap
+++ b/content/public/common/web_preferences.typemap
@@ -3,7 +3,8 @@
 # found in the LICENSE file.
 
 mojom = "//content/public/common/web_preferences.mojom"
-public_headers = [ "//content/public/common/web_preferences.h" ]
+public_headers =
+    [ "//third_party/blink/public/common/web_preferences/web_preferences.h" ]
 traits_headers = [ "//content/public/common/common_param_traits_macros.h" ]
 public_deps = [
   # Since the typemappings for browser_controls_state, drop_data and
@@ -18,9 +19,11 @@
   "//services/network/public/cpp:cpp_base",
   "//services/network/public/mojom",
   "//third_party/blink/public:blink_headers",
+  "//third_party/blink/public/common:headers",
   "//ui/accessibility:ax_enums_mojo",
   "//ui/base",
   "//ui/gfx/ipc",
   "//ui/surface",
 ]
-type_mappings = [ "content.mojom.WebPreferences=::content::WebPreferences" ]
+type_mappings =
+    [ "content.mojom.WebPreferences=::blink::web_pref::WebPreferences" ]
diff --git a/content/public/renderer/render_frame.h b/content/public/renderer/render_frame.h
index 788e444..2c414bd 100644
--- a/content/public/renderer/render_frame.h
+++ b/content/public/renderer/render_frame.h
@@ -29,6 +29,9 @@
 #include "ui/accessibility/ax_tree_update.h"
 
 namespace blink {
+namespace web_pref {
+struct WebPreferences;
+}  // namespace web_pref
 class AssociatedInterfaceProvider;
 class AssociatedInterfaceRegistry;
 class BrowserInterfaceBrokerProxy;
@@ -68,7 +71,6 @@
 class RenderView;
 struct UntrustworthyContextMenuParams;
 struct WebPluginInfo;
-struct WebPreferences;
 
 // A class that takes a snapshot of the accessibility tree. Accessibility
 // support in Blink is enabled for the lifetime of this object, which can
@@ -145,7 +147,7 @@
   virtual blink::WebLocalFrame* GetWebFrame() = 0;
 
   // Gets WebKit related preferences associated with this frame.
-  virtual const WebPreferences& GetWebkitPreferences() = 0;
+  virtual const blink::web_pref::WebPreferences& GetWebkitPreferences() = 0;
 
   // Shows a context menu with the given information. The given client will
   // be called with the result.
diff --git a/content/public/renderer/render_view.h b/content/public/renderer/render_view.h
index fbd64f3..50518b3 100644
--- a/content/public/renderer/render_view.h
+++ b/content/public/renderer/render_view.h
@@ -16,6 +16,9 @@
 #include "ui/gfx/native_widget_types.h"
 
 namespace blink {
+namespace web_pref {
+struct WebPreferences;
+}  // namespace web_pref
 class WebView;
 }  // namespace blink
 
@@ -23,7 +26,6 @@
 
 class RenderFrame;
 class RenderViewVisitor;
-struct WebPreferences;
 
 // RenderView corresponds to the content container of a renderer's subset
 // of the frame tree. A frame tree that spans multiple renderers will have a
@@ -55,8 +57,9 @@
   static void ForEach(RenderViewVisitor* visitor);
 
   // Applies WebKit related preferences to this view.
-  static void ApplyWebPreferences(const WebPreferences& preferences,
-                                  blink::WebView* web_view);
+  static void ApplyWebPreferences(
+      const blink::web_pref::WebPreferences& preferences,
+      blink::WebView* web_view);
 
   // Returns the main RenderFrame.
   virtual RenderFrame* GetMainRenderFrame() = 0;
@@ -68,11 +71,12 @@
   virtual float GetZoomLevel() = 0;
 
   // Gets WebKit related preferences associated with this view.
-  virtual const WebPreferences& GetWebkitPreferences() = 0;
+  virtual const blink::web_pref::WebPreferences& GetWebkitPreferences() = 0;
 
   // Overrides the WebKit related preferences associated with this view. Note
   // that the browser process may update the preferences at any time.
-  virtual void SetWebkitPreferences(const WebPreferences& preferences) = 0;
+  virtual void SetWebkitPreferences(
+      const blink::web_pref::WebPreferences& preferences) = 0;
 
   // Returns the associated WebView. May return NULL when the view is closing.
   virtual blink::WebView* GetWebView() = 0;
diff --git a/content/public/test/render_view_test.cc b/content/public/test/render_view_test.cc
index 3997e7e..1cafa36 100644
--- a/content/public/test/render_view_test.cc
+++ b/content/public/test/render_view_test.cc
@@ -443,7 +443,7 @@
   view_params->opener_frame_token = base::nullopt;
   view_params->window_was_created_with_opener = false;
   view_params->renderer_preferences = blink::mojom::RendererPreferences::New();
-  view_params->web_preferences = WebPreferences();
+  view_params->web_preferences = blink::web_pref::WebPreferences();
   view_params->view_id = render_thread_->GetNextRoutingID();
   view_params->main_frame_widget_routing_id =
       render_thread_->GetNextRoutingID();
diff --git a/content/public/test/test_renderer_host.h b/content/public/test/test_renderer_host.h
index d3b9853..955d1a107 100644
--- a/content/public/test/test_renderer_host.h
+++ b/content/public/test/test_renderer_host.h
@@ -35,6 +35,12 @@
 }
 }  // namespace aura
 
+namespace blink {
+namespace web_pref {
+struct WebPreferences;
+}
+}  // namespace blink
+
 namespace display {
 class Screen;
 }
@@ -62,7 +68,6 @@
 class TestRenderWidgetHostFactory;
 class TestNavigationURLLoaderFactory;
 class WebContents;
-struct WebPreferences;
 
 // An interface and utility for driving tests of RenderFrameHost.
 class RenderFrameHostTester {
@@ -162,7 +167,7 @@
   virtual void SimulateWasShown() = 0;
 
   // Promote ComputeWebPreferences to public.
-  virtual WebPreferences TestComputeWebPreferences() = 0;
+  virtual blink::web_pref::WebPreferences TestComputeWebPreferences() = 0;
 };
 
 // You can instantiate only one class like this at a time.  During its
diff --git a/content/renderer/media/media_factory.cc b/content/renderer/media/media_factory.cc
index 74093572..0015471 100644
--- a/content/renderer/media/media_factory.cc
+++ b/content/renderer/media/media_factory.cc
@@ -379,7 +379,7 @@
           media::AudioSinkParameters(/*session_id=*/base::UnguessableToken(),
                                      sink_id.Utf8()));
 
-  const WebPreferences webkit_preferences =
+  const blink::web_pref::WebPreferences webkit_preferences =
       render_frame_->GetWebkitPreferences();
   bool embedded_media_experience_enabled = false;
 #if defined(OS_ANDROID)
diff --git a/content/renderer/pepper/ppapi_preferences_builder.cc b/content/renderer/pepper/ppapi_preferences_builder.cc
index 4d09826..85c5d84 100644
--- a/content/renderer/pepper/ppapi_preferences_builder.cc
+++ b/content/renderer/pepper/ppapi_preferences_builder.cc
@@ -4,14 +4,14 @@
 
 #include "content/renderer/pepper/ppapi_preferences_builder.h"
 
-#include "content/public/common/web_preferences.h"
 #include "gpu/config/gpu_feature_info.h"
 #include "ppapi/shared_impl/ppapi_preferences.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 
 namespace content {
 
 ppapi::Preferences PpapiPreferencesBuilder::Build(
-    const WebPreferences& prefs,
+    const blink::web_pref::WebPreferences& prefs,
     const gpu::GpuFeatureInfo& gpu_feature_info) {
   ppapi::Preferences ppapi_prefs;
   ppapi_prefs.standard_font_family_map = prefs.standard_font_family_map;
diff --git a/content/renderer/pepper/ppapi_preferences_builder.h b/content/renderer/pepper/ppapi_preferences_builder.h
index 0a08fb76..bf34d8a 100644
--- a/content/renderer/pepper/ppapi_preferences_builder.h
+++ b/content/renderer/pepper/ppapi_preferences_builder.h
@@ -12,13 +12,17 @@
 struct Preferences;
 }
 
-namespace content {
-
+namespace blink {
+namespace web_pref {
 struct WebPreferences;
+}
+}  // namespace blink
+
+namespace content {
 
 class PpapiPreferencesBuilder {
  public:
-  static ppapi::Preferences Build(const WebPreferences& prefs,
+  static ppapi::Preferences Build(const blink::web_pref::WebPreferences& prefs,
                                   const gpu::GpuFeatureInfo& gpu_feature_info);
 };
 
diff --git a/content/renderer/pepper/ppb_graphics_3d_impl.cc b/content/renderer/pepper/ppb_graphics_3d_impl.cc
index 38ffa2e6f..9ab8d16 100644
--- a/content/renderer/pepper/ppb_graphics_3d_impl.cc
+++ b/content/renderer/pepper/ppb_graphics_3d_impl.cc
@@ -15,7 +15,6 @@
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/gpu_stream_constants.h"
-#include "content/public/common/web_preferences.h"
 #include "content/renderer/pepper/host_globals.h"
 #include "content/renderer/pepper/pepper_plugin_instance_impl.h"
 #include "content/renderer/pepper/plugin_instance_throttler_impl.h"
@@ -28,6 +27,7 @@
 #include "gpu/ipc/client/gpu_channel_host.h"
 #include "ppapi/c/ppp_graphics_3d.h"
 #include "ppapi/thunk/enter.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/web/web_console_message.h"
 #include "third_party/blink/public/web/web_document.h"
@@ -217,7 +217,8 @@
   if (!render_frame)
     return false;
 
-  const WebPreferences& prefs = render_frame->GetWebkitPreferences();
+  const blink::web_pref::WebPreferences& prefs =
+      render_frame->GetWebkitPreferences();
 
   // 3D access might be disabled.
   if (!prefs.pepper_3d_enabled)
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index fde431556..4e24877d 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -2828,7 +2828,7 @@
   return frame_;
 }
 
-const WebPreferences& RenderFrameImpl::GetWebkitPreferences() {
+const blink::web_pref::WebPreferences& RenderFrameImpl::GetWebkitPreferences() {
   return render_view_->GetWebkitPreferences();
 }
 
@@ -2973,8 +2973,8 @@
 void RenderFrameImpl::SetSelectedText(const base::string16& selection_text,
                                       size_t offset,
                                       const gfx::Range& range) {
-  Send(new FrameHostMsg_SelectionChanged(routing_id_, selection_text,
-                                         static_cast<uint32_t>(offset), range));
+  GetWebFrame()->TextSelectionChanged(WebString::FromUTF16(selection_text),
+                                      static_cast<uint32_t>(offset), range);
 }
 
 void RenderFrameImpl::SetDeviceScaleFactorOnRenderView(
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index 0578b94..5ba5acd 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -428,7 +428,7 @@
   std::unique_ptr<AXTreeSnapshotter> CreateAXTreeSnapshotter() override;
   int GetRoutingID() override;
   blink::WebLocalFrame* GetWebFrame() override;
-  const WebPreferences& GetWebkitPreferences() override;
+  const blink::web_pref::WebPreferences& GetWebkitPreferences() override;
   int ShowContextMenu(ContextMenuClient* client,
                       const UntrustworthyContextMenuParams& params) override;
   void CancelContextMenu(int request_id) override;
diff --git a/content/renderer/render_view_browsertest.cc b/content/renderer/render_view_browsertest.cc
index f7238ffd..e82a95a 100644
--- a/content/renderer/render_view_browsertest.cc
+++ b/content/renderer/render_view_browsertest.cc
@@ -234,6 +234,42 @@
   return params;
 }
 
+template <class MockedLocalFrameHostInterceptor>
+class MockedLocalFrameHostInterceptorTestRenderFrame : public TestRenderFrame {
+ public:
+  static RenderFrameImpl* CreateTestRenderFrame(
+      RenderFrameImpl::CreateParams params) {
+    return new MockedLocalFrameHostInterceptorTestRenderFrame(
+        std::move(params));
+  }
+
+  ~MockedLocalFrameHostInterceptorTestRenderFrame() override = default;
+
+  blink::AssociatedInterfaceProvider* GetRemoteAssociatedInterfaces() override {
+    blink::AssociatedInterfaceProvider* associated_interface_provider =
+        RenderFrameImpl::GetRemoteAssociatedInterfaces();
+
+    // Attach our fake local frame host at the very first call to
+    // GetRemoteAssociatedInterfaces.
+    if (!local_frame_host_) {
+      local_frame_host_ = std::make_unique<MockedLocalFrameHostInterceptor>(
+          associated_interface_provider);
+    }
+    return associated_interface_provider;
+  }
+
+  MockedLocalFrameHostInterceptor* mock_local_frame_host() {
+    return local_frame_host_.get();
+  }
+
+ private:
+  explicit MockedLocalFrameHostInterceptorTestRenderFrame(
+      RenderFrameImpl::CreateParams params)
+      : TestRenderFrame(std::move(params)) {}
+
+  std::unique_ptr<MockedLocalFrameHostInterceptor> local_frame_host_;
+};
+
 }  // namespace
 
 class RenderViewImplTest : public RenderViewTest {
@@ -838,50 +874,19 @@
                void(const base::Optional<::base::string16>& title,
                     base::i18n::TextDirection title_direction));
 };
-
-class UpdateTitleTestRenderFrame : public TestRenderFrame {
- public:
-  static RenderFrameImpl* CreateTestRenderFrame(
-      RenderFrameImpl::CreateParams params) {
-    return new UpdateTitleTestRenderFrame(std::move(params));
-  }
-
-  ~UpdateTitleTestRenderFrame() override = default;
-
-  blink::AssociatedInterfaceProvider* GetRemoteAssociatedInterfaces() override {
-    blink::AssociatedInterfaceProvider* associated_interface_provider =
-        RenderFrameImpl::GetRemoteAssociatedInterfaces();
-
-    // Attach our fake local frame host at the very first call to
-    // GetRemoteAssociatedInterfaces.
-    if (!local_frame_host_) {
-      local_frame_host_ = std::make_unique<UpdateTitleLocalFrameHost>(
-          associated_interface_provider);
-    }
-    return associated_interface_provider;
-  }
-
-  UpdateTitleLocalFrameHost* title_mock_frame_host() {
-    return local_frame_host_.get();
-  }
-
- private:
-  explicit UpdateTitleTestRenderFrame(RenderFrameImpl::CreateParams params)
-      : TestRenderFrame(std::move(params)) {}
-
-  std::unique_ptr<UpdateTitleLocalFrameHost> local_frame_host_;
-};
 }  // namespace
 
 class RenderViewImplUpdateTitleTest : public RenderViewImplTest {
  public:
+  using MockedTestRenderFrame =
+      MockedLocalFrameHostInterceptorTestRenderFrame<UpdateTitleLocalFrameHost>;
+
   RenderViewImplUpdateTitleTest()
-      : RenderViewImplTest(&UpdateTitleTestRenderFrame::CreateTestRenderFrame) {
-  }
+      : RenderViewImplTest(&MockedTestRenderFrame::CreateTestRenderFrame) {}
 
   UpdateTitleLocalFrameHost* title_mock_frame_host() {
-    return static_cast<UpdateTitleTestRenderFrame*>(frame())
-        ->title_mock_frame_host();
+    return static_cast<MockedTestRenderFrame*>(frame())
+        ->mock_local_frame_host();
   }
 };
 
@@ -2495,61 +2500,63 @@
   EXPECT_TRUE(view()->main_render_frame_);
 }
 
-class MessageOrderFakeRenderWidgetHost : public FakeRenderWidgetHost,
-                                         public IPC::Listener {
+namespace {
+class MessageOrderFakeRenderWidgetHost : public FakeRenderWidgetHost {
  public:
-  void TextInputStateChanged(ui::mojom::TextInputStatePtr state) override {
-    message_counter_++;
-    last_input_type_ = message_counter_;
-  }
-
-  ~MessageOrderFakeRenderWidgetHost() override {}
-
-  bool OnMessageReceived(const IPC::Message& message) override {
-    if (message.type() == FrameHostMsg_SelectionChanged::ID) {
-      base::RunLoop().RunUntilIdle();
-      message_counter_++;
-      last_selection_ = message_counter_;
-    }
-    return false;
-  }
-
-  uint32_t message_counter_ = 0;
-  uint32_t last_selection_ = 0;
-  uint32_t last_input_type_ = 0;
+  MOCK_METHOD1(TextInputStateChanged, void(ui::mojom::TextInputStatePtr state));
 };
 
+class TextSelectionChangedLocalFrameHost : public LocalFrameHostInterceptor {
+ public:
+  explicit TextSelectionChangedLocalFrameHost(
+      blink::AssociatedInterfaceProvider* provider)
+      : LocalFrameHostInterceptor(provider) {}
+  MOCK_METHOD3(TextSelectionChanged,
+               void(const base::string16& text,
+                    uint32_t offset,
+                    const gfx::Range& range));
+};
+}  // namespace
+
 class RenderViewImplTextInputMessageOrder : public RenderViewImplTest {
  public:
+  using MockedTestRenderFrame = MockedLocalFrameHostInterceptorTestRenderFrame<
+      TextSelectionChangedLocalFrameHost>;
+
+  RenderViewImplTextInputMessageOrder()
+      : RenderViewImplTest(&MockedTestRenderFrame::CreateTestRenderFrame) {}
+
   std::unique_ptr<FakeRenderWidgetHost> CreateRenderWidgetHost() override {
-    auto host = std::make_unique<MessageOrderFakeRenderWidgetHost>();
-    render_thread_->sink().AddFilter(host.get());
-    return host;
+    return std::make_unique<MessageOrderFakeRenderWidgetHost>();
   }
 
   MessageOrderFakeRenderWidgetHost* GetMessageOrderFakeRenderWidgetHost() {
     return static_cast<MessageOrderFakeRenderWidgetHost*>(
         render_widget_host_.get());
   }
+
+  TextSelectionChangedLocalFrameHost* GetMockLocalFrameHost() {
+    return static_cast<MockedTestRenderFrame*>(frame())
+        ->mock_local_frame_host();
+  }
 };
 
 TEST_F(RenderViewImplTextInputMessageOrder, MessageOrderInDidChangeSelection) {
   LoadHTML("<textarea id=\"test\"></textarea>");
 
+  // TextInputStateChanged should be called earlier than TextSelectionChanged.
+  testing::InSequence sequence;
+
+  // TextInputStateChanged and TextSelectionChanged should be called once each.
+  EXPECT_CALL(*GetMessageOrderFakeRenderWidgetHost(),
+              TextInputStateChanged(testing::_))
+      .Times(1);
+  EXPECT_CALL(*GetMockLocalFrameHost(),
+              TextSelectionChanged(testing::_, testing::_, testing::_))
+      .Times(1);
+
   main_widget()->SetHandlingInputEvent(true);
   ExecuteJavaScriptForTests("document.getElementById('test').focus();");
-
-  uint32_t last_input_type =
-      GetMessageOrderFakeRenderWidgetHost()->last_input_type_;
-  uint32_t last_selection =
-      GetMessageOrderFakeRenderWidgetHost()->last_selection_;
-
-  EXPECT_NE(0u, last_input_type);
-  EXPECT_NE(0u, last_selection);
-
-  // InputTypeChange shold be called earlier than SelectionChanged.
-  EXPECT_LT(last_input_type, last_selection);
-  render_thread_->sink().RemoveFilter(GetMessageOrderFakeRenderWidgetHost());
 }
 
 class RendererErrorPageTest : public RenderViewImplTest {
@@ -3023,50 +3030,19 @@
                void(const base::string16& alert_message,
                     RunModalAlertDialogCallback callback));
 };
-
-class AlertDialogTestRenderFrame : public TestRenderFrame {
- public:
-  static RenderFrameImpl* CreateTestRenderFrame(
-      RenderFrameImpl::CreateParams params) {
-    return new AlertDialogTestRenderFrame(std::move(params));
-  }
-
-  ~AlertDialogTestRenderFrame() override = default;
-
-  blink::AssociatedInterfaceProvider* GetRemoteAssociatedInterfaces() override {
-    blink::AssociatedInterfaceProvider* associated_interface_provider =
-        RenderFrameImpl::GetRemoteAssociatedInterfaces();
-
-    // Attach our fake local frame host at the very first call to
-    // GetRemoteAssociatedInterfaces.
-    if (!local_frame_host_) {
-      local_frame_host_ = std::make_unique<AlertDialogMockLocalFrameHost>(
-          associated_interface_provider);
-    }
-    return associated_interface_provider;
-  }
-
-  AlertDialogMockLocalFrameHost* alert_mock_frame_host() {
-    return local_frame_host_.get();
-  }
-
- private:
-  explicit AlertDialogTestRenderFrame(RenderFrameImpl::CreateParams params)
-      : TestRenderFrame(std::move(params)) {}
-
-  std::unique_ptr<AlertDialogMockLocalFrameHost> local_frame_host_;
-};
 }  // namespace
 
 class RenderViewImplModalDialogTest : public RenderViewImplTest {
  public:
+  using MockedTestRenderFrame = MockedLocalFrameHostInterceptorTestRenderFrame<
+      AlertDialogMockLocalFrameHost>;
+
   RenderViewImplModalDialogTest()
-      : RenderViewImplTest(&AlertDialogTestRenderFrame::CreateTestRenderFrame) {
-  }
+      : RenderViewImplTest(&MockedTestRenderFrame::CreateTestRenderFrame) {}
 
   AlertDialogMockLocalFrameHost* alert_mock_frame_host() {
-    return static_cast<AlertDialogTestRenderFrame*>(frame())
-        ->alert_mock_frame_host();
+    return static_cast<MockedTestRenderFrame*>(frame())
+        ->mock_local_frame_host();
   }
 };
 
@@ -3110,7 +3086,7 @@
   EXPECT_EQ(1.f, view()->GetWebView()->PageScaleFactor());
   EXPECT_EQ(1.f, view()->GetWebView()->MinimumPageScaleFactor());
 
-  WebPreferences prefs;
+  blink::web_pref::WebPreferences prefs;
   prefs.shrinks_viewport_contents_to_fit = true;
   prefs.default_minimum_page_scale_factor = 0.1f;
   prefs.default_maximum_page_scale_factor = 5.5f;
diff --git a/content/renderer/render_view_browsertest_mac.mm b/content/renderer/render_view_browsertest_mac.mm
index 8b410537..233c51a 100644
--- a/content/renderer/render_view_browsertest_mac.mm
+++ b/content/renderer/render_view_browsertest_mac.mm
@@ -11,10 +11,10 @@
 #include "content/common/input_messages.h"
 #include "content/common/unfreezable_frame_messages.h"
 #include "content/public/browser/native_web_keyboard_event.h"
-#include "content/public/common/web_preferences.h"
 #include "content/public/test/render_view_test.h"
 #include "content/renderer/render_view_impl.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "third_party/blink/public/web/web_frame_content_dumper.h"
 #include "third_party/blink/public/web/web_frame_widget.h"
 #include "third_party/blink/public/web/web_local_frame.h"
@@ -79,7 +79,7 @@
       "  }"
       "</script>";
 
-  WebPreferences prefs;
+  blink::web_pref::WebPreferences prefs;
   prefs.enable_scroll_animator = false;
 
   RenderViewImpl* view = static_cast<RenderViewImpl*>(view_);
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index 209362d..c9e6bdc 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -56,7 +56,6 @@
 #include "content/public/common/referrer_type_converters.h"
 #include "content/public/common/three_d_api_types.h"
 #include "content/public/common/url_constants.h"
-#include "content/public/common/web_preferences.h"
 #include "content/public/renderer/content_renderer_client.h"
 #include "content/public/renderer/document_state.h"
 #include "content/public/renderer/render_thread.h"
@@ -94,6 +93,7 @@
 #include "third_party/blink/public/common/dom_storage/session_storage_namespace_id.h"
 #include "third_party/blink/public/common/frame/user_activation_update_source.h"
 #include "third_party/blink/public/common/input/web_input_event.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "third_party/blink/public/platform/file_path_conversion.h"
 #include "third_party/blink/public/platform/modules/video_capture/web_video_capture_impl_manager.h"
 #include "third_party/blink/public/platform/url_conversion.h"
@@ -331,7 +331,7 @@
   }
 }
 
-void ApplyFontsFromMap(const ScriptFontFamilyMap& map,
+void ApplyFontsFromMap(const blink::web_pref::ScriptFontFamilyMap& map,
                        SetFontFamilyWrapper setter,
                        WebSettings* settings) {
   for (auto it = map.begin(); it != map.end(); ++it) {
@@ -546,8 +546,9 @@
 }
 
 /*static*/
-void RenderView::ApplyWebPreferences(const WebPreferences& prefs,
-                                     WebView* web_view) {
+void RenderView::ApplyWebPreferences(
+    const blink::web_pref::WebPreferences& prefs,
+    WebView* web_view) {
   WebSettings* settings = web_view->GetSettings();
   ApplyFontsFromMap(prefs.standard_font_family_map,
                     SetStandardFontFamilyWrapper, settings);
@@ -799,15 +800,15 @@
   settings->SetAccessibilityAlwaysShowFocus(prefs.always_show_focus);
 
   switch (prefs.autoplay_policy) {
-    case AutoplayPolicy::kNoUserGestureRequired:
+    case blink::web_pref::AutoplayPolicy::kNoUserGestureRequired:
       settings->SetAutoplayPolicy(
           WebSettings::AutoplayPolicy::kNoUserGestureRequired);
       break;
-    case AutoplayPolicy::kUserGestureRequired:
+    case blink::web_pref::AutoplayPolicy::kUserGestureRequired:
       settings->SetAutoplayPolicy(
           WebSettings::AutoplayPolicy::kUserGestureRequired);
       break;
-    case AutoplayPolicy::kDocumentUserActivationRequired:
+    case blink::web_pref::AutoplayPolicy::kDocumentUserActivationRequired:
       settings->SetAutoplayPolicy(
           WebSettings::AutoplayPolicy::kDocumentUserActivationRequired);
       break;
@@ -1529,11 +1530,12 @@
   return webview_->ZoomLevel();
 }
 
-const WebPreferences& RenderViewImpl::GetWebkitPreferences() {
+const blink::web_pref::WebPreferences& RenderViewImpl::GetWebkitPreferences() {
   return webkit_preferences_;
 }
 
-void RenderViewImpl::SetWebkitPreferences(const WebPreferences& preferences) {
+void RenderViewImpl::SetWebkitPreferences(
+    const blink::web_pref::WebPreferences& preferences) {
   OnUpdateWebPreferences(preferences);
 }
 
@@ -1545,7 +1547,8 @@
   return send_content_state_immediately_;
 }
 
-void RenderViewImpl::OnUpdateWebPreferences(const WebPreferences& prefs) {
+void RenderViewImpl::OnUpdateWebPreferences(
+    const blink::web_pref::WebPreferences& prefs) {
   webkit_preferences_ = prefs;
   ApplyWebPreferences(webkit_preferences_, GetWebView());
   ApplyCommandLineToSettings(GetWebView()->GetSettings());
diff --git a/content/renderer/render_view_impl.h b/content/renderer/render_view_impl.h
index 71dd97fd..88f4dad 100644
--- a/content/renderer/render_view_impl.h
+++ b/content/renderer/render_view_impl.h
@@ -32,7 +32,6 @@
 #include "content/public/common/page_visibility_state.h"
 #include "content/public/common/page_zoom.h"
 #include "content/public/common/referrer.h"
-#include "content/public/common/web_preferences.h"
 #include "content/public/renderer/render_view.h"
 #include "content/renderer/render_frame_impl.h"
 #include "content/renderer/render_widget.h"
@@ -43,6 +42,7 @@
 #include "third_party/blink/public/common/dom_storage/session_storage_namespace_id.h"
 #include "third_party/blink/public/common/feature_policy/feature_policy_features.h"
 #include "third_party/blink/public/common/input/web_input_event.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "third_party/blink/public/mojom/renderer_preference_watcher.mojom.h"
 #include "third_party/blink/public/mojom/renderer_preferences.mojom.h"
 #include "third_party/blink/public/platform/web_security_origin.h"
@@ -137,7 +137,7 @@
   // or not.
   bool widgets_never_composited() const { return widgets_never_composited_; }
 
-  const WebPreferences& webkit_preferences() const {
+  const blink::web_pref::WebPreferences& webkit_preferences() const {
     return webkit_preferences_;
   }
 
@@ -241,8 +241,9 @@
   RenderFrameImpl* GetMainRenderFrame() override;
   int GetRoutingID() override;
   float GetZoomLevel() override;
-  const WebPreferences& GetWebkitPreferences() override;
-  void SetWebkitPreferences(const WebPreferences& preferences) override;
+  const blink::web_pref::WebPreferences& GetWebkitPreferences() override;
+  void SetWebkitPreferences(
+      const blink::web_pref::WebPreferences& preferences) override;
   blink::WebView* GetWebView() override;
   bool GetContentStateImmediately() override;
   const std::string& GetAcceptLanguages() override;
@@ -379,7 +380,7 @@
   void OnSetRendererPrefs(
       const blink::mojom::RendererPreferences& renderer_prefs);
   void OnSuppressDialogsUntilSwapOut();
-  void OnUpdateWebPreferences(const WebPreferences& prefs);
+  void OnUpdateWebPreferences(const blink::web_pref::WebPreferences& prefs);
 
   // Page message handlers -----------------------------------------------------
   void SetPageFrozen(bool frozen);
@@ -455,7 +456,7 @@
 
   // Settings ------------------------------------------------------------------
 
-  WebPreferences webkit_preferences_;
+  blink::web_pref::WebPreferences webkit_preferences_;
   blink::mojom::RendererPreferences renderer_preferences_;
   // These are observing changes in |renderer_preferences_|. This is used for
   // keeping WorkerFetchContext in sync.
diff --git a/content/shell/browser/shell_content_browser_client.cc b/content/shell/browser/shell_content_browser_client.cc
index 491e16e..7ebc20d 100644
--- a/content/shell/browser/shell_content_browser_client.cc
+++ b/content/shell/browser/shell_content_browser_client.cc
@@ -31,7 +31,6 @@
 #include "content/public/common/content_switches.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/common/user_agent.h"
-#include "content/public/common/web_preferences.h"
 #include "content/shell/browser/shell.h"
 #include "content/shell/browser/shell_browser_context.h"
 #include "content/shell/browser/shell_browser_main_parts.h"
@@ -48,6 +47,7 @@
 #include "services/network/public/mojom/network_service.mojom.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/user_agent/user_agent_metadata.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_switches.h"
 #include "url/gurl.h"
@@ -284,7 +284,7 @@
 
 void ShellContentBrowserClient::OverrideWebkitPrefs(
     RenderViewHost* render_view_host,
-    WebPreferences* prefs) {
+    blink::web_pref::WebPreferences* prefs) {
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kForceDarkMode)) {
     prefs->preferred_color_scheme = blink::PreferredColorScheme::kDark;
diff --git a/content/shell/browser/shell_content_browser_client.h b/content/shell/browser/shell_content_browser_client.h
index 9e0009f..a89e10f0 100644
--- a/content/shell/browser/shell_content_browser_client.h
+++ b/content/shell/browser/shell_content_browser_client.h
@@ -63,7 +63,7 @@
   SpeechRecognitionManagerDelegate* CreateSpeechRecognitionManagerDelegate()
       override;
   void OverrideWebkitPrefs(RenderViewHost* render_view_host,
-                           WebPreferences* prefs) override;
+                           blink::web_pref::WebPreferences* prefs) override;
   base::FilePath GetFontLookupTableCacheDir() override;
   DevToolsManagerDelegate* GetDevToolsManagerDelegate() override;
   void ExposeInterfacesToRenderer(
@@ -173,7 +173,8 @@
   }
 
   void set_override_web_preferences_callback(
-      base::RepeatingCallback<void(WebPreferences*)> callback) {
+      base::RepeatingCallback<void(blink::web_pref::WebPreferences*)>
+          callback) {
     override_web_preferences_callback_ = std::move(callback);
   }
 
@@ -216,7 +217,7 @@
   base::RepeatingCallback<std::vector<std::unique_ptr<NavigationThrottle>>(
       NavigationHandle*)>
       create_throttles_for_navigation_callback_;
-  base::RepeatingCallback<void(WebPreferences*)>
+  base::RepeatingCallback<void(blink::web_pref::WebPreferences*)>
       override_web_preferences_callback_;
 
   // Owned by content::BrowserMainLoop.
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
index fb73370..85e4be0 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
@@ -69,7 +69,6 @@
 # ========================
 
 # Failing new updated tests
-crbug.com/1108588 conformance/glsl/bugs/character-set.html [ Failure ]
 crbug.com/1108044 conformance2/renderbuffers/readbuffer.html [ Failure ]
 crbug.com/1108086 [ no-passthrough ] conformance2/renderbuffers/framebuffer-object-attachment.html [ Failure ]
 crbug.com/angleproject/4807 [ win passthrough ] conformance2/glsl3/switch-case.html [ Failure ]
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
index 9dab132..adb09a41 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -125,7 +125,6 @@
 # Fails on all platforms
 
 crbug.com/1110449 [ linux passthrough ] conformance/misc/type-conversion-test.html [ Failure ]
-crbug.com/1108588 conformance/glsl/bugs/character-set.html [ Failure ]
 crbug.com/1081973 conformance/buffers/buffer-data-and-buffer-sub-data.html [ Failure ]
 crbug.com/1082533 [ mac intel ] conformance/textures/misc/texture-copying-and-deletion.html [ Failure ]
 crbug.com/1082533 [ win vulkan passthrough ] conformance/textures/misc/texture-copying-and-deletion.html [ Failure ]
@@ -643,6 +642,7 @@
 crbug.com/1083320 [ android qualcomm-adreno-(tm)-430 ] conformance/misc/uninitialized-test.html [ Skip ]
 crbug.com/1056830 [ android qualcomm-adreno-(tm)-430 ] conformance/extensions/webgl-compressed-texture-astc.html [ Failure ]
 crbug.com/1122644 [ android qualcomm-adreno-(tm)-420 ] conformance/textures/misc/texture-upload-size.html [ Skip ]
+crbug.com/1127140 [ android qualcomm-adreno-(tm)-420 ] conformance/extensions/webgl-draw-buffers-framebuffer-unsupported.html [ Skip ]
 
 # This test is skipped because running it causes a future test to fail.
 # The list of tests which may be that future test is very long. It is
diff --git a/content/test/stub_render_widget_host_owner_delegate.cc b/content/test/stub_render_widget_host_owner_delegate.cc
index e2202b2..107d61d9 100644
--- a/content/test/stub_render_widget_host_owner_delegate.cc
+++ b/content/test/stub_render_widget_host_owner_delegate.cc
@@ -4,7 +4,7 @@
 
 #include "content/test/stub_render_widget_host_owner_delegate.h"
 
-#include "content/public/common/web_preferences.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 
 namespace content {
 
@@ -25,7 +25,7 @@
   return false;
 }
 
-WebPreferences
+blink::web_pref::WebPreferences
 StubRenderWidgetHostOwnerDelegate::GetWebkitPreferencesForWidget() {
   return {};
 }
diff --git a/content/test/stub_render_widget_host_owner_delegate.h b/content/test/stub_render_widget_host_owner_delegate.h
index 9465f9d9..3cf79586 100644
--- a/content/test/stub_render_widget_host_owner_delegate.h
+++ b/content/test/stub_render_widget_host_owner_delegate.h
@@ -25,7 +25,7 @@
   void SetBackgroundOpaque(bool opaque) override {}
   bool IsMainFrameActive() override;
   bool IsNeverComposited() override;
-  WebPreferences GetWebkitPreferencesForWidget() override;
+  blink::web_pref::WebPreferences GetWebkitPreferencesForWidget() override;
 };
 
 }  // namespace content
diff --git a/content/test/test_render_view_host.cc b/content/test/test_render_view_host.cc
index 2c3aac7..5cd069a9 100644
--- a/content/test/test_render_view_host.cc
+++ b/content/test/test_render_view_host.cc
@@ -26,12 +26,12 @@
 #include "content/public/browser/storage_partition.h"
 #include "content/public/common/content_client.h"
 #include "content/public/common/page_state.h"
-#include "content/public/common/web_preferences.h"
 #include "content/test/test_render_frame_host.h"
 #include "content/test/test_render_view_host.h"
 #include "content/test/test_web_contents.h"
 #include "media/base/video_frame.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "ui/aura/env.h"
 #include "ui/compositor/compositor.h"
 #include "ui/compositor/layer_type.h"
@@ -311,7 +311,8 @@
   GetWidget()->WasShown({} /* record_tab_switch_time_request */);
 }
 
-WebPreferences TestRenderViewHost::TestComputeWebPreferences() {
+blink::web_pref::WebPreferences
+TestRenderViewHost::TestComputeWebPreferences() {
   return static_cast<WebContentsImpl*>(WebContents::FromRenderViewHost(this))
       ->ComputeWebPreferences();
 }
diff --git a/content/test/test_render_view_host.h b/content/test/test_render_view_host.h
index 07ff67b1..c4817009 100644
--- a/content/test/test_render_view_host.h
+++ b/content/test/test_render_view_host.h
@@ -17,9 +17,9 @@
 #include "components/viz/host/host_frame_sink_client.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
 #include "content/browser/renderer_host/render_widget_host_view_base.h"
-#include "content/public/common/web_preferences.h"
 #include "content/public/test/mock_render_process_host.h"
 #include "content/public/test/test_renderer_host.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "ui/base/ime/dummy_text_input_client.h"
 #include "ui/base/layout.h"
 #include "ui/base/page_transition_types.h"
@@ -195,7 +195,7 @@
   // RenderViewHostImpl, see below.
   void SimulateWasHidden() override;
   void SimulateWasShown() override;
-  WebPreferences TestComputeWebPreferences() override;
+  blink::web_pref::WebPreferences TestComputeWebPreferences() override;
 
   void TestOnUpdateStateWithFile(const base::FilePath& file_path);
 
diff --git a/content/web_test/browser/web_test_content_browser_client.cc b/content/web_test/browser/web_test_content_browser_client.cc
index 751db47..53e938e 100644
--- a/content/web_test/browser/web_test_content_browser_client.cc
+++ b/content/web_test/browser/web_test_content_browser_client.cc
@@ -60,6 +60,7 @@
 #include "services/service_manager/public/cpp/manifest_builder.h"
 #include "storage/browser/quota/quota_settings.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "ui/base/ui_base_switches.h"
 #include "url/origin.h"
 
@@ -253,7 +254,7 @@
 
 void WebTestContentBrowserClient::OverrideWebkitPrefs(
     RenderViewHost* render_view_host,
-    WebPreferences* prefs) {
+    blink::web_pref::WebPreferences* prefs) {
   if (WebTestControlHost::Get())
     WebTestControlHost::Get()->OverrideWebkitPrefs(prefs);
 }
diff --git a/content/web_test/browser/web_test_content_browser_client.h b/content/web_test/browser/web_test_content_browser_client.h
index 01ca5997..b96c23e 100644
--- a/content/web_test/browser/web_test_content_browser_client.h
+++ b/content/web_test/browser/web_test_content_browser_client.h
@@ -23,6 +23,12 @@
 #include "third_party/blink/public/mojom/permissions/permission_automation.mojom-forward.h"
 #include "third_party/blink/public/mojom/storage_access/storage_access_automation.mojom-forward.h"
 
+namespace blink {
+namespace web_pref {
+struct WebPreferences;
+}
+}  // namespace blink
+
 namespace content {
 class FakeBluetoothChooser;
 class FakeBluetoothChooserFactory;
@@ -57,7 +63,7 @@
       blink::AssociatedInterfaceRegistry* associated_registry,
       RenderProcessHost* render_process_host) override;
   void OverrideWebkitPrefs(RenderViewHost* render_view_host,
-                           WebPreferences* prefs) override;
+                           blink::web_pref::WebPreferences* prefs) override;
   void AppendExtraCommandLineSwitches(base::CommandLine* command_line,
                                       int child_process_id) override;
   std::unique_ptr<BrowserMainParts> CreateBrowserMainParts(
diff --git a/content/web_test/browser/web_test_control_host.cc b/content/web_test/browser/web_test_control_host.cc
index 42993ad..dd5d94b 100644
--- a/content/web_test/browser/web_test_control_host.cc
+++ b/content/web_test/browser/web_test_control_host.cc
@@ -231,7 +231,7 @@
 // Applies settings that differ between web tests and regular mode. Some
 // of the defaults are controlled via command line flags which are
 // automatically set for web tests.
-void ApplyWebTestDefaultPreferences(WebPreferences* prefs) {
+void ApplyWebTestDefaultPreferences(blink::web_pref::WebPreferences* prefs) {
   const base::CommandLine& command_line =
       *base::CommandLine::ForCurrentProcess();
 
@@ -261,28 +261,33 @@
   prefs->translate_service_available = true;
 
 #if defined(OS_MAC)
-  prefs->editing_behavior = EDITING_BEHAVIOR_MAC;
+  prefs->editing_behavior = blink::web_pref::EDITING_BEHAVIOR_MAC;
 #else
-  prefs->editing_behavior = EDITING_BEHAVIOR_WIN;
+  prefs->editing_behavior = blink::web_pref::EDITING_BEHAVIOR_WIN;
 #endif
 
 #if defined(OS_MAC)
-  prefs->cursive_font_family_map[kCommonScript] =
+  prefs->cursive_font_family_map[blink::web_pref::kCommonScript] =
       base::ASCIIToUTF16("Apple Chancery");
-  prefs->fantasy_font_family_map[kCommonScript] = base::ASCIIToUTF16("Papyrus");
-  prefs->serif_font_family_map[kCommonScript] = base::ASCIIToUTF16("Times");
-  prefs->standard_font_family_map[kCommonScript] = base::ASCIIToUTF16("Times");
+  prefs->fantasy_font_family_map[blink::web_pref::kCommonScript] =
+      base::ASCIIToUTF16("Papyrus");
+  prefs->serif_font_family_map[blink::web_pref::kCommonScript] =
+      base::ASCIIToUTF16("Times");
+  prefs->standard_font_family_map[blink::web_pref::kCommonScript] =
+      base::ASCIIToUTF16("Times");
 #else
-  prefs->cursive_font_family_map[kCommonScript] =
+  prefs->cursive_font_family_map[blink::web_pref::kCommonScript] =
       base::ASCIIToUTF16("Comic Sans MS");
-  prefs->fantasy_font_family_map[kCommonScript] = base::ASCIIToUTF16("Impact");
-  prefs->serif_font_family_map[kCommonScript] =
+  prefs->fantasy_font_family_map[blink::web_pref::kCommonScript] =
+      base::ASCIIToUTF16("Impact");
+  prefs->serif_font_family_map[blink::web_pref::kCommonScript] =
       base::ASCIIToUTF16("times new roman");
-  prefs->standard_font_family_map[kCommonScript] =
+  prefs->standard_font_family_map[blink::web_pref::kCommonScript] =
       base::ASCIIToUTF16("times new roman");
 #endif
-  prefs->fixed_font_family_map[kCommonScript] = base::ASCIIToUTF16("Courier");
-  prefs->sans_serif_font_family_map[kCommonScript] =
+  prefs->fixed_font_family_map[blink::web_pref::kCommonScript] =
+      base::ASCIIToUTF16("Courier");
+  prefs->sans_serif_font_family_map[blink::web_pref::kCommonScript] =
       base::ASCIIToUTF16("Helvetica");
 }
 
@@ -665,7 +670,7 @@
   test_phase_ = BETWEEN_TESTS;
   expected_pixel_hash_.clear();
   test_url_ = GURL();
-  prefs_ = WebPreferences();
+  prefs_ = blink::web_pref::WebPreferences();
   should_override_prefs_ = false;
   WebTestContentBrowserClient::Get()->SetPopupBlockingEnabled(false);
   WebTestContentBrowserClient::Get()->ResetMockClipboardHosts();
@@ -727,7 +732,8 @@
   temp_path_ = temp_path;
 }
 
-void WebTestControlHost::OverrideWebkitPrefs(WebPreferences* prefs) {
+void WebTestControlHost::OverrideWebkitPrefs(
+    blink::web_pref::WebPreferences* prefs) {
   if (should_override_prefs_) {
     *prefs = prefs_;
   } else {
@@ -1350,7 +1356,8 @@
   printer_->AddMessageRaw(message);
 }
 
-void WebTestControlHost::OverridePreferences(const WebPreferences& prefs) {
+void WebTestControlHost::OverridePreferences(
+    const blink::web_pref::WebPreferences& prefs) {
   should_override_prefs_ = true;
   prefs_ = prefs;
 
diff --git a/content/web_test/browser/web_test_control_host.h b/content/web_test/browser/web_test_control_host.h
index 1eb81be..6dbfe1f7 100644
--- a/content/web_test/browser/web_test_control_host.h
+++ b/content/web_test/browser/web_test_control_host.h
@@ -31,11 +31,11 @@
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_process_host_observer.h"
 #include "content/public/browser/web_contents_observer.h"
-#include "content/public/common/web_preferences.h"
 #include "content/web_test/browser/leak_detector.h"
 #include "content/web_test/common/web_test.mojom.h"
 #include "mojo/public/cpp/bindings/associated_receiver_set.h"
 #include "mojo/public/cpp/bindings/associated_remote.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "ui/gfx/geometry/size.h"
 
 class SkBitmap;
@@ -129,7 +129,7 @@
   void DidCreateOrAttachWebContents(WebContents* web_contents);
 
   void SetTempPath(const base::FilePath& temp_path);
-  void OverrideWebkitPrefs(WebPreferences* prefs);
+  void OverrideWebkitPrefs(blink::web_pref::WebPreferences* prefs);
   void OpenURL(const GURL& url);
   bool IsMainWindow(WebContents* web_contents) const;
   std::unique_ptr<BluetoothChooser> RunBluetoothChooser(
@@ -203,7 +203,7 @@
   void PrintMessage(const std::string& message) override;
   void Reload() override;
   void OverridePreferences(
-      const content::WebPreferences& web_preferences) override;
+      const blink::web_pref::WebPreferences& web_preferences) override;
   void SetMainWindowHidden(bool hidden) override;
   void CheckForLeakedWindows() override;
   void GoToOffset(int offset) override;
@@ -320,12 +320,12 @@
 
   // Stores the default test-adapted WebPreferences which is then used to fully
   // reset the main window's preferences if and when it is reused.
-  WebPreferences default_prefs_;
+  blink::web_pref::WebPreferences default_prefs_;
 
   // True if the WebPreferences of newly created RenderViewHost should be
   // overridden with prefs_.
   bool should_override_prefs_;
-  WebPreferences prefs_;
+  blink::web_pref::WebPreferences prefs_;
 
   bool crash_when_leak_found_;
   std::unique_ptr<LeakDetector> leak_detector_;
diff --git a/content/web_test/renderer/blink_test_helpers.cc b/content/web_test/renderer/blink_test_helpers.cc
index bc2f70a04..e5ee7bc 100644
--- a/content/web_test/renderer/blink_test_helpers.cc
+++ b/content/web_test/renderer/blink_test_helpers.cc
@@ -13,10 +13,10 @@
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "content/public/common/content_switches.h"
-#include "content/public/common/web_preferences.h"
 #include "content/web_test/common/web_test_switches.h"
 #include "content/web_test/renderer/test_preferences.h"
 #include "net/base/filename_util.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "ui/display/display.h"
 
 #if defined(OS_MAC)
@@ -71,9 +71,10 @@
 namespace content {
 
 void ExportWebTestSpecificPreferences(const TestPreferences& from,
-                                      WebPreferences* to) {
+                                      blink::web_pref::WebPreferences* to) {
   to->javascript_can_access_clipboard = from.java_script_can_access_clipboard;
-  to->editing_behavior = static_cast<EditingBehavior>(from.editing_behavior);
+  to->editing_behavior =
+      static_cast<blink::web_pref::EditingBehavior>(from.editing_behavior);
   to->default_font_size = from.default_font_size;
   to->minimum_font_size = from.minimum_font_size;
   to->default_encoding = from.default_text_encoding_name.Utf8().data();
diff --git a/content/web_test/renderer/blink_test_helpers.h b/content/web_test/renderer/blink_test_helpers.h
index c2a3267..064eb7c 100644
--- a/content/web_test/renderer/blink_test_helpers.h
+++ b/content/web_test/renderer/blink_test_helpers.h
@@ -10,15 +10,20 @@
 
 #include <string>
 
+namespace blink {
+namespace web_pref {
+struct WebPreferences;
+}
+}  // namespace blink
+
 namespace content {
 struct TestPreferences;
-struct WebPreferences;
 
 // The TestRunner library keeps its settings in a TestPreferences object.
 // The content_shell, however, uses WebPreferences. This method exports the
 // settings from the TestRunner library which are relevant for web tests.
 void ExportWebTestSpecificPreferences(const TestPreferences& from,
-                                      WebPreferences* to);
+                                      blink::web_pref::WebPreferences* to);
 
 // Replaces file:///tmp/web_tests/ with the actual path to the web_tests
 // directory, or rewrite URLs generated from absolute path links in
diff --git a/content/web_test/renderer/test_runner.cc b/content/web_test/renderer/test_runner.cc
index 1accf4ca..d1a4e8c 100644
--- a/content/web_test/renderer/test_runner.cc
+++ b/content/web_test/renderer/test_runner.cc
@@ -45,6 +45,7 @@
 #include "services/network/public/mojom/cors.mojom.h"
 #include "third_party/blink/public/common/page/page_zoom.h"
 #include "third_party/blink/public/common/permissions/permission_utils.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "third_party/blink/public/mojom/app_banner/app_banner.mojom.h"
 #include "third_party/blink/public/mojom/clipboard/clipboard.mojom.h"
 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
@@ -2769,7 +2770,8 @@
 void TestRunner::OnTestPreferencesChanged(const TestPreferences& test_prefs,
                                           RenderFrame* frame) {
   RenderView* render_view = frame->GetRenderView();
-  WebPreferences web_prefs = render_view->GetWebkitPreferences();
+  blink::web_pref::WebPreferences web_prefs =
+      render_view->GetWebkitPreferences();
 
   // Turns the TestPreferences into WebPreferences.
   ExportWebTestSpecificPreferences(test_prefs, &web_prefs);
diff --git a/docs/accessibility/reader_mode.md b/docs/accessibility/reader_mode.md
index da149185..986f2bd1 100644
--- a/docs/accessibility/reader_mode.md
+++ b/docs/accessibility/reader_mode.md
@@ -16,6 +16,15 @@
 Reader Mode” flag to “Enabled” in chrome://flags or start Chrome with
 --enable-feature=”ReaderMode”.
 
+There's also a flag that instead exposes a Setting; with this variant, users
+need to first enable the Setting once and then they can use Reader Mode
+on any supported page. Enabling this variant requires a long command-line
+argument:
+
+```
+--enable-features="ReaderMode<FakeStudy" --force-fieldtrials=FakeStudy/FakeGroup --force-fieldtrial-params="FakeStudy.FakeGroup:discoverability/offer-in-settings"
+```
+
 ### Code Locations
 
 Most of Reader Mode code is in components/dom_distiller (see the
@@ -164,4 +173,4 @@
 Pages are loaded by
 [DomDistillerViewerSource](https://source.chromium.org/chromium/chromium/src/+/master:components/dom_distiller/content/browser/dom_distiller_viewer_source.h),
 which serves the HTML and resources for viewing pages. After the DOM is
-initially loaded, the contents are populated via Javascript.
\ No newline at end of file
+initially loaded, the contents are populated via Javascript.
diff --git a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc
index 60c4fe74..998d6d0 100644
--- a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc
+++ b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc
@@ -16,7 +16,6 @@
 #include "content/public/browser/render_widget_host_view.h"
 #include "content/public/common/child_process_host.h"
 #include "content/public/common/url_constants.h"
-#include "content/public/common/web_preferences.h"
 #include "extensions/browser/api/extensions_api_client.h"
 #include "extensions/browser/event_router.h"
 #include "extensions/browser/extension_registry.h"
@@ -35,6 +34,7 @@
 #include "services/service_manager/public/cpp/binder_registry.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
 #include "third_party/blink/public/common/input/web_gesture_event.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 
 using content::WebContents;
 using guest_view::GuestViewBase;
diff --git a/extensions/renderer/api/automation/automation_internal_custom_bindings.cc b/extensions/renderer/api/automation/automation_internal_custom_bindings.cc
index 3989b7c..3b36960 100644
--- a/extensions/renderer/api/automation/automation_internal_custom_bindings.cc
+++ b/extensions/renderer/api/automation/automation_internal_custom_bindings.cc
@@ -1156,8 +1156,7 @@
                                         AutomationAXTreeWrapper* tree_wrapper,
                                         ui::AXNode* node) {
     const ui::AXNodeData& node_data = node->data();
-    const char* name =
-        node_data.GetStringAttribute(ax::mojom::StringAttribute::kName).c_str();
+    const char* name = nullptr;
     if (node_data.role == ax::mojom::Role::kPortal &&
         node_data.GetNameFrom() == ax::mojom::NameFrom::kNone) {
       if (GetRootOfChildTree(&node, &tree_wrapper)) {
@@ -1166,7 +1165,15 @@
                    .c_str();
       }
     }
-    result.Set(v8::String::NewFromUtf8(isolate, name).ToLocalChecked());
+
+    if (!name &&
+        node_data.HasStringAttribute(ax::mojom::StringAttribute::kName)) {
+      name = node_data.GetStringAttribute(ax::mojom::StringAttribute::kName)
+                 .c_str();
+    }
+
+    if (name)
+      result.Set(v8::String::NewFromUtf8(isolate, name).ToLocalChecked());
   });
   RouteNodeIDFunction(
       "GetDescriptionFrom",
diff --git a/fuchsia/engine/browser/web_engine_content_browser_client.cc b/fuchsia/engine/browser/web_engine_content_browser_client.cc
index 49def7b..0d3652f 100644
--- a/fuchsia/engine/browser/web_engine_content_browser_client.cc
+++ b/fuchsia/engine/browser/web_engine_content_browser_client.cc
@@ -15,7 +15,6 @@
 #include "content/public/browser/network_service_instance.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/user_agent.h"
-#include "content/public/common/web_preferences.h"
 #include "fuchsia/base/fuchsia_dir_scheme.h"
 #include "fuchsia/engine/browser/url_request_rewrite_rules_manager.h"
 #include "fuchsia/engine/browser/web_engine_browser_context.h"
@@ -27,6 +26,7 @@
 #include "fuchsia/engine/switches.h"
 #include "media/base/media_switches.h"
 #include "services/network/public/mojom/network_service.mojom.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 
 namespace {
 
@@ -109,7 +109,7 @@
 
 void WebEngineContentBrowserClient::OverrideWebkitPrefs(
     content::RenderViewHost* rvh,
-    content::WebPreferences* web_prefs) {
+    blink::web_pref::WebPreferences* web_prefs) {
   // Disable WebSQL support since it's being removed from the web platform.
   web_prefs->databases_enabled = false;
 
@@ -118,7 +118,8 @@
 
   // Allow media to autoplay.
   // TODO(crbug.com/1067101): Provide a FIDL API to configure AutoplayPolicy.
-  web_prefs->autoplay_policy = content::AutoplayPolicy::kNoUserGestureRequired;
+  web_prefs->autoplay_policy =
+      blink::web_pref::AutoplayPolicy::kNoUserGestureRequired;
 }
 
 void WebEngineContentBrowserClient::RegisterBrowserInterfaceBindersForFrame(
diff --git a/fuchsia/engine/browser/web_engine_content_browser_client.h b/fuchsia/engine/browser/web_engine_content_browser_client.h
index 2f866213..ca2a89f 100644
--- a/fuchsia/engine/browser/web_engine_content_browser_client.h
+++ b/fuchsia/engine/browser/web_engine_content_browser_client.h
@@ -35,7 +35,7 @@
   std::string GetProduct() final;
   std::string GetUserAgent() final;
   void OverrideWebkitPrefs(content::RenderViewHost* rvh,
-                           content::WebPreferences* web_prefs) final;
+                           blink::web_pref::WebPreferences* web_prefs) final;
   void RegisterBrowserInterfaceBindersForFrame(
       content::RenderFrameHost* render_frame_host,
       mojo::BinderMapWithContext<content::RenderFrameHost*>* map) final;
diff --git a/fuchsia/runners/cast/cast_runner.cc b/fuchsia/runners/cast/cast_runner.cc
index 1f827a47..8a43d77 100644
--- a/fuchsia/runners/cast/cast_runner.cc
+++ b/fuchsia/runners/cast/cast_runner.cc
@@ -197,6 +197,9 @@
 void CastRunner::OnComponentDestroyed(CastComponent* component) {
   if (component == audio_capturer_component_)
     audio_capturer_component_ = nullptr;
+
+  if (component == video_capturer_component_)
+    video_capturer_component_ = nullptr;
 }
 
 fuchsia::web::CreateContextParams CastRunner::GetCommonContextParams() {
diff --git a/fuchsia/runners/cast/cast_runner_integration_test.cc b/fuchsia/runners/cast/cast_runner_integration_test.cc
index 17962e7..d8589a0b 100644
--- a/fuchsia/runners/cast/cast_runner_integration_test.cc
+++ b/fuchsia/runners/cast/cast_runner_integration_test.cc
@@ -38,7 +38,6 @@
 #include "fuchsia/base/result_receiver.h"
 #include "fuchsia/base/string_util.h"
 #include "fuchsia/base/test_devtools_list_fetcher.h"
-#include "fuchsia/base/test_navigation_listener.h"
 #include "fuchsia/base/url_request_rewrite_test_util.h"
 #include "fuchsia/runners/cast/cast_runner.h"
 #include "fuchsia/runners/cast/fake_application_config_manager.h"
@@ -50,6 +49,7 @@
 namespace {
 
 constexpr char kTestAppId[] = "00000000";
+constexpr char kSecondTestAppId[] = "FFFFFFFF";
 
 constexpr char kBlankAppUrl[] = "/defaultresponse";
 constexpr char kEchoHeaderPath[] = "/echoheader?Test";
@@ -267,14 +267,21 @@
     std::vector<chromium::cast::ApiBinding> binding_list;
     chromium::cast::ApiBinding eval_js_binding;
     eval_js_binding.set_before_load_script(cr_fuchsia::MemBufferFromString(
+        "function valueOrUndefinedString(value) {"
+        "    return (typeof(value) == 'undefined') ? 'undefined' : value;"
+        "}"
         "window.addEventListener('DOMContentLoaded', (event) => {"
         "  var port = cast.__platform__.PortConnector.bind('testport');"
         "  port.onmessage = (e) => {"
         "    var result = eval(e.data);"
-        "    if (typeof(result) == \"undefined\") {"
-        "      result = \"undefined\";"
+        "    if (result && typeof(result.then) == 'function') {"
+        "      result"
+        "        .then(result =>"
+        "                port.postMessage(valueOrUndefinedString(result)))"
+        "        .catch(e => port.postMessage(JSON.stringify(e)));"
+        "    } else {"
+        "      port.postMessage(valueOrUndefinedString(result));"
         "    }"
-        "    port.postMessage(result);"
         "  };"
         "});",
         "test"));
@@ -331,8 +338,9 @@
     app_config_manager_.AddAppConfig(std::move(app_config));
   }
 
-  void CreateComponentContextAndStartComponent() {
-    auto component_url = base::StringPrintf("cast:%s", kTestAppId);
+  void CreateComponentContextAndStartComponent(
+      const char* app_id = kTestAppId) {
+    auto component_url = base::StringPrintf("cast:%s", app_id);
     CreateComponentContext(component_url);
     StartCastComponent(component_url);
     WaitComponentState();
@@ -390,8 +398,10 @@
     });
   }
 
-  // Executes |code| in the context of the test application and the returns
-  // serialized as string.
+  // Executes |code| in the context of the test application and then returns
+  // the result serialized as string. If the code evaluates to a promise then
+  // execution is blocked until the promise is complete and the result of the
+  // promise is returned.
   std::string ExecuteJavaScript(const std::string& code) {
     fuchsia::web::WebMessage message;
     message.set_data(cr_fuchsia::MemBufferFromString(code, "test-msg"));
@@ -463,9 +473,14 @@
 
     state_loop.Run();
 
+    ResetComponentState();
+  }
+
+  void ResetComponentState() {
     component_context_ = nullptr;
     component_services_client_ = nullptr;
     component_state_ = nullptr;
+    test_port_ = nullptr;
   }
 
   base::test::SingleThreadTaskEnvironment task_environment_{
@@ -806,28 +821,54 @@
   auto app_config =
       FakeApplicationConfigManager::CreateConfig(kTestAppId, app_url);
 
-  fuchsia::web::PermissionDescriptor canera_permission;
-  canera_permission.set_type(fuchsia::web::PermissionType::CAMERA);
-  app_config.mutable_permissions()->push_back(std::move(canera_permission));
+  fuchsia::web::PermissionDescriptor camera_permission;
+  camera_permission.set_type(fuchsia::web::PermissionType::CAMERA);
+  app_config.mutable_permissions()->push_back(std::move(camera_permission));
   app_config_manager_.AddAppConfig(std::move(app_config));
 
   CreateComponentContextAndStartComponent();
 
   // Expect fuchsia.camera3.DeviceWatcher connection to be redirected to the
   // agent.
-  base::RunLoop run_loop;
+  bool received_device_watcher_request = false;
   component_state_->outgoing_directory()->AddPublicService(
       std::make_unique<vfs::Service>(
-          [quit_closure = run_loop.QuitClosure()](
+          [&received_device_watcher_request](
               zx::channel channel, async_dispatcher_t* dispatcher) mutable {
-            std::move(quit_closure).Run();
+            received_device_watcher_request = true;
           }),
       fuchsia::camera3::DeviceWatcher::Name_);
 
   ExecuteJavaScript("connectCamera();");
+  EXPECT_TRUE(received_device_watcher_request);
+}
 
-  // Will quit once camara3::DeviceWatcher is connected.
-  run_loop.Run();
+TEST_F(CastRunnerIntegrationTest, CameraAccessAfterComponentShutdown) {
+  GURL app_url = test_server_.GetURL("/camera.html");
+
+  // First app with camera permission.
+  auto app_config =
+      FakeApplicationConfigManager::CreateConfig(kTestAppId, app_url);
+  fuchsia::web::PermissionDescriptor camera_permission;
+  camera_permission.set_type(fuchsia::web::PermissionType::CAMERA);
+  app_config.mutable_permissions()->push_back(std::move(camera_permission));
+  app_config_manager_.AddAppConfig(std::move(app_config));
+
+  // Second app without camera permission (but it will still try to access
+  // fuchsia.camera3.DeviceWatcher service to enumerate devices).
+  auto app_config_2 =
+      FakeApplicationConfigManager::CreateConfig(kSecondTestAppId, app_url);
+  app_config_manager_.AddAppConfig(std::move(app_config_2));
+
+  // Start and then shutdown the first app.
+  CreateComponentContextAndStartComponent(kTestAppId);
+  ShutdownComponent();
+  ResetComponentState();
+
+  // Start the second app and try to connect the camera. It's expected to fail
+  // to initialize the camera without crashing CastRunner.
+  CreateComponentContextAndStartComponent(kSecondTestAppId);
+  EXPECT_EQ(ExecuteJavaScript("connectCamera();"), "getUserMediaFailed");
 }
 
 class HeadlessCastRunnerIntegrationTest : public CastRunnerIntegrationTest {
diff --git a/fuchsia/runners/cast/testdata/camera.html b/fuchsia/runners/cast/testdata/camera.html
index 54b0bae..265b8d01 100644
--- a/fuchsia/runners/cast/testdata/camera.html
+++ b/fuchsia/runners/cast/testdata/camera.html
@@ -1,6 +1,7 @@
 <script>
 function connectCamera() {
-    navigator.mediaDevices.getUserMedia({ audio: false, video: true })
-        .then((stream) => { document.title = 'done'; });
+    return navigator.mediaDevices.getUserMedia({ audio: false, video: true })
+        .then((stream) => { return 'getUserMediaSucceeded'; })
+        .catch((error) => { return 'getUserMediaFailed'; });
 }
 </script>
\ No newline at end of file
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 2c87eac1..2d5ca43 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -13981,16 +13981,15 @@
     gl::GLImage* image = nullptr;
     GLuint texture_id = textures[i];
     if (texture_id) {
+      // If a |texture_id| is invalid (due to a client error), report that it
+      // is not in use. Failing the GL call can result in compositor hangs.
+      // https://crbug.com/1120795
       TextureRef* ref = texture_manager()->GetTexture(texture_id);
-      if (!ref) {
-        LOCAL_SET_GL_ERROR(GL_INVALID_VALUE,
-                           "glScheduleCALayerInUseQueryCHROMIUM",
-                           "unknown texture");
-        return;
+      if (ref) {
+        Texture::ImageState image_state;
+        image = ref->texture()->GetLevelImage(ref->texture()->target(), 0,
+                                              &image_state);
       }
-      Texture::ImageState image_state;
-      image = ref->texture()->GetLevelImage(ref->texture()->target(), 0,
-                                            &image_state);
     }
     gl::GLSurface::CALayerInUseQuery query;
     query.image = image;
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
index 6c9948d..76365bd 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
@@ -4909,15 +4909,17 @@
     gl::GLImage* image = nullptr;
     GLuint texture_id = textures[i];
     if (texture_id) {
+      // If a |texture_id| is invalid (due to a client error), report that it
+      // is not in use. Failing the GL call can result in compositor hangs.
+      // https://crbug.com/1120795
       scoped_refptr<TexturePassthrough> passthrough_texture;
-      if (!resources_->texture_object_map.GetServiceID(texture_id,
-                                                       &passthrough_texture) ||
-          passthrough_texture == nullptr) {
-        InsertError(GL_INVALID_VALUE, "unknown texture");
-        return error::kNoError;
+      if (resources_->texture_object_map.GetServiceID(texture_id,
+                                                      &passthrough_texture)) {
+        if (passthrough_texture) {
+          image = passthrough_texture->GetLevelImage(
+              passthrough_texture->target(), 0);
+        }
       }
-      image =
-          passthrough_texture->GetLevelImage(passthrough_texture->target(), 0);
     }
     gl::GLSurface::CALayerInUseQuery query;
     query.image = image;
diff --git a/headless/DEPS b/headless/DEPS
index 376faea..221cb10 100644
--- a/headless/DEPS
+++ b/headless/DEPS
@@ -28,4 +28,5 @@
   "+services/network/public",
   "+services/service_manager/embedder",
   "+services/service_manager/public",
+  "+third_party/blink/public/common/web_preferences",
 ]
diff --git a/headless/app/headless_shell.cc b/headless/app/headless_shell.cc
index 6b5120b..9de53a69 100644
--- a/headless/app/headless_shell.cc
+++ b/headless/app/headless_shell.cc
@@ -772,7 +772,7 @@
 
   if (command_line.HasSwitch(switches::kHideScrollbars)) {
     builder.SetOverrideWebPreferencesCallback(
-        base::BindRepeating([](WebPreferences* preferences) {
+        base::BindRepeating([](blink::web_pref::WebPreferences* preferences) {
           preferences->hide_scrollbars = true;
         }));
   }
diff --git a/headless/lib/browser/headless_browser_context_impl.cc b/headless/lib/browser/headless_browser_context_impl.cc
index cc26c045..5e6b44f 100644
--- a/headless/lib/browser/headless_browser_context_impl.cc
+++ b/headless/lib/browser/headless_browser_context_impl.cc
@@ -358,7 +358,7 @@
 
 HeadlessBrowserContext::Builder&
 HeadlessBrowserContext::Builder::SetOverrideWebPreferencesCallback(
-    base::RepeatingCallback<void(WebPreferences*)> callback) {
+    base::RepeatingCallback<void(blink::web_pref::WebPreferences*)> callback) {
   options_->override_web_preferences_callback_ = std::move(callback);
   return *this;
 }
diff --git a/headless/lib/browser/headless_browser_context_options.cc b/headless/lib/browser/headless_browser_context_options.cc
index 48cb678..d20c353d1 100644
--- a/headless/lib/browser/headless_browser_context_options.cc
+++ b/headless/lib/browser/headless_browser_context_options.cc
@@ -70,7 +70,7 @@
                                browser_options_->block_new_web_contents);
 }
 
-base::RepeatingCallback<void(WebPreferences*)>
+base::RepeatingCallback<void(blink::web_pref::WebPreferences*)>
 HeadlessBrowserContextOptions::override_web_preferences_callback() const {
   return ReturnOverriddenValue(
       override_web_preferences_callback_,
diff --git a/headless/lib/browser/headless_browser_context_options.h b/headless/lib/browser/headless_browser_context_options.h
index c5ef99c..02717394 100644
--- a/headless/lib/browser/headless_browser_context_options.h
+++ b/headless/lib/browser/headless_browser_context_options.h
@@ -50,7 +50,7 @@
 
   // Callback that is invoked to override WebPreferences for RenderViews
   // created within this HeadlessBrowserContext.
-  base::RepeatingCallback<void(WebPreferences*)>
+  base::RepeatingCallback<void(blink::web_pref::WebPreferences*)>
   override_web_preferences_callback() const;
 
  private:
@@ -69,7 +69,8 @@
   base::Optional<base::FilePath> user_data_dir_;
   base::Optional<bool> incognito_mode_;
   base::Optional<bool> block_new_web_contents_;
-  base::Optional<base::RepeatingCallback<void(WebPreferences*)>>
+  base::Optional<
+      base::RepeatingCallback<void(blink::web_pref::WebPreferences*)>>
       override_web_preferences_callback_;
 
   base::Optional<gfx::FontRenderParams::Hinting> font_render_hinting_;
diff --git a/headless/lib/browser/headless_content_browser_client.cc b/headless/lib/browser/headless_content_browser_client.cc
index 408aee3..ce0081b 100644
--- a/headless/lib/browser/headless_content_browser_client.cc
+++ b/headless/lib/browser/headless_content_browser_client.cc
@@ -130,10 +130,10 @@
 
 void HeadlessContentBrowserClient::OverrideWebkitPrefs(
     content::RenderViewHost* render_view_host,
-    content::WebPreferences* prefs) {
+    blink::web_pref::WebPreferences* prefs) {
   auto* browser_context = HeadlessBrowserContextImpl::From(
       render_view_host->GetProcess()->GetBrowserContext());
-  base::RepeatingCallback<void(WebPreferences*)> callback =
+  base::RepeatingCallback<void(blink::web_pref::WebPreferences*)> callback =
       browser_context->options()->override_web_preferences_callback();
   if (callback)
     callback.Run(prefs);
diff --git a/headless/lib/browser/headless_content_browser_client.h b/headless/lib/browser/headless_content_browser_client.h
index f462b9f..c59a2ac 100644
--- a/headless/lib/browser/headless_content_browser_client.h
+++ b/headless/lib/browser/headless_content_browser_client.h
@@ -23,7 +23,7 @@
   std::unique_ptr<content::BrowserMainParts> CreateBrowserMainParts(
       const content::MainFunctionParams&) override;
   void OverrideWebkitPrefs(content::RenderViewHost* render_view_host,
-                           content::WebPreferences* prefs) override;
+                           blink::web_pref::WebPreferences* prefs) override;
   content::DevToolsManagerDelegate* GetDevToolsManagerDelegate() override;
   scoped_refptr<content::QuotaPermissionContext> CreateQuotaPermissionContext()
       override;
diff --git a/headless/lib/headless_browser_context_browsertest.cc b/headless/lib/headless_browser_context_browsertest.cc
index a97bc78..f1c9a64 100644
--- a/headless/lib/headless_browser_context_browsertest.cc
+++ b/headless/lib/headless_browser_context_browsertest.cc
@@ -258,8 +258,8 @@
   HeadlessBrowserContext* browser_context =
       browser()
           ->CreateBrowserContextBuilder()
-          .SetOverrideWebPreferencesCallback(
-              base::BindRepeating([](WebPreferences* preferences) {
+          .SetOverrideWebPreferencesCallback(base::BindRepeating(
+              [](blink::web_pref::WebPreferences* preferences) {
                 preferences->hide_scrollbars = true;
               }))
           .Build();
diff --git a/headless/public/headless_browser.cc b/headless/public/headless_browser.cc
index 2f66f1f..bbf5d7c 100644
--- a/headless/public/headless_browser.cc
+++ b/headless/public/headless_browser.cc
@@ -163,7 +163,7 @@
 }
 
 Builder& Builder::SetOverrideWebPreferencesCallback(
-    base::RepeatingCallback<void(WebPreferences*)> callback) {
+    base::RepeatingCallback<void(blink::web_pref::WebPreferences*)> callback) {
   options_.override_web_preferences_callback = std::move(callback);
   return *this;
 }
diff --git a/headless/public/headless_browser.h b/headless/public/headless_browser.h
index fce5e1ae..4a6c74ae 100644
--- a/headless/public/headless_browser.h
+++ b/headless/public/headless_browser.h
@@ -188,7 +188,7 @@
   //
   // WARNING: We cannot provide any guarantees about the stability of the
   // exposed WebPreferences API, so use with care.
-  base::RepeatingCallback<void(WebPreferences*)>
+  base::RepeatingCallback<void(blink::web_pref::WebPreferences*)>
       override_web_preferences_callback;
 
   // Set a callback that is invoked when a new child process is spawned or
@@ -259,7 +259,7 @@
   Builder& SetSitePerProcess(bool site_per_process);
   Builder& SetBlockNewWebContents(bool block_new_web_contents);
   Builder& SetOverrideWebPreferencesCallback(
-      base::RepeatingCallback<void(WebPreferences*)> callback);
+      base::RepeatingCallback<void(blink::web_pref::WebPreferences*)> callback);
   Builder& SetCrashReporterEnabled(bool enabled);
   Builder& SetCrashDumpsDir(const base::FilePath& dir);
   Builder& SetFontRenderHinting(
diff --git a/headless/public/headless_browser_context.h b/headless/public/headless_browser_context.h
index 22fa9db..c8eb824 100644
--- a/headless/public/headless_browser_context.h
+++ b/headless/public/headless_browser_context.h
@@ -14,10 +14,10 @@
 #include "base/callback.h"
 #include "base/optional.h"
 #include "content/public/browser/browser_context.h"
-#include "content/public/common/web_preferences.h"
 #include "headless/public/headless_export.h"
 #include "headless/public/headless_web_contents.h"
 #include "net/proxy_resolution/proxy_resolution_service.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 
 namespace base {
 class FilePath;
@@ -29,7 +29,7 @@
 
 // Imported into headless namespace for
 // Builder::SetOverrideWebPreferencesCallback().
-using content::WebPreferences;
+using blink::web_pref::WebPreferences;
 
 // Represents an isolated session with a unique cache, cookies, and other
 // profile/session related data.
diff --git a/infra/config/dev/subprojects/chromium/ci.star b/infra/config/dev/subprojects/chromium/ci.star
index d7770be..0ab77d1 100644
--- a/infra/config/dev/subprojects/chromium/ci.star
+++ b/infra/config/dev/subprojects/chromium/ci.star
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-load("//lib/builders.star", "builder", "cpu", "defaults", "os")
+load("//lib/builders.star", "builder", "cpu", "defaults", "goma", "os")
 
 luci.bucket(
     name = "ci",
@@ -56,6 +56,7 @@
             bq_table = "luci-resultdb-dev.chromium.ci_test_results",
         )],
         isolated_server = "https://isolateserver-dev.appspot.com",
+        goma_backend = goma.backend.RBE_STAGING,
         **kwargs
     )
 
diff --git a/infra/config/generated/cr-buildbucket-dev.cfg b/infra/config/generated/cr-buildbucket-dev.cfg
index 26e241cf..a7a54ff 100644
--- a/infra/config/generated/cr-buildbucket-dev.cfg
+++ b/infra/config/generated/cr-buildbucket-dev.cfg
@@ -32,6 +32,7 @@
         name: "swarming/staging"
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/master"
+        properties_j: "$build/goma:{\"enable_ats\":true,\"rpc_extra_params\":\"?staging\",\"server_host\":\"staging-goma.chromium.org\"}"
         properties_j: "$recipe_engine/isolated:{\"server\":\"https://isolateserver-dev.appspot.com\"}"
         properties_j: "builder_group:\"chromium.dev\""
       }
@@ -58,6 +59,7 @@
         name: "swarming/staging"
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/master"
+        properties_j: "$build/goma:{\"enable_ats\":true,\"rpc_extra_params\":\"?staging\",\"server_host\":\"staging-goma.chromium.org\"}"
         properties_j: "$recipe_engine/isolated:{\"server\":\"https://isolateserver-dev.appspot.com\"}"
         properties_j: "builder_group:\"chromium.dev\""
       }
@@ -84,6 +86,7 @@
         name: "swarming/staging"
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/master"
+        properties_j: "$build/goma:{\"enable_ats\":true,\"rpc_extra_params\":\"?staging\",\"server_host\":\"staging-goma.chromium.org\"}"
         properties_j: "$recipe_engine/isolated:{\"server\":\"https://isolateserver-dev.appspot.com\"}"
         properties_j: "builder_group:\"chromium.dev\""
       }
@@ -111,6 +114,7 @@
         name: "swarming/staging"
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/master"
+        properties_j: "$build/goma:{\"enable_ats\":true,\"rpc_extra_params\":\"?staging\",\"server_host\":\"staging-goma.chromium.org\"}"
         properties_j: "$recipe_engine/isolated:{\"server\":\"https://isolateserver-dev.appspot.com\"}"
         properties_j: "builder_group:\"chromium.dev\""
       }
@@ -137,6 +141,7 @@
         name: "swarming/staging"
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/master"
+        properties_j: "$build/goma:{\"rpc_extra_params\":\"?staging\",\"server_host\":\"staging-goma.chromium.org\"}"
         properties_j: "$recipe_engine/isolated:{\"server\":\"https://isolateserver-dev.appspot.com\"}"
         properties_j: "builder_group:\"chromium.dev\""
       }
@@ -163,6 +168,7 @@
         name: "swarming/staging"
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/master"
+        properties_j: "$build/goma:{\"enable_ats\":true,\"rpc_extra_params\":\"?staging\",\"server_host\":\"staging-goma.chromium.org\"}"
         properties_j: "$recipe_engine/isolated:{\"server\":\"https://isolateserver-dev.appspot.com\"}"
         properties_j: "builder_group:\"chromium.dev\""
       }
@@ -189,6 +195,7 @@
         name: "swarming/staging"
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/master"
+        properties_j: "$build/goma:{\"enable_ats\":true,\"rpc_extra_params\":\"?staging\",\"server_host\":\"staging-goma.chromium.org\"}"
         properties_j: "$recipe_engine/isolated:{\"server\":\"https://isolateserver-dev.appspot.com\"}"
         properties_j: "builder_group:\"chromium.dev\""
       }
diff --git a/infra/config/generated/cr-buildbucket.cfg b/infra/config/generated/cr-buildbucket.cfg
index a515549..8547891c 100644
--- a/infra/config/generated/cr-buildbucket.cfg
+++ b/infra/config/generated/cr-buildbucket.cfg
@@ -1704,7 +1704,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
-      dimensions: "pool:luci.chromium.ci"
+      dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
@@ -1733,7 +1733,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
-      dimensions: "pool:luci.chromium.ci"
+      dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
@@ -1762,7 +1762,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
-      dimensions: "pool:luci.chromium.ci"
+      dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
@@ -1907,7 +1907,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
-      dimensions: "pool:luci.chromium.ci"
+      dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
@@ -1936,7 +1936,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
-      dimensions: "pool:luci.chromium.ci"
+      dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
@@ -2513,7 +2513,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
-      dimensions: "pool:luci.chromium.ci"
+      dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
@@ -2542,7 +2542,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
-      dimensions: "pool:luci.chromium.ci"
+      dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
@@ -2571,7 +2571,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
-      dimensions: "pool:luci.chromium.ci"
+      dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
@@ -2600,7 +2600,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
-      dimensions: "pool:luci.chromium.ci"
+      dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
@@ -2629,7 +2629,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
-      dimensions: "pool:luci.chromium.ci"
+      dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
@@ -2658,7 +2658,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
-      dimensions: "pool:luci.chromium.ci"
+      dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
@@ -2687,7 +2687,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
-      dimensions: "pool:luci.chromium.ci"
+      dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
@@ -2716,7 +2716,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
-      dimensions: "pool:luci.chromium.ci"
+      dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
@@ -2745,7 +2745,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
-      dimensions: "pool:luci.chromium.ci"
+      dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
@@ -2886,7 +2886,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
-      dimensions: "pool:luci.chromium.ci"
+      dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
@@ -2915,7 +2915,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
-      dimensions: "pool:luci.chromium.ci"
+      dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
@@ -7588,7 +7588,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
-      dimensions: "pool:luci.chromium.ci"
+      dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
@@ -9092,7 +9092,7 @@
         cipd_version: "refs/heads/master"
         cmd: "recipes"
       }
-      properties: "{\"$build/goma\":{\"enable_ats\":true,\"jobs\":150,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\",\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.android\",\"recipe\":\"chromium\"}"
+      properties: "{\"$build/goma\":{\"enable_ats\":true,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\",\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.android.fyi\",\"recipe\":\"chromium\"}"
       execution_timeout_secs: 10800
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
@@ -12051,7 +12051,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows-10"
-      dimensions: "pool:luci.chromium.ci"
+      dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
@@ -12080,7 +12080,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows-10"
-      dimensions: "pool:luci.chromium.ci"
+      dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
@@ -12109,7 +12109,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows-10"
-      dimensions: "pool:luci.chromium.ci"
+      dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
@@ -12138,7 +12138,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows-10"
-      dimensions: "pool:luci.chromium.ci"
+      dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
@@ -12167,7 +12167,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows-10"
-      dimensions: "pool:luci.chromium.ci"
+      dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
@@ -12196,7 +12196,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows-10"
-      dimensions: "pool:luci.chromium.ci"
+      dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
@@ -12225,7 +12225,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows-10"
-      dimensions: "pool:luci.chromium.ci"
+      dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
diff --git a/infra/config/generated/luci-milo.cfg b/infra/config/generated/luci-milo.cfg
index 51dd3128..31f4ecb1 100644
--- a/infra/config/generated/luci-milo.cfg
+++ b/infra/config/generated/luci-milo.cfg
@@ -4256,11 +4256,6 @@
     short_name: "N"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/android-pie-arm64-wpt-rel-non-cq"
-    category: "builder_tester|arm64"
-    short_name: "P-WPT"
-  }
-  builders {
     name: "buildbucket/luci.chromium.ci/android-marshmallow-x86-rel"
     category: "builder_tester|x86"
     short_name: "M"
@@ -4576,6 +4571,11 @@
     short_name: "p-rel"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/android-pie-arm64-wpt-rel-non-cq"
+    category: "builder_tester|arm64"
+    short_name: "P-WPT"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/android-marshmallow-x86-fyi-rel"
     category: "emulator|M|x86"
     short_name: "rel"
diff --git a/infra/config/lib/ci.star b/infra/config/lib/ci.star
index 3bf99e94..69cf90e7 100644
--- a/infra/config/lib/ci.star
+++ b/infra/config/lib/ci.star
@@ -606,6 +606,14 @@
         **kwargs
     )
 
+def dawn_windows_builder(*, name, **kwargs):
+    return dawn_builder(
+        name = name,
+        os = builders.os.WINDOWS_ANY,
+        pool = "luci.chromium.gpu.ci",
+        **kwargs
+    )
+
 def fuzz_builder(*, name, **kwargs):
     return ci.builder(
         name = name,
@@ -770,6 +778,7 @@
         builderless = True,
         goma_backend = builders.goma.backend.RBE_PROD,
         os = builders.os.WINDOWS_ANY,
+        pool = "luci.chromium.gpu.ci",
         **kwargs
     )
 
@@ -920,6 +929,7 @@
         name = name,
         goma_backend = builders.goma.backend.RBE_PROD,
         os = builders.os.WINDOWS_DEFAULT,
+        pool = "luci.chromium.gpu.ci",
         **kwargs
     )
 
@@ -970,6 +980,7 @@
     clang_builder = clang_builder,
     clang_mac_builder = clang_mac_builder,
     dawn_builder = dawn_builder,
+    dawn_windows_builder = dawn_windows_builder,
     fuzz_builder = fuzz_builder,
     fuzz_libfuzzer_builder = fuzz_libfuzzer_builder,
     fyi_builder = fyi_builder,
diff --git a/infra/config/subprojects/chromium/ci.star b/infra/config/subprojects/chromium/ci.star
index 5788c28..fe554de 100644
--- a/infra/config/subprojects/chromium/ci.star
+++ b/infra/config/subprojects/chromium/ci.star
@@ -860,7 +860,7 @@
     tree_closing = True,
 )
 
-ci.android_builder(
+ci.android_fyi_builder(
     name = "android-pie-arm64-wpt-rel-non-cq",
     console_view_entry = ci.console_view_entry(
         category = "builder_tester|arm64",
@@ -1686,25 +1686,23 @@
     triggered_by = ["Dawn Mac x64 Builder"],
 )
 
-ci.dawn_builder(
+ci.dawn_windows_builder(
     name = "Dawn Win10 x64 ASAN Release",
     console_view_entry = ci.console_view_entry(
         category = "ToT|Windows|ASAN",
         short_name = "x64",
     ),
-    os = os.WINDOWS_ANY,
 )
 
-ci.dawn_builder(
+ci.dawn_windows_builder(
     name = "Dawn Win10 x64 Builder",
     console_view_entry = ci.console_view_entry(
         category = "ToT|Windows|Builder",
         short_name = "x64",
     ),
-    os = os.WINDOWS_ANY,
 )
 
-ci.dawn_builder(
+ci.dawn_windows_builder(
     name = "Dawn Win10 x64 DEPS Builder",
     branch_selector = branches.STANDARD_RELEASES,
     console_view_entry = ci.console_view_entry(
@@ -1713,7 +1711,6 @@
     ),
     cq_mirrors_console_view = settings.cq_mirrors_console_name,
     main_console_view = main_console_if_on_branch(),
-    os = os.WINDOWS_ANY,
 )
 
 ci.dawn_builder(
@@ -1768,16 +1765,15 @@
     triggered_by = ["Dawn Win10 x64 Builder"],
 )
 
-ci.dawn_builder(
+ci.dawn_windows_builder(
     name = "Dawn Win10 x86 Builder",
     console_view_entry = ci.console_view_entry(
         category = "ToT|Windows|Builder",
         short_name = "x86",
     ),
-    os = os.WINDOWS_ANY,
 )
 
-ci.dawn_builder(
+ci.dawn_windows_builder(
     name = "Dawn Win10 x86 DEPS Builder",
     branch_selector = branches.STANDARD_RELEASES,
     console_view_entry = ci.console_view_entry(
@@ -1786,7 +1782,6 @@
     ),
     cq_mirrors_console_view = settings.cq_mirrors_console_name,
     main_console_view = main_console_if_on_branch(),
-    os = os.WINDOWS_ANY,
 )
 
 ci.dawn_builder(
@@ -3091,6 +3086,7 @@
     cq_mirrors_console_view = settings.cq_mirrors_console_name,
     main_console_view = main_console_if_on_branch(),
     os = os.WINDOWS_ANY,
+    pool = "luci.chromium.gpu.ci",
 )
 
 ci.gpu_builder(
@@ -3100,6 +3096,7 @@
         category = "Windows",
     ),
     os = os.WINDOWS_ANY,
+    pool = "luci.chromium.gpu.ci",
     tree_closing = False,
 )
 
diff --git a/ios/chrome/app/startup/chrome_app_startup_parameters.mm b/ios/chrome/app/startup/chrome_app_startup_parameters.mm
index 44a1bf7..b76ce288 100644
--- a/ios/chrome/app/startup/chrome_app_startup_parameters.mm
+++ b/ios/chrome/app/startup/chrome_app_startup_parameters.mm
@@ -6,6 +6,7 @@
 
 #include "base/mac/foundation_util.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
 #include "base/strings/sys_string_conversions.h"
 #include "ios/chrome/browser/chrome_url_constants.h"
@@ -168,13 +169,18 @@
     MobileSessionStartAction action = START_ACTION_OTHER;
     if (gurl.SchemeIs(url::kHttpScheme)) {
       action = START_ACTION_OPEN_HTTP_FROM_OS;
+      base::RecordAction(
+          base::UserMetricsAction("MobileDefaultBrowserViewIntent"));
     } else if (gurl.SchemeIs(url::kHttpsScheme)) {
       action = START_ACTION_OPEN_HTTPS_FROM_OS;
+      base::RecordAction(
+          base::UserMetricsAction("MobileDefaultBrowserViewIntent"));
     } else {
       // Replace the scheme with https or http depending on whether the input
       // |url| scheme ends with an 's'.
       BOOL useHttps = gurl.scheme()[gurl.scheme().length() - 1] == 's';
       action = useHttps ? START_ACTION_OPEN_HTTPS : START_ACTION_OPEN_HTTP;
+      base::RecordAction(base::UserMetricsAction("MobileFirstPartyViewIntent"));
 
       GURL::Replacements replace_scheme;
       if (useHttps)
diff --git a/ios/web/navigation/error_page_helper.mm b/ios/web/navigation/error_page_helper.mm
index ee993bd..b9b5473 100644
--- a/ios/web/navigation/error_page_helper.mm
+++ b/ios/web/navigation/error_page_helper.mm
@@ -79,14 +79,11 @@
     NSURLQueryItem* itemURL = [NSURLQueryItem
         queryItemWithName:base::SysUTF8ToNSString(kOriginalUrlKey)
                     value:self.failedNavigationURLString];
-    NSURLQueryItem* itemError =
-        [NSURLQueryItem queryItemWithName:@"error"
-                                    value:_error.localizedDescription];
     NSURLQueryItem* itemDontLoad = [NSURLQueryItem queryItemWithName:@"dontLoad"
                                                                value:@"true"];
     NSURLComponents* URL = [[NSURLComponents alloc] initWithString:@"file:///"];
     URL.path = LoadedErrorPageFilePath();
-    URL.queryItems = @[ itemURL, itemError, itemDontLoad ];
+    URL.queryItems = @[ itemURL, itemDontLoad ];
     DCHECK(URL.URL) << "file URL should be valid";
     _errorPageFileURL = URL.URL;
   }
diff --git a/ios/web/navigation/wk_based_navigation_manager_impl.mm b/ios/web/navigation/wk_based_navigation_manager_impl.mm
index 5dff6bed..dbbaab10 100644
--- a/ios/web/navigation/wk_based_navigation_manager_impl.mm
+++ b/ios/web/navigation/wk_based_navigation_manager_impl.mm
@@ -469,7 +469,7 @@
   if (web_view_url.SchemeIs(url::kAboutScheme) ||
       last_committed_url.SchemeIs(url::kAboutScheme) ||
       web_view_url.SchemeIs(url::kFileScheme) ||
-      last_committed_url.SchemeIs(url::kAboutScheme) ||
+      last_committed_url.SchemeIs(url::kFileScheme) ||
       web::GetWebClient()->IsAppSpecificURL(web_view_url) ||
       web::GetWebClient()->IsAppSpecificURL(last_committed_url)) {
     return true;
diff --git a/media/base/BUILD.gn b/media/base/BUILD.gn
index d4eee635..94712ff3 100644
--- a/media/base/BUILD.gn
+++ b/media/base/BUILD.gn
@@ -161,7 +161,6 @@
     "format_utils.h",
     "frame_rate_estimator.cc",
     "frame_rate_estimator.h",
-    "hdr_metadata.h",
     "key_system_names.cc",
     "key_system_names.h",
     "key_system_properties.cc",
diff --git a/media/base/android/jni_hdr_metadata.cc b/media/base/android/jni_hdr_metadata.cc
index 118d7e4a..939577a 100644
--- a/media/base/android/jni_hdr_metadata.cc
+++ b/media/base/android/jni_hdr_metadata.cc
@@ -5,13 +5,13 @@
 #include "media/base/android/jni_hdr_metadata.h"
 
 #include "media/base/android/media_jni_headers/HdrMetadata_jni.h"
-#include "media/base/hdr_metadata.h"
 #include "media/base/video_color_space.h"
+#include "ui/gl/hdr_metadata.h"
 
 namespace media {
 
 JniHdrMetadata::JniHdrMetadata(const VideoColorSpace& color_space,
-                               const HDRMetadata& hdr_metadata)
+                               const gl::HDRMetadata& hdr_metadata)
     : color_space_(color_space), hdr_metadata_(hdr_metadata) {
   JNIEnv* env = base::android::AttachCurrentThread();
   jobject_ = Java_HdrMetadata_create(env, reinterpret_cast<jlong>(this));
diff --git a/media/base/android/jni_hdr_metadata.h b/media/base/android/jni_hdr_metadata.h
index d81078e..af3510f3 100644
--- a/media/base/android/jni_hdr_metadata.h
+++ b/media/base/android/jni_hdr_metadata.h
@@ -7,7 +7,7 @@
 
 #include "base/android/jni_android.h"
 #include "base/macros.h"
-#include "media/base/hdr_metadata.h"
+#include "ui/gl/hdr_metadata.h"
 
 namespace media {
 
@@ -16,7 +16,7 @@
 class JniHdrMetadata {
  public:
   JniHdrMetadata(const VideoColorSpace& color_space,
-                 const HDRMetadata& hdr_metadata);
+                 const gl::HDRMetadata& hdr_metadata);
   ~JniHdrMetadata();
 
   base::android::ScopedJavaLocalRef<jobject> obj() { return jobject_; }
@@ -58,7 +58,7 @@
 
  private:
   const VideoColorSpace& color_space_;
-  const HDRMetadata& hdr_metadata_;
+  const gl::HDRMetadata& hdr_metadata_;
   base::android::ScopedJavaLocalRef<jobject> jobject_;
 
   DISALLOW_COPY_AND_ASSIGN(JniHdrMetadata);
diff --git a/media/base/android/media_codec_bridge_impl.h b/media/base/android/media_codec_bridge_impl.h
index 80ab24f..c21ec38 100644
--- a/media/base/android/media_codec_bridge_impl.h
+++ b/media/base/android/media_codec_bridge_impl.h
@@ -17,10 +17,10 @@
 #include "media/base/android/media_codec_bridge.h"
 #include "media/base/android/media_codec_direction.h"
 #include "media/base/audio_decoder_config.h"
-#include "media/base/hdr_metadata.h"
 #include "media/base/media_export.h"
 #include "media/base/video_decoder_config.h"
 #include "ui/gfx/geometry/size.h"
+#include "ui/gl/hdr_metadata.h"
 
 namespace media {
 
@@ -54,7 +54,7 @@
 
   // VP9 HDR metadata is only embedded in the container. HDR10 metadata is
   // embedded in the video stream.
-  base::Optional<HDRMetadata> hdr_metadata;
+  base::Optional<gl::HDRMetadata> hdr_metadata;
 
   // Enables the async MediaCodec.Callback API. |on_buffers_available_cb|
   // will be called when input or output buffers are available. This will be
diff --git a/media/base/hdr_metadata.h b/media/base/hdr_metadata.h
deleted file mode 100644
index e63ce041..0000000
--- a/media/base/hdr_metadata.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MEDIA_BASE_HDR_METADATA_H_
-#define MEDIA_BASE_HDR_METADATA_H_
-
-#include "ui/gl/hdr_metadata.h"
-
-namespace media {
-
-// TODO(crbug.com/1122910):Delete this file and switch all instances to the gl
-// versions.
-using HDRMetadata = gl::HDRMetadata;
-using MasteringMetadata = gl::MasteringMetadata;
-using HdrMetadataType = gl::HdrMetadataType;
-
-}  // namespace media
-
-#endif  // MEDIA_BASE_HDR_METADATA_H_
diff --git a/media/base/ipc/media_param_traits_macros.h b/media/base/ipc/media_param_traits_macros.h
index 1eca0ed..b81de0e 100644
--- a/media/base/ipc/media_param_traits_macros.h
+++ b/media/base/ipc/media_param_traits_macros.h
@@ -22,7 +22,6 @@
 #include "media/base/demuxer_stream.h"
 #include "media/base/eme_constants.h"
 #include "media/base/encryption_scheme.h"
-#include "media/base/hdr_metadata.h"
 #include "media/base/media_log_record.h"
 #include "media/base/media_status.h"
 #include "media/base/output_device_info.h"
@@ -40,6 +39,7 @@
 #include "media/media_buildflags.h"
 #include "media/video/supported_video_decoder_config.h"
 #include "ui/gfx/ipc/color/gfx_param_traits_macros.h"
+#include "ui/gl/hdr_metadata.h"
 
 #if BUILDFLAG(ENABLE_MEDIA_DRM_STORAGE)
 #include "media/base/media_drm_key_type.h"
@@ -186,7 +186,7 @@
   IPC_STRUCT_TRAITS_MEMBER(range)
 IPC_STRUCT_TRAITS_END()
 
-IPC_STRUCT_TRAITS_BEGIN(media::MasteringMetadata)
+IPC_STRUCT_TRAITS_BEGIN(gl::MasteringMetadata)
   IPC_STRUCT_TRAITS_MEMBER(primary_r)
   IPC_STRUCT_TRAITS_MEMBER(primary_g)
   IPC_STRUCT_TRAITS_MEMBER(primary_b)
@@ -195,7 +195,7 @@
   IPC_STRUCT_TRAITS_MEMBER(luminance_min)
 IPC_STRUCT_TRAITS_END()
 
-IPC_STRUCT_TRAITS_BEGIN(media::HDRMetadata)
+IPC_STRUCT_TRAITS_BEGIN(gl::HDRMetadata)
   IPC_STRUCT_TRAITS_MEMBER(mastering_metadata)
   IPC_STRUCT_TRAITS_MEMBER(max_content_light_level)
   IPC_STRUCT_TRAITS_MEMBER(max_frame_average_light_level)
diff --git a/media/base/media_serializers.h b/media/base/media_serializers.h
index 8e7aae2..1a2a6bd5 100644
--- a/media/base/media_serializers.h
+++ b/media/base/media_serializers.h
@@ -18,6 +18,7 @@
 #include "media/base/text_track_config.h"
 #include "media/base/video_decoder_config.h"
 #include "ui/gfx/geometry/size.h"
+#include "ui/gl/hdr_metadata.h"
 
 namespace media {
 
@@ -211,8 +212,8 @@
 
 // Class (complex)
 template <>
-struct MediaSerializer<HDRMetadata> {
-  static base::Value Serialize(const HDRMetadata& value) {
+struct MediaSerializer<gl::HDRMetadata> {
+  static base::Value Serialize(const gl::HDRMetadata& value) {
     // TODO(tmathmeyer) serialize more fields here potentially.
     base::Value result(base::Value::Type::DICTIONARY);
     FIELD_SERIALIZE("luminance range",
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index b61f3fc..49acd2d4 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -777,10 +777,10 @@
     base::FEATURE_DISABLED_BY_DEFAULT};
 
 const base::Feature kKaleidoscopeModule{"KaleidoscopeModule",
-                                        base::FEATURE_DISABLED_BY_DEFAULT};
+                                        base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kKaleidoscopeModuleCacheOnly{
-    "KaleidoscopeModuleCacheOnly", base::FEATURE_DISABLED_BY_DEFAULT};
+    "KaleidoscopeModuleCacheOnly", base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kUseFakeDeviceForMediaStream{
     "use-fake-device-for-media-stream", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/media/base/media_types.h b/media/base/media_types.h
index acffbce..b96ee7b 100644
--- a/media/base/media_types.h
+++ b/media/base/media_types.h
@@ -33,7 +33,7 @@
   VideoCodecProfile profile;
   int level;
   VideoColorSpace color_space;
-  HdrMetadataType hdr_metadata_type;
+  gl::HdrMetadataType hdr_metadata_type;
 };
 
 MEDIA_EXPORT bool operator==(const AudioType& x, const AudioType& y);
diff --git a/media/base/supported_types.cc b/media/base/supported_types.cc
index f3ccd25..2047bb5 100644
--- a/media/base/supported_types.cc
+++ b/media/base/supported_types.cc
@@ -35,14 +35,14 @@
 
 namespace {
 
-bool IsSupportedHdrMetadata(const HdrMetadataType& hdr_metadata_type) {
+bool IsSupportedHdrMetadata(const gl::HdrMetadataType& hdr_metadata_type) {
   switch (hdr_metadata_type) {
-    case HdrMetadataType::kNone:
+    case gl::HdrMetadataType::kNone:
       return true;
 
-    case HdrMetadataType::kSmpteSt2086:
-    case HdrMetadataType::kSmpteSt2094_10:
-    case HdrMetadataType::kSmpteSt2094_40:
+    case gl::HdrMetadataType::kSmpteSt2086:
+    case gl::HdrMetadataType::kSmpteSt2094_10:
+    case gl::HdrMetadataType::kSmpteSt2094_40:
       return false;
   }
 
diff --git a/media/base/supported_types_unittest.cc b/media/base/supported_types_unittest.cc
index 48013e1..437db52 100644
--- a/media/base/supported_types_unittest.cc
+++ b/media/base/supported_types_unittest.cc
@@ -284,14 +284,14 @@
   // No HDR metadata types are supported.
   EXPECT_FALSE(IsSupportedVideoType({media::kCodecVP8, media::VP8PROFILE_ANY,
                                      kUnspecifiedLevel, color_space,
-                                     media::HdrMetadataType::kSmpteSt2086}));
+                                     gl::HdrMetadataType::kSmpteSt2086}));
 
   EXPECT_FALSE(IsSupportedVideoType({media::kCodecVP8, media::VP8PROFILE_ANY,
                                      kUnspecifiedLevel, color_space,
-                                     media::HdrMetadataType::kSmpteSt2094_10}));
+                                     gl::HdrMetadataType::kSmpteSt2094_10}));
 
   EXPECT_FALSE(IsSupportedVideoType({media::kCodecVP8, media::VP8PROFILE_ANY,
                                      kUnspecifiedLevel, color_space,
-                                     media::HdrMetadataType::kSmpteSt2094_40}));
+                                     gl::HdrMetadataType::kSmpteSt2094_40}));
 }
 }  // namespace media
diff --git a/media/base/test_helpers.cc b/media/base/test_helpers.cc
index ed2b5f62..73ac060 100644
--- a/media/base/test_helpers.cc
+++ b/media/base/test_helpers.cc
@@ -244,6 +244,19 @@
 }
 
 // static
+VideoDecoderConfig TestVideoConfig::Custom(gfx::Size size, VideoCodec codec) {
+  return GetTestConfig(codec, MinProfile(codec), VideoColorSpace::JPEG(),
+                       VIDEO_ROTATION_0, size, false);
+}
+
+// static
+VideoDecoderConfig TestVideoConfig::CustomEncrypted(gfx::Size size,
+                                                    VideoCodec codec) {
+  return GetTestConfig(codec, MinProfile(codec), VideoColorSpace::JPEG(),
+                       VIDEO_ROTATION_0, size, true);
+}
+
+// static
 gfx::Size TestVideoConfig::NormalCodedSize() {
   return kNormalSize;
 }
diff --git a/media/base/test_helpers.h b/media/base/test_helpers.h
index d214340..cdb6352 100644
--- a/media/base/test_helpers.h
+++ b/media/base/test_helpers.h
@@ -110,6 +110,11 @@
   static VideoDecoderConfig ExtraLarge(VideoCodec codec = kCodecVP8);
   static VideoDecoderConfig ExtraLargeEncrypted(VideoCodec codec = kCodecVP8);
 
+  static VideoDecoderConfig Custom(gfx::Size size,
+                                   VideoCodec codec = kCodecVP8);
+  static VideoDecoderConfig CustomEncrypted(gfx::Size size,
+                                            VideoCodec codec = kCodecVP8);
+
   // Returns coded size for Normal and Large config.
   static gfx::Size NormalCodedSize();
   static gfx::Size LargeCodedSize();
diff --git a/media/base/video_decoder_config.h b/media/base/video_decoder_config.h
index 052adc4f..01ad01d4 100644
--- a/media/base/video_decoder_config.h
+++ b/media/base/video_decoder_config.h
@@ -13,7 +13,6 @@
 #include "base/macros.h"
 #include "base/optional.h"
 #include "media/base/encryption_scheme.h"
-#include "media/base/hdr_metadata.h"
 #include "media/base/media_export.h"
 #include "media/base/video_codecs.h"
 #include "media/base/video_color_space.h"
@@ -21,6 +20,7 @@
 #include "media/base/video_types.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
+#include "ui/gl/hdr_metadata.h"
 
 namespace media {
 
@@ -154,10 +154,10 @@
   const VideoColorSpace& color_space_info() const { return color_space_info_; }
 
   // Dynamic range of the image data.
-  void set_hdr_metadata(const HDRMetadata& hdr_metadata) {
+  void set_hdr_metadata(const gl::HDRMetadata& hdr_metadata) {
     hdr_metadata_ = hdr_metadata;
   }
-  const base::Optional<HDRMetadata>& hdr_metadata() const {
+  const base::Optional<gl::HDRMetadata>& hdr_metadata() const {
     return hdr_metadata_;
   }
 
@@ -192,7 +192,7 @@
   EncryptionScheme encryption_scheme_ = EncryptionScheme::kUnencrypted;
 
   VideoColorSpace color_space_info_;
-  base::Optional<HDRMetadata> hdr_metadata_;
+  base::Optional<gl::HDRMetadata> hdr_metadata_;
 
   // Not using DISALLOW_COPY_AND_ASSIGN here intentionally to allow the compiler
   // generated copy constructor and assignment operator. Since the extra data is
diff --git a/media/base/video_frame.h b/media/base/video_frame.h
index 001fe617..ee8f9e0 100644
--- a/media/base/video_frame.h
+++ b/media/base/video_frame.h
@@ -27,7 +27,6 @@
 #include "build/build_config.h"
 #include "gpu/command_buffer/common/mailbox_holder.h"
 #include "gpu/ipc/common/vulkan_ycbcr_info.h"
-#include "media/base/hdr_metadata.h"
 #include "media/base/video_frame_feedback.h"
 #include "media/base/video_frame_layout.h"
 #include "media/base/video_frame_metadata.h"
@@ -35,6 +34,7 @@
 #include "ui/gfx/color_space.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
+#include "ui/gl/hdr_metadata.h"
 
 #if defined(OS_MAC)
 #include <CoreVideo/CVPixelBuffer.h>
@@ -419,11 +419,11 @@
     color_space_ = color_space;
   }
 
-  const base::Optional<HDRMetadata>& hdr_metadata() const {
+  const base::Optional<gl::HDRMetadata>& hdr_metadata() const {
     return hdr_metadata_;
   }
 
-  void set_hdr_metadata(const base::Optional<HDRMetadata>& hdr_metadata) {
+  void set_hdr_metadata(const base::Optional<gl::HDRMetadata>& hdr_metadata) {
     hdr_metadata_ = hdr_metadata;
   }
 
@@ -709,7 +709,7 @@
   const int unique_id_;
 
   gfx::ColorSpace color_space_;
-  base::Optional<HDRMetadata> hdr_metadata_;
+  base::Optional<gl::HDRMetadata> hdr_metadata_;
 
   // Sampler conversion information which is used in vulkan context for android.
   base::Optional<gpu::VulkanYCbCrInfo> ycbcr_info_;
diff --git a/media/capture/BUILD.gn b/media/capture/BUILD.gn
index f370fdf8..67357fe 100644
--- a/media/capture/BUILD.gn
+++ b/media/capture/BUILD.gn
@@ -392,6 +392,11 @@
     "video/linux/camera_config_chromeos_unittest.cc",
     "video/linux/v4l2_capture_delegate_unittest.cc",
     "video/linux/video_capture_device_factory_linux_unittest.cc",
+    "video/mac/test/mock_video_capture_device_avfoundation_frame_receiver_mac.h",
+    "video/mac/test/mock_video_capture_device_avfoundation_frame_receiver_mac.mm",
+    "video/mac/test/video_capture_test_utils_mac.h",
+    "video/mac/test/video_capture_test_utils_mac.mm",
+    "video/mac/video_capture_device_avfoundation_mac_unittest.mm",
     "video/mac/video_capture_device_factory_mac_unittest.mm",
     "video/mac/video_capture_device_mac_unittest.mm",
     "video/video_capture_device_client_unittest.cc",
diff --git a/media/capture/video/mac/test/mock_video_capture_device_avfoundation_frame_receiver_mac.h b/media/capture/video/mac/test/mock_video_capture_device_avfoundation_frame_receiver_mac.h
new file mode 100644
index 0000000..fc59075
--- /dev/null
+++ b/media/capture/video/mac/test/mock_video_capture_device_avfoundation_frame_receiver_mac.h
@@ -0,0 +1,49 @@
+// Copyright 2020 The Chromium 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_CAPTURE_VIDEO_MAC_TEST_MOCK_VIDEO_CAPTURE_DEVICE_AVFOUNDATION_FRAME_RECEIVER_MAC_H_
+#define MEDIA_CAPTURE_VIDEO_MAC_TEST_MOCK_VIDEO_CAPTURE_DEVICE_AVFOUNDATION_FRAME_RECEIVER_MAC_H_
+
+#include "media/capture/video/mac/video_capture_device_avfoundation_mac.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace media {
+
+class MockVideoCaptureDeviceAVFoundationFrameReceiver
+    : public VideoCaptureDeviceAVFoundationFrameReceiver {
+ public:
+  MockVideoCaptureDeviceAVFoundationFrameReceiver();
+  ~MockVideoCaptureDeviceAVFoundationFrameReceiver() override;
+
+  MOCK_METHOD(void,
+              ReceiveFrame,
+              (const uint8_t* video_frame,
+               int video_frame_length,
+               const VideoCaptureFormat& frame_format,
+               const gfx::ColorSpace color_space,
+               int aspect_numerator,
+               int aspect_denominator,
+               base::TimeDelta timestamp),
+              (override));
+
+  MOCK_METHOD(void,
+              OnPhotoTaken,
+              (const uint8_t* image_data,
+               size_t image_length,
+               const std::string& mime_type),
+              (override));
+
+  MOCK_METHOD(void, OnPhotoError, (), (override));
+
+  MOCK_METHOD(void,
+              ReceiveError,
+              (VideoCaptureError error,
+               const base::Location& from_here,
+               const std::string& reason),
+              (override));
+};
+
+}  // namespace media
+
+#endif  // MEDIA_CAPTURE_VIDEO_MAC_TEST_MOCK_VIDEO_CAPTURE_DEVICE_AVFOUNDATION_FRAME_RECEIVER_MAC_H_
\ No newline at end of file
diff --git a/media/capture/video/mac/test/mock_video_capture_device_avfoundation_frame_receiver_mac.mm b/media/capture/video/mac/test/mock_video_capture_device_avfoundation_frame_receiver_mac.mm
new file mode 100644
index 0000000..537fc50
--- /dev/null
+++ b/media/capture/video/mac/test/mock_video_capture_device_avfoundation_frame_receiver_mac.mm
@@ -0,0 +1,15 @@
+// Copyright 2020 The Chromium 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 "media/capture/video/mac/test/mock_video_capture_device_avfoundation_frame_receiver_mac.h"
+
+namespace media {
+
+MockVideoCaptureDeviceAVFoundationFrameReceiver::
+    MockVideoCaptureDeviceAVFoundationFrameReceiver() = default;
+
+MockVideoCaptureDeviceAVFoundationFrameReceiver::
+    ~MockVideoCaptureDeviceAVFoundationFrameReceiver() = default;
+
+}  // namespace media
diff --git a/media/capture/video/mac/test/video_capture_test_utils_mac.h b/media/capture/video/mac/test/video_capture_test_utils_mac.h
new file mode 100644
index 0000000..310d212e
--- /dev/null
+++ b/media/capture/video/mac/test/video_capture_test_utils_mac.h
@@ -0,0 +1,31 @@
+// Copyright 2020 The Chromium 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_CAPTURE_VIDEO_MAC_TEST_VIDEO_CAPTURE_TEST_UTILS_MAC_H_
+#define MEDIA_CAPTURE_VIDEO_MAC_TEST_VIDEO_CAPTURE_TEST_UTILS_MAC_H_
+
+#import <Foundation/Foundation.h>
+#include <memory>
+
+#include "base/callback_forward.h"
+#include "media/capture/video/mac/video_capture_device_factory_mac.h"
+
+namespace media {
+
+// Video capture code on MacOSX must run on a CFRunLoop enabled thread
+// for interaction with AVFoundation.
+// In order to make the test case run on the actual message loop that has
+// been created for this thread, we need to run it inside a RunLoop. This is
+// required, because on MacOS the capture code must run on a CFRunLoop
+// enabled message loop.
+void RunTestCase(base::OnceClosure test_case);
+
+std::vector<VideoCaptureDeviceInfo> GetDevicesInfo(
+    VideoCaptureDeviceFactoryMac* video_capture_device_factory);
+// If there are no devices, nil is returned.
+NSString* GetFirstDeviceId();
+
+}  // namespace media
+
+#endif  // MEDIA_CAPTURE_VIDEO_MAC_TEST_VIDEO_CAPTURE_TEST_UTILS_MAC_H_
diff --git a/media/capture/video/mac/test/video_capture_test_utils_mac.mm b/media/capture/video/mac/test/video_capture_test_utils_mac.mm
new file mode 100644
index 0000000..1e6b8c88
--- /dev/null
+++ b/media/capture/video/mac/test/video_capture_test_utils_mac.mm
@@ -0,0 +1,57 @@
+// Copyright 2020 The Chromium 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 "media/capture/video/mac/test/video_capture_test_utils_mac.h"
+
+#import <Foundation/Foundation.h>
+#include <memory>
+
+#include "base/run_loop.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/gmock_callback_support.h"
+#include "base/test/task_environment.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "media/capture/video/mac/video_capture_device_factory_mac.h"
+
+namespace media {
+
+void RunTestCase(base::OnceClosure test_case) {
+  base::test::TaskEnvironment task_environment(
+      base::test::TaskEnvironment::MainThreadType::UI,
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME);
+  base::RunLoop run_loop;
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(
+                     [](base::RunLoop* run_loop, base::OnceClosure* test_case) {
+                       std::move(*test_case).Run();
+                       run_loop->Quit();
+                     },
+                     &run_loop, &test_case));
+  run_loop.Run();
+}
+
+std::vector<VideoCaptureDeviceInfo> GetDevicesInfo(
+    VideoCaptureDeviceFactoryMac* video_capture_device_factory) {
+  std::vector<VideoCaptureDeviceInfo> descriptors;
+  base::RunLoop run_loop;
+  video_capture_device_factory->GetDevicesInfo(base::BindLambdaForTesting(
+      [&descriptors, &run_loop](std::vector<VideoCaptureDeviceInfo> result) {
+        descriptors = std::move(result);
+        run_loop.Quit();
+      }));
+  run_loop.Run();
+  return descriptors;
+}
+
+NSString* GetFirstDeviceId() {
+  VideoCaptureDeviceFactoryMac video_capture_device_factory;
+  std::vector<VideoCaptureDeviceInfo> devices_info =
+      GetDevicesInfo(&video_capture_device_factory);
+  if (devices_info.empty())
+    return nil;
+  return [NSString
+      stringWithUTF8String:devices_info.front().descriptor.device_id.c_str()];
+}
+
+}  // namespace media
diff --git a/media/capture/video/mac/video_capture_device_avfoundation_mac.h b/media/capture/video/mac/video_capture_device_avfoundation_mac.h
index b635abc8..bbed9558 100644
--- a/media/capture/video/mac/video_capture_device_avfoundation_mac.h
+++ b/media/capture/video/mac/video_capture_device_avfoundation_mac.h
@@ -16,6 +16,27 @@
 
 namespace media {
 class VideoCaptureDeviceMac;
+
+class CAPTURE_EXPORT VideoCaptureDeviceAVFoundationFrameReceiver {
+ public:
+  virtual ~VideoCaptureDeviceAVFoundationFrameReceiver() = default;
+
+  virtual void ReceiveFrame(const uint8_t* video_frame,
+                            int video_frame_length,
+                            const VideoCaptureFormat& frame_format,
+                            const gfx::ColorSpace color_space,
+                            int aspect_numerator,
+                            int aspect_denominator,
+                            base::TimeDelta timestamp) = 0;
+  virtual void OnPhotoTaken(const uint8_t* image_data,
+                            size_t image_length,
+                            const std::string& mime_type) = 0;
+  virtual void OnPhotoError() = 0;
+  virtual void ReceiveError(VideoCaptureError error,
+                            const base::Location& from_here,
+                            const std::string& reason) = 0;
+};
+
 }  // namespace media
 
 // Class used by VideoCaptureDeviceMac (VCDM) for video and image capture using
@@ -47,6 +68,7 @@
 //    the VideoCaptureDeviceAVFoundation object.
 //
 //
+CAPTURE_EXPORT
 @interface VideoCaptureDeviceAVFoundation
     : NSObject<AVCaptureVideoDataOutputSampleBufferDelegate> {
  @private
@@ -59,7 +81,7 @@
   base::scoped_nsobject<AVCaptureDeviceFormat> _bestCaptureFormat;
 
   base::Lock _lock;  // Protects concurrent setting and using |frameReceiver_|.
-  media::VideoCaptureDeviceMac* _frameReceiver;  // weak.
+  media::VideoCaptureDeviceAVFoundationFrameReceiver* _frameReceiver;  // weak.
 
   base::scoped_nsobject<AVCaptureSession> _captureSession;
 
@@ -71,16 +93,27 @@
 
   // An AVDataOutput specialized for taking pictures out of |captureSession_|.
   base::scoped_nsobject<AVCaptureStillImageOutput> _stillImageOutput;
+  size_t _takePhotoStartedCount;
+  size_t _takePhotoPendingCount;
+  size_t _takePhotoCompletedCount;
+  bool _stillImageOutputWarmupCompleted;
+  std::unique_ptr<base::WeakPtrFactory<VideoCaptureDeviceAVFoundation>>
+      _weakPtrFactoryForTakePhoto;
 
-  base::ThreadChecker _main_thread_checker;
+  // For testing.
+  base::RepeatingCallback<void()> _onStillImageOutputStopped;
+
+  scoped_refptr<base::SingleThreadTaskRunner> _mainThreadTaskRunner;
 }
 
 // Initializes the instance and the underlying capture session and registers the
 // frame receiver.
-- (id)initWithFrameReceiver:(media::VideoCaptureDeviceMac*)frameReceiver;
+- (id)initWithFrameReceiver:
+    (media::VideoCaptureDeviceAVFoundationFrameReceiver*)frameReceiver;
 
 // Sets the frame receiver.
-- (void)setFrameReceiver:(media::VideoCaptureDeviceMac*)frameReceiver;
+- (void)setFrameReceiver:
+    (media::VideoCaptureDeviceAVFoundationFrameReceiver*)frameReceiver;
 
 // Sets which capture device to use by name, retrieved via |deviceNames|. Once
 // the deviceId is known, the library objects are created if needed and
@@ -111,6 +144,9 @@
 // -stopCapture.
 - (void)takePhoto;
 
+- (void)setOnStillImageOutputStoppedForTesting:
+    (base::RepeatingCallback<void()>)onStillImageOutputStopped;
+
 @end
 
 #endif  // MEDIA_CAPTURE_VIDEO_MAC_VIDEO_CAPTURE_DEVICE_AVFOUNDATION_MAC_H_
diff --git a/media/capture/video/mac/video_capture_device_avfoundation_mac.mm b/media/capture/video/mac/video_capture_device_avfoundation_mac.mm
index 3132309..3286dced 100644
--- a/media/capture/video/mac/video_capture_device_avfoundation_mac.mm
+++ b/media/capture/video/mac/video_capture_device_avfoundation_mac.mm
@@ -15,6 +15,7 @@
 #include "base/mac/foundation_util.h"
 #include "base/mac/mac_util.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/sequenced_task_runner.h"
 #include "base/strings/string_util.h"
 #include "base/strings/sys_string_conversions.h"
 #include "media/base/timestamp_constants.h"
@@ -26,14 +27,24 @@
 #include "services/video_capture/public/uma/video_capture_service_event.h"
 #include "ui/gfx/geometry/size.h"
 
+namespace {
+
+constexpr int kTimeToWaitBeforeStoppingStillImageCaptureInSeconds = 60;
+
+}  // anonymous namespace
+
 @implementation VideoCaptureDeviceAVFoundation
 
 #pragma mark Public methods
 
-- (id)initWithFrameReceiver:(media::VideoCaptureDeviceMac*)frameReceiver {
+- (id)initWithFrameReceiver:
+    (media::VideoCaptureDeviceAVFoundationFrameReceiver*)frameReceiver {
   if ((self = [super init])) {
-    DCHECK(_main_thread_checker.CalledOnValidThread());
+    _mainThreadTaskRunner = base::ThreadTaskRunnerHandle::Get();
     DCHECK(frameReceiver);
+    _weakPtrFactoryForTakePhoto =
+        std::make_unique<base::WeakPtrFactory<VideoCaptureDeviceAVFoundation>>(
+            self);
     [self setFrameReceiver:frameReceiver];
     _captureSession.reset([[AVCaptureSession alloc] init]);
   }
@@ -41,11 +52,15 @@
 }
 
 - (void)dealloc {
+  [self stopStillImageOutput];
   [self stopCapture];
+  _weakPtrFactoryForTakePhoto = nullptr;
+  _mainThreadTaskRunner = nullptr;
   [super dealloc];
 }
 
-- (void)setFrameReceiver:(media::VideoCaptureDeviceMac*)frameReceiver {
+- (void)setFrameReceiver:
+    (media::VideoCaptureDeviceAVFoundationFrameReceiver*)frameReceiver {
   base::AutoLock lock(_lock);
   _frameReceiver = frameReceiver;
 }
@@ -53,15 +68,14 @@
 - (BOOL)setCaptureDevice:(NSString*)deviceId
             errorMessage:(NSString**)outMessage {
   DCHECK(_captureSession);
-  DCHECK(_main_thread_checker.CalledOnValidThread());
+  DCHECK(_mainThreadTaskRunner->BelongsToCurrentThread());
 
   if (!deviceId) {
     // First stop the capture session, if it's running.
     [self stopCapture];
     // Now remove the input and output from the capture session.
     [_captureSession removeOutput:_captureVideoDataOutput];
-    if (_stillImageOutput)
-      [_captureSession removeOutput:_stillImageOutput];
+    [self stopStillImageOutput];
     if (_captureDeviceInput) {
       DCHECK(_captureDevice);
       [_captureSession stopRunning];
@@ -96,12 +110,6 @@
   }
   [_captureSession addInput:_captureDeviceInput];
 
-  // Create and plug the still image capture output. This should happen in
-  // advance of the actual picture to allow for the 3A to stabilize.
-  _stillImageOutput.reset([[AVCaptureStillImageOutput alloc] init]);
-  if (_stillImageOutput && [_captureSession canAddOutput:_stillImageOutput])
-    [_captureSession addOutput:_stillImageOutput];
-
   // Create a new data output for video. The data output is configured to
   // discard late frames by default.
   _captureVideoDataOutput.reset([[AVCaptureVideoDataOutput alloc] init]);
@@ -124,8 +132,8 @@
 - (BOOL)setCaptureHeight:(int)height
                    width:(int)width
                frameRate:(float)frameRate {
-  DCHECK(![_captureSession isRunning] &&
-         _main_thread_checker.CalledOnValidThread());
+  DCHECK(![_captureSession isRunning]);
+  DCHECK(_mainThreadTaskRunner->BelongsToCurrentThread());
 
   _frameWidth = width;
   _frameHeight = height;
@@ -134,8 +142,9 @@
       media::FindBestCaptureFormat([_captureDevice formats], width, height,
                                    frameRate),
       base::scoped_policy::RETAIN);
-
-  FourCharCode best_fourcc = kCMPixelFormat_422YpCbCr8;
+  // Default to NV12, a pixel format commonly supported by web cameras.
+  FourCharCode best_fourcc =
+      kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;  // NV12 (a.k.a. 420v)
   if (_bestCaptureFormat) {
     best_fourcc = CMFormatDescriptionGetMediaSubType(
         [_bestCaptureFormat formatDescription]);
@@ -188,7 +197,7 @@
 }
 
 - (BOOL)startCapture {
-  DCHECK(_main_thread_checker.CalledOnValidThread());
+  DCHECK(_mainThreadTaskRunner->BelongsToCurrentThread());
   if (!_captureSession) {
     DLOG(ERROR) << "Video capture session not initialized.";
     return NO;
@@ -214,56 +223,193 @@
 }
 
 - (void)stopCapture {
-  DCHECK(_main_thread_checker.CalledOnValidThread());
+  DCHECK(_mainThreadTaskRunner->BelongsToCurrentThread());
+  [self stopStillImageOutput];
   if ([_captureSession isRunning])
     [_captureSession stopRunning];  // Synchronous.
   [[NSNotificationCenter defaultCenter] removeObserver:self];
 }
 
 - (void)takePhoto {
-  DCHECK(_main_thread_checker.CalledOnValidThread());
+  DCHECK(_mainThreadTaskRunner->BelongsToCurrentThread());
   DCHECK([_captureSession isRunning]);
-  if (!_stillImageOutput)
-    return;
 
-  DCHECK_EQ(1u, [[_stillImageOutput connections] count]);
-  AVCaptureConnection* const connection =
-      [[_stillImageOutput connections] firstObject];
-  if (!connection) {
-    base::AutoLock lock(_lock);
-    _frameReceiver->OnPhotoError();
+  ++_takePhotoStartedCount;
+
+  // Ready to take a photo immediately?
+  if (_stillImageOutput && _stillImageOutputWarmupCompleted) {
+    [self takePhotoInternal];
     return;
   }
 
-  const auto handler = ^(CMSampleBufferRef sampleBuffer, NSError* error) {
-    base::AutoLock lock(_lock);
-    if (!_frameReceiver)
-      return;
-    if (error != nil) {
-      _frameReceiver->OnPhotoError();
+  // Lazily instantiate the |_stillImageOutput| the first time takePhoto() is
+  // called. When takePhoto() isn't called, this avoids JPEG compession work for
+  // every frame. This can save a lot of CPU in some cases (see
+  // https://crbug.com/1116241). However because it can take a couple of second
+  // for the 3A to stabilize, lazily instantiating like may result in noticeable
+  // delays. To avoid delays in future takePhoto() calls we don't delete
+  // |_stillImageOutput| until takePhoto() has not been called for 60 seconds.
+  if (!_stillImageOutput) {
+    // We use AVCaptureStillImageOutput for historical reasons, but note that it
+    // has been deprecated in macOS 10.15[1] in favor of
+    // AVCapturePhotoOutput[2].
+    //
+    // [1]
+    // https://developer.apple.com/documentation/avfoundation/avcapturestillimageoutput
+    // [2]
+    // https://developer.apple.com/documentation/avfoundation/avcapturephotooutput
+    // TODO(https://crbug.com/1124322): Migrate to the new API.
+    _stillImageOutput.reset([[AVCaptureStillImageOutput alloc] init]);
+    if (!_stillImageOutput ||
+        ![_captureSession canAddOutput:_stillImageOutput]) {
+      // Complete this started photo as error.
+      ++_takePhotoPendingCount;
+      {
+        base::AutoLock lock(_lock);
+        if (_frameReceiver) {
+          _frameReceiver->OnPhotoError();
+        }
+      }
+      [self takePhotoCompleted];
       return;
     }
+    [_captureSession addOutput:_stillImageOutput];
+    // A delay is needed before taking the photo or else the photo may be dark.
+    // 2 seconds was enough in manual testing; we delay by 3 for good measure.
+    _mainThreadTaskRunner->PostDelayedTask(
+        FROM_HERE,
+        base::BindOnce(
+            [](base::WeakPtr<VideoCaptureDeviceAVFoundation> weakSelf) {
+              [weakSelf.get() takePhotoInternal];
+            },
+            _weakPtrFactoryForTakePhoto->GetWeakPtr()),
+        base::TimeDelta::FromSeconds(3));
+  }
+}
 
-    // Recommended compressed pixel format is JPEG, we don't expect surprises.
-    // TODO(mcasas): Consider using [1] for merging EXIF output information:
-    // [1] +(NSData*)jpegStillImageNSDataRepresentation:jpegSampleBuffer;
-    DCHECK_EQ(kCMVideoCodecType_JPEG,
-              CMFormatDescriptionGetMediaSubType(
-                  CMSampleBufferGetFormatDescription(sampleBuffer)));
-
-    char* baseAddress = 0;
-    size_t length = 0;
-    media::ExtractBaseAddressAndLength(&baseAddress, &length, sampleBuffer);
-    _frameReceiver->OnPhotoTaken(reinterpret_cast<uint8_t*>(baseAddress),
-                                 length, "image/jpeg");
-  };
-
-  [_stillImageOutput captureStillImageAsynchronouslyFromConnection:connection
-                                                 completionHandler:handler];
+- (void)setOnStillImageOutputStoppedForTesting:
+    (base::RepeatingCallback<void()>)onStillImageOutputStopped {
+  DCHECK(_mainThreadTaskRunner->BelongsToCurrentThread());
+  _onStillImageOutputStopped = onStillImageOutputStopped;
 }
 
 #pragma mark Private methods
 
+- (void)takePhotoInternal {
+  DCHECK(_mainThreadTaskRunner->BelongsToCurrentThread());
+  // stopStillImageOutput invalidates all weak ptrs, meaning in-flight
+  // operations are affectively cancelled. So if this method is running, still
+  // image output must be good to go.
+  DCHECK([_captureSession isRunning]);
+  DCHECK(_stillImageOutput);
+  DCHECK([[_stillImageOutput connections] count] == 1);
+  AVCaptureConnection* const connection =
+      [[_stillImageOutput connections] firstObject];
+  DCHECK(connection);
+  _stillImageOutputWarmupCompleted = true;
+
+  // For all photos started that are not yet pending, take photos.
+  while (_takePhotoPendingCount < _takePhotoStartedCount) {
+    ++_takePhotoPendingCount;
+    const auto handler = ^(CMSampleBufferRef sampleBuffer, NSError* error) {
+      {
+        base::AutoLock lock(_lock);
+        if (_frameReceiver) {
+          if (error != nil) {
+            _frameReceiver->OnPhotoError();
+          } else {
+            // Recommended compressed pixel format is JPEG, we don't expect
+            // surprises.
+            // TODO(mcasas): Consider using [1] for merging EXIF output
+            // information:
+            // [1]
+            // +(NSData*)jpegStillImageNSDataRepresentation:jpegSampleBuffer;
+            DCHECK_EQ(kCMVideoCodecType_JPEG,
+                      CMFormatDescriptionGetMediaSubType(
+                          CMSampleBufferGetFormatDescription(sampleBuffer)));
+
+            char* baseAddress = 0;
+            size_t length = 0;
+            media::ExtractBaseAddressAndLength(&baseAddress, &length,
+                                               sampleBuffer);
+            _frameReceiver->OnPhotoTaken(
+                reinterpret_cast<uint8_t*>(baseAddress), length, "image/jpeg");
+          }
+        }
+      }
+      // Called both on success and failure.
+      _mainThreadTaskRunner->PostTask(
+          FROM_HERE,
+          base::BindOnce(
+              [](base::WeakPtr<VideoCaptureDeviceAVFoundation> weakSelf) {
+                [weakSelf.get() takePhotoCompleted];
+              },
+              _weakPtrFactoryForTakePhoto->GetWeakPtr()));
+    };
+    [_stillImageOutput captureStillImageAsynchronouslyFromConnection:connection
+                                                   completionHandler:handler];
+  }
+}
+
+- (void)takePhotoCompleted {
+  DCHECK(_mainThreadTaskRunner->BelongsToCurrentThread());
+  ++_takePhotoCompletedCount;
+  if (_takePhotoStartedCount != _takePhotoCompletedCount)
+    return;
+  // All pending takePhoto()s have completed. If no more photos are taken
+  // within 60 seconds, stop still image output to avoid expensive MJPEG
+  // conversions going forward.
+  _mainThreadTaskRunner->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(
+          [](base::WeakPtr<VideoCaptureDeviceAVFoundation> weakSelf,
+             size_t takePhotoCount) {
+            VideoCaptureDeviceAVFoundation* strongSelf = weakSelf.get();
+            if (!strongSelf)
+              return;
+            // Don't stop the still image output if takePhoto() was called
+            // while the task was pending.
+            if (strongSelf->_takePhotoStartedCount != takePhotoCount)
+              return;
+            [strongSelf stopStillImageOutput];
+          },
+          _weakPtrFactoryForTakePhoto->GetWeakPtr(), _takePhotoStartedCount),
+      base::TimeDelta::FromSeconds(
+          kTimeToWaitBeforeStoppingStillImageCaptureInSeconds));
+}
+
+- (void)stopStillImageOutput {
+  DCHECK(_mainThreadTaskRunner->BelongsToCurrentThread());
+  if (!_stillImageOutput) {
+    // Already stopped.
+    return;
+  }
+  if (_captureSession) {
+    [_captureSession removeOutput:_stillImageOutput];
+  }
+  _stillImageOutput.reset();
+  _stillImageOutputWarmupCompleted = false;
+
+  // Cancel all in-flight operations.
+  _weakPtrFactoryForTakePhoto->InvalidateWeakPtrs();
+  // Report error for all pending calls that were stopped.
+  size_t pendingCalls = _takePhotoStartedCount - _takePhotoCompletedCount;
+  _takePhotoCompletedCount = _takePhotoPendingCount = _takePhotoStartedCount;
+  {
+    base::AutoLock lock(_lock);
+    if (_frameReceiver) {
+      for (size_t i = 0; i < pendingCalls; ++i) {
+        _frameReceiver->OnPhotoError();
+      }
+    }
+  }
+
+  if (_onStillImageOutputStopped) {
+    // Callback used by tests.
+    _onStillImageOutputStopped.Run();
+  }
+}
+
 // |captureOutput| is called by the capture device to deliver a new frame.
 // AVFoundation calls from a number of threads, depending on, at least, if
 // Chrome is on foreground or background.
@@ -297,9 +443,25 @@
     if (videoFrame &&
         CVPixelBufferLockBaseAddress(videoFrame, kCVPixelBufferLock_ReadOnly) ==
             kCVReturnSuccess) {
-      baseAddress = static_cast<char*>(CVPixelBufferGetBaseAddress(videoFrame));
-      frameSize = CVPixelBufferGetHeight(videoFrame) *
-                  CVPixelBufferGetBytesPerRow(videoFrame);
+      if (!CVPixelBufferIsPlanar(videoFrame)) {
+        // For nonplanar buffers, CVPixelBufferGetBaseAddress returns a pointer
+        // to (0,0). (For planar buffers, it returns something else.)
+        // https://developer.apple.com/documentation/corevideo/1457115-cvpixelbuffergetbaseaddress?language=objc
+        baseAddress =
+            static_cast<char*>(CVPixelBufferGetBaseAddress(videoFrame));
+      } else {
+        // For planar buffers, CVPixelBufferGetBaseAddressOfPlane() is used. If
+        // the buffer is contiguous (CHECK'd below) then we only need to know
+        // the address of the first plane, regardless of
+        // CVPixelBufferGetPlaneCount().
+        baseAddress = static_cast<char*>(
+            CVPixelBufferGetBaseAddressOfPlane(videoFrame, 0));
+      }
+      // CVPixelBufferGetDataSize() works for both nonplanar and planar buffers
+      // as long as they are contiguous in memory.
+      frameSize = CVPixelBufferGetDataSize(videoFrame);
+      // Only contiguous buffers are supported.
+      CHECK(frameSize);
 
       // TODO(julien.isorce): move GetImageBufferColorSpace(CVImageBufferRef)
       // from media::VTVideoDecodeAccelerator to media/base/mac and call it
diff --git a/media/capture/video/mac/video_capture_device_avfoundation_mac_unittest.mm b/media/capture/video/mac/video_capture_device_avfoundation_mac_unittest.mm
new file mode 100644
index 0000000..63e3cf8
--- /dev/null
+++ b/media/capture/video/mac/video_capture_device_avfoundation_mac_unittest.mm
@@ -0,0 +1,216 @@
+// Copyright 2020 The Chromium 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/capture/video/mac/video_capture_device_avfoundation_mac.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/mac/scoped_nsobject.h"
+#include "base/run_loop.h"
+#include "base/test/gmock_callback_support.h"
+#import "media/capture/video/mac/test/mock_video_capture_device_avfoundation_frame_receiver_mac.h"
+#import "media/capture/video/mac/test/video_capture_test_utils_mac.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+
+namespace media {
+
+TEST(VideoCaptureDeviceAVFoundationMacTest, TakePhoto) {
+  RunTestCase(base::BindOnce([] {
+    NSString* deviceId = GetFirstDeviceId();
+    if (!deviceId) {
+      DVLOG(1) << "No camera available. Exiting test.";
+      return;
+    }
+
+    testing::NiceMock<MockVideoCaptureDeviceAVFoundationFrameReceiver>
+        frame_receiver;
+    base::scoped_nsobject<VideoCaptureDeviceAVFoundation> captureDevice(
+        [[VideoCaptureDeviceAVFoundation alloc]
+            initWithFrameReceiver:&frame_receiver]);
+
+    NSString* errorMessage = nil;
+    ASSERT_TRUE([captureDevice setCaptureDevice:deviceId
+                                   errorMessage:&errorMessage]);
+    ASSERT_TRUE([captureDevice startCapture]);
+
+    base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
+    EXPECT_CALL(frame_receiver, OnPhotoTaken)
+        .WillOnce(base::test::RunClosure(run_loop.QuitClosure()));
+    [captureDevice takePhoto];
+    run_loop.Run();
+  }));
+}
+
+TEST(VideoCaptureDeviceAVFoundationMacTest, StopCaptureWhileTakingPhoto) {
+  RunTestCase(base::BindOnce([] {
+    NSString* deviceId = GetFirstDeviceId();
+    if (!deviceId) {
+      DVLOG(1) << "No camera available. Exiting test.";
+      return;
+    }
+
+    testing::NiceMock<MockVideoCaptureDeviceAVFoundationFrameReceiver>
+        frame_receiver;
+    base::scoped_nsobject<VideoCaptureDeviceAVFoundation> captureDevice(
+        [[VideoCaptureDeviceAVFoundation alloc]
+            initWithFrameReceiver:&frame_receiver]);
+
+    NSString* errorMessage = nil;
+    ASSERT_TRUE([captureDevice setCaptureDevice:deviceId
+                                   errorMessage:&errorMessage]);
+    ASSERT_TRUE([captureDevice startCapture]);
+
+    base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
+    EXPECT_CALL(frame_receiver, OnPhotoError())
+        .WillOnce(base::test::RunClosure(run_loop.QuitClosure()));
+    [captureDevice takePhoto];
+    // There is no risk that takePhoto() has successfully finishes before
+    // stopCapture() because the takePhoto() call involes a PostDelayedTask()
+    // that cannot run until RunLoop::Run() below.
+    [captureDevice stopCapture];
+    run_loop.Run();
+  }));
+}
+
+TEST(VideoCaptureDeviceAVFoundationMacTest, MultiplePendingTakePhotos) {
+  RunTestCase(base::BindOnce([] {
+    NSString* deviceId = GetFirstDeviceId();
+    if (!deviceId) {
+      DVLOG(1) << "No camera available. Exiting test.";
+      return;
+    }
+
+    testing::NiceMock<MockVideoCaptureDeviceAVFoundationFrameReceiver>
+        frame_receiver;
+    base::scoped_nsobject<VideoCaptureDeviceAVFoundation> captureDevice(
+        [[VideoCaptureDeviceAVFoundation alloc]
+            initWithFrameReceiver:&frame_receiver]);
+
+    NSString* errorMessage = nil;
+    ASSERT_TRUE([captureDevice setCaptureDevice:deviceId
+                                   errorMessage:&errorMessage]);
+    ASSERT_TRUE([captureDevice startCapture]);
+
+    base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
+    size_t photos_taken_count = 0;
+    EXPECT_CALL(frame_receiver, OnPhotoTaken)
+        .WillRepeatedly([&photos_taken_count, &run_loop] {
+          ++photos_taken_count;
+          if (photos_taken_count == 3) {
+            run_loop.Quit();
+          }
+        });
+    [captureDevice takePhoto];
+    [captureDevice takePhoto];
+    [captureDevice takePhoto];
+    run_loop.Run();
+  }));
+}
+
+TEST(VideoCaptureDeviceAVFoundationMacTest,
+     StopCaptureWhileMultiplePendingTakePhotos) {
+  RunTestCase(base::BindOnce([] {
+    NSString* deviceId = GetFirstDeviceId();
+    if (!deviceId) {
+      DVLOG(1) << "No camera available. Exiting test.";
+      return;
+    }
+
+    testing::NiceMock<MockVideoCaptureDeviceAVFoundationFrameReceiver>
+        frame_receiver;
+    base::scoped_nsobject<VideoCaptureDeviceAVFoundation> captureDevice(
+        [[VideoCaptureDeviceAVFoundation alloc]
+            initWithFrameReceiver:&frame_receiver]);
+
+    NSString* errorMessage = nil;
+    ASSERT_TRUE([captureDevice setCaptureDevice:deviceId
+                                   errorMessage:&errorMessage]);
+    ASSERT_TRUE([captureDevice startCapture]);
+
+    base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
+    size_t photo_error_count = 0;
+    EXPECT_CALL(frame_receiver, OnPhotoError)
+        .WillRepeatedly([&photo_error_count, &run_loop] {
+          ++photo_error_count;
+          if (photo_error_count == 3) {
+            run_loop.Quit();
+          }
+        });
+    [captureDevice takePhoto];
+    [captureDevice takePhoto];
+    [captureDevice takePhoto];
+    // There is no risk that takePhoto() has successfully finishes before
+    // stopCapture() because the takePhoto() calls involes a PostDelayedTask()
+    // that cannot run until RunLoop::Run() below.
+    [captureDevice stopCapture];
+    run_loop.Run();
+  }));
+}
+
+TEST(VideoCaptureDeviceAVFoundationMacTest,
+     StopStillImageOutputWhenNoLongerTakingPhotos) {
+  RunTestCase(base::BindOnce([] {
+    NSString* deviceId = GetFirstDeviceId();
+    if (!deviceId) {
+      DVLOG(1) << "No camera available. Exiting test.";
+      return;
+    }
+
+    testing::NiceMock<MockVideoCaptureDeviceAVFoundationFrameReceiver>
+        frame_receiver;
+    base::scoped_nsobject<VideoCaptureDeviceAVFoundation> captureDevice(
+        [[VideoCaptureDeviceAVFoundation alloc]
+            initWithFrameReceiver:&frame_receiver]);
+
+    NSString* errorMessage = nil;
+    ASSERT_TRUE([captureDevice setCaptureDevice:deviceId
+                                   errorMessage:&errorMessage]);
+    ASSERT_TRUE([captureDevice startCapture]);
+
+    base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
+    [captureDevice
+        setOnStillImageOutputStoppedForTesting:run_loop.QuitClosure()];
+    base::TimeTicks start_time = base::TimeTicks::Now();
+    [captureDevice takePhoto];
+    // The RunLoop automatically advances mocked time when there are delayed
+    // tasks pending. This allows the test to run fast and still assert how much
+    // mocked time has elapsed.
+    run_loop.Run();
+    auto time_elapsed = base::TimeTicks::Now() - start_time;
+    // Still image output is not stopped until 60 seconds of inactivity, so the
+    // mocked time must have advanced at least this much.
+    EXPECT_GE(time_elapsed.InSeconds(), 60);
+  }));
+}
+
+// This test ensures we don't crash even if we leave operations pending.
+TEST(VideoCaptureDeviceAVFoundationMacTest,
+     TakePhotoAndShutDownWithoutWaiting) {
+  RunTestCase(base::BindOnce([] {
+    NSString* deviceId = GetFirstDeviceId();
+    if (!deviceId) {
+      DVLOG(1) << "No camera available. Exiting test.";
+      return;
+    }
+
+    testing::NiceMock<MockVideoCaptureDeviceAVFoundationFrameReceiver>
+        frame_receiver;
+    base::scoped_nsobject<VideoCaptureDeviceAVFoundation> captureDevice(
+        [[VideoCaptureDeviceAVFoundation alloc]
+            initWithFrameReceiver:&frame_receiver]);
+
+    NSString* errorMessage = nil;
+    ASSERT_TRUE([captureDevice setCaptureDevice:deviceId
+                                   errorMessage:&errorMessage]);
+    ASSERT_TRUE([captureDevice startCapture]);
+
+    [captureDevice takePhoto];
+  }));
+}
+
+}  // namespace media
diff --git a/media/capture/video/mac/video_capture_device_factory_mac_unittest.mm b/media/capture/video/mac/video_capture_device_factory_mac_unittest.mm
index 8b9d8ef..4249b01 100644
--- a/media/capture/video/mac/video_capture_device_factory_mac_unittest.mm
+++ b/media/capture/video/mac/video_capture_device_factory_mac_unittest.mm
@@ -7,48 +7,18 @@
 #include "base/run_loop.h"
 #include "base/test/bind_test_util.h"
 #include "base/test/task_environment.h"
+#import "media/capture/video/mac/test/video_capture_test_utils_mac.h"
 #include "media/capture/video/mac/video_capture_device_mac.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace media {
 
-// Video capture code on MacOSX must run on a CFRunLoop enabled thread
-// for interaction with AVFoundation.
-// In order to make the test case run on the actual message loop that has
-// been created for this thread, we need to run it inside a RunLoop. This is
-// required, because on MacOS the capture code must run on a CFRunLoop
-// enabled message loop.
-void RunTestCase(base::OnceClosure test_case) {
-  base::test::TaskEnvironment task_environment(
-      base::test::TaskEnvironment::MainThreadType::UI);
-  base::RunLoop run_loop;
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(
-                     [](base::RunLoop* run_loop, base::OnceClosure* test_case) {
-                       std::move(*test_case).Run();
-                       run_loop->Quit();
-                     },
-                     &run_loop, &test_case));
-  run_loop.Run();
-}
-
-void GetDevicesInfo(VideoCaptureDeviceFactoryMac* video_capture_device_factory,
-                    std::vector<VideoCaptureDeviceInfo>* descriptors) {
-  base::RunLoop run_loop;
-  video_capture_device_factory->GetDevicesInfo(base::BindLambdaForTesting(
-      [descriptors, &run_loop](std::vector<VideoCaptureDeviceInfo> result) {
-        *descriptors = std::move(result);
-        run_loop.Quit();
-      }));
-  run_loop.Run();
-}
-
 TEST(VideoCaptureDeviceFactoryMacTest, ListDevicesAVFoundation) {
   RunTestCase(base::BindOnce([]() {
     VideoCaptureDeviceFactoryMac video_capture_device_factory;
 
-    std::vector<VideoCaptureDeviceInfo> devices_info;
-    GetDevicesInfo(&video_capture_device_factory, &devices_info);
+    std::vector<VideoCaptureDeviceInfo> devices_info =
+        GetDevicesInfo(&video_capture_device_factory);
     if (devices_info.empty()) {
       DVLOG(1) << "No camera available. Exiting test.";
       return;
@@ -64,8 +34,8 @@
   RunTestCase(base::BindOnce([]() {
     VideoCaptureDeviceFactoryMac video_capture_device_factory;
 
-    std::vector<VideoCaptureDeviceInfo> devices_info;
-    GetDevicesInfo(&video_capture_device_factory, &devices_info);
+    std::vector<VideoCaptureDeviceInfo> devices_info =
+        GetDevicesInfo(&video_capture_device_factory);
     if (devices_info.empty()) {
       DVLOG(1) << "No camera available. Exiting test.";
       return;
diff --git a/media/capture/video/mac/video_capture_device_mac.h b/media/capture/video/mac/video_capture_device_mac.h
index 3df040d..ad4fd8f3 100644
--- a/media/capture/video/mac/video_capture_device_mac.h
+++ b/media/capture/video/mac/video_capture_device_mac.h
@@ -19,11 +19,10 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
+#import "media/capture/video/mac/video_capture_device_avfoundation_mac.h"
 #include "media/capture/video/video_capture_device.h"
 #include "media/capture/video_capture_types.h"
 
-@class VideoCaptureDeviceAVFoundation;
-
 namespace base {
 class SingleThreadTaskRunner;
 }
@@ -52,7 +51,9 @@
 
 // Called by VideoCaptureManager to open, close and start, stop Mac video
 // capture devices.
-class VideoCaptureDeviceMac : public VideoCaptureDevice {
+class VideoCaptureDeviceMac
+    : public VideoCaptureDevice,
+      public VideoCaptureDeviceAVFoundationFrameReceiver {
  public:
   explicit VideoCaptureDeviceMac(
       const VideoCaptureDeviceDescriptor& device_descriptor);
@@ -78,19 +79,19 @@
                     const gfx::ColorSpace color_space,
                     int aspect_numerator,
                     int aspect_denominator,
-                    base::TimeDelta timestamp);
+                    base::TimeDelta timestamp) override;
 
   // Callbacks with the result of a still image capture, or in case of error,
   // respectively. It's safe to call these methods from any thread.
   void OnPhotoTaken(const uint8_t* image_data,
                     size_t image_length,
-                    const std::string& mime_type);
-  void OnPhotoError();
+                    const std::string& mime_type) override;
+  void OnPhotoError() override;
 
   // Forwarder to VideoCaptureDevice::Client::OnError().
   void ReceiveError(VideoCaptureError error,
                     const base::Location& from_here,
-                    const std::string& reason);
+                    const std::string& reason) override;
 
   // Forwarder to VideoCaptureDevice::Client::OnLog().
   void LogMessage(const std::string& message);
diff --git a/media/ffmpeg/ffmpeg_common.cc b/media/ffmpeg/ffmpeg_common.cc
index aa4ea574..c17dd9f8 100644
--- a/media/ffmpeg/ffmpeg_common.cc
+++ b/media/ffmpeg/ffmpeg_common.cc
@@ -648,7 +648,7 @@
       if (side_data.type != AV_PKT_DATA_MASTERING_DISPLAY_METADATA)
         continue;
 
-      HDRMetadata hdr_metadata{};
+      gl::HDRMetadata hdr_metadata{};
       AVMasteringDisplayMetadata* metadata =
           reinterpret_cast<AVMasteringDisplayMetadata*>(side_data.data);
       if (metadata->has_primaries) {
diff --git a/media/filters/decoder_selector.cc b/media/filters/decoder_selector.cc
index 496f4abb..bb030ba 100644
--- a/media/filters/decoder_selector.cc
+++ b/media/filters/decoder_selector.cc
@@ -32,13 +32,14 @@
 
 const char kSelectDecoderTrace[] = "DecoderSelector::SelectDecoder";
 
-template <typename T>
-DecoderPriority UnspecifiedDecoderPriority(const T& /*config*/) {
-  return DecoderPriority::kUnspecified;
+template <typename ConfigT, typename DecoderT>
+DecoderPriority NormalDecoderPriority(const ConfigT& /*config*/,
+                                      const DecoderT& /*decoder*/) {
+  return DecoderPriority::kNormal;
 }
 
-DecoderPriority GetDefaultVideoDecoderPriority(
-    const VideoDecoderConfig& config) {
+DecoderPriority ResolutionBasedDecoderPriority(const VideoDecoderConfig& config,
+                                               const VideoDecoder& decoder) {
 #if defined(OS_ANDROID)
   constexpr auto kSoftwareDecoderHeightCutoff = 360;
 #elif defined(OS_CHROMEOS)
@@ -47,25 +48,31 @@
   constexpr auto kSoftwareDecoderHeightCutoff = 720;
 #endif
 
-  // We only do a height check to err on the side of hardware decoding
-  return config.visible_rect().height() < kSoftwareDecoderHeightCutoff
-             ? DecoderPriority::kPreferSoftwareDecoders
-             : DecoderPriority::kPreferPlatformDecoders;
+  // We only do a height check to err on the side of prioritizing platform
+  // decoders.
+  const auto at_or_above_software_cutoff =
+      config.visible_rect().height() >= kSoftwareDecoderHeightCutoff;
+
+  // Platform decoders are deprioritized below the cutoff, and non-platform
+  // decoders are deprioritized above it.
+  return at_or_above_software_cutoff == decoder.IsPlatformDecoder()
+             ? DecoderPriority::kNormal
+             : DecoderPriority::kDeprioritized;
 }
 
 void SetDefaultDecoderPriorityCB(VideoDecoderSelector::DecoderPriorityCB* out) {
   if (base::FeatureList::IsEnabled(kResolutionBasedDecoderPriority)) {
-    *out = base::BindRepeating(GetDefaultVideoDecoderPriority);
+    *out = base::BindRepeating(ResolutionBasedDecoderPriority);
   } else {
-    *out = base::BindRepeating<DecoderPriority(const VideoDecoderConfig&)>(
-        UnspecifiedDecoderPriority);
+    *out = base::BindRepeating(
+        NormalDecoderPriority<VideoDecoderConfig, VideoDecoder>);
   }
 }
 
 void SetDefaultDecoderPriorityCB(AudioDecoderSelector::DecoderPriorityCB* out) {
   // Platform audio decoders are not currently prioritized or deprioritized
-  *out = base::BindRepeating<DecoderPriority(const AudioDecoderConfig&)>(
-      UnspecifiedDecoderPriority);
+  *out = base::BindRepeating(
+      NormalDecoderPriority<AudioDecoderConfig, AudioDecoder>);
 }
 
 }  // namespace
@@ -346,31 +353,31 @@
 
 template <DemuxerStream::Type StreamType>
 void DecoderSelector<StreamType>::FilterAndSortAvailableDecoders() {
-  // Filter out any decoders that do not support decryption
-  if (config_.is_encrypted()) {
-    auto const non_decrypting = std::remove_if(
-        decoders_.begin(), decoders_.end(),
-        [](auto& decoder) { return !decoder->SupportsDecryption(); });
-    decoders_.erase(non_decrypting, decoders_.end());
+  std::vector<std::unique_ptr<Decoder>> decoders = std::move(decoders_);
+  std::vector<std::unique_ptr<Decoder>> deprioritized_decoders;
+
+  for (auto& decoder : decoders) {
+    // Skip the decoder if this decoder doesn't support encryption for a
+    // decrypting config
+    if (config_.is_encrypted() && !decoder->SupportsDecryption())
+      continue;
+
+    // Run the predicate on this decoder.
+    switch (decoder_priority_cb_.Run(config_, *decoder)) {
+      case DecoderPriority::kSkipped:
+        continue;
+      case DecoderPriority::kNormal:
+        decoders_.push_back(std::move(decoder));
+        break;
+      case DecoderPriority::kDeprioritized:
+        deprioritized_decoders.push_back(std::move(decoder));
+        break;
+    }
   }
 
-  // If platform decoders are prioritized for this config, shift all platform
-  // decoders to the front of the list (retaining their relative order).
-  const auto decoder_priority = decoder_priority_cb_.Run(config_);
-  switch (decoder_priority) {
-    case DecoderPriority::kUnspecified:
-      break;
-    case DecoderPriority::kPreferPlatformDecoders:
-    case DecoderPriority::kPreferSoftwareDecoders: {
-      auto prefer_platform_decoder =
-          decoder_priority == DecoderPriority::kPreferPlatformDecoders;
-      std::stable_partition(decoders_.begin(), decoders_.end(),
-                            [prefer_platform_decoder](auto& decoder) {
-                              return decoder->IsPlatformDecoder() ==
-                                     prefer_platform_decoder;
-                            });
-    } break;
-  }
+  // Post-insert deprioritized decoders
+  std::move(deprioritized_decoders.begin(), deprioritized_decoders.end(),
+            std::inserter(decoders_, decoders_.end()));
 }
 
 // These forward declarations tell the compiler that we will use
diff --git a/media/filters/decoder_selector.h b/media/filters/decoder_selector.h
index 2070411..55b4242 100644
--- a/media/filters/decoder_selector.h
+++ b/media/filters/decoder_selector.h
@@ -28,10 +28,19 @@
 class DecryptingDemuxerStream;
 class MediaLog;
 
+// Enum returned by `DecoderSelector::DecoderPriorityCB` to indicate
+// priority of the current decoder.
 enum class DecoderPriority {
-  kUnspecified,
-  kPreferPlatformDecoders,
-  kPreferSoftwareDecoders,
+  // `kNormal` indicates that the current decoder should continue through with
+  // selection in it's current order.
+  kNormal,
+
+  // `kDeprioritized` indicates that the current decoder should only be selected
+  // if other decoders have failed.
+  kDeprioritized,
+
+  // `kSkipped` indicates that the current decoder should not be used at all.
+  kSkipped,
 };
 
 // DecoderSelector handles construction and initialization of Decoders for a
@@ -51,10 +60,11 @@
   using CreateDecodersCB =
       base::RepeatingCallback<std::vector<std::unique_ptr<Decoder>>()>;
 
-  // Evaluates what type of decoders should be prioritized for the given config.
-  // If |kUnspecified| is returned, nothing is prioritized.
+  // Prediate to evaluate whether a decoder should be prioritized,
+  // deprioritized, or skipped.
   using DecoderPriorityCB =
-      base::RepeatingCallback<DecoderPriority(const DecoderConfig&)>;
+      base::RepeatingCallback<DecoderPriority(const DecoderConfig&,
+                                              const Decoder&)>;
 
   // Emits the result of a single call to SelectDecoder(). Parameters are
   //   1: The initialized Decoder. nullptr if selection failed.
diff --git a/media/filters/decoder_selector_unittest.cc b/media/filters/decoder_selector_unittest.cc
index d018657..df29425a 100644
--- a/media/filters/decoder_selector_unittest.cc
+++ b/media/filters/decoder_selector_unittest.cc
@@ -11,9 +11,11 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/notreached.h"
 #include "base/test/gmock_callback_support.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "build/build_config.h"
 #include "media/base/demuxer_stream.h"
+#include "media/base/media_switches.h"
 #include "media/base/media_util.h"
 #include "media/base/mock_filters.h"
 #include "media/base/test_helpers.h"
@@ -106,15 +108,23 @@
   }
 
   static media::DecoderPriority MockDecoderPriorityCB(
-      const media::AudioDecoderConfig& config) {
-    return config.samples_per_second() >
-                   TestAudioConfig::NormalSampleRateValue()
-               ? media::DecoderPriority::kPreferPlatformDecoders
-               : media::DecoderPriority::kPreferSoftwareDecoders;
+      const media::AudioDecoderConfig& config,
+      const media::AudioDecoder& decoder) {
+    const auto above_cutoff =
+        config.samples_per_second() > TestAudioConfig::NormalSampleRateValue();
+    return above_cutoff == decoder.IsPlatformDecoder()
+               ? media::DecoderPriority::kNormal
+               : media::DecoderPriority::kDeprioritized;
   }
-  static media::DecoderPriority UnspecifiedDecoderPriorityCB(
-      const media::AudioDecoderConfig& /*config*/) {
-    return media::DecoderPriority::kUnspecified;
+  static media::DecoderPriority NormalDecoderPriorityCB(
+      const media::AudioDecoderConfig& /*config*/,
+      const media::AudioDecoder& /*decoder*/) {
+    return media::DecoderPriority::kNormal;
+  }
+  static media::DecoderPriority SkipDecoderPriorityCB(
+      const media::AudioDecoderConfig& /*config*/,
+      const media::AudioDecoder& /*decoder*/) {
+    return media::DecoderPriority::kSkipped;
   }
 
   static void UseNormalClearDecoderConfig(
@@ -177,15 +187,23 @@
   }
 
   static media::DecoderPriority MockDecoderPriorityCB(
-      const media::VideoDecoderConfig& config) {
-    return config.visible_rect().height() >
-                   TestVideoConfig::NormalCodedSize().height()
-               ? media::DecoderPriority::kPreferPlatformDecoders
-               : media::DecoderPriority::kPreferSoftwareDecoders;
+      const media::VideoDecoderConfig& config,
+      const media::VideoDecoder& decoder) {
+    auto const above_cutoff = config.visible_rect().height() >
+                              TestVideoConfig::NormalCodedSize().height();
+    return decoder.IsPlatformDecoder() == above_cutoff
+               ? media::DecoderPriority::kNormal
+               : media::DecoderPriority::kDeprioritized;
   }
-  static media::DecoderPriority UnspecifiedDecoderPriorityCB(
-      const media::VideoDecoderConfig& /*config*/) {
-    return media::DecoderPriority::kUnspecified;
+  static media::DecoderPriority NormalDecoderPriorityCB(
+      const media::VideoDecoderConfig& /*config*/,
+      const media::VideoDecoder& /*decoder*/) {
+    return media::DecoderPriority::kNormal;
+  }
+  static media::DecoderPriority SkipDecoderPriorityCB(
+      const media::VideoDecoderConfig& /*config*/,
+      const media::VideoDecoder& /*decoder*/) {
+    return media::DecoderPriority::kSkipped;
   }
 
   static void UseNormalClearDecoderConfig(
@@ -374,8 +392,6 @@
         task_environment_.GetMainThreadTaskRunner(),
         base::BindRepeating(&Self::CreateDecoders, base::Unretained(this)),
         &media_log_);
-    decoder_selector_->OverrideDecoderPriorityCBForTesting(
-        base::BindRepeating(TypeParam::MockDecoderPriorityCB));
     decoder_selector_->Initialize(
         traits_.get(), &demuxer_stream_, cdm_context_.get(),
         base::BindRepeating(&Self::OnWaiting, base::Unretained(this)));
@@ -420,6 +436,9 @@
   DISALLOW_COPY_AND_ASSIGN(DecoderSelectorTest);
 };
 
+using VideoDecoderSelectorTest =
+    DecoderSelectorTest<VideoDecoderSelectorTestParam>;
+
 using DecoderSelectorTestParams =
     ::testing::Types<AudioDecoderSelectorTestParam,
                      VideoDecoderSelectorTestParam>;
@@ -505,6 +524,8 @@
 
   this->UseHighQualityClearDecoderConfig();
   this->CreateDecoderSelector();
+  this->decoder_selector_->OverrideDecoderPriorityCBForTesting(
+      base::BindRepeating(TypeParam::MockDecoderPriorityCB));
 
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder1, IsNull()));
   this->SelectDecoder();
@@ -529,6 +550,8 @@
 
   this->UseClearDecoderConfig();
   this->CreateDecoderSelector();
+  this->decoder_selector_->OverrideDecoderPriorityCBForTesting(
+      base::BindRepeating(TypeParam::MockDecoderPriorityCB));
 
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder2, IsNull()));
   this->SelectDecoder();
@@ -544,9 +567,9 @@
 }
 
 // Tests that platform and non-platform decoders remain in the order they are
-// given for a priority callback returning 'kNop'.
+// given for a priority callback returning 'kNormal'.
 TYPED_TEST(DecoderSelectorTest,
-           ClearStream_NopPriorityCallbackRetainsGivenOrder) {
+           ClearStream_NormalPriorityCallbackRetainsGivenOrder) {
   this->AddMockPlatformDecoder(kDecoder1, kAlwaysSucceed);
   this->AddMockDecoder(kDecoder2, kAlwaysSucceed);
   this->AddMockPlatformDecoder(kDecoder3, kAlwaysSucceed);
@@ -555,7 +578,7 @@
   this->UseClearDecoderConfig();
   this->CreateDecoderSelector();
   this->decoder_selector_->OverrideDecoderPriorityCBForTesting(
-      base::BindRepeating(TypeParam::UnspecifiedDecoderPriorityCB));
+      base::BindRepeating(TypeParam::NormalDecoderPriorityCB));
 
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder1, IsNull()));
   this->SelectDecoder();
@@ -570,6 +593,77 @@
   this->SelectDecoder();
 }
 
+TYPED_TEST(DecoderSelectorTest, ClearStream_SkipAllDecoders) {
+  this->AddMockPlatformDecoder(kDecoder1, kAlwaysSucceed);
+  this->AddMockDecoder(kDecoder2, kAlwaysSucceed);
+  this->AddMockPlatformDecoder(kDecoder3, kAlwaysSucceed);
+  this->AddMockDecoder(kDecoder4, kAlwaysSucceed);
+
+  this->UseClearDecoderConfig();
+  this->CreateDecoderSelector();
+  this->decoder_selector_->OverrideDecoderPriorityCBForTesting(
+      base::BindRepeating(TypeParam::SkipDecoderPriorityCB));
+
+  EXPECT_CALL(*this, OnDecoderSelected(kNoDecoder, IsNull()));
+  this->SelectDecoder();
+}
+
+// Tests the production predicate for `DecoderSelector<DemuxerStream::VIDEO>`
+TEST_F(VideoDecoderSelectorTest, ClearStream_PrioritizeSoftwareDecoders) {
+  base::test::ScopedFeatureList features;
+  features.InitAndEnableFeature(kResolutionBasedDecoderPriority);
+
+  this->AddMockPlatformDecoder(kDecoder1, kClearOnly);
+  this->AddMockDecoder(kDecoder2, kClearOnly);
+  this->AddMockPlatformDecoder(kDecoder3, kAlwaysSucceed);
+  this->AddMockDecoder(kDecoder4, kAlwaysSucceed);
+
+  // Create a clear config that will cause software decoders to be
+  // prioritized on any platform.
+  this->demuxer_stream_.set_video_decoder_config(
+      TestVideoConfig::Custom(gfx::Size(64, 64)));
+  this->CreateDecoderSelector();
+
+  EXPECT_CALL(*this, OnDecoderSelected(kDecoder2, IsNull()));
+  this->SelectDecoder();
+  EXPECT_CALL(*this, OnDecoderSelected(kDecoder4, IsNull()));
+  this->SelectDecoder();
+  EXPECT_CALL(*this, OnDecoderSelected(kDecoder1, IsNull()));
+  this->SelectDecoder();
+  EXPECT_CALL(*this, OnDecoderSelected(kDecoder3, IsNull()));
+  this->SelectDecoder();
+  EXPECT_CALL(*this, OnDecoderSelected(kNoDecoder, IsNull()));
+  this->SelectDecoder();
+}
+
+// Tests the production predicate for `DecoderSelector<DemuxerStream::VIDEO>`
+TEST_F(VideoDecoderSelectorTest, ClearStream_PrioritizePlatformDecoders) {
+  base::test::ScopedFeatureList features;
+  features.InitAndEnableFeature(kResolutionBasedDecoderPriority);
+
+  this->AddMockPlatformDecoder(kDecoder1, kClearOnly);
+  this->AddMockDecoder(kDecoder2, kClearOnly);
+  this->AddMockPlatformDecoder(kDecoder3, kAlwaysSucceed);
+  this->AddMockDecoder(kDecoder4, kAlwaysSucceed);
+
+  // Create a clear config that will cause hardware decoders to be prioritized
+  // on any platform.
+  this->demuxer_stream_.set_video_decoder_config(
+      TestVideoConfig::Custom(gfx::Size(4096, 4096)));
+  this->CreateDecoderSelector();
+
+  EXPECT_CALL(*this, OnDecoderSelected(kDecoder1, IsNull()));
+  this->SelectDecoder();
+  EXPECT_CALL(*this, OnDecoderSelected(kDecoder3, IsNull()));
+  this->SelectDecoder();
+  EXPECT_CALL(*this, OnDecoderSelected(kDecoder2, IsNull()));
+  this->SelectDecoder();
+  EXPECT_CALL(*this, OnDecoderSelected(kDecoder4, IsNull()));
+  this->SelectDecoder();
+  EXPECT_CALL(*this, OnDecoderSelected(kNoDecoder, IsNull()));
+  this->SelectDecoder();
+}
+
 // Tests for encrypted streams.
 
 // Tests that non-decrypting decoders are filtered out by DecoderSelector
@@ -604,6 +698,8 @@
 
   this->UseHighQualityEncryptedDecoderConfig();
   this->CreateDecoderSelector();
+  this->decoder_selector_->OverrideDecoderPriorityCBForTesting(
+      base::BindRepeating(TypeParam::MockDecoderPriorityCB));
 
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder1, IsNull()));
   this->SelectDecoder();
@@ -628,6 +724,8 @@
 
   this->UseEncryptedDecoderConfig();
   this->CreateDecoderSelector();
+  this->decoder_selector_->OverrideDecoderPriorityCBForTesting(
+      base::BindRepeating(TypeParam::MockDecoderPriorityCB));
 
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder2, IsNull()));
   this->SelectDecoder();
@@ -643,9 +741,9 @@
 }
 
 // Tests that platform and non-platform decoders remain in the order they are
-// given for a priority callback returning 'kNop'.
+// given for a priority callback returning 'kNormal'.
 TYPED_TEST(DecoderSelectorTest,
-           EncryptedStream_NopPriorityCallbackRetainsGivenOrder) {
+           EncryptedStream_NormalPriorityCallbackRetainsGivenOrder) {
   this->AddMockPlatformDecoder(kDecoder1, kAlwaysSucceed);
   this->AddMockDecoder(kDecoder2, kAlwaysSucceed);
   this->AddMockPlatformDecoder(kDecoder3, kAlwaysSucceed);
@@ -654,7 +752,7 @@
   this->UseEncryptedDecoderConfig();
   this->CreateDecoderSelector();
   this->decoder_selector_->OverrideDecoderPriorityCBForTesting(
-      base::BindRepeating(TypeParam::UnspecifiedDecoderPriorityCB));
+      base::BindRepeating(TypeParam::NormalDecoderPriorityCB));
 
   EXPECT_CALL(*this, OnDecoderSelected(kDecoder1, IsNull()));
   this->SelectDecoder();
@@ -669,6 +767,21 @@
   this->SelectDecoder();
 }
 
+TYPED_TEST(DecoderSelectorTest, EncryptedStream_SkipAllDecoders) {
+  this->AddMockPlatformDecoder(kDecoder1, kAlwaysSucceed);
+  this->AddMockDecoder(kDecoder2, kAlwaysSucceed);
+  this->AddMockPlatformDecoder(kDecoder3, kAlwaysSucceed);
+  this->AddMockDecoder(kDecoder4, kAlwaysSucceed);
+
+  this->UseEncryptedDecoderConfig();
+  this->CreateDecoderSelector();
+  this->decoder_selector_->OverrideDecoderPriorityCBForTesting(
+      base::BindRepeating(TypeParam::SkipDecoderPriorityCB));
+
+  EXPECT_CALL(*this, OnDecoderSelected(kNoDecoder, IsNull()));
+  this->SelectDecoder();
+}
+
 TYPED_TEST(DecoderSelectorTest, EncryptedStream_NoDecryptor_OneClearDecoder) {
   this->AddMockDecoder(kDecoder1, kClearOnly);
   this->CreateCdmContext(kNoDecryptor);
@@ -842,4 +955,52 @@
   this->SelectDecoder();
 }
 
+// Tests the production predicate for `DecoderSelector<DemuxerStream::VIDEO>`
+TEST_F(VideoDecoderSelectorTest, EncryptedStream_PrioritizeSoftwareDecoders) {
+  base::test::ScopedFeatureList features;
+  features.InitAndEnableFeature(kResolutionBasedDecoderPriority);
+
+  this->AddMockPlatformDecoder(kDecoder1, kClearOnly);
+  this->AddMockDecoder(kDecoder2, kClearOnly);
+  this->AddMockPlatformDecoder(kDecoder3, kAlwaysSucceed);
+  this->AddMockDecoder(kDecoder4, kAlwaysSucceed);
+
+  // Create an encrypted config that will cause software decoders to be
+  // prioritized on any platform.
+  this->demuxer_stream_.set_video_decoder_config(
+      TestVideoConfig::CustomEncrypted(gfx::Size(64, 64)));
+  this->CreateDecoderSelector();
+
+  EXPECT_CALL(*this, OnDecoderSelected(kDecoder4, IsNull()));
+  this->SelectDecoder();
+  EXPECT_CALL(*this, OnDecoderSelected(kDecoder3, IsNull()));
+  this->SelectDecoder();
+  EXPECT_CALL(*this, OnDecoderSelected(kNoDecoder, IsNull()));
+  this->SelectDecoder();
+}
+
+// Tests the production predicate for `DecoderSelector<DemuxerStream::VIDEO>`
+TEST_F(VideoDecoderSelectorTest, EncryptedStream_PrioritizePlatformDecoders) {
+  base::test::ScopedFeatureList features;
+  features.InitAndEnableFeature(kResolutionBasedDecoderPriority);
+
+  this->AddMockPlatformDecoder(kDecoder1, kClearOnly);
+  this->AddMockDecoder(kDecoder2, kClearOnly);
+  this->AddMockPlatformDecoder(kDecoder3, kAlwaysSucceed);
+  this->AddMockDecoder(kDecoder4, kAlwaysSucceed);
+
+  // Create an encrypted config that will cause hardware decoders to be
+  // prioritized on any platform.
+  this->demuxer_stream_.set_video_decoder_config(
+      TestVideoConfig::CustomEncrypted(gfx::Size(4096, 4096)));
+  this->CreateDecoderSelector();
+
+  EXPECT_CALL(*this, OnDecoderSelected(kDecoder3, IsNull()));
+  this->SelectDecoder();
+  EXPECT_CALL(*this, OnDecoderSelected(kDecoder4, IsNull()));
+  this->SelectDecoder();
+  EXPECT_CALL(*this, OnDecoderSelected(kNoDecoder, IsNull()));
+  this->SelectDecoder();
+}
+
 }  // namespace media
diff --git a/media/filters/video_decoder_stream_unittest.cc b/media/filters/video_decoder_stream_unittest.cc
index 3f13767d..75d0f30 100644
--- a/media/filters/video_decoder_stream_unittest.cc
+++ b/media/filters/video_decoder_stream_unittest.cc
@@ -51,11 +51,13 @@
   return std::string("VideoDecoder") + base::NumberToString(i);
 }
 
-DecoderPriority MockDecoderPriority(const VideoDecoderConfig& config) {
-  return config.visible_rect().height() >=
-                 TestVideoConfig::LargeCodedSize().height()
-             ? DecoderPriority::kPreferPlatformDecoders
-             : DecoderPriority::kPreferSoftwareDecoders;
+DecoderPriority MockDecoderPriority(const VideoDecoderConfig& config,
+                                    const VideoDecoder& decoder) {
+  auto const at_or_above_cutoff = config.visible_rect().height() >=
+                                  TestVideoConfig::LargeCodedSize().height();
+  return at_or_above_cutoff == decoder.IsPlatformDecoder()
+             ? DecoderPriority::kNormal
+             : DecoderPriority::kDeprioritized;
 }
 
 }  // namespace
diff --git a/media/formats/mp4/mp4_stream_parser.cc b/media/formats/mp4/mp4_stream_parser.cc
index b96c641..8899fda6 100644
--- a/media/formats/mp4/mp4_stream_parser.cc
+++ b/media/formats/mp4/mp4_stream_parser.cc
@@ -60,18 +60,18 @@
   return EncryptionScheme::kUnencrypted;
 }
 
-MasteringMetadata ConvertMdcvToMasteringMetadata(
+gl::MasteringMetadata ConvertMdcvToMasteringMetadata(
     const MasteringDisplayColorVolume& mdcv) {
-  MasteringMetadata mastering_metadata;
+  gl::MasteringMetadata mastering_metadata;
 
-  mastering_metadata.primary_r = MasteringMetadata::Chromaticity(
+  mastering_metadata.primary_r = gl::MasteringMetadata::Chromaticity(
       mdcv.display_primaries_rx, mdcv.display_primaries_ry);
-  mastering_metadata.primary_g = MasteringMetadata::Chromaticity(
+  mastering_metadata.primary_g = gl::MasteringMetadata::Chromaticity(
       mdcv.display_primaries_gx, mdcv.display_primaries_gy);
-  mastering_metadata.primary_b = MasteringMetadata::Chromaticity(
+  mastering_metadata.primary_b = gl::MasteringMetadata::Chromaticity(
       mdcv.display_primaries_bx, mdcv.display_primaries_by);
-  mastering_metadata.white_point =
-      MasteringMetadata::Chromaticity(mdcv.white_point_x, mdcv.white_point_y);
+  mastering_metadata.white_point = gl::MasteringMetadata::Chromaticity(
+      mdcv.white_point_x, mdcv.white_point_y);
 
   mastering_metadata.luminance_max = mdcv.max_display_mastering_luminance;
   mastering_metadata.luminance_min = mdcv.min_display_mastering_luminance;
@@ -544,7 +544,7 @@
 
       if (entry.mastering_display_color_volume ||
           entry.content_light_level_information) {
-        HDRMetadata hdr_metadata;
+        gl::HDRMetadata hdr_metadata;
         if (entry.mastering_display_color_volume) {
           hdr_metadata.mastering_metadata = ConvertMdcvToMasteringMetadata(
               *entry.mastering_display_color_volume);
diff --git a/media/formats/webm/webm_colour_parser.cc b/media/formats/webm/webm_colour_parser.cc
index a804931..966f954 100644
--- a/media/formats/webm/webm_colour_parser.cc
+++ b/media/formats/webm/webm_colour_parser.cc
@@ -192,7 +192,7 @@
 
   if (max_content_light_level_ != -1 || max_frame_average_light_level_ != -1 ||
       mastering_metadata_parsed_) {
-    color_metadata.hdr_metadata = HDRMetadata();
+    color_metadata.hdr_metadata = gl::HDRMetadata();
 
     if (max_content_light_level_ != -1) {
       color_metadata.hdr_metadata->max_content_light_level =
diff --git a/media/formats/webm/webm_colour_parser.h b/media/formats/webm/webm_colour_parser.h
index af05479..ae474ca72 100644
--- a/media/formats/webm/webm_colour_parser.h
+++ b/media/formats/webm/webm_colour_parser.h
@@ -7,9 +7,9 @@
 
 #include "base/macros.h"
 #include "base/optional.h"
-#include "media/base/hdr_metadata.h"
 #include "media/base/video_color_space.h"
 #include "media/formats/webm/webm_parser.h"
+#include "ui/gl/hdr_metadata.h"
 
 namespace media {
 
@@ -26,7 +26,7 @@
 
   VideoColorSpace color_space;
 
-  base::Optional<HDRMetadata> hdr_metadata;
+  base::Optional<gl::HDRMetadata> hdr_metadata;
 
   WebMColorMetadata();
   WebMColorMetadata(const WebMColorMetadata& rhs);
@@ -39,13 +39,15 @@
   WebMMasteringMetadataParser();
   ~WebMMasteringMetadataParser() override;
 
-  MasteringMetadata GetMasteringMetadata() const { return mastering_metadata_; }
+  gl::MasteringMetadata GetMasteringMetadata() const {
+    return mastering_metadata_;
+  }
 
  private:
   // WebMParserClient implementation.
   bool OnFloat(int id, double val) override;
 
-  MasteringMetadata mastering_metadata_;
+  gl::MasteringMetadata mastering_metadata_;
   DISALLOW_COPY_AND_ASSIGN(WebMMasteringMetadataParser);
 };
 
diff --git a/media/formats/webm/webm_stream_parser_unittest.cc b/media/formats/webm/webm_stream_parser_unittest.cc
index b62f5a5f..438a871 100644
--- a/media/formats/webm/webm_stream_parser_unittest.cc
+++ b/media/formats/webm/webm_stream_parser_unittest.cc
@@ -176,12 +176,12 @@
                                        gfx::ColorSpace::RangeID::FULL);
   EXPECT_EQ(video_config.color_space_info(), expected_color_space);
 
-  base::Optional<HDRMetadata> hdr_metadata = video_config.hdr_metadata();
+  base::Optional<gl::HDRMetadata> hdr_metadata = video_config.hdr_metadata();
   EXPECT_TRUE(hdr_metadata.has_value());
   EXPECT_EQ(hdr_metadata->max_content_light_level, 11u);
   EXPECT_EQ(hdr_metadata->max_frame_average_light_level, 12u);
 
-  const MasteringMetadata& mmdata = hdr_metadata->mastering_metadata;
+  const gl::MasteringMetadata& mmdata = hdr_metadata->mastering_metadata;
   EXPECT_FLOAT_EQ(mmdata.primary_r.x(), 0.1f);
   EXPECT_FLOAT_EQ(mmdata.primary_r.y(), 0.2f);
   EXPECT_FLOAT_EQ(mmdata.primary_g.x(), 0.1f);
diff --git a/media/gpu/android/media_codec_video_decoder_unittest.cc b/media/gpu/android/media_codec_video_decoder_unittest.cc
index 3a92d0e..21591dd26e 100644
--- a/media/gpu/android/media_codec_video_decoder_unittest.cc
+++ b/media/gpu/android/media_codec_video_decoder_unittest.cc
@@ -963,7 +963,7 @@
 
 TEST_P(MediaCodecVideoDecoderVp9Test, HdrMetadataIsIncludedInCodecConfig) {
   VideoDecoderConfig config = TestVideoConfig::Normal(kCodecVP9);
-  HDRMetadata hdr_metadata;
+  gl::HDRMetadata hdr_metadata;
   hdr_metadata.max_frame_average_light_level = 123;
   hdr_metadata.max_content_light_level = 456;
   hdr_metadata.mastering_metadata.primary_r.set_x(0.1f);
diff --git a/media/gpu/mac/vt_config_util.h b/media/gpu/mac/vt_config_util.h
index e640f42..acb58d7d 100644
--- a/media/gpu/mac/vt_config_util.h
+++ b/media/gpu/mac/vt_config_util.h
@@ -9,11 +9,11 @@
 #include <CoreMedia/CoreMedia.h>
 
 #include "base/optional.h"
-#include "media/base/hdr_metadata.h"
 #include "media/base/video_codecs.h"
 #include "media/base/video_color_space.h"
 #include "media/gpu/media_gpu_export.h"
 #include "media/video/video_decode_accelerator.h"
+#include "ui/gl/hdr_metadata.h"
 
 namespace media {
 
@@ -21,7 +21,7 @@
 CreateFormatExtensions(CMVideoCodecType codec_type,
                        VideoCodecProfile profile,
                        const VideoColorSpace& color_space,
-                       base::Optional<HDRMetadata> hdr_metadata);
+                       base::Optional<gl::HDRMetadata> hdr_metadata);
 
 MEDIA_GPU_EXPORT gfx::ColorSpace GetImageBufferColorSpace(
     CVImageBufferRef image_buffer);
diff --git a/media/gpu/mac/vt_config_util.mm b/media/gpu/mac/vt_config_util.mm
index 2a72f649..37bf96f 100644
--- a/media/gpu/mac/vt_config_util.mm
+++ b/media/gpu/mac/vt_config_util.mm
@@ -173,7 +173,7 @@
   }
 }
 
-void SetContentLightLevelInfo(const media::HDRMetadata& hdr_metadata,
+void SetContentLightLevelInfo(const gl::HDRMetadata& hdr_metadata,
                               NSMutableDictionary<NSString*, id>* extensions) {
   if (@available(macos 10.13, *)) {
     // This is a SMPTEST2086 Content Light Level Information box.
@@ -200,7 +200,7 @@
   }
 }
 
-void SetMasteringMetadata(const media::HDRMetadata& hdr_metadata,
+void SetMasteringMetadata(const gl::HDRMetadata& hdr_metadata,
                           NSMutableDictionary<NSString*, id>* extensions) {
   if (@available(macos 10.13, *)) {
     // This is a SMPTEST2086 Mastering Display Color Volume box.
@@ -469,7 +469,7 @@
     CMVideoCodecType codec_type,
     VideoCodecProfile profile,
     const VideoColorSpace& color_space,
-    base::Optional<HDRMetadata> hdr_metadata) {
+    base::Optional<gl::HDRMetadata> hdr_metadata) {
   auto* extensions = [[NSMutableDictionary alloc] init];
   SetDictionaryValue(extensions, kCMFormatDescriptionExtension_FormatName,
                      CMVideoCodecTypeToString(codec_type));
diff --git a/media/gpu/mac/vt_config_util_unittest.cc b/media/gpu/mac/vt_config_util_unittest.cc
index 43aff8a3..17a95d0f 100644
--- a/media/gpu/mac/vt_config_util_unittest.cc
+++ b/media/gpu/mac/vt_config_util_unittest.cc
@@ -56,9 +56,8 @@
 
 base::ScopedCFTypeRef<CVImageBufferRef> CreateCVImageBuffer(
     media::VideoColorSpace cs) {
-  base::ScopedCFTypeRef<CFDictionaryRef> fmt(
-      CreateFormatExtensions(kCMVideoCodecType_H264, media::H264PROFILE_MAIN,
-                             cs, media::HDRMetadata()));
+  base::ScopedCFTypeRef<CFDictionaryRef> fmt(CreateFormatExtensions(
+      kCMVideoCodecType_H264, media::H264PROFILE_MAIN, cs, gl::HDRMetadata()));
 
   base::ScopedCFTypeRef<CVImageBufferRef> image_buffer;
   OSStatus err =
@@ -137,7 +136,7 @@
                       VideoColorSpace::TransferID::SMPTEST2084,
                       VideoColorSpace::MatrixID::BT2020_NCL,
                       gfx::ColorSpace::RangeID::FULL),
-      HDRMetadata()));
+      gl::HDRMetadata()));
   EXPECT_EQ("avc1", GetStrValue(fmt, kCMFormatDescriptionExtension_FormatName));
   EXPECT_EQ(24, GetIntValue(fmt, kCMFormatDescriptionExtension_Depth));
 
@@ -161,7 +160,7 @@
                       VideoColorSpace::TransferID::ARIB_STD_B67,
                       VideoColorSpace::MatrixID::BT2020_NCL,
                       gfx::ColorSpace::RangeID::FULL),
-      HDRMetadata()));
+      gl::HDRMetadata()));
   EXPECT_EQ("avc1", GetStrValue(fmt, kCMFormatDescriptionExtension_FormatName));
   EXPECT_EQ(24, GetIntValue(fmt, kCMFormatDescriptionExtension_Depth));
 
@@ -180,7 +179,7 @@
 
 TEST(VTConfigUtil, CreateFormatExtensions_HDRMetadata) {
   // Values from real YouTube HDR content.
-  HDRMetadata hdr_meta;
+  gl::HDRMetadata hdr_meta;
   hdr_meta.max_content_light_level = 1000;
   hdr_meta.max_frame_average_light_level = 600;
   auto& mastering = hdr_meta.mastering_metadata;
diff --git a/media/gpu/mac/vt_video_decode_accelerator_mac.cc b/media/gpu/mac/vt_video_decode_accelerator_mac.cc
index 4484fd0..0959744 100644
--- a/media/gpu/mac/vt_video_decode_accelerator_mac.cc
+++ b/media/gpu/mac/vt_video_decode_accelerator_mac.cc
@@ -194,7 +194,7 @@
 base::ScopedCFTypeRef<CMFormatDescriptionRef> CreateVideoFormatVP9(
     media::VideoColorSpace color_space,
     media::VideoCodecProfile profile,
-    base::Optional<media::HDRMetadata> hdr_metadata,
+    base::Optional<gl::HDRMetadata> hdr_metadata,
     const gfx::Size& coded_size) {
   base::ScopedCFTypeRef<CFMutableDictionaryRef> format_config(
       CreateFormatExtensions(kCMVideoCodecType_VP9, profile, color_space,
diff --git a/media/gpu/windows/d3d11_copying_texture_wrapper.cc b/media/gpu/windows/d3d11_copying_texture_wrapper.cc
index b2bf528..93119a0 100644
--- a/media/gpu/windows/d3d11_copying_texture_wrapper.cc
+++ b/media/gpu/windows/d3d11_copying_texture_wrapper.cc
@@ -101,7 +101,7 @@
 }
 
 void CopyingTexture2DWrapper::SetStreamHDRMetadata(
-    const HDRMetadata& stream_metadata) {
+    const gl::HDRMetadata& stream_metadata) {
   auto dxgi_stream_metadata =
       gl::HDRMetadataHelperWin::HDRMetadataToDXGI(stream_metadata);
   video_processor_->SetStreamHDRMetadata(dxgi_stream_metadata);
diff --git a/media/gpu/windows/d3d11_copying_texture_wrapper.h b/media/gpu/windows/d3d11_copying_texture_wrapper.h
index e62d1c7..39c200a 100644
--- a/media/gpu/windows/d3d11_copying_texture_wrapper.h
+++ b/media/gpu/windows/d3d11_copying_texture_wrapper.h
@@ -40,7 +40,7 @@
   Status Init(scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
               GetCommandBufferHelperCB get_helper_cb) override;
 
-  void SetStreamHDRMetadata(const HDRMetadata& stream_metadata) override;
+  void SetStreamHDRMetadata(const gl::HDRMetadata& stream_metadata) override;
   void SetDisplayHDRMetadata(
       const DXGI_HDR_METADATA_HDR10& dxgi_display_metadata) override;
 
diff --git a/media/gpu/windows/d3d11_copying_texture_wrapper_unittest.cc b/media/gpu/windows/d3d11_copying_texture_wrapper_unittest.cc
index f1127f2..85491857 100644
--- a/media/gpu/windows/d3d11_copying_texture_wrapper_unittest.cc
+++ b/media/gpu/windows/d3d11_copying_texture_wrapper_unittest.cc
@@ -105,7 +105,8 @@
 
   MOCK_METHOD0(MockInit, Status());
   MOCK_METHOD0(MockProcessTexture, Status());
-  MOCK_METHOD1(SetStreamHDRMetadata, void(const HDRMetadata& stream_metadata));
+  MOCK_METHOD1(SetStreamHDRMetadata,
+               void(const gl::HDRMetadata& stream_metadata));
   MOCK_METHOD1(SetDisplayHDRMetadata,
                void(const DXGI_HDR_METADATA_HDR10& dxgi_display_metadata));
 
@@ -252,15 +253,15 @@
 }
 
 TEST_P(D3D11CopyingTexture2DWrapperTest, HDRMetadataIsSentToVideoProcessor) {
-  HDRMetadata metadata;
+  gl::HDRMetadata metadata;
   metadata.mastering_metadata.primary_r =
-      MasteringMetadata::Chromaticity(0.1, 0.2);
+      gl::MasteringMetadata::Chromaticity(0.1, 0.2);
   metadata.mastering_metadata.primary_g =
-      MasteringMetadata::Chromaticity(0.3, 0.4);
+      gl::MasteringMetadata::Chromaticity(0.3, 0.4);
   metadata.mastering_metadata.primary_b =
-      MasteringMetadata::Chromaticity(0.5, 0.6);
+      gl::MasteringMetadata::Chromaticity(0.5, 0.6);
   metadata.mastering_metadata.white_point =
-      MasteringMetadata::Chromaticity(0.7, 0.8);
+      gl::MasteringMetadata::Chromaticity(0.7, 0.8);
   metadata.mastering_metadata.luminance_max = 0.9;
   metadata.mastering_metadata.luminance_min = 0.05;
   metadata.max_content_light_level = 1000;
diff --git a/media/gpu/windows/d3d11_texture_wrapper.cc b/media/gpu/windows/d3d11_texture_wrapper.cc
index 80612a2f..4df1266 100644
--- a/media/gpu/windows/d3d11_texture_wrapper.cc
+++ b/media/gpu/windows/d3d11_texture_wrapper.cc
@@ -145,7 +145,7 @@
 }
 
 void DefaultTexture2DWrapper::SetStreamHDRMetadata(
-    const HDRMetadata& stream_metadata) {}
+    const gl::HDRMetadata& stream_metadata) {}
 
 void DefaultTexture2DWrapper::SetDisplayHDRMetadata(
     const DXGI_HDR_METADATA_HDR10& dxgi_display_metadata) {}
diff --git a/media/gpu/windows/d3d11_texture_wrapper.h b/media/gpu/windows/d3d11_texture_wrapper.h
index 3878f4fd9..3bbc3ff1 100644
--- a/media/gpu/windows/d3d11_texture_wrapper.h
+++ b/media/gpu/windows/d3d11_texture_wrapper.h
@@ -15,7 +15,6 @@
 #include "base/threading/sequence_bound.h"
 #include "gpu/command_buffer/service/mailbox_manager.h"
 #include "gpu/command_buffer/service/texture_manager.h"
-#include "media/base/hdr_metadata.h"
 #include "media/base/status.h"
 #include "media/base/video_frame.h"
 #include "media/gpu/command_buffer_helper.h"
@@ -26,6 +25,7 @@
 #include "ui/gl/gl_context.h"
 #include "ui/gl/gl_image_dxgi.h"
 #include "ui/gl/gl_surface_egl.h"
+#include "ui/gl/hdr_metadata.h"
 #include "ui/gl/scoped_binders.h"
 
 namespace media {
@@ -57,7 +57,7 @@
                                 MailboxHolderArray* mailbox_dest_out,
                                 gfx::ColorSpace* output_color_space) = 0;
 
-  virtual void SetStreamHDRMetadata(const HDRMetadata& stream_metadata) = 0;
+  virtual void SetStreamHDRMetadata(const gl::HDRMetadata& stream_metadata) = 0;
   virtual void SetDisplayHDRMetadata(
       const DXGI_HDR_METADATA_HDR10& dxgi_display_metadata) = 0;
 };
@@ -85,7 +85,7 @@
                         MailboxHolderArray* mailbox_dest,
                         gfx::ColorSpace* output_color_space) override;
 
-  void SetStreamHDRMetadata(const HDRMetadata& stream_metadata) override;
+  void SetStreamHDRMetadata(const gl::HDRMetadata& stream_metadata) override;
   void SetDisplayHDRMetadata(
       const DXGI_HDR_METADATA_HDR10& dxgi_display_metadata) override;
 
diff --git a/media/gpu/windows/d3d11_video_decoder.cc b/media/gpu/windows/d3d11_video_decoder.cc
index 492ec74..7f8de14d 100644
--- a/media/gpu/windows/d3d11_video_decoder.cc
+++ b/media/gpu/windows/d3d11_video_decoder.cc
@@ -688,7 +688,7 @@
   DCHECK(texture_selector_);
   gfx::Size size = accelerated_video_decoder_->GetPicSize();
 
-  HDRMetadata stream_metadata;
+  gl::HDRMetadata stream_metadata;
   if (config_.hdr_metadata())
     stream_metadata = *config_.hdr_metadata();
   // else leave |stream_metadata| default-initialized.  We might use it anyway.
diff --git a/media/gpu/windows/d3d11_video_processor_proxy.h b/media/gpu/windows/d3d11_video_processor_proxy.h
index 6543fff..5e15611 100644
--- a/media/gpu/windows/d3d11_video_processor_proxy.h
+++ b/media/gpu/windows/d3d11_video_processor_proxy.h
@@ -9,11 +9,11 @@
 #include <wrl/client.h>
 #include <cstdint>
 
-#include "media/base/hdr_metadata.h"
 #include "media/base/status.h"
 #include "media/gpu/media_gpu_export.h"
 #include "media/gpu/windows/d3d11_com_defs.h"
 #include "ui/gfx/color_space.h"
+#include "ui/gl/hdr_metadata.h"
 
 namespace media {
 
diff --git a/media/gpu/windows/dxva_video_decode_accelerator_win.cc b/media/gpu/windows/dxva_video_decode_accelerator_win.cc
index 3228a47..f2cafa5 100644
--- a/media/gpu/windows/dxva_video_decode_accelerator_win.cc
+++ b/media/gpu/windows/dxva_video_decode_accelerator_win.cc
@@ -2953,7 +2953,7 @@
   // stream metadata.  For the Radeon 5700, at least, this seems to do
   // something sane.  Not setting the metadata crashes intermittently.
   if (config_.hdr_metadata || use_empty_video_hdr_metadata_) {
-    HDRMetadata stream_metadata;
+    gl::HDRMetadata stream_metadata;
     if (config_.hdr_metadata)
       stream_metadata = *config_.hdr_metadata;
 
diff --git a/media/mojo/mojom/video_decoder_config_mojom_traits.cc b/media/mojo/mojom/video_decoder_config_mojom_traits.cc
index 6aadb71..393ff92b 100644
--- a/media/mojo/mojom/video_decoder_config_mojom_traits.cc
+++ b/media/mojo/mojom/video_decoder_config_mojom_traits.cc
@@ -47,7 +47,7 @@
   if (!input.ReadColorSpaceInfo(&color_space))
     return false;
 
-  base::Optional<media::HDRMetadata> hdr_metadata;
+  base::Optional<gl::HDRMetadata> hdr_metadata;
   if (!input.ReadHdrMetadata(&hdr_metadata))
     return false;
 
diff --git a/media/mojo/mojom/video_decoder_config_mojom_traits.h b/media/mojo/mojom/video_decoder_config_mojom_traits.h
index 442a4a5b..664aa1d 100644
--- a/media/mojo/mojom/video_decoder_config_mojom_traits.h
+++ b/media/mojo/mojom/video_decoder_config_mojom_traits.h
@@ -64,7 +64,7 @@
     return input.video_transformation();
   }
 
-  static const base::Optional<media::HDRMetadata>& hdr_metadata(
+  static const base::Optional<gl::HDRMetadata>& hdr_metadata(
       const media::VideoDecoderConfig& input) {
     return input.hdr_metadata();
   }
diff --git a/media/mojo/mojom/video_decoder_config_mojom_traits_unittest.cc b/media/mojo/mojom/video_decoder_config_mojom_traits_unittest.cc
index 2be014f..5c9f0fe 100644
--- a/media/mojo/mojom/video_decoder_config_mojom_traits_unittest.cc
+++ b/media/mojo/mojom/video_decoder_config_mojom_traits_unittest.cc
@@ -88,7 +88,7 @@
       kCodecVP8, VP8PROFILE_ANY, VideoDecoderConfig::AlphaMode::kIsOpaque,
       VideoColorSpace(), kNoTransformation, kCodedSize, kVisibleRect,
       kNaturalSize, EmptyExtraData(), EncryptionScheme::kUnencrypted);
-  HDRMetadata hdr_metadata;
+  gl::HDRMetadata hdr_metadata;
   hdr_metadata.max_frame_average_light_level = 123;
   hdr_metadata.max_content_light_level = 456;
   hdr_metadata.mastering_metadata.primary_r.set_x(0.1f);
diff --git a/media/mojo/mojom/video_frame_mojom_traits.cc b/media/mojo/mojom/video_frame_mojom_traits.cc
index cbf3031..1f50120d 100644
--- a/media/mojo/mojom/video_frame_mojom_traits.cc
+++ b/media/mojo/mojom/video_frame_mojom_traits.cc
@@ -286,7 +286,7 @@
     return false;
   frame->set_color_space(color_space);
 
-  base::Optional<media::HDRMetadata> hdr_metadata;
+  base::Optional<gl::HDRMetadata> hdr_metadata;
   if (!input.ReadHdrMetadata(&hdr_metadata))
     return false;
   frame->set_hdr_metadata(std::move(hdr_metadata));
diff --git a/media/mojo/mojom/video_frame_mojom_traits.h b/media/mojo/mojom/video_frame_mojom_traits.h
index 461bd9c..deb78ed 100644
--- a/media/mojo/mojom/video_frame_mojom_traits.h
+++ b/media/mojo/mojom/video_frame_mojom_traits.h
@@ -62,7 +62,7 @@
     return input->ColorSpace();
   }
 
-  static const base::Optional<media::HDRMetadata>& hdr_metadata(
+  static const base::Optional<gl::HDRMetadata>& hdr_metadata(
       const scoped_refptr<media::VideoFrame>& input) {
     return input->hdr_metadata();
   }
diff --git a/media/video/video_decode_accelerator.h b/media/video/video_decode_accelerator.h
index e7222f14..c4ad432 100644
--- a/media/video/video_decode_accelerator.h
+++ b/media/video/video_decode_accelerator.h
@@ -187,7 +187,7 @@
     gfx::ColorSpace target_color_space;
 
     // HDR metadata specified by the container.
-    base::Optional<HDRMetadata> hdr_metadata;
+    base::Optional<gl::HDRMetadata> hdr_metadata;
   };
 
   // Interface for collaborating with picture interface to provide memory for
diff --git a/net/quic/platform/impl/quic_udp_socket_platform_impl.h b/net/quic/platform/impl/quic_udp_socket_platform_impl.h
index 516074e0..12e589a9 100644
--- a/net/quic/platform/impl/quic_udp_socket_platform_impl.h
+++ b/net/quic/platform/impl/quic_udp_socket_platform_impl.h
@@ -21,6 +21,8 @@
   return false;
 }
 
+inline void SetGoogleSocketOptionsImpl(int fd) {}
+
 }  // namespace quic
 
 #endif  // NET_QUIC_PLATFORM_IMPL_QUIC_UDP_SOCKET_PLATFORM_IMPL_H_
diff --git a/net/quic/quic_event_logger.cc b/net/quic/quic_event_logger.cc
index 0efba24..1637750 100644
--- a/net/quic/quic_event_logger.cc
+++ b/net/quic/quic_event_logger.cc
@@ -298,7 +298,7 @@
     const quic::QuicStopSendingFrame* frame) {
   base::Value dict(base::Value::Type::DICTIONARY);
   dict.SetIntKey("stream_id", frame->stream_id);
-  dict.SetIntKey("application_error_code", frame->application_error_code);
+  dict.SetIntKey("error_code", frame->error_code);
   return dict;
 }
 
diff --git a/net/quic/quic_flags_list.h b/net/quic/quic_flags_list.h
index 80c6bb2..d74e2861 100644
--- a/net/quic/quic_flags_list.h
+++ b/net/quic/quic_flags_list.h
@@ -437,3 +437,28 @@
     bool,
     FLAGS_quic_reloadable_flag_quic_close_connection_on_serialization_failure,
     true)
+
+// If true, send PATH_RESPONSE upon receiving PATH_CHALLENGE regardless
+// of perspective.
+QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_send_path_response, false)
+
+// If true, when switching from BBR to BBR2, use BBR's CWND as the initial CWND.
+QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_copy_bbr_cwnd_to_bbr2, false)
+
+// If true, send the lowest stream ID that can be retried by the client in a
+// GOAWAY frame.
+QUIC_FLAG(bool,
+          FLAGS_quic_reloadable_flag_quic_fix_http3_goaway_stream_id,
+          false)
+
+// If true, close connection if writer is still blocked when OnCanWrite is
+// called.
+QUIC_FLAG(
+    bool,
+    FLAGS_quic_reloadable_flag_quic_close_connection_in_on_can_write_with_blocked_writer,
+    false)
+
+// If true, include stream information in idle timeout connection close detail.
+QUIC_FLAG(bool,
+          FLAGS_quic_reloadable_flag_quic_add_stream_info_to_idle_close_detail,
+          true)
diff --git a/net/quic/quic_test_packet_maker.cc b/net/quic/quic_test_packet_maker.cc
index 323218602..62c645c6 100644
--- a/net/quic/quic_test_packet_maker.cc
+++ b/net/quic/quic_test_packet_maker.cc
@@ -1205,7 +1205,7 @@
 
 void QuicTestPacketMaker::AddQuicStopSendingFrame(
     quic::QuicStreamId stream_id,
-    quic::QuicApplicationErrorCode error_code) {
+    quic::QuicRstStreamErrorCode error_code) {
   auto* stop_sending_frame =
       new quic::QuicStopSendingFrame(1, stream_id, error_code);
   frames_.push_back(quic::QuicFrame(stop_sending_frame));
diff --git a/net/quic/quic_test_packet_maker.h b/net/quic/quic_test_packet_maker.h
index 3ed6caa..f39c7588 100644
--- a/net/quic/quic_test_packet_maker.h
+++ b/net/quic/quic_test_packet_maker.h
@@ -407,7 +407,7 @@
   void AddQuicPathResponseFrame();
   void AddQuicPathChallengeFrame();
   void AddQuicStopSendingFrame(quic::QuicStreamId stream_id,
-                               quic::QuicApplicationErrorCode error_code);
+                               quic::QuicRstStreamErrorCode error_code);
   void AddQuicCryptoFrame(quic::EncryptionLevel level,
                           quic::QuicStreamOffset offset,
                           quic::QuicPacketLength data_length);
diff --git a/net/spdy/spdy_http_utils.cc b/net/spdy/spdy_http_utils.cc
index 5ad2d4a..4a21923 100644
--- a/net/spdy/spdy_http_utils.cc
+++ b/net/spdy/spdy_http_utils.cc
@@ -52,6 +52,13 @@
   raw_headers.append(status);
   raw_headers.push_back('\0');
   for (it = headers.begin(); it != headers.end(); ++it) {
+    std::string name = it->first.as_string();
+    DCHECK_GT(name.size(), 0u);
+    if (name[0] == ':') {
+      // https://tools.ietf.org/html/rfc7540#section-8.1.2.4
+      // Skip pseudo headers.
+      continue;
+    }
     // For each value, if the server sends a NUL-separated
     // list of values, we separate that back out into
     // individual headers for each value in the list.
@@ -64,17 +71,15 @@
     size_t start = 0;
     size_t end = 0;
     do {
+      raw_headers.append(name);
+      raw_headers.push_back(':');
+
       end = value.find('\0', start);
       std::string tval;
       if (end != value.npos)
         tval = value.substr(start, (end - start));
       else
         tval = value.substr(start);
-      if (it->first[0] == ':')
-        raw_headers.append(it->first.as_string().substr(1));
-      else
-        raw_headers.append(it->first.as_string());
-      raw_headers.push_back(':');
       raw_headers.append(tval);
       raw_headers.push_back('\0');
       start = end + 1;
diff --git a/net/spdy/spdy_network_transaction_unittest.cc b/net/spdy/spdy_network_transaction_unittest.cc
index d0dd6d8..c8896bd 100644
--- a/net/spdy/spdy_network_transaction_unittest.cc
+++ b/net/spdy/spdy_network_transaction_unittest.cc
@@ -4323,12 +4323,12 @@
     base::StringPiece expected_headers[8];
   } test_cases[] = {
       // No extra headers.
-      {0, {}, 2, {"status", "200", "hello", "bye"}},
+      {0, {}, 1, {"hello", "bye"}},
       // Comma-separated header value.
       {1,
        {"cookie", "val1, val2"},
-       3,
-       {"status", "200", "hello", "bye", "cookie", "val1, val2"}},
+       2,
+       {"hello", "bye", "cookie", "val1, val2"}},
       // Multiple headers are preserved: they are joined with \0 separator in
       // spdy::SpdyHeaderBlock.AppendValueOrAddHeader(), then split up in
       // HpackEncoder, then joined with \0 separator when
@@ -4337,14 +4337,14 @@
       // HttpResponseHeaders.
       {2,
        {"content-encoding", "val1", "content-encoding", "val2"},
-       4,
-       {"status", "200", "hello", "bye", "content-encoding", "val1",
-        "content-encoding", "val2"}},
+       3,
+       {"hello", "bye", "content-encoding", "val1", "content-encoding",
+        "val2"}},
       // Cookie header is not split up by HttpResponseHeaders.
       {2,
        {"cookie", "val1", "cookie", "val2"},
-       3,
-       {"status", "200", "hello", "bye", "cookie", "val1; val2"}}};
+       2,
+       {"hello", "bye", "cookie", "val1; val2"}}};
 
   for (size_t i = 0; i < base::size(test_cases); ++i) {
     SpdyTestUtil spdy_test_util;
@@ -4445,14 +4445,11 @@
     };
 
     // Construct the reply.
+    const char** expected_res_extra_headers = test_cases[i].extra_headers[1];
+    int expected_res_num_headers = test_cases[i].num_headers[1];
     spdy::SpdyHeaderBlock reply_headers;
-    AppendToHeaderBlock(test_cases[i].extra_headers[1],
-                        test_cases[i].num_headers[1],
+    AppendToHeaderBlock(expected_res_extra_headers, expected_res_num_headers,
                         &reply_headers);
-    // Construct the expected header reply string before moving |reply_headers|.
-    std::string expected_reply =
-        spdy_test_util.ConstructSpdyReplyString(reply_headers);
-
     spdy::SpdySerializedFrame frame_reply(
         spdy_test_util.ConstructSpdyReply(1, std::move(reply_headers)));
 
@@ -4505,7 +4502,17 @@
       lines.append("\n");
     }
 
-    EXPECT_EQ(expected_reply, lines) << i;
+    // Remove ":status" and ":path" field from HTTP response.
+    // See SpdyHeadersToHttpResponse().
+    ASSERT_EQ(expected_res_extra_headers[0], spdy::kHttp2StatusHeader);
+    ASSERT_EQ(expected_res_extra_headers[2], spdy::kHttp2PathHeader);
+    ASSERT_GT(expected_res_num_headers, 1);
+    spdy::SpdyHeaderBlock http_reply_headers;
+    AppendToHeaderBlock(&expected_res_extra_headers[4],
+                        expected_res_num_headers - 2, &http_reply_headers);
+    std::string expected_http_reply =
+        spdy_test_util.ConstructSpdyReplyString(http_reply_headers);
+    EXPECT_EQ(expected_http_reply, lines) << i;
   }
 }
 
diff --git a/pdf/out_of_process_instance.cc b/pdf/out_of_process_instance.cc
index bd0a5e0..45d89c3 100644
--- a/pdf/out_of_process_instance.cc
+++ b/pdf/out_of_process_instance.cc
@@ -2084,20 +2084,8 @@
     SendAccessibilityViewportInfo();
 }
 
-void OutOfProcessInstance::LoadUrl(const std::string& url,
-                                   bool is_print_preview) {
-  UrlRequest request;
-  request.url = url;
-  request.method = "GET";
-  request.ignore_redirects = true;
-
-  std::unique_ptr<UrlLoader> loader = CreateUrlLoaderInternal();
-  UrlLoader* raw_loader = loader.get();
-  raw_loader->Open(
-      request,
-      base::BindOnce(is_print_preview ? &OutOfProcessInstance::DidOpenPreview
-                                      : &OutOfProcessInstance::DidOpen,
-                     weak_factory_.GetWeakPtr(), std::move(loader)));
+base::WeakPtr<PdfViewPluginBase> OutOfProcessInstance::GetWeakPtr() {
+  return weak_factory_.GetWeakPtr();
 }
 
 std::unique_ptr<UrlLoader> OutOfProcessInstance::CreateUrlLoaderInternal() {
diff --git a/pdf/out_of_process_instance.h b/pdf/out_of_process_instance.h
index 48db493f..194cae0 100644
--- a/pdf/out_of_process_instance.h
+++ b/pdf/out_of_process_instance.h
@@ -57,26 +57,26 @@
   OutOfProcessInstance& operator=(const OutOfProcessInstance&) = delete;
   ~OutOfProcessInstance() override;
 
-  // pp::Instance implementation.
+  // pp::Instance:
   bool Init(uint32_t argc, const char* argn[], const char* argv[]) override;
   void HandleMessage(const pp::Var& message) override;
   bool HandleInputEvent(const pp::InputEvent& event) override;
   void DidChangeView(const pp::View& view) override;
   void DidChangeFocus(bool has_focus) override;
 
-  // pp::Find_Private implementation.
+  // pp::Find_Private:
   bool StartFind(const std::string& text, bool case_sensitive) override;
   void SelectFindResult(bool forward) override;
   void StopFind() override;
 
-  // pp::PaintManager::Client implementation.
+  // pp::PaintManager::Client:
   std::unique_ptr<Graphics> CreatePaintGraphics(const gfx::Size& size) override;
   bool BindPaintGraphics(Graphics& graphics) override;
   void OnPaint(const std::vector<gfx::Rect>& paint_rects,
                std::vector<PaintReadyRect>* ready,
                std::vector<gfx::Rect>* pending) override;
 
-  // pp::Printing_Dev implementation.
+  // pp::Printing_Dev:
   uint32_t QuerySupportedPrintOutputFormats() override;
   int32_t PrintBegin(const PP_PrintSettings_Dev& print_settings) override;
   pp::Resource PrintPages(const PP_PrintPageNumberRange_Dev* page_ranges,
@@ -84,7 +84,7 @@
   void PrintEnd() override;
   bool IsPrintScalingDisabled() override;
 
-  // pp::Private implementation.
+  // pp::Private:
   pp::Var GetLinkAtPosition(const pp::Point& point);
   void GetPrintPresetOptionsFromDocument(PP_PdfPrintPresetOptions_Dev* options);
   void EnableAccessibility();
@@ -105,10 +105,8 @@
                         const PP_PdfPrintSettings_Dev* pdf_print_settings);
 
   void FlushCallback(int32_t result);
-  void DidOpen(std::unique_ptr<UrlLoader> loader, int32_t result);
-  void DidOpenPreview(std::unique_ptr<UrlLoader> loader, int32_t result);
 
-  // PdfViewPluginBase implementation.
+  // PdfViewPluginBase:
   void ProposeDocumentLayout(const DocumentLayout& layout) override;
   void Invalidate(const gfx::Rect& rect) override;
   void DidScroll(const gfx::Vector2d& offset) override;
@@ -163,7 +161,7 @@
   float GetToolbarHeightInScreenCoords() override;
   void DocumentFocusChanged(bool document_has_focus) override;
 
-  // PreviewModeClient::Client implementation.
+  // PreviewModeClient::Client:
   void PreviewDocumentLoadComplete() override;
   void PreviewDocumentLoadFailed() override;
 
@@ -175,6 +173,14 @@
   // for testing.
   static std::string GetFileNameFromUrl(const std::string& url);
 
+ protected:
+  // PdfViewPluginBase:
+  base::WeakPtr<PdfViewPluginBase> GetWeakPtr() override;
+  std::unique_ptr<UrlLoader> CreateUrlLoaderInternal() override;
+  void DidOpen(std::unique_ptr<UrlLoader> loader, int32_t result) override;
+  void DidOpenPreview(std::unique_ptr<UrlLoader> loader,
+                      int32_t result) override;
+
  private:
   // Message handlers.
   void HandleBackgroundColorChangedMessage(const pp::VarDictionary& dict);
@@ -219,12 +225,6 @@
   // Draws a rectangle with the specified dimensions and color in our buffer.
   void FillRect(const pp::Rect& rect, uint32_t color);
 
-  void LoadUrl(const std::string& url, bool is_print_preview);
-
-  // Creates a URL loader and allows it to access all urls, i.e. not just the
-  // frame's origin.
-  std::unique_ptr<UrlLoader> CreateUrlLoaderInternal();
-
   bool CanSaveEdits() const;
   void SaveToFile(const std::string& token);
   void SaveToBuffer(const std::string& token);
diff --git a/pdf/pdf_view_plugin_base.cc b/pdf/pdf_view_plugin_base.cc
index 863c0745..1682c64 100644
--- a/pdf/pdf_view_plugin_base.cc
+++ b/pdf/pdf_view_plugin_base.cc
@@ -5,8 +5,14 @@
 #include "pdf/pdf_view_plugin_base.h"
 
 #include <memory>
+#include <string>
+#include <utility>
 
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
 #include "pdf/pdfium/pdfium_engine.h"
+#include "pdf/ppapi_migration/url_loader.h"
 
 namespace chrome_pdf {
 
@@ -22,4 +28,19 @@
   engine_.reset();
 }
 
+void PdfViewPluginBase::LoadUrl(const std::string& url, bool is_print_preview) {
+  UrlRequest request;
+  request.url = url;
+  request.method = "GET";
+  request.ignore_redirects = true;
+
+  std::unique_ptr<UrlLoader> loader = CreateUrlLoaderInternal();
+  UrlLoader* raw_loader = loader.get();
+  raw_loader->Open(
+      request,
+      base::BindOnce(is_print_preview ? &PdfViewPluginBase::DidOpenPreview
+                                      : &PdfViewPluginBase::DidOpen,
+                     GetWeakPtr(), std::move(loader)));
+}
+
 }  // namespace chrome_pdf
diff --git a/pdf/pdf_view_plugin_base.h b/pdf/pdf_view_plugin_base.h
index c1f2f64..ebe5889 100644
--- a/pdf/pdf_view_plugin_base.h
+++ b/pdf/pdf_view_plugin_base.h
@@ -7,11 +7,17 @@
 
 #include "pdf/pdf_engine.h"
 
+#include <stdint.h>
+
 #include <memory>
+#include <string>
+
+#include "base/memory/weak_ptr.h"
 
 namespace chrome_pdf {
 
 class PDFiumEngine;
+class UrlLoader;
 
 // Common base to share code between the two plugin implementations,
 // `OutOfProcessInstance` (Pepper) and `PdfViewWebPlugin` (Blink).
@@ -36,6 +42,24 @@
 
   PDFiumEngine* engine() { return engine_.get(); }
 
+  // Starts loading `url`. If `is_print_preview` is `true`, load for print
+  // preview instead of normal PDF viewing.
+  void LoadUrl(const std::string& url, bool is_print_preview);
+
+  // Gets a weak pointer with a lifetime matching the derived class.
+  virtual base::WeakPtr<PdfViewPluginBase> GetWeakPtr() = 0;
+
+  // Creates a URL loader and allows it to access all urls, i.e. not just the
+  // frame's origin.
+  virtual std::unique_ptr<UrlLoader> CreateUrlLoaderInternal() = 0;
+
+  // Handles `LoadUrl()` result.
+  virtual void DidOpen(std::unique_ptr<UrlLoader> loader, int32_t result) = 0;
+
+  // Handles `LoadUrl()` result for print preview.
+  virtual void DidOpenPreview(std::unique_ptr<UrlLoader> loader,
+                              int32_t result) = 0;
+
  private:
   std::unique_ptr<PDFiumEngine> engine_;
 };
diff --git a/pdf/pdf_view_web_plugin.cc b/pdf/pdf_view_web_plugin.cc
index bbefe9b..5cffa0e0 100644
--- a/pdf/pdf_view_web_plugin.cc
+++ b/pdf/pdf_view_web_plugin.cc
@@ -6,10 +6,13 @@
 
 #include <stddef.h>
 
+#include <memory>
 #include <string>
 #include <vector>
 
 #include "base/check_op.h"
+#include "base/memory/ptr_util.h"
+#include "base/notreached.h"
 #include "base/thread_annotations.h"
 #include "base/threading/thread_checker.h"
 #include "cc/paint/paint_canvas.h"
@@ -23,6 +26,10 @@
 #include "third_party/blink/public/platform/web_rect.h"
 #include "third_party/blink/public/platform/web_url_error.h"
 #include "third_party/blink/public/platform/web_url_response.h"
+#include "third_party/blink/public/web/web_associated_url_loader.h"
+#include "third_party/blink/public/web/web_associated_url_loader_options.h"
+#include "third_party/blink/public/web/web_document.h"
+#include "third_party/blink/public/web/web_local_frame.h"
 #include "third_party/blink/public/web/web_plugin_container.h"
 #include "third_party/blink/public/web/web_plugin_params.h"
 #include "ui/base/cursor/cursor.h"
@@ -255,4 +262,39 @@
 
 void PdfViewWebPlugin::DocumentFocusChanged(bool document_has_focus) {}
 
+std::unique_ptr<blink::WebAssociatedURLLoader>
+PdfViewWebPlugin::CreateAssociatedURLLoader(
+    const blink::WebAssociatedURLLoaderOptions& options) {
+  if (!container_)
+    return nullptr;
+
+  blink::WebLocalFrame* frame = container_->GetDocument().GetFrame();
+  if (!frame)
+    return nullptr;
+
+  // TODO(crbug.com/1127146): blink::WebLocalFrame::CreateAssociatedURLLoader()
+  // really should return a std::unique_ptr instead.
+  return base::WrapUnique(frame->CreateAssociatedURLLoader(options));
+}
+
+base::WeakPtr<PdfViewPluginBase> PdfViewWebPlugin::GetWeakPtr() {
+  return weak_factory_.GetWeakPtr();
+}
+
+std::unique_ptr<UrlLoader> PdfViewWebPlugin::CreateUrlLoaderInternal() {
+  auto loader = std::make_unique<BlinkUrlLoader>(weak_factory_.GetWeakPtr());
+  loader->GrantUniversalAccess();
+  return loader;
+}
+
+void PdfViewWebPlugin::DidOpen(std::unique_ptr<UrlLoader> loader,
+                               int32_t result) {
+  NOTIMPLEMENTED();
+}
+
+void PdfViewWebPlugin::DidOpenPreview(std::unique_ptr<UrlLoader> loader,
+                                      int32_t result) {
+  NOTIMPLEMENTED();
+}
+
 }  // namespace chrome_pdf
diff --git a/pdf/pdf_view_web_plugin.h b/pdf/pdf_view_web_plugin.h
index a6c10d2..dec9811 100644
--- a/pdf/pdf_view_web_plugin.h
+++ b/pdf/pdf_view_web_plugin.h
@@ -5,7 +5,9 @@
 #ifndef PDF_PDF_VIEW_WEB_PLUGIN_H_
 #define PDF_PDF_VIEW_WEB_PLUGIN_H_
 
+#include "base/memory/weak_ptr.h"
 #include "pdf/pdf_view_plugin_base.h"
+#include "pdf/ppapi_migration/url_loader.h"
 #include "third_party/blink/public/web/web_plugin.h"
 
 namespace blink {
@@ -17,7 +19,8 @@
 
 // Skeleton for a `blink::WebPlugin` to replace `OutOfProcessInstance`.
 class PdfViewWebPlugin final : public PdfViewPluginBase,
-                               public blink::WebPlugin {
+                               public blink::WebPlugin,
+                               public BlinkUrlLoader::Client {
  public:
   explicit PdfViewWebPlugin(const blink::WebPluginParams& params);
   PdfViewWebPlugin(const PdfViewWebPlugin& other) = delete;
@@ -98,11 +101,25 @@
   float GetToolbarHeightInScreenCoords() override;
   void DocumentFocusChanged(bool document_has_focus) override;
 
+  // BlinkUrlLoader::Client:
+  std::unique_ptr<blink::WebAssociatedURLLoader> CreateAssociatedURLLoader(
+      const blink::WebAssociatedURLLoaderOptions& options) override;
+
+ protected:
+  // PdfViewPluginBase:
+  base::WeakPtr<PdfViewPluginBase> GetWeakPtr() override;
+  std::unique_ptr<UrlLoader> CreateUrlLoaderInternal() override;
+  void DidOpen(std::unique_ptr<UrlLoader> loader, int32_t result) override;
+  void DidOpenPreview(std::unique_ptr<UrlLoader> loader,
+                      int32_t result) override;
+
  private:
   // Call `Destroy()` instead.
   ~PdfViewWebPlugin() override;
 
   blink::WebPluginContainer* container_ = nullptr;
+
+  base::WeakPtrFactory<PdfViewWebPlugin> weak_factory_{this};
 };
 
 }  // namespace chrome_pdf
diff --git a/printing/backend/print_backend.h b/printing/backend/print_backend.h
index f9ddcad3..6e595d7 100644
--- a/printing/backend/print_backend.h
+++ b/printing/backend/print_backend.h
@@ -62,7 +62,7 @@
   AdvancedCapability(const AdvancedCapability& other);
   ~AdvancedCapability();
 
-  enum class Type : uint8_t { kNone = 0, kBoolean, kFloat, kInteger, kString };
+  enum class Type : uint8_t { kBoolean, kFloat, kInteger, kString };
 
   // IPP identifier of the attribute.
   std::string name;
diff --git a/services/tracing/BUILD.gn b/services/tracing/BUILD.gn
index e0d3e15..f907e42 100644
--- a/services/tracing/BUILD.gn
+++ b/services/tracing/BUILD.gn
@@ -92,6 +92,11 @@
     "public/cpp/stack_sampling/tracing_sampler_profiler_unittest.cc",
   ]
 
+  if (is_android && current_cpu == "arm64") {
+    sources +=
+        [ "public/cpp/stack_sampling/stack_unwinder_arm64_android_unittest.cc" ]
+  }
+
   if (!is_android) {
     sources += [ "tracing_service_unittest.cc" ]
   }
diff --git a/services/tracing/public/cpp/BUILD.gn b/services/tracing/public/cpp/BUILD.gn
index 870afc2..26f14902 100644
--- a/services/tracing/public/cpp/BUILD.gn
+++ b/services/tracing/public/cpp/BUILD.gn
@@ -119,6 +119,13 @@
       "tracing_features.h",
     ]
 
+    if (is_android && current_cpu == "arm64") {
+      sources += [
+        "stack_sampling/stack_unwinder_arm64_android.cc",
+        "stack_sampling/stack_unwinder_arm64_android.h",
+      ]
+    }
+
     if (enable_loader_lock_sampling) {
       sources += [
         "stack_sampling/loader_lock_sampler_win.cc",
diff --git a/services/tracing/public/cpp/stack_sampling/stack_unwinder_arm64_android.cc b/services/tracing/public/cpp/stack_sampling/stack_unwinder_arm64_android.cc
new file mode 100644
index 0000000..92dc620
--- /dev/null
+++ b/services/tracing/public/cpp/stack_sampling/stack_unwinder_arm64_android.cc
@@ -0,0 +1,37 @@
+// Copyright 2020 The Chromium 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 "services/tracing/public/cpp/stack_sampling/stack_unwinder_arm64_android.h"
+
+#include "base/debug/stack_trace.h"
+
+namespace tracing {
+
+bool UnwinderArm64::CanUnwindFrom(const base::Frame& current_frame) const {
+  return true;
+}
+
+base::UnwindResult UnwinderArm64::TryUnwind(
+    base::RegisterContext* thread_context,
+    uintptr_t stack_top,
+    base::ModuleCache* module_cache,
+    std::vector<base::Frame>* stack) const {
+  uintptr_t fp = thread_context->regs[29];
+  constexpr size_t kMaxDepth = 40;
+  const void* out_trace[kMaxDepth] = {};
+  // If the fp is not valid, then pass the stack pointer as fp. The unwind
+  // function scans the stack to find the next frame.
+  if (fp < thread_context->sp || fp >= stack_top) {
+    fp = thread_context->sp;
+  }
+  size_t depth = base::debug::TraceStackFramePointersFromBuffer(
+      fp, stack_top, out_trace, kMaxDepth, 0, /*enable_scanning=*/true);
+  for (size_t i = 0; i < depth; ++i) {
+    uintptr_t pc = reinterpret_cast<uintptr_t>(out_trace[i]);
+    stack->push_back(base::Frame(pc, module_cache->GetModuleForAddress(pc)));
+  }
+  return base::UnwindResult::COMPLETED;
+}
+
+}  // namespace tracing
diff --git a/services/tracing/public/cpp/stack_sampling/stack_unwinder_arm64_android.h b/services/tracing/public/cpp/stack_sampling/stack_unwinder_arm64_android.h
new file mode 100644
index 0000000..515a6cb
--- /dev/null
+++ b/services/tracing/public/cpp/stack_sampling/stack_unwinder_arm64_android.h
@@ -0,0 +1,26 @@
+// Copyright 2020 The Chromium 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 SERVICES_TRACING_PUBLIC_CPP_STACK_SAMPLING_STACK_UNWINDER_ARM64_ANDROID_H_
+#define SERVICES_TRACING_PUBLIC_CPP_STACK_SAMPLING_STACK_UNWINDER_ARM64_ANDROID_H_
+
+#include "base/component_export.h"
+#include "base/profiler/unwinder.h"
+
+namespace tracing {
+
+// Unwinder implementation for arm64 builds with frame pointer enabled.
+class COMPONENT_EXPORT(TRACING_CPP) UnwinderArm64 : public base::Unwinder {
+ public:
+  bool CanUnwindFrom(const base::Frame& current_frame) const override;
+
+  base::UnwindResult TryUnwind(base::RegisterContext* thread_context,
+                               uintptr_t stack_top,
+                               base::ModuleCache* module_cache,
+                               std::vector<base::Frame>* stack) const override;
+};
+
+}  // namespace tracing
+
+#endif  // SERVICES_TRACING_PUBLIC_CPP_STACK_SAMPLING_STACK_UNWINDER_ARM64_ANDROID_H_
diff --git a/services/tracing/public/cpp/stack_sampling/stack_unwinder_arm64_android_unittest.cc b/services/tracing/public/cpp/stack_sampling/stack_unwinder_arm64_android_unittest.cc
new file mode 100644
index 0000000..02b47b9
--- /dev/null
+++ b/services/tracing/public/cpp/stack_sampling/stack_unwinder_arm64_android_unittest.cc
@@ -0,0 +1,118 @@
+// Copyright 2020 The Chromium 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 "services/tracing/public/cpp/stack_sampling/stack_unwinder_arm64_android.h"
+
+#include "base/profiler/register_context.h"
+#include "base/profiler/stack_buffer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace tracing {
+namespace {
+
+constexpr size_t kMaxFrameCount = 5;
+constexpr size_t kFrameSize = 40;
+constexpr size_t kStackSize = 1024;
+constexpr uintptr_t kFirstSamplePc = 0x12345678;
+
+void SetValue(uintptr_t address, uintptr_t value) {
+  uintptr_t* ptr = reinterpret_cast<uintptr_t*>(address);
+  *ptr = value;
+}
+
+void FillFrames(uintptr_t fp, size_t frame_count) {
+  for (size_t i = 0; i < frame_count; ++i) {
+    SetValue(fp, fp + kFrameSize);
+    SetValue(fp + sizeof(uintptr_t), kFirstSamplePc + i);
+    fp = fp + kFrameSize;
+  }
+}
+
+}  // namespace
+
+TEST(UnwinderArm64Test, UnwindValidStack) {
+  base::RegisterContext register_context{};
+  base::ModuleCache module_cache;
+  base::StackBuffer buffer(kStackSize);
+  memset(buffer.buffer(), 0, kStackSize);
+
+  const uintptr_t fp = reinterpret_cast<uintptr_t>(buffer.buffer());
+  const size_t stack_top = fp + kStackSize;
+  register_context.regs[29] = fp;
+  register_context.sp = fp;
+  FillFrames(fp, kMaxFrameCount);
+
+  UnwinderArm64 unwinder;
+  std::vector<base::Frame> stack;
+  EXPECT_EQ(
+      base::UnwindResult::COMPLETED,
+      unwinder.TryUnwind(&register_context, stack_top, &module_cache, &stack));
+
+  ASSERT_EQ(kMaxFrameCount, stack.size());
+  for (size_t i = 0; i < kMaxFrameCount; ++i) {
+    EXPECT_EQ(kFirstSamplePc + i, stack[i].instruction_pointer);
+    EXPECT_EQ(nullptr, stack[i].module);
+  }
+}
+
+TEST(UnwinderArm64Test, UnwindInvalidFirstFrame) {
+  base::RegisterContext register_context{};
+  base::ModuleCache module_cache;
+  base::StackBuffer buffer(kStackSize);
+  memset(buffer.buffer(), 0, kStackSize);
+
+  uintptr_t fp = reinterpret_cast<uintptr_t>(buffer.buffer());
+  const size_t stack_top = fp + kStackSize;
+  register_context.regs[29] = fp;
+  register_context.sp = fp;
+  // FP from register context points bad frame within stack.
+  fp += 80;
+  FillFrames(fp, kMaxFrameCount);
+
+  UnwinderArm64 unwinder;
+  std::vector<base::Frame> stack;
+  EXPECT_EQ(
+      base::UnwindResult::COMPLETED,
+      unwinder.TryUnwind(&register_context, stack_top, &module_cache, &stack));
+
+  // One extra frame is added when scanning starts.
+  ASSERT_EQ(kMaxFrameCount + 1, stack.size());
+  for (size_t i = 0; i < kMaxFrameCount; ++i) {
+    EXPECT_EQ(kFirstSamplePc + i, stack[i + 1].instruction_pointer);
+    EXPECT_EQ(nullptr, stack[i + 1].module);
+  }
+}
+
+TEST(UnwinderArm64Test, UnwindInvalidFp) {
+  base::RegisterContext register_context{};
+  base::ModuleCache module_cache;
+  base::StackBuffer buffer(kStackSize);
+  memset(buffer.buffer(), 0, kStackSize);
+
+  uintptr_t fp = reinterpret_cast<uintptr_t>(buffer.buffer());
+  const size_t stack_top = fp + kStackSize;
+
+  // FP points to a bad value. SP will be used to start unwinding. SP points to
+  // some point in end of stack, but the first frame starts after 20 bytes.
+  register_context.regs[29] = 50;
+  register_context.sp = fp;
+  fp += 80;
+
+  FillFrames(fp, kMaxFrameCount);
+
+  UnwinderArm64 unwinder;
+  std::vector<base::Frame> stack;
+  EXPECT_EQ(
+      base::UnwindResult::COMPLETED,
+      unwinder.TryUnwind(&register_context, stack_top, &module_cache, &stack));
+
+  // One extra frame is added when scanning starts.
+  ASSERT_EQ(kMaxFrameCount + 1, stack.size());
+  for (size_t i = 0; i < kMaxFrameCount; ++i) {
+    EXPECT_EQ(kFirstSamplePc + i, stack[i + 1].instruction_pointer);
+    EXPECT_EQ(nullptr, stack[i + 1].module);
+  }
+}
+
+}  // namespace tracing
diff --git a/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.cc b/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.cc
index f6ee0c49..b84216fc 100644
--- a/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.cc
+++ b/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.cc
@@ -7,8 +7,10 @@
 #include <limits>
 #include <set>
 
+#include "base/android/library_loader/anchor_functions.h"
 #include "base/bind_helpers.h"
 #include "base/debug/leak_annotations.h"
+#include "base/debug/stack_trace.h"
 #include "base/hash/hash.h"
 #include "base/memory/ptr_util.h"
 #include "base/no_destructor.h"
@@ -31,17 +33,21 @@
 #include "third_party/perfetto/protos/perfetto/trace/track_event/process_descriptor.pbzero.h"
 #include "third_party/perfetto/protos/perfetto/trace/track_event/thread_descriptor.pbzero.h"
 
-#if defined(OS_ANDROID)
-#include "base/android/reached_code_profiler.h"
-#endif
-
-#if defined(OS_ANDROID) && BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && \
-    defined(OFFICIAL_BUILD)
+#if ANDROID_ARM64_UNWINDING_SUPPORTED || ANDROID_CFI_UNWINDING_SUPPORTED
 #include <dlfcn.h>
 
+#include "base/android/reached_code_profiler.h"
+
+#if ANDROID_ARM64_UNWINDING_SUPPORTED
+#include "services/tracing/public/cpp/stack_sampling/stack_unwinder_arm64_android.h"
+
+#elif ANDROID_CFI_UNWINDING_SUPPORTED
 #include "base/trace_event/cfi_backtrace_android.h"
 #include "services/tracing/public/cpp/stack_sampling/stack_sampler_android.h"
-#endif
+
+#endif  // ANDROID_ARM64_UNWINDING_SUPPORTED
+
+#endif  // ANDROID_ARM64_UNWINDING_SUPPORTED || ANDROID_CFI_UNWINDING_SUPPORTED
 
 #if BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING)
 #include "services/tracing/public/cpp/stack_sampling/loader_lock_sampler_win.h"
@@ -54,6 +60,25 @@
 
 namespace {
 
+#if ANDROID_ARM64_UNWINDING_SUPPORTED || ANDROID_CFI_UNWINDING_SUPPORTED
+extern "C" {
+
+// The address of |__executable_start| gives the start address of the
+// executable or shared library. This value is used to find the offset address
+// of the instruction in binary from PC.
+extern char __executable_start;
+
+}  // extern "C"
+
+bool is_chrome_address(uintptr_t pc) {
+  return pc >= base::android::kStartOfText && pc < base::android::kEndOfText;
+}
+
+uintptr_t executable_start_addr() {
+  return reinterpret_cast<uintptr_t>(&__executable_start);
+}
+#endif  // ANDROID_ARM64_UNWINDING_SUPPORTED || ANDROID_CFI_UNWINDING_SUPPORTED
+
 // Pointer to the main thread instance, if any.
 TracingSamplerProfiler* g_main_thread_instance = nullptr;
 
@@ -360,18 +385,15 @@
     std::string module_id;
     uintptr_t rel_pc = 0;
 
-#if defined(OS_ANDROID) && BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && \
-    defined(OFFICIAL_BUILD)
+#if ANDROID_ARM64_UNWINDING_SUPPORTED || ANDROID_CFI_UNWINDING_SUPPORTED
     Dl_info info = {};
     // For chrome address we do not have symbols on the binary. So, just write
     // the offset address. For addresses on framework libraries, symbolize
     // and write the function name.
     if (frame.instruction_pointer == 0) {
       frame_name = "Scanned";
-    } else if (base::trace_event::CFIBacktraceAndroid::is_chrome_address(
-                   frame.instruction_pointer)) {
-      rel_pc = frame.instruction_pointer -
-               base::trace_event::CFIBacktraceAndroid::executable_start_addr();
+    } else if (is_chrome_address(frame.instruction_pointer)) {
+      rel_pc = frame.instruction_pointer - executable_start_addr();
     } else if (dladdr(reinterpret_cast<void*>(frame.instruction_pointer),
                       &info) != 0) {
       // TODO(ssid): Add offset and module debug id if symbol was not resolved
@@ -390,13 +412,11 @@
 
     // If no module is available, then name it unknown. Adding PC would be
     // useless anyway.
-    if (module_name.empty()) {
-      DCHECK(!base::trace_event::CFIBacktraceAndroid::is_chrome_address(
-          frame.instruction_pointer));
+    if (module_name.empty() && !is_chrome_address(frame.instruction_pointer)) {
       frame_name = "Unknown";
       rel_pc = 0;
     }
-#else
+#else   // ANDROID_ARM64_UNWINDING_SUPPORTED || ANDROID_CFI_UNWINDING_SUPPORTED
     if (frame.module) {
       module_name = frame.module->GetDebugBasename().MaybeAsASCII();
       module_id = frame.module->GetId();
@@ -405,7 +425,8 @@
       module_name = module_id = "";
       frame_name = "Unknown";
     }
-#endif
+#endif  // !(ANDROID_ARM64_UNWINDING_SUPPORTED ||
+        // ANDROID_CFI_UNWINDING_SUPPORTED)
 
     MangleModuleIDIfNeeded(&module_id);
 
@@ -654,15 +675,20 @@
     return;
   }
 
-#if defined(OS_ANDROID)
+#if ANDROID_ARM64_UNWINDING_SUPPORTED || ANDROID_CFI_UNWINDING_SUPPORTED
   // The sampler profiler would conflict with the reached code profiler if they
   // run at the same time because they use the same signal to suspend threads.
   if (base::android::IsReachedCodeProfilerEnabled())
     return;
-#endif
+#else   // ANDROID_ARM64_UNWINDING_SUPPORTED || ANDROID_CFI_UNWINDING_SUPPORTED
 
+  // On Android the sampling profiler is implemented by tracing service and is
+  // not yet supported by base::StackSamplingProfiler. So, only check this if
+  // service does not support unwinding in current platform.
   if (!base::StackSamplingProfiler::IsSupported())
     return;
+#endif  // !(ANDROID_ARM64_UNWINDING_SUPPORTED ||
+        // ANDROID_CFI_UNWINDING_SUPPORTED)
 
   base::StackSamplingProfiler::SamplingParams params;
   params.samples_per_profile = std::numeric_limits<int>::max();
@@ -679,14 +705,22 @@
   profile_builder_ = profile_builder.get();
   // Create and start the stack sampling profiler.
 #if defined(OS_ANDROID)
-#if BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && defined(OFFICIAL_BUILD)
+#if ANDROID_ARM64_UNWINDING_SUPPORTED
+  std::vector<std::unique_ptr<base::Unwinder>> unwinder;
+  unwinder.push_back(std::make_unique<UnwinderArm64>());
+  profiler_ = std::make_unique<base::StackSamplingProfiler>(
+      sampled_thread_token_, params, std::move(profile_builder),
+      std::move(unwinder));
+  profiler_->Start();
+
+#elif ANDROID_CFI_UNWINDING_SUPPORTED
   auto* module_cache = profile_builder->GetModuleCache();
   profiler_ = std::make_unique<base::StackSamplingProfiler>(
       params, std::move(profile_builder),
       std::make_unique<StackSamplerAndroid>(sampled_thread_token_,
                                             module_cache));
   profiler_->Start();
-#endif  // BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && defined(OFFICIAL_BUILD)
+#endif
 #else   // defined(OS_ANDROID)
   profiler_ = std::make_unique<base::StackSamplingProfiler>(
       sampled_thread_token_, params, std::move(profile_builder));
diff --git a/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h b/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h
index b1ab4154..c3c59e2 100644
--- a/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h
+++ b/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h
@@ -25,6 +25,20 @@
 #include "services/tracing/public/cpp/perfetto/interning_index.h"
 #include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_writer.h"
 
+#if defined(OS_ANDROID) && defined(ARCH_CPU_ARM64) && \
+    BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
+#define ANDROID_ARM64_UNWINDING_SUPPORTED 1
+#else
+#define ANDROID_ARM64_UNWINDING_SUPPORTED 0
+#endif
+
+#if defined(OS_ANDROID) && BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && \
+    defined(OFFICIAL_BUILD)
+#define ANDROID_CFI_UNWINDING_SUPPORTED 1
+#else
+#define ANDROID_CFI_UNWINDING_SUPPORTED 0
+#endif
+
 namespace tracing {
 
 class PerfettoProducer;
@@ -167,9 +181,8 @@
   // Returns whether of not the sampler profiling is able to unwind the stack
   // on this platform.
   constexpr static bool IsStackUnwindingSupported() {
-#if defined(OS_MAC) || defined(OS_WIN) && defined(_WIN64) ||      \
-    (defined(OS_ANDROID) && BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && \
-     defined(OFFICIAL_BUILD))
+#if defined(OS_MAC) || defined(OS_WIN) && defined(_WIN64) || \
+    ANDROID_ARM64_UNWINDING_SUPPORTED || ANDROID_CFI_UNWINDING_SUPPORTED
     return true;
 #else
     return false;
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index 3c6d2eff..5581a949 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -314,6 +314,38 @@
       }
     ]
   },
+  "android-pie-arm64-wpt-rel-non-cq": {
+    "isolated_scripts": [
+      {
+        "isolate_name": "weblayer_shell_wpt",
+        "merge": {
+          "args": [
+            "--verbose"
+          ],
+          "script": "//third_party/blink/tools/merge_web_test_results.py"
+        },
+        "name": "weblayer_shell_wpt",
+        "results_handler": "layout tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "device_os": "PQ3A.190801.002",
+              "device_os_flavor": "google",
+              "device_os_type": "userdebug",
+              "device_type": "walleye",
+              "os": "Android"
+            }
+          ],
+          "expiration": 18000,
+          "hard_timeout": 14400,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 15
+        },
+        "test_id_prefix": "ninja://weblayer/shell/android:weblayer_shell_wpt/"
+      }
+    ]
+  },
   "android-weblayer-pie-x86-fyi-rel": {
     "gtest_tests": [
       {
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index c83bd3e..cd9b377ad 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -41298,38 +41298,6 @@
       }
     ]
   },
-  "android-pie-arm64-wpt-rel-non-cq": {
-    "isolated_scripts": [
-      {
-        "isolate_name": "weblayer_shell_wpt",
-        "merge": {
-          "args": [
-            "--verbose"
-          ],
-          "script": "//third_party/blink/tools/merge_web_test_results.py"
-        },
-        "name": "weblayer_shell_wpt",
-        "results_handler": "layout tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "device_os": "PQ3A.190801.002",
-              "device_os_flavor": "google",
-              "device_os_type": "userdebug",
-              "device_type": "walleye",
-              "os": "Android"
-            }
-          ],
-          "expiration": 18000,
-          "hard_timeout": 14400,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 15
-        },
-        "test_id_prefix": "ninja://weblayer/shell/android:weblayer_shell_wpt/"
-      }
-    ]
-  },
   "android-pie-x86-rel": {
     "gtest_tests": [
       {
diff --git a/testing/buildbot/filters/lacros.browser_tests.filter b/testing/buildbot/filters/lacros.browser_tests.filter
index 72f36b5..df10860 100644
--- a/testing/buildbot/filters/lacros.browser_tests.filter
+++ b/testing/buildbot/filters/lacros.browser_tests.filter
@@ -136,10 +136,7 @@
 -PaymentRequestShippingAddressEditorTest.FocusFirstField_Name

 -PaymentRequestShippingAddressEditorTest.FocusFirstInvalidField_NotName

 -PDFExtensionJSTest.Bookmark/1

--PDFExtensionTestWithTestGuestViewManager.CSPDoesNotBlockEmbedStyles

--PDFExtensionTestWithTestGuestViewManager.CSPFrameAncestorsOverridesXFrameOptions

--PDFExtensionTestWithTestGuestViewManager.PdfExtensionLoadedInGuest

--PDFExtensionTestWithTestGuestViewManager.PdfInMainFrameHasFocus

+-PDFExtensionTestWithTestGuestViewManager*

 -PermissionRequestManagerWithBackForwardCacheBrowserTest.RequestsForPagesInCacheNotGrouped

 -PermissionsApiTest.AlwaysAllowed

 -PictureInPicturePixelComparisonBrowserTest.PlayAndPauseControls

@@ -225,5 +222,4 @@
 -MediaRouterUIBrowserTest.PinAndUnpinToolbarIcon

 -MultiActionAPITest.PopupCreation/*

 -PDFExtensionClipboardTest.CombinedShiftArrowPresses

--PDFExtensionTestWithTestGuestViewManager.CSPFrameAncestorsOverridesXFrameOptions*

 -PopupTrackerBrowserTest.ControlClick_HasTracker

diff --git a/testing/buildbot/filters/lacros.content_browsertests.filter b/testing/buildbot/filters/lacros.content_browsertests.filter
index 989f1ec..bf84e3e4 100644
--- a/testing/buildbot/filters/lacros.content_browsertests.filter
+++ b/testing/buildbot/filters/lacros.content_browsertests.filter
@@ -4,4 +4,3 @@
 

 # Following tests are flaky.

 -All/WebContentsVideoCaptureDeviceBrowserTestP.CapturesContentChanges*

--PDFExtensionTestWithTestGuestViewManager*
\ No newline at end of file
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 3ab54c7..796150f 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -810,17 +810,6 @@
         'use_swarming': True,
         'os_type': 'android',
       },
-      'android-pie-arm64-wpt-rel-non-cq': {
-        'mixins': [
-          'pie_fleet',
-          'walleye',
-        ],
-        'test_suites': {
-          'isolated_scripts': 'weblayer_shell_wpt',
-        },
-        'use_swarming': True,
-        'os_type': 'android',
-      },
       'android-pie-x86-rel': {
         'mixins': [
           'pie-x86-emulator',
@@ -865,6 +854,17 @@
           'gtest_tests': 'android_marshmallow_emulator_backlog_gtests',
         }
       },
+      'android-pie-arm64-wpt-rel-non-cq': {
+        'mixins': [
+          'pie_fleet',
+          'walleye',
+        ],
+        'test_suites': {
+          'isolated_scripts': 'weblayer_shell_wpt',
+        },
+        'use_swarming': True,
+        'os_type': 'android',
+      },
       'android-weblayer-pie-x86-fyi-rel': {
         'mixins': [
           'pie-x86-emulator',
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index e6159ed..80faeb6 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -72,6 +72,21 @@
             ]
         }
     ],
+    "AmpBackgroundTab": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "AmpBackgroundTab",
+                    "enable_features": [
+                        "CCTBackgroundTab"
+                    ]
+                }
+            ]
+        }
+    ],
     "AndroidDarkSearch": [
         {
             "platforms": [
@@ -108,6 +123,222 @@
             ]
         }
     ],
+    "AndroidDynamicWideColorGamut": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "DynamicColorGamut"
+                    ]
+                }
+            ]
+        }
+    ],
+    "AndroidInProductHelpContextualSearchPromotePanelOpen": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "PromotePanelOpeningExperiment",
+                    "params": {
+                        "availability": ">=14",
+                        "event_1": "name:contextual_search_promote_panel_open_iph_trigger;comparator:<2;window:680;storage:680",
+                        "event_2": "name:contextual_search_panel_opened;comparator:<3;window:90;storage:90",
+                        "event_trigger": "name:contextual_search_promote_panel_open_iph_trigger;comparator:==0;window:90;storage:680",
+                        "event_used": "name:contextual_search_panel_opened_for_entity;comparator:==0;window:90;storage:90",
+                        "session_rate": "<1"
+                    },
+                    "enable_features": [
+                        "IPH_ContextualSearchPromotePanelOpen"
+                    ]
+                }
+            ]
+        }
+    ],
+    "AndroidInProductHelpContextualSearchPromoteTap": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "PromoteTapExperiment",
+                    "params": {
+                        "availability": ">=14",
+                        "event_1": "name:contextual_search_panel_opened;comparator:>=2;window:90;storage:90",
+                        "event_2": "name:contextual_search_promote_tap_iph_trigger;comparator:<2;window:680;storage:680",
+                        "event_trigger": "name:contextual_search_promote_tap_iph_trigger;comparator:==0;window:90;storage:680",
+                        "event_used": "name:contextual_search_panel_opened_after_tap;comparator:==0;window:30;storage:90",
+                        "session_rate": "<1"
+                    },
+                    "enable_features": [
+                        "IPH_ContextualSearchPromoteTap"
+                    ]
+                }
+            ]
+        }
+    ],
+    "AndroidInProductHelpContextualSearchWebSearch": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "WebSearchExperiment",
+                    "params": {
+                        "availability": ">=14",
+                        "event_1": "name:web_search_performed;comparator:>=1;window:90;storage:90",
+                        "event_2": "name:contextual_search_web_search_iph_trigger;comparator:<2;window:680;storage:680",
+                        "event_3": "name:contextual_search_panel_opened;comparator:>0;window:680;storage:680",
+                        "event_trigger": "name:contextual_search_web_search_iph_trigger;comparator:==0;window:90;storage:680",
+                        "event_used": "name:contextual_search_panel_opened;comparator:==0;window:30;storage:90",
+                        "session_rate": "<1"
+                    },
+                    "enable_features": [
+                        "IPH_ContextualSearchWebSearch"
+                    ]
+                }
+            ]
+        }
+    ],
+    "AndroidInProductHelpDownloadInfoBarDownloadContinuing": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Tracking",
+                    "params": {
+                        "availability": "any",
+                        "event_trigger": "name:download_infobar_download_continuing_iph_trigger;comparator:==0;window:90;storage:360",
+                        "event_used": "name:download_home_opened;comparator:any;window:90;storage:360",
+                        "session_rate": "<1"
+                    },
+                    "enable_features": [
+                        "IPH_DownloadInfoBarDownloadContinuing"
+                    ]
+                }
+            ]
+        }
+    ],
+    "AndroidInProductHelpDownloadInfoBarDownloadsAreFaster": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Tracking",
+                    "params": {
+                        "availability": "any",
+                        "event_trigger": "name:download_infobar_downloads_are_faster_iph_trigger;comparator:==0;window:90;storage:360",
+                        "event_used": "name:download_completed;comparator:any;window:90;storage:360",
+                        "session_rate": "<1"
+                    },
+                    "enable_features": [
+                        "IPH_DownloadInfoBarDownloadsAreFaster"
+                    ]
+                }
+            ]
+        }
+    ],
+    "AndroidInProductHelpDownloadPage": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Tracking",
+                    "params": {
+                        "availability": ">=14",
+                        "event_1": "name:user_has_seen_dino;comparator:>=1;window:90;storage:360",
+                        "event_trigger": "name:download_page_iph_trigger;comparator:==0;window:90;storage:360",
+                        "event_used": "name:download_page_started;comparator:==0;window:90;storage:360",
+                        "session_rate": "<0"
+                    },
+                    "enable_features": [
+                        "IPH_DownloadPage"
+                    ]
+                }
+            ]
+        }
+    ],
+    "AndroidInProductHelpDownloadPageScreenshot": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "EnabledTracking",
+                    "params": {
+                        "availability": "any",
+                        "event_screenshot_taken": "name:screenshot_taken_chrome_in_foreground;comparator:>=1;window:90;storage:360",
+                        "event_trigger": "name:download_page_iph_would_have_triggered;comparator:==0;window:90;storage:360",
+                        "event_used": "name:download_page_started;comparator:==0;window:90;storage:360",
+                        "session_rate": "<=1",
+                        "tracking_only": "true"
+                    },
+                    "enable_features": [
+                        "IPH_DownloadPageScreenshot"
+                    ]
+                }
+            ]
+        }
+    ],
+    "AndroidInProductHelpDownloadSettings": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Tracking",
+                    "params": {
+                        "availability": "any",
+                        "event_settings_accessed": "name:download_settings_access_through_download_home;comparator:==0;window:60;storage:360",
+                        "event_trigger": "name:download_settings_iph_trigger;comparator:any;window:90;storage:360",
+                        "event_used": "name:download_settings_opened;comparator:any;window:90;storage:360",
+                        "session_rate": "<1"
+                    },
+                    "enable_features": [
+                        "IPH_DownloadSettings"
+                    ]
+                }
+            ]
+        }
+    ],
+    "AndroidInProductHelpManualTranslate": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "params": {
+                        "availability": "any",
+                        "event_trigger": "name:translate_menu_button_iph_triggered;comparator:<2;window:30;storage:360",
+                        "event_trigger_delay": "name:translate_menu_button_iph_triggered;comparator:<1;window:1;storage:360",
+                        "event_used": "name:translate_menu_button_clicked;comparator:==0;window:90;storage:360",
+                        "session_rate": "==0"
+                    },
+                    "enable_features": [
+                        "IPH_TranslateMenuButton"
+                    ]
+                }
+            ]
+        }
+    ],
     "AndroidInProductHelpReengagementNotification": [
         {
             "platforms": [
@@ -130,6 +361,39 @@
             ]
         }
     ],
+    "AndroidInlineUpdateFlowStudy": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled_NoNotification",
+                    "params": {
+                        "flow": "best_effort"
+                    },
+                    "enable_features": [
+                        "InlineUpdateFlow"
+                    ]
+                }
+            ]
+        }
+    ],
+    "AndroidMediaProcessPriority": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "ModerateBindingInBackground",
+                    "enable_features": [
+                        "BackgroundMediaRendererHasModerateBinding"
+                    ]
+                }
+            ]
+        }
+    ],
     "AndroidPartnerCustomizationPhenotype": [
         {
             "platforms": [
@@ -145,6 +409,22 @@
             ]
         }
     ],
+    "AndroidPictureInPictureAPI": [
+        {
+            "platforms": [
+                "android",
+                "android_weblayer"
+            ],
+            "experiments": [
+                {
+                    "name": "Disabled",
+                    "disable_features": [
+                        "PictureInPictureAPI"
+                    ]
+                }
+            ]
+        }
+    ],
     "AndroidSpellChecker": [
         {
             "platforms": [
@@ -163,6 +443,68 @@
             ]
         }
     ],
+    "AndroidSwitchToTab": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "OmniboxTabSwitchSuggestions"
+                    ]
+                }
+            ]
+        }
+    ],
+    "AndroidSystemTracing": [
+        {
+            "platforms": [
+                "android",
+                "android_weblayer",
+                "android_webview"
+            ],
+            "experiments": [
+                {
+                    "name": "EnablePerfettoSystemTracing",
+                    "enable_features": [
+                        "EnablePerfettoSystemTracing"
+                    ]
+                }
+            ]
+        }
+    ],
+    "AppListLaunchRecorder": [
+        {
+            "platforms": [
+                "chromeos"
+            ],
+            "experiments": [
+                {
+                    "name": "AppListLaunchRecorder",
+                    "enable_features": [
+                        "EnableAppListLaunchRecording"
+                    ]
+                }
+            ]
+        }
+    ],
+    "AppListZeroStateMixedTypesRanker": [
+        {
+            "platforms": [
+                "chromeos"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled_20191017",
+                    "enable_features": [
+                        "EnableZeroStateMixedTypesRanker"
+                    ]
+                }
+            ]
+        }
+    ],
     "AssistPersonalInfo": [
         {
             "platforms": [
@@ -193,6 +535,21 @@
             ]
         }
     ],
+    "AudioServiceSandboxLinux": [
+        {
+            "platforms": [
+                "linux"
+            ],
+            "experiments": [
+                {
+                    "name": "AudioProcessSandboxLinux",
+                    "enable_features": [
+                        "AudioServiceSandbox"
+                    ]
+                }
+            ]
+        }
+    ],
     "AutoScreenBrightness": [
         {
             "platforms": [
@@ -1867,6 +2224,21 @@
             ]
         }
     ],
+    "DisableForceDeferInChildFrames": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "DisableForceDeferInChildFrames"
+                    ]
+                }
+            ]
+        }
+    ],
     "DisableGles2ForOopR": [
         {
             "platforms": [
diff --git a/third_party/blink/common/BUILD.gn b/third_party/blink/common/BUILD.gn
index 025a283a..2670428 100644
--- a/third_party/blink/common/BUILD.gn
+++ b/third_party/blink/common/BUILD.gn
@@ -58,8 +58,8 @@
   # Dependencies instead should be to //third_party/blink/public/common:common.
   visibility = [
     ":*",
-    "//third_party/blink/public/common",
     "//third_party/blink/public:all_blink",
+    "//third_party/blink/public/common",
   ]
 
   configs += [ ":blink_common_implementation" ]
@@ -155,6 +155,7 @@
     "user_agent/user_agent_metadata.cc",
     "web_package/signed_exchange_consts.cc",
     "web_package/web_package_request_matcher.cc",
+    "web_preferences/web_preferences.cc",
     "widget/device_emulation_params_mojom_traits.cc",
     "widget/screen_info_mojom_traits.cc",
     "widget/visual_properties_mojom_traits.cc",
diff --git a/third_party/blink/common/DEPS b/third_party/blink/common/DEPS
index 9bc1a47..a23e935 100644
--- a/third_party/blink/common/DEPS
+++ b/third_party/blink/common/DEPS
@@ -27,7 +27,9 @@
     "+third_party/blink/common",
     "+third_party/blink/public/common",
     "+third_party/blink/public/mojom",
+    "+third_party/blink/public/web",
     "+third_party/icu/source/common/unicode/unistr.h",
+    "+ui/base/ui_base_switches_util.h",
     "+ui/events/base_event_utils.h",
     "+ui/gfx/transform.h",
     "+ui/gfx/geometry",
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index bb172b6..7f541274 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -76,6 +76,9 @@
 const base::Feature kTopLevelAwait{"TopLevelAwait",
                                    base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enable EditingNG by default. This feature is for a kill switch.
+const base::Feature kEditingNG{"EditingNG", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enable LayoutNG.
 const base::Feature kLayoutNG{"LayoutNG", base::FEATURE_ENABLED_BY_DEFAULT};
 
diff --git a/content/public/common/web_preferences.cc b/third_party/blink/common/web_preferences/web_preferences.cc
similarity index 95%
rename from content/public/common/web_preferences.cc
rename to third_party/blink/common/web_preferences/web_preferences.cc
index 839e3cbd..d28ac17b 100644
--- a/content/public/common/web_preferences.cc
+++ b/third_party/blink/common/web_preferences/web_preferences.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/public/common/web_preferences.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
@@ -11,8 +11,6 @@
 #include "third_party/blink/public/web/web_settings.h"
 #include "ui/base/ui_base_switches_util.h"
 
-using blink::WebSettings;
-
 namespace {
 
 bool IsTouchDragDropEnabled() {
@@ -24,7 +22,9 @@
 
 }  // namespace
 
-namespace content {
+namespace blink {
+
+namespace web_pref {
 
 // "Zyyy" is the ISO 15924 script code for undetermined script aka Common.
 const char kCommonScript[] = "Zyyy";
@@ -261,7 +261,15 @@
 
 WebPreferences::WebPreferences(const WebPreferences& other) = default;
 
-WebPreferences::~WebPreferences() {
-}
+WebPreferences::WebPreferences(WebPreferences&& other) = default;
 
-}  // namespace content
+WebPreferences::~WebPreferences() = default;
+
+WebPreferences& WebPreferences::operator=(const WebPreferences& other) =
+    default;
+
+WebPreferences& WebPreferences::operator=(WebPreferences&& other) = default;
+
+}  // namespace web_pref
+
+}  // namespace blink
diff --git a/third_party/blink/public/common/BUILD.gn b/third_party/blink/public/common/BUILD.gn
index 7f4fcc3..41d84210 100644
--- a/third_party/blink/public/common/BUILD.gn
+++ b/third_party/blink/public/common/BUILD.gn
@@ -34,23 +34,23 @@
 
 source_set("common_export") {
   visibility = [
-    "//third_party/blink/public/common/*",
     "//third_party/blink/common/*",
+    "//third_party/blink/public/common/*",
   ]
   sources = [ "common_export.h" ]
 }
 
 source_set("headers") {
   visibility = [
+    ":*",
     "//chrome/*",
     "//chromecast/*",
+    "//components/*",
     "//content/*",
     "//fuchsia/*",
-    "//third_party/blink/*",
-    "//components/*",
     "//services/*",
     "//storage/*",
-    ":*",
+    "//third_party/blink/*",
   ]
 
   sources = [
@@ -182,6 +182,7 @@
     "web_cache/web_cache_resource_type_stats.h",
     "web_package/signed_exchange_consts.h",
     "web_package/web_package_request_matcher.h",
+    "web_preferences/web_preferences.h",
     "widget/device_emulation_params.h",
     "widget/screen_info.h",
     "widget/visual_properties.h",
@@ -212,6 +213,7 @@
     "//mojo/public/cpp/system",
     "//mojo/public/mojom/base",
     "//net",
+    "//ui/base:base",
   ]
 
   # iOS doesn't use and must not depend on //media
diff --git a/third_party/blink/public/common/DEPS b/third_party/blink/public/common/DEPS
index 46621c0..3d1991fa 100644
--- a/third_party/blink/public/common/DEPS
+++ b/third_party/blink/public/common/DEPS
@@ -21,6 +21,7 @@
     "+skia/public/mojom",
     "+third_party/blink/public/common",
     "+third_party/blink/public/mojom",
+    "+ui/base/pointer/pointer_device.h",
     "+ui/events/event_constants.h",
     "+ui/events/types",
     "+ui/gfx/color_space.h",
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index 9443280..494f737e 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -34,6 +34,7 @@
 BLINK_COMMON_EXPORT extern const base::Feature kJSONModules;
 BLINK_COMMON_EXPORT extern const base::Feature kForceSynchronousHTMLParsing;
 BLINK_COMMON_EXPORT extern const base::Feature kTopLevelAwait;
+BLINK_COMMON_EXPORT extern const base::Feature kEditingNG;
 BLINK_COMMON_EXPORT extern const base::Feature kLayoutNG;
 BLINK_COMMON_EXPORT extern const base::Feature kLayoutNGFieldset;
 BLINK_COMMON_EXPORT extern const base::Feature kLayoutNGRuby;
diff --git a/content/public/common/web_preferences.h b/third_party/blink/public/common/web_preferences/web_preferences.h
similarity index 95%
rename from content/public/common/web_preferences.h
rename to third_party/blink/public/common/web_preferences/web_preferences.h
index 700b47b..85dccb41 100644
--- a/content/public/common/web_preferences.h
+++ b/third_party/blink/public/common/web_preferences/web_preferences.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_PUBLIC_COMMON_WEB_PREFERENCES_H_
-#define CONTENT_PUBLIC_COMMON_WEB_PREFERENCES_H_
+#ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_WEB_PREFERENCES_WEB_PREFERENCES_H_
+#define THIRD_PARTY_BLINK_PUBLIC_COMMON_WEB_PREFERENCES_WEB_PREFERENCES_H_
 
 #include <map>
 #include <string>
@@ -12,18 +12,18 @@
 #include "base/strings/string16.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
-#include "content/common/content_export.h"
 #include "net/nqe/effective_connection_type.h"
+#include "third_party/blink/public/common/common_export.h"
 #include "third_party/blink/public/common/css/preferred_color_scheme.h"
 #include "third_party/blink/public/mojom/v8_cache_options.mojom.h"
 #include "ui/base/pointer/pointer_device.h"
 #include "url/gurl.h"
 
 namespace blink {
-class WebView;
-}
 
-namespace content {
+class WebView;
+
+namespace web_pref {
 
 // Map of ISO 15924 four-letter script code to font family.  For example,
 // "Arab" to "My Arabic Font".
@@ -61,7 +61,7 @@
 // The ISO 15924 script code for undetermined script aka Common. It's the
 // default used on WebKit's side to get/set a font setting when no script is
 // specified.
-CONTENT_EXPORT extern const char kCommonScript[];
+BLINK_COMMON_EXPORT extern const char kCommonScript[];
 
 // A struct for managing blink's settings.
 //
@@ -69,7 +69,7 @@
 // blink::WebSettings, content/common/view_messages.h,
 // browser/profiles/profile.cc, and
 // content/public/common/common_param_traits_macros.h
-struct CONTENT_EXPORT WebPreferences {
+struct BLINK_COMMON_EXPORT WebPreferences {
   ScriptFontFamilyMap standard_font_family_map;
   ScriptFontFamilyMap fixed_font_family_map;
   ScriptFontFamilyMap serif_font_family_map;
@@ -368,9 +368,14 @@
   // the embedder to use the same default value.
   WebPreferences();
   WebPreferences(const WebPreferences& other);
+  WebPreferences(WebPreferences&& other);
   ~WebPreferences();
+  WebPreferences& operator=(const WebPreferences& other);
+  WebPreferences& operator=(WebPreferences&& other);
 };
 
-}  // namespace content
+}  // namespace web_pref
 
-#endif  // CONTENT_PUBLIC_COMMON_WEB_PREFERENCES_H_
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_WEB_PREFERENCES_WEB_PREFERENCES_H_
diff --git a/third_party/blink/public/mojom/BUILD.gn b/third_party/blink/public/mojom/BUILD.gn
index 27ba121e..361bc12 100644
--- a/third_party/blink/public/mojom/BUILD.gn
+++ b/third_party/blink/public/mojom/BUILD.gn
@@ -727,6 +727,7 @@
     "//skia/public/mojom",
     "//third_party/blink/public/mojom/service_worker:storage",
     "//ui/gfx/geometry/mojom",
+    "//ui/gfx/range/mojom",
     "//url/mojom:url_mojom_gurl",
     "//url/mojom:url_mojom_origin",
   ]
diff --git a/third_party/blink/public/mojom/frame/frame.mojom b/third_party/blink/public/mojom/frame/frame.mojom
index 58d7697..11a807f 100644
--- a/third_party/blink/public/mojom/frame/frame.mojom
+++ b/third_party/blink/public/mojom/frame/frame.mojom
@@ -46,6 +46,7 @@
 import "third_party/blink/public/mojom/web_feature/web_feature.mojom";
 import "ui/events/mojom/scroll_granularity.mojom";
 import "ui/gfx/geometry/mojom/geometry.mojom";
+import "ui/gfx/range/mojom/range.mojom";
 import "url/mojom/origin.mojom";
 import "url/mojom/url.mojom";
 
@@ -368,6 +369,13 @@
   FocusedElementChanged(bool is_editable_element,
                         gfx.mojom.Rect bounds_in_frame_widget, blink.mojom.FocusType focus_type);
 
+  // Notification that the text selection has changed.
+  // Note: The second parameter is the character based offset of the
+  // base::string16 text in the document.
+  TextSelectionChanged(mojo_base.mojom.String16 text,
+                       uint32 offset,
+                       gfx.mojom.Range range);
+
   // Show a popup menu using native controls on Mac or Android.
   // The popup menu is hidden when the mojo channel is closed.
   ShowPopupMenu(pending_remote<PopupMenuClient> popup_client,
diff --git a/third_party/blink/public/web/DEPS b/third_party/blink/public/web/DEPS
index 03b81a91..4be0558 100644
--- a/third_party/blink/public/web/DEPS
+++ b/third_party/blink/public/web/DEPS
@@ -47,6 +47,7 @@
     "+ui/base/ime/ime_text_span.h",
     "+ui/events/types",
     "+ui/gfx/geometry",
+    "+ui/gfx/range/range.h",
     # Enforce to use mojom-shared.h in blink/public so that it can compile
     # inside and outside Blink.
     "+third_party/blink/public/platform",
diff --git a/third_party/blink/public/web/web_local_frame.h b/third_party/blink/public/web/web_local_frame.h
index 8611d19..ffe7b0a 100644
--- a/third_party/blink/public/web/web_local_frame.h
+++ b/third_party/blink/public/web/web_local_frame.h
@@ -45,6 +45,7 @@
 #include "third_party/blink/public/web/web_optimization_guide_hints.h"
 #include "ui/accessibility/ax_tree_id.h"
 #include "ui/base/ime/ime_text_span.h"
+#include "ui/gfx/range/range.h"
 #include "v8/include/v8.h"
 
 namespace gfx {
@@ -458,6 +459,10 @@
   virtual WebString SelectionAsText() const = 0;
   virtual WebString SelectionAsMarkup() const = 0;
 
+  virtual void TextSelectionChanged(const WebString& selection_text,
+                                    uint32_t offset,
+                                    const gfx::Range& range) = 0;
+
   // Expands the selection to a word around the caret and returns
   // true. Does nothing and returns false if there is no caret or
   // there is ranged selection.
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_deserializer.cc b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_deserializer.cc
index 75ebacf..ff647c9 100644
--- a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_deserializer.cc
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_deserializer.cc
@@ -425,8 +425,8 @@
       ImageDataStorageFormat storage_format = color_params.GetStorageFormat();
       base::CheckedNumeric<size_t> computed_byte_length = width;
       computed_byte_length *= height;
-      computed_byte_length *= 4;
-      computed_byte_length *= ImageData::StorageFormatDataSize(storage_format);
+      computed_byte_length *=
+          ImageData::StorageFormatBytesPerPixel(storage_format);
       if (!computed_byte_length.IsValid() ||
           computed_byte_length.ValueOrDie() != byte_length)
         return nullptr;
diff --git a/third_party/blink/renderer/core/OWNERS b/third_party/blink/renderer/core/OWNERS
index b7e52613..b3dab08 100644
--- a/third_party/blink/renderer/core/OWNERS
+++ b/third_party/blink/renderer/core/OWNERS
@@ -2,8 +2,6 @@
 # For example, core/rendering/OWNERS for rendering changes.
 
 # Reviewers (comments indicate areas of expertise):
-# alancutter reviews changes in core/animation, core/css and core/style.
-alancutter@chromium.org
 alexis.menard@intel.com
 # bokan reviews changes around viewport behavior, scrolling, cc/blink interaction, co-ordinate spaces and input events
 bokan@chromium.org
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index 6625a1a..01e8b19 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -1228,6 +1228,12 @@
   return Selection().SelectedTextForClipboard();
 }
 
+void LocalFrame::TextSelectionChanged(const WTF::String& selection_text,
+                                      uint32_t offset,
+                                      const gfx::Range& range) const {
+  GetLocalFrameHostRemote().TextSelectionChanged(selection_text, offset, range);
+}
+
 PositionWithAffinity LocalFrame::PositionForPoint(
     const PhysicalOffset& frame_point) {
   HitTestLocation location(frame_point);
@@ -2584,7 +2590,7 @@
   prescient_networking_ = std::move(prescient_networking);
 }
 
-mojom::blink::LocalFrameHost& LocalFrame::GetLocalFrameHostRemote() {
+mojom::blink::LocalFrameHost& LocalFrame::GetLocalFrameHostRemote() const {
   return *local_frame_host_remote_.get();
 }
 
diff --git a/third_party/blink/renderer/core/frame/local_frame.h b/third_party/blink/renderer/core/frame/local_frame.h
index 5e054b25..d83c6af 100644
--- a/third_party/blink/renderer/core/frame/local_frame.h
+++ b/third_party/blink/renderer/core/frame/local_frame.h
@@ -76,6 +76,7 @@
 #if defined(OS_MAC)
 #include "third_party/blink/public/mojom/input/text_input_host.mojom-blink.h"
 #endif
+#include "ui/gfx/range/range.h"
 #include "ui/gfx/transform.h"
 
 namespace base {
@@ -320,6 +321,9 @@
 
   String SelectedText() const;
   String SelectedTextForClipboard() const;
+  void TextSelectionChanged(const WTF::String& selection_text,
+                            uint32_t offset,
+                            const gfx::Range& range) const;
 
   PositionWithAffinityTemplate<EditingAlgorithm<NodeTraversal>>
   PositionForPoint(const PhysicalOffset& frame_point);
@@ -488,7 +492,7 @@
 
   // Returns the frame host ptr. The interface returned is backed by an
   // associated interface with the legacy Chrome IPC channel.
-  mojom::blink::LocalFrameHost& GetLocalFrameHostRemote();
+  mojom::blink::LocalFrameHost& GetLocalFrameHostRemote() const;
 
   // Returns the bfcache controller host ptr. The interface returned is backed
   // by an associated interface with the legacy Chrome IPC channel.
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
index 688cd4bb..aeeb276 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
@@ -1287,6 +1287,12 @@
   return GetFrame()->Selection().SelectedHTMLForClipboard();
 }
 
+void WebLocalFrameImpl::TextSelectionChanged(const WebString& selection_text,
+                                             uint32_t offset,
+                                             const gfx::Range& range) {
+  GetFrame()->TextSelectionChanged(selection_text, offset, range);
+}
+
 bool WebLocalFrameImpl::SelectWordAroundCaret() {
   TRACE_EVENT0("blink", "WebLocalFrameImpl::selectWordAroundCaret");
 
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.h b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
index 0c52efeb..34f3fa3 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.h
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
@@ -205,6 +205,9 @@
   WebRange SelectionRange() const override;
   WebString SelectionAsText() const override;
   WebString SelectionAsMarkup() const override;
+  void TextSelectionChanged(const WebString& selection_text,
+                            uint32_t offset,
+                            const gfx::Range& range) override;
   bool SelectWordAroundCaret() override;
   void SelectRange(const gfx::Point& base, const gfx::Point& extent) override;
   void SelectRange(const WebRange&,
diff --git a/third_party/blink/renderer/core/html/canvas/image_data.cc b/third_party/blink/renderer/core/html/canvas/image_data.cc
index 80e845e9..8778bfc 100644
--- a/third_party/blink/renderer/core/html/canvas/image_data.cc
+++ b/third_party/blink/renderer/core/html/canvas/image_data.cc
@@ -78,10 +78,12 @@
   }
 
   if (param_flags & (kParamWidth | kParamHeight)) {
-    base::CheckedNumeric<unsigned> data_size = 4;
+    base::CheckedNumeric<unsigned> data_size =
+        ImageData::StorageFormatBytesPerPixel(
+            kUint8ClampedArrayStorageFormatName);
     if (color_settings) {
-      data_size *=
-          ImageData::StorageFormatDataSize(color_settings->storageFormat());
+      data_size = ImageData::StorageFormatBytesPerPixel(
+          color_settings->storageFormat());
     }
     data_size *= width;
     data_size *= height;
@@ -373,8 +375,11 @@
     return nullptr;
 
   NotShared<DOMUint8ClampedArray> byte_array =
-      AllocateAndValidateUint8ClampedArray(4 * width * height,
-                                           &exception_state);
+      AllocateAndValidateUint8ClampedArray(
+          ImageData::StorageFormatBytesPerPixel(
+              kUint8ClampedArrayStorageFormat) *
+              width * height,
+          &exception_state);
   return byte_array ? MakeGarbageCollected<ImageData>(IntSize(width, height),
                                                       byte_array)
                     : nullptr;
@@ -467,7 +472,8 @@
 // This function accepts size (0, 0) and always returns the ImageData in
 // "srgb" color space and "uint8" storage format.
 ImageData* ImageData::CreateForTest(const IntSize& size) {
-  base::CheckedNumeric<unsigned> data_size = 4;
+  base::CheckedNumeric<unsigned> data_size =
+      ImageData::StorageFormatBytesPerPixel(kUint8ClampedArrayStorageFormat);
   data_size *= size.Width();
   data_size *= size.Height();
   if (!data_size.IsValid() ||
@@ -517,7 +523,7 @@
                 data_size * buffer_view->TypeSize());
   } else {
     unsigned data_type_size =
-        ImageData::StorageFormatDataSize(color_settings_->storageFormat());
+        ImageData::StorageFormatBytesPerPixel(color_settings_->storageFormat());
     int src_index = (dst_rect.X() + dst_rect.Y() * src_rect.Width()) * 4;
     int dst_index = 0;
     if (flip_y)
@@ -525,11 +531,11 @@
     int src_row_stride = src_rect.Width() * 4;
     int dst_row_stride = flip_y ? -dst_rect.Width() * 4 : dst_rect.Width() * 4;
     for (int i = 0; i < dst_rect.Height(); i++) {
-      std::memcpy(
-          static_cast<char*>(buffer_view->BufferBase()->Data()) +
-              dst_index * data_type_size,
-          static_cast<char*>(BufferBase()->Data()) + src_index * data_type_size,
-          dst_rect.Width() * 4 * data_type_size);
+      std::memcpy(static_cast<char*>(buffer_view->BufferBase()->Data()) +
+                      dst_index / 4 * data_type_size,
+                  static_cast<char*>(BufferBase()->Data()) +
+                      src_index / 4 * data_type_size,
+                  dst_rect.Width() * data_type_size);
       src_index += src_row_stride;
       dst_index += dst_row_stride;
     }
@@ -635,26 +641,27 @@
   return kUint8ClampedArrayStorageFormat;
 }
 
-unsigned ImageData::StorageFormatDataSize(const String& storage_format_name) {
+unsigned ImageData::StorageFormatBytesPerPixel(
+    const String& storage_format_name) {
   if (storage_format_name == kUint8ClampedArrayStorageFormatName)
-    return 1;
-  if (storage_format_name == kUint16ArrayStorageFormatName)
-    return 2;
-  if (storage_format_name == kFloat32ArrayStorageFormatName)
     return 4;
+  if (storage_format_name == kUint16ArrayStorageFormatName)
+    return 8;
+  if (storage_format_name == kFloat32ArrayStorageFormatName)
+    return 16;
   NOTREACHED();
   return 1;
 }
 
-unsigned ImageData::StorageFormatDataSize(
+unsigned ImageData::StorageFormatBytesPerPixel(
     ImageDataStorageFormat storage_format) {
   switch (storage_format) {
     case kUint8ClampedArrayStorageFormat:
-      return 1;
-    case kUint16ArrayStorageFormat:
-      return 2;
-    case kFloat32ArrayStorageFormat:
       return 4;
+    case kUint16ArrayStorageFormat:
+      return 8;
+    case kFloat32ArrayStorageFormat:
+      return 16;
   }
   NOTREACHED();
   return 1;
@@ -802,7 +809,7 @@
   // for every line.
   if (crop_rect) {
     unsigned bytes_per_pixel =
-        ImageData::StorageFormatDataSize(storage_format) * 4;
+        ImageData::StorageFormatBytesPerPixel(storage_format);
     unsigned src_index =
         (crop_rect->X() + crop_rect->Y() * width()) * bytes_per_pixel;
     unsigned dst_index = 0;
diff --git a/third_party/blink/renderer/core/html/canvas/image_data.h b/third_party/blink/renderer/core/html/canvas/image_data.h
index 5a5e0da9..6ffebf5 100644
--- a/third_party/blink/renderer/core/html/canvas/image_data.h
+++ b/third_party/blink/renderer/core/html/canvas/image_data.h
@@ -117,8 +117,8 @@
   static CanvasColorSpace GetCanvasColorSpace(const String&);
   static String CanvasColorSpaceName(CanvasColorSpace);
   static ImageDataStorageFormat GetImageDataStorageFormat(const String&);
-  static unsigned StorageFormatDataSize(const String&);
-  static unsigned StorageFormatDataSize(ImageDataStorageFormat);
+  static unsigned StorageFormatBytesPerPixel(const String&);
+  static unsigned StorageFormatBytesPerPixel(ImageDataStorageFormat);
   static NotShared<DOMArrayBufferView>
   ConvertPixelsFromCanvasPixelFormatToImageDataStorageFormat(
       ArrayBufferContents&,
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_line_truncator.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_line_truncator.cc
index 090b7c1..cd1eb5a 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_line_truncator.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_line_truncator.cc
@@ -245,8 +245,11 @@
 
   const LayoutUnit static_width_left = line[initial_index_left].InlineOffset();
   LayoutUnit static_width_right = LayoutUnit(0);
-  for (wtf_size_t i = initial_index_right + 1; i < line.size(); ++i)
-    static_width_right += line[i].inline_size;
+  if (initial_index_right + 1 < line.size()) {
+    const NGLogicalLineItem& item = line[initial_index_right + 1];
+    static_width_right =
+        line_width - item.InlineOffset() + item.margin_line_left;
+  }
   const LayoutUnit available_width =
       available_width_ - static_width_left - static_width_right;
   if (available_width <= ellipsis_width_)
diff --git a/third_party/blink/renderer/core/loader/ping_loader.cc b/third_party/blink/renderer/core/loader/ping_loader.cc
index c3d32d52..2925061 100644
--- a/third_party/blink/renderer/core/loader/ping_loader.cc
+++ b/third_party/blink/renderer/core/loader/ping_loader.cc
@@ -209,7 +209,7 @@
   params.MutableOptions().initiator_info.name =
       fetch_initiator_type_names::kBeacon;
 
-  frame->Client()->DidDispatchPingLoader(request.Url());
+  frame->Client()->DidDispatchPingLoader(url);
   Resource* resource =
       RawResource::Fetch(params, frame->DomWindow()->Fetcher(), nullptr);
   return resource->GetStatus() != ResourceStatus::kLoadError;
diff --git a/third_party/blink/renderer/core/loader/threadable_loader.cc b/third_party/blink/renderer/core/loader/threadable_loader.cc
index 4e06299..bba7f9b5 100644
--- a/third_party/blink/renderer/core/loader/threadable_loader.cc
+++ b/third_party/blink/renderer/core/loader/threadable_loader.cc
@@ -1038,16 +1038,16 @@
     }
   }
 
+  const mojom::RequestContextType request_context = request.GetRequestContext();
   FetchParameters new_params(std::move(request), resource_loader_options);
   DCHECK(!GetResource());
 
   checker_.WillAddClient();
-  if (request.GetRequestContext() == mojom::RequestContextType::VIDEO ||
-      request.GetRequestContext() == mojom::RequestContextType::AUDIO) {
+  if (request_context == mojom::RequestContextType::VIDEO ||
+      request_context == mojom::RequestContextType::AUDIO) {
     DCHECK(async_);
     RawResource::FetchMedia(new_params, resource_fetcher_, this);
-  } else if (request.GetRequestContext() ==
-             mojom::RequestContextType::MANIFEST) {
+  } else if (request_context == mojom::RequestContextType::MANIFEST) {
     DCHECK(async_);
     RawResource::FetchManifest(new_params, resource_fetcher_, this);
   } else if (async_) {
diff --git a/third_party/blink/renderer/core/testing/fake_local_frame_host.cc b/third_party/blink/renderer/core/testing/fake_local_frame_host.cc
index 8c5acf6d..593fe7f 100644
--- a/third_party/blink/renderer/core/testing/fake_local_frame_host.cc
+++ b/third_party/blink/renderer/core/testing/fake_local_frame_host.cc
@@ -163,6 +163,9 @@
     const gfx::Rect& bounds_in_frame_widget,
     blink::mojom::FocusType focus_type) {}
 
+void FakeLocalFrameHost::TextSelectionChanged(const WTF::String& text,
+                                              uint32_t offset,
+                                              const gfx::Range& range) {}
 void FakeLocalFrameHost::ShowPopupMenu(
     mojo::PendingRemote<mojom::blink::PopupMenuClient> popup_client,
     const gfx::Rect& bounds,
diff --git a/third_party/blink/renderer/core/testing/fake_local_frame_host.h b/third_party/blink/renderer/core/testing/fake_local_frame_host.h
index 63b2395..5613f559 100644
--- a/third_party/blink/renderer/core/testing/fake_local_frame_host.h
+++ b/third_party/blink/renderer/core/testing/fake_local_frame_host.h
@@ -99,6 +99,9 @@
   void FocusedElementChanged(bool is_editable_element,
                              const gfx::Rect& bounds_in_frame_widget,
                              blink::mojom::FocusType focus_type) override;
+  void TextSelectionChanged(const WTF::String& text,
+                            uint32_t offset,
+                            const gfx::Range& range) override;
   void ShowPopupMenu(
       mojo::PendingRemote<mojom::blink::PopupMenuClient> popup_client,
       const gfx::Rect& bounds,
diff --git a/third_party/blink/renderer/core/timing/profiler_group.cc b/third_party/blink/renderer/core/timing/profiler_group.cc
index 1f7b00f..4404ed8 100644
--- a/third_party/blink/renderer/core/timing/profiler_group.cc
+++ b/third_party/blink/renderer/core/timing/profiler_group.cc
@@ -15,6 +15,7 @@
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
@@ -83,11 +84,11 @@
   DCHECK(cpu_profiler_);
 
   String profiler_id = NextProfilerId();
-  v8::CpuProfilingOptions options(
-      v8::kLeafNodeLineNumbers,
-      init_options.hasMaxBufferSize() ? init_options.maxBufferSize()
+  v8::CpuProfilingOptions options(v8::kLeafNodeLineNumbers,
+                                  init_options.hasMaxBufferSize()
+                                      ? init_options.maxBufferSize()
                                       : v8::CpuProfilingOptions::kNoSampleLimit,
-      static_cast<int>(sample_interval_us), script_state->GetContext());
+                                  static_cast<int>(sample_interval_us));
 
   cpu_profiler_->StartProfiling(V8String(isolate_, profiler_id), options);
 
@@ -199,14 +200,19 @@
   DCHECK(cpu_profiler_);
   DCHECK(!profiler->stopped());
   profilers_.erase(profiler);
-  ExecutionContext::From(script_state)
-      ->GetTaskRunner(TaskType::kInternalDefault)
-      ->PostTask(FROM_HERE,
-                 WTF::Bind(&ProfilerGroup::CancelProfilerImpl,
+
+  // Since it's possible for the profiler to get destructed along with its
+  // associated context, dispatch a task to cleanup context-independent isolate
+  // resources (rather than use the context's task runner).
+  ThreadScheduler::Current()->V8TaskRunner()->PostTask(
+      FROM_HERE, WTF::Bind(&ProfilerGroup::CancelProfilerImpl,
                            WrapPersistent(this), profiler->ProfilerId()));
 }
 
 void ProfilerGroup::CancelProfilerImpl(String profiler_id) {
+  if (!cpu_profiler_)
+    return;
+
   v8::HandleScope scope(isolate_);
   v8::Local<v8::String> v8_profiler_id = V8String(isolate_, profiler_id);
   auto* profile = cpu_profiler_->StopProfiling(v8_profiler_id);
diff --git a/third_party/blink/renderer/core/timing/profiler_group_test.cc b/third_party/blink/renderer/core/timing/profiler_group_test.cc
index d3bd881..c0af8fdd 100644
--- a/third_party/blink/renderer/core/timing/profiler_group_test.cc
+++ b/third_party/blink/renderer/core/timing/profiler_group_test.cc
@@ -9,6 +9,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_profiler_init_options.h"
 #include "third_party/blink/renderer/core/timing/profiler.h"
+#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 
 namespace blink {
 
@@ -207,4 +208,29 @@
   profiler->stop(scope.GetScriptState()).Then(function);
 }
 
+// Tests that a leaked profiler doesn't crash when disposed alongside its
+// context.
+TEST(ProfilerGroupTest, LeakProfilerWithContext) {
+  Profiler* profiler;
+  {
+    V8TestingScope scope;
+    ProfilerGroup* profiler_group = ProfilerGroup::From(scope.GetIsolate());
+
+    ProfilerInitOptions* init_options = ProfilerInitOptions::Create();
+    init_options->setSampleInterval(0);
+    init_options->setMaxBufferSize(0);
+    profiler = profiler_group->CreateProfiler(scope.GetScriptState(),
+                                              *init_options, base::TimeTicks(),
+                                              scope.GetExceptionState());
+
+    EXPECT_FALSE(profiler->stopped());
+  }
+
+  // Force a collection of the underlying Profiler and v8::Context, and ensure
+  // a crash doesn't occur.
+  profiler = nullptr;
+  ThreadState::Current()->CollectAllGarbageForTesting();
+  test::RunPendingTasks();
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/media_capabilities/media_capabilities.cc b/third_party/blink/renderer/modules/media_capabilities/media_capabilities.cc
index c254ff71..5d4422f 100644
--- a/third_party/blink/renderer/modules/media_capabilities/media_capabilities.cc
+++ b/third_party/blink/renderer/modules/media_capabilities/media_capabilities.cc
@@ -398,7 +398,7 @@
 void ParseDynamicRangeConfigurations(
     const blink::VideoConfiguration* video_config,
     media::VideoColorSpace* color_space,
-    media::HdrMetadataType* hdr_metadata) {
+    gl::HdrMetadataType* hdr_metadata) {
   DCHECK(color_space);
   DCHECK(hdr_metadata);
 
@@ -410,16 +410,16 @@
     const auto& hdr_metadata_type = video_config->hdrMetadataType();
     // TODO(crbug.com/1092328): Switch by V8HdrMetadataType::Enum.
     if (hdr_metadata_type == kSmpteSt2086HdrMetadataType) {
-      *hdr_metadata = media::HdrMetadataType::kSmpteSt2086;
+      *hdr_metadata = gl::HdrMetadataType::kSmpteSt2086;
     } else if (hdr_metadata_type == kSmpteSt209410HdrMetadataType) {
-      *hdr_metadata = media::HdrMetadataType::kSmpteSt2094_10;
+      *hdr_metadata = gl::HdrMetadataType::kSmpteSt2094_10;
     } else if (hdr_metadata_type == kSmpteSt209440HdrMetadataType) {
-      *hdr_metadata = media::HdrMetadataType::kSmpteSt2094_40;
+      *hdr_metadata = gl::HdrMetadataType::kSmpteSt2094_40;
     } else {
       NOTREACHED();
     }
   } else {
-    *hdr_metadata = media::HdrMetadataType::kNone;
+    *hdr_metadata = gl::HdrMetadataType::kNone;
   }
 
   if (video_config->hasColorGamut()) {
@@ -544,7 +544,7 @@
 bool IsVideoConfigurationSupported(const String& mime_type,
                                    const String& codec,
                                    media::VideoColorSpace video_color_space,
-                                   media::HdrMetadataType hdr_metadata_type) {
+                                   gl::HdrMetadataType hdr_metadata_type) {
   media::VideoCodec video_codec = media::kUnknownVideoCodec;
   media::VideoCodecProfile video_profile;
   uint8_t video_level = 0;
@@ -720,7 +720,7 @@
   DCHECK(message.IsEmpty());
 
   media::VideoColorSpace video_color_space;
-  media::HdrMetadataType hdr_metadata_type = media::HdrMetadataType::kNone;
+  gl::HdrMetadataType hdr_metadata_type = gl::HdrMetadataType::kNone;
   if (config->hasVideo()) {
     ParseDynamicRangeConfigurations(config->video(), &video_color_space,
                                     &hdr_metadata_type);
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream_unittest.cc b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream_unittest.cc
index c6e370c2..844c5bb 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream_unittest.cc
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream_unittest.cc
@@ -225,8 +225,8 @@
     // send to close the other direction.
     EXPECT_CALL(*connection_, SendControlFrame(_));
     EXPECT_CALL(*connection_, OnStreamReset(kStreamId, testing::_));
-    quic::QuicStopSendingFrame stop_sending_frame(quic::kInvalidControlFrameId,
-                                                  kStreamId, 0);
+    quic::QuicStopSendingFrame stop_sending_frame(
+        quic::kInvalidControlFrameId, kStreamId, quic::QUIC_STREAM_NO_ERROR);
     session_->OnStopSendingFrame(stop_sending_frame);
   }
 
diff --git a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
index df3e284..b9951a8 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
@@ -39,6 +39,7 @@
 #include "build/build_config.h"
 #include "cc/layers/texture_layer.h"
 #include "components/viz/common/resources/bitmap_allocation.h"
+#include "components/viz/common/resources/resource_sizes.h"
 #include "components/viz/common/resources/shared_bitmap.h"
 #include "components/viz/common/resources/transferable_resource.h"
 #include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
@@ -74,6 +75,20 @@
 
 bool g_should_fail_drawing_buffer_creation_for_testing = false;
 
+void FlipVertically(base::span<uint8_t> framebuffer,
+                    size_t num_rows,
+                    size_t row_bytes) {
+  DCHECK_EQ(framebuffer.size(), num_rows * row_bytes);
+  std::vector<uint8_t> scanline(row_bytes);
+  for (size_t i = 0; i < num_rows / 2; i++) {
+    uint8_t* row_a = framebuffer.data() + i * row_bytes;
+    uint8_t* row_b = framebuffer.data() + (num_rows - i - 1) * row_bytes;
+    memcpy(scanline.data(), row_b, row_bytes);
+    memcpy(row_b, row_a, row_bytes);
+    memcpy(row_a, scanline.data(), row_bytes);
+  }
+}
+
 }  // namespace
 
 // Increase cache to avoid reallocation on fuchsia, see
@@ -296,8 +311,7 @@
 DrawingBuffer::RegisteredBitmap DrawingBuffer::CreateOrRecycleBitmap(
     cc::SharedBitmapIdRegistrar* bitmap_registrar) {
   // When searching for a hit in SharedBitmap, we don't consider the bitmap
-  // format (RGBA 8888 vs F16). We expect to always have the same bitmap format,
-  // matching the back storage of the drawing buffer.
+  // format (RGBA 8888 vs F16) since the allocated bitmap is always RGBA_8888.
   auto* it = std::remove_if(recycled_bitmaps_.begin(), recycled_bitmaps_.end(),
                             [this](const RegisteredBitmap& registered) {
                               return registered.bitmap->size() !=
@@ -312,20 +326,10 @@
     return recycled;
   }
 
-  viz::SharedBitmapId id = viz::SharedBitmap::GenerateId();
-
-  // TODO(sunnyps): Software compositor only supports RGBA_8888 format, and
-  // AllocateSharedBitmap has a DCHECK for that, but we still allocate an F16
-  // buffer (of twice the size) so that there's no invalid memory access at
-  // runtime in ReadBackFramebuffer in release mode. Fixing this is non-trivial,
-  // so it will be done in a follow-up CL.
-  viz::ResourceFormat format = viz::RGBA_8888;
-  if (use_half_float_storage_)
-    format = viz::RGBA_F16;
-
+  const viz::SharedBitmapId id = viz::SharedBitmap::GenerateId();
+  const viz::ResourceFormat format = viz::RGBA_8888;
   base::MappedReadOnlyRegion shm = viz::bitmap_allocation::AllocateSharedBitmap(
       static_cast<gfx::Size>(size_), format);
-
   auto bitmap = base::MakeRefCounted<cc::CrossThreadSharedBitmap>(
       id, std::move(shm), static_cast<gfx::Size>(size_), format);
   RegisteredBitmap registered = {
@@ -394,8 +398,7 @@
 
   // Read the framebuffer into |bitmap|.
   {
-    unsigned char* pixels =
-        static_cast<unsigned char*>(registered.bitmap->memory());
+    uint8_t* pixels = static_cast<uint8_t*>(registered.bitmap->memory());
     DCHECK(pixels);
     bool need_premultiply = want_alpha_channel_ && !premultiplied_alpha_;
     WebGLImageConversion::AlphaOp op =
@@ -403,21 +406,16 @@
                          : WebGLImageConversion::kAlphaDoNothing;
     state_restorer_->SetFramebufferBindingDirty();
     gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
-    ReadBackFramebuffer(pixels, Size().Width(), Size().Height(), kReadbackSkia,
-                        op);
+
+    // Readback in Skia native byte order (RGBA or BGRA) with kN32_SkColorType.
+    const size_t buffer_size = viz::ResourceSizes::CheckedSizeInBytes<size_t>(
+        static_cast<gfx::Size>(size_), viz::RGBA_8888);
+    ReadBackFramebuffer(base::span<uint8_t>(pixels, buffer_size),
+                        kN32_SkColorType, op);
   }
 
-  // TODO(sunnyps): Software compositor only supports RGBA_8888 format, and
-  // AllocateSharedBitmap has a DCHECK for that, but we still allocate an F16
-  // buffer (of twice the size) so that there's no invalid memory access at
-  // runtime in ReadBackFramebuffer in release mode. Fixing this is non-trivial,
-  // so it will be done in a follow-up CL.
-  viz::ResourceFormat format = viz::RGBA_8888;
-  if (use_half_float_storage_)
-    format = viz::RGBA_F16;
-
   *out_resource = viz::TransferableResource::MakeSoftware(
-      registered.bitmap->id(), static_cast<gfx::Size>(size_), format);
+      registered.bitmap->id(), static_cast<gfx::Size>(size_), viz::RGBA_8888);
   out_resource->color_space = storage_color_space_;
 
   // This holds a ref on the DrawingBuffer that will keep it alive until the
@@ -1436,21 +1434,22 @@
     SourceDrawingBuffer source_buffer) {
   ScopedStateRestorer scoped_state_restorer(this);
 
-  int width = Size().Width();
-  int height = Size().Height();
-
-  base::CheckedNumeric<int> data_size = 4;
-  data_size *= width;
-  data_size *= height;
+  // Readback in native GL byte order (RGBA).
+  SkColorType color_type = kRGBA_8888_SkColorType;
+  base::CheckedNumeric<size_t> row_bytes = 4;
   if (RuntimeEnabledFeatures::CanvasColorManagementEnabled() &&
-      use_half_float_storage_) {
-    data_size *= 2;
+      back_color_buffer_->format == viz::RGBA_F16) {
+    color_type = kRGBA_F16_SkColorType;
+    row_bytes *= 2;
   }
+  row_bytes *= Size().Width();
+
+  base::CheckedNumeric<size_t> num_rows = Size().Height();
+  base::CheckedNumeric<size_t> data_size = num_rows * row_bytes;
   if (!data_size.IsValid())
     return nullptr;
 
-  unsigned byte_length = data_size.ValueOrDie<unsigned>();
-  sk_sp<SkData> dst_buffer = TryAllocateSkData(byte_length);
+  sk_sp<SkData> dst_buffer = TryAllocateSkData(data_size.ValueOrDie());
   if (!dst_buffer)
     return nullptr;
 
@@ -1466,10 +1465,11 @@
     gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
   }
 
-  auto* writable_data = static_cast<uint8_t*>(dst_buffer->writable_data());
-  ReadBackFramebuffer(writable_data, width, height, kReadbackRGBA,
+  auto pixels = base::span<uint8_t>(
+      static_cast<uint8_t*>(dst_buffer->writable_data()), dst_buffer->size());
+  ReadBackFramebuffer(pixels, color_type,
                       WebGLImageConversion::kAlphaDoNothing);
-  FlipVertically(writable_data, width, height);
+  FlipVertically(pixels, num_rows.ValueOrDie(), row_bytes.ValueOrDie());
 
   if (fbo) {
     gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
@@ -1480,10 +1480,8 @@
   return dst_buffer;
 }
 
-void DrawingBuffer::ReadBackFramebuffer(unsigned char* pixels,
-                                        int width,
-                                        int height,
-                                        ReadbackOrder readback_order,
+void DrawingBuffer::ReadBackFramebuffer(base::span<uint8_t> pixels,
+                                        SkColorType color_type,
                                         WebGLImageConversion::AlphaOp op) {
   DCHECK(state_restorer_);
   state_restorer_->SetPixelPackParametersDirty();
@@ -1498,63 +1496,42 @@
   }
 
   GLenum data_type = GL_UNSIGNED_BYTE;
-  if (RuntimeEnabledFeatures::CanvasColorManagementEnabled() &&
-      use_half_float_storage_) {
-    if (webgl_version_ > kWebGL1)
-      data_type = GL_HALF_FLOAT;
-    else
-      data_type = GL_HALF_FLOAT_OES;
-  }
-  gl_->ReadPixels(0, 0, width, height, GL_RGBA, data_type, pixels);
 
-  size_t buffer_size = 4 * width * height;
-  if (data_type != GL_UNSIGNED_BYTE)
-    buffer_size *= 2;
+  base::CheckedNumeric<size_t> expected_data_size = 4;
+  expected_data_size *= Size().Width();
+  expected_data_size *= Size().Height();
+
+  if (RuntimeEnabledFeatures::CanvasColorManagementEnabled() &&
+      color_type == kRGBA_F16_SkColorType) {
+    data_type = (webgl_version_ > kWebGL1) ? GL_HALF_FLOAT : GL_HALF_FLOAT_OES;
+    expected_data_size *= 2;
+  }
+
+  DCHECK_EQ(expected_data_size.ValueOrDie(), pixels.size());
+
+  gl_->ReadPixels(0, 0, Size().Width(), Size().Height(), GL_RGBA, data_type,
+                  pixels.data());
+
   // For half float storage Skia order is RGBA, hence no swizzling is needed.
-  if (readback_order == kReadbackSkia && data_type == GL_UNSIGNED_BYTE) {
-#if (SK_R32_SHIFT == 16) && !SK_B32_SHIFT
+  if (color_type == kBGRA_8888_SkColorType) {
     // Swizzle red and blue channels to match SkBitmap's byte ordering.
     // TODO(kbr): expose GL_BGRA as extension.
-    for (size_t i = 0; i < buffer_size; i += 4) {
+    for (size_t i = 0; i < pixels.size(); i += 4) {
       std::swap(pixels[i], pixels[i + 2]);
     }
-#endif
   }
 
   if (op == WebGLImageConversion::kAlphaDoPremultiply) {
-    auto color_type = kRGBA_8888_SkColorType;
-    if (data_type != GL_UNSIGNED_BYTE)
-      color_type = kRGBA_F16_SkColorType;
-    const auto src =
-        SkImageInfo::Make(width, height, color_type, kUnpremul_SkAlphaType);
-    const auto dst =
-        SkImageInfo::Make(width, height, color_type, kPremul_SkAlphaType);
-    SkPixmap{src, pixels, src.minRowBytes()}.readPixels(
-        SkPixmap{dst, pixels, dst.minRowBytes()});
+    for (size_t i = 0; i < pixels.size(); i += 4) {
+      uint8_t alpha = pixels[i + 3];
+      for (size_t j = 0; j < 3; j++)
+        pixels[i + j] = (pixels[i + j] * alpha + 127) / 255;
+    }
   } else if (op != WebGLImageConversion::kAlphaDoNothing) {
     NOTREACHED();
   }
 }
 
-void DrawingBuffer::FlipVertically(uint8_t* framebuffer,
-                                   int width,
-                                   int height) {
-  unsigned row_bytes = width * 4;
-  if (RuntimeEnabledFeatures::CanvasColorManagementEnabled() &&
-      use_half_float_storage_) {
-    row_bytes *= 2;
-  }
-  Vector<uint8_t> scanline(row_bytes);
-  unsigned count = height / 2;
-  for (unsigned i = 0; i < count; i++) {
-    uint8_t* row_a = framebuffer + i * row_bytes;
-    uint8_t* row_b = framebuffer + (height - i - 1) * row_bytes;
-    memcpy(scanline.data(), row_b, row_bytes);
-    memcpy(row_b, row_a, row_bytes);
-    memcpy(row_a, scanline.data(), row_bytes);
-  }
-}
-
 void DrawingBuffer::ResolveAndPresentSwapChainIfNeeded() {
   if (!contents_changed_)
     return;
diff --git a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h
index 0af7311..d821dda9 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h
+++ b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h
@@ -33,6 +33,7 @@
 
 #include <memory>
 
+#include "base/containers/span.h"
 #include "base/macros.h"
 #include "cc/layers/texture_layer_client.h"
 #include "cc/resources/cross_thread_shared_bitmap.h"
@@ -466,20 +467,12 @@
   // s_currentResourceUsePixels is updated.
   void SetSize(const IntSize&);
 
-  // This is the order of bytes to use when doing a readback.
-  enum ReadbackOrder { kReadbackRGBA, kReadbackSkia };
-
   // Helper function which does a readback from the currently-bound
   // framebuffer into a buffer of a certain size with 4-byte pixels.
-  void ReadBackFramebuffer(unsigned char* pixels,
-                           int width,
-                           int height,
-                           ReadbackOrder,
+  void ReadBackFramebuffer(base::span<uint8_t> pixels,
+                           SkColorType,
                            WebGLImageConversion::AlphaOp);
 
-  // Helper function to flip a bitmap vertically.
-  void FlipVertically(uint8_t* data, int width, int height);
-
   // If RGB emulation is required, then the CHROMIUM image's alpha channel
   // must be immediately cleared after it is bound to a texture. Nothing
   // should be allowed to change the alpha channel after this.
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
index 635ca9e..03da9bd8 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
@@ -440,7 +440,7 @@
           QueueTraits::PrioritisationType::kHighPriorityLocalFrame);
     case TaskType::kInternalContinueScriptLoading:
       return PausableTaskQueueTraits().SetPrioritisationType(
-          QueueTraits::PrioritisationType::kVeryHigh);
+          QueueTraits::PrioritisationType::kInternalScriptContinuation);
     case TaskType::kDatabaseAccess:
       if (base::FeatureList::IsEnabled(kHighPriorityDatabaseTaskType)) {
         return PausableTaskQueueTraits().SetPrioritisationType(
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
index d2c2c71..4602a823 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
@@ -240,7 +240,7 @@
   // space delimited task identifiers. The first letter of each task identifier
   // specifies the prioritisation type:
   // - 'R': Regular (normal priority)
-  // - 'V': Very high
+  // - 'V': Internal Script Continuation (very high priority)
   // - 'B': Best-effort
   // - 'D': Database
   void PostTestTasksForPrioritisationType(Vector<String>* run_order,
@@ -255,7 +255,7 @@
           prioritisation_type = PrioritisationType::kRegular;
           break;
         case 'V':
-          prioritisation_type = PrioritisationType::kVeryHigh;
+          prioritisation_type = PrioritisationType::kInternalScriptContinuation;
           break;
         case 'B':
           prioritisation_type = PrioritisationType::kBestEffort;
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller.cc
index 90906c3c..2beb5af 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller.cc
@@ -103,7 +103,7 @@
           .SetFrameScheduler(frame_scheduler_impl_);
 
   switch (queue_traits.prioritisation_type) {
-    case QueueTraits::PrioritisationType::kVeryHigh:
+    case QueueTraits::PrioritisationType::kInternalScriptContinuation:
       queue_creation_params = queue_creation_params.SetFixedPriority(
         TaskQueue::QueuePriority::kVeryHighPriority);
       break;
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller_unittest.cc
index 66e16e55..155132e 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller_unittest.cc
@@ -324,7 +324,7 @@
     All,
     TaskQueueCreationFromQueueTraitsTest,
     ::testing::Values(
-        QueueTraits::PrioritisationType::kVeryHigh,
+        QueueTraits::PrioritisationType::kInternalScriptContinuation,
         QueueTraits::PrioritisationType::kBestEffort,
         QueueTraits::PrioritisationType::kRegular,
         QueueTraits::PrioritisationType::kLoading,
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h
index 4bfc956d..3f28a23 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h
@@ -111,7 +111,7 @@
 
     // Separate enum class for handling prioritisation decisions in task queues.
     enum class PrioritisationType {
-      kVeryHigh = 0,
+      kInternalScriptContinuation = 0,
       kBestEffort = 1,
       kRegular = 2,
       kLoading = 3,
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index 4ae8b70..28820da 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -224130,6 +224130,10 @@
       "5245a3f2e16bf13967187231db515433217912aa",
       []
      ],
+     "pointerevent_mouse_pointercapture_inactivate_pointer-iframe.html": [
+      "3c88328b004c6f73bee1f83060d89fa0ea04d25d",
+      []
+     ],
      "pointerevent_pointerId_scope-iframe.html": [
       "ab33560b35216ea0976d1c037650122d9336ae39",
       []
@@ -231911,7 +231915,7 @@
       []
      ],
      "commands.json": [
-      "929d2c091457b34f085397a1a525c3ae5e5248fb",
+      "3fb6d222b17ded9f78b02607509d090b0661fb4d",
       []
      ],
      "epochs_update.sh": [
@@ -231934,6 +231938,10 @@
       "2396e9b8aee8a166c3b9321c7e9ec2e8f9b549c0",
       []
      ],
+     "regen_certs.py": [
+      "a449bd8b82b8ecef058e1625ca1339ad5f57adf6",
+      []
+     ],
      "run_tc.py": [
       "3a3b0d8090baf7ab60f364dfd6908b6be7011f8e",
       []
@@ -232283,7 +232291,7 @@
       []
      ],
      "serve.py": [
-      "74a2ebc386009adff5090ab7741f1c54074d56f4",
+      "1c44615358567d6477f3746171bffef3935d9cc3",
       []
      ],
      "test_functional.py": [
@@ -237911,7 +237919,7 @@
       []
      ],
      "browser.py": [
-      "6c1c445de23557953210be0b4595dec7fc6bf26a",
+      "664d984da0403acf03a13406f8263780f9be7ead",
       []
      ],
      "commands.json": [
@@ -238075,7 +238083,7 @@
         []
        ],
        "chrome.py": [
-        "89ca560be5ba0740c8fb6aa5d372ffd5a4c618f2",
+        "880e2bd43eb6bbb4c9b8d052af77537ce3f27e3d",
         []
        ],
        "chrome_android.py": [
@@ -238086,6 +238094,10 @@
         "6db0188e55ebff8d3a40f9a15dc714419edacbeb",
         []
        ],
+       "chrome_spki_certs.py": [
+        "e8971e7acde3ed68bf5671e7e2f812765eb7c6c8",
+        []
+       ],
        "edge.py": [
         "bdb37b3f84c2cdc4010a7a289a53564822a3a34c",
         []
@@ -239684,6 +239696,14 @@
       "assertions.js": [
        "b539513adcab7d84e67d65fcf97453e9bc22de43",
        []
+      ],
+      "constructor.any-expected.txt": [
+       "ae32ce6cca10e6508d7166ba651bacc529fe53cf",
+       []
+      ],
+      "constructor.any.worker-expected.txt": [
+       "ae32ce6cca10e6508d7166ba651bacc529fe53cf",
+       []
       ]
      },
      "proto-from-ctor-realm-expected.txt": [
@@ -239700,7 +239720,7 @@
      ],
      "table": {
       "assertions.js": [
-       "b1aeaf1a65dab748c56c8cdf9c1393f97bfdf0e5",
+       "19cc5c3b92d6fa6748c3831f3629c92b62ed757f",
        []
       ],
       "grow-reftypes.tentative.any-expected.txt": [
@@ -369316,6 +369336,15 @@
       }
      ]
     ],
+    "pointerevent_mouse_pointercapture_inactivate_pointer.html": [
+     "24b65213ecc336f02831a74327fcfd93e25eb4c4",
+     [
+      null,
+      {
+       "testdriver": true
+      }
+     ]
+    ],
     "pointerevent_on_event_handlers.html": [
      "d8cfa7a0f4c482be606e4e98f9a7900a0340a477",
      [
@@ -400048,8 +400077,31 @@
         }
        ]
       ],
+      "constructor-types.tentative.any.js": [
+       "764aac78670c5d0711345434060801b76eeb2291",
+       [
+        null,
+        {
+         "jsshell": true,
+         "script_metadata": [
+          [
+           "global",
+           "jsshell"
+          ],
+          [
+           "script",
+           "/wasm/jsapi/assertions.js"
+          ],
+          [
+           "script",
+           "/wasm/jsapi/memory/assertions.js"
+          ]
+         ]
+        }
+       ]
+      ],
       "constructor.any.js": [
-       "6524c8acc49016e10746d686c860eeba6fd3c16d",
+       "30cc8ef9885fe0507bcea15aa88ac57a2bd4c124",
        [
         null,
         {
@@ -400524,8 +400576,31 @@
       ]
      ],
      "table": {
+      "constructor-types.tentative.any.js": [
+       "09ab18a2d8cec7443eb58eee81f0ef7b86aacb41",
+       [
+        null,
+        {
+         "jsshell": true,
+         "script_metadata": [
+          [
+           "global",
+           "jsshell"
+          ],
+          [
+           "script",
+           "/wasm/jsapi/assertions.js"
+          ],
+          [
+           "script",
+           "/wasm/jsapi/table/assertions.js"
+          ]
+         ]
+        }
+       ]
+      ],
       "constructor.any.js": [
-       "e9e77a132b76a479a2895c18c69b402be71fe3e7",
+       "794976e8266f9ed0ebda0fc49ec9a92ca7c890c4",
        [
         null,
         {
@@ -400538,6 +400613,10 @@
           [
            "script",
            "/wasm/jsapi/assertions.js"
+          ],
+          [
+           "script",
+           "/wasm/jsapi/table/assertions.js"
           ]
          ]
         }
@@ -400553,6 +400632,10 @@
           [
            "script",
            "/wasm/jsapi/assertions.js"
+          ],
+          [
+           "script",
+           "/wasm/jsapi/table/assertions.js"
           ]
          ]
         }
@@ -400568,6 +400651,10 @@
           [
            "script",
            "/wasm/jsapi/assertions.js"
+          ],
+          [
+           "script",
+           "/wasm/jsapi/table/assertions.js"
           ]
          ]
         }
diff --git a/third_party/blink/web_tests/external/wpt/tools/ci/commands.json b/third_party/blink/web_tests/external/wpt/tools/ci/commands.json
index 929d2c09..3fb6d22 100644
--- a/third_party/blink/web_tests/external/wpt/tools/ci/commands.json
+++ b/third_party/blink/web_tests/external/wpt/tools/ci/commands.json
@@ -13,6 +13,13 @@
     "help": "Output a hosts file to stdout",
     "virtualenv": false
   },
+  "regen-certs": {
+    "path": "regen_certs.py",
+    "script": "run",
+    "parser": "get_parser",
+    "help": "Regenerate the WPT certificates",
+    "virtualenv": false
+  },
   "tc-download": {
     "path": "tc/download.py",
     "script": "run",
diff --git a/third_party/blink/web_tests/external/wpt/tools/ci/regen_certs.py b/third_party/blink/web_tests/external/wpt/tools/ci/regen_certs.py
new file mode 100644
index 0000000..a449bd8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/tools/ci/regen_certs.py
@@ -0,0 +1,99 @@
+import argparse
+import base64
+import logging
+import subprocess
+
+
+logger = logging.getLogger(__name__)
+
+
+# TODO(Issue #24180): Regenerate SXG fingerprint too.
+CHROME_SPKI_CERTS_CONTENT = """\
+# This file is automatically generated by 'wpt regen-certs'
+# DO NOT EDIT MANUALLY.
+
+# tools/certs/web-platform.test.pem
+WPT_FINGERPRINT = '{wpt_fingerprint}'
+
+# signed-exchange/resources/127.0.0.1.sxg.pem
+SXG_WPT_FINGERPRINT = '0Rt4mT6SJXojEMHTnKnlJ/hBKMBcI4kteBlhR1eTTdk='
+
+IGNORE_CERTIFICATE_ERRORS_SPKI_LIST = [
+    WPT_FINGERPRINT,
+    SXG_WPT_FINGERPRINT
+]
+"""
+
+
+def get_parser():
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--checkend-seconds", type=int, default=5184000,
+                        help="The number of seconds the certificates must be valid for")
+    parser.add_argument("--force", action="store_true",
+                        help="Regenerate certificates even if not reaching expiry")
+    return parser
+
+
+def check_cert(certificate, checkend_seconds):
+    """Checks whether an x509 certificate will expire within a set period.
+
+    Returns 0 if the certificate will not expire, non-zero otherwise."""
+    cmd = [
+        "openssl", "x509",
+        "-checkend", str(checkend_seconds),
+        "-noout",
+        "-in", certificate
+    ]
+    logger.info("Running '%s'" % " ".join(cmd))
+    return subprocess.call(cmd)
+
+
+def regen_certs():
+    """Regenerate the wpt openssl certificates, by delegating to wptserve."""
+    cmd = [
+        "python", "wpt", "serve",
+        "--config", "tools/certs/config.json",
+        "--exit-after-start",
+    ]
+    logger.info("Running '%s'" % " ".join(cmd))
+    subprocess.check_call(cmd)
+
+
+def regen_chrome_spki():
+    """Regenerate the SPKI fingerprints for Chrome's ignore-cert list.
+
+    Chrome requires us to explicitly list which certificates are ignored by its
+    security-checking, by listing a base64 hash of the public key. This will
+    change every time we replace our certificates, so we store the hashes in a
+    file and regenerate it here.
+    """
+    wpt_spki = calculate_spki("tools/certs/web-platform.test.pem")
+    with open("tools/wptrunner/wptrunner/browsers/chrome_spki_certs.py", "w") as f:
+        f.write(CHROME_SPKI_CERTS_CONTENT.format(wpt_fingerprint=wpt_spki))
+
+
+def calculate_spki(cert_path):
+    """Calculate the SPKI fingerprint for a given x509 certificate."""
+    # We use shell=True as we control the input |cert_path|, and piping
+    # correctly across processes is non-trivial in Python.
+    cmd = ("openssl x509 -noout -pubkey -in {cert_path} | ".format(cert_path=cert_path) +
+           "openssl pkey -pubin -outform der | " +
+           "openssl dgst -sha256 -binary")
+    dgst_output = subprocess.check_output(cmd, shell=True)
+
+    return base64.b64encode(dgst_output).decode('utf-8')
+
+
+def run(**kwargs):
+    logging.basicConfig()
+
+    if kwargs["force"]:
+        logger.info("Force regenerating WPT certificates")
+    checkend_seconds = kwargs["checkend_seconds"]
+    if (kwargs["force"] or
+        check_cert("tools/certs/cacert.pem", checkend_seconds) or
+        check_cert("tools/certs/web-platform.test.pem", checkend_seconds)):
+        regen_certs()
+        regen_chrome_spki()
+    else:
+        logger.info("Certificates are still valid for at least %s seconds, skipping regeneration" % checkend_seconds)
diff --git a/third_party/blink/web_tests/external/wpt/tools/serve/serve.py b/third_party/blink/web_tests/external/wpt/tools/serve/serve.py
index 74a2ebc3..1c446153 100644
--- a/third_party/blink/web_tests/external/wpt/tools/serve/serve.py
+++ b/third_party/blink/web_tests/external/wpt/tools/serve/serve.py
@@ -940,6 +940,7 @@
     parser.add_argument("--no-h2", action="store_false", dest="h2", default=None,
                         help="Disable the HTTP/2.0 server")
     parser.add_argument("--quic-transport", action="store_true", help="Enable QUIC server for WebTransport")
+    parser.add_argument("--exit-after-start", action="store_true", help="Exit after starting servers")
     parser.set_defaults(report=False)
     parser.set_defaults(is_wave=False)
     return parser
@@ -990,7 +991,7 @@
             signal.signal(signal.SIGINT, handle_signal)
 
             while (all(subproc.is_alive() for subproc in iter_procs(servers)) and
-                   not received_signal.is_set()):
+                   not received_signal.is_set() and not kwargs["exit_after_start"]):
                 for subproc in iter_procs(servers):
                     subproc.join(1)
 
diff --git a/third_party/blink/web_tests/external/wpt/tools/wpt/browser.py b/third_party/blink/web_tests/external/wpt/tools/wpt/browser.py
index 6c1c445..664d984d 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wpt/browser.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wpt/browser.py
@@ -649,7 +649,10 @@
             # All other channels share the same path on macOS.
             return "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
         if uname[0] == "Windows":
-            return os.path.expandvars(r"$SYSTEMDRIVE\Program Files (x86)\Google\Chrome\Application\chrome.exe")
+            path = os.path.expandvars(r"$SYSTEMDRIVE\Program Files (x86)\Google\Chrome\Application\chrome.exe")
+            if not os.path.exists(path):
+                path = os.path.expandvars(r"$SYSTEMDRIVE\Program Files\Google\Chrome\Application\chrome.exe")
+            return path
         self.logger.warning("Unable to find the browser binary.")
         return None
 
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/chrome.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/chrome.py
index 89ca560be..880e2bd43 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/chrome.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/chrome.py
@@ -1,3 +1,4 @@
+from . import chrome_spki_certs
 from .base import Browser, ExecutorBrowser, require_arg
 from .base import get_timeout_multiplier   # noqa: F401
 from ..webdriver_server import ChromeDriverServer
@@ -23,25 +24,6 @@
                  "env_options": "env_options",
                  "timeout_multiplier": "get_timeout_multiplier",}
 
-# This is generated using:
-# ```
-# openssl x509 -noout -pubkey -in ./tools/certs/web-platform.test.pem |
-# openssl pkey -pubin -outform der |
-# openssl dgst -sha256 -binary |
-# base64
-# ```
-# TODO(https://github.com/web-platform-tests/wpt/issues/21968):
-# Automate the regeneration of this value.
-WPT_FINGERPRINT = 'VPzsk0tdACJMqhsnPpMDesIkQYZrI2RGR+UlPK4emE4='
-
-# And one for signed-exchange/resources/127.0.0.1.sxg.pem
-SXG_WPT_FINGERPRINT = '0Rt4mT6SJXojEMHTnKnlJ/hBKMBcI4kteBlhR1eTTdk='
-
-IGNORE_CERTIFICATE_ERRORS_SPKI_LIST = [
-    WPT_FINGERPRINT,
-    SXG_WPT_FINGERPRINT
-]
-
 def check_args(**kwargs):
     require_arg(kwargs, "webdriver_binary")
 
@@ -89,7 +71,7 @@
 
     chrome_options["args"].append("--ignore-certificate-errors")
     chrome_options["args"].append("--ignore-certificate-errors-spki-list=%s" %
-                                  ','.join(IGNORE_CERTIFICATE_ERRORS_SPKI_LIST))
+                                  ','.join(chrome_spki_certs.IGNORE_CERTIFICATE_ERRORS_SPKI_LIST))
 
     # Allow audio autoplay without a user gesture.
     chrome_options["args"].append("--autoplay-policy=no-user-gesture-required")
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/chrome_spki_certs.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/chrome_spki_certs.py
new file mode 100644
index 0000000..e8971e7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/chrome_spki_certs.py
@@ -0,0 +1,13 @@
+# This file is automatically generated by 'wpt regen-certs'
+# DO NOT EDIT MANUALLY.
+
+# tools/certs/web-platform.test.pem
+WPT_FINGERPRINT = 'VPzsk0tdACJMqhsnPpMDesIkQYZrI2RGR+UlPK4emE4='
+
+# signed-exchange/resources/127.0.0.1.sxg.pem
+SXG_WPT_FINGERPRINT = '0Rt4mT6SJXojEMHTnKnlJ/hBKMBcI4kteBlhR1eTTdk='
+
+IGNORE_CERTIFICATE_ERRORS_SPKI_LIST = [
+    WPT_FINGERPRINT,
+    SXG_WPT_FINGERPRINT
+]
diff --git a/third_party/blink/web_tests/external/wpt/wasm/jsapi/memory/constructor-types.tentative.any.js b/third_party/blink/web_tests/external/wpt/wasm/jsapi/memory/constructor-types.tentative.any.js
new file mode 100644
index 0000000..764aac7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/wasm/jsapi/memory/constructor-types.tentative.any.js
@@ -0,0 +1,20 @@
+// META: global=jsshell
+// META: script=/wasm/jsapi/assertions.js
+// META: script=/wasm/jsapi/memory/assertions.js
+
+test(() => {
+    const argument = { initial: 5, minimum: 6 };
+    assert_throws_js(TypeError, () => new WebAssembly.Memory(argument));
+}, "Initializing with both initial and minimum");
+
+test(() => {
+    const argument = { minimum: 0 };
+    const memory = new WebAssembly.Memory(argument);
+    assert_Memory(memory, { "size": 0 });
+  }, "Zero minimum");
+
+  test(() => {
+    const argument = { minimum: 4 };
+    const memory = new WebAssembly.Memory(argument);
+    assert_Memory(memory, { "size": 4 });
+  }, "Non-zero minimum");
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/wasm/jsapi/memory/constructor.any-expected.txt b/third_party/blink/web_tests/external/wpt/wasm/jsapi/memory/constructor.any-expected.txt
new file mode 100644
index 0000000..ae32ce6c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/wasm/jsapi/memory/constructor.any-expected.txt
@@ -0,0 +1,27 @@
+This is a testharness.js-based test.
+PASS name
+PASS length
+PASS No arguments
+PASS Calling
+PASS Invalid descriptor argument
+PASS Undefined initial value in descriptor
+PASS Out-of-range initial value in descriptor: NaN
+PASS Out-of-range maximum value in descriptor: NaN
+PASS Out-of-range initial value in descriptor: Infinity
+PASS Out-of-range maximum value in descriptor: Infinity
+PASS Out-of-range initial value in descriptor: -Infinity
+PASS Out-of-range maximum value in descriptor: -Infinity
+PASS Out-of-range initial value in descriptor: -1
+PASS Out-of-range maximum value in descriptor: -1
+PASS Out-of-range initial value in descriptor: 4294967296
+PASS Out-of-range maximum value in descriptor: 4294967296
+PASS Out-of-range initial value in descriptor: 68719476736
+PASS Out-of-range maximum value in descriptor: 68719476736
+PASS Initial value exceeds maximum
+FAIL Proxy descriptor WebAssembly.Memory(): Property 'initial' is required
+PASS Order of evaluation for descriptor
+PASS Zero initial
+PASS Non-zero initial
+PASS Stray argument
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/wasm/jsapi/memory/constructor.any.js b/third_party/blink/web_tests/external/wpt/wasm/jsapi/memory/constructor.any.js
index 6524c8ac..30cc8ef9 100644
--- a/third_party/blink/web_tests/external/wpt/wasm/jsapi/memory/constructor.any.js
+++ b/third_party/blink/web_tests/external/wpt/wasm/jsapi/memory/constructor.any.js
@@ -72,7 +72,16 @@
       assert_unreached(`Should not call [[HasProperty]] with ${x}`);
     },
     get(o, x) {
-      return 0;
+      // Due to the requirement not to supply both minimum and initial, we need to ignore one of them.
+      switch (x) {
+        case "shared":
+          return false;
+        case "minimum":
+        case "maximum":
+          return 0;
+        default:
+          return undefined;
+      }
     },
   });
   new WebAssembly.Memory(proxy);
diff --git a/third_party/blink/web_tests/external/wpt/wasm/jsapi/memory/constructor.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/wasm/jsapi/memory/constructor.any.worker-expected.txt
new file mode 100644
index 0000000..ae32ce6c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/wasm/jsapi/memory/constructor.any.worker-expected.txt
@@ -0,0 +1,27 @@
+This is a testharness.js-based test.
+PASS name
+PASS length
+PASS No arguments
+PASS Calling
+PASS Invalid descriptor argument
+PASS Undefined initial value in descriptor
+PASS Out-of-range initial value in descriptor: NaN
+PASS Out-of-range maximum value in descriptor: NaN
+PASS Out-of-range initial value in descriptor: Infinity
+PASS Out-of-range maximum value in descriptor: Infinity
+PASS Out-of-range initial value in descriptor: -Infinity
+PASS Out-of-range maximum value in descriptor: -Infinity
+PASS Out-of-range initial value in descriptor: -1
+PASS Out-of-range maximum value in descriptor: -1
+PASS Out-of-range initial value in descriptor: 4294967296
+PASS Out-of-range maximum value in descriptor: 4294967296
+PASS Out-of-range initial value in descriptor: 68719476736
+PASS Out-of-range maximum value in descriptor: 68719476736
+PASS Initial value exceeds maximum
+FAIL Proxy descriptor WebAssembly.Memory(): Property 'initial' is required
+PASS Order of evaluation for descriptor
+PASS Zero initial
+PASS Non-zero initial
+PASS Stray argument
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/wasm/jsapi/table/assertions.js b/third_party/blink/web_tests/external/wpt/wasm/jsapi/table/assertions.js
index b1aeaf1..19cc5c3b 100644
--- a/third_party/blink/web_tests/external/wpt/wasm/jsapi/table/assertions.js
+++ b/third_party/blink/web_tests/external/wpt/wasm/jsapi/table/assertions.js
@@ -11,3 +11,14 @@
   assert_throws_js(RangeError, () => table.get(expected.length + 1),
                    `${message}: table.get(${expected.length + 1} of ${expected.length})`);
 }
+
+function assert_Table(actual, expected) {
+  assert_equals(Object.getPrototypeOf(actual), WebAssembly.Table.prototype,
+                "prototype");
+  assert_true(Object.isExtensible(actual), "extensible");
+
+  assert_equals(actual.length, expected.length, "length");
+  for (let i = 0; i < expected.length; ++i) {
+    assert_equals(actual.get(i), null, `actual.get(${i})`);
+  }
+}
diff --git a/third_party/blink/web_tests/external/wpt/wasm/jsapi/table/constructor-types.tentative.any.js b/third_party/blink/web_tests/external/wpt/wasm/jsapi/table/constructor-types.tentative.any.js
new file mode 100644
index 0000000..09ab18a2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/wasm/jsapi/table/constructor-types.tentative.any.js
@@ -0,0 +1,20 @@
+// META: global=jsshell
+// META: script=/wasm/jsapi/assertions.js
+// META: script=/wasm/jsapi/table/assertions.js
+
+test(() => {
+  const argument = { "element": "anyfunc", "initial": 0, "minimum": 0 };
+  assert_throws_js(TypeError, () => WebAssembly.Table(argument));
+}, "Supplying both initial and minimum");
+
+test(() => {
+  const argument = { "element": "anyfunc", "minimum": 0 };
+  const table = new WebAssembly.Table(argument);
+  assert_Table(table, { "length": 0 });
+}, "Basic (zero, minimum)");
+
+test(() => {
+  const argument = { "element": "anyfunc", "minimum": 5 };
+  const table = new WebAssembly.Table(argument);
+  assert_Table(table, { "length": 5 });
+}, "Basic (non-zero, minimum)");
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/wasm/jsapi/table/constructor.any.js b/third_party/blink/web_tests/external/wpt/wasm/jsapi/table/constructor.any.js
index e9e77a13..794976e 100644
--- a/third_party/blink/web_tests/external/wpt/wasm/jsapi/table/constructor.any.js
+++ b/third_party/blink/web_tests/external/wpt/wasm/jsapi/table/constructor.any.js
@@ -1,16 +1,6 @@
 // META: global=window,dedicatedworker,jsshell
 // META: script=/wasm/jsapi/assertions.js
-
-function assert_Table(actual, expected) {
-  assert_equals(Object.getPrototypeOf(actual), WebAssembly.Table.prototype,
-                "prototype");
-  assert_true(Object.isExtensible(actual), "extensible");
-
-  assert_equals(actual.length, expected.length, "length");
-  for (let i = 0; i < expected.length; ++i) {
-    assert_equals(actual.get(i), null, `actual.get(${i})`);
-  }
-}
+// META: script=/wasm/jsapi/table/assertions.js
 
 test(() => {
   assert_function_name(WebAssembly.Table, "Table", "WebAssembly.Table");
diff --git a/third_party/blink/web_tests/fast/forms/file/file-crash-by-pseudo-after-with-padding.html b/third_party/blink/web_tests/fast/forms/file/file-crash-by-pseudo-after-with-padding.html
new file mode 100644
index 0000000..3a51e100
--- /dev/null
+++ b/third_party/blink/web_tests/fast/forms/file/file-crash-by-pseudo-after-with-padding.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<style>
+.c18 {
+  width: 640px;
+}
+.c18:after {
+  content: attr(class);
+  width: 100%;
+  padding-right: 100%;
+}
+</style>
+<input type="file">
+<script>
+test(() => {
+  const fileInput = document.querySelector('input');
+  fileInput.classList.add('c18');
+  fileInput.offsetWidth;
+  // Ok if no crash.
+}, ':after with large padding should not cause a crash.');
+</script>
diff --git a/third_party/blink/web_tests/http/tests/js-self-profiling/iframe-context-filtration.html b/third_party/blink/web_tests/http/tests/js-self-profiling/iframe-context-filtration.html
index 164a9f3f..b4df969 100644
--- a/third_party/blink/web_tests/http/tests/js-self-profiling/iframe-context-filtration.html
+++ b/third_party/blink/web_tests/http/tests/js-self-profiling/iframe-context-filtration.html
@@ -33,22 +33,16 @@
       assert_true(ProfileUtils.containsFrame(trace, { name: 'parentWrapper' }),
                   'function from own (parent) context included in trace');
 
-      assert_false(ProfileUtils.containsFrame(trace, { name: 'childCollectSample' }),
-                   'function from child context excluded in trace');
+      assert_true(ProfileUtils.containsFrame(trace, { name: 'childCollectSample' }),
+                   'function from child context included in trace');
 
       assert_true(ProfileUtils.containsFrame(trace, { name: 'parentCollectSample' }),
                   'sampling wrapper function from own (parent) context included in trace');
 
-      console.log(JSON.stringify(trace));
-      assert_true(ProfileUtils.containsSubstack(trace, [
-        { name: 'parentCollectSample' },
-        { name: 'parentWrapper' },
-      ]), 'intermediate cross-context frames are discarded');
-
-      assert_false(ProfileUtils.containsResource(trace,
+      assert_true(ProfileUtils.containsResource(trace,
         'http://127.0.0.1:8000/js-self-profiling/resources/child-frame.html',
-      ), 'child resources are not included');
-    }, 'functions from child frame are not included in profile created by parent frame');
+      ), 'child resources are included');
+    }, 'functions from child frame are included in profile created by parent frame');
 
     promise_test(async t => {
       const iframe = frames[0];
@@ -62,21 +56,21 @@
 
       const trace = await profiler.stop();
 
-      assert_false(ProfileUtils.containsFrame(trace, { name: 'parentWrapper' }),
-                   'function from parent context excluded from trace');
+      assert_true(ProfileUtils.containsFrame(trace, { name: 'parentWrapper' }),
+                  'function from parent context included in trace');
 
       assert_true(ProfileUtils.containsFrame(trace, { name: 'childCollectSample' }),
                   'function from own (child) context included in trace');
 
-      assert_false(ProfileUtils.containsResource(trace,
+      assert_true(ProfileUtils.containsResource(trace,
         window.location.href,
-      ), 'parent resource is not included');
+      ), 'parent resource is included');
 
       assert_true(ProfileUtils.containsResource(trace,
         iframe.location.href,
       ), 'child resource is included');
 
-    }, 'functions from parent context are not included in profile created by child frame');
+    }, 'functions from parent context are included in profile created by child frame');
   </script>
 </body>
 </html>
diff --git a/third_party/wayland-protocols/BUILD.gn b/third_party/wayland-protocols/BUILD.gn
index e0fdcb18..52b414c29 100644
--- a/third_party/wayland-protocols/BUILD.gn
+++ b/third_party/wayland-protocols/BUILD.gn
@@ -115,3 +115,7 @@
 wayland_protocol("color_space_protocol") {
   sources = [ "unstable/color-space/color-space-unstable-v1.xml" ]
 }
+
+wayland_protocol("xdg_foreign") {
+  sources = [ "src/unstable/xdg-foreign/xdg-foreign-unstable-v1.xml" ]
+}
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index c3648f1..fdbeec4 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -107,7 +107,6 @@
       'android-marshmallow-x86-rel': 'android_release_bot_minimal_symbols_x86_fastbuild_resource_whitelisting_webview_google',
       'android-nougat-arm64-rel': 'android_release_bot_minimal_symbols_arm64_fastbuild_webview_google',
       'android-pie-arm64-rel': 'android_release_bot_minimal_symbols_arm64_webview_google',
-      'android-pie-arm64-wpt-rel-non-cq': 'android_release_bot_minimal_symbols_arm64',
       'android-pie-x86-rel': 'android_release_bot_minimal_symbols_x86_fastbuild_webview_google',
       'android-10-arm64-rel': 'android_release_bot_minimal_symbols_arm64_fastbuild_webview_google',
     },
@@ -115,6 +114,7 @@
     'chromium.android.fyi': {
       'Android WebView P FYI (rel)': 'android_release_bot_minimal_symbols_arm64_webview_google',
       'android-marshmallow-x86-fyi-rel': 'android_release_bot_minimal_symbols_x86_fastbuild_resource_whitelisting_webview_google',
+      'android-pie-arm64-wpt-rel-non-cq': 'android_release_bot_minimal_symbols_arm64',
       'android-weblayer-pie-x86-fyi-rel': 'android_release_bot_minimal_symbols_x86_fastbuild_webview_google',
     },
 
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 061b8eee..494e8a4 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -13091,6 +13091,7 @@
 </action>
 
 <action name="MobileBottomToolbarHomeButton">
+  <obsolete>Removed as of 2020/09/09.</obsolete>
   <owner>mdjones@chromium.org</owner>
   <owner>chrome-android-apps@chromium.org</owner>
   <description>
@@ -13433,6 +13434,13 @@
   </description>
 </action>
 
+<action name="MobileDefaultBrowserViewIntent">
+  <owner>thegreenfrog@chromium.org</owner>
+  <description>
+    Records when an iOS user opens the app from the default browser URL open.
+  </description>
+</action>
+
 <action name="MobileDownloadFileUIInstallGoogleDrive">
   <obsolete>
     Removed in 12/2019 after adds of IOSDownloadFileUIGoogleDrive's histogram.
@@ -13514,6 +13522,13 @@
   <description>Please enter the description of this user action.</description>
 </action>
 
+<action name="MobileFirstPartyViewIntent">
+  <owner>thegreenfrog@chromium.org</owner>
+  <description>
+    Records when an iOS user opens the app from a Google first party app.
+  </description>
+</action>
+
 <action name="MobileFocusedFakeboxOnNtp">
   <obsolete>Deprecated as of 12/2015</obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
@@ -15445,6 +15460,7 @@
 </action>
 
 <action name="MobileTopToolbarHomeButton">
+  <obsolete>Removed as of 2020/09/09.</obsolete>
   <owner>mdjones@chromium.org</owner>
   <owner>chrome-android-apps@chromium.org</owner>
   <description>The user tapped on the top toolbar's home button.</description>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 6685586..4c66ada9 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -65065,6 +65065,12 @@
   <int value="1" label="Failure"/>
 </enum>
 
+<enum name="ShortcutsCreationResult">
+  <summary>Result of creating shortcuts for PWA.</summary>
+  <int value="0" label="Success"/>
+  <int value="1" label="Fail to create shortcut"/>
+</enum>
+
 <enum name="ShortcutsMenuRegistrationWinResult">
   <summary>
     Result of registering app icon shortcuts menu for PWA on Windows
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 6a4c8b2..9433f1e 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -8130,6 +8130,10 @@
 
 <histogram name="Apps.FileHandler.Registration.Mac.Result"
     enum="FileHandlerRegistrationMacResult" expires_after="M90">
+  <obsolete>
+    Removed 08/2020. File handler registration is done through shortcuts
+    creation, replaced by WebApp.Shortcuts.Creation.Result.
+  </obsolete>
   <owner>phillis@chromium.org</owner>
   <owner>cmumford@chromium.org</owner>
   <summary>
@@ -8528,7 +8532,7 @@
 </histogram>
 
 <histogram name="Apps.PreferredApps.FileIOAction"
-    enum="PreferredAppsFileIOAction" expires_after="M87">
+    enum="PreferredAppsFileIOAction" expires_after="M89">
   <owner>dominickn@chromium.org</owner>
   <owner>mxcai@chromium.org</owner>
   <summary>
@@ -8538,7 +8542,7 @@
 </histogram>
 
 <histogram name="Apps.PreferredApps.UpdateAction"
-    enum="PreferredAppsUpdateAction" expires_after="M87">
+    enum="PreferredAppsUpdateAction" expires_after="M89">
   <owner>dominickn@chromium.org</owner>
   <owner>mxcai@chromium.org</owner>
   <summary>
@@ -46576,6 +46580,9 @@
 
 <histogram name="Download.EstimatedTimeSavedWithParallelDownload" units="ms"
     expires_after="2020-10-18">
+  <obsolete>
+    Removed in 09/2020.
+  </obsolete>
   <owner>qinmin@chromium.org</owner>
   <summary>
     Estimated time saved on a download when parallel downloading is enabled. To
@@ -90831,7 +90838,9 @@
 
   <owner>tedchoc@chromium.org</owner>
   <summary>
-    Android: Count of page loads started by intents from external apps.
+    Android: Count of page loads started by intents from external apps. In M87
+    the reporting of this histogram was updated to exclude intents sent from
+    Chrome itself.
   </summary>
 </histogram>
 
@@ -122897,7 +122906,7 @@
 </histogram>
 
 <histogram name="PageLoad.Clients.Ads.Resources.Bytes.Ads2" units="KB"
-    expires_after="2020-09-05">
+    expires_after="2021-09-05">
   <owner>johnidel@chromium.org</owner>
   <owner>jkarlin@chromium.org</owner>
   <summary>
@@ -162797,8 +162806,8 @@
 
 <histogram name="Session.Background.TotalDuration" units="ms"
     expires_after="2020-12-01">
-  <owner>harringtond@chromium.org</owner>
   <owner>asvitkine@chromium.org</owner>
+  <owner>src/base/metrics/OWNERS</owner>
   <summary>
     Sum of this metric yields the total time Chrome was running in the
     background. A single session may be represented by multiple values, so that
@@ -192166,6 +192175,13 @@
   </summary>
 </histogram>
 
+<histogram name="WebApp.Shortcuts.Creation.Result"
+    enum="ShortcutsCreationResult" expires_after="M93">
+  <owner>phillis@chromium.org</owner>
+  <owner>dmurph@chromium.org</owner>
+  <summary>Records the result of shortcut creation for PWA.</summary>
+</histogram>
+
 <histogram name="Webapp.Splashscreen.BackgroundColor" enum="SplashColorStatus"
     expires_after="M77">
   <obsolete>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 08c58db..75af1e9 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -9,8 +9,8 @@
             "remote_path": "perfetto_binaries/trace_processor_shell/mac/6b0afc48ae3d07d8cf6598be674562f9844333ec/trace_processor_shell"
         },
         "linux": {
-            "hash": "bf6138ec431dfb06a665f17080bc65558e488b29",
-            "remote_path": "perfetto_binaries/trace_processor_shell/linux/feb1579dfb25c2daa2169a8838c7c331ea79c23d/trace_processor_shell"
+            "hash": "1c6cfadcbce3044791b69891190e4f1c496debfd",
+            "remote_path": "perfetto_binaries/trace_processor_shell/linux/617d3f67c3d963d2bd8b1f70cc60b660123e8153/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/ui/base/pointer/pointer_device.h b/ui/base/pointer/pointer_device.h
index c50fdde..ef360d2 100644
--- a/ui/base/pointer/pointer_device.h
+++ b/ui/base/pointer/pointer_device.h
@@ -36,7 +36,7 @@
 
 // Bit field values indicating available pointer types. Identical to
 // blink::PointerType enums, enforced by compile-time assertions in
-// content/public/common/web_preferences.cc .
+// third_party/blink/public/common/web_preferences/web_preferences.cc.
 // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.ui.base
 // GENERATED_JAVA_PREFIX_TO_STRIP: POINTER_TYPE_
 enum PointerType {
@@ -49,7 +49,7 @@
 
 // Bit field values indicating available hover types. Identical to
 // blink::HoverType enums, enforced by compile-time assertions in
-// content/public/common/web_preferences.cc .
+// third_party/blink/public/common/web_preferences/web_preferences.cc.
 // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.ui.base
 // GENERATED_JAVA_PREFIX_TO_STRIP: HOVER_TYPE_
 enum HoverType {
diff --git a/ui/file_manager/file_manager/foreground/js/ui/file_metadata_formatter.js b/ui/file_manager/file_manager/foreground/js/ui/file_metadata_formatter.js
index db726b2..8788df4 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/file_metadata_formatter.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/file_metadata_formatter.js
@@ -23,9 +23,9 @@
    */
   setDateTimeFormat(use12hourClock) {
     this.timeFormatter_ = new Intl.DateTimeFormat(
-        [] /* default locale */,
+        navigator.language,
         {hour: 'numeric', minute: 'numeric', hour12: use12hourClock});
-    this.dateFormatter_ = new Intl.DateTimeFormat([] /* default locale */, {
+    this.dateFormatter_ = new Intl.DateTimeFormat(navigator.language, {
       year: 'numeric',
       month: 'short',
       day: 'numeric',
diff --git a/ui/ozone/platform/wayland/BUILD.gn b/ui/ozone/platform/wayland/BUILD.gn
index 64125a9..b453860d 100644
--- a/ui/ozone/platform/wayland/BUILD.gn
+++ b/ui/ozone/platform/wayland/BUILD.gn
@@ -124,6 +124,8 @@
     "host/wayland_window_observer.h",
     "host/wayland_zwp_linux_dmabuf.cc",
     "host/wayland_zwp_linux_dmabuf.h",
+    "host/xdg_foreign_wrapper.cc",
+    "host/xdg_foreign_wrapper.h",
     "host/xdg_popup_wrapper_impl.cc",
     "host/xdg_popup_wrapper_impl.h",
     "host/xdg_surface_wrapper_impl.cc",
@@ -156,6 +158,7 @@
     "//third_party/wayland-protocols:presentation_time_protocol",
     "//third_party/wayland-protocols:text_input_protocol",
     "//third_party/wayland-protocols:wayland_drm_protocol",
+    "//third_party/wayland-protocols:xdg_foreign",
     "//third_party/wayland-protocols:xdg_shell_protocol",
     "//ui/base",
     "//ui/base:buildflags",
diff --git a/ui/ozone/platform/wayland/common/wayland_object.cc b/ui/ozone/platform/wayland/common/wayland_object.cc
index 83428b6..7b16b2b 100644
--- a/ui/ozone/platform/wayland/common/wayland_object.cc
+++ b/ui/ozone/platform/wayland/common/wayland_object.cc
@@ -12,6 +12,7 @@
 #include <presentation-time-client-protocol.h>
 #include <text-input-unstable-v1-client-protocol.h>
 #include <wayland-drm-client-protocol.h>
+#include <xdg-foreign-unstable-v1-client-protocol.h>
 #include <xdg-shell-client-protocol.h>
 #include <xdg-shell-unstable-v6-client-protocol.h>
 
@@ -269,4 +270,14 @@
 void (*ObjectTraits<zwp_text_input_v1>::deleter)(zwp_text_input_v1*) =
     &zwp_text_input_v1_destroy;
 
+const wl_interface* ObjectTraits<zxdg_exporter_v1>::interface =
+    &zxdg_exporter_v1_interface;
+void (*ObjectTraits<zxdg_exporter_v1>::deleter)(zxdg_exporter_v1*) =
+    &zxdg_exporter_v1_destroy;
+
+const wl_interface* ObjectTraits<zxdg_exported_v1>::interface =
+    &zxdg_exported_v1_interface;
+void (*ObjectTraits<zxdg_exported_v1>::deleter)(zxdg_exported_v1*) =
+    &zxdg_exported_v1_destroy;
+
 }  // namespace wl
diff --git a/ui/ozone/platform/wayland/common/wayland_object.h b/ui/ozone/platform/wayland/common/wayland_object.h
index 110f7317..37ec4e5 100644
--- a/ui/ozone/platform/wayland/common/wayland_object.h
+++ b/ui/ozone/platform/wayland/common/wayland_object.h
@@ -55,6 +55,8 @@
 struct zxdg_positioner_v6;
 struct zwp_text_input_manager_v1;
 struct zwp_text_input_v1;
+struct zxdg_exporter_v1;
+struct zxdg_exported_v1;
 
 namespace wl {
 
@@ -343,6 +345,18 @@
   static void (*deleter)(zwp_text_input_v1*);
 };
 
+template <>
+struct ObjectTraits<zxdg_exporter_v1> {
+  static const wl_interface* interface;
+  static void (*deleter)(zxdg_exporter_v1*);
+};
+
+template <>
+struct ObjectTraits<zxdg_exported_v1> {
+  static const wl_interface* interface;
+  static void (*deleter)(zxdg_exported_v1*);
+};
+
 struct Deleter {
   template <typename T>
   void operator()(T* obj) {
diff --git a/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.cc b/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.cc
index e501784..0a0e471 100644
--- a/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.cc
+++ b/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.cc
@@ -7,9 +7,21 @@
 #include <gdk/gdkwayland.h>
 #include <gtk/gtk.h>
 
+#include <memory>
+
+#include "base/bind.h"
 #include "base/logging.h"
 #include "ui/gfx/native_widget_types.h"
 #include "ui/ozone/platform/wayland/host/wayland_connection.h"
+#include "ui/ozone/platform/wayland/host/wayland_surface.h"
+#include "ui/ozone/platform/wayland/host/wayland_window.h"
+#include "ui/ozone/platform/wayland/host/wayland_window_manager.h"
+#include "ui/ozone/platform/wayland/host/xdg_foreign_wrapper.h"
+#include "ui/platform_window/platform_window_init_properties.h"
+
+#define WEAK_GTK_FN(x) extern "C" __attribute__((weak)) decltype(x) x
+
+WEAK_GTK_FN(gdk_wayland_window_set_transient_for_exported);
 
 namespace ui {
 
@@ -39,12 +51,29 @@
 bool GtkUiDelegateWayland::SetGdkWindowTransientFor(
     GdkWindow* window,
     gfx::AcceleratedWidget parent) {
-  NOTIMPLEMENTED_LOG_ONCE();
-  return false;
+  if (!gdk_wayland_window_set_transient_for_exported) {
+    LOG(WARNING) << "set_transient_for_exported not supported in GTK version "
+                 << GTK_MAJOR_VERSION << '.' << GTK_MINOR_VERSION << '.'
+                 << GTK_MICRO_VERSION;
+    return false;
+  }
+
+  auto* parent_window =
+      connection_->wayland_window_manager()->GetWindow(parent);
+  auto* foreign = connection_->xdg_foreign();
+  if (!parent_window || !foreign)
+    return false;
+
+  DCHECK_EQ(parent_window->type(), PlatformWindowType::kWindow);
+
+  foreign->ExportSurfaceToForeign(
+      parent_window, base::BindOnce(&GtkUiDelegateWayland::OnHandle,
+                                    weak_factory_.GetWeakPtr(), window));
+  return true;
 }
 
 void GtkUiDelegateWayland::ClearTransientFor(gfx::AcceleratedWidget parent) {
-  NOTIMPLEMENTED_LOG_ONCE();
+  // Nothing to do here.
 }
 
 void GtkUiDelegateWayland::ShowGtkWindow(GtkWindow* window) {
@@ -53,4 +82,10 @@
   gtk_window_present(window);
 }
 
+void GtkUiDelegateWayland::OnHandle(GdkWindow* window,
+                                    const std::string& handle) {
+  gdk_wayland_window_set_transient_for_exported(
+      window, const_cast<char*>(handle.c_str()));
+}
+
 }  // namespace ui
diff --git a/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.h b/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.h
index 4f3f606..e5e23ae 100644
--- a/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.h
+++ b/ui/ozone/platform/wayland/host/gtk_ui_delegate_wayland.h
@@ -5,6 +5,9 @@
 #ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_GTK_UI_DELEGATE_WAYLAND_H_
 #define UI_OZONE_PLATFORM_WAYLAND_HOST_GTK_UI_DELEGATE_WAYLAND_H_
 
+#include <string>
+
+#include "base/memory/weak_ptr.h"
 #include "ui/gfx/native_widget_types.h"
 #include "ui/gtk/gtk_ui_delegate.h"
 
@@ -29,7 +32,13 @@
   void ShowGtkWindow(GtkWindow* window) override;
 
  private:
+  // Called when xdg-foreign exports a parent window passed in
+  // SetGdkWindowTransientFor.
+  void OnHandle(GdkWindow* window, const std::string& handle);
+
   WaylandConnection* const connection_;
+
+  base::WeakPtrFactory<GtkUiDelegateWayland> weak_factory_{this};
 };
 
 }  // namespace ui
diff --git a/ui/ozone/platform/wayland/host/wayland_connection.cc b/ui/ozone/platform/wayland/host/wayland_connection.cc
index 0a980d1..db6bee5 100644
--- a/ui/ozone/platform/wayland/host/wayland_connection.cc
+++ b/ui/ozone/platform/wayland/host/wayland_connection.cc
@@ -38,6 +38,7 @@
 #include "ui/ozone/platform/wayland/host/wayland_window.h"
 #include "ui/ozone/platform/wayland/host/wayland_window_drag_controller.h"
 #include "ui/ozone/platform/wayland/host/wayland_zwp_linux_dmabuf.h"
+#include "ui/ozone/platform/wayland/host/xdg_foreign_wrapper.h"
 
 namespace ui {
 
@@ -367,6 +368,10 @@
       LOG(ERROR) << "Failed to bind to zwp_text_input_manager_v1 global";
       return;
     }
+  } else if (!connection->xdg_foreign_ &&
+             strcmp(interface, "zxdg_exporter_v1") == 0) {
+    connection->xdg_foreign_ = std::make_unique<XdgForeignWrapper>(
+        connection, wl::Bind<zxdg_exporter_v1>(registry, name, version));
   } else if (!connection->drm_ && (strcmp(interface, "wl_drm") == 0) &&
              version >= kMinWlDrmVersion) {
     auto wayland_drm = wl::Bind<struct wl_drm>(registry, name, version);
diff --git a/ui/ozone/platform/wayland/host/wayland_connection.h b/ui/ozone/platform/wayland/host/wayland_connection.h
index af6c16a..b4412cf 100644
--- a/ui/ozone/platform/wayland/host/wayland_connection.h
+++ b/ui/ozone/platform/wayland/host/wayland_connection.h
@@ -36,6 +36,7 @@
 class WaylandCursorPosition;
 class WaylandWindowDragController;
 class GtkPrimarySelectionDeviceManager;
+class XdgForeignWrapper;
 
 class WaylandConnection {
  public:
@@ -133,6 +134,8 @@
     return window_drag_controller_.get();
   }
 
+  XdgForeignWrapper* xdg_foreign() const { return xdg_foreign_.get(); }
+
   // Returns true when dragging is entered or started.
   bool IsDragInProgress() const;
 
@@ -202,6 +205,7 @@
   std::unique_ptr<WaylandDrm> drm_;
   std::unique_ptr<WaylandShm> shm_;
   std::unique_ptr<WaylandBufferManagerHost> buffer_manager_host_;
+  std::unique_ptr<XdgForeignWrapper> xdg_foreign_;
 
   std::unique_ptr<GtkPrimarySelectionDeviceManager>
       primary_selection_device_manager_;
diff --git a/ui/ozone/platform/wayland/host/xdg_foreign_wrapper.cc b/ui/ozone/platform/wayland/host/xdg_foreign_wrapper.cc
new file mode 100644
index 0000000..511ad9e4
--- /dev/null
+++ b/ui/ozone/platform/wayland/host/xdg_foreign_wrapper.cc
@@ -0,0 +1,128 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/wayland/host/xdg_foreign_wrapper.h"
+
+#include <xdg-foreign-unstable-v1-client-protocol.h>
+
+#include "ui/ozone/platform/wayland/host/wayland_connection.h"
+#include "ui/ozone/platform/wayland/host/wayland_window.h"
+#include "ui/platform_window/platform_window_init_properties.h"
+
+namespace ui {
+
+struct XdgForeignWrapper::ExportedSurface {
+  ExportedSurface(wl_surface* surface, OnHandleExported cb);
+  ExportedSurface(ExportedSurface&& buffer);
+  ExportedSurface& operator=(ExportedSurface&& buffer);
+  ~ExportedSurface();
+
+  // Surface that is exported.
+  wl_surface* surface_for_export = nullptr;
+
+  // Exported |surface|.
+  wl::Object<zxdg_exported_v1> exported;
+
+  // Handle of the exported |surface|.
+  std::string exported_handle;
+
+  // The cb that will be executed when |handle| is exported.
+  std::vector<OnHandleExported> callbacks;
+};
+
+XdgForeignWrapper::ExportedSurface::ExportedSurface(wl_surface* surface,
+                                                    OnHandleExported cb)
+    : surface_for_export(surface) {
+  callbacks.emplace_back((std::move(cb)));
+}
+
+XdgForeignWrapper::ExportedSurface::ExportedSurface(ExportedSurface&& buffer) =
+    default;
+
+XdgForeignWrapper::ExportedSurface&
+XdgForeignWrapper::ExportedSurface::operator=(ExportedSurface&& buffer) =
+    default;
+
+XdgForeignWrapper::ExportedSurface::~ExportedSurface() = default;
+
+XdgForeignWrapper::XdgForeignWrapper(WaylandConnection* connection,
+                                     wl::Object<zxdg_exporter_v1> exporter_v1)
+    : connection_(connection), exporter_v1_(std::move(exporter_v1)) {}
+
+XdgForeignWrapper::~XdgForeignWrapper() = default;
+
+void XdgForeignWrapper::ExportSurfaceToForeign(WaylandWindow* window,
+                                               OnHandleExported cb) {
+  DCHECK_EQ(window->type(), PlatformWindowType::kWindow);
+  auto* surface = window->root_surface()->surface();
+  auto* exported_surface = GetExportedSurface(surface);
+  if (!exported_surface) {
+    // The |surface| has never been exported. Export it and return the handle
+    // via the |cb|.
+    ExportSurfaceInternal(surface, std::move(cb));
+  } else if (exported_surface->exported_handle.empty()) {
+    // The |surface| has already been exported, but its handle hasn't been
+    // received yet. Store the |cb| and execute when the handle is obtained.
+    exported_surface->callbacks.emplace_back(std::move(cb));
+  } else {
+    // The |surface| has already been exported and its handle has been received.
+    // Execute the |cb| and send the handle.
+    DCHECK(!exported_surface->exported_handle.empty());
+    std::move(cb).Run(exported_surface->exported_handle);
+  }
+}
+
+XdgForeignWrapper::ExportedSurface* XdgForeignWrapper::GetExportedSurface(
+    wl_surface* surface) {
+  for (auto& item : exported_surfaces_) {
+    if (item.surface_for_export == surface)
+      return &item;
+  }
+  return nullptr;
+}
+
+void XdgForeignWrapper::ExportSurfaceInternal(wl_surface* surface,
+                                              OnHandleExported cb) {
+  static const struct zxdg_exported_v1_listener kExportedListener = {
+      &XdgForeignWrapper::OnExported};
+
+  ExportedSurface exported_surface(surface, std::move(cb));
+  exported_surface.exported.reset(
+      zxdg_exporter_v1_export(exporter_v1_.get(), surface));
+  zxdg_exported_v1_add_listener(exported_surface.exported.get(),
+                                &kExportedListener, this);
+  exported_surfaces_.emplace_back(std::move(exported_surface));
+  connection_->ScheduleFlush();
+}
+
+void XdgForeignWrapper::OnWindowRemoved(WaylandWindow* window) {
+  auto it = std::find_if(exported_surfaces_.begin(), exported_surfaces_.end(),
+                         [window](const auto& surface) {
+                           return window->root_surface()->surface() ==
+                                  surface.surface_for_export;
+                         });
+  if (it != exported_surfaces_.end())
+    exported_surfaces_.erase(it);
+}
+
+// static
+void XdgForeignWrapper::OnExported(void* data,
+                                   zxdg_exported_v1* exported,
+                                   const char* handle) {
+  auto* self = static_cast<XdgForeignWrapper*>(data);
+  DCHECK(self);
+
+  auto exported_surface_it = std::find_if(
+      self->exported_surfaces_.begin(), self->exported_surfaces_.end(),
+      [exported](const auto& item) { return item.exported.get() == exported; });
+  DCHECK(exported_surface_it != self->exported_surfaces_.end());
+  exported_surface_it->exported_handle = handle;
+
+  for (auto& cb : exported_surface_it->callbacks)
+    std::move(cb).Run(exported_surface_it->exported_handle);
+
+  exported_surface_it->callbacks.clear();
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/wayland/host/xdg_foreign_wrapper.h b/ui/ozone/platform/wayland/host/xdg_foreign_wrapper.h
new file mode 100644
index 0000000..50bb4f97
--- /dev/null
+++ b/ui/ozone/platform/wayland/host/xdg_foreign_wrapper.h
@@ -0,0 +1,63 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_XDG_FOREIGN_WRAPPER_H_
+#define UI_OZONE_PLATFORM_WAYLAND_HOST_XDG_FOREIGN_WRAPPER_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "ui/ozone/platform/wayland/common/wayland_object.h"
+#include "ui/ozone/platform/wayland/host/wayland_connection.h"
+#include "ui/ozone/platform/wayland/host/wayland_window_observer.h"
+
+namespace ui {
+
+class WaylandConnection;
+
+// A wrapper for xdg foreign objects. Exports surfaces that have xdg_surface
+// roles and asynchronously returns handles for them. Only xdg_surface surfaces
+// may be exported. Currently supports only exporting surfaces.
+//
+// TODO(1126817): consider supporting xdg-foreign-v2.
+class XdgForeignWrapper : public WaylandWindowObserver {
+ public:
+  using OnHandleExported = base::OnceCallback<void(const std::string&)>;
+
+  XdgForeignWrapper(WaylandConnection* connection,
+                    wl::Object<zxdg_exporter_v1> exporter_v1);
+  XdgForeignWrapper(const XdgForeignWrapper&) = delete;
+  XdgForeignWrapper& operator=(const XdgForeignWrapper&) = delete;
+  ~XdgForeignWrapper() override;
+
+  // Exports |window|'s wl_surface and asynchronously returns a handle for that
+  // via the |cb|. Please note that wl_surface that has xdg_surface role can be
+  // exported.
+  void ExportSurfaceToForeign(WaylandWindow* window, OnHandleExported cb);
+
+ private:
+  struct ExportedSurface;
+
+  ExportedSurface* GetExportedSurface(wl_surface* surface);
+
+  void ExportSurfaceInternal(wl_surface* surface, OnHandleExported cb);
+
+  // WaylandWindowObserver:
+  void OnWindowRemoved(WaylandWindow* window) override;
+
+  // zxdg_exported_v1_listener:
+  static void OnExported(void* data,
+                         zxdg_exported_v1* exported,
+                         const char* handle);
+
+  WaylandConnection* const connection_;
+
+  wl::Object<zxdg_exporter_v1> exporter_v1_;
+
+  std::vector<ExportedSurface> exported_surfaces_;
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_WAYLAND_HOST_XDG_FOREIGN_WRAPPER_H_
diff --git a/weblayer/browser/browser_impl.cc b/weblayer/browser/browser_impl.cc
index 04c775fb..40f1a92 100644
--- a/weblayer/browser/browser_impl.cc
+++ b/weblayer/browser/browser_impl.cc
@@ -13,7 +13,7 @@
 #include "base/stl_util.h"
 #include "components/base32/base32.h"
 #include "content/public/browser/web_contents.h"
-#include "content/public/common/web_preferences.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "weblayer/browser/browser_context_impl.h"
 #include "weblayer/browser/browser_list.h"
 #include "weblayer/browser/feature_list_creator.h"
@@ -258,7 +258,7 @@
   return PersistMinimalState(this, max_size_in_bytes);
 }
 
-void BrowserImpl::SetWebPreferences(content::WebPreferences* prefs) {
+void BrowserImpl::SetWebPreferences(blink::web_pref::WebPreferences* prefs) {
 #if defined(OS_ANDROID)
   prefs->password_echo_enabled = Java_BrowserImpl_getPasswordEchoEnabled(
       AttachCurrentThread(), java_impl_);
diff --git a/weblayer/browser/browser_impl.h b/weblayer/browser/browser_impl.h
index 02363c1..79f92f6 100644
--- a/weblayer/browser/browser_impl.h
+++ b/weblayer/browser/browser_impl.h
@@ -21,9 +21,14 @@
 class FilePath;
 }
 
+namespace blink {
+namespace web_pref {
+struct WebPreferences;
+}
+}  // namespace blink
+
 namespace content {
 class WebContents;
-struct WebPreferences;
 }
 
 namespace weblayer {
@@ -95,7 +100,7 @@
   }
 
   bool GetPasswordEchoEnabled();
-  void SetWebPreferences(content::WebPreferences* prefs);
+  void SetWebPreferences(blink::web_pref::WebPreferences* prefs);
 
 #if defined(OS_ANDROID)
   // On Android the Java Tab class owns the C++ Tab. DestroyTab() calls to the
diff --git a/weblayer/browser/content_browser_client_impl.cc b/weblayer/browser/content_browser_client_impl.cc
index 844a6f0..4878761 100644
--- a/weblayer/browser/content_browser_client_impl.cc
+++ b/weblayer/browser/content_browser_client_impl.cc
@@ -55,7 +55,6 @@
 #include "content/public/common/content_switches.h"
 #include "content/public/common/service_names.mojom.h"
 #include "content/public/common/url_constants.h"
-#include "content/public/common/web_preferences.h"
 #include "content/public/common/window_container_type.mojom.h"
 #include "mojo/public/cpp/bindings/binder_map.h"
 #include "net/proxy_resolution/proxy_config.h"
@@ -67,6 +66,7 @@
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/loader/url_loader_throttle.h"
 #include "third_party/blink/public/common/user_agent/user_agent_metadata.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/page_transition_types.h"
 #include "url/gurl.h"
@@ -328,7 +328,7 @@
 
 void ContentBrowserClientImpl::OverrideWebkitPrefs(
     content::RenderViewHost* render_view_host,
-    content::WebPreferences* prefs) {
+    blink::web_pref::WebPreferences* prefs) {
   prefs->default_encoding = l10n_util::GetStringUTF8(IDS_DEFAULT_ENCODING);
 
   content::WebContents* web_contents =
diff --git a/weblayer/browser/content_browser_client_impl.h b/weblayer/browser/content_browser_client_impl.h
index 2703b378..967ff92 100644
--- a/weblayer/browser/content_browser_client_impl.h
+++ b/weblayer/browser/content_browser_client_impl.h
@@ -45,7 +45,7 @@
   std::string GetUserAgent() override;
   blink::UserAgentMetadata GetUserAgentMetadata() override;
   void OverrideWebkitPrefs(content::RenderViewHost* render_view_host,
-                           content::WebPreferences* prefs) override;
+                           blink::web_pref::WebPreferences* prefs) override;
   void ConfigureNetworkContextParams(
       content::BrowserContext* context,
       bool in_memory,
diff --git a/weblayer/browser/tab_impl.cc b/weblayer/browser/tab_impl.cc
index 7a32183..85189bfe 100644
--- a/weblayer/browser/tab_impl.cc
+++ b/weblayer/browser/tab_impl.cc
@@ -45,7 +45,7 @@
 #include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/renderer_preferences_util.h"
 #include "content/public/browser/web_contents.h"
-#include "content/public/common/web_preferences.h"
+#include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "third_party/blink/public/mojom/renderer_preferences.mojom.h"
 #include "third_party/blink/public/mojom/window_features/window_features.mojom.h"
 #include "ui/base/window_open_disposition.h"
@@ -511,7 +511,7 @@
   web_contents_->OnWebPreferencesChanged();
 }
 
-void TabImpl::SetWebPreferences(content::WebPreferences* prefs) {
+void TabImpl::SetWebPreferences(blink::web_pref::WebPreferences* prefs) {
   prefs->fullscreen_supported = !!fullscreen_delegate_;
 
   if (!browser_)
diff --git a/weblayer/browser/tab_impl.h b/weblayer/browser/tab_impl.h
index 4476276..f529b5b1 100644
--- a/weblayer/browser/tab_impl.h
+++ b/weblayer/browser/tab_impl.h
@@ -34,11 +34,16 @@
 class AutofillProvider;
 }  // namespace autofill
 
+namespace blink {
+namespace web_pref {
+struct WebPreferences;
+}
+}  // namespace blink
+
 namespace content {
 class RenderWidgetHostView;
 class WebContents;
 struct ContextMenuParams;
-struct WebPreferences;
 }
 
 namespace gfx {
@@ -231,7 +236,7 @@
 #endif
 
   void WebPreferencesChanged();
-  void SetWebPreferences(content::WebPreferences* prefs);
+  void SetWebPreferences(blink::web_pref::WebPreferences* prefs);
 
   // Executes |script| with a user gesture.
   void ExecuteScriptWithUserGestureForTests(const base::string16& script);