diff --git a/BUILD.gn b/BUILD.gn
index b2d4f2f..a0153dd 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -387,6 +387,10 @@
     if (enable_chrome_android_internal) {
       deps += [ "//clank" ]
     }
+
+    if (public_android_sdk) {
+      deps += [ "//chrome/android/monochrome:monochrome_apk_checker" ]
+    }
   }
 
   # NOTE: The following should really be 'is_android', but the fuzzing build
diff --git a/DEPS b/DEPS
index ae40dd9..53ba48a9 100644
--- a/DEPS
+++ b/DEPS
@@ -167,11 +167,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '0df7697235b4a02cd6dd6fa2a783345add40cbad',
+  'skia_revision': 'd26e865f413be435a6933140e4e854ac0414b94a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': 'be181e241c6da9baa49a424b7d91613c8ebf76f8',
+  'v8_revision': 'b32cc6513131fb1a4327d7d8a15e533eb20e64b4',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -179,7 +179,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': '0cb09633d68c9b6d922206214b99284c21305fb0',
+  'angle_revision': '68591effc086ae99611e5c5f7db46960f99a3bc6',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -298,7 +298,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'shaderc_revision': 'f28d5287c68b526e61e4f3c4b6098e751b4b5b6d',
+  'shaderc_revision': 'c4835b8523b129ed035a8c61e3e0e843600aa308',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -862,7 +862,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '973a36942fb66975502627ad17c25012b1addce6',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'ff30294118086d516a195c906fadf781f5d7d5f7',
       'condition': 'checkout_linux',
   },
 
@@ -1470,7 +1470,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '2701c130839edbeb226735b0775966b6423d9e83',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '0e3485c338ff50a6dbab1bd950690b177ff0f2df',
+    Var('webrtc_git') + '/src.git' + '@' + '3663ed3ad67893746efd86267a936a7295031ab1',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1532,7 +1532,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@6f8624c5117eeb4fd232b4855e8a74f04f4f8469',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@3fc7fb0892055e587930b3b5369b9cb44d9b47b4',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index 69b8f494..47b83d7 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -467,7 +467,6 @@
   deps = [
     ":browser_jni_headers",
     "//android_webview/apk",
-    "//android_webview/apk:webview_license_provider",
     "//android_webview/browser",
     "//android_webview/browser/gfx",
     "//android_webview/common",
diff --git a/android_webview/apk/BUILD.gn b/android_webview/apk/BUILD.gn
index cc44b70..5a028ae 100644
--- a/android_webview/apk/BUILD.gn
+++ b/android_webview/apk/BUILD.gn
@@ -10,25 +10,8 @@
   ]
 }
 
-# Monochrome uses a different copy of LicenseContentProvider.java.
-# This one is used in Trichrome and SystemWebview.
-android_library("webview_license_provider_java") {
-  java_files =
-      [ "java/src/com/android/webview/chromium/LicenseContentProvider.java" ]
-  deps = [
-    ":apk_java",
-    "//base:base_java",
-    "//components/about_ui/android:aboutui_java",
-  ]
-}
-
-# Contains the native dependencies used by LicenseContentProvider via JNI.
-# This should be included in the shared library of any APK that depends on
-# :webview_license_provider_java.
-group("webview_license_provider") {
-  deps = [
-    "//components/about_ui",
-  ]
+# Empty target to avoid breaking Clank.
+java_group("webview_license_provider_java") {
 }
 
 # Contains classes needed by the webview apk, but not used when loading the apk
@@ -36,6 +19,7 @@
 android_library("apk_java") {
   java_files = [
     "java/src/com/android/webview/chromium/LicenseActivity.java",
+    "java/src/com/android/webview/chromium/LicenseContentProvider.java",
     "java/src/com/android/webview/chromium/WebViewApkApplication.java",
   ]
   deps = [
@@ -43,6 +27,7 @@
     "//android_webview:common_commandline_java",
     "//base:base_java",
     "//base:jni_java",
+    "//components/about_ui/android:aboutui_java",
     "//components/embedder_support/android:application_java",
     "//ui/android:ui_java",
   ]
@@ -57,5 +42,8 @@
     ":apk_jni_headers",
     "//android_webview/common",
     "//base",
+
+    # Called via JNI by LicenseContentProvider in :apk_java.
+    "//components/about_ui:about_ui_android",
   ]
 }
diff --git a/android_webview/apk/java/src/com/android/webview/chromium/LicenseContentProvider.java b/android_webview/apk/java/src/com/android/webview/chromium/LicenseContentProvider.java
index 927da45..877bc88 100644
--- a/android_webview/apk/java/src/com/android/webview/chromium/LicenseContentProvider.java
+++ b/android_webview/apk/java/src/com/android/webview/chromium/LicenseContentProvider.java
@@ -31,7 +31,6 @@
         extends ContentProvider implements ContentProvider.PipeDataWriter<String> {
     public static final String LICENSES_URI_SUFFIX = "LicenseContentProvider/webview_licenses";
     public static final String LICENSES_CONTENT_TYPE = "text/html";
-    private static final String TAG = "LicenseCP";
 
     @Override
     public boolean onCreate() {
diff --git a/android_webview/browser/js_java_interaction/js_reply_proxy.cc b/android_webview/browser/js_java_interaction/js_reply_proxy.cc
index ef073b0..40ccb23 100644
--- a/android_webview/browser/js_java_interaction/js_reply_proxy.cc
+++ b/android_webview/browser/js_java_interaction/js_reply_proxy.cc
@@ -10,7 +10,8 @@
 namespace android_webview {
 
 JsReplyProxy::JsReplyProxy(
-    mojo::PendingRemote<mojom::JavaToJsMessaging> java_to_js_messaging)
+    mojo::PendingAssociatedRemote<mojom::JavaToJsMessaging>
+        java_to_js_messaging)
     : java_to_js_messaging_(std::move(java_to_js_messaging)) {
   JNIEnv* env = base::android::AttachCurrentThread();
   java_ref_ = JavaObjectWeakGlobalRef(
diff --git a/android_webview/browser/js_java_interaction/js_reply_proxy.h b/android_webview/browser/js_java_interaction/js_reply_proxy.h
index a321a5e1..e2bc7a2 100644
--- a/android_webview/browser/js_java_interaction/js_reply_proxy.h
+++ b/android_webview/browser/js_java_interaction/js_reply_proxy.h
@@ -8,15 +8,15 @@
 #include "android_webview/common/js_java_interaction/interfaces.mojom.h"
 #include "base/android/jni_weak_ref.h"
 #include "base/android/scoped_java_ref.h"
-#include "mojo/public/cpp/bindings/pending_remote.h"
-#include "mojo/public/cpp/bindings/remote.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
+#include "mojo/public/cpp/bindings/pending_associated_remote.h"
 
 namespace android_webview {
 
 class JsReplyProxy {
  public:
-  explicit JsReplyProxy(
-      mojo::PendingRemote<mojom::JavaToJsMessaging> java_to_js_messaging);
+  explicit JsReplyProxy(mojo::PendingAssociatedRemote<mojom::JavaToJsMessaging>
+                            java_to_js_messaging);
 
   ~JsReplyProxy();
 
@@ -27,7 +27,7 @@
 
  private:
   JavaObjectWeakGlobalRef java_ref_;
-  mojo::Remote<mojom::JavaToJsMessaging> java_to_js_messaging_;
+  mojo::AssociatedRemote<mojom::JavaToJsMessaging> java_to_js_messaging_;
 
   DISALLOW_COPY_AND_ASSIGN(JsReplyProxy);
 };
diff --git a/android_webview/browser/js_java_interaction/js_to_java_messaging.cc b/android_webview/browser/js_java_interaction/js_to_java_messaging.cc
index ad50c178..70b0d21 100644
--- a/android_webview/browser/js_java_interaction/js_to_java_messaging.cc
+++ b/android_webview/browser/js_java_interaction/js_to_java_messaging.cc
@@ -64,7 +64,8 @@
 }
 
 void JsToJavaMessaging::SetJavaToJsMessaging(
-    mojo::PendingRemote<mojom::JavaToJsMessaging> java_to_js_messaging) {
+    mojo::PendingAssociatedRemote<mojom::JavaToJsMessaging>
+        java_to_js_messaging) {
   // A RenderFrame may inject JsToJavaMessaging in the JavaScript context more
   // than once because of reusing of RenderFrame.
   reply_proxy_ =
diff --git a/android_webview/browser/js_java_interaction/js_to_java_messaging.h b/android_webview/browser/js_java_interaction/js_to_java_messaging.h
index 25197e1..6972748 100644
--- a/android_webview/browser/js_java_interaction/js_to_java_messaging.h
+++ b/android_webview/browser/js_java_interaction/js_to_java_messaging.h
@@ -14,6 +14,7 @@
 #include "mojo/public/cpp/bindings/associated_receiver_set.h"
 #include "mojo/public/cpp/bindings/associated_remote.h"
 #include "mojo/public/cpp/bindings/pending_associated_receiver.h"
+#include "mojo/public/cpp/bindings/pending_associated_remote.h"
 #include "mojo/public/cpp/system/message_pipe.h"
 #include "net/proxy_resolution/proxy_bypass_rules.h"
 
@@ -37,8 +38,9 @@
   // mojom::JsToJavaMessaging implementation.
   void PostMessage(const base::string16& message,
                    std::vector<mojo::ScopedMessagePipeHandle> ports) override;
-  void SetJavaToJsMessaging(mojo::PendingRemote<mojom::JavaToJsMessaging>
-                                java_to_js_messaging) override;
+  void SetJavaToJsMessaging(
+      mojo::PendingAssociatedRemote<mojom::JavaToJsMessaging>
+          java_to_js_messaging) override;
 
  private:
   content::RenderFrameHost* render_frame_host_;
diff --git a/android_webview/common/js_java_interaction/interfaces.mojom b/android_webview/common/js_java_interaction/interfaces.mojom
index ec016b3..ee295d5 100644
--- a/android_webview/common/js_java_interaction/interfaces.mojom
+++ b/android_webview/common/js_java_interaction/interfaces.mojom
@@ -28,7 +28,8 @@
 
   // When there is a new JavaToJsMessaging created in renderer, we need to send
   // it to browser, so browser could send message back to Js.
-  SetJavaToJsMessaging(pending_remote<JavaToJsMessaging> java_to_js_messaging);
+  SetJavaToJsMessaging(
+    pending_associated_remote<JavaToJsMessaging> java_to_js_messaging);
 };
 
 // For Java to reply back to injected JavaScript object. Implemented by
diff --git a/android_webview/renderer/js_java_interaction/js_binding.cc b/android_webview/renderer/js_java_interaction/js_binding.cc
index 9a1e1df..06623dc4 100644
--- a/android_webview/renderer/js_java_interaction/js_binding.cc
+++ b/android_webview/renderer/js_java_interaction/js_binding.cc
@@ -76,7 +76,7 @@
       js_java_configurator_->GetJsToJavaMessage(js_object_name_);
   if (js_to_java_messaging) {
     js_to_java_messaging->SetJavaToJsMessaging(
-        receiver_.BindNewPipeAndPassRemote());
+        receiver_.BindNewEndpointAndPassRemote());
   }
 }
 
diff --git a/android_webview/renderer/js_java_interaction/js_binding.h b/android_webview/renderer/js_java_interaction/js_binding.h
index cb0d57c9..9bd7432 100644
--- a/android_webview/renderer/js_java_interaction/js_binding.h
+++ b/android_webview/renderer/js_java_interaction/js_binding.h
@@ -12,8 +12,8 @@
 #include "base/strings/string16.h"
 #include "gin/arguments.h"
 #include "gin/wrappable.h"
+#include "mojo/public/cpp/bindings/associated_receiver.h"
 #include "mojo/public/cpp/bindings/associated_remote.h"
-#include "mojo/public/cpp/bindings/receiver.h"
 
 namespace v8 {
 template <typename T>
@@ -77,7 +77,7 @@
   // JsBinding's life cycle, it is safe to access it.
   JsJavaConfigurator* js_java_configurator_;
 
-  mojo::Receiver<mojom::JavaToJsMessaging> receiver_{this};
+  mojo::AssociatedReceiver<mojom::JavaToJsMessaging> receiver_{this};
 
   DISALLOW_COPY_AND_ASSIGN(JsBinding);
 };
diff --git a/android_webview/system_webview_apk_tmpl.gni b/android_webview/system_webview_apk_tmpl.gni
index 9a5c392..93fe32b 100644
--- a/android_webview/system_webview_apk_tmpl.gni
+++ b/android_webview/system_webview_apk_tmpl.gni
@@ -28,7 +28,6 @@
       "//android_webview:locale_pak_assets",
       "//android_webview:pak_file_assets",
       "//android_webview/apk:apk_java",
-      "//android_webview/apk:webview_license_provider_java",
       "//android_webview/glue",
       "//android_webview/support_library:support_lib_glue_java",
       "//base:base_java",
diff --git a/ash/assistant/assistant_interaction_controller.cc b/ash/assistant/assistant_interaction_controller.cc
index afda81ef..80b97a5b 100644
--- a/ash/assistant/assistant_interaction_controller.cc
+++ b/ash/assistant/assistant_interaction_controller.cc
@@ -407,8 +407,8 @@
     // set the pending query from outside of the interaction lifecycle, the
     // pending query type will always be |kNull| here.
     if (model_.pending_query().type() == AssistantQueryType::kNull) {
-      model_.SetPendingQuery(
-          std::make_unique<AssistantTextQuery>(metadata->query));
+      model_.SetPendingQuery(std::make_unique<AssistantTextQuery>(
+          metadata->query, AssistantQuerySource::kLibAssistantInitiated));
     }
     model_.CommitPendingQuery();
     model_.SetMicState(MicState::kClosed);
@@ -874,7 +874,8 @@
   const std::string& description = proactive_suggestions->description();
   const std::string& search_query = proactive_suggestions->search_query();
 
-  model_.SetPendingQuery(std::make_unique<AssistantTextQuery>(description));
+  model_.SetPendingQuery(std::make_unique<AssistantTextQuery>(
+      description, AssistantQuerySource::kProactiveSuggestions));
 
   OnInteractionStarted(AssistantInteractionMetadata::New(
       AssistantInteractionType::kText, /*query=*/description));
diff --git a/ash/assistant/model/assistant_query.h b/ash/assistant/model/assistant_query.h
index 785929f..de9fca5 100644
--- a/ash/assistant/model/assistant_query.h
+++ b/ash/assistant/model/assistant_query.h
@@ -31,7 +31,9 @@
   kStylus = 3,
   kSuggestionChip = 4,
   kVoiceInput = 5,
-  kMaxValue = kVoiceInput
+  kProactiveSuggestions = 6,
+  kLibAssistantInitiated = 7,
+  kMaxValue = kLibAssistantInitiated
 };
 
 // AssistantQuery --------------------------------------------------------------
@@ -87,9 +89,7 @@
 class COMPONENT_EXPORT(ASSISTANT_MODEL) AssistantTextQuery
     : public AssistantQuery {
  public:
-  AssistantTextQuery(
-      const std::string& text = std::string(),
-      AssistantQuerySource source = AssistantQuerySource::kUnspecified)
+  AssistantTextQuery(const std::string& text, AssistantQuerySource source)
       : AssistantQuery(AssistantQueryType::kText, source), text_(text) {}
 
   ~AssistantTextQuery() override = default;
diff --git a/ash/public/cpp/app_list/app_list_features.cc b/ash/public/cpp/app_list/app_list_features.cc
index e6e8f7d..56fe098 100644
--- a/ash/public/cpp/app_list/app_list_features.cc
+++ b/ash/public/cpp/app_list/app_list_features.cc
@@ -50,9 +50,6 @@
 const base::Feature kEnableAggregatedMlSearchRanking{
     "EnableAggregatedMlSearchRanking", base::FEATURE_DISABLED_BY_DEFAULT};
 
-const base::Feature kEnableCrOSActionRecorder{
-    "EnableCrOSActionRecorder", base::FEATURE_DISABLED_BY_DEFAULT};
-
 bool IsAnswerCardEnabled() {
   // Not using local static variable to allow tests to change this value.
   // Do not show answer card if the embedded Assistant UI is enabled.
@@ -129,10 +126,6 @@
   return base::FeatureList::IsEnabled(kEnableAggregatedMlSearchRanking);
 }
 
-bool IsCrOSActionRecorderEnabled() {
-  return base::FeatureList::IsEnabled(kEnableCrOSActionRecorder);
-}
-
 std::string AnswerServerUrl() {
   const std::string experiment_url =
       base::GetFieldTrialParamValueByFeature(kEnableAnswerCard, "ServerUrl");
@@ -154,11 +147,6 @@
   return std::string("MrfuAppLaunchPredictor");
 }
 
-int GetCrOSActionRecorderType() {
-  return base::GetFieldTrialParamByFeatureAsInt(kEnableCrOSActionRecorder,
-                                                "CrOSActionRecorderType", 0);
-}
-
 bool IsAppListLaunchRecordingEnabled() {
   return base::FeatureList::IsEnabled(kEnableAppListLaunchRecording);
 }
diff --git a/ash/public/cpp/app_list/app_list_features.h b/ash/public/cpp/app_list/app_list_features.h
index 6b32df4..b5154752 100644
--- a/ash/public/cpp/app_list/app_list_features.h
+++ b/ash/public/cpp/app_list/app_list_features.h
@@ -81,10 +81,6 @@
 // non empty queries.
 ASH_PUBLIC_EXPORT extern const base::Feature kEnableAggregatedMlSearchRanking;
 
-// Enables CrOSActionRecorder which records some user actions.
-// Should be only enabled for users that joined the experiment.
-ASH_PUBLIC_EXPORT extern const base::Feature kEnableCrOSActionRecorder;
-
 bool ASH_PUBLIC_EXPORT IsAnswerCardEnabled();
 bool ASH_PUBLIC_EXPORT IsPlayStoreAppSearchEnabled();
 bool ASH_PUBLIC_EXPORT IsAppDataSearchEnabled();
@@ -104,12 +100,10 @@
 bool ASH_PUBLIC_EXPORT IsScalableAppListEnabled();
 bool ASH_PUBLIC_EXPORT IsFuzzyAppSearchEnabled();
 bool ASH_PUBLIC_EXPORT IsAggregatedMlSearchRankingEnabled();
-bool ASH_PUBLIC_EXPORT IsCrOSActionRecorderEnabled();
 
 std::string ASH_PUBLIC_EXPORT AnswerServerUrl();
 std::string ASH_PUBLIC_EXPORT AnswerServerQuerySuffix();
 std::string ASH_PUBLIC_EXPORT AppSearchResultRankerPredictorName();
-int ASH_PUBLIC_EXPORT GetCrOSActionRecorderType();
 
 }  // namespace app_list_features
 
diff --git a/ash/public/cpp/app_list/app_list_switches.cc b/ash/public/cpp/app_list/app_list_switches.cc
index 4e43e93..b2eaa8d 100644
--- a/ash/public/cpp/app_list/app_list_switches.cc
+++ b/ash/public/cpp/app_list/app_list_switches.cc
@@ -21,6 +21,14 @@
 // If set, the app list will be enabled as if enabled from CWS.
 const char kEnableAppList[] = "enable-app-list";
 
+// If set, the CrOSActionRecorder will be enabled which will record some user
+// actions on device.
+const char kEnableCrOSActionRecorder[] = "enable-cros-action-recorder";
+// Log user actions with action name hashed.
+const char kCrOSActionRecorderWithHash[] = "log-with-hash";
+// Log user actions with action name unhashed.
+const char kCrOSActionRecorderWithoutHash[] = "log-without-hash";
+
 bool ShouldNotDismissOnBlur() {
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
       kDisableAppListDismissOnBlur);
diff --git a/ash/public/cpp/app_list/app_list_switches.h b/ash/public/cpp/app_list/app_list_switches.h
index c6eea8a8..3e526e2a 100644
--- a/ash/public/cpp/app_list/app_list_switches.h
+++ b/ash/public/cpp/app_list/app_list_switches.h
@@ -17,6 +17,9 @@
 ASH_PUBLIC_EXPORT extern const char kEnableAppList[];
 ASH_PUBLIC_EXPORT extern const char kEnableDriveSearchInChromeLauncher[];
 ASH_PUBLIC_EXPORT extern const char kDisableDriveSearchInChromeLauncher[];
+ASH_PUBLIC_EXPORT extern const char kEnableCrOSActionRecorder[];
+ASH_PUBLIC_EXPORT extern const char kCrOSActionRecorderWithHash[];
+ASH_PUBLIC_EXPORT extern const char kCrOSActionRecorderWithoutHash[];
 
 bool ASH_PUBLIC_EXPORT IsAppListSyncEnabled();
 
diff --git a/ash/wm/window_cycle_list.cc b/ash/wm/window_cycle_list.cc
index 1092b98e..43a69ca2 100644
--- a/ash/wm/window_cycle_list.cc
+++ b/ash/wm/window_cycle_list.cc
@@ -438,7 +438,6 @@
 
   if (!windows_.empty() && user_did_accept_) {
     auto* target_window = windows_[current_index_];
-    target_window->Show();
     SelectWindow(target_window);
   }
 
@@ -575,12 +574,20 @@
 }
 
 void WindowCycleList::SelectWindow(aura::Window* window) {
+  // If the list has only one window, the window can be selected twice (in
+  // Step() and the destructor). This causes ARC PIP windows to be restored
+  // twice, which leads to a wrong window state.
+  if (window_selected_)
+    return;
+
   window->Show();
   auto* window_state = WindowState::Get(window);
   if (window_util::IsArcPipWindow(window))
     window_state->Restore();
   else
     window_state->Activate();
+
+  window_selected_ = true;
 }
 
 }  // namespace ash
diff --git a/ash/wm/window_cycle_list.h b/ash/wm/window_cycle_list.h
index fb567d2..f1dc94cf 100644
--- a/ash/wm/window_cycle_list.h
+++ b/ash/wm/window_cycle_list.h
@@ -92,6 +92,9 @@
   // interrupting the interaction).
   bool user_did_accept_ = false;
 
+  // True if one of the windows in the list has already been selected.
+  bool window_selected_ = false;
+
   // The top level View for the window cycle UI. May be null if the UI is not
   // showing.
   WindowCycleView* cycle_view_ = nullptr;
diff --git a/base/test/launcher/test_launcher_test_utils.cc b/base/test/launcher/test_launcher_test_utils.cc
index ab3b9e3..5a47f67 100644
--- a/base/test/launcher/test_launcher_test_utils.cc
+++ b/base/test/launcher/test_launcher_test_utils.cc
@@ -8,6 +8,7 @@
 #include "base/json/json_reader.h"
 #include "base/json/json_writer.h"
 #include "base/optional.h"
+#include "base/test/gtest_util.h"
 #include "base/test/launcher/test_result.h"
 
 namespace base {
@@ -24,6 +25,18 @@
   return value ? *value : std::string();
 }
 
+// Find and return test case with name |test_case_name|,
+// return null if missing.
+const testing::TestCase* GetTestCase(const std::string& test_case_name) {
+  testing::UnitTest* const unit_test = testing::UnitTest::GetInstance();
+  for (int i = 0; i < unit_test->total_test_case_count(); ++i) {
+    const testing::TestCase* test_case = unit_test->GetTestCase(i);
+    if (test_case->name() == test_case_name)
+      return test_case;
+  }
+  return nullptr;
+}
+
 }  // namespace
 
 bool ValidateKeyValue(const Value& dict_value,
@@ -85,6 +98,24 @@
   return true;
 }
 
+bool ValidateTestLocations(const Value* test_locations,
+                           const std::string& test_case_name) {
+  const testing::TestCase* test_case = GetTestCase(test_case_name);
+  if (test_case == nullptr) {
+    ADD_FAILURE() << "Could not find test case " << test_case_name;
+    return false;
+  }
+  bool result = true;
+  for (int j = 0; j < test_case->total_test_count(); ++j) {
+    const testing::TestInfo* test_info = test_case->GetTestInfo(j);
+    std::string full_name =
+        FormatFullTestName(test_case->name(), test_info->name());
+    result &= ValidateTestLocation(test_locations, full_name, test_info->file(),
+                                   test_info->line());
+  }
+  return result;
+}
+
 bool ValidateTestLocation(const Value* test_locations,
                           const std::string& test_name,
                           const std::string& file,
diff --git a/base/test/launcher/test_launcher_test_utils.h b/base/test/launcher/test_launcher_test_utils.h
index f6b17d5..33170c5 100644
--- a/base/test/launcher/test_launcher_test_utils.h
+++ b/base/test/launcher/test_launcher_test_utils.h
@@ -37,6 +37,10 @@
                         const std::string& status,
                         size_t result_part_count);
 
+// Validate test_locations contains all tests in |test_case_name|.
+bool ValidateTestLocations(const Value* test_locations,
+                           const std::string& test_case_name);
+
 // Validate test_locations contains the correct file name and line number.
 bool ValidateTestLocation(const Value* test_locations,
                           const std::string& test_name,
diff --git a/base/test/launcher/test_launcher_unittest.cc b/base/test/launcher/test_launcher_unittest.cc
index 72b2916..0f70a643 100644
--- a/base/test/launcher/test_launcher_unittest.cc
+++ b/base/test/launcher/test_launcher_unittest.cc
@@ -701,17 +701,8 @@
   Value* val = root->FindDictKey("test_locations");
   ASSERT_TRUE(val);
   EXPECT_EQ(4u, val->DictSize());
-  // If path or test location changes, the following expectation
-  // will need to change accordingly.
-  std::string file_name = "../../base/test/launcher/test_launcher_unittest.cc";
-  EXPECT_TRUE(test_launcher_utils::ValidateTestLocation(
-      val, "MockUnitTests.DISABLED_PassTest", file_name, 659));
-  EXPECT_TRUE(test_launcher_utils::ValidateTestLocation(
-      val, "MockUnitTests.DISABLED_FailTest", file_name, 663));
-  EXPECT_TRUE(test_launcher_utils::ValidateTestLocation(
-      val, "MockUnitTests.DISABLED_CrashTest", file_name, 667));
-  EXPECT_TRUE(test_launcher_utils::ValidateTestLocation(
-      val, "MockUnitTests.DISABLED_NoRunTest", file_name, 671));
+
+  EXPECT_TRUE(test_launcher_utils::ValidateTestLocations(val, "MockUnitTests"));
 
   val = root->FindListKey("per_iteration_data");
   ASSERT_TRUE(val);
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index b1a8504c..9d82508 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8899236512803402976
\ No newline at end of file
+8899052292598171312
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 7427f4a..6e36654 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-8899240979698604064
\ No newline at end of file
+8899056964225451136
\ No newline at end of file
diff --git a/chrome/VERSION b/chrome/VERSION
index c2f390e..4949a5d7 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=80
 MINOR=0
-BUILD=3946
+BUILD=3947
 PATCH=0
diff --git a/chrome/android/chrome_junit_test_java_sources.gni b/chrome/android/chrome_junit_test_java_sources.gni
index c4fea608..5b45657 100644
--- a/chrome/android/chrome_junit_test_java_sources.gni
+++ b/chrome/android/chrome_junit_test_java_sources.gni
@@ -144,7 +144,7 @@
   "junit/src/org/chromium/chrome/browser/omnibox/AutocompleteEditTextTest.java",
   "junit/src/org/chromium/chrome/browser/omnibox/AutocompleteStateUnitTest.java",
   "junit/src/org/chromium/chrome/browser/omnibox/KeyboardHideHelperUnitTest.java",
-  "junit/src/org/chromium/chrome/browser/omnibox/SearchEngineLogoUtilsTest.java",
+  "junit/src/org/chromium/chrome/browser/omnibox/SearchEngineLogoUtilsUnitTest.java",
   "junit/src/org/chromium/chrome/browser/omnibox/SpannableAutocompleteEditTextModelUnitTest.java",
   "junit/src/org/chromium/chrome/browser/omnibox/UrlBarDataTest.java",
   "junit/src/org/chromium/chrome/browser/omnibox/UrlBarUnitTest.java",
diff --git a/chrome/android/chrome_public_apk_tmpl.gni b/chrome/android/chrome_public_apk_tmpl.gni
index da5fecc..f2cebb1 100644
--- a/chrome/android/chrome_public_apk_tmpl.gni
+++ b/chrome/android/chrome_public_apk_tmpl.gni
@@ -372,7 +372,6 @@
         "//android_webview/apk:apk_java",
         "//android_webview/glue",
         "//chrome/android:monochrome_java",
-        "//chrome/android/monochrome:monochrome_license_provider_java",
         "//third_party/crashpad/crashpad/handler:crashpad_handler_trampoline",
       ]
       loadable_modules += [ "$root_out_dir/libcrashpad_handler_trampoline.so" ]
diff --git a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutTest.java b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutTest.java
index ffd490e..a0308f00 100644
--- a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutTest.java
+++ b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutTest.java
@@ -6,6 +6,7 @@
 
 import static android.support.test.espresso.Espresso.onView;
 import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.matcher.ViewMatchers.withContentDescription;
 import static android.support.test.espresso.matcher.ViewMatchers.withId;
 
 import static org.junit.Assert.assertEquals;
@@ -51,9 +52,11 @@
 import org.chromium.chrome.browser.compositor.layouts.Layout;
 import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabSelectionType;
 import org.chromium.chrome.browser.tasks.tab_management.TabSwitcher;
 import org.chromium.chrome.browser.util.FeatureUtilities;
+import org.chromium.chrome.tab_ui.R;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.chrome.test.util.ApplicationTestUtils;
@@ -95,9 +98,11 @@
     private List<WeakReference<Bitmap>> mAllBitmaps = new LinkedList<>();
     private Callback<Bitmap> mBitmapListener =
             (bitmap) -> mAllBitmaps.add(new WeakReference<>(bitmap));
+    private TabSwitcher.TabListDelegate mTabListDelegate;
 
     @Before
     public void setUp() {
+        // After setUp, Chrome is launched and has one NTP.
         FeatureUtilities.setGridTabSwitcherEnabledForTesting(true);
 
         EmbeddedTestServer testServer =
@@ -110,10 +115,9 @@
         mUrl = testServer.getURL("/chrome/test/data/android/navigate/simple.html");
         mRepeat = 3;
 
-        TabSwitcher.TabListDelegate delegate =
-                mStartSurfaceLayout.getStartSurfaceForTesting().getTabListDelegate();
-        delegate.setBitmapCallbackForTesting(mBitmapListener);
-        Assert.assertEquals(0, delegate.getBitmapFetchCountForTesting());
+        mTabListDelegate = mStartSurfaceLayout.getStartSurfaceForTesting().getTabListDelegate();
+        mTabListDelegate.setBitmapCallbackForTesting(mBitmapListener);
+        Assert.assertEquals(0, mTabListDelegate.getBitmapFetchCountForTesting());
 
         mActivityTestRule.getActivity().getTabContentManager().setCaptureMinRequestTimeForTesting(
                 0);
@@ -132,12 +136,10 @@
     @CommandLineFlags.Add({BASE_PARAMS})
     public void testTabToGridFromLiveTab() throws InterruptedException {
         // clang-format on
-        TabSwitcher.TabListDelegate delegate =
-                mStartSurfaceLayout.getStartSurfaceForTesting().getTabListDelegate();
-        assertEquals(0, delegate.getSoftCleanupDelayForTesting());
-        assertEquals(0, delegate.getCleanupDelayForTesting());
+        assertEquals(0, mTabListDelegate.getSoftCleanupDelayForTesting());
+        assertEquals(0, mTabListDelegate.getCleanupDelayForTesting());
 
-        prepareTabs(2, NTP_URL);
+        prepareTabs(2, 0, NTP_URL);
         testTabToGrid(mUrl);
         assertThumbnailsAreReleased();
     }
@@ -153,7 +155,7 @@
         // clang-format on
         assertTrue(FeatureUtilities.isTabToGtsAnimationEnabled());
 
-        prepareTabs(2, NTP_URL);
+        prepareTabs(2, 0, NTP_URL);
         testTabToGrid(mUrl);
         assertThumbnailsAreReleased();
     }
@@ -165,12 +167,10 @@
     @CommandLineFlags.Add({BASE_PARAMS + "/soft-cleanup-delay/10000/cleanup-delay/10000"})
     public void testTabToGridFromLiveTabWarm() throws InterruptedException {
         // clang-format on
-        TabSwitcher.TabListDelegate delegate =
-                mStartSurfaceLayout.getStartSurfaceForTesting().getTabListDelegate();
-        assertEquals(10000, delegate.getSoftCleanupDelayForTesting());
-        assertEquals(10000, delegate.getCleanupDelayForTesting());
+        assertEquals(10000, mTabListDelegate.getSoftCleanupDelayForTesting());
+        assertEquals(10000, mTabListDelegate.getCleanupDelayForTesting());
 
-        prepareTabs(2, NTP_URL);
+        prepareTabs(2, 0, NTP_URL);
         testTabToGrid(mUrl);
     }
 
@@ -182,7 +182,7 @@
     @MinAndroidSdkLevel(Build.VERSION_CODES.M) // TODO(crbug.com/997065#c8): remove SDK restriction.
     public void testTabToGridFromLiveTabWarmAnimation() throws InterruptedException {
         // clang-format on
-        prepareTabs(2, NTP_URL);
+        prepareTabs(2, 0, NTP_URL);
         testTabToGrid(mUrl);
     }
 
@@ -193,7 +193,7 @@
     @CommandLineFlags.Add({BASE_PARAMS + "/cleanup-delay/10000"})
     public void testTabToGridFromLiveTabSoft() throws InterruptedException {
         // clang-format on
-        prepareTabs(2, NTP_URL);
+        prepareTabs(2, 0, NTP_URL);
         testTabToGrid(mUrl);
     }
 
@@ -205,7 +205,7 @@
     @MinAndroidSdkLevel(Build.VERSION_CODES.M) // TODO(crbug.com/997065#c8): remove SDK restriction.
     public void testTabToGridFromLiveTabSoftAnimation() throws InterruptedException {
         // clang-format on
-        prepareTabs(2, NTP_URL);
+        prepareTabs(2, 0, NTP_URL);
         testTabToGrid(mUrl);
     }
 
@@ -213,41 +213,77 @@
     @MediumTest
     @CommandLineFlags.Add({BASE_PARAMS})
     public void testTabToGridFromNtp() throws InterruptedException {
-        prepareTabs(2, NTP_URL);
+        prepareTabs(2, 0, NTP_URL);
         testTabToGrid(NTP_URL);
         assertThumbnailsAreReleased();
     }
 
     /**
-     * Make Chrome have {@code numTabs} or Tabs with {@code url} loaded.
-     * @see #prepareTabs(int, String, boolean)
+     * Make Chrome have {@code numTabs} of regular Tabs and {@code numIncognitoTabs} of incognito
+     * tabs with {@code url} loaded.
+     *
+     * @param numTabs The number of regular tabs.
+     * @param numIncognitoTabs The number of incognito tabs.
+     * @param url The URL to load.
      */
-    private void prepareTabs(int numTabs, @Nullable String url) {
-        prepareTabs(numTabs, url, true);
+    private void prepareTabs(int numTabs, int numIncognitoTabs, @Nullable String url) {
+        assertTrue(numTabs >= 1);
+        assertTrue(numIncognitoTabs >= 0);
+
+        assertEquals(1,
+                mActivityTestRule.getActivity().getTabModelSelector().getModel(false).getCount());
+        assertEquals(
+                0, mActivityTestRule.getActivity().getTabModelSelector().getModel(true).getCount());
+
+        if (numTabs == 1) {
+            if (url != null) mActivityTestRule.loadUrl(url);
+        } else {
+            // When Chrome started, there is already one Tab created by default.
+            createTabs(numTabs - 1, url, true, false);
+        }
+        if (numIncognitoTabs > 0) createTabs(numIncognitoTabs, url, true, true);
+
+        assertEquals(numTabs,
+                mActivityTestRule.getActivity().getTabModelSelector().getModel(false).getCount());
+        assertEquals(numIncognitoTabs,
+                mActivityTestRule.getActivity().getTabModelSelector().getModel(true).getCount());
     }
 
     /**
-     * Make Chrome have {@code numTabs} or Tabs with {@code url} loaded.
-     * @param numTabs The number of tabs we expect after finishing
+     * When Chrome started, there is already one Tab created by default. This method is used to add
+     * additional {@code numTabs} of {@link Tab}s with {@code url} loaded to Chrome.
+     * @param numTabs The number of tabs to create.
      * @param url The URL to load. Skip loading when null, but the thumbnail for the NTP might not
      *            be saved.
      * @param waitForLoading Whether wait for URL loading.
+     * @param isIncognito Whether the tab is incognito tab.
      */
-    private void prepareTabs(int numTabs, @Nullable String url, boolean waitForLoading) {
+    private void createTabs(
+            int numTabs, @Nullable String url, boolean waitForLoading, boolean isIncognito) {
         assertTrue(numTabs >= 1);
-        assertEquals(1, mActivityTestRule.getActivity().getTabModelSelector().getTotalTabCount());
 
         if (url != null) mActivityTestRule.loadUrl(url);
-        for (int i = 0; i < numTabs - 1; i++) {
-            MenuUtils.invokeCustomMenuActionSync(InstrumentationRegistry.getInstrumentation(),
-                    mActivityTestRule.getActivity(), org.chromium.chrome.R.id.new_tab_menu_id);
+
+        int previousTabCount = mActivityTestRule.getActivity()
+                                       .getTabModelSelector()
+                                       .getModel(isIncognito)
+                                       .getCount();
+
+        for (int i = 0; i < numTabs; i++) {
+            TabModel previousTabModel =
+                    mActivityTestRule.getActivity().getTabModelSelector().getCurrentModel();
+            int previousTabIndex = previousTabModel.index();
+            Tab previousTab = previousTabModel.getTabAt(previousTabIndex);
+
+            ChromeTabUtils.newTabFromMenu(InstrumentationRegistry.getInstrumentation(),
+                    mActivityTestRule.getActivity(), isIncognito, waitForLoading);
+
             if (url != null) mActivityTestRule.loadUrl(url);
             if (!waitForLoading) continue;
 
-            Tab previousTab = mActivityTestRule.getActivity()
-                                      .getTabModelSelector()
-                                      .getCurrentModel()
-                                      .getTabAt(i);
+            TabModel currentTabModel =
+                    mActivityTestRule.getActivity().getTabModelSelector().getCurrentModel();
+            int currentTabIndex = currentTabModel.index();
 
             boolean fixPendingReadbacks = mActivityTestRule.getActivity()
                                                   .getTabContentManager()
@@ -256,30 +292,33 @@
 
             // When there are pending readbacks due to detached Tabs, try to fix it by switching
             // back to that tab.
-            if (fixPendingReadbacks) {
-                int lastIndex = i;
+            if (fixPendingReadbacks && previousTabIndex != TabModel.INVALID_TAB_INDEX) {
                 // clang-format off
                 TestThreadUtils.runOnUiThreadBlocking(() ->
-                    mActivityTestRule.getActivity().getCurrentTabModel().setIndex(
-                            lastIndex, TabSelectionType.FROM_USER)
+                        previousTabModel.setIndex(previousTabIndex, TabSelectionType.FROM_USER)
                 );
                 // clang-format on
             }
+
             checkThumbnailsExist(previousTab);
+
             if (fixPendingReadbacks) {
-                int currentIndex = i + 1;
                 // clang-format off
-                TestThreadUtils.runOnUiThreadBlocking(() ->
-                    mActivityTestRule.getActivity().getCurrentTabModel().setIndex(
-                            currentIndex, TabSelectionType.FROM_USER)
+                TestThreadUtils.runOnUiThreadBlocking(() -> currentTabModel.setIndex(
+                        currentTabIndex, TabSelectionType.FROM_USER)
                 );
                 // clang-format on
             }
         }
+
         ChromeTabUtils.waitForTabPageLoaded(mActivityTestRule.getActivity().getActivityTab(), null,
                 null, WAIT_TIMEOUT_SECONDS * 10);
-        assertEquals(
-                numTabs, mActivityTestRule.getActivity().getTabModelSelector().getTotalTabCount());
+
+        assertEquals(numTabs + previousTabCount,
+                mActivityTestRule.getActivity()
+                        .getTabModelSelector()
+                        .getModel(isIncognito)
+                        .getCount());
 
         if (waitForLoading) {
             // clang-format off
@@ -316,14 +355,14 @@
     @Test
     @MediumTest
     public void testGridToTabToCurrentNTP() throws InterruptedException {
-        prepareTabs(1, NTP_URL);
+        prepareTabs(1, 0, NTP_URL);
         testGridToTab(false, false);
     }
 
     @Test
     @MediumTest
     public void testGridToTabToOtherNTP() throws InterruptedException {
-        prepareTabs(2, NTP_URL);
+        prepareTabs(2, 0, NTP_URL);
         testGridToTab(true, false);
     }
 
@@ -331,19 +370,32 @@
     @MediumTest
     @Features.DisableFeatures(ChromeFeatureList.TAB_TO_GTS_ANIMATION)
     public void testGridToTabToCurrentLive() throws InterruptedException {
-        prepareTabs(1, mUrl);
+        prepareTabs(1, 0, mUrl);
         testGridToTab(false, false);
     }
 
+    // From https://stackoverflow.com/a/21505193
+    private static boolean isEmulator() {
+        return Build.FINGERPRINT.startsWith("generic") || Build.FINGERPRINT.startsWith("unknown")
+                || Build.MODEL.contains("google_sdk") || Build.MODEL.contains("Emulator")
+                || Build.MODEL.contains("Android SDK built for x86")
+                || Build.MANUFACTURER.contains("Genymotion")
+                || (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic"))
+                || "google_sdk".equals(Build.PRODUCT);
+    }
+
     @Test
     @MediumTest
     @Features.DisableFeatures(ChromeFeatureList.TAB_TO_GTS_ANIMATION)
-    @DisabledTest(message = "crbug.com/986047. This works on emulators but not on real devices.")
     public void testGridToTabToCurrentLiveDetached() throws Exception {
+        // This works on emulators but not on real devices. See crbug.com/986047.
+        if (!isEmulator()) return;
+
         for (int i = 0; i < 10; i++) {
+            mActivityTestRule.loadUrl(mUrl);
             // Quickly create some tabs, navigate to web pages, and don't wait for thumbnail
             // capturing.
-            prepareTabs(2, mUrl, false);
+            createTabs(1, mUrl, false, false);
             // Hopefully we are in a state where some pending readbacks are stuck because their tab
             // is not attached to the view.
             if (mActivityTestRule.getActivity()
@@ -362,10 +414,12 @@
             mActivityTestRule.startMainActivityOnBlankPage();
             Assert.assertEquals(1, mActivityTestRule.tabsCount(false));
         }
+
         assertNotEquals(0,
                 mActivityTestRule.getActivity()
                         .getTabContentManager()
                         .getPendingReadbacksForTesting());
+
         // The last tab should still get thumbnail even though readbacks for other tabs are stuck.
         testGridToTab(false, false);
     }
@@ -376,7 +430,7 @@
     @MinAndroidSdkLevel(Build.VERSION_CODES.LOLLIPOP)
     @DisabledTest(message = "crbug.com/993201 This test fails deterministically on Nexus 5X")
     public void testGridToTabToCurrentLiveWithAnimation() throws InterruptedException {
-        prepareTabs(1, mUrl);
+        prepareTabs(1, 0, mUrl);
         testGridToTab(false, false);
     }
 
@@ -384,7 +438,7 @@
     @MediumTest
     @Features.DisableFeatures(ChromeFeatureList.TAB_TO_GTS_ANIMATION)
     public void testGridToTabToOtherLive() throws InterruptedException {
-        prepareTabs(2, mUrl);
+        prepareTabs(2, 0, mUrl);
         testGridToTab(true, false);
     }
 
@@ -394,7 +448,7 @@
     @MinAndroidSdkLevel(Build.VERSION_CODES.LOLLIPOP)
     @DisabledTest(message = "crbug.com/993201 This test fails deterministically on Nexus 5X")
     public void testGridToTabToOtherLiveWithAnimation() throws InterruptedException {
-        prepareTabs(2, mUrl);
+        prepareTabs(2, 0, mUrl);
         testGridToTab(true, false);
     }
 
@@ -402,7 +456,7 @@
     @MediumTest
     @Features.DisableFeatures(ChromeFeatureList.TAB_TO_GTS_ANIMATION)
     public void testGridToTabToOtherFrozen() throws InterruptedException {
-        prepareTabs(2, mUrl);
+        prepareTabs(2, 0, mUrl);
         testGridToTab(true, true);
     }
 
@@ -458,10 +512,8 @@
     @MediumTest
     @CommandLineFlags.Add({BASE_PARAMS})
     public void testRestoredTabsDontFetch() throws Exception {
-        prepareTabs(2, mUrl);
-        TabSwitcher.TabListDelegate delegate =
-                mStartSurfaceLayout.getStartSurfaceForTesting().getTabListDelegate();
-        int oldCount = delegate.getBitmapFetchCountForTesting();
+        prepareTabs(2, 0, mUrl);
+        int oldCount = mTabListDelegate.getBitmapFetchCountForTesting();
 
         // Restart Chrome.
         // Although we're destroying the activity, the Application will still live on since its in
@@ -473,7 +525,7 @@
         Layout layout = mActivityTestRule.getActivity().getLayoutManager().getOverviewLayout();
         assertTrue(layout instanceof StartSurfaceLayout);
         mStartSurfaceLayout = (StartSurfaceLayout) layout;
-        Assert.assertEquals(0, delegate.getBitmapFetchCountForTesting() - oldCount);
+        Assert.assertEquals(0, mTabListDelegate.getBitmapFetchCountForTesting() - oldCount);
     }
 
     @Test
@@ -498,7 +550,7 @@
     @CommandLineFlags.Add({BASE_PARAMS + "/soft-cleanup-delay/10000/cleanup-delay/10000"})
     public void testInvisibleTabsDontFetchWarm() throws InterruptedException {
         // Get the GTS in the warm state.
-        prepareTabs(2, NTP_URL);
+        prepareTabs(2, 0, NTP_URL);
         mRepeat = 2;
         testTabToGrid(NTP_URL);
 
@@ -522,7 +574,7 @@
     @CommandLineFlags.Add({BASE_PARAMS + "/cleanup-delay/10000"})
     public void testInvisibleTabsDontFetchSoft() throws InterruptedException {
         // Get the GTS in the soft cleaned up state.
-        prepareTabs(2, NTP_URL);
+        prepareTabs(2, 0, NTP_URL);
         mRepeat = 2;
         testTabToGrid(NTP_URL);
 
@@ -547,14 +599,9 @@
     // clang-format off
     @DisabledTest(message = "http://crbug/1005865 - Test was previously flaky but only on bots."
             + "Was not locally reproducible. Disabling until verified that it's deflaked on bots.")
-    public void testIncognitoEnterGts() {
-        // clang-format on
-        mActivityTestRule.newIncognitoTabFromMenu();
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> mActivityTestRule.getActivity().getLayoutManager().showOverview(false));
-        CriteriaHelper.pollInstrumentationThread(
-                () -> mActivityTestRule.getActivity().getLayoutManager().overviewVisible());
-
+    public void testIncognitoEnterGts() throws InterruptedException {
+        prepareTabs(1, 1, null);
+        enterGTS();
         onView(withId(org.chromium.chrome.tab_ui.R.id.tab_list_view))
                 .check(TabCountAssertion.havingTabCount(1));
 
@@ -563,15 +610,64 @@
         CriteriaHelper.pollInstrumentationThread(
                 () -> !mActivityTestRule.getActivity().getLayoutManager().overviewVisible());
 
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> mActivityTestRule.getActivity().getLayoutManager().showOverview(false));
-        CriteriaHelper.pollInstrumentationThread(
-                () -> mActivityTestRule.getActivity().getLayoutManager().overviewVisible());
-
+        enterGTS();
         onView(withId(org.chromium.chrome.tab_ui.R.id.tab_list_view))
                 .check(TabCountAssertion.havingTabCount(1));
     }
 
+    @Test
+    @MediumTest
+    @CommandLineFlags.Add({BASE_PARAMS})
+    public void testIncognitoToggle_tabCount() throws InterruptedException {
+        mActivityTestRule.loadUrl(mUrl);
+
+        // Prepare two incognito tabs and enter tab switcher.
+        prepareTabs(1, 2, mUrl);
+        enterGTS();
+        onView(withId(org.chromium.chrome.tab_ui.R.id.tab_list_view))
+                .check(TabCountAssertion.havingTabCount(2));
+
+        for (int i = 0; i < mRepeat; i++) {
+            switchTabModel(false);
+            onView(withId(org.chromium.chrome.tab_ui.R.id.tab_list_view))
+                    .check(TabCountAssertion.havingTabCount(1));
+
+            switchTabModel(true);
+            onView(withId(org.chromium.chrome.tab_ui.R.id.tab_list_view))
+                    .check(TabCountAssertion.havingTabCount(2));
+        }
+    }
+
+    @Test
+    @MediumTest
+    @CommandLineFlags.Add({BASE_PARAMS})
+    @DisabledTest(message = "crbug.com/1014792, should not fetch all tabs in TabModel when we" +
+            "switch model")
+    public void testIncognitoToggle_thumbnailFetchCount() throws InterruptedException {
+        mActivityTestRule.loadUrl(mUrl);
+        int oldFetchCount = mTabListDelegate.getBitmapFetchCountForTesting();
+
+        // Prepare two incognito tabs and enter tab switcher.
+        prepareTabs(1, 2, mUrl);
+        enterGTS();
+
+        int currentFetchCount = mTabListDelegate.getBitmapFetchCountForTesting();
+        Assert.assertEquals(2, currentFetchCount - oldFetchCount);
+        oldFetchCount = currentFetchCount;
+
+        for (int i = 0; i < mRepeat; i++) {
+            switchTabModel(false);
+            currentFetchCount = mTabListDelegate.getBitmapFetchCountForTesting();
+            Assert.assertEquals(1, currentFetchCount - oldFetchCount);
+            oldFetchCount = currentFetchCount;
+
+            switchTabModel(true);
+            currentFetchCount = mTabListDelegate.getBitmapFetchCountForTesting();
+            Assert.assertEquals(2, currentFetchCount - oldFetchCount);
+            oldFetchCount = currentFetchCount;
+        }
+    }
+
     private static class TabCountAssertion implements ViewAssertion {
         private int mExpectedCount;
 
@@ -592,6 +688,20 @@
         }
     }
 
+    private void switchTabModel(boolean isIncognito) {
+        assertTrue(isIncognito !=
+                mActivityTestRule.getActivity().getTabModelSelector().isIncognitoSelected());
+
+        onView(withContentDescription(
+                isIncognito ? R.string.accessibility_tab_switcher_incognito_stack
+                            : R.string.accessibility_tab_switcher_standard_stack)
+        ).perform(click());
+
+        CriteriaHelper.pollInstrumentationThread(() ->
+                mActivityTestRule.getActivity().getTabModelSelector().isIncognitoSelected()
+                        == isIncognito);
+    }
+
     private void enterGTS() throws InterruptedException {
         Tab currentTab = mActivityTestRule.getActivity().getTabModelSelector().getCurrentTab();
         // Native tabs need to be invalidated first to trigger thumbnail taking, so skip them.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/SearchEngineLogoUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/SearchEngineLogoUtils.java
index 2901ab94..45c27dc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/SearchEngineLogoUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/SearchEngineLogoUtils.java
@@ -10,11 +10,13 @@
 import android.graphics.Color;
 import android.text.TextUtils;
 
+import androidx.annotation.IntDef;
 import androidx.annotation.Nullable;
 
 import org.chromium.base.Callback;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.library_loader.LibraryProcessType;
+import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.favicon.FaviconHelper;
@@ -25,6 +27,8 @@
 import org.chromium.chrome.browser.util.UrlUtilities;
 import org.chromium.content_public.browser.BrowserStartupController;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -46,14 +50,68 @@
     private static int sSearchEngineLogoTargetSizePixels;
     private static int sSearchEngineLogoComposedSizePixels;
 
+    /** Encapsulates methods that rely on static dependencies that aren't available for testing. */
+    static class Delegate {
+        /** @see SearchEngineLogoUtils#isSearchEngineLogoEnabled */
+        public boolean isSearchEngineLogoEnabled() {
+            return !LocaleManager.getInstance().needToCheckForSearchEnginePromo()
+                    && ChromeFeatureList.isInitialized()
+                    && ChromeFeatureList.isEnabled(ChromeFeatureList.OMNIBOX_SEARCH_ENGINE_LOGO);
+        }
+
+        /** @see SearchEngineLogoUtils#shouldShowSearchEngineLogo */
+        public boolean shouldShowSearchEngineLogo(boolean isOffTheRecord) {
+            return !isOffTheRecord
+                    && isSearchEngineLogoEnabled()
+                    // Using the profile now, so we need to pay attention to browser initialization.
+                    && BrowserStartupController.get(LibraryProcessType.PROCESS_BROWSER)
+                               .isFullBrowserStarted();
+        }
+
+        /** @see SearchEngineLogoUtils#shouldShowRoundedSearchEngineLogo */
+        public boolean shouldShowRoundedSearchEngineLogo(boolean isOffTheRecord) {
+            return shouldShowSearchEngineLogo(isOffTheRecord) && ChromeFeatureList.isInitialized()
+                    && ChromeFeatureList.getFieldTrialParamByFeatureAsBoolean(
+                            ChromeFeatureList.OMNIBOX_SEARCH_ENGINE_LOGO, ROUNDED_EDGES_VARIANT,
+                            false);
+        }
+
+        /** @see SearchEngineLogoUtils#shouldShowSearchLoupeEverywhere */
+        public boolean shouldShowSearchLoupeEverywhere(boolean isOffTheRecord) {
+            return shouldShowSearchEngineLogo(isOffTheRecord)
+                    && ChromeFeatureList.getFieldTrialParamByFeatureAsBoolean(
+                            ChromeFeatureList.OMNIBOX_SEARCH_ENGINE_LOGO, LOUPE_EVERYWHERE_VARIANT,
+                            false);
+        }
+    }
+    private static Delegate sDelegate = new Delegate();
+
+    /**
+     * AndroidSearchEngineLogoEvents defined in tools/metrics/histograms/enums.xml. These values
+     * are persisted to logs. Entries should not be renumbered and numeric values should never be
+     * reused.
+     */
+    @IntDef({Events.FETCH_NON_GOOGLE_LOGO_REQUEST, Events.FETCH_FAILED_NULL_URL,
+            Events.FETCH_FAILED_FAVICON_HELPER_ERROR, Events.FETCH_FAILED_RETURNED_BITMAP_NULL,
+            Events.FETCH_SUCCESS_CACHE_HIT, Events.FETCH_SUCCESS})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Events {
+        int FETCH_NON_GOOGLE_LOGO_REQUEST = 0;
+        int FETCH_FAILED_NULL_URL = 1;
+        int FETCH_FAILED_FAVICON_HELPER_ERROR = 2;
+        int FETCH_FAILED_RETURNED_BITMAP_NULL = 3;
+        int FETCH_SUCCESS_CACHE_HIT = 4;
+        int FETCH_SUCCESS = 5;
+
+        int MAX = 6;
+    }
+
     /**
      * Encapsulates complicated boolean check for reuse and readability.
      * @return True if the search engine logo is enabled, regardless of visibility.
      */
     public static boolean isSearchEngineLogoEnabled() {
-        return !LocaleManager.getInstance().needToCheckForSearchEnginePromo()
-                && ChromeFeatureList.isInitialized()
-                && ChromeFeatureList.isEnabled(ChromeFeatureList.OMNIBOX_SEARCH_ENGINE_LOGO);
+        return sDelegate.isSearchEngineLogoEnabled();
     }
 
     /**
@@ -62,11 +120,7 @@
      * @return True if we should show the search engine logo.
      */
     public static boolean shouldShowSearchEngineLogo(boolean isOffTheRecord) {
-        return !isOffTheRecord
-                && isSearchEngineLogoEnabled()
-                // Using the profile now, so we need to pay attention to browser initialization.
-                && BrowserStartupController.get(LibraryProcessType.PROCESS_BROWSER)
-                           .isFullBrowserStarted();
+        return sDelegate.shouldShowSearchEngineLogo(isOffTheRecord);
     }
 
     /**
@@ -75,9 +129,7 @@
      * @return True if we should show the rounded search engine logo.
      */
     public static boolean shouldShowRoundedSearchEngineLogo(boolean isOffTheRecord) {
-        return shouldShowSearchEngineLogo(isOffTheRecord) && ChromeFeatureList.isInitialized()
-                && ChromeFeatureList.getFieldTrialParamByFeatureAsBoolean(
-                        ChromeFeatureList.OMNIBOX_SEARCH_ENGINE_LOGO, ROUNDED_EDGES_VARIANT, false);
+        return sDelegate.shouldShowRoundedSearchEngineLogo(isOffTheRecord);
     }
 
     /** Ignores the incognito state for instances where a caller would otherwise pass "false". */
@@ -91,17 +143,7 @@
      * @return True if we should show the search engine logo as a loupe everywhere.
      */
     public static boolean shouldShowSearchLoupeEverywhere(boolean isOffTheRecord) {
-        return shouldShowSearchEngineLogo(isOffTheRecord)
-                && ChromeFeatureList.getFieldTrialParamByFeatureAsBoolean(
-                        ChromeFeatureList.OMNIBOX_SEARCH_ENGINE_LOGO, LOUPE_EVERYWHERE_VARIANT,
-                        false);
-    }
-
-    /**
-     * @return True if the search engine is Google.
-     */
-    public static boolean isSearchEngineGoogle() {
-        return TemplateUrlServiceFactory.get().isDefaultSearchEngineGoogle();
+        return sDelegate.shouldShowSearchLoupeEverywhere(isOffTheRecord);
     }
 
     /**
@@ -167,11 +209,13 @@
      */
     public static void getSearchEngineLogoFavicon(
             Profile profile, Resources resources, Callback<Bitmap> callback) {
+        recordEvent(Events.FETCH_NON_GOOGLE_LOGO_REQUEST);
         if (sFaviconHelper == null) sFaviconHelper = new FaviconHelper();
 
         String logoUrl = getSearchLogoUrl();
         if (logoUrl == null) {
             callback.onResult(null);
+            recordEvent(Events.FETCH_FAILED_NULL_URL);
             return;
         }
 
@@ -179,20 +223,26 @@
         if (sCachedComposedBackground != null
                 && sCachedComposedBackgroundLogoUrl.equals(getSearchLogoUrl())) {
             callback.onResult(sCachedComposedBackground);
+            recordEvent(Events.FETCH_SUCCESS_CACHE_HIT);
             return;
         }
 
         final int logoSizePixels = SearchEngineLogoUtils.getSearchEngineLogoSizePixels(resources);
-        boolean willReturn = sFaviconHelper.getLocalFaviconImageForURL(
+        boolean willCallbackBeCalled = sFaviconHelper.getLocalFaviconImageForURL(
                 profile, logoUrl, logoSizePixels, (image, iconUrl) -> {
                     if (image == null) {
                         callback.onResult(image);
+                        recordEvent(Events.FETCH_FAILED_RETURNED_BITMAP_NULL);
                         return;
                     }
 
                     processReturnedLogo(logoUrl, image, resources, callback);
+                    recordEvent(Events.FETCH_SUCCESS);
                 });
-        if (!willReturn) callback.onResult(null);
+        if (!willCallbackBeCalled) {
+            callback.onResult(null);
+            recordEvent(Events.FETCH_FAILED_FAVICON_HELPER_ERROR);
+        }
     }
 
     /**
@@ -223,7 +273,10 @@
                 sRoundedIconGenerator = new RoundedIconGenerator(composedSizePixels,
                         composedSizePixels, composedSizePixels, Color.TRANSPARENT, 0);
             }
-            sRoundedIconGenerator.setBackgroundColor(getMostCommonEdgeColor(image));
+            int color = (image.getWidth() == 0 || image.getHeight() == 0)
+                    ? Color.TRANSPARENT
+                    : getMostCommonEdgeColor(image);
+            sRoundedIconGenerator.setBackgroundColor(color);
 
             // Generate a rounded background with no text.
             composedIcon = sRoundedIconGenerator.generateIconForText("");
@@ -249,7 +302,6 @@
         for (int i = 0; i < icon.getWidth(); i++) {
             // top edge
             int color = icon.getPixel(i, 0);
-            System.out.println("current color: " + color);
             if (!colorCount.containsKey(color)) colorCount.put(color, 0);
             colorCount.put(color, colorCount.get(color) + 1);
 
@@ -286,4 +338,36 @@
 
         return maxKey;
     }
+
+    /**
+     * Records an event to the search engine logo histogram. See {@link Events} and histograms.xml
+     * for more details.
+     * @param event The {@link Events} to be reported.
+     */
+    @VisibleForTesting
+    static void recordEvent(@Events int event) {
+        RecordHistogram.recordEnumeratedHistogram(
+                "AndroidSearchEngineLogo.Events", event, Events.MAX);
+    }
+
+    /** Set the favicon helper for testing. */
+    static void setFaviconHelperForTesting(FaviconHelper faviconHelper) {
+        sFaviconHelper = faviconHelper;
+    }
+
+    /** Set the delegate for testing. */
+    static void setDelegateForTesting(Delegate mDelegate) {
+        sDelegate = mDelegate;
+    }
+
+    /** Set the RoundedIconGenerator for testing. */
+    static void setRoundedIconGeneratorForTesting(RoundedIconGenerator roundedIconGenerator) {
+        sRoundedIconGenerator = roundedIconGenerator;
+    }
+
+    /** Reset the cache values for testing. */
+    static void resetCacheForTesting() {
+        sCachedComposedBackground = null;
+        sCachedComposedBackgroundLogoUrl = null;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/HomepagePreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/HomepagePreferences.java
index 43074d0..4c24b4a2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/HomepagePreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/HomepagePreferences.java
@@ -11,6 +11,7 @@
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.partnercustomizations.HomepageManager;
+import org.chromium.chrome.browser.util.FeatureUtilities;
 
 /**
  * Fragment that allows the user to configure homepage related preferences.
@@ -30,14 +31,19 @@
         getActivity().setTitle(R.string.options_homepage_title);
         PreferenceUtils.addPreferencesFromResource(this, R.xml.homepage_preferences);
 
-        ChromeSwitchPreference mHomepageSwitch =
+        ChromeSwitchPreference homepageSwitch =
                 (ChromeSwitchPreference) findPreference(PREF_HOMEPAGE_SWITCH);
-        boolean isHomepageEnabled = mHomepageManager.getPrefHomepageEnabled();
-        mHomepageSwitch.setChecked(isHomepageEnabled);
-        mHomepageSwitch.setOnPreferenceChangeListener((preference, newValue) -> {
-            mHomepageManager.setPrefHomepageEnabled((boolean) newValue);
-            return true;
-        });
+
+        if (FeatureUtilities.isBottomToolbarEnabled()) {
+            homepageSwitch.setVisible(false);
+        } else {
+            boolean isHomepageEnabled = mHomepageManager.getPrefHomepageEnabled();
+            homepageSwitch.setChecked(isHomepageEnabled);
+            homepageSwitch.setOnPreferenceChangeListener((preference, newValue) -> {
+                mHomepageManager.setPrefHomepageEnabled((boolean) newValue);
+                return true;
+            });
+        }
 
         mHomepageEdit = findPreference(PREF_HOMEPAGE_EDIT);
         updateCurrentHomepageUrl();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/search_engines/TemplateUrlServiceFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/search_engines/TemplateUrlServiceFactory.java
index c4573e43..9cc64086 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/search_engines/TemplateUrlServiceFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/search_engines/TemplateUrlServiceFactory.java
@@ -30,7 +30,7 @@
     }
 
     @VisibleForTesting
-    static void setInstanceForTesting(TemplateUrlService service) {
+    public static void setInstanceForTesting(TemplateUrlService service) {
         sTemplateUrlService = service;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkHandlerDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkHandlerDelegate.java
index df34c34..af4d36c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkHandlerDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkHandlerDelegate.java
@@ -103,7 +103,7 @@
                 webApkInfo.shortName(), webApkInfo.webApkPackageName(), webApkInfo.id(),
                 webApkInfo.shellApkVersion(), packageInfo.versionCode, webApkInfo.url(),
                 webApkInfo.scopeUrl(), webApkInfo.manifestUrl(), webApkInfo.manifestStartUrl(),
-                webApkInfo.displayMode(), webApkInfo.orientation(), webApkInfo.themeColor(),
+                webApkInfo.displayMode(), webApkInfo.orientation(), webApkInfo.toolbarColor(),
                 webApkInfo.backgroundColor(), lastUpdateCheckTimeMsForStorage,
                 lastUpdateCompletionTimeMsInStorage, relaxUpdatesForStorage,
                 backingBrowserPackageName, isBackingBrowser, updateStatus);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkIntentDataProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkIntentDataProvider.java
index e700583..3925c9b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkIntentDataProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkIntentDataProvider.java
@@ -53,11 +53,13 @@
 
     private static final String TAG = "WebApkInfo";
 
+    private int mToolbarColor;
     private WebappExtras mWebappExtras;
     private WebApkExtras mWebApkExtras;
 
     public static WebApkIntentDataProvider createEmpty() {
-        return new WebApkIntentDataProvider(WebappExtras.createEmpty(), WebApkExtras.createEmpty());
+        return new WebApkIntentDataProvider(WebappIntentDataProvider.getDefaultToolbarColor(),
+                WebappExtras.createEmpty(), WebApkExtras.createEmpty());
     }
 
     /**
@@ -343,7 +345,7 @@
         WebappExtras webappExtras =
                 new WebappExtras(WebappRegistry.webApkIdForPackage(webApkPackageName), url, scope,
                         primaryIcon, name, shortName, displayMode, orientation, source,
-                        WebappIntentDataProvider.colorFromLongColor(themeColor),
+                        WebappIntentDataProvider.isLongColorValid(themeColor),
                         WebappIntentDataProvider.colorFromLongColor(backgroundColor),
                         defaultBackgroundColor, false /* isIconGenerated */, isPrimaryIconMaskable,
                         forceNavigation);
@@ -351,10 +353,15 @@
                 isSplashIconMaskable, shellApkVersion, manifestUrl, manifestStartUrl, distributor,
                 iconUrlToMurmur2HashMap, shareTarget, isSplashProvidedByWebApk, shareData,
                 webApkVersionCode);
-        return new WebApkIntentDataProvider(webappExtras, webApkExtras);
+        int toolbarColor = webappExtras.hasCustomToolbarColor
+                ? (int) themeColor
+                : WebappIntentDataProvider.getDefaultToolbarColor();
+        return new WebApkIntentDataProvider(toolbarColor, webappExtras, webApkExtras);
     }
 
-    private WebApkIntentDataProvider(WebappExtras webappExtras, WebApkExtras webApkExtras) {
+    private WebApkIntentDataProvider(
+            int toolbarColor, WebappExtras webappExtras, WebApkExtras webApkExtras) {
+        mToolbarColor = toolbarColor;
         mWebappExtras = webappExtras;
         mWebApkExtras = webApkExtras;
     }
@@ -536,6 +543,11 @@
     }
 
     @Override
+    public int getToolbarColor() {
+        return mToolbarColor;
+    }
+
+    @Override
     @Nullable
     public WebappExtras getWebappExtras() {
         return mWebappExtras;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java
index 6ca3867..69e42ff 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java
@@ -331,7 +331,7 @@
             return WebApkUpdateReason.NAME_DIFFERS;
         } else if (oldInfo.backgroundColor() != fetchedInfo.backgroundColor()) {
             return WebApkUpdateReason.BACKGROUND_COLOR_DIFFERS;
-        } else if (oldInfo.themeColor() != fetchedInfo.themeColor()) {
+        } else if (oldInfo.toolbarColor() != fetchedInfo.toolbarColor()) {
             return WebApkUpdateReason.THEME_COLOR_DIFFERS;
         } else if (oldInfo.orientation() != fetchedInfo.orientation()) {
             return WebApkUpdateReason.ORIENTATION_DIFFERS;
@@ -379,7 +379,7 @@
                 info.manifestStartUrl(), info.scopeUrl(), info.name(), info.shortName(),
                 primaryIconUrl, info.icon().bitmap(), info.isIconAdaptive(), badgeIconUrl,
                 info.badgeIcon().bitmap(), iconUrls, iconHashes, info.displayMode(),
-                info.orientation(), info.themeColor(), info.backgroundColor(),
+                info.orientation(), info.toolbarColor(), info.backgroundColor(),
                 info.shareTarget().getAction(), info.shareTarget().getParamTitle(),
                 info.shareTarget().getParamText(), info.shareTarget().getParamUrl(),
                 info.shareTarget().isShareMethodPost(),
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
index 98dda43..c3a1342 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
@@ -664,8 +664,8 @@
             icon = mLargestFavicon;
         }
 
-        if (mBrandColor == null && mWebappInfo.hasValidThemeColor()) {
-            mBrandColor = (int) mWebappInfo.themeColor();
+        if (mBrandColor == null && mWebappInfo.hasValidToolbarColor()) {
+            mBrandColor = (int) mWebappInfo.toolbarColor();
         }
 
         int taskDescriptionColor =
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappDataStorage.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappDataStorage.java
index d142a70..e7c1608 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappDataStorage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappDataStorage.java
@@ -276,7 +276,7 @@
                 editor.putString(KEY_ICON, info.icon().encoded());
                 editor.putInt(KEY_DISPLAY_MODE, info.displayMode());
                 editor.putInt(KEY_ORIENTATION, info.orientation());
-                editor.putLong(KEY_THEME_COLOR, info.themeColor());
+                editor.putLong(KEY_THEME_COLOR, info.toolbarColor());
                 editor.putLong(KEY_BACKGROUND_COLOR, info.backgroundColor());
                 editor.putBoolean(KEY_IS_ICON_GENERATED, info.isIconGenerated());
                 editor.putBoolean(KEY_IS_ICON_ADAPTIVE, info.isIconAdaptive());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappExtras.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappExtras.java
index 1fd02180..0d88b0b7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappExtras.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappExtras.java
@@ -55,11 +55,9 @@
     public final @ShortcutSource int source;
 
     /**
-     * Theme color of the webapp.
-     * TODO(pkotwicz): Remove this property in favor of
-     * {@link BrowserServicesIntentDataProvider#getToolbarColor()}
+     * Whether the webapp provides a custom toolbar color.
      */
-    public final Integer themeColor;
+    public final boolean hasCustomToolbarColor;
 
     /**
      * Background color for webapp's splash screen.
@@ -89,15 +87,15 @@
     public static WebappExtras createEmpty() {
         return new WebappExtras(null /* id */, null /* url */, null /* scopeUrl */,
                 new WebappIcon(), null /* name */, null /* shortName */, WebDisplayMode.UNDEFINED,
-                ScreenOrientationValues.DEFAULT, ShortcutSource.UNKNOWN, null /* themeColor */,
-                null /* backgroundColor */, Color.WHITE /* defaultBackgroundColor */,
-                false /* isIconGenerated */, false /* isIconAdaptive */,
-                false /* shouldForceNavigation */);
+                ScreenOrientationValues.DEFAULT, ShortcutSource.UNKNOWN,
+                false /* hasCustomToolbarColor */, null /* backgroundColor */,
+                Color.WHITE /* defaultBackgroundColor */, false /* isIconGenerated */,
+                false /* isIconAdaptive */, false /* shouldForceNavigation */);
     }
 
     public WebappExtras(String id, String url, String scopeUrl, WebappIcon icon, String name,
             String shortName, @WebDisplayMode int displayMode, int orientation, int source,
-            Integer themeColor, Integer backgroundColor, int defaultBackgroundColor,
+            boolean hasCustomToolbarColor, Integer backgroundColor, int defaultBackgroundColor,
             boolean isIconGenerated, boolean isIconAdaptive, boolean shouldForceNavigation) {
         this.id = id;
         this.url = url;
@@ -108,7 +106,7 @@
         this.displayMode = displayMode;
         this.orientation = orientation;
         this.source = source;
-        this.themeColor = themeColor;
+        this.hasCustomToolbarColor = hasCustomToolbarColor;
         this.backgroundColor = backgroundColor;
         this.defaultBackgroundColor = defaultBackgroundColor;
         this.isIconGenerated = isIconGenerated;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappInfo.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappInfo.java
index 9ae2f63c..7b6ad9d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappInfo.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappInfo.java
@@ -84,24 +84,24 @@
     }
 
     /**
-     * Theme color is actually a 32 bit unsigned integer which encodes a color
-     * in ARGB format. mThemeColor is a long because we also need to encode the
-     * error state of ShortcutHelper.MANIFEST_COLOR_INVALID_OR_MISSING.
+     * Returns the toolbar color if it is valid, and
+     * ShortcutHelper.MANIFEST_COLOR_INVALID_OR_MISSING otherwise.
      */
-    public long themeColor() {
-        return WebappIntentDataProvider.colorFromIntegerColor(getWebappExtras().themeColor);
+    public long toolbarColor() {
+        return hasValidToolbarColor() ? mProvider.getToolbarColor()
+                                      : ShortcutHelper.MANIFEST_COLOR_INVALID_OR_MISSING;
     }
 
     /**
-     * Returns whether the theme color specified in the Intent is valid.
+     * Returns whether the toolbar color specified in the Intent is valid.
      */
-    public boolean hasValidThemeColor() {
-        return getWebappExtras().themeColor != null;
+    public boolean hasValidToolbarColor() {
+        return getWebappExtras().hasCustomToolbarColor;
     }
 
     /**
      * Background color is actually a 32 bit unsigned integer which encodes a color
-     * in ARGB format. mBackgroundColor is a long because we also need to encode the
+     * in ARGB format. Return value is a long because we also need to encode the
      * error state of ShortcutHelper.MANIFEST_COLOR_INVALID_OR_MISSING.
      */
     public long backgroundColor() {
@@ -170,7 +170,7 @@
         intent.putExtra(ShortcutHelper.EXTRA_DISPLAY_MODE, displayMode());
         intent.putExtra(ShortcutHelper.EXTRA_ORIENTATION, orientation());
         intent.putExtra(ShortcutHelper.EXTRA_SOURCE, source());
-        intent.putExtra(ShortcutHelper.EXTRA_THEME_COLOR, themeColor());
+        intent.putExtra(ShortcutHelper.EXTRA_THEME_COLOR, toolbarColor());
         intent.putExtra(ShortcutHelper.EXTRA_BACKGROUND_COLOR, backgroundColor());
         intent.putExtra(ShortcutHelper.EXTRA_IS_ICON_GENERATED, isIconGenerated());
         intent.putExtra(ShortcutHelper.EXTRA_IS_ICON_ADAPTIVE, isIconAdaptive());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappIntentDataProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappIntentDataProvider.java
index e6d93e1..a44f8e4d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappIntentDataProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappIntentDataProvider.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.webapps;
 
 import android.content.Intent;
+import android.graphics.Color;
 import android.text.TextUtils;
 
 import androidx.annotation.Nullable;
@@ -24,10 +25,18 @@
 public class WebappIntentDataProvider extends BrowserServicesIntentDataProvider {
     private static final String TAG = "WebappInfo";
 
+    private int mToolbarColor;
     private WebappExtras mWebappExtras;
 
     public static WebappIntentDataProvider createEmpty() {
-        return new WebappIntentDataProvider(WebappExtras.createEmpty());
+        return new WebappIntentDataProvider(getDefaultToolbarColor(), WebappExtras.createEmpty());
+    }
+
+    /**
+     * Returns the toolbar color to use if a custom color is not specified by the webapp.
+     */
+    public static int getDefaultToolbarColor() {
+        return Color.WHITE;
     }
 
     /**
@@ -42,15 +51,17 @@
         return ShortcutHelper.MANIFEST_COLOR_INVALID_OR_MISSING;
     }
 
+    public static boolean isLongColorValid(long longColor) {
+        return (longColor != ShortcutHelper.MANIFEST_COLOR_INVALID_OR_MISSING);
+    }
+
     /**
      * Converts color from unsigned long where an unspecified color is represented as
      * {@link ShortcutHelper.MANIFEST_COLOR_INVALID_OR_MISSING} to a signed Integer where an
      * unspecified color is represented as null.
      */
     public static Integer colorFromLongColor(long longColor) {
-        return (longColor == ShortcutHelper.MANIFEST_COLOR_INVALID_OR_MISSING)
-                ? null
-                : Integer.valueOf((int) longColor);
+        return isLongColorValid(longColor) ? Integer.valueOf((int) longColor) : null;
     }
 
     public static String idFromIntent(Intent intent) {
@@ -96,6 +107,11 @@
             return null;
         }
 
+        long themeColor = IntentUtils.safeGetLongExtra(intent, ShortcutHelper.EXTRA_THEME_COLOR,
+                ShortcutHelper.MANIFEST_COLOR_INVALID_OR_MISSING);
+        boolean hasValidToolbarColor = isLongColorValid(themeColor);
+        int toolbarColor = hasValidToolbarColor ? (int) themeColor : getDefaultToolbarColor();
+
         String icon = IntentUtils.safeGetStringExtra(intent, ShortcutHelper.EXTRA_ICON);
 
         String scope = IntentUtils.safeGetStringExtra(intent, ShortcutHelper.EXTRA_SCOPE);
@@ -109,9 +125,6 @@
         int orientation = IntentUtils.safeGetIntExtra(
                 intent, ShortcutHelper.EXTRA_ORIENTATION, ScreenOrientationValues.DEFAULT);
         int source = sourceFromIntent(intent);
-        Integer themeColor = colorFromLongColor(
-                IntentUtils.safeGetLongExtra(intent, ShortcutHelper.EXTRA_THEME_COLOR,
-                        ShortcutHelper.MANIFEST_COLOR_INVALID_OR_MISSING));
         Integer backgroundColor = colorFromLongColor(
                 IntentUtils.safeGetLongExtra(intent, ShortcutHelper.EXTRA_BACKGROUND_COLOR,
                         ShortcutHelper.MANIFEST_COLOR_INVALID_OR_MISSING));
@@ -129,16 +142,22 @@
                 SplashLayout.getDefaultBackgroundColor(ContextUtils.getApplicationContext());
 
         WebappExtras webappExtras = new WebappExtras(id, url, scope, new WebappIcon(icon), name,
-                shortName, displayMode, orientation, source, themeColor, backgroundColor,
+                shortName, displayMode, orientation, source, hasValidToolbarColor, backgroundColor,
                 defaultBackgroundColor, isIconGenerated, isIconAdaptive, forceNavigation);
-        return new WebappIntentDataProvider(webappExtras);
+        return new WebappIntentDataProvider(toolbarColor, webappExtras);
     }
 
-    private WebappIntentDataProvider(WebappExtras webappExtras) {
+    private WebappIntentDataProvider(int toolbarColor, WebappExtras webappExtras) {
+        mToolbarColor = toolbarColor;
         mWebappExtras = webappExtras;
     }
 
     @Override
+    public int getToolbarColor() {
+        return mToolbarColor;
+    }
+
+    @Override
     @Nullable
     public WebappExtras getWebappExtras() {
         return mWebappExtras;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/SearchEngineLogoUtilsTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/SearchEngineLogoUtilsTest.java
deleted file mode 100644
index 7be63ce..0000000
--- a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/SearchEngineLogoUtilsTest.java
+++ /dev/null
@@ -1,76 +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.omnibox;
-
-import static org.junit.Assert.assertEquals;
-
-import android.graphics.Bitmap;
-import android.graphics.Color;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.base.test.BaseRobolectricTestRunner;
-
-/**
- * Tests for SearchEngineLogoUtils.
- */
-@RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class SearchEngineLogoUtilsTest {
-    @Test
-    public void getMostCommonEdgeColor_allOneColor() {
-        int color = Color.BLUE;
-        Bitmap bitmap = createSolidImage(100, 100, color);
-        assertEquals(color, SearchEngineLogoUtils.getMostCommonEdgeColor(bitmap));
-    }
-
-    @Test
-    public void getMostCommonEdgeColor_outerInnerColor() {
-        int color = Color.BLUE;
-        Bitmap bitmap = createSolidImageWithDifferentInnerColor(100, 100, color, Color.RED);
-        assertEquals(color, SearchEngineLogoUtils.getMostCommonEdgeColor(bitmap));
-    }
-
-    @Test
-    public void getMostCommonEdgeColor_slightlyLargerColor() {
-        int color = Color.BLUE;
-        Bitmap bitmap = createSolidImageWithSlighlyLargerEdgeCoverage(100, 100, color, Color.RED);
-        assertEquals(color, SearchEngineLogoUtils.getMostCommonEdgeColor(bitmap));
-    }
-
-    private static Bitmap createSolidImage(int width, int height, int color) {
-        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
-        for (int x = 0; x < bitmap.getWidth(); x++) {
-            for (int y = 0; y < bitmap.getHeight(); y++) {
-                bitmap.setPixel(x, y, color);
-            }
-        }
-        return bitmap;
-    }
-
-    private static Bitmap createSolidImageWithDifferentInnerColor(
-            int width, int height, int outerColor, int innerColor) {
-        Bitmap bitmap = createSolidImage(width, height, outerColor);
-        for (int x = 1; x < bitmap.getWidth() - 1; x++) {
-            for (int y = 1; y < bitmap.getHeight() - 1; y++) {
-                bitmap.setPixel(x, y, innerColor);
-            }
-        }
-        return bitmap;
-    }
-
-    private static Bitmap createSolidImageWithSlighlyLargerEdgeCoverage(
-            int width, int height, int largerColor, int smallerColor) {
-        Bitmap bitmap = createSolidImage(width, height, largerColor);
-        for (int x = 0; x < bitmap.getWidth(); x++) {
-            for (int y = bitmap.getHeight() + 1; y < bitmap.getHeight(); y++) {
-                bitmap.setPixel(x, y, smallerColor);
-            }
-        }
-        return bitmap;
-    }
-}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/SearchEngineLogoUtilsUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/SearchEngineLogoUtilsUnitTest.java
new file mode 100644
index 0000000..216cb8fb
--- /dev/null
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/SearchEngineLogoUtilsUnitTest.java
@@ -0,0 +1,238 @@
+// 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.omnibox;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.robolectric.Shadows.shadowOf;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadow.api.Shadow;
+
+import org.chromium.base.Callback;
+import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.browser.favicon.FaviconHelper;
+import org.chromium.chrome.browser.omnibox.SearchEngineLogoUtils.Delegate;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
+import org.chromium.chrome.browser.ui.widget.RoundedIconGenerator;
+import org.chromium.chrome.test.support.DisableHistogramsRule;
+import org.chromium.components.search_engines.TemplateUrlService;
+
+/**
+ * Tests for SearchEngineLogoUtils.
+ */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
+public class SearchEngineLogoUtilsUnitTest {
+    private static final String LOGO_URL = "http://testlogo.com";
+    private static final String EVENTS_HISTOGRAM = "AndroidSearchEngineLogo.Events";
+
+    @Rule
+    public DisableHistogramsRule mDisableHistogramsRule = new DisableHistogramsRule();
+
+    @Captor
+    ArgumentCaptor<FaviconHelper.FaviconImageCallback> mCallbackCaptor;
+    @Mock
+    Callback<Bitmap> mCallback;
+    @Mock
+    Delegate mDelegate;
+    @Mock
+    FaviconHelper mFaviconHelper;
+    @Mock
+    TemplateUrlService mTemplateUrlService;
+    @Mock
+    RoundedIconGenerator mRoundedIconGenerator;
+
+    Bitmap mBitmap;
+
+    @Before
+    public void setUp() {
+        ShadowRecordHistogram.reset();
+        MockitoAnnotations.initMocks(this);
+
+        mBitmap = Shadow.newInstanceOf(Bitmap.class);
+        shadowOf(mBitmap).appendDescription("test");
+
+        TemplateUrlServiceFactory.setInstanceForTesting(mTemplateUrlService);
+
+        SearchEngineLogoUtils.resetCacheForTesting();
+        SearchEngineLogoUtils.setDelegateForTesting(mDelegate);
+        SearchEngineLogoUtils.setFaviconHelperForTesting(mFaviconHelper);
+        SearchEngineLogoUtils.setRoundedIconGeneratorForTesting(mRoundedIconGenerator);
+
+        when(mRoundedIconGenerator.generateIconForText(any())).thenReturn(mBitmap);
+        when(mDelegate.isSearchEngineLogoEnabled()).thenReturn(true);
+        when(mDelegate.shouldShowSearchEngineLogo(false)).thenReturn(true);
+        when(mDelegate.shouldShowRoundedSearchEngineLogo(false)).thenReturn(true);
+        when(mTemplateUrlService.getUrlForSearchQuery(any())).thenReturn(LOGO_URL);
+        when(mFaviconHelper.getLocalFaviconImageForURL(
+                     any(), any(), anyInt(), mCallbackCaptor.capture()))
+                .thenReturn(true);
+    }
+
+    @Test
+    public void recordEvent() {
+        SearchEngineLogoUtils.recordEvent(
+                SearchEngineLogoUtils.Events.FETCH_NON_GOOGLE_LOGO_REQUEST);
+        assertEquals(1,
+                ShadowRecordHistogram.getHistogramValueCountForTesting(EVENTS_HISTOGRAM,
+                        SearchEngineLogoUtils.Events.FETCH_NON_GOOGLE_LOGO_REQUEST));
+    }
+
+    @Test
+    public void getSearchEngineLogoFavicon() {
+        SearchEngineLogoUtils.getSearchEngineLogoFavicon(
+                Mockito.mock(Profile.class), Mockito.mock(Resources.class), mCallback);
+        FaviconHelper.FaviconImageCallback faviconCallback = mCallbackCaptor.getValue();
+        assertNotNull(faviconCallback);
+        faviconCallback.onFaviconAvailable(mBitmap, LOGO_URL);
+        assertEquals(1,
+                ShadowRecordHistogram.getHistogramValueCountForTesting(EVENTS_HISTOGRAM,
+                        SearchEngineLogoUtils.Events.FETCH_NON_GOOGLE_LOGO_REQUEST));
+        assertEquals(1,
+                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                        EVENTS_HISTOGRAM, SearchEngineLogoUtils.Events.FETCH_SUCCESS));
+    }
+
+    @Test
+    public void getSearchEngineLogoFavicon_faviconCached() {
+        SearchEngineLogoUtils.getSearchEngineLogoFavicon(
+                Mockito.mock(Profile.class), Mockito.mock(Resources.class), mCallback);
+        FaviconHelper.FaviconImageCallback faviconCallback = mCallbackCaptor.getValue();
+        assertNotNull(faviconCallback);
+        faviconCallback.onFaviconAvailable(mBitmap, LOGO_URL);
+        SearchEngineLogoUtils.getSearchEngineLogoFavicon(
+                Mockito.mock(Profile.class), Mockito.mock(Resources.class), mCallback);
+        assertEquals(2,
+                ShadowRecordHistogram.getHistogramValueCountForTesting(EVENTS_HISTOGRAM,
+                        SearchEngineLogoUtils.Events.FETCH_NON_GOOGLE_LOGO_REQUEST));
+        assertEquals(1,
+                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                        EVENTS_HISTOGRAM, SearchEngineLogoUtils.Events.FETCH_SUCCESS));
+        assertEquals(1,
+                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                        EVENTS_HISTOGRAM, SearchEngineLogoUtils.Events.FETCH_SUCCESS_CACHE_HIT));
+    }
+
+    @Test
+    public void getSearchEngineLogoFavicon_nullUrl() {
+        doReturn(null).when(mTemplateUrlService).getUrlForSearchQuery(any());
+        SearchEngineLogoUtils.getSearchEngineLogoFavicon(
+                Mockito.mock(Profile.class), Mockito.mock(Resources.class), mCallback);
+        verify(mCallback).onResult(null);
+        assertEquals(1,
+                ShadowRecordHistogram.getHistogramValueCountForTesting(EVENTS_HISTOGRAM,
+                        SearchEngineLogoUtils.Events.FETCH_NON_GOOGLE_LOGO_REQUEST));
+        assertEquals(1,
+                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                        EVENTS_HISTOGRAM, SearchEngineLogoUtils.Events.FETCH_FAILED_NULL_URL));
+    }
+
+    @Test
+    public void getSearchEngineLogoFavicon_faviconHelperError() {
+        when(mFaviconHelper.getLocalFaviconImageForURL(
+                     any(), any(), anyInt(), mCallbackCaptor.capture()))
+                .thenReturn(false);
+
+        SearchEngineLogoUtils.getSearchEngineLogoFavicon(
+                Mockito.mock(Profile.class), Mockito.mock(Resources.class), mCallback);
+        verify(mCallback).onResult(null);
+        assertEquals(1,
+                ShadowRecordHistogram.getHistogramValueCountForTesting(EVENTS_HISTOGRAM,
+                        SearchEngineLogoUtils.Events.FETCH_NON_GOOGLE_LOGO_REQUEST));
+        assertEquals(1,
+                ShadowRecordHistogram.getHistogramValueCountForTesting(EVENTS_HISTOGRAM,
+                        SearchEngineLogoUtils.Events.FETCH_FAILED_FAVICON_HELPER_ERROR));
+    }
+
+    @Test
+    public void getSearchEngineLogoFavicon_returnedBitmapNull() {
+        SearchEngineLogoUtils.getSearchEngineLogoFavicon(
+                Mockito.mock(Profile.class), Mockito.mock(Resources.class), mCallback);
+        FaviconHelper.FaviconImageCallback faviconCallback = mCallbackCaptor.getValue();
+        assertNotNull(faviconCallback);
+        faviconCallback.onFaviconAvailable(null, LOGO_URL);
+        verify(mCallback).onResult(null);
+        assertEquals(1,
+                ShadowRecordHistogram.getHistogramValueCountForTesting(EVENTS_HISTOGRAM,
+                        SearchEngineLogoUtils.Events.FETCH_NON_GOOGLE_LOGO_REQUEST));
+        assertEquals(1,
+                ShadowRecordHistogram.getHistogramValueCountForTesting(EVENTS_HISTOGRAM,
+                        SearchEngineLogoUtils.Events.FETCH_FAILED_RETURNED_BITMAP_NULL));
+    }
+
+    @Test
+    public void getMostCommonEdgeColor_allOneColor() {
+        int color = Color.BLUE;
+        Bitmap bitmap = createSolidImage(100, 100, color);
+        assertEquals(color, SearchEngineLogoUtils.getMostCommonEdgeColor(bitmap));
+    }
+
+    @Test
+    public void getMostCommonEdgeColor_outerInnerColor() {
+        int color = Color.BLUE;
+        Bitmap bitmap = createSolidImageWithDifferentInnerColor(100, 100, color, Color.RED);
+        assertEquals(color, SearchEngineLogoUtils.getMostCommonEdgeColor(bitmap));
+    }
+
+    @Test
+    public void getMostCommonEdgeColor_slightlyLargerColor() {
+        int color = Color.BLUE;
+        Bitmap bitmap = createSolidImageWithSlighlyLargerEdgeCoverage(100, 100, color, Color.RED);
+        assertEquals(color, SearchEngineLogoUtils.getMostCommonEdgeColor(bitmap));
+    }
+
+    private static Bitmap createSolidImage(int width, int height, int color) {
+        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+        for (int x = 0; x < bitmap.getWidth(); x++) {
+            for (int y = 0; y < bitmap.getHeight(); y++) {
+                bitmap.setPixel(x, y, color);
+            }
+        }
+        return bitmap;
+    }
+
+    private static Bitmap createSolidImageWithDifferentInnerColor(
+            int width, int height, int outerColor, int innerColor) {
+        Bitmap bitmap = createSolidImage(width, height, outerColor);
+        for (int x = 1; x < bitmap.getWidth() - 1; x++) {
+            for (int y = 1; y < bitmap.getHeight() - 1; y++) {
+                bitmap.setPixel(x, y, innerColor);
+            }
+        }
+        return bitmap;
+    }
+
+    private static Bitmap createSolidImageWithSlighlyLargerEdgeCoverage(
+            int width, int height, int largerColor, int smallerColor) {
+        Bitmap bitmap = createSolidImage(width, height, largerColor);
+        for (int x = 0; x < bitmap.getWidth(); x++) {
+            for (int y = bitmap.getHeight() + 1; y < bitmap.getHeight(); y++) {
+                bitmap.setPixel(x, y, smallerColor);
+            }
+        }
+        return bitmap;
+    }
+}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkInfoTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkInfoTest.java
index b319f3be7..743135e 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkInfoTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkInfoTest.java
@@ -164,8 +164,8 @@
         Assert.assertEquals(SHORT_NAME, info.shortName());
         Assert.assertEquals(WebDisplayMode.MINIMAL_UI, info.displayMode());
         Assert.assertEquals(ScreenOrientationValues.PORTRAIT, info.orientation());
-        Assert.assertTrue(info.hasValidThemeColor());
-        Assert.assertEquals(1L, info.themeColor());
+        Assert.assertTrue(info.hasValidToolbarColor());
+        Assert.assertEquals(1L, info.toolbarColor());
         Assert.assertTrue(info.hasValidBackgroundColor());
         Assert.assertEquals(2L, info.backgroundColor());
         Assert.assertEquals(WEBAPK_PACKAGE_NAME, info.webApkPackageName());
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappInfoTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappInfoTest.java
index de9d7d52..47a8d1d 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappInfoTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappInfoTest.java
@@ -142,16 +142,16 @@
     public void testNormalColors() {
         String name = "longName";
         String shortName = "name";
-        long themeColor = Color.argb(0xff, 0, 0xff, 0);
+        long toolbarColor = Color.argb(0xff, 0, 0xff, 0);
         long backgroundColor = Color.argb(0xff, 0, 0, 0xff);
 
         Intent intent = createIntentWithUrlAndId();
         intent.putExtra(ShortcutHelper.EXTRA_NAME, name);
         intent.putExtra(ShortcutHelper.EXTRA_SHORT_NAME, shortName);
-        intent.putExtra(ShortcutHelper.EXTRA_THEME_COLOR, themeColor);
+        intent.putExtra(ShortcutHelper.EXTRA_THEME_COLOR, toolbarColor);
         intent.putExtra(ShortcutHelper.EXTRA_BACKGROUND_COLOR, backgroundColor);
         WebappInfo info = WebappInfo.create(intent);
-        Assert.assertEquals(themeColor, info.themeColor());
+        Assert.assertEquals(toolbarColor, info.toolbarColor());
         Assert.assertEquals(backgroundColor, info.backgroundColor());
     }
 
@@ -164,22 +164,22 @@
         intent.putExtra(ShortcutHelper.EXTRA_NAME, name);
         intent.putExtra(ShortcutHelper.EXTRA_SHORT_NAME, shortName);
         WebappInfo info = WebappInfo.create(intent);
-        Assert.assertEquals(ShortcutHelper.MANIFEST_COLOR_INVALID_OR_MISSING, info.themeColor());
+        Assert.assertEquals(ShortcutHelper.MANIFEST_COLOR_INVALID_OR_MISSING, info.toolbarColor());
         Assert.assertEquals(
                 ShortcutHelper.MANIFEST_COLOR_INVALID_OR_MISSING, info.backgroundColor());
     }
 
     @Test
     public void testColorsIntentCreation() {
-        long themeColor = Color.argb(0xff, 0, 0xff, 0);
+        long toolbarColor = Color.argb(0xff, 0, 0xff, 0);
         long backgroundColor = Color.argb(0xff, 0, 0, 0xff);
 
         Intent intent = createIntentWithUrlAndId();
-        intent.putExtra(ShortcutHelper.EXTRA_THEME_COLOR, themeColor);
+        intent.putExtra(ShortcutHelper.EXTRA_THEME_COLOR, toolbarColor);
         intent.putExtra(ShortcutHelper.EXTRA_BACKGROUND_COLOR, backgroundColor);
 
         WebappInfo info = WebappInfo.create(intent);
-        Assert.assertEquals(themeColor, info.themeColor());
+        Assert.assertEquals(toolbarColor, info.toolbarColor());
         Assert.assertEquals(backgroundColor, info.backgroundColor());
     }
 
diff --git a/chrome/android/monochrome/BUILD.gn b/chrome/android/monochrome/BUILD.gn
index ebde7fe..b3e7d42c 100644
--- a/chrome/android/monochrome/BUILD.gn
+++ b/chrome/android/monochrome/BUILD.gn
@@ -2,22 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//build/config/android/rules.gni")
-
-android_library("monochrome_license_provider_java") {
-  java_files =
-      [ "java/src/com/android/webview/chromium/LicenseContentProvider.java" ]
-
-  deps = [
-    "//base:base_java",
-    "//chrome/android:chrome_java",
-    "//components/about_ui/android:aboutui_java",
-  ]
-
-  # Don't instrument this file as there exist another copy
-  # //android_webview:webview_license_provider_java
-  jacoco_never_instrument = true
-}
+import("//build/config/android/config.gni")
 
 if (public_android_sdk) {
   group("monochrome_apk_checker") {
diff --git a/chrome/android/monochrome/java/DEPS b/chrome/android/monochrome/java/DEPS
deleted file mode 100644
index 69fc978..0000000
--- a/chrome/android/monochrome/java/DEPS
+++ /dev/null
@@ -1,3 +0,0 @@
-include_rules = [
-  "+components/about_ui/android/java",
-]
diff --git a/chrome/android/monochrome/java/src/com/android/webview/chromium/LicenseContentProvider.java b/chrome/android/monochrome/java/src/com/android/webview/chromium/LicenseContentProvider.java
deleted file mode 100644
index 20b0ae2..0000000
--- a/chrome/android/monochrome/java/src/com/android/webview/chromium/LicenseContentProvider.java
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package com.android.webview.chromium;
-
-import android.annotation.TargetApi;
-import android.content.ContentProvider;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.ParcelFileDescriptor;
-
-import org.chromium.base.ThreadUtils;
-import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
-import org.chromium.components.aboutui.CreditUtilsJni;
-
-import java.io.FileNotFoundException;
-
-/**
- * Content provider for the OSS licenses file used on Monochrome and Trichrome.
- */
-@TargetApi(Build.VERSION_CODES.N)
-public class LicenseContentProvider
-        extends ContentProvider implements ContentProvider.PipeDataWriter<String> {
-    public static final String LICENSES_URI_SUFFIX = "LicenseContentProvider/webview_licenses";
-    public static final String LICENSES_CONTENT_TYPE = "text/html";
-
-    @Override
-    public boolean onCreate() {
-        return true;
-    }
-
-    @Override
-    public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
-        if (uri != null && uri.toString().endsWith(LICENSES_URI_SUFFIX)) {
-            return openPipeHelper(null, null, null, "webview_licenses.notice", this);
-        }
-        return null;
-    }
-
-    @Override
-    public void writeDataToPipe(
-            ParcelFileDescriptor output, Uri uri, String mimeType, Bundle opts, String filename) {
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                ChromeBrowserInitializer.getInstance(getContext()).handleSynchronousStartup();
-            }
-        });
-        CreditUtilsJni.get().writeCreditsHtml(output.detachFd());
-    }
-
-    @Override
-    public String getType(Uri uri) {
-        if (uri != null && uri.toString().endsWith(LICENSES_URI_SUFFIX)) {
-            return LICENSES_CONTENT_TYPE;
-        }
-        return null;
-    }
-
-    @Override
-    public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public int delete(Uri uri, String selection, String[] selectionArgs) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public Uri insert(Uri uri, ContentValues values) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
-            String sortOrder) {
-        throw new UnsupportedOperationException();
-    }
-}
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 1bdf5f97..b27b0d7 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -865,6 +865,8 @@
     "navigation_predictor/navigation_predictor_keyed_service.h",
     "navigation_predictor/navigation_predictor_keyed_service_factory.cc",
     "navigation_predictor/navigation_predictor_keyed_service_factory.h",
+    "navigation_predictor/navigation_predictor_preconnect_client.cc",
+    "navigation_predictor/navigation_predictor_preconnect_client.h",
     "net/chrome_cookie_notification_details.h",
     "net/chrome_mojo_proxy_resolver_factory.cc",
     "net/chrome_mojo_proxy_resolver_factory.h",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index dbb4418f..1d2b2d0 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -10,6 +10,7 @@
 #include <utility>
 
 #include "ash/public/cpp/app_list/app_list_features.h"
+#include "ash/public/cpp/app_list/app_list_switches.h"
 #include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/keyboard/keyboard_switches.h"
 #include "base/base_switches.h"
@@ -1357,15 +1358,15 @@
 };
 
 #if defined(OS_CHROMEOS)
-const FeatureEntry::FeatureParam kCrOSActionRecorderLogWithHash[] = {
-    {"CrOSActionRecorderType", "1"}};
-const FeatureEntry::FeatureParam kCrOSActionRecorderLogWithoutHash[] = {
-    {"CrOSActionRecorderType", "2"}};
-const FeatureEntry::FeatureVariation kCrOSActionRecorderVariations[] = {
-    {"Log with Hash", kCrOSActionRecorderLogWithHash,
-     base::size(kCrOSActionRecorderLogWithHash), nullptr},
-    {"Log without Hash", kCrOSActionRecorderLogWithoutHash,
-     base::size(kCrOSActionRecorderLogWithoutHash), nullptr}};
+const FeatureEntry::Choice kEnableCrOSActionRecorderChoices[] = {
+    {flags_ui::kGenericExperimentChoiceDefault, "", ""},
+    {ash::switches::kCrOSActionRecorderWithHash,
+     ash::switches::kEnableCrOSActionRecorder,
+     ash::switches::kCrOSActionRecorderWithHash},
+    {ash::switches::kCrOSActionRecorderWithoutHash,
+     ash::switches::kEnableCrOSActionRecorder,
+     ash::switches::kCrOSActionRecorderWithoutHash},
+};
 #endif  // defined(OS_CHROMEOS)
 
 // RECORDING USER METRICS FOR FLAGS:
@@ -2095,7 +2096,7 @@
     {"enable-subresource-redirect",
      flag_descriptions::kEnableSubresourceRedirectName,
      flag_descriptions::kEnableSubresourceRedirectDescription, kOsAll,
-     SINGLE_VALUE_TYPE(switches::kEnableSubresourceRedirect)},
+     FEATURE_VALUE_TYPE(blink::features::kSubresourceRedirect)},
 #if defined(OS_ANDROID)
     {"enable-offline-previews", flag_descriptions::kEnableOfflinePreviewsName,
      flag_descriptions::kEnableOfflinePreviewsDescription, kOsAndroid,
@@ -4672,13 +4673,14 @@
     {"enable-cros-action-recorder",
      flag_descriptions::kEnableCrOSActionRecorderName,
      flag_descriptions::kEnableCrOSActionRecorderDescription, kOsCrOS,
-     FEATURE_WITH_PARAMS_VALUE_TYPE(
-         app_list_features::kEnableCrOSActionRecorder,
-         kCrOSActionRecorderVariations,
-         "CrOSActionRecorderTypeVariations")},
+     MULTI_VALUE_TYPE(kEnableCrOSActionRecorderChoices)},
 #endif  // defined(OS_CHROMEOS)
 
 #if !defined(OS_ANDROID)
+    {"mix-browser-type-tabs", flag_descriptions::kMixBrowserTypeTabsName,
+     flag_descriptions::kMixBrowserTypeTabsDescription, kOsDesktop,
+     FEATURE_VALUE_TYPE(features::kMixBrowserTypeTabs)},
+
     {"mixed-content-setting", flag_descriptions::kMixedContentSiteSettingName,
      flag_descriptions::kMixedContentSiteSettingDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(features::kMixedContentSiteSetting)},
diff --git a/chrome/browser/chrome_browser_main_win.cc b/chrome/browser/chrome_browser_main_win.cc
index 164df34..a127640 100644
--- a/chrome/browser/chrome_browser_main_win.cc
+++ b/chrome/browser/chrome_browser_main_win.cc
@@ -75,6 +75,7 @@
 #include "components/crash/content/app/crash_export_thunks.h"
 #include "components/crash/content/app/dump_hung_process_with_ptype.h"
 #include "components/crash/core/common/crash_key.h"
+#include "components/os_crypt/os_crypt.h"
 #include "components/version_info/channel.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
@@ -510,6 +511,13 @@
   // setup.exe.  In Chrome, these strings are in the locale files.
   SetupInstallerUtilStrings();
 
+  PrefService* local_state = g_browser_process->local_state();
+  DCHECK(local_state);
+
+  // Initialize the OSCrypt.
+  bool os_crypt_init = OSCrypt::Init(local_state);
+  DCHECK(os_crypt_init);
+
   ChromeBrowserMainParts::PreMainMessageLoopStart();
   if (!parameters().ui_task) {
     // Make sure that we know how to handle exceptions from the message loop.
diff --git a/chrome/browser/chromeos/policy/status_collector/device_status_collector.cc b/chrome/browser/chromeos/policy/status_collector/device_status_collector.cc
index 602ebef..e308fd7 100644
--- a/chrome/browser/chromeos/policy/status_collector/device_status_collector.cc
+++ b/chrome/browser/chromeos/policy/status_collector/device_status_collector.cc
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include <algorithm>
+#include <cmath>
 #include <cstdio>
 #include <limits>
 #include <set>
@@ -124,10 +125,6 @@
 // The location where storage device statistics are read from.
 const char kStorageInfoPath[] = "/var/log/storage_info.txt";
 
-// Generic device name when reported from runtime_probe. Used to filter out
-// data for components.
-const char kGenericDeviceName[] = "generic";
-
 // The location where stateful partition info is read from.
 const char kStatefulPartitionPath[] = "/home/.shadow";
 
@@ -571,12 +568,6 @@
         &DeviceStatusCollectorState::OnCrosHealthdDataReceived, this));
   }
 
-  void FetchProbeData(const policy::DeviceStatusCollector::ProbeDataFetcher&
-                          probe_data_fetcher) {
-    probe_data_fetcher.Run(
-        base::BindOnce(&DeviceStatusCollectorState::OnProbeDataReceived, this));
-  }
-
   void FetchEMMCLifeTime(
       const policy::DeviceStatusCollector::EMMCLifetimeFetcher&
           emmc_lifetime_fetcher) {
@@ -613,7 +604,7 @@
 
   void OnCPUTempInfoReceived(
       const std::vector<em::CPUTempInfo>& cpu_temp_info) {
-    // Only one of OnProbeDataReceived and OnCPUTempInfoReceived should be
+    // Only one of OnCrosHealthdDataReceived or OnCPUTempInfoReceived should be
     // called.
     DCHECK(response_params_.device_status->cpu_temp_infos_size() == 0);
 
@@ -660,12 +651,25 @@
         tpm_status_struct.boot_lockbox_finalized);
   }
 
-  // Stores the contents of |probe_result| to |response_params_|.
+  // Stores the contents of |probe_result| and |samples| to |response_params_|.
   void OnCrosHealthdDataReceived(
-      chromeos::cros_healthd::mojom::TelemetryInfoPtr probe_result) {
+      chromeos::cros_healthd::mojom::TelemetryInfoPtr probe_result,
+      const base::circular_deque<std::unique_ptr<SampledData>>& samples) {
     // Make sure we edit the state on the right thread.
     DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
+    // Only one of OnCrosHealthdDataReceived or OnCPUTempInfoReceived should be
+    // called.
+    DCHECK_EQ(response_params_.device_status->cpu_temp_infos_size(), 0);
+
+    // Store CPU measurement samples.
+    for (const std::unique_ptr<SampledData>& sample_data : samples) {
+      for (auto kv : sample_data->cpu_samples) {
+        response_params_.device_status->mutable_cpu_temp_infos()->Add(
+            std::move(kv.second));
+      }
+    }
+
     if (probe_result.is_null())
       return;
 
@@ -689,71 +693,40 @@
           response_params_.device_status->mutable_system_status();
       system_status_out->set_vpd_sku_number(vpd_info->sku_number);
     }
-  }
-
-  // Note that we use proto3 syntax for ProbeResult, so missing fields will
-  // have default values.
-  void OnProbeDataReceived(
-      const base::Optional<runtime_probe::ProbeResult>& probe_result,
-      const base::circular_deque<std::unique_ptr<SampledData>>& samples) {
-    // Make sure we edit the state on the right thread.
-    DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-
-    // Only one of OnProbeDataReceived and OnCPUTempInfoReceived should be
-    // called.
-    DCHECK(response_params_.device_status->cpu_temp_infos_size() == 0);
-
-    // Store CPU measurement samples.
-    for (const std::unique_ptr<SampledData>& sample_data : samples) {
-      for (auto it = sample_data->cpu_samples.begin();
-           it != sample_data->cpu_samples.end(); it++) {
-        auto* new_info = response_params_.device_status->add_cpu_temp_infos();
-        *new_info = it->second;
-      }
-    }
-
-    if (!probe_result.has_value())
-      return;
-    if (probe_result.value().error() !=
-        runtime_probe::RUNTIME_PROBE_ERROR_NOT_SET) {
-      return;
-    }
-    if (probe_result.value().battery_size() > 0) {
-      em::PowerStatus* const power_status =
+    const auto& battery_info = probe_result->battery_info;
+    if (!battery_info.is_null()) {
+      em::PowerStatus* const power_status_out =
           response_params_.device_status->mutable_power_status();
-      for (const auto& battery : probe_result.value().battery()) {
-        if (battery.name() != kGenericDeviceName)
-          continue;
-        em::BatteryInfo* const battery_info = power_status->add_batteries();
-        battery_info->set_serial(battery.values().serial_number());
-        battery_info->set_manufacturer(battery.values().manufacturer());
-        battery_info->set_cycle_count(battery.values().cycle_count_smart());
-        // uAh to mAh
-        battery_info->set_design_capacity(
-            battery.values().charge_full_design() / 1000);
-        battery_info->set_full_charge_capacity(battery.values().charge_full() /
-                                               1000);
-        // uV to mV:
-        battery_info->set_design_min_voltage(
-            battery.values().voltage_min_design() / 1000);
-        if (battery.values().manufacture_date_smart() > 0) {
-          // manufacture_date in (((year-1980) * 16 + month) * 32 + day) format.
-          int remainder = battery.values().manufacture_date_smart();
-          int day = remainder % 32;
-          remainder /= 32;
-          int month = remainder % 16;
-          remainder /= 16;
-          int year = remainder + 1980;
-          // set manufacture_date in yyyy-mm-dd format.
-          battery_info->set_manufacture_date(
-              base::StringPrintf("%04d-%02d-%02d", year, month, day));
-        }
+      em::BatteryInfo* const battery_info_out =
+          power_status_out->add_batteries();
+      battery_info_out->set_serial(battery_info->serial_number);
+      battery_info_out->set_manufacturer(battery_info->vendor);
+      battery_info_out->set_cycle_count(battery_info->cycle_count);
+      // Convert Ah to mAh:
+      battery_info_out->set_design_capacity(
+          std::lround(battery_info->charge_full_design * 1000));
+      battery_info_out->set_full_charge_capacity(
+          std::lround(battery_info->charge_full * 1000));
+      // Convert V to mV:
+      battery_info_out->set_design_min_voltage(
+          std::lround(battery_info->voltage_min_design * 1000));
+      if (battery_info->manufacture_date_smart > 0) {
+        // manufacture_date in (((year-1980) * 16 + month) * 32 + day) format.
+        int remainder = battery_info->manufacture_date_smart;
+        int day = remainder % 32;
+        remainder /= 32;
+        int month = remainder % 16;
+        remainder /= 16;
+        int year = remainder + 1980;
+        // set manufacture_date in yyyy-mm-dd format.
+        battery_info_out->set_manufacture_date(
+            base::StringPrintf("%04d-%02d-%02d", year, month, day));
+      }
 
-        for (const std::unique_ptr<SampledData>& sample_data : samples) {
-          auto it = sample_data->battery_samples.find(battery.name());
-          if (it != sample_data->battery_samples.end())
-            battery_info->add_samples()->CheckTypeAndMergeFrom(it->second);
-        }
+      for (const std::unique_ptr<SampledData>& sample_data : samples) {
+        auto it = sample_data->battery_samples.find(battery_info->model_name);
+        if (it != sample_data->battery_samples.end())
+          battery_info_out->add_samples()->CheckTypeAndMergeFrom(it->second);
       }
     }
   }
@@ -828,9 +801,7 @@
       emmc_lifetime_fetcher_(emmc_lifetime_fetcher),
       stateful_partition_info_fetcher_(stateful_partition_info_fetcher),
       cros_healthd_data_fetcher_(cros_healthd_data_fetcher),
-      power_manager_(chromeos::PowerManagerClient::Get()),
-      runtime_probe_(
-          chromeos::DBusThreadManager::Get()->GetRuntimeProbeClient()) {
+      power_manager_(chromeos::PowerManagerClient::Get()) {
   // protected fields of `StatusCollector`.
   max_stored_past_activity_interval_ = kMaxStoredPastActivityInterval;
   max_stored_future_activity_interval_ = kMaxStoredFutureActivityInterval;
@@ -855,10 +826,6 @@
   if (tpm_status_fetcher_.is_null())
     tpm_status_fetcher_ = base::BindRepeating(&ReadTpmStatus);
 
-  if (probe_data_fetcher_.is_null())
-    probe_data_fetcher_ = base::BindRepeating(
-        &DeviceStatusCollector::FetchProbeData, weak_factory_.GetWeakPtr());
-
   if (emmc_lifetime_fetcher_.is_null())
     emmc_lifetime_fetcher_ = base::BindRepeating(&ReadDiskLifeTimeEstimation);
 
@@ -1216,12 +1183,15 @@
   sample->timestamp = timestamp;
 
   if (report_power_status_) {
-    runtime_probe::ProbeRequest request;
-    request.add_categories(runtime_probe::ProbeRequest::battery);
-    runtime_probe_->ProbeCategories(
-        request, base::BindOnce(&DeviceStatusCollector::SampleProbeData,
-                                weak_factory_.GetWeakPtr(), std::move(sample),
-                                SamplingProbeResultCallback()));
+    std::vector<chromeos::cros_healthd::mojom::ProbeCategoryEnum>
+        categories_to_probe = {
+            chromeos::cros_healthd::mojom::ProbeCategoryEnum::kBattery};
+    chromeos::cros_healthd::ServiceConnection::GetInstance()
+        ->ProbeTelemetryInfo(
+            categories_to_probe,
+            base::BindOnce(&DeviceStatusCollector::SampleProbeData,
+                           weak_factory_.GetWeakPtr(), std::move(sample),
+                           SamplingProbeResultCallback()));
   } else {
     base::PostTaskAndReplyWithResult(
         FROM_HERE,
@@ -1236,30 +1206,29 @@
 void DeviceStatusCollector::SampleProbeData(
     std::unique_ptr<SampledData> sample,
     SamplingProbeResultCallback callback,
-    base::Optional<runtime_probe::ProbeResult> result) {
+    chromeos::cros_healthd::mojom::TelemetryInfoPtr result) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  if (!result.has_value())
-    return;
-  if (result.value().error() != runtime_probe::RUNTIME_PROBE_ERROR_NOT_SET)
+
+  if (result.is_null())
     return;
 
-  for (const auto& battery : result.value().battery()) {
-    if (battery.name() != kGenericDeviceName)
-      continue;
-    enterprise_management::BatterySample battery_sample;
-    battery_sample.set_timestamp(sample->timestamp.ToJavaTime());
-    // Convert uV to mV
-    battery_sample.set_voltage(battery.values().voltage_now() / 1000);
-    // Convert uAh to mAh
-    battery_sample.set_remaining_capacity(battery.values().charge_now() / 1000);
-    // Convert 0.1 Kelvin to Celsius
-    battery_sample.set_temperature(
-        (battery.values().temperature_smart() - 2731) / 10);
-    sample->battery_samples[battery.name()] = battery_sample;
-  }
+  const auto& battery = result->battery_info;
+  enterprise_management::BatterySample battery_sample;
+  battery_sample.set_timestamp(sample->timestamp.ToJavaTime());
+  // Convert V to mV:
+  battery_sample.set_voltage(std::lround(battery->voltage_now * 1000));
+  // Convert Ah to mAh:
+  battery_sample.set_remaining_capacity(
+      std::lround(battery->charge_now * 1000));
+  // Convert 0.1 Kelvin to Celsius:
+  battery_sample.set_temperature((battery->temperature_smart - 2731) / 10);
+  sample->battery_samples[battery->model_name] = battery_sample;
+
   SamplingCallback completion_callback;
-  if (!callback.is_null())
-    completion_callback = base::BindOnce(std::move(callback), result);
+  if (!callback.is_null()) {
+    completion_callback =
+        base::BindOnce(std::move(callback), std::move(result));
+  }
 
   // PowerManagerClient::Observer::PowerChanged can be called as a result of
   // power_manager_->RequestStatusUpdate() as well as for other reasons,
@@ -1339,39 +1308,27 @@
       ProbeCategoryEnum::kCachedVpdData};
   if (report_storage_status_)
     categories_to_probe.push_back(ProbeCategoryEnum::kNonRemovableBlockDevices);
-
-  chromeos::cros_healthd::ServiceConnection::GetInstance()->ProbeTelemetryInfo(
-      categories_to_probe, std::move(callback));
-}
-
-void DeviceStatusCollector::FetchProbeData(ProbeDataReceiver callback) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  runtime_probe::ProbeRequest request;
   if (report_power_status_)
-    request.add_categories(runtime_probe::ProbeRequest::battery);
+    categories_to_probe.push_back(ProbeCategoryEnum::kBattery);
 
-  // Note that we could send a probe request without any categories. The reason
-  // for that is that the OnProbeDataFetched callback also samples CPU
-  // temperature independently of querying runtime_probe. Since cros_healthd is
-  // replacing runtime_probe in DeviceStatusCollector, it doesn't make sense to
-  // refactor the runtime_probe code to fix this oddity at the moment.
   auto sample = std::make_unique<SampledData>();
   sample->timestamp = base::Time::Now();
   auto completion_callback =
       base::BindOnce(&DeviceStatusCollector::OnProbeDataFetched,
                      weak_factory_.GetWeakPtr(), std::move(callback));
 
-  runtime_probe_->ProbeCategories(
-      request, base::BindOnce(&DeviceStatusCollector::SampleProbeData,
-                              weak_factory_.GetWeakPtr(), std::move(sample),
-                              std::move(completion_callback)));
+  chromeos::cros_healthd::ServiceConnection::GetInstance()->ProbeTelemetryInfo(
+      categories_to_probe,
+      base::BindOnce(&DeviceStatusCollector::SampleProbeData,
+                     weak_factory_.GetWeakPtr(), std::move(sample),
+                     std::move(completion_callback)));
 }
 
 void DeviceStatusCollector::OnProbeDataFetched(
-    ProbeDataReceiver callback,
-    base::Optional<runtime_probe::ProbeResult> reply) {
+    CrosHealthdDataReceiver callback,
+    chromeos::cros_healthd::mojom::TelemetryInfoPtr reply) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  std::move(callback).Run(reply, sampled_data_);
+  std::move(callback).Run(std::move(reply), sampled_data_);
 }
 
 void DeviceStatusCollector::ReportingUsersChanged() {
@@ -1698,7 +1655,6 @@
 
   if (report_power_status_ || report_storage_status_) {
     state->FetchEMMCLifeTime(emmc_lifetime_fetcher_);
-    state->FetchProbeData(probe_data_fetcher_);
     state->FetchCrosHealthdData(cros_healthd_data_fetcher_);
   } else {
     // Sample CPU temperature in a background thread.
diff --git a/chrome/browser/chromeos/policy/status_collector/device_status_collector.h b/chrome/browser/chromeos/policy/status_collector/device_status_collector.h
index c00f2d53..2f528b1f 100644
--- a/chrome/browser/chromeos/policy/status_collector/device_status_collector.h
+++ b/chrome/browser/chromeos/policy/status_collector/device_status_collector.h
@@ -28,8 +28,6 @@
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chromeos/dbus/cryptohome/cryptohome_client.h"
 #include "chromeos/dbus/power/power_manager_client.h"
-#include "chromeos/dbus/runtime_probe/runtime_probe.pb.h"
-#include "chromeos/dbus/runtime_probe_client.h"
 #include "chromeos/services/cros_healthd/public/mojom/cros_healthd.mojom.h"
 #include "components/policy/proto/device_management_backend.pb.h"
 #include "components/prefs/pref_member.h"
@@ -149,17 +147,10 @@
   // Gets the TpmStatusInfo and passes it to TpmStatusReceiver.
   using TpmStatusFetcher = base::RepeatingCallback<void(TpmStatusReceiver)>;
 
-  // Format of the function that asynchronously receives ProbeResult / sampled
-  // data.
-  using ProbeDataReceiver = base::OnceCallback<void(
-      const base::Optional<runtime_probe::ProbeResult>&,
-      const base::circular_deque<std::unique_ptr<SampledData>>&)>;
-  // Gets the ProbeResult/sampled data and passes it to ProbeDataReceiver.
-  using ProbeDataFetcher = base::RepeatingCallback<void(ProbeDataReceiver)>;
-
   // Format of the function that asynchronously receives data from cros_healthd.
-  using CrosHealthdDataReceiver =
-      base::OnceCallback<void(chromeos::cros_healthd::mojom::TelemetryInfoPtr)>;
+  using CrosHealthdDataReceiver = base::OnceCallback<void(
+      chromeos::cros_healthd::mojom::TelemetryInfoPtr,
+      const base::circular_deque<std::unique_ptr<SampledData>>&)>;
   // Gets the data from cros_healthd and passes it to CrosHealthdDataReceiver.
   using CrosHealthdDataFetcher =
       base::RepeatingCallback<void(CrosHealthdDataReceiver)>;
@@ -240,7 +231,7 @@
   // Callbacks used during sampling data collection, that allows to pass
   // additional data using partial function application.
   using SamplingProbeResultCallback =
-      base::OnceCallback<void(base::Optional<runtime_probe::ProbeResult>)>;
+      base::OnceCallback<void(chromeos::cros_healthd::mojom::TelemetryInfoPtr)>;
   using SamplingCallback = base::OnceCallback<void()>;
 
   // Clears the cached hardware resource usage.
@@ -296,11 +287,11 @@
   // Callback invoked to update our cpu usage information.
   void ReceiveCPUStatistics(const std::string& statistics);
 
-  // Callback for RuntimeProbe that samples probe live data. |callback| will
+  // Callback for CrosHealthd that samples probe live data. |callback| will
   // be called once all sampling is finished.
   void SampleProbeData(std::unique_ptr<SampledData> sample,
                        SamplingProbeResultCallback callback,
-                       base::Optional<runtime_probe::ProbeResult> result);
+                       chromeos::cros_healthd::mojom::TelemetryInfoPtr result);
 
   // Callback triggered from PowerManagedClient that samples battery discharge
   // rate. |callback| will be called once all sampling is finished.
@@ -321,14 +312,11 @@
   // cros_healthd and passes it to |callback|.
   void FetchCrosHealthdData(CrosHealthdDataReceiver callback);
 
-  // ProbeDataReceiver interface implementation, fetches data from
-  // RuntimeProbe passes it to |callback| via OnProbeDataFetched().
-  void FetchProbeData(ProbeDataReceiver callback);
-
-  // Callback for RuntimeProbe that performs final sampling and
+  // Callback for CrosHealthd that performs final sampling and
   // actually invokes |callback|.
-  void OnProbeDataFetched(ProbeDataReceiver callback,
-                          base::Optional<runtime_probe::ProbeResult> reply);
+  void OnProbeDataFetched(
+      CrosHealthdDataReceiver callback,
+      chromeos::cros_healthd::mojom::TelemetryInfoPtr reply);
 
   // Callback invoked when reporting users pref is changed.
   void ReportingUsersChanged();
@@ -403,8 +391,6 @@
 
   TpmStatusFetcher tpm_status_fetcher_;
 
-  ProbeDataFetcher probe_data_fetcher_;
-
   EMMCLifetimeFetcher emmc_lifetime_fetcher_;
 
   StatefulPartitionInfoFetcher stateful_partition_info_fetcher_;
@@ -416,9 +402,6 @@
   // Power manager client. Used to listen to power changed events.
   chromeos::PowerManagerClient* const power_manager_;
 
-  // Runtime probe client. Used to fetch hardware data.
-  chromeos::RuntimeProbeClient* const runtime_probe_;
-
   // The most recent CPU readings.
   uint64_t last_cpu_active_ = 0;
   uint64_t last_cpu_idle_ = 0;
diff --git a/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc b/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc
index 7848efa..2012b66 100644
--- a/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc
+++ b/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc
@@ -359,35 +359,35 @@
 void GetEmptyCrosHealthdData(
     policy::DeviceStatusCollector::CrosHealthdDataReceiver receiver) {
   chromeos::cros_healthd::mojom::TelemetryInfoPtr empty_info;
-  std::move(receiver).Run(std::move(empty_info));
+  base::circular_deque<std::unique_ptr<policy::SampledData>> empty_samples;
+  std::move(receiver).Run(std::move(empty_info), empty_samples);
 }
 
 void GetFakeCrosHealthdData(
-    const std::string& sku_number,
-    const std::string& path,
-    int size,
-    const std::string& type,
-    uint8_t manufacturer_id,
-    const std::string& name,
-    int serial,
+    const chromeos::cros_healthd::mojom::BatteryInfo& battery_info,
+    const chromeos::cros_healthd::mojom::CachedVpdInfo& cached_vpd_info,
+    const chromeos::cros_healthd::mojom::NonRemovableBlockDeviceInfo&
+        storage_info,
+    const em::CPUTempInfo& cpu_sample,
+    const em::BatterySample& battery_sample,
     policy::DeviceStatusCollector::CrosHealthdDataReceiver receiver) {
-  chromeos::cros_healthd::mojom::NonRemovableBlockDeviceInfoPtr vector_init[] =
-      {chromeos::cros_healthd::mojom::NonRemovableBlockDeviceInfo::New(
-          path, size, type, manufacturer_id, name, serial)};
+  std::vector<chromeos::cros_healthd::mojom::NonRemovableBlockDeviceInfoPtr>
+      storage_vector;
+  storage_vector.push_back(storage_info.Clone());
+  base::Optional<std::vector<
+      chromeos::cros_healthd::mojom::NonRemovableBlockDeviceInfoPtr>>
+      block_device_info(std::move(storage_vector));
   chromeos::cros_healthd::mojom::TelemetryInfo fake_info(
-      chromeos::cros_healthd::mojom::BatteryInfo::New(), /* battery_info */
-      base::Optional<std::vector<
-          chromeos::cros_healthd::mojom::NonRemovableBlockDeviceInfoPtr>>(
-          std::vector<
-              chromeos::cros_healthd::mojom::NonRemovableBlockDeviceInfoPtr>{
-              std::make_move_iterator(std::begin(vector_init)),
-              std::make_move_iterator(
-                  std::end(vector_init))}), /* block_device_info */
-      chromeos::cros_healthd::mojom::CachedVpdInfo::New(
-          sku_number) /* vpd_info */
-  );
+      battery_info.Clone(), std::move(block_device_info),
+      cached_vpd_info.Clone());
 
-  std::move(receiver).Run(fake_info.Clone());
+  auto sample = std::make_unique<policy::SampledData>();
+  sample->cpu_samples[cpu_sample.cpu_label()] = cpu_sample;
+  sample->battery_samples[battery_info.model_name] = battery_sample;
+  base::circular_deque<std::unique_ptr<policy::SampledData>> samples;
+  samples.push_back(std::move(sample));
+
+  std::move(receiver).Run(fake_info.Clone(), samples);
 }
 
 }  // namespace
@@ -2399,13 +2399,64 @@
 TEST_F(DeviceStatusCollectorTest, TestCrosHealthdInfo) {
   // Create a fake response from cros_healthd and populate it with some
   // arbitrary values.
+
+  // Cached VPD test values.
   constexpr char kFakeSkuNumber[] = "fake_sku_number";
-  constexpr char kFakePath[] = "fake_path";
-  constexpr int kFakeSize = 123;
-  constexpr char kFakeType[] = "fake_type";
-  constexpr uint8_t kFakeManfid = 2;
-  constexpr char kFakeName[] = "fake_name";
-  constexpr int kFakeSerial = 789;
+
+  // Storage test values.
+  constexpr char kFakeStoragePath[] = "fake_storage_path";
+  constexpr int kFakeStorageSize = 123;
+  constexpr char kFakeStorageType[] = "fake_storage_type";
+  constexpr uint8_t kFakeStorageManfid = 2;
+  constexpr char kFakeStorageName[] = "fake_storage_name";
+  constexpr int kFakeStorageSerial = 789;
+
+  // Battery test values.
+  constexpr int kFakeCycleCount = 3;
+  constexpr int kExpectedVoltageNow = 12574;                        // (mV)
+  constexpr double kFakeVoltageNow = kExpectedVoltageNow / 1000.0;  // (V)
+  constexpr char kFakeBatteryVendor[] = "fake_battery_vendor";
+  constexpr char kFakeBatterySerial[] = "fake_battery_serial";
+  constexpr int kExpectedChargeFullDesign = 5275;  // (mAh)
+  constexpr double kFakeChargeFullDesign =
+      kExpectedChargeFullDesign / 1000.0;                           // (Ah)
+  constexpr int kExpectedChargeFull = 5292;                         // (mAh)
+  constexpr double kFakeChargeFull = kExpectedChargeFull / 1000.0;  // (Ah)
+  constexpr int kExpectedVoltageMinDesign = 11550;                  // (mV)
+  constexpr double kFakeVoltageMinDesign =
+      kExpectedVoltageMinDesign / 1000.0;  // (V)
+  constexpr int kFakeManufactureDateSmart = 19718;
+  constexpr int kFakeTemperatureSmart = 3004;
+  constexpr char kFakeBatteryModel[] = "fake_battery_model";
+  constexpr int kExpectedChargeNow = 5281;                        // (mAh)
+  constexpr double kFakeChargeNow = kExpectedChargeNow / 1000.0;  // (Ah)
+
+  // CPU Temperature test values.
+  constexpr char kFakeCpuLabel[] = "fake_cpu_label";
+  constexpr int kFakeCpuTemp = 91832;
+  constexpr int kFakeCpuTimestamp = 912;
+
+  chromeos::cros_healthd::mojom::BatteryInfo battery_info(
+      kFakeCycleCount, kFakeVoltageNow, kFakeBatteryVendor, kFakeBatterySerial,
+      kFakeChargeFullDesign, kFakeChargeFull, kFakeVoltageMinDesign,
+      kFakeManufactureDateSmart, kFakeTemperatureSmart, kFakeBatteryModel,
+      kFakeChargeNow);
+  chromeos::cros_healthd::mojom::CachedVpdInfo cached_vpd_info(kFakeSkuNumber);
+  chromeos::cros_healthd::mojom::NonRemovableBlockDeviceInfo storage_info(
+      kFakeStoragePath, kFakeStorageSize, kFakeStorageType, kFakeStorageManfid,
+      kFakeStorageName, kFakeStorageSerial);
+
+  // Create a fake sample to test with.
+  em::CPUTempInfo fake_cpu_temp_sample;
+  fake_cpu_temp_sample.set_cpu_label(kFakeCpuLabel);
+  fake_cpu_temp_sample.set_cpu_temp(kFakeCpuTemp);
+  fake_cpu_temp_sample.set_timestamp(kFakeCpuTimestamp);
+  em::BatterySample fake_battery_sample;
+  // Convert from V to mV.
+  fake_battery_sample.set_voltage(kExpectedVoltageNow);
+  // Convert from Ah to mAh.
+  fake_battery_sample.set_remaining_capacity(kExpectedChargeNow);
+  fake_battery_sample.set_temperature(kFakeTemperatureSmart);
 
   RestartStatusCollector(
       base::BindRepeating(&GetEmptyVolumeInfo),
@@ -2415,9 +2466,9 @@
       base::BindRepeating(&GetEmptyTpmStatus),
       base::BindRepeating(&GetEmptyEMMCLifetimeEstimation),
       base::BindRepeating(&GetEmptyStatefulPartitionInfo),
-      base::BindRepeating(&GetFakeCrosHealthdData, kFakeSkuNumber, kFakePath,
-                          kFakeSize, kFakeType, kFakeManfid, kFakeName,
-                          kFakeSerial));
+      base::BindRepeating(&GetFakeCrosHealthdData, battery_info,
+                          cached_vpd_info, storage_info, fake_cpu_temp_sample,
+                          fake_battery_sample));
 
   // If neither kReportDevicePowerStatus nor kReportDeviceStorageStatus are set,
   // expect that the data from cros_healthd isn't present in the protobuf.
@@ -2426,6 +2477,7 @@
   scoped_testing_cros_settings_.device_settings()->SetBoolean(
       chromeos::kReportDeviceStorageStatus, false);
   GetStatus();
+  EXPECT_FALSE(device_status_.has_power_status());
   EXPECT_FALSE(device_status_.has_storage_status());
   EXPECT_FALSE(device_status_.has_system_status());
 
@@ -2436,20 +2488,45 @@
   scoped_testing_cros_settings_.device_settings()->SetBoolean(
       chromeos::kReportDeviceStorageStatus, true);
   GetStatus();
+
+  // Check that the CPU temperature samples are stored correctly.
+  ASSERT_EQ(device_status_.cpu_temp_infos_size(), 1);
+  const auto& cpu_sample = device_status_.cpu_temp_infos(0);
+  EXPECT_EQ(cpu_sample.cpu_label(), kFakeCpuLabel);
+  EXPECT_EQ(cpu_sample.cpu_temp(), kFakeCpuTemp);
+  EXPECT_EQ(cpu_sample.timestamp(), kFakeCpuTimestamp);
+
+  // Verify the battery data.
+  ASSERT_TRUE(device_status_.has_power_status());
+  ASSERT_EQ(device_status_.power_status().batteries_size(), 1);
+  const auto& battery = device_status_.power_status().batteries(0);
+  EXPECT_EQ(battery.serial(), kFakeBatterySerial);
+  EXPECT_EQ(battery.manufacturer(), kFakeBatteryVendor);
+  EXPECT_EQ(battery.design_capacity(), kExpectedChargeFullDesign);
+  EXPECT_EQ(battery.full_charge_capacity(), kExpectedChargeFull);
+  EXPECT_EQ(battery.cycle_count(), kFakeCycleCount);
+  EXPECT_EQ(battery.design_min_voltage(), kExpectedVoltageMinDesign);
+  EXPECT_EQ(battery.manufacture_date(), "2018-08-06");
+
+  // Verify the battery sample data.
+  ASSERT_EQ(battery.samples_size(), 1);
+  const auto& battery_sample = battery.samples(0);
+  EXPECT_EQ(battery_sample.voltage(), kExpectedVoltageNow);
+  EXPECT_EQ(battery_sample.remaining_capacity(), kExpectedChargeNow);
+  EXPECT_EQ(battery_sample.temperature(), kFakeTemperatureSmart);
+
+  // Verify the storage data.
   ASSERT_TRUE(device_status_.has_storage_status());
   ASSERT_EQ(device_status_.storage_status().disks_size(), 1);
   const auto& disk = device_status_.storage_status().disks(0);
-  ASSERT_TRUE(disk.has_size());
-  EXPECT_EQ(disk.size(), kFakeSize);
-  ASSERT_TRUE(disk.has_type());
-  EXPECT_EQ(disk.type(), kFakeType);
-  ASSERT_TRUE(disk.has_manufacturer());
-  EXPECT_EQ(disk.manufacturer(), base::NumberToString(kFakeManfid));
-  ASSERT_TRUE(disk.has_model());
-  EXPECT_EQ(disk.model(), kFakeName);
-  ASSERT_TRUE(disk.has_serial());
-  EXPECT_EQ(disk.serial(), base::NumberToString(kFakeSerial));
-  ASSERT_TRUE(device_status_.system_status().has_vpd_sku_number());
+  EXPECT_EQ(disk.size(), kFakeStorageSize);
+  EXPECT_EQ(disk.type(), kFakeStorageType);
+  EXPECT_EQ(disk.manufacturer(), base::NumberToString(kFakeStorageManfid));
+  EXPECT_EQ(disk.model(), kFakeStorageName);
+  EXPECT_EQ(disk.serial(), base::NumberToString(kFakeStorageSerial));
+
+  // Verify the Cached VPD.
+  ASSERT_TRUE(device_status_.has_system_status());
   EXPECT_EQ(device_status_.system_status().vpd_sku_number(), kFakeSkuNumber);
 }
 
diff --git a/chrome/browser/data_saver/subresource_redirect_browsertest.cc b/chrome/browser/data_saver/subresource_redirect_browsertest.cc
index e00d470..4679dfd 100644
--- a/chrome/browser/data_saver/subresource_redirect_browsertest.cc
+++ b/chrome/browser/data_saver/subresource_redirect_browsertest.cc
@@ -4,19 +4,27 @@
 
 #include "base/task/thread_pool/thread_pool_instance.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings.h"
+#include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings_factory.h"
 #include "chrome/browser/metrics/subprocess_metrics_provider.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
-#include "chrome/common/chrome_features.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/test/browser_test_utils.h"
 #include "net/base/escape.h"
 #include "net/test/embedded_test_server/http_request.h"
 #include "net/test/embedded_test_server/http_response.h"
+#include "third_party/blink/public/common/features.h"
 
 namespace {
 
+// TODO(rajendrant): Add tests to verify subresource redirect is applied only
+// for data saver users and also not applied for incognito profiles.
+
 // Retries fetching |histogram_name| until it contains at least |count| samples.
+// TODO(rajendrant): Convert the tests to wait for image load to complete or the
+// page load complete, instead of waiting on the histograms.
 void RetryForHistogramUntilCountReached(base::HistogramTester* histogram_tester,
                                         const std::string& histogram_name,
                                         size_t count) {
@@ -41,50 +49,42 @@
 
 class SubresourceRedirectBrowserTest : public InProcessBrowserTest {
  public:
-  SubresourceRedirectBrowserTest() {}
+  explicit SubresourceRedirectBrowserTest(
+      const std::string& included_path_suffixes = "")
+      : included_path_suffixes_(included_path_suffixes),
+        https_server_(net::EmbeddedTestServer::TYPE_HTTPS),
+        compression_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
 
   void SetUp() override {
     // |http_server| setup.
-    http_server_.reset(
-        new net::EmbeddedTestServer(net::EmbeddedTestServer::TYPE_HTTP));
-    http_server_->ServeFilesFromSourceDirectory("chrome/test/data");
-
-    ASSERT_TRUE(http_server_->Start());
-
-    http_url_ = http_server_->GetURL("insecure.com", "/");
+    http_server_.ServeFilesFromSourceDirectory("chrome/test/data");
+    ASSERT_TRUE(http_server_.Start());
+    http_url_ = http_server_.GetURL("insecure.com", "/");
     ASSERT_TRUE(http_url_.SchemeIs(url::kHttpScheme));
 
     // |https_server| setup.
-    https_server_.reset(
-        new net::EmbeddedTestServer(net::EmbeddedTestServer::TYPE_HTTPS));
-    https_server_->ServeFilesFromSourceDirectory("chrome/test/data");
-
-    ASSERT_TRUE(https_server_->Start());
-
-    https_url_ = https_server_->GetURL("secure.com", "/");
+    https_server_.ServeFilesFromSourceDirectory("chrome/test/data");
+    ASSERT_TRUE(https_server_.Start());
+    https_url_ = https_server_.GetURL("secure.com", "/");
     ASSERT_TRUE(https_url_.SchemeIs(url::kHttpsScheme));
 
     // |compression_server| setup.
-    compression_server_.reset(
-        new net::EmbeddedTestServer(net::EmbeddedTestServer::TYPE_HTTPS));
-    compression_server_->RegisterRequestHandler(base::BindRepeating(
+    compression_server_.RegisterRequestHandler(base::BindRepeating(
         &SubresourceRedirectBrowserTest::HandleCompressionServerRequest,
         base::Unretained(this)));
-
-    ASSERT_TRUE(compression_server_->Start());
-
-    compression_url_ = compression_server_->GetURL("compression.com", "/");
+    ASSERT_TRUE(compression_server_.Start());
+    compression_url_ = compression_server_.GetURL("compression.com", "/");
     ASSERT_TRUE(compression_url_.SchemeIs(url::kHttpsScheme));
 
+    scoped_feature_list_.InitAndEnableFeatureWithParameters(
+        blink::features::kSubresourceRedirect,
+        {{"included_path_suffixes", included_path_suffixes_},
+         {"lite_page_subresource_origin", compression_url_.spec()}});
+
     InProcessBrowserTest::SetUp();
   }
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitchASCII("litepage-server-subresource-host",
-                                    compression_url_.spec());
-
-    command_line->AppendSwitch("enable-subresource-redirect");
-
     //  Need to resolve all 3 of the above servers to 127.0.0.1:port, and
     //  the servers themselves can't serve using 127.0.0.1:port as the
     //  compressed resource URLs rely on subdomains, and subdomains
@@ -92,17 +92,30 @@
     command_line->AppendSwitchASCII("host-rules", "MAP * 127.0.0.1");
   }
 
-  bool RunScriptExtractBool(const std::string& script) {
+  void EnableDataSaver(bool enabled) {
+    Profile* profile = Profile::FromBrowserContext(browser()->profile());
+
+    data_reduction_proxy::DataReductionProxySettings::
+        SetDataSaverEnabledForTesting(profile->GetPrefs(), enabled);
+    base::RunLoop().RunUntilIdle();
+  }
+
+  bool RunScriptExtractBool(const std::string& script,
+                            content::WebContents* web_contents = nullptr) {
     bool result;
-    EXPECT_TRUE(ExecuteScriptAndExtractBool(
-        browser()->tab_strip_model()->GetActiveWebContents(), script, &result));
+    if (!web_contents)
+      web_contents = browser()->tab_strip_model()->GetActiveWebContents();
+    EXPECT_TRUE(ExecuteScriptAndExtractBool(web_contents, script, &result));
     return result;
   }
 
-  std::string RunScriptExtractString(const std::string& script) {
+  std::string RunScriptExtractString(
+      const std::string& script,
+      content::WebContents* web_contents = nullptr) {
+    if (!web_contents)
+      web_contents = browser()->tab_strip_model()->GetActiveWebContents();
     std::string result;
-    EXPECT_TRUE(ExecuteScriptAndExtractString(
-        browser()->tab_strip_model()->GetActiveWebContents(), script, &result));
+    EXPECT_TRUE(ExecuteScriptAndExtractString(web_contents, script, &result));
     return result;
   }
 
@@ -112,10 +125,10 @@
   GURL request_url() const { return request_url_; }
 
   GURL HttpURLWithPath(const std::string& path) {
-    return http_server_->GetURL("insecure.com", path);
+    return http_server_.GetURL("insecure.com", path);
   }
   GURL HttpsURLWithPath(const std::string& path) {
-    return https_server_->GetURL("secure.com", path);
+    return https_server_.GetURL("secure.com", path);
   }
 
   void SetCompressionServerToFail() { compression_server_fail_ = true; }
@@ -124,7 +137,7 @@
 
  private:
   void TearDownOnMainThread() override {
-    EXPECT_TRUE(https_server_->ShutdownAndWaitUntilComplete());
+    EXPECT_TRUE(https_server_.ShutdownAndWaitUntilComplete());
 
     InProcessBrowserTest::TearDownOnMainThread();
   }
@@ -163,14 +176,17 @@
     return std::move(response);
   }
 
+  base::test::ScopedFeatureList scoped_feature_list_;
+  const std::string included_path_suffixes_;
+
   GURL compression_url_;
   GURL http_url_;
   GURL https_url_;
   GURL request_url_;
 
-  std::unique_ptr<net::EmbeddedTestServer> http_server_;
-  std::unique_ptr<net::EmbeddedTestServer> https_server_;
-  std::unique_ptr<net::EmbeddedTestServer> compression_server_;
+  net::EmbeddedTestServer http_server_;
+  net::EmbeddedTestServer https_server_;
+  net::EmbeddedTestServer compression_server_;
 
   base::HistogramTester histogram_tester_;
 
@@ -182,15 +198,8 @@
 class DifferentMediaInclusionSubresourceRedirectBrowserTest
     : public SubresourceRedirectBrowserTest {
  public:
-  void SetUp() override {
-    feature_list_.InitAndEnableFeatureWithParameters(
-        features::kSubresourceRedirectIncludedMediaSuffixes,
-        {{"included_path_suffixes", ".svg"}});
-    SubresourceRedirectBrowserTest::SetUp();
-  }
-
- private:
-  base::test::ScopedFeatureList feature_list_;
+  DifferentMediaInclusionSubresourceRedirectBrowserTest()
+      : SubresourceRedirectBrowserTest(".svg") {}
 };
 
 //  NOTE: It is indirectly verified that correct requests are being sent to
@@ -202,7 +211,7 @@
 //  compression server, which responds with HTTP_OK.
 IN_PROC_BROWSER_TEST_F(SubresourceRedirectBrowserTest,
                        TestHTMLLoadRedirectSuccess) {
-  base::RunLoop().RunUntilIdle();
+  EnableDataSaver(true);
   ui_test_utils::NavigateToURL(browser(),
                                HttpsURLWithPath("/load_image/image.html"));
 
@@ -228,6 +237,7 @@
 //  mock compression server creates a redirect to the original resource.
 IN_PROC_BROWSER_TEST_F(SubresourceRedirectBrowserTest,
                        TestHTMLLoadRedirectBypass) {
+  EnableDataSaver(true);
   ui_test_utils::NavigateToURL(
       browser(), HttpsURLWithPath("/load_image/private_url_image.html"));
 
@@ -245,24 +255,17 @@
             https_url().port());
 }
 
-//  This test loads image_js.html, which triggers a javascript request
-//  for image.png.  This triggers an internal redirect to the mocked
-//  compression server, which responds with HTTP_OK.
 IN_PROC_BROWSER_TEST_F(SubresourceRedirectBrowserTest,
-                       TestJavaScriptRedirectSuccess) {
+                       NoTriggerWhenDataSaverOff) {
+  EnableDataSaver(false);
   ui_test_utils::NavigateToURL(browser(),
-                               HttpsURLWithPath("/load_image/image_js.html"));
+                               HttpsURLWithPath("/load_image/image.html"));
 
-  RetryForHistogramUntilCountReached(
-      histogram_tester(), "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      2);
+  content::FetchHistogramsFromChildProcesses();
+  SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
 
-  histogram_tester()->ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", net::HTTP_OK, 1);
-
-  histogram_tester()->ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      net::HTTP_TEMPORARY_REDIRECT, 1);
+  histogram_tester()->ExpectTotalCount(
+      "SubresourceRedirect.CompressionAttempt.ResponseCode", 0);
 
   EXPECT_TRUE(RunScriptExtractBool("checkImage()"));
 
@@ -270,27 +273,28 @@
             https_url().port());
 }
 
-//  This test loads private_url_image.html, which triggers a javascript
-//  request for private_url_image.png.  This triggers an internal redirect
-//  to the mock compression server, which bypasses the request. The
-//  mock compression server creates a redirect to the original resource.
-IN_PROC_BROWSER_TEST_F(SubresourceRedirectBrowserTest,
-                       TestJavaScriptRedirectBypass) {
-  ui_test_utils::NavigateToURL(
-      browser(), HttpsURLWithPath("/load_image/private_url_image_js.html"));
+IN_PROC_BROWSER_TEST_F(SubresourceRedirectBrowserTest, NoTriggerInIncognito) {
+  EnableDataSaver(true);
+  auto* incognito_browser = CreateIncognitoBrowser();
+  ui_test_utils::NavigateToURL(incognito_browser,
+                               HttpsURLWithPath("/load_image/image.html"));
 
-  RetryForHistogramUntilCountReached(
-      histogram_tester(), "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      2);
+  content::FetchHistogramsFromChildProcesses();
+  SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
 
-  histogram_tester()->ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      net::HTTP_TEMPORARY_REDIRECT, 2);
+  histogram_tester()->ExpectTotalCount(
+      "SubresourceRedirect.CompressionAttempt.ResponseCode", 0);
 
-  EXPECT_TRUE(RunScriptExtractBool("checkImage()"));
+  EXPECT_TRUE(RunScriptExtractBool(
+      "checkImage()",
+      incognito_browser->tab_strip_model()->GetActiveWebContents()));
 
-  EXPECT_EQ(GURL(RunScriptExtractString("imageSrc()")).port(),
-            https_url().port());
+  EXPECT_EQ(
+      GURL(RunScriptExtractString(
+               "imageSrc()",
+               incognito_browser->tab_strip_model()->GetActiveWebContents()))
+          .port(),
+      https_url().port());
 }
 
 //  This test loads image.html, from a non secure site. This triggers a
@@ -298,6 +302,7 @@
 //  non-secure sites.
 IN_PROC_BROWSER_TEST_F(SubresourceRedirectBrowserTest,
                        NoTriggerOnNonSecureSite) {
+  EnableDataSaver(true);
   ui_test_utils::NavigateToURL(browser(),
                                HttpURLWithPath("/load_image/image.html"));
 
@@ -317,6 +322,7 @@
 //  request for icon.png.  There should be no internal redirect as favicons
 //  are not considered images by chrome.
 IN_PROC_BROWSER_TEST_F(SubresourceRedirectBrowserTest, NoTriggerOnNonImage) {
+  EnableDataSaver(true);
   ui_test_utils::NavigateToURL(
       browser(), HttpsURLWithPath("/favicon/page_with_favicon.html"));
 
@@ -336,6 +342,7 @@
 // 200 ok from the original resource.
 IN_PROC_BROWSER_TEST_F(SubresourceRedirectBrowserTest,
                        FallbackOnServerNotFound) {
+  EnableDataSaver(true);
   ui_test_utils::NavigateToURL(browser(),
                                HttpsURLWithPath("/load_image/fail_image.html"));
 
@@ -363,6 +370,7 @@
 //  server/network fails and returns nothing.
 IN_PROC_BROWSER_TEST_F(SubresourceRedirectBrowserTest,
                        FallbackOnServerFailure) {
+  EnableDataSaver(true);
   SetCompressionServerToFail();
 
   base::RunLoop().RunUntilIdle();
@@ -382,11 +390,32 @@
             https_url().port());
 }
 
+//  This test loads image_js.html, which triggers a javascript request
+//  for image.png for which subresource redirect will not be attempted.
+IN_PROC_BROWSER_TEST_F(SubresourceRedirectBrowserTest,
+                       NoTriggerOnJavaScriptImageRequest) {
+  EnableDataSaver(true);
+  ui_test_utils::NavigateToURL(browser(),
+                               HttpsURLWithPath("/load_image/image_js.html"));
+
+  content::FetchHistogramsFromChildProcesses();
+  SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
+
+  histogram_tester()->ExpectTotalCount(
+      "SubresourceRedirect.CompressionAttempt.ResponseCode", 0);
+
+  EXPECT_TRUE(RunScriptExtractBool("checkImage()"));
+
+  EXPECT_EQ(GURL(RunScriptExtractString("imageSrc()")).port(),
+            https_url().port());
+}
+
 //  This test loads image.html, but with
 //  SubresourceRedirectIncludedMediaSuffixes set to only allow .svg, so no
 //  internal redirect should occur.
 IN_PROC_BROWSER_TEST_F(DifferentMediaInclusionSubresourceRedirectBrowserTest,
-                       NoTriggerWhenNotIncludedInMeidaSuffixes) {
+                       NoTriggerWhenNotIncludedInMediaSuffixes) {
+  EnableDataSaver(true);
   ui_test_utils::NavigateToURL(browser(),
                                HttpsURLWithPath("/load_image/image.html"));
 
diff --git a/chrome/browser/devtools/chrome_devtools_session.cc b/chrome/browser/devtools/chrome_devtools_session.cc
index 846cdec..64a874a 100644
--- a/chrome/browser/devtools/chrome_devtools_session.cc
+++ b/chrome/browser/devtools/chrome_devtools_session.cc
@@ -111,9 +111,10 @@
     content::DevToolsAgentHostClient* client,
     content::DevToolsAgentHost* agent_host,
     std::unique_ptr<protocol::Serializable> message) {
-  std::string cbor = message->serialize(/*binary=*/true);
+  std::vector<uint8_t> cbor = message->serializeToBinary();
   if (client->UsesBinaryProtocol()) {
-    client->DispatchProtocolMessage(agent_host, cbor);
+    client->DispatchProtocolMessage(agent_host,
+                                    std::string(cbor.begin(), cbor.end()));
     return;
   }
   std::string json;
diff --git a/chrome/browser/extensions/api/tabs/tabs_test.cc b/chrome/browser/extensions/api/tabs/tabs_test.cc
index e908335..a1c9759e 100644
--- a/chrome/browser/extensions/api/tabs/tabs_test.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_test.cc
@@ -2096,7 +2096,7 @@
                                 blink::WebMouseEvent::Button::kLeft,
                                 gfx::Point(400, 400));
 
-  EXPECT_TRUE(navigation_manager.WaitForRequestStart());
+  ASSERT_TRUE(navigation_manager.WaitForRequestStart());
 
   browser()->tab_strip_model()->ActivateTabAt(
       0, {TabStripModel::GestureType::kOther});
@@ -2109,7 +2109,7 @@
 
   EXPECT_EQ(url, second_web_contents->GetVisibleURL());
 
-  // Wait for the TestNavigationManager-initiated navigation to complete to
+  // Wait for the TestNavigationManager-monitored navigation to complete to
   // avoid a race during browser teardown (see crbug.com/882213).
   navigation_manager.WaitForNavigationFinished();
 }
diff --git a/chrome/browser/extensions/chrome_component_extension_resource_manager_unittest.cc b/chrome/browser/extensions/chrome_component_extension_resource_manager_unittest.cc
index e07a24e..be957040 100644
--- a/chrome/browser/extensions/chrome_component_extension_resource_manager_unittest.cc
+++ b/chrome/browser/extensions/chrome_component_extension_resource_manager_unittest.cc
@@ -78,7 +78,7 @@
 TEST_F(ChromeComponentExtensionResourceManagerTest,
        IsComponentExtensionResource_Generated) {
   // Check that the file being used for testing is indeed a generated resource.
-  int generated_resource_id = IDR_PDF_SHARED_VARS_M_JS;
+  int generated_resource_id = IDR_PDF_SHARED_VARS_JS;
   bool found_resource = false;
   for (size_t i = 0; i < kComponentExtensionResourcesSize; ++i) {
     if (kComponentExtensionResources[i].value == generated_resource_id) {
@@ -100,7 +100,7 @@
 
   base::FilePath extension_path = resources_dir.AppendASCII("pdf");
   base::FilePath resource_path =
-      base::FilePath().AppendASCII("elements/shared-vars.m.js");
+      base::FilePath().AppendASCII("elements/shared-vars.js");
 
   // Check that the resource is classified as a component resource.
   int resource_id = 0;
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index a39d61d..4a6f668 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -2446,6 +2446,11 @@
     "expiry_milestone": 83
   },
   {
+    "name": "mix-browser-type-tabs",
+    "owners": [ "joelhockey", "tbuckley" ],
+    "expiry_milestone": 85
+  },
+  {
     "name": "mixed-content-setting",
     "owners": [ "carlosil", "estark" ],
     "expiry_milestone": 82
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index d8e8f95..c4270cf 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1273,6 +1273,11 @@
     "Loads the MimeHandlerView (the extension viewer for certain MIME types "
     "such as PDF) in a cross-process frame as opposed to a BrowserPlugin.";
 
+const char kMixBrowserTypeTabsName[] = "Mix browser type tabs";
+const char kMixBrowserTypeTabsDescription[] =
+    "Allows tabs to be dragged between any browsers that support tabs, "
+    "including apps";
+
 const char kMixedContentSiteSettingName[] =
     "Blockable mixed content switch as site setting";
 const char kMixedContentSiteSettingDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 07d04e7..f40f963 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -770,6 +770,9 @@
 extern const char kMimeHandlerViewInCrossProcessFrameName[];
 extern const char kMimeHandlerViewInCrossProcessFrameDescription[];
 
+extern const char kMixBrowserTypeTabsName[];
+extern const char kMixBrowserTypeTabsDescription[];
+
 extern const char kMixedContentSiteSettingName[];
 extern const char kMixedContentSiteSettingDescription[];
 
diff --git a/chrome/browser/fullscreen_aurax11.cc b/chrome/browser/fullscreen_aurax11.cc
index b186c00..10ac4a5 100644
--- a/chrome/browser/fullscreen_aurax11.cc
+++ b/chrome/browser/fullscreen_aurax11.cc
@@ -7,12 +7,12 @@
 #include <vector>
 
 #include "ui/gfx/native_widget_types.h"
-#include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h"
+#include "ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h"
 #include "ui/views/widget/widget.h"
 
 bool IsFullScreenMode() {
   std::vector<aura::Window*> all_windows =
-      views::DesktopWindowTreeHostX11::GetAllOpenWindows();
+      views::DesktopWindowTreeHostLinux::GetAllOpenWindows();
   // Only the topmost window is checked. This works fine in the most cases, but
   // it may return false when there are multiple displays and one display has
   // a fullscreen window but others don't. See: crbug.com/345484
diff --git a/chrome/browser/navigation_predictor/navigation_predictor.cc b/chrome/browser/navigation_predictor/navigation_predictor.cc
index f5ec02e..27b7a3b 100644
--- a/chrome/browser/navigation_predictor/navigation_predictor.cc
+++ b/chrome/browser/navigation_predictor/navigation_predictor.cc
@@ -6,7 +6,6 @@
 
 #include <memory>
 
-#include "base/bind.h"
 #include "base/logging.h"
 #include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_functions.h"
@@ -16,8 +15,6 @@
 #include "base/system/sys_info.h"
 #include "chrome/browser/navigation_predictor/navigation_predictor_keyed_service.h"
 #include "chrome/browser/navigation_predictor/navigation_predictor_keyed_service_factory.h"
-#include "chrome/browser/predictors/loading_predictor.h"
-#include "chrome/browser/predictors/loading_predictor_factory.h"
 #include "chrome/browser/prerender/prerender_handle.h"
 #include "chrome/browser/prerender/prerender_manager.h"
 #include "chrome/browser/prerender/prerender_manager_factory.h"
@@ -29,7 +26,6 @@
 #include "content/public/browser/web_contents.h"
 #include "mojo/public/cpp/bindings/message.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
-#include "net/base/features.h"
 #include "services/metrics/public/cpp/metrics_utils.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/metrics/public/cpp/ukm_recorder.h"
@@ -39,11 +35,6 @@
 
 namespace {
 
-// Holds back the actual preconenct, but records histograms and acts normally
-// otherwise.
-const base::Feature kNavigationPredictorPreconnectHoldback{
-    "NavigationPredictorPreconnectHoldback", base::FEATURE_DISABLED_BY_DEFAULT};
-
 bool IsMainFrame(content::RenderFrameHost* rfh) {
   // Don't use rfh->GetRenderViewHost()->GetMainFrame() here because
   // RenderViewHost is being deprecated and because in OOPIF,
@@ -205,16 +196,7 @@
           blink::features::kNavigationPredictor,
           "prefetch_url_score_threshold",
           0)),
-      preconnect_origin_score_threshold_(base::GetFieldTrialParamByFeatureAsInt(
-          blink::features::kNavigationPredictor,
-          "preconnect_origin_score_threshold",
-          0)),
-      same_origin_preconnecting_allowed_(
-          base::GetFieldTrialParamByFeatureAsBool(
-              blink::features::kNavigationPredictor,
-              "same_origin_preconnecting_allowed",
-              true)),
-      prefetch_after_preconnect_(base::GetFieldTrialParamByFeatureAsBool(
+      prefetch_enabled_(base::GetFieldTrialParamByFeatureAsBool(
           blink::features::kNavigationPredictor,
           "prefetch_after_preconnect",
           false)),
@@ -225,7 +207,6 @@
       render_frame_host_(render_frame_host) {
   DCHECK(browser_context_);
   DETACH_FROM_SEQUENCE(sequence_checker_);
-  DCHECK_LE(0, preconnect_origin_score_threshold_);
   DCHECK(render_frame_host_);
 
   if (!IsMainFrame(render_frame_host))
@@ -299,7 +280,7 @@
   static constexpr char histogram_name_non_dse[] =
       "NavigationPredictor.OnNonDSE.AccuracyActionTaken";
 
-  if (!prefetch_url_ && !preconnect_origin_) {
+  if (!prefetch_url_) {
     base::UmaHistogramEnumeration(source_is_default_search_engine_page_
                                       ? histogram_name_dse
                                       : histogram_name_non_dse,
@@ -307,25 +288,6 @@
     return;
   }
 
-  // Exactly one action must have been taken.
-  DCHECK(prefetch_url_.has_value() != preconnect_origin_.has_value());
-
-  if (preconnect_origin_) {
-    if (url::Origin::Create(target_url) == preconnect_origin_) {
-      base::UmaHistogramEnumeration(
-          source_is_default_search_engine_page_ ? histogram_name_dse
-                                                : histogram_name_non_dse,
-          ActionAccuracy::kPreconnectActionClickToSameOrigin);
-      return;
-    }
-
-    base::UmaHistogramEnumeration(
-        source_is_default_search_engine_page_ ? histogram_name_dse
-                                              : histogram_name_non_dse,
-        ActionAccuracy::kPreconnectActionClickToDifferentOrigin);
-    return;
-  }
-
   DCHECK(prefetch_url_);
   if (target_url == prefetch_url_.value()) {
     base::UmaHistogramEnumeration(
@@ -368,79 +330,22 @@
       prerender_handle_->OnNavigateAway();
       prerender_handle_.reset();
     }
-
-    // Stop any future preconnects while hidden.
-    timer_.Stop();
     return;
   }
 
   current_visibility_ = visibility;
 
-  // Previously, the visibility was HIDDEN, and now it is VISIBLE implying that
-  // the web contents that was fully hidden is now fully visible.
-  MaybePreconnectNow(Action::kPreconnectOnVisibilityChange);
   if (prefetch_url_.has_value()) {
     MaybePrefetch();
   }
 }
 
-void NavigationPredictor::MaybePreconnectNow(Action log_action) {
-  base::Optional<url::Origin> preconnect_origin = preconnect_origin_;
-
-  if (prefetch_url_ && !preconnect_origin) {
-    // Preconnect to the origin of the prefetch URL.
-    preconnect_origin = url::Origin::Create(prefetch_url_.value());
-  }
-
-  if (!preconnect_origin)
-    return;
-  if (preconnect_origin->scheme() != url::kHttpScheme &&
-      preconnect_origin->scheme() != url::kHttpsScheme) {
-    return;
-  }
-
+void NavigationPredictor::RecordAction(Action log_action) {
   std::string action_histogram_name =
       source_is_default_search_engine_page_
           ? "NavigationPredictor.OnDSE.ActionTaken"
           : "NavigationPredictor.OnNonDSE.ActionTaken";
   base::UmaHistogramEnumeration(action_histogram_name, log_action);
-
-  if (!same_origin_preconnecting_allowed_)
-    return;
-
-  auto* loading_predictor = predictors::LoadingPredictorFactory::GetForProfile(
-      Profile::FromBrowserContext(browser_context_));
-  GURL preconnect_url_serialized(preconnect_origin->Serialize());
-  DCHECK(preconnect_url_serialized.is_valid());
-  if (!base::FeatureList::IsEnabled(kNavigationPredictorPreconnectHoldback)) {
-    loading_predictor->PrepareForPageLoad(
-        preconnect_url_serialized, predictors::HintOrigin::NAVIGATION_PREDICTOR,
-        true);
-  }
-
-  if (current_visibility_ != content::Visibility::VISIBLE)
-    return;
-
-  // The delay beyond the idle socket timeout that net uses when
-  // re-preconnecting. If negative, no retries occur.
-  int retry_delay_ms = base::GetFieldTrialParamByFeatureAsInt(
-      blink::features::kNavigationPredictor, "retry_preconnect_wait_time_ms",
-      50);
-
-  if (retry_delay_ms < 0) {
-    return;
-  }
-
-  // Set/Reset the timer to fire after the preconnect times out. Add an extra
-  // delay to make sure the preconnect has expired if it wasn't used.
-  timer_.Start(
-      FROM_HERE,
-      base::TimeDelta::FromSeconds(base::GetFieldTrialParamByFeatureAsInt(
-          net::features::kNetUnusedIdleSocketTimeout,
-          "unused_idle_socket_timeout_seconds", 60)) +
-          base::TimeDelta::FromMilliseconds(retry_delay_ms),
-      base::BindOnce(&NavigationPredictor::MaybePreconnectNow,
-                     base::Unretained(this), Action::kPreconnectAfterTimeout));
 }
 
 void NavigationPredictor::MaybeSendMetricsToUkm() const {
@@ -972,7 +877,6 @@
           ? "NavigationPredictor.OnDSE.ActionTaken"
           : "NavigationPredictor.OnNonDSE.ActionTaken";
 
-  DCHECK(!preconnect_origin_.has_value());
   DCHECK(!prefetch_url_.has_value());
 
   NotifyPredictionUpdated(sorted_navigation_scores);
@@ -981,28 +885,19 @@
   prefetch_url_ = GetUrlToPrefetch(document_url, sorted_navigation_scores);
   if (prefetch_url_.has_value()) {
     DCHECK_EQ(document_url.host(), prefetch_url_->host());
-    MaybePreconnectNow(Action::kPrefetch);
+    RecordAction(Action::kPrefetch);
     MaybePrefetch();
     return;
   }
 
-  // Compute preconnect origin only if there is no valid prefetch URL.
-  preconnect_origin_ =
-      GetOriginToPreconnect(document_url, sorted_navigation_scores);
-  if (preconnect_origin_.has_value()) {
-    DCHECK_EQ(document_url.host(), preconnect_origin_->host());
-    MaybePreconnectNow(Action::kPreconnect);
-    return;
-  }
-
-  base::UmaHistogramEnumeration(action_histogram_name, Action::kNone);
+  RecordAction(Action::kNone);
 }
 
 void NavigationPredictor::MaybePrefetch() {
   // If prefetches aren't allowed here, this URL has already
   // been prefetched, or the current tab is hidden,
   // we shouldn't prefetch again.
-  if (!prefetch_after_preconnect_ || prefetch_url_prefetched_ ||
+  if (!prefetch_enabled_ || prefetch_url_prefetched_ ||
       current_visibility_ == content::Visibility::HIDDEN) {
     return;
   }
@@ -1071,90 +966,6 @@
   return url_to_prefetch;
 }
 
-base::Optional<url::Origin> NavigationPredictor::GetOriginToPreconnect(
-    const GURL& document_url,
-    const std::vector<std::unique_ptr<NavigationScore>>&
-        sorted_navigation_scores) const {
-  // On search engine results page, next navigation is likely to be a different
-  // origin. Currently, the preconnect is only allowed for same origins. Hence,
-  // preconnect is currently disabled on search engine results page.
-  if (source_is_default_search_engine_page_)
-    return base::nullopt;
-
-  if (base::GetFieldTrialParamByFeatureAsBool(
-          blink::features::kNavigationPredictor, "preconnect_skip_link_scores",
-          true)) {
-    return url::Origin::Create(document_url);
-  }
-
-  // Compute preconnect score for each origins: Multiple anchor elements on
-  // the webpage may point to the same origin. The preconnect score for an
-  // origin is computed by taking sum of score of all anchor elements that
-  // point to that origin.
-  std::map<url::Origin, double> preconnect_score_by_origin_map;
-  for (const auto& navigation_score : sorted_navigation_scores) {
-    const url::Origin origin = url::Origin::Create(navigation_score->url);
-
-    auto iter = preconnect_score_by_origin_map.find(origin);
-    if (iter == preconnect_score_by_origin_map.end()) {
-      preconnect_score_by_origin_map[origin] = navigation_score->score;
-    } else {
-      double& existing_metric = iter->second;
-      existing_metric += navigation_score->score;
-    }
-  }
-
-  struct ScoreByOrigin {
-    url::Origin origin;
-    double score;
-
-    ScoreByOrigin(const url::Origin& origin, double score)
-        : origin(origin), score(score) {}
-  };
-
-  // |sorted_preconnect_scores| would contain preconnect scores of different
-  // origins sorted in descending order of the preconnect score.
-  std::vector<ScoreByOrigin> sorted_preconnect_scores;
-
-  // First copy all entries from |preconnect_score_by_origin_map| to
-  // |sorted_preconnect_scores|.
-  for (const auto& score_by_origin_map_entry : preconnect_score_by_origin_map) {
-    ScoreByOrigin entry(score_by_origin_map_entry.first,
-                        score_by_origin_map_entry.second);
-    sorted_preconnect_scores.push_back(entry);
-  }
-
-  if (sorted_preconnect_scores.empty())
-    return base::nullopt;
-
-  // Sort scores by the calculated preconnect score in descending order.
-  std::sort(sorted_preconnect_scores.begin(), sorted_preconnect_scores.end(),
-            [](const auto& a, const auto& b) { return a.score > b.score; });
-
-#if DCHECK_IS_ON()
-  // |sum_of_scores| must be close to the total score of 100.
-  double sum_of_scores = 0.0;
-  for (const auto& score_by_origin : sorted_preconnect_scores)
-    sum_of_scores += score_by_origin.score;
-  // Allow an error of 2.0. i.e., |sum_of_scores| is expected to be between 98
-  // and 102.
-  DCHECK_GE(2.0, std::abs(sum_of_scores - 100));
-#endif
-
-  // Connect to the origin with highest score provided the origin is same
-  // as the document origin.
-  if (sorted_preconnect_scores[0].origin != url::Origin::Create(document_url)) {
-    return base::nullopt;
-  }
-
-  // If the prediction score of the highest scoring origin is less than the
-  // threshold, then return.
-  if (sorted_preconnect_scores[0].score < preconnect_origin_score_threshold_) {
-    return base::nullopt;
-  }
-
-  return sorted_preconnect_scores[0].origin;
-}
 
 void NavigationPredictor::RecordMetricsOnLoad(
     const blink::mojom::AnchorElementMetrics& metric) const {
diff --git a/chrome/browser/navigation_predictor/navigation_predictor.h b/chrome/browser/navigation_predictor/navigation_predictor.h
index 0e58005..c408e937 100644
--- a/chrome/browser/navigation_predictor/navigation_predictor.h
+++ b/chrome/browser/navigation_predictor/navigation_predictor.h
@@ -13,7 +13,6 @@
 #include "base/optional.h"
 #include "base/sequence_checker.h"
 #include "base/time/time.h"
-#include "base/timer/timer.h"
 #include "content/public/browser/visibility.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -57,13 +56,13 @@
   enum class Action {
     kUnknown = 0,
     kNone = 1,
-    kPreresolve = 2,
-    kPreconnect = 3,
+    // DEPRECATED: kPreresolve = 2,
+    // DEPRECATED: kPreconnect = 3,
     kPrefetch = 4,
-    kPreconnectOnVisibilityChange = 5,
-    kDeprecatedPreconnectOnAppForeground = 6,  // Deprecated.
-    kPreconnectAfterTimeout = 7,
-    kMaxValue = kPreconnectAfterTimeout,
+    // DEPRECATED: kPreconnectOnVisibilityChange = 5,
+    // DEPRECATED: kPreconnectOnAppForeground = 6,  // Deprecated.
+    // DEPRECATED: kPreconnectAfterTimeout = 7,
+    kMaxValue = kPrefetch,
   };
 
   // Enum describing the accuracy of actions taken by the navigation predictor.
@@ -88,18 +87,15 @@
 
     // Navigation predictor preconnected to an origin, and an anchor element was
     // clicked whose URL had the same origin as the preconnected origin.
-    kPreconnectActionClickToSameOrigin = 4,
+    // DEPRECATED: kPreconnectActionClickToSameOrigin = 4,
 
     // Navigation predictor preconnected to an origin, and an anchor element was
     // clicked whose URL had a different origin than the preconnected origin.
-    kPreconnectActionClickToDifferentOrigin = 5,
-    kMaxValue = kPreconnectActionClickToDifferentOrigin,
+    // DEPRECATED: kPreconnectActionClickToDifferentOrigin = 5,
+    kMaxValue = kPrefetchActionClickToDifferentOrigin,
   };
 
  protected:
-  // Origin that we decided to preconnect to.
-  base::Optional<url::Origin> preconnect_origin_;
-
   // URL that we decided to prefetch.
   base::Optional<GURL> prefetch_url_;
 
@@ -166,11 +162,6 @@
       const std::vector<std::unique_ptr<NavigationScore>>&
           sorted_navigation_scores) const;
 
-  base::Optional<url::Origin> GetOriginToPreconnect(
-      const GURL& document_url,
-      const std::vector<std::unique_ptr<NavigationScore>>&
-          sorted_navigation_scores) const;
-
   // Record anchor element metrics on page load.
   void RecordMetricsOnLoad(
       const blink::mojom::AnchorElementMetrics& metric) const;
@@ -186,8 +177,8 @@
   // content::WebContentsObserver:
   void OnVisibilityChanged(content::Visibility visibility) override;
 
-  // MaybePreconnectNow preconnects to an origin server if it's allowed.
-  void MaybePreconnectNow(Action log_action);
+  // Records metrics on which action the predictor is taking.
+  void RecordAction(Action log_action);
 
   // Sends metrics to the UKM id at |ukm_source_id_|.
   void MaybeSendMetricsToUkm() const;
@@ -271,17 +262,8 @@
   // they are not comparable.
   const int prefetch_url_score_threshold_;
 
-  // Minimum preconnect score that the origin should have for preconnect. Note
-  // that scores of origins are computed differently from scores of URLs, so
-  // they are not comparable.
-  const int preconnect_origin_score_threshold_;
-
-  // True if |this| is allowed to preconnect to same origin hosts.
-  const bool same_origin_preconnecting_allowed_;
-
-  // True if |this| should use the PrerenderManager to prefetch after
-  // a preconnect.
-  const bool prefetch_after_preconnect_;
+  // True if |this| should use the PrerenderManager to prefetch.
+  const bool prefetch_enabled_;
 
   // True by default, otherwise navigation scores will not be normalized
   // by the sum of metrics weights nor normalized from 0 to 100 across
@@ -302,9 +284,6 @@
   // Current visibility state of the web contents.
   content::Visibility current_visibility_;
 
-  // Used to preconnect regularly.
-  base::OneShotTimer timer_;
-
   // PrerenderHandle returned after completing a prefetch in PrerenderManager.
   std::unique_ptr<prerender::PrerenderHandle> prerender_handle_;
 
diff --git a/chrome/browser/navigation_predictor/navigation_predictor_browsertest.cc b/chrome/browser/navigation_predictor/navigation_predictor_browsertest.cc
index d6a07a1..2d5ccda 100644
--- a/chrome/browser/navigation_predictor/navigation_predictor_browsertest.cc
+++ b/chrome/browser/navigation_predictor/navigation_predictor_browsertest.cc
@@ -52,29 +52,6 @@
   }
 }
 
-// Retries fetching |histogram_name| until it contains at least |count| samples.
-void RetryForHistogramBucketUntilCountReached(
-    base::HistogramTester* histogram_tester,
-    const std::string& histogram_name,
-    base::HistogramBase::Sample target_bucket,
-    size_t count) {
-  base::RunLoop().RunUntilIdle();
-  for (size_t attempt = 0; attempt < 50; ++attempt) {
-    const std::vector<base::Bucket> buckets =
-        histogram_tester->GetAllSamples(histogram_name);
-    size_t total_count = 0;
-    for (const auto& bucket : buckets) {
-      if (bucket.min == target_bucket)
-        total_count += bucket.count;
-    }
-    if (total_count >= count)
-      return;
-    content::FetchHistogramsFromChildProcesses();
-    SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
-    base::RunLoop().RunUntilIdle();
-  }
-}
-
 // Verifies that all URLs specified in |expected_urls| are present in
 // |urls_from_observed_prediction|. Ordering of URLs is NOT verified.
 void VerifyURLsPresent(const std::vector<GURL>& urls_from_observed_prediction,
@@ -328,7 +305,7 @@
 
   histogram_tester.ExpectUniqueSample(
       "NavigationPredictor.OnNonDSE.ActionTaken",
-      NavigationPredictor::Action::kPreconnect, 1);
+      NavigationPredictor::Action::kNone, 1);
 }
 
 // Simulate a click at the anchor element.
@@ -405,101 +382,6 @@
       NavigationPredictor::ActionAccuracy::
           kPrefetchActionClickToDifferentOrigin,
       1);
-
-  // Change to visibile.
-  browser()->tab_strip_model()->GetActiveWebContents()->WasShown();
-  histogram_tester.ExpectTotalCount("NavigationPredictor.OnNonDSE.ActionTaken",
-                                    1);
-
-  browser()->tab_strip_model()->GetActiveWebContents()->WasHidden();
-  histogram_tester.ExpectTotalCount("NavigationPredictor.OnNonDSE.ActionTaken",
-                                    1);
-
-  browser()->tab_strip_model()->GetActiveWebContents()->WasShown();
-  histogram_tester.ExpectTotalCount("NavigationPredictor.OnNonDSE.ActionTaken",
-                                    2);
-  histogram_tester.ExpectBucketCount(
-      "NavigationPredictor.OnNonDSE.ActionTaken",
-      NavigationPredictor::Action::kPreconnectOnVisibilityChange, 1);
-
-  // Hiding and showing the tab again should cause change in histograms since
-  // Pre* on tab foreground is done more than once per page.
-  browser()->tab_strip_model()->GetActiveWebContents()->WasHidden();
-  histogram_tester.ExpectTotalCount("NavigationPredictor.OnNonDSE.ActionTaken",
-                                    2);
-  browser()->tab_strip_model()->GetActiveWebContents()->WasShown();
-  histogram_tester.ExpectTotalCount("NavigationPredictor.OnNonDSE.ActionTaken",
-                                    3);
-}
-
-class NavigationPredictorBrowserTestWithUnusedIdleSocketTimeout
-    : public NavigationPredictorBrowserTest {
- public:
-  NavigationPredictorBrowserTestWithUnusedIdleSocketTimeout() {
-    feature_list_.InitAndEnableFeatureWithParameters(
-        net::features::kNetUnusedIdleSocketTimeout,
-        {{"unused_idle_socket_timeout_seconds", "0"}});
-  }
-
- private:
-  base::test::ScopedFeatureList feature_list_;
-};
-
-// Test that we preconnect after the last preconnect timed out.
-IN_PROC_BROWSER_TEST_F(
-    NavigationPredictorBrowserTestWithUnusedIdleSocketTimeout,
-    DISABLE_ON_CHROMEOS(ActionAccuracy_timeout)) {
-  base::HistogramTester histogram_tester;
-
-  const GURL& url = GetTestURL("/page_with_same_host_anchor_element.html");
-  ui_test_utils::NavigateToURL(browser(), url);
-  WaitForLayout();
-
-  RetryForHistogramBucketUntilCountReached(
-      &histogram_tester, "NavigationPredictor.OnNonDSE.ActionTaken",
-      static_cast<base::HistogramBase::Sample>(
-          NavigationPredictor::Action::kPreconnectAfterTimeout),
-      1);
-
-  EXPECT_LT(0, histogram_tester.GetBucketCount(
-                   "NavigationPredictor.OnNonDSE.ActionTaken",
-                   static_cast<base::HistogramBase::Sample>(
-                       NavigationPredictor::Action::kPreconnectAfterTimeout)));
-}
-
-class NavigationPredictorBrowserTestWithNegativePredictorRetryPreconnect
-    : public NavigationPredictorBrowserTest {
- public:
-  NavigationPredictorBrowserTestWithNegativePredictorRetryPreconnect() {
-    // -1 would force synchronous retries if retries were not disabled.
-    net_feature_list_.InitAndEnableFeatureWithParameters(
-        net::features::kNetUnusedIdleSocketTimeout,
-        {{"unused_idle_socket_timeout_seconds", "-1"}});
-    predictor_feature_list_.InitAndEnableFeatureWithParameters(
-        blink::features::kNavigationPredictor,
-        {{"retry_preconnect_wait_time_ms", "-1"}});
-  }
-
- private:
-  base::test::ScopedFeatureList net_feature_list_;
-  base::test::ScopedFeatureList predictor_feature_list_;
-};
-
-// Test that we don't preconnect after the last preconnect timed out when
-// retry_preconnect_wait_time_ms is negative.
-IN_PROC_BROWSER_TEST_F(
-    NavigationPredictorBrowserTestWithNegativePredictorRetryPreconnect,
-    DISABLE_ON_CHROMEOS(ActionAccuracy_timeout_no_retry)) {
-  base::HistogramTester histogram_tester;
-
-  const GURL& url = GetTestURL("/page_with_same_host_anchor_element.html");
-  ui_test_utils::NavigateToURL(browser(), url);
-  WaitForLayout();
-
-  EXPECT_EQ(0, histogram_tester.GetBucketCount(
-                   "NavigationPredictor.OnNonDSE.ActionTaken",
-                   static_cast<base::HistogramBase::Sample>(
-                       NavigationPredictor::Action::kPreconnectAfterTimeout)));
 }
 
 class NavigationPredictorBrowserTestWithDefaultPredictorEnabled
@@ -536,61 +418,6 @@
       "NavigationPredictor.OnNonDSE.ActionTaken",
       NavigationPredictor::Action::kPrefetch, 1);
 
-  // Change to visible.
-  browser()->tab_strip_model()->GetActiveWebContents()->WasShown();
-  histogram_tester.ExpectTotalCount("NavigationPredictor.OnNonDSE.ActionTaken",
-                                    1);
-
-  browser()->tab_strip_model()->GetActiveWebContents()->WasHidden();
-  histogram_tester.ExpectTotalCount("NavigationPredictor.OnNonDSE.ActionTaken",
-                                    1);
-
-  browser()->tab_strip_model()->GetActiveWebContents()->WasShown();
-  histogram_tester.ExpectTotalCount("NavigationPredictor.OnNonDSE.ActionTaken",
-                                    2);
-  histogram_tester.ExpectBucketCount(
-      "NavigationPredictor.OnNonDSE.ActionTaken",
-      NavigationPredictor::Action::kPreconnectOnVisibilityChange, 1);
-
-  // Hiding and showing the tab again should cause change in histograms since
-  // Pre* on tab foreground is done more than once per page.
-  browser()->tab_strip_model()->GetActiveWebContents()->WasHidden();
-  histogram_tester.ExpectTotalCount("NavigationPredictor.OnNonDSE.ActionTaken",
-                                    2);
-  browser()->tab_strip_model()->GetActiveWebContents()->WasShown();
-  histogram_tester.ExpectTotalCount("NavigationPredictor.OnNonDSE.ActionTaken",
-                                    3);
-  histogram_tester.ExpectBucketCount(
-      "NavigationPredictor.OnNonDSE.ActionTaken",
-      NavigationPredictor::Action::kPreconnectOnVisibilityChange, 2);
-}
-
-class NavigationPredictorBrowserTestNoSkipLinkScores
-    : public NavigationPredictorBrowserTest {
- public:
-  NavigationPredictorBrowserTestNoSkipLinkScores() {
-    feature_list_.InitAndEnableFeatureWithParameters(
-        blink::features::kNavigationPredictor,
-        {{"preconnect_skip_link_scores", "false"}});
-  }
-
- private:
-  base::test::ScopedFeatureList feature_list_;
-};
-
-IN_PROC_BROWSER_TEST_F(
-    NavigationPredictorBrowserTestNoSkipLinkScores,
-    DISABLE_ON_CHROMEOS(NoPreconnectNonSearchOnOtherHostLinks)) {
-  base::HistogramTester histogram_tester;
-
-  // This page only has non-same host links.
-  const GURL& url = GetTestURL("/anchors_different_area.html");
-  ui_test_utils::NavigateToURL(browser(), url);
-  WaitForLayout();
-
-  histogram_tester.ExpectUniqueSample(
-      "NavigationPredictor.OnNonDSE.ActionTaken",
-      NavigationPredictor::Action::kNone, 1);
 }
 
 IN_PROC_BROWSER_TEST_F(
@@ -605,7 +432,7 @@
 
   histogram_tester.ExpectUniqueSample(
       "NavigationPredictor.OnNonDSE.ActionTaken",
-      NavigationPredictor::Action::kPreconnect, 1);
+      NavigationPredictor::Action::kNone, 1);
 }
 
 class NavigationPredictorBrowserTestWithPrefetchAfterPreconnect
@@ -666,40 +493,6 @@
   }
 }
 
-IN_PROC_BROWSER_TEST_F(
-    NavigationPredictorBrowserTestWithDefaultPredictorEnabled,
-    DISABLE_ON_CHROMEOS(NoPreconnectSearch)) {
-  static const char kShortName[] = "test";
-  static const char kSearchURL[] =
-      "/anchors_different_area.html?q={searchTerms}";
-
-  // Set up default search engine.
-  TemplateURLService* model =
-      TemplateURLServiceFactory::GetForProfile(browser()->profile());
-  ASSERT_TRUE(model);
-  search_test_utils::WaitForTemplateURLServiceToLoad(model);
-  ASSERT_TRUE(model->loaded());
-
-  TemplateURLData data;
-  data.SetShortName(base::ASCIIToUTF16(kShortName));
-  data.SetKeyword(data.short_name());
-  data.SetURL(GetTestURL(kSearchURL).spec());
-
-  TemplateURL* template_url = model->Add(std::make_unique<TemplateURL>(data));
-  ASSERT_TRUE(template_url);
-  model->SetUserSelectedDefaultSearchProvider(template_url);
-
-  base::HistogramTester histogram_tester;
-
-  // This page only has non-same host links.
-  const GURL& url = GetTestURL("/anchors_different_area.html?q=cats");
-  ui_test_utils::NavigateToURL(browser(), url);
-  WaitForLayout();
-
-  histogram_tester.ExpectUniqueSample("NavigationPredictor.OnDSE.ActionTaken",
-                                      NavigationPredictor::Action::kNone, 1);
-}
-
 // Simulate a click at the anchor element.
 // Test that the action accuracy is properly recorded.
 // User clicks on an anchor element that points to same URL as the URL
@@ -919,7 +712,7 @@
       "AnchorElementMetrics.Visible.NumberOfAnchorElementsAfterMerge", 2, 1);
   histogram_tester.ExpectUniqueSample(
       "NavigationPredictor.OnNonDSE.ActionTaken",
-      NavigationPredictor::Action::kPreconnect, 1);
+      NavigationPredictor::Action::kNone, 1);
 }
 
 IN_PROC_BROWSER_TEST_F(NavigationPredictorBrowserTest,
@@ -1055,7 +848,7 @@
 
 // Verify that the observers are notified of predictions on search results page.
 IN_PROC_BROWSER_TEST_F(
-    NavigationPredictorBrowserTestWithDefaultPredictorEnabled,
+    NavigationPredictorBrowserTestWithPrefetchAfterPreconnect,
     DISABLE_ON_CHROMEOS(ObserverNotifiedOnSearchPage)) {
   TestObserver observer;
 
diff --git a/chrome/browser/navigation_predictor/navigation_predictor_preconnect_client.cc b/chrome/browser/navigation_predictor/navigation_predictor_preconnect_client.cc
new file mode 100644
index 0000000..3e12b74
--- /dev/null
+++ b/chrome/browser/navigation_predictor/navigation_predictor_preconnect_client.cc
@@ -0,0 +1,150 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/navigation_predictor/navigation_predictor_preconnect_client.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/metrics/field_trial_params.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "chrome/browser/predictors/loading_predictor.h"
+#include "chrome/browser/predictors/loading_predictor_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/search_engines/template_url_service_factory.h"
+#include "components/search_engines/template_url_service.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+#include "net/base/features.h"
+
+namespace {
+
+// A holdback that prevents the preconnect to measure benefit of the feature.
+const base::Feature kNavigationPredictorPreconnectHoldback {
+  "NavigationPredictorPreconnectHoldback",
+#if defined(OS_ANDROID)
+      base::FEATURE_DISABLED_BY_DEFAULT
+#else
+      base::FEATURE_ENABLED_BY_DEFAULT
+#endif
+};
+
+}  // namespace
+
+NavigationPredictorPreconnectClient::NavigationPredictorPreconnectClient(
+    content::WebContents* web_contents)
+    : browser_context_(web_contents->GetBrowserContext()),
+      current_visibility_(web_contents->GetVisibility()) {}
+
+NavigationPredictorPreconnectClient::~NavigationPredictorPreconnectClient() =
+    default;
+
+void NavigationPredictorPreconnectClient::DidFinishNavigation(
+    content::NavigationHandle* navigation_handle) {
+  if (!navigation_handle->IsInMainFrame() ||
+      !navigation_handle->HasCommitted() || navigation_handle->IsSameDocument())
+    return;
+
+  // New page, so stop the preconnect timer.
+  timer_.Stop();
+}
+
+void NavigationPredictorPreconnectClient::OnVisibilityChanged(
+    content::Visibility visibility) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (current_visibility_ == visibility)
+    return;
+
+  // Check if the visibility changed from VISIBLE to HIDDEN. Since navigation
+  // predictor is currently restricted to Android, it is okay to disregard the
+  // occluded state.
+  if (current_visibility_ != content::Visibility::HIDDEN ||
+      visibility != content::Visibility::VISIBLE) {
+    current_visibility_ = visibility;
+
+    // Stop any future preconnects while hidden.
+    timer_.Stop();
+    return;
+  }
+
+  current_visibility_ = visibility;
+
+  // Previously, the visibility was HIDDEN, and now it is VISIBLE implying that
+  // the web contents that was fully hidden is now fully visible.
+  MaybePreconnectNow();
+}
+
+void NavigationPredictorPreconnectClient::DidFinishLoad(
+    content::RenderFrameHost* render_frame_host,
+    const GURL& validated_url) {
+  // Ignore sub-frame loads.
+  if (render_frame_host->GetParent())
+    return;
+
+  MaybePreconnectNow();
+}
+
+void NavigationPredictorPreconnectClient::MaybePreconnectNow() {
+  if (base::FeatureList::IsEnabled(kNavigationPredictorPreconnectHoldback))
+    return;
+
+  if (browser_context_->IsOffTheRecord())
+    return;
+
+  // Only preconnect foreground tab.
+  if (current_visibility_ != content::Visibility::VISIBLE)
+    return;
+
+  // On search engine results page, next navigation is likely to be a different
+  // origin. Currently, the preconnect is only allowed for same origins. Hence,
+  // preconnect is currently disabled on search engine results page.
+  if (IsSearchEnginePage())
+    return;
+
+  url::Origin preconnect_origin =
+      url::Origin::Create(web_contents()->GetLastCommittedURL());
+  if (preconnect_origin.scheme() != url::kHttpScheme &&
+      preconnect_origin.scheme() != url::kHttpsScheme) {
+    return;
+  }
+
+  auto* loading_predictor = predictors::LoadingPredictorFactory::GetForProfile(
+      Profile::FromBrowserContext(browser_context_));
+  GURL preconnect_url_serialized(preconnect_origin.Serialize());
+  DCHECK(preconnect_url_serialized.is_valid());
+
+  loading_predictor->PrepareForPageLoad(
+      preconnect_url_serialized, predictors::HintOrigin::NAVIGATION_PREDICTOR,
+      true);
+
+  // The delay beyond the idle socket timeout that net uses when
+  // re-preconnecting. If negative, no retries occur.
+  constexpr int retry_delay_ms = 50;
+
+  // Set/Reset the timer to fire after the preconnect times out. Add an extra
+  // delay to make sure the preconnect has expired if it wasn't used.
+  timer_.Start(
+      FROM_HERE,
+      base::TimeDelta::FromSeconds(base::GetFieldTrialParamByFeatureAsInt(
+          net::features::kNetUnusedIdleSocketTimeout,
+          "unused_idle_socket_timeout_seconds", 60)) +
+          base::TimeDelta::FromMilliseconds(retry_delay_ms),
+      base::BindOnce(&NavigationPredictorPreconnectClient::MaybePreconnectNow,
+                     base::Unretained(this)));
+}
+
+bool NavigationPredictorPreconnectClient::IsSearchEnginePage() const {
+  auto* template_service = TemplateURLServiceFactory::GetForProfile(
+      Profile::FromBrowserContext(browser_context_));
+  if (!template_service)
+    return false;
+  return template_service->IsSearchResultsPageFromDefaultSearchProvider(
+      web_contents()->GetLastCommittedURL());
+}
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(NavigationPredictorPreconnectClient)
diff --git a/chrome/browser/navigation_predictor/navigation_predictor_preconnect_client.h b/chrome/browser/navigation_predictor/navigation_predictor_preconnect_client.h
new file mode 100644
index 0000000..af155bd
--- /dev/null
+++ b/chrome/browser/navigation_predictor/navigation_predictor_preconnect_client.h
@@ -0,0 +1,66 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_NAVIGATION_PREDICTOR_NAVIGATION_PREDICTOR_PRECONNECT_CLIENT_H_
+#define CHROME_BROWSER_NAVIGATION_PREDICTOR_NAVIGATION_PREDICTOR_PRECONNECT_CLIENT_H_
+
+#include "base/macros.h"
+#include "base/optional.h"
+#include "base/sequence_checker.h"
+#include "base/timer/timer.h"
+#include "content/public/browser/visibility.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_contents_user_data.h"
+#include "url/origin.h"
+
+namespace content {
+class BrowserContext;
+class RenderFrameHost;
+}  // namespace content
+
+class NavigationPredictorPreconnectClient
+    : public content::WebContentsObserver,
+      public content::WebContentsUserData<NavigationPredictorPreconnectClient> {
+ public:
+  ~NavigationPredictorPreconnectClient() override;
+
+ private:
+  friend class content::WebContentsUserData<
+      NavigationPredictorPreconnectClient>;
+  explicit NavigationPredictorPreconnectClient(
+      content::WebContents* web_contents);
+
+  // content::WebContentsObserver:
+  void OnVisibilityChanged(content::Visibility visibility) override;
+  void DidFinishLoad(content::RenderFrameHost* render_frame_host,
+                     const GURL& validated_url) override;
+  void DidFinishNavigation(
+      content::NavigationHandle* navigation_handle) override;
+
+  // Returns template URL service. Guaranteed to be non-null.
+  bool IsSearchEnginePage() const;
+
+  base::Optional<url::Origin> GetOriginToPreconnect(
+      const GURL& document_url) const;
+
+  // MaybePreconnectNow preconnects to an origin server if it's allowed.
+  void MaybePreconnectNow();
+
+  // Used to get keyed services.
+  content::BrowserContext* const browser_context_;
+
+  // Current visibility state of the web contents.
+  content::Visibility current_visibility_;
+
+  // Used to preconnect regularly.
+  base::OneShotTimer timer_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  WEB_CONTENTS_USER_DATA_KEY_DECL();
+
+  DISALLOW_COPY_AND_ASSIGN(NavigationPredictorPreconnectClient);
+};
+
+#endif  // CHROME_BROWSER_NAVIGATION_PREDICTOR_NAVIGATION_PREDICTOR_PRECONNECT_CLIENT_H_
diff --git a/chrome/browser/navigation_predictor/navigation_predictor_preconnect_client_browsertest.cc b/chrome/browser/navigation_predictor/navigation_predictor_preconnect_client_browsertest.cc
new file mode 100644
index 0000000..f416600
--- /dev/null
+++ b/chrome/browser/navigation_predictor/navigation_predictor_preconnect_client_browsertest.cc
@@ -0,0 +1,207 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/macros.h"
+#include "base/run_loop.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/metrics/subprocess_metrics_provider.h"
+#include "chrome/browser/search_engines/template_url_service_factory.h"
+#include "chrome/browser/subresource_filter/subresource_filter_browser_test_harness.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/search_test_utils.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/search_engines/template_url_service.h"
+#include "content/public/test/browser_test_utils.h"
+#include "net/base/features.h"
+#include "net/dns/mock_host_resolver.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+
+namespace {
+
+class NavigationPredictorPreconnectClientBrowserTest
+    : public subresource_filter::SubresourceFilterBrowserTest {
+ public:
+  NavigationPredictorPreconnectClientBrowserTest()
+      : subresource_filter::SubresourceFilterBrowserTest() {
+    feature_list_.InitFromCommandLine(std::string(),
+                                      "NavigationPredictorPreconnectHoldback");
+  }
+
+  void SetUp() override {
+    https_server_.reset(
+        new net::EmbeddedTestServer(net::EmbeddedTestServer::TYPE_HTTPS));
+    https_server_->ServeFilesFromSourceDirectory(
+        "chrome/test/data/navigation_predictor");
+    ASSERT_TRUE(https_server_->Start());
+
+    subresource_filter::SubresourceFilterBrowserTest::SetUp();
+  }
+
+  void SetUpOnMainThread() override {
+    subresource_filter::SubresourceFilterBrowserTest::SetUpOnMainThread();
+    host_resolver()->ClearRules();
+  }
+
+  const GURL GetTestURL(const char* file) const {
+    return https_server_->GetURL(file);
+  }
+
+  // Retries fetching |histogram_name| until it contains at least |count|
+  // samples.
+  void RetryForHistogramUntilCountReached(
+      const base::HistogramTester& histogram_tester,
+      const std::string& histogram_name,
+      size_t count) {
+    base::RunLoop().RunUntilIdle();
+    for (size_t attempt = 0; attempt < 50; ++attempt) {
+      const std::vector<base::Bucket> buckets =
+          histogram_tester.GetAllSamples(histogram_name);
+      size_t total_count = 0;
+      for (const auto& bucket : buckets)
+        total_count += bucket.count;
+      if (total_count >= count)
+        return;
+      content::FetchHistogramsFromChildProcesses();
+      SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
+      base::RunLoop().RunUntilIdle();
+    }
+  }
+
+ private:
+  std::unique_ptr<net::EmbeddedTestServer> https_server_;
+  base::test::ScopedFeatureList feature_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(NavigationPredictorPreconnectClientBrowserTest);
+};
+
+IN_PROC_BROWSER_TEST_F(NavigationPredictorPreconnectClientBrowserTest,
+                       NoPreconnectSearch) {
+  static const char kShortName[] = "test";
+  static const char kSearchURL[] =
+      "/anchors_different_area.html?q={searchTerms}";
+  TemplateURLService* model =
+      TemplateURLServiceFactory::GetForProfile(browser()->profile());
+  ASSERT_TRUE(model);
+  search_test_utils::WaitForTemplateURLServiceToLoad(model);
+  ASSERT_TRUE(model->loaded());
+
+  TemplateURLData data;
+  data.SetShortName(base::ASCIIToUTF16(kShortName));
+  data.SetKeyword(data.short_name());
+  data.SetURL(GetTestURL(kSearchURL).spec());
+
+  TemplateURL* template_url = model->Add(std::make_unique<TemplateURL>(data));
+  ASSERT_TRUE(template_url);
+  model->SetUserSelectedDefaultSearchProvider(template_url);
+  const GURL& url = GetTestURL("/anchors_different_area.html?q=cats");
+
+  base::HistogramTester histogram_tester;
+  ui_test_utils::NavigateToURL(browser(), url);
+  // There should be preconnect from navigation, but not preconnect client.
+  histogram_tester.ExpectTotalCount("LoadingPredictor.PreconnectCount", 1);
+
+  ui_test_utils::NavigateToURL(browser(), url);
+  // There should be 2 preconnects from navigation, but not any from preconnect
+  // client.
+  histogram_tester.ExpectTotalCount("LoadingPredictor.PreconnectCount", 2);
+}
+
+IN_PROC_BROWSER_TEST_F(NavigationPredictorPreconnectClientBrowserTest,
+                       PreconnectNotSearch) {
+  const GURL& url = GetTestURL("/anchors_different_area.html");
+
+  base::HistogramTester histogram_tester;
+  ui_test_utils::NavigateToURL(browser(), url);
+  // There should be preconnect from navigation and one from preconnect client.
+  RetryForHistogramUntilCountReached(histogram_tester,
+                                     "LoadingPredictor.PreconnectCount", 2);
+}
+
+IN_PROC_BROWSER_TEST_F(NavigationPredictorPreconnectClientBrowserTest,
+                       PreconnectNotSearchBackgroundForeground) {
+  const GURL& url = GetTestURL("/anchors_different_area.html");
+
+  browser()->tab_strip_model()->GetActiveWebContents()->WasHidden();
+  base::HistogramTester histogram_tester;
+  ui_test_utils::NavigateToURL(browser(), url);
+
+  // There should be a navigational preconnect.
+  histogram_tester.ExpectTotalCount("LoadingPredictor.PreconnectCount", 1);
+
+  // Change to visible.
+  browser()->tab_strip_model()->GetActiveWebContents()->WasShown();
+
+  // After showing the contents, there should be a preconnect client preconnect.
+  RetryForHistogramUntilCountReached(histogram_tester,
+                                     "LoadingPredictor.PreconnectCount", 2);
+
+  browser()->tab_strip_model()->GetActiveWebContents()->WasHidden();
+
+  browser()->tab_strip_model()->GetActiveWebContents()->WasShown();
+  // After showing the contents again, there should be another preconnect client
+  // preconnect.
+  RetryForHistogramUntilCountReached(histogram_tester,
+                                     "LoadingPredictor.PreconnectCount", 3);
+}
+
+class NavigationPredictorPreconnectClientBrowserTestWithUnusedIdleSocketTimeout
+    : public NavigationPredictorPreconnectClientBrowserTest {
+ public:
+  NavigationPredictorPreconnectClientBrowserTestWithUnusedIdleSocketTimeout()
+      : NavigationPredictorPreconnectClientBrowserTest() {
+    feature_list_.InitAndEnableFeatureWithParameters(
+        net::features::kNetUnusedIdleSocketTimeout,
+        {{"unused_idle_socket_timeout_seconds", "0"}});
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+// Test that we preconnect after the last preconnect timed out.
+IN_PROC_BROWSER_TEST_F(
+    NavigationPredictorPreconnectClientBrowserTestWithUnusedIdleSocketTimeout,
+    ActionAccuracy_timeout) {
+  base::HistogramTester histogram_tester;
+
+  const GURL& url = GetTestURL("/page_with_same_host_anchor_element.html");
+  ui_test_utils::NavigateToURL(browser(), url);
+
+  histogram_tester.ExpectTotalCount("LoadingPredictor.PreconnectCount", 1);
+}
+
+class NavigationPredictorPreconnectClientBrowserTestWithHoldback
+    : public NavigationPredictorPreconnectClientBrowserTest {
+ public:
+  NavigationPredictorPreconnectClientBrowserTestWithHoldback()
+      : NavigationPredictorPreconnectClientBrowserTest() {
+    feature_list_.InitFromCommandLine("NavigationPredictorPreconnectHoldback",
+                                      std::string());
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(
+    NavigationPredictorPreconnectClientBrowserTestWithHoldback,
+    NoPreconnectHoldback) {
+  const GURL& url = GetTestURL("/anchors_different_area.html");
+
+  base::HistogramTester histogram_tester;
+  ui_test_utils::NavigateToURL(browser(), url);
+  // There should be preconnect from navigation, but not preconnect client.
+  histogram_tester.ExpectTotalCount("LoadingPredictor.PreconnectCount", 1);
+
+  ui_test_utils::NavigateToURL(browser(), url);
+  // There should be 2 preconnects from navigation, but not any from preconnect
+  // client.
+  histogram_tester.ExpectTotalCount("LoadingPredictor.PreconnectCount", 2);
+}
+
+}  // namespace
diff --git a/chrome/browser/navigation_predictor/navigation_predictor_unittest.cc b/chrome/browser/navigation_predictor/navigation_predictor_unittest.cc
index d88460e..a5c18b2e 100644
--- a/chrome/browser/navigation_predictor/navigation_predictor_unittest.cc
+++ b/chrome/browser/navigation_predictor/navigation_predictor_unittest.cc
@@ -46,10 +46,6 @@
 
   base::Optional<GURL> prefetch_url() const { return prefetch_url_; }
 
-  base::Optional<url::Origin> preconnect_origin() const {
-    return preconnect_origin_;
-  }
-
   const std::map<GURL, int>& GetAreaRankMap() const { return area_rank_map_; }
 
   bool prefetch_url_prefetched() const { return prefetch_url_prefetched_; }
@@ -131,10 +127,6 @@
     return predictor_service_helper_->prefetch_url();
   }
 
-  base::Optional<url::Origin> preconnect_origin() const {
-    return predictor_service_helper_->preconnect_origin();
-  }
-
   bool prefetch_url_prefetched() {
     return predictor_service_helper_->prefetch_url_prefetched();
   }
@@ -147,8 +139,7 @@
         !field_trial_initiated_);
   }
 
-  void SetupFieldTrial(base::Optional<int> preconnect_origin_score_threshold,
-                       base::Optional<int> prefetch_url_score_threshold,
+  void SetupFieldTrial(base::Optional<int> prefetch_url_score_threshold,
                        base::Optional<bool> prefetch_after_preconnect) {
     if (field_trial_initiated_)
       return;
@@ -158,10 +149,6 @@
     const std::string kGroupName = "GroupFoo2";  // Value not used
 
     std::map<std::string, std::string> params;
-    if (preconnect_origin_score_threshold.has_value()) {
-      params["preconnect_origin_score_threshold"] =
-          base::NumberToString(preconnect_origin_score_threshold.value());
-    }
     if (prefetch_url_score_threshold.has_value()) {
       params["prefetch_url_score_threshold"] =
           base::NumberToString(prefetch_url_score_threshold.value());
@@ -478,7 +465,7 @@
 
   histogram_tester.ExpectUniqueSample(
       "NavigationPredictor.OnNonDSE.ActionTaken",
-      NavigationPredictor::Action::kPreconnect, 1);
+      NavigationPredictor::Action::kNone, 1);
   EXPECT_FALSE(prefetch_url().has_value());
 
   auto metrics_clicked = CreateMetricsPtr(source, same_origin_href_small, 0.01);
@@ -488,8 +475,7 @@
 
   histogram_tester.ExpectUniqueSample(
       "NavigationPredictor.OnNonDSE.AccuracyActionTaken",
-      NavigationPredictor::ActionAccuracy::kPreconnectActionClickToSameOrigin,
-      1);
+      NavigationPredictor::ActionAccuracy::kNoActionTakenClickHappened, 1);
 }
 
 TEST_F(NavigationPredictorTest,
@@ -509,7 +495,7 @@
 
   histogram_tester.ExpectUniqueSample(
       "NavigationPredictor.OnNonDSE.ActionTaken",
-      NavigationPredictor::Action::kPreconnect, 1);
+      NavigationPredictor::Action::kNone, 1);
   EXPECT_FALSE(prefetch_url().has_value());
 }
 
@@ -517,7 +503,7 @@
     : public NavigationPredictorTest {
  public:
   NavigationPredictorSendUkmMetricsEnabledTest() {
-    SetupFieldTrial(base::nullopt, base::nullopt, base::nullopt);
+    SetupFieldTrial(base::nullopt, base::nullopt);
   }
 
   void SetUp() override {
@@ -751,7 +737,7 @@
     : public NavigationPredictorTest {
  public:
   NavigationPredictorPrefetchAfterPreconnectEnabledTest() {
-    SetupFieldTrial(base::nullopt, base::nullopt, true);
+    SetupFieldTrial(base::nullopt, true);
   }
 
   void SetUp() override {
@@ -811,8 +797,7 @@
 class NavigationPredictorPrefetchDisabledTest : public NavigationPredictorTest {
  public:
   NavigationPredictorPrefetchDisabledTest() {
-    SetupFieldTrial(0 /* preconnect_origin_score_threshold */,
-                    101 /* prefetch_url_score_threshold */, base::nullopt);
+    SetupFieldTrial(101 /* prefetch_url_score_threshold */, base::nullopt);
   }
 
   void SetUp() override {
@@ -847,9 +832,8 @@
 
   histogram_tester.ExpectUniqueSample(
       "NavigationPredictor.OnNonDSE.ActionTaken",
-      NavigationPredictor::Action::kPreconnect, 1);
+      NavigationPredictor::Action::kNone, 1);
   EXPECT_FALSE(prefetch_url().has_value());
-  EXPECT_EQ(url::Origin::Create(GURL(source)), preconnect_origin());
 
   auto metrics_clicked = CreateMetricsPtr(source, same_origin_href_small, 0.01);
   predictor_service()->ReportAnchorElementMetricsOnClick(
@@ -858,8 +842,7 @@
 
   histogram_tester.ExpectUniqueSample(
       "NavigationPredictor.OnNonDSE.AccuracyActionTaken",
-      NavigationPredictor::ActionAccuracy::kPreconnectActionClickToSameOrigin,
-      1);
+      NavigationPredictor::ActionAccuracy::kNoActionTakenClickHappened, 1);
 }
 
 // Disables prefetch and loads a page where the preconnect score of a cross
@@ -886,9 +869,8 @@
 
   histogram_tester.ExpectUniqueSample(
       "NavigationPredictor.OnNonDSE.ActionTaken",
-      NavigationPredictor::Action::kPreconnect, 1);
+      NavigationPredictor::Action::kNone, 1);
   EXPECT_FALSE(prefetch_url().has_value());
-  EXPECT_TRUE(preconnect_origin().has_value());
 
   auto metrics_clicked = CreateMetricsPtr(source, same_origin_href_small, 0.01);
   predictor_service()->ReportAnchorElementMetricsOnClick(
@@ -897,8 +879,7 @@
 
   histogram_tester.ExpectUniqueSample(
       "NavigationPredictor.OnNonDSE.AccuracyActionTaken",
-      NavigationPredictor::ActionAccuracy::kPreconnectActionClickToSameOrigin,
-      1);
+      NavigationPredictor::ActionAccuracy::kNoActionTakenClickHappened, 1);
 }
 
 // Framework for testing cases where preconnect and prefetch are effectively
@@ -907,8 +888,7 @@
     : public NavigationPredictorTest {
  public:
   NavigationPredictorPreconnectPrefetchDisabledTest() {
-    SetupFieldTrial(101 /* preconnect_origin_score_threshold */,
-                    101 /* prefetch_url_score_threshold */, base::nullopt);
+    SetupFieldTrial(101 /* prefetch_url_score_threshold */, base::nullopt);
   }
 
   void SetUp() override {
@@ -938,7 +918,7 @@
 
   histogram_tester.ExpectUniqueSample(
       "NavigationPredictor.OnNonDSE.ActionTaken",
-      NavigationPredictor::Action::kPreconnect, 1);
+      NavigationPredictor::Action::kNone, 1);
   EXPECT_FALSE(prefetch_url().has_value());
 
   auto metrics_clicked = CreateMetricsPtr(source, same_origin_href_small, 0.01);
@@ -948,6 +928,5 @@
 
   histogram_tester.ExpectUniqueSample(
       "NavigationPredictor.OnNonDSE.AccuracyActionTaken",
-      NavigationPredictor::ActionAccuracy::kPreconnectActionClickToSameOrigin,
-      1);
+      NavigationPredictor::ActionAccuracy::kNoActionTakenClickHappened, 1);
 }
diff --git a/chrome/browser/net/system_network_context_manager.cc b/chrome/browser/net/system_network_context_manager.cc
index afec830..da04738f 100644
--- a/chrome/browser/net/system_network_context_manager.cc
+++ b/chrome/browser/net/system_network_context_manager.cc
@@ -668,9 +668,10 @@
   chrome::GetDefaultUserDataDirectory(&config->user_data_path);
   content::GetNetworkService()->SetCryptConfig(std::move(config));
 #endif
-#if defined(OS_MACOSX)
-  content::GetNetworkService()->SetEncryptionKey(
-      OSCrypt::GetRawEncryptionKey());
+#if defined(OS_WIN) || defined(OS_MACOSX)
+  std::string key = OSCrypt::GetRawEncryptionKey();
+  DCHECK(!key.empty());
+  content::GetNetworkService()->SetEncryptionKey(key);
 #endif
 
   // Asynchronously reapply the most recently received CRLSet (if any).
diff --git a/chrome/browser/page_load_metrics/observers/subresource_loading_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/subresource_loading_page_load_metrics_observer.cc
index 25f526f..f8da606 100644
--- a/chrome/browser/page_load_metrics/observers/subresource_loading_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/subresource_loading_page_load_metrics_observer.cc
@@ -92,7 +92,7 @@
         navigation_start_ /* end_time */,
         base::BindOnce(
             &SubresourceLoadingPageLoadMetricsObserver::OnOriginLastVisitResult,
-            weak_factory_.GetWeakPtr()),
+            weak_factory_.GetWeakPtr(), base::Time::Now()),
         &task_tracker_);
   }
 
@@ -100,7 +100,10 @@
 }
 
 void SubresourceLoadingPageLoadMetricsObserver::OnOriginLastVisitResult(
+    base::Time query_start_time,
     history::HistoryLastVisitToHostResult result) {
+  history_query_times_.push_back(base::Time::Now() - query_start_time);
+
   if (!result.success)
     return;
 
@@ -129,12 +132,14 @@
   partition->GetCookieManagerForBrowserProcess()->GetCookieList(
       url, net::CookieOptions::MakeAllInclusive(),
       base::BindOnce(&SubresourceLoadingPageLoadMetricsObserver::OnCookieResult,
-                     weak_factory_.GetWeakPtr()));
+                     weak_factory_.GetWeakPtr(), base::Time::Now()));
 }
 
 void SubresourceLoadingPageLoadMetricsObserver::OnCookieResult(
+    base::Time query_start_time,
     const net::CookieStatusList& cookies,
     const net::CookieStatusList& excluded_cookies) {
+  cookie_query_times_.push_back(base::Time::Now() - query_start_time);
   mainframe_had_cookies_ =
       mainframe_had_cookies_.value_or(false) || !cookies.empty();
 }
@@ -170,6 +175,17 @@
 
   task_tracker_.TryCancelAll();
 
+  for (base::TimeDelta cookie_query_time : cookie_query_times_) {
+    UMA_HISTOGRAM_TIMES("PageLoad.Clients.SubresourceLoading.CookiesQueryTime",
+                        cookie_query_time);
+  }
+  cookie_query_times_.clear();
+  for (base::TimeDelta history_query_time : history_query_times_) {
+    UMA_HISTOGRAM_TIMES("PageLoad.Clients.SubresourceLoading.HistoryQueryTime",
+                        history_query_time);
+  }
+  history_query_times_.clear();
+
   // TODO(crbug.com/995437): Add UKM for data saver users once all metrics
   // are in place.
 
diff --git a/chrome/browser/page_load_metrics/observers/subresource_loading_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/subresource_loading_page_load_metrics_observer.h
index 4b9f7d3..cce3a47 100644
--- a/chrome/browser/page_load_metrics/observers/subresource_loading_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/subresource_loading_page_load_metrics_observer.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_SUBRESOURCE_LOADING_PAGE_LOAD_METRICS_OBSERVER_H_
 
 #include <stdint.h>
+#include <vector>
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
@@ -33,7 +34,8 @@
  protected:
   // Used as a callback for history service query results. Protected for
   // testing.
-  void OnOriginLastVisitResult(history::HistoryLastVisitToHostResult result);
+  void OnOriginLastVisitResult(base::Time query_start_time,
+                               history::HistoryLastVisitToHostResult result);
 
  private:
   void RecordMetrics();
@@ -46,7 +48,8 @@
                             const GURL& url);
 
   // Used as a callback for the cookie manager query.
-  void OnCookieResult(const net::CookieStatusList& cookies,
+  void OnCookieResult(base::Time query_start_time,
+                      const net::CookieStatusList& cookies,
                       const net::CookieStatusList& excluded_cookies);
 
   // page_load_metrics::PageLoadMetricsObserver:
@@ -75,6 +78,13 @@
   size_t loaded_css_js_from_cache_before_fcp_ = 0;
   size_t loaded_css_js_from_network_before_fcp_ = 0;
 
+  // These vectors hold the durations that queries to the cookie manager and
+  // history service took, respectively. Since we only want to record these when
+  // we also record the query results, the query times are stashed here until
+  // |RecordMetrics()| is called.
+  std::vector<base::TimeDelta> cookie_query_times_;
+  std::vector<base::TimeDelta> history_query_times_;
+
   // The minimum number of days since the last visit, as reported by
   // HistoryService, to any origin in the redirect chain. Set to -1 if there is
   // a response from the history service but was no previous visit.
diff --git a/chrome/browser/page_load_metrics/observers/subresource_loading_page_load_metrics_observer_browsertest.cc b/chrome/browser/page_load_metrics/observers/subresource_loading_page_load_metrics_observer_browsertest.cc
index 759280a..e95469c5 100644
--- a/chrome/browser/page_load_metrics/observers/subresource_loading_page_load_metrics_observer_browsertest.cc
+++ b/chrome/browser/page_load_metrics/observers/subresource_loading_page_load_metrics_observer_browsertest.cc
@@ -60,6 +60,8 @@
       "PageLoad.Clients.SubresourceLoading.HasPreviousVisitToOrigin", false, 1);
   histogram_tester.ExpectTotalCount(
       "PageLoad.Clients.SubresourceLoading.DaysSinceLastVisitToOrigin", 0);
+  histogram_tester.ExpectTotalCount(
+      "PageLoad.Clients.SubresourceLoading.HistoryQueryTime", 1);
 
   // Revisit and expect a 0 days-ago entry.
   NavigateToPath("/index.html");
@@ -70,6 +72,8 @@
       "PageLoad.Clients.SubresourceLoading.HasPreviousVisitToOrigin", false, 1);
   histogram_tester.ExpectUniqueSample(
       "PageLoad.Clients.SubresourceLoading.DaysSinceLastVisitToOrigin", 0, 1);
+  histogram_tester.ExpectTotalCount(
+      "PageLoad.Clients.SubresourceLoading.HistoryQueryTime", 2);
 }
 
 IN_PROC_BROWSER_TEST_F(SubresourceLoadingPageLoadMetricsObserverBrowserTest,
@@ -80,6 +84,8 @@
 
   histogram_tester.ExpectUniqueSample(
       "PageLoad.Clients.SubresourceLoading.MainFrameHadCookies", false, 1);
+  histogram_tester.ExpectTotalCount(
+      "PageLoad.Clients.SubresourceLoading.CookiesQueryTime", 1);
 }
 
 IN_PROC_BROWSER_TEST_F(SubresourceLoadingPageLoadMetricsObserverBrowserTest,
@@ -94,6 +100,8 @@
   // From the first page load.
   histogram_tester.ExpectBucketCount(
       "PageLoad.Clients.SubresourceLoading.MainFrameHadCookies", false, 1);
+  histogram_tester.ExpectTotalCount(
+      "PageLoad.Clients.SubresourceLoading.CookiesQueryTime", 2);
 }
 
 IN_PROC_BROWSER_TEST_F(SubresourceLoadingPageLoadMetricsObserverBrowserTest,
@@ -108,4 +116,29 @@
   // From the first page load.
   histogram_tester.ExpectBucketCount(
       "PageLoad.Clients.SubresourceLoading.MainFrameHadCookies", false, 1);
+  histogram_tester.ExpectTotalCount(
+      "PageLoad.Clients.SubresourceLoading.CookiesQueryTime", 3);
+}
+
+IN_PROC_BROWSER_TEST_F(SubresourceLoadingPageLoadMetricsObserverBrowserTest,
+                       RecordNothingOnUntrackedPage) {
+  base::HistogramTester histogram_tester;
+
+  NavigateAway();
+  NavigateAway();
+
+  histogram_tester.ExpectTotalCount(
+      "PageLoad.Clients.SubresourceLoading.CookiesQueryTime", 0);
+  histogram_tester.ExpectTotalCount(
+      "PageLoad.Clients.SubresourceLoading.DaysSinceLastVisitToOrigin", 0);
+  histogram_tester.ExpectTotalCount(
+      "PageLoad.Clients.SubresourceLoading.HasPreviousVisitToOrigin", 0);
+  histogram_tester.ExpectTotalCount(
+      "PageLoad.Clients.SubresourceLoading.HistoryQueryTime", 0);
+  histogram_tester.ExpectTotalCount(
+      "PageLoad.Clients.SubresourceLoading.LoadedCSSJSBeforeFCP.Cached", 0);
+  histogram_tester.ExpectTotalCount(
+      "PageLoad.Clients.SubresourceLoading.LoadedCSSJSBeforeFCP.Noncached", 0);
+  histogram_tester.ExpectTotalCount(
+      "PageLoad.Clients.SubresourceLoading.MainFrameHadCookies", 0);
 }
diff --git a/chrome/browser/page_load_metrics/observers/subresource_loading_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/subresource_loading_page_load_metrics_observer_unittest.cc
index d77c551..6f9bc64 100644
--- a/chrome/browser/page_load_metrics/observers/subresource_loading_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/subresource_loading_page_load_metrics_observer_unittest.cc
@@ -32,7 +32,7 @@
  public:
   void CallOnOriginLastVisitResult(
       history::HistoryLastVisitToHostResult result) {
-    OnOriginLastVisitResult(result);
+    OnOriginLastVisitResult(base::Time::Now(), result);
   }
 };
 
@@ -350,6 +350,8 @@
   tester()->NavigateToUntrackedUrl();
 
   tester()->histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.SubresourceLoading.HistoryQueryTime", 0);
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.SubresourceLoading.HasPreviousVisitToOrigin", 0);
   tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.SubresourceLoading.DaysSinceLastVisitToOrigin", 0);
@@ -362,6 +364,8 @@
   tester()->NavigateToUntrackedUrl();
 
   tester()->histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.SubresourceLoading.HistoryQueryTime", 1);
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.SubresourceLoading.HasPreviousVisitToOrigin", 0);
   tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.SubresourceLoading.DaysSinceLastVisitToOrigin", 0);
@@ -374,6 +378,8 @@
       {true /* success */, base::Time()});
   tester()->NavigateToUntrackedUrl();
 
+  tester()->histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.SubresourceLoading.HistoryQueryTime", 1);
   tester()->histogram_tester().ExpectUniqueSample(
       "PageLoad.Clients.SubresourceLoading.HasPreviousVisitToOrigin", false, 1);
   tester()->histogram_tester().ExpectTotalCount(
@@ -387,6 +393,8 @@
 
   tester()->NavigateToUntrackedUrl();
 
+  tester()->histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.SubresourceLoading.HistoryQueryTime", 1);
   tester()->histogram_tester().ExpectUniqueSample(
       "PageLoad.Clients.SubresourceLoading.HasPreviousVisitToOrigin", true, 1);
   tester()->histogram_tester().ExpectUniqueSample(
@@ -401,6 +409,8 @@
 
   tester()->NavigateToUntrackedUrl();
 
+  tester()->histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.SubresourceLoading.HistoryQueryTime", 1);
   tester()->histogram_tester().ExpectUniqueSample(
       "PageLoad.Clients.SubresourceLoading.HasPreviousVisitToOrigin", true, 1);
   tester()->histogram_tester().ExpectUniqueSample(
@@ -415,5 +425,7 @@
   tester()->NavigateToUntrackedUrl();
 
   tester()->histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.SubresourceLoading.CookiesQueryTime", 0);
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.SubresourceLoading.MainFrameHadCookies", 0);
 }
diff --git a/chrome/browser/pdf/pdf_extension_test.cc b/chrome/browser/pdf/pdf_extension_test.cc
index abee25d..62783c6 100644
--- a/chrome/browser/pdf/pdf_extension_test.cc
+++ b/chrome/browser/pdf/pdf_extension_test.cc
@@ -18,6 +18,7 @@
 #include "base/path_service.h"
 #include "base/run_loop.h"
 #include "base/strings/pattern.h"
+#include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/synchronization/lock.h"
 #include "base/task/post_task.h"
@@ -154,6 +155,13 @@
     extensions::ExtensionApiTest::TearDownOnMainThread();
   }
 
+  // Serve paths prefixed with _test_resources/ from chrome/test/data/pdf.
+  base::FilePath GetTestResourcesParentDir() override {
+    base::FilePath test_root_path;
+    base::PathService::Get(chrome::DIR_TEST_DATA, &test_root_path);
+    return test_root_path.AppendASCII("pdf");
+  }
+
   bool PdfIsExpectedToLoad(const std::string& pdf_file) {
     const char* const kFailingPdfs[] = {
         "pdf_private/accessibility_crash_1.pdf",
@@ -170,9 +178,10 @@
   }
 
   // Runs the extensions test at chrome/test/data/pdf/<filename> on the PDF file
-  // at chrome/test/data/pdf/<pdf_filename>.
-  void RunTestsInFile(const std::string& filename,
-                      const std::string& pdf_filename) {
+  // at chrome/test/data/pdf/<pdf_filename>, where |filename| is loaded as a JS
+  // module.
+  void RunTestsInJsModule(const std::string& filename,
+                          const std::string& pdf_filename) {
     extensions::ResultCatcher catcher;
 
     GURL url(embedded_test_server()->GetURL("/pdf/" + pdf_filename));
@@ -184,34 +193,16 @@
     // loaded before continuing.
     WebContents* guest_contents = LoadPdfGetGuestContents(url);
     ASSERT_TRUE(guest_contents);
-    std::string test_util_js;
-    std::string mock_interactions_js;
 
-    {
-      base::ScopedAllowBlockingForTesting allow_blocking;
-      base::FilePath test_data_dir;
-      base::PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir);
-      test_data_dir = test_data_dir.Append(FILE_PATH_LITERAL("pdf"));
-      base::FilePath test_util_path = test_data_dir.AppendASCII("test_util.js");
-      ASSERT_TRUE(base::ReadFileToString(test_util_path, &test_util_js));
+    constexpr char kModuleLoaderTemplate[] =
+        R"(var s = document.createElement('script');
+           s.type = 'module';
+           s.src = '_test_resources/%s';
+           document.body.appendChild(s);)";
 
-      base::FilePath source_root_dir;
-      base::PathService::Get(base::DIR_SOURCE_ROOT, &source_root_dir);
-      base::FilePath mock_interactions_path = source_root_dir.Append(
-          FILE_PATH_LITERAL("third_party/polymer/v1_0/components-chromium/"
-                            "iron-test-helpers/mock-interactions.js"));
-      ASSERT_TRUE(base::ReadFileToString(mock_interactions_path,
-                                         &mock_interactions_js));
-      test_util_js.append(mock_interactions_js);
-
-      base::FilePath test_file_path = test_data_dir.AppendASCII(filename);
-      std::string test_js;
-      ASSERT_TRUE(base::ReadFileToString(test_file_path, &test_js));
-
-      test_util_js.append(test_js);
-    }
-
-    ASSERT_TRUE(content::ExecuteScript(guest_contents, test_util_js));
+    ASSERT_TRUE(content::ExecuteScript(
+        guest_contents,
+        base::StringPrintf(kModuleLoaderTemplate, filename.c_str())));
 
     if (!catcher.GetNextResult())
       FAIL() << catcher.message();
@@ -396,7 +387,7 @@
                      "register('" + worker_path + "', '/pdf');"));
 
     // Navigate to a PDF in the service worker's scope. It should load.
-    RunTestsInFile("basic_test.js", "test.pdf");
+    RunTestsInJsModule("basic_test.js", "test.pdf");
     // Ensure it loaded in a PPAPI process.
     EXPECT_EQ(1, CountPDFProcesses());
   }
@@ -612,72 +603,72 @@
                          testing::Range(0, kNumberLoadTestParts));
 
 IN_PROC_BROWSER_TEST_F(PDFExtensionTest, Basic) {
-  RunTestsInFile("basic_test.js", "test.pdf");
+  RunTestsInJsModule("basic_test.js", "test.pdf");
 
   // Ensure it loaded in a PPAPI process.
   EXPECT_EQ(1, CountPDFProcesses());
 }
 
 IN_PROC_BROWSER_TEST_F(PDFExtensionTest, BasicPlugin) {
-  RunTestsInFile("basic_plugin_test.js", "test.pdf");
+  RunTestsInJsModule("basic_plugin_test.js", "test.pdf");
 }
 
 IN_PROC_BROWSER_TEST_F(PDFExtensionTest, Viewport) {
-  RunTestsInFile("viewport_test.js", "test.pdf");
+  RunTestsInJsModule("viewport_test.js", "test.pdf");
 }
 
 IN_PROC_BROWSER_TEST_F(PDFExtensionTest, Layout3) {
-  RunTestsInFile("layout_test.js", "test-layout3.pdf");
+  RunTestsInJsModule("layout_test.js", "test-layout3.pdf");
 }
 
 IN_PROC_BROWSER_TEST_F(PDFExtensionTest, Layout4) {
-  RunTestsInFile("layout_test.js", "test-layout4.pdf");
+  RunTestsInJsModule("layout_test.js", "test-layout4.pdf");
 }
 
 IN_PROC_BROWSER_TEST_F(PDFExtensionTest, Bookmark) {
-  RunTestsInFile("bookmarks_test.js", "test-bookmarks-with-zoom.pdf");
+  RunTestsInJsModule("bookmarks_test.js", "test-bookmarks-with-zoom.pdf");
 }
 
 IN_PROC_BROWSER_TEST_F(PDFExtensionTest, Navigator) {
-  RunTestsInFile("navigator_test.js", "test.pdf");
+  RunTestsInJsModule("navigator_test.js", "test.pdf");
 }
 
 IN_PROC_BROWSER_TEST_F(PDFExtensionTest, ParamsParser) {
-  RunTestsInFile("params_parser_test.js", "test.pdf");
+  RunTestsInJsModule("params_parser_test.js", "test.pdf");
 }
 
 IN_PROC_BROWSER_TEST_F(PDFExtensionTest, ZoomManager) {
-  RunTestsInFile("zoom_manager_test.js", "test.pdf");
+  RunTestsInJsModule("zoom_manager_test.js", "test.pdf");
 }
 
 IN_PROC_BROWSER_TEST_F(PDFExtensionTest, GestureDetector) {
-  RunTestsInFile("gesture_detector_test.js", "test.pdf");
+  RunTestsInJsModule("gesture_detector_test.js", "test.pdf");
 }
 
 IN_PROC_BROWSER_TEST_F(PDFExtensionTest, TouchHandling) {
-  RunTestsInFile("touch_handling_test.js", "test.pdf");
+  RunTestsInJsModule("touch_handling_test.js", "test.pdf");
 }
 
 IN_PROC_BROWSER_TEST_F(PDFExtensionTest, Elements) {
   // Although this test file does not require a PDF to be loaded, loading the
   // elements without loading a PDF is difficult.
-  RunTestsInFile("material_elements_test.js", "test.pdf");
+  RunTestsInJsModule("material_elements_test.js", "test.pdf");
 }
 
 IN_PROC_BROWSER_TEST_F(PDFExtensionTest, ToolbarManager) {
-  RunTestsInFile("toolbar_manager_test.js", "test.pdf");
+  RunTestsInJsModule("toolbar_manager_test.js", "test.pdf");
 }
 
 IN_PROC_BROWSER_TEST_F(PDFExtensionTest, Title) {
-  RunTestsInFile("title_test.js", "test-title.pdf");
+  RunTestsInJsModule("title_test.js", "test-title.pdf");
 }
 
 IN_PROC_BROWSER_TEST_F(PDFExtensionTest, WhitespaceTitle) {
-  RunTestsInFile("whitespace_title_test.js", "test-whitespace-title.pdf");
+  RunTestsInJsModule("whitespace_title_test.js", "test-whitespace-title.pdf");
 }
 
 IN_PROC_BROWSER_TEST_F(PDFExtensionTest, Beep) {
-  RunTestsInFile("beep_test.js", "test-beep.pdf");
+  RunTestsInJsModule("beep_test.js", "test-beep.pdf");
 }
 
 #if defined(OS_CHROMEOS)
@@ -689,15 +680,15 @@
 #define MAYBE_AnnotationsFeatureEnabled AnnotationsFeatureEnabled
 #endif
 IN_PROC_BROWSER_TEST_F(PDFAnnotationsTest, MAYBE_AnnotationsFeatureEnabled) {
-  RunTestsInFile("annotations_feature_enabled_test.js", "test.pdf");
+  RunTestsInJsModule("annotations_feature_enabled_test.js", "test.pdf");
 }
 
 IN_PROC_BROWSER_TEST_F(PDFExtensionTest, AnnotationsFeatureDisabled) {
-  RunTestsInFile("annotations_feature_disabled_test.js", "test.pdf");
+  RunTestsInJsModule("annotations_feature_disabled_test.js", "test.pdf");
 }
 
 IN_PROC_BROWSER_TEST_F(PDFExtensionTest, Printing) {
-  RunTestsInFile("printing_icon_test.js", "test.pdf");
+  RunTestsInJsModule("printing_icon_test.js", "test.pdf");
 }
 #endif
 
@@ -714,15 +705,15 @@
           "chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai"),
       CONTENT_SETTINGS_TYPE_JAVASCRIPT, std::string(), CONTENT_SETTING_BLOCK);
 
-  RunTestsInFile("nobeep_test.js", "test-beep.pdf");
+  RunTestsInJsModule("nobeep_test.js", "test-beep.pdf");
 }
 
 IN_PROC_BROWSER_TEST_F(PDFExtensionTest, PageChange) {
-  RunTestsInFile("page_change_test.js", "test-bookmarks.pdf");
+  RunTestsInJsModule("page_change_test.js", "test-bookmarks.pdf");
 }
 
 IN_PROC_BROWSER_TEST_F(PDFExtensionTest, Metrics) {
-  RunTestsInFile("metrics_test.js", "test.pdf");
+  RunTestsInJsModule("metrics_test.js", "test.pdf");
 }
 
 // Ensure that the internal PDF plugin application/x-google-chrome-pdf won't be
@@ -1179,7 +1170,7 @@
 // to load. This is to avoid the source origin of the document changing during
 // the redirect, which can have security implications. https://crbug.com/653749.
 IN_PROC_BROWSER_TEST_F(PDFExtensionTest, RedirectsFailInPlugin) {
-  RunTestsInFile("redirects_fail_test.js", "test.pdf");
+  RunTestsInJsModule("redirects_fail_test.js", "test.pdf");
 }
 
 // Test that even if a different tab is selected when a navigation occurs,
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index a960db92..510a4dc 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -331,7 +331,6 @@
 #if defined(OS_MACOSX)
 #include "chrome/browser/ui/cocoa/apps/quit_with_apps_controller_mac.h"
 #include "chrome/browser/ui/cocoa/confirm_quit.h"
-#include "components/os_crypt/os_crypt.h"
 #endif
 
 #if defined(OS_WIN)
@@ -345,6 +344,10 @@
 #include "chrome/browser/safe_browsing/settings_reset_prompt/settings_reset_prompt_prefs_manager.h"
 #endif
 
+#if defined(OS_WIN) || defined(OS_MACOSX)
+#include "components/os_crypt/os_crypt.h"
+#endif
+
 #if defined(OS_WIN) || defined(OS_MACOSX) || \
     (defined(OS_LINUX) && !defined(OS_CHROMEOS))
 #include "chrome/browser/browser_switcher/browser_switcher_prefs.h"
@@ -729,11 +732,14 @@
 
 #if defined(OS_MACOSX)
   confirm_quit::RegisterLocalState(registry);
-  OSCrypt::RegisterLocalPrefs(registry);
   QuitWithAppsController::RegisterPrefs(registry);
   system_media_permissions::RegisterSystemMediaPermissionStatesPrefs(registry);
 #endif
 
+#if defined(OS_WIN) || defined(OS_MACOSX)
+  OSCrypt::RegisterLocalPrefs(registry);
+#endif
+
 #if defined(OS_WIN)
   registry->RegisterBooleanPref(prefs::kRendererCodeIntegrityEnabled, true);
   component_updater::RegisterPrefsForSwReporter(registry);
diff --git a/chrome/browser/resources/chromeos/bluetooth_pairing_dialog/bluetooth_pairing_dialog.html b/chrome/browser/resources/chromeos/bluetooth_pairing_dialog/bluetooth_pairing_dialog.html
index 5aaccb29..eb2ed339 100644
--- a/chrome/browser/resources/chromeos/bluetooth_pairing_dialog/bluetooth_pairing_dialog.html
+++ b/chrome/browser/resources/chromeos/bluetooth_pairing_dialog/bluetooth_pairing_dialog.html
@@ -15,7 +15,7 @@
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
 <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
 
-<script src="chrome://bluetooth-pairing/strings.js"></script>
+<script src="strings.js"></script>
 
 </head>
 
diff --git a/chrome/browser/resources/chromeos/internet_config_dialog/BUILD.gn b/chrome/browser/resources/chromeos/internet_config_dialog/BUILD.gn
index 5137ae3..6d581242 100644
--- a/chrome/browser/resources/chromeos/internet_config_dialog/BUILD.gn
+++ b/chrome/browser/resources/chromeos/internet_config_dialog/BUILD.gn
@@ -9,6 +9,7 @@
   host = "internet_config_dialog"
   html_in_files = [ "internet_config_dialog.html" ]
   html_out_files = [ "vulcanized.html" ]
+  excludes = [ "chrome://resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom.html" ]
 
   input = rebase_path(".", root_build_dir)
   js_out_files = [ "crisper.js" ]
diff --git a/chrome/browser/resources/chromeos/internet_detail_dialog/BUILD.gn b/chrome/browser/resources/chromeos/internet_detail_dialog/BUILD.gn
index c292ee7e..7cc89e7 100644
--- a/chrome/browser/resources/chromeos/internet_detail_dialog/BUILD.gn
+++ b/chrome/browser/resources/chromeos/internet_detail_dialog/BUILD.gn
@@ -12,6 +12,7 @@
 
   input = rebase_path(".", root_build_dir)
   js_out_files = [ "crisper.js" ]
+  excludes = [ "chrome://resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom.html" ]
 
   deps = []
 }
diff --git a/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.html b/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.html
index eb53b7c..02371a2 100644
--- a/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.html
+++ b/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.html
@@ -30,7 +30,7 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
 <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
 
-<script src="chrome://internet-detail-dialog/strings.js"></script>
+<script src="strings.js"></script>
 
 </head>
 
diff --git a/chrome/browser/resources/component_extension_resources.grd b/chrome/browser/resources/component_extension_resources.grd
index a534601..d4e08a64 100644
--- a/chrome/browser/resources/component_extension_resources.grd
+++ b/chrome/browser/resources/component_extension_resources.grd
@@ -98,7 +98,7 @@
              PDF in print preview. -->
         <include name="IDR_PDF_INDEX_HTML" file="pdf/index.html" allowexternalscript="true" type="BINDATA" preprocess="true" />
         <include name="IDR_PDF_INDEX_CSS" file="pdf/index.css" allowexternalscript="true" type="BINDATA" />
-        <include name="IDR_PDF_MAIN_JS" file="pdf/main.js" type="BINDATA" />
+        <include name="IDR_PDF_MAIN_JS" file="pdf/main.js" type="BINDATA" preprocess="true" />
         <include name="IDR_PDF_PDF_VIEWER_JS" file="pdf/pdf_viewer.js" type="BINDATA" />
         <include name="IDR_PDF_CONTROLLER_JS" file="pdf/controller.js" type="BINDATA" />
         <include name="IDR_PDF_TOOLBAR_MANAGER_JS" file="pdf/toolbar_manager.js" type="BINDATA" />
@@ -113,39 +113,24 @@
         <include name="IDR_PDF_BROWSER_API_JS" file="pdf/browser_api.js" type="BINDATA" />
         <include name="IDR_PDF_METRICS_JS" file="pdf/metrics.js" type="BINDATA" />
 
-        <include name="IDR_PDF_SHARED_VARS_HTML" file="pdf/elements/shared-vars.html" type="BINDATA" />
-        <!-- TODO(crbug.com/1005029): Dummy generated file used for ChromeComponentExtensionResourceManager tests.
-            Replace with a file actually used by the PDF Viewer once migration to Polymer3 is completed. -->
-        <include name="IDR_PDF_SHARED_VARS_M_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/shared-vars.m.js" use_base_dir="false" type="BINDATA" />
-        <include name="IDR_PDF_ICONS_HTML" file="pdf/elements/icons.html" type="BINDATA" />
-        <include name="IDR_PDF_VIEWER_BOOKMARK_HTML" file="pdf/elements/viewer-bookmark.html" type="BINDATA" />
-        <include name="IDR_PDF_VIEWER_BOOKMARK_JS" file="pdf/elements/viewer-bookmark.js" type="BINDATA" />
-        <include name="IDR_PDF_VIEWER_ERROR_SCREEN_HTML" file="pdf/elements/viewer-error-screen.html" type="BINDATA" />
-        <include name="IDR_PDF_VIEWER_ERROR_SCREEN_JS" file="pdf/elements/viewer-error-screen.js" type="BINDATA" />
+        <include name="IDR_PDF_SHARED_VARS_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/shared-vars.js" use_base_dir="false" type="BINDATA" />
+        <include name="IDR_PDF_ICONS_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/icons.js" use_base_dir="false" type="BINDATA" />
+        <include name="IDR_PDF_VIEWER_BOOKMARK_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/viewer-bookmark.js" use_base_dir="false" type="BINDATA" />
+        <include name="IDR_PDF_VIEWER_ERROR_SCREEN_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/viewer-error-screen.js" use_base_dir="false" type="BINDATA" />
         <if expr="chromeos">
           <include name="IDR_PDF_VIEWER_INK_INDEX_HTML" file="pdf/ink/index.html" type="BINDATA" />
           <include name="IDR_PDF_VIEWER_INK_INK_API_JS" file="pdf/ink/ink_api.js" type="BINDATA" />
-          <include name="IDR_PDF_VIEWER_INK_HOST_HTML" file="pdf/elements/viewer-ink-host.html" type="BINDATA" />
-          <include name="IDR_PDF_VIEWER_INK_HOST_JS" file="pdf/elements/viewer-ink-host.js" type="BINDATA" />
-          <include name="IDR_PDF_VIEWER_PEN_OPTIONS_HTML" file="pdf/elements/viewer-pen-options.html" type="BINDATA" />
-          <include name="IDR_PDF_VIEWER_PEN_OPTIONS_JS" file="pdf/elements/viewer-pen-options.js" type="BINDATA" />
-          <include name="IDR_PDF_VIEWER_FORM_WARNING_HTML" file="pdf/elements/viewer-form-warning.html" type="BINDATA" />
-          <include name="IDR_PDF_VIEWER_FORM_WARNING_JS" file="pdf/elements/viewer-form-warning.js" type="BINDATA" />
+          <include name="IDR_PDF_VIEWER_INK_HOST_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/viewer-ink-host.js" use_base_dir="false" type="BINDATA" />
+          <include name="IDR_PDF_VIEWER_PEN_OPTIONS_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/viewer-pen-options.js" use_base_dir="false" type="BINDATA" />
+          <include name="IDR_PDF_VIEWER_FORM_WARNING_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/viewer-form-warning.js" use_base_dir="false" type="BINDATA" />
         </if>
-        <include name="IDR_PDF_VIEWER_PAGE_INDICATOR_HTML" file="pdf/elements/viewer-page-indicator.html" type="BINDATA" />
-        <include name="IDR_PDF_VIEWER_PAGE_INDICATOR_JS" file="pdf/elements/viewer-page-indicator.js" type="BINDATA" flattenhtml="true" />
-        <include name="IDR_PDF_VIEWER_PAGE_SELECTOR_HTML" file="pdf/elements/viewer-page-selector.html" type="BINDATA" />
-        <include name="IDR_PDF_VIEWER_PAGE_SELECTOR_JS" file="pdf/elements/viewer-page-selector.js" type="BINDATA" />
-        <include name="IDR_PDF_VIEWER_PASSWORD_SCREEN_HTML" file="pdf/elements/viewer-password-screen.html" type="BINDATA" />
-        <include name="IDR_PDF_VIEWER_PASSWORD_SCREEN_JS" file="pdf/elements/viewer-password-screen.js" type="BINDATA" />
-        <include name="IDR_PDF_VIEWER_PDF_TOOLBAR_HTML" file="pdf/elements/viewer-pdf-toolbar.html" type="BINDATA" preprocess="true" />
-        <include name="IDR_PDF_VIEWER_PDF_TOOLBAR_JS" file="pdf/elements/viewer-pdf-toolbar.js" type="BINDATA" />
-        <include name="IDR_PDF_VIEWER_TOOLBAR_DROPDOWN_HTML" file="pdf/elements/viewer-toolbar-dropdown.html" type="BINDATA" />
-        <include name="IDR_PDF_VIEWER_TOOLBAR_DROPDOWN_JS" file="pdf/elements/viewer-toolbar-dropdown.js" type="BINDATA" />
-        <include name="IDR_PDF_VIEWER_ZOOM_BUTTON_HTML" file="pdf/elements/viewer-zoom-button.html" type="BINDATA" />
-        <include name="IDR_PDF_VIEWER_ZOOM_BUTTON_JS" file="pdf/elements/viewer-zoom-button.js" type="BINDATA" />
-        <include name="IDR_PDF_VIEWER_ZOOM_SELECTOR_HTML" file="pdf/elements/viewer-zoom-toolbar.html" type="BINDATA" />
-        <include name="IDR_PDF_VIEWER_ZOOM_SELECTOR_JS" file="pdf/elements/viewer-zoom-toolbar.js" type="BINDATA" />
+        <include name="IDR_PDF_VIEWER_PAGE_INDICATOR_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/viewer-page-indicator.js" use_base_dir="false" type="BINDATA" />
+        <include name="IDR_PDF_VIEWER_PAGE_SELECTOR_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/viewer-page-selector.js" use_base_dir="false" type="BINDATA" />
+        <include name="IDR_PDF_VIEWER_PASSWORD_SCREEN_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/viewer-password-screen.js" use_base_dir="false" type="BINDATA" />
+        <include name="IDR_PDF_VIEWER_PDF_TOOLBAR_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar.js" use_base_dir="false" type="BINDATA" preprocess="true"/>
+        <include name="IDR_PDF_VIEWER_TOOLBAR_DROPDOWN_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown.js" use_base_dir="false" type="BINDATA" />
+        <include name="IDR_PDF_VIEWER_ZOOM_BUTTON_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/viewer-zoom-button.js" use_base_dir="false" type="BINDATA" />
+        <include name="IDR_PDF_VIEWER_ZOOM_SELECTOR_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar.js" use_base_dir="false" type="BINDATA" />
       </if>
       <include name="IDR_CRYPTOTOKEN_UTIL_JS" file="cryptotoken/util.js" type="BINDATA" />
       <include name="IDR_CRYPTOTOKEN_B64_JS" file="cryptotoken/b64.js" type="BINDATA" />
diff --git a/chrome/browser/resources/optimize_webui.py b/chrome/browser/resources/optimize_webui.py
index e7804916..3776afb 100755
--- a/chrome/browser/resources/optimize_webui.py
+++ b/chrome/browser/resources/optimize_webui.py
@@ -23,26 +23,36 @@
 import node_modules
 
 
-_RESOURCES_PATH = os.path.join(_SRC_PATH, 'ui', 'webui', 'resources')
+_RESOURCES_PATH = os.path.join(
+    _SRC_PATH, 'ui', 'webui', 'resources', '').replace('\\', '/')
 
 
-_CR_ELEMENTS_PATH = os.path.join(_RESOURCES_PATH, 'cr_elements')
+_CR_ELEMENTS_PATH = os.path.join(
+    _RESOURCES_PATH, 'cr_elements', '').replace('\\', '/')
 
 
-_CR_COMPONENTS_PATH = os.path.join(_RESOURCES_PATH, 'cr_components')
+_CR_COMPONENTS_PATH = os.path.join(
+    _RESOURCES_PATH, 'cr_components', '').replace('\\', '/')
 
 
-_CSS_RESOURCES_PATH = os.path.join(_RESOURCES_PATH, 'css')
+_CSS_RESOURCES_PATH = os.path.join(
+    _RESOURCES_PATH, 'css', '').replace('\\', '/')
 
 
-_HTML_RESOURCES_PATH = os.path.join(_RESOURCES_PATH, 'html')
+_HTML_RESOURCES_PATH = os.path.join(
+    _RESOURCES_PATH, 'html', '').replace('\\', '/')
 
 
-_JS_RESOURCES_PATH = os.path.join(_RESOURCES_PATH, 'js')
+_JS_RESOURCES_PATH = os.path.join(_RESOURCES_PATH, 'js', '').replace('\\', '/')
+
+
+_IMAGES_RESOURCES_PATH = os.path.join(
+    _RESOURCES_PATH, 'images', '').replace('\\', '/')
 
 
 _POLYMER_PATH = os.path.join(
-    _SRC_PATH, 'third_party', 'polymer', 'v1_0', 'components-chromium')
+    _SRC_PATH, 'third_party', 'polymer', 'v1_0', 'components-chromium',
+    '').replace('\\', '/')
 
 
 _VULCANIZE_BASE_ARGS = [
@@ -60,6 +70,7 @@
   '--exclude', 'chrome://resources/css/text_defaults.css',
   '--exclude', 'chrome://resources/css/text_defaults_md.css',
   '--exclude', 'chrome://resources/js/load_time_data.js',
+  '--exclude', 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.html',
 
   '--inline-css',
   '--inline-scripts',
@@ -74,7 +85,8 @@
     ('chrome://resources/css/', _CSS_RESOURCES_PATH),
     ('chrome://resources/html/', _HTML_RESOURCES_PATH),
     ('chrome://resources/js/', _JS_RESOURCES_PATH),
-    ('chrome://resources/polymer/v1_0/', _POLYMER_PATH)
+    ('chrome://resources/polymer/v1_0/', _POLYMER_PATH),
+    ('chrome://resources/images/', _IMAGES_RESOURCES_PATH)
 ]
 
 
@@ -130,8 +142,8 @@
 
 
 def _optimize(in_folder, args):
-  in_path = os.path.normpath(os.path.join(_CWD, in_folder))
-  out_path = os.path.join(_CWD, args.out_folder)
+  in_path = os.path.normpath(os.path.join(_CWD, in_folder)).replace('\\', '/')
+  out_path = os.path.join(_CWD, args.out_folder).replace('\\', '/')
   manifest_out_path = _request_list_path(out_path, args.host)
 
   exclude_args = []
@@ -141,10 +153,9 @@
 
   in_html_args = []
   for f in args.html_in_files:
-    in_html_args.append('--in-html')
     in_html_args.append(f)
 
-  tmp_out_dir = os.path.join(out_path, 'bundled')
+  tmp_out_dir = os.path.join(out_path, 'bundled').replace('\\', '/')
   node.RunNode(
       [node_modules.PathToBundler()] +
       _VULCANIZE_BASE_ARGS + _VULCANIZE_REDIRECT_ARGS + exclude_args +
@@ -155,8 +166,8 @@
 
        '--manifest-out', manifest_out_path,
        '--root', in_path,
-       '--redirect', '"chrome://%s/|%s"' % (args.host, in_path),
-       '--out-dir', os.path.relpath(tmp_out_dir, _CWD),
+       '--redirect', '"chrome://%s/|%s"' % (args.host, in_path + '/'),
+       '--out-dir', os.path.relpath(tmp_out_dir, _CWD).replace('\\', '/'),
        '--shell', args.html_in_files[0],
       ] + in_html_args)
 
diff --git a/chrome/browser/resources/pdf/BUILD.gn b/chrome/browser/resources/pdf/BUILD.gn
index 0793818..095c13b 100644
--- a/chrome/browser/resources/pdf/BUILD.gn
+++ b/chrome/browser/resources/pdf/BUILD.gn
@@ -113,6 +113,7 @@
 }
 
 js_type_check("pdf_resources") {
+  is_polymer3 = true
   deps = [
     ":browser_api",
     ":controller",
diff --git a/chrome/browser/resources/pdf/elements/BUILD.gn b/chrome/browser/resources/pdf/elements/BUILD.gn
index 248059ae..e9de70e 100644
--- a/chrome/browser/resources/pdf/elements/BUILD.gn
+++ b/chrome/browser/resources/pdf/elements/BUILD.gn
@@ -6,6 +6,7 @@
 import("//tools/polymer/polymer.gni")
 
 js_type_check("closure_compile") {
+  is_polymer3 = true
   deps = [
     ":viewer-bookmark",
     ":viewer-error-screen",
@@ -28,21 +29,21 @@
 
 js_library("viewer-bookmark") {
   deps = [
-    "//third_party/polymer/v1_0/components-chromium/iron-a11y-keys-behavior:iron-a11y-keys-behavior-extracted",
+    "//third_party/polymer/v3_0/components-chromium/iron-a11y-keys-behavior",
   ]
 }
 
 js_library("viewer-error-screen") {
   deps = [
-    "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog",
+    "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog.m",
   ]
 }
 
 if (is_chromeos) {
   js_library("viewer-form-warning") {
     deps = [
-      "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog",
-      "//ui/webui/resources/js:promise_resolver",
+      "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog.m",
+      "//ui/webui/resources/js:promise_resolver.m",
     ]
   }
 
@@ -57,20 +58,20 @@
 
 js_library("viewer-page-indicator") {
   deps = [
-    "//ui/webui/resources/js:assert",
-    "//ui/webui/resources/js:util",
+    "//ui/webui/resources/js:assert.m",
+    "//ui/webui/resources/js:util.m",
   ]
 }
 
 js_library("viewer-page-selector") {
   deps = [
-    "//ui/webui/resources/cr_elements/cr_input:cr_input",
+    "//ui/webui/resources/cr_elements/cr_input:cr_input.m",
   ]
 }
 
 js_library("viewer-password-screen") {
   deps = [
-    "//ui/webui/resources/cr_elements/cr_input:cr_input",
+    "//ui/webui/resources/cr_elements/cr_input:cr_input.m",
   ]
 }
 
@@ -85,7 +86,6 @@
 }
 
 js_library("viewer-pen-options") {
-  externs_list = [ "$externs_path/pending_polymer.js" ]
 }
 
 js_library("viewer-toolbar-dropdown") {
@@ -96,8 +96,8 @@
   deps = [
     ":viewer-zoom-button",
     "..:pdf_fitting_type",
-    "//ui/webui/resources/js:assert",
-    "//ui/webui/resources/js:util",
+    "//ui/webui/resources/js:assert.m",
+    "//ui/webui/resources/js:util.m",
   ]
 }
 
@@ -105,14 +105,109 @@
   deps = []
 }
 
+polymer_modulizer("viewer-bookmark") {
+  js_file = "viewer-bookmark.js"
+  html_file = "viewer-bookmark.html"
+  html_type = "v3-ready"
+}
+
+polymer_modulizer("viewer-error-screen") {
+  js_file = "viewer-error-screen.js"
+  html_file = "viewer-error-screen.html"
+  html_type = "v3-ready"
+}
+
+polymer_modulizer("viewer-form-warning") {
+  js_file = "viewer-form-warning.js"
+  html_file = "viewer-form-warning.html"
+  html_type = "v3-ready"
+}
+
+polymer_modulizer("viewer-ink-host") {
+  js_file = "viewer-ink-host.js"
+  html_file = "viewer-ink-host.html"
+  html_type = "v3-ready"
+}
+
+polymer_modulizer("viewer-page-indicator") {
+  js_file = "viewer-page-indicator.js"
+  html_file = "viewer-page-indicator.html"
+  html_type = "v3-ready"
+}
+
+polymer_modulizer("viewer-page-selector") {
+  js_file = "viewer-page-selector.js"
+  html_file = "viewer-page-selector.html"
+  html_type = "v3-ready"
+}
+
+polymer_modulizer("viewer-password-screen") {
+  js_file = "viewer-password-screen.js"
+  html_file = "viewer-password-screen.html"
+  html_type = "v3-ready"
+}
+
+polymer_modulizer("viewer-pdf-toolbar") {
+  js_file = "viewer-pdf-toolbar.js"
+  html_file = "viewer-pdf-toolbar.html"
+  html_type = "v3-ready"
+}
+
+polymer_modulizer("viewer-pen-options") {
+  js_file = "viewer-pen-options.js"
+  html_file = "viewer-pen-options.html"
+  html_type = "v3-ready"
+}
+
+polymer_modulizer("viewer-toolbar-dropdown") {
+  js_file = "viewer-toolbar-dropdown.js"
+  html_file = "viewer-toolbar-dropdown.html"
+  html_type = "v3-ready"
+}
+
+polymer_modulizer("viewer-zoom-button") {
+  js_file = "viewer-zoom-button.js"
+  html_file = "viewer-zoom-button.html"
+  html_type = "v3-ready"
+}
+
+polymer_modulizer("viewer-zoom-toolbar") {
+  js_file = "viewer-zoom-toolbar.js"
+  html_file = "viewer-zoom-toolbar.html"
+  html_type = "v3-ready"
+}
+
 polymer_modulizer("shared-vars") {
-  js_file = "shared-vars.m.js"
+  js_file = "shared-vars.js"
   html_file = "shared-vars.html"
-  html_type = "custom-style"
+  html_type = "v3-ready"
+}
+
+polymer_modulizer("icons") {
+  js_file = "icons.js"
+  html_file = "icons.html"
+  html_type = "v3-ready"
 }
 
 group("polymer3_elements") {
   deps = [
+    ":icons_module",
     ":shared-vars_module",
+    ":viewer-bookmark_module",
+    ":viewer-error-screen_module",
+    ":viewer-page-indicator_module",
+    ":viewer-page-selector_module",
+    ":viewer-password-screen_module",
+    ":viewer-pdf-toolbar_module",
+    ":viewer-toolbar-dropdown_module",
+    ":viewer-zoom-button_module",
+    ":viewer-zoom-toolbar_module",
   ]
+  if (is_chromeos) {
+    deps += [
+      ":viewer-form-warning_module",
+      ":viewer-ink-host_module",
+      ":viewer-pen-options_module",
+    ]
+  }
 }
diff --git a/chrome/browser/resources/pdf/elements/icons.html b/chrome/browser/resources/pdf/elements/icons.html
index a5976b1..f24a50a 100644
--- a/chrome/browser/resources/pdf/elements/icons.html
+++ b/chrome/browser/resources/pdf/elements/icons.html
@@ -1,6 +1,3 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-iconset-svg/iron-iconset-svg.html">
-
 <iron-iconset-svg size="24" name="pdf">
   <svg>
     <defs>
diff --git a/chrome/browser/resources/pdf/elements/icons.js b/chrome/browser/resources/pdf/elements/icons.js
new file mode 100644
index 0000000..02ab37aa
--- /dev/null
+++ b/chrome/browser/resources/pdf/elements/icons.js
@@ -0,0 +1,10 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome://resources/polymer/v3_0/iron-iconset-svg/iron-iconset-svg.js';
+
+import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+const template = html`{__html_template__}`;
+document.head.appendChild(template.content);
diff --git a/chrome/browser/resources/pdf/elements/shared-vars.html b/chrome/browser/resources/pdf/elements/shared-vars.html
index 0a9eaf5..8a72005 100644
--- a/chrome/browser/resources/pdf/elements/shared-vars.html
+++ b/chrome/browser/resources/pdf/elements/shared-vars.html
@@ -1,13 +1,9 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
-
 <custom-style>
-<style>
-  html {
-    --iron-icon-height: 20px;
-    --iron-icon-width: 20px;
-    --viewer-icon-ink-color: rgb(189, 189, 189);
-  }
-</style>
+  <style>
+    html {
+      --iron-icon-height: 20px;
+      --iron-icon-width: 20px;
+      --viewer-icon-ink-color: rgb(189, 189, 189);
+    }
+  </style>
 </custom-style>
diff --git a/chrome/browser/resources/pdf/elements/shared-vars.js b/chrome/browser/resources/pdf/elements/shared-vars.js
new file mode 100644
index 0000000..9c9ca1bd
--- /dev/null
+++ b/chrome/browser/resources/pdf/elements/shared-vars.js
@@ -0,0 +1,11 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome://resources/polymer/v3_0/paper-styles/color.js';
+
+import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+const $_documentContainer = document.createElement('template');
+$_documentContainer.innerHTML = `{__html_template__}`;
+document.head.appendChild($_documentContainer.content);
diff --git a/chrome/browser/resources/pdf/elements/viewer-bookmark.html b/chrome/browser/resources/pdf/elements/viewer-bookmark.html
index 004aa1b..9e467bc0 100644
--- a/chrome/browser/resources/pdf/elements/viewer-bookmark.html
+++ b/chrome/browser/resources/pdf/elements/viewer-bookmark.html
@@ -1,13 +1,3 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-
-<link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html">
-<link rel="import" href="chrome://resources/cr_elements/icons.html">
-<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-ripple/paper-ripple.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
-
-<dom-module id="viewer-bookmark" attributes="bookmark">
-  <template>
     <style>
       #item {
         @apply --layout-center;
@@ -67,6 +57,3 @@
         </viewer-bookmark>
       </template>
     </template>
-  </template>
-  <script src="viewer-bookmark.js"></script>
-</dom-module>
diff --git a/chrome/browser/resources/pdf/elements/viewer-bookmark.js b/chrome/browser/resources/pdf/elements/viewer-bookmark.js
index f25ebce..fb9f73b 100644
--- a/chrome/browser/resources/pdf/elements/viewer-bookmark.js
+++ b/chrome/browser/resources/pdf/elements/viewer-bookmark.js
@@ -2,6 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.m.js';
+import 'chrome://resources/cr_elements/icons.m.js';
+import 'chrome://resources/cr_elements/shared_vars_css.m.js';
+import 'chrome://resources/polymer/v3_0/paper-ripple/paper-ripple.js';
+import 'chrome://resources/polymer/v3_0/paper-styles/color.js';
+
+import {IronA11yKeysBehavior} from 'chrome://resources/polymer/v3_0/iron-a11y-keys-behavior/iron-a11y-keys-behavior.js';
+import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
 /**
  * The |title| is the text label displayed for the bookmark.
  *
@@ -24,15 +33,16 @@
  *   children: !Array<!Bookmark>
  * }}
  */
-let Bookmark;
+export let Bookmark;
 
-(function() {
 /** Amount that each level of bookmarks is indented by (px). */
 const BOOKMARK_INDENT = 20;
 
 Polymer({
   is: 'viewer-bookmark',
 
+  _template: html`{__html_template__}`,
+
   properties: {
     /** @type {Bookmark} */
     bookmark: {
@@ -55,11 +65,11 @@
       value: false,
     },
 
-    /** @type {?HTMLElement} The target for the key bindings below. */
+    /** @type {?EventTarget} The target for the key bindings below. */
     keyEventTarget: Object,
   },
 
-  behaviors: [Polymer.IronA11yKeysBehavior],
+  behaviors: [IronA11yKeysBehavior],
 
   keyBindings: {'enter': 'onEnter_', 'space': 'onSpace_'},
 
@@ -137,4 +147,3 @@
     e.stopPropagation();  // Prevent the above onClick_ handler from firing.
   }
 });
-})();
diff --git a/chrome/browser/resources/pdf/elements/viewer-error-screen.html b/chrome/browser/resources/pdf/elements/viewer-error-screen.html
index 25737d4..0304714 100644
--- a/chrome/browser/resources/pdf/elements/viewer-error-screen.html
+++ b/chrome/browser/resources/pdf/elements/viewer-error-screen.html
@@ -1,11 +1,3 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-
-<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
-<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
-
-<dom-module id="viewer-error-screen">
-  <template>
     <style include="cr-hidden-style"></style>
     <cr-dialog id="dialog" no-cancel>
       <div slot="title">[[strings.errorDialogTitle]]</div>
@@ -16,6 +8,3 @@
         </cr-button>
       </div>
     </cr-dialog>
-  </template>
-  <script src="viewer-error-screen.js"></script>
-</dom-module>
diff --git a/chrome/browser/resources/pdf/elements/viewer-error-screen.js b/chrome/browser/resources/pdf/elements/viewer-error-screen.js
index 082220a..7bb0122 100644
--- a/chrome/browser/resources/pdf/elements/viewer-error-screen.js
+++ b/chrome/browser/resources/pdf/elements/viewer-error-screen.js
@@ -2,8 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
+import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.m.js';
+import 'chrome://resources/cr_elements/hidden_style_css.m.js';
+
+import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
 Polymer({
   is: 'viewer-error-screen',
+
+  _template: html`{__html_template__}`,
+
   properties: {
     reloadFn: Function,
 
diff --git a/chrome/browser/resources/pdf/elements/viewer-form-warning.html b/chrome/browser/resources/pdf/elements/viewer-form-warning.html
index 1e9e1ee..1906119 100644
--- a/chrome/browser/resources/pdf/elements/viewer-form-warning.html
+++ b/chrome/browser/resources/pdf/elements/viewer-form-warning.html
@@ -1,11 +1,3 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-
-<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
-<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
-
-<dom-module id="viewer-form-warning">
-  <template>
     <style include="cr-hidden-style"></style>
     <cr-dialog id="dialog" no-cancel>
       <div slot="title">[[strings.annotationFormWarningTitle]]</div>
@@ -19,6 +11,3 @@
         </cr-button>
       </div>
     </cr-dialog>
-  </template>
-  <script src="viewer-form-warning.js"></script>
-</dom-module>
diff --git a/chrome/browser/resources/pdf/elements/viewer-form-warning.js b/chrome/browser/resources/pdf/elements/viewer-form-warning.js
index 539eddd6..5bd8f3c 100644
--- a/chrome/browser/resources/pdf/elements/viewer-form-warning.js
+++ b/chrome/browser/resources/pdf/elements/viewer-form-warning.js
@@ -2,8 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
+import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.m.js';
+import 'chrome://resources/cr_elements/hidden_style_css.m.js';
+
+import {PromiseResolver} from 'chrome://resources/js/promise_resolver.m.js';
+import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
 Polymer({
   is: 'viewer-form-warning',
+
+  _template: html`{__html_template__}`,
+
   properties: {
     strings: Object,
   },
diff --git a/chrome/browser/resources/pdf/elements/viewer-ink-host.html b/chrome/browser/resources/pdf/elements/viewer-ink-host.html
index 1a7a2ff..c4277ac 100644
--- a/chrome/browser/resources/pdf/elements/viewer-ink-host.html
+++ b/chrome/browser/resources/pdf/elements/viewer-ink-host.html
@@ -1,7 +1,3 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-
-<dom-module id="viewer-ink-host">
-  <template>
     <style>
       :host {
         visibility: hidden;
@@ -14,6 +10,3 @@
       }
     </style>
     <iframe id=frame></iframe>
-  </template>
-  <script src="viewer-ink-host.js"></script>
-</dom-module>
diff --git a/chrome/browser/resources/pdf/elements/viewer-ink-host.js b/chrome/browser/resources/pdf/elements/viewer-ink-host.js
index 285edf8a3..a9b474d 100644
--- a/chrome/browser/resources/pdf/elements/viewer-ink-host.js
+++ b/chrome/browser/resources/pdf/elements/viewer-ink-host.js
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
 /** @enum {string} */
 const State = {
   LOADING: 'loading',
@@ -18,6 +20,8 @@
 Polymer({
   is: 'viewer-ink-host',
 
+  _template: html`{__html_template__}`,
+
   /** @private {InkAPI} */
   ink_: null,
 
diff --git a/chrome/browser/resources/pdf/elements/viewer-page-indicator.html b/chrome/browser/resources/pdf/elements/viewer-page-indicator.html
index 70ad257..4659288 100644
--- a/chrome/browser/resources/pdf/elements/viewer-page-indicator.html
+++ b/chrome/browser/resources/pdf/elements/viewer-page-indicator.html
@@ -1,9 +1,3 @@
-<link rel="import" href="chrome://resources/html/assert.html">
-<link rel="import" href="chrome://resources/html/polymer.html">
-<link rel="import" href="chrome://resources/html/util.html">
-
-<dom-module id="viewer-page-indicator">
-  <template>
     <style>
       :host {
         display: flex;
@@ -43,6 +37,3 @@
     </style>
     <div id="text">{{label}}</div>
     <div id="triangle-end"></div>
-  </template>
-  <script src="viewer-page-indicator.js"></script>
-</dom-module>
diff --git a/chrome/browser/resources/pdf/elements/viewer-page-indicator.js b/chrome/browser/resources/pdf/elements/viewer-page-indicator.js
index 50ad4f4..469ba84 100644
--- a/chrome/browser/resources/pdf/elements/viewer-page-indicator.js
+++ b/chrome/browser/resources/pdf/elements/viewer-page-indicator.js
@@ -2,9 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {assert} from 'chrome://resources/js/assert.m.js';
+import {isRTL} from 'chrome://resources/js/util.m.js';
+import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
 Polymer({
   is: 'viewer-page-indicator',
 
+  _template: html`{__html_template__}`,
+
   properties: {
     label: {type: String, value: '1'},
 
diff --git a/chrome/browser/resources/pdf/elements/viewer-page-selector.html b/chrome/browser/resources/pdf/elements/viewer-page-selector.html
index e1fdc869..b950e33 100644
--- a/chrome/browser/resources/pdf/elements/viewer-page-selector.html
+++ b/chrome/browser/resources/pdf/elements/viewer-page-selector.html
@@ -1,9 +1,3 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-
-<link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html">
-
-<dom-module id="viewer-page-selector">
-  <template>
     <style>
       :host {
         color: #fff;
@@ -51,6 +45,3 @@
     <span id="pagelength-spacer">
       <span id="pagelength">{{docLength}}</span>
     </span>
-  </template>
-  <script src="viewer-page-selector.js"></script>
-</dom-module>
diff --git a/chrome/browser/resources/pdf/elements/viewer-page-selector.js b/chrome/browser/resources/pdf/elements/viewer-page-selector.js
index b799923..035e5d8 100644
--- a/chrome/browser/resources/pdf/elements/viewer-page-selector.js
+++ b/chrome/browser/resources/pdf/elements/viewer-page-selector.js
@@ -2,9 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'chrome://resources/cr_elements/cr_input/cr_input.m.js';
+
+import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
 Polymer({
   is: 'viewer-page-selector',
 
+  _template: html`{__html_template__}`,
+
   properties: {
     /**
      * The number of pages the document contains.
@@ -26,7 +32,7 @@
 
   /** @return {!CrInputElement} */
   get pageSelector() {
-    return this.$.pageselector;
+    return /** @type {!CrInputElement} */ (this.$.pageselector);
   },
 
   pageNoCommitted: function() {
diff --git a/chrome/browser/resources/pdf/elements/viewer-password-screen.html b/chrome/browser/resources/pdf/elements/viewer-password-screen.html
index aebc16bf..3b4527c 100644
--- a/chrome/browser/resources/pdf/elements/viewer-password-screen.html
+++ b/chrome/browser/resources/pdf/elements/viewer-password-screen.html
@@ -1,13 +1,3 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-
-<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html">
-<link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
-<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
-
-<dom-module id="viewer-password-screen">
-  <template>
     <style include="cr-shared-style">
       #password {
         margin-top: var(--cr-form-field-bottom-spacing);
@@ -30,6 +20,3 @@
         </cr-button>
       </div>
     </cr-dialog>
-  </template>
-  <script src="viewer-password-screen.js"></script>
-</dom-module>
diff --git a/chrome/browser/resources/pdf/elements/viewer-password-screen.js b/chrome/browser/resources/pdf/elements/viewer-password-screen.js
index 5506ad29..5c38842 100644
--- a/chrome/browser/resources/pdf/elements/viewer-password-screen.js
+++ b/chrome/browser/resources/pdf/elements/viewer-password-screen.js
@@ -2,9 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
+import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.m.js';
+import 'chrome://resources/cr_elements/cr_input/cr_input.m.js';
+import 'chrome://resources/cr_elements/shared_style_css.m.js';
+import 'chrome://resources/cr_elements/shared_vars_css.m.js';
+
+import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
 Polymer({
   is: 'viewer-password-screen',
 
+  _template: html`{__html_template__}`,
+
   properties: {
     invalid: Boolean,
 
diff --git a/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar.html b/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar.html
index 348e2215..3aebcbf 100644
--- a/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar.html
+++ b/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar.html
@@ -1,19 +1,3 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-
-<link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html">
-<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-progress/paper-progress.html">
-<link rel="import" href="chrome://resources/cr_elements/icons.html">
-<link rel="import" href="icons.html">
-<link rel="import" href="viewer-bookmark.html">
-<link rel="import" href="viewer-page-selector.html">
-<if expr="chromeos">
-<link rel="import" href="viewer-pen-options.html">
-</if>
-<link rel="import" href="viewer-toolbar-dropdown.html">
-
-<dom-module id="viewer-pdf-toolbar">
-  <template>
     <style include="cr-hidden-style">
       :host ::selection {
         background: rgba(255, 255, 255, 0.3);
@@ -284,6 +268,3 @@
           aria-label$="{{strings.annotationRedo}}"
           title$="{{strings.annotationRedo}}"></cr-icon-button>
     </div>
-  </template>
-  <script src="viewer-pdf-toolbar.js"></script>
-</dom-module>
diff --git a/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar.js b/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar.js
index 07407cf..4f412de 100644
--- a/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar.js
+++ b/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar.js
@@ -2,10 +2,26 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-(function() {
+import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.m.js';
+import 'chrome://resources/cr_elements/hidden_style_css.m.js';
+import 'chrome://resources/polymer/v3_0/paper-progress/paper-progress.js';
+import 'chrome://resources/cr_elements/icons.m.js';
+import './icons.js';
+import './viewer-page-selector.js';
+import './viewer-toolbar-dropdown.js';
+
+// <if expr="chromeos">
+import './viewer-pen-options.js';
+// </if>
+
+import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {Bookmark} from './viewer-bookmark.js';
+
 Polymer({
   is: 'viewer-pdf-toolbar',
 
+  _template: html`{__html_template__}`,
+
   properties: {
     /**
      * Whether annotation mode can be entered. This would be false if for
@@ -201,7 +217,7 @@
     this.annotationMode = !this.annotationMode;
     if (this.annotationMode) {
       // Select pen tool when entering annotation mode.
-      this.updateAnnotationTool_(this.$.pen);
+      this.updateAnnotationTool_(/** @type {!HTMLElement} */ (this.$.pen));
     }
     this.dispatchEvent(new CustomEvent('annotation-mode-toggled', {
       detail: {
@@ -262,4 +278,3 @@
     return !!this.annotationTool && this.annotationTool.tool === toolName;
   },
 });
-})();
diff --git a/chrome/browser/resources/pdf/elements/viewer-pen-options.html b/chrome/browser/resources/pdf/elements/viewer-pen-options.html
index dbf18b0..b44bf89f 100644
--- a/chrome/browser/resources/pdf/elements/viewer-pen-options.html
+++ b/chrome/browser/resources/pdf/elements/viewer-pen-options.html
@@ -1,9 +1,3 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-
-<link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html">
-
-<dom-module id="viewer-pen-options">
-  <template>
     <style>
     #colors,
     #sizes {
@@ -85,6 +79,3 @@
             on-pointerdown="blurOnPointerDown">
       </template>
     </div>
-  </template>
-  <script src="viewer-pen-options.js"></script>
-</dom-module>
diff --git a/chrome/browser/resources/pdf/elements/viewer-pen-options.js b/chrome/browser/resources/pdf/elements/viewer-pen-options.js
index d6334340..134a0e8f 100644
--- a/chrome/browser/resources/pdf/elements/viewer-pen-options.js
+++ b/chrome/browser/resources/pdf/elements/viewer-pen-options.js
@@ -2,6 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.m.js';
+
+import {beforeNextRender, html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
 const colors = [
   // row 1
   {name: 'annotationColorBlack', color: '#000000'},
@@ -55,6 +59,8 @@
 Polymer({
   is: 'viewer-pen-options',
 
+  _template: html`{__html_template__}`,
+
   properties: {
     expanded_: {
       type: Boolean,
@@ -111,7 +117,7 @@
 
   /** @override */
   attached: function() {
-    Polymer.RenderStatus.beforeNextRender(this, () => {
+    beforeNextRender(this, () => {
       this.updateExpandedStateAndFinishAnimations_();
     });
   },
@@ -126,21 +132,36 @@
       const separator = this.$.separator;
       const expand = this.$.expand;
       this.expandAnimations_ = [
-        colors.animate({height: ['32px', '188px']}, {
-          easing: 'ease-in-out',
-          duration: 250,
-          fill: 'both',
-        }),
-        separator.animate({opacity: [0, 1]}, {
-          easing: 'ease-in-out',
-          duration: 250,
-          fill: 'both',
-        }),
-        expand.animate({transform: ['rotate(0deg)', 'rotate(180deg)']}, {
-          easing: 'ease-in-out',
-          duration: 250,
-          fill: 'forwards',
-        }),
+        colors.animate(
+            [
+              {height: '32px'},
+              {height: '188px'},
+            ],
+            {
+              easing: 'ease-in-out',
+              duration: 250,
+              fill: 'both',
+            }),
+        separator.animate(
+            [
+              {opacity: 0},
+              {opacity: 1},
+            ],
+            {
+              easing: 'ease-in-out',
+              duration: 250,
+              fill: 'both',
+            }),
+        expand.animate(
+            [
+              {transform: 'rotate(0deg)'},
+              {transform: 'rotate(180deg)'},
+            ],
+            {
+              easing: 'ease-in-out',
+              duration: 250,
+              fill: 'forwards',
+            }),
       ];
     }
     for (const animation of this.expandAnimations_) {
diff --git a/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown.html b/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown.html
index 1ebf0df..7c31ae61 100644
--- a/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown.html
+++ b/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown.html
@@ -1,11 +1,3 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-
-<link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html">
-<link rel="import" href="chrome://resources/cr_elements/icons.html">
-<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
-
-<dom-module id="viewer-toolbar-dropdown">
-  <template>
     <style>
       :host {
         display: inline-block;
@@ -104,6 +96,3 @@
         </div>
       </div>
     </div>
-  </template>
-  <script src="viewer-toolbar-dropdown.js"></script>
-</dom-module>
diff --git a/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown.js b/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown.js
index 191f4cfc..ceb2774 100644
--- a/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown.js
+++ b/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown.js
@@ -2,7 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-(function() {
+import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.m.js';
+import 'chrome://resources/cr_elements/icons.m.js';
+import 'chrome://resources/cr_elements/shared_vars_css.m.js';
+
+import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
 /**
  * Size of additional padding in the inner scrollable section of the dropdown.
  */
@@ -17,6 +22,8 @@
 Polymer({
   is: 'viewer-toolbar-dropdown',
 
+  _template: html`{__html_template__}`,
+
   properties: {
     /** Icon to display when the dropdown is closed. */
     closedIcon: String,
@@ -182,9 +189,7 @@
     }
 
     this.$.dropdown.animate(
-        {
-          opacity: [0, 1],
-        },
+        [{opacity: 0}, {opacity: 1}],
         {
           duration: 150,
           easing: 'cubic-bezier(0, 0, 0.2, 1)',
@@ -216,4 +221,3 @@
         });
   }
 });
-})();
diff --git a/chrome/browser/resources/pdf/elements/viewer-zoom-button.html b/chrome/browser/resources/pdf/elements/viewer-zoom-button.html
index 000d098..e3831e3 100644
--- a/chrome/browser/resources/pdf/elements/viewer-zoom-button.html
+++ b/chrome/browser/resources/pdf/elements/viewer-zoom-button.html
@@ -1,10 +1,3 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/shadow.html">
-
-<dom-module id="viewer-zoom-button">
-  <template>
     <style>
       :host {
         --translate-x-distance: 132px;
@@ -80,6 +73,3 @@
           aria-label$="[[visibleTooltip_]]" title="[[visibleTooltip_]]">
       </cr-icon-button>
     </div>
-  </template>
-  <script src="viewer-zoom-button.js"></script>
-</dom-module>
diff --git a/chrome/browser/resources/pdf/elements/viewer-zoom-button.js b/chrome/browser/resources/pdf/elements/viewer-zoom-button.js
index 2359900..30a832b 100644
--- a/chrome/browser/resources/pdf/elements/viewer-zoom-button.js
+++ b/chrome/browser/resources/pdf/elements/viewer-zoom-button.js
@@ -2,9 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'chrome://resources/cr_elements/shared_vars_css.m.js';
+import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.m.js';
+import 'chrome://resources/polymer/v3_0/paper-styles/shadow.js';
+
+import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
 Polymer({
   is: 'viewer-zoom-button',
 
+  _template: html`{__html_template__}`,
+
   properties: {
     /** Index of the icon currently being displayed. */
     activeIndex: {
diff --git a/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar.html b/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar.html
index 0807c5dfe..c1457e4 100644
--- a/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar.html
+++ b/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar.html
@@ -1,11 +1,3 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-<link rel="import" href="chrome://resources/html/util.html">
-<link rel="import" href="chrome://resources/cr_elements/icons.html">
-<link rel="import" href="icons.html">
-<link rel="import" href="viewer-zoom-button.html">
-
-<dom-module id="viewer-zoom-toolbar">
-  <template>
     <style>
       :host {
         --button-position-offset: 48px;
@@ -68,6 +60,3 @@
           keyboard-navigation-active="[[keyboardNavigationActive_]]"
           on-fabclick="zoomOut" delay="0"></viewer-zoom-button>
     </div>
-  </template>
-  <script src="viewer-zoom-toolbar.js"></script>
-</dom-module>
diff --git a/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar.js b/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar.js
index fe28ef8..b03e4aa9 100644
--- a/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar.js
+++ b/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar.js
@@ -2,15 +2,21 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'chrome://resources/cr_elements/icons.m.js';
+import './icons.js';
+import './viewer-zoom-button.js';
+
+import {assert} from 'chrome://resources/js/assert.m.js';
+import {isRTL} from 'chrome://resources/js/util.m.js';
+import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
 /**
  * @typedef {{
  *   fittingType: !FittingType,
  *   userInitiated: boolean,
  * }}
  */
-let FitToChangedEvent;
-
-(function() {
+export let FitToChangedEvent;
 
 const FIT_TO_PAGE_BUTTON_STATE = 0;
 const FIT_TO_WIDTH_BUTTON_STATE = 1;
@@ -18,6 +24,8 @@
 Polymer({
   is: 'viewer-zoom-toolbar',
 
+  _template: html`{__html_template__}`,
+
   properties: {
     newPrintPreview: {
       type: Boolean,
@@ -195,4 +203,3 @@
     }
   },
 });
-})();
diff --git a/chrome/browser/resources/pdf/index.html b/chrome/browser/resources/pdf/index.html
index 00d9643..a32f4d4 100644
--- a/chrome/browser/resources/pdf/index.html
+++ b/chrome/browser/resources/pdf/index.html
@@ -2,23 +2,13 @@
 <html>
 <head>
   <meta charset="utf-8">
-  <script src="chrome://resources/polymer/v1_0/html-imports/html-imports.min.js">
-  </script>
-  <link rel="import" href="elements/viewer-error-screen.html">
-  <link rel="import" href="elements/viewer-page-indicator.html">
-  <link rel="import" href="elements/viewer-page-selector.html">
-  <link rel="import" href="elements/viewer-password-screen.html">
-  <link rel="import" href="elements/viewer-pdf-toolbar.html">
-  <link rel="import" href="elements/viewer-zoom-toolbar.html">
-  <link rel="import" href="elements/shared-vars.html">
-  <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
-  <link rel="import" href="chrome://resources/html/cr/event_target.html">
-  <link rel="import" href="chrome://resources/html/event_tracker.html">
-
-<if expr="chromeos">
-  <link rel="import" href="elements/viewer-ink-host.html">
-  <link rel="import" href="elements/viewer-form-warning.html">
-</if>
+  <script src="chrome://resources/js/assert.js"></script>
+  <script src="chrome://resources/js/promise_resolver.js"></script>
+  <script src="chrome://resources/js/load_time_data.js"></script>
+  <script src="chrome://resources/js/util.js"></script>
+  <script src="chrome://resources/js/cr.js"></script>
+  <script src="chrome://resources/js/cr/event_target.js"></script>
+  <script src="chrome://resources/js/event_tracker.js"></script>
 
   <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
   <link rel="stylesheet" href="index.css">
@@ -52,13 +42,8 @@
 <script src="zoom_manager.js"></script>
 <script src="gesture_detector.js"></script>
 <script src="pdf_scripting_api.js"></script>
-<script src="chrome://resources/js/assert.js"></script>
-<script src="chrome://resources/js/load_time_data.js"></script>
-<script src="chrome://resources/js/util.js"></script>
-<script src="chrome://resources/js/promise_resolver.js"></script>
 <script src="browser_api.js"></script>
 <script src="metrics.js"></script>
 <script src="controller.js"></script>
-<script src="pdf_viewer.js"></script>
-<script src="main.js"></script>
+<script type="module" src="main.js"></script>
 </html>
diff --git a/chrome/browser/resources/pdf/main.js b/chrome/browser/resources/pdf/main.js
index 9d3f533..cf8f569 100644
--- a/chrome/browser/resources/pdf/main.js
+++ b/chrome/browser/resources/pdf/main.js
@@ -2,7 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-'use strict';
+import './elements/viewer-error-screen.js';
+import './elements/viewer-page-indicator.js';
+import './elements/viewer-password-screen.js';
+import './elements/viewer-pdf-toolbar.js';
+import './elements/viewer-zoom-toolbar.js';
+import './elements/shared-vars.js';
+// <if expr="chromeos">
+import './elements/viewer-ink-host.js';
+import './elements/viewer-form-warning.js';
+// </if>
+import {PDFViewer} from './pdf_viewer.js';
 
 /**
  * Global PDFViewer object, accessible for testing.
@@ -12,7 +22,6 @@
 window.viewer = null;
 
 
-(function() {
 /**
  * Stores any pending messages received which should be passed to the
  * PDFViewer when it is created.
@@ -73,17 +82,14 @@
   // Set up an event listener to catch scripting messages which are sent prior
   // to the PDFViewer being created.
   window.addEventListener('message', handleScriptingMessage, false);
-  HTMLImports.whenReady(() => {
-    let chain = createBrowserApi();
+  let chain = createBrowserApi();
 
-    // Content settings may not be present in test environments.
-    if (chrome.contentSettings) {
-      chain = chain.then(configureJavaScriptContentSetting);
-    }
+  // Content settings may not be present in test environments.
+  if (chrome.contentSettings) {
+    chain = chain.then(configureJavaScriptContentSetting);
+  }
 
-    chain = chain.then(initViewer);
-  });
+  chain = chain.then(initViewer);
 }
 
 main();
-})();
diff --git a/chrome/browser/resources/pdf/pdf_viewer.js b/chrome/browser/resources/pdf/pdf_viewer.js
index d85d80a2..180ab1c 100644
--- a/chrome/browser/resources/pdf/pdf_viewer.js
+++ b/chrome/browser/resources/pdf/pdf_viewer.js
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-'use strict';
+import {Bookmark} from './elements/viewer-bookmark.js';
+import {FitToChangedEvent} from './elements/viewer-zoom-toolbar.js';
 
 /**
  * @typedef {{
@@ -82,7 +83,7 @@
  * @param {string} url The URL to get the filename from.
  * @return {string} The filename component.
  */
-function getFilenameFromURL(url) {
+export function getFilenameFromURL(url) {
   // Ignore the query and fragment.
   const mainUrl = url.split(/#|\?/)[0];
   const components = mainUrl.split(/\/|\\/);
@@ -103,7 +104,7 @@
  * @param {Element} activeElement The currently selected DOM node.
  * @return {boolean} True if keydown events should be ignored.
  */
-function shouldIgnoreKeyEvents(activeElement) {
+export function shouldIgnoreKeyEvents(activeElement) {
   while (activeElement.shadowRoot != null &&
          activeElement.shadowRoot.activeElement != null) {
     activeElement = activeElement.shadowRoot.activeElement;
@@ -119,7 +120,7 @@
  * Creates a new PDFViewer. There should only be one of these objects per
  * document.
  */
-class PDFViewer {
+export class PDFViewer {
   /**
    * @param {!BrowserApi} browserApi An object providing an API to the browser.
    */
@@ -1165,7 +1166,12 @@
       } else {
         targetOrigin = this.originalUrl_;
       }
-      this.parentWindow_.postMessage(message, targetOrigin);
+      try {
+        this.parentWindow_.postMessage(message, targetOrigin);
+      } catch (ok) {
+        // TODO(crbug.com/1004425): targetOrigin probably was rejected, such as
+        // a "data:" URL. This shouldn't cause this method to throw, though.
+      }
     }
   }
 
@@ -1491,6 +1497,10 @@
   }
 }
 
+// Export PDFViewer on |window| such that scripts injected from
+// pdf_extension_test.cc can access it.
+window.PDFViewer = PDFViewer;
+
 /**
  * The height of the toolbar along the top of the page. The document will be
  * shifted down by this much in the viewport.
diff --git a/chrome/browser/resources/settings/BUILD.gn b/chrome/browser/resources/settings/BUILD.gn
index beba501..81388d1c 100644
--- a/chrome/browser/resources/settings/BUILD.gn
+++ b/chrome/browser/resources/settings/BUILD.gn
@@ -28,6 +28,7 @@
       "crisper.js",
       "lazy_load.crisper.js",
     ]
+    excludes = [ "chrome://resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom.html" ]
 
     deps = [
       ":unpak",
diff --git a/chrome/browser/resources/settings/chromeos/BUILD.gn b/chrome/browser/resources/settings/chromeos/BUILD.gn
index 37366df..9bd2231c 100644
--- a/chrome/browser/resources/settings/chromeos/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/BUILD.gn
@@ -29,11 +29,14 @@
       "lazy_load.crisper.js",
     ]
     excludes = [
+      "chrome://resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom.html",
       "chrome://os-settings/app-management/app_management.mojom-lite.js",
       "chrome://os-settings/app-management/bitmap.mojom-lite.js",
       "chrome://os-settings/app-management/image_info.mojom-lite.js",
       "chrome://os-settings/app-management/image.mojom-lite.js",
       "chrome://os-settings/app-management/types.mojom-lite.js",
+      "chrome://resources/mojo/mojo/public/mojom/base/big_buffer.mojom.html",
+      "chrome://resources/mojo/mojo/public/mojom/base/time.mojom.html",
     ]
 
     deps = [
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_item.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_item.html
index d015876..bf18116 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_item.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_item.html
@@ -34,8 +34,8 @@
       }
     </style>
     <img id="app-icon" src="[[iconUrlFromId_(app)]]"
-        alt="[[app.title]] app icon.">
-    <div id="app-title">[[app.title]]</div>
+        alt="[[app.title]] app icon." aria-hidden="true">
+    <div id="app-title" aria-hidden="true">[[app.title]]</div>
     <slot name="right-content"></slot>
   </template>
  <script src="app_item.js"></script>
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/arc_permission_view.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/arc_permission_view.html
index 04047695..8546e004 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/arc_permission_view.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/arc_permission_view.html
@@ -64,7 +64,7 @@
       <div id="more-settings"
         class="permission-card-row separated-row header-text clickable"
         on-click="onClickNativeSettingsButton_">
-        <div id="label" class="header-text">
+        <div id="label" class="header-text" aria-hidden="true">
           $i18n{appManagementMoreSettingsLabel}
         </div>
         <div class="permission-row-controls">
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/chrome_app_permission_view.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/chrome_app_permission_view.html
index 0ee04ab0..649c708 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/chrome_app_permission_view.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/chrome_app_permission_view.html
@@ -71,7 +71,7 @@
           class="permission-card-row separated-row header-text clickable"
           on-click="onClickExtensionsSettingsButton_"
           hidden$="[[app_.hideMoreSettings]]">
-          <div id="label" class="header-text">
+          <div id="label" class="header-text" aria-hidden="true">
             $i18n{appManagementMoreSettingsLabel}
           </div>
           <div class="permission-row-controls">
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/main_view.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/main_view.html
index 9d4780e3..e4a11e1 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/main_view.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/main_view.html
@@ -25,6 +25,7 @@
           <cr-icon-button slot="right-content"
               class="subpage-arrow app-management-item-arrow"
               aria-label$="[[app.title]]"
+              role="link"
               actionable>
           </cr-icon-button>
         </app-management-app-item>
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pwa_permission_view.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pwa_permission_view.html
index 1ab2cf52..8496f503 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pwa_permission_view.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pwa_permission_view.html
@@ -49,7 +49,7 @@
       <div id="more-settings"
         class="permission-card-row separated-row header-text clickable"
         on-click="onClickSiteSettingsButton_">
-        <div id="label" class="header-text">
+        <div id="label" class="header-text" aria-hidden="true">
           $i18n{appManagementMoreSettingsLabel}
         </div>
         <div class="permission-row-controls">
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/toggle_row.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/toggle_row.html
index 503a0dd..1b8fb7a8 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/toggle_row.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/toggle_row.html
@@ -26,7 +26,7 @@
       }
     </style>
 
-    <div id="left-content" class="horizontal-align">
+    <div id="left-content" class="horizontal-align" aria-hidden="true">
       <template is="dom-if" if="[[icon]]">
         <iron-icon id="icon" icon="[[icon]]"></iron-icon>
       </template>
diff --git a/chrome/browser/resources/settings/settings_page/settings_subpage.html b/chrome/browser/resources/settings/settings_page/settings_subpage.html
index 0b0c6b1..40355a6 100644
--- a/chrome/browser/resources/settings/settings_page/settings_subpage.html
+++ b/chrome/browser/resources/settings/settings_page/settings_subpage.html
@@ -75,7 +75,7 @@
       <cr-icon-button class="icon-arrow-back" id="closeButton"
           on-click="onTapBack_" aria-label="$i18n{back}"></cr-icon-button>
       <template is="dom-if" if="[[titleIcon]]">
-        <img id="title-icon" src="[[titleIcon]]">
+        <img id="title-icon" src="[[titleIcon]]" aria-hidden="true">
       </template>
       <h1 class="cr-title-text">[[pageTitle]]</h1>
       <slot name="subpage-title-extra"></slot>
diff --git a/chrome/browser/ui/android/autofill/card_expiration_date_fix_flow_view_android.cc b/chrome/browser/ui/android/autofill/card_expiration_date_fix_flow_view_android.cc
index 9059e65..3d1165e 100644
--- a/chrome/browser/ui/android/autofill/card_expiration_date_fix_flow_view_android.cc
+++ b/chrome/browser/ui/android/autofill/card_expiration_date_fix_flow_view_android.cc
@@ -9,7 +9,8 @@
 
 #include "chrome/android/chrome_jni_headers/AutofillExpirationDateFixFlowBridge_jni.h"
 #include "chrome/browser/android/resource_mapper.h"
-#include "components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_view_delegate_mobile.h"
+#include "components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_controller.h"
+#include "components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_view.h"
 #include "content/public/browser/web_contents.h"
 #include "ui/android/view_android.h"
 #include "ui/android/window_android.h"
@@ -20,51 +21,56 @@
 namespace autofill {
 
 CardExpirationDateFixFlowViewAndroid::CardExpirationDateFixFlowViewAndroid(
-    std::unique_ptr<CardExpirationDateFixFlowViewDelegateMobile> delegate,
+    CardExpirationDateFixFlowController* controller,
     content::WebContents* web_contents)
-    : delegate_(std::move(delegate)), web_contents_(web_contents) {}
-
-CardExpirationDateFixFlowViewAndroid::~CardExpirationDateFixFlowViewAndroid() {
-  JNIEnv* env = base::android::AttachCurrentThread();
-  Java_AutofillExpirationDateFixFlowBridge_dismiss(env, java_object_);
-}
-
-void CardExpirationDateFixFlowViewAndroid::Show() {
-  JNIEnv* env = base::android::AttachCurrentThread();
-  ui::ViewAndroid* view_android = web_contents_->GetNativeView();
-
-  ScopedJavaLocalRef<jstring> dialog_title =
-      base::android::ConvertUTF16ToJavaString(env, delegate_->GetTitleText());
-
-  ScopedJavaLocalRef<jstring> confirm = base::android::ConvertUTF16ToJavaString(
-      env, delegate_->GetSaveButtonLabel());
-
-  ScopedJavaLocalRef<jstring> card_label =
-      base::android::ConvertUTF16ToJavaString(env, delegate_->card_label());
-
-  java_object_.Reset(Java_AutofillExpirationDateFixFlowBridge_create(
-      env, reinterpret_cast<intptr_t>(this), dialog_title, confirm,
-      ResourceMapper::MapFromChromiumId(delegate_->GetIconId()), card_label));
-
-  Java_AutofillExpirationDateFixFlowBridge_show(
-      env, java_object_, view_android->GetWindowAndroid()->GetJavaObject());
-  delegate_->Shown();
-}
+    : controller_(controller), web_contents_(web_contents) {}
 
 void CardExpirationDateFixFlowViewAndroid::OnUserAccept(
     JNIEnv* env,
     const JavaParamRef<jobject>& obj,
     const JavaParamRef<jstring>& month,
     const JavaParamRef<jstring>& year) {
-  delegate_->Accept(base::android::ConvertJavaStringToUTF16(env, month),
-                    base::android::ConvertJavaStringToUTF16(env, year));
+  controller_->OnAccepted(base::android::ConvertJavaStringToUTF16(env, month),
+                          base::android::ConvertJavaStringToUTF16(env, year));
 }
 
 void CardExpirationDateFixFlowViewAndroid::PromptDismissed(
     JNIEnv* env,
     const JavaParamRef<jobject>& obj) {
-  delegate_->Dismissed();
+  controller_->OnDismissed();
   delete this;
 }
 
+void CardExpirationDateFixFlowViewAndroid::Show() {
+  JNIEnv* env = base::android::AttachCurrentThread();
+
+  ScopedJavaLocalRef<jstring> dialog_title =
+      base::android::ConvertUTF16ToJavaString(env, controller_->GetTitleText());
+
+  ScopedJavaLocalRef<jstring> confirm = base::android::ConvertUTF16ToJavaString(
+      env, controller_->GetSaveButtonLabel());
+
+  ScopedJavaLocalRef<jstring> card_label =
+      base::android::ConvertUTF16ToJavaString(env, controller_->GetCardLabel());
+
+  java_object_.Reset(Java_AutofillExpirationDateFixFlowBridge_create(
+      env, reinterpret_cast<intptr_t>(this), dialog_title, confirm,
+      ResourceMapper::MapFromChromiumId(controller_->GetIconId()), card_label));
+
+  Java_AutofillExpirationDateFixFlowBridge_show(
+      env, java_object_,
+      web_contents_->GetTopLevelNativeWindow()->GetJavaObject());
+}
+
+void CardExpirationDateFixFlowViewAndroid::ControllerGone() {
+  controller_ = nullptr;
+  JNIEnv* env = base::android::AttachCurrentThread();
+  Java_AutofillExpirationDateFixFlowBridge_dismiss(env, java_object_);
+}
+
+CardExpirationDateFixFlowViewAndroid::~CardExpirationDateFixFlowViewAndroid() {
+  if (controller_)
+    controller_->OnDialogClosed();
+}
+
 }  // namespace autofill
diff --git a/chrome/browser/ui/android/autofill/card_expiration_date_fix_flow_view_android.h b/chrome/browser/ui/android/autofill/card_expiration_date_fix_flow_view_android.h
index 9f8a2ae..06f54a29 100644
--- a/chrome/browser/ui/android/autofill/card_expiration_date_fix_flow_view_android.h
+++ b/chrome/browser/ui/android/autofill/card_expiration_date_fix_flow_view_android.h
@@ -12,6 +12,7 @@
 #include "base/android/scoped_java_ref.h"
 #include "base/macros.h"
 #include "base/strings/string16.h"
+#include "components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_view.h"
 
 namespace content {
 class WebContents;
@@ -19,16 +20,15 @@
 
 namespace autofill {
 
-class CardExpirationDateFixFlowViewDelegateMobile;
+class CardExpirationDateFixFlowController;
 
-class CardExpirationDateFixFlowViewAndroid {
+class CardExpirationDateFixFlowViewAndroid
+    : public CardExpirationDateFixFlowView {
  public:
   CardExpirationDateFixFlowViewAndroid(
-      std::unique_ptr<CardExpirationDateFixFlowViewDelegateMobile> delegate,
+      CardExpirationDateFixFlowController* controller,
       content::WebContents* web_contents);
 
-  ~CardExpirationDateFixFlowViewAndroid();
-
   void OnUserAccept(JNIEnv* env,
                     const base::android::JavaParamRef<jobject>& obj,
                     const base::android::JavaParamRef<jstring>& month,
@@ -36,13 +36,17 @@
   void PromptDismissed(JNIEnv* env,
                        const base::android::JavaParamRef<jobject>& obj);
 
-  void Show();
+  // CardExpirationDateFixFlowView implementation.
+  void Show() override;
+  void ControllerGone() override;
 
  private:
+  ~CardExpirationDateFixFlowViewAndroid() override;
+
   // The corresponding java object.
   base::android::ScopedJavaGlobalRef<jobject> java_object_;
 
-  std::unique_ptr<CardExpirationDateFixFlowViewDelegateMobile> delegate_;
+  CardExpirationDateFixFlowController* controller_;
 
   content::WebContents* web_contents_;
 
diff --git a/chrome/browser/ui/app_list/search/cros_action_history/cros_action_recorder.cc b/chrome/browser/ui/app_list/search/cros_action_history/cros_action_recorder.cc
index 4bc9d5c..a624a567 100644
--- a/chrome/browser/ui/app_list/search/cros_action_history/cros_action_recorder.cc
+++ b/chrome/browser/ui/app_list/search/cros_action_history/cros_action_recorder.cc
@@ -4,11 +4,12 @@
 
 #include "chrome/browser/ui/app_list/search/cros_action_history/cros_action_recorder.h"
 
-#include "ash/public/cpp/app_list/app_list_features.h"
+#include "ash/public/cpp/app_list/app_list_switches.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/files/important_file_writer.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/metrics/metrics_hashes.h"
 #include "base/no_destructor.h"
 #include "base/strings/string_number_conversions.h"
@@ -34,16 +35,41 @@
   kLogWithoutHash = 2,
 };
 
+// Represents the events of the CrOSActionRecorder.
+// This enum is used for a histogram and should not be renumbered and the old
+// values should not be reused.
+enum CrOSActionRecorderEvent {
+  kDisabled = 0,
+  kRecordAction = 1,
+  kFlushToDisk = 2,
+  kReadFromFileFail = 3,
+  kParseFromStringFail = 4,
+  kCreateDirectoryFail = 5,
+  kWriteFileAtomicallyFail = 6,
+  kMaxValue = kWriteFileAtomicallyFail,
+};
+
+// Records CrOSActionRecorder event.
+void RecordCrOSActionEvent(const CrOSActionRecorderEvent val) {
+  UMA_HISTOGRAM_ENUMERATION("Cros.CrOSActionRecorderEvent", val,
+                            CrOSActionRecorderEvent::kMaxValue);
+}
+
+// Append the |actions| to the |action_filepath|.
 void SaveToDiskOnWorkerThread(const CrOSActionHistoryProto actions,
                               const base::FilePath action_filepath) {
   // Loads proto string from local disk.
   std::string proto_str;
-  if (!base::ReadFileToString(action_filepath, &proto_str))
+  if (!base::ReadFileToString(action_filepath, &proto_str)) {
     proto_str.clear();
+    RecordCrOSActionEvent(CrOSActionRecorderEvent::kReadFromFileFail);
+  }
 
   CrOSActionHistoryProto actions_to_write;
-  if (!actions_to_write.ParseFromString(proto_str))
+  if (!actions_to_write.ParseFromString(proto_str)) {
     actions_to_write.Clear();
+    RecordCrOSActionEvent(CrOSActionRecorderEvent::kParseFromStringFail);
+  }
 
   if (actions_to_write.actions_size() > kActionLimitPerFile)
     return;
@@ -54,11 +80,18 @@
   // Create directory if it's not there yet.
   const bool create_directory_sucess =
       base::CreateDirectory(action_filepath.DirName());
-  DCHECK(create_directory_sucess)
-      << "Error create directory for " << action_filepath;
+  if (!create_directory_sucess) {
+    RecordCrOSActionEvent(CrOSActionRecorderEvent::kCreateDirectoryFail);
+    DCHECK(create_directory_sucess)
+        << "Error create directory for " << action_filepath;
+  }
+
   const bool write_success = base::ImportantFileWriter::WriteFileAtomically(
       action_filepath, proto_str_to_write, "CrOSActionHistory");
-  DCHECK(write_success) << "Error writing action_file " << action_filepath;
+  if (!write_success) {
+    RecordCrOSActionEvent(CrOSActionRecorderEvent::kWriteFileAtomicallyFail);
+    DCHECK(write_success) << "Error writing action_file " << action_filepath;
+  }
 }
 
 }  // namespace
@@ -101,6 +134,7 @@
   if (!should_log_)
     return;
 
+  RecordCrOSActionEvent(CrOSActionRecorderEvent::kRecordAction);
   CrOSActionProto& cros_action_proto = *actions_.add_actions();
 
   // Record action.
@@ -127,8 +161,9 @@
   const base::Time now = base::Time::Now();
   if (now - last_save_timestamp_ >= kSaveInternal ||
       actions_.actions_size() > kActionLimitInMemory) {
-    last_save_timestamp_ = now;
+    RecordCrOSActionEvent(CrOSActionRecorderEvent::kFlushToDisk);
 
+    last_save_timestamp_ = now;
     // Writes the predictor proto to disk asynchronously.
     const std::string day = base::NumberToString(
         static_cast<int>(now.ToDoubleT() / kSecondsPerDay));
@@ -143,16 +178,20 @@
 }
 
 void CrOSActionRecorder::SetCrOSActionRecorderType() {
-  if (!app_list_features::IsCrOSActionRecorderEnabled())
-    return;
-  const CrOSActionRecorderType type = static_cast<CrOSActionRecorderType>(
-      app_list_features::GetCrOSActionRecorderType());
-  if (type == CrOSActionRecorderType::kLogWithHash) {
-    should_log_ = true;
-    should_hash_ = true;
-  } else if (type == CrOSActionRecorderType::kLogWithoutHash) {
-    should_log_ = true;
-    should_hash_ = false;
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+
+  if (command_line->HasSwitch(ash::switches::kEnableCrOSActionRecorder)) {
+    std::string cros_action_flag = command_line->GetSwitchValueASCII(
+        ash::switches::kEnableCrOSActionRecorder);
+
+    if (cros_action_flag == ash::switches::kCrOSActionRecorderWithHash) {
+      should_log_ = true;
+      should_hash_ = true;
+    } else if (cros_action_flag ==
+               ash::switches::kCrOSActionRecorderWithoutHash) {
+      should_log_ = true;
+      should_hash_ = false;
+    }
   }
 }
 
diff --git a/chrome/browser/ui/app_list/search/cros_action_history/cros_action_recorder_unittest.cc b/chrome/browser/ui/app_list/search/cros_action_history/cros_action_recorder_unittest.cc
index c61411e..3a9f1cf3 100644
--- a/chrome/browser/ui/app_list/search/cros_action_history/cros_action_recorder_unittest.cc
+++ b/chrome/browser/ui/app_list/search/cros_action_history/cros_action_recorder_unittest.cc
@@ -3,12 +3,12 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/ui/app_list/search/cros_action_history/cros_action_recorder.h"
-#include "ash/public/cpp/app_list/app_list_features.h"
+#include "ash/public/cpp/app_list/app_list_switches.h"
+#include "base/command_line.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/metrics/metrics_hashes.h"
 #include "base/strings/string_number_conversions.h"
-#include "base/test/scoped_feature_list.h"
 #include "base/test/scoped_mock_clock_override.h"
 #include "base/test/task_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -52,17 +52,17 @@
   }
 
   void SetLogWithHash() {
-    scoped_feature_list_.InitAndEnableFeatureWithParameters(
-        app_list_features::kEnableCrOSActionRecorder,
-        {{"CrOSActionRecorderType", "1"}});
+    base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+        ash::switches::kEnableCrOSActionRecorder,
+        ash::switches::kCrOSActionRecorderWithHash);
 
     CrOSActionRecorder::GetCrosActionRecorder()->SetCrOSActionRecorderType();
   }
 
   void SetLogWithoutHash() {
-    scoped_feature_list_.InitAndEnableFeatureWithParameters(
-        app_list_features::kEnableCrOSActionRecorder,
-        {{"CrOSActionRecorderType", "2"}});
+    base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+        ash::switches::kEnableCrOSActionRecorder,
+        ash::switches::kCrOSActionRecorderWithoutHash);
 
     CrOSActionRecorder::GetCrosActionRecorder()->SetCrOSActionRecorderType();
   }
@@ -103,7 +103,6 @@
   base::FilePath profile_path_;
   int64_t save_internal_secs_ = 0;
   base::ScopedMockClockOverride time_;
-  base::test::ScopedFeatureList scoped_feature_list_;
   base::test::TaskEnvironment task_environment_{
       base::test::TaskEnvironment::MainThreadType::DEFAULT,
       base::test::TaskEnvironment::ThreadPoolExecutionMode::QUEUED};
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.cc b/chrome/browser/ui/autofill/chrome_autofill_client.cc
index 7e64d8b..b9e52141 100644
--- a/chrome/browser/ui/autofill/chrome_autofill_client.cc
+++ b/chrome/browser/ui/autofill/chrome_autofill_client.cc
@@ -76,7 +76,7 @@
 #include "components/autofill/core/browser/payments/autofill_credit_card_filling_infobar_delegate_mobile.h"
 #include "components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.h"
 #include "components/autofill/core/browser/payments/autofill_save_card_infobar_mobile.h"
-#include "components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_view_delegate_mobile.h"
+#include "components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_view.h"
 #include "components/autofill/core/browser/ui/payments/card_name_fix_flow_view.h"
 #include "components/infobars/core/infobar.h"
 #include "ui/android/window_android.h"
@@ -346,20 +346,13 @@
     const CreditCard& card,
     base::OnceCallback<void(const base::string16&, const base::string16&)>
         callback) {
-  std::unique_ptr<CardExpirationDateFixFlowViewDelegateMobile>
-      card_expiration_date_fix_flow_view_delegate_mobile =
-          std::make_unique<CardExpirationDateFixFlowViewDelegateMobile>(
-              card,
-              /*upload_save_card_callback=*/std::move(callback));
-
-  // Destruction is handled by the fix flow dialog by explicitly calling delete
-  // when the prompt is dismissed.
   CardExpirationDateFixFlowViewAndroid*
       card_expiration_date_fix_flow_view_android =
           new CardExpirationDateFixFlowViewAndroid(
-              std::move(card_expiration_date_fix_flow_view_delegate_mobile),
-              web_contents());
-  card_expiration_date_fix_flow_view_android->Show();
+              &card_expiration_date_fix_flow_controller_, web_contents());
+  card_expiration_date_fix_flow_controller_.Show(
+      card_expiration_date_fix_flow_view_android, card,
+      /*upload_save_card_callback=*/std::move(callback));
 }
 #endif
 
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.h b/chrome/browser/ui/autofill/chrome_autofill_client.h
index 6bc4052b..a5dc10c 100644
--- a/chrome/browser/ui/autofill/chrome_autofill_client.h
+++ b/chrome/browser/ui/autofill/chrome_autofill_client.h
@@ -24,6 +24,7 @@
 #include "content/public/browser/web_contents_user_data.h"
 
 #if defined(OS_ANDROID)
+#include "components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_controller_impl.h"
 #include "components/autofill/core/browser/ui/payments/card_name_fix_flow_controller_impl.h"
 #else  // !OS_ANDROID
 #include "chrome/browser/ui/autofill/payments/manage_migration_ui_controller.h"
@@ -31,10 +32,6 @@
 #include "components/zoom/zoom_observer.h"
 #endif
 
-#if defined(OS_ANDROID)
-#include "components/autofill/core/browser/ui/payments/card_name_fix_flow_controller_impl.h"
-#endif  // defined(OS_ANDROID)
-
 namespace content {
 class WebContents;
 }
@@ -173,6 +170,8 @@
   CardUnmaskPromptControllerImpl unmask_controller_;
   std::unique_ptr<LogManager> log_manager_;
 #if defined(OS_ANDROID)
+  CardExpirationDateFixFlowControllerImpl
+      card_expiration_date_fix_flow_controller_;
   CardNameFixFlowControllerImpl card_name_fix_flow_controller_;
 #endif
 
diff --git a/chrome/browser/ui/libgtkui/select_file_dialog_impl_gtk.cc b/chrome/browser/ui/libgtkui/select_file_dialog_impl_gtk.cc
index 4da1b5f..5be7e3e0 100644
--- a/chrome/browser/ui/libgtkui/select_file_dialog_impl_gtk.cc
+++ b/chrome/browser/ui/libgtkui/select_file_dialog_impl_gtk.cc
@@ -200,7 +200,7 @@
       host->ReleaseCapture();
       std::unique_ptr<base::OnceClosure> callback =
           std::make_unique<base::OnceClosure>(
-              views::DesktopWindowTreeHostX11::GetHostForXID(
+              views::DesktopWindowTreeHostLinux::GetHostForWidget(
                   host->GetAcceleratedWidget())
                   ->DisableEventListening());
       // OnFilePickerDestroy() is called when |dialog| destroyed, which allows
diff --git a/chrome/browser/ui/login/login_handler_unittest.cc b/chrome/browser/ui/login/login_handler_unittest.cc
index fb2fa41..562dbbb 100644
--- a/chrome/browser/ui/login/login_handler_unittest.cc
+++ b/chrome/browser/ui/login/login_handler_unittest.cc
@@ -8,6 +8,9 @@
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "chrome/browser/ui/login/login_handler.h"
+#include "chrome/browser/ui/login/login_tab_helper.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "content/public/test/mock_navigation_handle.h"
 #include "net/base/auth.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
@@ -111,6 +114,15 @@
   return str;
 }
 
+class LoginHandlerWithWebContentsTest : public ChromeRenderViewHostTestHarness {
+ public:
+  LoginHandlerWithWebContentsTest() {}
+  ~LoginHandlerWithWebContentsTest() override {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(LoginHandlerWithWebContentsTest);
+};
+
 }  // namespace
 
 TEST(LoginHandlerTest, DialogStringsAndRealm) {
@@ -145,3 +157,15 @@
                  LoginHandler::GetSignonRealm(request_url, auth_info).c_str());
   }
 }
+
+// Tests that LoginTabHelper does not crash if
+// WillProcessMainFrameUnauthorizedResponse() is called when there is no pending
+// entry. Regression test for https://crbug.com/1015787.
+TEST_F(LoginHandlerWithWebContentsTest, NoPendingEntryDoesNotCrash) {
+  LoginTabHelper::CreateForWebContents(web_contents());
+  LoginTabHelper* helper = LoginTabHelper::FromWebContents(web_contents());
+  content::MockNavigationHandle handle;
+  content::NavigationThrottle::ThrottleCheckResult result =
+      helper->WillProcessMainFrameUnauthorizedResponse(&handle);
+  EXPECT_EQ(content::NavigationThrottle::CANCEL, result.action());
+}
diff --git a/chrome/browser/ui/login/login_tab_helper.cc b/chrome/browser/ui/login/login_tab_helper.cc
index 2f78653..f8c4ac46 100644
--- a/chrome/browser/ui/login/login_tab_helper.cc
+++ b/chrome/browser/ui/login/login_tab_helper.cc
@@ -167,12 +167,9 @@
   // commits. Comparing against GetVisibleEntry() would also work, but it's less
   // specific and not guaranteed to exist in all cases (e.g., in the case of
   // navigating a window just opened via window.open()).
-  //
-  // TODO(https://crbug.com/1006955): if this line is crashing, the assumption
-  // that GetPendingEntry() must be non-null is incorrect, in which case a null
-  // check should be added here.
-  if (web_contents()->GetController().GetPendingEntry()->GetUniqueID() ==
-      navigation_entry_id_with_cancelled_prompt_) {
+  if (web_contents()->GetController().GetPendingEntry() &&
+      web_contents()->GetController().GetPendingEntry()->GetUniqueID() ==
+          navigation_entry_id_with_cancelled_prompt_) {
     // Note the navigation handle ID so that when this refresh navigation
     // finishes, DidFinishNavigation declines to show another login prompt. We
     // need the navigation handle ID (rather than the navigation entry ID) here
diff --git a/chrome/browser/ui/tab_helpers.cc b/chrome/browser/ui/tab_helpers.cc
index 12af7290..7bad4c3f 100644
--- a/chrome/browser/ui/tab_helpers.cc
+++ b/chrome/browser/ui/tab_helpers.cc
@@ -34,6 +34,7 @@
 #include "chrome/browser/metrics/oom/out_of_memory_reporter.h"
 #include "chrome/browser/metrics/renderer_uptime_web_contents_observer.h"
 #include "chrome/browser/native_file_system/native_file_system_permission_request_manager.h"
+#include "chrome/browser/navigation_predictor/navigation_predictor_preconnect_client.h"
 #include "chrome/browser/net/net_error_tab_helper.h"
 #include "chrome/browser/optimization_guide/optimization_guide_web_contents_observer.h"
 #include "chrome/browser/page_load_metrics/page_load_metrics_initialize.h"
@@ -232,6 +233,7 @@
   NativeFileSystemPermissionRequestManager::CreateForWebContents(web_contents);
   NavigationCorrectionTabObserver::CreateForWebContents(web_contents);
   NavigationMetricsRecorder::CreateForWebContents(web_contents);
+  NavigationPredictorPreconnectClient::CreateForWebContents(web_contents);
   OptimizationGuideWebContentsObserver::CreateForWebContents(web_contents);
   OutOfMemoryReporter::CreateForWebContents(web_contents);
   chrome::InitializePageLoadMetricsForWebContents(web_contents);
diff --git a/chrome/browser/ui/ui_features.cc b/chrome/browser/ui/ui_features.cc
index 557a04c..beaba03 100644
--- a/chrome/browser/ui/ui_features.cc
+++ b/chrome/browser/ui/ui_features.cc
@@ -29,10 +29,11 @@
 const base::Feature kExtensionsToolbarMenu{"ExtensionsToolbarMenu",
                                            base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Enables showing text next to the 3-dot menu when an update is available.
-// See https://crbug.com/1001731
-const base::Feature kUseTextForUpdateButton{"UseTextForUpdateButton",
-                                            base::FEATURE_DISABLED_BY_DEFAULT};
+// Enables tabs from different browser types (NORMAL vs APP) and different apps
+// to mix via dragging.
+// https://crbug.com/1012169
+const base::Feature kMixBrowserTypeTabs{"MixBrowserTypeTabs",
+                                        base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Enables updated tabstrip animations, required for a scrollable tabstrip.
 // https://crbug.com/958173
@@ -44,6 +45,11 @@
 const base::Feature kProfileMenuRevamp{"ProfileMenuRevamp",
                                        base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enables a more prominent active tab title in dark mode to aid with
+// accessibility.
+const base::Feature kProminentDarkModeActiveTabTitle{
+    "ProminentDarkModeActiveTabTitle", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enables tabs to scroll in the tabstrip. https://crbug.com/951078
 const base::Feature kScrollableTabStrip{"ScrollableTabStrip",
                                         base::FEATURE_DISABLED_BY_DEFAULT};
@@ -74,10 +80,10 @@
 const base::Feature kTabOutlinesInLowContrastThemes{
     "TabOutlinesInLowContrastThemes", base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Enables a more prominent active tab title in dark mode to aid with
-// accessibility.
-const base::Feature kProminentDarkModeActiveTabTitle{
-    "ProminentDarkModeActiveTabTitle", base::FEATURE_DISABLED_BY_DEFAULT};
+// Enables showing text next to the 3-dot menu when an update is available.
+// See https://crbug.com/1001731
+const base::Feature kUseTextForUpdateButton{"UseTextForUpdateButton",
+                                            base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Enables a web-based separator that's only used for performance testing. See
 // https://crbug.com/993502.
diff --git a/chrome/browser/ui/ui_features.h b/chrome/browser/ui/ui_features.h
index ee9f22a..188cf2f 100644
--- a/chrome/browser/ui/ui_features.h
+++ b/chrome/browser/ui/ui_features.h
@@ -25,12 +25,14 @@
 
 extern const base::Feature kExtensionsToolbarMenu;
 
-extern const base::Feature kUseTextForUpdateButton;
+extern const base::Feature kMixBrowserTypeTabs;
 
 extern const base::Feature kNewTabstripAnimation;
 
 extern const base::Feature kProfileMenuRevamp;
 
+extern const base::Feature kProminentDarkModeActiveTabTitle;
+
 extern const base::Feature kScrollableTabStrip;
 
 extern const base::Feature kShowSyncPausedReasonCookiesClearedOnExit;
@@ -44,7 +46,7 @@
 
 extern const base::Feature kTabOutlinesInLowContrastThemes;
 
-extern const base::Feature kProminentDarkModeActiveTabTitle;
+extern const base::Feature kUseTextForUpdateButton;
 
 extern const base::Feature kWebFooterExperiment;
 
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc
index 1335ab7..bd4764ab 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc
@@ -150,9 +150,11 @@
   if (frame_->ShouldUseTheme())
     return GetThemeProviderForProfile()->GetColor(color_id);
 
+  // Use app theme color if it is set, but not for apps with tabs.
   web_app::AppBrowserController* app_controller =
       browser_view_->browser()->app_controller();
-  if (app_controller && app_controller->GetThemeColor())
+  if (app_controller && app_controller->GetThemeColor() &&
+      !app_controller->HasTabStrip())
     return *app_controller->GetThemeColor();
 
   return ThemeProperties::GetDefaultColor(color_id,
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
index 10600aa..b85c973 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
@@ -84,9 +84,12 @@
 // Returns true if the header should be painted so that it looks the same as
 // the header used for packaged apps.
 bool UsePackagedAppHeaderStyle(const Browser* browser) {
-  // Use for non tabbed trusted source windows, e.g. Settings, as well as apps.
-  return (!browser->is_type_normal() && browser->is_trusted_source()) ||
-         browser->deprecated_is_app();
+  if (browser->is_type_normal() ||
+      (browser->is_type_popup() && !browser->is_trusted_source())) {
+    return false;
+  }
+
+  return !browser->SupportsWindowFeature(Browser::FEATURE_TABSTRIP);
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/views/frame/global_menu_bar_x11.cc b/chrome/browser/ui/views/frame/global_menu_bar_x11.cc
index 274f147c..e134f3f4 100644
--- a/chrome/browser/ui/views/frame/global_menu_bar_x11.cc
+++ b/chrome/browser/ui/views/frame/global_menu_bar_x11.cc
@@ -269,7 +269,8 @@
   ProfileManager* profile_manager = g_browser_process->profile_manager();
   DCHECK(profile_manager);
   avatar_menu_ = std::make_unique<AvatarMenu>(
-      &profile_manager->GetProfileAttributesStorage(), this, nullptr);
+      &profile_manager->GetProfileAttributesStorage(), this,
+      BrowserList::GetInstance()->GetLastActive());
   avatar_menu_->RebuildMenu();
   BrowserList::AddObserver(this);
 
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller.cc b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
index 0e64ded..217d74f 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller.cc
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
@@ -27,6 +27,7 @@
 #include "chrome/browser/ui/tabs/tab_group_id.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
+#include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/views/frame/browser_non_client_frame_view.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/tabs/tab.h"
@@ -2197,8 +2198,17 @@
   if (other_browser->profile() != browser->profile())
     return false;
 
-  // Browser type (e.g. NORMAL vs APP) must be the same.
-  return other_browser->type() == browser->type();
+  // Unless we allow Feature mix-browser-type-tabs, ensure that
+  // browser types and app names are the same.
+  if (!base::FeatureList::IsEnabled(features::kMixBrowserTypeTabs)) {
+    if (other_browser->type() != browser->type() ||
+        (browser->is_type_app() &&
+         browser->app_name() != other_browser->app_name())) {
+      return false;
+    }
+  }
+
+  return true;
 }
 
 void TabDragController::SetDeferredTargetTabstrip(
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
index 0654a84..0a97ef3 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
@@ -415,7 +415,9 @@
     : public TabDragControllerTest,
       public ::testing::WithParamInterface<const char*> {
  public:
-  DetachToBrowserTabDragControllerTest() {}
+  DetachToBrowserTabDragControllerTest() {
+    scoped_feature_list_.InitAndEnableFeature(features::kMixBrowserTypeTabs);
+  }
 
   void SetUpOnMainThread() override {
 #if defined(OS_CHROMEOS)
@@ -656,6 +658,7 @@
   // The root window for the event generator.
   aura::Window* root_ = nullptr;
 #endif
+  base::test::ScopedFeatureList scoped_feature_list_;
   base::Optional<web_app::AppId> terminal_app_id_;
 
   DISALLOW_COPY_AND_ASSIGN(DetachToBrowserTabDragControllerTest);
@@ -2820,6 +2823,38 @@
   EXPECT_EQ("1", IDString(app_browser1->tab_strip_model()));
 }
 
+// Move tab from TYPE_APP Browser to TYPE_NORMAL Browser.
+// Only allowed with feature MixBrowserTypeTabs.
+IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
+                       DragAppToNormalWindow) {
+  // Start the embedded server, and get a browser with Terminal System App.
+  ASSERT_TRUE(embedded_test_server()->Start());
+  Browser* app_browser = GetTerminalAppBrowser();
+  ASSERT_EQ(2u, browser_list->size());
+  // Close normal browser since other code expects only 1 browser to start.
+  CloseBrowserSynchronously(browser());
+  ASSERT_EQ(1u, browser_list->size());
+  SelectFirstBrowser();
+
+  Browser* browser2 = CreateAnotherBrowserAndResize();
+  ResetIDs(browser2->tab_strip_model(), 100);
+
+  AddTabsAndResetBrowser(browser(), 1);
+  TabStrip* tab_strip1 = GetTabStripForBrowser(app_browser);
+  TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
+
+  // Move to the first tab and drag it enough so that it detaches, but not
+  // enough that it attaches to browser2.
+  DragTabAndNotify(tab_strip1, base::BindOnce(&DragToSeparateWindowStep2, this,
+                                              tab_strip1, tab_strip2));
+
+  // Should now be attached to tab_strip2.
+  // Release mouse or touch, stopping the drag session.
+  ASSERT_TRUE(ReleaseInput());
+  EXPECT_EQ("100 0", IDString(browser2->tab_strip_model()));
+  EXPECT_EQ("1", IDString(app_browser->tab_strip_model()));
+}
+
 // Subclass of DetachToBrowserTabDragControllerTest that
 // creates multiple displays.
 class DetachToBrowserInSeparateDisplayTabDragControllerTest
diff --git a/chrome/browser/ui/webui/chromeos/login/assistant_optin_flow_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/assistant_optin_flow_screen_handler.cc
index 84fccba..e325f75d 100644
--- a/chrome/browser/ui/webui/chromeos/login/assistant_optin_flow_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/assistant_optin_flow_screen_handler.cc
@@ -54,6 +54,8 @@
 }
 
 AssistantOptInFlowScreenHandler::~AssistantOptInFlowScreenHandler() {
+  if (client_binding_)
+    StopSpeakerIdEnrollment();
   if (ash::AssistantState::Get())
     ash::AssistantState::Get()->RemoveObserver(this);
   if (screen_)
diff --git a/chrome/browser/ui/webui/ntp/cookie_controls_handler.cc b/chrome/browser/ui/webui/ntp/cookie_controls_handler.cc
index d2803541..3a7b700 100644
--- a/chrome/browser/ui/webui/ntp/cookie_controls_handler.cc
+++ b/chrome/browser/ui/webui/ntp/cookie_controls_handler.cc
@@ -4,15 +4,20 @@
 
 #include "chrome/browser/ui/webui/ntp/cookie_controls_handler.h"
 
+#include <utility>
+
 #include "base/bind.h"
 #include "base/feature_list.h"
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
 #include "base/values.h"
+#include "chrome/browser/policy/profile_policy_connector.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/content_settings/core/browser/cookie_settings.h"
 #include "components/content_settings/core/common/features.h"
 #include "components/content_settings/core/common/pref_names.h"
+#include "components/policy/core/common/policy_service.h"
+#include "components/policy/policy_constants.h"
 #include "components/prefs/pref_service.h"
 
 CookieControlsHandler::CookieControlsHandler() {}
@@ -43,10 +48,19 @@
       prefs::kBlockThirdPartyCookies,
       base::Bind(&CookieControlsHandler::OnThirdPartyCookieBlockingChanged,
                  base::Unretained(this)));
+  policy_registrar_ = std::make_unique<policy::PolicyChangeRegistrar>(
+      profile->GetProfilePolicyConnector()->policy_service(),
+      policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME, std::string()));
+  policy_registrar_->Observe(
+      policy::key::kBlockThirdPartyCookies,
+      base::BindRepeating(
+          &CookieControlsHandler::OnThirdPartyCookieBlockingPolicyChanged,
+          base::Unretained(this)));
 }
 
 void CookieControlsHandler::OnJavascriptDisallowed() {
   pref_change_registrar_.RemoveAll();
+  policy_registrar_.reset();
 }
 
 void CookieControlsHandler::HandleCookieControlsToggleChanged(
@@ -88,6 +102,12 @@
                     base::Value(ShouldHideCookieControlsUI(profile)));
 }
 
+void CookieControlsHandler::OnThirdPartyCookieBlockingPolicyChanged(
+    const base::Value* previous,
+    const base::Value* current) {
+  OnThirdPartyCookieBlockingChanged();
+}
+
 bool CookieControlsHandler::ShouldHideCookieControlsUI(const Profile* profile) {
   return !base::FeatureList::IsEnabled(
              content_settings::kImprovedCookieControls) ||
diff --git a/chrome/browser/ui/webui/ntp/cookie_controls_handler.h b/chrome/browser/ui/webui/ntp/cookie_controls_handler.h
index 95d545e..a0fd7bf 100644
--- a/chrome/browser/ui/webui/ntp/cookie_controls_handler.h
+++ b/chrome/browser/ui/webui/ntp/cookie_controls_handler.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_UI_WEBUI_NTP_COOKIE_CONTROLS_HANDLER_H_
 #define CHROME_BROWSER_UI_WEBUI_NTP_COOKIE_CONTROLS_HANDLER_H_
 
+#include <memory>
+
 #include "components/prefs/pref_change_registrar.h"
 #include "content/public/browser/web_ui_message_handler.h"
 
@@ -13,6 +15,11 @@
 
 namespace base {
 class ListValue;
+class Value;
+}  // namespace base
+
+namespace policy {
+class PolicyChangeRegistrar;
 }
 
 // Handles requests for prefs::kCookieControlsMode retrival/update.
@@ -45,8 +52,13 @@
   // changed.
   void OnThirdPartyCookieBlockingChanged();
 
+  void OnThirdPartyCookieBlockingPolicyChanged(const base::Value* previous,
+                                               const base::Value* current);
+
   PrefChangeRegistrar pref_change_registrar_;
 
+  std::unique_ptr<policy::PolicyChangeRegistrar> policy_registrar_;
+
   DISALLOW_COPY_AND_ASSIGN(CookieControlsHandler);
 };
 
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 8208206..d6b7ecb 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
@@ -350,42 +350,25 @@
   } kPdfResources[] = {
     {"pdf/browser_api.js", IDR_PDF_BROWSER_API_JS},
     {"pdf/controller.js", IDR_PDF_CONTROLLER_JS},
-    {"pdf/elements/icons.html", IDR_PDF_ICONS_HTML},
-    {"pdf/elements/shared-vars.html", IDR_PDF_SHARED_VARS_HTML},
-    {"pdf/elements/viewer-bookmark.html", IDR_PDF_VIEWER_BOOKMARK_HTML},
+    {"pdf/elements/icons.js", IDR_PDF_ICONS_JS},
+    {"pdf/elements/shared-vars.js", IDR_PDF_SHARED_VARS_JS},
     {"pdf/elements/viewer-bookmark.js", IDR_PDF_VIEWER_BOOKMARK_JS},
-    {"pdf/elements/viewer-error-screen.html", IDR_PDF_VIEWER_ERROR_SCREEN_HTML},
     {"pdf/elements/viewer-error-screen.js", IDR_PDF_VIEWER_ERROR_SCREEN_JS},
 #if defined(OS_CHROMEOS)
-    {"pdf/elements/viewer-ink-host.html", IDR_PDF_VIEWER_INK_HOST_HTML},
     {"pdf/elements/viewer-ink-host.js", IDR_PDF_VIEWER_INK_HOST_JS},
 #endif
-    {"pdf/elements/viewer-page-indicator.html",
-     IDR_PDF_VIEWER_PAGE_INDICATOR_HTML},
     {"pdf/elements/viewer-page-indicator.js", IDR_PDF_VIEWER_PAGE_INDICATOR_JS},
-    {"pdf/elements/viewer-page-selector.html",
-     IDR_PDF_VIEWER_PAGE_SELECTOR_HTML},
     {"pdf/elements/viewer-page-selector.js", IDR_PDF_VIEWER_PAGE_SELECTOR_JS},
-    {"pdf/elements/viewer-password-screen.html",
-     IDR_PDF_VIEWER_PASSWORD_SCREEN_HTML},
     {"pdf/elements/viewer-password-screen.js",
      IDR_PDF_VIEWER_PASSWORD_SCREEN_JS},
-    {"pdf/elements/viewer-pdf-toolbar.html", IDR_PDF_VIEWER_PDF_TOOLBAR_HTML},
     {"pdf/elements/viewer-pdf-toolbar.js", IDR_PDF_VIEWER_PDF_TOOLBAR_JS},
 #if defined(OS_CHROMEOS)
-    {"pdf/elements/viewer-form-warning.html", IDR_PDF_VIEWER_FORM_WARNING_HTML},
     {"pdf/elements/viewer-form-warning.js", IDR_PDF_VIEWER_FORM_WARNING_JS},
-    {"pdf/elements/viewer-pen-options.html", IDR_PDF_VIEWER_PEN_OPTIONS_HTML},
     {"pdf/elements/viewer-pen-options.js", IDR_PDF_VIEWER_PEN_OPTIONS_JS},
 #endif
-    {"pdf/elements/viewer-toolbar-dropdown.html",
-     IDR_PDF_VIEWER_TOOLBAR_DROPDOWN_HTML},
     {"pdf/elements/viewer-toolbar-dropdown.js",
      IDR_PDF_VIEWER_TOOLBAR_DROPDOWN_JS},
-    {"pdf/elements/viewer-zoom-button.html", IDR_PDF_VIEWER_ZOOM_BUTTON_HTML},
     {"pdf/elements/viewer-zoom-button.js", IDR_PDF_VIEWER_ZOOM_BUTTON_JS},
-    {"pdf/elements/viewer-zoom-toolbar.html",
-     IDR_PDF_VIEWER_ZOOM_SELECTOR_HTML},
     {"pdf/elements/viewer-zoom-toolbar.js", IDR_PDF_VIEWER_ZOOM_SELECTOR_JS},
     {"pdf/gesture_detector.js", IDR_PDF_GESTURE_DETECTOR_JS},
     {"pdf/index.css", IDR_PDF_INDEX_CSS},
diff --git a/chrome/browser/web_applications/BUILD.gn b/chrome/browser/web_applications/BUILD.gn
index 3381563..b21fb05 100644
--- a/chrome/browser/web_applications/BUILD.gn
+++ b/chrome/browser/web_applications/BUILD.gn
@@ -84,6 +84,8 @@
     "test/service_worker_registration_waiter.h",
     "test/test_app_registrar.cc",
     "test/test_app_registrar.h",
+    "test/test_app_shortcut_manager.cc",
+    "test/test_app_shortcut_manager.h",
     "test/test_data_retriever.cc",
     "test/test_data_retriever.h",
     "test/test_file_handler_manager.cc",
diff --git a/chrome/browser/web_applications/components/app_shortcut_manager.cc b/chrome/browser/web_applications/components/app_shortcut_manager.cc
index 855ce52..57380c0a 100644
--- a/chrome/browser/web_applications/components/app_shortcut_manager.cc
+++ b/chrome/browser/web_applications/components/app_shortcut_manager.cc
@@ -4,8 +4,31 @@
 
 #include "chrome/browser/web_applications/components/app_shortcut_manager.h"
 
+#include "base/callback.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+
 namespace web_app {
 
+namespace {
+
+void OnShortcutInfoRetrievedCreateShortcuts(
+    bool add_to_desktop,
+    CreateShortcutsCallback callback,
+    std::unique_ptr<ShortcutInfo> info) {
+  base::FilePath shortcut_data_dir = internals::GetShortcutDataDir(*info);
+
+  ShortcutLocations locations;
+  locations.on_desktop = add_to_desktop;
+  locations.applications_menu_location = APP_MENU_LOCATION_SUBDIR_CHROMEAPPS;
+
+  internals::ScheduleCreatePlatformShortcuts(
+      std::move(shortcut_data_dir), locations, SHORTCUT_CREATION_BY_USER,
+      std::move(info), std::move(callback));
+}
+
+}  // namespace
+
 AppShortcutManager::AppShortcutManager(Profile* profile) : profile_(profile) {}
 
 AppShortcutManager::~AppShortcutManager() = default;
@@ -14,4 +37,21 @@
   registrar_ = registrar;
 }
 
+bool AppShortcutManager::CanCreateShortcuts() const {
+#if defined(OS_CHROMEOS)
+  return false;
+#else
+  return true;
+#endif
+}
+
+void AppShortcutManager::CreateShortcuts(const AppId& app_id,
+                                         bool add_to_desktop,
+                                         CreateShortcutsCallback callback) {
+  DCHECK(CanCreateShortcuts());
+  GetShortcutInfoForApp(app_id,
+                        base::BindOnce(&OnShortcutInfoRetrievedCreateShortcuts,
+                                       add_to_desktop, std::move(callback)));
+}
+
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/components/app_shortcut_manager.h b/chrome/browser/web_applications/components/app_shortcut_manager.h
index 1822c589..78f9623 100644
--- a/chrome/browser/web_applications/components/app_shortcut_manager.h
+++ b/chrome/browser/web_applications/components/app_shortcut_manager.h
@@ -10,6 +10,7 @@
 #include "base/callback_forward.h"
 #include "base/macros.h"
 #include "chrome/browser/web_applications/components/web_app_helpers.h"
+#include "chrome/browser/web_applications/components/web_app_shortcut.h"
 
 class Profile;
 
@@ -22,6 +23,7 @@
 // web_app_extension_shortcut.(h|cc) and
 // platform_apps/shortcut_manager.(h|cc) to the AppShortcutManager, so web app
 // shortcuts can be managed in an extensions agnostic way.
+// Manages OS shortcuts for web applications.
 class AppShortcutManager {
  public:
   explicit AppShortcutManager(Profile* profile);
@@ -29,6 +31,14 @@
 
   void SetSubsystems(AppRegistrar* registrar);
 
+  // virtual for testing.
+  virtual bool CanCreateShortcuts() const;
+
+  // virtual for testing.
+  virtual void CreateShortcuts(const AppId& app_id,
+                               bool add_to_desktop,
+                               CreateShortcutsCallback callback);
+
   // The result of a call to GetShortcutInfo.
   using GetShortcutInfoCallback =
       base::OnceCallback<void(std::unique_ptr<ShortcutInfo>)>;
diff --git a/chrome/browser/web_applications/components/install_finalizer.h b/chrome/browser/web_applications/components/install_finalizer.h
index 44e92689..a72bcb6 100644
--- a/chrome/browser/web_applications/components/install_finalizer.h
+++ b/chrome/browser/web_applications/components/install_finalizer.h
@@ -31,8 +31,6 @@
   using InstallFinalizedCallback =
       base::OnceCallback<void(const AppId& app_id, InstallResultCode code)>;
   using UninstallWebAppCallback = base::OnceCallback<void(bool uninstalled)>;
-  using CreateOsShortcutsCallback =
-      base::OnceCallback<void(bool shortcuts_created)>;
 
   struct FinalizeOptions {
     WebappInstallSource install_source = WebappInstallSource::COUNT;
@@ -58,10 +56,6 @@
   virtual void UninstallWebApp(const AppId& app_id,
                                UninstallWebAppCallback) = 0;
 
-  virtual bool CanCreateOsShortcuts() const = 0;
-  virtual void CreateOsShortcuts(const AppId& app_id,
-                                 bool add_to_desktop,
-                                 CreateOsShortcutsCallback callback) = 0;
   // |virtual| for testing.
   virtual bool CanAddAppToQuickLaunchBar() const;
   virtual void AddAppToQuickLaunchBar(const AppId& app_id);
diff --git a/chrome/browser/web_applications/components/install_manager.cc b/chrome/browser/web_applications/components/install_manager.cc
index 39d8e04..9342cfd 100644
--- a/chrome/browser/web_applications/components/install_manager.cc
+++ b/chrome/browser/web_applications/components/install_manager.cc
@@ -54,8 +54,10 @@
 InstallManager::~InstallManager() = default;
 
 void InstallManager::SetSubsystems(AppRegistrar* registrar,
+                                   AppShortcutManager* shortcut_manager,
                                    InstallFinalizer* finalizer) {
   registrar_ = registrar;
+  shortcut_manager_ = shortcut_manager;
   finalizer_ = finalizer;
 }
 
diff --git a/chrome/browser/web_applications/components/install_manager.h b/chrome/browser/web_applications/components/install_manager.h
index 69ebcee..6a0e503a 100644
--- a/chrome/browser/web_applications/components/install_manager.h
+++ b/chrome/browser/web_applications/components/install_manager.h
@@ -28,6 +28,7 @@
 enum class InstallResultCode;
 class InstallFinalizer;
 class AppRegistrar;
+class AppShortcutManager;
 
 // TODO(loyso): Rework this interface. Unify the API and merge similar
 // InstallWebAppZZZZ functions.
@@ -125,7 +126,9 @@
   explicit InstallManager(Profile* profile);
   virtual ~InstallManager();
 
-  void SetSubsystems(AppRegistrar* registrar, InstallFinalizer* finalizer);
+  void SetSubsystems(AppRegistrar* registrar,
+                     AppShortcutManager* shortcut_manager,
+                     InstallFinalizer* finalizer);
 
   // Loads |web_app_url| in a new WebContents and determines if it is
   // installable. Returns the WebContents and whether the app is installable or
@@ -136,6 +139,7 @@
  protected:
   Profile* profile() { return profile_; }
   AppRegistrar* registrar() { return registrar_; }
+  AppShortcutManager* shortcut_manager() { return shortcut_manager_; }
   InstallFinalizer* finalizer() { return finalizer_; }
 
  private:
@@ -143,6 +147,7 @@
   WebAppUrlLoader url_loader_;
 
   AppRegistrar* registrar_ = nullptr;
+  AppShortcutManager* shortcut_manager_ = nullptr;
   InstallFinalizer* finalizer_ = nullptr;
 };
 
diff --git a/chrome/browser/web_applications/components/pending_app_manager.h b/chrome/browser/web_applications/components/pending_app_manager.h
index b9c26b8..597f2176 100644
--- a/chrome/browser/web_applications/components/pending_app_manager.h
+++ b/chrome/browser/web_applications/components/pending_app_manager.h
@@ -24,6 +24,7 @@
 enum class InstallResultCode;
 
 class AppRegistrar;
+class AppShortcutManager;
 class InstallFinalizer;
 class WebAppUiManager;
 
@@ -112,6 +113,7 @@
 
  protected:
   AppRegistrar* registrar() { return registrar_; }
+  AppShortcutManager* shortcut_manager() { return shortcut_manager_; }
   WebAppUiManager* ui_manager() { return ui_manager_; }
   InstallFinalizer* finalizer() { return finalizer_; }
 
@@ -144,6 +146,7 @@
   void OnAppSynchronized(ExternalInstallSource source, const GURL& app_url);
 
   AppRegistrar* registrar_ = nullptr;
+  AppShortcutManager* shortcut_manager_ = nullptr;
   WebAppUiManager* ui_manager_ = nullptr;
   InstallFinalizer* finalizer_ = nullptr;
 
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.cc b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.cc
index 0b1be89..0ee73474 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.cc
+++ b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.cc
@@ -170,20 +170,6 @@
       FROM_HERE, base::BindOnce(std::move(callback), uninstalled));
 }
 
-bool BookmarkAppInstallFinalizer::CanCreateOsShortcuts() const {
-  return CanBookmarkAppCreateOsShortcuts();
-}
-
-void BookmarkAppInstallFinalizer::CreateOsShortcuts(
-    const web_app::AppId& app_id,
-    bool add_to_desktop,
-    CreateOsShortcutsCallback callback) {
-  const Extension* app = GetEnabledExtension(app_id);
-  DCHECK(app);
-  BookmarkAppCreateOsShortcuts(profile_, app, add_to_desktop,
-                               std::move(callback));
-}
-
 bool BookmarkAppInstallFinalizer::CanRevealAppShim() const {
 #if defined(OS_MACOSX)
   return true;
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.h b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.h
index 65ca7c2..2c6ad39 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.h
+++ b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.h
@@ -41,10 +41,6 @@
                                UninstallWebAppCallback callback) override;
   void UninstallWebApp(const web_app::AppId& app_id,
                        UninstallWebAppCallback) override;
-  bool CanCreateOsShortcuts() const override;
-  void CreateOsShortcuts(const web_app::AppId& app_id,
-                         bool add_to_desktop,
-                         CreateOsShortcutsCallback callback) override;
   bool CanRevealAppShim() const override;
   void RevealAppShim(const web_app::AppId& app_id) override;
   bool CanUserUninstallFromSync(const web_app::AppId& app_id) const override;
diff --git a/chrome/browser/web_applications/extensions/install_manager_bookmark_app_unittest.cc b/chrome/browser/web_applications/extensions/install_manager_bookmark_app_unittest.cc
index 8490e0a24..0f6e076 100644
--- a/chrome/browser/web_applications/extensions/install_manager_bookmark_app_unittest.cc
+++ b/chrome/browser/web_applications/extensions/install_manager_bookmark_app_unittest.cc
@@ -100,13 +100,6 @@
   ~BookmarkAppInstallFinalizerInstallOnly() override = default;
 
   // InstallFinalizer:
-  void CreateOsShortcuts(const web_app::AppId& app_id,
-                         bool add_to_desktop,
-                         CreateOsShortcutsCallback callback) override {
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::BindOnce(std::move(callback), true /*shortcuts_created*/));
-  }
   void AddAppToQuickLaunchBar(const web_app::AppId& app_id) override {}
   void ReparentTab(const web_app::AppId& app_id,
                    bool shortcut_created,
diff --git a/chrome/browser/web_applications/extensions/pending_app_install_task_unittest.cc b/chrome/browser/web_applications/extensions/pending_app_install_task_unittest.cc
index 509f479..8437bf0 100644
--- a/chrome/browser/web_applications/extensions/pending_app_install_task_unittest.cc
+++ b/chrome/browser/web_applications/extensions/pending_app_install_task_unittest.cc
@@ -28,6 +28,7 @@
 #include "chrome/browser/web_applications/components/web_app_helpers.h"
 #include "chrome/browser/web_applications/components/web_app_provider_base.h"
 #include "chrome/browser/web_applications/test/test_app_registrar.h"
+#include "chrome/browser/web_applications/test/test_app_shortcut_manager.h"
 #include "chrome/browser/web_applications/test/test_data_retriever.h"
 #include "chrome/browser/web_applications/test/test_install_finalizer.h"
 #include "chrome/browser/web_applications/test/test_web_app_provider.h"
@@ -116,16 +117,6 @@
         GetAppIdForUrl(app_url), uninstalled};
   }
 
-  void SetNextCreateOsShortcutsResult(const AppId& app_id,
-                                      bool shortcut_created) {
-    DCHECK(!base::Contains(next_create_os_shortcuts_results_, app_id));
-    next_create_os_shortcuts_results_[app_id] = shortcut_created;
-  }
-
-  void SetCanCreateOsShortcuts(bool can_create_os_shortcuts) {
-    can_create_os_shortcuts_ = can_create_os_shortcuts;
-  }
-
   const std::vector<WebApplicationInfo>& web_app_info_list() {
     return web_app_info_list_;
   }
@@ -137,10 +128,6 @@
   const std::vector<GURL>& uninstall_external_web_app_urls() const {
     return uninstall_external_web_app_urls_;
   }
-
-  size_t num_create_os_shortcuts_calls() {
-    return num_create_os_shortcuts_calls_;
-  }
   size_t num_add_app_to_quick_launch_bar_calls() {
     return num_add_app_to_quick_launch_bar_calls_;
   }
@@ -203,24 +190,6 @@
   void UninstallWebApp(const AppId& app_dd,
                        UninstallWebAppCallback callback) override {}
 
-  bool CanCreateOsShortcuts() const override {
-    return can_create_os_shortcuts_;
-  }
-
-  void CreateOsShortcuts(const AppId& app_id,
-                         bool add_to_desktop,
-                         CreateOsShortcutsCallback callback) override {
-    DCHECK(base::Contains(next_create_os_shortcuts_results_, app_id));
-    ++num_create_os_shortcuts_calls_;
-    add_to_desktop_ = add_to_desktop;
-    bool shortcut_created = next_create_os_shortcuts_results_[app_id];
-    next_create_os_shortcuts_results_.erase(app_id);
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(std::move(callback), shortcut_created));
-  }
-
-  const base::Optional<bool>& add_to_desktop() { return add_to_desktop_; }
-
   bool CanAddAppToQuickLaunchBar() const override { return true; }
 
   void AddAppToQuickLaunchBar(const AppId& app_id) override {
@@ -252,15 +221,10 @@
  private:
   TestAppRegistrar* registrar_ = nullptr;
 
-  bool can_create_os_shortcuts_ = true;
-
   std::vector<WebApplicationInfo> web_app_info_list_;
   std::vector<FinalizeOptions> finalize_options_list_;
   std::vector<GURL> uninstall_external_web_app_urls_;
 
-  size_t num_create_os_shortcuts_calls_ = 0;
-  base::Optional<bool> add_to_desktop_;
-
   size_t num_add_app_to_quick_launch_bar_calls_ = 0;
   size_t num_reparent_tab_calls_ = 0;
   size_t num_reveal_appshim_calls_ = 0;
@@ -273,8 +237,6 @@
   std::map<GURL, std::pair<AppId, bool>>
       next_uninstall_external_web_app_results_;
 
-  std::map<AppId, bool> next_create_os_shortcuts_results_;
-
   DISALLOW_COPY_AND_ASSIGN(TestPendingAppInstallFinalizer);
 };
 
@@ -304,6 +266,9 @@
     auto install_manager = std::make_unique<WebAppInstallManager>(profile());
     install_manager_ = install_manager.get();
 
+    auto shortcut_manager = std::make_unique<TestAppShortcutManager>(profile());
+    shortcut_manager_ = shortcut_manager.get();
+
     auto ui_manager = std::make_unique<TestWebAppUiManager>();
     ui_manager_ = ui_manager.get();
 
@@ -311,6 +276,7 @@
     provider->SetInstallManager(std::move(install_manager));
     provider->SetInstallFinalizer(std::move(install_finalizer));
     provider->SetWebAppUiManager(std::move(ui_manager));
+    provider->SetShortcutManager(std::move(shortcut_manager));
 
     provider->Start();
   }
@@ -319,6 +285,7 @@
   TestWebAppUiManager* ui_manager() { return ui_manager_; }
   TestAppRegistrar* registrar() { return registrar_; }
   TestPendingAppInstallFinalizer* finalizer() { return install_finalizer_; }
+  TestAppShortcutManager* shortcut_manager() { return shortcut_manager_; }
 
   TestDataRetriever* data_retriever() { return data_retriever_; }
 
@@ -352,12 +319,12 @@
     install_finalizer_->SetNextFinalizeInstallResult(
         options.url, InstallResultCode::kSuccessNewInstall);
 
-    install_finalizer_->SetNextCreateOsShortcutsResult(
+    shortcut_manager_->SetNextCreateShortcutsResult(
         install_finalizer_->GetAppIdForUrl(options.url), true);
 
     auto task = std::make_unique<PendingAppInstallTask>(
-        profile(), registrar_, ui_manager_, install_finalizer_,
-        std::move(options));
+        profile(), registrar_, shortcut_manager_, ui_manager_,
+        install_finalizer_, std::move(options));
     return task;
   }
 
@@ -369,6 +336,7 @@
   TestDataRetriever* data_retriever_ = nullptr;
   TestPendingAppInstallFinalizer* install_finalizer_ = nullptr;
   TestWebAppUiManager* ui_manager_ = nullptr;
+  TestAppShortcutManager* shortcut_manager_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(PendingAppInstallTaskTest);
 };
@@ -395,8 +363,9 @@
 
         EXPECT_EQ(result.app_id.value(), id.value());
 
-        EXPECT_EQ(1u, finalizer()->num_create_os_shortcuts_calls());
-        EXPECT_TRUE(finalizer()->add_to_desktop().value());
+        EXPECT_EQ(1u, shortcut_manager()->num_create_shortcuts_calls());
+        EXPECT_TRUE(shortcut_manager()->did_add_to_desktop().value());
+
         EXPECT_EQ(1u, finalizer()->num_add_app_to_quick_launch_bar_calls());
         EXPECT_EQ(0u, finalizer()->num_reparent_tab_calls());
         EXPECT_EQ(0u, finalizer()->num_reveal_appshim_calls());
@@ -454,8 +423,8 @@
         EXPECT_EQ(InstallResultCode::kSuccessNewInstall, result.code);
         EXPECT_TRUE(result.app_id.has_value());
 
-        EXPECT_EQ(1u, finalizer()->num_create_os_shortcuts_calls());
-        EXPECT_FALSE(finalizer()->add_to_desktop().value());
+        EXPECT_EQ(1u, shortcut_manager()->num_create_shortcuts_calls());
+        EXPECT_FALSE(shortcut_manager()->did_add_to_desktop().value());
 
         EXPECT_EQ(1u, finalizer()->num_add_app_to_quick_launch_bar_calls());
         EXPECT_EQ(0u, finalizer()->num_reparent_tab_calls());
@@ -482,8 +451,8 @@
         EXPECT_EQ(InstallResultCode::kSuccessNewInstall, result.code);
         EXPECT_TRUE(result.app_id.has_value());
 
-        EXPECT_EQ(1u, finalizer()->num_create_os_shortcuts_calls());
-        EXPECT_TRUE(finalizer()->add_to_desktop().value());
+        EXPECT_EQ(1u, shortcut_manager()->num_create_shortcuts_calls());
+        EXPECT_TRUE(shortcut_manager()->did_add_to_desktop().value());
 
         EXPECT_EQ(0u, finalizer()->num_add_app_to_quick_launch_bar_calls());
         EXPECT_EQ(0u, finalizer()->num_reparent_tab_calls());
@@ -512,8 +481,8 @@
         EXPECT_EQ(InstallResultCode::kSuccessNewInstall, result.code);
         EXPECT_TRUE(result.app_id.has_value());
 
-        EXPECT_EQ(1u, finalizer()->num_create_os_shortcuts_calls());
-        EXPECT_FALSE(finalizer()->add_to_desktop().value());
+        EXPECT_EQ(1u, shortcut_manager()->num_create_shortcuts_calls());
+        EXPECT_FALSE(shortcut_manager()->did_add_to_desktop().value());
 
         EXPECT_EQ(0u, finalizer()->num_add_app_to_quick_launch_bar_calls());
         EXPECT_EQ(0u, finalizer()->num_reparent_tab_calls());
@@ -623,7 +592,7 @@
 
         EXPECT_TRUE(IsPlaceholderApp(profile(), kWebAppUrl));
 
-        EXPECT_EQ(1u, finalizer()->num_create_os_shortcuts_calls());
+        EXPECT_EQ(1u, shortcut_manager()->num_create_shortcuts_calls());
         EXPECT_EQ(1u, finalizer()->finalize_options_list().size());
         EXPECT_EQ(WebappInstallSource::EXTERNAL_POLICY,
                   finalize_options().install_source);
@@ -648,7 +617,7 @@
                                  ExternalInstallSource::kExternalPolicy);
   options.install_placeholder = true;
   auto task = GetInstallationTaskWithTestMocks(std::move(options));
-  finalizer()->SetCanCreateOsShortcuts(false);
+  shortcut_manager()->set_can_create_shortcuts(false);
 
   base::RunLoop run_loop;
   task->Install(
@@ -659,7 +628,7 @@
 
         EXPECT_TRUE(IsPlaceholderApp(profile(), kWebAppUrl));
 
-        EXPECT_EQ(0u, finalizer()->num_create_os_shortcuts_calls());
+        EXPECT_EQ(0u, shortcut_manager()->num_create_shortcuts_calls());
         EXPECT_EQ(1u, finalizer()->finalize_options_list().size());
         EXPECT_EQ(WebappInstallSource::EXTERNAL_POLICY,
                   finalize_options().install_source);
@@ -877,7 +846,8 @@
     ExternalInstallOptions install_options(
         GURL(), blink::mojom::DisplayMode::kStandalone,
         ExternalInstallSource::kInternalDefault);
-    PendingAppInstallTask install_task(profile(), registrar(), ui_manager(),
+    PendingAppInstallTask install_task(profile(), registrar(),
+                                       shortcut_manager(), ui_manager(),
                                        finalizer(), install_options);
 
     install_task.Install(
@@ -895,8 +865,9 @@
   ExternalInstallOptions install_options(
       GURL(), blink::mojom::DisplayMode::kStandalone,
       ExternalInstallSource::kInternalDefault);
-  PendingAppInstallTask install_task(profile(), registrar(), ui_manager(),
-                                     finalizer(), install_options);
+  PendingAppInstallTask install_task(profile(), registrar(), shortcut_manager(),
+                                     ui_manager(), finalizer(),
+                                     install_options);
 
   install_task.Install(
       web_contents(), WebAppUrlLoader::Result::kFailedWebContentsDestroyed,
diff --git a/chrome/browser/web_applications/pending_app_install_task.cc b/chrome/browser/web_applications/pending_app_install_task.cc
index 4fe4a23..1e30febe 100644
--- a/chrome/browser/web_applications/pending_app_install_task.cc
+++ b/chrome/browser/web_applications/pending_app_install_task.cc
@@ -17,6 +17,7 @@
 #include "chrome/browser/installable/installable_metrics.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ssl/security_state_tab_helper.h"
+#include "chrome/browser/web_applications/components/app_shortcut_manager.h"
 #include "chrome/browser/web_applications/components/install_finalizer.h"
 #include "chrome/browser/web_applications/components/install_manager.h"
 #include "chrome/browser/web_applications/components/web_app_constants.h"
@@ -49,11 +50,13 @@
 PendingAppInstallTask::PendingAppInstallTask(
     Profile* profile,
     AppRegistrar* registrar,
+    AppShortcutManager* shortcut_manger,
     WebAppUiManager* ui_manager,
     InstallFinalizer* install_finalizer,
     ExternalInstallOptions install_options)
     : profile_(profile),
       registrar_(registrar),
+      shortcut_manager_(shortcut_manger),
       install_finalizer_(install_finalizer),
       ui_manager_(ui_manager),
       externally_installed_app_prefs_(profile_->GetPrefs()),
@@ -236,8 +239,8 @@
   // TODO(ortuno): Make adding a shortcut to the applications menu independent
   // from adding a shortcut to desktop.
   if (install_options_.add_to_applications_menu &&
-      install_finalizer_->CanCreateOsShortcuts()) {
-    install_finalizer_->CreateOsShortcuts(
+      shortcut_manager_->CanCreateShortcuts()) {
+    shortcut_manager_->CreateShortcuts(
         app_id, install_options_.add_to_desktop,
         base::BindOnce(
             [](base::ScopedClosureRunner scoped_closure,
diff --git a/chrome/browser/web_applications/pending_app_install_task.h b/chrome/browser/web_applications/pending_app_install_task.h
index 9441c93..9b509705 100644
--- a/chrome/browser/web_applications/pending_app_install_task.h
+++ b/chrome/browser/web_applications/pending_app_install_task.h
@@ -25,9 +25,10 @@
 
 namespace web_app {
 
-enum class InstallResultCode;
+class AppShortcutManager;
 class InstallFinalizer;
 class WebAppUiManager;
+enum class InstallResultCode;
 
 // Class to install WebApp from a WebContents. A queue of such tasks is owned by
 // PendingAppManager. Can only be called from the UI thread.
@@ -56,6 +57,7 @@
   // policy, etc.
   explicit PendingAppInstallTask(Profile* profile,
                                  AppRegistrar* registrar,
+                                 AppShortcutManager* shortcut_manager,
                                  WebAppUiManager* ui_manager,
                                  InstallFinalizer* install_finalizer,
                                  ExternalInstallOptions install_options);
@@ -88,6 +90,7 @@
 
   Profile* const profile_;
   AppRegistrar* const registrar_;
+  AppShortcutManager* const shortcut_manager_;
   InstallFinalizer* const install_finalizer_;
   WebAppUiManager* const ui_manager_;
 
diff --git a/chrome/browser/web_applications/pending_app_manager_impl.cc b/chrome/browser/web_applications/pending_app_manager_impl.cc
index 6024cb3..ff208ee 100644
--- a/chrome/browser/web_applications/pending_app_manager_impl.cc
+++ b/chrome/browser/web_applications/pending_app_manager_impl.cc
@@ -96,9 +96,9 @@
 std::unique_ptr<PendingAppInstallTask>
 PendingAppManagerImpl::CreateInstallationTask(
     ExternalInstallOptions install_options) {
-  return std::make_unique<PendingAppInstallTask>(profile_, registrar(),
-                                                 ui_manager(), finalizer(),
-                                                 std::move(install_options));
+  return std::make_unique<PendingAppInstallTask>(
+      profile_, registrar(), shortcut_manager(), ui_manager(), finalizer(),
+      std::move(install_options));
 }
 
 std::unique_ptr<PendingAppRegistrationTaskBase>
diff --git a/chrome/browser/web_applications/pending_app_manager_impl_unittest.cc b/chrome/browser/web_applications/pending_app_manager_impl_unittest.cc
index 2bc4ebc..bb97c7d1 100644
--- a/chrome/browser/web_applications/pending_app_manager_impl_unittest.cc
+++ b/chrome/browser/web_applications/pending_app_manager_impl_unittest.cc
@@ -181,6 +181,7 @@
         ExternalInstallOptions install_options)
         : PendingAppInstallTask(profile,
                                 pending_app_manager_impl->registrar(),
+                                pending_app_manager_impl->shortcut_manager(),
                                 pending_app_manager_impl->ui_manager(),
                                 pending_app_manager_impl->finalizer(),
                                 install_options),
diff --git a/chrome/browser/web_applications/test/test_app_shortcut_manager.cc b/chrome/browser/web_applications/test/test_app_shortcut_manager.cc
new file mode 100644
index 0000000..c15524fe4
--- /dev/null
+++ b/chrome/browser/web_applications/test/test_app_shortcut_manager.cc
@@ -0,0 +1,51 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/web_applications/test/test_app_shortcut_manager.h"
+
+#include "base/callback.h"
+#include "base/threading/thread_task_runner_handle.h"
+
+namespace web_app {
+
+TestAppShortcutManager::TestAppShortcutManager(Profile* profile)
+    : AppShortcutManager(profile) {}
+
+TestAppShortcutManager::~TestAppShortcutManager() = default;
+
+void TestAppShortcutManager::SetNextCreateShortcutsResult(const AppId& app_id,
+                                                          bool success) {
+  DCHECK(!base::Contains(next_create_shortcut_results_, app_id));
+  next_create_shortcut_results_[app_id] = success;
+}
+
+bool TestAppShortcutManager::CanCreateShortcuts() const {
+  return can_create_shortcuts_;
+}
+
+void TestAppShortcutManager::CreateShortcuts(const AppId& app_id,
+                                             bool on_desktop,
+                                             CreateShortcutsCallback callback) {
+  ++num_create_shortcuts_calls_;
+  did_add_to_desktop_ = on_desktop;
+
+  bool success = true;
+
+  auto it = next_create_shortcut_results_.find(app_id);
+  if (it != next_create_shortcut_results_.end()) {
+    success = it->second;
+    next_create_shortcut_results_.erase(app_id);
+  }
+
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(callback), success));
+}
+
+void TestAppShortcutManager::GetShortcutInfoForApp(
+    const AppId& app_id,
+    GetShortcutInfoCallback callback) {
+  NOTIMPLEMENTED();
+}
+
+}  // namespace web_app
diff --git a/chrome/browser/web_applications/test/test_app_shortcut_manager.h b/chrome/browser/web_applications/test/test_app_shortcut_manager.h
new file mode 100644
index 0000000..ea4a61e5
--- /dev/null
+++ b/chrome/browser/web_applications/test/test_app_shortcut_manager.h
@@ -0,0 +1,55 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_WEB_APPLICATIONS_TEST_TEST_APP_SHORTCUT_MANAGER_H_
+#define CHROME_BROWSER_WEB_APPLICATIONS_TEST_TEST_APP_SHORTCUT_MANAGER_H_
+
+#include <map>
+
+#include "base/optional.h"
+#include "chrome/browser/web_applications/components/app_shortcut_manager.h"
+#include "chrome/browser/web_applications/components/web_app_helpers.h"
+
+class Profile;
+
+namespace web_app {
+
+class TestAppShortcutManager : public AppShortcutManager {
+ public:
+  explicit TestAppShortcutManager(Profile* profile);
+  ~TestAppShortcutManager() override;
+
+  size_t num_create_shortcuts_calls() const {
+    return num_create_shortcuts_calls_;
+  }
+
+  void set_can_create_shortcuts(bool can_create_shortcuts) {
+    can_create_shortcuts_ = can_create_shortcuts;
+  }
+
+  base::Optional<bool> did_add_to_desktop() const {
+    return did_add_to_desktop_;
+  }
+
+  void SetNextCreateShortcutsResult(const AppId& app_id, bool success);
+
+  // AppShortcutManager:
+  bool CanCreateShortcuts() const override;
+  void CreateShortcuts(const AppId& app_id,
+                       bool on_desktop,
+                       CreateShortcutsCallback callback) override;
+  void GetShortcutInfoForApp(const AppId& app_id,
+                             GetShortcutInfoCallback callback) override;
+
+ private:
+  size_t num_create_shortcuts_calls_ = 0;
+  base::Optional<bool> did_add_to_desktop_;
+
+  bool can_create_shortcuts_ = true;
+  std::map<AppId, bool> next_create_shortcut_results_;
+};
+
+}  // namespace web_app
+
+#endif  // CHROME_BROWSER_WEB_APPLICATIONS_TEST_TEST_APP_SHORTCUT_MANAGER_H_
diff --git a/chrome/browser/web_applications/test/test_install_finalizer.cc b/chrome/browser/web_applications/test/test_install_finalizer.cc
index 87fbe20c..1ef0ec5f 100644
--- a/chrome/browser/web_applications/test/test_install_finalizer.cc
+++ b/chrome/browser/web_applications/test/test_install_finalizer.cc
@@ -60,20 +60,6 @@
 void TestInstallFinalizer::UninstallWebApp(const AppId& app_url,
                                            UninstallWebAppCallback callback) {}
 
-bool TestInstallFinalizer::CanCreateOsShortcuts() const {
-  return true;
-}
-
-void TestInstallFinalizer::CreateOsShortcuts(
-    const AppId& app_id,
-    bool add_to_desktop,
-    CreateOsShortcutsCallback callback) {
-  ++num_create_os_shortcuts_calls_;
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE,
-      base::BindOnce(std::move(callback), true /* shortcuts_created */));
-}
-
 bool TestInstallFinalizer::CanAddAppToQuickLaunchBar() const {
   return true;
 }
diff --git a/chrome/browser/web_applications/test/test_install_finalizer.h b/chrome/browser/web_applications/test/test_install_finalizer.h
index e0410b0..618d0a92 100644
--- a/chrome/browser/web_applications/test/test_install_finalizer.h
+++ b/chrome/browser/web_applications/test/test_install_finalizer.h
@@ -34,10 +34,6 @@
                                UninstallWebAppCallback callback) override;
   void UninstallWebApp(const AppId& app_id,
                        UninstallWebAppCallback callback) override;
-  bool CanCreateOsShortcuts() const override;
-  void CreateOsShortcuts(const AppId& app_id,
-                         bool add_to_desktop,
-                         CreateOsShortcutsCallback callback) override;
   bool CanAddAppToQuickLaunchBar() const override;
   void AddAppToQuickLaunchBar(const AppId& app_id) override;
   bool CanReparentTab(const AppId& app_id,
@@ -66,7 +62,6 @@
     return uninstall_external_web_app_urls_;
   }
 
-  int num_create_os_shortcuts_calls() { return num_create_os_shortcuts_calls_; }
   int num_reparent_tab_calls() { return num_reparent_tab_calls_; }
   int num_reveal_appshim_calls() { return num_reveal_appshim_calls_; }
   int num_add_app_to_quick_launch_bar_calls() {
@@ -86,7 +81,6 @@
   base::Optional<InstallResultCode> next_result_code_;
   std::map<GURL, bool> next_uninstall_external_web_app_results_;
 
-  int num_create_os_shortcuts_calls_ = 0;
   int num_reparent_tab_calls_ = 0;
   int num_reveal_appshim_calls_ = 0;
   int num_add_app_to_quick_launch_bar_calls_ = 0;
diff --git a/chrome/browser/web_applications/test/test_web_app_provider.cc b/chrome/browser/web_applications/test/test_web_app_provider.cc
index 1923494..f137da7 100644
--- a/chrome/browser/web_applications/test/test_web_app_provider.cc
+++ b/chrome/browser/web_applications/test/test_web_app_provider.cc
@@ -9,6 +9,7 @@
 #include "base/bind.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/web_applications/components/app_registrar.h"
+#include "chrome/browser/web_applications/components/app_shortcut_manager.h"
 #include "chrome/browser/web_applications/components/file_handler_manager.h"
 #include "chrome/browser/web_applications/components/install_finalizer.h"
 #include "chrome/browser/web_applications/components/pending_app_manager.h"
@@ -102,6 +103,12 @@
   web_app_policy_manager_ = std::move(web_app_policy_manager);
 }
 
+void TestWebAppProvider::SetShortcutManager(
+    std::unique_ptr<AppShortcutManager> shortcut_manager) {
+  CheckNotStarted();
+  shortcut_manager_ = std::move(shortcut_manager);
+}
+
 void TestWebAppProvider::CheckNotStarted() const {
   CHECK(!started_) << "Attempted to set a WebAppProvider subsystem after "
                       "Start() was called.";
diff --git a/chrome/browser/web_applications/test/test_web_app_provider.h b/chrome/browser/web_applications/test/test_web_app_provider.h
index 70027474..7fad385 100644
--- a/chrome/browser/web_applications/test/test_web_app_provider.h
+++ b/chrome/browser/web_applications/test/test_web_app_provider.h
@@ -20,6 +20,7 @@
 namespace web_app {
 
 class AppRegistrar;
+class AppShortcutManager;
 class InstallFinalizer;
 class PendingAppManager;
 class SystemWebAppManager;
@@ -58,6 +59,7 @@
       std::unique_ptr<SystemWebAppManager> system_web_app_manager);
   void SetWebAppPolicyManager(
       std::unique_ptr<WebAppPolicyManager> web_app_policy_manager);
+  void SetShortcutManager(std::unique_ptr<AppShortcutManager> shortcut_manager);
 
  private:
   void CheckNotStarted() const;
diff --git a/chrome/browser/web_applications/web_app_install_finalizer.cc b/chrome/browser/web_applications/web_app_install_finalizer.cc
index dee2234..55b3948 100644
--- a/chrome/browser/web_applications/web_app_install_finalizer.cc
+++ b/chrome/browser/web_applications/web_app_install_finalizer.cc
@@ -202,23 +202,6 @@
   std::move(callback).Run(app_id, InstallResultCode::kSuccessNewInstall);
 }
 
-bool WebAppInstallFinalizer::CanCreateOsShortcuts() const {
-  // TODO(loyso): Implement it.
-  NOTIMPLEMENTED();
-  return false;
-}
-
-void WebAppInstallFinalizer::CreateOsShortcuts(
-    const AppId& app_id,
-    bool add_to_desktop,
-    CreateOsShortcutsCallback callback) {
-  // TODO(loyso): Implement it.
-  NOTIMPLEMENTED();
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE,
-      base::BindOnce(std::move(callback), false /* shortcuts_created */));
-}
-
 bool WebAppInstallFinalizer::CanRevealAppShim() const {
   // TODO(loyso): Implement it.
   NOTIMPLEMENTED();
diff --git a/chrome/browser/web_applications/web_app_install_finalizer.h b/chrome/browser/web_applications/web_app_install_finalizer.h
index 24dba87..b65f782f 100644
--- a/chrome/browser/web_applications/web_app_install_finalizer.h
+++ b/chrome/browser/web_applications/web_app_install_finalizer.h
@@ -34,10 +34,6 @@
   void UninstallExternalWebApp(const GURL& app_url,
                                UninstallWebAppCallback callback) override;
   void UninstallWebApp(const AppId& app_id, UninstallWebAppCallback) override;
-  bool CanCreateOsShortcuts() const override;
-  void CreateOsShortcuts(const AppId& app_id,
-                         bool add_to_desktop,
-                         CreateOsShortcutsCallback callback) override;
   bool CanRevealAppShim() const override;
   void RevealAppShim(const AppId& app_id) override;
   bool CanUserUninstallFromSync(const AppId& app_id) const override;
diff --git a/chrome/browser/web_applications/web_app_install_manager.cc b/chrome/browser/web_applications/web_app_install_manager.cc
index ca5bab2..f6600eb 100644
--- a/chrome/browser/web_applications/web_app_install_manager.cc
+++ b/chrome/browser/web_applications/web_app_install_manager.cc
@@ -46,7 +46,8 @@
     WebAppInstallDialogCallback dialog_callback,
     OnceInstallCallback callback) {
   auto task = std::make_unique<WebAppInstallTask>(
-      profile(), finalizer(), data_retriever_factory_.Run());
+      profile(), shortcut_manager(), finalizer(),
+      data_retriever_factory_.Run());
   task->InstallWebAppFromManifest(
       contents, install_source, std::move(dialog_callback),
       base::BindOnce(&WebAppInstallManager::OnTaskCompleted,
@@ -62,7 +63,8 @@
     WebAppInstallDialogCallback dialog_callback,
     OnceInstallCallback callback) {
   auto task = std::make_unique<WebAppInstallTask>(
-      profile(), finalizer(), data_retriever_factory_.Run());
+      profile(), shortcut_manager(), finalizer(),
+      data_retriever_factory_.Run());
   task->InstallWebAppFromManifestWithFallback(
       contents, force_shortcut_app, install_source, std::move(dialog_callback),
       base::BindOnce(&WebAppInstallManager::OnTaskCompleted,
@@ -77,7 +79,8 @@
     WebappInstallSource install_source,
     OnceInstallCallback callback) {
   auto task = std::make_unique<WebAppInstallTask>(
-      profile(), finalizer(), data_retriever_factory_.Run());
+      profile(), shortcut_manager(), finalizer(),
+      data_retriever_factory_.Run());
   task->InstallWebAppFromInfo(
       std::move(web_application_info), for_installable_site, install_source,
       base::BindOnce(&WebAppInstallManager::OnTaskCompleted,
@@ -92,7 +95,8 @@
     WebappInstallSource install_source,
     OnceInstallCallback callback) {
   auto task = std::make_unique<WebAppInstallTask>(
-      profile(), finalizer(), data_retriever_factory_.Run());
+      profile(), shortcut_manager(), finalizer(),
+      data_retriever_factory_.Run());
   task->InstallWebAppWithParams(
       web_contents, install_params, install_source,
       base::BindOnce(&WebAppInstallManager::OnTaskCompleted,
@@ -121,7 +125,8 @@
 #endif
 
   auto task = std::make_unique<WebAppInstallTask>(
-      profile(), finalizer(), data_retriever_factory_.Run());
+      profile(), shortcut_manager(), finalizer(),
+      data_retriever_factory_.Run());
 
   base::OnceClosure start_task = base::BindOnce(
       &WebAppInstallTask::InstallWebAppFromInfoRetrieveIcons,
@@ -139,7 +144,8 @@
     std::unique_ptr<WebApplicationInfo> web_application_info,
     OnceInstallCallback callback) {
   auto task = std::make_unique<WebAppInstallTask>(
-      profile(), finalizer(), data_retriever_factory_.Run());
+      profile(), shortcut_manager(), finalizer(),
+      data_retriever_factory_.Run());
 
   base::OnceClosure start_task = base::BindOnce(
       &WebAppInstallTask::UpdateWebAppFromInfo, base::Unretained(task.get()),
diff --git a/chrome/browser/web_applications/web_app_install_manager_unittest.cc b/chrome/browser/web_applications/web_app_install_manager_unittest.cc
index 930aa7e..084e7b7e 100644
--- a/chrome/browser/web_applications/web_app_install_manager_unittest.cc
+++ b/chrome/browser/web_applications/web_app_install_manager_unittest.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/web_applications/components/web_app_icon_generator.h"
 #include "chrome/browser/web_applications/components/web_app_utils.h"
 #include "chrome/browser/web_applications/test/test_app_registrar.h"
+#include "chrome/browser/web_applications/test/test_app_shortcut_manager.h"
 #include "chrome/browser/web_applications/test/test_data_retriever.h"
 #include "chrome/browser/web_applications/test/test_install_finalizer.h"
 #include "chrome/browser/web_applications/test/test_web_app_url_loader.h"
@@ -40,10 +41,13 @@
 
     registrar_ = std::make_unique<TestAppRegistrar>();
 
+    shortcut_manager_ = std::make_unique<TestAppShortcutManager>(profile());
+
     install_finalizer_ = std::make_unique<TestInstallFinalizer>();
 
     install_manager_ = std::make_unique<WebAppInstallManager>(profile());
-    install_manager_->SetSubsystems(registrar_.get(), install_finalizer_.get());
+    install_manager_->SetSubsystems(registrar_.get(), shortcut_manager_.get(),
+                                    install_finalizer_.get());
 
     auto test_url_loader = std::make_unique<TestWebAppUrlLoader>();
 
@@ -79,6 +83,7 @@
 
  private:
   std::unique_ptr<TestAppRegistrar> registrar_;
+  std::unique_ptr<TestAppShortcutManager> shortcut_manager_;
   std::unique_ptr<WebAppInstallManager> install_manager_;
   std::unique_ptr<TestInstallFinalizer> install_finalizer_;
 
diff --git a/chrome/browser/web_applications/web_app_install_task.cc b/chrome/browser/web_applications/web_app_install_task.cc
index 6118dd35..6cf12de 100644
--- a/chrome/browser/web_applications/web_app_install_task.cc
+++ b/chrome/browser/web_applications/web_app_install_task.cc
@@ -11,6 +11,7 @@
 #include "base/logging.h"
 #include "chrome/browser/installable/installable_metrics.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/web_applications/components/app_shortcut_manager.h"
 #include "chrome/browser/web_applications/components/install_bounce_metric.h"
 #include "chrome/browser/web_applications/components/install_finalizer.h"
 #include "chrome/browser/web_applications/components/web_app_constants.h"
@@ -60,9 +61,11 @@
 
 WebAppInstallTask::WebAppInstallTask(
     Profile* profile,
+    AppShortcutManager* shortcut_manager,
     InstallFinalizer* install_finalizer,
     std::unique_ptr<WebAppDataRetriever> data_retriever)
     : data_retriever_(std::move(data_retriever)),
+      shortcut_manager_(shortcut_manager),
       install_finalizer_(install_finalizer),
       profile_(profile) {}
 
@@ -541,11 +544,11 @@
       &WebAppInstallTask::OnShortcutsCreated, weak_ptr_factory_.GetWeakPtr(),
       std::move(web_app_info), app_id);
 
-  if (add_to_applications_menu && install_finalizer_->CanCreateOsShortcuts()) {
+  if (add_to_applications_menu && shortcut_manager_->CanCreateShortcuts()) {
     // TODO(ortuno): Make adding a shortcut to the applications menu independent
     // from adding a shortcut to desktop.
-    install_finalizer_->CreateOsShortcuts(app_id, add_to_desktop,
-                                          std::move(create_shortcuts_callback));
+    shortcut_manager_->CreateShortcuts(app_id, add_to_desktop,
+                                       std::move(create_shortcuts_callback));
   } else {
     std::move(create_shortcuts_callback).Run(false /* created_shortcuts */);
   }
diff --git a/chrome/browser/web_applications/web_app_install_task.h b/chrome/browser/web_applications/web_app_install_task.h
index 8c38d1ae..276d5430 100644
--- a/chrome/browser/web_applications/web_app_install_task.h
+++ b/chrome/browser/web_applications/web_app_install_task.h
@@ -32,12 +32,14 @@
 
 namespace web_app {
 
+class AppShortcutManager;
 class InstallFinalizer;
 class WebAppDataRetriever;
 
 class WebAppInstallTask : content::WebContentsObserver {
  public:
   WebAppInstallTask(Profile* profile,
+                    AppShortcutManager* shortcut_manager,
                     InstallFinalizer* install_finalizer,
                     std::unique_ptr<WebAppDataRetriever> data_retriever);
   ~WebAppInstallTask() override;
@@ -180,6 +182,7 @@
 
   std::unique_ptr<WebAppDataRetriever> data_retriever_;
 
+  AppShortcutManager* shortcut_manager_;
   InstallFinalizer* install_finalizer_;
   Profile* const profile_;
 
diff --git a/chrome/browser/web_applications/web_app_install_task_unittest.cc b/chrome/browser/web_applications/web_app_install_task_unittest.cc
index 3fe7279e..c43131a 100644
--- a/chrome/browser/web_applications/web_app_install_task_unittest.cc
+++ b/chrome/browser/web_applications/web_app_install_task_unittest.cc
@@ -22,6 +22,7 @@
 #include "chrome/browser/web_applications/components/web_app_constants.h"
 #include "chrome/browser/web_applications/components/web_app_icon_generator.h"
 #include "chrome/browser/web_applications/components/web_app_utils.h"
+#include "chrome/browser/web_applications/test/test_app_shortcut_manager.h"
 #include "chrome/browser/web_applications/test/test_data_retriever.h"
 #include "chrome/browser/web_applications/test/test_file_utils.h"
 #include "chrome/browser/web_applications/test/test_install_finalizer.h"
@@ -133,13 +134,17 @@
 
     install_finalizer_ = std::make_unique<WebAppInstallFinalizer>(
         &controller().sync_bridge(), icon_manager_.get());
+    shortcut_manager_ = std::make_unique<TestAppShortcutManager>(profile());
+
     install_finalizer_->SetSubsystems(&registrar(), ui_manager_.get());
+    shortcut_manager_->SetSubsystems(&registrar());
 
     auto data_retriever = std::make_unique<TestDataRetriever>();
     data_retriever_ = data_retriever.get();
 
     install_task_ = std::make_unique<WebAppInstallTask>(
-        profile(), install_finalizer_.get(), std::move(data_retriever));
+        profile(), shortcut_manager_.get(), install_finalizer_.get(),
+        std::move(data_retriever));
 
     controller().Init();
 
@@ -172,6 +177,7 @@
     install_finalizer_.reset();
     ui_manager_.reset();
     icon_manager_.reset();
+    shortcut_manager_.reset();
 
     test_registry_controller_.reset();
 
@@ -330,11 +336,13 @@
   }
 
   WebAppRegistrar& registrar() { return controller().registrar(); }
+  TestAppShortcutManager& test_shortcut_manager() { return *shortcut_manager_; }
 
   std::unique_ptr<WebAppIconManager> icon_manager_;
   std::unique_ptr<WebAppInstallTask> install_task_;
   std::unique_ptr<TestWebAppUiManager> ui_manager_;
   std::unique_ptr<InstallFinalizer> install_finalizer_;
+  std::unique_ptr<TestAppShortcutManager> shortcut_manager_;
 
   // Owned by install_task_:
   TestFileUtils* file_utils_ = nullptr;
@@ -741,7 +749,7 @@
 
   InstallWebAppFromManifestWithFallback();
 
-  EXPECT_EQ(1, test_install_finalizer().num_create_os_shortcuts_calls());
+  EXPECT_EQ(1u, test_shortcut_manager().num_create_shortcuts_calls());
   EXPECT_EQ(1, test_install_finalizer().num_reparent_tab_calls());
   EXPECT_EQ(1, test_install_finalizer().num_reveal_appshim_calls());
   EXPECT_EQ(1,
@@ -758,7 +766,7 @@
   EXPECT_TRUE(result.app_id.empty());
   EXPECT_EQ(InstallResultCode::kFailedUnknownReason, result.code);
 
-  EXPECT_EQ(0, test_install_finalizer().num_create_os_shortcuts_calls());
+  EXPECT_EQ(0u, test_shortcut_manager().num_create_shortcuts_calls());
   EXPECT_EQ(0, test_install_finalizer().num_reparent_tab_calls());
   EXPECT_EQ(0, test_install_finalizer().num_reveal_appshim_calls());
   EXPECT_EQ(0,
@@ -1080,7 +1088,8 @@
                                              /*scope=*/GURL{});
 
   auto install_task = std::make_unique<WebAppInstallTask>(
-      guest_profile, install_finalizer_.get(), std::move(data_retriever));
+      guest_profile, shortcut_manager_.get(), install_finalizer_.get(),
+      std::move(data_retriever));
 
   base::RunLoop run_loop;
   install_task->InstallWebAppWithParams(
diff --git a/chrome/browser/web_applications/web_app_provider.cc b/chrome/browser/web_applications/web_app_provider.cc
index 65d4719a..86545bf 100644
--- a/chrome/browser/web_applications/web_app_provider.cc
+++ b/chrome/browser/web_applications/web_app_provider.cc
@@ -214,7 +214,8 @@
   DCHECK(!started_);
 
   install_finalizer_->SetSubsystems(registrar_.get(), ui_manager_.get());
-  install_manager_->SetSubsystems(registrar_.get(), install_finalizer_.get());
+  install_manager_->SetSubsystems(registrar_.get(), shortcut_manager_.get(),
+                                  install_finalizer_.get());
   manifest_update_manager_->SetSubsystems(registrar_.get(), ui_manager_.get(),
                                           install_manager_.get());
   pending_app_manager_->SetSubsystems(registrar_.get(), ui_manager_.get(),
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index df65c42..e3e1bd8 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -704,13 +704,6 @@
 extern const base::Feature kSplitAuthCacheByNetworkIsolationKey{
     "SplitAuthCacheByNetworkIsolationKey", base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Enables filtering URLs by suffix to include subresources that look
-// like image resources for compression. For example,
-// http://chromium.org/image.jpg would be included.
-const base::Feature kSubresourceRedirectIncludedMediaSuffixes{
-    "SubresourceRedirectIncludedMediaSuffixes",
-    base::FEATURE_ENABLED_BY_DEFAULT};
-
 #if !defined(OS_ANDROID)
 // Enables or disables the Javascript API to propagate sync encryption keys.
 const base::Feature kSyncEncryptionKeysWebApi{
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index c428e4a6..f7760ffc 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -425,9 +425,6 @@
 COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kSplitAuthCacheByNetworkIsolationKey;
 
-COMPONENT_EXPORT(CHROME_FEATURES)
-extern const base::Feature kSubresourceRedirectIncludedMediaSuffixes;
-
 #if !defined(OS_ANDROID)
 COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kSyncEncryptionKeysWebApi;
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index 1ef8d05..2a6fae06 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -39,7 +39,6 @@
 #include "chrome/common/secure_origin_whitelist.h"
 #include "chrome/common/thread_profiler.h"
 #include "chrome/common/url_constants.h"
-#include "chrome/common/webui_url_constants.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/grit/renderer_resources.h"
@@ -1572,14 +1571,5 @@
 }
 
 bool ChromeContentRendererClient::RequiresHtmlImports(const GURL& url) {
-  // Chrome Web UI pages are in the process of being migrated to use the HTML
-  // Imports Polyfill so that they will not require native imports. Return true
-  // for only pages that have not been updated yet. See
-  // https://crbug.com/937747.
-  bool can_use_polyfill = url.host() == chrome::kChromeUIExtensionsHost ||
-                          url.host() == chrome::kChromeUIDownloadsHost;
-#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
-  can_use_polyfill |= url.host() == chrome::kChromeUIPrintHost;
-#endif
-  return url.SchemeIs(content::kChromeUIScheme) && !can_use_polyfill;
+  return url.SchemeIs(content::kChromeUIScheme);
 }
diff --git a/chrome/renderer/subresource_redirect/subresource_redirect_experiments.cc b/chrome/renderer/subresource_redirect/subresource_redirect_experiments.cc
index fca2f593..63ed54ed 100644
--- a/chrome/renderer/subresource_redirect/subresource_redirect_experiments.cc
+++ b/chrome/renderer/subresource_redirect/subresource_redirect_experiments.cc
@@ -8,21 +8,16 @@
 #include "base/metrics/field_trial.h"
 #include "base/metrics/field_trial_params.h"
 #include "base/strings/string_split.h"
-#include "chrome/common/chrome_features.h"
+#include "third_party/blink/public/common/features.h"
 
 namespace subresource_redirect {
 
 bool ShouldIncludeMediaSuffix(const GURL& url) {
-  if (!base::FeatureList::IsEnabled(
-          features::kSubresourceRedirectIncludedMediaSuffixes))
-    return true;
-
   std::vector<std::string> suffixes = {".jpg", ".jpeg", ".png", ".svg",
                                        ".webp"};
 
   std::string csv = base::GetFieldTrialParamValueByFeature(
-      features::kSubresourceRedirectIncludedMediaSuffixes,
-      "included_path_suffixes");
+      blink::features::kSubresourceRedirect, "included_path_suffixes");
   if (csv != "") {
     suffixes = base::SplitString(csv, ",", base::TRIM_WHITESPACE,
                                  base::SPLIT_WANT_NONEMPTY);
diff --git a/chrome/renderer/subresource_redirect/subresource_redirect_experiments_unittest.cc b/chrome/renderer/subresource_redirect/subresource_redirect_experiments_unittest.cc
index 99c66e1..70ae3ce 100644
--- a/chrome/renderer/subresource_redirect/subresource_redirect_experiments_unittest.cc
+++ b/chrome/renderer/subresource_redirect/subresource_redirect_experiments_unittest.cc
@@ -5,8 +5,8 @@
 #include "chrome/renderer/subresource_redirect/subresource_redirect_experiments.h"
 
 #include "base/test/scoped_feature_list.h"
-#include "chrome/common/chrome_features.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/features.h"
 
 namespace subresource_redirect {
 
@@ -15,7 +15,7 @@
 TEST(SubresourceRedirectExperimentsTest, TestDefaultShouldIncludeMediaSuffix) {
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndEnableFeature(
-      features::kSubresourceRedirectIncludedMediaSuffixes);
+      blink::features::kSubresourceRedirect);
 
   EXPECT_FALSE(ShouldIncludeMediaSuffix(GURL("http://chromium.org/path/")));
 
@@ -30,22 +30,13 @@
 TEST(SubresourceRedirectExperimentsTest, TestShouldIncludeMediaSuffix) {
   struct TestCase {
     std::string msg;
-    bool enable_feature;
     std::string varaiation_value;
     std::vector<std::string> urls;
     bool want_return;
   };
   const TestCase kTestCases[]{
       {
-          .msg = "Feature disabled, should always return true",
-          .enable_feature = false,
-          .varaiation_value = "",
-          .urls = {"http://chromium.org/image.jpg"},
-          .want_return = true,
-      },
-      {
           .msg = "Default values are overridden by variations",
-          .enable_feature = true,
           .varaiation_value = ".html",
           .urls = {"http://chromium.org/image.jpeg",
                    "http://chromium.org/image.png",
@@ -55,7 +46,6 @@
       },
       {
           .msg = "Variation value whitespace should be trimmed",
-          .enable_feature = true,
           .varaiation_value = " .svg , \t .png\n",
           .urls = {"http://chromium.org/image.svg",
                    "http://chromium.org/image.png"},
@@ -63,7 +53,6 @@
       },
       {
           .msg = "Variation value empty values should be excluded",
-          .enable_feature = true,
           .varaiation_value = ".svg,,.png,",
           .urls = {"http://chromium.org/image.svg",
                    "http://chromium.org/image.png"},
@@ -71,7 +60,6 @@
       },
       {
           .msg = "URLs should be compared case insensitive",
-          .enable_feature = true,
           .varaiation_value = ".svg,.png,",
           .urls = {"http://chromium.org/image.SvG",
                    "http://chromium.org/image.PNG"},
@@ -79,7 +67,6 @@
       },
       {
           .msg = "Query params and fragments don't matter",
-          .enable_feature = true,
           .varaiation_value = ".svg,.png,",
           .urls = {"http://chromium.org/image.svg?hello=world",
                    "http://chromium.org/image.png#test"},
@@ -87,7 +74,6 @@
       },
       {
           .msg = "Query params and fragments shouldn't be considered",
-          .enable_feature = true,
           .varaiation_value = ".svg,.png,",
           .urls = {"http://chromium.org/?image=image.svg",
                    "http://chromium.org/#image.png"},
@@ -98,14 +84,9 @@
     SCOPED_TRACE(test_case.msg);
 
     base::test::ScopedFeatureList scoped_feature_list;
-    if (test_case.enable_feature) {
-      scoped_feature_list.InitAndEnableFeatureWithParameters(
-          features::kSubresourceRedirectIncludedMediaSuffixes,
-          {{"included_path_suffixes", test_case.varaiation_value}});
-    } else {
-      scoped_feature_list.InitAndDisableFeature(
-          features::kSubresourceRedirectIncludedMediaSuffixes);
-    }
+    scoped_feature_list.InitAndEnableFeatureWithParameters(
+        blink::features::kSubresourceRedirect,
+        {{"included_path_suffixes", test_case.varaiation_value}});
 
     for (const std::string& url : test_case.urls) {
       EXPECT_EQ(test_case.want_return, ShouldIncludeMediaSuffix(GURL(url)));
diff --git a/chrome/renderer/subresource_redirect/subresource_redirect_params.cc b/chrome/renderer/subresource_redirect/subresource_redirect_params.cc
index a88bd5ce..0bb775ea8 100644
--- a/chrome/renderer/subresource_redirect/subresource_redirect_params.cc
+++ b/chrome/renderer/subresource_redirect/subresource_redirect_params.cc
@@ -5,22 +5,19 @@
 #include "chrome/renderer/subresource_redirect/subresource_redirect_params.h"
 
 #include "base/command_line.h"
-#include "content/public/common/content_switches.h"
+#include "base/metrics/field_trial_params.h"
+#include "chrome/common/chrome_features.h"
+#include "third_party/blink/public/common/features.h"
+#include "url/gurl.h"
 
 namespace subresource_redirect {
 
-bool ShouldForceEnableSubresourceRedirect() {
-  return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kEnableSubresourceRedirect);
-}
-
-std::string LitePageSubresourceHost() {
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kLitePagesServerSubresourceHost)) {
-    return base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
-        switches::kLitePagesServerSubresourceHost);
-  }
-  return "https://litepages.googlezip.net/";
+url::Origin GetSubresourceRedirectOrigin() {
+  auto lite_page_subresource_origin = base::GetFieldTrialParamValueByFeature(
+      blink::features::kSubresourceRedirect, "lite_page_subresource_origin");
+  if (lite_page_subresource_origin.empty())
+    return url::Origin::Create(GURL("https://litepages.googlezip.net/"));
+  return url::Origin::Create(GURL(lite_page_subresource_origin));
 }
 
 }  // namespace subresource_redirect
diff --git a/chrome/renderer/subresource_redirect/subresource_redirect_params.h b/chrome/renderer/subresource_redirect/subresource_redirect_params.h
index b2555848..8c14e7b 100644
--- a/chrome/renderer/subresource_redirect/subresource_redirect_params.h
+++ b/chrome/renderer/subresource_redirect/subresource_redirect_params.h
@@ -7,14 +7,13 @@
 
 #include <string>
 
+#include "url/origin.h"
+
 namespace subresource_redirect {
 
-// Returns true if Subresource Redirect is forced to be enabled from the
-// command line.
-bool ShouldForceEnableSubresourceRedirect();
-
-// Returns kLitePagesServerSubresourceHost if set, else returns the default.
-std::string LitePageSubresourceHost();
+// Returns the origin to use for subresource redirect from fieldtrial or the
+// default.
+url::Origin GetSubresourceRedirectOrigin();
 
 }  // namespace subresource_redirect
 
diff --git a/chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle.cc b/chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle.cc
index 1444e04..1600fc6 100644
--- a/chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle.cc
+++ b/chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle.cc
@@ -6,16 +6,39 @@
 
 #include "base/metrics/histogram_macros.h"
 #include "chrome/renderer/subresource_redirect/subresource_redirect_experiments.h"
+#include "chrome/renderer/subresource_redirect/subresource_redirect_params.h"
 #include "chrome/renderer/subresource_redirect/subresource_redirect_util.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
+#include "content/public/common/previews_state.h"
 #include "content/public/common/resource_type.h"
 #include "net/base/escape.h"
 #include "net/base/load_flags.h"
 #include "net/http/http_status_code.h"
 #include "services/network/public/cpp/resource_response.h"
+#include "third_party/blink/public/common/features.h"
+#include "third_party/blink/public/platform/web_url.h"
+#include "third_party/blink/public/platform/web_url_request.h"
 
 namespace subresource_redirect {
 
+// static
+std::unique_ptr<SubresourceRedirectURLLoaderThrottle>
+SubresourceRedirectURLLoaderThrottle::MaybeCreateThrottle(
+    const blink::WebURLRequest& request,
+    content::ResourceType resource_type) {
+  if (base::FeatureList::IsEnabled(blink::features::kSubresourceRedirect) &&
+      resource_type == content::ResourceType::kImage &&
+      (request.GetPreviewsState() &
+       blink::WebURLRequest::kSubresourceRedirectOn) &&
+      request.Url().ProtocolIs(url::kHttpsScheme)) {
+    // TODO(rajendrant): Verify that data saver is enabled as well, to not
+    // trigger the subresource redirect for incognito profiles.
+    return base::WrapUnique<SubresourceRedirectURLLoaderThrottle>(
+        new SubresourceRedirectURLLoaderThrottle());
+  }
+  return nullptr;
+}
+
 SubresourceRedirectURLLoaderThrottle::SubresourceRedirectURLLoaderThrottle() =
     default;
 SubresourceRedirectURLLoaderThrottle::~SubresourceRedirectURLLoaderThrottle() =
@@ -24,9 +47,11 @@
 void SubresourceRedirectURLLoaderThrottle::WillStartRequest(
     network::ResourceRequest* request,
     bool* defer) {
+  DCHECK(base::FeatureList::IsEnabled(blink::features::kSubresourceRedirect));
   DCHECK_EQ(request->resource_type,
             static_cast<int>(content::ResourceType::kImage));
-
+  DCHECK(request->previews_state &
+         content::PreviewsTypes::SUBRESOURCE_REDIRECT_ON);
   DCHECK(request->url.SchemeIs(url::kHttpsScheme));
 
   // Image subresources that have paths that do not end in one of the
@@ -58,10 +83,10 @@
   // If response was not from the compression server, don't restart it.
   if (!response_url.is_valid())
     return;
-  GURL compression_server = GetLitePageSubresourceDomainURL();
+  auto compression_server = GetSubresourceRedirectOrigin();
   if (!response_url.DomainIs(compression_server.host()))
     return;
-  if (response_url.EffectiveIntPort() != compression_server.EffectiveIntPort())
+  if (response_url.EffectiveIntPort() != compression_server.port())
     return;
   if (response_url.scheme() != compression_server.scheme())
     return;
@@ -92,10 +117,10 @@
   // If response was not from the compression server, don't record any metrics.
   if (!response_url.is_valid())
     return;
-  GURL compression_server = GetLitePageSubresourceDomainURL();
+  auto compression_server = GetSubresourceRedirectOrigin();
   if (!response_url.DomainIs(compression_server.host()))
     return;
-  if (response_url.EffectiveIntPort() != compression_server.EffectiveIntPort())
+  if (response_url.EffectiveIntPort() != compression_server.port())
     return;
   if (response_url.scheme() != compression_server.scheme())
     return;
diff --git a/chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle.h b/chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle.h
index 50cf816c..e4df33e 100644
--- a/chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle.h
+++ b/chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle.h
@@ -5,15 +5,24 @@
 #ifndef CHROME_RENDERER_SUBRESOURCE_REDIRECT_SUBRESOURCE_REDIRECT_URL_LOADER_THROTTLE_H_
 #define CHROME_RENDERER_SUBRESOURCE_REDIRECT_SUBRESOURCE_REDIRECT_URL_LOADER_THROTTLE_H_
 
+#include "base/macros.h"
+#include "content/public/common/resource_type.h"
 #include "third_party/blink/public/common/loader/url_loader_throttle.h"
 
+namespace blink {
+class WebURLRequest;
+}  // namespace blink
+
 namespace subresource_redirect {
 
 // This class handles internal redirects for subresouces on HTTPS sites to
 // compressed versions of subresources.
 class SubresourceRedirectURLLoaderThrottle : public blink::URLLoaderThrottle {
  public:
-  SubresourceRedirectURLLoaderThrottle();
+  static std::unique_ptr<SubresourceRedirectURLLoaderThrottle>
+  MaybeCreateThrottle(const blink::WebURLRequest& request,
+                      content::ResourceType resource_type);
+
   ~SubresourceRedirectURLLoaderThrottle() override;
 
   // blink::URLLoaderThrottle:
@@ -36,6 +45,10 @@
                                bool* defer) override;
   // Overridden to do nothing as the default implementation is NOT_REACHED()
   void DetachFromCurrentSequence() override;
+
+ private:
+  SubresourceRedirectURLLoaderThrottle();
+  DISALLOW_COPY_AND_ASSIGN(SubresourceRedirectURLLoaderThrottle);
 };
 
 }  // namespace subresource_redirect
diff --git a/chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle_unittest.cc b/chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle_unittest.cc
index a45fa3a7..d2d7b99 100644
--- a/chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle_unittest.cc
+++ b/chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle_unittest.cc
@@ -4,42 +4,117 @@
 
 #include "chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle.h"
 
+#include "base/test/scoped_feature_list.h"
 #include "chrome/renderer/subresource_redirect/subresource_redirect_util.h"
+#include "content/public/common/previews_state.h"
 #include "content/public/common/resource_type.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/features.h"
+#include "third_party/blink/public/platform/web_url.h"
+#include "third_party/blink/public/platform/web_url_request.h"
 
 namespace subresource_redirect {
 
 namespace {
 
+std::unique_ptr<SubresourceRedirectURLLoaderThrottle>
+CreateSubresourceRedirectURLLoaderThrottle(const GURL& url,
+                                           content::ResourceType resource_type,
+                                           int previews_state) {
+  blink::WebURLRequest request;
+  request.SetUrl(url);
+  request.SetPreviewsState(previews_state);
+  return SubresourceRedirectURLLoaderThrottle::MaybeCreateThrottle(
+      request, resource_type);
+}
+
+TEST(SubresourceRedirectURLLoaderThrottleTest, TestMaybeCreateThrottle) {
+  struct TestCase {
+    bool is_subresource_redirect_feature_enabled;
+    content::ResourceType resource_type;
+    int previews_state;
+    const std::string url;
+    bool expected_is_throttle_created;
+  };
+
+  const TestCase kTestCases[]{
+      {true, content::ResourceType::kImage,
+       content::PreviewsTypes::SUBRESOURCE_REDIRECT_ON,
+       "https://www.test.com/test.jpg", true},
+      {true, content::ResourceType::kImage,
+       content::PreviewsTypes::SUBRESOURCE_REDIRECT_ON |
+           content::PreviewsTypes::LAZY_IMAGE_AUTO_RELOAD,
+       "https://www.test.com/test.jpg", true},
+
+      // Failure cases
+      {false, content::ResourceType::kImage,
+       content::PreviewsTypes::SUBRESOURCE_REDIRECT_ON,
+       "https://www.test.com/test.jpg", false},
+      {true, content::ResourceType::kScript,
+       content::PreviewsTypes::SUBRESOURCE_REDIRECT_ON,
+       "https://www.test.com/test.jpg", false},
+      {true, content::ResourceType::kImage,
+       content::PreviewsTypes::LAZY_IMAGE_AUTO_RELOAD,
+       "https://www.test.com/test.jpg", false},
+      {true, content::ResourceType::kImage,
+       content::PreviewsTypes::SUBRESOURCE_REDIRECT_ON,
+       "http://www.test.com/test.jpg", false},
+  };
+
+  for (const TestCase& test_case : kTestCases) {
+    base::test::ScopedFeatureList scoped_feature_list;
+    if (test_case.is_subresource_redirect_feature_enabled) {
+      scoped_feature_list.InitAndEnableFeature(
+          blink::features::kSubresourceRedirect);
+    } else {
+      scoped_feature_list.InitAndDisableFeature(
+          blink::features::kSubresourceRedirect);
+    }
+
+    blink::WebURLRequest request;
+    request.SetPreviewsState(test_case.previews_state);
+    request.SetUrl(GURL(test_case.url));
+    EXPECT_EQ(test_case.expected_is_throttle_created,
+              SubresourceRedirectURLLoaderThrottle::MaybeCreateThrottle(
+                  request, test_case.resource_type) != nullptr);
+  }
+}
+
 TEST(SubresourceRedirectURLLoaderThrottleTest, TestGetSubresourceURL) {
   struct TestCase {
     GURL original_url;
-    int resource_type;
     GURL redirected_subresource_url;
   };
 
   const TestCase kTestCases[]{
       {
           GURL("https://www.test.com/test.jpg"),
-          static_cast<int>(content::ResourceType::kImage),
           GetSubresourceURLForURL(GURL("https://www.test.com/test.jpg")),
       },
       {
           GURL("https://www.test.com/test.jpg#test"),
-          static_cast<int>(content::ResourceType::kImage),
           GetSubresourceURLForURL(GURL("https://www.test.com/test.jpg#test")),
       },
+      {
+          GURL("https://www.test.com/test.jpg?foo=bar&baz"),
+          GetSubresourceURLForURL(
+              GURL("https://www.test.com/test.jpg?foo=bar&baz")),
+      },
   };
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      blink::features::kSubresourceRedirect);
 
   for (const TestCase& test_case : kTestCases) {
+    auto throttle = CreateSubresourceRedirectURLLoaderThrottle(
+        test_case.original_url, content::ResourceType::kImage,
+        content::PreviewsTypes::SUBRESOURCE_REDIRECT_ON);
     network::ResourceRequest request;
     request.url = test_case.original_url;
-    request.resource_type = test_case.resource_type;
+    request.resource_type = static_cast<int>(content::ResourceType::kImage);
+    request.previews_state = content::PreviewsTypes::SUBRESOURCE_REDIRECT_ON;
     bool defer = false;
-
-    SubresourceRedirectURLLoaderThrottle throttle;
-    throttle.WillStartRequest(&request, &defer);
+    throttle->WillStartRequest(&request, &defer);
 
     EXPECT_FALSE(defer);
     EXPECT_EQ(request.url, test_case.redirected_subresource_url);
@@ -47,14 +122,20 @@
 }
 
 TEST(SubresourceRedirectURLLoaderThrottleTest, DeferOverridenToFalse) {
-  SubresourceRedirectURLLoaderThrottle throttle;
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      blink::features::kSubresourceRedirect);
 
+  auto throttle = CreateSubresourceRedirectURLLoaderThrottle(
+      GURL("https://www.test.com/test.jpg"), content::ResourceType::kImage,
+      content::PreviewsTypes::SUBRESOURCE_REDIRECT_ON);
   network::ResourceRequest request;
   request.url = GURL("https://www.test.com/test.jpg");
   request.resource_type = static_cast<int>(content::ResourceType::kImage);
+  request.previews_state = content::PreviewsTypes::SUBRESOURCE_REDIRECT_ON;
   bool defer = true;
 
-  throttle.WillStartRequest(&request, &defer);
+  throttle->WillStartRequest(&request, &defer);
   EXPECT_FALSE(defer);
 }
 
diff --git a/chrome/renderer/subresource_redirect/subresource_redirect_util.cc b/chrome/renderer/subresource_redirect/subresource_redirect_util.cc
index 5423c4f..67caf0ba 100644
--- a/chrome/renderer/subresource_redirect/subresource_redirect_util.cc
+++ b/chrome/renderer/subresource_redirect/subresource_redirect_util.cc
@@ -6,8 +6,8 @@
 
 #include "base/command_line.h"
 #include "base/strings/string_number_conversions.h"
+#include "chrome/renderer/subresource_redirect/subresource_redirect_params.h"
 #include "components/base32/base32.h"
-#include "content/public/common/content_switches.h"
 #include "crypto/sha2.h"
 #include "net/base/escape.h"
 #include "url/gurl.h"
@@ -16,48 +16,29 @@
 
 GURL GetSubresourceURLForURL(const GURL& original_url) {
   DCHECK(original_url.is_valid());
-  std::string fragment;
-  if (original_url.has_ref()) {
-    fragment = "#" + original_url.ref();
-  }
 
+  GURL compressed_url = GetSubresourceRedirectOrigin().GetURL();
   std::string origin_hash = base::ToLowerASCII(base32::Base32Encode(
       crypto::SHA256HashString(
           original_url.scheme() + "://" + original_url.host() + ":" +
           base::NumberToString(original_url.EffectiveIntPort())),
       base32::Base32EncodePolicy::OMIT_PADDING));
-  GURL subresource_host = GetLitePageSubresourceDomainURL();
+  std::string host_str = origin_hash + "." + compressed_url.host();
+  std::string query_str =
+      "u=" + net::EscapeQueryParamValue(original_url.GetAsReferrer().spec(),
+                                        true /* use_plus */);
+  std::string ref_str = original_url.ref();
 
-  GURL compressed_url(
-      subresource_host.scheme() + "://" + origin_hash + "." +
-      subresource_host.host() +
-      (subresource_host.has_port() ? (":" + subresource_host.port()) : "") +
-      "/i?u=" +
-      // Strip out the fragment so that it is not sent to the server.
-      net::EscapeQueryParamValue(original_url.GetAsReferrer().spec(),
-                                 true /* use_plus */) +
-      fragment);
+  GURL::Replacements replacements;
+  replacements.SetHostStr(host_str);
+  replacements.SetPathStr("/i");
+  replacements.SetQueryStr(query_str);
+  if (!ref_str.empty())
+    replacements.SetRefStr(ref_str);
 
+  compressed_url = compressed_url.ReplaceComponents(replacements);
   DCHECK(compressed_url.is_valid());
-  DCHECK_EQ(subresource_host.scheme(), compressed_url.scheme());
   return compressed_url;
 }
 
-GURL GetLitePageSubresourceDomainURL() {
-  // Command line options take highest precedence.
-  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  if (command_line->HasSwitch(switches::kLitePagesServerSubresourceHost)) {
-    const std::string switch_value = command_line->GetSwitchValueASCII(
-        switches::kLitePagesServerSubresourceHost);
-    const GURL url(switch_value);
-    if (url.is_valid())
-      return url;
-    LOG(ERROR) << "The following litepages previews host URL specified at the "
-               << "command-line is invalid: " << switch_value;
-  }
-
-  // No override use the default litepages domain.
-  return GURL("https://litepages.googlezip.net/");
-}
-
 }  // namespace subresource_redirect
diff --git a/chrome/renderer/subresource_redirect/subresource_redirect_util.h b/chrome/renderer/subresource_redirect/subresource_redirect_util.h
index 65be06e..fb41647 100644
--- a/chrome/renderer/subresource_redirect/subresource_redirect_util.h
+++ b/chrome/renderer/subresource_redirect/subresource_redirect_util.h
@@ -13,9 +13,6 @@
 // internal redirects.
 GURL GetSubresourceURLForURL(const GURL& original_url);
 
-// Gets the URL for the compression server.
-GURL GetLitePageSubresourceDomainURL();
-
 }  // namespace subresource_redirect
 
 #endif  // CHROME_RENDERER_SUBRESOURCE_REDIRECT_SUBRESOURCE_REDIRECT_UTIL_H_
diff --git a/chrome/renderer/url_loader_throttle_provider_impl.cc b/chrome/renderer/url_loader_throttle_provider_impl.cc
index 1642d5d..73850aab 100644
--- a/chrome/renderer/url_loader_throttle_provider_impl.cc
+++ b/chrome/renderer/url_loader_throttle_provider_impl.cc
@@ -258,13 +258,10 @@
           ->chromeos_listener()));
 #endif  // defined(OS_CHROMEOS)
 
-  if (subresource_redirect::ShouldForceEnableSubresourceRedirect() &&
-      resource_type == content::ResourceType::kImage &&
-      GURL(request.Url()).SchemeIs(url::kHttpsScheme)) {
-    throttles.push_back(
-        std::make_unique<
-            subresource_redirect::SubresourceRedirectURLLoaderThrottle>());
-  }
+  auto throttle = subresource_redirect::SubresourceRedirectURLLoaderThrottle::
+      MaybeCreateThrottle(request, resource_type);
+  if (throttle)
+    throttles.push_back(std::move(throttle));
 
   return throttles;
 }
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 1df604d..dbd47e20 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -977,6 +977,7 @@
       "../browser/metrics/ukm_browsertest.cc",
       "../browser/metrics/variations/force_field_trials_browsertest.cc",
       "../browser/navigation_predictor/navigation_predictor_browsertest.cc",
+      "../browser/navigation_predictor/navigation_predictor_preconnect_client_browsertest.cc",
       "../browser/net/chrome_accept_header_browsertest.cc",
       "../browser/net/chrome_mojo_proxy_resolver_factory_browsertest.cc",
       "../browser/net/chrome_network_delegate_browsertest.cc",
diff --git a/chrome/test/base/in_process_browser_test.cc b/chrome/test/base/in_process_browser_test.cc
index dfec8850..53bcdcd 100644
--- a/chrome/test/base/in_process_browser_test.cc
+++ b/chrome/test/base/in_process_browser_test.cc
@@ -257,10 +257,14 @@
 
   SetScreenInstance();
 
-  // Always use a mocked password storage if OS encryption is used (which is
-  // when anything sensitive gets stored, including Cookies). Without this on
-  // Mac, many tests will hang waiting for a user to approve KeyChain access.
+  // Use a mocked password storage if OS encryption is used that might block or
+  // prompt the user (which is when anything sensitive gets stored, including
+  // Cookies). Without this on Mac and Linux, many tests will hang waiting for a
+  // user to approve KeyChain/kwallet access. On Windows this is not needed as
+  // OS APIs never block.
+#if defined(OS_MACOSX) || defined(OS_LINUX)
   OSCryptMocker::SetUp();
+#endif
 
 #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
   CaptivePortalService::set_state_for_testing(
@@ -322,7 +326,9 @@
           ->Skipped())
     return;
   BrowserTestBase::TearDown();
+#if defined(OS_MACOSX) || defined(OS_LINUX)
   OSCryptMocker::TearDown();
+#endif
   ChromeContentBrowserClient::SetDefaultQuotaSettingsForTesting(nullptr);
 
 #if defined(OS_CHROMEOS)
diff --git a/chrome/test/data/extensions/api_test/webrequest/test_cors.js b/chrome/test/data/extensions/api_test/webrequest/test_cors.js
index b6e3fb7..f52fac98 100644
--- a/chrome/test/data/extensions/api_test/webrequest/test_cors.js
+++ b/chrome/test/data/extensions/api_test/webrequest/test_cors.js
@@ -517,11 +517,6 @@
 function registerPreflightRedirectingListener() {
   const url = getServerURL(BASE + 'accept', 'cors.example.com');
 
-  // This is needed to observe preflight requests when OOR-CORS is enabled.
-  chrome.webRequest.onHeadersReceived.addListener(
-      () => {},
-      {urls: [url]}, ['extraHeaders']);
-
   const onBeforeRequestCalledForPreflight = callbackPass(() => {});
   chrome.webRequest.onBeforeRequest.addListener(
       function onBeforeRequest(details) {
@@ -533,7 +528,7 @@
           }, 0);
           return {redirectUrl: url + '?redirected'};
         }
-      }, {urls: [url]}, ['blocking']);
+      }, {urls: [url]}, ['blocking', 'extraHeaders']);
 
   if (getCorsMode() == 'network_service') {
     // When CORS is implemented in the network service, we see failures on both
@@ -560,6 +555,23 @@
   }
 }
 
+function registerOnBeforeRequestAndOnErrorOcurredListeners() {
+  const url = getServerURL(BASE + 'accept', 'cors.example.com');
+
+  const onBeforeRequestCalledForPreflight = callbackPass(() => {});
+  // onBeforeRequest doesn't have "extraHeaders", but it sees a preflight
+  // even when OOR-CORS is enabled, because onErrorOccurred has "extraHeaders".
+  chrome.webRequest.onBeforeRequest.addListener((details) => {
+    if (details.method === 'OPTIONS') {
+      onBeforeRequestCalledForPreflight();
+    }
+  }, {urls: [url]});
+
+  chrome.webRequest.onErrorOccurred.addListener(() => {
+  }, {urls: [url]}, ['extraHeaders']);
+}
+
+
 runTests([
   function testOriginHeader() {
     // Register two sets of listener. One with extraHeaders and the second one
@@ -636,4 +648,9 @@
     navigateAndWait(getServerURL(
         BASE + 'fetch.html?path=accept&with-preflight'));
   },
+  function testCorsPreflightIsObservableWhenAnyListenerHasExtraHeaders() {
+    registerOnBeforeRequestAndOnErrorOcurredListeners();
+    navigateAndWait(getServerURL(
+        BASE + 'fetch.html?path=accept&with-preflight'));
+  }
 ]);
diff --git a/chrome/test/data/load_image/private_url_image_js.html b/chrome/test/data/load_image/private_url_image_js.html
deleted file mode 100644
index a1232a7..0000000
--- a/chrome/test/data/load_image/private_url_image_js.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<html>
-<body>
-<div> div tag </div>
-<script>
-	var img = document.createElement("img");
-	img.src="private_url_image.png";
-	document.getElementsByTagName('div')[0].appendChild(img);
-
-	function checkImage() {
-		sendValueToTest((document.images[0].complete && (document.images[0].naturalWidth > 0)));
-	}
-
-	function imageSrc() {
-		sendValueToTest(document.images[0].src);
-	}
-
-	function sendValueToTest(value) {
-	  window.domAutomationController.send(value);
-	}
-</script>
-</body>
-</html>
\ No newline at end of file
diff --git a/chrome/test/data/pdf/basic_test.js b/chrome/test/data/pdf/basic_test.js
index 7f9827d..c22a03e 100644
--- a/chrome/test/data/pdf/basic_test.js
+++ b/chrome/test/data/pdf/basic_test.js
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {getFilenameFromURL, shouldIgnoreKeyEvents} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer.js';
+import {pressAndReleaseKeyOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
+
 var tests = [
   /**
    * Test that some key elements exist and that they have a shadowRoot. This
@@ -77,13 +80,13 @@
 
     dropdown.$.button.click();
     chrome.test.assertTrue(dropdown.dropdownOpen);
-    MockInteractions.pressAndReleaseKeyOn(document, ESC_KEY);
+    pressAndReleaseKeyOn(document, ESC_KEY);
     chrome.test.assertFalse(
         dropdown.dropdownOpen, 'Escape key closes dropdown');
     chrome.test.assertTrue(
         toolbar.opened, 'First escape key does not close toolbar');
 
-    MockInteractions.pressAndReleaseKeyOn(document, ESC_KEY);
+    pressAndReleaseKeyOn(document, ESC_KEY);
     chrome.test.assertFalse(toolbar.opened, 'Second escape key closes toolbar');
 
     chrome.test.succeed();
diff --git a/chrome/test/data/pdf/bookmarks_test.js b/chrome/test/data/pdf/bookmarks_test.js
index c0d38dee..554453c 100644
--- a/chrome/test/data/pdf/bookmarks_test.js
+++ b/chrome/test/data/pdf/bookmarks_test.js
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {createBookmarksForTest} from './test_util.js';
+
 var tests = [
   /**
    * Test that the correct bookmarks were loaded for
@@ -61,14 +64,14 @@
     bookmarkContent.bookmarks = viewer.bookmarks;
     document.body.appendChild(bookmarkContent);
 
-    Polymer.dom.flush();
+    flush();
 
     var rootBookmarks =
         bookmarkContent.shadowRoot.querySelectorAll('viewer-bookmark');
     chrome.test.assertEq(3, rootBookmarks.length, 'three root bookmarks');
     rootBookmarks[0].$.expand.click();
 
-    Polymer.dom.flush();
+    flush();
 
     var subBookmarks =
         rootBookmarks[0].shadowRoot.querySelectorAll('viewer-bookmark');
diff --git a/chrome/test/data/pdf/material_elements_test.js b/chrome/test/data/pdf/material_elements_test.js
index 0fa832c..e81c9e2 100644
--- a/chrome/test/data/pdf/material_elements_test.js
+++ b/chrome/test/data/pdf/material_elements_test.js
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {createBookmarksForTest} from './test_util.js';
+
 /**
  * Captures 'fit-to-changed' events and verifies the last one has the expected
  * paylod.
@@ -127,7 +130,7 @@
     document.body.appendChild(bookmarkContent);
 
     // Force templates to render.
-    Polymer.dom.flush();
+    flush();
 
     var rootBookmarks =
         bookmarkContent.shadowRoot.querySelectorAll('viewer-bookmark');
@@ -135,7 +138,7 @@
     var rootBookmark = rootBookmarks[0];
     rootBookmark.$.expand.click();
 
-    Polymer.dom.flush();
+    flush();
 
     var subBookmarks =
         rootBookmark.shadowRoot.querySelectorAll('viewer-bookmark');
diff --git a/chrome/test/data/pdf/navigator_test.js b/chrome/test/data/pdf/navigator_test.js
index 1d5254d..4d99664 100644
--- a/chrome/test/data/pdf/navigator_test.js
+++ b/chrome/test/data/pdf/navigator_test.js
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {MockDocumentDimensions, MockSizer, MockViewportChangedCallback, MockWindow} from './test_util.js';
+
 class MockNavigatorDelegate {
   constructor() {
     this.navigateInCurrentTabCalled = false;
diff --git a/chrome/test/data/pdf/page_change_test.js b/chrome/test/data/pdf/page_change_test.js
index 8c27397..8632aa0 100644
--- a/chrome/test/data/pdf/page_change_test.js
+++ b/chrome/test/data/pdf/page_change_test.js
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {pressAndReleaseKeyOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
+
 function resetDocument() {
   window.viewer.viewport.goToPage(0);
   window.viewer.viewport.setZoom(1);
@@ -18,11 +20,11 @@
    */
   function testPageChangesWithArrows() {
     // Right arrow -> Go to page 2.
-    MockInteractions.pressAndReleaseKeyOn(document, 39);
+    pressAndReleaseKeyOn(document, 39);
     chrome.test.assertEq(1, getCurrentPage());
 
     // Left arrow -> Back to page 1.
-    MockInteractions.pressAndReleaseKeyOn(document, 37);
+    pressAndReleaseKeyOn(document, 37);
     chrome.test.assertEq(0, getCurrentPage());
 
     resetDocument();
@@ -39,10 +41,10 @@
     window.viewer.isFormFieldFocused_ = true;
 
     // Page should not change when left/right are pressed.
-    MockInteractions.pressAndReleaseKeyOn(document, 39);
+    pressAndReleaseKeyOn(document, 39);
     chrome.test.assertEq(0, getCurrentPage());
 
-    MockInteractions.pressAndReleaseKeyOn(document, 37);
+    pressAndReleaseKeyOn(document, 37);
     chrome.test.assertEq(0, getCurrentPage());
 
     resetDocument();
@@ -57,11 +59,11 @@
     window.viewer.viewport.fitToPage();
 
     // Page down -> Go to page 2.
-    MockInteractions.pressAndReleaseKeyOn(document, 34);
+    pressAndReleaseKeyOn(document, 34);
     chrome.test.assertEq(1, getCurrentPage());
 
     // Page up -> Back to page 1.
-    MockInteractions.pressAndReleaseKeyOn(document, 33);
+    pressAndReleaseKeyOn(document, 33);
     chrome.test.assertEq(0, getCurrentPage());
 
     resetDocument();
diff --git a/chrome/test/data/pdf/test_util.js b/chrome/test/data/pdf/test_util.js
index 24a30f18..ed8f220 100644
--- a/chrome/test/data/pdf/test_util.js
+++ b/chrome/test/data/pdf/test_util.js
@@ -4,7 +4,9 @@
 
 // Utilities that are used in multiple tests.
 
-function MockWindow(width, height, sizer) {
+import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+export function MockWindow(width, height, sizer) {
   this.innerWidth = width;
   this.innerHeight = height;
   this.addEventListener = function(e, f) {
@@ -53,7 +55,7 @@
   this.timerCallback = null;
 }
 
-function MockSizer() {
+export function MockSizer() {
   var sizer = this;
   this.style = {
     width_: '0px',
@@ -79,7 +81,7 @@
   };
 }
 
-function MockViewportChangedCallback() {
+export function MockViewportChangedCallback() {
   this.wasCalled = false;
   this.callback = function() {
     this.wasCalled = true;
@@ -89,7 +91,7 @@
   };
 }
 
-function MockDocumentDimensions(width, height, layoutOptions) {
+export function MockDocumentDimensions(width, height, layoutOptions) {
   this.width = width || 0;
   this.height = height ? height : 0;
   this.layoutOptions = layoutOptions;
@@ -120,19 +122,15 @@
  * @return {!HTMLElement} An element containing a dom-repeat of bookmarks, for
  *     testing the bookmarks outside of the toolbar.
  */
-function createBookmarksForTest() {
-  const module = document.createElement('dom-module');
-  module.id = 'test-bookmarks';
-  module.innerHTML = `
-      <template>
-        <template is="dom-repeat" items="[[bookmarks]]">
-          <viewer-bookmark bookmark="[[item]]" depth="0"></viewer-bookmark>
-        </template>
-      </template>
-  `;
-  document.body.appendChild(module);
+export function createBookmarksForTest() {
   Polymer({
     is: 'test-bookmarks',
+
+    _template: html`
+      <template is="dom-repeat" items="[[bookmarks]]">
+        <viewer-bookmark bookmark="[[item]]" depth="0"></viewer-bookmark>
+      </template>`,
+
     properties: {
       bookmarks: Array,
     },
diff --git a/chrome/test/data/pdf/toolbar_manager_test.js b/chrome/test/data/pdf/toolbar_manager_test.js
index 88fe870..75f20bb 100644
--- a/chrome/test/data/pdf/toolbar_manager_test.js
+++ b/chrome/test/data/pdf/toolbar_manager_test.js
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {MockWindow} from './test_util.js';
+
 // A cut-down version of MockInteractions.move, which is not exposed
 // publicly.
 function getMouseMoveEvents(fromX, fromY, toX, toY, steps) {
diff --git a/chrome/test/data/pdf/viewport_test.js b/chrome/test/data/pdf/viewport_test.js
index 730af982..540175e 100644
--- a/chrome/test/data/pdf/viewport_test.js
+++ b/chrome/test/data/pdf/viewport_test.js
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {MockDocumentDimensions, MockSizer, MockViewportChangedCallback, MockWindow} from './test_util.js';
+
 var tests = [
   function testDocumentNeedsScrollbars() {
     var viewport =
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 366df99..a8d9456 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-12605.0.0
\ No newline at end of file
+12612.0.0
\ No newline at end of file
diff --git a/chromeos/services/assistant/assistant_manager_service_impl.h b/chromeos/services/assistant/assistant_manager_service_impl.h
index 9fb3d0d..9b33a14 100644
--- a/chromeos/services/assistant/assistant_manager_service_impl.h
+++ b/chromeos/services/assistant/assistant_manager_service_impl.h
@@ -206,6 +206,7 @@
   assistant_client::AssistantManagerInternal* assistant_manager_internal() {
     return assistant_manager_internal_;
   }
+  CrosPlatformApi* platform_api() { return platform_api_.get(); }
 
   // media_session::mojom::MediaControllerObserver overrides:
   void MediaSessionInfoChanged(
diff --git a/chromeos/services/assistant/assistant_settings_manager_impl.cc b/chromeos/services/assistant/assistant_settings_manager_impl.cc
index 0cefd6f9..2debf0e 100644
--- a/chromeos/services/assistant/assistant_settings_manager_impl.cc
+++ b/chromeos/services/assistant/assistant_settings_manager_impl.cc
@@ -12,6 +12,7 @@
 #include "chromeos/dbus/util/version_loader.h"
 #include "chromeos/services/assistant/assistant_manager_service_impl.h"
 #include "chromeos/services/assistant/constants.h"
+#include "chromeos/services/assistant/cros_platform_api.h"
 #include "chromeos/services/assistant/public/features.h"
 #include "chromeos/services/assistant/public/proto/assistant_device_settings_ui.pb.h"
 #include "chromeos/services/assistant/public/proto/settings_ui.pb.h"
@@ -126,6 +127,8 @@
   DCHECK(HasStarted(assistant_manager_service_));
   DCHECK(main_task_runner()->RunsTasksInCurrentSequence());
 
+  assistant_manager_service_->platform_api()->SetMicState(true);
+
   if (!assistant_manager_service_->assistant_manager_internal())
     return;
 
@@ -153,6 +156,8 @@
   DCHECK(HasStarted(assistant_manager_service_));
   DCHECK(main_task_runner()->RunsTasksInCurrentSequence());
 
+  assistant_manager_service_->platform_api()->SetMicState(false);
+
   if (!assistant_manager_service_->assistant_manager_internal()) {
     std::move(callback).Run();
     return;
diff --git a/chromeos/services/cros_healthd/public/cpp/service_connection_unittest.cc b/chromeos/services/cros_healthd/public/cpp/service_connection_unittest.cc
index 260fd67..20133750 100644
--- a/chromeos/services/cros_healthd/public/cpp/service_connection_unittest.cc
+++ b/chromeos/services/cros_healthd/public/cpp/service_connection_unittest.cc
@@ -45,7 +45,9 @@
       2 /* cycle_count */, 12.9 /* voltage_now */,
       "battery_vendor" /* vendor */, "serial_number" /* serial_number */,
       5.275 /* charge_full_design */, 5.292 /* charge_full */,
-      11.55 /* voltage_min_design */, 51785890 /* manufacture_date_smart */);
+      11.55 /* voltage_min_design */, 51785890 /* manufacture_date_smart */,
+      /*temperature smart=*/981729, /*model_name=*/"battery_model",
+      /*charge_now=*/5.123);
 }
 
 mojom::CachedVpdInfoPtr MakeCachedVpdInfo() {
diff --git a/chromeos/services/cros_healthd/public/mojom/cros_healthd_probe.mojom b/chromeos/services/cros_healthd/public/mojom/cros_healthd_probe.mojom
index f28c1a5f..f967f7d4 100644
--- a/chromeos/services/cros_healthd/public/mojom/cros_healthd_probe.mojom
+++ b/chromeos/services/cros_healthd/public/mojom/cros_healthd_probe.mojom
@@ -38,6 +38,13 @@
   // http://sbs-forum.org/specs/sbdat110.pdf. The value is calculated by
   // ((year-1980) * 512 + month * 32 + day).
   int64 manufacture_date_smart;
+  // Temperature in 0.1°K as Smart Battery Temperature defined in
+  // http://sbs-forum.org/specs/sbdat110.pdf
+  uint64 temperature_smart;
+  // Model name.
+  string model_name;
+  // Current battery charge (Ah)
+  double charge_now;
 };
 
 // Information related to a specific non-removable block device.
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn
index ea02a80..9d3df73d 100644
--- a/components/autofill/core/browser/BUILD.gn
+++ b/components/autofill/core/browser/BUILD.gn
@@ -293,8 +293,10 @@
       "payments/autofill_save_card_infobar_mobile.h",
       "ui/mobile_label_formatter.cc",
       "ui/mobile_label_formatter.h",
-      "ui/payments/card_expiration_date_fix_flow_view_delegate_mobile.cc",
-      "ui/payments/card_expiration_date_fix_flow_view_delegate_mobile.h",
+      "ui/payments/card_expiration_date_fix_flow_controller.h",
+      "ui/payments/card_expiration_date_fix_flow_controller_impl.cc",
+      "ui/payments/card_expiration_date_fix_flow_controller_impl.h",
+      "ui/payments/card_expiration_date_fix_flow_view.h",
       "ui/payments/card_name_fix_flow_controller.h",
       "ui/payments/card_name_fix_flow_controller_impl.cc",
       "ui/payments/card_name_fix_flow_controller_impl.h",
@@ -607,6 +609,7 @@
     sources += [
       "autofill_assistant_unittest.cc",
       "ui/mobile_label_formatter_unittest.cc",
+      "ui/payments/card_expiration_date_fix_flow_controller_impl_unittest.cc",
       "ui/payments/card_name_fix_flow_controller_impl_unittest.cc",
     ]
   }
diff --git a/components/autofill/core/browser/autofill_ie_toolbar_import_win_unittest.cc b/components/autofill/core/browser/autofill_ie_toolbar_import_win_unittest.cc
index f8593ff..089f2cc9 100644
--- a/components/autofill/core/browser/autofill_ie_toolbar_import_win_unittest.cc
+++ b/components/autofill/core/browser/autofill_ie_toolbar_import_win_unittest.cc
@@ -13,6 +13,7 @@
 #include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/autofill/core/browser/field_types.h"
 #include "components/os_crypt/os_crypt.h"
+#include "components/os_crypt/os_crypt_mocker.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 #include <windows.h>
@@ -132,6 +133,7 @@
 }
 
 void AutofillIeToolbarImportTest::SetUp() {
+  OSCryptMocker::SetUp();
   temp_hkcu_hive_key_.Create(HKEY_CURRENT_USER,
                              kUnitTestUserOverrideSubKey,
                              KEY_ALL_ACCESS);
@@ -145,6 +147,7 @@
   temp_hkcu_hive_key_.Close();
   RegKey key(HKEY_CURRENT_USER, kUnitTestRegistrySubKey, KEY_ALL_ACCESS);
   key.DeleteKey(L"");
+  OSCryptMocker::TearDown();
 }
 
 TEST_F(AutofillIeToolbarImportTest, TestAutofillImport) {
diff --git a/components/autofill/core/browser/autofill_metrics_unittest.cc b/components/autofill/core/browser/autofill_metrics_unittest.cc
index 6bb4553..6bc96609 100644
--- a/components/autofill/core/browser/autofill_metrics_unittest.cc
+++ b/components/autofill/core/browser/autofill_metrics_unittest.cc
@@ -373,7 +373,6 @@
   autofill_manager_.reset();
   autofill_driver_.reset();
   personal_data_.reset();
-  test::ReenableSystemServices();
   test_ukm_recorder_->Purge();
 }
 
diff --git a/components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_controller.h b/components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_controller.h
new file mode 100644
index 0000000..47aa680
--- /dev/null
+++ b/components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_controller.h
@@ -0,0 +1,39 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_UI_PAYMENTS_CARD_EXPIRATION_DATE_FIX_FLOW_CONTROLLER_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_UI_PAYMENTS_CARD_EXPIRATION_DATE_FIX_FLOW_CONTROLLER_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/strings/string16.h"
+#include "components/autofill/core/browser/autofill_metrics.h"
+#include "components/autofill/core/browser/data_model/credit_card.h"
+
+namespace autofill {
+
+// Enables the user to accept or deny expiration date fix flow prompt.
+// Only used on mobile.
+class CardExpirationDateFixFlowController {
+ public:
+  virtual ~CardExpirationDateFixFlowController() {}
+
+  // Interaction.
+  virtual void OnAccepted(const base::string16& month,
+                          const base::string16& year) = 0;
+  virtual void OnDismissed() = 0;
+  virtual void OnDialogClosed() = 0;
+
+  // State.
+  virtual int GetIconId() const = 0;
+  virtual base::string16 GetTitleText() const = 0;
+  virtual base::string16 GetSaveButtonLabel() const = 0;
+  virtual base::string16 GetCardLabel() const = 0;
+};
+
+}  // namespace autofill
+
+#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_UI_PAYMENTS_CARD_EXPIRATION_DATE_FIX_FLOW_CONTROLLER_H_
diff --git a/components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_controller_impl.cc b/components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_controller_impl.cc
new file mode 100644
index 0000000..1128f6c6
--- /dev/null
+++ b/components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_controller_impl.cc
@@ -0,0 +1,101 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_controller_impl.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "build/branding_buildflags.h"
+#include "components/autofill/core/browser/data_model/credit_card.h"
+#include "components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_view.h"
+#include "components/grit/components_scaled_resources.h"
+#include "components/strings/grit/components_strings.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace autofill {
+
+CardExpirationDateFixFlowControllerImpl::
+    CardExpirationDateFixFlowControllerImpl() {}
+
+CardExpirationDateFixFlowControllerImpl::
+    ~CardExpirationDateFixFlowControllerImpl() {
+  if (card_expiration_date_fix_flow_view_)
+    card_expiration_date_fix_flow_view_->ControllerGone();
+
+  if (shown_ && !had_user_interaction_) {
+    AutofillMetrics::LogExpirationDateFixFlowPromptEvent(
+        AutofillMetrics::ExpirationDateFixFlowPromptEvent::
+            EXPIRATION_DATE_FIX_FLOW_PROMPT_CLOSED_WITHOUT_INTERACTION);
+  }
+}
+
+void CardExpirationDateFixFlowControllerImpl::Show(
+    CardExpirationDateFixFlowView* card_expiration_date_fix_flow_view,
+    const CreditCard& card,
+    base::OnceCallback<void(const base::string16&, const base::string16&)>
+        callback) {
+  DCHECK(!callback.is_null());
+  DCHECK(card_expiration_date_fix_flow_view);
+
+  card_label_ = card.NetworkAndLastFourDigits();
+
+  if (card_expiration_date_fix_flow_view_)
+    card_expiration_date_fix_flow_view_->ControllerGone();
+  card_expiration_date_fix_flow_view_ = card_expiration_date_fix_flow_view;
+
+  upload_save_card_callback_ = std::move(callback);
+
+  card_expiration_date_fix_flow_view_->Show();
+  AutofillMetrics::LogExpirationDateFixFlowPromptShown();
+  shown_ = true;
+}
+
+void CardExpirationDateFixFlowControllerImpl::OnAccepted(
+    const base::string16& month,
+    const base::string16& year) {
+  AutofillMetrics::LogExpirationDateFixFlowPromptEvent(
+      AutofillMetrics::ExpirationDateFixFlowPromptEvent::
+          EXPIRATION_DATE_FIX_FLOW_PROMPT_ACCEPTED);
+  had_user_interaction_ = true;
+  std::move(upload_save_card_callback_).Run(month, year);
+}
+
+void CardExpirationDateFixFlowControllerImpl::OnDismissed() {
+  AutofillMetrics::LogExpirationDateFixFlowPromptEvent(
+      AutofillMetrics::ExpirationDateFixFlowPromptEvent::
+          EXPIRATION_DATE_FIX_FLOW_PROMPT_DISMISSED);
+  had_user_interaction_ = true;
+}
+
+void CardExpirationDateFixFlowControllerImpl::OnDialogClosed() {
+  card_expiration_date_fix_flow_view_ = nullptr;
+}
+
+int CardExpirationDateFixFlowControllerImpl::GetIconId() const {
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+  return IDR_AUTOFILL_GOOGLE_PAY_WITH_DIVIDER;
+#else
+  return 0;
+#endif
+}
+
+base::string16 CardExpirationDateFixFlowControllerImpl::GetTitleText() const {
+  return l10n_util::GetStringUTF16(
+      IDS_AUTOFILL_SAVE_CARD_UPDATE_EXPIRATION_DATE_TITLE);
+}
+
+base::string16 CardExpirationDateFixFlowControllerImpl::GetSaveButtonLabel()
+    const {
+  return l10n_util::GetStringUTF16(
+      IDS_AUTOFILL_FIX_FLOW_PROMPT_SAVE_CARD_LABEL);
+}
+
+base::string16 CardExpirationDateFixFlowControllerImpl::GetCardLabel() const {
+  return card_label_;
+}
+
+}  // namespace autofill
diff --git a/components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_controller_impl.h b/components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_controller_impl.h
new file mode 100644
index 0000000..0e077b0
--- /dev/null
+++ b/components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_controller_impl.h
@@ -0,0 +1,69 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_UI_PAYMENTS_CARD_EXPIRATION_DATE_FIX_FLOW_CONTROLLER_IMPL_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_UI_PAYMENTS_CARD_EXPIRATION_DATE_FIX_FLOW_CONTROLLER_IMPL_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/strings/string16.h"
+#include "components/autofill/core/browser/autofill_metrics.h"
+#include "components/autofill/core/browser/data_model/credit_card.h"
+#include "components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_controller.h"
+
+namespace autofill {
+
+class CardExpirationDateFixFlowView;
+
+// Enables the user to accept or deny expiration date fix flow prompt.
+// Only used on mobile. This class is responsible for its destruction.
+// Destruction is achieved by calling delete when the prompt is
+// dismissed.
+class CardExpirationDateFixFlowControllerImpl
+    : public CardExpirationDateFixFlowController {
+ public:
+  CardExpirationDateFixFlowControllerImpl();
+  ~CardExpirationDateFixFlowControllerImpl() override;
+
+  void Show(CardExpirationDateFixFlowView* card_expiration_date_fix_flow_view,
+            const CreditCard& card,
+            base::OnceCallback<void(const base::string16&,
+                                    const base::string16&)> callback);
+
+  // CardExpirationDateFixFlowController implementation.
+  void OnAccepted(const base::string16& month,
+                  const base::string16& year) override;
+  void OnDismissed() override;
+  void OnDialogClosed() override;
+  int GetIconId() const override;
+  base::string16 GetTitleText() const override;
+  base::string16 GetSaveButtonLabel() const override;
+  base::string16 GetCardLabel() const override;
+
+ private:
+  // View that displays the fix flow prompt.
+  CardExpirationDateFixFlowView* card_expiration_date_fix_flow_view_ = nullptr;
+
+  // The callback to save the credit card to Google Payments once user accepts
+  // fix flow.
+  base::OnceCallback<void(const base::string16&, const base::string16&)>
+      upload_save_card_callback_;
+
+  // Whether the prompt was shown to the user.
+  bool shown_ = false;
+
+  // Did the user ever explicitly accept or dismiss this prompt?
+  bool had_user_interaction_ = false;
+
+  // Label of the card describing the network and the last four digits.
+  base::string16 card_label_;
+
+  DISALLOW_COPY_AND_ASSIGN(CardExpirationDateFixFlowControllerImpl);
+};
+
+}  // namespace autofill
+
+#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_UI_PAYMENTS_CARD_EXPIRATION_DATE_FIX_FLOW_CONTROLLER_IMPL_H_
diff --git a/components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_controller_impl_unittest.cc b/components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_controller_impl_unittest.cc
new file mode 100644
index 0000000..b3834c9
--- /dev/null
+++ b/components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_controller_impl_unittest.cc
@@ -0,0 +1,109 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_controller_impl.h"
+
+#include <stddef.h>
+#include <memory>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "components/autofill/core/browser/autofill_metrics.h"
+#include "components/autofill/core/browser/autofill_test_utils.h"
+#include "components/autofill/core/browser/data_model/credit_card.h"
+#include "components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_view.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+
+class TestCardExpirationDateFixFlowView : public CardExpirationDateFixFlowView {
+ public:
+  void Show() override {}
+  void ControllerGone() override {}
+};
+
+class CardExpirationDateFixFlowControllerImplGenericTest {
+ public:
+  CardExpirationDateFixFlowControllerImplGenericTest() {}
+
+  void ShowPrompt() {
+    controller_->Show(
+        test_card_expiration_date_fix_flow_view_.get(), autofill::CreditCard(),
+        base::BindOnce(
+            &CardExpirationDateFixFlowControllerImplGenericTest::OnAccepted,
+            weak_ptr_factory_.GetWeakPtr()));
+  }
+
+ protected:
+  std::unique_ptr<TestCardExpirationDateFixFlowView>
+      test_card_expiration_date_fix_flow_view_;
+  std::unique_ptr<CardExpirationDateFixFlowControllerImpl> controller_;
+  base::string16 accepted_month_;
+  base::string16 accepted_year_;
+  base::WeakPtrFactory<CardExpirationDateFixFlowControllerImplGenericTest>
+      weak_ptr_factory_{this};
+
+ private:
+  void OnAccepted(const base::string16& month, const base::string16& year) {
+    accepted_month_ = month;
+    accepted_year_ = year;
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(CardExpirationDateFixFlowControllerImplGenericTest);
+};
+
+class CardExpirationDateFixFlowControllerImplTest
+    : public CardExpirationDateFixFlowControllerImplGenericTest,
+      public testing::Test {
+ public:
+  CardExpirationDateFixFlowControllerImplTest() {}
+  ~CardExpirationDateFixFlowControllerImplTest() override {}
+
+  void SetUp() override {
+    test_card_expiration_date_fix_flow_view_.reset(
+        new TestCardExpirationDateFixFlowView());
+    controller_.reset(new CardExpirationDateFixFlowControllerImpl());
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CardExpirationDateFixFlowControllerImplTest);
+};
+
+TEST_F(CardExpirationDateFixFlowControllerImplTest, LogShown) {
+  base::HistogramTester histogram_tester;
+  ShowPrompt();
+
+  histogram_tester.ExpectBucketCount(
+      "Autofill.ExpirationDateFixFlowPromptShown", true, 1);
+}
+
+TEST_F(CardExpirationDateFixFlowControllerImplTest, LogAccepted) {
+  base::HistogramTester histogram_tester;
+  ShowPrompt();
+  controller_->OnAccepted(base::ASCIIToUTF16("11"), base::ASCIIToUTF16("30"));
+
+  ASSERT_EQ(accepted_month_, base::ASCIIToUTF16("11"));
+  ASSERT_EQ(accepted_year_, base::ASCIIToUTF16("30"));
+  histogram_tester.ExpectBucketCount(
+      "Autofill.ExpirationDateFixFlowPrompt.Events",
+      AutofillMetrics::ExpirationDateFixFlowPromptEvent::
+          EXPIRATION_DATE_FIX_FLOW_PROMPT_ACCEPTED,
+      1);
+}
+
+TEST_F(CardExpirationDateFixFlowControllerImplTest, LogDismissed) {
+  base::HistogramTester histogram_tester;
+  ShowPrompt();
+  controller_->OnDismissed();
+
+  histogram_tester.ExpectBucketCount(
+      "Autofill.ExpirationDateFixFlowPrompt.Events",
+      AutofillMetrics::ExpirationDateFixFlowPromptEvent::
+          EXPIRATION_DATE_FIX_FLOW_PROMPT_DISMISSED,
+      1);
+}
+
+}  // namespace autofill
diff --git a/components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_view.h b/components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_view.h
new file mode 100644
index 0000000..90ac826
--- /dev/null
+++ b/components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_view.h
@@ -0,0 +1,27 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_UI_PAYMENTS_CARD_EXPIRATION_DATE_FIX_FLOW_VIEW_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_UI_PAYMENTS_CARD_EXPIRATION_DATE_FIX_FLOW_VIEW_H_
+
+#include "base/macros.h"
+#include "base/strings/string16.h"
+
+namespace autofill {
+
+// The cross-platform UI interface which prompts the user to confirm the
+// expiration date of their form of payment. This object is responsible for its
+// own lifetime.
+class CardExpirationDateFixFlowView {
+ public:
+  virtual void Show() = 0;
+  virtual void ControllerGone() = 0;
+
+ protected:
+  virtual ~CardExpirationDateFixFlowView() = default;
+};
+
+}  // namespace autofill
+
+#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_UI_PAYMENTS_CARD_EXPIRATION_DATE_FIX_FLOW_VIEW_H_
diff --git a/components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_view_delegate_mobile.cc b/components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_view_delegate_mobile.cc
deleted file mode 100644
index 920eafd..0000000
--- a/components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_view_delegate_mobile.cc
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_view_delegate_mobile.h"
-
-#include <utility>
-
-#include "base/logging.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/values.h"
-#include "build/branding_buildflags.h"
-#include "components/autofill/core/browser/data_model/credit_card.h"
-#include "components/grit/components_scaled_resources.h"
-#include "components/strings/grit/components_strings.h"
-#include "ui/base/l10n/l10n_util.h"
-
-namespace autofill {
-
-CardExpirationDateFixFlowViewDelegateMobile::
-    CardExpirationDateFixFlowViewDelegateMobile(
-        const CreditCard& card,
-        base::OnceCallback<void(const base::string16&, const base::string16&)>
-            upload_save_card_callback)
-    : upload_save_card_callback_(std::move(upload_save_card_callback)),
-      shown_(false),
-      had_user_interaction_(false),
-      card_label_(card.NetworkAndLastFourDigits()) {
-  DCHECK(!upload_save_card_callback_.is_null());
-}
-
-CardExpirationDateFixFlowViewDelegateMobile::
-    ~CardExpirationDateFixFlowViewDelegateMobile() {
-  if (shown_ && !had_user_interaction_)
-    AutofillMetrics::LogExpirationDateFixFlowPromptEvent(
-        AutofillMetrics::ExpirationDateFixFlowPromptEvent::
-            EXPIRATION_DATE_FIX_FLOW_PROMPT_CLOSED_WITHOUT_INTERACTION);
-}
-
-int CardExpirationDateFixFlowViewDelegateMobile::GetIconId() const {
-#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
-  return IDR_AUTOFILL_GOOGLE_PAY_WITH_DIVIDER;
-#else
-  return 0;
-#endif
-}
-
-base::string16 CardExpirationDateFixFlowViewDelegateMobile::GetTitleText()
-    const {
-  return l10n_util::GetStringUTF16(
-      IDS_AUTOFILL_SAVE_CARD_UPDATE_EXPIRATION_DATE_TITLE);
-}
-
-base::string16 CardExpirationDateFixFlowViewDelegateMobile::GetSaveButtonLabel()
-    const {
-  return l10n_util::GetStringUTF16(
-      IDS_AUTOFILL_FIX_FLOW_PROMPT_SAVE_CARD_LABEL);
-}
-
-void CardExpirationDateFixFlowViewDelegateMobile::Accept(
-    const base::string16& month,
-    const base::string16& year) {
-  std::move(upload_save_card_callback_).Run(month, year);
-  AutofillMetrics::LogExpirationDateFixFlowPromptEvent(
-      AutofillMetrics::ExpirationDateFixFlowPromptEvent::
-          EXPIRATION_DATE_FIX_FLOW_PROMPT_ACCEPTED);
-  had_user_interaction_ = true;
-}
-
-void CardExpirationDateFixFlowViewDelegateMobile::Dismissed() {
-  AutofillMetrics::LogExpirationDateFixFlowPromptEvent(
-      AutofillMetrics::ExpirationDateFixFlowPromptEvent::
-          EXPIRATION_DATE_FIX_FLOW_PROMPT_DISMISSED);
-  had_user_interaction_ = true;
-}
-
-void CardExpirationDateFixFlowViewDelegateMobile::Shown() {
-  AutofillMetrics::LogExpirationDateFixFlowPromptShown();
-  shown_ = true;
-}
-
-}  // namespace autofill
diff --git a/components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_view_delegate_mobile.h b/components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_view_delegate_mobile.h
deleted file mode 100644
index 389bdd1b..0000000
--- a/components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_view_delegate_mobile.h
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_UI_PAYMENTS_CARD_EXPIRATION_DATE_FIX_FLOW_VIEW_DELEGATE_MOBILE_H_
-#define COMPONENTS_AUTOFILL_CORE_BROWSER_UI_PAYMENTS_CARD_EXPIRATION_DATE_FIX_FLOW_VIEW_DELEGATE_MOBILE_H_
-
-#include <memory>
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "base/strings/string16.h"
-#include "components/autofill/core/browser/autofill_metrics.h"
-#include "components/autofill/core/browser/data_model/credit_card.h"
-
-namespace autofill {
-
-// Enables the user to accept or deny expiration date fix flow prompt.
-// Only used on mobile. This class is responsible for its destruction.
-// Destruction is achieved by calling delete when the prompt is
-// dismissed.
-class CardExpirationDateFixFlowViewDelegateMobile {
- public:
-  CardExpirationDateFixFlowViewDelegateMobile(
-      const CreditCard& card,
-      base::OnceCallback<void(const base::string16&, const base::string16&)>
-          upload_save_card_callback);
-
-  ~CardExpirationDateFixFlowViewDelegateMobile();
-
-  const base::string16& card_label() const { return card_label_; }
-
-  int GetIconId() const;
-  base::string16 GetTitleText() const;
-  base::string16 GetSaveButtonLabel() const;
-  void Accept(const base::string16& month, const base::string16& year);
-  void Dismissed();
-  void Shown();
-
- private:
-  // The callback to save the credit card to Google Payments once user accepts
-  // fix flow.
-  base::OnceCallback<void(const base::string16&, const base::string16&)>
-      upload_save_card_callback_;
-
-  // Whether the prompt was shown to the user.
-  bool shown_;
-
-  // Did the user ever explicitly accept or dismiss this prompt?
-  bool had_user_interaction_;
-
-  // Label of the card describing the network and the last four digits.
-  base::string16 card_label_;
-
-  DISALLOW_COPY_AND_ASSIGN(CardExpirationDateFixFlowViewDelegateMobile);
-};
-
-}  // namespace autofill
-
-#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_UI_PAYMENTS_CARD_EXPIRATION_DATE_FIX_FLOW_VIEW_DELEGATE_MOBILE_H_
diff --git a/components/autofill/core/browser/webdata/web_data_service_unittest.cc b/components/autofill/core/browser/webdata/web_data_service_unittest.cc
index fbdec05..85d18550 100644
--- a/components/autofill/core/browser/webdata/web_data_service_unittest.cc
+++ b/components/autofill/core/browser/webdata/web_data_service_unittest.cc
@@ -30,6 +30,7 @@
 #include "components/autofill/core/browser/webdata/autofill_webdata_service_observer.h"
 #include "components/autofill/core/common/autofill_clock.h"
 #include "components/autofill/core/common/form_field_data.h"
+#include "components/os_crypt/os_crypt_mocker.h"
 #include "components/webdata/common/web_data_results.h"
 #include "components/webdata/common/web_data_service_base.h"
 #include "components/webdata/common/web_data_service_consumer.h"
@@ -96,6 +97,8 @@
  protected:
   void SetUp() override {
     base::FilePath path(WebDatabase::kInMemoryPath);
+    // OSCrypt is used for encryption of credit card data in this test.
+    OSCryptMocker::SetUp();
 
     // TODO(pkasting): http://crbug.com/740773 This should likely be sequenced,
     // not single-threaded; it's also possible the various uses of this below
@@ -119,6 +122,7 @@
     wds_ = nullptr;
     wdbs_ = nullptr;
     task_environment_.RunUntilIdle();
+    OSCryptMocker::TearDown();
   }
 
   base::test::TaskEnvironment task_environment_;
diff --git a/components/os_crypt/os_crypt.h b/components/os_crypt/os_crypt.h
index c3a917a0..5aa09425 100644
--- a/components/os_crypt/os_crypt.h
+++ b/components/os_crypt/os_crypt.h
@@ -19,7 +19,7 @@
 class KeyStorageLinux;
 #endif  // defined(OS_LINUX) && !defined(OS_CHROMEOS)
 
-#if defined(OS_MACOSX) && !defined(OS_IOS)
+#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS))
 class PrefRegistrySimple;
 class PrefService;
 #endif
@@ -73,15 +73,16 @@
       const std::string& ciphertext,
       std::string* plaintext);
 
-#if defined(OS_MACOSX) && !defined(OS_IOS)
+#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS))
   // Registers preferences used by OSCrypt.
   static COMPONENT_EXPORT(OS_CRYPT) void RegisterLocalPrefs(
       PrefRegistrySimple* registry);
 
   // Initialises OSCrypt.
   // This method should be called on the main UI thread before any calls to
-  // encryption or decryption.
-  static COMPONENT_EXPORT(OS_CRYPT) void Init(PrefService* local_state);
+  // encryption or decryption. Returns |true| if os_crypt successfully
+  // initialized.
+  static COMPONENT_EXPORT(OS_CRYPT) bool Init(PrefService* local_state);
 #endif
 
 #if defined(OS_MACOSX)
@@ -96,7 +97,9 @@
   // mock Keychain. Use OSCryptMocker, instead of calling this method directly.
   static COMPONENT_EXPORT(OS_CRYPT) void UseLockedMockKeychainForTesting(
       bool use_locked);
+#endif
 
+#if defined(OS_WIN) || defined(OS_MACOSX)
   // Get the raw encryption key to be used for all AES encryption. Returns an
   // empty string in the case password access is denied or key generation error
   // occurs. This method is thread-safe.
@@ -108,6 +111,22 @@
       const std::string& key);
 #endif
 
+#if defined(OS_WIN)
+  // For unit testing purposes we instruct the Encryptor to use a mock Key. The
+  // default is to use the real Key bound to profile. Use OSCryptMocker, instead
+  // of calling this method directly.
+  static COMPONENT_EXPORT(OS_CRYPT) void UseMockKeyForTesting(bool use_mock);
+
+  // For unit testing purposes, encrypt data using the older DPAPI method rather
+  // than using a session key.
+  static COMPONENT_EXPORT(OS_CRYPT) void SetLegacyEncryptionForTesting(
+      bool legacy);
+
+  // For unit testing purposes, reset the state of OSCrypt so a new key can be
+  // loaded via Init() or SetRawEncryptionkey().
+  static COMPONENT_EXPORT(OS_CRYPT) void ResetStateForTesting();
+#endif
+
  private:
   DISALLOW_IMPLICIT_CONSTRUCTORS(OSCrypt);
 };
diff --git a/components/os_crypt/os_crypt_mac.mm b/components/os_crypt/os_crypt_mac.mm
index 88304b2c..1b5d658 100644
--- a/components/os_crypt/os_crypt_mac.mm
+++ b/components/os_crypt/os_crypt_mac.mm
@@ -239,10 +239,11 @@
   registry->RegisterBooleanPref(os_crypt::prefs::kKeyCreated, false);
 }
 
-void OSCrypt::Init(PrefService* local_state) {
+bool OSCrypt::Init(PrefService* local_state) {
   base::AutoLock auto_lock(g_lock.Get());
   g_key_creation_util = new os_crypt::EncryptionKeyCreationUtilMac(
       local_state, base::ThreadTaskRunnerHandle::Get());
+  return true;
 }
 #endif
 
diff --git a/components/os_crypt/os_crypt_mocker.cc b/components/os_crypt/os_crypt_mocker.cc
index 429cf91..394f059 100644
--- a/components/os_crypt/os_crypt_mocker.cc
+++ b/components/os_crypt/os_crypt_mocker.cc
@@ -16,6 +16,8 @@
   OSCrypt::UseMockKeychainForTesting(true);
 #elif defined(USE_LIBSECRET) || defined(USE_KEYRING) || defined(USE_KWALLET)
   OSCryptMockerLinux::SetUp();
+#elif defined(OS_WIN)
+  OSCrypt::UseMockKeyForTesting(true);
 #endif
 }
 
@@ -26,11 +28,25 @@
 }
 #endif
 
+#if defined(OS_WIN)
+// static
+void OSCryptMocker::SetLegacyEncryption(bool legacy) {
+  OSCrypt::SetLegacyEncryptionForTesting(legacy);
+}
+
+void OSCryptMocker::ResetState() {
+  OSCrypt::ResetStateForTesting();
+}
+
+#endif
+
 // static
 void OSCryptMocker::TearDown() {
 #if defined(OS_MACOSX)
   OSCrypt::UseMockKeychainForTesting(false);
 #elif defined(USE_LIBSECRET) || defined(USE_KEYRING) || defined(USE_KWALLET)
   OSCryptMockerLinux::TearDown();
+#elif defined(OS_WIN)
+  OSCrypt::UseMockKeyForTesting(false);
 #endif
 }
diff --git a/components/os_crypt/os_crypt_mocker.h b/components/os_crypt/os_crypt_mocker.h
index c0ae267..a205a4cc 100644
--- a/components/os_crypt/os_crypt_mocker.h
+++ b/components/os_crypt/os_crypt_mocker.h
@@ -20,6 +20,14 @@
   static void SetBackendLocked(bool locked);
 #endif
 
+#if defined(OS_WIN)
+  // Store data using the older DPAPI interface rather than session key.
+  static void SetLegacyEncryption(bool legacy);
+
+  // Reset OSCrypt so it can be initialized again with a new profile/key.
+  static void ResetState();
+#endif
+
   // Restore OSCrypt to its real behaviour.
   static void TearDown();
 
diff --git a/components/os_crypt/os_crypt_unittest.cc b/components/os_crypt/os_crypt_unittest.cc
index b25cc28..86ab74e 100644
--- a/components/os_crypt/os_crypt_unittest.cc
+++ b/components/os_crypt/os_crypt_unittest.cc
@@ -22,6 +22,12 @@
 #include "components/os_crypt/os_crypt_mocker_linux.h"
 #endif
 
+#if defined(OS_WIN)
+#include "base/strings/string_util.h"
+#include "components/prefs/testing_pref_service.h"
+#include "crypto/random.h"
+#endif
+
 namespace {
 
 class OSCryptTest : public testing::Test {
@@ -186,4 +192,115 @@
   }
 }
 
+#if defined(OS_WIN)
+
+class OSCryptTestWin : public testing::Test {
+ public:
+  OSCryptTestWin() {}
+
+  ~OSCryptTestWin() override { OSCryptMocker::ResetState(); }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(OSCryptTestWin);
+};
+
+// This test verifies that the header of the data returned from CryptProtectData
+// never collides with the kEncryptionVersionPrefix ("v10") used in
+// os_crypt_win.cc. If this ever happened, we would not be able to distinguish
+// between data encrypted using the legacy DPAPI interface, and data that's been
+// encrypted with the new session key.
+
+// If this test ever breaks do not ignore it as it might result in data loss for
+// users.
+TEST_F(OSCryptTestWin, DPAPIHeader) {
+  std::string plaintext;
+  std::string ciphertext;
+
+  OSCryptMocker::SetLegacyEncryption(true);
+  crypto::RandBytes(base::WriteInto(&plaintext, 11), 10);
+  ASSERT_TRUE(OSCrypt::EncryptString(plaintext, &ciphertext));
+
+  using std::string_literals::operator""s;
+  const std::string expected_header("\x01\x00\x00\x00"s);
+  ASSERT_EQ(4U, expected_header.length());
+
+  ASSERT_TRUE(ciphertext.length() >= expected_header.length());
+  std::string dpapi_header = ciphertext.substr(0, expected_header.length());
+  EXPECT_EQ(expected_header, dpapi_header);
+}
+
+TEST_F(OSCryptTestWin, ReadOldData) {
+  OSCryptMocker::SetLegacyEncryption(true);
+
+  std::string plaintext = "secrets";
+  std::string legacy_ciphertext;
+  ASSERT_TRUE(OSCrypt::EncryptString(plaintext, &legacy_ciphertext));
+
+  OSCryptMocker::SetLegacyEncryption(false);
+
+  TestingPrefServiceSimple pref_service_simple;
+  OSCrypt::RegisterLocalPrefs(pref_service_simple.registry());
+  ASSERT_TRUE(OSCrypt::Init(&pref_service_simple));
+
+  std::string decrypted;
+  // Should be able to decrypt data encrypted with DPAPI.
+  ASSERT_TRUE(OSCrypt::DecryptString(legacy_ciphertext, &decrypted));
+  EXPECT_EQ(plaintext, decrypted);
+
+  // Should now encrypt same plaintext to get different ciphertext.
+  std::string new_ciphertext;
+  ASSERT_TRUE(OSCrypt::EncryptString(plaintext, &new_ciphertext));
+
+  // Should be different from DPAPI ciphertext.
+  EXPECT_NE(legacy_ciphertext, new_ciphertext);
+
+  // Decrypt new ciphertext to give original string.
+  ASSERT_TRUE(OSCrypt::DecryptString(new_ciphertext, &decrypted));
+  EXPECT_EQ(plaintext, decrypted);
+}
+
+TEST_F(OSCryptTestWin, PrefsKeyTest) {
+  TestingPrefServiceSimple first_prefs;
+  OSCrypt::RegisterLocalPrefs(first_prefs.registry());
+
+  // Verify new random key can be generated.
+  ASSERT_TRUE(OSCrypt::Init(&first_prefs));
+  std::string first_key = OSCrypt::GetRawEncryptionKey();
+
+  std::string plaintext = "secrets";
+  std::string ciphertext;
+  ASSERT_TRUE(OSCrypt::EncryptString(plaintext, &ciphertext));
+
+  TestingPrefServiceSimple second_prefs;
+  OSCrypt::RegisterLocalPrefs(second_prefs.registry());
+
+  OSCryptMocker::ResetState();
+  ASSERT_TRUE(OSCrypt::Init(&second_prefs));
+  std::string second_key = OSCrypt::GetRawEncryptionKey();
+  // Keys should be different since they are random.
+  EXPECT_NE(first_key, second_key);
+
+  std::string decrypted;
+  // Cannot decrypt with the wrong key.
+  EXPECT_FALSE(OSCrypt::DecryptString(ciphertext, &decrypted));
+
+  // Initialize OSCrypt from existing key.
+  OSCryptMocker::ResetState();
+  OSCrypt::SetRawEncryptionKey(first_key);
+
+  // Verify decryption works with first key.
+  ASSERT_TRUE(OSCrypt::DecryptString(ciphertext, &decrypted));
+  EXPECT_EQ(plaintext, decrypted);
+
+  // Initialize OSCrypt from existing prefs.
+  OSCryptMocker::ResetState();
+  ASSERT_TRUE(OSCrypt::Init(&first_prefs));
+
+  // Verify decryption works with key from first prefs.
+  ASSERT_TRUE(OSCrypt::DecryptString(ciphertext, &decrypted));
+  EXPECT_EQ(plaintext, decrypted);
+}
+
+#endif  // defined(OS_WIN)
+
 }  // namespace
diff --git a/components/os_crypt/os_crypt_win.cc b/components/os_crypt/os_crypt_win.cc
index 792233d..8a7f33e7 100644
--- a/components/os_crypt/os_crypt_win.cc
+++ b/components/os_crypt/os_crypt_win.cc
@@ -6,25 +6,56 @@
 
 #include <windows.h>
 
+#include "base/base64.h"
+#include "base/no_destructor.h"
+#include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/win/wincrypt_shim.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+#include "crypto/aead.h"
+#include "crypto/hkdf.h"
+#include "crypto/random.h"
 
-bool OSCrypt::EncryptString16(const base::string16& plaintext,
-                              std::string* ciphertext) {
-  return EncryptString(base::UTF16ToUTF8(plaintext), ciphertext);
+namespace {
+
+// Contains base64 random key encrypted with DPAPI.
+const char kOsCryptEncryptedKeyPrefName[] = "os_crypt.encrypted_key";
+
+// AEAD key length in bytes.
+const size_t kKeyLength = 256 / 8;
+
+// AEAD nonce length in bytes.
+const size_t kNonceLength = 96 / 8;
+
+// Version prefix for data encrypted with profile bound key.
+const char kEncryptionVersionPrefix[] = "v10";
+
+// Key prefix for a key encrypted with DPAPI.
+const char kDPAPIKeyPrefix[] = "DPAPI";
+
+// Use mock key instead of a real encryption key. Used for testing.
+bool g_use_mock_key = false;
+
+// Store data using the legacy (DPAPI) method rather than session key.
+bool g_use_legacy = false;
+
+// These two keys must have no destructors to allow OSCrypt calls to function
+// correctly during shutdown.
+
+// Encryption Key. Set either by calling Init() or SetRawEncryptionKey().
+std::string& GetEncryptionKeyFactory() {
+  static base::NoDestructor<std::string> encryption_key;
+  return *encryption_key;
 }
 
-bool OSCrypt::DecryptString16(const std::string& ciphertext,
-                              base::string16* plaintext) {
-  std::string utf8;
-  if (!DecryptString(ciphertext, &utf8))
-    return false;
-
-  *plaintext = base::UTF8ToUTF16(utf8);
-  return true;
+// Mock Encryption Key. Only set and used if g_use_mock_key is true.
+std::string& GetMockEncryptionKeyFactory() {
+  static base::NoDestructor<std::string> mock_encryption_key;
+  return *mock_encryption_key;
 }
 
-bool OSCrypt::EncryptString(const std::string& plaintext,
+bool EncryptStringWithDPAPI(const std::string& plaintext,
                             std::string* ciphertext) {
   DATA_BLOB input;
   input.pbData = const_cast<BYTE*>(
@@ -47,7 +78,7 @@
   return true;
 }
 
-bool OSCrypt::DecryptString(const std::string& ciphertext,
+bool DecryptStringWithDPAPI(const std::string& ciphertext,
                             std::string* plaintext) {
   DATA_BLOB input;
   input.pbData = const_cast<BYTE*>(
@@ -66,3 +97,162 @@
   LocalFree(output.pbData);
   return true;
 }
+
+const std::string& GetEncryptionKeyInternal() {
+  if (g_use_mock_key) {
+    if (GetMockEncryptionKeyFactory().empty())
+      GetMockEncryptionKeyFactory().assign(
+          crypto::HkdfSha256("peanuts", "salt", "info", kKeyLength));
+    DCHECK(!GetMockEncryptionKeyFactory().empty())
+        << "Failed to initialize mock key.";
+    return GetMockEncryptionKeyFactory();
+  }
+
+  DCHECK(!GetEncryptionKeyFactory().empty()) << "No key.";
+  return GetEncryptionKeyFactory();
+}
+
+}  // namespace
+
+// static
+bool OSCrypt::EncryptString16(const base::string16& plaintext,
+                              std::string* ciphertext) {
+  return EncryptString(base::UTF16ToUTF8(plaintext), ciphertext);
+}
+
+// static
+bool OSCrypt::DecryptString16(const std::string& ciphertext,
+                              base::string16* plaintext) {
+  std::string utf8;
+  if (!DecryptString(ciphertext, &utf8))
+    return false;
+
+  *plaintext = base::UTF8ToUTF16(utf8);
+  return true;
+}
+
+// static
+bool OSCrypt::EncryptString(const std::string& plaintext,
+                            std::string* ciphertext) {
+  if (g_use_legacy)
+    return EncryptStringWithDPAPI(plaintext, ciphertext);
+
+  crypto::Aead aead(crypto::Aead::AES_256_GCM);
+
+  auto key = GetEncryptionKeyInternal();
+  aead.Init(&key);
+
+  // Note: can only check these once AEAD is initialized.
+  DCHECK_EQ(kKeyLength, aead.KeyLength());
+  DCHECK_EQ(kNonceLength, aead.NonceLength());
+
+  std::string nonce;
+  crypto::RandBytes(base::WriteInto(&nonce, kNonceLength + 1), kNonceLength);
+
+  if (!aead.Seal(plaintext, nonce, std::string(), ciphertext))
+    return false;
+
+  ciphertext->insert(0, nonce);
+  ciphertext->insert(0, kEncryptionVersionPrefix);
+  return true;
+}
+
+// static
+bool OSCrypt::DecryptString(const std::string& ciphertext,
+                            std::string* plaintext) {
+  if (!base::StartsWith(ciphertext, kEncryptionVersionPrefix,
+                        base::CompareCase::SENSITIVE))
+    return DecryptStringWithDPAPI(ciphertext, plaintext);
+
+  crypto::Aead aead(crypto::Aead::AES_256_GCM);
+
+  auto key = GetEncryptionKeyInternal();
+  aead.Init(&key);
+
+  // Obtain the nonce.
+  std::string nonce =
+      ciphertext.substr(sizeof(kEncryptionVersionPrefix) - 1, kNonceLength);
+  // Strip off the versioning prefix before decrypting.
+  std::string raw_ciphertext =
+      ciphertext.substr(kNonceLength + (sizeof(kEncryptionVersionPrefix) - 1));
+
+  return aead.Open(raw_ciphertext, nonce, std::string(), plaintext);
+}
+
+// static
+void OSCrypt::RegisterLocalPrefs(PrefRegistrySimple* registry) {
+  registry->RegisterStringPref(kOsCryptEncryptedKeyPrefName, "");
+}
+
+// static
+bool OSCrypt::Init(PrefService* local_state) {
+  DCHECK(GetEncryptionKeyFactory().empty()) << "Key already exists.";
+  // Try and pull the key from local state.
+  if (local_state->HasPrefPath(kOsCryptEncryptedKeyPrefName)) {
+    std::string base64_encrypted_key =
+        local_state->GetString(kOsCryptEncryptedKeyPrefName);
+    std::string encrypted_key_with_header;
+
+    base::Base64Decode(base64_encrypted_key, &encrypted_key_with_header);
+
+    if (!base::StartsWith(encrypted_key_with_header, kDPAPIKeyPrefix,
+                          base::CompareCase::SENSITIVE)) {
+      NOTREACHED() << "Invalid key format.";
+      return false;
+    }
+    std::string encrypted_key =
+        encrypted_key_with_header.substr(sizeof(kDPAPIKeyPrefix) - 1);
+    std::string key;
+    if (!DecryptStringWithDPAPI(encrypted_key, &key))
+      return false;
+    GetEncryptionKeyFactory().assign(key);
+    return true;
+  }
+
+  // Otherwise, generate a key.
+  std::string key;
+
+  crypto::RandBytes(base::WriteInto(&key, kKeyLength + 1), kKeyLength);
+
+  std::string encrypted_key;
+  if (!EncryptStringWithDPAPI(key, &encrypted_key))
+    return false;
+  // Add header indicating this key is encrypted with DPAPI.
+  encrypted_key.insert(0, kDPAPIKeyPrefix);
+  std::string base64_key;
+  base::Base64Encode(encrypted_key, &base64_key);
+  local_state->SetString(kOsCryptEncryptedKeyPrefName, base64_key);
+  GetEncryptionKeyFactory().assign(key);
+  return true;
+}
+
+// static
+void OSCrypt::SetRawEncryptionKey(const std::string& raw_key) {
+  DCHECK(!g_use_mock_key) << "Mock key in use.";
+  DCHECK(!raw_key.empty()) << "Bad key.";
+  DCHECK(GetEncryptionKeyFactory().empty()) << "Key already set.";
+  GetEncryptionKeyFactory().assign(raw_key);
+}
+
+// static
+std::string OSCrypt::GetRawEncryptionKey() {
+  return GetEncryptionKeyInternal();
+}
+
+// static
+void OSCrypt::UseMockKeyForTesting(bool use_mock) {
+  g_use_mock_key = use_mock;
+}
+
+// static
+void OSCrypt::SetLegacyEncryptionForTesting(bool legacy) {
+  g_use_legacy = legacy;
+}
+
+// static
+void OSCrypt::ResetStateForTesting() {
+  g_use_legacy = false;
+  g_use_mock_key = false;
+  GetEncryptionKeyFactory().clear();
+  GetMockEncryptionKeyFactory().clear();
+}
diff --git a/components/query_parser/query_parser_fuzzer.cc b/components/query_parser/query_parser_fuzzer.cc
index 8cbad965..a8060846 100644
--- a/components/query_parser/query_parser_fuzzer.cc
+++ b/components/query_parser/query_parser_fuzzer.cc
@@ -16,8 +16,12 @@
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
   static Environment env;
-  FuzzedDataProvider data_provider(data, size);
 
+  constexpr size_t kMaxSize = 1 << 16;
+  if (size > kMaxSize)
+    return 0;
+
+  FuzzedDataProvider data_provider(data, size);
   const query_parser::MatchingAlgorithm matching_alg =
       data_provider.ConsumeEnum<query_parser::MatchingAlgorithm>();
   const base::string16 query16 = base::UTF8ToUTF16(
diff --git a/components/safe_browsing/android/java/src/org/chromium/components/safe_browsing/SafeBrowsingApiHandler.java b/components/safe_browsing/android/java/src/org/chromium/components/safe_browsing/SafeBrowsingApiHandler.java
index ba751e3..f12deca7 100644
--- a/components/safe_browsing/android/java/src/org/chromium/components/safe_browsing/SafeBrowsingApiHandler.java
+++ b/components/safe_browsing/android/java/src/org/chromium/components/safe_browsing/SafeBrowsingApiHandler.java
@@ -82,8 +82,5 @@
      *
      * @return true if the uri is found in the corresponding allowlist. Otherwise, false.
      */
-    // TODO(xinghuilu@): remove default once downstream implementation is patched
-    default boolean startAllowlistLookup(String uri, int threatType) {
-        return false;
-    }
+    public boolean startAllowlistLookup(String uri, int threatType);
 }
diff --git a/content/browser/devtools/devtools_session.cc b/content/browser/devtools/devtools_session.cc
index ab21cb14..3cd3039 100644
--- a/content/browser/devtools/devtools_session.cc
+++ b/content/browser/devtools/devtools_session.cc
@@ -324,10 +324,11 @@
     DevToolsAgentHostClient* client,
     DevToolsAgentHostImpl* agent_host,
     std::unique_ptr<protocol::Serializable> message) {
-  std::string cbor = message->serialize(/*binary=*/true);
+  std::vector<uint8_t> cbor = message->serializeToBinary();
   DCHECK(IsCBORMessage(SpanFrom(cbor)));
   if (client->UsesBinaryProtocol()) {
-    client->DispatchProtocolMessage(agent_host, cbor);
+    client->DispatchProtocolMessage(agent_host,
+                                    std::string(cbor.begin(), cbor.end()));
     return;
   }
   std::string json;
diff --git a/content/browser/download/download_manager_impl.cc b/content/browser/download/download_manager_impl.cc
index 2acf127..7ca438f0 100644
--- a/content/browser/download/download_manager_impl.cc
+++ b/content/browser/download/download_manager_impl.cc
@@ -67,7 +67,8 @@
 #include "content/public/common/referrer.h"
 #include "content/public/common/url_utils.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "net/base/elements_upload_data_stream.h"
 #include "net/base/load_flags.h"
 #include "net/base/request_priority.h"
@@ -268,13 +269,12 @@
 std::unique_ptr<network::SharedURLLoaderFactoryInfo>
 CreateSharedURLLoaderFactoryInfoFromURLLoaderFactory(
     std::unique_ptr<network::mojom::URLLoaderFactory> factory) {
-  network::mojom::URLLoaderFactoryPtr factory_ptr;
-  mojo::MakeStrongBinding(std::move(factory), mojo::MakeRequest(&factory_ptr));
-  network::mojom::URLLoaderFactoryPtrInfo factory_ptr_info =
-      factory_ptr.PassInterface();
+  mojo::PendingRemote<network::mojom::URLLoaderFactory> factory_remote;
+  mojo::MakeSelfOwnedReceiver(std::move(factory),
+                              factory_remote.InitWithNewPipeAndPassReceiver());
 
   return std::make_unique<network::WrapperSharedURLLoaderFactoryInfo>(
-      std::move(factory_ptr_info));
+      std::move(factory_remote));
 }
 
 }  // namespace
diff --git a/content/browser/gpu/gpu_data_manager_testing_arrays_and_structs_autogen.h b/content/browser/gpu/gpu_data_manager_testing_arrays_and_structs_autogen.h
index cf4ebe4..67d4ed56 100644
--- a/content/browser/gpu/gpu_data_manager_testing_arrays_and_structs_autogen.h
+++ b/content/browser/gpu/gpu_data_manager_testing_arrays_and_structs_autogen.h
@@ -32,6 +32,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuManagerTestingEntry2[1] = {
@@ -59,6 +60,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuManagerTestingEntry3[1] = {
@@ -79,6 +81,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuManagerTestingEntry4[2] = {
@@ -107,6 +110,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuManagerTestingEntry5[1] = {
@@ -127,6 +131,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const GpuControlList::GLStrings kGLStringsForGpuManagerTestingEntry5Exception0 =
@@ -151,9 +156,10 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
-const int kFeatureListForGpuManagerTestingEntry6[13] = {
+const int kFeatureListForGpuManagerTestingEntry6[14] = {
     GPU_FEATURE_TYPE_FLASH_STAGE3D,
     GPU_FEATURE_TYPE_GPU_COMPOSITING,
     GPU_FEATURE_TYPE_GPU_RASTERIZATION,
@@ -166,6 +172,7 @@
     GPU_FEATURE_TYPE_ACCELERATED_VIDEO_DECODE,
     GPU_FEATURE_TYPE_ANDROID_SURFACE_CONTROL,
     GPU_FEATURE_TYPE_ACCELERATED_WEBGL,
+    GPU_FEATURE_TYPE_VULKAN,
     GPU_FEATURE_TYPE_FLASH_STAGE3D_BASELINE,
 };
 
@@ -183,6 +190,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuManagerTestingEntry7[1] = {
@@ -203,6 +211,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 }  // namespace gpu
diff --git a/content/browser/loader/navigation_url_loader_impl.cc b/content/browser/loader/navigation_url_loader_impl.cc
index a6b226c..4fbdfaf 100644
--- a/content/browser/loader/navigation_url_loader_impl.cc
+++ b/content/browser/loader/navigation_url_loader_impl.cc
@@ -1365,12 +1365,13 @@
       partition->GetURLLoaderFactoryForBrowserProcess()->Clone();
   if (header_client) {
     needs_loader_factory_interceptor = true;
-    network::mojom::URLLoaderFactoryPtrInfo factory_info;
+    mojo::PendingRemote<network::mojom::URLLoaderFactory> factory_remote;
     CreateURLLoaderFactoryWithHeaderClient(
-        std::move(header_client), mojo::MakeRequest(&factory_info), partition);
+        std::move(header_client),
+        factory_remote.InitWithNewPipeAndPassReceiver(), partition);
     network_factory_info =
         std::make_unique<network::WrapperSharedURLLoaderFactoryInfo>(
-            std::move(factory_info));
+            std::move(factory_remote));
   }
 
   DCHECK(!request_controller_);
diff --git a/content/browser/loader/navigation_url_loader_impl.h b/content/browser/loader/navigation_url_loader_impl.h
index 400e80b..31d22b7 100644
--- a/content/browser/loader/navigation_url_loader_impl.h
+++ b/content/browser/loader/navigation_url_loader_impl.h
@@ -15,6 +15,7 @@
 #include "content/public/browser/ssl_status.h"
 #include "content/public/common/previews_state.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/network/public/mojom/url_loader.mojom.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
 
diff --git a/content/browser/renderer_host/media/peer_connection_tracker_host.cc b/content/browser/renderer_host/media/peer_connection_tracker_host.cc
index 5048f9c..c12d484e 100644
--- a/content/browser/renderer_host/media/peer_connection_tracker_host.cc
+++ b/content/browser/renderer_host/media/peer_connection_tracker_host.cc
@@ -9,71 +9,34 @@
 #include "base/task/post_task.h"
 #include "content/browser/renderer_host/render_process_host_impl.h"
 #include "content/browser/webrtc/webrtc_internals.h"
-#include "content/common/media/peer_connection_tracker_messages.h"
-#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
 #include "content/public/browser/webrtc_event_logger.h"
 
 namespace content {
 
-PeerConnectionTrackerHost::PeerConnectionTrackerHost(int render_process_id)
-    : BrowserMessageFilter(PeerConnectionTrackerMsgStart),
-      BrowserAssociatedInterface<mojom::PeerConnectionTrackerHost>(this, this),
-      render_process_id_(render_process_id) {}
+PeerConnectionTrackerHost::PeerConnectionTrackerHost(RenderProcessHost* rph)
+    : render_process_id_(rph->GetID()), peer_pid_(rph->GetProcess().Pid()) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  base::PowerMonitor::AddObserver(this);
 
-bool PeerConnectionTrackerHost::OnMessageReceived(const IPC::Message& message) {
-  bool handled = true;
-
-  IPC_BEGIN_MESSAGE_MAP(PeerConnectionTrackerHost, message)
-    IPC_MESSAGE_HANDLER(PeerConnectionTrackerHost_AddStandardStats,
-                        OnAddStandardStats)
-    IPC_MESSAGE_HANDLER(PeerConnectionTrackerHost_AddLegacyStats,
-                        OnAddLegacyStats)
-    IPC_MESSAGE_UNHANDLED(handled = false)
-  IPC_END_MESSAGE_MAP()
-  return handled;
-}
-
-void PeerConnectionTrackerHost::OverrideThreadForMessage(
-    const IPC::Message& message, BrowserThread::ID* thread) {
-  if (IPC_MESSAGE_CLASS(message) == PeerConnectionTrackerMsgStart)
-    *thread = BrowserThread::UI;
+  mojo::PendingRemote<mojom::PeerConnectionManager> pending_tracker;
+  content::BindInterface(rph, &pending_tracker);
+  tracker_.Bind(std::move(pending_tracker));
 }
 
 PeerConnectionTrackerHost::~PeerConnectionTrackerHost() {
-}
-
-void PeerConnectionTrackerHost::OnChannelConnected(int32_t peer_pid) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  // Add PowerMonitor when connected to channel rather than in constructor due
-  // to thread safety concerns. Observers of PowerMonitor must be added and
-  // removed on the same thread. BrowserMessageFilter is created on the UI
-  // thread but can be destructed on the UI or IO thread because they are
-  // referenced by RenderProcessHostImpl on the UI thread and ChannelProxy on
-  // the IO thread. Using OnChannelConnected and OnChannelClosing guarantees
-  // execution on the IO thread.
-  base::PowerMonitor::AddObserver(this);
-}
-
-void PeerConnectionTrackerHost::OnChannelClosing() {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   base::PowerMonitor::RemoveObserver(this);
 }
 
 void PeerConnectionTrackerHost::AddPeerConnection(
     mojom::PeerConnectionInfoPtr info) {
-  // TODO(crbug.com/787254): Remove the thread jump on all the methods
-  // here, and make sure the mojo pipe is bound on the proper thread instead.
-  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
-    base::PostTask(FROM_HERE, {BrowserThread::UI},
-                   base::BindOnce(&PeerConnectionTrackerHost::AddPeerConnection,
-                                  this, std::move(info)));
-    return;
-  }
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   WebRTCInternals* webrtc_internals = WebRTCInternals::GetInstance();
   if (webrtc_internals) {
     webrtc_internals->OnAddPeerConnection(
-        render_process_id_, peer_pid(), info->lid, info->url,
+        render_process_id_, peer_pid_, info->lid, info->url,
         info->rtc_configuration, info->constraints);
   }
 
@@ -85,16 +48,11 @@
 }
 
 void PeerConnectionTrackerHost::RemovePeerConnection(int lid) {
-  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
-    base::PostTask(
-        FROM_HERE, {BrowserThread::UI},
-        base::BindOnce(&PeerConnectionTrackerHost::RemovePeerConnection, this,
-                       lid));
-    return;
-  }
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
   WebRTCInternals* webrtc_internals = WebRTCInternals::GetInstance();
   if (webrtc_internals) {
-    webrtc_internals->OnRemovePeerConnection(peer_pid(), lid);
+    webrtc_internals->OnRemovePeerConnection(peer_pid_, lid);
   }
   WebRtcEventLogger* const logger = WebRtcEventLogger::Get();
   if (logger) {
@@ -106,13 +64,8 @@
 void PeerConnectionTrackerHost::UpdatePeerConnection(int lid,
                                                      const std::string& type,
                                                      const std::string& value) {
-  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
-    base::PostTask(
-        FROM_HERE, {BrowserThread::UI},
-        base::BindOnce(&PeerConnectionTrackerHost::UpdatePeerConnection, this,
-                       lid, type, value));
-    return;
-  }
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
   // TODO(eladalon): Get rid of magic value. https://crbug.com/810383
   if (type == "stop") {
     WebRtcEventLogger* const logger = WebRtcEventLogger::Get();
@@ -124,20 +77,14 @@
 
   WebRTCInternals* webrtc_internals = WebRTCInternals::GetInstance();
   if (webrtc_internals) {
-    webrtc_internals->OnUpdatePeerConnection(peer_pid(), lid, type, value);
+    webrtc_internals->OnUpdatePeerConnection(peer_pid_, lid, type, value);
   }
 }
 
 void PeerConnectionTrackerHost::OnPeerConnectionSessionIdSet(
     int lid,
     const std::string& session_id) {
-  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
-    base::PostTask(
-        FROM_HERE, {BrowserThread::UI},
-        base::BindOnce(&PeerConnectionTrackerHost::OnPeerConnectionSessionIdSet,
-                       this, lid, session_id));
-    return;
-  }
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   WebRtcEventLogger* const logger = WebRtcEventLogger::Get();
   if (!logger) {
@@ -147,20 +94,29 @@
                                      base::OnceCallback<void(bool)>());
 }
 
-void PeerConnectionTrackerHost::OnAddStandardStats(
-    int lid,
-    const base::ListValue& value) {
+void PeerConnectionTrackerHost::AddStandardStats(int lid, base::Value value) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
   WebRTCInternals* webrtc_internals = WebRTCInternals::GetInstance();
   if (webrtc_internals) {
-    webrtc_internals->OnAddStandardStats(peer_pid(), lid, value);
+    // Churn to convert from base::Value to std::unique_ptr<base::ListValue>.
+    auto value_ptr = base::Value::ToUniquePtrValue(std::move(value));
+    auto list_value_ptr = base::ListValue::From(std::move(value_ptr));
+    webrtc_internals->OnAddStandardStats(peer_pid_, lid,
+                                         std::move(*list_value_ptr.get()));
   }
 }
 
-void PeerConnectionTrackerHost::OnAddLegacyStats(int lid,
-                                                 const base::ListValue& value) {
+void PeerConnectionTrackerHost::AddLegacyStats(int lid, base::Value value) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
   WebRTCInternals* webrtc_internals = WebRTCInternals::GetInstance();
   if (webrtc_internals) {
-    webrtc_internals->OnAddLegacyStats(peer_pid(), lid, value);
+    // Churn to convert from base::Value to std::unique_ptr<base::ListValue>.
+    auto value_ptr = base::Value::ToUniquePtrValue(std::move(value));
+    auto list_value_ptr = base::ListValue::From(std::move(value_ptr));
+    webrtc_internals->OnAddLegacyStats(peer_pid_, lid,
+                                       std::move(*list_value_ptr.get()));
   }
 }
 
@@ -170,16 +126,11 @@
     bool video,
     const std::string& audio_constraints,
     const std::string& video_constraints) {
-  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
-    base::PostTask(
-        FROM_HERE, {BrowserThread::UI},
-        base::BindOnce(&PeerConnectionTrackerHost::GetUserMedia, this, origin,
-                       audio, video, audio_constraints, video_constraints));
-    return;
-  }
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
   WebRTCInternals* webrtc_internals = WebRTCInternals::GetInstance();
   if (webrtc_internals) {
-    webrtc_internals->OnGetUserMedia(render_process_id_, peer_pid(), origin,
+    webrtc_internals->OnGetUserMedia(render_process_id_, peer_pid_, origin,
                                      audio, video, audio_constraints,
                                      video_constraints);
   }
@@ -187,13 +138,8 @@
 
 void PeerConnectionTrackerHost::WebRtcEventLogWrite(int lid,
                                                     const std::string& output) {
-  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
-    base::PostTask(
-        FROM_HERE, {BrowserThread::UI},
-        base::BindOnce(&PeerConnectionTrackerHost::WebRtcEventLogWrite, this,
-                       lid, output));
-    return;
-  }
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
   WebRtcEventLogger* const logger = WebRtcEventLogger::Get();
   if (logger) {
     logger->OnWebRtcEventLogWrite(
@@ -203,18 +149,35 @@
 }
 
 void PeerConnectionTrackerHost::OnSuspend() {
-  base::PostTask(
-      FROM_HERE, {BrowserThread::UI},
-      base::BindOnce(&PeerConnectionTrackerHost::SendOnSuspendOnUIThread,
-                     this));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  tracker_->OnSuspend();
 }
 
-void PeerConnectionTrackerHost::SendOnSuspendOnUIThread() {
+void PeerConnectionTrackerHost::StartEventLog(int lid, int output_period_ms) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  content::RenderProcessHost* host =
-      content::RenderProcessHost::FromID(render_process_id_);
-  if (host)
-    host->Send(new PeerConnectionTracker_OnSuspend());
+  tracker_->StartEventLog(lid, output_period_ms);
+}
+
+void PeerConnectionTrackerHost::StopEventLog(int lid) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  tracker_->StopEventLog(lid);
+}
+
+void PeerConnectionTrackerHost::GetStandardStats() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  tracker_->GetStandardStats();
+}
+
+void PeerConnectionTrackerHost::GetLegacyStats() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  tracker_->GetLegacyStats();
+}
+
+void PeerConnectionTrackerHost::BindReceiver(
+    mojo::PendingReceiver<mojom::PeerConnectionTrackerHost> pending_receiver) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  receiver_.reset();
+  receiver_.Bind(std::move(pending_receiver));
 }
 
 }  // namespace content
diff --git a/content/browser/renderer_host/media/peer_connection_tracker_host.h b/content/browser/renderer_host/media/peer_connection_tracker_host.h
index 62a5f60..268af21 100644
--- a/content/browser/renderer_host/media/peer_connection_tracker_host.h
+++ b/content/browser/renderer_host/media/peer_connection_tracker_host.h
@@ -9,48 +9,43 @@
 
 #include "base/macros.h"
 #include "base/power_monitor/power_observer.h"
+#include "base/process/process_handle.h"
 #include "content/common/media/peer_connection_tracker.mojom.h"
-#include "content/public/browser/browser_associated_interface.h"
-#include "content/public/browser/browser_message_filter.h"
-#include "content/public/browser/browser_thread.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
 
 namespace base {
-class ListValue;
+class Value;
 }  // namespace base
 
 namespace content {
 
+class RenderProcessHost;
+
 // This class is the host for PeerConnectionTracker in the browser process
 // managed by RenderProcessHostImpl. It receives PeerConnection events from
 // PeerConnectionTracker as IPC messages that it forwards to WebRTCInternals.
 // It also forwards browser process events to PeerConnectionTracker via IPC.
-class PeerConnectionTrackerHost
-    : public BrowserMessageFilter,
-      public base::PowerObserver,
-      public BrowserAssociatedInterface<mojom::PeerConnectionTrackerHost>,
-      public mojom::PeerConnectionTrackerHost {
+class PeerConnectionTrackerHost : public base::PowerObserver,
+                                  public mojom::PeerConnectionTrackerHost {
  public:
-  explicit PeerConnectionTrackerHost(int render_process_id);
-
-  // content::BrowserMessageFilter override.
-  bool OnMessageReceived(const IPC::Message& message) override;
-  void OverrideThreadForMessage(const IPC::Message& message,
-                                BrowserThread::ID* thread) override;
-  void OnChannelConnected(int32_t peer_pid) override;
-  void OnChannelClosing() override;
+  explicit PeerConnectionTrackerHost(RenderProcessHost* rph);
+  ~PeerConnectionTrackerHost() override;
 
   // base::PowerObserver override.
   void OnSuspend() override;
 
- protected:
-  ~PeerConnectionTrackerHost() override;
+  // These methods call out to mojom::PeerConnectionManager on renderer side.
+  void StartEventLog(int peer_connection_local_id, int output_period_ms);
+  void StopEventLog(int lid);
+  void GetStandardStats();
+  void GetLegacyStats();
+
+  void BindReceiver(
+      mojo::PendingReceiver<mojom::PeerConnectionTrackerHost> pending_receiver);
 
  private:
-  // Handlers for IPC messages coming from the renderer.
-  void OnAddStandardStats(int lid, const base::ListValue& value);
-  void OnAddLegacyStats(int lid, const base::ListValue& value);
-  void SendOnSuspendOnUIThread();
-
   // mojom::PeerConnectionTrackerHost implementation.
   void AddPeerConnection(mojom::PeerConnectionInfoPtr info) override;
   void RemovePeerConnection(int lid) override;
@@ -65,8 +60,13 @@
                     const std::string& audio_constraints,
                     const std::string& video_constraints) override;
   void WebRtcEventLogWrite(int lid, const std::string& output) override;
+  void AddStandardStats(int lid, base::Value value) override;
+  void AddLegacyStats(int lid, base::Value value) override;
 
   int render_process_id_;
+  base::ProcessId peer_pid_;
+  mojo::Receiver<mojom::PeerConnectionTrackerHost> receiver_{this};
+  mojo::Remote<mojom::PeerConnectionManager> tracker_;
 
   DISALLOW_COPY_AND_ASSIGN(PeerConnectionTrackerHost);
 };
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 72d89244..e25b8ee5 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -142,7 +142,6 @@
 #include "content/common/content_switches_internal.h"
 #include "content/common/frame_messages.h"
 #include "content/common/in_process_child_thread_params.h"
-#include "content/common/media/peer_connection_tracker_messages.h"
 #include "content/common/resource_messages.h"
 #include "content/common/service_manager/child_connection.h"
 #include "content/common/service_manager/service_manager_connection_impl.h"
@@ -1848,8 +1847,6 @@
       GetBrowserContext(), storage_partition_impl_, widget_helper_.get());
   AddFilter(render_frame_message_filter_.get());
 
-  peer_connection_tracker_host_ = new PeerConnectionTrackerHost(GetID());
-  AddFilter(peer_connection_tracker_host_.get());
 #if BUILDFLAG(ENABLE_PLUGINS)
   AddFilter(new PepperRendererConnection(GetID()));
 #endif
@@ -1982,6 +1979,15 @@
   cleanup_corb_exception_for_plugin_upon_destruction_ = true;
 }
 
+PeerConnectionTrackerHost*
+RenderProcessHostImpl::GetPeerConnectionTrackerHost() {
+  if (!peer_connection_tracker_host_) {
+    peer_connection_tracker_host_ =
+        std::make_unique<PeerConnectionTrackerHost>(this);
+  }
+  return peer_connection_tracker_host_.get();
+}
+
 void RenderProcessHostImpl::RegisterMojoInterfaces() {
   auto registry = std::make_unique<service_manager::BinderRegistry>();
 
@@ -2156,6 +2162,11 @@
 
   AddUIThreadInterface(
       registry.get(),
+      base::BindRepeating(&RenderProcessHostImpl::BindPeerConnectionTrackerHost,
+                          base::Unretained(this)));
+
+  AddUIThreadInterface(
+      registry.get(),
       base::BindRepeating(
           &CodeCacheHostImpl::Create, GetID(),
           base::RetainedRef(storage_partition_impl_->GetCacheStorageContext()),
@@ -3047,7 +3058,6 @@
     switches::kEnablePreferCompositingToLCDText,
     switches::kEnableRGBA4444Textures,
     switches::kEnableSkiaBenchmarking,
-    switches::kEnableSubresourceRedirect,
     switches::kEnableThreadedCompositing,
     switches::kEnableTouchDragDrop,
     switches::kEnableUnsafeWebGPU,
@@ -3071,7 +3081,6 @@
     switches::kFullMemoryCrashReport,
     switches::kIPCConnectionTimeout,
     switches::kJavaScriptFlags,
-    switches::kLitePagesServerSubresourceHost,
     switches::kLogBestEffortTasks,
     switches::kLogFile,
     switches::kLoggingLevel,
@@ -3637,11 +3646,11 @@
 
 void RenderProcessHostImpl::EnableWebRtcEventLogOutput(int lid,
                                                        int output_period_ms) {
-  Send(new PeerConnectionTracker_StartEventLog(lid, output_period_ms));
+  GetPeerConnectionTrackerHost()->StartEventLog(lid, output_period_ms);
 }
 
 void RenderProcessHostImpl::DisableWebRtcEventLogOutput(int lid) {
-  Send(new PeerConnectionTracker_StopEventLog(lid));
+  GetPeerConnectionTrackerHost()->StopEventLog(lid);
 }
 
 IPC::ChannelProxy* RenderProcessHostImpl::GetChannel() {
@@ -4701,6 +4710,11 @@
   media_stream_track_metrics_host_->BindReceiver(std::move(receiver));
 }
 
+void RenderProcessHostImpl::BindPeerConnectionTrackerHost(
+    mojo::PendingReceiver<mojom::PeerConnectionTrackerHost> receiver) {
+  GetPeerConnectionTrackerHost()->BindReceiver(std::move(receiver));
+}
+
 #if BUILDFLAG(ENABLE_MDNS)
 void RenderProcessHostImpl::CreateMdnsResponder(
     mojo::PendingReceiver<network::mojom::MdnsResponder> receiver) {
diff --git a/content/browser/renderer_host/render_process_host_impl.h b/content/browser/renderer_host/render_process_host_impl.h
index 4c19196..c150bf2 100644
--- a/content/browser/renderer_host/render_process_host_impl.h
+++ b/content/browser/renderer_host/render_process_host_impl.h
@@ -41,6 +41,7 @@
 #include "content/common/associated_interfaces.mojom.h"
 #include "content/common/child_process.mojom.h"
 #include "content/common/content_export.h"
+#include "content/common/media/peer_connection_tracker.mojom.h"
 #include "content/common/media/renderer_audio_output_stream_factory.mojom.h"
 #include "content/common/renderer.mojom.h"
 #include "content/common/renderer_host.mojom.h"
@@ -567,6 +568,8 @@
 
   size_t keep_alive_ref_count() const { return keep_alive_ref_count_; }
 
+  PeerConnectionTrackerHost* GetPeerConnectionTrackerHost();
+
  protected:
   // A proxy for our IPC::Channel that lives on the IO thread.
   std::unique_ptr<IPC::ChannelProxy> channel_;
@@ -741,6 +744,9 @@
       mojo::PendingReceiver<blink::mojom::MediaStreamTrackMetricsHost>
           receiver);
 
+  void BindPeerConnectionTrackerHost(
+      mojo::PendingReceiver<mojom::PeerConnectionTrackerHost> receiver);
+
 #if BUILDFLAG(ENABLE_MDNS)
   void CreateMdnsResponder(
       mojo::PendingReceiver<network::mojom::MdnsResponder> receiver);
@@ -976,9 +982,7 @@
 
   // Forwards messages between WebRTCInternals in the browser process
   // and PeerConnectionTracker in the renderer process.
-  // It holds a raw pointer to webrtc_eventlog_host_, and therefore should be
-  // defined below it so it is destructed first.
-  scoped_refptr<PeerConnectionTrackerHost> peer_connection_tracker_host_;
+  std::unique_ptr<PeerConnectionTrackerHost> peer_connection_tracker_host_;
 
   // Records the time when the process starts surviving for workers for UMA.
   base::TimeTicks keep_alive_start_time_;
diff --git a/content/browser/service_worker/embedded_worker_instance.cc b/content/browser/service_worker/embedded_worker_instance.cc
index b39886155..bf250df 100644
--- a/content/browser/service_worker/embedded_worker_instance.cc
+++ b/content/browser/service_worker/embedded_worker_instance.cc
@@ -36,7 +36,7 @@
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_switches.h"
 #include "ipc/ipc_message.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/mojom/loader/url_loader_factory_bundle.mojom.h"
 #include "third_party/blink/public/mojom/renderer_preference_watcher.mojom.h"
@@ -662,8 +662,8 @@
     // this is a non-installed service worker.
     DCHECK(factory_bundle_for_new_scripts || is_installed_);
     if (factory_bundle_for_new_scripts) {
-      params->provider_info->script_loader_factory_ptr_info =
-          instance_->MakeScriptLoaderFactoryPtrInfo(
+      params->provider_info->script_loader_factory_remote =
+          instance_->MakeScriptLoaderFactoryRemote(
               std::move(factory_bundle_for_new_scripts));
     }
 
@@ -1296,21 +1296,22 @@
   }
 }
 
-network::mojom::URLLoaderFactoryPtrInfo
-EmbeddedWorkerInstance::MakeScriptLoaderFactoryPtrInfo(
+mojo::PendingRemote<network::mojom::URLLoaderFactory>
+EmbeddedWorkerInstance::MakeScriptLoaderFactoryRemote(
     std::unique_ptr<blink::URLLoaderFactoryBundleInfo> script_bundle) {
-  network::mojom::URLLoaderFactoryPtrInfo script_loader_factory_ptr_info;
+  mojo::PendingRemote<network::mojom::URLLoaderFactory>
+      script_loader_factory_remote;
 
   auto script_bundle_factory =
       base::MakeRefCounted<blink::URLLoaderFactoryBundle>(
           std::move(script_bundle));
-  script_loader_factory_ = mojo::MakeStrongBinding(
+  script_loader_factory_ = mojo::MakeSelfOwnedReceiver(
       std::make_unique<ServiceWorkerScriptLoaderFactory>(
           context_, owner_version_->provider_host()->AsWeakPtr(),
           std::move(script_bundle_factory)),
-      mojo::MakeRequest(&script_loader_factory_ptr_info));
+      script_loader_factory_remote.InitWithNewPipeAndPassReceiver());
 
-  return script_loader_factory_ptr_info;
+  return script_loader_factory_remote;
 }
 
 }  // namespace content
diff --git a/content/browser/service_worker/embedded_worker_instance.h b/content/browser/service_worker/embedded_worker_instance.h
index d8523ee..5c7b458d 100644
--- a/content/browser/service_worker/embedded_worker_instance.h
+++ b/content/browser/service_worker/embedded_worker_instance.h
@@ -315,7 +315,8 @@
   void NotifyForegroundServiceWorkerAdded();
   void NotifyForegroundServiceWorkerRemoved();
 
-  network::mojom::URLLoaderFactoryPtrInfo MakeScriptLoaderFactoryPtrInfo(
+  mojo::PendingRemote<network::mojom::URLLoaderFactory>
+  MakeScriptLoaderFactoryRemote(
       std::unique_ptr<blink::URLLoaderFactoryBundleInfo> script_bundle);
 
   base::WeakPtr<ServiceWorkerContextCore> context_;
diff --git a/content/browser/service_worker/fake_embedded_worker_instance_client.cc b/content/browser/service_worker/fake_embedded_worker_instance_client.cc
index 0db7356..f6f1633 100644
--- a/content/browser/service_worker/fake_embedded_worker_instance_client.cc
+++ b/content/browser/service_worker/fake_embedded_worker_instance_client.cc
@@ -80,7 +80,7 @@
   // storage. We do that manually here.
   //
   // TODO(falken): For new workers, this should use
-  // |script_loader_factory_ptr_info| from |start_params_->provider_info|
+  // |script_loader_factory_remote| from |start_params_->provider_info|
   // to request the script and the browser process should be able to mock it.
   // For installed workers, the map should already be populated.
   ServiceWorkerVersion* version = helper_->context()->GetLiveVersion(
diff --git a/content/browser/webrtc/webrtc_internals_message_handler.cc b/content/browser/webrtc/webrtc_internals_message_handler.cc
index 62fa8da..b128afe 100644
--- a/content/browser/webrtc/webrtc_internals_message_handler.cc
+++ b/content/browser/webrtc/webrtc_internals_message_handler.cc
@@ -6,8 +6,9 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
+#include "content/browser/renderer_host/media/peer_connection_tracker_host.h"
+#include "content/browser/renderer_host/render_process_host_impl.h"
 #include "content/browser/webrtc/webrtc_internals.h"
-#include "content/common/media/peer_connection_tracker_messages.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
@@ -93,7 +94,9 @@
   for (RenderProcessHost::iterator i(
            content::RenderProcessHost::AllHostsIterator());
        !i.IsAtEnd(); i.Advance()) {
-    i.GetCurrentValue()->Send(new PeerConnectionTracker_GetStandardStats());
+    auto* render_process_host =
+        static_cast<RenderProcessHostImpl*>(i.GetCurrentValue());
+    render_process_host->GetPeerConnectionTrackerHost()->GetStandardStats();
   }
 }
 
@@ -102,7 +105,9 @@
   for (RenderProcessHost::iterator i(
        content::RenderProcessHost::AllHostsIterator());
        !i.IsAtEnd(); i.Advance()) {
-    i.GetCurrentValue()->Send(new PeerConnectionTracker_GetLegacyStats());
+    auto* render_process_host =
+        static_cast<RenderProcessHostImpl*>(i.GetCurrentValue());
+    render_process_host->GetPeerConnectionTrackerHost()->GetLegacyStats();
   }
 }
 
diff --git a/content/browser/webrtc/webrtc_video_capture_service_browsertest.cc b/content/browser/webrtc/webrtc_video_capture_service_browsertest.cc
index 3f3ab1d..2ebf766 100644
--- a/content/browser/webrtc/webrtc_video_capture_service_browsertest.cc
+++ b/content/browser/webrtc/webrtc_video_capture_service_browsertest.cc
@@ -238,16 +238,16 @@
  public:
   explicit SharedMemoryDeviceExerciser(
       media::mojom::PlaneStridesPtr strides = nullptr)
-      : strides_(std::move(strides)), producer_binding_(this) {}
+      : strides_(std::move(strides)) {}
 
   // VirtualDeviceExerciser implementation.
   void Initialize() override {}
   void RegisterVirtualDeviceAtFactory(
       video_capture::mojom::DeviceFactoryPtr* factory,
       const media::VideoCaptureDeviceInfo& info) override {
-    video_capture::mojom::ProducerPtr producer;
+    mojo::PendingRemote<video_capture::mojom::Producer> producer;
     static const bool kSendBufferHandlesToProducerAsRawFileDescriptors = false;
-    producer_binding_.Bind(mojo::MakeRequest(&producer));
+    producer_receiver_.Bind(producer.InitWithNewPipeAndPassReceiver());
     (*factory)->AddSharedMemoryVirtualDevice(
         info, std::move(producer),
         kSendBufferHandlesToProducerAsRawFileDescriptors,
@@ -266,7 +266,7 @@
   }
   void ShutDown() override {
     virtual_device_ = nullptr;
-    producer_binding_.Close();
+    producer_receiver_.reset();
     weak_factory_.InvalidateWeakPtrs();
   }
 
@@ -387,7 +387,7 @@
   }
 
   media::mojom::PlaneStridesPtr strides_;
-  mojo::Binding<video_capture::mojom::Producer> producer_binding_;
+  mojo::Receiver<video_capture::mojom::Producer> producer_receiver_{this};
   video_capture::mojom::SharedMemoryVirtualDevicePtr virtual_device_;
   std::map<int32_t /*buffer_id*/, base::WritableSharedMemoryMapping>
       outgoing_buffer_id_to_buffer_map_;
diff --git a/content/browser/webrtc/webrtc_video_capture_service_enumeration_browsertest.cc b/content/browser/webrtc/webrtc_video_capture_service_enumeration_browsertest.cc
index fa160bf6..263959b 100644
--- a/content/browser/webrtc/webrtc_video_capture_service_enumeration_browsertest.cc
+++ b/content/browser/webrtc/webrtc_video_capture_service_enumeration_browsertest.cc
@@ -102,20 +102,21 @@
     closure_to_be_called_on_devices_changed_ = wait_loop.QuitClosure();
     switch (GetParam().device_type) {
       case VirtualDeviceType::kSharedMemory: {
-        video_capture::mojom::SharedMemoryVirtualDevicePtr virtual_device;
-        video_capture::mojom::ProducerPtr producer;
+        mojo::PendingRemote<video_capture::mojom::SharedMemoryVirtualDevice>
+            virtual_device;
+        mojo::PendingRemote<video_capture::mojom::Producer> producer;
         auto mock_producer = std::make_unique<video_capture::MockProducer>(
-            mojo::MakeRequest(&producer));
+            producer.InitWithNewPipeAndPassReceiver());
         switch (GetParam().api_to_use) {
           case ServiceApi::kSingleClient:
             factory_->AddSharedMemoryVirtualDevice(
                 info, std::move(producer), false,
-                mojo::MakeRequest(&virtual_device));
+                virtual_device.InitWithNewPipeAndPassReceiver());
             break;
           case ServiceApi::kMultiClient:
             video_source_provider_->AddSharedMemoryVirtualDevice(
                 info, std::move(producer), false,
-                mojo::MakeRequest(&virtual_device));
+                virtual_device.InitWithNewPipeAndPassReceiver());
             break;
         }
         shared_memory_devices_by_id_.insert(std::make_pair(
diff --git a/content/browser/worker_host/worker_script_loader_factory_unittest.cc b/content/browser/worker_host/worker_script_loader_factory_unittest.cc
index 63e03331..8893bd0e 100644
--- a/content/browser/worker_host/worker_script_loader_factory_unittest.cc
+++ b/content/browser/worker_host/worker_script_loader_factory_unittest.cc
@@ -13,6 +13,7 @@
 #include "content/browser/service_worker/service_worker_navigation_handle_core.h"
 #include "content/public/test/browser_task_environment.h"
 #include "content/test/fake_network_url_loader_factory.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "net/base/network_isolation_key.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "services/network/public/cpp/wrapper_shared_url_loader_factory.h"
@@ -46,8 +47,9 @@
     // Set up the network factory.
     network_loader_factory_instance_ =
         std::make_unique<FakeNetworkURLLoaderFactory>();
-    network::mojom::URLLoaderFactoryPtrInfo factory;
-    network_loader_factory_instance_->Clone(mojo::MakeRequest(&factory));
+    mojo::PendingRemote<network::mojom::URLLoaderFactory> factory;
+    network_loader_factory_instance_->Clone(
+        factory.InitWithNewPipeAndPassReceiver());
     auto info = std::make_unique<network::WrapperSharedURLLoaderFactoryInfo>(
         std::move(factory));
     network_loader_factory_ =
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index 741dd4e..59199e3f 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -180,7 +180,6 @@
     "mac/font_loader.mm",
     "media/cdm_info.cc",
     "media/media_player_delegate_messages.h",
-    "media/peer_connection_tracker_messages.h",
     "navigation_gesture.h",
     "navigation_params.cc",
     "navigation_params.h",
diff --git a/content/common/content_message_generator.h b/content/common/content_message_generator.h
index 74af49b2..ac92e2d 100644
--- a/content/common/content_message_generator.h
+++ b/content/common/content_message_generator.h
@@ -42,12 +42,6 @@
 #ifndef CONTENT_COMMON_MEDIA_MEDIA_PLAYER_DELEGATE_MESSAGES_H_
 #error "Failed to include content/common/media/media_player_delegate_messages.h"
 #endif
-#undef CONTENT_COMMON_MEDIA_PEER_CONNECTION_TRACKER_MESSAGES_H_
-#include "content/common/media/peer_connection_tracker_messages.h"
-#ifndef CONTENT_COMMON_MEDIA_PEER_CONNECTION_TRACKER_MESSAGES_H_
-#error \
-    "Failed to include content/common/media/peer_connection_tracker_messages.h"
-#endif
 #undef CONTENT_COMMON_PAGE_MESSAGES_H_
 #include "content/common/page_messages.h"
 #ifndef CONTENT_COMMON_PAGE_MESSAGES_H_
diff --git a/content/common/media/peer_connection_tracker.mojom b/content/common/media/peer_connection_tracker.mojom
index 020e836..d2c4a27 100644
--- a/content/common/media/peer_connection_tracker.mojom
+++ b/content/common/media/peer_connection_tracker.mojom
@@ -4,6 +4,8 @@
 
 module content.mojom;
 
+import "mojo/public/mojom/base/values.mojom";
+
 struct PeerConnectionInfo {
   // ID of the peer connection. Unique only within the renderer process.
   int32 lid;
@@ -19,6 +21,25 @@
   string url;
 };
 
+// This interface collects data about each peer connection and sends it to the
+// browser process.
+interface PeerConnectionManager {
+    // Called when the browser process reports a suspend event from the OS.
+    OnSuspend();
+
+    // Enable WebRtc event log output.
+    StartEventLog(int32 peer_connection_local_id, int32 output_period_ms);
+
+    // Disable WebRtc event log output.
+    StopEventLog(int32 peer_connection_local_id);
+
+    // Requests standard PeerConnection stats for webrtc-internals.
+    GetStandardStats();
+
+    // Requests legacy PeerConnection stats for webrtc-internals.
+    GetLegacyStats();
+};
+
 // This interface allows forwarding PeerConnection events to WebRTCInternals in
 // the browser process.
 interface PeerConnectionTrackerHost {
@@ -59,4 +80,14 @@
 
   // Logs WebRtc log for the connection id.
   WebRtcEventLogWrite(int32 lid, string output);
+
+  // Adds standard PeerConnection stats for webrtc-internals.
+  // |lid| is the renderer local id,
+  // |value| is the list of stats reports.
+  AddStandardStats(int32 lid, mojo_base.mojom.ListValue value);
+
+  // Adds legacy PeerConnection stats for webrtc-internals.
+  // |lid| is the renderer local id,
+  // |value| is the list of stats reports.
+  AddLegacyStats(int32 lid, mojo_base.mojom.ListValue value);
 };
diff --git a/content/common/media/peer_connection_tracker_messages.h b/content/common/media/peer_connection_tracker_messages.h
deleted file mode 100644
index 0c721cb..0000000
--- a/content/common/media/peer_connection_tracker_messages.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_COMMON_MEDIA_PEER_CONNECTION_TRACKER_MESSAGES_H_
-#define CONTENT_COMMON_MEDIA_PEER_CONNECTION_TRACKER_MESSAGES_H_
-
-#include "base/values.h"
-#include "content/common/content_export.h"
-#include "ipc/ipc_message_macros.h"
-#include "ipc/ipc_platform_file.h"
-
-#undef IPC_MESSAGE_EXPORT
-#define IPC_MESSAGE_EXPORT CONTENT_EXPORT
-#define IPC_MESSAGE_START PeerConnectionTrackerMsgStart
-
-// Messages sent from PeerConnectionTracker to PeerConnectionTrackerHost.
-IPC_MESSAGE_CONTROL2(PeerConnectionTrackerHost_AddStandardStats,
-                     int /* lid */,
-                     base::ListValue /* value */)
-IPC_MESSAGE_CONTROL2(PeerConnectionTrackerHost_AddLegacyStats,
-                     int /* lid */,
-                     base::ListValue /* value */)
-
-// Messages sent to PeerConnectionTracker.
-IPC_MESSAGE_CONTROL0(PeerConnectionTracker_GetStandardStats)
-IPC_MESSAGE_CONTROL0(PeerConnectionTracker_GetLegacyStats)
-IPC_MESSAGE_CONTROL0(PeerConnectionTracker_OnSuspend)
-IPC_MESSAGE_CONTROL2(PeerConnectionTracker_StartEventLog,
-                     int /* peer_connection_local_id */,
-                     int /* output_period_ms */)
-IPC_MESSAGE_CONTROL1(PeerConnectionTracker_StopEventLog,
-                     int /* peer_connection_local_id */)
-
-#endif  // CONTENT_COMMON_MEDIA_PEER_CONNECTION_TRACKER_MESSAGES_H_
diff --git a/content/public/app/content_renderer_manifest.cc b/content/public/app/content_renderer_manifest.cc
index 625e51f..151b88b 100644
--- a/content/public/app/content_renderer_manifest.cc
+++ b/content/public/app/content_renderer_manifest.cc
@@ -32,6 +32,7 @@
                   "content.mojom.ChildProcess",
                   "content.mojom.FrameFactory",
                   "content.mojom.MhtmlFileWriter",
+                  "content.mojom.PeerConnectionManager",
                   "content.mojom.RenderWidgetWindowTreeClientFactory",
                   "content.mojom.ResourceUsageReporter",
                   "IPC.mojom.ChannelBootstrap",
diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc
index d0e2b583..8451b01 100644
--- a/content/public/common/content_switches.cc
+++ b/content/public/common/content_switches.cc
@@ -423,10 +423,6 @@
 const char kEnableStrictPowerfulFeatureRestrictions[] =
     "enable-strict-powerful-feature-restrictions";
 
-// Feature flag to enable HTTPS subresource internal redirects to compressed
-// versions.
-const char kEnableSubresourceRedirect[] = "enable-subresource-redirect";
-
 // Enabled threaded compositing for web tests.
 const char kEnableThreadedCompositing[]     = "enable-threaded-compositing";
 
@@ -564,10 +560,6 @@
 // Flag to launch tests in the browser process.
 const char kLaunchAsBrowser[] = "as-browser";
 
-// Overrides the Lite Page Subresource host.
-const char kLitePagesServerSubresourceHost[] =
-    "litepage-server-subresource-host";
-
 // Logs GPU control list decisions when enforcing blacklist rules.
 const char kLogGpuControlListDecisions[]    = "log-gpu-control-list-decisions";
 
diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h
index 20acdce1..20c20a1 100644
--- a/content/public/common/content_switches.h
+++ b/content/public/common/content_switches.h
@@ -132,7 +132,6 @@
 CONTENT_EXPORT extern const char kEnableSpatialNavigation[];
 CONTENT_EXPORT extern const char kEnableStrictMixedContentChecking[];
 CONTENT_EXPORT extern const char kEnableStrictPowerfulFeatureRestrictions[];
-CONTENT_EXPORT extern const char kEnableSubresourceRedirect[];
 CONTENT_EXPORT extern const char kEnableThreadedCompositing[];
 CONTENT_EXPORT extern const char kEnableTracing[];
 CONTENT_EXPORT extern const char kEnableTracingOutput[];
@@ -168,7 +167,6 @@
 CONTENT_EXPORT extern const char kJavaScriptFlags[];
 CONTENT_EXPORT extern const char kJavaScriptHarmony[];
 CONTENT_EXPORT extern const char kLaunchAsBrowser[];
-CONTENT_EXPORT extern const char kLitePagesServerSubresourceHost[];
 CONTENT_EXPORT extern const char kLogGpuControlListDecisions[];
 CONTENT_EXPORT extern const char kLoggingLevel[];
 CONTENT_EXPORT extern const char kLogFile[];
diff --git a/content/public/common/previews_state.cc b/content/public/common/previews_state.cc
index 34f598a..02e00399 100644
--- a/content/public/common/previews_state.cc
+++ b/content/public/common/previews_state.cc
@@ -34,7 +34,9 @@
                             blink::WebURLRequest::kLazyImageLoadDeferred);
 STATIC_ASSERT_PREVIEWS_ENUM(LAZY_IMAGE_AUTO_RELOAD,
                             blink::WebURLRequest::kLazyImageAutoReload);
+STATIC_ASSERT_PREVIEWS_ENUM(SUBRESOURCE_REDIRECT_ON,
+                            blink::WebURLRequest::kSubresourceRedirectOn);
 STATIC_ASSERT_PREVIEWS_ENUM(PREVIEWS_STATE_LAST,
-                            blink::WebURLRequest::kPreviewsStateLast);
+                            blink::WebURLRequest::kSubresourceRedirectOn);
 
 }  // namespace content
diff --git a/content/public/common/previews_state.h b/content/public/common/previews_state.h
index 11e6bbb..888cb7a 100644
--- a/content/public/common/previews_state.h
+++ b/content/public/common/previews_state.h
@@ -45,7 +45,10 @@
                                      // getting a lazy load placeholder.
   DEFER_ALL_SCRIPT_ON = 1 << 12,  // Request that script execution be deferred
                                   // until parsing completes.
-  PREVIEWS_STATE_LAST = DEFER_ALL_SCRIPT_ON
+  SUBRESOURCE_REDIRECT_ON =
+      1 << 13,  // Allow the subresources in the page to be redirected to
+                // serve better optimized resources. Set on subresources.
+  PREVIEWS_STATE_LAST = SUBRESOURCE_REDIRECT_ON
 };
 
 // Combination of all previews that are guaranteed not to provide partial
diff --git a/content/renderer/media/webrtc/peer_connection_tracker.cc b/content/renderer/media/webrtc/peer_connection_tracker.cc
index dcaa305..81cf653 100644
--- a/content/renderer/media/webrtc/peer_connection_tracker.cc
+++ b/content/renderer/media/webrtc/peer_connection_tracker.cc
@@ -14,14 +14,12 @@
 
 #include "base/bind.h"
 #include "base/lazy_instance.h"
-#include "base/numerics/safe_conversions.h"
 #include "base/strings/string_number_conversions.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/threading/thread_task_runner_handle.h"
 #include "base/values.h"
-#include "content/common/media/peer_connection_tracker_messages.h"
 #include "content/renderer/media/webrtc/rtc_peer_connection_handler.h"
 #include "content/renderer/render_thread_impl.h"
+#include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h"
+#include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_media_constraints.h"
 #include "third_party/blink/public/platform/web_media_stream.h"
 #include "third_party/blink/public/platform/web_media_stream_source.h"
@@ -463,8 +461,11 @@
  public:
   InternalLegacyStatsObserver(
       int lid,
-      scoped_refptr<base::SingleThreadTaskRunner> main_thread)
-      : lid_(lid), main_thread_(std::move(main_thread)) {}
+      scoped_refptr<base::SingleThreadTaskRunner> main_thread,
+      base::OnceCallback<void(int, base::Value)> completion_callback)
+      : lid_(lid),
+        main_thread_(std::move(main_thread)),
+        completion_callback_(std::move(completion_callback)) {}
 
   void OnComplete(const StatsReports& reports) override {
     std::unique_ptr<base::ListValue> list(new base::ListValue());
@@ -479,7 +480,8 @@
       main_thread_->PostTask(
           FROM_HERE,
           base::BindOnce(&InternalLegacyStatsObserver::OnCompleteImpl,
-                         std::move(list), lid_));
+                         std::move(list), lid_,
+                         std::move(completion_callback_)));
     }
   }
 
@@ -494,14 +496,17 @@
  private:
   // Static since |this| will most likely have been deleted by the time we
   // get here.
-  static void OnCompleteImpl(std::unique_ptr<base::ListValue> list, int lid) {
+  static void OnCompleteImpl(
+      std::unique_ptr<base::ListValue> list,
+      int lid,
+      base::OnceCallback<void(int, base::Value)> completion_callback) {
     DCHECK(!list->empty());
-    RenderThreadImpl::current()->Send(
-        new PeerConnectionTrackerHost_AddLegacyStats(lid, *list.get()));
+    std::move(completion_callback).Run(lid, std::move(*list.get()));
   }
 
   const int lid_;
   const scoped_refptr<base::SingleThreadTaskRunner> main_thread_;
+  base::OnceCallback<void(int, base::Value)> completion_callback_;
 };
 
 // chrome://webrtc-internals displays stats and stats graphs. The call path
@@ -513,8 +518,11 @@
  public:
   InternalStandardStatsObserver(
       int lid,
-      scoped_refptr<base::SingleThreadTaskRunner> main_thread)
-      : lid_(lid), main_thread_(std::move(main_thread)) {}
+      scoped_refptr<base::SingleThreadTaskRunner> main_thread,
+      base::OnceCallback<void(int, base::Value)> completion_callback)
+      : lid_(lid),
+        main_thread_(std::move(main_thread)),
+        completion_callback_(std::move(completion_callback)) {}
 
   void OnStatsDelivered(
       const rtc::scoped_refptr<const webrtc::RTCStatsReport>& report) override {
@@ -534,8 +542,7 @@
   void OnStatsDeliveredOnMainThread(
       rtc::scoped_refptr<const webrtc::RTCStatsReport> report) {
     auto list = ReportToList(report);
-    RenderThreadImpl::current()->Send(
-        new PeerConnectionTrackerHost_AddStandardStats(lid_, *list.get()));
+    std::move(completion_callback_).Run(lid_, std::move(*list.get()));
   }
 
   std::unique_ptr<base::ListValue> ReportToList(
@@ -602,6 +609,7 @@
 
   const int lid_;
   const scoped_refptr<base::SingleThreadTaskRunner> main_thread_;
+  base::OnceCallback<void(int, base::Value)> completion_callback_;
 };
 
 struct PeerConnectionTrackerLazyInstanceTraits
@@ -625,67 +633,25 @@
 PeerConnectionTracker::PeerConnectionTracker(
     scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner)
     : next_local_id_(1),
-      send_target_for_test_(nullptr),
-      main_thread_task_runner_(std::move(main_thread_task_runner)) {}
+      main_thread_task_runner_(std::move(main_thread_task_runner)) {
+  blink::Platform::Current()->GetBrowserInterfaceBrokerProxy()->GetInterface(
+      peer_connection_tracker_host_.BindNewPipeAndPassReceiver());
+}
 
 PeerConnectionTracker::PeerConnectionTracker(
-    mojom::PeerConnectionTrackerHostAssociatedPtr host,
+    mojo::Remote<mojom::PeerConnectionTrackerHost> host,
     scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner)
     : next_local_id_(1),
-      send_target_for_test_(nullptr),
-      peer_connection_tracker_host_ptr_(std::move(host)),
+      peer_connection_tracker_host_(std::move(host)),
       main_thread_task_runner_(std::move(main_thread_task_runner)) {}
 
 PeerConnectionTracker::~PeerConnectionTracker() {
 }
 
-bool PeerConnectionTracker::OnControlMessageReceived(
-    const IPC::Message& message) {
-  bool handled = true;
-  IPC_BEGIN_MESSAGE_MAP(PeerConnectionTracker, message)
-    IPC_MESSAGE_HANDLER(PeerConnectionTracker_GetStandardStats,
-                        OnGetStandardStats)
-    IPC_MESSAGE_HANDLER(PeerConnectionTracker_GetLegacyStats, OnGetLegacyStats)
-    IPC_MESSAGE_HANDLER(PeerConnectionTracker_OnSuspend, OnSuspend)
-    IPC_MESSAGE_HANDLER(PeerConnectionTracker_StartEventLog, OnStartEventLog)
-    IPC_MESSAGE_HANDLER(PeerConnectionTracker_StopEventLog, OnStopEventLog)
-    IPC_MESSAGE_UNHANDLED(handled = false)
-  IPC_END_MESSAGE_MAP()
-  return handled;
-}
-
-void PeerConnectionTracker::OnGetStandardStats() {
-  DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
-
-  const std::string empty_track_id;
-  for (const auto& pair : peer_connection_local_id_map_) {
-    scoped_refptr<InternalStandardStatsObserver> observer(
-        new rtc::RefCountedObject<InternalStandardStatsObserver>(
-            pair.second, main_thread_task_runner_));
-    pair.first->GetStandardStatsForTracker(observer);
-  }
-}
-
-void PeerConnectionTracker::OnGetLegacyStats() {
-  DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
-
-  const std::string empty_track_id;
-  for (const auto& pair : peer_connection_local_id_map_) {
-    rtc::scoped_refptr<InternalLegacyStatsObserver> observer(
-        new rtc::RefCountedObject<InternalLegacyStatsObserver>(
-            pair.second, main_thread_task_runner_));
-
-    pair.first->GetStats(
-        observer, webrtc::PeerConnectionInterface::kStatsOutputLevelDebug,
-        nullptr);
-  }
-}
-
-RenderThread* PeerConnectionTracker::SendTarget() {
-  if (send_target_for_test_) {
-    return send_target_for_test_;
-  }
-  return RenderThreadImpl::current();
+void PeerConnectionTracker::Bind(
+    mojo::PendingReceiver<mojom::PeerConnectionManager> receiver) {
+  DCHECK(!receiver_.is_bound());
+  receiver_.Bind(std::move(receiver));
 }
 
 void PeerConnectionTracker::OnSuspend() {
@@ -696,8 +662,8 @@
   }
 }
 
-void PeerConnectionTracker::OnStartEventLog(int peer_connection_local_id,
-                                            int output_period_ms) {
+void PeerConnectionTracker::StartEventLog(int peer_connection_local_id,
+                                          int output_period_ms) {
   DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
   for (auto& it : peer_connection_local_id_map_) {
     if (it.second == peer_connection_local_id) {
@@ -707,7 +673,7 @@
   }
 }
 
-void PeerConnectionTracker::OnStopEventLog(int peer_connection_local_id) {
+void PeerConnectionTracker::StopEventLog(int peer_connection_local_id) {
   DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
   for (auto& it : peer_connection_local_id_map_) {
     if (it.second == peer_connection_local_id) {
@@ -717,6 +683,34 @@
   }
 }
 
+void PeerConnectionTracker::GetStandardStats() {
+  DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
+
+  for (const auto& pair : peer_connection_local_id_map_) {
+    scoped_refptr<InternalStandardStatsObserver> observer(
+        new rtc::RefCountedObject<InternalStandardStatsObserver>(
+            pair.second, main_thread_task_runner_,
+            base::BindOnce(&PeerConnectionTracker::AddStandardStats,
+                           AsWeakPtr())));
+    pair.first->GetStandardStatsForTracker(observer);
+  }
+}
+
+void PeerConnectionTracker::GetLegacyStats() {
+  DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
+
+  for (const auto& pair : peer_connection_local_id_map_) {
+    rtc::scoped_refptr<InternalLegacyStatsObserver> observer(
+        new rtc::RefCountedObject<InternalLegacyStatsObserver>(
+            pair.second, main_thread_task_runner_,
+            base::BindOnce(&PeerConnectionTracker::AddLegacyStats,
+                           AsWeakPtr())));
+    pair.first->GetStats(
+        observer, webrtc::PeerConnectionInterface::kStatsOutputLevelDebug,
+        nullptr);
+  }
+}
+
 void PeerConnectionTracker::RegisterPeerConnection(
     RTCPeerConnectionHandler* pc_handler,
     const webrtc::PeerConnectionInterface::RTCConfiguration& config,
@@ -738,7 +732,7 @@
     info->url = "test:testing";
 
   int32_t lid = info->lid;
-  GetPeerConnectionTrackerHost()->AddPeerConnection(std::move(info));
+  peer_connection_tracker_host_->AddPeerConnection(std::move(info));
 
   peer_connection_local_id_map_.insert(std::make_pair(pc_handler, lid));
 }
@@ -756,7 +750,7 @@
     return;
   }
 
-  GetPeerConnectionTrackerHost()->RemovePeerConnection(it->second);
+  peer_connection_tracker_host_->RemovePeerConnection(it->second);
 
   peer_connection_local_id_map_.erase(it);
 }
@@ -1051,8 +1045,8 @@
   if (local_id == -1) {
     return;
   }
-  GetPeerConnectionTrackerHost().get()->OnPeerConnectionSessionIdSet(
-      local_id, session_id);
+  peer_connection_tracker_host_->OnPeerConnectionSessionIdSet(local_id,
+                                                              session_id);
 }
 
 void PeerConnectionTracker::TrackOnRenegotiationNeeded(
@@ -1068,7 +1062,7 @@
     const blink::WebUserMediaRequest& user_media_request) {
   DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
 
-  GetPeerConnectionTrackerHost()->GetUserMedia(
+  peer_connection_tracker_host_->GetUserMedia(
       user_media_request.GetSecurityOrigin().ToString().Utf8(),
       user_media_request.Audio(), user_media_request.Video(),
       SerializeMediaConstraints(user_media_request.AudioConstraints()),
@@ -1082,7 +1076,7 @@
   int id = GetLocalIDForHandler(pc_handler);
   if (id == -1)
     return;
-  GetPeerConnectionTrackerHost()->WebRtcEventLogWrite(id, output);
+  peer_connection_tracker_host_->WebRtcEventLogWrite(id, output);
 }
 
 int PeerConnectionTracker::GetNextLocalID() {
@@ -1107,21 +1101,16 @@
     const std::string& callback_type,
     const std::string& value) {
   DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
-  GetPeerConnectionTrackerHost().get()->UpdatePeerConnection(
-      local_id, callback_type, value);
+  peer_connection_tracker_host_->UpdatePeerConnection(local_id, callback_type,
+                                                      value);
 }
 
-void PeerConnectionTracker::OverrideSendTargetForTesting(RenderThread* target) {
-  send_target_for_test_ = target;
+void PeerConnectionTracker::AddStandardStats(int lid, base::Value value) {
+  peer_connection_tracker_host_->AddStandardStats(lid, std::move(value));
 }
 
-const mojom::PeerConnectionTrackerHostAssociatedPtr&
-PeerConnectionTracker::GetPeerConnectionTrackerHost() {
-  if (!peer_connection_tracker_host_ptr_) {
-    RenderThreadImpl::current()->channel()->GetRemoteAssociatedInterface(
-        &peer_connection_tracker_host_ptr_);
-  }
-  return peer_connection_tracker_host_ptr_;
+void PeerConnectionTracker::AddLegacyStats(int lid, base::Value value) {
+  peer_connection_tracker_host_->AddLegacyStats(lid, std::move(value));
 }
 
 }  // namespace content
diff --git a/content/renderer/media/webrtc/peer_connection_tracker.h b/content/renderer/media/webrtc/peer_connection_tracker.h
index 29af0f4..6487ada 100644
--- a/content/renderer/media/webrtc/peer_connection_tracker.h
+++ b/content/renderer/media/webrtc/peer_connection_tracker.h
@@ -7,14 +7,12 @@
 
 #include <map>
 
-#include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/threading/thread_checker.h"
 #include "content/common/media/peer_connection_tracker.mojom.h"
-#include "content/public/renderer/render_thread_observer.h"
-#include "ipc/ipc_platform_file.h"
-#include "third_party/blink/public/platform/web_media_stream.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/platform/web_rtc_peer_connection_handler_client.h"
 #include "third_party/blink/public/platform/web_rtc_rtp_transceiver.h"
 #include "third_party/blink/public/platform/web_rtc_session_description.h"
@@ -36,21 +34,22 @@
 
 namespace content {
 class RTCPeerConnectionHandler;
-class RenderThread;
 
 // This class collects data about each peer connection,
 // sends it to the browser process, and handles messages
 // from the browser process.
 class CONTENT_EXPORT PeerConnectionTracker
-    : public RenderThreadObserver,
+    : public mojom::PeerConnectionManager,
       public base::SupportsWeakPtr<PeerConnectionTracker> {
  public:
   static PeerConnectionTracker* GetInstance();
 
+  // TODO(crbug.com/787254): Make these ctors private, and accessible to
+  // tests only.
   explicit PeerConnectionTracker(
       scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner);
   PeerConnectionTracker(
-      mojom::PeerConnectionTrackerHostAssociatedPtr host,
+      mojo::Remote<mojom::PeerConnectionTrackerHost> host,
       scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner);
   ~PeerConnectionTracker() override;
 
@@ -76,15 +75,12 @@
     kSetRemoteDescription,
   };
 
-  // RenderThreadObserver implementation.
-  bool OnControlMessageReceived(const IPC::Message& message) override;
+  void Bind(mojo::PendingReceiver<mojom::PeerConnectionManager> receiver);
 
-  //
   // The following methods send an update to the browser process when a
   // PeerConnection update happens. The caller should call the Track* methods
   // after calling RegisterPeerConnection and before calling
   // UnregisterPeerConnection, otherwise the Track* call has no effect.
-  //
 
   // Sends an update when a PeerConnection has been created in Javascript. This
   // should be called once and only once for each PeerConnection. The
@@ -221,10 +217,10 @@
   // Sends a new fragment on an RtcEventLog.
   virtual void TrackRtcEventLogWrite(RTCPeerConnectionHandler* pc_handler,
                                      const std::string& output);
-  // For testing: Override the class that gets posted messages.
-  void OverrideSendTargetForTesting(RenderThread* target);
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(PeerConnectionTrackerTest, OnSuspend);
+
   // Assign a local ID to a peer connection so that the browser process can
   // uniquely identify a peer connection in the renderer process.
   // The return value will always be positive.
@@ -240,18 +236,13 @@
                         const blink::WebRTCRtpTransceiver& transceiver,
                         size_t transceiver_index);
 
-  // IPC Message handler for getting all stats.
-  void OnGetStandardStats();
-  void OnGetLegacyStats();
-
-  // Called when the browser process reports a suspend event from the OS.
-  void OnSuspend();
-
-  // IPC Message handler for starting event log.
-  void OnStartEventLog(int peer_connection_local_id, int output_period_ms);
-
-  // IPC Message handler for stopping event log.
-  void OnStopEventLog(int peer_connection_local_id);
+  // PeerConnectionTracker implementation.
+  void OnSuspend() override;
+  void StartEventLog(int peer_connection_local_id,
+                     int output_period_ms) override;
+  void StopEventLog(int peer_connection_local_id) override;
+  void GetStandardStats() override;
+  void GetLegacyStats() override;
 
   // Called to deliver an update to the host (PeerConnectionTrackerHost).
   // |local_id| - The id of the registered RTCPeerConnectionHandler.
@@ -268,9 +259,8 @@
                                 const std::string& callback_type,
                                 const std::string& value);
 
-  RenderThread* SendTarget();
-  const mojom::PeerConnectionTrackerHostAssociatedPtr&
-  GetPeerConnectionTrackerHost();
+  void AddStandardStats(int lid, base::Value value);
+  void AddLegacyStats(int lid, base::Value value);
 
   // This map stores the local ID assigned to each RTCPeerConnectionHandler.
   typedef std::map<RTCPeerConnectionHandler*, int> PeerConnectionLocalIdMap;
@@ -279,9 +269,8 @@
   // This keeps track of the next available local ID.
   int next_local_id_;
   THREAD_CHECKER(main_thread_);
-  RenderThread* send_target_for_test_;
-  mojom::PeerConnectionTrackerHostAssociatedPtr
-      peer_connection_tracker_host_ptr_;
+  mojo::Remote<mojom::PeerConnectionTrackerHost> peer_connection_tracker_host_;
+  mojo::Receiver<mojom::PeerConnectionManager> receiver_{this};
 
   scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
 
diff --git a/content/renderer/media/webrtc/peer_connection_tracker_unittest.cc b/content/renderer/media/webrtc/peer_connection_tracker_unittest.cc
index 9c3da6d..7eaf0b4 100644
--- a/content/renderer/media/webrtc/peer_connection_tracker_unittest.cc
+++ b/content/renderer/media/webrtc/peer_connection_tracker_unittest.cc
@@ -6,11 +6,7 @@
 
 #include "base/test/task_environment.h"
 #include "content/common/media/peer_connection_tracker.mojom.h"
-#include "content/common/media/peer_connection_tracker_messages.h"
-#include "content/public/test/mock_render_thread.h"
 #include "content/renderer/media/webrtc/rtc_peer_connection_handler.h"
-#include "ipc/ipc_message_macros.h"
-#include "mojo/public/cpp/bindings/associated_binding.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
@@ -57,7 +53,7 @@
 
 class MockPeerConnectionTrackerHost : public mojom::PeerConnectionTrackerHost {
  public:
-  MockPeerConnectionTrackerHost() : binding_(this) {}
+  MockPeerConnectionTrackerHost() {}
   MOCK_METHOD3(UpdatePeerConnection,
                void(int, const std::string&, const std::string&));
   MOCK_METHOD1(AddPeerConnection, void(mojom::PeerConnectionInfoPtr));
@@ -70,15 +66,17 @@
                     const std::string&,
                     const std::string&));
   MOCK_METHOD2(WebRtcEventLogWrite, void(int, const std::string&));
-  mojom::PeerConnectionTrackerHostAssociatedPtr CreateInterfacePtrAndBind() {
-    mojom::PeerConnectionTrackerHostAssociatedPtr
-        peer_connection_tracker_host_ptr_;
-    binding_.Bind(mojo::MakeRequestAssociatedWithDedicatedPipe(
-                      &peer_connection_tracker_host_ptr_),
-                  blink::scheduler::GetSingleThreadTaskRunnerForTesting());
-    return peer_connection_tracker_host_ptr_;
+  MOCK_METHOD2(AddStandardStats, void(int, base::Value));
+  MOCK_METHOD2(AddLegacyStats, void(int, base::Value));
+
+  mojo::Remote<mojom::PeerConnectionTrackerHost> CreatePendingRemoteAndBind() {
+    receiver_.reset();
+    return mojo::Remote<mojom::PeerConnectionTrackerHost>(
+        receiver_.BindNewPipeAndPassRemote(
+            blink::scheduler::GetSingleThreadTaskRunnerForTesting()));
   }
-  mojo::AssociatedBinding<mojom::PeerConnectionTrackerHost> binding_;
+
+  mojo::Receiver<mojom::PeerConnectionTrackerHost> receiver_{this};
 };
 
 // Creates a transceiver that is expected to be logged as
@@ -140,7 +138,7 @@
   void CreateTrackerWithMocks() {
     mock_host_.reset(new MockPeerConnectionTrackerHost());
     tracker_.reset(new PeerConnectionTracker(
-        mock_host_->CreateInterfacePtrAndBind(),
+        mock_host_->CreatePendingRemoteAndBind(),
         blink::scheduler::GetSingleThreadTaskRunnerForTesting()));
   }
 
@@ -187,8 +185,7 @@
   CreateTrackerWithMocks();
   CreateAndRegisterPeerConnectionHandler();
   EXPECT_CALL(*mock_handler_, CloseClientPeerConnection());
-  std::unique_ptr<IPC::Message> message(new PeerConnectionTracker_OnSuspend());
-  tracker_->OnControlMessageReceived(*message.get());
+  tracker_->OnSuspend();
 }
 
 TEST_F(PeerConnectionTrackerTest, AddTransceiverWithOptionalValuesPresent) {
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index de3feca4..64cd139 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -753,8 +753,6 @@
   browser_plugin_manager_.reset(new BrowserPluginManager());
   AddObserver(browser_plugin_manager_.get());
 
-  AddObserver(PeerConnectionTracker::GetInstance());
-
   unfreezable_message_filter_ = new UnfreezableMessageFilter(this);
   AddFilter(unfreezable_message_filter_.get());
 
@@ -767,6 +765,13 @@
   registry->AddInterface(base::BindRepeating(CreateResourceUsageReporter,
                                              weak_factory_.GetWeakPtr()),
                          base::ThreadTaskRunnerHandle::Get());
+  registry->AddInterface(
+      // Unretained here is safe, since PeerConnectionTracker instance
+      // is a leaky singleton.
+      base::BindRepeating(
+          &PeerConnectionTracker::Bind,
+          base::Unretained(PeerConnectionTracker::GetInstance())),
+      base::ThreadTaskRunnerHandle::Get());
 
   if (base::FeatureList::IsEnabled(
           blink::features::kOffMainThreadServiceWorkerStartup)) {
diff --git a/content/renderer/service_worker/service_worker_context_client.cc b/content/renderer/service_worker/service_worker_context_client.cc
index 54dfa3a..8ae5770 100644
--- a/content/renderer/service_worker/service_worker_context_client.cc
+++ b/content/renderer/service_worker/service_worker_context_client.cc
@@ -37,6 +37,7 @@
 #include "content/renderer/service_worker/navigation_preload_request.h"
 #include "content/renderer/service_worker/service_worker_fetch_context_impl.h"
 #include "content/renderer/service_worker/service_worker_type_converters.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "net/base/net_errors.h"
 #include "services/network/public/cpp/features.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
@@ -377,7 +378,7 @@
   // URLLoaderFactoryInfo.
   auto script_loader_factory_info =
       std::make_unique<network::WrapperSharedURLLoaderFactoryInfo>(std::move(
-          service_worker_provider_info_->script_loader_factory_ptr_info));
+          service_worker_provider_info_->script_loader_factory_remote));
 
   return base::MakeRefCounted<ServiceWorkerFetchContextImpl>(
       *renderer_preferences_, script_url_, loader_factories_->PassInterface(),
diff --git a/content/renderer/service_worker/service_worker_context_client.h b/content/renderer/service_worker/service_worker_context_client.h
index 224ec6f..589539d 100644
--- a/content/renderer/service_worker/service_worker_context_client.h
+++ b/content/renderer/service_worker/service_worker_context_client.h
@@ -239,7 +239,7 @@
 
   // This holds blink.mojom.ServiceWorkerContainer(Host) connections to the
   // browser-side ServiceWorkerProviderHost to keep it alive there.
-  // Note: |service_worker_provider_info_->script_loader_factory_ptr_info| is
+  // Note: |service_worker_provider_info_->script_loader_factory_remote| is
   // moved to WebServiceWorkerNetworkProviderImpl when
   // CreateServiceWorkerNetworkProvider is called.
   blink::mojom::ServiceWorkerProviderInfoForStartWorkerPtr
diff --git a/content/test/content_browser_test_test.cc b/content/test/content_browser_test_test.cc
index 4f5abb0..75415ed 100644
--- a/content/test/content_browser_test_test.cc
+++ b/content/test/content_browser_test_test.cc
@@ -190,15 +190,8 @@
   base::Value* val = root->FindDictKey("test_locations");
   ASSERT_TRUE(val);
   EXPECT_EQ(3u, val->DictSize());
-  // If path or test location changes, the following expectation
-  // will need to change accordingly.
-  std::string file_name = "../../content/test/content_browser_test_test.cc";
-  EXPECT_TRUE(base::test_launcher_utils::ValidateTestLocation(
-      val, "MockContentBrowserTest.DISABLED_PassTest", file_name, 153));
-  EXPECT_TRUE(base::test_launcher_utils::ValidateTestLocation(
-      val, "MockContentBrowserTest.DISABLED_FailTest", file_name, 157));
-  EXPECT_TRUE(base::test_launcher_utils::ValidateTestLocation(
-      val, "MockContentBrowserTest.DISABLED_CrashTest", file_name, 161));
+  EXPECT_TRUE(base::test_launcher_utils::ValidateTestLocations(
+      val, "MockContentBrowserTest"));
 
   val = root->FindListKey("per_iteration_data");
   ASSERT_TRUE(val);
diff --git a/extensions/browser/api/web_request/web_request_api.cc b/extensions/browser/api/web_request/web_request_api.cc
index 848c9c6e..544402e 100644
--- a/extensions/browser/api/web_request/web_request_api.cc
+++ b/extensions/browser/api/web_request/web_request_api.cc
@@ -148,14 +148,6 @@
     keys::kOnHeadersReceivedEvent,
 };
 
-// List of all webRequest events that support extraHeaders in the extraInfoSpec.
-const char* const kWebRequestExtraHeadersEventNames[] = {
-    keys::kOnBeforeSendHeadersEvent, keys::kOnSendHeadersEvent,
-    keys::kOnHeadersReceivedEvent,   keys::kOnAuthRequiredEvent,
-    keys::kOnResponseStartedEvent,   keys::kOnBeforeRedirectEvent,
-    keys::kOnCompletedEvent,
-};
-
 const char* GetRequestStageAsString(
     ExtensionWebRequestEventRouter::EventTypes type) {
   switch (type) {
@@ -1834,7 +1826,7 @@
     return false;
 
   int extra_info_spec = 0;
-  for (const char* name : kWebRequestExtraHeadersEventNames) {
+  for (const char* name : kWebRequestEvents) {
     GetMatchingListeners(browser_context, name, request, &extra_info_spec);
     if (extra_info_spec & ExtraInfoSpec::EXTRA_HEADERS)
       return true;
diff --git a/extensions/common/api/web_request.json b/extensions/common/api/web_request.json
index 9a694cc..fa23da2 100644
--- a/extensions/common/api/web_request.json
+++ b/extensions/common/api/web_request.json
@@ -21,7 +21,7 @@
       {
         "id": "OnBeforeRequestOptions",
         "type": "string",
-        "enum": ["blocking", "requestBody"]
+        "enum": ["blocking", "requestBody", "extraHeaders"]
       },
       {
         "id": "OnBeforeSendHeadersOptions",
@@ -59,6 +59,11 @@
         "enum": ["responseHeaders", "extraHeaders"]
       },
       {
+        "id": "OnErrorOccurredOptions",
+        "type": "string",
+        "enum": ["extraHeaders"]
+      },
+      {
         "id": "RequestFilter",
         "type": "object",
         "description": "An object describing filters to apply to webRequest events.",
@@ -598,6 +603,15 @@
             "$ref": "RequestFilter",
             "name": "filter",
             "description": "A set of filters that restricts the events that will be sent to this listener."
+          },
+          {
+            "type": "array",
+            "optional": true,
+            "name": "extraInfoSpec",
+            "description": "Array of extra information that should be passed to the listener function.",
+            "items": {
+              "$ref": "OnErrorOccurredOptions"
+            }
           }
         ]
       },
diff --git a/gpu/config/gpu_control_list.cc b/gpu/config/gpu_control_list.cc
index 38b497a..dac27c4 100644
--- a/gpu/config/gpu_control_list.cc
+++ b/gpu/config/gpu_control_list.cc
@@ -323,6 +323,12 @@
 #endif  // OS_WIN
       break;
   }
+  if ((subpixel_font_rendering == kUnsupported &&
+       gpu_info.subpixel_font_rendering) ||
+      (subpixel_font_rendering == kSupported &&
+       !gpu_info.subpixel_font_rendering)) {
+    return false;
+  }
   return true;
 }
 
diff --git a/gpu/config/gpu_control_list.h b/gpu/config/gpu_control_list.h
index f836439..4b60cc84 100644
--- a/gpu/config/gpu_control_list.h
+++ b/gpu/config/gpu_control_list.h
@@ -158,6 +158,8 @@
 
     uint32_t test_group;
 
+    SupportedOrNot subpixel_font_rendering;
+
     // Return true if GL_VERSION string does not fit the entry info
     // on GL type and GL version.
     bool GLVersionInfoMismatch(const std::string& gl_version_string) const;
diff --git a/gpu/config/gpu_control_list_entry_unittest.cc b/gpu/config/gpu_control_list_entry_unittest.cc
index d02c664..eeb183b6 100644
--- a/gpu/config/gpu_control_list_entry_unittest.cc
+++ b/gpu/config/gpu_control_list_entry_unittest.cc
@@ -1060,4 +1060,55 @@
 }
 #endif  // OS_WIN
 
+TEST_F(GpuControlListEntryTest, TestSubpixelFontRendering) {
+  const Entry& entry = GetEntry(kGpuControlListEntryTest_SubpixelFontRendering);
+
+  GPUInfo gpu_info;
+  gpu_info.subpixel_font_rendering = true;
+  gpu_info.gl_renderer = "Mali0xx";
+
+  EXPECT_TRUE(entry.Contains(kOsChromeOS, "10.0", gpu_info));
+
+  gpu_info.subpixel_font_rendering = false;
+  gpu_info.gl_renderer = "Mali1xx";
+  EXPECT_FALSE(entry.Contains(kOsChromeOS, "10.0", gpu_info));
+
+  gpu_info.subpixel_font_rendering = false;
+  gpu_info.gl_renderer = "DontCare";
+  EXPECT_FALSE(entry.Contains(kOsChromeOS, "10.0", gpu_info));
+
+  gpu_info.subpixel_font_rendering = true;
+  gpu_info.gl_renderer = "DontCare";
+  EXPECT_FALSE(entry.Contains(kOsChromeOS, "10.0", gpu_info));
+
+  gpu_info.subpixel_font_rendering = false;
+  gpu_info.gl_renderer = "Supported";
+  EXPECT_TRUE(entry.Contains(kOsChromeOS, "10.0", gpu_info));
+
+  gpu_info.subpixel_font_rendering = true;
+  gpu_info.gl_renderer = "Supported";
+  EXPECT_FALSE(entry.Contains(kOsChromeOS, "10.0", gpu_info));
+
+  gpu_info.subpixel_font_rendering = true;
+  gpu_info.gl_renderer = "Others";
+  EXPECT_TRUE(entry.Contains(kOsChromeOS, "10.0", gpu_info));
+
+  // Not ChromeOS
+  EXPECT_FALSE(entry.Contains(kOsLinux, "10.0", gpu_info));
+}
+
+TEST_F(GpuControlListEntryTest, TestSubpixelFontRenderingDontCare) {
+  const Entry& entry =
+      GetEntry(kGpuControlListEntryTest_SubpixelFontRenderingDontCare);
+
+  GPUInfo gpu_info;
+  gpu_info.subpixel_font_rendering = true;
+  gpu_info.gl_renderer = "Mali0xx";
+
+  EXPECT_TRUE(entry.Contains(kOsChromeOS, "10.0", gpu_info));
+
+  gpu_info.subpixel_font_rendering = false;
+  EXPECT_TRUE(entry.Contains(kOsChromeOS, "10.0", gpu_info));
+}
+
 }  // namespace gpu
diff --git a/gpu/config/gpu_control_list_format.txt b/gpu/config/gpu_control_list_format.txt
index b61c158f..338910a 100644
--- a/gpu/config/gpu_control_list_format.txt
+++ b/gpu/config/gpu_control_list_format.txt
@@ -80,6 +80,10 @@
 //     specify that.
 // 30. "intel_gpu_generation" is a VERSION structure. Each Intel GPU has a
 //     specific integer (meaning generation) associated.
+// 31. "subpixel_font_rendering" is either "supported" or "unsupported". Currently it
+//     only applies on ChromeOS where subpixel font rendering causes a glitch
+//     on Mali GPUs. By default it's "dont_care" and there is no need to
+//     specify that.
 //
 // VERSION includes "op", "style", "value", and "value2".  "op" can be any of
 // the following values: "=", "<", "<=", ">", ">=", "any", "between".  "style"
diff --git a/gpu/config/gpu_control_list_testing.json b/gpu/config/gpu_control_list_testing.json
index 55f5852d..d32c8ab 100644
--- a/gpu/config/gpu_control_list_testing.json
+++ b/gpu/config/gpu_control_list_testing.json
@@ -268,7 +268,7 @@
       "features": [
         "all",
         {
-	  "exceptions" : [
+          "exceptions" : [
             "test_feature_0"
           ]
         }
@@ -871,6 +871,34 @@
       "features": [
         "test_feature_0"
       ]
+    },
+    {
+      "id": 73,
+      "description": "GpuControlListEntryTest.SubpixelFontRendering",
+      "os": {
+        "type": "chromeos"
+      },
+      "features": [
+        "test_feature_0"
+      ],
+      "exceptions": [
+        { "gl_renderer": "Mali.*",
+          "subpixel_font_rendering": "unsupported"},
+        { "gl_renderer": "DontCare" },
+        { "gl_renderer": "Supported",
+          "subpixel_font_rendering": "supported"}
+      ]
+    },
+    {
+      "id": 74,
+      "description": "GpuControlListEntryTest.SubpixelFontRenderingDontCare",
+      "os": {
+        "type": "chromeos"
+      },
+      "gl_renderer": "Mali.*",
+      "features": [
+        "test_feature_0"
+      ]
     }
   ]
 }
diff --git a/gpu/config/gpu_control_list_testing_arrays_and_structs_autogen.h b/gpu/config/gpu_control_list_testing_arrays_and_structs_autogen.h
index cd45ad0..6564a31 100644
--- a/gpu/config/gpu_control_list_testing_arrays_and_structs_autogen.h
+++ b/gpu/config/gpu_control_list_testing_arrays_and_structs_autogen.h
@@ -52,6 +52,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry2[1] = {
@@ -72,6 +73,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry3[1] = {
@@ -92,6 +94,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry4[1] = {
@@ -112,6 +115,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const GpuControlList::More kMoreForEntry4_1440601243Exception0 = {
@@ -128,6 +132,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry5[1] = {
@@ -148,6 +153,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const GpuControlList::More kMoreForEntry5_1440601243Exception0 = {
@@ -164,6 +170,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry6[1] = {
@@ -189,6 +196,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry7[1] = {
@@ -209,6 +217,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry8[1] = {
@@ -229,6 +238,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry9[1] = {
@@ -249,6 +259,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry10[1] = {
@@ -269,6 +280,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry11[1] = {
@@ -296,6 +308,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry12[1] = {
@@ -323,6 +336,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry13[1] = {
@@ -350,6 +364,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry14[1] = {
@@ -377,6 +392,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry15[1] = {
@@ -404,6 +420,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry16[1] = {
@@ -424,6 +441,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry17[1] = {
@@ -444,6 +462,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry18[1] = {
@@ -470,6 +489,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry19[1] = {
@@ -496,6 +516,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry20[1] = {
@@ -522,6 +543,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry21[1] = {
@@ -542,6 +564,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const GpuControlList::GLStrings
@@ -566,6 +589,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry22[1] = {
@@ -586,6 +610,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry23[3] = {
@@ -608,6 +633,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry24[2] = {
@@ -629,6 +655,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry25[1] = {
@@ -653,6 +680,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry26[1] = {
@@ -687,6 +715,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry27[1] = {
@@ -707,6 +736,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const char* const kMachineModelNameForEntry27Exception0[1] = {
@@ -735,6 +765,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry28[1] = {
@@ -766,6 +797,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry29[1] = {
@@ -797,6 +829,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const GpuControlList::MachineModelInfo kMachineModelInfoForEntry29Exception0 = {
@@ -820,6 +853,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry30[1] = {
@@ -844,6 +878,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry31[1] = {
@@ -868,6 +903,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry32[1] = {
@@ -892,6 +928,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry33[1] = {
@@ -916,6 +953,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry34[1] = {
@@ -940,6 +978,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry35[1] = {
@@ -965,6 +1004,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry36[1] = {
@@ -985,6 +1025,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry37[1] = {
@@ -1009,6 +1050,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry38[1] = {
@@ -1029,6 +1071,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry39[1] = {
@@ -1049,6 +1092,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry40[1] = {
@@ -1069,6 +1113,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry41[1] = {
@@ -1089,6 +1134,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry42[1] = {
@@ -1109,6 +1155,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry43[1] = {
@@ -1129,6 +1176,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry44[1] = {
@@ -1149,6 +1197,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const uint32_t kDeviceIDsForGpuControlTestingEntry44Exception0[1] = {
@@ -1176,6 +1225,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const uint32_t kDeviceIDsForGpuControlTestingEntry44Exception1[1] = {
@@ -1203,6 +1253,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry45[1] = {
@@ -1223,6 +1274,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry46[1] = {
@@ -1243,6 +1295,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry47[1] = {
@@ -1263,6 +1316,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry48[1] = {
@@ -1283,6 +1337,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry49[1] = {
@@ -1309,6 +1364,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry50[1] = {
@@ -1335,6 +1391,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry51[1] = {
@@ -1355,6 +1412,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const GpuControlList::GLStrings
@@ -1379,6 +1437,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry52[1] = {
@@ -1399,6 +1458,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry53[1] = {
@@ -1425,6 +1485,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const char* const kDisabledExtensionsForEntry54[2] = {
@@ -1446,6 +1507,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const char* const kDisabledExtensionsForEntry55[2] = {
@@ -1467,6 +1529,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry56[1] = {
@@ -1487,6 +1550,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry57[1] = {
@@ -1507,6 +1571,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry58[1] = {
@@ -1527,6 +1592,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     1,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry59[1] = {
@@ -1547,6 +1613,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     2,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry60[1] = {
@@ -1572,6 +1639,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry61[1] = {
@@ -1596,6 +1664,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry62[1] = {
@@ -1620,6 +1689,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry63[1] = {
@@ -1644,6 +1714,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry64[1] = {
@@ -1668,6 +1739,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry65[1] = {
@@ -1688,6 +1760,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const GpuSeriesType kGpuSeriesForEntry65Exception0[1] = {
@@ -1708,6 +1781,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry66[1] = {
@@ -1734,6 +1808,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry67[1] = {
@@ -1754,6 +1829,7 @@
      nullptr},                     // gpu_count
     GpuControlList::kUnsupported,  // hardware_overlay
     0,                             // test_group
+    GpuControlList::kDontCare,     // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry68[1] = {
@@ -1774,6 +1850,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry69[1] = {
@@ -1794,6 +1871,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry70[1] = {
@@ -1814,6 +1892,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry71[1] = {
@@ -1834,6 +1913,7 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 const int kFeatureListForGpuControlTestingEntry72[1] = {
@@ -1854,6 +1934,131 @@
      nullptr},                  // gpu_count
     GpuControlList::kDontCare,  // hardware_overlay
     0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
+};
+
+const int kFeatureListForGpuControlTestingEntry73[1] = {
+    TEST_FEATURE_0,
+};
+
+const GpuControlList::More kMoreForEntry73_1440601243 = {
+    GpuControlList::kGLTypeNone,  // gl_type
+    {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical, nullptr,
+     nullptr},  // gl_version
+    {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical, nullptr,
+     nullptr},  // pixel_shader_version
+    false,      // in_process_gpu
+    0,          // gl_reset_notification_strategy
+    {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical, nullptr,
+     nullptr},  // direct_rendering_version
+    {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical, nullptr,
+     nullptr},                  // gpu_count
+    GpuControlList::kDontCare,  // hardware_overlay
+    0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
+};
+
+const GpuControlList::GLStrings
+    kGLStringsForGpuControlTestingEntry73Exception0 = {
+        nullptr,
+        "Mali.*",
+        nullptr,
+        nullptr,
+};
+
+const GpuControlList::More kMoreForEntry73_1440601243Exception0 = {
+    GpuControlList::kGLTypeNone,  // gl_type
+    {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical, nullptr,
+     nullptr},  // gl_version
+    {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical, nullptr,
+     nullptr},  // pixel_shader_version
+    false,      // in_process_gpu
+    0,          // gl_reset_notification_strategy
+    {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical, nullptr,
+     nullptr},  // direct_rendering_version
+    {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical, nullptr,
+     nullptr},                     // gpu_count
+    GpuControlList::kDontCare,     // hardware_overlay
+    0,                             // test_group
+    GpuControlList::kUnsupported,  // subpixel_font_rendering
+};
+
+const GpuControlList::GLStrings
+    kGLStringsForGpuControlTestingEntry73Exception1 = {
+        nullptr,
+        "DontCare",
+        nullptr,
+        nullptr,
+};
+
+const GpuControlList::More kMoreForEntry73_1440601243Exception1 = {
+    GpuControlList::kGLTypeNone,  // gl_type
+    {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical, nullptr,
+     nullptr},  // gl_version
+    {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical, nullptr,
+     nullptr},  // pixel_shader_version
+    false,      // in_process_gpu
+    0,          // gl_reset_notification_strategy
+    {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical, nullptr,
+     nullptr},  // direct_rendering_version
+    {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical, nullptr,
+     nullptr},                  // gpu_count
+    GpuControlList::kDontCare,  // hardware_overlay
+    0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
+};
+
+const GpuControlList::GLStrings
+    kGLStringsForGpuControlTestingEntry73Exception2 = {
+        nullptr,
+        "Supported",
+        nullptr,
+        nullptr,
+};
+
+const GpuControlList::More kMoreForEntry73_1440601243Exception2 = {
+    GpuControlList::kGLTypeNone,  // gl_type
+    {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical, nullptr,
+     nullptr},  // gl_version
+    {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical, nullptr,
+     nullptr},  // pixel_shader_version
+    false,      // in_process_gpu
+    0,          // gl_reset_notification_strategy
+    {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical, nullptr,
+     nullptr},  // direct_rendering_version
+    {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical, nullptr,
+     nullptr},                   // gpu_count
+    GpuControlList::kDontCare,   // hardware_overlay
+    0,                           // test_group
+    GpuControlList::kSupported,  // subpixel_font_rendering
+};
+
+const int kFeatureListForGpuControlTestingEntry74[1] = {
+    TEST_FEATURE_0,
+};
+
+const GpuControlList::GLStrings kGLStringsForGpuControlTestingEntry74 = {
+    nullptr,
+    "Mali.*",
+    nullptr,
+    nullptr,
+};
+
+const GpuControlList::More kMoreForEntry74_1440601243 = {
+    GpuControlList::kGLTypeNone,  // gl_type
+    {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical, nullptr,
+     nullptr},  // gl_version
+    {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical, nullptr,
+     nullptr},  // pixel_shader_version
+    false,      // in_process_gpu
+    0,          // gl_reset_notification_strategy
+    {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical, nullptr,
+     nullptr},  // direct_rendering_version
+    {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical, nullptr,
+     nullptr},                  // gpu_count
+    GpuControlList::kDontCare,  // hardware_overlay
+    0,                          // test_group
+    GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
 }  // namespace gpu
diff --git a/gpu/config/gpu_control_list_testing_autogen.cc b/gpu/config/gpu_control_list_testing_autogen.cc
index d8d7563..dcb6daf8 100644
--- a/gpu/config/gpu_control_list_testing_autogen.cc
+++ b/gpu/config/gpu_control_list_testing_autogen.cc
@@ -2330,6 +2330,70 @@
         0,        // exceptions count
         nullptr,  // exceptions
     },
+    {
+        73,  // id
+        "GpuControlListEntryTest.SubpixelFontRendering",
+        base::size(kFeatureListForGpuControlTestingEntry73),  // features size
+        kFeatureListForGpuControlTestingEntry73,              // features
+        0,        // DisabledExtensions size
+        nullptr,  // DisabledExtensions
+        0,        // DisabledWebGLExtensions size
+        nullptr,  // DisabledWebGLExtensions
+        0,        // CrBugs size
+        nullptr,  // CrBugs
+        {
+            GpuControlList::kOsChromeOS,  // os_type
+            {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
+             nullptr, nullptr},                     // os_version
+            0x00,                                   // vendor_id
+            0,                                      // DeviceIDs size
+            nullptr,                                // DeviceIDs
+            GpuControlList::kMultiGpuCategoryNone,  // multi_gpu_category
+            GpuControlList::kMultiGpuStyleNone,     // multi_gpu_style
+            nullptr,                                // driver info
+            nullptr,                                // GL strings
+            nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
+            {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
+             nullptr, nullptr},           // intel_gpu_generation
+            &kMoreForEntry73_1440601243,  // more data
+        },
+        base::size(kExceptionsForEntry73),  // exceptions count
+        kExceptionsForEntry73,              // exceptions
+    },
+    {
+        74,  // id
+        "GpuControlListEntryTest.SubpixelFontRenderingDontCare",
+        base::size(kFeatureListForGpuControlTestingEntry74),  // features size
+        kFeatureListForGpuControlTestingEntry74,              // features
+        0,        // DisabledExtensions size
+        nullptr,  // DisabledExtensions
+        0,        // DisabledWebGLExtensions size
+        nullptr,  // DisabledWebGLExtensions
+        0,        // CrBugs size
+        nullptr,  // CrBugs
+        {
+            GpuControlList::kOsChromeOS,  // os_type
+            {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
+             nullptr, nullptr},                      // os_version
+            0x00,                                    // vendor_id
+            0,                                       // DeviceIDs size
+            nullptr,                                 // DeviceIDs
+            GpuControlList::kMultiGpuCategoryNone,   // multi_gpu_category
+            GpuControlList::kMultiGpuStyleNone,      // multi_gpu_style
+            nullptr,                                 // driver info
+            &kGLStringsForGpuControlTestingEntry74,  // GL strings
+            nullptr,                                 // machine model info
+            0,                                       // gpu_series size
+            nullptr,                                 // gpu_series
+            {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
+             nullptr, nullptr},           // intel_gpu_generation
+            &kMoreForEntry74_1440601243,  // more data
+        },
+        0,        // exceptions count
+        nullptr,  // exceptions
+    },
 };
-const size_t kGpuControlListTestingEntryCount = 72;
+const size_t kGpuControlListTestingEntryCount = 74;
 }  // namespace gpu
diff --git a/gpu/config/gpu_control_list_testing_entry_enums_autogen.h b/gpu/config/gpu_control_list_testing_entry_enums_autogen.h
index c7c7da5e..f70d8b3 100644
--- a/gpu/config/gpu_control_list_testing_entry_enums_autogen.h
+++ b/gpu/config/gpu_control_list_testing_entry_enums_autogen.h
@@ -85,6 +85,8 @@
   kGpuControlListEntryTest_GpuGenerationAny = 69,
   kGpuControlListEntryTest_GpuGenerationPrimary = 70,
   kGpuControlListEntryTest_GpuGenerationSecondary = 71,
+  kGpuControlListEntryTest_SubpixelFontRendering = 72,
+  kGpuControlListEntryTest_SubpixelFontRenderingDontCare = 73,
 };
 }  // namespace gpu
 
diff --git a/gpu/config/gpu_control_list_testing_exceptions_autogen.h b/gpu/config/gpu_control_list_testing_exceptions_autogen.h
index e04e2df..0db38fff 100644
--- a/gpu/config/gpu_control_list_testing_exceptions_autogen.h
+++ b/gpu/config/gpu_control_list_testing_exceptions_autogen.h
@@ -200,6 +200,63 @@
     },
 };
 
+const GpuControlList::Conditions kExceptionsForEntry73[3] = {
+    {
+        GpuControlList::kOsAny,  // os_type
+        {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
+         nullptr, nullptr},                                // os_version
+        0x00,                                              // vendor_id
+        0,                                                 // DeviceIDs size
+        nullptr,                                           // DeviceIDs
+        GpuControlList::kMultiGpuCategoryNone,             // multi_gpu_category
+        GpuControlList::kMultiGpuStyleNone,                // multi_gpu_style
+        nullptr,                                           // driver info
+        &kGLStringsForGpuControlTestingEntry73Exception0,  // GL strings
+        nullptr,                                           // machine model info
+        0,                                                 // gpu_series size
+        nullptr,                                           // gpu_series
+        {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
+         nullptr, nullptr},                     // intel_gpu_generation
+        &kMoreForEntry73_1440601243Exception0,  // more data
+    },
+    {
+        GpuControlList::kOsAny,  // os_type
+        {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
+         nullptr, nullptr},                                // os_version
+        0x00,                                              // vendor_id
+        0,                                                 // DeviceIDs size
+        nullptr,                                           // DeviceIDs
+        GpuControlList::kMultiGpuCategoryNone,             // multi_gpu_category
+        GpuControlList::kMultiGpuStyleNone,                // multi_gpu_style
+        nullptr,                                           // driver info
+        &kGLStringsForGpuControlTestingEntry73Exception1,  // GL strings
+        nullptr,                                           // machine model info
+        0,                                                 // gpu_series size
+        nullptr,                                           // gpu_series
+        {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
+         nullptr, nullptr},                     // intel_gpu_generation
+        &kMoreForEntry73_1440601243Exception1,  // more data
+    },
+    {
+        GpuControlList::kOsAny,  // os_type
+        {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
+         nullptr, nullptr},                                // os_version
+        0x00,                                              // vendor_id
+        0,                                                 // DeviceIDs size
+        nullptr,                                           // DeviceIDs
+        GpuControlList::kMultiGpuCategoryNone,             // multi_gpu_category
+        GpuControlList::kMultiGpuStyleNone,                // multi_gpu_style
+        nullptr,                                           // driver info
+        &kGLStringsForGpuControlTestingEntry73Exception2,  // GL strings
+        nullptr,                                           // machine model info
+        0,                                                 // gpu_series size
+        nullptr,                                           // gpu_series
+        {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
+         nullptr, nullptr},                     // intel_gpu_generation
+        &kMoreForEntry73_1440601243Exception2,  // more data
+    },
+};
+
 }  // namespace gpu
 
 #endif  // GPU_CONFIG_GPU_CONTROL_LIST_TESTING_EXCEPTIONS_AUTOGEN_H_
diff --git a/gpu/config/gpu_info.cc b/gpu/config/gpu_info.cc
index 91cf6c1..f5aab647 100644
--- a/gpu/config/gpu_info.cc
+++ b/gpu/config/gpu_info.cc
@@ -188,7 +188,8 @@
       system_visual(0),
       rgba_visual(0),
 #endif
-      oop_rasterization_supported(false) {
+      oop_rasterization_supported(false),
+      subpixel_font_rendering(true) {
 }
 
 GPUInfo::GPUInfo(const GPUInfo& other) = default;
@@ -264,6 +265,7 @@
 #endif
 
     bool oop_rasterization_supported;
+    bool subpixel_font_rendering;
   };
 
   // If this assert fails then most likely something below needs to be updated.
@@ -333,6 +335,7 @@
   enumerator->AddInt64("rgbaVisual", rgba_visual);
 #endif
   enumerator->AddBool("oopRasterizationSupported", oop_rasterization_supported);
+  enumerator->AddBool("subpixelFontRendering", subpixel_font_rendering);
   enumerator->EndAuxAttributes();
 }
 
diff --git a/gpu/config/gpu_info.h b/gpu/config/gpu_info.h
index 720fdc8..ed44414 100644
--- a/gpu/config/gpu_info.h
+++ b/gpu/config/gpu_info.h
@@ -355,6 +355,8 @@
 
   bool oop_rasterization_supported;
 
+  bool subpixel_font_rendering;
+
   // Note: when adding new members, please remember to update EnumerateFields
   // in gpu_info.cc.
 
diff --git a/gpu/config/process_json.py b/gpu/config/process_json.py
index d8852c7..1c52768 100755
--- a/gpu/config/process_json.py
+++ b/gpu/config/process_json.py
@@ -385,6 +385,7 @@
   machine_model_name = None
   machine_model_version = None
   exception_count = 0
+  subpixel_font_rendering = None
   # process the entry
   for key in entry:
     if key == 'id':
@@ -464,6 +465,8 @@
       machine_model_name = entry[key]
     elif key == 'machine_model_version':
       machine_model_version = entry[key]
+    elif key == 'subpixel_font_rendering':
+      subpixel_font_rendering = entry[key]
     elif key == 'exceptions':
       assert not is_exception
       assert exception_count == 0
@@ -503,12 +506,14 @@
   # group a bunch of less used conditions
   if (gl_version != None or pixel_shader_version != None or in_process_gpu or
       gl_reset_notification_strategy != None or direct_rendering_version != None
-      or gpu_count != None or hardware_overlay != None or test_group != 0):
+      or gpu_count != None or hardware_overlay != None or test_group != 0 or
+      subpixel_font_rendering != None):
     write_entry_more_data(entry_id, is_exception, exception_id, gl_type,
                           gl_version, pixel_shader_version, in_process_gpu,
                           gl_reset_notification_strategy,
                           direct_rendering_version, gpu_count, hardware_overlay,
-                          test_group, data_file, data_helper_file)
+                          test_group, subpixel_font_rendering,
+                          data_file, data_helper_file)
   else:
     data_file.write('nullptr,  // more conditions\n')
 
@@ -555,7 +560,8 @@
                           gl_version, pixel_shader_version, in_process_gpu,
                           gl_reset_notification_strategy,
                           direct_rendering_version, gpu_count, hardware_overlay,
-                          test_group, data_file, data_helper_file):
+                          test_group, subpixel_font_rendering, data_file,
+                          data_helper_file):
   # write more data
 
   # Generate a unique name for jumbo build which concatenates multiple
@@ -581,6 +587,8 @@
   write_version(gpu_count, 'gpu_count', data_helper_file)
   write_supported_or_not(hardware_overlay, 'hardware_overlay', data_helper_file)
   write_integer_value(test_group, 'test_group', data_helper_file)
+  write_supported_or_not(subpixel_font_rendering, 'subpixel_font_rendering',
+                         data_helper_file)
   data_helper_file.write('};\n\n')
   # reference more data in entry
   data_file.write('&%s,  // more data\n' % var_name)
diff --git a/gpu/config/software_rendering_list.json b/gpu/config/software_rendering_list.json
index ea1294e..c1da1d8 100644
--- a/gpu/config/software_rendering_list.json
+++ b/gpu/config/software_rendering_list.json
@@ -1342,8 +1342,8 @@
     },
     {
       "id": 137,
-      "description": "GPU rasterization on CrOS is blacklisted on anything but Intel, Imagination, or AMD GPUs for now.",
-      "cr_bugs": [684094],
+      "description": "GPU rasterization on CrOS is blacklisted on anything but Intel, Mali, Imagination, or AMD GPUs for now.",
+      "cr_bugs": [684094, 996858],
       "os": {
         "type": "chromeos"
       },
@@ -1352,6 +1352,8 @@
       ],
       "exceptions": [
         { "vendor_id": "0x8086" },
+        { "gl_renderer": "Mali.*",
+          "subpixel_font_rendering": "unsupported"},
         { "gl_renderer": "PowerVR.*" },
         { "vendor_id": "0x1002" }
       ]
diff --git a/gpu/ipc/common/gpu_info.mojom b/gpu/ipc/common/gpu_info.mojom
index d0f8b34..5d4bacb2 100644
--- a/gpu/ipc/common/gpu_info.mojom
+++ b/gpu/ipc/common/gpu_info.mojom
@@ -172,4 +172,5 @@
   uint64 system_visual;
   uint64 rgba_visual;
   bool oop_rasterization_supported;
+  bool subpixel_font_rendering;
 };
diff --git a/gpu/ipc/common/gpu_info_mojom_traits.cc b/gpu/ipc/common/gpu_info_mojom_traits.cc
index f777a51..d13bd07 100644
--- a/gpu/ipc/common/gpu_info_mojom_traits.cc
+++ b/gpu/ipc/common/gpu_info_mojom_traits.cc
@@ -379,6 +379,7 @@
   out->rgba_visual = data.rgba_visual();
 #endif
   out->oop_rasterization_supported = data.oop_rasterization_supported();
+  out->subpixel_font_rendering = data.subpixel_font_rendering();
 
 #if defined(OS_WIN)
   out->direct_composition = data.direct_composition();
diff --git a/gpu/ipc/common/gpu_info_mojom_traits.h b/gpu/ipc/common/gpu_info_mojom_traits.h
index b617a17..24bcb30 100644
--- a/gpu/ipc/common/gpu_info_mojom_traits.h
+++ b/gpu/ipc/common/gpu_info_mojom_traits.h
@@ -391,6 +391,10 @@
   static bool oop_rasterization_supported(const gpu::GPUInfo& input) {
     return input.oop_rasterization_supported;
   }
+
+  static bool subpixel_font_rendering(const gpu::GPUInfo& input) {
+    return input.subpixel_font_rendering;
+  }
 };
 
 }  // namespace mojo
diff --git a/gpu/ipc/service/BUILD.gn b/gpu/ipc/service/BUILD.gn
index 082ac93..892f58f 100644
--- a/gpu/ipc/service/BUILD.gn
+++ b/gpu/ipc/service/BUILD.gn
@@ -10,6 +10,10 @@
   import("//build/config/mac/mac_sdk.gni")
 }
 
+declare_args() {
+  subpixel_font_rendering_disabled = false
+}
+
 jumbo_component("service") {
   output_name = "gpu_ipc_service"
   sources = [
@@ -52,6 +56,9 @@
   if (is_chromecast) {
     defines += [ "IS_CHROMECAST" ]
   }
+  if (subpixel_font_rendering_disabled) {
+    defines += [ "SUBPIXEL_FONT_RENDERING_DISABLED" ]
+  }
   public_deps = [
     "//base",
     "//components/viz/common",
diff --git a/gpu/ipc/service/gpu_init.cc b/gpu/ipc/service/gpu_init.cc
index d7d124b8b..c86571c 100644
--- a/gpu/ipc/service/gpu_init.cc
+++ b/gpu/ipc/service/gpu_init.cc
@@ -164,6 +164,11 @@
   // Set keys for crash logging based on preliminary gpu info, in case we
   // crash during feature collection.
   gpu::SetKeysForCrashLogging(gpu_info_);
+#if defined(SUBPIXEL_FONT_RENDERING_DISABLED)
+  gpu_info_.subpixel_font_rendering = false;
+#else
+  gpu_info_.subpixel_font_rendering = true;
+#endif
 
 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
   if (gpu_info_.gpu.vendor_id == 0x10de &&  // NVIDIA
@@ -178,7 +183,6 @@
   }
 #endif  // !OS_ANDROID && !IS_CHROMECAST
   gpu_info_.in_process_gpu = false;
-
   bool use_swiftshader = false;
 
   // GL bindings may have already been initialized, specifically on MacOSX.
@@ -554,6 +558,11 @@
   if (!PopGPUInfoCache(&gpu_info_)) {
     CollectBasicGraphicsInfo(command_line, &gpu_info_);
   }
+#if defined(SUBPIXEL_FONT_RENDERING_DISABLED)
+  gpu_info_.subpixel_font_rendering = false;
+#else
+  gpu_info_.subpixel_font_rendering = true;
+#endif
   if (!PopGpuFeatureInfoCache(&gpu_feature_info_)) {
     gpu_feature_info_ = ComputeGpuFeatureInfo(gpu_info_, gpu_preferences_,
                                               command_line, &needs_more_info);
diff --git a/headless/lib/browser/protocol/headless_devtools_session.cc b/headless/lib/browser/protocol/headless_devtools_session.cc
index faa0dd1d..48ac97c 100644
--- a/headless/lib/browser/protocol/headless_devtools_session.cc
+++ b/headless/lib/browser/protocol/headless_devtools_session.cc
@@ -108,9 +108,10 @@
     content::DevToolsAgentHostClient* client,
     content::DevToolsAgentHost* agent_host,
     std::unique_ptr<protocol::Serializable> message) {
-  std::string cbor = message->serialize(/*binary=*/true);
+  std::vector<uint8_t> cbor = message->serializeToBinary();
   if (client->UsesBinaryProtocol()) {
-    client->DispatchProtocolMessage(agent_host, cbor);
+    client->DispatchProtocolMessage(agent_host,
+                                    std::string(cbor.begin(), cbor.end()));
     return;
   }
   std::string json;
diff --git a/ios/build/bots/chromium.mac/ios-simulator-noncq.json b/ios/build/bots/chromium.mac/ios-simulator-noncq.json
index 2db0c4d..c6aaeff 100644
--- a/ios/build/bots/chromium.mac/ios-simulator-noncq.json
+++ b/ios/build/bots/chromium.mac/ios-simulator-noncq.json
@@ -30,15 +30,6 @@
     {
       "xcode parallelization": true,
       "include": "eg2_tests.json",
-      "device type": "iPhone 7",
-      "os": "13.0",
-      "xcode build version": "11a420a",
-      "pool":"Chrome",
-      "host os": "Mac-10.14.6"
-    },
-    {
-      "xcode parallelization": true,
-      "include": "eg2_tests.json",
       "device type": "iPad (6th generation)",
       "os": "13.0",
       "xcode build version": "11a420a",
diff --git a/media/capture/video/chromeos/camera_device_delegate_unittest.cc b/media/capture/video/chromeos/camera_device_delegate_unittest.cc
index 7832572..259ac99b 100644
--- a/media/capture/video/chromeos/camera_device_delegate_unittest.cc
+++ b/media/capture/video/chromeos/camera_device_delegate_unittest.cc
@@ -23,6 +23,7 @@
 #include "media/capture/video/chromeos/mock_video_capture_client.h"
 #include "media/capture/video/chromeos/video_capture_device_factory_chromeos.h"
 #include "media/capture/video/mock_gpu_memory_buffer_manager.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -155,9 +156,9 @@
   }
 
   void GetFakeVendorTagOps(
-      cros::mojom::VendorTagOpsRequest& vendor_tag_ops_request,
+      mojo::PendingReceiver<cros::mojom::VendorTagOps> vendor_tag_ops_receiver,
       cros::mojom::CameraModule::GetVendorTagOpsCallback& cb) {
-    mock_vendor_tag_ops_.Bind(std::move(vendor_tag_ops_request));
+    mock_vendor_tag_ops_.Bind(std::move(vendor_tag_ops_receiver));
   }
 
   void GetFakeCameraInfo(uint32_t camera_id,
diff --git a/media/capture/video/chromeos/camera_hal_delegate.cc b/media/capture/video/chromeos/camera_hal_delegate.cc
index dfa7a8d..bfba4c1c 100644
--- a/media/capture/video/chromeos/camera_hal_delegate.cc
+++ b/media/capture/video/chromeos/camera_hal_delegate.cc
@@ -425,7 +425,7 @@
       base::BindOnce(&CameraHalDelegate::OnSetCallbacksOnIpcThread, this));
 
   camera_module_->GetVendorTagOps(
-      vendor_tag_ops_delegate_.MakeRequest(),
+      vendor_tag_ops_delegate_.MakeReceiver(),
       base::BindOnce(&CameraHalDelegate::OnGotVendorTagOpsOnIpcThread, this));
 }
 
diff --git a/media/capture/video/chromeos/camera_hal_delegate_unittest.cc b/media/capture/video/chromeos/camera_hal_delegate_unittest.cc
index c36ec6848..f9eeee8 100644
--- a/media/capture/video/chromeos/camera_hal_delegate_unittest.cc
+++ b/media/capture/video/chromeos/camera_hal_delegate_unittest.cc
@@ -17,6 +17,7 @@
 #include "media/capture/video/chromeos/mock_vendor_tag_ops.h"
 #include "media/capture/video/chromeos/video_capture_device_factory_chromeos.h"
 #include "media/capture/video/mock_gpu_memory_buffer_manager.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -146,9 +147,10 @@
   };
 
   auto get_vendor_tag_ops_cb =
-      [&](cros::mojom::VendorTagOpsRequest& vendor_tag_ops_request,
+      [&](mojo::PendingReceiver<cros::mojom::VendorTagOps>
+              vendor_tag_ops_receiver,
           cros::mojom::CameraModule::GetVendorTagOpsCallback&) {
-        mock_vendor_tag_ops_.Bind(std::move(vendor_tag_ops_request));
+        mock_vendor_tag_ops_.Bind(std::move(vendor_tag_ops_receiver));
       };
 
   auto set_callbacks_cb =
@@ -169,7 +171,7 @@
       .WillOnce(Invoke(set_callbacks_cb));
   EXPECT_CALL(mock_camera_module_,
               DoGetVendorTagOps(
-                  A<cros::mojom::VendorTagOpsRequest&>(),
+                  A<mojo::PendingReceiver<cros::mojom::VendorTagOps>>(),
                   A<cros::mojom::CameraModule::GetVendorTagOpsCallback&>()))
       .Times(1)
       .WillOnce(Invoke(get_vendor_tag_ops_cb));
diff --git a/media/capture/video/chromeos/mock_camera_module.cc b/media/capture/video/chromeos/mock_camera_module.cc
index b7da84d..7de302515c2 100644
--- a/media/capture/video/chromeos/mock_camera_module.cc
+++ b/media/capture/video/chromeos/mock_camera_module.cc
@@ -61,9 +61,9 @@
 }
 
 void MockCameraModule::GetVendorTagOps(
-    cros::mojom::VendorTagOpsRequest vendor_tag_ops_request,
+    mojo::PendingReceiver<cros::mojom::VendorTagOps> vendor_tag_ops_receiver,
     GetVendorTagOpsCallback callback) {
-  DoGetVendorTagOps(vendor_tag_ops_request, callback);
+  DoGetVendorTagOps(std::move(vendor_tag_ops_receiver), callback);
   std::move(callback).Run();
 }
 
diff --git a/media/capture/video/chromeos/mock_camera_module.h b/media/capture/video/chromeos/mock_camera_module.h
index d8f3d47..84611e5 100644
--- a/media/capture/video/chromeos/mock_camera_module.h
+++ b/media/capture/video/chromeos/mock_camera_module.h
@@ -12,6 +12,7 @@
 #include "media/capture/video/chromeos/mojom/camera3.mojom.h"
 #include "media/capture/video/chromeos/mojom/camera_common.mojom.h"
 #include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace media {
@@ -57,10 +58,12 @@
                     bool enabled,
                     SetTorchModeCallback& callback));
 
-  void GetVendorTagOps(cros::mojom::VendorTagOpsRequest vendor_tag_ops_request,
-                       GetVendorTagOpsCallback callback) override;
+  void GetVendorTagOps(
+      mojo::PendingReceiver<cros::mojom::VendorTagOps> vendor_tag_ops_receiver,
+      GetVendorTagOpsCallback callback) override;
   MOCK_METHOD2(DoGetVendorTagOps,
-               void(cros::mojom::VendorTagOpsRequest& vendor_tag_ops_request,
+               void(mojo::PendingReceiver<cros::mojom::VendorTagOps>
+                        vendor_tag_ops_receiver,
                     GetVendorTagOpsCallback& callback));
 
   void NotifyCameraDeviceChange(int camera_id,
diff --git a/media/capture/video/chromeos/mock_vendor_tag_ops.cc b/media/capture/video/chromeos/mock_vendor_tag_ops.cc
index d4799904..137f99a 100644
--- a/media/capture/video/chromeos/mock_vendor_tag_ops.cc
+++ b/media/capture/video/chromeos/mock_vendor_tag_ops.cc
@@ -12,7 +12,7 @@
 namespace unittest_internal {
 
 MockVendorTagOps::MockVendorTagOps()
-    : mock_vendor_tag_ops_thread_("MockVendorTagOpsThread"), binding_(this) {
+    : mock_vendor_tag_ops_thread_("MockVendorTagOpsThread") {
   CHECK(mock_vendor_tag_ops_thread_.Start());
 }
 
@@ -23,14 +23,15 @@
   mock_vendor_tag_ops_thread_.Stop();
 }
 
-void MockVendorTagOps::Bind(cros::mojom::VendorTagOpsRequest request) {
+void MockVendorTagOps::Bind(
+    mojo::PendingReceiver<cros::mojom::VendorTagOps> receiver) {
   base::WaitableEvent done(base::WaitableEvent::ResetPolicy::MANUAL,
                            base::WaitableEvent::InitialState::NOT_SIGNALED);
   cros::mojom::CameraModulePtrInfo ptr_info;
   mock_vendor_tag_ops_thread_.task_runner()->PostTask(
       FROM_HERE,
       base::BindOnce(&MockVendorTagOps::BindOnThread, base::Unretained(this),
-                     base::Unretained(&done), std::move(request)));
+                     base::Unretained(&done), std::move(receiver)));
   done.Wait();
 }
 
@@ -52,14 +53,13 @@
 }
 
 void MockVendorTagOps::CloseBindingOnThread() {
-  if (binding_.is_bound()) {
-    binding_.Close();
-  }
+  receiver_.reset();
 }
 
-void MockVendorTagOps::BindOnThread(base::WaitableEvent* done,
-                                    cros::mojom::VendorTagOpsRequest request) {
-  binding_.Bind(std::move(request));
+void MockVendorTagOps::BindOnThread(
+    base::WaitableEvent* done,
+    mojo::PendingReceiver<cros::mojom::VendorTagOps> receiver) {
+  receiver_.Bind(std::move(receiver));
   done->Signal();
 }
 
diff --git a/media/capture/video/chromeos/mock_vendor_tag_ops.h b/media/capture/video/chromeos/mock_vendor_tag_ops.h
index 1c45c3272a..d9bd471 100644
--- a/media/capture/video/chromeos/mock_vendor_tag_ops.h
+++ b/media/capture/video/chromeos/mock_vendor_tag_ops.h
@@ -11,7 +11,8 @@
 
 #include "base/threading/thread.h"
 #include "media/capture/video/chromeos/mojom/camera_common.mojom.h"
-#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace media {
@@ -22,7 +23,7 @@
   MockVendorTagOps();
   ~MockVendorTagOps();
 
-  void Bind(cros::mojom::VendorTagOpsRequest request);
+  void Bind(mojo::PendingReceiver<cros::mojom::VendorTagOps> receiver);
 
   MOCK_METHOD0(DoGetTagCount, int32_t());
   void GetTagCount(GetTagCountCallback callback);
@@ -45,10 +46,10 @@
   void CloseBindingOnThread();
 
   void BindOnThread(base::WaitableEvent* done,
-                    cros::mojom::VendorTagOpsRequest request);
+                    mojo::PendingReceiver<cros::mojom::VendorTagOps> receiver);
 
   base::Thread mock_vendor_tag_ops_thread_;
-  mojo::Binding<cros::mojom::VendorTagOps> binding_;
+  mojo::Receiver<cros::mojom::VendorTagOps> receiver_{this};
 };
 
 }  // namespace unittest_internal
diff --git a/media/capture/video/chromeos/mojom/camera_common.mojom b/media/capture/video/chromeos/mojom/camera_common.mojom
index fadf3937..be70337 100644
--- a/media/capture/video/chromeos/mojom/camera_common.mojom
+++ b/media/capture/video/chromeos/mojom/camera_common.mojom
@@ -118,5 +118,6 @@
   // should fill in all the vendor tag operation methods, or leave ops unchanged
   // if no vendor tags are defined.
   [MinVersion=2]
-  GetVendorTagOps@6(VendorTagOps& vendor_tag_ops_request) => ();
+  GetVendorTagOps@6(pending_receiver<VendorTagOps> vendor_tag_ops_request)
+      => ();
 };
diff --git a/media/capture/video/chromeos/vendor_tag_ops_delegate.cc b/media/capture/video/chromeos/vendor_tag_ops_delegate.cc
index 24f47f12..2ce163e 100644
--- a/media/capture/video/chromeos/vendor_tag_ops_delegate.cc
+++ b/media/capture/video/chromeos/vendor_tag_ops_delegate.cc
@@ -17,12 +17,13 @@
 
 VendorTagOpsDelegate::~VendorTagOpsDelegate() = default;
 
-cros::mojom::VendorTagOpsRequest VendorTagOpsDelegate::MakeRequest() {
+mojo::PendingReceiver<cros::mojom::VendorTagOps>
+VendorTagOpsDelegate::MakeReceiver() {
   DCHECK(ipc_task_runner_->RunsTasksInCurrentSequence());
-  auto request = mojo::MakeRequest(&vendor_tag_ops_);
-  vendor_tag_ops_.set_connection_error_handler(
+  auto receiver = vendor_tag_ops_.BindNewPipeAndPassReceiver();
+  vendor_tag_ops_.set_disconnect_handler(
       base::BindOnce(&VendorTagOpsDelegate::Reset, base::Unretained(this)));
-  return request;
+  return receiver;
 }
 
 void VendorTagOpsDelegate::Initialize() {
diff --git a/media/capture/video/chromeos/vendor_tag_ops_delegate.h b/media/capture/video/chromeos/vendor_tag_ops_delegate.h
index 3920435..206394f 100644
--- a/media/capture/video/chromeos/vendor_tag_ops_delegate.h
+++ b/media/capture/video/chromeos/vendor_tag_ops_delegate.h
@@ -10,6 +10,8 @@
 #include <vector>
 
 #include "media/capture/video/chromeos/mojom/camera_common.mojom.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
 
 namespace media {
 
@@ -28,7 +30,7 @@
 
   // Setups/Teardowns the VendorTagOpsDelegate instance. All methods here should
   // be called on |ipc_task_runner_|.
-  cros::mojom::VendorTagOpsRequest MakeRequest();
+  mojo::PendingReceiver<cros::mojom::VendorTagOps> MakeReceiver();
   void Initialize();
   void Reset();
 
@@ -49,7 +51,7 @@
   void OnGotTagType(uint32_t tag, int32_t type);
 
   scoped_refptr<base::SequencedTaskRunner> ipc_task_runner_;
-  cros::mojom::VendorTagOpsPtr vendor_tag_ops_;
+  mojo::Remote<cros::mojom::VendorTagOps> vendor_tag_ops_;
 
   // The paritally initialized tags. A tag with its info would be moved to
   // |name_map_| and |tag_map_| once it's fully initialized. The |inited_| event
diff --git a/media/gpu/vaapi/vaapi_image_processor.cc b/media/gpu/vaapi/vaapi_image_processor.cc
index 885a0b8..47d6c0c 100644
--- a/media/gpu/vaapi/vaapi_image_processor.cc
+++ b/media/gpu/vaapi/vaapi_image_processor.cc
@@ -106,11 +106,19 @@
   }
 
   if (!base::Contains(input_config.preferred_storage_types,
-                      VideoFrame::STORAGE_DMABUFS) ||
+                      VideoFrame::STORAGE_DMABUFS) &&
+      !base::Contains(input_config.preferred_storage_types,
+                      VideoFrame::STORAGE_GPU_MEMORY_BUFFER)) {
+    VLOGF(2) << "VaapiImageProcessor supports Dmabuf-backed or GpuMemoryBuffer"
+             << " based VideoFrame only for input";
+    return nullptr;
+  }
+  if (!base::Contains(output_config.preferred_storage_types,
+                      VideoFrame::STORAGE_DMABUFS) &&
       !base::Contains(output_config.preferred_storage_types,
-                      VideoFrame::STORAGE_DMABUFS)) {
-    VLOGF(2) << "VaapiImageProcessor supports Dmabuf-backed VideoFrame only "
-             << "for both input and output";
+                      VideoFrame::STORAGE_GPU_MEMORY_BUFFER)) {
+    VLOGF(2) << "VaapiImageProcessor supports Dmabuf-backed or GpuMemoryBuffer"
+             << " based VideoFrame only for output";
     return nullptr;
   }
 
@@ -127,6 +135,12 @@
     return nullptr;
   }
 
+  // Size is irrelevant for a VPP context.
+  if (!vaapi_wrapper->CreateContext(gfx::Size())) {
+    VLOGF(1) << "Failed to create context for VPP";
+    return nullptr;
+  }
+
   // We should restrict the acceptable PortConfig for input and output both to
   // the one returned by GetPlatformVideoFrameLayout(). However,
   // ImageProcessorFactory interface doesn't provide information about what
diff --git a/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.cc b/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.cc
index f965406..9d6ad3c 100644
--- a/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.cc
+++ b/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.cc
@@ -456,6 +456,12 @@
     return PLATFORM_FAILURE;
   }
 
+  // Size is irrelevant for a VPP context.
+  if (!vpp_vaapi_wrapper->CreateContext(gfx::Size())) {
+    VLOGF(1) << "Failed to create context for VPP";
+    return PLATFORM_FAILURE;
+  }
+
   encoder_task_runner_ =
       base::CreateSingleThreadTaskRunner({base::ThreadPool(), base::MayBlock(),
                                           base::TaskPriority::USER_BLOCKING});
diff --git a/media/gpu/vaapi/vaapi_mjpeg_decode_accelerator.cc b/media/gpu/vaapi/vaapi_mjpeg_decode_accelerator.cc
index 1dca884f..fa5e3a6f 100644
--- a/media/gpu/vaapi/vaapi_mjpeg_decode_accelerator.cc
+++ b/media/gpu/vaapi/vaapi_mjpeg_decode_accelerator.cc
@@ -163,6 +163,12 @@
     return false;
   }
 
+  // Size is irrelevant for a VPP context.
+  if (!vpp_vaapi_wrapper_->CreateContext(gfx::Size())) {
+    VLOGF(1) << "Failed to create context for VPP";
+    return false;
+  }
+
   gpu_memory_buffer_support_ = std::make_unique<gpu::GpuMemoryBufferSupport>();
 
   if (!decoder_thread_.Start()) {
diff --git a/media/gpu/vaapi/vaapi_video_decode_accelerator.cc b/media/gpu/vaapi/vaapi_video_decode_accelerator.cc
index 17594e8..8ebe8e0a 100644
--- a/media/gpu/vaapi/vaapi_video_decode_accelerator.cc
+++ b/media/gpu/vaapi/vaapi_video_decode_accelerator.cc
@@ -645,10 +645,14 @@
     vpp_vaapi_wrapper_ = VaapiWrapper::Create(
         VaapiWrapper::kVideoProcess, VAProfileNone,
         base::BindRepeating(&ReportToUMA, VAAPI_VPP_ERROR));
-    if (!vpp_vaapi_wrapper_) {
-      VLOGF(1) << "Failed initializing VppVaapiWrapper";
-      NotifyError(PLATFORM_FAILURE);
-    }
+    RETURN_AND_NOTIFY_ON_FAILURE(vpp_vaapi_wrapper_,
+                                 "Failed to initialize VppVaapiWrapper",
+                                 PLATFORM_FAILURE, );
+
+    // Size is irrelevant for a VPP context.
+    RETURN_AND_NOTIFY_ON_FAILURE(vpp_vaapi_wrapper_->CreateContext(gfx::Size()),
+                                 "Failed to create Context",
+                                 PLATFORM_FAILURE, );
   }
 
   for (size_t i = 0; i < buffers.size(); ++i) {
diff --git a/media/gpu/vaapi/vaapi_wrapper.cc b/media/gpu/vaapi/vaapi_wrapper.cc
index 102df2f..ae8245cb 100644
--- a/media/gpu/vaapi/vaapi_wrapper.cc
+++ b/media/gpu/vaapi/vaapi_wrapper.cc
@@ -1412,6 +1412,8 @@
   // (non-null) IDs until the signature gets updated.
   constexpr VASurfaceID* empty_va_surfaces_ids_pointer = nullptr;
   constexpr size_t empty_va_surfaces_ids_size = 0u;
+  // TODO(hiroh): VA_PROGRESSIVE might not ought to be set in VPP case. Think
+  // about removing it if something wrong happens or it turns out to be wrong.
   const VAStatus va_res =
       vaCreateContext(va_display_, va_config_id_, size.width(), size.height(),
                       VA_PROGRESSIVE, empty_va_surfaces_ids_pointer,
@@ -1923,6 +1925,19 @@
     const scoped_refptr<VASurface>& va_surface_src,
     const scoped_refptr<VASurface>& va_surface_dest) {
   base::AutoLock auto_lock(*va_lock_);
+
+  if (va_buffers_.empty()) {
+    DCHECK_NE(VA_INVALID_ID, va_context_id_);
+    // Create a buffer for VPP if it has not been created.
+    VABufferID buffer_id;
+    VAStatus va_res = vaCreateBuffer(
+        va_display_, va_context_id_, VAProcPipelineParameterBufferType,
+        sizeof(VAProcPipelineParameterBuffer), 1, nullptr, &buffer_id);
+    VA_SUCCESS_OR_RETURN(va_res, "Couldn't create buffer", false);
+    DCHECK_NE(buffer_id, VA_INVALID_ID);
+    va_buffers_.emplace(buffer_id);
+  }
+
   DCHECK_EQ(va_buffers_.size(), 1u);
   VABufferID buffer_id = *va_buffers_.begin();
   {
@@ -2043,25 +2058,6 @@
                      required_attribs.empty() ? nullptr : &required_attribs[0],
                      required_attribs.size(), &va_config_id_);
   VA_SUCCESS_OR_RETURN(va_res, "vaCreateConfig failed", false);
-
-  if (mode != kVideoProcess)
-    return true;
-
-  // Creates context and buffer here in the case of kVideoProcess.
-  constexpr size_t kIrrelevantWidth = 0;
-  constexpr size_t kIrrelevantHeight = 0;
-  va_res = vaCreateContext(va_display_, va_config_id_, kIrrelevantWidth,
-                           kIrrelevantHeight, 0, NULL, 0, &va_context_id_);
-  VA_SUCCESS_OR_RETURN(va_res, "Couldn't create context", false);
-
-  VABufferID buffer_id;
-  va_res = vaCreateBuffer(
-      va_display_, va_context_id_, VAProcPipelineParameterBufferType,
-      sizeof(VAProcPipelineParameterBuffer), 1, NULL, &buffer_id);
-  VA_SUCCESS_OR_RETURN(va_res, "Couldn't create buffer", false);
-  DCHECK_NE(buffer_id, VA_INVALID_ID);
-  va_buffers_.emplace(buffer_id);
-
   return true;
 }
 
diff --git a/mojo/public/js/BUILD.gn b/mojo/public/js/BUILD.gn
index c67a006..9054cb5 100644
--- a/mojo/public/js/BUILD.gn
+++ b/mojo/public/js/BUILD.gn
@@ -84,6 +84,7 @@
                       "language_in=ECMASCRIPT_2017",
                       "language_out=ECMASCRIPT_2015",
                       "generate_exports",
+                      "export_local_property_definitions",
                     ]
   }
 } else {
diff --git a/mojo/public/js/interface_support.js b/mojo/public/js/interface_support.js
index 938e3117..2373f382 100644
--- a/mojo/public/js/interface_support.js
+++ b/mojo/public/js/interface_support.js
@@ -150,15 +150,29 @@
 };
 
 /**
+ * @interface
+ * @export
+ */
+mojo.internal.interfaceSupport.PendingReceiver = class {
+  /**
+   * @return {!MojoHandle}
+   * @export
+   */
+  get handle() {}
+};
+
+/**
  * Generic helper used to implement all generated remote classes. Knows how to
  * serialize requests and deserialize their replies, both according to
  * declarative message structure specs.
- * @template T
+ *
+ * TODO(crbug.com/1012109): Use a bounded generic type instead of
+ * mojo.internal.interfaceSupport.PendingReceiver.
  * @export
  */
 mojo.internal.interfaceSupport.InterfaceRemoteBase = class {
   /**
-   * @param {!function(new:T, !MojoHandle)} requestType
+   * @param {!function(new:mojo.internal.interfaceSupport.PendingReceiver, !MojoHandle)} requestType
    * @param {MojoHandle=} opt_handle The message pipe handle to use as a remote
    *     endpoint. If null, this object must be bound with bindHandle before
    *     it can be used to send any messages.
@@ -168,7 +182,7 @@
     /** @public {?MojoHandle} */
     this.handle = null;
 
-    /** @private {!function(new:T, !MojoHandle)} */
+    /** @private {!function(new:mojo.internal.interfaceSupport.PendingReceiver, !MojoHandle)} */
     this.requestType_ = requestType;
 
     /** @private {?mojo.internal.interfaceSupport.HandleReader} */
@@ -194,7 +208,7 @@
   }
 
   /**
-   * @return {!T}
+   * @return {!mojo.internal.interfaceSupport.PendingReceiver}
    */
   bindNewPipeAndPassReceiver() {
     let {handle0, handle1} = Mojo.createMessagePipe();
diff --git a/mojo/public/js/test/BUILD.gn b/mojo/public/js/test/BUILD.gn
index 00fb521..c969eac 100644
--- a/mojo/public/js/test/BUILD.gn
+++ b/mojo/public/js/test/BUILD.gn
@@ -53,6 +53,7 @@
                       "language_in=ECMASCRIPT_2017",
                       "language_out=ECMASCRIPT5_STRICT",
                       "generate_exports",
+                      "export_local_property_definitions",
                     ]
   }
 } else {
diff --git a/mojo/public/tools/bindings/generators/js_templates/lite/interface_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/lite/interface_definition.tmpl
index 4cd08f8e..55bdc964 100644
--- a/mojo/public/tools/bindings/generators/js_templates/lite/interface_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/js_templates/lite/interface_definition.tmpl
@@ -28,7 +28,10 @@
 goog.provide('{{module.namespace}}.{{interface.name}}{{primitives_names.pending_receiver}}');
 {% endif %}
 
-/** @export */
+/**
+ * @implements {mojo.internal.interfaceSupport.PendingReceiver}
+ * @export
+ */
 {{module.namespace}}.{{interface.name}}{{primitives_names.pending_receiver}} = class {
   /** @param {!MojoHandle} handle */
   constructor(handle) {
diff --git a/net/dns/dns_client.cc b/net/dns/dns_client.cc
index 2718feb..9155d7c 100644
--- a/net/dns/dns_client.cc
+++ b/net/dns/dns_client.cc
@@ -80,9 +80,6 @@
   }
 }
 
-constexpr base::TimeDelta kInitialDoHTimeout =
-    base::TimeDelta::FromMilliseconds(5000);
-
 class DnsClientImpl : public DnsClient,
                       public NetworkChangeNotifier::ConnectionTypeObserver {
  public:
@@ -96,7 +93,7 @@
         rand_int_callback_(rand_int_callback) {
     NetworkChangeNotifier::AddConnectionTypeObserver(this);
     delayed_probes_allowed_timer_.Start(
-        FROM_HERE, kInitialDoHTimeout,
+        FROM_HERE, kInitialDohTimeout,
         base::Bind(&DnsClientImpl::SetProbesAllowed, base::Unretained(this)));
   }
 
@@ -166,6 +163,10 @@
 
   void SetRequestContextForProbes(
       URLRequestContext* url_request_context) override {
+    DCHECK(url_request_context);
+    DCHECK(!url_request_context_for_probes_ ||
+           url_request_context == url_request_context_for_probes_);
+
     url_request_context_for_probes_ = url_request_context;
   }
 
@@ -174,6 +175,7 @@
       return;
 
     factory_->CancelDohProbes();
+    delayed_probes_start_timer_.Stop();
     url_request_context_for_probes_ = nullptr;
   }
 
@@ -203,6 +205,15 @@
     session_->SetProbeSuccess(index, success);
   }
 
+  void SetTransactionFactoryForTesting(
+      std::unique_ptr<DnsTransactionFactory> factory) override {
+    factory_ = std::move(factory);
+  }
+
+  void StartDohProbesForTesting() override {
+    StartDohProbes(false /* network_change */);
+  }
+
  private:
   base::Optional<DnsConfig> BuildEffectiveConfig() const {
     DnsConfig config;
@@ -279,14 +290,14 @@
       return;
 
     if (probes_allowed_) {
+      delayed_probes_start_timer_.Stop();
       factory_->StartDohProbes(url_request_context_for_probes_, network_change);
     } else {
-      base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
-          FROM_HERE,
+      delayed_probes_start_timer_.Start(
+          FROM_HERE, delayed_probes_allowed_timer_.GetCurrentDelay(),
           base::BindOnce(&DnsTransactionFactory::StartDohProbes,
                          factory_->weak_factory_.GetWeakPtr(),
-                         url_request_context_for_probes_, network_change),
-          delayed_probes_allowed_timer_.GetCurrentDelay());
+                         url_request_context_for_probes_, network_change));
     }
   }
 
@@ -306,6 +317,7 @@
   // prevent interference with startup tasks.
   bool probes_allowed_;
   base::OneShotTimer delayed_probes_allowed_timer_;
+  base::OneShotTimer delayed_probes_start_timer_;
   URLRequestContext* url_request_context_for_probes_;
 
   NetLog* net_log_;
@@ -319,6 +331,10 @@
 }  // namespace
 
 // static
+const base::TimeDelta DnsClient::kInitialDohTimeout =
+    base::TimeDelta::FromSeconds(5);
+
+// static
 std::unique_ptr<DnsClient> DnsClient::CreateClient(NetLog* net_log) {
   return std::make_unique<DnsClientImpl>(
       net_log, ClientSocketFactory::GetDefaultFactory(),
diff --git a/net/dns/dns_client.h b/net/dns/dns_client.h
index 8298ada..4eae6e2f 100644
--- a/net/dns/dns_client.h
+++ b/net/dns/dns_client.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "base/optional.h"
+#include "base/time/time.h"
 #include "net/base/net_export.h"
 #include "net/base/rand_callback.h"
 #include "net/dns/dns_config.h"
@@ -28,6 +29,7 @@
 class NET_EXPORT DnsClient {
  public:
   static const int kMaxInsecureFallbackFailures = 16;
+  static const base::TimeDelta kInitialDohTimeout;
 
   virtual ~DnsClient() {}
 
@@ -83,6 +85,10 @@
 
   virtual void SetProbeSuccessForTest(unsigned index, bool success) = 0;
 
+  virtual void SetTransactionFactoryForTesting(
+      std::unique_ptr<DnsTransactionFactory> factory) = 0;
+  virtual void StartDohProbesForTesting() = 0;
+
   // Creates default client.
   static std::unique_ptr<DnsClient> CreateClient(NetLog* net_log);
 
diff --git a/net/dns/dns_client_unittest.cc b/net/dns/dns_client_unittest.cc
index edb83024..b1b9a69 100644
--- a/net/dns/dns_client_unittest.cc
+++ b/net/dns/dns_client_unittest.cc
@@ -8,9 +8,11 @@
 
 #include "base/bind.h"
 #include "base/rand_util.h"
+#include "base/test/task_environment.h"
 #include "net/base/ip_address.h"
 #include "net/base/ip_endpoint.h"
 #include "net/dns/dns_config.h"
+#include "net/dns/dns_test_util.h"
 #include "net/socket/socket_test_util.h"
 #include "net/test/test_with_task_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -18,6 +20,8 @@
 
 namespace net {
 
+class ClientSocketFactory;
+
 namespace {
 
 class AlwaysFailSocketFactory : public MockClientSocketFactory {
@@ -32,6 +36,10 @@
 
 class DnsClientTest : public TestWithTaskEnvironment {
  protected:
+  DnsClientTest()
+      : TestWithTaskEnvironment(
+            base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
+
   void SetUp() override {
     client_ = DnsClient::CreateClientForTesting(
         nullptr /* net_log */, &socket_factory_, base::Bind(&base::RandInt));
@@ -58,8 +66,6 @@
 
   std::unique_ptr<DnsClient> client_;
   AlwaysFailSocketFactory socket_factory_;
-
- private:
 };
 
 TEST_F(DnsClientTest, NoConfig) {
@@ -240,6 +246,58 @@
   EXPECT_FALSE(client_->GetEffectiveConfig());
 }
 
+TEST_F(DnsClientTest, DohProbes) {
+  URLRequestContext context;
+  client_->SetRequestContextForProbes(&context);
+
+  client_->SetSystemConfig(ValidConfigWithDoh());
+  auto transaction_factory =
+      std::make_unique<MockDnsTransactionFactory>(MockDnsClientRuleList());
+  auto* transaction_factory_ptr = transaction_factory.get();
+  client_->SetTransactionFactoryForTesting(std::move(transaction_factory));
+
+  client_->StartDohProbesForTesting();
+  EXPECT_FALSE(transaction_factory_ptr->doh_probes_running());
+  FastForwardBy(DnsClient::kInitialDohTimeout);
+  EXPECT_TRUE(transaction_factory_ptr->doh_probes_running());
+}
+
+TEST_F(DnsClientTest, CancelDohProbesBeforeEnabled) {
+  URLRequestContext context;
+  client_->SetRequestContextForProbes(&context);
+
+  client_->SetSystemConfig(ValidConfigWithDoh());
+  auto transaction_factory =
+      std::make_unique<MockDnsTransactionFactory>(MockDnsClientRuleList());
+  auto* transaction_factory_ptr = transaction_factory.get();
+  client_->SetTransactionFactoryForTesting(std::move(transaction_factory));
+
+  client_->StartDohProbesForTesting();
+  EXPECT_FALSE(transaction_factory_ptr->doh_probes_running());
+  client_->CancelProbesForContext(&context);
+
+  FastForwardUntilNoTasksRemain();
+  EXPECT_FALSE(transaction_factory_ptr->doh_probes_running());
+}
+
+TEST_F(DnsClientTest, CancelDohProbesAfterEnabled) {
+  URLRequestContext context;
+  client_->SetRequestContextForProbes(&context);
+
+  client_->SetSystemConfig(ValidConfigWithDoh());
+  auto transaction_factory =
+      std::make_unique<MockDnsTransactionFactory>(MockDnsClientRuleList());
+  auto* transaction_factory_ptr = transaction_factory.get();
+  client_->SetTransactionFactoryForTesting(std::move(transaction_factory));
+
+  client_->StartDohProbesForTesting();
+  FastForwardUntilNoTasksRemain();
+  EXPECT_TRUE(transaction_factory_ptr->doh_probes_running());
+
+  client_->CancelProbesForContext(&context);
+  EXPECT_FALSE(transaction_factory_ptr->doh_probes_running());
+}
+
 }  // namespace
 
 }  // namespace net
diff --git a/net/dns/dns_test_util.cc b/net/dns/dns_test_util.cc
index 0802e90..5460db1 100644
--- a/net/dns/dns_test_util.cc
+++ b/net/dns/dns_test_util.cc
@@ -7,7 +7,6 @@
 #include "base/big_endian.h"
 #include "base/bind.h"
 #include "base/location.h"
-#include "base/memory/weak_ptr.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/single_thread_task_runner.h"
 #include "base/sys_byteorder.h"
@@ -18,7 +17,6 @@
 #include "net/dns/address_sorter.h"
 #include "net/dns/dns_hosts.h"
 #include "net/dns/dns_query.h"
-#include "net/dns/dns_transaction.h"
 #include "net/dns/dns_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -180,142 +178,6 @@
   return record;
 }
 
-// A DnsTransaction which uses MockDnsClientRuleList to determine the response.
-class MockTransaction : public DnsTransaction,
-                        public base::SupportsWeakPtr<MockTransaction> {
- public:
-  MockTransaction(const MockDnsClientRuleList& rules,
-                  const std::string& hostname,
-                  uint16_t qtype,
-                  bool secure,
-                  DnsConfig::SecureDnsMode secure_dns_mode,
-                  URLRequestContext* url_request_context,
-                  DnsTransactionFactory::CallbackType callback)
-      : result_(MockDnsClientRule::FAIL),
-        hostname_(hostname),
-        qtype_(qtype),
-        callback_(std::move(callback)),
-        started_(false),
-        delayed_(false) {
-    // Find the relevant rule which matches |qtype|, |secure|, prefix of
-    // |hostname|, and |url_request_context| (iff the rule context is not
-    // null).
-    for (size_t i = 0; i < rules.size(); ++i) {
-      const std::string& prefix = rules[i].prefix;
-      if ((rules[i].qtype == qtype) && (rules[i].secure == secure) &&
-          (hostname.size() >= prefix.size()) &&
-          (hostname.compare(0, prefix.size(), prefix) == 0) &&
-          (!rules[i].context || rules[i].context == url_request_context)) {
-        const MockDnsClientRule::Result* result = &rules[i].result;
-        result_ = MockDnsClientRule::Result(result->type);
-        delayed_ = rules[i].delay;
-
-        // Generate a DnsResponse when not provided with the rule.
-        std::vector<DnsResourceRecord> authority_records;
-        std::string dns_name;
-        CHECK(DNSDomainFromDot(hostname_, &dns_name));
-        base::Optional<DnsQuery> query(base::in_place, 22 /* id */, dns_name,
-                                       qtype_);
-        switch (result->type) {
-          case MockDnsClientRule::NODOMAIN:
-          case MockDnsClientRule::EMPTY:
-            DCHECK(!result->response);  // Not expected to be provided.
-            authority_records = {BuildSoaRecord(hostname_)};
-            result_.response = std::make_unique<DnsResponse>(
-                22 /* id */, false /* is_authoritative */,
-                std::vector<DnsResourceRecord>() /* answers */,
-                authority_records,
-                std::vector<DnsResourceRecord>() /* additional_records */,
-                query,
-                result->type == MockDnsClientRule::NODOMAIN
-                    ? dns_protocol::kRcodeNXDOMAIN
-                    : 0);
-            break;
-          case MockDnsClientRule::FAIL:
-          case MockDnsClientRule::TIMEOUT:
-            DCHECK(!result->response);  // Not expected to be provided.
-            break;
-          case MockDnsClientRule::OK:
-            if (result->response) {
-              // Copy response in case |rules| are destroyed before the
-              // transaction completes.
-              result_.response = std::make_unique<DnsResponse>(
-                  result->response->io_buffer(),
-                  result->response->io_buffer_size());
-              CHECK(result_.response->InitParseWithoutQuery(
-                  result->response->io_buffer_size()));
-            } else {
-              // Generated response only available for address types.
-              DCHECK(qtype_ == dns_protocol::kTypeA ||
-                     qtype_ == dns_protocol::kTypeAAAA);
-              result_.response = BuildTestDnsResponse(
-                  hostname_, qtype_ == dns_protocol::kTypeA
-                                 ? IPAddress::IPv4Localhost()
-                                 : IPAddress::IPv6Localhost());
-            }
-            break;
-          case MockDnsClientRule::MALFORMED:
-            DCHECK(!result->response);  // Not expected to be provided.
-            result_.response = CreateMalformedResponse(hostname_, qtype_);
-            break;
-        }
-
-        break;
-      }
-    }
-  }
-
-  const std::string& GetHostname() const override { return hostname_; }
-
-  uint16_t GetType() const override { return qtype_; }
-
-  void Start() override {
-    EXPECT_FALSE(started_);
-    started_ = true;
-    if (delayed_)
-      return;
-    // Using WeakPtr to cleanly cancel when transaction is destroyed.
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(&MockTransaction::Finish, AsWeakPtr()));
-  }
-
-  void FinishDelayedTransaction() {
-    EXPECT_TRUE(delayed_);
-    delayed_ = false;
-    Finish();
-  }
-
-  bool delayed() const { return delayed_; }
-
- private:
-  void Finish() {
-    switch (result_.type) {
-      case MockDnsClientRule::NODOMAIN:
-      case MockDnsClientRule::FAIL:
-        std::move(callback_).Run(this, ERR_NAME_NOT_RESOLVED,
-                                 result_.response.get());
-        break;
-      case MockDnsClientRule::EMPTY:
-      case MockDnsClientRule::OK:
-      case MockDnsClientRule::MALFORMED:
-        std::move(callback_).Run(this, OK, result_.response.get());
-        break;
-      case MockDnsClientRule::TIMEOUT:
-        std::move(callback_).Run(this, ERR_DNS_TIMED_OUT, nullptr);
-        break;
-    }
-  }
-
-  void SetRequestPriority(RequestPriority priority) override {}
-
-  MockDnsClientRule::Result result_;
-  const std::string hostname_;
-  const uint16_t qtype_;
-  DnsTransactionFactory::CallbackType callback_;
-  bool started_;
-  bool delayed_;
-};
-
 }  // namespace
 
 std::unique_ptr<DnsResponse> BuildTestDnsResponse(std::string name,
@@ -452,68 +314,201 @@
 
 MockDnsClientRule::MockDnsClientRule(MockDnsClientRule&& rule) = default;
 
-// A DnsTransactionFactory which creates MockTransaction.
-class MockDnsClient::MockTransactionFactory : public DnsTransactionFactory {
+// A DnsTransaction which uses MockDnsClientRuleList to determine the response.
+class MockDnsTransactionFactory::MockTransaction
+    : public DnsTransaction,
+      public base::SupportsWeakPtr<MockTransaction> {
  public:
-  explicit MockTransactionFactory(MockDnsClientRuleList rules)
-      : rules_(std::move(rules)) {}
+  MockTransaction(const MockDnsClientRuleList& rules,
+                  const std::string& hostname,
+                  uint16_t qtype,
+                  bool secure,
+                  DnsConfig::SecureDnsMode secure_dns_mode,
+                  URLRequestContext* url_request_context,
+                  DnsTransactionFactory::CallbackType callback)
+      : result_(MockDnsClientRule::FAIL),
+        hostname_(hostname),
+        qtype_(qtype),
+        callback_(std::move(callback)),
+        started_(false),
+        delayed_(false) {
+    // Find the relevant rule which matches |qtype|, |secure|, prefix of
+    // |hostname|, and |url_request_context| (iff the rule context is not
+    // null).
+    for (size_t i = 0; i < rules.size(); ++i) {
+      const std::string& prefix = rules[i].prefix;
+      if ((rules[i].qtype == qtype) && (rules[i].secure == secure) &&
+          (hostname.size() >= prefix.size()) &&
+          (hostname.compare(0, prefix.size(), prefix) == 0) &&
+          (!rules[i].context || rules[i].context == url_request_context)) {
+        const MockDnsClientRule::Result* result = &rules[i].result;
+        result_ = MockDnsClientRule::Result(result->type);
+        delayed_ = rules[i].delay;
 
-  ~MockTransactionFactory() override = default;
+        // Generate a DnsResponse when not provided with the rule.
+        std::vector<DnsResourceRecord> authority_records;
+        std::string dns_name;
+        CHECK(DNSDomainFromDot(hostname_, &dns_name));
+        base::Optional<DnsQuery> query(base::in_place, 22 /* id */, dns_name,
+                                       qtype_);
+        switch (result->type) {
+          case MockDnsClientRule::NODOMAIN:
+          case MockDnsClientRule::EMPTY:
+            DCHECK(!result->response);  // Not expected to be provided.
+            authority_records = {BuildSoaRecord(hostname_)};
+            result_.response = std::make_unique<DnsResponse>(
+                22 /* id */, false /* is_authoritative */,
+                std::vector<DnsResourceRecord>() /* answers */,
+                authority_records,
+                std::vector<DnsResourceRecord>() /* additional_records */,
+                query,
+                result->type == MockDnsClientRule::NODOMAIN
+                    ? dns_protocol::kRcodeNXDOMAIN
+                    : 0);
+            break;
+          case MockDnsClientRule::FAIL:
+          case MockDnsClientRule::TIMEOUT:
+            DCHECK(!result->response);  // Not expected to be provided.
+            break;
+          case MockDnsClientRule::OK:
+            if (result->response) {
+              // Copy response in case |rules| are destroyed before the
+              // transaction completes.
+              result_.response = std::make_unique<DnsResponse>(
+                  result->response->io_buffer(),
+                  result->response->io_buffer_size());
+              CHECK(result_.response->InitParseWithoutQuery(
+                  result->response->io_buffer_size()));
+            } else {
+              // Generated response only available for address types.
+              DCHECK(qtype_ == dns_protocol::kTypeA ||
+                     qtype_ == dns_protocol::kTypeAAAA);
+              result_.response = BuildTestDnsResponse(
+                  hostname_, qtype_ == dns_protocol::kTypeA
+                                 ? IPAddress::IPv4Localhost()
+                                 : IPAddress::IPv6Localhost());
+            }
+            break;
+          case MockDnsClientRule::MALFORMED:
+            DCHECK(!result->response);  // Not expected to be provided.
+            result_.response = CreateMalformedResponse(hostname_, qtype_);
+            break;
+        }
 
-  std::unique_ptr<DnsTransaction> CreateTransaction(
-      const std::string& hostname,
-      uint16_t qtype,
-      DnsTransactionFactory::CallbackType callback,
-      const NetLogWithSource&,
-      bool secure,
-      DnsConfig::SecureDnsMode secure_dns_mode,
-      URLRequestContext* url_request_context) override {
-    std::unique_ptr<MockTransaction> transaction =
-        std::make_unique<MockTransaction>(rules_, hostname, qtype, secure,
-                                          secure_dns_mode, url_request_context,
-                                          std::move(callback));
-    if (transaction->delayed())
-      delayed_transactions_.push_back(transaction->AsWeakPtr());
-    return transaction;
-  }
-
-  void AddEDNSOption(const OptRecordRdata::Opt& opt) override {}
-
-  base::TimeDelta GetDelayUntilNextProbeForTest(
-      unsigned doh_server_index) override {
-    NOTREACHED();
-    return base::TimeDelta();
-  }
-
-  void StartDohProbes(URLRequestContext* url_request_context,
-                      bool network_change) override {}
-
-  void CancelDohProbes() override {}
-
-  DnsConfig::SecureDnsMode GetSecureDnsModeForTest() override {
-    return DnsConfig::SecureDnsMode::AUTOMATIC;
-  }
-
-  void CompleteDelayedTransactions() {
-    DelayedTransactionList old_delayed_transactions;
-    old_delayed_transactions.swap(delayed_transactions_);
-    for (auto it = old_delayed_transactions.begin();
-         it != old_delayed_transactions.end(); ++it) {
-      if (it->get())
-        (*it)->FinishDelayedTransaction();
+        break;
+      }
     }
   }
 
- private:
-  typedef std::vector<base::WeakPtr<MockTransaction>> DelayedTransactionList;
+  const std::string& GetHostname() const override { return hostname_; }
 
-  MockDnsClientRuleList rules_;
-  DelayedTransactionList delayed_transactions_;
+  uint16_t GetType() const override { return qtype_; }
+
+  void Start() override {
+    EXPECT_FALSE(started_);
+    started_ = true;
+    if (delayed_)
+      return;
+    // Using WeakPtr to cleanly cancel when transaction is destroyed.
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::BindOnce(&MockTransaction::Finish, AsWeakPtr()));
+  }
+
+  void FinishDelayedTransaction() {
+    EXPECT_TRUE(delayed_);
+    delayed_ = false;
+    Finish();
+  }
+
+  bool delayed() const { return delayed_; }
+
+ private:
+  void Finish() {
+    switch (result_.type) {
+      case MockDnsClientRule::NODOMAIN:
+      case MockDnsClientRule::FAIL:
+        std::move(callback_).Run(this, ERR_NAME_NOT_RESOLVED,
+                                 result_.response.get());
+        break;
+      case MockDnsClientRule::EMPTY:
+      case MockDnsClientRule::OK:
+      case MockDnsClientRule::MALFORMED:
+        std::move(callback_).Run(this, OK, result_.response.get());
+        break;
+      case MockDnsClientRule::TIMEOUT:
+        std::move(callback_).Run(this, ERR_DNS_TIMED_OUT, nullptr);
+        break;
+    }
+  }
+
+  void SetRequestPriority(RequestPriority priority) override {}
+
+  MockDnsClientRule::Result result_;
+  const std::string hostname_;
+  const uint16_t qtype_;
+  DnsTransactionFactory::CallbackType callback_;
+  bool started_;
+  bool delayed_;
 };
 
+MockDnsTransactionFactory::MockDnsTransactionFactory(
+    MockDnsClientRuleList rules)
+    : rules_(std::move(rules)) {}
+
+MockDnsTransactionFactory::~MockDnsTransactionFactory() = default;
+
+std::unique_ptr<DnsTransaction> MockDnsTransactionFactory::CreateTransaction(
+    const std::string& hostname,
+    uint16_t qtype,
+    DnsTransactionFactory::CallbackType callback,
+    const NetLogWithSource&,
+    bool secure,
+    DnsConfig::SecureDnsMode secure_dns_mode,
+    URLRequestContext* url_request_context) {
+  std::unique_ptr<MockTransaction> transaction =
+      std::make_unique<MockTransaction>(rules_, hostname, qtype, secure,
+                                        secure_dns_mode, url_request_context,
+                                        std::move(callback));
+  if (transaction->delayed())
+    delayed_transactions_.push_back(transaction->AsWeakPtr());
+  return transaction;
+}
+
+void MockDnsTransactionFactory::AddEDNSOption(const OptRecordRdata::Opt& opt) {}
+
+base::TimeDelta MockDnsTransactionFactory::GetDelayUntilNextProbeForTest(
+    unsigned doh_server_index) {
+  NOTREACHED();
+  return base::TimeDelta();
+}
+
+void MockDnsTransactionFactory::StartDohProbes(
+    URLRequestContext* url_request_context,
+    bool network_change) {
+  doh_probes_running_ = true;
+}
+
+void MockDnsTransactionFactory::CancelDohProbes() {
+  doh_probes_running_ = false;
+}
+
+DnsConfig::SecureDnsMode MockDnsTransactionFactory::GetSecureDnsModeForTest() {
+  return DnsConfig::SecureDnsMode::AUTOMATIC;
+}
+
+void MockDnsTransactionFactory::CompleteDelayedTransactions() {
+  DelayedTransactionList old_delayed_transactions;
+  old_delayed_transactions.swap(delayed_transactions_);
+  for (auto it = old_delayed_transactions.begin();
+       it != old_delayed_transactions.end(); ++it) {
+    if (it->get())
+      (*it)->FinishDelayedTransaction();
+  }
+}
+
 MockDnsClient::MockDnsClient(DnsConfig config, MockDnsClientRuleList rules)
     : config_(std::move(config)),
-      factory_(new MockTransactionFactory(std::move(rules))),
+      factory_(new MockDnsTransactionFactory(std::move(rules))),
       address_sorter_(new MockAddressSorter()) {
   effective_config_ = BuildEffectiveConfig();
 }
@@ -605,6 +600,16 @@
 
 void MockDnsClient::SetProbeSuccessForTest(unsigned index, bool success) {}
 
+void MockDnsClient::SetTransactionFactoryForTesting(
+    std::unique_ptr<DnsTransactionFactory> factory) {
+  NOTREACHED();
+}
+
+void MockDnsClient::StartDohProbesForTesting() {
+  factory_->StartDohProbes(nullptr /* url_request_context */,
+                           false /* network_change */);
+}
+
 void MockDnsClient::CompleteDelayedTransactions() {
   factory_->CompleteDelayedTransactions();
 }
diff --git a/net/dns/dns_test_util.h b/net/dns/dns_test_util.h
index 653c7e14..469ec01 100644
--- a/net/dns/dns_test_util.h
+++ b/net/dns/dns_test_util.h
@@ -13,10 +13,13 @@
 #include <utility>
 #include <vector>
 
+#include "base/memory/weak_ptr.h"
 #include "base/stl_util.h"
+#include "base/time/time.h"
 #include "net/dns/dns_client.h"
 #include "net/dns/dns_config.h"
 #include "net/dns/dns_response.h"
+#include "net/dns/dns_transaction.h"
 #include "net/dns/dns_util.h"
 #include "net/dns/public/dns_protocol.h"
 
@@ -259,7 +262,47 @@
 
 typedef std::vector<MockDnsClientRule> MockDnsClientRuleList;
 
-// MockDnsClient provides MockTransactionFactory.
+// A DnsTransactionFactory which creates MockTransaction.
+class MockDnsTransactionFactory : public DnsTransactionFactory {
+ public:
+  explicit MockDnsTransactionFactory(MockDnsClientRuleList rules);
+  ~MockDnsTransactionFactory() override;
+
+  std::unique_ptr<DnsTransaction> CreateTransaction(
+      const std::string& hostname,
+      uint16_t qtype,
+      DnsTransactionFactory::CallbackType callback,
+      const NetLogWithSource&,
+      bool secure,
+      DnsConfig::SecureDnsMode secure_dns_mode,
+      URLRequestContext* url_request_context) override;
+
+  void AddEDNSOption(const OptRecordRdata::Opt& opt) override;
+
+  base::TimeDelta GetDelayUntilNextProbeForTest(
+      unsigned doh_server_index) override;
+
+  void StartDohProbes(URLRequestContext* url_request_context,
+                      bool network_change) override;
+
+  void CancelDohProbes() override;
+
+  DnsConfig::SecureDnsMode GetSecureDnsModeForTest() override;
+
+  void CompleteDelayedTransactions();
+
+  bool doh_probes_running() { return doh_probes_running_; }
+
+ private:
+  class MockTransaction;
+  using DelayedTransactionList = std::vector<base::WeakPtr<MockTransaction>>;
+
+  MockDnsClientRuleList rules_;
+  DelayedTransactionList delayed_transactions_;
+  bool doh_probes_running_ = false;
+};
+
+// MockDnsClient provides MockDnsTransactionFactory.
 class MockDnsClient : public DnsClient {
  public:
   MockDnsClient(DnsConfig config, MockDnsClientRuleList rules);
@@ -285,6 +328,9 @@
   base::Optional<DnsConfig> GetSystemConfigForTesting() const override;
   DnsConfigOverrides GetConfigOverridesForTesting() const override;
   void SetProbeSuccessForTest(unsigned index, bool success) override;
+  void SetTransactionFactoryForTesting(
+      std::unique_ptr<DnsTransactionFactory> factory) override;
+  void StartDohProbesForTesting() override;
 
   // Completes all DnsTransactions that were delayed by a rule.
   void CompleteDelayedTransactions();
@@ -302,8 +348,6 @@
   }
 
  private:
-  class MockTransactionFactory;
-
   base::Optional<DnsConfig> BuildEffectiveConfig();
 
   bool insecure_enabled_ = false;
@@ -315,7 +359,7 @@
   base::Optional<DnsConfig> config_;
   DnsConfigOverrides overrides_;
   base::Optional<DnsConfig> effective_config_;
-  std::unique_ptr<MockTransactionFactory> factory_;
+  std::unique_ptr<MockDnsTransactionFactory> factory_;
   std::unique_ptr<AddressSorter> address_sorter_;
 };
 
diff --git a/net/dns/dns_transaction.cc b/net/dns/dns_transaction.cc
index be99840..78193003 100644
--- a/net/dns/dns_transaction.cc
+++ b/net/dns/dns_transaction.cc
@@ -395,6 +395,8 @@
     }
 
     request_->SetExtraRequestHeaders(extra_request_headers);
+    // Disable secure DNS for any DoH server hostname lookups to avoid deadlock.
+    request_->SetDisableSecureDns(true);
     // Bypass proxy settings and certificate-related network fetches (currently
     // just OCSP and CRL requests) to avoid deadlock. AIA requests and the
     // Negotiate scheme for HTTP authentication may also cause deadlocks, but
diff --git a/net/dns/dns_transaction_unittest.cc b/net/dns/dns_transaction_unittest.cc
index 35a65bf..3f0bcaf 100644
--- a/net/dns/dns_transaction_unittest.cc
+++ b/net/dns/dns_transaction_unittest.cc
@@ -770,6 +770,7 @@
     EXPECT_TRUE(server_found);
 
     EXPECT_EQ(PRIVACY_MODE_ENABLED, request->privacy_mode());
+    EXPECT_TRUE(request->disable_secure_dns());
 
     std::string accept;
     EXPECT_TRUE(request->extra_request_headers().GetHeader("Accept", &accept));
diff --git a/net/dns/host_resolver_manager.cc b/net/dns/host_resolver_manager.cc
index 25e2bcf..d92aadd 100644
--- a/net/dns/host_resolver_manager.cc
+++ b/net/dns/host_resolver_manager.cc
@@ -3031,26 +3031,6 @@
   } else if (config) {
     secure_dns_mode = config->secure_dns_mode;
   }
-
-  // If the query name matches one of the DoH server names, downgrade to OFF to
-  // avoid infinite recursion.
-  // TODO(crbug.com/985589): Add a URLRequest-level parameter to skip DoH that
-  // can be set when a URLRequest to a DoH server is built, and use this
-  // parameters to set |secure_dns_mode_override| in ResolveHostParameters. This
-  // improvement will prevent us from unnecessarily skipping DoH when a
-  // connection to the DoH server has been established but the query happens to
-  // be for a DoH server hostname.
-  if (config) {
-    for (auto& server : config->dns_over_https_servers) {
-      if (hostname.compare(
-              GURL(GetURLFromTemplateWithoutParameters(server.server_template))
-                  .host()) == 0) {
-        secure_dns_mode = DnsConfig::SecureDnsMode::OFF;
-        break;
-      }
-    }
-  }
-
   return secure_dns_mode;
 }
 
diff --git a/net/dns/host_resolver_manager_unittest.cc b/net/dns/host_resolver_manager_unittest.cc
index 3bcd807..c9ca3d6 100644
--- a/net/dns/host_resolver_manager_unittest.cc
+++ b/net/dns/host_resolver_manager_unittest.cc
@@ -6202,33 +6202,6 @@
   EXPECT_FALSE(ResolveLocalHostname("foo.localhoste", &addresses));
 }
 
-TEST_F(HostResolverManagerDnsTest, ResolveDnsOverHttpsServerName) {
-  MockDnsClientRuleList rules;
-  rules.emplace_back(
-      "dns.example2.com", dns_protocol::kTypeA, false /* secure */,
-      MockDnsClientRule::Result(MockDnsClientRule::OK), false /* delay */);
-  rules.emplace_back(
-      "dns.example2.com", dns_protocol::kTypeAAAA, false /* secure */,
-      MockDnsClientRule::Result(MockDnsClientRule::OK), false /* delay */);
-  CreateResolver();
-  UseMockDnsClient(CreateValidDnsConfig(), std::move(rules));
-
-  DnsConfigOverrides overrides;
-  std::vector<DnsConfig::DnsOverHttpsServerConfig> doh_servers = {
-      DnsConfig::DnsOverHttpsServerConfig("https://dns.example.com/",
-                                          true /*  use_post */),
-      DnsConfig::DnsOverHttpsServerConfig(
-          "https://dns.example2.com/dns-query{?dns}", false /* use_post */)};
-  overrides.dns_over_https_servers = doh_servers;
-  overrides.secure_dns_mode = DnsConfig::SecureDnsMode::SECURE;
-  resolver_->SetDnsConfigOverrides(overrides);
-
-  ResolveHostResponseHelper response(resolver_->CreateRequest(
-      HostPortPair("dns.example2.com", 80), NetLogWithSource(), base::nullopt,
-      request_context_.get(), host_cache_.get()));
-  ASSERT_THAT(response.result_error(), IsOk());
-}
-
 TEST_F(HostResolverManagerDnsTest, AddDnsOverHttpsServerAfterConfig) {
   DestroyResolver();
   test::ScopedMockNetworkChangeNotifier notifier;
diff --git a/net/url_request/http_with_dns_over_https_unittest.cc b/net/url_request/http_with_dns_over_https_unittest.cc
index e0ea11b6..6405d4dd 100644
--- a/net/url_request/http_with_dns_over_https_unittest.cc
+++ b/net/url_request/http_with_dns_over_https_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "net/base/privacy_mode.h"
 #include "net/base/proxy_server.h"
+#include "net/cert/mock_cert_verifier.h"
 #include "net/dns/context_host_resolver.h"
 #include "net/dns/dns_client.h"
 #include "net/dns/dns_config.h"
@@ -38,29 +39,37 @@
 
 class TestHostResolverProc : public HostResolverProc {
  public:
-  TestHostResolverProc() : HostResolverProc(nullptr) {}
+  TestHostResolverProc()
+      : HostResolverProc(nullptr), insecure_queries_served_(0) {}
 
   int Resolve(const std::string& hostname,
               AddressFamily address_family,
               HostResolverFlags host_resolver_flags,
               AddressList* addrlist,
               int* os_error) override {
-    return ERR_NAME_NOT_RESOLVED;
+    insecure_queries_served_++;
+    *addrlist = AddressList::CreateFromIPAddress(IPAddress(127, 0, 0, 1), 443);
+    return OK;
   }
 
+  uint32_t insecure_queries_served() { return insecure_queries_served_; }
+
  private:
   ~TestHostResolverProc() override {}
+  uint32_t insecure_queries_served_;
 };
 
 class HttpWithDnsOverHttpsTest : public TestWithTaskEnvironment {
  public:
   HttpWithDnsOverHttpsTest()
       : resolver_(HostResolver::CreateStandaloneContextResolver(nullptr)),
+        host_resolver_proc_(new TestHostResolverProc()),
+        cert_verifier_(std::make_unique<MockCertVerifier>()),
         request_context_(true),
         doh_server_(EmbeddedTestServer::Type::TYPE_HTTPS),
         test_server_(EmbeddedTestServer::Type::TYPE_HTTPS),
         doh_queries_served_(0),
-        test_requests_served_(0) {
+        test_https_requests_served_(0) {
     doh_server_.RegisterRequestHandler(
         base::BindRepeating(&HttpWithDnsOverHttpsTest::HandleDefaultConnect,
                             base::Unretained(this)));
@@ -69,7 +78,7 @@
                             base::Unretained(this)));
     EXPECT_TRUE(doh_server_.Start());
     EXPECT_TRUE(test_server_.Start());
-    GURL url(doh_server_.GetURL("/dns_query"));
+    GURL url(doh_server_.GetURL("doh-server.com", "/dns_query"));
     std::unique_ptr<DnsClient> dns_client(DnsClient::CreateClient(nullptr));
 
     DnsConfig config;
@@ -79,7 +88,7 @@
 
     resolver_->SetRequestContext(&request_context_);
     resolver_->SetProcParamsForTesting(
-        ProcTaskParams(new TestHostResolverProc(), 1));
+        ProcTaskParams(host_resolver_proc_.get(), 1));
     resolver_->GetManagerForTesting()->SetDnsClientForTesting(
         std::move(dns_client));
 
@@ -90,8 +99,11 @@
     overrides.use_local_ipv6 = true;
     resolver_->GetManagerForTesting()->SetDnsConfigOverrides(
         std::move(overrides));
-
     request_context_.set_host_resolver(resolver_.get());
+
+    cert_verifier_->set_default_result(net::OK);
+    request_context_.set_cert_verifier(cert_verifier_.get());
+
     request_context_.Init();
   }
 
@@ -138,7 +150,7 @@
       http_response->set_content_type("application/dns-message");
       return std::move(http_response);
     } else {
-      test_requests_served_++;
+      test_https_requests_served_++;
       std::unique_ptr<test_server::BasicHttpResponse> http_response(
           new test_server::BasicHttpResponse);
       http_response->set_content(kTestBody);
@@ -149,11 +161,13 @@
 
  protected:
   std::unique_ptr<ContextHostResolver> resolver_;
+  scoped_refptr<net::TestHostResolverProc> host_resolver_proc_;
+  std::unique_ptr<MockCertVerifier> cert_verifier_;
   TestURLRequestContext request_context_;
   EmbeddedTestServer doh_server_;
   EmbeddedTestServer test_server_;
   uint32_t doh_queries_served_;
-  uint32_t test_requests_served_;
+  uint32_t test_https_requests_served_;
 };
 
 class TestHttpDelegate : public HttpStreamRequest::Delegate {
@@ -238,6 +252,13 @@
                 ->IdleSocketCountInGroup(group_id),
             1u);
 
+  // The domain "localhost" is resolved locally, so no DNS lookups should have
+  // occurred.
+  EXPECT_EQ(doh_queries_served_, 0u);
+  EXPECT_EQ(host_resolver_proc_->insecure_queries_served(), 0u);
+  // A stream was established, but no HTTPS request has been made yet.
+  EXPECT_EQ(test_https_requests_served_, 0u);
+
   // Make a request that will trigger a DoH query as well.
   TestDelegate d;
   d.set_allow_certificate_errors(true);
@@ -249,8 +270,17 @@
   EXPECT_TRUE(test_server_.ShutdownAndWaitUntilComplete());
   EXPECT_TRUE(http_server.ShutdownAndWaitUntilComplete());
   EXPECT_TRUE(doh_server_.ShutdownAndWaitUntilComplete());
+
+  // There should be two DoH lookups for "bar.example.com" (both A and AAAA
+  // records are queried).
   EXPECT_EQ(doh_queries_served_, 2u);
-  EXPECT_EQ(test_requests_served_, 1u);
+  // The requests to the DoH server are pooled, so there should only be one
+  // insecure lookup for the DoH server hostname.
+  EXPECT_EQ(host_resolver_proc_->insecure_queries_served(), 1u);
+  // There should be one non-DoH HTTPS request for the connection to
+  // "bar.example.com".
+  EXPECT_EQ(test_https_requests_served_, 1u);
+
   EXPECT_TRUE(d.response_completed());
   EXPECT_EQ(d.request_status(), 0);
   EXPECT_EQ(d.data_received(), kTestBody);
diff --git a/services/network/network_service.cc b/services/network/network_service.cc
index 10d1e215..41c4cb46 100644
--- a/services/network/network_service.cc
+++ b/services/network/network_service.cc
@@ -602,11 +602,11 @@
 }
 #endif
 
-#if defined(OS_MACOSX) && !defined(OS_IOS)
+#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS))
 void NetworkService::SetEncryptionKey(const std::string& encryption_key) {
   OSCrypt::SetRawEncryptionKey(encryption_key);
 }
-#endif  // OS_MACOSX
+#endif
 
 void NetworkService::AddCorbExceptionForPlugin(uint32_t process_id) {
   DCHECK_NE(mojom::kBrowserProcessId, process_id);
diff --git a/services/network/network_service.h b/services/network/network_service.h
index ac17779e..59472121 100644
--- a/services/network/network_service.h
+++ b/services/network/network_service.h
@@ -146,7 +146,7 @@
 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
   void SetCryptConfig(mojom::CryptConfigPtr crypt_config) override;
 #endif
-#if defined(OS_MACOSX) && !defined(OS_IOS)
+#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS))
   void SetEncryptionKey(const std::string& encryption_key) override;
 #endif
   void AddCorbExceptionForPlugin(uint32_t process_id) override;
diff --git a/services/network/public/cpp/weak_wrapper_shared_url_loader_factory.cc b/services/network/public/cpp/weak_wrapper_shared_url_loader_factory.cc
index dbc97fc..9cb2db8 100644
--- a/services/network/public/cpp/weak_wrapper_shared_url_loader_factory.cc
+++ b/services/network/public/cpp/weak_wrapper_shared_url_loader_factory.cc
@@ -4,7 +4,7 @@
 
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 
-#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/network/public/cpp/wrapper_shared_url_loader_factory.h"
 
 namespace network {
@@ -46,11 +46,11 @@
 
 std::unique_ptr<network::SharedURLLoaderFactoryInfo>
 WeakWrapperSharedURLLoaderFactory::Clone() {
-  mojom::URLLoaderFactoryPtrInfo factory_ptr_info;
+  mojo::PendingRemote<mojom::URLLoaderFactory> factory_remote;
   if (factory())
-    factory()->Clone(mojo::MakeRequest(&factory_ptr_info));
+    factory()->Clone(factory_remote.InitWithNewPipeAndPassReceiver());
   return std::make_unique<WrapperSharedURLLoaderFactoryInfo>(
-      std::move(factory_ptr_info));
+      std::move(factory_remote));
 }
 
 WeakWrapperSharedURLLoaderFactory::~WeakWrapperSharedURLLoaderFactory() =
diff --git a/services/network/public/cpp/wrapper_shared_url_loader_factory.cc b/services/network/public/cpp/wrapper_shared_url_loader_factory.cc
index 5161ef5..e4e05b9 100644
--- a/services/network/public/cpp/wrapper_shared_url_loader_factory.cc
+++ b/services/network/public/cpp/wrapper_shared_url_loader_factory.cc
@@ -10,8 +10,8 @@
     default;
 
 WrapperSharedURLLoaderFactoryInfo::WrapperSharedURLLoaderFactoryInfo(
-    network::mojom::URLLoaderFactoryPtrInfo factory_ptr_info)
-    : factory_ptr_info_(std::move(factory_ptr_info)) {}
+    mojo::PendingRemote<network::mojom::URLLoaderFactory> factory_remote)
+    : factory_remote_(std::move(factory_remote)) {}
 
 WrapperSharedURLLoaderFactoryInfo::~WrapperSharedURLLoaderFactoryInfo() =
     default;
@@ -19,7 +19,7 @@
 scoped_refptr<network::SharedURLLoaderFactory>
 WrapperSharedURLLoaderFactoryInfo::CreateFactory() {
   return base::MakeRefCounted<WrapperSharedURLLoaderFactory>(
-      std::move(factory_ptr_info_));
+      std::move(factory_remote_));
 }
 
 }  // namespace network
diff --git a/services/network/public/cpp/wrapper_shared_url_loader_factory.h b/services/network/public/cpp/wrapper_shared_url_loader_factory.h
index 42a73ab..33cd201 100644
--- a/services/network/public/cpp/wrapper_shared_url_loader_factory.h
+++ b/services/network/public/cpp/wrapper_shared_url_loader_factory.h
@@ -7,6 +7,7 @@
 
 #include "base/component_export.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/mojom/url_loader.mojom.h"
@@ -15,13 +16,13 @@
 namespace network {
 
 // A SharedURLLoaderFactoryInfo implementation that wraps a
-// network::mojom::URLLoaderFactoryPtrInfo.
+// mojo::PendingRemote<network::mojom::URLLoaderFactory>.
 class COMPONENT_EXPORT(NETWORK_CPP) WrapperSharedURLLoaderFactoryInfo
     : public network::SharedURLLoaderFactoryInfo {
  public:
   WrapperSharedURLLoaderFactoryInfo();
   explicit WrapperSharedURLLoaderFactoryInfo(
-      network::mojom::URLLoaderFactoryPtrInfo factory_ptr_info);
+      mojo::PendingRemote<network::mojom::URLLoaderFactory> factory_remote);
 
   ~WrapperSharedURLLoaderFactoryInfo() override;
 
@@ -29,7 +30,7 @@
   // SharedURLLoaderFactoryInfo implementation.
   scoped_refptr<network::SharedURLLoaderFactory> CreateFactory() override;
 
-  network::mojom::URLLoaderFactoryPtrInfo factory_ptr_info_;
+  mojo::PendingRemote<network::mojom::URLLoaderFactory> factory_remote_;
 };
 
 // A SharedURLLoaderFactory implementation that wraps a
diff --git a/services/network/public/mojom/network_service.mojom b/services/network/public/mojom/network_service.mojom
index 4a1dc2e..9f02d896 100644
--- a/services/network/public/mojom/network_service.mojom
+++ b/services/network/public/mojom/network_service.mojom
@@ -294,6 +294,10 @@
   [EnableIf=is_mac]
   SetEncryptionKey(string encryption_key);
 
+  // Send the encryption key to the network service to use for AES encryption.
+  [EnableIf=is_win]
+  SetEncryptionKey(string encryption_key);
+
   // Notifies CORB (Cross-Origin Read Blocking) that |process_id| is proxying
   // requests on behalf of a universal-access plugin and therefore CORB should
   // stop blocking requests marked as ResourceType::kPluginResource.
diff --git a/services/video_capture/device_factory_media_to_mojo_adapter.cc b/services/video_capture/device_factory_media_to_mojo_adapter.cc
index 6282cd8..70ad2c5 100644
--- a/services/video_capture/device_factory_media_to_mojo_adapter.cc
+++ b/services/video_capture/device_factory_media_to_mojo_adapter.cc
@@ -142,15 +142,17 @@
 
 void DeviceFactoryMediaToMojoAdapter::AddSharedMemoryVirtualDevice(
     const media::VideoCaptureDeviceInfo& device_info,
-    mojom::ProducerPtr producer,
+    mojo::PendingRemote<mojom::Producer> producer,
     bool send_buffer_handles_to_producer_as_raw_file_descriptors,
-    mojom::SharedMemoryVirtualDeviceRequest virtual_device_request) {
+    mojo::PendingReceiver<mojom::SharedMemoryVirtualDevice>
+        virtual_device_receiver) {
   NOTIMPLEMENTED();
 }
 
 void DeviceFactoryMediaToMojoAdapter::AddTextureVirtualDevice(
     const media::VideoCaptureDeviceInfo& device_info,
-    mojom::TextureVirtualDeviceRequest virtual_device_request) {
+    mojo::PendingReceiver<mojom::TextureVirtualDevice>
+        virtual_device_receiver) {
   NOTIMPLEMENTED();
 }
 
diff --git a/services/video_capture/device_factory_media_to_mojo_adapter.h b/services/video_capture/device_factory_media_to_mojo_adapter.h
index 6c3496c..daa1d9b2 100644
--- a/services/video_capture/device_factory_media_to_mojo_adapter.h
+++ b/services/video_capture/device_factory_media_to_mojo_adapter.h
@@ -10,6 +10,8 @@
 #include "media/capture/video/video_capture_device_client.h"
 #include "media/capture/video/video_capture_system.h"
 #include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/video_capture/device_factory.h"
 #include "services/video_capture/public/mojom/devices_changed_observer.mojom.h"
 
@@ -45,12 +47,14 @@
                     CreateDeviceCallback callback) override;
   void AddSharedMemoryVirtualDevice(
       const media::VideoCaptureDeviceInfo& device_info,
-      mojom::ProducerPtr producer,
+      mojo::PendingRemote<mojom::Producer> producer,
       bool send_buffer_handles_to_producer_as_raw_file_descriptors,
-      mojom::SharedMemoryVirtualDeviceRequest virtual_device) override;
+      mojo::PendingReceiver<mojom::SharedMemoryVirtualDevice>
+          virtual_device_receiver) override;
   void AddTextureVirtualDevice(
       const media::VideoCaptureDeviceInfo& device_info,
-      mojom::TextureVirtualDeviceRequest virtual_device) override;
+      mojo::PendingReceiver<mojom::TextureVirtualDevice>
+          virtual_device_receiver) override;
   void RegisterVirtualDevicesChangedObserver(
       mojom::DevicesChangedObserverPtr observer,
       bool raise_event_if_virtual_devices_already_present) override;
diff --git a/services/video_capture/public/cpp/mock_device_factory.cc b/services/video_capture/public/cpp/mock_device_factory.cc
index 37ef651..e59e1dcf 100644
--- a/services/video_capture/public/cpp/mock_device_factory.cc
+++ b/services/video_capture/public/cpp/mock_device_factory.cc
@@ -23,16 +23,19 @@
 
 void MockDeviceFactory::AddSharedMemoryVirtualDevice(
     const media::VideoCaptureDeviceInfo& device_info,
-    video_capture::mojom::ProducerPtr producer,
+    mojo::PendingRemote<video_capture::mojom::Producer> producer,
     bool send_buffer_handles_to_producer_as_raw_file_descriptors,
-    video_capture::mojom::SharedMemoryVirtualDeviceRequest virtual_device) {
-  DoAddVirtualDevice(device_info, producer.get(), &virtual_device);
+    mojo::PendingReceiver<video_capture::mojom::SharedMemoryVirtualDevice>
+        virtual_device_receiver) {
+  DoAddVirtualDevice(device_info, std::move(producer),
+                     std::move(virtual_device_receiver));
 }
 
 void MockDeviceFactory::AddTextureVirtualDevice(
     const media::VideoCaptureDeviceInfo& device_info,
-    video_capture::mojom::TextureVirtualDeviceRequest virtual_device) {
-  DoAddTextureVirtualDevice(device_info, &virtual_device);
+    mojo::PendingReceiver<video_capture::mojom::TextureVirtualDevice>
+        virtual_device_receiver) {
+  DoAddTextureVirtualDevice(device_info, std::move(virtual_device_receiver));
 }
 
 }  // namespace video_capture
diff --git a/services/video_capture/public/cpp/mock_device_factory.h b/services/video_capture/public/cpp/mock_device_factory.h
index dcbbe01f..e871852 100644
--- a/services/video_capture/public/cpp/mock_device_factory.h
+++ b/services/video_capture/public/cpp/mock_device_factory.h
@@ -5,6 +5,8 @@
 #ifndef SERVICES_VIDEO_CAPTURE_PUBLIC_CPP_MOCK_DEVICE_FACTORY_H_
 #define SERVICES_VIDEO_CAPTURE_PUBLIC_CPP_MOCK_DEVICE_FACTORY_H_
 
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/video_capture/public/mojom/device_factory.mojom.h"
 #include "services/video_capture/public/mojom/devices_changed_observer.mojom.h"
 #include "services/video_capture/public/mojom/producer.mojom.h"
@@ -23,13 +25,14 @@
                     CreateDeviceCallback callback) override;
   void AddSharedMemoryVirtualDevice(
       const media::VideoCaptureDeviceInfo& device_info,
-      video_capture::mojom::ProducerPtr producer,
+      mojo::PendingRemote<video_capture::mojom::Producer> producer,
       bool send_buffer_handles_to_producer_as_raw_file_descriptors,
-      video_capture::mojom::SharedMemoryVirtualDeviceRequest virtual_device)
-      override;
-  void AddTextureVirtualDevice(const media::VideoCaptureDeviceInfo& device_info,
-                               video_capture::mojom::TextureVirtualDeviceRequest
-                                   virtual_device) override;
+      mojo::PendingReceiver<video_capture::mojom::SharedMemoryVirtualDevice>
+          virtual_device_receiver) override;
+  void AddTextureVirtualDevice(
+      const media::VideoCaptureDeviceInfo& device_info,
+      mojo::PendingReceiver<video_capture::mojom::TextureVirtualDevice>
+          virtual_device_receiver) override;
   void RegisterVirtualDevicesChangedObserver(
       video_capture::mojom::DevicesChangedObserverPtr observer,
       bool raise_event_if_virtual_devices_already_present) override {
@@ -41,15 +44,18 @@
                void(const std::string& device_id,
                     video_capture::mojom::DeviceRequest* device_request,
                     CreateDeviceCallback& callback));
-  MOCK_METHOD3(DoAddVirtualDevice,
-               void(const media::VideoCaptureDeviceInfo& device_info,
-                    video_capture::mojom::ProducerProxy* producer,
-                    video_capture::mojom::SharedMemoryVirtualDeviceRequest*
-                        virtual_device_request));
+  MOCK_METHOD3(
+      DoAddVirtualDevice,
+      void(
+          const media::VideoCaptureDeviceInfo& device_info,
+          mojo::PendingRemote<video_capture::mojom::Producer> producer,
+          mojo::PendingReceiver<video_capture::mojom::SharedMemoryVirtualDevice>
+              virtual_device_receiver));
   MOCK_METHOD2(
       DoAddTextureVirtualDevice,
       void(const media::VideoCaptureDeviceInfo& device_info,
-           video_capture::mojom::TextureVirtualDeviceRequest* virtual_device));
+           mojo::PendingReceiver<video_capture::mojom::TextureVirtualDevice>
+               virtual_device_receiver));
 };
 
 }  // namespace video_capture
diff --git a/services/video_capture/public/cpp/mock_video_source_provider.cc b/services/video_capture/public/cpp/mock_video_source_provider.cc
index f1efc57..d03ba00 100644
--- a/services/video_capture/public/cpp/mock_video_source_provider.cc
+++ b/services/video_capture/public/cpp/mock_video_source_provider.cc
@@ -22,16 +22,19 @@
 
 void MockVideoSourceProvider::AddSharedMemoryVirtualDevice(
     const media::VideoCaptureDeviceInfo& device_info,
-    video_capture::mojom::ProducerPtr producer,
+    mojo::PendingRemote<video_capture::mojom::Producer> producer,
     bool send_buffer_handles_to_producer_as_raw_file_descriptors,
-    video_capture::mojom::SharedMemoryVirtualDeviceRequest virtual_device) {
-  DoAddVirtualDevice(device_info, producer.get(), &virtual_device);
+    mojo::PendingReceiver<video_capture::mojom::SharedMemoryVirtualDevice>
+        virtual_device_receiver) {
+  DoAddVirtualDevice(device_info, std::move(producer),
+                     std::move(virtual_device_receiver));
 }
 
 void MockVideoSourceProvider::AddTextureVirtualDevice(
     const media::VideoCaptureDeviceInfo& device_info,
-    video_capture::mojom::TextureVirtualDeviceRequest virtual_device) {
-  DoAddTextureVirtualDevice(device_info, &virtual_device);
+    mojo::PendingReceiver<video_capture::mojom::TextureVirtualDevice>
+        virtual_device_receiver) {
+  DoAddTextureVirtualDevice(device_info, std::move(virtual_device_receiver));
 }
 
 void MockVideoSourceProvider::Close(CloseCallback callback) {
diff --git a/services/video_capture/public/cpp/mock_video_source_provider.h b/services/video_capture/public/cpp/mock_video_source_provider.h
index 9c929af..730c4939 100644
--- a/services/video_capture/public/cpp/mock_video_source_provider.h
+++ b/services/video_capture/public/cpp/mock_video_source_provider.h
@@ -5,6 +5,8 @@
 #ifndef SERVICES_VIDEO_CAPTURE_PUBLIC_CPP_MOCK_VIDEO_SOURCE_PROVIDER_H_
 #define SERVICES_VIDEO_CAPTURE_PUBLIC_CPP_MOCK_VIDEO_SOURCE_PROVIDER_H_
 
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/video_capture/public/mojom/devices_changed_observer.mojom.h"
 #include "services/video_capture/public/mojom/producer.mojom.h"
 #include "services/video_capture/public/mojom/video_source_provider.mojom.h"
@@ -26,14 +28,15 @@
 
   void AddSharedMemoryVirtualDevice(
       const media::VideoCaptureDeviceInfo& device_info,
-      video_capture::mojom::ProducerPtr producer,
+      mojo::PendingRemote<video_capture::mojom::Producer> producer,
       bool send_buffer_handles_to_producer_as_raw_file_descriptors,
-      video_capture::mojom::SharedMemoryVirtualDeviceRequest virtual_device)
-      override;
+      mojo::PendingReceiver<video_capture::mojom::SharedMemoryVirtualDevice>
+          virtual_device_receiver) override;
 
-  void AddTextureVirtualDevice(const media::VideoCaptureDeviceInfo& device_info,
-                               video_capture::mojom::TextureVirtualDeviceRequest
-                                   virtual_device) override;
+  void AddTextureVirtualDevice(
+      const media::VideoCaptureDeviceInfo& device_info,
+      mojo::PendingReceiver<video_capture::mojom::TextureVirtualDevice>
+          virtual_device_receiver) override;
   void RegisterVirtualDevicesChangedObserver(
       video_capture::mojom::DevicesChangedObserverPtr observer,
       bool raise_event_if_virtual_devices_already_present) override {
@@ -46,15 +49,18 @@
   MOCK_METHOD2(DoGetVideoSource,
                void(const std::string& device_id,
                     video_capture::mojom::VideoSourceRequest* request));
-  MOCK_METHOD3(DoAddVirtualDevice,
-               void(const media::VideoCaptureDeviceInfo& device_info,
-                    video_capture::mojom::ProducerProxy* producer,
-                    video_capture::mojom::SharedMemoryVirtualDeviceRequest*
-                        virtual_device_request));
+  MOCK_METHOD3(
+      DoAddVirtualDevice,
+      void(
+          const media::VideoCaptureDeviceInfo& device_info,
+          mojo::PendingRemote<video_capture::mojom::Producer> producer,
+          mojo::PendingReceiver<video_capture::mojom::SharedMemoryVirtualDevice>
+              virtual_device_receiver));
   MOCK_METHOD2(
       DoAddTextureVirtualDevice,
       void(const media::VideoCaptureDeviceInfo& device_info,
-           video_capture::mojom::TextureVirtualDeviceRequest* virtual_device));
+           mojo::PendingReceiver<video_capture::mojom::TextureVirtualDevice>
+               virtual_device_receiver));
   MOCK_METHOD1(DoClose, void(CloseCallback& callback));
 };
 
diff --git a/services/video_capture/public/mojom/device_factory.mojom b/services/video_capture/public/mojom/device_factory.mojom
index e92b240..af5c2e46 100644
--- a/services/video_capture/public/mojom/device_factory.mojom
+++ b/services/video_capture/public/mojom/device_factory.mojom
@@ -54,16 +54,16 @@
   // |virtual_device| or the given |producer| is closed.
   AddSharedMemoryVirtualDevice(
       media.mojom.VideoCaptureDeviceInfo device_info,
-      Producer producer,
+      pending_remote<Producer> producer,
       bool send_buffer_handles_to_producer_as_raw_file_descriptors,
-      SharedMemoryVirtualDevice& virtual_device);
+      pending_receiver<SharedMemoryVirtualDevice> virtual_device_receiver);
 
   // Similar to AddSharedMemoryVirtualDevice() but for virtual devices that
   // are fed with textures (via MailboxHolders) allocated by the caller instead
   // of shared memory buffers provided by the service on demand.
   AddTextureVirtualDevice(
       media.mojom.VideoCaptureDeviceInfo device_info,
-      TextureVirtualDevice& virtual_device);
+      pending_receiver<TextureVirtualDevice> virtual_device_receiver);
 
   // Registered observers will get notified whenever a virtual device is added
   // or removed. Note: Changes to non-virtual devices are currently being
diff --git a/services/video_capture/public/mojom/video_source_provider.mojom b/services/video_capture/public/mojom/video_source_provider.mojom
index 1d2c3671..4f57ae7 100644
--- a/services/video_capture/public/mojom/video_source_provider.mojom
+++ b/services/video_capture/public/mojom/video_source_provider.mojom
@@ -32,16 +32,16 @@
   // |virtual_device| or the given |producer| is closed.
   AddSharedMemoryVirtualDevice(
       media.mojom.VideoCaptureDeviceInfo device_info,
-      Producer producer,
+      pending_remote<Producer> producer,
       bool send_buffer_handles_to_producer_as_raw_file_descriptors,
-      SharedMemoryVirtualDevice& virtual_device);
+      pending_receiver<SharedMemoryVirtualDevice> virtual_device_receiver);
 
   // Similar to AddSharedMemoryVirtualDevice() but for virtual devices that
   // are fed with textures (via MailboxHolders) allocated by the caller instead
   // of shared memory buffers provided by the service on demand.
   AddTextureVirtualDevice(
       media.mojom.VideoCaptureDeviceInfo device_info,
-      TextureVirtualDevice& virtual_device);
+      pending_receiver<TextureVirtualDevice> virtual_device_receiver);
 
   // Registered observers will get notified whenever a virtual device is added
   // or removed. Note: Changes to non-virtual devices are currently being
diff --git a/services/video_capture/shared_memory_virtual_device_mojo_adapter.cc b/services/video_capture/shared_memory_virtual_device_mojo_adapter.cc
index 92c635b..0bcf8c7b 100644
--- a/services/video_capture/shared_memory_virtual_device_mojo_adapter.cc
+++ b/services/video_capture/shared_memory_virtual_device_mojo_adapter.cc
@@ -29,7 +29,7 @@
 namespace video_capture {
 
 SharedMemoryVirtualDeviceMojoAdapter::SharedMemoryVirtualDeviceMojoAdapter(
-    mojom::ProducerPtr producer,
+    mojo::Remote<mojom::Producer> producer,
     bool send_buffer_handles_to_producer_as_raw_file_descriptors)
     : producer_(std::move(producer)),
       send_buffer_handles_to_producer_as_raw_file_descriptors_(
@@ -71,7 +71,8 @@
                                 known_buffer_ids_.end(), buffer_id_to_drop);
     if (entry_iter != known_buffer_ids_.end()) {
       known_buffer_ids_.erase(entry_iter);
-      producer_->OnBufferRetired(buffer_id_to_drop);
+      if (producer_.is_bound())
+        producer_->OnBufferRetired(buffer_id_to_drop);
       if (receiver_.is_bound()) {
         receiver_->OnBufferRetired(buffer_id_to_drop);
       }
@@ -110,9 +111,11 @@
     // because the |producer_| and the |callback| are bound to different
     // message pipes, so the order for calls to |producer_| and |callback|
     // is not guaranteed.
-    producer_->OnNewBuffer(buffer_id, std::move(buffer_handle),
-                           base::BindOnce(&OnNewBufferAcknowleged,
-                                          base::Passed(&callback), buffer_id));
+    if (producer_.is_bound())
+      producer_->OnNewBuffer(
+          buffer_id, std::move(buffer_handle),
+          base::BindOnce(&OnNewBufferAcknowleged, base::Passed(&callback),
+                         buffer_id));
     return;
   }
   std::move(callback).Run(buffer_id);
diff --git a/services/video_capture/shared_memory_virtual_device_mojo_adapter.h b/services/video_capture/shared_memory_virtual_device_mojo_adapter.h
index 8e942603..792a954e 100644
--- a/services/video_capture/shared_memory_virtual_device_mojo_adapter.h
+++ b/services/video_capture/shared_memory_virtual_device_mojo_adapter.h
@@ -8,6 +8,7 @@
 #include "base/sequence_checker.h"
 #include "media/capture/video/video_capture_buffer_pool.h"
 #include "media/capture/video_capture_types.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/video_capture/public/mojom/device.mojom.h"
 #include "services/video_capture/public/mojom/producer.mojom.h"
@@ -21,7 +22,7 @@
       public mojom::Device {
  public:
   SharedMemoryVirtualDeviceMojoAdapter(
-      mojom::ProducerPtr producer,
+      mojo::Remote<mojom::Producer> producer,
       bool send_buffer_handles_to_producer_as_raw_file_descriptors = false);
   ~SharedMemoryVirtualDeviceMojoAdapter() override;
 
@@ -54,7 +55,7 @@
   void OnReceiverConnectionErrorOrClose();
 
   mojo::Remote<mojom::Receiver> receiver_;
-  mojom::ProducerPtr producer_;
+  mojo::Remote<mojom::Producer> producer_;
   const bool send_buffer_handles_to_producer_as_raw_file_descriptors_;
   scoped_refptr<media::VideoCaptureBufferPool> buffer_pool_;
   std::vector<int> known_buffer_ids_;
diff --git a/services/video_capture/test/video_capture_service_test.cc b/services/video_capture/test/video_capture_service_test.cc
index 7d06979d..89c6892 100644
--- a/services/video_capture/test/video_capture_service_test.cc
+++ b/services/video_capture/test/video_capture_service_test.cc
@@ -7,6 +7,7 @@
 #include "base/command_line.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "media/base/media_switches.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/video_capture/public/cpp/mock_producer.h"
 #include "services/video_capture/public/mojom/constants.mojom.h"
 
@@ -46,9 +47,9 @@
     const std::string& device_id) {
   media::VideoCaptureDeviceInfo device_info;
   device_info.descriptor.device_id = device_id;
-  mojom::ProducerPtr producer;
+  mojo::PendingRemote<mojom::Producer> producer;
   auto result = std::make_unique<SharedMemoryVirtualDeviceContext>(
-      mojo::MakeRequest(&producer));
+      producer.InitWithNewPipeAndPassReceiver());
   factory_->AddSharedMemoryVirtualDevice(
       device_info, std::move(producer),
       false /* send_buffer_handles_to_producer_as_raw_file_descriptors */,
diff --git a/services/video_capture/test/virtual_device_unittest.cc b/services/video_capture/test/virtual_device_unittest.cc
index 6cd9222..f3fbb91 100644
--- a/services/video_capture/test/virtual_device_unittest.cc
+++ b/services/video_capture/test/virtual_device_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/test/mock_callback.h"
 #include "base/test/task_environment.h"
 #include "media/capture/video/video_capture_device_info.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "services/video_capture/public/cpp/mock_producer.h"
 #include "services/video_capture/public/cpp/mock_receiver.h"
 #include "services/video_capture/shared_memory_virtual_device_mojo_adapter.h"
@@ -37,11 +38,11 @@
   void SetUp() override {
     device_info_.descriptor.device_id = kTestDeviceId;
     device_info_.descriptor.set_display_name(kTestDeviceName);
-    mojom::ProducerPtr producer_proxy;
+    mojo::Remote<mojom::Producer> producer;
     producer_ =
-        std::make_unique<MockProducer>(mojo::MakeRequest(&producer_proxy));
+        std::make_unique<MockProducer>(producer.BindNewPipeAndPassReceiver());
     device_adapter_ = std::make_unique<SharedMemoryVirtualDeviceMojoAdapter>(
-        std::move(producer_proxy));
+        std::move(producer));
   }
 
   void OnFrameBufferReceived(bool valid_buffer_expected, int32_t buffer_id) {
diff --git a/services/video_capture/video_source_provider_impl.cc b/services/video_capture/video_source_provider_impl.cc
index 9f0fc13..469c8ba 100644
--- a/services/video_capture/video_source_provider_impl.cc
+++ b/services/video_capture/video_source_provider_impl.cc
@@ -53,20 +53,22 @@
 
 void VideoSourceProviderImpl::AddSharedMemoryVirtualDevice(
     const media::VideoCaptureDeviceInfo& device_info,
-    mojom::ProducerPtr producer,
+    mojo::PendingRemote<mojom::Producer> producer,
     bool send_buffer_handles_to_producer_as_raw_file_descriptors,
-    mojom::SharedMemoryVirtualDeviceRequest virtual_device) {
+    mojo::PendingReceiver<mojom::SharedMemoryVirtualDevice>
+        virtual_device_receiver) {
   device_factory_->AddSharedMemoryVirtualDevice(
       device_info, std::move(producer),
       send_buffer_handles_to_producer_as_raw_file_descriptors,
-      std::move(virtual_device));
+      std::move(virtual_device_receiver));
 }
 
 void VideoSourceProviderImpl::AddTextureVirtualDevice(
     const media::VideoCaptureDeviceInfo& device_info,
-    mojom::TextureVirtualDeviceRequest virtual_device) {
+    mojo::PendingReceiver<mojom::TextureVirtualDevice>
+        virtual_device_receiver) {
   device_factory_->AddTextureVirtualDevice(device_info,
-                                           std::move(virtual_device));
+                                           std::move(virtual_device_receiver));
 }
 
 void VideoSourceProviderImpl::RegisterVirtualDevicesChangedObserver(
diff --git a/services/video_capture/video_source_provider_impl.h b/services/video_capture/video_source_provider_impl.h
index f7d0ecd..2da0a4cf 100644
--- a/services/video_capture/video_source_provider_impl.h
+++ b/services/video_capture/video_source_provider_impl.h
@@ -8,6 +8,8 @@
 #include <map>
 
 #include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/video_capture/device_factory.h"
 #include "services/video_capture/public/mojom/video_source_provider.mojom.h"
 
@@ -30,12 +32,14 @@
                       mojom::VideoSourceRequest source_request) override;
   void AddSharedMemoryVirtualDevice(
       const media::VideoCaptureDeviceInfo& device_info,
-      mojom::ProducerPtr producer,
+      mojo::PendingRemote<mojom::Producer> producer,
       bool send_buffer_handles_to_producer_as_raw_file_descriptors,
-      mojom::SharedMemoryVirtualDeviceRequest virtual_device) override;
+      mojo::PendingReceiver<mojom::SharedMemoryVirtualDevice>
+          virtual_device_receiver) override;
   void AddTextureVirtualDevice(
       const media::VideoCaptureDeviceInfo& device_info,
-      mojom::TextureVirtualDeviceRequest virtual_device) override;
+      mojo::PendingReceiver<mojom::TextureVirtualDevice>
+          virtual_device_receiver) override;
   void RegisterVirtualDevicesChangedObserver(
       mojom::DevicesChangedObserverPtr observer,
       bool raise_event_if_virtual_devices_already_present) override;
diff --git a/services/video_capture/virtual_device_enabled_device_factory.cc b/services/video_capture/virtual_device_enabled_device_factory.cc
index a4a6faf4..28b7677 100644
--- a/services/video_capture/virtual_device_enabled_device_factory.cc
+++ b/services/video_capture/virtual_device_enabled_device_factory.cc
@@ -127,9 +127,10 @@
 
 void VirtualDeviceEnabledDeviceFactory::AddSharedMemoryVirtualDevice(
     const media::VideoCaptureDeviceInfo& device_info,
-    mojom::ProducerPtr producer,
+    mojo::PendingRemote<mojom::Producer> producer_pending_remote,
     bool send_buffer_handles_to_producer_as_raw_file_descriptors,
-    mojom::SharedMemoryVirtualDeviceRequest virtual_device_request) {
+    mojo::PendingReceiver<mojom::SharedMemoryVirtualDevice>
+        virtual_device_receiver) {
   auto device_id = device_info.descriptor.device_id;
   auto virtual_device_iter = virtual_devices_by_id_.find(device_id);
   if (virtual_device_iter != virtual_devices_by_id_.end()) {
@@ -138,7 +139,8 @@
     virtual_devices_by_id_.erase(virtual_device_iter);
   }
 
-  producer.set_connection_error_handler(
+  mojo::Remote<mojom::Producer> producer(std::move(producer_pending_remote));
+  producer.set_disconnect_handler(
       base::BindOnce(&VirtualDeviceEnabledDeviceFactory::
                          OnVirtualDeviceProducerConnectionErrorOrClose,
                      base::Unretained(this), device_id));
@@ -147,7 +149,7 @@
       send_buffer_handles_to_producer_as_raw_file_descriptors);
   auto producer_binding =
       std::make_unique<mojo::Binding<mojom::SharedMemoryVirtualDevice>>(
-          device.get(), std::move(virtual_device_request));
+          device.get(), std::move(virtual_device_receiver));
   producer_binding->set_connection_error_handler(
       base::BindOnce(&VirtualDeviceEnabledDeviceFactory::
                          OnVirtualDeviceProducerConnectionErrorOrClose,
@@ -161,7 +163,8 @@
 
 void VirtualDeviceEnabledDeviceFactory::AddTextureVirtualDevice(
     const media::VideoCaptureDeviceInfo& device_info,
-    mojom::TextureVirtualDeviceRequest virtual_device_request) {
+    mojo::PendingReceiver<mojom::TextureVirtualDevice>
+        virtual_device_receiver) {
   auto device_id = device_info.descriptor.device_id;
   auto virtual_device_iter = virtual_devices_by_id_.find(device_id);
   if (virtual_device_iter != virtual_devices_by_id_.end()) {
@@ -173,7 +176,7 @@
   auto device = std::make_unique<TextureVirtualDeviceMojoAdapter>();
   auto producer_binding =
       std::make_unique<mojo::Binding<mojom::TextureVirtualDevice>>(
-          device.get(), std::move(virtual_device_request));
+          device.get(), std::move(virtual_device_receiver));
   producer_binding->set_connection_error_handler(
       base::BindOnce(&VirtualDeviceEnabledDeviceFactory::
                          OnVirtualDeviceProducerConnectionErrorOrClose,
diff --git a/services/video_capture/virtual_device_enabled_device_factory.h b/services/video_capture/virtual_device_enabled_device_factory.h
index b2068665..737c3a4 100644
--- a/services/video_capture/virtual_device_enabled_device_factory.h
+++ b/services/video_capture/virtual_device_enabled_device_factory.h
@@ -9,6 +9,8 @@
 #include <utility>
 
 #include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/video_capture/device_factory.h"
 #include "services/video_capture/public/mojom/device.mojom.h"
 #include "services/video_capture/public/mojom/devices_changed_observer.mojom.h"
@@ -30,12 +32,14 @@
                     CreateDeviceCallback callback) override;
   void AddSharedMemoryVirtualDevice(
       const media::VideoCaptureDeviceInfo& device_info,
-      mojom::ProducerPtr producer,
+      mojo::PendingRemote<mojom::Producer> producer,
       bool send_buffer_handles_to_producer_as_raw_file_descriptors,
-      mojom::SharedMemoryVirtualDeviceRequest virtual_device) override;
+      mojo::PendingReceiver<mojom::SharedMemoryVirtualDevice>
+          virtual_device_receiver) override;
   void AddTextureVirtualDevice(
       const media::VideoCaptureDeviceInfo& device_info,
-      mojom::TextureVirtualDeviceRequest virtual_device) override;
+      mojo::PendingReceiver<mojom::TextureVirtualDevice>
+          virtual_device_receiver) override;
   void RegisterVirtualDevicesChangedObserver(
       mojom::DevicesChangedObserverPtr observer,
       bool raise_event_if_virtual_devices_already_present) override;
diff --git a/testing/buildbot/filters/webui_html_imports_polyfill_browser_tests.filter b/testing/buildbot/filters/webui_html_imports_polyfill_browser_tests.filter
index 0d113ecc8..be65b6e 100644
--- a/testing/buildbot/filters/webui_html_imports_polyfill_browser_tests.filter
+++ b/testing/buildbot/filters/webui_html_imports_polyfill_browser_tests.filter
@@ -37,16 +37,6 @@
 DownloadsToolbarTest.*
 DownloadsUrlTest.*
 
-PDFAnnotationsTest.*
-PDFExtensionClipboardTest.*
-PDFExtensionHitTestTest.*
-PDFExtensionInternalLinkClickTest.*
-PDFExtensionLinkClickTest.*
-PDFExtensionLoadTest.*
-PDFExtensionTest.*
-PDFIsolatedExtensionTest.*
-PDFPluginDisabledTest.*
-
 PrintPreviewAdvancedDialogTest.*
 PrintPreviewAdvancedItemTest.*
 PrintPreviewAppTest.*
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index 90f0d65c..30c7eff4 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -52,6 +52,10 @@
 const base::Feature kCSSBackdropFilter{"CSSBackdropFilter",
                                        base::FEATURE_ENABLED_BY_DEFAULT};
 
+// When enabled, the compositing of trivial 3D transforms is disabled.
+const base::Feature kDoNotCompositeTrivial3D{"DoNotCompositeTrivial3D",
+                                             base::FEATURE_ENABLED_BY_DEFAULT};
+
 // Enable Display Locking JavaScript APIs.
 const base::Feature kDisplayLocking{"DisplayLocking",
                                     base::FEATURE_DISABLED_BY_DEFAULT};
@@ -406,5 +410,10 @@
 const base::Feature kDisableDirectlyCompositedImages{
     "DisableDirectlyCompositedImages", base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enables redirecting subresources in the page to better compressed and
+// optimized versions to provide data savings.
+const base::Feature kSubresourceRedirect{"SubresourceRedirect",
+                                         base::FEATURE_DISABLED_BY_DEFAULT};
+
 }  // namespace features
 }  // namespace blink
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index 21917273..77eadb79 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -29,6 +29,7 @@
 BLINK_COMMON_EXPORT extern const base::Feature kCSSOMViewScrollCoordinates;
 BLINK_COMMON_EXPORT extern const base::Feature kCSSBackdropFilter;
 BLINK_COMMON_EXPORT extern const base::Feature kDisplayLocking;
+BLINK_COMMON_EXPORT extern const base::Feature kDoNotCompositeTrivial3D;
 BLINK_COMMON_EXPORT extern const base::Feature kFastBorderRadius;
 BLINK_COMMON_EXPORT extern const base::Feature kLayoutNG;
 BLINK_COMMON_EXPORT extern const base::Feature kMixedContentAutoupgrade;
@@ -130,6 +131,8 @@
 
 BLINK_COMMON_EXPORT extern const base::Feature kDisableDirectlyCompositedImages;
 
+BLINK_COMMON_EXPORT extern const base::Feature kSubresourceRedirect;
+
 }  // namespace features
 }  // namespace blink
 
diff --git a/third_party/blink/public/mojom/service_worker/service_worker_provider.mojom b/third_party/blink/public/mojom/service_worker/service_worker_provider.mojom
index 6684e05..4bcd418e 100644
--- a/third_party/blink/public/mojom/service_worker/service_worker_provider.mojom
+++ b/third_party/blink/public/mojom/service_worker/service_worker_provider.mojom
@@ -40,7 +40,7 @@
 
   // The loader to use for loading the worker's main script and
   // importScripts().
-  network.mojom.URLLoaderFactory? script_loader_factory_ptr_info;
+  pending_remote<network.mojom.URLLoaderFactory>? script_loader_factory_remote;
 
   // |cache_storage| is an optional optimization so the service worker can use
   // the Cache Storage API immediately without using InterfaceProvider. May be
diff --git a/third_party/blink/public/platform/web_url_request.h b/third_party/blink/public/platform/web_url_request.h
index b7e32b8..f181d40 100644
--- a/third_party/blink/public/platform/web_url_request.h
+++ b/third_party/blink/public/platform/web_url_request.h
@@ -114,7 +114,10 @@
                                        // placeholder by lazyload.
     kDeferAllScriptOn = 1 << 12,  // Request that script execution be deferred
                                   // until parsing completes.
-    kPreviewsStateLast = kDeferAllScriptOn
+    kSubresourceRedirectOn =
+        1 << 13,  // Allow the subresources in the page to be redirected
+                  // to serve better optimized resources.
+    kPreviewsStateLast = kSubresourceRedirectOn
   };
 
   class ExtraData {
diff --git a/third_party/blink/renderer/bindings/templates/attributes.cc.tmpl b/third_party/blink/renderer/bindings/templates/attributes.cc.tmpl
index 17a6000..204de5f 100644
--- a/third_party/blink/renderer/bindings/templates/attributes.cc.tmpl
+++ b/third_party/blink/renderer/bindings/templates/attributes.cc.tmpl
@@ -182,7 +182,6 @@
   if ({{attribute.cpp_value}} && DOMDataStore::SetReturnValue{{world_suffix}}(info.GetReturnValue(), {{attribute.cpp_value_to_script_wrappable}}))
     return;
   v8::Local<v8::Value> v8_value(ToV8({{attribute.cpp_value_to_script_wrappable}}, holder, info.GetIsolate()));
-  // This key is used for uniquely identifying v8::Private.
   static int private_property_key;
   V8PrivateProperty::GetSymbol(
       info.GetIsolate(), &private_property_key, "KeepAlive#{{interface_name}}#{{attribute.name}}")
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.cc
index f7b6ae5..c045d29 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.cc
@@ -166,7 +166,6 @@
   if (cpp_value && DOMDataStore::SetReturnValue(info.GetReturnValue(), cpp_value))
     return;
   v8::Local<v8::Value> v8_value(ToV8(cpp_value, holder, info.GetIsolate()));
-  // This key is used for uniquely identifying v8::Private.
   static int private_property_key;
   V8PrivateProperty::GetSymbol(
       info.GetIsolate(), &private_property_key, "KeepAlive#TestObject#readonlyTestInterfaceEmptyAttribute")
@@ -2484,7 +2483,6 @@
   if (cpp_value && DOMDataStore::SetReturnValue(info.GetReturnValue(), cpp_value))
     return;
   v8::Local<v8::Value> v8_value(ToV8(cpp_value, holder, info.GetIsolate()));
-  // This key is used for uniquely identifying v8::Private.
   static int private_property_key;
   V8PrivateProperty::GetSymbol(
       info.GetIsolate(), &private_property_key, "KeepAlive#TestObject#perWorldBindingsReadonlyTestInterfaceEmptyAttribute")
@@ -2505,7 +2503,6 @@
   if (cpp_value && DOMDataStore::SetReturnValueForMainWorld(info.GetReturnValue(), cpp_value))
     return;
   v8::Local<v8::Value> v8_value(ToV8(cpp_value, holder, info.GetIsolate()));
-  // This key is used for uniquely identifying v8::Private.
   static int private_property_key;
   V8PrivateProperty::GetSymbol(
       info.GetIsolate(), &private_property_key, "KeepAlive#TestObject#perWorldBindingsReadonlyTestInterfaceEmptyAttribute")
@@ -4067,7 +4064,6 @@
   if (cpp_value && DOMDataStore::SetReturnValue(info.GetReturnValue(), cpp_value))
     return;
   v8::Local<v8::Value> v8_value(ToV8(cpp_value, holder, info.GetIsolate()));
-  // This key is used for uniquely identifying v8::Private.
   static int private_property_key;
   V8PrivateProperty::GetSymbol(
       info.GetIsolate(), &private_property_key, "KeepAlive#TestObject#sameObjectAttribute")
@@ -4103,7 +4099,6 @@
   if (cpp_value && DOMDataStore::SetReturnValue(info.GetReturnValue(), cpp_value))
     return;
   v8::Local<v8::Value> v8_value(ToV8(cpp_value, holder, info.GetIsolate()));
-  // This key is used for uniquely identifying v8::Private.
   static int private_property_key;
   V8PrivateProperty::GetSymbol(
       info.GetIsolate(), &private_property_key, "KeepAlive#TestObject#saveSameObjectAttribute")
diff --git a/third_party/blink/renderer/core/html/parser/preload_request.cc b/third_party/blink/renderer/core/html/parser/preload_request.cc
index 3e9be89..c478da1c2 100644
--- a/third_party/blink/renderer/core/html/parser/preload_request.cc
+++ b/third_party/blink/renderer/core/html/parser/preload_request.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/core/html/parser/preload_request.h"
 
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/settings.h"
@@ -16,7 +17,7 @@
 #include "third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
-#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/network/network_state_notifier.h"
 #include "third_party/blink/renderer/platform/weborigin/security_policy.h"
 
 namespace blink {
@@ -49,6 +50,13 @@
 
   resource_request.SetFetchImportanceMode(importance_);
 
+  if (resource_type_ == ResourceType::kImage && url.ProtocolIsInHTTPFamily() &&
+      base::FeatureList::IsEnabled(blink::features::kSubresourceRedirect) &&
+      blink::GetNetworkStateNotifier().SaveDataEnabled()) {
+    resource_request.SetPreviewsState(resource_request.GetPreviewsState() |
+                                      WebURLRequest::kSubresourceRedirectOn);
+  }
+
   ResourceLoaderOptions options;
   options.initiator_info = initiator_info;
   FetchParameters params(resource_request, options);
diff --git a/third_party/blink/renderer/core/inspector/devtools_session.cc b/third_party/blink/renderer/core/inspector/devtools_session.cc
index 8cf28b8..06cd9e3 100644
--- a/third_party/blink/renderer/core/inspector/devtools_session.cc
+++ b/third_party/blink/renderer/core/inspector/devtools_session.cc
@@ -285,7 +285,10 @@
 void DevToolsSession::sendProtocolResponse(
     int call_id,
     std::unique_ptr<protocol::Serializable> message) {
-  SendProtocolResponse(call_id, message->serialize(/*binary=*/true));
+  SendProtocolResponse(
+      call_id,
+      protocol::ProtocolMessage{
+          WTF::String(), WebVector<uint8_t>(message->serializeToBinary())});
 }
 
 void DevToolsSession::fallThrough(int call_id,
@@ -343,15 +346,19 @@
       : v8_notification_(std::move(notification)) {}
 
   protocol::ProtocolMessage Serialize() {
-    protocol::ProtocolMessage result;
     if (blink_notification_) {
-      result = blink_notification_->serialize(/*binary=*/true);
+      auto result = protocol::ProtocolMessage{
+          WTF::String(),
+          WebVector<uint8_t>(blink_notification_->serializeToBinary())};
       blink_notification_.reset();
-    } else if (v8_notification_) {
-      result = ToProtocolMessage(std::move(v8_notification_));
-      v8_notification_.reset();
+      return result;
     }
-    return result;
+    if (v8_notification_) {
+      auto result = ToProtocolMessage(std::move(v8_notification_));
+      v8_notification_.reset();
+      return result;
+    }
+    return protocol::ProtocolMessage();
   }
 
  private:
diff --git a/third_party/blink/renderer/core/layout/flexible_box_algorithm.cc b/third_party/blink/renderer/core/layout/flexible_box_algorithm.cc
index 3cc1dbb..967e37ac 100644
--- a/third_party/blink/renderer/core/layout/flexible_box_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/flexible_box_algorithm.cc
@@ -431,7 +431,9 @@
   const LayoutUnit available_free_space = remaining_free_space;
   LayoutUnit initial_position =
       FlexLayoutAlgorithm::InitialContentPositionOffset(
-          available_free_space, justify_content, line_items.size());
+          algorithm->StyleRef(), available_free_space, justify_content,
+          line_items.size());
+
   main_axis_offset += initial_position;
   sum_justify_adjustments += initial_position;
   LayoutUnit max_descent;  // Used when align-items: baseline.
@@ -593,10 +595,8 @@
     return false;
 
   // webkit-box treats min-size: auto as 0.
-  if (StyleRef().Display() == EDisplay::kWebkitBox ||
-      StyleRef().Display() == EDisplay::kWebkitInlineBox) {
+  if (StyleRef().IsDeprecatedWebkitBox())
     return false;
-  }
 
   return !child.ShouldApplySizeContainment() &&
          MainAxisOverflowForChild(child) == EOverflow::kVisible;
@@ -633,8 +633,9 @@
   for (const FlexLine& line : flex_lines_)
     available_cross_axis_space -= line.cross_axis_extent;
 
-  LayoutUnit line_offset = InitialContentPositionOffset(
-      available_cross_axis_space, align_content, flex_lines_.size());
+  LayoutUnit line_offset =
+      InitialContentPositionOffset(StyleRef(), available_cross_axis_space,
+                                   align_content, flex_lines_.size());
   for (FlexLine& line_context : flex_lines_) {
     line_context.cross_axis_offset += line_offset;
 
@@ -754,9 +755,16 @@
 
 // static
 LayoutUnit FlexLayoutAlgorithm::InitialContentPositionOffset(
+    const ComputedStyle& style,
     LayoutUnit available_free_space,
     const StyleContentAlignmentData& data,
     unsigned number_of_items) {
+  if (available_free_space <= 0 && style.IsDeprecatedWebkitBox() &&
+      style.ResolvedIsColumnFlexDirection()) {
+    // -webkit-box with vertical orientation and no available spaces positions
+    // relative to the start regardless of ContentPosition.
+    return LayoutUnit();
+  }
   if (data.GetPosition() == ContentPosition::kFlexEnd)
     return available_free_space;
   if (data.GetPosition() == ContentPosition::kCenter)
diff --git a/third_party/blink/renderer/core/layout/flexible_box_algorithm.h b/third_party/blink/renderer/core/layout/flexible_box_algorithm.h
index 2099705..0849860 100644
--- a/third_party/blink/renderer/core/layout/flexible_box_algorithm.h
+++ b/third_party/blink/renderer/core/layout/flexible_box_algorithm.h
@@ -387,6 +387,7 @@
                                         const ComputedStyle& child_style);
 
   static LayoutUnit InitialContentPositionOffset(
+      const ComputedStyle& style,
       LayoutUnit available_free_space,
       const StyleContentAlignmentData&,
       unsigned number_of_items);
diff --git a/third_party/blink/renderer/core/layout/layout_flexible_box.cc b/third_party/blink/renderer/core/layout/layout_flexible_box.cc
index ab7ad9d9..46f763a 100644
--- a/third_party/blink/renderer/core/layout/layout_flexible_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_flexible_box.cc
@@ -1261,8 +1261,8 @@
       MainAxisExtentForChild(child);
 
   LayoutUnit offset = FlexLayoutAlgorithm::InitialContentPositionOffset(
-      available_space, FlexLayoutAlgorithm::ResolvedJustifyContent(StyleRef()),
-      1);
+      StyleRef(), available_space,
+      FlexLayoutAlgorithm::ResolvedJustifyContent(StyleRef()), 1);
   if (StyleRef().ResolvedIsRowReverseFlexDirection() ||
       StyleRef().ResolvedIsColumnReverseFlexDirection())
     offset = available_space - offset;
@@ -1516,7 +1516,7 @@
   // we place the children starting from the end of the flexbox.
   LayoutUnit main_axis_offset = LogicalHeight() - FlowAwareContentInsetEnd();
   main_axis_offset -= FlexLayoutAlgorithm::InitialContentPositionOffset(
-      available_free_space, justify_content, children.size());
+      StyleRef(), available_free_space, justify_content, children.size());
 
   for (wtf_size_t i = 0; i < children.size(); ++i) {
     FlexItem& flex_item = children[i];
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.cc
index a30f40c..c5b31629 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.cc
@@ -80,10 +80,10 @@
       const NGPhysicalBoxFragment& box =
           To<NGPhysicalBoxFragment>(child.layout_result->PhysicalFragment());
       if (child.children_count <= 1) {
-        // Compute |has_floating_descendants_| to optimize tree traversal in
-        // paint.
-        if (!has_floating_descendants_ && box.IsFloating())
-          has_floating_descendants_ = true;
+        // Compute |has_floating_descendants_for_paint_| to optimize tree
+        // traversal in paint.
+        if (!has_floating_descendants_for_paint_ && box.IsFloating())
+          has_floating_descendants_for_paint_ = true;
 
         items_.push_back(std::make_unique<NGFragmentItem>(box, 1));
         offsets_.push_back(child.offset);
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.h b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.h
index b0265f1832..5d4a1f7 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.h
@@ -25,8 +25,11 @@
  public:
   NGFragmentItemsBuilder(NGBoxFragmentBuilder* box_builder) {}
 
-  // Returns true if we have any floating descendants.
-  bool HasFloatingDescendants() const { return has_floating_descendants_; }
+  // Returns true if we have any floating descendants which need to be
+  // traversed during the float paint phase.
+  bool HasFloatingDescendantsForPaint() const {
+    return has_floating_descendants_for_paint_;
+  }
 
   const String& TextContent(bool first_line) const {
     return UNLIKELY(first_line && first_line_text_content_)
@@ -78,7 +81,7 @@
   // Keeps children of a line until the offset is determined. See |AddLine|.
   ChildList current_line_;
 
-  bool has_floating_descendants_ = false;
+  bool has_floating_descendants_for_paint_ = false;
 
 #if DCHECK_IS_ON()
   const NGPhysicalLineBoxFragment* current_line_fragment_ = nullptr;
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc
index 3317ee87..4264b98d 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc
@@ -27,7 +27,7 @@
   line_box_type_ = NGPhysicalLineBoxFragment::kNormalLineBox;
 
   break_appeal_ = kBreakAppealPerfect;
-  has_floating_descendants_ = false;
+  has_floating_descendants_for_paint_ = false;
   has_orthogonal_flow_roots_ = false;
   has_descendant_that_depends_on_percentage_block_size_ = false;
   has_block_fragmentation_ = false;
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc
index 722bc55..243d206 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc
@@ -65,7 +65,7 @@
   DCHECK(!metrics_.IsEmpty() || IsEmptyLineBox());
   base_direction_ = static_cast<unsigned>(builder->base_direction_);
   has_hanging_ = builder->hang_inline_size_ != 0;
-  has_propagated_descendants_ = has_floating_descendants_ ||
+  has_propagated_descendants_ = has_floating_descendants_for_paint_ ||
                                 HasOutOfFlowPositionedDescendants() ||
                                 builder->unpositioned_list_marker_;
 }
diff --git a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
index 105b8d8..e8c2cfcc 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
@@ -119,7 +119,6 @@
       items_builder_->AddLine(*line, offset);
       // TODO(kojii): We probably don't need to AddChild this line, but there
       // maybe OOF objects. Investigate how to handle them.
-      return;
     }
   }
   AddChild(fragment, offset, inline_container);
@@ -228,8 +227,10 @@
     }
   }
 
-  if (!has_floating_descendants_ && items_builder_)
-    has_floating_descendants_ = items_builder_->HasFloatingDescendants();
+  if (!has_floating_descendants_for_paint_ && items_builder_) {
+    has_floating_descendants_for_paint_ =
+        items_builder_->HasFloatingDescendantsForPaint();
+  }
 
   scoped_refptr<const NGPhysicalBoxFragment> fragment =
       NGPhysicalBoxFragment::Create(this, block_or_line_writing_mode);
diff --git a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc
index f5bf014..b08fc69 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc
@@ -95,15 +95,14 @@
        child.MayHaveDescendantAboveBlockStart()))
     may_have_descendant_above_block_start_ = true;
 
-  // Compute |has_floating_descendants_| to optimize tree traversal in paint.
-  if (!has_floating_descendants_) {
-    if (child.IsFloating()) {
-      has_floating_descendants_ = true;
-    } else {
-      if (!child.IsBlockFormattingContextRoot() &&
-          child.HasFloatingDescendants())
-        has_floating_descendants_ = true;
-    }
+  // Compute |has_floating_descendants_for_paint_| to optimize tree traversal
+  // in paint.
+  if (!has_floating_descendants_for_paint_) {
+    // TODO(layout-dev): The |NGPhysicalFragment::IsAtomicInline| check should
+    // be checking for any children which paint all phases atomically.
+    if (child.IsFloating() || child.IsLegacyLayoutRoot() ||
+        (child.HasFloatingDescendantsForPaint() && !child.IsAtomicInline()))
+      has_floating_descendants_for_paint_ = true;
   }
 
   // The |has_adjoining_object_descendants_| is used to determine if a fragment
diff --git a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h
index 870f70f..209039c7 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h
@@ -226,7 +226,7 @@
   bool is_pushed_by_floats_ = false;
   bool is_legacy_layout_root_ = false;
 
-  bool has_floating_descendants_ = false;
+  bool has_floating_descendants_for_paint_ = false;
   bool has_orthogonal_flow_roots_ = false;
   bool has_descendant_that_depends_on_percentage_block_size_ = false;
   bool has_block_fragmentation_ = false;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc
index df53440..a0881981 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc
@@ -309,7 +309,7 @@
   DCHECK_EQ(sub_type_, other.sub_type_);
   DCHECK_EQ(style_variant_, other.style_variant_);
 
-  DCHECK_EQ(has_floating_descendants_, other.has_floating_descendants_);
+  // |has_floating_descendants_for_paint_| can change during simplified layout.
   DCHECK_EQ(has_orthogonal_flow_roots_, other.has_orthogonal_flow_roots_);
   DCHECK_EQ(may_have_descendant_above_block_start_,
             other.may_have_descendant_above_block_start_);
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment_test.cc b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment_test.cc
index 71829d70..327d158 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment_test.cc
@@ -40,10 +40,10 @@
 
   const NGPhysicalBoxFragment& has_floats =
       GetPhysicalBoxFragmentByElementId("hasfloats");
-  EXPECT_TRUE(has_floats.HasFloatingDescendants());
+  EXPECT_TRUE(has_floats.HasFloatingDescendantsForPaint());
   const NGPhysicalBoxFragment& no_floats =
       GetPhysicalBoxFragmentByElementId("nofloats");
-  EXPECT_FALSE(no_floats.HasFloatingDescendants());
+  EXPECT_FALSE(no_floats.HasFloatingDescendantsForPaint());
 }
 
 TEST_F(NGPhysicalBoxFragmentTest, FloatingDescendantsBlockChlidren) {
@@ -59,14 +59,14 @@
 
   const NGPhysicalBoxFragment& has_floats =
       GetPhysicalBoxFragmentByElementId("hasfloats");
-  EXPECT_TRUE(has_floats.HasFloatingDescendants());
+  EXPECT_TRUE(has_floats.HasFloatingDescendantsForPaint());
   const NGPhysicalBoxFragment& no_floats =
       GetPhysicalBoxFragmentByElementId("nofloats");
-  EXPECT_FALSE(no_floats.HasFloatingDescendants());
+  EXPECT_FALSE(no_floats.HasFloatingDescendantsForPaint());
 }
 
-// HasFloatingDescendants() should be set for each inline formatting context and
-// should not be propagated across inline formatting context.
+// HasFloatingDescendantsForPaint() should be set for each inline formatting
+// context and should not be propagated across inline formatting context.
 TEST_F(NGPhysicalBoxFragmentTest, FloatingDescendantsInlineBlock) {
   SetBodyInnerHTML(R"HTML(
     <div id="nofloats">
@@ -79,10 +79,35 @@
 
   const NGPhysicalBoxFragment& has_floats =
       GetPhysicalBoxFragmentByElementId("hasfloats");
-  EXPECT_TRUE(has_floats.HasFloatingDescendants());
+  EXPECT_TRUE(has_floats.HasFloatingDescendantsForPaint());
   const NGPhysicalBoxFragment& no_floats =
       GetPhysicalBoxFragmentByElementId("nofloats");
-  EXPECT_FALSE(no_floats.HasFloatingDescendants());
+  EXPECT_FALSE(no_floats.HasFloatingDescendantsForPaint());
+}
+
+// HasFloatingDescendantsForPaint() should be set even if it crosses a block
+// formatting context.
+TEST_F(NGPhysicalBoxFragmentTest, FloatingDescendantsBlockFormattingContext) {
+  SetBodyInnerHTML(R"HTML(
+    <div id="hasfloats">
+      <div style="display: flow-root">
+        <div style="float: left"></div>
+      </div>
+    </div>
+    <div id="hasfloats2" style="position: relative">
+      <div style="position: absolute">
+        <div style="float: left"></div>
+      </div>
+    </div>
+  )HTML");
+
+  const NGPhysicalBoxFragment& has_floats =
+      GetPhysicalBoxFragmentByElementId("hasfloats");
+  EXPECT_TRUE(has_floats.HasFloatingDescendantsForPaint());
+
+  const NGPhysicalBoxFragment& has_floats_2 =
+      GetPhysicalBoxFragmentByElementId("hasfloats2");
+  EXPECT_TRUE(has_floats_2.HasFloatingDescendantsForPaint());
 }
 
 // TODO(layout-dev): Design more straightforward way to ensure old layout
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.cc b/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.cc
index f418fab..29c3b68 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.cc
@@ -44,7 +44,8 @@
               : new Vector<NGPhysicalOutOfFlowPositionedNode>()),
       buffer_(buffer),
       num_children_(builder->children_.size()) {
-  has_floating_descendants_ = builder->has_floating_descendants_;
+  has_floating_descendants_for_paint_ =
+      builder->has_floating_descendants_for_paint_;
   has_adjoining_object_descendants_ =
       builder->has_adjoining_object_descendants_;
   has_orthogonal_flow_roots_ = builder->has_orthogonal_flow_roots_;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h b/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h
index 86f35a5..4cf2b93 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h
@@ -101,8 +101,11 @@
     return PostLayoutChildLinkList(num_children_, buffer_);
   }
 
-  // Returns true if we have any floating descendants.
-  bool HasFloatingDescendants() const { return has_floating_descendants_; }
+  // Returns true if we have any floating descendants which need to be
+  // traversed during the float paint phase.
+  bool HasFloatingDescendantsForPaint() const {
+    return has_floating_descendants_for_paint_;
+  }
 
   // Returns true if we have any adjoining-object descendants (floats, or
   // inline-level OOF-positioned objects).
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc b/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc
index c916a7e0..5a1587a6c 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc
@@ -224,7 +224,7 @@
       sub_type_(sub_type),
       style_variant_((unsigned)builder->style_variant_),
       is_hidden_for_paint_(builder->is_hidden_for_paint_),
-      has_floating_descendants_(false),
+      has_floating_descendants_for_paint_(false),
       is_fieldset_container_(false),
       is_legacy_layout_root_(false) {
   DCHECK(builder->layout_object_);
@@ -241,7 +241,7 @@
       sub_type_(sub_type),
       style_variant_((unsigned)style_variant),
       is_hidden_for_paint_(false),
-      has_floating_descendants_(false),
+      has_floating_descendants_for_paint_(false),
       is_fieldset_container_(false),
       is_legacy_layout_root_(false) {
   DCHECK(layout_object);
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h b/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h
index 271350d4..9748cd1d5 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h
@@ -295,7 +295,7 @@
 
   // The following bitfields are only to be used by NGPhysicalContainerFragment
   // (it's defined here to save memory, since that class has no bitfields).
-  unsigned has_floating_descendants_ : 1;
+  unsigned has_floating_descendants_for_paint_ : 1;
   unsigned has_adjoining_object_descendants_ : 1;
   unsigned has_orthogonal_flow_roots_ : 1;
   unsigned may_have_descendant_above_block_start_ : 1;
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc
index 1b050b0..4d6005f4 100644
--- a/third_party/blink/renderer/core/loader/document_loader.cc
+++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -1728,11 +1728,12 @@
   // Verify that certain types are not on main frame requests.
   DCHECK_NE(WebURLRequest::kClientLoFiAutoReload, previews_state_);
   DCHECK_NE(WebURLRequest::kLazyImageLoadDeferred, previews_state_);
+  DCHECK_NE(WebURLRequest::kSubresourceRedirectOn, previews_state_);
 
-  static_assert(
-      WebURLRequest::kPreviewsStateLast == WebURLRequest::kDeferAllScriptOn,
-      "If a new Preview type is added, verify that the Intervention "
-      "Report should be sent (or not sent) for that type.");
+  static_assert(WebURLRequest::kPreviewsStateLast ==
+                    WebURLRequest::kSubresourceRedirectOn,
+                "If a new Preview type is added, verify that the Intervention "
+                "Report should be sent (or not sent) for that type.");
 
   // If the preview type is not unspecified, off, or no transform, it is a
   // preview that needs to be reported.
diff --git a/third_party/blink/renderer/core/loader/image_loader.cc b/third_party/blink/renderer/core/loader/image_loader.cc
index a285b86..d616ee705 100644
--- a/third_party/blink/renderer/core/loader/image_loader.cc
+++ b/third_party/blink/renderer/core/loader/image_loader.cc
@@ -25,6 +25,7 @@
 #include <memory>
 #include <utility>
 
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/platform/web_client_hints_type.h"
 #include "third_party/blink/public/platform/web_url_request.h"
@@ -64,7 +65,7 @@
 #include "third_party/blink/renderer/platform/loader/fetch/memory_cache.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_loading_log.h"
-#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/network/network_state_notifier.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 #include "third_party/blink/renderer/platform/weborigin/security_policy.h"
 
@@ -550,6 +551,23 @@
       params.SetLazyImageAutoReload();
     }
 
+    // Enable subresource redirect for <img> elements created by parser when
+    // data saver is on. Images created from javascript, fetched via XHR/Fetch
+    // API should not be subresource redirected due to the additional CORB/CORS
+    // handling needed for them.
+    // TODO(rajendrant): Disable subresource redirect when CORS,
+    // content-security-policy does not allow cross-origin accesses.
+    if (auto* html_image = ToHTMLImageElementOrNull(GetElement())) {
+      if (base::FeatureList::IsEnabled(blink::features::kSubresourceRedirect) &&
+          html_image->ElementCreatedByParser() &&
+          GetNetworkStateNotifier().SaveDataEnabled()) {
+        auto& resource_request = params.MutableResourceRequest();
+        resource_request.SetPreviewsState(
+            resource_request.GetPreviewsState() |
+            WebURLRequest::kSubresourceRedirectOn);
+      }
+    }
+
     if (lazy_image_load_state_ == LazyImageLoadState::kDeferred &&
         was_fully_deferred_ && !ShouldLoadImmediately(url)) {
       // TODO(rajendrant): Remove this temporary workaround of creating a 1x1
diff --git a/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc b/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc
index 1049487..11b5b9e 100644
--- a/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc
+++ b/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc
@@ -4,6 +4,8 @@
 
 #include "third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.h"
 
+#include "base/feature_list.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/renderer/core/css/css_property_names.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/node.h"
@@ -15,8 +17,6 @@
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
 
-#include "third_party/blink/public/platform/platform.h"
-
 namespace blink {
 
 CompositingReasons CompositingReasonFinder::DirectReasons(
@@ -143,13 +143,14 @@
   // Note that we ask the layoutObject if it has a transform, because the style
   // may have transforms, but the layoutObject may be an inline that doesn't
   // support them.
-  return layout_object.HasTransformRelatedProperty() &&
-         layout_object.StyleRef().Has3DTransformOperation() &&
-         // Don't composite "trivial" 3D transforms such as translateZ(0) on
-         // low-end devices. These devices are much more sensitive to memory
-         // and per-composited-layer commit overhead.
-         (!Platform::Current()->IsLowEndDevice() ||
-          layout_object.StyleRef().Transform().HasNonTrivial3DComponent());
+  if (!layout_object.HasTransformRelatedProperty())
+    return false;
+
+  // Don't composite "trivial" 3D transforms such as translateZ(0).
+  if (base::FeatureList::IsEnabled(blink::features::kDoNotCompositeTrivial3D))
+    return layout_object.StyleRef().HasNonTrivial3DTransformOperation();
+
+  return layout_object.StyleRef().Has3DTransformOperation();
 }
 
 CompositingReasons CompositingReasonFinder::NonStyleDeterminedDirectReasons(
diff --git a/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder_test.cc b/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder_test.cc
index 40102a1..480f783 100644
--- a/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder_test.cc
+++ b/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder_test.cc
@@ -4,6 +4,8 @@
 
 #include "third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.h"
 
+#include "base/test/scoped_feature_list.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/core/layout/layout_block.h"
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
@@ -11,7 +13,6 @@
 #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
-#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
 
 namespace blink {
 
@@ -27,11 +28,6 @@
   }
 };
 
-class CompositingReasonFinderTestPlatform : public TestingPlatformSupport {
- public:
-  bool IsLowEndDevice() override { return true; }
-};
-
 TEST_F(CompositingReasonFinderTest, CompositingReasonDependencies) {
   EXPECT_FALSE(CompositingReason::kComboAllDirectNonStyleDeterminedReasons &
                (~CompositingReason::kComboAllDirectReasons));
@@ -42,9 +38,7 @@
                CompositingReason::kComboAllStyleDeterminedReasons);
 }
 
-TEST_F(CompositingReasonFinderTest, DontPromoteTrivial3DLowEnd) {
-  ScopedTestingPlatformSupport<CompositingReasonFinderTestPlatform> platform;
-
+TEST_F(CompositingReasonFinderTest, DontPromoteTrivial3D) {
   SetBodyInnerHTML(R"HTML(
     <div id='target'
       style='width: 100px; height: 100px; transform: translateZ(0)'></div>
@@ -56,21 +50,18 @@
   EXPECT_EQ(kNotComposited, paint_layer->GetCompositingState());
 }
 
-TEST_F(CompositingReasonFinderTest, PromoteNonTrivial3DLowEnd) {
-  ScopedTestingPlatformSupport<CompositingReasonFinderTestPlatform> platform;
+class CompositingReasonFinderTestWithCompositeTrivial3D
+    : public CompositingReasonFinderTest {
+ public:
+  CompositingReasonFinderTestWithCompositeTrivial3D() {
+    scoped_feature_list_.InitAndDisableFeature(
+        blink::features::kDoNotCompositeTrivial3D);
+  }
 
-  SetBodyInnerHTML(R"HTML(
-    <div id='target'
-      style='width: 100px; height: 100px; transform: translateZ(1px)'></div>
-  )HTML");
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
 
-  Element* target = GetDocument().getElementById("target");
-  PaintLayer* paint_layer =
-      ToLayoutBoxModelObject(target->GetLayoutObject())->Layer();
-  EXPECT_EQ(kPaintsIntoOwnBacking, paint_layer->GetCompositingState());
-}
-
-TEST_F(CompositingReasonFinderTest, PromoteTrivial3DByDefault) {
+TEST_F(CompositingReasonFinderTestWithCompositeTrivial3D, PromoteTrivial3D) {
   SetBodyInnerHTML(R"HTML(
     <div id='target'
       style='width: 100px; height: 100px; transform: translateZ(0)'></div>
@@ -82,7 +73,7 @@
   EXPECT_EQ(kPaintsIntoOwnBacking, paint_layer->GetCompositingState());
 }
 
-TEST_F(CompositingReasonFinderTest, PromoteNonTrivial3DByDefault) {
+TEST_F(CompositingReasonFinderTest, PromoteNonTrivial3D) {
   SetBodyInnerHTML(R"HTML(
     <div id='target'
       style='width: 100px; height: 100px; transform: translateZ(1px)'></div>
diff --git a/third_party/blink/renderer/core/paint/compositing/compositing_requirements_updater_test.cc b/third_party/blink/renderer/core/paint/compositing/compositing_requirements_updater_test.cc
index d82e4b1c..4cca9095 100644
--- a/third_party/blink/renderer/core/paint/compositing/compositing_requirements_updater_test.cc
+++ b/third_party/blink/renderer/core/paint/compositing/compositing_requirements_updater_test.cc
@@ -125,6 +125,96 @@
   EXPECT_EQ(IntRect(0, 0, 100, 100), tracking->Invalidations()[0].rect);
 }
 
+TEST_F(CompositingRequirementsUpdaterTest, NonTrivial3DTransforms) {
+  ScopedCSSIndependentTransformPropertiesForTest feature_scope(true);
+
+  SetBodyInnerHTML(R"HTML(
+    <!DOCTYPE html>
+    <div id="3d-transform" style="transform: translate3d(1px, 1px, 1px);"></div>
+    <div id="2d-transform" style="transform: translate3d(1px, 1px, 0px);"></div>
+    <div id="3d-transform-translate-z" style="transform:translateZ(1px);"></div>
+    <div id="2d-transform-translate-z" style="transform:translateZ(0px);"></div>
+    <div id="2d-transform-translate-x" style="transform:translateX(1px);"></div>
+    <div id="3d-transform-rot-x" style="transform: rotateX(1deg);"></div>
+    <div id="2d-transform-rot-x" style="transform: rotateX(0deg);"></div>
+    <div id="2d-transform-rot-z" style="transform: rotateZ(1deg);"></div>
+    <div id="3d-rotation-y" style="rotate: 0 1 0 1deg;"></div>
+    <div id="2d-rotation-y" style="rotate: 0 1 0 0deg;"></div>
+    <div id="2d-rotation-z" style="rotate: 0 0 1 1deg;"></div>
+    <div id="3d-translation" style="translate: 0px 0px 1px;"></div>
+    <div id="2d-translation" style="translate: 1px 1px 0px;"></div>
+    <div id="3d-scale" style="scale: 2 2 2;"></div>
+    <div id="2d-scale" style="scale: 2 2 1;"></div>
+  )HTML");
+  UpdateAllLifecyclePhasesForTest();
+
+  const auto* transform_3d = GetLayoutObjectByElementId("3d-transform");
+  EXPECT_TRUE(transform_3d->StyleRef().HasNonTrivial3DTransformOperation());
+  EXPECT_TRUE(CompositingReason::k3DTransform &
+              ToLayoutBox(transform_3d)->Layer()->GetCompositingReasons());
+  const auto* transform_2d = GetLayoutObjectByElementId("2d-transform");
+  EXPECT_FALSE(transform_2d->StyleRef().HasNonTrivial3DTransformOperation());
+  EXPECT_FALSE(ToLayoutBox(transform_2d)->Layer()->GetCompositingReasons());
+
+  const auto* transform_3d_translate_z =
+      GetLayoutObjectByElementId("3d-transform-translate-z");
+  EXPECT_TRUE(
+      transform_3d_translate_z->StyleRef().HasNonTrivial3DTransformOperation());
+  EXPECT_TRUE(
+      CompositingReason::k3DTransform &
+      ToLayoutBox(transform_3d_translate_z)->Layer()->GetCompositingReasons());
+  const auto* transform_2d_translate_z =
+      GetLayoutObjectByElementId("2d-transform-translate-z");
+  EXPECT_FALSE(
+      transform_2d_translate_z->StyleRef().HasNonTrivial3DTransformOperation());
+  EXPECT_FALSE(
+      ToLayoutBox(transform_2d_translate_z)->Layer()->GetCompositingReasons());
+  const auto* transform_2d_translate_x =
+      GetLayoutObjectByElementId("2d-transform-translate-x");
+  EXPECT_FALSE(
+      transform_2d_translate_x->StyleRef().HasNonTrivial3DTransformOperation());
+  EXPECT_FALSE(
+      ToLayoutBox(transform_2d_translate_x)->Layer()->GetCompositingReasons());
+
+  const auto* xform_rot_x_3d = GetLayoutObjectByElementId("3d-transform-rot-x");
+  EXPECT_TRUE(xform_rot_x_3d->StyleRef().HasNonTrivial3DTransformOperation());
+  EXPECT_TRUE(CompositingReason::k3DTransform &
+              ToLayoutBox(xform_rot_x_3d)->Layer()->GetCompositingReasons());
+  const auto* xform_rot_x_2d = GetLayoutObjectByElementId("2d-transform-rot-x");
+  EXPECT_FALSE(xform_rot_x_2d->StyleRef().HasNonTrivial3DTransformOperation());
+  EXPECT_FALSE(ToLayoutBox(xform_rot_x_2d)->Layer()->GetCompositingReasons());
+  const auto* xform_rot_z_2d = GetLayoutObjectByElementId("2d-transform-rot-z");
+  EXPECT_FALSE(xform_rot_z_2d->StyleRef().HasNonTrivial3DTransformOperation());
+  EXPECT_FALSE(ToLayoutBox(xform_rot_z_2d)->Layer()->GetCompositingReasons());
+
+  const auto* rotation_y_3d = GetLayoutObjectByElementId("3d-rotation-y");
+  EXPECT_TRUE(rotation_y_3d->StyleRef().HasNonTrivial3DTransformOperation());
+  EXPECT_TRUE(CompositingReason::k3DTransform &
+              ToLayoutBox(rotation_y_3d)->Layer()->GetCompositingReasons());
+  const auto* rotation_y_2d = GetLayoutObjectByElementId("2d-rotation-y");
+  EXPECT_FALSE(rotation_y_2d->StyleRef().HasNonTrivial3DTransformOperation());
+  EXPECT_FALSE(ToLayoutBox(rotation_y_2d)->Layer()->GetCompositingReasons());
+  const auto* rotation_z_2d = GetLayoutObjectByElementId("2d-rotation-z");
+  EXPECT_FALSE(rotation_z_2d->StyleRef().HasNonTrivial3DTransformOperation());
+  EXPECT_FALSE(ToLayoutBox(rotation_z_2d)->Layer()->GetCompositingReasons());
+
+  const auto* translation_3d = GetLayoutObjectByElementId("3d-translation");
+  EXPECT_TRUE(translation_3d->StyleRef().HasNonTrivial3DTransformOperation());
+  EXPECT_TRUE(CompositingReason::k3DTransform &
+              ToLayoutBox(translation_3d)->Layer()->GetCompositingReasons());
+  const auto* translation_2d = GetLayoutObjectByElementId("2d-translation");
+  EXPECT_FALSE(translation_2d->StyleRef().HasNonTrivial3DTransformOperation());
+  EXPECT_FALSE(ToLayoutBox(translation_2d)->Layer()->GetCompositingReasons());
+
+  const auto* scale_3d = GetLayoutObjectByElementId("3d-scale");
+  EXPECT_TRUE(scale_3d->StyleRef().HasNonTrivial3DTransformOperation());
+  EXPECT_TRUE(CompositingReason::k3DTransform &
+              ToLayoutBox(scale_3d)->Layer()->GetCompositingReasons());
+  const auto* scale_2d = GetLayoutObjectByElementId("2d-scale");
+  EXPECT_FALSE(scale_2d->StyleRef().HasNonTrivial3DTransformOperation());
+  EXPECT_FALSE(ToLayoutBox(scale_2d)->Layer()->GetCompositingReasons());
+}
+
 TEST_F(CompositingRequirementsUpdaterTest,
        DontPromotePerspectiveOnlyTransform) {
   ScopedCSSIndependentTransformPropertiesForTest feature_scope(true);
diff --git a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
index 641d3c6b..1955c8e 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
@@ -378,7 +378,7 @@
       if (paint_phase == PaintPhase::kFloat ||
           paint_phase == PaintPhase::kSelection ||
           paint_phase == PaintPhase::kTextClip) {
-        if (physical_box_fragment.HasFloatingDescendants())
+        if (physical_box_fragment.HasFloatingDescendantsForPaint())
           PaintFloats(paint_info);
       }
     } else {
@@ -387,8 +387,10 @@
 
       if (paint_phase == PaintPhase::kFloat ||
           paint_phase == PaintPhase::kSelection ||
-          paint_phase == PaintPhase::kTextClip)
-        PaintFloats(paint_info);
+          paint_phase == PaintPhase::kTextClip) {
+        if (physical_box_fragment.HasFloatingDescendantsForPaint())
+          PaintFloats(paint_info);
+      }
     }
   }
 
@@ -479,36 +481,9 @@
   }
 }
 
-void NGBoxFragmentPainter::PaintInlineFloatingChildren(
-    NGPaintFragment::ChildList children,
-    const PaintInfo& paint_info) {
-  for (const NGPaintFragment* child : children) {
-    const NGPhysicalFragment& child_fragment = child->PhysicalFragment();
-    if (child_fragment.HasSelfPaintingLayer())
-      continue;
-    if (child_fragment.IsFloating()) {
-      // TODO(kojii): The float is outside of the inline formatting context and
-      // that it maybe another NG inline formatting context, NG block layout, or
-      // legacy. NGBoxFragmentPainter can handle only the first case. In order
-      // to cover more tests for other two cases, we always fallback to legacy,
-      // which will forward back to NGBoxFragmentPainter if the float is for
-      // NGBoxFragmentPainter. We can shortcut this for the first case when
-      // we're more stable.
-      ObjectPainter(*child_fragment.GetLayoutObject())
-          .PaintAllPhasesAtomically(paint_info);
-      continue;
-    }
-    if (const auto* child_container =
-            DynamicTo<NGPhysicalContainerFragment>(&child_fragment)) {
-      if (child_container->HasFloatingDescendants())
-        PaintInlineFloatingChildren(child->Children(), paint_info);
-    }
-  }
-}
-
 void NGBoxFragmentPainter::PaintFloatingItems(const PaintInfo& paint_info) {
   DCHECK(items_);
-  DCHECK(PhysicalFragment().HasFloatingDescendants());
+  DCHECK(PhysicalFragment().HasFloatingDescendantsForPaint());
 
   for (const std::unique_ptr<NGFragmentItem>& item : items_->Items()) {
     const NGPhysicalBoxFragment* child_fragment = item->BoxFragment();
@@ -527,13 +502,21 @@
   }
 }
 
-void NGBoxFragmentPainter::PaintBlockFloatingChildren(
+void NGBoxFragmentPainter::PaintFloatingChildren(
     const NGPhysicalContainerFragment& container,
-    const PaintInfo& paint_info) {
+    const PaintInfo& paint_info,
+    const PaintInfo& float_paint_info) {
   for (const NGLink& child : container.Children()) {
     const NGPhysicalFragment& child_fragment = *child;
     if (child_fragment.HasSelfPaintingLayer())
       continue;
+
+    // Atomic-inlines paint atomically, and shouldn't be traversed.
+    // TODO(layout-dev): This check should include all children which paint
+    // atomically.
+    if (child_fragment.IsAtomicInline())
+      continue;
+
     if (child_fragment.IsFloating()) {
       // TODO(kojii): The float is outside of the inline formatting context and
       // that it maybe another NG inline formatting context, NG block layout, or
@@ -543,31 +526,36 @@
       // NGBoxFragmentPainter. We can shortcut this for the first case when
       // we're more stable.
       ObjectPainter(*child_fragment.GetLayoutObject())
-          .PaintAllPhasesAtomically(paint_info);
+          .PaintAllPhasesAtomically(float_paint_info);
       continue;
     }
+
+    if (child_fragment.Type() == NGPhysicalFragment::kFragmentBox &&
+        FragmentRequiresLegacyFallback(child_fragment)) {
+      child_fragment.GetLayoutObject()->Paint(paint_info);
+      continue;
+    }
+
     if (const auto* child_container =
-            DynamicTo<NGPhysicalContainerFragment>(&child_fragment))
-      PaintBlockFloatingChildren(*child_container, paint_info);
+            DynamicTo<NGPhysicalContainerFragment>(&child_fragment)) {
+      if (child_container->HasFloatingDescendantsForPaint())
+        PaintFloatingChildren(*child_container, paint_info, float_paint_info);
+    }
   }
 }
 
 void NGBoxFragmentPainter::PaintFloats(const PaintInfo& paint_info) {
-  DCHECK(PhysicalFragment().HasFloatingDescendants() ||
+  DCHECK(PhysicalFragment().HasFloatingDescendantsForPaint() ||
          !PhysicalFragment().ChildrenInline());
 
   PaintInfo float_paint_info(paint_info);
   if (paint_info.phase == PaintPhase::kFloat)
     float_paint_info.phase = PaintPhase::kForeground;
-  if (paint_fragment_) {
-    PaintInlineFloatingChildren(paint_fragment_->Children(), float_paint_info);
-    return;
-  }
   if (items_) {
     PaintFloatingItems(float_paint_info);
     return;
   }
-  PaintBlockFloatingChildren(PhysicalFragment(), float_paint_info);
+  PaintFloatingChildren(PhysicalFragment(), paint_info, float_paint_info);
 }
 
 void NGBoxFragmentPainter::PaintMask(const PaintInfo& paint_info,
@@ -1503,7 +1491,7 @@
   // restructuring. Changing the caller logic isn't easy because currently
   // floats are in the bounds of line boxes only in NG.
   const auto& line = To<NGPhysicalLineBoxFragment>(fragment.PhysicalFragment());
-  if (line.HasFloatingDescendants()) {
+  if (line.HasFloatingDescendantsForPaint()) {
     DCHECK_NE(action, kHitTestFloat);
     if (HitTestChildren(result, fragment.Children(), hit_test_location,
                         physical_offset, kHitTestFloat)) {
diff --git a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h
index 9a4851b..e51594c1 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h
+++ b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h
@@ -117,11 +117,10 @@
   MoveTo PaintBoxItem(const NGFragmentItem& item,
                       const PaintInfo& paint_info,
                       const PhysicalOffset& paint_offset);
-  void PaintInlineFloatingChildren(NGPaintFragment::ChildList,
-                                   const PaintInfo&);
   void PaintFloatingItems(const PaintInfo&);
-  void PaintBlockFloatingChildren(const NGPhysicalContainerFragment&,
-                                  const PaintInfo&);
+  void PaintFloatingChildren(const NGPhysicalContainerFragment&,
+                             const PaintInfo& paint_info,
+                             const PaintInfo& float_paint_info);
   void PaintFloats(const PaintInfo&);
   void PaintMask(const PaintInfo&, const PhysicalOffset& paint_offset);
   void PaintAtomicInline(const PaintInfo&);
diff --git a/third_party/blink/renderer/core/style/computed_style.h b/third_party/blink/renderer/core/style/computed_style.h
index 68258db..61d5c0c0 100644
--- a/third_party/blink/renderer/core/style/computed_style.h
+++ b/third_party/blink/renderer/core/style/computed_style.h
@@ -2220,16 +2220,28 @@
     return TableLayout() == ETableLayout::kFixed && !LogicalWidth().IsAuto();
   }
 
-  // Returns true if the computed style contains a 3D transform operation.
-  // This can be individual operations from the transform property, or
-  // individual values from translate/rotate/scale properties. Note that
-  // perspective is omitted, since it does not by itself specify a 3D transform.
+  // Returns true if the computed style contains a 3D transform operation. This
+  // can be individual operations from the transform property, or individual
+  // values from translate/rotate/scale properties. Perspective is omitted since
+  // it does not, by itself, specify a 3D transform.
   bool Has3DTransformOperation() const {
     return Transform().HasNonPerspective3DOperation() ||
            (Translate() && Translate()->Z() != 0) ||
            (Rotate() && (Rotate()->X() != 0 || Rotate()->Y() != 0)) ||
            (Scale() && Scale()->Z() != 1);
   }
+  // Returns true if the computed style contains a 3D transform operation with a
+  // non-trivial component in the Z axis. This can be individual operations from
+  // the transform property, or individual values from translate/rotate/scale
+  // properties. Perspective is omitted since it does not, by itself, specify a
+  // 3D transform.
+  bool HasNonTrivial3DTransformOperation() const {
+    return Transform().HasNonTrivial3DComponent() ||
+           (Translate() && Translate()->Z() != 0) ||
+           (Rotate() && Rotate()->Angle() != 0 &&
+            (Rotate()->X() != 0 || Rotate()->Y() != 0)) ||
+           (Scale() && Scale()->Z() != 1);
+  }
   bool HasTransform() const {
     return HasTransformOperations() || HasOffset() ||
            HasCurrentTransformAnimation() || Translate() || Rotate() || Scale();
diff --git a/third_party/blink/renderer/platform/bindings/v8_private_property.h b/third_party/blink/renderer/platform/bindings/v8_private_property.h
index 6d41991b..20342d95 100644
--- a/third_party/blink/renderer/platform/bindings/v8_private_property.h
+++ b/third_party/blink/renderer/platform/bindings/v8_private_property.h
@@ -149,7 +149,6 @@
   static Symbol V8_PRIVATE_PROPERTY_GETTER_NAME(/* // NOLINT */          \
                                                 InterfaceName, KeyName)( \
       v8::Isolate * isolate) {                                           \
-    /* This key is used for uniquely identifying v8::Private. */         \
     static int private_property_key;                                     \
     return GetSymbol(                                                    \
         isolate, &private_property_key,                                  \
@@ -191,7 +190,6 @@
   // This is a hack for PopStateEvent to get the same private property of
   // History, named State.
   static Symbol GetHistoryStateSymbol(v8::Isolate* isolate) {
-    // This key is used for uniquely identifying v8::Private.
     static int private_property_key;
     return GetSymbol(isolate, &private_property_key, "History#State");
   }
@@ -202,7 +200,8 @@
 
   // Returns a Symbol to access a private property. Symbol instances from same
   // |key|s are guaranteed to access the same property. |desc| is a description
-  // of the property.
+  // of the property. By defining the key as the address of a static variable,
+  // the key is unique for each private property.
   static Symbol GetSymbol(v8::Isolate* isolate, void* key, const char* desc) {
     V8PrivateProperty* private_prop =
         V8PerIsolateData::From(isolate)->PrivateProperty();
diff --git a/third_party/blink/renderer/platform/transforms/perspective_transform_operation.h b/third_party/blink/renderer/platform/transforms/perspective_transform_operation.h
index 6928e93..9ae1a18 100644
--- a/third_party/blink/renderer/platform/transforms/perspective_transform_operation.h
+++ b/third_party/blink/renderer/platform/transforms/perspective_transform_operation.h
@@ -68,6 +68,9 @@
       bool blend_to_identity = false) override;
   scoped_refptr<TransformOperation> Zoom(double factor) final;
 
+  // Perspective does not, by itself, specify a 3D transform.
+  bool HasNonTrivial3DComponent() const override { return false; }
+
   PerspectiveTransformOperation(double p) : p_(p) {}
 
   double p_;
diff --git a/third_party/blink/renderer/platform/transforms/rotate_transform_operation.h b/third_party/blink/renderer/platform/transforms/rotate_transform_operation.h
index 2aae491..2fa68f2 100644
--- a/third_party/blink/renderer/platform/transforms/rotate_transform_operation.h
+++ b/third_party/blink/renderer/platform/transforms/rotate_transform_operation.h
@@ -86,6 +86,10 @@
  protected:
   bool operator==(const TransformOperation&) const override;
 
+  bool HasNonTrivial3DComponent() const override {
+    return Angle() && (X() || Y());
+  }
+
   scoped_refptr<TransformOperation> Blend(
       const TransformOperation* from,
       double progress,
diff --git a/third_party/blink/renderer/platform/transforms/transform_operations.h b/third_party/blink/renderer/platform/transforms/transform_operations.h
index 51d2536..cbe4e449 100644
--- a/third_party/blink/renderer/platform/transforms/transform_operations.h
+++ b/third_party/blink/renderer/platform/transforms/transform_operations.h
@@ -73,7 +73,7 @@
   }
 
   // Return true if any of the operation types are non-perspective 3D operation
-  // types (even if the values describe affine transforms)
+  // types (even if the values describe affine transforms).
   bool HasNonPerspective3DOperation() const {
     for (auto& operation : operations_) {
       if (operation->Is3DOperation() &&
@@ -91,8 +91,7 @@
     return true;
   }
 
-  // Returns true if any operation has a non-trivial component in the Z
-  // axis.
+  // Returns true if any operation has a non-trivial component in the Z axis.
   bool HasNonTrivial3DComponent() const {
     for (auto& operation : operations_) {
       if (operation->HasNonTrivial3DComponent())
diff --git a/third_party/blink/renderer/platform/transforms/transform_operations_test.cc b/third_party/blink/renderer/platform/transforms/transform_operations_test.cc
index 2a9c9c3..ee743c30 100644
--- a/third_party/blink/renderer/platform/transforms/transform_operations_test.cc
+++ b/third_party/blink/renderer/platform/transforms/transform_operations_test.cc
@@ -550,20 +550,24 @@
   TransformOperations ops;
   EXPECT_FALSE(ops.HasPerspective());
   EXPECT_FALSE(ops.HasNonPerspective3DOperation());
+  EXPECT_FALSE(ops.HasNonTrivial3DComponent());
 
   ops.Operations().push_back(TranslateTransformOperation::Create(
       Length::Fixed(1), Length::Fixed(2), TransformOperation::kTranslate));
   EXPECT_FALSE(ops.HasPerspective());
   EXPECT_FALSE(ops.HasNonPerspective3DOperation());
+  EXPECT_FALSE(ops.HasNonTrivial3DComponent());
 
   ops.Operations().push_back(PerspectiveTransformOperation::Create(1234));
   EXPECT_TRUE(ops.HasPerspective());
   EXPECT_FALSE(ops.HasNonPerspective3DOperation());
+  EXPECT_FALSE(ops.HasNonTrivial3DComponent());
 
   ops.Operations().push_back(TranslateTransformOperation::Create(
       Length::Fixed(1), Length::Fixed(2), 3, TransformOperation::kTranslate3D));
   EXPECT_TRUE(ops.HasPerspective());
   EXPECT_TRUE(ops.HasNonPerspective3DOperation());
+  EXPECT_TRUE(ops.HasNonTrivial3DComponent());
 }
 
 }  // namespace blink
diff --git a/third_party/blink/web_tests/compositing/iframes/connect-compositing-iframe.html b/third_party/blink/web_tests/compositing/iframes/connect-compositing-iframe.html
index 93ea82b6..e8c8081 100644
--- a/third_party/blink/web_tests/compositing/iframes/connect-compositing-iframe.html
+++ b/third_party/blink/web_tests/compositing/iframes/connect-compositing-iframe.html
@@ -11,7 +11,7 @@
         height: 150px;
         width: 300px;
         -webkit-box-shadow: 0 0 20px black;
-        transform: translateZ(0);
+        will-change: transform;
     }
     
     .overlay {
diff --git a/third_party/blink/web_tests/external/wpt/compat/webkit-box-ignore-box-pack.html b/third_party/blink/web_tests/external/wpt/compat/webkit-box-ignore-box-pack.html
new file mode 100644
index 0000000..80b52fb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/compat/webkit-box-ignore-box-pack.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+#outer {
+  display: -webkit-box;
+  -webkit-box-orient: vertical;
+  -webkit-box-pack: center;
+  width: 100px;
+  height: 100px;
+}
+#inner {
+  width: 100px;
+  height: 200px;
+}
+</style>
+<div id="outer">
+  <div id="inner"></div>
+</div>
+<script>
+  test(function() {
+    var child = document.getElementById("inner");
+    assert_greater_than(child.offsetTop, 0);
+  }, "Child should be positioned at y-offset greater than 0 if children exceed bounds of parent");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-columns-computed-withcontent.html b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-columns-computed-withcontent.html
new file mode 100644
index 0000000..9b7cd030
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-columns-computed-withcontent.html
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: getComputedStyle().gridTemplateColumns</title>
+<link rel="help" href="https://drafts.csswg.org/css-grid-1/#propdef-grid-template-columns">
+<meta name="assert" content="grid-template-columns computed value is the keyword none or a computed track list.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+<style>
+  #target {
+    display: grid;
+    font-size: 40px;
+    min-width: 200px;
+    width: 300px;
+    max-width: 400px;
+    min-height: 500px;
+    height: 600px;
+    max-height: 700px;
+  }
+  #child {
+    min-width: 20px;
+    width: 30px;
+    max-width: 40px;
+    min-height: 50px;
+    height: 60px;
+    max-height: 70px;
+  }
+</style>
+</head>
+<body>
+<div id="container">
+  <div id="target">
+    <div id="child"></div>
+  </div>
+</div>
+<script>
+test_computed_value("grid-template-columns", 'none', '300px'); // "none" without #child
+
+// track-size <fixed-breadth> = <length-percentage> | <flex> | min-content | max-content | auto
+test_computed_value("grid-template-columns", '20%', '60px'); // 20% * width
+test_computed_value("grid-template-columns", 'calc(-0.5em + 10px)', '0px');
+test_computed_value("grid-template-columns", 'calc(0.5em + 10px)', '30px');
+test_computed_value("grid-template-columns", 'calc(30% + 40px)', '130px'); // 30% * width + 40px
+test_computed_value("grid-template-columns", '5fr', '300px'); // width
+test_computed_value("grid-template-columns", 'min-content', '30px');
+test_computed_value("grid-template-columns", 'max-content', '30px');
+test_computed_value("grid-template-columns", 'auto', '300px'); // width
+
+// track-size minmax( <inflexible-breadth> , <track-breadth> )
+test_computed_value("grid-template-columns", 'minmax(10px, auto)', '300px'); // width
+test_computed_value("grid-template-columns", 'minmax(20%, max-content)', '60px'); // 20% * width
+test_computed_value("grid-template-columns", 'minmax(min-content, calc(-0.5em + 10px))', '30px');
+test_computed_value("grid-template-columns", 'minmax(auto, 0)', '30px');
+
+// track-size fit-content( <length-percentage> )
+test_computed_value("grid-template-columns", 'fit-content(70px)', '30px');
+test_computed_value("grid-template-columns", 'fit-content(20%)', '30px');
+test_computed_value("grid-template-columns", 'fit-content(calc(-0.5em + 10px))', '30px');
+
+// <track-repeat> = repeat( [ <positive-integer> ] , [ <line-names>? <track-size> ]+ <line-names>? )
+test_computed_value("grid-template-columns", 'repeat(1, 10px)', '10px');
+test_computed_value("grid-template-columns", 'repeat(1, [one two] 20%)', '[one two] 60px');
+test_computed_value("grid-template-columns", 'repeat(2, minmax(10px, auto))', '160px 140px');
+
+test_computed_value("grid-template-columns", 'repeat(2, fit-content(20%) [three four] 30px 40px [five six])',
+                    '30px [three four] 30px 40px [five six] 0px [three four] 30px 40px [five six]');
+
+// <track-list> = [ <line-names>? [ <track-size> | <track-repeat> ] ]+ <line-names>?
+test_computed_value("grid-template-columns", 'min-content repeat(5, minmax(10px, auto))',
+                    '30px 54px 54px 54px 54px 54px');
+test_computed_value("grid-template-columns", '[] 150px [] 1fr []', '150px 150px');
+
+// <auto-repeat> = repeat( [ auto-fill | auto-fit ] , [ <line-names>? <fixed-size> ]+ <line-names>? )
+test_computed_value("grid-template-columns", 'repeat(auto-fill, 200px)', '200px');
+test_computed_value("grid-template-columns", 'repeat(auto-fit, [one] 20%)',
+                    '[one] 60px [one] 0px [one] 0px [one] 0px [one] 0px');
+test_computed_value("grid-template-columns", 'repeat(auto-fill, minmax(100px, 5fr) [two])',
+                    '100px [two] 100px [two] 100px [two]');
+test_computed_value("grid-template-columns", 'repeat(auto-fit, [three] minmax(max-content, 6em) [four])',
+                    '[three] 240px [four]');
+
+// <auto-track-list> =
+// [ <line-names>? [ <fixed-size> | <fixed-repeat> ] ]* <line-names>?
+// <auto-repeat>
+// [ <line-names>? [ <fixed-size> | <fixed-repeat> ] ]* <line-names>?
+
+test_computed_value("grid-template-columns", '[one] repeat(2, minmax(50px, auto)) [two] 30px [three] repeat(auto-fill, 10px) 40px [four five] repeat(2, minmax(200px, auto)) [six]',
+                    '[one] 50px 50px [two] 30px [three] 10px 40px [four five] 200px 200px [six]');
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-rows-computed-withcontent-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-rows-computed-withcontent-expected.txt
new file mode 100644
index 0000000..27c7ad78
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-rows-computed-withcontent-expected.txt
@@ -0,0 +1,30 @@
+This is a testharness.js-based test.
+PASS Property grid-template-rows value 'none' computes to '600px'
+PASS Property grid-template-rows value '20%' computes to '120px'
+PASS Property grid-template-rows value 'calc(-0.5em + 10px)' computes to '0px'
+PASS Property grid-template-rows value 'calc(0.5em + 10px)' computes to '30px'
+PASS Property grid-template-rows value 'calc(30% + 40px)' computes to '220px'
+PASS Property grid-template-rows value '5fr' computes to '600px'
+PASS Property grid-template-rows value 'min-content' computes to '60px'
+PASS Property grid-template-rows value 'max-content' computes to '60px'
+PASS Property grid-template-rows value 'auto' computes to '600px'
+PASS Property grid-template-rows value 'minmax(10px, auto)' computes to '600px'
+PASS Property grid-template-rows value 'minmax(20%, max-content)' computes to '120px'
+PASS Property grid-template-rows value 'minmax(min-content, calc(-0.5em + 10px))' computes to '60px'
+PASS Property grid-template-rows value 'minmax(auto, 0)' computes to '60px'
+PASS Property grid-template-rows value 'fit-content(70px)' computes to '60px'
+PASS Property grid-template-rows value 'fit-content(20%)' computes to '60px'
+PASS Property grid-template-rows value 'fit-content(calc(-0.5em + 10px))' computes to '60px'
+PASS Property grid-template-rows value 'repeat(1, 10px)' computes to '10px'
+PASS Property grid-template-rows value 'repeat(1, [one two] 20%)' computes to '[one two] 120px'
+PASS Property grid-template-rows value 'repeat(2, minmax(10px, auto))' computes to '325px 275px'
+PASS Property grid-template-rows value 'repeat(2, fit-content(20%) [three four] 30px 40px [five six])' computes to '60px [three four] 30px 40px [five six] 0px [three four] 30px 40px [five six]'
+PASS Property grid-template-rows value 'min-content repeat(5, minmax(10px, auto))' computes to '60px 108px 108px 108px 108px 108px'
+PASS Property grid-template-rows value '[] 150px [] 1fr []' computes to '150px 450px'
+PASS Property grid-template-rows value 'repeat(auto-fill, 200px)' computes to '200px 200px 200px'
+PASS Property grid-template-rows value 'repeat(auto-fit, [one] 20%)' computes to '[one] 120px [one] 0px [one] 0px [one] 0px [one] 0px'
+PASS Property grid-template-rows value 'repeat(auto-fill, minmax(100px, 5fr) [two])' computes to '100px [two] 100px [two] 100px [two] 100px [two] 100px [two] 100px [two]'
+PASS Property grid-template-rows value 'repeat(auto-fit, [three] minmax(max-content, 6em) [four])' computes to '[three] 240px [four three] 0px [four]'
+FAIL Property grid-template-rows value '[one] repeat(2, minmax(50px, auto)) [two] 30px [three] repeat(auto-fill, 10px) 40px [four five] repeat(2, minmax(200px, auto)) [six]' computes to '[one] 50px 50px [two] 30px [three] 10px 10px 10px 40px [four five] 200px 200px [six]' assert_equals: expected "[one] 50px 50px [two] 30px [three] 10px 10px 10px 40px [four five] 200px 200px [six]" but got "[one] 50px 50px [two] 30px [three] 10px 10px [four five] 10px 40px [six] 200px 200px"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-rows-computed-withcontent.html b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-rows-computed-withcontent.html
new file mode 100644
index 0000000..693cf338
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-rows-computed-withcontent.html
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: getComputedStyle().gridTemplateRows</title>
+<link rel="help" href="https://drafts.csswg.org/css-grid-1/#propdef-grid-template-rows">
+<meta name="assert" content="grid-template-rows computed value is the keyword none or a computed track list.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+<style>
+  #target {
+    display: grid;
+    font-size: 40px;
+    min-width: 200px;
+    width: 300px;
+    max-width: 400px;
+    min-height: 500px;
+    height: 600px;
+    max-height: 700px;
+  }
+  #child {
+    min-width: 20px;
+    width: 30px;
+    max-width: 40px;
+    min-height: 50px;
+    height: 60px;
+    max-height: 70px;
+  }
+</style>
+</head>
+<body>
+<div id="container">
+  <div id="target">
+    <div id="child"></div>
+  </div>
+</div>
+<script>
+test_computed_value("grid-template-rows", 'none', '600px'); // "none" without #child
+
+// track-size <fixed-breadth> = <length-percentage> | <flex> | min-content | max-content | auto
+test_computed_value("grid-template-rows", '20%', '120px'); // 20% * height
+test_computed_value("grid-template-rows", 'calc(-0.5em + 10px)', '0px');
+test_computed_value("grid-template-rows", 'calc(0.5em + 10px)', '30px');
+test_computed_value("grid-template-rows", 'calc(30% + 40px)', '220px'); // 30% * height + 40px
+test_computed_value("grid-template-rows", '5fr', '600px'); // height
+test_computed_value("grid-template-rows", 'min-content', '60px');
+test_computed_value("grid-template-rows", 'max-content', '60px');
+test_computed_value("grid-template-rows", 'auto', '600px'); // height
+
+// track-size minmax( <inflexible-breadth> , <track-breadth> )
+test_computed_value("grid-template-rows", 'minmax(10px, auto)', '600px'); // height
+test_computed_value("grid-template-rows", 'minmax(20%, max-content)', '120px'); // 20% * height
+test_computed_value("grid-template-rows", 'minmax(min-content, calc(-0.5em + 10px))', '60px');
+test_computed_value("grid-template-rows", 'minmax(auto, 0)', '60px');
+
+// track-size fit-content( <length-percentage> )
+test_computed_value("grid-template-rows", 'fit-content(70px)', '60px');
+test_computed_value("grid-template-rows", 'fit-content(20%)', '60px');
+test_computed_value("grid-template-rows", 'fit-content(calc(-0.5em + 10px))', '60px');
+
+// <track-repeat> = repeat( [ <positive-integer> ] , [ <line-names>? <track-size> ]+ <line-names>? )
+test_computed_value("grid-template-rows", 'repeat(1, 10px)', '10px');
+test_computed_value("grid-template-rows", 'repeat(1, [one two] 20%)', '[one two] 120px');
+test_computed_value("grid-template-rows", 'repeat(2, minmax(10px, auto))', '325px 275px');
+
+test_computed_value("grid-template-rows", 'repeat(2, fit-content(20%) [three four] 30px 40px [five six])',
+                    '60px [three four] 30px 40px [five six] 0px [three four] 30px 40px [five six]');
+
+// <track-list> = [ <line-names>? [ <track-size> | <track-repeat> ] ]+ <line-names>?
+test_computed_value("grid-template-rows", 'min-content repeat(5, minmax(10px, auto))',
+                    '60px 108px 108px 108px 108px 108px');
+test_computed_value("grid-template-rows", '[] 150px [] 1fr []', '150px 450px');
+
+// <auto-repeat> = repeat( [ auto-fill | auto-fit ] , [ <line-names>? <fixed-size> ]+ <line-names>? )
+test_computed_value("grid-template-rows", 'repeat(auto-fill, 200px)', '200px 200px 200px');
+test_computed_value("grid-template-rows", 'repeat(auto-fit, [one] 20%)',
+                    '[one] 120px [one] 0px [one] 0px [one] 0px [one] 0px');
+test_computed_value("grid-template-rows", 'repeat(auto-fill, minmax(100px, 5fr) [two])',
+                    '100px [two] 100px [two] 100px [two] 100px [two] 100px [two] 100px [two]');
+test_computed_value("grid-template-rows", 'repeat(auto-fit, [three] minmax(max-content, 6em) [four])',
+                    '[three] 240px [four three] 0px [four]');
+
+// <auto-track-list> =
+// [ <line-names>? [ <fixed-size> | <fixed-repeat> ] ]* <line-names>?
+// <auto-repeat>
+// [ <line-names>? [ <fixed-size> | <fixed-repeat> ] ]* <line-names>?
+
+test_computed_value("grid-template-rows", '[one] repeat(2, minmax(50px, auto)) [two] 30px [three] repeat(auto-fill, 10px) 40px [four five] repeat(2, minmax(200px, auto)) [six]',
+                    '[one] 50px 50px [two] 30px [three] 10px 10px 10px 40px [four five] 200px 200px [six]');
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/fast/borders/border-radius-mask-video-ratio.html b/third_party/blink/web_tests/fast/borders/border-radius-mask-video-ratio.html
index 160117b..2416712 100644
--- a/third_party/blink/web_tests/fast/borders/border-radius-mask-video-ratio.html
+++ b/third_party/blink/web_tests/fast/borders/border-radius-mask-video-ratio.html
@@ -7,7 +7,7 @@
         width: 500px;
         height: 225px;
         border-radius: 200px 0px 100px 0px;
-        transform: translateZ(0);
+        will-change: transform;
       }
     </style>
     <script>
diff --git a/third_party/blink/web_tests/fast/borders/border-radius-mask-video-shadow.html b/third_party/blink/web_tests/fast/borders/border-radius-mask-video-shadow.html
index 2bacdd9c..b2045aa 100644
--- a/third_party/blink/web_tests/fast/borders/border-radius-mask-video-shadow.html
+++ b/third_party/blink/web_tests/fast/borders/border-radius-mask-video-shadow.html
@@ -9,7 +9,7 @@
         border-radius: 200px 0px 100px 0px;
         box-shadow: 0 0 8px 8px red;
         margin: 10px;
-        transform: translateZ(0);
+        will-change: transform;
       }
     </style>
     <script>
diff --git a/third_party/blink/web_tests/fast/borders/border-radius-mask-video.html b/third_party/blink/web_tests/fast/borders/border-radius-mask-video.html
index 8eedc451..4b84b76 100644
--- a/third_party/blink/web_tests/fast/borders/border-radius-mask-video.html
+++ b/third_party/blink/web_tests/fast/borders/border-radius-mask-video.html
@@ -7,7 +7,7 @@
         width: 400px;
         height: 225px;
         border-radius: 200px 0px 100px 0px;
-        transform: translateZ(0);
+        will-change: transform;
       }
     </style>
     <script>
diff --git a/third_party/blink/web_tests/fast/events/touch/compositor-touch-hit-rects-scroll.html b/third_party/blink/web_tests/fast/events/touch/compositor-touch-hit-rects-scroll.html
index d1b4230..bb617a3 100644
--- a/third_party/blink/web_tests/fast/events/touch/compositor-touch-hit-rects-scroll.html
+++ b/third_party/blink/web_tests/fast/events/touch/compositor-touch-hit-rects-scroll.html
@@ -15,6 +15,7 @@
 }
 #transformed {
   transform: translate3d(10px, 10px, 0);
+  will-change: transform;
 }
 .relative {
   position: relative;
diff --git a/third_party/inspector_protocol/README.chromium b/third_party/inspector_protocol/README.chromium
index a73a85c..aa50b47 100644
--- a/third_party/inspector_protocol/README.chromium
+++ b/third_party/inspector_protocol/README.chromium
@@ -2,7 +2,7 @@
 Short Name: inspector_protocol
 URL: https://chromium.googlesource.com/deps/inspector_protocol/
 Version: 0
-Revision: a14dad30f0e5b0fc05911856d5a20b1ffe89fd9b
+Revision: bbc72612409377752c8fd2e7a63a1a5947b7dc4b
 License: BSD
 License File: LICENSE
 Security Critical: yes
diff --git a/third_party/inspector_protocol/lib/DispatcherBase_cpp.template b/third_party/inspector_protocol/lib/DispatcherBase_cpp.template
index 84c3efd..5da47d2 100644
--- a/third_party/inspector_protocol/lib/DispatcherBase_cpp.template
+++ b/third_party/inspector_protocol/lib/DispatcherBase_cpp.template
@@ -142,11 +142,6 @@
         return std::unique_ptr<ProtocolError>(new ProtocolError(code, errorMessage));
     }
 
-    String serializeToJSON() override
-    {
-        return serialize()->serializeToJSON();
-    }
-
     std::vector<uint8_t> serializeToBinary() override
     {
         return serialize()->serializeToBinary();
@@ -319,20 +314,6 @@
     return ProtocolError::createErrorResponse(callId, code, message, nullptr);
 }
 
-String InternalResponse::serializeToJSON()
-{
-    std::unique_ptr<DictionaryValue> result = DictionaryValue::create();
-    std::unique_ptr<Serializable> params(m_params ? std::move(m_params) : DictionaryValue::create());
-    if (m_notification.length()) {
-        result->setString("method", m_notification);
-        result->setValue("params", SerializedValue::fromJSON(params->serializeToJSON()));
-    } else {
-        result->setInteger("id", m_callId);
-        result->setValue("result", SerializedValue::fromJSON(params->serializeToJSON()));
-    }
-    return result->serializeToJSON();
-}
-
 std::vector<uint8_t> InternalResponse::serializeToBinary()
 {
     std::unique_ptr<DictionaryValue> result = DictionaryValue::create();
diff --git a/third_party/inspector_protocol/lib/DispatcherBase_h.template b/third_party/inspector_protocol/lib/DispatcherBase_h.template
index 3862fb9a..2281b1aa 100644
--- a/third_party/inspector_protocol/lib/DispatcherBase_h.template
+++ b/third_party/inspector_protocol/lib/DispatcherBase_h.template
@@ -132,7 +132,6 @@
     static std::unique_ptr<Serializable> createNotification(const String& notification, std::unique_ptr<Serializable> params = nullptr);
     static std::unique_ptr<Serializable> createErrorResponse(int callId, DispatchResponse::ErrorCode code, const String& message);
 
-    String serializeToJSON() override;
     std::vector<uint8_t> serializeToBinary() override;
 
     ~InternalResponse() override {}
@@ -147,11 +146,6 @@
 
 class InternalRawNotification : public Serializable {
 public:
-    static std::unique_ptr<InternalRawNotification> fromJSON(String notification)
-    {
-        return std::unique_ptr<InternalRawNotification>(new InternalRawNotification(std::move(notification)));
-    }
-
     static std::unique_ptr<InternalRawNotification> fromBinary(std::vector<uint8_t> notification)
     {
         return std::unique_ptr<InternalRawNotification>(new InternalRawNotification(std::move(notification)));
@@ -159,23 +153,15 @@
 
     ~InternalRawNotification() override {}
 
-    String serializeToJSON() override
-    {
-        return std::move(m_jsonNotification);
-    }
-
     std::vector<uint8_t> serializeToBinary() override
     {
         return std::move(m_binaryNotification);
     }
 
 private:
-  explicit InternalRawNotification(String notification)
-    : m_jsonNotification(std::move(notification)) { }
   explicit InternalRawNotification(std::vector<uint8_t> notification)
     : m_binaryNotification(std::move(notification)) { }
 
-  String m_jsonNotification;
   std::vector<uint8_t> m_binaryNotification;
 };
 
diff --git a/third_party/inspector_protocol/lib/FrontendChannel_h.template b/third_party/inspector_protocol/lib/FrontendChannel_h.template
index df104de..08291fe2 100644
--- a/third_party/inspector_protocol/lib/FrontendChannel_h.template
+++ b/third_party/inspector_protocol/lib/FrontendChannel_h.template
@@ -13,13 +13,6 @@
 
 class {{config.lib.export_macro}} Serializable {
 public:
-    ProtocolMessage serialize(bool binary) {
-      if (binary)
-        return StringUtil::binaryToMessage(serializeToBinary());
-      else
-        return StringUtil::jsonToMessage(serializeToJSON());
-    }
-    virtual String serializeToJSON() = 0;
     virtual std::vector<uint8_t> serializeToBinary() = 0;
     virtual ~Serializable() = default;
 };
diff --git a/third_party/inspector_protocol/lib/Values_cpp.template b/third_party/inspector_protocol/lib/Values_cpp.template
index 038992f..c73e83d9 100644
--- a/third_party/inspector_protocol/lib/Values_cpp.template
+++ b/third_party/inspector_protocol/lib/Values_cpp.template
@@ -299,10 +299,6 @@
     return StringUtil::builderToString(result);
 }
 
-String Value::serializeToJSON() {
-    return toJSONString();
-}
-
 std::vector<uint8_t> Value::serializeToBinary() {
     std::vector<uint8_t> bytes;
     writeBinary(&bytes);
diff --git a/third_party/inspector_protocol/lib/Values_h.template b/third_party/inspector_protocol/lib/Values_h.template
index 4d6fde0..c206f931 100644
--- a/third_party/inspector_protocol/lib/Values_h.template
+++ b/third_party/inspector_protocol/lib/Values_h.template
@@ -57,7 +57,6 @@
     virtual void writeBinary(std::vector<uint8_t>* bytes) const;
     virtual std::unique_ptr<Value> clone() const;
     String toJSONString() const;
-    String serializeToJSON() override;
     std::vector<uint8_t> serializeToBinary() override;
 
 protected:
diff --git a/third_party/inspector_protocol/templates/TypeBuilder_cpp.template b/third_party/inspector_protocol/templates/TypeBuilder_cpp.template
index b1c3ab7..5d794460 100644
--- a/third_party/inspector_protocol/templates/TypeBuilder_cpp.template
+++ b/third_party/inspector_protocol/templates/TypeBuilder_cpp.template
@@ -101,7 +101,7 @@
 
 {{config.exported.string_out}} {{type.id}}::toJSONString() const
 {
-    String json = toValue()->serializeToJSON();
+    String json = toValue()->toJSONString();
     return {{config.exported.to_string_out % "json"}};
 }
 
@@ -203,11 +203,6 @@
     m_frontendChannel->flushProtocolNotifications();
 }
 
-void Frontend::sendRawJSONNotification(String notification)
-{
-    m_frontendChannel->sendProtocolNotification(InternalRawNotification::fromJSON(std::move(notification)));
-}
-
 void Frontend::sendRawCBORNotification(std::vector<uint8_t> notification)
 {
     m_frontendChannel->sendProtocolNotification(InternalRawNotification::fromBinary(std::move(notification)));
diff --git a/third_party/inspector_protocol/templates/TypeBuilder_h.template b/third_party/inspector_protocol/templates/TypeBuilder_h.template
index 9d86d7a4..0a6924c 100644
--- a/third_party/inspector_protocol/templates/TypeBuilder_h.template
+++ b/third_party/inspector_protocol/templates/TypeBuilder_h.template
@@ -100,7 +100,6 @@
     {% endfor %}
 
     std::unique_ptr<protocol::DictionaryValue> toValue() const;
-    String serializeToJSON() override { return toValue()->serializeToJSON(); }
     std::vector<uint8_t> serializeToBinary() override { return toValue()->serializeToBinary(); }
     String toJSON() const { return toValue()->toJSONString(); }
     std::unique_ptr<{{type.id}}> clone() const;
@@ -269,7 +268,6 @@
   {% endfor %}
 
     void flush();
-    void sendRawJSONNotification(String);
     void sendRawCBORNotification(std::vector<uint8_t>);
 private:
     FrontendChannel* m_frontendChannel;
diff --git a/third_party/libaddressinput/BUILD.gn b/third_party/libaddressinput/BUILD.gn
index d3c8e84..8631f982 100644
--- a/third_party/libaddressinput/BUILD.gn
+++ b/third_party/libaddressinput/BUILD.gn
@@ -308,3 +308,13 @@
   ]
   dict = "//third_party/libaddressinput/fuzz/data/fmt.dict"
 }
+
+fuzzer_test("libaddressinput_address_formatter_fuzzer") {
+  sources = [
+    "fuzz/address_formatter_fuzzer.cc",
+  ]
+  deps = [
+    ":libaddressinput",
+    ":util",
+  ]
+}
diff --git a/third_party/libaddressinput/fuzz/address_formatter_fuzzer.cc b/third_party/libaddressinput/fuzz/address_formatter_fuzzer.cc
new file mode 100644
index 0000000..a9e1c79
--- /dev/null
+++ b/third_party/libaddressinput/fuzz/address_formatter_fuzzer.cc
@@ -0,0 +1,48 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+#include <string>
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_data.h"
+#include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_formatter.h"
+
+namespace {
+
+constexpr size_t kMaxFieldLength = 128;
+
+}  // namespace
+
+// Entry point for LibFuzzer.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  FuzzedDataProvider provider(data, size);
+
+  i18n::addressinput::AddressData address;
+  address.region_code = provider.ConsumeRandomLengthString(kMaxFieldLength);
+  address.administrative_area =
+      provider.ConsumeRandomLengthString(kMaxFieldLength);
+  address.locality = provider.ConsumeRandomLengthString(kMaxFieldLength);
+  address.dependent_locality =
+      provider.ConsumeRandomLengthString(kMaxFieldLength);
+  address.postal_code = provider.ConsumeRandomLengthString(kMaxFieldLength);
+  address.sorting_code = provider.ConsumeRandomLengthString(kMaxFieldLength);
+  address.language_code = provider.ConsumeRandomLengthString(kMaxFieldLength);
+  address.organization = provider.ConsumeRandomLengthString(kMaxFieldLength);
+  address.recipient = provider.ConsumeRandomLengthString(kMaxFieldLength);
+
+  while (provider.remaining_bytes() > 0) {
+    address.address_line.push_back(
+        provider.ConsumeRandomLengthString(kMaxFieldLength));
+  }
+
+  std::vector<std::string> output_multiline;
+  i18n::addressinput::GetFormattedNationalAddress(address, &output_multiline);
+
+  std::string output;
+  i18n::addressinput::GetFormattedNationalAddressLine(address, &output);
+  i18n::addressinput::GetStreetAddressLinesAsSingleLine(address, &output);
+  return 0;
+}
diff --git a/third_party/node/node_modules.tar.gz.sha1 b/third_party/node/node_modules.tar.gz.sha1
index 9f90b148..3a89d20 100644
--- a/third_party/node/node_modules.tar.gz.sha1
+++ b/third_party/node/node_modules.tar.gz.sha1
@@ -1 +1 @@
-5e3e142e86e26030ca3ebfe37d5c408aabc7b36d
+a9603a3dc03fa41af9cc5c8ac28d23d1f98dfe3c
diff --git a/third_party/node/package-lock.json b/third_party/node/package-lock.json
index f118bd6..5795bd76 100644
--- a/third_party/node/package-lock.json
+++ b/third_party/node/package-lock.json
@@ -129,6 +129,35 @@
         "to-fast-properties": "^2.0.0"
       }
     },
+    "@types/babel-generator": {
+      "version": "6.25.3",
+      "resolved": "https://registry.npmjs.org/@types/babel-generator/-/babel-generator-6.25.3.tgz",
+      "integrity": "sha512-pGgnuxVddKcYIc+VJkRDop7gxLhqclNKBdlsm/5Vp8d+37pQkkDK7fef8d9YYImRzw9xcojEPc18pUYnbxmjqA==",
+      "requires": {
+        "@types/babel-types": "*"
+      }
+    },
+    "@types/babel-traverse": {
+      "version": "6.25.5",
+      "resolved": "https://registry.npmjs.org/@types/babel-traverse/-/babel-traverse-6.25.5.tgz",
+      "integrity": "sha512-WrMbwmu+MWf8FiUMbmVOGkc7bHPzndUafn1CivMaBHthBBoo0VNIcYk1KV71UovYguhsNOwf3UF5oRmkkGOU3w==",
+      "requires": {
+        "@types/babel-types": "*"
+      }
+    },
+    "@types/babel-types": {
+      "version": "7.0.7",
+      "resolved": "https://registry.npmjs.org/@types/babel-types/-/babel-types-7.0.7.tgz",
+      "integrity": "sha512-dBtBbrc+qTHy1WdfHYjBwRln4+LWqASWakLHsWHR2NWHIFkv4W3O070IGoGLEBrJBvct3r0L1BUPuvURi7kYUQ=="
+    },
+    "@types/babylon": {
+      "version": "6.16.5",
+      "resolved": "https://registry.npmjs.org/@types/babylon/-/babylon-6.16.5.tgz",
+      "integrity": "sha512-xH2e58elpj1X4ynnKp9qSnWlsRTIs6n3tgLGNfwAGHwePw0mulHQllV34n0T25uYSu1k0hRKkWXF890B1yS47w==",
+      "requires": {
+        "@types/babel-types": "*"
+      }
+    },
     "@types/chai": {
       "version": "4.1.7",
       "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.1.7.tgz",
@@ -162,24 +191,21 @@
       "resolved": "https://registry.npmjs.org/@types/doctrine/-/doctrine-0.0.1.tgz",
       "integrity": "sha1-uZny2fe0PKvgoaLzm8IDvH3K2p0="
     },
-    "@types/escodegen": {
-      "version": "0.0.2",
-      "resolved": "https://registry.npmjs.org/@types/escodegen/-/escodegen-0.0.2.tgz",
-      "integrity": "sha1-fOpBqyQukQ6xD2WuGK66RZ1ms18="
-    },
-    "@types/estraverse": {
-      "version": "0.0.6",
-      "resolved": "https://registry.npmjs.org/@types/estraverse/-/estraverse-0.0.6.tgz",
-      "integrity": "sha1-Zp9833KreX5hJfjQD+0z1M8wwiE=",
-      "requires": {
-        "@types/estree": "*"
-      }
-    },
     "@types/estree": {
       "version": "0.0.37",
       "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.37.tgz",
       "integrity": "sha512-1IT6vNpmU9w18P3v6mN9idv18z5KPVTi4t7+rU9VLnkxo0LCam8IXy/eSVzOaQ1Wpabra2cN3A8K/SliPK/Suw=="
     },
+    "@types/is-windows": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/@types/is-windows/-/is-windows-0.2.0.tgz",
+      "integrity": "sha1-byTuSHMdMRaOpRBhDW3RXl/Jxv8="
+    },
+    "@types/minimatch": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
+      "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA=="
+    },
     "@types/node": {
       "version": "4.9.3",
       "resolved": "https://registry.npmjs.org/@types/node/-/node-4.9.3.tgz",
@@ -200,11 +226,32 @@
         }
       }
     },
+    "@types/path-is-inside": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/@types/path-is-inside/-/path-is-inside-1.0.0.tgz",
+      "integrity": "sha512-hfnXRGugz+McgX2jxyy5qz9sB21LRzlGn24zlwN2KEgoPtEvjzNRrLtUkOOebPDPZl3Rq7ywKxYvylVcEZDnEw=="
+    },
     "@types/q": {
       "version": "1.5.2",
       "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.2.tgz",
       "integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw=="
     },
+    "@types/resolve": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-0.0.6.tgz",
+      "integrity": "sha512-g+Rg8uMWY76oYTyaL+m7ZcblqF/oj7pE6uEUyACluJx4zcop1Lk14qQiocdEkEVMDFm6DmKpxJhsER+ZuTwG3g==",
+      "requires": {
+        "@types/node": "*"
+      }
+    },
+    "@types/whatwg-url": {
+      "version": "6.4.0",
+      "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-6.4.0.tgz",
+      "integrity": "sha512-tonhlcbQ2eho09am6RHnHOgvtDfDYINd5rgxD+2YSkKENooVCFsWizJz139MQW/PV8FfClyKrNe9ZbdHrSCxGg==",
+      "requires": {
+        "@types/node": "*"
+      }
+    },
     "acorn": {
       "version": "5.7.3",
       "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz",
@@ -280,6 +327,23 @@
       "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
       "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg=="
     },
+    "babel-code-frame": {
+      "version": "6.26.0",
+      "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
+      "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
+      "requires": {
+        "chalk": "^1.1.3",
+        "esutils": "^2.0.2",
+        "js-tokens": "^3.0.2"
+      },
+      "dependencies": {
+        "js-tokens": {
+          "version": "3.0.2",
+          "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
+          "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls="
+        }
+      }
+    },
     "babel-eslint": {
       "version": "10.0.2",
       "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.0.2.tgz",
@@ -304,6 +368,104 @@
         }
       }
     },
+    "babel-generator": {
+      "version": "6.26.1",
+      "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz",
+      "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==",
+      "requires": {
+        "babel-messages": "^6.23.0",
+        "babel-runtime": "^6.26.0",
+        "babel-types": "^6.26.0",
+        "detect-indent": "^4.0.0",
+        "jsesc": "^1.3.0",
+        "lodash": "^4.17.4",
+        "source-map": "^0.5.7",
+        "trim-right": "^1.0.1"
+      },
+      "dependencies": {
+        "jsesc": {
+          "version": "1.3.0",
+          "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz",
+          "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s="
+        }
+      }
+    },
+    "babel-messages": {
+      "version": "6.23.0",
+      "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz",
+      "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=",
+      "requires": {
+        "babel-runtime": "^6.22.0"
+      }
+    },
+    "babel-runtime": {
+      "version": "6.26.0",
+      "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
+      "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
+      "requires": {
+        "core-js": "^2.4.0",
+        "regenerator-runtime": "^0.11.0"
+      }
+    },
+    "babel-traverse": {
+      "version": "6.26.0",
+      "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz",
+      "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=",
+      "requires": {
+        "babel-code-frame": "^6.26.0",
+        "babel-messages": "^6.23.0",
+        "babel-runtime": "^6.26.0",
+        "babel-types": "^6.26.0",
+        "babylon": "^6.18.0",
+        "debug": "^2.6.8",
+        "globals": "^9.18.0",
+        "invariant": "^2.2.2",
+        "lodash": "^4.17.4"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "globals": {
+          "version": "9.18.0",
+          "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz",
+          "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ=="
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+        }
+      }
+    },
+    "babel-types": {
+      "version": "6.26.0",
+      "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz",
+      "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=",
+      "requires": {
+        "babel-runtime": "^6.26.0",
+        "esutils": "^2.0.2",
+        "lodash": "^4.17.4",
+        "to-fast-properties": "^1.0.3"
+      },
+      "dependencies": {
+        "to-fast-properties": {
+          "version": "1.0.3",
+          "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz",
+          "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc="
+        }
+      }
+    },
+    "babylon": {
+      "version": "6.18.0",
+      "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz",
+      "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ=="
+    },
     "balanced-match": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
@@ -328,6 +490,14 @@
       "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
       "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="
     },
+    "cancel-token": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/cancel-token/-/cancel-token-0.1.1.tgz",
+      "integrity": "sha1-wYGXZ0uxyEwdaTPr8V2NWlznm08=",
+      "requires": {
+        "@types/node": "^4.0.30"
+      }
+    },
     "chalk": {
       "version": "1.1.3",
       "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
@@ -559,6 +729,14 @@
         "object-keys": "^1.0.12"
       }
     },
+    "detect-indent": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz",
+      "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=",
+      "requires": {
+        "repeating": "^2.0.0"
+      }
+    },
     "doctrine": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
@@ -640,31 +818,6 @@
       "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
       "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
     },
-    "escodegen": {
-      "version": "1.11.1",
-      "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.1.tgz",
-      "integrity": "sha512-JwiqFD9KdGVVpeuRa68yU3zZnBEOcPs0nKW7wZzXky8Z7tffdYUHbe11bPCV5jYlK6DVdKLWLm0f5I/QlL0Kmw==",
-      "requires": {
-        "esprima": "^3.1.3",
-        "estraverse": "^4.2.0",
-        "esutils": "^2.0.2",
-        "optionator": "^0.8.1",
-        "source-map": "~0.6.1"
-      },
-      "dependencies": {
-        "esprima": {
-          "version": "3.1.3",
-          "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz",
-          "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM="
-        },
-        "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-          "optional": true
-        }
-      }
-    },
     "eslint": {
       "version": "5.16.0",
       "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.16.0.tgz",
@@ -787,9 +940,12 @@
       }
     },
     "eslint-utils": {
-      "version": "1.3.1",
-      "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz",
-      "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q=="
+      "version": "1.4.2",
+      "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.2.tgz",
+      "integrity": "sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q==",
+      "requires": {
+        "eslint-visitor-keys": "^1.0.0"
+      }
     },
     "eslint-visitor-keys": {
       "version": "1.0.0",
@@ -995,6 +1151,11 @@
       "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
       "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o="
     },
+    "indent": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/indent/-/indent-0.0.2.tgz",
+      "integrity": "sha1-jHnwgBkFWbaHA0uEx676l9WpEdk="
+    },
     "inflight": {
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@@ -1070,6 +1231,14 @@
         }
       }
     },
+    "invariant": {
+      "version": "2.2.4",
+      "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
+      "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
+      "requires": {
+        "loose-envify": "^1.0.0"
+      }
+    },
     "is-callable": {
       "version": "1.1.4",
       "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz",
@@ -1080,6 +1249,14 @@
       "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz",
       "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY="
     },
+    "is-finite": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz",
+      "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=",
+      "requires": {
+        "number-is-nan": "^1.0.0"
+      }
+    },
     "is-fullwidth-code-point": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
@@ -1106,6 +1283,11 @@
         "has-symbols": "^1.0.0"
       }
     },
+    "is-windows": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
+      "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA=="
+    },
     "isexe": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@@ -1169,6 +1351,27 @@
       "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz",
       "integrity": "sha1-U8y6BH0G4VjTEfRdpiX05J5vFm4="
     },
+    "lodash.sortby": {
+      "version": "4.7.0",
+      "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
+      "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg="
+    },
+    "loose-envify": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+      "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+      "requires": {
+        "js-tokens": "^3.0.0 || ^4.0.0"
+      }
+    },
+    "magic-string": {
+      "version": "0.22.5",
+      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.22.5.tgz",
+      "integrity": "sha512-oreip9rJZkzvA8Qzk9HFs8fZGF/u7H/gtrE8EN6RjKJ9kh2HlC+yQ2QezifqTZfGyiuAV0dRv5a+y/8gBb1m9w==",
+      "requires": {
+        "vlq": "^0.2.2"
+      }
+    },
     "mdn-data": {
       "version": "1.1.4",
       "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-1.1.4.tgz",
@@ -1228,6 +1431,11 @@
         "boolbase": "~1.0.0"
       }
     },
+    "number-is-nan": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+      "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
+    },
     "object-keys": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
@@ -1315,40 +1523,59 @@
       "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
       "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A="
     },
+    "path-parse": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+      "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
+    },
     "polymer-analyzer": {
-      "version": "2.7.0",
-      "resolved": "https://registry.npmjs.org/polymer-analyzer/-/polymer-analyzer-2.7.0.tgz",
-      "integrity": "sha512-yD5FYQ8thX/2vHTaEgTtCs/NSG3ko4VlEb0IjM/PFsu03lHNHnpadC1NGwKyvI9vOjcFhnw4mDEVW0lbxLo8Eg==",
+      "version": "3.2.4",
+      "resolved": "https://registry.npmjs.org/polymer-analyzer/-/polymer-analyzer-3.2.4.tgz",
+      "integrity": "sha512-JmxUhMajTuC18tLXbTtu2+aN2x9bTX+4MvCD4IZKJ0rtAL8jWi1iRLfogpHJB4Ig9Dc8EEEuEYipLuzPFl3vqA==",
       "requires": {
+        "@babel/generator": "^7.0.0-beta.42",
+        "@babel/traverse": "^7.0.0-beta.42",
+        "@babel/types": "^7.0.0-beta.42",
+        "@types/babel-generator": "^6.25.1",
+        "@types/babel-traverse": "^6.25.2",
+        "@types/babel-types": "^6.25.1",
+        "@types/babylon": "^6.16.2",
         "@types/chai-subset": "^1.3.0",
         "@types/chalk": "^0.4.30",
         "@types/clone": "^0.1.30",
         "@types/cssbeautify": "^0.3.1",
         "@types/doctrine": "^0.0.1",
-        "@types/escodegen": "^0.0.2",
-        "@types/estraverse": "^0.0.6",
-        "@types/estree": "^0.0.37",
-        "@types/node": "^6.0.0",
+        "@types/is-windows": "^0.2.0",
+        "@types/minimatch": "^3.0.1",
         "@types/parse5": "^2.2.34",
+        "@types/path-is-inside": "^1.0.0",
+        "@types/resolve": "0.0.6",
+        "@types/whatwg-url": "^6.4.0",
+        "babylon": "^7.0.0-beta.42",
+        "cancel-token": "^0.1.1",
         "chalk": "^1.1.3",
         "clone": "^2.0.0",
         "cssbeautify": "^0.3.1",
-        "doctrine": "^2.0.0",
-        "dom5": "^2.1.0",
-        "escodegen": "^1.7.0",
-        "espree": "^3.1.7",
-        "estraverse": "^4.2.0",
+        "doctrine": "^2.0.2",
+        "dom5": "^3.0.0",
+        "indent": "0.0.2",
+        "is-windows": "^1.0.2",
         "jsonschema": "^1.1.0",
-        "parse5": "^2.2.1",
+        "minimatch": "^3.0.4",
+        "parse5": "^4.0.0",
+        "path-is-inside": "^1.0.2",
+        "resolve": "^1.5.0",
         "shady-css-parser": "^0.1.0",
         "stable": "^0.1.6",
-        "strip-indent": "^2.0.0"
+        "strip-indent": "^2.0.0",
+        "vscode-uri": "=1.0.6",
+        "whatwg-url": "^6.4.0"
       },
       "dependencies": {
-        "@types/node": {
-          "version": "6.14.5",
-          "resolved": "https://registry.npmjs.org/@types/node/-/node-6.14.5.tgz",
-          "integrity": "sha512-50PRp2qLJYjnFV/BWc839MN/9YeSrNz5DWzCiKYw3GVF/YyMClcHxTWGsVc0CPNpJpk3CIp0dOqLxqP3U/Pc+A=="
+        "@types/babel-types": {
+          "version": "6.25.2",
+          "resolved": "https://registry.npmjs.org/@types/babel-types/-/babel-types-6.25.2.tgz",
+          "integrity": "sha512-+3bMuktcY4a70a0KZc8aPJlEOArPuAKQYHU5ErjkOqGJdx8xuEEVK6nWogqigBOJ8nKPxRpyCUDTCPmZ3bUxGA=="
         },
         "@types/parse5": {
           "version": "2.2.34",
@@ -1358,50 +1585,61 @@
             "@types/node": "*"
           }
         },
+        "babylon": {
+          "version": "7.0.0-beta.47",
+          "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.47.tgz",
+          "integrity": "sha512-+rq2cr4GDhtToEzKFD6KZZMDBXhjFAr9JjPw9pAppZACeEWqNM294j+NdBzkSHYXwzzBmVjZ3nEVJlOhbR2gOQ=="
+        },
         "clone": {
           "version": "2.1.2",
           "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
           "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18="
         },
         "dom5": {
-          "version": "2.3.0",
-          "resolved": "https://registry.npmjs.org/dom5/-/dom5-2.3.0.tgz",
-          "integrity": "sha1-+CBJdb0NrLvltYqKk//B/tD/zSo=",
+          "version": "3.0.1",
+          "resolved": "https://registry.npmjs.org/dom5/-/dom5-3.0.1.tgz",
+          "integrity": "sha512-JPFiouQIr16VQ4dX6i0+Hpbg3H2bMKPmZ+WZgBOSSvOPx9QHwwY8sPzeM2baUtViESYto6wC2nuZOMC/6gulcA==",
           "requires": {
-            "@types/clone": "^0.1.29",
-            "@types/node": "^6.0.0",
-            "@types/parse5": "^2.2.32",
+            "@types/parse5": "^2.2.34",
             "clone": "^2.1.0",
-            "parse5": "^2.2.2"
+            "parse5": "^4.0.0"
           }
         },
         "parse5": {
-          "version": "2.2.3",
-          "resolved": "https://registry.npmjs.org/parse5/-/parse5-2.2.3.tgz",
-          "integrity": "sha1-DE/EHBAAxea5PUiwP4CDg3g06fY="
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz",
+          "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA=="
         }
       }
     },
     "polymer-bundler": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/polymer-bundler/-/polymer-bundler-3.1.1.tgz",
-      "integrity": "sha512-a/MItYr/rTRZ8Ymj5npmoAAm89RC5hRh4yz8bs22GyMn+NlE7HqJ4EzLrPkyRWlOyBhbhYFPx2Zot8PlaWXvmQ==",
+      "version": "4.0.10",
+      "resolved": "https://registry.npmjs.org/polymer-bundler/-/polymer-bundler-4.0.10.tgz",
+      "integrity": "sha512-nwlN3LQlQDqbZ2sUH3394C/dHZUDHq8tpdS5HARvPDb0Q9IXWD+znOR1cr7wSjF0EZN4LiUH5hWyUoV4QSjhpQ==",
       "requires": {
+        "@types/babel-generator": "^6.25.1",
+        "@types/babel-traverse": "^6.25.3",
+        "babel-generator": "^6.26.1",
+        "babel-traverse": "^6.26.0",
+        "babel-types": "^6.26.0",
         "clone": "^2.1.0",
-        "command-line-args": "^3.0.1",
-        "command-line-usage": "^3.0.3",
-        "dom5": "^2.2.0",
-        "espree": "^3.4.0",
+        "command-line-args": "^5.0.2",
+        "command-line-usage": "^5.0.5",
+        "dom5": "^3.0.0",
+        "espree": "^3.5.2",
+        "magic-string": "^0.22.4",
         "mkdirp": "^0.5.1",
-        "parse5": "^2.2.2",
-        "polymer-analyzer": "^2.3.0",
-        "source-map": "^0.5.6"
+        "parse5": "^4.0.0",
+        "polymer-analyzer": "^3.2.2",
+        "rollup": "^1.3.0",
+        "source-map": "^0.5.6",
+        "vscode-uri": "=1.0.6"
       },
       "dependencies": {
-        "@types/node": {
-          "version": "6.14.5",
-          "resolved": "https://registry.npmjs.org/@types/node/-/node-6.14.5.tgz",
-          "integrity": "sha512-50PRp2qLJYjnFV/BWc839MN/9YeSrNz5DWzCiKYw3GVF/YyMClcHxTWGsVc0CPNpJpk3CIp0dOqLxqP3U/Pc+A=="
+        "@types/babel-types": {
+          "version": "6.25.2",
+          "resolved": "https://registry.npmjs.org/@types/babel-types/-/babel-types-6.25.2.tgz",
+          "integrity": "sha512-+3bMuktcY4a70a0KZc8aPJlEOArPuAKQYHU5ErjkOqGJdx8xuEEVK6nWogqigBOJ8nKPxRpyCUDTCPmZ3bUxGA=="
         },
         "@types/parse5": {
           "version": "2.2.34",
@@ -1411,39 +1649,239 @@
             "@types/node": "*"
           }
         },
+        "ansi-styles": {
+          "version": "3.2.1",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+          "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+          "requires": {
+            "color-convert": "^1.9.0"
+          }
+        },
+        "array-back": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz",
+          "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q=="
+        },
+        "babylon": {
+          "version": "7.0.0-beta.47",
+          "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.47.tgz",
+          "integrity": "sha512-+rq2cr4GDhtToEzKFD6KZZMDBXhjFAr9JjPw9pAppZACeEWqNM294j+NdBzkSHYXwzzBmVjZ3nEVJlOhbR2gOQ=="
+        },
+        "chalk": {
+          "version": "2.4.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+          "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+          "requires": {
+            "ansi-styles": "^3.2.1",
+            "escape-string-regexp": "^1.0.5",
+            "supports-color": "^5.3.0"
+          }
+        },
         "clone": {
           "version": "2.1.2",
           "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
           "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18="
         },
-        "dom5": {
-          "version": "2.3.0",
-          "resolved": "https://registry.npmjs.org/dom5/-/dom5-2.3.0.tgz",
-          "integrity": "sha1-+CBJdb0NrLvltYqKk//B/tD/zSo=",
+        "command-line-args": {
+          "version": "5.1.1",
+          "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.1.1.tgz",
+          "integrity": "sha512-hL/eG8lrll1Qy1ezvkant+trihbGnaKaeEjj6Scyr3DN+RC7iQ5Rz84IeLERfAWDGo0HBSNAakczwgCilDXnWg==",
           "requires": {
-            "@types/clone": "^0.1.29",
-            "@types/node": "^6.0.0",
-            "@types/parse5": "^2.2.32",
+            "array-back": "^3.0.1",
+            "find-replace": "^3.0.0",
+            "lodash.camelcase": "^4.3.0",
+            "typical": "^4.0.0"
+          }
+        },
+        "command-line-usage": {
+          "version": "5.0.5",
+          "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-5.0.5.tgz",
+          "integrity": "sha512-d8NrGylA5oCXSbGoKz05FkehDAzSmIm4K03S5VDh4d5lZAtTWfc3D1RuETtuQCn8129nYfJfDdF7P/lwcz1BlA==",
+          "requires": {
+            "array-back": "^2.0.0",
+            "chalk": "^2.4.1",
+            "table-layout": "^0.4.3",
+            "typical": "^2.6.1"
+          },
+          "dependencies": {
+            "array-back": {
+              "version": "2.0.0",
+              "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz",
+              "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==",
+              "requires": {
+                "typical": "^2.6.1"
+              }
+            },
+            "typical": {
+              "version": "2.6.1",
+              "resolved": "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz",
+              "integrity": "sha1-XAgOXWYcu+OCWdLnCjxyU+hziB0="
+            }
+          }
+        },
+        "deep-extend": {
+          "version": "0.6.0",
+          "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+          "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
+        },
+        "dom5": {
+          "version": "3.0.1",
+          "resolved": "https://registry.npmjs.org/dom5/-/dom5-3.0.1.tgz",
+          "integrity": "sha512-JPFiouQIr16VQ4dX6i0+Hpbg3H2bMKPmZ+WZgBOSSvOPx9QHwwY8sPzeM2baUtViESYto6wC2nuZOMC/6gulcA==",
+          "requires": {
+            "@types/parse5": "^2.2.34",
             "clone": "^2.1.0",
-            "parse5": "^2.2.2"
+            "parse5": "^4.0.0"
+          }
+        },
+        "find-replace": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz",
+          "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==",
+          "requires": {
+            "array-back": "^3.0.1"
           }
         },
         "parse5": {
-          "version": "2.2.3",
-          "resolved": "https://registry.npmjs.org/parse5/-/parse5-2.2.3.tgz",
-          "integrity": "sha1-DE/EHBAAxea5PUiwP4CDg3g06fY="
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz",
+          "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA=="
+        },
+        "polymer-analyzer": {
+          "version": "3.2.4",
+          "resolved": "https://registry.npmjs.org/polymer-analyzer/-/polymer-analyzer-3.2.4.tgz",
+          "integrity": "sha512-JmxUhMajTuC18tLXbTtu2+aN2x9bTX+4MvCD4IZKJ0rtAL8jWi1iRLfogpHJB4Ig9Dc8EEEuEYipLuzPFl3vqA==",
+          "requires": {
+            "@babel/generator": "^7.0.0-beta.42",
+            "@babel/traverse": "^7.0.0-beta.42",
+            "@babel/types": "^7.0.0-beta.42",
+            "@types/babel-generator": "^6.25.1",
+            "@types/babel-traverse": "^6.25.2",
+            "@types/babel-types": "^6.25.1",
+            "@types/babylon": "^6.16.2",
+            "@types/chai-subset": "^1.3.0",
+            "@types/chalk": "^0.4.30",
+            "@types/clone": "^0.1.30",
+            "@types/cssbeautify": "^0.3.1",
+            "@types/doctrine": "^0.0.1",
+            "@types/is-windows": "^0.2.0",
+            "@types/minimatch": "^3.0.1",
+            "@types/parse5": "^2.2.34",
+            "@types/path-is-inside": "^1.0.0",
+            "@types/resolve": "0.0.6",
+            "@types/whatwg-url": "^6.4.0",
+            "babylon": "^7.0.0-beta.42",
+            "cancel-token": "^0.1.1",
+            "chalk": "^1.1.3",
+            "clone": "^2.0.0",
+            "cssbeautify": "^0.3.1",
+            "doctrine": "^2.0.2",
+            "dom5": "^3.0.0",
+            "indent": "0.0.2",
+            "is-windows": "^1.0.2",
+            "jsonschema": "^1.1.0",
+            "minimatch": "^3.0.4",
+            "parse5": "^4.0.0",
+            "path-is-inside": "^1.0.2",
+            "resolve": "^1.5.0",
+            "shady-css-parser": "^0.1.0",
+            "stable": "^0.1.6",
+            "strip-indent": "^2.0.0",
+            "vscode-uri": "=1.0.6",
+            "whatwg-url": "^6.4.0"
+          },
+          "dependencies": {
+            "ansi-styles": {
+              "version": "2.2.1",
+              "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+              "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
+            },
+            "chalk": {
+              "version": "1.1.3",
+              "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+              "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+              "requires": {
+                "ansi-styles": "^2.2.1",
+                "escape-string-regexp": "^1.0.2",
+                "has-ansi": "^2.0.0",
+                "strip-ansi": "^3.0.0",
+                "supports-color": "^2.0.0"
+              }
+            },
+            "supports-color": {
+              "version": "2.0.0",
+              "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+              "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
+            }
+          }
+        },
+        "supports-color": {
+          "version": "5.5.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+          "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+          "requires": {
+            "has-flag": "^3.0.0"
+          }
+        },
+        "table-layout": {
+          "version": "0.4.5",
+          "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-0.4.5.tgz",
+          "integrity": "sha512-zTvf0mcggrGeTe/2jJ6ECkJHAQPIYEwDoqsiqBjI24mvRmQbInK5jq33fyypaCBxX08hMkfmdOqj6haT33EqWw==",
+          "requires": {
+            "array-back": "^2.0.0",
+            "deep-extend": "~0.6.0",
+            "lodash.padend": "^4.6.1",
+            "typical": "^2.6.1",
+            "wordwrapjs": "^3.0.0"
+          },
+          "dependencies": {
+            "array-back": {
+              "version": "2.0.0",
+              "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz",
+              "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==",
+              "requires": {
+                "typical": "^2.6.1"
+              }
+            },
+            "typical": {
+              "version": "2.6.1",
+              "resolved": "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz",
+              "integrity": "sha1-XAgOXWYcu+OCWdLnCjxyU+hziB0="
+            }
+          }
+        },
+        "typical": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz",
+          "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw=="
+        },
+        "wordwrapjs": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-3.0.0.tgz",
+          "integrity": "sha512-mO8XtqyPvykVCsrwj5MlOVWvSnCdT+C+QVbm6blradR7JExAhbkZ7hZ9A+9NUtwzSqrlUo9a67ws0EiILrvRpw==",
+          "requires": {
+            "reduce-flatten": "^1.0.1",
+            "typical": "^2.6.1"
+          },
+          "dependencies": {
+            "typical": {
+              "version": "2.6.1",
+              "resolved": "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz",
+              "integrity": "sha1-XAgOXWYcu+OCWdLnCjxyU+hziB0="
+            }
+          }
         }
       }
     },
     "polymer-css-build": {
-      "version": "0.3.3",
-      "resolved": "https://registry.npmjs.org/polymer-css-build/-/polymer-css-build-0.3.3.tgz",
-      "integrity": "sha512-KbMPaFNoMUJFUfjnpj4b3AqiS21vQfcwNNgzU60xsQU1/bI2xkCL+3tb+F/+qUPdVnOJFlinM++lCRuRKV73Yw==",
+      "version": "0.7.0",
+      "resolved": "https://registry.npmjs.org/polymer-css-build/-/polymer-css-build-0.7.0.tgz",
+      "integrity": "sha512-VZDHvFLxLEkHDhPn0lnoJ/17OEwG0DDFcvsLKPt4mdzbcgXwu89sbvqj6w/0sDzgC8Ki5RBeOE7X+q1hkjVrvA==",
       "requires": {
         "command-line-args": "^5.0.2",
         "command-line-usage": "^5.0.5",
         "dom5": "^3.0.1",
-        "polymer-analyzer": "2.7 - 3"
+        "polymer-analyzer": "^3.1.0"
       },
       "dependencies": {
         "@types/parse5": {
@@ -1556,9 +1994,9 @@
           }
         },
         "table-layout": {
-          "version": "0.4.4",
-          "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-0.4.4.tgz",
-          "integrity": "sha512-uNaR3SRMJwfdp9OUr36eyEi6LLsbcTqTO/hfTsNviKsNeyMBPICJCC7QXRF3+07bAP6FRwA8rczJPBqXDc0CkQ==",
+          "version": "0.4.5",
+          "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-0.4.5.tgz",
+          "integrity": "sha512-zTvf0mcggrGeTe/2jJ6ECkJHAQPIYEwDoqsiqBjI24mvRmQbInK5jq33fyypaCBxX08hMkfmdOqj6haT33EqWw==",
           "requires": {
             "array-back": "^2.0.0",
             "deep-extend": "~0.6.0",
@@ -1630,11 +2068,32 @@
       "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-1.0.1.tgz",
       "integrity": "sha1-JYx479FT3fk8tWEjf2EYTzaW4yc="
     },
+    "regenerator-runtime": {
+      "version": "0.11.1",
+      "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
+      "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
+    },
     "regexpp": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz",
       "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw=="
     },
+    "repeating": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz",
+      "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=",
+      "requires": {
+        "is-finite": "^1.0.0"
+      }
+    },
+    "resolve": {
+      "version": "1.12.0",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz",
+      "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==",
+      "requires": {
+        "path-parse": "^1.0.6"
+      }
+    },
     "resolve-from": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
@@ -1657,6 +2116,23 @@
         "glob": "^7.1.3"
       }
     },
+    "rollup": {
+      "version": "1.23.1",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.23.1.tgz",
+      "integrity": "sha512-95C1GZQpr/NIA0kMUQmSjuMDQ45oZfPgDBcN0yZwBG7Kee//m7H68vgIyg+SPuyrTZ5PrXfyLK80OzXeKG5dAA==",
+      "requires": {
+        "@types/estree": "*",
+        "@types/node": "*",
+        "acorn": "^7.1.0"
+      },
+      "dependencies": {
+        "acorn": {
+          "version": "7.1.0",
+          "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz",
+          "integrity": "sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ=="
+        }
+      }
+    },
     "run-async": {
       "version": "2.3.0",
       "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
@@ -1923,6 +2399,14 @@
       "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
       "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4="
     },
+    "tr46": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
+      "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=",
+      "requires": {
+        "punycode": "^2.1.0"
+      }
+    },
     "trim-right": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz",
@@ -1989,6 +2473,31 @@
         "object.getownpropertydescriptors": "^2.0.3"
       }
     },
+    "vlq": {
+      "version": "0.2.3",
+      "resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.3.tgz",
+      "integrity": "sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow=="
+    },
+    "vscode-uri": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-1.0.6.tgz",
+      "integrity": "sha512-sLI2L0uGov3wKVb9EB+vIQBl9tVP90nqRvxSoJ35vI3NjxE8jfsE5DSOhWgSunHSZmKS4OCi2jrtfxK7uyp2ww=="
+    },
+    "webidl-conversions": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
+      "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg=="
+    },
+    "whatwg-url": {
+      "version": "6.5.0",
+      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz",
+      "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==",
+      "requires": {
+        "lodash.sortby": "^4.7.0",
+        "tr46": "^1.0.1",
+        "webidl-conversions": "^4.0.2"
+      }
+    },
     "which": {
       "version": "1.3.1",
       "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
diff --git a/third_party/node/package.json b/third_party/node/package.json
index a7584625..d0801ae 100644
--- a/third_party/node/package.json
+++ b/third_party/node/package.json
@@ -6,8 +6,8 @@
     "babel-eslint": "10.0.2",
     "crisper": "2.1.1",
     "eslint": "5.16.0",
-    "polymer-bundler": "3.1.1",
-    "polymer-css-build": "0.3.3",
+    "polymer-bundler": "4.0.10",
+    "polymer-css-build": "0.7.0",
     "uglify-es": "3.3.9",
     "svgo": "1.2.0",
     "typescript": "3.5.3"
diff --git a/tools/android/android_studio/ChromiumStyle.xml b/tools/android/android_studio/ChromiumStyle.xml
index 8549f7f..5b5a808 100644
--- a/tools/android/android_studio/ChromiumStyle.xml
+++ b/tools/android/android_studio/ChromiumStyle.xml
@@ -19,6 +19,8 @@
     <value>
       <package name="android" withSubpackages="true" static="true" />
       <emptyLine />
+      <package name="androidx" withSubpackages="true" static="true" />
+      <emptyLine />
       <package name="com" withSubpackages="true" static="true" />
       <emptyLine />
       <package name="dalvik" withSubpackages="true" static="true" />
@@ -39,6 +41,8 @@
       <emptyLine />
       <package name="android" withSubpackages="true" static="false" />
       <emptyLine />
+      <package name="androidx" withSubpackages="true" static="false" />
+      <emptyLine />
       <package name="com" withSubpackages="true" static="false" />
       <emptyLine />
       <package name="dalvik" withSubpackages="true" static="false" />
diff --git a/tools/mb/docs/user_guide.md b/tools/mb/docs/user_guide.md
index 2bbf65f..cc00ee13 100644
--- a/tools/mb/docs/user_guide.md
+++ b/tools/mb/docs/user_guide.md
@@ -171,7 +171,7 @@
 Tries your change on the trybots. Right now this is essentially a fancy tryjob,
 like one you could trigger via `git cl try` or via CQ dry runs. Basic usage is
 
-`mb.py try -m tryserver.chromium.linux -b linux-rel //base:base_unittests`
+`mb.py try -m tryserver.chromium.linux -b linux-rel base_unittests`
 
 Your change must be uploaded to Gerrit. Local changes will not be uploaded for
 you. It uses the gerrit CL associated with your given git branch.
@@ -182,7 +182,7 @@
 Any trybot in `trybots.py` is supported; you can test your code on windows, for
 example. The tryjob will compile and run your code on windows.
 
-The target (`base_unittests`) in the example is a GN build target. Most GN
+The target (`base_unittests`) in the example is a ninja build target. Most ninja
 unittest targets can be put here which currently runs on the bots.
 
 ### mb validate
diff --git a/tools/mb/mb.py b/tools/mb/mb.py
index f6428ce..806da4c 100755
--- a/tools/mb/mb.py
+++ b/tools/mb/mb.py
@@ -384,20 +384,11 @@
     return 0
 
   def CmdTry(self):
-    target = self.args.target
-    if not target.startswith('//'):
-      self.Print("Expected a GN target like //:foo, got %s" % target)
+    ninja_target = self.args.target
+    if ninja_target.startswith('//'):
+      self.Print("Expected a nijna target like base_unittests, got %s" % target)
       return 1
 
-    recipe_name = None
-    isolate_map = self.ReadIsolateMap()
-    for name, config in isolate_map.iteritems():
-      if 'label' in config and config['label'] == target:
-        recipe_name = name
-        break
-    if not recipe_name:
-      self.Print("Unable to find a recipe entry for %s." % target)
-
     json_path = self.PathJoin(self.chromium_src_dir, 'out.json')
     try:
       ret, out, err = self.Run(
@@ -430,7 +421,7 @@
       # TODO(martiniss): maybe don't always assume the bucket?
       'led', 'get-builder', 'luci.chromium.try:%s' % self.args.builder).then(
       'led', 'edit', '-r', 'chromium_trybot_experimental',
-        '-p', 'tests=["%s"]' % recipe_name).then(
+        '-p', 'tests=["%s"]' % ninja_target).then(
       'led', 'edit-cr-cl', issue_data['issue_url']).then(
       'led', 'launch').result
 
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index daa8acd4..0e4e7a2 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -1765,6 +1765,16 @@
   <int value="7" label="Request success"/>
 </enum>
 
+<enum name="AndroidSearchEngineLogoEvents">
+  <summary>Events related to Search Engine logo feature.</summary>
+  <int value="0" label="Fetch non-Google logo request"/>
+  <int value="1" label="Fetch failed, null logo url"/>
+  <int value="2" label="Fetch failed, favicon helper error"/>
+  <int value="3" label="Fetch failed, returned bitmap null"/>
+  <int value="4" label="Fetch success, cache hit"/>
+  <int value="5" label="Fetch from network success"/>
+</enum>
+
 <enum name="AndroidSeccompSandboxStatus">
   <int value="0" label="Not Supported"/>
   <int value="1" label="Detection Failed"/>
@@ -3002,6 +3012,8 @@
   <int value="3" label="kStylus"/>
   <int value="4" label="kSuggestionChip"/>
   <int value="5" label="kVoiceInput"/>
+  <int value="6" label="kProactiveSuggestions"/>
+  <int value="7" label="kLibAssistantInitiated"/>
 </enum>
 
 <enum name="AsyncDNSConfigParsePosix">
@@ -11643,6 +11655,16 @@
   <int value="1" label="JCB card"/>
 </enum>
 
+<enum name="CrOSActionRecorderEvent">
+  <int value="0" label="Disabled"/>
+  <int value="1" label="RecordAction"/>
+  <int value="2" label="FlushToDisk"/>
+  <int value="3" label="ReadFromFileFail"/>
+  <int value="4" label="ParseFromStringFail"/>
+  <int value="5" label="CreateDirectoryFail"/>
+  <int value="6" label="WriteFileAtomicallyFail"/>
+</enum>
+
 <enum name="CrosBeamformingDeviceState">
   <int value="0" label="Default enabled"/>
   <int value="1" label="User enabled"/>
@@ -35496,6 +35518,7 @@
   <int value="-1732561795" label="ConsistentOmniboxGeolocation:enabled"/>
   <int value="-1731149013" label="AndroidMessagesIntegration:enabled"/>
   <int value="-1729926412" label="enable-webusb-notifications"/>
+  <int value="-1729808721" label="SubresourceRedirect:enabled"/>
   <int value="-1727530898" label="LookalikeUrlNavigationSuggestionsUI:enabled"/>
   <int value="-1727173228" label="OmniboxZeroSuggestionsOnNTP:enabled"/>
   <int value="-1725507605" label="enable-web-midi"/>
@@ -35638,6 +35661,7 @@
       label="ContextualSuggestionsAlternateCardLayout:disabled"/>
   <int value="-1536293422" label="SharedArrayBuffer:enabled"/>
   <int value="-1536242739" label="security-chip"/>
+  <int value="-1535946978" label="MixBrowserTypeTabs:disabled"/>
   <int value="-1535758690" label="AutoplayIgnoreWebAudio:disabled"/>
   <int value="-1533258008" label="CalculateNativeWinOcclusion:enabled"/>
   <int value="-1532035450" label="DragTabsInTabletMode:disabled"/>
@@ -35870,6 +35894,7 @@
   <int value="-1268836676" label="disable-out-of-process-pdf"/>
   <int value="-1267958145" label="disable-pdf-material-ui"/>
   <int value="-1262730949" label="EnableDspHotword:enabled"/>
+  <int value="-1262303946" label="SubresourceRedirectPreviews:disabled"/>
   <int value="-1262152606" label="disable-lock-screen-apps"/>
   <int value="-1261972671" label="OmniboxDocumentProvider:disabled"/>
   <int value="-1261263046"
@@ -36184,6 +36209,7 @@
   <int value="-885601782" label="enable-contextual-search"/>
   <int value="-884864731" label="WebPaymentsSingleAppUiSkip:enabled"/>
   <int value="-883694393" label="SyncPseudoUSSSupervisedUsers:disabled"/>
+  <int value="-883608641" label="enable-cros-action-recorder"/>
   <int value="-882434910" label="EnableAggregatedMlSearchRanking:enabled"/>
   <int value="-881447505" label="ash-disable-shelf-model-synchronization"/>
   <int value="-881054479" label="WebAssemblyStreaming:disabled"/>
@@ -36469,7 +36495,6 @@
   <int value="-510488450" label="disable-pnacl"/>
   <int value="-508250572" label="ServiceWorkerLongRunningMessage:disabled"/>
   <int value="-508143738" label="disable-accelerated-fixed-root-background"/>
-  <int value="-506743105" label="enable-subresource-redirect"/>
   <int value="-506706655" label="respect-autocomplete-off-autofill"/>
   <int value="-506366023" label="VizHitTest:enabled"/>
   <int value="-505679399" label="FontCacheScaling:enabled"/>
@@ -36654,6 +36679,7 @@
   <int value="-258081634" label="AutofillAssistantDirectActions:disabled"/>
   <int value="-255264176" label="OmniboxSearchEngineLogo:disabled"/>
   <int value="-254887599" label="google-profile-info"/>
+  <int value="-254158542" label="SubresourceRedirectPreviews:enabled"/>
   <int value="-250822813" label="PwaImprovedSplashScreen:enabled"/>
   <int value="-250721831" label="AndroidAutofillAccessibility:disabled"/>
   <int value="-250543540" label="DeferAllScript:enabled"/>
@@ -36920,7 +36946,6 @@
   <int value="83422372"
       label="ChromeHomePersonalizedOmniboxSuggestions:enabled"/>
   <int value="84911198" label="ScanCardsInWebPayments:disabled"/>
-  <int value="88412508" label="EnableCrOSActionRecorder:enabled"/>
   <int value="88437020" label="FeaturePolicy:enabled"/>
   <int value="89785725"
       label="DataReductionProxyEnabledWithNetworkService:disabled"/>
@@ -37008,7 +37033,6 @@
   <int value="217455219" label="SyncStandaloneTransport:enabled"/>
   <int value="218890378" label="ManualSaving:disabled"/>
   <int value="219117936" label="AllowReaderForAccessibility:enabled"/>
-  <int value="220418174" label="EnableCrOSActionRecorder:disabled"/>
   <int value="221212638" label="StartSurfaceAndroid:enabled"/>
   <int value="222184258"
       label="AutofillEnforceMinRequiredFieldsForHeuristics:disabled"/>
@@ -37947,6 +37971,7 @@
   <int value="1473967338" label="OmniboxShortBookmarkSuggestions:enabled"/>
   <int value="1474861626" label="disable-multi-mirroring"/>
   <int value="1479248574" label="disable-voice-input"/>
+  <int value="1480607006" label="MixBrowserTypeTabs:enabled"/>
   <int value="1481562816" label="disable-password-link"/>
   <int value="1482039233" label="SearchSuggestionsOnLocalNtp:disabled"/>
   <int value="1482839038" label="AutofillCreditCardAuthentication:enabled"/>
@@ -38143,6 +38168,7 @@
   <int value="1745053254" label="ClickToCallOpenDialerDirectly:enabled"/>
   <int value="1747279677" label="disable-delegated-renderer"/>
   <int value="1748481830" label="AppManagement:enabled"/>
+  <int value="1749028785" label="SubresourceRedirect:disabled"/>
   <int value="1750822869" label="CrostiniBackup:disabled"/>
   <int value="1752168018" label="enable-stale-while-revalidate"/>
   <int value="1755024316" label="HostWindowsInAppShimProcess:disabled"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 840db8b..69b9968 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -4357,6 +4357,18 @@
   </summary>
 </histogram>
 
+<histogram name="AndroidSearchEngineLogo.Events"
+    enum="AndroidSearchEngineLogoEvents" expires_after="2020-10-14">
+  <owner>wylieb@chromium.org</owner>
+  <owner>lzbylut@chromium.org</owner>
+  <summary>
+    Counts occurences of various events related to the Search Engine Logo
+    feature. When enabled, the feature will show the logo of the default search
+    engine in the omnibox. These events record the performance of fetching
+    non-Google logos.
+  </summary>
+</histogram>
+
 <histogram name="AndroidSms.EffectivePWAInstallationSuccess"
     enum="BooleanSuccess" expires_after="2020-05-10">
   <owner>azeemarshad@chromium.org</owner>
@@ -26542,6 +26554,18 @@
   <summary>Chrome OS shelf clicks.</summary>
 </histogram>
 
+<histogram name="Cros.CrOSActionRecorderEvent" enum="CrOSActionRecorderEvent"
+    expires_after="2020-11-02">
+  <owner>charleszhao@chromium.org</owner>
+  <owner>tby@chromium.org</owner>
+  <summary>
+    This histogram is emitted each time when CrOSAction is recorded by the
+    CrOSActionRecorder. For each CrOSAction multiple values can be emitted,
+    corresponding to different events happened during the action processing by
+    CrOSActionRecorder.
+  </summary>
+</histogram>
+
 <histogram name="CrosDisks.ArchiveType" enum="CrosDisksArchiveType"
     expires_after="2020-02-16">
   <owner>benchan@chromium.org</owner>
@@ -36170,20 +36194,45 @@
   </summary>
 </histogram>
 
+<histogram name="DrmUtil.CreateDisplaySnapshot.BitsPerChannel" units="bits"
+    expires_after="M89">
+  <owner>andrescj@chromium.org</owner>
+  <owner>mcasas@chromium.org</owner>
+  <owner>chromeos-gfx@google.com</owner>
+  <summary>
+    Number of bits per channel described by a parsed EDID blob. This UMA is
+    recorded whenever the color space is extracted from an EDID blob.
+  </summary>
+</histogram>
+
 <histogram name="DrmUtil.CreateDisplaySnapshot.HasEdidBlob" enum="Boolean"
     expires_after="2020-07-24">
   <owner>andrescj@chromium.org</owner>
   <owner>mcasas@chromium.org</owner>
+  <owner>chromeos-gfx@google.com</owner>
   <summary>
     Whether an EDID blob was detected. This UMA is recorded whenever we attempt
     to parse the EDID from a display.
   </summary>
 </histogram>
 
+<histogram name="DrmUtil.CreateDisplaySnapshot.IsHDR" enum="Boolean"
+    expires_after="M89">
+  <owner>andrescj@chromium.org</owner>
+  <owner>mcasas@chromium.org</owner>
+  <owner>chromeos-gfx@google.com</owner>
+  <summary>
+    Whether a EDID blob contained an HDR transfer function (e.g. SMPT SE2084 or
+    HLG). This UMA is recorded whenever the color space is extracted from an
+    EDID blob.
+  </summary>
+</histogram>
+
 <histogram name="DrmUtil.GetColorSpaceFromEdid.ChecksOutcome"
     enum="EdidColorSpaceChecksOutcome" expires_after="2020-07-24">
   <owner>andrescj@chromium.org</owner>
   <owner>mcasas@chromium.org</owner>
+  <owner>chromeos-gfx@google.com</owner>
   <summary>
     When attempting to get the color space from an EDID blob, whether the sanity
     checks passed (and if not, which check failed). This UMA is recorded
@@ -98788,6 +98837,16 @@
   </summary>
 </histogram>
 
+<histogram name="PageLoad.Clients.SubresourceLoading.CookiesQueryTime"
+    units="ms" expires_after="M90">
+  <owner>robertogden@chromium.org</owner>
+  <owner>tbansal@chromium.org</owner>
+  <summary>
+    How long the query to the cookie manager took to complete. Recorded on every
+    query response.
+  </summary>
+</histogram>
+
 <histogram
     name="PageLoad.Clients.SubresourceLoading.DaysSinceLastVisitToOrigin"
     units="days" expires_after="M90">
@@ -98813,6 +98872,16 @@
   </summary>
 </histogram>
 
+<histogram name="PageLoad.Clients.SubresourceLoading.HistoryQueryTime"
+    units="ms" expires_after="M90">
+  <owner>robertogden@chromium.org</owner>
+  <owner>tbansal@chromium.org</owner>
+  <summary>
+    How long the query to the history service took to complete. Recorded on
+    every query response.
+  </summary>
+</histogram>
+
 <histogram
     name="PageLoad.Clients.SubresourceLoading.LoadedCSSJSBeforeFCP.Cached"
     units="count of subresources" expires_after="M90">
diff --git a/ui/display/util/edid_parser_unittest.cc b/ui/display/util/edid_parser_unittest.cc
index 53517a9..635d1ca 100644
--- a/ui/display/util/edid_parser_unittest.cc
+++ b/ui/display/util/edid_parser_unittest.cc
@@ -165,7 +165,7 @@
     "\x00\x53\x41\x4d\x53\x55\x4e\x47\x0a\x20\x20\x20\x20\x20\x01\x5a"
     "\x02\x03\x4f\xf0\x53\x5f\x10\x1f\x04\x13\x05\x14\x20\x21\x22\x5d"
     "\x5e\x62\x63\x64\x07\x16\x03\x12\x2c\x09\x07\x07\x15\x07\x50\x3d"
-    "\x04\xc0\x57\x07\x00\x83\x01\x00\x00\xe2\x00\x0f\xe3\x05\x03\x01"
+    "\x04\xc0\x57\x07\x00\x83\x01\x00\x00\xe2\x00\x0f\xe3\x05\x83\x01"
     "\x6e\x03\x0c\x00\x30\x00\xb8\x3c\x20\x00\x80\x01\x02\x03\x04\xe3"
     "\x06\x0d\x01\xe5\x0e\x60\x61\x65\x66\xe5\x01\x8b\x84\x90\x01\x01"
     "\x1d\x80\xd0\x72\x1c\x16\x20\x10\x2c\x25\x80\x50\x1d\x74\x00\x00"
@@ -414,7 +414,8 @@
      21442559853606400,
      "SAM",
      "0DF6",
-     {gfx::ColorSpace::PrimaryID::BT709, gfx::ColorSpace::PrimaryID::SMPTE170M},
+     {gfx::ColorSpace::PrimaryID::BT709, gfx::ColorSpace::PrimaryID::SMPTE170M,
+      gfx::ColorSpace::PrimaryID::BT2020},
      {gfx::ColorSpace::TransferID::BT709,
       gfx::ColorSpace::TransferID::SMPTEST2084,
       gfx::ColorSpace::TransferID::ARIB_STD_B67},
diff --git a/ui/file_manager/file_manager/foreground/elements/files_xf_elements_unittest.js b/ui/file_manager/file_manager/foreground/elements/files_xf_elements_unittest.js
index ca6debc..6ca55e0 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_xf_elements_unittest.js
+++ b/ui/file_manager/file_manager/foreground/elements/files_xf_elements_unittest.js
@@ -55,22 +55,76 @@
   const panelItem = displayPanel.addPanelItem('testpanel');
   panelItem.panelType = panelItem.panelTypeProgress;
 
+  // Setup the panel item signal (click) callback.
+  /** @type {?string} */
+  let signal = null;
+  panelItem.signalCallback = (name) => {
+    assert(typeof name === 'string');
+    signal = name;
+  };
+
   // Verify the panel item indicator is progress.
-  assertTrue(
-      panelItem.indicator === 'progress',
+  assertEquals(
+      panelItem.indicator, 'progress',
       'Wrong panel indicator, got ' + panelItem.indicator);
 
+  // Check cancel signal from the panel from a click.
+  /** @type {!HTMLElement} */
+  const cancel = assert(panelItem.secondaryButton);
+  cancel.click();
+  assertEquals(
+      signal, 'cancel', 'Expected signal name "cancel". Got ' + signal);
+
   // Change the panel item to an error panel.
   panelItem.panelType = panelItem.panelTypeError;
 
   // Verify the panel item indicator is set to error.
-  assertTrue(
-      panelItem.indicator === 'status',
+  assertEquals(
+      panelItem.indicator, 'status',
       'Wrong panel indicator, got ' + panelItem.indicator);
-  assertTrue(
-      panelItem.status === 'failure',
+  assertEquals(
+      panelItem.status, 'failure',
       'Wrong panel status, got ' + panelItem.status);
 
+  // Check dismiss signal from the panel from a click.
+  /** @type {!HTMLElement} */
+  let dismiss = assert(panelItem.secondaryButton);
+  dismiss.click();
+  assertEquals(
+      signal, 'dismiss', 'Expected signal name "dismiss". Got ' + signal);
+
+  // Change the panel type to a done panel.
+  panelItem.panelType = panelItem.panelTypeDone;
+
+  // Verify the panel item indicator is set to done.
+  assertEquals(
+      panelItem.indicator, 'status',
+      'Wrong panel indicator, got ' + panelItem.indicator);
+  assertEquals(
+      panelItem.status, 'success',
+      'Wrong panel status, got ' + panelItem.status);
+
+  // Check the dimiss signal from the panel from a click.
+  signal = 'none';
+  dismiss = assert(panelItem.primaryButton);
+  dismiss.click();
+  assertEquals(
+      signal, 'dismiss', 'Expected signal name "dismiss". Got ' + signal);
+
+  // Change the type to a summary panel.
+  panelItem.panelType = panelItem.panelTypeSummary;
+
+  // Verify the panel item indicator is largeprogress.
+  assertEquals(
+      panelItem.indicator, 'largeprogress',
+      'Wrong panel indicator, got ' + panelItem.indicator);
+
+  // Check no signal emitted from the summary panel from a click.
+  const expand = assert(panelItem.primaryButton);
+  signal = 'none';
+  expand.click();
+  assertEquals(signal, 'none', 'Expected no signal. Got ' + signal);
+
   done();
 }
 
@@ -101,7 +155,7 @@
   // Confirm multiple progress panels cause creation of a summary panel.
   const summaryContainer = displayPanel.shadowRoot.querySelector('#summary');
   let summaryPanelItem = summaryContainer.querySelector('xf-panel-item');
-  assertTrue(summaryPanelItem.panelType === summaryPanelItem.panelTypeSummary);
+  assertEquals(summaryPanelItem.panelType, summaryPanelItem.panelTypeSummary);
 
   // Confirm the expected height of the summary panel.
   bounds = summaryPanelItem.getBoundingClientRect();
@@ -119,10 +173,10 @@
   let panelToRemove = displayPanel.findPanelItemById('testpanel1');
   displayPanel.removePanelItem(panelToRemove);
   summaryPanelItem = summaryContainer.querySelector('xf-panel-item');
-  assertTrue(summaryPanelItem === null);
+  assertEquals(summaryPanelItem, null);
 
   // Confirm the reference to the removed panel item is gone.
-  assertTrue(displayPanel.findPanelItemById('testpanel1') === null);
+  assertEquals(displayPanel.findPanelItemById('testpanel1'), null);
 
   // Add another panel item and confirm the expanded state persists by
   // checking the panel container is not hidden and there is a summary panel.
@@ -130,7 +184,7 @@
   progressPanel.panelType = progressPanel.panelTypeProgress;
   assertFalse(panelContainer.hasAttribute('hidden'));
   summaryPanelItem = summaryContainer.querySelector('xf-panel-item');
-  assertTrue(summaryPanelItem.panelType === summaryPanelItem.panelTypeSummary);
+  assertEquals(summaryPanelItem.panelType, summaryPanelItem.panelTypeSummary);
 
   done();
 }
diff --git a/ui/gfx/color_space.cc b/ui/gfx/color_space.cc
index d94a1c6..77c4829d 100644
--- a/ui/gfx/color_space.cc
+++ b/ui/gfx/color_space.cc
@@ -93,6 +93,15 @@
   return result;
 }
 
+// static
+ColorSpace ColorSpace::CreateCustom(const skcms_Matrix3x3& to_XYZD50,
+                                    TransferID transfer) {
+  ColorSpace result(ColorSpace::PrimaryID::CUSTOM, transfer,
+                    ColorSpace::MatrixID::RGB, ColorSpace::RangeID::FULL);
+  result.SetCustomPrimaries(to_XYZD50);
+  return result;
+}
+
 void ColorSpace::SetCustomPrimaries(const skcms_Matrix3x3& to_XYZD50) {
   const PrimaryID kIDsToCheck[] = {
       PrimaryID::BT709,
diff --git a/ui/gfx/color_space.h b/ui/gfx/color_space.h
index cb72649..f3d38e0 100644
--- a/ui/gfx/color_space.h
+++ b/ui/gfx/color_space.h
@@ -151,6 +151,7 @@
              const skcms_TransferFunction& fn,
              MatrixID matrix,
              RangeID full_range);
+
   explicit ColorSpace(const SkColorSpace& sk_color_space);
 
   // Returns true if this is not the default-constructor object.
@@ -167,6 +168,8 @@
   }
   static ColorSpace CreateCustom(const skcms_Matrix3x3& to_XYZD50,
                                  const skcms_TransferFunction& fn);
+  static ColorSpace CreateCustom(const skcms_Matrix3x3& to_XYZD50,
+                                 TransferID transfer);
   static constexpr ColorSpace CreateXYZD50() {
     return ColorSpace(PrimaryID::XYZ_D50, TransferID::LINEAR, MatrixID::RGB,
                       RangeID::FULL);
@@ -220,7 +223,7 @@
   size_t GetHash() const;
   std::string ToString() const;
 
-  // Returns true if the decoded values can be outside of the 0.0-1.0 range.
+  // Returns true if the transfer function is an HDR one (SMPTE 2084, HLG, etc).
   bool IsHDR() const;
 
   // Returns true if the encoded values can be outside of the 0.0-1.0 range.
diff --git a/ui/ozone/platform/drm/common/drm_util.cc b/ui/ozone/platform/drm/common/drm_util.cc
index 902fec34..db3f9e1 100644
--- a/ui/ozone/platform/drm/common/drm_util.cc
+++ b/ui/ozone/platform/drm/common/drm_util.cc
@@ -15,7 +15,7 @@
 #include <utility>
 
 #include "base/containers/flat_map.h"
-#include "base/metrics/histogram_macros.h"
+#include "base/metrics/histogram_functions.h"
 #include "ui/display/types/display_constants.h"
 #include "ui/display/types/display_mode.h"
 #include "ui/display/util/edid_parser.h"
@@ -30,8 +30,8 @@
 // Used in the GetColorSpaceFromEdid function to collect data on whether the
 // color space extracted from an EDID blob passed the sanity checks.
 void EmitEdidColorSpaceChecksOutcomeUma(EdidColorSpaceChecksOutcome outcome) {
-  UMA_HISTOGRAM_ENUMERATION("DrmUtil.GetColorSpaceFromEdid.ChecksOutcome",
-                            outcome);
+  base::UmaHistogramEnumeration("DrmUtil.GetColorSpaceFromEdid.ChecksOutcome",
+                                outcome);
 }
 
 bool IsCrtcInUse(
@@ -466,8 +466,8 @@
 
   ScopedDrmPropertyBlobPtr edid_blob(
       GetDrmPropertyBlob(fd, info->connector(), "EDID"));
-  UMA_HISTOGRAM_BOOLEAN("DrmUtil.CreateDisplaySnapshot.HasEdidBlob",
-                        !!edid_blob);
+  base::UmaHistogramBoolean("DrmUtil.CreateDisplaySnapshot.HasEdidBlob",
+                            !!edid_blob);
   std::vector<uint8_t> edid;
   if (edid_blob) {
     edid.assign(static_cast<uint8_t*>(edid_blob->data),
@@ -482,7 +482,11 @@
     has_overscan =
         edid_parser.has_overscan_flag() && edid_parser.overscan_flag();
     display_color_space = GetColorSpaceFromEdid(edid_parser);
+    base::UmaHistogramBoolean("DrmUtil.CreateDisplaySnapshot.IsHDR",
+                              display_color_space.IsHDR());
     bits_per_channel = std::max(edid_parser.bits_per_channel(), 0);
+    base::UmaHistogramCounts100("DrmUtil.CreateDisplaySnapshot.BitsPerChannel",
+                                bits_per_channel);
   } else {
     VLOG(1) << "Failed to get EDID blob for connector "
             << info->connector()->connector_id;
@@ -701,9 +705,29 @@
         EdidColorSpaceChecksOutcome::kErrorBadGamma);
     return gfx::ColorSpace();
   }
+  EmitEdidColorSpaceChecksOutcomeUma(EdidColorSpaceChecksOutcome::kSuccess);
+
+  gfx::ColorSpace::TransferID transfer_id =
+      gfx::ColorSpace::TransferID::INVALID;
+  if (base::Contains(edid_parser.supported_color_primary_ids(),
+                     gfx::ColorSpace::PrimaryID::BT2020)) {
+    if (base::Contains(edid_parser.supported_color_transfer_ids(),
+                       gfx::ColorSpace::TransferID::SMPTEST2084)) {
+      transfer_id = gfx::ColorSpace::TransferID::SMPTEST2084;
+    } else if (base::Contains(edid_parser.supported_color_transfer_ids(),
+                              gfx::ColorSpace::TransferID::ARIB_STD_B67)) {
+      transfer_id = gfx::ColorSpace::TransferID::ARIB_STD_B67;
+    }
+  } else if (gamma == 2.2f) {
+    transfer_id = gfx::ColorSpace::TransferID::GAMMA22;
+  } else if (gamma == 2.4f) {
+    transfer_id = gfx::ColorSpace::TransferID::GAMMA24;
+  }
+
+  if (transfer_id != gfx::ColorSpace::TransferID::INVALID)
+    return gfx::ColorSpace::CreateCustom(color_space_as_matrix, transfer_id);
 
   skcms_TransferFunction transfer = {gamma, 1.f, 0.f, 0.f, 0.f, 0.f, 0.f};
-  EmitEdidColorSpaceChecksOutcomeUma(EdidColorSpaceChecksOutcome::kSuccess);
   return gfx::ColorSpace::CreateCustom(color_space_as_matrix, transfer);
 }
 
diff --git a/ui/ozone/platform/drm/common/drm_util_unittest.cc b/ui/ozone/platform/drm/common/drm_util_unittest.cc
index 1340d3d..d51eaca 100644
--- a/ui/ozone/platform/drm/common/drm_util_unittest.cc
+++ b/ui/ozone/platform/drm/common/drm_util_unittest.cc
@@ -64,6 +64,25 @@
     "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xfc"
     "\x00\x4c\x51\x31\x32\x33\x50\x31\x4a\x58\x33\x32\x0a\x20\x00\xb6";
 
+// A Samsung monitor that supports HDR metadata.
+constexpr unsigned char kHDR[] =
+    "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\x2d\xf6\x0d\x00\x0e\x00\x01"
+    "\x01\x1b\x01\x03\x80\x5f\x36\x78\x0a\x23\xad\xa4\x54\x4d\x99\x26"
+    "\x0f\x47\x4a\xbd\xef\x80\x71\x4f\x81\xc0\x81\x00\x81\x80\x95\x00"
+    "\xa9\xc0\xb3\x00\x01\x01\x04\x74\x00\x30\xf2\x70\x5a\x80\xb0\x58"
+    "\x8a\x00\x50\x1d\x74\x00\x00\x1e\x02\x3a\x80\x18\x71\x38\x2d\x40"
+    "\x58\x2c\x45\x00\x50\x1d\x74\x00\x00\x1e\x00\x00\x00\xfd\x00\x18"
+    "\x4b\x0f\x51\x1e\x00\x0a\x20\x20\x20\x20\x20\x20\x00\x00\x00\xfc"
+    "\x00\x53\x41\x4d\x53\x55\x4e\x47\x0a\x20\x20\x20\x20\x20\x01\x5a"
+    "\x02\x03\x4f\xf0\x53\x5f\x10\x1f\x04\x13\x05\x14\x20\x21\x22\x5d"
+    "\x5e\x62\x63\x64\x07\x16\x03\x12\x2c\x09\x07\x07\x15\x07\x50\x3d"
+    "\x04\xc0\x57\x07\x00\x83\x01\x00\x00\xe2\x00\x0f\xe3\x05\x83\x01"
+    "\x6e\x03\x0c\x00\x30\x00\xb8\x3c\x20\x00\x80\x01\x02\x03\x04\xe3"
+    "\x06\x0d\x01\xe5\x0e\x60\x61\x65\x66\xe5\x01\x8b\x84\x90\x01\x01"
+    "\x1d\x80\xd0\x72\x1c\x16\x20\x10\x2c\x25\x80\x50\x1d\x74\x00\x00"
+    "\x9e\x66\x21\x56\xaa\x51\x00\x1e\x30\x46\x8f\x33\x00\x50\x1d\x74"
+    "\x00\x00\x1e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbd";
+
 // Partially valid EDID: gamma information is marked as non existent.
 const unsigned char kEdidWithNoGamma[] =
     "\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x22\xF0\x76\x26\x01\x01\x01\x01"
@@ -391,6 +410,30 @@
           EdidColorSpaceChecksOutcome::kSuccess),
       3);
 
+  // Test with a display that supports HDR.
+  constexpr SkColorSpacePrimaries expected_hdr_primaries = {.fRX = 0.640625f,
+                                                            .fRY = 0.330078f,
+                                                            .fGX = 0.300781f,
+                                                            .fGY = 0.600586f,
+                                                            .fBX = 0.150391f,
+                                                            .fBY = 0.060547f,
+                                                            .fWX = 0.280273f,
+                                                            .fWY = 0.290039f};
+  skcms_Matrix3x3 expected_hdr_toXYZ50_matrix;
+  expected_hdr_primaries.toXYZD50(&expected_hdr_toXYZ50_matrix);
+  const std::vector<uint8_t> hdr_edid(kHDR, kHDR + base::size(kHDR) - 1);
+  const gfx::ColorSpace expected_hdr_color_space =
+      gfx::ColorSpace::CreateCustom(expected_hdr_toXYZ50_matrix,
+                                    gfx::ColorSpace::TransferID::SMPTEST2084);
+  EXPECT_TRUE(expected_hdr_color_space.IsHDR());
+  EXPECT_EQ(expected_hdr_color_space.ToString(),
+            GetColorSpaceFromEdid(display::EdidParser(hdr_edid)).ToString());
+  histogram_tester.ExpectBucketCount(
+      "DrmUtil.GetColorSpaceFromEdid.ChecksOutcome",
+      static_cast<base::HistogramBase::Sample>(
+          EdidColorSpaceChecksOutcome::kSuccess),
+      4);
+
   // Test with gamma marked as non-existent.
   const std::vector<uint8_t> no_gamma_edid(
       kEdidWithNoGamma, kEdidWithNoGamma + base::size(kEdidWithNoGamma) - 1);
@@ -403,7 +446,7 @@
           EdidColorSpaceChecksOutcome::kErrorBadGamma),
       1);
   histogram_tester.ExpectTotalCount(
-      "DrmUtil.GetColorSpaceFromEdid.ChecksOutcome", 4);
+      "DrmUtil.GetColorSpaceFromEdid.ChecksOutcome", 5);
 }
 
 TEST_F(DrmUtilTest, GetInvalidColorSpaceFromEdid) {
diff --git a/ui/views/test/ui_controls_factory_desktop_aurax11.cc b/ui/views/test/ui_controls_factory_desktop_aurax11.cc
index a258860..6e69e40 100644
--- a/ui/views/test/ui_controls_factory_desktop_aurax11.cc
+++ b/ui/views/test/ui_controls_factory_desktop_aurax11.cc
@@ -251,10 +251,10 @@
     // Most interactive_ui_tests run inside of the aura_test_helper
     // environment. This means that we can't rely on display::Screen and several
     // other things to work properly. Therefore we hack around this by
-    // iterating across the windows owned DesktopWindowTreeHostX11 since this
+    // iterating across the windows owned DesktopWindowTreeHostLinux since this
     // doesn't rely on having a DesktopScreenX11.
     std::vector<aura::Window*> windows =
-        DesktopWindowTreeHostX11::GetAllOpenWindows();
+        DesktopWindowTreeHostLinux::GetAllOpenWindows();
     const auto i =
         std::find_if(windows.cbegin(), windows.cend(), [point](auto* window) {
           return window->GetBoundsInScreen().Contains(point) ||
diff --git a/ui/views/test/widget_test_aura.cc b/ui/views/test/widget_test_aura.cc
index e1d6b9b8..0ae91d8f 100644
--- a/ui/views/test/widget_test_aura.cc
+++ b/ui/views/test/widget_test_aura.cc
@@ -13,10 +13,13 @@
 #include "ui/views/widget/widget.h"
 #include "ui/wm/core/shadow_controller.h"
 
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+#include "ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h"
+#endif
+
 #if defined(USE_X11)
 #include "ui/gfx/x/x11.h"        // nogncheck
 #include "ui/gfx/x/x11_types.h"  // nogncheck
-#include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h"
 #endif
 
 namespace views {
@@ -69,8 +72,8 @@
 
 std::vector<aura::Window*> GetAllTopLevelWindows() {
   std::vector<aura::Window*> roots;
-#if defined(USE_X11)
-  roots = DesktopWindowTreeHostX11::GetAllOpenWindows();
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+  roots = DesktopWindowTreeHostLinux::GetAllOpenWindows();
 #elif defined(OS_WIN)
   {
     FindAllWindowsData data = {&roots};
diff --git a/ui/views/widget/desktop_aura/desktop_screen_x11.cc b/ui/views/widget/desktop_aura/desktop_screen_x11.cc
index a4e8174..6ca1be4 100644
--- a/ui/views/widget/desktop_aura/desktop_screen_x11.cc
+++ b/ui/views/widget/desktop_aura/desktop_screen_x11.cc
@@ -118,8 +118,8 @@
   // aura::Window's screen bounds.
   aura::WindowTreeHost* host = window->GetHost();
   if (host) {
-    DesktopWindowTreeHostX11* rwh =
-        DesktopWindowTreeHostX11::GetHostForXID(host->GetAcceleratedWidget());
+    auto* rwh = DesktopWindowTreeHostLinux::GetHostForWidget(
+        host->GetAcceleratedWidget());
     if (rwh) {
       const gfx::Rect pixel_rect = rwh->GetBoundsInPixels();
       const gfx::Rect dip_rect =
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc
index 62e8913..4f888a1 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc
@@ -19,8 +19,17 @@
 #include "ui/views/widget/desktop_aura/window_event_filter_linux.h"
 #include "ui/views/widget/widget.h"
 
+DEFINE_UI_CLASS_PROPERTY_TYPE(views::DesktopWindowTreeHostLinux*)
+
 namespace views {
 
+std::list<gfx::AcceleratedWidget>* DesktopWindowTreeHostLinux::open_windows_ =
+    nullptr;
+
+DEFINE_UI_CLASS_PROPERTY_KEY(DesktopWindowTreeHostLinux*,
+                             kHostForRootWindow,
+                             nullptr)
+
 namespace {
 
 class SwapWithNewSizeObserverHelper : public ui::CompositorObserver {
@@ -63,7 +72,48 @@
     : DesktopWindowTreeHostPlatform(native_widget_delegate,
                                     desktop_native_widget_aura) {}
 
-DesktopWindowTreeHostLinux::~DesktopWindowTreeHostLinux() = default;
+DesktopWindowTreeHostLinux::~DesktopWindowTreeHostLinux() {
+  window()->ClearProperty(kHostForRootWindow);
+}
+
+// static
+aura::Window* DesktopWindowTreeHostLinux::GetContentWindowForWidget(
+    gfx::AcceleratedWidget widget) {
+  auto* host = DesktopWindowTreeHostLinux::GetHostForWidget(widget);
+  return host ? host->GetContentWindow() : nullptr;
+}
+
+// static
+DesktopWindowTreeHostLinux* DesktopWindowTreeHostLinux::GetHostForWidget(
+    gfx::AcceleratedWidget widget) {
+  aura::WindowTreeHost* host =
+      aura::WindowTreeHost::GetForAcceleratedWidget(widget);
+  return host ? host->window()->GetProperty(kHostForRootWindow) : nullptr;
+}
+
+// static
+std::vector<aura::Window*> DesktopWindowTreeHostLinux::GetAllOpenWindows() {
+  std::vector<aura::Window*> windows(open_windows().size());
+  std::transform(open_windows().begin(), open_windows().end(), windows.begin(),
+                 GetContentWindowForWidget);
+  return windows;
+}
+
+// static
+void DesktopWindowTreeHostLinux::CleanUpWindowList(
+    void (*func)(aura::Window* window)) {
+  if (!open_windows_)
+    return;
+  while (!open_windows_->empty()) {
+    gfx::AcceleratedWidget widget = open_windows_->front();
+    func(GetContentWindowForWidget(widget));
+    if (!open_windows_->empty() && open_windows_->front() == widget)
+      open_windows_->erase(open_windows_->begin());
+  }
+
+  delete open_windows_;
+  open_windows_ = nullptr;
+}
 
 void DesktopWindowTreeHostLinux::SetPendingXVisualId(int x_visual_id) {
   pending_x_visual_id_ = x_visual_id;
@@ -106,6 +156,8 @@
 
 void DesktopWindowTreeHostLinux::OnNativeWidgetCreated(
     const Widget::InitParams& params) {
+  window()->SetProperty(kHostForRootWindow, this);
+
   CreateNonClientEventFilter();
   DesktopWindowTreeHostPlatform::OnNativeWidgetCreated(params);
 }
@@ -229,10 +281,26 @@
 }
 
 void DesktopWindowTreeHostLinux::OnClosed() {
+  open_windows().remove(GetAcceleratedWidget());
   DestroyNonClientEventFilter();
   DesktopWindowTreeHostPlatform::OnClosed();
 }
 
+void DesktopWindowTreeHostLinux::OnAcceleratedWidgetAvailable(
+    gfx::AcceleratedWidget widget) {
+  open_windows().push_front(widget);
+  DesktopWindowTreeHostPlatform::OnAcceleratedWidgetAvailable(widget);
+}
+
+void DesktopWindowTreeHostLinux::OnActivationChanged(bool active) {
+  if (active) {
+    auto widget = GetAcceleratedWidget();
+    open_windows().remove(widget);
+    open_windows().insert(open_windows().begin(), widget);
+  }
+  DesktopWindowTreeHostPlatform::OnActivationChanged(active);
+}
+
 void DesktopWindowTreeHostLinux::AddAdditionalInitProperties(
     const Widget::InitParams& params,
     ui::PlatformWindowInitProperties* properties) {
@@ -318,6 +386,12 @@
   return static_cast<ui::PlatformWindowLinux*>(platform_window());
 }
 
+std::list<gfx::AcceleratedWidget>& DesktopWindowTreeHostLinux::open_windows() {
+  if (!open_windows_)
+    open_windows_ = new std::list<gfx::AcceleratedWidget>();
+  return *open_windows_;
+}
+
 // As DWTHX11 subclasses DWTHPlatform through DWTHLinux now (during transition
 // period. see https://crbug.com/990756), we need to guard this factory method.
 // TODO(msisov): remove this guard once DWTHX11 is finally merged into
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h b/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h
index ceeabac..0dd3955 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h
@@ -35,6 +35,23 @@
       DesktopNativeWidgetAura* desktop_native_widget_aura);
   ~DesktopWindowTreeHostLinux() override;
 
+  // A way of converting a |widget| into the content_window()
+  // of the associated DesktopNativeWidgetAura.
+  static aura::Window* GetContentWindowForWidget(gfx::AcceleratedWidget widget);
+
+  // A way of converting a |widget| into this object.
+  static DesktopWindowTreeHostLinux* GetHostForWidget(
+      gfx::AcceleratedWidget widget);
+
+  // Get all open top-level windows. This includes windows that may not be
+  // visible. This list is sorted in their stacking order, i.e. the first window
+  // is the topmost window.
+  static std::vector<aura::Window*> GetAllOpenWindows();
+
+  // Runs the |func| callback for each content-window, and deallocates the
+  // internal list of open windows.
+  static void CleanUpWindowList(void (*func)(aura::Window* window));
+
   // This must be called before the window is created, because the visual cannot
   // be changed after. Useful for X11. Not in use for Wayland.
   void SetPendingXVisualId(int x_visual_id);
@@ -64,6 +81,8 @@
   // PlatformWindowDelegateBase:
   void DispatchEvent(ui::Event* event) override;
   void OnClosed() override;
+  void OnAcceleratedWidgetAvailable(gfx::AcceleratedWidget widget) override;
+  void OnActivationChanged(bool active) override;
 
  private:
   FRIEND_TEST_ALL_PREFIXES(DesktopWindowTreeHostLinuxTest, HitTest);
@@ -94,6 +113,9 @@
   const ui::PlatformWindowLinux* GetPlatformWindowLinux() const;
   ui::PlatformWindowLinux* GetPlatformWindowLinux();
 
+  // See comment for variable open_windows_.
+  static std::list<gfx::AcceleratedWidget>& open_windows();
+
   // A handler for events intended for non client area.
   // A posthandler for events intended for non client area. Handles events if no
   // other consumer handled them.
@@ -110,6 +132,10 @@
 
   uint32_t modal_dialog_counter_ = 0;
 
+  // A list of all (top-level) windows that have been created but not yet
+  // destroyed.
+  static std::list<gfx::AcceleratedWidget>* open_windows_;
+
   // The display and the native X window hosting the root window.
   base::WeakPtrFactory<DesktopWindowTreeHostLinux> weak_factory_{this};
 
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
index cbcebb0..3f3097b 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
@@ -66,18 +66,8 @@
 #include "ui/accessibility/platform/atk_util_auralinux.h"
 #endif
 
-DEFINE_UI_CLASS_PROPERTY_TYPE(views::DesktopWindowTreeHostX11*)
-
 namespace views {
 
-std::list<XID>* DesktopWindowTreeHostX11::open_windows_ = nullptr;
-
-DEFINE_UI_CLASS_PROPERTY_KEY(aura::Window*, kViewsWindowForRootWindow, NULL)
-
-DEFINE_UI_CLASS_PROPERTY_KEY(DesktopWindowTreeHostX11*,
-                             kHostForRootWindow,
-                             NULL)
-
 namespace {
 
 bool ShouldDiscardKeyEvent(XEvent* xev) {
@@ -100,35 +90,12 @@
                                  desktop_native_widget_aura) {}
 
 DesktopWindowTreeHostX11::~DesktopWindowTreeHostX11() {
-  window()->ClearProperty(kHostForRootWindow);
   wm::SetWindowMoveClient(window(), nullptr);
 
   // ~DWTHPlatform notifies the DestkopNativeWidgetAura about destruction and
   // also destroyes the dispatcher.
 }
 
-// static
-aura::Window* DesktopWindowTreeHostX11::GetContentWindowForXID(XID xid) {
-  aura::WindowTreeHost* host =
-      aura::WindowTreeHost::GetForAcceleratedWidget(xid);
-  return host ? host->window()->GetProperty(kViewsWindowForRootWindow) : NULL;
-}
-
-// static
-DesktopWindowTreeHostX11* DesktopWindowTreeHostX11::GetHostForXID(XID xid) {
-  aura::WindowTreeHost* host =
-      aura::WindowTreeHost::GetForAcceleratedWidget(xid);
-  return host ? host->window()->GetProperty(kHostForRootWindow) : nullptr;
-}
-
-// static
-std::vector<aura::Window*> DesktopWindowTreeHostX11::GetAllOpenWindows() {
-  std::vector<aura::Window*> windows(open_windows().size());
-  std::transform(open_windows().begin(), open_windows().end(), windows.begin(),
-                 GetContentWindowForXID);
-  return windows;
-}
-
 void DesktopWindowTreeHostX11::AddObserver(
     DesktopWindowTreeHostObserverX11* observer) {
   observer_list_.AddObserver(observer);
@@ -139,21 +106,6 @@
   observer_list_.RemoveObserver(observer);
 }
 
-void DesktopWindowTreeHostX11::CleanUpWindowList(
-    void (*func)(aura::Window* window)) {
-  if (!open_windows_)
-    return;
-  while (!open_windows_->empty()) {
-    gfx::AcceleratedWidget widget = open_windows_->front();
-    func(GetContentWindowForXID(widget));
-    if (!open_windows_->empty() && open_windows_->front() == widget)
-      open_windows_->erase(open_windows_->begin());
-  }
-
-  delete open_windows_;
-  open_windows_ = nullptr;
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // DesktopWindowTreeHostX11, DesktopWindowTreeHost implementation:
 
@@ -171,9 +123,6 @@
 
 void DesktopWindowTreeHostX11::OnNativeWidgetCreated(
     const Widget::InitParams& params) {
-  window()->SetProperty(kViewsWindowForRootWindow, GetContentWindow());
-  window()->SetProperty(kHostForRootWindow, this);
-
   // Ensure that the X11DesktopHandler exists so that it tracks create/destroy
   // notify events.
   X11DesktopHandler::get();
@@ -214,41 +163,8 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-// DesktopWindowTreeHostX11, private:
-
-std::list<gfx::AcceleratedWidget>& DesktopWindowTreeHostX11::open_windows() {
-  if (!open_windows_)
-    open_windows_ = new std::list<gfx::AcceleratedWidget>();
-  return *open_windows_;
-}
-
-////////////////////////////////////////////////////////////////////////////////
 // DesktopWindowTreeHostX11 implementation:
 
-void DesktopWindowTreeHostX11::OnClosed() {
-  open_windows().remove(GetAcceleratedWidget());
-  DesktopWindowTreeHostLinux::OnClosed();
-}
-
-void DesktopWindowTreeHostX11::OnAcceleratedWidgetAvailable(
-    gfx::AcceleratedWidget widget) {
-  open_windows().push_front(widget);
-  WindowTreeHostPlatform::OnAcceleratedWidgetAvailable(widget);
-}
-
-void DesktopWindowTreeHostX11::OnAcceleratedWidgetDestroyed() {}
-
-void DesktopWindowTreeHostX11::OnActivationChanged(bool active) {
-  if (active) {
-    // TODO(thomasanderson): Remove this window shuffling and use XWindowCache
-    // instead.
-    auto widget = GetAcceleratedWidget();
-    open_windows().remove(widget);
-    open_windows().insert(open_windows().begin(), widget);
-  }
-  DesktopWindowTreeHostPlatform::OnActivationChanged(active);
-}
-
 void DesktopWindowTreeHostX11::OnXWindowMapped() {
   for (DesktopWindowTreeHostObserverX11& observer : observer_list_)
     observer.OnWindowMapped(GetXWindow()->window());
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h
index e641d8b..4f4263c7 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h
@@ -44,25 +44,9 @@
       DesktopNativeWidgetAura* desktop_native_widget_aura);
   ~DesktopWindowTreeHostX11() override;
 
-  // A way of converting an X11 |xid| host window into the content_window()
-  // of the associated DesktopNativeWidgetAura.
-  static aura::Window* GetContentWindowForXID(XID xid);
-
-  // A way of converting an X11 |xid| host window into this object.
-  static DesktopWindowTreeHostX11* GetHostForXID(XID xid);
-
-  // Get all open top-level windows. This includes windows that may not be
-  // visible. This list is sorted in their stacking order, i.e. the first window
-  // is the topmost window.
-  static std::vector<aura::Window*> GetAllOpenWindows();
-
   void AddObserver(DesktopWindowTreeHostObserverX11* observer);
   void RemoveObserver(DesktopWindowTreeHostObserverX11* observer);
 
-  // Runs the |func| callback for each content-window, and deallocates the
-  // internal list of open windows.
-  static void CleanUpWindowList(void (*func)(aura::Window* window));
-
  protected:
   // Overridden from DesktopWindowTreeHost:
   void Init(const Widget::InitParams& params) override;
@@ -78,18 +62,11 @@
  private:
   friend class DesktopWindowTreeHostX11HighDPITest;
 
-  // See comment for variable open_windows_.
-  static std::list<XID>& open_windows();
-
   // PlatformWindowDelegate overrides:
   //
   // DWTHX11 temporarily overrides the PlatformWindowDelegate methods instead of
   // underlying DWTHPlatform and WTHPlatform. Eventually, these will be removed
   // from here as we progress in https://crbug.com/990756.
-  void OnClosed() override;
-  void OnAcceleratedWidgetAvailable(gfx::AcceleratedWidget widget) override;
-  void OnAcceleratedWidgetDestroyed() override;
-  void OnActivationChanged(bool active) override;
   void OnXWindowMapped() override;
   void OnXWindowUnmapped() override;
 
@@ -111,10 +88,6 @@
   base::ObserverList<DesktopWindowTreeHostObserverX11>::Unchecked
       observer_list_;
 
-  // A list of all (top-level) windows that have been created but not yet
-  // destroyed.
-  static std::list<gfx::AcceleratedWidget>* open_windows_;
-
   // The display and the native X window hosting the root window.
   base::WeakPtrFactory<DesktopWindowTreeHostX11> weak_factory_{this};
 
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc
index f82417b..1c43e58 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc
@@ -454,7 +454,7 @@
   ASSERT_NE(parent_widget.GetNativeWindow()->GetHost()->GetAcceleratedWidget(),
             child_widget.GetNativeWindow()->GetHost()->GetAcceleratedWidget());
   Widget::CloseAllSecondaryWidgets();
-  EXPECT_TRUE(DesktopWindowTreeHostX11::GetAllOpenWindows().empty());
+  EXPECT_TRUE(DesktopWindowTreeHostLinux::GetAllOpenWindows().empty());
 }
 
 // A Widget that allows setting the min/max size for the widget.
diff --git a/ui/views/widget/desktop_aura/x11_topmost_window_finder.cc b/ui/views/widget/desktop_aura/x11_topmost_window_finder.cc
index 4ef8e358e..670b2e1 100644
--- a/ui/views/widget/desktop_aura/x11_topmost_window_finder.cc
+++ b/ui/views/widget/desktop_aura/x11_topmost_window_finder.cc
@@ -23,7 +23,7 @@
   ignore_ = ignore;
 
   std::vector<aura::Window*> local_process_windows =
-      DesktopWindowTreeHostX11::GetAllOpenWindows();
+      DesktopWindowTreeHostLinux::GetAllOpenWindows();
   if (std::none_of(local_process_windows.cbegin(), local_process_windows.cend(),
                    [this](auto* window) {
                      return ShouldStopIteratingAtLocalProcessWindow(window);
@@ -31,7 +31,8 @@
     return nullptr;
 
   ui::EnumerateTopLevelWindows(this);
-  return DesktopWindowTreeHostX11::GetContentWindowForXID(toplevel_);
+  return DesktopWindowTreeHostLinux::GetContentWindowForWidget(
+      static_cast<gfx::AcceleratedWidget>(toplevel_));
 }
 
 XID X11TopmostWindowFinder::FindWindowAt(
@@ -45,8 +46,8 @@
   if (!ui::IsWindowVisible(xid))
     return false;
 
-  aura::Window* window =
-      views::DesktopWindowTreeHostX11::GetContentWindowForXID(xid);
+  auto* window = DesktopWindowTreeHostLinux::GetContentWindowForWidget(
+      static_cast<gfx::AcceleratedWidget>(xid));
   if (window) {
     if (ShouldStopIteratingAtLocalProcessWindow(window)) {
       toplevel_ = xid;
@@ -72,9 +73,11 @@
   if (!window->IsVisible())
     return false;
 
-  DesktopWindowTreeHostX11* host = DesktopWindowTreeHostX11::GetHostForXID(
+  auto* host = DesktopWindowTreeHostLinux::GetHostForWidget(
       window->GetHost()->GetAcceleratedWidget());
-  if (!host->GetXRootWindowOuterBounds().Contains(screen_loc_in_pixels_))
+  if (!static_cast<DesktopWindowTreeHostX11*>(host)
+           ->GetXRootWindowOuterBounds()
+           .Contains(screen_loc_in_pixels_))
     return false;
 
   aura::client::ScreenPositionClient* screen_position_client =
diff --git a/ui/views/widget/native_widget_aura.cc b/ui/views/widget/native_widget_aura.cc
index 894b80a..1ec2999 100644
--- a/ui/views/widget/native_widget_aura.cc
+++ b/ui/views/widget/native_widget_aura.cc
@@ -59,9 +59,9 @@
 #include "ui/views/widget/desktop_aura/desktop_window_tree_host_win.h"
 #endif
 
-#if defined(USE_X11)
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
 #include "ui/views/linux_ui/linux_ui.h"
-#include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h"
+#include "ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h"
 #endif
 
 #if !defined(OS_CHROMEOS)
@@ -1065,7 +1065,7 @@
 // Widget, public:
 
 namespace {
-#if defined(OS_WIN) || defined(USE_X11)
+#if defined(OS_WIN) || (defined(OS_LINUX) && !defined(OS_CHROMEOS))
 void CloseWindow(aura::Window* window) {
   if (window) {
     Widget* widget = Widget::GetWidgetForNativeView(window);
@@ -1095,13 +1095,13 @@
   EnumThreadWindows(GetCurrentThreadId(), WindowCallbackProc, 0);
 #endif
 
-#if defined(USE_X11)
-  DesktopWindowTreeHostX11::CleanUpWindowList(CloseWindow);
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+  DesktopWindowTreeHostLinux::CleanUpWindowList(CloseWindow);
 #endif
 }
 
 const ui::NativeTheme* Widget::GetNativeTheme() const {
-#if defined(USE_X11)
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
   const LinuxUI* linux_ui = LinuxUI::instance();
   if (linux_ui) {
     ui::NativeTheme* native_theme =