diff --git a/AUTHORS b/AUTHORS
index 15477cbc..e1b3fe7 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -19,6 +19,7 @@
 Abhishek Kanike <abhishek.ka@samsung.com>
 Abhishek Singh <abhi.rathore@samsung.com>
 Adam Bonner <abonner-chromium@solscope.com>
+Adam Bujalski <abujalski@gmail.com>
 Adam Kallai <kadam@inf.u-szeged.hu>
 Adam Roben <adam@github.com>
 Adam Treat <adam.treat@samsung.com>
diff --git a/DEPS b/DEPS
index 79a6cf74..90c20c7 100644
--- a/DEPS
+++ b/DEPS
@@ -142,11 +142,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': 'effee206579608fad40658e608db4aa13910f76f',
+  'skia_revision': '4d03689edb5dfdce22f11414d2351ce2b221a410',
   # 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': 'c382128adc7b4bb1678d6357bbbc993e62afce38',
+  'v8_revision': 'da069071ec861916b268f20bcc21d2d440299a74',
   # 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.
@@ -154,15 +154,15 @@
   # 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': '2afc3398b3d824c1ca7d9a7066f8c10ae52f03c6',
+  'angle_revision': 'da904484bfc696193c15d89c9c6e847afeb978cc',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '142f4581ffc9a90213c2c80496de6d3436208e94',
+  'swiftshader_revision': 'a29aa7717ff8963249d44e2163b78689dfde7565',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': 'c2cdb2edb607b9907d8f2360cce5825cfef2828b',
+  'pdfium_revision': '815726bc4c6997a9054ad124b2b6b82e45008ac5',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -205,7 +205,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '2c752374f1ec682dc41e179278071bc8621bff3e',
+  'catapult_revision': '63f7fcc05ac1011f52d81e926f8638e7aea91133',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -273,11 +273,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'shaderc_revision': '30d8262c71d9267c213103cf37a412fd6fd4c483',
+  'shaderc_revision': '23bbb3211ecee065c118b14b4c8bd13f0a61c04d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': 'ce32a94b3711d6a94f59deb2876f5507a4b674fc',
+  'dawn_revision': '4886403b610c39e2fa92463503b5b6d872b82736',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -806,7 +806,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '16584a68b378caef055f936da36d044aee87865d',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '104b067af6a2eea63e010a4d2a3038c59f178436',
       'condition': 'checkout_linux',
   },
 
@@ -900,7 +900,7 @@
   },
 
   'src/third_party/glslang/src':
-    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + '974a586688c169c5724faf34ed5ed6c8e7563711',
+    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + '22683b409e6df419da940df561b24b4b5d8ab90a',
 
   'src/third_party/google_toolbox_for_mac/src': {
       'url': Var('chromium_git') + '/external/github.com/google/google-toolbox-for-mac.git' + '@' + Var('google_toolbox_for_mac_revision'),
@@ -1080,13 +1080,13 @@
   },
 
   'src/third_party/libvpx/source/libvpx':
-    Var('chromium_git') + '/webm/libvpx.git' + '@' +  '30e7f9d856eb1cc6df895f6d9562493e04f6116d',
+    Var('chromium_git') + '/webm/libvpx.git' + '@' +  'cd9f1763c861edfd86d2814e029a34f3ce821e72',
 
   'src/third_party/libwebm/source':
     Var('chromium_git') + '/webm/libwebm.git' + '@' + '51ca718c3adf0ddedacd7df25fe45f67dc5a9ce1',
 
   'src/third_party/libyuv':
-    Var('chromium_git') + '/libyuv/libyuv.git' + '@' + 'b36c86fdfe746d7be904c3a565b047b24d58087e',  # from r1714
+    Var('chromium_git') + '/libyuv/libyuv.git' + '@' + '09cfb2bbd61448da73a65cdf8eac3594290522bf',  # from r1714
 
   'src/third_party/lighttpd': {
       'url': Var('chromium_git') + '/chromium/deps/lighttpd.git' + '@' + Var('lighttpd_revision'),
@@ -1191,7 +1191,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '6ce3dc661e2c8757ae4b29633c21c2146179b7e5',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  'd1674c0531e0260965667cd9c70609bc1c4cccb2',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1359,7 +1359,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '6f0b34abee8dba611c253738d955c59f703c147a',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '14be7993c6cb04d2c26bad9762c4b09390e2aa1d',
+    Var('webrtc_git') + '/src.git' + '@' + '02d7d353a90bccc6fdd0e517af31fab50944d31c',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1400,7 +1400,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@9c72a3d95c97c8047c931fe8f1b75a2d4ba99c9f',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@dbcafbd04544f460eadeccb3a7f8f5b6149937cc',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/browser/aw_browser_context.cc b/android_webview/browser/aw_browser_context.cc
index 9e17af6a..c6a08f1f 100644
--- a/android_webview/browser/aw_browser_context.cc
+++ b/android_webview/browser/aw_browser_context.cc
@@ -8,6 +8,7 @@
 #include <string>
 #include <utility>
 
+#include "android_webview/browser/aw_browser_policy_connector.h"
 #include "android_webview/browser/aw_download_manager_delegate.h"
 #include "android_webview/browser/aw_form_database_service.h"
 #include "android_webview/browser/aw_metrics_service_client.h"
@@ -24,11 +25,18 @@
 #include "base/single_thread_task_runner.h"
 #include "base/task/post_task.h"
 #include "components/autofill/core/browser/autocomplete_history_manager.h"
+#include "components/autofill/core/common/autofill_prefs.h"
 #include "components/download/public/common/in_progress_download_manager.h"
 #include "components/keyed_service/core/simple_key_map.h"
 #include "components/policy/core/browser/browser_policy_connector_base.h"
+#include "components/policy/core/browser/configuration_policy_pref_store.h"
+#include "components/policy/core/browser/url_blacklist_manager.h"
+#include "components/pref_registry/pref_registry_syncable.h"
+#include "components/prefs/in_memory_pref_store.h"
+#include "components/prefs/json_pref_store.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
+#include "components/prefs/pref_service_factory.h"
 #include "components/safe_browsing/triggers/trigger_manager.h"
 #include "components/url_formatter/url_fixer.h"
 #include "components/user_prefs/user_prefs.h"
@@ -44,7 +52,6 @@
 #include "net/proxy_resolution/proxy_resolution_service.h"
 #include "services/network/public/cpp/features.h"
 #include "services/preferences/tracked/segregated_pref_store.h"
-
 using base::FilePath;
 using content::BrowserThread;
 
@@ -100,22 +107,14 @@
 
 }  // namespace
 
-AwBrowserContext::AwBrowserContext(
-    const base::FilePath path,
-    std::unique_ptr<PrefService> pref_service,
-    std::unique_ptr<policy::BrowserPolicyConnectorBase> policy_connector)
-    : context_storage_path_(path),
-      user_pref_service_(std::move(pref_service)),
-      browser_policy_connector_(std::move(policy_connector)),
+AwBrowserContext::AwBrowserContext()
+    : context_storage_path_(GetContextStoragePath()),
       simple_factory_key_(GetPath(), IsOffTheRecord()) {
   DCHECK(!g_browser_context);
   g_browser_context = this;
   SimpleKeyMap::GetInstance()->Associate(this, &simple_factory_key_);
 
-  BrowserContext::Initialize(this, path);
-
-  pref_change_registrar_.Init(user_pref_service_.get());
-  user_prefs::UserPrefs::Set(this, user_pref_service_.get());
+  BrowserContext::Initialize(this, context_storage_path_);
 
   // This constructor is entered during the creation of ContentBrowserClient,
   // before browser threads are created. Therefore any checks to enforce
@@ -164,12 +163,66 @@
 }
 
 // static
+base::FilePath AwBrowserContext::GetContextStoragePath() {
+  base::FilePath user_data_dir;
+  if (!base::PathService::Get(base::DIR_ANDROID_APP_DATA, &user_data_dir)) {
+    NOTREACHED() << "Failed to get app data directory for Android WebView";
+  }
+
+  return user_data_dir;
+}
+
+// static
 void AwBrowserContext::RegisterPrefs(PrefRegistrySimple* registry) {
+  safe_browsing::RegisterProfilePrefs(registry);
+
+  // Register the Autocomplete Data Retention Policy pref.
+  // The default value '0' represents the latest Chrome major version on which
+  // the retention policy ran. By setting it to a low default value, we're
+  // making sure it runs now (as it only runs once per major version).
+  registry->RegisterIntegerPref(
+      autofill::prefs::kAutocompleteLastVersionRetentionPolicy, 0);
+
+  // We only use the autocomplete feature of Autofill, which is controlled via
+  // the manager_delegate. We don't use the rest of Autofill, which is why it is
+  // hardcoded as disabled here.
+  // TODO(crbug.com/873740): The following also disables autocomplete.
+  // Investigate what the intended behavior is.
+  registry->RegisterBooleanPref(autofill::prefs::kAutofillProfileEnabled,
+                                false);
+  registry->RegisterBooleanPref(autofill::prefs::kAutofillCreditCardEnabled,
+                                false);
+
   registry->RegisterStringPref(prefs::kAuthServerWhitelist, std::string());
   registry->RegisterStringPref(prefs::kAuthAndroidNegotiateAccountType,
                                std::string());
 }
 
+void AwBrowserContext::CreateUserPrefService() {
+  browser_policy_connector_ = std::make_unique<AwBrowserPolicyConnector>();
+
+  auto pref_registry = base::MakeRefCounted<user_prefs::PrefRegistrySyncable>();
+
+  RegisterPrefs(pref_registry.get());
+
+  PrefServiceFactory pref_service_factory;
+  pref_service_factory.set_user_prefs(
+      base::MakeRefCounted<InMemoryPrefStore>());
+
+  policy::URLBlacklistManager::RegisterProfilePrefs(pref_registry.get());
+  pref_service_factory.set_managed_prefs(
+      base::MakeRefCounted<policy::ConfigurationPolicyPrefStore>(
+          browser_policy_connector_.get(),
+          browser_policy_connector_->GetPolicyService(),
+          browser_policy_connector_->GetHandlerList(),
+          policy::POLICY_LEVEL_MANDATORY));
+
+  user_pref_service_ = pref_service_factory.Create(pref_registry);
+  pref_change_registrar_.Init(user_pref_service_.get());
+
+  user_prefs::UserPrefs::Set(this, user_pref_service_.get());
+}
+
 // static
 std::vector<std::string> AwBrowserContext::GetAuthSchemes() {
   // In Chrome this is configurable via the AuthSchemes policy. For WebView
@@ -182,6 +235,8 @@
 void AwBrowserContext::PreMainMessageLoopRun(net::NetLog* net_log) {
   FilePath cache_path = GetCacheDir();
 
+  CreateUserPrefService();
+
   if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) {
     url_request_context_getter_ =
         new AwURLRequestContextGetter(cache_path, CreateProxyConfigService(),
diff --git a/android_webview/browser/aw_browser_context.h b/android_webview/browser/aw_browser_context.h
index 0b7c78b..048b0ca 100644
--- a/android_webview/browser/aw_browser_context.h
+++ b/android_webview/browser/aw_browser_context.h
@@ -23,7 +23,6 @@
 
 class GURL;
 class PrefService;
-class PrefRegistrySimple;
 
 namespace autofill {
 class AutocompleteHistoryManager;
@@ -74,10 +73,7 @@
 class AwBrowserContext : public content::BrowserContext,
                          public visitedlink::VisitedLinkDelegate {
  public:
-  AwBrowserContext(
-      const base::FilePath path,
-      std::unique_ptr<PrefService> pref_service,
-      std::unique_ptr<policy::BrowserPolicyConnectorBase> policy_connector);
+  AwBrowserContext();
   ~AwBrowserContext() override;
 
   // Currently only one instance per process is supported.
@@ -92,6 +88,7 @@
   // common/aw_paths.h (http://crbug.com/934184).
   static base::FilePath GetCacheDir();
   static base::FilePath GetCookieStorePath();
+  static base::FilePath GetContextStoragePath();
 
   static void RegisterPrefs(PrefRegistrySimple* registry);
 
@@ -148,6 +145,7 @@
 
  private:
   void OnAuthPrefsChanged();
+  void CreateUserPrefService();
 
   // The file path where data for this context is persisted.
   base::FilePath context_storage_path_;
diff --git a/android_webview/browser/aw_content_browser_client.cc b/android_webview/browser/aw_content_browser_client.cc
index 0b4382c1..0818133 100644
--- a/android_webview/browser/aw_content_browser_client.cc
+++ b/android_webview/browser/aw_content_browser_client.cc
@@ -23,6 +23,7 @@
 #include "android_webview/browser/aw_feature_list_creator.h"
 #include "android_webview/browser/aw_http_auth_handler.h"
 #include "android_webview/browser/aw_quota_permission_context.h"
+#include "android_webview/browser/aw_resource_context.h"
 #include "android_webview/browser/aw_settings.h"
 #include "android_webview/browser/aw_speech_recognition_manager_delegate.h"
 #include "android_webview/browser/aw_web_contents_view_delegate.h"
@@ -86,6 +87,7 @@
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/content_descriptors.h"
+#include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/service_names.mojom.h"
 #include "content/public/common/url_constants.h"
@@ -432,14 +434,7 @@
 }
 
 AwBrowserContext* AwContentBrowserClient::InitBrowserContext() {
-  base::FilePath user_data_dir;
-  if (!base::PathService::Get(base::DIR_ANDROID_APP_DATA, &user_data_dir)) {
-    NOTREACHED() << "Failed to get app data directory for Android WebView";
-  }
-
-  browser_context_ = std::make_unique<AwBrowserContext>(
-      user_data_dir, aw_feature_list_creator_->TakePrefService(),
-      aw_feature_list_creator_->TakeBrowserPolicyConnector());
+  browser_context_ = std::make_unique<AwBrowserContext>();
   return browser_context_.get();
 }
 
@@ -866,6 +861,7 @@
     content::NavigationUIData* navigation_ui_data,
     int frame_tree_node_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(!base::FeatureList::IsEnabled(::features::kNavigationLoaderOnUI));
 
   std::vector<std::unique_ptr<content::URLLoaderThrottle>> result;
 
@@ -893,8 +889,40 @@
     const bool is_reload = ui::PageTransitionCoreTypeIs(
         static_cast<ui::PageTransition>(request.transition_type),
         ui::PAGE_TRANSITION_RELOAD);
-    if (is_load_url || is_go_back_forward || is_reload)
-      result.push_back(std::make_unique<AwURLLoaderThrottle>(resource_context));
+    if (is_load_url || is_go_back_forward || is_reload) {
+      result.push_back(std::make_unique<AwURLLoaderThrottle>(
+          static_cast<AwResourceContext*>(resource_context)));
+    }
+  }
+
+  return result;
+}
+
+std::vector<std::unique_ptr<content::URLLoaderThrottle>>
+AwContentBrowserClient::CreateURLLoaderThrottles(
+    const network::ResourceRequest& request,
+    content::BrowserContext* browser_context,
+    const base::RepeatingCallback<content::WebContents*()>& wc_getter,
+    content::NavigationUIData* navigation_ui_data,
+    int frame_tree_node_id) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  std::vector<std::unique_ptr<content::URLLoaderThrottle>> result;
+
+  if (request.resource_type ==
+      static_cast<int>(content::ResourceType::kMainFrame)) {
+    const bool is_load_url =
+        request.transition_type & ui::PAGE_TRANSITION_FROM_API;
+    const bool is_go_back_forward =
+        request.transition_type & ui::PAGE_TRANSITION_FORWARD_BACK;
+    const bool is_reload = ui::PageTransitionCoreTypeIs(
+        static_cast<ui::PageTransition>(request.transition_type),
+        ui::PAGE_TRANSITION_RELOAD);
+    if (is_load_url || is_go_back_forward || is_reload) {
+      result.push_back(
+          std::make_unique<AwURLLoaderThrottle>(static_cast<AwResourceContext*>(
+              browser_context->GetResourceContext())));
+    }
   }
 
   return result;
diff --git a/android_webview/browser/aw_content_browser_client.h b/android_webview/browser/aw_content_browser_client.h
index f7cd6a6..d15ef02 100644
--- a/android_webview/browser/aw_content_browser_client.h
+++ b/android_webview/browser/aw_content_browser_client.h
@@ -189,6 +189,13 @@
       const base::RepeatingCallback<content::WebContents*()>& wc_getter,
       content::NavigationUIData* navigation_ui_data,
       int frame_tree_node_id) override;
+  std::vector<std::unique_ptr<content::URLLoaderThrottle>>
+  CreateURLLoaderThrottles(
+      const network::ResourceRequest& request,
+      content::BrowserContext* browser_context,
+      const base::RepeatingCallback<content::WebContents*()>& wc_getter,
+      content::NavigationUIData* navigation_ui_data,
+      int frame_tree_node_id) override;
   bool ShouldOverrideUrlLoading(int frame_tree_node_id,
                                 bool browser_initiated,
                                 const GURL& gurl,
diff --git a/android_webview/browser/aw_feature_list_creator.cc b/android_webview/browser/aw_feature_list_creator.cc
index 217a7a0..be90a46 100644
--- a/android_webview/browser/aw_feature_list_creator.cc
+++ b/android_webview/browser/aw_feature_list_creator.cc
@@ -11,7 +11,6 @@
 #include <vector>
 
 #include "android_webview/browser/aw_browser_context.h"
-#include "android_webview/browser/aw_browser_policy_connector.h"
 #include "android_webview/browser/aw_metrics_service_client.h"
 #include "android_webview/browser/aw_variations_seed_bridge.h"
 #include "android_webview/browser/net/aw_url_request_context_getter.h"
@@ -28,8 +27,6 @@
 #include "components/cdm/browser/media_drm_storage_impl.h"
 #include "components/metrics/metrics_pref_names.h"
 #include "components/metrics/metrics_service.h"
-#include "components/policy/core/browser/configuration_policy_pref_store.h"
-#include "components/policy/core/browser/url_blacklist_manager.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/in_memory_pref_store.h"
 #include "components/prefs/json_pref_store.h"
@@ -73,32 +70,11 @@
   return path;
 }
 
-std::unique_ptr<PrefService> CreatePrefService(
-    policy::BrowserPolicyConnectorBase* browser_policy_connector) {
+std::unique_ptr<PrefService> CreatePrefService() {
   auto pref_registry = base::MakeRefCounted<user_prefs::PrefRegistrySyncable>();
-  // We only use the autocomplete feature of Autofill, which is controlled via
-  // the manager_delegate. We don't use the rest of Autofill, which is why it is
-  // hardcoded as disabled here.
-  // TODO(crbug.com/873740): The following also disables autocomplete.
-  // Investigate what the intended behavior is.
-  pref_registry->RegisterBooleanPref(autofill::prefs::kAutofillProfileEnabled,
-                                     false);
-  pref_registry->RegisterBooleanPref(
-      autofill::prefs::kAutofillCreditCardEnabled, false);
-  policy::URLBlacklistManager::RegisterProfilePrefs(pref_registry.get());
-
-  // Register the Autocomplete Data Retention Policy pref.
-  // The default value '0' represents the latest Chrome major version on which
-  // the retention policy ran. By setting it to a low default value, we're
-  // making sure it runs now (as it only runs once per major version).
-  pref_registry->RegisterIntegerPref(
-      autofill::prefs::kAutocompleteLastVersionRetentionPolicy, 0);
-
-  AwBrowserContext::RegisterPrefs(pref_registry.get());
 
   metrics::MetricsService::RegisterPrefs(pref_registry.get());
   variations::VariationsService::RegisterPrefs(pref_registry.get());
-  safe_browsing::RegisterProfilePrefs(pref_registry.get());
 
 #if BUILDFLAG(ENABLE_MOJO_CDM)
   cdm::MediaDrmStorageImpl::RegisterProfilePrefs(pref_registry.get());
@@ -117,12 +93,7 @@
       base::MakeRefCounted<InMemoryPrefStore>(),
       base::MakeRefCounted<JsonPrefStore>(GetPrefStorePath()), persistent_prefs,
       /*validation_delegate=*/nullptr));
-  pref_service_factory.set_managed_prefs(
-      base::MakeRefCounted<policy::ConfigurationPolicyPrefStore>(
-          browser_policy_connector,
-          browser_policy_connector->GetPolicyService(),
-          browser_policy_connector->GetHandlerList(),
-          policy::POLICY_LEVEL_MANDATORY));
+
   pref_service_factory.set_read_error_callback(
       base::BindRepeating(&HandleReadError));
 
@@ -186,8 +157,7 @@
 }
 
 void AwFeatureListCreator::CreateFeatureListAndFieldTrials() {
-  browser_policy_connector_ = std::make_unique<AwBrowserPolicyConnector>();
-  local_state_ = CreatePrefService(browser_policy_connector_.get());
+  local_state_ = CreatePrefService();
   AwMetricsServiceClient::GetInstance()->Initialize(local_state_.get());
   SetUpFieldTrials();
 }
diff --git a/android_webview/browser/aw_feature_list_creator.h b/android_webview/browser/aw_feature_list_creator.h
index 79c0e4a..0742ceaf 100644
--- a/android_webview/browser/aw_feature_list_creator.h
+++ b/android_webview/browser/aw_feature_list_creator.h
@@ -35,13 +35,6 @@
     return std::move(local_state_);
   }
 
-  // Passes ownership of the |browser_policy_connector_| to the caller.
-  std::unique_ptr<policy::BrowserPolicyConnectorBase>
-  TakeBrowserPolicyConnector() {
-    DCHECK(browser_policy_connector_);
-    return std::move(browser_policy_connector_);
-  }
-
  private:
   // Sets up the field trials and related initialization.
   void SetUpFieldTrials();
@@ -63,10 +56,6 @@
   std::unique_ptr<variations::VariationsFieldTrialCreator>
       variations_field_trial_creator_;
 
-  // If TakePrefService() is called, the caller will take the ownership
-  // of this variable. Stop using this variable afterwards.
-  std::unique_ptr<policy::BrowserPolicyConnectorBase> browser_policy_connector_;
-
   std::unique_ptr<AwVariationsServiceClient> client_;
 
   DISALLOW_COPY_AND_ASSIGN(AwFeatureListCreator);
diff --git a/android_webview/browser/aw_resource_context.cc b/android_webview/browser/aw_resource_context.cc
index 8de62b9c..cb8f0d7 100644
--- a/android_webview/browser/aw_resource_context.cc
+++ b/android_webview/browser/aw_resource_context.cc
@@ -30,7 +30,6 @@
 }
 
 std::string AwResourceContext::GetExtraHeaders(const GURL& url) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   if (!url.is_valid()) return std::string();
   base::AutoLock scoped_lock(extra_headers_lock_);
   std::map<std::string, std::string>::iterator iter =
diff --git a/android_webview/browser/network_service/aw_url_loader_throttle.cc b/android_webview/browser/network_service/aw_url_loader_throttle.cc
index cc96d28..94d2e9c1 100644
--- a/android_webview/browser/network_service/aw_url_loader_throttle.cc
+++ b/android_webview/browser/network_service/aw_url_loader_throttle.cc
@@ -5,21 +5,17 @@
 #include "android_webview/browser/network_service/aw_url_loader_throttle.h"
 
 #include "android_webview/browser/aw_resource_context.h"
-#include "content/public/browser/browser_thread.h"
 #include "net/http/http_response_headers.h"
 
 namespace android_webview {
 
-AwURLLoaderThrottle::AwURLLoaderThrottle(
-    content::ResourceContext* resource_context)
-    : aw_resource_context_(static_cast<AwResourceContext*>(resource_context)) {}
+AwURLLoaderThrottle::AwURLLoaderThrottle(AwResourceContext* aw_resource_context)
+    : aw_resource_context_(aw_resource_context) {}
 
 AwURLLoaderThrottle::~AwURLLoaderThrottle() = default;
 
 void AwURLLoaderThrottle::WillStartRequest(network::ResourceRequest* request,
                                            bool* defer) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-
   AddExtraHeadersIfNeeded(request->url, &request->headers);
 }
 
@@ -29,8 +25,6 @@
     bool* defer,
     std::vector<std::string>* to_be_removed_request_headers,
     net::HttpRequestHeaders* modified_request_headers) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-
   AddExtraHeadersIfNeeded(redirect_info->new_url, modified_request_headers);
 }
 
diff --git a/android_webview/browser/network_service/aw_url_loader_throttle.h b/android_webview/browser/network_service/aw_url_loader_throttle.h
index 38901ed..20f212a 100644
--- a/android_webview/browser/network_service/aw_url_loader_throttle.h
+++ b/android_webview/browser/network_service/aw_url_loader_throttle.h
@@ -10,10 +10,6 @@
 
 class GURL;
 
-namespace content {
-class ResourceContext;
-}
-
 namespace net {
 class HttpRequestHeaders;
 }
@@ -23,7 +19,7 @@
 
 class AwURLLoaderThrottle : public content::URLLoaderThrottle {
  public:
-  explicit AwURLLoaderThrottle(content::ResourceContext* resource_context);
+  explicit AwURLLoaderThrottle(AwResourceContext* aw_resource_context);
   ~AwURLLoaderThrottle() override;
 
   // content::URLLoaderThrottle implementation:
diff --git a/android_webview/docs/quick-start.md b/android_webview/docs/quick-start.md
index c1c6ef9c..a69dc52 100644
--- a/android_webview/docs/quick-start.md
+++ b/android_webview/docs/quick-start.md
@@ -167,7 +167,7 @@
 not in the WebView provider whitelist. Double-check the package name in your GN
 args. If you're on AOSP (any OS level), choose
 `"com.android.webview"`. If you're on L-M, choose
-`"com.android.google.webview"`. In either case, you'll likely need to [remove
+`"com.google.android.webview"`. In either case, you'll likely need to [remove
 the preinstalled WebView
 APK](/android_webview/tools/remove_preinstalled_webview.py).
 
diff --git a/ash/login/ui/parent_access_view.cc b/ash/login/ui/parent_access_view.cc
index a90eaf60..84e26c5 100644
--- a/ash/login/ui/parent_access_view.cc
+++ b/ash/login/ui/parent_access_view.cc
@@ -59,8 +59,8 @@
 constexpr int kParentAccessViewHeightDp = 340;
 constexpr int kParentAccessViewTabletModeHeightDp = 580;
 constexpr int kParentAccessViewRoundedCornerRadiusDp = 8;
-constexpr int kParentAccessViewVerticalInsetDp = 16;
-constexpr int kParentAccessViewHorizontalInsetDp = 26;
+constexpr int kParentAccessViewVerticalInsetDp = 24;
+constexpr int kParentAccessViewHorizontalInsetDp = 36;
 
 constexpr int kLockIconSizeDp = 24;
 
diff --git a/ash/shell/content/client/shell_browser_main_parts.cc b/ash/shell/content/client/shell_browser_main_parts.cc
index 88bf8194..513e88d7 100644
--- a/ash/shell/content/client/shell_browser_main_parts.cc
+++ b/ash/shell/content/client/shell_browser_main_parts.cc
@@ -33,7 +33,6 @@
 #include "content/public/browser/system_connector.h"
 #include "content/public/common/content_switches.h"
 #include "content/shell/browser/shell_browser_context.h"
-#include "content/shell/browser/shell_net_log.h"
 #include "device/bluetooth/dbus/bluez_dbus_manager.h"
 #include "net/base/net_module.h"
 #include "services/service_manager/public/cpp/connector.h"
@@ -67,9 +66,7 @@
 }
 
 void ShellBrowserMainParts::PreMainMessageLoopRun() {
-  net_log_.reset(new content::ShellNetLog("ash_shell"));
-  browser_context_.reset(
-      new content::ShellBrowserContext(false, net_log_.get()));
+  browser_context_.reset(new content::ShellBrowserContext(false));
 
   // A ViewsDelegate is required.
   if (!views::ViewsDelegate::GetInstance())
diff --git a/ash/shell/content/client/shell_browser_main_parts.h b/ash/shell/content/client/shell_browser_main_parts.h
index d5133255..181ca4b 100644
--- a/ash/shell/content/client/shell_browser_main_parts.h
+++ b/ash/shell/content/client/shell_browser_main_parts.h
@@ -15,10 +15,6 @@
 struct MainFunctionParams;
 }
 
-namespace net {
-class NetLog;
-}
-
 namespace views {
 class ViewsDelegate;
 }
@@ -52,7 +48,6 @@
   }
 
  private:
-  std::unique_ptr<net::NetLog> net_log_;
   std::unique_ptr<content::ShellBrowserContext> browser_context_;
   std::unique_ptr<views::ViewsDelegate> views_delegate_;
   std::unique_ptr<WindowWatcher> window_watcher_;
diff --git a/ash/system/unified/feature_pods_container_view.cc b/ash/system/unified/feature_pods_container_view.cc
index 3fe2433b..4b52c1a 100644
--- a/ash/system/unified/feature_pods_container_view.cc
+++ b/ash/system/unified/feature_pods_container_view.cc
@@ -87,17 +87,6 @@
          kUnifiedFeaturePodCollapsedSize.height();
 }
 
-void FeaturePodsContainerView::SaveFocus() {
-  const auto i = std::find_if(children().cbegin(), children().cend(),
-                              [](const auto* v) { return v->HasFocus(); });
-  focused_button_ = (i == children().cend()) ? nullptr : *i;
-}
-
-void FeaturePodsContainerView::RestoreFocus() {
-  if (focused_button_)
-    focused_button_->RequestFocus();
-}
-
 gfx::Size FeaturePodsContainerView::CalculatePreferredSize() const {
   return gfx::Size(
       kTrayMenuWidth,
diff --git a/ash/system/unified/feature_pods_container_view.h b/ash/system/unified/feature_pods_container_view.h
index c67844a..e41589a 100644
--- a/ash/system/unified/feature_pods_container_view.h
+++ b/ash/system/unified/feature_pods_container_view.h
@@ -43,11 +43,6 @@
   // Get the height of the view when |expanded_amount| is set to 0.0.
   int GetCollapsedHeight() const;
 
-  // Save and restore keyboard focus of a child feature pod button. If no button
-  // has focus or no focus is saved, these methods are no-op.
-  void SaveFocus();
-  void RestoreFocus();
-
   // Returns the number of children that prefer to be visible.
   int GetVisibleCount() const;
 
@@ -109,9 +104,6 @@
   // always false if FeaturePodsContainerView is not in the call stack.
   bool changing_visibility_ = false;
 
-  // A button that had focus at the point SaveButtonFocus is called.
-  views::View* focused_button_ = nullptr;
-
   // A view model that contains all visible feature pod buttons.
   // Used to calculate required number of pages.
   views::ViewModelT<FeaturePodButton> visible_buttons_;
diff --git a/ash/system/unified/unified_system_tray_controller.cc b/ash/system/unified/unified_system_tray_controller.cc
index 0098c71b..0a25cf3 100644
--- a/ash/system/unified/unified_system_tray_controller.cc
+++ b/ash/system/unified/unified_system_tray_controller.cc
@@ -295,7 +295,7 @@
   detailed_view_controller_.reset();
   unified_view_->ResetDetailedView();
   if (restore_focus)
-    unified_view_->RestoreFeaturePodFocus();
+    unified_view_->RestoreFocus();
 }
 
 void UnifiedSystemTrayController::CloseBubble() {
@@ -382,7 +382,7 @@
   animation_->Reset(1.0);
   UpdateExpandedAmount();
 
-  unified_view_->SaveFeaturePodFocus();
+  unified_view_->SaveFocus();
   views::FocusManager* manager = unified_view_->GetFocusManager();
   if (manager && manager->GetFocusedView())
     manager->ClearFocus();
diff --git a/ash/system/unified/unified_system_tray_view.cc b/ash/system/unified/unified_system_tray_view.cc
index ef7e06e..e6a48aff 100644
--- a/ash/system/unified/unified_system_tray_view.cc
+++ b/ash/system/unified/unified_system_tray_view.cc
@@ -338,12 +338,17 @@
   Layout();
 }
 
-void UnifiedSystemTrayView::SaveFeaturePodFocus() {
-  feature_pods_container_->SaveFocus();
+void UnifiedSystemTrayView::SaveFocus() {
+  auto* focus_manager = GetFocusManager();
+  if (!focus_manager)
+    return;
+
+  saved_focused_view_ = focus_manager->GetFocusedView();
 }
 
-void UnifiedSystemTrayView::RestoreFeaturePodFocus() {
-  feature_pods_container_->RestoreFocus();
+void UnifiedSystemTrayView::RestoreFocus() {
+  if (saved_focused_view_)
+    saved_focused_view_->RequestFocus();
 }
 
 void UnifiedSystemTrayView::SetExpandedAmount(double expanded_amount) {
diff --git a/ash/system/unified/unified_system_tray_view.h b/ash/system/unified/unified_system_tray_view.h
index 139e461..0394d6ca 100644
--- a/ash/system/unified/unified_system_tray_view.h
+++ b/ash/system/unified/unified_system_tray_view.h
@@ -80,9 +80,10 @@
   // It deletes |detailed_view| and children.
   void ResetDetailedView();
 
-  // Save and restore keyboard focus of feature pod.
-  void SaveFeaturePodFocus();
-  void RestoreFeaturePodFocus();
+  // Save and restore keyboard focus of the currently focused element. Called
+  // before transitioning into a detailed view.
+  void SaveFocus();
+  void RestoreFocus();
 
   // Change the expanded state. 0.0 if collapsed, and 1.0 if expanded.
   // Otherwise, it shows intermediate state.
@@ -164,6 +165,9 @@
   // The maximum height available to the view.
   int max_height_ = 0;
 
+  // The view that is saved by calling SaveFocus().
+  views::View* saved_focused_view_ = nullptr;
+
   const std::unique_ptr<FocusSearch> focus_search_;
   const std::unique_ptr<ui::EventHandler> interacted_by_tap_recorder_;
 
diff --git a/ash/wm/overview/overview_session.cc b/ash/wm/overview/overview_session.cc
index 47fd6d7..57bd1294 100644
--- a/ash/wm/overview/overview_session.cc
+++ b/ash/wm/overview/overview_session.cc
@@ -13,6 +13,7 @@
 #include "ash/metrics/user_metrics_recorder.h"
 #include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/shell_window_ids.h"
+#include "ash/public/cpp/window_properties.h"
 #include "ash/screen_util.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_constants.h"
@@ -20,6 +21,7 @@
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/wm/desks/desk.h"
 #include "ash/wm/desks/desks_controller.h"
+#include "ash/wm/desks/desks_util.h"
 #include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/overview/overview_delegate.h"
@@ -1032,6 +1034,9 @@
 }
 
 void OverviewSession::UpdateNoWindowsWidget() {
+  if (is_shutting_down_)
+    return;
+
   // Hide the widget if there is an item in overview.
   if (!IsEmpty()) {
     no_windows_widget_.reset();
@@ -1041,6 +1046,7 @@
   if (!no_windows_widget_) {
     // Create and fade in the widget.
     RoundedLabelWidget::InitParams params;
+    params.name = "OverviewNoWindowsLabel";
     params.horizontal_padding = kNoItemsIndicatorHorizontalPaddingDp;
     params.vertical_padding = kNoItemsIndicatorVerticalPaddingDp;
     params.background_color = kNoItemsIndicatorBackgroundColor;
@@ -1049,14 +1055,15 @@
     params.preferred_height = kNoItemsIndicatorHeightDp;
     params.message_id = IDS_ASH_OVERVIEW_NO_RECENT_ITEMS;
     params.parent = Shell::GetPrimaryRootWindow()->GetChildById(
-        kShellWindowId_AlwaysOnTopContainer);
+        desks_util::GetActiveDeskContainerId());
     no_windows_widget_ = std::make_unique<RoundedLabelWidget>();
     no_windows_widget_->Init(params);
 
     aura::Window* widget_window = no_windows_widget_->GetNativeWindow();
+    widget_window->SetProperty(kHideInDeskMiniViewKey, true);
+    widget_window->parent()->StackChildAtBottom(widget_window);
     ScopedOverviewAnimationSettings settings(OVERVIEW_ANIMATION_NO_RECENTS_FADE,
                                              widget_window);
-    widget_window->SetName("OverviewNoWindowsLabel");
     no_windows_widget_->SetOpacity(1.f);
   }
 
diff --git a/ash/wm/overview/rounded_label_widget.cc b/ash/wm/overview/rounded_label_widget.cc
index 8b98c68..0b851b0 100644
--- a/ash/wm/overview/rounded_label_widget.cc
+++ b/ash/wm/overview/rounded_label_widget.cc
@@ -69,12 +69,15 @@
 
 }  // namespace
 
+RoundedLabelWidget::InitParams::InitParams() = default;
+
 RoundedLabelWidget::RoundedLabelWidget() = default;
 
 RoundedLabelWidget::~RoundedLabelWidget() = default;
 
 void RoundedLabelWidget::Init(const InitParams& params) {
   views::Widget::InitParams widget_params;
+  widget_params.name = params.name;
   widget_params.type = views::Widget::InitParams::TYPE_POPUP;
   widget_params.keep_on_top = false;
   widget_params.ownership =
diff --git a/ash/wm/overview/rounded_label_widget.h b/ash/wm/overview/rounded_label_widget.h
index ba00b290..958e5472 100644
--- a/ash/wm/overview/rounded_label_widget.h
+++ b/ash/wm/overview/rounded_label_widget.h
@@ -5,6 +5,8 @@
 #ifndef ASH_WM_OVERVIEW_ROUNDED_LABEL_WIDGET_H_
 #define ASH_WM_OVERVIEW_ROUNDED_LABEL_WIDGET_H_
 
+#include <string>
+
 #include "base/macros.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/gfx/geometry/rect.h"
@@ -18,6 +20,8 @@
  public:
   // Params to modify the look of the label.
   struct InitParams {
+    InitParams();
+    std::string name;
     int horizontal_padding;
     int vertical_padding;
     SkColor background_color;
diff --git a/base/android/jni_generator/README.md b/base/android/jni_generator/README.md
index dd462d0d4..33a7008 100644
--- a/base/android/jni_generator/README.md
+++ b/base/android/jni_generator/README.md
@@ -154,13 +154,14 @@
 
 #### Testing Mockable Natives
 
-(Currently JniMocker only works for junit/robolectric tests, and is not yet
-ready for instrumentation tests.)
-
 1. Add the `JniMocker` rule to your test.
 2. Call `JniMocker#mock` in a `setUp()` method for each interface you want to
    stub out.
 
+Note: Mocking native methods doesn't work in tests that are part of APKs that
+use an [`apk_under_test`](https://cs.chromium.org/search/?q=file:BUILD.gn+%22apk_under_test+%3D%22&type=cs).
+[This crbug](http://crbug.com/890452) tracks removing the `apk_under_test` variable.
+
 JniMocker will reset the stubs during `tearDown()`.
 
 ```java
@@ -249,9 +250,3 @@
 
  * Python unit tests live in `jni_generator_tests.py`
  * A working demo app exists as `//base/android/jni_generator:sample_jni_apk`
-
-## Known Issues
-
-The `jni_generator` can cause build issues when source files that are used by
-JNI are moved due to how `include_dirs` are handled. This is tracked in
-[issue 964169](http://crbug.com/964169).
\ No newline at end of file
diff --git a/base/memory/fake_memory_pressure_monitor.cc b/base/memory/fake_memory_pressure_monitor.cc
index 713b161..59fd3ef6 100644
--- a/base/memory/fake_memory_pressure_monitor.cc
+++ b/base/memory/fake_memory_pressure_monitor.cc
@@ -20,7 +20,7 @@
 }
 
 base::MemoryPressureMonitor::MemoryPressureLevel
-FakeMemoryPressureMonitor::GetCurrentPressureLevel() {
+FakeMemoryPressureMonitor::GetCurrentPressureLevel() const {
   return memory_pressure_level_;
 }
 
diff --git a/base/memory/fake_memory_pressure_monitor.h b/base/memory/fake_memory_pressure_monitor.h
index 2194b5f8..d012876 100644
--- a/base/memory/fake_memory_pressure_monitor.h
+++ b/base/memory/fake_memory_pressure_monitor.h
@@ -19,7 +19,7 @@
   void SetAndNotifyMemoryPressure(MemoryPressureLevel level);
 
   // base::MemoryPressureMonitor overrides:
-  MemoryPressureLevel GetCurrentPressureLevel() override;
+  MemoryPressureLevel GetCurrentPressureLevel() const override;
   void SetDispatchCallback(const DispatchCallback& callback) override;
 
  private:
diff --git a/base/memory/memory_pressure_monitor.h b/base/memory/memory_pressure_monitor.h
index 13bc483..40d3415 100644
--- a/base/memory/memory_pressure_monitor.h
+++ b/base/memory/memory_pressure_monitor.h
@@ -40,7 +40,7 @@
   static const base::TimeDelta kUMAMemoryPressureLevelPeriod;
 
   // Returns the currently observed memory pressure.
-  virtual MemoryPressureLevel GetCurrentPressureLevel() = 0;
+  virtual MemoryPressureLevel GetCurrentPressureLevel() const = 0;
 
   // Sets a notification callback. The default callback invokes
   // base::MemoryPressureListener::NotifyMemoryPressure.
diff --git a/base/memory/memory_pressure_monitor_chromeos.cc b/base/memory/memory_pressure_monitor_chromeos.cc
index 2b4ff01c..a75d2be 100644
--- a/base/memory/memory_pressure_monitor_chromeos.cc
+++ b/base/memory/memory_pressure_monitor_chromeos.cc
@@ -149,7 +149,7 @@
 }
 
 MemoryPressureListener::MemoryPressureLevel
-MemoryPressureMonitor::GetCurrentPressureLevel() {
+MemoryPressureMonitor::GetCurrentPressureLevel() const {
   return current_memory_pressure_level_;
 }
 
diff --git a/base/memory/memory_pressure_monitor_chromeos.h b/base/memory/memory_pressure_monitor_chromeos.h
index 563ba85..722c1d6 100644
--- a/base/memory/memory_pressure_monitor_chromeos.h
+++ b/base/memory/memory_pressure_monitor_chromeos.h
@@ -59,7 +59,7 @@
 
   // Get the current memory pressure level.
   MemoryPressureListener::MemoryPressureLevel GetCurrentPressureLevel()
-      override;
+      const override;
   void SetDispatchCallback(const DispatchCallback& callback) override;
 
   // Returns a type-casted version of the current memory pressure monitor. A
diff --git a/base/memory/memory_pressure_monitor_mac.cc b/base/memory/memory_pressure_monitor_mac.cc
index a91e52a..bd829fcf 100644
--- a/base/memory/memory_pressure_monitor_mac.cc
+++ b/base/memory/memory_pressure_monitor_mac.cc
@@ -165,7 +165,7 @@
 }
 
 MemoryPressureListener::MemoryPressureLevel
-MemoryPressureMonitor::GetCurrentPressureLevel() {
+MemoryPressureMonitor::GetCurrentPressureLevel() const {
   return last_pressure_level_;
 }
 
diff --git a/base/memory/memory_pressure_monitor_mac.h b/base/memory/memory_pressure_monitor_mac.h
index b85b6c90..b98a5c3e 100644
--- a/base/memory/memory_pressure_monitor_mac.h
+++ b/base/memory/memory_pressure_monitor_mac.h
@@ -29,7 +29,7 @@
   ~MemoryPressureMonitor() override;
 
   // Returns the currently-observed memory pressure.
-  MemoryPressureLevel GetCurrentPressureLevel() override;
+  MemoryPressureLevel GetCurrentPressureLevel() const override;
 
   void SetDispatchCallback(const DispatchCallback& callback) override;
 
diff --git a/base/memory/memory_pressure_monitor_notifying_chromeos.cc b/base/memory/memory_pressure_monitor_notifying_chromeos.cc
index 74e96c0..cb0eeb5 100644
--- a/base/memory/memory_pressure_monitor_notifying_chromeos.cc
+++ b/base/memory/memory_pressure_monitor_notifying_chromeos.cc
@@ -203,7 +203,7 @@
 }
 
 MemoryPressureListener::MemoryPressureLevel
-MemoryPressureMonitorNotifying::GetCurrentPressureLevel() {
+MemoryPressureMonitorNotifying::GetCurrentPressureLevel() const {
   return current_memory_pressure_level_;
 }
 
diff --git a/base/memory/memory_pressure_monitor_notifying_chromeos.h b/base/memory/memory_pressure_monitor_notifying_chromeos.h
index 5af176f..6761cc5 100644
--- a/base/memory/memory_pressure_monitor_notifying_chromeos.h
+++ b/base/memory/memory_pressure_monitor_notifying_chromeos.h
@@ -42,7 +42,7 @@
 
   // Get the current memory pressure level.
   MemoryPressureListener::MemoryPressureLevel GetCurrentPressureLevel()
-      override;
+      const override;
   void SetDispatchCallback(const DispatchCallback& callback) override;
 
   // GetMarginFileParts returns a vector of the configured margin file values.
diff --git a/base/memory/memory_pressure_monitor_win.cc b/base/memory/memory_pressure_monitor_win.cc
index 34a3d71..f2ccb7d7 100644
--- a/base/memory/memory_pressure_monitor_win.cc
+++ b/base/memory/memory_pressure_monitor_win.cc
@@ -88,7 +88,7 @@
 }
 
 MemoryPressureListener::MemoryPressureLevel
-MemoryPressureMonitor::GetCurrentPressureLevel() {
+MemoryPressureMonitor::GetCurrentPressureLevel() const {
   return current_memory_pressure_level_;
 }
 
diff --git a/base/memory/memory_pressure_monitor_win.h b/base/memory/memory_pressure_monitor_win.h
index 6a7d0bb8..6d9df60 100644
--- a/base/memory/memory_pressure_monitor_win.h
+++ b/base/memory/memory_pressure_monitor_win.h
@@ -55,7 +55,7 @@
   void CheckMemoryPressureSoon();
 
   // Get the current memory pressure level. This can be called from any thread.
-  MemoryPressureLevel GetCurrentPressureLevel() override;
+  MemoryPressureLevel GetCurrentPressureLevel() const override;
   void SetDispatchCallback(const DispatchCallback& callback) override;
 
   // Returns the moderate pressure level free memory threshold, in MB.
diff --git a/base/metrics/field_trial_unittest.cc b/base/metrics/field_trial_unittest.cc
index c542bcf..f925fbc 100644
--- a/base/metrics/field_trial_unittest.cc
+++ b/base/metrics/field_trial_unittest.cc
@@ -1212,10 +1212,8 @@
 }
 
 TEST(FieldTrialListTest, InstantiateAllocator) {
-  test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.Init();
-
   FieldTrialList field_trial_list(nullptr);
+
   FieldTrialList::CreateFieldTrial("Trial1", "Group1");
 
   FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded();
@@ -1240,7 +1238,6 @@
     test::ScopedFeatureList scoped_feature_list;
     scoped_feature_list.Init();
 
-    FieldTrialList field_trial_list(nullptr);
     FieldTrialList::CreateFieldTrial("Trial1", "Group1");
     FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded();
     FieldTrialList::AllStatesToString(&save_string, false);
@@ -1267,7 +1264,6 @@
 
     // Create a simulated trial and a real trial and call group() on them, which
     // should only add the real trial to the field trial allocator.
-    FieldTrialList field_trial_list(nullptr);
     FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded();
 
     // This shouldn't add to the allocator.
@@ -1304,7 +1300,6 @@
   std::string group_name("Group1");
 
   // Create a field trial with some params.
-  FieldTrialList field_trial_list(nullptr);
   FieldTrialList::CreateFieldTrial(trial_name, group_name);
   std::map<std::string, std::string> params;
   params["key1"] = "value1";
@@ -1346,7 +1341,6 @@
     scoped_feature_list.Init();
 
     // Create a field trial with some params.
-    FieldTrialList field_trial_list(nullptr);
     FieldTrial* trial =
         FieldTrialList::CreateFieldTrial(trial_name, group_name);
     std::map<std::string, std::string> params;
diff --git a/base/test/BUILD.gn b/base/test/BUILD.gn
index 5632c71e..fe1864b 100644
--- a/base/test/BUILD.gn
+++ b/base/test/BUILD.gn
@@ -53,7 +53,6 @@
     "copy_only_int.h",
     "do_nothing_promise.cc",
     "do_nothing_promise.h",
-    "fuzzed_data_provider.h",
     "gmock_callback_support.h",
     "gtest_util.cc",
     "gtest_util.h",
diff --git a/base/test/fuzzed_data_provider.h b/base/test/fuzzed_data_provider.h
deleted file mode 100644
index 33d1c00..0000000
--- a/base/test/fuzzed_data_provider.h
+++ /dev/null
@@ -1,206 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_TEST_FUZZED_DATA_PROVIDER_H_
-#define BASE_TEST_FUZZED_DATA_PROVIDER_H_
-
-#include <limits.h>
-#include <stddef.h>
-#include <stdint.h>
-
-#include <algorithm>
-#include <cstring>
-#include <string>
-#include <type_traits>
-#include <utility>
-#include <vector>
-
-// A single-header library providing an utility class to break up an array of
-// bytes (supposedly provided by a fuzzing engine) for multiple consumers.
-// Whenever run on the same input, provides the same output, as long as its
-// methods are called in the same order, with the same arguments.
-
-// TODO(mmoroz): move this out of //base as it should be an independent library.
-namespace base {
-
-class FuzzedDataProvider {
- public:
-  typedef uint8_t data_type;
-
-  // |data| is an array of length |size| that the FuzzedDataProvider wraps to
-  // provide more granular access. |data| must outlive the FuzzedDataProvider.
-  FuzzedDataProvider(const uint8_t* data, size_t size)
-      : data_ptr_(data), remaining_bytes_(size) {}
-  ~FuzzedDataProvider() = default;
-
-  // Returns a std::vector containing |num_bytes| of input data. If fewer than
-  // |num_bytes| of data remain, returns a shorter std::vector containing all
-  // of the data that's left.
-  template <typename T = data_type>
-  std::vector<T> ConsumeBytes(size_t num_bytes) {
-    static_assert(sizeof(T) == sizeof(data_type), "Incompatible data type.");
-
-    num_bytes = std::min(num_bytes, remaining_bytes_);
-
-    // The point of using the size-based constructor below is to increase the
-    // odds of having a vector object with capacity being equal to the length.
-    // That part is always implementation specific, but at least both libc++ and
-    // libstdc++ allocate the requested number of bytes in that constructor,
-    // which seems to be a natual choice for other implementations as well.
-    // To increase the odds even more, we also call |shrink_to_fit| below.
-    std::vector<T> result(num_bytes);
-    std::memcpy(result.data(), data_ptr_, num_bytes);
-    Advance(num_bytes);
-
-    // Even though |shrink_to_fit| is also implementation specific, we expect it
-    // to provide an additional assurance in case vector's constructor allocated
-    // a buffer which is larger than the actual amount of data we put inside it.
-    result.shrink_to_fit();
-    return result;
-  }
-
-  // Prefer using |ConsumeBytes| unless you actually need a std::string object.
-  // Returns a std::string containing |num_bytes| of input data. If fewer than
-  // |num_bytes| of data remain, returns a shorter std::string containing all
-  // of the data that's left.
-  std::string ConsumeBytesAsString(size_t num_bytes) {
-    static_assert(sizeof(std::string::value_type) == sizeof(data_type),
-                  "ConsumeBytesAsString cannot convert the data to a string.");
-
-    num_bytes = std::min(num_bytes, remaining_bytes_);
-    std::string result(
-        reinterpret_cast<const std::string::value_type*>(data_ptr_), num_bytes);
-    Advance(num_bytes);
-    return result;
-  }
-
-  // Returns a number in the range [min, max] by consuming bytes from the input
-  // data. The value might not be uniformly distributed in the given range. If
-  // there's no input data left, always returns |min|. |min| must be less than
-  // or equal to |max|.
-  template <typename T>
-  T ConsumeIntegralInRange(T min, T max) {
-    static_assert(std::is_integral<T>::value, "An integral type is required.");
-    static_assert(sizeof(T) <= sizeof(uint64_t), "Unsupported integral type.");
-
-    if (min > max)
-      abort();
-
-    // Use the biggest type possible to hold the range and the result.
-    uint64_t range = static_cast<uint64_t>(max) - min;
-    uint64_t result = 0;
-    size_t offset = 0;
-
-    while (offset < sizeof(T) * CHAR_BIT && (range >> offset) > 0 &&
-           remaining_bytes_ != 0) {
-      // Pull bytes off the end of the seed data. Experimentally, this seems to
-      // allow the fuzzer to more easily explore the input space. This makes
-      // sense, since it works by modifying inputs that caused new code to run,
-      // and this data is often used to encode length of data read by
-      // |ConsumeBytes|. Separating out read lengths makes it easier modify the
-      // contents of the data that is actually read.
-      --remaining_bytes_;
-      result = (result << CHAR_BIT) | data_ptr_[remaining_bytes_];
-      offset += CHAR_BIT;
-    }
-
-    // Avoid division by 0, in the case |range + 1| results in overflow.
-    if (range != std::numeric_limits<decltype(range)>::max())
-      result = result % (range + 1);
-
-    return static_cast<T>(min + result);
-  }
-
-  // Returns a std::string of length from 0 to |max_length|. When it runs out of
-  // input data, returns what remains of the input. Designed to be more stable
-  // with respect to a fuzzer inserting characters than just picking a random
-  // length and then consuming that many bytes with |ConsumeBytes|.
-  std::string ConsumeRandomLengthString(size_t max_length) {
-    // Reads bytes from the start of |data_ptr_|. Maps "\\" to "\", and maps "\"
-    // followed by anything else to the end of the string. As a result of this
-    // logic, a fuzzer can insert characters into the string, and the string
-    // will be lengthened to include those new characters, resulting in a more
-    // stable fuzzer than picking the length of a string independently from
-    // picking its contents.
-    std::string result;
-    for (size_t i = 0; i < max_length && remaining_bytes_ != 0; ++i) {
-      char next = static_cast<char>(data_ptr_[0]);
-      Advance(1);
-      if (next == '\\' && remaining_bytes_ != 0) {
-        next = static_cast<char>(data_ptr_[0]);
-        Advance(1);
-        if (next != '\\')
-          return result;
-      }
-      result += next;
-    }
-
-    result.shrink_to_fit();
-    return result;
-  }
-
-  // Returns a std::vector containing all remaining bytes of the input data.
-  template <typename T = data_type>
-  std::vector<T> ConsumeRemainingBytes() {
-    return ConsumeBytes<T>(remaining_bytes_);
-  }
-
-  // Prefer using |ConsumeRemainingBytes| unless you actually need a std::string
-  // object.
-  // Returns a std::vector containing all remaining bytes of the input data.
-  std::string ConsumeRemainingBytesAsString() {
-    return ConsumeBytesAsString(remaining_bytes_);
-  }
-
-  // Returns a number in the range [Type's min, Type's max]. The value might
-  // not be uniformly distributed in the given range. If there's no input data
-  // left, always returns |min|.
-  template <typename T>
-  T ConsumeIntegral() {
-    return ConsumeIntegralInRange(std::numeric_limits<T>::min(),
-                                  std::numeric_limits<T>::max());
-  }
-
-  // Reads one byte and returns a bool, or false when no data remains.
-  bool ConsumeBool() { return 1 & ConsumeIntegral<uint8_t>(); }
-
-  // Returns a value from |array|, consuming as many bytes as needed to do so.
-  // |array| must be a fixed-size array.
-  template <typename T, size_t size>
-  T PickValueInArray(T (&array)[size]) {
-    return array[ConsumeIntegralInRange<size_t>(0, size - 1)];
-  }
-
-  // Return an enum value. The enum must start at 0 and be contiguous. It must
-  // also contain kMaxValue aliased to its largest (inclusive) value. Such as:
-  // enum class Foo { SomeValue, OtherValue, kMaxValue = OtherValue };
-  template <typename T>
-  T ConsumeEnum() {
-    static_assert(std::is_enum<T>::value, "|T| must be an enum type.");
-    return static_cast<T>(ConsumeIntegralInRange<uint32_t>(
-        0, static_cast<uint32_t>(T::kMaxValue)));
-  }
-
-  // Reports the remaining bytes available for fuzzed input.
-  size_t remaining_bytes() { return remaining_bytes_; }
-
- private:
-  FuzzedDataProvider(const FuzzedDataProvider&) = delete;
-  FuzzedDataProvider& operator=(const FuzzedDataProvider&) = delete;
-
-  void Advance(size_t num_bytes) {
-    if (num_bytes > remaining_bytes_)
-      abort();
-
-    data_ptr_ += num_bytes;
-    remaining_bytes_ -= num_bytes;
-  }
-
-  const data_type* data_ptr_;
-  size_t remaining_bytes_;
-};
-
-}  // namespace base
-
-#endif  // BASE_TEST_FUZZED_DATA_PROVIDER_H_
diff --git a/base/test/scoped_feature_list.cc b/base/test/scoped_feature_list.cc
index e855543c..e23a5579 100644
--- a/base/test/scoped_feature_list.cc
+++ b/base/test/scoped_feature_list.cc
@@ -150,9 +150,7 @@
 }
 
 void ScopedFeatureList::Init() {
-  std::unique_ptr<FeatureList> feature_list(new FeatureList);
-  feature_list->InitializeFromCommandLine(std::string(), std::string());
-  InitWithFeatureList(std::move(feature_list));
+  InitWithFeaturesImpl({}, {}, {});
 }
 
 void ScopedFeatureList::InitWithFeatureList(
diff --git a/base/test/scoped_feature_list.h b/base/test/scoped_feature_list.h
index 1402c85..586deed 100644
--- a/base/test/scoped_feature_list.h
+++ b/base/test/scoped_feature_list.h
@@ -51,10 +51,9 @@
   // Resets the instance to a non-initialized state.
   void Reset();
 
-  // WARNING: This method will reset any globally configured features to their
-  // default values, which can hide feature interaction bugs. Please use
-  // sparingly.  https://crbug.com/713390
-  // Initializes and registers a FeatureList instance with no overrides.
+  // Initializes and registers a FeatureList instance without any additional
+  // enabled or disabled features. Existing state, if any, will be kept. This is
+  // equivalent to calling InitWithFeatures({}, {}).
   void Init();
 
   // WARNING: This method will reset any globally configured features to their
diff --git a/base/threading/sequence_bound.h b/base/threading/sequence_bound.h
index df8e818..37b5b524 100644
--- a/base/threading/sequence_bound.h
+++ b/base/threading/sequence_bound.h
@@ -234,8 +234,8 @@
 
   // Run on impl thread to construct |t|'s storage.
   template <typename... Args>
-  static void ConstructOwnerRecord(T* t, Args&&... args) {
-    new (t) T(std::forward<Args>(args)...);
+  static void ConstructOwnerRecord(T* t, std::decay_t<Args>&&... args) {
+    new (t) T(std::move(args)...);
   }
 
   // Destruct the object associated with |t|, and delete |storage|.
diff --git a/base/threading/sequence_bound_unittest.cc b/base/threading/sequence_bound_unittest.cc
index 4514573..b571c8d 100644
--- a/base/threading/sequence_bound_unittest.cc
+++ b/base/threading/sequence_bound_unittest.cc
@@ -323,4 +323,25 @@
                 "|VirtuallyDerived shouldn't be a virtual base of |Base|");
 }
 
+TEST_F(SequenceBoundTest, LvalueConstructionParameter) {
+  // Note here that |value_ptr| is an lvalue, while |&value| would be an rvalue.
+  Value value = kInitialValue;
+  Value* value_ptr = &value;
+  SequenceBound<Derived> derived(task_runner_, value_ptr);
+  {
+    derived.Post(FROM_HERE, &Derived::SetValue, kDifferentValue);
+    base::RunLoop run_loop;
+    task_runner_->PostTask(FROM_HERE, run_loop.QuitClosure());
+    run_loop.Run();
+    EXPECT_EQ(value, kDifferentValue);
+  }
+  {
+    derived.Reset();
+    base::RunLoop run_loop;
+    task_runner_->PostTask(FROM_HERE, run_loop.QuitClosure());
+    run_loop.Run();
+    EXPECT_EQ(value, kDerivedDtorValue);
+  }
+}
+
 }  // namespace base
diff --git a/build/android/apk_operations.py b/build/android/apk_operations.py
index 78400c7..7395d2c 100755
--- a/build/android/apk_operations.py
+++ b/build/android/apk_operations.py
@@ -692,24 +692,26 @@
 
   def _ParseLine(self, line):
     tokens = line.split(None, 6)
-    date = tokens[0]
-    invokation_time = tokens[1]
-    pid = int(tokens[2])
-    tid = int(tokens[3])
-    priority = tokens[4]
-    tag = tokens[5]
-    if len(tokens) > 6:
-      original_message = tokens[6]
-    else:  # Empty log message
-      original_message = ''
+
+    def consume_token_or_default(default):
+      return tokens.pop(0) if len(tokens) > 0 else default
+
+    date = consume_token_or_default('')
+    invokation_time = consume_token_or_default('')
+    pid = int(consume_token_or_default(-1))
+    tid = int(consume_token_or_default(-1))
+    priority = consume_token_or_default('')
+    tag = consume_token_or_default('')
+    original_message = consume_token_or_default('')
+
     # Example:
     #   09-19 06:35:51.113  9060  9154 W GCoreFlp: No location...
     #   09-19 06:01:26.174  9060 10617 I Auth    : [ReflectiveChannelBinder]...
     # Parsing "GCoreFlp:" vs "Auth    :", we only want tag to contain the word,
     # and we don't want to keep the colon for the message.
-    if tag[-1] == ':':
+    if tag and tag[-1] == ':':
       tag = tag[:-1]
-    else:
+    elif len(original_message) > 2:
       original_message = original_message[2:]
     return self.ParsedLine(
         date, invokation_time, pid, tid, priority, tag, original_message)
diff --git a/build/chromeos/test_runner.py b/build/chromeos/test_runner.py
index f4e2f3c..147221d 100755
--- a/build/chromeos/test_runner.py
+++ b/build/chromeos/test_runner.py
@@ -64,10 +64,10 @@
 
     # /home is mounted with "noexec" in the device, but some of our tools
     # and tests use the home dir as a workspace (eg: vpython downloads
-    # python binaries to ~/.vpython-root). /tmp doesn't have this
+    # python binaries to ~/.vpython-root). /usr/local/tmp doesn't have this
     # restriction, so change the location of the home dir for the
     # duration of the test.
-    'export HOME=/tmp',
+    'export HOME=/usr/local/tmp',
   ]
 
   def __init__(self, args, unknown_args):
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index 70f4929..57befd8f 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -370,6 +370,12 @@
   #   header_output: Path to the generated .h file (optional).
   #   sources_blacklist: List of .java files that should be skipped. (optional)
   #   namespace: Registration functions will be wrapped into this. (optional)
+  #   require_native_mocks: Enforce that any native calls using
+  #     org.chromium.base.annotations.NativeMethods must have a mock set
+  #     (optional).
+  #   enable_native_mocks: Allow native calls using
+  #     org.chromium.base.annotations.NativeMethods to be mocked in tests
+  #     (optional).
   #
   # Example
   #   generate_jni_registration("chrome_jni_registration") {
@@ -414,11 +420,11 @@
 
       if (defined(invoker.enable_native_mocks) && invoker.enable_native_mocks) {
         args += [ "--enable_proxy_mocks" ]
-      }
 
-      if (defined(invoker.require_native_mocks) &&
-          invoker.require_native_mocks) {
-        args += [ "--require_mocks" ]
+        if (defined(invoker.require_native_mocks) &&
+            invoker.require_native_mocks) {
+          args += [ "--require_mocks" ]
+        }
       }
 
       if (defined(invoker.header_output)) {
@@ -2029,6 +2035,12 @@
   #     Optional, default 23.
   #   max_sdk_version: The maximum Android SDK version this target supports.
   #     Optional, default not set.
+  #   require_native_mocks: Enforce that any native calls using
+  #     org.chromium.base.annotations.NativeMethods must have a mock set
+  #     (optional).
+  #   enable_native_mocks: Allow native calls using
+  #     org.chromium.base.annotations.NativeMethods to be mocked in tests
+  #     (optional).
   template("android_apk_or_module") {
     forward_variables_from(invoker, [ "testonly" ])
     assert(defined(invoker.final_apk_path) || defined(invoker.name))
@@ -2639,6 +2651,11 @@
 
     if (_generate_final_jni) {
       generate_jni_registration("${_template_name}__final_jni") {
+        forward_variables_from(invoker,
+                               [
+                                 "enable_native_mocks",
+                                 "require_native_mocks",
+                               ])
         target = ":$_template_name"
         if (defined(invoker.jni_registration_header)) {
           header_output = invoker.jni_registration_header
@@ -3268,6 +3285,7 @@
                                "dont_load_shared_libraries",
                                "enable_chromium_linker_tests",
                                "enable_multidex",
+                               "enable_native_mocks",
                                "final_apk_path",
                                "generate_buildconfig_java",
                                "generate_final_jni",
@@ -3302,6 +3320,7 @@
                                "resource_blacklist_regex",
                                "resource_ids_provider_dep",
                                "resources_config_path",
+                               "require_native_mocks",
                                "secondary_abi_loadable_modules",
                                "secondary_abi_shared_libraries",
                                "secondary_native_lib_placeholders",
@@ -3595,6 +3614,8 @@
       ]
       if (defined(invoker.apk_under_test)) {
         data_deps += [ invoker.apk_under_test ]
+      } else {
+        enable_native_mocks = true
       }
       if (defined(invoker.additional_apks)) {
         data_deps += invoker.additional_apks
diff --git a/build/config/compiler/compiler.gni b/build/config/compiler/compiler.gni
index 9bcc5d2e..7d71a12 100644
--- a/build/config/compiler/compiler.gni
+++ b/build/config/compiler/compiler.gni
@@ -76,6 +76,11 @@
   # It's currently not possible to collect AFDO profiles on anything but
   # x86{,_64}.
   using_mismatched_sample_profile = current_cpu != "x64" && current_cpu != "x86"
+
+  # Whether an error should be raised on attempts to make debug builds with
+  # is_component_build=false. Very large debug symbols can have unwanted side
+  # effects so this is enforced by default for chromium.
+  forbid_non_component_debug_builds = build_with_chromium
 }
 
 assert(!is_cfi || use_thin_lto, "CFI requires ThinLTO")
@@ -232,8 +237,8 @@
     # info or variable info, so we can leave that out to speed up the build.
     # Sanitizers also require symbols for filename suppressions to work.
     symbol_level = 1
-  } else if ((!is_nacl && !is_linux && !is_fuchsia && current_os != "aix") || is_debug ||
-             is_official_build || is_chromecast) {
+  } else if ((!is_nacl && !is_linux && !is_fuchsia && current_os != "aix") ||
+             is_debug || is_official_build || is_chromecast) {
     # Linux builds slower by having symbols as part of the target binary,
     # whereas Mac and Windows have them separate, so in Release Linux, default
     # them off, but keep them on for Official builds and Chromecast builds.
@@ -256,7 +261,7 @@
 # because the is_component_build flag is set to false in various components of
 # the build (like nacl) and we don't want to assert on those.
 # iOS does not support component builds so add an exception for this platform.
-if (build_with_chromium) {
+if (forbid_non_component_debug_builds) {
   assert(symbol_level != 2 || current_toolchain != default_toolchain ||
              is_component_build || !is_debug || is_ios,
          "Can't do non-component debug builds at symbol_level=2")
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 9522b9c..db96dbf 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8909064476425739776
\ No newline at end of file
+8909016706543512784
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 2b8b660a..d5d8184 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-8909076652554121136
\ No newline at end of file
+8909064474621102512
\ No newline at end of file
diff --git a/cc/tiles/software_image_decode_cache_utils.h b/cc/tiles/software_image_decode_cache_utils.h
index 835d68c..3293d8b4 100644
--- a/cc/tiles/software_image_decode_cache_utils.h
+++ b/cc/tiles/software_image_decode_cache_utils.h
@@ -50,12 +50,11 @@
     bool operator==(const CacheKey& other) const {
       // The frame_key always has to be the same. However, after that all
       // original decodes are the same, so if we can use the original decode,
-      // return true. If not, then we have to compare every field. Note we don't
-      // compare |nearest_neighbor_| because we would only use kOriginal type in
-      // that case (dchecked below), which implies no scale. The returned scale
-      // to Skia would respect the nearest neighbor value of the requested
-      // image.
-      DCHECK(!is_nearest_neighbor_ || type_ == kOriginal);
+      // return true. If not, then we have to compare every field.
+      // |nearest_neighbor_| is not compared below since it is not used for
+      // scaled decodes and does not affect the contents of the cache entry
+      // (just passed to skia for the filtering to be done at raster time).
+      DCHECK(!is_nearest_neighbor_ || type_ != kSubrectAndScale);
       return frame_key_ == other.frame_key_ && type_ == other.type_ &&
              target_color_space_ == other.target_color_space_ &&
              (type_ == kOriginal || (src_rect_ == other.src_rect_ &&
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index a59bace..515cc0e 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -1321,7 +1321,7 @@
     if (is_component_build) {
       ldflags += [
         "-rpath",
-        "@loader_path/../../../../../../../..",
+        "@loader_path/../../../../../..",
         "-Wl,-reexport_library,libchrome_dll.dylib",
       ]
 
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 0c5f78e9..2c7b24ec 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -2612,8 +2612,8 @@
     "java/src/org/chromium/chrome/browser/offlinepages/BackgroundSchedulerBridge.java",
     "java/src/org/chromium/chrome/browser/offlinepages/CCTRequestStatus.java",
     "java/src/org/chromium/chrome/browser/offlinepages/CctOfflinePageModelObserver.java",
+    "java/src/org/chromium/chrome/browser/offlinepages/OfflinePageArchivePublisherBridge.java",
     "java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java",
-    "java/src/org/chromium/chrome/browser/offlinepages/OfflinePagesDownloadManagerBridge.java",
     "java/src/org/chromium/chrome/browser/offlinepages/PublishPageCallback.java",
     "java/src/org/chromium/chrome/browser/offlinepages/RequestCoordinatorBridge.java",
     "java/src/org/chromium/chrome/browser/offlinepages/SavePageRequest.java",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index c9c5753..bffe389 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -1002,12 +1002,12 @@
   "java/src/org/chromium/chrome/browser/offlinepages/DeletedPageInfo.java",
   "java/src/org/chromium/chrome/browser/offlinepages/GetPagesByNamespaceForLivePageSharingCallback.java",
   "java/src/org/chromium/chrome/browser/offlinepages/OfflineBackgroundTask.java",
+  "java/src/org/chromium/chrome/browser/offlinepages/OfflinePageArchivePublisherBridge.java",
   "java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java",
   "java/src/org/chromium/chrome/browser/offlinepages/OfflinePageItem.java",
   "java/src/org/chromium/chrome/browser/offlinepages/OfflinePageOrigin.java",
   "java/src/org/chromium/chrome/browser/offlinepages/OfflinePageTabObserver.java",
   "java/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtils.java",
-  "java/src/org/chromium/chrome/browser/offlinepages/OfflinePagesDownloadManagerBridge.java",
   "java/src/org/chromium/chrome/browser/offlinepages/PublishPageCallback.java",
   "java/src/org/chromium/chrome/browser/offlinepages/RequestCoordinatorBridge.java",
   "java/src/org/chromium/chrome/browser/offlinepages/SavePageAndShareCallback.java",
diff --git a/chrome/android/features/tab_ui/BUILD.gn b/chrome/android/features/tab_ui/BUILD.gn
index a4c49ae..e6d9578 100644
--- a/chrome/android/features/tab_ui/BUILD.gn
+++ b/chrome/android/features/tab_ui/BUILD.gn
@@ -63,6 +63,7 @@
     "java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripToolbarViewProperties.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripViewBinder.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripViewHolder.java",
+    "java/src/org/chromium/chrome/browser/tasks/tab_management/UndoGroupSnackbarController.java",
   ]
 
   deps = [
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherCoordinator.java
index 14f44cf8..f623d9cd 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherCoordinator.java
@@ -21,6 +21,7 @@
 import org.chromium.chrome.browser.gesturenav.HistoryNavigationLayout;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.chrome.browser.snackbar.SnackbarManager;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabCreatorManager;
 import org.chromium.chrome.browser.tabmodel.TabList;
@@ -47,6 +48,7 @@
     private final MultiThumbnailCardProvider mMultiThumbnailCardProvider;
     private final TabGridDialogCoordinator mTabGridDialogCoordinator;
     private final TabSelectionEditorCoordinator mTabSelectionEditorCoordinator;
+    private final UndoGroupSnackbarController mUndoGroupSnackbarController;
 
     private final MenuOrKeyboardActionController
             .MenuOrKeyboardActionHandler mGridTabSwitcherMenuActionHandler =
@@ -66,7 +68,8 @@
             ActivityLifecycleDispatcher lifecycleDispatcher, TabModelSelector tabModelSelector,
             TabContentManager tabContentManager, CompositorViewHolder compositorViewHolder,
             ChromeFullscreenManager fullscreenManager, TabCreatorManager tabCreatorManager,
-            MenuOrKeyboardActionController menuOrKeyboardActionController, Runnable backPress) {
+            MenuOrKeyboardActionController menuOrKeyboardActionController, Runnable backPress,
+            SnackbarManager.SnackbarManageable snackbarManageable) {
         PropertyModel containerViewModel = new PropertyModel(TabListContainerProperties.ALL_KEYS);
 
         mTabSelectionEditorCoordinator = new TabSelectionEditorCoordinator(
@@ -84,6 +87,9 @@
                     mTabSelectionEditorCoordinator.getController());
 
             gridCardOnClickListenerProvider = mMediator::getGridCardOnClickListener;
+
+            mUndoGroupSnackbarController =
+                    new UndoGroupSnackbarController(context, tabModelSelector, snackbarManageable);
         } else {
             mTabGridDialogCoordinator = null;
 
@@ -92,6 +98,7 @@
                     mTabSelectionEditorCoordinator.getController());
 
             gridCardOnClickListenerProvider = null;
+            mUndoGroupSnackbarController = null;
         }
 
         mMultiThumbnailCardProvider =
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
index 96e66ee..da201e71 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
@@ -420,6 +420,8 @@
                 @Override
                 public void didMoveWithinGroup(
                         Tab movedTab, int tabModelOldIndex, int tabModelNewIndex) {
+                    if (tabModelNewIndex == tabModelOldIndex) return;
+
                     int curPosition = mModel.indexFromId(movedTab.getId());
                     TabModel tabModel = mTabModelSelector.getCurrentModel();
 
@@ -449,8 +451,12 @@
                     }
                     if (mActionsOnAllRelatedTabs) {
                         Tab currentSelectedTab = mTabModelSelector.getCurrentTab();
-                        addTabInfoToModel(movedTab, mModel.size(),
-                                currentSelectedTab.getId() == movedTab.getId());
+                        int index = TabModelUtils.getTabIndexById(
+                                mTabModelSelector.getTabModelFilterProvider()
+                                        .getCurrentTabModelFilter(),
+                                movedTab.getId());
+                        addTabInfoToModel(
+                                movedTab, index, currentSelectedTab.getId() == movedTab.getId());
                         boolean isSelected = mTabModelSelector.getCurrentTabId()
                                 == filter.getTabAt(prevFilterIndex).getId();
                         updateTab(prevFilterIndex, filter.getTabAt(prevFilterIndex), isSelected,
@@ -481,7 +487,7 @@
                 @Override
                 public void didMoveTabGroup(
                         Tab movedTab, int tabModelOldIndex, int tabModelNewIndex) {
-                    if (!mActionsOnAllRelatedTabs) return;
+                    if (!mActionsOnAllRelatedTabs || tabModelNewIndex == tabModelOldIndex) return;
                     TabGroupModelFilter filter =
                             (TabGroupModelFilter) mTabModelSelector.getTabModelFilterProvider()
                                     .getCurrentTabModelFilter();
@@ -518,6 +524,10 @@
 
                     mModel.move(curPosition, newPosition);
                 }
+
+                @Override
+                public void didCreateGroup(
+                        List<Tab> tabs, List<Integer> tabOriginalIndex, boolean isSameGroup) {}
             };
 
             ((TabGroupModelFilter) mTabModelSelector.getTabModelFilterProvider().getTabModelFilter(
@@ -598,7 +608,7 @@
         sTabClosedFromMapTabClosedFromMap.put(tabId, TabClosedFrom.GRID_TAB_SWITCHER_GROUP);
     }
 
-    public void setActionOnAllRelatedTabsForTest(boolean actionOnAllRelatedTabs) {
+    void setActionOnAllRelatedTabsForTest(boolean actionOnAllRelatedTabs) {
         mActionsOnAllRelatedTabs = actionOnAllRelatedTabs;
     }
 
@@ -609,9 +619,10 @@
     }
 
     private void onTabAdded(Tab tab, boolean onlyShowRelatedTabs) {
-        List<Tab> related = getRelatedTabsForId(tab.getId());
         int index;
         if (onlyShowRelatedTabs) {
+            if (mModel.size() == 0) return;
+            List<Tab> related = getRelatedTabsForId(mModel.get(0).get(TabProperties.TAB_ID));
             index = related.indexOf(tab);
             if (index == -1) return;
         } else {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegateImpl.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegateImpl.java
index ee063ae..dae9029 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegateImpl.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegateImpl.java
@@ -39,7 +39,7 @@
         return new GridTabSwitcherCoordinator(activity, activity.getLifecycleDispatcher(),
                 activity.getTabModelSelector(), activity.getTabContentManager(),
                 activity.getCompositorViewHolder(), activity.getFullscreenManager(), activity,
-                activity.getMenuOrKeyboardActionController(), activity::onBackPressed);
+                activity.getMenuOrKeyboardActionController(), activity::onBackPressed, activity);
     }
 
     @Override
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMediator.java
index a787747..d384ebe 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMediator.java
@@ -69,7 +69,7 @@
                     (TabGroupModelFilter) mTabModelSelector.getTabModelFilterProvider()
                             .getCurrentTabModelFilter();
 
-            tabGroupModelFilter.mergeListOfTabsToGroup(selectedTabs, destinationTab);
+            tabGroupModelFilter.mergeListOfTabsToGroup(selectedTabs, destinationTab, false, true);
 
             hide();
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/UndoGroupSnackbarController.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/UndoGroupSnackbarController.java
new file mode 100644
index 0000000..0dc3f20
--- /dev/null
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/UndoGroupSnackbarController.java
@@ -0,0 +1,113 @@
+// 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.tasks.tab_management;
+
+import android.content.Context;
+
+import org.chromium.chrome.browser.snackbar.Snackbar;
+import org.chromium.chrome.browser.snackbar.SnackbarManager;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tabmodel.TabModelSelector;
+import org.chromium.chrome.browser.tasks.tabgroup.TabGroupModelFilter;
+import org.chromium.chrome.tab_ui.R;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * A controller that listens to
+ * {@link TabGroupModelFilter.Observer#didCreateGroup(List, List, boolean)} and shows a
+ * undo snackbar.
+ */
+public class UndoGroupSnackbarController implements SnackbarManager.SnackbarController {
+    private final Context mContext;
+    private final TabModelSelector mTabModelSelector;
+    private final SnackbarManager.SnackbarManageable mSnackbarManageable;
+    private final TabGroupModelFilter.Observer mTabGroupModelFilterObserver;
+
+    private class TabUndoInfo {
+        public final Tab tab;
+        public final int tabOriginalIndex;
+        public final int tabOriginalGroupId;
+
+        TabUndoInfo(Tab tab, int tabIndex, int tabGroupId) {
+            this.tab = tab;
+            this.tabOriginalIndex = tabIndex;
+            this.tabOriginalGroupId = tabGroupId;
+        }
+    }
+
+    public UndoGroupSnackbarController(Context context, TabModelSelector tabModelSelector,
+            SnackbarManager.SnackbarManageable snackbarManageable) {
+        mContext = context;
+        mTabModelSelector = tabModelSelector;
+        mSnackbarManageable = snackbarManageable;
+        mTabGroupModelFilterObserver = new TabGroupModelFilter.Observer() {
+            @Override
+            public void didMergeTabToGroup(Tab movedTab, int selectedTabIdInGroup) {}
+
+            @Override
+            public void didMoveTabGroup(Tab movedTab, int tabModelOldIndex, int tabModelNewIndex) {}
+
+            @Override
+            public void didMoveWithinGroup(
+                    Tab movedTab, int tabModelOldIndex, int tabModelNewIndex) {}
+
+            @Override
+            public void didMoveTabOutOfGroup(Tab movedTab, int prevFilterIndex) {}
+
+            @Override
+            public void didCreateGroup(
+                    List<Tab> tabs, List<Integer> tabOriginalIndex, boolean isSameGroup) {
+                assert tabs.size() == tabOriginalIndex.size();
+
+                List<TabUndoInfo> tabUndoInfo = new ArrayList<>();
+                for (int i = 0; i < tabs.size(); i++) {
+                    Tab tab = tabs.get(i);
+                    int index = tabOriginalIndex.get(i);
+                    int groupId = isSameGroup ? tabs.get(0).getId() : tab.getId();
+
+                    tabUndoInfo.add(new TabUndoInfo(tab, index, groupId));
+                }
+                showUndoGroupSnackbar(tabUndoInfo);
+            }
+        };
+
+        ((TabGroupModelFilter) mTabModelSelector.getTabModelFilterProvider().getTabModelFilter(
+                 false))
+                .addTabGroupObserver(mTabGroupModelFilterObserver);
+        ((TabGroupModelFilter) mTabModelSelector.getTabModelFilterProvider().getTabModelFilter(
+                 true))
+                .addTabGroupObserver(mTabGroupModelFilterObserver);
+    }
+
+    private void showUndoGroupSnackbar(List<TabUndoInfo> tabUndoInfo) {
+        String content = String.format(Locale.getDefault(), "%d", tabUndoInfo.size());
+        mSnackbarManageable.getSnackbarManager().showSnackbar(
+                Snackbar.make(content, this, Snackbar.TYPE_ACTION,
+                                Snackbar.UMA_TAB_GROUP_MANUAL_CREATION_UNDO)
+                        .setTemplateText(mContext.getString(R.string.undo_bar_group_tabs_message))
+                        .setAction(mContext.getString(R.string.undo), tabUndoInfo));
+    }
+
+    @Override
+    public void onAction(Object actionData) {
+        undo((List<TabUndoInfo>) actionData);
+    }
+
+    private void undo(List<TabUndoInfo> data) {
+        assert data.size() != 0;
+
+        TabGroupModelFilter tabGroupModelFilter =
+                (TabGroupModelFilter) mTabModelSelector.getTabModelFilterProvider()
+                        .getCurrentTabModelFilter();
+        for (int i = data.size() - 1; i >= 0; i--) {
+            TabUndoInfo info = data.get(i);
+            tabGroupModelFilter.undoGroupedTab(
+                    info.tab, info.tabOriginalIndex, info.tabOriginalGroupId);
+        }
+    }
+}
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
index e6143f21..2289c27 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
@@ -456,14 +456,14 @@
     }
 
     @Test
-    public void tabAdditionEnd() {
+    public void tabAddition_Dialog_End() {
         initAndAssertAllProperties();
 
         Tab newTab = prepareTab(TAB3_ID, TAB3_TITLE);
         doReturn(3).when(mTabModel).getCount();
         doReturn(Arrays.asList(mTab1, mTab2, newTab))
                 .when(mTabModelFilter)
-                .getRelatedTabList(eq(TAB3_ID));
+                .getRelatedTabList(eq(TAB1_ID));
 
         mTabModelObserverCaptor.getValue().didAddTab(newTab, TabLaunchType.FROM_CHROME_UI);
 
@@ -473,14 +473,14 @@
     }
 
     @Test
-    public void tabAdditionMiddle() {
+    public void tabAddition_Dialog_Middle() {
         initAndAssertAllProperties();
 
         Tab newTab = prepareTab(TAB3_ID, TAB3_TITLE);
         doReturn(3).when(mTabModel).getCount();
         doReturn(Arrays.asList(mTab1, newTab, mTab2))
                 .when(mTabModelFilter)
-                .getRelatedTabList(eq(TAB3_ID));
+                .getRelatedTabList(eq(TAB1_ID));
 
         mTabModelObserverCaptor.getValue().didAddTab(newTab, TabLaunchType.FROM_CHROME_UI);
 
@@ -490,6 +490,19 @@
     }
 
     @Test
+    public void tabAddition_Dialog_Skip() {
+        initAndAssertAllProperties();
+
+        Tab newTab = prepareTab(TAB3_ID, TAB3_TITLE);
+        // newTab is of another group.
+        doReturn(Arrays.asList(mTab1, mTab2)).when(mTabModelFilter).getRelatedTabList(eq(TAB1_ID));
+
+        mTabModelObserverCaptor.getValue().didAddTab(newTab, TabLaunchType.FROM_CHROME_UI);
+
+        assertThat(mModel.size(), equalTo(2));
+    }
+
+    @Test
     public void tabSelection() {
         initAndAssertAllProperties();
 
@@ -509,7 +522,7 @@
         doReturn(3).when(mTabModel).getCount();
         doReturn(Arrays.asList(mTab1, mTab2, newTab))
                 .when(mTabModelFilter)
-                .getRelatedTabList(eq(TAB3_ID));
+                .getRelatedTabList(eq(TAB1_ID));
 
         mTabModelObserverCaptor.getValue().tabClosureUndone(newTab);
 
@@ -569,6 +582,7 @@
         // Assume that TabGroupModelFilter is already updated.
         doReturn(mTab2).when(mTabGroupModelFilter).getTabAt(POSITION1);
         doReturn(mTab1).when(mTabGroupModelFilter).getTabAt(POSITION2);
+        doReturn(2).when(mTabGroupModelFilter).getCount();
 
         mTabGroupModelFilterObserverCaptor.getValue().didMoveTabOutOfGroup(mTab1, POSITION1);
 
@@ -600,6 +614,7 @@
         // Assume that TabGroupModelFilter is already updated.
         doReturn(mTab1).when(mTabGroupModelFilter).getTabAt(POSITION1);
         doReturn(mTab2).when(mTabGroupModelFilter).getTabAt(POSITION2);
+        doReturn(2).when(mTabGroupModelFilter).getCount();
 
         mTabGroupModelFilterObserverCaptor.getValue().didMoveTabOutOfGroup(mTab2, POSITION1);
 
@@ -754,6 +769,78 @@
         assertThat(mModel.get(0).get(TabProperties.TITLE), equalTo(TAB2_TITLE));
     }
 
+    @Test
+    public void undoGrouped_One_Adjacent_Tab() {
+        setUpForTabGroupOperation();
+
+        // Assume there are 3 tabs in TabModel, mTab2 just grouped with mTab1;
+        Tab tab3 = prepareTab(TAB3_ID, TAB3_TITLE);
+        List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1, tab3));
+        mMediator.resetWithListOfTabs(tabs, false);
+        assertThat(mModel.size(), equalTo(2));
+
+        // Assume undo grouping mTab2 with mTab1.
+        doReturn(3).when(mTabGroupModelFilter).getCount();
+        doReturn(mTab1).when(mTabGroupModelFilter).getTabAt(POSITION1);
+        doReturn(mTab2).when(mTabGroupModelFilter).getTabAt(POSITION2);
+        doReturn(tab3).when(mTabGroupModelFilter).getTabAt(2);
+
+        mTabGroupModelFilterObserverCaptor.getValue().didMoveTabOutOfGroup(mTab2, POSITION1);
+
+        assertThat(mModel.size(), equalTo(3));
+        assertThat(mModel.indexFromId(TAB1_ID), equalTo(0));
+        assertThat(mModel.indexFromId(TAB2_ID), equalTo(1));
+        assertThat(mModel.indexFromId(TAB3_ID), equalTo(2));
+    }
+
+    @Test
+    public void undoForwardGrouped_One_Tab() {
+        setUpForTabGroupOperation();
+
+        // Assume there are 3 tabs in TabModel, tab3 just grouped with mTab1;
+        Tab tab3 = prepareTab(TAB3_ID, TAB3_TITLE);
+        List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1, mTab2));
+        mMediator.resetWithListOfTabs(tabs, false);
+        assertThat(mModel.size(), equalTo(2));
+
+        // Assume undo grouping tab3 with mTab1.
+        doReturn(3).when(mTabGroupModelFilter).getCount();
+        doReturn(mTab1).when(mTabGroupModelFilter).getTabAt(POSITION1);
+        doReturn(mTab2).when(mTabGroupModelFilter).getTabAt(POSITION2);
+        doReturn(tab3).when(mTabGroupModelFilter).getTabAt(2);
+
+        mTabGroupModelFilterObserverCaptor.getValue().didMoveTabOutOfGroup(tab3, POSITION1);
+
+        assertThat(mModel.size(), equalTo(3));
+        assertThat(mModel.indexFromId(TAB1_ID), equalTo(0));
+        assertThat(mModel.indexFromId(TAB2_ID), equalTo(1));
+        assertThat(mModel.indexFromId(TAB3_ID), equalTo(2));
+    }
+
+    @Test
+    public void undoBackwardGrouped_One_Tab() {
+        setUpForTabGroupOperation();
+
+        // Assume there are 3 tabs in TabModel, mTab1 just grouped with mTab2;
+        Tab tab3 = prepareTab(TAB3_ID, TAB3_TITLE);
+        List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab2, tab3));
+        mMediator.resetWithListOfTabs(tabs, false);
+        assertThat(mModel.size(), equalTo(2));
+
+        // Assume undo grouping tab3 with mTab1.
+        doReturn(3).when(mTabGroupModelFilter).getCount();
+        doReturn(mTab1).when(mTabGroupModelFilter).getTabAt(POSITION1);
+        doReturn(mTab2).when(mTabGroupModelFilter).getTabAt(POSITION2);
+        doReturn(tab3).when(mTabGroupModelFilter).getTabAt(2);
+
+        mTabGroupModelFilterObserverCaptor.getValue().didMoveTabOutOfGroup(mTab1, POSITION2);
+
+        assertThat(mModel.size(), equalTo(3));
+        assertThat(mModel.indexFromId(TAB1_ID), equalTo(0));
+        assertThat(mModel.indexFromId(TAB2_ID), equalTo(1));
+        assertThat(mModel.indexFromId(TAB3_ID), equalTo(2));
+    }
+
     private void initAndAssertAllProperties() {
         List<Tab> tabs = new ArrayList<>();
         for (int i = 0; i < mTabModel.getCount(); i++) {
diff --git a/chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/VrShell.java b/chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/VrShell.java
index d1a52ce..a0351ee 100644
--- a/chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/VrShell.java
+++ b/chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/VrShell.java
@@ -208,9 +208,6 @@
 
         if (mVrBrowsingEnabled) {
             injectVrRootView();
-
-            // Hide FindInPage toolbar.
-            mActivity.getFindToolbarManager().hideToolbar();
         }
 
         // This overrides the default intent created by GVR to return to Chrome when the DON flow
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedImageLoader.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedImageLoader.java
index 583159f..6037ab5d 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedImageLoader.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedImageLoader.java
@@ -142,6 +142,13 @@
      */
     private @DrawableRes int lookupDrawableIdentifier(String resourceName) {
         switch (resourceName) {
+            case BundledAssets.AMP_ICON:
+            case BundledAssets.AMP_ICON_DARK_BG:
+                return R.drawable.ic_amp_24dp;
+            case BundledAssets.MENU_ICON:
+                return R.drawable.ic_more_vert_24dp_on_light_bg;
+            case BundledAssets.MENU_ICON_DARK_BG:
+                return R.drawable.ic_more_vert_24dp_on_dark_bg;
             case BundledAssets.OFFLINE_INDICATOR_BADGE:
                 return R.drawable.ic_offline_pin_24dp_on_light_bg;
             case BundledAssets.OFFLINE_INDICATOR_BADGE_DARK_BG:
diff --git a/chrome/android/java/res/drawable-hdpi/ic_amp_24dp.png b/chrome/android/java/res/drawable-hdpi/ic_amp_24dp.png
new file mode 100644
index 0000000..2216d7d
--- /dev/null
+++ b/chrome/android/java/res/drawable-hdpi/ic_amp_24dp.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-hdpi/ic_more_vert_24dp_on_dark_bg.png b/chrome/android/java/res/drawable-hdpi/ic_more_vert_24dp_on_dark_bg.png
new file mode 100644
index 0000000..ffb6a7f
--- /dev/null
+++ b/chrome/android/java/res/drawable-hdpi/ic_more_vert_24dp_on_dark_bg.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-hdpi/ic_more_vert_24dp_on_light_bg.png b/chrome/android/java/res/drawable-hdpi/ic_more_vert_24dp_on_light_bg.png
new file mode 100644
index 0000000..5bde3f7
--- /dev/null
+++ b/chrome/android/java/res/drawable-hdpi/ic_more_vert_24dp_on_light_bg.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-hdpi/ic_more_vert_black_24dp.png b/chrome/android/java/res/drawable-hdpi/ic_more_vert_black_24dp.png
deleted file mode 100644
index fa58ddccc..0000000
--- a/chrome/android/java/res/drawable-hdpi/ic_more_vert_black_24dp.png
+++ /dev/null
Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/ic_amp_24dp.png b/chrome/android/java/res/drawable-mdpi/ic_amp_24dp.png
new file mode 100644
index 0000000..4998126
--- /dev/null
+++ b/chrome/android/java/res/drawable-mdpi/ic_amp_24dp.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/ic_more_vert_24dp_on_dark_bg.png b/chrome/android/java/res/drawable-mdpi/ic_more_vert_24dp_on_dark_bg.png
new file mode 100644
index 0000000..2358e561
--- /dev/null
+++ b/chrome/android/java/res/drawable-mdpi/ic_more_vert_24dp_on_dark_bg.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/ic_more_vert_24dp_on_light_bg.png b/chrome/android/java/res/drawable-mdpi/ic_more_vert_24dp_on_light_bg.png
new file mode 100644
index 0000000..7c08bc4
--- /dev/null
+++ b/chrome/android/java/res/drawable-mdpi/ic_more_vert_24dp_on_light_bg.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/ic_more_vert_black_24dp.png b/chrome/android/java/res/drawable-mdpi/ic_more_vert_black_24dp.png
deleted file mode 100644
index 1380bf5..0000000
--- a/chrome/android/java/res/drawable-mdpi/ic_more_vert_black_24dp.png
+++ /dev/null
Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/ic_amp_24dp.png b/chrome/android/java/res/drawable-xhdpi/ic_amp_24dp.png
new file mode 100644
index 0000000..3f5b945
--- /dev/null
+++ b/chrome/android/java/res/drawable-xhdpi/ic_amp_24dp.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/ic_more_vert_24dp_on_dark_bg.png b/chrome/android/java/res/drawable-xhdpi/ic_more_vert_24dp_on_dark_bg.png
new file mode 100644
index 0000000..7385c967
--- /dev/null
+++ b/chrome/android/java/res/drawable-xhdpi/ic_more_vert_24dp_on_dark_bg.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/ic_more_vert_24dp_on_light_bg.png b/chrome/android/java/res/drawable-xhdpi/ic_more_vert_24dp_on_light_bg.png
new file mode 100644
index 0000000..0324bdb
--- /dev/null
+++ b/chrome/android/java/res/drawable-xhdpi/ic_more_vert_24dp_on_light_bg.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/ic_more_vert_black_24dp.png b/chrome/android/java/res/drawable-xhdpi/ic_more_vert_black_24dp.png
deleted file mode 100644
index c92d445..0000000
--- a/chrome/android/java/res/drawable-xhdpi/ic_more_vert_black_24dp.png
+++ /dev/null
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/ic_amp_24dp.png b/chrome/android/java/res/drawable-xxhdpi/ic_amp_24dp.png
new file mode 100644
index 0000000..fea7a785
--- /dev/null
+++ b/chrome/android/java/res/drawable-xxhdpi/ic_amp_24dp.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/ic_more_vert_24dp_on_dark_bg.png b/chrome/android/java/res/drawable-xxhdpi/ic_more_vert_24dp_on_dark_bg.png
new file mode 100644
index 0000000..979ebcfd
--- /dev/null
+++ b/chrome/android/java/res/drawable-xxhdpi/ic_more_vert_24dp_on_dark_bg.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/ic_more_vert_24dp_on_light_bg.png b/chrome/android/java/res/drawable-xxhdpi/ic_more_vert_24dp_on_light_bg.png
new file mode 100644
index 0000000..62dd9165
--- /dev/null
+++ b/chrome/android/java/res/drawable-xxhdpi/ic_more_vert_24dp_on_light_bg.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/ic_more_vert_black_24dp.png b/chrome/android/java/res/drawable-xxhdpi/ic_more_vert_black_24dp.png
deleted file mode 100644
index 94b09da..0000000
--- a/chrome/android/java/res/drawable-xxhdpi/ic_more_vert_black_24dp.png
+++ /dev/null
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/ic_amp_24dp.png b/chrome/android/java/res/drawable-xxxhdpi/ic_amp_24dp.png
new file mode 100644
index 0000000..c5d24b9
--- /dev/null
+++ b/chrome/android/java/res/drawable-xxxhdpi/ic_amp_24dp.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/ic_more_vert_24dp_on_dark_bg.png b/chrome/android/java/res/drawable-xxxhdpi/ic_more_vert_24dp_on_dark_bg.png
new file mode 100644
index 0000000..ea57cd6
--- /dev/null
+++ b/chrome/android/java/res/drawable-xxxhdpi/ic_more_vert_24dp_on_dark_bg.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/ic_more_vert_24dp_on_light_bg.png b/chrome/android/java/res/drawable-xxxhdpi/ic_more_vert_24dp_on_light_bg.png
new file mode 100644
index 0000000..f58a28cf
--- /dev/null
+++ b/chrome/android/java/res/drawable-xxxhdpi/ic_more_vert_24dp_on_light_bg.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/ic_more_vert_black_24dp.png b/chrome/android/java/res/drawable-xxxhdpi/ic_more_vert_black_24dp.png
deleted file mode 100644
index 3bf4e985..0000000
--- a/chrome/android/java/res/drawable-xxxhdpi/ic_more_vert_black_24dp.png
+++ /dev/null
Binary files differ
diff --git a/chrome/android/java/res/layout/accept_languages_item.xml b/chrome/android/java/res/layout/accept_languages_item.xml
index 690b012..deae885 100644
--- a/chrome/android/java/res/layout/accept_languages_item.xml
+++ b/chrome/android/java/res/layout/accept_languages_item.xml
@@ -46,7 +46,7 @@
         android:paddingStart="@dimen/selectable_list_layout_row_padding"
         android:paddingEnd="@dimen/selectable_list_layout_row_padding"
         android:background="@null"
-        android:src="@drawable/ic_more_vert_black_24dp"
+        android:src="@drawable/ic_more_vert_24dp"
         app:menuWidth="@dimen/pref_languages_item_popup_width"
         app:tint="@color/standard_mode_tint"
         tools:ignore="ContentDescription" />
diff --git a/chrome/android/java/res/layout/bottom_toolbar_menu_button.xml b/chrome/android/java/res/layout/bottom_toolbar_menu_button.xml
index 01d34843..d7c3d3cc 100644
--- a/chrome/android/java/res/layout/bottom_toolbar_menu_button.xml
+++ b/chrome/android/java/res/layout/bottom_toolbar_menu_button.xml
@@ -17,7 +17,7 @@
         android:layout_height="wrap_content"
         android:layout_width="wrap_content"
         android:background="@null"
-        android:src="@drawable/ic_more_vert_black_24dp"
+        android:src="@drawable/ic_more_vert_24dp"
         android:importantForAccessibility="no"
         android:layout_gravity="center"
         app:tint="@color/standard_mode_tint" />
diff --git a/chrome/android/java/res/layout/custom_tabs_toolbar.xml b/chrome/android/java/res/layout/custom_tabs_toolbar.xml
index 9e37eb7..0319aca4 100644
--- a/chrome/android/java/res/layout/custom_tabs_toolbar.xml
+++ b/chrome/android/java/res/layout/custom_tabs_toolbar.xml
@@ -102,7 +102,7 @@
         android:layout_gravity="center_vertical|end"
         android:layout_width="42dp"
         android:paddingEnd="4dp"
-        android:src="@drawable/ic_more_vert_black_24dp"
+        android:src="@drawable/ic_more_vert_24dp"
         android:contentDescription="@string/accessibility_toolbar_btn_menu"
         android:background="@null"
         app:tint="@color/standard_mode_tint" />
diff --git a/chrome/android/java/res/layout/empty_background_view_tablet.xml b/chrome/android/java/res/layout/empty_background_view_tablet.xml
index 2ff8f6f..8d935d96 100644
--- a/chrome/android/java/res/layout/empty_background_view_tablet.xml
+++ b/chrome/android/java/res/layout/empty_background_view_tablet.xml
@@ -45,7 +45,7 @@
             style="@style/ToolbarButton"
             android:layout_width="48dp"
             android:layout_height="@dimen/toolbar_height_no_shadow"
-            android:src="@drawable/ic_more_vert_black_24dp"
+            android:src="@drawable/ic_more_vert_24dp"
             android:scaleType="center"
             android:contentDescription="@string/accessibility_toolbar_btn_menu"
             android:paddingStart="2dp"
diff --git a/chrome/android/java/res/layout/infobar_translate_compact_content.xml b/chrome/android/java/res/layout/infobar_translate_compact_content.xml
index dc12798..449f21d 100644
--- a/chrome/android/java/res/layout/infobar_translate_compact_content.xml
+++ b/chrome/android/java/res/layout/infobar_translate_compact_content.xml
@@ -32,6 +32,6 @@
         android:scaleType="center"
         android:background="?attr/selectableItemBackground"
         android:contentDescription="@string/accessibility_toolbar_btn_menu"
-        android:src="@drawable/ic_more_vert_black_24dp"
+        android:src="@drawable/ic_more_vert_24dp"
         app:tint="@color/standard_mode_tint" />
 </LinearLayout>
diff --git a/chrome/android/java/res/layout/list_menu_button.xml b/chrome/android/java/res/layout/list_menu_button.xml
index cbc932f7..5972c366 100644
--- a/chrome/android/java/res/layout/list_menu_button.xml
+++ b/chrome/android/java/res/layout/list_menu_button.xml
@@ -15,6 +15,6 @@
     android:paddingStart="@dimen/selectable_list_layout_row_padding"
     android:paddingEnd="@dimen/selectable_list_layout_row_padding"
     android:background="@null"
-    android:src="@drawable/ic_more_vert_black_24dp"
+    android:src="@drawable/ic_more_vert_24dp"
     app:tint="@color/standard_mode_tint"
     tools:ignore="ContentDescription" />
diff --git a/chrome/android/java/res/layout/menu_button.xml b/chrome/android/java/res/layout/menu_button.xml
index 9b88b45..21421a3 100644
--- a/chrome/android/java/res/layout/menu_button.xml
+++ b/chrome/android/java/res/layout/menu_button.xml
@@ -15,7 +15,7 @@
        <org.chromium.ui.widget.ChromeImageButton
            android:id="@+id/menu_button"
            style="@style/ToolbarMenuButtonPhone"
-           android:src="@drawable/ic_more_vert_black_24dp"
+           android:src="@drawable/ic_more_vert_24dp"
            android:contentDescription="@string/accessibility_toolbar_btn_menu"
            android:layout_gravity="center"
            app:tint="@color/standard_mode_tint" />
diff --git a/chrome/android/java/res/layout/toolbar_tablet.xml b/chrome/android/java/res/layout/toolbar_tablet.xml
index 4ead17f..12630f4 100644
--- a/chrome/android/java/res/layout/toolbar_tablet.xml
+++ b/chrome/android/java/res/layout/toolbar_tablet.xml
@@ -84,7 +84,7 @@
             <org.chromium.ui.widget.ChromeImageButton
                 android:id="@+id/menu_button"
                 style="@style/ToolbarMenuButtonTablet"
-                android:src="@drawable/ic_more_vert_black_24dp"
+                android:src="@drawable/ic_more_vert_24dp"
                 android:contentDescription="@string/accessibility_toolbar_btn_menu"
                 android:layout_gravity="center"
                 app:tint="@color/standard_mode_tint" />
diff --git a/chrome/android/java/res/values/drawables.xml b/chrome/android/java/res/values/drawables.xml
index 13b97976..2da6607 100644
--- a/chrome/android/java/res/values/drawables.xml
+++ b/chrome/android/java/res/values/drawables.xml
@@ -7,6 +7,7 @@
     <drawable name="ntp_search_box">@drawable/modern_toolbar_text_box_background</drawable>
     <drawable name="badge_update">@drawable/badge_update_dark</drawable>
     <drawable name="ic_error_24dp_filled">@drawable/ic_error_grey800_24dp_filled</drawable>
+    <drawable name="ic_more_vert_24dp">@drawable/ic_more_vert_24dp_on_light_bg</drawable>
     <drawable name="ic_offline_pin_24dp">@drawable/ic_offline_pin_24dp_on_light_bg</drawable>
     <drawable name="ic_play_circle_filled_24dp">
         @drawable/ic_play_circle_filled_24dp_on_light_bg
diff --git a/chrome/android/java/res_night/values-night/drawables.xml b/chrome/android/java/res_night/values-night/drawables.xml
index 0d6929ad..2c54f0f4 100644
--- a/chrome/android/java/res_night/values-night/drawables.xml
+++ b/chrome/android/java/res_night/values-night/drawables.xml
@@ -5,6 +5,7 @@
 <resources>
     <drawable name="badge_update">@drawable/badge_update_light</drawable>
     <drawable name="ic_error_24dp_filled">@drawable/ic_error_white_24dp_filled</drawable>
+    <drawable name="ic_more_vert_24dp">@drawable/ic_more_vert_24dp_on_dark_bg</drawable>
     <drawable name="ic_offline_pin_24dp">@drawable/ic_offline_pin_24dp_on_dark_bg</drawable>
     <drawable name="ic_play_circle_filled_24dp">
         @drawable/ic_play_circle_filled_24dp_on_dark_bg
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
index a31b993..1cd08339 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -47,6 +47,7 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.DiscardableReferencePool;
 import org.chromium.base.ObservableSupplier;
+import org.chromium.base.ObservableSupplierImpl;
 import org.chromium.base.StrictModeContext;
 import org.chromium.base.SysUtils;
 import org.chromium.base.TraceEvent;
@@ -64,7 +65,6 @@
 import org.chromium.chrome.browser.bookmarks.BookmarkModel;
 import org.chromium.chrome.browser.bookmarks.BookmarkUtils;
 import org.chromium.chrome.browser.compositor.CompositorViewHolder;
-import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel.StateChangeReason;
 import org.chromium.chrome.browser.compositor.bottombar.ephemeraltab.EphemeralTabPanel;
 import org.chromium.chrome.browser.compositor.layouts.Layout;
 import org.chromium.chrome.browser.compositor.layouts.LayoutManager;
@@ -156,7 +156,6 @@
 import org.chromium.chrome.browser.widget.ScrimView;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController;
-import org.chromium.chrome.browser.widget.findinpage.FindToolbarManager;
 import org.chromium.chrome.browser.widget.textbubble.TextBubble;
 import org.chromium.components.bookmarks.BookmarkId;
 import org.chromium.components.feature_engagement.EventConstants;
@@ -273,14 +272,14 @@
     private PictureInPictureController mPictureInPictureController;
 
     private CompositorViewHolder mCompositorViewHolder;
+    private ObservableSupplierImpl<LayoutManager> mLayoutManagerSupplier =
+            new ObservableSupplierImpl<>();
     private InsetObserverView mInsetObserverView;
     private ContextualSearchManager mContextualSearchManager;
     protected ReaderModeManager mReaderModeManager;
     private SnackbarManager mSnackbarManager;
     @Nullable
     private ToolbarManager mToolbarManager;
-    @Nullable
-    private FindToolbarManager mFindToolbarManager;
     private BottomSheetController mBottomSheetController;
     private UpdateNotificationController mUpdateNotificationController;
     private BottomSheet mBottomSheet;
@@ -628,8 +627,9 @@
     }
 
     private void initializeToolbarIfNecessary() {
-        // TODO(pshmakov): make ToolbarManager and FindToolbarManager lazy, don't create them unless
-        // getToolbarManager() or getFindToolbarManager() is called.
+        // TODO(https://crbug.com/931496): Move toolbar ownership to RootUiCoordinator.
+        // TODO(pshmakov): make ToolbarManager lazy, don't create them unless getToolbarManager()
+        // is called.
         if (!mToolbarInitialized) {
             mToolbarInitialized = true;
             initializeToolbar();
@@ -649,8 +649,6 @@
             mToolbarManager = new ToolbarManager(this, toolbarContainer,
                     getCompositorViewHolder().getInvalidator(), urlFocusChangedCallback,
                     mTabThemeColorProvider);
-            mFindToolbarManager =
-                    new FindToolbarManager(this, mToolbarManager.getActionModeControllerCallback());
         }
     }
 
@@ -733,17 +731,6 @@
     }
 
     /**
-     * @return {@link FindToolbarManager} that belongs to this activity.
-     */
-    @Nullable
-    public FindToolbarManager getFindToolbarManager() {
-        if (isInitialLayoutInflationComplete()) {
-            initializeToolbarIfNecessary();
-        }
-        return mFindToolbarManager;
-    }
-
-    /**
      * @return The {@link ManualFillingComponent} that belongs to this activity.
      */
     public ManualFillingComponent getManualFillingComponent() {
@@ -835,9 +822,6 @@
 
         if (isContextualSearchAllowed() && ContextualSearchFieldTrial.isEnabled()) {
             mContextualSearchManager = new ContextualSearchManager(this, this);
-            if (mFindToolbarManager != null) {
-                mContextualSearchManager.setFindToolbarManager(mFindToolbarManager);
-            }
         }
 
         if (ReaderModeManager.isEnabled(this)) {
@@ -1869,6 +1853,16 @@
         mActivityTabProvider.setLayoutManager(layoutManager);
         EphemeralTabPanel panel = layoutManager.getEphemeralTabPanel();
         if (panel != null) panel.setChromeActivity(this);
+
+        mLayoutManagerSupplier.set(layoutManager);
+    }
+
+    /**
+     * @return An {@link ObservableSupplier} that will supply the {@link LayoutManager} when it is
+     *         ready.
+     */
+    public ObservableSupplier<LayoutManager> getLayoutManagerSupplier() {
+        return mLayoutManagerSupplier;
     }
 
     /**
@@ -2084,22 +2078,6 @@
         if (id == R.id.preferences_id) {
             PreferencesLauncher.launchSettingsPage(this, null);
             RecordUserAction.record("MobileMenuSettings");
-        } else if (id == R.id.find_in_page_id) {
-            if (mFindToolbarManager == null) return false;
-
-            mFindToolbarManager.showToolbar();
-            if (mContextualSearchManager != null) {
-                getContextualSearchManager().hideContextualSearch(StateChangeReason.UNKNOWN);
-            }
-            if (getEphemeralTabPanel() != null) {
-                getEphemeralTabPanel().closePanel(StateChangeReason.UNKNOWN, true);
-            }
-            if (fromMenu) {
-                RecordUserAction.record("MobileMenuFindInPage");
-            } else {
-                RecordUserAction.record("MobileShortcutFindInPage");
-            }
-            return true;
         }
 
         if (id == R.id.update_menu_id) {
@@ -2519,6 +2497,13 @@
     }
 
     /**
+     * @return  Whether this activity supports the find in page feature.
+     */
+    public boolean supportsFindInPage() {
+        return true;
+    }
+
+    /**
      * TODO(mthiesse): Figure out a way to clean this up. The problem is that the
      * TouchlessUiCoordinator has an implementation of the ModalDialogManager, which is created in
      * AsyncInitializationActivity#onCreateInternal, before any ChromeActivity init functions are
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index 402e898..08947b0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -751,9 +751,9 @@
             }
 
             getToolbarManager().initializeWithNative(mTabModelSelectorImpl,
-                    getFullscreenManager().getBrowserVisibilityDelegate(), getFindToolbarManager(),
-                    mOverviewModeController, mLayoutManager, tabSwitcherClickHandler,
-                    newTabClickHandler, bookmarkClickHandler, null);
+                    getFullscreenManager().getBrowserVisibilityDelegate(), mOverviewModeController,
+                    mLayoutManager, tabSwitcherClickHandler, newTabClickHandler,
+                    bookmarkClickHandler, null);
 
             mLayoutManager.setToolbarManager(getToolbarManager());
 
@@ -2303,7 +2303,6 @@
 
     @Override
     public void onOverviewModeStartedShowing(boolean showToolbar) {
-        if (getFindToolbarManager() != null) getFindToolbarManager().hideToolbar();
         if (getAssistStatusHandler() != null) getAssistStatusHandler().updateAssistState();
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
index a81b15df..74c4a2b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
@@ -47,8 +47,6 @@
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabModelObserver;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabObserver;
 import org.chromium.chrome.browser.tabmodel.TabSelectionType;
-import org.chromium.chrome.browser.widget.findinpage.FindToolbarManager;
-import org.chromium.chrome.browser.widget.findinpage.FindToolbarObserver;
 import org.chromium.components.feature_engagement.EventConstants;
 import org.chromium.components.feature_engagement.FeatureConstants;
 import org.chromium.components.feature_engagement.Tracker;
@@ -152,8 +150,6 @@
     private OverlayPanelContentViewDelegate mSearchContentViewDelegate;
     private TabModelSelectorTabModelObserver mTabModelObserver;
     private TabModelSelectorTabObserver mTabModelSelectorTabObserver;
-    private FindToolbarManager mFindToolbarManager;
-    private FindToolbarObserver mFindToolbarObserver;
     private ContextualSearchIPH mInProductHelp;
 
     private boolean mDidStartLoadingResolvedSearchRequest;
@@ -278,30 +274,6 @@
     }
 
     /**
-     * Sets the {@link FindToolbarManager} and attaches an observer that dismisses the Contextual
-     * Search panel when the find toolbar is shown.
-     *
-     * @param findToolbarManager The {@link FindToolbarManager} for the current activity.
-     */
-    public void setFindToolbarManager(FindToolbarManager findToolbarManager) {
-        if (mFindToolbarManager != null) {
-            mFindToolbarManager.removeObserver(mFindToolbarObserver);
-        }
-
-        mFindToolbarManager = findToolbarManager;
-
-        if (mFindToolbarObserver == null) {
-            mFindToolbarObserver = new FindToolbarObserver() {
-                @Override
-                public void onFindToolbarShown() {
-                    hideContextualSearch(StateChangeReason.UNKNOWN);
-                }
-            };
-        }
-        mFindToolbarManager.addObserver(mFindToolbarObserver);
-    }
-
-    /**
      * Destroys the native Contextual Search Manager.
      * Call this method before orphaning this object to allow it to be garbage collected.
      */
@@ -313,11 +285,6 @@
         nativeDestroy(mNativeContextualSearchManagerPtr);
         stopListeningForHideNotifications();
         mTabRedirectHandler.clear();
-        if (mFindToolbarManager != null) {
-            mFindToolbarManager.removeObserver(mFindToolbarObserver);
-            mFindToolbarManager = null;
-            mFindToolbarObserver = null;
-        }
         mInternalStateController.enter(InternalState.UNDEFINED);
     }
 
@@ -434,9 +401,6 @@
      */
     private void showContextualSearch(@StateChangeReason int stateChangeReason) {
         assert mSearchPanel != null;
-        if (mFindToolbarManager != null) {
-            mFindToolbarManager.hideToolbar(false);
-        }
 
         // Dismiss the undo SnackBar if present by committing all tab closures.
         mActivity.getTabModelSelector().commitAllTabClosures();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarCoordinator.java
index 904ed394..deb2a5e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarCoordinator.java
@@ -35,7 +35,6 @@
 import org.chromium.chrome.browser.toolbar.ToolbarManager;
 import org.chromium.chrome.browser.util.ColorUtils;
 import org.chromium.chrome.browser.util.FeatureUtilities;
-import org.chromium.chrome.browser.widget.findinpage.FindToolbarManager;
 
 import javax.inject.Inject;
 import javax.inject.Named;
@@ -67,7 +66,6 @@
     private final Context mAppContext;
     private final CustomTabActivityTabController mTabController;
     private final Lazy<ChromeFullscreenManager> mFullscreenManager;
-    private final Lazy<FindToolbarManager> mFindToolbarManager;
     private final CustomTabActivityNavigationController mNavigationController;
     private final TabObserverRegistrar mTabObserverRegistrar;
     private final CustomTabStatusBarColorProvider mStatusBarColorProvider;
@@ -87,7 +85,6 @@
             @Named(APP_CONTEXT) Context appContext,
             CustomTabActivityTabController tabController,
             Lazy<ChromeFullscreenManager> fullscreenManager,
-            Lazy<FindToolbarManager> findToolbarManager,
             CustomTabActivityNavigationController navigationController,
             TabObserverRegistrar tabObserverRegistrar,
             CustomTabStatusBarColorProvider statusBarColorProvider,
@@ -101,7 +98,6 @@
         mAppContext = appContext;
         mTabController = tabController;
         mFullscreenManager = fullscreenManager;
-        mFindToolbarManager = findToolbarManager;
         mNavigationController = navigationController;
         mTabObserverRegistrar = tabObserverRegistrar;
         mStatusBarColorProvider = statusBarColorProvider;
@@ -231,8 +227,8 @@
                 mActivity.findViewById(R.id.control_container));
 
         mToolbarManager.get().initializeWithNative(mTabController.getTabModelSelector(),
-                mFullscreenManager.get().getBrowserVisibilityDelegate(), mFindToolbarManager.get(),
-                null, layoutDriver, null, null, null, v -> onCloseButtonClick());
+                mFullscreenManager.get().getBrowserVisibilityDelegate(), null, layoutDriver, null,
+                null, null, v -> onCloseButtonClick());
         mInitializedToolbarWithNative = true;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeActivityCommonsModule.java b/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeActivityCommonsModule.java
index b3ab4a1..2d29bdf 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeActivityCommonsModule.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeActivityCommonsModule.java
@@ -24,7 +24,6 @@
 import org.chromium.chrome.browser.ui.system.StatusBarColorController;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController;
 import org.chromium.content_public.browser.ScreenOrientationProvider;
-import org.chromium.chrome.browser.widget.findinpage.FindToolbarManager;
 import org.chromium.ui.base.ActivityWindowAndroid;
 
 import javax.inject.Named;
@@ -144,9 +143,4 @@
     public ScreenOrientationProvider provideScreenOrientationProvider() {
         return ScreenOrientationProvider.getInstance();
     }
-
-    @Provides
-    public FindToolbarManager provideFindToolbarManager() {
-        return mActivity.getFindToolbarManager();
-    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePagesDownloadManagerBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageArchivePublisherBridge.java
similarity index 96%
rename from chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePagesDownloadManagerBridge.java
rename to chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageArchivePublisherBridge.java
index 35215fd..fc92a54 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePagesDownloadManagerBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageArchivePublisherBridge.java
@@ -20,9 +20,9 @@
  * transfer all C++ calls over to Java land for making the call to ADM.  This is a one-way bridge,
  * from C++ to Java only.  The Java side of this bridge is not called by other Java code.
  */
-@JNINamespace("offline_pages::android")
-public class OfflinePagesDownloadManagerBridge {
-    private static final String TAG = "OfflinePagesDMBridge";
+@JNINamespace("offline_pages")
+public class OfflinePageArchivePublisherBridge {
+    private static final String TAG = "Publisher";
     /** Offline pages should not be scanned as for media content. */
     public static final boolean IS_MEDIA_SCANNER_SCANNABLE = false;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java
index 5bc237d6e..ef2e053 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java
@@ -58,17 +58,9 @@
         if (profile == null)
             return null;
 
-        OfflinePageBridge bridge = nativeGetOfflinePageBridgeForProfileKey(profile.getProfileKey());
-
-        // TODO(crbug.com/978871): Storing Profile is a temporary hack during the transition
-        // of some members to RequestCoordinatorBridge.
-        if (bridge != null) {
-            bridge.mProfile = profile;
-        }
-        return bridge;
+        return nativeGetOfflinePageBridgeForProfileKey(profile.getProfileKey());
     }
 
-    private Profile mProfile;
     private long mNativeOfflinePageBridge;
     private boolean mIsNativeOfflinePageModelLoaded;
     private final ObserverList<OfflinePageModelObserver> mObservers =
@@ -220,70 +212,6 @@
     }
 
     /**
-     * Gets all the URLs in the request queue.
-     *
-     * @return A list of {@link SavePageRequest} representing all the queued requests.
-     * @deprecated Use {@link
-     *         RequestCoordinatorBridge#getRequestsInQueue(Callback<SavePageRequest[]>)} instead.
-     */
-    @Deprecated
-    @VisibleForTesting
-    public void getRequestsInQueue(Callback<SavePageRequest[]> callback) {
-        RequestCoordinatorBridge.getForProfile(mProfile).getRequestsInQueue(callback);
-    }
-
-    /**
-     * Contains a result for a remove page request.
-     * @deprecated Use {@link RequestCoordinatorBridge.RequestRemovedResult} instead.
-     */
-    @Deprecated
-    public static class RequestRemovedResult {
-        private long mRequestId;
-        private int mUpdateRequestResult;
-
-        public RequestRemovedResult(long requestId, int requestResult) {
-            mRequestId = requestId;
-            mUpdateRequestResult = requestResult;
-        }
-
-        /** Request ID as found in the SavePageRequest. */
-        public long getRequestId() {
-            return mRequestId;
-        }
-
-        /** {@see org.chromium.components.offlinepages.background.UpdateRequestResult} enum. */
-        public int getUpdateRequestResult() {
-            return mUpdateRequestResult;
-        }
-    }
-
-    /**
-     * Removes SavePageRequests from the request queue.
-     *
-     * The callback will be called with |null| in the case that the queue is unavailable.  This can
-     * happen in incognito, for example.
-     *
-     * @param requestIdList The IDs of the requests to remove.
-     * @param callback Called when the removal is done, with the SavePageRequest objects that were
-     *     actually removed.
-     * @deprecated Use {@link RequestCoordinatorBridge#removeRequestsFromQueue(List<Long>,
-     *         Callback<List<RequestCoordinatorBridge.RequestRemovedResult>>)} instead.
-     */
-    @Deprecated
-    public void removeRequestsFromQueue(
-            List<Long> requestIdList, Callback<List<RequestRemovedResult>> callback) {
-        RequestCoordinatorBridge.getForProfile(mProfile).removeRequestsFromQueue(
-                requestIdList, (results) -> {
-                    List<RequestRemovedResult> transformedResults = new ArrayList<>(results.size());
-                    for (RequestCoordinatorBridge.RequestRemovedResult result : results) {
-                        transformedResults.add(new RequestRemovedResult(
-                                result.getRequestId(), result.getUpdateRequestResult()));
-                    }
-                    callback.onResult(transformedResults);
-                });
-    }
-
-    /**
      * Get the offline page associated with the provided offline URL.
      *
      * @param onlineUrl URL of the page.
@@ -351,143 +279,6 @@
     }
 
     /**
-     * Save the given URL as an offline page when the network becomes available.
-     *
-     * The page is marked as not having been saved by the user.  Use the 3-argument form to specify
-     * a user request.
-     *
-     * @param url The given URL to save for later.
-     * @param clientId The client ID for the offline page to be saved later.
-     * @deprecated Use {@link RequestCoordinatorBridge#savePageLater(String, ClientId)} instead.
-     */
-    @Deprecated
-    @VisibleForTesting
-    public void savePageLater(String url, ClientId clientId) {
-        savePageLater(url, clientId, true);
-    }
-
-    /**
-     * Save the given URL as an offline page when the network becomes available. Origin is
-     * assumed to be Chrome.
-     *
-     * @param url The given URL to save for later.
-     * @param clientId The client ID for the offline page to be saved later.
-     * @param userRequested Whether this request should be prioritized because the user explicitly
-     *     requested it.
-     * @deprecated Use {@link RequestCoordinatorBridge#savePageLater(String, ClientId, boolean)}
-     *         instead.
-     */
-    @Deprecated
-    public void savePageLater(final String url, final ClientId clientId, boolean userRequested) {
-        savePageLater(url, clientId, userRequested, new OfflinePageOrigin());
-    }
-
-    /**
-     * Save the given URL as an offline page when the network becomes available with the given
-     * origin.
-     *
-     * @param url The given URL to save for later
-     * @param clientId The clientId for the offline page to be saved later.
-     * @param userRequested Whether this request should be prioritized because the user explicitly
-     *                      requested it.
-     * @param origin The app that initiated the request.
-     * @deprecated Use {@link RequestCoordinatorBridge#savePageLater(String, ClientId, boolean,
-     *         OfflinePageOrigin)} instead.
-     */
-    @Deprecated
-    public void savePageLater(final String url, final ClientId clientId, boolean userRequested,
-            OfflinePageOrigin origin) {
-        savePageLater(url, clientId, userRequested, origin, null);
-    }
-
-    /**
-     * Save the given URL as an offline page when the network becomes available with the given
-     * origin. Callback with status when done.
-     *
-     * @param url The given URL to save for later.
-     * @param clientId the clientId for the offline page to be saved later.
-     * @param userRequested Whether this request should be prioritized because the user explicitly
-     *                      requested it.
-     * @param origin The app that initiated the request.
-     * @param callback Callback for whether the URL is successfully added to queue. Non-zero number
-     *                 represents a failure reason (See offline_pages::AddRequestResult enum). 0 is
-     * success.
-     * @deprecated Use {@link RequestCoordinatorBridge#savePageLater(String, ClientId, boolean,
-     *         OfflinePageOrigin, Callback<Integer>)} instead.
-     */
-    @Deprecated
-    public void savePageLater(final String url, final ClientId clientId, boolean userRequested,
-            OfflinePageOrigin origin, Callback<Integer> callback) {
-        Callback<Integer> wrapper = new Callback<Integer>() {
-            @Override
-            public void onResult(Integer i) {
-                if (callback != null) {
-                    callback.onResult(i);
-                }
-            }
-        };
-        RequestCoordinatorBridge.getForProfile(mProfile).savePageLater(
-                url, clientId, userRequested, origin, callback);
-    }
-
-    /**
-     * Save the given URL as an offline page when the network becomes available with a randomly
-     * generated clientId in the given namespace. Origin is defaulted to Chrome.
-     *
-     * @param url The given URL to save for later.
-     * @param namespace The namespace for the offline page to be saved later.
-     * @param userRequested Whether this request should be prioritized because the user explicitly
-     *                      requested it.
-     * @deprecated Use {@link RequestCoordinatorBridge#savePageLater(String, String, boolean)}
-     *         instead.
-     */
-    @Deprecated
-    public void savePageLater(final String url, final String namespace, boolean userRequested) {
-        savePageLater(url, namespace, userRequested, new OfflinePageOrigin());
-    }
-
-    /**
-     * Save the given URL as an offline page when the network becomes available with a randomly
-     * generated clientId in the given namespace and the given origin.
-     *
-     * @param url The given URL to save for later
-     * @param namespace The namespace for the offline page to be saved later.
-     * @param userRequested Whether this request should be prioritized because the user explicitly
-     *                      requested it.
-     * @param origin The app that initiated the request.
-     * @deprecated Use {@link RequestCoordinatorBridge#savePageLater(String, String, boolean,
-     *         OfflinePageOrigin)} instead.
-     */
-    @Deprecated
-    public void savePageLater(final String url, final String namespace, boolean userRequested,
-            OfflinePageOrigin origin) {
-        savePageLater(url, namespace, userRequested, origin, null);
-    }
-
-    /**
-     * Save the given URL as an offline page when the network becomes available with a randomly
-     * generated clientId in the given namespace and the given origin. Calls back with whether
-     * the URL has been successfully added to queue.
-     *
-     * @param url The given URL to save for later
-     * @param namespace The namespace for the offline page to be saved later.
-     * @param userRequested Whether this request should be prioritized because the user explicitly
-     *                      requested it.
-     * @param origin The app that initiated the request.
-     * @param callback Callback to call whether the URL is successfully added to the queue. Non-zero
-     *                 number represents failure reason (see offline_pages::AddRequestResult enum).
-     * @deprecated Use {@link RequestCoordinatorBridge#savePageLater(String, String, boolean,
-     *         OfflinePageOrigin, Callback<Integer>)} instead.
-     * 0 is success.
-     */
-    @Deprecated
-    public void savePageLater(final String url, final String namespace, boolean userRequested,
-            OfflinePageOrigin origin, Callback<Integer> callback) {
-        ClientId clientId = ClientId.createGuidClientIdForNamespace(namespace);
-        savePageLater(url, clientId, userRequested, origin, callback);
-    }
-
-    /**
      * Deletes an offline page related to a specified bookmark.
      *
      * @param clientId Client ID for which the offline copy will be deleted.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionViewBinder.java
index a3be117a..9e11de43 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionViewBinder.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser.omnibox.suggestions.answer;
 
+import android.support.v4.view.ViewCompat;
+
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.omnibox.suggestions.SuggestionCommonProperties;
 import org.chromium.chrome.browser.omnibox.suggestions.answer.AnswerSuggestionViewProperties.AnswerIcon;
@@ -37,6 +39,9 @@
                            propertyKey)) {
             view.setLine2AccessibilityDescription(model.get(
                     AnswerSuggestionViewProperties.TEXT_LINE_2_ACCESSIBILITY_DESCRIPTION));
+        } else if (SuggestionCommonProperties.LAYOUT_DIRECTION.equals(propertyKey)) {
+            ViewCompat.setLayoutDirection(
+                    view, model.get(SuggestionCommonProperties.LAYOUT_DIRECTION));
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionViewBinder.java
index 3e0500b..3c6388c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionViewBinder.java
@@ -7,6 +7,7 @@
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
 import android.support.v4.graphics.drawable.DrawableCompat;
+import android.support.v7.content.res.AppCompatResources;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageView;
@@ -60,7 +61,8 @@
             view.setImageBitmap(bitmap);
         } else {
             boolean useDarkColors = model.get(SuggestionCommonProperties.USE_DARK_COLORS);
-            Drawable icon = view.getContext().getResources().getDrawable(R.drawable.ic_globe_24dp);
+            Drawable icon =
+                    AppCompatResources.getDrawable(view.getContext(), R.drawable.ic_globe_24dp);
             int color = view.getContext().getResources().getColor(useDarkColors
                             ? R.color.default_icon_color_secondary_list
                             : R.color.white_mode_tint);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionViewBinder.java
index 44bcf82..ab7043d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionViewBinder.java
@@ -7,6 +7,7 @@
 import android.graphics.Bitmap;
 import android.graphics.Color;
 import android.os.Handler;
+import android.support.v4.view.ViewCompat;
 import android.view.MotionEvent;
 
 import org.chromium.chrome.browser.omnibox.suggestions.SuggestionCommonProperties;
@@ -104,6 +105,9 @@
             applySuggestionImage(model, view);
         } else if (SuggestionCommonProperties.USE_DARK_COLORS.equals(propertyKey)) {
             view.setUseDarkColors(model.get(SuggestionCommonProperties.USE_DARK_COLORS));
+        } else if (SuggestionCommonProperties.LAYOUT_DIRECTION.equals(propertyKey)) {
+            ViewCompat.setLayoutDirection(
+                    view, model.get(SuggestionCommonProperties.LAYOUT_DIRECTION));
         }
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestFactory.java
index 6f56531..a8502a6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestFactory.java
@@ -8,6 +8,8 @@
 
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeFeatureList;
+import org.chromium.chrome.browser.preferences.Pref;
+import org.chromium.chrome.browser.preferences.PrefServiceBridge;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelUtils;
 import org.chromium.components.payments.ErrorStrings;
@@ -118,6 +120,11 @@
         }
 
         @Override
+        public boolean prefsCanMakePayment() {
+            return PrefServiceBridge.getInstance().getBoolean(Pref.CAN_MAKE_PAYMENT_ENABLED);
+        }
+
+        @Override
         public boolean skipUiForBasicCard() {
             return false; // Only tests do this.
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
index 7a59337..0e7d82a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
@@ -34,8 +34,6 @@
 import org.chromium.chrome.browser.payments.ui.SectionInformation;
 import org.chromium.chrome.browser.payments.ui.ShoppingCart;
 import org.chromium.chrome.browser.preferences.MainPreferences;
-import org.chromium.chrome.browser.preferences.Pref;
-import org.chromium.chrome.browser.preferences.PrefServiceBridge;
 import org.chromium.chrome.browser.preferences.PreferencesLauncher;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.ssl.SecurityStateModel;
@@ -132,6 +130,10 @@
          */
         boolean isWebContentsActive(TabModel model, WebContents webContents);
         /**
+         * Returns whether the preferences allow CAN_MAKE_PAYMENT.
+         */
+        boolean prefsCanMakePayment();
+        /**
          * Returns true if the UI can be skipped for "basic-card" scenarios. This will only ever
          * be true in tests.
          */
@@ -1875,7 +1877,7 @@
         mIsCanMakePaymentResponsePending = false;
 
         boolean response = legacyMode ? mHasEnrolledInstrument : mArePaymentMethodsSupported;
-        response &= PrefServiceBridge.getInstance().getBoolean(Pref.CAN_MAKE_PAYMENT_ENABLED);
+        response &= mDelegate.prefsCanMakePayment();
 
         // Only need to enforce query quota in legacy mode. Per-method quota not supported.
         if (legacyMode
@@ -2010,8 +2012,7 @@
         }
 
         // Always return false when can make payment is disabled.
-        mHasEnrolledInstrument &=
-                PrefServiceBridge.getInstance().getBoolean(Pref.CAN_MAKE_PAYMENT_ENABLED);
+        mHasEnrolledInstrument &= mDelegate.prefsCanMakePayment();
 
         int additionalTextResourceId = app.getAdditionalAppTextResourceId();
         if (additionalTextResourceId != 0) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentAppBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentAppBridge.java
index 3307133..a988da2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentAppBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentAppBridge.java
@@ -25,6 +25,7 @@
 import org.chromium.content_public.browser.UiThreadTaskTraits;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.payments.mojom.PaymentDetailsModifier;
+import org.chromium.payments.mojom.PaymentEventResponseType;
 import org.chromium.payments.mojom.PaymentItem;
 import org.chromium.payments.mojom.PaymentMethodData;
 
@@ -257,13 +258,15 @@
                 // Notify closing payment app window so as to abort payment if unsecure.
                 WebContents webContents = tab.getWebContents();
                 if (!SslValidityChecker.isValidPageInPaymentHandlerWindow(webContents)) {
-                    onClosingPaymentAppWindow(webContents);
+                    nativeOnClosingPaymentAppWindow(webContents,
+                            PaymentEventResponseType.PAYMENT_HANDLER_INSECURE_NAVIGATION);
                 }
             }
 
             @Override
             public void onDidAttachInterstitialPage(Tab tab) {
-                onClosingPaymentAppWindow(tab.getWebContents());
+                nativeOnClosingPaymentAppWindow(tab.getWebContents(),
+                        PaymentEventResponseType.PAYMENT_HANDLER_INSECURE_NAVIGATION);
             }
         });
     }
@@ -274,7 +277,8 @@
      * @param webContents The web contents in the opened window.
      */
     public static void onClosingPaymentAppWindow(WebContents webContents) {
-        nativeOnClosingPaymentAppWindow(webContents);
+        nativeOnClosingPaymentAppWindow(
+                webContents, PaymentEventResponseType.PAYMENT_HANDLER_WINDOW_CLOSING);
     }
 
     @CalledByNative
@@ -479,5 +483,5 @@
             String topOrigin, String paymentRequestOrigin, PaymentMethodData[] methodData,
             PaymentDetailsModifier[] modifiers, CanMakePaymentCallback callback);
 
-    private static native void nativeOnClosingPaymentAppWindow(WebContents webContents);
+    private static native void nativeOnClosingPaymentAppWindow(WebContents webContents, int reason);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PrivacyPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PrivacyPreferences.java
index a55626ef..24e28d6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PrivacyPreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PrivacyPreferences.java
@@ -15,7 +15,6 @@
 import android.view.MenuItem;
 
 import org.chromium.base.BuildInfo;
-import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.contextualsearch.ContextualSearchFieldTrial;
@@ -156,7 +155,6 @@
                     (boolean) newValue);
         } else if (PREF_NETWORK_PREDICTIONS.equals(key)) {
             PrefServiceBridge.getInstance().setNetworkPredictionEnabled((boolean) newValue);
-            recordNetworkPredictionEnablingUMA((boolean) newValue);
         } else if (PREF_NAVIGATION_ERROR.equals(key)) {
             PrefServiceBridge.getInstance().setResolveNavigationErrorEnabled((boolean) newValue);
         } else if (PREF_CAN_MAKE_PAYMENT.equals(key)) {
@@ -167,11 +165,6 @@
         return true;
     }
 
-    private void recordNetworkPredictionEnablingUMA(boolean enabled) {
-        // Report user turning on and off NetworkPrediction.
-        RecordHistogram.recordBooleanHistogram("PrefService.NetworkPredictionEnabled", enabled);
-    }
-
     @Override
     public void onResume() {
         super.onResume();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/snackbar/Snackbar.java b/chrome/android/java/src/org/chromium/chrome/browser/snackbar/Snackbar.java
index 9ea8a47..fb10292 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/snackbar/Snackbar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/snackbar/Snackbar.java
@@ -83,6 +83,7 @@
     public static final int UMA_AUTOFILL_ASSISTANT_STOP_UNDO = 29;
     public static final int UMA_TAB_CLOSE_MULTIPLE_UNDO = 30;
     public static final int UMA_SEARCH_ENGINE_CHOICE_NOTIFICATION = 31;
+    public static final int UMA_TAB_GROUP_MANUAL_CREATION_UNDO = 32;
 
     private SnackbarController mController;
     private CharSequence mText;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tasks/tabgroup/TabGroupModelFilter.java b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tabgroup/TabGroupModelFilter.java
index 368b289..037f6ae 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tasks/tabgroup/TabGroupModelFilter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tabgroup/TabGroupModelFilter.java
@@ -82,6 +82,16 @@
          *         moveTab} is in  before ungrouping.
          */
         void didMoveTabOutOfGroup(Tab movedTab, int prevFilterIndex);
+
+        /**
+         * This method is called after a group is created manually by user. Either using the
+         * TabSelectionEditor (Group tab menu item) or using drag and drop.
+         * @param tabs The list of modified {@link Tab}s.
+         * @param tabOriginalIndex The original tab index for each modified tab.
+         * @param isSameGroup Whether the given list is in a group already.
+         */
+        void didCreateGroup(
+                List<Tab> tabs, List<Integer> tabOriginalIndex, boolean isSameGroup);
     }
 
     /**
@@ -279,8 +289,10 @@
                         tabsToMerge.get(tabsToMerge.size() - 1), group.getLastShownTabId());
             }
         } else {
-            mergeListOfTabsToGroup(tabsToMerge, destinationTab);
+            mergeListOfTabsToGroup(tabsToMerge, destinationTab, true, false);
         }
+        // TODO(978508): Send didCreateGroup signal to activate the
+        // {@link UndoGroupSnackbarController}.
     }
 
     /**
@@ -291,22 +303,36 @@
      *
      * @param tabs List of {@link Tab}s to be appended.
      * @param destinationTab The destination {@link Tab} to be append to.
+     * @param isSameGroup Whether the given list of {@link Tab}s belongs in the same group
+     *                    originally.
+     * @param notify Whether or not to notify observers about the merging events.
      */
-    public void mergeListOfTabsToGroup(List<Tab> tabs, Tab destinationTab) {
+    public void mergeListOfTabsToGroup(
+            List<Tab> tabs, Tab destinationTab, boolean isSameGroup, boolean notify) {
         int destinationGroupId = destinationTab.getRootId();
         int destinationIndexInTabModel = getTabModelDestinationIndex(destinationTab);
+        List<Integer> originalIndexes = new ArrayList<>();
 
         for (int i = 0; i < tabs.size(); i++) {
             Tab tab = tabs.get(i);
+            int index = TabModelUtils.getTabIndexById(getTabModel(), tab.getId());
+            assert index != TabModel.INVALID_TAB_INDEX;
+            originalIndexes.add(index);
+
             if (tab.getId() == destinationTab.getId()) continue;
 
-            int index = TabModelUtils.getTabIndexById(getTabModel(), tab.getId());
             boolean isMergingBackward = index < destinationIndexInTabModel;
 
             tab.setRootId(destinationGroupId);
             getTabModel().moveTab(tab.getId(),
                     isMergingBackward ? destinationIndexInTabModel : destinationIndexInTabModel++);
         }
+
+        if (notify) {
+            for (Observer observer : mGroupFilterObserver) {
+                observer.didCreateGroup(tabs, originalIndexes, isSameGroup);
+            }
+        }
     }
 
     /**
@@ -367,6 +393,26 @@
         return firstTabIndexInTabModel != destinationIndexInTabModel;
     }
 
+    /**
+     * This method undo the given grouped {@link Tab}.
+     *
+     * @param tab undo this grouped {@link Tab}.
+     * @param originalIndex The tab index before grouped.
+     * @param originalGroupId The rootId before grouped.
+     */
+    public void undoGroupedTab(Tab tab, int originalIndex, int originalGroupId) {
+        int currentIndex = TabModelUtils.getTabIndexById(getTabModel(), tab.getId());
+        assert currentIndex != TabModel.INVALID_TAB_INDEX;
+
+        tab.setRootId(originalGroupId);
+        if (currentIndex == originalIndex) {
+            didMoveTab(tab, originalIndex, currentIndex);
+        } else {
+            if (currentIndex < originalIndex) originalIndex++;
+            getTabModel().moveTab(tab.getId(), originalIndex);
+        }
+    }
+
     // TODO(crbug.com/951608): follow up with sessions count histogram for TabGroups.
     private int updateAndGetSessionsCount(int groupId) {
         ThreadUtils.assertOnBackgroundThread();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabCountProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabCountProvider.java
index 5a85cc99..86fe4ea 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabCountProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabCountProvider.java
@@ -160,6 +160,12 @@
             public void didMoveTabOutOfGroup(Tab moveTab, int oldFilterIndex) {
                 updateTabCount();
             }
+
+            @Override
+            public void didCreateGroup(
+                    List<Tab> tabs, List<Integer> tabOriginalIndex, boolean isSameGroup) {
+                updateTabCount();
+            }
         };
 
         if (mTabModelSelector.getTabModelFilterProvider().getCurrentTabModelFilter()
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
index 0468a3ec..2593b38b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -935,17 +935,15 @@
      *
      * @param tabModelSelector           The selector that handles tab management.
      * @param controlsVisibilityDelegate The delegate to handle visibility of browser controls.
-     * @param findToolbarManager         The manager for find in page.
      * @param overviewModeBehavior       The overview mode manager.
      * @param layoutManager              A {@link LayoutManager} instance used to watch for scene
      *                                   changes.
      */
     public void initializeWithNative(TabModelSelector tabModelSelector,
             BrowserStateBrowserControlsVisibilityDelegate controlsVisibilityDelegate,
-            FindToolbarManager findToolbarManager, OverviewModeBehavior overviewModeBehavior,
-            LayoutManager layoutManager, OnClickListener tabSwitcherClickHandler,
-            OnClickListener newTabClickHandler, OnClickListener bookmarkClickHandler,
-            OnClickListener customTabsBackClickHandler) {
+            OverviewModeBehavior overviewModeBehavior, LayoutManager layoutManager,
+            OnClickListener tabSwitcherClickHandler, OnClickListener newTabClickHandler,
+            OnClickListener bookmarkClickHandler, OnClickListener customTabsBackClickHandler) {
         assert !mInitializedWithNative;
 
         mTabModelSelector = tabModelSelector;
@@ -971,15 +969,12 @@
         mLocationBarModel.setShouldShowOmniboxInOverviewMode(
                 ReturnToChromeExperimentsUtil.shouldShowOmniboxOnTabSwitcher());
 
-        mFindToolbarManager = findToolbarManager;
 
         assert controlsVisibilityDelegate != null;
         mControlsVisibilityDelegate = controlsVisibilityDelegate;
 
         mNativeLibraryReady = false;
 
-        mFindToolbarManager.addObserver(mFindToolbarObserver);
-
         if (overviewModeBehavior != null) {
             mOverviewModeBehavior = overviewModeBehavior;
             mOverviewModeBehavior.addOverviewModeObserver(mOverviewModeObserver);
@@ -1030,6 +1025,15 @@
     }
 
     /**
+     * Set the {@link FindToolbarManager}.
+     * @param findToolbarManager The manager for find in page.
+     */
+    public void setFindToolbarManager(FindToolbarManager findToolbarManager) {
+        mFindToolbarManager = findToolbarManager;
+        mFindToolbarManager.addObserver(mFindToolbarObserver);
+    }
+
+    /**
      * Show the update badge in both the top and bottom toolbar.
      * TODO(amaralp): Only the top or bottom menu should be visible.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
index 746c121..6c5e472 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
@@ -2702,7 +2702,7 @@
     private void setTabSwitcherAnimationMenuDrawable() {
         mTabSwitcherAnimationMenuDrawable =
                 ApiCompatibilityUtils
-                        .getDrawable(getResources(), R.drawable.ic_more_vert_black_24dp)
+                        .getDrawable(getResources(), R.drawable.ic_more_vert_24dp_on_dark_bg)
                         .mutate();
         ((BitmapDrawable) mTabSwitcherAnimationMenuDrawable).setGravity(Gravity.CENTER);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
index 83ebebba..8fabb70 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
@@ -6,15 +6,25 @@
 
 import android.support.annotation.Nullable;
 
+import org.chromium.base.Callback;
 import org.chromium.base.VisibleForTesting;
+import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.MenuOrKeyboardActionController;
 import org.chromium.chrome.browser.appmenu.AppMenuBlocker;
 import org.chromium.chrome.browser.appmenu.AppMenuCoordinator;
 import org.chromium.chrome.browser.appmenu.AppMenuCoordinatorFactory;
+import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel;
+import org.chromium.chrome.browser.compositor.bottombar.OverlayPanelManager;
+import org.chromium.chrome.browser.compositor.layouts.LayoutManager;
+import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior;
 import org.chromium.chrome.browser.lifecycle.Destroyable;
 import org.chromium.chrome.browser.lifecycle.InflationObserver;
+import org.chromium.chrome.browser.vr.VrModeObserver;
+import org.chromium.chrome.browser.vr.VrModuleProvider;
+import org.chromium.chrome.browser.widget.findinpage.FindToolbarManager;
+import org.chromium.chrome.browser.widget.findinpage.FindToolbarObserver;
 import org.chromium.ui.base.DeviceFormFactor;
 
 /**
@@ -31,6 +41,19 @@
     protected @Nullable AppMenuCoordinator mAppMenuCoordinator;
     private final MenuOrKeyboardActionController mMenuOrKeyboardActionController;
 
+    protected @Nullable FindToolbarManager mFindToolbarManager;
+    private @Nullable FindToolbarObserver mFindToolbarObserver;
+
+    private Callback<LayoutManager> mLayoutManagerSupplierCallback;
+    private OverlayPanelManager mOverlayPanelManager;
+    private OverlayPanelManager.OverlayPanelManagerObserver mOverlayPanelManagerObserver;
+
+    private Callback<OverviewModeBehavior> mOverviewModeBehaviorSupplierCallback;
+    private OverviewModeBehavior mOverviewModeBehavior;
+    private OverviewModeBehavior.OverviewModeObserver mOverviewModeObserver;
+
+    private VrModeObserver mVrModeObserver;
+
     /**
      * Create a new {@link RootUiCoordinator} for the given activity.
      * @param activity The containing {@link ChromeActivity}. TODO(https://crbug.com/931496):
@@ -39,19 +62,42 @@
     public RootUiCoordinator(ChromeActivity activity) {
         mActivity = activity;
         mActivity.getLifecycleDispatcher().register(this);
+
         mMenuOrKeyboardActionController = mActivity.getMenuOrKeyboardActionController();
         mMenuOrKeyboardActionController.registerMenuOrKeyboardActionHandler(this);
+
+        initLayoutManagerSupplierObserver();
+        initOverviewModeSupplierObserver();
     }
 
     @Override
     public void destroy() {
         mMenuOrKeyboardActionController.unregisterMenuOrKeyboardActionHandler(this);
-        mActivity = null;
+
+        mActivity.getLayoutManagerSupplier().removeObserver(mLayoutManagerSupplierCallback);
+        if (mOverlayPanelManager != null) {
+            mOverlayPanelManager.removeObserver(mOverlayPanelManagerObserver);
+        }
+
+        if (mActivity.getOverviewModeBehaviorSupplier() != null) {
+            mActivity.getOverviewModeBehaviorSupplier().removeObserver(
+                    mOverviewModeBehaviorSupplierCallback);
+        }
+        if (mOverviewModeBehavior != null) {
+            mOverviewModeBehavior.removeOverviewModeObserver(mOverviewModeObserver);
+        }
+
         if (mAppMenuCoordinator != null) {
             mAppMenuCoordinator.unregisterAppMenuBlocker(this);
             mAppMenuCoordinator.unregisterAppMenuBlocker(mActivity);
             mAppMenuCoordinator.destroy();
         }
+
+        if (mFindToolbarManager != null) mFindToolbarManager.removeObserver(mFindToolbarObserver);
+
+        if (mVrModeObserver != null) VrModuleProvider.unregisterVrModeObserver(mVrModeObserver);
+
+        mActivity = null;
     }
 
     @Override
@@ -59,37 +105,45 @@
 
     @Override
     public void onPostInflationStartup() {
-        // TODO(https://crbug.com/931496): Revisit this as part of the broader
-        // discussion around activity-specific UI customizations.
-        if (mActivity.supportsAppMenu()) {
-            mAppMenuCoordinator = AppMenuCoordinatorFactory.createAppMenuCoordinator(mActivity,
-                    mActivity.getLifecycleDispatcher(), mActivity.getToolbarManager(), mActivity,
-                    mActivity.getWindow().getDecorView(),
-                    mActivity.getOverviewModeBehaviorSupplier());
-            mActivity.getToolbarManager().onAppMenuInitialized(
-                    mAppMenuCoordinator.getAppMenuHandler(),
-                    mAppMenuCoordinator.getAppMenuPropertiesDelegate());
-            mAppMenuCoordinator.registerAppMenuBlocker(this);
-            mAppMenuCoordinator.registerAppMenuBlocker(mActivity);
-        } else if (mActivity.getToolbarManager() != null) {
-            mActivity.getToolbarManager().getToolbar().disableMenuButton();
-        }
+        initAppMenu();
+        initFindToolbarManager();
+
+        mVrModeObserver = new VrModeObserver() {
+            @Override
+            public void onEnterVr() {
+                mFindToolbarManager.hideToolbar();
+            }
+
+            @Override
+            public void onExitVr() {}
+        };
+        VrModuleProvider.registerVrModeObserver(mVrModeObserver);
     }
 
+    // MenuOrKeyboardActionHandler implementation
+
     @Override
     public boolean onMenuOrKeyboardAction(int id, boolean fromMenu) {
         if (id == R.id.show_menu && mAppMenuCoordinator != null) {
             mAppMenuCoordinator.showAppMenuForKeyboardEvent();
             return true;
+        } else if (id == R.id.find_in_page_id) {
+            if (mFindToolbarManager == null) return false;
+
+            mFindToolbarManager.showToolbar();
+
+            if (fromMenu) {
+                RecordUserAction.record("MobileMenuFindInPage");
+            } else {
+                RecordUserAction.record("MobileShortcutFindInPage");
+            }
+            return true;
         }
 
         return false;
     }
 
-    @VisibleForTesting
-    public AppMenuCoordinator getAppMenuCoordinatorForTesting() {
-        return mAppMenuCoordinator;
-    }
+    // AppMenuBlocker implementation
 
     @Override
     public boolean canShowAppMenu() {
@@ -108,12 +162,132 @@
         }
 
         // Do not show the menu if we are in find in page view.
-        if (mActivity.getFindToolbarManager() != null
-                && mActivity.getFindToolbarManager().isShowing()
+        if (mFindToolbarManager != null && mFindToolbarManager.isShowing()
                 && !DeviceFormFactor.isNonMultiDisplayContextOnTablet(mActivity)) {
             return false;
         }
 
         return true;
     }
+
+    // Private class methods
+
+    private void initLayoutManagerSupplierObserver() {
+        mLayoutManagerSupplierCallback = layoutManager -> {
+            if (mOverlayPanelManager != null) {
+                mOverlayPanelManager.removeObserver(mOverlayPanelManagerObserver);
+            }
+            mOverlayPanelManager = layoutManager.getOverlayPanelManager();
+
+            if (mOverlayPanelManagerObserver == null) {
+                mOverlayPanelManagerObserver =
+                        new OverlayPanelManager.OverlayPanelManagerObserver() {
+                            @Override
+                            public void onOverlayPanelShown() {
+                                if (mFindToolbarManager != null) {
+                                    mFindToolbarManager.hideToolbar(false);
+                                }
+                            }
+
+                            @Override
+                            public void onOverlayPanelHidden() {}
+                        };
+            }
+
+            mOverlayPanelManager.addObserver(mOverlayPanelManagerObserver);
+        };
+        mActivity.getLayoutManagerSupplier().addObserver(mLayoutManagerSupplierCallback);
+    }
+
+    private void initOverviewModeSupplierObserver() {
+        if (mActivity.getOverviewModeBehaviorSupplier() != null) {
+            mOverviewModeBehaviorSupplierCallback = overviewModeBehavior -> {
+                if (mOverviewModeBehavior != null) {
+                    mOverviewModeBehavior.removeOverviewModeObserver(mOverviewModeObserver);
+                }
+
+                mOverviewModeBehavior = overviewModeBehavior;
+
+                if (mOverviewModeObserver == null) {
+                    mOverviewModeObserver = new OverviewModeBehavior.OverviewModeObserver() {
+                        @Override
+                        public void onOverviewModeStartedShowing(boolean showToolbar) {
+                            if (mFindToolbarManager != null) mFindToolbarManager.hideToolbar();
+                        }
+
+                        @Override
+                        public void onOverviewModeFinishedShowing() {}
+
+                        @Override
+                        public void onOverviewModeStartedHiding(
+                                boolean showToolbar, boolean delayAnimation) {}
+
+                        @Override
+                        public void onOverviewModeFinishedHiding() {}
+                    };
+                }
+                mOverviewModeBehavior.addOverviewModeObserver(mOverviewModeObserver);
+            };
+            mActivity.getOverviewModeBehaviorSupplier().addObserver(
+                    mOverviewModeBehaviorSupplierCallback);
+        }
+    }
+
+    private void initAppMenu() {
+        // TODO(https://crbug.com/931496): Revisit this as part of the broader
+        // discussion around activity-specific UI customizations.
+        if (mActivity.supportsAppMenu()) {
+            mAppMenuCoordinator = AppMenuCoordinatorFactory.createAppMenuCoordinator(mActivity,
+                    mActivity.getLifecycleDispatcher(), mActivity.getToolbarManager(), mActivity,
+                    mActivity.getWindow().getDecorView(),
+                    mActivity.getOverviewModeBehaviorSupplier());
+            mActivity.getToolbarManager().onAppMenuInitialized(
+                    mAppMenuCoordinator.getAppMenuHandler(),
+                    mAppMenuCoordinator.getAppMenuPropertiesDelegate());
+            mAppMenuCoordinator.registerAppMenuBlocker(this);
+            mAppMenuCoordinator.registerAppMenuBlocker(mActivity);
+        } else if (mActivity.getToolbarManager() != null) {
+            mActivity.getToolbarManager().getToolbar().disableMenuButton();
+        }
+    }
+
+    private void initFindToolbarManager() {
+        if (!mActivity.supportsFindInPage()) return;
+
+        int stubId = R.id.find_toolbar_stub;
+        if (DeviceFormFactor.isNonMultiDisplayContextOnTablet(mActivity)) {
+            stubId = R.id.find_toolbar_tablet_stub;
+        }
+        mFindToolbarManager = new FindToolbarManager(mActivity.findViewById(stubId),
+                mActivity.getTabModelSelector(), mActivity.getWindowAndroid(),
+                mActivity.getToolbarManager().getActionModeControllerCallback());
+
+        mFindToolbarObserver = new FindToolbarObserver() {
+            @Override
+            public void onFindToolbarShown() {
+                if (mActivity.getContextualSearchManager() != null) {
+                    mActivity.getContextualSearchManager().hideContextualSearch(
+                            OverlayPanel.StateChangeReason.UNKNOWN);
+                }
+                if (mActivity.getEphemeralTabPanel() != null) {
+                    mActivity.getEphemeralTabPanel().closePanel(
+                            OverlayPanel.StateChangeReason.UNKNOWN, true);
+                }
+            }
+
+            @Override
+            public void onFindToolbarHidden() {}
+        };
+
+        mFindToolbarManager.addObserver(mFindToolbarObserver);
+
+        mActivity.getToolbarManager().setFindToolbarManager(mFindToolbarManager);
+    }
+
+    // Testing methods
+
+    @VisibleForTesting
+    public AppMenuCoordinator getAppMenuCoordinatorForTesting() {
+        return mAppMenuCoordinator;
+    }
 }
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 f95f5ac5..09a30313 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
@@ -317,8 +317,8 @@
                 (ViewGroup) findViewById(android.R.id.content),
                 (ToolbarControlContainer) findViewById(R.id.control_container));
         getToolbarManager().initializeWithNative(getTabModelSelector(),
-                getFullscreenManager().getBrowserVisibilityDelegate(), getFindToolbarManager(),
-                null, layoutDriver, null, null, null, view -> onToolbarCloseButtonClicked());
+                getFullscreenManager().getBrowserVisibilityDelegate(), null, layoutDriver, null,
+                null, null, view -> onToolbarCloseButtonClicked());
         getToolbarManager().setShowTitle(true);
         getToolbarManager().setCloseButtonDrawable(null); // Hides close button.
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webauth/AuthenticatorImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/webauth/AuthenticatorImpl.java
index 45d9479..19187e7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webauth/AuthenticatorImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webauth/AuthenticatorImpl.java
@@ -26,7 +26,7 @@
 /**
  * Android implementation of the authenticator.mojom interface.
  */
-public class AuthenticatorImpl extends EmptyHandlerResponseCallback implements Authenticator {
+public class AuthenticatorImpl extends HandlerResponseCallback implements Authenticator {
     private final RenderFrameHost mRenderFrameHost;
     private final WebContents mWebContents;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webauth/EmptyHandlerResponseCallback.java b/chrome/android/java/src/org/chromium/chrome/browser/webauth/EmptyHandlerResponseCallback.java
index 9c53c5ef..a657b05 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webauth/EmptyHandlerResponseCallback.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webauth/EmptyHandlerResponseCallback.java
@@ -11,7 +11,7 @@
  * Empty HandlerResponseCallback Temporarily used for landing CLs for
  * IsUserVerifyingPlatformAuthenticatorAvailable error response.
  */
-public class EmptyHandlerResponseCallback implements HandlerResponseCallback {
+public class EmptyHandlerResponseCallback extends HandlerResponseCallback {
     @Override
     public void onRegisterResponse(Integer status, MakeCredentialAuthenticatorResponse response){};
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webauth/HandlerResponseCallback.java b/chrome/android/java/src/org/chromium/chrome/browser/webauth/HandlerResponseCallback.java
index 35c67b06..f90efbe6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webauth/HandlerResponseCallback.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webauth/HandlerResponseCallback.java
@@ -10,27 +10,27 @@
 /**
  * Callback for receiving responses from an internal handler.
  */
-public interface HandlerResponseCallback {
+public class HandlerResponseCallback {
     /**
-     * Interface that returns the response from a request to register a
+     * Callback for handling the response from a request to register a
      * credential with an authenticator.
      */
-    void onRegisterResponse(Integer status, MakeCredentialAuthenticatorResponse response);
+    public void onRegisterResponse(Integer status, MakeCredentialAuthenticatorResponse response){};
 
     /**
-     * Interface that returns the response from a request to use a credential
+     * Callback for handling the response from a request to use a credential
      * to produce a signed assertion.
      */
-    void onSignResponse(Integer status, GetAssertionAuthenticatorResponse response);
+    public void onSignResponse(Integer status, GetAssertionAuthenticatorResponse response){};
 
     /**
-     * Interface that returns the response from a request to call
+     * Callback for handling response from a request to call
      * isUserVerifyingPlatformAuthenticatorAvailable.
      */
-    void onIsUserVerifyingPlatformAuthenticatorAvailableResponse(boolean isUVPAA);
+    public void onIsUserVerifyingPlatformAuthenticatorAvailableResponse(boolean isUVPAA){};
 
     /**
-     * Interface that returns any errors from either register or sign requests.
+     * Callback for handling any errors from either register or sign requests.
      */
-    void onError(Integer status);
+    public void onError(Integer status){};
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/findinpage/FindToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/findinpage/FindToolbarManager.java
index 2311864..582c6dd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/findinpage/FindToolbarManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/findinpage/FindToolbarManager.java
@@ -9,26 +9,33 @@
 import android.view.ViewStub;
 
 import org.chromium.base.ObserverList;
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.tabmodel.TabModelSelector;
+import org.chromium.ui.base.WindowAndroid;
 
 /**
  * Manages the interactions with the find toolbar.
  */
 public class FindToolbarManager {
     private FindToolbar mFindToolbar;
-    private final ChromeActivity mActivity;
+    private final ViewStub mFindToolbarStub;
+    private final TabModelSelector mTabModelSelector;
+    private final WindowAndroid mWindowAndroid;
     private final ActionMode.Callback mCallback;
     private final ObserverList<FindToolbarObserver> mObservers;
 
     /**
      * Creates an instance of a {@link FindToolbarManager}.
-     * @param activity The ChromeActivity that contains the {@link FindToolbar}.
+     * @param findToolbarStub The {@link ViewStub} where for the find toolbar.
+     * @param tabModelSelector The {@link TabModelSelector} for the containing activity.
+     * @param windowAndroid The {@link WindowAndroid} for the containing activity.
      * @param callback The ActionMode.Callback that will be used when selection occurs on the
      *         {@link FindToolbar}.
      */
-    public FindToolbarManager(ChromeActivity activity, ActionMode.Callback callback) {
-        mActivity = activity;
+    public FindToolbarManager(ViewStub findToolbarStub, TabModelSelector tabModelSelector,
+            WindowAndroid windowAndroid, ActionMode.Callback callback) {
+        mFindToolbarStub = findToolbarStub;
+        mTabModelSelector = tabModelSelector;
+        mWindowAndroid = windowAndroid;
         mCallback = callback;
         mObservers = new ObserverList<FindToolbarObserver>();
     }
@@ -62,13 +69,9 @@
      */
     public void showToolbar() {
         if (mFindToolbar == null) {
-            int stubId = R.id.find_toolbar_stub;
-            if (mActivity.isTablet()) {
-                stubId = R.id.find_toolbar_tablet_stub;
-            }
-            mFindToolbar = (FindToolbar) ((ViewStub) mActivity.findViewById(stubId)).inflate();
-            mFindToolbar.setTabModelSelector(mActivity.getTabModelSelector());
-            mFindToolbar.setWindowAndroid(mActivity.getWindowAndroid());
+            mFindToolbar = (FindToolbar) mFindToolbarStub.inflate();
+            mFindToolbar.setTabModelSelector(mTabModelSelector);
+            mFindToolbar.setWindowAndroid(mWindowAndroid);
             mFindToolbar.setActionModeCallbackForTextEdit(mCallback);
             mFindToolbar.setObserver(new FindToolbarObserver() {
                 @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/findinpage/FindToolbarObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/findinpage/FindToolbarObserver.java
index a09d6f6..f0c0e1d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/findinpage/FindToolbarObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/findinpage/FindToolbarObserver.java
@@ -7,18 +7,14 @@
 /**
  * Observer for find in page actions.
  */
-public class FindToolbarObserver {
-
+public interface FindToolbarObserver {
     /**
      * Notified when the find in page toolbar has been shown.
      */
-    public void onFindToolbarShown() {
-    }
+    void onFindToolbarShown();
 
     /**
      * Notified when the find in page toolbar has been hidden.
      */
-    public void onFindToolbarHidden() {
-    }
-
+    void onFindToolbarHidden();
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListToolbar.java
index fc5c7c2..117758d1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListToolbar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListToolbar.java
@@ -11,6 +11,7 @@
 import android.graphics.drawable.Drawable;
 import android.support.annotation.CallSuper;
 import android.support.annotation.StringRes;
+import android.support.v4.graphics.drawable.DrawableCompat;
 import android.support.v4.view.ViewCompat;
 import android.support.v7.content.res.AppCompatResources;
 import android.support.v7.widget.Toolbar;
@@ -47,6 +48,7 @@
 import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
 import org.chromium.chrome.browser.widget.selection.SelectionDelegate.SelectionObserver;
 import org.chromium.ui.KeyboardVisibilityDelegate;
+import org.chromium.ui.UiUtils;
 
 import java.util.List;
 
@@ -98,9 +100,9 @@
     private boolean mUpdateStatusBarColor;
 
     protected NumberRollView mNumberRollView;
-    private TintedDrawable mNormalMenuButton;
-    private TintedDrawable mSelectionMenuButton;
-    private TintedDrawable mNavigationIconDrawable;
+    private Drawable mNormalMenuButton;
+    private Drawable mSelectionMenuButton;
+    private Drawable mNavigationIconDrawable;
 
     private int mNavigationButton;
     private int mTitleResId;
@@ -199,12 +201,12 @@
 
         // TODO(twellington): add the concept of normal & selected tint to apply to all toolbar
         //                    buttons.
-        mNormalMenuButton = TintedDrawable.constructTintedDrawable(
-                getContext(), R.drawable.ic_more_vert_black_24dp);
-        mSelectionMenuButton = TintedDrawable.constructTintedDrawable(getContext(),
-                R.drawable.ic_more_vert_black_24dp, R.color.default_icon_color_inverse);
-        mNavigationIconDrawable = TintedDrawable.constructTintedDrawable(
-                getContext(), R.drawable.ic_arrow_back_white_24dp);
+        mNormalMenuButton = UiUtils.getTintedDrawable(
+                getContext(), R.drawable.ic_more_vert_24dp, R.color.standard_mode_tint);
+        mSelectionMenuButton = UiUtils.getTintedDrawable(
+                getContext(), R.drawable.ic_more_vert_24dp, R.color.default_icon_color_inverse);
+        mNavigationIconDrawable = UiUtils.getTintedDrawable(
+                getContext(), R.drawable.ic_arrow_back_white_24dp, R.color.standard_mode_tint);
 
         VrModuleProvider.registerVrModeObserver(this);
         if (VrModuleProvider.getDelegate().isInVr()) onEnterVr();
@@ -217,8 +219,8 @@
         // Will not be needed after a tint is applied to all toolbar buttons.
         MenuItem extraMenuItem = getMenu().findItem(mExtraMenuItemId);
         if (extraMenuItem != null) {
-            Drawable iconDrawable = TintedDrawable.constructTintedDrawable(
-                    getContext(), R.drawable.ic_more_vert_black_24dp, R.color.standard_mode_tint);
+            Drawable iconDrawable = UiUtils.getTintedDrawable(
+                    getContext(), R.drawable.ic_more_vert_24dp, R.color.standard_mode_tint);
             extraMenuItem.setIcon(iconDrawable);
         }
     }
@@ -368,11 +370,11 @@
             case NAVIGATION_BUTTON_NONE:
                 break;
             case NAVIGATION_BUTTON_BACK:
-                mNavigationIconDrawable.setTint(mDarkIconColorList);
+                DrawableCompat.setTintList(mNavigationIconDrawable, mDarkIconColorList);
                 contentDescriptionId = R.string.accessibility_toolbar_btn_back;
                 break;
             case NAVIGATION_BUTTON_SELECTION_BACK:
-                mNavigationIconDrawable.setTint(mLightIconColorList);
+                DrawableCompat.setTintList(mNavigationIconDrawable, mLightIconColorList);
                 contentDescriptionId = R.string.accessibility_cancel_selection;
                 break;
             default:
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeTest.java
index d79f553..b4d82e3 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeTest.java
@@ -34,7 +34,6 @@
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.components.offlinepages.DeletePageResult;
 import org.chromium.components.offlinepages.SavePageResult;
-import org.chromium.components.offlinepages.background.UpdateRequestResult;
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
@@ -177,40 +176,6 @@
     @Test
     @MediumTest
     @RetryOnFailure
-    public void testGetRequestsInQueue() throws Exception {
-        String url = "https://www.google.com/";
-        String namespace = "custom_tabs";
-        savePageLater(url, namespace);
-        SavePageRequest[] requests = OfflineTestUtil.getRequestsInQueue();
-        Assert.assertEquals(1, requests.length);
-        Assert.assertEquals(namespace, requests[0].getClientId().getNamespace());
-        Assert.assertEquals(url, requests[0].getUrl());
-
-        String url2 = "https://mail.google.com/";
-        String namespace2 = "last_n";
-        savePageLater(url2, namespace2);
-        requests = OfflineTestUtil.getRequestsInQueue();
-        Assert.assertEquals(2, requests.length);
-
-        HashSet<String> expectedUrls = new HashSet<>();
-        expectedUrls.add(url);
-        expectedUrls.add(url2);
-
-        HashSet<String> expectedNamespaces = new HashSet<>();
-        expectedNamespaces.add(namespace);
-        expectedNamespaces.add(namespace2);
-
-        for (SavePageRequest request : requests) {
-            Assert.assertTrue(expectedNamespaces.contains(request.getClientId().getNamespace()));
-            expectedNamespaces.remove(request.getClientId().getNamespace());
-            Assert.assertTrue(expectedUrls.contains(request.getUrl()));
-            expectedUrls.remove(request.getUrl());
-        }
-    }
-
-    @Test
-    @MediumTest
-    @RetryOnFailure
     public void testOfflinePageBridgeDisabledInIncognito() throws Exception {
         initializeBridgeForProfile(true);
         Assert.assertEquals(null, mOfflinePageBridge);
@@ -218,35 +183,6 @@
 
     @Test
     @MediumTest
-    @RetryOnFailure
-    public void testRemoveRequestsFromQueue() throws Exception {
-        String url = "https://www.google.com/";
-        String namespace = "custom_tabs";
-        savePageLater(url, namespace);
-
-        String url2 = "https://mail.google.com/";
-        String namespace2 = "last_n";
-        savePageLater(url2, namespace2);
-
-        SavePageRequest[] requests = OfflineTestUtil.getRequestsInQueue();
-        Assert.assertEquals(2, requests.length);
-
-        List<Long> requestsToRemove = new ArrayList<>();
-        requestsToRemove.add(Long.valueOf(requests[1].getRequestId()));
-
-        List<OfflinePageBridge.RequestRemovedResult> removed =
-                removeRequestsFromQueue(requestsToRemove);
-        Assert.assertEquals(requests[1].getRequestId(), removed.get(0).getRequestId());
-        Assert.assertEquals(UpdateRequestResult.SUCCESS, removed.get(0).getUpdateRequestResult());
-        SavePageRequest[] remaining = OfflineTestUtil.getRequestsInQueue();
-        Assert.assertEquals(1, remaining.length);
-
-        Assert.assertEquals(requests[0].getRequestId(), remaining[0].getRequestId());
-        Assert.assertEquals(requests[0].getUrl(), remaining[0].getUrl());
-    }
-
-    @Test
-    @MediumTest
     public void testDeletePagesByOfflineIds() throws Exception {
         // Save 3 pages and record their offline IDs to delete later.
         Set<String> pageUrls = new HashSet<>();
@@ -559,43 +495,6 @@
         return result;
     }
 
-    private void savePageLater(final String url, final String namespace)
-            throws InterruptedException {
-        final Semaphore semaphore = new Semaphore(0);
-        PostTask.runOrPostTask(UiThreadTaskTraits.DEFAULT, () -> {
-            mOfflinePageBridge.savePageLater(url, namespace, true /* userRequested */,
-                    new OfflinePageOrigin(), new Callback<Integer>() {
-                        @Override
-                        public void onResult(Integer i) {
-                            Assert.assertEquals("SavePageLater did not succeed", Integer.valueOf(0),
-                                    i); // 0 is SUCCESS
-                            semaphore.release();
-                        }
-                    });
-        });
-        Assert.assertTrue(semaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-    }
-
-    private List<OfflinePageBridge.RequestRemovedResult> removeRequestsFromQueue(
-            final List<Long> requestsToRemove) throws InterruptedException {
-        final AtomicReference<List<OfflinePageBridge.RequestRemovedResult>> ref =
-                new AtomicReference<>();
-        final Semaphore semaphore = new Semaphore(0);
-        PostTask.runOrPostTask(UiThreadTaskTraits.DEFAULT, () -> {
-            mOfflinePageBridge.removeRequestsFromQueue(
-                    requestsToRemove, new Callback<List<OfflinePageBridge.RequestRemovedResult>>() {
-                        @Override
-                        public void onResult(
-                                List<OfflinePageBridge.RequestRemovedResult> removedRequests) {
-                            ref.set(removedRequests);
-                            semaphore.release();
-                        }
-                    });
-        });
-        Assert.assertTrue(semaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-        return ref.get();
-    }
-
     private LoadUrlParams getLoadUrlParamsForOpeningMhtmlFileOrContent(String url)
             throws InterruptedException {
         final AtomicReference<LoadUrlParams> ref = new AtomicReference<>();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCanMakePaymentMetricsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCanMakePaymentMetricsTest.java
index 39ba99ea..07ff494 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCanMakePaymentMetricsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCanMakePaymentMetricsTest.java
@@ -84,7 +84,7 @@
         // Make sure the canMakePayment events were logged correctly.
         int expectedSample = Event.SHOWN | Event.USER_ABORTED | Event.CAN_MAKE_PAYMENT_TRUE
                 | Event.HAS_ENROLLED_INSTRUMENT_FALSE | Event.REQUEST_METHOD_BASIC_CARD
-                | Event.REQUEST_METHOD_OTHER;
+                | Event.REQUEST_METHOD_OTHER | Event.NEEDS_COMPLETION_PAYMENT;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
@@ -127,7 +127,8 @@
         int expectedSample = Event.SHOWN | Event.PAY_CLICKED | Event.RECEIVED_INSTRUMENT_DETAILS
                 | Event.COMPLETED | Event.CAN_MAKE_PAYMENT_TRUE
                 | Event.HAS_ENROLLED_INSTRUMENT_FALSE | Event.REQUEST_METHOD_BASIC_CARD
-                | Event.REQUEST_METHOD_OTHER | Event.SELECTED_CREDIT_CARD;
+                | Event.REQUEST_METHOD_OTHER | Event.SELECTED_CREDIT_CARD
+                | Event.NEEDS_COMPLETION_PAYMENT;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
@@ -250,7 +251,7 @@
 
         // Make sure no canMakePayment events were logged.
         int expectedSample = Event.SHOWN | Event.USER_ABORTED | Event.REQUEST_METHOD_BASIC_CARD
-                | Event.REQUEST_METHOD_OTHER;
+                | Event.REQUEST_METHOD_OTHER | Event.NEEDS_COMPLETION_PAYMENT;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestJourneyLoggerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestJourneyLoggerTest.java
index 622c53c..64172def 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestJourneyLoggerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestJourneyLoggerTest.java
@@ -616,9 +616,11 @@
         mPaymentRequestTestRule.expectResultContains(
                 new String[] {"User closed the Payment Request UI."});
 
-        // Make sure the events were logged correctly.
+        // Make sure the events were logged correctly. Since the added credit card is using the same
+        // incomplete profile for billing address, NEEDS_COMPLETION_PAYMENT is also set.
         int expectedSample = Event.SHOWN | Event.USER_ABORTED | Event.HAD_INITIAL_FORM_OF_PAYMENT
-                | Event.REQUEST_SHIPPING | Event.REQUEST_METHOD_BASIC_CARD;
+                | Event.REQUEST_SHIPPING | Event.REQUEST_METHOD_BASIC_CARD
+                | Event.NEEDS_COMPLETION_PAYMENT | Event.NEEDS_COMPLETION_SHIPPING;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
@@ -652,7 +654,8 @@
 
         // Make sure the events were logged correctly.
         int expectedSample = Event.SHOWN | Event.USER_ABORTED | Event.HAD_INITIAL_FORM_OF_PAYMENT
-                | Event.REQUEST_SHIPPING | Event.REQUEST_METHOD_BASIC_CARD;
+                | Event.REQUEST_SHIPPING | Event.REQUEST_METHOD_BASIC_CARD
+                | Event.NEEDS_COMPLETION_PAYMENT;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
@@ -687,7 +690,7 @@
 
         // Make sure the events were logged correctly.
         int expectedSample = Event.SHOWN | Event.USER_ABORTED | Event.REQUEST_SHIPPING
-                | Event.REQUEST_METHOD_BASIC_CARD;
+                | Event.REQUEST_METHOD_BASIC_CARD | Event.NEEDS_COMPLETION_PAYMENT;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
@@ -720,7 +723,7 @@
 
         // Make sure the events were logged correctly.
         int expectedSample = Event.SHOWN | Event.USER_ABORTED | Event.REQUEST_SHIPPING
-                | Event.REQUEST_METHOD_BASIC_CARD;
+                | Event.REQUEST_METHOD_BASIC_CARD | Event.NEEDS_COMPLETION_PAYMENT;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
@@ -753,7 +756,8 @@
 
         // Make sure the events were logged correctly.
         int expectedSample = Event.SHOWN | Event.USER_ABORTED | Event.REQUEST_SHIPPING
-                | Event.REQUEST_METHOD_BASIC_CARD | Event.REQUEST_METHOD_OTHER;
+                | Event.REQUEST_METHOD_BASIC_CARD | Event.REQUEST_METHOD_OTHER
+                | Event.NEEDS_COMPLETION_PAYMENT;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
@@ -818,10 +822,12 @@
         mPaymentRequestTestRule.expectResultContains(
                 new String[] {"User closed the Payment Request UI."});
 
-        // Make sure the events were logged correctly.
+        // Make sure the events were logged correctly. Since the added credit card is using the same
+        // incomplete profile, NEEDS_COMPLETION_PAYMENT is also set.
         int expectedSample = Event.SHOWN | Event.USER_ABORTED | Event.HAD_INITIAL_FORM_OF_PAYMENT
                 | Event.REQUEST_SHIPPING | Event.REQUEST_METHOD_BASIC_CARD
-                | Event.REQUEST_METHOD_OTHER;
+                | Event.REQUEST_METHOD_OTHER | Event.NEEDS_COMPLETION_PAYMENT
+                | Event.NEEDS_COMPLETION_SHIPPING;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
@@ -960,7 +966,8 @@
 
         // Make sure the events were logged correctly.
         int expectedSample = Event.SHOWN | Event.USER_ABORTED | Event.REQUEST_SHIPPING
-                | Event.REQUEST_METHOD_BASIC_CARD | Event.REQUEST_METHOD_OTHER;
+                | Event.REQUEST_METHOD_BASIC_CARD | Event.REQUEST_METHOD_OTHER
+                | Event.NEEDS_COMPLETION_PAYMENT;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tasks/tabgroup/TabGroupModelFilterUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tasks/tabgroup/TabGroupModelFilterUnitTest.java
index 4705c96..7d862df2 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/tasks/tabgroup/TabGroupModelFilterUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/tasks/tabgroup/TabGroupModelFilterUnitTest.java
@@ -6,6 +6,7 @@
 
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertThat;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -463,7 +464,7 @@
                 new ArrayList<>(Arrays.asList(mTab2, mTab3, mTab5, mTab6, mTab1, mTab4));
         List<Tab> tabsToMerge = new ArrayList<>(Arrays.asList(mTab1, mTab4));
 
-        mTabGroupModelFilter.mergeListOfTabsToGroup(tabsToMerge, mTab5);
+        mTabGroupModelFilter.mergeListOfTabsToGroup(tabsToMerge, mTab5, false, false);
 
         verify(mTabModel).moveTab(mTab1.getId(), POSITION6 + 1);
         verify(mTabModel).moveTab(mTab4.getId(), POSITION6 + 1);
@@ -479,7 +480,7 @@
         List<Tab> expectedTabModel =
                 new ArrayList<>(Arrays.asList(mTab1, mTab4, newTab, mTab2, mTab3, mTab5, mTab6));
 
-        mTabGroupModelFilter.mergeListOfTabsToGroup(tabsToMerge, mTab1);
+        mTabGroupModelFilter.mergeListOfTabsToGroup(tabsToMerge, mTab1, false, false);
 
         verify(mTabModel).moveTab(mTab4.getId(), POSITION1 + 1);
         verify(mTabModel).moveTab(newTab.getId(), POSITION1 + 2);
@@ -495,7 +496,7 @@
         List<Tab> expectedTabModel =
                 new ArrayList<>(Arrays.asList(mTab2, mTab3, mTab4, mTab1, newTab, mTab5, mTab6));
 
-        mTabGroupModelFilter.mergeListOfTabsToGroup(tabsToMerge, mTab4);
+        mTabGroupModelFilter.mergeListOfTabsToGroup(tabsToMerge, mTab4, false, false);
 
         verify(mTabModel).moveTab(mTab1.getId(), POSITION4 + 1);
         verify(mTabModel).moveTab(newTab.getId(), POSITION4 + 1);
@@ -551,4 +552,74 @@
         verify(mTabGroupModelFilterObserver, never())
                 .didMoveTabGroup(any(Tab.class), anyInt(), anyInt());
     }
+
+    @Test
+    public void undoGroupedTab_NoUpdateTabModel() {
+        List<Tab> expectedTabModel =
+                new ArrayList<>(Arrays.asList(mTab1, mTab2, mTab3, mTab4, mTab5, mTab6));
+
+        // Simulate we just grouped mTab4 with mTab2 and mTab3
+        doReturn(TAB2_ROOT_ID).when(mTab4).getRootId();
+        mTabGroupModelFilter.resetFilterState();
+        assertThat(mTab4.getRootId(), equalTo(TAB2_ROOT_ID));
+        assertThat(mTabGroupModelFilter.indexOf(mTab4), equalTo(1));
+
+        // Undo the grouped action
+        mTabGroupModelFilter.undoGroupedTab(mTab4, POSITION4, TAB4_ROOT_ID);
+
+        assertArrayEquals(mTabs.toArray(), expectedTabModel.toArray());
+        assertThat(mTab4.getRootId(), equalTo(TAB4_ROOT_ID));
+        assertThat(mTabGroupModelFilter.indexOf(mTab4), equalTo(2));
+    }
+
+    @Test
+    public void undoGroupedTab_Forward_UpdateTabModel() {
+        List<Tab> expectedTabModel =
+                new ArrayList<>(Arrays.asList(mTab1, mTab2, mTab3, mTab4, mTab5, mTab6));
+
+        // Simulate we just grouped mTab1 with mTab4
+        doReturn(TAB4_ROOT_ID).when(mTab1).getRootId();
+        mTabModel.moveTab(mTab1.getId(), POSITION4 + 1);
+        mTabGroupModelFilter.resetFilterState();
+        assertThat(mTab1.getRootId(), equalTo(TAB4_ROOT_ID));
+        assertThat(mTabGroupModelFilter.indexOf(mTab1), equalTo(1));
+        assertFalse(Arrays.equals(mTabs.toArray(), expectedTabModel.toArray()));
+
+        // Undo the grouped action.
+        mTabGroupModelFilter.undoGroupedTab(mTab1, POSITION1, TAB1_ROOT_ID);
+
+        assertArrayEquals(mTabs.toArray(), expectedTabModel.toArray());
+        assertThat(mTab1.getRootId(), equalTo(TAB1_ROOT_ID));
+        assertThat(mTabGroupModelFilter.indexOf(mTab1), equalTo(0));
+    }
+
+    @Test
+    public void undoGroupedTab_Backward_UpdateTabModel() {
+        List<Tab> expectedTabModel =
+                new ArrayList<>(Arrays.asList(mTab1, mTab2, mTab3, mTab4, mTab5, mTab6));
+
+        // Simulate we just grouped mTab4 with mTab1
+        doReturn(TAB1_ROOT_ID).when(mTab4).getRootId();
+        mTabModel.moveTab(mTab4.getId(), POSITION1 + 1);
+        mTabGroupModelFilter.resetFilterState();
+        assertThat(mTab4.getRootId(), equalTo(TAB1_ROOT_ID));
+        assertThat(mTabGroupModelFilter.indexOf(mTab4), equalTo(0));
+        assertFalse(Arrays.equals(mTabs.toArray(), expectedTabModel.toArray()));
+
+        // Undo the grouped action.
+        mTabGroupModelFilter.undoGroupedTab(mTab4, POSITION4, TAB4_ROOT_ID);
+
+        assertArrayEquals(mTabs.toArray(), expectedTabModel.toArray());
+        assertThat(mTab4.getRootId(), equalTo(TAB4_ROOT_ID));
+        assertThat(mTabGroupModelFilter.indexOf(mTab4), equalTo(2));
+    }
+
+    @Test(expected = AssertionError.class)
+    public void undoGroupedTab_AssertTest() {
+        // Simulate mTab6 is not in TabModel.
+        doReturn(5).when(mTabModel).getCount();
+
+        // Undo the grouped action.
+        mTabGroupModelFilter.undoGroupedTab(mTab6, POSITION1, TAB1_ROOT_ID);
+    }
 }
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index bd362a3..7fc3f33 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-77.0.3836.0_rc-r1-merged.afdo.bz2
\ No newline at end of file
+chromeos-chrome-amd64-77.0.3841.0_rc-r1-merged.afdo.bz2
\ No newline at end of file
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/DinoActivity.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/DinoActivity.java
index 7d437b60..7b2ba47 100644
--- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/DinoActivity.java
+++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/DinoActivity.java
@@ -88,6 +88,11 @@
     protected void initializeToolbar() {}
 
     @Override
+    public boolean supportsFindInPage() {
+        return false;
+    }
+
+    @Override
     protected ChromeFullscreenManager createFullscreenManager() {
         return new ChromeFullscreenManager(this,
                 () -> mBrowserControlsOffsetHelper, ChromeFullscreenManager.ControlsPosition.NONE);
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/NoTouchActivity.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/NoTouchActivity.java
index 97ac9d9..b19b965 100644
--- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/NoTouchActivity.java
+++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/NoTouchActivity.java
@@ -205,6 +205,13 @@
     @Override
     protected void initializeToolbar() {}
 
+    /**
+     * @return  Whether this activity supports the find in page page
+     */
+    public boolean supportsFindInPage() {
+        return false;
+    }
+
     @Override
     protected ChromeFullscreenManager createFullscreenManager() {
         return new ChromeFullscreenManager(this,
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 5d787f3..239951c 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -484,12 +484,12 @@
     "external_protocol/external_protocol_observer.h",
     "favicon/chrome_favicon_client.cc",
     "favicon/chrome_favicon_client.h",
-    "favicon/favicon_request_handler_factory.cc",
-    "favicon/favicon_request_handler_factory.h",
     "favicon/favicon_service_factory.cc",
     "favicon/favicon_service_factory.h",
     "favicon/favicon_utils.cc",
     "favicon/favicon_utils.h",
+    "favicon/history_ui_favicon_request_handler_factory.cc",
+    "favicon/history_ui_favicon_request_handler_factory.h",
     "favicon/large_icon_service_factory.cc",
     "favicon/large_icon_service_factory.h",
     "feature_engagement/tracker_factory.cc",
@@ -4583,6 +4583,8 @@
         "offline_pages/android/downloads/offline_page_share_helper.h",
         "offline_pages/android/load_termination_listener_impl.cc",
         "offline_pages/android/load_termination_listener_impl.h",
+        "offline_pages/android/offline_page_archive_publisher_impl.cc",
+        "offline_pages/android/offline_page_archive_publisher_impl.h",
         "offline_pages/android/offline_page_auto_fetcher.cc",
         "offline_pages/android/offline_page_auto_fetcher.h",
         "offline_pages/android/offline_page_auto_fetcher_service.cc",
@@ -4594,8 +4596,6 @@
         "offline_pages/android/offline_page_model_factory.cc",
         "offline_pages/android/offline_page_origin_utils_android.cc",
         "offline_pages/android/offline_page_utils_android.cc",
-        "offline_pages/android/offline_pages_download_manager_bridge.cc",
-        "offline_pages/android/offline_pages_download_manager_bridge.h",
         "offline_pages/android/prefetch_background_task_android.cc",
         "offline_pages/android/prefetch_background_task_android.h",
         "offline_pages/android/prefetch_background_task_scheduler_android.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 5630d3d..0984de9 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2479,6 +2479,11 @@
      flag_descriptions::kOmniboxNewAnswerLayoutName,
      flag_descriptions::kOmniboxNewAnswerLayoutDescription, kOsAndroid,
      FEATURE_VALUE_TYPE(omnibox::kOmniboxNewAnswerLayout)},
+
+    {"omnibox-on-device-head-suggestions",
+     flag_descriptions::kOmniboxOnDeviceHeadSuggestionsName,
+     flag_descriptions::kOmniboxOnDeviceHeadSuggestionsDescription, kOsAndroid,
+     FEATURE_VALUE_TYPE(omnibox::kOnDeviceHeadProvider)},
 #endif  // defined(OS_ANDROID)
 
     {"omnibox-rich-entity-suggestions",
@@ -3315,10 +3320,6 @@
      FEATURE_VALUE_TYPE(app_list_features::kEnableAppReinstallZeroState)},
 #endif  // OS_CHROMEOS
 
-    {"enable-bloated-renderer-detection",
-     flag_descriptions::kEnableBloatedRendererDetectionName,
-     flag_descriptions::kEnableBloatedRendererDetectionDescription, kOsAll,
-     FEATURE_VALUE_TYPE(features::kBloatedRendererDetection)},
     {"enable-sync-uss-bookmarks",
      flag_descriptions::kEnableSyncUSSBookmarksName,
      flag_descriptions::kEnableSyncUSSBookmarksDescription, kOsAll,
diff --git a/chrome/browser/android/favicon_helper.cc b/chrome/browser/android/favicon_helper.cc
index 08e017e..dd7c868 100644
--- a/chrome/browser/android/favicon_helper.cc
+++ b/chrome/browser/android/favicon_helper.cc
@@ -17,14 +17,14 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/android/chrome_jni_headers/FaviconHelper_jni.h"
-#include "chrome/browser/favicon/favicon_request_handler_factory.h"
 #include "chrome/browser/favicon/favicon_service_factory.h"
+#include "chrome/browser/favicon/history_ui_favicon_request_handler_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_android.h"
 #include "chrome/browser/sync/session_sync_service_factory.h"
-#include "components/favicon/core/favicon_request_handler.h"
 #include "components/favicon/core/favicon_service.h"
 #include "components/favicon/core/favicon_util.h"
+#include "components/favicon/core/history_ui_favicon_request_handler.h"
 #include "components/favicon_base/favicon_util.h"
 #include "components/sync_sessions/open_tabs_ui_delegate.h"
 #include "components/sync_sessions/session_sync_service.h"
@@ -152,12 +152,12 @@
   DCHECK(session_sync_service);
   sync_sessions::OpenTabsUIDelegate* open_tabs =
       session_sync_service->GetOpenTabsUIDelegate();
-  favicon::FaviconRequestHandler* favicon_request_handler =
-      FaviconRequestHandlerFactory::GetForBrowserContext(profile);
+  favicon::HistoryUiFaviconRequestHandler* history_ui_favicon_request_handler =
+      HistoryUiFaviconRequestHandlerFactory::GetForBrowserContext(profile);
   // Can be null in tests.
-  if (!favicon_request_handler)
+  if (!history_ui_favicon_request_handler)
     return false;
-  favicon_request_handler->GetRawFaviconForPageURL(
+  history_ui_favicon_request_handler->GetRawFaviconForPageURL(
       page_url, static_cast<int>(j_desired_size_in_pixel),
       base::BindOnce(&OnFaviconBitmapResultAvailable,
                      ScopedJavaGlobalRef<jobject>(j_favicon_image_callback)),
diff --git a/chrome/browser/android/payments/service_worker_payment_app_bridge.cc b/chrome/browser/android/payments/service_worker_payment_app_bridge.cc
index fe5752c87..b2149a8 100644
--- a/chrome/browser/android/payments/service_worker_payment_app_bridge.cc
+++ b/chrome/browser/android/payments/service_worker_payment_app_bridge.cc
@@ -488,10 +488,12 @@
 
 static void JNI_ServiceWorkerPaymentAppBridge_OnClosingPaymentAppWindow(
     JNIEnv* env,
-    const JavaParamRef<jobject>& jweb_contents) {
+    const JavaParamRef<jobject>& jweb_contents,
+    jint reason) {
   content::WebContents* web_contents =
       content::WebContents::FromJavaWebContents(jweb_contents);
 
   content::PaymentAppProvider::GetInstance()->OnClosingOpenedWindow(
-      web_contents->GetBrowserContext());
+      web_contents->GetBrowserContext(),
+      static_cast<payments::mojom::PaymentEventResponseType>(reason));
 }
diff --git a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
index 5fb3f623..1997173 100644
--- a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
+++ b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
@@ -391,14 +391,6 @@
                                                base::DoNothing());
 }
 
-void ChromeAutocompleteProviderClient::OnAutocompleteControllerResultReady(
-    AutocompleteController* controller) {
-  content::NotificationService::current()->Notify(
-      chrome::NOTIFICATION_AUTOCOMPLETE_CONTROLLER_RESULT_READY,
-      content::Source<AutocompleteController>(controller),
-      content::NotificationService::NoDetails());
-}
-
 // TODO(crbug.com/46623): Maintain a map of URL->WebContents for fast look-up.
 bool ChromeAutocompleteProviderClient::IsTabOpenWithURL(
     const GURL& url,
diff --git a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.h b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.h
index bb354c8f5..0c44f334 100644
--- a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.h
+++ b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.h
@@ -72,8 +72,6 @@
       const base::string16& term) override;
   void PrefetchImage(const GURL& url) override;
   void StartServiceWorker(const GURL& destination_url) override;
-  void OnAutocompleteControllerResultReady(
-      AutocompleteController* controller) override;
   bool IsTabOpenWithURL(const GURL& url,
                         const AutocompleteInput* input) override;
   bool IsBrowserUpdateAvailable() const override;
diff --git a/chrome/browser/background/background_contents_service.cc b/chrome/browser/background/background_contents_service.cc
index 112f87d..d301c74 100644
--- a/chrome/browser/background/background_contents_service.cc
+++ b/chrome/browser/background/background_contents_service.cc
@@ -99,37 +99,46 @@
 
   void Click(const base::Optional<int>& button_index,
              const base::Optional<base::string16>& reply) override {
-    // http://crbug.com/247790 involves a crash notification balloon being
-    // clicked while the extension isn't in the TERMINATED state. In that case,
-    // any of the "reload" methods called below can unload the extension, which
-    // indirectly destroys *this, invalidating all the member variables, so we
-    // copy the extension ID before using it.
-    std::string copied_extension_id = extension_id_;
-    if (is_hosted_app_) {
-      // There can be a race here: user clicks the balloon, and simultaneously
-      // reloads the sad tab for the app. So we check here to be safe before
-      // loading the background page.
-      BackgroundContentsService* service =
-          BackgroundContentsServiceFactory::GetForProfile(profile_);
-      if (!service->GetAppBackgroundContents(copied_extension_id)) {
-        service->LoadBackgroundContentsForExtension(profile_,
-                                                    copied_extension_id);
-      }
-    } else if (is_platform_app_) {
-      apps::AppLoadService::Get(profile_)->RestartApplication(
-          copied_extension_id);
-    } else {
-      extensions::ExtensionSystem::Get(profile_)
-          ->extension_service()
-          ->ReloadExtension(copied_extension_id);
-    }
-
-    CloseBalloon(copied_extension_id, profile_);
+    // Pass arguments by value as HandleClick() might destroy *this.
+    HandleClick(is_hosted_app_, is_platform_app_, extension_id_, profile_);
+    // *this might be destroyed now, do not access any members anymore!
   }
 
  private:
   ~CrashNotificationDelegate() override {}
 
+  // Static to prevent accidental use of members as *this might get destroyed.
+  static void HandleClick(bool is_hosted_app,
+                          bool is_platform_app,
+                          std::string extension_id,
+                          Profile* profile) {
+    // http://crbug.com/247790 involves a crash notification balloon being
+    // clicked while the extension isn't in the TERMINATED state. In that case,
+    // any of the "reload" methods called below can unload the extension, which
+    // indirectly destroys the CrashNotificationDelegate, invalidating all its
+    // member variables. Make sure to pass arguments by value when adding new
+    // ones to this method.
+    // TODO(knollr): Write a test for the flow of clicking on an extension
+    // crashed notification.
+    if (is_hosted_app) {
+      // There can be a race here: user clicks the balloon, and simultaneously
+      // reloads the sad tab for the app. So we check here to be safe before
+      // loading the background page.
+      BackgroundContentsService* service =
+          BackgroundContentsServiceFactory::GetForProfile(profile);
+      if (!service->GetAppBackgroundContents(extension_id))
+        service->LoadBackgroundContentsForExtension(profile, extension_id);
+    } else if (is_platform_app) {
+      apps::AppLoadService::Get(profile)->RestartApplication(extension_id);
+    } else {
+      extensions::ExtensionSystem::Get(profile)
+          ->extension_service()
+          ->ReloadExtension(extension_id);
+    }
+
+    CloseBalloon(extension_id, profile);
+  }
+
   Profile* profile_;
   bool is_hosted_app_;
   bool is_platform_app_;
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 828ad687..d7c3ddb 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -982,10 +982,6 @@
         master_prefs_->suppress_default_browser_prompt_for_version);
   }
 
-#if defined(OS_WIN)
-  if (!master_prefs_->welcome_page_on_os_upgrade_enabled)
-    local_state->SetBoolean(prefs::kWelcomePageOnOSUpgradeEnabled, false);
-#endif
 #endif  // !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
   return service_manager::RESULT_CODE_NORMAL_EXIT;
 }
diff --git a/chrome/browser/chrome_notification_types.h b/chrome/browser/chrome_notification_types.h
index 8681df5..33fe370 100644
--- a/chrome/browser/chrome_notification_types.h
+++ b/chrome/browser/chrome_notification_types.h
@@ -180,14 +180,6 @@
   // The details are none and the source is a Profile*.
   NOTIFICATION_PROFILE_URL_REQUEST_CONTEXT_GETTER_INITIALIZED,
 
-  // Non-history storage services --------------------------------------------
-
-  // Autocomplete ------------------------------------------------------------
-
-  // Sent by the autocomplete controller when done.  The source is the
-  // AutocompleteController, the details not used.
-  NOTIFICATION_AUTOCOMPLETE_CONTROLLER_RESULT_READY,
-
   // Printing ----------------------------------------------------------------
 
   // Notification from PrintJob that an event occurred. It can be that a page
@@ -313,10 +305,6 @@
   NOTIFICATION_WALLPAPER_ANIMATION_FINISHED,
 #endif
 
-  // Sent when the FullscreenController changes, confirms, or denies mouse lock.
-  // The source is the browser's FullscreenController, no details.
-  NOTIFICATION_MOUSE_LOCK_CHANGED,
-
   // Note:-
   // Currently only Content and Chrome define and use notifications.
   // Custom notifications not belonging to Content and Chrome should start
diff --git a/chrome/browser/chromeos/extensions/wallpaper_private_api.cc b/chrome/browser/chromeos/extensions/wallpaper_private_api.cc
index 0e856811..b519a0b0 100644
--- a/chrome/browser/chromeos/extensions/wallpaper_private_api.cc
+++ b/chrome/browser/chromeos/extensions/wallpaper_private_api.cc
@@ -231,6 +231,7 @@
   SET_STRING("setSuccessfullyMessage",
              IDS_WALLPAPER_MANAGER_SET_SUCCESSFULLY_MESSAGE);
   SET_STRING("defaultWallpaperLabel", IDS_DEFAULT_WALLPAPER_ACCESSIBLE_LABEL);
+  SET_STRING("backButton", IDS_ACCNAME_BACK);
 #undef SET_STRING
 
   const std::string& app_locale = g_browser_process->GetApplicationLocale();
diff --git a/chrome/browser/chromeos/printing/cups_proxy_service_delegate_impl.cc b/chrome/browser/chromeos/printing/cups_proxy_service_delegate_impl.cc
index 9c37432..5ec845c4 100644
--- a/chrome/browser/chromeos/printing/cups_proxy_service_delegate_impl.cc
+++ b/chrome/browser/chromeos/printing/cups_proxy_service_delegate_impl.cc
@@ -45,6 +45,11 @@
   return printers_manager_->IsPrinterInstalled(printer);
 }
 
+scoped_refptr<base::SingleThreadTaskRunner>
+CupsProxyServiceDelegateImpl::GetIOTaskRunner() {
+  return base::CreateSingleThreadTaskRunner({content::BrowserThread::IO});
+}
+
 void CupsProxyServiceDelegateImpl::SetupPrinter(
     const Printer& printer,
     printing::PrinterSetupCallback cb) {
diff --git a/chrome/browser/chromeos/printing/cups_proxy_service_delegate_impl.h b/chrome/browser/chromeos/printing/cups_proxy_service_delegate_impl.h
index bf2d172..8a786a8 100644
--- a/chrome/browser/chromeos/printing/cups_proxy_service_delegate_impl.h
+++ b/chrome/browser/chromeos/printing/cups_proxy_service_delegate_impl.h
@@ -14,6 +14,8 @@
 #include "chrome/services/cups_proxy/cups_proxy_service_delegate.h"
 #include "chromeos/printing/printer_configuration.h"
 
+#include "base/task/post_task.h"
+
 class Profile;
 
 namespace chromeos {
@@ -40,6 +42,9 @@
   // Returns whether |printer| is currently installed in CUPS with this config.
   bool IsPrinterInstalled(const Printer& printer) override;
 
+  // Returns an IO-thread task runner.
+  scoped_refptr<base::SingleThreadTaskRunner> GetIOTaskRunner() override;
+
   // Install |printer| into CUPS.
   void SetupPrinter(const Printer& printer,
                     printing::PrinterSetupCallback cb) override;
diff --git a/chrome/browser/chromeos/printing/usb_printer_detector.cc b/chrome/browser/chromeos/printing/usb_printer_detector.cc
index 0e5de535..5c3a75d4 100644
--- a/chrome/browser/chromeos/printing/usb_printer_detector.cc
+++ b/chrome/browser/chromeos/printing/usb_printer_detector.cc
@@ -27,11 +27,13 @@
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/debug_daemon_client.h"
 #include "chromeos/printing/ppd_provider.h"
+#include "chromeos/printing/usb_printer_id.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/system_connector.h"
 #include "mojo/public/cpp/bindings/associated_binding.h"
 #include "services/device/public/mojom/constants.mojom.h"
+#include "services/device/public/mojom/usb_device.mojom.h"
 #include "services/device/public/mojom/usb_manager_client.mojom.h"
 #include "services/service_manager/public/cpp/connector.h"
 
@@ -128,9 +130,24 @@
         GuessEffectiveMakeAndModel(device_info));
     entry.ppd_search_data.discovery_type =
         PrinterSearchData::PrinterDiscoveryType::kUsb;
-    // TODO(https://crbug.com/895037): Add in command set from IEEE1284
 
-    printers_[device_info.guid] = entry;
+    // Query printer for an IEEE Device ID.
+    device::mojom::UsbDevicePtr device_ptr;
+    device_manager_->GetDevice(device_info.guid, mojo::MakeRequest(&device_ptr),
+                               nullptr /* device_client */);
+    GetDeviceId(std::move(device_ptr),
+                base::BindOnce(&UsbPrinterDetectorImpl::OnGetDeviceId,
+                               weak_factory_.GetWeakPtr(), std::move(entry),
+                               device_info.guid));
+  }
+
+  void OnGetDeviceId(DetectedPrinter entry,
+                     std::string guid,
+                     UsbPrinterId printer_id) {
+    entry.ppd_search_data.printer_id = std::move(printer_id);
+
+    // Add detected printer.
+    printers_[guid] = entry;
     if (on_printers_found_callback_) {
       on_printers_found_callback_.Run(GetPrinters());
     }
diff --git a/chrome/browser/chromeos/printing/usb_printer_util.cc b/chrome/browser/chromeos/printing/usb_printer_util.cc
index fe57a93..e486a02481 100644
--- a/chrome/browser/chromeos/printing/usb_printer_util.cc
+++ b/chrome/browser/chromeos/printing/usb_printer_util.cc
@@ -7,10 +7,13 @@
 #include <ctype.h>
 #include <stdint.h>
 
+#include <algorithm>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include "base/big_endian.h"
+#include "base/bind_helpers.h"
 #include "base/hash/md5.h"
 #include "base/strings/string16.h"
 #include "base/strings/string_piece.h"
@@ -19,7 +22,9 @@
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/grit/generated_resources.h"
 #include "chromeos/printing/printer_configuration.h"
+#include "chromeos/printing/usb_printer_id.h"
 #include "services/device/public/cpp/usb/usb_utils.h"
+#include "services/device/public/mojom/usb_device.mojom.h"
 #include "services/device/public/mojom/usb_enumeration_options.mojom.h"
 #include "services/device/public/mojom/usb_manager.mojom.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -41,6 +46,69 @@
 // (http://www.usb.org/developers/docs/devclass_docs/IPP.zip).
 constexpr uint8_t kPrinterIppusbProtocol = 4;
 
+// Configuration for a GET_DEVICE_ID Printer Class-Specific Request.
+const int kGetDeviceIdRequest = 0;
+const int kDefaultInterface = 0;
+const int kDefaultConfiguration = 0;
+
+// Callback for device.mojom.UsbDevice.ControlTransferIn.
+// Expects |data| to hold a newly queried Device ID.
+void OnControlTransfer(device::mojom::UsbDevicePtr device_ptr,
+                       GetDeviceIdCallback cb,
+                       device::mojom::UsbTransferStatus status,
+                       const std::vector<uint8_t>& data) {
+  if (status != device::mojom::UsbTransferStatus::COMPLETED || data.empty()) {
+    return std::move(cb).Run({});
+  }
+
+  // Cleanup device_ptr.
+  device_ptr->ReleaseInterface(kDefaultInterface, base::DoNothing());
+  device_ptr->Close(base::DoNothing());
+
+  return std::move(cb).Run(UsbPrinterId(data));
+}
+
+// Callback for device.mojom.UsbDevice.ClaimInterface.
+// If interface was claimed successfully, attempts to query printer for a
+// Device ID.
+void OnClaimInterface(device::mojom::UsbDevicePtr device_ptr,
+                      GetDeviceIdCallback cb,
+                      bool success) {
+  if (!success) {
+    return std::move(cb).Run({});
+  }
+
+  auto params = device::mojom::UsbControlTransferParams::New();
+  params->type = device::mojom::UsbControlTransferType::CLASS;
+  params->recipient = device::mojom::UsbControlTransferRecipient::INTERFACE;
+  params->request = kGetDeviceIdRequest;
+  params->value = kDefaultConfiguration;  // default config index
+  params->index = kDefaultInterface;      // default interface index
+
+  // Query for IEEE1284 string.
+  auto* device = device_ptr.get();
+  device->ControlTransferIn(
+      std::move(params), 255 /* max size */, 2000 /* 2 second timeout */,
+      base::BindOnce(OnControlTransfer, std::move(device_ptr), std::move(cb)));
+}
+
+// Callback for device.mojom.UsbDevice.Open.
+// If device was opened successfully, attempts to claim printer's default
+// interface.
+void OnDeviceOpen(device::mojom::UsbDevicePtr device_ptr,
+                  GetDeviceIdCallback cb,
+                  device::mojom::UsbOpenDeviceError error) {
+  if (error != device::mojom::UsbOpenDeviceError::OK || !device_ptr) {
+    return std::move(cb).Run({});
+  }
+
+  // Claim interface.
+  auto* device = device_ptr.get();
+  device->ClaimInterface(
+      kDefaultInterface,
+      base::BindOnce(OnClaimInterface, std::move(device_ptr), std::move(cb)));
+}
+
 // Escape URI strings the same way cups does it, so we end up with a URI cups
 // recognizes.  Cups hex-encodes '%', ' ', and anything not in the standard
 // ASCII range.  CUPS lets everything else through unchanged.
@@ -108,7 +176,7 @@
 // possible for that device.  So we basically toss every bit of stable
 // information from the device into an MD5 hash, and then hexify the hash value
 // as a suffix to "usb-" as the final printer id.
-std::string UsbPrinterId(const UsbDeviceInfo& device_info) {
+std::string CreateUsbPrinterId(const UsbDeviceInfo& device_info) {
   // Paranoid checks; in the unlikely event someone messes with the USB device
   // definition, our (supposedly stable) hashes will change.
   static_assert(sizeof(device_info.class_code) == 1, "Class size changed");
@@ -258,9 +326,17 @@
 
   printer->set_description(printer->display_name());
   printer->set_uri(UsbPrinterUri(device_info));
-  printer->set_id(UsbPrinterId(device_info));
+  printer->set_id(CreateUsbPrinterId(device_info));
   printer->set_supports_ippusb(UsbDeviceSupportsIppusb(device_info));
   return printer;
 }
 
+void GetDeviceId(device::mojom::UsbDevicePtr device_ptr,
+                 GetDeviceIdCallback cb) {
+  // Open device.
+  auto* device = device_ptr.get();
+  device->Open(
+      base::BindOnce(OnDeviceOpen, std::move(device_ptr), std::move(cb)));
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/printing/usb_printer_util.h b/chrome/browser/chromeos/printing/usb_printer_util.h
index 76066b5..f05d8ce 100644
--- a/chrome/browser/chromeos/printing/usb_printer_util.h
+++ b/chrome/browser/chromeos/printing/usb_printer_util.h
@@ -14,6 +14,7 @@
 namespace chromeos {
 
 class Printer;
+class UsbPrinterId;
 
 base::string16 GetManufacturerName(
     const device::mojom::UsbDeviceInfo& device_info);
@@ -33,6 +34,12 @@
 std::unique_ptr<Printer> UsbDeviceToPrinter(
     const device::mojom::UsbDeviceInfo& device_info);
 
+// Expects |device_ptr| to be linked to a Printer-class USB Device. Queries the
+// printer for its IEEE 1284 Standard Device ID.
+using GetDeviceIdCallback = base::OnceCallback<void(UsbPrinterId)>;
+void GetDeviceId(device::mojom::UsbDevicePtr device_ptr,
+                 GetDeviceIdCallback cb);
+
 }  // namespace chromeos
 
 #endif  // CHROME_BROWSER_CHROMEOS_PRINTING_USB_PRINTER_UTIL_H__
diff --git a/chrome/browser/download/download_request_limiter.cc b/chrome/browser/download/download_request_limiter.cc
index e22cc6e..1cf321f 100644
--- a/chrome/browser/download/download_request_limiter.cc
+++ b/chrome/browser/download/download_request_limiter.cc
@@ -147,11 +147,11 @@
     if (navigation_handle->IsRendererInitiated()) {
       GURL origin = navigation_handle->GetURL().GetOrigin();
       // Mark the origin as restricted. If the origin does not exist in
-      // |restricted_status_map_|, give it a default value of
+      // |download_status_map_|, give it a default value of
       // PROMPT_BEFORE_DOWNLOAD and content setting will be checked later once
       // CanDownloadImpl() is called.
       if (!origin.is_empty())
-        restricted_status_map_.emplace(origin, PROMPT_BEFORE_DOWNLOAD);
+        download_status_map_.emplace(origin, PROMPT_BEFORE_DOWNLOAD);
       return;
     }
 
@@ -290,8 +290,8 @@
 DownloadRequestLimiter::DownloadStatus
 DownloadRequestLimiter::TabDownloadState::GetDownloadStatus(
     const GURL& request_origin) {
-  auto it = restricted_status_map_.find(request_origin);
-  if (it != restricted_status_map_.end())
+  auto it = download_status_map_.find(request_origin);
+  if (it != download_status_map_.end())
     return it->second;
   return ALLOW_ONE_DOWNLOAD;
 }
@@ -415,10 +415,10 @@
   origin_ = request_origin;
 
   if (!origin_.is_empty()) {
-    if (status_ == PROMPT_BEFORE_DOWNLOAD || status_ == DOWNLOADS_NOT_ALLOWED)
-      restricted_status_map_[request_origin] = status_;
+    if (status_ != ALLOW_ONE_DOWNLOAD)
+      download_status_map_[request_origin] = status_;
     else
-      restricted_status_map_.erase(request_origin);
+      download_status_map_.erase(request_origin);
   }
 
   if (!web_contents())
@@ -445,8 +445,12 @@
     return true;
 
   GURL origin = navigation_handle->GetURL().GetOrigin();
-  if (navigation_handle->GetPageTransition() & ui::PAGE_TRANSITION_FORWARD_BACK)
-    return restricted_status_map_.find(origin) != restricted_status_map_.end();
+  if (navigation_handle->GetPageTransition() &
+      ui::PAGE_TRANSITION_FORWARD_BACK) {
+    auto it = download_status_map_.find(origin);
+    if (it != download_status_map_.end())
+      return it->second != ALLOW_ALL_DOWNLOADS;
+  }
   return false;
 }
 
@@ -581,11 +585,15 @@
     setting = content_settings->GetContentSetting(
         initiator, initiator, CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS,
         std::string());
-    // Override the status if content setting is block or always allow.
-    if (setting == CONTENT_SETTING_BLOCK)
+    // Override the status if content setting is block. If the content setting
+    // is always allow, only reset the status if it is |DOWNLOADS_NOT_ALLOWED|
+    // so unnecessary notifications will not be triggered.
+    if (setting == CONTENT_SETTING_BLOCK) {
       status = DOWNLOADS_NOT_ALLOWED;
-    else if (setting == CONTENT_SETTING_ALLOW)
+    } else if (setting == CONTENT_SETTING_ALLOW &&
+               status == DOWNLOADS_NOT_ALLOWED) {
       status = ALLOW_ALL_DOWNLOADS;
+    }
   }
 
   // Always call SetDownloadStatusAndNotify since we may need to change the
diff --git a/chrome/browser/download/download_request_limiter.h b/chrome/browser/download/download_request_limiter.h
index f1a4901..83d3542 100644
--- a/chrome/browser/download/download_request_limiter.h
+++ b/chrome/browser/download/download_request_limiter.h
@@ -208,9 +208,9 @@
     // callbacks.
     std::vector<DownloadRequestLimiter::Callback> callbacks_;
 
-    // Origins that have restricted download state.
+    // Origins that have non-default download state.
     using DownloadStatusMap = std::map<GURL, DownloadStatus>;
-    DownloadStatusMap restricted_status_map_;
+    DownloadStatusMap download_status_map_;
 
     ScopedObserver<HostContentSettingsMap, content_settings::Observer>
         observer_;
diff --git a/chrome/browser/download/download_request_limiter_unittest.cc b/chrome/browser/download/download_request_limiter_unittest.cc
index 1dcc88e..57b1285 100644
--- a/chrome/browser/download/download_request_limiter_unittest.cc
+++ b/chrome/browser/download/download_request_limiter_unittest.cc
@@ -822,7 +822,7 @@
   // ALLOW_ONE_DOWNLOAD default status.
   CanDownload();
   ExpectAndResetCounts(1, 0, 0, __LINE__);
-  EXPECT_EQ(DownloadRequestLimiter::ALLOW_ALL_DOWNLOADS,
+  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
             download_request_limiter_->GetDownloadStatus(web_contents()));
 
   CanDownload();
diff --git a/chrome/browser/extensions/api/management/management_apitest.cc b/chrome/browser/extensions/api/management/management_apitest.cc
index 851424d..9328e95 100644
--- a/chrome/browser/extensions/api/management/management_apitest.cc
+++ b/chrome/browser/extensions/api/management/management_apitest.cc
@@ -68,8 +68,8 @@
     DisableExtension(extension_ids_["disabled_app"]);
   }
 
-  // Load an app, and wait for a message from app "management/launch_on_install"
-  // indicating that the new app has been launched.
+  // Load an app, and wait for a message that it has been launched. This should
+  // be sent by the launched app, to ensure the page is fully loaded.
   void LoadAndWaitForLaunch(const std::string& app_path,
                             std::string* out_app_id) {
     ExtensionTestMessageListener launched_app("launched app", false);
diff --git a/chrome/browser/extensions/api/omnibox/omnibox_api_browsertest.cc b/chrome/browser/extensions/api/omnibox/omnibox_api_browsertest.cc
deleted file mode 100644
index 82c2994..0000000
--- a/chrome/browser/extensions/api/omnibox/omnibox_api_browsertest.cc
+++ /dev/null
@@ -1,260 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/strings/string16.h"
-#include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.h"
-#include "chrome/browser/extensions/api/omnibox/omnibox_api_testbase.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/search_engines/template_url_service_factory.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/location_bar/location_bar.h"
-#include "chrome/test/base/search_test_utils.h"
-#include "components/omnibox/browser/autocomplete_controller.h"
-#include "components/omnibox/browser/autocomplete_input.h"
-#include "components/omnibox/browser/autocomplete_match.h"
-#include "components/omnibox/browser/autocomplete_result.h"
-#include "components/omnibox/browser/omnibox_view.h"
-#include "extensions/test/result_catcher.h"
-#include "third_party/metrics_proto/omnibox_event.pb.h"
-#include "ui/base/window_open_disposition.h"
-
-using base::ASCIIToUTF16;
-using extensions::ResultCatcher;
-using metrics::OmniboxEventProto;
-
-// http://crbug.com/167158
-IN_PROC_BROWSER_TEST_F(OmniboxApiTest, DISABLED_Basic) {
-  ASSERT_TRUE(RunExtensionTest("omnibox")) << message_;
-
-  // The results depend on the TemplateURLService being loaded. Make sure it is
-  // loaded so that the autocomplete results are consistent.
-  Profile* profile = browser()->profile();
-  search_test_utils::WaitForTemplateURLServiceToLoad(
-      TemplateURLServiceFactory::GetForProfile(profile));
-
-  AutocompleteController* autocomplete_controller =
-      GetAutocompleteController(browser());
-
-  // Test that our extension's keyword is suggested to us when we partially type
-  // it.
-  {
-    AutocompleteInput input(ASCIIToUTF16("keywor"),
-                            metrics::OmniboxEventProto::NTP,
-                            ChromeAutocompleteSchemeClassifier(profile));
-    autocomplete_controller->Start(input);
-    WaitForAutocompleteDone(autocomplete_controller);
-    EXPECT_TRUE(autocomplete_controller->done());
-
-    // Now, peek into the controller to see if it has the results we expect.
-    // First result should be to search for what was typed, second should be to
-    // enter "extension keyword" mode.
-    const AutocompleteResult& result = autocomplete_controller->result();
-    ASSERT_EQ(2U, result.size()) << AutocompleteResultAsString(result);
-    AutocompleteMatch match = result.match_at(0);
-    EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, match.type);
-    EXPECT_FALSE(match.deletable);
-
-    match = result.match_at(1);
-    EXPECT_EQ(ASCIIToUTF16("kw"), match.keyword);
-  }
-
-  // Test that our extension can send suggestions back to us.
-  {
-    AutocompleteInput input(ASCIIToUTF16("kw suggestio"),
-                            metrics::OmniboxEventProto::NTP,
-                            ChromeAutocompleteSchemeClassifier(profile));
-    autocomplete_controller->Start(input);
-    WaitForAutocompleteDone(autocomplete_controller);
-    EXPECT_TRUE(autocomplete_controller->done());
-
-    // Now, peek into the controller to see if it has the results we expect.
-    // First result should be to invoke the keyword with what we typed, 2-4
-    // should be to invoke with suggestions from the extension, and the last
-    // should be to search for what we typed.
-    const AutocompleteResult& result = autocomplete_controller->result();
-    ASSERT_EQ(5U, result.size()) << AutocompleteResultAsString(result);
-
-    EXPECT_EQ(ASCIIToUTF16("kw"), result.match_at(0).keyword);
-    EXPECT_EQ(ASCIIToUTF16("kw suggestio"), result.match_at(0).fill_into_edit);
-    EXPECT_EQ(AutocompleteMatchType::SEARCH_OTHER_ENGINE,
-              result.match_at(0).type);
-    EXPECT_EQ(AutocompleteProvider::TYPE_KEYWORD,
-              result.match_at(0).provider->type());
-    EXPECT_EQ(ASCIIToUTF16("kw"), result.match_at(1).keyword);
-    EXPECT_EQ(ASCIIToUTF16("kw suggestion1"),
-              result.match_at(1).fill_into_edit);
-    EXPECT_EQ(AutocompleteProvider::TYPE_KEYWORD,
-              result.match_at(1).provider->type());
-    EXPECT_EQ(ASCIIToUTF16("kw"), result.match_at(2).keyword);
-    EXPECT_EQ(ASCIIToUTF16("kw suggestion2"),
-              result.match_at(2).fill_into_edit);
-    EXPECT_EQ(AutocompleteProvider::TYPE_KEYWORD,
-              result.match_at(2).provider->type());
-    EXPECT_EQ(ASCIIToUTF16("kw"), result.match_at(3).keyword);
-    EXPECT_EQ(ASCIIToUTF16("kw suggestion3"),
-              result.match_at(3).fill_into_edit);
-    EXPECT_EQ(AutocompleteProvider::TYPE_KEYWORD,
-              result.match_at(3).provider->type());
-
-    base::string16 description =
-        ASCIIToUTF16("Description with style: <match>, [dim], (url till end)");
-    EXPECT_EQ(description, result.match_at(1).contents);
-    ASSERT_EQ(6u, result.match_at(1).contents_class.size());
-
-    EXPECT_EQ(0u,
-              result.match_at(1).contents_class[0].offset);
-    EXPECT_EQ(ACMatchClassification::NONE,
-              result.match_at(1).contents_class[0].style);
-
-    EXPECT_EQ(description.find('<'),
-              result.match_at(1).contents_class[1].offset);
-    EXPECT_EQ(ACMatchClassification::MATCH,
-              result.match_at(1).contents_class[1].style);
-
-    EXPECT_EQ(description.find('>') + 1u,
-              result.match_at(1).contents_class[2].offset);
-    EXPECT_EQ(ACMatchClassification::NONE,
-              result.match_at(1).contents_class[2].style);
-
-    EXPECT_EQ(description.find('['),
-              result.match_at(1).contents_class[3].offset);
-    EXPECT_EQ(ACMatchClassification::DIM,
-              result.match_at(1).contents_class[3].style);
-
-    EXPECT_EQ(description.find(']') + 1u,
-              result.match_at(1).contents_class[4].offset);
-    EXPECT_EQ(ACMatchClassification::NONE,
-              result.match_at(1).contents_class[4].style);
-
-    EXPECT_EQ(description.find('('),
-              result.match_at(1).contents_class[5].offset);
-    EXPECT_EQ(ACMatchClassification::URL,
-              result.match_at(1).contents_class[5].style);
-
-    AutocompleteMatch match = result.match_at(4);
-    EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, match.type);
-    EXPECT_EQ(AutocompleteProvider::TYPE_SEARCH,
-              result.match_at(4).provider->type());
-    EXPECT_FALSE(match.deletable);
-  }
-
-  // Flaky, see http://crbug.com/167158
-  /*
-  {
-    LocationBar* location_bar = GetLocationBar(browser());
-    ResultCatcher catcher;
-    OmniboxView* omnibox_view = location_bar->GetOmniboxView();
-    omnibox_view->OnBeforePossibleChange();
-    omnibox_view->SetUserText(ASCIIToUTF16("kw command"));
-    omnibox_view->OnAfterPossibleChange(true);
-    location_bar->AcceptInput();
-    // This checks that the keyword provider (via javascript)
-    // gets told to navigate to the string "command".
-    EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
-  }
-  */
-}
-
-IN_PROC_BROWSER_TEST_F(OmniboxApiTest, OnInputEntered) {
-  ASSERT_TRUE(RunExtensionTest("omnibox")) << message_;
-  Profile* profile = browser()->profile();
-  search_test_utils::WaitForTemplateURLServiceToLoad(
-      TemplateURLServiceFactory::GetForProfile(profile));
-
-  LocationBar* location_bar = GetLocationBar(browser());
-  OmniboxView* omnibox_view = location_bar->GetOmniboxView();
-  ResultCatcher catcher;
-  AutocompleteController* autocomplete_controller =
-      GetAutocompleteController(browser());
-  omnibox_view->OnBeforePossibleChange();
-  omnibox_view->SetUserText(ASCIIToUTF16("kw command"));
-  omnibox_view->OnAfterPossibleChange(true);
-
-  {
-    AutocompleteInput input(ASCIIToUTF16("kw command"),
-                            metrics::OmniboxEventProto::NTP,
-                            ChromeAutocompleteSchemeClassifier(profile));
-    autocomplete_controller->Start(input);
-  }
-  omnibox_view->model()->AcceptInput(WindowOpenDisposition::CURRENT_TAB);
-  WaitForAutocompleteDone(autocomplete_controller);
-  EXPECT_TRUE(autocomplete_controller->done());
-  EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
-
-  omnibox_view->OnBeforePossibleChange();
-  omnibox_view->SetUserText(ASCIIToUTF16("kw newtab"));
-  omnibox_view->OnAfterPossibleChange(true);
-  WaitForAutocompleteDone(autocomplete_controller);
-  EXPECT_TRUE(autocomplete_controller->done());
-
-  {
-    AutocompleteInput input(ASCIIToUTF16("kw newtab"),
-                            metrics::OmniboxEventProto::NTP,
-                            ChromeAutocompleteSchemeClassifier(profile));
-    autocomplete_controller->Start(input);
-  }
-  omnibox_view->model()->AcceptInput(WindowOpenDisposition::NEW_FOREGROUND_TAB);
-  WaitForAutocompleteDone(autocomplete_controller);
-  EXPECT_TRUE(autocomplete_controller->done());
-  EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
-}
-
-// Tests that we get suggestions from and send input to the incognito context
-// of an incognito split mode extension.
-// http://crbug.com/100927
-// Test is flaky: http://crbug.com/101219
-IN_PROC_BROWSER_TEST_F(OmniboxApiTest, DISABLED_IncognitoSplitMode) {
-  Profile* profile = browser()->profile();
-  ResultCatcher catcher_incognito;
-  catcher_incognito.RestrictToBrowserContext(profile->GetOffTheRecordProfile());
-
-  ASSERT_TRUE(RunExtensionTestIncognito("omnibox")) << message_;
-
-  // Open an incognito window and wait for the incognito extension process to
-  // respond.
-  Browser* incognito_browser = CreateIncognitoBrowser();
-  ASSERT_TRUE(catcher_incognito.GetNextResult()) << catcher_incognito.message();
-
-  // The results depend on the TemplateURLService being loaded. Make sure it is
-  // loaded so that the autocomplete results are consistent.
-  search_test_utils::WaitForTemplateURLServiceToLoad(
-      TemplateURLServiceFactory::GetForProfile(browser()->profile()));
-
-  LocationBar* location_bar = GetLocationBar(incognito_browser);
-  AutocompleteController* autocomplete_controller =
-      GetAutocompleteController(incognito_browser);
-
-  // Test that we get the incognito-specific suggestions.
-  {
-    AutocompleteInput input(ASCIIToUTF16("kw suggestio"),
-                            metrics::OmniboxEventProto::NTP,
-                            ChromeAutocompleteSchemeClassifier(profile));
-    autocomplete_controller->Start(input);
-    WaitForAutocompleteDone(autocomplete_controller);
-    EXPECT_TRUE(autocomplete_controller->done());
-
-    // First result should be to invoke the keyword with what we typed, 2-4
-    // should be to invoke with suggestions from the extension, and the last
-    // should be to search for what we typed.
-    const AutocompleteResult& result = autocomplete_controller->result();
-    ASSERT_EQ(5U, result.size()) << AutocompleteResultAsString(result);
-    ASSERT_FALSE(result.match_at(0).keyword.empty());
-    EXPECT_EQ(ASCIIToUTF16("kw suggestion3 incognito"),
-              result.match_at(3).fill_into_edit);
-  }
-
-  // Test that our input is sent to the incognito context. The test will do a
-  // text comparison and succeed only if "command incognito" is sent to the
-  // incognito context.
-  {
-    ResultCatcher catcher;
-    AutocompleteInput input(ASCIIToUTF16("kw command incognito"),
-                            metrics::OmniboxEventProto::NTP,
-                            ChromeAutocompleteSchemeClassifier(profile));
-    autocomplete_controller->Start(input);
-    location_bar->AcceptInput();
-    EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
-  }
-}
diff --git a/chrome/browser/extensions/api/omnibox/omnibox_api_interactive_test.cc b/chrome/browser/extensions/api/omnibox/omnibox_api_interactive_test.cc
index c45a45c..87a86d9 100644
--- a/chrome/browser/extensions/api/omnibox/omnibox_api_interactive_test.cc
+++ b/chrome/browser/extensions/api/omnibox/omnibox_api_interactive_test.cc
@@ -2,21 +2,44 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/format_macros.h"
+#include "base/strings/string16.h"
+#include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.h"
-#include "chrome/browser/extensions/api/omnibox/omnibox_api_testbase.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/extensions/extension_apitest.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
+#include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/location_bar/location_bar.h"
 #include "chrome/browser/ui/view_ids.h"
 #include "chrome/test/base/interactive_test_utils.h"
 #include "chrome/test/base/search_test_utils.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/omnibox/browser/autocomplete_controller.h"
+#include "components/omnibox/browser/autocomplete_input.h"
+#include "components/omnibox/browser/autocomplete_match.h"
+#include "components/omnibox/browser/autocomplete_result.h"
+#include "components/omnibox/browser/omnibox_controller_emitter.h"
+#include "components/omnibox/browser/omnibox_popup_model.h"
+#include "components/omnibox/browser/omnibox_view.h"
+#include "content/public/test/test_utils.h"
 #include "extensions/test/extension_test_message_listener.h"
 #include "extensions/test/result_catcher.h"
 #include "third_party/metrics_proto/omnibox_event.pb.h"
+#include "ui/base/window_open_disposition.h"
 
 namespace {
 
+using base::ASCIIToUTF16;
+using extensions::ResultCatcher;
+using metrics::OmniboxEventProto;
+using ui_test_utils::WaitForAutocompleteDone;
+
 void InputKeys(Browser* browser, const std::vector<ui::KeyboardCode>& keys) {
   for (auto key : keys) {
     // Note that sending key presses can be flaky at times.
@@ -25,8 +48,267 @@
   }
 }
 
+LocationBar* GetLocationBar(Browser* browser) {
+  return browser->window()->GetLocationBar();
+}
+
+AutocompleteController* GetAutocompleteController(Browser* browser) {
+  return GetLocationBar(browser)
+      ->GetOmniboxView()
+      ->model()
+      ->autocomplete_controller();
+}
+
+base::string16 AutocompleteResultAsString(const AutocompleteResult& result) {
+  std::string output(base::StringPrintf("{%" PRIuS "} ", result.size()));
+  for (size_t i = 0; i < result.size(); ++i) {
+    AutocompleteMatch match = result.match_at(i);
+    std::string provider_name = match.provider->GetName();
+    output.append(base::StringPrintf("[\"%s\" by \"%s\"] ",
+                                     base::UTF16ToUTF8(match.contents).c_str(),
+                                     provider_name.c_str()));
+  }
+  return base::UTF8ToUTF16(output);
+}
+
+using OmniboxApiTest = extensions::ExtensionApiTest;
+
 }  // namespace
 
+// http://crbug.com/167158
+IN_PROC_BROWSER_TEST_F(OmniboxApiTest, DISABLED_Basic) {
+  ASSERT_TRUE(RunExtensionTest("omnibox")) << message_;
+
+  // The results depend on the TemplateURLService being loaded. Make sure it is
+  // loaded so that the autocomplete results are consistent.
+  Profile* profile = browser()->profile();
+  search_test_utils::WaitForTemplateURLServiceToLoad(
+      TemplateURLServiceFactory::GetForProfile(profile));
+
+  AutocompleteController* autocomplete_controller =
+      GetAutocompleteController(browser());
+
+  // Test that our extension's keyword is suggested to us when we partially type
+  // it.
+  {
+    AutocompleteInput input(ASCIIToUTF16("keywor"),
+                            metrics::OmniboxEventProto::NTP,
+                            ChromeAutocompleteSchemeClassifier(profile));
+    autocomplete_controller->Start(input);
+    WaitForAutocompleteDone(browser());
+    EXPECT_TRUE(autocomplete_controller->done());
+
+    // Now, peek into the controller to see if it has the results we expect.
+    // First result should be to search for what was typed, second should be to
+    // enter "extension keyword" mode.
+    const AutocompleteResult& result = autocomplete_controller->result();
+    ASSERT_EQ(2U, result.size()) << AutocompleteResultAsString(result);
+    AutocompleteMatch match = result.match_at(0);
+    EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, match.type);
+    EXPECT_FALSE(match.deletable);
+
+    match = result.match_at(1);
+    EXPECT_EQ(ASCIIToUTF16("kw"), match.keyword);
+  }
+
+  // Test that our extension can send suggestions back to us.
+  {
+    AutocompleteInput input(ASCIIToUTF16("kw suggestio"),
+                            metrics::OmniboxEventProto::NTP,
+                            ChromeAutocompleteSchemeClassifier(profile));
+    autocomplete_controller->Start(input);
+    WaitForAutocompleteDone(browser());
+    EXPECT_TRUE(autocomplete_controller->done());
+
+    // Now, peek into the controller to see if it has the results we expect.
+    // First result should be to invoke the keyword with what we typed, 2-4
+    // should be to invoke with suggestions from the extension, and the last
+    // should be to search for what we typed.
+    const AutocompleteResult& result = autocomplete_controller->result();
+    ASSERT_EQ(5U, result.size()) << AutocompleteResultAsString(result);
+
+    EXPECT_EQ(ASCIIToUTF16("kw"), result.match_at(0).keyword);
+    EXPECT_EQ(ASCIIToUTF16("kw suggestio"), result.match_at(0).fill_into_edit);
+    EXPECT_EQ(AutocompleteMatchType::SEARCH_OTHER_ENGINE,
+              result.match_at(0).type);
+    EXPECT_EQ(AutocompleteProvider::TYPE_KEYWORD,
+              result.match_at(0).provider->type());
+    EXPECT_EQ(ASCIIToUTF16("kw"), result.match_at(1).keyword);
+    EXPECT_EQ(ASCIIToUTF16("kw suggestion1"),
+              result.match_at(1).fill_into_edit);
+    EXPECT_EQ(AutocompleteProvider::TYPE_KEYWORD,
+              result.match_at(1).provider->type());
+    EXPECT_EQ(ASCIIToUTF16("kw"), result.match_at(2).keyword);
+    EXPECT_EQ(ASCIIToUTF16("kw suggestion2"),
+              result.match_at(2).fill_into_edit);
+    EXPECT_EQ(AutocompleteProvider::TYPE_KEYWORD,
+              result.match_at(2).provider->type());
+    EXPECT_EQ(ASCIIToUTF16("kw"), result.match_at(3).keyword);
+    EXPECT_EQ(ASCIIToUTF16("kw suggestion3"),
+              result.match_at(3).fill_into_edit);
+    EXPECT_EQ(AutocompleteProvider::TYPE_KEYWORD,
+              result.match_at(3).provider->type());
+
+    base::string16 description =
+        ASCIIToUTF16("Description with style: <match>, [dim], (url till end)");
+    EXPECT_EQ(description, result.match_at(1).contents);
+    ASSERT_EQ(6u, result.match_at(1).contents_class.size());
+
+    EXPECT_EQ(0u, result.match_at(1).contents_class[0].offset);
+    EXPECT_EQ(ACMatchClassification::NONE,
+              result.match_at(1).contents_class[0].style);
+
+    EXPECT_EQ(description.find('<'),
+              result.match_at(1).contents_class[1].offset);
+    EXPECT_EQ(ACMatchClassification::MATCH,
+              result.match_at(1).contents_class[1].style);
+
+    EXPECT_EQ(description.find('>') + 1u,
+              result.match_at(1).contents_class[2].offset);
+    EXPECT_EQ(ACMatchClassification::NONE,
+              result.match_at(1).contents_class[2].style);
+
+    EXPECT_EQ(description.find('['),
+              result.match_at(1).contents_class[3].offset);
+    EXPECT_EQ(ACMatchClassification::DIM,
+              result.match_at(1).contents_class[3].style);
+
+    EXPECT_EQ(description.find(']') + 1u,
+              result.match_at(1).contents_class[4].offset);
+    EXPECT_EQ(ACMatchClassification::NONE,
+              result.match_at(1).contents_class[4].style);
+
+    EXPECT_EQ(description.find('('),
+              result.match_at(1).contents_class[5].offset);
+    EXPECT_EQ(ACMatchClassification::URL,
+              result.match_at(1).contents_class[5].style);
+
+    AutocompleteMatch match = result.match_at(4);
+    EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, match.type);
+    EXPECT_EQ(AutocompleteProvider::TYPE_SEARCH,
+              result.match_at(4).provider->type());
+    EXPECT_FALSE(match.deletable);
+  }
+
+  // Flaky, see http://crbug.com/167158
+  /*
+  {
+    LocationBar* location_bar = GetLocationBar(browser());
+    ResultCatcher catcher;
+    OmniboxView* omnibox_view = location_bar->GetOmniboxView();
+    omnibox_view->OnBeforePossibleChange();
+    omnibox_view->SetUserText(ASCIIToUTF16("kw command"));
+    omnibox_view->OnAfterPossibleChange(true);
+    location_bar->AcceptInput();
+    // This checks that the keyword provider (via javascript)
+    // gets told to navigate to the string "command".
+    EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
+  }
+  */
+}
+
+IN_PROC_BROWSER_TEST_F(OmniboxApiTest, OnInputEntered) {
+  ASSERT_TRUE(RunExtensionTest("omnibox")) << message_;
+  Profile* profile = browser()->profile();
+  search_test_utils::WaitForTemplateURLServiceToLoad(
+      TemplateURLServiceFactory::GetForProfile(profile));
+
+  LocationBar* location_bar = GetLocationBar(browser());
+  OmniboxView* omnibox_view = location_bar->GetOmniboxView();
+  ResultCatcher catcher;
+  AutocompleteController* autocomplete_controller =
+      GetAutocompleteController(browser());
+  omnibox_view->OnBeforePossibleChange();
+  omnibox_view->SetUserText(ASCIIToUTF16("kw command"));
+  omnibox_view->OnAfterPossibleChange(true);
+
+  {
+    AutocompleteInput input(ASCIIToUTF16("kw command"),
+                            metrics::OmniboxEventProto::NTP,
+                            ChromeAutocompleteSchemeClassifier(profile));
+    autocomplete_controller->Start(input);
+  }
+  omnibox_view->model()->AcceptInput(WindowOpenDisposition::CURRENT_TAB);
+  WaitForAutocompleteDone(browser());
+  EXPECT_TRUE(autocomplete_controller->done());
+  EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
+
+  omnibox_view->OnBeforePossibleChange();
+  omnibox_view->SetUserText(ASCIIToUTF16("kw newtab"));
+  omnibox_view->OnAfterPossibleChange(true);
+  WaitForAutocompleteDone(browser());
+  EXPECT_TRUE(autocomplete_controller->done());
+
+  {
+    AutocompleteInput input(ASCIIToUTF16("kw newtab"),
+                            metrics::OmniboxEventProto::NTP,
+                            ChromeAutocompleteSchemeClassifier(profile));
+    autocomplete_controller->Start(input);
+  }
+  omnibox_view->model()->AcceptInput(WindowOpenDisposition::NEW_FOREGROUND_TAB);
+  WaitForAutocompleteDone(browser());
+  EXPECT_TRUE(autocomplete_controller->done());
+  EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
+}
+
+// Tests that we get suggestions from and send input to the incognito context
+// of an incognito split mode extension.
+// http://crbug.com/100927
+// Test is flaky: http://crbug.com/101219
+IN_PROC_BROWSER_TEST_F(OmniboxApiTest, DISABLED_IncognitoSplitMode) {
+  Profile* profile = browser()->profile();
+  ResultCatcher catcher_incognito;
+  catcher_incognito.RestrictToBrowserContext(profile->GetOffTheRecordProfile());
+
+  ASSERT_TRUE(RunExtensionTestIncognito("omnibox")) << message_;
+
+  // Open an incognito window and wait for the incognito extension process to
+  // respond.
+  Browser* incognito_browser = CreateIncognitoBrowser();
+  ASSERT_TRUE(catcher_incognito.GetNextResult()) << catcher_incognito.message();
+
+  // The results depend on the TemplateURLService being loaded. Make sure it is
+  // loaded so that the autocomplete results are consistent.
+  search_test_utils::WaitForTemplateURLServiceToLoad(
+      TemplateURLServiceFactory::GetForProfile(browser()->profile()));
+
+  LocationBar* location_bar = GetLocationBar(incognito_browser);
+  AutocompleteController* autocomplete_controller =
+      GetAutocompleteController(incognito_browser);
+
+  // Test that we get the incognito-specific suggestions.
+  {
+    AutocompleteInput input(ASCIIToUTF16("kw suggestio"),
+                            metrics::OmniboxEventProto::NTP,
+                            ChromeAutocompleteSchemeClassifier(profile));
+    autocomplete_controller->Start(input);
+    WaitForAutocompleteDone(browser());
+    EXPECT_TRUE(autocomplete_controller->done());
+
+    // First result should be to invoke the keyword with what we typed, 2-4
+    // should be to invoke with suggestions from the extension, and the last
+    // should be to search for what we typed.
+    const AutocompleteResult& result = autocomplete_controller->result();
+    ASSERT_EQ(5U, result.size()) << AutocompleteResultAsString(result);
+    ASSERT_FALSE(result.match_at(0).keyword.empty());
+    EXPECT_EQ(ASCIIToUTF16("kw suggestion3 incognito"),
+              result.match_at(3).fill_into_edit);
+  }
+
+  // Test that our input is sent to the incognito context. The test will do a
+  // text comparison and succeed only if "command incognito" is sent to the
+  // incognito context.
+  {
+    ResultCatcher catcher;
+    AutocompleteInput input(ASCIIToUTF16("kw command incognito"),
+                            metrics::OmniboxEventProto::NTP,
+                            ChromeAutocompleteSchemeClassifier(profile));
+    autocomplete_controller->Start(input);
+    location_bar->AcceptInput();
+    EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
+  }
+}
+
 // Tests that the autocomplete popup doesn't reopen after accepting input for
 // a given query.
 // http://crbug.com/88552
@@ -49,7 +331,7 @@
   omnibox_view->OnBeforePossibleChange();
   omnibox_view->SetUserText(base::ASCIIToUTF16("kw comman"));
   omnibox_view->OnAfterPossibleChange(true);
-  WaitForAutocompleteDone(autocomplete_controller);
+  WaitForAutocompleteDone(browser());
   EXPECT_TRUE(autocomplete_controller->done());
   EXPECT_TRUE(popup_model->IsOpen());
 
@@ -66,7 +348,7 @@
                           ChromeAutocompleteSchemeClassifier(profile));
   autocomplete_controller->Start(input);
   location_bar->AcceptInput();
-  WaitForAutocompleteDone(autocomplete_controller);
+  WaitForAutocompleteDone(browser());
   EXPECT_TRUE(autocomplete_controller->done());
   // This checks that the keyword provider (via javascript)
   // gets told to navigate to the string "command".
@@ -100,7 +382,7 @@
   // Input a keyword query and wait for suggestions from the extension.
   InputKeys(browser(), {ui::VKEY_K, ui::VKEY_W, ui::VKEY_SPACE, ui::VKEY_D});
 
-  WaitForAutocompleteDone(autocomplete_controller);
+  WaitForAutocompleteDone(browser());
   EXPECT_TRUE(autocomplete_controller->done());
 
   // Peek into the controller to see if it has the results we expect.
@@ -172,7 +454,7 @@
 
   // Input a keyword query and wait for suggestions from the extension.
   InputKeys(browser(), {ui::VKEY_K, ui::VKEY_W, ui::VKEY_SPACE, ui::VKEY_D});
-  WaitForAutocompleteDone(autocomplete_controller);
+  WaitForAutocompleteDone(browser());
   EXPECT_TRUE(autocomplete_controller->done());
 
   // Peek into the controller to see if it has the results we expect.
@@ -209,7 +491,7 @@
   InputKeys(browser(), {ui::VKEY_K, ui::VKEY_W, ui::VKEY_SPACE, ui::VKEY_BACK,
                         ui::VKEY_D});
 
-  WaitForAutocompleteDone(autocomplete_controller);
+  WaitForAutocompleteDone(browser());
   EXPECT_TRUE(autocomplete_controller->done());
 
   // Peek into the controller to see if it has the results we expect.  Since
diff --git a/chrome/browser/extensions/api/omnibox/omnibox_api_testbase.h b/chrome/browser/extensions/api/omnibox/omnibox_api_testbase.h
deleted file mode 100644
index 8d0f684..0000000
--- a/chrome/browser/extensions/api/omnibox/omnibox_api_testbase.h
+++ /dev/null
@@ -1,62 +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 CHROME_BROWSER_EXTENSIONS_API_OMNIBOX_OMNIBOX_API_TESTBASE_H_
-#define CHROME_BROWSER_EXTENSIONS_API_OMNIBOX_OMNIBOX_API_TESTBASE_H_
-
-#include <stddef.h>
-
-#include "base/format_macros.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/extensions/extension_apitest.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_window.h"
-#include "chrome/browser/ui/location_bar/location_bar.h"
-#include "components/omnibox/browser/autocomplete_match.h"
-#include "components/omnibox/browser/autocomplete_result.h"
-#include "components/omnibox/browser/omnibox_popup_model.h"
-#include "components/omnibox/browser/omnibox_view.h"
-#include "content/public/test/test_utils.h"
-
-class AutocompleteController;
-
-class OmniboxApiTest : public extensions::ExtensionApiTest {
- protected:
-  LocationBar* GetLocationBar(Browser* browser) const {
-    return browser->window()->GetLocationBar();
-  }
-
-  AutocompleteController* GetAutocompleteController(Browser* browser) const {
-    return GetLocationBar(browser)->GetOmniboxView()->model()->popup_model()->
-        autocomplete_controller();
-  }
-
-  // TODO(phajdan.jr): Get rid of this wait-in-a-loop pattern.
-  void WaitForAutocompleteDone(AutocompleteController* controller) {
-    while (!controller->done()) {
-      content::WindowedNotificationObserver ready_observer(
-          chrome::NOTIFICATION_AUTOCOMPLETE_CONTROLLER_RESULT_READY,
-          content::Source<AutocompleteController>(controller));
-      ready_observer.Wait();
-    }
-  }
-
-  static base::string16 AutocompleteResultAsString(
-      const AutocompleteResult& result) {
-    std::string output(base::StringPrintf("{%" PRIuS "} ", result.size()));
-    for (size_t i = 0; i < result.size(); ++i) {
-      AutocompleteMatch match = result.match_at(i);
-      std::string provider_name = match.provider->GetName();
-      output.append(
-          base::StringPrintf("[\"%s\" by \"%s\"] ",
-                             base::UTF16ToUTF8(match.contents).c_str(),
-                             provider_name.c_str()));
-    }
-    return base::UTF8ToUTF16(output);
-  }
-};
-
-#endif  // CHROME_BROWSER_EXTENSIONS_API_OMNIBOX_OMNIBOX_API_TESTBASE_H_
diff --git a/chrome/browser/extensions/api/web_request/web_request_apitest.cc b/chrome/browser/extensions/api/web_request/web_request_apitest.cc
index e411538..3c5f8c1 100644
--- a/chrome/browser/extensions/api/web_request/web_request_apitest.cc
+++ b/chrome/browser/extensions/api/web_request/web_request_apitest.cc
@@ -253,6 +253,10 @@
     test_dir.WriteFile(FILE_PATH_LITERAL("background.js"), R"(
         chrome.webRequest.onBeforeSendHeaders.addListener(function(details) {
           details.requestHeaders.push({name: 'foo', value: 'bar'});
+          details.requestHeaders.push({
+            name: 'frameId',
+            value: details.frameId.toString()
+          });
           return {requestHeaders: details.requestHeaders};
         }, {urls: ['*://*/echoheader*']}, ['blocking', 'requestHeaders']);
 
@@ -2546,21 +2550,47 @@
   // response for the navigation preload request, and respond with it to create
   // the page.
   GURL url = embedded_test_server()->GetURL(
-      "/echoheader?foo&service-worker-navigation-preload");
+      "/echoheader?foo&frameId&service-worker-navigation-preload");
   ui_test_utils::NavigateToURL(browser(), url);
 
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+
   // Since the request was to "/echoheader", the response describes the request
   // headers.
   //
-  // The extension is expected to add a "foo: bar" header to the request
-  // before it goes to network. Verify that it did.
+  // The expectation is "bar\n0\ntrue" because...
   //
-  // The browser adds a "service-worker-navigation-preload: true" header for
-  // navigation preload requests, so also sanity check that header to prove
-  // that this test is really testing the navigation preload request.
-  EXPECT_EQ("bar\ntrue",
-            EvalJs(browser()->tab_strip_model()->GetActiveWebContents(),
-                   "document.body.textContent;"));
+  // 1) The extension is expected to add a "foo: bar" header to the request
+  //    before it goes to network.
+  // 2) The extension is similarly expected to add a "frameId: {id}" header,
+  //    where {id} is details.frameId. This id is 0 for the main frame.
+  // 3) The browser adds a "service-worker-navigation-preload: true" header for
+  //    navigation preload requests, so also sanity check that header to prove
+  //    that this test is really testing the navigation preload request.
+  EXPECT_EQ("bar\n0\ntrue", EvalJs(web_contents, "document.body.textContent;"));
+
+  // Repeat the test from an iframe, to test that details.frameId is populated
+  // correctly.
+  const char kAddIframe[] = R"(
+    (async () => {
+      const iframe = document.createElement('iframe');
+      await new Promise(resolve => {
+        iframe.src = $1;
+        iframe.onload = resolve;
+        document.body.appendChild(iframe);
+      });
+      const result = iframe.contentWindow.document.body.textContent;
+
+      // Expect "bar\n{frameId}\ntrue" where {frameId} is a positive integer.
+      const split = result.split('\n');
+      if (split[0] == 'bar' && parseInt(split[1]) > 0 && split[2] == 'true')
+        return 'ok';
+      return 'bad result: ' + result;
+    })();
+  )";
+
+  EXPECT_EQ("ok", EvalJs(web_contents, content::JsReplace(kAddIframe, url)));
 }
 
 // Ensure we don't strip off initiator incorrectly in web request events when
diff --git a/chrome/browser/extensions/bookmark_app_extension_util.cc b/chrome/browser/extensions/bookmark_app_extension_util.cc
index 1d92612..6968b5b 100644
--- a/chrome/browser/extensions/bookmark_app_extension_util.cc
+++ b/chrome/browser/extensions/bookmark_app_extension_util.cc
@@ -86,9 +86,9 @@
   DCHECK(CanBookmarkAppBePinnedToShelf());
 #if defined(OS_CHROMEOS)
   // ChromeLauncherController does not exist in unit tests.
-  if (ChromeLauncherController::instance()) {
-    ChromeLauncherController::instance()->shelf_model()->PinAppWithID(
-        extension->id());
+  if (auto* controller = ChromeLauncherController::instance()) {
+    controller->PinAppWithID(extension->id());
+    controller->UpdateV1AppState(extension->id());
   }
 #endif  // defined(OS_CHROMEOS)
 }
diff --git a/chrome/browser/favicon/favicon_request_handler_factory.h b/chrome/browser/favicon/favicon_request_handler_factory.h
deleted file mode 100644
index 9690ab9..0000000
--- a/chrome/browser/favicon/favicon_request_handler_factory.h
+++ /dev/null
@@ -1,46 +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 CHROME_BROWSER_FAVICON_FAVICON_REQUEST_HANDLER_FACTORY_H_
-#define CHROME_BROWSER_FAVICON_FAVICON_REQUEST_HANDLER_FACTORY_H_
-
-#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
-
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}
-
-namespace content {
-class BrowserContext;
-}
-
-namespace favicon {
-class FaviconRequestHandler;
-}
-
-class FaviconRequestHandlerFactory : public BrowserContextKeyedServiceFactory {
- public:
-  static favicon::FaviconRequestHandler* GetForBrowserContext(
-      content::BrowserContext* context);
-
-  static FaviconRequestHandlerFactory* GetInstance();
-
- private:
-  friend struct base::DefaultSingletonTraits<FaviconRequestHandlerFactory>;
-
-  FaviconRequestHandlerFactory();
-  ~FaviconRequestHandlerFactory() override;
-
-  // BrowserContextKeyedServiceFactory:
-  content::BrowserContext* GetBrowserContextToUse(
-      content::BrowserContext* context) const override;
-  KeyedService* BuildServiceInstanceFor(
-      content::BrowserContext* context) const override;
-  bool ServiceIsNULLWhileTesting() const override;
-
-  DISALLOW_COPY_AND_ASSIGN(FaviconRequestHandlerFactory);
-};
-
-#endif  // CHROME_BROWSER_FAVICON_FAVICON_REQUEST_HANDLER_FACTORY_H_
diff --git a/chrome/browser/favicon/favicon_request_handler_factory.cc b/chrome/browser/favicon/history_ui_favicon_request_handler_factory.cc
similarity index 73%
rename from chrome/browser/favicon/favicon_request_handler_factory.cc
rename to chrome/browser/favicon/history_ui_favicon_request_handler_factory.cc
index 27c820c..3212bfe 100644
--- a/chrome/browser/favicon/favicon_request_handler_factory.cc
+++ b/chrome/browser/favicon/history_ui_favicon_request_handler_factory.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/favicon/favicon_request_handler_factory.h"
+#include "chrome/browser/favicon/history_ui_favicon_request_handler_factory.h"
 
 #include "base/memory/singleton.h"
 #include "base/task/post_task.h"
@@ -12,7 +12,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/sync/session_sync_service_factory.h"
-#include "components/favicon/core/favicon_request_handler.h"
+#include "components/favicon/core/history_ui_favicon_request_handler_impl.h"
 #include "components/favicon_base/favicon_types.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/sync/driver/sync_service.h"
@@ -41,21 +41,22 @@
 }  // namespace
 
 // static
-favicon::FaviconRequestHandler*
-FaviconRequestHandlerFactory::GetForBrowserContext(
+favicon::HistoryUiFaviconRequestHandler*
+HistoryUiFaviconRequestHandlerFactory::GetForBrowserContext(
     content::BrowserContext* context) {
-  return static_cast<favicon::FaviconRequestHandler*>(
+  return static_cast<favicon::HistoryUiFaviconRequestHandler*>(
       GetInstance()->GetServiceForBrowserContext(context, true));
 }
 
 // static
-FaviconRequestHandlerFactory* FaviconRequestHandlerFactory::GetInstance() {
-  return base::Singleton<FaviconRequestHandlerFactory>::get();
+HistoryUiFaviconRequestHandlerFactory*
+HistoryUiFaviconRequestHandlerFactory::GetInstance() {
+  return base::Singleton<HistoryUiFaviconRequestHandlerFactory>::get();
 }
 
-FaviconRequestHandlerFactory::FaviconRequestHandlerFactory()
+HistoryUiFaviconRequestHandlerFactory::HistoryUiFaviconRequestHandlerFactory()
     : BrowserContextKeyedServiceFactory(
-          "FaviconRequestHandler",
+          "HistoryUiFaviconRequestHandler",
           BrowserContextDependencyManager::GetInstance()) {
   DependsOn(FaviconServiceFactory::GetInstance());
   DependsOn(LargeIconServiceFactory::GetInstance());
@@ -63,17 +64,19 @@
   DependsOn(ProfileSyncServiceFactory::GetInstance());
 }
 
-FaviconRequestHandlerFactory::~FaviconRequestHandlerFactory() {}
+HistoryUiFaviconRequestHandlerFactory::
+    ~HistoryUiFaviconRequestHandlerFactory() {}
 
-content::BrowserContext* FaviconRequestHandlerFactory::GetBrowserContextToUse(
+content::BrowserContext*
+HistoryUiFaviconRequestHandlerFactory::GetBrowserContextToUse(
     content::BrowserContext* context) const {
   return chrome::GetBrowserContextRedirectedInIncognito(context);
 }
 
-KeyedService* FaviconRequestHandlerFactory::BuildServiceInstanceFor(
+KeyedService* HistoryUiFaviconRequestHandlerFactory::BuildServiceInstanceFor(
     content::BrowserContext* context) const {
   Profile* profile = Profile::FromBrowserContext(context);
-  return new favicon::FaviconRequestHandler(
+  return new favicon::HistoryUiFaviconRequestHandlerImpl(
       base::BindRepeating(&GetSyncedFaviconForPageUrl,
                           SessionSyncServiceFactory::GetForProfile(profile)),
       base::BindRepeating(&CanSendHistoryData,
@@ -83,6 +86,6 @@
       LargeIconServiceFactory::GetForBrowserContext(context));
 }
 
-bool FaviconRequestHandlerFactory::ServiceIsNULLWhileTesting() const {
+bool HistoryUiFaviconRequestHandlerFactory::ServiceIsNULLWhileTesting() const {
   return true;
 }
diff --git a/chrome/browser/favicon/history_ui_favicon_request_handler_factory.h b/chrome/browser/favicon/history_ui_favicon_request_handler_factory.h
new file mode 100644
index 0000000..c9725d8
--- /dev/null
+++ b/chrome/browser/favicon/history_ui_favicon_request_handler_factory.h
@@ -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.
+
+#ifndef CHROME_BROWSER_FAVICON_HISTORY_UI_FAVICON_REQUEST_HANDLER_FACTORY_H_
+#define CHROME_BROWSER_FAVICON_HISTORY_UI_FAVICON_REQUEST_HANDLER_FACTORY_H_
+
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+namespace base {
+template <typename T>
+struct DefaultSingletonTraits;
+}
+
+namespace content {
+class BrowserContext;
+}
+
+namespace favicon {
+class HistoryUiFaviconRequestHandler;
+}
+
+class HistoryUiFaviconRequestHandlerFactory
+    : public BrowserContextKeyedServiceFactory {
+ public:
+  static favicon::HistoryUiFaviconRequestHandler* GetForBrowserContext(
+      content::BrowserContext* context);
+
+  static HistoryUiFaviconRequestHandlerFactory* GetInstance();
+
+ private:
+  friend struct base::DefaultSingletonTraits<
+      HistoryUiFaviconRequestHandlerFactory>;
+
+  HistoryUiFaviconRequestHandlerFactory();
+  ~HistoryUiFaviconRequestHandlerFactory() override;
+
+  // BrowserContextKeyedServiceFactory:
+  content::BrowserContext* GetBrowserContextToUse(
+      content::BrowserContext* context) const override;
+  KeyedService* BuildServiceInstanceFor(
+      content::BrowserContext* context) const override;
+  bool ServiceIsNULLWhileTesting() const override;
+
+  DISALLOW_COPY_AND_ASSIGN(HistoryUiFaviconRequestHandlerFactory);
+};
+
+#endif  // CHROME_BROWSER_FAVICON_HISTORY_UI_FAVICON_REQUEST_HANDLER_FACTORY_H_
diff --git a/chrome/browser/first_run/first_run.cc b/chrome/browser/first_run/first_run.cc
index 8cca4b5d..ea97735 100644
--- a/chrome/browser/first_run/first_run.cc
+++ b/chrome/browser/first_run/first_run.cc
@@ -336,13 +336,6 @@
   install_prefs.GetString(
       installer::master_preferences::kDistroSuppressDefaultBrowserPromptPref,
       &out_prefs->suppress_default_browser_prompt_for_version);
-
-  if (install_prefs.GetBool(
-          installer::master_preferences::kDistroWelcomePageOnOSUpgradeEnabled,
-          &value) &&
-      !value) {
-    out_prefs->welcome_page_on_os_upgrade_enabled = false;
-  }
 }
 
 bool GetFirstRunSentinelFilePath(base::FilePath* path) {
diff --git a/chrome/browser/first_run/first_run.h b/chrome/browser/first_run/first_run.h
index dbe046f..56e9d391 100644
--- a/chrome/browser/first_run/first_run.h
+++ b/chrome/browser/first_run/first_run.h
@@ -64,7 +64,6 @@
 
   bool make_chrome_default_for_user = false;
   bool suppress_first_run_default_browser_prompt = false;
-  bool welcome_page_on_os_upgrade_enabled = true;
   std::vector<GURL> new_tabs;
   std::vector<GURL> bookmarks;
   std::string import_bookmarks_path;
diff --git a/chrome/browser/first_run/first_run_unittest.cc b/chrome/browser/first_run/first_run_unittest.cc
index 8b39b934..68a7134 100644
--- a/chrome/browser/first_run/first_run_unittest.cc
+++ b/chrome/browser/first_run/first_run_unittest.cc
@@ -53,32 +53,6 @@
   EXPECT_TRUE(install_prefs.master_dictionary().empty());
 }
 
-TEST_F(FirstRunTest,
-       SetupMasterPrefsFromInstallPrefs_WelcomePageOnOSUpgradeMissing) {
-  installer::MasterPreferences install_prefs("{\"distribution\":{}}");
-  MasterPrefs out_prefs;
-  internal::SetupMasterPrefsFromInstallPrefs(install_prefs, &out_prefs);
-  EXPECT_TRUE(out_prefs.welcome_page_on_os_upgrade_enabled);
-}
-
-TEST_F(FirstRunTest,
-       SetupMasterPrefsFromInstallPrefs_WelcomePageOnOSUpgradeEnabled) {
-  installer::MasterPreferences install_prefs(
-      "{\"distribution\":{\"welcome_page_on_os_upgrade_enabled\": true}}");
-  MasterPrefs out_prefs;
-  internal::SetupMasterPrefsFromInstallPrefs(install_prefs, &out_prefs);
-  EXPECT_TRUE(out_prefs.welcome_page_on_os_upgrade_enabled);
-}
-
-TEST_F(FirstRunTest,
-       SetupMasterPrefsFromInstallPrefs_WelcomePageOnOSUpgradeDisabled) {
-  installer::MasterPreferences install_prefs(
-      "{\"distribution\":{\"welcome_page_on_os_upgrade_enabled\": false}}");
-  MasterPrefs out_prefs;
-  internal::SetupMasterPrefsFromInstallPrefs(install_prefs, &out_prefs);
-  EXPECT_FALSE(out_prefs.welcome_page_on_os_upgrade_enabled);
-}
-
 // No switches and no sentinel present. This is the standard case for first run.
 TEST_F(FirstRunTest, DetermineFirstRunState_FirstRun) {
   internal::FirstRunState result =
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index d98202f..414c88d 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -2485,6 +2485,11 @@
     "expiry_milestone": 76
   },
   {
+    "name": "omnibox-on-device-head-suggestions",
+    "owners": [ "cechen", "suggest-2g@google.com" ],
+    "expiry_milestone": 80
+  },
+  {
     "name": "omnibox-pedal-suggestions",
     "owners": [ "orinj", "chrome-omnibox-team@google.com" ],
     "expiry_milestone": 76
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 6f81723..0901aa2 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -118,10 +118,6 @@
 const char kDrawVerticallyEdgeToEdgeDescription[] =
     "Draw contents vertically from edge to edge.";
 
-const char kEnableBloatedRendererDetectionName[] = "Bloated renderer detection";
-const char kEnableBloatedRendererDetectionDescription[] =
-    "Enable bloated renderer detection";
-
 extern const char kAutofillAlwaysShowServerCardsInSyncTransportName[] =
     "AlwaysShowServerCardsInSyncTransport";
 extern const char kAutofillAlwaysShowServerCardsInSyncTransportDescription[] =
@@ -1376,6 +1372,12 @@
     "The maximum number of URL matches to show, unless there are no "
     "replacements.";
 
+const char kOmniboxOnDeviceHeadSuggestionsName[] =
+    "Omnibox on device head suggestions";
+const char kOmniboxOnDeviceHeadSuggestionsDescription[] =
+    "Google head non personalized search suggestions provided by a compact on "
+    "device model";
+
 const char kOmniboxUIShowPlaceholderWhenCaretShowingName[] =
     "Omnibox UI Show Placeholder When Caret Showing";
 const char kOmniboxUIShowPlaceholderWhenCaretShowingDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 9808db73..7e84c58 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -90,9 +90,6 @@
 extern const char kAutomaticPasswordGenerationName[];
 extern const char kAutomaticPasswordGenerationDescription[];
 
-extern const char kEnableBloatedRendererDetectionName[];
-extern const char kEnableBloatedRendererDetectionDescription[];
-
 extern const char kAutofillAlwaysShowServerCardsInSyncTransportName[];
 extern const char kAutofillAlwaysShowServerCardsInSyncTransportDescription[];
 
@@ -831,6 +828,9 @@
 extern const char kOmniboxMaxURLMatchesName[];
 extern const char kOmniboxMaxURLMatchesDescription[];
 
+extern const char kOmniboxOnDeviceHeadSuggestionsName[];
+extern const char kOmniboxOnDeviceHeadSuggestionsDescription[];
+
 extern const char kOmniboxUIShowPlaceholderWhenCaretShowingName[];
 extern const char kOmniboxUIShowPlaceholderWhenCaretShowingDescription[];
 
diff --git a/chrome/browser/notifications/proto/notification_data.proto b/chrome/browser/notifications/proto/notification_data.proto
index 70814cb..02d089f 100644
--- a/chrome/browser/notifications/proto/notification_data.proto
+++ b/chrome/browser/notifications/proto/notification_data.proto
@@ -14,9 +14,24 @@
   optional bytes value = 2;
 }
 
+// Button type.
+enum ActionButtonType {
+  UNKNOWN_ACTION = 0;
+  HELPFUL = 1;
+  UNHELPFUL = 2;
+}
+
 // Stores data used to display a notification in the UI.
-// Next tag: 6
+// Next tag: 7
 message NotificationData {
+  // Represents the button on the notification.
+  // Next tag: 4
+  message Button {
+    optional string text = 1;
+    optional ActionButtonType button_type = 2;
+    optional string id = 3;
+  }
+
   // Identifier of the notification.
   optional string id = 1;
 
@@ -32,4 +47,7 @@
 
   // Custom data associated with a notification.
   repeated CustomData custom_data = 5;
+
+  // A list of buttons on the notification.
+  repeated Button buttons = 6;
 }
diff --git a/chrome/browser/notifications/scheduler/internal/proto_conversion.cc b/chrome/browser/notifications/scheduler/internal/proto_conversion.cc
index 4d70263..2167858 100644
--- a/chrome/browser/notifications/scheduler/internal/proto_conversion.cc
+++ b/chrome/browser/notifications/scheduler/internal/proto_conversion.cc
@@ -171,6 +171,29 @@
   NOTREACHED();
 }
 
+proto::ActionButtonType ToActionButtonType(ActionButtonType type) {
+  switch (type) {
+    case ActionButtonType::kUnknownAction:
+      return proto::ActionButtonType::UNKNOWN_ACTION;
+    case ActionButtonType::kHelpful:
+      return proto::ActionButtonType::HELPFUL;
+    case ActionButtonType::kUnhelpful:
+      return proto::ActionButtonType::UNHELPFUL;
+  }
+  NOTREACHED();
+}
+
+ActionButtonType FromActionButtonType(proto::ActionButtonType proto_type) {
+  switch (proto_type) {
+    case proto::ActionButtonType::UNKNOWN_ACTION:
+      return ActionButtonType::kUnknownAction;
+    case proto::ActionButtonType::HELPFUL:
+      return ActionButtonType::kHelpful;
+    case proto::ActionButtonType::UNHELPFUL:
+      return ActionButtonType::kUnhelpful;
+  }
+}
+
 // Converts NotificationData to proto buffer type.
 void NotificationDataToProto(NotificationData* notification_data,
                              proto::NotificationData* proto) {
@@ -182,6 +205,13 @@
     data->set_key(pair.first);
     data->set_value(pair.second);
   }
+
+  for (const auto& button : notification_data->buttons) {
+    auto* proto_button = proto->add_buttons();
+    proto_button->set_text(base::UTF16ToUTF8(button.text));
+    proto_button->set_button_type(ToActionButtonType(button.type));
+    proto_button->set_id(button.id);
+  }
 }
 
 // Converts NotificationData from proto buffer type.
@@ -194,6 +224,15 @@
     const auto& pair = proto->custom_data(i);
     notification_data->custom_data.emplace(pair.key(), pair.value());
   }
+
+  for (int i = 0; i < proto->buttons_size(); ++i) {
+    NotificationData::Button button;
+    const auto& proto_button = proto->buttons(i);
+    button.text = base::UTF8ToUTF16(proto_button.text());
+    button.type = FromActionButtonType(proto_button.button_type());
+    button.id = proto_button.id();
+    notification_data->buttons.emplace_back(button);
+  }
 }
 
 // Converts ScheduleParams::Priority to proto buffer type.
diff --git a/chrome/browser/notifications/scheduler/internal/proto_conversion_unittest.cc b/chrome/browser/notifications/scheduler/internal/proto_conversion_unittest.cc
index b3184f13..20ccd82 100644
--- a/chrome/browser/notifications/scheduler/internal/proto_conversion_unittest.cc
+++ b/chrome/browser/notifications/scheduler/internal/proto_conversion_unittest.cc
@@ -48,6 +48,16 @@
       << " \n Expected: " << test::DebugString(&expected);
 }
 
+NotificationData::Button CreateButton(const char* text,
+                                      ActionButtonType type,
+                                      const char* id) {
+  NotificationData::Button button;
+  button.text = base::UTF8ToUTF16(text);
+  button.type = type;
+  button.id = id;
+  return button;
+}
+
 TEST(ProtoConversionTest, IconEntryFromProto) {
   IconProto proto;
   proto.set_uuid(kUuid);
@@ -187,5 +197,22 @@
   }
 }
 
+// Verifies buttons are converted correctly to proto buffers.
+TEST(ProtoConversionTest, NotificationEntryButtonsConversion) {
+  NotificationEntry entry(SchedulerClientType::kTest2, kGuid);
+  bool success =
+      base::Time::FromString("04/25/20 01:00:00 AM", &entry.create_time);
+  DCHECK(success);
+
+  NotificationData::Button button;
+  entry.notification_data.buttons.emplace_back(
+      CreateButton("text1", ActionButtonType::kUnknownAction, "id1"));
+  entry.notification_data.buttons.emplace_back(
+      CreateButton("text2", ActionButtonType::kHelpful, "id2"));
+  entry.notification_data.buttons.emplace_back(
+      CreateButton("text3", ActionButtonType::kUnhelpful, "id3"));
+  TestNotificationEntryConversion(&entry);
+}
+
 }  // namespace
 }  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/public/notification_data.cc b/chrome/browser/notifications/scheduler/public/notification_data.cc
index 8337eb8..28c0773 100644
--- a/chrome/browser/notifications/scheduler/public/notification_data.cc
+++ b/chrome/browser/notifications/scheduler/public/notification_data.cc
@@ -6,13 +6,23 @@
 
 namespace notifications {
 
+NotificationData::Button::Button() = default;
+NotificationData::Button::Button(const Button& other) = default;
+
+bool NotificationData::Button::operator==(const Button& other) const {
+  return text == other.text && type == other.type && id == other.id;
+}
+
+NotificationData::Button::~Button() = default;
+
 NotificationData::NotificationData() = default;
 
 NotificationData::NotificationData(const NotificationData& other) = default;
 
 bool NotificationData::operator==(const NotificationData& other) const {
   return id == other.id && title == other.title && message == other.message &&
-         icons.size() == other.icons.size() && custom_data == other.custom_data;
+         icons.size() == other.icons.size() &&
+         custom_data == other.custom_data && buttons == other.buttons;
 }
 
 NotificationData::~NotificationData() = default;
diff --git a/chrome/browser/notifications/scheduler/public/notification_data.h b/chrome/browser/notifications/scheduler/public/notification_data.h
index 13ea8c4..ade6de8 100644
--- a/chrome/browser/notifications/scheduler/public/notification_data.h
+++ b/chrome/browser/notifications/scheduler/public/notification_data.h
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "base/strings/string16.h"
+#include "chrome/browser/notifications/scheduler/public/notification_scheduler_types.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 
 namespace notifications {
@@ -20,6 +21,23 @@
 // or retrieving the hard coded assets and rewrites the data before notification
 // is shown in NotificationSchedulerClient::BeforeShowNotification().
 struct NotificationData {
+  // Represents a button on the notification UI.
+  struct Button {
+    Button();
+    Button(const Button& other);
+    bool operator==(const Button& other) const;
+    ~Button();
+
+    // The text associated with the button.
+    base::string16 text;
+
+    // The button type.
+    ActionButtonType type;
+
+    // The id of the button.
+    std::string id;
+  };
+
   using CustomData = std::map<std::string, std::string>;
   NotificationData();
   NotificationData(const NotificationData& other);
@@ -44,6 +62,9 @@
   // Custom key value pair data associated with each notification. Will be sent
   // back after user interaction.
   CustomData custom_data;
+
+  // A list of buttons on the notification.
+  std::vector<Button> buttons;
 };
 
 }  // namespace notifications
diff --git a/chrome/browser/offline_pages/android/offline_page_archive_publisher_impl.cc b/chrome/browser/offline_pages/android/offline_page_archive_publisher_impl.cc
new file mode 100644
index 0000000..2377d7d
--- /dev/null
+++ b/chrome/browser/offline_pages/android/offline_page_archive_publisher_impl.cc
@@ -0,0 +1,173 @@
+// 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/offline_pages/android/offline_page_archive_publisher_impl.h"
+
+#include <errno.h>
+#include <utility>
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_array.h"
+#include "base/android/jni_string.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/sequenced_task_runner.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/task_runner_util.h"
+#include "chrome/android/chrome_jni_headers/OfflinePageArchivePublisherBridge_jni.h"
+#include "components/offline_pages/core/archive_manager.h"
+#include "components/offline_pages/core/model/offline_page_model_utils.h"
+#include "components/offline_pages/core/offline_store_utils.h"
+
+using base::android::ScopedJavaLocalRef;
+
+namespace offline_pages {
+
+namespace {
+
+using offline_pages::SavePageResult;
+
+// Creates a singleton Delegate.
+OfflinePageArchivePublisherImpl::Delegate* GetDefaultDelegate() {
+  static OfflinePageArchivePublisherImpl::Delegate delegate;
+  return &delegate;
+}
+
+// Helper function to do the move and register synchronously. Make sure this is
+// called from a background thread.
+PublishArchiveResult MoveAndRegisterArchive(
+    const offline_pages::OfflinePageItem& offline_page,
+    const base::FilePath& publish_directory,
+    OfflinePageArchivePublisherImpl::Delegate* delegate) {
+  PublishArchiveResult archive_result;
+  // Calculate the new file name.
+  base::FilePath new_file_path =
+      offline_pages::model_utils::GenerateUniqueFilenameForOfflinePage(
+          offline_page.title, offline_page.url, publish_directory);
+
+  // Create the destination directory if it does not already exist.
+  if (!publish_directory.empty() && !base::DirectoryExists(publish_directory)) {
+    base::File::Error file_error;
+    base::CreateDirectoryAndGetError(publish_directory, &file_error);
+  }
+
+  // Move the file.
+  bool moved = base::Move(offline_page.file_path, new_file_path);
+  if (!moved) {
+    archive_result.move_result = SavePageResult::FILE_MOVE_FAILED;
+    DVPLOG(0) << "OfflinePage publishing file move failure " << __func__;
+
+    if (!base::PathExists(offline_page.file_path)) {
+      DVLOG(0) << "Can't copy from non-existent path, from "
+               << offline_page.file_path << " " << __func__;
+    }
+    if (!base::PathExists(publish_directory)) {
+      DVLOG(0) << "Target directory does not exist, " << publish_directory
+               << " " << __func__;
+    }
+    return archive_result;
+  }
+
+  // Tell the download manager about our file, get back an id.
+  if (!delegate->IsDownloadManagerInstalled()) {
+    archive_result.move_result = SavePageResult::ADD_TO_DOWNLOAD_MANAGER_FAILED;
+    return archive_result;
+  }
+
+  // TODO(petewil): Handle empty page title.
+  std::string page_title = base::UTF16ToUTF8(offline_page.title);
+  // We use the title for a description, since the add to the download manager
+  // fails without a description, and we don't have anything better to use.
+  int64_t download_id = delegate->AddCompletedDownload(
+      page_title, page_title,
+      offline_pages::store_utils::ToDatabaseFilePath(new_file_path),
+      offline_page.file_size, offline_page.url.spec(), std::string());
+  if (download_id == 0LL) {
+    archive_result.move_result = SavePageResult::ADD_TO_DOWNLOAD_MANAGER_FAILED;
+    return archive_result;
+  }
+
+  // Put results into the result object.
+  archive_result.move_result = SavePageResult::SUCCESS;
+  archive_result.new_file_path = new_file_path;
+  archive_result.download_id = download_id;
+
+  return archive_result;
+}
+
+}  // namespace
+
+OfflinePageArchivePublisherImpl::OfflinePageArchivePublisherImpl(
+    ArchiveManager* archive_manager)
+    : archive_manager_(archive_manager), delegate_(GetDefaultDelegate()) {}
+
+OfflinePageArchivePublisherImpl::~OfflinePageArchivePublisherImpl() {}
+
+void OfflinePageArchivePublisherImpl::SetDelegateForTesting(
+    OfflinePageArchivePublisherImpl::Delegate* delegate) {
+  delegate_ = delegate;
+}
+
+void OfflinePageArchivePublisherImpl::PublishArchive(
+    const OfflinePageItem& offline_page,
+    const scoped_refptr<base::SequencedTaskRunner>& background_task_runner,
+    PublishArchiveDoneCallback publish_done_callback) const {
+  base::PostTaskAndReplyWithResult(
+      background_task_runner.get(), FROM_HERE,
+      base::BindOnce(&MoveAndRegisterArchive, offline_page,
+                     archive_manager_->GetPublicArchivesDir(), delegate_),
+      base::BindOnce(std::move(publish_done_callback), offline_page));
+}
+
+void OfflinePageArchivePublisherImpl::UnpublishArchives(
+    const std::vector<int64_t>& download_manager_ids) const {
+  delegate_->Remove(download_manager_ids);
+}
+
+// Delegate implementation using Android download manager.
+
+bool OfflinePageArchivePublisherImpl::Delegate::IsDownloadManagerInstalled() {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  jboolean is_installed =
+      Java_OfflinePageArchivePublisherBridge_isAndroidDownloadManagerInstalled(
+          env);
+  return is_installed;
+}
+
+int64_t OfflinePageArchivePublisherImpl::Delegate::AddCompletedDownload(
+    const std::string& title,
+    const std::string& description,
+    const std::string& path,
+    int64_t length,
+    const std::string& uri,
+    const std::string& referer) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  // Convert strings to jstring references.
+  ScopedJavaLocalRef<jstring> j_title =
+      base::android::ConvertUTF8ToJavaString(env, title);
+  ScopedJavaLocalRef<jstring> j_description =
+      base::android::ConvertUTF8ToJavaString(env, description);
+  ScopedJavaLocalRef<jstring> j_path =
+      base::android::ConvertUTF8ToJavaString(env, path);
+  ScopedJavaLocalRef<jstring> j_uri =
+      base::android::ConvertUTF8ToJavaString(env, uri);
+  ScopedJavaLocalRef<jstring> j_referer =
+      base::android::ConvertUTF8ToJavaString(env, referer);
+
+  return Java_OfflinePageArchivePublisherBridge_addCompletedDownload(
+      env, j_title, j_description, j_path, length, j_uri, j_referer);
+}
+
+int OfflinePageArchivePublisherImpl::Delegate::Remove(
+    const std::vector<int64_t>& android_download_manager_ids) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  // Build a JNI array with our ID data.
+  ScopedJavaLocalRef<jlongArray> j_ids =
+      base::android::ToJavaLongArray(env, android_download_manager_ids);
+
+  return Java_OfflinePageArchivePublisherBridge_remove(env, j_ids);
+}
+
+}  // namespace offline_pages
diff --git a/chrome/browser/offline_pages/android/offline_page_archive_publisher_impl.h b/chrome/browser/offline_pages/android/offline_page_archive_publisher_impl.h
new file mode 100644
index 0000000..40fe4fe
--- /dev/null
+++ b/chrome/browser/offline_pages/android/offline_page_archive_publisher_impl.h
@@ -0,0 +1,75 @@
+// 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_OFFLINE_PAGES_ANDROID_OFFLINE_PAGE_ARCHIVE_PUBLISHER_IMPL_H_
+#define CHROME_BROWSER_OFFLINE_PAGES_ANDROID_OFFLINE_PAGE_ARCHIVE_PUBLISHER_IMPL_H_
+
+#include <cstdint>
+#include <string>
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "components/offline_pages/core/offline_page_archive_publisher.h"
+#include "components/offline_pages/core/offline_page_item.h"
+#include "components/offline_pages/core/offline_page_types.h"
+
+namespace base {
+class SequencedTaskRunner;
+}  // namespace base
+
+namespace offline_pages {
+
+class ArchiveManager;
+
+class OfflinePageArchivePublisherImpl : public OfflinePageArchivePublisher {
+ public:
+  class Delegate {
+   public:
+    Delegate() = default;
+
+    // Returns true if a system download manager is available on this platform.
+    virtual bool IsDownloadManagerInstalled();
+
+    // Returns the download manager ID of the download, which we will place in
+    // the offline pages database as part of the offline page item.
+    // TODO(petewil): it might make sense to move all these params into a
+    // struct.
+    virtual int64_t AddCompletedDownload(const std::string& title,
+                                         const std::string& description,
+                                         const std::string& path,
+                                         int64_t length,
+                                         const std::string& uri,
+                                         const std::string& referer);
+
+    // Returns the number of pages removed.
+    virtual int Remove(
+        const std::vector<int64_t>& android_download_manager_ids);
+
+   private:
+    Delegate(const Delegate&) = delete;
+    Delegate& operator=(const Delegate&) = delete;
+  };
+
+  explicit OfflinePageArchivePublisherImpl(ArchiveManager* archive_manager);
+  ~OfflinePageArchivePublisherImpl() override;
+
+  void SetDelegateForTesting(Delegate* delegate);
+
+  // OfflinePageArchivePublisher implementation.
+  void PublishArchive(
+      const OfflinePageItem& offline_page,
+      const scoped_refptr<base::SequencedTaskRunner>& background_task_runner,
+      PublishArchiveDoneCallback publish_done_callback) const override;
+
+  void UnpublishArchives(
+      const std::vector<int64_t>& download_manager_ids) const override;
+
+ private:
+  ArchiveManager* archive_manager_;
+  Delegate* delegate_;
+};
+
+}  // namespace offline_pages
+
+#endif  // CHROME_BROWSER_OFFLINE_PAGES_ANDROID_OFFLINE_PAGE_ARCHIVE_PUBLISHER_IMPL_H_
diff --git a/chrome/browser/offline_pages/android/offline_page_archive_publisher_impl_unittest.cc b/chrome/browser/offline_pages/android/offline_page_archive_publisher_impl_unittest.cc
new file mode 100644
index 0000000..66f829e
--- /dev/null
+++ b/chrome/browser/offline_pages/android/offline_page_archive_publisher_impl_unittest.cc
@@ -0,0 +1,173 @@
+// 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/offline_pages/android/offline_page_archive_publisher_impl.h"
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequenced_task_runner.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/offline_pages/core/archive_manager.h"
+#include "components/offline_pages/core/model/offline_page_item_generator.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+const int64_t kDownloadId = 42LL;
+}  // namespace
+
+namespace offline_pages {
+
+class OfflinePageArchivePublisherImplTest
+    : public testing::Test,
+      public base::SupportsWeakPtr<OfflinePageArchivePublisherImplTest> {
+ public:
+  OfflinePageArchivePublisherImplTest()
+      : task_runner_(new base::TestSimpleTaskRunner),
+        task_runner_handle_(task_runner_),
+        weak_ptr_factory_(this) {}
+  ~OfflinePageArchivePublisherImplTest() override {}
+
+  SavePageCallback save_page_callback;
+
+  void SetUp() override;
+  void PumpLoop();
+
+  OfflinePageItemGenerator* page_generator() { return &page_generator_; }
+
+  const base::FilePath& temporary_dir_path() {
+    return temporary_dir_.GetPath();
+  }
+  const base::FilePath& private_archive_dir_path() {
+    return private_archive_dir_.GetPath();
+  }
+  const base::FilePath& public_archive_dir_path() {
+    return public_archive_dir_.GetPath();
+  }
+  const PublishArchiveResult& publish_archive_result() {
+    return publish_archive_result_;
+  }
+  scoped_refptr<base::SequencedTaskRunner> task_runner() {
+    return task_runner_;
+  }
+  base::WeakPtr<OfflinePageArchivePublisherImplTest> get_weak_ptr() {
+    return weak_ptr_factory_.GetWeakPtr();
+  }
+
+  void PublishArchiveDone(SavePageCallback save_page_callback,
+                          const OfflinePageItem& offline_page,
+                          PublishArchiveResult archive_result);
+
+ private:
+  base::ScopedTempDir temporary_dir_;
+  base::ScopedTempDir private_archive_dir_;
+  base::ScopedTempDir public_archive_dir_;
+  OfflinePageItemGenerator page_generator_;
+  PublishArchiveResult publish_archive_result_;
+  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+  base::ThreadTaskRunnerHandle task_runner_handle_;
+  base::WeakPtrFactory<OfflinePageArchivePublisherImplTest> weak_ptr_factory_;
+};
+
+void OfflinePageArchivePublisherImplTest::SetUp() {
+  ASSERT_TRUE(temporary_dir_.CreateUniqueTempDir());
+  ASSERT_TRUE(private_archive_dir_.CreateUniqueTempDir());
+  ASSERT_TRUE(public_archive_dir_.CreateUniqueTempDir());
+}
+
+class TestArchivePublisherDelegate
+    : public OfflinePageArchivePublisherImpl::Delegate {
+ public:
+  TestArchivePublisherDelegate(int64_t id_to_use, bool installed)
+      : download_id_(id_to_use), last_removed_id_(0), installed_(installed) {}
+
+  bool IsDownloadManagerInstalled() override { return installed_; }
+  int64_t AddCompletedDownload(const std::string& title,
+                               const std::string& description,
+                               const std::string& path,
+                               int64_t length,
+                               const std::string& uri,
+                               const std::string& referer) override {
+    return download_id_;
+  }
+  int Remove(
+      const std::vector<int64_t>& android_download_manager_ids) override {
+    int count = static_cast<int>(android_download_manager_ids.size());
+    if (count > 0)
+      last_removed_id_ = android_download_manager_ids[count - 1];
+    return count;
+  }
+
+  int64_t last_removed_id() const { return last_removed_id_; }
+
+ private:
+  int64_t download_id_;
+  int64_t last_removed_id_;
+  bool installed_;
+};
+
+void OfflinePageArchivePublisherImplTest::PublishArchiveDone(
+    SavePageCallback save_page_callback,
+    const OfflinePageItem& offline_page,
+    PublishArchiveResult archive_result) {
+  publish_archive_result_ = archive_result;
+}
+
+void OfflinePageArchivePublisherImplTest::PumpLoop() {
+  task_runner_->RunUntilIdle();
+}
+
+TEST_F(OfflinePageArchivePublisherImplTest, PublishArchive) {
+  ArchiveManager archive_manager(temporary_dir_path(),
+                                 private_archive_dir_path(),
+                                 public_archive_dir_path(), task_runner());
+
+  OfflinePageArchivePublisherImpl publisher(&archive_manager);
+  TestArchivePublisherDelegate delegate(kDownloadId, true);
+  publisher.SetDelegateForTesting(&delegate);
+
+  // Put an offline page into the private dir, adjust the FilePath.
+  page_generator()->SetArchiveDirectory(temporary_dir_path());
+  OfflinePageItem offline_page = page_generator()->CreateItemWithTempFile();
+  base::FilePath old_file_path = offline_page.file_path;
+  base::FilePath new_file_path =
+      public_archive_dir_path().Append(offline_page.file_path.BaseName());
+
+  publisher.PublishArchive(
+      offline_page, base::ThreadTaskRunnerHandle::Get(),
+      base::BindOnce(&OfflinePageArchivePublisherImplTest::PublishArchiveDone,
+                     get_weak_ptr(), std::move(save_page_callback)));
+  PumpLoop();
+
+  EXPECT_EQ(SavePageResult::SUCCESS, publish_archive_result().move_result);
+  EXPECT_EQ(kDownloadId, publish_archive_result().download_id);
+  // Check there is a file in the new location.
+  EXPECT_TRUE(public_archive_dir_path().IsParent(
+      publish_archive_result().new_file_path));
+  EXPECT_TRUE(base::PathExists(publish_archive_result().new_file_path));
+  // Check there is no longer a file in the old location.
+  EXPECT_FALSE(base::PathExists(old_file_path));
+}
+
+TEST_F(OfflinePageArchivePublisherImplTest, UnpublishArchive) {
+  ArchiveManager archive_manager(temporary_dir_path(),
+                                 private_archive_dir_path(),
+                                 public_archive_dir_path(), task_runner());
+
+  TestArchivePublisherDelegate delegate(kDownloadId, true);
+  OfflinePageArchivePublisherImpl publisher(&archive_manager);
+  publisher.SetDelegateForTesting(&delegate);
+
+  std::vector<int64_t> ids_to_remove = {kDownloadId};
+  publisher.UnpublishArchives(std::move(ids_to_remove));
+
+  EXPECT_EQ(kDownloadId, delegate.last_removed_id());
+}
+
+// TODO(petewil): Add test cases for move failed, and adding to ADM failed.
+
+}  // namespace offline_pages
diff --git a/chrome/browser/offline_pages/android/offline_page_model_factory.cc b/chrome/browser/offline_pages/android/offline_page_model_factory.cc
index cf5a2d3..ee62667 100644
--- a/chrome/browser/offline_pages/android/offline_page_model_factory.cc
+++ b/chrome/browser/offline_pages/android/offline_page_model_factory.cc
@@ -17,7 +17,7 @@
 #include "base/time/default_clock.h"
 #include "chrome/browser/download/download_prefs.h"
 #include "chrome/browser/offline_pages/android/cct_origin_observer.h"
-#include "chrome/browser/offline_pages/android/offline_pages_download_manager_bridge.h"
+#include "chrome/browser/offline_pages/android/offline_page_archive_publisher_impl.h"
 #include "chrome/browser/offline_pages/download_archive_manager.h"
 #include "chrome/browser/offline_pages/fresh_offline_content_observer.h"
 #include "chrome/browser/profiles/profile.h"
@@ -78,16 +78,13 @@
       profile_key->GetPrefs());
   auto clock = std::make_unique<base::DefaultClock>();
 
-  auto download_manager =
-      std::make_unique<android::OfflinePagesDownloadManagerBridge>();
-  auto publisher = std::make_unique<OfflinePageArchivePublisher>(
-      archive_manager.get(), download_manager.get());
+  auto publisher =
+      std::make_unique<OfflinePageArchivePublisherImpl>(archive_manager.get());
 
   std::unique_ptr<OfflinePageModelTaskified> model =
       std::make_unique<OfflinePageModelTaskified>(
           std::move(metadata_store), std::move(archive_manager),
-          std::move(download_manager), std::move(publisher),
-          background_task_runner);
+          std::move(publisher), background_task_runner);
 
   CctOriginObserver::AttachToOfflinePageModel(model.get());
 
diff --git a/chrome/browser/offline_pages/android/offline_pages_download_manager_bridge.cc b/chrome/browser/offline_pages/android/offline_pages_download_manager_bridge.cc
deleted file mode 100644
index 6fd0b124..0000000
--- a/chrome/browser/offline_pages/android/offline_pages_download_manager_bridge.cc
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/offline_pages/android/offline_pages_download_manager_bridge.h"
-
-#include "base/android/jni_android.h"
-#include "base/android/jni_array.h"
-#include "base/android/jni_string.h"
-#include "base/android/scoped_java_ref.h"
-#include "chrome/android/chrome_jni_headers/OfflinePagesDownloadManagerBridge_jni.h"
-
-using base::android::JavaParamRef;
-using base::android::ScopedJavaLocalRef;
-
-namespace offline_pages {
-namespace android {
-
-bool OfflinePagesDownloadManagerBridge::IsDownloadManagerInstalled() {
-  JNIEnv* env = base::android::AttachCurrentThread();
-  jboolean is_installed =
-      Java_OfflinePagesDownloadManagerBridge_isAndroidDownloadManagerInstalled(
-          env);
-  return is_installed;
-}
-
-int64_t OfflinePagesDownloadManagerBridge::AddCompletedDownload(
-    const std::string& title,
-    const std::string& description,
-    const std::string& path,
-    int64_t length,
-    const std::string& uri,
-    const std::string& referer) {
-  JNIEnv* env = base::android::AttachCurrentThread();
-  // Convert strings to jstring references.
-  ScopedJavaLocalRef<jstring> j_title =
-      base::android::ConvertUTF8ToJavaString(env, title);
-  ScopedJavaLocalRef<jstring> j_description =
-      base::android::ConvertUTF8ToJavaString(env, description);
-  ScopedJavaLocalRef<jstring> j_path =
-      base::android::ConvertUTF8ToJavaString(env, path);
-  ScopedJavaLocalRef<jstring> j_uri =
-      base::android::ConvertUTF8ToJavaString(env, uri);
-  ScopedJavaLocalRef<jstring> j_referer =
-      base::android::ConvertUTF8ToJavaString(env, referer);
-
-  return Java_OfflinePagesDownloadManagerBridge_addCompletedDownload(
-      env, j_title, j_description, j_path, length, j_uri, j_referer);
-}
-
-int OfflinePagesDownloadManagerBridge::Remove(
-    const std::vector<int64_t>& android_download_manager_ids) {
-  JNIEnv* env = base::android::AttachCurrentThread();
-  // Build a JNI array with our ID data.
-  ScopedJavaLocalRef<jlongArray> j_ids =
-      base::android::ToJavaLongArray(env, android_download_manager_ids);
-
-  return Java_OfflinePagesDownloadManagerBridge_remove(env, j_ids);
-}
-
-}  // namespace android
-}  // namespace offline_pages
diff --git a/chrome/browser/offline_pages/android/offline_pages_download_manager_bridge.h b/chrome/browser/offline_pages/android/offline_pages_download_manager_bridge.h
deleted file mode 100644
index e22ba6af..0000000
--- a/chrome/browser/offline_pages/android/offline_pages_download_manager_bridge.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_OFFLINE_PAGES_ANDROID_OFFLINE_PAGES_DOWNLOAD_MANAGER_BRIDGE_H_
-#define CHROME_BROWSER_OFFLINE_PAGES_ANDROID_OFFLINE_PAGES_DOWNLOAD_MANAGER_BRIDGE_H_
-
-#include <stdint.h>
-
-#include <vector>
-
-#include "components/offline_pages/core/system_download_manager.h"
-
-namespace offline_pages {
-namespace android {
-
-// Bridge between C++ and Java for communicating with the AndroidDownloadManager
-// on Android.
-class OfflinePagesDownloadManagerBridge : public SystemDownloadManager {
- public:
-  bool IsDownloadManagerInstalled() override;
-
-  int64_t AddCompletedDownload(const std::string& title,
-                               const std::string& description,
-                               const std::string& path,
-                               int64_t length,
-                               const std::string& uri,
-                               const std::string& referer) override;
-
-  int Remove(const std::vector<int64_t>& android_download_manager_ids) override;
-};
-
-}  // namespace android
-}  // namespace offline_pages
-
-#endif  // CHROME_BROWSER_OFFLINE_PAGES_ANDROID_OFFLINE_PAGES_DOWNLOAD_MANAGER_BRIDGE_H_
diff --git a/chrome/browser/offline_pages/android/prefetch_test_bridge.cc b/chrome/browser/offline_pages/android/prefetch_test_bridge.cc
index 128c86e..a9940e1 100644
--- a/chrome/browser/offline_pages/android/prefetch_test_bridge.cc
+++ b/chrome/browser/offline_pages/android/prefetch_test_bridge.cc
@@ -62,7 +62,7 @@
   std::string image_data;
   base::android::JavaByteArrayToString(env, j_image_data, &image_data);
 
-  cache->SaveImage(url, image_data);
+  cache->SaveImage(url, image_data, false /* needs_transcoding */);
 }
 
 JNI_EXPORT void JNI_PrefetchTestBridge_AddCandidatePrefetchURL(
diff --git a/chrome/browser/offline_pages/offline_page_request_handler_unittest.cc b/chrome/browser/offline_pages/offline_page_request_handler_unittest.cc
index ab1de6d..758a3c32 100644
--- a/chrome/browser/offline_pages/offline_page_request_handler_unittest.cc
+++ b/chrome/browser/offline_pages/offline_page_request_handler_unittest.cc
@@ -37,7 +37,6 @@
 #include "components/offline_pages/core/offline_page_test_archive_publisher.h"
 #include "components/offline_pages/core/offline_page_test_archiver.h"
 #include "components/offline_pages/core/request_header/offline_page_navigation_ui_data.h"
-#include "components/offline_pages/core/stub_system_download_manager.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/resource_request_info.h"
@@ -865,8 +864,6 @@
       key->GetPath().Append(chrome::kOfflinePageMetadataDirname);
   std::unique_ptr<OfflinePageMetadataStore> metadata_store(
       new OfflinePageMetadataStore(task_runner, store_path));
-  auto download_manager =
-      std::make_unique<StubSystemDownloadManager>(kDownloadId, true);
 
   // Since we're not saving page into temporary dir, it's set the same as the
   // private dir.
@@ -875,13 +872,13 @@
       task_runner);
 
   auto archive_publisher = std::make_unique<OfflinePageTestArchivePublisher>(
-      archive_manager.get(), download_manager.get());
+      archive_manager.get(), kDownloadId);
   // TODO(iwells): Figure out how to make use_verbatim_archive_path go away.
   archive_publisher->use_verbatim_archive_path(true);
 
   return std::unique_ptr<KeyedService>(new OfflinePageModelTaskified(
       std::move(metadata_store), std::move(archive_manager),
-      std::move(download_manager), std::move(archive_publisher), task_runner));
+      std::move(archive_publisher), task_runner));
 }
 
 // static
diff --git a/chrome/browser/offline_pages/test_offline_page_model_builder.cc b/chrome/browser/offline_pages/test_offline_page_model_builder.cc
index 5003dde..3a02d2f2 100644
--- a/chrome/browser/offline_pages/test_offline_page_model_builder.cc
+++ b/chrome/browser/offline_pages/test_offline_page_model_builder.cc
@@ -17,7 +17,6 @@
 #include "components/offline_pages/core/model/offline_page_model_taskified.h"
 #include "components/offline_pages/core/offline_page_metadata_store.h"
 #include "components/offline_pages/core/offline_page_test_archive_publisher.h"
-#include "components/offline_pages/core/stub_system_download_manager.h"
 
 namespace {
 const int64_t kDownloadId = 42LL;
@@ -47,15 +46,12 @@
   auto archive_manager = std::make_unique<ArchiveManager>(
       temporary_archives_dir, private_archives_dir, public_archives_dir,
       task_runner);
-  auto stub_download_manager =
-      std::make_unique<StubSystemDownloadManager>(kDownloadId, true);
-
   auto publisher = std::make_unique<OfflinePageTestArchivePublisher>(
-      archive_manager.get(), stub_download_manager.get());
+      archive_manager.get(), kDownloadId);
 
   return std::unique_ptr<KeyedService>(new OfflinePageModelTaskified(
       std::move(metadata_store), std::move(archive_manager),
-      std::move(stub_download_manager), std::move(publisher), task_runner));
+      std::move(publisher), task_runner));
 }
 
 }  // namespace offline_pages
diff --git a/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_base.cc b/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_base.cc
index 5f1bff1..809db81 100644
--- a/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_base.cc
+++ b/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_base.cc
@@ -79,20 +79,15 @@
     return 0;
   }
 
-  std::string session_key = data->session_key();
   uint64_t page_id = data->page_id().value();
-
   char buf[8];
   base::WriteBigEndian<uint64_t>(buf, page_id);
 
-  std::vector<char> to_hash;
-  std::copy(session_key.begin(), session_key.end(),
-            std::back_inserter(to_hash));
-  to_hash.insert(to_hash.end(), buf, buf + 8);
+  std::string to_hash(data->session_key());
+  to_hash.append(std::begin(buf), std::end(buf));
 
   char hash[32];
-  crypto::SHA256HashString(base::StringPiece(to_hash.begin(), to_hash.end()),
-                           hash, 32);
+  crypto::SHA256HashString(base::StringPiece(to_hash), hash, 32);
 
   uint64_t uuid;
   base::ReadBigEndian<uint64_t>(hash, &uuid);
diff --git a/chrome/browser/page_load_metrics/observers/largest_contentful_paint_handler.h b/chrome/browser/page_load_metrics/observers/largest_contentful_paint_handler.h
index 6a11714..59f205f6 100644
--- a/chrome/browser/page_load_metrics/observers/largest_contentful_paint_handler.h
+++ b/chrome/browser/page_load_metrics/observers/largest_contentful_paint_handler.h
@@ -31,6 +31,7 @@
   page_load_metrics::PageLoadMetricsObserver::LargestContentType Type() const {
     return type_;
   }
+
   bool IsEmpty() const {
     // |size_| is not necessarily 0, for example, when the largest image is
     // still loading.
@@ -62,13 +63,21 @@
 
 class LargestContentfulPaintHandler {
  public:
+  using FrameTreeNodeId =
+      page_load_metrics::PageLoadMetricsObserver::FrameTreeNodeId;
   static void SetTestMode(bool enabled);
   LargestContentfulPaintHandler();
   ~LargestContentfulPaintHandler();
-  using FrameTreeNodeId =
-      page_load_metrics::PageLoadMetricsObserver::FrameTreeNodeId;
   void RecordTiming(const page_load_metrics::mojom::PaintTimingPtr&,
                     content::RenderFrameHost* subframe_rfh);
+  inline void RecordMainFrameTreeNodeId(int main_frame_tree_node_id) {
+    main_frame_tree_node_id_.emplace(main_frame_tree_node_id);
+  }
+
+  inline int MainFrameTreeNodeId() const {
+    return main_frame_tree_node_id_.value();
+  }
+
   // We merge the candidates from main frame and subframe to get the largest
   // candidate across all frames.
   const ContentfulPaintTimingInfo& MergeMainFrameAndSubframes();
@@ -83,6 +92,10 @@
   ContentfulPaint main_frame_contentful_paint_;
   ContentfulPaint subframe_contentful_paint_;
 
+  // Used for Telemetry to distinguish the LCP events from different
+  // navigations.
+  base::Optional<int> main_frame_tree_node_id_;
+
   // Navigation start offsets for the most recently committed document in each
   // frame.
   std::map<FrameTreeNodeId, base::TimeDelta> subframe_navigation_start_offset_;
diff --git a/chrome/browser/page_load_metrics/observers/previews_ukm_observer.cc b/chrome/browser/page_load_metrics/observers/previews_ukm_observer.cc
index 5fbda6d9..f70e1814 100644
--- a/chrome/browser/page_load_metrics/observers/previews_ukm_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/previews_ukm_observer.cc
@@ -118,6 +118,10 @@
                             previews::PreviewsType::RESOURCE_LOADING_HINTS) {
     resource_loading_hints_seen_ = true;
   }
+  if (previews_state && previews::GetMainFramePreviewsType(previews_state) ==
+                            previews::PreviewsType::DEFER_ALL_SCRIPT) {
+    defer_all_script_seen_ = true;
+  }
   if (previews_user_data->cache_control_no_transform_directive()) {
     origin_opt_out_occurred_ = true;
   }
@@ -139,6 +143,9 @@
   resource_loading_hints_eligibility_reason_ =
       previews_user_data->EligibilityReasonForPreview(
           previews::PreviewsType::RESOURCE_LOADING_HINTS);
+  defer_all_script_eligibility_reason_ =
+      previews_user_data->EligibilityReasonForPreview(
+          previews::PreviewsType::DEFER_ALL_SCRIPT);
   offline_eligibility_reason_ = previews_user_data->EligibilityReasonForPreview(
       previews::PreviewsType::OFFLINE);
 
@@ -219,9 +226,9 @@
   // preview can be attempted and not commit. This incurs the penalty but may
   // also cause no preview to be committed.
   if (!lite_page_seen_ && !noscript_seen_ && !resource_loading_hints_seen_ &&
-      !offline_preview_seen_ && !origin_opt_out_occurred_ &&
-      !save_data_enabled_ && !lite_page_redirect_seen_ &&
-      !navigation_restart_penalty_.has_value()) {
+      !defer_all_script_seen_ && !offline_preview_seen_ &&
+      !origin_opt_out_occurred_ && !save_data_enabled_ &&
+      !lite_page_redirect_seen_ && !navigation_restart_penalty_.has_value()) {
     return;
   }
 
@@ -235,6 +242,8 @@
     builder.Setnoscript(1);
   if (resource_loading_hints_seen_)
     builder.Setresource_loading_hints(1);
+  if (defer_all_script_seen_)
+    builder.Setdefer_all_script(1);
   if (offline_preview_seen_)
     builder.Setoffline_preview(1);
   // 2 is set here for legacy reasons as it denotes an optout through the
@@ -271,6 +280,11 @@
     builder.Setresource_loading_hints_eligibility_reason(
         static_cast<int>(resource_loading_hints_eligibility_reason_.value()));
   }
+  if (ShouldOptionalEligibilityReasonBeRecorded(
+          defer_all_script_eligibility_reason_)) {
+    builder.Setdefer_all_script_eligibility_reason(
+        static_cast<int>(defer_all_script_eligibility_reason_.value()));
+  }
   if (ShouldOptionalEligibilityReasonBeRecorded(offline_eligibility_reason_)) {
     builder.Setoffline_eligibility_reason(
         static_cast<int>(offline_eligibility_reason_.value()));
diff --git a/chrome/browser/page_load_metrics/observers/previews_ukm_observer.h b/chrome/browser/page_load_metrics/observers/previews_ukm_observer.h
index 6bc3c6e..7213918 100644
--- a/chrome/browser/page_load_metrics/observers/previews_ukm_observer.h
+++ b/chrome/browser/page_load_metrics/observers/previews_ukm_observer.h
@@ -69,24 +69,27 @@
   bool lite_page_redirect_seen_ = false;
   bool noscript_seen_ = false;
   bool resource_loading_hints_seen_ = false;
+  bool defer_all_script_seen_ = false;
   bool offline_preview_seen_ = false;
   bool opt_out_occurred_ = false;
   bool origin_opt_out_occurred_ = false;
   bool save_data_enabled_ = false;
   bool previews_likely_ = false;
   base::Optional<previews::PreviewsEligibilityReason>
-      lite_page_eligibility_reason_ = base::nullopt;
+      lite_page_eligibility_reason_;
   base::Optional<previews::PreviewsEligibilityReason>
-      lite_page_redirect_eligibility_reason_ = base::nullopt;
+      lite_page_redirect_eligibility_reason_;
   base::Optional<previews::PreviewsEligibilityReason>
-      noscript_eligibility_reason_ = base::nullopt;
+      noscript_eligibility_reason_;
   base::Optional<previews::PreviewsEligibilityReason>
-      resource_loading_hints_eligibility_reason_ = base::nullopt;
+      resource_loading_hints_eligibility_reason_;
   base::Optional<previews::PreviewsEligibilityReason>
-      offline_eligibility_reason_ = base::nullopt;
+      defer_all_script_eligibility_reason_;
+  base::Optional<previews::PreviewsEligibilityReason>
+      offline_eligibility_reason_;
   CoinFlipHoldbackResult coin_flip_result_ = CoinFlipHoldbackResult::kNotSet;
-  base::Optional<base::TimeDelta> navigation_restart_penalty_ = base::nullopt;
-  base::Optional<std::string> serialized_hint_version_string_ = base::nullopt;
+  base::Optional<base::TimeDelta> navigation_restart_penalty_;
+  base::Optional<std::string> serialized_hint_version_string_;
 
   SEQUENCE_CHECKER(sequence_checker_);
 
diff --git a/chrome/browser/page_load_metrics/observers/previews_ukm_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/previews_ukm_observer_unittest.cc
index c404b93..9c33c386 100644
--- a/chrome/browser/page_load_metrics/observers/previews_ukm_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/previews_ukm_observer_unittest.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.h"
 #include "chrome/browser/page_load_metrics/page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/page_load_tracker.h"
+#include "chrome/browser/previews/previews_content_util.h"
 #include "chrome/browser/previews/previews_ui_tab_helper.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "components/optimization_guide/proto/hints.pb.h"
@@ -42,29 +43,19 @@
 class TestPreviewsUKMObserver : public PreviewsUKMObserver {
  public:
   TestPreviewsUKMObserver(
-      PreviewsType committed_preview,
+      content::PreviewsState committed_state,
       content::PreviewsState allowed_state,
-      bool lite_page_received,
-      bool lite_page_redirect_received,
-      bool noscript_on,
-      bool resource_loading_hints_on,
       bool origin_opt_out_received,
       bool save_data_enabled,
-      bool is_offline_preview,
       CoinFlipHoldbackResult coin_flip_result,
       std::unordered_map<PreviewsType, PreviewsEligibilityReason>
           eligibility_reasons,
       base::Optional<base::TimeDelta> navigation_restart_penalty,
       base::Optional<std::string> hint_version_string)
-      : committed_preview_(committed_preview),
+      : committed_state_(committed_state),
         allowed_state_(allowed_state),
-        lite_page_received_(lite_page_received),
-        lite_page_redirect_received_(lite_page_redirect_received),
-        noscript_on_(noscript_on),
-        resource_loading_hints_on_(resource_loading_hints_on),
         origin_opt_out_received_(origin_opt_out_received),
         save_data_enabled_(save_data_enabled),
-        is_offline_preview_(is_offline_preview),
         coin_flip_result_(coin_flip_result),
         eligibility_reasons_(eligibility_reasons),
         navigation_restart_penalty_(navigation_restart_penalty),
@@ -81,45 +72,12 @@
         ui_tab_helper->CreatePreviewsUserDataForNavigationHandle(
             navigation_handle, 1u);
 
-    user_data->SetCommittedPreviewsTypeForTesting(committed_preview_);
     user_data->set_allowed_previews_state(allowed_state_);
+    user_data->set_committed_previews_state(committed_state_);
+    user_data->SetCommittedPreviewsTypeForTesting(
+        previews::GetMainFramePreviewsType(committed_state_));
     user_data->set_coin_flip_holdback_result(coin_flip_result_);
 
-    if (noscript_on_) {
-      content::PreviewsState previews_state =
-          user_data->CommittedPreviewsState();
-      user_data->set_committed_previews_state(previews_state |=
-                                              content::NOSCRIPT_ON);
-    }
-
-    if (resource_loading_hints_on_) {
-      content::PreviewsState previews_state =
-          user_data->CommittedPreviewsState();
-      user_data->set_committed_previews_state(
-          previews_state |= content::RESOURCE_LOADING_HINTS_ON);
-    }
-
-    if (lite_page_received_) {
-      content::PreviewsState previews_state =
-          user_data->CommittedPreviewsState();
-      user_data->set_committed_previews_state(previews_state |=
-                                              content::SERVER_LITE_PAGE_ON);
-    }
-
-    if (lite_page_redirect_received_) {
-      content::PreviewsState previews_state =
-          user_data->CommittedPreviewsState();
-      user_data->set_committed_previews_state(previews_state |=
-                                              content::LITE_PAGE_REDIRECT_ON);
-    }
-
-    if (is_offline_preview_) {
-      content::PreviewsState previews_state =
-          user_data->CommittedPreviewsState();
-      user_data->set_committed_previews_state(previews_state |=
-                                              content::OFFLINE_PAGE_ON);
-    }
-
     if (navigation_restart_penalty_.has_value()) {
       user_data->set_server_lite_page_info(
           std::make_unique<previews::PreviewsUserData::ServerLitePageInfo>());
@@ -152,18 +110,13 @@
   }
 
   bool IsOfflinePreview(content::WebContents* web_contents) const override {
-    return is_offline_preview_;
+    return committed_state_ == content::OFFLINE_PAGE_ON;
   }
 
-  PreviewsType committed_preview_;
+  content::PreviewsState committed_state_;
   content::PreviewsState allowed_state_;
-  bool lite_page_received_;
-  bool lite_page_redirect_received_;
-  bool noscript_on_;
-  bool resource_loading_hints_on_;
   bool origin_opt_out_received_;
   const bool save_data_enabled_;
-  const bool is_offline_preview_;
   CoinFlipHoldbackResult coin_flip_result_;
   std::unordered_map<PreviewsType, PreviewsEligibilityReason>
       eligibility_reasons_;
@@ -179,49 +132,35 @@
   PreviewsUKMObserverTest() {}
   ~PreviewsUKMObserverTest() override {}
 
-  void RunTest(content::PreviewsState allowed_state,
-               PreviewsType committed_preview,
-               bool lite_page_received,
-               bool lite_page_redirect_received,
-               bool noscript_on,
-               bool resource_loading_hints_on,
+  void RunTest(content::PreviewsState committed_state,
+               content::PreviewsState allowed_state,
                bool origin_opt_out,
                bool save_data_enabled,
-               bool is_offline_preview,
                CoinFlipHoldbackResult coin_flip_result,
                std::unordered_map<PreviewsType, PreviewsEligibilityReason>
                    eligibility_reasons,
                base::Optional<base::TimeDelta> navigation_restart_penalty,
                base::Optional<std::string> hint_version_string) {
-    committed_preview_ = committed_preview;
+    committed_state_ = committed_state;
     allowed_state_ = allowed_state;
-    lite_page_received_ = lite_page_received;
-    lite_page_redirect_received_ = lite_page_redirect_received;
-    noscript_on_ = noscript_on;
-    resource_loading_hints_on_ = resource_loading_hints_on;
     origin_opt_out_ = origin_opt_out;
     save_data_enabled_ = save_data_enabled;
-    is_offline_preview_ = is_offline_preview;
     coin_flip_result_ = coin_flip_result;
     eligibility_reasons_ = eligibility_reasons;
     navigation_restart_penalty_ = navigation_restart_penalty;
     hint_version_string_ = hint_version_string;
     auto navigation = content::NavigationSimulator::CreateBrowserInitiated(
         GURL(kDefaultTestUrl), web_contents());
-    if (is_offline_preview_)
+    if (committed_state == content::OFFLINE_PAGE_ON)
       navigation->SetContentsMimeType("multipart/related");
 
     navigation->Commit();
   }
 
-  void ValidateUKM(bool lite_page_expected,
-                   bool lite_page_redirect_expected,
-                   bool noscript_expected,
-                   bool resource_loading_hints_expected,
+  void ValidateUKM(content::PreviewsState expected_recorded_previews,
                    int opt_out_value,
                    bool origin_opt_out_expected,
                    bool save_data_enabled_expected,
-                   bool offline_preview_expected,
                    bool previews_likely_expected,
                    CoinFlipHoldbackResult coin_flip_result_expected,
                    std::unordered_map<PreviewsType, PreviewsEligibilityReason>
@@ -229,10 +168,8 @@
                    base::Optional<base::TimeDelta> navigation_restart_penalty,
                    base::Optional<int64_t> hint_generation_timestamp,
                    base::Optional<int> hint_source) {
-    ValidatePreviewsUKM(lite_page_expected, lite_page_redirect_expected,
-                        noscript_expected, resource_loading_hints_expected,
-                        opt_out_value, origin_opt_out_expected,
-                        save_data_enabled_expected, offline_preview_expected,
+    ValidatePreviewsUKM(expected_recorded_previews, opt_out_value,
+                        origin_opt_out_expected, save_data_enabled_expected,
                         previews_likely_expected, coin_flip_result_expected,
                         eligibility_reasons, navigation_restart_penalty);
     ValidateOptimizationGuideUKM(hint_generation_timestamp, hint_source);
@@ -246,18 +183,12 @@
  protected:
   void RegisterObservers(page_load_metrics::PageLoadTracker* tracker) override {
     tracker->AddObserver(std::make_unique<TestPreviewsUKMObserver>(
-        committed_preview_, allowed_state_, lite_page_received_,
-        lite_page_redirect_received_, noscript_on_, resource_loading_hints_on_,
-        origin_opt_out_, save_data_enabled_, is_offline_preview_,
+        committed_state_, allowed_state_, origin_opt_out_, save_data_enabled_,
         coin_flip_result_, eligibility_reasons_, navigation_restart_penalty_,
         hint_version_string_));
     // Data is only added to the first navigation after RunTest().
-    committed_preview_ = PreviewsType::NONE;
+    committed_state_ = content::PREVIEWS_OFF;
     allowed_state_ = content::PREVIEWS_OFF;
-    lite_page_received_ = false;
-    lite_page_redirect_received_ = false;
-    noscript_on_ = false;
-    resource_loading_hints_on_ = false;
     origin_opt_out_ = false;
     coin_flip_result_ = CoinFlipHoldbackResult::kNotSet;
     eligibility_reasons_.clear();
@@ -267,14 +198,10 @@
 
  private:
   void ValidatePreviewsUKM(
-      bool lite_page_expected,
-      bool lite_page_redirect_expected,
-      bool noscript_expected,
-      bool resource_loading_hints_expected,
+      content::PreviewsState expected_recorded_previews,
       int opt_out_value,
       bool origin_opt_out_expected,
       bool save_data_enabled_expected,
-      bool offline_preview_expected,
       bool previews_likely_expected,
       CoinFlipHoldbackResult coin_flip_result_expected,
       std::unordered_map<PreviewsType, PreviewsEligibilityReason>
@@ -282,10 +209,8 @@
       base::Optional<base::TimeDelta> navigation_restart_penalty) {
     using UkmEntry = ukm::builders::Previews;
     auto entries = test_ukm_recorder().GetEntriesByName(UkmEntry::kEntryName);
-    if (!lite_page_expected && !lite_page_redirect_expected &&
-        !noscript_expected && !resource_loading_hints_expected &&
-        opt_out_value == 0 && !origin_opt_out_expected &&
-        !save_data_enabled_expected && !offline_preview_expected &&
+    if (expected_recorded_previews == 0 && opt_out_value == 0 &&
+        !origin_opt_out_expected && !save_data_enabled_expected &&
         !previews_likely_expected &&
         coin_flip_result_expected == CoinFlipHoldbackResult::kNotSet &&
         !navigation_restart_penalty.has_value()) {
@@ -293,27 +218,38 @@
       return;
     }
     EXPECT_EQ(1u, entries.size());
-    for (const auto* const entry : entries) {
-      test_ukm_recorder().ExpectEntrySourceHasUrl(entry, GURL(kDefaultTestUrl));
-      EXPECT_EQ(lite_page_expected, test_ukm_recorder().EntryHasMetric(
-                                        entry, UkmEntry::klite_pageName));
-      EXPECT_EQ(lite_page_redirect_expected,
-                test_ukm_recorder().EntryHasMetric(
-                    entry, UkmEntry::klite_page_redirectName));
-      EXPECT_EQ(noscript_expected, test_ukm_recorder().EntryHasMetric(
-                                       entry, UkmEntry::knoscriptName));
-      EXPECT_EQ(resource_loading_hints_expected,
-                test_ukm_recorder().EntryHasMetric(
-                    entry, UkmEntry::kresource_loading_hintsName));
-      EXPECT_EQ(offline_preview_expected,
-                test_ukm_recorder().EntryHasMetric(
-                    entry, UkmEntry::koffline_previewName));
-      EXPECT_EQ(opt_out_value != 0, test_ukm_recorder().EntryHasMetric(
-                                        entry, UkmEntry::kopt_outName));
-      if (opt_out_value != 0) {
-        test_ukm_recorder().ExpectEntryMetric(entry, UkmEntry::kopt_outName,
-                                              opt_out_value);
-      }
+
+    const auto* const entry = entries.front();
+    test_ukm_recorder().ExpectEntrySourceHasUrl(entry, GURL(kDefaultTestUrl));
+
+    // Collect the set of recorded previews into a PreviewsState bitmask to
+    // compare against the expected previews.
+    content::PreviewsState recorded_previews = 0;
+    if (test_ukm_recorder().EntryHasMetric(entry,
+                                           UkmEntry::koffline_previewName))
+      recorded_previews |= content::OFFLINE_PAGE_ON;
+    if (test_ukm_recorder().EntryHasMetric(entry, UkmEntry::klite_pageName))
+      recorded_previews |= content::SERVER_LITE_PAGE_ON;
+    if (test_ukm_recorder().EntryHasMetric(entry,
+                                           UkmEntry::klite_page_redirectName)) {
+      recorded_previews |= content::LITE_PAGE_REDIRECT_ON;
+    }
+    if (test_ukm_recorder().EntryHasMetric(entry, UkmEntry::knoscriptName))
+      recorded_previews |= content::NOSCRIPT_ON;
+    if (test_ukm_recorder().EntryHasMetric(
+            entry, UkmEntry::kresource_loading_hintsName))
+      recorded_previews |= content::RESOURCE_LOADING_HINTS_ON;
+    if (test_ukm_recorder().EntryHasMetric(entry,
+                                           UkmEntry::kdefer_all_scriptName))
+      recorded_previews |= content::DEFER_ALL_SCRIPT_ON;
+    EXPECT_EQ(expected_recorded_previews, recorded_previews);
+
+    EXPECT_EQ(opt_out_value != 0, test_ukm_recorder().EntryHasMetric(
+                                      entry, UkmEntry::kopt_outName));
+    if (opt_out_value != 0) {
+      test_ukm_recorder().ExpectEntryMetric(entry, UkmEntry::kopt_outName,
+                                            opt_out_value);
+    }
       EXPECT_EQ(origin_opt_out_expected,
                 test_ukm_recorder().EntryHasMetric(
                     entry, UkmEntry::korigin_opt_outName));
@@ -386,7 +322,6 @@
         EXPECT_FALSE(test_ukm_recorder().EntryHasMetric(
             entry, UkmEntry::koffline_eligibility_reasonName));
       }
-    }
   }
 
   void ValidateOptimizationGuideUKM(
@@ -420,15 +355,10 @@
     }
   }
 
-  PreviewsType committed_preview_ = PreviewsType::NONE;
+  content::PreviewsState committed_state_ = content::PREVIEWS_OFF;
   content::PreviewsState allowed_state_ = content::PREVIEWS_OFF;
-  bool lite_page_received_ = false;
-  bool lite_page_redirect_received_ = false;
-  bool noscript_on_ = false;
-  bool resource_loading_hints_on_ = false;
   bool origin_opt_out_ = false;
   bool save_data_enabled_ = false;
-  bool is_offline_preview_ = false;
   std::unordered_map<PreviewsType, PreviewsEligibilityReason>
       eligibility_reasons_ = {};
   CoinFlipHoldbackResult coin_flip_result_ = CoinFlipHoldbackResult::kNotSet;
@@ -439,35 +369,28 @@
 };
 
 TEST_F(PreviewsUKMObserverTest, NoPreviewSeen) {
-  RunTest(content::PREVIEWS_UNSPECIFIED /* allowed_state */, PreviewsType::NONE,
-          false /* lite_page_received */,
-          false /* lite_page_redirect_received */, false /* noscript_on */,
-          false /* resource_loading_hints_on */, false /* origin_opt_out */,
-          false /* save_data_enabled */, false /* is_offline_preview */,
+  RunTest(content::PREVIEWS_OFF /* committed_state */,
+          content::PREVIEWS_UNSPECIFIED /* allowed_state */,
+          false /* origin_opt_out */, false /* save_data_enabled */,
           CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
   NavigateToUntrackedUrl();
 
-  ValidateUKM(false /* lite_page_expected */,
-              false /* lite_page_redirect_expected */,
-              false /* noscript_expected */,
-              false /* resource_loading_hints_expected */,
-              0 /* opt_out_value */, false /* origin_opt_out_expected */,
+  ValidateUKM(content::PREVIEWS_UNSPECIFIED, 0 /* opt_out_value */,
+              false /* origin_opt_out_expected */,
               false /* save_data_enabled_expected */,
-              false /* offline_preview_expected */, false /* previews_likely */,
-              CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
+              false /* previews_likely */, CoinFlipHoldbackResult::kNotSet,
+              {} /* eligibility_reasons */,
               base::nullopt /* navigation_restart_penalty */,
               base::nullopt /* hint_generation_timestamp */,
               base::nullopt /* hint_source */);
 }
 
 TEST_F(PreviewsUKMObserverTest, UntrackedPreviewTypeOptOut) {
-  RunTest(content::PREVIEWS_UNSPECIFIED /* allowed_state */, PreviewsType::NONE,
-          false /* lite_page_received */,
-          false /* lite_page_redirect_received */, false /* noscript_on */,
-          false /* resource_loading_hints_on */, false /* origin_opt_out */,
-          false /* save_data_enabled */, false /* is_offline_preview */,
+  RunTest(content::PREVIEWS_OFF /* committed_state */,
+          content::PREVIEWS_UNSPECIFIED /* allowed_state */,
+          false /* origin_opt_out */, false /* save_data_enabled */,
           CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
@@ -475,50 +398,41 @@
   NavigateToUntrackedUrl();
 
   // Opt out should not be added since we don't track this type.
-  ValidateUKM(false /* lite_page_expected */,
-              false /* lite_page_redirect_expected */,
-              false /* noscript_expected */,
-              false /* resource_loading_hints_expected */,
-              0 /* opt_out_value */, false /* origin_opt_out_expected */,
+  ValidateUKM(content::PREVIEWS_UNSPECIFIED, 0 /* opt_out_value */,
+              false /* origin_opt_out_expected */,
               false /* save_data_enabled_expected */,
-              false /* offline_preview_expected */, false /* previews_likely */,
-              CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
+              false /* previews_likely */, CoinFlipHoldbackResult::kNotSet,
+              {} /* eligibility_reasons */,
               base::nullopt /* navigation_restart_penalty */,
               base::nullopt /* hint_generation_timestamp */,
               base::nullopt /* hint_source */);
 }
 
 TEST_F(PreviewsUKMObserverTest, LitePageSeen) {
-  RunTest(content::PREVIEWS_UNSPECIFIED /* allowed_state */, PreviewsType::NONE,
-          true /* lite_page_received */,
-          false /* lite_page_redirect_received */, false /* noscript_on */,
-          false /* resource_loading_hints_on */, false /* origin_opt_out */,
-          false /* save_data_enabled */, false /* is_offline_preview */,
+  RunTest(content::SERVER_LITE_PAGE_ON /* committed_state */,
+          content::SERVER_LITE_PAGE_ON |
+              content::DEFER_ALL_SCRIPT_ON /* allowed_state */,
+          false /* origin_opt_out */, false /* save_data_enabled */,
           CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
 
   NavigateToUntrackedUrl();
 
-  ValidateUKM(true /* lite_page_expected */,
-              false /* lite_page_redirect_expected */,
-              false /* noscript_expected */,
-              false /* resource_loading_hints_expected */,
-              0 /* opt_out_value */, false /* origin_opt_out_expected */,
+  ValidateUKM(content::SERVER_LITE_PAGE_ON, 0 /* opt_out_value */,
+              false /* origin_opt_out_expected */,
               false /* save_data_enabled_expected */,
-              false /* offline_preview_expected */, false /* previews_likely */,
-              CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
+              true /* previews_likely */, CoinFlipHoldbackResult::kNotSet,
+              {} /* eligibility_reasons */,
               base::nullopt /* navigation_restart_penalty */,
               base::nullopt /* hint_generation_timestamp */,
               base::nullopt /* hint_source */);
 }
 
 TEST_F(PreviewsUKMObserverTest, LitePageOptOutChip) {
-  RunTest(content::PREVIEWS_UNSPECIFIED /* allowed_state */,
-          PreviewsType::LITE_PAGE, true /* lite_page_received */,
-          false /* lite_page_redirect_received */, false /* noscript_on */,
-          false /* resource_loading_hints_on */, false /* origin_opt_out */,
-          false /* save_data_enabled */, false /* is_offline_preview */,
+  RunTest(content::SERVER_LITE_PAGE_ON /* committed_state */,
+          content::SERVER_LITE_PAGE_ON /* allowed_state */,
+          false /* origin_opt_out */, false /* save_data_enabled */,
           CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
@@ -526,50 +440,41 @@
   observer()->BroadcastEventToObservers(PreviewsUITabHelper::OptOutEventKey());
   NavigateToUntrackedUrl();
 
-  ValidateUKM(true /* lite_page_expected */,
-              false /* lite_page_redirect_expected */,
-              false /* noscript_expected */,
-              false /* resource_loading_hints_expected */,
-              2 /* opt_out_value */, false /* origin_opt_out_expected */,
+  ValidateUKM(content::SERVER_LITE_PAGE_ON, 2 /* opt_out_value */,
+              false /* origin_opt_out_expected */,
               false /* save_data_enabled_expected */,
-              false /* offline_preview_expected */, false /* previews_likely */,
-              CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
+              true /* previews_likely */, CoinFlipHoldbackResult::kNotSet,
+              {} /* eligibility_reasons */,
               base::nullopt /* navigation_restart_penalty */,
               base::nullopt /* hint_generation_timestamp */,
               base::nullopt /* hint_source */);
 }
 
 TEST_F(PreviewsUKMObserverTest, LitePageRedirectSeen) {
-  RunTest(content::PREVIEWS_UNSPECIFIED /* allowed_state */, PreviewsType::NONE,
-          false /* lite_page_received */,
-          true /* lite_page_redirect_received */, false /* noscript_on */,
-          false /* resource_loading_hints_on */, false /* origin_opt_out */,
-          false /* save_data_enabled */, false /* is_offline_preview */,
+  RunTest(content::LITE_PAGE_REDIRECT_ON /* committed_state */,
+          content::LITE_PAGE_REDIRECT_ON |
+              content::OFFLINE_PAGE_ON /* allowed_state */,
+          false /* origin_opt_out */, false /* save_data_enabled */,
           CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
 
   NavigateToUntrackedUrl();
 
-  ValidateUKM(false /* lite_page_expected */,
-              true /* lite_page_redirect_expected */,
-              false /* noscript_expected */,
-              false /* resource_loading_hints_expected */,
-              0 /* opt_out_value */, false /* origin_opt_out_expected */,
+  ValidateUKM(content::LITE_PAGE_REDIRECT_ON, 0 /* opt_out_value */,
+              false /* origin_opt_out_expected */,
               false /* save_data_enabled_expected */,
-              false /* offline_preview_expected */, false /* previews_likely */,
-              CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
+              true /* previews_likely */, CoinFlipHoldbackResult::kNotSet,
+              {} /* eligibility_reasons */,
               base::nullopt /* navigation_restart_penalty */,
               base::nullopt /* hint_generation_timestamp */,
               base::nullopt /* hint_source */);
 }
 
 TEST_F(PreviewsUKMObserverTest, LitePageRedirectOptOutChip) {
-  RunTest(content::PREVIEWS_UNSPECIFIED /* allowed_state */,
-          PreviewsType::LITE_PAGE_REDIRECT, false /* lite_page_received */,
-          true /* lite_page_redirect_received */, false /* noscript_on */,
-          false /* resource_loading_hints_on */, false /* origin_opt_out */,
-          false /* save_data_enabled */, false /* is_offline_preview */,
+  RunTest(content::LITE_PAGE_REDIRECT_ON /* committed_state */,
+          content::LITE_PAGE_REDIRECT_ON /* allowed_state */,
+          false /* origin_opt_out */, false /* save_data_enabled */,
           CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
@@ -577,49 +482,39 @@
   observer()->BroadcastEventToObservers(PreviewsUITabHelper::OptOutEventKey());
   NavigateToUntrackedUrl();
 
-  ValidateUKM(false /* lite_page_expected */,
-              true /* lite_page_redirect_expected */,
-              false /* noscript_expected */,
-              false /* resource_loading_hints_expected */,
-              2 /* opt_out_value */, false /* origin_opt_out_expected */,
+  ValidateUKM(content::LITE_PAGE_REDIRECT_ON, 2 /* opt_out_value */,
+              false /* origin_opt_out_expected */,
               false /* save_data_enabled_expected */,
-              false /* offline_preview_expected */, false /* previews_likely */,
-              CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
+              true /* previews_likely */, CoinFlipHoldbackResult::kNotSet,
+              {} /* eligibility_reasons */,
               base::nullopt /* navigation_restart_penalty */,
               base::nullopt /* hint_generation_timestamp */,
               base::nullopt /* hint_source */);
 }
 
 TEST_F(PreviewsUKMObserverTest, NoScriptSeenWithBadVersionString) {
-  RunTest(content::PREVIEWS_UNSPECIFIED /* allowed_state */,
-          PreviewsType::NOSCRIPT, false /* lite_page_received */,
-          false /* lite_page_redirect_received */, true /* noscript_on */,
-          false /* resource_loading_hints_on */, false /* origin_opt_out */,
-          false /* save_data_enabled */, false /* is_offline_preview */,
-          CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
+  RunTest(content::NOSCRIPT_ON /* committed_state */,
+          content::NOSCRIPT_ON /* allowed_state */, false /* origin_opt_out */,
+          false /* save_data_enabled */, CoinFlipHoldbackResult::kNotSet,
+          {} /* eligibility_reasons */,
           base::nullopt /* navigation_restart_penalty */, "badversion");
 
   NavigateToUntrackedUrl();
 
-  ValidateUKM(
-
-      false /* lite_page_expected */, false /* lite_page_redirect_expected */,
-      true /* noscript_expected */, false /* resource_loading_hints_expected */,
-      0 /* opt_out_value */, false /* origin_opt_out_expected */,
-      false /* save_data_enabled_expected */,
-      false /* offline_preview_expected */, false /* previews_likely */,
-      CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
-      base::nullopt /* navigation_restart_penalty */,
-      base::nullopt /* hint_generation_timestamp */,
-      base::nullopt /* hint_source */);
+  ValidateUKM(content::NOSCRIPT_ON, 0 /* opt_out_value */,
+              false /* origin_opt_out_expected */,
+              false /* save_data_enabled_expected */,
+              true /* previews_likely */, CoinFlipHoldbackResult::kNotSet,
+              {} /* eligibility_reasons */,
+              base::nullopt /* navigation_restart_penalty */,
+              base::nullopt /* hint_generation_timestamp */,
+              base::nullopt /* hint_source */);
 }
 
 TEST_F(PreviewsUKMObserverTest, NoScriptOptOutChip) {
-  RunTest(content::PREVIEWS_UNSPECIFIED /* allowed_state */,
-          PreviewsType::NOSCRIPT, false /* lite_page_received */,
-          false /* lite_page_redirect_received */, true /* noscript_on */,
-          false /* resource_loading_hints_on */, false /* origin_opt_out */,
-          false /* save_data_enabled */, false /* is_offline_preview */,
+  RunTest(content::NOSCRIPT_ON /* committed_state */,
+          content::PREVIEWS_UNSPECIFIED /* allowed_state */,
+          false /* origin_opt_out */, false /* save_data_enabled */,
           CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
@@ -627,75 +522,60 @@
   observer()->BroadcastEventToObservers(PreviewsUITabHelper::OptOutEventKey());
   NavigateToUntrackedUrl();
 
-  ValidateUKM(
-
-      false /* lite_page_expected */, false /* lite_page_redirect_expected */,
-      true /* noscript_expected */, false /* resource_loading_hints_expected */,
-      2 /* opt_out_value */, false /* origin_opt_out_expected */,
-      false /* save_data_enabled_expected */,
-      false /* offline_preview_expected */, false /* previews_likely */,
-      CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
-      base::nullopt /* navigation_restart_penalty */,
-      base::nullopt /* hint_generation_timestamp */,
-      base::nullopt /* hint_source */);
+  ValidateUKM(content::NOSCRIPT_ON, 2 /* opt_out_value */,
+              false /* origin_opt_out_expected */,
+              false /* save_data_enabled_expected */,
+              true /* previews_likely */, CoinFlipHoldbackResult::kNotSet,
+              {} /* eligibility_reasons */,
+              base::nullopt /* navigation_restart_penalty */,
+              base::nullopt /* hint_generation_timestamp */,
+              base::nullopt /* hint_source */);
 }
 
 TEST_F(PreviewsUKMObserverTest, OfflinePreviewsSeen) {
-  RunTest(content::PREVIEWS_UNSPECIFIED /* allowed_state */,
-          PreviewsType::OFFLINE, false /* lite_page_received */,
-          false /* lite_page_redirect_received */, false /* noscript_on */,
-          false /* resource_loading_hints_on */, false /* origin_opt_out */,
-          false /* save_data_enabled */, true /* is_offline_preview */,
+  RunTest(content::OFFLINE_PAGE_ON /* committed_state */,
+          content::PREVIEWS_UNSPECIFIED /* allowed_state */,
+          false /* origin_opt_out */, false /* save_data_enabled */,
           CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
 
   NavigateToUntrackedUrl();
 
-  ValidateUKM(false /* lite_page_expected */,
-              false /* lite_page_redirect_expected */,
-              false /* noscript_expected */,
-              false /* resource_loading_hints_expected */,
-              0 /* opt_out_value */, false /* origin_opt_out_expected */,
+  ValidateUKM(content::OFFLINE_PAGE_ON, 0 /* opt_out_value */,
+              false /* origin_opt_out_expected */,
               false /* save_data_enabled_expected */,
-              true /* offline_preview_expected */, false /* previews_likely */,
-              CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
+              true /* previews_likely */, CoinFlipHoldbackResult::kNotSet,
+              {} /* eligibility_reasons */,
               base::nullopt /* navigation_restart_penalty */,
               base::nullopt /* hint_generation_timestamp */,
               base::nullopt /* hint_source */);
 }
 
 TEST_F(PreviewsUKMObserverTest, ResourceLoadingHintsSeen) {
-  RunTest(content::PREVIEWS_UNSPECIFIED /* allowed_state */,
-          PreviewsType::RESOURCE_LOADING_HINTS, false /* lite_page_received */,
-          false /* lite_page_redirect_received */, false /* noscript_on */,
-          true /* resource_loading_hints_on */, false /* origin_opt_out */,
-          false /* save_data_enabled */, false /* is_offline_preview */,
+  RunTest(content::RESOURCE_LOADING_HINTS_ON /* committed_state */,
+          content::PREVIEWS_UNSPECIFIED /* allowed_state */,
+          false /* origin_opt_out */, false /* save_data_enabled */,
           CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
 
   NavigateToUntrackedUrl();
 
-  ValidateUKM(
-
-      false /* lite_page_expected */, false /* lite_page_redirect_expected */,
-      false /* noscript_expected */, true /* resource_loading_hints_expected */,
-      0 /* opt_out_value */, false /* origin_opt_out_expected */,
-      false /* save_data_enabled_expected */,
-      false /* offline_preview_expected */, false /* previews_likely */,
-      CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
-      base::nullopt /* navigation_restart_penalty */,
-      base::nullopt /* hint_generation_timestamp */,
-      base::nullopt /* hint_source */);
+  ValidateUKM(content::RESOURCE_LOADING_HINTS_ON, 0 /* opt_out_value */,
+              false /* origin_opt_out_expected */,
+              false /* save_data_enabled_expected */,
+              true /* previews_likely */, CoinFlipHoldbackResult::kNotSet,
+              {} /* eligibility_reasons */,
+              base::nullopt /* navigation_restart_penalty */,
+              base::nullopt /* hint_generation_timestamp */,
+              base::nullopt /* hint_source */);
 }
 
 TEST_F(PreviewsUKMObserverTest, ResourceLoadingHintsOptOutChip) {
-  RunTest(content::PREVIEWS_UNSPECIFIED /* allowed_state */,
-          PreviewsType::RESOURCE_LOADING_HINTS, false /* lite_page_received */,
-          false /* lite_page_redirect_received */, false /* noscript_on */,
-          true /* resource_loading_hints_on */, false /* origin_opt_out */,
-          false /* save_data_enabled */, false /* is_offline_preview */,
+  RunTest(content::RESOURCE_LOADING_HINTS_ON /* committed_state */,
+          content::PREVIEWS_UNSPECIFIED /* allowed_state */,
+          false /* origin_opt_out */, false /* save_data_enabled */,
           CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
@@ -703,64 +583,92 @@
   observer()->BroadcastEventToObservers(PreviewsUITabHelper::OptOutEventKey());
   NavigateToUntrackedUrl();
 
-  ValidateUKM(
-
-      false /* lite_page_expected */, false /* lite_page_redirect_expected */,
-      false /* noscript_expected */, true /* resource_loading_hints_expected */,
-      2 /* opt_out_value */, false /* origin_opt_out_expected */,
-      false /* save_data_enabled_expected */,
-      false /* offline_preview_expected */, false /* previews_likely */,
-      CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
-      base::nullopt /* navigation_restart_penalty */,
-      base::nullopt /* hint_generation_timestamp */,
-      base::nullopt /* hint_source */);
+  ValidateUKM(content::RESOURCE_LOADING_HINTS_ON, 2 /* opt_out_value */,
+              false /* origin_opt_out_expected */,
+              false /* save_data_enabled_expected */,
+              true /* previews_likely */, CoinFlipHoldbackResult::kNotSet,
+              {} /* eligibility_reasons */,
+              base::nullopt /* navigation_restart_penalty */,
+              base::nullopt /* hint_generation_timestamp */,
+              base::nullopt /* hint_source */);
 }
 
-TEST_F(PreviewsUKMObserverTest, OriginOptOut) {
-  RunTest(content::PREVIEWS_UNSPECIFIED /* allowed_state */, PreviewsType::NONE,
-          false /* lite_page_received */,
-          false /* lite_page_redirect_received */, false /* noscript_on */,
-          false /* resource_loading_hints_on */, true /* origin_opt_out */,
-          false /* save_data_enabled */, false /* is_offline_preview */,
+TEST_F(PreviewsUKMObserverTest, DeferAllScriptSeen) {
+  RunTest(content::DEFER_ALL_SCRIPT_ON /* committed_state */,
+          content::PREVIEWS_UNSPECIFIED /* allowed_state */,
+          false /* origin_opt_out */, false /* save_data_enabled */,
           CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
 
   NavigateToUntrackedUrl();
 
-  ValidateUKM(false /* lite_page_expected */,
-              false /* lite_page_redirect_expected */,
-              false /* noscript_expected */,
-              false /* resource_loading_hints_expected */,
-              0 /* opt_out_value */, true /* origin_opt_out_expected */,
+  ValidateUKM(content::DEFER_ALL_SCRIPT_ON, 0 /* opt_out_value */,
+              false /* origin_opt_out_expected */,
               false /* save_data_enabled_expected */,
-              false /* offline_preview_expected */, false /* previews_likely */,
-              CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
+              true /* previews_likely */, CoinFlipHoldbackResult::kNotSet,
+              {} /* eligibility_reasons */,
+              base::nullopt /* navigation_restart_penalty */,
+              base::nullopt /* hint_generation_timestamp */,
+              base::nullopt /* hint_source */);
+}
+
+TEST_F(PreviewsUKMObserverTest, DeferAllScriptOptOutChip) {
+  RunTest(content::DEFER_ALL_SCRIPT_ON /* committed_state */,
+          content::PREVIEWS_UNSPECIFIED /* allowed_state */,
+          false /* origin_opt_out */, false /* save_data_enabled */,
+          CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
+          base::nullopt /* navigation_restart_penalty */,
+          base::nullopt /* hint_version_string */);
+
+  observer()->BroadcastEventToObservers(PreviewsUITabHelper::OptOutEventKey());
+  NavigateToUntrackedUrl();
+
+  ValidateUKM(content::DEFER_ALL_SCRIPT_ON, 2 /* opt_out_value */,
+              false /* origin_opt_out_expected */,
+              false /* save_data_enabled_expected */,
+              true /* previews_likely */, CoinFlipHoldbackResult::kNotSet,
+              {} /* eligibility_reasons */,
+              base::nullopt /* navigation_restart_penalty */,
+              base::nullopt /* hint_generation_timestamp */,
+              base::nullopt /* hint_source */);
+}
+
+TEST_F(PreviewsUKMObserverTest, OriginOptOut) {
+  RunTest(content::PREVIEWS_OFF /* committed_state */,
+          content::PREVIEWS_UNSPECIFIED /* allowed_state */,
+          true /* origin_opt_out */, false /* save_data_enabled */,
+          CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
+          base::nullopt /* navigation_restart_penalty */,
+          base::nullopt /* hint_version_string */);
+
+  NavigateToUntrackedUrl();
+
+  ValidateUKM(content::PREVIEWS_UNSPECIFIED, 0 /* opt_out_value */,
+              true /* origin_opt_out_expected */,
+              false /* save_data_enabled_expected */,
+              false /* previews_likely */, CoinFlipHoldbackResult::kNotSet,
+              {} /* eligibility_reasons */,
               base::nullopt /* navigation_restart_penalty */,
               base::nullopt /* hint_generation_timestamp */,
               base::nullopt /* hint_source */);
 }
 
 TEST_F(PreviewsUKMObserverTest, DataSaverEnabled) {
-  RunTest(content::PREVIEWS_UNSPECIFIED /* allowed_state */, PreviewsType::NONE,
-          false /* lite_page_received */,
-          false /* lite_page_redirect_received */, false /* noscript_on */,
-          false /* resource_loading_hints_on */, false /* origin_opt_out */,
-          true /* save_data_enabled */, false /* is_offline_preview */,
+  RunTest(content::PREVIEWS_OFF /* committed_state */,
+          content::PREVIEWS_UNSPECIFIED /* allowed_state */,
+          false /* origin_opt_out */, true /* save_data_enabled */,
           CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
 
   NavigateToUntrackedUrl();
 
-  ValidateUKM(false /* lite_page_expected */,
-              false /* lite_page_redirect_expected */,
-              false /* noscript_expected */,
-              false /* resource_loading_hints_expected */,
-              0 /* opt_out_value */, false /* origin_opt_out_expected */,
+  ValidateUKM(content::PREVIEWS_UNSPECIFIED, 0 /* opt_out_value */,
+              false /* origin_opt_out_expected */,
               true /* save_data_enabled_expected */,
-              false /* offline_preview_expected */, false /* previews_likely */,
-              CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
+              false /* previews_likely */, CoinFlipHoldbackResult::kNotSet,
+              {} /* eligibility_reasons */,
               base::nullopt /* navigation_restart_penalty */,
               base::nullopt /* hint_generation_timestamp */,
               base::nullopt /* hint_source */);
@@ -770,25 +678,19 @@
 // committed so we do not consider the opt out tests here.
 TEST_F(PreviewsUKMObserverTest, NavigationRestartPenaltySeen) {
   RunTest(
-      content::PREVIEWS_UNSPECIFIED /* allowed_state */, PreviewsType::NONE,
-      false /* lite_page_received */, false /* lite_page_redirect_received */,
-      false /* noscript_on */, false /* resource_loading_hints_on */,
+      content::PREVIEWS_OFF /* committed_state */,
+      content::PREVIEWS_UNSPECIFIED /* allowed_state */,
       false /* origin_opt_out */, false /* save_data_enabled */,
-      false /* is_offline_preview */, CoinFlipHoldbackResult::kNotSet,
-      {} /* eligibility_reasons */,
+      CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
       base::TimeDelta::FromMilliseconds(1337) /* navigation_restart_penalty */,
       base::nullopt /* hint_version_string */);
 
   NavigateToUntrackedUrl();
 
   ValidateUKM(
-
-      false /* lite_page_expected */, false /* lite_page_redirect_expected */,
-      false /* noscript_expected */,
-      false /* resource_loading_hints_expected */, 0 /* opt_out_value */,
+      content::PREVIEWS_UNSPECIFIED, 0 /* opt_out_value */,
       false /* origin_opt_out_expected */,
-      false /* save_data_enabled_expected */,
-      false /* offline_preview_expected */, false /* previews_likely */,
+      false /* save_data_enabled_expected */, false /* previews_likely */,
       CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
       base::TimeDelta::FromMilliseconds(1337) /* navigation_restart_penalty */,
       base::nullopt /* hint_generation_timestamp */,
@@ -796,24 +698,18 @@
 }
 
 TEST_F(PreviewsUKMObserverTest, PreviewsLikelySet_PreCommitDecision) {
-  RunTest(content::OFFLINE_PAGE_ON | content::NOSCRIPT_ON /* allowed_state */,
-          PreviewsType::NONE, false /* lite_page_received */,
-          false /* lite_page_redirect_received */, false /* noscript_on */,
-          false /* resource_loading_hints_on */, false /* origin_opt_out */,
-          true /* save_data_enabled */, true /* is_offline_preview */,
+  RunTest(content::OFFLINE_PAGE_ON /* committed_state */,
+          content::OFFLINE_PAGE_ON | content::NOSCRIPT_ON /* allowed_state */,
+          false /* origin_opt_out */, true /* save_data_enabled */,
           CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
 
   NavigateToUntrackedUrl();
 
-  ValidateUKM(false /* lite_page_expected */,
-              false /* lite_page_redirect_expected */,
-              false /* noscript_expected */,
-              false /* resource_loading_hints_expected */,
-              0 /* opt_out_value */, false /* origin_opt_out_expected */,
-              true /* save_data_enabled_expected */,
-              true /* offline_preview_expected */, true /* previews_likely */,
+  ValidateUKM(content::OFFLINE_PAGE_ON, 0 /* opt_out_value */,
+              false /* origin_opt_out_expected */,
+              true /* save_data_enabled_expected */, true /* previews_likely */,
               CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
               base::nullopt /* navigation_restart_penalty */,
               base::nullopt /* hint_generation_timestamp */,
@@ -821,74 +717,58 @@
 }
 
 TEST_F(PreviewsUKMObserverTest, PreviewsLikelyNotSet_PostCommitDecision) {
-  RunTest(content::NOSCRIPT_ON /* allowed_state */, PreviewsType::NONE,
-          false /* lite_page_received */,
-          false /* lite_page_redirect_received */, false /* noscript_on */,
-          false /* resource_loading_hints_on */, false /* origin_opt_out */,
-          true /* save_data_enabled */, false /* is_offline_preview */,
-          CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
+  RunTest(content::PREVIEWS_OFF /* committed_state */,
+          content::NOSCRIPT_ON /* allowed_state */, false /* origin_opt_out */,
+          true /* save_data_enabled */, CoinFlipHoldbackResult::kNotSet,
+          {} /* eligibility_reasons */,
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
 
   NavigateToUntrackedUrl();
 
-  ValidateUKM(false /* lite_page_expected */,
-              false /* lite_page_redirect_expected */,
-              false /* noscript_expected */,
-              false /* resource_loading_hints_expected */,
-              0 /* opt_out_value */, false /* origin_opt_out_expected */,
+  ValidateUKM(content::PREVIEWS_UNSPECIFIED, 0 /* opt_out_value */,
+              false /* origin_opt_out_expected */,
               true /* save_data_enabled_expected */,
-              false /* offline_preview_expected */, false /* previews_likely */,
-              CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
+              false /* previews_likely */, CoinFlipHoldbackResult::kNotSet,
+              {} /* eligibility_reasons */,
               base::nullopt /* navigation_restart_penalty */,
               base::nullopt /* hint_generation_timestamp */,
               base::nullopt /* hint_source */);
 }
 
 TEST_F(PreviewsUKMObserverTest, PreviewsLikelyNotSet_PreviewsOff) {
-  RunTest(content::PREVIEWS_OFF /* allowed_state */, PreviewsType::NONE,
-          false /* lite_page_received */,
-          false /* lite_page_redirect_received */, false /* noscript_on */,
-          false /* resource_loading_hints_on */, false /* origin_opt_out */,
-          true /* save_data_enabled */, false /* is_offline_preview */,
-          CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
+  RunTest(content::PREVIEWS_OFF /* committed_state */,
+          content::PREVIEWS_OFF /* allowed_state */, false /* origin_opt_out */,
+          true /* save_data_enabled */, CoinFlipHoldbackResult::kNotSet,
+          {} /* eligibility_reasons */,
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
 
   NavigateToUntrackedUrl();
 
-  ValidateUKM(false /* lite_page_expected */,
-              false /* lite_page_redirect_expected */,
-              false /* noscript_expected */,
-              false /* resource_loading_hints_expected */,
-              0 /* opt_out_value */, false /* origin_opt_out_expected */,
+  ValidateUKM(content::PREVIEWS_UNSPECIFIED, 0 /* opt_out_value */,
+              false /* origin_opt_out_expected */,
               true /* save_data_enabled_expected */,
-              false /* offline_preview_expected */, false /* previews_likely */,
-              CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
+              false /* previews_likely */, CoinFlipHoldbackResult::kNotSet,
+              {} /* eligibility_reasons */,
               base::nullopt /* navigation_restart_penalty */,
               base::nullopt /* hint_generation_timestamp */,
               base::nullopt /* hint_source */);
 }
 
 TEST_F(PreviewsUKMObserverTest, CoinFlipResult_Holdback) {
-  RunTest(content::OFFLINE_PAGE_ON /* allowed_state */, PreviewsType::NONE,
-          false /* lite_page_received */,
-          false /* lite_page_redirect_received */, false /* noscript_on */,
-          false /* resource_loading_hints_on */, false /* origin_opt_out */,
-          true /* save_data_enabled */, true /* is_offline_preview */,
+  RunTest(content::OFFLINE_PAGE_ON /* committed_state */,
+          content::OFFLINE_PAGE_ON /* allowed_state */,
+          false /* origin_opt_out */, true /* save_data_enabled */,
           CoinFlipHoldbackResult::kHoldback, {} /* eligibility_reasons */,
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
 
   NavigateToUntrackedUrl();
 
-  ValidateUKM(false /* lite_page_expected */,
-              false /* lite_page_redirect_expected */,
-              false /* noscript_expected */,
-              false /* resource_loading_hints_expected */,
-              0 /* opt_out_value */, false /* origin_opt_out_expected */,
-              true /* save_data_enabled_expected */,
-              true /* offline_preview_expected */, true /* previews_likely */,
+  ValidateUKM(content::OFFLINE_PAGE_ON, 0 /* opt_out_value */,
+              false /* origin_opt_out_expected */,
+              true /* save_data_enabled_expected */, true /* previews_likely */,
               CoinFlipHoldbackResult::kHoldback, {} /* eligibility_reasons */,
               base::nullopt /* navigation_restart_penalty */,
               base::nullopt /* hint_generation_timestamp */,
@@ -896,24 +776,18 @@
 }
 
 TEST_F(PreviewsUKMObserverTest, CoinFlipResult_Allowed) {
-  RunTest(content::OFFLINE_PAGE_ON /* allowed_state */, PreviewsType::NONE,
-          false /* lite_page_received */,
-          false /* lite_page_redirect_received */, false /* noscript_on */,
-          false /* resource_loading_hints_on */, false /* origin_opt_out */,
-          true /* save_data_enabled */, true /* is_offline_preview */,
+  RunTest(content::OFFLINE_PAGE_ON /* committed_state */,
+          content::OFFLINE_PAGE_ON /* allowed_state */,
+          false /* origin_opt_out */, true /* save_data_enabled */,
           CoinFlipHoldbackResult::kAllowed, {} /* eligibility_reasons */,
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
 
   NavigateToUntrackedUrl();
 
-  ValidateUKM(false /* lite_page_expected */,
-              false /* lite_page_redirect_expected */,
-              false /* noscript_expected */,
-              false /* resource_loading_hints_expected */,
-              0 /* opt_out_value */, false /* origin_opt_out_expected */,
-              true /* save_data_enabled_expected */,
-              true /* offline_preview_expected */, true /* previews_likely */,
+  ValidateUKM(content::OFFLINE_PAGE_ON, 0 /* opt_out_value */,
+              false /* origin_opt_out_expected */,
+              true /* save_data_enabled_expected */, true /* previews_likely */,
               CoinFlipHoldbackResult::kAllowed, {} /* eligibility_reasons */,
               base::nullopt /* navigation_restart_penalty */,
               base::nullopt /* hint_generation_timestamp */,
@@ -921,11 +795,9 @@
 }
 
 TEST_F(PreviewsUKMObserverTest, LogPreviewsEligibilityReason_WithAllowed) {
-  RunTest(content::PREVIEWS_UNSPECIFIED /* allowed_state */, PreviewsType::NONE,
-          false /* lite_page_received */,
-          false /* lite_page_redirect_received */, false /* noscript_on */,
-          false /* resource_loading_hints_on */, false /* origin_opt_out */,
-          true /* save_data_enabled */, false /* is_offline_preview */,
+  RunTest(content::PREVIEWS_OFF /* committed_state */,
+          content::PREVIEWS_UNSPECIFIED /* allowed_state */,
+          false /* origin_opt_out */, true /* save_data_enabled */,
           CoinFlipHoldbackResult::kNotSet,
           {{PreviewsType::OFFLINE,
             PreviewsEligibilityReason::BLACKLIST_UNAVAILABLE},
@@ -941,14 +813,10 @@
 
   NavigateToUntrackedUrl();
 
-  ValidateUKM(false /* lite_page_expected */,
-              false /* lite_page_redirect_expected */,
-              false /* noscript_expected */,
-              false /* resource_loading_hints_expected */,
-              0 /* opt_out_value */, false /* origin_opt_out_expected */,
+  ValidateUKM(content::PREVIEWS_UNSPECIFIED, 0 /* opt_out_value */,
+              false /* origin_opt_out_expected */,
               true /* save_data_enabled_expected */,
-              false /* offline_preview_expected */, false /* previews_likely */,
-              CoinFlipHoldbackResult::kNotSet,
+              false /* previews_likely */, CoinFlipHoldbackResult::kNotSet,
               {{PreviewsType::OFFLINE,
                 PreviewsEligibilityReason::BLACKLIST_UNAVAILABLE},
                {PreviewsType::LITE_PAGE,
@@ -962,11 +830,9 @@
 }
 
 TEST_F(PreviewsUKMObserverTest, LogPreviewsEligibilityReason_NoneAllowed) {
-  RunTest(content::PREVIEWS_UNSPECIFIED /* allowed_state */, PreviewsType::NONE,
-          false /* lite_page_received */,
-          false /* lite_page_redirect_received */, false /* noscript_on */,
-          false /* resource_loading_hints_on */, false /* origin_opt_out */,
-          true /* save_data_enabled */, false /* is_offline_preview */,
+  RunTest(content::PREVIEWS_OFF /* committed_state */,
+          content::PREVIEWS_UNSPECIFIED /* allowed_state */,
+          false /* origin_opt_out */, true /* save_data_enabled */,
           CoinFlipHoldbackResult::kNotSet,
           {{PreviewsType::OFFLINE,
             PreviewsEligibilityReason::BLACKLIST_UNAVAILABLE},
@@ -982,14 +848,10 @@
 
   NavigateToUntrackedUrl();
 
-  ValidateUKM(false /* lite_page_expected */,
-              false /* lite_page_redirect_expected */,
-              false /* noscript_expected */,
-              false /* resource_loading_hints_expected */,
-              0 /* opt_out_value */, false /* origin_opt_out_expected */,
+  ValidateUKM(content::PREVIEWS_UNSPECIFIED, 0 /* opt_out_value */,
+              false /* origin_opt_out_expected */,
               true /* save_data_enabled_expected */,
-              false /* offline_preview_expected */, false /* previews_likely */,
-              CoinFlipHoldbackResult::kNotSet,
+              false /* previews_likely */, CoinFlipHoldbackResult::kNotSet,
               {{PreviewsType::OFFLINE,
                 PreviewsEligibilityReason::BLACKLIST_UNAVAILABLE},
                {PreviewsType::LITE_PAGE,
@@ -1010,24 +872,18 @@
   std::string hint_version_string;
   hint_version.SerializeToString(&hint_version_string);
   base::Base64Encode(hint_version_string, &hint_version_string);
-  RunTest(content::PREVIEWS_UNSPECIFIED /* allowed_state */, PreviewsType::NONE,
-          false /* lite_page_received */,
-          false /* lite_page_redirect_received */, false /* noscript_on */,
-          false /* resource_loading_hints_on */, false /* origin_opt_out */,
-          true /* save_data_enabled */, false /* is_offline_preview */,
+  RunTest(content::PREVIEWS_OFF /* committed_state */,
+          content::PREVIEWS_UNSPECIFIED /* allowed_state */,
+          false /* origin_opt_out */, true /* save_data_enabled */,
           CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
           base::nullopt /* navigation_restart_penalty */, hint_version_string);
 
   NavigateToUntrackedUrl();
 
   ValidateUKM(
-
-      false /* lite_page_expected */, false /* lite_page_redirect_expected */,
-      false /* noscript_expected */,
-      false /* resource_loading_hints_expected */, 0 /* opt_out_value */,
+      content::PREVIEWS_UNSPECIFIED, 0 /* opt_out_value */,
       false /* origin_opt_out_expected */,
-      true /* save_data_enabled_expected */,
-      false /* offline_preview_expected */, false /* previews_likely */,
+      true /* save_data_enabled_expected */, false /* previews_likely */,
       CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
       base::nullopt /* navigation_restart_penalty */,
       123 /* hint_generation_timestamp */, base::nullopt /* hint_source */);
@@ -1041,24 +897,18 @@
   std::string hint_version_string;
   hint_version.SerializeToString(&hint_version_string);
   base::Base64Encode(hint_version_string, &hint_version_string);
-  RunTest(content::PREVIEWS_UNSPECIFIED /* allowed_state */, PreviewsType::NONE,
-          false /* lite_page_received */,
-          false /* lite_page_redirect_received */, false /* noscript_on */,
-          false /* resource_loading_hints_on */, false /* origin_opt_out */,
-          true /* save_data_enabled */, false /* is_offline_preview */,
+  RunTest(content::PREVIEWS_OFF /* committed_state */,
+          content::PREVIEWS_UNSPECIFIED /* allowed_state */,
+          false /* origin_opt_out */, true /* save_data_enabled */,
           CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
           base::nullopt /* navigation_restart_penalty */, hint_version_string);
 
   NavigateToUntrackedUrl();
 
   ValidateUKM(
-
-      false /* lite_page_expected */, false /* lite_page_redirect_expected */,
-      false /* noscript_expected */,
-      false /* resource_loading_hints_expected */, 0 /* opt_out_value */,
+      content::PREVIEWS_UNSPECIFIED, 0 /* opt_out_value */,
       false /* origin_opt_out_expected */,
-      true /* save_data_enabled_expected */,
-      false /* offline_preview_expected */, false /* previews_likely */,
+      true /* save_data_enabled_expected */, false /* previews_likely */,
       CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
       base::nullopt /* navigation_restart_penalty */,
       base::nullopt /* hint_generation_timestamp */, 1 /* hint_source */);
@@ -1066,147 +916,162 @@
 
 TEST_F(PreviewsUKMObserverTest,
        LogOptimizationGuideHintVersion_NotActuallyAVersionProto) {
-  RunTest(content::PREVIEWS_UNSPECIFIED /* allowed_state */, PreviewsType::NONE,
-          false /* lite_page_received */,
-          false /* lite_page_redirect_received */, false /* noscript_on */,
-          false /* resource_loading_hints_on */, false /* origin_opt_out */,
-          true /* save_data_enabled */, false /* is_offline_preview */,
+  RunTest(content::PREVIEWS_OFF /* committed_state */,
+          content::PREVIEWS_UNSPECIFIED /* allowed_state */,
+          false /* origin_opt_out */, true /* save_data_enabled */,
           CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
           base::nullopt /* navigation_restart_penalty */, "notahintversion");
 
   NavigateToUntrackedUrl();
 
-  ValidateUKM(false /* lite_page_expected */,
-              false /* lite_page_redirect_expected */,
-              false /* noscript_expected */,
-              false /* resource_loading_hints_expected */,
-              0 /* opt_out_value */, false /* origin_opt_out_expected */,
+  ValidateUKM(content::PREVIEWS_UNSPECIFIED, 0 /* opt_out_value */,
+              false /* origin_opt_out_expected */,
               true /* save_data_enabled_expected */,
-              false /* offline_preview_expected */, false /* previews_likely */,
-              CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
+              false /* previews_likely */, CoinFlipHoldbackResult::kNotSet,
+              {} /* eligibility_reasons */,
               base::nullopt /* navigation_restart_penalty */,
               base::nullopt /* hint_generation_timestamp */,
               base::nullopt /* hint_source */);
 }
 
 TEST_F(PreviewsUKMObserverTest, CheckReportingForHidden) {
-  RunTest(content::PREVIEWS_UNSPECIFIED /* allowed_state */, PreviewsType::NONE,
-          false /* lite_page_received */,
-          false /* lite_page_redirect_received */, false /* noscript_on */,
-          false /* resource_loading_hints_on */, false /* origin_opt_out */,
-          true /* save_data_enabled */, false /* is_offline_preview */,
+  RunTest(content::PREVIEWS_OFF /* committed_state */,
+          content::PREVIEWS_UNSPECIFIED /* allowed_state */,
+          false /* origin_opt_out */, true /* save_data_enabled */,
           CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
 
   web_contents()->WasHidden();
 
-  ValidateUKM(false /* lite_page_expected */,
-              false /* lite_page_redirect_expected */,
-              false /* noscript_expected */,
-              false /* resource_loading_hints_expected */,
-              0 /* opt_out_value */, false /* origin_opt_out_expected */,
+  ValidateUKM(content::PREVIEWS_UNSPECIFIED, 0 /* opt_out_value */,
+              false /* origin_opt_out_expected */,
               true /* save_data_enabled_expected */,
-              false /* offline_preview_expected */, false /* previews_likely */,
-              CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
+              false /* previews_likely */, CoinFlipHoldbackResult::kNotSet,
+              {} /* eligibility_reasons */,
               base::nullopt /* navigation_restart_penalty */,
               base::nullopt /* hint_generation_timestamp */,
               base::nullopt /* hint_source */);
 }
 
 TEST_F(PreviewsUKMObserverTest, CheckReportingForFlushMetrics) {
-  RunTest(content::PREVIEWS_UNSPECIFIED /* allowed_state */, PreviewsType::NONE,
-          false /* lite_page_received */,
-          false /* lite_page_redirect_received */, false /* noscript_on */,
-          false /* resource_loading_hints_on */, false /* origin_opt_out */,
-          true /* save_data_enabled */, false /* is_offline_preview */,
+  RunTest(content::PREVIEWS_OFF /* committed_state */,
+          content::PREVIEWS_UNSPECIFIED /* allowed_state */,
+          false /* origin_opt_out */, true /* save_data_enabled */,
           CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
 
   SimulateAppEnterBackground();
 
-  ValidateUKM(false /* lite_page_expected */,
-              false /* lite_page_redirect_expected */,
-              false /* noscript_expected */,
-              false /* resource_loading_hints_expected */,
-              0 /* opt_out_value */, false /* origin_opt_out_expected */,
+  ValidateUKM(content::PREVIEWS_UNSPECIFIED, 0 /* opt_out_value */,
+              false /* origin_opt_out_expected */,
               true /* save_data_enabled_expected */,
-              false /* offline_preview_expected */, false /* previews_likely */,
-              CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
+              false /* previews_likely */, CoinFlipHoldbackResult::kNotSet,
+              {} /* eligibility_reasons */,
               base::nullopt /* navigation_restart_penalty */,
               base::nullopt /* hint_generation_timestamp */,
               base::nullopt /* hint_source */);
 }
 
 TEST_F(PreviewsUKMObserverTest, TestPageEndReasonUMA) {
-  for (int i = static_cast<int>(PreviewsType::NONE);
-       i < static_cast<int>(PreviewsType::LAST); i++) {
-    PreviewsType type = static_cast<PreviewsType>(i);
-    if (type == PreviewsType::DEPRECATED_AMP_REDIRECTION)
-      continue;
-    if (type == PreviewsType::DEPRECATED_LOFI)
-      continue;
-
     base::HistogramTester tester;
-    RunTest(content::PREVIEWS_UNSPECIFIED /* allowed_state */, type,
-            false /* lite_page_received */,
-            false /* lite_page_redirect_received */, false /* noscript_on */,
-            false /* resource_loading_hints_on */, false /* origin_opt_out */,
-            false /* save_data_enabled */, false /* is_offline_preview */,
+
+    // No preview:
+    RunTest(content::PREVIEWS_OFF /* committed_state */,
+            content::PREVIEWS_UNSPECIFIED /* allowed_state */,
+            false /* origin_opt_out */, false /* save_data_enabled */,
             CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
             base::nullopt /* navigation_restart_penalty */,
             base::nullopt /* hint_version_string */);
-
     NavigateToUntrackedUrl();
-
-    // The top level metric is not recorded on a non-preview.
-    if (type != PreviewsType::NONE) {
-      tester.ExpectUniqueSample(
-          "Previews.PageEndReason",
-          page_load_metrics::PageEndReason::END_NEW_NAVIGATION, 1);
-    }
-    tester.ExpectUniqueSample(
-        "Previews.PageEndReason." + GetStringNameForType(type),
-        page_load_metrics::PageEndReason::END_NEW_NAVIGATION, 1);
-  }
-}
-
-TEST_F(PreviewsUKMObserverTest, TestPageEndReasonUMACoinFlipHoldback) {
-  for (int i = static_cast<int>(PreviewsType::NONE);
-       i < static_cast<int>(PreviewsType::LAST); i++) {
-    PreviewsType type = static_cast<PreviewsType>(i);
-    if (type == PreviewsType::DEPRECATED_AMP_REDIRECTION)
-      continue;
-    if (type == PreviewsType::DEPRECATED_LOFI)
-      continue;
-
-    base::HistogramTester tester;
-    RunTest(content::OFFLINE_PAGE_ON /* allowed_state */, type,
-            false /* lite_page_received */,
-            false /* lite_page_redirect_received */, false /* noscript_on */,
-            false /* resource_loading_hints_on */, false /* origin_opt_out */,
-            false /* save_data_enabled */, false /* is_offline_preview */,
-            CoinFlipHoldbackResult::kHoldback, {} /* eligibility_reasons */,
-            base::nullopt /* navigation_restart_penalty */,
-            base::nullopt /* hint_version_string */);
-
-    NavigateToUntrackedUrl();
-
-    // The top level metric is not recorded on a non-preview.
-    tester.ExpectTotalCount("Previews.PageEndReason", 0);
-    // We do not expect the individual preview PageEndReason UMAs to get
-    // recorded when the preview was not actually shown.
-    if (type != PreviewsType::NONE) {
-      tester.ExpectTotalCount(
-          "Previews.PageEndReason." + GetStringNameForType(type), 0);
-    }
-    // Since the preview is not actually shown on the holdback, we expect the
-    // NONE-variant to be recorded.
     tester.ExpectUniqueSample(
         "Previews.PageEndReason.None",
         page_load_metrics::PageEndReason::END_NEW_NAVIGATION, 1);
-  }
+    // The top level metric is not recorded on a non-preview.
+    tester.ExpectTotalCount("Previews.PageEndReason", 0);
+
+    // Lite Page Redirect:
+    RunTest(content::LITE_PAGE_REDIRECT_ON /* committed_state */,
+            content::PREVIEWS_UNSPECIFIED /* allowed_state */,
+            false /* origin_opt_out */, false /* save_data_enabled */,
+            CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
+            base::nullopt /* navigation_restart_penalty */,
+            base::nullopt /* hint_version_string */);
+    NavigateToUntrackedUrl();
+    tester.ExpectUniqueSample(
+        "Previews.PageEndReason.LitePageRedirect",
+        page_load_metrics::PageEndReason::END_NEW_NAVIGATION, 1);
+    tester.ExpectBucketCount(
+        "Previews.PageEndReason",
+        page_load_metrics::PageEndReason::END_NEW_NAVIGATION, 1);
+
+    // Defer All Script:
+    RunTest(content::DEFER_ALL_SCRIPT_ON /* committed_state */,
+            content::PREVIEWS_UNSPECIFIED /* allowed_state */,
+            false /* origin_opt_out */, false /* save_data_enabled */,
+            CoinFlipHoldbackResult::kNotSet, {} /* eligibility_reasons */,
+            base::nullopt /* navigation_restart_penalty */,
+            base::nullopt /* hint_version_string */);
+    NavigateToUntrackedUrl();
+    tester.ExpectUniqueSample(
+        "Previews.PageEndReason.DeferAllScript",
+        page_load_metrics::PageEndReason::END_NEW_NAVIGATION, 1);
+    tester.ExpectBucketCount(
+        "Previews.PageEndReason",
+        page_load_metrics::PageEndReason::END_NEW_NAVIGATION, 2);
+}
+
+TEST_F(PreviewsUKMObserverTest, TestPageEndReasonUMACoinFlipHoldback) {
+  base::HistogramTester tester;
+
+  // No preview:
+  RunTest(content::PREVIEWS_OFF /* committed_state */,
+          content::OFFLINE_PAGE_ON /* allowed_state */,
+          false /* origin_opt_out */, false /* save_data_enabled */,
+          CoinFlipHoldbackResult::kHoldback, {} /* eligibility_reasons */,
+          base::nullopt /* navigation_restart_penalty */,
+          base::nullopt /* hint_version_string */);
+  NavigateToUntrackedUrl();
+  tester.ExpectUniqueSample(
+      "Previews.PageEndReason.None",
+      page_load_metrics::PageEndReason::END_NEW_NAVIGATION, 1);
+  // The top level metric is not recorded on a non-preview.
+  tester.ExpectTotalCount("Previews.PageEndReason", 0);
+
+  // Lite Page Redirect:
+  RunTest(content::LITE_PAGE_REDIRECT_ON /* committed_state */,
+          content::OFFLINE_PAGE_ON /* allowed_state */,
+          false /* origin_opt_out */, false /* save_data_enabled */,
+          CoinFlipHoldbackResult::kHoldback, {} /* eligibility_reasons */,
+          base::nullopt /* navigation_restart_penalty */,
+          base::nullopt /* hint_version_string */);
+  NavigateToUntrackedUrl();
+  // Preview was not actually shown, so expect no PageEndReason for it.
+  tester.ExpectTotalCount("Previews.PageEndReason.LitePageRedirect", 0);
+  tester.ExpectBucketCount("Previews.PageEndReason.None",
+                           page_load_metrics::PageEndReason::END_NEW_NAVIGATION,
+                           2);
+  tester.ExpectBucketCount("Previews.PageEndReason",
+                           page_load_metrics::PageEndReason::END_NEW_NAVIGATION,
+                           0);
+
+  // Defer All Script:
+  RunTest(content::DEFER_ALL_SCRIPT_ON /* committed_state */,
+          content::OFFLINE_PAGE_ON /* allowed_state */,
+          false /* origin_opt_out */, false /* save_data_enabled */,
+          CoinFlipHoldbackResult::kHoldback, {} /* eligibility_reasons */,
+          base::nullopt /* navigation_restart_penalty */,
+          base::nullopt /* hint_version_string */);
+  NavigateToUntrackedUrl();
+  // Preview was not actually shown, so expect no PageEndReason for it.
+  tester.ExpectTotalCount("Previews.PageEndReason.DeferAllScript", 0);
+  tester.ExpectBucketCount("Previews.PageEndReason.None",
+                           page_load_metrics::PageEndReason::END_NEW_NAVIGATION,
+                           3);
+  tester.ExpectBucketCount("Previews.PageEndReason",
+                           page_load_metrics::PageEndReason::END_NEW_NAVIGATION,
+                           0);
 }
 
 }  // namespace
diff --git a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc
index a7dc8f6..43ceab7 100644
--- a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc
@@ -139,6 +139,10 @@
 UkmPageLoadMetricsObserver::ObservePolicy UkmPageLoadMetricsObserver::OnCommit(
     content::NavigationHandle* navigation_handle,
     ukm::SourceId source_id) {
+  if (navigation_handle->IsInMainFrame()) {
+    largest_contentful_paint_handler_.RecordMainFrameTreeNodeId(
+        navigation_handle->GetFrameTreeNodeId());
+  }
   if (navigation_handle->GetWebContents()->GetContentsMimeType() ==
       kOfflinePreviewsMimeType) {
     if (!IsOfflinePreview(navigation_handle->GetWebContents()))
@@ -627,16 +631,21 @@
     return;
   const page_load_metrics::ContentfulPaintTimingInfo& paint =
       largest_contentful_paint_handler_.MergeMainFrameAndSubframes();
+
   if (!paint.IsEmpty()) {
-    TRACE_EVENT_INSTANT1(
+    TRACE_EVENT_INSTANT2(
         "loading",
         "NavStartToLargestContentfulPaint::Candidate::AllFrames::UKM",
-        TRACE_EVENT_SCOPE_THREAD, "data", paint.DataAsTraceValue());
+        TRACE_EVENT_SCOPE_THREAD, "data", paint.DataAsTraceValue(),
+        "main_frame_tree_node_id",
+        largest_contentful_paint_handler_.MainFrameTreeNodeId());
   } else {
-    TRACE_EVENT_INSTANT0("loading",
-                         "NavStartToLargestContentfulPaint::"
-                         "Invalidate::AllFrames::UKM",
-                         TRACE_EVENT_SCOPE_THREAD);
+    TRACE_EVENT_INSTANT1(
+        "loading",
+        "NavStartToLargestContentfulPaint::"
+        "Invalidate::AllFrames::UKM",
+        TRACE_EVENT_SCOPE_THREAD, "main_frame_tree_node_id",
+        largest_contentful_paint_handler_.MainFrameTreeNodeId());
   }
 }
 
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc b/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc
index 46534fd..78fd90ba 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc
+++ b/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc
@@ -2148,9 +2148,8 @@
   auto waiter = CreatePageLoadMetricsTestWaiter();
   waiter->AddPageExpectation(TimingField::kLoadEvent);
   waiter->AddPageExpectation(TimingField::kFirstContentfulPaint);
-  LocationBar* location_bar = browser()->window()->GetLocationBar();
   ui_test_utils::SendToOmniboxAndSubmit(
-      location_bar, embedded_test_server()->GetURL("/title1.html").spec(),
+      browser(), embedded_test_server()->GetURL("/title1.html").spec(),
       base::TimeTicks::Now());
   waiter->Wait();
 
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index cf760950e..47251c8f 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -841,9 +841,6 @@
 #endif  // !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
 
 #if defined(OS_WIN)
-  { key::kWelcomePageOnOSUpgradeEnabled,
-    prefs::kWelcomePageOnOSUpgradeEnabled,
-    base::Value::Type::BOOLEAN },
   { key::kChromeCleanupEnabled,
     prefs::kSwReporterEnabled,
     base::Value::Type::BOOLEAN },
diff --git a/chrome/browser/policy/policy_browsertest.cc b/chrome/browser/policy/policy_browsertest.cc
index e1ae927..2e77e2d 100644
--- a/chrome/browser/policy/policy_browsertest.cc
+++ b/chrome/browser/policy/policy_browsertest.cc
@@ -1034,10 +1034,10 @@
     content::WebContents* web_contents =
         browser->tab_strip_model()->GetActiveWebContents();
     content::TestNavigationObserver observer(web_contents);
-    LocationBar* location_bar = browser->window()->GetLocationBar();
-    ui_test_utils::SendToOmniboxAndSubmit(location_bar, url);
-    OmniboxEditModel* model = location_bar->GetOmniboxView()->model();
+    ui_test_utils::SendToOmniboxAndSubmit(browser, url);
     observer.Wait();
+    OmniboxEditModel* model =
+        browser->window()->GetLocationBar()->GetOmniboxView()->model();
     EXPECT_TRUE(model->CurrentMatch(NULL).destination_url.is_valid());
     EXPECT_EQ(GetExpectedSearchURL(expect_safe_search), web_contents->GetURL());
   }
@@ -1522,9 +1522,9 @@
 
   // Verify that searching from the omnibox uses kSearchURL.
   chrome::FocusLocationBar(browser());
-  LocationBar* location_bar = browser()->window()->GetLocationBar();
-  ui_test_utils::SendToOmniboxAndSubmit(location_bar, "stuff to search for");
-  OmniboxEditModel* model = location_bar->GetOmniboxView()->model();
+  ui_test_utils::SendToOmniboxAndSubmit(browser(), "stuff to search for");
+  OmniboxEditModel* model =
+      browser()->window()->GetLocationBar()->GetOmniboxView()->model();
   EXPECT_TRUE(model->CurrentMatch(NULL).destination_url.is_valid());
   content::WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
@@ -1539,7 +1539,7 @@
   EXPECT_TRUE(service->GetDefaultSearchProvider());
   UpdateProviderPolicy(policies);
   EXPECT_FALSE(service->GetDefaultSearchProvider());
-  ui_test_utils::SendToOmniboxAndSubmit(location_bar, "should not work");
+  ui_test_utils::SendToOmniboxAndSubmit(browser(), "should not work");
   // This means that submitting won't trigger any action.
   EXPECT_FALSE(model->CurrentMatch(NULL).destination_url.is_valid());
   EXPECT_EQ(GURL(url::kAboutBlankURL), web_contents->GetURL());
diff --git a/chrome/browser/prerender/prerender_browsertest.cc b/chrome/browser/prerender/prerender_browsertest.cc
index 64ea7236..f2ccba3b 100644
--- a/chrome/browser/prerender/prerender_browsertest.cc
+++ b/chrome/browser/prerender/prerender_browsertest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include <stddef.h>
+
 #include <set>
 #include <string>
 #include <unordered_map>
@@ -2332,17 +2333,6 @@
     return GetLocationBar()->GetOmniboxView();
   }
 
-  void WaitForAutocompleteDone(OmniboxView* omnibox_view) {
-    AutocompleteController* controller =
-        omnibox_view->model()->popup_model()->autocomplete_controller();
-    while (!controller->done()) {
-      content::WindowedNotificationObserver ready_observer(
-          chrome::NOTIFICATION_AUTOCOMPLETE_CONTROLLER_RESULT_READY,
-          content::Source<AutocompleteController>(controller));
-      ready_observer.Wait();
-    }
-  }
-
   predictors::AutocompleteActionPredictor* GetAutocompleteActionPredictor() {
     Profile* profile = current_browser()->profile();
     return predictors::AutocompleteActionPredictorFactory::GetForProfile(
@@ -2392,7 +2382,7 @@
   omnibox_view->SetUserText(base::UTF8ToUTF16(
       embedded_test_server()->GetURL("/empty.html?1").spec()));
   omnibox_view->OnAfterPossibleChange(true);
-  WaitForAutocompleteDone(omnibox_view);
+  ui_test_utils::WaitForAutocompleteDone(current_browser());
 
   // Fake an omnibox prerender for a different URL.
   std::unique_ptr<TestPrerender> prerender =
diff --git a/chrome/browser/previews/defer_all_script_browsertest.cc b/chrome/browser/previews/defer_all_script_browsertest.cc
index 8b6e69d9..2037eee 100644
--- a/chrome/browser/previews/defer_all_script_browsertest.cc
+++ b/chrome/browser/previews/defer_all_script_browsertest.cc
@@ -302,7 +302,6 @@
   test_ukm_recorder.ExpectEntryMetric(entry, UkmEntry::kcoin_flip_resultName,
                                       2);
   test_ukm_recorder.ExpectEntryMetric(entry, UkmEntry::kpreviews_likelyName, 1);
-  // TODO(dougarnett): Verify preview type not set once ukm support landed.
-  //  test_ukm_recorder.ExpectEntryMetric(
-  //      entry, UkmEntry::kdefer_all_scriptName, false);
+  test_ukm_recorder.ExpectEntryMetric(entry, UkmEntry::kdefer_all_scriptName,
+                                      true);
 }
diff --git a/chrome/browser/previews/previews_prober.cc b/chrome/browser/previews/previews_prober.cc
index 8ed87f1a..66aef53 100644
--- a/chrome/browser/previews/previews_prober.cc
+++ b/chrome/browser/previews/previews_prober.cc
@@ -21,6 +21,13 @@
 
 namespace {
 
+std::string NameForClient(PreviewsProber::ClientName name) {
+  switch (name) {
+    case PreviewsProber::ClientName::kLitepages:
+      return "litepages";
+  }
+}
+
 std::string HttpMethodToString(PreviewsProber::HttpMethod http_method) {
   switch (http_method) {
     case PreviewsProber::HttpMethod::kGet:
@@ -57,7 +64,7 @@
 PreviewsProber::PreviewsProber(
     Delegate* delegate,
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    const std::string& name,
+    const ClientName name,
     const GURL& url,
     const HttpMethod http_method,
     const net::HttpRequestHeaders headers,
@@ -76,7 +83,7 @@
 PreviewsProber::PreviewsProber(
     Delegate* delegate,
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    const std::string& name,
+    const ClientName name,
     const GURL& url,
     const HttpMethod http_method,
     const net::HttpRequestHeaders headers,
@@ -84,7 +91,7 @@
     const TimeoutPolicy& timeout_policy,
     const base::TickClock* tick_clock)
     : delegate_(delegate),
-      name_(name),
+      name_(NameForClient(name)),
       url_(url),
       http_method_(http_method),
       headers_(headers),
diff --git a/chrome/browser/previews/previews_prober.h b/chrome/browser/previews/previews_prober.h
index 3c6e5c1a..6476e45 100644
--- a/chrome/browser/previews/previews_prober.h
+++ b/chrome/browser/previews/previews_prober.h
@@ -51,6 +51,14 @@
                                    std::unique_ptr<std::string> body) = 0;
   };
 
+  // Callers who wish to use this class should add a value to this enum. This
+  // enum is mapped to a string value which is then used in histograms and
+  // prefs.
+  enum class ClientName {
+    // TODO(crbug.com/971918): Use in litepages.
+    kLitepages,
+  };
+
   // This enum describes the different algorithms that can be used to calculate
   // a time delta between probe events like retries or timeout ttl.
   enum class Backoff {
@@ -108,7 +116,7 @@
   PreviewsProber(
       Delegate* delegate,
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      const std::string& name,
+      ClientName name,
       const GURL& url,
       HttpMethod http_method,
       const net::HttpRequestHeaders headers,
@@ -134,7 +142,7 @@
   PreviewsProber(
       Delegate* delegate,
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      const std::string& name,
+      ClientName name,
       const GURL& url,
       HttpMethod http_method,
       const net::HttpRequestHeaders headers,
diff --git a/chrome/browser/previews/previews_prober_browsertest.cc b/chrome/browser/previews/previews_prober_browsertest.cc
index 443de74..d962b40 100644
--- a/chrome/browser/previews/previews_prober_browsertest.cc
+++ b/chrome/browser/previews/previews_prober_browsertest.cc
@@ -22,7 +22,6 @@
 #include "services/service_manager/public/cpp/connector.h"
 
 namespace {
-const char kName[] = "testing";
 
 void WaitForCompletedProbe(PreviewsProber* prober) {
   while (true) {
@@ -130,8 +129,9 @@
   PreviewsProber::TimeoutPolicy timeout_policy;
 
   PreviewsProber prober(&delegate, browser()->profile()->GetURLLoaderFactory(),
-                        kName, url, PreviewsProber::HttpMethod::kGet, headers,
-                        retry_policy, timeout_policy);
+                        PreviewsProber::ClientName::kLitepages, url,
+                        PreviewsProber::HttpMethod::kGet, headers, retry_policy,
+                        timeout_policy);
   prober.SendNowIfInactive();
   WaitForCompletedProbe(&prober);
 
@@ -150,8 +150,9 @@
   timeout_policy.base_timeout = base::TimeDelta::FromMilliseconds(1);
 
   PreviewsProber prober(&delegate, browser()->profile()->GetURLLoaderFactory(),
-                        kName, url, PreviewsProber::HttpMethod::kGet, headers,
-                        retry_policy, timeout_policy);
+                        PreviewsProber::ClientName::kLitepages, url,
+                        PreviewsProber::HttpMethod::kGet, headers, retry_policy,
+                        timeout_policy);
   prober.SendNowIfInactive();
   WaitForCompletedProbe(&prober);
 
@@ -166,8 +167,9 @@
   PreviewsProber::TimeoutPolicy timeout_policy;
 
   PreviewsProber prober(&delegate, browser()->profile()->GetURLLoaderFactory(),
-                        kName, url, PreviewsProber::HttpMethod::kGet, headers,
-                        retry_policy, timeout_policy);
+                        PreviewsProber::ClientName::kLitepages, url,
+                        PreviewsProber::HttpMethod::kGet, headers, retry_policy,
+                        timeout_policy);
   SimulateNetworkChange(network::mojom::ConnectionType::CONNECTION_4G);
   WaitForCompletedProbe(&prober);
 
diff --git a/chrome/browser/previews/previews_prober_unittest.cc b/chrome/browser/previews/previews_prober_unittest.cc
index 8f312b9..ce36be51 100644
--- a/chrome/browser/previews/previews_prober_unittest.cc
+++ b/chrome/browser/previews/previews_prober_unittest.cc
@@ -18,7 +18,6 @@
 
 namespace {
 const GURL kTestUrl("https://test.com");
-const char kName[] = "testing";
 }  // namespace
 
 class TestDelegate : public PreviewsProber::Delegate {
@@ -48,7 +47,7 @@
   TestPreviewsProber(
       PreviewsProber::Delegate* delegate,
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      const std::string& name,
+      const PreviewsProber::ClientName name,
       const GURL& url,
       const HttpMethod http_method,
       const net::HttpRequestHeaders headers,
@@ -100,7 +99,8 @@
     net::HttpRequestHeaders headers;
     headers.SetHeader("X-Testing", "Hello world");
     return std::make_unique<TestPreviewsProber>(
-        delegate, test_shared_loader_factory_, kName, kTestUrl,
+        delegate, test_shared_loader_factory_,
+        PreviewsProber::ClientName::kLitepages, kTestUrl,
         PreviewsProber::HttpMethod::kGet, headers, retry_policy, timeout_policy,
         thread_bundle_.GetMockTickClock());
   }
diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
index 19386646..0ccf7965 100644
--- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
+++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -29,8 +29,8 @@
 #include "chrome/browser/download/download_service_factory.h"
 #include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/engagement/site_engagement_service_factory.h"
-#include "chrome/browser/favicon/favicon_request_handler_factory.h"
 #include "chrome/browser/favicon/favicon_service_factory.h"
+#include "chrome/browser/favicon/history_ui_favicon_request_handler_factory.h"
 #include "chrome/browser/feature_engagement/tracker_factory.h"
 #include "chrome/browser/google/google_search_domain_mixing_metrics_emitter_factory.h"
 #include "chrome/browser/google/google_url_tracker_factory.h"
@@ -274,7 +274,7 @@
   extensions::VerifyTrustAPI::GetFactoryInstance();
 #endif
   FaviconServiceFactory::GetInstance();
-  FaviconRequestHandlerFactory::GetInstance();
+  HistoryUiFaviconRequestHandlerFactory::GetInstance();
 #if BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
   feature_engagement::BookmarkTrackerFactory::GetInstance();
   feature_engagement::IncognitoWindowTrackerFactory::GetInstance();
diff --git a/chrome/browser/resources/chromeos/autoclick/autoclick.js b/chrome/browser/resources/chromeos/autoclick/autoclick.js
index c0711b1..16e4a268 100644
--- a/chrome/browser/resources/chromeos/autoclick/autoclick.js
+++ b/chrome/browser/resources/chromeos/autoclick/autoclick.js
@@ -10,13 +10,23 @@
 const AUTOCLICK_FOCUS_RING_COLOR = '#aac9fa';
 
 /**
+ * The amount of time to wait before hiding the focus rings from the display.
+ * @private {number}
+ * @const
+ */
+const AUTOCLICK_FOCUS_RING_DISPLAY_TIME_MS = 250;
+
+/**
  * Class to manage Automatic Clicks' interaction with the accessibility tree.
  */
 class Autoclick {
-  constructor() {
-    console.log('Autoclick is enabled.');
-
-
+  constructor(blinkFocusRings) {
+    /**
+     * Whether to blink the focus rings. Disabled during tests due to
+     * complications with callbacks.
+     * @private {boolean}
+     */
+    this.blinkFocusRings_ = blinkFocusRings;
 
     /**
      * @private {chrome.automation.AutomationNode}
@@ -53,6 +63,8 @@
    * @private
    */
   setFocusRings_(rects) {
+    // TODO(katie): Add a property to FocusRingInfo to set FocusRingBehavior
+    // to fade out.
     chrome.accessibilityPrivate.setFocusRings([{
       rects: rects,
       type: chrome.accessibilityPrivate.FocusType.SOLID,
@@ -97,8 +109,26 @@
     }
     if (!node.location)
       return;
-    this.setFocusRings_([node.location]);
-    chrome.accessibilityPrivate.onScrollableBoundsForPointFound(node.location);
+    let bounds = node.location;
+    this.setFocusRings_([bounds]);
+    if (this.blinkFocusRings_) {
+      // Blink the focus ring briefly per UX spec, using timeouts to turn it
+      // off, on, and off again. The focus ring is only used to show the user
+      // where the scroll might occur, but is not persisted after the blink.
+      // Turn off after 500 ms.
+      setTimeout(() => {
+        this.setFocusRings_([]);
+      }, AUTOCLICK_FOCUS_RING_DISPLAY_TIME_MS * 2);
+      // Back on after an additional 250 ms.
+      setTimeout(() => {
+        this.setFocusRings_([bounds]);
+      }, AUTOCLICK_FOCUS_RING_DISPLAY_TIME_MS * 3);
+      // And off after another 500 ms.
+      setTimeout(() => {
+        this.setFocusRings_([]);
+      }, AUTOCLICK_FOCUS_RING_DISPLAY_TIME_MS * 5);
+    }
+    chrome.accessibilityPrivate.onScrollableBoundsForPointFound(bounds);
   }
 
   /**
@@ -114,4 +144,4 @@
 }
 
 // Initialize the Autoclick extension.
-let autoclick = new Autoclick();
\ No newline at end of file
+let autoclick = new Autoclick(true /* blink focus rings */);
diff --git a/chrome/browser/resources/chromeos/autoclick/autoclick_test.extjs b/chrome/browser/resources/chromeos/autoclick/autoclick_test.extjs
index 720ae33..b381083 100644
--- a/chrome/browser/resources/chromeos/autoclick/autoclick_test.extjs
+++ b/chrome/browser/resources/chromeos/autoclick/autoclick_test.extjs
@@ -15,7 +15,7 @@
   chrome.accessibilityPrivate = this.mockAccessibilityPrivate;
 
   // Re-initialize Autoclick with mock AccessibilityPrivate API.
-  autoclick = new Autoclick();
+  autoclick = new Autoclick(false /* do not blink focus rings */);
 }
 
 AutoclickE2ETest.prototype = {
@@ -170,4 +170,4 @@
 // TODO(crbug.com/978163): Add tests for when the scrollable area is scrolled
 // all the way up or down, left or right. Add tests for nested scrollable areas.
 // Add tests for root types like toolbar, dialog, and window to ensure
-// we don't break boundaries when searching for scroll bars.
\ No newline at end of file
+// we don't break boundaries when searching for scroll bars.
diff --git a/chrome/browser/resources/chromeos/camera/src/js/mojo/imagecapture.js b/chrome/browser/resources/chromeos/camera/src/js/mojo/imagecapture.js
index 36da568..2fad22c 100644
--- a/chrome/browser/resources/chromeos/camera/src/js/mojo/imagecapture.js
+++ b/chrome/browser/resources/chromeos/camera/src/js/mojo/imagecapture.js
@@ -77,10 +77,11 @@
 /**
  * Gets the data from Camera metadata by its tag.
  * @param {cros.mojom.CameraMetadata} metadata Camera metadata from which to
- *   query the data.
+ *     query the data.
  * @param {cros.mojom.CameraMetadataTag} tag Camera metadata tag to query for.
  * @return {Array<Object>} An array containing elements whose types correspond
- *   to the format of input |tag|. If nothing is found, returns an empty array.
+ *     to the format of input |tag|. If nothing is found, returns an empty
+ *     array.
  * @private
  */
 cca.mojo.getMetadataData_ = function(metadata, tag) {
@@ -201,17 +202,13 @@
 /**
  * Gets supported photo resolutions for specific camera.
  * @param {string} deviceId The renderer-facing device Id of the target camera
- *   which could be retrieved from MediaDeviceInfo.deviceId.
+ *     which could be retrieved from MediaDeviceInfo.deviceId.
  * @return {Promise<Array<Object>>} Promise of supported resolutions. Each
- *   photo resolution is represented as [width, height].
+ *     photo resolution is represented as [width, height].
  */
 cca.mojo.getPhotoResolutions = function(deviceId) {
   const formatBlob = 33;
   const typeOutputStream = 0;
-  const formatIndex = 0;
-  const widthIndex = 1;
-  const heightIndex = 2;
-  const typeIndex = 3;
   const numElementPerEntry = 4;
 
   return cca.mojo.MojoInterface.getProxy().getCameraInfo(deviceId).then(
@@ -229,17 +226,11 @@
         }
 
         let supportedResolutions = [];
-        for (let configIdx = 0, configBase = 0;
-             configBase < streamConfigs.length;
-             configIdx++, configBase = configIdx * numElementPerEntry) {
-          const format = streamConfigs[configBase + formatIndex];
-          if (format === formatBlob) {
-            const type = streamConfigs[configBase + typeIndex];
-            if (type === typeOutputStream) {
-              const width = streamConfigs[configBase + widthIndex];
-              const height = streamConfigs[configBase + heightIndex];
-              supportedResolutions.push([width, height]);
-            }
+        for (let i = 0; i < streamConfigs.length; i += numElementPerEntry) {
+          const [format, width, height, type] =
+              streamConfigs.slice(i, i + numElementPerEntry);
+          if (format === formatBlob && type === typeOutputStream) {
+            supportedResolutions.push([width, height]);
           }
         }
         return supportedResolutions;
@@ -249,18 +240,14 @@
 /**
  * Gets supported video configurations for specific camera.
  * @param {string} deviceId The renderer-facing device Id of the target camera
- *   which could be retrieved from MediaDeviceInfo.deviceId.
+ *     which could be retrieved from MediaDeviceInfo.deviceId.
  * @return {Promise<Array<Object>>} Promise of supported video configurations.
- * Each configuration is represented as [width, height, fps].
+ *     Each configuration is represented as [width, height, maxFps].
  */
 cca.mojo.getVideoConfigs = function(deviceId) {
   // Currently we use YUV format for both recording and previewing on Chrome.
   const formatYuv = 35;
   const oneSecondInNs = 1000000000;
-  const formatIndex = 0;
-  const widthIndex = 1;
-  const heightIndex = 2;
-  const durationIndex = 3;
   const numElementPerEntry = 4;
 
   return cca.mojo.MojoInterface.getProxy().getCameraInfo(deviceId).then(
@@ -279,17 +266,13 @@
         }
 
         let supportedConfigs = [];
-        for (let configIdx = 0, configBase = 0;
-             configBase < minFrameDurationConfigs.length;
-             configIdx++, configBase = configIdx * numElementPerEntry) {
-          const format = minFrameDurationConfigs[configBase + formatIndex];
+        for (let i = 0; i < minFrameDurationConfigs.length;
+             i += numElementPerEntry) {
+          const [format, width, height, minDuration] =
+              minFrameDurationConfigs.slice(i, i + numElementPerEntry);
           if (format === formatYuv) {
-            const width = minFrameDurationConfigs[configBase + widthIndex];
-            const height = minFrameDurationConfigs[configBase + heightIndex];
-            const fps = Math.round(
-                oneSecondInNs /
-                minFrameDurationConfigs[configBase + durationIndex]);
-            supportedConfigs.push([width, height, fps]);
+            const maxFps = Math.round(oneSecondInNs / minDuration);
+            supportedConfigs.push([width, height, maxFps]);
           }
         }
         return supportedConfigs;
@@ -299,7 +282,7 @@
 /**
  * Gets camera facing for given device.
  * @param {string} deviceId The renderer-facing device Id of the target camera
- *   which could be retrieved from MediaDeviceInfo.deviceId.
+ *     which could be retrieved from MediaDeviceInfo.deviceId.
  * @return {Promise<cros.mojom.CameraFacing>} Promise of device facing.
  */
 cca.mojo.getCameraFacing = function(deviceId) {
@@ -308,3 +291,86 @@
         return cameraInfo.facing;
       });
 };
+
+/**
+ * Gets supported fps ranges for specific camera.
+ * @param {string} deviceId The renderer-facing device Id of the target camera
+ *     which could be retrieved from MediaDeviceInfo.deviceId.
+ * @return {Promise<Array<Object>>} Promise of supported fps ranges. Each range
+ *     is represented as [min, max].
+ */
+cca.mojo.getSupportedFpsRanges = function(deviceId) {
+  const numElementPerEntry = 2;
+
+  return cca.mojo.MojoInterface.getProxy().getCameraInfo(deviceId).then(
+      ({cameraInfo}) => {
+        const staticMetadata = cameraInfo.staticCameraCharacteristics;
+        const availableFpsRanges = cca.mojo.getMetadataData_(
+            staticMetadata,
+            cros.mojom.CameraMetadataTag
+                .ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);
+        // The data of |availableFpsRanges| looks like:
+        // availableFpsRanges: [RANGE_1_MIN, RANGE_1_MAX,
+        //                      RANGE_2_MIN, RANGE_2_MAX, ...]
+        if (availableFpsRanges.length % numElementPerEntry != 0) {
+          throw new Error('Unexpected length of available fps range configs');
+        }
+
+        let supportedFpsRanges = [];
+        for (let i = 0; i < availableFpsRanges.length;
+             i += numElementPerEntry) {
+          const [rangeMin, rangeMax] =
+              availableFpsRanges.slice(i, i + numElementPerEntry);
+          supportedFpsRanges.push([rangeMin, rangeMax]);
+        }
+        return supportedFpsRanges;
+      });
+};
+
+/**
+ * Gets user media with custom negotiation through CrosImageCapture API,
+ * such as frame rate range negotiation.
+ * @param {string} deviceId The renderer-facing device Id of the target camera
+ *     which could be retrieved from MediaDeviceInfo.deviceId.
+ * @param {MediaStreamConstraints} constraints The constraints that would be
+ *     passed to get user media. If frame rate range negotiation is needed, the
+ *     caller should either set exact field or set both min and max fields for
+ *     frame rate property.
+ * @return {Promise<MediaStream>} Promise of the MediaStream that returned from
+ *     MediaDevices.getUserMedia().
+ */
+cca.mojo.getUserMedia = function(deviceId, constraints) {
+  let streamWidth = 0;
+  let streamHeight = 0;
+  let minFrameRate = 0;
+  let maxFrameRate = 0;
+
+  if (constraints && constraints.video && constraints.video.frameRate) {
+    const frameRate = constraints.video.frameRate;
+    if (frameRate.exact) {
+      minFrameRate = frameRate.exact;
+      maxFrameRate = frameRate.exact;
+    } else if (frameRate.min && frameRate.max) {
+      minFrameRate = frameRate.min;
+      maxFrameRate = frameRate.max;
+    }
+
+    streamWidth = constraints.video.width;
+    streamHeight = constraints.video.height;
+  }
+
+  try {
+    return cca.mojo.MojoInterface.getProxy()
+        .setFpsRange(
+            deviceId, streamWidth, streamHeight, minFrameRate, maxFrameRate)
+        .then(({isSuccess}) => {
+          if (!isSuccess) {
+            console.error('Failed to negotiate the frame rate range.');
+          }
+          return navigator.mediaDevices.getUserMedia(constraints);
+        });
+  } catch (e) {
+    console.error(e);
+  }
+  return navigator.mediaDevices.getUserMedia(constraints);
+};
diff --git a/chrome/browser/resources/chromeos/wallpaper_manager/main.html b/chrome/browser/resources/chromeos/wallpaper_manager/main.html
index 13dcbbd..2b78418 100644
--- a/chrome/browser/resources/chromeos/wallpaper_manager/main.html
+++ b/chrome/browser/resources/chromeos/wallpaper_manager/main.html
@@ -158,7 +158,8 @@
   </div>
   <div id="top-header">
     <div class="top-header-contents">
-      <div id="cancel-preview-wallpaper" class="preview-option"></div>
+      <div id="cancel-preview-wallpaper" i18n-values="title:backButton"
+          class="preview-option"></div>
       <div id="image-title"></div>
       <div class="divider"></div>
       <div id="wallpaper-description"></div>
diff --git a/chrome/browser/resources/local_ntp/customize.js b/chrome/browser/resources/local_ntp/customize.js
index 39a85f6..10079a0 100644
--- a/chrome/browser/resources/local_ntp/customize.js
+++ b/chrome/browser/resources/local_ntp/customize.js
@@ -200,10 +200,10 @@
 customize.builtTiles = false;
 
 /**
- * The background image tile that was selected by the user.
- * @type {?Element}
+ * The default title for the richer picker.
+ * @type {string}
  */
-customize.selectedBackgroundTile = null;
+customize.richerPicker_defaultTitle = '';
 
 /**
  * Called when the error notification should be shown.
@@ -218,39 +218,53 @@
 customize.hideCustomLinkNotification = null;
 
 /**
- * The currently selected submenu (i.e. Background, Shortcuts, etc.) in the
- * richer picker. Corresponds to the submenu's button element in the sidebar.
- * @type {?Element}
+ * The currently active Background submenu. This can be the collections page or
+ * a collection's image menu. Defaults to the collections page.
+ * @type {Object}
  */
-customize.richerPicker_selectedSubmenu = null;
+customize.richerPicker_openBackgroundSubmenu = {
+  menuId: customize.IDS.BACKGROUNDS_MENU,
+  title: '',
+};
+
+/**
+ * The currently selected submenu (i.e. Background, Shortcuts, etc.) in the
+ * richer picker.
+ * @type {Object}
+ */
+customize.richerPicker_selectedSubmenu = {
+  menuButton: null,  // The submenu's button element in the sidebar.
+  menu: null,        // The submenu's menu element.
+  // The submenu's title. Will usually be |customize.richerPicker_defaultTitle|
+  // unless this is a background collection's image menu.
+  title: '',
+};
+
+/**
+ * The currently selected options in the customization menu.
+ * @type {Object}
+ */
+customize.selectedOptions = {
+  background: null,  // Contains the background image tile.
+  // Contains the selected shortcut type's DOM element, i.e. either custom links
+  // or most visited.
+  shortcutType: null,
+  shortcutsAreHidden: false,
+  color: null,  // Contains the selected color tile's DOM element.
+};
 
 /**
  * The preselected options for Shortcuts in the richer picker.
  * @type {Object}
  */
 customize.preselectedShortcutOptions = {
-  // Contains the selected type's DOM element, i.e. either custom links or most visited.
+  // Contains the selected type's DOM element, i.e. either custom links or most
+  // visited.
   shortcutType: null,
   isHidden: false,
 };
 
 /**
- * The currently selected options for Shortcuts in the richer picker.
- * @type {Object}
- */
-customize.selectedShortcutOptions = {
-  // Contains the preselected type's DOM element, i.e. either custom links or most visited.
-  shortcutType: null,
-  isHidden: false,
-};
-
-/**
- * The currently selected option in the Colors menu.
- * @type {?Element}
- */
-customize.selectedColorTile = null;
-
-/**
  * Whether tiles for Colors menu already loaded.
  * @type {boolean}
  */
@@ -339,7 +353,7 @@
 
 customize.unselectTile = function() {
   $(customize.IDS.DONE).disabled = true;
-  customize.selectedBackgroundTile = null;
+  customize.selectedOptions.background = null;
   $(customize.IDS.DONE).tabIndex = -1;
 };
 
@@ -358,37 +372,79 @@
 
 /**
  * Apply selected styling to |menuButton| and make corresponding |menu| visible.
- * @param {?Element} menuButton The button element to apply styling to.
+ * If |title| is not specified, the default title will be used.
+ * @param {?Element} menuButton The sidebar button element to apply styling to.
  * @param {?Element} menu The submenu element to apply styling to.
+ * @param {string=} title The submenu's title.
  */
-customize.richerPicker_selectSubmenu = function(menuButton, menu) {
+customize.richerPicker_showSubmenu = function(menuButton, menu, title = '') {
   if (!menuButton || !menu) {
     return;
   }
-  customize.richerPicker_selectedSubmenu = menuButton;
+
+  customize.richerPicker_hideOpenSubmenu();
+
+  if (!title) {  // Use the default title if not specified.
+    title = customize.richerPicker_defaultTitle;
+  }
+
+  // Save this as the currently open submenu.
+  customize.richerPicker_selectedSubmenu.menuButton = menuButton;
+  customize.richerPicker_selectedSubmenu.menu = menu;
+  customize.richerPicker_selectedSubmenu.title = title;
+
   menuButton.classList.toggle(customize.CLASSES.SELECTED, true);
   menu.classList.toggle(customize.CLASSES.MENU_SHOWN, true);
+  $(customize.IDS.MENU_TITLE).textContent = title;
+
+  // Indicate if this is a Background collection's image menu, which will enable
+  // the back button.
+  $(customize.IDS.CUSTOMIZATION_MENU)
+      .classList.toggle(
+          customize.CLASSES.ON_IMAGE_MENU,
+          menu.id === customize.IDS.BACKGROUNDS_IMAGE_MENU);
+};
+
+/**
+ * Hides the currently open submenu if any.
+ */
+customize.richerPicker_hideOpenSubmenu = function() {
+  if (!customize.richerPicker_selectedSubmenu.menuButton) {
+    return;  // No submenu is open.
+  }
+
+  customize.richerPicker_selectedSubmenu.menuButton.classList.toggle(
+      customize.CLASSES.SELECTED, false);
+  customize.richerPicker_selectedSubmenu.menu.classList.toggle(
+      customize.CLASSES.MENU_SHOWN, false);
+  $(customize.IDS.MENU_TITLE).textContent = customize.richerPicker_defaultTitle;
+
+  customize.richerPicker_selectedSubmenu.menuButton = null;
+  customize.richerPicker_selectedSubmenu.menu = null;
+  customize.richerPicker_selectedSubmenu.title =
+      customize.richerPicker_defaultTitle;
 };
 
 /**
  * Remove image tiles and maybe swap back to main background menu.
- * @param {boolean} showMenu Whether the main background menu should be shown.
  */
-customize.richerPicker_resetImageMenu = function(showMenu) {
+customize.richerPicker_resetImageMenu = function() {
   const backgroundMenu = $(customize.IDS.BACKGROUNDS_MENU);
   const imageMenu = $(customize.IDS.BACKGROUNDS_IMAGE_MENU);
   const menu = $(customize.IDS.CUSTOMIZATION_MENU);
   const menuTitle = $(customize.IDS.MENU_TITLE);
 
   imageMenu.innerHTML = '';
-  imageMenu.classList.toggle(customize.CLASSES.MENU_SHOWN, false);
-  menuTitle.textContent = menuTitle.dataset.mainTitle;
   menu.classList.toggle(customize.CLASSES.ON_IMAGE_MENU, false);
-  backgroundMenu.classList.toggle(customize.CLASSES.MENU_SHOWN, showMenu);
+  customize.richerPicker_showSubmenu(
+      $(customize.IDS.BACKGROUNDS_BUTTON), backgroundMenu);
+  customize.richerPicker_openBackgroundSubmenu.menuId =
+      customize.IDS.BACKGROUNDS_MENU;
+  customize.richerPicker_openBackgroundSubmenu.title = '';
   backgroundMenu.scrollTop = 0;
 
   customize.richerPicker_deselectBackgroundTile(
-      customize.selectedBackgroundTile);
+      customize.selectedOptions.background);
 };
 
 /**
@@ -404,14 +460,13 @@
 };
 
 /**
- * Close and reset the dialog, and set the background.
+ * Close and reset the dialog if this is not the richer picker, and set the
+ * background.
  * @param {string} url The url of the selected background.
  */
 customize.setBackground = function(
     url, attributionLine1, attributionLine2, attributionActionUrl) {
-  if (configData.richerPicker) {
-    customize.richerPicker_closeCustomizationMenu();
-  } else {
+  if (!configData.richerPicker) {
     customize.closeCollectionDialog($(customize.IDS.MENU));
   }
   window.chrome.embeddedSearch.newTabPage.setBackgroundURLWithAttributions(
@@ -422,13 +477,19 @@
  * Apply selected shortcut options.
  */
 customize.richerPicker_setShortcutOptions = function() {
-  if (customize.preselectedShortcutOptions.shortcutType !==
-      customize.selectedShortcutOptions.shortcutType) {
-    chrome.embeddedSearch.newTabPage.toggleMostVisitedOrCustomLinks();
+  const shortcutTypeChanged =
+      customize.preselectedShortcutOptions.shortcutType !==
+      customize.selectedOptions.shortcutType;
+  if (customize.preselectedShortcutOptions.shortcutsAreHidden !==
+      customize.selectedOptions.shortcutsAreHidden) {
+    // Only trigger a notification if |toggleMostVisitedOrCustomLinks| will not
+    // be called immediately after. Successive |onmostvisitedchange| events can
+    // interfere with each other.
+    chrome.embeddedSearch.newTabPage.toggleShortcutsVisibility(
+        !shortcutTypeChanged);
   }
-  if (customize.preselectedShortcutOptions.isHidden !==
-      customize.selectedShortcutOptions.isHidden) {
-    chrome.embeddedSearch.newTabPage.toggleShortcutsVisibility();
+  if (shortcutTypeChanged) {
+    chrome.embeddedSearch.newTabPage.toggleMostVisitedOrCustomLinks();
   }
 };
 
@@ -633,7 +694,7 @@
         // In the RP the upload or default tile may be selected.
         if (configData.richerPicker) {
           customize.richerPicker_deselectBackgroundTile(
-              customize.selectedBackgroundTile);
+              customize.selectedOptions.background);
         } else {
           customize.resetSelectionDialog();
         }
@@ -707,10 +768,35 @@
 };
 
 /**
- * Enable or disable the 'done' button.
- * @param {boolean} enable True if the done button should be enabled.
+ * Return true if any shortcut option is selected.
+ * Note: Shortcut options are preselected according to current user settings.
  */
-customize.richerPicker_toggleDone = function(enable) {
+customize.richerPicker_isShortcutOptionSelected = function() {
+  // Check if the currently selected options are not the preselection.
+  const notPreselectedType =
+      customize.preselectedShortcutOptions.shortcutType !==
+      customize.selectedOptions.shortcutType;
+  const notPreselectedHidden =
+      customize.preselectedShortcutOptions.shortcutsAreHidden !==
+      customize.selectedOptions.shortcutsAreHidden;
+  return notPreselectedType || notPreselectedHidden;
+};
+
+/**
+ * Return true if any option is selected. Used to enable the 'done' button.
+ */
+customize.richerPicker_isOptionSelected = function() {
+  return !!customize.selectedOptions.background ||
+      !!customize.selectedOptions.color ||
+      customize.richerPicker_isShortcutOptionSelected();
+};
+
+/**
+ * Enable the 'done' button if any option is selected. If no option is selected,
+ * disable the 'done' button.
+ */
+customize.richerPicker_maybeToggleDone = function() {
+  const enable = customize.richerPicker_isOptionSelected();
   $(customize.IDS.MENU_DONE).disabled = !enable;
   $(customize.IDS.MENU_DONE).tabIndex = enable ? 1 : 0;
 };
@@ -790,9 +876,9 @@
   if (!tile) {
     return;
   }
-  customize.selectedBackgroundTile = tile;
+  customize.selectedOptions.background = tile;
   customize.richerPicker_applySelectedState(tile);
-  customize.richerPicker_toggleDone(true);
+  customize.richerPicker_maybeToggleDone();
   customize.richerPicker_previewImage(tile);
 };
 
@@ -805,61 +891,47 @@
   if (!tile) {
     return;
   }
-  customize.selectedBackgroundTile = null;
+  customize.selectedOptions.background = null;
   customize.richerPicker_removeSelectedState(tile);
-  customize.richerPicker_toggleDone(false);
+  customize.richerPicker_maybeToggleDone();
   customize.richerPicker_unpreviewImage(tile);
 };
 
 /**
- * Enable the 'done' button if the selected shortcut options were not
- * preselected.
- * Note: Shortcut options are preselected according to current user settings.
- */
-customize.richerPicker_maybeToggleDoneShortcuts = function() {
-  const notPreselectedType =
-      customize.preselectedShortcutOptions.shortcutType !==
-      customize.selectedShortcutOptions.shortcutType;
-  const notPreselectedHidden = customize.preselectedShortcutOptions.isHidden !==
-      customize.selectedShortcutOptions.isHidden;
-  customize.richerPicker_toggleDone(notPreselectedType || notPreselectedHidden);
-};
-
-/**
  * Handles shortcut type selection. Apply styling to a selected shortcut option
  * and enable the done button.
  * @param {?Element} shortcutType The shortcut type option's element.
  */
 customize.richerPicker_selectShortcutType = function(shortcutType) {
   if (!shortcutType ||
-      customize.selectedShortcutOptions.shortcutType === shortcutType) {
+      customize.selectedOptions.shortcutType === shortcutType) {
     return;  // The option has already been selected.
   }
 
   // Clear the previous selection, if any.
-  if (customize.selectedShortcutOptions.shortcutType) {
+  if (customize.selectedOptions.shortcutType) {
     customize.richerPicker_removeSelectedState(
-        customize.selectedShortcutOptions.shortcutType);
+        customize.selectedOptions.shortcutType);
   }
-  customize.selectedShortcutOptions.shortcutType = shortcutType;
+  customize.selectedOptions.shortcutType = shortcutType;
   customize.richerPicker_applySelectedState(shortcutType);
-  customize.richerPicker_maybeToggleDoneShortcuts();
+  customize.richerPicker_maybeToggleDone();
 };
 
 /**
  * Handles hide shortcuts toggle. Apply/remove styling for the toggle and
  * enable/disable the done button.
- * @param {boolean} isHidden True if the shortcuts are hidden, i.e. the toggle
+ * @param {boolean} areHidden True if the shortcuts are hidden, i.e. the toggle
  *     is on.
  */
-customize.richerPicker_toggleShortcutHide = function(isHidden) {
+customize.richerPicker_toggleShortcutHide = function(areHidden) {
   // (De)select the shortcut hide option.
   $(customize.IDS.SHORTCUTS_HIDE)
-      .classList.toggle(customize.CLASSES.SELECTED, isHidden);
-  $(customize.IDS.SHORTCUTS_HIDE_TOGGLE).checked = isHidden;
+      .classList.toggle(customize.CLASSES.SELECTED, areHidden);
+  $(customize.IDS.SHORTCUTS_HIDE_TOGGLE).checked = areHidden;
 
-  customize.selectedShortcutOptions.isHidden = isHidden;
-  customize.richerPicker_maybeToggleDoneShortcuts();
+  customize.selectedOptions.shortcutsAreHidden = areHidden;
+  customize.richerPicker_maybeToggleDone();
 };
 
 /**
@@ -909,8 +981,14 @@
                                          $(customize.IDS.MENU);
 
   if (configData.richerPicker) {
-    $(customize.IDS.MENU_TITLE).textContent = dialogTitle;
     menu.classList.toggle(customize.CLASSES.ON_IMAGE_MENU, true);
+    customize.richerPicker_showSubmenu(
+        $(customize.IDS.BACKGROUNDS_BUTTON), tileContainer, dialogTitle);
+    // Save the current image menu. Used to restore to when the Background
+    // submenu is reopened.
+    customize.richerPicker_openBackgroundSubmenu.menuId =
+        customize.IDS.BACKGROUNDS_IMAGE_MENU;
+    customize.richerPicker_openBackgroundSubmenu.title = dialogTitle;
   } else {
     $(customize.IDS.TITLE).textContent = dialogTitle;
     menu.classList.remove(customize.CLASSES.COLLECTION_DIALOG);
@@ -918,17 +996,17 @@
   }
 
   const tileInteraction = function(tile) {
-    if (customize.selectedBackgroundTile) {
+    if (customize.selectedOptions.background) {
       if (configData.richerPicker) {
-        const id = customize.selectedBackgroundTile.id;
+        const id = customize.selectedOptions.background.id;
         customize.richerPicker_deselectBackgroundTile(
-            customize.selectedBackgroundTile);
+            customize.selectedOptions.background);
         if (id === tile.id) {
           return;
         }
       } else {
-        customize.removeSelectedState(customize.selectedBackgroundTile);
-        if (customize.selectedBackgroundTile.id === tile.id) {
+        customize.removeSelectedState(customize.selectedOptions.background);
+        if (customize.selectedOptions.background.id === tile.id) {
           customize.unselectTile();
           return;
         }
@@ -939,7 +1017,7 @@
       customize.richerPicker_selectBackgroundTile(tile);
     } else {
       customize.applySelectedState(tile);
-      customize.selectedBackgroundTile = tile;
+      customize.selectedOptions.background = tile;
     }
 
     $(customize.IDS.DONE).tabIndex = 0;
@@ -957,7 +1035,7 @@
       tileInteraction(event.currentTarget);
     } else if (
         clickCount === 2 &&
-        customize.selectedBackgroundTile === event.currentTarget) {
+        customize.selectedOptions.background === event.currentTarget) {
       customize.setBackground(
           event.currentTarget.dataset.url,
           event.currentTarget.dataset.attributionLine1,
@@ -1158,64 +1236,55 @@
  */
 customize.richerPicker_openCustomizationMenu = function() {
   customize.richerPicker_resetCustomizationMenu();
-  $(customize.IDS.BACKGROUNDS_MENU)
-      .classList.toggle(customize.CLASSES.MENU_SHOWN, true);
-  customize.richerPicker_selectedSubmenu = $(customize.IDS.BACKGROUNDS_BUTTON);
+
+  customize.richerPicker_showSubmenu(
+      $(customize.IDS.BACKGROUNDS_BUTTON), $(customize.IDS.BACKGROUNDS_MENU));
+
+  customize.loadChromeBackgrounds();
+  customize.loadColorTiles();
+  if (!$(customize.IDS.CUSTOMIZATION_MENU).open) {
+    $(customize.IDS.CUSTOMIZATION_MENU).showModal();
+  }
 };
 
 /**
  * Reset the selected options in the customization menu.
  */
 customize.richerPicker_resetSelectedOptions = function() {
-  if (customize.selectedBackgroundTile) {
-    // Reset background selection.
-    customize.richerPicker_removeSelectedState(
-        customize.selectedBackgroundTile);
-    customize.selectedBackgroundTile = null;
-  } else if (customize.selectedColorTile) {
-    // Reset color selection.
-    customize.richerPicker_removeSelectedState(customize.selectedColorTile);
-    customize.cancelColor();
-    customize.selectedColorTile = null;
-  }
+  // Reset background selection.
+  customize.richerPicker_deselectBackgroundTile(
+      customize.selectedOptions.background);
+  customize.selectedOptions.background = null;
+
+  // Reset color selection.
+  customize.richerPicker_removeSelectedState(customize.selectedOptions.color);
+  customize.selectedOptions.color = null;
 
   // Preselect the shortcut options.
   const shortcutType = chrome.embeddedSearch.newTabPage.isUsingMostVisited ?
       $(customize.IDS.SHORTCUTS_OPTION_MOST_VISITED) :
       $(customize.IDS.SHORTCUTS_OPTION_CUSTOM_LINKS);
-  const isHidden = !chrome.embeddedSearch.newTabPage.areShortcutsVisible;
+  const shortcutsAreHidden =
+      !chrome.embeddedSearch.newTabPage.areShortcutsVisible;
   customize.richerPicker_selectShortcutType(shortcutType);
-  customize.richerPicker_toggleShortcutHide(isHidden);
-  customize.selectedShortcutOptions.shortcutType = shortcutType;
+  customize.richerPicker_toggleShortcutHide(shortcutsAreHidden);
+  customize.selectedOptions.shortcutType = shortcutType;
   customize.preselectedShortcutOptions.shortcutType = shortcutType;
-  customize.selectedShortcutOptions.isHidden = isHidden;
-  customize.preselectedShortcutOptions.isHidden = isHidden;
+  customize.selectedOptions.shortcutsAreHidden = shortcutsAreHidden;
+  customize.preselectedShortcutOptions.shortcutsAreHidden = shortcutsAreHidden;
 };
 
 /**
  * Resets the customization menu.
  */
 customize.richerPicker_resetCustomizationMenu = function() {
-  // Reset the submenus.
-  customize.richerPicker_resetImageMenu(false);
-  $(customize.IDS.BACKGROUNDS_MENU)
-      .classList.toggle(customize.CLASSES.MENU_SHOWN, false);
-  $(customize.IDS.SHORTCUTS_MENU)
-      .classList.toggle(customize.CLASSES.MENU_SHOWN, false);
-  $(customize.IDS.COLORS_MENU)
-      .classList.toggle(customize.CLASSES.MENU_SHOWN, false);
-  if (customize.richerPicker_selectedSubmenu) {
-    customize.richerPicker_selectedSubmenu.classList.toggle(
-        customize.CLASSES.SELECTED, false);
-    customize.richerPicker_selectedSubmenu = null;
-  }
-  // Reset any selected options.
   customize.richerPicker_resetSelectedOptions();
-  customize.richerPicker_toggleDone(false);
+  customize.richerPicker_resetImageMenu();
+  customize.richerPicker_hideOpenSubmenu();
 };
 
 /**
- * Close customization menu.
+ * Close and reset the customization menu.
  */
 customize.richerPicker_closeCustomizationMenu = function() {
   $(customize.IDS.CUSTOMIZATION_MENU).close();
@@ -1223,6 +1292,39 @@
 };
 
 /**
+ * Cancel customization, revert any changes, and close the richer picker.
+ */
+customize.richerPicker_cancelCustomization = function() {
+  // Cancel any color changes.
+  if (customize.selectedOptions.color) {
+    customize.cancelColor();
+  }
+
+  customize.richerPicker_closeCustomizationMenu();
+};
+
+/**
+ * Apply the currently selected customization options and close the richer
+ * picker.
+ */
+customize.richerPicker_applyCustomization = function() {
+  if (customize.selectedOptions.background) {
+    customize.setBackground(
+        customize.selectedOptions.background.dataset.url,
+        customize.selectedOptions.background.dataset.attributionLine1,
+        customize.selectedOptions.background.dataset.attributionLine2,
+        customize.selectedOptions.background.dataset.attributionActionUrl);
+  }
+  if (customize.richerPicker_isShortcutOptionSelected()) {
+    customize.richerPicker_setShortcutOptions();
+  }
+  if (customize.selectedOptions.color) {
+    customize.confirmColor();
+  }
+  customize.richerPicker_closeCustomizationMenu();
+};
+
+/**
  * Initialize the settings menu, custom backgrounds dialogs, and custom
  * links menu items. Set the text and event handlers for the various
  * elements.
@@ -1239,9 +1341,11 @@
   $(customize.IDS.OPTIONS_TITLE).textContent =
       configData.translatedStrings.customizeBackground;
 
-  // Store the main menu title so it can be restored if needed.
-  $(customize.IDS.MENU_TITLE).dataset.mainTitle =
-      $(customize.IDS.MENU_TITLE).textContent;
+  if (configData.richerPicker) {
+    // Store the main menu title so it can be restored if needed.
+    customize.richerPicker_defaultTitle =
+        $(customize.IDS.MENU_TITLE).textContent;
+  }
 
   $(customize.IDS.EDIT_BG_ICON)
       .setAttribute(
@@ -1254,11 +1358,6 @@
   const editBackgroundInteraction = function() {
     if (configData.richerPicker) {
       customize.richerPicker_openCustomizationMenu();
-      customize.loadChromeBackgrounds();
-      customize.loadColorTiles();
-      if (!$(customize.IDS.CUSTOMIZATION_MENU).open) {
-        $(customize.IDS.CUSTOMIZATION_MENU).showModal();
-      }
     } else {
       editDialog.showModal();
     }
@@ -1269,11 +1368,7 @@
   };
 
   $(customize.IDS.MENU_CANCEL).onclick = function(event) {
-    if (customize.richerPicker_selectedSubmenu ==
-        $(customize.IDS.COLORS_BUTTON)) {
-      customize.cancelColor();
-    }
-    customize.richerPicker_closeCustomizationMenu();
+    customize.richerPicker_cancelCustomization();
   };
 
 
@@ -1561,7 +1656,7 @@
   // Interactions with the back arrow on the image selection dialog.
   const backInteraction = function(event) {
     if (configData.richerPicker) {
-      customize.richerPicker_resetImageMenu(true);
+      customize.richerPicker_resetImageMenu();
     }
     customize.resetSelectionDialog();
     customize.showCollectionSelectionDialog();
@@ -1609,22 +1704,15 @@
     if (done.disabled) {
       return;
     }
-
-    if (customize.richerPicker_selectedSubmenu ==
-        $(customize.IDS.COLORS_BUTTON)) {
-      customize.confirmColor();
-      customize.richerPicker_closeCustomizationMenu();
-    } else if (
-        customize.richerPicker_selectedSubmenu ==
-        $(customize.IDS.SHORTCUTS_BUTTON)) {
-      customize.richerPicker_setShortcutOptions();
-      customize.richerPicker_closeCustomizationMenu();
-    } else {
+    if (configData.richerPicker) {
+      customize.richerPicker_applyCustomization();
+    } else if (customize.selectedOptions.background) {
+      // Also closes the customization menu.
       customize.setBackground(
-          customize.selectedBackgroundTile.dataset.url,
-          customize.selectedBackgroundTile.dataset.attributionLine1,
-          customize.selectedBackgroundTile.dataset.attributionLine2,
-          customize.selectedBackgroundTile.dataset.attributionActionUrl);
+          customize.selectedOptions.background.dataset.url,
+          customize.selectedOptions.background.dataset.attributionLine1,
+          customize.selectedOptions.background.dataset.attributionLine2,
+          customize.selectedOptions.background.dataset.attributionActionUrl);
     }
   };
   $(customize.IDS.DONE).onclick = doneInteraction;
@@ -1692,9 +1780,11 @@
   };
 
   const richerPickerOpenBackgrounds = function() {
-    customize.richerPicker_resetCustomizationMenu();
-    customize.richerPicker_selectSubmenu(
-        $(customize.IDS.BACKGROUNDS_BUTTON), $(customize.IDS.BACKGROUNDS_MENU));
+    // Open the previously open Background submenu, if applicable.
+    customize.richerPicker_showSubmenu(
+        $(customize.IDS.BACKGROUNDS_BUTTON),
+        $(customize.richerPicker_openBackgroundSubmenu.menuId),
+        customize.richerPicker_openBackgroundSubmenu.title);
   };
 
   $(customize.IDS.BACKGROUNDS_BUTTON).onclick = richerPickerOpenBackgrounds;
@@ -1744,8 +1834,7 @@
   };
 
   const richerPickerOpenShortcuts = function() {
-    customize.richerPicker_resetCustomizationMenu();
-    customize.richerPicker_selectSubmenu(
+    customize.richerPicker_showSubmenu(
         $(customize.IDS.SHORTCUTS_BUTTON), $(customize.IDS.SHORTCUTS_MENU));
   };
 
@@ -1758,8 +1847,7 @@
   };
 
   $(customize.IDS.COLORS_BUTTON).onclick = function() {
-    customize.richerPicker_resetCustomizationMenu();
-    customize.richerPicker_selectSubmenu(
+    customize.richerPicker_showSubmenu(
         $(customize.IDS.COLORS_BUTTON), $(customize.IDS.COLORS_MENU));
     ntpApiHandle.getColorsInfo();
   };
@@ -1804,12 +1892,12 @@
     return;
   }
   // Clear the previous selection, if any.
-  if (customize.selectedColorTile) {
-    customize.richerPicker_removeSelectedState(customize.selectedColorTile);
+  if (customize.selectedOptions.color) {
+    customize.richerPicker_removeSelectedState(customize.selectedOptions.color);
   }
-  customize.selectedColorTile = tile;
+  customize.selectedOptions.color = tile;
   customize.richerPicker_applySelectedState(tile);
-  customize.richerPicker_toggleDone(true);
+  customize.richerPicker_maybeToggleDone();
 };
 
 /**
diff --git a/chrome/browser/resources/local_ntp/externs.js b/chrome/browser/resources/local_ntp/externs.js
index d6632a10..279b9dbc 100644
--- a/chrome/browser/resources/local_ntp/externs.js
+++ b/chrome/browser/resources/local_ntp/externs.js
@@ -26,16 +26,20 @@
  * The type of the config data object. The definition is based on
  * chrome/browser/search/local_ntp_source.cc:
  *     LocalNtpSource::SearchConfigurationProvider::UpdateConfigData()
- * @typedef {{translatedStrings: Array<string>,
- *            isGooglePage: boolean,
- *            googleBaseUrl: string,
- *            isAccessibleBrowser: boolean,
+ * @typedef {{alternateFakebox: boolean,
+ *            alternateFakeboxRect: boolean,
+ *            chromeColors: boolean,
  *            enableShortcutsGrid: boolean,
- *            removeFakebox: boolean,
- *            alternateFakebox: boolean,
  *            fakeboxSearchIcon: boolean,
+ *            fakeboxSearchIconColor: boolean,
+ *            googleBaseUrl: string,
  *            hideShortcuts: boolean,
- *            chromeColors: boolean}}
+ *            isAccessibleBrowser: boolean,
+ *            isGooglePage: boolean,
+ *            removeFakebox: boolean,
+ *            richerPicker: boolean,
+ *            showFakeboxPlaceholderOnFocus: boolean,
+ *            translatedStrings: Array<string>}}
  */
 let configData;
 
diff --git a/chrome/browser/resources/settings/appearance_page/appearance_fonts_page.html b/chrome/browser/resources/settings/appearance_page/appearance_fonts_page.html
index e531e1a0..f6f7a00 100644
--- a/chrome/browser/resources/settings/appearance_page/appearance_fonts_page.html
+++ b/chrome/browser/resources/settings/appearance_page/appearance_fonts_page.html
@@ -36,11 +36,13 @@
         <settings-slider  pref="{{prefs.webkit.webprefs.minimum_font_size}}"
             ticks="[[minimumFontSizeRange_]]" label-min="$i18n{tiny}"
             label-max="$i18n{huge}"></settings-slider>
-        <div id="minimumSizeSample" style="
-            font-size:[[computeMinimumFontSize_(
-                prefs.webkit.webprefs.minimum_font_size.value)]]px;
-            font-family:
-                '[[prefs.webkit.webprefs.fonts.standard.Zyyy.value]]';">
+        <div id="minimumSizeSample"
+            style="
+                font-size:[[computeMinimumFontSize_(
+                    prefs.webkit.webprefs.minimum_font_size.value)]]px;
+                font-family:
+                    '[[prefs.webkit.webprefs.fonts.standard.Zyyy.value]]';"
+            hidden>
           [[computeMinimumFontSize_(
                   prefs.webkit.webprefs.minimum_font_size.value)]]:
           $i18n{quickBrownFox}
diff --git a/chrome/browser/resources/settings/appearance_page/appearance_fonts_page.js b/chrome/browser/resources/settings/appearance_page/appearance_fonts_page.js
index e3877f9..7852e2e 100644
--- a/chrome/browser/resources/settings/appearance_page/appearance_fonts_page.js
+++ b/chrome/browser/resources/settings/appearance_page/appearance_fonts_page.js
@@ -76,6 +76,10 @@
     },
   },
 
+  observers: [
+    'onMinimumSizeChange_(prefs.webkit.webprefs.minimum_font_size.value)',
+  ],
+
   /** @private {?settings.FontsBrowserProxy} */
   browserProxy_: null,
 
@@ -136,12 +140,18 @@
 
   /**
    * Get the minimum font size, accounting for unset prefs.
-   * @return {?}
+   * @return {number}
    * @private
    */
   computeMinimumFontSize_: function() {
-    return this.get('prefs.webkit.webprefs.minimum_font_size.value') ||
-        MINIMUM_FONT_SIZE_RANGE[0];
+    const prefValue = this.get('prefs.webkit.webprefs.minimum_font_size.value');
+    return /** @type {number} */ (prefValue) || MINIMUM_FONT_SIZE_RANGE[0];
+  },
+
+
+  /** @private */
+  onMinimumSizeChange_: function() {
+    this.$.minimumSizeSample.hidden = this.computeMinimumFontSize_() <= 0;
   },
 });
 })();
diff --git a/chrome/browser/search/instant_service.cc b/chrome/browser/search/instant_service.cc
index b4a946fa..0457b1f 100644
--- a/chrome/browser/search/instant_service.cc
+++ b/chrome/browser/search/instant_service.cc
@@ -379,7 +379,7 @@
   return true;
 }
 
-bool InstantService::ToggleShortcutsVisibility() {
+bool InstantService::ToggleShortcutsVisibility(bool do_notify) {
   // Non-Google NTPs are not supported.
   if (!most_visited_sites_ || !search_provider_observer_ ||
       !search_provider_observer_->is_google()) {
@@ -389,7 +389,9 @@
   pref_service_->SetBoolean(prefs::kNtpShortcutsVisible, is_visible);
   most_visited_info_->is_visible = is_visible;
 
-  NotifyAboutMostVisitedInfo();
+  if (do_notify) {
+    NotifyAboutMostVisitedInfo();
+  }
   return true;
 }
 
diff --git a/chrome/browser/search/instant_service.h b/chrome/browser/search/instant_service.h
index 4432b63..2a73342 100644
--- a/chrome/browser/search/instant_service.h
+++ b/chrome/browser/search/instant_service.h
@@ -112,8 +112,10 @@
   // false and does nothing if the profile is using a third-party NTP.
   bool ToggleMostVisitedOrCustomLinks();
   // Invoked when the Instant page wants to toggle visibility of the tiles.
+  // Notifies observers only if |do_notify| is true, which is usually the case
+  // if |ToggleMostVisitedOrCustomLinks| will not be called immediately after.
   // Returns false and does nothing if the profile is using a third-party NTP.
-  bool ToggleShortcutsVisibility();
+  bool ToggleShortcutsVisibility(bool do_notify);
 
   // Invoked to update theme information for the NTP.
   void UpdateThemeInfo();
diff --git a/chrome/browser/search/instant_service_unittest.cc b/chrome/browser/search/instant_service_unittest.cc
index c20d5fc0..610fad2 100644
--- a/chrome/browser/search/instant_service_unittest.cc
+++ b/chrome/browser/search/instant_service_unittest.cc
@@ -112,6 +112,9 @@
 }
 
 TEST_F(InstantServiceTest, DoesToggleShortcutsVisibility) {
+  testing::StrictMock<MockInstantServiceObserver> mock_observer;
+  instant_service_->AddObserver(&mock_observer);
+
   sync_preferences::TestingPrefServiceSyncable* pref_service =
       profile()->GetTestingPrefService();
   SetUserSelectedDefaultSearchProvider("{google:baseURL}");
@@ -119,18 +122,22 @@
   ASSERT_TRUE(instant_service_->most_visited_info_->is_visible);
 
   // Hide shortcuts.
-  EXPECT_TRUE(instant_service_->ToggleShortcutsVisibility());
+  EXPECT_CALL(mock_observer, MostVisitedInfoChanged(testing::_)).Times(0);
+  EXPECT_TRUE(instant_service_->ToggleShortcutsVisibility(false));
   EXPECT_FALSE(pref_service->GetBoolean(prefs::kNtpShortcutsVisible));
   EXPECT_FALSE(instant_service_->most_visited_info_->is_visible);
+  thread_bundle()->RunUntilIdle();
 
-  // Show shortcuts.
-  EXPECT_TRUE(instant_service_->ToggleShortcutsVisibility());
+  // Show shortcuts, and check that a notification was sent.
+  EXPECT_CALL(mock_observer, MostVisitedInfoChanged(testing::_)).Times(1);
+  EXPECT_TRUE(instant_service_->ToggleShortcutsVisibility(true));
   EXPECT_TRUE(pref_service->GetBoolean(prefs::kNtpShortcutsVisible));
   EXPECT_TRUE(instant_service_->most_visited_info_->is_visible);
 
   // Should do nothing if this is a non-Google NTP.
+  EXPECT_CALL(mock_observer, MostVisitedInfoChanged(testing::_)).Times(0);
   SetUserSelectedDefaultSearchProvider("https://www.search.com");
-  EXPECT_FALSE(instant_service_->ToggleShortcutsVisibility());
+  EXPECT_FALSE(instant_service_->ToggleShortcutsVisibility(false));
   EXPECT_TRUE(pref_service->GetBoolean(prefs::kNtpShortcutsVisible));
   EXPECT_TRUE(instant_service_->most_visited_info_->is_visible);
 }
@@ -176,7 +183,8 @@
   const GURL kUrl("https://www.foo.com");
 
   instant_service_->AddValidBackdropUrlForTesting(kUrl);
-  instant_service_->SetCustomBackgroundURLWithAttributions(kUrl, std::string(), std::string(), GURL());
+  instant_service_->SetCustomBackgroundURLWithAttributions(
+      kUrl, std::string(), std::string(), GURL());
 
   ThemeBackgroundInfo* theme_info = instant_service_->GetInitializedThemeInfo();
   EXPECT_EQ(kUrl, theme_info->custom_background_url);
@@ -193,7 +201,8 @@
   ThemeBackgroundInfo* theme_info = instant_service_->GetInitializedThemeInfo();
   EXPECT_EQ(kValidUrl.spec(), theme_info->custom_background_url.spec());
 
-  instant_service_->SetCustomBackgroundURLWithAttributions(kInvalidUrl, std::string(), std::string(), GURL());
+  instant_service_->SetCustomBackgroundURLWithAttributions(
+      kInvalidUrl, std::string(), std::string(), GURL());
 
   theme_info = instant_service_->GetInitializedThemeInfo();
   EXPECT_EQ(std::string(), theme_info->custom_background_url.spec());
diff --git a/chrome/browser/site_isolation/site_per_process_interactive_browsertest.cc b/chrome/browser/site_isolation/site_per_process_interactive_browsertest.cc
index 05538d5..dacca77 100644
--- a/chrome/browser/site_isolation/site_per_process_interactive_browsertest.cc
+++ b/chrome/browser/site_isolation/site_per_process_interactive_browsertest.cc
@@ -32,6 +32,7 @@
 #include "content/public/browser/notification_registrar.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_source.h"
+#include "content/public/browser/notification_types.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_widget_host.h"
 #include "content/public/browser/render_widget_host_iterator.h"
@@ -764,6 +765,7 @@
   EXPECT_TRUE(
       ExecuteScript(grandchild, "location.href = '/fullscreen_frame.html'"));
   observer.Wait();
+  grandchild = ChildFrameAt(child, 0);
   EXPECT_EQ(embedded_test_server()->GetURL("a.com", "/fullscreen_frame.html"),
             grandchild->GetLastCommittedURL());
 
@@ -916,6 +918,7 @@
   EXPECT_TRUE(
       ExecuteScript(c_middle, "location.href = '/fullscreen_frame.html'"));
   observer.Wait();
+  c_middle = ChildFrameAt(c_top, 0);
   EXPECT_EQ(embedded_test_server()->GetURL("c.com", "/fullscreen_frame.html"),
             c_middle->GetLastCommittedURL());
   content::RenderFrameHost* c_bottom = ChildFrameAt(c_middle, 0);
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 412921d..389bd318 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -467,6 +467,7 @@
     "//components/password_manager/core/browser",
     "//components/payments/content:utils",
     "//components/payments/content/icon",
+    "//components/payments/core:error_strings",
     "//components/pdf/browser",
     "//components/policy/core/browser",
     "//components/pref_registry",
@@ -1431,6 +1432,8 @@
       "app_list/search/launcher_search/launcher_search_result.h",
       "ash/accessibility/accessibility_controller_client.cc",
       "ash/accessibility/accessibility_controller_client.h",
+      "ash/arc_chrome_actions_client.cc",
+      "ash/arc_chrome_actions_client.h",
       "ash/ash_shell_init.cc",
       "ash/ash_shell_init.h",
       "ash/ash_util.cc",
diff --git a/chrome/browser/ui/android/ssl_client_certificate_request.cc b/chrome/browser/ui/android/ssl_client_certificate_request.cc
index 9665976..9efd3b0 100644
--- a/chrome/browser/ui/android/ssl_client_certificate_request.cc
+++ b/chrome/browser/ui/android/ssl_client_certificate_request.cc
@@ -302,7 +302,9 @@
   // When we receive an OnCancel message, we remove this ClientCertRequest from
   // the queue of pending requests.
   auto should_keep = [this](auto* req) { return req != this; };
-  pending_requests_->FilterPendingRequests(should_keep);
+  if (pending_requests_) {
+    pending_requests_->FilterPendingRequests(should_keep);
+  }
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/ash/arc_chrome_actions_client.cc b/chrome/browser/ui/ash/arc_chrome_actions_client.cc
new file mode 100644
index 0000000..eb4d65b
--- /dev/null
+++ b/chrome/browser/ui/ash/arc_chrome_actions_client.cc
@@ -0,0 +1,30 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/ash/arc_chrome_actions_client.h"
+
+#include "chrome/browser/chromeos/arc/arc_util.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "components/arc/intent_helper/arc_intent_helper_bridge.h"
+#include "components/user_manager/user_manager.h"
+
+ArcChromeActionsClient::ArcChromeActionsClient() {
+  arc::ArcIntentHelperBridge::SetFactoryResetDelegate(this);
+}
+
+ArcChromeActionsClient::~ArcChromeActionsClient() {
+  arc::ArcIntentHelperBridge::SetFactoryResetDelegate(nullptr);
+}
+
+void ArcChromeActionsClient::ResetArc() {
+  const user_manager::User* user = ArcChromeActionsClient::GetArcUser();
+  Profile* profile = chromeos::ProfileHelper::Get()->GetProfileByUser(user);
+  arc::SetArcPlayStoreEnabledForProfile(profile, /*enabled=*/false);
+  arc::SetArcPlayStoreEnabledForProfile(profile, /*enabled=*/true);
+}
+
+// ARC is only running for the primary user.
+const user_manager::User* ArcChromeActionsClient::GetArcUser() {
+  return user_manager::UserManager::Get()->GetPrimaryUser();
+}
diff --git a/chrome/browser/ui/ash/arc_chrome_actions_client.h b/chrome/browser/ui/ash/arc_chrome_actions_client.h
new file mode 100644
index 0000000..1120d8ab
--- /dev/null
+++ b/chrome/browser/ui/ash/arc_chrome_actions_client.h
@@ -0,0 +1,32 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_ASH_ARC_CHROME_ACTIONS_CLIENT_H_
+#define CHROME_BROWSER_UI_ASH_ARC_CHROME_ACTIONS_CLIENT_H_
+
+#include "components/arc/intent_helper/factory_reset_delegate.h"
+
+namespace user_manager {
+class User;
+}
+
+// Allows ARC to call into browser code via ArcIntentHelperBridge, such
+// as factory resetting ARC itself from Chrome.
+// This should only for browser code not related to opening web URLs.
+// For that, please use OpenUrlDelegate.
+class ArcChromeActionsClient : public arc::FactoryResetDelegate {
+ public:
+  ArcChromeActionsClient();
+  ~ArcChromeActionsClient() override;
+
+  void ResetArc() override;
+  // Returns the primary user since it is not possible for
+  // non-primary user (e.g. secondary profile) to use ARC.
+  static const user_manager::User* GetArcUser();
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ArcChromeActionsClient);
+};
+
+#endif  // CHROME_BROWSER_UI_ASH_ARC_CHROME_ACTIONS_CLIENT_H_
diff --git a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
index ed5b9fe..371ea74 100644
--- a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
+++ b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
@@ -25,6 +25,7 @@
 #include "chrome/browser/sync/sync_error_notifier_factory_ash.h"
 #include "chrome/browser/ui/app_list/app_list_client_impl.h"
 #include "chrome/browser/ui/ash/accessibility/accessibility_controller_client.h"
+#include "chrome/browser/ui/ash/arc_chrome_actions_client.h"
 #include "chrome/browser/ui/ash/ash_shell_init.h"
 #include "chrome/browser/ui/ash/cast_config_controller_media_router.h"
 #include "chrome/browser/ui/ash/chrome_new_window_client.h"
@@ -159,6 +160,8 @@
 
   ui::SelectFileDialog::SetFactory(new SelectFileDialogExtensionFactory);
 
+  arc_chrome_actions_client_ = std::make_unique<ArcChromeActionsClient>();
+
 #if BUILDFLAG(ENABLE_WAYLAND_SERVER)
   exo_parts_ = ExoParts::CreateIfNecessary();
 #endif
diff --git a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.h b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.h
index 5dd38371..8bcdb51 100644
--- a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.h
+++ b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.h
@@ -21,6 +21,7 @@
 
 class AccessibilityControllerClient;
 class AppListClientImpl;
+class ArcChromeActionsClient;
 class AshShellInit;
 class CastConfigControllerMediaRouter;
 class ChromeNewWindowClient;
@@ -76,6 +77,7 @@
   std::unique_ptr<AccessibilityControllerClient>
       accessibility_controller_client_;
   std::unique_ptr<AppListClientImpl> app_list_client_;
+  std::unique_ptr<ArcChromeActionsClient> arc_chrome_actions_client_;
   std::unique_ptr<ChromeNewWindowClient> chrome_new_window_client_;
   std::unique_ptr<ImeControllerClient> ime_controller_client_;
   std::unique_ptr<ScreenOrientationDelegateChromeos>
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
index 48a3a5c..ceba2b3 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
@@ -487,6 +487,26 @@
   SetItemStatusOrRemove(shelf_id, GetAppState(shelf_id.app_id));
 }
 
+void ChromeLauncherController::UpdateV1AppState(const std::string& app_id) {
+  for (Browser* browser : *BrowserList::GetInstance()) {
+    if (browser->is_app() || !browser->is_type_tabbed() ||
+        !multi_user_util::IsProfileFromActiveUser(browser->profile())) {
+      continue;
+    }
+    for (int i = 0; i < browser->tab_strip_model()->count(); ++i) {
+      content::WebContents* const web_contents =
+          browser->tab_strip_model()->GetWebContentsAt(i);
+      if (launcher_controller_helper_->GetAppID(web_contents) != app_id)
+        continue;
+      UpdateAppState(web_contents, false /*remove*/);
+      if (browser->tab_strip_model()->GetActiveWebContents() == web_contents) {
+        GetBrowserShortcutLauncherItemController()
+            ->SetShelfIDForBrowserWindowContents(browser, web_contents);
+      }
+    }
+  }
+}
+
 ash::ShelfID ChromeLauncherController::GetShelfIDForWebContents(
     content::WebContents* contents) {
   std::string app_id = launcher_controller_helper_->GetAppID(contents);
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h
index 0707b86..276abb6 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h
@@ -149,6 +149,11 @@
   // the association of |contents| with an app.
   void UpdateAppState(content::WebContents* contents, bool remove);
 
+  // Updates app state for all tabs where a specific v1 app is running.
+  // This call is necessary if an app has been created for an existing
+  // web page (see IDC_CREATE_SHORTCUT).
+  void UpdateV1AppState(const std::string& app_id);
+
   // Returns ShelfID for |contents|. If |contents| is not an app or is not
   // pinned, returns the id of browser shrotcut.
   ash::ShelfID GetShelfIDForWebContents(content::WebContents* contents);
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
index d2867997..f45f664 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
@@ -4544,10 +4544,13 @@
   InitLauncherController();
 
   const char* kPinnableApp = file_manager::kFileManagerAppId;
-  const char* kUnpinnableApp = file_manager::kGalleryAppId;
+  const char* kNoPinApps[] = {file_manager::kGalleryAppId,
+                              extension_misc::kFeedbackExtensionId};
 
   EXPECT_EQ(AppListControllerDelegate::PIN_EDITABLE,
             GetPinnableForAppID(kPinnableApp, profile()));
-  EXPECT_EQ(AppListControllerDelegate::NO_PIN,
-            GetPinnableForAppID(kUnpinnableApp, profile()));
+  for (const char* id : kNoPinApps) {
+    EXPECT_EQ(AppListControllerDelegate::NO_PIN,
+              GetPinnableForAppID(id, profile()));
+  }
 }
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_util.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_util.cc
index 81c6e3a9..6549f851 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_util.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_util.cc
@@ -18,6 +18,7 @@
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "extensions/browser/extension_registry.h"
+#include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
 
 const extensions::Extension* GetExtensionForAppID(const std::string& app_id,
@@ -30,14 +31,15 @@
     const std::string& app_id,
     Profile* profile) {
   // These file manager apps have a shelf presence, but can only be launched
-  // when provided a filename to open. Pinning them creates an item that does
-  // nothing.
-  const char* kUnpinnableAppIds[] = {
+  // when provided a filename to open. Likewise, the feedback extension needs
+  // context when launching. Pinning these creates an item that does nothing.
+  const char* kNoPinAppIds[] = {
       file_manager::kVideoPlayerAppId,
       file_manager::kGalleryAppId,
       file_manager::kAudioPlayerAppId,
+      extension_misc::kFeedbackExtensionId,
   };
-  if (base::Contains(kUnpinnableAppIds, app_id))
+  if (base::Contains(kNoPinAppIds, app_id))
     return AppListControllerDelegate::NO_PIN;
 
   const base::ListValue* pref =
diff --git a/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc b/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
index eec89eba..7df5f3b 100644
--- a/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
+++ b/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
@@ -526,9 +526,9 @@
   TemplateURLService* service =
       TemplateURLServiceFactory::GetForProfile(browser()->profile());
   search_test_utils::WaitForTemplateURLServiceToLoad(service);
-  LocationBar* location_bar = browser()->window()->GetLocationBar();
-  ui_test_utils::SendToOmniboxAndSubmit(location_bar, search_string);
-  OmniboxEditModel* model = location_bar->GetOmniboxView()->model();
+  ui_test_utils::SendToOmniboxAndSubmit(browser(), search_string);
+  OmniboxEditModel* model =
+      browser()->window()->GetLocationBar()->GetOmniboxView()->model();
   EXPECT_EQ(GURL(search_string), model->CurrentMatch(nullptr).destination_url);
   EXPECT_EQ(base::ASCIIToUTF16(search_string),
             model->CurrentMatch(nullptr).contents);
diff --git a/chrome/browser/ui/exclusive_access/fullscreen_controller_interactive_browsertest.cc b/chrome/browser/ui/exclusive_access/fullscreen_controller_interactive_browsertest.cc
index 642dc9d..fea598b 100644
--- a/chrome/browser/ui/exclusive_access/fullscreen_controller_interactive_browsertest.cc
+++ b/chrome/browser/ui/exclusive_access/fullscreen_controller_interactive_browsertest.cc
@@ -64,6 +64,17 @@
     return browser()->IsMouseLocked();
   }
 
+  void PressKeyAndWaitForMouseLockRequest(ui::KeyboardCode key_code) {
+    base::RunLoop run_loop;
+    browser()
+        ->exclusive_access_manager()
+        ->mouse_lock_controller()
+        ->set_lock_state_callback_for_test(run_loop.QuitClosure());
+    ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), key_code, false,
+                                                false, false, false));
+    run_loop.Run();
+  }
+
  private:
    void ToggleTabFullscreen_Internal(bool enter_fullscreen,
                                      bool retry_until_success);
@@ -299,12 +310,7 @@
   ASSERT_FALSE(IsFullscreenBubbleDisplayed());
 
   // Request to lock the mouse.
-  {
-    ASSERT_TRUE(ui_test_utils::SendKeyPressAndWait(
-        browser(), ui::VKEY_1, false, false, false, false,
-        chrome::NOTIFICATION_MOUSE_LOCK_CHANGED,
-        content::NotificationService::AllSources()));
-  }
+  PressKeyAndWaitForMouseLockRequest(ui::VKEY_1);
 
   ASSERT_TRUE(IsMouseLocked());
   ASSERT_FALSE(IsWindowFullscreenForTabOrPending());
@@ -329,10 +335,7 @@
   // Request to lock the mouse and enter fullscreen.
   {
     FullscreenNotificationObserver fullscreen_observer(browser());
-    ASSERT_TRUE(ui_test_utils::SendKeyPressAndWait(
-        browser(), ui::VKEY_B, false, true, false, false,
-        chrome::NOTIFICATION_MOUSE_LOCK_CHANGED,
-        content::NotificationService::AllSources()));
+    PressKeyAndWaitForMouseLockRequest(ui::VKEY_B);
     fullscreen_observer.Wait();
   }
 
@@ -361,18 +364,12 @@
   ASSERT_FALSE(IsFullscreenBubbleDisplayed());
 
   // Lock the mouse without a user gesture, expect no response.
-  ASSERT_TRUE(ui_test_utils::SendKeyPressAndWait(
-      browser(), ui::VKEY_D, false, false, false, false,
-      chrome::NOTIFICATION_MOUSE_LOCK_CHANGED,
-      content::NotificationService::AllSources()));
+  PressKeyAndWaitForMouseLockRequest(ui::VKEY_D);
   ASSERT_FALSE(IsFullscreenBubbleDisplayed());
   ASSERT_FALSE(IsMouseLocked());
 
   // Lock the mouse with a user gesture.
-  ASSERT_TRUE(ui_test_utils::SendKeyPressAndWait(
-      browser(), ui::VKEY_1, false, false, false, false,
-      chrome::NOTIFICATION_MOUSE_LOCK_CHANGED,
-      content::NotificationService::AllSources()));
+  PressKeyAndWaitForMouseLockRequest(ui::VKEY_1);
   ASSERT_TRUE(IsFullscreenBubbleDisplayed());
   ASSERT_TRUE(IsMouseLocked());
 
@@ -402,10 +399,7 @@
   // Request to lock the mouse and enter fullscreen.
   {
     FullscreenNotificationObserver fullscreen_observer(browser());
-    ASSERT_TRUE(ui_test_utils::SendKeyPressAndWait(
-        browser(), ui::VKEY_B, false, true, false, false,
-        chrome::NOTIFICATION_MOUSE_LOCK_CHANGED,
-        content::NotificationService::AllSources()));
+    PressKeyAndWaitForMouseLockRequest(ui::VKEY_B);
     fullscreen_observer.Wait();
   }
   ASSERT_TRUE(IsFullscreenBubbleDisplayed());
@@ -428,10 +422,7 @@
 
   // Request to lock the mouse and enter fullscreen.
   FullscreenNotificationObserver fullscreen_observer(browser());
-  ASSERT_TRUE(ui_test_utils::SendKeyPressAndWait(
-      browser(), ui::VKEY_B, false, true, false, false,
-      chrome::NOTIFICATION_MOUSE_LOCK_CHANGED,
-      content::NotificationService::AllSources()));
+  PressKeyAndWaitForMouseLockRequest(ui::VKEY_B);
   fullscreen_observer.Wait();
 
   // Confirm they are enabled and there is no prompt.
@@ -456,57 +447,36 @@
   ASSERT_FALSE(IsFullscreenBubbleDisplayed());
 
   // Lock the mouse with a user gesture.
-  ASSERT_TRUE(ui_test_utils::SendKeyPressAndWait(
-      browser(), ui::VKEY_1, false, false, false, false,
-      chrome::NOTIFICATION_MOUSE_LOCK_CHANGED,
-      content::NotificationService::AllSources()));
+  PressKeyAndWaitForMouseLockRequest(ui::VKEY_1);
   ASSERT_TRUE(IsFullscreenBubbleDisplayed());
   ASSERT_TRUE(IsMouseLocked());
   ASSERT_TRUE(IsFullscreenBubbleDisplayed());
 
   // Unlock the mouse from target, make sure it's unlocked.
-  ASSERT_TRUE(ui_test_utils::SendKeyPressAndWait(
-        browser(), ui::VKEY_U, false, false, false, false,
-        chrome::NOTIFICATION_MOUSE_LOCK_CHANGED,
-        content::NotificationService::AllSources()));
+  PressKeyAndWaitForMouseLockRequest(ui::VKEY_U);
   ASSERT_FALSE(IsMouseLocked());
   ASSERT_FALSE(IsFullscreenBubbleDisplayed());
 
   // Lock mouse again, make sure it works with no bubble.
-  ASSERT_TRUE(ui_test_utils::SendKeyPressAndWait(
-        browser(), ui::VKEY_1, false, false, false, false,
-        chrome::NOTIFICATION_MOUSE_LOCK_CHANGED,
-        content::NotificationService::AllSources()));
+  PressKeyAndWaitForMouseLockRequest(ui::VKEY_1);
   ASSERT_TRUE(IsMouseLocked());
   ASSERT_FALSE(IsFullscreenBubbleDisplayed());
 
   // Unlock the mouse again by target.
-  ASSERT_TRUE(ui_test_utils::SendKeyPressAndWait(
-        browser(), ui::VKEY_U, false, false, false, false,
-        chrome::NOTIFICATION_MOUSE_LOCK_CHANGED,
-        content::NotificationService::AllSources()));
+  PressKeyAndWaitForMouseLockRequest(ui::VKEY_U);
   ASSERT_FALSE(IsMouseLocked());
 
   // Lock from target, not user gesture, make sure it works.
-  ASSERT_TRUE(ui_test_utils::SendKeyPressAndWait(
-        browser(), ui::VKEY_D, false, false, false, false,
-        chrome::NOTIFICATION_MOUSE_LOCK_CHANGED,
-        content::NotificationService::AllSources()));
+  PressKeyAndWaitForMouseLockRequest(ui::VKEY_D);
   ASSERT_TRUE(IsMouseLocked());
   ASSERT_FALSE(IsFullscreenBubbleDisplayed());
 
   // Unlock by escape.
-  ASSERT_TRUE(ui_test_utils::SendKeyPressAndWait(
-          browser(), ui::VKEY_ESCAPE, false, false, false, false,
-          chrome::NOTIFICATION_MOUSE_LOCK_CHANGED,
-          content::NotificationService::AllSources()));
+  PressKeyAndWaitForMouseLockRequest(ui::VKEY_ESCAPE);
   ASSERT_FALSE(IsMouseLocked());
 
   // Lock the mouse with a user gesture, make sure we see bubble again.
-  ASSERT_TRUE(ui_test_utils::SendKeyPressAndWait(
-      browser(), ui::VKEY_1, false, false, false, false,
-      chrome::NOTIFICATION_MOUSE_LOCK_CHANGED,
-      content::NotificationService::AllSources()));
+  PressKeyAndWaitForMouseLockRequest(ui::VKEY_1);
   ASSERT_TRUE(IsFullscreenBubbleDisplayed());
   ASSERT_TRUE(IsMouseLocked());
 }
@@ -533,10 +503,7 @@
       browser(), embedded_test_server()->GetURL(kFullscreenMouseLockHTML));
 
   // Lock the mouse with a user gesture.
-  ASSERT_TRUE(ui_test_utils::SendKeyPressAndWait(
-      browser(), ui::VKEY_1, false, false, false, false,
-      chrome::NOTIFICATION_MOUSE_LOCK_CHANGED,
-      content::NotificationService::AllSources()));
+  PressKeyAndWaitForMouseLockRequest(ui::VKEY_1);
   ASSERT_TRUE(IsFullscreenBubbleDisplayed());
 
   ASSERT_TRUE(IsMouseLocked());
@@ -557,10 +524,7 @@
       browser(), embedded_test_server()->GetURL(kFullscreenMouseLockHTML));
 
   // Lock the mouse with a user gesture.
-  ASSERT_TRUE(ui_test_utils::SendKeyPressAndWait(
-      browser(), ui::VKEY_1, false, false, false, false,
-      chrome::NOTIFICATION_MOUSE_LOCK_CHANGED,
-      content::NotificationService::AllSources()));
+  PressKeyAndWaitForMouseLockRequest(ui::VKEY_1);
   ASSERT_TRUE(IsFullscreenBubbleDisplayed());
 
   ASSERT_TRUE(IsMouseLocked());
@@ -592,10 +556,7 @@
   ui_test_utils::NavigateToURL(browser(), url);
 
   // Lock the mouse with a user gesture.
-  ASSERT_TRUE(ui_test_utils::SendKeyPressAndWait(
-      browser(), ui::VKEY_1, false, false, false, false,
-      chrome::NOTIFICATION_MOUSE_LOCK_CHANGED,
-      content::NotificationService::AllSources()));
+  PressKeyAndWaitForMouseLockRequest(ui::VKEY_1);
   ASSERT_TRUE(IsFullscreenBubbleDisplayed());
 
   ASSERT_TRUE(IsMouseLocked());
@@ -622,28 +583,26 @@
       browser(), embedded_test_server()->GetURL(kFullscreenMouseLockHTML));
 
   // Request mouse lock.
-  ASSERT_TRUE(ui_test_utils::SendKeyPressAndWait(
-      browser(), ui::VKEY_1, false, false, false, false,
-      chrome::NOTIFICATION_MOUSE_LOCK_CHANGED,
-      content::NotificationService::AllSources()));
+  PressKeyAndWaitForMouseLockRequest(ui::VKEY_1);
 
   ASSERT_TRUE(IsMouseLocked());
   ASSERT_TRUE(IsFullscreenBubbleDisplayed());
 
   // Reload. Mouse lock request should be cleared.
   {
-    MouseLockNotificationObserver mouselock_observer;
+    base::RunLoop run_loop;
+    browser()
+        ->exclusive_access_manager()
+        ->mouse_lock_controller()
+        ->set_lock_state_callback_for_test(run_loop.QuitClosure());
     Reload();
-    mouselock_observer.Wait();
+    run_loop.Run();
   }
 
   // Request to lock the mouse and enter fullscreen.
   {
     FullscreenNotificationObserver fullscreen_observer(browser());
-    ASSERT_TRUE(ui_test_utils::SendKeyPressAndWait(
-        browser(), ui::VKEY_B, false, true, false, false,
-        chrome::NOTIFICATION_MOUSE_LOCK_CHANGED,
-        content::NotificationService::AllSources()));
+    PressKeyAndWaitForMouseLockRequest(ui::VKEY_B);
     fullscreen_observer.Wait();
   }
 
diff --git a/chrome/browser/ui/exclusive_access/fullscreen_controller_test.h b/chrome/browser/ui/exclusive_access/fullscreen_controller_test.h
index e7de20b..2042279 100644
--- a/chrome/browser/ui/exclusive_access/fullscreen_controller_test.h
+++ b/chrome/browser/ui/exclusive_access/fullscreen_controller_test.h
@@ -14,13 +14,11 @@
 #include "base/scoped_observer.h"
 #include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
-#include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/ui/exclusive_access/exclusive_access_bubble_hide_callback.h"
 #include "chrome/browser/ui/exclusive_access/exclusive_access_bubble_type.h"
 #include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
 #include "chrome/browser/ui/exclusive_access/fullscreen_observer.h"
 #include "chrome/test/base/in_process_browser_test.h"
-#include "content/public/browser/notification_service.h"
 #include "content/public/test/test_utils.h"
 
 #if defined(OS_MACOSX)
@@ -54,17 +52,6 @@
   DISALLOW_COPY_AND_ASSIGN(FullscreenNotificationObserver);
 };
 
-// Observer for NOTIFICATION_MOUSE_LOCK_CHANGED notifications.
-class MouseLockNotificationObserver
-    : public content::WindowedNotificationObserver {
- public:
-  MouseLockNotificationObserver() : WindowedNotificationObserver(
-      chrome::NOTIFICATION_MOUSE_LOCK_CHANGED,
-      content::NotificationService::AllSources()) {}
- protected:
-  DISALLOW_COPY_AND_ASSIGN(MouseLockNotificationObserver);
-};
-
 // Test fixture with convenience functions for fullscreen, keyboard lock, and
 // mouse lock.
 class FullscreenControllerTest : public InProcessBrowserTest {
diff --git a/chrome/browser/ui/exclusive_access/mouse_lock_controller.cc b/chrome/browser/ui/exclusive_access/mouse_lock_controller.cc
index 9eb606fd..deb2779 100644
--- a/chrome/browser/ui/exclusive_access/mouse_lock_controller.cc
+++ b/chrome/browser/ui/exclusive_access/mouse_lock_controller.cc
@@ -6,7 +6,6 @@
 
 #include "base/bind.h"
 #include "base/metrics/histogram_macros.h"
-#include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
@@ -14,7 +13,6 @@
 #include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h"
 #include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
-#include "content/public/browser/notification_service.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/render_widget_host.h"
 #include "content/public/browser/render_widget_host_view.h"
@@ -53,7 +51,8 @@
                                              bool user_gesture,
                                              bool last_unlocked_by_target) {
   DCHECK(!IsMouseLocked());
-  NotifyMouseLockChange();
+  if (lock_state_callback_for_test_)
+    std::move(lock_state_callback_for_test_).Run();
 
   // Must have a user gesture to prevent misbehaving sites from constantly
   // re-locking the mouse. Exceptions are when the page has unlocked
@@ -122,21 +121,16 @@
 }
 
 void MouseLockController::LostMouseLock() {
+  if (lock_state_callback_for_test_)
+    std::move(lock_state_callback_for_test_).Run();
+
   RecordExitingUMA();
   mouse_lock_state_ = MOUSELOCK_UNLOCKED;
   SetTabWithExclusiveAccess(nullptr);
-  NotifyMouseLockChange();
   exclusive_access_manager()->UpdateExclusiveAccessExitBubbleContent(
       ExclusiveAccessBubbleHideCallback());
 }
 
-void MouseLockController::NotifyMouseLockChange() {
-  content::NotificationService::current()->Notify(
-      chrome::NOTIFICATION_MOUSE_LOCK_CHANGED,
-      content::Source<MouseLockController>(this),
-      content::NotificationService::NoDetails());
-}
-
 void MouseLockController::UnlockMouse() {
   WebContents* tab = exclusive_access_tab();
 
diff --git a/chrome/browser/ui/exclusive_access/mouse_lock_controller.h b/chrome/browser/ui/exclusive_access/mouse_lock_controller.h
index 70db0a9..429a0bcf 100644
--- a/chrome/browser/ui/exclusive_access/mouse_lock_controller.h
+++ b/chrome/browser/ui/exclusive_access/mouse_lock_controller.h
@@ -64,6 +64,10 @@
     bubble_hide_callback_for_test_ = std::move(callback_for_test);
   }
 
+  void set_lock_state_callback_for_test(base::OnceClosure callback) {
+    lock_state_callback_for_test_ = std::move(callback);
+  }
+
  private:
   enum MouseLockState {
     MOUSELOCK_UNLOCKED,
@@ -73,8 +77,6 @@
     MOUSELOCK_LOCKED_SILENTLY
   };
 
-  void NotifyMouseLockChange();
-
   void ExitExclusiveAccessIfNecessary() override;
   void NotifyTabExclusiveAccessLost() override;
   void RecordBubbleReshowsHistogram(int bubble_reshow_count) override;
@@ -93,6 +95,9 @@
   bool fake_mouse_lock_for_test_;
   ExclusiveAccessBubbleHideCallbackForTest bubble_hide_callback_for_test_;
 
+  // Called when the page requests (successfully or not) or loses mouse lock.
+  base::OnceClosure lock_state_callback_for_test_;
+
   base::WeakPtrFactory<MouseLockController> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(MouseLockController);
diff --git a/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc b/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
index f82a7a8..e6aa603 100644
--- a/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
+++ b/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
@@ -225,29 +225,25 @@
     observer.WaitForNavigationFinished();
   }
 
-  void WaitForTabOpenOrCloseForBrowser(const Browser* browser,
-                                       int expected_tab_count) {
-    int tab_count = browser->tab_strip_model()->count();
+  void WaitForTabOpenOrClose(int expected_tab_count) {
+    int tab_count = browser()->tab_strip_model()->count();
     if (tab_count == expected_tab_count)
       return;
 
     content::NotificationRegistrar registrar;
-    registrar.Add(this,
-        (tab_count < expected_tab_count) ?
-            static_cast<int>(chrome::NOTIFICATION_TAB_PARENTED) :
-            static_cast<int>(content::NOTIFICATION_WEB_CONTENTS_DESTROYED),
+    registrar.Add(
+        this,
+        (tab_count < expected_tab_count)
+            ? static_cast<int>(chrome::NOTIFICATION_TAB_PARENTED)
+            : static_cast<int>(content::NOTIFICATION_WEB_CONTENTS_DESTROYED),
         content::NotificationService::AllSources());
 
     while (!HasFailure() &&
-           browser->tab_strip_model()->count() != expected_tab_count) {
+           browser()->tab_strip_model()->count() != expected_tab_count) {
       content::RunMessageLoop();
     }
 
-    ASSERT_EQ(expected_tab_count, browser->tab_strip_model()->count());
-  }
-
-  void WaitForTabOpenOrClose(int expected_tab_count) {
-    WaitForTabOpenOrCloseForBrowser(browser(), expected_tab_count);
+    ASSERT_EQ(expected_tab_count, browser()->tab_strip_model()->count());
   }
 
   void WaitForAutocompleteControllerDone() {
@@ -261,14 +257,7 @@
     if (controller->done())
       return;
 
-    content::NotificationRegistrar registrar;
-    registrar.Add(this,
-                  chrome::NOTIFICATION_AUTOCOMPLETE_CONTROLLER_RESULT_READY,
-                  content::Source<AutocompleteController>(controller));
-
-    while (!HasFailure() && !controller->done())
-      content::RunMessageLoop();
-
+    ui_test_utils::WaitForAutocompleteDone(browser());
     ASSERT_TRUE(controller->done());
   }
 
@@ -385,12 +374,10 @@
     switch (type) {
       case content::NOTIFICATION_WEB_CONTENTS_DESTROYED:
       case chrome::NOTIFICATION_TAB_PARENTED:
-      case chrome::NOTIFICATION_AUTOCOMPLETE_CONTROLLER_RESULT_READY:
         break;
       default:
         FAIL() << "Unexpected notification type";
     }
-    base::RunLoop::QuitCurrentWhenIdleDeprecated();
   }
 
   policy::MockConfigurationPolicyProvider* policy_provider() {
diff --git a/chrome/browser/ui/profile_error_dialog.cc b/chrome/browser/ui/profile_error_dialog.cc
index 453bce9..ff6d152b 100644
--- a/chrome/browser/ui/profile_error_dialog.cc
+++ b/chrome/browser/ui/profile_error_dialog.cc
@@ -17,7 +17,7 @@
 
 namespace {
 
-#if !defined(OS_ANDROID)
+#if !defined(OS_ANDROID) && defined(GOOGLE_CHROME_BUILD)
 constexpr char kProfileErrorFeedbackCategory[] = "FEEDBACK_PROFILE_ERROR";
 
 bool g_is_showing_profile_error_dialog = false;
@@ -37,7 +37,7 @@
                            std::string() /* description_placeholder_text */,
                            kProfileErrorFeedbackCategory, diagnostics);
 }
-#endif  // !defined(OS_ANDROID)
+#endif  // !defined(OS_ANDROID) && defined(GOOGLE_CHROME_BUILD)
 
 }  // namespace
 
@@ -46,7 +46,7 @@
                             const std::string& diagnostics) {
 #if defined(OS_ANDROID)
   NOTIMPLEMENTED();
-#else
+#else  // defined(OS_ANDROID)
   UMA_HISTOGRAM_ENUMERATION("Profile.ProfileError", static_cast<int>(type),
                             static_cast<int>(ProfileErrorType::END));
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
@@ -54,6 +54,7 @@
     return;
   }
 
+#if defined(GOOGLE_CHROME_BUILD)
   if (g_is_showing_profile_error_dialog)
     return;
 
@@ -63,5 +64,11 @@
       l10n_util::GetStringUTF16(message_id),
       l10n_util::GetStringUTF16(IDS_PROFILE_ERROR_DIALOG_CHECKBOX),
       base::Bind(&OnProfileErrorDialogDismissed, diagnostics));
-#endif
+#else   // defined(GOOGLE_CHROME_BUILD)
+  chrome::ShowWarningMessageBox(
+      nullptr, l10n_util::GetStringUTF16(IDS_PROFILE_ERROR_DIALOG_TITLE),
+      l10n_util::GetStringUTF16(message_id));
+#endif  // !defined(GOOGLE_CHROME_BUILD)
+
+#endif  // !defined(OS_ANDROID)
 }
diff --git a/chrome/browser/ui/search/local_ntp_browsertest.cc b/chrome/browser/ui/search/local_ntp_browsertest.cc
index c30d1628..14bbee3 100644
--- a/chrome/browser/ui/search/local_ntp_browsertest.cc
+++ b/chrome/browser/ui/search/local_ntp_browsertest.cc
@@ -743,7 +743,8 @@
   // Hide the shortcuts.
   local_ntp_test_utils::ExecuteScriptOnNTPAndWaitUntilLoaded(
       iframe,
-      "window.chrome.embeddedSearch.newTabPage.toggleShortcutsVisibility()");
+      "window.chrome.embeddedSearch.newTabPage.toggleShortcutsVisibility("
+      "true)");
 
   // Check that the shortcuts are hidden.
   result = false;
@@ -759,7 +760,8 @@
   // Show the shortcuts.
   local_ntp_test_utils::ExecuteScriptOnNTPAndWaitUntilLoaded(
       iframe,
-      "window.chrome.embeddedSearch.newTabPage.toggleShortcutsVisibility()");
+      "window.chrome.embeddedSearch.newTabPage.toggleShortcutsVisibility("
+      "true)");
 
   // Check that the shortcuts are visible.
   result = false;
@@ -773,6 +775,101 @@
   EXPECT_TRUE(result);
 }
 
+IN_PROC_BROWSER_TEST_F(LocalNTPTest, ToggleShortcutVisibilityAndType) {
+  content::WebContents* active_tab =
+      local_ntp_test_utils::OpenNewTab(browser(), GURL("about:blank"));
+
+  TestInstantServiceObserver observer(
+      InstantServiceFactory::GetForProfile(browser()->profile()));
+
+  local_ntp_test_utils::NavigateToNTPAndWaitUntilLoaded(browser());
+  observer.WaitForMostVisitedItems(kDefaultMostVisitedItemCount);
+
+  // Initialize custom links by adding a shortcut.
+  content::RenderFrameHost* iframe = GetIframe(active_tab, kMostVisitedIframe);
+  local_ntp_test_utils::ExecuteScriptOnNTPAndWaitUntilLoaded(
+      iframe,
+      "window.chrome.embeddedSearch.newTabPage.updateCustomLink(-1, "
+      "'https://1.com', 'Title1')");
+  // Confirm that there are the correct number of custom link tiles.
+  observer.WaitForMostVisitedItems(kDefaultMostVisitedItemCount + 1);
+
+  // Check that the shortcuts are visible.
+  bool result = false;
+  ASSERT_TRUE(instant_test_utils::GetBoolFromJS(
+      iframe, "window.chrome.embeddedSearch.newTabPage.areShortcutsVisible",
+      &result));
+  ASSERT_TRUE(result);
+  result = false;
+  ASSERT_TRUE(instant_test_utils::GetBoolFromJS(
+      iframe, "!document.body.classList.contains('hide')", &result));
+  ASSERT_TRUE(result);
+
+  // Hide the shortcuts and immediately enable Most Visited sites. The
+  // successive calls should not interfere with each other, and only a single
+  // update should be sent.
+  local_ntp_test_utils::ExecuteScriptOnNTPAndWaitUntilLoaded(
+      iframe,
+      "window.chrome.embeddedSearch.newTabPage.toggleShortcutsVisibility("
+      "false); "
+      "window.chrome.embeddedSearch.newTabPage.toggleMostVisitedOrCustomLinks("
+      ")");
+  // Confirm that there are the correct number of Most Visited tiles.
+  observer.WaitForMostVisitedItems(kDefaultMostVisitedItemCount);
+
+  // Check that the tiles have updated properly, i.e. the tiles should not have
+  // an edit menu nor be visible.
+  result = false;
+  ASSERT_TRUE(instant_test_utils::GetBoolFromJS(
+      iframe, "window.chrome.embeddedSearch.newTabPage.isUsingMostVisited",
+      &result));
+  EXPECT_TRUE(result);
+  result = false;
+  ASSERT_TRUE(instant_test_utils::GetBoolFromJS(
+      iframe, "!document.querySelector('#mv-tiles .md-edit-menu')", &result));
+  EXPECT_TRUE(result);
+  result = false;
+  ASSERT_TRUE(instant_test_utils::GetBoolFromJS(
+      iframe, "!window.chrome.embeddedSearch.newTabPage.areShortcutsVisible",
+      &result));
+  EXPECT_TRUE(result);
+  result = false;
+  ASSERT_TRUE(instant_test_utils::GetBoolFromJS(
+      iframe, "document.body.classList.contains('hide')", &result));
+  EXPECT_TRUE(result);
+
+  // Show the shortcuts and immediately enable custom links.
+  local_ntp_test_utils::ExecuteScriptOnNTPAndWaitUntilLoaded(
+      iframe,
+      "window.chrome.embeddedSearch.newTabPage.toggleShortcutsVisibility("
+      "false); "
+      "window.chrome.embeddedSearch.newTabPage.toggleMostVisitedOrCustomLinks("
+      ")");
+  // Confirm that there are the correct number of custom link tiles.
+  observer.WaitForMostVisitedItems(kDefaultMostVisitedItemCount + 1);
+
+  // Check that the tiles have updated properly, i.e. the tiles should have an
+  // edit menu and be visible.
+  result = false;
+  ASSERT_TRUE(instant_test_utils::GetBoolFromJS(
+      iframe, "!window.chrome.embeddedSearch.newTabPage.isUsingMostVisited",
+      &result));
+  EXPECT_TRUE(result);
+  result = false;
+  ASSERT_TRUE(instant_test_utils::GetBoolFromJS(
+      iframe, "!!document.querySelector('#mv-tiles .md-edit-menu')", &result));
+  EXPECT_TRUE(result);
+  result = false;
+  ASSERT_TRUE(instant_test_utils::GetBoolFromJS(
+      iframe, "window.chrome.embeddedSearch.newTabPage.areShortcutsVisible",
+      &result));
+  EXPECT_TRUE(result);
+  result = false;
+  ASSERT_TRUE(instant_test_utils::GetBoolFromJS(
+      iframe, "!document.body.classList.contains('hide')", &result));
+  EXPECT_TRUE(result);
+}
+
 class LocalNTPRTLTest : public LocalNTPTest {
  public:
   LocalNTPRTLTest() {}
diff --git a/chrome/browser/ui/search/search_ipc_router.cc b/chrome/browser/ui/search/search_ipc_router.cc
index 612d85b3..98ebd92 100644
--- a/chrome/browser/ui/search/search_ipc_router.cc
+++ b/chrome/browser/ui/search/search_ipc_router.cc
@@ -268,14 +268,15 @@
   delegate_->OnToggleMostVisitedOrCustomLinks();
 }
 
-void SearchIPCRouter::ToggleShortcutsVisibility(int page_seq_no) {
+void SearchIPCRouter::ToggleShortcutsVisibility(int page_seq_no,
+                                                bool do_notify) {
   if (page_seq_no != commit_counter_)
     return;
 
   if (!policy_->ShouldProcessToggleShortcutsVisibility())
     return;
 
-  delegate_->OnToggleShortcutsVisibility();
+  delegate_->OnToggleShortcutsVisibility(do_notify);
 }
 
 void SearchIPCRouter::LogEvent(int page_seq_no,
diff --git a/chrome/browser/ui/search/search_ipc_router.h b/chrome/browser/ui/search/search_ipc_router.h
index 9d807d2..1b4c0014 100644
--- a/chrome/browser/ui/search/search_ipc_router.h
+++ b/chrome/browser/ui/search/search_ipc_router.h
@@ -82,7 +82,7 @@
 
     // Called when the EmbeddedSearch wants to toggle visibility of the
     // shortcuts.
-    virtual void OnToggleShortcutsVisibility() = 0;
+    virtual void OnToggleShortcutsVisibility(bool do_notify) = 0;
 
     // Called to signal that an event has occurred on the New Tab Page at a
     // particular time since navigation start.
@@ -255,7 +255,7 @@
   void UndoCustomLinkAction(int page_seq_no) override;
   void ResetCustomLinks(int page_seq_no) override;
   void ToggleMostVisitedOrCustomLinks(int page_seq_no) override;
-  void ToggleShortcutsVisibility(int page_seq_no) override;
+  void ToggleShortcutsVisibility(int page_seq_no, bool do_notify) override;
   void LogEvent(int page_seq_no,
                 NTPLoggingEventType event,
                 base::TimeDelta time) override;
diff --git a/chrome/browser/ui/search/search_ipc_router_unittest.cc b/chrome/browser/ui/search/search_ipc_router_unittest.cc
index a337c5d..674b12a 100644
--- a/chrome/browser/ui/search/search_ipc_router_unittest.cc
+++ b/chrome/browser/ui/search/search_ipc_router_unittest.cc
@@ -72,7 +72,7 @@
   MOCK_METHOD0(OnUndoCustomLinkAction, void());
   MOCK_METHOD0(OnResetCustomLinks, void());
   MOCK_METHOD0(OnToggleMostVisitedOrCustomLinks, void());
-  MOCK_METHOD0(OnToggleShortcutsVisibility, void());
+  MOCK_METHOD1(OnToggleShortcutsVisibility, void(bool do_notify));
   MOCK_METHOD2(OnLogEvent, void(NTPLoggingEventType event,
                                 base::TimeDelta time));
   MOCK_METHOD3(OnLogSuggestionEventWithValue,
@@ -687,24 +687,26 @@
   NavigateAndCommitActiveTab(GURL("chrome-search://foo/bar"));
   SetupMockDelegateAndPolicy();
   MockSearchIPCRouterPolicy* policy = GetSearchIPCRouterPolicy();
-  EXPECT_CALL(*mock_delegate(), OnToggleShortcutsVisibility()).Times(1);
+  EXPECT_CALL(*mock_delegate(), OnToggleShortcutsVisibility(false)).Times(1);
   EXPECT_CALL(*policy, ShouldProcessToggleShortcutsVisibility())
       .Times(1)
       .WillOnce(Return(true));
 
-  GetSearchIPCRouter().ToggleShortcutsVisibility(GetSearchIPCRouterSeqNo());
+  GetSearchIPCRouter().ToggleShortcutsVisibility(GetSearchIPCRouterSeqNo(),
+                                                 false);
 }
 
 TEST_F(SearchIPCRouterTest, IgnoreToggleShortcutsVisibility) {
   NavigateAndCommitActiveTab(GURL("chrome-search://foo/bar"));
   SetupMockDelegateAndPolicy();
   MockSearchIPCRouterPolicy* policy = GetSearchIPCRouterPolicy();
-  EXPECT_CALL(*mock_delegate(), OnToggleShortcutsVisibility()).Times(0);
+  EXPECT_CALL(*mock_delegate(), OnToggleShortcutsVisibility(true)).Times(0);
   EXPECT_CALL(*policy, ShouldProcessToggleShortcutsVisibility())
       .Times(1)
       .WillOnce(Return(false));
 
-  GetSearchIPCRouter().ToggleShortcutsVisibility(GetSearchIPCRouterSeqNo());
+  GetSearchIPCRouter().ToggleShortcutsVisibility(GetSearchIPCRouterSeqNo(),
+                                                 true);
 }
 
 TEST_F(SearchIPCRouterTest, ProcessPasteAndOpenDropdownMsg) {
diff --git a/chrome/browser/ui/search/search_tab_helper.cc b/chrome/browser/ui/search/search_tab_helper.cc
index 03d7e58..d0c64b7b 100644
--- a/chrome/browser/ui/search/search_tab_helper.cc
+++ b/chrome/browser/ui/search/search_tab_helper.cc
@@ -323,9 +323,9 @@
     instant_service_->ToggleMostVisitedOrCustomLinks();
 }
 
-void SearchTabHelper::OnToggleShortcutsVisibility() {
+void SearchTabHelper::OnToggleShortcutsVisibility(bool do_notify) {
   if (instant_service_)
-    instant_service_->ToggleShortcutsVisibility();
+    instant_service_->ToggleShortcutsVisibility(do_notify);
 }
 
 void SearchTabHelper::OnLogEvent(NTPLoggingEventType event,
diff --git a/chrome/browser/ui/search/search_tab_helper.h b/chrome/browser/ui/search/search_tab_helper.h
index ab0191c..fafc41c 100644
--- a/chrome/browser/ui/search/search_tab_helper.h
+++ b/chrome/browser/ui/search/search_tab_helper.h
@@ -101,7 +101,7 @@
   void OnUndoCustomLinkAction() override;
   void OnResetCustomLinks() override;
   void OnToggleMostVisitedOrCustomLinks() override;
-  void OnToggleShortcutsVisibility() override;
+  void OnToggleShortcutsVisibility(bool do_notify) override;
   void OnLogEvent(NTPLoggingEventType event, base::TimeDelta time) override;
   void OnLogSuggestionEventWithValue(NTPSuggestionsLoggingEventType event,
                                      int data,
diff --git a/chrome/browser/ui/startup/startup_browser_creator.cc b/chrome/browser/ui/startup/startup_browser_creator.cc
index 04467bf..f798328 100644
--- a/chrome/browser/ui/startup/startup_browser_creator.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator.cc
@@ -479,9 +479,6 @@
 // static
 void StartupBrowserCreator::RegisterLocalStatePrefs(
     PrefRegistrySimple* registry) {
-#if defined(OS_WIN)
-  registry->RegisterBooleanPref(prefs::kWelcomePageOnOSUpgradeEnabled, true);
-#endif
 #if !defined(OS_CHROMEOS)
   registry->RegisterBooleanPref(prefs::kPromotionalTabsEnabled, true);
   registry->RegisterBooleanPref(prefs::kCommandLineFlagSecurityWarningsEnabled,
diff --git a/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.cc b/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.cc
index b083d2f3..2de1c80 100644
--- a/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.cc
+++ b/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.cc
@@ -16,9 +16,9 @@
 #include "build/build_config.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/app/vector_icons/vector_icons.h"
-#include "chrome/browser/favicon/favicon_request_handler_factory.h"
 #include "chrome/browser/favicon/favicon_service_factory.h"
 #include "chrome/browser/favicon/favicon_utils.h"
+#include "chrome/browser/favicon/history_ui_favicon_request_handler_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search/search.h"
 #include "chrome/browser/sessions/session_restore.h"
@@ -31,7 +31,7 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/toolbar/app_menu_model.h"
 #include "chrome/grit/generated_resources.h"
-#include "components/favicon/core/favicon_request_handler.h"
+#include "components/favicon/core/history_ui_favicon_request_handler.h"
 #include "components/favicon_base/favicon_types.h"
 #include "components/feature_engagement/buildflags.h"
 #include "components/prefs/scoped_user_pref_update.h"
@@ -584,13 +584,15 @@
                    weak_ptr_factory_.GetWeakPtr(), command_id),
         &local_tab_cancelable_task_tracker_);
   } else {
-    favicon::FaviconRequestHandler* favicon_request_handler =
-        FaviconRequestHandlerFactory::GetForBrowserContext(browser_->profile());
+    favicon::HistoryUiFaviconRequestHandler*
+        history_ui_favicon_request_handler =
+            HistoryUiFaviconRequestHandlerFactory::GetForBrowserContext(
+                browser_->profile());
     // Can be null for tests.
-    if (!favicon_request_handler)
+    if (!history_ui_favicon_request_handler)
       return;
     sync_sessions::OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate();
-    favicon_request_handler->GetFaviconImageForPageURL(
+    history_ui_favicon_request_handler->GetFaviconImageForPageURL(
         url,
         base::BindOnce(&RecentTabsSubMenuModel::OnFaviconDataAvailable,
                        weak_ptr_factory_.GetWeakPtr(), command_id),
diff --git a/chrome/browser/ui/views/find_bar_views_interactive_uitest.cc b/chrome/browser/ui/views/find_bar_views_interactive_uitest.cc
index 4ca9ce7..e90952b 100644
--- a/chrome/browser/ui/views/find_bar_views_interactive_uitest.cc
+++ b/chrome/browser/ui/views/find_bar_views_interactive_uitest.cc
@@ -6,6 +6,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
+#include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
@@ -613,7 +614,7 @@
 }
 #endif
 
-IN_PROC_BROWSER_TEST_F(FindInPageTest, EscapeOnPageClosesFind) {
+IN_PROC_BROWSER_TEST_F(FindInPageTest, GlobalEscapeClosesFind) {
   ASSERT_TRUE(embedded_test_server()->Start());
   // Make sure Chrome is in the foreground, otherwise sending input
   // won't do anything and the test will hang.
@@ -626,17 +627,13 @@
   browser()->GetFindBarController()->Show(false, true);
   EXPECT_TRUE(IsViewFocused(browser(), VIEW_ID_FIND_IN_PAGE_TEXT_FIELD));
 
-  // put focus onto the page
-  ASSERT_NO_FATAL_FAILURE(
-      ui_test_utils::ClickOnView(browser(), VIEW_ID_TAB_CONTAINER));
-  ASSERT_TRUE(IsViewFocused(browser(), VIEW_ID_TAB_CONTAINER));
+  // Put focus into location bar
+  chrome::FocusLocationBar(browser());
 
   // Close find with escape
   ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_ESCAPE, false,
                                               false, false, false));
 
-  // Focus should still be on the page
-  ASSERT_TRUE(IsViewFocused(browser(), VIEW_ID_TAB_CONTAINER));
   // Find should be closed
   ASSERT_FALSE(IsFindBarVisible());
 }
diff --git a/chrome/browser/ui/views/intent_picker_bubble_view.cc b/chrome/browser/ui/views/intent_picker_bubble_view.cc
index e026f499..f07be172 100644
--- a/chrome/browser/ui/views/intent_picker_bubble_view.cc
+++ b/chrome/browser/ui/views/intent_picker_bubble_view.cc
@@ -379,20 +379,24 @@
                               views::GridLayout::kFixedSize, kTitlePadding);
   scroll_view_ = layout->AddView(std::move(scroll_view));
   layout->StartRow(views::GridLayout::kFixedSize, kColumnSetId, 0);
-  layout->AddView(CreateHorizontalSeparator());
 
-  // This second ColumnSet has a padding column in order to manipulate the
-  // Checkbox positioning freely.
-  constexpr int kColumnSetIdPadded = 1;
-  views::ColumnSet* cs_padded = layout->AddColumnSet(kColumnSetIdPadded);
-  cs_padded->AddPaddingColumn(views::GridLayout::kFixedSize, kTitlePadding);
-  cs_padded->AddColumn(views::GridLayout::FILL, views::GridLayout::CENTER,
-                       views::GridLayout::kFixedSize, views::GridLayout::FIXED,
-                       kMaxIntentPickerLabelButtonWidth - 2 * kTitlePadding, 0);
-
-  layout->StartRowWithPadding(views::GridLayout::kFixedSize, kColumnSetIdPadded,
-                              views::GridLayout::kFixedSize, 0);
   if (show_remember_selection) {
+    layout->AddView(CreateHorizontalSeparator());
+
+    // This second ColumnSet has a padding column in order to manipulate the
+    // Checkbox positioning freely.
+    constexpr int kColumnSetIdPadded = 1;
+    views::ColumnSet* cs_padded = layout->AddColumnSet(kColumnSetIdPadded);
+    cs_padded->AddPaddingColumn(views::GridLayout::kFixedSize, kTitlePadding);
+    cs_padded->AddColumn(
+        views::GridLayout::FILL, views::GridLayout::CENTER,
+        views::GridLayout::kFixedSize, views::GridLayout::FIXED,
+        kMaxIntentPickerLabelButtonWidth - 2 * kTitlePadding, 0);
+
+    layout->StartRowWithPadding(views::GridLayout::kFixedSize,
+                                kColumnSetIdPadded,
+                                views::GridLayout::kFixedSize, 0);
+
     remember_selection_checkbox_ = layout->AddView(
         std::make_unique<views::Checkbox>(l10n_util::GetStringUTF16(
             IDS_INTENT_PICKER_BUBBLE_VIEW_REMEMBER_SELECTION)));
diff --git a/chrome/browser/ui/views/omnibox/omnibox_result_view.cc b/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
index 6a1917e..519db4b 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
@@ -121,7 +121,7 @@
     animation_->Hide();
 }
 
-void OmniboxResultView::Invalidate() {
+void OmniboxResultView::Invalidate(bool force_reapply_styles) {
   bool high_contrast =
       GetNativeTheme() && GetNativeTheme()->UsesHighContrastColors();
   // TODO(tapted): Consider using background()->SetNativeControlColor() and
@@ -191,7 +191,7 @@
     // Normally, OmniboxTextView caches its appearance, but in high contrast,
     // selected-ness changes the text colors, so the styling of the text part of
     // the results needs to be recomputed.
-    if (high_contrast) {
+    if (high_contrast || force_reapply_styles) {
       suggestion_view_->content()->ReapplyStyling();
       suggestion_view_->description()->ReapplyStyling();
     }
@@ -404,7 +404,7 @@
 }
 
 void OmniboxResultView::OnThemeChanged() {
-  Invalidate();
+  Invalidate(true);
   SchedulePaint();
 }
 
diff --git a/chrome/browser/ui/views/omnibox/omnibox_result_view.h b/chrome/browser/ui/views/omnibox/omnibox_result_view.h
index 580cb9b..dc2d8131 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_result_view.h
+++ b/chrome/browser/ui/views/omnibox/omnibox_result_view.h
@@ -60,7 +60,7 @@
 
   void ShowKeyword(bool show_keyword);
 
-  void Invalidate();
+  void Invalidate(bool force_reapply_styles = false);
 
   // Invoked when this result view has been selected.
   void OnSelected();
diff --git a/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.cc b/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.cc
index cca70446..b644f2f 100644
--- a/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.cc
+++ b/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/ui/views/payments/payment_request_views_util.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/payments/content/icon/icon_size.h"
+#include "components/payments/core/error_strings.h"
 #include "components/payments/core/url_util.h"
 #include "components/web_modal/web_contents_modal_dialog_manager.h"
 #include "components/web_modal/web_contents_modal_dialog_manager_delegate.h"
@@ -290,9 +291,6 @@
     content::WebContents* source) {
   DCHECK(source == web_contents());
   if (!SslValidityChecker::IsValidPageInPaymentHandlerWindow(source)) {
-    log_.Error("Aborting payment handler window \"" + target_.spec() +
-               "\" because of insecure certificate state on \"" +
-               source->GetVisibleURL().spec() + "\"");
     AbortPayment();
   }
 }
@@ -330,9 +328,6 @@
 
   if (!SslValidityChecker::IsValidPageInPaymentHandlerWindow(
           navigation_handle->GetWebContents())) {
-    log_.Error("Aborting payment handler window \"" + target_.spec() +
-               "\" because of navigation to an insecure url \"" +
-               navigation_handle->GetURL().spec() + "\"");
     AbortPayment();
     return;
   }
@@ -353,9 +348,6 @@
 }
 
 void PaymentHandlerWebFlowViewController::DidAttachInterstitialPage() {
-  log_.Error("Aborting payment handler window \"" + target_.spec() +
-             "\" because of navigation to a page with invalid certificate "
-             "state or malicious content.");
   AbortPayment();
 }
 
@@ -363,7 +355,7 @@
   if (web_contents())
     web_contents()->Close();
 
-  dialog()->ShowErrorMessage();
+  state()->OnPaymentResponseError(errors::kPaymentHandlerInsecureNavigation);
 }
 
 }  // namespace payments
diff --git a/chrome/browser/ui/views/payments/payment_request_completion_status_metrics_browsertest.cc b/chrome/browser/ui/views/payments/payment_request_completion_status_metrics_browsertest.cc
index 1d4f1d7..62306dc 100644
--- a/chrome/browser/ui/views/payments/payment_request_completion_status_metrics_browsertest.cc
+++ b/chrome/browser/ui/views/payments/payment_request_completion_status_metrics_browsertest.cc
@@ -462,7 +462,8 @@
   ASSERT_EQ(1U, buckets.size());
   EXPECT_EQ(JourneyLogger::EVENT_INITIATED | JourneyLogger::EVENT_USER_ABORTED |
                 JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD |
-                JourneyLogger::EVENT_REQUEST_METHOD_OTHER,
+                JourneyLogger::EVENT_REQUEST_METHOD_OTHER |
+                JourneyLogger::EVENT_NEEDS_COMPLETION_PAYMENT,
             buckets[0].min);
 }
 
diff --git a/chrome/browser/ui/views/payments/payment_request_journey_logger_browsertest.cc b/chrome/browser/ui/views/payments/payment_request_journey_logger_browsertest.cc
index 9b31df58..2a357f0 100644
--- a/chrome/browser/ui/views/payments/payment_request_journey_logger_browsertest.cc
+++ b/chrome/browser/ui/views/payments/payment_request_journey_logger_browsertest.cc
@@ -840,7 +840,8 @@
                 JourneyLogger::EVENT_CAN_MAKE_PAYMENT_TRUE |
                 JourneyLogger::EVENT_HAS_ENROLLED_INSTRUMENT_FALSE |
                 JourneyLogger::EVENT_REQUEST_METHOD_OTHER |
-                JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD,
+                JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD |
+                JourneyLogger::EVENT_NEEDS_COMPLETION_PAYMENT,
             buckets[0].min);
 
   // Make sure that the metrics that required the Payment Request to be shown
@@ -1040,7 +1041,8 @@
   int64_t expected_step_metric =
       JourneyLogger::EVENT_SHOWN |
       JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD |
-      JourneyLogger::EVENT_USER_ABORTED;
+      JourneyLogger::EVENT_USER_ABORTED |
+      JourneyLogger::EVENT_NEEDS_COMPLETION_PAYMENT;
 
   // Make sure the correct UMA events were logged.
   std::vector<base::Bucket> buckets =
diff --git a/chrome/browser/ui/views/payments/payment_request_missing_fields_metrics_browsertest.cc b/chrome/browser/ui/views/payments/payment_request_missing_fields_metrics_browsertest.cc
new file mode 100644
index 0000000..4152797
--- /dev/null
+++ b/chrome/browser/ui/views/payments/payment_request_missing_fields_metrics_browsertest.cc
@@ -0,0 +1,235 @@
+// 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/test/metrics/histogram_tester.h"
+#include "chrome/browser/ui/views/payments/payment_request_browsertest_base.h"
+#include "components/autofill/core/browser/autofill_test_utils.h"
+#include "components/autofill/core/browser/data_model/autofill_profile.h"
+#include "components/autofill/core/browser/data_model/credit_card.h"
+#include "components/autofill/core/browser/validation.h"
+#include "components/payments/core/journey_logger.h"
+#include "components/payments/core/payments_profile_comparator.h"
+
+namespace payments {
+
+using PaymentRequestMissingFieldsMetricsTest = PaymentRequestBrowserTestBase;
+
+// Tests that proper UMA metrics are logged when payment section has no
+// suggestion.
+IN_PROC_BROWSER_TEST_F(PaymentRequestMissingFieldsMetricsTest,
+                       TestMissingPaymentMethod) {
+  NavigateTo("/payment_request_shipping_address_instance_test.html");
+  base::HistogramTester histogram_tester;
+
+  // Add an autofill profile for billing address without adding any cards.
+  autofill::AutofillProfile billing_address = autofill::test::GetFullProfile();
+  AddAutofillProfile(billing_address);
+
+  // Show a Payment Request.
+  InvokePaymentRequestUI();
+
+  // Navigate away to abort the Payment Request and trigger the logs.
+  NavigateTo("/payment_request_email_test.html");
+
+  // Make sure the correct events were logged.
+  int32_t expected_event_bits = JourneyLogger::EVENT_SHOWN |
+                                JourneyLogger::EVENT_USER_ABORTED |
+                                JourneyLogger::EVENT_REQUEST_SHIPPING |
+                                JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD |
+                                JourneyLogger::EVENT_NEEDS_COMPLETION_PAYMENT;
+  histogram_tester.ExpectBucketCount("PaymentRequest.Events",
+                                     expected_event_bits, 1);
+
+  // Since no card is added to the profile, all payment fields should be
+  // missing.
+  int32_t expected_missing_payment_bits =
+      autofill::CREDIT_CARD_EXPIRED | autofill::CREDIT_CARD_NO_CARDHOLDER |
+      autofill::CREDIT_CARD_NO_NUMBER |
+      autofill::CREDIT_CARD_NO_BILLING_ADDRESS;
+  histogram_tester.ExpectBucketCount("PaymentRequest.MissingPaymentFields",
+                                     expected_missing_payment_bits, 1);
+
+  // There should be no log for missing shipping address fields since the
+  // section had one complete suggestion.
+  histogram_tester.ExpectTotalCount("PaymentRequest.MissingShippingFields", 0);
+}
+
+// Tests that proper UMA metrics are logged when payment section has incomplete
+// card.
+IN_PROC_BROWSER_TEST_F(PaymentRequestMissingFieldsMetricsTest,
+                       TestIncompleteCard) {
+  NavigateTo("/payment_request_shipping_address_instance_test.html");
+  base::HistogramTester histogram_tester;
+
+  // Add an autofill profile for billing address with a credit card missing card
+  // holder's name.
+  autofill::AutofillProfile billing_address = autofill::test::GetFullProfile();
+  AddAutofillProfile(billing_address);
+  autofill::CreditCard card = autofill::test::GetIncompleteCreditCard();
+  card.set_billing_address_id(billing_address.guid());
+  AddCreditCard(card);  // Visa
+
+  // Show a Payment Request.
+  InvokePaymentRequestUI();
+
+  // Navigate away to abort the Payment Request and trigger the logs.
+  NavigateTo("/payment_request_email_test.html");
+
+  // Make sure the correct events were logged. Even though the suggested card
+  // is incomplete, EVENT_HAD_INITIAL_FORM_OF_PAYMENT is set since it only
+  // shows whether the payment suggestion list is empty or not.
+  int32_t expected_event_bits =
+      JourneyLogger::EVENT_SHOWN | JourneyLogger::EVENT_USER_ABORTED |
+      JourneyLogger::EVENT_HAD_INITIAL_FORM_OF_PAYMENT |
+      JourneyLogger::EVENT_REQUEST_SHIPPING |
+      JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD |
+      JourneyLogger::EVENT_NEEDS_COMPLETION_PAYMENT;
+  histogram_tester.ExpectBucketCount("PaymentRequest.Events",
+                                     expected_event_bits, 1);
+
+  int32_t expected_missing_payment_bits = autofill::CREDIT_CARD_NO_CARDHOLDER;
+  histogram_tester.ExpectBucketCount("PaymentRequest.MissingPaymentFields",
+                                     expected_missing_payment_bits, 1);
+
+  // There should be no log for missing shipping address fields since the
+  // section had one complete suggestion.
+  histogram_tester.ExpectTotalCount("PaymentRequest.MissingShippingFields", 0);
+}
+
+// Tests that proper UMA metrics are logged when payment section has expired
+// card.
+IN_PROC_BROWSER_TEST_F(PaymentRequestMissingFieldsMetricsTest,
+                       TestExpiredCard) {
+  NavigateTo("/payment_request_shipping_address_instance_test.html");
+  base::HistogramTester histogram_tester;
+
+  // Add an autofill profile for billing address with an expired card.
+  autofill::AutofillProfile billing_address = autofill::test::GetFullProfile();
+  AddAutofillProfile(billing_address);
+  autofill::CreditCard card = autofill::test::GetExpiredCreditCard();
+  card.set_billing_address_id(billing_address.guid());
+  AddCreditCard(card);  // Visa
+
+  // Show a Payment Request.
+  InvokePaymentRequestUI();
+
+  // Navigate away to abort the Payment Request and trigger the logs.
+  NavigateTo("/payment_request_email_test.html");
+
+  // Make sure the correct events were logged.
+  // EVENT_HAD_NECESSARY_COMPLETE_SUGGESTIONS is set since expired cards are
+  // treated as complete.
+  int32_t expected_event_bits =
+      JourneyLogger::EVENT_SHOWN | JourneyLogger::EVENT_USER_ABORTED |
+      JourneyLogger::EVENT_HAD_INITIAL_FORM_OF_PAYMENT |
+      JourneyLogger::EVENT_HAD_NECESSARY_COMPLETE_SUGGESTIONS |
+      JourneyLogger::EVENT_REQUEST_SHIPPING |
+      JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD;
+  histogram_tester.ExpectBucketCount("PaymentRequest.Events",
+                                     expected_event_bits, 1);
+
+  // Expired cards are considered as complete according to
+  // AutofillPaymentInstrument::IsCompleteForPayment().
+  histogram_tester.ExpectTotalCount("PaymentRequest.MissingPaymentFields", 0);
+
+  // There should be no log for missing shipping address fields since the
+  // section had one complete suggestion.
+  histogram_tester.ExpectTotalCount("PaymentRequest.MissingShippingFields", 0);
+}
+
+// Tests that proper UMA metrics are logged when shipping section is incomplete.
+IN_PROC_BROWSER_TEST_F(PaymentRequestMissingFieldsMetricsTest,
+                       TestIncompleteShippingProfile) {
+  NavigateTo("/payment_request_shipping_address_instance_test.html");
+  base::HistogramTester histogram_tester;
+
+  // Add an incomplete profile for billing address. The profile has email
+  // address only.
+  autofill::AutofillProfile billing_address =
+      autofill::test::GetIncompleteProfile2();
+  AddAutofillProfile(billing_address);
+  autofill::CreditCard card = autofill::test::GetCreditCard();
+  card.set_billing_address_id(billing_address.guid());
+  AddCreditCard(card);  // Visa
+
+  // Show a Payment Request.
+  InvokePaymentRequestUI();
+
+  // Navigate away to abort the Payment Request and trigger the logs.
+  NavigateTo("/payment_request_email_test.html");
+
+  // Make sure the correct events were logged.
+  int32_t expected_event_bits =
+      JourneyLogger::EVENT_SHOWN | JourneyLogger::EVENT_USER_ABORTED |
+      JourneyLogger::EVENT_HAD_INITIAL_FORM_OF_PAYMENT |
+      JourneyLogger::EVENT_REQUEST_SHIPPING |
+      JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD |
+      JourneyLogger::EVENT_NEEDS_COMPLETION_SHIPPING;
+  histogram_tester.ExpectBucketCount("PaymentRequest.Events",
+                                     expected_event_bits, 1);
+
+  // Since the incomplete profile has email address only, the rest of the bits
+  // should be logged in MissingShippingFields.
+  int32_t expected_missing_shipping_bits = PaymentsProfileComparator::kName |
+                                           PaymentsProfileComparator::kPhone |
+                                           PaymentsProfileComparator::kAddress;
+  histogram_tester.ExpectBucketCount("PaymentRequest.MissingShippingFields",
+                                     expected_missing_shipping_bits, 1);
+
+  // There should be no log for missing payment fields since the section had one
+  // complete suggestion.
+  histogram_tester.ExpectTotalCount("PaymentRequest.MissingPaymentFields", 0);
+}
+
+// Tests that proper UMA metrics are logged when contacts section is incomplete.
+IN_PROC_BROWSER_TEST_F(PaymentRequestMissingFieldsMetricsTest,
+                       TestIncompleteContactDetails) {
+  NavigateTo("/payment_request_contact_details_test.html");
+  base::HistogramTester histogram_tester;
+
+  // Add an incomplete profile for billing address. The profile has email
+  // address only.
+  autofill::AutofillProfile billing_address =
+      autofill::test::GetIncompleteProfile2();
+  AddAutofillProfile(billing_address);
+  autofill::CreditCard card = autofill::test::GetCreditCard();
+  card.set_billing_address_id(billing_address.guid());
+  AddCreditCard(card);  // Visa
+
+  // Show a Payment Request.
+  InvokePaymentRequestUI();
+
+  // Navigate away to abort the Payment Request and trigger the logs.
+  NavigateTo("/payment_request_email_test.html");
+
+  // Make sure the correct events were logged.
+  int32_t expected_event_bits =
+      JourneyLogger::EVENT_SHOWN | JourneyLogger::EVENT_USER_ABORTED |
+      JourneyLogger::EVENT_HAD_INITIAL_FORM_OF_PAYMENT |
+      JourneyLogger::EVENT_REQUEST_PAYER_NAME |
+      JourneyLogger::EVENT_REQUEST_PAYER_EMAIL |
+      JourneyLogger::EVENT_REQUEST_PAYER_PHONE |
+      JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD |
+      JourneyLogger::EVENT_REQUEST_METHOD_OTHER |
+      JourneyLogger::EVENT_NEEDS_COMPLETION_CONTACT_INFO;
+  histogram_tester.ExpectBucketCount("PaymentRequest.Events",
+                                     expected_event_bits, 1);
+
+  // Since the incomplete profile has email address only, the rest of the bits
+  // should be logged in MissingContactFields.
+  int32_t expected_missing_contact_bits =
+      PaymentsProfileComparator::kName | PaymentsProfileComparator::kPhone;
+  histogram_tester.ExpectBucketCount("PaymentRequest.MissingContactFields",
+                                     expected_missing_contact_bits, 1);
+
+  // There should be no log for missing payment fields since the section had one
+  // complete suggestion.
+  histogram_tester.ExpectTotalCount("PaymentRequest.MissingPaymentFields", 0);
+
+  // Even though the profile is incomplete, there should be no log for missing
+  // shipping fields since shipping was not required.
+  histogram_tester.ExpectTotalCount("PaymentRequest.MissingShippingFields", 0);
+}
+
+}  // namespace payments
diff --git a/chrome/browser/ui/views/translate/translate_bubble_view.cc b/chrome/browser/ui/views/translate/translate_bubble_view.cc
index fe99a680..24af817 100644
--- a/chrome/browser/ui/views/translate/translate_bubble_view.cc
+++ b/chrome/browser/ui/views/translate/translate_bubble_view.cc
@@ -226,7 +226,7 @@
     before_translate_view_ = AddChildView(GM2CreateViewBeforeTranslate());
     translating_view_ = AddChildView(GM2CreateViewTranslating());
     after_translate_view_ = AddChildView(GM2CreateViewAfterTranslate());
-    error_view_ = AddChildView(CreateViewError());
+    error_view_ = AddChildView(CreateViewErrorGM2());
     advanced_view_ = AddChildView(CreateViewAdvanced());
   } else if (bubble_ui_model_ == language::TranslateUIBubbleModel::TAB) {
     // |tab_translate_view| is the child view being used before/during/after
@@ -237,7 +237,7 @@
     after_translate_view_ = tab_translate_view_;
     advanced_view_source_ = AddChildView(TabUiCreateViewAdvanedSource());
     advanced_view_target_ = AddChildView(TabUiCreateViewAdvanedTarget());
-    error_view_ = AddChildView(CreateViewError());
+    error_view_ = AddChildView(CreateViewErrorTab());
   } else {
     before_translate_view_ = AddChildView(CreateViewBeforeTranslate());
     translating_view_ = AddChildView(CreateViewTranslating());
@@ -311,6 +311,13 @@
       ResetLanguage();
       break;
     }
+    case BUTTON_ID_RETURN: {
+      SwitchView(TranslateBubbleModel::VIEW_STATE_AFTER_TRANSLATE);
+      tabbed_pane_->SelectTabAt(1);
+      UpdateChildVisibilities();
+      SizeToContents();
+      break;
+    }
   }
 }
 
@@ -320,14 +327,12 @@
 
 bool TranslateBubbleView::ShouldShowCloseButton() const {
   return bubble_ui_model_ != language::TranslateUIBubbleModel::TAB &&
-         bubble_ui_model_ != language::TranslateUIBubbleModel::BUTTON_GM2 &&
-         model_->GetViewState() != TranslateBubbleModel::VIEW_STATE_ERROR;
+         bubble_ui_model_ != language::TranslateUIBubbleModel::BUTTON_GM2;
 }
 
 bool TranslateBubbleView::ShouldShowWindowTitle() const {
   return bubble_ui_model_ != language::TranslateUIBubbleModel::TAB &&
-         bubble_ui_model_ != language::TranslateUIBubbleModel::BUTTON_GM2 &&
-         model_->GetViewState() != TranslateBubbleModel::VIEW_STATE_ERROR;
+         bubble_ui_model_ != language::TranslateUIBubbleModel::BUTTON_GM2;
 }
 
 void TranslateBubbleView::ResetLanguage() {
@@ -657,7 +662,14 @@
       SwitchView(TranslateBubbleModel::VIEW_STATE_TRANSLATING);
     }
   } else if (bubble_ui_model_ == language::TranslateUIBubbleModel::TAB) {
-    if (model_->IsPageTranslatedInCurrentLanguages()) {
+    // Switch back to the original page language if target language is the same
+    // as source language without triggering translating.
+    if (target_language_combobox_->GetSelectedIndex() ==
+        model_->GetOriginalLanguageIndex()) {
+      SwitchView(TranslateBubbleModel::VIEW_STATE_AFTER_TRANSLATE);
+      tabbed_pane_->SelectTabAt(0);
+      ShowOriginal();
+    } else if (model_->IsPageTranslatedInCurrentLanguages()) {
       SwitchView(TranslateBubbleModel::VIEW_STATE_BEFORE_TRANSLATE);
       SizeToContents();
     } else {
@@ -667,8 +679,9 @@
       UpdateLanguageNames(&original_language_name, &target_language_name);
       tabbed_pane_->GetTabAt(0)->SetTitleText(original_language_name);
       tabbed_pane_->GetTabAt(1)->SetTitleText(target_language_name);
-      SwitchView(TranslateBubbleModel::VIEW_STATE_AFTER_TRANSLATE);
+      model_->Translate();
       tabbed_pane_->SelectTabAt(1);
+      SwitchView(TranslateBubbleModel::VIEW_STATE_AFTER_TRANSLATE);
     }
   } else {
     if (model_->IsPageTranslatedInCurrentLanguages()) {
@@ -703,24 +716,32 @@
           source_language_combobox_->GetSelectedIndex() - 1) {
         UpdateAdvancedView();
         break;
+      } else {
+        model_->UpdateOriginalLanguageIndex(
+            source_language_combobox_->GetSelectedIndex() - 1);
+        UpdateAdvancedView();
+        translate::ReportUiAction(translate::SOURCE_LANGUAGE_MENU_CLICKED);
+        break;
       }
-      model_->UpdateOriginalLanguageIndex(
-          source_language_combobox_->GetSelectedIndex() - 1);
-      UpdateAdvancedView();
-      translate::ReportUiAction(translate::SOURCE_LANGUAGE_MENU_CLICKED);
-      break;
     }
     case COMBOBOX_ID_TARGET_LANGUAGE: {
       if (model_->GetTargetLanguageIndex() ==
-          target_language_combobox_->GetSelectedIndex()) {
+              target_language_combobox_->GetSelectedIndex() ||
+          // TAB UI doesn't change target language if it's the same as original
+          // source language. It simply shows page in original language and
+          // return to |after_translate_view_|.
+          (bubble_ui_model_ == language::TranslateUIBubbleModel::TAB &&
+           target_language_combobox_->GetSelectedIndex() ==
+               model_->GetOriginalLanguageIndex())) {
         UpdateAdvancedView();
         break;
+      } else {
+        model_->UpdateTargetLanguageIndex(
+            target_language_combobox_->GetSelectedIndex());
+        UpdateAdvancedView();
+        translate::ReportUiAction(translate::TARGET_LANGUAGE_MENU_CLICKED);
+        break;
       }
-      model_->UpdateTargetLanguageIndex(
-          target_language_combobox_->GetSelectedIndex());
-      UpdateAdvancedView();
-      translate::ReportUiAction(translate::TARGET_LANGUAGE_MENU_CLICKED);
-      break;
     }
   }
 }
@@ -1266,6 +1287,69 @@
   return view;
 }
 
+std::unique_ptr<views::View> TranslateBubbleView::CreateViewErrorTab() {
+  auto return_button = views::MdTextButton::CreateSecondaryUiButton(
+      this, l10n_util::GetStringUTF16(IDS_CANCEL));
+  return_button->SetID(BUTTON_ID_RETURN);
+  return CreateViewErrorNoTitle(std::move(return_button));
+}
+
+std::unique_ptr<views::View> TranslateBubbleView::CreateViewErrorGM2() {
+  auto advanced_button = views::MdTextButton::CreateSecondaryUiButton(
+      this, l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_ADVANCED_BUTTON));
+  advanced_button->SetID(BUTTON_ID_ADVANCED);
+  return CreateViewErrorNoTitle(std::move(advanced_button));
+}
+
+std::unique_ptr<views::View> TranslateBubbleView::CreateViewErrorNoTitle(
+    std::unique_ptr<views::Button> advanced_button) {
+  auto view = std::make_unique<views::View>();
+  views::GridLayout* layout =
+      view->SetLayoutManager(std::make_unique<views::GridLayout>());
+
+  ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
+
+  enum { COLUMN_SET_ID_TITLE, COLUMN_SET_ID_BUTTONS };
+
+  views::ColumnSet* cs = layout->AddColumnSet(COLUMN_SET_ID_TITLE);
+  cs->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER,
+                views::GridLayout::kFixedSize, views::GridLayout::USE_PREF, 0,
+                0);
+
+  cs = layout->AddColumnSet(COLUMN_SET_ID_BUTTONS);
+  cs->AddPaddingColumn(1.0, 0);
+  cs->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER,
+                views::GridLayout::kFixedSize, views::GridLayout::USE_PREF, 0,
+                0);
+  cs->AddPaddingColumn(
+      views::GridLayout::kFixedSize,
+      provider->GetDistanceMetric(views::DISTANCE_RELATED_BUTTON_HORIZONTAL));
+  cs->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER,
+                views::GridLayout::kFixedSize, views::GridLayout::USE_PREF, 0,
+                0);
+
+  layout->StartRow(views::GridLayout::kFixedSize, COLUMN_SET_ID_TITLE);
+
+  int error_message_id = IDS_TRANSLATE_BUBBLE_COULD_NOT_TRANSLATE_TITLE;
+  auto error_message_label = std::make_unique<views::Label>(
+      l10n_util::GetStringUTF16(error_message_id),
+      views::style::CONTEXT_DIALOG_TITLE, views::style::STYLE_PRIMARY);
+  layout->AddView(std::move(error_message_label));
+
+  layout->StartRowWithPadding(
+      views::GridLayout::kFixedSize, COLUMN_SET_ID_BUTTONS,
+      views::GridLayout::kFixedSize,
+      provider->GetDistanceMetric(views::DISTANCE_UNRELATED_CONTROL_VERTICAL));
+  auto try_again_button = views::MdTextButton::CreateSecondaryUiButton(
+      this, l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_TRY_AGAIN));
+  try_again_button->SetID(BUTTON_ID_TRY_AGAIN);
+  layout->AddView(std::move(try_again_button));
+
+  layout->AddView(std::move(advanced_button));
+  Layout();
+  return view;
+}
+
 // TODO(hajimehoshi): Revice this later to show a specific message for each
 // error. (crbug/307350)
 std::unique_ptr<views::View> TranslateBubbleView::CreateViewAdvanced() {
@@ -1576,8 +1660,8 @@
     TranslateBubbleModel::ViewState view_state) {
   TranslateBubbleModel::ViewState current_state = model_->GetViewState();
   if ((bubble_ui_model_ == language::TranslateUIBubbleModel::TAB &&
-       !TabUiIsAdvancedState(view_state) &&
-       !TabUiIsAdvancedState(current_state)) ||
+       TabUiIsEquivalentState(view_state) &&
+       TabUiIsEquivalentState(current_state)) ||
       current_state == view_state) {
     return;
   }
@@ -1596,11 +1680,11 @@
   model_->ShowError(error_type);
 }
 
-bool TranslateBubbleView::TabUiIsAdvancedState(
+bool TranslateBubbleView::TabUiIsEquivalentState(
     TranslateBubbleModel::ViewState view_state) {
-  return !(view_state == TranslateBubbleModel::VIEW_STATE_BEFORE_TRANSLATE ||
-           view_state == TranslateBubbleModel::VIEW_STATE_TRANSLATING ||
-           view_state == TranslateBubbleModel::VIEW_STATE_AFTER_TRANSLATE);
+  return view_state == TranslateBubbleModel::VIEW_STATE_BEFORE_TRANSLATE ||
+         view_state == TranslateBubbleModel::VIEW_STATE_TRANSLATING ||
+         view_state == TranslateBubbleModel::VIEW_STATE_AFTER_TRANSLATE;
 }
 
 void TranslateBubbleView::UpdateAdvancedView() {
diff --git a/chrome/browser/ui/views/translate/translate_bubble_view.h b/chrome/browser/ui/views/translate/translate_bubble_view.h
index a2dabed..0cc9092 100644
--- a/chrome/browser/ui/views/translate/translate_bubble_view.h
+++ b/chrome/browser/ui/views/translate/translate_bubble_view.h
@@ -144,7 +144,8 @@
     BUTTON_ID_OPTIONS_MENU,
     BUTTON_ID_OPTIONS_MENU_TAB,
     BUTTON_ID_CLOSE,
-    BUTTON_ID_RESET
+    BUTTON_ID_RESET,
+    BUTTON_ID_RETURN
   };
 
   enum ComboboxID {
@@ -251,10 +252,19 @@
   // Creates the 'after translate' view.
   std::unique_ptr<views::View> CreateViewAfterTranslate();
 
-  // Creates the 'error' view. Caller takes ownership of the returned view.
-  // Three options depending on UI selection in kUseButtonTranslateBubbleUI.
+  // Creates the 'error' view for Button UI. Caller takes ownership of the
+  // returned view.
   std::unique_ptr<views::View> CreateViewError();
 
+  // Creates the 'error' view skeleton UI with no title. Caller takes ownership
+  // of the returned view.
+  std::unique_ptr<views::View> CreateViewErrorNoTitle(
+      std::unique_ptr<views::Button> advanced_button);
+
+  // Creates the 'error' view for Tab and Button_GM2 UI.
+  std::unique_ptr<views::View> CreateViewErrorTab();
+  std::unique_ptr<views::View> CreateViewErrorGM2();
+
   // Creates the 'advanced' view. Caller takes ownership of the returned view.
   // Three options depending on UI selection in kUseButtonTranslateBubbleUI.
   std::unique_ptr<views::View> CreateViewAdvanced();
@@ -306,7 +316,7 @@
   void ConfirmAdvancedOptions();
 
   // Return true if the current state is in advanced state for TAB UI.
-  bool TabUiIsAdvancedState(TranslateBubbleModel::ViewState view_state);
+  bool TabUiIsEquivalentState(TranslateBubbleModel::ViewState view_state);
 
   // Handles the reset button in advanced view under Tab UI.
   void ResetLanguage();
diff --git a/chrome/browser/ui/webui/favicon_source.cc b/chrome/browser/ui/webui/favicon_source.cc
index 0153c7e..0299e976 100644
--- a/chrome/browser/ui/webui/favicon_source.cc
+++ b/chrome/browser/ui/webui/favicon_source.cc
@@ -10,15 +10,15 @@
 #include "base/bind_helpers.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_number_conversions.h"
-#include "chrome/browser/favicon/favicon_request_handler_factory.h"
 #include "chrome/browser/favicon/favicon_service_factory.h"
+#include "chrome/browser/favicon/history_ui_favicon_request_handler_factory.h"
 #include "chrome/browser/history/top_sites_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search/instant_io_context.h"
 #include "chrome/browser/sync/session_sync_service_factory.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/common/webui_url_constants.h"
-#include "components/favicon/core/favicon_request_handler.h"
+#include "components/favicon/core/history_ui_favicon_request_handler.h"
 #include "components/favicon_base/favicon_url_parser.h"
 #include "components/history/core/browser/top_sites.h"
 #include "components/sync_sessions/open_tabs_ui_delegate.h"
@@ -142,9 +142,11 @@
         }
       }
     }
-    favicon::FaviconRequestHandler* favicon_request_handler =
-        FaviconRequestHandlerFactory::GetForBrowserContext(profile_);
-    if (!favicon_request_handler) {
+    favicon::HistoryUiFaviconRequestHandler*
+        history_ui_favicon_request_handler =
+            HistoryUiFaviconRequestHandlerFactory::GetForBrowserContext(
+                profile_);
+    if (!history_ui_favicon_request_handler) {
       SendDefaultResponse(callback);
       return;
     }
@@ -152,7 +154,7 @@
         SessionSyncServiceFactory::GetInstance()->GetForProfile(profile_);
     sync_sessions::OpenTabsUIDelegate* open_tabs =
         session_sync_service->GetOpenTabsUIDelegate();
-    favicon_request_handler->GetRawFaviconForPageURL(
+    history_ui_favicon_request_handler->GetRawFaviconForPageURL(
         url, desired_size_in_pixel,
         base::BindOnce(&FaviconSource::OnFaviconDataAvailable,
                        base::Unretained(this),
diff --git a/chrome/browser/vr/metrics/session_metrics_helper.cc b/chrome/browser/vr/metrics/session_metrics_helper.cc
index ed0203c..47638a7 100644
--- a/chrome/browser/vr/metrics/session_metrics_helper.cc
+++ b/chrome/browser/vr/metrics/session_metrics_helper.cc
@@ -262,8 +262,6 @@
           std::make_unique<ukm::builders::XR_WebXR_Session>(
               ukm::GetSourceIdForWebContentsDocument(web_contents()))));
 
-  // TODO(https://crbug.com/968648): Use chain notation to set values for
-  // metrics.
   // TODO(https://crbug.com/968546): StartAction is currently not present in
   // XR.WebXR.Session event. Remove this & change the below code with
   // replacement metrics once they are designed:
@@ -271,8 +269,7 @@
   //    PresentationStartAction::kOther);
   // WebVR does not come through this path as it does not have a separate
   // concept of inline sessions.
-  result.first->second->ukm_entry()->SetIsLegacyWebVR(false);
-  result.first->second->ukm_entry()->SetMode(
+  result.first->second->ukm_entry()->SetIsLegacyWebVR(false).SetMode(
       static_cast<int64_t>(device::SessionMode::kInline));
 }
 
@@ -354,16 +351,13 @@
 
   UMA_HISTOGRAM_ENUMERATION("XR.WebXR.PresentationSession", action);
 
-  // TODO(https://crbug.com/968648): Use chain notation to set values for
-  // metrics.
   // TODO(https://crbug.com/968546): StartAction is currently not present in
   // XR.WebXR.Session event. Remove this & change the below code with
   // replacement metrics once they are designed:
   // webxr_immersive_session_tracker_->ukm_entry()->SetStartAction(action);
-  webxr_immersive_session_tracker_->ukm_entry()->SetIsLegacyWebVR(
-      is_legacy_webvr);
-  webxr_immersive_session_tracker_->ukm_entry()->SetMode(
-      static_cast<int64_t>(xr_session_mode));
+  webxr_immersive_session_tracker_->ukm_entry()
+      ->SetIsLegacyWebVR(is_legacy_webvr)
+      .SetMode(static_cast<int64_t>(xr_session_mode));
 }
 
 void SessionMetricsHelper::SetWebVREnabled(bool is_webvr_presenting) {
@@ -688,17 +682,16 @@
               std::make_unique<ukm::builders::XR_WebXR_Session>(
                   ukm::GetSourceIdForWebContentsDocument(web_contents())));
       if (pending_immersive_session_start_info_) {
-        // TODO(https://crbug.com/968648): Use chain notation to set values for
-        // metrics.
         // TODO(https://crbug.com/968546): StartAction is currently not present
         // in XR.WebXR.Session event. Remove this & change the below code with
         // replacement metrics once they are designed:
         // webxr_immersive_session_tracker_->ukm_entry()->SetStartAction(
         //    pending_immersive_session_start_info_->action);
-        webxr_immersive_session_tracker_->ukm_entry()->SetIsLegacyWebVR(
-            pending_immersive_session_start_info_->is_legacy_webvr);
-        webxr_immersive_session_tracker_->ukm_entry()->SetMode(
-            static_cast<int64_t>(pending_immersive_session_start_info_->mode));
+        webxr_immersive_session_tracker_->ukm_entry()
+            ->SetIsLegacyWebVR(
+                pending_immersive_session_start_info_->is_legacy_webvr)
+            .SetMode(static_cast<int64_t>(
+                pending_immersive_session_start_info_->mode));
         pending_immersive_session_start_info_ = base::nullopt;
       }
     }
diff --git a/chrome/browser/vr/service/xr_device_impl.cc b/chrome/browser/vr/service/xr_device_impl.cc
index 72bb987..a8bb327 100644
--- a/chrome/browser/vr/service/xr_device_impl.cc
+++ b/chrome/browser/vr/service/xr_device_impl.cc
@@ -24,7 +24,6 @@
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/origin_util.h"
-#include "device/vr/vr_display_impl.h"
 
 #if defined(OS_WIN)
 #include "chrome/browser/vr/service/xr_session_request_consent_manager.h"
diff --git a/chrome/browser/vr/service/xr_device_impl.h b/chrome/browser/vr/service/xr_device_impl.h
index 9f1fb0f..dad22da 100644
--- a/chrome/browser/vr/service/xr_device_impl.h
+++ b/chrome/browser/vr/service/xr_device_impl.h
@@ -23,16 +23,12 @@
 class WebContents;
 }  // namespace content
 
-namespace device {
-class VRDisplayImpl;
-}  // namespace device
-
 namespace vr {
 
 class XRRuntimeManager;
 class BrowserXRRuntime;
 
-// The browser-side host for a device::VRDisplayImpl. Controls access to VR
+// The browser-side host for VR services. Controls access to VR
 // APIs like poses and presentation.
 class XRDeviceImpl : public device::mojom::XRDevice {
  public:
diff --git a/chrome/browser/wake_lock/wake_lock_browsertest.cc b/chrome/browser/wake_lock/wake_lock_browsertest.cc
new file mode 100644
index 0000000..0753014
--- /dev/null
+++ b/chrome/browser/wake_lock/wake_lock_browsertest.cc
@@ -0,0 +1,138 @@
+// Copyright (c) 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 <string>
+
+#include "base/command_line.h"
+#include "chrome/browser/permissions/permission_request_manager.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/ui_test_utils.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/test/browser_test_utils.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "testing/gmock/include/gmock/gmock-matchers.h"
+
+namespace {
+
+// Trimmed down version of the class found in geolocation_browsertest.cc.
+// Used to observe the creation of a single permission request without
+// responding.
+class PermissionRequestObserver : public PermissionRequestManager::Observer {
+ public:
+  explicit PermissionRequestObserver(content::WebContents* web_contents)
+      : request_manager_(
+            PermissionRequestManager::FromWebContents(web_contents)),
+        request_shown_(false) {
+    request_manager_->AddObserver(this);
+  }
+  ~PermissionRequestObserver() override {
+    // Safe to remove twice if it happens.
+    request_manager_->RemoveObserver(this);
+  }
+
+  bool request_shown() { return request_shown_; }
+
+ private:
+  // PermissionRequestManager::Observer
+  void OnBubbleAdded() override {
+    request_shown_ = true;
+    request_manager_->RemoveObserver(this);
+  }
+
+  PermissionRequestManager* request_manager_;
+  bool request_shown_;
+
+  DISALLOW_COPY_AND_ASSIGN(PermissionRequestObserver);
+};
+
+}  // namespace
+
+class WakeLockBrowserTest : public InProcessBrowserTest {
+ protected:
+  // InProcessBrowserTest:
+  void SetUpCommandLine(base::CommandLine* command_line) override;
+  void SetUpOnMainThread() override;
+};
+
+void WakeLockBrowserTest::SetUpCommandLine(base::CommandLine* command_line) {
+  command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures, "WakeLock");
+}
+
+void WakeLockBrowserTest::SetUpOnMainThread() {
+  // Navigate to a secure context.
+  embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
+  ASSERT_TRUE(embedded_test_server()->Start());
+  ui_test_utils::NavigateToURL(
+      browser(),
+      embedded_test_server()->GetURL("localhost", "/simple_page.html"));
+  auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
+  EXPECT_THAT(
+      web_contents->GetMainFrame()->GetLastCommittedOrigin().Serialize(),
+      testing::StartsWith("http://localhost:"));
+}
+
+IN_PROC_BROWSER_TEST_F(WakeLockBrowserTest, RequestPermissionScreen) {
+  // Requests for a screen lock should always be granted, and there should be no
+  // permission prompt.
+  PermissionRequestObserver observer(
+      browser()->tab_strip_model()->GetActiveWebContents());
+  std::string response;
+  EXPECT_TRUE(content::ExecuteScriptAndExtractString(
+      browser()->tab_strip_model()->GetActiveWebContents(),
+      "WakeLock.requestPermission('screen').then(status => "
+      "    domAutomationController.send(status));",
+      &response));
+  EXPECT_EQ(response, "granted");
+  EXPECT_EQ(observer.request_shown(), false);
+}
+
+IN_PROC_BROWSER_TEST_F(WakeLockBrowserTest,
+                       RequestPermissionScreenNoUserGesture) {
+  // Requests for a screen lock should always be granted, and there should be no
+  // permission prompt.
+  PermissionRequestObserver observer(
+      browser()->tab_strip_model()->GetActiveWebContents());
+  std::string response;
+  EXPECT_TRUE(content::ExecuteScriptWithoutUserGestureAndExtractString(
+      browser()->tab_strip_model()->GetActiveWebContents(),
+      "WakeLock.requestPermission('screen').then(status => "
+      "    domAutomationController.send(status));",
+      &response));
+  EXPECT_EQ(response, "granted");
+  EXPECT_EQ(observer.request_shown(), false);
+}
+
+IN_PROC_BROWSER_TEST_F(WakeLockBrowserTest, RequestPermissionSystem) {
+  // Requests for a system lock should always be denied, and there should be no
+  // permission prompt.
+  PermissionRequestObserver observer(
+      browser()->tab_strip_model()->GetActiveWebContents());
+  std::string response;
+  EXPECT_TRUE(content::ExecuteScriptAndExtractString(
+      browser()->tab_strip_model()->GetActiveWebContents(),
+      "WakeLock.requestPermission('system').then(status => "
+      "    domAutomationController.send(status));",
+      &response));
+  EXPECT_EQ(response, "denied");
+  EXPECT_EQ(observer.request_shown(), false);
+}
+
+IN_PROC_BROWSER_TEST_F(WakeLockBrowserTest,
+                       RequestPermissionSystemNoUserGesture) {
+  // Requests for a system lock should always be denied, and there should be no
+  // permission prompt.
+  PermissionRequestObserver observer(
+      browser()->tab_strip_model()->GetActiveWebContents());
+  std::string response;
+  EXPECT_TRUE(content::ExecuteScriptWithoutUserGestureAndExtractString(
+      browser()->tab_strip_model()->GetActiveWebContents(),
+      "WakeLock.requestPermission('system').then(status => "
+      "    domAutomationController.send(status));",
+      &response));
+  EXPECT_EQ(response, "denied");
+  EXPECT_EQ(observer.request_shown(), false);
+}
diff --git a/chrome/browser/web_applications/extensions/install_manager_bookmark_app_browsertest.cc b/chrome/browser/web_applications/extensions/install_manager_bookmark_app_browsertest.cc
index 1969213..55abee6 100644
--- a/chrome/browser/web_applications/extensions/install_manager_bookmark_app_browsertest.cc
+++ b/chrome/browser/web_applications/extensions/install_manager_bookmark_app_browsertest.cc
@@ -19,6 +19,11 @@
 #include "extensions/browser/extension_registry.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+#if defined(OS_CHROMEOS)
+#include "ash/public/cpp/shelf_model.h"
+#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
+#endif  // defined(OS_CHROMEOS)
+
 namespace extensions {
 
 class InstallManagerBookmarkAppDialogTest : public DialogBrowserTest {
@@ -80,6 +85,14 @@
   chrome::SetAutoAcceptBookmarkAppDialogForTesting(true);
   ShowAndVerifyUi();
   chrome::SetAutoAcceptBookmarkAppDialogForTesting(false);
+
+#if defined(OS_CHROMEOS)
+  const ash::ShelfID shelf_id(installed_app_id());
+  EXPECT_TRUE(ChromeLauncherController::instance()->IsPinned(shelf_id));
+  EXPECT_EQ(
+      shelf_id,
+      ChromeLauncherController::instance()->shelf_model()->active_shelf_id());
+#endif  // defined(OS_CHROMEOS)
 }
 
 IN_PROC_BROWSER_TEST_F(InstallManagerBookmarkAppDialogTest,
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index d4bbb09..9db1c715 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -1732,14 +1732,6 @@
 // launches.
 const char kRegisteredBackgroundContents[] = "background_contents.registered";
 
-#if defined(OS_WIN)
-// Boolean that specifies whether or not showing the welcome page following an
-// OS upgrade is enabled. True by default. May be set by master_preferences or
-// overridden by the WelcomePageOnOSUpgradeEnabled policy setting.
-const char kWelcomePageOnOSUpgradeEnabled[] =
-    "browser.welcome_page_on_os_upgrade_enabled";
-#endif
-
 // String that lists supported HTTP authentication schemes.
 const char kAuthSchemes[] = "auth.schemes";
 
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 01f42dc48..7bb25ef 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -691,10 +691,6 @@
 
 extern const char kRegisteredBackgroundContents[];
 
-#if defined(OS_WIN)
-extern const char kWelcomePageOnOSUpgradeEnabled[];
-#endif
-
 extern const char kAuthSchemes[];
 extern const char kDisableAuthNegotiateCnameLookup[];
 extern const char kEnableAuthNegotiatePort[];
diff --git a/chrome/common/search.mojom b/chrome/common/search.mojom
index 92b70609..0812afc 100644
--- a/chrome/common/search.mojom
+++ b/chrome/common/search.mojom
@@ -75,8 +75,9 @@
   // Tells InstantExtended to toggle between most visited items or custom links.
   ToggleMostVisitedOrCustomLinks(int32 page_seq_no);
 
-  // Tells InstantExtended to toggle visibility of the shortcuts.
-  ToggleShortcutsVisibility(int32 page_seq_no);
+  // Tells InstantExtended to toggle visibility of the shortcuts and notify
+  // observers if |do_notify|.
+  ToggleShortcutsVisibility(int32 page_seq_no, bool do_notify);
 
   // Logs events from InstantExtended New Tab Pages.
   LogEvent(int32 page_seq_no,
diff --git a/chrome/installer/util/master_preferences.h b/chrome/installer/util/master_preferences.h
index 1883271..53d56bd 100644
--- a/chrome/installer/util/master_preferences.h
+++ b/chrome/installer/util/master_preferences.h
@@ -46,8 +46,7 @@
 //      "require_eula": true,
 //      "skip_first_run_ui": true,
 //      "system_level": false,
-//      "verbose_logging": true,
-//      "welcome_page_on_os_upgrade_enabled": false
+//      "verbose_logging": true
 //   },
 //   "browser": {
 //      "show_home_button": true
diff --git a/chrome/installer/util/master_preferences_constants.cc b/chrome/installer/util/master_preferences_constants.cc
index 7ce48427..3ab961a 100644
--- a/chrome/installer/util/master_preferences_constants.cc
+++ b/chrome/installer/util/master_preferences_constants.cc
@@ -12,8 +12,6 @@
       "import_bookmarks_from_file";
   const char kDistroSuppressDefaultBrowserPromptPref[] =
       "suppress_default_browser_prompt_for_version";
-  const char kDistroWelcomePageOnOSUpgradeEnabled[] =
-      "welcome_page_on_os_upgrade_enabled";
   const char kDoNotCreateAnyShortcuts[] = "do_not_create_any_shortcuts";
   const char kDoNotCreateDesktopShortcut[] = "do_not_create_desktop_shortcut";
   const char kDoNotCreateQuickLaunchShortcut[] =
diff --git a/chrome/installer/util/master_preferences_constants.h b/chrome/installer/util/master_preferences_constants.h
index a115f01..832e59e0 100644
--- a/chrome/installer/util/master_preferences_constants.h
+++ b/chrome/installer/util/master_preferences_constants.h
@@ -25,10 +25,6 @@
 // String of Chrome version for which the "set as default browser" infobar will
 // never be shown.
 extern const char kDistroSuppressDefaultBrowserPromptPref[];
-// Boolean that specifies whether or not showing the welcome page following an
-// OS upgrade is enabled. True by default. May be overridden by the
-// WelcomePageOnOSUpgradeEnabled policy setting.
-extern const char kDistroWelcomePageOnOSUpgradeEnabled[];
 // Boolean. Prevent creation of all shortcuts to chrome, including the
 // desktop, quick launch, taskbar and the start menu shortcuts.
 extern const char kDoNotCreateAnyShortcuts[];
diff --git a/chrome/installer/util/master_preferences_unittest.cc b/chrome/installer/util/master_preferences_unittest.cc
index 887f143..9c58b94e 100644
--- a/chrome/installer/util/master_preferences_unittest.cc
+++ b/chrome/installer/util/master_preferences_unittest.cc
@@ -76,7 +76,6 @@
       "  \"distribution\": { \n"
       "     \"show_welcome_page\": true,\n"
       "     \"import_bookmarks_from_file\": \"c:\\\\foo\",\n"
-      "     \"welcome_page_on_os_upgrade_enabled\": true,\n"
       "     \"do_not_create_any_shortcuts\": true,\n"
       "     \"do_not_create_desktop_shortcut\": true,\n"
       "     \"do_not_create_quick_launch_shortcut\": true,\n"
@@ -99,7 +98,6 @@
   EXPECT_TRUE(prefs.read_from_file());
 
   const char* const expected_true[] = {
-      installer::master_preferences::kDistroWelcomePageOnOSUpgradeEnabled,
       installer::master_preferences::kDoNotCreateAnyShortcuts,
       installer::master_preferences::kDoNotCreateDesktopShortcut,
       installer::master_preferences::kDoNotCreateQuickLaunchShortcut,
@@ -154,7 +152,6 @@
   }
 
   const char* const missing_bools[] = {
-    installer::master_preferences::kDistroWelcomePageOnOSUpgradeEnabled,
     installer::master_preferences::kDoNotRegisterForUpdateLaunch,
     installer::master_preferences::kMakeChromeDefault,
     installer::master_preferences::kMakeChromeDefaultForUser,
diff --git a/chrome/renderer/chromeos_delayed_callback_group.h b/chrome/renderer/chromeos_delayed_callback_group.h
index 5fd8ae8..e878546 100644
--- a/chrome/renderer/chromeos_delayed_callback_group.h
+++ b/chrome/renderer/chromeos_delayed_callback_group.h
@@ -6,6 +6,7 @@
 #define CHROME_RENDERER_CHROMEOS_DELAYED_CALLBACK_GROUP_H_
 
 #include <functional>
+#include <list>
 #include <queue>
 
 #include "base/cancelable_callback.h"
@@ -81,7 +82,8 @@
   // Call all remaining callbacks with the RunReason::TIMEOUT parameter value.
   void ExpireAllCallbacks() EXCLUSIVE_LOCKS_REQUIRED(callbacks_lock_);
 
-  std::queue<CallbackEntry> callbacks_ GUARDED_BY(callbacks_lock_);
+  std::queue<CallbackEntry, std::list<CallbackEntry>> callbacks_
+      GUARDED_BY(callbacks_lock_);
   base::TimeDelta expiration_delay_ GUARDED_BY(callbacks_lock_);
   base::CancelableOnceClosure expiration_timeout_;
   mutable base::Lock callbacks_lock_;
diff --git a/chrome/renderer/page_load_metrics/page_resource_data_use.cc b/chrome/renderer/page_load_metrics/page_resource_data_use.cc
index ad71297..fb6f326 100644
--- a/chrome/renderer/page_load_metrics/page_resource_data_use.cc
+++ b/chrome/renderer/page_load_metrics/page_resource_data_use.cc
@@ -118,7 +118,7 @@
   total_received_bytes_ += received_data_length;
 }
 
-bool PageResourceDataUse::DidCompleteResponse(
+void PageResourceDataUse::DidCompleteResponse(
     const network::URLLoaderCompletionStatus& status) {
   // Report the difference in received bytes.
   is_complete_ = true;
@@ -126,9 +126,7 @@
   int64_t delta_bytes = status.encoded_data_length - total_received_bytes_;
   if (delta_bytes > 0) {
     total_received_bytes_ += delta_bytes;
-    return true;
   }
-  return false;
 }
 
 void PageResourceDataUse::DidCancelResponse() {
diff --git a/chrome/renderer/page_load_metrics/page_resource_data_use.h b/chrome/renderer/page_load_metrics/page_resource_data_use.h
index cb94a90d..e11e7c9 100644
--- a/chrome/renderer/page_load_metrics/page_resource_data_use.h
+++ b/chrome/renderer/page_load_metrics/page_resource_data_use.h
@@ -37,8 +37,8 @@
   // Updates received bytes.
   void DidReceiveTransferSizeUpdate(int received_data_length);
 
-  // Updates received bytes from encoded length, returns whether it was updated.
-  bool DidCompleteResponse(const network::URLLoaderCompletionStatus& status);
+  // Updates received bytes from encoded length.
+  void DidCompleteResponse(const network::URLLoaderCompletionStatus& status);
 
   // Flags the resource as canceled.
   void DidCancelResponse();
diff --git a/chrome/renderer/page_load_metrics/page_timing_metrics_sender.cc b/chrome/renderer/page_load_metrics/page_timing_metrics_sender.cc
index d3f7e20..8a1a692 100644
--- a/chrome/renderer/page_load_metrics/page_timing_metrics_sender.cc
+++ b/chrome/renderer/page_load_metrics/page_timing_metrics_sender.cc
@@ -168,9 +168,8 @@
     resource_it = new_resource_it.first;
   }
 
-  if (resource_it->second->DidCompleteResponse(status)) {
-    EnsureSendTimer();
-  }
+  resource_it->second->DidCompleteResponse(status);
+  EnsureSendTimer();
   modified_resources_.insert(resource_it->second.get());
 }
 
diff --git a/chrome/renderer/searchbox/searchbox.cc b/chrome/renderer/searchbox/searchbox.cc
index cedc7d9d..d9511411 100644
--- a/chrome/renderer/searchbox/searchbox.cc
+++ b/chrome/renderer/searchbox/searchbox.cc
@@ -370,8 +370,8 @@
   embedded_search_service_->ToggleMostVisitedOrCustomLinks(page_seq_no_);
 }
 
-void SearchBox::ToggleShortcutsVisibility() {
-  embedded_search_service_->ToggleShortcutsVisibility(page_seq_no_);
+void SearchBox::ToggleShortcutsVisibility(bool do_notify) {
+  embedded_search_service_->ToggleShortcutsVisibility(page_seq_no_, do_notify);
 }
 
 std::string SearchBox::FixupAndValidateUrl(const std::string& url) const {
diff --git a/chrome/renderer/searchbox/searchbox.h b/chrome/renderer/searchbox/searchbox.h
index 049eb541..396bc447 100644
--- a/chrome/renderer/searchbox/searchbox.h
+++ b/chrome/renderer/searchbox/searchbox.h
@@ -140,7 +140,7 @@
   void ToggleMostVisitedOrCustomLinks();
 
   // Sends ToggleShortcutsVisibility to the browser.
-  void ToggleShortcutsVisibility();
+  void ToggleShortcutsVisibility(bool do_notify);
 
   // Attempts to fix obviously invalid URLs. Uses the "https" scheme unless
   // otherwise specified. Returns the fixed URL if valid, otherwise returns an
diff --git a/chrome/renderer/searchbox/searchbox_extension.cc b/chrome/renderer/searchbox/searchbox_extension.cc
index 3a9e576..6990408 100644
--- a/chrome/renderer/searchbox/searchbox_extension.cc
+++ b/chrome/renderer/searchbox/searchbox_extension.cc
@@ -667,7 +667,7 @@
   static void UndoCustomLinkAction();
   static void ResetCustomLinks();
   static void ToggleMostVisitedOrCustomLinks();
-  static void ToggleShortcutsVisibility();
+  static void ToggleShortcutsVisibility(bool do_notify);
   static std::string FixupAndValidateUrl(const std::string& url);
   static void LogEvent(int event);
   static void LogSuggestionEventWithValue(int event, int data);
@@ -1008,11 +1008,11 @@
 }
 
 // static
-void NewTabPageBindings::ToggleShortcutsVisibility() {
+void NewTabPageBindings::ToggleShortcutsVisibility(bool do_notify) {
   SearchBox* search_box = GetSearchBoxForCurrentContext();
   if (!search_box)
     return;
-  search_box->ToggleShortcutsVisibility();
+  search_box->ToggleShortcutsVisibility(do_notify);
 }
 
 // static
diff --git a/chrome/service/service_process.cc b/chrome/service/service_process.cc
index ba4dbd0..dba3432 100644
--- a/chrome/service/service_process.cc
+++ b/chrome/service/service_process.cc
@@ -48,7 +48,6 @@
 #include "components/prefs/json_pref_store.h"
 #include "mojo/core/embedder/embedder.h"
 #include "mojo/core/embedder/scoped_ipc_support.h"
-#include "mojo/public/cpp/platform/features.h"
 #include "net/base/network_change_notifier.h"
 #include "net/url_request/url_fetcher.h"
 #include "services/network/public/cpp/network_switches.h"
@@ -350,11 +349,7 @@
   mojo::PlatformChannelServerEndpoint server_endpoint;
 #if defined(OS_MACOSX)
   // Mach receive rights (named server channels) are not Clone-able.
-  if (base::FeatureList::IsEnabled(mojo::features::kMojoChannelMac)) {
-    server_endpoint = std::move(server_endpoint_);
-  } else {
-    server_endpoint = server_endpoint_.Clone();
-  }
+  server_endpoint = std::move(server_endpoint_);
 #elif defined(OS_POSIX)
   server_endpoint = server_endpoint_.Clone();
 #elif defined(OS_WIN)
diff --git a/chrome/services/cups_proxy/cups_proxy_service_delegate.h b/chrome/services/cups_proxy/cups_proxy_service_delegate.h
index 0db3263..61837815 100644
--- a/chrome/services/cups_proxy/cups_proxy_service_delegate.h
+++ b/chrome/services/cups_proxy/cups_proxy_service_delegate.h
@@ -13,6 +13,8 @@
 #include "base/memory/weak_ptr.h"
 #include "chromeos/printing/printer_configuration.h"
 
+#include "base/task/post_task.h"
+
 namespace chromeos {
 namespace printing {
 
@@ -34,6 +36,7 @@
   virtual base::Optional<chromeos::Printer> GetPrinter(
       const std::string& id) = 0;
   virtual bool IsPrinterInstalled(const Printer& printer) = 0;
+  virtual scoped_refptr<base::SingleThreadTaskRunner> GetIOTaskRunner() = 0;
 
   // |cb| will be run on this delegate's sequenced context.
   virtual void SetupPrinter(const Printer& printer,
diff --git a/chrome/services/cups_proxy/fake_cups_proxy_service_delegate.cc b/chrome/services/cups_proxy/fake_cups_proxy_service_delegate.cc
index 7685271..aee7401 100644
--- a/chrome/services/cups_proxy/fake_cups_proxy_service_delegate.cc
+++ b/chrome/services/cups_proxy/fake_cups_proxy_service_delegate.cc
@@ -20,6 +20,11 @@
   return false;
 }
 
+scoped_refptr<base::SingleThreadTaskRunner>
+FakeCupsProxyServiceDelegate::GetIOTaskRunner() {
+  return nullptr;
+}
+
 void FakeCupsProxyServiceDelegate::SetupPrinter(const Printer& printer,
                                                 PrinterSetupCallback cb) {}
 
diff --git a/chrome/services/cups_proxy/fake_cups_proxy_service_delegate.h b/chrome/services/cups_proxy/fake_cups_proxy_service_delegate.h
index c6a2085..7ef8e84b 100644
--- a/chrome/services/cups_proxy/fake_cups_proxy_service_delegate.h
+++ b/chrome/services/cups_proxy/fake_cups_proxy_service_delegate.h
@@ -25,6 +25,7 @@
   std::vector<chromeos::Printer> GetPrinters() override;
   base::Optional<chromeos::Printer> GetPrinter(const std::string& id) override;
   bool IsPrinterInstalled(const Printer& printer) override;
+  scoped_refptr<base::SingleThreadTaskRunner> GetIOTaskRunner() override;
   void SetupPrinter(const Printer& printer, PrinterSetupCallback cb) override;
 };
 
diff --git a/chrome/services/cups_proxy/socket_manager.cc b/chrome/services/cups_proxy/socket_manager.cc
index db4a11b..b2f8bc7c 100644
--- a/chrome/services/cups_proxy/socket_manager.cc
+++ b/chrome/services/cups_proxy/socket_manager.cc
@@ -83,7 +83,8 @@
 class SocketManagerImpl : public SocketManager {
  public:
   explicit SocketManagerImpl(
-      std::unique_ptr<net::UnixDomainClientSocket> socket);
+      std::unique_ptr<net::UnixDomainClientSocket> socket,
+      base::WeakPtr<chromeos::printing::CupsProxyServiceDelegate> delegate);
   ~SocketManagerImpl() override;
 
   void ProxyToCups(std::vector<uint8_t> request,
@@ -106,10 +107,10 @@
   void Fail(const char* error_message);
 
   // Sequence this manager runs on, |in_flight_->cb_| is posted here.
-  scoped_refptr<base::SequencedTaskRunner> main_runner_;
+  const scoped_refptr<base::SequencedTaskRunner> main_runner_;
 
   // Single thread task runner the thread-affine |socket_| runs on.
-  scoped_refptr<base::SingleThreadTaskRunner> socket_runner_;
+  const scoped_refptr<base::SingleThreadTaskRunner> socket_runner_;
 
   // Created in sequence but accessed and deleted on IO thread.
   std::unique_ptr<SocketRequest> in_flight_;
@@ -125,13 +126,12 @@
 SocketRequest::~SocketRequest() = default;
 
 SocketManagerImpl::SocketManagerImpl(
-    std::unique_ptr<net::UnixDomainClientSocket> socket)
-    : socket_(std::move(socket)), weak_factory_(this) {
-  socket_runner_ =
-      base::CreateSingleThreadTaskRunnerWithTraits(base::TaskTraits());
-  DETACH_FROM_SEQUENCE(sequence_checker_);
-}
-
+    std::unique_ptr<net::UnixDomainClientSocket> socket,
+    base::WeakPtr<chromeos::printing::CupsProxyServiceDelegate> delegate)
+    : main_runner_(base::SequencedTaskRunnerHandle::Get()),
+      socket_runner_(delegate->GetIOTaskRunner()),
+      socket_(std::move(socket)),
+      weak_factory_(this) {}
 SocketManagerImpl::~SocketManagerImpl() {}
 
 void SocketManagerImpl::ProxyToCups(std::vector<uint8_t> request,
@@ -139,11 +139,6 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!in_flight_);  // Only handles one request at a time.
 
-  // Lazily save the main runner, needed to post |cb| to.
-  if (!main_runner_) {
-    main_runner_ = base::SequencedTaskRunnerHandle::Get();
-  }
-
   // Save request.
   in_flight_ = std::make_unique<SocketRequest>();
   in_flight_->cb = std::move(cb);
@@ -286,15 +281,19 @@
 
 }  // namespace
 
-std::unique_ptr<SocketManager> SocketManager::Create() {
+std::unique_ptr<SocketManager> SocketManager::Create(
+    base::WeakPtr<chromeos::printing::CupsProxyServiceDelegate> delegate) {
   return std::make_unique<SocketManagerImpl>(
       std::make_unique<net::UnixDomainClientSocket>(
-          kCupsSocketPath, false /* not abstract namespace */));
+          kCupsSocketPath, false /* not abstract namespace */),
+      std::move(delegate));
 }
 
 std::unique_ptr<SocketManager> SocketManager::CreateForTesting(
-    std::unique_ptr<net::UnixDomainClientSocket> socket) {
-  return std::make_unique<SocketManagerImpl>(std::move(socket));
+    std::unique_ptr<net::UnixDomainClientSocket> socket,
+    base::WeakPtr<chromeos::printing::CupsProxyServiceDelegate> delegate) {
+  return std::make_unique<SocketManagerImpl>(std::move(socket),
+                                             std::move(delegate));
 }
 
 }  // namespace cups_proxy
diff --git a/chrome/services/cups_proxy/socket_manager.h b/chrome/services/cups_proxy/socket_manager.h
index 04d98d80e..3221fe35 100644
--- a/chrome/services/cups_proxy/socket_manager.h
+++ b/chrome/services/cups_proxy/socket_manager.h
@@ -11,6 +11,8 @@
 #include "base/callback.h"
 #include "base/optional.h"
 
+#include "chrome/services/cups_proxy/cups_proxy_service_delegate.h"
+
 namespace net {
 class UnixDomainClientSocket;
 }  // namespace net
@@ -21,16 +23,18 @@
     base::OnceCallback<void(base::Optional<std::vector<uint8_t>>)>;
 
 // This manager proxies IPP requests to the CUPS daemon and asynchronously
-// responds with the IPP response. This class can be created anywhere but must
-// be accessed from a sequenced context.
+// responds with the IPP response. This class must be created and accessed
+// from a sequenced context.
 class SocketManager {
  public:
   // Factory function.
-  static std::unique_ptr<SocketManager> Create();
+  static std::unique_ptr<SocketManager> Create(
+      base::WeakPtr<chromeos::printing::CupsProxyServiceDelegate> delegate);
 
   // Factory function that allows injected dependencies, for testing.
   static std::unique_ptr<SocketManager> CreateForTesting(
-      std::unique_ptr<net::UnixDomainClientSocket> socket);
+      std::unique_ptr<net::UnixDomainClientSocket> socket,
+      base::WeakPtr<chromeos::printing::CupsProxyServiceDelegate> delegate);
 
   virtual ~SocketManager() = default;
 
diff --git a/chrome/services/cups_proxy/socket_manager_unittest.cc b/chrome/services/cups_proxy/socket_manager_unittest.cc
index 0ef2175c..b79b38d 100644
--- a/chrome/services/cups_proxy/socket_manager_unittest.cc
+++ b/chrome/services/cups_proxy/socket_manager_unittest.cc
@@ -8,14 +8,15 @@
 
 #include "base/files/file_util.h"
 #include "base/path_service.h"
+#include "base/test/scoped_task_environment.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/threading/thread_restrictions.h"
 #include "chrome/common/chrome_paths.h"
+#include "chrome/services/cups_proxy/fake_cups_proxy_service_delegate.h"
 #include "chrome/services/cups_proxy/public/cpp/type_conversions.h"
 #include "chrome/services/cups_proxy/socket_manager.h"
 #include "net/base/io_buffer.h"
 #include "net/socket/unix_domain_client_socket_posix.h"
-#include "net/test/test_with_scoped_task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace cups_proxy {
@@ -50,6 +51,20 @@
 
 }  // namespace
 
+// Fake delegate granting handle to an IO-thread task runner.
+class FakeServiceDelegate
+    : public chromeos::printing::FakeCupsProxyServiceDelegate {
+ public:
+  FakeServiceDelegate() = default;
+  ~FakeServiceDelegate() override = default;
+
+  // Note: Can't simulate actual IO thread in unit_tests, so we serve an
+  // arbitrary SingleThreadTaskRunner.
+  scoped_refptr<base::SingleThreadTaskRunner> GetIOTaskRunner() override {
+    return base::CreateSingleThreadTaskRunner({});
+  }
+};
+
 // Gives full control over the "CUPS daemon" in this test.
 class FakeSocket : public net::UnixDomainClientSocket {
  public:
@@ -158,11 +173,14 @@
 
 class SocketManagerTest : public testing::Test {
  public:
-  SocketManagerTest() : weak_factory_(this) {
+  SocketManagerTest() {
+    delegate_ = std::make_unique<FakeServiceDelegate>();
+
     std::unique_ptr<FakeSocket> socket = std::make_unique<FakeSocket>();
     socket_ = socket.get();
 
-    manager_ = SocketManager::CreateForTesting(std::move(socket));
+    manager_ = SocketManager::CreateForTesting(std::move(socket),
+                                               delegate_->GetWeakPtr());
   }
 
   base::Optional<std::vector<uint8_t>> ProxyToCups(std::string request) {
@@ -190,11 +208,14 @@
     std::move(finish_cb).Run();
   }
 
+  // Fake injected service delegate.
+  std::unique_ptr<FakeServiceDelegate> delegate_;
+
   // Not owned.
   FakeSocket* socket_;
 
   std::unique_ptr<SocketManager> manager_;
-  base::WeakPtrFactory<SocketManagerTest> weak_factory_;
+  base::WeakPtrFactory<SocketManagerTest> weak_factory_{this};
 };
 
 // "basic_handshake" test file contains a simple HTTP request sent by libCUPS,
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index fbeca9e..8fcd94db 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1058,6 +1058,7 @@
       "../browser/ui/passwords/google_password_manager_navigation_throttle_browsertest.cc",
       "../browser/ui/tabs/pinned_tab_service_browsertest.cc",
       "../browser/ui/views/tabs/tab_hover_card_bubble_view_browsertest.cc",
+      "../browser/wake_lock/wake_lock_browsertest.cc",
 
       # If this list is used on Android in the future, these browser / speech/*
       # files will probably not be applicable.
@@ -1534,7 +1535,6 @@
         "../browser/extensions/api/metrics_private/metrics_apitest.cc",
         "../browser/extensions/api/module/module_apitest.cc",
         "../browser/extensions/api/networking_config_chromeos_apitest_chromeos.cc",
-        "../browser/extensions/api/omnibox/omnibox_api_browsertest.cc",
         "../browser/extensions/api/page_capture/page_capture_apitest.cc",
         "../browser/extensions/api/passwords_private/passwords_private_apitest.cc",
         "../browser/extensions/api/permissions/permissions_apitest.cc",
@@ -1811,6 +1811,7 @@
         "../browser/ui/views/payments/payment_request_debit_browsertest.cc",
         "../browser/ui/views/payments/payment_request_has_enrolled_instrument_browsertest.cc",
         "../browser/ui/views/payments/payment_request_journey_logger_browsertest.cc",
+        "../browser/ui/views/payments/payment_request_missing_fields_metrics_browsertest.cc",
         "../browser/ui/views/payments/payment_request_no_update_with_browsertest.cc",
         "../browser/ui/views/payments/payment_request_payment_app_browsertest.cc",
         "../browser/ui/views/payments/payment_request_payment_response_browsertest.cc",
@@ -3395,6 +3396,7 @@
   if (enable_offline_pages) {
     sources += [
       "../browser/offline_pages/android/auto_fetch_page_load_watcher_unittest.cc",
+      "../browser/offline_pages/android/offline_page_archive_publisher_impl_unittest.cc",
       "../browser/offline_pages/android/offline_page_auto_fetcher_service_unittest.cc",
       "../browser/offline_pages/background_loader_offliner_unittest.cc",
       "../browser/offline_pages/download_archive_manager_unittest.cc",
@@ -5294,7 +5296,6 @@
     ldflags = []
 
     deps = [
-      ":captured_sites_interactive_tests",
       ":test_support",
       ":test_support_ui",
       "//chrome:packed_resources",
diff --git a/chrome/test/android/BUILD.gn b/chrome/test/android/BUILD.gn
index f0c64b2a0..a55994b4 100644
--- a/chrome/test/android/BUILD.gn
+++ b/chrome/test/android/BUILD.gn
@@ -7,12 +7,21 @@
 android_library("chrome_java_test_pagecontroller") {
   testonly = true
   java_files = [
+    "javatests/src/org/chromium/chrome/test/pagecontroller/controllers/ElementController.java",
+    "javatests/src/org/chromium/chrome/test/pagecontroller/controllers/PageController.java",
+    "javatests/src/org/chromium/chrome/test/pagecontroller/controllers/first_run/DataSaverController.java",
+    "javatests/src/org/chromium/chrome/test/pagecontroller/controllers/first_run/SyncController.java",
+    "javatests/src/org/chromium/chrome/test/pagecontroller/controllers/first_run/TOSController.java",
+    "javatests/src/org/chromium/chrome/test/pagecontroller/controllers/ntp/NewTabPageController.java",
+    "javatests/src/org/chromium/chrome/test/pagecontroller/rules/ChromeUiApplicationTestRule.java",
+    "javatests/src/org/chromium/chrome/test/pagecontroller/rules/ChromeUiAutomatorTestRule.java",
     "javatests/src/org/chromium/chrome/test/pagecontroller/utils/BySelectorIndexUi2Locator.java",
     "javatests/src/org/chromium/chrome/test/pagecontroller/utils/BySelectorUi2Locator.java",
     "javatests/src/org/chromium/chrome/test/pagecontroller/utils/ChildIndexUi2Locator.java",
     "javatests/src/org/chromium/chrome/test/pagecontroller/utils/IUi2Locator.java",
     "javatests/src/org/chromium/chrome/test/pagecontroller/utils/PathUi2Locator.java",
     "javatests/src/org/chromium/chrome/test/pagecontroller/utils/Ui2Locators.java",
+    "javatests/src/org/chromium/chrome/test/pagecontroller/utils/UiAutomatorUtils.java",
     "javatests/src/org/chromium/chrome/test/pagecontroller/utils/UiLocationException.java",
     "javatests/src/org/chromium/chrome/test/pagecontroller/utils/UiLocatorHelper.java",
     "javatests/src/org/chromium/chrome/test/pagecontroller/utils/Utils.java",
@@ -21,10 +30,32 @@
     "//base:base_java",
     "//base:base_java_test_support",
     "//third_party/android_support_test_runner:runner_java",
+    "//third_party/junit",
     "//third_party/ub-uiautomator:ub_uiautomator_java",
   ]
 }
 
+instrumentation_test_apk("chrome_java_test_pagecontroller_tests") {
+  apk_name = "ChromePageControllerTests"
+  apk_under_test = "//chrome/android:chrome_public_apk"
+  android_manifest = "javatests/src/org/chromium/chrome/test/pagecontroller/tests/AndroidManifest.xml"
+  target_sdk_version = 28
+  testonly = true
+  java_files = [
+    "javatests/src/org/chromium/chrome/test/pagecontroller/tests/ExampleTest.java",
+    "javatests/src/org/chromium/chrome/test/pagecontroller/tests/FirstRunControllerTest.java",
+  ]
+  deps = [
+    ":chrome_java_test_pagecontroller",
+    "//third_party/junit",
+  ]
+
+  if (!is_java_debug) {
+    proguard_enabled = true
+    proguard_configs = [ "//chrome/android/java/apk_for_test.flags" ]
+  }
+}
+
 junit_binary("chrome_java_test_pagecontroller_unit_tests") {
   testonly = true
   java_files = [
diff --git a/chrome/test/android/browsertests_apk/src/org/chromium/android_browsertests_apk/PaymentRequestTestBridge.java b/chrome/test/android/browsertests_apk/src/org/chromium/android_browsertests_apk/PaymentRequestTestBridge.java
index 0bbb440..2bf6d89 100644
--- a/chrome/test/android/browsertests_apk/src/org/chromium/android_browsertests_apk/PaymentRequestTestBridge.java
+++ b/chrome/test/android/browsertests_apk/src/org/chromium/android_browsertests_apk/PaymentRequestTestBridge.java
@@ -28,13 +28,16 @@
         private final boolean mIsIncognito;
         private final boolean mIsValidSsl;
         private final boolean mIsWebContentsActive;
+        private final boolean mPrefsCanMakePayment;
         private final boolean mSkipUiForBasicCard;
 
         PaymentRequestDelegateForTest(boolean isIncognito, boolean isValidSsl,
-                boolean isWebContentsActive, boolean skipUiForBasicCard) {
+                boolean isWebContentsActive, boolean prefsCanMakePayment,
+                boolean skipUiForBasicCard) {
             mIsIncognito = isIncognito;
             mIsValidSsl = isValidSsl;
             mIsWebContentsActive = isWebContentsActive;
+            mPrefsCanMakePayment = prefsCanMakePayment;
             mSkipUiForBasicCard = skipUiForBasicCard;
         }
 
@@ -55,6 +58,11 @@
         }
 
         @Override
+        public boolean prefsCanMakePayment() {
+            return mPrefsCanMakePayment;
+        }
+
+        @Override
         public boolean skipUiForBasicCard() {
             return false;
         }
@@ -122,10 +130,11 @@
 
     @CalledByNative
     public static void setUseDelegateForTest(boolean useDelegate, boolean isIncognito,
-            boolean isValidSsl, boolean isWebContentsActive, boolean skipUiForBasicCard) {
+            boolean isValidSsl, boolean isWebContentsActive, boolean prefsCanMakePayment,
+            boolean skipUiForBasicCard) {
         if (useDelegate) {
-            PaymentRequestFactory.sDelegateForTest = new PaymentRequestDelegateForTest(
-                    isIncognito, isValidSsl, isWebContentsActive, skipUiForBasicCard);
+            PaymentRequestFactory.sDelegateForTest = new PaymentRequestDelegateForTest(isIncognito,
+                    isValidSsl, isWebContentsActive, prefsCanMakePayment, skipUiForBasicCard);
         } else {
             PaymentRequestFactory.sDelegateForTest = null;
         }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/README.md b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/README.md
new file mode 100644
index 0000000..cf0468c
--- /dev/null
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/README.md
@@ -0,0 +1,62 @@
+# Chrome's Page Controller Test Library
+
+## Introduction
+
+Page Controllers simplify the task of writing and maintaining integration tests
+by organizing the logic to interact with the various application UI components
+into re-usable classes.  Learn how to write and use Page Controllers to solve
+your testing needs.
+
+## File Organization
+
+[controllers](https://cs.chromium.org/chromium/src/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/): Contains all the Page Controllers.<br/>
+[rules](https://cs.chromium.org/chromium/src/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/rules/): Junit Test Rules that provide access to the Page Controllers in a
+test case.<br/>
+[tests](https://cs.chromium.org/chromium/src/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/tests/): Tests for the Page Controllers themselves.<br/>
+[utils](https://cs.chromium.org/chromium/src/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/utils/): Utility classes that are useful for writing Page Controllers.<br/>
+
+## Writing Testcases
+
+See the [ExampleTest](https://cs.chromium.org/chromium/src/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/tests/ExampleTest.java)
+
+## Creating + Updating Page Controllers
+
+Currently the Page Controllers use UIAutomator under-the-hood.  But it is
+entirely possible to write methods or whole page controllers using Espresso or
+some other driver framework.
+
+```
+/**
+ * Page controller for CoolNewPage
+ */
+class CoolNewPageController extends PageController {
+    // Locators allow the controller to find UI elements on the page
+    // It is preferred to use Resource Ids to find elements since they are
+    // stable across minor UI changes.
+    private static final IUi2Locator LOCATOR_COOL_PAGE = Ui2Locators.withResIds("cool_page");
+    // Any of the resource ids in the list will result in a match.
+    private static final IUi2Locator LOCATOR_COOL_BUTTON = Ui2Locators.withResIds("cool_button_v1", "cool_button_v2");
+
+    public CoolerPageController clickButton() {
+        // [UiAutomatorUtils.click](https://cs.chromium.org/chromium/src/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/utils/UiAutomatorUtils.java?q=click) operates on UI elements via IUi2Locators.
+        // In general, methods that operate on IUi2Locators will throw if that
+        // locator could not find an UI elements on the page.
+        // UiAutomatorUtils has retry functionality with a configurable timeout,
+        // so that flakiness can be drastically reduced.
+        mUtils.click(LOCATOR_SOME_BUTTON);
+
+        // If clicking on cool button should always result in the app going
+        // to the EvenCoolerPage, then the action method should return an
+        // instance of that controller via its assertIsCurrentPage() method.
+        // This ensures the UI state is synced upon the return of the method.
+        return EvenCoolerPageController.getInstance().assertIsCurrentPage();
+    }
+
+    // All page controllers must implement this method.
+    @Override
+    public SomePageController assertIsCurrentPage() {
+        mLocatorHelper.get(LOCATOR_SOME_PAGE);  // Throws if not found
+        return this;
+    }
+}
+```
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/ElementController.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/ElementController.java
new file mode 100644
index 0000000..68344e2
--- /dev/null
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/ElementController.java
@@ -0,0 +1,22 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.test.pagecontroller.controllers;
+
+import org.chromium.chrome.test.pagecontroller.utils.UiAutomatorUtils;
+import org.chromium.chrome.test.pagecontroller.utils.UiLocatorHelper;
+
+/**
+ * Base class for a Page Controller or Page Element Controller.
+ * Allows Controllers to perform UI actions and verify UI state.
+ */
+public class ElementController {
+    protected final UiAutomatorUtils mUtils;
+    protected final UiLocatorHelper mLocatorHelper;
+
+    public ElementController() {
+        mUtils = UiAutomatorUtils.getInstance();
+        mLocatorHelper = mUtils.getLocatorHelper();
+    }
+}
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/PageController.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/PageController.java
new file mode 100644
index 0000000..db4e676
--- /dev/null
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/PageController.java
@@ -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.
+
+package org.chromium.chrome.test.pagecontroller.controllers;
+
+import android.os.RemoteException;
+
+/**
+ * Base class for page controllers.
+ * A page controller allows tests to interact with a single page (think Android activity)
+ * in the app-under-test.
+ */
+public abstract class PageController extends ElementController {
+    public void pressAndroidBackButton() {
+        mUtils.pressBack();
+    }
+
+    public void pressAndroidHomeButton() {
+        mUtils.pressHome();
+    }
+
+    public void pressAndroidOverviewButton() {
+        try {
+            // UiDevice (used by UiAutomatorUtils) calls this the recent apps button,
+            // Android UI seems to prefer the overview button name (as evidenced by talkback's
+            // readout)
+            mUtils.pressRecentApps();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Checks if the current page displayed corresponds to this page controller.
+     * @return True if current page can be controlled by this controller, else false.
+     */
+    public abstract boolean isCurrentPageThis();
+}
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/first_run/DataSaverController.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/first_run/DataSaverController.java
new file mode 100644
index 0000000..2fb8e1d2
--- /dev/null
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/first_run/DataSaverController.java
@@ -0,0 +1,33 @@
+// 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.test.pagecontroller.controllers.first_run;
+
+import org.chromium.chrome.test.pagecontroller.controllers.PageController;
+import org.chromium.chrome.test.pagecontroller.utils.IUi2Locator;
+import org.chromium.chrome.test.pagecontroller.utils.Ui2Locators;
+
+/**
+ * Data Saver Dialog (part of the First Run Experience) Page Controller.
+ */
+public class DataSaverController extends PageController {
+    private static final IUi2Locator LOCATOR_DATA_SAVER =
+            Ui2Locators.withResIds("enable_data_saver_switch");
+    private static final IUi2Locator LOCATOR_NEXT = Ui2Locators.withResIds("next_button");
+
+    private static DataSaverController sInstance = new DataSaverController();
+    private DataSaverController() {}
+    public static DataSaverController getInstance() {
+        return sInstance;
+    }
+
+    public void clickNext() {
+        mUtils.click(LOCATOR_NEXT);
+    }
+
+    @Override
+    public boolean isCurrentPageThis() {
+        return mLocatorHelper.isOnScreen(LOCATOR_DATA_SAVER);
+    }
+}
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/first_run/SyncController.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/first_run/SyncController.java
new file mode 100644
index 0000000..f4b818b
--- /dev/null
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/first_run/SyncController.java
@@ -0,0 +1,33 @@
+// 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.test.pagecontroller.controllers.first_run;
+
+import org.chromium.chrome.test.pagecontroller.controllers.PageController;
+import org.chromium.chrome.test.pagecontroller.utils.IUi2Locator;
+import org.chromium.chrome.test.pagecontroller.utils.Ui2Locators;
+
+/**
+ * Sync Dialog (part of First Run Experience) Page Controller.
+ */
+public class SyncController extends PageController {
+    private final static IUi2Locator LOCATOR_SYNC_CONTROLLER =
+            Ui2Locators.withResIds("signin_sync_title");
+    private final static IUi2Locator LOCATOR_NO_THANKS = Ui2Locators.withResIds("negative_button");
+
+    private static SyncController sInstance = new SyncController();
+    private SyncController() {}
+    public static SyncController getInstance() {
+        return sInstance;
+    }
+
+    public void clickNoThanks() {
+        mUtils.click(LOCATOR_NO_THANKS);
+    }
+
+    @Override
+    public boolean isCurrentPageThis() {
+        return mLocatorHelper.isOnScreen(LOCATOR_SYNC_CONTROLLER);
+    }
+}
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/first_run/TOSController.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/first_run/TOSController.java
new file mode 100644
index 0000000..f4965abf
--- /dev/null
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/first_run/TOSController.java
@@ -0,0 +1,43 @@
+// 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.test.pagecontroller.controllers.first_run;
+
+import org.chromium.chrome.test.pagecontroller.controllers.PageController;
+import org.chromium.chrome.test.pagecontroller.utils.IUi2Locator;
+import org.chromium.chrome.test.pagecontroller.utils.Ui2Locators;
+
+/**
+ * TOS Dialog (part of the First Run Experience) Page Controller.
+ */
+public class TOSController extends PageController {
+    private static final IUi2Locator LOCATOR_TOS = Ui2Locators.withResIds("tos_and_privacy");
+    private static final IUi2Locator LOCATOR_SEND_REPORT_CHECKBOX =
+            Ui2Locators.withResIds("send_report_checkbox");
+    private static final IUi2Locator LOCATOR_ACCEPT = Ui2Locators.withResIds("terms_accept");
+
+    private static TOSController sInstance = new TOSController();
+    private TOSController() {}
+    public static TOSController getInstance() {
+        return sInstance;
+    }
+
+    @Override
+    public boolean isCurrentPageThis() {
+        return mLocatorHelper.isOnScreen(LOCATOR_TOS);
+    }
+
+    public boolean isSendingReports() {
+        return mLocatorHelper.getOneChecked(LOCATOR_SEND_REPORT_CHECKBOX);
+    }
+
+    public TOSController toggleSendReports() {
+        mUtils.click(LOCATOR_SEND_REPORT_CHECKBOX, LOCATOR_TOS);
+        return this;
+    }
+
+    public void acceptAndContinue() {
+        mUtils.click(LOCATOR_ACCEPT);
+    }
+}
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/ntp/NewTabPageController.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/ntp/NewTabPageController.java
new file mode 100644
index 0000000..8d028b8
--- /dev/null
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/ntp/NewTabPageController.java
@@ -0,0 +1,28 @@
+// 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.test.pagecontroller.controllers.ntp;
+
+import org.chromium.chrome.test.pagecontroller.controllers.PageController;
+import org.chromium.chrome.test.pagecontroller.utils.IUi2Locator;
+import org.chromium.chrome.test.pagecontroller.utils.Ui2Locators;
+
+/**
+ * New Tab Page Page Controller, handles either Feed or Zine implementations.
+ */
+public class NewTabPageController extends PageController {
+    private static final IUi2Locator LOCATOR_NEW_TAB_PAGE =
+            Ui2Locators.withResIds("ntp_content", "feed_stream_recycler_view", "card_contents");
+
+    private static NewTabPageController sInstance = new NewTabPageController();
+    private NewTabPageController() {}
+    public static NewTabPageController getInstance() {
+        return sInstance;
+    }
+
+    @Override
+    public boolean isCurrentPageThis() {
+        return mLocatorHelper.isOnScreen(LOCATOR_NEW_TAB_PAGE);
+    }
+}
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/rules/ChromeUiApplicationTestRule.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/rules/ChromeUiApplicationTestRule.java
new file mode 100644
index 0000000..68b39b5
--- /dev/null
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/rules/ChromeUiApplicationTestRule.java
@@ -0,0 +1,59 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.test.pagecontroller.rules;
+
+import android.support.test.InstrumentationRegistry;
+
+import org.junit.rules.ExternalResource;
+
+import org.chromium.chrome.test.pagecontroller.controllers.PageController;
+import org.chromium.chrome.test.pagecontroller.utils.UiAutomatorUtils;
+import org.chromium.chrome.test.pagecontroller.utils.UiLocationException;
+
+/**
+ * Test rule that provides access to a Chrome application.
+ */
+public class ChromeUiApplicationTestRule extends ExternalResource {
+    // TODO(aluo): Adjust according to https://crrev.com/c/1585142.
+    private static final String PACKAGE_NAME_ARG = "PackageUnderTest";
+
+    private String mPackageName;
+
+    /**
+     * Returns an instance of the page controller that corresponds to the current page.
+     * @param controllers      List of possible page controller instances to search among.
+     * @return                 The detected page controller.
+     * @throws UiLocationError If page can't be determined.
+     */
+    public static PageController detectPageAmong(PageController... controllers) {
+        for (PageController instance : controllers) {
+            if (instance.isCurrentPageThis()) return instance;
+        }
+        throw UiLocationException.newInstance("Could not detect current Page");
+    }
+
+    /**
+     * Launch the Chrome application.
+     */
+    public void launchApplication() {
+        UiAutomatorUtils utils = UiAutomatorUtils.getInstance();
+        utils.launchApplication(mPackageName);
+    }
+
+    public String getApplicationPackage() {
+        return mPackageName;
+    }
+
+    @Override
+    protected void before() throws Throwable {
+        super.before();
+        mPackageName = InstrumentationRegistry.getArguments().getString(PACKAGE_NAME_ARG);
+        // If the runner didn't pass the package name under test to us, then we can assume
+        // that the target package name provided in the AndroidManifest is the app-under-test.
+        if (mPackageName == null) {
+            mPackageName = InstrumentationRegistry.getTargetContext().getPackageName();
+        }
+    }
+}
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/rules/ChromeUiAutomatorTestRule.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/rules/ChromeUiAutomatorTestRule.java
new file mode 100644
index 0000000..06836e3
--- /dev/null
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/rules/ChromeUiAutomatorTestRule.java
@@ -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.
+
+package org.chromium.chrome.test.pagecontroller.rules;
+
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+
+import org.chromium.base.Log;
+import org.chromium.chrome.test.pagecontroller.utils.UiAutomatorUtils;
+
+/**
+ * Custom Rule that logs useful information for debugging UiAutomator
+ * related issues in the event of a test failure.
+ */
+public class ChromeUiAutomatorTestRule extends TestWatcher {
+    private static final String TAG = "ChromeUiAutomatorTR";
+
+    @Override
+    protected void failed(Throwable e, Description description) {
+        super.failed(e, description);
+        Log.e(TAG, description.toString() + " failed", e);
+        UiAutomatorUtils utils = UiAutomatorUtils.getInstance();
+        utils.printWindowHierarchy("UI hierarchy when " + description.toString() + " failed");
+    }
+}
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/tests/AndroidManifest.xml b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/tests/AndroidManifest.xml
new file mode 100644
index 0000000..f66dde0
--- /dev/null
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/tests/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<!--
+ * Copyright 2019 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+-->
+
+<manifest
+      xmlns:android="http://schemas.android.com/apk/res/android"
+      package="org.chromium.chrome.test.pagecontroller.tests">
+
+    <uses-permission android:name="android.permission.RUN_INSTRUMENTATION" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+    <!-- We add an application tag here just so that we can indicate that this
+         package needs to link against the android.test library, which is
+         needed when building test cases. -->
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="org.chromium.base.test.BaseChromiumAndroidJUnitRunner"
+        android:targetPackage="org.chromium.chrome"
+        android:label="Runner for org.chromium.chrome.test.pagecontroller"/>
+</manifest>
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/tests/ExampleTest.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/tests/ExampleTest.java
new file mode 100644
index 0000000..451b3625
--- /dev/null
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/tests/ExampleTest.java
@@ -0,0 +1,81 @@
+// 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.test.pagecontroller.tests;
+
+import android.support.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.BaseJUnit4ClassRunner;
+import org.chromium.chrome.test.pagecontroller.controllers.PageController;
+import org.chromium.chrome.test.pagecontroller.rules.ChromeUiApplicationTestRule;
+import org.chromium.chrome.test.pagecontroller.rules.ChromeUiAutomatorTestRule;
+import org.chromium.chrome.test.pagecontroller.utils.IUi2Locator;
+import org.chromium.chrome.test.pagecontroller.utils.Ui2Locators;
+
+/**
+ * An example test that demonstrates how to use Page Controllers.
+ */
+@SmallTest
+@RunWith(BaseJUnit4ClassRunner.class)
+public class ExampleTest {
+    // ChromeUiAutomatorTestRule will capture a screen shot and UI Hierarchy info in the event
+    // of a test failure to aid test debugging.
+    public ChromeUiAutomatorTestRule mUiAutomatorRule = new ChromeUiAutomatorTestRule();
+
+    // ChromeUiApplicationTestRule provides a way to launch the Chrome Application under test
+    // and access to the Page Controllers.
+    public ChromeUiApplicationTestRule mChromeUiRule = new ChromeUiApplicationTestRule();
+
+    // The rule chain allows deterministic ordering of test rules so that the
+    // UiAutomatorRule will print debugging information in case of errors
+    // before the application is shut-down.
+    @Rule
+    public final TestRule mChain = RuleChain.outerRule(mChromeUiRule).around(mUiAutomatorRule);
+
+    @Before
+    public void setUp() {
+        // Do any common setup here.
+        mChromeUiRule.launchApplication();
+    }
+
+    @After
+    public void tearDown() {
+        // Do any common cleanup here.
+    }
+
+    @Test
+    public void testPageFound() {
+        PageController controller = new PageController() {
+            @Override
+            public boolean isCurrentPageThis() {
+                IUi2Locator packageLocator =
+                        Ui2Locators.withPackageName(mChromeUiRule.getApplicationPackage());
+                return mLocatorHelper.isOnScreen(packageLocator);
+            }
+        };
+        Assert.assertTrue("Application should have loaded", controller.isCurrentPageThis());
+    }
+
+    @Test
+    public void testPageNotFound() {
+        PageController controller = new PageController() {
+            @Override
+            public boolean isCurrentPageThis() {
+                IUi2Locator packageLocator = Ui2Locators.withPackageName("wrong.package.name");
+                return mLocatorHelper.isOnScreen(packageLocator);
+            }
+        };
+        Assert.assertFalse(
+                "Wrong package should not have been detected", controller.isCurrentPageThis());
+    }
+}
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/tests/FirstRunControllerTest.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/tests/FirstRunControllerTest.java
new file mode 100644
index 0000000..92a2d1f
--- /dev/null
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/tests/FirstRunControllerTest.java
@@ -0,0 +1,46 @@
+// 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.test.pagecontroller.tests;
+
+import android.support.test.filters.SmallTest;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.BaseJUnit4ClassRunner;
+import org.chromium.chrome.test.pagecontroller.controllers.first_run.TOSController;
+import org.chromium.chrome.test.pagecontroller.rules.ChromeUiApplicationTestRule;
+import org.chromium.chrome.test.pagecontroller.rules.ChromeUiAutomatorTestRule;
+
+/**
+ * Test the First Run Experience.
+ */
+@SmallTest
+@RunWith(BaseJUnit4ClassRunner.class)
+public class FirstRunControllerTest {
+    public ChromeUiAutomatorTestRule mUiAutomatorRule = new ChromeUiAutomatorTestRule();
+    public ChromeUiApplicationTestRule mChromeUiRule = new ChromeUiApplicationTestRule();
+
+    @Rule
+    public final TestRule mChain = RuleChain.outerRule(mChromeUiRule).around(mUiAutomatorRule);
+
+    private TOSController mTOSController;
+
+    @Before
+    public void setUp() {
+        mChromeUiRule.launchApplication();
+        mTOSController = TOSController.getInstance();
+    }
+
+    @Test
+    public void testFirstRunIsShown() {
+        Assert.assertTrue("TOS page should be shown", mTOSController.isCurrentPageThis());
+    }
+}
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/utils/UiAutomatorUtils.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/utils/UiAutomatorUtils.java
new file mode 100644
index 0000000..547dfb66
--- /dev/null
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/utils/UiAutomatorUtils.java
@@ -0,0 +1,492 @@
+// 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.test.pagecontroller.utils;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.Environment;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.RemoteException;
+import android.support.annotation.NonNull;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject2;
+
+import org.chromium.base.Log;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ *  Allows tests to perform UI actions.
+ */
+public class UiAutomatorUtils {
+    private static final String TAG = "UiAutomatorUtils";
+    private static final int SWIPE_STEPS_PER_SECOND = 200;
+    private static final int MAX_SWIPES = 30;
+    private static final float DEFAULT_SWIPE_SECONDS_PER_PAGE = 0.2f;
+    private static final float DEFAULT_SWIPE_SCREEN_FRACTION = 0.6f;
+    private static final long SHORT_CLICK_DURATION = 10L;
+    private static final long LONG_CLICK_DURATION = 1000L;
+    // Give applications more time to launch.
+    private static final long LAUNCH_TIMEOUT_MULTIPLIER = 3L;
+    // 100 steps corresponds to ~1 secs, this was determined
+    // experimentally.  Internally uses UiDevice.drag to simulate
+    // clicking, steps is one of the parameters to drag.
+    public static final int CLICK_STEPS_PER_SECOND = 100;
+
+    private UiDevice mDevice;
+    private UiLocatorHelper mLocatorHelper;
+
+    private static class LazyHolder {
+        static final UiAutomatorUtils sInstance = new UiAutomatorUtils();
+    }
+
+    public static UiAutomatorUtils getInstance() {
+        return LazyHolder.sInstance;
+    }
+
+    private UiAutomatorUtils() {
+        mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+        mLocatorHelper = new UiLocatorHelper();
+    }
+
+    public String getText(UiObject2 root) {
+        return root.getText();
+    }
+
+    /**
+     * Returns the timeout used for location operations.
+     * @return Timeout in milliseconds.
+     */
+    public long getTimeout() {
+        return mLocatorHelper.getTimeout();
+    }
+
+    /**
+     * Set the timeout used for location operations.
+     * @param timeout Timeout in milliseconds.
+     */
+    public void setTimeout(long timeout) {
+        mLocatorHelper.setTimeout(timeout);
+    }
+
+    /**
+     * Launch application.
+     * @param packageName Package name of the application.
+     */
+    public void launchApplication(String packageName) {
+        Log.d(TAG, "Launching " + packageName);
+        launchApplication(packageName, mLocatorHelper.getTimeout() * LAUNCH_TIMEOUT_MULTIPLIER);
+    }
+
+    /**
+     * Stops the application.
+     * @param packageName Package name of the application to stop.
+     */
+    public void stopApplication(String packageName) {
+        Log.d(TAG, "Stopping " + packageName);
+        try {
+            executeShellCommand("am force-stop " + packageName);
+        } catch (IOException e) {
+            Log.d(TAG, "Failed to stop " + packageName);
+            e.printStackTrace();
+        }
+    }
+
+    public void pressBack() {
+        mDevice.pressBack();
+    }
+
+    public void pressHome() {
+        mDevice.pressHome();
+    }
+
+    public void pressRecentApps() throws RemoteException {
+        mDevice.pressRecentApps();
+    }
+
+    /**
+     * Takes device screenshot and saves it to screenShotFile.
+     * @param screenShotFile Where the screenshot should be saved.
+     */
+    public void takeScreenShot(@NonNull File screenShotFile) {
+        if (mDevice.takeScreenshot(screenShotFile)) {
+            Log.d(TAG, "Screenshot successfully saved to " + screenShotFile.getAbsolutePath());
+        } else {
+            Log.e(TAG, "Screenshot unsuccessful " + screenShotFile.getAbsolutePath());
+        }
+    }
+
+    /**
+     * Performs click outside of the UI element found using locator.
+     * The click will be centered in the rectangular screen area with the greatest
+     * width or height that does not overlap with the UI element.
+     * @param locator Locator to use to find the element.
+     */
+    public void clickOutsideOf(@NonNull IUi2Locator locator) {
+        Rect bounds = getBounds(locator);
+        Log.d(TAG,
+                "Clicking outside of bounds with Bottom:" + bounds.bottom + " Top:" + bounds.top
+                        + " Left:" + bounds.left + " Right:" + bounds.right);
+        clickOutsideOfArea(bounds.left, bounds.top, bounds.right, bounds.bottom);
+    }
+
+    public UiLocatorHelper getLocatorHelper() {
+        return mLocatorHelper;
+    }
+
+    /**
+     * Performs a long click on node.
+     * @param locator Locator to use to find the node.
+     */
+    public void longClick(@NonNull IUi2Locator locator) {
+        clickDurationInternal(locator, LONG_CLICK_DURATION);
+    }
+
+    /**
+     * Perform a click.
+     * @param locator  Locator to find the UI element to click on.
+     * @param duration Milliseconds that the click should last for.
+     */
+    private void clickDurationInternal(IUi2Locator locator, long duration) {
+        UiObject2 object2 = mLocatorHelper.getOne(locator);
+        Point center = object2.getVisibleCenter();
+        mDevice.swipe(center.x, center.y, center.x, center.y,
+                (int) (CLICK_STEPS_PER_SECOND * duration / 1000L));
+    }
+
+    /**
+     * Get the rectangular bounds of the first UI element found using locator.
+     * @param locator Locator used to find the UI element.
+     * @return        Bounds of the UI element.
+     */
+    private Rect getBounds(@NonNull IUi2Locator locator) {
+        UiObject2 object2 = mLocatorHelper.getOne(locator);
+        return object2.getVisibleBounds();
+    }
+
+    /**
+     * Copied over from UiAutomator UiDevice v18.0.1, it was removed for some reason, but is useful.
+     * Executes a shell command using shell user identity, and return the standard output in string.
+     * <p>
+     * Calling function with large amount of output will have memory impacts, and the function call
+     * will block if the command executed is blocking.
+     * <p>Note: calling this function requires API level 21 or above
+     * @param cmd Command to run
+     * @return    The standard output of the command
+     * @throws IOException
+     * @since API Level 21
+     */
+    public String executeShellCommand(@NonNull String cmd) throws IOException {
+        ParcelFileDescriptor pfd =
+                InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
+                        cmd);
+        byte[] buf = new byte[512];
+        int bytesRead;
+        FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+        StringBuilder stdout = new StringBuilder();
+        while ((bytesRead = fis.read(buf)) != -1) {
+            stdout.append(new String(buf, 0, bytesRead));
+        }
+        fis.close();
+        return stdout.toString();
+    }
+
+    /**
+     * Click on a node.
+     * @param locator           Locator used to find the node.
+     * @throws  UiLocationError If locator didn't find any nodes within timeout interval.
+     */
+    public void click(@NonNull IUi2Locator locator) {
+        clickDurationInternal(locator, SHORT_CLICK_DURATION);
+    }
+
+    /**
+     * Click on a node and checks for an expected outcome.
+     * @param locator          Locator used to find the node to click on.
+     * @param outcomeLocator   Locator to check for existence after the click.
+     * @throws UiLocationError If locator didn't find any nodes within timeout interval or if
+     *                         provided outcomeLocator didn't find any nodes after the click.
+     */
+    public void click(@NonNull IUi2Locator locator, @NonNull IUi2Locator outcomeLocator) {
+        clickInternal(locator, outcomeLocator);
+    }
+
+    /**
+     * Enters text in a node and press enter.
+     * @param locator          Locator used to find the node.
+     * @param text             The text to enter.
+     * @throws UiLocationError If locator didn't find any nodes within timeout interval.
+     */
+    public void setTextAndEnter(@NonNull IUi2Locator locator, @NonNull String text) {
+        click(locator);
+        UiObject2 root = mLocatorHelper.getOne(locator);
+        root.setText(text);
+        mDevice.pressEnter();
+    }
+
+    /**
+     * Performs the swipe up gesture repeatedly until a locator is found.
+     * @param locator  locator that will stop the swipe if found on screen.
+     * @param stopLocator  locator that will cause an UiLocationError if found before locator.
+     * @throws UiLocationError
+     */
+    public void swipeUpVerticallyUntilFound(IUi2Locator locator, IUi2Locator stopLocator) {
+        swipeVerticallyUntilFound(locator, stopLocator, DEFAULT_SWIPE_SCREEN_FRACTION);
+    }
+
+    /**
+     * Performs the swipe down gesture repeatedly until a locator is found.
+     * @param locator     locator that will stop the swipe if found on screen.
+     * @param stopLocator locator that will cause an UiLocationError if found before locator.
+     * @throws UiLocationError
+     */
+    public void swipeDownVerticallyUntilFound(IUi2Locator locator, IUi2Locator stopLocator) {
+        swipeVerticallyUntilFound(locator, stopLocator, -DEFAULT_SWIPE_SCREEN_FRACTION);
+    }
+
+    /**
+     * Performs a swipe down gesture that's centered on the screen.
+     * If the intention is to scroll to an element, consider using swipeDownVerticallyUntilFound.
+     * @param fractionOfScreen The length of the swipe as a fraction of the screen, with 1 being
+     *                         the max.
+     */
+    public void swipeDownVertically(float fractionOfScreen) {
+        if (fractionOfScreen > 1 || fractionOfScreen <= 0) {
+            throw new IllegalArgumentException("fractionOfScreen must be in the interval [0,1]");
+        }
+        swipeVertically(-fractionOfScreen);
+    }
+
+    /**
+     * Performs a swipe up gesture that's centered on the screen.
+     * If the intention is to scroll to an element, consider using swipeUpVerticallyUntilFound.
+     * @param fractionOfScreen The length of the swipe as a fraction of the screen, with 1 being
+     *                         the max.
+     */
+    public void swipeUpVertically(float fractionOfScreen) {
+        if (fractionOfScreen > 1 || fractionOfScreen <= 0) {
+            throw new IllegalArgumentException("fractionOfScreen must be in the interval [0,1]");
+        }
+        swipeVertically(fractionOfScreen);
+    }
+
+    /**
+     * Performs a swipe gesture between 2 locators.
+     * @param beginLocator Center the start of swipe on beginLocator.
+     * @param endLocator   Center the end of swipe on endLocator.
+     * @param duration     Time the swipe should take in milliseconds.
+     */
+    public void swipe(IUi2Locator beginLocator, IUi2Locator endLocator, float duration) {
+        UiObject2 begin = mLocatorHelper.getOne(beginLocator);
+        UiObject2 end = mLocatorHelper.getOne(endLocator);
+        Point beginPoint = begin.getVisibleCenter();
+        Point endPoint = end.getVisibleCenter();
+        int steps = (int) (duration * SWIPE_STEPS_PER_SECOND / 1000);
+        steps = steps > 0 ? steps : 1;
+        mDevice.swipe(beginPoint.x, beginPoint.y, endPoint.x, endPoint.y, steps);
+    }
+
+    /**
+     * Prints the UiAutomator window hierarchy to logcat.
+     * @param message A leading message for the debug log.
+     */
+    public void printWindowHierarchy(String message) {
+        try {
+            List<String> hierarchy = getWindowHierarchy();
+            Log.d(TAG, message);
+            for (String line : hierarchy) {
+                Log.d(TAG, line);
+            }
+        } catch (IOException e) {
+            // Just log any errors and move on, testing can still continue.
+            Log.e(TAG, "Printing hierarchy", e);
+        }
+    }
+
+    private void launchApplication(String packageName, long timeout) {
+        Context context = InstrumentationRegistry.getContext();
+        final Intent intent = context.getPackageManager().getLaunchIntentForPackage(packageName);
+        if (intent == null) {
+            throw new IllegalStateException("Could not get intent to launch " + packageName
+                    + ", please ensure that it is installed");
+        }
+        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
+        context.startActivity(intent);
+
+        IUi2Locator packageLocator = Ui2Locators.withPackageName(packageName);
+        long oldTimeout = getTimeout();
+        try {
+            setTimeout(timeout);
+            mLocatorHelper.getOne(packageLocator);
+        } finally {
+            setTimeout(oldTimeout);
+        }
+    }
+
+    // positive fraction indicates swipe up
+    private void swipeVerticallyUntilFound(
+            IUi2Locator locator, IUi2Locator stopLocator, float fractionOfScreen) {
+        if (mLocatorHelper.isOnScreen(locator)) return;
+        Utils.sleep(UiLocatorHelper.UI_CHECK_INTERVAL_MS);
+        int iterationsLeft = MAX_SWIPES;
+        while (!mLocatorHelper.isOnScreen(locator) && iterationsLeft-- > 0) {
+            if (mLocatorHelper.isOnScreen(stopLocator))
+                throw UiLocationException.newInstance(
+                        "Did not find locator while swiping to " + stopLocator, locator, null);
+            swipeVertically(fractionOfScreen);
+            Utils.sleep(UiLocatorHelper.UI_CHECK_INTERVAL_MS);
+        }
+        if (!mLocatorHelper.isOnScreen(locator)) {
+            throw UiLocationException.newInstance(
+                    "Did not find locator after swiping for " + MAX_SWIPES + " times", locator,
+                    null);
+        }
+    }
+
+    private List<String> getWindowHierarchy() throws IOException {
+        File tempFile = null;
+        try {
+            tempFile = getTempFile("temp_window_hierarchy", ".txt");
+            mDevice.dumpWindowHierarchy(tempFile.getAbsolutePath());
+            List<String> hierarchy = readAllFromFile(tempFile);
+            List<String> formattedHiearchy = formatXml(hierarchy, 2);
+            return formattedHiearchy;
+        } finally {
+            if (tempFile != null) {
+                tempFile.delete();
+            }
+        }
+    }
+
+    // TODO(aluo): Refactor this to use standard libraries, see if Apache
+    // xml-commons can be used for this.
+    // Formats one-liner xml hierarchy dump into properly indented list of tags
+    // to ease readability in logs.
+    private List<String> formatXml(List<String> inputs, int indentSpaces) {
+        StringBuilder inputBuilder = new StringBuilder();
+        List<String> output = new ArrayList<>();
+
+        for (String line : inputs) {
+            inputBuilder.append(line.trim());
+        }
+
+        String xmlLine = inputBuilder.toString();
+        int indent = 0;
+        StringBuilder lineBuilder = new StringBuilder();
+        int i;
+        for (i = 0; i < xmlLine.length() - 1; i++) {
+            char c = xmlLine.charAt(i);
+            char nextC = xmlLine.charAt(i + 1);
+            if (c == '<') {
+                if (nextC == '/') {
+                    indent--;
+                }
+                lineBuilder.append(new String(new char[indent * indentSpaces]).replace("\0", " "));
+                lineBuilder.append(c);
+                if (nextC != '/') {
+                    indent++;
+                }
+            } else if (c == '>') {
+                lineBuilder.append(c);
+                output.add(lineBuilder.toString());
+                lineBuilder.delete(0, lineBuilder.length());
+            } else if (c == '/' && nextC == '>') {
+                indent--;
+                lineBuilder.append(c);
+            } else {
+                lineBuilder.append(c);
+            }
+        }
+        if (xmlLine.length() > 0) {
+            lineBuilder.append(xmlLine.charAt(i));
+            output.add(lineBuilder.toString());
+        }
+        return output;
+    }
+
+    /**
+     * Swipe screen vertically by fractions of screen height.
+     * @param fractionOfScreen Amount to swipe by, -1 to 1.
+     *                         Negative value swipes down, positive up.
+     */
+    private void swipeVertically(float fractionOfScreen) {
+        int x = mDevice.getDisplayWidth() / 2;
+        int h = mDevice.getDisplayHeight();
+        int startY = h / 2 - (int) (fractionOfScreen / 2f * h);
+        int stopY = startY + (int) (fractionOfScreen * h);
+        int steps = (int) (DEFAULT_SWIPE_SECONDS_PER_PAGE * Math.abs(fractionOfScreen)
+                * SWIPE_STEPS_PER_SECOND);
+        Log.d(TAG,
+                "Swiping vertically from " + stopY + " to " + startY + " in " + steps + " steps");
+        mDevice.swipe(x, stopY, x, startY, steps);
+    }
+
+    private void clickOutsideOfArea(int minX, int minY, int maxX, int maxY) {
+        int screenHeight = mDevice.getDisplayHeight();
+        int screenWidth = mDevice.getDisplayWidth();
+        // Calculate horizontal and vertical margins from rect to screen's edge
+        int leftMargin = minX;
+        int rightMargin = screenWidth - maxX;
+        int topMargin = minY;
+        int bottomMargin = screenHeight - maxY;
+        // Find the biggest margin value (widest or tallest)
+        int maxMargin =
+                Collections.max(Arrays.asList(rightMargin, leftMargin, topMargin, bottomMargin));
+        // Click on the center of the area with the biggest margin value,
+        // the order chosen here is arbitrary in case of a tie.
+        if (maxMargin == rightMargin) {
+            mDevice.click(maxX + rightMargin / 2, screenHeight / 2);
+        } else if (maxMargin == leftMargin) {
+            mDevice.click(leftMargin / 2, screenHeight / 2);
+        } else if (maxMargin == topMargin) {
+            mDevice.click(screenWidth / 2, topMargin / 2);
+        } else if (maxMargin == bottomMargin) {
+            mDevice.click(screenWidth / 2, screenHeight - bottomMargin / 2);
+        }
+    }
+
+    private File getTempFile(String prefix, String suffix) throws IOException {
+        File cacheDir = Environment.getExternalStorageDirectory();
+        Log.d(TAG, "Create temp file in: " + cacheDir);
+        Log.d(TAG, "My user id: " + Process.myUid());
+        return File.createTempFile(prefix, suffix, cacheDir);
+    }
+
+    private void clickInternal(IUi2Locator locator, IUi2Locator outcomeLocator) {
+        clickDurationInternal(locator, SHORT_CLICK_DURATION);
+        if (outcomeLocator != null) {
+            mLocatorHelper.getOne(outcomeLocator);
+        }
+        // If outcomeLocator is not specified, then caller intended not to check the
+        // effect of the click here.
+    }
+
+    private List<String> readAllFromFile(File file) throws IOException {
+        List<String> strings = new ArrayList<>();
+        try (FileInputStream fileStream = new FileInputStream(file);
+                InputStreamReader inputStream = new InputStreamReader(fileStream);
+                BufferedReader streamReader = new BufferedReader(inputStream)) {
+            String line;
+            while ((line = streamReader.readLine()) != null) {
+                strings.add(line);
+            }
+        }
+        Log.d(TAG,
+                "readAllFromFile read " + strings.size() + " lines from " + file.getAbsolutePath());
+        return strings;
+    }
+}
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/utils/UiLocatorHelper.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/utils/UiLocatorHelper.java
index fcae32e..53d78b27 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/utils/UiLocatorHelper.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/utils/UiLocatorHelper.java
@@ -11,8 +11,6 @@
 import android.support.test.uiautomator.UiDevice;
 import android.support.test.uiautomator.UiObject2;
 
-import org.chromium.base.Log;
-
 import java.util.ArrayList;
 import java.util.List;
 
@@ -27,10 +25,10 @@
 public class UiLocatorHelper {
     private static final String TAG = "UiLocatorHelper";
     private static final long DEFAULT_TIMEOUT_MS = 3000L;
-    private static final long DEFAULT_MAX_UI_SETTLE_TIME_MS = 200L;
     // UI_CHECK_INTERVAL_MS is intentionally not modifiable so that longer timeouts
     // don't lead to slowness due to the checking interval being too coarse.
-    private static final long UI_CHECK_INTERVAL_MS = DEFAULT_TIMEOUT_MS / 4L;
+    static final long UI_CHECK_INTERVAL_MS = DEFAULT_TIMEOUT_MS / 4L;
+    private static final long DEFAULT_MAX_UI_SETTLE_TIME_MS = 200L;
 
     private static final ElementConverter<String> CONVERTER_TEXT = object2 -> {
         return object2.getText();
@@ -51,7 +49,7 @@
     private final UiDevice mDevice;
     private long mTimeout;
 
-    public UiLocatorHelper() {
+    UiLocatorHelper() {
         mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
         mTimeout = DEFAULT_TIMEOUT_MS;
     }
@@ -74,7 +72,6 @@
 
     /**
      * Checks and waits if a node is found on the screen.
-     *
      * @param locator Locator used to find the node.
      * @return        true if node is found, false otherwise.
      */
@@ -89,7 +86,6 @@
 
     /**
      * Checks if a node is found on the screen, does not retry.
-     *
      * @param locator Locator used to find the node.
      * @param root    Node to search under, or null if all the nodes should be searched.
      *                Possible root staleness will make retries ineffective, this means
@@ -238,7 +234,6 @@
 
     /**
      * Returns the first element found using locator.
-     *
      * Throws UiLocationError if not found.
      * @param locator Locator to use to find the element.
      * @return        UiObject2
@@ -250,7 +245,6 @@
 
     /**
      * Returns the first element found using locator.
-     *
      * Could return null but does not throw.
      * @param locator Locator to use to find the element.
      * @param root    Search for elements within root, or on the device if null.
@@ -292,7 +286,7 @@
             if (elapsedTime >= mTimeout) {
                 break;
             }
-            sleep(UI_CHECK_INTERVAL_MS);
+            Utils.sleep(UI_CHECK_INTERVAL_MS);
         } while (true);
         throw UiLocationException.newInstance("Could not find any objects after " + elapsedTime
                         + " ms and " + attempts + " attempts",
@@ -319,7 +313,6 @@
 
     /**
      * Delegate to be used with getCustomElements.
-     *
      * @param <T> The type of the element.
      */
     static public interface CustomElementMaker<T> {
@@ -381,7 +374,7 @@
                 // makeElement could throw while going through the list of roots,
                 // so clear out any elements to avoid duplicates and stale ones.
                 elements.clear();
-                sleep(UI_CHECK_INTERVAL_MS);
+                Utils.sleep(UI_CHECK_INTERVAL_MS);
                 // If the next interaction will cause timeout to be exceeded, then
                 // flag isLastAttempt so client can choose to perform a work-around
                 // instead of throwing an exception again.
@@ -432,18 +425,15 @@
         return allT;
     }
 
-    /** Could return empty list or throw StaleObject exception or throw NullPointerException*/
+    /**
+     * Returns all nodes that matched the locator.
+     * Note that StaleObject or NPE may be thrown from this if the UI has gotten into an
+     * inconsistent state, this usually means the caller should retry the operation.
+     * @param locator Locator to use.
+     * @param root    Root node to match under, or null to match on any node.
+     * @return        List of matched nodes, possibly empty.
+     */
     private List<UiObject2> getAllInternal(@NonNull IUi2Locator locator, UiObject2 root) {
         return root == null ? locator.locateAll(mDevice) : locator.locateAll(root);
     }
-
-    private void sleep(long ms) {
-        try {
-            Thread.sleep(ms);
-        } catch (InterruptedException e) {
-            for (StackTraceElement elem : e.getStackTrace()) {
-                Log.e(TAG, elem.toString());
-            }
-        }
-    }
 }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/utils/Utils.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/utils/Utils.java
index 1cb197b..03092b87 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/utils/Utils.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/utils/Utils.java
@@ -8,6 +8,8 @@
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 
+import org.chromium.base.Log;
+
 import java.util.Collections;
 import java.util.List;
 
@@ -16,6 +18,8 @@
  */
 
 public final class Utils {
+    private static final String TAG = "PageController Utils";
+
     /**
      * Calculates the time interval from previousTime to now.
      * @param previousTime  The previousTime, as returned from a previous call to currentTime().
@@ -33,12 +37,33 @@
         return SystemClock.uptimeMillis();
     }
 
-    // Return empty list if t is null, else return a singleton list containing t.
+    /**
+     * Sleeps for ms, logs but does not throw exceptions.
+     * @param ms Milliseconds to sleep for.
+     */
+    public static void sleep(long ms) {
+        try {
+            Thread.sleep(ms);
+        } catch (InterruptedException e) {
+            Log.e(TAG, "Sleep interrupted", e);
+        }
+    }
+
+    /**
+     * Convert singleton to list.
+     * @param t Possibly null singleton.
+     * @return Empty list if t is null, else return a singleton list containing t.
+     */
     public static <T> List<T> nullableIntoList(@Nullable T t) {
         return t == null ? Collections.<T>emptyList() : Collections.singletonList(t);
     }
 
-    // Returns the index-th item in the list or null if it's out of bounds.
+    /**
+     * Convert list and index into singleton.
+     * @param list  List to be traversed.
+     * @param index 0-based index into the list.
+     * @return      The index-th item in the list or null if it's out of bounds.
+     */
     public static @Nullable<T> T nullableGet(@NonNull List<T> list, int index) {
         return index >= list.size() ? null : list.get(index);
     }
diff --git a/chrome/test/base/android/payment_request_test_bridge.cc b/chrome/test/base/android/payment_request_test_bridge.cc
index b9ead57..e10bded4 100644
--- a/chrome/test/base/android/payment_request_test_bridge.cc
+++ b/chrome/test/base/android/payment_request_test_bridge.cc
@@ -13,11 +13,12 @@
                                               bool is_incognito,
                                               bool is_valid_ssl,
                                               bool is_web_contents_active,
+                                              bool prefs_can_make_payment,
                                               bool skip_ui_for_basic_card) {
   JNIEnv* env = base::android::AttachCurrentThread();
   Java_PaymentRequestTestBridge_setUseDelegateForTest(
       env, use_delegate, is_incognito, is_valid_ssl, is_web_contents_active,
-      skip_ui_for_basic_card);
+      prefs_can_make_payment, skip_ui_for_basic_card);
 }
 
 struct NativeObserverCallbacks {
diff --git a/chrome/test/base/android/payment_request_test_bridge.h b/chrome/test/base/android/payment_request_test_bridge.h
index e83571c..10a4e9f 100644
--- a/chrome/test/base/android/payment_request_test_bridge.h
+++ b/chrome/test/base/android/payment_request_test_bridge.h
@@ -16,6 +16,7 @@
                                               bool is_incognito,
                                               bool is_valid_ssl,
                                               bool is_web_contents_active,
+                                              bool prefs_can_make_payment,
                                               bool skip_ui_for_basic_card);
 
 // Sets an observer on future Java PaymentRequests that will call these
diff --git a/chrome/test/base/ui_test_utils.cc b/chrome/test/base/ui_test_utils.cc
index 89de1bd..09b8418f 100644
--- a/chrome/test/base/ui_test_utils.cc
+++ b/chrome/test/base/ui_test_utils.cc
@@ -17,6 +17,8 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/path_service.h"
+#include "base/run_loop.h"
+#include "base/scoped_observer.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/bind_test_util.h"
 #include "base/test/test_timeouts.h"
@@ -33,6 +35,7 @@
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_navigator.h"
 #include "chrome/browser/ui/browser_navigator_params.h"
+#include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/find_bar/find_notification_details.h"
 #include "chrome/browser/ui/find_bar/find_tab_helper.h"
 #include "chrome/browser/ui/location_bar/location_bar.h"
@@ -46,7 +49,10 @@
 #include "components/download/public/common/download_item.h"
 #include "components/history/core/browser/history_service_observer.h"
 #include "components/omnibox/browser/autocomplete_controller.h"
+#include "components/omnibox/browser/omnibox_controller_emitter.h"
 #include "components/omnibox/browser/omnibox_edit_model.h"
+#include "components/omnibox/browser/omnibox_popup_model.h"
+#include "components/omnibox/browser/omnibox_view.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/download_manager.h"
 #include "content/public/browser/navigation_controller.h"
@@ -150,6 +156,34 @@
   DISALLOW_COPY_AND_ASSIGN(AppModalDialogWaiter);
 };
 
+class AutocompleteChangeObserver : public OmniboxControllerEmitter::Observer {
+ public:
+  explicit AutocompleteChangeObserver(Profile* profile) {
+    scoped_observer_.Add(
+        OmniboxControllerEmitter::GetForBrowserContext(profile));
+  }
+
+  ~AutocompleteChangeObserver() override = default;
+
+  void Wait() { run_loop_.Run(); }
+
+  // OmniboxControllerEmitter::Observer:
+  void OnOmniboxQuery(AutocompleteController* controller,
+                      const base::string16& input_text) override {}
+  void OnOmniboxResultChanged(bool default_match_changed,
+                              AutocompleteController* controller) override {
+    if (run_loop_.running())
+      run_loop_.Quit();
+  }
+
+ private:
+  base::RunLoop run_loop_;
+  ScopedObserver<OmniboxControllerEmitter, OmniboxControllerEmitter::Observer>
+      scoped_observer_{this};
+
+  DISALLOW_COPY_AND_ASSIGN(AutocompleteChangeObserver);
+};
+
 }  // namespace
 
 bool GetCurrentTabTitle(const Browser* browser, base::string16* title) {
@@ -367,19 +401,26 @@
   observer->WaitForFinished();
 }
 
-void SendToOmniboxAndSubmit(LocationBar* location_bar,
+void WaitForAutocompleteDone(Browser* browser) {
+  auto* controller = browser->window()
+                         ->GetLocationBar()
+                         ->GetOmniboxView()
+                         ->model()
+                         ->autocomplete_controller();
+  while (!controller->done())
+    AutocompleteChangeObserver(browser->profile()).Wait();
+}
+
+void SendToOmniboxAndSubmit(Browser* browser,
                             const std::string& input,
                             base::TimeTicks match_selection_timestamp) {
+  LocationBar* location_bar = browser->window()->GetLocationBar();
   OmniboxView* omnibox = location_bar->GetOmniboxView();
   omnibox->model()->OnSetFocus(false);
   omnibox->SetUserText(base::ASCIIToUTF16(input));
   location_bar->AcceptInput(match_selection_timestamp);
-  while (!omnibox->model()->autocomplete_controller()->done()) {
-    content::WindowedNotificationObserver observer(
-        chrome::NOTIFICATION_AUTOCOMPLETE_CONTROLLER_RESULT_READY,
-        content::NotificationService::AllSources());
-    observer.Wait();
-  }
+
+  WaitForAutocompleteDone(browser);
 }
 
 Browser* GetBrowserNotInSet(const std::set<Browser*>& excluded_browsers) {
diff --git a/chrome/test/base/ui_test_utils.h b/chrome/test/base/ui_test_utils.h
index 3febc41..2556432 100644
--- a/chrome/test/base/ui_test_utils.h
+++ b/chrome/test/base/ui_test_utils.h
@@ -26,7 +26,6 @@
 #include "url/gurl.h"
 
 class Browser;
-class LocationBar;
 class Profile;
 
 namespace app_modal {
@@ -182,9 +181,12 @@
 // Download the given file and waits for the download to complete.
 void DownloadURL(Browser* browser, const GURL& download_url);
 
+// Waits until the autocomplete controller reaches its done state.
+void WaitForAutocompleteDone(Browser* browser);
+
 // Send the given text to the omnibox and wait until it's updated.
 void SendToOmniboxAndSubmit(
-    LocationBar* location_bar,
+    Browser* browser,
     const std::string& input,
     base::TimeTicks match_selection_timestamp = base::TimeTicks());
 
diff --git a/chrome/test/chromedriver/capabilities.cc b/chrome/test/chromedriver/capabilities.cc
index 89de48a..014a1fa1 100644
--- a/chrome/test/chromedriver/capabilities.cc
+++ b/chrome/test/chromedriver/capabilities.cc
@@ -591,6 +591,25 @@
   return Status(kOk);
 }
 
+Status ParseSeleniumOptions(
+    const base::Value& capability,
+    Capabilities* capabilities) {
+  const base::DictionaryValue* selenium_options = NULL;
+  if (!capability.GetAsDictionary(&selenium_options))
+    return Status(kInvalidArgument, "must be a dictionary");
+  std::map<std::string, Parser> parser_map;
+  parser_map["loggingPrefs"] = base::Bind(&ParseLoggingPrefs);
+
+  for (base::DictionaryValue::Iterator it(*selenium_options); !it.IsAtEnd();
+       it.Advance()) {
+    if (parser_map.find(it.key()) == parser_map.end())
+      continue;
+    Status status = parser_map[it.key()].Run(it.value(), capabilities);
+    if (status.IsError())
+      return Status(kInvalidArgument, "cannot parse " + it.key(), status);
+  }
+  return Status(kOk);
+}
 }  // namespace
 
 Switches::Switches() {}
@@ -763,10 +782,12 @@
   } else {
     parser_map["chromeOptions"] = base::BindRepeating(&ParseChromeOptions);
   }
-  // goog:loggingPrefs is spec-compliant name, but loggingPrefs is still
-  // supported in legacy mode.
-  if (w3c_compliant ||
-      desired_caps.GetDictionary("goog:loggingPrefs", nullptr)) {
+  // se:options.loggingPrefs and goog:loggingPrefs is spec-compliant name,
+  // but loggingPrefs is still supported in legacy mode.
+  if (desired_caps.GetDictionary("se:options.loggingPrefs", nullptr)) {
+    parser_map["se:options"] = base::BindRepeating(&ParseSeleniumOptions);
+  } else if (w3c_compliant ||
+             desired_caps.GetDictionary("goog:loggingPrefs", nullptr)) {
     parser_map["goog:loggingPrefs"] = base::BindRepeating(&ParseLoggingPrefs);
   } else {
     parser_map["loggingPrefs"] = base::BindRepeating(&ParseLoggingPrefs);
diff --git a/chrome/test/chromedriver/client/chromedriver.py b/chrome/test/chromedriver/client/chromedriver.py
index e1e5dab..f2712fe 100644
--- a/chrome/test/chromedriver/client/chromedriver.py
+++ b/chrome/test/chromedriver/client/chromedriver.py
@@ -231,7 +231,9 @@
 
     params = {
         'goog:chromeOptions': options,
-        'goog:loggingPrefs': logging_prefs
+        'se:options': {
+            'loggingPrefs': logging_prefs
+        }
     }
 
     if page_load_strategy:
diff --git a/chrome/test/data/android/render_tests/BookmarkTest.bookmark_manager_folder_selected.Nexus_5-19.png.sha1 b/chrome/test/data/android/render_tests/BookmarkTest.bookmark_manager_folder_selected.Nexus_5-19.png.sha1
index 66570f6..0963b67c 100644
--- a/chrome/test/data/android/render_tests/BookmarkTest.bookmark_manager_folder_selected.Nexus_5-19.png.sha1
+++ b/chrome/test/data/android/render_tests/BookmarkTest.bookmark_manager_folder_selected.Nexus_5-19.png.sha1
@@ -1 +1 @@
-e577a1a908010f2929d6f47bab823c5ec45cd386
\ No newline at end of file
+c92eb683b85f26c37ff0cdcb1a2e95bdeee137a9
\ No newline at end of file
diff --git a/chrome/test/data/android/render_tests/BookmarkTest.bookmark_manager_one_folder.Nexus_5-19.png.sha1 b/chrome/test/data/android/render_tests/BookmarkTest.bookmark_manager_one_folder.Nexus_5-19.png.sha1
index ae7e783e..d35f3d8 100644
--- a/chrome/test/data/android/render_tests/BookmarkTest.bookmark_manager_one_folder.Nexus_5-19.png.sha1
+++ b/chrome/test/data/android/render_tests/BookmarkTest.bookmark_manager_one_folder.Nexus_5-19.png.sha1
@@ -1 +1 @@
-235755b6e2ecb0b5bcc493ee24cac5bd1186edaa
\ No newline at end of file
+498c2ad61394be8b7bdf4e003e7dc995cf789a7c
\ No newline at end of file
diff --git a/chrome/test/data/extensions/api_test/management/launch_app_panel/launch.html b/chrome/test/data/extensions/api_test/management/launch_app_panel/launch.html
index 33687a2..b3fb631 100644
--- a/chrome/test/data/extensions/api_test/management/launch_app_panel/launch.html
+++ b/chrome/test/data/extensions/api_test/management/launch_app_panel/launch.html
@@ -11,4 +11,6 @@
   <body>
      App launch page
   </body>
+
+  <script src="launch.js"></script>
 </html>
diff --git a/chrome/test/data/extensions/api_test/management/launch_app_panel/launch.js b/chrome/test/data/extensions/api_test/management/launch_app_panel/launch.js
new file mode 100644
index 0000000..2e4c2e4
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/management/launch_app_panel/launch.js
@@ -0,0 +1,5 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+chrome.test.sendMessage('launched app');
diff --git a/chrome/test/data/extensions/api_test/management/launch_app_tab/launch.html b/chrome/test/data/extensions/api_test/management/launch_app_tab/launch.html
index 33687a2..90df5dd 100644
--- a/chrome/test/data/extensions/api_test/management/launch_app_tab/launch.html
+++ b/chrome/test/data/extensions/api_test/management/launch_app_tab/launch.html
@@ -11,4 +11,5 @@
   <body>
      App launch page
   </body>
+  <script src="launch.js"></script>
 </html>
diff --git a/chrome/test/data/extensions/api_test/management/launch_app_tab/launch.js b/chrome/test/data/extensions/api_test/management/launch_app_tab/launch.js
new file mode 100644
index 0000000..2e4c2e4
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/management/launch_app_tab/launch.js
@@ -0,0 +1,5 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+chrome.test.sendMessage('launched app');
diff --git a/chrome/test/data/extensions/api_test/management/launch_on_install/background.js b/chrome/test/data/extensions/api_test/management/launch_on_install/background.js
index d021ada..1f976f0 100644
--- a/chrome/test/data/extensions/api_test/management/launch_on_install/background.js
+++ b/chrome/test/data/extensions/api_test/management/launch_on_install/background.js
@@ -11,10 +11,7 @@
   console.log("Launch " + extensionInfo.name + " (" +
               extensionInfo.id + ")");
 
-  chrome.management.launchApp(extensionInfo.id, function() {
-    chrome.test.sendMessage("launched app");
-  });
-
+  chrome.management.launchApp(extensionInfo.id);
 });
 
 chrome.test.sendMessage("launcher loaded");
diff --git a/chrome/test/data/local_ntp/customize_menu_browsertest.js b/chrome/test/data/local_ntp/customize_menu_browsertest.js
index f5975cd..5874f55 100644
--- a/chrome/test/data/local_ntp/customize_menu_browsertest.js
+++ b/chrome/test/data/local_ntp/customize_menu_browsertest.js
@@ -17,6 +17,7 @@
  * @const
  */
 test.customizeMenu.IDS = {
+  BACKGROUNDS_BUTTON: 'backgrounds-button',
   BACKGROUNDS_MENU: 'backgrounds-menu',
   BACKGROUNDS_IMAGE_MENU: 'backgrounds-image-menu',
   COLORS_BUTTON: 'colors-button',
@@ -28,6 +29,7 @@
   MENU_BACK: 'menu-back',
   MENU_CANCEL: 'menu-cancel',
   MENU_DONE: 'menu-done',
+  MENU_TITLE: 'menu-title',
   SHORTCUTS_BUTTON: 'shortcuts-button',
   SHORTCUTS_HIDE_TOGGLE: 'sh-hide-toggle',
   SHORTCUTS_MENU: 'shortcuts-menu',
@@ -41,8 +43,8 @@
  * @const
  */
 test.customizeMenu.CLASSES = {
+  COLLECTION_TILE: 'bg-sel-tile',
   ENTRY_POINT_ENHANCED: 'ep-enhanced',
-  MENU_SHOWN: 'menu-shown',
   SELECTED: 'selected',
 };
 
@@ -66,6 +68,27 @@
 
 /**
  * The number of times
+ * chrome.embeddedSearch.newTabPage.applyAutogeneratedTheme is called.
+ * @type {number}
+ */
+test.customizeMenu.applyAutogeneratedThemeCount = 0;
+
+/**
+ * The number of times
+ * chrome.embeddedSearch.newTabPage.confirmThemeChanges is called.
+ * @type {number}
+ */
+test.customizeMenu.confirmThemeChangesCount = 0;
+
+/**
+ * The number of times
+ * chrome.embeddedSearch.newTabPage.revertThemeChanges is called.
+ * @type {number}
+ */
+test.customizeMenu.revertThemeChangesCount = 0;
+
+/**
+ * The number of times
  * chrome.embeddedSearch.newTabPage.toggleMostVisitedOrCustomLinks is called.
  * @type {number}
  */
@@ -102,6 +125,9 @@
   test.customizeMenu.stubs.reset();
   test.customizeMenu.isUsingMostVisited = false;
   test.customizeMenu.areShortcutsVisible = true;
+  test.customizeMenu.applyAutogeneratedThemeCount = 0;
+  test.customizeMenu.confirmThemeChangesCount = 0;
+  test.customizeMenu.revertThemeChangesCount = 0;
   test.customizeMenu.toggleMostVisitedOrCustomLinksCount = 0;
   test.customizeMenu.toggleShortcutsVisibilityCount = 0;
   test.customizeMenu.timesCustomBackgroundWasSet = 0;
@@ -112,9 +138,9 @@
 // Functions from test_utils.js are automatically imported.
 
 /**
- * Tests that the richer picker opens to the default submenu (Background).
+ * Tests that the richer picker opens.
  */
-test.customizeMenu.testOpenCustomizeMenu = function() {
+test.customizeMenu.testMenu_Open = function() {
   init();
 
   const menuButton = $(test.customizeMenu.IDS.EDIT_BG);
@@ -127,28 +153,170 @@
   const menuDialog = $(test.customizeMenu.IDS.CUSTOMIZATION_MENU);
   assertTrue(menuDialog.open);
   assertTrue(elementIsVisible(menuDialog));
-
-  // The default submenu (Background) should be shown.
-  const backgroundSubmenu = $(test.customizeMenu.IDS.BACKGROUNDS_MENU);
-  assertTrue(backgroundSubmenu.classList.contains(
-      test.customizeMenu.CLASSES.MENU_SHOWN));
 };
 
 /**
- * Tests that the Shortcuts submenu can be opened.
+ * Tests that the richer picker submenus open.
  */
-test.customizeMenu.testShortcuts_OpenSubmenu = function() {
+test.customizeMenu.testMenu_OpenSubmenus = function() {
   init();
 
-  // Open the Shortcuts submenu.
+  // Open the richer picker. The default submenu (Background) should be shown.
   $(test.customizeMenu.IDS.EDIT_BG).click();
-  $(test.customizeMenu.IDS.SHORTCUTS_BUTTON).click();
+  const backgroundSubmenu = $(test.customizeMenu.IDS.BACKGROUNDS_MENU);
+  const shortcutsSubmenu = $(test.customizeMenu.IDS.SHORTCUTS_MENU);
+  const colorSubmenu = $(test.customizeMenu.IDS.COLORS_MENU);
+  assertTrue(elementIsVisible(backgroundSubmenu));
+  assertFalse(elementIsVisible(shortcutsSubmenu));
+  assertFalse(elementIsVisible(colorSubmenu));
 
-  assertTrue($(test.customizeMenu.IDS.SHORTCUTS_MENU)
-                 .classList.contains(test.customizeMenu.CLASSES.MENU_SHOWN));
+  // Open the Shortcuts submenu. All other submenus should be hidden.
+  $(test.customizeMenu.IDS.SHORTCUTS_BUTTON).click();
+  assertFalse(elementIsVisible(backgroundSubmenu));
+  assertTrue(elementIsVisible(shortcutsSubmenu));
+  assertFalse(elementIsVisible(colorSubmenu));
+
+  // Open the Color submenu.
+  $(test.customizeMenu.IDS.COLORS_BUTTON).click();
+  assertFalse(elementIsVisible(backgroundSubmenu));
+  assertFalse(elementIsVisible(shortcutsSubmenu));
+  assertTrue(elementIsVisible(colorSubmenu));
+
+  // Open the Background submenu.
+  $(test.customizeMenu.IDS.BACKGROUNDS_BUTTON).click();
+  assertTrue(elementIsVisible(backgroundSubmenu));
+  assertFalse(elementIsVisible(shortcutsSubmenu));
+  assertFalse(elementIsVisible(colorSubmenu));
 };
 
 /**
+ * Tests that user selections are saved across submenus.
+ */
+test.customizeMenu.testMenu_SaveUserSelections = function() {
+  setupFakeAsyncCollectionLoad();
+  init();
+
+  $(test.customizeMenu.IDS.EDIT_BG).click();
+
+  // Select a background.
+  setupFakeAsyncImageLoad('coll_tile_0');
+  $('coll_tile_0').click();
+  const background = $('img_tile_0');
+  background.click();
+  assertTrue(background.parentElement.classList.contains(
+      test.customizeMenu.CLASSES.SELECTED));
+  // The submenu's title should be the image collection's.
+  assertEquals(
+      'Collection 1', $(test.customizeMenu.IDS.MENU_TITLE).textContent);
+
+  // Select some shortcut options.
+  $(test.customizeMenu.IDS.SHORTCUTS_BUTTON).click();
+  assertShortcutOptionsSelected(
+      /*clSelected=*/ true, /*mvSelected=*/ false, /*isHidden=*/ false);
+  $(test.customizeMenu.IDS.SHORTCUTS_OPTION_MOST_VISITED).click();
+  $(test.customizeMenu.IDS.SHORTCUTS_HIDE_TOGGLE).click();
+  assertShortcutOptionsSelected(
+      /*clSelected=*/ false, /*mvSelected=*/ true, /*isHidden=*/ true);
+
+  // Select a color.
+  $(test.customizeMenu.IDS.COLORS_BUTTON).click();
+  const colorOptions =
+      $(test.customizeMenu.IDS.COLORS_MENU)
+          .getElementsByClassName(test.customizeMenu.CLASSES.COLLECTION_TILE);
+  const color = colorOptions[1];  // Skip the default theme option.
+  color.click();
+  assertTrue(color.parentElement.classList.contains(
+      test.customizeMenu.CLASSES.SELECTED));
+
+  // Open the Background submenu and check that it's still selected.
+  $(test.customizeMenu.IDS.BACKGROUNDS_BUTTON).click();
+  assertTrue(background.parentElement.classList.contains(
+      test.customizeMenu.CLASSES.SELECTED));
+  // The image collection should still be open.
+  assertEquals(
+      'Collection 1', $(test.customizeMenu.IDS.MENU_TITLE).textContent);
+
+  // Open the Shortcuts submenu and check that they're still selected.
+  $(test.customizeMenu.IDS.SHORTCUTS_BUTTON).click();
+  assertShortcutOptionsSelected(
+      /*clSelected=*/ false, /*mvSelected=*/ true, /*isHidden=*/ true);
+
+  // Open the Color submenu and check that it's still selected.
+  $(test.customizeMenu.IDS.COLORS_BUTTON).click();
+  assertTrue(color.parentElement.classList.contains(
+      test.customizeMenu.CLASSES.SELECTED));
+};
+
+/**
+ * Tests that user selections across submenus are applied on "done".
+ */
+test.customizeMenu.testMenu_ApplyUserSelections = function() {
+  setupFakeAsyncCollectionLoad();
+  init();
+
+  $(test.customizeMenu.IDS.EDIT_BG).click();
+
+  // Select a background.
+  setupFakeAsyncImageLoad('coll_tile_0');
+  $('coll_tile_0').click();
+  $('img_tile_0').click();
+
+  // Select a shortcut option.
+  $(test.customizeMenu.IDS.SHORTCUTS_BUTTON).click();
+  $(test.customizeMenu.IDS.SHORTCUTS_OPTION_MOST_VISITED).click();
+
+  // Select a color.
+  $(test.customizeMenu.IDS.COLORS_BUTTON).click();
+  const colorOptions =
+      $(test.customizeMenu.IDS.COLORS_MENU)
+          .getElementsByClassName(test.customizeMenu.CLASSES.COLLECTION_TILE);
+  colorOptions[1].click();  // Skip the default theme option.
+
+  // Click done and check that all selections have applied.
+  $(test.customizeMenu.IDS.MENU_DONE).click();
+  assertEquals(1, test.customizeMenu.timesCustomBackgroundWasSet);
+  assertEquals(1, test.customizeMenu.toggleMostVisitedOrCustomLinksCount);
+  assertEquals(1, test.customizeMenu.applyAutogeneratedThemeCount);
+  assertEquals(1, test.customizeMenu.confirmThemeChangesCount);
+};
+
+/**
+ * Tests that user selections across submenus are not applied on "cancel".
+ */
+test.customizeMenu.testMenu_CancelUserSelections = function() {
+  setupFakeAsyncCollectionLoad();
+  init();
+
+  $(test.customizeMenu.IDS.EDIT_BG).click();
+
+  // Select a background.
+  setupFakeAsyncImageLoad('coll_tile_0');
+  $('coll_tile_0').click();
+  $('img_tile_0').click();
+
+  // Select a shortcut option.
+  $(test.customizeMenu.IDS.SHORTCUTS_BUTTON).click();
+  $(test.customizeMenu.IDS.SHORTCUTS_OPTION_MOST_VISITED).click();
+
+  // Select a color.
+  $(test.customizeMenu.IDS.COLORS_BUTTON).click();
+  const colorOptions =
+      $(test.customizeMenu.IDS.COLORS_MENU)
+          .getElementsByClassName(test.customizeMenu.CLASSES.COLLECTION_TILE);
+  const color = colorOptions[1];  // Skip the default theme option.
+  color.click();
+
+  // Click cancel and check that all changes have been reverted.
+  $(test.customizeMenu.IDS.MENU_CANCEL).click();
+  assertEquals(0, test.customizeMenu.timesCustomBackgroundWasSet);
+  assertEquals(0, test.customizeMenu.toggleMostVisitedOrCustomLinksCount);
+  assertEquals(1, test.customizeMenu.applyAutogeneratedThemeCount);
+  assertEquals(1, test.customizeMenu.revertThemeChangesCount);
+};
+
+//// SHORTCUT SUBMENU TESTS ////
+
+/**
  * Tests that the custom link option will be preselected.
  */
 test.customizeMenu.testShortcuts_CustomLinksPreselected = function() {
@@ -319,6 +487,8 @@
   assertEquals(1, test.customizeMenu.toggleShortcutsVisibilityCount);
 };
 
+//// COLOR SUBMENU TESTS ////
+
 /**
  * Test that Customization dialog doesn't have Colors option when Colors are
  * disabled.
@@ -341,23 +511,6 @@
   assertTrue(elementIsVisible($(test.customizeMenu.IDS.COLORS_BUTTON)));
 };
 
-/**
- * Test that Colors menu is visible after Colors button is clicked.
- */
-test.customizeMenu.testColors_MenuVisibility = function() {
-  init();
-
-  $(test.customizeMenu.IDS.EDIT_BG).click();
-  assertTrue(elementIsVisible($(test.customizeMenu.IDS.COLORS_BUTTON)));
-  assertFalse(elementIsVisible($(test.customizeMenu.IDS.COLORS_MENU)));
-
-  $(test.customizeMenu.IDS.COLORS_BUTTON).click();
-  assertTrue(elementIsVisible($(test.customizeMenu.IDS.COLORS_MENU)));
-
-  $(test.customizeMenu.IDS.SHORTCUTS_BUTTON).click();
-  assertFalse(elementIsVisible($(test.customizeMenu.IDS.COLORS_MENU)));
-};
-
 
 /**
  * Test that default tile is visible.
@@ -383,6 +536,8 @@
   assertTrue(!!$('color_0').style.backgroundImage);
 };
 
+//// BACKGROUND SUBMENU TESTS ////
+
 /*
  * Tests that a custom background can be set through the menu.
  */
@@ -397,8 +552,7 @@
   const backgroundSubmenu = $(test.customizeMenu.IDS.BACKGROUNDS_MENU);
   const backgroundImageSubmenu =
       $(test.customizeMenu.IDS.BACKGROUNDS_IMAGE_MENU);
-  assertTrue(backgroundSubmenu.classList.contains(
-      test.customizeMenu.CLASSES.MENU_SHOWN));
+  assertTrue(elementIsVisible(backgroundSubmenu));
 
   // 5 total tiles: upload, default, and the 3 collection tiles.
   assertTrue(
@@ -410,10 +564,8 @@
   $('coll_tile_0').click();
 
   // The open menu is now the images menu with 4 tiles.
-  assertFalse(backgroundSubmenu.classList.contains(
-      test.customizeMenu.CLASSES.MENU_SHOWN));
-  assertTrue(backgroundImageSubmenu.classList.contains(
-      test.customizeMenu.CLASSES.MENU_SHOWN));
+  assertFalse(elementIsVisible(backgroundSubmenu));
+  assertTrue(elementIsVisible(backgroundImageSubmenu));
   assertTrue(
       $(test.customizeMenu.IDS.BACKGROUNDS_IMAGE_MENU)
           .getElementsByClassName('bg-sel-tile')
@@ -423,10 +575,8 @@
   $(test.customizeMenu.IDS.MENU_DONE).click();
 
   // No menu should be open, and setCustomBackground should have been called.
-  assertFalse(backgroundSubmenu.classList.contains(
-      test.customizeMenu.CLASSES.MENU_SHOWN));
-  assertFalse(backgroundImageSubmenu.classList.contains(
-      test.customizeMenu.CLASSES.MENU_SHOWN));
+  assertFalse(elementIsVisible(backgroundSubmenu));
+  assertFalse(elementIsVisible(backgroundImageSubmenu));
   assertEquals(1, test.customizeMenu.timesCustomBackgroundWasSet);
 };
 
@@ -446,8 +596,7 @@
   const backgroundSubmenu = $(test.customizeMenu.IDS.BACKGROUNDS_MENU);
   const backgroundImageSubmenu =
       $(test.customizeMenu.IDS.BACKGROUNDS_IMAGE_MENU);
-  assertTrue(backgroundSubmenu.classList.contains(
-      test.customizeMenu.CLASSES.MENU_SHOWN));
+  assertTrue(elementIsVisible(backgroundSubmenu));
 
   // 5 total tiles: upload, default, and the 3 collection tiles.
   assertTrue(
@@ -459,10 +608,8 @@
   $('coll_tile_0').click();
 
   // The open menu is now the images menu with 4 tiles.
-  assertFalse(backgroundSubmenu.classList.contains(
-      test.customizeMenu.CLASSES.MENU_SHOWN));
-  assertTrue(backgroundImageSubmenu.classList.contains(
-      test.customizeMenu.CLASSES.MENU_SHOWN));
+  assertFalse(elementIsVisible(backgroundSubmenu));
+  assertTrue(elementIsVisible(backgroundImageSubmenu));
   assertTrue(
       $(test.customizeMenu.IDS.BACKGROUNDS_IMAGE_MENU)
           .getElementsByClassName('bg-sel-tile')
@@ -473,10 +620,8 @@
 
   // No menu should be open, and setCustomBackground should NOT have been
   // called.
-  assertFalse(backgroundSubmenu.classList.contains(
-      test.customizeMenu.CLASSES.MENU_SHOWN));
-  assertFalse(backgroundImageSubmenu.classList.contains(
-      test.customizeMenu.CLASSES.MENU_SHOWN));
+  assertFalse(elementIsVisible(backgroundSubmenu));
+  assertFalse(elementIsVisible(backgroundImageSubmenu));
   assertEquals(0, test.customizeMenu.timesCustomBackgroundWasSet);
 };
 
@@ -494,8 +639,7 @@
   const backgroundSubmenu = $(test.customizeMenu.IDS.BACKGROUNDS_MENU);
   const backgroundImageSubmenu =
       $(test.customizeMenu.IDS.BACKGROUNDS_IMAGE_MENU);
-  assertTrue(backgroundSubmenu.classList.contains(
-      test.customizeMenu.CLASSES.MENU_SHOWN));
+  assertTrue(elementIsVisible(backgroundSubmenu));
 
   // 5 total tiles: upload, default, and the 3 collection tiles.
   assertTrue(
@@ -507,10 +651,8 @@
   $('coll_tile_0').click();
 
   // The open menu is now the images menu with 4 tiles.
-  assertFalse(backgroundSubmenu.classList.contains(
-      test.customizeMenu.CLASSES.MENU_SHOWN));
-  assertTrue(backgroundImageSubmenu.classList.contains(
-      test.customizeMenu.CLASSES.MENU_SHOWN));
+  assertFalse(elementIsVisible(backgroundSubmenu));
+  assertTrue(elementIsVisible(backgroundImageSubmenu));
   assertTrue(
       $(test.customizeMenu.IDS.BACKGROUNDS_IMAGE_MENU)
           .getElementsByClassName('bg-sel-tile')
@@ -520,19 +662,15 @@
   $(test.customizeMenu.IDS.MENU_BACK).click();
 
   // The main backgrounds menu should be open, and no custom background set.
-  assertTrue(backgroundSubmenu.classList.contains(
-      test.customizeMenu.CLASSES.MENU_SHOWN));
-  assertFalse(backgroundImageSubmenu.classList.contains(
-      test.customizeMenu.CLASSES.MENU_SHOWN));
+  assertTrue(elementIsVisible(backgroundSubmenu));
+  assertFalse(elementIsVisible(backgroundImageSubmenu));
   assertEquals(0, test.customizeMenu.timesCustomBackgroundWasSet);
 
   // Reopen the images menu, the selection should be cleared.
   $('coll_tile_0').click();
 
-  assertFalse(backgroundSubmenu.classList.contains(
-      test.customizeMenu.CLASSES.MENU_SHOWN));
-  assertTrue(backgroundImageSubmenu.classList.contains(
-      test.customizeMenu.CLASSES.MENU_SHOWN));
+  assertFalse(elementIsVisible(backgroundSubmenu));
+  assertTrue(elementIsVisible(backgroundImageSubmenu));
   assertTrue(
       $(test.customizeMenu.IDS.BACKGROUNDS_IMAGE_MENU)
           .getElementsByClassName('bg-sel-tile')
@@ -551,10 +689,19 @@
 init = function() {
   // Mock chrome.embeddedSearch functions. This must be done right before
   // initializing the local NTP.
+  const applyAutogeneratedTheme = () => {
+    test.customizeMenu.applyAutogeneratedThemeCount++
+  };
+  const confirmThemeChanges = () => {
+    test.customizeMenu.confirmThemeChangesCount++
+  };
+  const revertThemeChanges = () => {
+    test.customizeMenu.revertThemeChangesCount++
+  };
   const toggleMostVisitedOrCustomLinks = () => {
     test.customizeMenu.toggleMostVisitedOrCustomLinksCount++
   };
-  const toggleShortcutsVisibility = () => {
+  const toggleShortcutsVisibility = (doNotify) => {
     test.customizeMenu.toggleShortcutsVisibilityCount++
   };
   const timesCustomBackgroundWasSet = () => {
@@ -567,17 +714,20 @@
       chrome.embeddedSearch.newTabPage.themeBackgroundInfo;
 
   test.customizeMenu.stubs.replace(chrome.embeddedSearch, 'newTabPage', {
-    toggleMostVisitedOrCustomLinks: toggleMostVisitedOrCustomLinks,
-    toggleShortcutsVisibility: toggleShortcutsVisibility,
-    isUsingMostVisited: test.customizeMenu.isUsingMostVisited,
+    applyAutogeneratedTheme: applyAutogeneratedTheme,
     areShortcutsVisible: test.customizeMenu.areShortcutsVisible,
-    setBackgroundURLWithAttributions: timesCustomBackgroundWasSet,
+    confirmThemeChanges: confirmThemeChanges,
+    getColorsInfo: getColorsInfo,
+    isUsingMostVisited: test.customizeMenu.isUsingMostVisited,
+    logEvent: (a) => {},
     resetCustomLinks: () => {},
+    revertThemeChanges: revertThemeChanges,
     selectLocalBackgroundImage: () => {},
     setBackgroundURL: timesCustomBackgroundWasSet,
-    logEvent: (a) => {},
-    getColorsInfo: getColorsInfo,
+    setBackgroundURLWithAttributions: timesCustomBackgroundWasSet,
     themeBackgroundInfo: themeBackgroundInfo,
+    toggleMostVisitedOrCustomLinks: toggleMostVisitedOrCustomLinks,
+    toggleShortcutsVisibility: toggleShortcutsVisibility,
   });
 
   initLocalNTP(/*isGooglePage=*/ true);
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index cd351bbe..51a6cc2 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -2563,13 +2563,7 @@
   },
 
   "WelcomePageOnOSUpgradeEnabled": {
-    "os": ["win"],
-    "test_policy": { "WelcomePageOnOSUpgradeEnabled": false },
-    "pref_mappings": [
-      { "pref": "browser.welcome_page_on_os_upgrade_enabled",
-        "local_state": true
-      }
-    ]
+    "note": "This policy is retired, see https://crbug.com/978349."
   },
 
   "SuppressUnsupportedOSWarning": {
diff --git a/chrome/test/data/webui/cr_elements/cr_slider_test.js b/chrome/test/data/webui/cr_elements/cr_slider_test.js
index 200cecc..7510fc3 100644
--- a/chrome/test/data/webui/cr_elements/cr_slider_test.js
+++ b/chrome/test/data/webui/cr_elements/cr_slider_test.js
@@ -10,6 +10,7 @@
     document.body.innerHTML = '<cr-slider min="0" max="100"></cr-slider>';
 
     crSlider = document.body.querySelector('cr-slider');
+    crSlider.value = 0;
     return PolymerTest.flushTasks();
   });
 
@@ -77,7 +78,6 @@
   }
 
   test('key events', () => {
-    crSlider.value = 0;
     pressArrowRight();
     assertEquals(1, crSlider.value);
     pressPageUp();
@@ -110,7 +110,6 @@
 
   test('no-keybindings', () => {
     crSlider.noKeybindings = true;
-    crSlider.value = 0;
     pressArrowRight();
     assertEquals(0, crSlider.value);
     crSlider.noKeybindings = false;
@@ -125,7 +124,6 @@
   });
 
   test('mouse events', () => {
-    crSlider.value = 0;
     pointerMove(.25);
     assertEquals(0, crSlider.value);
     pointerDown(.5);
@@ -171,7 +169,6 @@
   });
 
   test('markers', () => {
-    crSlider.value = 0;
     assertTrue(crSlider.$.markers.hidden);
     crSlider.markerCount = 10;
     assertFalse(crSlider.$.markers.hidden);
@@ -386,4 +383,12 @@
     crSlider.max = 50;
     assertEquals(50, crSlider.value);
   });
+
+  test('container hidden until value set', () => {
+    document.body.innerHTML = '<cr-slider></cr-slider>';
+    crSlider = document.body.querySelector('cr-slider');
+    assertTrue(crSlider.$.container.hidden);
+    crSlider.value = 0;
+    assertFalse(crSlider.$.container.hidden);
+  });
 });
diff --git a/chrome/test/data/webui/settings/appearance_fonts_page_test.js b/chrome/test/data/webui/settings/appearance_fonts_page_test.js
index a998bcd..f691324 100644
--- a/chrome/test/data/webui/settings/appearance_fonts_page_test.js
+++ b/chrome/test/data/webui/settings/appearance_fonts_page_test.js
@@ -67,4 +67,13 @@
     button.click();
     return fontsBrowserProxy.whenCalled('openAdvancedFontSettings');
   });
+
+  test('minimum font size sample', async () => {
+    fontsPage.prefs = {webkit: {webprefs: {minimum_font_size: {value: 0}}}};
+    assertTrue(fontsPage.$.minimumSizeSample.hidden);
+    fontsPage.set('prefs.webkit.webprefs.minimum_font_size.value', 6);
+    assertFalse(fontsPage.$.minimumSizeSample.hidden);
+    fontsPage.set('prefs.webkit.webprefs.minimum_font_size.value', 0);
+    assertTrue(fontsPage.$.minimumSizeSample.hidden);
+  });
 });
diff --git a/chromecast/base/BUILD.gn b/chromecast/base/BUILD.gn
index a36b681..ab01e6a2 100644
--- a/chromecast/base/BUILD.gn
+++ b/chromecast/base/BUILD.gn
@@ -42,6 +42,8 @@
   sources = [
     "alarm_manager.cc",
     "alarm_manager.h",
+    "android/cast_settings_manager.cc",
+    "android/cast_settings_manager.h",
     "android/dumpstate_writer.cc",
     "android/dumpstate_writer.h",
     "android/system_time_change_notifier_android.cc",
@@ -350,6 +352,7 @@
 if (is_android) {
   generate_jni("jni_headers") {
     sources = [
+      "java/src/org/chromium/chromecast/base/CastSettingsManager.java",
       "java/src/org/chromium/chromecast/base/DumpstateWriter.java",
       "java/src/org/chromium/chromecast/base/SystemTimeChangeNotifierAndroid.java",
     ]
@@ -407,6 +410,7 @@
     java_test_dir = "//chromecast/base/java/test"
     java_files = [
       "$java_test_dir/org/chromium/chromecast/base/BothTest.java",
+      "$java_test_dir/org/chromium/chromecast/base/CastSettingsManagerTest.java",
       "$java_test_dir/org/chromium/chromecast/base/ControllerTest.java",
       "$java_test_dir/org/chromium/chromecast/base/ItertoolsTest.java",
       "$java_test_dir/org/chromium/chromecast/base/ObservableAndTest.java",
@@ -423,6 +427,7 @@
     deps = [
       ":base_java",
       ":cast_base_test_utils_java",
+      "//base:base_junit_test_support",
       "//third_party/hamcrest:hamcrest_java",
     ]
   }
diff --git a/chromecast/base/android/cast_settings_manager.cc b/chromecast/base/android/cast_settings_manager.cc
new file mode 100644
index 0000000..d72ff44
--- /dev/null
+++ b/chromecast/base/android/cast_settings_manager.cc
@@ -0,0 +1,25 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromecast/base/android/cast_settings_manager.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "chromecast/base/jni_headers/CastSettingsManager_jni.h"
+
+namespace chromecast {
+
+bool CastSettingsManager::UpdateGlobalDeviceName(
+    const std::string& deviceName) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  return Java_CastSettingsManager_updateGlobalDeviceName(
+      env, base::android::ConvertUTF8ToJavaString(env, deviceName));
+}
+
+bool CastSettingsManager::HasWriteSecureSettingsPermission() {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  return Java_CastSettingsManager_hasWriteSecureSettingsPermission(env);
+}
+
+}  // namespace chromecast
diff --git a/chromecast/base/android/cast_settings_manager.h b/chromecast/base/android/cast_settings_manager.h
new file mode 100644
index 0000000..109d057
--- /dev/null
+++ b/chromecast/base/android/cast_settings_manager.h
@@ -0,0 +1,22 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMECAST_BASE_ANDROID_CAST_SETTINGS_MANAGER_H_
+#define CHROMECAST_BASE_ANDROID_CAST_SETTINGS_MANAGER_H_
+
+#include <string>
+
+namespace chromecast {
+
+class CastSettingsManager {
+ public:
+  CastSettingsManager() {}
+
+  static bool UpdateGlobalDeviceName(const std::string& deviceName);
+  static bool HasWriteSecureSettingsPermission();
+};
+
+}  // namespace chromecast
+
+#endif  // CHROMECAST_BASE_ANDROID_CAST_SETTINGS_MANAGER_H_
diff --git a/chromecast/base/java/src/org/chromium/chromecast/base/CastSettingsManager.java b/chromecast/base/java/src/org/chromium/chromecast/base/CastSettingsManager.java
index 28d1e23d..5bcf4fa 100644
--- a/chromecast/base/java/src/org/chromium/chromecast/base/CastSettingsManager.java
+++ b/chromecast/base/java/src/org/chromium/chromecast/base/CastSettingsManager.java
@@ -4,6 +4,9 @@
 
 package org.chromium.chromecast.base;
 
+import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
 import android.annotation.SuppressLint;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -11,12 +14,17 @@
 import android.os.Build;
 import android.os.Handler;
 import android.provider.Settings;
+import android.support.annotation.VisibleForTesting;
 
+import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
 
 /**
  * Manager for Cast settings.
  */
+@JNINamespace("chromecast")
 public final class CastSettingsManager {
     private static final String TAG = "cr_CastSettingsManager";
 
@@ -25,14 +33,15 @@
     /** The default device name, which is the model name. */
     private static final String DEFAULT_DEVICE_NAME = Build.MODEL;
 
-    // TODO(gunsch): Switch to Settings.Global.DEVICE_NAME once it's in public SDK.
-    // private static final String DEVICE_NAME_SETTING_KEY = Settings.Global.DEVICE_NAME;
-    private static final String DEVICE_NAME_SETTING_KEY = "device_name";
+    private static final String DEVICE_NAME_SETTING_KEY = Settings.Global.DEVICE_NAME;
     private static final String DEVICE_PROVISIONED_SETTING_KEY = Settings.Global.DEVICE_PROVISIONED;
 
     private final ContentResolver mContentResolver;
-    private ContentObserver mDeviceNameObserver;
-    private ContentObserver mIsDeviceProvisionedObserver;
+
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    protected ContentObserver mDeviceNameObserver;
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    protected ContentObserver mIsDeviceProvisionedObserver;
 
     /**
      * Can be implemented to receive notifications from a CastSettingsManager instance when
@@ -111,4 +120,38 @@
         return (deviceName != null) ? deviceName : DEFAULT_DEVICE_NAME;
     }
 
+    /**
+     * Updates Settings.Global DEVICE_NAME to desired string. Returns true if
+     * successful, false otherwise. Requires the application to have
+     * android.permission.WRITE_SECURE_SETTINGS permission
+     */
+    @CalledByNative
+    public static boolean updateGlobalDeviceName(String deviceName) {
+        if (deviceName == null || deviceName.isEmpty()) {
+            Log.e(TAG, "deviceName must not be null or empty");
+            return false;
+        }
+        Context context = ContextUtils.getApplicationContext();
+        if (!hasWriteSecureSettingsPermission()) {
+            Log.e(TAG, "PERMISSION DENIED while attempting to update deviceName");
+            return false;
+        }
+        return Settings.Global.putString(
+                context.getContentResolver(), DEVICE_NAME_SETTING_KEY, deviceName);
+    }
+
+    /**
+     * Indicates whether or not the application has the necessary permission
+     * to update the deviceName.
+     */
+    @CalledByNative
+    private static boolean hasWriteSecureSettingsPermission() {
+        // checkSelfPermission only available in API Version >= 23
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            Context context = ContextUtils.getApplicationContext();
+            return context.checkSelfPermission(WRITE_SECURE_SETTINGS) == PERMISSION_GRANTED;
+        } else {
+            return false;
+        }
+    }
 }
diff --git a/chromecast/base/java/test/org/chromium/chromecast/base/CastSettingsManagerTest.java b/chromecast/base/java/test/org/chromium/chromecast/base/CastSettingsManagerTest.java
new file mode 100644
index 0000000..9f65cb6
--- /dev/null
+++ b/chromecast/base/java/test/org/chromium/chromecast/base/CastSettingsManagerTest.java
@@ -0,0 +1,119 @@
+// 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.chromecast.base;
+
+import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.provider.Settings.Global.DEVICE_NAME;
+import static android.provider.Settings.Global.DEVICE_PROVISIONED;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.os.Build;
+import android.provider.Settings;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+import org.chromium.base.ContextUtils;
+import org.chromium.base.test.BaseRobolectricTestRunner;
+
+/**
+ * Unit Tests for CastSettingsManager
+ */
+@RunWith(BaseRobolectricTestRunner.class)
+public class CastSettingsManagerTest {
+    private CastSettingsManager mCastSettingsManager;
+
+    @Mock
+    private ContentResolver mContentResolver;
+    @Mock
+    private Context mContext;
+    @Mock
+    private CastSettingsManager.OnSettingChangedListener mListener;
+
+    @Before
+    public void before() {
+        mContext = mock(Context.class);
+        mContentResolver = mock(ContentResolver.class);
+        mListener = mock(CastSettingsManager.OnSettingChangedListener.class);
+        ContextUtils.initApplicationContextForTests(mContext);
+        when(mContext.getContentResolver()).thenReturn(mContentResolver);
+        mCastSettingsManager = CastSettingsManager.createCastSettingsManager(mContext, mListener);
+    }
+
+    @After
+    public void after() {
+        mCastSettingsManager.dispose();
+    }
+
+    @Test
+    public void testGetDeviceNameReturnsBuildModelWhenNotSet() {
+        assertTrue(Settings.Global.putString(mContentResolver, DEVICE_NAME, null));
+        assertEquals(Build.MODEL, mCastSettingsManager.getDeviceName());
+    }
+
+    @Test
+    public void testGetDeviceNameReturnsExpectedName() {
+        assertTrue(Settings.Global.putString(mContentResolver, DEVICE_NAME, "fooDevice"));
+        assertEquals("fooDevice", mCastSettingsManager.getDeviceName());
+    }
+
+    @Test
+    public void testIsCastEnabled() {
+        assertTrue(Settings.Global.putInt(mContentResolver, DEVICE_PROVISIONED, 0));
+        assertFalse(mCastSettingsManager.isCastEnabled());
+        assertTrue(Settings.Global.putInt(mContentResolver, DEVICE_PROVISIONED, 1));
+        assertTrue(mCastSettingsManager.isCastEnabled());
+    }
+
+    @Test
+    public void testUpdateGlobalDeviceNameUpdatesDeviceNameWithPermission() {
+        when(mContext.checkSelfPermission(WRITE_SECURE_SETTINGS)).thenReturn(PERMISSION_GRANTED);
+        assertTrue(CastSettingsManager.updateGlobalDeviceName("newName"));
+        assertEquals("newName", mCastSettingsManager.getDeviceName());
+    }
+
+    @Test
+    public void testUpdateGlobalDeviceNameReturnsFalseWithoutPermission() {
+        when(mContext.checkSelfPermission(WRITE_SECURE_SETTINGS)).thenReturn(PERMISSION_DENIED);
+        assertFalse(CastSettingsManager.updateGlobalDeviceName("newName2"));
+        assertNotEquals("newName2", mCastSettingsManager.getDeviceName());
+    }
+
+    @Test
+    public void testSettingsListenersRegistered() {
+        verify(mContentResolver)
+                .registerContentObserver(Settings.Global.getUriFor(DEVICE_NAME), true,
+                        mCastSettingsManager.mDeviceNameObserver);
+        verify(mContentResolver)
+                .registerContentObserver(Settings.Global.getUriFor(DEVICE_PROVISIONED), true,
+                        mCastSettingsManager.mIsDeviceProvisionedObserver);
+    }
+
+    @Test
+    public void testDeviceNameListenerMethodCalledWhenObserverTriggered() {
+        mCastSettingsManager.mDeviceNameObserver.onChange(true);
+        verify(mListener).onDeviceNameChanged(mCastSettingsManager.getDeviceName());
+    }
+
+    @Test
+    public void testCastEnabledListenerMethodCalledWhenObserverTriggered() {
+        mCastSettingsManager.mIsDeviceProvisionedObserver.onChange(true);
+        verify(mListener).onCastEnabledChanged(mCastSettingsManager.isCastEnabled());
+    }
+}
diff --git a/chromecast/browser/cast_memory_pressure_monitor.cc b/chromecast/browser/cast_memory_pressure_monitor.cc
index 69277d1..f8af2818 100644
--- a/chromecast/browser/cast_memory_pressure_monitor.cc
+++ b/chromecast/browser/cast_memory_pressure_monitor.cc
@@ -61,7 +61,7 @@
 CastMemoryPressureMonitor::~CastMemoryPressureMonitor() {}
 
 CastMemoryPressureMonitor::MemoryPressureLevel
-CastMemoryPressureMonitor::GetCurrentPressureLevel() {
+CastMemoryPressureMonitor::GetCurrentPressureLevel() const {
   return current_level_;
 }
 
diff --git a/chromecast/browser/cast_memory_pressure_monitor.h b/chromecast/browser/cast_memory_pressure_monitor.h
index 3b44c81..14262a0 100644
--- a/chromecast/browser/cast_memory_pressure_monitor.h
+++ b/chromecast/browser/cast_memory_pressure_monitor.h
@@ -19,7 +19,7 @@
   ~CastMemoryPressureMonitor() override;
 
   // base::MemoryPressureMonitor implementation:
-  MemoryPressureLevel GetCurrentPressureLevel() override;
+  MemoryPressureLevel GetCurrentPressureLevel() const override;
   void SetDispatchCallback(const DispatchCallback& callback) override;
 
  private:
diff --git a/chromecast/net/connectivity_checker_impl.cc b/chromecast/net/connectivity_checker_impl.cc
index f7c02192..22bd00d 100644
--- a/chromecast/net/connectivity_checker_impl.cc
+++ b/chromecast/net/connectivity_checker_impl.cc
@@ -159,7 +159,8 @@
 
   DVLOG(1) << "Connectivity check: url=" << *connectivity_check_url_;
   url_request_ = url_request_context_->CreateRequest(
-      *connectivity_check_url_, net::MAXIMUM_PRIORITY, this);
+      *connectivity_check_url_, net::MAXIMUM_PRIORITY, this,
+      MISSING_TRAFFIC_ANNOTATION);
   url_request_->set_method("HEAD");
   url_request_->Start();
 
diff --git a/chromecast/net/small_message_socket.cc b/chromecast/net/small_message_socket.cc
index 329d576e..63cefb60 100644
--- a/chromecast/net/small_message_socket.cc
+++ b/chromecast/net/small_message_socket.cc
@@ -83,7 +83,7 @@
         socket_->Write(write_buffer_.get(), write_buffer_->BytesRemaining(),
                        base::BindRepeating(&SmallMessageSocket::OnWriteComplete,
                                            base::Unretained(this)),
-                       NO_TRAFFIC_ANNOTATION_YET);
+                       MISSING_TRAFFIC_ANNOTATION);
     if (!HandleWriteResult(result)) {
       return;
     }
diff --git a/chromeos/BUILD.gn b/chromeos/BUILD.gn
index 18369f10..d4947c69 100644
--- a/chromeos/BUILD.gn
+++ b/chromeos/BUILD.gn
@@ -62,6 +62,8 @@
     "printing/printer_translator.h",
     "printing/uri_components.cc",
     "printing/uri_components.h",
+    "printing/usb_printer_id.cc",
+    "printing/usb_printer_id.h",
     "process_proxy/process_output_watcher.cc",
     "process_proxy/process_output_watcher.h",
     "process_proxy/process_proxy.cc",
@@ -182,6 +184,7 @@
     "printing/ppd_provider_unittest.cc",
     "printing/printer_configuration_unittest.cc",
     "printing/printer_translator_unittest.cc",
+    "printing/usb_printer_id_unittest.cc",
     "process_proxy/process_output_watcher_unittest.cc",
     "process_proxy/process_proxy_unittest.cc",
     "test/run_all_unittests.cc",
diff --git a/chromeos/printing/epson_driver_matching.cc b/chromeos/printing/epson_driver_matching.cc
index d25b5a6..8f1bd00d 100644
--- a/chromeos/printing/epson_driver_matching.cc
+++ b/chromeos/printing/epson_driver_matching.cc
@@ -34,7 +34,7 @@
                             "application/octet-stream");
 
     case PrinterSearchData::PrinterDiscoveryType::kUsb:
-      return base::Contains(sd.usb_command_set, "ESC/P-R");
+      return base::Contains(sd.printer_id.command_set(), "ESC/P-R");
 
     case PrinterSearchData::PrinterDiscoveryType::kZeroconf:
       // For printers found through mDNS/DNS-SD discovery,
diff --git a/chromeos/printing/epson_driver_matching_unittest.cc b/chromeos/printing/epson_driver_matching_unittest.cc
index 42d274a..8bf627a 100644
--- a/chromeos/printing/epson_driver_matching_unittest.cc
+++ b/chromeos/printing/epson_driver_matching_unittest.cc
@@ -5,6 +5,7 @@
 #include "chromeos/printing/epson_driver_matching.h"
 
 #include <string>
+#include <vector>
 
 #include "chromeos/printing/ppd_provider.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -31,7 +32,7 @@
 
     case PrinterDiscoveryType::kUsb:
       sd.discovery_type = PrinterDiscoveryType::kUsb;
-      sd.usb_command_set.push_back(kEscPr);
+      sd.printer_id.set_command_set({kEscPr});
       break;
 
     case PrinterDiscoveryType::kZeroconf:
@@ -96,7 +97,7 @@
   sd.supported_document_formats.push_back(std::string(kOctetStream) + "afds");
   EXPECT_FALSE(CanUseEpsonGenericPPD(sd));
 
-  sd.usb_command_set.push_back(kOctetStream);
+  sd.printer_id.set_command_set({kOctetStream});
   EXPECT_FALSE(CanUseEpsonGenericPPD(sd));
 
   sd.supported_document_formats.push_back(kOctetStream);
@@ -106,18 +107,21 @@
 // Simple PrinterDiscoveryType::kUsb checks.
 TEST(EpsonDriverMatchingTest, UsbDiscovery) {
   PrinterSearchData sd(GetTestPrinterSearchData(PrinterDiscoveryType::kUsb));
-  sd.usb_command_set.clear();
+  std::vector<std::string> command_set;
 
-  sd.usb_command_set.push_back("ESC");
+  command_set.push_back("ESC");
+  sd.printer_id.set_command_set(command_set);
   EXPECT_FALSE(CanUseEpsonGenericPPD(sd));
 
-  sd.usb_command_set.push_back(std::string(kEscPr) + ":asfd");
+  command_set.push_back(std::string(kEscPr) + ":asfd");
+  sd.printer_id.set_command_set(command_set);
   EXPECT_FALSE(CanUseEpsonGenericPPD(sd));
 
   sd.supported_document_formats.push_back(kEscPr);
   EXPECT_FALSE(CanUseEpsonGenericPPD(sd));
 
-  sd.usb_command_set.push_back(kEscPr);
+  command_set.push_back(kEscPr);
+  sd.printer_id.set_command_set(command_set);
   EXPECT_TRUE(CanUseEpsonGenericPPD(sd));
 }
 
@@ -132,7 +136,7 @@
   sd.supported_document_formats.push_back(std::string(kEpsonEscpr) + ":asfd");
   EXPECT_FALSE(CanUseEpsonGenericPPD(sd));
 
-  sd.usb_command_set.push_back(kEpsonEscpr);
+  sd.printer_id.set_command_set({kEpsonEscpr});
   EXPECT_FALSE(CanUseEpsonGenericPPD(sd));
 
   sd.supported_document_formats.push_back(kEpsonEscpr);
diff --git a/chromeos/printing/ppd_provider.h b/chromeos/printing/ppd_provider.h
index 51baa82a..b5110b03 100644
--- a/chromeos/printing/ppd_provider.h
+++ b/chromeos/printing/ppd_provider.h
@@ -16,6 +16,7 @@
 #include "base/version.h"
 #include "chromeos/chromeos_export.h"
 #include "chromeos/printing/printer_configuration.h"
+#include "chromeos/printing/usb_printer_id.h"
 
 namespace network {
 namespace mojom {
@@ -58,9 +59,9 @@
   // Set of MIME types supported by this printer.
   std::vector<std::string> supported_document_formats;
 
-  // Stripped from IEEE1284 signaling method(from the device ID key 'CMD').
-  // Details a set of languages this printer understands.
-  std::vector<std::string> usb_command_set;
+  // Representation of IEEE1284 standard printing device ID.
+  // Contains a set of languages this printer understands.
+  UsbPrinterId printer_id;
 };
 
 // PpdProvider is responsible for mapping printer descriptions to
diff --git a/chromeos/printing/usb_printer_id.cc b/chromeos/printing/usb_printer_id.cc
new file mode 100644
index 0000000..5c7a81e
--- /dev/null
+++ b/chromeos/printing/usb_printer_id.cc
@@ -0,0 +1,88 @@
+// 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 "chromeos/printing/usb_printer_id.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "base/stl_util.h"
+#include "base/strings/string_split.h"
+
+namespace chromeos {
+
+// Device ID keys pulled from IEEE Standard 1284.
+const char kManufacturer[] = "MANUFACTURER";
+const char kManufacturerAbbr[] = "MFG";
+const char kModel[] = "MODEL";
+const char kModelAbbr[] = "MDL";
+const char kCommandSet[] = "COMMAND SET";
+const char kCommandSetAbbr[] = "CMD";
+
+UsbPrinterId::UsbPrinterId(const std::vector<uint8_t>& device_id_data) {
+  // Build mapping.
+  id_mappings_ = BuildDeviceIdMapping(device_id_data);
+
+  // Save required mappings.
+  // Save make_.
+  if (base::Contains(id_mappings_, kManufacturer)) {
+    make_ = id_mappings_[kManufacturer].front();
+  } else if (base::Contains(id_mappings_, kManufacturerAbbr)) {
+    make_ = id_mappings_[kManufacturerAbbr].front();
+  }
+
+  // Save model_.
+  if (base::Contains(id_mappings_, kModel)) {
+    model_ = id_mappings_[kModel].front();
+  } else if (base::Contains(id_mappings_, kModelAbbr)) {
+    model_ = id_mappings_[kModelAbbr].front();
+  }
+
+  // Save command_set_.
+  if (base::Contains(id_mappings_, kCommandSet)) {
+    command_set_ = id_mappings_[kCommandSet];
+  } else if (base::Contains(id_mappings_, kCommandSetAbbr)) {
+    command_set_ = id_mappings_[kCommandSetAbbr];
+  }
+}
+
+UsbPrinterId::UsbPrinterId() = default;
+UsbPrinterId::UsbPrinterId(const UsbPrinterId& other) = default;
+UsbPrinterId::~UsbPrinterId() = default;
+
+std::map<std::string, std::vector<std::string>> BuildDeviceIdMapping(
+    const std::vector<uint8_t>& data) {
+  // Must contain at least the length information.
+  if (data.size() < 2) {
+    return {};
+  }
+
+  std::map<std::string, std::vector<std::string>> ret;
+
+  // Convert to string to work on.
+  // Note: First two bytes contain the length, so we skip those.
+  std::string printer_id;
+  std::copy(data.begin() + 2, data.end(), std::back_inserter(printer_id));
+
+  // We filter out terms with empty keys or values.
+  base::StringPairs terms;
+  base::SplitStringIntoKeyValuePairs(printer_id, ':', ';', &terms);
+  for (const auto& term : terms) {
+    if (term.first.empty()) {
+      continue;
+    }
+
+    std::vector<std::string> values = base::SplitString(
+        term.second, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+    if (values.empty()) {
+      continue;
+    }
+
+    ret[term.first] = values;
+  }
+
+  return ret;
+}
+
+}  // namespace chromeos
diff --git a/chromeos/printing/usb_printer_id.h b/chromeos/printing/usb_printer_id.h
new file mode 100644
index 0000000..3df4257
--- /dev/null
+++ b/chromeos/printing/usb_printer_id.h
@@ -0,0 +1,60 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_PRINTING_USB_PRINTER_ID_H_
+#define CHROMEOS_PRINTING_USB_PRINTER_ID_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "chromeos/chromeos_export.h"
+
+namespace chromeos {
+
+// This class parses and holds the IEEE 1284 Device ID string as queried
+// from a USB-connected printer.
+class CHROMEOS_EXPORT UsbPrinterId {
+ public:
+  UsbPrinterId();
+  UsbPrinterId(const UsbPrinterId& other);
+  ~UsbPrinterId();
+
+  // Expects |printer_id_data| to contain the data portion response to a USB
+  // Printer Class-Specific GET_DEVICE_ID Request.
+  explicit UsbPrinterId(const std::vector<uint8_t>& printer_id_data);
+
+  // Accessors.
+  std::string make() const { return make_; }
+  std::string model() const { return model_; }
+  std::vector<std::string> command_set() const { return command_set_; }
+
+  // Setters (only used in testing).
+  void set_make(std::string make) { make_ = make; }
+  void set_model(std::string model) { model_ = model; }
+  void set_command_set(std::vector<std::string> command_set) {
+    command_set_ = std::move(command_set);
+  }
+
+ private:
+  std::string make_;
+  std::string model_;
+
+  // List of supported document formats (MIME types).
+  std::vector<std::string> command_set_;
+
+  // Holds the fully parsed IEEE 1284 Device ID.
+  std::map<std::string, std::vector<std::string>> id_mappings_;
+};
+
+// Expects data to hold a IEEE 1284 Device ID. Parses |data| and returns the
+// resulting key-value(s) pairs.
+CHROMEOS_EXPORT std::map<std::string, std::vector<std::string>>
+BuildDeviceIdMapping(const std::vector<uint8_t>& data);
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_PRINTING_USB_PRINTER_ID_H_
diff --git a/chromeos/printing/usb_printer_id_unittest.cc b/chromeos/printing/usb_printer_id_unittest.cc
new file mode 100644
index 0000000..7ad21a1
--- /dev/null
+++ b/chromeos/printing/usb_printer_id_unittest.cc
@@ -0,0 +1,74 @@
+// 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 "chromeos/printing/usb_printer_id.h"
+
+#include <algorithm>
+#include <map>
+#include <string>
+
+#include "base/strings/string_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+namespace {
+
+using testing::IsEmpty;
+
+using MapType = std::map<std::string, std::vector<std::string>>;
+
+MapType GetDefaultDeviceId() {
+  MapType ret;
+
+  // Make.
+  ret["MFG"].push_back("EPSON");
+
+  // Model.
+  ret["MDL"].push_back("ET-2700");
+
+  // Command set.
+  ret["CMD"].push_back("ESCPL2");
+  ret["CMD"].push_back("BDC");
+  ret["CMD"].push_back("D4");
+  ret["CMD"].push_back("END4");
+  ret["CMD"].push_back("GENEP");
+
+  return ret;
+}
+
+std::string MapToString(const MapType& map) {
+  std::vector<std::string> terms;
+  for (auto& term : map) {
+    std::string values = base::JoinString(term.second, ",");
+    terms.push_back(base::JoinString({term.first, values}, ":"));
+  }
+
+  std::string device_id_str = "xx";  // Two unused bytes for the length.
+  device_id_str += base::JoinString(terms, ";") + ";";
+  return device_id_str;
+}
+
+std::vector<uint8_t> MapToBuffer(const MapType& map) {
+  std::string device_id_str = MapToString(map);
+
+  std::vector<uint8_t> ret;
+  std::copy(device_id_str.begin(), device_id_str.end(),
+            std::back_inserter(ret));
+  return ret;
+}
+
+TEST(UsbPrinterIdTest, EmptyDeviceId) {
+  EXPECT_THAT(BuildDeviceIdMapping({}), IsEmpty());
+}
+
+// Tests that we get the same map back after parsing.
+TEST(UsbPrinterIdTest, SimpleSanityTest) {
+  MapType mapping = GetDefaultDeviceId();
+  std::vector<uint8_t> buffer = MapToBuffer(mapping);
+  EXPECT_EQ(mapping, BuildDeviceIdMapping(buffer));
+}
+
+}  // namespace
+}  // namespace chromeos
diff --git a/chromeos/services/assistant/assistant_manager_service_impl.cc b/chromeos/services/assistant/assistant_manager_service_impl.cc
index 1da8f63..fb9c0f7 100644
--- a/chromeos/services/assistant/assistant_manager_service_impl.cc
+++ b/chromeos/services/assistant/assistant_manager_service_impl.cc
@@ -1098,6 +1098,12 @@
 void AssistantManagerServiceImpl::OnStartFinished() {
   ENSURE_MAIN_THREAD(&AssistantManagerServiceImpl::OnStartFinished);
 
+  // It is possible the |assistant_manager_| was destructed before the
+  // rescheduled main thread task got a chance to run. We check this and also
+  // try to avoid double run by check |start_finished_|.
+  if (!assistant_manager_ || start_finished_)
+    return;
+
   // TODO(b/129896357): find a better place for additional setups.
   start_finished_ = true;
 
diff --git a/chromeos/services/device_sync/cryptauth_ecies_encryptor.cc b/chromeos/services/device_sync/cryptauth_ecies_encryptor.cc
index 30d79a5..ec31a131 100644
--- a/chromeos/services/device_sync/cryptauth_ecies_encryptor.cc
+++ b/chromeos/services/device_sync/cryptauth_ecies_encryptor.cc
@@ -34,6 +34,11 @@
     const std::string& key)
     : payload(payload), key(key) {}
 
+bool CryptAuthEciesEncryptor::PayloadAndKey::operator==(
+    const PayloadAndKey& other) const {
+  return payload == other.payload && key == other.key;
+}
+
 CryptAuthEciesEncryptor::CryptAuthEciesEncryptor() = default;
 
 CryptAuthEciesEncryptor::~CryptAuthEciesEncryptor() = default;
diff --git a/chromeos/services/device_sync/cryptauth_ecies_encryptor.h b/chromeos/services/device_sync/cryptauth_ecies_encryptor.h
index 51bfc45..e86e4a69 100644
--- a/chromeos/services/device_sync/cryptauth_ecies_encryptor.h
+++ b/chromeos/services/device_sync/cryptauth_ecies_encryptor.h
@@ -27,6 +27,7 @@
   struct PayloadAndKey {
     PayloadAndKey();
     PayloadAndKey(const std::string& payload, const std::string& key);
+    bool operator==(const PayloadAndKey& other) const;
 
     // Unencrypted/Encrypted payload to be encrypted/decrypted.
     std::string payload;
diff --git a/components/arc/BUILD.gn b/components/arc/BUILD.gn
index 7bdab54..0ebd5f2 100644
--- a/components/arc/BUILD.gn
+++ b/components/arc/BUILD.gn
@@ -32,6 +32,7 @@
     "intent_helper/arc_intent_helper_bridge.cc",
     "intent_helper/arc_intent_helper_bridge.h",
     "intent_helper/arc_intent_helper_observer.h",
+    "intent_helper/factory_reset_delegate.h",
     "intent_helper/font_size_util.cc",
     "intent_helper/font_size_util.h",
     "intent_helper/intent_constants.cc",
diff --git a/components/arc/common/intent_helper.mojom b/components/arc/common/intent_helper.mojom
index a5d94012..675db19 100644
--- a/components/arc/common/intent_helper.mojom
+++ b/components/arc/common/intent_helper.mojom
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-// Next MinVersion: 27
+// Next MinVersion: 28
 
 module arc.mojom;
 
@@ -173,7 +173,7 @@
 
 // Handles intents from ARC in Chrome.
 // Deprecated method ID: 4
-// Next method ID: 11
+// Next method ID: 12
 interface IntentHelperHost {
   // Called when icons associated with the package are no longer up to date.
   [MinVersion=3] OnIconInvalidated@1(string package_name);
@@ -220,6 +220,10 @@
 
   // Records Sharing files feature's metrics via Chrome.
   [MinVersion=24] RecordShareFilesMetrics@9(ShareFiles flag);
+
+  // Does a reset of ARC; this wipes /data, and then re-calls on OOBE for
+  // account binding to happen again, as if the user just went through OOBE.
+  [MinVersion=27] FactoryResetArc@11();
 };
 
 // Sends intents to ARC on behalf of Chrome.
diff --git a/components/arc/intent_helper/arc_intent_helper_bridge.cc b/components/arc/intent_helper/arc_intent_helper_bridge.cc
index 251884d..6a38030 100644
--- a/components/arc/intent_helper/arc_intent_helper_bridge.cc
+++ b/components/arc/intent_helper/arc_intent_helper_bridge.cc
@@ -18,6 +18,7 @@
 #include "components/arc/arc_browser_context_keyed_service_factory_base.h"
 #include "components/arc/arc_service_manager.h"
 #include "components/arc/audio/arc_audio_bridge.h"
+#include "components/arc/intent_helper/factory_reset_delegate.h"
 #include "components/arc/intent_helper/open_url_delegate.h"
 #include "components/arc/session/arc_bridge_service.h"
 #include "components/url_formatter/url_fixer.h"
@@ -76,6 +77,7 @@
 // Not owned. Must outlive all ArcIntentHelperBridge instances. Typically this
 // is ChromeNewWindowClient in the browser.
 OpenUrlDelegate* g_open_url_delegate = nullptr;
+FactoryResetDelegate* g_factory_reset_delegate = nullptr;
 
 // Singleton factory for ArcIntentHelperBridge.
 class ArcIntentHelperBridgeFactory
@@ -146,6 +148,12 @@
   g_open_url_delegate = delegate;
 }
 
+// static
+void ArcIntentHelperBridge::SetFactoryResetDelegate(
+    FactoryResetDelegate* delegate) {
+  g_factory_reset_delegate = delegate;
+}
+
 ArcIntentHelperBridge::ArcIntentHelperBridge(content::BrowserContext* context,
                                              ArcBridgeService* bridge_service)
     : context_(context),
@@ -225,6 +233,10 @@
   }
 }
 
+void ArcIntentHelperBridge::FactoryResetArc() {
+  g_factory_reset_delegate->ResetArc();
+}
+
 void ArcIntentHelperBridge::OpenWallpaperPicker() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   RecordOpenType(ArcIntentHelperOpenType::WALLPAPER_PICKER);
diff --git a/components/arc/intent_helper/arc_intent_helper_bridge.h b/components/arc/intent_helper/arc_intent_helper_bridge.h
index 8cdf2c3..2286c48 100644
--- a/components/arc/intent_helper/arc_intent_helper_bridge.h
+++ b/components/arc/intent_helper/arc_intent_helper_bridge.h
@@ -29,6 +29,7 @@
 namespace arc {
 
 class ArcBridgeService;
+class FactoryResetDelegate;
 class IntentFilter;
 class OpenUrlDelegate;
 
@@ -51,6 +52,8 @@
 
   static void SetOpenUrlDelegate(OpenUrlDelegate* delegate);
 
+  static void SetFactoryResetDelegate(FactoryResetDelegate* delegate);
+
   ArcIntentHelperBridge(content::BrowserContext* context,
                         ArcBridgeService* bridge_service);
   ~ArcIntentHelperBridge() override;
@@ -74,6 +77,7 @@
   void OpenWallpaperPicker() override;
   void SetWallpaperDeprecated(const std::vector<uint8_t>& jpeg_data) override;
   void OpenVolumeControl() override;
+  void FactoryResetArc() override;
   void OnOpenWebApp(const std::string& url) override;
   void RecordShareFilesMetrics(mojom::ShareFiles flag) override;
 
diff --git a/components/arc/intent_helper/factory_reset_delegate.h b/components/arc/intent_helper/factory_reset_delegate.h
new file mode 100644
index 0000000..edddbe3
--- /dev/null
+++ b/components/arc/intent_helper/factory_reset_delegate.h
@@ -0,0 +1,23 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_ARC_INTENT_HELPER_FACTORY_RESET_DELEGATE_H_
+#define COMPONENTS_ARC_INTENT_HELPER_FACTORY_RESET_DELEGATE_H_
+
+#include "components/arc/common/intent_helper.mojom.h"
+
+namespace arc {
+
+class FactoryResetDelegate {
+ public:
+  virtual ~FactoryResetDelegate() = default;
+
+  // Does a reset of ARC; this wipes /data, and then re-calls on OOBE for
+  // account binding to happen again, as if the user just went through OOBE.
+  virtual void ResetArc() = 0;
+};
+
+}  // namespace arc
+
+#endif  // COMPONENTS_ARC_INTENT_HELPER_FACTORY_RESET_DELEGATE_H_
diff --git a/components/autofill/content/browser/BUILD.gn b/components/autofill/content/browser/BUILD.gn
index 0d8a0b4..1a228a58 100644
--- a/components/autofill/content/browser/BUILD.gn
+++ b/components/autofill/content/browser/BUILD.gn
@@ -49,6 +49,10 @@
     "//ui/gfx/geometry",
     "//url",
   ]
+
+  if (!is_android) {
+    deps += [ "//components/autofill/content/browser/webauthn" ]
+  }
 }
 
 proto_library("risk_proto") {
diff --git a/components/autofill/content/browser/content_autofill_driver.cc b/components/autofill/content/browser/content_autofill_driver.cc
index 080139b..30a66ee 100644
--- a/components/autofill/content/browser/content_autofill_driver.cc
+++ b/components/autofill/content/browser/content_autofill_driver.cc
@@ -7,12 +7,14 @@
 #include <utility>
 #include <vector>
 
+#include "build/build_config.h"
 #include "components/autofill/content/browser/content_autofill_driver_factory.h"
 #include "components/autofill/core/browser/autofill_client.h"
 #include "components/autofill/core/browser/autofill_external_delegate.h"
 #include "components/autofill/core/browser/autofill_handler_proxy.h"
 #include "components/autofill/core/browser/autofill_manager.h"
 #include "components/autofill/core/browser/form_structure.h"
+#include "components/autofill/core/browser/payments/payments_service_url.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_handle.h"
@@ -24,8 +26,10 @@
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/origin_util.h"
+#include "services/service_manager/public/cpp/interface_provider.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
 #include "ui/gfx/geometry/size_f.h"
+#include "url/origin.h"
 
 namespace autofill {
 
@@ -96,6 +100,17 @@
   return render_frame_host_->GetRenderViewHost() != nullptr;
 }
 
+void ContentAutofillDriver::ConnectToAuthenticator(
+    blink::mojom::InternalAuthenticatorRequest request) {
+#if defined(OS_ANDROID)
+  render_frame_host_->GetJavaInterfaces()->GetInterface(std::move(request));
+#else
+  authenticator_impl_ = std::make_unique<content::InternalAuthenticatorImpl>(
+      render_frame_host_, url::Origin::Create(payments::GetBaseSecureUrl()));
+  authenticator_impl_->Bind(std::move(request));
+#endif
+}
+
 void ContentAutofillDriver::SendFormDataToRenderer(
     int query_id,
     RendererFormDataAction action,
diff --git a/components/autofill/content/browser/content_autofill_driver.h b/components/autofill/content/browser/content_autofill_driver.h
index 18e5fa1..cb81f63f 100644
--- a/components/autofill/content/browser/content_autofill_driver.h
+++ b/components/autofill/content/browser/content_autofill_driver.h
@@ -9,7 +9,9 @@
 #include <string>
 
 #include "base/supports_user_data.h"
+#include "build/build_config.h"
 #include "components/autofill/content/browser/key_press_handler_manager.h"
+#include "components/autofill/content/browser/webauthn/internal_authenticator_impl.h"
 #include "components/autofill/content/common/autofill_agent.mojom.h"
 #include "components/autofill/content/common/autofill_driver.mojom.h"
 #include "components/autofill/core/browser/autofill_driver.h"
@@ -54,6 +56,8 @@
   net::URLRequestContextGetter* GetURLRequestContext() override;
   scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() override;
   bool RendererIsAvailable() override;
+  void ConnectToAuthenticator(
+      blink::mojom::InternalAuthenticatorRequest request) override;
   void SendFormDataToRenderer(int query_id,
                               RendererFormDataAction action,
                               const FormData& data) override;
@@ -155,6 +159,11 @@
   // a common root.
   AutofillManager* autofill_manager_;
 
+#if !defined(OS_ANDROID)
+  // Implementation of the InternalAuthenticator mojom.
+  std::unique_ptr<content::InternalAuthenticatorImpl> authenticator_impl_;
+#endif
+
   // AutofillExternalDelegate instance that this object instantiates in the
   // case where the Autofill native UI is enabled.
   std::unique_ptr<AutofillExternalDelegate> autofill_external_delegate_;
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn
index b3395cd82..8630436 100644
--- a/components/autofill/core/browser/BUILD.gn
+++ b/components/autofill/core/browser/BUILD.gn
@@ -150,8 +150,6 @@
     "payments/credit_card_access_manager.h",
     "payments/credit_card_cvc_authenticator.cc",
     "payments/credit_card_cvc_authenticator.h",
-    "payments/credit_card_fido_authenticator.cc",
-    "payments/credit_card_fido_authenticator.h",
     "payments/credit_card_save_manager.cc",
     "payments/credit_card_save_manager.h",
     "payments/credit_card_save_strike_database.cc",
@@ -303,6 +301,8 @@
       "autofill_credit_card_policy_handler.h",
       "autofill_policy_handler.cc",
       "autofill_policy_handler.h",
+      "payments/credit_card_fido_authenticator.cc",
+      "payments/credit_card_fido_authenticator.h",
     ]
   }
 
@@ -361,6 +361,7 @@
     deps += [
       "//components/policy/core/browser",
       "//components/policy/core/common",
+      "//third_party/blink/public/common",
     ]
   }
 
@@ -387,8 +388,6 @@
     "mock_autocomplete_history_manager.h",
     "payments/test_authentication_requester.cc",
     "payments/test_authentication_requester.h",
-    "payments/test_credit_card_fido_authenticator.cc",
-    "payments/test_credit_card_fido_authenticator.h",
     "payments/test_credit_card_save_manager.cc",
     "payments/test_credit_card_save_manager.h",
     "payments/test_credit_card_save_strike_database.cc",
@@ -463,6 +462,15 @@
     "//ui/gfx:test_support",
     "//ui/gfx/geometry",
   ]
+
+  if (!is_ios) {
+    sources += [
+      "payments/test_credit_card_fido_authenticator.cc",
+      "payments/test_credit_card_fido_authenticator.h",
+    ]
+
+    deps += [ "//third_party/blink/public/common" ]
+  }
 }
 
 bundle_data("unit_tests_bundle_data") {
@@ -535,7 +543,6 @@
     "payments/autofill_wallet_data_type_controller_unittest.cc",
     "payments/credit_card_access_manager_unittest.cc",
     "payments/credit_card_cvc_authenticator_unittest.cc",
-    "payments/credit_card_fido_authenticator_unittest.cc",
     "payments/credit_card_save_manager_unittest.cc",
     "payments/full_card_request_unittest.cc",
     "payments/legal_message_line_unittest.cc",
@@ -578,6 +585,7 @@
       "autofill_address_policy_handler_unittest.cc",
       "autofill_credit_card_policy_handler_unittest.cc",
       "autofill_policy_handler_unittest.cc",
+      "payments/credit_card_fido_authenticator_unittest.cc",
     ]
   }
 
@@ -639,7 +647,10 @@
   ]
 
   if (!is_ios) {
-    deps += [ "//components/policy/core/common" ]
+    deps += [
+      "//components/policy/core/common",
+      "//third_party/blink/public/common",
+    ]
   }
 }
 
diff --git a/components/autofill/core/browser/DEPS b/components/autofill/core/browser/DEPS
index d668f841..7cca31e 100644
--- a/components/autofill/core/browser/DEPS
+++ b/components/autofill/core/browser/DEPS
@@ -59,6 +59,13 @@
   "autofill_metrics_unittest\.cc": [
     "+components/ukm",
   ],
+  "(test_)?credit_card_fido_authenticator\.(cc|h)": [
+    "+third_party/blink/public/mojom/webauthn/authenticator.mojom.h",
+    "+third_party/blink/public/mojom/webauthn/internal_authenticator.mojom.h",
+  ],
+  "autofill_driver\.h": [
+    "+third_party/blink/public/mojom/webauthn/internal_authenticator.mojom.h"
+  ],
   "credit_card_save_manager_unittest\.cc": [
     "+components/ukm",
   ],
diff --git a/components/autofill/core/browser/autofill_driver.h b/components/autofill/core/browser/autofill_driver.h
index e57ceed..dca5305 100644
--- a/components/autofill/core/browser/autofill_driver.h
+++ b/components/autofill/core/browser/autofill_driver.h
@@ -8,8 +8,13 @@
 #include <vector>
 
 #include "base/memory/scoped_refptr.h"
+#include "build/build_config.h"
 #include "components/autofill/core/common/form_data.h"
 
+#if !defined(OS_IOS)
+#include "third_party/blink/public/mojom/webauthn/internal_authenticator.mojom.h"
+#endif
+
 namespace net {
 class URLRequestContextGetter;
 }
@@ -57,6 +62,12 @@
   // Returns true iff the renderer is available for communication.
   virtual bool RendererIsAvailable() = 0;
 
+#if !defined(OS_IOS)
+  // Binds the mojom request in order to facilitate WebAuthn flows.
+  virtual void ConnectToAuthenticator(
+      blink::mojom::InternalAuthenticatorRequest request) = 0;
+#endif
+
   // Forwards |data| to the renderer. |query_id| is the id of the renderer's
   // original request for the data. |action| is the action the renderer should
   // perform with the |data|. This method is a no-op if the renderer is not
diff --git a/components/autofill/core/browser/autofill_manager.cc b/components/autofill/core/browser/autofill_manager.cc
index e92a5e6e..a2964b1 100644
--- a/components/autofill/core/browser/autofill_manager.cc
+++ b/components/autofill/core/browser/autofill_manager.cc
@@ -1262,7 +1262,7 @@
       driver()->IsInMainFrame(), form_interactions_ukm_logger_.get(),
       personal_data_, client_));
   credit_card_access_manager_.reset(new CreditCardAccessManager(
-      client_, personal_data_, credit_card_form_event_logger_.get()));
+      driver(), client_, personal_data_, credit_card_form_event_logger_.get()));
 #if defined(OS_ANDROID) || defined(OS_IOS)
   autofill_assistant_.Reset();
 #endif
@@ -1310,6 +1310,7 @@
               personal_data_,
               client_)),
       credit_card_access_manager_(std::make_unique<CreditCardAccessManager>(
+          driver,
           client_,
           personal_data_,
           credit_card_form_event_logger_.get())),
diff --git a/components/autofill/core/browser/autofill_test_utils.cc b/components/autofill/core/browser/autofill_test_utils.cc
index ebaee70..9ff57ce1 100644
--- a/components/autofill/core/browser/autofill_test_utils.cc
+++ b/components/autofill/core/browser/autofill_test_utils.cc
@@ -399,6 +399,20 @@
   return credit_card;
 }
 
+CreditCard GetExpiredCreditCard() {
+  CreditCard credit_card(base::GenerateGUID(), kEmptyOrigin);
+  SetCreditCardInfo(&credit_card, "Test User", "4111111111111111" /* Visa */,
+                    "11", "2002", "1");
+  return credit_card;
+}
+
+CreditCard GetIncompleteCreditCard() {
+  CreditCard credit_card(base::GenerateGUID(), kEmptyOrigin);
+  SetCreditCardInfo(&credit_card, "", "4111111111111111" /* Visa */, "11",
+                    "2022", "1");
+  return credit_card;
+}
+
 CreditCard GetVerifiedCreditCard() {
   CreditCard credit_card(GetCreditCard());
   credit_card.set_origin(kSettingsOrigin);
diff --git a/components/autofill/core/browser/autofill_test_utils.h b/components/autofill/core/browser/autofill_test_utils.h
index f8e34221..678a211 100644
--- a/components/autofill/core/browser/autofill_test_utils.h
+++ b/components/autofill/core/browser/autofill_test_utils.h
@@ -124,6 +124,13 @@
 // Returns a credit card full of dummy info, different to the above.
 CreditCard GetCreditCard2();
 
+// Returns an expired credit card full of fake info.
+CreditCard GetExpiredCreditCard();
+
+// Returns an incomplete credit card full of fake info with card holder's name
+// missing.
+CreditCard GetIncompleteCreditCard();
+
 // Returns a masked server card full of dummy info.
 CreditCard GetMaskedServerCard();
 CreditCard GetMaskedServerCardAmex();
diff --git a/components/autofill/core/browser/payments/credit_card_access_manager.cc b/components/autofill/core/browser/payments/credit_card_access_manager.cc
index 05f87c3..87adc19 100644
--- a/components/autofill/core/browser/payments/credit_card_access_manager.cc
+++ b/components/autofill/core/browser/payments/credit_card_access_manager.cc
@@ -17,6 +17,7 @@
 #include "base/task/post_task.h"
 #include "base/task/task_traits.h"
 #include "base/time/time.h"
+#include "build/build_config.h"
 #include "components/autofill/core/browser/autofill_client.h"
 #include "components/autofill/core/browser/autofill_driver.h"
 #include "components/autofill/core/browser/autofill_manager.h"
@@ -41,16 +42,20 @@
 }  // namespace
 
 CreditCardAccessManager::CreditCardAccessManager(
+    AutofillDriver* driver,
     AutofillManager* autofill_manager)
     : CreditCardAccessManager(
+          driver,
           autofill_manager->client(),
           autofill_manager->client()->GetPersonalDataManager()) {}
 
 CreditCardAccessManager::CreditCardAccessManager(
+    AutofillDriver* driver,
     AutofillClient* client,
     PersonalDataManager* personal_data_manager,
     CreditCardFormEventLogger* form_event_logger)
-    : client_(client),
+    : driver_(driver),
+      client_(client),
       payments_client_(client_->GetPaymentsClient()),
       personal_data_manager_(personal_data_manager),
       form_event_logger_(form_event_logger),
@@ -147,16 +152,26 @@
 void CreditCardAccessManager::PrepareToFetchCreditCard() {
   // Reset in case a late response was ignored.
   ready_to_start_authentication_.Reset();
+#if !defined(OS_IOS)
+  GetOrCreateFIDOAuthenticator()->IsUserVerifiable(base::BindOnce(
+      &CreditCardAccessManager::GetUnmaskDetailsIfUserIsVerifiable,
+      weak_ptr_factory_.GetWeakPtr()));
+#endif
+}
 
-  // If user is not verifiable, the only option is to perform CVC Auth, which
-  // does not require unmask details.
-  if (!GetOrCreateFIDOAuthenticator()->IsUserVerifiable())
-    return;
+void CreditCardAccessManager::GetUnmaskDetailsIfUserIsVerifiable(
+    bool is_user_verifiable) {
+  is_user_verifiable_ = is_user_verifiable;
 
-  payments_client_->GetUnmaskDetails(
-      base::BindOnce(&CreditCardAccessManager::OnDidGetUnmaskDetails,
-                     weak_ptr_factory_.GetWeakPtr()),
-      personal_data_manager_->app_locale());
+  // If user is verifiable, then make preflight call to payments to fetch unmask
+  // details, otherwise the only option is to perform CVC Auth, which does not
+  // require any.
+  if (is_user_verifiable_) {
+    payments_client_->GetUnmaskDetails(
+        base::BindOnce(&CreditCardAccessManager::OnDidGetUnmaskDetails,
+                       weak_ptr_factory_.GetWeakPtr()),
+        personal_data_manager_->app_locale());
+  }
 }
 
 void CreditCardAccessManager::OnDidGetUnmaskDetails(
@@ -218,10 +233,14 @@
           unmask_details_.fido_eligible_card_ids.end();
 
   if (card_is_eligible_for_fido) {
+#if defined(OS_IOS)
+    NOTREACHED();
+#else
     DCHECK(unmask_details_.fido_request_options.is_dict());
     GetOrCreateFIDOAuthenticator()->Authenticate(
-        card_, weak_ptr_factory_.GetWeakPtr(),
+        card_, weak_ptr_factory_.GetWeakPtr(), form_parsed_timestamp_,
         std::move(unmask_details_.fido_request_options));
+#endif
   } else {
     GetOrCreateCVCAuthenticator()->Authenticate(
         card_, weak_ptr_factory_.GetWeakPtr(), personal_data_manager_,
@@ -236,13 +255,15 @@
   return cvc_authenticator_.get();
 }
 
+#if !defined(OS_IOS)
 CreditCardFIDOAuthenticator*
 CreditCardAccessManager::GetOrCreateFIDOAuthenticator() {
   if (!fido_authenticator_)
     fido_authenticator_ =
-        std::make_unique<CreditCardFIDOAuthenticator>(client_);
+        std::make_unique<CreditCardFIDOAuthenticator>(driver_, client_);
   return fido_authenticator_.get();
 }
+#endif
 
 void CreditCardAccessManager::OnCVCAuthenticationComplete(
     bool did_succeed,
@@ -252,6 +273,7 @@
   accessor_->OnCreditCardFetched(did_succeed, card, cvc);
 }
 
+#if !defined(OS_IOS)
 void CreditCardAccessManager::OnFIDOAuthenticationComplete(
     bool did_succeed,
     const CreditCard* card) {
@@ -266,10 +288,15 @@
         form_parsed_timestamp_);
   }
 }
+#endif
 
 bool CreditCardAccessManager::AuthenticationRequiresUnmaskDetails() {
-  return GetOrCreateFIDOAuthenticator()->IsUserVerifiable() &&
+#if defined(OS_IOS)
+  return false;
+#else
+  return is_user_verifiable_.value_or(false) &&
          GetOrCreateFIDOAuthenticator()->IsUserOptedIn();
+#endif
 }
 
 bool CreditCardAccessManager::IsLocalCard(const CreditCard* card) {
diff --git a/components/autofill/core/browser/payments/credit_card_access_manager.h b/components/autofill/core/browser/payments/credit_card_access_manager.h
index 84ff78e..71b63791 100644
--- a/components/autofill/core/browser/payments/credit_card_access_manager.h
+++ b/components/autofill/core/browser/payments/credit_card_access_manager.h
@@ -13,23 +13,31 @@
 
 #include "base/strings/string16.h"
 #include "base/synchronization/waitable_event.h"
+#include "build/build_config.h"
 #include "components/autofill/core/browser/autofill_client.h"
 #include "components/autofill/core/browser/autofill_driver.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/autofill/core/browser/metrics/credit_card_form_event_logger.h"
 #include "components/autofill/core/browser/payments/credit_card_cvc_authenticator.h"
-#include "components/autofill/core/browser/payments/credit_card_fido_authenticator.h"
 #include "components/autofill/core/browser/payments/payments_client.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
 
+#if !defined(OS_IOS)
+#include "components/autofill/core/browser/payments/credit_card_fido_authenticator.h"
+#endif
+
 namespace autofill {
 
 class AutofillManager;
 
 // Manages logic for accessing credit cards either stored locally or stored
 // with Google Payments. Owned by AutofillManager.
+#if defined(OS_IOS)
+class CreditCardAccessManager : public CreditCardCVCAuthenticator::Requester {
+#else
 class CreditCardAccessManager : public CreditCardCVCAuthenticator::Requester,
                                 public CreditCardFIDOAuthenticator::Requester {
+#endif
  public:
   class Accessor {
    public:
@@ -40,8 +48,10 @@
         const base::string16& cvc = base::string16()) = 0;
   };
 
-  explicit CreditCardAccessManager(AutofillManager* autofill_manager);
+  explicit CreditCardAccessManager(AutofillDriver* driver,
+                                   AutofillManager* autofill_manager);
   CreditCardAccessManager(
+      AutofillDriver* driver,
       AutofillClient* client,
       PersonalDataManager* personal_data_manager,
       CreditCardFormEventLogger* credit_card_form_event_logger = nullptr);
@@ -79,7 +89,9 @@
 
   CreditCardCVCAuthenticator* GetOrCreateCVCAuthenticator();
 
+#if !defined(OS_IOS)
   CreditCardFIDOAuthenticator* GetOrCreateFIDOAuthenticator();
+#endif
 
  private:
   friend class AutofillAssistantTest;
@@ -87,10 +99,19 @@
   friend class AutofillMetricsTest;
   friend class CreditCardAccessManagerTest;
 
+#if !defined(OS_IOS)
   void set_fido_authenticator_for_testing(
       std::unique_ptr<CreditCardFIDOAuthenticator> fido_authenticator) {
     fido_authenticator_ = std::move(fido_authenticator);
   }
+#endif
+
+  // Invoked from CreditCardFIDOAuthenticator::IsUserVerifiable().
+  // |is_user_verifiable| is set to true only if user has a verifying platform
+  // authenticator. e.g. Touch/Face ID, Windows Hello, Android fingerprint,
+  // etc., is available and enabled. If set to true, then an Unmask Details
+  // request will be sent to Google Payments.
+  void GetUnmaskDetailsIfUserIsVerifiable(bool is_user_verifiable);
 
   // Sets |unmask_details_|. May be ignored if response is too late and user is
   // not opted-in for FIDO auth, or if user does not select a card.
@@ -108,9 +129,11 @@
       const CreditCard* card = nullptr,
       const base::string16& cvc = base::string16()) override;
 
+#if !defined(OS_IOS)
   // CreditCardFIDOAuthenticator::Requester:
   void OnFIDOAuthenticationComplete(bool did_succeed,
                                     const CreditCard* card = nullptr) override;
+#endif
 
   bool is_authentication_in_progress() {
     return is_authentication_in_progress_;
@@ -128,6 +151,9 @@
   // OnCVCAuthenticationComplete() to be executed.
   bool is_authentication_in_progress_ = false;
 
+  // The associated autofill driver. Weak reference.
+  AutofillDriver* const driver_;
+
   // The associated autofill client. Weak reference.
   AutofillClient* const client_;
 
@@ -148,7 +174,9 @@
 
   // Authenticators for card unmasking.
   std::unique_ptr<CreditCardCVCAuthenticator> cvc_authenticator_;
+#if !defined(OS_IOS)
   std::unique_ptr<CreditCardFIDOAuthenticator> fido_authenticator_;
+#endif
 
   // Suggested authentication method and other information to facilitate card
   // unmasking.
@@ -162,6 +190,11 @@
   // The credit card being accessed.
   const CreditCard* card_;
 
+  // Set to true only if user has a verifying platform authenticator.
+  // e.g. Touch/Face ID, Windows Hello, Android fingerprint, etc., is available
+  // and enabled.
+  base::Optional<bool> is_user_verifiable_;
+
   // The object attempting to access a card.
   base::WeakPtr<Accessor> accessor_;
 
diff --git a/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc b/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc
index 6b6fb9f..10b154d 100644
--- a/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc
@@ -39,7 +39,6 @@
 #include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/autofill/core/browser/form_structure.h"
 #include "components/autofill/core/browser/metrics/form_events.h"
-#include "components/autofill/core/browser/payments/test_credit_card_fido_authenticator.h"
 #include "components/autofill/core/browser/payments/test_payments_client.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill/core/browser/test_autofill_client.h"
@@ -70,6 +69,10 @@
 #include "ui/gfx/geometry/rect.h"
 #include "url/gurl.h"
 
+#if !defined(OS_IOS)
+#include "components/autofill/core/browser/payments/test_credit_card_fido_authenticator.h"
+#endif
+
 using base::ASCIIToUTF16;
 
 namespace autofill {
@@ -154,9 +157,13 @@
     autofill_client_.set_test_payments_client(
         std::unique_ptr<payments::TestPaymentsClient>(payments_client_));
     credit_card_access_manager_ = std::make_unique<CreditCardAccessManager>(
-        &autofill_client_, &personal_data_manager_, nullptr);
+        autofill_driver_.get(), &autofill_client_, &personal_data_manager_,
+        nullptr);
+#if !defined(OS_IOS)
     credit_card_access_manager_->set_fido_authenticator_for_testing(
-        std::make_unique<TestCreditCardFIDOAuthenticator>(&autofill_client_));
+        std::make_unique<TestCreditCardFIDOAuthenticator>(
+            autofill_driver_.get(), &autofill_client_));
+#endif
   }
 
   void TearDown() override {
@@ -197,6 +204,10 @@
     personal_data_manager_.AddServerCreditCard(masked_server_card);
   }
 
+  CreditCardCVCAuthenticator* GetCVCAuthenticator() {
+    return credit_card_access_manager_->GetOrCreateCVCAuthenticator();
+  }
+
   // Returns true if full card request was sent from CVC auth.
   bool GetRealPanForCVCAuth(AutofillClient::PaymentsRpcResult result,
                             const std::string& real_pan) {
@@ -210,6 +221,7 @@
     return true;
   }
 
+#if !defined(OS_IOS)
   // Returns true if full card request was sent from FIDO auth.
   bool GetRealPanForFIDOAuth(AutofillClient::PaymentsRpcResult result,
                              const std::string& real_pan) {
@@ -223,10 +235,6 @@
     return true;
   }
 
-  CreditCardCVCAuthenticator* GetCVCAuthenticator() {
-    return credit_card_access_manager_->GetOrCreateCVCAuthenticator();
-  }
-
   TestCreditCardFIDOAuthenticator* GetFIDOAuthenticator() {
     return static_cast<TestCreditCardFIDOAuthenticator*>(
         credit_card_access_manager_->GetOrCreateFIDOAuthenticator());
@@ -237,6 +245,7 @@
     // default. Once implemented, update this function along with
     // TestCreditCardFIDOAuthenticator to mock a user verification gesture.
   }
+#endif
 
   void InvokeUnmaskDetailsTimeout() {
     credit_card_access_manager_->ready_to_start_authentication_.Signal();
@@ -331,6 +340,9 @@
   CreateLocalCard(kTestGUID, kTestNumber);
   CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
 
+  credit_card_access_manager_->PrepareToFetchCreditCard();
+  WaitForCallbacks();
+
   credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr());
 
   EXPECT_TRUE(accessor_->did_succeed());
@@ -341,6 +353,9 @@
 TEST_F(CreditCardAccessManagerTest, FetchNullptrFailure) {
   personal_data_manager_.ClearCreditCards();
 
+  credit_card_access_manager_->PrepareToFetchCreditCard();
+  WaitForCallbacks();
+
   credit_card_access_manager_->FetchCreditCard(nullptr,
                                                accessor_->GetWeakPtr());
   EXPECT_FALSE(accessor_->did_succeed());
@@ -352,6 +367,9 @@
   CreateServerCard(kTestGUID, kTestNumber);
   CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
 
+  credit_card_access_manager_->PrepareToFetchCreditCard();
+  WaitForCallbacks();
+
   credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr());
 
   EXPECT_TRUE(GetRealPanForCVCAuth(AutofillClient::SUCCESS, kTestNumber));
@@ -365,6 +383,9 @@
   CreateServerCard(kTestGUID, kTestNumber);
   CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
 
+  credit_card_access_manager_->PrepareToFetchCreditCard();
+  WaitForCallbacks();
+
   credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr());
 
   EXPECT_TRUE(
@@ -378,6 +399,9 @@
   CreateServerCard(kTestGUID, kTestNumber);
   CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
 
+  credit_card_access_manager_->PrepareToFetchCreditCard();
+  WaitForCallbacks();
+
   credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr());
 
   EXPECT_TRUE(
@@ -401,6 +425,7 @@
   EXPECT_EQ(ASCIIToUTF16(kTestNumber), accessor_->number());
 }
 
+#if !defined(OS_IOS)
 // Ensures that CVC prompt is invoked after WebAuthn fails.
 TEST_F(CreditCardAccessManagerTest, FetchServerCardFIDOFailureCVCFallback) {
   CreateServerCard(kTestGUID, kTestNumber);
@@ -442,6 +467,7 @@
   EXPECT_TRUE(accessor_->did_succeed());
   EXPECT_EQ(ASCIIToUTF16(kTestNumber), accessor_->number());
 }
+#endif
 
 // Ensures that |is_authentication_in_progress_| is set correctly.
 TEST_F(CreditCardAccessManagerTest, AuthenticationInProgress) {
diff --git a/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc b/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc
index 584e03b06..a8893baa 100644
--- a/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc
+++ b/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc
@@ -4,16 +4,24 @@
 
 #include "components/autofill/core/browser/payments/credit_card_fido_authenticator.h"
 
+#include <string>
+#include <utility>
+#include <vector>
+
 #include "base/strings/string16.h"
 #include "components/autofill/core/browser/autofill_client.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/autofill/core/browser/payments/payments_client.h"
 #include "components/autofill/core/common/autofill_payments_features.h"
+#include "third_party/blink/public/mojom/webauthn/authenticator.mojom.h"
+#include "third_party/blink/public/mojom/webauthn/internal_authenticator.mojom.h"
 
 namespace autofill {
 
-CreditCardFIDOAuthenticator::CreditCardFIDOAuthenticator(AutofillClient* client)
-    : autofill_client_(client),
+CreditCardFIDOAuthenticator::CreditCardFIDOAuthenticator(AutofillDriver* driver,
+                                                         AutofillClient* client)
+    : autofill_driver_(driver),
+      autofill_client_(client),
       payments_client_(client->GetPaymentsClient()),
       weak_ptr_factory_(this) {}
 
@@ -22,18 +30,26 @@
 void CreditCardFIDOAuthenticator::Authenticate(
     const CreditCard* card,
     base::WeakPtr<Requester> requester,
+    base::TimeTicks form_parsed_timestamp,
     base::Value request_options) {
-  // TODO(crbug/949269): Add call to InternalAuthenticatorImpl::GetAssertion.
+  card_ = card;
   requester_ = requester;
-  requester_->OnFIDOAuthenticationComplete(false);
+  form_parsed_timestamp_ = form_parsed_timestamp;
+
+  requester_->OnFIDOAuthenticationComplete(/*did_succeed=*/false);
 }
 
-bool CreditCardFIDOAuthenticator::IsUserVerifiable() {
-  // TODO(crbug/949269): Add call to
-  // InternalAuthenticatorImpl::IsUserVerifyingPlatformAuthenticatorAvailable.
-  return base::FeatureList::IsEnabled(
-             features::kAutofillCreditCardAuthentication) &&
-         false;
+void CreditCardFIDOAuthenticator::IsUserVerifiable(
+    base::OnceCallback<void(bool)> callback) {
+  if (base::FeatureList::IsEnabled(
+          features::kAutofillCreditCardAuthentication)) {
+    autofill_driver_->ConnectToAuthenticator(
+        mojo::MakeRequest(&authenticator_));
+    authenticator_->IsUserVerifyingPlatformAuthenticatorAvailable(
+        std::move(callback));
+  } else {
+    std::move(callback).Run(false);
+  }
 }
 
 bool CreditCardFIDOAuthenticator::IsUserOptedIn() {
diff --git a/components/autofill/core/browser/payments/credit_card_fido_authenticator.h b/components/autofill/core/browser/payments/credit_card_fido_authenticator.h
index 2078016..3fed058 100644
--- a/components/autofill/core/browser/payments/credit_card_fido_authenticator.h
+++ b/components/autofill/core/browser/payments/credit_card_fido_authenticator.h
@@ -9,12 +9,16 @@
 
 #include "base/strings/string16.h"
 #include "components/autofill/core/browser/autofill_client.h"
+#include "components/autofill/core/browser/autofill_driver.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/autofill/core/browser/payments/full_card_request.h"
 #include "components/autofill/core/browser/payments/payments_client.h"
+#include "third_party/blink/public/mojom/webauthn/internal_authenticator.mojom.h"
 
 namespace autofill {
 
+using blink::mojom::InternalAuthenticatorPtr;
+
 // Authenticates credit card unmasking through FIDO authentication, using the
 // WebAuthn specification, standardized by the FIDO alliance. The Webauthn
 // specification defines an API to cryptographically bind a server and client,
@@ -30,18 +34,19 @@
         bool did_succeed,
         const CreditCard* card = nullptr) = 0;
   };
-  explicit CreditCardFIDOAuthenticator(AutofillClient* client);
+  CreditCardFIDOAuthenticator(AutofillDriver* driver, AutofillClient* client);
   virtual ~CreditCardFIDOAuthenticator();
 
   // Authentication
   void Authenticate(const CreditCard* card,
                     base::WeakPtr<Requester> requester,
+                    base::TimeTicks form_parsed_timestamp,
                     base::Value request_options);
 
-  // Returns true only if user has a verifying platform authenticator.
-  // e.g. Touch/Face ID, Windows Hello, Android Fingerprint etc is available and
-  // enabled.
-  virtual bool IsUserVerifiable();
+  // Invokes callback with true if user has a verifying platform authenticator.
+  // e.g. Touch/Face ID, Windows Hello, Android fingerprint, etc., is available
+  // and enabled. Otherwise invokes callback with false.
+  virtual void IsUserVerifiable(base::OnceCallback<void(bool)> callback);
 
   // Returns true only if the user has opted-in to use WebAuthn for autofill.
   virtual bool IsUserOptedIn();
@@ -51,12 +56,24 @@
   friend class CreditCardAccessManagerTest;
   friend class CreditCardFIDOAuthenticatorTest;
 
+  // Card being unmasked.
+  const CreditCard* card_;
+
+  // Meant for histograms recorded in FullCardRequest.
+  base::TimeTicks form_parsed_timestamp_;
+
+  // The associated autofill driver. Weak reference.
+  AutofillDriver* const autofill_driver_;
+
   // The associated autofill client. Weak reference.
   AutofillClient* const autofill_client_;
 
   // Payments client to make requests to Google Payments.
   payments::PaymentsClient* const payments_client_;
 
+  // Authenticator pointer to facilitate WebAuthn.
+  InternalAuthenticatorPtr authenticator_;
+
   // Responsible for getting the full card details, including the PAN and the
   // CVC.
   std::unique_ptr<payments::FullCardRequest> full_card_request_;
diff --git a/components/autofill/core/browser/payments/credit_card_fido_authenticator_unittest.cc b/components/autofill/core/browser/payments/credit_card_fido_authenticator_unittest.cc
index 92e41dd..e3a0c5a8 100644
--- a/components/autofill/core/browser/payments/credit_card_fido_authenticator_unittest.cc
+++ b/components/autofill/core/browser/payments/credit_card_fido_authenticator_unittest.cc
@@ -38,6 +38,7 @@
 #include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/autofill/core/browser/metrics/form_events.h"
 #include "components/autofill/core/browser/payments/test_authentication_requester.h"
+#include "components/autofill/core/browser/payments/test_credit_card_fido_authenticator.h"
 #include "components/autofill/core/browser/payments/test_payments_client.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill/core/browser/test_autofill_client.h"
@@ -113,8 +114,8 @@
             autofill_client_.GetIdentityManager(), &personal_data_manager_);
     autofill_client_.set_test_payments_client(
         std::unique_ptr<payments::TestPaymentsClient>(payments_client));
-    fido_authenticator_ =
-        std::make_unique<CreditCardFIDOAuthenticator>(&autofill_client_);
+    fido_authenticator_ = std::make_unique<CreditCardFIDOAuthenticator>(
+        autofill_driver_.get(), &autofill_client_);
   }
 
   void TearDown() override {
@@ -156,14 +157,17 @@
 }
 
 TEST_F(CreditCardFIDOAuthenticatorTest, IsUserVerifiableFalse) {
-  EXPECT_FALSE(fido_authenticator_->IsUserVerifiable());
+  fido_authenticator_->IsUserVerifiable(
+      base::BindOnce(&TestAuthenticationRequester::IsUserVerifiableCallback,
+                     requester_->GetWeakPtr()));
+  EXPECT_FALSE(requester_->is_user_verifiable().value());
 }
 
 TEST_F(CreditCardFIDOAuthenticatorTest, AuthenticateCardFailure) {
   CreditCard card = CreateServerCard(kTestGUID, kTestNumber);
 
   fido_authenticator_->Authenticate(&card, requester_->GetWeakPtr(),
-                                    base::Value());
+                                    base::TimeTicks::Now(), base::Value());
   EXPECT_FALSE(requester_->did_succeed());
 }
 
diff --git a/components/autofill/core/browser/payments/test_authentication_requester.cc b/components/autofill/core/browser/payments/test_authentication_requester.cc
index b6afed0..4a17806 100644
--- a/components/autofill/core/browser/payments/test_authentication_requester.cc
+++ b/components/autofill/core/browser/payments/test_authentication_requester.cc
@@ -30,6 +30,7 @@
   }
 }
 
+#if !defined(OS_IOS)
 void TestAuthenticationRequester::OnFIDOAuthenticationComplete(
     bool did_succeed,
     const CreditCard* card) {
@@ -40,4 +41,10 @@
   }
 }
 
+void TestAuthenticationRequester::IsUserVerifiableCallback(
+    bool is_user_verifiable) {
+  is_user_verifiable_ = is_user_verifiable;
+}
+#endif
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/payments/test_authentication_requester.h b/components/autofill/core/browser/payments/test_authentication_requester.h
index a57a3cd..cc97c70 100644
--- a/components/autofill/core/browser/payments/test_authentication_requester.h
+++ b/components/autofill/core/browser/payments/test_authentication_requester.h
@@ -6,18 +6,31 @@
 #define COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_TEST_AUTHENTICATION_REQUESTER_H_
 
 #include <memory>
+#include <string>
+#include <utility>
+#include <vector>
 
 #include "base/strings/string16.h"
+#include "build/build_config.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/autofill/core/browser/payments/credit_card_cvc_authenticator.h"
+
+#if !defined(OS_IOS)
 #include "components/autofill/core/browser/payments/credit_card_fido_authenticator.h"
+#endif
 
 namespace autofill {
 
-// Test class for requesting authentication from CreditCardCVCAuthenticator.
+// Test class for requesting authentication from CreditCardCVCAuthenticator or
+// CreditCardFIDOAuthenticator.
+#if defined(OS_IOS)
+class TestAuthenticationRequester
+    : public CreditCardCVCAuthenticator::Requester {
+#else
 class TestAuthenticationRequester
     : public CreditCardCVCAuthenticator::Requester,
       public CreditCardFIDOAuthenticator::Requester {
+#endif
  public:
   TestAuthenticationRequester();
   ~TestAuthenticationRequester() override;
@@ -28,17 +41,26 @@
       const CreditCard* card = nullptr,
       const base::string16& cvc = base::string16()) override;
 
+#if !defined(OS_IOS)
   // CreditCardFIDOAuthenticator::Requester:
   void OnFIDOAuthenticationComplete(bool did_succeed,
                                     const CreditCard* card = nullptr) override;
 
+  void IsUserVerifiableCallback(bool is_user_verifiable);
+#endif
+
   base::WeakPtr<TestAuthenticationRequester> GetWeakPtr();
 
-  base::string16 number() { return number_; }
+  base::Optional<bool> is_user_verifiable() { return is_user_verifiable_; }
 
   bool did_succeed() { return did_succeed_; }
 
+  base::string16 number() { return number_; }
+
  private:
+  // Set when CreditCardFIDOAuthenticator invokes IsUserVerifiableCallback().
+  base::Optional<bool> is_user_verifiable_;
+
   // Is set to true if authentication was successful.
   bool did_succeed_ = false;
 
diff --git a/components/autofill/core/browser/payments/test_credit_card_fido_authenticator.cc b/components/autofill/core/browser/payments/test_credit_card_fido_authenticator.cc
index ee60ad7..192047c 100644
--- a/components/autofill/core/browser/payments/test_credit_card_fido_authenticator.cc
+++ b/components/autofill/core/browser/payments/test_credit_card_fido_authenticator.cc
@@ -4,19 +4,25 @@
 
 #include "components/autofill/core/browser/payments/test_credit_card_fido_authenticator.h"
 
+#include <utility>
+
 #include "base/strings/string16.h"
 #include "components/autofill/core/browser/autofill_client.h"
+#include "components/autofill/core/browser/autofill_driver.h"
+#include "third_party/blink/public/mojom/webauthn/authenticator.mojom.h"
 
 namespace autofill {
 
 TestCreditCardFIDOAuthenticator::TestCreditCardFIDOAuthenticator(
+    AutofillDriver* driver,
     AutofillClient* client)
-    : CreditCardFIDOAuthenticator(client) {}
+    : CreditCardFIDOAuthenticator(driver, client) {}
 
 TestCreditCardFIDOAuthenticator::~TestCreditCardFIDOAuthenticator() {}
 
-bool TestCreditCardFIDOAuthenticator::IsUserVerifiable() {
-  return is_user_verifiable_;
+void TestCreditCardFIDOAuthenticator::IsUserVerifiable(
+    base::OnceCallback<void(bool)> callback) {
+  return std::move(callback).Run(is_user_verifiable_);
 }
 
 bool TestCreditCardFIDOAuthenticator::IsUserOptedIn() {
diff --git a/components/autofill/core/browser/payments/test_credit_card_fido_authenticator.h b/components/autofill/core/browser/payments/test_credit_card_fido_authenticator.h
index bf326792..eb462f3 100644
--- a/components/autofill/core/browser/payments/test_credit_card_fido_authenticator.h
+++ b/components/autofill/core/browser/payments/test_credit_card_fido_authenticator.h
@@ -6,19 +6,24 @@
 #define COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_TEST_CREDIT_CARD_FIDO_AUTHENTICATOR_H_
 
 #include <memory>
+#include <string>
+#include <vector>
 
 #include "base/strings/string16.h"
 #include "components/autofill/core/browser/autofill_client.h"
+#include "components/autofill/core/browser/autofill_driver.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/autofill/core/browser/payments/credit_card_fido_authenticator.h"
 #include "components/autofill/core/browser/payments/payments_client.h"
+#include "third_party/blink/public/mojom/webauthn/authenticator.mojom.h"
 
 namespace autofill {
 
 // Test class for CreditCardFIDOAuthenticator.
 class TestCreditCardFIDOAuthenticator : public CreditCardFIDOAuthenticator {
  public:
-  explicit TestCreditCardFIDOAuthenticator(AutofillClient* client);
+  explicit TestCreditCardFIDOAuthenticator(AutofillDriver* driver,
+                                           AutofillClient* client);
   ~TestCreditCardFIDOAuthenticator() override;
 
   void SetUserVerifiable(bool is_user_verifiable) {
@@ -30,7 +35,7 @@
   }
 
   // CreditCardFIDOAuthenticator:
-  bool IsUserVerifiable() override;
+  void IsUserVerifiable(base::OnceCallback<void(bool)> callback) override;
   bool IsUserOptedIn() override;
 
  private:
diff --git a/components/autofill/core/browser/test_autofill_driver.cc b/components/autofill/core/browser/test_autofill_driver.cc
index 2a1da042..92426282 100644
--- a/components/autofill/core/browser/test_autofill_driver.cc
+++ b/components/autofill/core/browser/test_autofill_driver.cc
@@ -3,6 +3,8 @@
 // found in the LICENSE file.
 
 #include "components/autofill/core/browser/test_autofill_driver.h"
+
+#include "build/build_config.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 #include "services/network/test/test_url_loader_factory.h"
@@ -40,6 +42,11 @@
   return true;
 }
 
+#if !defined(OS_IOS)
+void TestAutofillDriver::ConnectToAuthenticator(
+    blink::mojom::InternalAuthenticatorRequest request) {}
+#endif
+
 void TestAutofillDriver::SendFormDataToRenderer(int query_id,
                                                 RendererFormDataAction action,
                                                 const FormData& form_data) {
diff --git a/components/autofill/core/browser/test_autofill_driver.h b/components/autofill/core/browser/test_autofill_driver.h
index 52aba333..86fbd4e0 100644
--- a/components/autofill/core/browser/test_autofill_driver.h
+++ b/components/autofill/core/browser/test_autofill_driver.h
@@ -8,6 +8,7 @@
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
+#include "build/build_config.h"
 #include "components/autofill/core/browser/autofill_driver.h"
 #include "services/network/test/test_url_loader_factory.h"
 
@@ -27,6 +28,10 @@
   net::URLRequestContextGetter* GetURLRequestContext() override;
   scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() override;
   bool RendererIsAvailable() override;
+#if !defined(OS_IOS)
+  void ConnectToAuthenticator(
+      blink::mojom::InternalAuthenticatorRequest request) override;
+#endif
   void SendFormDataToRenderer(int query_id,
                               RendererFormDataAction action,
                               const FormData& data) override;
diff --git a/components/cronet/cronet_url_request.cc b/components/cronet/cronet_url_request.cc
index ef8ce2fd..b0476e53 100644
--- a/components/cronet/cronet_url_request.cc
+++ b/components/cronet/cronet_url_request.cc
@@ -24,6 +24,7 @@
 #include "net/http/http_util.h"
 #include "net/ssl/ssl_info.h"
 #include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
 #include "net/url_request/redirect_info.h"
 #include "net/url_request/url_request_context.h"
 
@@ -277,7 +278,7 @@
           << initial_url_.possibly_invalid_spec().c_str()
           << " priority: " << RequestPriorityToString(initial_priority_);
   url_request_ = context->GetURLRequestContext()->CreateRequest(
-      initial_url_, net::DEFAULT_PRIORITY, this);
+      initial_url_, net::DEFAULT_PRIORITY, this, MISSING_TRAFFIC_ANNOTATION);
   url_request_->SetLoadFlags(initial_load_flags_);
   url_request_->set_method(method);
   url_request_->SetExtraRequestHeaders(*request_headers);
diff --git a/components/favicon/core/BUILD.gn b/components/favicon/core/BUILD.gn
index b9e3a01..f3acac1 100644
--- a/components/favicon/core/BUILD.gn
+++ b/components/favicon/core/BUILD.gn
@@ -14,8 +14,6 @@
     "favicon_driver_observer.h",
     "favicon_handler.cc",
     "favicon_handler.h",
-    "favicon_request_handler.cc",
-    "favicon_request_handler.h",
     "favicon_server_fetcher_params.cc",
     "favicon_server_fetcher_params.h",
     "favicon_service.h",
@@ -28,6 +26,9 @@
     "favicon_util.h",
     "features.cc",
     "features.h",
+    "history_ui_favicon_request_handler.h",
+    "history_ui_favicon_request_handler_impl.cc",
+    "history_ui_favicon_request_handler_impl.h",
     "large_icon_service.h",
     "large_icon_service_impl.cc",
     "large_icon_service_impl.h",
@@ -53,8 +54,8 @@
   sources = [
     "fallback_url_util_unittest.cc",
     "favicon_handler_unittest.cc",
-    "favicon_request_handler_unittest.cc",
     "favicon_service_impl_unittest.cc",
+    "history_ui_favicon_request_handler_impl_unittest.cc",
     "large_icon_service_impl_unittest.cc",
   ]
 
diff --git a/components/favicon/core/history_ui_favicon_request_handler.h b/components/favicon/core/history_ui_favicon_request_handler.h
new file mode 100644
index 0000000..e0fa7167
--- /dev/null
+++ b/components/favicon/core/history_ui_favicon_request_handler.h
@@ -0,0 +1,80 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_FAVICON_CORE_HISTORY_UI_FAVICON_REQUEST_HANDLER_H_
+#define COMPONENTS_FAVICON_CORE_HISTORY_UI_FAVICON_REQUEST_HANDLER_H_
+
+#include "components/favicon_base/favicon_callback.h"
+#include "components/keyed_service/core/keyed_service.h"
+
+namespace base {
+class CancelableTaskTracker;
+}
+
+class GURL;
+
+namespace favicon {
+
+// The UI origin of an icon request.
+// TODO(victorvianna): Adopt same naming style used in the platform enum.
+// TODO(victorvianna): Rename enum to mention history UIs.
+enum class FaviconRequestOrigin {
+  // Unknown origin.
+  UNKNOWN,
+  // History page.
+  HISTORY,
+  // History synced tabs page (desktop only).
+  HISTORY_SYNCED_TABS,
+  // Recently closed tabs menu.
+  RECENTLY_CLOSED_TABS,
+};
+
+// Platform making the request.
+enum class FaviconRequestPlatform {
+  kMobile,
+  kDesktop,
+};
+
+// Keyed service for handling favicon requests made by a history UI, forwarding
+// them to local storage, sync or Google server accordingly. This service should
+// only be used by the UIs listed in the FaviconRequestOrigin enum. Requests
+// must be made by page url, as opposed to icon url.
+// TODO(victorvianna): Use a more natural order for the parameters in the API.
+// TODO(victorvianna): Remove |icon_url_for_uma| when we have access to the
+// FaviconUrlMapper.
+class HistoryUiFaviconRequestHandler : public KeyedService {
+ public:
+  // Requests favicon bitmap at |page_url| of size |desired_size_in_pixel|.
+  // Tries to fetch the icon from local storage and falls back to sync, or to
+  // Google favicon server if |favicon::kEnableHistoryFaviconsGoogleServerQuery|
+  // is enabled. If a non-empty |icon_url_for_uma| (optional) is passed, it will
+  // be used to record UMA about the grouping of requests to the favicon server.
+  // |request_platform| specifies whether the caller is mobile or desktop code.
+  virtual void GetRawFaviconForPageURL(
+      const GURL& page_url,
+      int desired_size_in_pixel,
+      favicon_base::FaviconRawBitmapCallback callback,
+      FaviconRequestOrigin request_origin,
+      FaviconRequestPlatform request_platform,
+      const GURL& icon_url_for_uma,
+      base::CancelableTaskTracker* tracker) = 0;
+
+  // Requests favicon image at |page_url|.
+  // Tries to fetch the icon from local storage and falls back to sync, or to
+  // Google favicon server if |favicon::kEnableHistoryFaviconsGoogleServerQuery|
+  // is enabled.
+  // If a non-empty |icon_url_for_uma| (optional) is passed, it will be used to
+  // record UMA about the grouping of requests to the favicon server.
+  // This method is only called by desktop code.
+  virtual void GetFaviconImageForPageURL(
+      const GURL& page_url,
+      favicon_base::FaviconImageCallback callback,
+      FaviconRequestOrigin request_origin,
+      const GURL& icon_url_for_uma,
+      base::CancelableTaskTracker* tracker) = 0;
+};
+
+}  // namespace favicon
+
+#endif  // COMPONENTS_FAVICON_CORE_HISTORY_UI_FAVICON_REQUEST_HANDLER_H_
diff --git a/components/favicon/core/favicon_request_handler.cc b/components/favicon/core/history_ui_favicon_request_handler_impl.cc
similarity index 88%
rename from components/favicon/core/favicon_request_handler.cc
rename to components/favicon/core/history_ui_favicon_request_handler_impl.cc
index 05f7a0b..3ca9cf3cf 100644
--- a/components/favicon/core/favicon_request_handler.cc
+++ b/components/favicon/core/history_ui_favicon_request_handler_impl.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/favicon/core/favicon_request_handler.h"
+#include "components/favicon/core/history_ui_favicon_request_handler_impl.h"
 
 #include <utility>
 
@@ -11,6 +11,7 @@
 #include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/task/cancelable_task_tracker.h"
 #include "components/favicon/core/favicon_server_fetcher_params.h"
 #include "components/favicon/core/favicon_service.h"
 #include "components/favicon/core/features.h"
@@ -19,6 +20,7 @@
 #include "components/favicon_base/favicon_util.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "ui/gfx/image/image_png_rep.h"
+#include "url/gurl.h"
 
 namespace favicon {
 
@@ -95,7 +97,7 @@
 
 }  // namespace
 
-FaviconRequestHandler::FaviconRequestHandler(
+HistoryUiFaviconRequestHandlerImpl::HistoryUiFaviconRequestHandlerImpl(
     const SyncedFaviconGetter& synced_favicon_getter,
     const CanSendHistoryDataGetter& can_send_history_data_getter,
     FaviconService* favicon_service,
@@ -108,9 +110,9 @@
   DCHECK(large_icon_service);
 }
 
-FaviconRequestHandler::~FaviconRequestHandler() {}
+HistoryUiFaviconRequestHandlerImpl::~HistoryUiFaviconRequestHandlerImpl() {}
 
-void FaviconRequestHandler::GetRawFaviconForPageURL(
+void HistoryUiFaviconRequestHandlerImpl::GetRawFaviconForPageURL(
     const GURL& page_url,
     int desired_size_in_pixel,
     favicon_base::FaviconRawBitmapCallback callback,
@@ -122,16 +124,16 @@
   favicon_service_->GetRawFaviconForPageURL(
       page_url, GetIconTypesForLocalQuery(), desired_size_in_pixel,
       kFallbackToHost,
-      base::BindOnce(&FaviconRequestHandler::OnBitmapLocalDataAvailable,
-                     weak_ptr_factory_.GetWeakPtr(), page_url,
-                     desired_size_in_pixel,
-                     /*response_callback=*/std::move(callback), request_origin,
-                     request_platform, icon_url_for_uma,
-                     CanQueryGoogleServer(request_origin), tracker),
+      base::BindOnce(
+          &HistoryUiFaviconRequestHandlerImpl::OnBitmapLocalDataAvailable,
+          weak_ptr_factory_.GetWeakPtr(), page_url, desired_size_in_pixel,
+          /*response_callback=*/std::move(callback), request_origin,
+          request_platform, icon_url_for_uma,
+          CanQueryGoogleServer(request_origin), tracker),
       tracker);
 }
 
-void FaviconRequestHandler::GetFaviconImageForPageURL(
+void HistoryUiFaviconRequestHandlerImpl::GetFaviconImageForPageURL(
     const GURL& page_url,
     favicon_base::FaviconImageCallback callback,
     FaviconRequestOrigin request_origin,
@@ -140,15 +142,15 @@
   // First attempt to find the icon locally.
   favicon_service_->GetFaviconImageForPageURL(
       page_url,
-      base::BindOnce(&FaviconRequestHandler::OnImageLocalDataAvailable,
-                     weak_ptr_factory_.GetWeakPtr(), page_url,
-                     /*response_callback=*/std::move(callback), request_origin,
-                     icon_url_for_uma, CanQueryGoogleServer(request_origin),
-                     tracker),
+      base::BindOnce(
+          &HistoryUiFaviconRequestHandlerImpl::OnImageLocalDataAvailable,
+          weak_ptr_factory_.GetWeakPtr(), page_url,
+          /*response_callback=*/std::move(callback), request_origin,
+          icon_url_for_uma, CanQueryGoogleServer(request_origin), tracker),
       tracker);
 }
 
-void FaviconRequestHandler::OnBitmapLocalDataAvailable(
+void HistoryUiFaviconRequestHandlerImpl::OnBitmapLocalDataAvailable(
     const GURL& page_url,
     int desired_size_in_pixel,
     favicon_base::FaviconRawBitmapCallback response_callback,
@@ -210,7 +212,7 @@
   std::move(response_callback).Run(favicon_base::FaviconRawBitmapResult());
 }
 
-void FaviconRequestHandler::OnImageLocalDataAvailable(
+void HistoryUiFaviconRequestHandlerImpl::OnImageLocalDataAvailable(
     const GURL& page_url,
     favicon_base::FaviconImageCallback response_callback,
     FaviconRequestOrigin origin,
@@ -266,7 +268,7 @@
   std::move(response_callback).Run(favicon_base::FaviconImageResult());
 }
 
-void FaviconRequestHandler::RequestFromGoogleServer(
+void HistoryUiFaviconRequestHandlerImpl::RequestFromGoogleServer(
     const GURL& page_url,
     std::unique_ptr<FaviconServerFetcherParams> server_parameters,
     base::OnceClosure empty_response_callback,
@@ -274,8 +276,9 @@
     const GURL& icon_url_for_uma,
     FaviconRequestOrigin origin) {
   net::NetworkTrafficAnnotationTag traffic_annotation =
-      net::DefineNetworkTrafficAnnotation("favicon_request_handler_get_favicon",
-                                          R"(
+      net::DefineNetworkTrafficAnnotation(
+          "history_ui_favicon_request_handler_get_favicon",
+          R"(
       semantics {
         sender: "Favicon Request Handler"
         description:
@@ -320,14 +323,14 @@
           std::move(server_parameters),
           /*may_page_url_be_private=*/true, should_trim_url_path,
           traffic_annotation,
-          base::BindOnce(&FaviconRequestHandler::OnGoogleServerDataAvailable,
-                         weak_ptr_factory_.GetWeakPtr(),
-                         std::move(empty_response_callback),
-                         std::move(local_lookup_callback), origin,
-                         group_to_clear));
+          base::BindOnce(
+              &HistoryUiFaviconRequestHandlerImpl::OnGoogleServerDataAvailable,
+              weak_ptr_factory_.GetWeakPtr(),
+              std::move(empty_response_callback),
+              std::move(local_lookup_callback), origin, group_to_clear));
 }
 
-void FaviconRequestHandler::OnGoogleServerDataAvailable(
+void HistoryUiFaviconRequestHandlerImpl::OnGoogleServerDataAvailable(
     base::OnceClosure empty_response_callback,
     base::OnceClosure local_lookup_callback,
     FaviconRequestOrigin origin,
@@ -358,7 +361,7 @@
   }
 }
 
-bool FaviconRequestHandler::CanQueryGoogleServer(
+bool HistoryUiFaviconRequestHandlerImpl::CanQueryGoogleServer(
     FaviconRequestOrigin origin) const {
   // TODO(victorvianna): Remove origin check once extensions don't talk to this
   // layer anymore.
diff --git a/components/favicon/core/favicon_request_handler.h b/components/favicon/core/history_ui_favicon_request_handler_impl.h
similarity index 69%
rename from components/favicon/core/favicon_request_handler.h
rename to components/favicon/core/history_ui_favicon_request_handler_impl.h
index 9e03d6b..1b302193 100644
--- a/components/favicon/core/favicon_request_handler.h
+++ b/components/favicon/core/history_ui_favicon_request_handler_impl.h
@@ -2,19 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_FAVICON_CORE_FAVICON_REQUEST_HANDLER_H_
-#define COMPONENTS_FAVICON_CORE_FAVICON_REQUEST_HANDLER_H_
+#ifndef COMPONENTS_FAVICON_CORE_HISTORY_UI_FAVICON_REQUEST_HANDLER_IMPL_H_
+#define COMPONENTS_FAVICON_CORE_HISTORY_UI_FAVICON_REQUEST_HANDLER_IMPL_H_
 
 #include <map>
 #include <memory>
 
-#include "base/memory/ref_counted_memory.h"
 #include "base/memory/weak_ptr.h"
-#include "base/task/cancelable_task_tracker.h"
-#include "components/favicon_base/favicon_callback.h"
+#include "components/favicon/core/history_ui_favicon_request_handler.h"
 #include "components/favicon_base/favicon_types.h"
-#include "components/keyed_service/core/keyed_service.h"
-#include "url/gurl.h"
 
 namespace favicon {
 
@@ -22,19 +18,6 @@
 class FaviconService;
 class LargeIconService;
 
-// The UI origin of an icon request.
-// TODO(victorvianna): Rename to agree with the naming style of the other enums.
-enum class FaviconRequestOrigin {
-  // Unknown origin.
-  UNKNOWN,
-  // History page.
-  HISTORY,
-  // History synced tabs page (desktop only).
-  HISTORY_SYNCED_TABS,
-  // Recently closed tabs menu.
-  RECENTLY_CLOSED_TABS,
-};
-
 // Where the icon sent in the response is coming from. Used for metrics.
 enum class FaviconAvailability {
   // Icon recovered from local storage (but may originally come from server).
@@ -46,18 +29,9 @@
   kMaxValue = kNotAvailable,
 };
 
-// Platform making the request.
-enum class FaviconRequestPlatform {
-  kMobile,
-  kDesktop,
-};
-
-// Keyed service for handling favicon requests by page url, forwarding them to
-// local storage, sync or Google server accordingly.
-// TODO(victorvianna): Use a more natural order for the parameters in the API.
-// TODO(victorvianna): Remove |icon_url_for_uma| when we have access to the
-// FaviconUrlMapper.
-class FaviconRequestHandler : public KeyedService {
+// Implementation class for HistoryUiFaviconRequestHandler.
+class HistoryUiFaviconRequestHandlerImpl
+    : public HistoryUiFaviconRequestHandler {
  public:
   // Callback that requests the synced bitmap for a page url.
   using SyncedFaviconGetter =
@@ -69,40 +43,27 @@
   // enabled and no custom passphrase is set).
   using CanSendHistoryDataGetter = base::RepeatingCallback<bool()>;
 
-  FaviconRequestHandler(
+  HistoryUiFaviconRequestHandlerImpl(
       const SyncedFaviconGetter& synced_favicon_getter,
       const CanSendHistoryDataGetter& can_send_history_data_getter,
       FaviconService* favicon_service,
       LargeIconService* large_icon_service);
 
-  ~FaviconRequestHandler() override;
+  ~HistoryUiFaviconRequestHandlerImpl() override;
 
-  // Requests favicon bitmap at |page_url| of size |desired_size_in_pixel|.
-  // Tries to fetch the icon from local storage and falls back to sync, or to
-  // Google favicon server if |favicon::kEnableHistoryFaviconsGoogleServerQuery|
-  // is enabled. If a non-empty |icon_url_for_uma| (optional) is passed, it will
-  // be used to record UMA about the grouping of requests to the favicon server.
-  // |request_platform| specifies whether the caller is mobile or desktop code.
   void GetRawFaviconForPageURL(const GURL& page_url,
                                int desired_size_in_pixel,
                                favicon_base::FaviconRawBitmapCallback callback,
                                FaviconRequestOrigin request_origin,
                                FaviconRequestPlatform request_platform,
                                const GURL& icon_url_for_uma,
-                               base::CancelableTaskTracker* tracker);
+                               base::CancelableTaskTracker* tracker) override;
 
-  // Requests favicon image at |page_url|.
-  // Tries to fetch the icon from local storage and falls back to sync, or to
-  // Google favicon server if |favicon::kEnableHistoryFaviconsGoogleServerQuery|
-  // is enabled.
-  // If a non-empty |icon_url_for_uma| (optional) is passed, it will be used to
-  // record UMA about the grouping of requests to the favicon server.
-  // This method is only called by desktop code.
   void GetFaviconImageForPageURL(const GURL& page_url,
                                  favicon_base::FaviconImageCallback callback,
                                  FaviconRequestOrigin request_origin,
                                  const GURL& icon_url_for_uma,
-                                 base::CancelableTaskTracker* tracker);
+                                 base::CancelableTaskTracker* tracker) override;
 
  private:
   // Called after the first attempt to retrieve the icon bitmap from local
@@ -173,11 +134,12 @@
   // benefit of grouping.
   std::map<GURL, int> group_callbacks_count_;
 
-  base::WeakPtrFactory<FaviconRequestHandler> weak_ptr_factory_{this};
+  base::WeakPtrFactory<HistoryUiFaviconRequestHandlerImpl> weak_ptr_factory_{
+      this};
 
-  DISALLOW_COPY_AND_ASSIGN(FaviconRequestHandler);
+  DISALLOW_COPY_AND_ASSIGN(HistoryUiFaviconRequestHandlerImpl);
 };
 
 }  // namespace favicon
 
-#endif  // COMPONENTS_FAVICON_CORE_FAVICON_REQUEST_HANDLER_H_
+#endif  // COMPONENTS_FAVICON_CORE_HISTORY_UI_FAVICON_REQUEST_HANDLER_IMPL_H_
diff --git a/components/favicon/core/favicon_request_handler_unittest.cc b/components/favicon/core/history_ui_favicon_request_handler_impl_unittest.cc
similarity index 88%
rename from components/favicon/core/favicon_request_handler_unittest.cc
rename to components/favicon/core/history_ui_favicon_request_handler_impl_unittest.cc
index 17b4dfc..55cf72f 100644
--- a/components/favicon/core/favicon_request_handler_unittest.cc
+++ b/components/favicon/core/history_ui_favicon_request_handler_impl_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/favicon/core/favicon_request_handler.h"
+#include "components/favicon/core/history_ui_favicon_request_handler_impl.h"
 
 #include <utility>
 
@@ -120,32 +120,32 @@
   MOCK_METHOD1(TouchIconFromGoogleServer, void(const GURL& icon_url));
 };
 
-class FaviconRequestHandlerTest : public ::testing::Test {
+class HistoryUiFaviconRequestHandlerImplTest : public ::testing::Test {
  public:
-  FaviconRequestHandlerTest()
-      : favicon_request_handler_(synced_favicon_getter_.Get(),
-                                 can_send_history_data_getter_.Get(),
-                                 &mock_favicon_service_,
-                                 &mock_large_icon_service_) {
+  HistoryUiFaviconRequestHandlerImplTest()
+      : history_ui_favicon_request_handler_(synced_favicon_getter_.Get(),
+                                            can_send_history_data_getter_.Get(),
+                                            &mock_favicon_service_,
+                                            &mock_large_icon_service_) {
     ON_CALL(can_send_history_data_getter_, Run()).WillByDefault(Return(true));
   }
 
  protected:
   testing::NiceMock<MockFaviconService> mock_favicon_service_;
   testing::NiceMock<MockLargeIconService> mock_large_icon_service_;
-  testing::NiceMock<
-      base::MockCallback<FaviconRequestHandler::SyncedFaviconGetter>>
+  testing::NiceMock<base::MockCallback<
+      HistoryUiFaviconRequestHandlerImpl::SyncedFaviconGetter>>
       synced_favicon_getter_;
-  testing::NiceMock<
-      base::MockCallback<FaviconRequestHandler::CanSendHistoryDataGetter>>
+  testing::NiceMock<base::MockCallback<
+      HistoryUiFaviconRequestHandlerImpl::CanSendHistoryDataGetter>>
       can_send_history_data_getter_;
   base::CancelableTaskTracker tracker_;
   base::HistogramTester histogram_tester_;
   base::test::ScopedFeatureList scoped_feature_list_;
-  FaviconRequestHandler favicon_request_handler_;
+  HistoryUiFaviconRequestHandlerImpl history_ui_favicon_request_handler_;
 };
 
-TEST_F(FaviconRequestHandlerTest, ShouldGetEmptyBitmap) {
+TEST_F(HistoryUiFaviconRequestHandlerImplTest, ShouldGetEmptyBitmap) {
   scoped_feature_list_.InitAndDisableFeature(
       kEnableHistoryFaviconsGoogleServerQuery);
   EXPECT_CALL(
@@ -160,7 +160,7 @@
   EXPECT_CALL(synced_favicon_getter_, Run(GURL(kDummyPageUrl)))
       .WillOnce([](auto) { return favicon_base::FaviconRawBitmapResult(); });
   favicon_base::FaviconRawBitmapResult result;
-  favicon_request_handler_.GetRawFaviconForPageURL(
+  history_ui_favicon_request_handler_.GetRawFaviconForPageURL(
       GURL(kDummyPageUrl), kDefaultDesiredSizeInPixel,
       base::BindOnce(&StoreBitmap, &result), FaviconRequestOrigin::UNKNOWN,
       kDummyPlatform,
@@ -170,7 +170,7 @@
                                        FaviconAvailability::kNotAvailable, 1);
 }
 
-TEST_F(FaviconRequestHandlerTest, ShouldGetSyncBitmap) {
+TEST_F(HistoryUiFaviconRequestHandlerImplTest, ShouldGetSyncBitmap) {
   scoped_feature_list_.InitAndDisableFeature(
       kEnableHistoryFaviconsGoogleServerQuery);
   EXPECT_CALL(
@@ -185,7 +185,7 @@
   EXPECT_CALL(synced_favicon_getter_, Run(GURL(kDummyPageUrl)))
       .WillOnce([](auto) { return CreateTestBitmapResult(); });
   favicon_base::FaviconRawBitmapResult result;
-  favicon_request_handler_.GetRawFaviconForPageURL(
+  history_ui_favicon_request_handler_.GetRawFaviconForPageURL(
       GURL(kDummyPageUrl), kDefaultDesiredSizeInPixel,
       base::BindOnce(&StoreBitmap, &result), FaviconRequestOrigin::UNKNOWN,
       kDummyPlatform,
@@ -195,7 +195,7 @@
                                        FaviconAvailability::kSync, 1);
 }
 
-TEST_F(FaviconRequestHandlerTest, ShouldGetLocalBitmap) {
+TEST_F(HistoryUiFaviconRequestHandlerImplTest, ShouldGetLocalBitmap) {
   scoped_feature_list_.InitAndDisableFeature(
       kEnableHistoryFaviconsGoogleServerQuery);
   EXPECT_CALL(
@@ -211,7 +211,7 @@
               TouchIconFromGoogleServer(GURL(kDummyIconUrl)));
   EXPECT_CALL(synced_favicon_getter_, Run(_)).Times(0);
   favicon_base::FaviconRawBitmapResult result;
-  favicon_request_handler_.GetRawFaviconForPageURL(
+  history_ui_favicon_request_handler_.GetRawFaviconForPageURL(
       GURL(kDummyPageUrl), kDefaultDesiredSizeInPixel,
       base::BindOnce(&StoreBitmap, &result), FaviconRequestOrigin::UNKNOWN,
       kDummyPlatform,
@@ -221,7 +221,8 @@
                                        FaviconAvailability::kLocal, 1);
 }
 
-TEST_F(FaviconRequestHandlerTest, ShouldGetGoogleServerBitmapForFullUrl) {
+TEST_F(HistoryUiFaviconRequestHandlerImplTest,
+       ShouldGetGoogleServerBitmapForFullUrl) {
   scoped_feature_list_.InitAndEnableFeatureWithParameters(
       kEnableHistoryFaviconsGoogleServerQuery, {{"trim_url_path", "false"}});
   EXPECT_CALL(can_send_history_data_getter_, Run());
@@ -249,7 +250,7 @@
       });
   EXPECT_CALL(synced_favicon_getter_, Run(_)).Times(0);
   favicon_base::FaviconRawBitmapResult result;
-  favicon_request_handler_.GetRawFaviconForPageURL(
+  history_ui_favicon_request_handler_.GetRawFaviconForPageURL(
       GURL(kDummyPageUrl), kDefaultDesiredSizeInPixel,
       base::BindOnce(&StoreBitmap, &result), FaviconRequestOrigin::HISTORY,
       kDummyPlatform,
@@ -257,7 +258,8 @@
   EXPECT_TRUE(result.is_valid());
 }
 
-TEST_F(FaviconRequestHandlerTest, ShouldGetGoogleServerBitmapForTrimmedUrl) {
+TEST_F(HistoryUiFaviconRequestHandlerImplTest,
+       ShouldGetGoogleServerBitmapForTrimmedUrl) {
   scoped_feature_list_.InitAndEnableFeatureWithParameters(
       kEnableHistoryFaviconsGoogleServerQuery, {{"trim_url_path", "true"}});
   EXPECT_CALL(can_send_history_data_getter_, Run());
@@ -285,7 +287,7 @@
       });
   EXPECT_CALL(synced_favicon_getter_, Run(_)).Times(0);
   favicon_base::FaviconRawBitmapResult result;
-  favicon_request_handler_.GetRawFaviconForPageURL(
+  history_ui_favicon_request_handler_.GetRawFaviconForPageURL(
       GURL(kDummyPageUrl), kDefaultDesiredSizeInPixel,
       base::BindOnce(&StoreBitmap, &result), FaviconRequestOrigin::HISTORY,
       kDummyPlatform,
@@ -297,7 +299,7 @@
       "Sync.SizeOfFaviconServerRequestGroup.HISTORY", 1, 1);
 }
 
-TEST_F(FaviconRequestHandlerTest, ShouldGetEmptyImage) {
+TEST_F(HistoryUiFaviconRequestHandlerImplTest, ShouldGetEmptyImage) {
   scoped_feature_list_.InitAndDisableFeature(
       kEnableHistoryFaviconsGoogleServerQuery);
   EXPECT_CALL(mock_favicon_service_,
@@ -309,7 +311,7 @@
   EXPECT_CALL(synced_favicon_getter_, Run(GURL(kDummyPageUrl)))
       .WillOnce([](auto) { return favicon_base::FaviconRawBitmapResult(); });
   favicon_base::FaviconImageResult result;
-  favicon_request_handler_.GetFaviconImageForPageURL(
+  history_ui_favicon_request_handler_.GetFaviconImageForPageURL(
       GURL(kDummyPageUrl), base::BindOnce(&StoreImage, &result),
       FaviconRequestOrigin::UNKNOWN,
       /*icon_url_for_uma=*/GURL(), &tracker_);
@@ -318,7 +320,7 @@
                                        FaviconAvailability::kNotAvailable, 1);
 }
 
-TEST_F(FaviconRequestHandlerTest, ShouldGetSyncImage) {
+TEST_F(HistoryUiFaviconRequestHandlerImplTest, ShouldGetSyncImage) {
   scoped_feature_list_.InitAndDisableFeature(
       kEnableHistoryFaviconsGoogleServerQuery);
   EXPECT_CALL(mock_favicon_service_,
@@ -330,7 +332,7 @@
   EXPECT_CALL(synced_favicon_getter_, Run(GURL(kDummyPageUrl)))
       .WillOnce([](auto) { return CreateTestBitmapResult(); });
   favicon_base::FaviconImageResult result;
-  favicon_request_handler_.GetFaviconImageForPageURL(
+  history_ui_favicon_request_handler_.GetFaviconImageForPageURL(
       GURL(kDummyPageUrl), base::BindOnce(&StoreImage, &result),
       FaviconRequestOrigin::UNKNOWN,
       /*icon_url_for_uma=*/GURL(), &tracker_);
@@ -339,7 +341,7 @@
                                        FaviconAvailability::kSync, 1);
 }
 
-TEST_F(FaviconRequestHandlerTest, ShouldGetLocalImage) {
+TEST_F(HistoryUiFaviconRequestHandlerImplTest, ShouldGetLocalImage) {
   scoped_feature_list_.InitAndDisableFeature(
       kEnableHistoryFaviconsGoogleServerQuery);
   EXPECT_CALL(mock_favicon_service_,
@@ -352,7 +354,7 @@
               TouchIconFromGoogleServer(GURL(kDummyIconUrl)));
   EXPECT_CALL(synced_favicon_getter_, Run(_)).Times(0);
   favicon_base::FaviconImageResult result;
-  favicon_request_handler_.GetFaviconImageForPageURL(
+  history_ui_favicon_request_handler_.GetFaviconImageForPageURL(
       GURL(kDummyPageUrl), base::BindOnce(&StoreImage, &result),
       FaviconRequestOrigin::UNKNOWN,
       /*icon_url_for_uma=*/GURL(), &tracker_);
@@ -361,7 +363,8 @@
                                        FaviconAvailability::kLocal, 1);
 }
 
-TEST_F(FaviconRequestHandlerTest, ShouldGetGoogleServerImageForFullUrl) {
+TEST_F(HistoryUiFaviconRequestHandlerImplTest,
+       ShouldGetGoogleServerImageForFullUrl) {
   scoped_feature_list_.InitAndEnableFeatureWithParameters(
       kEnableHistoryFaviconsGoogleServerQuery, {{"trim_url_path", "false"}});
   EXPECT_CALL(can_send_history_data_getter_, Run());
@@ -385,7 +388,7 @@
       });
   EXPECT_CALL(synced_favicon_getter_, Run(_)).Times(0);
   favicon_base::FaviconImageResult result;
-  favicon_request_handler_.GetFaviconImageForPageURL(
+  history_ui_favicon_request_handler_.GetFaviconImageForPageURL(
       GURL(kDummyPageUrl), base::BindOnce(&StoreImage, &result),
       FaviconRequestOrigin::RECENTLY_CLOSED_TABS, /*icon_url_for_uma=*/GURL(),
       &tracker_);
@@ -397,7 +400,8 @@
       "Sync.SizeOfFaviconServerRequestGroup.RECENTLY_CLOSED_TABS", 1, 1);
 }
 
-TEST_F(FaviconRequestHandlerTest, ShouldGetGoogleServerImageForTrimmedUrl) {
+TEST_F(HistoryUiFaviconRequestHandlerImplTest,
+       ShouldGetGoogleServerImageForTrimmedUrl) {
   scoped_feature_list_.InitAndEnableFeatureWithParameters(
       kEnableHistoryFaviconsGoogleServerQuery, {{"trim_url_path", "true"}});
   EXPECT_CALL(can_send_history_data_getter_, Run());
@@ -421,7 +425,7 @@
       });
   EXPECT_CALL(synced_favicon_getter_, Run(_)).Times(0);
   favicon_base::FaviconImageResult result;
-  favicon_request_handler_.GetFaviconImageForPageURL(
+  history_ui_favicon_request_handler_.GetFaviconImageForPageURL(
       GURL(kDummyPageUrl), base::BindOnce(&StoreImage, &result),
       FaviconRequestOrigin::RECENTLY_CLOSED_TABS, /*icon_url_for_uma=*/GURL(),
 
@@ -429,7 +433,8 @@
   EXPECT_FALSE(result.image.IsEmpty());
 }
 
-TEST_F(FaviconRequestHandlerTest, ShouldNotQueryGoogleServerIfCannotSendData) {
+TEST_F(HistoryUiFaviconRequestHandlerImplTest,
+       ShouldNotQueryGoogleServerIfCannotSendData) {
   scoped_feature_list_.InitAndEnableFeature(
       kEnableHistoryFaviconsGoogleServerQuery);
   EXPECT_CALL(can_send_history_data_getter_, Run()).WillOnce([]() {
@@ -449,14 +454,14 @@
                   _, _, _, _, _))
       .Times(0);
   favicon_base::FaviconRawBitmapResult result;
-  favicon_request_handler_.GetRawFaviconForPageURL(
+  history_ui_favicon_request_handler_.GetRawFaviconForPageURL(
       GURL(kDummyPageUrl), kDefaultDesiredSizeInPixel,
       base::BindOnce(&StoreBitmap, &result), FaviconRequestOrigin::HISTORY,
       kDummyPlatform,
       /*icon_url_for_uma=*/GURL(), &tracker_);
 }
 
-TEST_F(FaviconRequestHandlerTest, ShouldResizeSyncBitmap) {
+TEST_F(HistoryUiFaviconRequestHandlerImplTest, ShouldResizeSyncBitmap) {
   const int kDesiredSizeInPixel = 32;
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list_.InitAndDisableFeature(
@@ -473,7 +478,7 @@
   EXPECT_CALL(synced_favicon_getter_, Run(GURL(kDummyPageUrl)))
       .WillOnce([](auto) { return CreateTestBitmapResult(16); });
   favicon_base::FaviconRawBitmapResult result;
-  favicon_request_handler_.GetRawFaviconForPageURL(
+  history_ui_favicon_request_handler_.GetRawFaviconForPageURL(
       GURL(kDummyPageUrl), kDesiredSizeInPixel,
       base::BindOnce(&StoreBitmap, &result), FaviconRequestOrigin::UNKNOWN,
       kDummyPlatform,
diff --git a/components/feedback/anonymizer_tool_unittest.cc b/components/feedback/anonymizer_tool_unittest.cc
index 4bb2faa..4f490481 100644
--- a/components/feedback/anonymizer_tool_unittest.cc
+++ b/components/feedback/anonymizer_tool_unittest.cc
@@ -209,145 +209,153 @@
 }
 
 TEST_F(AnonymizerToolTest, AnonymizeChunk) {
-  std::string data =
-      "aaaaaaaa [SSID=123aaaaaa]aaaaa\n"  // SSID.
-      "aaaaaaaahttp://tets.comaaaaaaa\n"  // URL.
-      "aaaaaemail@example.comaaa\n"       // Email address.
-      "example@@1234\n"           // No PII, it is not valid email address.
-      "255.255.155.2\n"           // IP address.
-      "255.255.155.255\n"         // IP address.
-      "127.0.0.1\n"               // IPv4 loopback.
-      "127.255.0.1\n"             // IPv4 loopback.
-      "0.0.0.0\n"                 // Any IPv4.
-      "0.255.255.255\n"           // Any IPv4.
-      "10.10.10.100\n"            // IPv4 private class A.
-      "10.10.10.100\n"            // Intentional duplicate.
-      "10.10.10.101\n"            // IPv4 private class A.
-      "10.255.255.255\n"          // IPv4 private class A.
-      "172.16.0.0\n"              // IPv4 private class B.
-      "172.31.255.255\n"          // IPv4 private class B.
-      "172.11.5.5\n"              // IP address.
-      "172.111.5.5\n"             // IP address.
-      "192.168.0.0\n"             // IPv4 private class C.
-      "192.168.255.255\n"         // IPv4 private class C.
-      "192.169.2.120\n"           // IP address.
-      "169.254.0.1\n"             // Link local.
-      "169.200.0.1\n"             // IP address.
-      "fe80::\n"                  // Link local.
-      "fe80::ffff\n"              // Link local.
-      "febf:ffff::ffff\n"         // Link local.
-      "fecc::1111\n"              // IP address.
-      "224.0.0.24\n"              // Multicast.
-      "240.0.0.0\n"               // IP address.
-      "255.255.255.255\n"         // Broadcast.
-      "100.115.92.92\n"           // ChromeOS.
-      "100.115.91.92\n"           // IP address.
-      "1.1.1.1\n"                 // DNS
-      "8.8.8.8\n"                 // DNS
-      "8.8.4.4\n"                 // DNS
-      "8.8.8.4\n"                 // IP address.
-      "255.255.259.255\n"         // Not an IP address.
-      "255.300.255.255\n"         // Not an IP address.
-      "aaaa123.123.45.4aaa\n"     // IP address.
-      "11:11;11::11\n"            // IP address.
-      "11::11\n"                  // IP address.
-      "11:11:abcdef:0:0:0:0:0\n"  // No PII.
-      "::\n"                      // Unspecified.
-      "::1\n"                     // Local host.
-      "Instance::Set\n"           // Ignore match, no PII.
-      "Instant::ff\n"             // Ignore match, no PII.
-      "net::ERR_CONN_TIMEOUT\n"   // Ignore match, no PII.
-      "ff01::1\n"                 // All nodes address (interface local).
-      "ff01::2\n"                 // All routers (interface local).
-      "ff01::3\n"                 // Multicast (interface local).
-      "ff02::1\n"                 // All nodes address (link local).
-      "ff02::2\n"                 // All routers (link local).
-      "ff02::3\n"                 // Multicast (link local).
-      "ff02::fb\n"                // mDNSv6 (link local).
-      "ff08::fb\n"                // mDNSv6.
-      "ff0f::101\n"               // All NTP servers.
-      "::ffff:cb0c:10ea\n"        // IPv4-mapped IPV6 (IP address).
-      "::ffff:a0a:a0a\n"          // IPv4-mapped IPV6 (private class A).
-      "::ffff:a0a:a0a\n"          // Intentional duplicate.
-      "::ffff:ac1e:1e1e\n"        // IPv4-mapped IPV6 (private class B).
-      "::ffff:c0a8:640a\n"        // IPv4-mapped IPV6 (private class C).
-      "::ffff:6473:5c01\n"        // IPv4-mapped IPV6 (Chrome).
-      "64:ff9b::a0a:a0a\n"       // IPv4-translated 6to4 IPV6 (private class A).
-      "64:ff9b::6473:5c01\n"     // IPv4-translated 6to4 IPV6 (Chrome).
-      "::0101:ffff:c0a8:640a\n"  // IP address.
-      "aa:aa:aa:aa:aa:aa\n"      // MAC address (BSSID).
-      "chrome://resources/foo\n"        // Secure chrome resource, whitelisted.
-      "chrome://resources/f?user=bar";  // Potentially PII in parameter.
-  std::string result =
-      "aaaaaaaa [SSID=1]aaaaa\n"
-      "aaaaaaaa<URL: 1>\n"
-      "<email: 1>\n"
-      "example@@1234\n"
-      "<IPv4: 1>\n"
-      "<IPv4: 2>\n"
-      "<127.0.0.0/8: 3>\n"
-      "<127.0.0.0/8: 4>\n"
-      "<0.0.0.0/8: 5>\n"
-      "<0.0.0.0/8: 6>\n"
-      "<10.0.0.0/8: 7>\n"
-      "<10.0.0.0/8: 7>\n"
-      "<10.0.0.0/8: 8>\n"
-      "<10.0.0.0/8: 9>\n"
-      "<172.16.0.0/12: 10>\n"
-      "<172.16.0.0/12: 11>\n"
-      "<IPv4: 12>\n"
-      "<IPv4: 13>\n"
-      "<192.168.0.0/16: 14>\n"
-      "<192.168.0.0/16: 15>\n"
-      "<IPv4: 16>\n"
-      "<169.254.0.0/16: 17>\n"
-      "<IPv4: 18>\n"
-      "<fe80::/10: 1>\n"
-      "<fe80::/10: 2>\n"
-      "<fe80::/10: 3>\n"
-      "<IPv6: 4>\n"
-      "<224.0.0.0/4: 19>\n"
-      "<IPv4: 20>\n"
-      "255.255.255.255\n"
-      "100.115.92.92\n"
-      "<IPv4: 23>\n"
-      "1.1.1.1\n"
-      "8.8.8.8\n"
-      "8.8.4.4\n"
-      "<IPv4: 27>\n"
-      "255.255.259.255\n"
-      "255.300.255.255\n"
-      "aaaa<IPv4: 28>aaa\n"
-      "11:11;<IPv6: 5>\n"
-      "<IPv6: 5>\n"
-      "11:11:abcdef:0:0:0:0:0\n"
-      "::\n"
-      "::1\n"
-      "Instance::Set\n"
-      "Instant::ff\n"
-      "net::ERR_CONN_TIMEOUT\n"
-      "ff01::1\n"
-      "ff01::2\n"
-      "<ff01::/16: 13>\n"
-      "ff02::1\n"
-      "ff02::2\n"
-      "<ff02::/16: 16>\n"
-      "<ff02::/16: 17>\n"
-      "<IPv6: 18>\n"
-      "<IPv6: 19>\n"
-      "<IPv6: 20>\n"
-      "<M 10.0.0.0/8: 21>\n"
-      "<M 10.0.0.0/8: 21>\n"
-      "<M 172.16.0.0/12: 22>\n"
-      "<M 192.168.0.0/16: 23>\n"
-      "<M 100.115.92.1: 24>\n"
-      "<T 10.0.0.0/8: 25>\n"
-      "<T 100.115.92.1: 26>\n"
-      "<IPv6: 27>\n"
-      "aa:aa:aa:00:00:01\n"
-      "chrome://resources/foo\n"
-      "<URL: 2>";
-  EXPECT_EQ(result, anonymizer_.Anonymize(data));
+  // For better readability, put all the pre/post redaction strings in an array
+  // of pairs, and then convert that to two strings which become the input and
+  // output of the anonymizer.
+  std::pair<std::string, std::string> data[] = {
+      {"aaaaaaaa [SSID=123aaaaaa]aaaaa",  // SSID.
+       "aaaaaaaa [SSID=1]aaaaa"},
+      {"aaaaaaaahttp://tets.comaaaaaaa",  // URL.
+       "aaaaaaaa<URL: 1>"},
+      {"aaaaaemail@example.comaaa",  // Email address.
+       "<email: 1>"},
+      {"example@@1234",  // No PII, it is not invalid email address.
+       "example@@1234"},
+      {"255.255.155.2",  // IP address.
+       "<IPv4: 1>"},
+      {"255.255.155.255",  // IP address.
+       "<IPv4: 2>"},
+      {"127.0.0.1",  // IPv4 loopback.
+       "<127.0.0.0/8: 3>"},
+      {"127.255.0.1",  // IPv4 loopback.
+       "<127.0.0.0/8: 4>"},
+      {"0.0.0.0",  // Any IPv4.
+       "<0.0.0.0/8: 5>"},
+      {"0.255.255.255",  // Any IPv4.
+       "<0.0.0.0/8: 6>"},
+      {"10.10.10.100",  // IPv4 private class A.
+       "<10.0.0.0/8: 7>"},
+      {"10.10.10.100",  // Intentional duplicate.
+       "<10.0.0.0/8: 7>"},
+      {"10.10.10.101",  // IPv4 private class A.
+       "<10.0.0.0/8: 8>"},
+      {"10.255.255.255",  // IPv4 private class A.
+       "<10.0.0.0/8: 9>"},
+      {"172.16.0.0",  // IPv4 private class B.
+       "<172.16.0.0/12: 10>"},
+      {"172.31.255.255",  // IPv4 private class B.
+       "<172.16.0.0/12: 11>"},
+      {"172.11.5.5",  // IP address.
+       "<IPv4: 12>"},
+      {"172.111.5.5",  // IP address.
+       "<IPv4: 13>"},
+      {"192.168.0.0",  // IPv4 private class C.
+       "<192.168.0.0/16: 14>"},
+      {"192.168.255.255",  // IPv4 private class C.
+       "<192.168.0.0/16: 15>"},
+      {"192.169.2.120",  // IP address.
+       "<IPv4: 16>"},
+      {"169.254.0.1",  // Link local.
+       "<169.254.0.0/16: 17>"},
+      {"169.200.0.1",  // IP address.
+       "<IPv4: 18>"},
+      {"fe80::",  // Link local.
+       "<fe80::/10: 1>"},
+      {"fe80::ffff",  // Link local.
+       "<fe80::/10: 2>"},
+      {"febf:ffff::ffff",  // Link local.
+       "<fe80::/10: 3>"},
+      {"fecc::1111",  // IP address.
+       "<IPv6: 4>"},
+      {"224.0.0.24",  // Multicast.
+       "<224.0.0.0/4: 19>"},
+      {"240.0.0.0",  // IP address.
+       "<IPv4: 20>"},
+      {"255.255.255.255",  // Broadcast.
+       "255.255.255.255"},
+      {"100.115.92.92",  // ChromeOS.
+       "100.115.92.92"},
+      {"100.115.91.92",  // IP address.
+       "<IPv4: 23>"},
+      {"1.1.1.1",  // DNS
+       "1.1.1.1"},
+      {"8.8.8.8",  // DNS
+       "8.8.8.8"},
+      {"8.8.4.4",  // DNS
+       "8.8.4.4"},
+      {"8.8.8.4",  // IP address.
+       "<IPv4: 27>"},
+      {"255.255.259.255",  // Not an IP address.
+       "255.255.259.255"},
+      {"255.300.255.255",  // Not an IP address.
+       "255.300.255.255"},
+      {"aaaa123.123.45.4aaa",  // IP address.
+       "aaaa<IPv4: 28>aaa"},
+      {"11:11;11::11",  // IP address.
+       "11:11;<IPv6: 5>"},
+      {"11::11",  // IP address.
+       "<IPv6: 5>"},
+      {"11:11:abcdef:0:0:0:0:0",  // No PII.
+       "11:11:abcdef:0:0:0:0:0"},
+      {"::",  // Unspecified.
+       "::"},
+      {"::1",  // Local host.
+       "::1"},
+      {"Instance::Set",  // Ignore match, no PII.
+       "Instance::Set"},
+      {"Instant::ff",  // Ignore match, no PII.
+       "Instant::ff"},
+      {"net::ERR_CONN_TIMEOUT",  // Ignore match, no PII.
+       "net::ERR_CONN_TIMEOUT"},
+      {"ff01::1",  // All nodes address (interface local).
+       "ff01::1"},
+      {"ff01::2",  // All routers (interface local).
+       "ff01::2"},
+      {"ff01::3",  // Multicast (interface local).
+       "<ff01::/16: 13>"},
+      {"ff02::1",  // All nodes address (link local).
+       "ff02::1"},
+      {"ff02::2",  // All routers (link local).
+       "ff02::2"},
+      {"ff02::3",  // Multicast (link local).
+       "<ff02::/16: 16>"},
+      {"ff02::fb",  // mDNSv6 (link local).
+       "<ff02::/16: 17>"},
+      {"ff08::fb",  // mDNSv6.
+       "<IPv6: 18>"},
+      {"ff0f::101",  // All NTP servers.
+       "<IPv6: 19>"},
+      {"::ffff:cb0c:10ea",  // IPv4-mapped IPV6 (IP address).
+       "<IPv6: 20>"},
+      {"::ffff:a0a:a0a",  // IPv4-mapped IPV6 (private class A).
+       "<M 10.0.0.0/8: 21>"},
+      {"::ffff:a0a:a0a",  // Intentional duplicate.
+       "<M 10.0.0.0/8: 21>"},
+      {"::ffff:ac1e:1e1e",  // IPv4-mapped IPV6 (private class B).
+       "<M 172.16.0.0/12: 22>"},
+      {"::ffff:c0a8:640a",  // IPv4-mapped IPV6 (private class C).
+       "<M 192.168.0.0/16: 23>"},
+      {"::ffff:6473:5c01",  // IPv4-mapped IPV6 (Chrome).
+       "<M 100.115.92.1: 24>"},
+      {"64:ff9b::a0a:a0a",  // IPv4-translated 6to4 IPV6 (private class A).
+       "<T 10.0.0.0/8: 25>"},
+      {"64:ff9b::6473:5c01",  // IPv4-translated 6to4 IPV6 (Chrome).
+       "<T 100.115.92.1: 26>"},
+      {"::0101:ffff:c0a8:640a",  // IP address.
+       "<IPv6: 27>"},
+      {"aa:aa:aa:aa:aa:aa",  // MAC address (BSSID).
+       "aa:aa:aa:00:00:01"},
+      {"chrome://resources/foo",  // Secure chrome resource, whitelisted.
+       "chrome://resources/foo"},
+      {"chrome://resources/f?user=bar",  // Potentially PII in parameter.
+       "<URL: 2>"}};
+  std::string anon_input;
+  std::string anon_output;
+  for (const auto& s : data) {
+    anon_input.append(s.first).append("\n");
+    anon_output.append(s.second).append("\n");
+  }
+  EXPECT_EQ(anon_output, anonymizer_.Anonymize(anon_input));
 }
 
 }  // namespace feedback
diff --git a/components/gwp_asan/BUILD.gn b/components/gwp_asan/BUILD.gn
index 5176c8c..3359d70e 100644
--- a/components/gwp_asan/BUILD.gn
+++ b/components/gwp_asan/BUILD.gn
@@ -7,7 +7,7 @@
   deps = [
     "//components/gwp_asan/common:unit_tests",
   ]
-  if (is_win || is_mac) {
+  if (is_win || is_mac || is_linux) {
     deps += [
       "//components/gwp_asan/client:unit_tests",
       "//components/gwp_asan/crash_handler:unit_tests",
diff --git a/components/gwp_asan/client/sampling_malloc_shims_unittest.cc b/components/gwp_asan/client/sampling_malloc_shims_unittest.cc
index a42f7a4..d7b6660 100644
--- a/components/gwp_asan/client/sampling_malloc_shims_unittest.cc
+++ b/components/gwp_asan/client/sampling_malloc_shims_unittest.cc
@@ -41,8 +41,6 @@
 static size_t GetAllocatedSize(void* mem) {
   return malloc_usable_size(mem);
 }
-#else
-#error "Needs to be implemented for platform."
 #endif
 
 namespace gwp_asan {
diff --git a/components/image_fetcher/core/cache/image_cache.cc b/components/image_fetcher/core/cache/image_cache.cc
index 6a60788..52103c0 100644
--- a/components/image_fetcher/core/cache/image_cache.cc
+++ b/components/image_fetcher/core/cache/image_cache.cc
@@ -71,7 +71,9 @@
 
 ImageCache::~ImageCache() = default;
 
-void ImageCache::SaveImage(std::string url, std::string image_data) {
+void ImageCache::SaveImage(std::string url,
+                           std::string image_data,
+                           bool needs_transcoding) {
   // If the image data is larger than the cache's max size, bail out.
   if (image_data.length() > kCacheMaxSize) {
     return;
@@ -79,7 +81,7 @@
 
   base::OnceClosure request =
       base::BindOnce(&ImageCache::SaveImageImpl, weak_ptr_factory_.GetWeakPtr(),
-                     url, std::move(image_data));
+                     url, std::move(image_data), needs_transcoding);
   QueueOrStartRequest(std::move(request));
 }
 
@@ -149,23 +151,45 @@
                      weak_ptr_factory_.GetWeakPtr()));
 }
 
-void ImageCache::SaveImageImpl(const std::string& url, std::string image_data) {
+void ImageCache::SaveImageImpl(const std::string& url,
+                               std::string image_data,
+                               bool needs_transcoding) {
   std::string key = ImageCache::HashUrlToKey(url);
 
   // If the cache is full, evict some stuff.
   RunEvictionWhenFull();
 
   size_t length = image_data.length();
-  data_store_->SaveImage(key, std::move(image_data));
-  metadata_store_->SaveImageMetadata(key, length);
+  data_store_->SaveImage(key, std::move(image_data), needs_transcoding);
+  metadata_store_->SaveImageMetadata(key, length, needs_transcoding);
 }
 
 void ImageCache::LoadImageImpl(bool read_only,
                                const std::string& url,
                                ImageDataCallback callback) {
   std::string key = ImageCache::HashUrlToKey(url);
+  metadata_store_->LoadImageMetadata(
+      key, base::BindOnce(&ImageCache::OnImageMetadataLoadedForLoadImage,
+                          weak_ptr_factory_.GetWeakPtr(), read_only, key,
+                          std::move(callback), base::TimeTicks::Now()));
+}
 
-  data_store_->LoadImage(key, std::move(callback));
+void ImageCache::OnImageMetadataLoadedForLoadImage(
+    bool read_only,
+    const std::string& key,
+    ImageDataCallback callback,
+    base::TimeTicks start_time,
+    base::Optional<CachedImageMetadataProto> metadata) {
+  // Record time spent to load metadata.
+  ImageFetcherMetricsReporter::ReportLoadImageMetadata(start_time);
+
+  if (!metadata.has_value()) {
+    std::move(callback).Run(/* needs_transcoding */ false, "");
+    return;
+  }
+
+  data_store_->LoadImage(key, metadata->needs_transcoding(),
+                         std::move(callback));
   if (!read_only) {
     metadata_store_->UpdateImageMetadata(key);
   }
diff --git a/components/image_fetcher/core/cache/image_cache.h b/components/image_fetcher/core/cache/image_cache.h
index f6e4017..2f06ad6 100644
--- a/components/image_fetcher/core/cache/image_cache.h
+++ b/components/image_fetcher/core/cache/image_cache.h
@@ -11,7 +11,9 @@
 
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
+#include "base/optional.h"
 #include "components/image_fetcher/core/cache/image_store_types.h"
+#include "components/image_fetcher/core/cache/proto/cached_image_metadata.pb.h"
 
 class PrefRegistrySimple;
 class PrefService;
@@ -42,7 +44,9 @@
 
   // Adds or updates the image data for the |url|. If the class hasn't been
   // initialized yet, the call is queued.
-  void SaveImage(std::string url, std::string image_data);
+  void SaveImage(std::string url,
+                 std::string image_data,
+                 bool needs_transcoding);
 
   // Loads the image data for the |url| and passes it to |callback|. If there's
   // no image in the cache, then an empty string is returned. If |read_only|
@@ -67,11 +71,21 @@
   void OnDependencyInitialized();
 
   // Saves the |image_data| for |url|.
-  void SaveImageImpl(const std::string& url, std::string image_data);
+  void SaveImageImpl(const std::string& url,
+                     std::string image_data,
+                     bool needs_transcoding);
   // Loads the data for |url|, calls the user back before updating metadata.
   void LoadImageImpl(bool read_only,
                      const std::string& url,
                      ImageDataCallback callback);
+  // Loads the image metadata for the given |url|, used to inspect if there's
+  // data available on disk that's in need of transcoding.
+  void OnImageMetadataLoadedForLoadImage(
+      bool read_only,
+      const std::string& key,
+      ImageDataCallback callback,
+      base::TimeTicks start_time,
+      base::Optional<CachedImageMetadataProto> metadata);
   // Deletes the data for |url|.
   void DeleteImageImpl(const std::string& url);
 
diff --git a/components/image_fetcher/core/cache/image_cache_unittest.cc b/components/image_fetcher/core/cache/image_cache_unittest.cc
index b4349d4..19347e2 100644
--- a/components/image_fetcher/core/cache/image_cache_unittest.cc
+++ b/components/image_fetcher/core/cache/image_cache_unittest.cc
@@ -72,11 +72,11 @@
     ASSERT_TRUE(metadata_store()->IsInitialized());
   }
 
-  void PrepareImageCache() {
+  void PrepareImageCache(bool needs_transcoding) {
     CreateImageCache();
     InitializeImageCache();
 
-    image_cache()->SaveImage(kImageUrl, kImageData);
+    image_cache()->SaveImage(kImageUrl, kImageData, needs_transcoding);
     RunUntilIdle();
 
     ASSERT_TRUE(IsMetadataPresent(kImageUrlHashed));
@@ -139,12 +139,12 @@
     RunUntilIdle();
   }
 
-  void InjectMetadata(std::string key, int data_size) {
-    metadata_store_->SaveImageMetadata(key, data_size);
+  void InjectMetadata(std::string key, int data_size, bool needs_transcoding) {
+    metadata_store_->SaveImageMetadata(key, data_size, needs_transcoding);
   }
 
-  void InjectData(std::string key, std::string data) {
-    data_store_->SaveImage(key, data);
+  void InjectData(std::string key, std::string data, bool needs_transcoding) {
+    data_store_->SaveImage(key, data, needs_transcoding);
     RunUntilIdle();
   }
 
@@ -158,7 +158,7 @@
   FakeDB<CachedImageMetadataProto>* db() { return db_; }
   base::HistogramTester& histogram_tester() { return histogram_tester_; }
 
-  MOCK_METHOD1(DataCallback, void(std::string));
+  MOCK_METHOD2(DataCallback, void(bool, std::string));
 
  private:
   scoped_refptr<ImageCache> image_cache_;
@@ -186,24 +186,27 @@
   CreateImageCache();
   InitializeImageCache();
 
-  image_cache()->SaveImage(kImageUrl, kImageData);
+  image_cache()->SaveImage(kImageUrl, kImageData,
+                           /* needs_transcoding */ false);
   RunUntilIdle();
 
-  EXPECT_CALL(*this, DataCallback(kImageData));
+  EXPECT_CALL(*this, DataCallback(false, kImageData));
   image_cache()->LoadImage(
       false, kImageUrl,
       base::BindOnce(&CachedImageFetcherImageCacheTest::DataCallback,
                      base::Unretained(this)));
+  db()->LoadCallback(true);
   RunUntilIdle();
 
   image_cache()->DeleteImage(kImageUrl);
   RunUntilIdle();
 
-  EXPECT_CALL(*this, DataCallback(std::string()));
+  EXPECT_CALL(*this, DataCallback(false, std::string()));
   image_cache()->LoadImage(
       false, kImageUrl,
       base::BindOnce(&CachedImageFetcherImageCacheTest::DataCallback,
                      base::Unretained(this)));
+  db()->LoadCallback(true);
   RunUntilIdle();
 }
 
@@ -211,7 +214,8 @@
   CreateImageCache();
 
   ASSERT_FALSE(IsCacheInitialized());
-  image_cache()->SaveImage(kImageUrl, kImageData);
+  image_cache()->SaveImage(kImageUrl, kImageData,
+                           /* needs_transcoding */ false);
   db()->InitStatusCallback(leveldb_proto::Enums::InitStatus::kOK);
   RunUntilIdle();
 
@@ -222,27 +226,29 @@
   CreateImageCache();
   InitializeImageCache();
 
-  image_cache()->SaveImage(kImageUrl, kImageData);
-  RunUntilIdle();
+  image_cache()->SaveImage(kImageUrl, kImageData,
+                           /* needs_transcoding */ false);
 
-  EXPECT_CALL(*this, DataCallback(kImageData));
+  EXPECT_CALL(*this, DataCallback(false, kImageData));
   image_cache()->LoadImage(
       false, kImageUrl,
       base::BindOnce(&CachedImageFetcherImageCacheTest::DataCallback,
                      base::Unretained(this)));
+  db()->LoadCallback(true);
   RunUntilIdle();
 }
 
 TEST_F(CachedImageFetcherImageCacheTest, Load) {
-  PrepareImageCache();
+  PrepareImageCache(false);
   auto metadata_before = GetMetadata(kImageUrlHashed);
 
   clock()->SetNow(clock()->Now() + base::TimeDelta::FromHours(1));
-  EXPECT_CALL(*this, DataCallback(kImageData));
+  EXPECT_CALL(*this, DataCallback(false, kImageData));
   image_cache()->LoadImage(
       false, kImageUrl,
       base::BindOnce(&CachedImageFetcherImageCacheTest::DataCallback,
                      base::Unretained(this)));
+  db()->LoadCallback(true);
   RunUntilIdle();
   db()->LoadCallback(true);
   db()->UpdateCallback(true);
@@ -253,15 +259,16 @@
 }
 
 TEST_F(CachedImageFetcherImageCacheTest, LoadReadOnly) {
-  PrepareImageCache();
+  PrepareImageCache(false);
   auto metadata_before = GetMetadata(kImageUrlHashed);
 
   clock()->SetNow(clock()->Now() + base::TimeDelta::FromHours(1));
-  EXPECT_CALL(*this, DataCallback(kImageData));
+  EXPECT_CALL(*this, DataCallback(false, kImageData));
   image_cache()->LoadImage(
       true, kImageUrl,
       base::BindOnce(&CachedImageFetcherImageCacheTest::DataCallback,
                      base::Unretained(this)));
+  db()->LoadCallback(true);
   RunUntilIdle();
 
   auto metadata_after = GetMetadata(kImageUrlHashed);
@@ -269,38 +276,41 @@
 }
 
 TEST_F(CachedImageFetcherImageCacheTest, Delete) {
-  PrepareImageCache();
+  PrepareImageCache(false);
 
-  EXPECT_CALL(*this, DataCallback(kImageData));
+  EXPECT_CALL(*this, DataCallback(false, kImageData));
   image_cache()->LoadImage(
       false, kImageUrl,
       base::BindOnce(&CachedImageFetcherImageCacheTest::DataCallback,
                      base::Unretained(this)));
+  db()->LoadCallback(true);
   RunUntilIdle();
 
   image_cache()->DeleteImage(kImageUrl);
   RunUntilIdle();
 
-  EXPECT_CALL(*this, DataCallback(std::string()));
+  EXPECT_CALL(*this, DataCallback(false, std::string()));
   image_cache()->LoadImage(
       false, kImageUrl,
       base::BindOnce(&CachedImageFetcherImageCacheTest::DataCallback,
                      base::Unretained(this)));
+  db()->LoadCallback(true);
   RunUntilIdle();
 }
 
 TEST_F(CachedImageFetcherImageCacheTest, Eviction) {
-  PrepareImageCache();
+  PrepareImageCache(false);
 
   clock()->SetNow(clock()->Now() + base::TimeDelta::FromDays(7));
   RunEvictionOnStartup(/* success */ true);
   ASSERT_EQ(clock()->Now(), prefs()->GetTime(kPrefLastStartupEviction));
 
-  EXPECT_CALL(*this, DataCallback(std::string()));
+  EXPECT_CALL(*this, DataCallback(false, std::string()));
   image_cache()->LoadImage(
       false, kImageUrl,
       base::BindOnce(&CachedImageFetcherImageCacheTest::DataCallback,
                      base::Unretained(this)));
+  db()->LoadCallback(true);
   RunUntilIdle();
 
   histogram_tester().ExpectBucketCount(
@@ -312,47 +322,50 @@
 }
 
 TEST_F(CachedImageFetcherImageCacheTest, EvictionWhenFull) {
-  PrepareImageCache();
-  InjectMetadata(kImageUrl, kOverMaxCacheSize);
+  PrepareImageCache(false);
+  InjectMetadata(kImageUrl, kOverMaxCacheSize, /* needs_transcoding */ false);
   clock()->SetNow(clock()->Now() + base::TimeDelta::FromDays(6));
   RunEvictionWhenFull(/* success */ true);
 
   // The data should be removed because it's over the allowed limit.
-  EXPECT_CALL(*this, DataCallback(""));
+  EXPECT_CALL(*this, DataCallback(false, ""));
   image_cache()->LoadImage(
       false, kImageUrl,
       base::BindOnce(&CachedImageFetcherImageCacheTest::DataCallback,
                      base::Unretained(this)));
+  db()->LoadCallback(true);
   RunUntilIdle();
 }
 
 TEST_F(CachedImageFetcherImageCacheTest, EvictionTooSoon) {
-  PrepareImageCache();
+  PrepareImageCache(false);
 
   clock()->SetNow(clock()->Now() + base::TimeDelta::FromDays(6));
   RunEvictionOnStartup(/* success */ true);
 
-  EXPECT_CALL(*this, DataCallback(kImageData));
+  EXPECT_CALL(*this, DataCallback(false, kImageData));
   image_cache()->LoadImage(
       false, kImageUrl,
       base::BindOnce(&CachedImageFetcherImageCacheTest::DataCallback,
                      base::Unretained(this)));
+  db()->LoadCallback(true);
   RunUntilIdle();
 }
 
 TEST_F(CachedImageFetcherImageCacheTest, EvictionWhenEvictionAlreadyPerformed) {
-  PrepareImageCache();
+  PrepareImageCache(false);
 
   prefs()->SetTime("cached_image_fetcher_last_startup_eviction_time",
                    clock()->Now());
   clock()->SetNow(clock()->Now() + base::TimeDelta::FromHours(23));
   RunEvictionOnStartup(/* success */ false);
 
-  EXPECT_CALL(*this, DataCallback(kImageData));
+  EXPECT_CALL(*this, DataCallback(false, kImageData));
   image_cache()->LoadImage(
       false, kImageUrl,
       base::BindOnce(&CachedImageFetcherImageCacheTest::DataCallback,
                      base::Unretained(this)));
+  db()->LoadCallback(true);
   RunUntilIdle();
 }
 
@@ -361,16 +374,17 @@
   InitializeImageCache();
 
   // Inject differing keys so they mismatch, then run reconciliation.
-  InjectData("foo", "z");
-  InjectMetadata("bar", 10);
+  InjectData("foo", "z", /* needs_transcoding */ false);
+  InjectMetadata("bar", 10, /* needs_transcoding */ false);
   RunReconciliation();
 
   // Data should be gone.
-  EXPECT_CALL(*this, DataCallback(std::string()));
+  EXPECT_CALL(*this, DataCallback(false, std::string()));
   image_cache()->LoadImage(
       false, "foo",
       base::BindOnce(&CachedImageFetcherImageCacheTest::DataCallback,
                      base::Unretained(this)));
+  db()->LoadCallback(true);
   RunUntilIdle();
 
   // Metadata should be gone.
@@ -382,17 +396,18 @@
   InitializeImageCache();
 
   // Inject differing keys so they mismatch, then run reconciliation.
-  InjectData("foo", "z");
-  InjectData("bar", "z");
-  InjectMetadata("foo", 10);
+  InjectData("foo", "z", /* needs_transcoding */ false);
+  InjectData("bar", "z", /* needs_transcoding */ false);
+  InjectMetadata("foo", 10, /* needs_transcoding */ false);
   RunReconciliation();
 
   // Data should be gone.
-  EXPECT_CALL(*this, DataCallback(std::string()));
+  EXPECT_CALL(*this, DataCallback(false, std::string()));
   image_cache()->LoadImage(
       false, "bar",
       base::BindOnce(&CachedImageFetcherImageCacheTest::DataCallback,
                      base::Unretained(this)));
+  db()->LoadCallback(true);
   RunUntilIdle();
 }
 
@@ -401,9 +416,9 @@
   InitializeImageCache();
 
   // Inject differing keys so they mismatch, then run reconciliation.
-  InjectData("foo", "z");
-  InjectMetadata("foo", 10);
-  InjectMetadata("bar", 10);
+  InjectData("foo", "z", /* needs_transcoding */ false);
+  InjectMetadata("foo", 10, /* needs_transcoding */ false);
+  InjectMetadata("bar", 10, /* needs_transcoding */ false);
   RunReconciliation();
 
   // Metadata should be gone.
diff --git a/components/image_fetcher/core/cache/image_data_store.h b/components/image_fetcher/core/cache/image_data_store.h
index 9ad58c28..f27ee0c8 100644
--- a/components/image_fetcher/core/cache/image_data_store.h
+++ b/components/image_fetcher/core/cache/image_data_store.h
@@ -27,11 +27,14 @@
   virtual bool IsInitialized() = 0;
 
   // Adds or updates the image data for the |key|.
-  virtual void SaveImage(const std::string& key, std::string image_data) = 0;
+  virtual void SaveImage(const std::string& key,
+                         std::string image_data,
+                         bool needs_transcoding) = 0;
 
   // Loads the image data for the |key| and passes it to |callback|. If the
   // image isn't available, empty data will be returned.
   virtual void LoadImage(const std::string& key,
+                         bool needs_transcoding,
                          ImageDataCallback callback) = 0;
 
   // Deletes the image data for the |key|.
diff --git a/components/image_fetcher/core/cache/image_data_store_disk.cc b/components/image_fetcher/core/cache/image_data_store_disk.cc
index 04909f0..43acd5a 100644
--- a/components/image_fetcher/core/cache/image_data_store_disk.cc
+++ b/components/image_fetcher/core/cache/image_data_store_disk.cc
@@ -25,6 +25,9 @@
 
 namespace {
 
+// The prefix of the file names for images that need transcoding before use.
+constexpr char kNeedsTranscodingPrefix[] = "ntr_";
+
 const FilePath::CharType kPathPostfix[] =
     FILE_PATH_LITERAL("image_data_storage");
 
@@ -40,19 +43,39 @@
   return InitializationStatus::INIT_FAILURE;
 }
 
+base::FilePath BuildFilePath(const FilePath& storage_path,
+                             const std::string& key,
+                             bool needs_transcoding) {
+  if (needs_transcoding)
+    return storage_path.AppendASCII(kNeedsTranscodingPrefix + key);
+  return storage_path.AppendASCII(key);
+}
+
 void SaveImageImpl(FilePath storage_path,
                    const std::string& key,
-                   std::string data) {
-  FilePath file_path = storage_path.AppendASCII(key);
+                   std::string data,
+                   bool needs_transcoding) {
+  FilePath file_path = BuildFilePath(storage_path, key, needs_transcoding);
 
   int len = base::WriteFile(file_path, data.c_str(), data.length());
   if (len == -1 || (size_t)len != data.length()) {
     DVLOG(1) << "WriteFile failed.";
   }
+
+  if (!needs_transcoding) {
+    // Attempt to delete the image data there that needs transcoding.
+    bool success = base::DeleteFile(
+        BuildFilePath(storage_path, key, /* needs_transcoding */ true), false);
+    if (!success) {
+      DVLOG(1) << "Deleting the transcoded file failed.";
+    }
+  }
 }
 
-std::string LoadImageImpl(FilePath storage_path, const std::string& key) {
-  FilePath file_path = storage_path.AppendASCII(key);
+std::string LoadImageImpl(FilePath storage_path,
+                          const std::string& key,
+                          bool needs_transcoding) {
+  FilePath file_path = BuildFilePath(storage_path, key, needs_transcoding);
 
   if (!base::PathExists(file_path)) {
     return "";
@@ -64,10 +87,19 @@
 }
 
 void DeleteImageImpl(FilePath storage_path, const std::string& key) {
-  FilePath file_path = storage_path.AppendASCII(key);
-
+  FilePath file_path =
+      BuildFilePath(storage_path, key, /* needs_transcoding */ false);
   bool success = base::DeleteFile(file_path, false);
-  if (!success) {
+  if (success) {
+    // We don't know if this image came from network or from an untranscoded
+    // file. Instead of checking, we can simply blindly delete the untranscoded
+    // file.
+    if (!base::DeleteFile(
+            BuildFilePath(storage_path, key, /* needs_transcoding */ true),
+            false)) {
+      DVLOG(1) << "Attempting to delete " << kNeedsTranscodingPrefix << ".";
+    }
+  } else {
     DVLOG(1) << "DeleteFile failed.";
   }
 }
@@ -111,25 +143,31 @@
 }
 
 void ImageDataStoreDisk::SaveImage(const std::string& key,
-                                   std::string image_data) {
+                                   std::string image_data,
+                                   bool needs_transcoding) {
   if (!IsInitialized()) {
     return;
   }
 
-  task_runner_->PostTask(FROM_HERE, base::BindOnce(SaveImageImpl, storage_path_,
-                                                   key, std::move(image_data)));
+  task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(SaveImageImpl, storage_path_, key,
+                                std::move(image_data), needs_transcoding));
 }
 
 void ImageDataStoreDisk::LoadImage(const std::string& key,
+                                   bool needs_transcoding,
                                    ImageDataCallback callback) {
   if (!IsInitialized()) {
-    std::move(callback).Run(std::string());
+    std::move(callback).Run(/* needs_transcoding */ false, std::string());
     return;
   }
 
   base::PostTaskAndReplyWithResult(
       task_runner_.get(), FROM_HERE,
-      base::BindOnce(LoadImageImpl, storage_path_, key), std::move(callback));
+      base::BindOnce(LoadImageImpl, storage_path_, key, needs_transcoding),
+      base::BindOnce(&ImageDataStoreDisk::OnImageLoaded,
+                     weak_ptr_factory_.GetWeakPtr(), needs_transcoding,
+                     std::move(callback)));
 }
 
 void ImageDataStoreDisk::DeleteImage(const std::string& key) {
@@ -159,4 +197,10 @@
   std::move(callback).Run();
 }
 
+void ImageDataStoreDisk::OnImageLoaded(bool needs_transcoding,
+                                       ImageDataCallback callback,
+                                       std::string data) {
+  std::move(callback).Run(needs_transcoding, std::move(data));
+}
+
 }  // namespace image_fetcher
diff --git a/components/image_fetcher/core/cache/image_data_store_disk.h b/components/image_fetcher/core/cache/image_data_store_disk.h
index 0e6a69e..ca8293b 100644
--- a/components/image_fetcher/core/cache/image_data_store_disk.h
+++ b/components/image_fetcher/core/cache/image_data_store_disk.h
@@ -31,8 +31,12 @@
   // ImageDataStorage:
   void Initialize(base::OnceClosure callback) override;
   bool IsInitialized() override;
-  void SaveImage(const std::string& key, std::string data) override;
-  void LoadImage(const std::string& key, ImageDataCallback callback) override;
+  void SaveImage(const std::string& key,
+                 std::string data,
+                 bool needs_transcoding) override;
+  void LoadImage(const std::string& key,
+                 bool needs_transcoding,
+                 ImageDataCallback callback) override;
   void DeleteImage(const std::string& key) override;
   void GetAllKeys(KeysCallback callback) override;
 
@@ -41,6 +45,11 @@
   void OnInitializationComplete(base::OnceClosure callback,
                                 InitializationStatus initialization_status);
 
+  // Called when data is loaded from disk.
+  void OnImageLoaded(bool needs_transcoding,
+                     ImageDataCallback callback,
+                     std::string data);
+
   // Set to be INITIALIZED if a directory exists, or can be created under
   // |storage_path|. If initialization fails, there's no need to retry.
   InitializationStatus initialization_status_;
diff --git a/components/image_fetcher/core/cache/image_data_store_disk_unittest.cc b/components/image_fetcher/core/cache/image_data_store_disk_unittest.cc
index cd18e10..fecf8bb1 100644
--- a/components/image_fetcher/core/cache/image_data_store_disk_unittest.cc
+++ b/components/image_fetcher/core/cache/image_data_store_disk_unittest.cc
@@ -45,31 +45,33 @@
     RunUntilIdle();
   }
 
-  void PrepareDataStore(bool initialize) {
+  void PrepareDataStore(bool initialize, bool setup_for_needs_transcoding) {
     CreateDataStore();
     InitializeDataStore();
-    SaveData(kImageKey);
+    SaveData(kImageKey, setup_for_needs_transcoding);
 
     if (!initialize) {
       CreateDataStore();
     }
   }
 
-  void AssertDataPresent(const std::string& key) {
-    AssertDataPresent(key, kImageData);
+  void AssertDataPresent(const std::string& key, bool needs_transcoding) {
+    AssertDataPresent(key, kImageData, needs_transcoding);
   }
 
-  void AssertDataPresent(const std::string& key, const std::string& data) {
-    EXPECT_CALL(*this, DataCallback(data));
+  void AssertDataPresent(const std::string& key,
+                         const std::string& data,
+                         bool needs_transcoding) {
+    EXPECT_CALL(*this, DataCallback(needs_transcoding, data));
     data_store()->LoadImage(
-        key,
+        key, needs_transcoding,
         base::BindOnce(&CachedImageFetcherImageDataStoreDiskTest::DataCallback,
                        base::Unretained(this)));
     RunUntilIdle();
   }
 
-  void SaveData(const std::string& key) {
-    data_store()->SaveImage(key, kImageData);
+  void SaveData(const std::string& key, bool needs_transcoding) {
+    data_store()->SaveImage(key, kImageData, needs_transcoding);
     RunUntilIdle();
   }
 
@@ -82,7 +84,7 @@
   ImageDataStore* data_store() { return data_store_.get(); }
 
   MOCK_METHOD0(OnInitialized, void());
-  MOCK_METHOD1(DataCallback, void(std::string));
+  MOCK_METHOD2(DataCallback, void(bool, std::string));
   MOCK_METHOD1(KeysCallback, void(std::vector<std::string>));
 
  private:
@@ -99,11 +101,11 @@
   CreateDataStore();
   InitializeDataStore();
 
-  SaveData(kImageKey);
-  AssertDataPresent(kImageKey);
+  SaveData(kImageKey, /* needs_transcoding */ false);
+  AssertDataPresent(kImageKey, /* needs_transcoding */ false);
 
   DeleteData(kImageKey);
-  AssertDataPresent(kImageKey, "");
+  AssertDataPresent(kImageKey, "", /* needs_transcoding */ false);
 }
 
 TEST_F(CachedImageFetcherImageDataStoreDiskTest, Init) {
@@ -115,47 +117,50 @@
 }
 
 TEST_F(CachedImageFetcherImageDataStoreDiskTest, InitWithExistingDirectory) {
-  PrepareDataStore(/* initialize */ true);
+  PrepareDataStore(/* initialize */ true, /* needs_transcoding */ false);
 
   // Recreating the data store shouldn't wipe the directory.
   CreateDataStore();
   InitializeDataStore();
 
-  AssertDataPresent(kImageKey);
+  AssertDataPresent(kImageKey, /* needs_transcoding */ false);
 }
 
 TEST_F(CachedImageFetcherImageDataStoreDiskTest, SaveBeforeInit) {
-  PrepareDataStore(/* initialize */ false);
+  PrepareDataStore(/* initialize */ false, /* needs_transcoding */ false);
   // No data should be present (empty string).
-  AssertDataPresent(kImageKey, "");
+  AssertDataPresent(kImageKey, "", /* needs_transcoding */ false);
 }
 
 TEST_F(CachedImageFetcherImageDataStoreDiskTest, Save) {
-  PrepareDataStore(/* initialize */ true);
-  AssertDataPresent(kImageKey);
+  PrepareDataStore(/* initialize */ true, /* needs_transcoding */ false);
+  AssertDataPresent(kImageKey, /* needs_transcoding */ false);
+
+  PrepareDataStore(/* initialize */ true, /* needs_transcoding */ true);
+  AssertDataPresent(kImageKey, /* needs_transcoding */ true);
 }
 
 TEST_F(CachedImageFetcherImageDataStoreDiskTest, DeleteBeforeInit) {
-  PrepareDataStore(/* initialize */ false);
+  PrepareDataStore(/* initialize */ false, /* needs_transcoding */ false);
 
   DeleteData(kImageKey);
 
   InitializeDataStore();
   // Delete should have failed, data still there.
-  AssertDataPresent(kImageKey);
+  AssertDataPresent(kImageKey, /* needs_transcoding */ false);
 }
 
 TEST_F(CachedImageFetcherImageDataStoreDiskTest, Delete) {
-  PrepareDataStore(/* initialize */ true);
+  PrepareDataStore(/* initialize */ true, /* needs_transcoding */ false);
 
   DeleteData(kImageKey);
 
   // Should be empty string (not present).
-  AssertDataPresent(kImageKey, "");
+  AssertDataPresent(kImageKey, "", /* needs_transcoding */ false);
 }
 
 TEST_F(CachedImageFetcherImageDataStoreDiskTest, GetAllKeysBeforeInit) {
-  PrepareDataStore(/* initialize */ false);
+  PrepareDataStore(/* initialize */ false, /* needs_transcoding */ false);
 
   // Should return empty vector even though there is a file present.
   EXPECT_CALL(*this, KeysCallback(std::vector<std::string>()));
@@ -166,7 +171,7 @@
 }
 
 TEST_F(CachedImageFetcherImageDataStoreDiskTest, GetAllKeys) {
-  PrepareDataStore(/* initialize */ true);
+  PrepareDataStore(/* initialize */ true, /* needs_transcoding */ false);
 
   // Should return empty vector even though there is a file present.
   EXPECT_CALL(*this, KeysCallback(std::vector<std::string>({kImageKey})));
@@ -181,10 +186,11 @@
   CreateDataStore();
   InitializeDataStore();
 
-  SaveData(kImageKey);
-  EXPECT_CALL(*this, DataCallback(kImageData));
+  SaveData(kImageKey, /* needs_transcoding */ false);
+  EXPECT_CALL(*this, DataCallback(false, kImageData));
   data_store()->LoadImage(
       kImageKey,
+      /* needs_transcoding */ false,
       base::BindOnce(&CachedImageFetcherImageDataStoreDiskTest::DataCallback,
                      base::Unretained(this)));
   DeleteData(kImageKey);
diff --git a/components/image_fetcher/core/cache/image_metadata_store.h b/components/image_fetcher/core/cache/image_metadata_store.h
index b14d381..3f9d88e9 100644
--- a/components/image_fetcher/core/cache/image_metadata_store.h
+++ b/components/image_fetcher/core/cache/image_metadata_store.h
@@ -27,9 +27,16 @@
   // While this is false, initialization may have already started.
   virtual bool IsInitialized() = 0;
 
-  // Adds or updates the image metadata for the |key|.
+  // Loads the image metadata for the |key|.
+  virtual void LoadImageMetadata(const std::string& key,
+                                 ImageMetadataCallback) = 0;
+
+  // Adds or updates the image metadata for the |key|. If metadata exists for an
+  // image and the |needs_transcoding| is still true, we don't need to update
+  // the existing metadata.
   virtual void SaveImageMetadata(const std::string& key,
-                                 const size_t data_size) = 0;
+                                 const size_t data_size,
+                                 bool needs_transcoding) = 0;
 
   // Deletes the image metadata for the |key|.
   virtual void DeleteImageMetadata(const std::string& key) = 0;
diff --git a/components/image_fetcher/core/cache/image_metadata_store_leveldb.cc b/components/image_fetcher/core/cache/image_metadata_store_leveldb.cc
index 11a669b..32bfa3a 100644
--- a/components/image_fetcher/core/cache/image_metadata_store_leveldb.cc
+++ b/components/image_fetcher/core/cache/image_metadata_store_leveldb.cc
@@ -98,8 +98,21 @@
          initialization_status_ == InitializationStatus::INIT_FAILURE;
 }
 
+void ImageMetadataStoreLevelDB::LoadImageMetadata(
+    const std::string& key,
+    ImageMetadataCallback callback) {
+  DCHECK(IsInitialized());
+
+  database_->LoadEntriesWithFilter(
+      base::BindRepeating(&KeyMatcherFilter, key), CreateReadOptions(),
+      /* target_prefix */ "",
+      base::BindOnce(&ImageMetadataStoreLevelDB::LoadImageMetadataImpl,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
 void ImageMetadataStoreLevelDB::SaveImageMetadata(const std::string& key,
-                                                  const size_t data_size) {
+                                                  const size_t data_size,
+                                                  bool needs_transcoding) {
   // If the database is not initialized yet, ignore the request.
   if (!IsInitialized()) {
     return;
@@ -112,6 +125,7 @@
   metadata_proto.set_data_size(data_size);
   metadata_proto.set_creation_time(current_time);
   metadata_proto.set_last_used_time(current_time);
+  metadata_proto.set_needs_transcoding(needs_transcoding);
 
   auto entries_to_save = std::make_unique<MetadataKeyEntryVector>();
   entries_to_save->emplace_back(key, metadata_proto);
@@ -188,6 +202,19 @@
   std::move(callback).Run();
 }
 
+void ImageMetadataStoreLevelDB::LoadImageMetadataImpl(
+    ImageMetadataCallback callback,
+    bool success,
+    std::unique_ptr<std::vector<CachedImageMetadataProto>> entries) {
+  if (!success || entries->size() == 0) {
+    std::move(callback).Run(CachedImageMetadataProto());
+    return;
+  }
+
+  DCHECK(entries->size() == 1);
+  std::move(callback).Run(std::move(entries->at(0)));
+}
+
 void ImageMetadataStoreLevelDB::OnImageUpdated(bool success) {
   DVLOG_IF(1, !success) << "ImageMetadataStoreLevelDB update failed.";
 }
diff --git a/components/image_fetcher/core/cache/image_metadata_store_leveldb.h b/components/image_fetcher/core/cache/image_metadata_store_leveldb.h
index e5189a8d..a08d422 100644
--- a/components/image_fetcher/core/cache/image_metadata_store_leveldb.h
+++ b/components/image_fetcher/core/cache/image_metadata_store_leveldb.h
@@ -49,8 +49,11 @@
   // ImageMetadataStorage:
   void Initialize(base::OnceClosure callback) override;
   bool IsInitialized() override;
+  void LoadImageMetadata(const std::string& key,
+                         ImageMetadataCallback callback) override;
   void SaveImageMetadata(const std::string& key,
-                         const size_t data_size) override;
+                         const size_t data_size,
+                         bool needs_transcoding) override;
   void DeleteImageMetadata(const std::string& key) override;
   void UpdateImageMetadata(const std::string& key) override;
   void GetAllKeys(KeysCallback callback) override;
@@ -68,6 +71,10 @@
  private:
   void OnDatabaseInitialized(base::OnceClosure callback,
                              leveldb_proto::Enums::InitStatus status);
+  void LoadImageMetadataImpl(
+      ImageMetadataCallback callback,
+      bool success,
+      std::unique_ptr<std::vector<CachedImageMetadataProto>> entries);
   void OnImageUpdated(bool success);
   void UpdateImageMetadataImpl(
       bool success,
diff --git a/components/image_fetcher/core/cache/image_metadata_store_leveldb_unittest.cc b/components/image_fetcher/core/cache/image_metadata_store_leveldb_unittest.cc
index 52d68dfeb..402bb75c 100644
--- a/components/image_fetcher/core/cache/image_metadata_store_leveldb_unittest.cc
+++ b/components/image_fetcher/core/cache/image_metadata_store_leveldb_unittest.cc
@@ -64,7 +64,8 @@
   void PrepareDatabase(bool initialize) {
     CreateDatabase();
     InitializeDatabase();
-    metadata_store()->SaveImageMetadata(kImageKey, kImageDataLength);
+    metadata_store()->SaveImageMetadata(kImageKey, kImageDataLength,
+                                        /* needs_transcoding */ false);
     ASSERT_TRUE(IsDataPresent(kImageKey));
 
     if (!initialize) {
@@ -103,7 +104,8 @@
   void AssertDataPresent(const std::string& key,
                          int64_t data_size,
                          base::Time creation_time,
-                         base::Time last_used_time) {
+                         base::Time last_used_time,
+                         bool needs_transcoding) {
     if (!IsDataPresent(key)) {
       ASSERT_TRUE(false);
     }
@@ -114,6 +116,7 @@
               creation_time.since_origin().InMicroseconds());
     ASSERT_EQ(entry.last_used_time(),
               last_used_time.since_origin().InMicroseconds());
+    ASSERT_EQ(entry.needs_transcoding(), needs_transcoding);
   }
 
   void RunUntilIdle() { scoped_task_environment_.RunUntilIdle(); }
@@ -124,6 +127,8 @@
   MOCK_METHOD0(OnInitialized, void());
   MOCK_METHOD1(OnKeysReturned, void(std::vector<std::string>));
   MOCK_METHOD1(OnStoreOperationComplete, void(bool));
+  MOCK_METHOD1(OnImageMetadataLoaded,
+               void(base::Optional<CachedImageMetadataProto>));
 
  private:
   std::unique_ptr<base::SimpleTestClock> clock_;
@@ -148,7 +153,8 @@
   CreateDatabase();
   EXPECT_FALSE(metadata_store()->IsInitialized());
   // Start an image load before the database is initialized.
-  metadata_store()->SaveImageMetadata(kImageKey, kImageDataLength);
+  metadata_store()->SaveImageMetadata(kImageKey, kImageDataLength,
+                                      /* needs_transcoding */ false);
 
   InitializeDatabase();
   EXPECT_TRUE(metadata_store()->IsInitialized());
@@ -160,9 +166,14 @@
   CreateDatabase();
   InitializeDatabase();
 
-  metadata_store()->SaveImageMetadata(kImageKey, kImageDataLength);
-  AssertDataPresent(kImageKey, kImageDataLength, clock()->Now(),
-                    clock()->Now());
+  metadata_store()->SaveImageMetadata(kImageKey, kImageDataLength,
+                                      /* needs_transcoding */ false);
+  AssertDataPresent(kImageKey, kImageDataLength, clock()->Now(), clock()->Now(),
+                    /* needs_transcoding */ false);
+  metadata_store()->SaveImageMetadata(kImageKey, kImageDataLength,
+                                      /* needs_transcoding */ true);
+  AssertDataPresent(kImageKey, kImageDataLength, clock()->Now(), clock()->Now(),
+                    /* needs_transcoding */ true);
 }
 
 TEST_F(CachedImageFetcherImageMetadataStoreLevelDBTest, DeleteBeforeInit) {
@@ -177,7 +188,8 @@
   // Put some data in the database to start.
   CreateDatabase();
   InitializeDatabase();
-  metadata_store()->SaveImageMetadata(kImageKey, kImageDataLength);
+  metadata_store()->SaveImageMetadata(kImageKey, kImageDataLength,
+                                      /* needs_transcoding */ false);
   ASSERT_TRUE(IsDataPresent(kImageKey));
 
   // Delete the data.
@@ -191,7 +203,8 @@
   // Put some data in the database to start.
   CreateDatabase();
   InitializeDatabase();
-  metadata_store()->SaveImageMetadata(kImageKey, kImageDataLength);
+  metadata_store()->SaveImageMetadata(kImageKey, kImageDataLength,
+                                      /* needs_transcoding */ false);
   ASSERT_TRUE(IsDataPresent(kImageKey));
 
   // Delete the data.
@@ -210,8 +223,8 @@
   RunUntilIdle();
 
   InitializeDatabase();
-  AssertDataPresent(kImageKey, kImageDataLength, clock()->Now(),
-                    clock()->Now());
+  AssertDataPresent(kImageKey, kImageDataLength, clock()->Now(), clock()->Now(),
+                    /* needs_transcoding */ false);
 }
 
 TEST_F(CachedImageFetcherImageMetadataStoreLevelDBTest, UpdateImageMetadata) {
@@ -225,7 +238,7 @@
 
   AssertDataPresent(kImageKey, kImageDataLength,
                     clock()->Now() - base::TimeDelta::FromHours(1),
-                    clock()->Now());
+                    clock()->Now(), /* needs_transcoding */ false);
 }
 
 TEST_F(CachedImageFetcherImageMetadataStoreLevelDBTest,
@@ -237,8 +250,8 @@
   db()->UpdateCallback(true);
   RunUntilIdle();
 
-  AssertDataPresent(kImageKey, kImageDataLength, clock()->Now(),
-                    clock()->Now());
+  AssertDataPresent(kImageKey, kImageDataLength, clock()->Now(), clock()->Now(),
+                    /* needs_transcoding */ false);
 }
 
 TEST_F(CachedImageFetcherImageMetadataStoreLevelDBTest,
@@ -249,8 +262,8 @@
   db()->LoadCallback(true);
   RunUntilIdle();
 
-  AssertDataPresent(kImageKey, kImageDataLength, clock()->Now(),
-                    clock()->Now());
+  AssertDataPresent(kImageKey, kImageDataLength, clock()->Now(), clock()->Now(),
+                    /* needs_transcoding */ false);
 }
 
 TEST_F(CachedImageFetcherImageMetadataStoreLevelDBTest, GetAllKeysBeforeInit) {
@@ -266,7 +279,8 @@
 
 TEST_F(CachedImageFetcherImageMetadataStoreLevelDBTest, GetAllKeys) {
   PrepareDatabase(true);
-  metadata_store()->SaveImageMetadata(kOtherImageKey, kImageDataLength);
+  metadata_store()->SaveImageMetadata(kOtherImageKey, kImageDataLength,
+                                      /* needs_transcoding */ false);
 
   // A GC call before the db is initialized should be ignore.
   EXPECT_CALL(
@@ -280,7 +294,8 @@
 
 TEST_F(CachedImageFetcherImageMetadataStoreLevelDBTest, GetAllKeysLoadFailed) {
   PrepareDatabase(true);
-  metadata_store()->SaveImageMetadata(kOtherImageKey, kImageDataLength);
+  metadata_store()->SaveImageMetadata(kOtherImageKey, kImageDataLength,
+                                      /* needs_transcoding */ false);
 
   // A GC call before the db is initialized should be ignore.
   EXPECT_CALL(*this, OnKeysReturned(std::vector<std::string>({})));
@@ -350,7 +365,8 @@
 
   // Insert an item one our later.
   clock()->SetNow(clock()->Now() + base::TimeDelta::FromHours(1));
-  metadata_store()->SaveImageMetadata(kOtherImageKey, kImageDataLength);
+  metadata_store()->SaveImageMetadata(kOtherImageKey, kImageDataLength,
+                                      /* needs_transcoding */ false);
   clock()->SetNow(clock()->Now() - base::TimeDelta::FromHours(1));
   ASSERT_TRUE(IsDataPresent(kOtherImageKey));
 
@@ -416,7 +432,21 @@
           base::Unretained(this)));
   db()->LoadCallback(true);
   db()->UpdateCallback(false);
-  // Update failed only simlulates the callback, not the actual data behavior.
+  // Update failed only simulates the callback, not the actual data behavior.
+}
+
+TEST_F(CachedImageFetcherImageMetadataStoreLevelDBTest, LoadImageMetadata) {
+  PrepareDatabase(true);
+  metadata_store()->SaveImageMetadata(kOtherImageKey, kImageDataLength,
+                                      /* needs_transcoding */ true);
+
+  EXPECT_CALL(*this, OnImageMetadataLoaded(_));
+  metadata_store()->LoadImageMetadata(
+      kOtherImageKey,
+      base::BindOnce(&CachedImageFetcherImageMetadataStoreLevelDBTest::
+                         OnImageMetadataLoaded,
+                     base::Unretained(this)));
+  db()->LoadCallback(true);
 }
 
 }  // namespace image_fetcher
diff --git a/components/image_fetcher/core/cache/image_store_types.h b/components/image_fetcher/core/cache/image_store_types.h
index fa64ae8..66737a51 100644
--- a/components/image_fetcher/core/cache/image_store_types.h
+++ b/components/image_fetcher/core/cache/image_store_types.h
@@ -9,6 +9,8 @@
 #include <vector>
 
 #include "base/callback.h"
+#include "base/optional.h"
+#include "components/image_fetcher/core/cache/proto/cached_image_metadata.pb.h"
 
 namespace image_fetcher {
 
@@ -20,12 +22,19 @@
 };
 
 // Returns the resulting raw image data as a std::string. Data will be returned
-// using move semantics.
-using ImageDataCallback = base::OnceCallback<void(std::string)>;
+// using move semantics. If |needs_transcoding| is true, this data must be
+// decoded in a sandbox process.
+using ImageDataCallback =
+    base::OnceCallback<void(bool needs_transcoding, std::string)>;
 
 // Returns bool success when the underlying storage completes an operation.
 using ImageStoreOperationCallback = base::OnceCallback<void(bool)>;
 
+// CachedImageMetadataProto will be returned if image metadata is loaded
+// successfully.
+using ImageMetadataCallback =
+    base::OnceCallback<void(base::Optional<CachedImageMetadataProto>)>;
+
 // Returns a vector of keys.
 using KeysCallback = base::OnceCallback<void(std::vector<std::string>)>;
 
diff --git a/components/image_fetcher/core/cache/proto/cached_image_metadata.proto b/components/image_fetcher/core/cache/proto/cached_image_metadata.proto
index 901adf3..407050c 100644
--- a/components/image_fetcher/core/cache/proto/cached_image_metadata.proto
+++ b/components/image_fetcher/core/cache/proto/cached_image_metadata.proto
@@ -23,4 +23,7 @@
   // certain amount of space has been freed. Is not used to decide the order in
   // which images are evicted.
   optional int64 data_size = 4;
+
+  // True if the image data needs transcoding before it can be trusted.
+  optional bool needs_transcoding = 5;
 }
diff --git a/components/image_fetcher/core/cached_image_fetcher.cc b/components/image_fetcher/core/cached_image_fetcher.cc
index b07e41f..04bd8dd4 100644
--- a/components/image_fetcher/core/cached_image_fetcher.cc
+++ b/components/image_fetcher/core/cached_image_fetcher.cc
@@ -127,6 +127,7 @@
     CachedImageFetcherRequest request,
     ImageDataFetcherCallback image_data_callback,
     ImageFetcherCallback image_callback,
+    bool cache_result_needs_transcoding,
     std::string image_data) {
   if (image_data.empty()) {
     ImageFetcherMetricsReporter::ReportEvent(request.params.uma_client_name(),
@@ -142,14 +143,16 @@
     ImageFetcherMetricsReporter::ReportEvent(request.params.uma_client_name(),
                                              ImageFetcherEvent::kCacheHit);
 
-    // Only continue with decoding if the user actually asked for an image.
-    if (!image_callback.is_null()) {
+    // Only continue with decoding if the user actually asked for an image, or
+    // the image hasn't been transcoded yet.
+    if (!image_callback.is_null() || cache_result_needs_transcoding) {
       GetImageDecoder()->DecodeImage(
           image_data, gfx::Size(),
           base::BindOnce(&CachedImageFetcher::OnImageDecodedFromCache,
                          weak_ptr_factory_.GetWeakPtr(), std::move(request),
                          std::move(image_data_callback),
-                         std::move(image_callback)));
+                         std::move(image_callback),
+                         cache_result_needs_transcoding));
     }
   }
 }
@@ -158,6 +161,7 @@
     CachedImageFetcherRequest request,
     ImageDataFetcherCallback image_data_callback,
     ImageFetcherCallback image_callback,
+    bool cache_result_needs_transcoding,
     const gfx::Image& image) {
   if (image.IsEmpty()) {
     // Upon failure, fetch from the network.
@@ -173,6 +177,16 @@
     ImageCallbackIfPresent(std::move(image_callback), image, RequestMetadata());
     ImageFetcherMetricsReporter::ReportImageLoadFromCacheTime(
         request.params.uma_client_name(), request.start_time);
+
+    // If cache_result_needs_transcoding is true, then this should be stored
+    // again to replace the image data already on disk with the transcoded data.
+    if (cache_result_needs_transcoding) {
+      EncodeAndStoreData(/* cache_result_needs_transcoding */ true,
+                         std::move(request), image);
+      ImageFetcherMetricsReporter::ReportEvent(
+          request.params.uma_client_name(),
+          ImageFetcherEvent::kImageQueuedForTranscodingDecoded);
+    }
   }
 }
 
@@ -200,7 +214,7 @@
   bool skip_transcoding = request.params.skip_transcoding();
   if (skip_transcoding) {
     wrapper_data_callback =
-        base::BindOnce(&CachedImageFetcher::StoreImageDataWithoutTranscoding,
+        base::BindOnce(&CachedImageFetcher::OnImageFetchedWithoutTranscoding,
                        weak_ptr_factory_.GetWeakPtr(), std::move(request),
                        std::move(image_data_callback));
   } else {
@@ -211,7 +225,7 @@
     // 3. Cache the result.
     wrapper_data_callback = std::move(image_data_callback);
     wrapper_image_callback =
-        base::BindOnce(&CachedImageFetcher::StoreImageDataWithTranscoding,
+        base::BindOnce(&CachedImageFetcher::OnImageFetchedForTranscoding,
                        weak_ptr_factory_.GetWeakPtr(), std::move(request),
                        std::move(image_callback));
   }
@@ -220,7 +234,7 @@
                                     std::move(request.params));
 }
 
-void CachedImageFetcher::StoreImageDataWithoutTranscoding(
+void CachedImageFetcher::OnImageFetchedWithoutTranscoding(
     CachedImageFetcherRequest request,
     ImageDataFetcherCallback image_data_callback,
     const std::string& image_data,
@@ -233,10 +247,11 @@
                                              ImageFetcherEvent::kTotalFailure);
   }
 
-  StoreData(std::move(request), image_data);
+  StoreData(/* cache_result_needs_transcoding */ false, std::move(request),
+            image_data);
 }
 
-void CachedImageFetcher::StoreImageDataWithTranscoding(
+void CachedImageFetcher::OnImageFetchedForTranscoding(
     CachedImageFetcherRequest request,
     ImageFetcherCallback image_callback,
     const gfx::Image& image,
@@ -252,13 +267,21 @@
         request.params.uma_client_name(), request.start_time);
   }
 
+  EncodeAndStoreData(/* cache_result_needs_transcoding */ false,
+                     std::move(request), image);
+}
+
+void CachedImageFetcher::EncodeAndStoreData(bool cache_result_needs_transcoding,
+                                            CachedImageFetcherRequest request,
+                                            const gfx::Image& image) {
   // Copy the image data out and store it on disk.
   const SkBitmap* bitmap = image.IsEmpty() ? nullptr : image.ToSkBitmap();
   // If the bitmap is null or otherwise not ready, skip encoding.
   if (bitmap == nullptr || bitmap->isNull() || !bitmap->readyToDraw()) {
     ImageFetcherMetricsReporter::ReportEvent(request.params.uma_client_name(),
                                              ImageFetcherEvent::kTotalFailure);
-    StoreData(std::move(request), "");
+
+    image_cache_->DeleteImage(request.url.spec());
   } else {
     std::string uma_client_name = request.params.uma_client_name();
     // Post a task to another thread to encode the image data downloaded.
@@ -266,11 +289,13 @@
         FROM_HERE,
         base::BindOnce(&EncodeSkBitmapToPNG, uma_client_name, *bitmap),
         base::BindOnce(&CachedImageFetcher::StoreData,
-                       weak_ptr_factory_.GetWeakPtr(), std::move(request)));
+                       weak_ptr_factory_.GetWeakPtr(),
+                       cache_result_needs_transcoding, std::move(request)));
   }
 }
 
-void CachedImageFetcher::StoreData(CachedImageFetcherRequest request,
+void CachedImageFetcher::StoreData(bool cache_result_needs_transcoding,
+                                   CachedImageFetcherRequest request,
                                    std::string image_data) {
   std::string url = request.url.spec();
   // If the image is empty, delete the image.
@@ -280,7 +305,14 @@
   }
 
   if (!read_only_) {
-    image_cache_->SaveImage(std::move(url), std::move(image_data));
+    if (cache_result_needs_transcoding) {
+      ImageFetcherMetricsReporter::ReportEvent(
+          request.params.uma_client_name(),
+          ImageFetcherEvent::kImageQueuedForTranscodingStoredBack);
+    }
+
+    image_cache_->SaveImage(std::move(url), std::move(image_data),
+                            /* needs_transcoding */ false);
   }
 }
 
diff --git a/components/image_fetcher/core/cached_image_fetcher.h b/components/image_fetcher/core/cached_image_fetcher.h
index df911d12..c273b9df 100644
--- a/components/image_fetcher/core/cached_image_fetcher.h
+++ b/components/image_fetcher/core/cached_image_fetcher.h
@@ -46,16 +46,16 @@
 
  private:
   // Cache
-  void OnImageFetchedFromCache(
-      CachedImageFetcherRequest request,
-      ImageDataFetcherCallback image_data_callback,
-      ImageFetcherCallback image_callback,
-      std::string image_data);
-  void OnImageDecodedFromCache(
-      CachedImageFetcherRequest request,
-      ImageDataFetcherCallback image_data_callback,
-      ImageFetcherCallback image_callback,
-      const gfx::Image& image);
+  void OnImageFetchedFromCache(CachedImageFetcherRequest request,
+                               ImageDataFetcherCallback image_data_callback,
+                               ImageFetcherCallback image_callback,
+                               bool cache_result_needs_transcoding,
+                               std::string image_data);
+  void OnImageDecodedFromCache(CachedImageFetcherRequest request,
+                               ImageDataFetcherCallback image_data_callback,
+                               ImageFetcherCallback image_callback,
+                               bool cache_result_needs_transcoding,
+                               const gfx::Image& image);
 
   // Network
   void EnqueueFetchImageFromNetwork(
@@ -66,16 +66,25 @@
       CachedImageFetcherRequest request,
       ImageDataFetcherCallback image_data_callback,
       ImageFetcherCallback image_callback);
-  void StoreImageDataWithoutTranscoding(
+  void OnImageFetchedWithoutTranscoding(
       CachedImageFetcherRequest request,
       ImageDataFetcherCallback image_data_callback,
       const std::string& image_data,
       const RequestMetadata& request_metadata);
-  void StoreImageDataWithTranscoding(CachedImageFetcherRequest request,
-                                     ImageFetcherCallback image_data_callback,
-                                     const gfx::Image& image,
-                                     const RequestMetadata& request_metadata);
-  void StoreData(CachedImageFetcherRequest request, std::string image_data);
+  void OnImageFetchedForTranscoding(CachedImageFetcherRequest request,
+                                    ImageFetcherCallback image_data_callback,
+                                    const gfx::Image& image,
+                                    const RequestMetadata& request_metadata);
+  // Encode the given |image_data| and store it.
+  // |cache_result_needs_transcoding| is passed along for metrics purposes.
+  void EncodeAndStoreData(bool cache_result_needs_transcoding,
+                          CachedImageFetcherRequest request,
+                          const gfx::Image& image);
+  // Store the given |image_data| in the cache. |cache_result_needs_transcoding|
+  // is passed along for metrics purposes.
+  void StoreData(bool cache_result_needs_transcoding,
+                 CachedImageFetcherRequest request,
+                 std::string image_data);
 
   // Owned by ImageFetcherService.
   ImageFetcher* image_fetcher_;
diff --git a/components/image_fetcher/core/cached_image_fetcher_unittest.cc b/components/image_fetcher/core/cached_image_fetcher_unittest.cc
index b1fb203..a6f6448 100644
--- a/components/image_fetcher/core/cached_image_fetcher_unittest.cc
+++ b/components/image_fetcher/core/cached_image_fetcher_unittest.cc
@@ -92,7 +92,8 @@
         base::SequencedTaskRunnerHandle::Get());
 
     // Use an initial request to start the cache up.
-    image_cache_->SaveImage(kImageUrl.spec(), kImageData);
+    image_cache_->SaveImage(kImageUrl.spec(), kImageData,
+                            /* needs_transcoding */ false);
     RunUntilIdle();
     db_->InitStatusCallback(leveldb_proto::Enums::InitStatus::kOK);
     image_cache_->DeleteImage(kImageUrl.spec());
@@ -124,8 +125,9 @@
     return &test_url_loader_factory_;
   }
   base::HistogramTester& histogram_tester() { return histogram_tester_; }
+  FakeDB<CachedImageMetadataProto>* db() { return db_; }
 
-  MOCK_METHOD1(OnImageLoaded, void(std::string));
+  MOCK_METHOD2(OnImageLoaded, void(bool, std::string));
 
  private:
   std::unique_ptr<ImageFetcher> image_fetcher_;
@@ -163,7 +165,8 @@
 // that they both can use what's inside.
 TEST_F(CachedImageFetcherTest, FetchImageFromCache) {
   // Save the image in the database.
-  image_cache()->SaveImage(kImageUrl.spec(), kImageData);
+  image_cache()->SaveImage(kImageUrl.spec(), kImageData,
+                           /* needs_transcoding */ false);
   RunUntilIdle();
 
   base::MockCallback<ImageDataFetcherCallback> data_callback;
@@ -174,7 +177,7 @@
   cached_image_fetcher()->FetchImageAndData(
       kImageUrl, data_callback.Get(), image_callback.Get(),
       ImageFetcherParams(TRAFFIC_ANNOTATION_FOR_TESTS, kUmaClientName));
-
+  db()->LoadCallback(true);
   RunUntilIdle();
 
   histogram_tester().ExpectTotalCount(kCacheLoadHistogramName, 1);
@@ -184,10 +187,36 @@
                                        ImageFetcherEvent::kCacheHit, 1);
 }
 
+TEST_F(CachedImageFetcherTest, FetchImageFromCacheNeedsTranscoding) {
+  // Save the image in the database.
+  image_cache()->SaveImage(kImageUrl.spec(), kImageData,
+                           /* needs_transcoding */ true);
+  RunUntilIdle();
+
+  base::MockCallback<ImageDataFetcherCallback> data_callback;
+  base::MockCallback<ImageFetcherCallback> image_callback;
+
+  EXPECT_CALL(data_callback, Run(kImageData, _));
+  EXPECT_CALL(image_callback, Run(NonEmptyImage(), _));
+  cached_image_fetcher()->FetchImageAndData(
+      kImageUrl, data_callback.Get(), image_callback.Get(),
+      ImageFetcherParams(TRAFFIC_ANNOTATION_FOR_TESTS, kUmaClientName));
+  db()->LoadCallback(true);
+  RunUntilIdle();
+
+  histogram_tester().ExpectBucketCount(
+      kImageFetcherEventHistogramName,
+      ImageFetcherEvent::kImageQueuedForTranscodingDecoded, 1);
+  histogram_tester().ExpectBucketCount(
+      kImageFetcherEventHistogramName,
+      ImageFetcherEvent::kImageQueuedForTranscodingStoredBack, 1);
+}
+
 TEST_F(CachedImageFetcherTest, FetchImageFromCacheReadOnly) {
   CreateCachedImageFetcher(/* read_only */ true);
   // Save the image in the database.
-  image_cache()->SaveImage(kImageUrl.spec(), kImageData);
+  image_cache()->SaveImage(kImageUrl.spec(), kImageData,
+                           /* needs_transcoding */ false);
   test_url_loader_factory()->AddResponse(kImageUrl.spec(), kImageData);
   RunUntilIdle();
   {
@@ -200,6 +229,7 @@
     cached_image_fetcher()->FetchImageAndData(
         kImageUrl, data_callback.Get(), image_callback.Get(),
         ImageFetcherParams(TRAFFIC_ANNOTATION_FOR_TESTS, kUmaClientName));
+    db()->LoadCallback(true);
     RunUntilIdle();
 
     histogram_tester().ExpectBucketCount(kImageFetcherEventHistogramName,
@@ -219,6 +249,7 @@
     cached_image_fetcher()->FetchImageAndData(
         kImageUrl, data_callback.Get(), image_callback.Get(),
         ImageFetcherParams(TRAFFIC_ANNOTATION_FOR_TESTS, kUmaClientName));
+    db()->LoadCallback(true);
     RunUntilIdle();
   }
 }
@@ -236,7 +267,7 @@
     cached_image_fetcher()->FetchImageAndData(
         kImageUrl, data_callback.Get(), image_callback.Get(),
         ImageFetcherParams(TRAFFIC_ANNOTATION_FOR_TESTS, kUmaClientName));
-
+    db()->LoadCallback(true);
     RunUntilIdle();
 
     histogram_tester().ExpectTotalCount(kNetworkLoadHistogramName, 1);
@@ -247,11 +278,12 @@
   }
   // Make sure the image data is in the database.
   {
-    EXPECT_CALL(*this, OnImageLoaded(NonEmptyString()));
+    EXPECT_CALL(*this, OnImageLoaded(false, NonEmptyString()));
     image_cache()->LoadImage(
         /* read_only */ false, kImageUrl.spec(),
         base::BindOnce(&CachedImageFetcherTest::OnImageLoaded,
                        base::Unretained(this)));
+    db()->LoadCallback(true);
     RunUntilIdle();
   }
   // Fetch again. The cache should be populated, no network request is needed.
@@ -266,7 +298,7 @@
     cached_image_fetcher()->FetchImageAndData(
         kImageUrl, data_callback.Get(), image_callback.Get(),
         ImageFetcherParams(TRAFFIC_ANNOTATION_FOR_TESTS, kUmaClientName));
-
+    db()->LoadCallback(true);
     RunUntilIdle();
   }
 }
@@ -285,7 +317,7 @@
     cached_image_fetcher()->FetchImageAndData(
         kImageUrl, data_callback.Get(), image_callback.Get(),
         ImageFetcherParams(TRAFFIC_ANNOTATION_FOR_TESTS, kUmaClientName));
-
+    db()->LoadCallback(true);
     RunUntilIdle();
 
     histogram_tester().ExpectTotalCount(kNetworkLoadHistogramName, 1);
@@ -296,11 +328,12 @@
   }
   // Make sure the image data is not in the database.
   {
-    EXPECT_CALL(*this, OnImageLoaded(std::string()));
+    EXPECT_CALL(*this, OnImageLoaded(false, std::string()));
     image_cache()->LoadImage(
         /* read_only */ false, kImageUrl.spec(),
         base::BindOnce(&CachedImageFetcherTest::OnImageLoaded,
                        base::Unretained(this)));
+    db()->LoadCallback(true);
     RunUntilIdle();
   }
 }
@@ -317,7 +350,7 @@
     params.set_skip_transcoding_for_testing(true);
     cached_image_fetcher()->FetchImageAndData(kImageUrl, data_callback.Get(),
                                               ImageFetcherCallback(), params);
-
+    db()->LoadCallback(true);
     RunUntilIdle();
   }
   {
@@ -327,14 +360,15 @@
     cached_image_fetcher()->FetchImageAndData(
         kImageUrl, data_callback.Get(), ImageFetcherCallback(),
         ImageFetcherParams(TRAFFIC_ANNOTATION_FOR_TESTS, kUmaClientName));
-
+    db()->LoadCallback(true);
     RunUntilIdle();
   }
 }
 
 TEST_F(CachedImageFetcherTest, FetchImageWithSkipDiskCache) {
   // Save the image in the database.
-  image_cache()->SaveImage(kImageUrl.spec(), kImageDataOther);
+  image_cache()->SaveImage(kImageUrl.spec(), kImageDataOther,
+                           /* needs_transcoding */ false);
   RunUntilIdle();
   test_url_loader_factory()->AddResponse(kImageUrl.spec(), kImageData);
 
diff --git a/components/image_fetcher/core/image_fetcher_metrics_reporter.cc b/components/image_fetcher/core/image_fetcher_metrics_reporter.cc
index b2a4a4a7..9779f5b1 100644
--- a/components/image_fetcher/core/image_fetcher_metrics_reporter.cc
+++ b/components/image_fetcher/core/image_fetcher_metrics_reporter.cc
@@ -29,6 +29,7 @@
     "CachedImageFetcher.ImageLoadFromNetworkTime";
 constexpr char kImageLoadFromNetworkAfterCacheHitHistogram[] =
     "CachedImageFetcher.ImageLoadFromNetworkAfterCacheHit";
+constexpr char kLoadImageMetadata[] = "CachedImageFetcher.LoadImageMetadata";
 
 // Returns a raw pointer to a histogram which is owned
 base::HistogramBase* GetTimeHistogram(const std::string& histogram_name,
@@ -118,4 +119,11 @@
                       time_delta);
 }
 
+// static
+void ImageFetcherMetricsReporter::ReportLoadImageMetadata(
+    base::TimeTicks start_time) {
+  base::TimeDelta time_delta = base::TimeTicks::Now() - start_time;
+  UMA_HISTOGRAM_TIMES(kLoadImageMetadata, time_delta);
+}
+
 }  // namespace image_fetcher
diff --git a/components/image_fetcher/core/image_fetcher_metrics_reporter.h b/components/image_fetcher/core/image_fetcher_metrics_reporter.h
index 45c08b8..f09432b 100644
--- a/components/image_fetcher/core/image_fetcher_metrics_reporter.h
+++ b/components/image_fetcher/core/image_fetcher_metrics_reporter.h
@@ -27,7 +27,10 @@
   kCacheStartupEvictionFinished = 7,
   kJavaInMemoryCacheHit = 8,
   kJavaDiskCacheHit = 9,
-  kMaxValue = kJavaDiskCacheHit,
+  kImageQueuedForTranscodingDecoded = 10,
+  kImageQueuedForTranscodingStoredBack = 11,
+  kLoadImageMetadata = 12,
+  kMaxValue = kLoadImageMetadata,
 };
 
 class ImageFetcherMetricsReporter {
@@ -63,6 +66,9 @@
 
   // Report the time between cache evictions.
   static void ReportTimeSinceLastCacheLRUEviction(base::Time start_time);
+
+  // Report the time it takes to load metadata.
+  static void ReportLoadImageMetadata(base::TimeTicks start_time);
 };
 
 }  // namespace image_fetcher
diff --git a/components/nacl/browser/nacl_process_host.cc b/components/nacl/browser/nacl_process_host.cc
index 30dbc8c6..0fd53ebb 100644
--- a/components/nacl/browser/nacl_process_host.cc
+++ b/components/nacl/browser/nacl_process_host.cc
@@ -17,7 +17,6 @@
 #include "base/feature_list.h"
 #include "base/files/file_util.h"
 #include "base/location.h"
-#include "base/metrics/field_trial.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/path_service.h"
 #include "base/process/launch.h"
@@ -540,12 +539,6 @@
   if (NaClBrowser::GetDelegate()->DialogsAreSuppressed())
     cmd_line->AppendSwitch(switches::kNoErrorDialogs);
 
-  // TODO(crbug.com/932175): Remove this after field trials no longer need to
-  // be consulted for the MojoChannelMac launch.
-  base::FieldTrialList::CopyFieldTrialStateToFlags(
-      switches::kFieldTrialHandle, switches::kEnableFeatures,
-      switches::kDisableFeatures, cmd_line.get());
-
 #if defined(OS_WIN)
   cmd_line->AppendArg(switches::kPrefetchArgumentOther);
 #endif  // defined(OS_WIN)
diff --git a/components/nacl/common/nacl_service.cc b/components/nacl/common/nacl_service.cc
index 4648333..0763a6c 100644
--- a/components/nacl/common/nacl_service.cc
+++ b/components/nacl/common/nacl_service.cc
@@ -29,7 +29,6 @@
 
 #if defined(OS_MACOSX)
 #include "base/mac/mach_port_rendezvous.h"
-#include "mojo/public/cpp/platform/features.h"
 #endif
 
 namespace {
@@ -39,22 +38,16 @@
 #if defined(OS_WIN)
   endpoint = mojo::PlatformChannel::RecoverPassedEndpointFromCommandLine(
       *base::CommandLine::ForCurrentProcess());
-#else
-#if defined(OS_MACOSX)
-  if (base::FeatureList::IsEnabled(mojo::features::kMojoChannelMac)) {
-    auto* client = base::MachPortRendezvousClient::GetInstance();
-    if (client) {
-      endpoint = mojo::PlatformChannelEndpoint(
-          mojo::PlatformHandle(client->TakeReceiveRight('mojo')));
-    }
-  } else {
-#endif  // defined(OS_MACOSX)
-    endpoint = mojo::PlatformChannelEndpoint(mojo::PlatformHandle(
-        base::ScopedFD(base::GlobalDescriptors::GetInstance()->Get(
-            service_manager::kMojoIPCChannel))));
-#if defined(OS_MACOSX)
+#elif defined(OS_MACOSX)
+  auto* client = base::MachPortRendezvousClient::GetInstance();
+  if (client) {
+    endpoint = mojo::PlatformChannelEndpoint(
+        mojo::PlatformHandle(client->TakeReceiveRight('mojo')));
   }
-#endif  // defined(OS_MACOSX)
+#else
+  endpoint = mojo::PlatformChannelEndpoint(mojo::PlatformHandle(
+      base::ScopedFD(base::GlobalDescriptors::GetInstance()->Get(
+          service_manager::kMojoIPCChannel))));
 #endif  // !defined(OS_WIN)
   DCHECK(endpoint.is_valid());
   return mojo::IncomingInvitation::Accept(std::move(endpoint));
diff --git a/components/offline_pages/core/BUILD.gn b/components/offline_pages/core/BUILD.gn
index 12ecaa4..36655d4 100644
--- a/components/offline_pages/core/BUILD.gn
+++ b/components/offline_pages/core/BUILD.gn
@@ -56,7 +56,6 @@
     "offline_clock.h",
     "offline_event_logger.cc",
     "offline_event_logger.h",
-    "offline_page_archive_publisher.cc",
     "offline_page_archive_publisher.h",
     "offline_page_archiver.cc",
     "offline_page_archiver.h",
@@ -82,7 +81,6 @@
     "page_criteria.h",
     "snapshot_controller.cc",
     "snapshot_controller.h",
-    "system_download_manager.h",
     "visuals_decoder.h",
   ]
 
@@ -117,8 +115,6 @@
     "offline_page_test_archiver.h",
     "stub_offline_page_model.cc",
     "stub_offline_page_model.h",
-    "stub_system_download_manager.cc",
-    "stub_system_download_manager.h",
     "test_scoped_offline_clock.cc",
     "test_scoped_offline_clock.h",
   ]
@@ -177,7 +173,6 @@
     "model/update_file_path_task_unittest.cc",
     "model/visuals_availability_task_unittest.cc",
     "offline_event_logger_unittest.cc",
-    "offline_page_archive_publisher_unittest.cc",
     "offline_page_feature_unittest.cc",
     "offline_page_item_utils_unittest.cc",
     "offline_page_metadata_store_unittest.cc",
diff --git a/components/offline_pages/core/model/offline_page_model_taskified.cc b/components/offline_pages/core/model/offline_page_model_taskified.cc
index 7606e7da..871a40e 100644
--- a/components/offline_pages/core/model/offline_page_model_taskified.cc
+++ b/components/offline_pages/core/model/offline_page_model_taskified.cc
@@ -35,7 +35,6 @@
 #include "components/offline_pages/core/offline_page_metadata_store.h"
 #include "components/offline_pages/core/offline_page_model.h"
 #include "components/offline_pages/core/offline_store_utils.h"
-#include "components/offline_pages/core/system_download_manager.h"
 #include "url/gurl.h"
 
 namespace offline_pages {
@@ -172,12 +171,10 @@
 OfflinePageModelTaskified::OfflinePageModelTaskified(
     std::unique_ptr<OfflinePageMetadataStore> store,
     std::unique_ptr<ArchiveManager> archive_manager,
-    std::unique_ptr<SystemDownloadManager> download_manager,
     std::unique_ptr<OfflinePageArchivePublisher> archive_publisher,
     const scoped_refptr<base::SequencedTaskRunner>& task_runner)
     : store_(std::move(store)),
       archive_manager_(std::move(archive_manager)),
-      download_manager_(std::move(download_manager)),
       archive_publisher_(std::move(archive_publisher)),
       policy_controller_(new ClientPolicyController()),
       task_queue_(this),
@@ -587,9 +584,8 @@
   // Remove the page from the system download manager. We don't need to wait for
   // completion before calling the delete page callback.
   task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&OfflinePageModelTaskified::RemoveFromDownloadManager,
-                     download_manager_.get(), system_download_ids));
+      FROM_HERE, base::BindOnce(&OfflinePageModelTaskified::Unpublish,
+                                archive_publisher_.get(), system_download_ids));
 
   if (!callback.is_null())
     std::move(callback).Run(result);
@@ -613,11 +609,11 @@
   }
 }
 
-void OfflinePageModelTaskified::RemoveFromDownloadManager(
-    SystemDownloadManager* download_manager,
+void OfflinePageModelTaskified::Unpublish(
+    OfflinePageArchivePublisher* publisher,
     const std::vector<int64_t>& system_download_ids) {
   if (system_download_ids.size() > 0)
-    download_manager->Remove(system_download_ids);
+    publisher->UnpublishArchives(system_download_ids);
 }
 
 void OfflinePageModelTaskified::ScheduleMaintenanceTasks() {
@@ -668,11 +664,9 @@
 void OfflinePageModelTaskified::OnPersistentPageConsistencyCheckDone(
     bool success,
     const std::vector<int64_t>& pages_deleted) {
-  // If there's no persistent page expired, save some effort by exiting early.
   // TODO(https://crbug.com/834909): Use the temporary hidden bit in
-  // DownloadUIAdapter instead of calling remove directly.
-  if (pages_deleted.size() > 0)
-    download_manager_->Remove(pages_deleted);
+  // DownloadUIAdapter instead of removing directly.
+  Unpublish(archive_publisher_.get(), pages_deleted);
 }
 
 void OfflinePageModelTaskified::OnClearCachedPagesDone(
diff --git a/components/offline_pages/core/model/offline_page_model_taskified.h b/components/offline_pages/core/model/offline_page_model_taskified.h
index f064418..026a9d23 100644
--- a/components/offline_pages/core/model/offline_page_model_taskified.h
+++ b/components/offline_pages/core/model/offline_page_model_taskified.h
@@ -17,6 +17,7 @@
 #include "base/observer_list.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/offline_pages/core/model/clear_storage_task.h"
+#include "components/offline_pages/core/offline_page_archive_publisher.h"
 #include "components/offline_pages/core/offline_page_archiver.h"
 #include "components/offline_pages/core/offline_page_model.h"
 #include "components/offline_pages/core/offline_page_model_event_logger.h"
@@ -39,10 +40,8 @@
 
 class ArchiveManager;
 class ClientPolicyController;
-class OfflinePageArchivePublisher;
 class OfflinePageArchiver;
 class OfflinePageMetadataStore;
-class SystemDownloadManager;
 
 // Implementaion of OfflinePageModel, which is a service for saving pages
 // offline. It's an entry point to get information about Offline Pages and the
@@ -66,7 +65,6 @@
   OfflinePageModelTaskified(
       std::unique_ptr<OfflinePageMetadataStore> store,
       std::unique_ptr<ArchiveManager> archive_manager,
-      std::unique_ptr<SystemDownloadManager> download_manager,
       std::unique_ptr<OfflinePageArchivePublisher> archive_publisher,
       const scoped_refptr<base::SequencedTaskRunner>& task_runner);
   ~OfflinePageModelTaskified() override;
@@ -185,10 +183,9 @@
                                   const OfflinePageItem& offline_page,
                                   PublishArchiveResult publish_results);
 
-  // Method for unpublishing the page from the system download manager.
-  static void RemoveFromDownloadManager(
-      SystemDownloadManager* download_manager,
-      const std::vector<int64_t>& system_download_ids);
+  // Method for unpublishing the page from downloads.
+  static void Unpublish(OfflinePageArchivePublisher* publisher,
+                        const std::vector<int64_t>& system_download_ids);
 
   // Other utility methods.
   void RemovePagesMatchingUrlAndNamespace(const OfflinePageItem& page);
@@ -200,9 +197,6 @@
   // Manager for the offline archive files and directory.
   std::unique_ptr<ArchiveManager> archive_manager_;
 
-  // Manages interaction with the OS download manager, if present.
-  std::unique_ptr<SystemDownloadManager> download_manager_;
-
   // Used for moving archives into public storage.
   std::unique_ptr<OfflinePageArchivePublisher> archive_publisher_;
 
diff --git a/components/offline_pages/core/model/offline_page_model_taskified_unittest.cc b/components/offline_pages/core/model/offline_page_model_taskified_unittest.cc
index c799d59..d57291a 100644
--- a/components/offline_pages/core/model/offline_page_model_taskified_unittest.cc
+++ b/components/offline_pages/core/model/offline_page_model_taskified_unittest.cc
@@ -34,7 +34,6 @@
 #include "components/offline_pages/core/offline_page_test_archiver.h"
 #include "components/offline_pages/core/offline_page_types.h"
 #include "components/offline_pages/core/offline_store_utils.h"
-#include "components/offline_pages/core/stub_system_download_manager.h"
 #include "components/offline_pages/core/test_scoped_offline_clock.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -141,8 +140,9 @@
                                                          ArchiverResult result);
   void CheckTaskQueueIdle();
 
-  void SetArchivePublisher(
-      std::unique_ptr<OfflinePageArchivePublisher> publisher) {
+  void SetTestArchivePublisher(
+      std::unique_ptr<OfflinePageTestArchivePublisher> publisher) {
+    publisher_ = publisher.get();
     model()->archive_publisher_ = std::move(publisher);
   }
 
@@ -156,9 +156,6 @@
   }
 
   ArchiveManager* archive_manager() { return archive_manager_; }
-  StubSystemDownloadManager* download_manager_stub() {
-    return download_manager_stub_;
-  }
   OfflinePageItemGenerator* page_generator() { return &generator_; }
   TaskQueue* task_queue() { return &model_->task_queue_; }
   base::HistogramTester* histogram_tester() { return histogram_tester_.get(); }
@@ -182,16 +179,17 @@
   base::Time last_maintenance_tasks_schedule_time() {
     return model_->last_maintenance_tasks_schedule_time_;
   }
+  OfflinePageTestArchivePublisher* publisher() { return publisher_; }
 
  private:
   scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
   base::ThreadTaskRunnerHandle task_runner_handle_;
   std::unique_ptr<OfflinePageModelTaskified> model_;
   OfflinePageMetadataStoreTestUtil store_test_util_;
-  StubSystemDownloadManager* download_manager_stub_;
   ArchiveManager* archive_manager_;
   OfflinePageItemGenerator generator_;
   std::unique_ptr<base::HistogramTester> histogram_tester_;
+  OfflinePageTestArchivePublisher* publisher_;
   base::ScopedTempDir temporary_dir_;
   base::ScopedTempDir private_archive_dir_;
   base::ScopedTempDir public_archive_dir_;
@@ -257,17 +255,14 @@
       temporary_dir_path(), private_archive_dir_path(),
       public_archive_dir_path(), base::ThreadTaskRunnerHandle::Get());
   archive_manager_ = archive_manager.get();
-  auto download_manager =
-      std::make_unique<StubSystemDownloadManager>(kDownloadId, true);
-  download_manager_stub_ = download_manager.get();
 
   auto publisher = std::make_unique<OfflinePageTestArchivePublisher>(
-      archive_manager.get(), download_manager.get());
+      archive_manager.get(), kDownloadId);
+  publisher_ = publisher.get();
 
   model_ = std::make_unique<OfflinePageModelTaskified>(
       store_test_util()->ReleaseStore(), std::move(archive_manager),
-      std::move(download_manager), std::move(publisher),
-      base::ThreadTaskRunnerHandle::Get());
+      std::move(publisher), base::ThreadTaskRunnerHandle::Get());
   model_->AddObserver(this);
   histogram_tester_ = std::make_unique<base::HistogramTester>();
   ResetResults();
@@ -887,8 +882,7 @@
   EXPECT_EQ(last_deleted_page().offline_id, page1.offline_id);
   EXPECT_EQ(1UL, test_utils::GetFileCountInDirectory(temporary_dir_path()));
   EXPECT_EQ(1LL, store_test_util()->GetPageCount());
-  EXPECT_EQ(page1.system_download_id,
-            download_manager_stub()->last_removed_id());
+  EXPECT_EQ(page1.system_download_id, publisher()->last_removed_id());
   histogram_tester()->ExpectUniqueSample(
       "OfflinePages.DeletePageCount",
       static_cast<int>(
@@ -1171,10 +1165,10 @@
 
   // Expect that PublishArchive is called and force returning FILE_MOVE_FAILED.
   auto publisher = std::make_unique<OfflinePageTestArchivePublisher>(
-      archive_manager(), download_manager_stub());
+      archive_manager(), kDownloadId);
   publisher->expect_publish_archive_called(true);
   publisher->set_archive_attempt_failure(true);
-  SetArchivePublisher(std::move(publisher));
+  SetTestArchivePublisher(std::move(publisher));
 
   SavePageWithFileMoveFailure(kTestUrl, kTestUserRequestedClientId, GURL(),
                               kEmptyRequestOrigin, std::move(archiver));
@@ -1499,8 +1493,7 @@
   EXPECT_EQ(0UL,
             test_utils::GetFileCountInDirectory(public_archive_dir_path()));
   EXPECT_EQ(1LL, store_test_util()->GetPageCount());
-  EXPECT_EQ(page.system_download_id,
-            download_manager_stub()->last_removed_id());
+  EXPECT_EQ(page.system_download_id, publisher()->last_removed_id());
   histogram_tester()->ExpectTotalCount(
       "OfflinePages.ConsistencyCheck.Persistent.Result", 3);
 }
diff --git a/components/offline_pages/core/offline_page_archive_publisher.cc b/components/offline_pages/core/offline_page_archive_publisher.cc
deleted file mode 100644
index e616869..0000000
--- a/components/offline_pages/core/offline_page_archive_publisher.cc
+++ /dev/null
@@ -1,107 +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/offline_pages/core/offline_page_archive_publisher.h"
-
-#include <errno.h>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/files/file_util.h"
-#include "base/sequenced_task_runner.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/task_runner_util.h"
-#include "components/offline_pages/core/archive_manager.h"
-#include "components/offline_pages/core/model/offline_page_model_utils.h"
-#include "components/offline_pages/core/offline_store_utils.h"
-#include "components/offline_pages/core/system_download_manager.h"
-
-namespace offline_pages {
-
-namespace {
-
-using offline_pages::SavePageResult;
-
-// Helper function to do the move and register synchronously. Make sure this is
-// called from a background thread.
-PublishArchiveResult MoveAndRegisterArchive(
-    const offline_pages::OfflinePageItem& offline_page,
-    const base::FilePath& publish_directory,
-    offline_pages::SystemDownloadManager* download_manager) {
-  PublishArchiveResult archive_result;
-  // Calculate the new file name.
-  base::FilePath new_file_path =
-      offline_pages::model_utils::GenerateUniqueFilenameForOfflinePage(
-          offline_page.title, offline_page.url, publish_directory);
-
-  // Create the destination directory if it does not already exist.
-  if (!publish_directory.empty() && !base::DirectoryExists(publish_directory)) {
-    base::File::Error file_error;
-    base::CreateDirectoryAndGetError(publish_directory, &file_error);
-  }
-
-  // Move the file.
-  bool moved = base::Move(offline_page.file_path, new_file_path);
-  if (!moved) {
-    archive_result.move_result = SavePageResult::FILE_MOVE_FAILED;
-    DVPLOG(0) << "OfflinePage publishing file move failure " << __func__;
-
-    if (!base::PathExists(offline_page.file_path)) {
-      DVLOG(0) << "Can't copy from non-existent path, from "
-               << offline_page.file_path << " " << __func__;
-    }
-    if (!base::PathExists(publish_directory)) {
-      DVLOG(0) << "Target directory does not exist, " << publish_directory
-               << " " << __func__;
-    }
-    return archive_result;
-  }
-
-  // Tell the download manager about our file, get back an id.
-  if (!download_manager->IsDownloadManagerInstalled()) {
-    archive_result.move_result = SavePageResult::ADD_TO_DOWNLOAD_MANAGER_FAILED;
-    return archive_result;
-  }
-
-  // TODO(petewil): Handle empty page title.
-  std::string page_title = base::UTF16ToUTF8(offline_page.title);
-  // We use the title for a description, since the add to the download manager
-  // fails without a description, and we don't have anything better to use.
-  int64_t download_id = download_manager->AddCompletedDownload(
-      page_title, page_title,
-      offline_pages::store_utils::ToDatabaseFilePath(new_file_path),
-      offline_page.file_size, offline_page.url.spec(), std::string());
-  if (download_id == 0LL) {
-    archive_result.move_result = SavePageResult::ADD_TO_DOWNLOAD_MANAGER_FAILED;
-    return archive_result;
-  }
-
-  // Put results into the result object.
-  archive_result.move_result = SavePageResult::SUCCESS;
-  archive_result.new_file_path = new_file_path;
-  archive_result.download_id = download_id;
-
-  return archive_result;
-}
-
-}  // namespace
-
-OfflinePageArchivePublisher::OfflinePageArchivePublisher(
-    ArchiveManager* archive_manager,
-    SystemDownloadManager* download_manager)
-    : archive_manager_(archive_manager), download_manager_(download_manager) {}
-
-void OfflinePageArchivePublisher::PublishArchive(
-    const OfflinePageItem& offline_page,
-    const scoped_refptr<base::SequencedTaskRunner>& background_task_runner,
-    PublishArchiveDoneCallback publish_done_callback) const {
-  base::PostTaskAndReplyWithResult(
-      background_task_runner.get(), FROM_HERE,
-      base::BindOnce(&MoveAndRegisterArchive, offline_page,
-                     archive_manager_->GetPublicArchivesDir(),
-                     download_manager_),
-      base::BindOnce(std::move(publish_done_callback), offline_page));
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/core/offline_page_archive_publisher.h b/components/offline_pages/core/offline_page_archive_publisher.h
index 76c31fd1..364f2aa 100644
--- a/components/offline_pages/core/offline_page_archive_publisher.h
+++ b/components/offline_pages/core/offline_page_archive_publisher.h
@@ -10,7 +10,6 @@
 
 #include "base/callback.h"
 #include "base/files/file_path.h"
-#include "base/strings/string16.h"
 #include "components/offline_pages/core/offline_page_item.h"
 #include "components/offline_pages/core/offline_page_types.h"
 
@@ -20,9 +19,6 @@
 
 namespace offline_pages {
 
-class ArchiveManager;
-class SystemDownloadManager;
-
 // The result of publishing an offline page to Downloads.
 struct PublishArchiveResult {
   SavePageResult move_result;
@@ -38,23 +34,18 @@
       base::OnceCallback<void(const OfflinePageItem& /* offline_page */,
                               PublishArchiveResult /* archive_result */)>;
 
-  OfflinePageArchivePublisher(ArchiveManager* archive_manager,
-                              SystemDownloadManager* download_manager);
   virtual ~OfflinePageArchivePublisher() {}
 
   // Publishes the page on a background thread, then returns to the
   // OfflinePageModelTaskified's done callback.
-  //
-  // A default implementation publishing the file via SystemDownloadManager is
-  // provided but embedders may implement it themselves.
   virtual void PublishArchive(
       const OfflinePageItem& offline_page,
       const scoped_refptr<base::SequencedTaskRunner>& background_task_runner,
-      PublishArchiveDoneCallback publish_done_callback) const;
+      PublishArchiveDoneCallback publish_done_callback) const = 0;
 
- protected:
-  ArchiveManager* archive_manager_;
-  SystemDownloadManager* download_manager_;
+  // Removes the archives identified by |download_manager_ids| from downloads.
+  virtual void UnpublishArchives(
+      const std::vector<int64_t>& download_manager_ids) const = 0;
 };
 
 }  // namespace offline_pages
diff --git a/components/offline_pages/core/offline_page_archive_publisher_unittest.cc b/components/offline_pages/core/offline_page_archive_publisher_unittest.cc
deleted file mode 100644
index 3b20141..0000000
--- a/components/offline_pages/core/offline_page_archive_publisher_unittest.cc
+++ /dev/null
@@ -1,129 +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/offline_pages/core/offline_page_archive_publisher.h"
-
-#include "base/bind.h"
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/files/scoped_temp_dir.h"
-#include "base/memory/weak_ptr.h"
-#include "base/sequenced_task_runner.h"
-#include "base/test/test_simple_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "components/offline_pages/core/archive_manager.h"
-#include "components/offline_pages/core/model/offline_page_item_generator.h"
-#include "components/offline_pages/core/stub_system_download_manager.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-const int64_t kDownloadId = 42LL;
-}  // namespace
-
-namespace offline_pages {
-
-class OfflinePageArchivePublisherTest
-    : public testing::Test,
-      public base::SupportsWeakPtr<OfflinePageArchivePublisherTest> {
- public:
-  OfflinePageArchivePublisherTest()
-      : task_runner_(new base::TestSimpleTaskRunner),
-        task_runner_handle_(task_runner_),
-        weak_ptr_factory_(this) {}
-  ~OfflinePageArchivePublisherTest() override {}
-
-  SavePageCallback save_page_callback;
-
-  void SetUp() override;
-  void PumpLoop();
-
-  OfflinePageItemGenerator* page_generator() { return &page_generator_; }
-
-  const base::FilePath& temporary_dir_path() {
-    return temporary_dir_.GetPath();
-  }
-  const base::FilePath& private_archive_dir_path() {
-    return private_archive_dir_.GetPath();
-  }
-  const base::FilePath& public_archive_dir_path() {
-    return public_archive_dir_.GetPath();
-  }
-  const PublishArchiveResult& publish_archive_result() {
-    return publish_archive_result_;
-  }
-  scoped_refptr<base::SequencedTaskRunner> task_runner() {
-    return task_runner_;
-  }
-  base::WeakPtr<OfflinePageArchivePublisherTest> get_weak_ptr() {
-    return weak_ptr_factory_.GetWeakPtr();
-  }
-
-  void PublishArchiveDone(SavePageCallback save_page_callback,
-                          const OfflinePageItem& offline_page,
-                          PublishArchiveResult archive_result);
-
- private:
-  base::ScopedTempDir temporary_dir_;
-  base::ScopedTempDir private_archive_dir_;
-  base::ScopedTempDir public_archive_dir_;
-  OfflinePageItemGenerator page_generator_;
-  PublishArchiveResult publish_archive_result_;
-  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
-  base::ThreadTaskRunnerHandle task_runner_handle_;
-  base::WeakPtrFactory<OfflinePageArchivePublisherTest> weak_ptr_factory_;
-};
-
-void OfflinePageArchivePublisherTest::SetUp() {
-  ASSERT_TRUE(temporary_dir_.CreateUniqueTempDir());
-  ASSERT_TRUE(private_archive_dir_.CreateUniqueTempDir());
-  ASSERT_TRUE(public_archive_dir_.CreateUniqueTempDir());
-}
-
-void OfflinePageArchivePublisherTest::PublishArchiveDone(
-    SavePageCallback save_page_callback,
-    const OfflinePageItem& offline_page,
-    PublishArchiveResult archive_result) {
-  publish_archive_result_ = archive_result;
-}
-
-void OfflinePageArchivePublisherTest::PumpLoop() {
-  task_runner_->RunUntilIdle();
-}
-
-TEST_F(OfflinePageArchivePublisherTest, PublishArchive) {
-  auto archive_manager = std::make_unique<ArchiveManager>(
-      temporary_dir_path(), private_archive_dir_path(),
-      public_archive_dir_path(), task_runner());
-  auto download_manager =
-      std::make_unique<StubSystemDownloadManager>(kDownloadId, true);
-
-  OfflinePageArchivePublisher archiver(archive_manager.get(),
-                                       download_manager.get());
-
-  // Put an offline page into the private dir, adjust the FilePath.
-  page_generator()->SetArchiveDirectory(temporary_dir_path());
-  OfflinePageItem offline_page = page_generator()->CreateItemWithTempFile();
-  base::FilePath old_file_path = offline_page.file_path;
-  base::FilePath new_file_path =
-      public_archive_dir_path().Append(offline_page.file_path.BaseName());
-
-  archiver.PublishArchive(
-      offline_page, base::ThreadTaskRunnerHandle::Get(),
-      base::BindOnce(&OfflinePageArchivePublisherTest::PublishArchiveDone,
-                     get_weak_ptr(), std::move(save_page_callback)));
-  PumpLoop();
-
-  EXPECT_EQ(SavePageResult::SUCCESS, publish_archive_result().move_result);
-  EXPECT_EQ(kDownloadId, publish_archive_result().download_id);
-  // Check there is a file in the new location.
-  EXPECT_TRUE(public_archive_dir_path().IsParent(
-      publish_archive_result().new_file_path));
-  EXPECT_TRUE(base::PathExists(publish_archive_result().new_file_path));
-  // Check there is no longer a file in the old location.
-  EXPECT_FALSE(base::PathExists(old_file_path));
-}
-
-// TODO(petewil): Add test cases for move failed, and adding to ADM failed.
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/core/offline_page_test_archive_publisher.cc b/components/offline_pages/core/offline_page_test_archive_publisher.cc
index 54b884a..895b264 100644
--- a/components/offline_pages/core/offline_page_test_archive_publisher.cc
+++ b/components/offline_pages/core/offline_page_test_archive_publisher.cc
@@ -19,12 +19,14 @@
 
 OfflinePageTestArchivePublisher::OfflinePageTestArchivePublisher(
     ArchiveManager* archive_manager,
-    SystemDownloadManager* download_manager)
-    : OfflinePageArchivePublisher(archive_manager, download_manager),
-      expect_publish_archive_called_(false),
+    int64_t download_id_to_use)
+    : expect_publish_archive_called_(false),
       publish_archive_called_(false),
       archive_attempt_failure_(false),
-      use_verbatim_archive_path_(false) {}
+      use_verbatim_archive_path_(false),
+      download_id_(download_id_to_use),
+      last_removed_id_(0LL),
+      archive_manager_(archive_manager) {}
 
 OfflinePageTestArchivePublisher::~OfflinePageTestArchivePublisher() {
   if (expect_publish_archive_called_)
@@ -40,6 +42,7 @@
 
   if (archive_attempt_failure_) {
     publish_archive_result.move_result = SavePageResult::FILE_MOVE_FAILED;
+    publish_archive_result.download_id = 0LL;
   } else {
     publish_archive_result.move_result = SavePageResult::SUCCESS;
 
@@ -54,7 +57,7 @@
           archive_manager_->GetPublicArchivesDir().Append(
               offline_page.file_path.BaseName());
     }
-    publish_archive_result.download_id = 0;
+    publish_archive_result.download_id = download_id_;
   }
 
   background_task_runner->PostTask(
@@ -62,4 +65,10 @@
                                 std::move(publish_archive_result)));
 }
 
+void OfflinePageTestArchivePublisher::UnpublishArchives(
+    const std::vector<int64_t>& download_manager_ids) const {
+  if (!download_manager_ids.empty())
+    last_removed_id_ = download_manager_ids.back();
+}
+
 }  // namespace offline_pages
diff --git a/components/offline_pages/core/offline_page_test_archive_publisher.h b/components/offline_pages/core/offline_page_test_archive_publisher.h
index 9a8eb75..a570038 100644
--- a/components/offline_pages/core/offline_page_test_archive_publisher.h
+++ b/components/offline_pages/core/offline_page_test_archive_publisher.h
@@ -22,12 +22,11 @@
 namespace offline_pages {
 
 class ArchiveManager;
-class SystemDownloadManager;
 
 class OfflinePageTestArchivePublisher : public OfflinePageArchivePublisher {
  public:
   OfflinePageTestArchivePublisher(ArchiveManager* archive_manager,
-                                  SystemDownloadManager* download_manager);
+                                  int64_t download_id_to_use);
   ~OfflinePageTestArchivePublisher() override;
 
   void PublishArchive(
@@ -35,6 +34,9 @@
       const scoped_refptr<base::SequencedTaskRunner>& background_task_runner,
       PublishArchiveDoneCallback publish_done_callback) const override;
 
+  void UnpublishArchives(
+      const std::vector<int64_t>& download_manager_ids) const override;
+
   void set_archive_attempt_failure(bool fail) {
     archive_attempt_failure_ = fail;
   }
@@ -45,11 +47,17 @@
 
   void use_verbatim_archive_path(bool use) { use_verbatim_archive_path_ = use; }
 
+  int64_t last_removed_id() const { return last_removed_id_; }
+
  private:
   bool expect_publish_archive_called_;
   mutable bool publish_archive_called_;
   bool archive_attempt_failure_;
   bool use_verbatim_archive_path_;
+  int64_t download_id_;
+  mutable int64_t last_removed_id_;
+
+  ArchiveManager* archive_manager_;
 };
 
 }  // namespace offline_pages
diff --git a/components/offline_pages/core/stub_system_download_manager.cc b/components/offline_pages/core/stub_system_download_manager.cc
deleted file mode 100644
index 2b8562f..0000000
--- a/components/offline_pages/core/stub_system_download_manager.cc
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/core/stub_system_download_manager.h"
-
-namespace offline_pages {
-
-StubSystemDownloadManager::StubSystemDownloadManager(int64_t id_to_use,
-                                                     bool installed)
-    : download_id_(id_to_use), last_removed_id_(0), installed_(installed) {}
-
-StubSystemDownloadManager::~StubSystemDownloadManager() {}
-
-bool StubSystemDownloadManager::IsDownloadManagerInstalled() {
-  return installed_;
-}
-
-int64_t StubSystemDownloadManager::AddCompletedDownload(
-    const std::string& title,
-    const std::string& description,
-    const std::string& path,
-    int64_t length,
-    const std::string& uri,
-    const std::string& referer) {
-  title_ = title;
-  description_ = description;
-  path_ = path;
-  length_ = length;
-  uri_ = uri;
-  referer_ = referer;
-
-  return download_id_;
-}
-
-int StubSystemDownloadManager::Remove(
-    const std::vector<int64_t>& android_download_manager_ids) {
-  int count = static_cast<int>(android_download_manager_ids.size());
-  if (count > 0)
-    last_removed_id_ = android_download_manager_ids[count - 1];
-  return count;
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/core/stub_system_download_manager.h b/components/offline_pages/core/stub_system_download_manager.h
deleted file mode 100644
index dab2194..0000000
--- a/components/offline_pages/core/stub_system_download_manager.h
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_CORE_STUB_SYSTEM_DOWNLOAD_MANAGER_H_
-#define COMPONENTS_OFFLINE_PAGES_CORE_STUB_SYSTEM_DOWNLOAD_MANAGER_H_
-
-#include "components/offline_pages/core/system_download_manager.h"
-
-namespace offline_pages {
-
-// Stub replacement for the DownloadManager to be used by unit tests.
-class StubSystemDownloadManager : public SystemDownloadManager {
- public:
-  StubSystemDownloadManager(int64_t download_id, bool installed);
-  ~StubSystemDownloadManager() override;
-
-  bool IsDownloadManagerInstalled() override;
-
-  int64_t AddCompletedDownload(const std::string& title,
-                               const std::string& description,
-                               const std::string& path,
-                               int64_t length,
-                               const std::string& uri,
-                               const std::string& referer) override;
-
-  int Remove(const std::vector<int64_t>& android_download_manager_ids) override;
-
-  // Accessors for the test to use to check passed parameters.
-  std::string title() { return title_; }
-  std::string description() { return description_; }
-  std::string path() { return path_; }
-  std::string uri() { return uri_; }
-  std::string referer() { return referer_; }
-  long length() { return length_; }
-  void set_installed(bool installed) { installed_ = installed; }
-  void set_download_id(int64_t download_id) { download_id_ = download_id; }
-  int64_t last_removed_id() { return last_removed_id_; }
-
- private:
-  int64_t download_id_;
-  int64_t last_removed_id_;
-  std::string title_;
-  std::string description_;
-  std::string path_;
-  std::string uri_;
-  std::string referer_;
-  long length_;
-  bool installed_;
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_CORE_STUB_SYSTEM_DOWNLOAD_MANAGER_H_
diff --git a/components/offline_pages/core/system_download_manager.h b/components/offline_pages/core/system_download_manager.h
deleted file mode 100644
index b6520ac..0000000
--- a/components/offline_pages/core/system_download_manager.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_CORE_SYSTEM_DOWNLOAD_MANAGER_H_
-#define COMPONENTS_OFFLINE_PAGES_CORE_SYSTEM_DOWNLOAD_MANAGER_H_
-
-#include <string>
-#include <vector>
-
-namespace offline_pages {
-
-// Interface of a class responsible for interacting with the Android download
-// manager
-class SystemDownloadManager {
- public:
-  SystemDownloadManager() = default;
-  virtual ~SystemDownloadManager() = default;
-
-  // Returns true if a system download manager is available on this platform.
-  virtual bool IsDownloadManagerInstalled() = 0;
-
-  // Returns the download manager ID of the download, which we will place in the
-  // offline pages database as part of the offline page item.
-  // TODO(petewil): it might make sense to move all these params into a struct.
-  virtual int64_t AddCompletedDownload(const std::string& title,
-                                       const std::string& description,
-                                       const std::string& path,
-                                       int64_t length,
-                                       const std::string& uri,
-                                       const std::string& referer) = 0;
-
-  // Returns the number of pages removed.
-  virtual int Remove(
-      const std::vector<int64_t>& android_download_manager_ids) = 0;
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_CORE_SYSTEM_DOWNLOAD_MANAGER_H_
diff --git a/components/omnibox/browser/autocomplete_classifier.cc b/components/omnibox/browser/autocomplete_classifier.cc
index 5147aed..b81537d 100644
--- a/components/omnibox/browser/autocomplete_classifier.cc
+++ b/components/omnibox/browser/autocomplete_classifier.cc
@@ -47,6 +47,9 @@
       (base::FeatureList::IsEnabled(omnibox::kDocumentProvider)
            ? AutocompleteProvider::TYPE_DOCUMENT
            : 0) |
+      (base::FeatureList::IsEnabled(omnibox::kOnDeviceHeadProvider)
+           ? AutocompleteProvider::TYPE_ON_DEVICE_HEAD
+           : 0) |
       AutocompleteProvider::TYPE_BOOKMARK | AutocompleteProvider::TYPE_BUILTIN |
       AutocompleteProvider::TYPE_HISTORY_QUICK |
       AutocompleteProvider::TYPE_HISTORY_URL |
diff --git a/components/omnibox/browser/autocomplete_controller.cc b/components/omnibox/browser/autocomplete_controller.cc
index 40100e56..304f3c82 100644
--- a/components/omnibox/browser/autocomplete_controller.cc
+++ b/components/omnibox/browser/autocomplete_controller.cc
@@ -34,6 +34,7 @@
 #include "components/omnibox/browser/history_quick_provider.h"
 #include "components/omnibox/browser/history_url_provider.h"
 #include "components/omnibox/browser/keyword_provider.h"
+#include "components/omnibox/browser/on_device_head_provider.h"
 #include "components/omnibox/browser/search_provider.h"
 #include "components/omnibox/browser/shortcuts_provider.h"
 #include "components/omnibox/browser/zero_suggest_provider.h"
@@ -222,6 +223,7 @@
       keyword_provider_(nullptr),
       search_provider_(nullptr),
       zero_suggest_provider_(nullptr),
+      on_device_head_provider_(nullptr),
       stop_timer_duration_(OmniboxFieldTrial::StopTimerFieldTrialDuration()),
       done_(true),
       in_start_(false),
@@ -278,6 +280,11 @@
     document_provider_ = DocumentProvider::Create(provider_client_.get(), this);
     providers_.push_back(document_provider_);
   }
+  if (provider_types & AutocompleteProvider::TYPE_ON_DEVICE_HEAD) {
+    on_device_head_provider_ =
+        OnDeviceHeadProvider::Create(provider_client_.get(), this);
+    providers_.push_back(on_device_head_provider_);
+  }
   if (provider_types & AutocompleteProvider::TYPE_CLIPBOARD) {
 #if !defined(OS_IOS)
     // On iOS, a global ClipboardRecentContent should've been created by now
diff --git a/components/omnibox/browser/autocomplete_controller.h b/components/omnibox/browser/autocomplete_controller.h
index 1e34feb1..691fffd 100644
--- a/components/omnibox/browser/autocomplete_controller.h
+++ b/components/omnibox/browser/autocomplete_controller.h
@@ -29,6 +29,7 @@
 class SearchProvider;
 class TemplateURLService;
 class ZeroSuggestProvider;
+class OnDeviceHeadProvider;
 
 // The AutocompleteController is the center of the autocomplete system.  A
 // class creates an instance of the controller, which in turn creates a set of
@@ -237,6 +238,8 @@
 
   ZeroSuggestProvider* zero_suggest_provider_;
 
+  OnDeviceHeadProvider* on_device_head_provider_;
+
   // Input passed to Start.
   AutocompleteInput input_;
 
diff --git a/components/omnibox/browser/autocomplete_provider.h b/components/omnibox/browser/autocomplete_provider.h
index 064273e..e862204 100644
--- a/components/omnibox/browser/autocomplete_provider.h
+++ b/components/omnibox/browser/autocomplete_provider.h
@@ -65,7 +65,8 @@
 // Search Secondary Provider (past query in history)                   |  200*
 // Search Secondary Provider (navigational suggestion)                 |  150++
 // Search Secondary Provider (suggestion)                              |  100++
-// Non Personalized On Device Head Suggest Provider                    |   99--
+// Non Personalized On Device Head Suggest Provider                    |    *
+//                  (default value 99--, can be changed by Finch)
 // Document Suggestions (*experimental): value controlled by Finch     |    *
 //
 // URL input type:
@@ -86,6 +87,7 @@
 // Search Secondary Provider (past query in history)                   |  200*
 // Search Secondary Provider (navigational suggestion)                 |  150++
 // Search Secondary Provider (suggestion)                              |  100++
+// Non Personalized On Device Head Suggest Provider                    |   99--
 //
 // QUERY input type:
 // --------------------------------------------------------------------|-----
@@ -104,7 +106,8 @@
 // Search Secondary Provider (past query in history)                   |  200*
 // Search Secondary Provider (navigational suggestion)                 |  150++
 // Search Secondary Provider (suggestion)                              |  100++
-// Non Personalized On Device Head Suggest Provider                    |   99--
+// Non Personalized On Device Head Suggest Provider                    |    *
+//                  (default value 99--, can be changed by Finch)
 //
 // (A search keyword is a keyword with a replacement string; a bookmark keyword
 // is a keyword with no replacement string, that is, a shortcut for a URL.)
diff --git a/components/omnibox/browser/autocomplete_provider_client.h b/components/omnibox/browser/autocomplete_provider_client.h
index 6e175c2..9dccc0b 100644
--- a/components/omnibox/browser/autocomplete_provider_client.h
+++ b/components/omnibox/browser/autocomplete_provider_client.h
@@ -145,12 +145,8 @@
   virtual void StartServiceWorker(const GURL& destination_url) {}
 
   // Called by |controller| when its results have changed and all providers are
-  // done processing the autocomplete request. At the //chrome level, this
-  // callback results in firing the
-  // NOTIFICATION_AUTOCOMPLETE_CONTROLLER_RESULT_READY notification.
-  // TODO(blundell): Remove the //chrome-level notification entirely in favor of
-  // having AutocompleteController expose a CallbackList that //chrome-level
-  // listeners add themselves to, and then kill this method.
+  // done processing the autocomplete request. Chrome ignores this. It's only
+  // used in components unit tests. TODO(blundell): remove it.
   virtual void OnAutocompleteControllerResultReady(
       AutocompleteController* controller) {}
 
diff --git a/components/omnibox/browser/document_provider.cc b/components/omnibox/browser/document_provider.cc
index 95df411..4782b32 100644
--- a/components/omnibox/browser/document_provider.cc
+++ b/components/omnibox/browser/document_provider.cc
@@ -551,7 +551,7 @@
   // https://docs.google.com/[a/domain.tld/]document/d/(id)/[...]
   // https://docs.google.com/[a/domain.tld/]spreadsheets/d/(id)/edit#gid=12345
   // https://docs.google.com/[a/domain.tld/]presentation/d/(id)/edit#slide=id.g12345a_0_26
-  // https://www.google.com/url?[...]url=https://drive.google.com/a/domain.tld/open?id%3D1fkxx6KYRYnSqljThxShJVliQJLdKzuJBnzogzL3n8rE&[...]
+  // https://www.google.com/url?[...]url=https://drive.google.com/a/domain.tld/open?id%3D(id)[&...]
   // where id is comprised of characters in [0-9A-Za-z\-_] = [\w\-]
   std::string id;
 
diff --git a/components/omnibox/browser/document_provider_unittest.cc b/components/omnibox/browser/document_provider_unittest.cc
index 9106d0a..5f6b5eacf 100644
--- a/components/omnibox/browser/document_provider_unittest.cc
+++ b/components/omnibox/browser/document_provider_unittest.cc
@@ -5,6 +5,7 @@
 #include "components/omnibox/browser/document_provider.h"
 
 #include "base/json/json_reader.h"
+#include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/time/time_to_iso8601.h"
@@ -23,6 +24,18 @@
 
 namespace {
 
+const std::string SAMPLE_ORIGINAL_URL =
+    "https://www.google.com/url?_placeholder_url=https://drive.google.com/a/"
+    "domain.tld/open?id%3D_0123_ID_4567_&_placeholder_";
+
+const std::string SAMPLE_STRIPPED_URL =
+#if defined(OS_IOS) || defined(OS_ANDROID)
+    SAMPLE_ORIGINAL_URL
+#else
+    "https://drive.google.com/open?id=_0123_ID_4567_"
+#endif
+    ;
+
 using testing::Return;
 
 class FakeAutocompleteProviderClient : public MockAutocompleteProviderClient {
@@ -228,20 +241,22 @@
 }
 
 TEST_F(DocumentProviderTest, ParseDocumentSearchResults) {
-  const char kGoodJSONResponse[] = R"({
+  const std::string kGoodJSONResponse = base::StringPrintf(
+      R"({
       "results": [
         {
           "title": "Document 1",
           "url": "https://documentprovider.tld/doc?id=1",
           "score": 1234,
-          "originalUrl": "https://shortened.url"
+          "originalUrl": "%s"
         },
         {
           "title": "Document 2",
           "url": "https://documentprovider.tld/doc?id=2"
         }
       ]
-     })";
+     })",
+      SAMPLE_ORIGINAL_URL.c_str());
 
   base::Optional<base::Value> response =
       base::JSONReader::Read(kGoodJSONResponse);
@@ -256,7 +271,7 @@
   EXPECT_EQ(matches[0].destination_url,
             GURL("https://documentprovider.tld/doc?id=1"));
   EXPECT_EQ(matches[0].relevance, 1234);  // Server-specified.
-  EXPECT_EQ(matches[0].stripped_destination_url, GURL("https://shortened.url"));
+  EXPECT_EQ(matches[0].stripped_destination_url, GURL(SAMPLE_STRIPPED_URL));
 
   EXPECT_EQ(matches[1].contents, base::ASCIIToUTF16("Document 2"));
   EXPECT_EQ(matches[1].destination_url,
@@ -270,13 +285,14 @@
 TEST_F(DocumentProviderTest, ProductDescriptionStringsAndAccessibleLabels) {
   // Dates are kept > 1 year in the past since
   // See comments for GenerateLastModifiedString in this file for references.
-  const char kGoodJSONResponseWithMimeTypes[] = R"({
+  const std::string kGoodJSONResponseWithMimeTypes = base::StringPrintf(
+      R"({
       "results": [
         {
           "title": "My Google Doc",
           "url": "https://documentprovider.tld/doc?id=1",
           "score": 999,
-          "originalUrl": "https://shortened.url",
+          "originalUrl": "%s",
           "metadata": {
             "mimeType": "application/vnd.google-apps.document",
             "updateTime": "Mon, 15 Oct 2007 19:45:00 GMT"
@@ -300,7 +316,8 @@
           }
         }
       ]
-     })";
+     })",
+      SAMPLE_ORIGINAL_URL.c_str());
 
   base::Optional<base::Value> response =
       base::JSONReader::Read(kGoodJSONResponseWithMimeTypes);
@@ -335,13 +352,14 @@
 }
 
 TEST_F(DocumentProviderTest, ParseDocumentSearchResultsBreakTies) {
-  const char kGoodJSONResponseWithTies[] = R"({
+  const std::string kGoodJSONResponseWithTies = base::StringPrintf(
+      R"({
       "results": [
         {
           "title": "Document 1",
           "url": "https://documentprovider.tld/doc?id=1",
           "score": 1234,
-          "originalUrl": "https://shortened.url"
+          "originalUrl": "%s"
         },
         {
           "title": "Document 2",
@@ -354,7 +372,8 @@
           "url": "https://documentprovider.tld/doc?id=3"
         }
       ]
-     })";
+     })",
+      SAMPLE_ORIGINAL_URL.c_str());
 
   base::Optional<base::Value> response =
       base::JSONReader::Read(kGoodJSONResponseWithTies);
@@ -371,7 +390,7 @@
   EXPECT_EQ(matches[0].destination_url,
             GURL("https://documentprovider.tld/doc?id=1"));
   EXPECT_EQ(matches[0].relevance, 1234);  // As the server specified.
-  EXPECT_EQ(matches[0].stripped_destination_url, GURL("https://shortened.url"));
+  EXPECT_EQ(matches[0].stripped_destination_url, GURL(SAMPLE_STRIPPED_URL));
 
   EXPECT_EQ(matches[1].contents, base::ASCIIToUTF16("Document 2"));
   EXPECT_EQ(matches[1].destination_url,
@@ -389,13 +408,14 @@
 }
 
 TEST_F(DocumentProviderTest, ParseDocumentSearchResultsBreakTiesCascade) {
-  const char kGoodJSONResponseWithTies[] = R"({
+  const std::string kGoodJSONResponseWithTies = base::StringPrintf(
+      R"({
       "results": [
         {
           "title": "Document 1",
           "url": "https://documentprovider.tld/doc?id=1",
           "score": 1234,
-          "originalUrl": "https://shortened.url"
+          "originalUrl": "%s"
         },
         {
           "title": "Document 2",
@@ -408,7 +428,8 @@
           "url": "https://documentprovider.tld/doc?id=3"
         }
       ]
-     })";
+     })",
+      SAMPLE_ORIGINAL_URL.c_str());
 
   base::Optional<base::Value> response =
       base::JSONReader::Read(kGoodJSONResponseWithTies);
@@ -425,7 +446,7 @@
   EXPECT_EQ(matches[0].destination_url,
             GURL("https://documentprovider.tld/doc?id=1"));
   EXPECT_EQ(matches[0].relevance, 1234);  // As the server specified.
-  EXPECT_EQ(matches[0].stripped_destination_url, GURL("https://shortened.url"));
+  EXPECT_EQ(matches[0].stripped_destination_url, GURL(SAMPLE_STRIPPED_URL));
 
   EXPECT_EQ(matches[1].contents, base::ASCIIToUTF16("Document 2"));
   EXPECT_EQ(matches[1].destination_url,
@@ -445,13 +466,14 @@
 }
 
 TEST_F(DocumentProviderTest, ParseDocumentSearchResultsBreakTiesZeroLimit) {
-  const char kGoodJSONResponseWithTies[] = R"({
+  const std::string kGoodJSONResponseWithTies = base::StringPrintf(
+      R"({
       "results": [
         {
           "title": "Document 1",
           "url": "https://documentprovider.tld/doc?id=1",
           "score": 1,
-          "originalUrl": "https://shortened.url"
+          "originalUrl": "%s"
         },
         {
           "title": "Document 2",
@@ -464,7 +486,8 @@
           "url": "https://documentprovider.tld/doc?id=3"
         }
       ]
-     })";
+     })",
+      SAMPLE_ORIGINAL_URL.c_str());
 
   base::Optional<base::Value> response =
       base::JSONReader::Read(kGoodJSONResponseWithTies);
@@ -481,7 +504,7 @@
   EXPECT_EQ(matches[0].destination_url,
             GURL("https://documentprovider.tld/doc?id=1"));
   EXPECT_EQ(matches[0].relevance, 1);  // As the server specified.
-  EXPECT_EQ(matches[0].stripped_destination_url, GURL("https://shortened.url"));
+  EXPECT_EQ(matches[0].stripped_destination_url, GURL(SAMPLE_STRIPPED_URL));
 
   EXPECT_EQ(matches[1].contents, base::ASCIIToUTF16("Document 2"));
   EXPECT_EQ(matches[1].destination_url,
diff --git a/components/omnibox/browser/on_device_head_provider.cc b/components/omnibox/browser/on_device_head_provider.cc
index 68328ee1..c4d9d0a 100644
--- a/components/omnibox/browser/on_device_head_provider.cc
+++ b/components/omnibox/browser/on_device_head_provider.cc
@@ -9,13 +9,16 @@
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/i18n/case_conversion.h"
+#include "base/metrics/field_trial_params.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/post_task.h"
 #include "base/trace_event/trace_event.h"
 #include "components/omnibox/browser/autocomplete_provider_listener.h"
 #include "components/omnibox/browser/base_search_provider.h"
+#include "components/omnibox/common/omnibox_features.h"
 #include "components/search_engines/search_terms_data.h"
 #include "components/search_engines/template_url_service.h"
+#include "third_party/metrics_proto/omnibox_input_type.pb.h"
 
 namespace {
 const int kBaseRelevance = 99;
@@ -127,10 +130,21 @@
     // request.
     std::unique_ptr<OnDeviceHeadProviderParams> params = base::WrapUnique(
         new OnDeviceHeadProviderParams(on_device_search_request_id_, input));
-    task_runner_->PostTask(
+
+    // Since the On Device provider usually runs much faster than online
+    // providers, it will be very likely users will see on device suggestions
+    // first and then the Omnibox UI gets refreshed to show suggestions fetched
+    // from server, if we issue both requests simultaneously.
+    // Therefore, we might want to delay the On Device suggest requests (and
+    // also apply a timeout to search default loader) to mitigate this issue.
+    int delay = base::GetFieldTrialParamByFeatureAsInt(
+        omnibox::kOnDeviceHeadProvider, "DelayOnDeviceHeadSuggestRequestMs", 0);
+    task_runner_->PostDelayedTask(
         FROM_HERE,
         base::BindOnce(&OnDeviceHeadProvider::DoSearch,
-                       weak_ptr_factory_.GetWeakPtr(), std::move(params)));
+                       weak_ptr_factory_.GetWeakPtr(), std::move(params)),
+        delay > 0 ? base::TimeDelta::FromMilliseconds(delay)
+                  : base::TimeDelta());
   }
 }
 
@@ -196,7 +210,12 @@
 
   if (IsDefaultSearchProviderGoogle(template_url_service) && !params->failed) {
     matches_.clear();
-    int relevance = kBaseRelevance;
+    int relevance =
+        (params->input.type() != metrics::OmniboxInputType::URL)
+            ? base::GetFieldTrialParamByFeatureAsInt(
+                  omnibox::kOnDeviceHeadProvider,
+                  "OnDeviceSuggestMaxScoreForNonUrlInput", kBaseRelevance)
+            : kBaseRelevance;
     for (const auto& item : params->suggestions) {
       matches_.push_back(BaseSearchProvider::CreateOnDeviceSearchSuggestion(
           /*autocomplete_provider=*/this, /*input=*/params->input,
diff --git a/components/omnibox/browser/search_provider.cc b/components/omnibox/browser/search_provider.cc
index cea23458..0e2520b 100644
--- a/components/omnibox/browser/search_provider.cc
+++ b/components/omnibox/browser/search_provider.cc
@@ -16,6 +16,7 @@
 #include "base/i18n/break_iterator.h"
 #include "base/i18n/case_conversion.h"
 #include "base/json/json_string_value_serializer.h"
+#include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
 #include "base/rand_util.h"
@@ -96,6 +97,14 @@
   return false;
 }
 
+bool IsSearchEngineGoogle(const TemplateURL* template_url,
+                          const AutocompleteProviderClient* client) {
+  return template_url && client &&
+         template_url->GetEngineType(
+             client->GetTemplateURLService()->search_terms_data()) ==
+             SEARCH_ENGINE_GOOGLE;
+}
+
 }  // namespace
 
 // SearchProvider::Providers --------------------------------------------------
@@ -502,11 +511,8 @@
   // Record response time for suggest requests sent to Google.  We care
   // only about the common case: the Google default provider used in
   // non-keyword mode.
-  const TemplateURL* default_url = providers_.GetDefaultProviderURL();
-  if (!is_keyword && default_url &&
-      (default_url->GetEngineType(
-          client()->GetTemplateURLService()->search_terms_data()) ==
-       SEARCH_ENGINE_GOOGLE)) {
+  if (!is_keyword &&
+      IsSearchEngineGoogle(providers_.GetDefaultProviderURL(), client())) {
     const base::TimeDelta elapsed_time =
         base::TimeTicks::Now() - time_suggest_request_sent_;
     if (success) {
@@ -615,11 +621,21 @@
   time_suggest_request_sent_ = base::TimeTicks::Now();
 
   if (!query_is_private) {
-    default_loader_ =
-        CreateSuggestLoader(providers_.GetDefaultProviderURL(), input_);
+    int timeout_ms = 0;
+    // Consider explicitly setting a timeout for requests sent to Google when
+    // On Device Head provider is enabled.
+    if (IsSearchEngineGoogle(providers_.GetDefaultProviderURL(), client())) {
+      timeout_ms = base::GetFieldTrialParamByFeatureAsInt(
+          omnibox::kOnDeviceHeadProvider,
+          "SearchProviderDefaultLoaderTimeoutMs", 0);
+    }
+    default_loader_ = CreateSuggestLoader(
+        providers_.GetDefaultProviderURL(), input_,
+        timeout_ms > 0 ? base::TimeDelta::FromMilliseconds(timeout_ms)
+                       : base::TimeDelta());
   }
-  keyword_loader_ =
-      CreateSuggestLoader(providers_.GetKeywordProviderURL(), keyword_input_);
+  keyword_loader_ = CreateSuggestLoader(providers_.GetKeywordProviderURL(),
+                                        keyword_input_, base::TimeDelta());
 
   // Both the above can fail if the providers have been modified or deleted
   // since the query began.
@@ -865,7 +881,8 @@
 
 std::unique_ptr<network::SimpleURLLoader> SearchProvider::CreateSuggestLoader(
     const TemplateURL* template_url,
-    const AutocompleteInput& input) {
+    const AutocompleteInput& input,
+    const base::TimeDelta& timeout) {
   if (!template_url || template_url->suggestions_url().empty())
     return nullptr;
 
@@ -968,6 +985,8 @@
 
   std::unique_ptr<network::SimpleURLLoader> loader =
       network::SimpleURLLoader::Create(std::move(request), traffic_annotation);
+  if (timeout > base::TimeDelta())
+    loader->SetTimeoutDuration(timeout);
   loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
       client()->GetURLLoaderFactory().get(),
       base::BindOnce(&SearchProvider::OnURLLoadComplete, base::Unretained(this),
diff --git a/components/omnibox/browser/search_provider.h b/components/omnibox/browser/search_provider.h
index f4f9090..3f8a679 100644
--- a/components/omnibox/browser/search_provider.h
+++ b/components/omnibox/browser/search_provider.h
@@ -264,10 +264,12 @@
 
   // Starts a new SimpleURLLoader requesting suggest results from
   // |template_url|; callers own the returned SimpleURLLoader, which is NULL for
-  // invalid providers.
+  // invalid providers. Note the request will never time out unless the given
+  // |timeout| is greater than 0.
   std::unique_ptr<network::SimpleURLLoader> CreateSuggestLoader(
       const TemplateURL* template_url,
-      const AutocompleteInput& input);
+      const AutocompleteInput& input,
+      const base::TimeDelta& timeout);
 
   // Converts the parsed results to a set of AutocompleteMatches, |matches_|.
   void ConvertResultsToAutocompleteMatches();
diff --git a/components/omnibox/common/omnibox_features.cc b/components/omnibox/common/omnibox_features.cc
index d2dc995..8ac55bf 100644
--- a/components/omnibox/common/omnibox_features.cc
+++ b/components/omnibox/common/omnibox_features.cc
@@ -4,6 +4,8 @@
 
 #include "components/omnibox/common/omnibox_features.h"
 
+#include "build/build_config.h"
+
 namespace omnibox {
 
 // Feature used to hide the scheme from steady state URLs displayed in the
@@ -255,8 +257,14 @@
 // Feature used to dedupe Google Drive URLs between different formats.
 // OmniboxDocumentProvider arms may wish to enable this, though it may also be
 // run on its own.
-const base::Feature kDedupeGoogleDriveURLs{"OmniboxDedupeGoogleDriveURLs",
-                                           base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kDedupeGoogleDriveURLs {
+  "OmniboxDedupeGoogleDriveURLs",
+#if defined(OS_IOS) || defined(OS_ANDROID)
+      base::FEATURE_DISABLED_BY_DEFAULT
+#else
+      base::FEATURE_ENABLED_BY_DEFAULT
+#endif
+};
 
 // Feature to replace the standard ZeroSuggest with icons for most visited sites
 // and collections (bookmarks, history, recent tabs, reading list). Only
@@ -288,4 +296,9 @@
 const base::Feature kZeroSuggestionsOnNTP{"OmniboxZeroSuggestionsOnNTP",
                                           base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Feature to provide non personalized head search suggestion from a compact
+// on device model.
+const base::Feature kOnDeviceHeadProvider{"OmniboxOnDeviceHeadProvider",
+                                          base::FEATURE_DISABLED_BY_DEFAULT};
+
 }  // namespace omnibox
diff --git a/components/omnibox/common/omnibox_features.h b/components/omnibox/common/omnibox_features.h
index d5323a4..d95912c 100644
--- a/components/omnibox/common/omnibox_features.h
+++ b/components/omnibox/common/omnibox_features.h
@@ -46,6 +46,7 @@
 extern const base::Feature kUIExperimentShowPlaceholderWhenCaretShowing;
 extern const base::Feature kSpeculativeServiceWorkerStartOnQueryInput;
 extern const base::Feature kDocumentProvider;
+extern const base::Feature kOnDeviceHeadProvider;
 extern const base::Feature kDedupeGoogleDriveURLs;
 extern const base::Feature kOmniboxPopupShortcutIconsInZeroState;
 extern const base::Feature kOmniboxMaterialDesignWeatherIcons;
diff --git a/components/payments/content/payment_event_response_util.cc b/components/payments/content/payment_event_response_util.cc
index eff4a35..b7de46a 100644
--- a/components/payments/content/payment_event_response_util.cc
+++ b/components/payments/content/payment_event_response_util.cc
@@ -36,6 +36,8 @@
       return errors::kPaymentEventBrowserError;
     case mojom::PaymentEventResponseType::PAYMENT_EVENT_TIMEOUT:
       return errors::kPaymentEventTimeout;
+    case mojom::PaymentEventResponseType::PAYMENT_HANDLER_INSECURE_NAVIGATION:
+      return errors::kPaymentHandlerInsecureNavigation;
   }
 }
 
diff --git a/components/payments/content/payment_request_state.cc b/components/payments/content/payment_request_state.cc
index 1304726..361769d 100644
--- a/components/payments/content/payment_request_state.cc
+++ b/components/payments/content/payment_request_state.cc
@@ -10,12 +10,14 @@
 
 #include "base/bind.h"
 #include "base/feature_list.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/autofill/core/browser/autofill_data_util.h"
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/autofill/core/browser/geo/autofill_country.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
+#include "components/autofill/core/browser/validation.h"
 #include "components/payments/content/content_payment_request_delegate.h"
 #include "components/payments/content/payment_manifest_web_data_service.h"
 #include "components/payments/content/payment_response_helper.h"
@@ -514,7 +516,10 @@
       profile_comparator()->IsShippingComplete(shipping_profiles_[0])) {
     selected_shipping_profile_ = shipping_profiles()[0];
   }
-
+  // Record the missing required fields (if any) of the most complete shipping
+  // profile.
+  profile_comparator()->RecordMissingFieldsOfShippingProfile(
+      shipping_profiles().empty() ? nullptr : shipping_profiles()[0]);
   UpdateIsReadyToPayAndNotifyObservers();
 }
 
@@ -578,6 +583,11 @@
       profile_comparator()->IsContactInfoComplete(contact_profiles_[0]))
     selected_contact_profile_ = contact_profiles()[0];
 
+  // Record the missing required fields (if any) of the most complete contact
+  // profile.
+  profile_comparator()->RecordMissingFieldsOfContactProfile(
+      contact_profiles().empty() ? nullptr : contact_profiles()[0]);
+
   // TODO(crbug.com/702063): Change this code to prioritize instruments by use
   // count and other means, and implement a way to modify this function's return
   // value.
@@ -593,16 +603,33 @@
                              ? nullptr
                              : first_complete_instrument->get();
 
-  SelectDefaultShippingAddressAndNotifyObservers();
+  // Record the missing required payment fields when no complete payment
+  // info exists.
+  if (instruments.empty()) {
+    if (spec_->supports_basic_card()) {
+      // All fields are missing when basic-card is requested but no card exits.
+      base::UmaHistogramSparse("PaymentRequest.MissingPaymentFields",
+                               autofill::CREDIT_CARD_EXPIRED |
+                                   autofill::CREDIT_CARD_NO_CARDHOLDER |
+                                   autofill::CREDIT_CARD_NO_NUMBER |
+                                   autofill::CREDIT_CARD_NO_BILLING_ADDRESS);
+    }
+  } else if (!selected_instrument_) {
+    // selected_instrument_ == false means no complete payment instrument is
+    // added to the available_instruments list. Since SW based payment
+    // instruments are always complete, selected_instrument_ == false also means
+    // all instruments added to the list are autofill based.
+    DCHECK_EQ(instruments[0]->type(), PaymentInstrument::Type::AUTOFILL);
+    // Record the missing info of the first available autofill instrument.
+    static_cast<const AutofillPaymentInstrument*>(instruments[0].get())
+        ->RecordMissingFieldsForInstrument();
+  }
 
-  bool has_complete_instrument =
-      available_instruments().empty()
-          ? false
-          : available_instruments()[0]->IsCompleteForPayment();
+  SelectDefaultShippingAddressAndNotifyObservers();
 
   journey_logger_->SetNumberOfSuggestionsShown(
       JourneyLogger::Section::SECTION_PAYMENT_METHOD,
-      available_instruments().size(), has_complete_instrument);
+      available_instruments().size(), selected_instrument_);
 }
 
 void PaymentRequestState::UpdateIsReadyToPayAndNotifyObservers() {
diff --git a/components/payments/content/service_worker_payment_instrument.cc b/components/payments/content/service_worker_payment_instrument.cc
index d94620d..484eba9 100644
--- a/components/payments/content/service_worker_payment_instrument.cc
+++ b/components/payments/content/service_worker_payment_instrument.cc
@@ -248,7 +248,8 @@
 void ServiceWorkerPaymentInstrument::OnPaymentAppWindowClosed() {
   delegate_ = nullptr;
   content::PaymentAppProvider::GetInstance()->OnClosingOpenedWindow(
-      browser_context_);
+      browser_context_,
+      mojom::PaymentEventResponseType::PAYMENT_HANDLER_WINDOW_CLOSING);
 }
 
 mojom::PaymentRequestEventDataPtr
diff --git a/components/payments/core/autofill_payment_instrument.cc b/components/payments/core/autofill_payment_instrument.cc
index b5cb8b6..8aecac5 100644
--- a/components/payments/core/autofill_payment_instrument.cc
+++ b/components/payments/core/autofill_payment_instrument.cc
@@ -9,6 +9,7 @@
 
 #include "base/bind.h"
 #include "base/json/json_writer.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/stl_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
@@ -85,6 +86,15 @@
          autofill::CREDIT_CARD_EXPIRED;
 }
 
+void AutofillPaymentInstrument::RecordMissingFieldsForInstrument() const {
+  if (IsCompleteForPayment())
+    return;
+
+  base::UmaHistogramSparse("PaymentRequest.MissingPaymentFields",
+                           autofill::GetCompletionStatusForCard(
+                               credit_card_, app_locale_, billing_profiles_));
+}
+
 bool AutofillPaymentInstrument::IsExactlyMatchingMerchantRequest() const {
   return matches_merchant_card_type_exactly_;
 }
diff --git a/components/payments/core/autofill_payment_instrument.h b/components/payments/core/autofill_payment_instrument.h
index b73b560..b39a01ac 100644
--- a/components/payments/core/autofill_payment_instrument.h
+++ b/components/payments/core/autofill_payment_instrument.h
@@ -64,6 +64,8 @@
       const base::string16& cvc) override;
   void OnFullCardRequestFailed() override;
 
+  void RecordMissingFieldsForInstrument() const;
+
   autofill::CreditCard* credit_card() { return &credit_card_; }
 
   const std::string& method_name() const { return method_name_; }
diff --git a/components/payments/core/error_strings.cc b/components/payments/core/error_strings.cc
index 4c8831a..b228932 100644
--- a/components/payments/core/error_strings.cc
+++ b/components/payments/core/error_strings.cc
@@ -45,6 +45,7 @@
 const char kPaymentEventRejected[] = "Payment handler rejected the promise passed into PaymentRequestEvent.respondWith(). This is how payment handlers close their own window programmatically.";
 const char kPaymentEventServiceWorkerError[] = "Payment handler failed to provide a response because either the \"paymentrequest\" event took too long or the service worker stopped for some reason or was killed before the request finished.";
 const char kPaymentEventTimeout[] = "The \"paymentrequest\" event timed out after 5 minutes.";
+const char kPaymentHandlerInsecureNavigation[] = "The payment handler navigated to a page with insecure context, invalid certificate state, or malicious content.";
 const char kProhibitedOrigin[] = "Only localhost, file://, and cryptographic scheme origins allowed.";
 const char kProhibitedOriginOrInvalidSslExplanation[] = "No UI will be shown. CanMakePayment and hasEnrolledInstrument will always return false. Show will be rejected with NotSupportedError.";
 const char kSinglePaymentMethodNotSupportedFormat[] = "The payment method $ is not supported.";
diff --git a/components/payments/core/error_strings.h b/components/payments/core/error_strings.h
index 1be03dd..a60c2558 100644
--- a/components/payments/core/error_strings.h
+++ b/components/payments/core/error_strings.h
@@ -122,6 +122,10 @@
 // Service worker timed out while responding to "paymentrequest" event.
 extern const char kPaymentEventTimeout[];
 
+// Payment handler navigated to a page with insecure context, invalid SSL, or
+// malicious content.
+extern const char kPaymentHandlerInsecureNavigation[];
+
 // Payment handler encountered an internal error when handling the
 // "paymentrequest" event.
 extern const char kPaymentEventInternalError[];
diff --git a/components/payments/core/journey_logger.cc b/components/payments/core/journey_logger.cc
index 4f31801..50e0cb6 100644
--- a/components/payments/core/journey_logger.cc
+++ b/components/payments/core/journey_logger.cc
@@ -11,6 +11,7 @@
 #include "base/debug/dump_without_crashing.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/metrics/public/cpp/ukm_recorder.h"
@@ -111,6 +112,22 @@
   sections_[section].has_complete_suggestion_ = has_complete_suggestion;
 }
 
+void JourneyLogger::SetSectionNeedsCompletion(const Section section) {
+  switch (section) {
+    case SECTION_CONTACT_INFO:
+      events_ |= EVENT_NEEDS_COMPLETION_CONTACT_INFO;
+      break;
+    case SECTION_PAYMENT_METHOD:
+      events_ |= EVENT_NEEDS_COMPLETION_PAYMENT;
+      break;
+    case SECTION_SHIPPING_ADDRESS:
+      events_ |= EVENT_NEEDS_COMPLETION_SHIPPING;
+      break;
+    default:
+      NOTREACHED();
+  }
+}
+
 void JourneyLogger::SetCanMakePaymentValue(bool value) {
   // Do not log the outcome of canMakePayment in incognito mode.
   if (is_incognito_)
@@ -267,6 +284,7 @@
       if (sections_[i].number_suggestions_shown_ == 0 ||
           !sections_[i].has_complete_suggestion_) {
         user_had_complete_suggestions_for_requested_information = false;
+        SetSectionNeedsCompletion(static_cast<const Section>(i));
       }
     }
   }
diff --git a/components/payments/core/journey_logger.h b/components/payments/core/journey_logger.h
index e136013..82c463c5 100644
--- a/components/payments/core/journey_logger.h
+++ b/components/payments/core/journey_logger.h
@@ -99,7 +99,10 @@
     EVENT_HAS_ENROLLED_INSTRUMENT_FALSE = 1 << 22,
     // True when a NotShownReason is set.
     EVENT_COULD_NOT_SHOW = 1 << 23,
-    EVENT_ENUM_MAX = 2097152,
+    EVENT_NEEDS_COMPLETION_CONTACT_INFO = 1 << 24,
+    EVENT_NEEDS_COMPLETION_PAYMENT = 1 << 25,
+    EVENT_NEEDS_COMPLETION_SHIPPING = 1 << 26,
+    EVENT_ENUM_MAX = 1 << 27,
   };
 
   // The reason why the Payment Request was aborted.
@@ -234,6 +237,9 @@
   // Returns whether this Payment Request was triggered (shown or skipped show).
   bool WasPaymentRequestTriggered();
 
+  // Sets needs completion bit in events_ bit field for the given section.
+  void SetSectionNeedsCompletion(Section section);
+
   SectionStats sections_[NUMBER_OF_SECTIONS];
   bool has_recorded_ = false;
   bool is_incognito_;
diff --git a/components/payments/core/payments_profile_comparator.cc b/components/payments/core/payments_profile_comparator.cc
index 548e6ed2..b104f4d 100644
--- a/components/payments/core/payments_profile_comparator.cc
+++ b/components/payments/core/payments_profile_comparator.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 #include <memory>
 
+#include "base/metrics/histogram_functions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/autofill/core/browser/autofill_data_util.h"
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
@@ -177,6 +178,37 @@
            GetRequiredProfileFieldsForShipping());
 }
 
+void PaymentsProfileComparator::RecordMissingFieldsOfShippingProfile(
+    const autofill::AutofillProfile* profile) const {
+  // We should not record anything when no shipping fields is required.
+  if (!GetRequiredProfileFieldsForShipping())
+    return;
+
+  // Record any required fields that are missing.
+  PaymentsProfileComparator::ProfileFields missing_fields =
+      GetMissingProfileFields(profile) & GetRequiredProfileFieldsForShipping();
+  if (missing_fields != kNone) {
+    base::UmaHistogramSparse("PaymentRequest.MissingShippingFields",
+                             missing_fields);
+  }
+}
+
+void PaymentsProfileComparator::RecordMissingFieldsOfContactProfile(
+    const autofill::AutofillProfile* profile) const {
+  // We should not record anything when no contact fields is required.
+  if (GetRequiredProfileFieldsForContact() == kNone)
+    return;
+
+  // Record any required fields that are missing.
+  PaymentsProfileComparator::ProfileFields missing_fields =
+      GetMissingProfileFields(profile) & GetRequiredProfileFieldsForContact();
+  if (missing_fields != kNone) {
+    base::UmaHistogramSparse("PaymentRequest.MissingContactFields",
+                             missing_fields);
+    return;
+  }
+}
+
 base::string16 PaymentsProfileComparator::GetStringForMissingContactFields(
     const autofill::AutofillProfile& profile) const {
   return GetStringForMissingFields(GetMissingProfileFields(&profile) &
@@ -209,7 +241,7 @@
 PaymentsProfileComparator::ProfileFields
 PaymentsProfileComparator::ComputeMissingFields(
     const autofill::AutofillProfile& profile) const {
-  ProfileFields missing = 0;
+  ProfileFields missing = kNone;
 
   if (!profile.HasInfo(autofill::NAME_FULL))
     missing |= kName;
@@ -242,7 +274,7 @@
 
 PaymentsProfileComparator::ProfileFields
 PaymentsProfileComparator::GetRequiredProfileFieldsForContact() const {
-  ProfileFields required = 0;
+  ProfileFields required = kNone;
   if (options_.request_payer_name())
     required |= kName;
   if (options_.request_payer_phone())
@@ -254,13 +286,13 @@
 
 PaymentsProfileComparator::ProfileFields
 PaymentsProfileComparator::GetRequiredProfileFieldsForShipping() const {
-  return options_.request_shipping() ? (kAddress | kName | kPhone) : 0;
+  return options_.request_shipping() ? (kAddress | kName | kPhone) : kNone;
 }
 
 base::string16 PaymentsProfileComparator::GetStringForMissingFields(
     PaymentsProfileComparator::ProfileFields fields) const {
   switch (fields) {
-    case 0:
+    case kNone:
       // No bits are set, so no fields are missing.
       return base::string16();
     case kName:
diff --git a/components/payments/core/payments_profile_comparator.h b/components/payments/core/payments_profile_comparator.h
index 3efea9c..6395d69f 100644
--- a/components/payments/core/payments_profile_comparator.h
+++ b/components/payments/core/payments_profile_comparator.h
@@ -33,6 +33,7 @@
  public:
   // Bitmask of potentially-required fields used in evaluating completeness.
   using ProfileFields = uint32_t;
+  const static ProfileFields kNone = 0;
   const static ProfileFields kName = 1 << 0;
   const static ProfileFields kPhone = 1 << 1;
   const static ProfileFields kEmail = 1 << 2;
@@ -107,6 +108,11 @@
   base::string16 GetTitleForMissingShippingFields(
       const autofill::AutofillProfile& profile) const;
 
+  void RecordMissingFieldsOfShippingProfile(
+      const autofill::AutofillProfile* profile) const;
+  void RecordMissingFieldsOfContactProfile(
+      const autofill::AutofillProfile* profile) const;
+
   // Clears the cached evaluation result for |profile|. Must be called when a
   // profile is modified and saved during the course of a PaymentRequest.
   virtual void Invalidate(const autofill::AutofillProfile& profile);
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index a9f38de..93dc2a2 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -10729,7 +10729,7 @@
     },
     {
       'name': 'WelcomePageOnOSUpgradeEnabled',
-      'supported_on': ['chrome.win:45-'],
+      'supported_on': ['chrome.win:45-62'],
       'type': 'main',
       'schema': { 'type': 'boolean' },
       'id': 303,
diff --git a/components/safe_browsing/db/v4_update_protocol_manager_unittest.cc b/components/safe_browsing/db/v4_update_protocol_manager_unittest.cc
index 07445051..fa27844e 100644
--- a/components/safe_browsing/db/v4_update_protocol_manager_unittest.cc
+++ b/components/safe_browsing/db/v4_update_protocol_manager_unittest.cc
@@ -27,6 +27,10 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 
+#if !defined(FULL_SAFE_BROWSING)
+#include "base/system/sys_info.h"
+#endif
+
 using base::Time;
 using base::TimeDelta;
 
@@ -283,11 +287,13 @@
   std::string encoded_request_with_minus =
       pm->GetBase64SerializedUpdateRequestProto();
 
-  const std::string expected =
-#if defined(FULL_SAFE_BROWSING)
+  std::string expected =
       "Cg8KCHVuaXR0ZXN0EgMxLjAaGAgBEAIaCmg4eGZZcVk-OlIiBCABIAIoASICCAE=";
-#else
-      "Cg8KCHVuaXR0ZXN0EgMxLjAaGwgBEAIaCmg4eGZZcVk-OlIiBxCAICABIAIoASICCAE=";
+#if !defined(FULL_SAFE_BROWSING)
+  if (base::SysInfo::IsLowEndDevice()) {
+    expected =
+        "Cg8KCHVuaXR0ZXN0EgMxLjAaGwgBEAIaCmg4eGZZcVk-OlIiBxCAICABIAIoASICCAE=";
+  }
 #endif
 
   EXPECT_EQ(expected, encoded_request_with_minus);
@@ -353,11 +359,11 @@
   store_state_map_->clear();
   (*store_state_map_)[ListIdentifier(LINUX_PLATFORM, URL, MALWARE_THREAT)] =
       "state";
-  const std::string base =
-#if defined(FULL_SAFE_BROWSING)
-      "Cg8KCHVuaXR0ZXN0EgMxLjAaEwgBEAIaBXN0YXRlIgQgASACKAEiAgg";
-#else
-      "Cg8KCHVuaXR0ZXN0EgMxLjAaFggBEAIaBXN0YXRlIgcQgCAgASACKAEiAgg";
+  std::string base = "Cg8KCHVuaXR0ZXN0EgMxLjAaEwgBEAIaBXN0YXRlIgQgASACKAEiAgg";
+#if !defined(FULL_SAFE_BROWSING)
+  if (base::SysInfo::IsLowEndDevice()) {
+    base = "Cg8KCHVuaXR0ZXN0EgMxLjAaFggBEAIaBXN0YXRlIgcQgCAgASACKAEiAgg";
+  }
 #endif
 
   std::unique_ptr<V4UpdateProtocolManager> pm_with_off(CreateProtocolManager(
diff --git a/components/safe_browsing/password_protection/password_protection_request.cc b/components/safe_browsing/password_protection/password_protection_request.cc
index da9214a..1fdaf94a 100644
--- a/components/safe_browsing/password_protection/password_protection_request.cc
+++ b/components/safe_browsing/password_protection/password_protection_request.cc
@@ -266,7 +266,9 @@
 
 void PasswordProtectionRequest::OnGetDomFeatures(const std::string& verdict) {
   ClientPhishingRequest dom_features_request;
-  if (dom_features_request.ParseFromString(verdict)) {
+  bool parsed = dom_features_request.ParseFromString(verdict);
+  UMA_HISTOGRAM_BOOLEAN("PasswordProtection.DomFeatureParsing", parsed);
+  if (parsed) {
     for (const ClientPhishingRequest::Feature& feature :
          dom_features_request.feature_map()) {
       DomFeatures::Feature* new_feature =
diff --git a/components/signin/core/browser/fake_profile_oauth2_token_service.cc b/components/signin/core/browser/fake_profile_oauth2_token_service.cc
index 30654f6..a38940a 100644
--- a/components/signin/core/browser/fake_profile_oauth2_token_service.cc
+++ b/components/signin/core/browser/fake_profile_oauth2_token_service.cc
@@ -6,20 +6,9 @@
 
 #include <memory>
 
-#include "base/bind.h"
-#include "base/location.h"
-#include "base/single_thread_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
 #include "google_apis/gaia/fake_oauth2_token_service_delegate.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
-FakeProfileOAuth2TokenService::PendingRequest::PendingRequest() {}
-
-FakeProfileOAuth2TokenService::PendingRequest::PendingRequest(
-    const PendingRequest& other) = default;
-
-FakeProfileOAuth2TokenService::PendingRequest::~PendingRequest() {}
-
 FakeProfileOAuth2TokenService::FakeProfileOAuth2TokenService(
     PrefService* user_prefs)
     : FakeProfileOAuth2TokenService(
@@ -29,9 +18,12 @@
 FakeProfileOAuth2TokenService::FakeProfileOAuth2TokenService(
     PrefService* user_prefs,
     std::unique_ptr<OAuth2TokenServiceDelegate> delegate)
-    : ProfileOAuth2TokenService(user_prefs, std::move(delegate)),
-      auto_post_fetch_response_on_message_loop_(false),
-      weak_ptr_factory_(this) {}
+    : ProfileOAuth2TokenService(user_prefs, std::move(delegate)) {
+  OverrideAccessTokenManagerForTesting(
+      std::make_unique<FakeOAuth2AccessTokenManager>(
+          this /* OAuth2TokenService* */,
+          this /* OAuth2AccessTokenManager::Delegate* */));
+}
 
 FakeProfileOAuth2TokenService::~FakeProfileOAuth2TokenService() {}
 
@@ -39,137 +31,79 @@
     const std::string& account_id,
     const std::string& access_token,
     const base::Time& expiration) {
-  DCHECK(!auto_post_fetch_response_on_message_loop_);
-  CompleteRequests(account_id, true, OAuth2AccessTokenManager::ScopeSet(),
-                   GoogleServiceAuthError::AuthErrorNone(),
-                   OAuth2AccessTokenConsumer::TokenResponse(
-                       access_token, expiration, std::string() /* id_token */));
+  GetFakeAccessTokenManager()->IssueAllTokensForAccount(
+      account_id, access_token, expiration);
 }
 
 void FakeProfileOAuth2TokenService::IssueAllTokensForAccount(
     const std::string& account_id,
     const OAuth2AccessTokenConsumer::TokenResponse& token_response) {
-  DCHECK(!auto_post_fetch_response_on_message_loop_);
-  CompleteRequests(account_id, true, OAuth2AccessTokenManager::ScopeSet(),
-                   GoogleServiceAuthError::AuthErrorNone(), token_response);
+  GetFakeAccessTokenManager()->IssueAllTokensForAccount(account_id,
+                                                        token_response);
 }
 
 void FakeProfileOAuth2TokenService::IssueErrorForAllPendingRequestsForAccount(
     const std::string& account_id,
     const GoogleServiceAuthError& error) {
-  DCHECK(!auto_post_fetch_response_on_message_loop_);
-  CompleteRequests(account_id, true, OAuth2AccessTokenManager::ScopeSet(),
-                   error, OAuth2AccessTokenConsumer::TokenResponse());
+  GetFakeAccessTokenManager()->IssueErrorForAllPendingRequestsForAccount(
+      account_id, error);
 }
 
 void FakeProfileOAuth2TokenService::IssueTokenForScope(
     const OAuth2AccessTokenManager::ScopeSet& scope,
     const std::string& access_token,
     const base::Time& expiration) {
-  DCHECK(!auto_post_fetch_response_on_message_loop_);
-  CompleteRequests("", false, scope, GoogleServiceAuthError::AuthErrorNone(),
-                   OAuth2AccessTokenConsumer::TokenResponse(
-                       access_token, expiration, std::string() /* id_token */));
+  GetFakeAccessTokenManager()->IssueTokenForScope(scope, access_token,
+                                                  expiration);
 }
 
 void FakeProfileOAuth2TokenService::IssueTokenForScope(
     const OAuth2AccessTokenManager::ScopeSet& scope,
     const OAuth2AccessTokenConsumer::TokenResponse& token_response) {
-  DCHECK(!auto_post_fetch_response_on_message_loop_);
-  CompleteRequests("", false, scope, GoogleServiceAuthError::AuthErrorNone(),
-                   token_response);
+  GetFakeAccessTokenManager()->IssueTokenForScope(scope, token_response);
 }
 
 void FakeProfileOAuth2TokenService::IssueErrorForScope(
     const OAuth2AccessTokenManager::ScopeSet& scope,
     const GoogleServiceAuthError& error) {
-  DCHECK(!auto_post_fetch_response_on_message_loop_);
-  CompleteRequests("", false, scope, error,
-                   OAuth2AccessTokenConsumer::TokenResponse());
+  GetFakeAccessTokenManager()->IssueErrorForScope(scope, error);
 }
 
 void FakeProfileOAuth2TokenService::IssueErrorForAllPendingRequests(
     const GoogleServiceAuthError& error) {
-  DCHECK(!auto_post_fetch_response_on_message_loop_);
-  CompleteRequests("", true, OAuth2AccessTokenManager::ScopeSet(), error,
-                   OAuth2AccessTokenConsumer::TokenResponse());
+  GetFakeAccessTokenManager()->IssueErrorForAllPendingRequests(error);
+}
+
+void FakeProfileOAuth2TokenService::
+    set_auto_post_fetch_response_on_message_loop(bool auto_post_response) {
+  GetFakeAccessTokenManager()->set_auto_post_fetch_response_on_message_loop(
+      auto_post_response);
 }
 
 void FakeProfileOAuth2TokenService::IssueTokenForAllPendingRequests(
     const std::string& access_token,
     const base::Time& expiration) {
-  DCHECK(!auto_post_fetch_response_on_message_loop_);
-  CompleteRequests("", true, OAuth2AccessTokenManager::ScopeSet(),
-                   GoogleServiceAuthError::AuthErrorNone(),
-                   OAuth2AccessTokenConsumer::TokenResponse(
-                       access_token, expiration, std::string() /* id_token */));
+  GetFakeAccessTokenManager()->IssueTokenForAllPendingRequests(access_token,
+                                                               expiration);
 }
 
 void FakeProfileOAuth2TokenService::IssueTokenForAllPendingRequests(
     const OAuth2AccessTokenConsumer::TokenResponse& token_response) {
-  DCHECK(!auto_post_fetch_response_on_message_loop_);
-  CompleteRequests("", true, OAuth2AccessTokenManager::ScopeSet(),
-                   GoogleServiceAuthError::AuthErrorNone(), token_response);
+  GetFakeAccessTokenManager()->IssueTokenForAllPendingRequests(token_response);
 }
 
-void FakeProfileOAuth2TokenService::CompleteRequests(
-    const std::string& account_id,
-    bool all_scopes,
-    const OAuth2AccessTokenManager::ScopeSet& scope,
-    const GoogleServiceAuthError& error,
-    const OAuth2AccessTokenConsumer::TokenResponse& token_response) {
-  std::vector<FakeProfileOAuth2TokenService::PendingRequest> requests =
-      GetPendingRequests();
-
-  // Walk the requests and notify the callbacks.
-  for (auto it = requests.begin(); it != requests.end(); ++it) {
-    // Consumers can drop requests in response to callbacks on other requests
-    // (e.g., OAuthMultiloginFetcher clears all of its requests when it gets an
-    // error on any of them).
-    if (!it->request)
-      continue;
-
-    bool scope_matches = all_scopes || it->scopes == scope;
-    bool account_matches = account_id.empty() || account_id == it->account_id;
-    if (account_matches && scope_matches) {
-      for (auto& diagnostic_observer : GetAccessTokenDiagnosticsObservers()) {
-        diagnostic_observer.OnFetchAccessTokenComplete(
-            account_id, it->request->GetConsumerId(), scope, error,
-            base::Time());
-      }
-
-      it->request->InformConsumer(
-          error, OAuth2AccessTokenConsumer::TokenResponse(
-                     token_response.access_token,
-                     token_response.expiration_time, token_response.id_token));
-    }
-  }
-}
-
-std::vector<FakeProfileOAuth2TokenService::PendingRequest>
+std::vector<FakeOAuth2AccessTokenManager::PendingRequest>
 FakeProfileOAuth2TokenService::GetPendingRequests() {
-  std::vector<PendingRequest> valid_requests;
-  for (auto it = pending_requests_.begin(); it != pending_requests_.end();
-       ++it) {
-    if (it->request)
-      valid_requests.push_back(*it);
-  }
-  return valid_requests;
+  return GetFakeAccessTokenManager()->GetPendingRequests();
 }
 
 void FakeProfileOAuth2TokenService::CancelAllRequests() {
-  CompleteRequests(
-      "", true, OAuth2AccessTokenManager::ScopeSet(),
-      GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED),
-      OAuth2AccessTokenConsumer::TokenResponse());
+  GetFakeAccessTokenManager()->CancelAllRequests();
 }
 
 void FakeProfileOAuth2TokenService::CancelRequestsForAccount(
     const CoreAccountId& account_id) {
-  CompleteRequests(
-      account_id, true, OAuth2AccessTokenManager::ScopeSet(),
-      GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED),
-      OAuth2AccessTokenConsumer::TokenResponse());
+  GetFakeAccessTokenManager()->CancelRequestsForAccount(account_id);
 }
 
 void FakeProfileOAuth2TokenService::FetchOAuth2Token(
@@ -179,25 +113,9 @@
     const std::string& client_id,
     const std::string& client_secret,
     const OAuth2AccessTokenManager::ScopeSet& scopes) {
-  PendingRequest pending_request;
-  pending_request.account_id = account_id;
-  pending_request.client_id = client_id;
-  pending_request.client_secret = client_secret;
-  pending_request.url_loader_factory = url_loader_factory;
-  pending_request.scopes = scopes;
-  pending_request.request = request->AsWeakPtr();
-  pending_requests_.push_back(pending_request);
-
-  if (auto_post_fetch_response_on_message_loop_) {
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::BindOnce(&FakeProfileOAuth2TokenService::CompleteRequests,
-                       weak_ptr_factory_.GetWeakPtr(), account_id,
-                       /*all_scoped=*/true, scopes,
-                       GoogleServiceAuthError::AuthErrorNone(),
-                       OAuth2AccessTokenConsumer::TokenResponse(
-                           "access_token", base::Time::Max(), std::string())));
-  }
+  GetFakeAccessTokenManager()->FetchOAuth2Token(request, account_id,
+                                                url_loader_factory, client_id,
+                                                client_secret, scopes);
 }
 
 void FakeProfileOAuth2TokenService::InvalidateAccessTokenImpl(
@@ -205,5 +123,11 @@
     const std::string& client_id,
     const OAuth2AccessTokenManager::ScopeSet& scopes,
     const std::string& access_token) {
-  // Do nothing, as we don't have a cache from which to remove the token.
+  GetFakeAccessTokenManager()->InvalidateAccessTokenImpl(account_id, client_id,
+                                                         scopes, access_token);
+}
+
+FakeOAuth2AccessTokenManager*
+FakeProfileOAuth2TokenService::GetFakeAccessTokenManager() {
+  return static_cast<FakeOAuth2AccessTokenManager*>(GetAccessTokenManager());
 }
diff --git a/components/signin/core/browser/fake_profile_oauth2_token_service.h b/components/signin/core/browser/fake_profile_oauth2_token_service.h
index 18bb90aa..9208162 100644
--- a/components/signin/core/browser/fake_profile_oauth2_token_service.h
+++ b/components/signin/core/browser/fake_profile_oauth2_token_service.h
@@ -10,8 +10,8 @@
 
 #include "base/compiler_specific.h"
 #include "base/macros.h"
-#include "base/memory/weak_ptr.h"
 #include "components/signin/core/browser/profile_oauth2_token_service.h"
+#include "google_apis/gaia/fake_oauth2_access_token_manager.h"
 
 namespace network {
 class SharedURLLoaderFactory;
@@ -39,19 +39,6 @@
 //
 class FakeProfileOAuth2TokenService : public ProfileOAuth2TokenService {
  public:
-  struct PendingRequest {
-    PendingRequest();
-    PendingRequest(const PendingRequest& other);
-    ~PendingRequest();
-
-    std::string account_id;
-    std::string client_id;
-    std::string client_secret;
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory;
-    OAuth2AccessTokenManager::ScopeSet scopes;
-    base::WeakPtr<OAuth2AccessTokenManager::RequestImpl> request;
-  };
-
   explicit FakeProfileOAuth2TokenService(PrefService* user_prefs);
   FakeProfileOAuth2TokenService(
       PrefService* user_prefs,
@@ -60,7 +47,8 @@
 
   // Gets a list of active requests (can be used by tests to validate that the
   // correct request has been issued).
-  std::vector<PendingRequest> GetPendingRequests();
+  std::vector<FakeOAuth2AccessTokenManager::PendingRequest>
+  GetPendingRequests();
 
   // Helper routines to issue tokens for pending requests.
   void IssueAllTokensForAccount(const std::string& account_id,
@@ -95,9 +83,7 @@
 
   void IssueErrorForAllPendingRequests(const GoogleServiceAuthError& error);
 
-  void set_auto_post_fetch_response_on_message_loop(bool auto_post_response) {
-    auto_post_fetch_response_on_message_loop_ = auto_post_response;
-  }
+  void set_auto_post_fetch_response_on_message_loop(bool auto_post_response);
 
  protected:
   // OAuth2TokenService overrides.
@@ -120,26 +106,7 @@
       const std::string& access_token) override;
 
  private:
-  // Helper function to complete pending requests - if |all_scopes| is true,
-  // then all pending requests are completed, otherwise, only those requests
-  // matching |scopes| are completed.  If |account_id| is empty, then pending
-  // requests for all accounts are completed, otherwise only requests for the
-  // given account.
-  void CompleteRequests(
-      const std::string& account_id,
-      bool all_scopes,
-      const OAuth2AccessTokenManager::ScopeSet& scopes,
-      const GoogleServiceAuthError& error,
-      const OAuth2AccessTokenConsumer::TokenResponse& token_response);
-
-  std::vector<PendingRequest> pending_requests_;
-
-  // If true, then this fake service will post responses to
-  // |FetchOAuth2Token| on the current run loop. There is no need to call
-  // |IssueTokenForScope| in this case.
-  bool auto_post_fetch_response_on_message_loop_;
-
-  base::WeakPtrFactory<FakeProfileOAuth2TokenService> weak_ptr_factory_;
+  FakeOAuth2AccessTokenManager* GetFakeAccessTokenManager();
 
   DISALLOW_COPY_AND_ASSIGN(FakeProfileOAuth2TokenService);
 };
diff --git a/components/signin/ios/browser/device_accounts_provider.h b/components/signin/ios/browser/device_accounts_provider.h
index 1d9268ac..522ee3f 100644
--- a/components/signin/ios/browser/device_accounts_provider.h
+++ b/components/signin/ios/browser/device_accounts_provider.h
@@ -47,9 +47,8 @@
     std::string email;
   };
 
-  typedef base::Callback<
-      void(NSString* token, NSDate* expiration, NSError* error)>
-      AccessTokenCallback;
+  using AccessTokenCallback = base::OnceCallback<
+      void(NSString* token, NSDate* expiration, NSError* error)>;
 
   DeviceAccountsProvider() {}
   virtual ~DeviceAccountsProvider() {}
@@ -62,7 +61,7 @@
   virtual void GetAccessToken(const std::string& gaia_id,
                               const std::string& client_id,
                               const std::set<std::string>& scopes,
-                              const AccessTokenCallback& callback);
+                              AccessTokenCallback callback);
 
   // Returns the authentication error category of |error| associated with the
   // account with id |gaia_id|.
diff --git a/components/signin/ios/browser/device_accounts_provider.mm b/components/signin/ios/browser/device_accounts_provider.mm
index 38d9c60..df675fd1 100644
--- a/components/signin/ios/browser/device_accounts_provider.mm
+++ b/components/signin/ios/browser/device_accounts_provider.mm
@@ -13,11 +13,10 @@
   return std::vector<DeviceAccountsProvider::AccountInfo>();
 }
 
-void DeviceAccountsProvider::GetAccessToken(
-    const std::string& gaia_id,
-    const std::string& client_id,
-    const std::set<std::string>& scopes,
-    const AccessTokenCallback& callback) {}
+void DeviceAccountsProvider::GetAccessToken(const std::string& gaia_id,
+                                            const std::string& client_id,
+                                            const std::set<std::string>& scopes,
+                                            AccessTokenCallback callback) {}
 
 AuthenticationErrorCategory
 DeviceAccountsProvider::GetAuthenticationErrorCategory(
diff --git a/components/signin/ios/browser/fake_device_accounts_provider.h b/components/signin/ios/browser/fake_device_accounts_provider.h
index 15342a8e..a93947d 100644
--- a/components/signin/ios/browser/fake_device_accounts_provider.h
+++ b/components/signin/ios/browser/fake_device_accounts_provider.h
@@ -23,7 +23,7 @@
   void GetAccessToken(const std::string& account_id,
                       const std::string& client_id,
                       const std::set<std::string>& scopes,
-                      const AccessTokenCallback& callback) override;
+                      AccessTokenCallback callback) override;
   std::vector<AccountInfo> GetAllAccounts() const override;
   AuthenticationErrorCategory GetAuthenticationErrorCategory(
       const std::string& gaia_id,
@@ -38,7 +38,7 @@
   void IssueAccessTokenErrorForAllRequests();
 
  private:
-  typedef std::pair<std::string, AccessTokenCallback> AccessTokenRequest;
+  using AccessTokenRequest = std::pair<std::string, AccessTokenCallback>;
 
   std::vector<AccountInfo> accounts_;
   std::vector<AccessTokenRequest> requests_;
diff --git a/components/signin/ios/browser/fake_device_accounts_provider.mm b/components/signin/ios/browser/fake_device_accounts_provider.mm
index d313875..63bcbb0e 100644
--- a/components/signin/ios/browser/fake_device_accounts_provider.mm
+++ b/components/signin/ios/browser/fake_device_accounts_provider.mm
@@ -21,8 +21,8 @@
     const std::string& account_id,
     const std::string& client_id,
     const std::set<std::string>& scopes,
-    const AccessTokenCallback& callback) {
-  requests_.push_back(AccessTokenRequest(account_id, callback));
+    AccessTokenCallback callback) {
+  requests_.push_back(AccessTokenRequest(account_id, std::move(callback)));
 }
 
 std::vector<DeviceAccountsProvider::AccountInfo>
@@ -45,25 +45,21 @@
 }
 
 void FakeDeviceAccountsProvider::IssueAccessTokenForAllRequests() {
-  for (auto i = requests_.begin(); i != requests_.end(); ++i) {
-    std::string account_id = i->first;
-    AccessTokenCallback callback = i->second;
+  for (auto& pair : requests_) {
     NSString* access_token = [NSString
-        stringWithFormat:@"fake_access_token [account=%s]", account_id.c_str()];
+        stringWithFormat:@"fake_access_token [account=%s]", pair.first.c_str()];
     NSDate* one_hour_from_now = [NSDate dateWithTimeIntervalSinceNow:3600];
-    callback.Run(access_token, one_hour_from_now, nil);
+    std::move(pair.second).Run(access_token, one_hour_from_now, nil);
   }
   requests_.clear();
 }
 
 void FakeDeviceAccountsProvider::IssueAccessTokenErrorForAllRequests() {
-  for (auto i = requests_.begin(); i != requests_.end(); ++i) {
-    std::string account_id = i->first;
-    AccessTokenCallback callback = i->second;
+  for (auto& pair : requests_) {
     NSError* error = [[NSError alloc] initWithDomain:@"fake_access_token_error"
                                                 code:-1
                                             userInfo:nil];
-    callback.Run(nil, nil, error);
+    std::move(pair.second).Run(nil, nil, error);
   }
   requests_.clear();
 }
diff --git a/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.mm b/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.mm
index 5f6da62..f53294b 100644
--- a/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.mm
+++ b/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.mm
@@ -118,8 +118,8 @@
   std::set<std::string> scopes_set(scopes.begin(), scopes.end());
   provider_->GetAccessToken(
       account_.gaia, client_id, scopes_set,
-      base::Bind(&SSOAccessTokenFetcher::OnAccessTokenResponse,
-                 weak_factory_.GetWeakPtr()));
+      base::BindOnce(&SSOAccessTokenFetcher::OnAccessTokenResponse,
+                     weak_factory_.GetWeakPtr()));
 }
 
 void SSOAccessTokenFetcher::CancelRequest() {
diff --git a/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle.cc b/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle.cc
index e10618a..523cdbbb 100644
--- a/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle.cc
+++ b/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle.cc
@@ -9,9 +9,7 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/memory/ptr_util.h"
-#include "base/metrics/histogram_macros.h"
 #include "components/subresource_filter/content/browser/async_document_subresource_filter.h"
-#include "components/subresource_filter/core/common/time_measurements.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
@@ -95,13 +93,12 @@
   // finish, or start a new check now if there was no previous speculative
   // check.
   if (async_filter_ && async_filter_->has_activation_state()) {
-    LogDelayMetrics(base::TimeDelta::FromMilliseconds(0));
     if (navigation_handle()->IsInMainFrame())
       UpdateWithMoreAccurateState();
     return content::NavigationThrottle::PROCEED;
   }
-  DCHECK(!defer_timer_);
-  defer_timer_ = std::make_unique<base::ElapsedTimer>();
+  DCHECK(!deferred_);
+  deferred_ = true;
   if (!async_filter_) {
     DCHECK(navigation_handle()->IsInMainFrame());
     CheckActivationState();
@@ -138,8 +135,7 @@
 
 void ActivationStateComputingNavigationThrottle::OnActivationStateComputed(
     mojom::ActivationState state) {
-  if (defer_timer_) {
-    LogDelayMetrics(defer_timer_->Elapsed());
+  if (deferred_) {
     if (navigation_handle()->IsInMainFrame())
       UpdateWithMoreAccurateState();
     Resume();
@@ -156,20 +152,6 @@
   async_filter_->UpdateWithMoreAccurateState(*parent_activation_state_);
 }
 
-void ActivationStateComputingNavigationThrottle::LogDelayMetrics(
-    base::TimeDelta delay) const {
-  UMA_HISTOGRAM_CUSTOM_MICRO_TIMES(
-      "SubresourceFilter.DocumentLoad.ActivationComputingDelay", delay,
-      base::TimeDelta::FromMicroseconds(1), base::TimeDelta::FromSeconds(10),
-      50);
-  if (navigation_handle()->IsInMainFrame()) {
-    UMA_HISTOGRAM_CUSTOM_MICRO_TIMES(
-        "SubresourceFilter.DocumentLoad.ActivationComputingDelay.MainFrame",
-        delay, base::TimeDelta::FromMicroseconds(1),
-        base::TimeDelta::FromSeconds(10), 50);
-  }
-}
-
 AsyncDocumentSubresourceFilter*
 ActivationStateComputingNavigationThrottle::filter() const {
   // TODO(csharrison): This should not really be necessary, as we should be
diff --git a/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle.h b/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle.h
index fc89ea4..20ba9abb 100644
--- a/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle.h
+++ b/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle.h
@@ -10,8 +10,6 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
-#include "base/time/time.h"
-#include "base/timer/elapsed_timer.h"
 #include "components/subresource_filter/content/browser/verified_ruleset_dealer.h"
 #include "components/subresource_filter/core/mojom/subresource_filter.mojom.h"
 #include "content/public/browser/navigation_throttle.h"
@@ -97,8 +95,6 @@
   // This must be called at the end of the WillProcessResponse stage.
   void UpdateWithMoreAccurateState();
 
-  void LogDelayMetrics(base::TimeDelta delay) const;
-
   ActivationStateComputingNavigationThrottle(
       content::NavigationHandle* navigation_handle,
       const base::Optional<mojom::ActivationState> parent_activation_state,
@@ -113,9 +109,8 @@
   // nullptr until NotifyPageActivationWithRuleset is called.
   VerifiedRuleset::Handle* ruleset_handle_;
 
-  // Will be set when DEFER is called in WillProcessResponse. If nullptr, not
-  // deferred.
-  std::unique_ptr<base::ElapsedTimer> defer_timer_;
+  // Will be set to true when DEFER is called in WillProcessResponse.
+  bool deferred_ = false;
 
   // Will become true when the throttle manager reaches ReadyToCommitNavigation.
   // Makes sure a caller cannot take ownership of the subresource filter unless
diff --git a/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle_unittest.cc b/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle_unittest.cc
index 27282df..eeb44643 100644
--- a/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle_unittest.cc
+++ b/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle_unittest.cc
@@ -483,45 +483,6 @@
   EXPECT_TRUE(state.generic_blocking_rules_disabled);
 }
 
-TEST_P(ActivationStateComputingThrottleSubFrameTest, DelayMetrics) {
-  base::HistogramTester histogram_tester;
-  NavigateAndCommitMainFrameWithPageActivationState(
-      GURL("http://example.test/"), mojom::ActivationLevel::kEnabled);
-  mojom::ActivationState state = last_activation_state();
-  EXPECT_EQ(mojom::ActivationLevel::kEnabled, state.activation_level);
-  EXPECT_FALSE(state.filtering_disabled_for_document);
-
-  const char kActivationDelay[] =
-      "SubresourceFilter.DocumentLoad.ActivationComputingDelay";
-  const char kActivationDelayMainFrame[] =
-      "SubresourceFilter.DocumentLoad.ActivationComputingDelay.MainFrame";
-  histogram_tester.ExpectTotalCount(kActivationDelay, 1);
-  histogram_tester.ExpectTotalCount(kActivationDelayMainFrame, 1);
-
-  // Subframe activation should not log main frame metrics.
-  CreateSubframeAndInitTestNavigation(GURL("http://example.test/"),
-                                      last_committed_frame_host(),
-                                      last_activation_state());
-  SimulateStartAndExpectToProceed();
-  SimulateCommitAndExpectToProceed();
-  histogram_tester.ExpectTotalCount(kActivationDelay, 2);
-  histogram_tester.ExpectTotalCount(kActivationDelayMainFrame, 1);
-
-  // No page activation should imply no delay.
-  CreateTestNavigationForMainFrame(GURL("http://example.test2/"));
-  SimulateStartAndExpectToProceed();
-  SimulateCommitAndExpectToProceed();
-
-  state = last_activation_state();
-  EXPECT_EQ(dryrun_speculation() ? mojom::ActivationLevel::kDryRun
-                                 : mojom::ActivationLevel::kDisabled,
-            state.activation_level);
-  int extra_activation = dryrun_speculation() ? 1 : 0;
-  histogram_tester.ExpectTotalCount(kActivationDelay, 2 + extra_activation);
-  histogram_tester.ExpectTotalCount(kActivationDelayMainFrame,
-                                    1 + extra_activation);
-}
-
 TEST_P(ActivationStateComputingThrottleSubFrameTest, Speculation) {
   // Use the activation performance metric as a proxy for how many times
   // activation computation occurred.
diff --git a/components/translate/core/browser/translate_manager.cc b/components/translate/core/browser/translate_manager.cc
index 46fa3d2..8a83711 100644
--- a/components/translate/core/browser/translate_manager.cc
+++ b/components/translate/core/browser/translate_manager.cc
@@ -16,6 +16,7 @@
 #include "base/strings/string_split.h"
 #include "base/strings/stringprintf.h"
 #include "base/time/time.h"
+#include "build/build_config.h"
 #include "components/language/core/browser/language_model.h"
 #include "components/language/core/common/language_experiments.h"
 #include "components/language/core/common/language_util.h"
@@ -282,13 +283,12 @@
         TranslateErrors::IDENTICAL_LANGUAGES, triggered_from_menu);
     NotifyTranslateError(TranslateErrors::IDENTICAL_LANGUAGES);
     return;
-  } else {
-    // Trigger the "translating now" UI.
-    translate_client_->ShowTranslateUI(
-        translate::TRANSLATE_STEP_TRANSLATING, source_lang, target_lang,
-        TranslateErrors::NONE, triggered_from_menu);
   }
 
+  translate_client_->ShowTranslateUI(
+      translate::TRANSLATE_STEP_TRANSLATING, source_lang, target_lang,
+      TranslateErrors::NONE, triggered_from_menu);
+
   TranslateScript* script = TranslateDownloadManager::GetInstance()->script();
   DCHECK(script != nullptr);
 
diff --git a/components/viz/service/display/display.cc b/components/viz/service/display/display.cc
index 766c36b..ca61367 100644
--- a/components/viz/service/display/display.cc
+++ b/components/viz/service/display/display.cc
@@ -10,6 +10,7 @@
 #include "base/debug/dump_without_crashing.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/numerics/checked_math.h"
+#include "base/optional.h"
 #include "base/timer/elapsed_timer.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
@@ -486,6 +487,7 @@
   bool should_draw = have_copy_requests || (have_damage && size_matches);
   client_->DisplayWillDrawAndSwap(should_draw, &frame.render_pass_list);
 
+  base::Optional<base::ElapsedTimer> draw_timer;
   if (should_draw) {
     TRACE_EVENT_ASYNC_STEP_INTO0("viz,benchmark",
                                  "Graphics.Pipeline.DrawAndSwap",
@@ -506,16 +508,16 @@
       DCHECK(!disable_image_filtering);
     }
 
-    base::ElapsedTimer draw_timer;
+    draw_timer.emplace();
     renderer_->DecideRenderPassAllocationsForFrame(frame.render_pass_list);
     renderer_->DrawFrame(&frame.render_pass_list, device_scale_factor_,
                          current_surface_size);
     if (software_renderer_) {
       UMA_HISTOGRAM_COUNTS_1M("Compositing.DirectRenderer.Software.DrawFrameUs",
-                              draw_timer.Elapsed().InMicroseconds());
+                              draw_timer->Elapsed().InMicroseconds());
     } else {
       UMA_HISTOGRAM_COUNTS_1M("Compositing.DirectRenderer.GL.DrawFrameUs",
-                              draw_timer.Elapsed().InMicroseconds());
+                              draw_timer->Elapsed().InMicroseconds());
     }
   } else {
     TRACE_EVENT_INSTANT0("viz", "Draw skipped.", TRACE_EVENT_SCOPE_THREAD);
@@ -526,6 +528,7 @@
     TRACE_EVENT_ASYNC_STEP_INTO0("viz,benchmark",
                                  "Graphics.Pipeline.DrawAndSwap",
                                  swapped_trace_id_, "Swap");
+    draw_start_times_pending_swap_ack_.emplace_back(draw_timer->Begin());
     swapped_since_resize_ = true;
 
     if (scheduler_) {
@@ -597,6 +600,8 @@
 }
 
 void Display::DidReceiveSwapBuffersAck(const gfx::SwapTimings& timings) {
+  DCHECK(!draw_start_times_pending_swap_ack_.empty());
+
   if (scheduler_) {
     scheduler_->DidReceiveSwapBuffersAck();
     if (no_pending_swaps_callback_ && scheduler_->pending_swaps() == 0)
@@ -616,8 +621,16 @@
   // the swap was triggered. Note that not all output surfaces provide timing
   // information, hence the check for a valid swap_start.
   const auto swap_time = pending_presented_callbacks_.front().first;
-  if (!timings.swap_start.is_null())
+  if (!timings.swap_start.is_null()) {
     DCHECK_LE(swap_time, timings.swap_start);
+    base::TimeDelta delta =
+        timings.swap_start - draw_start_times_pending_swap_ack_.front();
+    UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
+        "Compositing.Display.DrawToSwapUs", delta,
+        base::TimeDelta::FromMicroseconds(100),
+        base::TimeDelta::FromMilliseconds(100), 50);
+  }
+  draw_start_times_pending_swap_ack_.pop_front();
 }
 
 void Display::DidReceiveTextureInUseResponses(
diff --git a/components/viz/service/display/display.h b/components/viz/service/display/display.h
index 289e775..cf97b87f 100644
--- a/components/viz/service/display/display.h
+++ b/components/viz/service/display/display.h
@@ -211,6 +211,8 @@
   int64_t swapped_trace_id_ = 0;
   int64_t last_presented_trace_id_ = 0;
 
+  base::circular_deque<base::TimeTicks> draw_start_times_pending_swap_ack_;
+
   DISALLOW_COPY_AND_ASSIGN(Display);
 };
 
diff --git a/components/viz/service/display_embedder/buffer_queue.cc b/components/viz/service/display_embedder/buffer_queue.cc
index 83545a7..e874cd7 100644
--- a/components/viz/service/display_embedder/buffer_queue.cc
+++ b/components/viz/service/display_embedder/buffer_queue.cc
@@ -137,40 +137,6 @@
   return true;
 }
 
-unsigned BufferQueue::RecreateBuffers() {
-  // We need to recreate the buffers, for whatever reason the old ones are not
-  // presentable on the device anymore.
-  // Unused buffers can be freed directly, they will be re-allocated as needed.
-  // Any in flight, current or displayed surface must be replaced.
-  available_surfaces_.clear();
-
-  // Recreate all in-flight surfaces and put the recreated copies in the queue.
-  for (auto& surface : in_flight_surfaces_)
-    surface = RecreateBuffer(std::move(surface));
-
-  current_surface_ = RecreateBuffer(std::move(current_surface_));
-  displayed_surface_ = RecreateBuffer(std::move(displayed_surface_));
-
-  return current_surface_ ? current_surface_->texture : 0u;
-}
-
-std::unique_ptr<BufferQueue::AllocatedSurface> BufferQueue::RecreateBuffer(
-    std::unique_ptr<AllocatedSurface> surface) {
-  if (!surface)
-    return nullptr;
-
-  std::unique_ptr<AllocatedSurface> new_surface(GetNextSurface());
-  if (!new_surface)
-    return nullptr;
-
-  new_surface->damage = surface->damage;
-
-  // Copy the entire texture.
-  CopyBufferDamage(new_surface->texture, surface->texture, gfx::Rect(),
-                   gfx::Rect(size_));
-  return new_surface;
-}
-
 void BufferQueue::PageFlipComplete() {
   DCHECK(!in_flight_surfaces_.empty());
   if (in_flight_surfaces_.front()) {
@@ -227,7 +193,7 @@
   }
 
   // We don't want to allow anything more than triple buffering.
-  DCHECK_LT(allocated_count_, 4U);
+  DCHECK_LT(allocated_count_, 3U);
   std::unique_ptr<gfx::GpuMemoryBuffer> buffer(
       gpu_memory_buffer_manager_->CreateGpuMemoryBuffer(
           size_, format_, gfx::BufferUsage::SCANOUT, surface_handle_));
diff --git a/components/viz/service/display_embedder/buffer_queue.h b/components/viz/service/display_embedder/buffer_queue.h
index 2abf9dca..4baa6acb 100644
--- a/components/viz/service/display_embedder/buffer_queue.h
+++ b/components/viz/service/display_embedder/buffer_queue.h
@@ -66,6 +66,10 @@
   // be marked as displayed.
   void PageFlipComplete();
 
+  // Destroys all the buffers (useful if for some reason, the buffers are no
+  // longer presentable).
+  void FreeAllSurfaces();
+
   // Frees all buffers if |size|, |color_space|, or |use_stencil| correspond to
   // a change of state. Otherwise, it's a no-op. Returns true if there was a
   // change of state, false otherwise.
@@ -74,11 +78,6 @@
                const gfx::ColorSpace& color_space,
                bool use_stencil);
 
-  // Recreates all the buffers (useful if for some reason, the old buffers are
-  // no longer presentable). Returns the name of the texture corresponding to
-  // the current buffer or 0u if there is no current buffer.
-  unsigned RecreateBuffers();
-
   // Copies the damage from the most recently swapped available buffer into the
   // current buffer.
   void CopyDamageForCurrentSurface(const gfx::Rect& damage);
@@ -107,8 +106,6 @@
     gfx::Rect damage;  // This is the damage for this frame from the previous.
   };
 
-  void FreeAllSurfaces();
-
   void FreeSurfaceResources(AllocatedSurface* surface);
 
   // Copy everything that is in |copy_rect|, except for what is in
@@ -123,9 +120,6 @@
   // Return a surface, available to be drawn into.
   std::unique_ptr<AllocatedSurface> GetNextSurface();
 
-  std::unique_ptr<AllocatedSurface> RecreateBuffer(
-      std::unique_ptr<AllocatedSurface> surface);
-
   gpu::gles2::GLES2Interface* const gl_;
   gfx::Size size_;
   gfx::ColorSpace color_space_;
diff --git a/components/viz/service/display_embedder/buffer_queue_unittest.cc b/components/viz/service/display_embedder/buffer_queue_unittest.cc
index c4602db..edd31791 100644
--- a/components/viz/service/display_embedder/buffer_queue_unittest.cc
+++ b/components/viz/service/display_embedder/buffer_queue_unittest.cc
@@ -586,79 +586,6 @@
             gpu_memory_buffer_manager_->set_color_space_count());
 }
 
-TEST_F(BufferQueueMockedContextTest, RecreateBuffers) {
-  // This setup is to easily get one frame in each of:
-  // - currently bound for drawing.
-  // - in flight to GPU.
-  // - currently displayed.
-  // - free frame.
-  // This tests buffers in all states.
-  // Bind/swap pushes frames into the in flight list, then the PageFlipComplete
-  // calls pull one frame into displayed and another into the free list.
-  EXPECT_TRUE(
-      output_surface_->Reshape(screen_size, 1.0f, gfx::ColorSpace(), false));
-  // TODO(andrescj): fix a bunch of uninteresting mock function calls.
-  unsigned stencil;
-  EXPECT_GT(output_surface_->GetCurrentBuffer(&stencil), 0u);
-  SwapBuffers();
-  EXPECT_GT(output_surface_->GetCurrentBuffer(&stencil), 0u);
-  SwapBuffers();
-  EXPECT_GT(output_surface_->GetCurrentBuffer(&stencil), 0u);
-  SwapBuffers();
-  EXPECT_GT(output_surface_->GetCurrentBuffer(&stencil), 0u);
-  output_surface_->PageFlipComplete();
-  output_surface_->PageFlipComplete();
-  // We should have one buffer in each possible state right now, including one
-  // being drawn to.
-  ASSERT_EQ(1U, in_flight_surfaces().size());
-  ASSERT_EQ(1U, available_surfaces().size());
-  EXPECT_TRUE(displayed_frame());
-  EXPECT_TRUE(current_frame());
-
-  auto* current = current_frame();
-  auto* displayed = displayed_frame();
-  auto* in_flight = in_flight_surfaces().front().get();
-  auto* available = available_surfaces().front().get();
-
-  // Expect all 4 images to be destroyed, 3 of the existing textures to be
-  // copied from and 3 new images to be created.
-  EXPECT_CALL(*context_,
-              CreateImageCHROMIUM(_, screen_size.width(), screen_size.height(),
-                                  kBufferQueueInternalformat))
-      .Times(3);
-  Expectation copy1 = EXPECT_CALL(*mock_output_surface_,
-                                  CopyBufferDamage(_, displayed->texture, _, _))
-                          .Times(1);
-  Expectation copy2 = EXPECT_CALL(*mock_output_surface_,
-                                  CopyBufferDamage(_, current->texture, _, _))
-                          .Times(1);
-  Expectation copy3 = EXPECT_CALL(*mock_output_surface_,
-                                  CopyBufferDamage(_, in_flight->texture, _, _))
-                          .Times(1);
-
-  EXPECT_CALL(*context_, DestroyImageCHROMIUM(displayed->image))
-      .Times(1)
-      .After(copy1);
-  EXPECT_CALL(*context_, DestroyImageCHROMIUM(current->image))
-      .Times(1)
-      .After(copy2);
-  EXPECT_CALL(*context_, DestroyImageCHROMIUM(in_flight->image))
-      .Times(1)
-      .After(copy3);
-  EXPECT_CALL(*context_, DestroyImageCHROMIUM(available->image)).Times(1);
-
-  output_surface_->RecreateBuffers();
-  testing::Mock::VerifyAndClearExpectations(context_);
-  testing::Mock::VerifyAndClearExpectations(mock_output_surface_);
-
-  // All free buffers should be destroyed, the remaining buffers should all
-  // be replaced but still valid.
-  EXPECT_EQ(1U, in_flight_surfaces().size());
-  EXPECT_EQ(0U, available_surfaces().size());
-  EXPECT_TRUE(displayed_frame());
-  EXPECT_TRUE(current_frame());
-}
-
 TEST_F(BufferQueueTest, AllocateFails) {
   EXPECT_TRUE(
       output_surface_->Reshape(screen_size, 1.0f, gfx::ColorSpace(), false));
diff --git a/components/viz/service/display_embedder/gl_output_surface_buffer_queue.cc b/components/viz/service/display_embedder/gl_output_surface_buffer_queue.cc
index 8ab97f3e..01db6191 100644
--- a/components/viz/service/display_embedder/gl_output_surface_buffer_queue.cc
+++ b/components/viz/service/display_embedder/gl_output_surface_buffer_queue.cc
@@ -144,9 +144,7 @@
   if (response.result == gfx::SwapResult::SWAP_NAK_RECREATE_BUFFERS) {
     // Even through the swap failed, this is a fixable error so we can pretend
     // it succeeded to the rest of the system.
-    unsigned current_surface_texture = buffer_queue_->RecreateBuffers();
-    if (current_surface_texture)
-      BindFramebuffer();
+    buffer_queue_->FreeAllSurfaces();
     force_swap = true;
   }
 
diff --git a/components/viz/service/display_embedder/skia_output_device.cc b/components/viz/service/display_embedder/skia_output_device.cc
index a07b5b1..5d778e4 100644
--- a/components/viz/service/display_embedder/skia_output_device.cc
+++ b/components/viz/service/display_embedder/skia_output_device.cc
@@ -21,11 +21,10 @@
 
 gfx::SwapResponse SkiaOutputDevice::PostSubBuffer(
     const gfx::Rect& rect,
-    const GrBackendSemaphore& semaphore,
     BufferPresentedCallback feedback) {
   NOTREACHED();
   StartSwapBuffers(std::move(feedback));
-  return FinishSwapBuffers(gfx::SwapResult::SWAP_FAILED);
+  return FinishSwapBuffers(gfx::SwapResult::SWAP_FAILED, gfx::Size());
 }
 
 void SkiaOutputDevice::SetDrawRectangle(const gfx::Rect& draw_rectangle) {}
@@ -41,13 +40,13 @@
   params_->swap_response.timings.swap_start = base::TimeTicks::Now();
 }
 
-gfx::SwapResponse SkiaOutputDevice::FinishSwapBuffers(gfx::SwapResult result) {
+gfx::SwapResponse SkiaOutputDevice::FinishSwapBuffers(gfx::SwapResult result,
+                                                      const gfx::Size& size) {
   DCHECK(params_);
 
   params_->swap_response.result = result;
   params_->swap_response.timings.swap_end = base::TimeTicks::Now();
-  did_swap_buffer_complete_callback_.Run(
-      *params_, gfx::Size(draw_surface_->width(), draw_surface_->height()));
+  did_swap_buffer_complete_callback_.Run(*params_, size);
 
   if (feedback_) {
     std::move(*feedback_)
diff --git a/components/viz/service/display_embedder/skia_output_device.h b/components/viz/service/display_embedder/skia_output_device.h
index 0c49d762..75c91d9 100644
--- a/components/viz/service/display_embedder/skia_output_device.h
+++ b/components/viz/service/display_embedder/skia_output_device.h
@@ -11,9 +11,9 @@
 #include "components/viz/service/display/output_surface.h"
 #include "gpu/command_buffer/common/swap_buffers_complete_params.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
+#include "third_party/skia/src/gpu/GrSemaphore.h"
 #include "ui/gfx/swap_result.h"
 
-class GrBackendSemaphore;
 class SkSurface;
 
 namespace gfx {
@@ -27,6 +27,29 @@
 
 class SkiaOutputDevice {
  public:
+  // A helper class for defining a BeginPaint() and EndPaint() scope.
+  class ScopedPaint {
+   public:
+    explicit ScopedPaint(SkiaOutputDevice* device)
+        : device_(device), sk_surface_(device->BeginPaint()) {
+      DCHECK(sk_surface_);
+    }
+    ~ScopedPaint() { device_->EndPaint(semaphore_); }
+
+    SkSurface* sk_surface() const { return sk_surface_; }
+    void set_semaphore(const GrBackendSemaphore& semaphore) {
+      DCHECK(!semaphore_.isInitialized());
+      semaphore_ = semaphore;
+    }
+
+   private:
+    SkiaOutputDevice* const device_;
+    SkSurface* const sk_surface_;
+    GrBackendSemaphore semaphore_;
+
+    DISALLOW_COPY_AND_ASSIGN(ScopedPaint);
+  };
+
   using BufferPresentedCallback =
       base::OnceCallback<void(const gfx::PresentationFeedback& feedback)>;
   using DidSwapBufferCompleteCallback =
@@ -37,20 +60,15 @@
       DidSwapBufferCompleteCallback did_swap_buffer_complete_callback);
   virtual ~SkiaOutputDevice();
 
-  // SkSurface that can be drawn to.
-  SkSurface* draw_surface() const { return draw_surface_.get(); }
-
   // Changes the size of draw surface and invalidates it's contents.
   virtual void Reshape(const gfx::Size& size,
                        float device_scale_factor,
                        const gfx::ColorSpace& color_space,
                        bool has_alpha) = 0;
 
-  // Presents DrawSurface.
-  virtual gfx::SwapResponse SwapBuffers(const GrBackendSemaphore& semaphore,
-                                        BufferPresentedCallback feedback) = 0;
+  // Presents the back buffer.
+  virtual gfx::SwapResponse SwapBuffers(BufferPresentedCallback feedback) = 0;
   virtual gfx::SwapResponse PostSubBuffer(const gfx::Rect& rect,
-                                          const GrBackendSemaphore& semaphore,
                                           BufferPresentedCallback feedback);
 
   // Set the rectangle that will be drawn into on the surface.
@@ -69,13 +87,23 @@
   bool need_swap_semaphore() const { return need_swap_semaphore_; }
 
  protected:
-  void StartSwapBuffers(base::Optional<BufferPresentedCallback> feedback);
-  gfx::SwapResponse FinishSwapBuffers(gfx::SwapResult result);
+  // Begin paint the back buffer.
+  virtual SkSurface* BeginPaint() = 0;
 
-  sk_sp<SkSurface> draw_surface_;
+  // End paint the back buffer.
+  virtual void EndPaint(const GrBackendSemaphore& semaphore) = 0;
+
+  // Helper method for SwapBuffers() and PostSubBuffer(). It should be called
+  // at the beginning of SwapBuffers() and PostSubBuffer() implementations
+  void StartSwapBuffers(base::Optional<BufferPresentedCallback> feedback);
+
+  // Helper method for SwapBuffers() and PostSubBuffer(). It should be called
+  // at the end of SwapBuffers() and PostSubBuffer() implementations
+  gfx::SwapResponse FinishSwapBuffers(gfx::SwapResult result,
+                                      const gfx::Size& size);
+
   OutputSurface::Capabilities capabilities_;
 
- private:
   const bool need_swap_semaphore_;
   uint64_t swap_id_ = 0;
   DidSwapBufferCompleteCallback did_swap_buffer_complete_callback_;
diff --git a/components/viz/service/display_embedder/skia_output_device_gl.cc b/components/viz/service/display_embedder/skia_output_device_gl.cc
index ad4d1cfb7..d31721a8 100644
--- a/components/viz/service/display_embedder/skia_output_device_gl.cc
+++ b/components/viz/service/display_embedder/skia_output_device_gl.cc
@@ -94,28 +94,30 @@
                                                : kBottomLeft_GrSurfaceOrigin;
   auto color_type =
       supports_alpha_ ? kRGBA_8888_SkColorType : kRGB_888x_SkColorType;
-  draw_surface_ = SkSurface::MakeFromBackendRenderTarget(
+  sk_surface_ = SkSurface::MakeFromBackendRenderTarget(
       gr_context_, render_target, origin, color_type,
       color_space.ToSkColorSpace(), &surface_props);
-  DCHECK(draw_surface_);
+  DCHECK(sk_surface_);
 }
 
 gfx::SwapResponse SkiaOutputDeviceGL::SwapBuffers(
-    const GrBackendSemaphore& semaphore,
     BufferPresentedCallback feedback) {
   // TODO(backer): Support SwapBuffersAsync
   StartSwapBuffers({});
-  return FinishSwapBuffers(gl_surface_->SwapBuffers(std::move(feedback)));
+  return FinishSwapBuffers(
+      gl_surface_->SwapBuffers(std::move(feedback)),
+      gfx::Size(sk_surface_->width(), sk_surface_->height()));
 }
 
 gfx::SwapResponse SkiaOutputDeviceGL::PostSubBuffer(
     const gfx::Rect& rect,
-    const GrBackendSemaphore& semaphore,
     BufferPresentedCallback feedback) {
   // TODO(backer): Support PostSubBufferAsync
   StartSwapBuffers({});
-  return FinishSwapBuffers(gl_surface_->PostSubBuffer(
-      rect.x(), rect.y(), rect.width(), rect.height(), std::move(feedback)));
+  return FinishSwapBuffers(
+      gl_surface_->PostSubBuffer(rect.x(), rect.y(), rect.width(),
+                                 rect.height(), std::move(feedback)),
+      gfx::Size(sk_surface_->width(), sk_surface_->height()));
 }
 
 void SkiaOutputDeviceGL::SetDrawRectangle(const gfx::Rect& draw_rectangle) {
@@ -130,6 +132,13 @@
   gl_surface_->SetBackbufferAllocation(false);
 }
 
+SkSurface* SkiaOutputDeviceGL::BeginPaint() {
+  DCHECK(sk_surface_);
+  return sk_surface_.get();
+}
+
+void SkiaOutputDeviceGL::EndPaint(const GrBackendSemaphore& semaphore) {}
+
 #if defined(OS_WIN)
 void SkiaOutputDeviceGL::DidCreateAcceleratedSurfaceChildWindow(
     gpu::SurfaceHandle parent_window,
diff --git a/components/viz/service/display_embedder/skia_output_device_gl.h b/components/viz/service/display_embedder/skia_output_device_gl.h
index 2263b8b..e640e36 100644
--- a/components/viz/service/display_embedder/skia_output_device_gl.h
+++ b/components/viz/service/display_embedder/skia_output_device_gl.h
@@ -53,14 +53,14 @@
                float device_scale_factor,
                const gfx::ColorSpace& color_space,
                bool has_alpha) override;
-  gfx::SwapResponse SwapBuffers(const GrBackendSemaphore& semaphore,
-                                BufferPresentedCallback feedback) override;
+  gfx::SwapResponse SwapBuffers(BufferPresentedCallback feedback) override;
   gfx::SwapResponse PostSubBuffer(const gfx::Rect& rect,
-                                  const GrBackendSemaphore& semaphore,
                                   BufferPresentedCallback feedback) override;
   void SetDrawRectangle(const gfx::Rect& draw_rectangle) override;
   void EnsureBackbuffer() override;
   void DiscardBackbuffer() override;
+  SkSurface* BeginPaint() override;
+  void EndPaint(const GrBackendSemaphore& semaphore) override;
 
   // gpu::ImageTransportSurfaceDelegate implementation:
 #if defined(OS_WIN)
@@ -82,6 +82,8 @@
   scoped_refptr<gl::GLSurface> gl_surface_;
   GrContext* gr_context_ = nullptr;
 
+  sk_sp<SkSurface> sk_surface_;
+
   bool supports_alpha_ = false;
 
   base::WeakPtrFactory<SkiaOutputDeviceGL> weak_ptr_factory_{this};
diff --git a/components/viz/service/display_embedder/skia_output_device_offscreen.cc b/components/viz/service/display_embedder/skia_output_device_offscreen.cc
index e676c698..8cd89133 100644
--- a/components/viz/service/display_embedder/skia_output_device_offscreen.cc
+++ b/components/viz/service/display_embedder/skia_output_device_offscreen.cc
@@ -35,40 +35,40 @@
       SkImageInfo::Make(size.width(), size.height(), kRGBA_8888_SkColorType,
                         has_alpha_ ? kPremul_SkAlphaType : kOpaque_SkAlphaType,
                         color_space.ToSkColorSpace());
-  draw_surface_ = SkSurface::MakeRenderTarget(
+  sk_surface_ = SkSurface::MakeRenderTarget(
       gr_context_, SkBudgeted::kNo, image_info_, 0 /* sampleCount */,
       capabilities_.flipped_output_surface ? kTopLeft_GrSurfaceOrigin
                                            : kBottomLeft_GrSurfaceOrigin,
       nullptr /* surfaceProps */);
-  DCHECK(!!draw_surface_);
+  DCHECK(!!sk_surface_);
 
   // Initialize alpha channel to opaque.
   if (!has_alpha_) {
-    auto* canvas = draw_surface_->getCanvas();
+    auto* canvas = sk_surface_->getCanvas();
     canvas->clear(SkColorSetARGB(255, 0, 0, 0));
   }
 }
 
-gfx::SwapResponse SkiaOutputDeviceOffscreen::PostSubBuffer(
-    const gfx::Rect& rect,
-    const GrBackendSemaphore& semaphore,
-    BufferPresentedCallback feedback) {
-  return SwapBuffers(semaphore, std::move(feedback));
-}
-
 gfx::SwapResponse SkiaOutputDeviceOffscreen::SwapBuffers(
-    const GrBackendSemaphore& semaphore,
     BufferPresentedCallback feedback) {
   // Reshape should have been called first.
-  DCHECK(draw_surface_);
+  DCHECK(sk_surface_);
 
   StartSwapBuffers(std::move(feedback));
-  return FinishSwapBuffers(gfx::SwapResult::SWAP_ACK);
+  return FinishSwapBuffers(
+      gfx::SwapResult::SWAP_ACK,
+      gfx::Size(sk_surface_->width(), sk_surface_->height()));
+}
+
+gfx::SwapResponse SkiaOutputDeviceOffscreen::PostSubBuffer(
+    const gfx::Rect& rect,
+    BufferPresentedCallback feedback) {
+  return SwapBuffers(std::move(feedback));
 }
 
 void SkiaOutputDeviceOffscreen::EnsureBackbuffer() {
-  if (!image_info_.isEmpty() && !draw_surface_) {
-    draw_surface_ = SkSurface::MakeRenderTarget(
+  if (!image_info_.isEmpty() && !sk_surface_) {
+    sk_surface_ = SkSurface::MakeRenderTarget(
         gr_context_, SkBudgeted::kNo, image_info_, 0 /* sampleCount */,
         capabilities_.flipped_output_surface ? kTopLeft_GrSurfaceOrigin
                                              : kBottomLeft_GrSurfaceOrigin,
@@ -77,7 +77,13 @@
 }
 
 void SkiaOutputDeviceOffscreen::DiscardBackbuffer() {
-  draw_surface_.reset();
+  sk_surface_.reset();
 }
 
+SkSurface* SkiaOutputDeviceOffscreen::BeginPaint() {
+  return sk_surface_.get();
+}
+
+void SkiaOutputDeviceOffscreen::EndPaint(const GrBackendSemaphore& semaphore) {}
+
 }  // namespace viz
diff --git a/components/viz/service/display_embedder/skia_output_device_offscreen.h b/components/viz/service/display_embedder/skia_output_device_offscreen.h
index a1f38d7..bebd366 100644
--- a/components/viz/service/display_embedder/skia_output_device_offscreen.h
+++ b/components/viz/service/display_embedder/skia_output_device_offscreen.h
@@ -27,18 +27,18 @@
                float device_scale_factor,
                const gfx::ColorSpace& color_space,
                bool has_alpha) override;
-  gfx::SwapResponse SwapBuffers(const GrBackendSemaphore& semaphore,
-                                BufferPresentedCallback feedback) override;
+  gfx::SwapResponse SwapBuffers(BufferPresentedCallback feedback) override;
   gfx::SwapResponse PostSubBuffer(const gfx::Rect& rect,
-                                  const GrBackendSemaphore& semaphore,
-
                                   BufferPresentedCallback feedback) override;
   void EnsureBackbuffer() override;
   void DiscardBackbuffer() override;
+  SkSurface* BeginPaint() override;
+  void EndPaint(const GrBackendSemaphore& semaphore) override;
 
  protected:
   GrContext* const gr_context_;
   const bool has_alpha_;
+  sk_sp<SkSurface> sk_surface_;
 
  private:
   SkImageInfo image_info_;
diff --git a/components/viz/service/display_embedder/skia_output_device_vulkan.cc b/components/viz/service/display_embedder/skia_output_device_vulkan.cc
index 403f3ca..c9f1f1f 100644
--- a/components/viz/service/display_embedder/skia_output_device_vulkan.cc
+++ b/components/viz/service/display_embedder/skia_output_device_vulkan.cc
@@ -32,7 +32,7 @@
 }
 
 SkiaOutputDeviceVulkan::~SkiaOutputDeviceVulkan() {
-  scoped_write_.reset();
+  DCHECK(!scoped_write_);
   if (vulkan_surface_) {
     auto* fence_helper = context_provider_->GetDeviceQueue()->GetFenceHelper();
     fence_helper->EnqueueVulkanObjectCleanupForSubmittedWork(
@@ -44,75 +44,45 @@
                                      float device_scale_factor,
                                      const gfx::ColorSpace& color_space,
                                      bool has_alpha) {
-  if (!vulkan_surface_)
-    CreateVulkanSurface();
+  DCHECK(!scoped_write_);
 
-  scoped_write_.reset();
-  auto old_size = vulkan_surface_->size();
+  uint32_t generation = 0;
+  if (!vulkan_surface_) {
+    CreateVulkanSurface();
+  } else {
+    generation = vulkan_surface_->swap_chain_generation();
+  }
+
   vulkan_surface_->SetSize(size);
-  if (vulkan_surface_->size() != old_size) {
-    // Size has been changed, we need to clear all surfaces which will be
-    // recreated later.
+
+  if (vulkan_surface_->swap_chain_generation() != generation) {
+    // swapchain is changed, we need recreate all cached sk surfaces.
     sk_surfaces_.clear();
     sk_surfaces_.resize(vulkan_surface_->GetSwapChain()->num_images());
   }
-
-  UpdateDrawSurface();
 }
 
 gfx::SwapResponse SkiaOutputDeviceVulkan::SwapBuffers(
-    const GrBackendSemaphore& semaphore,
     BufferPresentedCallback feedback) {
   // Reshape should have been called first.
   DCHECK(vulkan_surface_);
-  DCHECK(draw_surface_);
-  DCHECK(scoped_write_);
+  DCHECK(!scoped_write_);
 
   StartSwapBuffers(std::move(feedback));
-  auto backend = draw_surface_->getBackendRenderTarget(
-      SkSurface::kFlushRead_BackendHandleAccess);
-  GrVkImageInfo vk_image_info;
-  if (!backend.getVkImageInfo(&vk_image_info))
-    NOTREACHED() << "Failed to get the image info.";
-  scoped_write_->set_image_layout(vk_image_info.fImageLayout);
-  scoped_write_->SetEndSemaphore(semaphore.vkSemaphore());
-  scoped_write_.reset();
-
-  auto response = FinishSwapBuffers(vulkan_surface_->SwapBuffers());
-  UpdateDrawSurface();
-
+  auto size = vulkan_surface_->size();
+  auto response = FinishSwapBuffers(vulkan_surface_->SwapBuffers(), size);
   return response;
 }
 
-void SkiaOutputDeviceVulkan::CreateVulkanSurface() {
-  gfx::AcceleratedWidget accelerated_widget = gfx::kNullAcceleratedWidget;
-#if defined(OS_ANDROID)
-  bool can_be_used_with_surface_control = false;
-  accelerated_widget =
-      gpu::GpuSurfaceLookup::GetInstance()->AcquireNativeWidget(
-          surface_handle_, &can_be_used_with_surface_control);
-#else
-  accelerated_widget = surface_handle_;
-#endif
-  auto vulkan_surface =
-      context_provider_->GetVulkanImplementation()->CreateViewSurface(
-          accelerated_widget);
-  if (!vulkan_surface)
-    LOG(FATAL) << "Failed to create vulkan surface.";
-  if (!vulkan_surface->Initialize(context_provider_->GetDeviceQueue(),
-                                  gpu::VulkanSurface::FORMAT_RGBA_32)) {
-    LOG(FATAL) << "Failed to initialize vulkan surface.";
-  }
-  vulkan_surface_ = std::move(vulkan_surface);
-  sk_surfaces_.resize(vulkan_surface_->GetSwapChain()->num_images());
-}
-
-void SkiaOutputDeviceVulkan::UpdateDrawSurface() {
+SkSurface* SkiaOutputDeviceVulkan::BeginPaint() {
   DCHECK(vulkan_surface_);
   DCHECK(!scoped_write_);
 
   scoped_write_.emplace(vulkan_surface_->GetSwapChain());
-
+  if (!scoped_write_->success()) {
+    scoped_write_.reset();
+    return nullptr;
+  }
   auto& sk_surface = sk_surfaces_[scoped_write_->image_index()];
 
   if (!sk_surface) {
@@ -140,11 +110,51 @@
         SkSurface::kFlushRead_BackendHandleAccess);
     backend.setVkImageLayout(scoped_write_->image_layout());
   }
-  GrBackendSemaphore semaphore;
-  semaphore.initVulkan(scoped_write_->TakeBeginSemaphore());
-  auto result = sk_surface->wait(1, &semaphore);
-  DCHECK(result);
-  draw_surface_ = sk_surface;
+  VkSemaphore vk_semaphore = scoped_write_->TakeBeginSemaphore();
+  if (vk_semaphore != VK_NULL_HANDLE) {
+    GrBackendSemaphore semaphore;
+    semaphore.initVulkan(vk_semaphore);
+    auto result = sk_surface->wait(1, &semaphore);
+    DCHECK(result);
+  }
+  return sk_surface.get();
+}
+
+void SkiaOutputDeviceVulkan::EndPaint(const GrBackendSemaphore& semaphore) {
+  DCHECK(scoped_write_);
+
+  auto& sk_surface = sk_surfaces_[scoped_write_->image_index()];
+  auto backend = sk_surface->getBackendRenderTarget(
+      SkSurface::kFlushRead_BackendHandleAccess);
+  GrVkImageInfo vk_image_info;
+  if (!backend.getVkImageInfo(&vk_image_info))
+    NOTREACHED() << "Failed to get the image info.";
+  scoped_write_->set_image_layout(vk_image_info.fImageLayout);
+  if (semaphore.isInitialized())
+    scoped_write_->SetEndSemaphore(semaphore.vkSemaphore());
+  scoped_write_.reset();
+}
+
+void SkiaOutputDeviceVulkan::CreateVulkanSurface() {
+  gfx::AcceleratedWidget accelerated_widget = gfx::kNullAcceleratedWidget;
+#if defined(OS_ANDROID)
+  bool can_be_used_with_surface_control = false;
+  accelerated_widget =
+      gpu::GpuSurfaceLookup::GetInstance()->AcquireNativeWidget(
+          surface_handle_, &can_be_used_with_surface_control);
+#else
+  accelerated_widget = surface_handle_;
+#endif
+  auto vulkan_surface =
+      context_provider_->GetVulkanImplementation()->CreateViewSurface(
+          accelerated_widget);
+  if (!vulkan_surface)
+    LOG(FATAL) << "Failed to create vulkan surface.";
+  if (!vulkan_surface->Initialize(context_provider_->GetDeviceQueue(),
+                                  gpu::VulkanSurface::FORMAT_RGBA_32)) {
+    LOG(FATAL) << "Failed to initialize vulkan surface.";
+  }
+  vulkan_surface_ = std::move(vulkan_surface);
 }
 
 }  // namespace viz
diff --git a/components/viz/service/display_embedder/skia_output_device_vulkan.h b/components/viz/service/display_embedder/skia_output_device_vulkan.h
index cc35ef7..1c45ecb 100644
--- a/components/viz/service/display_embedder/skia_output_device_vulkan.h
+++ b/components/viz/service/display_embedder/skia_output_device_vulkan.h
@@ -35,12 +35,13 @@
                float device_scale_factor,
                const gfx::ColorSpace& color_space,
                bool has_alpha) override;
-  gfx::SwapResponse SwapBuffers(const GrBackendSemaphore& semaphore,
-                                BufferPresentedCallback feedback) override;
+  gfx::SwapResponse SwapBuffers(BufferPresentedCallback feedback) override;
+  SkSurface* BeginPaint() override;
+  void EndPaint(const GrBackendSemaphore& semaphore) override;
 
  private:
   void CreateVulkanSurface();
-  void UpdateDrawSurface();
+  void CreateSkSurface();
 
   VulkanContextProvider* const context_provider_;
 
diff --git a/components/viz/service/display_embedder/skia_output_device_x11.cc b/components/viz/service/display_embedder/skia_output_device_x11.cc
index 9f20972..777348d1 100644
--- a/components/viz/service/display_embedder/skia_output_device_x11.cc
+++ b/components/viz/service/display_embedder/skia_output_device_x11.cc
@@ -53,16 +53,14 @@
 }
 
 gfx::SwapResponse SkiaOutputDeviceX11::SwapBuffers(
-    const GrBackendSemaphore& semaphore,
     BufferPresentedCallback feedback) {
   return PostSubBuffer(
-      gfx::Rect(0, 0, draw_surface_->width(), draw_surface_->height()),
-      semaphore, std::move(feedback));
+      gfx::Rect(0, 0, sk_surface_->width(), sk_surface_->height()),
+      std::move(feedback));
 }
 
 gfx::SwapResponse SkiaOutputDeviceX11::PostSubBuffer(
     const gfx::Rect& rect,
-    const GrBackendSemaphore& semaphore,
     BufferPresentedCallback feedback) {
   StartSwapBuffers(std::move(feedback));
 
@@ -70,7 +68,7 @@
       SkImageInfo::MakeN32(rect.width(), rect.height(), kOpaque_SkAlphaType);
   DCHECK_GE(pixels_.capacity(), ii.computeMinByteSize());
   SkPixmap sk_pixmap(ii, pixels_.data(), ii.minRowBytes());
-  bool result = draw_surface_->readPixels(sk_pixmap, rect.x(), rect.y());
+  bool result = sk_surface_->readPixels(sk_pixmap, rect.x(), rect.y());
   LOG_IF(FATAL, !result) << "Failed to read pixels from offscreen SkSurface.";
 
   if (bpp_ == 32 || bpp_ == 16) {
@@ -131,7 +129,9 @@
     NOTIMPLEMENTED();
   }
   XFlush(display_);
-  return FinishSwapBuffers(gfx::SwapResult::SWAP_ACK);
+  return FinishSwapBuffers(
+      gfx::SwapResult::SWAP_ACK,
+      gfx::Size(sk_surface_->width(), sk_surface_->height()));
 }
 
 }  // namespace viz
diff --git a/components/viz/service/display_embedder/skia_output_device_x11.h b/components/viz/service/display_embedder/skia_output_device_x11.h
index f599b7a..a82d72e7 100644
--- a/components/viz/service/display_embedder/skia_output_device_x11.h
+++ b/components/viz/service/display_embedder/skia_output_device_x11.h
@@ -27,10 +27,8 @@
                float device_scale_factor,
                const gfx::ColorSpace& color_space,
                bool has_alpha) override;
-  gfx::SwapResponse SwapBuffers(const GrBackendSemaphore& semaphore,
-                                BufferPresentedCallback feedback) override;
+  gfx::SwapResponse SwapBuffers(BufferPresentedCallback feedback) override;
   gfx::SwapResponse PostSubBuffer(const gfx::Rect& rect,
-                                  const GrBackendSemaphore& semaphore,
                                   BufferPresentedCallback feedback) override;
 
  private:
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
index d0f0cef..6539529 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
@@ -621,8 +621,11 @@
   output_device_->Reshape(size_, device_scale_factor, color_space, has_alpha);
 
   if (characterization) {
+    // Start a paint temporarily for getting sk surface characterization.
+    scoped_output_device_paint_.emplace(output_device_.get());
     output_sk_surface()->characterize(characterization);
     DCHECK(characterization->isValid());
+    scoped_output_device_paint_.reset();
   }
 }
 
@@ -635,6 +638,12 @@
     base::OnceClosure on_finished) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(ddl);
+  DCHECK(!scoped_output_device_paint_);
+
+  // We do not reset scoped_output_device_paint_ after drawing the ddl until
+  // SwapBuffers() is called, because we may need access to output_sk_surface()
+  // for CopyOutput().
+  scoped_output_device_paint_.emplace(output_device_.get());
   DCHECK(output_sk_surface());
 
   if (!MakeCurrent(true /* need_fbo0 */))
@@ -660,7 +669,8 @@
       DCHECK(result);
     }
 
-    output_sk_surface()->draw(ddl.get());
+    if (!output_sk_surface()->draw(ddl.get()))
+      DLOG(ERROR) << "output_sk_surface()->draw() failed.";
     ddl = nullptr;
 
     if (overdraw_ddl) {
@@ -704,10 +714,9 @@
       return;
     }
     if (output_device_->need_swap_semaphore()) {
-      DCHECK(!swap_buffers_semaphore_.isInitialized());
-      swap_buffers_semaphore_ =
-          scoped_promise_image_access.end_semaphores().back();
-      DCHECK(swap_buffers_semaphore_.isInitialized());
+      auto& semaphore = scoped_promise_image_access.end_semaphores().back();
+      DCHECK(semaphore.isInitialized());
+      scoped_output_device_paint_->set_semaphore(std::move(semaphore));
     }
   }
   ReleaseFenceSyncAndPushTextureUpdates(sync_fence_release);
@@ -715,11 +724,14 @@
 
 void SkiaOutputSurfaceImplOnGpu::SwapBuffers(OutputSurfaceFrame frame) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(output_sk_surface());
+  DCHECK(scoped_output_device_paint_);
+  DCHECK(output_device_);
+
+  scoped_output_device_paint_.reset();
+
   if (!MakeCurrent(!dependency_->IsOffscreen() /* need_fbo0 */))
     return;
 
-  DCHECK(output_device_);
   gfx::SwapResponse response;
   if (frame.sub_buffer_rect && frame.sub_buffer_rect->IsEmpty()) {
     // TODO(https://crbug.com/898680): Maybe do something for overlays here.
@@ -730,13 +742,10 @@
       frame.sub_buffer_rect->set_y(size_.height() - frame.sub_buffer_rect->y() -
                                    frame.sub_buffer_rect->height());
     response = output_device_->PostSubBuffer(*frame.sub_buffer_rect,
-                                             swap_buffers_semaphore_,
                                              buffer_presented_callback_);
   } else {
-    response = output_device_->SwapBuffers(swap_buffers_semaphore_,
-                                           buffer_presented_callback_);
+    response = output_device_->SwapBuffers(buffer_presented_callback_);
   }
-  swap_buffers_semaphore_ = GrBackendSemaphore();
 
   for (auto& latency : frame.latency_info) {
     latency.AddLatencyNumberWithTimestamp(
@@ -831,6 +840,8 @@
   // TODO(crbug.com/898595): Do this on the GPU instead of CPU with Vulkan.
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   bool from_fbo0 = !id;
+  DCHECK(scoped_output_device_paint_ || !from_fbo0);
+
   if (!MakeCurrent(true /* need_fbo0 */))
     return;
 
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
index a1146d65..f06fa850 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
@@ -175,7 +175,7 @@
   bool is_using_vulkan() const { return !!vulkan_context_provider_; }
 
   SkSurface* output_sk_surface() const {
-    return output_device_->draw_surface();
+    return scoped_output_device_paint_->sk_surface();
   }
 
   void CreateFallbackImage(ImageContext* context);
@@ -187,9 +187,9 @@
       shared_image_representation_factory_;
   VulkanContextProvider* const vulkan_context_provider_;
   const RendererSettings renderer_settings_;
-  DidSwapBufferCompleteCallback did_swap_buffer_complete_callback_;
-  BufferPresentedCallback buffer_presented_callback_;
-  ContextLostCallback context_lost_callback_;
+  const DidSwapBufferCompleteCallback did_swap_buffer_complete_callback_;
+  const BufferPresentedCallback buffer_presented_callback_;
+  const ContextLostCallback context_lost_callback_;
 
 #if defined(USE_OZONE)
   // This should outlive gl_surface_ and vulkan_surface_.
@@ -205,9 +205,7 @@
   size_t max_resource_cache_bytes_ = 0u;
 
   std::unique_ptr<SkiaOutputDevice> output_device_;
-
-  // Semaphore for SkiaOutputDevice::SwapBuffers() to wait on.
-  GrBackendSemaphore swap_buffers_semaphore_;
+  base::Optional<SkiaOutputDevice::ScopedPaint> scoped_output_device_paint_;
 
   // Offscreen surfaces for render passes. It can only be accessed on GPU
   // thread.
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 53852f76..87618062 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -306,8 +306,8 @@
     "../common/service_manager/child_connection.h",
     "about_url_loader_factory.cc",
     "about_url_loader_factory.h",
-    "accessibility/accessibility_tree_formatter.cc",
-    "accessibility/accessibility_tree_formatter.h",
+    "accessibility/accessibility_tree_formatter_base.cc",
+    "accessibility/accessibility_tree_formatter_base.h",
     "accessibility/accessibility_tree_formatter_blink.cc",
     "accessibility/accessibility_tree_formatter_blink.h",
     "accessibility/accessibility_tree_formatter_browser.cc",
diff --git a/content/browser/accessibility/accessibility_tree_formatter.cc b/content/browser/accessibility/accessibility_tree_formatter_base.cc
similarity index 78%
rename from content/browser/accessibility/accessibility_tree_formatter.cc
rename to content/browser/accessibility/accessibility_tree_formatter_base.cc
index 81d2a74..6734ee8 100644
--- a/content/browser/accessibility/accessibility_tree_formatter.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_base.cc
@@ -1,8 +1,8 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/browser/accessibility/accessibility_tree_formatter.h"
+#include "content/browser/accessibility/accessibility_tree_formatter_base.h"
 
 #include <stddef.h>
 
@@ -32,23 +32,6 @@
 
 }  // namespace
 
-AccessibilityTreeFormatter::AccessibilityTreeFormatter() : show_ids_(false) {}
-
-AccessibilityTreeFormatter::~AccessibilityTreeFormatter() {}
-
-void AccessibilityTreeFormatter::FormatAccessibilityTree(
-    BrowserAccessibility* root,
-    base::string16* contents) {
-  std::unique_ptr<base::DictionaryValue> dict = BuildAccessibilityTree(root);
-  RecursiveFormatAccessibilityTree(*(dict.get()), contents);
-}
-
-void AccessibilityTreeFormatter::FormatAccessibilityTree(
-    const base::DictionaryValue& dict,
-    base::string16* contents) {
-  RecursiveFormatAccessibilityTree(dict, contents);
-}
-
 base::string16 AccessibilityTreeFormatter::DumpAccessibilityTreeFromManager(
     BrowserAccessibilityManager* ax_mgr,
     bool internal) {
@@ -67,77 +50,6 @@
   return accessibility_contents_utf16;
 }
 
-std::unique_ptr<base::DictionaryValue>
-AccessibilityTreeFormatter::FilterAccessibilityTree(
-    const base::DictionaryValue& dict) {
-  auto filtered_dict = std::make_unique<base::DictionaryValue>();
-  ProcessTreeForOutput(dict, filtered_dict.get());
-  const base::ListValue* children;
-  if (dict.GetList(kChildrenDictAttr, &children) && !children->empty()) {
-    const base::DictionaryValue* child_dict;
-    auto filtered_children = std::make_unique<base::ListValue>();
-    for (size_t i = 0; i < children->GetSize(); i++) {
-      children->GetDictionary(i, &child_dict);
-      auto filtered_child = FilterAccessibilityTree(*child_dict);
-      filtered_children->Append(std::move(filtered_child));
-    }
-    filtered_dict->Set(kChildrenDictAttr, std::move(filtered_children));
-  }
-  return filtered_dict;
-}
-
-void AccessibilityTreeFormatter::RecursiveFormatAccessibilityTree(
-    const base::DictionaryValue& dict,
-    base::string16* contents,
-    int depth) {
-  // Check dictionary against node filters, may require us to skip this node
-  // and its children.
-  if (MatchesNodeFilters(dict))
-    return;
-
-  base::string16 indent =
-      base::string16(depth * kIndentSymbolCount, kIndentSymbol);
-  base::string16 line = indent + ProcessTreeForOutput(dict);
-  if (line.find(base::ASCIIToUTF16(kSkipString)) != base::string16::npos)
-    return;
-
-  // Normalize any Windows-style line endings by removing \r.
-  base::RemoveChars(line, base::ASCIIToUTF16("\r"), &line);
-
-  // Replace literal newlines with "<newline>"
-  base::ReplaceChars(line, base::ASCIIToUTF16("\n"),
-                     base::ASCIIToUTF16("<newline>"), &line);
-
-  *contents += line + base::ASCIIToUTF16("\n");
-  if (line.find(base::ASCIIToUTF16(kSkipChildren)) != base::string16::npos)
-    return;
-
-  const base::ListValue* children;
-  if (!dict.GetList(kChildrenDictAttr, &children))
-    return;
-  const base::DictionaryValue* child_dict;
-  for (size_t i = 0; i < children->GetSize(); i++) {
-    children->GetDictionary(i, &child_dict);
-    RecursiveFormatAccessibilityTree(*child_dict, contents, depth + 1);
-  }
-}
-
-void AccessibilityTreeFormatter::SetPropertyFilters(
-    const std::vector<PropertyFilter>& property_filters) {
-  property_filters_ = property_filters;
-}
-
-void AccessibilityTreeFormatter::SetNodeFilters(
-    const std::vector<NodeFilter>& node_filters) {
-  node_filters_ = node_filters;
-}
-
-const base::FilePath::StringType
-AccessibilityTreeFormatter::GetVersionSpecificExpectedFileSuffix() {
-  return FILE_PATH_LITERAL("");
-}
-
-// static
 bool AccessibilityTreeFormatter::MatchesPropertyFilters(
     const std::vector<PropertyFilter>& property_filters,
     const base::string16& text,
@@ -176,18 +88,111 @@
   return false;
 }
 
-bool AccessibilityTreeFormatter::MatchesPropertyFilters(
+AccessibilityTreeFormatterBase::AccessibilityTreeFormatterBase()
+    : show_ids_(false) {}
+
+AccessibilityTreeFormatterBase::~AccessibilityTreeFormatterBase() {}
+
+void AccessibilityTreeFormatterBase::FormatAccessibilityTree(
+    BrowserAccessibility* root,
+    base::string16* contents) {
+  std::unique_ptr<base::DictionaryValue> dict = BuildAccessibilityTree(root);
+  RecursiveFormatAccessibilityTree(*(dict.get()), contents);
+}
+
+void AccessibilityTreeFormatterBase::FormatAccessibilityTree(
+    const base::DictionaryValue& dict,
+    base::string16* contents) {
+  RecursiveFormatAccessibilityTree(dict, contents);
+}
+
+std::unique_ptr<base::DictionaryValue>
+AccessibilityTreeFormatterBase::FilterAccessibilityTree(
+    const base::DictionaryValue& dict) {
+  auto filtered_dict = std::make_unique<base::DictionaryValue>();
+  ProcessTreeForOutput(dict, filtered_dict.get());
+  const base::ListValue* children;
+  if (dict.GetList(kChildrenDictAttr, &children) && !children->empty()) {
+    const base::DictionaryValue* child_dict;
+    auto filtered_children = std::make_unique<base::ListValue>();
+    for (size_t i = 0; i < children->GetSize(); i++) {
+      children->GetDictionary(i, &child_dict);
+      auto filtered_child = FilterAccessibilityTree(*child_dict);
+      filtered_children->Append(std::move(filtered_child));
+    }
+    filtered_dict->Set(kChildrenDictAttr, std::move(filtered_children));
+  }
+  return filtered_dict;
+}
+
+void AccessibilityTreeFormatterBase::RecursiveFormatAccessibilityTree(
+    const base::DictionaryValue& dict,
+    base::string16* contents,
+    int depth) {
+  // Check dictionary against node filters, may require us to skip this node
+  // and its children.
+  if (MatchesNodeFilters(dict))
+    return;
+
+  base::string16 indent =
+      base::string16(depth * kIndentSymbolCount, kIndentSymbol);
+  base::string16 line = indent + ProcessTreeForOutput(dict);
+  if (line.find(base::ASCIIToUTF16(kSkipString)) != base::string16::npos)
+    return;
+
+  // Normalize any Windows-style line endings by removing \r.
+  base::RemoveChars(line, base::ASCIIToUTF16("\r"), &line);
+
+  // Replace literal newlines with "<newline>"
+  base::ReplaceChars(line, base::ASCIIToUTF16("\n"),
+                     base::ASCIIToUTF16("<newline>"), &line);
+
+  *contents += line + base::ASCIIToUTF16("\n");
+  if (line.find(base::ASCIIToUTF16(kSkipChildren)) != base::string16::npos)
+    return;
+
+  const base::ListValue* children;
+  if (!dict.GetList(kChildrenDictAttr, &children))
+    return;
+  const base::DictionaryValue* child_dict;
+  for (size_t i = 0; i < children->GetSize(); i++) {
+    children->GetDictionary(i, &child_dict);
+    RecursiveFormatAccessibilityTree(*child_dict, contents, depth + 1);
+  }
+}
+
+void AccessibilityTreeFormatterBase::SetPropertyFilters(
+    const std::vector<PropertyFilter>& property_filters) {
+  property_filters_ = property_filters;
+}
+
+void AccessibilityTreeFormatterBase::SetNodeFilters(
+    const std::vector<NodeFilter>& node_filters) {
+  node_filters_ = node_filters;
+}
+
+void AccessibilityTreeFormatterBase::set_show_ids(bool show_ids) {
+  show_ids_ = show_ids;
+}
+
+const base::FilePath::StringType
+AccessibilityTreeFormatterBase::GetVersionSpecificExpectedFileSuffix() {
+  return FILE_PATH_LITERAL("");
+}
+
+bool AccessibilityTreeFormatterBase::MatchesPropertyFilters(
     const base::string16& text,
     bool default_result) const {
-  return MatchesPropertyFilters(property_filters_, text, default_result);
+  return AccessibilityTreeFormatter::MatchesPropertyFilters(
+      property_filters_, text, default_result);
 }
 
-bool AccessibilityTreeFormatter::MatchesNodeFilters(
+bool AccessibilityTreeFormatterBase::MatchesNodeFilters(
     const base::DictionaryValue& dict) const {
-  return MatchesNodeFilters(node_filters_, dict);
+  return AccessibilityTreeFormatter::MatchesNodeFilters(node_filters_, dict);
 }
 
-base::string16 AccessibilityTreeFormatter::FormatCoordinates(
+base::string16 AccessibilityTreeFormatterBase::FormatCoordinates(
     const char* name,
     const char* x_name,
     const char* y_name,
@@ -200,15 +205,15 @@
   return base::UTF8ToUTF16(xy_str);
 }
 
-bool AccessibilityTreeFormatter::WriteAttribute(bool include_by_default,
-                                                const std::string& attr,
-                                                base::string16* line) {
+bool AccessibilityTreeFormatterBase::WriteAttribute(bool include_by_default,
+                                                    const std::string& attr,
+                                                    base::string16* line) {
   return WriteAttribute(include_by_default, base::UTF8ToUTF16(attr), line);
 }
 
-bool AccessibilityTreeFormatter::WriteAttribute(bool include_by_default,
-                                                const base::string16& attr,
-                                                base::string16* line) {
+bool AccessibilityTreeFormatterBase::WriteAttribute(bool include_by_default,
+                                                    const base::string16& attr,
+                                                    base::string16* line) {
   if (attr.empty())
     return false;
   if (!MatchesPropertyFilters(attr, include_by_default))
@@ -219,13 +224,13 @@
   return true;
 }
 
-void AccessibilityTreeFormatter::AddPropertyFilter(
+void AccessibilityTreeFormatterBase::AddPropertyFilter(
     std::vector<PropertyFilter>* property_filters,
     std::string filter,
     PropertyFilter::Type type) {
   property_filters->push_back(PropertyFilter(base::ASCIIToUTF16(filter), type));
 }
 
-void AccessibilityTreeFormatter::AddDefaultFilters(
+void AccessibilityTreeFormatterBase::AddDefaultFilters(
     std::vector<PropertyFilter>* property_filters) {}
 }  // namespace content
diff --git a/content/browser/accessibility/accessibility_tree_formatter_base.h b/content/browser/accessibility/accessibility_tree_formatter_base.h
new file mode 100644
index 0000000..d2430c04
--- /dev/null
+++ b/content/browser/accessibility/accessibility_tree_formatter_base.h
@@ -0,0 +1,124 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_TREE_FORMATTER_BASE_H_
+#define CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_TREE_FORMATTER_BASE_H_
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/process/process_handle.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "content/browser/accessibility/browser_accessibility.h"
+#include "content/common/content_export.h"
+#include "content/public/browser/accessibility_tree_formatter.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace {
+const char kChildrenDictAttr[] = "children";
+}
+
+namespace content {
+
+// A utility class for formatting platform-specific accessibility information,
+// for use in testing, debugging, and developer tools.
+// This is extended by a subclass for each platform where accessibility is
+// implemented.
+class CONTENT_EXPORT AccessibilityTreeFormatterBase
+    : public AccessibilityTreeFormatter {
+ public:
+  explicit AccessibilityTreeFormatterBase();
+  ~AccessibilityTreeFormatterBase() override;
+
+  // AccessibilityTreeFormatter overrides.
+  void AddDefaultFilters(
+      std::vector<PropertyFilter>* property_filters) override;
+  std::unique_ptr<base::DictionaryValue> FilterAccessibilityTree(
+      const base::DictionaryValue& dict) override;
+  void FormatAccessibilityTree(BrowserAccessibility* root,
+                               base::string16* contents) override;
+  void FormatAccessibilityTree(const base::DictionaryValue& tree_node,
+                               base::string16* contents) override;
+  void SetPropertyFilters(
+      const std::vector<PropertyFilter>& property_filters) override;
+  void SetNodeFilters(const std::vector<NodeFilter>& node_filters) override;
+  void set_show_ids(bool show_ids) override;
+  const base::FilePath::StringType GetVersionSpecificExpectedFileSuffix()
+      override;
+
+ protected:
+  //
+  // Overridden by platform subclasses.
+  //
+
+  // Process accessibility tree with filters for output.
+  // Given a dictionary that contains a platform-specific dictionary
+  // representing an accessibility tree, and utilizing property_filters_ and
+  // node_filters_:
+  // - Returns a filtered text view as one large string.
+  // - Provides a filtered version of the dictionary in an out param,
+  //   (only if the out param is provided).
+  virtual base::string16 ProcessTreeForOutput(
+      const base::DictionaryValue& node,
+      base::DictionaryValue* filtered_dict_result = nullptr) = 0;
+
+  //
+  // Utility functions to be used by each platform.
+  //
+
+  base::string16 FormatCoordinates(const char* name,
+                                   const char* x_name,
+                                   const char* y_name,
+                                   const base::DictionaryValue& value);
+
+  // Writes the given attribute string out to |line| if it matches the property
+  // filters.
+  // Returns false if the attribute was filtered out.
+  bool WriteAttribute(bool include_by_default,
+                      const base::string16& attr,
+                      base::string16* line);
+  bool WriteAttribute(bool include_by_default,
+                      const std::string& attr,
+                      base::string16* line);
+  void AddPropertyFilter(std::vector<PropertyFilter>* property_filters,
+                         std::string filter,
+                         PropertyFilter::Type type = PropertyFilter::ALLOW);
+  bool show_ids() { return show_ids_; }
+
+ private:
+  void RecursiveFormatAccessibilityTree(const BrowserAccessibility& node,
+                                        base::string16* contents,
+                                        int indent);
+  void RecursiveFormatAccessibilityTree(const base::DictionaryValue& tree_node,
+                                        base::string16* contents,
+                                        int depth = 0);
+
+  bool MatchesPropertyFilters(const base::string16& text,
+                              bool default_result) const;
+  bool MatchesNodeFilters(const base::DictionaryValue& dict) const;
+
+  // Property filters used when formatting the accessibility tree as text.
+  // Any property which matches a property filter will be skipped.
+  std::vector<PropertyFilter> property_filters_;
+
+  // Node filters used when formatting the accessibility tree as text.
+  // Any node which matches a node wilder will be skipped, along with all its
+  // children.
+  std::vector<NodeFilter> node_filters_;
+
+  // Whether or not node ids should be included in the dump.
+  bool show_ids_;
+
+  DISALLOW_COPY_AND_ASSIGN(AccessibilityTreeFormatterBase);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_TREE_FORMATTER_BASE_H_
diff --git a/content/browser/accessibility/accessibility_tree_formatter_browser.h b/content/browser/accessibility/accessibility_tree_formatter_browser.h
index c5713fd..62535d3 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_browser.h
+++ b/content/browser/accessibility/accessibility_tree_formatter_browser.h
@@ -5,7 +5,7 @@
 #ifndef CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_TREE_FORMATTER_BROWSER_H_
 #define CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_TREE_FORMATTER_BROWSER_H_
 
-#include "content/browser/accessibility/accessibility_tree_formatter.h"
+#include "content/browser/accessibility/accessibility_tree_formatter_base.h"
 
 #include "base/macros.h"
 #include "base/strings/string16.h"
@@ -20,7 +20,7 @@
 // walking the tree and getting properties. Tree formatters that walk native
 // objects should not inherit from this class.
 class CONTENT_EXPORT AccessibilityTreeFormatterBrowser
-    : public AccessibilityTreeFormatter {
+    : public AccessibilityTreeFormatterBase {
  public:
   std::unique_ptr<base::DictionaryValue> BuildAccessibilityTree(
       BrowserAccessibility* root) override;
diff --git a/content/browser/accessibility/accessibility_tree_formatter_uia_win.h b/content/browser/accessibility/accessibility_tree_formatter_uia_win.h
index 577e96b3..e6abfb7 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_uia_win.h
+++ b/content/browser/accessibility/accessibility_tree_formatter_uia_win.h
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 #ifndef CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_TREE_FORMATTER_UIA_WIN_H_
 #define CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_TREE_FORMATTER_UIA_WIN_H_
-#include "content/browser/accessibility/accessibility_tree_formatter.h"
+#include "content/browser/accessibility/accessibility_tree_formatter_base.h"
 
 #include <ole2.h>
 #include <stdint.h>
@@ -16,7 +16,7 @@
 #include "base/win/scoped_variant.h"
 namespace content {
 
-class AccessibilityTreeFormatterUia : public AccessibilityTreeFormatter {
+class AccessibilityTreeFormatterUia : public AccessibilityTreeFormatterBase {
  public:
   AccessibilityTreeFormatterUia();
 
diff --git a/content/browser/accessibility/accessibility_tree_formatter_win.cc b/content/browser/accessibility/accessibility_tree_formatter_win.cc
index e8330f43..41e4ba70 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_win.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_win.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/browser/accessibility/accessibility_tree_formatter.h"
+#include "content/browser/accessibility/accessibility_tree_formatter_base.h"
 
 #include <math.h>
 #include <oleacc.h>
@@ -37,7 +37,7 @@
 
 namespace content {
 
-class AccessibilityTreeFormatterWin : public AccessibilityTreeFormatter {
+class AccessibilityTreeFormatterWin : public AccessibilityTreeFormatterBase {
  public:
   AccessibilityTreeFormatterWin();
   ~AccessibilityTreeFormatterWin() override;
diff --git a/content/browser/accessibility/accessibility_win_browsertest.cc b/content/browser/accessibility/accessibility_win_browsertest.cc
index ba10463..3b76d86 100644
--- a/content/browser/accessibility/accessibility_win_browsertest.cc
+++ b/content/browser/accessibility/accessibility_win_browsertest.cc
@@ -23,7 +23,6 @@
 #include "build/build_config.h"
 #include "content/browser/accessibility/accessibility_browsertest.h"
 #include "content/browser/accessibility/accessibility_event_recorder.h"
-#include "content/browser/accessibility/accessibility_tree_formatter.h"
 #include "content/browser/accessibility/accessibility_tree_formatter_utils_win.h"
 #include "content/browser/accessibility/browser_accessibility_manager_win.h"
 #include "content/browser/accessibility/browser_accessibility_win.h"
@@ -31,6 +30,7 @@
 #include "content/browser/renderer_host/render_widget_host_view_aura.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/browser/web_contents/web_contents_view_aura.h"
+#include "content/public/browser/accessibility_tree_formatter.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_types.h"
 #include "content/public/browser/render_frame_host.h"
diff --git a/content/browser/accessibility/dump_accessibility_browsertest_base.cc b/content/browser/accessibility/dump_accessibility_browsertest_base.cc
index 965f4ac9..9fa59908 100644
--- a/content/browser/accessibility/dump_accessibility_browsertest_base.cc
+++ b/content/browser/accessibility/dump_accessibility_browsertest_base.cc
@@ -20,7 +20,6 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "content/browser/accessibility/accessibility_event_recorder.h"
-#include "content/browser/accessibility/accessibility_tree_formatter.h"
 #include "content/browser/accessibility/browser_accessibility.h"
 #include "content/browser/accessibility/browser_accessibility_manager.h"
 #include "content/browser/accessibility/browser_accessibility_state_impl.h"
diff --git a/content/browser/accessibility/dump_accessibility_browsertest_base.h b/content/browser/accessibility/dump_accessibility_browsertest_base.h
index 42154c5d..a97efe3 100644
--- a/content/browser/accessibility/dump_accessibility_browsertest_base.h
+++ b/content/browser/accessibility/dump_accessibility_browsertest_base.h
@@ -13,7 +13,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "content/browser/accessibility/accessibility_event_recorder.h"
-#include "content/browser/accessibility/accessibility_tree_formatter.h"
+#include "content/public/browser/accessibility_tree_formatter.h"
 #include "content/public/test/content_browser_test.h"
 
 namespace content {
diff --git a/content/browser/accessibility/dump_accessibility_events_browsertest.cc b/content/browser/accessibility/dump_accessibility_events_browsertest.cc
index 21e6f18..820193e 100644
--- a/content/browser/accessibility/dump_accessibility_events_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_events_browsertest.cc
@@ -16,11 +16,11 @@
 #include "base/threading/thread_restrictions.h"
 #include "build/build_config.h"
 #include "content/browser/accessibility/accessibility_event_recorder.h"
-#include "content/browser/accessibility/accessibility_tree_formatter.h"
 #include "content/browser/accessibility/browser_accessibility.h"
 #include "content/browser/accessibility/browser_accessibility_manager.h"
 #include "content/browser/accessibility/dump_accessibility_browsertest_base.h"
 #include "content/browser/web_contents/web_contents_impl.h"
+#include "content/public/browser/accessibility_tree_formatter.h"
 #include "content/public/common/content_paths.h"
 #include "content/public/test/content_browser_test_utils.h"
 #include "content/public/test/test_utils.h"
diff --git a/content/browser/accessibility/dump_accessibility_test_helper.cc b/content/browser/accessibility/dump_accessibility_test_helper.cc
index 8d6dc4d..c6eed89 100644
--- a/content/browser/accessibility/dump_accessibility_test_helper.cc
+++ b/content/browser/accessibility/dump_accessibility_test_helper.cc
@@ -10,7 +10,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/threading/thread_restrictions.h"
-#include "content/browser/accessibility/accessibility_tree_formatter.h"
+#include "content/public/browser/accessibility_tree_formatter.h"
 #include "content/public/common/content_switches.h"
 
 namespace content {
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index ebedcf5..063e42e 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -14,7 +14,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread_restrictions.h"
 #include "build/build_config.h"
-#include "content/browser/accessibility/accessibility_tree_formatter.h"
 #include "content/browser/accessibility/accessibility_tree_formatter_blink.h"
 #include "content/browser/accessibility/browser_accessibility.h"
 #include "content/browser/accessibility/browser_accessibility_manager.h"
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index d4426f9..777e9ccf6 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -602,15 +602,17 @@
       return pre_early_init_error_code;
   }
 
-#if defined(OS_ANDROID) || defined(OS_CHROMEOS) || defined(USE_OZONE)
-  // Up the priority of the UI thread unless it was already high (since recent
-  // versions of Android (O+) do this automatically).
-  if (base::PlatformThread::GetCurrentThreadPriority() <
-      base::ThreadPriority::DISPLAY) {
+  // Up the priority of the UI thread unless it was already high (since Mac
+  // and recent versions of Android (O+) do this automatically).
+#if !defined(OS_MACOSX)
+  if (base::FeatureList::IsEnabled(
+          features::kBrowserUseDisplayThreadPriority) &&
+      base::PlatformThread::GetCurrentThreadPriority() <
+          base::ThreadPriority::DISPLAY) {
     base::PlatformThread::SetCurrentThreadPriority(
         base::ThreadPriority::DISPLAY);
   }
-#endif  // defined(OS_ANDROID) || defined(OS_CHROMEOS)
+#endif  // !defined(OS_MACOSX)
 
 #if defined(OS_MACOSX) || defined(OS_LINUX) || defined(OS_CHROMEOS) || \
     defined(OS_ANDROID)
diff --git a/content/browser/child_process_launcher_helper_mac.cc b/content/browser/child_process_launcher_helper_mac.cc
index 8a46b585..d3b86f50 100644
--- a/content/browser/child_process_launcher_helper_mac.cc
+++ b/content/browser/child_process_launcher_helper_mac.cc
@@ -21,7 +21,6 @@
 #include "content/public/common/content_switches.h"
 #include "content/public/common/result_codes.h"
 #include "content/public/common/sandboxed_process_launcher_delegate.h"
-#include "mojo/public/cpp/platform/features.h"
 #include "sandbox/mac/seatbelt_exec.h"
 #include "services/service_manager/embedder/result_codes.h"
 #include "services/service_manager/sandbox/mac/sandbox_mac.h"
@@ -61,12 +60,11 @@
   base::FieldTrialList::InsertFieldTrialHandleIfNeeded(
       &options->mach_ports_for_rendezvous);
 
-  if (base::FeatureList::IsEnabled(mojo::features::kMojoChannelMac)) {
-    options->mach_ports_for_rendezvous.insert(std::make_pair(
-        'mojo', base::MachRendezvousPort(mojo_channel_->TakeRemoteEndpoint()
-                                             .TakePlatformHandle()
-                                             .TakeMachReceiveRight())));
-  }
+  mojo::PlatformHandle endpoint =
+      mojo_channel_->TakeRemoteEndpoint().TakePlatformHandle();
+  DCHECK(endpoint.is_valid_mach_receive());
+  options->mach_ports_for_rendezvous.insert(std::make_pair(
+      'mojo', base::MachRendezvousPort(endpoint.TakeMachReceiveRight())));
 
   options->environment = delegate_->GetEnvironment();
 
diff --git a/content/browser/child_process_launcher_helper_posix.cc b/content/browser/child_process_launcher_helper_posix.cc
index c18406f..d52a508 100644
--- a/content/browser/child_process_launcher_helper_posix.cc
+++ b/content/browser/child_process_launcher_helper_posix.cc
@@ -15,7 +15,6 @@
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_descriptors.h"
 #include "content/public/common/content_switches.h"
-#include "mojo/public/cpp/platform/features.h"
 #include "mojo/public/cpp/platform/platform_channel_endpoint.h"
 #include "services/service_manager/embedder/shared_file_util.h"
 #include "services/service_manager/embedder/switches.h"
@@ -87,22 +86,13 @@
   DCHECK_NE(fd, -1);
   files_to_register->Share(service_manager::kFieldTrialDescriptor, fd);
 
-  const bool mojo_channel_mac = false;
-#else
-  const bool mojo_channel_mac =
-      base::FeatureList::IsEnabled(mojo::features::kMojoChannelMac);
-#endif
-
-  if (!mojo_channel_mac) {
-    DCHECK(mojo_channel_remote_endpoint.is_valid());
-    files_to_register->Share(
-        service_manager::kMojoIPCChannel,
-        mojo_channel_remote_endpoint.platform_handle().GetFD().get());
-  }
+  DCHECK(mojo_channel_remote_endpoint.is_valid());
+  files_to_register->Share(
+      service_manager::kMojoIPCChannel,
+      mojo_channel_remote_endpoint.platform_handle().GetFD().get());
 
   // TODO(jcivelli): remove this "if defined" by making
   // GetAdditionalMappedFilesForChildProcess a no op on Mac.
-#if !defined(OS_MACOSX)
   GetContentClient()->browser()->GetAdditionalMappedFilesForChildProcess(
       *command_line, child_process_id, files_to_register.get());
 #endif
diff --git a/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.cc b/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.cc
index b48fe688..f695189 100644
--- a/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.cc
+++ b/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.cc
@@ -157,9 +157,7 @@
     // Even through the swap failed, this is a fixable error so we can pretend
     // it succeeded to the rest of the system.
     modified_params.swap_response.result = gfx::SwapResult::SWAP_ACK;
-    unsigned current_surface_texture = buffer_queue_->RecreateBuffers();
-    if (current_surface_texture)
-      BindFramebuffer();
+    buffer_queue_->FreeAllSurfaces();
     force_swap = true;
   }
   buffer_queue_->PageFlipComplete();
diff --git a/content/browser/devtools/devtools_protocol_encoding_cbor_fuzzer.cc b/content/browser/devtools/devtools_protocol_encoding_cbor_fuzzer.cc
index ad57032..4ffa6bf 100644
--- a/content/browser/devtools/devtools_protocol_encoding_cbor_fuzzer.cc
+++ b/content/browser/devtools/devtools_protocol_encoding_cbor_fuzzer.cc
@@ -19,8 +19,8 @@
 }  // namespace
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  std::string cbor;
-  content::ConvertJSONToCBOR(span<uint8_t>(data, size), &cbor);
+  std::string json;
+  content::ConvertCBORToJSON(span<uint8_t>(data, size), &json);
   return 0;
 }
 }  // namespace content
diff --git a/content/browser/devtools/devtools_protocol_encoding_json_fuzzer.cc b/content/browser/devtools/devtools_protocol_encoding_json_fuzzer.cc
index b20782b..cb9ba6b2 100644
--- a/content/browser/devtools/devtools_protocol_encoding_json_fuzzer.cc
+++ b/content/browser/devtools/devtools_protocol_encoding_json_fuzzer.cc
@@ -13,8 +13,8 @@
 }  // namespace
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  std::string json;
-  content::ConvertJSONToCBOR(span<uint8_t>(data, size), &json);
+  std::string cbor;
+  content::ConvertJSONToCBOR(span<uint8_t>(data, size), &cbor);
   return 0;
 }
 }  // namespace content
diff --git a/content/browser/network_service_restart_browsertest.cc b/content/browser/network_service_restart_browsertest.cc
index 3eac522f..84de870 100644
--- a/content/browser/network_service_restart_browsertest.cc
+++ b/content/browser/network_service_restart_browsertest.cc
@@ -482,7 +482,7 @@
     return;
   base::ScopedAllowBlockingForTesting allow_blocking;
   std::unique_ptr<ShellBrowserContext> browser_context =
-      std::make_unique<ShellBrowserContext>(true, nullptr);
+      std::make_unique<ShellBrowserContext>(true);
   auto* partition = static_cast<StoragePartitionImpl*>(
       BrowserContext::GetDefaultStoragePartition(browser_context.get()));
   auto factory_owner = IOThreadSharedURLLoaderFactoryOwner::Create(
@@ -572,7 +572,7 @@
     return;
   base::ScopedAllowBlockingForTesting allow_blocking;
   std::unique_ptr<ShellBrowserContext> browser_context =
-      std::make_unique<ShellBrowserContext>(true, nullptr);
+      std::make_unique<ShellBrowserContext>(true);
   auto* partition =
       BrowserContext::GetDefaultStoragePartition(browser_context.get());
   scoped_refptr<network::SharedURLLoaderFactory> factory(
diff --git a/content/browser/payments/payment_app_provider_impl.cc b/content/browser/payments/payment_app_provider_impl.cc
index 9941db8..dbb172c 100644
--- a/content/browser/payments/payment_app_provider_impl.cc
+++ b/content/browser/payments/payment_app_provider_impl.cc
@@ -206,12 +206,11 @@
 
   int request_id() { return request_id_; }
 
-  void AbortPaymentSinceOpennedWindowClosing() {
+  void AbortPaymentSinceOpennedWindowClosing(PaymentEventResponseType reason) {
     DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
     service_worker_version_->FinishRequest(request_id_, false);
-    RespondWithErrorAndDeleteSelf(
-        PaymentEventResponseType::PAYMENT_HANDLER_WINDOW_CLOSING);
+    RespondWithErrorAndDeleteSelf(reason);
   }
 
  private:
@@ -440,14 +439,15 @@
   std::move(callback).Run(std::move(permitted_apps));
 }
 
-void AbortInvokePaymentApp(BrowserContext* browser_context) {
+void AbortInvokePaymentApp(BrowserContext* browser_context,
+                           PaymentEventResponseType reason) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   RespondWithCallbacks* callback =
       InvokePaymentAppCallbackRepository::GetInstance()->GetCallback(
           browser_context);
   if (callback)
-    callback->AbortPaymentSinceOpennedWindowClosing();
+    callback->AbortPaymentSinceOpennedWindowClosing(reason);
 }
 
 }  // namespace
@@ -584,12 +584,13 @@
 }
 
 void PaymentAppProviderImpl::OnClosingOpenedWindow(
-    BrowserContext* browser_context) {
+    BrowserContext* browser_context,
+    PaymentEventResponseType reason) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   base::PostTaskWithTraits(
       FROM_HERE, {BrowserThread::IO},
-      base::BindOnce(&AbortInvokePaymentApp, browser_context));
+      base::BindOnce(&AbortInvokePaymentApp, browser_context, reason));
 }
 
 bool PaymentAppProviderImpl::IsValidInstallablePaymentApp(
diff --git a/content/browser/payments/payment_app_provider_impl.h b/content/browser/payments/payment_app_provider_impl.h
index cd7d6956..9908948 100644
--- a/content/browser/payments/payment_app_provider_impl.h
+++ b/content/browser/payments/payment_app_provider_impl.h
@@ -44,7 +44,9 @@
                     PaymentEventResultCallback callback) override;
   void SetOpenedWindow(WebContents* web_contents) override;
   void CloseOpenedWindow(BrowserContext* browser_context) override;
-  void OnClosingOpenedWindow(BrowserContext* browser_context) override;
+  void OnClosingOpenedWindow(
+      BrowserContext* browser_context,
+      payments::mojom::PaymentEventResponseType reason) override;
   bool IsValidInstallablePaymentApp(const GURL& manifest_url,
                                     const GURL& sw_js_url,
                                     const GURL& sw_scope,
diff --git a/content/browser/payments/payment_app_provider_impl_unittest.cc b/content/browser/payments/payment_app_provider_impl_unittest.cc
index 9aa7ef7..8b46f9b6 100644
--- a/content/browser/payments/payment_app_provider_impl_unittest.cc
+++ b/content/browser/payments/payment_app_provider_impl_unittest.cc
@@ -113,7 +113,8 @@
 
   void OnClosingOpenedWindow() {
     PaymentAppProviderImpl::GetInstance()->OnClosingOpenedWindow(
-        browser_context());
+        browser_context(), payments::mojom::PaymentEventResponseType::
+                               PAYMENT_HANDLER_WINDOW_CLOSING);
     base::RunLoop().RunUntilIdle();
   }
 
diff --git a/content/browser/pointer_lock_browsertest.cc b/content/browser/pointer_lock_browsertest.cc
index 0afb41fe..2f639b7 100644
--- a/content/browser/pointer_lock_browsertest.cc
+++ b/content/browser/pointer_lock_browsertest.cc
@@ -9,6 +9,7 @@
 #include "content/browser/renderer_host/render_widget_host_input_event_router.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/public/browser/web_contents_delegate.h"
+#include "content/public/common/content_features.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_browser_test.h"
 #include "content/public/test/content_browser_test_utils.h"
@@ -172,7 +173,9 @@
   blink::WebMouseEvent mouse_event(
       blink::WebInputEvent::kMouseMove, blink::WebInputEvent::kNoModifiers,
       blink::WebInputEvent::GetStaticTimeStampForTests());
+  mouse_event.pointer_type = blink::WebPointerProperties::PointerType::kMouse;
   mouse_event.SetPositionInWidget(6, 7);
+  mouse_event.SetPositionInScreen(6, 7);
   mouse_event.movement_x = 8;
   mouse_event.movement_y = 9;
   router->RouteMouseEvent(root_view, &mouse_event, ui::LatencyInfo());
@@ -181,7 +184,10 @@
   MainThreadFrameObserver root_observer(root_view->GetRenderWidgetHost());
   root_observer.Wait();
 
-  EXPECT_EQ("[6,7,8,9]", EvalJs(root, "JSON.stringify([x,y,mX,mY])"));
+  if (base::FeatureList::IsEnabled(features::kConsolidatedMovementXY))
+    EXPECT_EQ("[6,7,0,0]", EvalJs(root, "JSON.stringify([x,y,mX,mY])"));
+  else
+    EXPECT_EQ("[6,7,8,9]", EvalJs(root, "JSON.stringify([x,y,mX,mY])"));
 
   // Request a pointer lock on the root frame's body.
   EXPECT_TRUE(ExecJs(root, "document.body.requestPointerLock()"));
@@ -189,7 +195,8 @@
   // Root frame should have been granted pointer lock.
   EXPECT_EQ(true, EvalJs(root, "document.pointerLockElement == document.body"));
 
-  mouse_event.SetPositionInWidget(10, 11);
+  mouse_event.SetPositionInWidget(10, 12);
+  mouse_event.SetPositionInScreen(10, 12);
   mouse_event.movement_x = 12;
   mouse_event.movement_y = 13;
   router->RouteMouseEvent(root_view, &mouse_event, ui::LatencyInfo());
@@ -198,7 +205,10 @@
   root_observer.Wait();
 
   // Locked event has same coordinates as before locked.
-  EXPECT_EQ("[6,7,12,13]", EvalJs(root, "JSON.stringify([x,y,mX,mY])"));
+  if (base::FeatureList::IsEnabled(features::kConsolidatedMovementXY))
+    EXPECT_EQ("[6,7,4,5]", EvalJs(root, "JSON.stringify([x,y,mX,mY])"));
+  else
+    EXPECT_EQ("[6,7,12,13]", EvalJs(root, "JSON.stringify([x,y,mX,mY])"));
 
   // Release pointer lock on root frame.
   EXPECT_TRUE(ExecJs(root, "document.exitPointerLock()"));
@@ -222,6 +232,8 @@
 
   mouse_event.SetPositionInWidget(-transformed_point.x() + 14,
                                   -transformed_point.y() + 15);
+  mouse_event.SetPositionInScreen(-transformed_point.x() + 14,
+                                  -transformed_point.y() + 15);
   mouse_event.movement_x = 16;
   mouse_event.movement_y = 17;
   // We use root_view intentionally as the RenderWidgetHostInputEventRouter is
@@ -233,7 +245,10 @@
   child_observer.Wait();
 
   // This is the first event to child render, so the coordinates is (0, 0)
-  EXPECT_EQ("[0,0,16,17]", EvalJs(child, "JSON.stringify([x,y,mX,mY])"));
+  if (base::FeatureList::IsEnabled(features::kConsolidatedMovementXY))
+    EXPECT_EQ("[0,0,0,0]", EvalJs(child, "JSON.stringify([x,y,mX,mY])"));
+  else
+    EXPECT_EQ("[0,0,16,17]", EvalJs(child, "JSON.stringify([x,y,mX,mY])"));
 }
 
 // Tests that the browser will not unlock the pointer if a RenderWidgetHostView
@@ -404,7 +419,9 @@
   blink::WebMouseEvent mouse_event(
       blink::WebInputEvent::kMouseMove, blink::WebInputEvent::kNoModifiers,
       blink::WebInputEvent::GetStaticTimeStampForTests());
+  mouse_event.pointer_type = blink::WebPointerProperties::PointerType::kMouse;
   mouse_event.SetPositionInWidget(6, 7);
+  mouse_event.SetPositionInScreen(6, 7);
   mouse_event.movement_x = 8;
   mouse_event.movement_y = 9;
   router->RouteMouseEvent(root_view, &mouse_event, ui::LatencyInfo());
@@ -413,7 +430,10 @@
   MainThreadFrameObserver root_observer(root_view->GetRenderWidgetHost());
   root_observer.Wait();
 
-  EXPECT_EQ("[6,7,8,9]", EvalJs(root, "JSON.stringify([x,y,mX,mY])"));
+  if (base::FeatureList::IsEnabled(features::kConsolidatedMovementXY))
+    EXPECT_EQ("[6,7,0,0]", EvalJs(root, "JSON.stringify([x,y,mX,mY])"));
+  else
+    EXPECT_EQ("[6,7,8,9]", EvalJs(root, "JSON.stringify([x,y,mX,mY])"));
 
   // Request a pointer lock on the root frame's body.
   EXPECT_TRUE(ExecJs(root, "document.body.requestPointerLock()"));
@@ -431,7 +451,7 @@
   blink::WebMouseWheelEvent wheel_event(
       blink::WebInputEvent::kMouseWheel, blink::WebInputEvent::kNoModifiers,
       blink::WebInputEvent::GetStaticTimeStampForTests());
-  wheel_event.SetPositionInWidget(10, 11);
+  wheel_event.SetPositionInScreen(10, 11);
   wheel_event.delta_x = -12;
   wheel_event.delta_y = -13;
   wheel_event.phase = blink::WebMouseWheelEvent::kPhaseBegan;
@@ -478,6 +498,8 @@
 
   wheel_event.SetPositionInWidget(-transformed_point.x() + 14,
                                   -transformed_point.y() + 15);
+  wheel_event.SetPositionInScreen(-transformed_point.x() + 14,
+                                  -transformed_point.y() + 15);
   wheel_event.delta_x = -16;
   wheel_event.delta_y = -17;
   wheel_event.phase = blink::WebMouseWheelEvent::kPhaseBegan;
diff --git a/content/browser/renderer_host/render_widget_host_view_event_handler.cc b/content/browser/renderer_host/render_widget_host_view_event_handler.cc
index 4e2075b..21f93de0 100644
--- a/content/browser/renderer_host/render_widget_host_view_event_handler.cc
+++ b/content/browser/renderer_host/render_widget_host_view_event_handler.cc
@@ -124,6 +124,8 @@
       pinch_zoom_enabled_(content::IsPinchToZoomEnabled()),
       set_focus_on_mouse_down_or_key_event_(false),
       synthetic_move_sent_(false),
+      enable_consolidated_movement_(
+          base::FeatureList::IsEnabled(features::kConsolidatedMovementXY)),
       host_(host),
       host_view_(host_view),
       popup_child_host_view_(nullptr),
@@ -184,9 +186,9 @@
     cursor_client->LockCursor();
   }
 
-  if (ShouldMoveToCenter()) {
-    MoveCursorToCenter();
-  }
+  if (ShouldMoveToCenter(unlocked_global_mouse_position_))
+    MoveCursorToCenter(nullptr);
+
   delegate_->SetTooltipsEnabled(false);
   return true;
 }
@@ -214,6 +216,8 @@
   // ModifyEventMovementAndCoords function.
   global_mouse_position_ = unlocked_global_mouse_position_;
   window_->MoveCursorTo(gfx::ToFlooredPoint(unlocked_mouse_position_));
+  synthetic_move_position_ =
+      gfx::ToFlooredPoint(unlocked_global_mouse_position_);
 
   aura::client::CursorClient* cursor_client =
       aura::client::GetCursorClient(root_window);
@@ -749,7 +753,7 @@
     if (event->flags() & ui::EF_IS_NON_CLIENT) {
       // TODO(jonross): ideally this would not be done for mus
       // (crbug.com/621412)
-      MoveCursorToCenter();
+      MoveCursorToCenter(event);
       return;
     }
 
@@ -780,15 +784,13 @@
       }
     }
 
+    bool should_not_forward = is_move_to_center_event && synthetic_move_sent_;
+
     ModifyEventMovementAndCoords(*event, &mouse_event);
 
-    bool should_not_forward = is_move_to_center_event && synthetic_move_sent_;
-    if (should_not_forward) {
+    if (!enable_consolidated_movement_ && should_not_forward) {
       synthetic_move_sent_ = false;
     } else {
-      // Check if the mouse has reached the border and needs to be centered.
-      if (ShouldMoveToCenter())
-        MoveCursorToCenter();
       bool is_selection_popup = NeedsInputGrab(popup_child_host_view_);
       // Forward event to renderer.
       if (CanRendererHandleEvent(event, mouse_locked_, is_selection_popup) &&
@@ -804,6 +806,14 @@
         if (event->type() == ui::ET_MOUSE_PRESSED)
           SetKeyboardFocus();
       }
+
+      // Check if the mouse has reached the border and needs to be centered.
+      // Use event position if consolidated_movement_ is enabled, otherwise use
+      // stored global_mouse_position_.
+      if (ShouldMoveToCenter(enable_consolidated_movement_
+                                 ? gfx::PointF(mouse_event.PositionInScreen())
+                                 : global_mouse_position_))
+        MoveCursorToCenter(event);
     }
   }
   if (!ShouldGenerateAppCommand(event))
@@ -813,39 +823,56 @@
 void RenderWidgetHostViewEventHandler::ModifyEventMovementAndCoords(
     const ui::MouseEvent& ui_mouse_event,
     blink::WebMouseEvent* event) {
-  // If the mouse has just entered, we must report zero movementX/Y. Hence we
-  // reset any global_mouse_position set previously.
-  if (ui_mouse_event.type() == ui::ET_MOUSE_ENTERED ||
-      ui_mouse_event.type() == ui::ET_MOUSE_EXITED) {
+  if (!enable_consolidated_movement_) {
+    // If the mouse has just entered, we must report zero movementX/Y. Hence we
+    // reset any global_mouse_position set previously.
+    if (ui_mouse_event.type() == ui::ET_MOUSE_ENTERED ||
+        ui_mouse_event.type() == ui::ET_MOUSE_EXITED) {
+      global_mouse_position_.SetPoint(event->PositionInScreen().x,
+                                      event->PositionInScreen().y);
+    }
+
+    // Movement is computed by taking the difference of the new cursor position
+    // and the previous. Under mouse lock the cursor will be warped back to the
+    // center so that we are not limited by clipping boundaries.
+    // We do not measure movement as the delta from cursor to center because
+    // we may receive more mouse movement events before our warp has taken
+    // effect.
+    // TODO(crbug.com/802067): We store event coordinates as pointF but
+    // movement_x/y are integer. In order not to lose fractional part, we need
+    // to keep the movement calculation as "floor(cur_pos) - floor(last_pos)".
+    // Remove the floor here when movement_x/y is changed to double.
+    event->movement_x = gfx::ToFlooredInt(event->PositionInScreen().x) -
+                        gfx::ToFlooredInt(global_mouse_position_.x());
+    event->movement_y = gfx::ToFlooredInt(event->PositionInScreen().y) -
+                        gfx::ToFlooredInt(global_mouse_position_.y());
+
     global_mouse_position_.SetPoint(event->PositionInScreen().x,
                                     event->PositionInScreen().y);
   }
 
-  // Movement is computed by taking the difference of the new cursor position
-  // and the previous. Under mouse lock the cursor will be warped back to the
-  // center so that we are not limited by clipping boundaries.
-  // We do not measure movement as the delta from cursor to center because
-  // we may receive more mouse movement events before our warp has taken
-  // effect.
-  // TODO(crbug.com/802067): We store event coordinates as pointF but
-  // movement_x/y are integer. In order not to lose fractional part, we need
-  // to keep the movement calculation as "floor(cur_pos) - floor(last_pos)".
-  // Remove the floor here when movement_x/y is changed to double.
-  event->movement_x = gfx::ToFlooredInt(event->PositionInScreen().x) -
-                      gfx::ToFlooredInt(global_mouse_position_.x());
-  event->movement_y = gfx::ToFlooredInt(event->PositionInScreen().y) -
-                      gfx::ToFlooredInt(global_mouse_position_.y());
-
-  global_mouse_position_.SetPoint(event->PositionInScreen().x,
-                                  event->PositionInScreen().y);
+  // This logic is similar to |is_move_to_center_event| check when
+  // consolidated_movement disabled. We can not guarantee that |MoveCursorTo|
+  // is taking effect immediately, so wait for the event that has matching
+  // coordiantes to marked as synthesized event.
+  if (enable_consolidated_movement_ && synthetic_move_position_.has_value() &&
+      synthetic_move_position_.value() ==
+          gfx::ToRoundedPoint(event->PositionInScreen())) {
+    event->SetModifiers(event->GetModifiers() |
+                        blink::WebInputEvent::Modifiers::kRelativeMotionEvent);
+    synthetic_move_position_.reset();
+    return;
+  }
 
   // Under mouse lock, coordinates of mouse are locked to what they were when
   // mouse lock was entered.
   if (mouse_locked_) {
-    event->SetPositionInWidget(unlocked_mouse_position_.x(),
-                               unlocked_mouse_position_.y());
-    event->SetPositionInScreen(unlocked_global_mouse_position_.x(),
-                               unlocked_global_mouse_position_.y());
+    if (!enable_consolidated_movement_) {
+      event->SetPositionInWidget(unlocked_mouse_position_.x(),
+                                 unlocked_mouse_position_.y());
+      event->SetPositionInScreen(unlocked_global_mouse_position_.x(),
+                                 unlocked_global_mouse_position_.y());
+    }
   } else {
     unlocked_mouse_position_.SetPoint(event->PositionInWidget().x,
                                       event->PositionInWidget().y);
@@ -854,19 +881,35 @@
   }
 }
 
-void RenderWidgetHostViewEventHandler::MoveCursorToCenter() {
+void RenderWidgetHostViewEventHandler::MoveCursorToCenter(
+    ui::MouseEvent* event) {
+  gfx::PointF center_in_screen(window_->GetBoundsInScreen().CenterPoint());
 #if defined(OS_WIN)
   // TODO(crbug.com/781182): Set the global position when move cursor to center.
-  // This is a workaround for a bug from Windows update 16299, and should be remove
-  // once the bug is fixed in OS.
-  gfx::PointF center_in_screen(window_->GetBoundsInScreen().CenterPoint());
+  // This is a workaround for a bug from Windows update 16299, and should be
+  // remove once the bug is fixed in OS. When consolidate_movement_ flag is
+  // enabled, send a synthesized event to update the blink side states.
   global_mouse_position_ = center_in_screen;
+  if (enable_consolidated_movement_ && event) {
+    blink::WebMouseEvent mouse_event = ui::MakeWebMouseEvent(*event);
+    mouse_event.SetModifiers(
+        mouse_event.GetModifiers() |
+        blink::WebInputEvent::Modifiers::kRelativeMotionEvent);
+    mouse_event.SetPositionInScreen(center_in_screen);
+    if (ShouldRouteEvents()) {
+      host_->delegate()->GetInputEventRouter()->RouteMouseEvent(
+          host_view_, &mouse_event, *event->latency());
+    } else {
+      ProcessMouseEvent(mouse_event, *event->latency());
+    }
+  }
 #else
   synthetic_move_sent_ = true;
 #endif
 
   gfx::Point center(gfx::Rect(window_->bounds().size()).CenterPoint());
   window_->MoveCursorTo(center);
+  synthetic_move_position_ = gfx::ToFlooredPoint(center_in_screen);
 }
 
 void RenderWidgetHostViewEventHandler::SetKeyboardFocus() {
@@ -887,16 +930,17 @@
   }
 }
 
-bool RenderWidgetHostViewEventHandler::ShouldMoveToCenter() {
+bool RenderWidgetHostViewEventHandler::ShouldMoveToCenter(
+    gfx::PointF mouse_screen_position) {
   gfx::Rect rect = window_->bounds();
   rect = delegate_->ConvertRectToScreen(rect);
   float border_x = rect.width() * kMouseLockBorderPercentage / 100.0;
   float border_y = rect.height() * kMouseLockBorderPercentage / 100.0;
 
-  return global_mouse_position_.x() < rect.x() + border_x ||
-         global_mouse_position_.x() > rect.right() - border_x ||
-         global_mouse_position_.y() < rect.y() + border_y ||
-         global_mouse_position_.y() > rect.bottom() - border_y;
+  return mouse_screen_position.x() < rect.x() + border_x ||
+         mouse_screen_position.x() > rect.right() - border_x ||
+         mouse_screen_position.y() < rect.y() + border_y ||
+         mouse_screen_position.y() > rect.bottom() - border_y;
 }
 
 bool RenderWidgetHostViewEventHandler::ShouldRouteEvents() const {
diff --git a/content/browser/renderer_host/render_widget_host_view_event_handler.h b/content/browser/renderer_host/render_widget_host_view_event_handler.h
index 19c945b..9d620c8 100644
--- a/content/browser/renderer_host/render_widget_host_view_event_handler.h
+++ b/content/browser/renderer_host/render_widget_host_view_event_handler.h
@@ -206,14 +206,16 @@
                                     blink::WebMouseEvent* event);
 
   // This method moves cursor to window center for pointer lock.
-  void MoveCursorToCenter();
+  // In Windows, a non-null |event| is used for creating the synthesize move to
+  // update blink side states.
+  void MoveCursorToCenter(ui::MouseEvent* event);
 
   // Helper function to set keyboard focus to the main window.
   void SetKeyboardFocus();
 
   // Helper method to determine if, in mouse locked mode, the cursor should be
   // moved to center.
-  bool ShouldMoveToCenter();
+  bool ShouldMoveToCenter(gfx::PointF mouse_screen_position);
 
   // Returns true when we can hit test input events with location data to be
   // sent to the targeted RenderWidgetHost.
@@ -269,6 +271,10 @@
   // This flag is used to differentiate between these synthetic mouse move
   // events vs. normal mouse move events.
   bool synthetic_move_sent_;
+
+  bool enable_consolidated_movement_;
+  base::Optional<gfx::Point> synthetic_move_position_;
+
   // Stores the current state of the active pointers targeting this
   // object.
   ui::MotionEventAura pointer_state_;
diff --git a/content/browser/scheduler/browser_task_executor.cc b/content/browser/scheduler/browser_task_executor.cc
index 15ae24ad..a0a22330 100644
--- a/content/browser/scheduler/browser_task_executor.cc
+++ b/content/browser/scheduler/browser_task_executor.cc
@@ -15,6 +15,7 @@
 #include "content/browser/browser_process_sub_thread.h"
 #include "content/browser/browser_thread_impl.h"
 #include "content/public/browser/browser_task_traits.h"
+#include "content/public/common/content_features.h"
 
 #if defined(OS_ANDROID)
 #include "base/android/task_scheduler/post_task_android.h"
@@ -278,11 +279,10 @@
   options.message_loop_type = base::MessagePump::Type::IO;
   options.task_environment =
       g_browser_task_executor->browser_io_task_environment_.release();
-#if defined(OS_ANDROID) || defined(OS_CHROMEOS) || defined(USE_OZONE)
   // Up the priority of the |io_thread_| as some of its IPCs relate to
   // display tasks.
-  options.priority = base::ThreadPriority::DISPLAY;
-#endif
+  if (base::FeatureList::IsEnabled(features::kBrowserUseDisplayThreadPriority))
+    options.priority = base::ThreadPriority::DISPLAY;
   if (!io_thread->StartWithOptions(options))
     LOG(FATAL) << "Failed to start BrowserThread:IO";
   return io_thread;
diff --git a/content/browser/service_worker/service_worker_fetch_dispatcher.cc b/content/browser/service_worker/service_worker_fetch_dispatcher.cc
index d027d892..148ec3ac 100644
--- a/content/browser/service_worker/service_worker_fetch_dispatcher.cc
+++ b/content/browser/service_worker/service_worker_fetch_dispatcher.cc
@@ -18,6 +18,7 @@
 #include "content/browser/child_process_security_policy_impl.h"
 #include "content/browser/devtools/service_worker_devtools_agent_host.h"
 #include "content/browser/devtools/service_worker_devtools_manager.h"
+#include "content/browser/frame_host/frame_tree_node.h"
 #include "content/browser/loader/navigation_url_loader_impl.h"
 #include "content/browser/loader/resource_dispatcher_host_impl.h"
 #include "content/browser/loader/resource_request_info_impl.h"
@@ -292,15 +293,16 @@
 
 // Creates the network URLLoaderFactory for the navigation preload request.
 void CreateNetworkFactoryForNavigationPreloadOnUI(
-    const ServiceWorkerFetchDispatcher::WebContentsGetter& web_contents_getter,
+    int frame_tree_node_id,
     scoped_refptr<ServiceWorkerContextWrapper> context_wrapper,
     network::mojom::URLLoaderFactoryRequest request) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(base::FeatureList::IsEnabled(network::features::kNetworkService));
 
-  WebContents* web_contents = web_contents_getter.Run();
+  FrameTreeNode* frame_tree_node =
+      FrameTreeNode::GloballyFindByID(frame_tree_node_id);
   StoragePartitionImpl* partition = context_wrapper->storage_partition();
-  if (!web_contents || !partition) {
+  if (!frame_tree_node || !partition) {
     // The navigation was cancelled or we are in shutdown. Just drop the
     // request. Otherwise, we might go to network without consulting the
     // embedder first, which would break guarantees.
@@ -314,7 +316,7 @@
   // origin instead.
   url::Origin initiator = url::Origin();
 
-  // We ignore the value of |bypass_redirect_checks_unused| since a redirects is
+  // We ignore the value of |bypass_redirect_checks_unused| since a redirect is
   // just relayed to the service worker where preloadResponse is resolved as
   // redirect.
   bool bypass_redirect_checks_unused;
@@ -322,9 +324,9 @@
   // Consult the embedder.
   network::mojom::TrustedURLLoaderHeaderClientPtrInfo header_client;
   GetContentClient()->browser()->WillCreateURLLoaderFactory(
-      web_contents->GetBrowserContext(), web_contents->GetMainFrame(),
-      web_contents->GetMainFrame()->GetProcess()->GetID(),
-      true /* is_navigation */, false /* is_download */, initiator, &request,
+      partition->browser_context(), frame_tree_node->current_frame_host(),
+      frame_tree_node->current_frame_host()->GetProcess()->GetID(),
+      /*is_navigation=*/true, /*is_download=*/false, initiator, &request,
       &header_client, &bypass_redirect_checks_unused);
 
   // Make the network factory.
@@ -645,7 +647,7 @@
     const network::ResourceRequest& original_request,
     URLLoaderFactoryGetter* url_loader_factory_getter,
     scoped_refptr<ServiceWorkerContextWrapper> context_wrapper,
-    const WebContentsGetter& web_contents_getter) {
+    int frame_tree_node_id) {
   if (resource_type_ != ResourceType::kMainFrame &&
       resource_type_ != ResourceType::kSubFrame) {
     return false;
@@ -680,7 +682,7 @@
     base::PostTaskWithTraits(
         FROM_HERE, {BrowserThread::UI},
         base::BindOnce(&CreateNetworkFactoryForNavigationPreloadOnUI,
-                       web_contents_getter, std::move(context_wrapper),
+                       frame_tree_node_id, std::move(context_wrapper),
                        mojo::MakeRequest(&network_factory)));
     factory = base::MakeRefCounted<network::WrapperSharedURLLoaderFactory>(
         std::move(network_factory));
diff --git a/content/browser/service_worker/service_worker_fetch_dispatcher.h b/content/browser/service_worker/service_worker_fetch_dispatcher.h
index 14222de..a7940e04 100644
--- a/content/browser/service_worker/service_worker_fetch_dispatcher.h
+++ b/content/browser/service_worker/service_worker_fetch_dispatcher.h
@@ -66,7 +66,7 @@
       const network::ResourceRequest& original_request,
       URLLoaderFactoryGetter* url_loader_factory_getter,
       scoped_refptr<ServiceWorkerContextWrapper> context_wrapper,
-      const WebContentsGetter& web_contents_getter);
+      int frame_tree_node_id);
 
   // Dispatches a fetch event to the |version| given in ctor, and fires
   // |fetch_callback_| (also given in ctor) once a response is received from the
diff --git a/content/browser/service_worker/service_worker_navigation_loader.cc b/content/browser/service_worker/service_worker_navigation_loader.cc
index 0b0ebb5..4a1699b 100644
--- a/content/browser/service_worker/service_worker_navigation_loader.cc
+++ b/content/browser/service_worker/service_worker_navigation_loader.cc
@@ -223,7 +223,7 @@
                      weak_factory_.GetWeakPtr()));
   did_navigation_preload_ = fetch_dispatcher_->MaybeStartNavigationPreload(
       resource_request_, url_loader_factory_getter_.get(), std::move(context),
-      provider_host_->web_contents_getter());
+      provider_host_->frame_tree_node_id());
 
   // Record worker start time here as |fetch_dispatcher_| will start a service
   // worker if there is no running service worker.
diff --git a/content/browser/service_worker/service_worker_provider_host.h b/content/browser/service_worker/service_worker_provider_host.h
index 5a62b7c..7911b526 100644
--- a/content/browser/service_worker/service_worker_provider_host.h
+++ b/content/browser/service_worker/service_worker_provider_host.h
@@ -164,6 +164,7 @@
   const WebContentsGetter& web_contents_getter() const {
     return web_contents_getter_;
   }
+  int frame_tree_node_id() const { return frame_tree_node_id_; }
 
   bool is_parent_frame_secure() const { return is_parent_frame_secure_; }
 
diff --git a/content/browser/storage_partition_impl_browsertest.cc b/content/browser/storage_partition_impl_browsertest.cc
index d1f5c03..0d232a9 100644
--- a/content/browser/storage_partition_impl_browsertest.cc
+++ b/content/browser/storage_partition_impl_browsertest.cc
@@ -160,7 +160,7 @@
 
   base::ScopedAllowBlockingForTesting allow_blocking;
   std::unique_ptr<ShellBrowserContext> browser_context =
-      std::make_unique<ShellBrowserContext>(true, nullptr);
+      std::make_unique<ShellBrowserContext>(true);
   auto* partition =
       BrowserContext::GetDefaultStoragePartition(browser_context.get());
   auto shared_url_loader_factory_info =
@@ -184,7 +184,7 @@
 
   base::ScopedAllowBlockingForTesting allow_blocking;
   std::unique_ptr<ShellBrowserContext> browser_context =
-      std::make_unique<ShellBrowserContext>(true, nullptr);
+      std::make_unique<ShellBrowserContext>(true);
   auto* partition =
       BrowserContext::GetDefaultStoragePartition(browser_context.get());
   auto factory_owner = IOThreadSharedURLLoaderFactoryOwner::Create(
@@ -207,7 +207,7 @@
 
   base::ScopedAllowBlockingForTesting allow_blocking;
   std::unique_ptr<ShellBrowserContext> browser_context =
-      std::make_unique<ShellBrowserContext>(true, nullptr);
+      std::make_unique<ShellBrowserContext>(true);
   auto* partition =
       BrowserContext::GetDefaultStoragePartition(browser_context.get());
 
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index b504f37..ba964827 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -40,7 +40,6 @@
 #include "components/download/public/common/download_stats.h"
 #include "components/rappor/public/rappor_utils.h"
 #include "components/url_formatter/url_formatter.h"
-#include "content/browser/accessibility/accessibility_tree_formatter.h"
 #include "content/browser/accessibility/accessibility_tree_formatter_blink.h"
 #include "content/browser/bad_message.h"
 #include "content/browser/browser_main_loop.h"
diff --git a/content/browser/web_contents/web_contents_impl_browsertest.cc b/content/browser/web_contents/web_contents_impl_browsertest.cc
index e75ece1..7a540c1 100644
--- a/content/browser/web_contents/web_contents_impl_browsertest.cc
+++ b/content/browser/web_contents/web_contents_impl_browsertest.cc
@@ -977,33 +977,35 @@
   }
 
  protected:
-  bool TestResourceLoadFromDedicatedWorker(const GURL& url,
-                                           const GURL& worker) {
-    // Do a cross-process navigation to clear the in-memory cache.
-    // We assume that we don't start this call from "chrome://gpu", as
-    // otherwise it won't be a cross-process navigation. We are relying
-    // on this navigation to discard the old process.
-    EXPECT_TRUE(NavigateToURL(shell(), GetWebUIURL("gpu")));
-
-    // Observe network requests.
-    ResourceLoadObserver observer(shell());
-
-    EXPECT_TRUE(NavigateToURL(shell(), url));
-
-    const char kLoadWorkerScript[] = "let w = new Worker('%s');";
-    std::string create_worker_script =
-        base::StringPrintf(kLoadWorkerScript, worker.spec().c_str());
-
-    EXPECT_TRUE(ExecuteScript(shell(), create_worker_script));
-
-    GURL resource = GenURL("3p.com", "/script");
-    observer.WaitForResourceCompletion(resource);
-    return (*observer.FindResource(resource))->was_cached;
+  // Loads 3p.com/script on page |url|, optionally from |sub_frame| if it's
+  // valid, and returns whether the script was cached or not.
+  bool TestResourceLoad(const GURL& url, const GURL& sub_frame) {
+    return TestResourceLoadHelper(url, sub_frame, GURL());
   }
 
-  // Loads 3p.com/script on page |url|, optionally from |sub_frame| if it's
-  // valid and return if the script was cached or not.
-  bool TestResourceLoad(const GURL& url, const GURL& sub_frame) {
+  // Loads 3p.com/script on page |url| from |worker| and returns whether
+  // the script was cached or not.
+  bool TestResourceLoadFromDedicatedWorker(const GURL& url,
+                                           const GURL& worker) {
+    DCHECK(worker.is_valid());
+    return TestResourceLoadHelper(url, GURL(), worker);
+  }
+
+  // Loads 3p.com/script on page |url| from |worker| inside |sub_frame|
+  // and returns whether the script was cached or not.
+  bool TestResourceLoadFromDedicatedWorkerInIframe(const GURL& url,
+                                                   const GURL& sub_frame,
+                                                   const GURL& worker) {
+    DCHECK(sub_frame.is_valid());
+    DCHECK(worker.is_valid());
+    return TestResourceLoadHelper(url, sub_frame, worker);
+  }
+
+  bool TestResourceLoadHelper(const GURL& url,
+                              const GURL& sub_frame,
+                              const GURL& worker) {
+    DCHECK(url.is_valid());
+
     // Do a cross-process navigation to clear the in-memory cache.
     // We assume that we don't start this call from "chrome://gpu", as
     // otherwise it won't be a cross-process navigation. We are relying
@@ -1027,13 +1029,13 @@
 
     // If there is supposed to be a sub-frame, create it.
     if (sub_frame.is_valid()) {
-      const char kLoadIframeScript[] =
-          "var iframe = document.createElement('iframe');"
-          "iframe.src='%s';"
-          "document.body.appendChild(iframe);";
-      std::string create_iframe_script =
-          base::StringPrintf(kLoadIframeScript, sub_frame.spec().c_str());
-      EXPECT_TRUE(ExecuteScript(shell(), create_iframe_script));
+      const char kLoadIframeScript[] = R"(
+        let iframe = document.createElement('iframe');
+        iframe.src = $1;
+        document.body.appendChild(iframe);
+      )";
+      EXPECT_TRUE(
+          ExecuteScript(shell(), JsReplace(kLoadIframeScript, sub_frame)));
       EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
 
       host_to_load_resource =
@@ -1044,16 +1046,22 @@
               ->current_frame_host();
     }
 
-    const char kLoadResourceScript[] = R"(
-    var script = document.createElement("script");
-    script.src = '%s';
-    document.body.appendChild(script);
-  )";
     GURL resource = GenURL("3p.com", "/script");
-    std::string loader_script =
-        base::StringPrintf(kLoadResourceScript, resource.spec().c_str());
 
-    EXPECT_TRUE(ExecuteScript(host_to_load_resource, loader_script));
+    if (worker.is_valid()) {
+      const char kLoadWorkerScript[] = R"(let w = new Worker($1);)";
+      EXPECT_TRUE(ExecuteScript(host_to_load_resource,
+                                JsReplace(kLoadWorkerScript, worker)));
+    } else {
+      const char kLoadResourceScript[] = R"(
+        let script = document.createElement('script');
+        script.src = $1;
+        document.body.appendChild(script);
+      )";
+      EXPECT_TRUE(ExecuteScript(host_to_load_resource,
+                                JsReplace(kLoadResourceScript, resource)));
+    }
+
     observer.WaitForResourceCompletion(resource);
 
     // Test the network isolation key.
@@ -1189,7 +1197,7 @@
   EXPECT_TRUE(TestResourceLoadFromDedicatedWorker(
       GenURL("a.com", "/title1.html"), GenURL("a.com", "/worker.js")));
 
-  // Load 3p.com/script from a worker with a new top frame origin. Due to split
+  // Load 3p.com/script from a worker with a new top-frame origin. Due to split
   // caching it's a cache miss.
   EXPECT_FALSE(TestResourceLoadFromDedicatedWorker(
       GenURL("b.com", "/title1.html"), GenURL("b.com", "/worker.js")));
@@ -1204,6 +1212,24 @@
   EXPECT_TRUE(TestResourceLoadFromDedicatedWorker(
       GenURL("c.com", "/title1.html"),
       GenURL("c.com", "/embedding_worker.js?c")));
+
+  // Load 3p.com/script from a worker with a new top-frame origin and nested in
+  // a cross-origin iframe. Due to split caching it's a cache miss.
+  EXPECT_FALSE(TestResourceLoadFromDedicatedWorkerInIframe(
+      GenURL("d.com", "/title1.html"), GenURL("e.com", "/title1.html"),
+      GenURL("e.com", "/worker.js")));
+  EXPECT_TRUE(TestResourceLoadFromDedicatedWorkerInIframe(
+      GenURL("d.com", "/title1.html"), GenURL("e.com", "/title1.html"),
+      GenURL("e.com", "/worker.js")));
+
+  // Load 3p.com/script from a worker with a new top-frame origin and nested in
+  // a cross-origin iframe whose URL has previously been loaded.
+  EXPECT_FALSE(TestResourceLoadFromDedicatedWorkerInIframe(
+      GenURL("f.com", "/title1.html"), GenURL("e.com", "/title1.html"),
+      GenURL("e.com", "/worker.js")));
+  EXPECT_TRUE(TestResourceLoadFromDedicatedWorkerInIframe(
+      GenURL("f.com", "/title1.html"), GenURL("e.com", "/title1.html"),
+      GenURL("e.com", "/worker.js")));
 }
 
 IN_PROC_BROWSER_TEST_F(WebContentsSplitCacheBrowserTestDisabled,
@@ -1215,16 +1241,26 @@
   EXPECT_TRUE(TestResourceLoadFromDedicatedWorker(
       GenURL("a.com", "/title1.html"), GenURL("a.com", "/worker.js")));
 
-  // Load 3p.com/script from b.com's worker. The cache isn't split by top frame
+  // Load 3p.com/script from b.com's worker. The cache isn't split by top-frame
   // origin so the resource is already cached.
   EXPECT_TRUE(TestResourceLoadFromDedicatedWorker(
       GenURL("b.com", "/title1.html"), GenURL("b.com", "/worker.js")));
 
   // Load 3p.com/script from a nested worker with a new top-frame origin. The
-  // cache isn't split by top frame origin so the resource is already cached.
+  // cache isn't split by top-frame origin so the resource is already cached.
   EXPECT_TRUE(TestResourceLoadFromDedicatedWorker(
       GenURL("c.com", "/title1.html"),
       GenURL("c.com", "/embedding_worker.js?c")));
+
+  // Load 3p.com/script from a worker with a new top-frame origin and nested in
+  // a cross-origin iframe. The cache isn't split by top-frame origin so the
+  // resource is already cached.
+  EXPECT_TRUE(TestResourceLoadFromDedicatedWorkerInIframe(
+      GenURL("d.com", "/title1.html"), GenURL("e.com", "/title1.html"),
+      GenURL("e.com", "/worker.js")));
+  EXPECT_TRUE(TestResourceLoadFromDedicatedWorkerInIframe(
+      GenURL("f.com", "/title1.html"), GenURL("e.com", "/title1.html"),
+      GenURL("e.com", "/worker.js")));
 }
 
 IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest,
diff --git a/content/browser/web_contents/web_contents_impl_unittest.cc b/content/browser/web_contents/web_contents_impl_unittest.cc
index a17f373..7954f085 100644
--- a/content/browser/web_contents/web_contents_impl_unittest.cc
+++ b/content/browser/web_contents/web_contents_impl_unittest.cc
@@ -3001,7 +3001,8 @@
 
   // Navigate the frame to another URL, which will send again
   // DidStartLoading and DidStopLoading messages.
-  NavigationSimulator::NavigateAndCommitFromDocument(foo_url, subframe);
+  subframe = static_cast<TestRenderFrameHost*>(
+      NavigationSimulator::NavigateAndCommitFromDocument(foo_url, subframe));
   subframe->OnMessageReceived(
       FrameHostMsg_DidStopLoading(subframe->GetRoutingID()));
 
@@ -3017,10 +3018,11 @@
   {
     auto navigation =
         NavigationSimulatorImpl::CreateBrowserInitiated(bar_url, contents());
+
     NavigationController::LoadURLParams load_params(bar_url);
     load_params.referrer = Referrer(GURL("http://referrer"),
                                     network::mojom::ReferrerPolicy::kDefault);
-    load_params.transition_type = ui::PAGE_TRANSITION_GENERATED;
+    load_params.transition_type = ui::PAGE_TRANSITION_MANUAL_SUBFRAME;
     load_params.extra_headers = "content-type: text/plain";
     load_params.load_type = NavigationController::LOAD_TYPE_DEFAULT;
     load_params.is_renderer_initiated = false;
@@ -3028,14 +3030,11 @@
     load_params.frame_tree_node_id =
         subframe->frame_tree_node()->frame_tree_node_id();
     navigation->SetLoadURLParams(&load_params);
-    navigation->Start();
-    entry_id = controller().GetPendingEntry()->GetUniqueID();
 
-    // Commit the navigation in the child frame and send the DidStopLoading
-    // message.
-    subframe->PrepareForCommit();
-    contents()->TestDidNavigate(subframe, entry_id, true, bar_url,
-                                ui::PAGE_TRANSITION_MANUAL_SUBFRAME);
+    navigation->Commit();
+    subframe = static_cast<TestRenderFrameHost*>(
+        navigation->GetFinalRenderFrameHost());
+
     subframe->OnMessageReceived(
         FrameHostMsg_DidStopLoading(subframe->GetRoutingID()));
   }
diff --git a/content/child/child_thread_impl.cc b/content/child/child_thread_impl.cc
index 691259ba..979c895 100644
--- a/content/child/child_thread_impl.cc
+++ b/content/child/child_thread_impl.cc
@@ -58,7 +58,6 @@
 #include "ipc/ipc_sync_channel.h"
 #include "ipc/ipc_sync_message_filter.h"
 #include "mojo/core/embedder/scoped_ipc_support.h"
-#include "mojo/public/cpp/platform/features.h"
 #include "mojo/public/cpp/platform/named_platform_channel.h"
 #include "mojo/public/cpp/platform/platform_channel.h"
 #include "mojo/public/cpp/platform/platform_channel_endpoint.h"
@@ -197,31 +196,23 @@
 #elif defined(OS_FUCHSIA)
   endpoint = mojo::PlatformChannel::RecoverPassedEndpointFromCommandLine(
       *base::CommandLine::ForCurrentProcess());
-#elif defined(OS_POSIX)
-
-#if defined(OS_MACOSX)
-  if (base::FeatureList::IsEnabled(mojo::features::kMojoChannelMac)) {
-    auto* client = base::MachPortRendezvousClient::GetInstance();
-    if (!client) {
-      LOG(ERROR) << "Mach rendezvous failed.";
-      return base::nullopt;
-    }
-    auto receive = client->TakeReceiveRight('mojo');
-    if (!receive.is_valid()) {
-      LOG(ERROR) << "Invalid PlatformChannel receive right";
-      return base::nullopt;
-    }
-    endpoint =
-        mojo::PlatformChannelEndpoint(mojo::PlatformHandle(std::move(receive)));
-  } else {
-#endif  // defined(OS_MACOSX)
-    endpoint = mojo::PlatformChannelEndpoint(mojo::PlatformHandle(
-        base::ScopedFD(base::GlobalDescriptors::GetInstance()->Get(
-            service_manager::kMojoIPCChannel))));
-#if defined(OS_MACOSX)
+#elif defined(OS_MACOSX)
+  auto* client = base::MachPortRendezvousClient::GetInstance();
+  if (!client) {
+    LOG(ERROR) << "Mach rendezvous failed.";
+    return base::nullopt;
   }
-#endif
-
+  auto receive = client->TakeReceiveRight('mojo');
+  if (!receive.is_valid()) {
+    LOG(ERROR) << "Invalid PlatformChannel receive right";
+    return base::nullopt;
+  }
+  endpoint =
+      mojo::PlatformChannelEndpoint(mojo::PlatformHandle(std::move(receive)));
+#elif defined(OS_POSIX)
+  endpoint = mojo::PlatformChannelEndpoint(mojo::PlatformHandle(
+      base::ScopedFD(base::GlobalDescriptors::GetInstance()->Get(
+          service_manager::kMojoIPCChannel))));
 #endif
   // Mojo isn't supported on all child process types.
   // TODO(crbug.com/604282): Support Mojo in the remaining processes.
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index de68f01..831ca5d 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -66,9 +66,6 @@
   WebRuntimeFeatures::EnableBlinkHeapIncrementalMarking(
       base::FeatureList::IsEnabled(features::kBlinkHeapIncrementalMarking));
 
-  if (base::FeatureList::IsEnabled(features::kBloatedRendererDetection))
-    WebRuntimeFeatures::EnableBloatedRendererDetection(true);
-
   if (base::FeatureList::IsEnabled(
           blink::features::kBlockingFocusWithoutUserActivation)) {
     WebRuntimeFeatures::EnableBlockingFocusWithoutUserActivation(true);
diff --git a/content/gpu/gpu_main.cc b/content/gpu/gpu_main.cc
index 35be2f6..5433738 100644
--- a/content/gpu/gpu_main.cc
+++ b/content/gpu/gpu_main.cc
@@ -296,11 +296,13 @@
 
   base::PlatformThread::SetName("CrGpuMain");
 
+#if !defined(OS_MACOSX)
   if (base::FeatureList::IsEnabled(features::kGpuUseDisplayThreadPriority)) {
     // Set thread priority before sandbox initialization.
     base::PlatformThread::SetCurrentThreadPriority(
         base::ThreadPriority::DISPLAY);
   }
+#endif
 
   auto gpu_init = std::make_unique<gpu::GpuInit>();
   ContentSandboxHelper sandbox_helper;
diff --git a/content/public/browser/BUILD.gn b/content/public/browser/BUILD.gn
index b3a677c..37d805b 100644
--- a/content/public/browser/BUILD.gn
+++ b/content/public/browser/BUILD.gn
@@ -24,6 +24,7 @@
   # External code should depend on via ":browser" above.
   visibility = [ "//content/*" ]
   sources = [
+    "accessibility_tree_formatter.h",
     "android/android_overlay_provider.h",
     "android/child_process_importance.h",
     "android/compositor.h",
diff --git a/content/browser/accessibility/accessibility_tree_formatter.h b/content/public/browser/accessibility_tree_formatter.h
similarity index 61%
rename from content/browser/accessibility/accessibility_tree_formatter.h
rename to content/public/browser/accessibility_tree_formatter.h
index 45d934c..0a94707 100644
--- a/content/browser/accessibility/accessibility_tree_formatter.h
+++ b/content/public/browser/accessibility_tree_formatter.h
@@ -1,9 +1,9 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_TREE_FORMATTER_H_
-#define CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_TREE_FORMATTER_H_
+#ifndef CONTENT_PUBLIC_BROWSER_ACCESSIBILITY_TREE_FORMATTER_H_
+#define CONTENT_PUBLIC_BROWSER_ACCESSIBILITY_TREE_FORMATTER_H_
 
 #include <stdint.h>
 
@@ -16,28 +16,25 @@
 #include "base/strings/string_piece.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
-#include "content/browser/accessibility/browser_accessibility.h"
 #include "content/common/content_export.h"
 #include "ui/gfx/native_widget_types.h"
 
-namespace {
-const char kChildrenDictAttr[] = "children";
-}
-
 namespace base {
 class CommandLine;
 }
 
 namespace content {
 
+class BrowserAccessibility;
+class BrowserAccessibilityManager;
+
 // A utility class for formatting platform-specific accessibility information,
 // for use in testing, debugging, and developer tools.
 // This is extended by a subclass for each platform where accessibility is
 // implemented.
 class CONTENT_EXPORT AccessibilityTreeFormatter {
  public:
-  explicit AccessibilityTreeFormatter();
-  virtual ~AccessibilityTreeFormatter();
+  virtual ~AccessibilityTreeFormatter() = default;
 
   // A single property filter specification. See GetAllowString() and
   // GetDenyString() for more information.
@@ -79,7 +76,8 @@
   };
   static std::vector<TestPass> GetTestPasses();
 
-  virtual void AddDefaultFilters(std::vector<PropertyFilter>* property_filters);
+  virtual void AddDefaultFilters(
+      std::vector<PropertyFilter>* property_filters) = 0;
 
   static bool MatchesPropertyFilters(
       const std::vector<PropertyFilter>& property_filters,
@@ -129,14 +127,14 @@
 
   // Returns a filtered accesibility tree using the current property and node
   // filters.
-  std::unique_ptr<base::DictionaryValue> FilterAccessibilityTree(
-      const base::DictionaryValue& dict);
+  virtual std::unique_ptr<base::DictionaryValue> FilterAccessibilityTree(
+      const base::DictionaryValue& dict) = 0;
 
   // Dumps a BrowserAccessibility tree into a string.
-  void FormatAccessibilityTree(BrowserAccessibility* root,
-                               base::string16* contents);
-  void FormatAccessibilityTree(const base::DictionaryValue& tree_node,
-                               base::string16* contents);
+  virtual void FormatAccessibilityTree(BrowserAccessibility* root,
+                                       base::string16* contents) = 0;
+  virtual void FormatAccessibilityTree(const base::DictionaryValue& tree_node,
+                                       base::string16* contents) = 0;
 
   static base::string16 DumpAccessibilityTreeFromManager(
       BrowserAccessibilityManager* ax_mgr,
@@ -144,14 +142,15 @@
 
   // Set regular expression filters that apply to each property of every node
   // before it's output.
-  void SetPropertyFilters(const std::vector<PropertyFilter>& property_filters);
+  virtual void SetPropertyFilters(
+      const std::vector<PropertyFilter>& property_filters) = 0;
 
   // Set regular expression filters that apply to every node before output.
-  void SetNodeFilters(const std::vector<NodeFilter>& node_filters);
+  virtual void SetNodeFilters(const std::vector<NodeFilter>& node_filters) = 0;
 
   // If true, the internal accessibility id of each node will be included
   // in its output.
-  void set_show_ids(bool show_ids) { show_ids_ = show_ids; }
+  virtual void set_show_ids(bool show_ids) = 0;
 
   // Suffix of the expectation file corresponding to html file.
   // Overridden by each platform subclass.
@@ -163,7 +162,7 @@
   // Most test outputs are identical but this allows a version specific
   // expected file to be used.
   virtual const base::FilePath::StringType
-  GetVersionSpecificExpectedFileSuffix();
+  GetVersionSpecificExpectedFileSuffix() = 0;
 
   // A string that indicates a given line in a file is an allow-empty,
   // allow or deny filter. Overridden by each platform subclass. Example:
@@ -184,71 +183,6 @@
   virtual const std::string GetAllowString() = 0;
   virtual const std::string GetDenyString() = 0;
   virtual const std::string GetDenyNodeString() = 0;
-
- protected:
-  //
-  // Overridden by platform subclasses.
-  //
-
-  // Process accessibility tree with filters for output.
-  // Given a dictionary that contains a platform-specific dictionary
-  // representing an accessibility tree, and utilizing property_filters_ and
-  // node_filters_:
-  // - Returns a filtered text view as one large string.
-  // - Provides a filtered version of the dictionary in an out param,
-  //   (only if the out param is provided).
-  virtual base::string16 ProcessTreeForOutput(
-      const base::DictionaryValue& node,
-      base::DictionaryValue* filtered_dict_result = nullptr) = 0;
-
-  //
-  // Utility functions to be used by each platform.
-  //
-
-  base::string16 FormatCoordinates(const char* name,
-                                   const char* x_name,
-                                   const char* y_name,
-                                   const base::DictionaryValue& value);
-
-  // Writes the given attribute string out to |line| if it matches the property
-  // filters.
-  // Returns false if the attribute was filtered out.
-  bool WriteAttribute(bool include_by_default,
-                      const base::string16& attr,
-                      base::string16* line);
-  bool WriteAttribute(bool include_by_default,
-                      const std::string& attr,
-                      base::string16* line);
-  void AddPropertyFilter(std::vector<PropertyFilter>* property_filters,
-                         std::string filter,
-                         PropertyFilter::Type type = PropertyFilter::ALLOW);
-  bool show_ids() { return show_ids_; }
-
- private:
-  void RecursiveFormatAccessibilityTree(const BrowserAccessibility& node,
-                                        base::string16* contents,
-                                        int indent);
-  void RecursiveFormatAccessibilityTree(const base::DictionaryValue& tree_node,
-                                        base::string16* contents,
-                                        int depth = 0);
-
-  bool MatchesPropertyFilters(const base::string16& text,
-                              bool default_result) const;
-  bool MatchesNodeFilters(const base::DictionaryValue& dict) const;
-
-  // Property filters used when formatting the accessibility tree as text.
-  // Any property which matches a property filter will be skipped.
-  std::vector<PropertyFilter> property_filters_;
-
-  // Node filters used when formatting the accessibility tree as text.
-  // Any node which matches a node wilder will be skipped, along with all its
-  // children.
-  std::vector<NodeFilter> node_filters_;
-
-  // Whether or not node ids should be included in the dump.
-  bool show_ids_;
-
-  DISALLOW_COPY_AND_ASSIGN(AccessibilityTreeFormatter);
 };
 
 }  // namespace content
diff --git a/content/public/browser/payment_app_provider.h b/content/public/browser/payment_app_provider.h
index 4cfb416..34546bba 100644
--- a/content/public/browser/payment_app_provider.h
+++ b/content/public/browser/payment_app_provider.h
@@ -77,7 +77,9 @@
 
   // Notify the opened payment handler window is closing or closed by user so as
   // to abort payment request.
-  virtual void OnClosingOpenedWindow(BrowserContext* browser_context) = 0;
+  virtual void OnClosingOpenedWindow(
+      BrowserContext* browser_context,
+      payments::mojom::PaymentEventResponseType reason) = 0;
 
   // Check whether given |sw_js_url| from |manifest_url| is allowed to register
   // with |sw_scope|.
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 630b3ac..813136ca 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -42,10 +42,10 @@
 // Creates audio output and input streams using the audio service.
 const base::Feature kAudioServiceAudioStreams{
   "AudioServiceAudioStreams",
-#if defined(OS_MACOSX) || defined(OS_LINUX)
-      base::FEATURE_ENABLED_BY_DEFAULT
-#else
+#if defined(OS_WIN)
       base::FEATURE_DISABLED_BY_DEFAULT
+#else
+      base::FEATURE_ENABLED_BY_DEFAULT
 #endif
 };
 
@@ -76,10 +76,6 @@
 const base::Feature kBlinkHeapIncrementalMarking{
     "BlinkHeapIncrementalMarking", base::FEATURE_ENABLED_BY_DEFAULT};
 
-// Enable bloated renderer detection.
-const base::Feature kBloatedRendererDetection{
-    "BloatedRendererDetection", base::FEATURE_DISABLED_BY_DEFAULT};
-
 // Allows swipe left/right from touchpad change browser navigation. Currently
 // only enabled by default on CrOS.
 const base::Feature kTouchpadOverscrollHistoryNavigation {
@@ -736,6 +732,15 @@
 const base::Feature kTrustedDOMTypes{"TrustedDOMTypes",
                                      base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Use ThreadPriority::DISPLAY for browser UI and IO threads.
+#if defined(OS_ANDROID) || defined(OS_CHROMEOS)
+const base::Feature kBrowserUseDisplayThreadPriority{
+    "BrowserUseDisplayThreadPriority", base::FEATURE_ENABLED_BY_DEFAULT};
+#else
+const base::Feature kBrowserUseDisplayThreadPriority{
+    "BrowserUseDisplayThreadPriority", base::FEATURE_DISABLED_BY_DEFAULT};
+#endif
+
 #if defined(OS_ANDROID)
 // Autofill Accessibility in Android.
 // crbug.com/627860
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 88262cda..7b7ceb56 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -29,7 +29,6 @@
 CONTENT_EXPORT extern const base::Feature kBackgroundFetch;
 CONTENT_EXPORT extern const base::Feature kBackForwardCache;
 CONTENT_EXPORT extern const base::Feature kBlinkHeapIncrementalMarking;
-CONTENT_EXPORT extern const base::Feature kBloatedRendererDetection;
 CONTENT_EXPORT extern const base::Feature kBlockCredentialedSubresources;
 CONTENT_EXPORT extern const base::Feature kBundledHTTPExchanges;
 CONTENT_EXPORT extern const base::Feature kCacheInlineScriptCode;
@@ -154,6 +153,7 @@
 CONTENT_EXPORT extern const base::Feature kWebXrPlaneDetection;
 CONTENT_EXPORT extern const base::Feature kScriptStreamingOnPreload;
 CONTENT_EXPORT extern const base::Feature kTrustedDOMTypes;
+CONTENT_EXPORT extern const base::Feature kBrowserUseDisplayThreadPriority;
 
 #if defined(OS_ANDROID)
 CONTENT_EXPORT extern const base::Feature kAndroidAutofillAccessibility;
diff --git a/content/renderer/browser_plugin/browser_plugin.cc b/content/renderer/browser_plugin/browser_plugin.cc
index 5bbfd2f..34769f4 100644
--- a/content/renderer/browser_plugin/browser_plugin.cc
+++ b/content/renderer/browser_plugin/browser_plugin.cc
@@ -587,6 +587,8 @@
   if (blink::WebInputEvent::IsGestureEventType(event.GetType())) {
     auto gesture_event = static_cast<const blink::WebGestureEvent&>(event);
     DCHECK(blink::WebInputEvent::kGestureTapDown == event.GetType() ||
+           blink::WebInputEvent::kGestureScrollBegin == event.GetType() ||
+           blink::WebInputEvent::kGestureScrollEnd == event.GetType() ||
            gesture_event.resending_plugin_id == browser_plugin_instance_id_);
 
     // We shouldn't be forwarding GestureEvents to the Guest anymore. Indicate
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 03ad3a2..4217051 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -7025,29 +7025,6 @@
       commit_params.current_history_list_length;
 }
 
-namespace {
-std::unique_ptr<base::DictionaryValue> GetDevToolsInitiator(
-    const WebString& initiator_str) {
-  if (initiator_str.IsNull())
-    return nullptr;
-  std::unique_ptr<base::DictionaryValue> initiator =
-      base::DictionaryValue::From(
-          base::JSONReader::ReadDeprecated(initiator_str.Utf8()));
-  if (!initiator)
-    return nullptr;
-  // TODO(kozy,caseq): the hack below is due to the fact that initiators include
-  // the chain of async callstacks that results in a tree of Values so deep
-  // that it triggers mojo structure nesting limit upon deserialization.
-  // See https://crbug.com/809996 for more details.
-  // We trim async stacks here, but it should be possible to capture locations
-  // without async stacks (or with custom limit on their number) instead.
-  base::Value* parent = initiator->FindPath({"stack", "parent"});
-  if (parent && parent->is_dict())
-    parent->RemoveKey("parent");
-  return initiator;
-}
-}  // namespace
-
 void RenderFrameImpl::BeginNavigationInternal(
     std::unique_ptr<blink::WebNavigationInfo> info,
     bool is_history_navigation_in_new_child_frame) {
@@ -7135,8 +7112,11 @@
       CloneBlobURLToken(info->blob_url_token.get()));
 
   int load_flags = info->url_request.GetLoadFlagsForWebUrlRequest();
-  std::unique_ptr<base::DictionaryValue> initiator =
-      GetDevToolsInitiator(info->devtools_initiator_info);
+  std::unique_ptr<base::DictionaryValue> initiator;
+  if (!info->devtools_initiator_info.IsNull()) {
+    initiator = base::DictionaryValue::From(
+        base::JSONReader::ReadDeprecated(info->devtools_initiator_info.Utf8()));
+  }
   mojom::BeginNavigationParamsPtr begin_navigation_params =
       mojom::BeginNavigationParams::New(
           GetWebURLRequestHeadersAsString(info->url_request), load_flags,
diff --git a/content/shell/BUILD.gn b/content/shell/BUILD.gn
index 08d7b85..bb3b556 100644
--- a/content/shell/BUILD.gn
+++ b/content/shell/BUILD.gn
@@ -137,8 +137,6 @@
     "browser/shell_javascript_dialog_manager.h",
     "browser/shell_javascript_dialog_win.cc",
     "browser/shell_mac.mm",
-    "browser/shell_net_log.cc",
-    "browser/shell_net_log.h",
     "browser/shell_network_delegate.cc",
     "browser/shell_network_delegate.h",
     "browser/shell_permission_manager.cc",
diff --git a/content/shell/browser/shell_browser_context.cc b/content/shell/browser/shell_browser_context.cc
index 226d5b3..12bbe1e 100644
--- a/content/shell/browser/shell_browser_context.cc
+++ b/content/shell/browser/shell_browser_context.cc
@@ -48,12 +48,10 @@
 }
 
 ShellBrowserContext::ShellBrowserContext(bool off_the_record,
-                                         net::NetLog* net_log,
                                          bool delay_services_creation)
     : resource_context_(new ShellResourceContext),
       ignore_certificate_errors_(false),
       off_the_record_(off_the_record),
-      net_log_(net_log),
       guest_manager_(nullptr) {
   InitWhileIOAllowed();
   if (!delay_services_creation) {
@@ -179,7 +177,7 @@
   return new ShellURLRequestContextGetter(
       ignore_certificate_errors_, off_the_record_, GetPath(),
       base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::IO}),
-      protocol_handlers, std::move(request_interceptors), net_log_);
+      protocol_handlers, std::move(request_interceptors));
 }
 
 net::URLRequestContextGetter* ShellBrowserContext::CreateRequestContext(
diff --git a/content/shell/browser/shell_browser_context.h b/content/shell/browser/shell_browser_context.h
index 14919de..abadbde 100644
--- a/content/shell/browser/shell_browser_context.h
+++ b/content/shell/browser/shell_browser_context.h
@@ -20,10 +20,6 @@
 
 class SimpleFactoryKey;
 
-namespace net {
-class NetLog;
-}
-
 namespace content {
 
 class BackgroundSyncController;
@@ -39,7 +35,6 @@
   // If |delay_services_creation| is true, the owner is responsible for calling
   // CreateBrowserContextServices() for this BrowserContext.
   ShellBrowserContext(bool off_the_record,
-                      net::NetLog* net_log,
                       bool delay_services_creation = false);
   ~ShellBrowserContext() override;
 
@@ -97,7 +92,6 @@
   }
 
   bool ignore_certificate_errors() const { return ignore_certificate_errors_; }
-  net::NetLog* net_log() const { return net_log_; }
 
   std::unique_ptr<ShellResourceContext> resource_context_;
   std::unique_ptr<ShellDownloadManagerDelegate> download_manager_delegate_;
@@ -112,7 +106,6 @@
 
   bool ignore_certificate_errors_;
   bool off_the_record_;
-  net::NetLog* net_log_;
   base::FilePath path_;
   BrowserPluginGuestManager* guest_manager_;
   scoped_refptr<ShellURLRequestContextGetter> url_request_getter_;
diff --git a/content/shell/browser/shell_browser_main_parts.cc b/content/shell/browser/shell_browser_main_parts.cc
index 266b1e0..09d0eac 100644
--- a/content/shell/browser/shell_browser_main_parts.cc
+++ b/content/shell/browser/shell_browser_main_parts.cc
@@ -24,7 +24,6 @@
 #include "content/shell/browser/shell.h"
 #include "content/shell/browser/shell_browser_context.h"
 #include "content/shell/browser/shell_devtools_manager_delegate.h"
-#include "content/shell/browser/shell_net_log.h"
 #include "content/shell/common/shell_switches.h"
 #include "device/bluetooth/bluetooth_adapter_factory.h"
 #include "net/base/filename_util.h"
@@ -137,9 +136,8 @@
 }
 
 void ShellBrowserMainParts::InitializeBrowserContexts() {
-  set_browser_context(new ShellBrowserContext(false, net_log_.get()));
-  set_off_the_record_browser_context(
-      new ShellBrowserContext(true, net_log_.get()));
+  set_browser_context(new ShellBrowserContext(false));
+  set_off_the_record_browser_context(new ShellBrowserContext(true));
 }
 
 void ShellBrowserMainParts::InitializeMessageLoopContext() {
@@ -158,8 +156,6 @@
         std::make_unique<crash_reporter::ChildProcessCrashObserver>());
   }
 #endif
-
-  net_log_ = std::make_unique<ShellNetLog>("content_shell");
   return 0;
 }
 
diff --git a/content/shell/browser/shell_browser_main_parts.h b/content/shell/browser/shell_browser_main_parts.h
index 09de5a0..17ed638a 100644
--- a/content/shell/browser/shell_browser_main_parts.h
+++ b/content/shell/browser/shell_browser_main_parts.h
@@ -14,10 +14,6 @@
 #include "content/public/common/main_function_params.h"
 #include "content/shell/browser/shell_browser_context.h"
 
-namespace net {
-class NetLog;
-}
-
 namespace content {
 
 class ShellBrowserMainParts : public BrowserMainParts {
@@ -41,8 +37,6 @@
     return off_the_record_browser_context_.get();
   }
 
-  net::NetLog* net_log() { return net_log_.get(); }
-
  protected:
   virtual void InitializeBrowserContexts();
   virtual void InitializeMessageLoopContext();
@@ -56,7 +50,6 @@
 
  private:
 
-  std::unique_ptr<net::NetLog> net_log_;
   std::unique_ptr<ShellBrowserContext> browser_context_;
   std::unique_ptr<ShellBrowserContext> off_the_record_browser_context_;
 
diff --git a/content/shell/browser/shell_content_browser_client.cc b/content/shell/browser/shell_content_browser_client.cc
index 22624464..47d16f6 100644
--- a/content/shell/browser/shell_content_browser_client.cc
+++ b/content/shell/browser/shell_content_browser_client.cc
@@ -38,7 +38,6 @@
 #include "content/shell/browser/shell_browser_context.h"
 #include "content/shell/browser/shell_browser_main_parts.h"
 #include "content/shell/browser/shell_devtools_manager_delegate.h"
-#include "content/shell/browser/shell_net_log.h"
 #include "content/shell/browser/shell_quota_permission_context.h"
 #include "content/shell/browser/shell_url_request_context_getter.h"
 #include "content/shell/browser/shell_web_contents_view_delegate_creator.h"
diff --git a/content/shell/browser/shell_net_log.cc b/content/shell/browser/shell_net_log.cc
deleted file mode 100644
index 8837dd14..0000000
--- a/content/shell/browser/shell_net_log.cc
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/shell/browser/shell_net_log.h"
-
-#include <stdio.h>
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/command_line.h"
-#include "base/files/file_path.h"
-#include "base/values.h"
-#include "build/build_config.h"
-#include "content/public/common/content_switches.h"
-#include "net/log/file_net_log_observer.h"
-#include "net/log/net_log_util.h"
-#include "services/network/public/cpp/network_switches.h"
-
-namespace content {
-
-namespace {
-
-std::unique_ptr<base::DictionaryValue> GetShellConstants(
-    const std::string& app_name) {
-  std::unique_ptr<base::DictionaryValue> constants_dict =
-      net::GetNetConstants();
-
-  // Add a dictionary with client information
-  auto dict = std::make_unique<base::DictionaryValue>();
-
-  dict->SetString("name", app_name);
-  dict->SetString(
-      "command_line",
-      base::CommandLine::ForCurrentProcess()->GetCommandLineString());
-
-  constants_dict->Set("clientInfo", std::move(dict));
-
-  return constants_dict;
-}
-
-}  // namespace
-
-ShellNetLog::ShellNetLog(const std::string& app_name) {
-  const base::CommandLine* command_line =
-      base::CommandLine::ForCurrentProcess();
-
-  if (command_line->HasSwitch(network::switches::kLogNetLog)) {
-    base::FilePath log_path =
-        command_line->GetSwitchValuePath(network::switches::kLogNetLog);
-    net::NetLogCaptureMode capture_mode = net::NetLogCaptureMode::Default();
-
-    file_net_log_observer_ = net::FileNetLogObserver::CreateUnbounded(
-        log_path, GetShellConstants(app_name));
-    file_net_log_observer_->StartObserving(this, capture_mode);
-  }
-}
-
-ShellNetLog::~ShellNetLog() {
-  // Remove the observer we own before we're destroyed.
-  if (file_net_log_observer_)
-    file_net_log_observer_->StopObserving(nullptr, base::OnceClosure());
-}
-
-}  // namespace content
diff --git a/content/shell/browser/shell_net_log.h b/content/shell/browser/shell_net_log.h
deleted file mode 100644
index 13ac91b..0000000
--- a/content/shell/browser/shell_net_log.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_SHELL_BROWSER_SHELL_NET_LOG_H_
-#define CONTENT_SHELL_BROWSER_SHELL_NET_LOG_H_
-
-#include <memory>
-#include <string>
-
-#include "base/macros.h"
-#include "net/log/net_log.h"
-
-namespace net {
-class FileNetLogObserver;
-}
-
-namespace content {
-
-class ShellNetLog : public net::NetLog {
- public:
-  explicit ShellNetLog(const std::string& app_name);
-  ~ShellNetLog() override;
-
- private:
-  std::unique_ptr<net::FileNetLogObserver> file_net_log_observer_;
-
-  DISALLOW_COPY_AND_ASSIGN(ShellNetLog);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_SHELL_BROWSER_SHELL_NET_LOG_H_
diff --git a/content/shell/browser/shell_url_request_context_getter.cc b/content/shell/browser/shell_url_request_context_getter.cc
index a461c866..fc37df047 100644
--- a/content/shell/browser/shell_url_request_context_getter.cc
+++ b/content/shell/browser/shell_url_request_context_getter.cc
@@ -87,14 +87,12 @@
     const base::FilePath& base_path,
     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
     ProtocolHandlerMap* protocol_handlers,
-    URLRequestInterceptorScopedVector request_interceptors,
-    net::NetLog* net_log)
+    URLRequestInterceptorScopedVector request_interceptors)
     : ignore_certificate_errors_(ignore_certificate_errors),
       off_the_record_(off_the_record),
       shut_down_(false),
       base_path_(base_path),
       io_task_runner_(std::move(io_task_runner)),
-      net_log_(net_log),
       request_interceptors_(std::move(request_interceptors)) {
   // Must first be created on the UI thread.
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -160,10 +158,10 @@
         *base::CommandLine::ForCurrentProcess();
 
     net::URLRequestContextBuilder builder;
-    builder.set_net_log(net_log_);
+    net::NetLog* net_log = nullptr;
     builder.set_network_delegate(CreateNetworkDelegate());
     std::unique_ptr<net::CookieStore> cookie_store =
-        CreateCookieStore(CookieStoreConfig(), net_log_);
+        CreateCookieStore(CookieStoreConfig(), net_log);
     builder.SetCookieStore(std::move(cookie_store));
     builder.set_accept_language(GetAcceptLanguages());
     builder.set_user_agent(GetShellUserAgent());
diff --git a/content/shell/browser/shell_url_request_context_getter.h b/content/shell/browser/shell_url_request_context_getter.h
index fa20e77..eec78ad 100644
--- a/content/shell/browser/shell_url_request_context_getter.h
+++ b/content/shell/browser/shell_url_request_context_getter.h
@@ -21,7 +21,6 @@
 
 namespace net {
 class CertVerifier;
-class NetLog;
 class NetworkDelegate;
 class ProxyConfigService;
 class ProxyResolutionService;
@@ -38,8 +37,7 @@
       const base::FilePath& base_path,
       scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
       ProtocolHandlerMap* protocol_handlers,
-      URLRequestInterceptorScopedVector request_interceptors,
-      net::NetLog* net_log);
+      URLRequestInterceptorScopedVector request_interceptors);
 
   // net::URLRequestContextGetter implementation.
   net::URLRequestContext* GetURLRequestContext() override;
@@ -75,7 +73,6 @@
   bool shut_down_;
   base::FilePath base_path_;
   scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
-  net::NetLog* net_log_;
 
   std::unique_ptr<net::ProxyConfigService> proxy_config_service_;
   std::unique_ptr<net::URLRequestContext> url_request_context_;
diff --git a/content/shell/browser/web_test/web_test_browser_context.cc b/content/shell/browser/web_test/web_test_browser_context.cc
index 7fed044d..0635ec4d 100644
--- a/content/shell/browser/web_test/web_test_browser_context.cc
+++ b/content/shell/browser/web_test/web_test_browser_context.cc
@@ -36,9 +36,8 @@
 
 namespace content {
 
-WebTestBrowserContext::WebTestBrowserContext(bool off_the_record,
-                                             net::NetLog* net_log)
-    : ShellBrowserContext(off_the_record, net_log) {
+WebTestBrowserContext::WebTestBrowserContext(bool off_the_record)
+    : ShellBrowserContext(off_the_record) {
   // Overrides geolocation coordinates for testing.
   geolocation_overrider_ =
       std::make_unique<device::ScopedGeolocationOverrider>(0, 0);
@@ -55,7 +54,7 @@
   return new WebTestURLRequestContextGetter(
       ignore_certificate_errors(), IsOffTheRecord(), GetPath(),
       base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::IO}),
-      protocol_handlers, std::move(request_interceptors), net_log());
+      protocol_handlers, std::move(request_interceptors));
 }
 
 DownloadManagerDelegate* WebTestBrowserContext::GetDownloadManagerDelegate() {
diff --git a/content/shell/browser/web_test/web_test_browser_context.h b/content/shell/browser/web_test/web_test_browser_context.h
index ea98b141..22e140d8 100644
--- a/content/shell/browser/web_test/web_test_browser_context.h
+++ b/content/shell/browser/web_test/web_test_browser_context.h
@@ -13,10 +13,6 @@
 class ScopedGeolocationOverrider;
 }
 
-namespace net {
-class NetLog;
-}
-
 namespace content {
 
 class BackgroundSyncController;
@@ -29,7 +25,7 @@
 
 class WebTestBrowserContext final : public ShellBrowserContext {
  public:
-  WebTestBrowserContext(bool off_the_record, net::NetLog* net_log);
+  WebTestBrowserContext(bool off_the_record);
   ~WebTestBrowserContext() override;
 
   // BrowserContext implementation.
diff --git a/content/shell/browser/web_test/web_test_browser_main_parts.cc b/content/shell/browser/web_test/web_test_browser_main_parts.cc
index 694b1ef4..8309fcfe 100644
--- a/content/shell/browser/web_test/web_test_browser_main_parts.cc
+++ b/content/shell/browser/web_test/web_test_browser_main_parts.cc
@@ -19,7 +19,6 @@
 #include "content/shell/browser/shell.h"
 #include "content/shell/browser/shell_browser_context.h"
 #include "content/shell/browser/shell_devtools_manager_delegate.h"
-#include "content/shell/browser/shell_net_log.h"
 #include "content/shell/browser/web_test/web_test_browser_context.h"
 #include "content/shell/common/shell_switches.h"
 #include "net/base/filename_util.h"
@@ -50,7 +49,7 @@
 WebTestBrowserMainParts::~WebTestBrowserMainParts() {}
 
 void WebTestBrowserMainParts::InitializeBrowserContexts() {
-  set_browser_context(new WebTestBrowserContext(false, net_log()));
+  set_browser_context(new WebTestBrowserContext(false));
   set_off_the_record_browser_context(nullptr);
 }
 
diff --git a/content/shell/browser/web_test/web_test_url_request_context_getter.cc b/content/shell/browser/web_test/web_test_url_request_context_getter.cc
index 78aea74..f97bdf9 100644
--- a/content/shell/browser/web_test/web_test_url_request_context_getter.cc
+++ b/content/shell/browser/web_test/web_test_url_request_context_getter.cc
@@ -24,15 +24,13 @@
     const base::FilePath& base_path,
     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
     ProtocolHandlerMap* protocol_handlers,
-    URLRequestInterceptorScopedVector request_interceptors,
-    net::NetLog* net_log)
+    URLRequestInterceptorScopedVector request_interceptors)
     : ShellURLRequestContextGetter(ignore_certificate_errors,
                                    off_the_record,
                                    base_path,
                                    std::move(io_task_runner),
                                    protocol_handlers,
-                                   std::move(request_interceptors),
-                                   net_log) {
+                                   std::move(request_interceptors)) {
   // Must first be created on the UI thread.
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
diff --git a/content/shell/browser/web_test/web_test_url_request_context_getter.h b/content/shell/browser/web_test/web_test_url_request_context_getter.h
index ccf0f53..825742a 100644
--- a/content/shell/browser/web_test/web_test_url_request_context_getter.h
+++ b/content/shell/browser/web_test/web_test_url_request_context_getter.h
@@ -18,7 +18,6 @@
 
 namespace net {
 class NetworkDelegate;
-class NetLog;
 class ProxyConfigService;
 }  // namespace net
 
@@ -32,8 +31,7 @@
       const base::FilePath& base_path,
       scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
       ProtocolHandlerMap* protocol_handlers,
-      URLRequestInterceptorScopedVector request_interceptors,
-      net::NetLog* net_log);
+      URLRequestInterceptorScopedVector request_interceptors);
 
  protected:
   ~WebTestURLRequestContextGetter() override;
diff --git a/content/test/fuzzer/BUILD.gn b/content/test/fuzzer/BUILD.gn
index d34ca44..4d58db5 100644
--- a/content/test/fuzzer/BUILD.gn
+++ b/content/test/fuzzer/BUILD.gn
@@ -259,7 +259,6 @@
     "../../browser/speech/audio_encoder.cc",
     "../../browser/speech/audio_encoder.h",
     "../../browser/speech/audio_encoder_fuzzer.cc",
-    "//base/test/fuzzed_data_provider.h",
     "//content/common/content_export.h",
   ]
 
diff --git a/content/test/gpu/gpu_tests/cloud_storage_integration_test_base.py b/content/test/gpu/gpu_tests/cloud_storage_integration_test_base.py
index affdd6e..154479d 100644
--- a/content/test/gpu/gpu_tests/cloud_storage_integration_test_base.py
+++ b/content/test/gpu/gpu_tests/cloud_storage_integration_test_base.py
@@ -141,12 +141,6 @@
       help='For Skia Gold integration. Always report that the test passed even '
            'if the Skia Gold image comparison reported a failure, but '
            'otherwise perform the same steps as usual.')
-    parser.add_option(
-      '--stream-goldctl-output',
-      action='store_true', default=False,
-      help='When running goldctl commands, always show the stdout/stderr from '
-           'the subprocess instead of only on failure, and stream the output '
-           'instead of collecting it at the end.')
 
   def _CompareScreenshotSamples(self, tab, screenshot, expected_colors,
                                 tolerance, device_pixel_ratio,
@@ -459,8 +453,9 @@
       json.dump(gpu_keys, f)
     try:
       if not self.GetParsedCommandLineOptions().no_luci_auth:
-        self._RunGoldctlCommand([goldctl_bin, 'auth', '--luci',
-                                 '--work-dir', self._skia_gold_temp_dir])
+        subprocess.check_output([goldctl_bin, 'auth', '--luci',
+                                 '--work-dir', self._skia_gold_temp_dir],
+            stderr=subprocess.STDOUT)
       cmd = ([goldctl_bin, 'imgtest', 'add'] + mode +
                             ['--test-name', image_name,
                              '--instance', SKIA_GOLD_INSTANCE,
@@ -469,7 +464,7 @@
                              '--work-dir', self._skia_gold_temp_dir,
                              '--failure-file', failure_file] +
                             build_id_args)
-      self._RunGoldctlCommand(cmd)
+      subprocess.check_output(cmd, stderr=subprocess.STDOUT)
     except CalledProcessError as e:
       contents = ''
       try:
@@ -481,26 +476,6 @@
       if not self.GetParsedCommandLineOptions().no_skia_gold_failure:
         raise Exception('goldctl command failed: ' + contents)
 
-  def _RunGoldctlCommand(self, command):
-    if self.GetParsedCommandLineOptions().stream_goldctl_output:
-      process = subprocess.Popen(
-          command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-      full_output = ""
-      while True:
-        line = process.stdout.readline()
-        if not line and process.poll() is not None:
-          break
-        if line:
-          full_output += line
-          logging.info(line.rstrip())
-      rc = process.returncode
-      # Emulate what subprocess.check_output would do so that callers don't
-      # have to care how the command is being run.
-      if rc != 0:
-        raise subprocess.CalledProcessError(rc, command, full_output)
-    else:
-      subprocess.check_output(command, stderr=subprocess.STDOUT)
-
   def _ValidateScreenshotSamplesWithSkiaGold(self, tab, page, screenshot,
                                              expectations, tolerance,
                                              device_pixel_ratio,
diff --git a/content/test/navigation_simulator_impl.cc b/content/test/navigation_simulator_impl.cc
index e45554c..c9c1eda 100644
--- a/content/test/navigation_simulator_impl.cc
+++ b/content/test/navigation_simulator_impl.cc
@@ -899,6 +899,11 @@
 void NavigationSimulatorImpl::SetLoadURLParams(
     NavigationController::LoadURLParams* load_url_params) {
   load_url_params_ = load_url_params;
+
+  // Make sure the internal attributes of NavigationSimulatorImpl match the
+  // LoadURLParams that is going to be sent.
+  referrer_ = load_url_params->referrer;
+  transition_ = load_url_params->transition_type;
 }
 
 void NavigationSimulatorImpl::SetAutoAdvance(bool auto_advance) {
diff --git a/device/BUILD.gn b/device/BUILD.gn
index 4e35e957..8bc2a11 100644
--- a/device/BUILD.gn
+++ b/device/BUILD.gn
@@ -323,7 +323,6 @@
       "vr/util/fps_meter_unittest.cc",
       "vr/util/sliding_average_unittest.cc",
       "vr/vr_device_base_unittest.cc",
-      "vr/vr_display_impl_unittest.cc",
     ]
 
     if (is_android) {
diff --git a/device/vr/orientation/orientation_device.cc b/device/vr/orientation/orientation_device.cc
index 49be329..dc1a98e 100644
--- a/device/vr/orientation/orientation_device.cc
+++ b/device/vr/orientation/orientation_device.cc
@@ -9,6 +9,7 @@
 #include "base/numerics/math_constants.h"
 #include "base/time/time.h"
 #include "device/vr/orientation/orientation_device.h"
+#include "device/vr/vr_display_impl.h"
 #include "services/device/public/cpp/generic_sensor/sensor_reading.h"
 #include "services/device/public/cpp/generic_sensor/sensor_reading_shared_buffer_reader.h"
 #include "services/device/public/mojom/sensor_provider.mojom.h"
@@ -134,13 +135,38 @@
     mojom::XRRuntimeSessionOptionsPtr options,
     mojom::XRRuntime::RequestSessionCallback callback) {
   DCHECK(!options->immersive);
+
   // TODO(http://crbug.com/695937): Perform a check to see if sensors are
   // available when RequestSession is called for non-immersive sessions.
-  ReturnNonImmersiveSession(std::move(callback));
+
+  mojom::XRFrameDataProviderPtr data_provider;
+  mojom::XRSessionControllerPtr controller;
+  magic_window_sessions_.push_back(std::make_unique<VRDisplayImpl>(
+      this, mojo::MakeRequest(&data_provider), mojo::MakeRequest(&controller)));
+
+  auto session = mojom::XRSession::New();
+  session->data_provider = data_provider.PassInterface();
+  if (display_info_) {
+    session->display_info = display_info_.Clone();
+  }
+
+  std::move(callback).Run(std::move(session), std::move(controller));
 }
 
-void VROrientationDevice::OnGetInlineFrameData(
+void VROrientationDevice::EndMagicWindowSession(VRDisplayImpl* session) {
+  base::EraseIf(magic_window_sessions_,
+                [session](const std::unique_ptr<VRDisplayImpl>& item) {
+                  return item.get() == session;
+                });
+}
+
+void VROrientationDevice::GetInlineFrameData(
     mojom::XRFrameDataProvider::GetFrameDataCallback callback) {
+  if (!inline_poses_enabled_) {
+    std::move(callback).Run(nullptr);
+    return;
+  }
+
   mojom::VRPosePtr pose = mojom::VRPose::New();
 
   SensorReading latest_reading;
diff --git a/device/vr/orientation/orientation_device.h b/device/vr/orientation/orientation_device.h
index 04459c92..10de2602 100644
--- a/device/vr/orientation/orientation_device.h
+++ b/device/vr/orientation/orientation_device.h
@@ -20,6 +20,7 @@
 namespace device {
 
 class SensorReadingSharedBufferReader;
+class VRDisplayImpl;
 
 // Use RELATIVE_ORIENTATION_QUATERNION rather than
 // ABSOLUTE_ORIENTATION_QUATERNION because compass readings can be inacurate
@@ -47,13 +48,13 @@
       mojom::XRRuntimeSessionOptionsPtr options,
       mojom::XRRuntime::RequestSessionCallback callback) override;
 
-  // VRDeviceBase
-  void OnGetInlineFrameData(
-      mojom::XRFrameDataProvider::GetFrameDataCallback callback) override;
-
   // Indicates whether the device was able to connect to orientation events.
   bool IsAvailable() const { return available_; }
 
+  void EndMagicWindowSession(VRDisplayImpl* session);
+  virtual void GetInlineFrameData(
+      mojom::XRFrameDataProvider::GetFrameDataCallback callback);
+
  private:
   // SensorClient Functions.
   void RaiseError() override;
@@ -78,6 +79,8 @@
   mojom::SensorPtr sensor_;
   std::unique_ptr<SensorReadingSharedBufferReader> shared_buffer_reader_;
   mojo::Binding<mojom::SensorClient> binding_;
+
+  std::vector<std::unique_ptr<VRDisplayImpl>> magic_window_sessions_;
 };
 
 }  // namespace device
diff --git a/device/vr/orientation/orientation_device_unittest.cc b/device/vr/orientation/orientation_device_unittest.cc
index c5368f6..e7aaf4c 100644
--- a/device/vr/orientation/orientation_device_unittest.cc
+++ b/device/vr/orientation/orientation_device_unittest.cc
@@ -15,6 +15,7 @@
 #include "device/vr/orientation/orientation_device.h"
 #include "device/vr/test/fake_orientation_provider.h"
 #include "device/vr/test/fake_sensor_provider.h"
+#include "device/vr/vr_display_impl.h"
 #include "services/device/public/cpp/generic_sensor/sensor_reading.h"
 #include "services/device/public/cpp/generic_sensor/sensor_reading_shared_buffer_reader.h"
 #include "services/device/public/cpp/generic_sensor/sensor_traits.h"
@@ -135,7 +136,7 @@
 
     base::RunLoop loop;
 
-    device_->OnGetInlineFrameData(base::BindOnce(
+    device_->GetInlineFrameData(base::BindOnce(
         [](base::OnceClosure quit_closure,
            base::OnceCallback<void(mojom::VRPosePtr)> callback,
            mojom::XRFrameDataPtr ptr) {
@@ -150,6 +151,43 @@
     loop.Run();
   }
 
+  void AssertInlineFrameDataAvailable(bool expect_available) {
+    if (expect_available) {
+      device_->GetInlineFrameData(base::BindOnce(
+          [](device::mojom::XRFrameDataPtr data) { EXPECT_TRUE(data); }));
+    } else {
+      device_->GetInlineFrameData(base::BindOnce(
+          [](device::mojom::XRFrameDataPtr data) { EXPECT_FALSE(data); }));
+    }
+  }
+
+  void SetInlinePosesEnabled(bool enabled) {
+    device_->SetInlinePosesEnabled(enabled);
+  }
+
+  std::unique_ptr<VRDisplayImpl> MakeDisplay() {
+    mojom::XRFrameDataProviderPtr data_provider;
+    mojom::XRSessionControllerPtr controller;
+    return std::make_unique<VRDisplayImpl>(device_.get(),
+                                           mojo::MakeRequest(&data_provider),
+                                           mojo::MakeRequest(&controller));
+  }
+
+  void TryGetFrameData(VRDisplayImpl* display, bool expect_null) {
+    bool was_called = false;
+    auto callback = [](bool expect_null, bool* was_called,
+                       mojom::XRFrameDataPtr data) {
+      *was_called = true;
+      EXPECT_EQ(expect_null, !data);
+    };
+
+    static_cast<mojom::XRFrameDataProvider*>(display)->GetFrameData(
+        nullptr, base::BindOnce(callback, expect_null, &was_called));
+
+    base::RunLoop().RunUntilIdle();
+    EXPECT_TRUE(was_called);
+  }
+
   mojom::SensorInitParamsPtr FakeInitParams() {
     auto init_params = mojom::SensorInitParams::New();
     init_params->sensor = std::move(sensor_ptr_);
@@ -342,4 +380,26 @@
                  }));
 }
 
+TEST_F(VROrientationDeviceTest, NoMagicWindowPosesWhileBrowsing) {
+  InitializeDevice(FakeInitParams());
+
+  AssertInlineFrameDataAvailable(true);
+  SetInlinePosesEnabled(false);
+  AssertInlineFrameDataAvailable(false);
+}
+
+TEST_F(VROrientationDeviceTest, GetFrameDataHelper) {
+  InitializeDevice(FakeInitParams());
+
+  // 1) create display impl with restricted frame data
+  // 2) call GetFrameData and check behavior
+  // 3) unrestrict frame data
+  // 4) call GetFrameData and check behavior
+  std::unique_ptr<VRDisplayImpl> display = MakeDisplay();
+  TryGetFrameData(display.get(), true);
+  static_cast<mojom::XRSessionController*>(display.get())
+      ->SetFrameDataRestricted(false);
+  TryGetFrameData(display.get(), false);
+}
+
 }  // namespace device
diff --git a/device/vr/test/fake_vr_device.cc b/device/vr/test/fake_vr_device.cc
index 39a7e536..3eb2eaf 100644
--- a/device/vr/test/fake_vr_device.cc
+++ b/device/vr/test/fake_vr_device.cc
@@ -61,11 +61,4 @@
   controller_binding_.Close();
 }
 
-void FakeVRDevice::OnGetInlineFrameData(
-    mojom::XRFrameDataProvider::GetFrameDataCallback callback) {
-  mojom::XRFrameDataPtr frame_data = mojom::XRFrameData::New();
-  frame_data->pose = pose_.Clone();
-  std::move(callback).Run(std::move(frame_data));
-}
-
 }  // namespace device
diff --git a/device/vr/test/fake_vr_device.h b/device/vr/test/fake_vr_device.h
index fa01b3a..e519b8a 100644
--- a/device/vr/test/fake_vr_device.h
+++ b/device/vr/test/fake_vr_device.h
@@ -34,9 +34,6 @@
  private:
   void OnPresentingControllerMojoConnectionError();
 
-  void OnGetInlineFrameData(
-      mojom::XRFrameDataProvider::GetFrameDataCallback callback) override;
-
   mojom::VRDisplayInfoPtr InitBasicDevice();
   mojom::VREyeParametersPtr InitEye(float fov, float offset, uint32_t size);
 
diff --git a/device/vr/vr_device_base.cc b/device/vr/vr_device_base.cc
index e88e257..f0be39e 100644
--- a/device/vr/vr_device_base.cc
+++ b/device/vr/vr_device_base.cc
@@ -6,7 +6,6 @@
 
 #include "base/metrics/histogram_functions.h"
 #include "device/vr/vr_device_provider.h"
-#include "device/vr/vr_display_impl.h"
 
 namespace device {
 
@@ -48,16 +47,6 @@
   std::move(callback).Run(display_info_.Clone());
 }
 
-void VRDeviceBase::GetInlineFrameData(
-    mojom::XRFrameDataProvider::GetFrameDataCallback callback) {
-  if (!inline_poses_enabled_) {
-    std::move(callback).Run(nullptr);
-    return;
-  }
-
-  OnGetInlineFrameData(std::move(callback));
-}
-
 void VRDeviceBase::SetVRDisplayInfo(mojom::VRDisplayInfoPtr display_info) {
   DCHECK(display_info);
   DCHECK(display_info->id == id_);
@@ -79,17 +68,8 @@
   return runtime;
 }
 
-bool VRDeviceBase::ShouldPauseTrackingWhenFrameDataRestricted() {
-  return false;
-}
-
 void VRDeviceBase::OnListeningForActivate(bool listening) {}
 
-void VRDeviceBase::OnGetInlineFrameData(
-    mojom::XRFrameDataProvider::GetFrameDataCallback callback) {
-  std::move(callback).Run(nullptr);
-}
-
 void VRDeviceBase::SetListeningForActivate(bool is_listening) {
   OnListeningForActivate(is_listening);
 }
@@ -111,32 +91,8 @@
   std::move(callback).Run(base::nullopt);
 }
 
-void VRDeviceBase::ReturnNonImmersiveSession(
-    mojom::XRRuntime::RequestSessionCallback callback) {
-  mojom::XRFrameDataProviderPtr data_provider;
-  mojom::XREnvironmentIntegrationProviderPtr environment_provider;
-  mojom::XRSessionControllerPtr controller;
-  magic_window_sessions_.push_back(std::make_unique<VRDisplayImpl>(
-      this, mojo::MakeRequest(&data_provider), mojo::MakeRequest(&controller)));
-
-  auto session = mojom::XRSession::New();
-  session->data_provider = data_provider.PassInterface();
-  if (display_info_) {
-    session->display_info = display_info_.Clone();
-  }
-
-  std::move(callback).Run(std::move(session), std::move(controller));
-}
-
 void LogViewerType(VrViewerType type) {
   base::UmaHistogramSparse("VRViewerType", static_cast<int>(type));
 }
 
-void VRDeviceBase::EndMagicWindowSession(VRDisplayImpl* session) {
-  base::EraseIf(magic_window_sessions_,
-                [&](const std::unique_ptr<VRDisplayImpl>& item) {
-                  return item.get() == session;
-                });
-}
-
 }  // namespace device
diff --git a/device/vr/vr_device_base.h b/device/vr/vr_device_base.h
index f145511..1815026 100644
--- a/device/vr/vr_device_base.h
+++ b/device/vr/vr_device_base.h
@@ -15,8 +15,6 @@
 
 namespace device {
 
-class VRDisplayImpl;
-
 // Represents one of the platform's VR devices. Owned by the respective
 // VRDeviceProvider.
 // TODO(mthiesse, crbug.com/769373): Remove DEVICE_VR_EXPORT.
@@ -35,25 +33,14 @@
                          EnsureInitializedCallback callback) override;
   void SetInlinePosesEnabled(bool enable) override;
 
-  void GetInlineFrameData(
-      mojom::XRFrameDataProvider::GetFrameDataCallback callback);
-
   virtual void RequestHitTest(
       mojom::XRRayPtr ray,
       mojom::XREnvironmentIntegrationProvider::RequestHitTestCallback callback);
   device::mojom::XRDeviceId GetId() const;
 
   bool HasExclusiveSession();
-  void EndMagicWindowSession(VRDisplayImpl* session);
 
-  // TODO(https://crbug.com/845283): This method is a temporary solution
-  // until a XR related refactor lands. It allows to keep using the
-  // existing PauseTracking/ResumeTracking while not changing the
-  // existing VR functionality.
-  virtual bool ShouldPauseTrackingWhenFrameDataRestricted();
-
-  // Devices may be paused/resumed when focus changes by VRDisplayImpl or
-  // GVR delegate.
+  // Devices may be paused/resumed when focus changes by GVR delegate.
   virtual void PauseTracking();
   virtual void ResumeTracking();
 
@@ -78,24 +65,19 @@
   void OnActivate(mojom::VRDisplayEventReason reason,
                   base::Callback<void(bool)> on_handled);
 
-  void ReturnNonImmersiveSession(
-      mojom::XRRuntime::RequestSessionCallback callback);
-
   mojom::VRDisplayInfoPtr display_info_;
-  std::vector<std::unique_ptr<VRDisplayImpl>> magic_window_sessions_;
+
+  bool inline_poses_enabled_ = true;
 
  private:
   // TODO(https://crbug.com/842227): Rename methods to HandleOnXXX
   virtual void OnListeningForActivate(bool listening);
-  virtual void OnGetInlineFrameData(
-      mojom::XRFrameDataProvider::GetFrameDataCallback callback);
 
   mojom::XRRuntimeEventListenerAssociatedPtr listener_;
 
   bool presenting_ = false;
 
   device::mojom::XRDeviceId id_;
-  bool inline_poses_enabled_ = true;
 
   mojo::Binding<mojom::XRRuntime> runtime_binding_;
 
diff --git a/device/vr/vr_device_base_unittest.cc b/device/vr/vr_device_base_unittest.cc
index ba7e549b..d1e3697 100644
--- a/device/vr/vr_device_base_unittest.cc
+++ b/device/vr/vr_device_base_unittest.cc
@@ -12,7 +12,6 @@
 #include "base/test/scoped_task_environment.h"
 #include "device/vr/public/mojom/vr_service.mojom.h"
 #include "device/vr/test/fake_vr_device.h"
-#include "device/vr/test/fake_vr_service_client.h"
 #include "device/vr/vr_device_base.h"
 #include "mojo/public/cpp/bindings/associated_binding.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -94,11 +93,6 @@
   ~VRDeviceTest() override {}
 
  protected:
-  void SetUp() override {
-    mojom::VRServiceClientPtr proxy;
-    client_ = std::make_unique<FakeVRServiceClient>(mojo::MakeRequest(&proxy));
-  }
-
   std::unique_ptr<VRDeviceBaseForTesting> MakeVRDevice() {
     std::unique_ptr<VRDeviceBaseForTesting> device =
         std::make_unique<VRDeviceBaseForTesting>();
@@ -113,9 +107,6 @@
     return display_info;
   }
 
-  FakeVRServiceClient* client() { return client_.get(); }
-
-  std::unique_ptr<FakeVRServiceClient> client_;
   base::test::ScopedTaskEnvironment scoped_task_environment_;
 
   DISALLOW_COPY_AND_ASSIGN(VRDeviceTest);
@@ -157,16 +148,4 @@
   base::RunLoop().RunUntilIdle();
 }
 
-TEST_F(VRDeviceTest, NoMagicWindowPosesWhileBrowsing) {
-  auto device =
-      std::make_unique<FakeVRDevice>(static_cast<device::mojom::XRDeviceId>(1));
-  device->SetPose(mojom::VRPose::New());
-
-  device->GetInlineFrameData(base::BindOnce(
-      [](device::mojom::XRFrameDataPtr data) { EXPECT_TRUE(data); }));
-  device->SetInlinePosesEnabled(false);
-  device->GetInlineFrameData(base::BindOnce(
-      [](device::mojom::XRFrameDataPtr data) { EXPECT_FALSE(data); }));
-}
-
 }  // namespace device
diff --git a/device/vr/vr_display_impl.cc b/device/vr/vr_display_impl.cc
index 98921904..c7c6dfae 100644
--- a/device/vr/vr_display_impl.cc
+++ b/device/vr/vr_display_impl.cc
@@ -7,12 +7,12 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "device/vr/vr_device_base.h"
+#include "device/vr/orientation/orientation_device.h"
 
 namespace device {
 
 VRDisplayImpl::VRDisplayImpl(
-    VRDeviceBase* device,
+    VROrientationDevice* device,
     mojom::XRFrameDataProviderRequest magic_window_request,
     mojom::XRSessionControllerRequest session_request)
     : magic_window_binding_(this, std::move(magic_window_request)),
@@ -30,7 +30,11 @@
 void VRDisplayImpl::GetFrameData(
     mojom::XRFrameDataRequestOptionsPtr options,
     mojom::XRFrameDataProvider::GetFrameDataCallback callback) {
-  if (device_->HasExclusiveSession() || restrict_frame_data_) {
+  // VRDisplayImpl is only used by VROrientationDevice, which should never have
+  // an exclusive session / be presenting.
+  DCHECK(!device_->HasExclusiveSession());
+
+  if (restrict_frame_data_) {
     std::move(callback).Run(nullptr);
     return;
   }
@@ -56,13 +60,6 @@
 // XRSessionController
 void VRDisplayImpl::SetFrameDataRestricted(bool frame_data_restricted) {
   restrict_frame_data_ = frame_data_restricted;
-  if (device_->ShouldPauseTrackingWhenFrameDataRestricted()) {
-    if (restrict_frame_data_) {
-      device_->PauseTracking();
-    } else {
-      device_->ResumeTracking();
-    }
-  }
 }
 
 void VRDisplayImpl::OnMojoConnectionError() {
diff --git a/device/vr/vr_display_impl.h b/device/vr/vr_display_impl.h
index 2494214..ca244f1 100644
--- a/device/vr/vr_display_impl.h
+++ b/device/vr/vr_display_impl.h
@@ -19,7 +19,7 @@
 
 namespace device {
 
-class VRDeviceBase;
+class VROrientationDevice;
 
 // VR device process implementation of a XRFrameDataProvider within a WebVR
 // or WebXR site session.
@@ -28,7 +28,7 @@
 class DEVICE_VR_EXPORT VRDisplayImpl : public mojom::XRFrameDataProvider,
                                        public mojom::XRSessionController {
  public:
-  VRDisplayImpl(VRDeviceBase* device,
+  VRDisplayImpl(VROrientationDevice* device,
                 mojom::XRFrameDataProviderRequest,
                 mojom::XRSessionControllerRequest);
   ~VRDisplayImpl() override;
@@ -39,11 +39,6 @@
   void SetInputSourceButtonListener(
       mojom::XRInputSourceButtonListenerAssociatedPtrInfo) override;
 
-  gfx::Size sessionFrameSize() { return session_frame_size_; }
-  display::Display::Rotation sessionRotation() { return session_rotation_; }
-
-  device::VRDeviceBase* device() { return device_; }
-
   // Accessible to tests.
  protected:
   // mojom::XRFrameDataProvider
@@ -51,17 +46,14 @@
                     GetFrameDataCallback callback) override;
 
   // mojom::XRSessionController
-  void SetFrameDataRestricted(bool paused) override;
+  void SetFrameDataRestricted(bool frame_data_restricted) override;
 
   void OnMojoConnectionError();
 
   mojo::Binding<mojom::XRFrameDataProvider> magic_window_binding_;
   mojo::Binding<mojom::XRSessionController> session_controller_binding_;
-  device::VRDeviceBase* device_;
+  device::VROrientationDevice* device_;
   bool restrict_frame_data_ = true;
-
-  gfx::Size session_frame_size_ = gfx::Size(0, 0);
-  display::Display::Rotation session_rotation_ = display::Display::ROTATE_0;
 };
 
 }  // namespace device
diff --git a/device/vr/vr_display_impl_unittest.cc b/device/vr/vr_display_impl_unittest.cc
deleted file mode 100644
index e03cc1b3..0000000
--- a/device/vr/vr_display_impl_unittest.cc
+++ /dev/null
@@ -1,143 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "device/vr/vr_display_impl.h"
-
-#include <memory>
-
-#include "base/bind.h"
-#include "base/memory/ptr_util.h"
-#include "base/run_loop.h"
-#include "base/test/scoped_task_environment.h"
-#include "device/vr/public/mojom/vr_service.mojom.h"
-#include "device/vr/test/fake_vr_device.h"
-#include "device/vr/test/fake_vr_service_client.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace device {
-
-class VRDisplayImplTest : public testing::Test {
- public:
-  VRDisplayImplTest() {}
-  ~VRDisplayImplTest() override {}
-  void onDisplaySynced() {}
-
- protected:
-  void SetUp() override {
-    device_ = std::make_unique<FakeVRDevice>(static_cast<mojom::XRDeviceId>(1));
-    device_->SetPose(mojom::VRPose::New());
-    mojom::VRServiceClientPtr proxy;
-    client_ = std::make_unique<FakeVRServiceClient>(mojo::MakeRequest(&proxy));
-  }
-
-  std::unique_ptr<VRDisplayImpl> MakeDisplay(
-      mojom::XRSessionControllerPtr* controller) {
-    mojom::XRFrameDataProviderPtr data_provider;
-    auto display = std::make_unique<VRDisplayImpl>(
-        device(), mojo::MakeRequest(&data_provider),
-        mojo::MakeRequest(controller));
-    static_cast<mojom::XRSessionController*>(display.get())
-        ->SetFrameDataRestricted(true);
-    return display;
-  }
-
-  void RequestSession(VRDisplayImpl* display_impl) {
-    device_->RequestSession(
-        mojom::XRRuntimeSessionOptionsPtr(),
-        base::BindOnce(
-            [](device::mojom::XRSessionPtr session,
-               mojom::XRSessionControllerPtr immersive_session_controller) {}));
-  }
-
-  void ExitPresent() { device_->StopSession(); }
-
-  bool presenting() { return device_->IsPresenting(); }
-  VRDeviceBase* device() { return device_.get(); }
-  FakeVRServiceClient* client() { return client_.get(); }
-
-  base::test::ScopedTaskEnvironment scoped_task_environment_;
-  std::unique_ptr<FakeVRDevice> device_;
-  std::unique_ptr<FakeVRServiceClient> client_;
-
-  DISALLOW_COPY_AND_ASSIGN(VRDisplayImplTest);
-};
-
-TEST_F(VRDisplayImplTest, DevicePresentationIsolation) {
-  mojom::XRSessionControllerPtr controller1;
-  std::unique_ptr<VRDisplayImpl> display_1 = MakeDisplay(&controller1);
-  static_cast<mojom::XRSessionController*>(display_1.get())
-      ->SetFrameDataRestricted(false);
-
-  mojom::XRSessionControllerPtr controller2;
-  std::unique_ptr<VRDisplayImpl> display_2 = MakeDisplay(&controller2);
-  static_cast<mojom::XRSessionController*>(display_2.get())
-      ->SetFrameDataRestricted(false);
-
-  // When not presenting either service should be able to access the device.
-  EXPECT_FALSE(device()->HasExclusiveSession());
-
-  bool was_called = false;
-  auto callback = [](bool expect_null, bool* was_called,
-                     mojom::XRFrameDataPtr data) {
-    *was_called = true;
-    EXPECT_EQ(expect_null, !data);
-  };
-
-  static_cast<mojom::XRFrameDataProvider*>(display_1.get())
-      ->GetFrameData(nullptr, base::BindOnce(callback, false, &was_called));
-
-  base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(was_called);
-  was_called = false;
-
-  static_cast<mojom::XRFrameDataProvider*>(display_2.get())
-      ->GetFrameData(nullptr, base::BindOnce(callback, false, &was_called));
-
-  base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(was_called);
-  was_called = false;
-
-  // Attempt to present.
-  RequestSession(display_1.get());
-  EXPECT_TRUE(presenting());
-  EXPECT_TRUE(device()->HasExclusiveSession());
-
-  // While a device is presenting, no one should have access to magic window.
-  static_cast<mojom::XRFrameDataProvider*>(display_1.get())
-      ->GetFrameData(nullptr, base::BindOnce(callback, true, &was_called));
-
-  base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(was_called);
-  was_called = false;
-
-  static_cast<mojom::XRFrameDataProvider*>(display_2.get())
-      ->GetFrameData(nullptr, base::BindOnce(callback, true, &was_called));
-
-  base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(was_called);
-  was_called = false;
-
-  // Service 1 should be able to exit the presentation it initiated.
-  ExitPresent();
-  EXPECT_FALSE(presenting());
-  EXPECT_FALSE(device()->HasExclusiveSession());
-
-  // Once presentation had ended both services should be able to access the
-  // device.
-  static_cast<mojom::XRFrameDataProvider*>(display_1.get())
-      ->GetFrameData(nullptr, base::BindOnce(callback, false, &was_called));
-
-  base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(was_called);
-  was_called = false;
-
-  static_cast<mojom::XRFrameDataProvider*>(display_2.get())
-      ->GetFrameData(nullptr, base::BindOnce(callback, false, &was_called));
-
-  base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(was_called);
-  was_called = false;
-}
-
-}  // namespace device
diff --git a/docs/android_debugging_instructions.md b/docs/android_debugging_instructions.md
index 3c69e40..5658a40 100644
--- a/docs/android_debugging_instructions.md
+++ b/docs/android_debugging_instructions.md
@@ -192,6 +192,19 @@
 etc.
 ```
 
+When debugging a failing test on the build waterfall, you can find the mapping
+file as follows:
+
+1. Open buildbot page for the failing build (e.g.,
+   https://ci.chromium.org/p/chrome/builders/ci/android-go-perf/1234).
+2. Open the swarming page for the failing shard (e.g., shard #3).
+3. Click on "Isolated Inputs" to locate the files the shard used to run the
+   test.
+4. Download the `.mapping` file for the APK used by the test (e.g.,
+   `ChromePublic.apk.mapping`). Note that you may need to use the
+   `tools/swarming_client/isolateserver.py` script to download the mapping
+   file if it's too big. The viewer will provide instructions for this.
+
 Build the `java_deobfuscate` tool:
 
 ```shell
diff --git a/docs/fuchsia_build_instructions.md b/docs/fuchsia_build_instructions.md
index 46d5605..fdd839ce 100644
--- a/docs/fuchsia_build_instructions.md
+++ b/docs/fuchsia_build_instructions.md
@@ -188,16 +188,15 @@
 3. Add users to the "kvm" group, and have them login again, to pick-up the new
 group.
 
-### Run test target
+### Running test suites
+
+Building test suites generate a launcher script to run them on a QEMU instance
+or a physical device. These scripts are generated at `out/fuchsia/bin`. For
+instance,to run the `base_unittests` target, launch:
 
 ```shell
 $ out/fuchsia/bin/run_base_unittests
 ```
 
-This packages the built binary and test data into a disk image, and runs a QEMU
-instance from the Fuchsia SDK, outputting to the console.
-
 Common gtest arguments such as `--gtest_filter=...` are supported by the run
-script.
-
-The run script also symbolizes backtraces.
+script. The launcher script also symbolizes backtraces.
diff --git a/docs/threading_and_tasks.md b/docs/threading_and_tasks.md
index 6fe31f4..5e85d2a 100644
--- a/docs/threading_and_tasks.md
+++ b/docs/threading_and_tasks.md
@@ -142,7 +142,7 @@
   at a time on any thread.
 * [Single Threaded](#Posting-Multiple-Tasks-to-the-Same-Thread): Tasks executed
   in posting order, one at a time on a single thread.
-   * [COM Single Threaded](#Posting-Tasks-to-a-COM-Single-Thread-Apartment-STA_Thread-Windows_):
+   * [COM Single Threaded](#Posting-Tasks-to-a-COM-Single_Thread-Apartment-STA_Thread-Windows):
      A variant of single threaded with COM initialized.
 
 ### Prefer Sequences to Physical Threads
diff --git a/extensions/browser/content_verifier.cc b/extensions/browser/content_verifier.cc
index 0c402d1b..ea89a51 100644
--- a/extensions/browser/content_verifier.cc
+++ b/extensions/browser/content_verifier.cc
@@ -391,10 +391,7 @@
 ContentVerifier::ContentVerifier(
     content::BrowserContext* context,
     std::unique_ptr<ContentVerifierDelegate> delegate)
-    : context_(context),
-      delegate_(std::move(delegate)),
-      observer_(this),
-      io_data_(new ContentVerifierIOData) {}
+    : context_(context), delegate_(std::move(delegate)), observer_(this) {}
 
 ContentVerifier::~ContentVerifier() {
 }
@@ -416,7 +413,7 @@
 void ContentVerifier::ShutdownOnIO() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   shutdown_on_io_ = true;
-  io_data_->Clear();
+  io_data_.Clear();
   hash_helper_.reset();
 }
 
@@ -427,7 +424,7 @@
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
 
   const ContentVerifierIOData::ExtensionData* data =
-      io_data_->GetData(extension_id);
+      io_data_.GetData(extension_id);
   // The absence of |data| generally means that we don't have to verify the
   // extension resource. However, it could also mean that
   // OnExtensionLoadedOnIO didn't get a chance to fire yet.
@@ -557,7 +554,7 @@
   if (shutdown_on_io_)
     return;
 
-  io_data_->AddData(extension_id, std::move(data));
+  io_data_.AddData(extension_id, std::move(data));
   GetContentHash(extension_id, extension_root, extension_version,
                  false /* force_missing_computed_hashes_creation */,
                  // HashHelper will respond directly to OnFetchComplete().
@@ -593,7 +590,7 @@
     const base::Version& extension_version) {
   if (shutdown_on_io_)
     return;
-  io_data_->RemoveData(extension_id);
+  io_data_.RemoveData(extension_id);
 
   // Remove all possible cache entries for this extension version.
   cache_.erase(CacheKey(extension_id, extension_version, true));
@@ -669,7 +666,7 @@
     const std::set<base::FilePath>& relative_unix_paths) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   const ContentVerifierIOData::ExtensionData* data =
-      io_data_->GetData(extension_id);
+      io_data_.GetData(extension_id);
   if (!data)
     return false;
 
@@ -751,7 +748,7 @@
 }
 
 void ContentVerifier::ResetIODataForTesting(const Extension* extension) {
-  io_data_->AddData(extension->id(), CreateIOData(extension, delegate_.get()));
+  io_data_.AddData(extension->id(), CreateIOData(extension, delegate_.get()));
 }
 
 base::FilePath ContentVerifier::NormalizeRelativePathForTesting(
diff --git a/extensions/browser/content_verifier.h b/extensions/browser/content_verifier.h
index 1b6601b..c2b5b91 100644
--- a/extensions/browser/content_verifier.h
+++ b/extensions/browser/content_verifier.h
@@ -177,7 +177,7 @@
   ScopedObserver<ExtensionRegistry, ExtensionRegistryObserver> observer_;
 
   // Data that should only be used on the IO thread.
-  scoped_refptr<ContentVerifierIOData> io_data_;
+  ContentVerifierIOData io_data_;
 
   DISALLOW_COPY_AND_ASSIGN(ContentVerifier);
 };
diff --git a/extensions/browser/content_verifier_io_data.h b/extensions/browser/content_verifier_io_data.h
index ecba0b4..4937394c 100644
--- a/extensions/browser/content_verifier_io_data.h
+++ b/extensions/browser/content_verifier_io_data.h
@@ -18,8 +18,7 @@
 
 // A helper class for keeping track of data for the ContentVerifier that should
 // only be accessed on the IO thread.
-class ContentVerifierIOData
-    : public base::RefCountedThreadSafe<ContentVerifierIOData> {
+class ContentVerifierIOData {
  public:
   struct ExtensionData {
     // Set of images file paths used within the browser process.
@@ -36,6 +35,7 @@
   };
 
   ContentVerifierIOData();
+  ~ContentVerifierIOData();
 
   void AddData(const std::string& extension_id,
                std::unique_ptr<ExtensionData> data);
@@ -46,11 +46,10 @@
   // be retained or used on other threads.
   const ExtensionData* GetData(const std::string& extension_id);
 
- protected:
-  friend class base::RefCountedThreadSafe<ContentVerifierIOData>;
-  virtual ~ContentVerifierIOData();
-
+ private:
   std::map<std::string, std::unique_ptr<ExtensionData>> data_map_;
+
+  DISALLOW_COPY_AND_ASSIGN(ContentVerifierIOData);
 };
 
 }  // namespace extensions
diff --git a/extensions/shell/browser/shell_browser_context.cc b/extensions/shell/browser/shell_browser_context.cc
index abd8742..abeb37f 100644
--- a/extensions/shell/browser/shell_browser_context.cc
+++ b/extensions/shell/browser/shell_browser_context.cc
@@ -17,7 +17,6 @@
 // then app_shell would also have to create a normal context and manage both.
 ShellBrowserContext::ShellBrowserContext()
     : content::ShellBrowserContext(false /* off_the_record */,
-                                   nullptr /* net_log */,
                                    true /* delay_services_creation */),
       storage_policy_(new ShellSpecialStoragePolicy) {}
 
diff --git a/fuchsia/BUILD.gn b/fuchsia/BUILD.gn
index 3082ec3..ecc9681 100644
--- a/fuchsia/BUILD.gn
+++ b/fuchsia/BUILD.gn
@@ -100,6 +100,7 @@
     "engine:web_engine_browsertests",
     "engine:web_engine_unittests",
     "http:http_service_tests",
+    "mojo:fuchsia_mojo_unittests",
     "runners:cast_runner",
     "runners:cast_runner_browsertests",
     "runners:web_runner",
diff --git a/fuchsia/README.md b/fuchsia/README.md
index 99db56b..df342bf5 100644
--- a/fuchsia/README.md
+++ b/fuchsia/README.md
@@ -1,6 +1,7 @@
 # Chromium-based Fuchsia services
 This directory contains implementation code for various Fuchsia services living
-in the Chromium repository.
+in the Chromium repository. To build Chromium on Fuchsia, check this
+[documentation](../docs/fuchsia_build_instructions.md).
 
 [TOC]
 
@@ -24,29 +25,38 @@
 The `./cipd` and `./fidl` subdirectories contain CIPD definitions and FIDL
 interface definitions, respectfully.
 
-### Test-only code
-
-Test-only code should live in the same directory as the code under test.
-There is one exception to this rule for fake implementations of interfaces and
-shared test fixtures. When the number of source files containing code related
-to these has reached a certain threshold, they should be moved to a `test`
-subdirectory. For instance, see the `//fuchsia/engine/test` directory.
-
 ### Namespacing
 
 Code that is not shared across multiple targets should live in the global
 namespace. Code that is shared across multiple targets should live in the
 `cr_fuchsia` namespace.
 
-### Running test suites
+### Test code
 
-Building test suites generate a launcher script to run them on a QEMU instance.
-These scripts are generated at `out/fuchsia/bin`. For instance,to run the
-`base_unittests` target, launch:
+Under the `//fuchsia` directory , there are 3 major types of tests:
+* Unit tests: Exercises a single class in isolation, allowing full control
+  over the external environment of this class.
+* Browser tests: Spawns a full browser process along child processes. The test
+  code is run inside the browser process, allowing for full access to the
+  browser code, but not other processes.
+* Integration tests: they exercise the published API of a Fuchsia component. For
+  instance, `//fuchsia/engine:web_engine_integration_tests` make use of the
+  `//fuchsia/engine:web_engine` component. The test code is run in a separate
+  process in a separate component, allowing only access to the published API of
+  the component under test.
 
-```bash
-$ out/fuchsia/bin/run_base_unittests
-```
+Integration tests are more resource-intensive than browser tests, which are in
+turn more expensive than unit tests. Therefore, when writing new tests, it is
+preferred to write unit tests over browser tests over integration tests.
+
+As a general rule, test-only code should live in the same directory as the code
+under test with an explicit file name, either `fake_*`, `test_*`,
+`*_unittest.cc`, `*_ browser_test.cc` or `*_integration_test.cc`.
+
+Test code that is shared across components should live in a dedicated `test`
+directory, under the `cr_fuchsia` namespace. For instance, see the
+`//fuchsia/engine/test` directory, which contains code shared by all browser
+tests.
 
 ## Building and deploying the WebRunner service
 
diff --git a/fuchsia/mojo/BUILD.gn b/fuchsia/mojo/BUILD.gn
new file mode 100644
index 0000000..c797103
--- /dev/null
+++ b/fuchsia/mojo/BUILD.gn
@@ -0,0 +1,41 @@
+# 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.
+
+assert(is_fuchsia)
+
+import("//mojo/public/tools/bindings/mojom.gni")
+import("//testing/test.gni")
+
+mojom("example_interfaces") {
+  testonly = true
+  sources = [
+    "example.mojom",
+  ]
+}
+
+source_set("traits") {
+  sources = [
+    "fidl_interface_request_mojom_traits.h",
+  ]
+
+  public_deps = [
+    "//mojo/public/cpp/platform",
+    "//mojo/public/cpp/system",
+  ]
+}
+
+test("fuchsia_mojo_unittests") {
+  sources = [
+    "fidl_interface_request_mojom_traits_unittest.cc",
+    "test_interface_request_mojom_traits.h",
+  ]
+
+  deps = [
+    ":example_interfaces",
+    "//base:testfidl",
+    "//mojo/core/test:run_all_unittests",
+    "//mojo/public/cpp/test_support:test_utils",
+    "//testing/gtest",
+  ]
+}
diff --git a/fuchsia/mojo/DEPS b/fuchsia/mojo/DEPS
new file mode 100644
index 0000000..ef8ad28
--- /dev/null
+++ b/fuchsia/mojo/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+mojo/public",
+]
diff --git a/fuchsia/mojo/OWNERS b/fuchsia/mojo/OWNERS
new file mode 100644
index 0000000..ae29a36aa
--- /dev/null
+++ b/fuchsia/mojo/OWNERS
@@ -0,0 +1,6 @@
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
+per-file *_mojom_traits*.*=set noparent
+per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS
+per-file *.typemap=set noparent
+per-file *.typemap=file://ipc/SECURITY_OWNERS
diff --git a/fuchsia/mojo/example.mojom b/fuchsia/mojo/example.mojom
new file mode 100644
index 0000000..a27c9561
--- /dev/null
+++ b/fuchsia/mojo/example.mojom
@@ -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.
+
+module fuchsia.test.mojom;
+
+// Mojo struct of fidl::InterfaceRequest<TestInterface>. |request| is a
+// channel handle.
+struct TestInterfaceRequest {
+  handle request;
+};
diff --git a/fuchsia/mojo/example.typemap b/fuchsia/mojo/example.typemap
new file mode 100644
index 0000000..b1d1512
--- /dev/null
+++ b/fuchsia/mojo/example.typemap
@@ -0,0 +1,16 @@
+# 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.
+
+mojom = "//fuchsia/mojo/example.mojom"
+os_whitelist = [ "fuchsia" ]
+public_headers = [ "base/fuchsia/testfidl/cpp/fidl.h" ]
+traits_headers = [ "//fuchsia/mojo/test_interface_request_mojom_traits.h" ]
+sources = [
+  "//fuchsia/mojo/test_interface_request_mojom_traits.h",
+]
+public_deps = [
+  "//base:testfidl",
+  "//fuchsia/mojo:traits",
+]
+type_mappings = [ "fuchsia.test.mojom.TestInterfaceRequest=fidl::InterfaceRequest<base::fuchsia::testfidl::TestInterface>[move_only]" ]
diff --git a/fuchsia/mojo/fidl_interface_request_mojom_traits.h b/fuchsia/mojo/fidl_interface_request_mojom_traits.h
new file mode 100644
index 0000000..2c38438
--- /dev/null
+++ b/fuchsia/mojo/fidl_interface_request_mojom_traits.h
@@ -0,0 +1,43 @@
+// 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 FUCHSIA_MOJO_FIDL_INTERFACE_REQUEST_MOJOM_TRAITS_H_
+#define FUCHSIA_MOJO_FIDL_INTERFACE_REQUEST_MOJOM_TRAITS_H_
+
+#include <lib/fidl/cpp/interface_request.h>
+
+#include "mojo/public/cpp/platform/platform_handle.h"
+#include "mojo/public/cpp/system/platform_handle.h"
+
+namespace mojo {
+
+// Implementation of StructTratis<DataView, fidl::InterfaceRequest<Interface>>.
+// Different Interface still needs to define its own StructTraits by subclassing
+// this struct. Read test_request_interface_mojom_traits.h for an example.
+template <typename DataView, typename Interface>
+struct FidlInterfaceRequestStructTraits {
+  static ScopedHandle request(fidl::InterfaceRequest<Interface>& request) {
+    DCHECK(request.is_valid());
+    PlatformHandle handle(request.TakeChannel());
+    return WrapPlatformHandle(std::move(handle));
+  }
+
+  static bool Read(DataView input, fidl::InterfaceRequest<Interface>* output) {
+    ScopedHandle input_request = input.TakeRequest();
+    if (!input_request.is_valid())
+      return false;
+
+    PlatformHandle handle =
+        mojo::UnwrapPlatformHandle(std::move(input_request));
+    if (!handle.is_valid_handle())
+      return false;
+
+    output->set_channel(zx::channel(handle.TakeHandle()));
+    return true;
+  }
+};
+
+}  // namespace mojo
+
+#endif  // FUCHSIA_MOJO_FIDL_INTERFACE_REQUEST_MOJOM_TRAITS_H_
diff --git a/fuchsia/mojo/fidl_interface_request_mojom_traits_unittest.cc b/fuchsia/mojo/fidl_interface_request_mojom_traits_unittest.cc
new file mode 100644
index 0000000..8e34582c
--- /dev/null
+++ b/fuchsia/mojo/fidl_interface_request_mojom_traits_unittest.cc
@@ -0,0 +1,29 @@
+// 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/fuchsia/testfidl/cpp/fidl.h"
+#include "base/message_loop/message_loop.h"
+#include "fuchsia/mojo/example.mojom.h"
+#include "fuchsia/mojo/test_interface_request_mojom_traits.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace fuchsia {
+
+using base::fuchsia::testfidl::TestInterface;
+using base::fuchsia::testfidl::TestInterfacePtr;
+
+TEST(InterfaceRequestStructTraitsTest, Serialization) {
+  base::MessageLoopForIO message_loop;
+  TestInterfacePtr test_ptr;
+  fidl::InterfaceRequest<TestInterface> input_request = test_ptr.NewRequest();
+  fidl::InterfaceRequest<TestInterface> output_request;
+
+  EXPECT_TRUE(mojo::test::SerializeAndDeserialize<
+              fuchsia::test::mojom::TestInterfaceRequest>(&input_request,
+                                                          &output_request));
+  EXPECT_TRUE(output_request.is_valid());
+}
+
+}  // namespace fuchsia
diff --git a/fuchsia/mojo/test_interface_request_mojom_traits.h b/fuchsia/mojo/test_interface_request_mojom_traits.h
new file mode 100644
index 0000000..0f0aeb6
--- /dev/null
+++ b/fuchsia/mojo/test_interface_request_mojom_traits.h
@@ -0,0 +1,22 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FUCHSIA_MOJO_TEST_INTERFACE_REQUEST_MOJOM_TRAITS_H_
+#define FUCHSIA_MOJO_TEST_INTERFACE_REQUEST_MOJOM_TRAITS_H_
+
+#include "fuchsia/mojo/fidl_interface_request_mojom_traits.h"
+
+namespace mojo {
+
+template <>
+struct StructTraits<
+    fuchsia::test::mojom::TestInterfaceRequestDataView,
+    fidl::InterfaceRequest<base::fuchsia::testfidl::TestInterface>>
+    : public FidlInterfaceRequestStructTraits<
+          fuchsia::test::mojom::TestInterfaceRequestDataView,
+          base::fuchsia::testfidl::TestInterface> {};
+
+}  // namespace mojo
+
+#endif  // FUCHSIA_MOJO_TEST_INTERFACE_REQUEST_MOJOM_TRAITS_H_
diff --git a/fuchsia/mojo/test_typemaps.gni b/fuchsia/mojo/test_typemaps.gni
new file mode 100644
index 0000000..0fb662f
--- /dev/null
+++ b/fuchsia/mojo/test_typemaps.gni
@@ -0,0 +1,5 @@
+# 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.
+
+typemaps = [ "//fuchsia/mojo/example.typemap" ]
diff --git a/google_apis/BUILD.gn b/google_apis/BUILD.gn
index 9560f53..199cab7 100644
--- a/google_apis/BUILD.gn
+++ b/google_apis/BUILD.gn
@@ -177,6 +177,8 @@
   sources = [
     "gaia/fake_gaia.cc",
     "gaia/fake_gaia.h",
+    "gaia/fake_oauth2_access_token_manager.cc",
+    "gaia/fake_oauth2_access_token_manager.h",
     "gaia/fake_oauth2_token_service.cc",
     "gaia/fake_oauth2_token_service.h",
     "gaia/fake_oauth2_token_service_delegate.cc",
diff --git a/google_apis/gaia/fake_oauth2_access_token_manager.cc b/google_apis/gaia/fake_oauth2_access_token_manager.cc
new file mode 100644
index 0000000..65df882
--- /dev/null
+++ b/google_apis/gaia/fake_oauth2_access_token_manager.cc
@@ -0,0 +1,202 @@
+// 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 "google_apis/gaia/fake_oauth2_access_token_manager.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+
+FakeOAuth2AccessTokenManager::PendingRequest::PendingRequest() {}
+
+FakeOAuth2AccessTokenManager::PendingRequest::PendingRequest(
+    const PendingRequest& other) = default;
+
+FakeOAuth2AccessTokenManager::PendingRequest::~PendingRequest() {}
+
+FakeOAuth2AccessTokenManager::FakeOAuth2AccessTokenManager(
+    OAuth2TokenService* token_service,
+    OAuth2AccessTokenManager::Delegate* delegate)
+    : OAuth2AccessTokenManager(token_service, delegate),
+      auto_post_fetch_response_on_message_loop_(false),
+      weak_ptr_factory_(this) {}
+
+FakeOAuth2AccessTokenManager::~FakeOAuth2AccessTokenManager() {}
+
+void FakeOAuth2AccessTokenManager::IssueAllTokensForAccount(
+    const std::string& account_id,
+    const std::string& access_token,
+    const base::Time& expiration) {
+  DCHECK(!auto_post_fetch_response_on_message_loop_);
+  CompleteRequests(account_id, true, FakeOAuth2AccessTokenManager::ScopeSet(),
+                   GoogleServiceAuthError::AuthErrorNone(),
+                   OAuth2AccessTokenConsumer::TokenResponse(
+                       access_token, expiration, std::string() /* id_token */));
+}
+
+void FakeOAuth2AccessTokenManager::IssueAllTokensForAccount(
+    const std::string& account_id,
+    const OAuth2AccessTokenConsumer::TokenResponse& token_response) {
+  DCHECK(!auto_post_fetch_response_on_message_loop_);
+  CompleteRequests(account_id, true, FakeOAuth2AccessTokenManager::ScopeSet(),
+                   GoogleServiceAuthError::AuthErrorNone(), token_response);
+}
+
+void FakeOAuth2AccessTokenManager::IssueErrorForAllPendingRequestsForAccount(
+    const std::string& account_id,
+    const GoogleServiceAuthError& error) {
+  DCHECK(!auto_post_fetch_response_on_message_loop_);
+  CompleteRequests(account_id, true, FakeOAuth2AccessTokenManager::ScopeSet(),
+                   error, OAuth2AccessTokenConsumer::TokenResponse());
+}
+
+void FakeOAuth2AccessTokenManager::IssueTokenForScope(
+    const FakeOAuth2AccessTokenManager::ScopeSet& scope,
+    const std::string& access_token,
+    const base::Time& expiration) {
+  DCHECK(!auto_post_fetch_response_on_message_loop_);
+  CompleteRequests("", false, scope, GoogleServiceAuthError::AuthErrorNone(),
+                   OAuth2AccessTokenConsumer::TokenResponse(
+                       access_token, expiration, std::string() /* id_token */));
+}
+
+void FakeOAuth2AccessTokenManager::IssueTokenForScope(
+    const FakeOAuth2AccessTokenManager::ScopeSet& scope,
+    const OAuth2AccessTokenConsumer::TokenResponse& token_response) {
+  DCHECK(!auto_post_fetch_response_on_message_loop_);
+  CompleteRequests("", false, scope, GoogleServiceAuthError::AuthErrorNone(),
+                   token_response);
+}
+
+void FakeOAuth2AccessTokenManager::IssueErrorForScope(
+    const FakeOAuth2AccessTokenManager::ScopeSet& scope,
+    const GoogleServiceAuthError& error) {
+  DCHECK(!auto_post_fetch_response_on_message_loop_);
+  CompleteRequests("", false, scope, error,
+                   OAuth2AccessTokenConsumer::TokenResponse());
+}
+
+void FakeOAuth2AccessTokenManager::IssueErrorForAllPendingRequests(
+    const GoogleServiceAuthError& error) {
+  DCHECK(!auto_post_fetch_response_on_message_loop_);
+  CompleteRequests("", true, FakeOAuth2AccessTokenManager::ScopeSet(), error,
+                   OAuth2AccessTokenConsumer::TokenResponse());
+}
+
+void FakeOAuth2AccessTokenManager::IssueTokenForAllPendingRequests(
+    const std::string& access_token,
+    const base::Time& expiration) {
+  DCHECK(!auto_post_fetch_response_on_message_loop_);
+  CompleteRequests("", true, FakeOAuth2AccessTokenManager::ScopeSet(),
+                   GoogleServiceAuthError::AuthErrorNone(),
+                   OAuth2AccessTokenConsumer::TokenResponse(
+                       access_token, expiration, std::string() /* id_token */));
+}
+
+void FakeOAuth2AccessTokenManager::IssueTokenForAllPendingRequests(
+    const OAuth2AccessTokenConsumer::TokenResponse& token_response) {
+  DCHECK(!auto_post_fetch_response_on_message_loop_);
+  CompleteRequests("", true, FakeOAuth2AccessTokenManager::ScopeSet(),
+                   GoogleServiceAuthError::AuthErrorNone(), token_response);
+}
+
+void FakeOAuth2AccessTokenManager::CompleteRequests(
+    const std::string& account_id,
+    bool all_scopes,
+    const FakeOAuth2AccessTokenManager::ScopeSet& scope,
+    const GoogleServiceAuthError& error,
+    const OAuth2AccessTokenConsumer::TokenResponse& token_response) {
+  std::vector<FakeOAuth2AccessTokenManager::PendingRequest> requests =
+      GetPendingRequests();
+
+  // Walk the requests and notify the callbacks.
+  for (auto it = requests.begin(); it != requests.end(); ++it) {
+    // Consumers can drop requests in response to callbacks on other requests
+    // (e.g., OAuthMultiloginFetcher clears all of its requests when it gets an
+    // error on any of them).
+    if (!it->request)
+      continue;
+
+    bool scope_matches = all_scopes || it->scopes == scope;
+    bool account_matches = account_id.empty() || account_id == it->account_id;
+    if (account_matches && scope_matches) {
+      for (auto& diagnostic_observer : GetDiagnosticsObserversForTesting()) {
+        diagnostic_observer.OnFetchAccessTokenComplete(
+            account_id, it->request->GetConsumerId(), scope, error,
+            base::Time());
+      }
+
+      it->request->InformConsumer(
+          error, OAuth2AccessTokenConsumer::TokenResponse(
+                     token_response.access_token,
+                     token_response.expiration_time, token_response.id_token));
+    }
+  }
+}
+
+std::vector<FakeOAuth2AccessTokenManager::PendingRequest>
+FakeOAuth2AccessTokenManager::GetPendingRequests() {
+  std::vector<PendingRequest> valid_requests;
+  for (auto it = pending_requests_.begin(); it != pending_requests_.end();
+       ++it) {
+    if (it->request)
+      valid_requests.push_back(*it);
+  }
+  return valid_requests;
+}
+
+void FakeOAuth2AccessTokenManager::CancelAllRequests() {
+  CompleteRequests(
+      "", true, FakeOAuth2AccessTokenManager::ScopeSet(),
+      GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED),
+      OAuth2AccessTokenConsumer::TokenResponse());
+}
+
+void FakeOAuth2AccessTokenManager::CancelRequestsForAccount(
+    const CoreAccountId& account_id) {
+  CompleteRequests(
+      account_id, true, FakeOAuth2AccessTokenManager::ScopeSet(),
+      GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED),
+      OAuth2AccessTokenConsumer::TokenResponse());
+}
+
+void FakeOAuth2AccessTokenManager::FetchOAuth2Token(
+    FakeOAuth2AccessTokenManager::RequestImpl* request,
+    const CoreAccountId& account_id,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    const std::string& client_id,
+    const std::string& client_secret,
+    const FakeOAuth2AccessTokenManager::ScopeSet& scopes) {
+  PendingRequest pending_request;
+  pending_request.account_id = account_id;
+  pending_request.client_id = client_id;
+  pending_request.client_secret = client_secret;
+  pending_request.url_loader_factory = url_loader_factory;
+  pending_request.scopes = scopes;
+  pending_request.request = request->AsWeakPtr();
+  pending_requests_.push_back(pending_request);
+
+  if (auto_post_fetch_response_on_message_loop_) {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&FakeOAuth2AccessTokenManager::CompleteRequests,
+                       weak_ptr_factory_.GetWeakPtr(), account_id,
+                       /*all_scoped=*/true, scopes,
+                       GoogleServiceAuthError::AuthErrorNone(),
+                       OAuth2AccessTokenConsumer::TokenResponse(
+                           "access_token", base::Time::Max(), std::string())));
+  }
+}
+
+void FakeOAuth2AccessTokenManager::InvalidateAccessTokenImpl(
+    const CoreAccountId& account_id,
+    const std::string& client_id,
+    const FakeOAuth2AccessTokenManager::ScopeSet& scopes,
+    const std::string& access_token) {
+  // Do nothing, as we don't have a cache from which to remove the token.
+}
diff --git a/google_apis/gaia/fake_oauth2_access_token_manager.h b/google_apis/gaia/fake_oauth2_access_token_manager.h
new file mode 100644
index 0000000..fed88c1
--- /dev/null
+++ b/google_apis/gaia/fake_oauth2_access_token_manager.h
@@ -0,0 +1,124 @@
+// 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 GOOGLE_APIS_GAIA_FAKE_OAUTH2_ACCESS_TOKEN_MANAGER_H_
+#define GOOGLE_APIS_GAIA_FAKE_OAUTH2_ACCESS_TOKEN_MANAGER_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "google_apis/gaia/oauth2_access_token_manager.h"
+
+namespace network {
+class SharedURLLoaderFactory;
+}
+
+// Helper class to simplify writing unittests that depend on an instance of
+// OAuth2AccessTokenManager.
+class FakeOAuth2AccessTokenManager : public OAuth2AccessTokenManager {
+ public:
+  struct PendingRequest {
+    PendingRequest();
+    PendingRequest(const PendingRequest& other);
+    ~PendingRequest();
+
+    std::string account_id;
+    std::string client_id;
+    std::string client_secret;
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory;
+    OAuth2AccessTokenManager::ScopeSet scopes;
+    base::WeakPtr<OAuth2AccessTokenManager::RequestImpl> request;
+  };
+
+  explicit FakeOAuth2AccessTokenManager(
+      OAuth2TokenService* token_service,
+      OAuth2AccessTokenManager::Delegate* delegate);
+  ~FakeOAuth2AccessTokenManager() override;
+
+  // Gets a list of active requests (can be used by tests to validate that the
+  // correct request has been issued).
+  std::vector<PendingRequest> GetPendingRequests();
+
+  // Helper routines to issue tokens for pending requests.
+  void IssueAllTokensForAccount(const std::string& account_id,
+                                const std::string& access_token,
+                                const base::Time& expiration);
+
+  // Helper routines to issue token for pending requests based on TokenResponse.
+  void IssueAllTokensForAccount(
+      const std::string& account_id,
+      const OAuth2AccessTokenConsumer::TokenResponse& token_response);
+
+  void IssueErrorForAllPendingRequestsForAccount(
+      const std::string& account_id,
+      const GoogleServiceAuthError& error);
+
+  void IssueTokenForScope(const OAuth2AccessTokenManager::ScopeSet& scopes,
+                          const std::string& access_token,
+                          const base::Time& expiration);
+
+  void IssueTokenForScope(
+      const OAuth2AccessTokenManager::ScopeSet& scopes,
+      const OAuth2AccessTokenConsumer::TokenResponse& token_response);
+
+  void IssueErrorForScope(const OAuth2AccessTokenManager::ScopeSet& scopes,
+                          const GoogleServiceAuthError& error);
+
+  void IssueTokenForAllPendingRequests(const std::string& access_token,
+                                       const base::Time& expiration);
+
+  void IssueTokenForAllPendingRequests(
+      const OAuth2AccessTokenConsumer::TokenResponse& token_response);
+
+  void IssueErrorForAllPendingRequests(const GoogleServiceAuthError& error);
+
+  void set_auto_post_fetch_response_on_message_loop(bool auto_post_response) {
+    auto_post_fetch_response_on_message_loop_ = auto_post_response;
+  }
+
+  // OAuth2AccessTokenManager overrides.
+  void CancelAllRequests() override;
+
+  void CancelRequestsForAccount(const CoreAccountId& account_id) override;
+
+  void FetchOAuth2Token(
+      OAuth2AccessTokenManager::RequestImpl* request,
+      const CoreAccountId& account_id,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      const std::string& client_id,
+      const std::string& client_secret,
+      const OAuth2AccessTokenManager::ScopeSet& scopes) override;
+
+  void InvalidateAccessTokenImpl(
+      const CoreAccountId& account_id,
+      const std::string& client_id,
+      const OAuth2AccessTokenManager::ScopeSet& scopes,
+      const std::string& access_token) override;
+
+ private:
+  // Helper function to complete pending requests - if |all_scopes| is true,
+  // then all pending requests are completed, otherwise, only those requests
+  // matching |scopes| are completed.  If |account_id| is empty, then pending
+  // requests for all accounts are completed, otherwise only requests for the
+  // given account.
+  void CompleteRequests(
+      const std::string& account_id,
+      bool all_scopes,
+      const OAuth2AccessTokenManager::ScopeSet& scopes,
+      const GoogleServiceAuthError& error,
+      const OAuth2AccessTokenConsumer::TokenResponse& token_response);
+
+  std::vector<PendingRequest> pending_requests_;
+
+  // If true, then this fake manager will post responses to
+  // |FetchOAuth2Token| on the current run loop. There is no need to call
+  // |IssueTokenForScope| in this case.
+  bool auto_post_fetch_response_on_message_loop_;
+
+  base::WeakPtrFactory<FakeOAuth2AccessTokenManager> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeOAuth2AccessTokenManager);
+};
+
+#endif  // GOOGLE_APIS_GAIA_FAKE_OAUTH2_ACCESS_TOKEN_MANAGER_H_
diff --git a/google_apis/gaia/fake_oauth2_token_service.cc b/google_apis/gaia/fake_oauth2_token_service.cc
index 0fede583..988f5e3 100644
--- a/google_apis/gaia/fake_oauth2_token_service.cc
+++ b/google_apis/gaia/fake_oauth2_token_service.cc
@@ -6,17 +6,13 @@
 
 #include <memory>
 
-FakeOAuth2TokenService::PendingRequest::PendingRequest() {
-}
-
-FakeOAuth2TokenService::PendingRequest::PendingRequest(
-    const PendingRequest& other) = default;
-
-FakeOAuth2TokenService::PendingRequest::~PendingRequest() {
-}
-
 FakeOAuth2TokenService::FakeOAuth2TokenService()
-    : OAuth2TokenService(std::make_unique<FakeOAuth2TokenServiceDelegate>()) {}
+    : OAuth2TokenService(std::make_unique<FakeOAuth2TokenServiceDelegate>()) {
+  OverrideAccessTokenManagerForTesting(
+      std::make_unique<FakeOAuth2AccessTokenManager>(
+          this /* OAuth2TokenService* */,
+          this /* OAuth2AccessTokenManager::Delegate* */));
+}
 
 FakeOAuth2TokenService::~FakeOAuth2TokenService() {
 }
@@ -28,13 +24,9 @@
     const std::string& client_id,
     const std::string& client_secret,
     const OAuth2AccessTokenManager::ScopeSet& scopes) {
-  PendingRequest pending_request;
-  pending_request.account_id = account_id;
-  pending_request.client_id = client_id;
-  pending_request.client_secret = client_secret;
-  pending_request.scopes = scopes;
-  pending_request.request = request->AsWeakPtr();
-  pending_requests_.push_back(pending_request);
+  GetFakeAccessTokenManager()->FetchOAuth2Token(request, account_id,
+                                                url_loader_factory, client_id,
+                                                client_secret, scopes);
 }
 
 void FakeOAuth2TokenService::AddAccount(const CoreAccountId& account_id) {
@@ -48,38 +40,23 @@
 void FakeOAuth2TokenService::IssueAllTokensForAccount(
     const CoreAccountId& account_id,
     const OAuth2AccessTokenConsumer::TokenResponse& token_response) {
-  // Walk the requests and notify the callbacks.
-  // Using a copy of pending requests to make sure a new token request triggered
-  // from the handling code does not invalidate the iterator.
-  std::vector<PendingRequest> pending_requests_copy = pending_requests_;
-  for (std::vector<PendingRequest>::iterator it = pending_requests_copy.begin();
-       it != pending_requests_copy.end();
-       ++it) {
-    if (it->request && (account_id == it->account_id)) {
-      it->request->InformConsumer(GoogleServiceAuthError::AuthErrorNone(),
-                                  token_response);
-    }
-  }
+  GetFakeAccessTokenManager()->IssueAllTokensForAccount(account_id,
+                                                        token_response);
 }
 
 void FakeOAuth2TokenService::IssueErrorForAllPendingRequestsForAccount(
     const CoreAccountId& account_id,
     const GoogleServiceAuthError& auth_error) {
-  // Walk the requests and notify the callbacks.
-  // Using a copy of pending requests to make sure retrying a request in
-  // response to the error does not invalidate the iterator.
-  std::vector<PendingRequest> pending_requests_copy = pending_requests_;
-  for (std::vector<PendingRequest>::iterator it = pending_requests_copy.begin();
-       it != pending_requests_copy.end();
-       ++it) {
-    if (it->request && (account_id == it->account_id)) {
-      it->request->InformConsumer(auth_error,
-                                  OAuth2AccessTokenConsumer::TokenResponse());
-    }
-  }
+  GetFakeAccessTokenManager()->IssueErrorForAllPendingRequestsForAccount(
+      account_id, auth_error);
 }
 
 FakeOAuth2TokenServiceDelegate*
 FakeOAuth2TokenService::GetFakeOAuth2TokenServiceDelegate() {
   return static_cast<FakeOAuth2TokenServiceDelegate*>(GetDelegate());
 }
+
+FakeOAuth2AccessTokenManager*
+FakeOAuth2TokenService::GetFakeAccessTokenManager() {
+  return static_cast<FakeOAuth2AccessTokenManager*>(GetAccessTokenManager());
+}
diff --git a/google_apis/gaia/fake_oauth2_token_service.h b/google_apis/gaia/fake_oauth2_token_service.h
index 9d5df0c..1a35d91 100644
--- a/google_apis/gaia/fake_oauth2_token_service.h
+++ b/google_apis/gaia/fake_oauth2_token_service.h
@@ -7,8 +7,8 @@
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "google_apis/gaia/fake_oauth2_access_token_manager.h"
 #include "google_apis/gaia/fake_oauth2_token_service_delegate.h"
-#include "google_apis/gaia/oauth2_access_token_manager.h"
 #include "google_apis/gaia/oauth2_token_service.h"
 
 namespace network {
@@ -55,19 +55,7 @@
       const std::string& access_token) override {}
 
  private:
-  struct PendingRequest {
-    PendingRequest();
-    PendingRequest(const PendingRequest& other);
-    ~PendingRequest();
-
-    CoreAccountId account_id;
-    std::string client_id;
-    std::string client_secret;
-    OAuth2AccessTokenManager::ScopeSet scopes;
-    base::WeakPtr<OAuth2AccessTokenManager::RequestImpl> request;
-  };
-
-  std::vector<PendingRequest> pending_requests_;
+  FakeOAuth2AccessTokenManager* GetFakeAccessTokenManager();
 
   DISALLOW_COPY_AND_ASSIGN(FakeOAuth2TokenService);
 };
diff --git a/google_apis/gaia/gaia_auth_fetcher_unittest.cc b/google_apis/gaia/gaia_auth_fetcher_unittest.cc
index 291e38d3..579a52f6 100644
--- a/google_apis/gaia/gaia_auth_fetcher_unittest.cc
+++ b/google_apis/gaia/gaia_auth_fetcher_unittest.cc
@@ -29,6 +29,7 @@
 #include "net/base/net_errors.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_status_code.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 #include "services/network/test/test_url_loader_factory.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -441,7 +442,7 @@
   TestGaiaAuthFetcher auth(&consumer, GetURLLoaderFactory());
   auth.CreateAndStartGaiaFetcherForTesting(/*body=*/"", /*headers=*/"",
                                            oauth_login_gurl_, /*load_flags=*/0,
-                                           NO_TRAFFIC_ANNOTATION_YET);
+                                           TRAFFIC_ANNOTATION_FOR_TESTS);
   auth.TestOnURLLoadCompleteInternal(net::OK, net::HTTP_OK, data);
 }
 
@@ -462,7 +463,7 @@
       /*body=*/"", /*headers=*/"",
       GaiaUrls::GetInstance()->ListAccountsURLWithSource(
           GaiaConstants::kChromeSource),
-      /*load_flags=*/0, NO_TRAFFIC_ANNOTATION_YET);
+      /*load_flags=*/0, TRAFFIC_ANNOTATION_FOR_TESTS);
   ASSERT_EQ(received_requests_.size(), 1U);
   EXPECT_EQ(net::LOAD_NORMAL, received_requests_.at(0).load_flags);
   EXPECT_EQ(GaiaUrls::GetInstance()->gaia_url(),
@@ -479,7 +480,7 @@
       /*body=*/"", /*headers=*/"",
       GaiaUrls::GetInstance()->LogOutURLWithSource(
           GaiaConstants::kChromeSource),
-      /*load_flags=*/0, NO_TRAFFIC_ANNOTATION_YET);
+      /*load_flags=*/0, TRAFFIC_ANNOTATION_FOR_TESTS);
   auth.TestOnURLLoadCompleteInternal(net::OK);
 }
 
@@ -497,7 +498,7 @@
       /*body=*/"", /*headers=*/"",
       GaiaUrls::GetInstance()->LogOutURLWithSource(
           GaiaConstants::kChromeSource),
-      /*load_flags=*/0, NO_TRAFFIC_ANNOTATION_YET);
+      /*load_flags=*/0, TRAFFIC_ANNOTATION_FOR_TESTS);
   auth.TestOnURLLoadCompleteInternal(error_no);
 }
 
@@ -513,7 +514,7 @@
       /*body=*/"", /*headers=*/"",
       GaiaUrls::GetInstance()->GetCheckConnectionInfoURLWithSource(
           GaiaConstants::kChromeSource),
-      /*load_flags=*/0, NO_TRAFFIC_ANNOTATION_YET);
+      /*load_flags=*/0, TRAFFIC_ANNOTATION_FOR_TESTS);
   auth.TestOnURLLoadCompleteInternal(net::OK, net::HTTP_OK, data);
 }
 
@@ -527,7 +528,7 @@
   TestGaiaAuthFetcher auth(&consumer, GetURLLoaderFactory());
   auth.CreateAndStartGaiaFetcherForTesting(
       /*body=*/"", /*headers=*/"", GaiaUrls::GetInstance()->oauth2_revoke_url(),
-      /*load_flags=*/0, NO_TRAFFIC_ANNOTATION_YET);
+      /*load_flags=*/0, TRAFFIC_ANNOTATION_FOR_TESTS);
   auth.TestOnURLLoadCompleteInternal(net::OK, net::HTTP_OK, data);
 }
 
@@ -541,7 +542,7 @@
   TestGaiaAuthFetcher auth(&consumer, GetURLLoaderFactory());
   auth.CreateAndStartGaiaFetcherForTesting(
       /*body=*/"", /*headers=*/"", GaiaUrls::GetInstance()->oauth2_revoke_url(),
-      /*load_flags=*/0, NO_TRAFFIC_ANNOTATION_YET);
+      /*load_flags=*/0, TRAFFIC_ANNOTATION_FOR_TESTS);
   auth.TestOnURLLoadCompleteInternal(net::ERR_ABORTED);
 }
 
@@ -555,7 +556,7 @@
   TestGaiaAuthFetcher auth(&consumer, GetURLLoaderFactory());
   auth.CreateAndStartGaiaFetcherForTesting(
       /*body=*/"", /*headers=*/"", GaiaUrls::GetInstance()->oauth2_revoke_url(),
-      /*load_flags=*/0, NO_TRAFFIC_ANNOTATION_YET);
+      /*load_flags=*/0, TRAFFIC_ANNOTATION_FOR_TESTS);
   auth.TestOnURLLoadCompleteInternal(net::ERR_CERT_CONTAINS_ERRORS);
 }
 
@@ -569,7 +570,7 @@
   TestGaiaAuthFetcher auth(&consumer, GetURLLoaderFactory());
   auth.CreateAndStartGaiaFetcherForTesting(
       /*body=*/"", /*headers=*/"", GaiaUrls::GetInstance()->oauth2_revoke_url(),
-      /*load_flags=*/0, NO_TRAFFIC_ANNOTATION_YET);
+      /*load_flags=*/0, TRAFFIC_ANNOTATION_FOR_TESTS);
   auth.TestOnURLLoadCompleteInternal(net::ERR_TIMED_OUT);
 }
 
@@ -584,7 +585,7 @@
   TestGaiaAuthFetcher auth(&consumer, GetURLLoaderFactory());
   auth.CreateAndStartGaiaFetcherForTesting(
       /*body=*/"", /*headers=*/"", GaiaUrls::GetInstance()->oauth2_revoke_url(),
-      /*load_flags=*/0, NO_TRAFFIC_ANNOTATION_YET);
+      /*load_flags=*/0, TRAFFIC_ANNOTATION_FOR_TESTS);
   auth.TestOnURLLoadCompleteInternal(net::OK, net::HTTP_BAD_REQUEST, data);
 }
 
@@ -599,7 +600,7 @@
   TestGaiaAuthFetcher auth(&consumer, GetURLLoaderFactory());
   auth.CreateAndStartGaiaFetcherForTesting(
       /*body=*/"", /*headers=*/"", GaiaUrls::GetInstance()->oauth2_revoke_url(),
-      /*load_flags=*/0, NO_TRAFFIC_ANNOTATION_YET);
+      /*load_flags=*/0, TRAFFIC_ANNOTATION_FOR_TESTS);
   auth.TestOnURLLoadCompleteInternal(net::OK, net::HTTP_BAD_REQUEST, data);
 }
 
@@ -614,7 +615,7 @@
   TestGaiaAuthFetcher auth(&consumer, GetURLLoaderFactory());
   auth.CreateAndStartGaiaFetcherForTesting(
       /*body=*/"", /*headers=*/"", GaiaUrls::GetInstance()->oauth2_revoke_url(),
-      /*load_flags=*/0, NO_TRAFFIC_ANNOTATION_YET);
+      /*load_flags=*/0, TRAFFIC_ANNOTATION_FOR_TESTS);
   auth.TestOnURLLoadCompleteInternal(net::OK, net::HTTP_INTERNAL_SERVER_ERROR,
                                      data);
 }
diff --git a/google_apis/gaia/oauth2_access_token_manager.cc b/google_apis/gaia/oauth2_access_token_manager.cc
index 202e36c..3b932c0 100644
--- a/google_apis/gaia/oauth2_access_token_manager.cc
+++ b/google_apis/gaia/oauth2_access_token_manager.cc
@@ -580,6 +580,12 @@
              : iter->second->GetWaitingRequestCount();
 }
 
+const base::ObserverList<OAuth2AccessTokenManager::DiagnosticsObserver,
+                         true>::Unchecked&
+OAuth2AccessTokenManager::GetDiagnosticsObserversForTesting() {
+  return diagnostics_observer_list_;
+}
+
 std::unique_ptr<OAuth2AccessTokenFetcher>
 OAuth2AccessTokenManager::CreateAccessTokenFetcher(
     const CoreAccountId& account_id,
diff --git a/google_apis/gaia/oauth2_access_token_manager.h b/google_apis/gaia/oauth2_access_token_manager.h
index 61aab64..36cdbed 100644
--- a/google_apis/gaia/oauth2_access_token_manager.h
+++ b/google_apis/gaia/oauth2_access_token_manager.h
@@ -203,8 +203,10 @@
       const ScopeSet& scopes,
       Consumer* consumer);
 
-  // Fetches an OAuth token for the specified client/scopes.
-  void FetchOAuth2Token(
+  // Fetches an OAuth token for the specified client/scopes. Virtual so it can
+  // be overridden for tests.
+  // TODO(https://crbug.com/967598): Move this to protected.
+  virtual void FetchOAuth2Token(
       RequestImpl* request,
       const CoreAccountId& account_id,
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
@@ -234,11 +236,15 @@
   // used to request the tokens.
   void ClearCacheForAccount(const CoreAccountId& account_id);
 
-  // Cancels all requests that are currently in progress.
-  void CancelAllRequests();
+  // Cancels all requests that are currently in progress. Virtual so it can be
+  // overridden for tests.
+  // TODO(https://crbug.com/967598): Move this to protected.
+  virtual void CancelAllRequests();
 
-  // Cancels all requests related to a given |account_id|.
-  void CancelRequestsForAccount(const CoreAccountId& account_id);
+  // Cancels all requests related to a given |account_id|. Virtual so it can be
+  // overridden for tests.
+  // TODO(https://crbug.com/967598): Move this to protected.
+  virtual void CancelRequestsForAccount(const CoreAccountId& account_id);
 
   // Mark an OAuth2 |access_token| issued for |account_id| and |scopes| as
   // invalid. This should be done if the token was received from this class,
@@ -250,11 +256,12 @@
                              const std::string& access_token);
 
   // Invalidates the |access_token| issued for |account_id|, |client_id| and
-  // |scopes|.
-  void InvalidateAccessTokenImpl(const CoreAccountId& account_id,
-                                 const std::string& client_id,
-                                 const ScopeSet& scopes,
-                                 const std::string& access_token);
+  // |scopes|. Virtual so it can be overridden for tests.
+  // TODO(https://crbug.com/967598): Move this to protected.
+  virtual void InvalidateAccessTokenImpl(const CoreAccountId& account_id,
+                                         const std::string& client_id,
+                                         const ScopeSet& scopes,
+                                         const std::string& access_token);
 
   void set_max_authorization_token_fetch_retries_for_testing(int max_retries);
 
@@ -263,6 +270,10 @@
                                          const CoreAccountId& account_id,
                                          const ScopeSet& scopes) const;
 
+  // Returns a list of DiagnosticsObservers.
+  const base::ObserverList<DiagnosticsObserver, true>::Unchecked&
+  GetDiagnosticsObserversForTesting();
+
  private:
   // TODO(https://crbug.com/967598): Remove this once |token_cache_| management
   // is moved to OAuth2AccessTokenManager.
diff --git a/gpu/config/gpu_finch_features.cc b/gpu/config/gpu_finch_features.cc
index c5a7732b..66675ae1 100644
--- a/gpu/config/gpu_finch_features.cc
+++ b/gpu/config/gpu_finch_features.cc
@@ -75,7 +75,7 @@
     "DirectCompositionUnderlays", base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Use ThreadPriority::DISPLAY for GPU main, viz compositor and IO threads.
-#if defined(OS_ANDROID) || defined(OS_CHROMEOS) || defined(USE_OZONE)
+#if defined(OS_ANDROID) || defined(OS_CHROMEOS)
 const base::Feature kGpuUseDisplayThreadPriority{
     "GpuUseDisplayThreadPriority", base::FEATURE_ENABLED_BY_DEFAULT};
 #else
diff --git a/gpu/config/gpu_info.cc b/gpu/config/gpu_info.cc
index 963884c..7b9fddb 100644
--- a/gpu/config/gpu_info.cc
+++ b/gpu/config/gpu_info.cc
@@ -326,6 +326,7 @@
   enumerator->AddInt64("rgbaVisual", rgba_visual);
 #endif
   enumerator->AddBool("oopRasterizationSupported", oop_rasterization_supported);
+  enumerator->EndAuxAttributes();
 }
 
 }  // namespace gpu
diff --git a/gpu/vulkan/vulkan_surface.cc b/gpu/vulkan/vulkan_surface.cc
index 339a0b5..a4a1e05 100644
--- a/gpu/vulkan/vulkan_surface.cc
+++ b/gpu/vulkan/vulkan_surface.cc
@@ -113,12 +113,13 @@
 
 void VulkanSurface::Destroy() {
   swap_chain_->Destroy();
+  swap_chain_ = nullptr;
   vkDestroySurfaceKHR(vk_instance_, surface_, nullptr);
   surface_ = VK_NULL_HANDLE;
 }
 
 gfx::SwapResult VulkanSurface::SwapBuffers() {
-  return swap_chain_->SwapBuffers();
+  return swap_chain_->PresentBuffer();
 }
 
 VulkanSwapChain* VulkanSurface::GetSwapChain() {
@@ -133,7 +134,7 @@
   return CreateSwapChain(size);
 }
 
-bool VulkanSurface::CreateSwapChain(const gfx::Size& size) {
+bool VulkanSurface::CreateSwapChain(const gfx::Size& new_size) {
   // Get Surface Information.
   VkSurfaceCapabilitiesKHR surface_caps;
   VkResult result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
@@ -144,19 +145,19 @@
     return false;
   }
 
-  // If width and height of the surface are 0xFFFFFFFF, it means the surface
-  // size will be determined by the extent of a swapchain targeting the surface.
-  // In that case, we will use the |size| which is the window size for the
-  // swapchain. Otherwise, we just use the current surface size for the
-  // swapchian.
-  const uint32_t kUndefinedExtent = 0xFFFFFFFF;
-  if (surface_caps.currentExtent.width == kUndefinedExtent &&
-      surface_caps.currentExtent.height == kUndefinedExtent) {
-    surface_caps.currentExtent.width = std::max(
-        surface_caps.minImageExtent.width, static_cast<uint32_t>(size.width()));
-    surface_caps.currentExtent.height =
-        std::max(surface_caps.minImageExtent.height,
-                 static_cast<uint32_t>(size.height()));
+  // For Android, the current vulkan surface size may not match the new_size
+  // (the current window size), in that case, we will create a swapchain with
+  // the requested new_size, and vulkan surface size should match the swapchain
+  // images size soon.
+  if (!new_size.IsEmpty()) {
+    DLOG_IF(ERROR,
+            base::checked_cast<int>(surface_caps.currentExtent.width) !=
+                    new_size.width() ||
+                base::checked_cast<int>(surface_caps.currentExtent.height) !=
+                    new_size.height())
+        << "Requested new size doesn't match vulkan surface size.";
+    surface_caps.currentExtent.width = new_size.width();
+    surface_caps.currentExtent.height = new_size.height();
   }
 
   DCHECK_GE(surface_caps.currentExtent.width,
@@ -170,14 +171,12 @@
   DCHECK_GT(surface_caps.currentExtent.width, 0u);
   DCHECK_GT(surface_caps.currentExtent.height, 0u);
 
-  gfx::Size new_size(
-      base::checked_cast<int>(surface_caps.currentExtent.width),
-      base::checked_cast<int>(surface_caps.currentExtent.height));
   if (size_ == new_size)
     return true;
 
   size_ = new_size;
   auto swap_chain = std::make_unique<VulkanSwapChain>();
+
   // Create Swapchain.
   if (!swap_chain->Initialize(device_queue_, surface_, surface_caps,
                               surface_format_, std::move(swap_chain_))) {
@@ -185,6 +184,7 @@
   }
 
   swap_chain_ = std::move(swap_chain);
+  ++swap_chain_generation_;
   return true;
 }
 
diff --git a/gpu/vulkan/vulkan_surface.h b/gpu/vulkan/vulkan_surface.h
index 8bae0235..c33a7387 100644
--- a/gpu/vulkan/vulkan_surface.h
+++ b/gpu/vulkan/vulkan_surface.h
@@ -42,6 +42,7 @@
   gfx::SwapResult SwapBuffers();
 
   VulkanSwapChain* GetSwapChain();
+  uint32_t swap_chain_generation() const { return swap_chain_generation_; }
 
   void Finish();
 
@@ -51,13 +52,17 @@
   VkSurfaceFormatKHR surface_format() const { return surface_format_; }
 
  private:
-  bool CreateSwapChain(const gfx::Size& size);
+  bool CreateSwapChain(const gfx::Size& new_size);
 
   const VkInstance vk_instance_;
   gfx::Size size_;
   VkSurfaceKHR surface_ = VK_NULL_HANDLE;
   VkSurfaceFormatKHR surface_format_ = {};
   VulkanDeviceQueue* device_queue_ = nullptr;
+
+  // The generation of |swap_chain_|, it will be increasted if a new
+  // |swap_chain_| is created due to resizing, etec.
+  uint32_t swap_chain_generation_ = 0u;
   std::unique_ptr<VulkanSwapChain> swap_chain_;
 
   DISALLOW_COPY_AND_ASSIGN(VulkanSurface);
diff --git a/gpu/vulkan/vulkan_swap_chain.cc b/gpu/vulkan/vulkan_swap_chain.cc
index 552d892..c73853c 100644
--- a/gpu/vulkan/vulkan_swap_chain.cc
+++ b/gpu/vulkan/vulkan_swap_chain.cc
@@ -56,7 +56,8 @@
   DestroySwapChain();
 }
 
-gfx::SwapResult VulkanSwapChain::SwapBuffers() {
+gfx::SwapResult VulkanSwapChain::PresentBuffer() {
+  DCHECK(acquired_image_);
   DCHECK(end_write_semaphore_ != VK_NULL_HANDLE);
 
   VkResult result = VK_SUCCESS;
@@ -64,7 +65,7 @@
   VkQueue queue = device_queue_->GetVulkanQueue();
   auto* fence_helper = device_queue_->GetFenceHelper();
 
-  auto& current_image_data = images_[current_image_];
+  auto& current_image_data = images_[*acquired_image_];
   if (current_image_data.layout != VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) {
     {
       current_image_data.command_buffer->Clear();
@@ -96,31 +97,17 @@
   present_info.pWaitSemaphores = &end_write_semaphore_;
   present_info.swapchainCount = 1;
   present_info.pSwapchains = &swap_chain_;
-  present_info.pImageIndices = &current_image_;
+  present_info.pImageIndices = &acquired_image_.value();
 
   result = vkQueuePresentKHR(queue, &present_info);
-  if (VK_SUCCESS != result) {
+  if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
+    DLOG(ERROR) << "vkQueuePresentKHR() failed: " << result;
     return gfx::SwapResult::SWAP_FAILED;
   }
+  acquired_image_.reset();
   fence_helper->EnqueueSemaphoreCleanupForSubmittedWork(end_write_semaphore_);
   end_write_semaphore_ = VK_NULL_HANDLE;
 
-  VkSemaphore vk_semaphore = CreateSemaphore(device);
-  DCHECK(vk_semaphore != VK_NULL_HANDLE);
-
-  uint32_t next_image = 0;
-  // Acquire then next image.
-  result = vkAcquireNextImageKHR(device, swap_chain_, UINT64_MAX, vk_semaphore,
-                                 VK_NULL_HANDLE, &next_image);
-  if (VK_SUCCESS != result) {
-    vkDestroySemaphore(device, vk_semaphore, nullptr /* pAllocator */);
-    DLOG(ERROR) << "vkAcquireNextImageKHR() failed: " << result;
-    return gfx::SwapResult::SWAP_FAILED;
-  }
-
-  current_image_ = next_image;
-  DCHECK(begin_write_semaphore_ == VK_NULL_HANDLE);
-  begin_write_semaphore_ = vk_semaphore;
   return gfx::SwapResult::SWAP_ACK;
 }
 
@@ -143,7 +130,14 @@
   swap_chain_create_info.imageArrayLayers = 1;
   swap_chain_create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
   swap_chain_create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
-  swap_chain_create_info.preTransform = surface_caps.currentTransform;
+  // Always set preTransform to VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR (which is
+  // relative to the presentation engine's natural orientation), if it does not
+  // match the currentTransform value returned by
+  // vkGetPhysicalDeviceSurfaceCapabilitiesKHR, the presentation engine will
+  // transform the image content as part of the presentation operation.
+  // TODO(penghuang): Support preTransform for better performance.
+  // https://crbug.com/957485
+  swap_chain_create_info.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
   swap_chain_create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
   swap_chain_create_info.presentMode = VK_PRESENT_MODE_FIFO_KHR;
   swap_chain_create_info.clipped = true;
@@ -212,27 +206,10 @@
     // Initialize the command buffer for this buffer data.
     image_data.command_buffer = command_pool_->CreatePrimaryCommandBuffer();
   }
-
-  VkSemaphore vk_semaphore = CreateSemaphore(device);
-  DCHECK(vk_semaphore != VK_NULL_HANDLE);
-
-  // Acquire the initial buffer.
-  result = vkAcquireNextImageKHR(device, swap_chain_, UINT64_MAX, vk_semaphore,
-                                 VK_NULL_HANDLE, &current_image_);
-  if (VK_SUCCESS != result) {
-    DLOG(ERROR) << "vkAcquireNextImageKHR() failed: " << result;
-    return false;
-  }
-  begin_write_semaphore_ = vk_semaphore;
   return true;
 }
 
 void VulkanSwapChain::DestroySwapImages() {
-  if (begin_write_semaphore_)
-    vkDestroySemaphore(device_queue_->GetVulkanDevice(), begin_write_semaphore_,
-                       nullptr /* pAllocator */);
-  begin_write_semaphore_ = VK_NULL_HANDLE;
-
   if (end_write_semaphore_)
     vkDestroySemaphore(device_queue_->GetVulkanDevice(), end_write_semaphore_,
                        nullptr /* pAllocator */);
@@ -250,7 +227,7 @@
   command_pool_ = nullptr;
 }
 
-void VulkanSwapChain::BeginWriteCurrentImage(VkImage* image,
+bool VulkanSwapChain::BeginWriteCurrentImage(VkImage* image,
                                              uint32_t* image_index,
                                              VkImageLayout* image_layout,
                                              VkSemaphore* semaphore) {
@@ -259,25 +236,52 @@
   DCHECK(image_layout);
   DCHECK(semaphore);
   DCHECK(!is_writing_);
-  DCHECK(begin_write_semaphore_ != VK_NULL_HANDLE);
-  DCHECK(end_write_semaphore_ == VK_NULL_HANDLE);
 
-  auto& current_image_data = images_[current_image_];
+  VkSemaphore vk_semaphore = VK_NULL_HANDLE;
+
+  if (!acquired_image_) {
+    DCHECK(end_write_semaphore_ == VK_NULL_HANDLE);
+
+    VkDevice device = device_queue_->GetVulkanDevice();
+    vk_semaphore = CreateSemaphore(device);
+    DCHECK(vk_semaphore != VK_NULL_HANDLE);
+
+    uint32_t next_image = 0;
+    // Acquire then next image.
+    auto result =
+        vkAcquireNextImageKHR(device, swap_chain_, UINT64_MAX, vk_semaphore,
+                              VK_NULL_HANDLE, &next_image);
+    if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
+      vkDestroySemaphore(device, vk_semaphore, nullptr /* pAllocator */);
+      DLOG(ERROR) << "vkAcquireNextImageKHR() failed: " << result;
+      return false;
+    }
+    acquired_image_.emplace(next_image);
+  } else {
+    // In this case, PresentBuffer() is not called after
+    // {Begin,End}WriteCurrentImage pairs, |end_write_semaphore_| should be
+    // waited on before writing the image again.
+    vk_semaphore = end_write_semaphore_;
+    end_write_semaphore_ = VK_NULL_HANDLE;
+  }
+
+  auto& current_image_data = images_[*acquired_image_];
   *image = current_image_data.image;
-  *image_index = current_image_;
+  *image_index = *acquired_image_;
   *image_layout = current_image_data.layout;
-  *semaphore = begin_write_semaphore_;
-  begin_write_semaphore_ = VK_NULL_HANDLE;
+  *semaphore = vk_semaphore;
   is_writing_ = true;
+
+  return true;
 }
 
 void VulkanSwapChain::EndWriteCurrentImage(VkImageLayout image_layout,
                                            VkSemaphore semaphore) {
   DCHECK(is_writing_);
-  DCHECK(begin_write_semaphore_ == VK_NULL_HANDLE);
+  DCHECK(acquired_image_);
   DCHECK(end_write_semaphore_ == VK_NULL_HANDLE);
 
-  auto& current_image_data = images_[current_image_];
+  auto& current_image_data = images_[*acquired_image_];
   current_image_data.layout = image_layout;
   end_write_semaphore_ = semaphore;
   is_writing_ = false;
@@ -285,17 +289,17 @@
 
 VulkanSwapChain::ScopedWrite::ScopedWrite(VulkanSwapChain* swap_chain)
     : swap_chain_(swap_chain) {
-  swap_chain_->BeginWriteCurrentImage(&image_, &image_index_, &image_layout_,
-                                      &begin_semaphore_);
+  success_ = swap_chain_->BeginWriteCurrentImage(
+      &image_, &image_index_, &image_layout_, &begin_semaphore_);
 }
 
 VulkanSwapChain::ScopedWrite::~ScopedWrite() {
   DCHECK(begin_semaphore_ == VK_NULL_HANDLE);
-  swap_chain_->EndWriteCurrentImage(image_layout_, end_semaphore_);
+  if (success_)
+    swap_chain_->EndWriteCurrentImage(image_layout_, end_semaphore_);
 }
 
 VkSemaphore VulkanSwapChain::ScopedWrite::TakeBeginSemaphore() {
-  DCHECK(begin_semaphore_ != VK_NULL_HANDLE);
   VkSemaphore semaphore = begin_semaphore_;
   begin_semaphore_ = VK_NULL_HANDLE;
   return semaphore;
diff --git a/gpu/vulkan/vulkan_swap_chain.h b/gpu/vulkan/vulkan_swap_chain.h
index c073c50..11f8996a 100644
--- a/gpu/vulkan/vulkan_swap_chain.h
+++ b/gpu/vulkan/vulkan_swap_chain.h
@@ -10,6 +10,7 @@
 #include <vulkan/vulkan.h>
 
 #include "base/logging.h"
+#include "base/optional.h"
 #include "gpu/vulkan/vulkan_export.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/swap_result.h"
@@ -27,6 +28,7 @@
     explicit ScopedWrite(VulkanSwapChain* swap_chain);
     ~ScopedWrite();
 
+    bool success() const { return success_; }
     VkImage image() const { return image_; }
     uint32_t image_index() const { return image_index_; }
     VkImageLayout image_layout() const { return image_layout_; }
@@ -42,6 +44,7 @@
 
    private:
     VulkanSwapChain* const swap_chain_;
+    bool success_ = false;
     VkImage image_ = VK_NULL_HANDLE;
     uint32_t image_index_ = 0;
     VkImageLayout image_layout_ = VK_IMAGE_LAYOUT_UNDEFINED;
@@ -61,10 +64,11 @@
                   std::unique_ptr<VulkanSwapChain> old_swap_chain);
   // Destroy() should be called when all related GPU tasks have been finished.
   void Destroy();
-  gfx::SwapResult SwapBuffers();
+
+  // Present the current buffer.
+  gfx::SwapResult PresentBuffer();
 
   uint32_t num_images() const { return static_cast<uint32_t>(images_.size()); }
-  uint32_t current_image() const { return current_image_; }
   const gfx::Size& size() const { return size_; }
 
  private:
@@ -77,7 +81,7 @@
   bool InitializeSwapImages(const VkSurfaceCapabilitiesKHR& surface_caps,
                             const VkSurfaceFormatKHR& surface_format);
   void DestroySwapImages();
-  void BeginWriteCurrentImage(VkImage* image,
+  bool BeginWriteCurrentImage(VkImage* image,
                               uint32_t* image_index,
                               VkImageLayout* layout,
                               VkSemaphore* semaphore);
@@ -102,9 +106,10 @@
     std::unique_ptr<VulkanCommandBuffer> command_buffer;
   };
   std::vector<ImageData> images_;
-  uint32_t current_image_ = 0;
+
+  // Acquired image index.
+  base::Optional<uint32_t> acquired_image_;
   bool is_writing_ = false;
-  VkSemaphore begin_write_semaphore_ = VK_NULL_HANDLE;
   VkSemaphore end_write_semaphore_ = VK_NULL_HANDLE;
 
   DISALLOW_COPY_AND_ASSIGN(VulkanSwapChain);
diff --git a/infra/config/luci-milo.cfg b/infra/config/luci-milo.cfg
index 45acd85a..f469948b 100644
--- a/infra/config/luci-milo.cfg
+++ b/infra/config/luci-milo.cfg
@@ -189,7 +189,7 @@
     }
     links {
       text: "chrome"
-      url: "/p/chrome/g/tryserver.chromium.chrome/builders"
+      url: "/p/chrome/g/tryserver.chrome/builders"
       alt: "Chrome"
     }
     links {
diff --git a/infra/config/tricium-prod.cfg b/infra/config/tricium-prod.cfg
index 6a82b50..88027e2 100644
--- a/infra/config/tricium-prod.cfg
+++ b/infra/config/tricium-prod.cfg
@@ -25,6 +25,11 @@
 }
 
 selections {
+  function: "MojomCommentator"
+  platform: UBUNTU
+}
+
+selections {
   function: "SpellChecker"
   platform: UBUNTU
 }
diff --git a/ios/chrome/app/application_delegate/metrics_mediator.mm b/ios/chrome/app/application_delegate/metrics_mediator.mm
index 41042f89..276e123 100644
--- a/ios/chrome/app/application_delegate/metrics_mediator.mm
+++ b/ios/chrome/app/application_delegate/metrics_mediator.mm
@@ -23,9 +23,9 @@
 #import "ios/chrome/browser/net/connection_type_observer_bridge.h"
 #include "ios/chrome/browser/pref_names.h"
 #include "ios/chrome/browser/system_flags.h"
-#import "ios/chrome/browser/tabs/tab.h"
 #import "ios/chrome/browser/tabs/tab_model.h"
 #import "ios/chrome/browser/ui/main/browser_interface_provider.h"
+#import "ios/chrome/browser/web_state_list/web_state_list.h"
 #include "ios/chrome/common/app_group/app_group_metrics_mainapp.h"
 #include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
 #include "ios/public/provider/chrome/browser/distribution/app_distribution_provider.h"
@@ -142,9 +142,10 @@
     [startupInformation
         activateFirstUserActionRecorderWithBackgroundTime:interval];
 
-    Tab* currentTab = interfaceProvider.currentInterface.tabModel.currentTab;
-    if (currentTab.webState &&
-        currentTab.webState->GetLastCommittedURL() == kChromeUINewTabURL) {
+    web::WebState* currentWebState = interfaceProvider.currentInterface.tabModel
+                                         .webStateList->GetActiveWebState();
+    if (currentWebState &&
+        currentWebState->GetLastCommittedURL() == kChromeUINewTabURL) {
       startupInformation.firstUserActionRecorder->RecordStartOnNTP();
       [startupInformation resetFirstUserActionRecorder];
     } else {
diff --git a/ios/chrome/app/application_delegate/metrics_mediator_unittest.mm b/ios/chrome/app/application_delegate/metrics_mediator_unittest.mm
index f2b744f..8f6a2d6 100644
--- a/ios/chrome/app/application_delegate/metrics_mediator_unittest.mm
+++ b/ios/chrome/app/application_delegate/metrics_mediator_unittest.mm
@@ -17,6 +17,8 @@
 #import "ios/chrome/browser/ui/main/browser_interface_provider.h"
 #import "ios/chrome/browser/ui/main/test/stub_browser_interface.h"
 #import "ios/chrome/browser/ui/main/test/stub_browser_interface_provider.h"
+#import "ios/chrome/browser/web_state_list/fake_web_state_list_delegate.h"
+#import "ios/chrome/browser/web_state_list/web_state_list.h"
 #import "ios/chrome/test/base/scoped_block_swizzler.h"
 #import "ios/chrome/test/ocmock/OCMockObject+BreakpadControllerTesting.h"
 #include "net/base/network_change_notifier.h"
@@ -123,12 +125,17 @@
 
 class MetricsMediatorLogLaunchTest : public PlatformTest {
  protected:
-  MetricsMediatorLogLaunchTest() : has_been_called_(FALSE) {}
+  MetricsMediatorLogLaunchTest()
+      : has_been_called_(FALSE),
+        web_state_list_(
+            std::make_unique<WebStateList>(&web_state_list_delegate_)) {}
 
   void initiateMetricsMediator(BOOL coldStart, int tabCount) {
     id mainTabModel = [OCMockObject mockForClass:[TabModel class]];
     [[[mainTabModel stub] andReturnValue:@(tabCount)] count];
-    [[[mainTabModel stub] andReturn:nil] currentTab];
+    WebStateList* web_state_list = web_state_list_.get();
+    [[[mainTabModel stub] andReturnValue:OCMOCK_VALUE(web_state_list)]
+        webStateList];
 
     StubBrowserInterfaceProvider* concreteProvider =
         [[StubBrowserInterfaceProvider alloc] init];
@@ -162,6 +169,8 @@
   __block BOOL has_been_called_;
   logLaunchMetricsBlock swizzle_block_;
   std::unique_ptr<ScopedBlockSwizzler> uma_histogram_swizzler_;
+  std::unique_ptr<WebStateList> web_state_list_;
+  FakeWebStateListDelegate web_state_list_delegate_;
 };
 
 // Verifies that the log of the number of open tabs is sent and verifies
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm
index 2cd1e30..faad283 100644
--- a/ios/chrome/app/main_controller.mm
+++ b/ios/chrome/app/main_controller.mm
@@ -109,7 +109,6 @@
 #import "ios/chrome/browser/snapshots/snapshot_cache_factory.h"
 #import "ios/chrome/browser/snapshots/snapshot_tab_helper.h"
 #include "ios/chrome/browser/system_flags.h"
-#import "ios/chrome/browser/tabs/tab.h"
 #import "ios/chrome/browser/tabs/tab_model.h"
 #import "ios/chrome/browser/ui/authentication/signed_in_accounts_view_controller.h"
 #import "ios/chrome/browser/ui/browser_view/browser_coordinator.h"
@@ -1496,7 +1495,8 @@
 }
 
 - (void)prepareTabSwitcher {
-  web::WebState* currentWebState = self.currentBVC.tabModel.currentTab.webState;
+  web::WebState* currentWebState =
+      self.currentBVC.tabModel.webStateList->GetActiveWebState();
   if (currentWebState) {
     BOOL loading = currentWebState->IsLoading();
     SnapshotTabHelper::FromWebState(currentWebState)
@@ -2171,7 +2171,8 @@
   // Removing browsing data triggers session restore in navigation manager. If
   // there is an in-progress session restore, wait for it to finish before
   // attempting to clear browsing data again.
-  web::WebState* webState = [[[self.currentBVC tabModel] currentTab] webState];
+  web::WebState* webState =
+      self.currentBVC.tabModel.webStateList->GetActiveWebState();
   if (webState && webState->GetNavigationManager()) {
     webState->GetNavigationManager()->AddRestoreCompletionCallback(
         base::BindOnce(^{
@@ -2262,15 +2263,13 @@
          tabOpenedCompletion:(ProceduralBlock)tabOpenedCompletion {
   BrowserViewController* targetBVC =
       targetMode == ApplicationMode::NORMAL ? self.mainBVC : self.otrBVC;
-  TabModel* targetTabModel = targetBVC.tabModel;
-
-  Tab* currentTabInTargetBVC = [targetTabModel currentTab];
+  web::WebState* currentWebState =
+      targetBVC.tabModel.webStateList->GetActiveWebState();
 
   // Don't call loadWithParams for chrome://newtab when it's already loaded.
   // Note that it's safe to use -GetVisibleURL here, as it doesn't matter if the
   // NTP hasn't finished loading.
-  if (currentTabInTargetBVC.webState &&
-      IsURLNtp(currentTabInTargetBVC.webState->GetVisibleURL()) &&
+  if (currentWebState && IsURLNtp(currentWebState->GetVisibleURL()) &&
       IsURLNtp(urlLoadParams.web_params.url)) {
     if (tabOpenedCompletion) {
       tabOpenedCompletion();
@@ -2287,8 +2286,7 @@
   // If the current tab isn't an NTP, open a new tab.  Be sure to use
   // -GetLastCommittedURL incase the NTP is still loading.
   if (alwaysInsertNewTab ||
-      !(currentTabInTargetBVC.webState &&
-        IsURLNtp(currentTabInTargetBVC.webState->GetVisibleURL()))) {
+      !(currentWebState && IsURLNtp(currentWebState->GetVisibleURL()))) {
     [targetBVC appendTabAddedCompletion:tabOpenedCompletion];
     UrlLoadParams newTabParams = urlLoadParams;
     newTabParams.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
@@ -2590,7 +2588,8 @@
 - (NSString*)currentPageDisplayURL {
   if (_tabSwitcherIsActive)
     return nil;
-  web::WebState* webState = [[[self currentTabModel] currentTab] webState];
+  web::WebState* webState =
+      self.currentTabModel.webStateList->GetActiveWebState();
   if (!webState)
     return nil;
   // Returns URL of browser tab that is currently showing.
diff --git a/ios/chrome/browser/autocomplete/autocomplete_provider_client_impl.h b/ios/chrome/browser/autocomplete/autocomplete_provider_client_impl.h
index 541af35..f091c7d 100644
--- a/ios/chrome/browser/autocomplete/autocomplete_provider_client_impl.h
+++ b/ios/chrome/browser/autocomplete/autocomplete_provider_client_impl.h
@@ -75,8 +75,6 @@
       history::KeywordID keyword_id,
       const base::string16& term) override;
   void PrefetchImage(const GURL& url) override;
-  void OnAutocompleteControllerResultReady(
-      AutocompleteController* controller) override;
   bool IsTabOpenWithURL(const GURL& url,
                         const AutocompleteInput* input) override;
 
diff --git a/ios/chrome/browser/autocomplete/autocomplete_provider_client_impl.mm b/ios/chrome/browser/autocomplete/autocomplete_provider_client_impl.mm
index b15dd770..71e5924 100644
--- a/ios/chrome/browser/autocomplete/autocomplete_provider_client_impl.mm
+++ b/ios/chrome/browser/autocomplete/autocomplete_provider_client_impl.mm
@@ -215,11 +215,6 @@
 
 void AutocompleteProviderClientImpl::PrefetchImage(const GURL& url) {}
 
-void AutocompleteProviderClientImpl::OnAutocompleteControllerResultReady(
-    AutocompleteController* controller) {
-  // iOS currently has no client for this event.
-}
-
 bool AutocompleteProviderClientImpl::IsTabOpenWithURL(
     const GURL& url,
     const AutocompleteInput* input) {
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index 0a28e63..d1568b95 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -392,6 +392,10 @@
      flag_descriptions::kClosingLastIncognitoTabName,
      flag_descriptions::kClosingLastIncognitoTabDescription, flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(kClosingLastIncognitoTab)},
+    {"omnibox-on-device-head-suggestions",
+     flag_descriptions::kOmniboxOnDeviceHeadSuggestionsName,
+     flag_descriptions::kOmniboxOnDeviceHeadSuggestionsDescription,
+     flags_ui::kOsIos, FEATURE_VALUE_TYPE(omnibox::kOnDeviceHeadProvider)},
     {"omnibox-ui-max-autocomplete-matches",
      flag_descriptions::kOmniboxUIMaxAutocompleteMatchesName,
      flag_descriptions::kOmniboxUIMaxAutocompleteMatchesDescription,
diff --git a/ios/chrome/browser/infobars/BUILD.gn b/ios/chrome/browser/infobars/BUILD.gn
index 78fd90d..67937969 100644
--- a/ios/chrome/browser/infobars/BUILD.gn
+++ b/ios/chrome/browser/infobars/BUILD.gn
@@ -37,6 +37,8 @@
 source_set("badge") {
   configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
+    "infobar_badge_model.h",
+    "infobar_badge_model.mm",
     "infobar_badge_tab_helper.h",
     "infobar_badge_tab_helper.mm",
     "infobar_badge_tab_helper_delegate.h",
@@ -44,6 +46,7 @@
   deps = [
     ":infobars",
     ":public",
+    "//ios/chrome/browser/ui/badges:public",
     "//ios/chrome/browser/ui/infobars:feature_flags",
     "//ios/chrome/browser/ui/infobars:infobars_ui",
     "//ios/web",
diff --git a/ios/chrome/browser/infobars/infobar_badge_model.h b/ios/chrome/browser/infobars/infobar_badge_model.h
new file mode 100644
index 0000000..8e26d74b
--- /dev/null
+++ b/ios/chrome/browser/infobars/infobar_badge_model.h
@@ -0,0 +1,22 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_INFOBARS_INFOBAR_BADGE_MODEL_H_
+#define IOS_CHROME_BROWSER_INFOBARS_INFOBAR_BADGE_MODEL_H_
+
+#import <UIKit/UIKit.h>
+
+#import "ios/chrome/browser/infobars/infobar_type.h"
+#import "ios/chrome/browser/ui/badges/badge_item.h"
+
+// A model object that represents a badge for an Infobar.
+@interface InfobarBadgeModel : NSObject <BadgeItem>
+
+- (instancetype)initWithInfobarType:(InfobarType)type
+                           accepted:(BOOL)accepted NS_DESIGNATED_INITIALIZER;
+- (instancetype)init NS_UNAVAILABLE;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_INFOBARS_INFOBAR_BADGE_MODEL_H_
diff --git a/ios/chrome/browser/infobars/infobar_badge_model.mm b/ios/chrome/browser/infobars/infobar_badge_model.mm
new file mode 100644
index 0000000..27543c6
--- /dev/null
+++ b/ios/chrome/browser/infobars/infobar_badge_model.mm
@@ -0,0 +1,47 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/infobars/infobar_badge_model.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface InfobarBadgeModel ()
+
+// The type of Infobar associated with this badge.
+@property(nonatomic, assign) InfobarType infobarType;
+
+@end
+
+@implementation InfobarBadgeModel
+// Synthesized from protocol.
+@synthesize tappable = _tappable;
+// Synthesized from protocol.
+@synthesize accepted = _accepted;
+
+- (instancetype)initWithInfobarType:(InfobarType)type accepted:(BOOL)accepted {
+  self = [super init];
+  if (self) {
+    _tappable = YES;
+    _infobarType = type;
+    _accepted = accepted;
+  }
+  return self;
+}
+
+#pragma mark - BadgeViewModel
+
+- (BadgeType)badgeType {
+  switch (self.infobarType) {
+    case InfobarType::kInfobarTypeConfirm:
+      return BadgeType::kBadgeTypeConfirm;
+    case InfobarType::kInfobarTypePasswordSave:
+      return BadgeType::kBadgeTypePasswordSave;
+    case InfobarType::kInfobarTypePasswordUpdate:
+      return BadgeType::kBadgeTypePasswordUpdate;
+  }
+}
+
+@end
diff --git a/ios/chrome/browser/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/ios_chrome_flag_descriptions.cc
index 260c52c..526c6f8 100644
--- a/ios/chrome/browser/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/ios_chrome_flag_descriptions.cc
@@ -336,6 +336,12 @@
 const char kOmniboxUseDefaultSearchEngineFaviconDescription[] =
     "Shows default search engine favicon in the omnibox";
 
+const char kOmniboxOnDeviceHeadSuggestionsName[] =
+    "Omnibox on device head suggestions";
+const char kOmniboxOnDeviceHeadSuggestionsDescription[] =
+    "Shows Google head non personalized search suggestions provided by a "
+    "compact on device model";
+
 const char kOnlyNewPasswordFormParsingName[] =
     "Use only new password form parsing";
 const char kOnlyNewPasswordFormParsingDescription[] =
diff --git a/ios/chrome/browser/ios_chrome_flag_descriptions.h b/ios/chrome/browser/ios_chrome_flag_descriptions.h
index b3ecf83..b675513 100644
--- a/ios/chrome/browser/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/ios_chrome_flag_descriptions.h
@@ -281,6 +281,11 @@
 extern const char kOmniboxUseDefaultSearchEngineFaviconName[];
 extern const char kOmniboxUseDefaultSearchEngineFaviconDescription[];
 
+// Title and description for the flag to enable Omnibox On Device Head
+// suggestions.
+extern const char kOmniboxOnDeviceHeadSuggestionsName[];
+extern const char kOmniboxOnDeviceHeadSuggestionsDescription[];
+
 // Title and description for the flag to enable using only new password form
 // parsing.
 extern const char kOnlyNewPasswordFormParsingName[];
diff --git a/ios/chrome/browser/metrics/new_tab_page_uma.mm b/ios/chrome/browser/metrics/new_tab_page_uma.mm
index ff0121ba..0f24afb 100644
--- a/ios/chrome/browser/metrics/new_tab_page_uma.mm
+++ b/ios/chrome/browser/metrics/new_tab_page_uma.mm
@@ -8,9 +8,9 @@
 #include "components/google/core/common/google_util.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/chrome_url_constants.h"
-#import "ios/chrome/browser/tabs/tab.h"
 #import "ios/chrome/browser/tabs/tab_model.h"
 #import "ios/chrome/browser/tabs/tab_model_list.h"
+#import "ios/chrome/browser/web_state_list/web_state_list.h"
 #import "ios/web/public/web_state/web_state.h"
 #include "url/gurl.h"
 
@@ -21,10 +21,12 @@
 namespace new_tab_page_uma {
 
 bool IsCurrentlyOnNTP(ios::ChromeBrowserState* browser_state) {
-  TabModel* tab_model =
-      TabModelList::GetLastActiveTabModelForChromeBrowserState(browser_state);
-  return tab_model.currentTab.webState &&
-         tab_model.currentTab.webState->GetVisibleURL() == kChromeUINewTabURL;
+  WebStateList* webStateList =
+      TabModelList::GetLastActiveTabModelForChromeBrowserState(browser_state)
+          .webStateList;
+  return webStateList->GetActiveWebState() &&
+         webStateList->GetActiveWebState()->GetVisibleURL() ==
+             kChromeUINewTabURL;
 }
 
 void RecordAction(ios::ChromeBrowserState* browserState, ActionType type) {
diff --git a/ios/chrome/browser/send_tab_to_self/ios_send_tab_to_self_infobar_delegate.cc b/ios/chrome/browser/send_tab_to_self/ios_send_tab_to_self_infobar_delegate.cc
index 744f4e37..6bf918b3b 100644
--- a/ios/chrome/browser/send_tab_to_self/ios_send_tab_to_self_infobar_delegate.cc
+++ b/ios/chrome/browser/send_tab_to_self/ios_send_tab_to_self_infobar_delegate.cc
@@ -10,6 +10,7 @@
 #include "components/infobars/core/infobar.h"
 #include "components/send_tab_to_self/send_tab_to_self_entry.h"
 #include "components/send_tab_to_self/send_tab_to_self_metrics.h"
+#include "components/send_tab_to_self/send_tab_to_self_model.h"
 #include "ios/chrome/grit/ios_theme_resources.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/window_open_disposition.h"
@@ -19,16 +20,19 @@
 
 // static
 std::unique_ptr<IOSSendTabToSelfInfoBarDelegate>
-IOSSendTabToSelfInfoBarDelegate::Create(const SendTabToSelfEntry* entry) {
-  return base::WrapUnique(new IOSSendTabToSelfInfoBarDelegate(entry));
+IOSSendTabToSelfInfoBarDelegate::Create(const SendTabToSelfEntry* entry,
+                                        SendTabToSelfModel* model) {
+  return std::make_unique<IOSSendTabToSelfInfoBarDelegate>(entry, model);
 }
 
 IOSSendTabToSelfInfoBarDelegate::~IOSSendTabToSelfInfoBarDelegate() {}
 
 IOSSendTabToSelfInfoBarDelegate::IOSSendTabToSelfInfoBarDelegate(
-    const SendTabToSelfEntry* entry) {
+    const SendTabToSelfEntry* entry,
+    SendTabToSelfModel* model)
+    : entry_(entry), model_(model) {
   DCHECK(entry);
-  entry_ = entry;
+  DCHECK(model);
   RecordNotificationHistogram(SendTabToSelfNotification::kShown);
 }
 
@@ -59,6 +63,7 @@
 }
 
 bool IOSSendTabToSelfInfoBarDelegate::Accept() {
+  model_->MarkEntryOpened(entry_->GetGUID());
   RecordNotificationHistogram(SendTabToSelfNotification::kOpened);
   infobar()->owner()->OpenURL(entry_->GetURL(),
                               WindowOpenDisposition::CURRENT_TAB);
@@ -66,6 +71,7 @@
 }
 
 bool IOSSendTabToSelfInfoBarDelegate::Cancel() {
+  model_->DismissEntry(entry_->GetGUID());
   RecordNotificationHistogram(SendTabToSelfNotification::kDismissed);
   return true;
 }
diff --git a/ios/chrome/browser/send_tab_to_self/ios_send_tab_to_self_infobar_delegate.h b/ios/chrome/browser/send_tab_to_self/ios_send_tab_to_self_infobar_delegate.h
index f4a32b8..d4d3045 100644
--- a/ios/chrome/browser/send_tab_to_self/ios_send_tab_to_self_infobar_delegate.h
+++ b/ios/chrome/browser/send_tab_to_self/ios_send_tab_to_self_infobar_delegate.h
@@ -15,16 +15,19 @@
 namespace send_tab_to_self {
 
 class SendTabToSelfEntry;
+class SendTabToSelfModel;
 
 class IOSSendTabToSelfInfoBarDelegate : public ConfirmInfoBarDelegate {
  public:
   static std::unique_ptr<IOSSendTabToSelfInfoBarDelegate> Create(
-      const SendTabToSelfEntry* entry);
+      const SendTabToSelfEntry* entry,
+      SendTabToSelfModel* model);
 
+  explicit IOSSendTabToSelfInfoBarDelegate(const SendTabToSelfEntry* entry,
+                                           SendTabToSelfModel* model);
   ~IOSSendTabToSelfInfoBarDelegate() override;
 
  private:
-  IOSSendTabToSelfInfoBarDelegate(const SendTabToSelfEntry* entry);
 
   // ConfirmInfoBarDelegate:
   infobars::InfoBarDelegate::InfoBarIdentifier GetIdentifier() const override;
@@ -39,6 +42,9 @@
   // The entry that was share to this device. Must outlive this instance.
   const SendTabToSelfEntry* entry_ = nullptr;
 
+  // The SendTabToSelfModel that holds the |entry_|. Must outlive this instance.
+  SendTabToSelfModel* model_ = nullptr;
+
   DISALLOW_COPY_AND_ASSIGN(IOSSendTabToSelfInfoBarDelegate);
 };
 
diff --git a/ios/chrome/browser/send_tab_to_self/send_tab_to_self_client_service_ios.h b/ios/chrome/browser/send_tab_to_self/send_tab_to_self_client_service_ios.h
index ba386076..1a59d5d 100644
--- a/ios/chrome/browser/send_tab_to_self/send_tab_to_self_client_service_ios.h
+++ b/ios/chrome/browser/send_tab_to_self/send_tab_to_self_client_service_ios.h
@@ -10,7 +10,6 @@
 
 #include "base/macros.h"
 #include "components/keyed_service/core/keyed_service.h"
-#include "components/send_tab_to_self/send_tab_to_self_model.h"
 #include "components/send_tab_to_self/send_tab_to_self_model_observer.h"
 #import "ios/chrome/browser/web_state_list/web_state_list_observer.h"
 #import "ios/web/public/web_state/web_state_observer.h"
diff --git a/ios/chrome/browser/send_tab_to_self/send_tab_to_self_client_service_ios.mm b/ios/chrome/browser/send_tab_to_self/send_tab_to_self_client_service_ios.mm
index c30858f..4505d5b59 100644
--- a/ios/chrome/browser/send_tab_to_self/send_tab_to_self_client_service_ios.mm
+++ b/ios/chrome/browser/send_tab_to_self/send_tab_to_self_client_service_ios.mm
@@ -13,6 +13,7 @@
 #include "base/logging.h"
 #include "components/infobars/core/infobar.h"
 #include "components/infobars/core/infobar_manager.h"
+#include "components/send_tab_to_self/send_tab_to_self_model.h"
 #include "components/sync/driver/sync_driver_switches.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/infobars/infobar.h"
@@ -166,8 +167,8 @@
     return;
   }
 
-  infobar_manager->AddInfoBar(
-      CreateConfirmInfoBar(IOSSendTabToSelfInfoBarDelegate::Create(entry)));
+  infobar_manager->AddInfoBar(CreateConfirmInfoBar(
+      IOSSendTabToSelfInfoBarDelegate::Create(entry, model_)));
 }
 
 void SendTabToSelfClientServiceIOS::CleanUpObserversAndVariables() {
diff --git a/ios/chrome/browser/signin/device_accounts_provider_impl.h b/ios/chrome/browser/signin/device_accounts_provider_impl.h
index e270a2c0..ce89364 100644
--- a/ios/chrome/browser/signin/device_accounts_provider_impl.h
+++ b/ios/chrome/browser/signin/device_accounts_provider_impl.h
@@ -21,7 +21,7 @@
   void GetAccessToken(const std::string& gaia_id,
                       const std::string& client_id,
                       const std::set<std::string>& scopes,
-                      const AccessTokenCallback& callback) override;
+                      AccessTokenCallback callback) override;
   std::vector<AccountInfo> GetAllAccounts() const override;
   AuthenticationErrorCategory GetAuthenticationErrorCategory(
       const std::string& gaia_id,
diff --git a/ios/chrome/browser/signin/device_accounts_provider_impl.mm b/ios/chrome/browser/signin/device_accounts_provider_impl.mm
index 958072a..e16dfda 100644
--- a/ios/chrome/browser/signin/device_accounts_provider_impl.mm
+++ b/ios/chrome/browser/signin/device_accounts_provider_impl.mm
@@ -38,15 +38,20 @@
     const std::string& gaia_id,
     const std::string& client_id,
     const std::set<std::string>& scopes,
-    const AccessTokenCallback& callback) {
-  AccessTokenCallback scoped_callback = callback;
+    AccessTokenCallback callback) {
+  DCHECK(!callback.is_null());
   ios::ChromeIdentityService* identity_service =
       ios::GetChromeBrowserProvider()->GetChromeIdentityService();
+
+  // AccessTokenCallback is non-copyable. Using __block allocates the memory
+  // directly in the block object at compilation time (instead of doing a
+  // copy). This is required to have correct interaction between move-only
+  // types and Objective-C blocks.
+  __block AccessTokenCallback scopedCallback = std::move(callback);
   identity_service->GetAccessToken(
       identity_service->GetIdentityWithGaiaID(gaia_id), client_id, scopes,
       ^(NSString* token, NSDate* expiration, NSError* error) {
-        if (!scoped_callback.is_null())
-          scoped_callback.Run(token, expiration, error);
+        std::move(scopedCallback).Run(token, expiration, error);
       });
 }
 
diff --git a/ios/chrome/browser/tabs/tab_model.mm b/ios/chrome/browser/tabs/tab_model.mm
index 2d168a2..37ed359 100644
--- a/ios/chrome/browser/tabs/tab_model.mm
+++ b/ios/chrome/browser/tabs/tab_model.mm
@@ -306,6 +306,7 @@
 }
 
 - (WebStateList*)webStateList {
+  DCHECK(_webStateList);
   return _webStateList.get();
 }
 
diff --git a/ios/chrome/browser/translate/translate_egtest.mm b/ios/chrome/browser/translate/translate_egtest.mm
index 5e3c091..e5ac48a 100644
--- a/ios/chrome/browser/translate/translate_egtest.mm
+++ b/ios/chrome/browser/translate/translate_egtest.mm
@@ -1102,15 +1102,8 @@
       performAction:grey_tap()];
 
   // Make sure the "Always Translate French" entry is now selected and tap it.
-  id<GREYMatcher> selectedMatcher = ElementIsSelected(YES);
-  if (@available(iOS 13, *)) {
-    // TODO(crbug.com/979079): "Always Translate French" is actually invisible
-    // due to a real bug in the options menu. Remove the line below when the bug
-    // is fixed.
-    selectedMatcher = grey_accessibilityTrait(UIAccessibilityTraitSelected);
-  }
   [[[EarlGrey selectElementWithMatcher:AlwaysTranslate(@"French")]
-      assertWithMatcher:selectedMatcher] performAction:grey_tap()];
+      assertWithMatcher:ElementIsSelected(YES)] performAction:grey_tap()];
 
   // Make sure that French to English translation is no longer whitelisted.
   GREYAssert(!translatePrefs->IsLanguagePairWhitelisted("fr", "en"),
diff --git a/ios/chrome/browser/ui/activity_services/BUILD.gn b/ios/chrome/browser/ui/activity_services/BUILD.gn
index cd31a20..4009ba1a 100644
--- a/ios/chrome/browser/ui/activity_services/BUILD.gn
+++ b/ios/chrome/browser/ui/activity_services/BUILD.gn
@@ -73,6 +73,7 @@
     "//ios/chrome/browser/ui/activity_services/requirements",
     "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
+    "//ios/chrome/browser/web_state_list",
     "//url",
   ]
 }
diff --git a/ios/chrome/browser/ui/activity_services/activity_service_legacy_coordinator.mm b/ios/chrome/browser/ui/activity_services/activity_service_legacy_coordinator.mm
index 35bd049a..35fa6ee3 100644
--- a/ios/chrome/browser/ui/activity_services/activity_service_legacy_coordinator.mm
+++ b/ios/chrome/browser/ui/activity_services/activity_service_legacy_coordinator.mm
@@ -8,7 +8,6 @@
 #include "base/time/time.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #import "ios/chrome/browser/passwords/password_tab_helper.h"
-#import "ios/chrome/browser/tabs/tab.h"
 #import "ios/chrome/browser/tabs/tab_model.h"
 #import "ios/chrome/browser/ui/activity_services/activity_service_controller.h"
 #import "ios/chrome/browser/ui/activity_services/canonical_url_retriever.h"
@@ -20,6 +19,7 @@
 #import "ios/chrome/browser/ui/activity_services/share_to_data_builder.h"
 #import "ios/chrome/browser/ui/commands/activity_service_commands.h"
 #import "ios/chrome/browser/ui/commands/command_dispatcher.h"
+#import "ios/chrome/browser/web_state_list/web_state_list.h"
 #include "url/gurl.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -86,7 +86,7 @@
   self.sharePageStartTime = base::TimeTicks::Now();
   __weak ActivityServiceLegacyCoordinator* weakSelf = self;
   activity_services::RetrieveCanonicalUrl(
-      self.tabModel.currentTab.webState, ^(const GURL& url) {
+      self.tabModel.webStateList->GetActiveWebState(), ^(const GURL& url) {
         [weakSelf sharePageWithCanonicalURL:url];
       });
 }
@@ -94,7 +94,7 @@
 #pragma mark - Providers
 
 - (id<PasswordFormFiller>)currentPasswordFormFiller {
-  web::WebState* webState = self.tabModel.currentTab.webState;
+  web::WebState* webState = self.tabModel.webStateList->GetActiveWebState();
   return webState ? PasswordTabHelper::FromWebState(webState)
                         ->GetPasswordFormFiller()
                   : nil;
diff --git a/ios/chrome/browser/ui/badges/BUILD.gn b/ios/chrome/browser/ui/badges/BUILD.gn
new file mode 100644
index 0000000..18e1d67
--- /dev/null
+++ b/ios/chrome/browser/ui/badges/BUILD.gn
@@ -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.
+
+source_set("public") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  sources = [
+    "badge_item.h",
+    "badge_type.h",
+  ]
+}
diff --git a/ios/chrome/browser/ui/badges/badge_item.h b/ios/chrome/browser/ui/badges/badge_item.h
new file mode 100644
index 0000000..4c44e420
--- /dev/null
+++ b/ios/chrome/browser/ui/badges/badge_item.h
@@ -0,0 +1,24 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_BADGES_BADGE_ITEM_H_
+#define IOS_CHROME_BROWSER_UI_BADGES_BADGE_ITEM_H_
+
+#import <UIKit/UIKit.h>
+
+#import "ios/chrome/browser/ui/badges/badge_type.h"
+
+// Holds properties and values the UI needs to configure a badge button.
+@protocol BadgeItem
+
+// The type of the badge.
+- (BadgeType)badgeType;
+// Some badges may not be tappable if there is no action associated with it.
+@property(nonatomic, assign, readonly, getter=isTappable) BOOL tappable;
+// Whether this badge is in an accepted state.
+@property(nonatomic, assign, readonly, getter=isAccepted) BOOL accepted;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_BADGES_BADGE_ITEM_H_
diff --git a/ios/chrome/browser/ui/badges/badge_type.h b/ios/chrome/browser/ui/badges/badge_type.h
new file mode 100644
index 0000000..c36b97e
--- /dev/null
+++ b/ios/chrome/browser/ui/badges/badge_type.h
@@ -0,0 +1,18 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_BADGES_BADGE_TYPE_H_
+#define IOS_CHROME_BROWSER_UI_BADGES_BADGE_TYPE_H_
+
+// Badge types.
+enum class BadgeType {
+  // Badge type for the confirm Infobar.
+  kBadgeTypeConfirm = 0,
+  // Badge type for the Save Passwords Infobar.
+  kBadgeTypePasswordSave = 1,
+  // Badge type for the Update Passwords Infobar.
+  kBadgeTypePasswordUpdate = 2,
+};
+
+#endif  // IOS_CHROME_BROWSER_UI_BADGES_BADGE_TYPE_H_
diff --git a/ios/chrome/browser/ui/bookmarks/bookmarks_egtest.mm b/ios/chrome/browser/ui/bookmarks/bookmarks_egtest.mm
index 4aaaadf..e55abfd 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmarks_egtest.mm
+++ b/ios/chrome/browser/ui/bookmarks/bookmarks_egtest.mm
@@ -2016,12 +2016,6 @@
 
 // Verify Copy URL functionality on single URL selection.
 - (void)testCopyFunctionalityOnSingleURL {
-  // TODO(crbug.com/979402): Disabled on iOS13 since copy/paste is not working
-  // in the Simulator.
-  if (@available(iOS 13.0, *)) {
-    EARL_GREY_TEST_DISABLED(@"Disabled on iOS 13");
-  }
-
   [BookmarksTestCase setupStandardBookmarks];
   [BookmarksTestCase openBookmarks];
   [BookmarksTestCase openMobileBookmarks];
diff --git a/ios/chrome/browser/ui/first_run/BUILD.gn b/ios/chrome/browser/ui/first_run/BUILD.gn
index e91f47e..791019e 100644
--- a/ios/chrome/browser/ui/first_run/BUILD.gn
+++ b/ios/chrome/browser/ui/first_run/BUILD.gn
@@ -46,6 +46,7 @@
     "//ios/chrome/browser/ui/settings/utils",
     "//ios/chrome/browser/ui/util",
     "//ios/chrome/browser/ui/util:terms_util",
+    "//ios/chrome/browser/web_state_list",
     "//ios/chrome/common",
     "//ios/public/provider/chrome/browser",
     "//ios/public/provider/chrome/browser/signin",
diff --git a/ios/chrome/browser/ui/first_run/first_run_chrome_signin_view_controller.mm b/ios/chrome/browser/ui/first_run/first_run_chrome_signin_view_controller.mm
index 266014eb..87f0f6b 100644
--- a/ios/chrome/browser/ui/first_run/first_run_chrome_signin_view_controller.mm
+++ b/ios/chrome/browser/ui/first_run/first_run_chrome_signin_view_controller.mm
@@ -16,11 +16,14 @@
 #import "ios/chrome/browser/ui/first_run/first_run_util.h"
 #import "ios/chrome/browser/ui/promos/signin_promo_view_controller.h"
 #include "ios/chrome/browser/ui/util/ui_util.h"
+#import "ios/chrome/browser/web_state_list/web_state_list.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "ios/public/provider/chrome/browser/chrome_browser_provider.h"
 #import "ios/public/provider/chrome/browser/signin/chrome_identity.h"
 #import "ios/public/provider/chrome/browser/signin/chrome_identity_service.h"
 #import "ios/third_party/material_components_ios/src/components/Palettes/src/MaterialPalettes.h"
+#import "ios/web/public/web_state/web_state.h"
+
 #import "ui/base/l10n/l10n_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -109,7 +112,8 @@
 
 - (void)finishFirstRunAndDismissWithCompletion:(ProceduralBlock)completion {
   DCHECK(self.presentingViewController);
-  FinishFirstRun(self.browserState, [_tabModel currentTab], _firstRunConfig,
+  web::WebState* currentWebState = _tabModel.webStateList->GetActiveWebState();
+  FinishFirstRun(self.browserState, currentWebState, _firstRunConfig,
                  self.presenter);
   [self.presentingViewController dismissViewControllerAnimated:YES
                                                     completion:^{
diff --git a/ios/chrome/browser/ui/first_run/first_run_util.h b/ios/chrome/browser/ui/first_run/first_run_util.h
index 2f5b378..61ef002 100644
--- a/ios/chrome/browser/ui/first_run/first_run_util.h
+++ b/ios/chrome/browser/ui/first_run/first_run_util.h
@@ -18,6 +18,9 @@
 namespace ios {
 class ChromeBrowserState;
 }
+namespace web {
+class WebState;
+}
 
 // Notification sent when the first run ends, right before dimissing the Terms
 // of Service modal view.
@@ -45,7 +48,7 @@
 
 // Methods for writing sentinel and recording metrics and posting notifications
 void FinishFirstRun(ios::ChromeBrowserState* browserState,
-                    Tab* tab,
+                    web::WebState* web_state,
                     FirstRunConfiguration* config,
                     id<SyncPresenter> presenter);
 
diff --git a/ios/chrome/browser/ui/first_run/first_run_util.mm b/ios/chrome/browser/ui/first_run/first_run_util.mm
index 9f75f0a..06b053e5 100644
--- a/ios/chrome/browser/ui/first_run/first_run_util.mm
+++ b/ios/chrome/browser/ui/first_run/first_run_util.mm
@@ -17,12 +17,12 @@
 #import "ios/chrome/browser/first_run/first_run_configuration.h"
 #include "ios/chrome/browser/first_run/first_run_metrics.h"
 #include "ios/chrome/browser/signin/identity_manager_factory.h"
-#include "ios/chrome/browser/tabs/tab.h"
 #include "ios/chrome/browser/ui/first_run/first_run_histograms.h"
 #import "ios/chrome/browser/ui/settings/sync/utils/sync_util.h"
 #import "ios/chrome/browser/ui/settings/utils/settings_utils.h"
 #include "ios/chrome/browser/ui/util/ui_util.h"
 #include "ios/web/public/thread/web_thread.h"
+#import "ios/web/public/web_state/web_state.h"
 #include "services/identity/public/cpp/identity_manager.h"
 #import "ui/gfx/ios/NSString+CrStringDrawing.h"
 
@@ -153,7 +153,7 @@
 }
 
 void FinishFirstRun(ios::ChromeBrowserState* browserState,
-                    Tab* tab,
+                    web::WebState* web_state,
                     FirstRunConfiguration* config,
                     id<SyncPresenter> presenter) {
   [[NSNotificationCenter defaultCenter]
@@ -163,7 +163,7 @@
                                         config.hasSSOAccount);
 
   // Display the sync errors infobar.
-  DisplaySyncErrors(browserState, tab.webState, presenter);
+  DisplaySyncErrors(browserState, web_state, presenter);
 }
 
 void RecordProductTourTimingMetrics(NSString* timer_name,
diff --git a/ios/chrome/browser/ui/history/history_ui_egtest.mm b/ios/chrome/browser/ui/history/history_ui_egtest.mm
index 25d47c9..1d5e421 100644
--- a/ios/chrome/browser/ui/history/history_ui_egtest.mm
+++ b/ios/chrome/browser/ui/history/history_ui_egtest.mm
@@ -379,12 +379,6 @@
 // Tests display and selection of 'Copy URL' in a context menu on a history
 // entry.
 - (void)testContextMenuCopy {
-  // TODO(crbug.com/979728): Disabled on iOS13 since copy/paste is not working
-  // in the Simulator.
-  if (@available(iOS 13.0, *)) {
-    EARL_GREY_TEST_DISABLED(@"Disabled on iOS 13");
-  }
-
   ProceduralBlock clearPasteboard = ^{
     [[UIPasteboard generalPasteboard] setURLs:nil];
   };
diff --git a/ios/chrome/browser/ui/page_info/page_info_legacy_coordinator.mm b/ios/chrome/browser/ui/page_info/page_info_legacy_coordinator.mm
index 146d1a06..96b603e 100644
--- a/ios/chrome/browser/ui/page_info/page_info_legacy_coordinator.mm
+++ b/ios/chrome/browser/ui/page_info/page_info_legacy_coordinator.mm
@@ -13,7 +13,6 @@
 #include "ios/chrome/browser/reading_list/features.h"
 #import "ios/chrome/browser/reading_list/offline_page_tab_helper.h"
 #include "ios/chrome/browser/reading_list/offline_url_utils.h"
-#import "ios/chrome/browser/tabs/tab.h"
 #import "ios/chrome/browser/tabs/tab_model.h"
 #import "ios/chrome/browser/ui/commands/command_dispatcher.h"
 #import "ios/chrome/browser/ui/commands/open_new_tab_command.h"
@@ -165,7 +164,7 @@
 #pragma mark - PageInfoReloading
 
 - (void)reload {
-  web::WebState* webState = self.tabModel.currentTab.webState;
+  web::WebState* webState = self.tabModel.webStateList->GetActiveWebState();
   if (webState) {
     // |check_for_repost| is true because the reload is explicitly initiated
     // by the user.
diff --git a/ios/chrome/browser/ui/payments/payment_request_journey_logger_egtest.mm b/ios/chrome/browser/ui/payments/payment_request_journey_logger_egtest.mm
index 8007042..1bc73668 100644
--- a/ios/chrome/browser/ui/payments/payment_request_journey_logger_egtest.mm
+++ b/ios/chrome/browser/ui/payments/payment_request_journey_logger_egtest.mm
@@ -750,7 +750,8 @@
   GREYAssertEqual(JourneyLogger::EVENT_USER_ABORTED |
                       JourneyLogger::EVENT_CAN_MAKE_PAYMENT_FALSE |
                       JourneyLogger::EVENT_REQUEST_METHOD_OTHER |
-                      JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD,
+                      JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD |
+                      JourneyLogger::EVENT_NEEDS_COMPLETION_PAYMENT,
                   buckets[0].min, @"");
 
   // Make sure that the metrics that required the Payment Request to be shown
diff --git a/ios/chrome/browser/ui/settings/cells/settings_switch_cell.mm b/ios/chrome/browser/ui/settings/cells/settings_switch_cell.mm
index 197aac2..d467398 100644
--- a/ios/chrome/browser/ui/settings/cells/settings_switch_cell.mm
+++ b/ios/chrome/browser/ui/settings/cells/settings_switch_cell.mm
@@ -253,4 +253,12 @@
   }
 }
 
+- (UIAccessibilityTraits)accessibilityTraits {
+  UIAccessibilityTraits accessibilityTraits = super.accessibilityTraits;
+  if (!self.switchView.isEnabled) {
+    accessibilityTraits |= UIAccessibilityTraitNotEnabled;
+  }
+  return accessibilityTraits;
+}
+
 @end
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_image_item.mm b/ios/chrome/browser/ui/table_view/cells/table_view_image_item.mm
index da6b41c..cb9a5d6 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_image_item.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_image_item.mm
@@ -192,4 +192,12 @@
   return self.textLabel.text;
 }
 
+- (UIAccessibilityTraits)accessibilityTraits {
+  UIAccessibilityTraits accessibilityTraits = super.accessibilityTraits;
+  if (!self.isUserInteractionEnabled) {
+    accessibilityTraits |= UIAccessibilityTraitNotEnabled;
+  }
+  return accessibilityTraits;
+}
+
 @end
diff --git a/ios/chrome/browser/ui/translate/cells/translate_popup_menu_item.h b/ios/chrome/browser/ui/translate/cells/translate_popup_menu_item.h
index a8e1825..9fd3dac7 100644
--- a/ios/chrome/browser/ui/translate/cells/translate_popup_menu_item.h
+++ b/ios/chrome/browser/ui/translate/cells/translate_popup_menu_item.h
@@ -26,6 +26,9 @@
 
 - (void)setTitle:(NSString*)title;
 
+// Whether the cell will display a trailing checkmark or not.
+- (void)setCheckmark:(BOOL)checkmark;
+
 // After this is called, the cell is listening for the
 // UIContentSizeCategoryDidChangeNotification notification and updates its font
 // size to the new category.
diff --git a/ios/chrome/browser/ui/translate/cells/translate_popup_menu_item.mm b/ios/chrome/browser/ui/translate/cells/translate_popup_menu_item.mm
index 1535287..b083def 100644
--- a/ios/chrome/browser/ui/translate/cells/translate_popup_menu_item.mm
+++ b/ios/chrome/browser/ui/translate/cells/translate_popup_menu_item.mm
@@ -38,7 +38,7 @@
            withStyler:(ChromeTableViewStyler*)styler {
   [super configureCell:cell withStyler:styler];
   cell.accessibilityTraits = UIAccessibilityTraitButton;
-  cell.selected = self.isSelected;
+  [cell setCheckmark:self.selected];
   [cell setTitle:self.title];
 }
 
@@ -136,10 +136,8 @@
   self.titleLabel.text = title;
 }
 
-- (void)setSelected:(BOOL)selected {
-  [super setSelected:selected];
-
-  if (selected) {
+- (void)setCheckmark:(BOOL)checkmark {
+  if (checkmark) {
     self.checkmarkView.hidden = NO;
     self.accessibilityTraits |= UIAccessibilityTraitSelected;
   }
diff --git a/ios/chrome/browser/ui/util/CRUILabel+AttributeUtils.h b/ios/chrome/browser/ui/util/CRUILabel+AttributeUtils.h
index 97f4b9b..2e1a95d 100644
--- a/ios/chrome/browser/ui/util/CRUILabel+AttributeUtils.h
+++ b/ios/chrome/browser/ui/util/CRUILabel+AttributeUtils.h
@@ -8,9 +8,16 @@
 #import <UIKit/UIKit.h>
 
 @interface UILabel (CRUILabelAttributeUtils)
-// The height of the label.
+// The line height for the text in the receiver.
 // Make sure to create a LabelObserver for this label and start observing before
-// setting this property.
+// setting this property. When the last LabelObserver is removed for a label
+// where this property is set, there is no expected behavior for the line
+// height on further text changes -- it may be retained or overwritten.
+//
+// TODO(crbug.com/980510) : When iOS12 support is removed, determine if this
+//   property is still needed, or if (under iOS13+) setting the line height
+//   in a paragraph style persits across label frame changes.
+//
 @property(nonatomic, assign, setter=cr_setLineHeight:) CGFloat cr_lineHeight;
 
 @end
diff --git a/ios/chrome/browser/ui/util/CRUILabel+AttributeUtils_unittest.mm b/ios/chrome/browser/ui/util/CRUILabel+AttributeUtils_unittest.mm
index f6edb9b6..0d436bf 100644
--- a/ios/chrome/browser/ui/util/CRUILabel+AttributeUtils_unittest.mm
+++ b/ios/chrome/browser/ui/util/CRUILabel+AttributeUtils_unittest.mm
@@ -45,6 +45,15 @@
   UILabel* label = _scopedLabel;
   label.text = @"sample text";
 
+  NSMutableAttributedString* textWithLineHeight =
+      [[NSMutableAttributedString alloc]
+          initWithString:@"attributed sample text"];
+  NSMutableParagraphStyle* style = [[NSMutableParagraphStyle alloc] init];
+  style.maximumLineHeight = 15.0;
+  [textWithLineHeight addAttribute:NSParagraphStyleAttributeName
+                             value:style
+                             range:NSMakeRange(0, [textWithLineHeight length])];
+
   // Add a second observer.
   LabelObserver* secondObserver = [LabelObserver observerForLabel:label];
   [secondObserver startObserving];
@@ -62,8 +71,8 @@
   // Once both are stopped, the height isn't persisted when the text changes.
   [_observer stopObserving];
   label.cr_lineHeight = 25.0;
-  label.text = @"longer sample text";
-  CheckLabelLineHeight(0);
+  label.attributedText = textWithLineHeight;
+  CheckLabelLineHeight(15.0);
 }
 
 TEST_F(UILabelAttributeUtilsTest, SettingTests) {
@@ -80,18 +89,19 @@
 
   NSMutableAttributedString* string = [[NSMutableAttributedString alloc]
       initWithString:@"attributed sample text"];
-  label.attributedText = string;
+  label.attributedText = [string copy];
   CheckLabelLineHeight(20.0);
   [string addAttribute:NSForegroundColorAttributeName
                  value:[UIColor brownColor]
                  range:NSMakeRange(0, [string length])];
-  label.attributedText = string;
+  label.attributedText = [string copy];
   CheckLabelLineHeight(20.0);
   NSMutableParagraphStyle* style = [[NSMutableParagraphStyle alloc] init];
   style.maximumLineHeight = 15.0;
   [string addAttribute:NSParagraphStyleAttributeName
                  value:style
                  range:NSMakeRange(0, [string length])];
+  label.attributedText = [string copy];
   CheckLabelLineHeight(20.0);
 }
 
diff --git a/ios/chrome/browser/ui/util/label_observer.h b/ios/chrome/browser/ui/util/label_observer.h
index 198c620..23ec5a1 100644
--- a/ios/chrome/browser/ui/util/label_observer.h
+++ b/ios/chrome/browser/ui/util/label_observer.h
@@ -27,8 +27,10 @@
 // should be called before |label| is deallocated.
 - (void)startObserving;
 
-// Stops observing the label. The label stop being observed once the number of
-// call to this function match the number of call to |-startObserving|.
+// Stops observing the label. The label stops being observed once the number of
+// call to this function match the number of call to |-startObserving|. When
+// observation of a label ends, none of its attributes are changed, and none
+// of the registered actions are called.
 - (void)stopObserving;
 
 // Block type that takes a label.  Blocks registered for a label will be called
diff --git a/ios/chrome/browser/url_loading/app_url_loading_service.mm b/ios/chrome/browser/url_loading/app_url_loading_service.mm
index cf9eb0e..936436c 100644
--- a/ios/chrome/browser/url_loading/app_url_loading_service.mm
+++ b/ios/chrome/browser/url_loading/app_url_loading_service.mm
@@ -7,11 +7,12 @@
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #import "ios/chrome/browser/main/browser.h"
 #import "ios/chrome/browser/snapshots/snapshot_tab_helper.h"
-#import "ios/chrome/browser/tabs/tab.h"
 #import "ios/chrome/browser/tabs/tab_model.h"
 #import "ios/chrome/browser/url_loading/url_loading_params.h"
 #import "ios/chrome/browser/url_loading/url_loading_service.h"
 #import "ios/chrome/browser/url_loading/url_loading_service_factory.h"
+#import "ios/chrome/browser/web_state_list/web_state_list.h"
+#import "ios/web/public/web_state/web_state.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -56,9 +57,10 @@
                                                params.in_incognito) {
       // Must take a snapshot of the tab before we switch the incognito mode
       // because the currentTab will change after the switch.
-      Tab* currentTab = [delegate_ currentTabModel].currentTab;
-      if (currentTab) {
-        SnapshotTabHelper::FromWebState(currentTab.webState)
+      web::WebState* currentWebState =
+          [delegate_ currentTabModel].webStateList->GetActiveWebState();
+      if (currentWebState) {
+        SnapshotTabHelper::FromWebState(currentWebState)
             ->UpdateSnapshotWithCallback(nil);
       }
 
diff --git a/ios/chrome/browser/web/forms_egtest.mm b/ios/chrome/browser/web/forms_egtest.mm
index a6f8a6f8..53e370e 100644
--- a/ios/chrome/browser/web/forms_egtest.mm
+++ b/ios/chrome/browser/web/forms_egtest.mm
@@ -431,11 +431,7 @@
 }
 
 // A new navigation dismisses the repost dialog.
-// TODO(crbug.com/971670): Reenable for slim nav.
 - (void)testRepostFormDismissedByNewNavigation {
-  if (web::GetWebClient()->IsSlimNavigationManagerEnabled()) {
-    EARL_GREY_TEST_SKIPPED(@"Test not running with slim nav.");
-  }
   [self setUpFormTestSimpleHttpServer];
   const GURL destinationURL = GetDestinationUrl();
 
@@ -455,21 +451,23 @@
     [ChromeEarlGrey reload];
   }
 
-  // When slim navigation manager is enabled, synchronization must be disabled
-  // until after the repost confirmation is dismissed because it is presented
-  // during the load. It is always disabled, but immediately re-enabled if
-  // slim navigation manger is not enabled. This is necessary in order to keep
-  // the correct scope of ScopedSynchronizationDisabler which ensures
-  // synchronization is not left disabled if the test fails.
-  std::unique_ptr<ScopedSynchronizationDisabler> disabler =
-      std::make_unique<ScopedSynchronizationDisabler>();
-  if (![ChromeEarlGrey isSlimNavigationManagerEnabled]) {
-    disabler.reset();
-  }
+  {
+    // When slim navigation manager is enabled, synchronization must be disabled
+    // until after the repost confirmation is dismissed because it is presented
+    // during the load. It is always disabled, but immediately re-enabled if
+    // slim navigation manger is not enabled. This is necessary in order to keep
+    // the correct scope of ScopedSynchronizationDisabler which ensures
+    // synchronization is not left disabled if the test fails.
+    std::unique_ptr<ScopedSynchronizationDisabler> disabler =
+        std::make_unique<ScopedSynchronizationDisabler>();
+    if (![ChromeEarlGrey isSlimNavigationManagerEnabled]) {
+      disabler.reset();
+    }
 
-  // Repost confirmation box should be visible.
-  [ChromeEarlGrey
-      waitForSufficientlyVisibleElementWithMatcher:ResendPostButtonMatcher()];
+    // Repost confirmation box should be visible.
+    [ChromeEarlGrey
+        waitForSufficientlyVisibleElementWithMatcher:ResendPostButtonMatcher()];
+  }
 
   // Starting a new navigation while the repost dialog is presented should not
   // crash.
diff --git a/ios/chrome/test/earl_grey/BUILD.gn b/ios/chrome/test/earl_grey/BUILD.gn
index cf617cc..0fa3abd 100644
--- a/ios/chrome/test/earl_grey/BUILD.gn
+++ b/ios/chrome/test/earl_grey/BUILD.gn
@@ -17,7 +17,6 @@
     ":ios_chrome_reading_list_egtests",
     ":ios_chrome_settings_egtests",
     ":ios_chrome_smoke_egtests",
-    ":ios_chrome_tab_grid_egtests",
     ":ios_chrome_translate_egtests",
     ":ios_chrome_ui_egtests",
     ":ios_chrome_web_egtests",
@@ -71,13 +70,6 @@
   ]
 }
 
-chrome_ios_eg_test("ios_chrome_tab_grid_egtests") {
-  deps = [
-    ":test_support",
-    "//ios/chrome/browser/ui/tab_grid:eg_tests",
-  ]
-}
-
 chrome_ios_eg_test("ios_chrome_ui_egtests") {
   deps = [
     "//ios/chrome/browser/ui/activity_services:eg_tests",
@@ -106,6 +98,7 @@
     "//ios/chrome/browser/ui/settings/sync/utils:eg_tests",
     "//ios/chrome/browser/ui/side_swipe:eg_tests",
     "//ios/chrome/browser/ui/signin_interaction:eg_tests",
+    "//ios/chrome/browser/ui/tab_grid:eg_tests",
     "//ios/chrome/browser/ui/tabs:eg_tests",
     "//ios/chrome/browser/ui/toolbar:eg_tests",
     "//ios/chrome/browser/ui/webui:eg_tests",
diff --git a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
index f7e648d..1a9405a2 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
+++ b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
@@ -33,6 +33,7 @@
 #import "ios/chrome/browser/ui/settings/sync/sync_settings_table_view_controller.h"
 #import "ios/chrome/browser/ui/static_content/static_html_view_controller.h"
 #import "ios/chrome/browser/ui/toolbar/public/toolbar_constants.h"
+#import "ios/chrome/browser/ui/util/ui_util.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "ios/chrome/test/app/chrome_test_util.h"
@@ -296,6 +297,9 @@
 }
 
 + (id<GREYMatcher>)showTabsButton {
+  if (IsIPadIdiom()) {
+    return grey_accessibilityID(@"Enter Tab Switcher");
+  }
   return grey_allOf(grey_accessibilityID(kToolbarStackButtonIdentifier),
                     grey_sufficientlyVisible(), nil);
 }
diff --git a/ios/web/navigation/crw_wk_navigation_handler.mm b/ios/web/navigation/crw_wk_navigation_handler.mm
index 0fd83149..a4581854 100644
--- a/ios/web/navigation/crw_wk_navigation_handler.mm
+++ b/ios/web/navigation/crw_wk_navigation_handler.mm
@@ -243,7 +243,9 @@
         (action.navigationType == WKNavigationTypeFormResubmitted)) {
       self.webStateImpl->ShowRepostFormWarningDialog(
           base::BindOnce(^(bool shouldContinue) {
-            if (shouldContinue) {
+            if ([self.delegate navigationHandlerWebViewBeingDestroyed:self]) {
+              decisionHandler(WKNavigationActionPolicyCancel);
+            } else if (shouldContinue) {
               decisionHandler(WKNavigationActionPolicyAllow);
             } else {
               decisionHandler(WKNavigationActionPolicyCancel);
diff --git a/ios/web/web_state/js/find_in_page_js_unittest.mm b/ios/web/web_state/js/find_in_page_js_unittest.mm
index 3ea5190..60a1963 100644
--- a/ios/web/web_state/js/find_in_page_js_unittest.mm
+++ b/ios/web/web_state/js/find_in_page_js_unittest.mm
@@ -430,9 +430,9 @@
         web_state()->GetWebViewProxy().scrollViewProxy.contentOffset.y;
     return top_scroll_after_select > 0;
   }));
-  // Scroll offset should be 1032.6667 for iPhoneX and 1033 for every
-  // other device.
-  EXPECT_NEAR(top_scroll_after_select, 1032.75, 0.5);
+  // Scroll offset should either be 1035.333 for most iPhone and 1035.5 for iPad
+  // and 5S.
+  EXPECT_NEAR(top_scroll_after_select, 1035, 0.5);
 }
 
 // Tests that FindInPage is able to clear CSS and match highlighting.
diff --git a/ios/web/web_state/js/resources/find_in_page.js b/ios/web/web_state/js/resources/find_in_page.js
index 675a3ff5..f4302faa 100644
--- a/ios/web/web_state/js/resources/find_in_page.js
+++ b/ios/web/web_state/js/resources/find_in_page.js
@@ -683,7 +683,7 @@
     return;
   }
 
-  match.nodes[0].scrollIntoView(/*alignToTop=*/false);
+  match.nodes[0].scrollIntoView({block: "center", inline: "center"});
 };
 
 /**
diff --git a/ios/web_view/internal/signin/web_view_device_accounts_provider_impl.h b/ios/web_view/internal/signin/web_view_device_accounts_provider_impl.h
index b996d88..8066be23 100644
--- a/ios/web_view/internal/signin/web_view_device_accounts_provider_impl.h
+++ b/ios/web_view/internal/signin/web_view_device_accounts_provider_impl.h
@@ -25,7 +25,7 @@
   void GetAccessToken(const std::string& gaia_id,
                       const std::string& client_id,
                       const std::set<std::string>& scopes,
-                      const AccessTokenCallback& callback) override;
+                      AccessTokenCallback callback) override;
   std::vector<AccountInfo> GetAllAccounts() const override;
   AuthenticationErrorCategory GetAuthenticationErrorCategory(
       const std::string& gaia_id,
diff --git a/ios/web_view/internal/signin/web_view_device_accounts_provider_impl.mm b/ios/web_view/internal/signin/web_view_device_accounts_provider_impl.mm
index 52355a1..e611608 100644
--- a/ios/web_view/internal/signin/web_view_device_accounts_provider_impl.mm
+++ b/ios/web_view/internal/signin/web_view_device_accounts_provider_impl.mm
@@ -25,12 +25,13 @@
     const std::string& gaia_id,
     const std::string& client_id,
     const std::set<std::string>& scopes,
-    const AccessTokenCallback& callback) {
+    AccessTokenCallback callback) {
   // |sync_controller| may still be nil if this is called too early so
   // |callback| will not be invoked. That's OK because this will be called again
   // after |sync_controller| has been set.
   CWVSyncController* sync_controller = signin_client_->GetSyncController();
-  [sync_controller fetchAccessTokenForScopes:scopes callback:callback];
+  [sync_controller fetchAccessTokenForScopes:scopes
+                                    callback:std::move(callback)];
 }
 
 std::vector<DeviceAccountsProvider::AccountInfo>
diff --git a/ios/web_view/internal/sync/cwv_sync_controller.mm b/ios/web_view/internal/sync/cwv_sync_controller.mm
index 16e9824..75bb3090 100644
--- a/ios/web_view/internal/sync/cwv_sync_controller.mm
+++ b/ios/web_view/internal/sync/cwv_sync_controller.mm
@@ -23,6 +23,8 @@
 #error "This file requires ARC support."
 #endif
 
+using AccessTokenCallback = DeviceAccountsProvider::AccessTokenCallback;
+
 NSErrorDomain const CWVSyncErrorDomain =
     @"org.chromium.chromewebview.SyncErrorDomain";
 
@@ -203,23 +205,24 @@
 
 #pragma mark - Internal Methods
 
-- (void)
-    fetchAccessTokenForScopes:(const std::set<std::string>&)scopes
-                     callback:
-                         (const DeviceAccountsProvider::AccessTokenCallback&)
-                             callback {
+- (void)fetchAccessTokenForScopes:(const std::set<std::string>&)scopes
+                         callback:(AccessTokenCallback)callback {
+  DCHECK(!callback.is_null());
   NSMutableArray<NSString*>* scopesArray = [NSMutableArray array];
   for (const auto& scope : scopes) {
     [scopesArray addObject:base::SysUTF8ToNSString(scope)];
   }
-  DeviceAccountsProvider::AccessTokenCallback scopedCallback = callback;
+
+  // AccessTokenCallback is non-copyable. Using __block allocates the memory
+  // directly in the block object at compilation time (instead of doing a
+  // copy). This is required to have correct interaction between move-only
+  // types and Objective-C blocks.
+  __block AccessTokenCallback scopedCallback = std::move(callback);
   [_dataSource syncController:self
       getAccessTokenForScopes:[scopesArray copy]
             completionHandler:^(NSString* accessToken, NSDate* expirationDate,
                                 NSError* error) {
-              if (!scopedCallback.is_null()) {
-                scopedCallback.Run(accessToken, expirationDate, error);
-              }
+              std::move(scopedCallback).Run(accessToken, expirationDate, error);
             }];
 }
 
diff --git a/ios/web_view/internal/sync/cwv_sync_controller_internal.h b/ios/web_view/internal/sync/cwv_sync_controller_internal.h
index c51f07b..e865deb 100644
--- a/ios/web_view/internal/sync/cwv_sync_controller_internal.h
+++ b/ios/web_view/internal/sync/cwv_sync_controller_internal.h
@@ -35,11 +35,9 @@
 
 // Called by WebViewDeviceAccountsProviderImpl to obtain
 // access tokens for |scopes| to be passed back in |callback|.
-- (void)
-    fetchAccessTokenForScopes:(const std::set<std::string>&)scopes
-                     callback:
-                         (const DeviceAccountsProvider::AccessTokenCallback&)
-                             callback;
+- (void)fetchAccessTokenForScopes:(const std::set<std::string>&)scopes
+                         callback:(DeviceAccountsProvider::AccessTokenCallback)
+                                      callback;
 
 // Called by IOSWebViewSigninClient when signing out.
 - (void)didSignoutWithSourceMetric:(signin_metrics::ProfileSignout)metric;
diff --git a/ios/web_view/internal/sync/cwv_sync_controller_unittest.mm b/ios/web_view/internal/sync/cwv_sync_controller_unittest.mm
index 5ca956c..eeb9fe2 100644
--- a/ios/web_view/internal/sync/cwv_sync_controller_unittest.mm
+++ b/ios/web_view/internal/sync/cwv_sync_controller_unittest.mm
@@ -125,8 +125,8 @@
     [sync_controller_ startSyncWithIdentity:identity dataSource:data_source];
 
     std::set<std::string> scopes = {kTestScope1, kTestScope2};
-    DeviceAccountsProvider::AccessTokenCallback callback;
-    [sync_controller_ fetchAccessTokenForScopes:scopes callback:callback];
+    [sync_controller_ fetchAccessTokenForScopes:scopes
+                                       callback:base::DoNothing()];
 
     [data_source verify];
   }
diff --git a/media/audio/android/opensles_output.cc b/media/audio/android/opensles_output.cc
index 17554fa..594174c 100644
--- a/media/audio/android/opensles_output.cc
+++ b/media/audio/android/opensles_output.cc
@@ -436,7 +436,9 @@
         reinterpret_cast<int16_t*>(audio_data_[active_buffer_index_]));
   } else {
     DCHECK_EQ(sample_format_, kSampleFormatF32);
-    audio_bus_->ToInterleaved<Float32SampleTypeTraits>(
+
+    // We skip clipping since that occurs at the shared memory boundary.
+    audio_bus_->ToInterleaved<Float32SampleTypeTraitsNoClip>(
         frames_filled,
         reinterpret_cast<float*>(audio_data_[active_buffer_index_]));
   }
diff --git a/media/audio/audio_sync_reader.cc b/media/audio/audio_sync_reader.cc
index bba78e8..9cc45305 100644
--- a/media/audio/audio_sync_reader.cc
+++ b/media/audio/audio_sync_reader.cc
@@ -235,8 +235,12 @@
     }
     output_bus_->SetBitstreamDataSize(data_size);
     output_bus_->SetBitstreamFrames(bitstream_frames);
+    output_bus_->CopyTo(dest);
+    return;
   }
-  output_bus_->CopyTo(dest);
+
+  // Copy and clip data coming across the shared memory since it's untrusted.
+  output_bus_->CopyAndClipTo(dest);
 }
 
 void AudioSyncReader::Close() {
diff --git a/media/audio/fuchsia/audio_output_stream_fuchsia.cc b/media/audio/fuchsia/audio_output_stream_fuchsia.cc
index 4a79c4a0..cbcda51b 100644
--- a/media/audio/fuchsia/audio_output_stream_fuchsia.cc
+++ b/media/audio/fuchsia/audio_output_stream_fuchsia.cc
@@ -221,7 +221,9 @@
   // Save samples to the |payload_buffer_|.
   size_t packet_size = parameters_.GetBytesPerBuffer(kSampleFormatF32);
   DCHECK_LE(payload_buffer_pos_ + packet_size, payload_buffer_.size());
-  audio_bus_->ToInterleaved<media::Float32SampleTypeTraits>(
+
+  // We skip clipping since that occurs at the shared memory boundary.
+  audio_bus_->ToInterleaved<Float32SampleTypeTraitsNoClip>(
       audio_bus_->frames(),
       reinterpret_cast<float*>(static_cast<uint8_t*>(payload_buffer_.memory()) +
                                payload_buffer_pos_));
diff --git a/media/audio/pulse/pulse_output.cc b/media/audio/pulse/pulse_output.cc
index 6b03e08..6ee4d63 100644
--- a/media/audio/pulse/pulse_output.cc
+++ b/media/audio/pulse/pulse_output.cc
@@ -164,7 +164,8 @@
       frames_to_copy =
           std::min(audio_bus_->frames() - frame_offset_in_bus, frames_to_copy);
 
-      audio_bus_->ToInterleavedPartial<Float32SampleTypeTraits>(
+      // We skip clipping since that occurs at the shared memory boundary.
+      audio_bus_->ToInterleavedPartial<Float32SampleTypeTraitsNoClip>(
           frame_offset_in_bus, frames_to_copy,
           reinterpret_cast<float*>(pa_buffer));
       frame_offset_in_bus += frames_to_copy;
diff --git a/media/audio/win/audio_low_latency_output_win.cc b/media/audio/win/audio_low_latency_output_win.cc
index fbb3bf4..225682e 100644
--- a/media/audio/win/audio_low_latency_output_win.cc
+++ b/media/audio/win/audio_low_latency_output_win.cc
@@ -599,7 +599,9 @@
     DCHECK_LE(num_filled_bytes, packet_size_bytes_);
 
     audio_bus_->Scale(volume_);
-    audio_bus_->ToInterleaved<Float32SampleTypeTraits>(
+
+    // We skip clipping since that occurs at the shared memory boundary.
+    audio_bus_->ToInterleaved<Float32SampleTypeTraitsNoClip>(
         frames_filled, reinterpret_cast<float*>(audio_data));
 
     // Release the buffer space acquired in the GetBuffer() call.
diff --git a/media/base/audio_bus.cc b/media/base/audio_bus.cc
index 12f2e7ae..991e73e1 100644
--- a/media/base/audio_bus.cc
+++ b/media/base/audio_bus.cc
@@ -358,6 +358,18 @@
   CopyPartialFramesTo(0, frames(), 0, dest);
 }
 
+void AudioBus::CopyAndClipTo(AudioBus* dest) const {
+  DCHECK(!is_bitstream_format_);
+  CHECK_EQ(channels(), dest->channels());
+  CHECK_LE(frames(), dest->frames());
+  for (int i = 0; i < channels(); ++i) {
+    float* dest_ptr = dest->channel(i);
+    const float* source_ptr = channel(i);
+    for (int j = 0; j < frames(); ++j)
+      dest_ptr[j] = Float32SampleTypeTraits::FromFloat(source_ptr[j]);
+  }
+}
+
 void AudioBus::CopyPartialFramesTo(int source_start_frame,
                                    int frame_count,
                                    int dest_start_frame,
diff --git a/media/base/audio_bus.h b/media/base/audio_bus.h
index e128f32..024fee8 100644
--- a/media/base/audio_bus.h
+++ b/media/base/audio_bus.h
@@ -151,6 +151,9 @@
   // AudioBus object must have the same frames() and channels().
   void CopyTo(AudioBus* dest) const;
 
+  // Similar to above, but clips values to [-1, 1] during the copy process.
+  void CopyAndClipTo(AudioBus* dest) const;
+
   // Helper method to copy frames from one AudioBus to another. Both AudioBus
   // objects must have the same number of channels(). |source_start_frame| is
   // the starting offset. |dest_start_frame| is the starting offset in |dest|.
diff --git a/media/base/audio_bus_perftest.cc b/media/base/audio_bus_perftest.cc
index 2140a228..cf12edfc1 100644
--- a/media/base/audio_bus_perftest.cc
+++ b/media/base/audio_bus_perftest.cc
@@ -18,7 +18,9 @@
 static const int kSampleRate = 48000;
 
 template <typename T, class SampleTraits>
-void RunInterleaveBench(AudioBus* bus, const std::string& trace_name) {
+void RunInterleaveBench(AudioBus* bus,
+                        const std::string& trace_name,
+                        bool to_interleaved_only = false) {
   const int frame_size = bus->frames() * bus->channels();
   std::unique_ptr<T[]> interleaved(new T[frame_size]);
 
@@ -31,6 +33,9 @@
                          total_time_milliseconds / kBenchmarkIterations, "ms",
                          true);
 
+  if (to_interleaved_only)
+    return;
+
   start = base::TimeTicks::Now();
   for (int i = 0; i < kBenchmarkIterations; ++i)
     bus->FromInterleaved<SampleTraits>(interleaved.get(), bus->frames());
@@ -52,4 +57,42 @@
   RunInterleaveBench<float, Float32SampleTypeTraits>(bus.get(), "float");
 }
 
+TEST(AudioBusPerfTest, DISABLED_ToInterleavedFloat) {
+  std::unique_ptr<AudioBus> bus = AudioBus::Create(2, kSampleRate * 120);
+  FakeAudioRenderCallback callback(0.2, kSampleRate);
+  callback.Render(base::TimeDelta(), base::TimeTicks::Now(), 0, bus.get());
+
+  RunInterleaveBench<float, Float32SampleTypeTraits>(
+      bus.get(), "to_interleave_float", true);
+  RunInterleaveBench<float, Float32SampleTypeTraitsNoClip>(
+      bus.get(), "to_interleave_float_no_clip", true);
+}
+
+void RunCopyBench(void (AudioBus::*f)(AudioBus*) const, const char* name) {
+  // Setup.
+  std::unique_ptr<AudioBus> bus = AudioBus::Create(2, kSampleRate * 120);
+  std::unique_ptr<AudioBus> dest = AudioBus::Create(2, kSampleRate * 120);
+  FakeAudioRenderCallback callback(0.2, kSampleRate);
+  callback.Render(base::TimeDelta(), base::TimeTicks::Now(), 0, bus.get());
+
+  // Warmup.
+  for (int i = 0; i < kBenchmarkIterations; ++i)
+    (bus.get()->*f)(dest.get());
+
+  // Benchmark.
+  auto start = base::TimeTicks::Now();
+  for (int i = 0; i < kBenchmarkIterations; ++i)
+    (bus.get()->*f)(dest.get());
+  auto total_time_milliseconds =
+      (base::TimeTicks::Now() - start).InMillisecondsF();
+
+  perf_test::PrintResult(
+      name, "", "", total_time_milliseconds / kBenchmarkIterations, "ms", true);
+}
+
+TEST(AudioBusPerfTest, DISABLED_CopyBench) {
+  RunCopyBench(&AudioBus::CopyTo, "audio_bus_copy_no_clip");
+  RunCopyBench(&AudioBus::CopyAndClipTo, "audio_bus_copy_clip");
+}
+
 }  // namespace media
diff --git a/media/base/audio_bus_unittest.cc b/media/base/audio_bus_unittest.cc
index 9b21d76..46a124cf 100644
--- a/media/base/audio_bus_unittest.cc
+++ b/media/base/audio_bus_unittest.cc
@@ -300,6 +300,23 @@
 static const float kTestVectorFloat32[kTestVectorSize] = {
     -1.0f, 0.0f, 1.0f, -1.0f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f};
 
+// This is based on kTestVectorFloat32, but has some of the values outside of
+// sanity.
+static const float kTestVectorFloat32Invalid[kTestVectorSize] = {
+    -5.0f,
+    0.0f,
+    5.0f,
+    -1.0f,
+    0.5f,
+    -0.5f,
+    0.0f,
+    std::numeric_limits<float>::infinity(),
+    std::numeric_limits<float>::signaling_NaN(),
+    std::numeric_limits<float>::quiet_NaN()};
+
+static const float kTestVectorFloat32Sanitized[kTestVectorSize] = {
+    -1.0f, 0.0f, 1.0f, -1.0f, 0.5f, -0.5f, 0.0f, 1.0f, -1.0f, -1.0f};
+
 // Expected results.
 static const int kTestVectorFrameCount = kTestVectorSize / 2;
 static const float kTestVectorResult[][kTestVectorFrameCount] = {
@@ -519,29 +536,50 @@
 }
 
 TEST_F(AudioBusTest, ToInterleavedSanitized) {
-  // This is based on kTestVectorFloat32, but has some of the values outside of
-  // sanity.
-  static const float kTestVectorFloat32Invalid[kTestVectorSize] = {
-      -5.0f,
-      0.0f,
-      5.0f,
-      -1.0f,
-      0.5f,
-      -0.5f,
-      0.0f,
-      std::numeric_limits<float>::infinity(),
-      std::numeric_limits<float>::signaling_NaN(),
-      std::numeric_limits<float>::quiet_NaN()};
   std::unique_ptr<AudioBus> bus =
       AudioBus::Create(kTestVectorChannelCount, kTestVectorFrameCount);
   bus->FromInterleaved<Float32SampleTypeTraits>(kTestVectorFloat32Invalid,
                                                 bus->frames());
   // Verify FromInterleaved applied no sanity.
   ASSERT_EQ(bus->channel(0)[0], kTestVectorFloat32Invalid[0]);
-  float test_array[base::size(kTestVectorFloat32)];
+  float test_array[base::size(kTestVectorFloat32Sanitized)];
   bus->ToInterleaved<Float32SampleTypeTraits>(bus->frames(), test_array);
+  for (size_t i = 0; i < base::size(kTestVectorFloat32Sanitized); ++i)
+    ASSERT_EQ(kTestVectorFloat32Sanitized[i], test_array[i]);
+
+  // Verify that Float32SampleTypeTraitsNoClip applied no sanity.
+  bus->ToInterleaved<Float32SampleTypeTraitsNoClip>(bus->frames(), test_array);
   ASSERT_EQ(0,
-            memcmp(test_array, kTestVectorFloat32, sizeof(kTestVectorFloat32)));
+            memcmp(test_array, kTestVectorFloat32Invalid,
+                   kTestVectorFrameCount * sizeof(*kTestVectorFloat32Invalid) *
+                       kTestVectorChannelCount));
+}
+
+TEST_F(AudioBusTest, CopyAndClipTo) {
+  auto bus = AudioBus::Create(kTestVectorChannelCount, kTestVectorFrameCount);
+  bus->FromInterleaved<Float32SampleTypeTraits>(kTestVectorFloat32Invalid,
+                                                bus->frames());
+  auto expected =
+      AudioBus::Create(kTestVectorChannelCount, kTestVectorFrameCount);
+  expected->FromInterleaved<Float32SampleTypeTraits>(
+      kTestVectorFloat32Sanitized, bus->frames());
+
+  // Verify FromInterleaved applied no sanity.
+  ASSERT_EQ(bus->channel(0)[0], kTestVectorFloat32Invalid[0]);
+
+  std::unique_ptr<AudioBus> copy_to_bus =
+      AudioBus::Create(kTestVectorChannelCount, kTestVectorFrameCount);
+  bus->CopyAndClipTo(copy_to_bus.get());
+
+  for (int ch = 0; ch < expected->channels(); ++ch) {
+    for (int i = 0; i < expected->frames(); ++i)
+      ASSERT_EQ(copy_to_bus->channel(ch)[i], expected->channel(ch)[i]);
+  }
+
+  ASSERT_EQ(expected->channels(), copy_to_bus->channels());
+  ASSERT_EQ(expected->frames(), copy_to_bus->frames());
+  ASSERT_EQ(expected->is_bitstream_format(),
+            copy_to_bus->is_bitstream_format());
 }
 
 // Verify ToInterleavedPartial() interleaves audio correctly.
diff --git a/media/base/audio_sample_types.h b/media/base/audio_sample_types.h
index f77edf3c..af734ec 100644
--- a/media/base/audio_sample_types.h
+++ b/media/base/audio_sample_types.h
@@ -63,11 +63,9 @@
   static SampleType From(FloatType source_value) {
     // Apply clipping (aka. clamping). These values are frequently sent to OS
     // level drivers that may not properly handle these values.
-    if (std::isnan(source_value))
-      return kZeroPointValue;
-    if (source_value <= kMinValue)
+    if (UNLIKELY(!(source_value >= kMinValue)))
       return kMinValue;
-    if (source_value >= kMaxValue)
+    if (UNLIKELY(source_value >= kMaxValue))
       return kMaxValue;
     return static_cast<SampleType>(source_value);
   }
@@ -78,6 +76,44 @@
   }
 };
 
+// Similar to above, but does not apply clipping.
+template <typename SampleType>
+class FloatSampleTypeTraitsNoClip {
+  static_assert(std::is_floating_point<SampleType>::value,
+                "Template is only valid for float types.");
+
+ public:
+  using ValueType = SampleType;
+
+  static constexpr SampleType kMinValue = -1.0f;
+  static constexpr SampleType kMaxValue = +1.0f;
+  static constexpr SampleType kZeroPointValue = 0.0f;
+
+  static SampleType FromFloat(float source_value) {
+    return From<float>(source_value);
+  }
+  static float ToFloat(SampleType source_value) {
+    return To<float>(source_value);
+  }
+  static SampleType FromDouble(double source_value) {
+    return From<double>(source_value);
+  }
+  static double ToDouble(SampleType source_value) {
+    return To<double>(source_value);
+  }
+
+ private:
+  template <typename FloatType>
+  static SampleType From(FloatType source_value) {
+    return static_cast<SampleType>(source_value);
+  }
+
+  template <typename FloatType>
+  static FloatType To(SampleType source_value) {
+    return static_cast<FloatType>(source_value);
+  }
+};
+
 // For uint8_t, int16_t, int32_t...
 // See also the aliases for commonly used types at the bottom of this file.
 template <typename SampleType>
@@ -208,6 +244,7 @@
 
 // Aliases for commonly used sample formats.
 using Float32SampleTypeTraits = FloatSampleTypeTraits<float>;
+using Float32SampleTypeTraitsNoClip = FloatSampleTypeTraitsNoClip<float>;
 using Float64SampleTypeTraits = FloatSampleTypeTraits<double>;
 using UnsignedInt8SampleTypeTraits = FixedSampleTypeTraits<uint8_t>;
 using SignedInt16SampleTypeTraits = FixedSampleTypeTraits<int16_t>;
diff --git a/media/base/supported_types.cc b/media/base/supported_types.cc
index 0f5b4aa6..aa46e61b 100644
--- a/media/base/supported_types.cc
+++ b/media/base/supported_types.cc
@@ -7,6 +7,7 @@
 #include "base/feature_list.h"
 #include "base/no_destructor.h"
 #include "build/build_config.h"
+#include "media/base/media.h"
 #include "media/base/media_client.h"
 #include "media/base/media_switches.h"
 #include "media/media_buildflags.h"
@@ -153,9 +154,11 @@
     return true;
 
 #if defined(OS_ANDROID)
-  // Support for VP9.2, VP9.3 was not added until Nougat.
+  // Support for VP9.2, VP9.3 was not added until Nougat and requires that we
+  // have the platform decoder (MediaCodec) available.
   if (base::android::BuildInfo::GetInstance()->sdk_int() <
-      base::android::SDK_VERSION_NOUGAT) {
+          base::android::SDK_VERSION_NOUGAT ||
+      !HasPlatformDecoderSupport()) {
     return false;
   }
 
diff --git a/media/capture/video/chromeos/camera_device_delegate.cc b/media/capture/video/chromeos/camera_device_delegate.cc
index 01711828..88ef6a4 100644
--- a/media/capture/video/chromeos/camera_device_delegate.cc
+++ b/media/capture/video/chromeos/camera_device_delegate.cc
@@ -221,7 +221,7 @@
     base::OnceClosure device_close_callback) {
   DCHECK(ipc_task_runner_->BelongsToCurrentThread());
 
-  reprocess_manager_->FlushReprocessOptions(device_descriptor_.device_id);
+  reprocess_manager_->Flush(device_descriptor_.device_id);
 
   if (!device_context_ ||
       device_context_->GetState() == CameraDeviceContext::State::kStopped ||
@@ -781,17 +781,13 @@
         FROM_HERE, "Failed to get default request settings");
     return;
   }
-  device_context_->SetState(CameraDeviceContext::State::kCapturing);
-  camera_3a_controller_->SetAutoFocusModeForStillCapture();
-  request_manager_->StartPreview(std::move(settings));
-
-  if (!take_photo_callbacks_.empty()) {
-    TakePhotoImpl();
-  }
-
-  if (set_photo_option_callback_) {
-    std::move(set_photo_option_callback_).Run(true);
-  }
+  reprocess_manager_->GetFpsRange(
+      device_descriptor_.device_id,
+      chrome_capture_params_.requested_format.frame_size.width(),
+      chrome_capture_params_.requested_format.frame_size.height(),
+      media::BindToCurrentLoop(
+          base::BindOnce(&CameraDeviceDelegate::OnGotFpsRange, GetWeakPtr(),
+                         std::move(settings))));
 }
 
 void CameraDeviceDelegate::OnConstructedDefaultStillCaptureRequestSettings(
@@ -812,6 +808,42 @@
   }
 }
 
+void CameraDeviceDelegate::OnGotFpsRange(
+    cros::mojom::CameraMetadataPtr settings,
+    base::Optional<gfx::Range> specified_fps_range) {
+  device_context_->SetState(CameraDeviceContext::State::kCapturing);
+  camera_3a_controller_->SetAutoFocusModeForStillCapture();
+  if (specified_fps_range.has_value()) {
+    const int32_t entry_length = 2;
+
+    // CameraMetadata is represented as an uint8 array. According to the
+    // definition of the FPS metadata tag, its data type is int32, so we
+    // reinterpret_cast here.
+    std::vector<uint8_t> fps_range(sizeof(int32_t) * entry_length);
+    auto* fps_ptr = reinterpret_cast<int32_t*>(fps_range.data());
+    fps_ptr[0] = specified_fps_range->GetMin();
+    fps_ptr[1] = specified_fps_range->GetMax();
+    cros::mojom::CameraMetadataEntryPtr e =
+        cros::mojom::CameraMetadataEntry::New();
+    e->tag =
+        cros::mojom::CameraMetadataTag::ANDROID_CONTROL_AE_TARGET_FPS_RANGE;
+    e->type = cros::mojom::EntryType::TYPE_INT32;
+    e->count = entry_length;
+    e->data = std::move(fps_range);
+
+    AddOrUpdateMetadataEntry(&settings, std::move(e));
+  }
+  request_manager_->StartPreview(std::move(settings));
+
+  if (!take_photo_callbacks_.empty()) {
+    TakePhotoImpl();
+  }
+
+  if (set_photo_option_callback_) {
+    std::move(set_photo_option_callback_).Run(true);
+  }
+}
+
 void CameraDeviceDelegate::ProcessCaptureRequest(
     cros::mojom::Camera3CaptureRequestPtr request,
     base::OnceCallback<void(int32_t)> callback) {
diff --git a/media/capture/video/chromeos/camera_device_delegate.h b/media/capture/video/chromeos/camera_device_delegate.h
index 971a308..b1cca24 100644
--- a/media/capture/video/chromeos/camera_device_delegate.h
+++ b/media/capture/video/chromeos/camera_device_delegate.h
@@ -16,6 +16,7 @@
 #include "media/capture/video/video_capture_device.h"
 #include "media/capture/video_capture_types.h"
 #include "ui/gfx/geometry/size.h"
+#include "ui/gfx/range/range.h"
 
 namespace media {
 
@@ -159,6 +160,9 @@
   void OnConstructedDefaultStillCaptureRequestSettings(
       cros::mojom::CameraMetadataPtr settings);
 
+  void OnGotFpsRange(cros::mojom::CameraMetadataPtr settings,
+                     base::Optional<gfx::Range> specified_fps_range);
+
   // StreamCaptureInterface implementations.  These methods are called by
   // |stream_buffer_manager_| on |ipc_task_runner_|.
   void ProcessCaptureRequest(cros::mojom::Camera3CaptureRequestPtr request,
diff --git a/media/capture/video/chromeos/cros_image_capture_impl.cc b/media/capture/video/chromeos/cros_image_capture_impl.cc
index 1d14432..b0c3b19 100644
--- a/media/capture/video/chromeos/cros_image_capture_impl.cc
+++ b/media/capture/video/chromeos/cros_image_capture_impl.cc
@@ -34,6 +34,17 @@
       device_id, effect, media::BindToCurrentLoop(std::move(callback)));
 }
 
+void CrosImageCaptureImpl::SetFpsRange(const std::string& device_id,
+                                       const uint32_t stream_width,
+                                       const uint32_t stream_height,
+                                       const int32_t min_fps,
+                                       const int32_t max_fps,
+                                       SetFpsRangeCallback callback) {
+  reprocess_manager_->SetFpsRange(
+      device_id, stream_width, stream_height, min_fps, max_fps,
+      media::BindToCurrentLoop(std::move(callback)));
+}
+
 void CrosImageCaptureImpl::OnGotCameraInfo(
     GetCameraInfoCallback callback,
     cros::mojom::CameraInfoPtr camera_info) {
diff --git a/media/capture/video/chromeos/cros_image_capture_impl.h b/media/capture/video/chromeos/cros_image_capture_impl.h
index 9a5c78f1..553a049a 100644
--- a/media/capture/video/chromeos/cros_image_capture_impl.h
+++ b/media/capture/video/chromeos/cros_image_capture_impl.h
@@ -28,6 +28,12 @@
   void SetReprocessOption(const std::string& device_id,
                           cros::mojom::Effect effect,
                           SetReprocessOptionCallback callback) override;
+  void SetFpsRange(const std::string& device_id,
+                   const uint32_t stream_width,
+                   const uint32_t stream_height,
+                   const int32_t min_fps,
+                   const int32_t max_fps,
+                   SetFpsRangeCallback callback) override;
 
  private:
   void OnGotCameraInfo(GetCameraInfoCallback callback,
diff --git a/media/capture/video/chromeos/mojo/cros_image_capture.mojom b/media/capture/video/chromeos/mojo/cros_image_capture.mojom
index e2adaf2..215f191 100644
--- a/media/capture/video/chromeos/mojo/cros_image_capture.mojom
+++ b/media/capture/video/chromeos/mojo/cros_image_capture.mojom
@@ -32,4 +32,15 @@
   // actual video device id.
   SetReprocessOption(string source_id, Effect effect)
       => (int32 status, media.mojom.Blob blob);
+
+  // Sets the frame rate range for upcoming configured camera stream.
+  // The |source_id| might need translation to be actual video device id.
+  // The |stream_width| and |stream_height| are the target stream resolution
+  // that the caller sets the frame rate range for.
+  // The |min_frame_rate| and |max_frame_rate| represent the frame rate range.
+  // If the given frame rate is valid and set successfully, |is_success| returns
+  // true. False otherwise.
+  SetFpsRange(string source_id, uint32 stream_width, uint32 stream_height,
+              int32 min_frame_rate, int32 max_frame_rate)
+      => (bool is_success);
 };
\ No newline at end of file
diff --git a/media/capture/video/chromeos/renderer_facing_cros_image_capture.cc b/media/capture/video/chromeos/renderer_facing_cros_image_capture.cc
index 6d09adf..f48ea78 100644
--- a/media/capture/video/chromeos/renderer_facing_cros_image_capture.cc
+++ b/media/capture/video/chromeos/renderer_facing_cros_image_capture.cc
@@ -39,6 +39,19 @@
                                           std::move(callback));
 }
 
+void RendererFacingCrosImageCapture::SetFpsRangeWithRealId(
+    const uint32_t stream_width,
+    const uint32_t stream_height,
+    const int32_t min_frame_rate,
+    const int32_t max_frame_rate,
+    SetFpsRangeCallback callback,
+    const base::Optional<std::string>& device_id) {
+  DCHECK(device_id.has_value());
+  cros_image_capture_->SetFpsRange(*device_id, stream_width, stream_height,
+                                   min_frame_rate, max_frame_rate,
+                                   std::move(callback));
+}
+
 void RendererFacingCrosImageCapture::GetCameraInfo(
     const std::string& source_id,
     GetCameraInfoCallback callback) {
@@ -59,4 +72,18 @@
           weak_ptr_factory_.GetWeakPtr(), effect, std::move(callback))));
 }
 
+void RendererFacingCrosImageCapture::SetFpsRange(const std::string& source_id,
+                                                 const uint32_t stream_width,
+                                                 const uint32_t stream_height,
+                                                 const int32_t min_frame_rate,
+                                                 const int32_t max_frame_rate,
+                                                 SetFpsRangeCallback callback) {
+  mapping_callback_.Run(
+      source_id,
+      media::BindToCurrentLoop(base::BindOnce(
+          &RendererFacingCrosImageCapture::SetFpsRangeWithRealId,
+          weak_ptr_factory_.GetWeakPtr(), stream_width, stream_height,
+          min_frame_rate, max_frame_rate, std::move(callback))));
+}
+
 }  // namespace media
\ No newline at end of file
diff --git a/media/capture/video/chromeos/renderer_facing_cros_image_capture.h b/media/capture/video/chromeos/renderer_facing_cros_image_capture.h
index ad6fa0b..cc951e8 100644
--- a/media/capture/video/chromeos/renderer_facing_cros_image_capture.h
+++ b/media/capture/video/chromeos/renderer_facing_cros_image_capture.h
@@ -41,12 +41,25 @@
       SetReprocessOptionCallback callback,
       const base::Optional<std::string>& device_id);
 
+  void SetFpsRangeWithRealId(const uint32_t stream_width,
+                             const uint32_t stream_height,
+                             const int32_t min_frame_rate,
+                             const int32_t max_frame_rate,
+                             SetFpsRangeCallback callback,
+                             const base::Optional<std::string>& device_id);
+
   // cros::mojom::CrosImageCapture implementations.
   void GetCameraInfo(const std::string& source_id,
                      GetCameraInfoCallback callback) override;
   void SetReprocessOption(const std::string& source_id,
                           cros::mojom::Effect effect,
                           SetReprocessOptionCallback callback) override;
+  void SetFpsRange(const std::string& source_id,
+                   const uint32_t stream_width,
+                   const uint32_t stream_height,
+                   const int32_t min_frame_rate,
+                   const int32_t max_frame_rate,
+                   SetFpsRangeCallback callback) override;
 
  private:
   cros::mojom::CrosImageCapturePtr cros_image_capture_;
diff --git a/media/capture/video/chromeos/reprocess_manager.cc b/media/capture/video/chromeos/reprocess_manager.cc
index 9fce50d..3c0776c 100644
--- a/media/capture/video/chromeos/reprocess_manager.cc
+++ b/media/capture/video/chromeos/reprocess_manager.cc
@@ -79,12 +79,10 @@
           std::move(take_photo_callback), std::move(consumption_callback)));
 }
 
-void ReprocessManager::FlushReprocessOptions(const std::string& device_id) {
+void ReprocessManager::Flush(const std::string& device_id) {
   sequenced_task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(
-          &ReprocessManager::ReprocessManagerImpl::FlushReprocessOptions,
-          base::Unretained(impl.get()), device_id));
+      FROM_HERE, base::BindOnce(&ReprocessManager::ReprocessManagerImpl::Flush,
+                                base::Unretained(impl.get()), device_id));
 }
 
 void ReprocessManager::GetCameraInfo(const std::string& device_id,
@@ -96,6 +94,31 @@
                      std::move(callback)));
 }
 
+void ReprocessManager::SetFpsRange(
+    const std::string& device_id,
+    const uint32_t stream_width,
+    const uint32_t stream_height,
+    const int32_t min_fps,
+    const int32_t max_fps,
+    cros::mojom::CrosImageCapture::SetFpsRangeCallback callback) {
+  sequenced_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&ReprocessManager::ReprocessManagerImpl::SetFpsRange,
+                     base::Unretained(impl.get()), device_id, stream_width,
+                     stream_height, min_fps, max_fps, std::move(callback)));
+}
+
+void ReprocessManager::GetFpsRange(const std::string& device_id,
+                                   const uint32_t stream_width,
+                                   const uint32_t stream_height,
+                                   GetFpsRangeCallback callback) {
+  sequenced_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&ReprocessManager::ReprocessManagerImpl::GetFpsRange,
+                     base::Unretained(impl.get()), device_id, stream_width,
+                     stream_height, std::move(callback)));
+}
+
 ReprocessManager::ReprocessManagerImpl::ReprocessManagerImpl(
     CameraInfoGetter get_camera_info)
     : get_camera_info_(std::move(get_camera_info)) {}
@@ -147,10 +170,13 @@
   std::move(consumption_callback).Run(std::move(result_task_queue));
 }
 
-void ReprocessManager::ReprocessManagerImpl::FlushReprocessOptions(
+void ReprocessManager::ReprocessManagerImpl::Flush(
     const std::string& device_id) {
   auto empty_queue = ReprocessTaskQueue();
   reprocess_task_queue_map_[device_id].swap(empty_queue);
+
+  auto empty_map = ResolutionFpsRangeMap();
+  resolution_fps_range_map_[device_id].swap(empty_map);
 }
 
 void ReprocessManager::ReprocessManagerImpl::GetCameraInfo(
@@ -159,4 +185,69 @@
   std::move(callback).Run(get_camera_info_.Run(device_id));
 }
 
+void ReprocessManager::ReprocessManagerImpl::SetFpsRange(
+    const std::string& device_id,
+    const uint32_t stream_width,
+    const uint32_t stream_height,
+    const int32_t min_fps,
+    const int32_t max_fps,
+    cros::mojom::CrosImageCapture::SetFpsRangeCallback callback) {
+  const int entry_length = 2;
+
+  auto camera_info = get_camera_info_.Run(device_id);
+  auto& static_metadata = camera_info->static_camera_characteristics;
+  auto available_fps_range_entries = GetMetadataEntryAsSpan<int32_t>(
+      static_metadata, cros::mojom::CameraMetadataTag::
+                           ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);
+  CHECK(available_fps_range_entries.size() % entry_length == 0);
+
+  bool is_valid = false;
+  for (size_t i = 0; i < available_fps_range_entries.size();
+       i += entry_length) {
+    if (available_fps_range_entries[i] == min_fps &&
+        available_fps_range_entries[i + 1] == max_fps) {
+      is_valid = true;
+      break;
+    }
+  }
+
+  if (!is_valid) {
+    std::move(callback).Run(false);
+    return;
+  }
+
+  auto resolution = gfx::Size(stream_width, stream_height);
+  auto fps_range = gfx::Range(min_fps, max_fps);
+  resolution_fps_range_map_[device_id][resolution] = fps_range;
+  std::move(callback).Run(true);
+}
+
+void ReprocessManager::ReprocessManagerImpl::GetFpsRange(
+    const std::string& device_id,
+    const uint32_t stream_width,
+    const uint32_t stream_height,
+    GetFpsRangeCallback callback) {
+  if (resolution_fps_range_map_.find(device_id) ==
+      resolution_fps_range_map_.end()) {
+    std::move(callback).Run({});
+    return;
+  }
+
+  auto resolution = gfx::Size(stream_width, stream_height);
+  auto& fps_map = resolution_fps_range_map_[device_id];
+  if (fps_map.find(resolution) == fps_map.end()) {
+    std::move(callback).Run({});
+    return;
+  }
+
+  std::move(callback).Run(fps_map[resolution]);
+}
+
+bool ReprocessManager::ReprocessManagerImpl::SizeComparator::operator()(
+    const gfx::Size size_1,
+    const gfx::Size size_2) const {
+  return size_1.width() < size_2.width() || (size_1.width() == size_2.width() &&
+                                             size_1.height() < size_2.height());
+}
+
 }  // namespace media
diff --git a/media/capture/video/chromeos/reprocess_manager.h b/media/capture/video/chromeos/reprocess_manager.h
index c7dea66..ac2add5 100644
--- a/media/capture/video/chromeos/reprocess_manager.h
+++ b/media/capture/video/chromeos/reprocess_manager.h
@@ -19,6 +19,8 @@
 #include "media/capture/video/chromeos/mojo/camera_common.mojom.h"
 #include "media/capture/video/chromeos/mojo/cros_image_capture.mojom.h"
 #include "mojo/public/cpp/bindings/binding.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/range/range.h"
 
 namespace media {
 
@@ -51,9 +53,18 @@
       const std::string& device_id)>;
   using GetCameraInfoCallback =
       base::OnceCallback<void(cros::mojom::CameraInfoPtr camera_info)>;
+  using GetFpsRangeCallback =
+      base::OnceCallback<void(base::Optional<gfx::Range>)>;
 
   class ReprocessManagerImpl {
    public:
+    struct SizeComparator {
+      bool operator()(const gfx::Size size_1, const gfx::Size size_2) const;
+    };
+
+    using ResolutionFpsRangeMap =
+        base::flat_map<gfx::Size, gfx::Range, SizeComparator>;
+
     ReprocessManagerImpl(CameraInfoGetter get_camera_info);
     ~ReprocessManagerImpl();
 
@@ -68,14 +79,29 @@
         media::mojom::ImageCapture::TakePhotoCallback take_photo_callback,
         base::OnceCallback<void(ReprocessTaskQueue)> consumption_callback);
 
-    void FlushReprocessOptions(const std::string& device_id);
+    void Flush(const std::string& device_id);
 
     void GetCameraInfo(const std::string& device_id,
                        GetCameraInfoCallback callback);
 
+    void SetFpsRange(
+        const std::string& device_id,
+        const uint32_t stream_width,
+        const uint32_t stream_height,
+        const int32_t min_fps,
+        const int32_t max_fps,
+        cros::mojom::CrosImageCapture::SetFpsRangeCallback callback);
+
+    void GetFpsRange(const std::string& device_id,
+                     const uint32_t stream_width,
+                     const uint32_t stream_height,
+                     GetFpsRangeCallback callback);
+
    private:
     base::flat_map<std::string, base::queue<ReprocessTask>>
         reprocess_task_queue_map_;
+    base::flat_map<std::string, ResolutionFpsRangeMap>
+        resolution_fps_range_map_;
 
     CameraInfoGetter get_camera_info_;
 
@@ -103,13 +129,27 @@
       media::mojom::ImageCapture::TakePhotoCallback take_photo_callback,
       base::OnceCallback<void(ReprocessTaskQueue)> consumption_callback);
 
-  // Clears all remaining ReprocessTasks in the queue for given device id.
-  void FlushReprocessOptions(const std::string& device_id);
+  // Clears all temporary queues and maps that is used for given device id.
+  void Flush(const std::string& device_id);
 
   // Gets camera information for current active device.
   void GetCameraInfo(const std::string& device_id,
                      GetCameraInfoCallback callback);
 
+  // Sets fps range for given device.
+  void SetFpsRange(const std::string& device_id,
+                   const uint32_t stream_width,
+                   const uint32_t stream_height,
+                   const int32_t min_fps,
+                   const int32_t max_fps,
+                   cros::mojom::CrosImageCapture::SetFpsRangeCallback callback);
+
+  // Gets fps range for given device and resolution.
+  void GetFpsRange(const std::string& device_id,
+                   const uint32_t stream_width,
+                   const uint32_t stream_height,
+                   GetFpsRangeCallback callback);
+
  private:
   scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_;
   std::unique_ptr<ReprocessManagerImpl> impl;
diff --git a/media/cast/sender/external_video_encoder.cc b/media/cast/sender/external_video_encoder.cc
index cc13dc5b..282194d 100644
--- a/media/cast/sender/external_video_encoder.cc
+++ b/media/cast/sender/external_video_encoder.cc
@@ -600,15 +600,6 @@
       video_config.codec != CODEC_VIDEO_H264)
     return false;
 
-#if defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_FAMILY)
-  // Disable VP8 HW encoding on ChromeOS Intel platform, because some target
-  // cannot decode correctly VP8 bitstream generated by ChromeOS Intel HW video
-  // encoder.
-  // TODO(crbug.com/955286): Enable once the decoders on the targets are fixed.
-  if (video_config.codec == CODEC_VIDEO_VP8)
-    return false;
-#endif
-
   // TODO(miu): "Layering hooks" are needed to be able to query outside of
   // libmedia, to determine whether the system provides a hardware encoder.  For
   // now, assume that this was already checked by this point.
diff --git a/media/cast/sender/video_encoder_unittest.cc b/media/cast/sender/video_encoder_unittest.cc
index 6eba9d5..743bc7f 100644
--- a/media/cast/sender/video_encoder_unittest.cc
+++ b/media/cast/sender/video_encoder_unittest.cc
@@ -365,16 +365,8 @@
   values.push_back(std::make_pair(CODEC_VIDEO_FAKE, false));
   // Software VP8 encoder.
   values.push_back(std::make_pair(CODEC_VIDEO_VP8, false));
-
-  // VP8 HW encoding on ChromeOS Intel platform is disabled.
-  // See ExternalVideoEncoder::IsSupported().
-  // TODO(crbug.com/955286): Remove this once VP8 HW encoder is enabled on
-  // ChromeOS Intel platform.
-#if !defined(OS_CHROMEOS) || !defined(ARCH_CPU_X86_FAMILY)
   // Hardware-accelerated encoder (faked).
   values.push_back(std::make_pair(CODEC_VIDEO_VP8, true));
-#endif
-
 #if defined(OS_MACOSX)
   // VideoToolbox encoder (when VideoToolbox is present).
   FrameSenderConfig video_config = GetDefaultVideoSenderConfig();
diff --git a/media/cast/sender/video_sender_unittest.cc b/media/cast/sender/video_sender_unittest.cc
index 47b57154..8eadecf 100644
--- a/media/cast/sender/video_sender_unittest.cc
+++ b/media/cast/sender/video_sender_unittest.cc
@@ -14,7 +14,6 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/test/simple_test_tick_clock.h"
-#include "build/build_config.h"
 #include "media/base/fake_single_thread_task_runner.h"
 #include "media/base/video_frame.h"
 #include "media/cast/cast_environment.h"
@@ -42,16 +41,6 @@
 using testing::_;
 using testing::AtLeast;
 
-#if defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_FAMILY)
-// ExternalEncoder::IsFormatSupported() returns false because some target cannot
-// decode correctly VP8 bitstream generated by ChromeOS Intel HW video encoder.
-// Because of this, ExternalEncoder is not created and a VEA factory function is
-// not called. It causes ExternalEncoder tests fail because it expects the VEA
-// factory function is called.
-// TODO(crbug.com/955286): Run ExternalEncoder tests again once VP8 HW encoding
-// on ChromeOS Intel platform is enabled.
-#define DISABLED_EXTERNAL_ENCODER_TESTS
-#endif
 
 void SaveOperationalStatus(OperationalStatus* out_status,
                            OperationalStatus in_status) {
@@ -269,13 +258,7 @@
   EXPECT_LE(1, transport_->number_of_rtcp_packets());
 }
 
-#ifdef DISABLED_EXTERNAL_ENCODER_TESTS
-#define MAYBE_ExternalEncoder DISABLED_ExternalEncoder
-#else
-#define MAYBE_ExternalEncoder ExternalEncoder
-#endif
-
-TEST_F(VideoSenderTest, MAYBE_ExternalEncoder) {
+TEST_F(VideoSenderTest, ExternalEncoder) {
   InitEncoder(true, true);
   ASSERT_EQ(STATUS_INITIALIZED, operational_status_);
 
@@ -314,13 +297,7 @@
   EXPECT_LT(0, vea_factory_.shm_response_count());
 }
 
-#ifdef DISABLED_EXTERNAL_ENCODER_TESTS
-#define MAYBE_ExternalEncoderInitFails DISABLED_ExternalEncoderInitFails
-#else
-#define MAYBE_ExternalEncoderInitFails ExternalEncoderInitFails
-#endif
-
-TEST_F(VideoSenderTest, MAYBE_ExternalEncoderInitFails) {
+TEST_F(VideoSenderTest, ExternalEncoderInitFails) {
   InitEncoder(true, false);
 
   // The SizeAdaptableExternalVideoEncoder initally reports STATUS_INITIALIZED
diff --git a/media/gpu/test/video_player/test_vda_video_decoder.cc b/media/gpu/test/video_player/test_vda_video_decoder.cc
index e88cd0a4..820931a3 100644
--- a/media/gpu/test/video_player/test_vda_video_decoder.cc
+++ b/media/gpu/test/video_player/test_vda_video_decoder.cc
@@ -61,8 +61,11 @@
   // Invalidate all scheduled tasks.
   weak_this_factory_.InvalidateWeakPtrs();
 
-  // Delete all video frames and related textures.
+  // Delete all video frames and related textures and the decoder.
+  frame_renderer_->AcquireGLContext();
   video_frames_.clear();
+  decoder_ = nullptr;
+  frame_renderer_->ReleaseGLContext();
 
   delete this;
 }
diff --git a/media/gpu/vaapi/vaapi_video_decoder.cc b/media/gpu/vaapi/vaapi_video_decoder.cc
index 738bea3..cbf279e 100644
--- a/media/gpu/vaapi/vaapi_video_decoder.cc
+++ b/media/gpu/vaapi/vaapi_video_decoder.cc
@@ -476,7 +476,7 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
   DCHECK_EQ(state_, State::kDecoding);
   DCHECK(video_frame);
-  VLOGF(4);
+  DVLOGF(4);
 
   scoped_refptr<VideoFrame> converted_frame =
       frame_converter_->ConvertFrame(video_frame);
@@ -540,7 +540,7 @@
 
 void VaapiVideoDecoder::NotifyFrameAvailableTask() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
-  VLOGF(4);
+  DVLOGF(4);
 
   // If we were waiting for output buffers, retry the current decode task.
   if (state_ == State::kWaitingForOutput) {
diff --git a/media/gpu/vaapi/vaapi_wrapper.cc b/media/gpu/vaapi/vaapi_wrapper.cc
index 20282ba..f47c240 100644
--- a/media/gpu/vaapi/vaapi_wrapper.cc
+++ b/media/gpu/vaapi/vaapi_wrapper.cc
@@ -139,7 +139,6 @@
 
 constexpr const char* kMesaGalliumDriverPrefix = "Mesa Gallium driver";
 constexpr const char* kIntelI965DriverPrefix = "Intel i965 driver";
-constexpr const char* kIntelIHDDriverPrefix = "Intel iHD driver";
 
 static const struct {
   std::string va_driver;
@@ -1035,19 +1034,6 @@
       i420_format.fourcc = VA_FOURCC_I420;
       supported_formats_.push_back(i420_format);
     }
-  } else if (base::StartsWith(VADisplayState::Get()->va_vendor_string(),
-                              kIntelIHDDriverPrefix,
-                              base::CompareCase::SENSITIVE)) {
-    // TODO(andrescj): for Intel's media driver, remove everything that's not
-    // I420 or NV12 since it is 'over-advertising' the supported formats. Remove
-    // this workaround once https://crbug.com/955284 is fixed.
-    supported_formats_.erase(
-        std::remove_if(supported_formats_.begin(), supported_formats_.end(),
-                       [](const VAImageFormat& format) {
-                         return format.fourcc != VA_FOURCC_I420 &&
-                                format.fourcc != VA_FOURCC_NV12;
-                       }),
-        supported_formats_.end());
   }
   return true;
 }
diff --git a/mojo/public/tools/bindings/chromium_bindings_configuration.gni b/mojo/public/tools/bindings/chromium_bindings_configuration.gni
index d4327c6..3f26ece 100644
--- a/mojo/public/tools/bindings/chromium_bindings_configuration.gni
+++ b/mojo/public/tools/bindings/chromium_bindings_configuration.gni
@@ -22,6 +22,7 @@
   "//device/bluetooth/public/mojom/typemaps.gni",
   "//device/bluetooth/public/mojom/test/typemaps.gni",
   "//device/gamepad/public/cpp/typemaps.gni",
+  "//fuchsia/mojo/test_typemaps.gni",
   "//gpu/ipc/common/typemaps.gni",
   "//ipc/typemaps.gni",
   "//media/capture/mojom/typemaps.gni",
diff --git a/net/cert/cert_verify_proc_unittest.cc b/net/cert/cert_verify_proc_unittest.cc
index 254961d..b5e8795 100644
--- a/net/cert/cert_verify_proc_unittest.cc
+++ b/net/cert/cert_verify_proc_unittest.cc
@@ -250,6 +250,18 @@
          verify_proc_type == CERT_VERIFY_PROC_ANDROID;
 }
 
+// Returns true if a non-self-signed CA certificate added through
+// ScopedTestRoot can verify successfully as the root of a chain by the given
+// CertVerifyProcType.
+bool ScopedTestRootCanTrustIntermediateCert(
+    CertVerifyProcType verify_proc_type) {
+  return verify_proc_type == CERT_VERIFY_PROC_MAC ||
+         verify_proc_type == CERT_VERIFY_PROC_IOS ||
+         verify_proc_type == CERT_VERIFY_PROC_NSS ||
+         verify_proc_type == CERT_VERIFY_PROC_BUILTIN ||
+         verify_proc_type == CERT_VERIFY_PROC_ANDROID;
+}
+
 // TODO(crbug.com/649017): This is not parameterized by the CertVerifyProc
 // because the CertVerifyProc::Verify() does this unconditionally based on the
 // platform.
@@ -1038,6 +1050,99 @@
   EXPECT_FALSE(verify_result.cert_status & CERT_STATUS_IS_EV);
 }
 
+// Target cert has an EV policy, and has a valid path to the EV root, but the
+// intermediate has been trusted directly. Should stop building the path at the
+// intermediate and verify OK but not with STATUS_IS_EV.
+// See https://crbug.com/979801
+TEST_P(CertVerifyProcInternalTest, TrustedIntermediateCertWithEVPolicy) {
+  if (!SupportsEV()) {
+    LOG(INFO) << "Skipping test as EV verification is not yet supported";
+    return;
+  }
+  if (!ScopedTestRootCanTrustIntermediateCert(verify_proc_type())) {
+    LOG(INFO) << "Skipping test as intermediate cert cannot be trusted";
+    return;
+  }
+
+  CertificateList orig_certs = CreateCertificateListFromFile(
+      GetTestCertsDirectory(), "explicit-policy-chain.pem",
+      X509Certificate::FORMAT_AUTO);
+  ASSERT_EQ(3U, orig_certs.size());
+
+  for (bool trust_the_intermediate : {false, true}) {
+    // Need to build unique certs for each try otherwise caching can break
+    // things.
+    CertBuilder root(orig_certs[2]->cert_buffer(), nullptr);
+    CertBuilder intermediate(orig_certs[1]->cert_buffer(), &root);
+    CertBuilder leaf(orig_certs[0]->cert_buffer(), &intermediate);
+
+    // The policy that "explicit-policy-chain.pem" target certificate asserts.
+    static const char kEVTestCertPolicy[] = "1.2.3.4";
+    // Consider the root of the test chain a valid EV root for the test policy.
+    ScopedTestEVPolicy scoped_test_ev_policy(
+        EVRootCAMetadata::GetInstance(),
+        X509Certificate::CalculateFingerprint256(root.GetCertBuffer()),
+        kEVTestCertPolicy);
+
+    // CRLSet which covers the leaf.
+    base::StringPiece intermediate_spki;
+    ASSERT_TRUE(asn1::ExtractSPKIFromDERCert(
+        x509_util::CryptoBufferAsStringPiece(intermediate.GetCertBuffer()),
+        &intermediate_spki));
+    SHA256HashValue intermediate_spki_hash;
+    crypto::SHA256HashString(intermediate_spki, &intermediate_spki_hash,
+                             sizeof(SHA256HashValue));
+    scoped_refptr<CRLSet> crl_set =
+        CRLSet::ForTesting(false, &intermediate_spki_hash, "", "", {});
+
+    std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> intermediates;
+    intermediates.push_back(bssl::UpRef(intermediate.GetCertBuffer()));
+    scoped_refptr<X509Certificate> cert = X509Certificate::CreateFromBuffer(
+        bssl::UpRef(leaf.GetCertBuffer()), std::move(intermediates));
+    ASSERT_TRUE(cert.get());
+
+    scoped_refptr<X509Certificate> intermediate_cert =
+        X509Certificate::CreateFromBuffer(
+            bssl::UpRef(intermediate.GetCertBuffer()), {});
+    ASSERT_TRUE(intermediate_cert.get());
+
+    scoped_refptr<X509Certificate> root_cert =
+        X509Certificate::CreateFromBuffer(bssl::UpRef(root.GetCertBuffer()),
+                                          {});
+    ASSERT_TRUE(root_cert.get());
+
+    if (!trust_the_intermediate) {
+      // First trust just the root. This verifies that the test setup is
+      // actually correct.
+      ScopedTestRoot scoped_test_root({root_cert});
+      CertVerifyResult verify_result;
+      int flags = 0;
+      int error = Verify(cert.get(), "policy_test.example", flags,
+                         crl_set.get(), CertificateList(), &verify_result);
+      EXPECT_THAT(error, IsOk());
+      ASSERT_TRUE(verify_result.verified_cert);
+      // Verified chain should include the intermediate and the root.
+      EXPECT_EQ(2U, verify_result.verified_cert->intermediate_buffers().size());
+      // Should be EV.
+      EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_IS_EV);
+    } else {
+      // Now try with trusting both the intermediate and the root.
+      ScopedTestRoot scoped_test_root({intermediate_cert, root_cert});
+      CertVerifyResult verify_result;
+      int flags = 0;
+      int error = Verify(cert.get(), "policy_test.example", flags,
+                         crl_set.get(), CertificateList(), &verify_result);
+      EXPECT_THAT(error, IsOk());
+      ASSERT_TRUE(verify_result.verified_cert);
+      // Verified chain should only go to the trusted intermediate, not the
+      // root.
+      EXPECT_EQ(1U, verify_result.verified_cert->intermediate_buffers().size());
+      // Should not be EV.
+      EXPECT_FALSE(verify_result.cert_status & CERT_STATUS_IS_EV);
+    }
+  }
+}
+
 // TODO(crbug.com/605457): the test expectation was incorrect on some
 // configurations, so disable the test until it is fixed (better to have
 // a bug to track a failing test than a false sense of security due to
diff --git a/net/cert/test_root_certs.cc b/net/cert/test_root_certs.cc
index f455d18f..2790669 100644
--- a/net/cert/test_root_certs.cc
+++ b/net/cert/test_root_certs.cc
@@ -60,19 +60,23 @@
 ScopedTestRoot::ScopedTestRoot() = default;
 
 ScopedTestRoot::ScopedTestRoot(X509Certificate* cert) {
-  Reset(cert);
+  Reset({cert});
+}
+
+ScopedTestRoot::ScopedTestRoot(CertificateList certs) {
+  Reset(std::move(certs));
 }
 
 ScopedTestRoot::~ScopedTestRoot() {
-  Reset(nullptr);
+  Reset({});
 }
 
-void ScopedTestRoot::Reset(X509Certificate* cert) {
-  if (cert_.get())
+void ScopedTestRoot::Reset(CertificateList certs) {
+  if (!certs_.empty())
     TestRootCerts::GetInstance()->Clear();
-  if (cert)
-    TestRootCerts::GetInstance()->Add(cert);
-  cert_ = cert;
+  for (const auto& cert : certs)
+    TestRootCerts::GetInstance()->Add(cert.get());
+  certs_ = certs;
 }
 
 }  // namespace net
diff --git a/net/cert/test_root_certs.h b/net/cert/test_root_certs.h
index 5315718..c0be7b7 100644
--- a/net/cert/test_root_certs.h
+++ b/net/cert/test_root_certs.h
@@ -32,6 +32,7 @@
 namespace net {
 
 class X509Certificate;
+typedef std::vector<scoped_refptr<X509Certificate>> CertificateList;
 
 // TestRootCerts is a helper class for unit tests that is used to
 // artificially mark a certificate as trusted, independent of the local
@@ -149,17 +150,21 @@
   // TestRootCerts store (if there were existing roots they are
   // cleared).
   explicit ScopedTestRoot(X509Certificate* cert);
+  // Creates a ScopedTestRoot that sets |certs| as the only roots in the
+  // TestRootCerts store (if there were existing roots they are
+  // cleared).
+  explicit ScopedTestRoot(CertificateList certs);
   ~ScopedTestRoot();
 
-  // Assigns |cert| to be the new test root cert. If |cert| is NULL, undoes
+  // Assigns |certs| to be the new test root certs. If |certs| is empty, undoes
   // any work the ScopedTestRoot may have previously done.
-  // If |cert_| contains a certificate (due to a prior call to Reset or due to
-  // a cert being passed at construction), the existing TestRootCerts store is
+  // If |certs_| contains certificates (due to a prior call to Reset or due to
+  // certs being passed at construction), the existing TestRootCerts store is
   // cleared.
-  void Reset(X509Certificate* cert);
+  void Reset(CertificateList certs);
 
  private:
-  scoped_refptr<X509Certificate> cert_;
+  CertificateList certs_;
 
   DISALLOW_COPY_AND_ASSIGN(ScopedTestRoot);
 };
diff --git a/net/quic/crypto_test_utils_chromium.cc b/net/quic/crypto_test_utils_chromium.cc
index f1c2d89f..e63cdc8 100644
--- a/net/quic/crypto_test_utils_chromium.cc
+++ b/net/quic/crypto_test_utils_chromium.cc
@@ -59,7 +59,7 @@
     // Load and install the root for the validated chain.
     scoped_refptr<X509Certificate> root_cert =
         ImportCertFromFile(GetTestCertsDirectory(), cert_file);
-    scoped_root_.Reset(root_cert.get());
+    scoped_root_.Reset({root_cert});
   }
 
   ~TestProofVerifierChromium() override {}
diff --git a/net/quic/platform/impl/quic_flags_impl.cc b/net/quic/platform/impl/quic_flags_impl.cc
index 3cd9d44..d2c719d0 100644
--- a/net/quic/platform/impl/quic_flags_impl.cc
+++ b/net/quic/platform/impl/quic_flags_impl.cc
@@ -203,7 +203,7 @@
   }
 
   logging::LoggingSettings settings;
-  settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
+  settings.logging_dest = logging::LOG_TO_STDERR;
   CHECK(logging::InitLogging(settings));
 
   return result.non_flag_args;
diff --git a/net/test/spawned_test_server/remote_test_server_spawner_request.cc b/net/test/spawned_test_server/remote_test_server_spawner_request.cc
index 3ca8305..91aacd5 100644
--- a/net/test/spawned_test_server/remote_test_server_spawner_request.cc
+++ b/net/test/spawned_test_server/remote_test_server_spawner_request.cc
@@ -22,6 +22,7 @@
 #include "net/base/port_util.h"
 #include "net/base/upload_bytes_element_reader.h"
 #include "net/http/http_response_headers.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_test_util.h"
 #include "url/gurl.h"
@@ -85,7 +86,8 @@
   // Prepare the URLRequest for sending the command.
   DCHECK(!request_.get());
   context_.reset(new TestURLRequestContext);
-  request_ = context_->CreateRequest(url, DEFAULT_PRIORITY, this);
+  request_ = context_->CreateRequest(url, DEFAULT_PRIORITY, this,
+                                     TRAFFIC_ANNOTATION_FOR_TESTS);
 
   if (post_data.empty()) {
     request_->set_method("GET");
diff --git a/net/tools/quic/quic_http_proxy_backend_stream.cc b/net/tools/quic/quic_http_proxy_backend_stream.cc
index 9aecf83..cdd598cc 100644
--- a/net/tools/quic/quic_http_proxy_backend_stream.cc
+++ b/net/tools/quic/quic_http_proxy_backend_stream.cc
@@ -12,6 +12,7 @@
 #include "net/http/http_status_code.h"
 #include "net/http/http_util.h"
 #include "net/ssl/ssl_info.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
 #include "net/url_request/redirect_info.h"
 #include "net/url_request/url_request_context.h"
 
@@ -188,7 +189,7 @@
 void QuicHttpProxyBackendStream::SendRequestOnBackendThread() {
   DCHECK(quic_proxy_task_runner_->BelongsToCurrentThread());
   url_request_ = proxy_context_->GetURLRequestContext()->CreateRequest(
-      url_, net::DEFAULT_PRIORITY, this);
+      url_, net::DEFAULT_PRIORITY, this, MISSING_TRAFFIC_ANNOTATION);
   url_request_->set_method(method_type_);
   url_request_->SetExtraRequestHeaders(request_headers_);
   if (upload_) {
diff --git a/net/traffic_annotation/network_traffic_annotation.h b/net/traffic_annotation/network_traffic_annotation.h
index a4f0b8f..6319a81 100644
--- a/net/traffic_annotation/network_traffic_annotation.h
+++ b/net/traffic_annotation/network_traffic_annotation.h
@@ -361,12 +361,20 @@
   net::DefineNetworkTrafficAnnotation(ANNOTATION_ID, "No proto yet.")
 #endif
 
+// These annotations are unavailable on desktop Linux + Windows. They are
+// available on other platforms, since we only audit network annotations on
+// Linux & Windows.
+//
+// On Linux and Windows, use MISSING_TRAFFIC_ANNOTATION or
+// TRAFFIC_ANNOTATION_FOR_TESTS.
+#if (!defined(OS_WIN) && !defined(OS_LINUX)) || defined(OS_CHROMEOS)
 #define NO_TRAFFIC_ANNOTATION_YET \
   net::DefineNetworkTrafficAnnotation("undefined", "Nothing here yet.")
 
 #define NO_PARTIAL_TRAFFIC_ANNOTATION_YET                              \
   net::DefinePartialNetworkTrafficAnnotation("undefined", "undefined", \
                                              "Nothing here yet.")
+#endif
 
 #define MISSING_TRAFFIC_ANNOTATION     \
   net::DefineNetworkTrafficAnnotation( \
diff --git a/net/url_request/url_fetcher.cc b/net/url_request/url_fetcher.cc
index e815b57..331420f 100644
--- a/net/url_request/url_fetcher.cc
+++ b/net/url_request/url_fetcher.cc
@@ -11,6 +11,7 @@
 
 URLFetcher::~URLFetcher() = default;
 
+#if (!defined(OS_WIN) && !defined(OS_LINUX)) || defined(OS_CHROMEOS)
 // static
 std::unique_ptr<URLFetcher> URLFetcher::Create(
     const GURL& url,
@@ -27,6 +28,7 @@
     URLFetcherDelegate* d) {
   return Create(id, url, request_type, d, MISSING_TRAFFIC_ANNOTATION);
 }
+#endif
 
 // static
 std::unique_ptr<URLFetcher> URLFetcher::Create(
diff --git a/net/url_request/url_fetcher.h b/net/url_request/url_fetcher.h
index fc8d1d1..6579924 100644
--- a/net/url_request/url_fetcher.h
+++ b/net/url_request/url_fetcher.h
@@ -14,6 +14,7 @@
 #include "base/callback_forward.h"
 #include "base/memory/ref_counted.h"
 #include "base/supports_user_data.h"
+#include "build/build_config.h"
 #include "net/base/ip_endpoint.h"
 #include "net/base/net_export.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
@@ -109,6 +110,10 @@
 
   virtual ~URLFetcher();
 
+  // The unannotated Create() methods are not available on desktop Linux +
+  // Windows. They are available on other platforms, since we only audit network
+  // annotations on Linux & Windows.
+#if (!defined(OS_WIN) && !defined(OS_LINUX)) || defined(OS_CHROMEOS)
   // |url| is the URL to send the request to. It must be valid.
   // |request_type| is the type of request to make.
   // |d| the object that will receive the callback on fetch completion.
@@ -129,6 +134,7 @@
       const GURL& url,
       URLFetcher::RequestType request_type,
       URLFetcherDelegate* d);
+#endif
 
   // |url| is the URL to send the request to. It must be valid.
   // |request_type| is the type of request to make.
diff --git a/net/url_request/url_request_context.cc b/net/url_request/url_request_context.cc
index 7f93577..4b7b5547 100644
--- a/net/url_request/url_request_context.cc
+++ b/net/url_request/url_request_context.cc
@@ -89,12 +89,14 @@
   return &network_session->context();
 }
 
+#if (!defined(OS_WIN) && !defined(OS_LINUX)) || defined(OS_CHROMEOS)
 std::unique_ptr<URLRequest> URLRequestContext::CreateRequest(
     const GURL& url,
     RequestPriority priority,
     URLRequest::Delegate* delegate) const {
   return CreateRequest(url, priority, delegate, MISSING_TRAFFIC_ANNOTATION);
 }
+#endif
 
 std::unique_ptr<URLRequest> URLRequestContext::CreateRequest(
     const GURL& url,
diff --git a/net/url_request/url_request_context.h b/net/url_request/url_request_context.h
index 6199f94..41663f7 100644
--- a/net/url_request/url_request_context.h
+++ b/net/url_request/url_request_context.h
@@ -17,6 +17,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/threading/thread_checker.h"
 #include "base/trace_event/memory_dump_provider.h"
+#include "build/build_config.h"
 #include "net/base/net_export.h"
 #include "net/base/request_priority.h"
 #include "net/http/http_network_session.h"
@@ -89,12 +90,18 @@
   // session.
   const HttpNetworkSession::Context* GetNetworkSessionContext() const;
 
+#if (!defined(OS_WIN) && !defined(OS_LINUX)) || defined(OS_CHROMEOS)
   // This function should not be used in Chromium, please use the version with
   // NetworkTrafficAnnotationTag in the future.
+  //
+  // The unannotated method is not available on desktop Linux + Windows. It's
+  // available on other platforms, since we only audit network annotations on
+  // Linux & Windows.
   std::unique_ptr<URLRequest> CreateRequest(
       const GURL& url,
       RequestPriority priority,
       URLRequest::Delegate* delegate) const;
+#endif
 
   // |traffic_annotation| is metadata about the network traffic send via this
   // URLRequest, see net::DefineNetworkTrafficAnnotation. Note that:
diff --git a/remoting/host/ftl_signaling_connector.cc b/remoting/host/ftl_signaling_connector.cc
index 1a745ca..0c4a8f9 100644
--- a/remoting/host/ftl_signaling_connector.cc
+++ b/remoting/host/ftl_signaling_connector.cc
@@ -19,6 +19,35 @@
 
 namespace {
 
+constexpr base::TimeDelta kBackoffResetDelay = base::TimeDelta::FromSeconds(30);
+constexpr base::TimeDelta kNetworkChangeDelay = base::TimeDelta::FromSeconds(5);
+
+const net::BackoffEntry::Policy kBackoffPolicy = {
+    // Number of initial errors (in sequence) to ignore before applying
+    // exponential back-off rules.
+    0,
+
+    // Initial delay for exponential back-off in ms. (1s)
+    1000,
+
+    // Factor by which the waiting time will be multiplied.
+    2,
+
+    // Fuzzing percentage. ex: 10% will spread requests randomly
+    // between 90%-100% of the calculated time.
+    0.5,
+
+    // Maximum amount of time we are willing to delay our request in ms. (1m)
+    60000,
+
+    // Time to keep an entry from being discarded even when it
+    // has no significant state, -1 to never discard.
+    -1,
+
+    // Starts with initial delay.
+    false,
+};
+
 const char* SignalStrategyErrorToString(SignalStrategy::Error error) {
   switch (error) {
     case SignalStrategy::OK:
@@ -39,7 +68,8 @@
     FtlSignalStrategy* signal_strategy,
     base::OnceClosure auth_failed_callback)
     : signal_strategy_(signal_strategy),
-      auth_failed_callback_(std::move(auth_failed_callback)) {
+      auth_failed_callback_(std::move(auth_failed_callback)),
+      backoff_(&kBackoffPolicy) {
   DCHECK(signal_strategy_);
   DCHECK(auth_failed_callback_);
   net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
@@ -53,7 +83,7 @@
 }
 
 void FtlSignalingConnector::Start() {
-  TryReconnect();
+  TryReconnect(base::TimeDelta());
 }
 
 void FtlSignalingConnector::OnSignalStrategyStateChange(
@@ -63,15 +93,19 @@
   if (state == SignalStrategy::CONNECTED) {
     HOST_LOG << "Signaling connected. New JID: "
              << signal_strategy_->GetLocalAddress().jid();
+    backoff_reset_timer_.Start(FROM_HERE, kBackoffResetDelay, &backoff_,
+                               &net::BackoffEntry::Reset);
   } else if (state == SignalStrategy::DISCONNECTED) {
     HOST_LOG << "Signaling disconnected. error="
              << SignalStrategyErrorToString(signal_strategy_->GetError());
+    backoff_reset_timer_.AbandonAndStop();
+    backoff_.InformOfRequest(false);
     if (signal_strategy_->IsSignInError() &&
         signal_strategy_->GetError() == SignalStrategy::AUTHENTICATION_FAILED) {
       std::move(auth_failed_callback_).Run();
       return;
     }
-    TryReconnect();
+    TryReconnect(backoff_.GetTimeUntilRelease());
   }
 }
 
@@ -86,14 +120,13 @@
   if (type != net::NetworkChangeNotifier::CONNECTION_NONE &&
       signal_strategy_->GetState() == SignalStrategy::DISCONNECTED) {
     HOST_LOG << "Network state changed to online.";
-    TryReconnect();
+    TryReconnect(kNetworkChangeDelay);
   }
 }
 
-void FtlSignalingConnector::TryReconnect() {
+void FtlSignalingConnector::TryReconnect(base::TimeDelta delay) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  timer_.Start(FROM_HERE, base::TimeDelta(), this,
-               &FtlSignalingConnector::DoReconnect);
+  timer_.Start(FROM_HERE, delay, this, &FtlSignalingConnector::DoReconnect);
 }
 
 void FtlSignalingConnector::DoReconnect() {
diff --git a/remoting/host/ftl_signaling_connector.h b/remoting/host/ftl_signaling_connector.h
index d930465..3dbb1b2 100644
--- a/remoting/host/ftl_signaling_connector.h
+++ b/remoting/host/ftl_signaling_connector.h
@@ -8,6 +8,7 @@
 #include "base/macros.h"
 #include "base/sequence_checker.h"
 #include "base/timer/timer.h"
+#include "net/base/backoff_entry.h"
 #include "net/base/network_change_notifier.h"
 #include "remoting/signaling/ftl_signal_strategy.h"
 
@@ -43,14 +44,19 @@
       net::NetworkChangeNotifier::ConnectionType type) override;
 
  private:
-  void TryReconnect();
+  void TryReconnect(base::TimeDelta delay);
   void DoReconnect();
 
   FtlSignalStrategy* signal_strategy_;
   base::OnceClosure auth_failed_callback_;
 
+  net::BackoffEntry backoff_;
   base::OneShotTimer timer_;
 
+  // Timer to reset |backoff_|. We delay resetting the backoff so that we can
+  // treat an immediate CONNECTED->DISCONNECTED transition as failure.
+  base::OneShotTimer backoff_reset_timer_;
+
   SEQUENCE_CHECKER(sequence_checker_);
 
   DISALLOW_COPY_AND_ASSIGN(FtlSignalingConnector);
diff --git a/remoting/host/signaling_connector.cc b/remoting/host/signaling_connector.cc
index 397ea44..ffe1cee 100644
--- a/remoting/host/signaling_connector.cc
+++ b/remoting/host/signaling_connector.cc
@@ -20,6 +20,8 @@
 
 namespace {
 
+constexpr base::TimeDelta kBackoffResetDelay = base::TimeDelta::FromSeconds(30);
+
 // The delay between reconnect attempts will increase exponentially up
 // to the maximum specified here.
 const int kMaxReconnectDelaySeconds = 10 * 60;
@@ -72,10 +74,12 @@
   if (state == SignalStrategy::CONNECTED) {
     HOST_LOG << "Signaling connected. New JID: "
              << signal_strategy_->GetLocalAddress().jid();
-    reconnect_attempts_ = 0;
+    backoff_reset_timer_.Start(FROM_HERE, kBackoffResetDelay, this,
+                               &SignalingConnector::ResetBackoff);
   } else if (state == SignalStrategy::DISCONNECTED) {
     HOST_LOG << "Signaling disconnected. error="
              << SignalStrategyErrorToString(signal_strategy_->GetError());
+    backoff_reset_timer_.Stop();
     reconnect_attempts_++;
 
     if (signal_strategy_->GetError() == SignalStrategy::AUTHENTICATION_FAILED)
@@ -143,7 +147,7 @@
 void SignalingConnector::ResetAndTryReconnect() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   signal_strategy_->Disconnect();
-  reconnect_attempts_ = 0;
+  ResetBackoff();
   timer_.Stop();
   ScheduleTryReconnect();
 }
@@ -180,4 +184,9 @@
   }
 }
 
+void SignalingConnector::ResetBackoff() {
+  backoff_reset_timer_.Stop();
+  reconnect_attempts_ = 0;
+}
+
 }  // namespace remoting
diff --git a/remoting/host/signaling_connector.h b/remoting/host/signaling_connector.h
index 3ece5ec..f0d78f3 100644
--- a/remoting/host/signaling_connector.h
+++ b/remoting/host/signaling_connector.h
@@ -53,6 +53,7 @@
   void ResetAndTryReconnect();
   void TryReconnect();
   void OnDnsBlackholeCheckerDone(bool allow);
+  void ResetBackoff();
 
   XmppSignalStrategy* signal_strategy_;
   base::Closure auth_failed_callback_;
@@ -65,6 +66,10 @@
 
   base::OneShotTimer timer_;
 
+  // Timer to reset |backoff_|. We delay resetting the backoff so that we can
+  // treat an immediate CONNECTED->DISCONNECTED transition as failure.
+  base::OneShotTimer backoff_reset_timer_;
+
   SEQUENCE_CHECKER(sequence_checker_);
 
   base::WeakPtrFactory<SignalingConnector> weak_factory_;
diff --git a/remoting/host/token_validator_factory_impl.cc b/remoting/host/token_validator_factory_impl.cc
index 6d2a066d..b209068 100644
--- a/remoting/host/token_validator_factory_impl.cc
+++ b/remoting/host/token_validator_factory_impl.cc
@@ -89,7 +89,7 @@
 
   request_ = request_context_getter_->GetURLRequestContext()->CreateRequest(
       third_party_auth_config_.token_validation_url, net::DEFAULT_PRIORITY,
-      this);
+      this, MISSING_TRAFFIC_ANNOTATION);
 
 #if defined(GOOGLE_CHROME_BUILD)
   std::string app_name = "Chrome Remote Desktop";
diff --git a/remoting/protocol/webrtc_frame_scheduler_simple.cc b/remoting/protocol/webrtc_frame_scheduler_simple.cc
index eb7f245f..db58984 100644
--- a/remoting/protocol/webrtc_frame_scheduler_simple.cc
+++ b/remoting/protocol/webrtc_frame_scheduler_simple.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 
 #include "base/bind.h"
+#include "base/numerics/ranges.h"
 #include "base/time/default_tick_clock.h"
 #include "remoting/base/constants.h"
 #include "remoting/protocol/frame_stats.h"
@@ -275,8 +276,17 @@
 
     // Ensure that the capture rate is capped by kTargetFrameInterval, to avoid
     // excessive CPU usage by the capturer.
-    target_capture_time = std::max(
-        target_capture_time, last_capture_started_time_ + kTargetFrameInterval);
+    // Also ensure that the video does not freeze for excessively long periods.
+    // This protects against, for example, bugs in the b/w estimator or the
+    // LeakyBucket implementation which may result in unbounded wait times.
+    // If the network is such that it really takes > 2 or 3 seconds to send one
+    // video frame, then this upper-bound cap could result in packet-loss,
+    // triggering PLI (key-frame request). But the session would be already
+    // unusable under such network conditions. And the client would trigger PLI
+    // anyway if it doesn't receive any video for > 3 seconds.
+    target_capture_time = base::ClampToRange(
+        target_capture_time, last_capture_started_time_ + kTargetFrameInterval,
+        last_capture_started_time_ + kKeepAliveInterval);
   }
 
   target_capture_time = std::max(target_capture_time, now);
diff --git a/services/audio/sync_reader.cc b/services/audio/sync_reader.cc
index ed3b6a9..17889fb4 100644
--- a/services/audio/sync_reader.cc
+++ b/services/audio/sync_reader.cc
@@ -218,8 +218,12 @@
     }
     output_bus_->SetBitstreamDataSize(data_size);
     output_bus_->SetBitstreamFrames(bitstream_frames);
+    output_bus_->CopyTo(dest);
+    return;
   }
-  output_bus_->CopyTo(dest);
+
+  // Copy and clip data coming across the shared memory since it's untrusted.
+  output_bus_->CopyAndClipTo(dest);
 }
 
 void SyncReader::Close() {
diff --git a/services/device/usb/usb_device_handle_usbfs.cc b/services/device/usb/usb_device_handle_usbfs.cc
index 0204bd6..cc3d8e46 100644
--- a/services/device/usb/usb_device_handle_usbfs.cc
+++ b/services/device/usb/usb_device_handle_usbfs.cc
@@ -450,6 +450,8 @@
   // see if the handle is closed.
   device_->HandleClosed(this);
   device_ = nullptr;
+  // The device is no longer attached so we don't have any endpoints either.
+  endpoints_.clear();
 
   // Releases |helper_|.
   blocking_task_runner_->PostTask(
@@ -746,7 +748,10 @@
   auto it = interfaces_.find(interface_number);
   DCHECK(it != interfaces_.end());
   interfaces_.erase(it);
-  RefreshEndpointInfo();
+  if (device_) {
+    // Only refresh endpoints if a device is still attached.
+    RefreshEndpointInfo();
+  }
   std::move(callback).Run(true);
 }
 
@@ -853,6 +858,7 @@
 
 void UsbDeviceHandleUsbfs::RefreshEndpointInfo() {
   DCHECK(sequence_checker_.CalledOnValidSequence());
+  DCHECK(device_);
   endpoints_.clear();
 
   const UsbConfigDescriptor* config = device_->active_configuration();
diff --git a/services/identity/public/cpp/access_token_fetcher_unittest.cc b/services/identity/public/cpp/access_token_fetcher_unittest.cc
index fa0942691..af9f08d 100644
--- a/services/identity/public/cpp/access_token_fetcher_unittest.cc
+++ b/services/identity/public/cpp/access_token_fetcher_unittest.cc
@@ -553,7 +553,7 @@
 
   // The URLLoaderFactory present in the pending request should match
   // the one we specified when creating the AccessTokenFetcher.
-  std::vector<FakeProfileOAuth2TokenService::PendingRequest> pending_requests =
+  std::vector<FakeOAuth2AccessTokenManager::PendingRequest> pending_requests =
       token_service()->GetPendingRequests();
 
   EXPECT_EQ(pending_requests.size(), 1U);
@@ -588,7 +588,7 @@
   run_loop2.Run();
 
   // There should be one pending request in this case too.
-  std::vector<FakeProfileOAuth2TokenService::PendingRequest> pending_requests2 =
+  std::vector<FakeOAuth2AccessTokenManager::PendingRequest> pending_requests2 =
       token_service()->GetPendingRequests();
   EXPECT_EQ(pending_requests2.size(), 1U);
 
diff --git a/services/identity/public/cpp/identity_manager_unittest.cc b/services/identity/public/cpp/identity_manager_unittest.cc
index 8ff3d28..2e2561b 100644
--- a/services/identity/public/cpp/identity_manager_unittest.cc
+++ b/services/identity/public/cpp/identity_manager_unittest.cc
@@ -1065,7 +1065,7 @@
 
   // The URLLoaderFactory present in the pending request should match
   // the one we specified when creating the AccessTokenFetcher.
-  std::vector<FakeProfileOAuth2TokenService::PendingRequest> pending_requests =
+  std::vector<FakeOAuth2AccessTokenManager::PendingRequest> pending_requests =
       token_service()->GetPendingRequests();
   EXPECT_EQ(pending_requests.size(), 1U);
   EXPECT_EQ(pending_requests[0].url_loader_factory,
@@ -1105,7 +1105,7 @@
   run_loop2.Run();
 
   // There should be one pending request now as well, just like before.
-  std::vector<FakeProfileOAuth2TokenService::PendingRequest> pending_requests2 =
+  std::vector<FakeOAuth2AccessTokenManager::PendingRequest> pending_requests2 =
       token_service()->GetPendingRequests();
   EXPECT_EQ(pending_requests2.size(), 1U);
 
diff --git a/services/network/network_service_proxy_delegate_unittest.cc b/services/network/network_service_proxy_delegate_unittest.cc
index 5a30ebd..d422b32 100644
--- a/services/network/network_service_proxy_delegate_unittest.cc
+++ b/services/network/network_service_proxy_delegate_unittest.cc
@@ -7,6 +7,7 @@
 #include <string>
 
 #include "base/test/scoped_task_environment.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_request_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -50,7 +51,8 @@
   }
 
   std::unique_ptr<net::URLRequest> CreateRequest(const GURL& url) {
-    return context_->CreateRequest(url, net::DEFAULT_PRIORITY, nullptr);
+    return context_->CreateRequest(url, net::DEFAULT_PRIORITY, nullptr,
+                                   TRAFFIC_ANNOTATION_FOR_TESTS);
   }
 
   void SetConfig(mojom::CustomProxyConfigPtr config) {
diff --git a/services/tracing/perfetto/system_perfetto_unittest.cc b/services/tracing/perfetto/system_perfetto_unittest.cc
index 899bae8..0e628c23 100644
--- a/services/tracing/perfetto/system_perfetto_unittest.cc
+++ b/services/tracing/perfetto/system_perfetto_unittest.cc
@@ -114,6 +114,7 @@
   void RunUntilIdle() { scoped_task_environment_.RunUntilIdle(); }
 
  protected:
+  // |tmp_dir_| must be destroyed last. So must be declared first.
   base::ScopedTempDir tmp_dir_;
   std::string producer_socket_;
   std::string consumer_socket_;
@@ -122,8 +123,7 @@
   base::test::ScopedTaskEnvironment scoped_task_environment_;
 };
 
-// https://crbug.com/980247, crash on Android M.
-TEST_F(SystemPerfettoTest, DISABLED_SystemTraceEndToEnd) {
+TEST_F(SystemPerfettoTest, SystemTraceEndToEnd) {
   auto system_service = CreateMockSystemService();
 
   // Set up the producer to talk to the system.
@@ -153,8 +153,7 @@
   PerfettoProducer::DeleteSoonForTesting(std::move(system_producer));
 }
 
-// https://crbug.com/980247, crash on Android M.
-TEST_F(SystemPerfettoTest, DISABLED_OneSystemSourceWithMultipleLocalSources) {
+TEST_F(SystemPerfettoTest, OneSystemSourceWithMultipleLocalSources) {
   auto system_service = CreateMockSystemService();
 
   // Start a trace using the system Perfetto service.
@@ -242,9 +241,8 @@
   PerfettoProducer::DeleteSoonForTesting(std::move(system_producer));
 }
 
-// https://crbug.com/980247, crash on Android M.
 TEST_F(SystemPerfettoTest,
-       DISABLED_MultipleSystemSourceWithOneLocalSourcesLocalFirst) {
+       MultipleSystemSourceWithOneLocalSourcesLocalFirst) {
   auto system_service = CreateMockSystemService();
 
   base::RunLoop local_no_more_packets_runloop;
@@ -339,8 +337,7 @@
   PerfettoProducer::DeleteSoonForTesting(std::move(system_producer));
 }
 
-// https://crbug.com/980247, crash on Android M.
-TEST_F(SystemPerfettoTest, DISABLED_MultipleSystemAndLocalSources) {
+TEST_F(SystemPerfettoTest, MultipleSystemAndLocalSources) {
   auto system_service = CreateMockSystemService();
 
   // Start a trace using the system Perfetto service.
@@ -431,8 +428,7 @@
   PerfettoProducer::DeleteSoonForTesting(std::move(system_producer));
 }
 
-// https://crbug.com/980247, crash on Android M.
-TEST_F(SystemPerfettoTest, DISABLED_MultipleSystemAndLocalSourcesLocalFirst) {
+TEST_F(SystemPerfettoTest, MultipleSystemAndLocalSourcesLocalFirst) {
   auto system_service = CreateMockSystemService();
 
   // We construct it up front so it connects to the service before the local
@@ -519,8 +515,7 @@
   PerfettoProducer::DeleteSoonForTesting(std::move(system_producer));
 }
 
-// https://crbug.com/980247, crash on Android M.
-TEST_F(SystemPerfettoTest, DISABLED_SystemToLowAPILevel) {
+TEST_F(SystemPerfettoTest, SystemToLowAPILevel) {
   if (base::android::BuildInfo::GetInstance()->sdk_int() >=
       base::android::SDK_VERSION_P) {
     LOG(INFO) << "Skipping SystemToLowAPILevel test, this phone supports the "
diff --git a/services/tracing/perfetto/system_test_utils.cc b/services/tracing/perfetto/system_test_utils.cc
index d077c91e..830adfb 100644
--- a/services/tracing/perfetto/system_test_utils.cc
+++ b/services/tracing/perfetto/system_test_utils.cc
@@ -21,9 +21,11 @@
       task_runner_(std::make_unique<PerfettoTaskRunner>(
           base::SequencedTaskRunnerHandle::Get())) {
   service_ = perfetto::ServiceIPCHost::CreateInstance(task_runner_.get());
+  DCHECK(service_);
   unlink(producer_socket.c_str());
   unlink(consumer_socket.c_str());
-  DCHECK(service_->Start(producer_.c_str(), consumer_.c_str()));
+  bool succeeded = service_->Start(producer_.c_str(), consumer_.c_str());
+  DCHECK(succeeded);
 }
 
 MockSystemService::~MockSystemService() {
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index 833d317..5a991d4 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -20147,7 +20147,6 @@
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
-        "experiment_percentage": 100,
         "merge": {
           "args": [
             "--bucket",
@@ -22946,6 +22945,50 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
+            "android_browsertests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "MMB29Q",
+              "device_os_type": "userdebug",
+              "device_type": "bullhead",
+              "os": "Android"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "android_browsertests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
             "android_webview_unittests"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
diff --git a/testing/buildbot/chromium.chrome.json b/testing/buildbot/chromium.chrome.json
deleted file mode 100644
index 81fceb6..0000000
--- a/testing/buildbot/chromium.chrome.json
+++ /dev/null
@@ -1,2225 +0,0 @@
-{
-  "AAAAA1 AUTOGENERATED FILE DO NOT EDIT": {},
-  "AAAAA2 See generate_buildbot_json.py to make changes": {},
-  "chromeos-amd64-generic-google-rel": {
-    "additional_compile_targets": [
-      "chrome"
-    ]
-  },
-  "chromeos-betty-google-rel": {
-    "additional_compile_targets": [
-      "chromiumos_preflight"
-    ],
-    "gtest_tests": [
-      {
-        "args": [
-          "--ozone-platform=headless"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "kvm": "1",
-              "os": "Ubuntu-16.04",
-              "pool": "chrome.tests.cros-vm"
-            }
-          ]
-        },
-        "test": "aura_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "kvm": "1",
-              "os": "Ubuntu-16.04",
-              "pool": "chrome.tests.cros-vm"
-            }
-          ]
-        },
-        "test": "base_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "kvm": "1",
-              "os": "Ubuntu-16.04",
-              "pool": "chrome.tests.cros-vm"
-            }
-          ]
-        },
-        "test": "cacheinvalidation_unittests"
-      },
-      {
-        "args": [
-          "--test-launcher-jobs=1",
-          "--gtest_filter=-*UsingRealWebcam_CaptureMjpeg*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "kvm": "1",
-              "os": "Ubuntu-16.04",
-              "pool": "chrome.tests.cros-vm"
-            }
-          ]
-        },
-        "test": "capture_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "kvm": "1",
-              "os": "Ubuntu-16.04",
-              "pool": "chrome.tests.cros-vm"
-            }
-          ]
-        },
-        "test": "cc_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "kvm": "1",
-              "os": "Ubuntu-16.04",
-              "pool": "chrome.tests.cros-vm"
-            }
-          ],
-          "idempotent": false
-        },
-        "test": "chrome_all_tast_tests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "kvm": "1",
-              "os": "Ubuntu-16.04",
-              "pool": "chrome.tests.cros-vm"
-            }
-          ]
-        },
-        "test": "cros_vm_sanity_test"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "kvm": "1",
-              "os": "Ubuntu-16.04",
-              "pool": "chrome.tests.cros-vm"
-            }
-          ]
-        },
-        "test": "crypto_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "kvm": "1",
-              "os": "Ubuntu-16.04",
-              "pool": "chrome.tests.cros-vm"
-            }
-          ]
-        },
-        "test": "display_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "kvm": "1",
-              "os": "Ubuntu-16.04",
-              "pool": "chrome.tests.cros-vm"
-            }
-          ]
-        },
-        "test": "google_apis_unittests"
-      },
-      {
-        "args": [
-          "--stop-ui",
-          "--dbus-stub",
-          "--gtest_filter=SplitViewTest.SplitViewResize"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "kvm": "1",
-              "os": "Ubuntu-16.04",
-              "pool": "chrome.tests.cros-vm"
-            }
-          ]
-        },
-        "test": "interactive_ui_tests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "kvm": "1",
-              "os": "Ubuntu-16.04",
-              "pool": "chrome.tests.cros-vm"
-            }
-          ]
-        },
-        "test": "ipc_tests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "kvm": "1",
-              "os": "Ubuntu-16.04",
-              "pool": "chrome.tests.cros-vm"
-            }
-          ]
-        },
-        "test": "jingle_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "kvm": "1",
-              "os": "Ubuntu-16.04",
-              "pool": "chrome.tests.cros-vm"
-            }
-          ]
-        },
-        "test": "latency_unittests"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/chromeos.media_unittests.filter"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "kvm": "1",
-              "os": "Ubuntu-16.04",
-              "pool": "chrome.tests.cros-vm"
-            }
-          ]
-        },
-        "test": "media_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "kvm": "1",
-              "os": "Ubuntu-16.04",
-              "pool": "chrome.tests.cros-vm"
-            }
-          ]
-        },
-        "test": "midi_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "kvm": "1",
-              "os": "Ubuntu-16.04",
-              "pool": "chrome.tests.cros-vm"
-            }
-          ]
-        },
-        "test": "mojo_unittests"
-      },
-      {
-        "args": [
-          "--vpython-dir=../../vpython_dir_linux_amd64"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/vpython/linux-amd64",
-              "location": "vpython_dir_linux_amd64",
-              "revision": "git_revision:9a931a5307c46b16b1c12e01e8239d4a73830b89"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "kvm": "1",
-              "os": "Ubuntu-16.04",
-              "pool": "chrome.tests.cros-vm"
-            }
-          ],
-          "shards": 3
-        },
-        "test": "net_unittests"
-      },
-      {
-        "args": [
-          "--stop-ui"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "kvm": "1",
-              "os": "Ubuntu-16.04",
-              "pool": "chrome.tests.cros-vm"
-            }
-          ]
-        },
-        "test": "ozone_gl_unittests"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/chromeos.ozone_unittests.filter"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "kvm": "1",
-              "os": "Ubuntu-16.04",
-              "pool": "chrome.tests.cros-vm"
-            }
-          ]
-        },
-        "test": "ozone_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "kvm": "1",
-              "os": "Ubuntu-16.04",
-              "pool": "chrome.tests.cros-vm"
-            }
-          ]
-        },
-        "test": "pdf_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "kvm": "1",
-              "os": "Ubuntu-16.04",
-              "pool": "chrome.tests.cros-vm"
-            }
-          ]
-        },
-        "test": "printing_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "kvm": "1",
-              "os": "Ubuntu-16.04",
-              "pool": "chrome.tests.cros-vm"
-            }
-          ]
-        },
-        "test": "sandbox_linux_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "kvm": "1",
-              "os": "Ubuntu-16.04",
-              "pool": "chrome.tests.cros-vm"
-            }
-          ]
-        },
-        "test": "sql_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "kvm": "1",
-              "os": "Ubuntu-16.04",
-              "pool": "chrome.tests.cros-vm"
-            }
-          ]
-        },
-        "test": "url_unittests"
-      }
-    ],
-    "isolated_scripts": [
-      {
-        "args": [
-          "--browser=cros-chrome",
-          "--remote=127.0.0.1",
-          "--remote-ssh-port=9222",
-          "--xvfb"
-        ],
-        "isolate_name": "telemetry_perf_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "telemetry_perf_unittests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "kvm": "1",
-              "os": "Ubuntu-16.04",
-              "pool": "chrome.tests.cros-vm"
-            }
-          ],
-          "idempotent": false,
-          "shards": 6
-        }
-      },
-      {
-        "args": [
-          "--jobs=1",
-          "--browser=cros-chrome",
-          "--remote=127.0.0.1",
-          "--remote-ssh-port=9222"
-        ],
-        "isolate_name": "telemetry_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "telemetry_unittests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "kvm": "1",
-              "os": "Ubuntu-16.04",
-              "pool": "chrome.tests.cros-vm"
-            }
-          ],
-          "idempotent": false,
-          "shards": 24
-        }
-      }
-    ]
-  },
-  "linux-chromeos-google-rel": {
-    "additional_compile_targets": [
-      "chrome",
-      "chrome_sandbox",
-      "linux_symbols",
-      "symupload"
-    ],
-    "gtest_tests": [
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "accessibility_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "angle_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "app_list_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "app_shell_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "ash_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "aura_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "base_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "base_util_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "blink_common_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "blink_fuzzer_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "blink_heap_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "blink_platform_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "webkit_unit_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "blink_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "boringssl_crypto_tests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "boringssl_ssl_tests"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/chromeos.browser_tests.filter"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ],
-          "shards": 10
-        },
-        "test": "browser_tests"
-      },
-      {
-        "args": [
-          "--enable-features=VizDisplayCompositor",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/chromeos.browser_tests.filter"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "viz_browser_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ],
-          "shards": 10
-        },
-        "test": "browser_tests"
-      },
-      {
-        "args": [
-          "--disable-blink-features=HTMLImports,ShadowDOMV0,CustomElementsV0",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/webui_html_imports_polyfill_browser_tests.filter"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "webui_html_imports_polyfill_browser_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "browser_tests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "cacheinvalidation_unittests"
-      },
-      {
-        "args": [
-          "--gtest_filter=-*UsingRealWebcam*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "capture_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "cast_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "cc_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "chrome_app_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "chromedriver_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "chromeos_components_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "chromeos_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "components_browsertests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "components_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "compositor_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ],
-          "shards": 6
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
-          "--enable-features=VizDisplayCompositor"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "viz_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ],
-          "shards": 10
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "content_unittests"
-      },
-      {
-        "args": [
-          "--enable-features=VizDisplayCompositor"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "viz_content_unittests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "content_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "crypto_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "dbus_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "device_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "display_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "events_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "exo_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "extensions_browsertests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "extensions_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "filesystem_service_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "gcm_unit_tests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "gfx_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "gin_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "gl_unittests_ozone"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "google_apis_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "gpu_unittests"
-      },
-      {
-        "args": [
-          "--gtest_filter=-SadTabViewInteractiveUITest.ReloadMultipleSadTabs"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ],
-          "shards": 3
-        },
-        "test": "interactive_ui_tests"
-      },
-      {
-        "args": [
-          "--disable-blink-features=HTMLImports,ShadowDOMV0,CustomElementsV0",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/webui_html_imports_polyfill_interactive_ui_tests.filter"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "webui_html_imports_polyfill_interactive_ui_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "interactive_ui_tests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "ipc_tests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "jingle_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "keyboard_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "latency_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "leveldb_service_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "libjingle_xmpp_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "media_blink_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "media_service_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "media_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "message_center_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "midi_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "mojo_core_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "mojo_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "nacl_helper_nonsfi_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "nacl_loader_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "native_theme_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "net_unittests"
-      },
-      {
-        "args": [
-          "--ozone-platform=headless"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "ozone_gl_unittests"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/chromeos.ozone_unittests.filter"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "ozone_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "ozone_x11_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "pdf_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "perfetto_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "ppapi_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "printing_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "remoting_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "sandbox_linux_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "service_manager_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "services_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "shell_dialogs_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "skia_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "snapshot_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "sql_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "storage_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "sync_integration_tests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "traffic_annotation_auditor_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "ui_base_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "ui_chromeos_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "ui_touch_selection_unittests"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/chromeos.unit_tests.filter"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "unit_tests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "url_unittests"
-      },
-      {
-        "experiment_percentage": 100,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "usage_time_limit_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "views_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "viz_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "wayland_client_perftests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "wm_unittests"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
-            }
-          ]
-        },
-        "test": "wtf_unittests"
-      }
-    ]
-  },
-  "linux-google-rel": {
-    "additional_compile_targets": [
-      "chrome",
-      "chrome/installer/linux"
-    ],
-    "isolated_scripts": [
-      {
-        "isolate_name": "chrome_sizes",
-        "merge": {
-          "script": "//tools/perf/process_perf_results.py"
-        },
-        "name": "chrome_sizes",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "pool": "chrome.tests"
-            }
-          ]
-        }
-      }
-    ]
-  },
-  "mac-google-rel": {
-    "additional_compile_targets": [
-      "chrome"
-    ],
-    "isolated_scripts": [
-      {
-        "isolate_name": "chrome_sizes",
-        "merge": {
-          "script": "//tools/perf/process_perf_results.py"
-        },
-        "name": "chrome_sizes",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Mac-10.14.4",
-              "pool": "chrome.tests"
-            }
-          ],
-          "optional_dimensions": {
-            "300": [
-              {
-                "os": "Mac-10.14.3"
-              }
-            ],
-            "600": [
-              {
-                "os": "Mac-10.14.5"
-              }
-            ]
-          }
-        }
-      }
-    ]
-  },
-  "win-google-rel": {
-    "additional_compile_targets": [
-      "chrome",
-      "chrome_official_builder"
-    ],
-    "isolated_scripts": [
-      {
-        "isolate_name": "chrome_sizes",
-        "merge": {
-          "script": "//tools/perf/process_perf_results.py"
-        },
-        "name": "chrome_sizes",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063",
-              "pool": "chrome.tests"
-            }
-          ]
-        }
-      }
-    ]
-  }
-}
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index 2d6bd52..96a1493 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -2369,6 +2369,9 @@
     ]
   },
   "Android FYI 32 Vk Release (Pixel 2)": {
+    "additional_compile_targets": [
+      "angle_apks"
+    ],
     "gtest_tests": [
       {
         "args": [
@@ -2727,6 +2730,9 @@
     ]
   },
   "Android FYI 64 Vk Release (Pixel 2)": {
+    "additional_compile_targets": [
+      "angle_apks"
+    ],
     "gtest_tests": [
       {
         "args": [
@@ -11784,11 +11790,18 @@
         "args": [
           "--num-retries=3",
           "--additional-driver-flag=--enable-features=VizDisplayCompositor,UseSkiaRenderer",
-          "--additional-driver-flag=--use-gl=swiftshader",
+          "--additional-driver-flag=--use-gl=any",
+          "--additional-driver-flag=--enable-gpu-rasterization",
+          "--additional-driver-flag=--force-gpu-rasterization",
+          "--additional-driver-flag=--enable-oop-rasterization",
+          "--additional-driver-flag=--disable-software-compositing-fallback",
+          "--additional-driver-flag=--disable-headless-mode",
+          "--no-xvfb",
           "--fuzzy-diff",
           "--skipped=always",
           "--test-list=../../testing/buildbot/filters/gpu.skiarenderer_vulkan_blink_web_tests.filter",
-          "--additional-expectations=../../third_party/blink/web_tests/FlagExpectations/enable-features=UseSkiaRenderer"
+          "--additional-expectations=../../third_party/blink/web_tests/FlagExpectations/enable-features=UseSkiaRenderer",
+          "--additional-expectations=../../third_party/blink/web_tests/FlagExpectations/enable-gpu-rasterization"
         ],
         "isolate_name": "blink_web_tests_exparchive",
         "merge": {
@@ -11797,7 +11810,7 @@
           ],
           "script": "//third_party/blink/tools/merge_web_test_results.py"
         },
-        "name": "skia_renderer_blink_web_tests",
+        "name": "skia_renderer_gl_blink_web_tests",
         "results_handler": "layout tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
@@ -11863,43 +11876,6 @@
           "idempotent": false,
           "service_account": "chrome-gpu-gold@chops-service-accounts.iam.gserviceaccount.com"
         }
-      },
-      {
-        "args": [
-          "--num-retries=3",
-          "--additional-driver-flag=--enable-features=VizDisplayCompositor,UseSkiaRenderer",
-          "--additional-driver-flag=--use-gl=swiftshader",
-          "--additional-driver-flag=--enable-gpu-rasterization",
-          "--additional-driver-flag=--force-gpu-rasterization",
-          "--additional-driver-flag=--enable-oop-rasterization",
-          "--additional-driver-flag=--disable-software-compositing-fallback",
-          "--additional-driver-flag=--use-vulkan=swiftshader",
-          "--additional-driver-flag=--disable-vulkan-fallback-to-gl-for-testing",
-          "--fuzzy-diff",
-          "--skipped=always",
-          "--test-list=../../testing/buildbot/filters/gpu.skiarenderer_vulkan_blink_web_tests.filter",
-          "--additional-expectations=../../third_party/blink/web_tests/FlagExpectations/use-vulkan=swiftshader"
-        ],
-        "isolate_name": "blink_web_tests_exparchive",
-        "merge": {
-          "args": [
-            "--verbose"
-          ],
-          "script": "//third_party/blink/tools/merge_web_test_results.py"
-        },
-        "name": "vulkan_swift_shader_blink_web_tests",
-        "results_handler": "layout tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "nvidia-quadro-p400-ubuntu-stable",
-              "os": "linux-nvidia-stable",
-              "pool": "Chrome-GPU"
-            }
-          ]
-        }
       }
     ]
   },
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index 34d30d8e..033a41ac 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -15891,7 +15891,8 @@
       {
         "args": [
           "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
+          "--recover-devices",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/android.asan.services_unittests.filter"
         ],
         "merge": {
           "args": [
diff --git a/testing/buildbot/client.v8.fyi.json b/testing/buildbot/client.v8.fyi.json
index c346c260..9ddbee9b 100644
--- a/testing/buildbot/client.v8.fyi.json
+++ b/testing/buildbot/client.v8.fyi.json
@@ -172,8 +172,7 @@
           "${got_cr_revision}",
           "--test-machine-name",
           "${buildername}",
-          "--use-skia-gold",
-          "--stream-goldctl-output"
+          "--use-skia-gold"
         ],
         "experiment_percentage": 100,
         "isolate_name": "telemetry_gpu_integration_test",
@@ -542,8 +541,7 @@
           "${got_cr_revision}",
           "--test-machine-name",
           "${buildername}",
-          "--use-skia-gold",
-          "--stream-goldctl-output"
+          "--use-skia-gold"
         ],
         "experiment_percentage": 100,
         "isolate_name": "telemetry_gpu_integration_test",
@@ -906,8 +904,7 @@
           "${got_cr_revision}",
           "--test-machine-name",
           "${buildername}",
-          "--use-skia-gold",
-          "--stream-goldctl-output"
+          "--use-skia-gold"
         ],
         "experiment_percentage": 100,
         "isolate_name": "telemetry_gpu_integration_test",
@@ -1265,8 +1262,7 @@
           "${got_cr_revision}",
           "--test-machine-name",
           "${buildername}",
-          "--use-skia-gold",
-          "--stream-goldctl-output"
+          "--use-skia-gold"
         ],
         "experiment_percentage": 100,
         "isolate_name": "telemetry_gpu_integration_test",
@@ -1728,8 +1724,7 @@
           "${got_cr_revision}",
           "--test-machine-name",
           "${buildername}",
-          "--use-skia-gold",
-          "--stream-goldctl-output"
+          "--use-skia-gold"
         ],
         "experiment_percentage": 100,
         "isolate_name": "telemetry_gpu_integration_test",
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index 0b60cf97..d1d211b 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -105,6 +105,10 @@
     "label": "//android_webview/test:android_webview_unittests",
     "type": "console_test_launcher",
   },
+  "angle_apks": {
+    "label": "//third_party/angle:angle_apks",
+    "type": "additional_compile_target",
+  },
   "angle_deqp_egl_tests": {
     "args": [],
     "label": "//third_party/angle/src/tests:angle_deqp_egl_tests",
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 6604147..85a1718a 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -26,7 +26,6 @@
       'Lollipop Tablet Tester',
       'Marshmallow 64 bit Tester',
       'Marshmallow Tablet Tester',
-      'android-marshmallow-arm64-rel',
       # chromium.clang.json
       'ToTAndroid',
       'ToTAndroidCFI',
@@ -1238,28 +1237,7 @@
       },
     },
   },
-  # TODO(https://crbug.com/977620): Remove this once the V8 bots are fixed and
-  # no longer need additional logging.
-  'pixel_skia_gold_test': {
-    'modifications': {
-      'Android V8 FYI Release (Nexus 5X)': {
-        'args': [ '--stream-goldctl-output' ]
-      },
-      'Linux V8 FYI Release (NVIDIA)': {
-        'args': [ '--stream-goldctl-output' ]
-      },
-      'Linux V8 FYI Release - pointer compression (NVIDIA)': {
-        'args': [ '--stream-goldctl-output' ]
-      },
-      'Mac V8 FYI Release (Intel)': {
-        'args': [ '--stream-goldctl-output' ]
-      },
-      'Win V8 FYI Release (NVIDIA)': {
-        'args': [ '--stream-goldctl-output' ]
-      },
-    },
-  },
-  'pixel_test': {
+ 'pixel_test':{
     'modifications': {
       'Android Release (Nexus 5X)': {
         'swarming': {
@@ -1282,6 +1260,13 @@
     ],
   },
   'services_unittests': {
+    'modifications': {
+      'android-asan': {
+        'args': [
+          '--test-launcher-filter-file=../../testing/buildbot/filters/android.asan.services_unittests.filter',
+        ],
+      },
+    },
     'remove_from': [
       # chromium.clang
       'ToTLinuxMSan',  # https://crbug.com/831676
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 40ad437..8a0425b 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -100,7 +100,6 @@
         'swarming': {
           'shards': 1,
         },
-        'experiment_percentage': 100,
       },
       'android_webview_unittests': {},
       'breakpad_unittests': {},
@@ -3141,46 +3140,25 @@
     },
 
     'gpu_blink_web_tests': {
-      'skia_renderer_blink_web_tests': {
+      'skia_renderer_gl_blink_web_tests': {
         # layout test failures are retried 3 times when '--test-list' is not
         # passed, but 0 times when '--test-list' is passed. We want to always
         # retry 3 times, so we explicitly specify it.
         'args': [
           '--num-retries=3',
           '--additional-driver-flag=--enable-features=VizDisplayCompositor,UseSkiaRenderer',
-          '--additional-driver-flag=--use-gl=swiftshader',
-          '--fuzzy-diff',
-          '--skipped=always',
-          '--test-list=../../testing/buildbot/filters/gpu.skiarenderer_vulkan_blink_web_tests.filter',
-          '--additional-expectations=../../third_party/blink/web_tests/FlagExpectations/enable-features=UseSkiaRenderer',
-        ],
-        'isolate_name': 'blink_web_tests_exparchive',
-        'merge': {
-          'args': [
-            '--verbose',
-          ],
-          'script': '//third_party/blink/tools/merge_web_test_results.py',
-        },
-        'results_handler': 'layout tests',
-      },
-      'vulkan_swift_shader_blink_web_tests': {
-        # layout test failures are retried 3 times when '--test-list' is not
-        # passed, but 0 times when '--test-list' is passed. We want to always
-        # retry 3 times, so we explicitly specify it.
-        'args': [
-          '--num-retries=3',
-          '--additional-driver-flag=--enable-features=VizDisplayCompositor,UseSkiaRenderer',
-          '--additional-driver-flag=--use-gl=swiftshader',
+          '--additional-driver-flag=--use-gl=any',
           '--additional-driver-flag=--enable-gpu-rasterization',
           '--additional-driver-flag=--force-gpu-rasterization',
           '--additional-driver-flag=--enable-oop-rasterization',
           '--additional-driver-flag=--disable-software-compositing-fallback',
-          '--additional-driver-flag=--use-vulkan=swiftshader',
-          '--additional-driver-flag=--disable-vulkan-fallback-to-gl-for-testing',
+          '--additional-driver-flag=--disable-headless-mode',
+          '--no-xvfb',
           '--fuzzy-diff',
           '--skipped=always',
           '--test-list=../../testing/buildbot/filters/gpu.skiarenderer_vulkan_blink_web_tests.filter',
-          '--additional-expectations=../../third_party/blink/web_tests/FlagExpectations/use-vulkan=swiftshader',
+          '--additional-expectations=../../third_party/blink/web_tests/FlagExpectations/enable-features=UseSkiaRenderer',
+          '--additional-expectations=../../third_party/blink/web_tests/FlagExpectations/enable-gpu-rasterization',
         ],
         'isolate_name': 'blink_web_tests_exparchive',
         'merge': {
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 079924b..df795c73 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -622,87 +622,6 @@
       },
     },
   },
-  # TODO(bpastene): Remove 'chromium.chrome' when it's been switched to 'chrome'
-  {
-    'name': 'chromium.chrome',
-    'machines': {
-      'chromeos-amd64-generic-google-rel': {
-        'additional_compile_targets': [
-          'chrome',
-        ],
-      },
-      'chromeos-betty-google-rel': {
-        'additional_compile_targets': [
-          'chromiumos_preflight',
-        ],
-        'test_suites': {
-          'gtest_tests': 'chromeos_device_friendly_gtests',
-          'isolated_scripts': 'chromeos_isolated_scripts',
-        },
-        'swarming': {
-          'dimension_sets': [
-            {
-              'kvm': '1',
-              'os': 'Ubuntu-16.04',
-              'pool': 'chrome.tests.cros-vm',
-            },
-          ],
-        },
-      },
-      'linux-chromeos-google-rel': {
-        'additional_compile_targets': [
-          'chrome',
-          'chrome_sandbox',
-          'linux_symbols',
-          'symupload'
-        ],
-        'mixins': [
-          'chrome-swarming-pool',
-          'linux-trusty',
-        ],
-        'test_suites': {
-          'gtest_tests': 'linux_chromeos_gtests',
-        },
-      },
-      'linux-google-rel': {
-        'additional_compile_targets': [
-          'chrome',
-          'chrome/installer/linux',
-        ],
-        'mixins': [
-          'chrome-swarming-pool',
-        ],
-        'test_suites': {
-          'isolated_scripts': 'chrome_sizes',
-        },
-      },
-      'mac-google-rel': {
-        'additional_compile_targets': [
-          'chrome',
-        ],
-        'mixins': [
-          'chrome-swarming-pool',
-          'mac_10.14',
-        ],
-        'test_suites': {
-          'isolated_scripts': 'chrome_sizes',
-        },
-      },
-      'win-google-rel': {
-        'additional_compile_targets': [
-          'chrome',
-          'chrome_official_builder',
-        ],
-        'mixins': [
-          'chrome-swarming-pool',
-          'win10',
-        ],
-        'test_suites': {
-          'isolated_scripts': 'chrome_sizes',
-        },
-      },
-    },
-  },
   {
     'name': 'chromium.chromiumos',
     'machines': {
@@ -2408,6 +2327,9 @@
           'pie',
           'walleye',
         ],
+        'additional_compile_targets': [
+          'angle_apks',
+        ],
         'test_suites': {
           'gtest_tests': 'gpu_angle_gtests',
           'isolated_scripts': 'gpu_angle_perf_isolated_scripts',
@@ -2433,6 +2355,9 @@
           'pie',
           'walleye',
         ],
+        'additional_compile_targets': [
+          'angle_apks',
+        ],
         'test_suites': {
           'gtest_tests': 'gpu_angle_gtests',
           'isolated_scripts': 'gpu_angle_perf_isolated_scripts',
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 0fde556..b562f0e 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -574,19 +574,6 @@
                     ]
                 }
             ]
-        },
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "AudioServiceStreams",
-                    "enable_features": [
-                        "AudioServiceAudioStreams"
-                    ]
-                }
-            ]
         }
     ],
     "AudioServiceOutOfProcessSandboxApmMac": [
@@ -1445,22 +1432,6 @@
             ]
         }
     ],
-    "ClearOldOnDemandFavicons": [
-        {
-            "platforms": [
-                "android",
-                "ios"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "ClearOldOnDemandFavicons"
-                    ]
-                }
-            ]
-        }
-    ],
     "ClipboardContentBehavior": [
         {
             "platforms": [
@@ -3526,7 +3497,6 @@
                         "UIVerticalMargin": "10"
                     },
                     "enable_features": [
-                        "OmniboxDedupeGoogleDriveURLs",
                         "OmniboxDisplayTitleForCurrentUrl",
                         "OmniboxDocumentProvider",
                         "OmniboxGroupSuggestionsBySearchVsUrl",
diff --git a/third_party/.gitignore b/third_party/.gitignore
index 8d544dc..c21d7d8 100644
--- a/third_party/.gitignore
+++ b/third_party/.gitignore
@@ -81,7 +81,6 @@
 /fuchsia-sdk/images
 /fuchsia-sdk/sdk
 /gestures/gestures
-/gles1_conform
 /gles2_conform
 /glfw/src
 /glslang/src
diff --git a/third_party/blink/public/mojom/payments/payment_app.mojom b/third_party/blink/public/mojom/payments/payment_app.mojom
index 567f572..4b88899 100644
--- a/third_party/blink/public/mojom/payments/payment_app.mojom
+++ b/third_party/blink/public/mojom/payments/payment_app.mojom
@@ -33,6 +33,7 @@
   PAYMENT_DETAILS_NOT_OBJECT,
   PAYMENT_EVENT_BROWSER_ERROR,
   PAYMENT_EVENT_TIMEOUT,
+  PAYMENT_HANDLER_INSECURE_NAVIGATION,
 };
 
 // This struct is provided to hold a payment instrument from render
diff --git a/third_party/blink/public/mojom/web_feature/web_feature.mojom b/third_party/blink/public/mojom/web_feature/web_feature.mojom
index 57b0633..adbd4e62 100644
--- a/third_party/blink/public/mojom/web_feature/web_feature.mojom
+++ b/third_party/blink/public/mojom/web_feature/web_feature.mojom
@@ -2328,6 +2328,16 @@
   kCreateObjectURLMediaSourceFromWorker = 2939,
   kCSSAtRuleProperty = 2940,
   kServiceWorkerInterceptedRequestFromOriginDirtyStyleSheet = 2941,
+  kWebkitMarginBeforeCollapseDiscard = 2942,
+  kWebkitMarginBeforeCollapseSeparate = 2943,
+  kWebkitMarginBeforeCollapseSeparateMaybeDoesSomething = 2944,
+  kWebkitMarginAfterCollapseDiscard = 2945,
+  kWebkitMarginAfterCollapseSeparate = 2946,
+  kWebkitMarginAfterCollapseSeparateMaybeDoesSomething = 2947,
+  kCredentialManagerCreateWithUVM = 2948,
+  kCredentialManagerGetWithUVM = 2949,
+  kCredentialManagerCreateSuccessWithUVM = 2950,
+  kCredentialManagerGetSuccessWithUVM = 2951,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/public/platform/task_type.h b/third_party/blink/public/platform/task_type.h
index c3a709b..c258f61 100644
--- a/third_party/blink/public/platform/task_type.h
+++ b/third_party/blink/public/platform/task_type.h
@@ -130,9 +130,9 @@
   // Tasks used for DedicatedWorker's requestAnimationFrame.
   kWorkerAnimation = 51,
 
-  // For tasks started with the experimental Scheduling API
-  kExperimentalWebSchedulingUserInteraction = 53,
-  kExperimentalWebSchedulingBestEffort = 54,
+  // Obsolete.
+  // kExperimentalWebSchedulingUserInteraction = 53,
+  // kExperimentalWebSchedulingBestEffort = 54,
 
   // https://drafts.csswg.org/css-font-loading/#task-source
   kFontLoading = 56,
diff --git a/third_party/blink/public/platform/web_runtime_features.h b/third_party/blink/public/platform/web_runtime_features.h
index 37e738e..5a76012 100644
--- a/third_party/blink/public/platform/web_runtime_features.h
+++ b/third_party/blink/public/platform/web_runtime_features.h
@@ -85,7 +85,6 @@
   BLINK_PLATFORM_EXPORT static void EnableAutomaticLazyImageLoading(bool);
   BLINK_PLATFORM_EXPORT static void EnableBackgroundFetch(bool);
   BLINK_PLATFORM_EXPORT static void EnableBlinkHeapIncrementalMarking(bool);
-  BLINK_PLATFORM_EXPORT static void EnableBloatedRendererDetection(bool);
   BLINK_PLATFORM_EXPORT static void EnableBlockingFocusWithoutUserActivation(
       bool);
   BLINK_PLATFORM_EXPORT static void EnableCacheInlineScriptCode(bool);
diff --git a/third_party/blink/renderer/bindings/core/v8/BUILD.gn b/third_party/blink/renderer/bindings/core/v8/BUILD.gn
index d8da6a0..bc35093 100644
--- a/third_party/blink/renderer/bindings/core/v8/BUILD.gn
+++ b/third_party/blink/renderer/bindings/core/v8/BUILD.gn
@@ -180,8 +180,6 @@
   "$bindings_core_v8_output_dir/v8_resize_observer_callback.h",
   "$bindings_core_v8_output_dir/v8_scroll_state_callback.cc",
   "$bindings_core_v8_output_dir/v8_scroll_state_callback.h",
-  "$bindings_core_v8_output_dir/v8_task_queue_post_callback.cc",
-  "$bindings_core_v8_output_dir/v8_task_queue_post_callback.h",
   "$bindings_core_v8_output_dir/v8_void_function.cc",
   "$bindings_core_v8_output_dir/v8_void_function.h",
 ]
diff --git a/third_party/blink/renderer/bindings/core/v8/source_location.cc b/third_party/blink/renderer/bindings/core/v8/source_location.cc
index 3585c18..35c9508 100644
--- a/third_party/blink/renderer/bindings/core/v8/source_location.cc
+++ b/third_party/blink/renderer/bindings/core/v8/source_location.cc
@@ -181,7 +181,13 @@
 
 std::unique_ptr<v8_inspector::protocol::Runtime::API::StackTrace>
 SourceLocation::BuildInspectorObject() const {
-  return stack_trace_ ? stack_trace_->buildInspectorObject() : nullptr;
+  return BuildInspectorObject(std::numeric_limits<int>::max());
+}
+
+std::unique_ptr<v8_inspector::protocol::Runtime::API::StackTrace>
+SourceLocation::BuildInspectorObject(int max_async_depth) const {
+  return stack_trace_ ? stack_trace_->buildInspectorObject(max_async_depth)
+                      : nullptr;
 }
 
 String SourceLocation::ToString() const {
diff --git a/third_party/blink/renderer/bindings/core/v8/source_location.h b/third_party/blink/renderer/bindings/core/v8/source_location.h
index 5d14e3e..3126e04 100644
--- a/third_party/blink/renderer/bindings/core/v8/source_location.h
+++ b/third_party/blink/renderer/bindings/core/v8/source_location.h
@@ -58,8 +58,8 @@
     return std::move(stack_trace_);
   }
 
-  std::unique_ptr<SourceLocation> Clone()
-      const;  // Safe to pass between threads.
+  // Safe to pass between threads, drops async chain in stack trace.
+  std::unique_ptr<SourceLocation> Clone() const;
 
   // No-op when stack trace is unknown.
   void ToTracedValue(TracedValue*, const char* name) const;
@@ -71,6 +71,9 @@
   std::unique_ptr<v8_inspector::protocol::Runtime::API::StackTrace>
   BuildInspectorObject() const;
 
+  std::unique_ptr<v8_inspector::protocol::Runtime::API::StackTrace>
+  BuildInspectorObject(int max_async_depth) const;
+
  private:
   static std::unique_ptr<SourceLocation> CreateFromNonEmptyV8StackTrace(
       std::unique_ptr<v8_inspector::V8StackTrace>,
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc b/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc
index 82bfcb5..d4fcac3 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc
@@ -40,6 +40,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_value.h"
 #include "third_party/blink/renderer/bindings/core/v8/source_location.h"
+#include "third_party/blink/renderer/bindings/core/v8/string_or_trusted_script.h"
 #include "third_party/blink/renderer/bindings/core/v8/use_counter_callback.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_context_snapshot.h"
@@ -59,6 +60,7 @@
 #include "third_party/blink/renderer/core/inspector/main_thread_debugger.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/script/modulator.h"
+#include "third_party/blink/renderer/core/trustedtypes/trusted_types_util.h"
 #include "third_party/blink/renderer/core/workers/worker_global_scope.h"
 #include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h"
 #include "third_party/blink/renderer/platform/bindings/v8_per_context_data.h"
@@ -348,7 +350,8 @@
                                         WrapperTypeInfo::Unwrap(data), holder);
 }
 
-static bool CodeGenerationCheckCallbackInMainThread(
+// Check whether Content Security Policy allows script execution.
+static bool ContentSecurityPolicyCodeGenerationCheck(
     v8::Local<v8::Context> context,
     v8::Local<v8::String> source) {
   if (ExecutionContext* execution_context = ToExecutionContext(context)) {
@@ -370,6 +373,53 @@
   return false;
 }
 
+static v8::MaybeLocal<v8::String> TrustedTypesCodeGenerationCheck(
+    v8::Local<v8::Context> context,
+    v8::Local<v8::Value> source) {
+  ExceptionState exception_state(context->GetIsolate(),
+                                 ExceptionState::kExecutionContext, "eval", "");
+  StringOrTrustedScript string_or_trusted_script;
+  V8StringOrTrustedScript::ToImpl(
+      context->GetIsolate(), source, string_or_trusted_script,
+      UnionTypeConversionMode::kNotNullable, exception_state);
+
+  String modified_source = GetStringFromTrustedScript(
+      string_or_trusted_script, ToExecutionContext(context), exception_state);
+  if (exception_state.HadException()) {
+    exception_state.ClearException();
+    return v8::MaybeLocal<v8::String>();
+  }
+
+  return V8String(context->GetIsolate(), modified_source);
+}
+
+static v8::MaybeLocal<v8::String> CodeGenerationCheckCallbackInMainThread(
+    v8::Local<v8::Context> context,
+    v8::Local<v8::Value> source) {
+  bool allowed_by_csp =
+      source->IsString() && ContentSecurityPolicyCodeGenerationCheck(
+                                context, source.As<v8::String>());
+  // Without trusted types, we decide based on CSP.
+  if (!RequireTrustedTypesCheck(ToExecutionContext(context))) {
+    if (allowed_by_csp)
+      return source.As<v8::String>();
+    return v8::MaybeLocal<v8::String>();
+  }
+
+  // With Trusted Types, we pass when either CSP or TT allow the value.
+  // We will always run the TT check because of reporting, and because a
+  // default policy might want to modify the string.
+  v8::MaybeLocal<v8::String> trusted_types_string =
+      TrustedTypesCodeGenerationCheck(context, source);
+  if (allowed_by_csp || !trusted_types_string.IsEmpty()) {
+    if (trusted_types_string.IsEmpty()) {
+      return source.As<v8::String>();
+    }
+    return trusted_types_string;
+  }
+  return v8::MaybeLocal<v8::String>();
+}
+
 static bool WasmCodeGenerationCheckCallbackInMainThread(
     v8::Local<v8::Context> context,
     v8::Local<v8::String> source) {
@@ -647,7 +697,7 @@
           v8::Isolate::kMessageLog);
   isolate->SetFailedAccessCheckCallbackFunction(
       FailedAccessCheckCallbackInMainThread);
-  isolate->SetAllowCodeGenerationFromStringsCallback(
+  isolate->SetModifyCodeGenerationFromStringsCallback(
       CodeGenerationCheckCallbackInMainThread);
   isolate->SetAllowWasmCodeGenerationCallback(
       WasmCodeGenerationCheckCallbackInMainThread);
diff --git a/third_party/blink/renderer/core/animation/compositor_animations.cc b/third_party/blink/renderer/core/animation/compositor_animations.cc
index fb2a8fa..8dc50ef3 100644
--- a/third_party/blink/renderer/core/animation/compositor_animations.cc
+++ b/third_party/blink/renderer/core/animation/compositor_animations.cc
@@ -336,37 +336,27 @@
   if (!Platform::Current()->IsThreadedAnimationEnabled())
     reasons |= kAcceleratedAnimationsDisabled;
 
-  LayoutObject* layout_object = target_element.GetLayoutObject();
-  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
+  if (const auto* layout_object = target_element.GetLayoutObject()) {
     // We query paint property tree state below to determine whether the
-    // animation is compositable. There is a known lifecycle violation where an
-    // animation can be cancelled during style update. See
-    // CompositorAnimations::cancelAnimationOnCompositor and
-    // http://crbug.com/676456. When this is fixed we would like to enable
-    // the DCHECK below.
-    // DCHECK(document().lifecycle().state() >=
-    // DocumentLifecycle::PrePaintClean);
-    DCHECK(layout_object);
-
-    if (!layout_object->UniqueId())
-      reasons |= kTargetHasInvalidCompositingState;
-
+    // animation is compositable. TODO(crbug.com/676456): There is a known
+    // lifecycle violation where an animation can be cancelled during style
+    // update. See CompositorAnimations::CancelAnimationOnCompositor().
+    // When this is fixed we would like to enable the DCHECK below.
+    // DCHECK_GE(GetDocument().Lifecycle().GetState(),
+    //           DocumentLifecycle::kPrePaintClean);
+    bool has_direct_compositing_reasons = false;
     if (const auto* paint_properties =
             layout_object->FirstFragment().PaintProperties()) {
-      const TransformPaintPropertyNode* transform_node =
-          paint_properties->Transform();
-      const EffectPaintPropertyNode* effect_node = paint_properties->Effect();
-      bool has_direct_compositing_reasons =
-          (transform_node && transform_node->HasDirectCompositingReasons()) ||
-          (effect_node && effect_node->HasDirectCompositingReasons());
-      if (!has_direct_compositing_reasons)
-        reasons |= kTargetHasInvalidCompositingState;
+      const auto* transform = paint_properties->Transform();
+      const auto* effect = paint_properties->Effect();
+      has_direct_compositing_reasons =
+          (transform && transform->HasDirectCompositingReasons()) ||
+          (effect && effect->HasDirectCompositingReasons());
     }
-  } else {
-    if (!layout_object ||
-        layout_object->GetCompositingState() != kPaintsIntoOwnBacking) {
+    if (!has_direct_compositing_reasons)
       reasons |= kTargetHasInvalidCompositingState;
-    }
+  } else {
+    reasons |= kTargetHasInvalidCompositingState;
   }
 
   return reasons;
@@ -482,11 +472,6 @@
     const Animation& animation,
     int id,
     double pause_time) {
-  // FIXME: CheckCanStartAnimationOnCompositor queries compositingState, which
-  // is not necessarily up to date.
-  // https://code.google.com/p/chromium/issues/detail?id=339847
-  DisableCompositingQueryAsserts disabler;
-
   DCHECK_EQ(CheckCanStartElementOnCompositor(element), kNoFailure);
   CompositorAnimation* compositor_animation =
       animation.GetCompositorAnimation();
diff --git a/third_party/blink/renderer/core/animation/compositor_animations_test.cc b/third_party/blink/renderer/core/animation/compositor_animations_test.cc
index 58bc43b8..2f5080e 100644
--- a/third_party/blink/renderer/core/animation/compositor_animations_test.cc
+++ b/third_party/blink/renderer/core/animation/compositor_animations_test.cc
@@ -1645,11 +1645,9 @@
 }
 
 }  // namespace
+
 TEST_P(AnimationCompositorAnimationsTest,
        CanStartElementOnCompositorTransformBasedOnPaintProperties) {
-  if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
-    return;
-
   Persistent<Element> element = GetDocument().CreateElementForBinding("shared");
   LayoutObjectProxy* layout_object = LayoutObjectProxy::Create(element.Get());
   layout_object->EnsureIdForTestingProxy();
@@ -1682,9 +1680,6 @@
 
 TEST_P(AnimationCompositorAnimationsTest,
        CanStartElementOnCompositorEffectBasedOnPaintProperties) {
-  if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
-    return;
-
   Persistent<Element> element = GetDocument().CreateElementForBinding("shared");
   LayoutObjectProxy* layout_object = LayoutObjectProxy::Create(element.Get());
   layout_object->EnsureIdForTestingProxy();
@@ -1780,8 +1775,9 @@
 }
 
 TEST_P(AnimationCompositorAnimationsTest, CompositedTransformAnimation) {
-  // TODO(wangxianzhu): Fix this test for CompositeAfterPaint.
-  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
+  if (!RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled() ||
+      // TODO(wangxianzhu): Fix this test for CompositeAfterPaint.
+      RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
     return;
 
   LoadTestData("transform-animation.html");
@@ -1792,22 +1788,20 @@
   ASSERT_NE(nullptr, properties);
   const auto* transform = properties->Transform();
   ASSERT_NE(nullptr, transform);
-  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled() ||
-      RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled()) {
-    EXPECT_TRUE(transform->HasDirectCompositingReasons());
-    EXPECT_TRUE(transform->HasActiveTransformAnimation());
-    // Make sure the animation state is initialized in paint properties.
-    auto* property_trees =
-        document->View()->RootCcLayer()->layer_tree_host()->property_trees();
-    auto* cc_transform = property_trees->transform_tree.Node(
-        property_trees->element_id_to_transform_node_index
-            [transform->GetCompositorElementId()]);
-    ASSERT_NE(nullptr, cc_transform);
-    EXPECT_TRUE(cc_transform->has_potential_animation);
-    EXPECT_TRUE(cc_transform->is_currently_animating);
-    EXPECT_EQ(cc::kNotScaled, cc_transform->starting_animation_scale);
-    EXPECT_EQ(cc::kNotScaled, cc_transform->maximum_animation_scale);
-  }
+  EXPECT_TRUE(transform->HasDirectCompositingReasons());
+  EXPECT_TRUE(transform->HasActiveTransformAnimation());
+
+  // Make sure the animation state is initialized in paint properties.
+  auto* property_trees =
+      document->View()->RootCcLayer()->layer_tree_host()->property_trees();
+  auto* cc_transform = property_trees->transform_tree.Node(
+      property_trees->element_id_to_transform_node_index
+          [transform->GetCompositorElementId()]);
+  ASSERT_NE(nullptr, cc_transform);
+  EXPECT_TRUE(cc_transform->has_potential_animation);
+  EXPECT_TRUE(cc_transform->is_currently_animating);
+  EXPECT_EQ(cc::kNotScaled, cc_transform->starting_animation_scale);
+  EXPECT_EQ(cc::kNotScaled, cc_transform->maximum_animation_scale);
 
   // Make sure the animation is started on the compositor.
   EXPECT_EQ(CheckCanStartElementOnCompositor(*target),
@@ -1819,8 +1813,9 @@
 }
 
 TEST_P(AnimationCompositorAnimationsTest, CompositedScaleAnimation) {
-  // TODO(wangxianzhu): Fix this test for CompositeAfterPaint.
-  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
+  if (!RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled() ||
+      // TODO(wangxianzhu): Fix this test for CompositeAfterPaint.
+      RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
     return;
 
   LoadTestData("scale-animation.html");
@@ -1831,22 +1826,20 @@
   ASSERT_NE(nullptr, properties);
   const auto* transform = properties->Transform();
   ASSERT_NE(nullptr, transform);
-  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled() ||
-      RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled()) {
-    EXPECT_TRUE(transform->HasDirectCompositingReasons());
-    EXPECT_TRUE(transform->HasActiveTransformAnimation());
-    // Make sure the animation state is initialized in paint properties.
-    auto* property_trees =
-        document->View()->RootCcLayer()->layer_tree_host()->property_trees();
-    auto* cc_transform = property_trees->transform_tree.Node(
-        property_trees->element_id_to_transform_node_index
-            [transform->GetCompositorElementId()]);
-    ASSERT_NE(nullptr, cc_transform);
-    EXPECT_TRUE(cc_transform->has_potential_animation);
-    EXPECT_TRUE(cc_transform->is_currently_animating);
-    EXPECT_EQ(2.f, cc_transform->starting_animation_scale);
-    EXPECT_EQ(5.f, cc_transform->maximum_animation_scale);
-  }
+  EXPECT_TRUE(transform->HasDirectCompositingReasons());
+  EXPECT_TRUE(transform->HasActiveTransformAnimation());
+
+  // Make sure the animation state is initialized in paint properties.
+  auto* property_trees =
+      document->View()->RootCcLayer()->layer_tree_host()->property_trees();
+  auto* cc_transform = property_trees->transform_tree.Node(
+      property_trees->element_id_to_transform_node_index
+          [transform->GetCompositorElementId()]);
+  ASSERT_NE(nullptr, cc_transform);
+  EXPECT_TRUE(cc_transform->has_potential_animation);
+  EXPECT_TRUE(cc_transform->is_currently_animating);
+  EXPECT_EQ(2.f, cc_transform->starting_animation_scale);
+  EXPECT_EQ(5.f, cc_transform->maximum_animation_scale);
 
   // Make sure the animation is started on the compositor.
   EXPECT_EQ(CheckCanStartElementOnCompositor(*target),
diff --git a/third_party/blink/renderer/core/animation/css/css_animations.cc b/third_party/blink/renderer/core/animation/css/css_animations.cc
index f391fd95..f83470e4 100644
--- a/third_party/blink/renderer/core/animation/css/css_animations.cc
+++ b/third_party/blink/renderer/core/animation/css/css_animations.cc
@@ -494,12 +494,6 @@
   previous_active_interpolations_for_standard_animations_.swap(
       pending_update_.ActiveInterpolationsForStandardAnimations());
 
-  // FIXME: cancelling, pausing, unpausing animations all query
-  // compositingState, which is not necessarily up to date here
-  // since we call this from recalc style.
-  // https://code.google.com/p/chromium/issues/detail?id=339847
-  DisableCompositingQueryAsserts disabler;
-
   for (wtf_size_t paused_index :
        pending_update_.AnimationIndicesWithPauseToggled()) {
     Animation& animation = *running_animations_[paused_index]->animation;
diff --git a/third_party/blink/renderer/core/animation/css_angle_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_angle_interpolation_type.cc
index c0db244..832abb8 100644
--- a/third_party/blink/renderer/core/animation/css_angle_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_angle_interpolation_type.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/core/animation/css_angle_interpolation_type.h"
 
+#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
 
 namespace blink {
@@ -29,8 +30,8 @@
     const InterpolableValue& value,
     const NonInterpolableValue*,
     const StyleResolverState&) const {
-  return CSSPrimitiveValue::Create(ToInterpolableNumber(value).Value(),
-                                   CSSPrimitiveValue::UnitType::kDegrees);
+  return CSSNumericLiteralValue::Create(ToInterpolableNumber(value).Value(),
+                                        CSSPrimitiveValue::UnitType::kDegrees);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/animation/css_image_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_image_interpolation_type.cc
index 9addee20..5bc7819 100644
--- a/third_party/blink/renderer/core/animation/css_image_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_image_interpolation_type.cc
@@ -8,6 +8,7 @@
 
 #include "base/memory/ptr_util.h"
 #include "third_party/blink/renderer/core/css/css_crossfade_value.h"
+#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
 #include "third_party/blink/renderer/core/css/css_property_names.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
@@ -59,8 +60,8 @@
       return end_;
     return MakeGarbageCollected<cssvalue::CSSCrossfadeValue>(
         start_, end_,
-        CSSPrimitiveValue::Create(progress,
-                                  CSSPrimitiveValue::UnitType::kNumber));
+        CSSNumericLiteralValue::Create(progress,
+                                       CSSPrimitiveValue::UnitType::kNumber));
   }
 
   DECLARE_NON_INTERPOLABLE_VALUE_TYPE();
diff --git a/third_party/blink/renderer/core/animation/css_number_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_number_interpolation_type.cc
index 303f5ab..54893a3 100644
--- a/third_party/blink/renderer/core/animation/css_number_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_number_interpolation_type.cc
@@ -9,6 +9,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/optional.h"
 #include "third_party/blink/renderer/core/animation/number_property_functions.h"
+#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/resolver/style_builder.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
 
@@ -38,8 +39,9 @@
     const NonInterpolableValue*,
     const StyleResolverState&) const {
   double number = ToInterpolableNumber(value).Value();
-  return CSSPrimitiveValue::Create(round_to_integer_ ? round(number) : number,
-                                   CSSPrimitiveValue::UnitType::kNumber);
+  return CSSNumericLiteralValue::Create(
+      round_to_integer_ ? round(number) : number,
+      CSSPrimitiveValue::UnitType::kNumber);
 }
 
 InterpolationValue CSSNumberInterpolationType::CreateNumberValue(
@@ -105,11 +107,12 @@
   double clamped_number = NumberPropertyFunctions::ClampNumber(
       CssProperty(), ToInterpolableNumber(interpolable_value).Value());
   if (!NumberPropertyFunctions::SetNumber(CssProperty(), *state.Style(),
-                                          clamped_number))
+                                          clamped_number)) {
     StyleBuilder::ApplyProperty(
         GetProperty().GetCSSProperty(), state,
-        *CSSPrimitiveValue::Create(clamped_number,
-                                   CSSPrimitiveValue::UnitType::kNumber));
+        *CSSNumericLiteralValue::Create(clamped_number,
+                                        CSSPrimitiveValue::UnitType::kNumber));
+  }
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/animation/css_resolution_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_resolution_interpolation_type.cc
index af862cd3..3b8b3af 100644
--- a/third_party/blink/renderer/core/animation/css_resolution_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_resolution_interpolation_type.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/core/animation/css_resolution_interpolation_type.h"
 
+#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
 
 namespace blink {
@@ -30,8 +31,9 @@
     const InterpolableValue& value,
     const NonInterpolableValue*,
     const StyleResolverState&) const {
-  return CSSPrimitiveValue::Create(ToInterpolableNumber(value).Value(),
-                                   CSSPrimitiveValue::UnitType::kDotsPerPixel);
+  return CSSNumericLiteralValue::Create(
+      ToInterpolableNumber(value).Value(),
+      CSSPrimitiveValue::UnitType::kDotsPerPixel);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/animation/css_time_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_time_interpolation_type.cc
index 1b1980d..6f41dba 100644
--- a/third_party/blink/renderer/core/animation/css_time_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_time_interpolation_type.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/core/animation/css_time_interpolation_type.h"
 
+#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
 
 namespace blink {
@@ -29,8 +30,8 @@
     const InterpolableValue& value,
     const NonInterpolableValue*,
     const StyleResolverState&) const {
-  return CSSPrimitiveValue::Create(ToInterpolableNumber(value).Value(),
-                                   CSSPrimitiveValue::UnitType::kSeconds);
+  return CSSNumericLiteralValue::Create(ToInterpolableNumber(value).Value(),
+                                        CSSPrimitiveValue::UnitType::kSeconds);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/animation/css_transform_origin_interpolation_type.h b/third_party/blink/renderer/core/animation/css_transform_origin_interpolation_type.h
index 1dd8f99d..a26c050 100644
--- a/third_party/blink/renderer/core/animation/css_transform_origin_interpolation_type.h
+++ b/third_party/blink/renderer/core/animation/css_transform_origin_interpolation_type.h
@@ -9,6 +9,7 @@
 #include "third_party/blink/renderer/core/animation/css_position_axis_list_interpolation_type.h"
 #include "third_party/blink/renderer/core/animation/length_interpolation_functions.h"
 #include "third_party/blink/renderer/core/animation/list_interpolation_functions.h"
+#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_value_list.h"
 
 namespace blink {
@@ -29,7 +30,7 @@
         3, [&list](wtf_size_t index) {
           if (index == list.length()) {
             return LengthInterpolationFunctions::MaybeConvertCSSValue(
-                *CSSPrimitiveValue::Create(
+                *CSSNumericLiteralValue::Create(
                     0, CSSPrimitiveValue::UnitType::kPixels));
           }
           const CSSValue& item = list.Item(index);
diff --git a/third_party/blink/renderer/core/animation/keyframe_effect.cc b/third_party/blink/renderer/core/animation/keyframe_effect.cc
index 3881c11..a6b9953 100644
--- a/third_party/blink/renderer/core/animation/keyframe_effect.cc
+++ b/third_party/blink/renderer/core/animation/keyframe_effect.cc
@@ -275,10 +275,6 @@
 
 bool KeyframeEffect::CancelAnimationOnCompositor(
     CompositorAnimation* compositor_animation) {
-  // FIXME: cancelAnimationOnCompositor is called from withins style recalc.
-  // This queries compositingState, which is not necessarily up to date.
-  // https://code.google.com/p/chromium/issues/detail?id=339847
-  DisableCompositingQueryAsserts disabler;
   if (!HasActiveAnimationsOnCompositor())
     return false;
   if (!target_ || !target_->GetLayoutObject())
diff --git a/third_party/blink/renderer/core/animation/length_interpolation_functions.cc b/third_party/blink/renderer/core/animation/length_interpolation_functions.cc
index 6e7df16..8125dac 100644
--- a/third_party/blink/renderer/core/animation/length_interpolation_functions.cc
+++ b/third_party/blink/renderer/core/animation/length_interpolation_functions.cc
@@ -5,6 +5,8 @@
 #include "third_party/blink/renderer/core/animation/length_interpolation_functions.h"
 
 #include "third_party/blink/renderer/core/css/css_calculation_value.h"
+#include "third_party/blink/renderer/core/css/css_math_function_value.h"
+#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
 #include "third_party/blink/renderer/core/css/css_to_length_conversion_data.h"
 #include "third_party/blink/renderer/platform/geometry/calculation_value.h"
@@ -212,7 +214,7 @@
       CSSLengthNonInterpolableValue::HasPercentage(non_interpolable_value);
 
   CSSCalcExpressionNode* root_node = nullptr;
-  CSSPrimitiveValue* first_value = nullptr;
+  CSSNumericLiteralValue* first_value = nullptr;
 
   for (wtf_size_t i = 0; i < CSSPrimitiveValue::kLengthUnitTypeCount; i++) {
     double value = ToInterpolableNumber(*interpolable_list.Get(i)).Value();
@@ -220,8 +222,8 @@
         (i != CSSPrimitiveValue::kUnitTypePercentage || !has_percentage)) {
       continue;
     }
-    CSSPrimitiveValue* current_value =
-        CSSPrimitiveValue::Create(value, IndexToUnitType(i));
+    CSSNumericLiteralValue* current_value =
+        CSSNumericLiteralValue::Create(value, IndexToUnitType(i));
 
     if (!first_value) {
       DCHECK(!root_node);
@@ -238,12 +240,13 @@
   }
 
   if (root_node) {
-    return CSSPrimitiveValue::Create(CSSCalcValue::Create(root_node));
+    return CSSMathFunctionValue::Create(CSSCalcValue::Create(root_node));
   }
   if (first_value) {
     return first_value;
   }
-  return CSSPrimitiveValue::Create(0, CSSPrimitiveValue::UnitType::kPixels);
+  return CSSNumericLiteralValue::Create(0,
+                                        CSSPrimitiveValue::UnitType::kPixels);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/core_idl_files.gni b/third_party/blink/renderer/core/core_idl_files.gni
index a22c8098..37bf162 100644
--- a/third_party/blink/renderer/core/core_idl_files.gni
+++ b/third_party/blink/renderer/core/core_idl_files.gni
@@ -129,8 +129,6 @@
                     "dom/node_list.idl",
                     "dom/processing_instruction.idl",
                     "dom/range.idl",
-                    "dom/scripted_task_queue.idl",
-                    "dom/scripted_task_queue_controller.idl",
                     "dom/static_range.idl",
                     "dom/text.idl",
                     "dom/tree_walker.idl",
diff --git a/third_party/blink/renderer/core/css/BUILD.gn b/third_party/blink/renderer/core/css/BUILD.gn
index 997ce670..9375ce6 100644
--- a/third_party/blink/renderer/core/css/BUILD.gn
+++ b/third_party/blink/renderer/core/css/BUILD.gn
@@ -108,12 +108,16 @@
     "css_layout_function_value.h",
     "css_markup.cc",
     "css_markup.h",
+    "css_math_function_value.cc",
+    "css_math_function_value.h",
     "css_math_operator.cc",
     "css_math_operator.h",
     "css_media_rule.cc",
     "css_media_rule.h",
     "css_namespace_rule.cc",
     "css_namespace_rule.h",
+    "css_numeric_literal_value.cc",
+    "css_numeric_literal_value.h",
     "css_origin_clean.h",
     "css_page_rule.cc",
     "css_page_rule.h",
diff --git a/third_party/blink/renderer/core/css/basic_shape_functions.cc b/third_party/blink/renderer/core/css/basic_shape_functions.cc
index a9c160dd..f6418d3 100644
--- a/third_party/blink/renderer/core/css/basic_shape_functions.cc
+++ b/third_party/blink/renderer/core/css/basic_shape_functions.cc
@@ -31,6 +31,7 @@
 
 #include "third_party/blink/renderer/core/css/css_basic_shape_values.h"
 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
+#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value_mappings.h"
 #include "third_party/blink/renderer/core/css/css_ray_value.h"
 #include "third_party/blink/renderer/core/css/css_value_pair.h"
@@ -122,8 +123,8 @@
     case BasicShape::kStyleRayType: {
       const StyleRay& ray = To<StyleRay>(*basic_shape);
       return MakeGarbageCollected<cssvalue::CSSRayValue>(
-          *CSSPrimitiveValue::Create(ray.Angle(),
-                                     CSSPrimitiveValue::UnitType::kDegrees),
+          *CSSNumericLiteralValue::Create(
+              ray.Angle(), CSSPrimitiveValue::UnitType::kDegrees),
           *CSSIdentifierValue::Create(RaySizeToKeyword(ray.Size())),
           (ray.Contain() ? CSSIdentifierValue::Create(CSSValueID::kContain)
                          : nullptr));
@@ -169,8 +170,10 @@
       const Vector<Length>& values = polygon->Values();
       for (unsigned i = 0; i < values.size(); i += 2) {
         polygon_value->AppendPoint(
-            CSSPrimitiveValue::Create(values.at(i), style.EffectiveZoom()),
-            CSSPrimitiveValue::Create(values.at(i + 1), style.EffectiveZoom()));
+            CSSPrimitiveValue::CreateFromLength(values.at(i),
+                                                style.EffectiveZoom()),
+            CSSPrimitiveValue::CreateFromLength(values.at(i + 1),
+                                                style.EffectiveZoom()));
       }
       return polygon_value;
     }
@@ -179,14 +182,14 @@
       auto* inset_value =
           MakeGarbageCollected<cssvalue::CSSBasicShapeInsetValue>();
 
-      inset_value->SetTop(
-          CSSPrimitiveValue::Create(inset->Top(), style.EffectiveZoom()));
-      inset_value->SetRight(
-          CSSPrimitiveValue::Create(inset->Right(), style.EffectiveZoom()));
-      inset_value->SetBottom(
-          CSSPrimitiveValue::Create(inset->Bottom(), style.EffectiveZoom()));
-      inset_value->SetLeft(
-          CSSPrimitiveValue::Create(inset->Left(), style.EffectiveZoom()));
+      inset_value->SetTop(CSSPrimitiveValue::CreateFromLength(
+          inset->Top(), style.EffectiveZoom()));
+      inset_value->SetRight(CSSPrimitiveValue::CreateFromLength(
+          inset->Right(), style.EffectiveZoom()));
+      inset_value->SetBottom(CSSPrimitiveValue::CreateFromLength(
+          inset->Bottom(), style.EffectiveZoom()));
+      inset_value->SetLeft(CSSPrimitiveValue::CreateFromLength(
+          inset->Left(), style.EffectiveZoom()));
 
       inset_value->SetTopLeftRadius(
           ValueForLengthSize(inset->TopLeftRadius(), style));
diff --git a/third_party/blink/renderer/core/css/css_axis_value.cc b/third_party/blink/renderer/core/css/css_axis_value.cc
index b0183b8..81e2585 100644
--- a/third_party/blink/renderer/core/css/css_axis_value.cc
+++ b/third_party/blink/renderer/core/css/css_axis_value.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/core/css/css_axis_value.h"
 
 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
+#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
 
@@ -32,9 +33,12 @@
     default:
       NOTREACHED();
   }
-  Append(*CSSPrimitiveValue::Create(x, CSSPrimitiveValue::UnitType::kNumber));
-  Append(*CSSPrimitiveValue::Create(y, CSSPrimitiveValue::UnitType::kNumber));
-  Append(*CSSPrimitiveValue::Create(z, CSSPrimitiveValue::UnitType::kNumber));
+  Append(
+      *CSSNumericLiteralValue::Create(x, CSSPrimitiveValue::UnitType::kNumber));
+  Append(
+      *CSSNumericLiteralValue::Create(y, CSSPrimitiveValue::UnitType::kNumber));
+  Append(
+      *CSSNumericLiteralValue::Create(z, CSSPrimitiveValue::UnitType::kNumber));
 }
 
 CSSAxisValue::CSSAxisValue(double x, double y, double z)
@@ -51,9 +55,12 @@
     z = 1;
     axis_name_ = CSSValueID::kZ;
   }
-  Append(*CSSPrimitiveValue::Create(x, CSSPrimitiveValue::UnitType::kNumber));
-  Append(*CSSPrimitiveValue::Create(y, CSSPrimitiveValue::UnitType::kNumber));
-  Append(*CSSPrimitiveValue::Create(z, CSSPrimitiveValue::UnitType::kNumber));
+  Append(
+      *CSSNumericLiteralValue::Create(x, CSSPrimitiveValue::UnitType::kNumber));
+  Append(
+      *CSSNumericLiteralValue::Create(y, CSSPrimitiveValue::UnitType::kNumber));
+  Append(
+      *CSSNumericLiteralValue::Create(z, CSSPrimitiveValue::UnitType::kNumber));
 }
 
 String CSSAxisValue::CustomCSSText() const {
diff --git a/third_party/blink/renderer/core/css/css_basic_shape_values.cc b/third_party/blink/renderer/core/css/css_basic_shape_values.cc
index 2639c9e..0c035a0 100644
--- a/third_party/blink/renderer/core/css/css_basic_shape_values.cc
+++ b/third_party/blink/renderer/core/css/css_basic_shape_values.cc
@@ -30,6 +30,7 @@
 #include "third_party/blink/renderer/core/css/css_basic_shape_values.h"
 
 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
+#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
 #include "third_party/blink/renderer/core/css/css_value_pair.h"
 #include "third_party/blink/renderer/platform/geometry/length.h"
@@ -90,9 +91,9 @@
     if ((side == CSSValueID::kRight || side == CSSValueID::kBottom) &&
         amount->IsPercentage()) {
       side = default_side;
-      amount =
-          CSSPrimitiveValue::Create(100 - amount->GetFloatValue(),
-                                    CSSPrimitiveValue::UnitType::kPercentage);
+      amount = CSSNumericLiteralValue::Create(
+          100 - amount->GetFloatValue(),
+          CSSPrimitiveValue::UnitType::kPercentage);
     }
   } else {
     amount = To<CSSPrimitiveValue>(offset);
@@ -100,14 +101,14 @@
 
   if (side == CSSValueID::kCenter) {
     side = default_side;
-    amount =
-        CSSPrimitiveValue::Create(50, CSSPrimitiveValue::UnitType::kPercentage);
+    amount = CSSNumericLiteralValue::Create(
+        50, CSSPrimitiveValue::UnitType::kPercentage);
   } else if (!amount || (amount->IsLength() && !amount->GetFloatValue())) {
     if (side == CSSValueID::kRight || side == CSSValueID::kBottom)
-      amount = CSSPrimitiveValue::Create(
+      amount = CSSNumericLiteralValue::Create(
           100, CSSPrimitiveValue::UnitType::kPercentage);
     else
-      amount = CSSPrimitiveValue::Create(
+      amount = CSSNumericLiteralValue::Create(
           0, CSSPrimitiveValue::UnitType::kPercentage);
     side = default_side;
   }
diff --git a/third_party/blink/renderer/core/css/css_calculation_value.cc b/third_party/blink/renderer/core/css/css_calculation_value.cc
index 6e65b97..f0fd1294 100644
--- a/third_party/blink/renderer/core/css/css_calculation_value.cc
+++ b/third_party/blink/renderer/core/css/css_calculation_value.cc
@@ -30,6 +30,7 @@
 
 #include "third_party/blink/renderer/core/css/css_calculation_value.h"
 
+#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value_mappings.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
@@ -177,7 +178,7 @@
   if (std::isnan(value) || std::isinf(value))
     return nullptr;
   return MakeGarbageCollected<CSSCalcPrimitiveValue>(
-      CSSPrimitiveValue::Create(value, type), is_integer);
+      CSSNumericLiteralValue::Create(value, type), is_integer);
 }
 
 CSSCalcPrimitiveValue::CSSCalcPrimitiveValue(CSSPrimitiveValue* value,
@@ -702,7 +703,7 @@
       return nullptr;
 
     return CSSCalcPrimitiveValue::Create(
-        CSSPrimitiveValue::Create(token.NumericValue(), type),
+        CSSNumericLiteralValue::Create(token.NumericValue(), type),
         token.GetNumericValueType() == kIntegerValueType);
   }
 
@@ -818,10 +819,10 @@
                                                           double percent) {
   return CreateExpressionNode(
       CreateExpressionNode(
-          CSSPrimitiveValue::Create(percent,
-                                    CSSPrimitiveValue::UnitType::kPercentage),
+          CSSNumericLiteralValue::Create(
+              percent, CSSPrimitiveValue::UnitType::kPercentage),
           percent == trunc(percent)),
-      CreateExpressionNode(CSSPrimitiveValue::Create(
+      CreateExpressionNode(CSSNumericLiteralValue::Create(
                                pixels, CSSPrimitiveValue::UnitType::kPixels),
                            pixels == trunc(pixels)),
       CSSMathOperator::kAdd);
diff --git a/third_party/blink/renderer/core/css/css_calculation_value.h b/third_party/blink/renderer/core/css/css_calculation_value.h
index 0a704f9..b7090eb 100644
--- a/third_party/blink/renderer/core/css/css_calculation_value.h
+++ b/third_party/blink/renderer/core/css/css_calculation_value.h
@@ -97,6 +97,7 @@
   bool is_nested_calc_ = false;
 };
 
+// TODO(crbug.com/825895): Rename it and make it store a CSSNumericLiteralValue
 class CSSCalcPrimitiveValue final : public CSSCalcExpressionNode {
  public:
   static CSSCalcPrimitiveValue* Create(CSSPrimitiveValue* value,
diff --git a/third_party/blink/renderer/core/css/css_calculation_value_test.cc b/third_party/blink/renderer/core/css/css_calculation_value_test.cc
index 21edc4b..e90935a 100644
--- a/third_party/blink/renderer/core/css/css_calculation_value_test.cc
+++ b/third_party/blink/renderer/core/css/css_calculation_value_test.cc
@@ -31,6 +31,7 @@
 #include "third_party/blink/renderer/core/css/css_calculation_value.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
 #include "third_party/blink/renderer/core/css/css_property_value_set.h"
 #include "third_party/blink/renderer/core/css/css_to_length_conversion_data.h"
@@ -86,7 +87,8 @@
   TestAccumulatePixelsAndPercent(
       conversion_data,
       CSSCalcValue::CreateExpressionNode(
-          CSSPrimitiveValue::Create(10, CSSPrimitiveValue::UnitType::kPixels),
+          CSSNumericLiteralValue::Create(10,
+                                         CSSPrimitiveValue::UnitType::kPixels),
           true),
       50, 0);
 
@@ -94,12 +96,12 @@
       conversion_data,
       CSSCalcValue::CreateExpressionNode(
           CSSCalcValue::CreateExpressionNode(
-              CSSPrimitiveValue::Create(10,
-                                        CSSPrimitiveValue::UnitType::kPixels),
+              CSSNumericLiteralValue::Create(
+                  10, CSSPrimitiveValue::UnitType::kPixels),
               true),
           CSSCalcValue::CreateExpressionNode(
-              CSSPrimitiveValue::Create(20,
-                                        CSSPrimitiveValue::UnitType::kPixels),
+              CSSNumericLiteralValue::Create(
+                  20, CSSPrimitiveValue::UnitType::kPixels),
               true),
           CSSMathOperator::kAdd),
       150, 0);
@@ -108,12 +110,12 @@
       conversion_data,
       CSSCalcValue::CreateExpressionNode(
           CSSCalcValue::CreateExpressionNode(
-              CSSPrimitiveValue::Create(1,
-                                        CSSPrimitiveValue::UnitType::kInches),
+              CSSNumericLiteralValue::Create(
+                  1, CSSPrimitiveValue::UnitType::kInches),
               true),
           CSSCalcValue::CreateExpressionNode(
-              CSSPrimitiveValue::Create(2,
-                                        CSSPrimitiveValue::UnitType::kNumber),
+              CSSNumericLiteralValue::Create(
+                  2, CSSPrimitiveValue::UnitType::kNumber),
               true),
           CSSMathOperator::kMultiply),
       960, 0);
@@ -123,21 +125,21 @@
       CSSCalcValue::CreateExpressionNode(
           CSSCalcValue::CreateExpressionNode(
               CSSCalcValue::CreateExpressionNode(
-                  CSSPrimitiveValue::Create(
+                  CSSNumericLiteralValue::Create(
                       50, CSSPrimitiveValue::UnitType::kPixels),
                   true),
               CSSCalcValue::CreateExpressionNode(
-                  CSSPrimitiveValue::Create(
+                  CSSNumericLiteralValue::Create(
                       0.25, CSSPrimitiveValue::UnitType::kNumber),
                   false),
               CSSMathOperator::kMultiply),
           CSSCalcValue::CreateExpressionNode(
               CSSCalcValue::CreateExpressionNode(
-                  CSSPrimitiveValue::Create(
+                  CSSNumericLiteralValue::Create(
                       20, CSSPrimitiveValue::UnitType::kPixels),
                   true),
               CSSCalcValue::CreateExpressionNode(
-                  CSSPrimitiveValue::Create(
+                  CSSNumericLiteralValue::Create(
                       40, CSSPrimitiveValue::UnitType::kPercentage),
                   false),
               CSSMathOperator::kSubtract),
diff --git a/third_party/blink/renderer/core/css/css_math_function_value.cc b/third_party/blink/renderer/core/css/css_math_function_value.cc
new file mode 100644
index 0000000..14efb05
--- /dev/null
+++ b/third_party/blink/renderer/core/css/css_math_function_value.cc
@@ -0,0 +1,40 @@
+// 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 "third_party/blink/renderer/core/css/css_math_function_value.h"
+
+#include "third_party/blink/renderer/core/css/css_calculation_value.h"
+#include "third_party/blink/renderer/platform/geometry/length.h"
+#include "third_party/blink/renderer/platform/wtf/size_assertions.h"
+
+namespace blink {
+
+struct SameSizeAsCSSMathFunctionValue : CSSPrimitiveValue {
+  Member<void*> calc;
+};
+ASSERT_SIZE(CSSMathFunctionValue, SameSizeAsCSSMathFunctionValue);
+
+void CSSMathFunctionValue::TraceAfterDispatch(blink::Visitor* visitor) {
+  visitor->Trace(calc_);
+  CSSPrimitiveValue::TraceAfterDispatch(visitor);
+}
+
+CSSMathFunctionValue::CSSMathFunctionValue(CSSCalcValue* calc)
+    : CSSPrimitiveValue(UnitType::kCalc, kMathFunctionClass), calc_(calc) {}
+
+// static
+CSSMathFunctionValue* CSSMathFunctionValue::Create(CSSCalcValue* calc) {
+  return MakeGarbageCollected<CSSMathFunctionValue>(calc);
+}
+
+// static
+CSSMathFunctionValue* CSSMathFunctionValue::Create(const Length& length,
+                                                   float zoom) {
+  const CalculationValue& calc = length.GetCalculationValue();
+  return Create(CSSCalcValue::Create(
+      CSSCalcValue::CreateExpressionNode(calc.Pixels() / zoom, calc.Percent()),
+      calc.GetValueRange()));
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/css/css_math_function_value.h b/third_party/blink/renderer/core/css/css_math_function_value.h
new file mode 100644
index 0000000..680f02cf
--- /dev/null
+++ b/third_party/blink/renderer/core/css/css_math_function_value.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 THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_MATH_FUNCTION_VALUE_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_MATH_FUNCTION_VALUE_H_
+
+#include "third_party/blink/renderer/core/css/css_primitive_value.h"
+
+namespace blink {
+
+// Numeric values that involve math functions (calc(), min(), max(), etc). This
+// is the equivalence of CSS Typed OM's |CSSMathValue| in the |CSSValue| class
+// hierarchy.
+class CORE_EXPORT CSSMathFunctionValue : public CSSPrimitiveValue {
+ public:
+  static CSSMathFunctionValue* Create(const Length&, float zoom);
+  static CSSMathFunctionValue* Create(CSSCalcValue*);
+
+  explicit CSSMathFunctionValue(CSSCalcValue*);
+
+  CSSCalcValue* CssCalcValue() const { return calc_; }
+
+  void TraceAfterDispatch(blink::Visitor* visitor);
+
+ private:
+  Member<CSSCalcValue> calc_;
+};
+
+template <>
+struct DowncastTraits<CSSMathFunctionValue> {
+  static bool AllowFrom(const CSSValue& value) {
+    return value.IsMathFunctionValue();
+  }
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_MATH_FUNCTION_VALUE_H_
diff --git a/third_party/blink/renderer/core/css/css_numeric_literal_value.cc b/third_party/blink/renderer/core/css/css_numeric_literal_value.cc
new file mode 100644
index 0000000..3446926
--- /dev/null
+++ b/third_party/blink/renderer/core/css/css_numeric_literal_value.cc
@@ -0,0 +1,75 @@
+// 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 "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
+
+#include "third_party/blink/renderer/core/css/css_value_pool.h"
+#include "third_party/blink/renderer/platform/wtf/size_assertions.h"
+
+namespace blink {
+
+struct SameSizeAsCSSNumericLiteralValue : CSSPrimitiveValue {
+  double num;
+};
+ASSERT_SIZE(CSSNumericLiteralValue, SameSizeAsCSSNumericLiteralValue);
+
+void CSSNumericLiteralValue::TraceAfterDispatch(blink::Visitor* visitor) {
+  CSSPrimitiveValue::TraceAfterDispatch(visitor);
+}
+
+CSSNumericLiteralValue::CSSNumericLiteralValue(double num, UnitType type)
+    : CSSPrimitiveValue(type, kNumericLiteralClass), num_(num) {
+  DCHECK(std::isfinite(num));
+}
+
+// static
+CSSNumericLiteralValue* CSSNumericLiteralValue::Create(double value,
+                                                       UnitType type) {
+  // TODO(timloh): This looks wrong.
+  if (std::isinf(value))
+    return nullptr;
+
+  if (value < 0 || value > CSSValuePool::kMaximumCacheableIntegerValue)
+    return MakeGarbageCollected<CSSNumericLiteralValue>(value, type);
+
+  int int_value = clampTo<int>(value);
+  if (value != int_value)
+    return MakeGarbageCollected<CSSNumericLiteralValue>(value, type);
+
+  // TODO(xiaochengh): Make |CSSValuePool| cache |CSSNumericLiteralValue| to
+  // avoid type casting.
+  CSSValuePool& pool = CssValuePool();
+  CSSPrimitiveValue* result = nullptr;
+  switch (type) {
+    case CSSPrimitiveValue::UnitType::kPixels:
+      result = pool.PixelCacheValue(int_value);
+      if (!result) {
+        result = pool.SetPixelCacheValue(
+            int_value,
+            MakeGarbageCollected<CSSNumericLiteralValue>(value, type));
+      }
+      return To<CSSNumericLiteralValue>(result);
+    case CSSPrimitiveValue::UnitType::kPercentage:
+      result = pool.PercentCacheValue(int_value);
+      if (!result) {
+        result = pool.SetPercentCacheValue(
+            int_value,
+            MakeGarbageCollected<CSSNumericLiteralValue>(value, type));
+      }
+      return To<CSSNumericLiteralValue>(result);
+    case CSSPrimitiveValue::UnitType::kNumber:
+    case CSSPrimitiveValue::UnitType::kInteger:
+      result = pool.NumberCacheValue(int_value);
+      if (!result) {
+        result = pool.SetNumberCacheValue(
+            int_value, MakeGarbageCollected<CSSNumericLiteralValue>(
+                           value, CSSPrimitiveValue::UnitType::kInteger));
+      }
+      return To<CSSNumericLiteralValue>(result);
+    default:
+      return MakeGarbageCollected<CSSNumericLiteralValue>(value, type);
+  }
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/css/css_numeric_literal_value.h b/third_party/blink/renderer/core/css/css_numeric_literal_value.h
new file mode 100644
index 0000000..4d2de315
--- /dev/null
+++ b/third_party/blink/renderer/core/css/css_numeric_literal_value.h
@@ -0,0 +1,38 @@
+// 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 THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_NUMERIC_LITERAL_VALUE_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_NUMERIC_LITERAL_VALUE_H_
+
+#include "third_party/blink/renderer/core/css/css_primitive_value.h"
+
+namespace blink {
+
+// Numeric values that can be expressed as a single unit (or a naked number or
+// percentage). The equivalence of CSS Typed OM's |CSSUnitValue| in the
+// |CSSValue| class hierarchy.
+class CORE_EXPORT CSSNumericLiteralValue : public CSSPrimitiveValue {
+ public:
+  static CSSNumericLiteralValue* Create(double num, UnitType);
+
+  CSSNumericLiteralValue(double num, UnitType type);
+
+  double DoubleValue() const { return num_; }
+
+  void TraceAfterDispatch(blink::Visitor* visitor);
+
+ private:
+  double num_;
+};
+
+template <>
+struct DowncastTraits<CSSNumericLiteralValue> {
+  static bool AllowFrom(const CSSValue& value) {
+    return value.IsNumericLiteralValue();
+  }
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_NUMERIC_LITERAL_VALUE_H_
diff --git a/third_party/blink/renderer/core/css/css_primitive_value.cc b/third_party/blink/renderer/core/css/css_primitive_value.cc
index 6e843c5d..2ee06c5 100644
--- a/third_party/blink/renderer/core/css/css_primitive_value.cc
+++ b/third_party/blink/renderer/core/css/css_primitive_value.cc
@@ -26,6 +26,8 @@
 #include "build/build_config.h"
 #include "third_party/blink/renderer/core/css/css_calculation_value.h"
 #include "third_party/blink/renderer/core/css/css_markup.h"
+#include "third_party/blink/renderer/core/css/css_math_function_value.h"
+#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_resolution_units.h"
 #include "third_party/blink/renderer/core/css/css_to_length_conversion_data.h"
 #include "third_party/blink/renderer/core/css/css_value_pool.h"
@@ -48,7 +50,6 @@
 }  // namespace
 
 struct SameSizeAsCSSPrimitiveValue : CSSValue {
-  double num;
 };
 ASSERT_SIZE(CSSPrimitiveValue, SameSizeAsCSSPrimitiveValue);
 
@@ -92,53 +93,11 @@
   }
 }
 
-CSSPrimitiveValue* CSSPrimitiveValue::Create(double value, UnitType type) {
-  // TODO(timloh): This looks wrong.
-  if (std::isinf(value))
-    value = 0;
-
-  if (value < 0 || value > CSSValuePool::kMaximumCacheableIntegerValue)
-    return MakeGarbageCollected<CSSPrimitiveValue>(value, type);
-
-  int int_value = clampTo<int>(value);
-  if (value != int_value)
-    return MakeGarbageCollected<CSSPrimitiveValue>(value, type);
-
-  CSSValuePool& pool = CssValuePool();
-  CSSPrimitiveValue* result = nullptr;
-  switch (type) {
-    case CSSPrimitiveValue::UnitType::kPixels:
-      result = pool.PixelCacheValue(int_value);
-      if (!result) {
-        result = pool.SetPixelCacheValue(
-            int_value, MakeGarbageCollected<CSSPrimitiveValue>(value, type));
-      }
-      return result;
-    case CSSPrimitiveValue::UnitType::kPercentage:
-      result = pool.PercentCacheValue(int_value);
-      if (!result) {
-        result = pool.SetPercentCacheValue(
-            int_value, MakeGarbageCollected<CSSPrimitiveValue>(value, type));
-      }
-      return result;
-    case CSSPrimitiveValue::UnitType::kNumber:
-    case CSSPrimitiveValue::UnitType::kInteger:
-      result = pool.NumberCacheValue(int_value);
-      if (!result)
-        result = pool.SetNumberCacheValue(
-            int_value, MakeGarbageCollected<CSSPrimitiveValue>(
-                           value, CSSPrimitiveValue::UnitType::kInteger));
-      return result;
-    default:
-      return MakeGarbageCollected<CSSPrimitiveValue>(value, type);
-  }
-}
-
 CSSPrimitiveValue::UnitType CSSPrimitiveValue::TypeWithCalcResolved() const {
   if (GetType() != UnitType::kCalc)
     return GetType();
 
-  switch (value_.calc->Category()) {
+  switch (CssCalcValue()->Category()) {
     case kCalcAngle:
       return UnitType::kDegrees;
     case kCalcFrequency:
@@ -165,69 +124,43 @@
   return UnitType::kUnknown;
 }
 
-CSSPrimitiveValue::CSSPrimitiveValue(double num, UnitType type)
-    : CSSValue(kPrimitiveClass) {
-  Init(type);
-  DCHECK(std::isfinite(num));
-  value_.num = num;
+CSSPrimitiveValue::CSSPrimitiveValue(UnitType unit_type, ClassType class_type)
+    : CSSValue(class_type) {
+  primitive_unit_type_ = static_cast<unsigned>(unit_type);
 }
 
-CSSPrimitiveValue::CSSPrimitiveValue(const Length& length, float zoom)
-    : CSSValue(kPrimitiveClass) {
+// static
+CSSPrimitiveValue* CSSPrimitiveValue::CreateFromLength(const Length& length,
+                                                       float zoom) {
   switch (length.GetType()) {
     case Length::kPercent:
-      Init(UnitType::kPercentage);
-      DCHECK(std::isfinite(length.Percent()));
-      value_.num = length.Percent();
-      break;
+      return CSSNumericLiteralValue::Create(length.Percent(),
+                                            UnitType::kPercentage);
     case Length::kFixed:
-      Init(UnitType::kPixels);
-      value_.num = length.Value() / zoom;
-      break;
+      return CSSNumericLiteralValue::Create(length.Value() / zoom,
+                                            UnitType::kPixels);
     case Length::kCalculated: {
       const CalculationValue& calc = length.GetCalculationValue();
-      if (calc.Pixels() && calc.Percent()) {
-        Init(CSSCalcValue::Create(CSSCalcValue::CreateExpressionNode(
-                                      calc.Pixels() / zoom, calc.Percent()),
-                                  calc.GetValueRange()));
-        break;
-      }
+      if (calc.Pixels() && calc.Percent())
+        return CSSMathFunctionValue::Create(length, zoom);
       if (calc.Percent()) {
-        Init(UnitType::kPercentage);
-        value_.num = calc.Percent();
-      } else {
-        Init(UnitType::kPixels);
-        value_.num = calc.Pixels() / zoom;
+        double num = calc.Percent();
+        if (num < 0 && calc.IsNonNegative())
+          num = 0;
+        return CSSNumericLiteralValue::Create(num, UnitType::kPercentage);
       }
-      if (value_.num < 0 && calc.IsNonNegative())
-        value_.num = 0;
-      break;
+      double num = calc.Pixels() / zoom;
+      if (num < 0 && calc.IsNonNegative())
+        num = 0;
+      return CSSNumericLiteralValue::Create(num, UnitType::kPixels);
     }
-    case Length::kAuto:
-    case Length::kMinContent:
-    case Length::kMaxContent:
-    case Length::kFillAvailable:
-    case Length::kFitContent:
-    case Length::kExtendToZoom:
-    case Length::kDeviceWidth:
-    case Length::kDeviceHeight:
-    case Length::kMaxSizeNone:
-      NOTREACHED();
+    default:
       break;
   }
+  NOTREACHED();
+  return nullptr;
 }
 
-void CSSPrimitiveValue::Init(UnitType type) {
-  primitive_unit_type_ = static_cast<unsigned>(type);
-}
-
-void CSSPrimitiveValue::Init(CSSCalcValue* c) {
-  Init(UnitType::kCalc);
-  value_.calc = c;
-}
-
-CSSPrimitiveValue::~CSSPrimitiveValue() = default;
-
 double CSSPrimitiveValue::ComputeSeconds() const {
   DCHECK(IsTime() ||
          (IsCalculated() && CssCalcValue()->Category() == kCalcTime));
@@ -325,7 +258,7 @@
 double CSSPrimitiveValue::ComputeLengthDouble(
     const CSSToLengthConversionData& conversion_data) const {
   if (GetType() == UnitType::kCalc)
-    return value_.calc->ComputeLengthPx(conversion_data);
+    return CssCalcValue()->ComputeLengthPx(conversion_data);
   return conversion_data.ZoomedComputedPixels(GetDoubleValue(), GetType());
 }
 
@@ -343,8 +276,8 @@
   bool conversion_success = UnitTypeToLengthUnitType(GetType(), length_type);
   DCHECK(conversion_success);
   length_array.values[length_type] +=
-      value_.num * ConversionToCanonicalUnitsScaleFactor(GetType()) *
-      multiplier;
+      To<CSSNumericLiteralValue>(this)->DoubleValue() *
+      ConversionToCanonicalUnitsScaleFactor(GetType()) * multiplier;
   length_array.type_flags.set(length_type);
 }
 
@@ -415,7 +348,9 @@
 }
 
 double CSSPrimitiveValue::GetDoubleValue() const {
-  return GetType() != UnitType::kCalc ? value_.num : value_.calc->DoubleValue();
+  return GetType() != UnitType::kCalc
+             ? To<CSSNumericLiteralValue>(this)->DoubleValue()
+             : CssCalcValue()->DoubleValue();
 }
 
 CSSPrimitiveValue::UnitType CSSPrimitiveValue::CanonicalUnitTypeForCategory(
@@ -649,21 +584,22 @@
       // be represented in non-exponential format with 6 digit precision.
       constexpr int kMinInteger = -999999;
       constexpr int kMaxInteger = 999999;
-      // If the value_.num is small integer, go the fast path.
-      if (value_.num < kMinInteger || value_.num > kMaxInteger ||
-          std::trunc(value_.num) != value_.num) {
-        text = FormatNumber(value_.num, UnitTypeToString(GetType()));
+      double value = To<CSSNumericLiteralValue>(this)->DoubleValue();
+      // If the value is small integer, go the fast path.
+      if (value < kMinInteger || value > kMaxInteger ||
+          std::trunc(value) != value) {
+        text = FormatNumber(value, UnitTypeToString(GetType()));
       } else {
         StringBuilder builder;
-        int value = value_.num;
+        int int_value = value;
         const char* unit_type = UnitTypeToString(GetType());
-        builder.AppendNumber(value);
+        builder.AppendNumber(int_value);
         builder.Append(unit_type, strlen(unit_type));
         text = builder.ToString();
       }
     } break;
     case UnitType::kCalc:
-      text = value_.calc->CustomCSSText();
+      text = CssCalcValue()->CustomCSSText();
       break;
     case UnitType::kCalcPercentageWithNumber:
     case UnitType::kCalcPercentageWithLength:
@@ -713,10 +649,11 @@
     case UnitType::kViewportMin:
     case UnitType::kViewportMax:
     case UnitType::kFraction:
-      return value_.num == other.value_.num;
+      return To<CSSNumericLiteralValue>(this)->DoubleValue() ==
+             To<CSSNumericLiteralValue>(other).DoubleValue();
     case UnitType::kCalc:
-      return value_.calc && other.value_.calc &&
-             value_.calc->Equals(*other.value_.calc);
+      return CssCalcValue() && other.CssCalcValue() &&
+             CssCalcValue()->Equals(*other.CssCalcValue());
     case UnitType::kChs:
     case UnitType::kCalcPercentageWithNumber:
     case UnitType::kCalcPercentageWithLength:
@@ -728,14 +665,12 @@
   return false;
 }
 
+CSSCalcValue* CSSPrimitiveValue::CssCalcValue() const {
+  DCHECK(IsCalculated());
+  return To<CSSMathFunctionValue>(this)->CssCalcValue();
+}
+
 void CSSPrimitiveValue::TraceAfterDispatch(blink::Visitor* visitor) {
-  switch (GetType()) {
-    case UnitType::kCalc:
-      visitor->Trace(value_.calc);
-      break;
-    default:
-      break;
-  }
   CSSValue::TraceAfterDispatch(visitor);
 }
 
diff --git a/third_party/blink/renderer/core/css/css_primitive_value.h b/third_party/blink/renderer/core/css/css_primitive_value.h
index 7381336..f2a0ad8d 100644
--- a/third_party/blink/renderer/core/css/css_primitive_value.h
+++ b/third_party/blink/renderer/core/css/css_primitive_value.h
@@ -62,8 +62,8 @@
   return static_cast<float>(value);
 }
 
-// CSSPrimitiveValue stores numeric data types (e.g. 1, 10px, 4%) and calc()
-// values (e.g. calc(3px + 2em)).
+// Common interface for numeric data types, including both literals (e.g. 1,
+// 10px, 4%) and values involving math functions (e.g. calc(3px + 2em)).
 class CORE_EXPORT CSSPrimitiveValue : public CSSValue {
  public:
   // These units are iterated through, so be careful when adding or changing the
@@ -106,6 +106,9 @@
     // Other units
     kFraction,
     kInteger,
+
+    // TODO(crbug.com/979895): Remove |kCalc|, and move/remove the remaining
+    // calc flags to CSSMathFunctionValue.
     kCalc,
     kCalcPercentageWithNumber,
     kCalcPercentageWithLength,
@@ -218,21 +221,12 @@
   static bool IsFlex(UnitType unit) { return unit == UnitType::kFraction; }
   bool IsFlex() const { return IsFlex(GetType()); }
 
-  static CSSPrimitiveValue* Create(double value, UnitType);
-  static CSSPrimitiveValue* Create(const Length& value, float zoom) {
-    return MakeGarbageCollected<CSSPrimitiveValue>(value, zoom);
-  }
-  static CSSPrimitiveValue* Create(CSSCalcValue* value) {
-    return MakeGarbageCollected<CSSPrimitiveValue>(value);
-  }
+  // Creates either a |CSSNumericLiteralValue| or a |CSSMathFunctionValue|,
+  // depending on whether |value| is calculated or not. We should never create a
+  // |CSSPrimitiveValue| that's not of any of its subclasses.
+  static CSSPrimitiveValue* CreateFromLength(const Length& value, float zoom);
 
-  CSSPrimitiveValue(const Length&, float zoom);
-  CSSPrimitiveValue(double, UnitType);
-  CSSPrimitiveValue(CSSCalcValue* val) : CSSValue(kPrimitiveClass) {
-    Init(val);
-  }
-  ~CSSPrimitiveValue();
-
+  // TODO(crbug.com/979895): Split this function properly.
   UnitType TypeWithCalcResolved() const;
 
   double ComputeDegrees() const;
@@ -246,6 +240,9 @@
   // Converts to a Length (Fixed, Percent or Calculated)
   Length ConvertToLength(const CSSToLengthConversionData&) const;
 
+  // TODO(crbug.com/979895): Make sure this functions are called only when
+  // applicable: on a numeric literal, or on a calc() that can be resolved into
+  // a single value without extra context (e.g., CSSToLengthConversionData).
   double GetDoubleValue() const;
   float GetFloatValue() const { return GetValue<float>(); }
   int GetIntValue() const { return GetValue<int>(); }
@@ -254,10 +251,8 @@
     return clampTo<T>(GetDoubleValue());
   }
 
-  CSSCalcValue* CssCalcValue() const {
-    DCHECK(IsCalculated());
-    return value_.calc;
-  }
+  // TODO(crbug.com/979895): Move this to |CSSMathFunctionValue|
+  CSSCalcValue* CssCalcValue() const;
 
   template <typename T>
   inline T ConvertTo() const;  // Defined in CSSPrimitiveValueMappings.h
@@ -283,27 +278,18 @@
   static bool UnitTypeToLengthUnitType(UnitType, LengthUnitType&);
   static UnitType LengthUnitTypeToUnitType(LengthUnitType);
 
- private:
+ protected:
+  CSSPrimitiveValue(UnitType unit_type, ClassType class_type);
+
   // Code generated by css_primitive_value_unit_trie.cc.tmpl
   static UnitType StringToUnitType(const LChar*, unsigned length);
   static UnitType StringToUnitType(const UChar*, unsigned length);
 
-  void Init(UnitType);
-  void Init(const Length&);
-  void Init(CSSCalcValue*);
-
   double ComputeLengthDouble(const CSSToLengthConversionData&) const;
 
   inline UnitType GetType() const {
     return static_cast<UnitType>(primitive_unit_type_);
   }
-
-  union {
-    double num;
-    // FIXME: oilpan: Should be a member, but no support for members in unions.
-    // Just trace the raw ptr for now.
-    CSSCalcValue* calc;
-  } value_;
 };
 
 using CSSLengthArray = CSSPrimitiveValue::CSSLengthArray;
diff --git a/third_party/blink/renderer/core/css/css_primitive_value_test.cc b/third_party/blink/renderer/core/css/css_primitive_value_test.cc
index c375ad1..da73b41 100644
--- a/third_party/blink/renderer/core/css/css_primitive_value_test.cc
+++ b/third_party/blink/renderer/core/css/css_primitive_value_test.cc
@@ -6,6 +6,8 @@
 
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/core/css/css_calculation_value.h"
+#include "third_party/blink/renderer/core/css/css_math_function_value.h"
+#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 
 namespace blink {
 namespace {
@@ -18,11 +20,11 @@
 };
 
 CSSPrimitiveValue* Create(UnitValue v) {
-  return CSSPrimitiveValue::Create(v.value, v.unit_type);
+  return CSSNumericLiteralValue::Create(v.value, v.unit_type);
 }
 
 CSSPrimitiveValue* CreateAddition(UnitValue a, UnitValue b) {
-  return CSSPrimitiveValue::Create(
+  return CSSMathFunctionValue::Create(
       CSSCalcValue::Create(CSSCalcValue::CreateExpressionNode(
           CSSCalcValue::CreateExpressionNode(Create(a)),
           CSSCalcValue::CreateExpressionNode(Create(b)),
diff --git a/third_party/blink/renderer/core/css/css_value.cc b/third_party/blink/renderer/core/css/css_value.cc
index 1e56eae..8bc8973 100644
--- a/third_party/blink/renderer/core/css/css_value.cc
+++ b/third_party/blink/renderer/core/css/css_value.cc
@@ -54,6 +54,8 @@
 #include "third_party/blink/renderer/core/css/css_initial_value.h"
 #include "third_party/blink/renderer/core/css/css_invalid_variable_value.h"
 #include "third_party/blink/renderer/core/css/css_layout_function_value.h"
+#include "third_party/blink/renderer/core/css/css_math_function_value.h"
+#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_paint_value.h"
 #include "third_party/blink/renderer/core/css/css_path_value.h"
 #include "third_party/blink/renderer/core/css/css_pending_substitution_value.h"
@@ -95,7 +97,7 @@
     case Length::kPercent:
     case Length::kFixed:
     case Length::kCalculated:
-      return CSSPrimitiveValue::Create(value, zoom);
+      return CSSPrimitiveValue::CreateFromLength(value, zoom);
     case Length::kDeviceWidth:
     case Length::kDeviceHeight:
     case Length::kMaxSizeNone:
@@ -214,7 +216,9 @@
         return CompareCSSValues<CSSGridTemplateAreasValue>(*this, other);
       case kPathClass:
         return CompareCSSValues<CSSPathValue>(*this, other);
-      case kPrimitiveClass:
+      case kNumericLiteralClass:
+      case kMathFunctionClass:
+        // TODO(crbug.com/979895): Should call into the subclasses.
         return CompareCSSValues<CSSPrimitiveValue>(*this, other);
       case kRayClass:
         return CompareCSSValues<CSSRayValue>(*this, other);
@@ -324,7 +328,9 @@
       return To<CSSGridTemplateAreasValue>(this)->CustomCSSText();
     case kPathClass:
       return To<CSSPathValue>(this)->CustomCSSText();
-    case kPrimitiveClass:
+    case kNumericLiteralClass:
+    case kMathFunctionClass:
+      // TODO(crbug.com/979895): Should call into the subclasses.
       return To<CSSPrimitiveValue>(this)->CustomCSSText();
     case kRayClass:
       return To<CSSRayValue>(this)->CustomCSSText();
@@ -462,8 +468,11 @@
     case kPathClass:
       To<CSSPathValue>(this)->~CSSPathValue();
       return;
-    case kPrimitiveClass:
-      To<CSSPrimitiveValue>(this)->~CSSPrimitiveValue();
+    case kNumericLiteralClass:
+      To<CSSNumericLiteralValue>(this)->~CSSNumericLiteralValue();
+      return;
+    case kMathFunctionClass:
+      To<CSSMathFunctionValue>(this)->~CSSMathFunctionValue();
       return;
     case kRayClass:
       To<CSSRayValue>(this)->~CSSRayValue();
@@ -619,8 +628,11 @@
     case kPathClass:
       To<CSSPathValue>(this)->TraceAfterDispatch(visitor);
       return;
-    case kPrimitiveClass:
-      To<CSSPrimitiveValue>(this)->TraceAfterDispatch(visitor);
+    case kNumericLiteralClass:
+      To<CSSNumericLiteralValue>(this)->TraceAfterDispatch(visitor);
+      return;
+    case kMathFunctionClass:
+      To<CSSMathFunctionValue>(this)->TraceAfterDispatch(visitor);
       return;
     case kRayClass:
       To<CSSRayValue>(this)->TraceAfterDispatch(visitor);
diff --git a/third_party/blink/renderer/core/css/css_value.h b/third_party/blink/renderer/core/css/css_value.h
index aa39904..bc02bde 100644
--- a/third_party/blink/renderer/core/css/css_value.h
+++ b/third_party/blink/renderer/core/css/css_value.h
@@ -49,7 +49,13 @@
 
   String CssText() const;
 
-  bool IsPrimitiveValue() const { return class_type_ == kPrimitiveClass; }
+  bool IsNumericLiteralValue() const {
+    return class_type_ == kNumericLiteralClass;
+  }
+  bool IsMathFunctionValue() const { return class_type_ == kMathFunctionClass; }
+  bool IsPrimitiveValue() const {
+    return IsNumericLiteralValue() || IsMathFunctionValue();
+  }
   bool IsIdentifierValue() const { return class_type_ == kIdentifierClass; }
   bool IsValuePair() const { return class_type_ == kValuePairClass; }
   bool IsValueList() const { return class_type_ >= kValueListClass; }
@@ -181,7 +187,8 @@
  protected:
   static const size_t kClassTypeBits = 6;
   enum ClassType {
-    kPrimitiveClass,
+    kNumericLiteralClass,
+    kMathFunctionClass,
     kIdentifierClass,
     kColorClass,
     kCounterClass,
diff --git a/third_party/blink/renderer/core/css/cssom/computed_style_property_map.cc b/third_party/blink/renderer/core/css/cssom/computed_style_property_map.cc
index 5ab8054..196ad3a 100644
--- a/third_party/blink/renderer/core/css/cssom/computed_style_property_map.cc
+++ b/third_party/blink/renderer/core/css/cssom/computed_style_property_map.cc
@@ -8,6 +8,7 @@
 #include "third_party/blink/renderer/core/css/css_custom_property_declaration.h"
 #include "third_party/blink/renderer/core/css/css_function_value.h"
 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
+#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_variable_data.h"
 #include "third_party/blink/renderer/core/css/properties/css_property_ref.h"
 #include "third_party/blink/renderer/core/dom/document.h"
@@ -36,12 +37,12 @@
       CSSFunctionValue* result = MakeGarbageCollected<CSSFunctionValue>(
           operation.Is3DOperation() ? CSSValueID::kScale3d
                                     : CSSValueID::kScale);
-      result->Append(*CSSPrimitiveValue::Create(
+      result->Append(*CSSNumericLiteralValue::Create(
           scale.X(), CSSPrimitiveValue::UnitType::kNumber));
-      result->Append(*CSSPrimitiveValue::Create(
+      result->Append(*CSSNumericLiteralValue::Create(
           scale.Y(), CSSPrimitiveValue::UnitType::kNumber));
       if (operation.Is3DOperation()) {
-        result->Append(*CSSPrimitiveValue::Create(
+        result->Append(*CSSNumericLiteralValue::Create(
             scale.Z(), CSSPrimitiveValue::UnitType::kNumber));
       }
       return result;
@@ -55,10 +56,10 @@
       CSSFunctionValue* result = MakeGarbageCollected<CSSFunctionValue>(
           operation.Is3DOperation() ? CSSValueID::kTranslate3d
                                     : CSSValueID::kTranslate);
-      result->Append(*CSSPrimitiveValue::Create(translate.X(), zoom));
-      result->Append(*CSSPrimitiveValue::Create(translate.Y(), zoom));
+      result->Append(*CSSPrimitiveValue::CreateFromLength(translate.X(), zoom));
+      result->Append(*CSSPrimitiveValue::CreateFromLength(translate.Y(), zoom));
       if (operation.Is3DOperation()) {
-        result->Append(*CSSPrimitiveValue::Create(
+        result->Append(*CSSNumericLiteralValue::Create(
             translate.Z(), CSSPrimitiveValue::UnitType::kPixels));
       }
       return result;
@@ -69,13 +70,13 @@
       const auto& rotate = ToRotateTransformOperation(operation);
       CSSFunctionValue* result =
           MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kRotate3d);
-      result->Append(*CSSPrimitiveValue::Create(
+      result->Append(*CSSNumericLiteralValue::Create(
           rotate.X(), CSSPrimitiveValue::UnitType::kNumber));
-      result->Append(*CSSPrimitiveValue::Create(
+      result->Append(*CSSNumericLiteralValue::Create(
           rotate.Y(), CSSPrimitiveValue::UnitType::kNumber));
-      result->Append(*CSSPrimitiveValue::Create(
+      result->Append(*CSSNumericLiteralValue::Create(
           rotate.Z(), CSSPrimitiveValue::UnitType::kNumber));
-      result->Append(*CSSPrimitiveValue::Create(
+      result->Append(*CSSNumericLiteralValue::Create(
           rotate.Angle(), CSSPrimitiveValue::UnitType::kDegrees));
       return result;
     }
@@ -83,30 +84,30 @@
       const auto& rotate = ToRotateTransformOperation(operation);
       auto* result =
           MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kRotate);
-      result->Append(*CSSPrimitiveValue::Create(
+      result->Append(*CSSNumericLiteralValue::Create(
           rotate.Angle(), CSSPrimitiveValue::UnitType::kDegrees));
       return result;
     }
     case TransformOperation::kSkewX: {
       const auto& skew = ToSkewTransformOperation(operation);
       auto* result = MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kSkewX);
-      result->Append(*CSSPrimitiveValue::Create(
+      result->Append(*CSSNumericLiteralValue::Create(
           skew.AngleX(), CSSPrimitiveValue::UnitType::kDegrees));
       return result;
     }
     case TransformOperation::kSkewY: {
       const auto& skew = ToSkewTransformOperation(operation);
       auto* result = MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kSkewY);
-      result->Append(*CSSPrimitiveValue::Create(
+      result->Append(*CSSNumericLiteralValue::Create(
           skew.AngleY(), CSSPrimitiveValue::UnitType::kDegrees));
       return result;
     }
     case TransformOperation::kSkew: {
       const auto& skew = ToSkewTransformOperation(operation);
       auto* result = MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kSkew);
-      result->Append(*CSSPrimitiveValue::Create(
+      result->Append(*CSSNumericLiteralValue::Create(
           skew.AngleX(), CSSPrimitiveValue::UnitType::kDegrees));
-      result->Append(*CSSPrimitiveValue::Create(
+      result->Append(*CSSNumericLiteralValue::Create(
           skew.AngleY(), CSSPrimitiveValue::UnitType::kDegrees));
       return result;
     }
@@ -114,7 +115,7 @@
       const auto& perspective = ToPerspectiveTransformOperation(operation);
       auto* result =
           MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kPerspective);
-      result->Append(*CSSPrimitiveValue::Create(
+      result->Append(*CSSNumericLiteralValue::Create(
           perspective.Perspective(), CSSPrimitiveValue::UnitType::kPixels));
       return result;
     }
@@ -125,7 +126,7 @@
       double values[6] = {matrix.A(), matrix.B(), matrix.C(),
                           matrix.D(), matrix.E(), matrix.F()};
       for (double value : values) {
-        result->Append(*CSSPrimitiveValue::Create(
+        result->Append(*CSSNumericLiteralValue::Create(
             value, CSSPrimitiveValue::UnitType::kNumber));
       }
       return result;
@@ -140,7 +141,7 @@
           matrix.M31(), matrix.M32(), matrix.M33(), matrix.M34(),
           matrix.M41(), matrix.M42(), matrix.M43(), matrix.M44()};
       for (double value : values) {
-        result->Append(*CSSPrimitiveValue::Create(
+        result->Append(*CSSNumericLiteralValue::Create(
             value, CSSPrimitiveValue::UnitType::kNumber));
       }
       return result;
diff --git a/third_party/blink/renderer/core/css/cssom/css_math_value.cc b/third_party/blink/renderer/core/css/cssom/css_math_value.cc
index 633fbbf..d443b26 100644
--- a/third_party/blink/renderer/core/css/cssom/css_math_value.cc
+++ b/third_party/blink/renderer/core/css/cssom/css_math_value.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/core/css/cssom/css_math_value.h"
 
 #include "third_party/blink/renderer/core/css/css_calculation_value.h"
+#include "third_party/blink/renderer/core/css/css_math_function_value.h"
 
 namespace blink {
 
@@ -12,7 +13,7 @@
   CSSCalcExpressionNode* node = ToCalcExpressionNode();
   if (!node)
     return nullptr;
-  return CSSPrimitiveValue::Create(CSSCalcValue::Create(node));
+  return CSSMathFunctionValue::Create(CSSCalcValue::Create(node));
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/cssom/css_matrix_component.cc b/third_party/blink/renderer/core/css/cssom/css_matrix_component.cc
index 1137dafa..d6545b6 100644
--- a/third_party/blink/renderer/core/css/cssom/css_matrix_component.cc
+++ b/third_party/blink/renderer/core/css/cssom/css_matrix_component.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/core/css/cssom/css_matrix_component.h"
 
+#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
 #include "third_party/blink/renderer/core/css/cssom/css_matrix_component_options.h"
 #include "third_party/blink/renderer/core/geometry/dom_matrix.h"
@@ -58,7 +59,7 @@
     double values[6] = {matrix_->a(), matrix_->b(), matrix_->c(),
                         matrix_->d(), matrix_->e(), matrix_->f()};
     for (double value : values) {
-      result->Append(*CSSPrimitiveValue::Create(
+      result->Append(*CSSNumericLiteralValue::Create(
           value, CSSPrimitiveValue::UnitType::kNumber));
     }
   } else {
@@ -68,7 +69,7 @@
         matrix_->m31(), matrix_->m32(), matrix_->m33(), matrix_->m34(),
         matrix_->m41(), matrix_->m42(), matrix_->m43(), matrix_->m44()};
     for (double value : values) {
-      result->Append(*CSSPrimitiveValue::Create(
+      result->Append(*CSSNumericLiteralValue::Create(
           value, CSSPrimitiveValue::UnitType::kNumber));
     }
   }
diff --git a/third_party/blink/renderer/core/css/cssom/css_perspective.cc b/third_party/blink/renderer/core/css/cssom/css_perspective.cc
index 3dacfe9..7b728d8 100644
--- a/third_party/blink/renderer/core/css/cssom/css_perspective.cc
+++ b/third_party/blink/renderer/core/css/cssom/css_perspective.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/core/css/cssom/css_perspective.h"
 
 #include "third_party/blink/renderer/core/css/css_calculation_value.h"
+#include "third_party/blink/renderer/core/css/css_math_function_value.h"
 #include "third_party/blink/renderer/core/css/cssom/css_unit_value.h"
 #include "third_party/blink/renderer/core/geometry/dom_matrix.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
@@ -69,7 +70,7 @@
     // Wrap out of range length with a calc.
     CSSCalcExpressionNode* node = length_->ToCalcExpressionNode();
     node->SetIsNestedCalc();
-    length = CSSPrimitiveValue::Create(CSSCalcValue::Create(node));
+    length = CSSMathFunctionValue::Create(CSSCalcValue::Create(node));
   } else {
     length = length_->ToCSSValue();
   }
diff --git a/third_party/blink/renderer/core/css/cssom/css_unit_value.cc b/third_party/blink/renderer/core/css/cssom/css_unit_value.cc
index 608ecd9..c1901b4 100644
--- a/third_party/blink/renderer/core/css/cssom/css_unit_value.cc
+++ b/third_party/blink/renderer/core/css/cssom/css_unit_value.cc
@@ -6,6 +6,8 @@
 
 #include "third_party/blink/renderer/core/animation/length_property_functions.h"
 #include "third_party/blink/renderer/core/css/css_calculation_value.h"
+#include "third_party/blink/renderer/core/css/css_math_function_value.h"
+#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_resolution_units.h"
 #include "third_party/blink/renderer/core/css/css_syntax_descriptor.h"
 #include "third_party/blink/renderer/core/css/cssom/css_math_invert.h"
@@ -169,7 +171,7 @@
 }
 
 const CSSPrimitiveValue* CSSUnitValue::ToCSSValue() const {
-  return CSSPrimitiveValue::Create(value_, unit_);
+  return CSSNumericLiteralValue::Create(value_, unit_);
 }
 
 const CSSPrimitiveValue* CSSUnitValue::ToCSSValueWithProperty(
@@ -179,15 +181,15 @@
     // Wrap out of range values with a calc.
     CSSCalcExpressionNode* node = ToCalcExpressionNode();
     node->SetIsNestedCalc();
-    return CSSPrimitiveValue::Create(CSSCalcValue::Create(node));
+    return CSSMathFunctionValue::Create(CSSCalcValue::Create(node));
   }
 
-  return CSSPrimitiveValue::Create(value_, unit_);
+  return CSSNumericLiteralValue::Create(value_, unit_);
 }
 
 CSSCalcExpressionNode* CSSUnitValue::ToCalcExpressionNode() const {
   return CSSCalcValue::CreateExpressionNode(
-      CSSPrimitiveValue::Create(value_, unit_));
+      CSSNumericLiteralValue::Create(value_, unit_));
 }
 
 CSSNumericValue* CSSUnitValue::Negate() {
diff --git a/third_party/blink/renderer/core/css/font_face_cache_test.cc b/third_party/blink/renderer/core/css/font_face_cache_test.cc
index fe60820..813528eb 100644
--- a/third_party/blink/renderer/core/css/font_face_cache_test.cc
+++ b/third_party/blink/renderer/core/css/font_face_cache_test.cc
@@ -9,6 +9,7 @@
 #include "third_party/blink/renderer/core/css/css_font_family_value.h"
 #include "third_party/blink/renderer/core/css/css_font_style_range_value.h"
 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
+#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_property_value_set.h"
 #include "third_party/blink/renderer/core/css/css_segmented_font_face.h"
 #include "third_party/blink/renderer/core/css/css_value_list.h"
@@ -115,7 +116,7 @@
       CSSIdentifierValue::Create(CSSValueID::kUltraExpanded);
   CSSIdentifierValue* stretch_value_condensed =
       CSSIdentifierValue::Create(CSSValueID::kCondensed);
-  CSSPrimitiveValue* weight_value = CSSPrimitiveValue::Create(
+  CSSPrimitiveValue* weight_value = CSSNumericLiteralValue::Create(
       BoldWeightValue(), CSSPrimitiveValue::UnitType::kNumber);
   CSSIdentifierValue* style_value =
       CSSIdentifierValue::Create(CSSValueID::kItalic);
@@ -132,7 +133,7 @@
       CSSIdentifierValue::Create(CSSValueID::kUltraExpanded);
   CSSIdentifierValue* stretch_value_condensed =
       CSSIdentifierValue::Create(CSSValueID::kCondensed);
-  CSSPrimitiveValue* weight_value = CSSPrimitiveValue::Create(
+  CSSPrimitiveValue* weight_value = CSSNumericLiteralValue::Create(
       NormalWeightValue(), CSSPrimitiveValue::UnitType::kNumber);
   CSSIdentifierValue* style_value =
       CSSIdentifierValue::Create(CSSValueID::kNormal);
@@ -164,11 +165,11 @@
   CSSIdentifierValue* style_value =
       CSSIdentifierValue::Create(CSSValueID::kNormal);
   CSSPrimitiveValue* weight_value_black =
-      CSSPrimitiveValue::Create(900, CSSPrimitiveValue::UnitType::kNumber);
+      CSSNumericLiteralValue::Create(900, CSSPrimitiveValue::UnitType::kNumber);
   AppendTestFaceForCapabilities(*stretch_value, *style_value,
                                 *weight_value_black);
   CSSPrimitiveValue* weight_value_thin =
-      CSSPrimitiveValue::Create(100, CSSPrimitiveValue::UnitType::kNumber);
+      CSSNumericLiteralValue::Create(100, CSSPrimitiveValue::UnitType::kNumber);
   AppendTestFaceForCapabilities(*stretch_value, *style_value,
                                 *weight_value_thin);
   ASSERT_EQ(cache_.GetNumSegmentedFacesForTesting(), 2ul);
@@ -234,8 +235,9 @@
   CSSValue* slopes[] = {CSSIdentifierValue::Create(CSSValueID::kNormal),
                         CSSIdentifierValue::Create(CSSValueID::kItalic)};
   CSSValue* weights[] = {
-      CSSPrimitiveValue::Create(100, CSSPrimitiveValue::UnitType::kNumber),
-      CSSPrimitiveValue::Create(900, CSSPrimitiveValue::UnitType::kNumber)};
+      CSSNumericLiteralValue::Create(100, CSSPrimitiveValue::UnitType::kNumber),
+      CSSNumericLiteralValue::Create(900,
+                                     CSSPrimitiveValue::UnitType::kNumber)};
 
   Vector<FontSelectionValue> width_choices = {CondensedWidthValue(),
                                               ExpandedWidthValue()};
@@ -295,18 +297,18 @@
   CSSIdentifierValue* style_value =
       CSSIdentifierValue::Create(CSSValueID::kNormal);
   CSSPrimitiveValue* weight_value_from =
-      CSSPrimitiveValue::Create(700, CSSPrimitiveValue::UnitType::kNumber);
+      CSSNumericLiteralValue::Create(700, CSSPrimitiveValue::UnitType::kNumber);
   CSSPrimitiveValue* weight_value_to =
-      CSSPrimitiveValue::Create(800, CSSPrimitiveValue::UnitType::kNumber);
+      CSSNumericLiteralValue::Create(800, CSSPrimitiveValue::UnitType::kNumber);
   CSSValueList* weight_list = CSSValueList::CreateSpaceSeparated();
   weight_list->Append(*weight_value_from);
   weight_list->Append(*weight_value_to);
   AppendTestFaceForCapabilities(*stretch_value, *style_value, *weight_list);
 
   CSSPrimitiveValue* second_weight_value_from =
-      CSSPrimitiveValue::Create(100, CSSPrimitiveValue::UnitType::kNumber);
+      CSSNumericLiteralValue::Create(100, CSSPrimitiveValue::UnitType::kNumber);
   CSSPrimitiveValue* second_weight_value_to =
-      CSSPrimitiveValue::Create(200, CSSPrimitiveValue::UnitType::kNumber);
+      CSSNumericLiteralValue::Create(200, CSSPrimitiveValue::UnitType::kNumber);
   CSSValueList* second_weight_list = CSSValueList::CreateSpaceSeparated();
   second_weight_list->Append(*second_weight_value_from);
   second_weight_list->Append(*second_weight_value_to);
@@ -340,15 +342,15 @@
       CSSIdentifierValue::Create(CSSValueID::kNormal);
 
   CSSPrimitiveValue* weight_values_lower[] = {
-      CSSPrimitiveValue::Create(600, CSSPrimitiveValue::UnitType::kNumber),
-      CSSPrimitiveValue::Create(415, CSSPrimitiveValue::UnitType::kNumber),
-      CSSPrimitiveValue::Create(475, CSSPrimitiveValue::UnitType::kNumber),
+      CSSNumericLiteralValue::Create(600, CSSPrimitiveValue::UnitType::kNumber),
+      CSSNumericLiteralValue::Create(415, CSSPrimitiveValue::UnitType::kNumber),
+      CSSNumericLiteralValue::Create(475, CSSPrimitiveValue::UnitType::kNumber),
   };
 
   CSSPrimitiveValue* weight_values_upper[] = {
-      CSSPrimitiveValue::Create(610, CSSPrimitiveValue::UnitType::kNumber),
-      CSSPrimitiveValue::Create(425, CSSPrimitiveValue::UnitType::kNumber),
-      CSSPrimitiveValue::Create(485, CSSPrimitiveValue::UnitType::kNumber),
+      CSSNumericLiteralValue::Create(610, CSSPrimitiveValue::UnitType::kNumber),
+      CSSNumericLiteralValue::Create(425, CSSPrimitiveValue::UnitType::kNumber),
+      CSSNumericLiteralValue::Create(485, CSSPrimitiveValue::UnitType::kNumber),
   };
 
   // From https://drafts.csswg.org/css-fonts-4/#font-style-matching: "If the
@@ -397,14 +399,14 @@
 }
 
 TEST_F(FontFaceCacheTest, StretchRangeMatching) {
-  CSSPrimitiveValue* stretch_value_from =
-      CSSPrimitiveValue::Create(65, CSSPrimitiveValue::UnitType::kPercentage);
-  CSSPrimitiveValue* stretch_value_to =
-      CSSPrimitiveValue::Create(70, CSSPrimitiveValue::UnitType::kPercentage);
+  CSSPrimitiveValue* stretch_value_from = CSSNumericLiteralValue::Create(
+      65, CSSPrimitiveValue::UnitType::kPercentage);
+  CSSPrimitiveValue* stretch_value_to = CSSNumericLiteralValue::Create(
+      70, CSSPrimitiveValue::UnitType::kPercentage);
   CSSIdentifierValue* style_value =
       CSSIdentifierValue::Create(CSSValueID::kNormal);
   CSSPrimitiveValue* weight_value =
-      CSSPrimitiveValue::Create(400, CSSPrimitiveValue::UnitType::kNumber);
+      CSSNumericLiteralValue::Create(400, CSSPrimitiveValue::UnitType::kNumber);
   CSSValueList* stretch_list = CSSValueList::CreateSpaceSeparated();
   stretch_list->Append(*stretch_value_from);
   stretch_list->Append(*stretch_value_to);
@@ -412,9 +414,9 @@
 
   const float kStretchFrom = 110;
   const float kStretchTo = 120;
-  CSSPrimitiveValue* second_stretch_value_from = CSSPrimitiveValue::Create(
+  CSSPrimitiveValue* second_stretch_value_from = CSSNumericLiteralValue::Create(
       kStretchFrom, CSSPrimitiveValue::UnitType::kPercentage);
-  CSSPrimitiveValue* second_stretch_value_to = CSSPrimitiveValue::Create(
+  CSSPrimitiveValue* second_stretch_value_to = CSSNumericLiteralValue::Create(
       kStretchTo, CSSPrimitiveValue::UnitType::kPercentage);
   CSSValueList* second_stretch_list = CSSValueList::CreateSpaceSeparated();
   second_stretch_list->Append(*second_stretch_value_from);
@@ -444,16 +446,16 @@
   CSSIdentifierValue* stretch_value =
       CSSIdentifierValue::Create(CSSValueID::kNormal);
   CSSPrimitiveValue* weight_value =
-      CSSPrimitiveValue::Create(400, CSSPrimitiveValue::UnitType::kNumber);
+      CSSNumericLiteralValue::Create(400, CSSPrimitiveValue::UnitType::kNumber);
 
   CSSIdentifierValue* oblique_keyword_value =
       CSSIdentifierValue::Create(CSSValueID::kOblique);
 
   CSSValueList* oblique_range = CSSValueList::CreateCommaSeparated();
   CSSPrimitiveValue* oblique_from =
-      CSSPrimitiveValue::Create(30, CSSPrimitiveValue::UnitType::kNumber);
+      CSSNumericLiteralValue::Create(30, CSSPrimitiveValue::UnitType::kNumber);
   CSSPrimitiveValue* oblique_to =
-      CSSPrimitiveValue::Create(35, CSSPrimitiveValue::UnitType::kNumber);
+      CSSNumericLiteralValue::Create(35, CSSPrimitiveValue::UnitType::kNumber);
   oblique_range->Append(*oblique_from);
   oblique_range->Append(*oblique_to);
   auto* oblique_value = MakeGarbageCollected<CSSFontStyleRangeValue>(
@@ -463,9 +465,9 @@
 
   CSSValueList* oblique_range_second = CSSValueList::CreateCommaSeparated();
   CSSPrimitiveValue* oblique_from_second =
-      CSSPrimitiveValue::Create(5, CSSPrimitiveValue::UnitType::kNumber);
+      CSSNumericLiteralValue::Create(5, CSSPrimitiveValue::UnitType::kNumber);
   CSSPrimitiveValue* oblique_to_second =
-      CSSPrimitiveValue::Create(10, CSSPrimitiveValue::UnitType::kNumber);
+      CSSNumericLiteralValue::Create(10, CSSPrimitiveValue::UnitType::kNumber);
   oblique_range_second->Append(*oblique_from_second);
   oblique_range_second->Append(*oblique_to_second);
   auto* oblique_value_second = MakeGarbageCollected<CSSFontStyleRangeValue>(
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_fast_paths.cc b/third_party/blink/renderer/core/css/parser/css_parser_fast_paths.cc
index 9ac477d..3086876 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser_fast_paths.cc
+++ b/third_party/blink/renderer/core/css/parser/css_parser_fast_paths.cc
@@ -10,6 +10,7 @@
 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
 #include "third_party/blink/renderer/core/css/css_inherited_value.h"
 #include "third_party/blink/renderer/core/css/css_initial_value.h"
+#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
 #include "third_party/blink/renderer/core/css/css_unset_value.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_idioms.h"
@@ -144,7 +145,7 @@
   if (number < 0 && !accepts_negative_numbers)
     return nullptr;
 
-  return CSSPrimitiveValue::Create(number, unit);
+  return CSSNumericLiteralValue::Create(number, unit);
 }
 
 static inline bool IsColorPropertyID(CSSPropertyID property_id) {
@@ -1152,7 +1153,7 @@
     if (unit != CSSPrimitiveValue::UnitType::kPixels &&
         (number || unit != CSSPrimitiveValue::UnitType::kNumber))
       return false;
-    transform_value->Append(*CSSPrimitiveValue::Create(
+    transform_value->Append(*CSSNumericLiteralValue::Create(
         number, CSSPrimitiveValue::UnitType::kPixels));
     pos += argument_length + 1;
     --expected_count;
@@ -1175,7 +1176,7 @@
     double number = CharactersToDouble(pos, argument_length, &ok);
     if (!ok)
       return false;
-    transform_value->Append(*CSSPrimitiveValue::Create(
+    transform_value->Append(*CSSNumericLiteralValue::Create(
         number, CSSPrimitiveValue::UnitType::kNumber));
     pos += argument_length + 1;
     --expected_count;
diff --git a/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc b/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc
index 9c17e8bf..c1b1386 100644
--- a/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc
+++ b/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc
@@ -12,6 +12,8 @@
 #include "third_party/blink/renderer/core/css/css_image_set_value.h"
 #include "third_party/blink/renderer/core/css/css_image_value.h"
 #include "third_party/blink/renderer/core/css/css_initial_value.h"
+#include "third_party/blink/renderer/core/css/css_math_function_value.h"
+#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_paint_value.h"
 #include "third_party/blink/renderer/core/css/css_property_value.h"
 #include "third_party/blink/renderer/core/css/css_shadow_value.h"
@@ -130,7 +132,7 @@
         double max_allowed = is_percentage ? 100.0 : 1.0;
         if (To<CSSPrimitiveValue>(parsed_value)->GetDoubleValue() >
             max_allowed) {
-          parsed_value = CSSPrimitiveValue::Create(
+          parsed_value = CSSNumericLiteralValue::Create(
               max_allowed, is_percentage
                                ? CSSPrimitiveValue::UnitType::kPercentage
                                : CSSPrimitiveValue::UnitType::kNumber);
@@ -202,7 +204,7 @@
     if (!calc_value_)
       return nullptr;
     source_range_ = range_;
-    return CSSPrimitiveValue::Create(calc_value_.Release());
+    return CSSMathFunctionValue::Create(calc_value_.Release());
   }
 
   CSSPrimitiveValue* ConsumeRoundedInt() {
@@ -212,7 +214,7 @@
     CSSPrimitiveValue::UnitType unit_type =
         CSSPrimitiveValue::UnitType::kInteger;
     double rounded_value = floor(calc_value_->DoubleValue() + 0.5);
-    return CSSPrimitiveValue::Create(rounded_value, unit_type);
+    return CSSNumericLiteralValue::Create(rounded_value, unit_type);
   }
 
   CSSPrimitiveValue* ConsumeNumber() {
@@ -222,7 +224,8 @@
     CSSPrimitiveValue::UnitType unit_type =
         calc_value_->IsInt() ? CSSPrimitiveValue::UnitType::kInteger
                              : CSSPrimitiveValue::UnitType::kNumber;
-    return CSSPrimitiveValue::Create(calc_value_->DoubleValue(), unit_type);
+    return CSSNumericLiteralValue::Create(calc_value_->DoubleValue(),
+                                          unit_type);
   }
 
   bool ConsumeNumberRaw(double& result) {
@@ -246,7 +249,7 @@
     if (token.GetNumericValueType() == kNumberValueType ||
         token.NumericValue() < minimum_value)
       return nullptr;
-    return CSSPrimitiveValue::Create(
+    return CSSNumericLiteralValue::Create(
         range.ConsumeIncludingWhitespace().NumericValue(),
         CSSPrimitiveValue::UnitType::kInteger);
   }
@@ -312,7 +315,7 @@
   if (token.GetType() == kNumberToken) {
     if (value_range == kValueRangeNonNegative && token.NumericValue() < 0)
       return nullptr;
-    return CSSPrimitiveValue::Create(
+    return CSSNumericLiteralValue::Create(
         range.ConsumeIncludingWhitespace().NumericValue(), token.GetUnitType());
   }
   CalcParser calc_parser(range, kValueRangeAll);
@@ -368,7 +371,7 @@
     }
     if (value_range == kValueRangeNonNegative && token.NumericValue() < 0)
       return nullptr;
-    return CSSPrimitiveValue::Create(
+    return CSSNumericLiteralValue::Create(
         range.ConsumeIncludingWhitespace().NumericValue(), token.GetUnitType());
   }
   if (token.GetType() == kNumberToken) {
@@ -380,7 +383,7 @@
         CSSPrimitiveValue::UnitType::kPixels;
     if (css_parser_mode == kSVGAttributeMode)
       unit_type = CSSPrimitiveValue::UnitType::kUserUnits;
-    return CSSPrimitiveValue::Create(
+    return CSSNumericLiteralValue::Create(
         range.ConsumeIncludingWhitespace().NumericValue(), unit_type);
   }
   if (css_parser_mode == kSVGAttributeMode)
@@ -397,7 +400,7 @@
   if (token.GetType() == kPercentageToken) {
     if (value_range == kValueRangeNonNegative && token.NumericValue() < 0)
       return nullptr;
-    return CSSPrimitiveValue::Create(
+    return CSSNumericLiteralValue::Create(
         range.ConsumeIncludingWhitespace().NumericValue(),
         CSSPrimitiveValue::UnitType::kPercentage);
   }
@@ -488,7 +491,7 @@
       case CSSPrimitiveValue::UnitType::kRadians:
       case CSSPrimitiveValue::UnitType::kGradians:
       case CSSPrimitiveValue::UnitType::kTurns:
-        return CSSPrimitiveValue::Create(
+        return CSSNumericLiteralValue::Create(
             range.ConsumeIncludingWhitespace().NumericValue(),
             token.GetUnitType());
       default:
@@ -499,7 +502,8 @@
       unitless_zero_feature) {
     range.ConsumeIncludingWhitespace();
     context->Count(*unitless_zero_feature);
-    return CSSPrimitiveValue::Create(0, CSSPrimitiveValue::UnitType::kDegrees);
+    return CSSNumericLiteralValue::Create(
+        0, CSSPrimitiveValue::UnitType::kDegrees);
   }
   CalcParser calc_parser(range, kValueRangeAll);
   if (const CSSCalcValue* calculation = calc_parser.Value()) {
@@ -507,12 +511,12 @@
       return nullptr;
     if (CSSPrimitiveValue* result = calc_parser.ConsumeValue()) {
       if (result->GetDoubleValue() < minimum_value) {
-        return CSSPrimitiveValue::Create(minimum_value,
-                                         result->TypeWithCalcResolved());
+        return CSSNumericLiteralValue::Create(minimum_value,
+                                              result->TypeWithCalcResolved());
       }
       if (result->GetDoubleValue() > maximum_value) {
-        return CSSPrimitiveValue::Create(maximum_value,
-                                         result->TypeWithCalcResolved());
+        return CSSNumericLiteralValue::Create(maximum_value,
+                                              result->TypeWithCalcResolved());
       }
       return result;
     }
@@ -538,7 +542,7 @@
     CSSPrimitiveValue::UnitType unit = token.GetUnitType();
     if (unit == CSSPrimitiveValue::UnitType::kMilliseconds ||
         unit == CSSPrimitiveValue::UnitType::kSeconds)
-      return CSSPrimitiveValue::Create(
+      return CSSNumericLiteralValue::Create(
           range.ConsumeIncludingWhitespace().NumericValue(),
           token.GetUnitType());
     return nullptr;
@@ -560,7 +564,7 @@
   if (unit == CSSPrimitiveValue::UnitType::kDotsPerPixel ||
       unit == CSSPrimitiveValue::UnitType::kDotsPerInch ||
       unit == CSSPrimitiveValue::UnitType::kDotsPerCentimeter) {
-    return CSSPrimitiveValue::Create(
+    return CSSNumericLiteralValue::Create(
         range.ConsumeIncludingWhitespace().NumericValue(), unit);
   }
   return nullptr;
@@ -1116,14 +1120,14 @@
   if (args.Peek().GetType() == kIdentToken) {
     if ((horizontal && ConsumeIdent<CSSValueID::kLeft>(args)) ||
         (!horizontal && ConsumeIdent<CSSValueID::kTop>(args)))
-      return CSSPrimitiveValue::Create(
+      return CSSNumericLiteralValue::Create(
           0., CSSPrimitiveValue::UnitType::kPercentage);
     if ((horizontal && ConsumeIdent<CSSValueID::kRight>(args)) ||
         (!horizontal && ConsumeIdent<CSSValueID::kBottom>(args)))
-      return CSSPrimitiveValue::Create(
+      return CSSNumericLiteralValue::Create(
           100., CSSPrimitiveValue::UnitType::kPercentage);
     if (ConsumeIdent<CSSValueID::kCenter>(args))
-      return CSSPrimitiveValue::Create(
+      return CSSNumericLiteralValue::Create(
           50., CSSPrimitiveValue::UnitType::kPercentage);
     return nullptr;
   }
@@ -1165,8 +1169,8 @@
       return false;
   }
 
-  stop.offset_ =
-      CSSPrimitiveValue::Create(position, CSSPrimitiveValue::UnitType::kNumber);
+  stop.offset_ = CSSNumericLiteralValue::Create(
+      position, CSSPrimitiveValue::UnitType::kNumber);
   stop.color_ = ConsumeDeprecatedGradientStopColor(args, css_parser_mode);
   return stop.color_ && args.AtEnd();
 }
@@ -1550,12 +1554,12 @@
 
   CSSPrimitiveValue* percentage = nullptr;
   if (CSSPrimitiveValue* percent_value = ConsumePercent(args, kValueRangeAll))
-    percentage = CSSPrimitiveValue::Create(
+    percentage = CSSNumericLiteralValue::Create(
         clampTo<double>(percent_value->GetDoubleValue() / 100.0, 0, 1),
         CSSPrimitiveValue::UnitType::kNumber);
   else if (CSSPrimitiveValue* number_value =
                ConsumeNumber(args, kValueRangeAll))
-    percentage = CSSPrimitiveValue::Create(
+    percentage = CSSNumericLiteralValue::Create(
         clampTo<double>(number_value->GetDoubleValue(), 0, 1),
         CSSPrimitiveValue::UnitType::kNumber);
 
@@ -1695,7 +1699,7 @@
     double image_scale_factor = token.NumericValue();
     if (image_scale_factor <= 0)
       return nullptr;
-    image_set->Append(*CSSPrimitiveValue::Create(
+    image_set->Append(*CSSNumericLiteralValue::Create(
         image_scale_factor, CSSPrimitiveValue::UnitType::kNumber));
   } while (ConsumeCommaIncludingWhitespace(args));
   if (!args.AtEnd())
diff --git a/third_party/blink/renderer/core/css/properties/computed_style_utils.cc b/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
index a722dfde..fa8511a 100644
--- a/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
+++ b/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
@@ -15,6 +15,7 @@
 #include "third_party/blink/renderer/core/css/css_function_value.h"
 #include "third_party/blink/renderer/core/css/css_grid_line_names_value.h"
 #include "third_party/blink/renderer/core/css/css_initial_value.h"
+#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value_mappings.h"
 #include "third_party/blink/renderer/core/css/css_quad_value.h"
 #include "third_party/blink/renderer/core/css/css_reflect_value.h"
@@ -305,11 +306,12 @@
 
   // TODO(alancutter): Make this code aware of calc lengths.
   if (image.ImageSlices().Top().IsPercentOrCalc()) {
-    top = CSSPrimitiveValue::Create(image.ImageSlices().Top().Value(),
-                                    CSSPrimitiveValue::UnitType::kPercentage);
+    top = CSSNumericLiteralValue::Create(
+        image.ImageSlices().Top().Value(),
+        CSSPrimitiveValue::UnitType::kPercentage);
   } else {
-    top = CSSPrimitiveValue::Create(image.ImageSlices().Top().Value(),
-                                    CSSPrimitiveValue::UnitType::kNumber);
+    top = CSSNumericLiteralValue::Create(image.ImageSlices().Top().Value(),
+                                         CSSPrimitiveValue::UnitType::kNumber);
   }
 
   if (image.ImageSlices().Right() == image.ImageSlices().Top() &&
@@ -320,12 +322,13 @@
     left = top;
   } else {
     if (image.ImageSlices().Right().IsPercentOrCalc()) {
-      right =
-          CSSPrimitiveValue::Create(image.ImageSlices().Right().Value(),
-                                    CSSPrimitiveValue::UnitType::kPercentage);
+      right = CSSNumericLiteralValue::Create(
+          image.ImageSlices().Right().Value(),
+          CSSPrimitiveValue::UnitType::kPercentage);
     } else {
-      right = CSSPrimitiveValue::Create(image.ImageSlices().Right().Value(),
-                                        CSSPrimitiveValue::UnitType::kNumber);
+      right =
+          CSSNumericLiteralValue::Create(image.ImageSlices().Right().Value(),
+                                         CSSPrimitiveValue::UnitType::kNumber);
     }
 
     if (image.ImageSlices().Bottom() == image.ImageSlices().Top() &&
@@ -334,26 +337,26 @@
       left = right;
     } else {
       if (image.ImageSlices().Bottom().IsPercentOrCalc()) {
-        bottom =
-            CSSPrimitiveValue::Create(image.ImageSlices().Bottom().Value(),
-                                      CSSPrimitiveValue::UnitType::kPercentage);
+        bottom = CSSNumericLiteralValue::Create(
+            image.ImageSlices().Bottom().Value(),
+            CSSPrimitiveValue::UnitType::kPercentage);
       } else {
-        bottom =
-            CSSPrimitiveValue::Create(image.ImageSlices().Bottom().Value(),
-                                      CSSPrimitiveValue::UnitType::kNumber);
+        bottom = CSSNumericLiteralValue::Create(
+            image.ImageSlices().Bottom().Value(),
+            CSSPrimitiveValue::UnitType::kNumber);
       }
 
       if (image.ImageSlices().Left() == image.ImageSlices().Right()) {
         left = right;
       } else {
         if (image.ImageSlices().Left().IsPercentOrCalc()) {
-          left = CSSPrimitiveValue::Create(
+          left = CSSNumericLiteralValue::Create(
               image.ImageSlices().Left().Value(),
               CSSPrimitiveValue::UnitType::kPercentage);
         } else {
-          left =
-              CSSPrimitiveValue::Create(image.ImageSlices().Left().Value(),
-                                        CSSPrimitiveValue::UnitType::kNumber);
+          left = CSSNumericLiteralValue::Create(
+              image.ImageSlices().Left().Value(),
+              CSSPrimitiveValue::UnitType::kNumber);
         }
       }
     }
@@ -369,8 +372,8 @@
     const BorderImageLength& border_image_length,
     const ComputedStyle& style) {
   if (border_image_length.IsNumber()) {
-    return CSSPrimitiveValue::Create(border_image_length.Number(),
-                                     CSSPrimitiveValue::UnitType::kNumber);
+    return CSSNumericLiteralValue::Create(border_image_length.Number(),
+                                          CSSPrimitiveValue::UnitType::kNumber);
   }
   return CSSValue::Create(border_image_length.length(), style.EffectiveZoom());
 }
@@ -477,9 +480,9 @@
   CSSPrimitiveValue* offset = nullptr;
   // TODO(alancutter): Make this work correctly for calc lengths.
   if (reflection->Offset().IsPercentOrCalc()) {
-    offset =
-        CSSPrimitiveValue::Create(reflection->Offset().Percent(),
-                                  CSSPrimitiveValue::UnitType::kPercentage);
+    offset = CSSNumericLiteralValue::Create(
+        reflection->Offset().Percent(),
+        CSSPrimitiveValue::UnitType::kPercentage);
   } else {
     offset = ZoomAdjustedPixelValue(reflection->Offset().Value(), style);
   }
@@ -585,8 +588,8 @@
       // is negative right. So we get the opposite length unit and see if it is
       // auto.
       if (opposite.IsAuto()) {
-        return CSSPrimitiveValue::Create(0,
-                                         CSSPrimitiveValue::UnitType::kPixels);
+        return CSSNumericLiteralValue::Create(
+            0, CSSPrimitiveValue::UnitType::kPixels);
       }
 
       if (opposite.IsPercentOrCalc()) {
@@ -769,8 +772,9 @@
 
 CSSPrimitiveValue* ComputedStyleUtils::ValueForFontStretch(
     const ComputedStyle& style) {
-  return CSSPrimitiveValue::Create(style.GetFontDescription().Stretch(),
-                                   CSSPrimitiveValue::UnitType::kPercentage);
+  return CSSNumericLiteralValue::Create(
+      style.GetFontDescription().Stretch(),
+      CSSPrimitiveValue::UnitType::kPercentage);
 }
 
 CSSValue* ComputedStyleUtils::ValueForFontStyle(const ComputedStyle& style) {
@@ -787,16 +791,16 @@
   // "20deg"', but since we compute that to 'italic' (handled above),
   // we don't perform any special treatment of that value here.
   CSSValueList* oblique_values = CSSValueList::CreateSpaceSeparated();
-  oblique_values->Append(
-      *CSSPrimitiveValue::Create(angle, CSSPrimitiveValue::UnitType::kDegrees));
+  oblique_values->Append(*CSSNumericLiteralValue::Create(
+      angle, CSSPrimitiveValue::UnitType::kDegrees));
   return MakeGarbageCollected<CSSFontStyleRangeValue>(
       *CSSIdentifierValue::Create(CSSValueID::kOblique), *oblique_values);
 }
 
 CSSPrimitiveValue* ComputedStyleUtils::ValueForFontWeight(
     const ComputedStyle& style) {
-  return CSSPrimitiveValue::Create(style.GetFontDescription().Weight(),
-                                   CSSPrimitiveValue::UnitType::kNumber);
+  return CSSNumericLiteralValue::Create(style.GetFontDescription().Weight(),
+                                        CSSPrimitiveValue::UnitType::kNumber);
 }
 
 CSSIdentifierValue* ComputedStyleUtils::ValueForFontVariantCaps(
@@ -1041,8 +1045,8 @@
 CSSValue* SpecifiedValueForGridTrackBreadth(const GridLength& track_breadth,
                                             const ComputedStyle& style) {
   if (!track_breadth.IsLength()) {
-    return CSSPrimitiveValue::Create(track_breadth.Flex(),
-                                     CSSPrimitiveValue::UnitType::kFraction);
+    return CSSNumericLiteralValue::Create(
+        track_breadth.Flex(), CSSPrimitiveValue::UnitType::kFraction);
   }
 
   const Length& track_breadth_length = track_breadth.length();
@@ -1062,7 +1066,7 @@
     case kMinMaxTrackSizing: {
       if (track_size.MinTrackBreadth().IsAuto() &&
           track_size.MaxTrackBreadth().IsFlex()) {
-        return CSSPrimitiveValue::Create(
+        return CSSNumericLiteralValue::Create(
             track_size.MaxTrackBreadth().Flex(),
             CSSPrimitiveValue::UnitType::kFraction);
       }
@@ -1282,10 +1286,10 @@
   CSSValueList* list = CSSValueList::CreateSpaceSeparated();
   if (position.IsSpan()) {
     list->Append(*CSSIdentifierValue::Create(CSSValueID::kSpan));
-    list->Append(*CSSPrimitiveValue::Create(
+    list->Append(*CSSNumericLiteralValue::Create(
         position.SpanPosition(), CSSPrimitiveValue::UnitType::kNumber));
   } else {
-    list->Append(*CSSPrimitiveValue::Create(
+    list->Append(*CSSNumericLiteralValue::Create(
         position.IntegerPosition(), CSSPrimitiveValue::UnitType::kNumber));
   }
 
@@ -1419,11 +1423,11 @@
   CSSValueList* list = CSSValueList::CreateCommaSeparated();
   if (timing_data) {
     for (wtf_size_t i = 0; i < timing_data->DelayList().size(); ++i) {
-      list->Append(*CSSPrimitiveValue::Create(
+      list->Append(*CSSNumericLiteralValue::Create(
           timing_data->DelayList()[i], CSSPrimitiveValue::UnitType::kSeconds));
     }
   } else {
-    list->Append(*CSSPrimitiveValue::Create(
+    list->Append(*CSSNumericLiteralValue::Create(
         CSSTimingData::InitialDelay(), CSSPrimitiveValue::UnitType::kSeconds));
   }
   return list;
@@ -1451,14 +1455,14 @@
   CSSValueList* list = CSSValueList::CreateCommaSeparated();
   if (timing_data) {
     for (wtf_size_t i = 0; i < timing_data->DurationList().size(); ++i) {
-      list->Append(
-          *CSSPrimitiveValue::Create(timing_data->DurationList()[i],
-                                     CSSPrimitiveValue::UnitType::kSeconds));
+      list->Append(*CSSNumericLiteralValue::Create(
+          timing_data->DurationList()[i],
+          CSSPrimitiveValue::UnitType::kSeconds));
     }
   } else {
     list->Append(
-        *CSSPrimitiveValue::Create(CSSTimingData::InitialDuration(),
-                                   CSSPrimitiveValue::UnitType::kSeconds));
+        *CSSNumericLiteralValue::Create(CSSTimingData::InitialDuration(),
+                                        CSSPrimitiveValue::UnitType::kSeconds));
   }
   return list;
 }
@@ -1484,8 +1488,8 @@
     double iteration_count) {
   if (iteration_count == std::numeric_limits<double>::infinity())
     return CSSIdentifierValue::Create(CSSValueID::kInfinite);
-  return CSSPrimitiveValue::Create(iteration_count,
-                                   CSSPrimitiveValue::UnitType::kNumber);
+  return CSSNumericLiteralValue::Create(iteration_count,
+                                        CSSPrimitiveValue::UnitType::kNumber);
 }
 
 CSSValue* ComputedStyleUtils::ValueForAnimationPlayState(
@@ -1566,13 +1570,13 @@
     const ComputedStyle& style) {
   CSSValueList* list = CSSValueList::CreateSpaceSeparated();
   if (radius.Width().IsPercent()) {
-    list->Append(*CSSPrimitiveValue::Create(
+    list->Append(*CSSNumericLiteralValue::Create(
         radius.Width().Percent(), CSSPrimitiveValue::UnitType::kPercentage));
   } else {
     list->Append(*ZoomAdjustedPixelValueForLength(radius.Width(), style));
   }
   if (radius.Height().IsPercent()) {
-    list->Append(*CSSPrimitiveValue::Create(
+    list->Append(*CSSNumericLiteralValue::Create(
         radius.Height().Percent(), CSSPrimitiveValue::UnitType::kPercentage));
   } else {
     list->Append(*ZoomAdjustedPixelValueForLength(radius.Height(), style));
@@ -1601,56 +1605,56 @@
     transform_value =
         MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kMatrix);
 
-    transform_value->Append(*CSSPrimitiveValue::Create(
+    transform_value->Append(*CSSNumericLiteralValue::Create(
         transform.A(), CSSPrimitiveValue::UnitType::kNumber));
-    transform_value->Append(*CSSPrimitiveValue::Create(
+    transform_value->Append(*CSSNumericLiteralValue::Create(
         transform.B(), CSSPrimitiveValue::UnitType::kNumber));
-    transform_value->Append(*CSSPrimitiveValue::Create(
+    transform_value->Append(*CSSNumericLiteralValue::Create(
         transform.C(), CSSPrimitiveValue::UnitType::kNumber));
-    transform_value->Append(*CSSPrimitiveValue::Create(
+    transform_value->Append(*CSSNumericLiteralValue::Create(
         transform.D(), CSSPrimitiveValue::UnitType::kNumber));
-    transform_value->Append(*CSSPrimitiveValue::Create(
+    transform_value->Append(*CSSNumericLiteralValue::Create(
         transform.E(), CSSPrimitiveValue::UnitType::kNumber));
-    transform_value->Append(*CSSPrimitiveValue::Create(
+    transform_value->Append(*CSSNumericLiteralValue::Create(
         transform.F(), CSSPrimitiveValue::UnitType::kNumber));
   } else {
     transform_value =
         MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kMatrix3d);
 
-    transform_value->Append(*CSSPrimitiveValue::Create(
+    transform_value->Append(*CSSNumericLiteralValue::Create(
         transform.M11(), CSSPrimitiveValue::UnitType::kNumber));
-    transform_value->Append(*CSSPrimitiveValue::Create(
+    transform_value->Append(*CSSNumericLiteralValue::Create(
         transform.M12(), CSSPrimitiveValue::UnitType::kNumber));
-    transform_value->Append(*CSSPrimitiveValue::Create(
+    transform_value->Append(*CSSNumericLiteralValue::Create(
         transform.M13(), CSSPrimitiveValue::UnitType::kNumber));
-    transform_value->Append(*CSSPrimitiveValue::Create(
+    transform_value->Append(*CSSNumericLiteralValue::Create(
         transform.M14(), CSSPrimitiveValue::UnitType::kNumber));
 
-    transform_value->Append(*CSSPrimitiveValue::Create(
+    transform_value->Append(*CSSNumericLiteralValue::Create(
         transform.M21(), CSSPrimitiveValue::UnitType::kNumber));
-    transform_value->Append(*CSSPrimitiveValue::Create(
+    transform_value->Append(*CSSNumericLiteralValue::Create(
         transform.M22(), CSSPrimitiveValue::UnitType::kNumber));
-    transform_value->Append(*CSSPrimitiveValue::Create(
+    transform_value->Append(*CSSNumericLiteralValue::Create(
         transform.M23(), CSSPrimitiveValue::UnitType::kNumber));
-    transform_value->Append(*CSSPrimitiveValue::Create(
+    transform_value->Append(*CSSNumericLiteralValue::Create(
         transform.M24(), CSSPrimitiveValue::UnitType::kNumber));
 
-    transform_value->Append(*CSSPrimitiveValue::Create(
+    transform_value->Append(*CSSNumericLiteralValue::Create(
         transform.M31(), CSSPrimitiveValue::UnitType::kNumber));
-    transform_value->Append(*CSSPrimitiveValue::Create(
+    transform_value->Append(*CSSNumericLiteralValue::Create(
         transform.M32(), CSSPrimitiveValue::UnitType::kNumber));
-    transform_value->Append(*CSSPrimitiveValue::Create(
+    transform_value->Append(*CSSNumericLiteralValue::Create(
         transform.M33(), CSSPrimitiveValue::UnitType::kNumber));
-    transform_value->Append(*CSSPrimitiveValue::Create(
+    transform_value->Append(*CSSNumericLiteralValue::Create(
         transform.M34(), CSSPrimitiveValue::UnitType::kNumber));
 
-    transform_value->Append(*CSSPrimitiveValue::Create(
+    transform_value->Append(*CSSNumericLiteralValue::Create(
         transform.M41(), CSSPrimitiveValue::UnitType::kNumber));
-    transform_value->Append(*CSSPrimitiveValue::Create(
+    transform_value->Append(*CSSNumericLiteralValue::Create(
         transform.M42(), CSSPrimitiveValue::UnitType::kNumber));
-    transform_value->Append(*CSSPrimitiveValue::Create(
+    transform_value->Append(*CSSNumericLiteralValue::Create(
         transform.M43(), CSSPrimitiveValue::UnitType::kNumber));
-    transform_value->Append(*CSSPrimitiveValue::Create(
+    transform_value->Append(*CSSNumericLiteralValue::Create(
         transform.M44(), CSSPrimitiveValue::UnitType::kNumber));
   }
 
@@ -1810,7 +1814,7 @@
     list->Append(*MakeGarbageCollected<CSSCustomIdentValue>(item.key));
     int32_t number =
         is_increment ? item.value.IncrementValue() : item.value.ResetValue();
-    list->Append(*CSSPrimitiveValue::Create(
+    list->Append(*CSSNumericLiteralValue::Create(
         (double)number, CSSPrimitiveValue::UnitType::kInteger));
   }
 
@@ -1994,35 +1998,35 @@
       case FilterOperation::GRAYSCALE:
         filter_value =
             MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kGrayscale);
-        filter_value->Append(*CSSPrimitiveValue::Create(
+        filter_value->Append(*CSSNumericLiteralValue::Create(
             To<BasicColorMatrixFilterOperation>(filter_operation)->Amount(),
             CSSPrimitiveValue::UnitType::kNumber));
         break;
       case FilterOperation::SEPIA:
         filter_value =
             MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kSepia);
-        filter_value->Append(*CSSPrimitiveValue::Create(
+        filter_value->Append(*CSSNumericLiteralValue::Create(
             To<BasicColorMatrixFilterOperation>(filter_operation)->Amount(),
             CSSPrimitiveValue::UnitType::kNumber));
         break;
       case FilterOperation::SATURATE:
         filter_value =
             MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kSaturate);
-        filter_value->Append(*CSSPrimitiveValue::Create(
+        filter_value->Append(*CSSNumericLiteralValue::Create(
             To<BasicColorMatrixFilterOperation>(filter_operation)->Amount(),
             CSSPrimitiveValue::UnitType::kNumber));
         break;
       case FilterOperation::HUE_ROTATE:
         filter_value =
             MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kHueRotate);
-        filter_value->Append(*CSSPrimitiveValue::Create(
+        filter_value->Append(*CSSNumericLiteralValue::Create(
             To<BasicColorMatrixFilterOperation>(filter_operation)->Amount(),
             CSSPrimitiveValue::UnitType::kDegrees));
         break;
       case FilterOperation::INVERT:
         filter_value =
             MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kInvert);
-        filter_value->Append(*CSSPrimitiveValue::Create(
+        filter_value->Append(*CSSNumericLiteralValue::Create(
             To<BasicComponentTransferFilterOperation>(filter_operation)
                 ->Amount(),
             CSSPrimitiveValue::UnitType::kNumber));
@@ -2030,7 +2034,7 @@
       case FilterOperation::OPACITY:
         filter_value =
             MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kOpacity);
-        filter_value->Append(*CSSPrimitiveValue::Create(
+        filter_value->Append(*CSSNumericLiteralValue::Create(
             To<BasicComponentTransferFilterOperation>(filter_operation)
                 ->Amount(),
             CSSPrimitiveValue::UnitType::kNumber));
@@ -2038,7 +2042,7 @@
       case FilterOperation::BRIGHTNESS:
         filter_value =
             MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kBrightness);
-        filter_value->Append(*CSSPrimitiveValue::Create(
+        filter_value->Append(*CSSNumericLiteralValue::Create(
             To<BasicComponentTransferFilterOperation>(filter_operation)
                 ->Amount(),
             CSSPrimitiveValue::UnitType::kNumber));
@@ -2046,7 +2050,7 @@
       case FilterOperation::CONTRAST:
         filter_value =
             MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kContrast);
-        filter_value->Append(*CSSPrimitiveValue::Create(
+        filter_value->Append(*CSSNumericLiteralValue::Create(
             To<BasicComponentTransferFilterOperation>(filter_operation)
                 ->Amount(),
             CSSPrimitiveValue::UnitType::kNumber));
diff --git a/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc b/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
index 7f7525dc..d7d96dfa 100644
--- a/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
+++ b/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
@@ -21,6 +21,7 @@
 #include "third_party/blink/renderer/core/css/css_grid_template_areas_value.h"
 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
 #include "third_party/blink/renderer/core/css/css_initial_value.h"
+#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_path_value.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
 #include "third_party/blink/renderer/core/css/css_property_names.h"
@@ -395,7 +396,7 @@
       return false;
     }
     context.Count(WebFeature::kUnitlessPerspectiveInTransformProperty);
-    parsed_value = CSSPrimitiveValue::Create(
+    parsed_value = CSSNumericLiteralValue::Create(
         perspective, CSSPrimitiveValue::UnitType::kPixels);
   }
   if (!parsed_value)
@@ -1296,7 +1297,8 @@
       value = clampTo<int>(counter_value->GetDoubleValue());
     list->Append(*MakeGarbageCollected<CSSValuePair>(
         counter_name,
-        CSSPrimitiveValue::Create(value, CSSPrimitiveValue::UnitType::kInteger),
+        CSSNumericLiteralValue::Create(value,
+                                       CSSPrimitiveValue::UnitType::kInteger),
         CSSValuePair::kDropIdenticalValues));
   } while (!range.AtEnd());
   return list;
@@ -1609,7 +1611,7 @@
       token.GetUnitType() == CSSPrimitiveValue::UnitType::kFraction) {
     if (range.Peek().NumericValue() < 0)
       return nullptr;
-    return CSSPrimitiveValue::Create(
+    return CSSNumericLiteralValue::Create(
         range.ConsumeIncludingWhitespace().NumericValue(),
         CSSPrimitiveValue::UnitType::kFraction);
   }
@@ -1904,7 +1906,7 @@
                      // invalid.
 
   if (numeric_value) {
-    numeric_value = CSSPrimitiveValue::Create(
+    numeric_value = CSSNumericLiteralValue::Create(
         clampTo(numeric_value->GetIntValue(), -kGridMaxTracks, kGridMaxTracks),
         CSSPrimitiveValue::UnitType::kInteger);
   }
diff --git a/third_party/blink/renderer/core/css/properties/css_parsing_utils.h b/third_party/blink/renderer/core/css/properties/css_parsing_utils.h
index bfe84e38..8cac7f3 100644
--- a/third_party/blink/renderer/core/css/properties/css_parsing_utils.h
+++ b/third_party/blink/renderer/core/css/properties/css_parsing_utils.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_CSS_PROPERTIES_CSS_PARSING_UTILS_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_PROPERTIES_CSS_PARSING_UTILS_H_
 
+#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_mode.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_token_range.h"
 #include "third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h"
@@ -255,8 +256,8 @@
     else
       return nullptr;
     range.ConsumeIncludingWhitespace();
-    return CSSPrimitiveValue::Create(percent,
-                                     CSSPrimitiveValue::UnitType::kPercentage);
+    return CSSNumericLiteralValue::Create(
+        percent, CSSPrimitiveValue::UnitType::kPercentage);
   }
   return css_property_parser_helpers::ConsumeLengthOrPercent(
       range, css_parser_mode, kValueRangeAll);
diff --git a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
index 47e99b6..1aed7ef 100644
--- a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
+++ b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
@@ -14,6 +14,7 @@
 #include "third_party/blink/renderer/core/css/css_grid_template_areas_value.h"
 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
 #include "third_party/blink/renderer/core/css/css_layout_function_value.h"
+#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value_mappings.h"
 #include "third_party/blink/renderer/core/css/css_quad_value.h"
@@ -146,8 +147,8 @@
 const CSSValue* AnimationDelay::InitialValue() const {
   DEFINE_STATIC_LOCAL(
       const Persistent<CSSValue>, value,
-      (CSSPrimitiveValue::Create(CSSTimingData::InitialDelay(),
-                                 CSSPrimitiveValue::UnitType::kSeconds)));
+      (CSSNumericLiteralValue::Create(CSSTimingData::InitialDelay(),
+                                      CSSPrimitiveValue::UnitType::kSeconds)));
   return value;
 }
 
@@ -207,8 +208,8 @@
 const CSSValue* AnimationDuration::InitialValue() const {
   DEFINE_STATIC_LOCAL(
       const Persistent<CSSValue>, value,
-      (CSSPrimitiveValue::Create(CSSTimingData::InitialDuration(),
-                                 CSSPrimitiveValue::UnitType::kSeconds)));
+      (CSSNumericLiteralValue::Create(CSSTimingData::InitialDuration(),
+                                      CSSPrimitiveValue::UnitType::kSeconds)));
   return value;
 }
 
@@ -279,8 +280,8 @@
 const CSSValue* AnimationIterationCount::InitialValue() const {
   DEFINE_STATIC_LOCAL(
       const Persistent<CSSValue>, value,
-      (CSSPrimitiveValue::Create(CSSAnimationData::InitialIterationCount(),
-                                 CSSPrimitiveValue::UnitType::kNumber)));
+      (CSSNumericLiteralValue::Create(CSSAnimationData::InitialIterationCount(),
+                                      CSSPrimitiveValue::UnitType::kNumber)));
   return value;
 }
 
@@ -829,9 +830,9 @@
 const CSSValue* BorderImageOutset::InitialValue() const {
   DEFINE_STATIC_LOCAL(
       const Persistent<CSSQuadValue>, value,
-      (CSSQuadValue::Create(
-          CSSPrimitiveValue::Create(0, CSSPrimitiveValue::UnitType::kInteger),
-          CSSQuadValue::kSerializeAsQuad)));
+      (CSSQuadValue::Create(CSSNumericLiteralValue::Create(
+                                0, CSSPrimitiveValue::UnitType::kInteger),
+                            CSSQuadValue::kSerializeAsQuad)));
   return value;
 }
 
@@ -879,7 +880,7 @@
       const Persistent<CSSBorderImageSliceValue>, value,
       (MakeGarbageCollected<CSSBorderImageSliceValue>(
           CSSQuadValue::Create(
-              CSSPrimitiveValue::Create(
+              CSSNumericLiteralValue::Create(
                   100, CSSPrimitiveValue::UnitType::kPercentage),
               CSSQuadValue::kSerializeAsQuad),
           /* fill */ false)));
@@ -936,9 +937,9 @@
 const CSSValue* BorderImageWidth::InitialValue() const {
   DEFINE_STATIC_LOCAL(
       const Persistent<CSSQuadValue>, value,
-      (CSSQuadValue::Create(
-          CSSPrimitiveValue::Create(1, CSSPrimitiveValue::UnitType::kInteger),
-          CSSQuadValue::kSerializeAsQuad)));
+      (CSSQuadValue::Create(CSSNumericLiteralValue::Create(
+                                1, CSSPrimitiveValue::UnitType::kInteger),
+                            CSSQuadValue::kSerializeAsQuad)));
   return value;
 }
 
@@ -1635,8 +1636,8 @@
     bool allow_visited_style) const {
   if (style.HasAutoColumnCount())
     return CSSIdentifierValue::Create(CSSValueID::kAuto);
-  return CSSPrimitiveValue::Create(style.ColumnCount(),
-                                   CSSPrimitiveValue::UnitType::kNumber);
+  return CSSNumericLiteralValue::Create(style.ColumnCount(),
+                                        CSSPrimitiveValue::UnitType::kNumber);
 }
 
 const CSSValue* ColumnFill::CSSValueFromComputedStyleInternal(
@@ -2395,8 +2396,8 @@
     const LayoutObject*,
     Node*,
     bool allow_visited_style) const {
-  return CSSPrimitiveValue::Create(svg_style.FillOpacity(),
-                                   CSSPrimitiveValue::UnitType::kNumber);
+  return CSSNumericLiteralValue::Create(svg_style.FillOpacity(),
+                                        CSSPrimitiveValue::UnitType::kNumber);
 }
 
 const CSSValue* FillRule::CSSValueFromComputedStyleInternal(
@@ -2466,8 +2467,8 @@
     const LayoutObject*,
     Node*,
     bool allow_visited_style) const {
-  return CSSPrimitiveValue::Create(style.FlexGrow(),
-                                   CSSPrimitiveValue::UnitType::kNumber);
+  return CSSNumericLiteralValue::Create(style.FlexGrow(),
+                                        CSSPrimitiveValue::UnitType::kNumber);
 }
 
 const CSSValue* FlexShrink::ParseSingleValue(
@@ -2484,8 +2485,8 @@
     const LayoutObject*,
     Node*,
     bool allow_visited_style) const {
-  return CSSPrimitiveValue::Create(style.FlexShrink(),
-                                   CSSPrimitiveValue::UnitType::kNumber);
+  return CSSNumericLiteralValue::Create(style.FlexShrink(),
+                                        CSSPrimitiveValue::UnitType::kNumber);
 }
 
 const CSSValue* FlexWrap::CSSValueFromComputedStyleInternal(
@@ -2547,8 +2548,8 @@
     const LayoutObject*,
     Node*,
     bool allow_visited_style) const {
-  return CSSPrimitiveValue::Create(svg_style.FloodOpacity(),
-                                   CSSPrimitiveValue::UnitType::kNumber);
+  return CSSNumericLiteralValue::Create(svg_style.FloodOpacity(),
+                                        CSSPrimitiveValue::UnitType::kNumber);
 }
 
 const CSSValue* FontFamily::ParseSingleValue(
@@ -2621,8 +2622,8 @@
     Node* styled_node,
     bool allow_visited_style) const {
   if (style.HasFontSizeAdjust()) {
-    return CSSPrimitiveValue::Create(style.FontSizeAdjust(),
-                                     CSSPrimitiveValue::UnitType::kNumber);
+    return CSSNumericLiteralValue::Create(style.FontSizeAdjust(),
+                                          CSSPrimitiveValue::UnitType::kNumber);
   }
   return CSSIdentifierValue::Create(CSSValueID::kNone);
 }
@@ -3277,7 +3278,8 @@
     bool allow_visited_style) const {
   if (style.RespectImageOrientation() == kRespectImageOrientation)
     return CSSIdentifierValue::Create(CSSValueID::kFromImage);
-  return CSSPrimitiveValue::Create(0, CSSPrimitiveValue::UnitType::kDegrees);
+  return CSSNumericLiteralValue::Create(0,
+                                        CSSPrimitiveValue::UnitType::kDegrees);
 }
 
 const CSSValue* ImageRendering::CSSValueFromComputedStyleInternal(
@@ -4361,7 +4363,7 @@
   CSSValueList* list = CSSValueList::CreateSpaceSeparated();
   if (style.OffsetRotate().type == OffsetRotationType::kAuto)
     list->Append(*CSSIdentifierValue::Create(CSSValueID::kAuto));
-  list->Append(*CSSPrimitiveValue::Create(
+  list->Append(*CSSNumericLiteralValue::Create(
       style.OffsetRotate().angle, CSSPrimitiveValue::UnitType::kDegrees));
   return list;
 }
@@ -4378,8 +4380,8 @@
     const LayoutObject*,
     Node*,
     bool allow_visited_style) const {
-  return CSSPrimitiveValue::Create(style.Opacity(),
-                                   CSSPrimitiveValue::UnitType::kNumber);
+  return CSSNumericLiteralValue::Create(style.Opacity(),
+                                        CSSPrimitiveValue::UnitType::kNumber);
 }
 
 const CSSValue* Order::ParseSingleValue(CSSParserTokenRange& range,
@@ -4394,8 +4396,8 @@
     const LayoutObject*,
     Node*,
     bool allow_visited_style) const {
-  return CSSPrimitiveValue::Create(style.Order(),
-                                   CSSPrimitiveValue::UnitType::kNumber);
+  return CSSNumericLiteralValue::Create(style.Order(),
+                                        CSSPrimitiveValue::UnitType::kNumber);
 }
 
 const CSSValue* Orphans::ParseSingleValue(CSSParserTokenRange& range,
@@ -4410,8 +4412,8 @@
     const LayoutObject*,
     Node*,
     bool allow_visited_style) const {
-  return CSSPrimitiveValue::Create(style.Orphans(),
-                                   CSSPrimitiveValue::UnitType::kNumber);
+  return CSSNumericLiteralValue::Create(style.Orphans(),
+                                        CSSPrimitiveValue::UnitType::kNumber);
 }
 
 const CSSValue* OutlineColor::ParseSingleValue(
@@ -4832,7 +4834,7 @@
     if (!css_property_parser_helpers::ConsumeNumberRaw(range, perspective))
       return nullptr;
     context.Count(WebFeature::kUnitlessPerspectiveInPerspectiveProperty);
-    parsed_value = CSSPrimitiveValue::Create(
+    parsed_value = CSSNumericLiteralValue::Create(
         perspective, CSSPrimitiveValue::UnitType::kPixels);
   }
   if (parsed_value &&
@@ -5070,14 +5072,14 @@
   CSSValueList* list = CSSValueList::CreateSpaceSeparated();
   if (style.Rotate()->X() != 0 || style.Rotate()->Y() != 0 ||
       style.Rotate()->Z() != 1) {
-    list->Append(*CSSPrimitiveValue::Create(
+    list->Append(*CSSNumericLiteralValue::Create(
         style.Rotate()->X(), CSSPrimitiveValue::UnitType::kNumber));
-    list->Append(*CSSPrimitiveValue::Create(
+    list->Append(*CSSNumericLiteralValue::Create(
         style.Rotate()->Y(), CSSPrimitiveValue::UnitType::kNumber));
-    list->Append(*CSSPrimitiveValue::Create(
+    list->Append(*CSSNumericLiteralValue::Create(
         style.Rotate()->Z(), CSSPrimitiveValue::UnitType::kNumber));
   }
-  list->Append(*CSSPrimitiveValue::Create(
+  list->Append(*CSSNumericLiteralValue::Create(
       style.Rotate()->Angle(), CSSPrimitiveValue::UnitType::kDegrees));
   return list;
 }
@@ -5170,12 +5172,12 @@
   if (!style.Scale())
     return CSSIdentifierValue::Create(CSSValueID::kNone);
   CSSValueList* list = CSSValueList::CreateSpaceSeparated();
-  list->Append(*CSSPrimitiveValue::Create(
+  list->Append(*CSSNumericLiteralValue::Create(
       style.Scale()->X(), CSSPrimitiveValue::UnitType::kNumber));
-  list->Append(*CSSPrimitiveValue::Create(
+  list->Append(*CSSNumericLiteralValue::Create(
       style.Scale()->Y(), CSSPrimitiveValue::UnitType::kNumber));
   if (style.Scale()->Z() != 1) {
-    list->Append(*CSSPrimitiveValue::Create(
+    list->Append(*CSSNumericLiteralValue::Create(
         style.Scale()->Z(), CSSPrimitiveValue::UnitType::kNumber));
   }
   return list;
@@ -5548,8 +5550,8 @@
     const LayoutObject*,
     Node*,
     bool allow_visited_style) const {
-  return CSSPrimitiveValue::Create(style.ShapeImageThreshold(),
-                                   CSSPrimitiveValue::UnitType::kNumber);
+  return CSSNumericLiteralValue::Create(style.ShapeImageThreshold(),
+                                        CSSPrimitiveValue::UnitType::kNumber);
 }
 
 const CSSValue* ShapeMargin::ParseSingleValue(
@@ -5797,8 +5799,8 @@
     const LayoutObject*,
     Node*,
     bool allow_visited_style) const {
-  return CSSPrimitiveValue::Create(svg_style.StopOpacity(),
-                                   CSSPrimitiveValue::UnitType::kNumber);
+  return CSSNumericLiteralValue::Create(svg_style.StopOpacity(),
+                                        CSSPrimitiveValue::UnitType::kNumber);
 }
 
 const CSSValue* Stroke::ParseSingleValue(CSSParserTokenRange& range,
@@ -5900,8 +5902,8 @@
     const LayoutObject*,
     Node*,
     bool allow_visited_style) const {
-  return CSSPrimitiveValue::Create(svg_style.StrokeMiterLimit(),
-                                   CSSPrimitiveValue::UnitType::kNumber);
+  return CSSNumericLiteralValue::Create(svg_style.StrokeMiterLimit(),
+                                        CSSPrimitiveValue::UnitType::kNumber);
 }
 
 const CSSValue* StrokeOpacity::ParseSingleValue(
@@ -5917,8 +5919,8 @@
     const LayoutObject*,
     Node*,
     bool allow_visited_style) const {
-  return CSSPrimitiveValue::Create(svg_style.StrokeOpacity(),
-                                   CSSPrimitiveValue::UnitType::kNumber);
+  return CSSNumericLiteralValue::Create(svg_style.StrokeOpacity(),
+                                        CSSPrimitiveValue::UnitType::kNumber);
 }
 
 const CSSValue* StrokeWidth::ParseSingleValue(
@@ -5938,8 +5940,8 @@
     bool allow_visited_style) const {
   const Length& length = svg_style.StrokeWidth().length();
   if (length.IsFixed()) {
-    return CSSPrimitiveValue::Create(length.Value(),
-                                     CSSPrimitiveValue::UnitType::kPixels);
+    return CSSNumericLiteralValue::Create(length.Value(),
+                                          CSSPrimitiveValue::UnitType::kPixels);
   }
   return CSSValue::Create(length, style.EffectiveZoom());
 }
@@ -5961,10 +5963,10 @@
     const LayoutObject*,
     Node*,
     bool allow_visited_style) const {
-  return CSSPrimitiveValue::Create(style.GetTabSize().GetPixelSize(1.0),
-                                   style.GetTabSize().IsSpaces()
-                                       ? CSSPrimitiveValue::UnitType::kNumber
-                                       : CSSPrimitiveValue::UnitType::kPixels);
+  return CSSNumericLiteralValue::Create(
+      style.GetTabSize().GetPixelSize(1.0),
+      style.GetTabSize().IsSpaces() ? CSSPrimitiveValue::UnitType::kNumber
+                                    : CSSPrimitiveValue::UnitType::kPixels);
 }
 
 const CSSValue* TableLayout::CSSValueFromComputedStyleInternal(
@@ -6291,8 +6293,9 @@
     bool allow_visited_style) const {
   if (style.GetTextSizeAdjust().IsAuto())
     return CSSIdentifierValue::Create(CSSValueID::kAuto);
-  return CSSPrimitiveValue::Create(style.GetTextSizeAdjust().Multiplier() * 100,
-                                   CSSPrimitiveValue::UnitType::kPercentage);
+  return CSSNumericLiteralValue::Create(
+      style.GetTextSizeAdjust().Multiplier() * 100,
+      CSSPrimitiveValue::UnitType::kPercentage);
 }
 
 const CSSValue* TextTransform::CSSValueFromComputedStyleInternal(
@@ -6565,8 +6568,8 @@
 const CSSValue* TransitionDelay::InitialValue() const {
   DEFINE_STATIC_LOCAL(
       const Persistent<CSSValue>, value,
-      (CSSPrimitiveValue::Create(CSSTimingData::InitialDelay(),
-                                 CSSPrimitiveValue::UnitType::kSeconds)));
+      (CSSNumericLiteralValue::Create(CSSTimingData::InitialDelay(),
+                                      CSSPrimitiveValue::UnitType::kSeconds)));
   return value;
 }
 
@@ -6590,8 +6593,8 @@
 const CSSValue* TransitionDuration::InitialValue() const {
   DEFINE_STATIC_LOCAL(
       const Persistent<CSSValue>, value,
-      (CSSPrimitiveValue::Create(CSSTimingData::InitialDuration(),
-                                 CSSPrimitiveValue::UnitType::kSeconds)));
+      (CSSNumericLiteralValue::Create(CSSTimingData::InitialDuration(),
+                                      CSSPrimitiveValue::UnitType::kSeconds)));
   return value;
 }
 
@@ -6949,8 +6952,8 @@
     const LayoutObject*,
     Node*,
     bool allow_visited_style) const {
-  return CSSPrimitiveValue::Create(style.BoxFlex(),
-                                   CSSPrimitiveValue::UnitType::kNumber);
+  return CSSNumericLiteralValue::Create(style.BoxFlex(),
+                                        CSSPrimitiveValue::UnitType::kNumber);
 }
 
 const CSSValue* WebkitBoxOrdinalGroup::ParseSingleValue(
@@ -6966,8 +6969,8 @@
     const LayoutObject*,
     Node*,
     bool allow_visited_style) const {
-  return CSSPrimitiveValue::Create(style.BoxOrdinalGroup(),
-                                   CSSPrimitiveValue::UnitType::kNumber);
+  return CSSNumericLiteralValue::Create(style.BoxOrdinalGroup(),
+                                        CSSPrimitiveValue::UnitType::kNumber);
 }
 
 const CSSValue* WebkitBoxOrient::CSSValueFromComputedStyleInternal(
@@ -7000,7 +7003,8 @@
 
   CSSPrimitiveValue* offset = nullptr;
   if (range.AtEnd()) {
-    offset = CSSPrimitiveValue::Create(0, CSSPrimitiveValue::UnitType::kPixels);
+    offset =
+        CSSNumericLiteralValue::Create(0, CSSPrimitiveValue::UnitType::kPixels);
   } else {
     offset = ConsumeLengthOrPercent(
         range, context.Mode(), kValueRangeAll,
@@ -7120,8 +7124,8 @@
     bool allow_visited_style) const {
   if (!style.HasLineClamp())
     return CSSIdentifierValue::Create(CSSValueID::kNone);
-  return CSSPrimitiveValue::Create(style.LineClamp(),
-                                   CSSPrimitiveValue::UnitType::kNumber);
+  return CSSNumericLiteralValue::Create(style.LineClamp(),
+                                        CSSPrimitiveValue::UnitType::kNumber);
 }
 
 const CSSValue* WebkitLocale::ParseSingleValue(
@@ -7931,8 +7935,8 @@
     const LayoutObject*,
     Node*,
     bool allow_visited_style) const {
-  return CSSPrimitiveValue::Create(style.Widows(),
-                                   CSSPrimitiveValue::UnitType::kNumber);
+  return CSSNumericLiteralValue::Create(style.Widows(),
+                                        CSSPrimitiveValue::UnitType::kNumber);
 }
 
 const CSSValue* Width::ParseSingleValue(CSSParserTokenRange& range,
@@ -8169,8 +8173,8 @@
     bool allow_visited_style) const {
   if (style.HasAutoZIndex() || !style.IsStackingContext())
     return CSSIdentifierValue::Create(CSSValueID::kAuto);
-  return CSSPrimitiveValue::Create(style.ZIndex(),
-                                   CSSPrimitiveValue::UnitType::kInteger);
+  return CSSNumericLiteralValue::Create(style.ZIndex(),
+                                        CSSPrimitiveValue::UnitType::kInteger);
 }
 
 const CSSValue* Zoom::ParseSingleValue(CSSParserTokenRange& range,
@@ -8206,8 +8210,8 @@
     const LayoutObject*,
     Node*,
     bool allow_visited_style) const {
-  return CSSPrimitiveValue::Create(style.Zoom(),
-                                   CSSPrimitiveValue::UnitType::kNumber);
+  return CSSNumericLiteralValue::Create(style.Zoom(),
+                                        CSSPrimitiveValue::UnitType::kNumber);
 }
 
 void Zoom::ApplyInitial(StyleResolverState& state) const {
diff --git a/third_party/blink/renderer/core/css/properties/shorthands/shorthands_custom.cc b/third_party/blink/renderer/core/css/properties/shorthands/shorthands_custom.cc
index eef5e2f..af3c16b 100644
--- a/third_party/blink/renderer/core/css/properties/shorthands/shorthands_custom.cc
+++ b/third_party/blink/renderer/core/css/properties/shorthands/shorthands_custom.cc
@@ -6,6 +6,7 @@
 #include "third_party/blink/renderer/core/css/css_font_family_value.h"
 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
 #include "third_party/blink/renderer/core/css/css_initial_value.h"
+#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value_mappings.h"
 #include "third_party/blink/renderer/core/css/css_property_value.h"
 #include "third_party/blink/renderer/core/css/css_value_pair.h"
@@ -110,13 +111,13 @@
     CSSValueList* animations_list = CSSValueList::CreateCommaSeparated();
     for (wtf_size_t i = 0; i < animation_data->NameList().size(); ++i) {
       CSSValueList* list = CSSValueList::CreateSpaceSeparated();
-      list->Append(*CSSPrimitiveValue::Create(
+      list->Append(*CSSNumericLiteralValue::Create(
           CSSTimingData::GetRepeated(animation_data->DurationList(), i),
           CSSPrimitiveValue::UnitType::kSeconds));
       list->Append(*ComputedStyleUtils::CreateTimingFunctionValue(
           CSSTimingData::GetRepeated(animation_data->TimingFunctionList(), i)
               .get()));
-      list->Append(*CSSPrimitiveValue::Create(
+      list->Append(*CSSNumericLiteralValue::Create(
           CSSTimingData::GetRepeated(animation_data->DelayList(), i),
           CSSPrimitiveValue::UnitType::kSeconds));
       list->Append(*ComputedStyleUtils::ValueForAnimationIterationCount(
@@ -138,15 +139,15 @@
   // animation-name default value.
   list->Append(*CSSIdentifierValue::Create(CSSValueID::kNone));
   list->Append(
-      *CSSPrimitiveValue::Create(CSSAnimationData::InitialDuration(),
-                                 CSSPrimitiveValue::UnitType::kSeconds));
+      *CSSNumericLiteralValue::Create(CSSAnimationData::InitialDuration(),
+                                      CSSPrimitiveValue::UnitType::kSeconds));
   list->Append(*ComputedStyleUtils::CreateTimingFunctionValue(
       CSSAnimationData::InitialTimingFunction().get()));
-  list->Append(*CSSPrimitiveValue::Create(
+  list->Append(*CSSNumericLiteralValue::Create(
       CSSAnimationData::InitialDelay(), CSSPrimitiveValue::UnitType::kSeconds));
   list->Append(
-      *CSSPrimitiveValue::Create(CSSAnimationData::InitialIterationCount(),
-                                 CSSPrimitiveValue::UnitType::kNumber));
+      *CSSNumericLiteralValue::Create(CSSAnimationData::InitialIterationCount(),
+                                      CSSPrimitiveValue::UnitType::kNumber));
   list->Append(*ComputedStyleUtils::ValueForAnimationDirection(
       CSSAnimationData::InitialDirection()));
   list->Append(*ComputedStyleUtils::ValueForAnimationFillMode(
@@ -957,7 +958,7 @@
           // flex only allows a basis of 0 (sans units) if
           // flex-grow and flex-shrink values have already been
           // set.
-          flex_basis = CSSPrimitiveValue::Create(
+          flex_basis = CSSNumericLiteralValue::Create(
               0, CSSPrimitiveValue::UnitType::kPixels);
         } else {
           return false;
@@ -980,7 +981,7 @@
     if (flex_shrink == kUnsetValue)
       flex_shrink = 1;
     if (!flex_basis) {
-      flex_basis = CSSPrimitiveValue::Create(
+      flex_basis = CSSNumericLiteralValue::Create(
           0, CSSPrimitiveValue::UnitType::kPercentage);
     }
   }
@@ -989,14 +990,14 @@
     return false;
   css_property_parser_helpers::AddProperty(
       CSSPropertyID::kFlexGrow, CSSPropertyID::kFlex,
-      *CSSPrimitiveValue::Create(clampTo<float>(flex_grow),
-                                 CSSPrimitiveValue::UnitType::kNumber),
+      *CSSNumericLiteralValue::Create(clampTo<float>(flex_grow),
+                                      CSSPrimitiveValue::UnitType::kNumber),
       important, css_property_parser_helpers::IsImplicitProperty::kNotImplicit,
       properties);
   css_property_parser_helpers::AddProperty(
       CSSPropertyID::kFlexShrink, CSSPropertyID::kFlex,
-      *CSSPrimitiveValue::Create(clampTo<float>(flex_shrink),
-                                 CSSPrimitiveValue::UnitType::kNumber),
+      *CSSNumericLiteralValue::Create(clampTo<float>(flex_shrink),
+                                      CSSPrimitiveValue::UnitType::kNumber),
       important, css_property_parser_helpers::IsImplicitProperty::kNotImplicit,
       properties);
 
@@ -1065,14 +1066,14 @@
       properties);
   css_property_parser_helpers::AddProperty(
       CSSPropertyID::kFontWeight, CSSPropertyID::kFont,
-      *CSSPrimitiveValue::Create(font_weight,
-                                 CSSPrimitiveValue::UnitType::kNumber),
+      *CSSNumericLiteralValue::Create(font_weight,
+                                      CSSPrimitiveValue::UnitType::kNumber),
       important, css_property_parser_helpers::IsImplicitProperty::kNotImplicit,
       properties);
   css_property_parser_helpers::AddProperty(
       CSSPropertyID::kFontSize, CSSPropertyID::kFont,
-      *CSSPrimitiveValue::Create(font_size,
-                                 CSSPrimitiveValue::UnitType::kPixels),
+      *CSSNumericLiteralValue::Create(font_size,
+                                      CSSPrimitiveValue::UnitType::kPixels),
       important, css_property_parser_helpers::IsImplicitProperty::kNotImplicit,
       properties);
 
@@ -2909,13 +2910,13 @@
       CSSValueList* list = CSSValueList::CreateSpaceSeparated();
       list->Append(*ComputedStyleUtils::CreateTransitionPropertyValue(
           transition_data->PropertyList()[i]));
-      list->Append(*CSSPrimitiveValue::Create(
+      list->Append(*CSSNumericLiteralValue::Create(
           CSSTimingData::GetRepeated(transition_data->DurationList(), i),
           CSSPrimitiveValue::UnitType::kSeconds));
       list->Append(*ComputedStyleUtils::CreateTimingFunctionValue(
           CSSTimingData::GetRepeated(transition_data->TimingFunctionList(), i)
               .get()));
-      list->Append(*CSSPrimitiveValue::Create(
+      list->Append(*CSSNumericLiteralValue::Create(
           CSSTimingData::GetRepeated(transition_data->DelayList(), i),
           CSSPrimitiveValue::UnitType::kSeconds));
       transitions_list->Append(*list);
@@ -2927,13 +2928,13 @@
   // transition-property default value.
   list->Append(*CSSIdentifierValue::Create(CSSValueID::kAll));
   list->Append(
-      *CSSPrimitiveValue::Create(CSSTransitionData::InitialDuration(),
-                                 CSSPrimitiveValue::UnitType::kSeconds));
+      *CSSNumericLiteralValue::Create(CSSTransitionData::InitialDuration(),
+                                      CSSPrimitiveValue::UnitType::kSeconds));
   list->Append(*ComputedStyleUtils::CreateTimingFunctionValue(
       CSSTransitionData::InitialTimingFunction().get()));
   list->Append(
-      *CSSPrimitiveValue::Create(CSSTransitionData::InitialDelay(),
-                                 CSSPrimitiveValue::UnitType::kSeconds));
+      *CSSNumericLiteralValue::Create(CSSTransitionData::InitialDelay(),
+                                      CSSPrimitiveValue::UnitType::kSeconds));
   return list;
 }
 
diff --git a/third_party/blink/renderer/core/css/resolver/css_variable_resolver_test.cc b/third_party/blink/renderer/core/css/resolver/css_variable_resolver_test.cc
index 5abadf99..c7d3d620 100644
--- a/third_party/blink/renderer/core/css/resolver/css_variable_resolver_test.cc
+++ b/third_party/blink/renderer/core/css/resolver/css_variable_resolver_test.cc
@@ -6,6 +6,7 @@
 #include "third_party/blink/renderer/core/css/css_custom_property_declaration.h"
 #include "third_party/blink/renderer/core/css/css_inherited_value.h"
 #include "third_party/blink/renderer/core/css/css_initial_value.h"
+#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_syntax_string_parser.h"
 #include "third_party/blink/renderer/core/css/css_unset_value.h"
 #include "third_party/blink/renderer/core/css/css_variable_reference_value.h"
@@ -108,7 +109,8 @@
   }
 
   const CSSValue* CreatePxValue(double px) {
-    return CSSPrimitiveValue::Create(px, CSSPrimitiveValue::UnitType::kPixels);
+    return CSSNumericLiteralValue::Create(px,
+                                          CSSPrimitiveValue::UnitType::kPixels);
   }
 
   size_t MaxSubstitutionTokens() const {
diff --git a/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc b/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc
index 01e53b7d..fcc8b5a 100644
--- a/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc
@@ -42,6 +42,7 @@
 #include "third_party/blink/renderer/core/css/css_font_variation_value.h"
 #include "third_party/blink/renderer/core/css/css_grid_auto_repeat_value.h"
 #include "third_party/blink/renderer/core/css/css_grid_integer_repeat_value.h"
+#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_path_value.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value_mappings.h"
 #include "third_party/blink/renderer/core/css/css_quad_value.h"
@@ -1465,8 +1466,8 @@
   auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
   if (identifier_value && IsValidCSSValueID(identifier_value->GetValueID())) {
     float multiplier = ConvertLineWidth<float>(state, value);
-    return CSSPrimitiveValue::Create(multiplier / 48,
-                                     CSSPrimitiveValue::UnitType::kEms)
+    return CSSNumericLiteralValue::Create(multiplier / 48,
+                                          CSSPrimitiveValue::UnitType::kEms)
         ->ComputeLength<float>(state.CssToLengthConversionData());
   }
   return To<CSSPrimitiveValue>(value).ComputeLength<float>(
@@ -1726,7 +1727,7 @@
       // Instead of the actual zoom, use 1 to avoid potential rounding errors
       Length length = primitive_value->ConvertToLength(
           css_to_length_conversion_data.CopyWithAdjustedZoom(1));
-      return *CSSPrimitiveValue::Create(length, 1);
+      return *CSSPrimitiveValue::CreateFromLength(length, 1);
     }
 
     // If we encounter a calculated number that was not resolved during
@@ -1739,21 +1740,24 @@
          CSSPrimitiveValue::UnitType::kNumber)) {
       double double_value = primitive_value->CssCalcValue()->DoubleValue();
       auto unit_type = CSSPrimitiveValue::UnitType::kInteger;
-      return *CSSPrimitiveValue::Create(std::round(double_value), unit_type);
+      return *CSSNumericLiteralValue::Create(std::round(double_value),
+                                             unit_type);
     }
 
     if (primitive_value->IsAngle()) {
-      return *CSSPrimitiveValue::Create(primitive_value->ComputeDegrees(),
-                                        CSSPrimitiveValue::UnitType::kDegrees);
+      return *CSSNumericLiteralValue::Create(
+          primitive_value->ComputeDegrees(),
+          CSSPrimitiveValue::UnitType::kDegrees);
     }
 
     if (primitive_value->IsTime()) {
-      return *CSSPrimitiveValue::Create(primitive_value->ComputeSeconds(),
-                                        CSSPrimitiveValue::UnitType::kSeconds);
+      return *CSSNumericLiteralValue::Create(
+          primitive_value->ComputeSeconds(),
+          CSSPrimitiveValue::UnitType::kSeconds);
     }
 
     if (primitive_value->IsResolution()) {
-      return *CSSPrimitiveValue::Create(
+      return *CSSNumericLiteralValue::Create(
           primitive_value->ComputeDotsPerPixel(),
           CSSPrimitiveValue::UnitType::kDotsPerPixel);
     }
diff --git a/third_party/blink/renderer/core/css/threaded/css_to_length_conversion_data_threaded_test.cc b/third_party/blink/renderer/core/css/threaded/css_to_length_conversion_data_threaded_test.cc
index afa544e..6f1fd9ad 100644
--- a/third_party/blink/renderer/core/css/threaded/css_to_length_conversion_data_threaded_test.cc
+++ b/third_party/blink/renderer/core/css/threaded/css_to_length_conversion_data_threaded_test.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/core/css/css_to_length_conversion_data.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/threaded/multi_threaded_test_util.h"
 #include "third_party/blink/renderer/platform/fonts/font.h"
 #include "third_party/blink/renderer/platform/fonts/font_description.h"
@@ -32,8 +33,8 @@
     CSSToLengthConversionData conversionData(nullptr, fontSizes, viewportSize,
                                              1);
 
-    CSSPrimitiveValue& value =
-        *CSSPrimitiveValue::Create(3.14, CSSPrimitiveValue::UnitType::kEms);
+    CSSPrimitiveValue& value = *CSSNumericLiteralValue::Create(
+        3.14, CSSPrimitiveValue::UnitType::kEms);
 
     Length length = value.ConvertToLength(conversionData);
     EXPECT_EQ(length.Value(), 50.24f);
@@ -49,8 +50,8 @@
     CSSToLengthConversionData conversionData(nullptr, fontSizes, viewportSize,
                                              1);
 
-    CSSPrimitiveValue& value =
-        *CSSPrimitiveValue::Create(44, CSSPrimitiveValue::UnitType::kPixels);
+    CSSPrimitiveValue& value = *CSSNumericLiteralValue::Create(
+        44, CSSPrimitiveValue::UnitType::kPixels);
 
     Length length = value.ConvertToLength(conversionData);
     EXPECT_EQ(length.Value(), 44);
@@ -66,7 +67,7 @@
     CSSToLengthConversionData conversionData(nullptr, fontSizes, viewportSize,
                                              1);
 
-    CSSPrimitiveValue& value = *CSSPrimitiveValue::Create(
+    CSSPrimitiveValue& value = *CSSNumericLiteralValue::Create(
         1, CSSPrimitiveValue::UnitType::kViewportWidth);
 
     Length length = value.ConvertToLength(conversionData);
@@ -84,7 +85,7 @@
                                              1);
 
     CSSPrimitiveValue& value =
-        *CSSPrimitiveValue::Create(1, CSSPrimitiveValue::UnitType::kRems);
+        *CSSNumericLiteralValue::Create(1, CSSPrimitiveValue::UnitType::kRems);
 
     Length length = value.ConvertToLength(conversionData);
     EXPECT_EQ(length.Value(), 16);
diff --git a/third_party/blink/renderer/core/css/zoom_adjusted_pixel_value.h b/third_party/blink/renderer/core/css/zoom_adjusted_pixel_value.h
index 05cb1f9..81bf92f6 100644
--- a/third_party/blink/renderer/core/css/zoom_adjusted_pixel_value.h
+++ b/third_party/blink/renderer/core/css/zoom_adjusted_pixel_value.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_CSS_ZOOM_ADJUSTED_PIXEL_VALUE_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_ZOOM_ADJUSTED_PIXEL_VALUE_H_
 
+#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
 #include "third_party/blink/renderer/core/layout/adjust_for_absolute_zoom.h"
 
@@ -14,7 +15,7 @@
 
 inline CSSPrimitiveValue* ZoomAdjustedPixelValue(double value,
                                                  const ComputedStyle& style) {
-  return CSSPrimitiveValue::Create(
+  return CSSNumericLiteralValue::Create(
       AdjustForAbsoluteZoom::AdjustFloat(value, style),
       CSSPrimitiveValue::UnitType::kPixels);
 }
diff --git a/third_party/blink/renderer/core/dom/BUILD.gn b/third_party/blink/renderer/core/dom/BUILD.gn
index 1ca2ba9..394115b 100644
--- a/third_party/blink/renderer/core/dom/BUILD.gn
+++ b/third_party/blink/renderer/core/dom/BUILD.gn
@@ -226,10 +226,6 @@
     "scripted_animation_controller.h",
     "scripted_idle_task_controller.cc",
     "scripted_idle_task_controller.h",
-    "scripted_task_queue.cc",
-    "scripted_task_queue.h",
-    "scripted_task_queue_controller.cc",
-    "scripted_task_queue_controller.h",
     "shadow_root.cc",
     "shadow_root.h",
     "shadow_root_v0.cc",
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 9a717518..97f7acb 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -599,22 +599,358 @@
       online_observer_handle_;
 };
 
+// A helper class that allows the security context be initialized in the
+// process of constructing the document.
+class Document::SecurityContextInit : public FeaturePolicyParserDelegate {
+  STACK_ALLOCATED();
+
+ public:
+  SecurityContextInit(const DocumentInit& initializer,
+                      DocumentClassFlags document_classes) {
+    // Content Security Policy can provide sandbox flags. In CSP
+    // 'self' will be determined when the policy is bound. That occurs
+    // once the document is constructed.
+    InitializeContentSecurityPolicy(initializer, document_classes);
+
+    // Sandbox flags can come from initializer, loader or CSP.
+    InitializeSandboxFlags(initializer);
+
+    // The origin can be opaque based on sandbox flags.
+    InitializeOrigin(initializer);
+
+    // Initialize origin trials, requires the post sandbox flags
+    // security origin.
+    InitializeOriginTrials(initializer);
+
+    // Initialize feature policy, depends on origin trials.
+    InitializeFeaturePolicy(initializer, document_classes);
+
+    // Initialize the agent. Depends on security origin.
+    InitializeAgent(initializer);
+  }
+
+  const scoped_refptr<SecurityOrigin>& GetSecurityOrigin() const {
+    return security_origin_;
+  }
+
+  WebSandboxFlags GetSandboxFlags() { return sandbox_flags_; }
+
+  ContentSecurityPolicy* GetCSP() const { return csp_; }
+
+  std::unique_ptr<FeaturePolicy> TakeFeaturePolicy() {
+    DCHECK(feature_policy_);
+    return std::move(feature_policy_);
+  }
+
+  const Vector<String>& FeaturePolicyParseMessages() const {
+    return feature_policy_parse_messages_;
+  }
+  const ParsedFeaturePolicy& ParsedHeader() const { return parsed_header_; }
+
+  OriginTrialContext* GetOriginTrialContext() { return origin_trials_; }
+
+  Agent* GetAgent() { return agent_; }
+
+  void CountFeaturePolicyUsage(mojom::WebFeature feature) override {
+    feature_count_.insert(feature);
+  }
+
+  bool FeaturePolicyFeatureObserved(
+      mojom::FeaturePolicyFeature feature) override {
+    if (parsed_feature_policies_.Contains(feature))
+      return true;
+    parsed_feature_policies_.insert(feature);
+    return false;
+  }
+
+  bool FeatureEnabled(OriginTrialFeature feature) const override {
+    return origin_trials_->IsFeatureEnabled(feature);
+  }
+
+  void ApplyPendingDataToDocument(Document& document) {
+    for (auto feature : feature_count_)
+      UseCounter::Count(document, feature);
+    for (auto feature : parsed_feature_policies_)
+      document.FeaturePolicyFeatureObserved(feature);
+  }
+
+  bool BindCSPImmediately() const { return bind_csp_immediately_; }
+
+ private:
+  void InitializeContentSecurityPolicy(const DocumentInit& initializer,
+                                       DocumentClassFlags document_classes) {
+    auto* frame = initializer.GetFrame();
+    ContentSecurityPolicy* last_origin_document_csp =
+        frame ? frame->Loader().GetLastOriginDocumentCSP() : nullptr;
+
+    KURL url;
+    if (initializer.ShouldSetURL()) {
+      url = initializer.Url();
+      if (url.IsEmpty())
+        url = BlankURL();
+    }
+
+    if (initializer.HasSecurityContext() && !initializer.OriginToCommit() &&
+        initializer.OwnerDocument()) {
+      // Alias certain security properties from |owner_document|. Used for
+      // the case of about:blank pages inheriting the security properties of
+      //   their requestor context.
+      // Note that this is currently somewhat broken; Blink always inherits
+      // from the parent or opener, even though it should actually be
+      // inherited from the request initiator.
+      if (url.IsEmpty()) {
+        last_origin_document_csp =
+            initializer.OwnerDocument()->GetContentSecurityPolicy();
+      }
+    }
+
+    csp_ = initializer.GetContentSecurityPolicy();
+
+    if (!csp_ && initializer.ImportsController()) {
+      // If this document is an HTML import, grab a reference to its master
+      // document's Content Security Policy. We don't bind the CSP's delegate
+      // in 'InitSecurityPolicy' in this case, as we can't rebind the
+      // master document's policy object: The Content Security Policy's delegate
+      // needs to remain set to the master document.
+      csp_ =
+          initializer.ImportsController()->Master()->GetContentSecurityPolicy();
+    } else {
+      if (!csp_) {
+        csp_ = MakeGarbageCollected<ContentSecurityPolicy>();
+        bind_csp_immediately_ = true;
+      }
+
+      // We should inherit the navigation initiator CSP if the document is
+      // loaded using a local-scheme url.
+      if (last_origin_document_csp &&
+          (url.IsEmpty() || url.ProtocolIsAbout() || url.ProtocolIsData() ||
+           url.ProtocolIs("blob") || url.ProtocolIs("filesystem"))) {
+        csp_->CopyStateFrom(last_origin_document_csp);
+      }
+
+      if (document_classes & kPluginDocumentClass) {
+        // TODO(andypaicu): This should inherit the origin document's plugin
+        // types but because this could be a OOPIF document it might not have
+        // access. In this situation we fallback on using the parent/opener.
+        if (last_origin_document_csp) {
+          csp_->CopyPluginTypesFrom(last_origin_document_csp);
+        } else if (frame) {
+          Frame* inherit_from = frame->Tree().Parent()
+                                    ? frame->Tree().Parent()
+                                    : frame->Client()->Opener();
+          if (inherit_from && frame != inherit_from) {
+            DCHECK(
+                inherit_from->GetSecurityContext() &&
+                inherit_from->GetSecurityContext()->GetContentSecurityPolicy());
+            csp_->CopyPluginTypesFrom(
+                inherit_from->GetSecurityContext()->GetContentSecurityPolicy());
+          }
+        }
+      }
+    }
+  }
+
+  void InitializeSandboxFlags(const DocumentInit& initializer) {
+    sandbox_flags_ = initializer.GetSandboxFlags() | csp_->GetSandboxMask();
+    auto* frame = initializer.GetFrame();
+    if (frame && frame->Loader().GetDocumentLoader()->Archive()) {
+      // The URL of a Document loaded from a MHTML archive is controlled by
+      // the Content-Location header. This would allow UXSS, since
+      // Content-Location can be arbitrarily controlled to control the
+      // Document's URL and origin. Instead, force a Document loaded from a
+      // MHTML archive to be sandboxed, providing exceptions only for creating
+      // new windows.
+      sandbox_flags_ |=
+          (WebSandboxFlags::kAll &
+           ~(WebSandboxFlags::kPopups |
+             WebSandboxFlags::kPropagatesToAuxiliaryBrowsingContexts));
+    }
+  }
+
+  void InitializeOrigin(const DocumentInit& initializer) {
+    scoped_refptr<SecurityOrigin> document_origin =
+        initializer.GetDocumentOrigin();
+    if ((sandbox_flags_ & WebSandboxFlags::kOrigin) != WebSandboxFlags::kNone) {
+      scoped_refptr<SecurityOrigin> sandboxed_origin =
+          initializer.OriginToCommit()
+              ? initializer.OriginToCommit()
+              : document_origin->DeriveNewOpaqueOrigin();
+
+      // If we're supposed to inherit our security origin from our
+      // owner, but we're also sandboxed, the only things we inherit are
+      // the origin's potential trustworthiness and the ability to
+      // load local resources. The latter lets about:blank iframes in
+      // file:// URL documents load images and other resources from
+      // the file system.
+      //
+      // Note: Sandboxed about:srcdoc iframe without "allow-same-origin" aren't
+      // allowed to load user's file, even if its parent can.
+      if (initializer.OwnerDocument()) {
+        if (document_origin->IsPotentiallyTrustworthy())
+          sandboxed_origin->SetOpaqueOriginIsPotentiallyTrustworthy(true);
+        if (document_origin->CanLoadLocalResources() &&
+            !initializer.IsSrcdocDocument())
+          sandboxed_origin->GrantLoadLocalResources();
+      }
+      security_origin_ = sandboxed_origin;
+    } else {
+      security_origin_ = document_origin;
+    }
+  }
+
+  void InitializeFeaturePolicy(const DocumentInit& initializer,
+                               DocumentClassFlags document_classes) {
+    auto* frame = initializer.GetFrame();
+    // For a main frame, get inherited feature policy from the opener if any.
+    const FeaturePolicy::FeatureState* opener_feature_state = nullptr;
+    if (frame && frame->IsMainFrame() && !frame->OpenerFeatureState().empty()) {
+      opener_feature_state = &frame->OpenerFeatureState();
+    }
+
+    parsed_header_ = FeaturePolicyParser::ParseHeader(
+        initializer.FeaturePolicyHeader(), security_origin_,
+        &feature_policy_parse_messages_, this);
+
+    if (sandbox_flags_ != WebSandboxFlags::kNone &&
+        RuntimeEnabledFeatures::FeaturePolicyForSandboxEnabled()) {
+      // The sandbox flags might have come from CSP header or the browser; in
+      // such cases the sandbox is not part of the container policy. They are
+      // added to the header policy (which specifically makes sense in the case
+      // of CSP sandbox).
+      ApplySandboxFlagsToParsedFeaturePolicy(sandbox_flags_, parsed_header_);
+    }
+
+    ParsedFeaturePolicy container_policy;
+    if (frame && frame->Owner())
+      container_policy = frame->Owner()->GetFramePolicy().container_policy;
+
+    // TODO(icelland): This is problematic querying sandbox flags before
+    // feature policy is initialized.
+    if (RuntimeEnabledFeatures::BlockingFocusWithoutUserActivationEnabled() &&
+        frame && frame->Tree().Parent() &&
+        (sandbox_flags_ & WebSandboxFlags::kNavigation) !=
+            WebSandboxFlags::kNone) {
+      // Enforcing the policy for sandbox frames (for context see
+      // https://crbug.com/954349).
+      DisallowFeatureIfNotPresent(
+          mojom::FeaturePolicyFeature::kFocusWithoutUserActivation,
+          container_policy);
+    }
+
+    const FeaturePolicy* parent_feature_policy = nullptr;
+    if (frame && !frame->IsMainFrame()) {
+      parent_feature_policy =
+          frame->Tree().Parent()->GetSecurityContext()->GetFeaturePolicy();
+    }
+
+    // If we are a HTMLViewSourceDocument we use container, header or
+    // inherited policies. https://crbug.com/898688
+    if (document_classes & kViewSourceDocumentClass) {
+      feature_policy_ = FeaturePolicy::CreateFromParentPolicy(
+          nullptr, {}, security_origin_->ToUrlOrigin());
+      return;
+    }
+
+    // Feature policy should either come from a parent in the case of an
+    // embedded child frame, or from an opener if any when a new window is
+    // created by an opener. A main frame without an opener would not have a
+    // parent policy nor an opener feature state.
+    DCHECK(!parent_feature_policy || !opener_feature_state);
+    if (!opener_feature_state ||
+        !RuntimeEnabledFeatures::FeaturePolicyForSandboxEnabled()) {
+      feature_policy_ = FeaturePolicy::CreateFromParentPolicy(
+          parent_feature_policy, container_policy,
+          security_origin_->ToUrlOrigin());
+    } else {
+      DCHECK(!parent_feature_policy);
+      feature_policy_ = FeaturePolicy::CreateWithOpenerPolicy(
+          *opener_feature_state, security_origin_->ToUrlOrigin());
+    }
+    feature_policy_->SetHeaderPolicy(parsed_header_);
+  }
+
+  void InitializeOriginTrials(const DocumentInit& initializer) {
+    origin_trials_ = MakeGarbageCollected<OriginTrialContext>();
+
+    const String& header_value = initializer.OriginTrialsHeader();
+
+    if (header_value.IsEmpty())
+      return;
+    std::unique_ptr<Vector<String>> tokens(
+        OriginTrialContext::ParseHeaderValue(header_value));
+    if (!tokens)
+      return;
+    origin_trials_->AddTokens(security_origin_.get(), true, *tokens);
+  }
+
+  void InitializeAgent(const DocumentInit& initializer) {
+    Document* context_document = initializer.ContextDocument();
+    Frame* frame = nullptr;
+    if (context_document) {
+      frame = context_document->GetFrame();
+    } else {
+      frame = initializer.GetFrame();
+    }
+    if (frame) {
+      bool has_potential_universal_access_privilege = false;
+      if (Settings* settings = frame->GetSettings()) {
+        // TODO(keishi): Also check if AllowUniversalAccessFromFileURLs might
+        // dynamically change.
+        if (!settings->GetWebSecurityEnabled() ||
+            settings->GetAllowUniversalAccessFromFileURLs())
+          has_potential_universal_access_privilege = true;
+      }
+      agent_ = frame->window_agent_factory().GetAgentForOrigin(
+          has_potential_universal_access_privilege,
+          V8PerIsolateData::MainThreadIsolate(), security_origin_.get());
+    } else {
+      // ContextDocument is null only for Documents created in unit tests.
+      // In that case, use a throw away WindowAgent.
+      agent_ = MakeGarbageCollected<WindowAgent>(
+          V8PerIsolateData::MainThreadIsolate());
+    }
+  }
+
+  scoped_refptr<SecurityOrigin> security_origin_;
+  WebSandboxFlags sandbox_flags_ = WebSandboxFlags::kNone;
+  std::unique_ptr<FeaturePolicy> feature_policy_;
+  Vector<String> feature_policy_parse_messages_;
+  ParsedFeaturePolicy parsed_header_;
+  Member<ContentSecurityPolicy> csp_;
+  Member<OriginTrialContext> origin_trials_;
+  Member<Agent> agent_;
+  HashSet<mojom::FeaturePolicyFeature> parsed_feature_policies_;
+  HashSet<mojom::WebFeature> feature_count_;
+  bool bind_csp_immediately_ = false;
+};
+
 Document* Document::Create(Document& document) {
-  Document* new_document = MakeGarbageCollected<Document>(
-      DocumentInit::Create().WithContextDocument(&document).WithURL(
-          BlankURL()));
-  new_document->SetSecurityOrigin(document.GetMutableSecurityOrigin());
+  Document* new_document =
+      MakeGarbageCollected<Document>(DocumentInit::Create()
+                                         .WithContextDocument(&document)
+                                         .WithURL(BlankURL())
+                                         .WithOwnerDocument(&document));
   new_document->SetContextFeatures(document.GetContextFeatures());
   return new_document;
 }
 
 Document::Document(const DocumentInit& initializer,
                    DocumentClassFlags document_classes)
+    : Document(initializer,
+               SecurityContextInit(initializer, document_classes),
+               document_classes) {}
+
+Document::Document(const DocumentInit& initializer,
+                   SecurityContextInit security_initializer,
+                   DocumentClassFlags document_classes)
     : ContainerNode(nullptr, kCreateDocument),
       TreeScope(*this),
+      SecurityContext(security_initializer.GetSecurityOrigin(),
+                      security_initializer.GetSandboxFlags(),
+                      security_initializer.TakeFeaturePolicy()),
       ExecutionContext(V8PerIsolateData::MainThreadIsolate(),
-                       nullptr,
-                       MakeGarbageCollected<OriginTrialContext>()),
+                       security_initializer.GetAgent(),
+                       security_initializer.GetOriginTrialContext()),
       evaluate_media_queries_on_style_recalc_(false),
       pending_sheet_layout_(kNoLayoutWithPendingSheets),
       frame_(initializer.GetFrame()),
@@ -707,6 +1043,7 @@
       isolated_world_csp_map_(
           MakeGarbageCollected<
               HeapHashMap<int, Member<ContentSecurityPolicy>>>()) {
+  security_initializer.ApplyPendingDataToDocument(*this);
   if (frame_) {
     DCHECK(frame_->GetPage());
     ProvideContextFeaturesToDocumentFrom(*this, *frame_->GetPage());
@@ -746,29 +1083,8 @@
     UpdateBaseURL();
   }
 
-  InitSecurityContext(initializer);
-  if (frame_)
-    frame_->Client()->DidSetFramePolicyHeaders(GetSandboxFlags(), {});
-
-  Document* context_document = ContextDocument();
-  if (context_document && context_document->GetFrame()) {
-    bool has_potential_universal_access_privilege = false;
-    if (Settings* settings = context_document->GetFrame()->GetSettings()) {
-      // TODO(keishi): Also check if AllowUniversalAccessFromFileURLs might
-      // dynamically change.
-      if (!settings->GetWebSecurityEnabled() ||
-          settings->GetAllowUniversalAccessFromFileURLs())
-        has_potential_universal_access_privilege = true;
-    }
-    SetAgent(
-        context_document->GetFrame()->window_agent_factory().GetAgentForOrigin(
-            has_potential_universal_access_privilege, GetIsolate(),
-            GetSecurityOrigin()));
-  } else {
-    // If the ContextDocument or its frame is not available, use a throw away
-    // WindowAgent as we do in GetScheduler.
-    SetAgent(MakeGarbageCollected<WindowAgent>(GetIsolate()));
-  }
+  InitSecurityContext(initializer, security_initializer);
+  FeaturePolicyInitialized(initializer, security_initializer);
 
   InitDNSPrefetch();
 
@@ -4549,9 +4865,11 @@
 }
 
 Document* Document::CloneDocumentWithoutChildren() const {
-  DocumentInit init = DocumentInit::Create()
-                          .WithContextDocument(ContextDocument())
-                          .WithURL(Url());
+  DocumentInit init =
+      DocumentInit::Create()
+          .WithContextDocument(ContextDocument())
+          .WithURL(Url())
+          .WithOriginToCommit(GetSecurityOrigin()->IsolatedCopy());
   if (IsXMLDocument()) {
     if (IsXHTMLDocument())
       return XMLDocument::CreateXHTML(
@@ -4565,7 +4883,6 @@
   SetCompatibilityMode(other.GetCompatibilityMode());
   SetEncodingData(other.encoding_data_);
   SetContextFeatures(other.GetContextFeatures());
-  SetSecurityOrigin(other.GetSecurityOrigin()->IsolatedCopy());
   SetMimeType(other.contentType());
 }
 
@@ -6291,32 +6608,33 @@
   });
 }
 
-void Document::ApplyFeaturePolicyFromHeader(
-    const String& feature_policy_header) {
-  if (!feature_policy_header.IsEmpty())
+void Document::FeaturePolicyInitialized(
+    const DocumentInit& document_initializer,
+    const SecurityContextInit& security_initializer) {
+  // Processing of the feature policy header is done before the SecurityContext
+  // is initialized. This method just records the usage.
+  if (!document_initializer.FeaturePolicyHeader().IsEmpty())
     UseCounter::Count(*this, WebFeature::kFeaturePolicyHeader);
-  Vector<String> messages;
-  auto declared_policy = FeaturePolicyParser::ParseHeader(
-      feature_policy_header, GetSecurityOrigin(), &messages, this);
-  for (auto& message : messages) {
+  for (const auto& message :
+       security_initializer.FeaturePolicyParseMessages()) {
     AddConsoleMessage(
         ConsoleMessage::Create(mojom::ConsoleMessageSource::kSecurity,
                                mojom::ConsoleMessageLevel::kError,
                                "Error with Feature-Policy header: " + message));
   }
-  if (GetSandboxFlags() != WebSandboxFlags::kNone &&
-      RuntimeEnabledFeatures::FeaturePolicyForSandboxEnabled()) {
-    // The sandbox flags might have come from CSP header or the browser; in such
-    // cases the sandbox is not part of the container policy. They are added
-    // to the header policy (which specifically makes sense in the case of CSP
-    // sandbox).
-    ApplySandboxFlagsToParsedFeaturePolicy(GetSandboxFlags(), declared_policy);
-  }
-  ApplyFeaturePolicy(declared_policy);
   if (frame_) {
-    frame_->Client()->DidSetFramePolicyHeaders(GetSandboxFlags(),
-                                               declared_policy);
+    pending_parsed_headers_ = security_initializer.ParsedHeader();
   }
+
+  // At this point, the document will not have been installed in the frame's
+  // LocalDOMWindow, so we cannot call frame_->IsFeatureEnabled. This calls
+  // SecurityContext::IsFeatureEnabled instead, which cannot report, but we
+  // don't need reporting here in any case.
+  is_vertical_scroll_enforced_ =
+      frame_ && !frame_->IsMainFrame() &&
+      RuntimeEnabledFeatures::ExperimentalProductivityFeaturesEnabled() &&
+      !GetFeaturePolicy()->IsFeatureEnabled(
+          mojom::FeaturePolicyFeature::kVerticalScroll);
 }
 
 const ParsedFeaturePolicy Document::GetOwnerContainerPolicy() const {
@@ -6335,36 +6653,12 @@
   return nullptr;
 }
 
-void Document::ApplyFeaturePolicy(const ParsedFeaturePolicy& declared_policy) {
-  // For a main frame, get inherited feature policy from the opener if any.
-  const FeaturePolicy::FeatureState* opener_feature_state = nullptr;
-  if (frame_ && frame_->IsMainFrame() &&
-      !frame_->OpenerFeatureState().empty()) {
-    opener_feature_state = &frame_->OpenerFeatureState();
+void Document::ApplyPendingFeaturePolicyHeaders() {
+  if (frame_) {
+    frame_->Client()->DidSetFramePolicyHeaders(GetSandboxFlags(),
+                                               pending_parsed_headers_);
   }
-
-  auto container_policy = GetOwnerContainerPolicy();
-  if (RuntimeEnabledFeatures::BlockingFocusWithoutUserActivationEnabled() &&
-      frame_ && frame_->Tree().Parent() &&
-      IsSandboxed(WebSandboxFlags::kNavigation)) {
-    // Enforcing the policy for sandbox frames (for context see
-    // https://crbug.com/954349).
-    DisallowFeatureIfNotPresent(
-        mojom::FeaturePolicyFeature::kFocusWithoutUserActivation,
-        container_policy);
-  }
-  InitializeFeaturePolicy(declared_policy, container_policy,
-                          GetParentFeaturePolicy(), opener_feature_state);
-
-  // At this point, the document will not have been installed in the frame's
-  // LocalDOMWindow, so we cannot call frame_->IsFeatureEnabled. This calls
-  // SecurityContext::IsFeatureEnabled instead, which cannot report, but we
-  // don't need reporting here in any case.
-  is_vertical_scroll_enforced_ =
-      frame_ && !frame_->IsMainFrame() &&
-      RuntimeEnabledFeatures::ExperimentalProductivityFeaturesEnabled() &&
-      !GetFeaturePolicy()->IsFeatureEnabled(
-          mojom::FeaturePolicyFeature::kVerticalScroll);
+  pending_parsed_headers_.clear();
 }
 
 void Document::ApplyReportOnlyFeaturePolicyFromHeader(
@@ -6444,90 +6738,45 @@
   return ukm_source_id_;
 }
 
-void Document::InitSecurityContext(const DocumentInit& initializer) {
-  DCHECK(!GetSecurityOrigin());
+void Document::InitContentSecurityPolicy(ContentSecurityPolicy* csp) {
+  SetContentSecurityPolicy(csp);
+  GetContentSecurityPolicy()->BindToDelegate(
+      GetContentSecurityPolicyDelegate());
+}
 
+void Document::InitSecurityContext(
+    const DocumentInit& initializer,
+    const SecurityContextInit& security_initializer) {
+  DCHECK(GetSecurityOrigin());
+
+  // If the CSP was provided by the DocumentLoader or is from ImportsController
+  // it doesn't need to be bound right now. ImportsController takes a reference
+  // to a master document's CSP which is already bound. Document construction
+  // occurs in the DocumentLoader occurs before the frame reference is bound so
+  // callbacks from binding the CSP delegate immediately would not get called
+  // if it was bound immediately. eg. Callbacks back to browser or console
+  // logging.
+  if (security_initializer.BindCSPImmediately()) {
+    InitContentSecurityPolicy(security_initializer.GetCSP());
+  } else {
+    SetContentSecurityPolicy(security_initializer.GetCSP());
+  }
   if (!initializer.HasSecurityContext()) {
     // No source for a security context.
     // This can occur via document.implementation.createDocument().
     cookie_url_ = KURL(g_empty_string);
-    SetSecurityOrigin(initializer.GetDocumentOrigin());
-    InitContentSecurityPolicy();
-    ApplyFeaturePolicy({});
     return;
   }
-
-  SandboxFlags sandbox_flags = initializer.GetSandboxFlags();
-  if (fetcher_->Archive()) {
-    // The URL of a Document loaded from a MHTML archive is controlled by the
-    // Content-Location header. This would allow UXSS, since Content-Location
-    // can be arbitrarily controlled to control the Document's URL and origin.
-    // Instead, force a Document loaded from a MHTML archive to be sandboxed,
-    // providing exceptions only for creating new windows.
-    sandbox_flags |=
-        (WebSandboxFlags::kAll &
-         ~(WebSandboxFlags::kPopups |
-           WebSandboxFlags::kPropagatesToAuxiliaryBrowsingContexts));
-  }
-  // In the common case, create the security context from the currently
-  // loading URL with a fresh content security policy.
-  EnforceSandboxFlags(sandbox_flags);
   SetInsecureRequestPolicy(initializer.GetInsecureRequestPolicy());
   if (initializer.InsecureNavigationsToUpgrade()) {
     for (auto to_upgrade : *initializer.InsecureNavigationsToUpgrade())
       AddInsecureNavigationUpgrade(to_upgrade);
   }
 
-  ContentSecurityPolicy* last_origin_document_csp_ =
-      frame_ ? frame_->Loader().GetLastOriginDocumentCSP() : nullptr;
-
-  scoped_refptr<SecurityOrigin> document_origin =
-      initializer.GetDocumentOrigin();
   cookie_url_ = url_;
 
   if (!initializer.OriginToCommit() && initializer.OwnerDocument()) {
-    // Alias certain security properties from |owner_document|. Used for
-    // the case of about:blank pages inheriting the security properties of
-    // their requestor context.
-    // Note that this is currently somewhat broken; Blink always inherits
-    // from the parent or opener, even though it should actually be
-    // inherited from the request initiator.
     cookie_url_ = initializer.OwnerDocument()->CookieURL();
-    if (url_.IsEmpty()) {
-      last_origin_document_csp_ =
-          initializer.OwnerDocument()->GetContentSecurityPolicy();
-    }
-  }
-
-  if (IsSandboxed(WebSandboxFlags::kOrigin)) {
-    DCHECK(!initializer.ContextDocument());
-    scoped_refptr<SecurityOrigin> sandboxed_origin =
-        initializer.OriginToCommit() ? initializer.OriginToCommit()
-                                     : document_origin->DeriveNewOpaqueOrigin();
-
-    // If we're supposed to inherit our security origin from our
-    // owner, but we're also sandboxed, the only things we inherit are
-    // the origin's potential trustworthiness and the ability to
-    // load local resources. The latter lets about:blank iframes in
-    // file:// URL documents load images and other resources from
-    // the file system.
-    //
-    // Note: Sandboxed about:srcdoc iframe without "allow-same-origin" aren't
-    // allowed to load user's file, even if its parent can.
-    if (initializer.OwnerDocument()) {
-      if (document_origin->IsPotentiallyTrustworthy())
-        sandboxed_origin->SetOpaqueOriginIsPotentiallyTrustworthy(true);
-      if (document_origin->CanLoadLocalResources() && !IsSrcdocDocument())
-        sandboxed_origin->GrantLoadLocalResources();
-      if (url_.IsEmpty()) {
-        last_origin_document_csp_ =
-            initializer.OwnerDocument()->GetContentSecurityPolicy();
-      }
-    }
-    cookie_url_ = url_;
-    SetSecurityOrigin(std::move(sandboxed_origin));
-  } else {
-    SetSecurityOrigin(std::move(document_origin));
   }
 
   // Set the address space before setting up CSP, as the latter may override
@@ -6547,18 +6796,6 @@
     SetAddressSpace(mojom::IPAddressSpace::kPublic);
   }
 
-  if (ImportsController()) {
-    // If this document is an HTML import, grab a reference to it's master
-    // document's Content Security Policy. We don't call
-    // 'initContentSecurityPolicy' in this case, as we can't rebind the master
-    // document's policy object: its ExecutionContext needs to remain tied to
-    // the master document.
-    SetContentSecurityPolicy(
-        ImportsController()->Master()->GetContentSecurityPolicy());
-  } else {
-    InitContentSecurityPolicy(nullptr, last_origin_document_csp_);
-  }
-
   if (Settings* settings = initializer.GetSettings()) {
     if (!settings->GetWebSecurityEnabled()) {
       // Web security is turned off. We should let this document access every
@@ -6582,19 +6819,30 @@
       SecurityOrigin::Create(url_)->IsPotentiallyTrustworthy())
     GetMutableSecurityOrigin()->SetOpaqueOriginIsPotentiallyTrustworthy(true);
 
-  ParsedFeaturePolicy declared_policy = {};
-  if (GetSandboxFlags() != WebSandboxFlags::kNone &&
-      RuntimeEnabledFeatures::FeaturePolicyForSandboxEnabled()) {
-    // If any sandbox flags are enforced above they should also be added as
-    // part of a declared policy to properly initialize the sandbox feature
-    // policies.
-    ApplySandboxFlagsToParsedFeaturePolicy(GetSandboxFlags(), declared_policy);
-  }
-  ApplyFeaturePolicy(declared_policy);
-
   InitSecureContextState();
 }
 
+void Document::SetSecurityOrigin(scoped_refptr<SecurityOrigin> origin) {
+  // Enforce that we don't change access, we might change the reference (via
+  // IsolatedCopy but we can't change the security policy).
+  CHECK(origin);
+  CHECK(GetSecurityOrigin()->CanAccess(origin.get()));
+  SecurityContext::SetSecurityOrigin(origin);
+}
+
+void Document::SetSecurityOriginForTesting(
+    scoped_refptr<SecurityOrigin> origin) {
+  SecurityContext::SetSecurityOrigin(origin);
+  if (frame_)
+    frame_->GetScriptController().UpdateSecurityOrigin(GetSecurityOrigin());
+}
+
+void Document::BindContentSecurityPolicy() {
+  DCHECK(!GetContentSecurityPolicy()->IsBound());
+  GetContentSecurityPolicy()->BindToDelegate(
+      GetContentSecurityPolicyDelegate());
+}
+
 void Document::InitSecureContextState() {
   DCHECK_EQ(secure_context_state_, SecureContextState::kUnknown);
   if (!GetSecurityOrigin()->IsPotentiallyTrustworthy()) {
@@ -6621,47 +6869,6 @@
   DCHECK_NE(secure_context_state_, SecureContextState::kUnknown);
 }
 
-// the first parameter specifies a policy to use as the document csp meaning
-// the document will take ownership of the policy
-// the second parameter specifies a policy to inherit meaning the document
-// will attempt to copy over the policy
-void Document::InitContentSecurityPolicy(
-    ContentSecurityPolicy* csp,
-    const ContentSecurityPolicy* last_origin_document_csp) {
-  SetContentSecurityPolicy(csp ? csp
-                               : MakeGarbageCollected<ContentSecurityPolicy>());
-
-  GetContentSecurityPolicy()->BindToDelegate(
-      GetContentSecurityPolicyDelegate());
-
-  // We should inherit the navigation initiator CSP if the document is loaded
-  // using a local-scheme url.
-  if (last_origin_document_csp &&
-      (url_.IsEmpty() || url_.ProtocolIsAbout() || url_.ProtocolIsData() ||
-       url_.ProtocolIs("blob") || url_.ProtocolIs("filesystem"))) {
-    GetContentSecurityPolicy()->CopyStateFrom(last_origin_document_csp);
-  }
-
-  if (IsPluginDocument()) {
-    // TODO(andypaicu): This should inherit the origin document's plugin types
-    // but because this could be a OOPIF document it might not have access.
-    // In this situation we fallback on using the parent/opener.
-    if (last_origin_document_csp) {
-      GetContentSecurityPolicy()->CopyPluginTypesFrom(last_origin_document_csp);
-    } else if (frame_) {
-      Frame* inherit_from = frame_->Tree().Parent()
-                                ? frame_->Tree().Parent()
-                                : frame_->Client()->Opener();
-      if (inherit_from && frame_ != inherit_from) {
-        DCHECK(inherit_from->GetSecurityContext() &&
-               inherit_from->GetSecurityContext()->GetContentSecurityPolicy());
-        GetContentSecurityPolicy()->CopyPluginTypesFrom(
-            inherit_from->GetSecurityContext()->GetContentSecurityPolicy());
-      }
-    }
-  }
-}
-
 bool Document::CanExecuteScripts(ReasonForCallingCanExecuteScripts reason) {
   DCHECK(GetFrame())
       << "you are querying canExecuteScripts on a non contextDocument.";
@@ -6736,24 +6943,6 @@
   return true;
 }
 
-void Document::EnforceSandboxFlags(SandboxFlags mask) {
-  scoped_refptr<const SecurityOrigin> stand_in_origin = GetSecurityOrigin();
-  bool is_potentially_trustworthy =
-      stand_in_origin && stand_in_origin->IsPotentiallyTrustworthy();
-  ApplySandboxFlags(mask, is_potentially_trustworthy);
-}
-
-void Document::UpdateSecurityOrigin(scoped_refptr<SecurityOrigin> origin) {
-  SetSecurityOrigin(std::move(origin));
-  DidUpdateSecurityOrigin();
-}
-
-void Document::DidUpdateSecurityOrigin() {
-  if (!frame_)
-    return;
-  frame_->GetScriptController().UpdateSecurityOrigin(GetSecurityOrigin());
-}
-
 bool Document::IsContextThread() const {
   return IsMainThread();
 }
@@ -8188,7 +8377,6 @@
 template class CORE_TEMPLATE_EXPORT Supplement<Document>;
 
 }  // namespace blink
-
 #ifndef NDEBUG
 static WeakDocumentSet& liveDocumentSet() {
   DEFINE_STATIC_LOCAL(blink::Persistent<WeakDocumentSet>, set,
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index b04fc0e..8e9eada 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -225,6 +225,7 @@
   kMediaDocumentClass = 1 << 4,
   kSVGDocumentClass = 1 << 5,
   kXMLDocumentClass = 1 << 6,
+  kViewSourceDocumentClass = 1 << 7,
 };
 
 enum ShadowCascadeOrder {
@@ -564,6 +565,8 @@
   void Initialize();
   virtual void Shutdown();
 
+  void InitContentSecurityPolicy(ContentSecurityPolicy*);
+
   // If you have a Document, use GetLayoutView() instead which is faster.
   void GetLayoutObject() const = delete;
 
@@ -1097,20 +1100,11 @@
   const SVGDocumentExtensions* SvgExtensions();
   SVGDocumentExtensions& AccessSVGExtensions();
 
-  // the first parameter specifies a policy to use as the document csp meaning
-  // the document will take ownership of the policy
-  // the second parameter specifies a policy to inherit meaning the document
-  // will attempt to copy over the policy
-  void InitContentSecurityPolicy(ContentSecurityPolicy* = nullptr,
-                                 const ContentSecurityPolicy* = nullptr);
-
   bool AllowInlineEventHandler(Node*,
                                EventListener*,
                                const String& context_url,
                                const WTF::OrdinalNumber& context_line);
 
-  void EnforceSandboxFlags(WebSandboxFlags mask) override;
-
   void StatePopped(scoped_refptr<SerializedScriptValue>);
 
   enum LoadEventProgress {
@@ -1288,8 +1282,6 @@
   void MaybeHandleHttpRefresh(const String&, HttpRefreshType);
   bool IsHttpRefreshScheduledWithin(double interval_in_seconds);
 
-  void UpdateSecurityOrigin(scoped_refptr<SecurityOrigin>);
-
   void SetHasViewportUnits() { has_viewport_units_ = true; }
   bool HasViewportUnits() const { return has_viewport_units_; }
   void SetResizedForViewportUnits();
@@ -1393,10 +1385,8 @@
   // May return nullptr when PerformanceManager instrumentation is disabled.
   DocumentResourceCoordinator* GetResourceCoordinator();
 
-  // Set an explicit feature policy on this document in response to an HTTP
-  // Feature-Policy header. This will be relayed to the embedder through the
-  // LocalFrameClient.
-  void ApplyFeaturePolicyFromHeader(const String& feature_policy_header);
+  // Apply pending feature policy headers.
+  void ApplyPendingFeaturePolicyHeaders();
 
   // Set the report-only feature policy on this document in response to an HTTP
   // Feature-Policy-Report-Only header.
@@ -1570,10 +1560,20 @@
   bool IsUseCounted(CSSPropertyID property,
                     UseCounterHelper::CSSPropertyType) const;
   void ClearUseCounterForTesting(mojom::WebFeature);
+  void SetSecurityOrigin(scoped_refptr<SecurityOrigin>) final;
+
+  // This method should be used sparingly because it does not adjust the
+  // window agent or agent cluster. If you are using this in a unit test
+  // you should likely navigate the document to adjust the security origin
+  // instead.
+  void SetSecurityOriginForTesting(scoped_refptr<SecurityOrigin>);
+
+  // Bind Content Security Policy to this document. This will cause the
+  // CSP to resolve the 'self' attribute and all policies will then be
+  // applied to this document.
+  void BindContentSecurityPolicy();
 
  protected:
-  void DidUpdateSecurityOrigin() final;
-
   void ClearXMLVersion() { xml_version_ = String(); }
 
   virtual Document* CloneDocumentWithoutChildren() const;
@@ -1591,6 +1591,16 @@
   FRIEND_TEST_ALL_PREFIXES(FrameFetchContextSubresourceFilterTest,
                            DuringOnFreeze);
   class NetworkStateObserver;
+  class SecurityContextInit;
+
+  Document(const DocumentInit& initization,
+           SecurityContextInit init_helper,
+           DocumentClassFlags document_classes);
+
+  // Post initialization of the object handling of the feature policy.
+  void FeaturePolicyInitialized(
+      const DocumentInit& document_initializer,
+      const SecurityContextInit& security_initializer);
 
   friend class AXContext;
   void AddAXContext(AXContext*);
@@ -1605,7 +1615,8 @@
 
   ScriptedAnimationController& EnsureScriptedAnimationController();
   ScriptedIdleTaskController& EnsureScriptedIdleTaskController();
-  void InitSecurityContext(const DocumentInit&);
+  void InitSecurityContext(const DocumentInit&,
+                           const SecurityContextInit& security_initializer);
   void InitSecureContextState();
   SecurityContext& GetSecurityContext() final { return *this; }
   const SecurityContext& GetSecurityContext() const final { return *this; }
@@ -1702,12 +1713,6 @@
   const ParsedFeaturePolicy GetOwnerContainerPolicy() const;
   const FeaturePolicy* GetParentFeaturePolicy() const;
 
-  // Set the feature policy on this document, inheriting as necessary from the
-  // parent document and frame owner (if they exist). The caller must ensure
-  // that any changes to the declared policy are relayed to the embedder through
-  // the LocalFrameClient.
-  void ApplyFeaturePolicy(const ParsedFeaturePolicy& declared_policy);
-
   // Returns true if use of |method_name| for markup insertion is allowed by
   // feature policy; otherwise returns false and throws a DOM exception.
   bool AllowedToUseDynamicMarkUpInsertion(const char* method_name,
@@ -2039,6 +2044,10 @@
       static_cast<size_t>(mojom::FeaturePolicyFeature::kMaxValue) + 1>
       potentially_violated_features_;
 
+  // Pending parsed headers to send to browser after DidCommitNavigation
+  // IPC.
+  ParsedFeaturePolicy pending_parsed_headers_;
+
   AtomicString override_last_modified_;
 
   // Map from isolated world IDs to their ContentSecurityPolicy instances.
diff --git a/third_party/blink/renderer/core/dom/document_init.cc b/third_party/blink/renderer/core/dom/document_init.cc
index 2971f96..b05c7171 100644
--- a/third_party/blink/renderer/core/dom/document_init.cc
+++ b/third_party/blink/renderer/core/dom/document_init.cc
@@ -94,17 +94,19 @@
 }
 
 WebSandboxFlags DocumentInit::GetSandboxFlags() const {
-  DCHECK(MasterDocumentLoader());
   DocumentLoader* loader = MasterDocumentLoader();
-  WebSandboxFlags flags = loader->GetFrame()->Loader().EffectiveSandboxFlags();
+  WebSandboxFlags flags = sandbox_flags_;
+  if (loader) {
+    flags |= loader->GetFrame()->Loader().EffectiveSandboxFlags();
 
-  // If the load was blocked by CSP, force the Document's origin to be unique,
-  // so that the blocked document appears to be a normal cross-origin document's
-  // load per CSP spec: https://www.w3.org/TR/CSP3/#directive-frame-ancestors.
-  if (loader->WasBlockedAfterCSP()) {
-    flags |= WebSandboxFlags::kOrigin;
+    // If the load was blocked by CSP, force the Document's origin to be unique,
+    // so that the blocked document appears to be a normal cross-origin
+    // document's load per CSP spec:
+    // https://www.w3.org/TR/CSP3/#directive-frame-ancestors.
+    if (loader->WasBlockedAfterCSP()) {
+      flags |= WebSandboxFlags::kOrigin;
+    }
   }
-
   return flags;
 }
 
@@ -242,4 +244,28 @@
   return context_document_;
 }
 
+DocumentInit& DocumentInit::WithFeaturePolicyHeader(const String& header) {
+  DCHECK(feature_policy_header_.IsEmpty());
+  feature_policy_header_ = header;
+  return *this;
+}
+
+DocumentInit& DocumentInit::WithOriginTrialsHeader(const String& header) {
+  DCHECK(origin_trials_header_.IsEmpty());
+  origin_trials_header_ = header;
+  return *this;
+}
+
+DocumentInit& DocumentInit::WithSandboxFlags(WebSandboxFlags flags) {
+  // Only allow adding more sandbox flags.
+  sandbox_flags_ |= flags;
+  return *this;
+}
+
+DocumentInit& DocumentInit::WithContentSecurityPolicy(
+    ContentSecurityPolicy* policy) {
+  content_security_policy_ = policy;
+  return *this;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/dom/document_init.h b/third_party/blink/renderer/core/dom/document_init.h
index fef80d0..cd5ed82 100644
--- a/third_party/blink/renderer/core/dom/document_init.h
+++ b/third_party/blink/renderer/core/dom/document_init.h
@@ -40,6 +40,7 @@
 
 namespace blink {
 
+class ContentSecurityPolicy;
 class Document;
 class DocumentLoader;
 class LocalFrame;
@@ -118,6 +119,19 @@
   V0CustomElementRegistrationContext* RegistrationContext(Document*) const;
   DocumentInit& WithNewRegistrationContext();
 
+  DocumentInit& WithFeaturePolicyHeader(const String& header);
+  const String& FeaturePolicyHeader() const { return feature_policy_header_; }
+
+  DocumentInit& WithOriginTrialsHeader(const String& header);
+  const String& OriginTrialsHeader() const { return origin_trials_header_; }
+
+  DocumentInit& WithSandboxFlags(WebSandboxFlags flags);
+
+  DocumentInit& WithContentSecurityPolicy(ContentSecurityPolicy* policy);
+  ContentSecurityPolicy* GetContentSecurityPolicy() const {
+    return content_security_policy_;
+  }
+
  private:
   DocumentInit(HTMLImportsController*);
 
@@ -162,6 +176,18 @@
 
   Member<V0CustomElementRegistrationContext> registration_context_;
   bool create_new_registration_context_;
+
+  // The feature policy set via response header.
+  String feature_policy_header_;
+
+  // The origin trial set via response header.
+  String origin_trials_header_;
+
+  // Additional sandbox flags
+  WebSandboxFlags sandbox_flags_ = WebSandboxFlags::kNone;
+
+  // Loader's CSP
+  Member<ContentSecurityPolicy> content_security_policy_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/dom/dom_implementation.cc b/third_party/blink/renderer/core/dom/dom_implementation.cc
index d2ef3ca..d5fe1fe 100644
--- a/third_party/blink/renderer/core/dom/dom_implementation.cc
+++ b/third_party/blink/renderer/core/dom/dom_implementation.cc
@@ -83,8 +83,9 @@
     DocumentType* doctype,
     ExceptionState& exception_state) {
   XMLDocument* doc = nullptr;
-  DocumentInit init =
-      DocumentInit::Create().WithContextDocument(document_->ContextDocument());
+  DocumentInit init = DocumentInit::Create()
+                          .WithContextDocument(document_->ContextDocument())
+                          .WithOwnerDocument(document_->ContextDocument());
   if (namespace_uri == svg_names::kNamespaceURI) {
     doc = XMLDocument::CreateSVG(init);
   } else if (namespace_uri == html_names::xhtmlNamespaceURI) {
@@ -94,7 +95,6 @@
     doc = MakeGarbageCollected<XMLDocument>(init);
   }
 
-  doc->SetSecurityOrigin(document_->GetMutableSecurityOrigin());
   doc->SetContextFeatures(document_->GetContextFeatures());
 
   Node* document_element = nullptr;
@@ -206,6 +206,7 @@
   DocumentInit init =
       DocumentInit::Create()
           .WithContextDocument(document_->ContextDocument())
+          .WithOwnerDocument(document_->ContextDocument())
           .WithRegistrationContext(document_->RegistrationContext());
   auto* d = MakeGarbageCollected<HTMLDocument>(init);
   d->open();
@@ -217,7 +218,6 @@
     head_element->AppendChild(title_element);
     title_element->AppendChild(d->createTextNode(title), ASSERT_NO_EXCEPTION);
   }
-  d->SetSecurityOrigin(document_->GetMutableSecurityOrigin());
   d->SetContextFeatures(document_->GetContextFeatures());
   return d;
 }
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index 9ee037b1..ab9b8c6 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -44,6 +44,7 @@
 #include "third_party/blink/renderer/core/animation/css/css_animations.h"
 #include "third_party/blink/renderer/core/aom/computed_accessible_node.h"
 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
+#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
 #include "third_party/blink/renderer/core/css/css_property_value_set.h"
 #include "third_party/blink/renderer/core/css/css_selector_watch.h"
@@ -4555,7 +4556,8 @@
 }
 
 const ComputedStyle* Element::CachedStyleForPseudoElement(
-    const PseudoStyleRequest& request) {
+    const PseudoStyleRequest& request,
+    const ComputedStyle* parent_style) {
   const ComputedStyle* style = GetComputedStyle();
 
   if (!style || (request.pseudo_id < kFirstInternalPseudoId &&
@@ -4567,7 +4569,8 @@
           style->GetCachedPseudoStyle(request.pseudo_id))
     return cached;
 
-  scoped_refptr<ComputedStyle> result = StyleForPseudoElement(request, style);
+  scoped_refptr<ComputedStyle> result =
+      StyleForPseudoElement(request, parent_style);
   if (result)
     return style->AddCachedPseudoStyle(std::move(result));
   return nullptr;
@@ -5405,8 +5408,8 @@
                                      double value,
                                      CSSPrimitiveValue::UnitType unit,
                                      bool important) {
-  SetInlineStyleProperty(property_id, *CSSPrimitiveValue::Create(value, unit),
-                         important);
+  SetInlineStyleProperty(
+      property_id, *CSSNumericLiteralValue::Create(value, unit), important);
 }
 
 void Element::SetInlineStyleProperty(CSSPropertyID property_id,
@@ -5483,7 +5486,7 @@
     double value,
     CSSPrimitiveValue::UnitType unit) {
   DCHECK(IsStyledElement());
-  style->SetProperty(property_id, *CSSPrimitiveValue::Create(value, unit));
+  style->SetProperty(property_id, *CSSNumericLiteralValue::Create(value, unit));
 }
 
 void Element::AddPropertyToPresentationAttributeStyle(
diff --git a/third_party/blink/renderer/core/dom/element.h b/third_party/blink/renderer/core/dom/element.h
index accc434..fd76212 100644
--- a/third_party/blink/renderer/core/dom/element.h
+++ b/third_party/blink/renderer/core/dom/element.h
@@ -731,7 +731,9 @@
   PseudoElement* GetPseudoElement(PseudoId) const;
   LayoutObject* PseudoElementLayoutObject(PseudoId) const;
 
-  const ComputedStyle* CachedStyleForPseudoElement(const PseudoStyleRequest&);
+  const ComputedStyle* CachedStyleForPseudoElement(
+      const PseudoStyleRequest&,
+      const ComputedStyle* parent_style = nullptr);
   scoped_refptr<ComputedStyle> StyleForPseudoElement(
       const PseudoStyleRequest&,
       const ComputedStyle* parent_style = nullptr);
diff --git a/third_party/blink/renderer/core/dom/scripted_task_queue.cc b/third_party/blink/renderer/core/dom/scripted_task_queue.cc
deleted file mode 100644
index f4cfc3c..0000000
--- a/third_party/blink/renderer/core/dom/scripted_task_queue.cc
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "third_party/blink/renderer/core/dom/scripted_task_queue.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/macros.h"
-#include "third_party/blink/public/platform/platform.h"
-#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
-#include "third_party/blink/renderer/bindings/core/v8/v8_task_queue_post_callback.h"
-#include "third_party/blink/renderer/core/dom/abort_signal.h"
-#include "third_party/blink/renderer/platform/scheduler/public/post_cancellable_task.h"
-#include "third_party/blink/renderer/platform/wtf/functional.h"
-
-namespace blink {
-
-class ScriptedTaskQueue::WrappedCallback
-    : public GarbageCollectedFinalized<WrappedCallback> {
- public:
-  WrappedCallback(V8TaskQueuePostCallback* callback,
-                  ScriptPromiseResolver* resolver,
-                  TaskHandle task_handle)
-      : callback_(callback),
-        resolver_(resolver),
-        task_handle_(std::move(task_handle)) {}
-
-  void Trace(Visitor* visitor) {
-    visitor->Trace(callback_);
-    visitor->Trace(resolver_);
-  }
-
-  void Invoke() {
-    callback_->InvokeAndReportException(nullptr);
-    resolver_->Resolve();
-  }
-
-  void Reject() { resolver_->Reject(); }
-
- private:
-  Member<V8TaskQueuePostCallback> callback_;
-  Member<ScriptPromiseResolver> resolver_;
-  TaskHandle task_handle_;
-
-  DISALLOW_COPY_AND_ASSIGN(WrappedCallback);
-};
-
-ScriptedTaskQueue::ScriptedTaskQueue(ExecutionContext* context,
-                                     TaskType task_type)
-    : ContextLifecycleObserver(context) {
-  task_runner_ = GetExecutionContext()->GetTaskRunner(task_type);
-}
-
-void ScriptedTaskQueue::Trace(Visitor* visitor) {
-  visitor->Trace(pending_tasks_);
-  ScriptWrappable::Trace(visitor);
-  ContextLifecycleObserver::Trace(visitor);
-}
-
-ScriptPromise ScriptedTaskQueue::postTask(ScriptState* script_state,
-                                          V8TaskQueuePostCallback* callback,
-                                          AbortSignal* signal) {
-  CallbackId id = next_callback_id_++;
-
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
-
-  if (signal) {
-    if (signal->aborted()) {
-      resolver->Reject();
-      return resolver->Promise();
-    }
-
-    signal->AddAlgorithm(
-        WTF::Bind(&ScriptedTaskQueue::AbortTask, WrapPersistent(this), id));
-  }
-
-  TaskHandle task_handle = PostCancellableTask(
-      *task_runner_, FROM_HERE,
-      WTF::Bind(&ScriptedTaskQueue::CallbackFired, WrapPersistent(this), id));
-
-  pending_tasks_.Set(id, MakeGarbageCollected<WrappedCallback>(
-                             callback, resolver, std::move(task_handle)));
-
-  return resolver->Promise();
-}
-
-void ScriptedTaskQueue::CallbackFired(CallbackId id) {
-  auto task_iter = pending_tasks_.find(id);
-  if (task_iter == pending_tasks_.end())
-    return;
-
-  task_iter->value->Invoke();
-  // Can't use the iterator here since running the task
-  // might invalidate it.
-  pending_tasks_.erase(id);
-}
-
-void ScriptedTaskQueue::AbortTask(CallbackId id) {
-  auto task_iter = pending_tasks_.find(id);
-  if (task_iter == pending_tasks_.end())
-    return;
-
-  task_iter->value->Reject();
-  pending_tasks_.erase(id);
-}
-
-void ScriptedTaskQueue::ContextDestroyed(ExecutionContext*) {
-  pending_tasks_.clear();
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/core/dom/scripted_task_queue.h b/third_party/blink/renderer/core/dom/scripted_task_queue.h
deleted file mode 100644
index 98bebb59..0000000
--- a/third_party/blink/renderer/core/dom/scripted_task_queue.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_DOM_SCRIPTED_TASK_QUEUE_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_DOM_SCRIPTED_TASK_QUEUE_H_
-
-#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
-#include "third_party/blink/renderer/core/execution_context/context_lifecycle_observer.h"
-#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
-#include "third_party/blink/renderer/platform/heap/handle.h"
-
-namespace blink {
-
-class AbortSignal;
-class ScriptState;
-class V8TaskQueuePostCallback;
-
-// This class corresponds to the ScriptedTaskQueue interface.
-class CORE_EXPORT ScriptedTaskQueue final : public ScriptWrappable,
-                                            public ContextLifecycleObserver {
-  DEFINE_WRAPPERTYPEINFO();
-  USING_GARBAGE_COLLECTED_MIXIN(ScriptedTaskQueue);
-
- public:
-  static ScriptedTaskQueue* Create(ExecutionContext* context,
-                                   TaskType task_type) {
-    return MakeGarbageCollected<ScriptedTaskQueue>(context, task_type);
-  }
-
-  explicit ScriptedTaskQueue(ExecutionContext*, TaskType);
-
-  using CallbackId = int;
-
-  ScriptPromise postTask(ScriptState*,
-                         V8TaskQueuePostCallback* callback,
-                         AbortSignal*);
-
-  void CallbackFired(CallbackId id);
-
-  void Trace(Visitor*) override;
-
- private:
-  // ContextLifecycleObserver interface.
-  void ContextDestroyed(ExecutionContext*) override;
-
-  void AbortTask(CallbackId id);
-
-  class WrappedCallback;
-  HeapHashMap<CallbackId, Member<WrappedCallback>> pending_tasks_;
-  CallbackId next_callback_id_ = 1;
-  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_DOM_SCRIPTED_TASK_QUEUE_H_
diff --git a/third_party/blink/renderer/core/dom/scripted_task_queue.idl b/third_party/blink/renderer/core/dom/scripted_task_queue.idl
deleted file mode 100644
index df4c298..0000000
--- a/third_party/blink/renderer/core/dom/scripted_task_queue.idl
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-callback TaskQueuePostCallback = void ();
-
-[
-    RuntimeEnabled=ScriptedTaskQueue,
-    Exposed=Window
-] interface ScriptedTaskQueue {
-    [CallWith=ScriptState] Promise<any> postTask(TaskQueuePostCallback callback, optional AbortSignal signal = null);
-};
diff --git a/third_party/blink/renderer/core/dom/scripted_task_queue_controller.cc b/third_party/blink/renderer/core/dom/scripted_task_queue_controller.cc
deleted file mode 100644
index fa6c7ad..0000000
--- a/third_party/blink/renderer/core/dom/scripted_task_queue_controller.cc
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "third_party/blink/renderer/core/dom/scripted_task_queue_controller.h"
-
-#include "third_party/blink/renderer/core/dom/document.h"
-#include "third_party/blink/renderer/core/dom/scripted_task_queue.h"
-#include "third_party/blink/renderer/platform/bindings/exception_state.h"
-#include "third_party/blink/renderer/platform/bindings/script_state.h"
-
-namespace blink {
-
-const char ScriptedTaskQueueController::kSupplementName[] =
-    "ScriptedTaskQueueController";
-
-ScriptedTaskQueueController* ScriptedTaskQueueController::From(
-    Document& document) {
-  ScriptedTaskQueueController* task_queue_controller =
-      Supplement<Document>::From<ScriptedTaskQueueController>(document);
-  if (!task_queue_controller) {
-    task_queue_controller =
-        MakeGarbageCollected<ScriptedTaskQueueController>(&document);
-    Supplement<Document>::ProvideTo(document, task_queue_controller);
-  }
-  return task_queue_controller;
-}
-
-ScriptedTaskQueueController::ScriptedTaskQueueController(
-    ExecutionContext* context)
-    : ContextLifecycleObserver(context) {}
-
-void ScriptedTaskQueueController::Trace(Visitor* visitor) {
-  visitor->Trace(task_queues_);
-  Supplement<Document>::Trace(visitor);
-  ScriptWrappable::Trace(visitor);
-  ContextLifecycleObserver::Trace(visitor);
-}
-
-ScriptedTaskQueue* ScriptedTaskQueueController::defaultQueue(
-    const String& queue_name) {
-  auto iter = task_queues_.find(queue_name);
-  if (iter != task_queues_.end()) {
-    return iter->value.Get();
-  }
-
-  TaskType task_type = TaskType::kExperimentalWebSchedulingBestEffort;
-  if (queue_name == "user-interaction")
-    task_type = TaskType::kExperimentalWebSchedulingUserInteraction;
-  else if (queue_name != "best-effort")
-    NOTREACHED();
-
-  auto* task_queue =
-      ScriptedTaskQueue::Create(GetExecutionContext(), task_type);
-  task_queues_.insert(queue_name, task_queue);
-
-  return task_queue;
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/core/dom/scripted_task_queue_controller.h b/third_party/blink/renderer/core/dom/scripted_task_queue_controller.h
deleted file mode 100644
index 07446c9..0000000
--- a/third_party/blink/renderer/core/dom/scripted_task_queue_controller.h
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_DOM_SCRIPTED_TASK_QUEUE_CONTROLLER_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_DOM_SCRIPTED_TASK_QUEUE_CONTROLLER_H_
-
-#include "third_party/blink/renderer/core/dom/document.h"
-#include "third_party/blink/renderer/core/execution_context/context_lifecycle_observer.h"
-#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
-#include "third_party/blink/renderer/platform/heap/handle.h"
-#include "third_party/blink/renderer/platform/supplementable.h"
-#include "third_party/blink/renderer/platform/wtf/hash_map.h"
-#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
-
-namespace blink {
-
-class Document;
-
-class ScriptedTaskQueue;
-
-// This class corresponds to the ScriptedTaskQueueController interface.
-class CORE_EXPORT ScriptedTaskQueueController final
-    : public ScriptWrappable,
-      public ContextLifecycleObserver,
-      public Supplement<Document> {
-  DEFINE_WRAPPERTYPEINFO();
-  USING_GARBAGE_COLLECTED_MIXIN(ScriptedTaskQueueController);
-
- public:
-  static const char kSupplementName[];
-
-  static ScriptedTaskQueueController* From(Document&);
-
-  ScriptedTaskQueue* defaultQueue(const String&);
-
-  explicit ScriptedTaskQueueController(ExecutionContext*);
-
-  void Trace(Visitor*) override;
-
- private:
-  HeapHashMap<String, Member<ScriptedTaskQueue>> task_queues_;
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_DOM_SCRIPTED_TASK_QUEUE_CONTROLLER_H_
diff --git a/third_party/blink/renderer/core/dom/scripted_task_queue_controller.idl b/third_party/blink/renderer/core/dom/scripted_task_queue_controller.idl
deleted file mode 100644
index bda09c2..0000000
--- a/third_party/blink/renderer/core/dom/scripted_task_queue_controller.idl
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-enum MainThreadTaskQueueType { "user-interaction", "best-effort" };
-
-[
-    RuntimeEnabled=ScriptedTaskQueue,
-    Exposed=Window
-] interface ScriptedTaskQueueController {
-    [ImplementedAs=defaultQueue] ScriptedTaskQueue default(MainThreadTaskQueueType queue_type);
-};
diff --git a/third_party/blink/renderer/core/editing/commands/apply_style_command.cc b/third_party/blink/renderer/core/editing/commands/apply_style_command.cc
index aff5d4a0..68df523 100644
--- a/third_party/blink/renderer/core/editing/commands/apply_style_command.cc
+++ b/third_party/blink/renderer/core/editing/commands/apply_style_command.cc
@@ -26,6 +26,7 @@
 #include "third_party/blink/renderer/core/editing/commands/apply_style_command.h"
 
 #include "third_party/blink/renderer/core/css/css_computed_style_declaration.h"
+#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
 #include "third_party/blink/renderer/core/css/css_property_names.h"
 #include "third_party/blink/renderer/core/css/css_property_value_set.h"
@@ -510,8 +511,8 @@
     if (current_font_size != desired_font_size) {
       inline_style->SetProperty(
           CSSPropertyID::kFontSize,
-          *CSSPrimitiveValue::Create(desired_font_size,
-                                     CSSPrimitiveValue::UnitType::kPixels),
+          *CSSNumericLiteralValue::Create(desired_font_size,
+                                          CSSPrimitiveValue::UnitType::kPixels),
           false);
       SetNodeAttribute(element, kStyleAttr,
                        AtomicString(inline_style->AsText()));
diff --git a/third_party/blink/renderer/core/editing/editing_style.cc b/third_party/blink/renderer/core/editing/editing_style.cc
index 755b7bb..6312c1f 100644
--- a/third_party/blink/renderer/core/editing/editing_style.cc
+++ b/third_party/blink/renderer/core/editing/editing_style.cc
@@ -30,6 +30,7 @@
 #include "third_party/blink/renderer/core/css/css_color_value.h"
 #include "third_party/blink/renderer/core/css/css_computed_style_declaration.h"
 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
+#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value_mappings.h"
 #include "third_party/blink/renderer/core/css/css_property_value_set.h"
@@ -535,8 +536,8 @@
       // ReplaceSelectionCommandTest_TextAutosizingDoesntInflateText gets here.
       mutable_style_->SetProperty(
           CSSPropertyID::kFontSize,
-          CSSPrimitiveValue::Create(computed_style->SpecifiedFontSize(),
-                                    CSSPrimitiveValue::UnitType::kPixels)
+          CSSNumericLiteralValue::Create(computed_style->SpecifiedFontSize(),
+                                         CSSPrimitiveValue::UnitType::kPixels)
               ->CssText(),
           /* important */ false, node->GetDocument().GetSecureContextMode());
     }
diff --git a/third_party/blink/renderer/core/events/pointer_event_factory.cc b/third_party/blink/renderer/core/events/pointer_event_factory.cc
index 5168b09..7e44bd3 100644
--- a/third_party/blink/renderer/core/events/pointer_event_factory.cc
+++ b/third_party/blink/renderer/core/events/pointer_event_factory.cc
@@ -6,6 +6,8 @@
 
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
+#include "third_party/blink/renderer/core/page/chrome_client.h"
+#include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/platform/geometry/float_size.h"
 
 namespace blink {
@@ -94,13 +96,30 @@
 
   MouseEvent::SetCoordinatesFromWebPointerProperties(
       web_pointer_event_in_root_frame, dom_window, pointer_event_init);
+  // TODO(crbug.com/802067): pointerrawupdate event's movements are not
+  // calculated.
   if (RuntimeEnabledFeatures::ConsolidatedMovementXYEnabled() &&
       web_pointer_event.GetType() == WebInputEvent::kPointerMove) {
-    // TODO(eirage): pointerrawupdate event's movements are not calculated.
-    pointer_event_init->setMovementX(web_pointer_event.PositionInScreen().x -
-                                     last_global_position.X());
-    pointer_event_init->setMovementY(web_pointer_event.PositionInScreen().y -
-                                     last_global_position.Y());
+    // TODO(crbug.com/907309): Current movementX/Y is in physical pixel when
+    // zoom-for-dsf is enabled. Here we apply the device-scale-factor to align
+    // with the current behavior. We need to figure out what is the best
+    // behavior here.
+    float device_scale_factor = 1;
+    if (dom_window && dom_window->GetFrame()) {
+      LocalFrame* frame = dom_window->GetFrame();
+      if (frame->GetPage()->DeviceScaleFactorDeprecated() == 1) {
+        device_scale_factor = frame->GetPage()
+                                  ->GetChromeClient()
+                                  .GetScreenInfo()
+                                  .device_scale_factor;
+      }
+    }
+    pointer_event_init->setMovementX(
+        (web_pointer_event.PositionInScreen().x - last_global_position.X()) *
+        device_scale_factor);
+    pointer_event_init->setMovementY(
+        (web_pointer_event.PositionInScreen().y - last_global_position.Y()) *
+        device_scale_factor);
   }
 
   // If width/height is unknown we let PointerEventInit set it to 1.
@@ -316,14 +335,16 @@
   pointer_event_init->setCoalescedEvents(coalesced_pointer_events);
   pointer_event_init->setPredictedEvents(predicted_pointer_events);
 
-  SetLastPosition(pointer_event_init->pointerId(), web_pointer_event);
+  SetLastPosition(pointer_event_init->pointerId(),
+                  web_pointer_event.PositionInScreen());
   return PointerEvent::Create(type, pointer_event_init,
                               web_pointer_event.TimeStamp());
 }
 
-void PointerEventFactory::SetLastPosition(int pointer_id,
-                                          const WebPointerProperties& event) {
-  pointer_id_last_position_mapping_.Set(pointer_id, event.PositionInScreen());
+void PointerEventFactory::SetLastPosition(
+    int pointer_id,
+    const FloatPoint& position_in_screen) {
+  pointer_id_last_position_mapping_.Set(pointer_id, position_in_screen);
 }
 
 void PointerEventFactory::RemoveLastPosition(const int pointer_id) {
diff --git a/third_party/blink/renderer/core/events/pointer_event_factory.h b/third_party/blink/renderer/core/events/pointer_event_factory.h
index 897ffd6..8c69118 100644
--- a/third_party/blink/renderer/core/events/pointer_event_factory.h
+++ b/third_party/blink/renderer/core/events/pointer_event_factory.h
@@ -80,6 +80,7 @@
   bool IsPrimary(const WebPointerProperties&) const;
 
   static const PointerId kMouseId;
+  static const PointerId kInvalidId;
 
   // Removes pointer_id from the map.
   void RemoveLastPosition(const PointerId pointer_id);
@@ -90,6 +91,9 @@
   FloatPoint GetLastPointerPosition(PointerId pointer_id,
                                     const WebPointerProperties& event) const;
 
+  void SetLastPosition(PointerId pointer_id,
+                       const FloatPoint& position_in_screen);
+
  private:
   // We use int64_t to cover the whole range for PointerId with no
   // deleted hash value.
@@ -141,10 +145,6 @@
       const Vector<WebPointerEvent>& event_list,
       LocalDOMWindow* view);
 
-  void SetLastPosition(PointerId pointer_id, const WebPointerProperties& event);
-
-  static const PointerId kInvalidId;
-
   PointerId current_id_;
   HashMap<IncomingId,
           PointerId,
diff --git a/third_party/blink/renderer/core/execution_context/execution_context.h b/third_party/blink/renderer/core/execution_context/execution_context.h
index dd9329d..73bd1fa 100644
--- a/third_party/blink/renderer/core/execution_context/execution_context.h
+++ b/third_party/blink/renderer/core/execution_context/execution_context.h
@@ -313,16 +313,11 @@
       mojom::FeaturePolicyFeature feature) override;
 
  protected:
-  explicit ExecutionContext(v8::Isolate* isolate,
-                            Agent* agent,
-                            OriginTrialContext* origin_trial_context);
+  ExecutionContext(v8::Isolate* isolate,
+                   Agent* agent,
+                   OriginTrialContext* origin_trial_context);
   ~ExecutionContext() override;
 
-  void SetAgent(Agent* agent) {
-    DCHECK(agent);
-    agent_ = agent;
-  }
-
  private:
   // ConsoleLogger implementation.
   void AddConsoleMessageImpl(mojom::ConsoleMessageSource,
diff --git a/third_party/blink/renderer/core/execution_context/remote_security_context.cc b/third_party/blink/renderer/core/execution_context/remote_security_context.cc
index d8138e2..f2670bd8 100644
--- a/third_party/blink/renderer/core/execution_context/remote_security_context.cc
+++ b/third_party/blink/renderer/core/execution_context/remote_security_context.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/core/execution_context/remote_security_context.h"
 
 #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 #include "third_party/blink/renderer/platform/wtf/assertions.h"
 
@@ -40,8 +41,37 @@
   GetContentSecurityPolicy()->SetupSelf(*GetSecurityOrigin());
 }
 
-void RemoteSecurityContext::ResetSandboxFlags() {
-  sandbox_flags_ = WebSandboxFlags::kNone;
+void RemoteSecurityContext::ResetAndEnforceSandboxFlags(WebSandboxFlags flags) {
+  sandbox_flags_ = flags;
+
+  if (IsSandboxed(WebSandboxFlags::kOrigin) && GetSecurityOrigin() &&
+      !GetSecurityOrigin()->IsOpaque()) {
+    SetSecurityOrigin(GetSecurityOrigin()->DeriveNewOpaqueOrigin());
+  }
+}
+
+void RemoteSecurityContext::InitializeFeaturePolicy(
+    const ParsedFeaturePolicy& parsed_header,
+    const ParsedFeaturePolicy& container_policy,
+    const FeaturePolicy* parent_feature_policy,
+    const FeaturePolicy::FeatureState* opener_feature_state) {
+  // Feature policy should either come from a parent in the case of an embedded
+  // child frame, or from an opener if any when a new window is created by an
+  // opener. A main frame without an opener would not have a parent policy nor
+  // an opener feature state.
+  DCHECK(!parent_feature_policy || !opener_feature_state);
+  report_only_feature_policy_ = nullptr;
+  if (!opener_feature_state ||
+      !RuntimeEnabledFeatures::FeaturePolicyForSandboxEnabled()) {
+    feature_policy_ = FeaturePolicy::CreateFromParentPolicy(
+        parent_feature_policy, container_policy,
+        security_origin_->ToUrlOrigin());
+  } else {
+    DCHECK(!parent_feature_policy);
+    feature_policy_ = FeaturePolicy::CreateWithOpenerPolicy(
+        *opener_feature_state, security_origin_->ToUrlOrigin());
+  }
+  feature_policy_->SetHeaderPolicy(parsed_header);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/execution_context/remote_security_context.h b/third_party/blink/renderer/core/execution_context/remote_security_context.h
index 28c511b..9ef0272e5 100644
--- a/third_party/blink/renderer/core/execution_context/remote_security_context.h
+++ b/third_party/blink/renderer/core/execution_context/remote_security_context.h
@@ -23,10 +23,24 @@
 
   void SetReplicatedOrigin(scoped_refptr<SecurityOrigin>);
   void ResetReplicatedContentSecurityPolicy();
-  void ResetSandboxFlags();
+  void ResetAndEnforceSandboxFlags(WebSandboxFlags flags);
 
-  // FIXME: implement
-  void DidUpdateSecurityOrigin() override {}
+  // Constructs the enforcement FeaturePolicy struct for this security context.
+  // The resulting FeaturePolicy is a combination of:
+  //   * |parsed_header|: from the FeaturePolicy part of the response headers.
+  //   * |container_policy|: from <iframe>'s allow attribute.
+  //   * |parent_feature_policy|: which is the current state of feature policies
+  //     in a parent browsing context (frame).
+  //   * |opener_feature_state|: the current state of the policies in an opener
+  //     if any.
+  // Note that at most one of the |parent_feature_policy| or
+  // |opener_feature_state| should be provided. The |container_policy| is empty
+  // for a top-level security context.
+  void InitializeFeaturePolicy(
+      const ParsedFeaturePolicy& parsed_header,
+      const ParsedFeaturePolicy& container_policy,
+      const FeaturePolicy* parent_feature_policy,
+      const FeaturePolicy::FeatureState* opener_feature_state);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/execution_context/security_context.cc b/third_party/blink/renderer/core/execution_context/security_context.cc
index 55504fc2..479b162 100644
--- a/third_party/blink/renderer/core/execution_context/security_context.cc
+++ b/third_party/blink/renderer/core/execution_context/security_context.cc
@@ -82,7 +82,14 @@
 }
 
 SecurityContext::SecurityContext()
-    : sandbox_flags_(WebSandboxFlags::kNone),
+    : SecurityContext(nullptr, WebSandboxFlags::kNone, nullptr) {}
+
+SecurityContext::SecurityContext(scoped_refptr<SecurityOrigin> origin,
+                                 WebSandboxFlags sandbox_flags,
+                                 std::unique_ptr<FeaturePolicy> feature_policy)
+    : sandbox_flags_(sandbox_flags),
+      security_origin_(std::move(origin)),
+      feature_policy_(std::move(feature_policy)),
       address_space_(mojom::IPAddressSpace::kPublic),
       insecure_request_policy_(kLeaveInsecureRequestsAlone),
       require_safe_types_(false) {}
@@ -144,25 +151,6 @@
   return (sandbox_flags_ & mask) != WebSandboxFlags::kNone;
 }
 
-void SecurityContext::EnforceSandboxFlags(WebSandboxFlags mask) {
-  ApplySandboxFlags(mask);
-}
-
-void SecurityContext::ApplySandboxFlags(WebSandboxFlags mask,
-                                        bool is_potentially_trustworthy) {
-  sandbox_flags_ |= mask;
-
-  if (IsSandboxed(WebSandboxFlags::kOrigin) && GetSecurityOrigin() &&
-      !GetSecurityOrigin()->IsOpaque()) {
-    scoped_refptr<SecurityOrigin> security_origin =
-        GetSecurityOrigin()->DeriveNewOpaqueOrigin();
-    security_origin->SetOpaqueOriginIsPotentiallyTrustworthy(
-        is_potentially_trustworthy);
-    SetSecurityOrigin(std::move(security_origin));
-    DidUpdateSecurityOrigin();
-  }
-}
-
 String SecurityContext::addressSpaceForBindings() const {
   switch (address_space_) {
     case mojom::IPAddressSpace::kPublic:
@@ -199,36 +187,6 @@
   feature_policy_ = std::move(feature_policy);
 }
 
-void SecurityContext::InitializeFeaturePolicy(
-    const ParsedFeaturePolicy& parsed_header,
-    const ParsedFeaturePolicy& container_policy,
-    const FeaturePolicy* parent_feature_policy,
-    const FeaturePolicy::FeatureState* opener_feature_state) {
-  // Feature policy should either come from a parent in the case of an embedded
-  // child frame, or from an opener if any when a new window is created by an
-  // opener. A main frame without an opener would not have a parent policy nor
-  // an opener feature state.
-  DCHECK(!parent_feature_policy || !opener_feature_state);
-  report_only_feature_policy_ = nullptr;
-  if (!HasCustomizedFeaturePolicy()) {
-    feature_policy_ = FeaturePolicy::CreateFromParentPolicy(
-        nullptr, {}, security_origin_->ToUrlOrigin());
-    return;
-  }
-
-  if (!opener_feature_state ||
-      !RuntimeEnabledFeatures::FeaturePolicyForSandboxEnabled()) {
-    feature_policy_ = FeaturePolicy::CreateFromParentPolicy(
-        parent_feature_policy, container_policy,
-        security_origin_->ToUrlOrigin());
-  } else {
-    DCHECK(!parent_feature_policy);
-    feature_policy_ = FeaturePolicy::CreateWithOpenerPolicy(
-        *opener_feature_state, security_origin_->ToUrlOrigin());
-  }
-  feature_policy_->SetHeaderPolicy(parsed_header);
-}
-
 // Uses the parent enforcing policy as the basis for the report-only policy.
 void SecurityContext::AddReportOnlyFeaturePolicy(
     const ParsedFeaturePolicy& parsed_report_only_header,
diff --git a/third_party/blink/renderer/core/execution_context/security_context.h b/third_party/blink/renderer/core/execution_context/security_context.h
index ae66bb0..a376caf 100644
--- a/third_party/blink/renderer/core/execution_context/security_context.h
+++ b/third_party/blink/renderer/core/execution_context/security_context.h
@@ -88,12 +88,10 @@
   // Explicitly override the security origin for this security context.
   // Note: It is dangerous to change the security origin of a script context
   //       that already contains content.
-  void SetSecurityOrigin(scoped_refptr<SecurityOrigin>);
-  virtual void DidUpdateSecurityOrigin() = 0;
+  virtual void SetSecurityOrigin(scoped_refptr<SecurityOrigin>);
 
   WebSandboxFlags GetSandboxFlags() const { return sandbox_flags_; }
   bool IsSandboxed(WebSandboxFlags mask) const;
-  virtual void EnforceSandboxFlags(WebSandboxFlags mask);
 
   void SetAddressSpace(mojom::IPAddressSpace space) { address_space_ = space; }
   mojom::IPAddressSpace AddressSpace() const { return address_space_; }
@@ -136,22 +134,6 @@
     return feature_policy_.get();
   }
   void SetFeaturePolicy(std::unique_ptr<FeaturePolicy> feature_policy);
-  // Constructs the enforcement FeaturePolicy struct for this security context.
-  // The resulted FeaturePolicy is a combination of:
-  //   * |parsed_header|: from the FeaturePolicy part of the response headers.
-  //   * |container_policy|: from <iframe>'s allow attribute.
-  //   * |parent_feature_policy|: which is the current state of feature policies
-  //     in a parent browsing context (frame).
-  //   * |opener_feature_state|: the current state of the policies in an opener
-  //     if any.
-  // Note that at most one of the |parent_feature_policy| or
-  // |opener_feature_state| should be provided. The |container_policy| is empty
-  // for a top-level security context.
-  void InitializeFeaturePolicy(
-      const ParsedFeaturePolicy& parsed_header,
-      const ParsedFeaturePolicy& container_policy,
-      const FeaturePolicy* parent_feature_policy,
-      const FeaturePolicy::FeatureState* opener_feature_state);
   void AddReportOnlyFeaturePolicy(
       const ParsedFeaturePolicy& parsed_report_only_header,
       const ParsedFeaturePolicy& container_policy,
@@ -181,31 +163,23 @@
       mojom::FeaturePolicyDisposition,
       const String& message = g_empty_string) const {}
 
-  // Apply the sandbox flag. In addition, if the origin is not already opaque,
-  // the origin is updated to a newly created unique opaque origin, setting the
-  // potentially trustworthy bit from |is_potentially_trustworthy|.
-  void ApplySandboxFlags(WebSandboxFlags mask,
-                         bool is_potentially_trustworthy = false);
-
  protected:
   SecurityContext();
+  SecurityContext(scoped_refptr<SecurityOrigin> origin,
+                  WebSandboxFlags sandbox_flags,
+                  std::unique_ptr<FeaturePolicy> feature_policy);
   virtual ~SecurityContext();
 
   void SetContentSecurityPolicy(ContentSecurityPolicy*);
 
-  // Determines whether or not the SecurityContext has a customized feature
-  // policy. If this method returns false, |feature_policy_| is reset to a
-  // default value ignoring container, header, and inherited policies.
-  virtual bool HasCustomizedFeaturePolicy() const { return true; }
-
   WebSandboxFlags sandbox_flags_;
-
- private:
   scoped_refptr<SecurityOrigin> security_origin_;
-  Member<ContentSecurityPolicy> content_security_policy_;
   std::unique_ptr<FeaturePolicy> feature_policy_;
   std::unique_ptr<FeaturePolicy> report_only_feature_policy_;
 
+ private:
+  Member<ContentSecurityPolicy> content_security_policy_;
+
   mojom::IPAddressSpace address_space_;
   WebInsecureRequestPolicy insecure_request_policy_;
   bool mixed_autoupgrade_opt_out_;
diff --git a/third_party/blink/renderer/core/exported/web_frame_serializer.cc b/third_party/blink/renderer/core/exported/web_frame_serializer.cc
index 11e15b0..6bd4f09d 100644
--- a/third_party/blink/renderer/core/exported/web_frame_serializer.cc
+++ b/third_party/blink/renderer/core/exported/web_frame_serializer.cc
@@ -105,7 +105,7 @@
   MHTMLFrameSerializerDelegate(
       WebFrameSerializer::MHTMLPartsGenerationDelegate&,
       HeapHashSet<WeakMember<const Element>>&);
-  ~MHTMLFrameSerializerDelegate() override;
+  ~MHTMLFrameSerializerDelegate() override = default;
   bool ShouldIgnoreElement(const Element&) override;
   bool ShouldIgnoreAttribute(const Element&, const Attribute&) override;
   bool RewriteLink(const Element&, String& rewritten_link) override;
@@ -135,14 +135,6 @@
       shadow_template_elements_(shadow_template_elements),
       popup_overlays_skipped_(false) {}
 
-MHTMLFrameSerializerDelegate::~MHTMLFrameSerializerDelegate() {
-  if (web_delegate_.RemovePopupOverlay()) {
-    UMA_HISTOGRAM_BOOLEAN(
-        "PageSerialization.MhtmlGeneration.PopupOverlaySkipped",
-        popup_overlays_skipped_);
-  }
-}
-
 bool MHTMLFrameSerializerDelegate::ShouldIgnoreElement(const Element& element) {
   if (ShouldIgnoreHiddenElement(element))
     return true;
diff --git a/third_party/blink/renderer/core/exported/web_frame_serializer_sanitization_test.cc b/third_party/blink/renderer/core/exported/web_frame_serializer_sanitization_test.cc
index 15264f4..98e4a7c2 100644
--- a/third_party/blink/renderer/core/exported/web_frame_serializer_sanitization_test.cc
+++ b/third_party/blink/renderer/core/exported/web_frame_serializer_sanitization_test.cc
@@ -41,7 +41,6 @@
 #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
 #include "third_party/blink/renderer/platform/mhtml/mhtml_archive.h"
 #include "third_party/blink/renderer/platform/mhtml/mhtml_parser.h"
-#include "third_party/blink/renderer/platform/testing/histogram_tester.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
@@ -188,8 +187,6 @@
 
   WebLocalFrameImpl* MainFrameImpl() { return helper_.LocalMainFrame(); }
 
-  HistogramTester histogram_tester_;
-
  private:
   frame_test_helpers::WebViewHelper helper_;
   SimpleMHTMLPartsGenerationDelegate mhtml_delegate_;
@@ -323,8 +320,6 @@
   String mhtml = GenerateMHTMLFromHtml("http://www.test.com", "popup.html");
   EXPECT_EQ(WTF::kNotFound, mhtml.Find("class=3D\"overlay"));
   EXPECT_EQ(WTF::kNotFound, mhtml.Find("class=3D\"modal"));
-  histogram_tester_.ExpectUniqueSample(
-      "PageSerialization.MhtmlGeneration.PopupOverlaySkipped", true, 1);
 }
 
 TEST_F(WebFrameSerializerSanitizationTest, PopupOverlayNotFound) {
@@ -332,8 +327,6 @@
   SetRemovePopupOverlay(true);
   String mhtml =
       GenerateMHTMLFromHtml("http://www.test.com", "text_only_page.html");
-  histogram_tester_.ExpectUniqueSample(
-      "PageSerialization.MhtmlGeneration.PopupOverlaySkipped", false, 1);
 }
 
 TEST_F(WebFrameSerializerSanitizationTest, KeepPopupOverlayIfNotRequested) {
@@ -342,8 +335,6 @@
   String mhtml = GenerateMHTMLFromHtml("http://www.test.com", "popup.html");
   EXPECT_NE(WTF::kNotFound, mhtml.Find("class=3D\"overlay"));
   EXPECT_NE(WTF::kNotFound, mhtml.Find("class=3D\"modal"));
-  histogram_tester_.ExpectTotalCount(
-      "PageSerialization.MhtmlGeneration.PopupOverlaySkipped", 0);
 }
 
 TEST_F(WebFrameSerializerSanitizationTest, LinkIntegrity) {
diff --git a/third_party/blink/renderer/core/exported/web_remote_frame_impl.cc b/third_party/blink/renderer/core/exported/web_remote_frame_impl.cc
index 9c666af..969f491 100644
--- a/third_party/blink/renderer/core/exported/web_remote_frame_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_remote_frame_impl.cc
@@ -245,8 +245,7 @@
 
 void WebRemoteFrameImpl::SetReplicatedSandboxFlags(WebSandboxFlags flags) {
   DCHECK(GetFrame());
-  GetFrame()->GetSecurityContext()->ResetSandboxFlags();
-  GetFrame()->GetSecurityContext()->EnforceSandboxFlags(
+  GetFrame()->GetSecurityContext()->ResetAndEnforceSandboxFlags(
       static_cast<SandboxFlags>(flags));
 }
 
diff --git a/third_party/blink/renderer/core/feature_policy/policy_test.cc b/third_party/blink/renderer/core/feature_policy/policy_test.cc
index f7bd73e..d915fd5f 100644
--- a/third_party/blink/renderer/core/feature_policy/policy_test.cc
+++ b/third_party/blink/renderer/core/feature_policy/policy_test.cc
@@ -24,11 +24,13 @@
 class PolicyTest : public testing::Test {
  public:
   void SetUp() override {
-    document_ = MakeGarbageCollected<Document>();
-    document_->SetSecurityOrigin(SecurityOrigin::CreateFromString(kSelfOrigin));
-    document_->ApplyFeaturePolicyFromHeader(
-        "fullscreen *; payment 'self'; midi 'none'; camera 'self' "
-        "https://example.com https://example.net");
+    DocumentInit init =
+        DocumentInit::Create()
+            .WithOriginToCommit(SecurityOrigin::CreateFromString(kSelfOrigin))
+            .WithFeaturePolicyHeader(
+                "fullscreen *; payment 'self'; midi 'none'; camera 'self' "
+                "https://example.com https://example.net");
+    document_ = MakeGarbageCollected<Document>(init);
   }
 
   DOMFeaturePolicy* GetPolicy() const { return policy_; }
diff --git a/third_party/blink/renderer/core/frame/csp/content_security_policy.cc b/third_party/blink/renderer/core/frame/csp/content_security_policy.cc
index 8ecf36b1..853bfaa 100644
--- a/third_party/blink/renderer/core/frame/csp/content_security_policy.cc
+++ b/third_party/blink/renderer/core/frame/csp/content_security_policy.cc
@@ -158,6 +158,10 @@
       require_trusted_types_(false),
       insecure_request_policy_(kLeaveInsecureRequestsAlone) {}
 
+bool ContentSecurityPolicy::IsBound() {
+  return delegate_;
+}
+
 void ContentSecurityPolicy::BindToDelegate(
     ContentSecurityPolicyDelegate& delegate) {
   // TODO(crbug.com/915954): Add DCHECK(!delegate_). It seems some call sites
@@ -346,10 +350,7 @@
     Member<CSPDirectiveList> policy =
         CSPDirectiveList::Create(this, begin, position, type, source);
 
-    if (!policy->AllowEval(nullptr,
-                           SecurityViolationReportingPolicy::kSuppressReporting,
-                           kWillNotThrowException, g_empty_string) &&
-        disable_eval_error_message_.IsNull()) {
+    if (policy->ShouldDisableEval() && disable_eval_error_message_.IsNull()) {
       disable_eval_error_message_ = policy->EvalDisabledErrorMessage();
     }
 
@@ -558,9 +559,7 @@
 
 String ContentSecurityPolicy::EvalDisabledErrorMessage() const {
   for (const auto& policy : policies_) {
-    if (!policy->AllowEval(nullptr,
-                           SecurityViolationReportingPolicy::kSuppressReporting,
-                           kWillNotThrowException, g_empty_string)) {
+    if (policy->ShouldDisableEval()) {
       return policy->EvalDisabledErrorMessage();
     }
   }
diff --git a/third_party/blink/renderer/core/frame/csp/content_security_policy.h b/third_party/blink/renderer/core/frame/csp/content_security_policy.h
index 616556f..75f8137 100644
--- a/third_party/blink/renderer/core/frame/csp/content_security_policy.h
+++ b/third_party/blink/renderer/core/frame/csp/content_security_policy.h
@@ -211,6 +211,7 @@
   ~ContentSecurityPolicy();
   void Trace(blink::Visitor*);
 
+  bool IsBound();
   void BindToDelegate(ContentSecurityPolicyDelegate&);
   void SetupSelf(const SecurityOrigin&);
   void SetupSelf(const ContentSecurityPolicy&);
diff --git a/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc b/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc
index 5da19f6d..1792c14 100644
--- a/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc
+++ b/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc
@@ -161,6 +161,11 @@
             ->GetText() +
         "\".\n";
     directives->SetEvalDisabledErrorMessage(message);
+  } else if (directives->trusted_types_) {
+    String message =
+        "Refused to evaluate a string as JavaScript because this document "
+        "requires 'Trusted Type' assignment.";
+    directives->SetEvalDisabledErrorMessage(message);
   }
 
   if (directives->IsReportOnly() &&
@@ -772,6 +777,16 @@
              ContentSecurityPolicy::DirectiveType::kScriptSrc));
 }
 
+bool CSPDirectiveList::ShouldDisableEvalBecauseScriptSrc() const {
+  return !AllowEval(
+      nullptr, SecurityViolationReportingPolicy::kSuppressReporting,
+      ContentSecurityPolicy::kWillNotThrowException, g_empty_string);
+}
+
+bool CSPDirectiveList::ShouldDisableEvalBecauseTrustedTypes() const {
+  return trusted_types_;
+}
+
 bool CSPDirectiveList::AllowPluginType(
     const String& type,
     const String& type_attribute,
diff --git a/third_party/blink/renderer/core/frame/csp/csp_directive_list.h b/third_party/blink/renderer/core/frame/csp/csp_directive_list.h
index 27af666a..fc9f93e2b 100644
--- a/third_party/blink/renderer/core/frame/csp/csp_directive_list.h
+++ b/third_party/blink/renderer/core/frame/csp/csp_directive_list.h
@@ -108,6 +108,12 @@
   void ReportMixedContent(const KURL& mixed_url,
                           ResourceRequest::RedirectStatus) const;
 
+  bool ShouldDisableEval() const {
+    return ShouldDisableEvalBecauseScriptSrc() ||
+           ShouldDisableEvalBecauseTrustedTypes();
+  }
+  bool ShouldDisableEvalBecauseScriptSrc() const;
+  bool ShouldDisableEvalBecauseTrustedTypes() const;
   const String& EvalDisabledErrorMessage() const {
     return eval_disabled_error_message_;
   }
diff --git a/third_party/blink/renderer/core/frame/csp/execution_context_csp_delegate.cc b/third_party/blink/renderer/core/frame/csp/execution_context_csp_delegate.cc
index bdb9726..c4a1c22 100644
--- a/third_party/blink/renderer/core/frame/csp/execution_context_csp_delegate.cc
+++ b/third_party/blink/renderer/core/frame/csp/execution_context_csp_delegate.cc
@@ -43,7 +43,25 @@
 }
 
 void ExecutionContextCSPDelegate::SetSandboxFlags(SandboxFlags mask) {
-  GetSecurityContext().EnforceSandboxFlags(mask);
+  // Ideally sandbox flags are determined at construction time since
+  // sandbox flags influence the security origin and that influences
+  // the Agent that is assigned for the ExecutionContext. Changing
+  // an ExecutionContext's agent in the middle of an object lifecycle
+  // is not permitted.
+
+  // Since Workers and Worklets don't share agents (each one is unique)
+  // we allow them to apply new sandbox flags on top of the current ones.
+  WorkerOrWorkletGlobalScope* worklet_or_worker =
+      DynamicTo<WorkerOrWorkletGlobalScope>(execution_context_.Get());
+  if (worklet_or_worker) {
+    worklet_or_worker->ApplySandboxFlags(mask);
+  }
+  // Just check that all the sandbox flags that are set by CSP have
+  // already been set on the security context. Meta tags can't set them
+  // and we should have already constructed the document with the correct
+  // sandbox flags from CSP already.
+  WebSandboxFlags flags = GetSecurityContext().GetSandboxFlags();
+  CHECK_EQ(flags | mask, flags);
 }
 
 void ExecutionContextCSPDelegate::SetAddressSpace(mojom::IPAddressSpace space) {
diff --git a/third_party/blink/renderer/core/frame/local_dom_window.cc b/third_party/blink/renderer/core/frame/local_dom_window.cc
index 693be68..21d9b7f 100644
--- a/third_party/blink/renderer/core/frame/local_dom_window.cc
+++ b/third_party/blink/renderer/core/frame/local_dom_window.cc
@@ -55,7 +55,6 @@
 #include "third_party/blink/renderer/core/dom/events/scoped_event_queue.h"
 #include "third_party/blink/renderer/core/dom/frame_request_callback_collection.h"
 #include "third_party/blink/renderer/core/dom/scripted_idle_task_controller.h"
-#include "third_party/blink/renderer/core/dom/scripted_task_queue_controller.h"
 #include "third_party/blink/renderer/core/dom/sink_document.h"
 #include "third_party/blink/renderer/core/dom/user_gesture_indicator.h"
 #include "third_party/blink/renderer/core/editing/editor.h"
@@ -985,13 +984,6 @@
   return promise;
 }
 
-ScriptedTaskQueueController* LocalDOMWindow::taskQueue() const {
-  if (Document* document = this->document()) {
-    return ScriptedTaskQueueController::From(*document);
-  }
-  return nullptr;
-}
-
 double LocalDOMWindow::devicePixelRatio() const {
   if (!GetFrame())
     return 0.0;
diff --git a/third_party/blink/renderer/core/frame/local_dom_window.h b/third_party/blink/renderer/core/frame/local_dom_window.h
index e62a3e6..c2190f73 100644
--- a/third_party/blink/renderer/core/frame/local_dom_window.h
+++ b/third_party/blink/renderer/core/frame/local_dom_window.h
@@ -61,7 +61,6 @@
 class Modulator;
 class Navigator;
 class Screen;
-class ScriptedTaskQueueController;
 class ScriptPromise;
 class ScriptState;
 class ScrollToOptions;
@@ -218,8 +217,6 @@
   // Acessibility Object Model
   ScriptPromise getComputedAccessibleNode(ScriptState*, Element*);
 
-  ScriptedTaskQueueController* taskQueue() const;
-
   // WebKit animation extensions
   int requestAnimationFrame(V8FrameRequestCallback*);
   int webkitRequestAnimationFrame(V8FrameRequestCallback*);
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
index e9fda55..2ea36ad 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
@@ -1629,12 +1629,8 @@
   DCHECK(!page.MainFrame());
   frame->InitializeCoreFrame(
       page, nullptr, name,
-      opener ? &ToCoreFrame(*opener)->window_agent_factory() : nullptr);
-  if (RuntimeEnabledFeatures::FeaturePolicyForSandboxEnabled())
-    frame->GetFrame()->SetOpenerFeatureState(opener_feature_state);
-  // Can't force sandbox flags until there's a core frame.
-  frame->GetFrame()->Loader().ForceSandboxFlags(
-      static_cast<SandboxFlags>(sandbox_flags));
+      opener ? &ToCoreFrame(*opener)->window_agent_factory() : nullptr,
+      sandbox_flags, opener_feature_state);
   return frame;
 }
 
@@ -1652,6 +1648,17 @@
   Frame* previous_frame = ToCoreFrame(*previous_web_frame);
   web_frame->SetParent(previous_web_frame->Parent());
   web_frame->SetOpener(previous_web_frame->Opener());
+  WebSandboxFlags sandbox_flags = WebSandboxFlags::kNone;
+  FeaturePolicy::FeatureState feature_state;
+  if (!previous_frame->Owner()) {
+    // Provisional main frames need to force sandbox flags.  This is necessary
+    // to inherit sandbox flags when a sandboxed frame does a window.open()
+    // which triggers a cross-process navigation.
+    sandbox_flags = frame_policy.sandbox_flags;
+    // If there is an opener (even disowned), the opener policies must be
+    // inherited the same way as sandbox flag.
+    feature_state = previous_frame->OpenerFeatureState();
+  }
   // Note: this *always* temporarily sets a frame owner, even for main frames!
   // When a core Frame is created with no owner, it attempts to set itself as
   // the main frame of the Page. However, this is a provisional frame, and may
@@ -1666,21 +1673,14 @@
   web_frame->InitializeCoreFrame(
       *previous_frame->GetPage(), MakeGarbageCollected<DummyFrameOwner>(),
       previous_frame->Tree().GetName(),
-      &ToCoreFrame(*previous_web_frame)->window_agent_factory());
+      &ToCoreFrame(*previous_web_frame)->window_agent_factory(), sandbox_flags,
+      feature_state);
 
   LocalFrame* new_frame = web_frame->GetFrame();
   new_frame->SetOwner(previous_frame->Owner());
   if (auto* remote_frame_owner =
           DynamicTo<RemoteFrameOwner>(new_frame->Owner())) {
     remote_frame_owner->SetFramePolicy(frame_policy);
-  } else if (!new_frame->Owner()) {
-    // Provisional main frames need to force sandbox flags.  This is necessary
-    // to inherit sandbox flags when a sandboxed frame does a window.open()
-    // which triggers a cross-process navigation.
-    new_frame->Loader().ForceSandboxFlags(frame_policy.sandbox_flags);
-    // If there is an opener (even disowned), the opener policies must be
-    // inherited the same way as sandbox flag.
-    new_frame->SetOpenerFeatureState(previous_frame->OpenerFeatureState());
   }
 
   return web_frame;
@@ -1745,11 +1745,17 @@
     Page& page,
     FrameOwner* owner,
     const AtomicString& name,
-    WindowAgentFactory* window_agent_factory) {
+    WindowAgentFactory* window_agent_factory,
+    WebSandboxFlags sandbox_flags,
+    const FeaturePolicy::FeatureState& opener_feature_state) {
   SetCoreFrame(MakeGarbageCollected<LocalFrame>(local_frame_client_.Get(), page,
                                                 owner, window_agent_factory,
                                                 interface_registry_));
   frame_->Tree().SetName(name);
+  if (RuntimeEnabledFeatures::FeaturePolicyForSandboxEnabled())
+    frame_->SetOpenerFeatureState(opener_feature_state);
+  frame_->Loader().ForceSandboxFlags(sandbox_flags);
+
   // We must call init() after frame_ is assigned because it is referenced
   // during init().
   frame_->Init();
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.h b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
index 15ed90d..043afe00 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.h
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
@@ -347,10 +347,14 @@
   void WasHidden() override;
   void WasShown() override;
 
-  void InitializeCoreFrame(Page&,
-                           FrameOwner*,
-                           const AtomicString& name,
-                           WindowAgentFactory*);
+  void InitializeCoreFrame(
+      Page&,
+      FrameOwner*,
+      const AtomicString& name,
+      WindowAgentFactory*,
+      WebSandboxFlags sandbox_flags = WebSandboxFlags::kNone,
+      const FeaturePolicy::FeatureState& opener_feature_state =
+          FeaturePolicy::FeatureState());
   LocalFrame* GetFrame() const { return frame_.Get(); }
 
   void WillBeDetached();
diff --git a/third_party/blink/renderer/core/frame/window.idl b/third_party/blink/renderer/core/frame/window.idl
index 64959e80..b5c9a7b6 100644
--- a/third_party/blink/renderer/core/frame/window.idl
+++ b/third_party/blink/renderer/core/frame/window.idl
@@ -120,8 +120,6 @@
     [HighEntropy, Measure, NewObject] MediaQueryList matchMedia(DOMString query);
     [SameObject, Replaceable] readonly attribute Screen screen;
 
-    [RuntimeEnabled=ScriptedTaskQueue, SameObject, Replaceable] readonly attribute ScriptedTaskQueueController TaskQueue;
-
     // browsing context
     void moveTo(long x, long y);
     void moveBy(long x, long y);
diff --git a/third_party/blink/renderer/core/html/html_document.cc b/third_party/blink/renderer/core/html/html_document.cc
index fd65513..3db77e50 100644
--- a/third_party/blink/renderer/core/html/html_document.cc
+++ b/third_party/blink/renderer/core/html/html_document.cc
@@ -81,7 +81,8 @@
       DocumentInit::Create()
           .WithContextDocument(ContextDocument())
           .WithURL(Url())
-          .WithRegistrationContext(RegistrationContext()));
+          .WithRegistrationContext(RegistrationContext())
+          .WithOriginToCommit(GetSecurityOrigin()->IsolatedCopy()));
 }
 
 // --------------------------------------------------------------------------
diff --git a/third_party/blink/renderer/core/html/html_table_element.cc b/third_party/blink/renderer/core/html/html_table_element.cc
index 6eb0866..6f71888 100644
--- a/third_party/blink/renderer/core/html/html_table_element.cc
+++ b/third_party/blink/renderer/core/html/html_table_element.cc
@@ -28,6 +28,7 @@
 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
 #include "third_party/blink/renderer/core/css/css_image_value.h"
 #include "third_party/blink/renderer/core/css/css_inherited_value.h"
+#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_property_names.h"
 #include "third_party/blink/renderer/core/css/css_property_value_set.h"
 #include "third_party/blink/renderer/core/css/style_change_reason.h"
@@ -532,18 +533,18 @@
                          *CSSInheritedValue::Create());
       break;
     case kSolidBorders:
-      style->SetProperty(
-          CSSPropertyID::kBorderWidth,
-          *CSSPrimitiveValue::Create(1, CSSPrimitiveValue::UnitType::kPixels));
+      style->SetProperty(CSSPropertyID::kBorderWidth,
+                         *CSSNumericLiteralValue::Create(
+                             1, CSSPrimitiveValue::UnitType::kPixels));
       style->SetProperty(CSSPropertyID::kBorderStyle,
                          *CSSIdentifierValue::Create(CSSValueID::kSolid));
       style->SetProperty(CSSPropertyID::kBorderColor,
                          *CSSInheritedValue::Create());
       break;
     case kInsetBorders:
-      style->SetProperty(
-          CSSPropertyID::kBorderWidth,
-          *CSSPrimitiveValue::Create(1, CSSPrimitiveValue::UnitType::kPixels));
+      style->SetProperty(CSSPropertyID::kBorderWidth,
+                         *CSSNumericLiteralValue::Create(
+                             1, CSSPrimitiveValue::UnitType::kPixels));
       style->SetProperty(CSSPropertyID::kBorderStyle,
                          *CSSIdentifierValue::Create(CSSValueID::kInset));
       style->SetProperty(CSSPropertyID::kBorderColor,
@@ -557,7 +558,7 @@
 
   if (padding_)
     style->SetProperty(CSSPropertyID::kPadding,
-                       *CSSPrimitiveValue::Create(
+                       *CSSNumericLiteralValue::Create(
                            padding_, CSSPrimitiveValue::UnitType::kPixels));
 
   return style;
diff --git a/third_party/blink/renderer/core/html/html_view_source_document.cc b/third_party/blink/renderer/core/html/html_view_source_document.cc
index 26fd34d..e45fe09 100644
--- a/third_party/blink/renderer/core/html/html_view_source_document.cc
+++ b/third_party/blink/renderer/core/html/html_view_source_document.cc
@@ -52,7 +52,7 @@
 
 HTMLViewSourceDocument::HTMLViewSourceDocument(const DocumentInit& initializer,
                                                const String& mime_type)
-    : HTMLDocument(initializer), type_(mime_type) {
+    : HTMLDocument(initializer, kViewSourceDocumentClass), type_(mime_type) {
   SetIsViewSource(true);
 
   // FIXME: Why do view-source pages need to load in quirks mode?
diff --git a/third_party/blink/renderer/core/html/html_view_source_document.h b/third_party/blink/renderer/core/html/html_view_source_document.h
index 97ffda6..021425c 100644
--- a/third_party/blink/renderer/core/html/html_view_source_document.h
+++ b/third_party/blink/renderer/core/html/html_view_source_document.h
@@ -75,9 +75,6 @@
   Element* AddLink(const AtomicString& url, bool is_anchor);
   Element* AddBase(const AtomicString& href);
 
-  // A view-source document is not a regular WebPage.
-  bool HasCustomizedFeaturePolicy() const final { return false; }
-
   String type_;
   Member<Element> current_;
   Member<HTMLTableSectionElement> tbody_;
diff --git a/third_party/blink/renderer/core/input/event_handler.cc b/third_party/blink/renderer/core/input/event_handler.cc
index c3189ff..344bd58 100644
--- a/third_party/blink/renderer/core/input/event_handler.cc
+++ b/third_party/blink/renderer/core/input/event_handler.cc
@@ -1401,6 +1401,12 @@
     mouse_wheel_event_manager_->ElementRemoved(target);
 }
 
+void EventHandler::SetMousePositionForPointerUnlock(
+    FloatPoint lock_position_in_screen) {
+  pointer_event_manager_->SetLastMousePositionForPointerUnlock(
+      lock_position_in_screen);
+}
+
 WebInputEventResult EventHandler::DispatchMousePointerEvent(
     const WebInputEvent::Type event_type,
     Element* target_element,
diff --git a/third_party/blink/renderer/core/input/event_handler.h b/third_party/blink/renderer/core/input/event_handler.h
index 7666b16..1f954f0 100644
--- a/third_party/blink/renderer/core/input/event_handler.h
+++ b/third_party/blink/renderer/core/input/event_handler.h
@@ -295,6 +295,10 @@
 
   void SetIsFallbackCursorModeOn(bool is_on);
 
+  // Set the last mouse position so that next movemove after unlock will be
+  // calculated from the lock position.
+  void SetMousePositionForPointerUnlock(FloatPoint lock_position_in_screen);
+
  private:
   enum NoCursorChangeType { kNoCursorChange };
 
diff --git a/third_party/blink/renderer/core/input/event_handler_test.cc b/third_party/blink/renderer/core/input/event_handler_test.cc
index 54a85db..7837fa7 100644
--- a/third_party/blink/renderer/core/input/event_handler_test.cc
+++ b/third_party/blink/renderer/core/input/event_handler_test.cc
@@ -2796,4 +2796,53 @@
                    .IsMousePositionUnknown());
 }
 
+// Tests that pen dragging on an element and moves will keep the element active.
+TEST_F(EventHandlerSimTest, PenDraggingOnElementActive) {
+  WebView().MainFrameWidget()->Resize(WebSize(800, 600));
+
+  SimRequest main_resource("https://example.com/test.html", "text/html");
+  LoadURL("https://example.com/test.html");
+
+  main_resource.Complete(R"HTML(
+    <!DOCTYPE html>
+    <style>
+    div {
+      width: 200px;
+      height: 200px;
+    }
+    </style>
+    <div id="target"></div>
+  )HTML");
+
+  Compositor().BeginFrame();
+  WebMouseEvent pen_down(WebMouseEvent::kMouseDown, WebFloatPoint(100, 100),
+                         WebFloatPoint(100, 100),
+                         WebPointerProperties::Button::kLeft, 0,
+                         WebInputEvent::Modifiers::kLeftButtonDown,
+                         WebInputEvent::GetStaticTimeStampForTests());
+  pen_down.pointer_type = blink::WebPointerProperties::PointerType::kPen;
+  pen_down.SetFrameScale(1);
+  WebView().MainFrameWidget()->HandleInputEvent(
+      WebCoalescedInputEvent(pen_down));
+
+  WebMouseEvent pen_move(WebMouseEvent::kMouseMove, WebFloatPoint(100, 100),
+                         WebFloatPoint(100, 100),
+                         WebPointerProperties::Button::kLeft, 0,
+                         WebInputEvent::Modifiers::kLeftButtonDown,
+                         WebInputEvent::GetStaticTimeStampForTests());
+  pen_move.pointer_type = blink::WebPointerProperties::PointerType::kPen;
+  pen_move.SetFrameScale(1);
+  // Send first mouse move to update mouse event sates.
+  WebView().MainFrameWidget()->HandleInputEvent(
+      WebCoalescedInputEvent(pen_move));
+
+  // Send another mouse move again to update active element to verify mouse
+  // event states.
+  WebView().MainFrameWidget()->HandleInputEvent(
+      WebCoalescedInputEvent(pen_move));
+
+  EXPECT_EQ(GetDocument().GetActiveElement(),
+            GetDocument().getElementById("target"));
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/input/mouse_event_manager.cc b/third_party/blink/renderer/core/input/mouse_event_manager.cc
index 4bde658..6049b4db 100644
--- a/third_party/blink/renderer/core/input/mouse_event_manager.cc
+++ b/third_party/blink/renderer/core/input/mouse_event_manager.cc
@@ -31,9 +31,11 @@
 #include "third_party/blink/renderer/core/layout/hit_test_result.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/core/page/autoscroll_controller.h"
+#include "third_party/blink/renderer/core/page/chrome_client.h"
 #include "third_party/blink/renderer/core/page/drag_controller.h"
 #include "third_party/blink/renderer/core/page/drag_state.h"
 #include "third_party/blink/renderer/core/page/focus_controller.h"
+#include "third_party/blink/renderer/core/page/pointer_lock_controller.h"
 #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/renderer/core/svg/svg_document_extensions.h"
@@ -63,21 +65,40 @@
 
 void UpdateMouseMovementXY(const WebMouseEvent& mouse_event,
                            const FloatPoint* last_position,
+                           LocalDOMWindow* dom_window,
                            MouseEventInit* initializer) {
   if (RuntimeEnabledFeatures::ConsolidatedMovementXYEnabled() &&
       mouse_event.GetType() == WebInputEvent::kMouseMove && last_position) {
+    // TODO(crbug.com/907309): Current movementX/Y is in physical pixel when
+    // zoom-for-dsf is enabled. Here we apply the device-scale-factor to align
+    // with the current behavior. We need to figure out what is the best
+    // behavior here.
+    float device_scale_factor = 1;
+    if (dom_window && dom_window->GetFrame()) {
+      LocalFrame* frame = dom_window->GetFrame();
+      if (frame->GetPage()->DeviceScaleFactorDeprecated() == 1) {
+        device_scale_factor = frame->GetPage()
+                                  ->GetChromeClient()
+                                  .GetScreenInfo()
+                                  .device_scale_factor;
+      }
+    }
     if (RuntimeEnabledFeatures::FractionalMouseEventEnabled()) {
-      initializer->setMovementX(mouse_event.PositionInScreen().x -
-                                last_position->X());
-      initializer->setMovementY(mouse_event.PositionInScreen().y -
-                                last_position->Y());
+      initializer->setMovementX(
+          (mouse_event.PositionInScreen().x - last_position->X()) *
+          device_scale_factor);
+      initializer->setMovementY(
+          (mouse_event.PositionInScreen().y - last_position->Y()) *
+          device_scale_factor);
     } else {
       initializer->setMovementX(
-          static_cast<int>(mouse_event.PositionInScreen().x) -
-          static_cast<int>(last_position->X()));
+          static_cast<int>(mouse_event.PositionInScreen().x *
+                           device_scale_factor) -
+          static_cast<int>(last_position->X() * device_scale_factor));
       initializer->setMovementY(
-          static_cast<int>(mouse_event.PositionInScreen().y) -
-          static_cast<int>(last_position->Y()));
+          static_cast<int>(mouse_event.PositionInScreen().y *
+                           device_scale_factor) -
+          static_cast<int>(last_position->Y() * device_scale_factor));
     }
   }
 }
@@ -255,7 +276,8 @@
     MouseEvent::SetCoordinatesFromWebPointerProperties(
         mouse_event.FlattenTransform(), target_node->GetDocument().domWindow(),
         initializer);
-    UpdateMouseMovementXY(mouse_event, last_position, initializer);
+    UpdateMouseMovementXY(mouse_event, last_position,
+                          target_node->GetDocument().domWindow(), initializer);
     initializer->setButton(static_cast<int16_t>(mouse_event.button));
     initializer->setButtons(MouseEvent::WebInputEventModifiersToButtons(
         mouse_event.GetModifiers()));
@@ -393,6 +415,10 @@
   if (!frame_->GetPage()->IsCursorVisible())
     return;
 
+  // Don't dispatch a synthetic event if pointer is locked.
+  if (frame_->GetPage()->GetPointerLockController().GetElement())
+    return;
+
   WebPointerEvent::Button button = WebPointerProperties::Button::kNoButton;
   int modifiers = KeyboardEventManager::GetCurrentModifierState() |
                   WebInputEvent::kRelativeMotionEvent;
@@ -829,25 +855,12 @@
       frame_->GetSettings()->GetBarrelButtonForDragEnabled())
     pen_drag_button = WebPointerProperties::Button::kBarrel;
 
-  // While resetting m_mousePressed here may seem out of place, it turns out
-  // to be needed to handle some bugs^Wfeatures in Blink mouse event handling:
-  // 1. Certain elements, such as <embed>, capture mouse events. They do not
-  //    bubble back up. One way for a <embed> to start capturing mouse events
-  //    is on a mouse press. The problem is the <embed> node only starts
-  //    capturing mouse events *after* m_mousePressed for the containing frame
-  //    has already been set to true. As a result, the frame's EventHandler
-  //    never sees the mouse release event, which is supposed to reset
-  //    m_mousePressed... so m_mousePressed ends up remaining true until the
-  //    event handler finally gets another mouse released event. Oops.
-  // 2. Dragging doesn't start until after a mouse press event, but a drag
-  //    that ends as a result of a mouse release does not send a mouse release
-  //    event. As a result, m_mousePressed also ends up remaining true until
-  //    the next mouse release event seen by the EventHandler.
+  // Only handles dragging for mouse left button drag and pen drag button.
   if ((!is_pen &&
        event.Event().button != WebPointerProperties::Button::kLeft) ||
       (is_pen && event.Event().button != pen_drag_button)) {
-    mouse_pressed_ = false;
     mouse_down_may_start_drag_ = false;
+    return WebInputEventResult::kNotHandled;
   }
 
   //  When pressing Esc key while dragging and the object is outside of the
diff --git a/third_party/blink/renderer/core/input/pointer_event_manager.cc b/third_party/blink/renderer/core/input/pointer_event_manager.cc
index ed303b4..ee5126d 100644
--- a/third_party/blink/renderer/core/input/pointer_event_manager.cc
+++ b/third_party/blink/renderer/core/input/pointer_event_manager.cc
@@ -465,6 +465,9 @@
   WebInputEventResult result = WebInputEventResult::kHandledSystem;
   if (pointer_event_target.target_element &&
       pointer_event_target.target_frame && !non_hovering_pointers_canceled_) {
+    SetLastPointerPositionForFrameBoundary(web_pointer_event,
+                                           pointer_event_target.target_element);
+
     PointerEvent* pointer_event = pointer_event_factory_.Create(
         web_pointer_event, coalesced_events, predicted_events,
         pointer_event_target.target_element
@@ -664,21 +667,29 @@
     const Vector<WebMouseEvent>& coalesced_events,
     const Vector<WebMouseEvent>& predicted_events,
     const String& canvas_region_id) {
-  // Fetch the last_mouse_position for creating MouseEvent before
-  // pointer_event_factory updates it.
-  FloatPoint last_mouse_position =
-      pointer_event_factory_.GetLastPointerPosition(
-          PointerEventFactory::kMouseId, event);
-  WebInputEventResult result = CreateAndDispatchPointerEvent(
-      target, mouse_event_type, event, coalesced_events, predicted_events,
-      canvas_region_id);
+  if (!(event.GetModifiers() &
+        WebInputEvent::Modifiers::kRelativeMotionEvent)) {
+    // Fetch the last_mouse_position for creating MouseEvent before
+    // pointer_event_factory updates it.
+    FloatPoint last_mouse_position =
+        pointer_event_factory_.GetLastPointerPosition(
+            PointerEventFactory::kMouseId, event);
 
-  result = event_handling_util::MergeEventResult(
-      result, mouse_event_manager_->DispatchMouseEvent(
-                  target, mouse_event_type, event, canvas_region_id,
-                  &last_mouse_position, nullptr));
+    WebInputEventResult result = CreateAndDispatchPointerEvent(
+        target, mouse_event_type, event, coalesced_events, predicted_events,
+        canvas_region_id);
 
-  return result;
+    result = event_handling_util::MergeEventResult(
+        result, mouse_event_manager_->DispatchMouseEvent(
+                    target, mouse_event_type, event, canvas_region_id,
+                    &last_mouse_position, nullptr));
+    return result;
+  }
+  pointer_event_factory_.SetLastPosition(
+      pointer_event_factory_.GetPointerEventId(event),
+      event.PositionInScreen());
+
+  return WebInputEventResult::kHandledSuppressed;
 }
 
 WebInputEventResult PointerEventManager::SendMousePointerEvent(
@@ -705,7 +716,7 @@
   // pointer_event_factory updates it.
   FloatPoint last_mouse_position =
       pointer_event_factory_.GetLastPointerPosition(
-          PointerEventFactory::kMouseId, mouse_event);
+          pointer_event_factory_.GetPointerEventId(mouse_event), mouse_event);
 
   PointerEvent* pointer_event = pointer_event_factory_.Create(
       web_pointer_event, pointer_coalesced_events, pointer_predicted_events,
@@ -1043,6 +1054,29 @@
   return false;
 }
 
+void PointerEventManager::SetLastPointerPositionForFrameBoundary(
+    const WebPointerEvent& web_pointer_event,
+    Element* new_target) {
+  PointerId pointer_id =
+      pointer_event_factory_.GetPointerEventId(web_pointer_event);
+  Element* last_target = element_under_pointer_.Contains(pointer_id)
+                             ? element_under_pointer_.at(pointer_id).target
+                             : nullptr;
+  if (!new_target) {
+    pointer_event_factory_.RemoveLastPosition(pointer_id);
+  } else if (!last_target || new_target->GetDocument().GetFrame() !=
+                                 last_target->GetDocument().GetFrame()) {
+    pointer_event_factory_.SetLastPosition(
+        pointer_id, web_pointer_event.PositionInScreen());
+  }
+}
+
+void PointerEventManager::SetLastMousePositionForPointerUnlock(
+    FloatPoint mouse_lock_position_in_screen) {
+  pointer_event_factory_.SetLastPosition(PointerEventFactory::kMouseId,
+                                         mouse_lock_position_in_screen);
+}
+
 void PointerEventManager::RemoveLastMousePosition() {
   pointer_event_factory_.RemoveLastPosition(PointerEventFactory::kMouseId);
 }
diff --git a/third_party/blink/renderer/core/input/pointer_event_manager.h b/third_party/blink/renderer/core/input/pointer_event_manager.h
index 55cbda4f..09a29bf7 100644
--- a/third_party/blink/renderer/core/input/pointer_event_manager.h
+++ b/third_party/blink/renderer/core/input/pointer_event_manager.h
@@ -98,6 +98,9 @@
 
   void RemoveLastMousePosition();
 
+  void SetLastMousePositionForPointerUnlock(
+      FloatPoint mouse_lock_position_in_screen);
+
   Element* GetMouseCaptureTarget();
 
   // Sends any outstanding events. For example it notifies TouchEventManager
@@ -191,6 +194,13 @@
                           PointerEvent*);
   void SetElementUnderPointer(PointerEvent*, Element*);
 
+  // First movement after entering a new frame should be 0 as the new frame
+  // doesn't have the info for the previous events. This function sets the
+  // LastPosition to be same as current event position when target is in
+  // different frame, so that movement_x/y will be 0.
+  void SetLastPointerPositionForFrameBoundary(const WebPointerEvent& event,
+                                              Element* target);
+
   // Processes the assignment of |m_pointerCaptureTarget| from
   // |m_pendingPointerCaptureTarget| and sends the got/lostpointercapture
   // events, as per the spec:
diff --git a/third_party/blink/renderer/core/input/scroll_manager.cc b/third_party/blink/renderer/core/input/scroll_manager.cc
index fa4008d..c16ddd13 100644
--- a/third_party/blink/renderer/core/input/scroll_manager.cc
+++ b/third_party/blink/renderer/core/input/scroll_manager.cc
@@ -318,7 +318,7 @@
 
     ScrollableArea::ScrollCallback callback;
     if (RuntimeEnabledFeatures::UpdateHoverFromScrollAtBeginFrameEnabled()) {
-      callback = ScrollableArea::ScrollCallback(base::BindOnce(
+      callback = ScrollableArea::ScrollCallback(WTF::Bind(
           [](WeakPersistent<ScrollableArea> area) {
             if (area)
               area->MarkHoverStateDirty();
diff --git a/third_party/blink/renderer/core/inspector/inspector_network_agent.cc b/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
index 0efc5a8..1bffc8a 100644
--- a/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
@@ -776,7 +776,7 @@
       BuildInitiatorObject(loader && loader->GetFrame()
                                ? loader->GetFrame()->GetDocument()
                                : nullptr,
-                           initiator_info);
+                           initiator_info, std::numeric_limits<int>::max());
 
   std::unique_ptr<protocol::Network::Request> request_info(
       BuildObjectForResourceRequest(request, max_post_data_size_.Get()));
@@ -1152,7 +1152,8 @@
 std::unique_ptr<protocol::Network::Initiator>
 InspectorNetworkAgent::BuildInitiatorObject(
     Document* document,
-    const FetchInitiatorInfo& initiator_info) {
+    const FetchInitiatorInfo& initiator_info,
+    int max_async_depth) {
   if (!initiator_info.imported_module_referrer.IsEmpty()) {
     std::unique_ptr<protocol::Network::Initiator> initiator_object =
         protocol::Network::Initiator::create()
@@ -1166,7 +1167,8 @@
 
   std::unique_ptr<v8_inspector::protocol::Runtime::API::StackTrace>
       current_stack_trace =
-          SourceLocation::Capture(document)->BuildInspectorObject();
+          SourceLocation::Capture(document)->BuildInspectorObject(
+              max_async_depth);
   if (current_stack_trace) {
     std::unique_ptr<protocol::Network::Initiator> initiator_object =
         protocol::Network::Initiator::create()
@@ -1559,9 +1561,13 @@
                                                      const KURL&,
                                                      double,
                                                      ClientNavigationReason) {
+  // For navigations, we limit async stack trace to depth 1 to avoid the
+  // base::Value depth limits with Mojo serialization / parsing.
+  // See http://crbug.com/809996.
   frame_navigation_initiator_map_.Set(
       IdentifiersFactory::FrameId(frame),
-      BuildInitiatorObject(frame->GetDocument(), FetchInitiatorInfo()));
+      BuildInitiatorObject(frame->GetDocument(), FetchInitiatorInfo(),
+                           /*max_async_depth=*/1));
 }
 
 void InspectorNetworkAgent::FrameClearedScheduledNavigation(LocalFrame* frame) {
@@ -1661,7 +1667,11 @@
       frame_navigation_initiator_map_.find(IdentifiersFactory::FrameId(frame));
   if (it != frame_navigation_initiator_map_.end())
     return it->value->toJSON();
-  return BuildInitiatorObject(frame->GetDocument(), FetchInitiatorInfo())
+  // For navigations, we limit async stack trace to depth 1 to avoid the
+  // base::Value depth limits with Mojo serialization / parsing.
+  // See http://crbug.com/809996.
+  return BuildInitiatorObject(frame->GetDocument(), FetchInitiatorInfo(),
+                              /*max_async_depth=*/1)
       ->toJSON();
 }
 
diff --git a/third_party/blink/renderer/core/inspector/inspector_network_agent.h b/third_party/blink/renderer/core/inspector/inspector_network_agent.h
index 3b916fdb..86a67007 100644
--- a/third_party/blink/renderer/core/inspector/inspector_network_agent.h
+++ b/third_party/blink/renderer/core/inspector/inspector_network_agent.h
@@ -260,7 +260,8 @@
 
   static std::unique_ptr<protocol::Network::Initiator> BuildInitiatorObject(
       Document*,
-      const FetchInitiatorInfo&);
+      const FetchInitiatorInfo&,
+      int max_async_depth);
   static bool IsNavigation(DocumentLoader*, uint64_t identifier);
 
   // This is null while inspecting workers.
diff --git a/third_party/blink/renderer/core/layout/custom/layout_worklet_global_scope.cc b/third_party/blink/renderer/core/layout/custom/layout_worklet_global_scope.cc
index e78530e..1fb3468 100644
--- a/third_party/blink/renderer/core/layout/custom/layout_worklet_global_scope.cc
+++ b/third_party/blink/renderer/core/layout/custom/layout_worklet_global_scope.cc
@@ -45,14 +45,14 @@
     std::unique_ptr<GlobalScopeCreationParams> creation_params,
     WorkerReportingProxy& reporting_proxy,
     PendingLayoutRegistry* pending_layout_registry)
-    : WorkletGlobalScope(std::move(creation_params), reporting_proxy, frame),
-      pending_layout_registry_(pending_layout_registry) {
-  // Enable a separate microtask queue for LayoutWorklet.
-  //
-  // TODO(yutak): Set agent for all worklets and workers, not just
-  // LayoutWorklet.
-  SetAgent(Agent::CreateForWorkerOrWorklet(ToIsolate(frame)));
-}
+    : WorkletGlobalScope(std::move(creation_params),
+                         reporting_proxy,
+                         frame,
+                         // Enable a separate microtask queue for LayoutWorklet.
+                         // TODO(yutak): Set agent for all worklets and workers,
+                         // not just LayoutWorklet.
+                         Agent::CreateForWorkerOrWorklet(ToIsolate(frame))),
+      pending_layout_registry_(pending_layout_registry) {}
 
 LayoutWorkletGlobalScope::~LayoutWorkletGlobalScope() = default;
 
diff --git a/third_party/blink/renderer/core/layout/layout_block_flow_line.cc b/third_party/blink/renderer/core/layout/layout_block_flow_line.cc
index eee0e9cc..8b719f5 100644
--- a/third_party/blink/renderer/core/layout/layout_block_flow_line.cc
+++ b/third_party/blink/renderer/core/layout/layout_block_flow_line.cc
@@ -2697,7 +2697,7 @@
 void LayoutBlockFlow::SetShouldDoFullPaintInvalidationForFirstLine() {
   DCHECK(ChildrenInline());
   if (RootInlineBox* first_root_box = FirstRootBox())
-    first_root_box->SetShouldDoFullPaintInvalidationForFirstLine();
+    first_root_box->SetShouldDoFullPaintInvalidationRecursively();
   else if (const NGPaintFragment* paint_fragment = PaintFragment())
     paint_fragment->SetShouldDoFullPaintInvalidationForFirstLine();
 }
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc
index 04d1a46..2e9fa45 100644
--- a/third_party/blink/renderer/core/layout/layout_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -5358,13 +5358,28 @@
 
 DISABLE_CFI_PERF
 bool LayoutBox::ShouldBeConsideredAsReplaced() const {
-  // Checkboxes and radioboxes are not isAtomicInlineLevel() nor do they have
-  // their own layoutObject in which to override avoidFloats().
   if (IsAtomicInlineLevel())
     return true;
+  // We need to detect all types of objects that should be treated as replaced.
+  // Callers of this method will use the result for various things, such as
+  // determining how to size the object, or whether it needs to avoid adjacent
+  // floats, just like objects that establish a new formatting context.
+  // IsAtomicInlineLevel() will not catch all the cases. Objects may be
+  // block-level and still replaced, and we cannot deduce this from the
+  // LayoutObject type. Checkboxes and radio buttons are such examples. We need
+  // to check the Element type. This also applies to images, since we may have
+  // created a block-flow LayoutObject for the ALT text (which still counts as
+  // replaced).
   auto* element = DynamicTo<Element>(GetNode());
-  return element &&
-         (element->IsFormControlElement() || IsHTMLImageElement(element));
+  if (!element)
+    return false;
+  if (element->IsFormControlElement()) {
+    // Form control elements are generally replaced objects. Fieldsets are not,
+    // though. A fieldset is (almost) a regular block container, and should be
+    // treated as such.
+    return !IsHTMLFieldSetElement(element);
+  }
+  return IsHTMLImageElement(element);
 }
 
 bool LayoutBox::HasNonCompositedScrollbars() const {
diff --git a/third_party/blink/renderer/core/layout/layout_object.cc b/third_party/blink/renderer/core/layout/layout_object.cc
index 57e34c45..31972be 100644
--- a/third_party/blink/renderer/core/layout/layout_object.cc
+++ b/third_party/blink/renderer/core/layout/layout_object.cc
@@ -1983,18 +1983,8 @@
   DCHECK(style);
 
   StyleDifference diff;
-  if (style_) {
+  if (style_)
     diff = style_->VisualInvalidationDiff(GetDocument(), *style);
-    if (const auto* cached_inherited_first_line_style =
-            style_->GetCachedPseudoStyle(kPseudoIdFirstLineInherited)) {
-      // Merge the difference to the first line style because even if the new
-      // style is the same as the old style, the new style may have some higher
-      // priority properties overriding first line style.
-      // See external/wpt/css/css-pseudo/first-line-change-inline-color*.html.
-      diff.Merge(cached_inherited_first_line_style->VisualInvalidationDiff(
-          GetDocument(), *style));
-    }
-  }
 
   diff = AdjustStyleDifference(diff);
 
@@ -2134,8 +2124,7 @@
 void LayoutObject::UpdateFirstLineImageObservers(
     const ComputedStyle* new_style) {
   bool has_new_first_line_style =
-      new_style && new_style->HasPseudoStyle(kPseudoIdFirstLine) &&
-      BehavesLikeBlockContainer();
+      new_style && new_style->HasPseudoStyle(kPseudoIdFirstLine);
   if (!bitfields_.RegisteredAsFirstLineImageObserver() &&
       !has_new_first_line_style)
     return;
@@ -2150,21 +2139,36 @@
           ? first_line_style_map.at(this)
           : nullptr;
 
-  const auto* new_first_line_style =
-      has_new_first_line_style ? FirstLineStyleWithoutFallback() : nullptr;
+  // Don't call CacheFirstLineStyle() which will update the cache, because this
+  // function can be called when the object has not been inserted into the tree
+  // and we can't update the pseudo style cache which may depend on ancestors.
+  const auto* cached_new_first_line_style =
+      has_new_first_line_style
+          ? new_style->GetCachedPseudoStyle(kPseudoIdFirstLine)
+          : nullptr;
 
-  if (new_first_line_style && !new_first_line_style->HasBackgroundImage())
-    new_first_line_style = nullptr;
+  if (has_new_first_line_style) {
+    // If cached_new_first_line_style is null, it means that the new first line
+    // style has not been cached yet. Will check again when the object's first
+    // line style is actually used and cached.
+    bitfields_.SetPendingUpdateFirstLineImageObservers(
+        !cached_new_first_line_style);
+  }
 
-  if (old_first_line_style || new_first_line_style) {
-    UpdateFillImages(
-        old_first_line_style ? &old_first_line_style->BackgroundLayers()
-                             : nullptr,
-        new_first_line_style ? &new_first_line_style->BackgroundLayers()
-                             : nullptr);
-    if (new_first_line_style) {
+  if (cached_new_first_line_style &&
+      !cached_new_first_line_style->HasBackgroundImage())
+    cached_new_first_line_style = nullptr;
+
+  if (old_first_line_style || cached_new_first_line_style) {
+    UpdateFillImages(old_first_line_style
+                         ? &old_first_line_style->BackgroundLayers()
+                         : nullptr,
+                     cached_new_first_line_style
+                         ? &cached_new_first_line_style->BackgroundLayers()
+                         : nullptr);
+    if (cached_new_first_line_style) {
       bitfields_.SetRegisteredAsFirstLineImageObserver(true);
-      first_line_style_map.Set(this, new_first_line_style);
+      first_line_style_map.Set(this, cached_new_first_line_style);
     } else {
       bitfields_.SetRegisteredAsFirstLineImageObserver(false);
       first_line_style_map.erase(this);
@@ -2398,7 +2402,7 @@
   if (Parent() && has_old_first_line_style && has_new_first_line_style) {
     if (const auto* old_first_line_style =
             old_style->GetCachedPseudoStyle(kPseudoIdFirstLine)) {
-      if (const auto* new_first_line_style = FirstLineStyleWithoutFallback()) {
+      if (auto new_first_line_style = UncachedFirstLineStyle()) {
         diff = old_first_line_style->VisualInvalidationDiff(
             GetDocument(), *new_first_line_style);
         has_diff = true;
@@ -3365,50 +3369,75 @@
   UpdateLayout();
 }
 
-const ComputedStyle* LayoutObject::FirstLineStyleWithoutFallback() const {
-  DCHECK(GetDocument().GetStyleEngine().UsesFirstLineRules());
+enum StyleCacheState { kCached, kUncached };
 
-  if (IsBeforeOrAfterContent() || IsText()) {
-    if (!Parent())
+static scoped_refptr<const ComputedStyle> FirstLineStyleForCachedUncachedType(
+    StyleCacheState type,
+    const LayoutObject* layout_object,
+    const ComputedStyle* style) {
+  DCHECK(layout_object);
+
+  const LayoutObject* layout_object_for_first_line_style = layout_object;
+  if (layout_object->IsBeforeOrAfterContent()) {
+    if (!layout_object->Parent())
       return nullptr;
-    return Parent()->FirstLineStyleWithoutFallback();
+    layout_object_for_first_line_style = layout_object->Parent();
   }
 
-  if (BehavesLikeBlockContainer()) {
-    if (const ComputedStyle* cached =
-            StyleRef().GetCachedPseudoStyle(kPseudoIdFirstLine))
-      return cached;
-
+  if (layout_object_for_first_line_style->BehavesLikeBlockContainer()) {
     if (const LayoutBlock* first_line_block =
-            To<LayoutBlock>(this)->EnclosingFirstLineStyleBlock()) {
-      if (first_line_block->Style() == Style())
-        return first_line_block->GetCachedPseudoStyle(kPseudoIdFirstLine);
-
-      // We can't use first_line_block->GetCachedPseudoStyle() because it's
-      // based on first_line_block's style. We need to get the uncached first
-      // line style based on this object's style and cache the result in it.
-      return StyleRef().AddCachedPseudoStyle(
-          first_line_block->GetUncachedPseudoStyle(
-              PseudoStyleRequest(kPseudoIdFirstLine), Style()));
+            To<LayoutBlock>(layout_object_for_first_line_style)
+                ->EnclosingFirstLineStyleBlock()) {
+      if (type == kCached)
+        return first_line_block->GetCachedPseudoStyle(kPseudoIdFirstLine,
+                                                      style);
+      return first_line_block->GetUncachedPseudoStyle(
+          PseudoStyleRequest(kPseudoIdFirstLine), style);
     }
-  } else if (!IsAnonymous() && IsLayoutInline() &&
-             !GetNode()->IsFirstLetterPseudoElement()) {
-    if (const ComputedStyle* cached =
-            StyleRef().GetCachedPseudoStyle(kPseudoIdFirstLineInherited))
-      return cached;
-
-    if (const ComputedStyle* parent_first_line_style =
-            Parent()->FirstLineStyleWithoutFallback()) {
-      // A first-line style is in effect. Get uncached first line style based on
-      // parent_first_line_style and cache the result in this object's style.
-      return StyleRef().AddCachedPseudoStyle(GetUncachedPseudoStyle(
-          kPseudoIdFirstLineInherited, parent_first_line_style));
+  } else if (!layout_object_for_first_line_style->IsAnonymous() &&
+             layout_object_for_first_line_style->IsLayoutInline() &&
+             !layout_object_for_first_line_style->GetNode()
+                  ->IsFirstLetterPseudoElement()) {
+    const ComputedStyle* parent_style =
+        layout_object_for_first_line_style->Parent()->FirstLineStyle();
+    if (parent_style != layout_object_for_first_line_style->Parent()->Style()) {
+      if (type == kCached) {
+        // A first-line style is in effect. Cache a first-line style for
+        // ourselves.
+        return layout_object_for_first_line_style->GetCachedPseudoStyle(
+            kPseudoIdFirstLineInherited, parent_style);
+      }
+      return layout_object_for_first_line_style->GetUncachedPseudoStyle(
+          PseudoStyleRequest(kPseudoIdFirstLineInherited), parent_style);
     }
   }
   return nullptr;
 }
 
-const ComputedStyle* LayoutObject::GetCachedPseudoStyle(PseudoId pseudo) const {
+scoped_refptr<const ComputedStyle> LayoutObject::UncachedFirstLineStyle()
+    const {
+  if (!GetDocument().GetStyleEngine().UsesFirstLineRules())
+    return nullptr;
+
+  DCHECK(!IsText());
+
+  return FirstLineStyleForCachedUncachedType(kUncached, this, style_.get());
+}
+
+const ComputedStyle* LayoutObject::CachedFirstLineStyle() const {
+  DCHECK(GetDocument().GetStyleEngine().UsesFirstLineRules());
+
+  if (scoped_refptr<const ComputedStyle> style =
+          FirstLineStyleForCachedUncachedType(
+              kCached, IsText() ? Parent() : this, style_.get()))
+    return style.get();
+
+  return style_.get();
+}
+
+const ComputedStyle* LayoutObject::GetCachedPseudoStyle(
+    PseudoId pseudo,
+    const ComputedStyle* parent_style) const {
   DCHECK_NE(pseudo, kPseudoIdBefore);
   DCHECK_NE(pseudo, kPseudoIdAfter);
   if (!GetNode())
@@ -3418,7 +3447,15 @@
   if (!element)
     return nullptr;
 
-  return element->CachedStyleForPseudoElement(PseudoStyleRequest(pseudo));
+  const auto* cached_pseudo_style = element->CachedStyleForPseudoElement(
+      PseudoStyleRequest(pseudo), parent_style);
+  if (cached_pseudo_style && pseudo == kPseudoIdFirstLine &&
+      bitfields_.PendingUpdateFirstLineImageObservers()) {
+    // Update image observers now after we have updated the first line
+    // style cache.
+    const_cast<LayoutObject*>(this)->UpdateFirstLineImageObservers(Style());
+  }
+  return cached_pseudo_style;
 }
 
 scoped_refptr<ComputedStyle> LayoutObject::GetUncachedPseudoStyle(
diff --git a/third_party/blink/renderer/core/layout/layout_object.h b/third_party/blink/renderer/core/layout/layout_object.h
index 95bcef7..7a7a448 100644
--- a/third_party/blink/renderer/core/layout/layout_object.h
+++ b/third_party/blink/renderer/core/layout/layout_object.h
@@ -1137,12 +1137,12 @@
 
   bool IsRenderedLegendInternal() const;
 
-  // The pseudo element style can be cached or uncached. Use the cached method
+  // The pseudo element style can be cached or uncached.  Use the cached method
   // if the pseudo element doesn't respect any pseudo classes (and therefore
-  // has no concept of changing state). The cached pseudo style always inherits
-  // from the originating element's style (because we can cache only one
-  // version), while the uncached pseudo style can inherit from any style.
-  const ComputedStyle* GetCachedPseudoStyle(PseudoId) const;
+  // has no concept of changing state).
+  const ComputedStyle* GetCachedPseudoStyle(
+      PseudoId,
+      const ComputedStyle* parent_style = nullptr) const;
   scoped_refptr<ComputedStyle> GetUncachedPseudoStyle(
       const PseudoStyleRequest&,
       const ComputedStyle* parent_style = nullptr) const;
@@ -1735,8 +1735,6 @@
   }
 
   /* The following methods are inlined in LayoutObjectInlines.h */
-  // If first line style is requested and there is no applicable first line
-  // style, the functions will return the style of this object.
   inline const ComputedStyle* FirstLineStyle() const;
   inline const ComputedStyle& FirstLineStyleRef() const;
   inline const ComputedStyle* Style(bool first_line) const;
@@ -2640,6 +2638,10 @@
       AncestorSkipInfo* = nullptr) const;
 
  private:
+  // Used only by applyFirstLineChanges to get a first line style based off of a
+  // given new style, without accessing the cache.
+  scoped_refptr<const ComputedStyle> UncachedFirstLineStyle() const;
+
   // Adjusts a visual rect in the space of |visual_rect| to be in the space of
   // the |paint_invalidation_container|, if needed. They can be different only
   // if |paint_invalidation_container| is a composited scroller.
@@ -2685,13 +2687,7 @@
   LayoutFlowThread* LocateFlowThreadContainingBlock() const;
   void RemoveFromLayoutFlowThreadRecursive(LayoutFlowThread*);
 
-  // Returns the first line style declared in CSS. The style may be declared on
-  // an ancestor block (see EnclosingFirstLineStyleBlock()) that applies to this
-  // object. Returns nullptr if there is no applicable first line style.
-  // Whether the style applies is based on CSS rules, regardless of whether this
-  // object is really in the first line which is unknown before layout.
-  const ComputedStyle* FirstLineStyleWithoutFallback() const;
-
+  const ComputedStyle* CachedFirstLineStyle() const;
   StyleDifference AdjustStyleDifference(StyleDifference) const;
 
 #if DCHECK_IS_ON()
@@ -2831,6 +2827,7 @@
           descendant_effective_allowed_touch_action_changed_(false),
           is_effective_root_scroller_(false),
           is_global_root_scroller_(false),
+          pending_update_first_line_image_observers_(false),
           registered_as_first_line_image_observer_(false),
           is_html_legend_element_(false),
           has_non_collapsed_border_decoration_(false),
@@ -3080,6 +3077,11 @@
     ADD_BOOLEAN_BITFIELD(is_effective_root_scroller_, IsEffectiveRootScroller);
     ADD_BOOLEAN_BITFIELD(is_global_root_scroller_, IsGlobalRootScroller);
 
+    // First line style is resolvable only after the object is inserted into
+    // the tree, so we should set this flag when the object set a style having
+    // first line style before it is inserted into the tree.
+    ADD_BOOLEAN_BITFIELD(pending_update_first_line_image_observers_,
+                         PendingUpdateFirstLineImageObservers);
     // Indicates whether this object has been added as a first line image
     // observer.
     ADD_BOOLEAN_BITFIELD(registered_as_first_line_image_observer_,
diff --git a/third_party/blink/renderer/core/layout/layout_object_inlines.h b/third_party/blink/renderer/core/layout/layout_object_inlines.h
index e48a354..d5cb8e3 100644
--- a/third_party/blink/renderer/core/layout/layout_object_inlines.h
+++ b/third_party/blink/renderer/core/layout/layout_object_inlines.h
@@ -16,11 +16,9 @@
 // these methods.
 
 inline const ComputedStyle* LayoutObject::FirstLineStyle() const {
-  if (GetDocument().GetStyleEngine().UsesFirstLineRules()) {
-    if (const ComputedStyle* first_line_style = FirstLineStyleWithoutFallback())
-      return first_line_style;
-  }
-  return Style();
+  return GetDocument().GetStyleEngine().UsesFirstLineRules()
+             ? CachedFirstLineStyle()
+             : Style();
 }
 
 inline const ComputedStyle& LayoutObject::FirstLineStyleRef() const {
diff --git a/third_party/blink/renderer/core/layout/layout_object_test.cc b/third_party/blink/renderer/core/layout/layout_object_test.cc
index 4d708109..28a54ec 100644
--- a/third_party/blink/renderer/core/layout/layout_object_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_object_test.cc
@@ -1040,19 +1040,4 @@
   UpdateAllLifecyclePhasesForTest();
 }
 
-TEST_F(LayoutObjectTest, FirstLineBackgroundImageChangeStyleCrash) {
-  SetBodyInnerHTML(R"HTML(
-    <style>
-      #target::first-line {
-        background-image: url(data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==);
-      }
-    </style>
-    <div id="target">Target</div>
-  )HTML");
-  // This should not crash.
-  GetDocument().getElementById("target")->setAttribute(html_names::kStyleAttr,
-                                                       "color: blue");
-  UpdateAllLifecyclePhasesForTest();
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/layout_shift_tracker.cc b/third_party/blink/renderer/core/layout/layout_shift_tracker.cc
index f4a595efb..04efa65 100644
--- a/third_party/blink/renderer/core/layout/layout_shift_tracker.cc
+++ b/third_party/blink/renderer/core/layout/layout_shift_tracker.cc
@@ -119,7 +119,8 @@
              &LayoutShiftTracker::TimerFired),
       frame_max_distance_(0.0),
       overall_max_distance_(0.0),
-      observed_input_or_scroll_(false) {}
+      observed_input_or_scroll_(false),
+      most_recent_input_timestamp_initialized_(false) {}
 
 void LayoutShiftTracker::AccumulateJank(
     const LayoutObject& source,
@@ -291,7 +292,8 @@
   double jank_fraction = region_area / viewport_area;
   DCHECK_GT(jank_fraction, 0);
 
-  score_ += jank_fraction;
+  if (!HadRecentInput())
+    score_ += jank_fraction;
 
   DCHECK_GT(frame_max_distance_, 0.0);
   double viewport_max_dimension = std::max(viewport.Width(), viewport.Height());
@@ -302,13 +304,14 @@
   double jank_fraction_with_move_distance =
       jank_fraction * move_distance_factor;
 
-  score_with_move_distance_ += jank_fraction_with_move_distance;
+  if (!HadRecentInput())
+    score_with_move_distance_ += jank_fraction_with_move_distance;
 
   overall_max_distance_ = std::max(overall_max_distance_, frame_max_distance_);
 
   LocalFrame& frame = frame_view_->GetFrame();
 #if DCHECK_IS_ON()
-  if (ShouldLog(frame)) {
+  if (!HadRecentInput() && ShouldLog(frame)) {
     DVLOG(1) << "in " << (frame.IsMainFrame() ? "" : "subframe ")
              << frame.GetDocument()->Url().GetString() << ", viewport was "
              << (jank_fraction * 100) << "% janked; raising score to "
@@ -319,16 +322,18 @@
   TRACE_EVENT_INSTANT2(
       "loading", "LayoutShift", TRACE_EVENT_SCOPE_THREAD, "data",
       PerFrameTraceData(jank_fraction, jank_fraction_with_move_distance,
-                        granularity_scale),
+                        granularity_scale, HadRecentInput()),
       "frame", ToTraceValue(&frame));
 
-  double weighted_jank_fraction = jank_fraction * SubframeWeightingFactor();
-  if (weighted_jank_fraction > 0) {
-    weighted_score_ += weighted_jank_fraction;
-    if (RuntimeEnabledFeatures::LayoutInstabilityMoveDistanceEnabled())
-      weighted_jank_fraction *= move_distance_factor;
-    frame.Client()->DidObserveLayoutJank(weighted_jank_fraction,
-                                         observed_input_or_scroll_);
+  if (!HadRecentInput()) {
+    double weighted_jank_fraction = jank_fraction * SubframeWeightingFactor();
+    if (weighted_jank_fraction > 0) {
+      weighted_score_ += weighted_jank_fraction;
+      if (RuntimeEnabledFeatures::LayoutInstabilityMoveDistanceEnabled())
+        weighted_jank_fraction *= move_distance_factor;
+      frame.Client()->DidObserveLayoutJank(weighted_jank_fraction,
+                                           observed_input_or_scroll_);
+    }
   }
 
   if (RuntimeEnabledFeatures::LayoutInstabilityAPIEnabled(
@@ -336,23 +341,22 @@
       frame.DomWindow()) {
     WindowPerformance* performance =
         DOMWindowPerformance::performance(*frame.DomWindow());
-    if (performance &&
-        (performance->HasObserverFor(PerformanceEntry::kLayoutJank) ||
-         performance->ShouldBufferEntries())) {
+    if (performance) {
       performance->AddLayoutJankFraction(
           RuntimeEnabledFeatures::LayoutInstabilityMoveDistanceEnabled()
               ? jank_fraction_with_move_distance
-              : jank_fraction);
+              : jank_fraction,
+          HadRecentInput(), most_recent_input_timestamp_);
     }
   }
 
   if (use_sweep_line) {
-    if (!region_experimental_.IsEmpty()) {
+    if (!region_experimental_.IsEmpty() && !HadRecentInput()) {
       SetLayoutShiftRects(region_experimental_.GetRects(), 1, true);
     }
     region_experimental_.Reset();
   } else {
-    if (!region_.IsEmpty()) {
+    if (!region_.IsEmpty() && !HadRecentInput()) {
       SetLayoutShiftRects(region_.Rects(), granularity_scale, false);
     }
     region_ = Region();
@@ -378,6 +382,16 @@
 
   // This cancels any previously scheduled task from the same timer.
   timer_.StartOneShot(kTimerDelay, FROM_HERE);
+  UpdateInputTimestamp(event.TimeStamp());
+}
+
+void LayoutShiftTracker::UpdateInputTimestamp(base::TimeTicks timestamp) {
+  if (!most_recent_input_timestamp_initialized_) {
+    most_recent_input_timestamp_ = timestamp;
+    most_recent_input_timestamp_initialized_ = true;
+  } else if (timestamp > most_recent_input_timestamp_) {
+    most_recent_input_timestamp_ = timestamp;
+  }
 }
 
 void LayoutShiftTracker::NotifyScroll(ScrollType scroll_type) {
@@ -392,6 +406,11 @@
 void LayoutShiftTracker::NotifyViewportSizeChanged() {
   // This cancels any previously scheduled task from the same timer.
   timer_.StartOneShot(kTimerDelay, FROM_HERE);
+  UpdateInputTimestamp(base::TimeTicks::Now());
+}
+
+bool LayoutShiftTracker::HadRecentInput() {
+  return timer_.IsActive();
 }
 
 bool LayoutShiftTracker::IsActive() {
@@ -399,17 +418,14 @@
   // SVGImage::DataChanged.
   if (frame_view_->GetFrame().GetChromeClient().IsSVGImageChromeClient())
     return false;
-
-  if (timer_.IsActive())
-    return false;
-
   return true;
 }
 
 std::unique_ptr<TracedValue> LayoutShiftTracker::PerFrameTraceData(
     double jank_fraction,
     double jank_fraction_with_move_distance,
-    double granularity_scale) const {
+    double granularity_scale,
+    bool input_detected) const {
   auto value = std::make_unique<TracedValue>();
   value->SetDouble("score", jank_fraction);
   value->SetDouble("score_with_move_distance",
@@ -424,6 +440,7 @@
   else
     RegionToTracedValue(region_, granularity_scale, *value);
   value->SetBoolean("is_main_frame", frame_view_->GetFrame().IsMainFrame());
+  value->SetBoolean("had_recent_input", input_detected);
   return value;
 }
 
diff --git a/third_party/blink/renderer/core/layout/layout_shift_tracker.h b/third_party/blink/renderer/core/layout/layout_shift_tracker.h
index 147e6144..7138142 100644
--- a/third_party/blink/renderer/core/layout/layout_shift_tracker.h
+++ b/third_party/blink/renderer/core/layout/layout_shift_tracker.h
@@ -42,6 +42,7 @@
   void NotifyInput(const WebInputEvent&);
   void NotifyScroll(ScrollType);
   void NotifyViewportSizeChanged();
+  bool HadRecentInput();
   bool IsActive();
   double Score() const { return score_; }
   double ScoreWithMoveDistance() const { return score_with_move_distance_; }
@@ -49,6 +50,9 @@
   float OverallMaxDistance() const { return overall_max_distance_; }
   bool ObservedInputOrScroll() const { return observed_input_or_scroll_; }
   void Dispose() { timer_.Stop(); }
+  base::TimeTicks MostRecentInputTimestamp() {
+    return most_recent_input_timestamp_;
+  }
 
  private:
   void AccumulateJank(const LayoutObject&,
@@ -59,7 +63,8 @@
   std::unique_ptr<TracedValue> PerFrameTraceData(
       double jank_fraction,
       double jank_fraction_with_move_distance,
-      double granularity_scale) const;
+      double granularity_scale,
+      bool input_detected) const;
   double SubframeWeightingFactor() const;
   WebVector<gfx::Rect> ConvertIntRectsToGfxRects(
       const Vector<IntRect>& int_rects,
@@ -67,6 +72,7 @@
   void SetLayoutShiftRects(const Vector<IntRect>& int_rects,
                            double granularity_scale,
                            bool using_sweep_line);
+  void UpdateInputTimestamp(base::TimeTicks timestamp);
 
   // This owns us.
   UntracedMember<LocalFrameView> frame_view_;
@@ -105,6 +111,11 @@
 
   // Whether either a user input or document scroll have been observed.
   bool observed_input_or_scroll_;
+
+  // Most recent timestamp of a user input event that has been observed.
+  // User input includes window resizing but not scrolling.
+  base::TimeTicks most_recent_input_timestamp_;
+  bool most_recent_input_timestamp_initialized_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/layout_shift_tracker_test.cc b/third_party/blink/renderer/core/layout/layout_shift_tracker_test.cc
index 4e42d5c..269b55a 100644
--- a/third_party/blink/renderer/core/layout/layout_shift_tracker_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_shift_tracker_test.cc
@@ -150,6 +150,10 @@
   UpdateAllLifecyclePhases();
   EXPECT_EQ(0.0, GetLayoutShiftTracker().Score());
   EXPECT_TRUE(GetLayoutShiftTracker().ObservedInputOrScroll());
+  EXPECT_TRUE(GetLayoutShiftTracker()
+                  .MostRecentInputTimestamp()
+                  .since_origin()
+                  .InSecondsF() > 0.0);
 }
 
 TEST_F(LayoutShiftTrackerTest, CompositedElementMovement) {
diff --git a/third_party/blink/renderer/core/layout/line/inline_box.cc b/third_party/blink/renderer/core/layout/line/inline_box.cc
index 32e388df..ce4ee01 100644
--- a/third_party/blink/renderer/core/layout/line/inline_box.cc
+++ b/third_party/blink/renderer/core/layout/line/inline_box.cc
@@ -352,14 +352,13 @@
   return Root().Block().FlipForWritingMode(point);
 }
 
-void InlineBox::SetShouldDoFullPaintInvalidationForFirstLine() {
-  GetLineLayoutItem().StyleRef().ClearCachedPseudoStyles();
+void InlineBox::SetShouldDoFullPaintInvalidationRecursively() {
   GetLineLayoutItem().SetShouldDoFullPaintInvalidation();
   if (!IsInlineFlowBox())
     return;
   for (InlineBox* child = ToInlineFlowBox(this)->FirstChild(); child;
        child = child->NextOnLine())
-    child->SetShouldDoFullPaintInvalidationForFirstLine();
+    child->SetShouldDoFullPaintInvalidationRecursively();
 }
 
 void InlineBox::SetLineLayoutItemShouldDoFullPaintInvalidationIfNeeded() {
diff --git a/third_party/blink/renderer/core/layout/line/inline_box.h b/third_party/blink/renderer/core/layout/line/inline_box.h
index 2d5dc20..bd7a4bc 100644
--- a/third_party/blink/renderer/core/layout/line/inline_box.h
+++ b/third_party/blink/renderer/core/layout/line/inline_box.h
@@ -374,8 +374,8 @@
   }
 
   // Set all LineLayoutItems in the inline box subtree should do full paint
-  // invalidation and clear the first line style cache.
-  void SetShouldDoFullPaintInvalidationForFirstLine();
+  // invalidation.
+  void SetShouldDoFullPaintInvalidationRecursively();
 
 #define ADD_BOOLEAN_BITFIELD(field_name_, MethodNameBase)               \
  public:                                                                \
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc
index 748872f..efe866f3 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc
@@ -324,6 +324,8 @@
   // Use 'text-indent' as the initial position. This lets tab positions to align
   // regardless of 'text-indent'.
   position_ = line_info->TextIndent();
+
+  overflow_item_index_ = 0;
 }
 
 void NGLineBreaker::NextLine(
@@ -407,6 +409,11 @@
 #endif
       continue;
     }
+    if (item.Type() == NGInlineItem::kOpenTag) {
+      if (HandleOpenTag(item, line_info))
+        continue;
+      return;
+    }
     if (item.Type() == NGInlineItem::kAtomicInline) {
       if (HandleAtomicInline(item, percentage_resolution_block_size_for_min_max,
                              line_info)) {
@@ -439,9 +446,7 @@
       return;
     }
 
-    if (item.Type() == NGInlineItem::kOpenTag) {
-      HandleOpenTag(item, line_info);
-    } else if (item.Type() == NGInlineItem::kOutOfFlowPositioned) {
+    if (item.Type() == NGInlineItem::kOutOfFlowPositioned) {
       AddItem(item, line_info);
       MoveToNextOf(item);
     } else if (item.Length()) {
@@ -1337,7 +1342,8 @@
 bool NGLineBreaker::ComputeOpenTagResult(
     const NGInlineItem& item,
     const NGConstraintSpace& constraint_space,
-    NGInlineItemResult* item_result) {
+    NGInlineItemResult* item_result,
+    base::Optional<NGLineBoxStrut> margins) {
   DCHECK_EQ(item.Type(), NGInlineItem::kOpenTag);
   DCHECK(item.Style());
   const ComputedStyle& style = *item.Style();
@@ -1348,7 +1354,9 @@
     item_result->borders = ComputeLineBorders(style);
     item_result->padding = ComputeLinePadding(constraint_space, style);
     if (item_result->has_edge) {
-      item_result->margins = ComputeLineMarginsForSelf(constraint_space, style);
+      item_result->margins =
+          margins ? *margins
+                  : ComputeLineMarginsForSelf(constraint_space, style);
       item_result->inline_size = item_result->margins.inline_start +
                                  item_result->borders.inline_start +
                                  item_result->padding.inline_start;
@@ -1358,11 +1366,39 @@
   return false;
 }
 
-void NGLineBreaker::HandleOpenTag(const NGInlineItem& item,
+bool NGLineBreaker::HandleOpenTag(const NGInlineItem& item,
                                   NGLineInfo* line_info) {
+  DCHECK_EQ(item.Type(), NGInlineItem::kOpenTag);
+  DCHECK(item.Style());
+  const ComputedStyle& style = *item.Style();
+
+  // OpenTag is not trailable, except when it has negative inline-start margin,
+  // which can bring the position back to inside of the available width.
+  base::Optional<NGLineBoxStrut> margins;
+  if (UNLIKELY(state_ == LineBreakState::kTrailing &&
+               CanBreakAfterLast(line_info->Results()))) {
+    bool can_continue = false;
+    if (UNLIKELY(item_index_ >= overflow_item_index_ &&
+                 item.ShouldCreateBoxFragment() && item.HasStartEdge() &&
+                 style.MayHaveMargin())) {
+      margins = ComputeLineMarginsForSelf(constraint_space_, style);
+      LayoutUnit inline_start_margin = margins->inline_start;
+      can_continue = inline_start_margin < 0 &&
+                     position_ + inline_start_margin < AvailableWidthToFit();
+    }
+    if (!can_continue) {
+      // Not that case. Break the line before this OpenTag.
+      line_info->SetIsLastLine(false);
+      return false;
+    }
+    // The state is back to normal because the position is back to inside of the
+    // available width.
+    state_ = LineBreakState::kContinue;
+  }
+
   NGInlineItemResult* item_result = AddItem(item, line_info);
 
-  if (ComputeOpenTagResult(item, constraint_space_, item_result)) {
+  if (ComputeOpenTagResult(item, constraint_space_, item_result, margins)) {
     position_ += item_result->inline_size;
 
     // While the spec defines "non-zero margins, padding, or borders" prevents
@@ -1374,8 +1410,6 @@
   }
 
   bool was_auto_wrap = auto_wrap_;
-  DCHECK(item.Style());
-  const ComputedStyle& style = *item.Style();
   SetCurrentStyle(style);
   MoveToNextOf(item);
 
@@ -1384,6 +1418,7 @@
   if (UNLIKELY(!was_auto_wrap && auto_wrap_ && item_results.size() >= 2)) {
     ComputeCanBreakAfter(std::prev(item_result), auto_wrap_, break_iterator_);
   }
+  return true;
 }
 
 void NGLineBreaker::HandleCloseTag(const NGInlineItem& item,
@@ -1437,6 +1472,8 @@
 // At this point, item_results does not fit into the current line, and there
 // are no break opportunities in item_results.back().
 void NGLineBreaker::HandleOverflow(NGLineInfo* line_info) {
+  overflow_item_index_ = std::max(overflow_item_index_, item_index_);
+
   // Compute the width needing to rewind. When |width_to_rewind| goes negative,
   // items can fit within the line.
   LayoutUnit available_width = AvailableWidthToFit();
@@ -1529,6 +1566,7 @@
     if (!item_results->IsEmpty())
       Rewind(0, line_info);
     state_ = LineBreakState::kContinue;
+    overflow_item_index_ = 0;
     return;
   }
 
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h
index 9cfae3f..3ba1558 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h
@@ -72,9 +72,11 @@
 
   // Compute NGInlineItemResult for an open tag item.
   // Returns true if this item has edge and may have non-zero inline size.
-  static bool ComputeOpenTagResult(const NGInlineItem&,
-                                   const NGConstraintSpace&,
-                                   NGInlineItemResult*);
+  static bool ComputeOpenTagResult(
+      const NGInlineItem&,
+      const NGConstraintSpace&,
+      NGInlineItemResult*,
+      base::Optional<NGLineBoxStrut> margins = base::nullopt);
 
   // This enum is private, except for |WhitespaceStateForTesting()|. See
   // |whitespace_| member.
@@ -164,7 +166,7 @@
                    Vector<LayoutObject*>* out_floats_for_min_max,
                    NGLineInfo*);
 
-  void HandleOpenTag(const NGInlineItem&, NGLineInfo*);
+  bool HandleOpenTag(const NGInlineItem&, NGLineInfo*);
   void HandleCloseTag(const NGInlineItem&, NGLineInfo*);
 
   void HandleOverflow(NGLineInfo*);
@@ -265,6 +267,9 @@
   };
   base::Optional<TrailingCollapsibleSpace> trailing_collapsible_space_;
 
+  // Keep track of item index where overflow occurrred.
+  unsigned overflow_item_index_;
+
   // Keep track of handled float items. See HandleFloat().
   const NGPositionedFloatVector& leading_floats_;
   unsigned leading_floats_index_ = 0u;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
index 06472ca..161685c 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
@@ -1560,11 +1560,27 @@
   LayoutUnit logical_block_offset =
       previous_inflow_position.logical_block_offset;
 
-  if (child.Style().MarginBeforeCollapse() != EMarginCollapse::kCollapse) {
+  EMarginCollapse margin_before_collapse = child.Style().MarginBeforeCollapse();
+  if (margin_before_collapse != EMarginCollapse::kCollapse) {
     // Stop margin collapsing on the block-start side of the child.
     StopMarginCollapsing(child.Style().MarginBeforeCollapse(),
                          margins.block_start, &logical_block_offset,
                          &margin_strut);
+
+    if (margin_before_collapse == EMarginCollapse::kSeparate) {
+      UseCounter::Count(Node().GetDocument(),
+                        WebFeature::kWebkitMarginBeforeCollapseSeparate);
+      if (margin_strut != previous_inflow_position.margin_strut ||
+          logical_block_offset !=
+              previous_inflow_position.logical_block_offset) {
+        UseCounter::Count(
+            Node().GetDocument(),
+            WebFeature::kWebkitMarginBeforeCollapseSeparateMaybeDoesSomething);
+      }
+    } else if (margin_before_collapse == EMarginCollapse::kDiscard) {
+      UseCounter::Count(Node().GetDocument(),
+                        WebFeature::kWebkitMarginBeforeCollapseDiscard);
+    }
   } else {
     margin_strut.Append(margins.block_start,
                         child.Style().HasMarginBeforeQuirk());
@@ -1655,11 +1671,26 @@
 
   NGMarginStrut margin_strut = layout_result.EndMarginStrut();
 
-  if (child.Style().MarginAfterCollapse() != EMarginCollapse::kCollapse) {
+  EMarginCollapse margin_after_collapse = child.Style().MarginAfterCollapse();
+  if (margin_after_collapse != EMarginCollapse::kCollapse) {
+    LayoutUnit logical_block_offset_copy = logical_block_offset;
     // Stop margin collapsing on the block-end side of the child.
-    StopMarginCollapsing(child.Style().MarginAfterCollapse(),
-                         child_data.margins.block_end, &logical_block_offset,
-                         &margin_strut);
+    StopMarginCollapsing(margin_after_collapse, child_data.margins.block_end,
+                         &logical_block_offset, &margin_strut);
+
+    if (margin_after_collapse == EMarginCollapse::kSeparate) {
+      UseCounter::Count(Node().GetDocument(),
+                        WebFeature::kWebkitMarginAfterCollapseSeparate);
+      if (margin_strut != layout_result.EndMarginStrut() ||
+          logical_block_offset != logical_block_offset_copy) {
+        UseCounter::Count(
+            Node().GetDocument(),
+            WebFeature::kWebkitMarginAfterCollapseSeparateMaybeDoesSomething);
+      }
+    } else if (margin_after_collapse == EMarginCollapse::kDiscard) {
+      UseCounter::Count(Node().GetDocument(),
+                        WebFeature::kWebkitMarginAfterCollapseDiscard);
+    }
   } else {
     // Self collapsing child's end margin can "inherit" quirkiness from its
     // start margin. E.g.
diff --git a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
index 7df0c59..c47b57b 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
@@ -20,6 +20,7 @@
 #include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_node.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h"
+#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
 
 namespace blink {
@@ -441,10 +442,7 @@
          node.GetLayoutBox()->ContainingBlock()->IsTable());
 
   const ContainingBlockInfo& container_info = GetContainingBlockInfo(candidate);
-
-  const TextDirection container_direction = container_info.direction;
   const TextDirection default_direction = default_containing_block_.direction;
-
   const ComputedStyle& candidate_style = node.Style();
   const WritingMode candidate_writing_mode = candidate_style.GetWritingMode();
 
@@ -481,6 +479,49 @@
           .SetPercentageResolutionSize(container_content_size)
           .ToConstraintSpace();
 
+  base::Optional<PaintLayerScrollableArea::FreezeScrollbarsScope>
+      freeze_scrollbars;
+  do {
+    scoped_refptr<const NGLayoutResult> layout_result =
+        Layout(node, candidate_constraint_space, physical_static_position,
+               container_content_size, container_info, only_layout);
+
+    if (!freeze_scrollbars.has_value()) {
+      // Since out-of-flow positioning sets up a constraint space with fixed
+      // inline-size, the regular layout code (|NGBlockNode::Layout()|) cannot
+      // re-layout if it discovers that a scrollbar was added or removed. Handle
+      // that situation here. The assumption is that if preferred logical widths
+      // are dirty after layout, it means that scrollbars appeared or
+      // disappeared. We have the same logic in legacy layout in
+      // |LayoutBlockFlow::UpdateBlockLayout()|.
+      if (node.GetLayoutBox()->PreferredLogicalWidthsDirty()) {
+        // Freeze the scrollbars for this layout pass. We don't want them to
+        // change *again*.
+        freeze_scrollbars.emplace();
+        continue;
+      }
+    }
+
+    return layout_result;
+  } while (true);
+}
+
+scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::Layout(
+    NGBlockNode node,
+    const NGConstraintSpace& candidate_constraint_space,
+    const NGPhysicalStaticPosition& physical_static_position,
+    LogicalSize container_content_size,
+    const ContainingBlockInfo& container_info,
+    const LayoutBox* only_layout) {
+  const TextDirection default_direction = default_containing_block_.direction;
+  const ComputedStyle& candidate_style = node.Style();
+  const WritingMode candidate_writing_mode = candidate_style.GetWritingMode();
+  const TextDirection container_direction = container_info.direction;
+
+  PhysicalSize container_physical_content_size =
+      ToPhysicalSize(container_content_size, writing_mode_);
+  LogicalSize container_content_size_in_candidate_writing_mode =
+      container_physical_content_size.ConvertToLogical(candidate_writing_mode);
   NGBoxStrut border_padding =
       ComputeBorders(candidate_constraint_space, node) +
       ComputePadding(candidate_constraint_space, candidate_style);
@@ -622,6 +663,8 @@
                          block_estimate, node_position);
   }
 
+  // TODO(mstensho): Move the rest of this method back into LayoutCandidate().
+
   if (node.GetLayoutBox()->IsLayoutNGObject()) {
     To<LayoutBlock>(node.GetLayoutBox())
         ->SetIsLegacyInitiatedOutOfFlowLayout(false);
diff --git a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h
index 9f5f90e..2d66b63 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h
@@ -109,6 +109,13 @@
       const NGLogicalOutOfFlowPositionedNode&,
       const LayoutBox* only_layout);
 
+  scoped_refptr<const NGLayoutResult> Layout(NGBlockNode,
+                                             const NGConstraintSpace&,
+                                             const NGPhysicalStaticPosition&,
+                                             LogicalSize container_content_size,
+                                             const ContainingBlockInfo&,
+                                             const LayoutBox* only_layout);
+
   bool IsContainingBlockForCandidate(const NGLogicalOutOfFlowPositionedNode&);
 
   scoped_refptr<const NGLayoutResult> GenerateFragment(
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc
index 6d4d478..36f4b8b 100644
--- a/third_party/blink/renderer/core/loader/document_loader.cc
+++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -1304,11 +1304,8 @@
 
 void DocumentLoader::DidInstallNewDocument(Document* document) {
   document->SetReadyState(Document::kLoading);
-  if (content_security_policy_) {
-    document->InitContentSecurityPolicy(
-        content_security_policy_.Release(),
-        GetFrameLoader().GetLastOriginDocumentCSP());
-  }
+  if (content_security_policy_)
+    document->BindContentSecurityPolicy();
 
   if (history_item_ && IsBackForwardLoadType(load_type_))
     document->SetStateForNewFormElements(history_item_->GetDocumentState());
@@ -1480,14 +1477,26 @@
   DCHECK(!frame_->GetDocument() || !frame_->GetDocument()->IsActive());
   DCHECK_EQ(frame_->Tree().ChildCount(), 0u);
 
-  DocumentInit init = DocumentInit::Create()
-                          .WithDocumentLoader(this)
-                          .WithURL(url)
-                          .WithOwnerDocument(owner_document)
-                          .WithInitiatorOrigin(initiator_origin)
-                          .WithOriginToCommit(origin_to_commit_)
-                          .WithSrcdocDocument(loading_srcdoc_)
-                          .WithNewRegistrationContext();
+  // FeaturePolicy is reset in the browser process on commit, so this needs to
+  // be initialized and replicated to the browser process after commit messages
+  // are sent in didCommitNavigation().
+  WTF::StringBuilder feature_policy;
+  feature_policy.Append(response_.HttpHeaderField(http_names::kFeaturePolicy));
+  MergeFeaturesFromOriginPolicy(feature_policy, origin_policy_);
+
+  DocumentInit init =
+      DocumentInit::Create()
+          .WithDocumentLoader(this)
+          .WithURL(url)
+          .WithOwnerDocument(owner_document)
+          .WithInitiatorOrigin(initiator_origin)
+          .WithOriginToCommit(origin_to_commit_)
+          .WithSrcdocDocument(loading_srcdoc_)
+          .WithNewRegistrationContext()
+          .WithFeaturePolicyHeader(feature_policy.ToString())
+          .WithOriginTrialsHeader(
+              response_.HttpHeaderField(http_names::kOriginTrial))
+          .WithContentSecurityPolicy(content_security_policy_.Get());
 
   // A javascript: url inherits CSP from the document in which it was
   // executed.
@@ -1592,9 +1601,6 @@
     }
 #endif
 
-    OriginTrialContext::AddTokensFromHeader(
-        document, response_.HttpHeaderField(http_names::kOriginTrial));
-
     OriginTrialContext::ActivateNavigationFeaturesFromInitiator(
         document, &initiator_origin_trial_features_);
   }
@@ -1629,10 +1635,7 @@
   // FeaturePolicy is reset in the browser process on commit, so this needs to
   // be initialized and replicated to the browser process after commit messages
   // are sent in didCommitNavigation().
-  WTF::StringBuilder feature_policy;
-  feature_policy.Append(response_.HttpHeaderField(http_names::kFeaturePolicy));
-  MergeFeaturesFromOriginPolicy(feature_policy, origin_policy_);
-  document->ApplyFeaturePolicyFromHeader(feature_policy.ToString());
+  document->ApplyPendingFeaturePolicyHeaders();
 
   WTF::String report_only_feature_policy(
       response_.HttpHeaderField(http_names::kFeaturePolicyReportOnly));
diff --git a/third_party/blink/renderer/core/loader/document_loader.h b/third_party/blink/renderer/core/loader/document_loader.h
index be60065c..b5e42faa 100644
--- a/third_party/blink/renderer/core/loader/document_loader.h
+++ b/third_party/blink/renderer/core/loader/document_loader.h
@@ -260,13 +260,6 @@
   void BlockParser();
   void ResumeParser();
 
-  // Returns the currently stored content security policy, if this is called
-  // after the document has been installed it will return nullptr as the
-  // CSP belongs to the document at that point.
-  const ContentSecurityPolicy* GetContentSecurityPolicy() const {
-    return content_security_policy_.Get();
-  }
-
   bool IsListingFtpDirectory() const { return listing_ftp_directory_; }
 
   UseCounterHelper& GetUseCounterHelper() { return use_counter_; }
diff --git a/third_party/blink/renderer/core/page/pointer_lock_controller.cc b/third_party/blink/renderer/core/page/pointer_lock_controller.cc
index 869c348..9585b32 100644
--- a/third_party/blink/renderer/core/page/pointer_lock_controller.cc
+++ b/third_party/blink/renderer/core/page/pointer_lock_controller.cc
@@ -132,10 +132,18 @@
 }
 
 void PointerLockController::DidLosePointerLock() {
-  EnqueueEvent(
-      event_type_names::kPointerlockchange,
+  Document* pointer_lock_document =
       element_ ? &element_->GetDocument()
-               : document_of_removed_element_while_waiting_for_unlock_.Get());
+               : document_of_removed_element_while_waiting_for_unlock_.Get();
+  EnqueueEvent(event_type_names::kPointerlockchange, pointer_lock_document);
+
+  // Set the last mouse position back the locked position.
+  if (pointer_lock_document) {
+    pointer_lock_document->GetFrame()
+        ->GetEventHandler()
+        .SetMousePositionForPointerUnlock(pointer_lock_screen_position_);
+  }
+
   ClearElement();
   document_of_removed_element_while_waiting_for_unlock_ = nullptr;
 }
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 3058d24..edff13e 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
@@ -139,6 +139,9 @@
   if (RequiresCompositingForScrollTimeline(*layer))
     reasons |= CompositingReason::kScrollTimelineTarget;
 
+  if (RequiresCompositingForScrollDependentPosition(*layer))
+    reasons |= CompositingReason::kScrollDependentPosition;
+
   return reasons;
 }
 
diff --git a/third_party/blink/renderer/core/paint/image_element_timing.cc b/third_party/blink/renderer/core/paint/image_element_timing.cc
index 49f8613..abac00e2 100644
--- a/third_party/blink/renderer/core/paint/image_element_timing.cc
+++ b/third_party/blink/renderer/core/paint/image_element_timing.cc
@@ -43,10 +43,6 @@
 
 }  // namespace internal
 
-// The maximum amount of characters included in Element Timing for inline
-// images.
-constexpr const unsigned kInlineImageMaxChars = 100u;
-
 // static
 const char ImageElementTiming::kSupplementName[] = "ImageElementTiming";
 
@@ -110,6 +106,12 @@
   if (node->IsInShadowTree())
     return;
 
+  // Do not expose elements which should have effective zero opacity.
+  // We can afford to call this expensive method because this is only called
+  // once per image annotated with the elementtiming attribute.
+  if (!layout_object.HasNonZeroEffectiveOpacity())
+    return;
+
   FloatRect intersection_rect = ComputeIntersectionRect(
       frame, layout_object, current_paint_chunk_properties);
   const AtomicString attr =
@@ -129,9 +131,7 @@
           &layout_object.GetDocument())) {
     WindowPerformance* performance =
         DOMWindowPerformance::performance(*GetSupplementable());
-    if (performance &&
-        (performance->HasObserverFor(PerformanceEntry::kElement) ||
-         performance->ShouldBufferEntries())) {
+    if (performance) {
       // Create an entry with a |startTime| of 0.
       performance->AddElementTiming(
           ImagePaintString(), url.GetString(), intersection_rect,
@@ -210,8 +210,7 @@
                                                   base::TimeTicks timestamp) {
   WindowPerformance* performance =
       DOMWindowPerformance::performance(*GetSupplementable());
-  if (performance && (performance->HasObserverFor(PerformanceEntry::kElement) ||
-                      performance->ShouldBufferEntries())) {
+  if (performance) {
     for (const auto& element_timing : element_timings_) {
       performance->AddElementTiming(
           ImagePaintString(), element_timing->url, element_timing->rect,
diff --git a/third_party/blink/renderer/core/paint/image_element_timing.h b/third_party/blink/renderer/core/paint/image_element_timing.h
index 1482e47..82cc5bb 100644
--- a/third_party/blink/renderer/core/paint/image_element_timing.h
+++ b/third_party/blink/renderer/core/paint/image_element_timing.h
@@ -32,6 +32,10 @@
  public:
   static const char kSupplementName[];
 
+  // The maximum amount of characters included in Element Timing and Largest
+  // Contentful Paint for inline images.
+  static constexpr const unsigned kInlineImageMaxChars = 100;
+
   explicit ImageElementTiming(LocalDOMWindow&);
   virtual ~ImageElementTiming() = default;
 
diff --git a/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc b/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc
index fcbf385..003ad9fa 100644
--- a/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc
+++ b/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc
@@ -331,7 +331,7 @@
   if (rect_size == 0) {
     records_manager_.RecordInvisibleNode(node_id);
   } else {
-    records_manager_.RecordVisibleNode(node_id, rect_size);
+    records_manager_.RecordVisibleNode(node_id, rect_size, cached_image);
     if (is_loaded) {
       records_manager_.OnImageLoaded(node_id, frame_index_);
       need_update_timing_at_frame_end_ = true;
@@ -371,10 +371,12 @@
   QueueToMeasurePaintTime(record, current_frame_index);
 }
 
-void ImageRecordsManager::RecordVisibleNode(const DOMNodeId& node_id,
-                                            const uint64_t& visual_size) {
+void ImageRecordsManager::RecordVisibleNode(
+    const DOMNodeId& node_id,
+    const uint64_t& visual_size,
+    const ImageResourceContent& cached_image) {
   std::unique_ptr<ImageRecord> record =
-      CreateImageRecord(node_id, nullptr, visual_size);
+      CreateImageRecord(node_id, &cached_image, visual_size);
   size_ordered_set_.insert(record->AsWeakPtr());
   visible_node_map_.insert(node_id, std::move(record));
 }
diff --git a/third_party/blink/renderer/core/paint/image_paint_timing_detector.h b/third_party/blink/renderer/core/paint/image_paint_timing_detector.h
index 03f6e42..dd7cafd8 100644
--- a/third_party/blink/renderer/core/paint/image_paint_timing_detector.h
+++ b/third_party/blink/renderer/core/paint/image_paint_timing_detector.h
@@ -78,7 +78,9 @@
     DCHECK(!RecordedTooManyNodes());
     invisible_node_ids_.insert(node_id);
   }
-  void RecordVisibleNode(const DOMNodeId&, const uint64_t& visual_size);
+  void RecordVisibleNode(const DOMNodeId&,
+                         const uint64_t& visual_size,
+                         const ImageResourceContent&);
   void RecordVisibleNode(const BackgroundImageId& background_image_id,
                          const uint64_t& visual_size);
   size_t CountVisibleNodes() const { return visible_node_map_.size(); }
diff --git a/third_party/blink/renderer/core/paint/largest_contentful_paint_calculator.cc b/third_party/blink/renderer/core/paint/largest_contentful_paint_calculator.cc
index f220825..1c3c8604 100644
--- a/third_party/blink/renderer/core/paint/largest_contentful_paint_calculator.cc
+++ b/third_party/blink/renderer/core/paint/largest_contentful_paint_calculator.cc
@@ -4,6 +4,8 @@
 
 #include "third_party/blink/renderer/core/paint/largest_contentful_paint_calculator.h"
 
+#include "third_party/blink/renderer/core/paint/image_element_timing.h"
+
 namespace blink {
 
 LargestContentfulPaintCalculator::LargestContentfulPaintCalculator(
@@ -15,8 +17,10 @@
   largest_image_.reset();
   if (largest_image) {
     largest_image_ = std::make_unique<ImageRecord>();
+    largest_image_->node_id = largest_image->node_id;
     largest_image_->first_size = largest_image->first_size;
     largest_image_->paint_time = largest_image->paint_time;
+    largest_image_->cached_image = largest_image->cached_image;
   }
 
   if (LargestImageSize() > LargestTextSize()) {
@@ -55,14 +59,42 @@
   DCHECK(window_performance_);
   DCHECK(type != LargestContentType::kUnknown);
   last_type_ = type;
-  // TODO(crbug.com/965505): finish implementation, including adding more
-  // attribution and doing proper cross-origin checks for images.
   if (type == LargestContentType::kImage) {
+    const ImageResourceContent* cached_image = largest_image_->cached_image;
+    DCHECK(cached_image);
+    const KURL& url = cached_image->Url();
+    auto* document = window_performance_->GetExecutionContext();
+    if (!url.ProtocolIsData() &&
+        (!document || !Performance::PassesTimingAllowCheck(
+                          cached_image->GetResponse(),
+                          *document->GetSecurityOrigin(), document))) {
+      // Reset the paint time of this image. It cannot be exposed to the
+      // webexposed API.
+      largest_image_->paint_time = base::TimeTicks();
+    }
+    const String& image_url =
+        url.ProtocolIsData()
+            ? url.GetString().Left(ImageElementTiming::kInlineImageMaxChars)
+            : url.GetString();
+    Node* image_node = DOMNodeIds::NodeForId(largest_image_->node_id);
+    // Do not expose element attribution from shadow trees.
+    Element* image_element =
+        image_node->IsInShadowTree() ? nullptr : ToElement(image_node);
+    const AtomicString& image_id =
+        image_element ? image_element->GetIdAttribute() : AtomicString();
     window_performance_->OnLargestContentfulPaintUpdated(
-        largest_image_->paint_time, largest_image_->first_size);
+        largest_image_->paint_time, largest_image_->first_size,
+        cached_image->LoadResponseEnd(), image_id, image_url, image_element);
   } else {
+    Node* text_node = DOMNodeIds::NodeForId(largest_text_->node_id);
+    // Do not expose element attribution from shadow trees.
+    Element* text_element =
+        text_node->IsInShadowTree() ? nullptr : ToElement(text_node);
+    const AtomicString& text_id =
+        text_element ? text_element->GetIdAttribute() : AtomicString();
     window_performance_->OnLargestContentfulPaintUpdated(
-        largest_text_->paint_time, largest_text_->first_size);
+        largest_text_->paint_time, largest_text_->first_size, base::TimeTicks(),
+        text_id, g_empty_string, text_element);
   }
 }
 
diff --git a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc
index de12ac6e..6793b9b 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc
@@ -911,10 +911,9 @@
 }
 
 void NGPaintFragment::SetShouldDoFullPaintInvalidationRecursively() {
-  if (LayoutObject* layout_object = GetMutableLayoutObject()) {
-    layout_object->StyleRef().ClearCachedPseudoStyles();
+  if (LayoutObject* layout_object = GetMutableLayoutObject())
     layout_object->SetShouldDoFullPaintInvalidation();
-  }
+
   for (NGPaintFragment* child : Children())
     child->SetShouldDoFullPaintInvalidationRecursively();
 }
@@ -925,7 +924,6 @@
 
   if (NGPaintFragment* line_box = FirstLineBox()) {
     line_box->SetShouldDoFullPaintInvalidationRecursively();
-    GetLayoutObject()->StyleRef().ClearCachedPseudoStyles();
     GetMutableLayoutObject()->SetShouldDoFullPaintInvalidation();
   }
 }
diff --git a/third_party/blink/renderer/core/paint/paint_layer.cc b/third_party/blink/renderer/core/paint/paint_layer.cc
index 20ecd4ba..954f57fd 100644
--- a/third_party/blink/renderer/core/paint/paint_layer.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer.cc
@@ -2511,16 +2511,10 @@
 gfx::RRectF PaintLayer::BackdropFilterBounds(
     const FloatRect& reference_box) const {
   auto& style = GetLayoutObject().StyleRef();
-  gfx::RRectF backdrop_filter_bounds;
-  if (!style.HasBorderRadius()) {
-    backdrop_filter_bounds = gfx::RRectF(reference_box, 0);
-  } else {
-    FloatRoundedRect rrect =
-        style.GetRoundedBorderFor(LayoutRect(reference_box));
-    backdrop_filter_bounds = gfx::RRectF(rrect);
-  }
-  float zoom = style.EffectiveZoom();
-  backdrop_filter_bounds.Scale(zoom);
+  FloatRect scaled_reference_box(reference_box);
+  scaled_reference_box.Scale(style.EffectiveZoom());
+  gfx::RRectF backdrop_filter_bounds =
+      gfx::RRectF(style.GetRoundedBorderFor(LayoutRect(scaled_reference_box)));
   return backdrop_filter_bounds;
 }
 bool PaintLayer::HitTestClippedOutByClipPath(
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
index 4af7bc46..c9ffd61 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
@@ -480,6 +480,9 @@
         context_.current.should_flatten_inherited_transform;
     state.affected_by_outer_viewport_bounds_delta =
         IsAffectedByOuterViewportBoundsDelta();
+    state.direct_compositing_reasons =
+        full_context_.direct_compositing_reasons &
+        CompositingReason::kScrollDependentPosition;
     if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled() ||
         RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled())
       state.rendering_context_id = context_.current.rendering_context_id;
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
index 153a488..2770d44 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
@@ -4263,19 +4263,10 @@
   ASSERT_TRUE(multicol_container->FirstFragment().NextFragment());
   ASSERT_FALSE(
       multicol_container->FirstFragment().NextFragment()->NextFragment());
-  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
-    EXPECT_EQ(PhysicalOffset(8, 8),
-              multicol_container->FirstFragment().PaintOffset());
-    EXPECT_EQ(
-        PhysicalOffset(59, -12),
-        multicol_container->FirstFragment().NextFragment()->PaintOffset());
-  } else {
-    EXPECT_EQ(PhysicalOffset(),
-              multicol_container->FirstFragment().PaintOffset());
-    EXPECT_EQ(
-        PhysicalOffset(51, -20),
-        multicol_container->FirstFragment().NextFragment()->PaintOffset());
-  }
+  EXPECT_EQ(PhysicalOffset(),
+            multicol_container->FirstFragment().PaintOffset());
+  EXPECT_EQ(PhysicalOffset(51, -20),
+            multicol_container->FirstFragment().NextFragment()->PaintOffset());
 
   GetDocument().View()->LayoutViewport()->ScrollBy(ScrollOffset(0, 25),
                                                    kUserScroll);
@@ -4284,15 +4275,10 @@
   ASSERT_TRUE(multicol_container->FirstFragment().NextFragment());
   ASSERT_FALSE(
       multicol_container->FirstFragment().NextFragment()->NextFragment());
-
-  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
-    EXPECT_EQ(PhysicalOffset(8, 8),
-              multicol_container->FirstFragment().PaintOffset());
-    EXPECT_EQ(
-        PhysicalOffset(59, -12),
-        multicol_container->FirstFragment().NextFragment()->PaintOffset());
-  } else {
-  }
+  EXPECT_EQ(PhysicalOffset(),
+            multicol_container->FirstFragment().PaintOffset());
+  EXPECT_EQ(PhysicalOffset(51, -20),
+            multicol_container->FirstFragment().NextFragment()->PaintOffset());
 }
 
 TEST_P(PaintPropertyTreeBuilderTest, FragmentsUnderMultiColumn) {
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc b/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc
index 35b616d..11b515d0 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc
@@ -1701,4 +1701,28 @@
   EXPECT_TRUE(span->GetLayoutObject()->NeedsPaintPropertyUpdate());
 }
 
+TEST_P(PaintPropertyTreeUpdateTest, FixedPositionCompositing) {
+  SetBodyInnerHTML(R"HTML(
+    <div id="space" style="height: 200px"></div>
+    <div id="fixed" style="position: fixed; top: 50px; left: 60px">Fixed</div>
+  )HTML");
+
+  EXPECT_FALSE(PaintPropertiesForElement("fixed"));
+
+  auto* space = GetDocument().getElementById("space");
+  space->setAttribute(html_names::kStyleAttr, "height: 2000px");
+  UpdateAllLifecyclePhasesForTest();
+  auto* properties = PaintPropertiesForElement("fixed");
+  ASSERT_TRUE(properties);
+  auto* paint_offset_translation = properties->PaintOffsetTranslation();
+  ASSERT_TRUE(paint_offset_translation);
+  EXPECT_EQ(FloatSize(60, 50), paint_offset_translation->Translation2D());
+  EXPECT_TRUE(paint_offset_translation->HasDirectCompositingReasons());
+  EXPECT_FALSE(properties->Transform());
+
+  space->setAttribute(html_names::kStyleAttr, "height: 100px");
+  UpdateAllLifecyclePhasesForTest();
+  EXPECT_FALSE(PaintPropertiesForElement("fixed"));
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/scroll/scrollable_area.cc b/third_party/blink/renderer/core/scroll/scrollable_area.cc
index 08d111a4..3666288 100644
--- a/third_party/blink/renderer/core/scroll/scrollable_area.cc
+++ b/third_party/blink/renderer/core/scroll/scrollable_area.cc
@@ -31,7 +31,6 @@
 
 #include "third_party/blink/renderer/core/scroll/scrollable_area.h"
 
-#include "base/bind.h"
 #include "build/build_config.h"
 #include "cc/input/main_thread_scrolling_reason.h"
 #include "cc/input/scrollbar.h"
diff --git a/third_party/blink/renderer/core/style/computed_style.cc b/third_party/blink/renderer/core/style/computed_style.cc
index 57ffbd0e..5c171223 100644
--- a/third_party/blink/renderer/core/style/computed_style.cc
+++ b/third_party/blink/renderer/core/style/computed_style.cc
@@ -235,58 +235,34 @@
     return Difference::kEqual;
   if (!old_style || !new_style)
     return Difference::kInherited;
-
-  // For inline elements, the new computed first line style will be |new_style|
-  // inheriting from the parent's first line style. If |new_style| is different
-  // from |old_style|'s cached inherited first line style, the new computed
-  // first line style may be different from the old even if |new_style| and
-  // |old_style| equal. Especially if the difference is on inherited properties,
-  // we need to propagate the difference to descendants.
-  // See external/wpt/css/css-pseudo/first-line-change-inline-color*.html.
-  auto inherited_first_line_style_diff = Difference::kEqual;
-  if (const ComputedStyle* cached_inherited_first_line_style =
-          old_style->GetCachedPseudoStyle(kPseudoIdFirstLineInherited)) {
-    DCHECK(!new_style->GetCachedPseudoStyle(kPseudoIdFirstLineInherited));
-    inherited_first_line_style_diff =
-        ComputeDifferenceIgnoringInheritedFirstLineStyle(
-            *cached_inherited_first_line_style, *new_style);
-  }
-  return std::max(
-      inherited_first_line_style_diff,
-      ComputeDifferenceIgnoringInheritedFirstLineStyle(*old_style, *new_style));
-}
-
-ComputedStyle::Difference
-ComputedStyle::ComputeDifferenceIgnoringInheritedFirstLineStyle(
-    const ComputedStyle& old_style,
-    const ComputedStyle& new_style) {
-  DCHECK_NE(&old_style, &new_style);
-  if (old_style.Display() != new_style.Display() &&
-      (old_style.IsDisplayFlexibleOrGridBox() ||
-       old_style.IsDisplayLayoutCustomBox() ||
-       new_style.IsDisplayFlexibleOrGridBox() ||
-       new_style.IsDisplayLayoutCustomBox())) {
+  if (old_style->Display() != new_style->Display() &&
+      (old_style->IsDisplayFlexibleOrGridBox() ||
+       old_style->IsDisplayLayoutCustomBox() ||
+       old_style->Display() == EDisplay::kContents ||
+       new_style->IsDisplayFlexibleOrGridBox() ||
+       new_style->IsDisplayLayoutCustomBox() ||
+       new_style->Display() == EDisplay::kContents)) {
     return Difference::kDisplayAffectingDescendantStyles;
   }
-  if (!old_style.NonIndependentInheritedEqual(new_style))
+  if (!old_style->NonIndependentInheritedEqual(*new_style))
     return Difference::kInherited;
-  if (!old_style.LoadingCustomFontsEqual(new_style) ||
-      old_style.JustifyItems() != new_style.JustifyItems())
+  if (!old_style->LoadingCustomFontsEqual(*new_style) ||
+      old_style->JustifyItems() != new_style->JustifyItems())
     return Difference::kInherited;
-  bool non_inherited_equal = old_style.NonInheritedEqual(new_style);
-  if (!non_inherited_equal && old_style.HasExplicitlyInheritedProperties()) {
+  bool non_inherited_equal = old_style->NonInheritedEqual(*new_style);
+  if (!non_inherited_equal && old_style->HasExplicitlyInheritedProperties()) {
     return Difference::kInherited;
   }
-  if (!old_style.IndependentInheritedEqual(new_style))
+  if (!old_style->IndependentInheritedEqual(*new_style))
     return Difference::kIndependentInherited;
   if (non_inherited_equal) {
-    DCHECK(old_style == new_style);
-    if (PseudoStylesEqual(old_style, new_style))
+    DCHECK(*old_style == *new_style);
+    if (PseudoStylesEqual(*old_style, *new_style))
       return Difference::kEqual;
     return Difference::kPseudoStyle;
   }
-  if (new_style.HasAnyPublicPseudoStyles() ||
-      old_style.HasAnyPublicPseudoStyles())
+  if (new_style->HasAnyPublicPseudoStyles() ||
+      old_style->HasAnyPublicPseudoStyles())
     return Difference::kPseudoStyle;
   return Difference::kNonInherited;
 }
diff --git a/third_party/blink/renderer/core/style/computed_style.h b/third_party/blink/renderer/core/style/computed_style.h
index 5522c29..cec12c8b6 100644
--- a/third_party/blink/renderer/core/style/computed_style.h
+++ b/third_party/blink/renderer/core/style/computed_style.h
@@ -369,10 +369,6 @@
 
   const ComputedStyle* GetCachedPseudoStyle(PseudoId) const;
   const ComputedStyle* AddCachedPseudoStyle(scoped_refptr<ComputedStyle>) const;
-  void ClearCachedPseudoStyles() const {
-    if (cached_pseudo_styles_)
-      cached_pseudo_styles_->clear();
-  }
 
   /**
    * ComputedStyle properties
@@ -2682,10 +2678,6 @@
                                             Top(), Right(), Bottom(), Left());
   }
 
-  static Difference ComputeDifferenceIgnoringInheritedFirstLineStyle(
-      const ComputedStyle& old_style,
-      const ComputedStyle& new_style);
-
   FRIEND_TEST_ALL_PREFIXES(
       ComputedStyleTest,
       UpdatePropertySpecificDifferencesRespectsTransformAnimation);
diff --git a/third_party/blink/renderer/core/style/computed_style_test.cc b/third_party/blink/renderer/core/style/computed_style_test.cc
index cb14340..6c16aa4 100644
--- a/third_party/blink/renderer/core/style/computed_style_test.cc
+++ b/third_party/blink/renderer/core/style/computed_style_test.cc
@@ -9,6 +9,7 @@
 #include "third_party/blink/renderer/core/css/css_font_selector.h"
 #include "third_party/blink/renderer/core/css/css_gradient_value.h"
 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
+#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_test_helpers.h"
 #include "third_party/blink/renderer/core/css/css_value_list.h"
 #include "third_party/blink/renderer/core/css/properties/css_property_ref.h"
@@ -447,9 +448,9 @@
 
   using UnitType = CSSPrimitiveValue::UnitType;
 
-  const auto* value1 = CSSPrimitiveValue::Create(1.0, UnitType::kPixels);
-  const auto* value2 = CSSPrimitiveValue::Create(2.0, UnitType::kPixels);
-  const auto* value3 = CSSPrimitiveValue::Create(1.0, UnitType::kPixels);
+  const auto* value1 = CSSNumericLiteralValue::Create(1.0, UnitType::kPixels);
+  const auto* value2 = CSSNumericLiteralValue::Create(2.0, UnitType::kPixels);
+  const auto* value3 = CSSNumericLiteralValue::Create(1.0, UnitType::kPixels);
 
   Vector<AtomicString> properties;
   properties.push_back("--x");
diff --git a/third_party/blink/renderer/core/style/style_difference.h b/third_party/blink/renderer/core/style/style_difference.h
index 9129296..b3cf6266 100644
--- a/third_party/blink/renderer/core/style/style_difference.h
+++ b/third_party/blink/renderer/core/style/style_difference.h
@@ -44,20 +44,6 @@
         scroll_anchor_disabling_property_changed_(false),
         compositing_reasons_changed_(false) {}
 
-  void Merge(StyleDifference other) {
-    paint_invalidation_type_ =
-        std::max(paint_invalidation_type_, other.paint_invalidation_type_);
-    layout_type_ = std::max(layout_type_, other.layout_type_);
-    needs_collect_inlines_ |= other.needs_collect_inlines_;
-    needs_reshape_ |= other.needs_reshape_;
-    recompute_overflow_ |= other.recompute_overflow_;
-    visual_rect_update_ |= other.visual_rect_update_;
-    property_specific_differences_ |= other.property_specific_differences_;
-    scroll_anchor_disabling_property_changed_ |=
-        other.scroll_anchor_disabling_property_changed_;
-    compositing_reasons_changed_ |= other.compositing_reasons_changed_;
-  }
-
   bool HasDifference() const {
     return paint_invalidation_type_ || layout_type_ || needs_collect_inlines_ ||
            needs_reshape_ || property_specific_differences_ ||
diff --git a/third_party/blink/renderer/core/svg/animation/svg_smil_element.cc b/third_party/blink/renderer/core/svg/animation/svg_smil_element.cc
index 559f63fa..ea22338 100644
--- a/third_party/blink/renderer/core/svg/animation/svg_smil_element.cc
+++ b/third_party/blink/renderer/core/svg/animation/svg_smil_element.cc
@@ -761,9 +761,9 @@
 SMILTime SVGSMILElement::RepeatingDuration() const {
   // Computing the active duration
   // http://www.w3.org/TR/SMIL2/smil-timing.html#Timing-ComputingActiveDur
-  SMILTime repeat_count = this->RepeatCount();
-  SMILTime repeat_dur = this->RepeatDur();
-  SMILTime simple_duration = this->SimpleDuration();
+  SMILTime repeat_count = RepeatCount();
+  SMILTime repeat_dur = RepeatDur();
+  SMILTime simple_duration = SimpleDuration();
   if (!simple_duration ||
       (repeat_dur.IsUnresolved() && repeat_count.IsUnresolved()))
     return simple_duration;
diff --git a/third_party/blink/renderer/core/svg/svg_length.cc b/third_party/blink/renderer/core/svg/svg_length.cc
index 1329dda..6ff0ded 100644
--- a/third_party/blink/renderer/core/svg/svg_length.cc
+++ b/third_party/blink/renderer/core/svg/svg_length.cc
@@ -21,6 +21,7 @@
 
 #include "third_party/blink/renderer/core/svg/svg_length.h"
 
+#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
 #include "third_party/blink/renderer/core/css/css_value.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser.h"
@@ -63,17 +64,17 @@
   size_t initial_value_index = static_cast<size_t>(initial_value);
   DCHECK_LT(initial_value_index, base::size(g_initial_lengths_table));
   const auto& entry = g_initial_lengths_table[initial_value_index];
-  return *CSSPrimitiveValue::Create(
+  return *CSSNumericLiteralValue::Create(
       entry.value, static_cast<CSSPrimitiveValue::UnitType>(entry.unit));
 }
 
 }  // namespace
 
 SVGLength::SVGLength(SVGLengthMode mode)
-    : SVGLength(
-          *CSSPrimitiveValue::Create(0,
-                                     CSSPrimitiveValue::UnitType::kUserUnits),
-          mode) {}
+    : SVGLength(*CSSNumericLiteralValue::Create(
+                    0,
+                    CSSPrimitiveValue::UnitType::kUserUnits),
+                mode) {}
 
 SVGLength::SVGLength(Initial initial, SVGLengthMode mode)
     : SVGLength(CreateInitialCSSValue(initial), mode) {}
@@ -99,9 +100,10 @@
   auto* length = MakeGarbageCollected<SVGLength>();
   length->unit_mode_ = unit_mode_;
 
-  if (length->SetValueAsString(value) != SVGParseStatus::kNoError)
-    length->value_ =
-        CSSPrimitiveValue::Create(0, CSSPrimitiveValue::UnitType::kUserUnits);
+  if (length->SetValueAsString(value) != SVGParseStatus::kNoError) {
+    length->value_ = CSSNumericLiteralValue::Create(
+        0, CSSPrimitiveValue::UnitType::kUserUnits);
+  }
 
   return length;
 }
@@ -119,12 +121,12 @@
 }
 
 void SVGLength::SetValueAsNumber(float value) {
-  value_ =
-      CSSPrimitiveValue::Create(value, CSSPrimitiveValue::UnitType::kUserUnits);
+  value_ = CSSNumericLiteralValue::Create(
+      value, CSSPrimitiveValue::UnitType::kUserUnits);
 }
 
 void SVGLength::SetValue(float value, const SVGLengthContext& context) {
-  value_ = CSSPrimitiveValue::Create(
+  value_ = CSSNumericLiteralValue::Create(
       context.ConvertValueFromUserUnits(value, UnitMode(),
                                         value_->TypeWithCalcResolved()),
       value_->TypeWithCalcResolved());
@@ -146,7 +148,7 @@
 
 void SVGLength::SetUnitType(CSSPrimitiveValue::UnitType type) {
   DCHECK(IsSupportedCSSUnitType(type));
-  value_ = CSSPrimitiveValue::Create(value_->GetFloatValue(), type);
+  value_ = CSSNumericLiteralValue::Create(value_->GetFloatValue(), type);
 }
 
 float SVGLength::ValueAsPercentage() const {
@@ -199,8 +201,8 @@
   // string (which we'll get for example for removeAttribute.)
   // Hopefully work on crbug.com/225807 can help here.
   if (string.IsNull()) {
-    value_ =
-        CSSPrimitiveValue::Create(0, CSSPrimitiveValue::UnitType::kUserUnits);
+    value_ = CSSNumericLiteralValue::Create(
+        0, CSSPrimitiveValue::UnitType::kUserUnits);
     return SVGParseStatus::kNoError;
   }
 
@@ -223,7 +225,7 @@
 
 void SVGLength::NewValueSpecifiedUnits(CSSPrimitiveValue::UnitType type,
                                        float value) {
-  value_ = CSSPrimitiveValue::Create(value, type);
+  value_ = CSSNumericLiteralValue::Create(value, type);
 }
 
 void SVGLength::ConvertToSpecifiedUnits(CSSPrimitiveValue::UnitType type,
@@ -231,7 +233,7 @@
   DCHECK(IsSupportedCSSUnitType(type));
 
   float value_in_user_units = Value(context);
-  value_ = CSSPrimitiveValue::Create(
+  value_ = CSSNumericLiteralValue::Create(
       context.ConvertValueFromUserUnits(value_in_user_units, UnitMode(), type),
       type);
 }
@@ -329,7 +331,7 @@
   }
   animated_number = length_context.ConvertValueFromUserUnits(
       animated_number, UnitMode(), new_unit);
-  value_ = CSSPrimitiveValue::Create(animated_number, new_unit);
+  value_ = CSSNumericLiteralValue::Create(animated_number, new_unit);
 }
 
 float SVGLength::CalculateDistance(SVGPropertyBase* to_value,
diff --git a/third_party/blink/renderer/core/svg/svg_length.h b/third_party/blink/renderer/core/svg/svg_length.h
index 5e598038..f63aa8c 100644
--- a/third_party/blink/renderer/core/svg/svg_length.h
+++ b/third_party/blink/renderer/core/svg/svg_length.h
@@ -21,6 +21,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_SVG_SVG_LENGTH_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_SVG_SVG_LENGTH_H_
 
+#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
 #include "third_party/blink/renderer/core/svg/properties/svg_property.h"
 #include "third_party/blink/renderer/core/svg/svg_length_context.h"
@@ -81,7 +82,8 @@
 
   float ValueInSpecifiedUnits() const { return value_->GetFloatValue(); }
   void SetValueInSpecifiedUnits(float value) {
-    value_ = CSSPrimitiveValue::Create(value, value_->TypeWithCalcResolved());
+    value_ =
+        CSSNumericLiteralValue::Create(value, value_->TypeWithCalcResolved());
   }
 
   const CSSPrimitiveValue& AsCSSPrimitiveValue() const { return *value_; }
diff --git a/third_party/blink/renderer/core/svg/svg_transform_list.cc b/third_party/blink/renderer/core/svg/svg_transform_list.cc
index c24b8c2..c85f8ec 100644
--- a/third_party/blink/renderer/core/svg/svg_transform_list.cc
+++ b/third_party/blink/renderer/core/svg/svg_transform_list.cc
@@ -26,6 +26,7 @@
 #include "base/stl_util.h"
 #include "third_party/blink/renderer/core/css/css_function_value.h"
 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
+#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
 #include "third_party/blink/renderer/core/css/css_value_list.h"
 #include "third_party/blink/renderer/core/svg/svg_parser_utilities.h"
@@ -228,46 +229,46 @@
       MakeGarbageCollected<CSSFunctionValue>(function_id);
   switch (function_id) {
     case CSSValueID::kRotate: {
-      transform_value->Append(*CSSPrimitiveValue::Create(
+      transform_value->Append(*CSSNumericLiteralValue::Create(
           transform.Angle(), CSSPrimitiveValue::UnitType::kDegrees));
       FloatPoint rotation_origin = transform.RotationCenter();
       if (!ToFloatSize(rotation_origin).IsZero()) {
-        transform_value->Append(*CSSPrimitiveValue::Create(
+        transform_value->Append(*CSSNumericLiteralValue::Create(
             rotation_origin.X(), CSSPrimitiveValue::UnitType::kUserUnits));
-        transform_value->Append(*CSSPrimitiveValue::Create(
+        transform_value->Append(*CSSNumericLiteralValue::Create(
             rotation_origin.Y(), CSSPrimitiveValue::UnitType::kUserUnits));
       }
       break;
     }
     case CSSValueID::kSkewX:
     case CSSValueID::kSkewY:
-      transform_value->Append(*CSSPrimitiveValue::Create(
+      transform_value->Append(*CSSNumericLiteralValue::Create(
           transform.Angle(), CSSPrimitiveValue::UnitType::kDegrees));
       break;
     case CSSValueID::kMatrix:
-      transform_value->Append(*CSSPrimitiveValue::Create(
+      transform_value->Append(*CSSNumericLiteralValue::Create(
           transform.Matrix().A(), CSSPrimitiveValue::UnitType::kUserUnits));
-      transform_value->Append(*CSSPrimitiveValue::Create(
+      transform_value->Append(*CSSNumericLiteralValue::Create(
           transform.Matrix().B(), CSSPrimitiveValue::UnitType::kUserUnits));
-      transform_value->Append(*CSSPrimitiveValue::Create(
+      transform_value->Append(*CSSNumericLiteralValue::Create(
           transform.Matrix().C(), CSSPrimitiveValue::UnitType::kUserUnits));
-      transform_value->Append(*CSSPrimitiveValue::Create(
+      transform_value->Append(*CSSNumericLiteralValue::Create(
           transform.Matrix().D(), CSSPrimitiveValue::UnitType::kUserUnits));
-      transform_value->Append(*CSSPrimitiveValue::Create(
+      transform_value->Append(*CSSNumericLiteralValue::Create(
           transform.Matrix().E(), CSSPrimitiveValue::UnitType::kUserUnits));
-      transform_value->Append(*CSSPrimitiveValue::Create(
+      transform_value->Append(*CSSNumericLiteralValue::Create(
           transform.Matrix().F(), CSSPrimitiveValue::UnitType::kUserUnits));
       break;
     case CSSValueID::kScale:
-      transform_value->Append(*CSSPrimitiveValue::Create(
+      transform_value->Append(*CSSNumericLiteralValue::Create(
           transform.Matrix().A(), CSSPrimitiveValue::UnitType::kUserUnits));
-      transform_value->Append(*CSSPrimitiveValue::Create(
+      transform_value->Append(*CSSNumericLiteralValue::Create(
           transform.Matrix().D(), CSSPrimitiveValue::UnitType::kUserUnits));
       break;
     case CSSValueID::kTranslate:
-      transform_value->Append(*CSSPrimitiveValue::Create(
+      transform_value->Append(*CSSNumericLiteralValue::Create(
           transform.Matrix().E(), CSSPrimitiveValue::UnitType::kUserUnits));
-      transform_value->Append(*CSSPrimitiveValue::Create(
+      transform_value->Append(*CSSNumericLiteralValue::Create(
           transform.Matrix().F(), CSSPrimitiveValue::UnitType::kUserUnits));
       break;
     default:
diff --git a/third_party/blink/renderer/core/testing/internals.cc b/third_party/blink/renderer/core/testing/internals.cc
index f741152..da3a667 100644
--- a/third_party/blink/renderer/core/testing/internals.cc
+++ b/third_party/blink/renderer/core/testing/internals.cc
@@ -890,7 +890,7 @@
         To<LocalDOMWindow>(page->GetChromeClient().PagePopupWindowForTesting());
     if (popup) {
       // We need to make the popup same origin so web tests can access it.
-      popup->document()->UpdateSecurityOrigin(
+      popup->document()->SetSecurityOriginForTesting(
           document_->GetMutableSecurityOrigin());
     }
     return popup;
diff --git a/third_party/blink/renderer/core/testing/null_execution_context.h b/third_party/blink/renderer/core/testing/null_execution_context.h
index 9c3edf1..06a6b411 100644
--- a/third_party/blink/renderer/core/testing/null_execution_context.h
+++ b/third_party/blink/renderer/core/testing/null_execution_context.h
@@ -44,7 +44,6 @@
   bool TasksNeedPause() override { return tasks_need_pause_; }
   void SetTasksNeedPause(bool flag) { tasks_need_pause_ = flag; }
 
-  void DidUpdateSecurityOrigin() override {}
   SecurityContext& GetSecurityContext() final { return *this; }
   const SecurityContext& GetSecurityContext() const final { return *this; }
   DOMTimerCoordinator* Timers() override { return nullptr; }
diff --git a/third_party/blink/renderer/core/timing/event_timing.cc b/third_party/blink/renderer/core/timing/event_timing.cc
index 7bb1018..eda6d81 100644
--- a/third_party/blink/renderer/core/timing/event_timing.cc
+++ b/third_party/blink/renderer/core/timing/event_timing.cc
@@ -49,14 +49,8 @@
           performance->GetExecutionContext()))
     return false;
 
-  if (performance->HasObserverFor(PerformanceEntry::kEvent))
-    return true;
-  if (performance->ShouldBufferEntries() &&
-      !performance->IsEventTimingBufferFull()) {
-    return true;
-  }
-
-  return false;
+  return (!performance->IsEventTimingBufferFull() ||
+          performance->HasObserverFor(PerformanceEntry::kEvent));
 }
 
 EventTiming::EventTiming(base::TimeTicks processing_start,
diff --git a/third_party/blink/renderer/core/timing/largest_contentful_paint.cc b/third_party/blink/renderer/core/timing/largest_contentful_paint.cc
index b7b67e7..1e25325 100644
--- a/third_party/blink/renderer/core/timing/largest_contentful_paint.cc
+++ b/third_party/blink/renderer/core/timing/largest_contentful_paint.cc
@@ -10,8 +10,18 @@
 
 namespace blink {
 
-LargestContentfulPaint::LargestContentfulPaint(double paint_time, uint64_t size)
-    : PerformanceEntry(g_empty_atom, paint_time, paint_time), size_(size) {}
+LargestContentfulPaint::LargestContentfulPaint(double paint_time,
+                                               uint64_t size,
+                                               double response_end,
+                                               const AtomicString& id,
+                                               const String& url,
+                                               Element* element)
+    : PerformanceEntry(g_empty_atom, paint_time, paint_time),
+      size_(size),
+      response_end_(response_end),
+      id_(id),
+      url_(url),
+      element_(element) {}
 
 LargestContentfulPaint::~LargestContentfulPaint() = default;
 
@@ -23,12 +33,24 @@
   return PerformanceEntry::EntryType::kLargestContentfulPaint;
 }
 
+Element* LargestContentfulPaint::element() const {
+  if (!element_ || !element_->isConnected() || element_->IsInShadowTree())
+    return nullptr;
+
+  return element_;
+}
+
 void LargestContentfulPaint::BuildJSONValue(V8ObjectBuilder& builder) const {
   PerformanceEntry::BuildJSONValue(builder);
   builder.Add("size", size_);
+  builder.Add("responseEnd", response_end_);
+  builder.Add("id", id_);
+  builder.Add("url", url_);
+  builder.Add("element", element_);
 }
 
 void LargestContentfulPaint::Trace(blink::Visitor* visitor) {
+  visitor->Trace(element_);
   PerformanceEntry::Trace(visitor);
 }
 
diff --git a/third_party/blink/renderer/core/timing/largest_contentful_paint.h b/third_party/blink/renderer/core/timing/largest_contentful_paint.h
index 31ccc79..0b9ce41 100644
--- a/third_party/blink/renderer/core/timing/largest_contentful_paint.h
+++ b/third_party/blink/renderer/core/timing/largest_contentful_paint.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_TIMING_LARGEST_CONTENTFUL_PAINT_H_
 
 #include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/core/timing/performance_entry.h"
 
 namespace blink {
@@ -16,13 +17,22 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  LargestContentfulPaint(double paint_time, uint64_t size);
+  LargestContentfulPaint(double paint_time,
+                         uint64_t size,
+                         double response_end,
+                         const AtomicString& id,
+                         const String& url,
+                         Element*);
   ~LargestContentfulPaint() override;
 
   AtomicString entryType() const override;
   PerformanceEntryType EntryTypeEnum() const override;
 
   uint64_t size() const { return size_; }
+  DOMHighResTimeStamp responseEnd() const { return response_end_; }
+  const AtomicString& id() const { return id_; }
+  const String& url() const { return url_; }
+  Element* element() const;
 
   void Trace(blink::Visitor*) override;
 
@@ -30,6 +40,10 @@
   void BuildJSONValue(V8ObjectBuilder&) const override;
 
   uint64_t size_;
+  DOMHighResTimeStamp response_end_;
+  AtomicString id_;
+  String url_;
+  WeakMember<Element> element_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/timing/largest_contentful_paint.idl b/third_party/blink/renderer/core/timing/largest_contentful_paint.idl
index d4b5232c2..2c7ff70 100644
--- a/third_party/blink/renderer/core/timing/largest_contentful_paint.idl
+++ b/third_party/blink/renderer/core/timing/largest_contentful_paint.idl
@@ -6,6 +6,10 @@
 [Exposed=Window, RuntimeEnabled=LargestContentfulPaint]
 interface LargestContentfulPaint : PerformanceEntry {
     readonly attribute unsigned long long size;
+    readonly attribute DOMHighResTimeStamp responseEnd;
+    readonly attribute DOMString id;
+    readonly attribute DOMString url;
+    readonly attribute Element? element;
 
     [CallWith=ScriptState, ImplementedAs=toJSONForBinding] object toJSON();
 };
diff --git a/third_party/blink/renderer/core/timing/layout_shift.cc b/third_party/blink/renderer/core/timing/layout_shift.cc
index a842adc..b11ba21 100644
--- a/third_party/blink/renderer/core/timing/layout_shift.cc
+++ b/third_party/blink/renderer/core/timing/layout_shift.cc
@@ -10,8 +10,14 @@
 
 namespace blink {
 
-LayoutShift::LayoutShift(double start_time, double value)
-    : PerformanceEntry(g_empty_atom, start_time, start_time), value_(value) {}
+LayoutShift::LayoutShift(double start_time,
+                         double value,
+                         bool input_detected,
+                         double input_timestamp)
+    : PerformanceEntry(g_empty_atom, start_time, start_time),
+      value_(value),
+      had_recent_input_(input_detected),
+      most_recent_input_timestamp_(input_timestamp) {}
 
 LayoutShift::~LayoutShift() = default;
 
@@ -26,6 +32,8 @@
 void LayoutShift::BuildJSONValue(V8ObjectBuilder& builder) const {
   PerformanceEntry::BuildJSONValue(builder);
   builder.Add("value", value_);
+  builder.Add("hadRecentInput", had_recent_input_);
+  builder.Add("lastInputTime", most_recent_input_timestamp_);
 }
 
 void LayoutShift::Trace(blink::Visitor* visitor) {
diff --git a/third_party/blink/renderer/core/timing/layout_shift.h b/third_party/blink/renderer/core/timing/layout_shift.h
index 9652c89..d9dc5a0 100644
--- a/third_party/blink/renderer/core/timing/layout_shift.h
+++ b/third_party/blink/renderer/core/timing/layout_shift.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_TIMING_LAYOUT_SHIFT_H_
 
 #include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/dom/dom_high_res_time_stamp.h"
 #include "third_party/blink/renderer/core/timing/performance_entry.h"
 
 namespace blink {
@@ -16,13 +17,18 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  LayoutShift(double start_time, double value);
+  LayoutShift(double start_time,
+              double value,
+              bool input_detected,
+              double input_timestamp);
   ~LayoutShift() override;
 
   AtomicString entryType() const override;
   PerformanceEntryType EntryTypeEnum() const override;
 
   double value() const { return value_; }
+  bool hadRecentInput() const { return had_recent_input_; }
+  double lastInputTime() const { return most_recent_input_timestamp_; }
 
   void Trace(blink::Visitor*) override;
 
@@ -30,6 +36,8 @@
   void BuildJSONValue(V8ObjectBuilder&) const override;
 
   double value_;
+  bool had_recent_input_;
+  DOMHighResTimeStamp most_recent_input_timestamp_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/timing/layout_shift.idl b/third_party/blink/renderer/core/timing/layout_shift.idl
index 76b6c76..639c82d 100644
--- a/third_party/blink/renderer/core/timing/layout_shift.idl
+++ b/third_party/blink/renderer/core/timing/layout_shift.idl
@@ -6,6 +6,8 @@
 [Exposed=Window, RuntimeEnabled=LayoutInstabilityAPI]
 interface LayoutShift : PerformanceEntry {
     readonly attribute double value;
+    readonly attribute boolean hadRecentInput;
+    readonly attribute DOMHighResTimeStamp lastInputTime;
 
     // TODO(peria): toJSON is not in spec. https://crbug.com/736332
     [CallWith=ScriptState, ImplementedAs=toJSONForBinding] object toJSON();
diff --git a/third_party/blink/renderer/core/timing/performance.cc b/third_party/blink/renderer/core/timing/performance.cc
index 7807f85..3d6ee0a0 100644
--- a/third_party/blink/renderer/core/timing/performance.cc
+++ b/third_party/blink/renderer/core/timing/performance.cc
@@ -564,18 +564,16 @@
 }
 
 void Performance::AddElementTimingBuffer(PerformanceElementTiming& entry) {
-  element_timing_buffer_.push_back(&entry);
-
-  if (IsElementTimingBufferFull())
-    DispatchEvent(*Event::Create(event_type_names::kElementtimingbufferfull));
+  if (!IsElementTimingBufferFull()) {
+    element_timing_buffer_.push_back(&entry);
+  }
 }
 
 void Performance::AddEventTimingBuffer(PerformanceEventTiming& entry) {
   DCHECK(RuntimeEnabledFeatures::EventTimingEnabled(GetExecutionContext()));
-  event_timing_buffer_.push_back(&entry);
-
-  if (IsEventTimingBufferFull())
-    DispatchEvent(*Event::Create(event_type_names::kEventtimingbufferfull));
+  if (!IsEventTimingBufferFull()) {
+    event_timing_buffer_.push_back(&entry);
+  }
 }
 
 void Performance::AddLayoutJankBuffer(LayoutShift& entry) {
@@ -590,37 +588,6 @@
   }
 }
 
-unsigned Performance::ElementTimingBufferSize() const {
-  return element_timing_buffer_.size();
-}
-
-unsigned Performance::EventTimingBufferSize() const {
-  return event_timing_buffer_.size();
-}
-
-void Performance::clearElementTimings() {
-  element_timing_buffer_.clear();
-}
-
-void Performance::clearEventTimings() {
-  event_timing_buffer_.clear();
-}
-
-// TODO(crbug.com/72556): remove Element Timing buffering when shipping the
-// 'buffered' flag.
-void Performance::setElementTimingBufferMaxSize(unsigned size) {
-  element_timing_buffer_max_size_ = size;
-  if (IsElementTimingBufferFull())
-    DispatchEvent(*Event::Create(event_type_names::kElementtimingbufferfull));
-}
-
-// TODO(yoav): EventTiming should follow a simpler buffering model.
-void Performance::setEventTimingBufferMaxSize(unsigned size) {
-  event_timing_buffer_max_size_ = size;
-  if (IsEventTimingBufferFull())
-    DispatchEvent(*Event::Create(event_type_names::kEventtimingbufferfull));
-}
-
 void Performance::AddFirstPaintTiming(base::TimeTicks start_time) {
   AddPaintTiming(PerformancePaintTiming::PaintType::kFirstPaint, start_time);
 }
diff --git a/third_party/blink/renderer/core/timing/performance.h b/third_party/blink/renderer/core/timing/performance.h
index c0d6a4e7..f2544bf 100644
--- a/third_party/blink/renderer/core/timing/performance.h
+++ b/third_party/blink/renderer/core/timing/performance.h
@@ -175,18 +175,9 @@
 
   bool IsElementTimingBufferFull() const;
   void AddElementTimingBuffer(PerformanceElementTiming&);
-  unsigned ElementTimingBufferSize() const;
-  void clearElementTimings();
-  void setElementTimingBufferMaxSize(unsigned);
-  DEFINE_ATTRIBUTE_EVENT_LISTENER(elementtimingbufferfull,
-                                  kElementtimingbufferfull)
 
   bool IsEventTimingBufferFull() const;
   void AddEventTimingBuffer(PerformanceEventTiming&);
-  unsigned EventTimingBufferSize() const;
-  void clearEventTimings();
-  void setEventTimingBufferMaxSize(unsigned);
-  DEFINE_ATTRIBUTE_EVENT_LISTENER(eventtimingbufferfull, kEventtimingbufferfull)
 
   void AddLayoutJankBuffer(LayoutShift&);
 
diff --git a/third_party/blink/renderer/core/timing/performance.idl b/third_party/blink/renderer/core/timing/performance.idl
index 4e31532..b95d0b1 100644
--- a/third_party/blink/renderer/core/timing/performance.idl
+++ b/third_party/blink/renderer/core/timing/performance.idl
@@ -51,19 +51,6 @@
     void setResourceTimingBufferSize(unsigned long maxSize);
     attribute EventHandler onresourcetimingbufferfull;
 
-    // Element Timing
-    // https://github.com/npm1/Element-Timing
-    [MeasureAs=ElementTimingExplicitlyRequested, RuntimeEnabled=ElementTiming] void clearElementTimings();
-    [MeasureAs=ElementTimingExplicitlyRequested, RuntimeEnabled=ElementTiming] void setElementTimingBufferMaxSize(unsigned long maxSize);
-    [MeasureAs=ElementTimingExplicitlyRequested, RuntimeEnabled=ElementTiming] attribute EventHandler onelementtimingbufferfull;
-
-
-    // Event Timing
-    // https://github.com/wicg/event-timing
-    [MeasureAs=PerformanceEventTimingBuffer, RuntimeEnabled=EventTiming] void clearEventTimings();
-    [MeasureAs=PerformanceEventTimingBuffer, RuntimeEnabled=EventTiming] void setEventTimingBufferMaxSize(unsigned long maxSize);
-    [MeasureAs=PerformanceEventTimingBuffer, RuntimeEnabled=EventTiming] attribute EventHandler oneventtimingbufferfull;
-
     // Navigation Timing
     // https://w3c.github.io/navigation-timing/#extensions-to-the-performance-interface
     [Exposed=Window, SameObject, Measure] readonly attribute PerformanceTiming timing;
diff --git a/third_party/blink/renderer/core/timing/performance_observer_test.cc b/third_party/blink/renderer/core/timing/performance_observer_test.cc
index 3ce2de72..62094a6a 100644
--- a/third_party/blink/renderer/core/timing/performance_observer_test.cc
+++ b/third_party/blink/renderer/core/timing/performance_observer_test.cc
@@ -74,7 +74,7 @@
   EXPECT_EQ(0, NumPerformanceEntries());
 
   // add a layoutjank to performance so getEntries() returns it
-  auto* entry = MakeGarbageCollected<LayoutShift>(0.0, 1234);
+  auto* entry = MakeGarbageCollected<LayoutShift>(0.0, 1234, true, 5678);
   base_->AddLayoutJankBuffer(*entry);
 
   // call observe with the buffered flag
diff --git a/third_party/blink/renderer/core/timing/window_performance.cc b/third_party/blink/renderer/core/timing/window_performance.cc
index 8b8ffa36..b8fe4cfc 100644
--- a/third_party/blink/renderer/core/timing/window_performance.cc
+++ b/third_party/blink/renderer/core/timing/window_performance.cc
@@ -322,12 +322,6 @@
   }
 }
 
-// We buffer Element Timing and Event Timing (long-latency events) entries until
-// onload, i.e., LoadEventStart is not reached yet.
-bool WindowPerformance::ShouldBufferEntries() {
-  return !timing() || !timing()->loadEventStart();
-}
-
 void WindowPerformance::RegisterEventTiming(const AtomicString& event_type,
                                             base::TimeTicks start_time,
                                             base::TimeTicks processing_start,
@@ -387,7 +381,7 @@
       NotifyObserversOfEntry(*entry);
     }
 
-    if (ShouldBufferEntries() && !IsEventTimingBufferFull())
+    if (!IsEventTimingBufferFull())
       AddEventTimingBuffer(*entry);
   }
   event_timings_.clear();
@@ -412,7 +406,7 @@
                       WebFeature::kElementTimingExplicitlyRequested);
     NotifyObserversOfEntry(*entry);
   }
-  if (ShouldBufferEntries() && !IsElementTimingBufferFull())
+  if (!IsElementTimingBufferFull())
     AddElementTimingBuffer(*entry);
 }
 
@@ -431,21 +425,30 @@
   first_input_timing_ = entry;
 }
 
-void WindowPerformance::AddLayoutJankFraction(double jank_fraction) {
+void WindowPerformance::AddLayoutJankFraction(double jank_fraction,
+                                              bool input_detected,
+                                              base::TimeTicks input_timestamp) {
   DCHECK(RuntimeEnabledFeatures::LayoutInstabilityAPIEnabled(
       GetExecutionContext()));
-  auto* entry = MakeGarbageCollected<LayoutShift>(now(), jank_fraction);
+  auto* entry = MakeGarbageCollected<LayoutShift>(
+      now(), jank_fraction, input_detected,
+      input_detected ? MonotonicTimeToDOMHighResTimeStamp(input_timestamp)
+                     : 0.0);
   if (HasObserverFor(PerformanceEntry::kLayoutJank))
     NotifyObserversOfEntry(*entry);
-  if (ShouldBufferEntries())
-    AddLayoutJankBuffer(*entry);
+  AddLayoutJankBuffer(*entry);
 }
 
 void WindowPerformance::OnLargestContentfulPaintUpdated(
     base::TimeTicks paint_time,
-    uint64_t paint_size) {
+    uint64_t paint_size,
+    base::TimeTicks response_end,
+    const AtomicString& id,
+    const String& url,
+    Element* element) {
   auto* entry = MakeGarbageCollected<LargestContentfulPaint>(
-      MonotonicTimeToDOMHighResTimeStamp(paint_time), paint_size);
+      MonotonicTimeToDOMHighResTimeStamp(paint_time), paint_size,
+      MonotonicTimeToDOMHighResTimeStamp(response_end), id, url, element);
   if (HasObserverFor(PerformanceEntry::kLargestContentfulPaint))
     NotifyObserversOfEntry(*entry);
   AddLargestContentfulPaint(entry);
diff --git a/third_party/blink/renderer/core/timing/window_performance.h b/third_party/blink/renderer/core/timing/window_performance.h
index 56898d4..383c55c 100644
--- a/third_party/blink/renderer/core/timing/window_performance.h
+++ b/third_party/blink/renderer/core/timing/window_performance.h
@@ -63,8 +63,6 @@
 
   void UpdateLongTaskInstrumentation() override;
 
-  bool ShouldBufferEntries();
-
   bool FirstInputDetected() const { return !!first_input_timing_; }
 
   // This method creates a PerformanceEventTiming and if needed creates a swap
@@ -86,10 +84,16 @@
                         const AtomicString& id,
                         Element*);
 
-  void AddLayoutJankFraction(double jank_fraction);
+  void AddLayoutJankFraction(double jank_fraction,
+                             bool input_detected,
+                             base::TimeTicks input_timestamp);
 
-  void OnLargestContentfulPaintUpdated(base::TimeTicks text_paint_time,
-                                       uint64_t text_paint_size);
+  void OnLargestContentfulPaintUpdated(base::TimeTicks paint_time,
+                                       uint64_t paint_size,
+                                       base::TimeTicks response_end,
+                                       const AtomicString& id,
+                                       const String& url,
+                                       Element*);
 
   void Trace(blink::Visitor*) override;
 
diff --git a/third_party/blink/renderer/core/timing/window_performance_test.cc b/third_party/blink/renderer/core/timing/window_performance_test.cc
index ba7e86d..5f6cb7be 100644
--- a/third_party/blink/renderer/core/timing/window_performance_test.cc
+++ b/third_party/blink/renderer/core/timing/window_performance_test.cc
@@ -225,7 +225,7 @@
   }
 }
 
-TEST_F(WindowPerformanceTest, EventTimingBeforeOnLoad) {
+TEST_F(WindowPerformanceTest, EventTimingEntryBuffering) {
   ScopedEventTimingForTest event_timing(true);
   EXPECT_TRUE(page_holder_->GetFrame().Loader().GetDocumentLoader());
 
@@ -241,7 +241,6 @@
       GetTimeOrigin() + base::TimeDelta::FromSecondsD(6.0);
   SimulateSwapPromise(swap_time);
   EXPECT_EQ(1u, performance_->getEntriesByName("click", "event").size());
-  performance_->clearEventTimings();
 
   page_holder_->GetFrame()
       .Loader()
@@ -251,8 +250,7 @@
   performance_->RegisterEventTiming("click", start_time, processing_start,
                                     processing_end, true);
   SimulateSwapPromise(swap_time);
-  EXPECT_EQ(0u, performance_->getEntriesByName("click", "event").size());
-  performance_->clearEventTimings();
+  EXPECT_EQ(2u, performance_->getEntriesByName("click", "event").size());
 
   EXPECT_TRUE(page_holder_->GetFrame().Loader().GetDocumentLoader());
   GetFrame()->PrepareForCommit();
@@ -260,8 +258,7 @@
   performance_->RegisterEventTiming("click", start_time, processing_start,
                                     processing_end, false);
   SimulateSwapPromise(swap_time);
-  EXPECT_EQ(1u, performance_->getEntriesByName("click", "event").size());
-  performance_->clearEventTimings();
+  EXPECT_EQ(3u, performance_->getEntriesByName("click", "event").size());
 }
 
 TEST_F(WindowPerformanceTest, Expose100MsEvents) {
diff --git a/third_party/blink/renderer/core/trustedtypes/trusted_types_util.cc b/third_party/blink/renderer/core/trustedtypes/trusted_types_util.cc
index 695d0dd2..499b062 100644
--- a/third_party/blink/renderer/core/trustedtypes/trusted_types_util.cc
+++ b/third_party/blink/renderer/core/trustedtypes/trusted_types_util.cc
@@ -110,17 +110,17 @@
   return !allow;
 }
 
-bool RequireTrustedTypes(const ExecutionContext* execution_context) {
-  return execution_context &&
-         execution_context->GetSecurityContext().RequireTrustedTypes();
-}
-
 TrustedTypePolicy* GetDefaultPolicy(const ExecutionContext* execution_context) {
   return execution_context->GetTrustedTypes()->getExposedPolicy("default");
 }
 
 }  // namespace
 
+bool RequireTrustedTypesCheck(const ExecutionContext* execution_context) {
+  return execution_context &&
+         execution_context->GetSecurityContext().RequireTrustedTypes();
+}
+
 String GetStringFromTrustedType(
     const StringOrTrustedHTMLOrTrustedScriptOrTrustedScriptURLOrTrustedURL&
         string_or_trusted_type,
@@ -129,7 +129,7 @@
   DCHECK(!string_or_trusted_type.IsNull());
 
   if (string_or_trusted_type.IsString() &&
-      RequireTrustedTypes(execution_context)) {
+      RequireTrustedTypesCheck(execution_context)) {
     TrustedTypeFail(kAnyTrustedTypeAssignment, execution_context,
                     exception_state);
     return g_empty_string;
@@ -236,7 +236,7 @@
 String GetStringFromTrustedHTML(const String& string,
                                 const ExecutionContext* execution_context,
                                 ExceptionState& exception_state) {
-  bool require_trusted_type = RequireTrustedTypes(execution_context);
+  bool require_trusted_type = RequireTrustedTypesCheck(execution_context);
   if (!require_trusted_type) {
     return string;
   }
@@ -272,7 +272,7 @@
   // string_or_trusted_script.IsNull(), unlike the various similar methods in
   // this file.
 
-  bool require_trusted_type = RequireTrustedTypes(execution_context);
+  bool require_trusted_type = RequireTrustedTypesCheck(execution_context);
   if (!require_trusted_type) {
     if (string_or_trusted_script.IsString()) {
       return string_or_trusted_script.GetAsString();
@@ -325,7 +325,7 @@
   DCHECK(!string_or_trusted_script_url.IsNull());
 
   bool require_trusted_type =
-      RequireTrustedTypes(execution_context) &&
+      RequireTrustedTypesCheck(execution_context) &&
       RuntimeEnabledFeatures::TrustedDOMTypesEnabled(execution_context);
   if (!require_trusted_type && string_or_trusted_script_url.IsString()) {
     return string_or_trusted_script_url.GetAsString();
@@ -363,7 +363,7 @@
                                ExceptionState& exception_state) {
   DCHECK(!string_or_trusted_url.IsNull());
 
-  bool require_trusted_type = RequireTrustedTypes(execution_context);
+  bool require_trusted_type = RequireTrustedTypesCheck(execution_context);
   if (!require_trusted_type && string_or_trusted_url.IsUSVString()) {
     return string_or_trusted_url.GetAsUSVString();
   }
@@ -397,7 +397,7 @@
 Node* TrustedTypesCheckForHTMLScriptElement(Node* child,
                                             Document* doc,
                                             ExceptionState& exception_state) {
-  bool require_trusted_type = RequireTrustedTypes(doc);
+  bool require_trusted_type = RequireTrustedTypesCheck(doc);
   if (!require_trusted_type)
     return child;
 
diff --git a/third_party/blink/renderer/core/trustedtypes/trusted_types_util.h b/third_party/blink/renderer/core/trustedtypes/trusted_types_util.h
index 31c9f6ce..d28a8ec 100644
--- a/third_party/blink/renderer/core/trustedtypes/trusted_types_util.h
+++ b/third_party/blink/renderer/core/trustedtypes/trusted_types_util.h
@@ -72,6 +72,15 @@
                                             Document*,
                                             ExceptionState&);
 
+// Determine whether a Trusted Types check is needed in this execution context.
+//
+// Note: All methods above handle this internally and will return success if a
+// check is not required. However, in cases where not-required doesn't
+// immediately imply "okay" this method can be used.
+// Example: To determine whether 'eval' may pass, one needs to also take CSP
+// into account.
+bool CORE_EXPORT RequireTrustedTypesCheck(const ExecutionContext*);
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_TRUSTEDTYPES_TRUSTED_TYPES_UTIL_H_
diff --git a/third_party/blink/renderer/core/workers/worker_global_scope.cc b/third_party/blink/renderer/core/workers/worker_global_scope.cc
index bb7d06a..86d6f330 100644
--- a/third_party/blink/renderer/core/workers/worker_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/worker_global_scope.cc
@@ -84,6 +84,17 @@
   GetMemoryCache()->RemoveURLFromCache(url);
 }
 
+scoped_refptr<SecurityOrigin> CreateSecurityOrigin(
+    GlobalScopeCreationParams* creation_params) {
+  scoped_refptr<SecurityOrigin> security_origin =
+      SecurityOrigin::Create(creation_params->script_url);
+  if (creation_params->starter_origin) {
+    security_origin->TransferPrivilegesFrom(
+        creation_params->starter_origin->CreatePrivilegeData());
+  }
+  return security_origin;
+}
+
 }  // namespace
 
 FontFaceSet* WorkerGlobalScope::fonts() {
@@ -454,6 +465,7 @@
     base::TimeTicks time_origin)
     : WorkerOrWorkletGlobalScope(
           thread->GetIsolate(),
+          CreateSecurityOrigin(creation_params.get()),
           Agent::CreateForWorkerOrWorklet(thread->GetIsolate()),
           creation_params->off_main_thread_fetch_option,
           creation_params->global_scope_name,
@@ -478,13 +490,6 @@
       script_eval_state_(ScriptEvalState::kPauseAfterFetch) {
   InstanceCounters::IncrementCounter(
       InstanceCounters::kWorkerGlobalScopeCounter);
-  scoped_refptr<SecurityOrigin> security_origin =
-      SecurityOrigin::Create(creation_params->script_url);
-  if (creation_params->starter_origin) {
-    security_origin->TransferPrivilegesFrom(
-        creation_params->starter_origin->CreatePrivilegeData());
-  }
-  SetSecurityOrigin(std::move(security_origin));
 
   // https://html.spec.whatwg.org/C/#run-a-worker
   // 4. Set worker global scope's HTTPS state to response's HTTPS state. [spec
diff --git a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc
index 2c0c166..cdd93b5 100644
--- a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc
@@ -168,6 +168,7 @@
 
 WorkerOrWorkletGlobalScope::WorkerOrWorkletGlobalScope(
     v8::Isolate* isolate,
+    scoped_refptr<SecurityOrigin> origin,
     Agent* agent,
     OffMainThreadWorkerScriptFetchOption off_main_thread_fetch_option,
     const String& name,
@@ -179,6 +180,7 @@
     : ExecutionContext(isolate,
                        agent,
                        MakeGarbageCollected<OriginTrialContext>()),
+      SecurityContext(std::move(origin), WebSandboxFlags::kNone, nullptr),
       off_main_thread_fetch_option_(off_main_thread_fetch_option),
       name_(name),
       parent_devtools_token_(parent_devtools_token),
@@ -397,6 +399,14 @@
   return GetThread()->GetTaskRunner(type);
 }
 
+void WorkerOrWorkletGlobalScope::ApplySandboxFlags(SandboxFlags mask) {
+  sandbox_flags_ |= mask;
+  if (IsSandboxed(WebSandboxFlags::kOrigin) &&
+      !GetSecurityOrigin()->IsOpaque()) {
+    SetSecurityOrigin(GetSecurityOrigin()->DeriveNewOpaqueOrigin());
+  }
+}
+
 void WorkerOrWorkletGlobalScope::SetOutsideContentSecurityPolicyHeaders(
     const Vector<CSPHeaderAndType>& headers) {
   outside_content_security_policy_headers_ = headers;
diff --git a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h
index 2184df6..c1dbaf8e 100644
--- a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h
+++ b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h
@@ -46,6 +46,7 @@
 
   WorkerOrWorkletGlobalScope(
       v8::Isolate*,
+      scoped_refptr<SecurityOrigin> origin,
       Agent* agent,
       OffMainThreadWorkerScriptFetchOption,
       const String& name,
@@ -74,9 +75,6 @@
   void DisableEval(const String& error_message) final;
   bool CanExecuteScripts(ReasonForCallingCanExecuteScripts) final;
 
-  // SecurityContext
-  void DidUpdateSecurityOrigin() final {}
-
   // Returns true when the WorkerOrWorkletGlobalScope is closing (e.g. via
   // WorkerGlobalScope#close() method). If this returns true, the worker is
   // going to be shutdown after the current task execution. Globals that
@@ -143,6 +141,8 @@
     return off_main_thread_fetch_option_;
   }
 
+  void ApplySandboxFlags(SandboxFlags mask);
+
  protected:
   // Sets outside's CSP used for off-main-thread top-level worker script
   // fetch.
diff --git a/third_party/blink/renderer/core/workers/worklet_global_scope.cc b/third_party/blink/renderer/core/workers/worklet_global_scope.cc
index 0418a301..85633e9 100644
--- a/third_party/blink/renderer/core/workers/worklet_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/worklet_global_scope.cc
@@ -31,13 +31,15 @@
 WorkletGlobalScope::WorkletGlobalScope(
     std::unique_ptr<GlobalScopeCreationParams> creation_params,
     WorkerReportingProxy& reporting_proxy,
-    LocalFrame* frame)
+    LocalFrame* frame,
+    Agent* agent)
     : WorkletGlobalScope(std::move(creation_params),
                          reporting_proxy,
                          ToIsolate(frame),
                          ThreadType::kMainThread,
                          frame,
-                         nullptr /* worker_thread */) {}
+                         nullptr /* worker_thread */,
+                         agent) {}
 
 WorkletGlobalScope::WorkletGlobalScope(
     std::unique_ptr<GlobalScopeCreationParams> creation_params,
@@ -48,7 +50,8 @@
                          worker_thread->GetIsolate(),
                          ThreadType::kOffMainThread,
                          nullptr /* frame */,
-                         worker_thread) {}
+                         worker_thread,
+                         nullptr /* agent */) {}
 
 // Partial implementation of the "set up a worklet environment settings object"
 // algorithm:
@@ -59,12 +62,14 @@
     v8::Isolate* isolate,
     ThreadType thread_type,
     LocalFrame* frame,
-    WorkerThread* worker_thread)
+    WorkerThread* worker_thread,
+    Agent* agent)
     : WorkerOrWorkletGlobalScope(
           isolate,
+          SecurityOrigin::CreateUniqueOpaque(),
           // TODO(tzik): Assign an Agent for Worklets after
           // NonMainThreadScheduler gets ready to run microtasks.
-          nullptr,
+          agent,
           creation_params->off_main_thread_fetch_option,
           creation_params->global_scope_name,
           creation_params->parent_devtools_token,
@@ -91,9 +96,6 @@
   // Step 2: "Let inheritedAPIBaseURL be outsideSettings's API base URL."
   // |url_| is the inheritedAPIBaseURL passed from the parent Document.
 
-  // Step 3: "Let origin be a unique opaque origin."
-  SetSecurityOrigin(SecurityOrigin::CreateUniqueOpaque());
-
   // Step 5: "Let inheritedReferrerPolicy be outsideSettings's referrer policy."
   SetReferrerPolicy(creation_params->referrer_policy);
 
diff --git a/third_party/blink/renderer/core/workers/worklet_global_scope.h b/third_party/blink/renderer/core/workers/worklet_global_scope.h
index 3d267c4..bfde821 100644
--- a/third_party/blink/renderer/core/workers/worklet_global_scope.h
+++ b/third_party/blink/renderer/core/workers/worklet_global_scope.h
@@ -129,7 +129,8 @@
   // thread.
   WorkletGlobalScope(std::unique_ptr<GlobalScopeCreationParams>,
                      WorkerReportingProxy&,
-                     LocalFrame*);
+                     LocalFrame*,
+                     Agent* = nullptr);
   // Constructs an instance as a threaded worklet. Must be called on a worker
   // thread.
   WorkletGlobalScope(std::unique_ptr<GlobalScopeCreationParams>,
@@ -153,7 +154,8 @@
                      v8::Isolate*,
                      ThreadType,
                      LocalFrame*,
-                     WorkerThread*);
+                     WorkerThread*,
+                     Agent*);
 
   EventTarget* ErrorEventTarget() final { return nullptr; }
 
diff --git a/third_party/blink/renderer/core/xml/dom_parser.cc b/third_party/blink/renderer/core/xml/dom_parser.cc
index c7973483..996234e 100644
--- a/third_party/blink/renderer/core/xml/dom_parser.cc
+++ b/third_party/blink/renderer/core/xml/dom_parser.cc
@@ -43,13 +43,15 @@
 Document* DOMParser::parseFromStringInternal(const String& str,
                                              const String& type) {
   Document* doc = DOMImplementation::createDocument(
-      type, DocumentInit::Create().WithContextDocument(context_document_),
+      type,
+      DocumentInit::Create()
+          .WithContextDocument(context_document_)
+          .WithOwnerDocument(context_document_),
       false);
   doc->SetContent(str);
   doc->SetMimeType(AtomicString(type));
   if (context_document_) {
     doc->SetURL(context_document_->Url());
-    doc->SetSecurityOrigin(context_document_->GetMutableSecurityOrigin());
   }
   return doc;
 }
diff --git a/third_party/blink/renderer/core/xml/xslt_processor.cc b/third_party/blink/renderer/core/xml/xslt_processor.cc
index 699f8228a..1a2ebe4 100644
--- a/third_party/blink/renderer/core/xml/xslt_processor.cc
+++ b/third_party/blink/renderer/core/xml/xslt_processor.cc
@@ -87,6 +87,9 @@
 
   if (frame) {
     Document* old_document = frame->GetDocument();
+    init = init.WithOwnerDocument(old_document)
+               .WithSandboxFlags(old_document->GetSandboxFlags());
+
     // Before parsing, we need to save & detach the old document and get the new
     // document in place. Document::Shutdown() tears down the LocalFrameView, so
     // remember whether or not there was one.
@@ -109,9 +112,7 @@
 
     if (old_document) {
       DocumentXSLT::From(*result).SetTransformSourceDocument(old_document);
-      result->UpdateSecurityOrigin(old_document->GetMutableSecurityOrigin());
       result->SetCookieURL(old_document->CookieURL());
-      result->EnforceSandboxFlags(old_document->GetSandboxFlags());
 
       auto* csp = MakeGarbageCollected<ContentSecurityPolicy>();
       csp->CopyStateFrom(old_document->GetContentSecurityPolicy());
diff --git a/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc b/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
index e9f8f3b..3cf125a 100644
--- a/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
+++ b/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
@@ -358,6 +358,7 @@
 
   DocumentInit init = DocumentInit::Create()
                           .WithContextDocument(GetDocument()->ContextDocument())
+                          .WithOwnerDocument(GetDocument()->ContextDocument())
                           .WithURL(response_.ResponseUrl());
   if (is_html)
     response_document_ = MakeGarbageCollected<HTMLDocument>(init);
@@ -365,7 +366,6 @@
     response_document_ = MakeGarbageCollected<XMLDocument>(init);
 
   // FIXME: Set Last-Modified.
-  response_document_->SetSecurityOrigin(GetMutableSecurityOrigin());
   response_document_->SetContextFeatures(GetDocument()->GetContextFeatures());
   response_document_->SetMimeType(FinalResponseMIMETypeWithFallback());
 }
diff --git a/third_party/blink/renderer/devtools/front_end/console/ConsolePrompt.js b/third_party/blink/renderer/devtools/front_end/console/ConsolePrompt.js
index 7b99c74..80299e5f 100644
--- a/third_party/blink/renderer/devtools/front_end/console/ConsolePrompt.js
+++ b/third_party/blink/renderer/devtools/front_end/console/ConsolePrompt.js
@@ -46,8 +46,13 @@
      * @this {Console.ConsolePrompt}
      */
     function gotFactory(factory) {
-      this._editor =
-          factory.createEditor({lineNumbers: false, lineWrapping: true, mimeType: 'javascript', autoHeight: true});
+      this._editor = factory.createEditor({
+        devtoolsAccessibleName: ls`Console prompt`,
+        lineNumbers: false,
+        lineWrapping: true,
+        mimeType: 'javascript',
+        autoHeight: true
+      });
 
       this._defaultAutocompleteConfig = ObjectUI.JavaScriptAutocompleteConfig.createConfigForEditor(this._editor);
       this._editor.configureAutocomplete(Object.assign({}, this._defaultAutocompleteConfig, {
diff --git a/third_party/blink/renderer/devtools/front_end/console/console_strings.grdp b/third_party/blink/renderer/devtools/front_end/console/console_strings.grdp
index e14b1fb..b51975fa 100644
--- a/third_party/blink/renderer/devtools/front_end/console/console_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/console/console_strings.grdp
@@ -198,6 +198,9 @@
   <message name="IDS_DEVTOOLS_c0274fa278f2e0dfef862234e0eb9b8b" desc="">
     &lt;exception&gt;
   </message>
+  <message name="IDS_DEVTOOLS_c43a34d2abee57824fac5bb704994d88" desc="">
+    Console prompt
+  </message>
   <message name="IDS_DEVTOOLS_c9818b2c0890a205d294a2b31354f8b4" desc="">
     All levels
   </message>
diff --git a/third_party/blink/renderer/modules/BUILD.gn b/third_party/blink/renderer/modules/BUILD.gn
index d31d9b9..afb8575 100644
--- a/third_party/blink/renderer/modules/BUILD.gn
+++ b/third_party/blink/renderer/modules/BUILD.gn
@@ -392,6 +392,7 @@
     "service_worker/web_embedded_worker_impl_test.cc",
     "wake_lock/wake_lock_controller_test.cc",
     "wake_lock/wake_lock_state_record_test.cc",
+    "wake_lock/wake_lock_test_utils.cc",
     "wake_lock/wake_lock_test_utils.h",
     "webaudio/audio_basic_processor_handler_test.cc",
     "webaudio/audio_context_autoplay_test.cc",
diff --git a/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc b/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc
index ed4a32b3..e676d55 100644
--- a/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc
+++ b/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc
@@ -408,6 +408,8 @@
     if (credential->echo_user_verification_methods) {
       extension_outputs->setUvm(
           UvmEntryToArray(std::move(*credential->user_verification_methods)));
+      UseCounter::Count(resolver->GetExecutionContext(),
+                        WebFeature::kCredentialManagerCreateSuccessWithUVM);
     }
 #endif
     resolver->Resolve(MakeGarbageCollected<PublicKeyCredential>(
@@ -461,6 +463,8 @@
     if (credential->echo_user_verification_methods) {
       extension_outputs->setUvm(
           UvmEntryToArray(std::move(*credential->user_verification_methods)));
+      UseCounter::Count(resolver->GetExecutionContext(),
+                        WebFeature::kCredentialManagerGetSuccessWithUVM);
     }
 #endif
     resolver->Resolve(MakeGarbageCollected<PublicKeyCredential>(
@@ -497,6 +501,13 @@
       UseCounter::Count(resolver->GetExecutionContext(),
                         WebFeature::kCredentialManagerGetPublicKeyCredential);
     }
+#if defined(OS_ANDROID)
+    if (options->publicKey()->hasExtensions() &&
+        options->publicKey()->extensions()->hasUvm()) {
+      UseCounter::Count(resolver->GetExecutionContext(),
+                        WebFeature::kCredentialManagerGetWithUVM);
+    }
+#endif
 
     const String& relying_party_id = options->publicKey()->rpId();
     if (!CheckPublicKeySecurityRequirements(resolver, relying_party_id))
@@ -684,6 +695,13 @@
           resolver->GetExecutionContext(),
           WebFeature::kCredentialManagerCreatePublicKeyCredential);
     }
+#if defined(OS_ANDROID)
+    if (options->publicKey()->hasExtensions() &&
+        options->publicKey()->extensions()->hasUvm()) {
+      UseCounter::Count(resolver->GetExecutionContext(),
+                        WebFeature::kCredentialManagerCreateWithUVM);
+    }
+#endif
 
     const String& relying_party_id = options->publicKey()->rp()->id();
     if (!CheckPublicKeySecurityRequirements(resolver, relying_party_id))
diff --git a/third_party/blink/renderer/modules/csspaint/paint_rendering_context_2d.h b/third_party/blink/renderer/modules/csspaint/paint_rendering_context_2d.h
index a436d83f..d02e835 100644
--- a/third_party/blink/renderer/modules/csspaint/paint_rendering_context_2d.h
+++ b/third_party/blink/renderer/modules/csspaint/paint_rendering_context_2d.h
@@ -20,6 +20,12 @@
 class CanvasImageSource;
 class Color;
 
+// In our internal implementation, there are different kinds of canvas such as
+// recording canvas, GPU canvas. The CSS Paint API uses the recording canvas and
+// this class is specifically designed for the recording canvas.
+//
+// The main difference between this class and other contexts is that
+// PaintRenderingContext2D operates on CSS pixels rather than physical pixels.
 class MODULES_EXPORT PaintRenderingContext2D : public ScriptWrappable,
                                                public BaseRenderingContext2D {
   DEFINE_WRAPPERTYPEINFO();
@@ -93,6 +99,10 @@
   IntSize container_size_;
   Member<const PaintRenderingContext2DSettings> context_settings_;
   bool did_record_draw_commands_in_paint_recorder_;
+  // The paint worklet canvas operates on CSS pixels, and that's different than
+  // the HTML canvas which operates on physical pixels. In other words, the
+  // paint worklet canvas needs to handle device scale factor and browser zoom,
+  // and this is designed for that purpose.
   float effective_zoom_;
 
   DISALLOW_COPY_AND_ASSIGN(PaintRenderingContext2D);
diff --git a/third_party/blink/renderer/modules/mediarecorder/media_recorder.cc b/third_party/blink/renderer/modules/mediarecorder/media_recorder.cc
index c3226c44..04506f3d 100644
--- a/third_party/blink/renderer/modules/mediarecorder/media_recorder.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/media_recorder.cc
@@ -219,6 +219,11 @@
 }
 
 void MediaRecorder::start(int time_slice, ExceptionState& exception_state) {
+  if (!GetExecutionContext() || GetExecutionContext()->IsContextDestroyed()) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kNotAllowedError,
+                                      "Execution context is detached.");
+    return;
+  }
   if (state_ != State::kInactive) {
     exception_state.ThrowDOMException(
         DOMExceptionCode::kInvalidStateError,
@@ -238,6 +243,11 @@
 }
 
 void MediaRecorder::stop(ExceptionState& exception_state) {
+  if (!GetExecutionContext() || GetExecutionContext()->IsContextDestroyed()) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kNotAllowedError,
+                                      "Execution context is detached.");
+    return;
+  }
   if (state_ == State::kInactive) {
     exception_state.ThrowDOMException(
         DOMExceptionCode::kInvalidStateError,
@@ -249,6 +259,11 @@
 }
 
 void MediaRecorder::pause(ExceptionState& exception_state) {
+  if (!GetExecutionContext() || GetExecutionContext()->IsContextDestroyed()) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kNotAllowedError,
+                                      "Execution context is detached.");
+    return;
+  }
   if (state_ == State::kInactive) {
     exception_state.ThrowDOMException(
         DOMExceptionCode::kInvalidStateError,
@@ -266,6 +281,11 @@
 }
 
 void MediaRecorder::resume(ExceptionState& exception_state) {
+  if (!GetExecutionContext() || GetExecutionContext()->IsContextDestroyed()) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kNotAllowedError,
+                                      "Execution context is detached.");
+    return;
+  }
   if (state_ == State::kInactive) {
     exception_state.ThrowDOMException(
         DOMExceptionCode::kInvalidStateError,
@@ -282,6 +302,11 @@
 }
 
 void MediaRecorder::requestData(ExceptionState& exception_state) {
+  if (!GetExecutionContext() || GetExecutionContext()->IsContextDestroyed()) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kNotAllowedError,
+                                      "Execution context is detached.");
+    return;
+  }
   if (state_ == State::kInactive) {
     exception_state.ThrowDOMException(
         DOMExceptionCode::kInvalidStateError,
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_adapter_impl.cc b/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_adapter_impl.cc
index c8a537bd..001cd2b1 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_adapter_impl.cc
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_adapter_impl.cc
@@ -33,7 +33,11 @@
                              cricket::PORTALLOCATOR_ENABLE_IPV6_ON_WIFI);
   port_allocator_->Initialize();
 
-  ice_transport_channel_ = webrtc::CreateIceTransport(port_allocator_.get());
+  webrtc::IceTransportInit ice_transport_init;
+  ice_transport_init.set_port_allocator(port_allocator_.get());
+  ice_transport_init.set_async_resolver_factory(async_resolver_factory_.get());
+  ice_transport_channel_ =
+      webrtc::CreateIceTransport(std::move(ice_transport_init));
   SetupIceTransportChannel();
   // We need to set the ICE role even before Start is called since the Port
   // assumes that the role has been set before receiving incoming connectivity
diff --git a/third_party/blink/renderer/modules/sms/BUILD.gn b/third_party/blink/renderer/modules/sms/BUILD.gn
index f9ef335..6e270728 100644
--- a/third_party/blink/renderer/modules/sms/BUILD.gn
+++ b/third_party/blink/renderer/modules/sms/BUILD.gn
@@ -10,6 +10,8 @@
     "navigator_sms.h",
     "sms.cc",
     "sms.h",
+    "sms_metrics.cc",
+    "sms_metrics.h",
     "sms_receiver.cc",
     "sms_receiver.h",
   ]
diff --git a/third_party/blink/renderer/modules/sms/sms_metrics.cc b/third_party/blink/renderer/modules/sms/sms_metrics.cc
new file mode 100644
index 0000000..a1e21106
--- /dev/null
+++ b/third_party/blink/renderer/modules/sms/sms_metrics.cc
@@ -0,0 +1,19 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/sms/sms_metrics.h"
+
+#include "base/metrics/histogram_macros.h"
+
+namespace blink {
+
+void RecordSMSOutcome(SMSReceiverOutcome outcome) {
+  UMA_HISTOGRAM_ENUMERATION("Blink.Sms.Receive.Outcome", outcome);
+}
+
+void RecordSMSSuccessTime(const base::TimeDelta duration) {
+  UMA_HISTOGRAM_MEDIUM_TIMES("Blink.Sms.Receive.TimeSuccess", duration);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/sms/sms_metrics.h b/third_party/blink/renderer/modules/sms/sms_metrics.h
new file mode 100644
index 0000000..2623be8
--- /dev/null
+++ b/third_party/blink/renderer/modules/sms/sms_metrics.h
@@ -0,0 +1,29 @@
+// 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 THIRD_PARTY_BLINK_RENDERER_MODULES_SMS_SMS_METRICS_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_SMS_SMS_METRICS_H_
+
+namespace base {
+class TimeDelta;
+}
+
+namespace blink {
+
+// Don't change the meaning of these values because they are being recorded in a
+// metric.
+enum class SMSReceiverOutcome {
+  kSuccess = 0,
+  kTimeout = 1,
+  kConnectionError = 2,
+  kMaxValue = kConnectionError
+};
+
+void RecordSMSOutcome(SMSReceiverOutcome outcome);
+
+void RecordSMSSuccessTime(const base::TimeDelta duration);
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_SMS_SMS_METRICS_H_
diff --git a/third_party/blink/renderer/modules/sms/sms_receiver.cc b/third_party/blink/renderer/modules/sms/sms_receiver.cc
index 05f0015..f2ae5928 100644
--- a/third_party/blink/renderer/modules/sms/sms_receiver.cc
+++ b/third_party/blink/renderer/modules/sms/sms_receiver.cc
@@ -13,6 +13,7 @@
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/modules/sms/sms.h"
+#include "third_party/blink/renderer/modules/sms/sms_metrics.h"
 #include "third_party/blink/renderer/modules/sms/sms_receiver_options.h"
 #include "third_party/blink/renderer/platform/bindings/name_client.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
@@ -66,9 +67,10 @@
         &SMSReceiver::OnSMSReceiverConnectionError, WrapWeakPersistent(this)));
   }
 
-  service_->Receive(base::TimeDelta::FromSeconds(timeout_seconds),
-                    WTF::Bind(&SMSReceiver::OnReceive, WrapPersistent(this),
-                              WrapPersistent(resolver)));
+  service_->Receive(
+      base::TimeDelta::FromSeconds(timeout_seconds),
+      WTF::Bind(&SMSReceiver::OnReceive, WrapPersistent(this),
+                WrapPersistent(resolver), base::TimeTicks::Now()));
 
   return resolver->Promise();
 }
@@ -76,6 +78,7 @@
 SMSReceiver::~SMSReceiver() = default;
 
 void SMSReceiver::OnReceive(ScriptPromiseResolver* resolver,
+                            base::TimeTicks start_time,
                             mojom::blink::SmsStatus status,
                             const WTF::String& sms) {
   requests_.erase(resolver);
@@ -83,8 +86,13 @@
   if (status == mojom::blink::SmsStatus::kTimeout) {
     resolver->Reject(MakeGarbageCollected<DOMException>(
         DOMExceptionCode::kTimeoutError, "SMSReceiver timed out."));
+    RecordSMSOutcome(SMSReceiverOutcome::kTimeout);
     return;
   }
+
+  RecordSMSSuccessTime(base::TimeTicks::Now() - start_time);
+  RecordSMSOutcome(SMSReceiverOutcome::kSuccess);
+
   resolver->Resolve(MakeGarbageCollected<blink::SMS>(sms));
 }
 
@@ -93,6 +101,7 @@
   for (ScriptPromiseResolver* request : requests_) {
     request->Reject(MakeGarbageCollected<DOMException>(
         DOMExceptionCode::kNotFoundError, "SMSReceiver not available."));
+    RecordSMSOutcome(SMSReceiverOutcome::kConnectionError);
   }
   requests_.clear();
 }
diff --git a/third_party/blink/renderer/modules/sms/sms_receiver.h b/third_party/blink/renderer/modules/sms/sms_receiver.h
index ad9a370..1c5f382 100644
--- a/third_party/blink/renderer/modules/sms/sms_receiver.h
+++ b/third_party/blink/renderer/modules/sms/sms_receiver.h
@@ -35,6 +35,7 @@
   HeapHashSet<Member<ScriptPromiseResolver>> requests_;
 
   void OnReceive(ScriptPromiseResolver* resolver,
+                 base::TimeTicks start_time,
                  mojom::blink::SmsStatus status,
                  const WTF::String& sms);
 
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock.cc b/third_party/blink/renderer/modules/wake_lock/wake_lock.cc
index 9d993b0..50ee0f4 100644
--- a/third_party/blink/renderer/modules/wake_lock/wake_lock.cc
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock.cc
@@ -27,6 +27,24 @@
 }  // namespace
 
 // static
+ScriptPromise WakeLock::requestPermission(ScriptState* script_state,
+                                          const String& type) {
+  // https://w3c.github.io/wake-lock/#requestpermission-static-method
+  // 1. Let promise be a new promise.
+  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
+  ScriptPromise promise = resolver->Promise();
+
+  // 2. Return promise and run the following steps in parallel:
+  // 2.1. Let state be the result of running and waiting for the obtain
+  //      permission steps with type.
+  // 2.2. Resolve promise with state.
+  auto* document = GetDocument(script_state);
+  WakeLockController::From(*document).RequestPermission(ToWakeLockType(type),
+                                                        resolver);
+  return promise;
+}
+
+// static
 ScriptPromise WakeLock::request(ScriptState* script_state,
                                 const String& type,
                                 WakeLockRequestOptions* options) {
@@ -113,12 +131,7 @@
 
   // 6. Run the following steps in parallel, but abort when options' signal
   // member is present and its aborted flag is set:
-  // [...]
-  // 6.2. Let success be the result of awaiting acquire a wake lock with promise
-  //      and type:
-  // 6.2.1. If success is false then reject promise with a "NotAllowedError"
-  //        DOMException, and abort these steps.
-  controller.AcquireWakeLock(wake_lock_type, resolver);
+  controller.RequestWakeLock(wake_lock_type, resolver, options->signal());
 
   // 7. Return promise.
   return promise;
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock.h b/third_party/blink/renderer/modules/wake_lock/wake_lock.h
index de8c04c2..4042b2b 100644
--- a/third_party/blink/renderer/modules/wake_lock/wake_lock.h
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock.h
@@ -23,6 +23,7 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
+  static ScriptPromise requestPermission(ScriptState*, const WTF::String& type);
   static ScriptPromise request(ScriptState*,
                                const WTF::String& type,
                                WakeLockRequestOptions*);
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock.idl b/third_party/blink/renderer/modules/wake_lock/wake_lock.idl
index 8884f6ef..d465b52 100644
--- a/third_party/blink/renderer/modules/wake_lock/wake_lock.idl
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock.idl
@@ -13,5 +13,6 @@
   Exposed=Window,
   RuntimeEnabled=WakeLock
 ] interface WakeLock {
+  [CallWith=ScriptState, Exposed=Window] static Promise<PermissionState> requestPermission(WakeLockType type);
   [CallWith=ScriptState] static Promise<void> request(WakeLockType type, optional WakeLockRequestOptions options);
 };
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock_controller.cc b/third_party/blink/renderer/modules/wake_lock/wake_lock_controller.cc
index 2678fd03..4f887b0 100644
--- a/third_party/blink/renderer/modules/wake_lock/wake_lock_controller.cc
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock_controller.cc
@@ -5,10 +5,18 @@
 #include "third_party/blink/renderer/modules/wake_lock/wake_lock_controller.h"
 
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
+#include "third_party/blink/renderer/core/dom/abort_signal.h"
+#include "third_party/blink/renderer/core/dom/dom_exception.h"
+#include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/modules/permissions/permission_utils.h"
 #include "third_party/blink/renderer/modules/wake_lock/wake_lock_state_record.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
 
 namespace blink {
 
+using mojom::blink::PermissionService;
+using mojom::blink::PermissionStatus;
+
 WakeLockController::WakeLockController(Document& document)
     : Supplement<Document>(document),
       ContextLifecycleObserver(&document),
@@ -39,12 +47,56 @@
   Supplement<Document>::Trace(visitor);
 }
 
-void WakeLockController::AcquireWakeLock(WakeLockType type,
-                                         ScriptPromiseResolver* resolver) {
-  DCHECK_LE(type, WakeLockType::kMaxValue);
-  WakeLockStateRecord* state_record = state_records_[static_cast<size_t>(type)];
-  DCHECK(state_record);
-  state_record->AcquireWakeLock(resolver);
+void WakeLockController::RequestWakeLock(WakeLockType type,
+                                         ScriptPromiseResolver* resolver,
+                                         AbortSignal* signal) {
+  // https://w3c.github.io/wake-lock/#request-static-method
+  // 6. [...] abort when options' signal member is present and its aborted flag
+  // is set:
+  DCHECK(resolver);
+  // We were called via WakeLock::request(), which should have handled the
+  // signal->aborted() case.
+  DCHECK(!signal || !signal->aborted());
+  // 6.1. Let state be the result of awaiting obtain permission steps with
+  // type:
+  ObtainPermission(
+      type, WTF::Bind(&WakeLockController::DidReceivePermissionResponse,
+                      WrapPersistent(this), type, WrapPersistent(resolver),
+                      WrapPersistent(signal)));
+}
+
+void WakeLockController::DidReceivePermissionResponse(
+    WakeLockType type,
+    ScriptPromiseResolver* resolver,
+    AbortSignal* signal,
+    PermissionStatus status) {
+  // https://w3c.github.io/wake-lock/#request-static-method
+  DCHECK(status == PermissionStatus::GRANTED ||
+         status == PermissionStatus::DENIED);
+  DCHECK(resolver);
+  if (signal && signal->aborted())
+    return;
+  // 6.1.1. If state is "denied", then reject promise with a "NotAllowedError"
+  //        DOMException, and abort these steps.
+  if (status != PermissionStatus::GRANTED) {
+    resolver->Reject(MakeGarbageCollected<DOMException>(
+        DOMExceptionCode::kNotAllowedError,
+        "Wake Lock permission request denied"));
+    return;
+  }
+  // https://github.com/w3c/wake-lock/issues/222: the page can become hidden
+  // between RequestWakeLock() and AcquireWakeLock(), in which case we need to
+  // abort early.
+  if (type == WakeLockType::kScreen &&
+      !(GetPage() && GetPage()->IsPageVisible())) {
+    ReleaseWakeLock(type, resolver);
+    return;
+  }
+  // 6.2. Let success be the result of awaiting acquire a wake lock with promise
+  // and type:
+  // 6.2.1. If success is false then reject promise with a "NotAllowedError"
+  //        DOMException, and abort these steps.
+  AcquireWakeLock(type, resolver);
 }
 
 void WakeLockController::ReleaseWakeLock(WakeLockType type,
@@ -55,6 +107,23 @@
   state_record->ReleaseWakeLock(resolver);
 }
 
+void WakeLockController::RequestPermission(WakeLockType type,
+                                           ScriptPromiseResolver* resolver) {
+  // https://w3c.github.io/wake-lock/#requestpermission-static-method
+  // 2.1. Let state be the result of running and waiting for the obtain
+  //      permission steps with type.
+  // 2.2. Resolve promise with state.
+  auto permission_callback = WTF::Bind(
+      [](ScriptPromiseResolver* resolver, PermissionStatus status) {
+        DCHECK(status == PermissionStatus::GRANTED ||
+               status == PermissionStatus::DENIED);
+        DCHECK(resolver);
+        resolver->Resolve(PermissionStatusToString(status));
+      },
+      WrapPersistent(resolver));
+  ObtainPermission(type, std::move(permission_callback));
+}
+
 void WakeLockController::ContextDestroyed(ExecutionContext*) {
   // https://w3c.github.io/wake-lock/#handling-document-loss-of-full-activity
   // 1. Let document be the responsible document of the current settings object.
@@ -88,4 +157,54 @@
     state_record->ClearWakeLocks();
 }
 
+void WakeLockController::AcquireWakeLock(WakeLockType type,
+                                         ScriptPromiseResolver* resolver) {
+  DCHECK_LE(type, WakeLockType::kMaxValue);
+  WakeLockStateRecord* state_record = state_records_[static_cast<size_t>(type)];
+  DCHECK(state_record);
+  state_record->AcquireWakeLock(resolver);
+}
+
+void WakeLockController::ObtainPermission(
+    WakeLockType type,
+    base::OnceCallback<void(PermissionStatus)> callback) {
+  // https:w3c.github.io/wake-lock/#dfn-obtain-permission
+  // Note we actually implement a simplified version of the "obtain permission"
+  // algorithm that essentially just calls the "request permission to use"
+  // algorithm from the Permissions spec (i.e. we bypass all the steps covering
+  // calling the "query a permission" algorithm and handling its result).
+  // * Right now, we can do that because there is no way for Chromium's
+  //   permission system to get to the "prompt" state given how
+  //   WakeLockPermissionContext is currently implemented.
+  // * Even if WakeLockPermissionContext changes in the future, this Blink
+  //   implementation is unlikely to change because
+  //   WakeLockPermissionContext::RequestPermission() will take its
+  //   |user_gesture| argument into account to actually implement a slightly
+  //   altered version of "request permission to use", the behavior of which
+  //   will match the definition of "obtain permission" in the Wake Lock spec.
+  DCHECK(type == WakeLockType::kScreen || type == WakeLockType::kSystem);
+  static_assert(
+      static_cast<mojom::blink::WakeLockType>(WakeLockType::kScreen) ==
+          mojom::blink::WakeLockType::kScreen,
+      "WakeLockType and mojom::blink::WakeLockType must have identical values");
+  static_assert(
+      static_cast<mojom::blink::WakeLockType>(WakeLockType::kSystem) ==
+          mojom::blink::WakeLockType::kSystem,
+      "WakeLockType and mojom::blink::WakeLockType must have identical values");
+
+  GetPermissionService().RequestPermission(
+      CreateWakeLockPermissionDescriptor(
+          static_cast<mojom::blink::WakeLockType>(type)),
+      LocalFrame::HasTransientUserActivation(GetSupplementable()->GetFrame()),
+      std::move(callback));
+}
+
+PermissionService& WakeLockController::GetPermissionService() {
+  if (!permission_service_) {
+    ConnectToPermissionService(GetSupplementable(),
+                               mojo::MakeRequest(&permission_service_));
+  }
+  return *permission_service_;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock_controller.h b/third_party/blink/renderer/modules/wake_lock/wake_lock_controller.h
index 91da4e8..241b7cf 100644
--- a/third_party/blink/renderer/modules/wake_lock/wake_lock_controller.h
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock_controller.h
@@ -5,6 +5,9 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WAKE_LOCK_WAKE_LOCK_CONTROLLER_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_WAKE_LOCK_WAKE_LOCK_CONTROLLER_H_
 
+#include "base/callback.h"
+#include "base/gtest_prod_util.h"
+#include "third_party/blink/public/mojom/permissions/permission.mojom-blink.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/execution_context/context_lifecycle_observer.h"
 #include "third_party/blink/renderer/core/page/page_visibility_observer.h"
@@ -14,6 +17,7 @@
 
 namespace blink {
 
+class AbortSignal;
 class ScriptPromiseResolver;
 class WakeLockStateRecord;
 
@@ -35,10 +39,14 @@
 
   void Trace(blink::Visitor*) override;
 
-  void AcquireWakeLock(WakeLockType type, ScriptPromiseResolver*);
+  void RequestWakeLock(WakeLockType type,
+                       ScriptPromiseResolver* resolver,
+                       AbortSignal* signal);
 
   void ReleaseWakeLock(WakeLockType type, ScriptPromiseResolver*);
 
+  void RequestPermission(WakeLockType type, ScriptPromiseResolver*);
+
  private:
   // ContextLifecycleObserver implementation
   void ContextDestroyed(ExecutionContext*) override;
@@ -46,11 +54,29 @@
   // PageVisibilityObserver implementation
   void PageVisibilityChanged() override;
 
+  void AcquireWakeLock(WakeLockType type, ScriptPromiseResolver*);
+
+  void DidReceivePermissionResponse(WakeLockType type,
+                                    ScriptPromiseResolver*,
+                                    AbortSignal*,
+                                    mojom::blink::PermissionStatus);
+
+  // Permission handling
+  void ObtainPermission(
+      WakeLockType type,
+      base::OnceCallback<void(mojom::blink::PermissionStatus)> callback);
+  mojom::blink::PermissionService& GetPermissionService();
+
+  mojom::blink::PermissionServicePtr permission_service_;
+
   // https://w3c.github.io/wake-lock/#concepts-and-state-record
   // Each platform wake lock (one per wake lock type) has an associated state
   // record per responsible document [...] internal slots.
-  Member<WakeLockStateRecord>
-      state_records_[static_cast<size_t>(WakeLockType::kMaxValue) + 1];
+  Member<WakeLockStateRecord> state_records_[kWakeLockTypeCount];
+
+  FRIEND_TEST_ALL_PREFIXES(WakeLockControllerTest, AcquireScreenWakeLock);
+  FRIEND_TEST_ALL_PREFIXES(WakeLockControllerTest, AcquireSystemWakeLock);
+  FRIEND_TEST_ALL_PREFIXES(WakeLockControllerTest, AcquireMultipleLocks);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock_controller_test.cc b/third_party/blink/renderer/modules/wake_lock/wake_lock_controller_test.cc
index e44accb..4e8c086 100644
--- a/third_party/blink/renderer/modules/wake_lock/wake_lock_controller_test.cc
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock_controller_test.cc
@@ -9,6 +9,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/core/dom/abort_signal.h"
 #include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/modules/wake_lock/wake_lock_test_utils.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
@@ -16,6 +17,107 @@
 
 namespace blink {
 
+TEST(WakeLockControllerTest, RequestWakeLockGranted) {
+  MockWakeLockService wake_lock_service;
+  WakeLockTestingContext context(&wake_lock_service);
+  auto& controller = WakeLockController::From(*context.GetDocument());
+
+  context.GetPermissionService().SetPermissionResponse(
+      WakeLockType::kScreen, mojom::blink::PermissionStatus::GRANTED);
+
+  auto* screen_resolver =
+      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
+  ScriptPromise screen_promise = screen_resolver->Promise();
+
+  controller.RequestWakeLock(WakeLockType::kScreen, screen_resolver, nullptr);
+
+  MockWakeLock& screen_lock =
+      wake_lock_service.get_wake_lock(WakeLockType::kScreen);
+  MockPermissionService& permission_service = context.GetPermissionService();
+
+  permission_service.WaitForPermissionRequest(WakeLockType::kScreen);
+  screen_lock.WaitForRequest();
+
+  EXPECT_EQ(v8::Promise::kPending,
+            ScriptPromiseUtils::GetPromiseState(screen_promise));
+  EXPECT_TRUE(screen_lock.is_acquired());
+}
+
+TEST(WakeLockControllerTest, RequestWakeLockDenied) {
+  MockWakeLockService wake_lock_service;
+  WakeLockTestingContext context(&wake_lock_service);
+  auto& controller = WakeLockController::From(*context.GetDocument());
+
+  context.GetPermissionService().SetPermissionResponse(
+      WakeLockType::kSystem, mojom::blink::PermissionStatus::DENIED);
+
+  auto* system_resolver =
+      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
+  ScriptPromise system_promise = system_resolver->Promise();
+
+  controller.RequestWakeLock(WakeLockType::kSystem, system_resolver, nullptr);
+
+  MockWakeLock& system_lock =
+      wake_lock_service.get_wake_lock(WakeLockType::kSystem);
+  MockPermissionService& permission_service = context.GetPermissionService();
+
+  permission_service.WaitForPermissionRequest(WakeLockType::kSystem);
+  context.WaitForPromiseRejection(system_promise);
+
+  EXPECT_EQ(v8::Promise::kRejected,
+            ScriptPromiseUtils::GetPromiseState(system_promise));
+  EXPECT_FALSE(system_lock.is_acquired());
+
+  // System locks are not allowed by default, so the promise should have been
+  // rejected with a NotAllowedError DOMException.
+  DOMException* dom_exception =
+      ScriptPromiseUtils::GetPromiseResolutionAsDOMException(system_promise);
+  ASSERT_NE(dom_exception, nullptr);
+  EXPECT_EQ("NotAllowedError", dom_exception->name());
+}
+
+// Abort early in DidReceivePermissionResponse().
+TEST(WakeLockControllerTest, RequestWakeLockAbortEarly) {
+  MockWakeLockService wake_lock_service;
+  WakeLockTestingContext context(&wake_lock_service);
+  auto& controller = WakeLockController::From(*context.GetDocument());
+
+  context.GetPermissionService().SetPermissionResponse(
+      WakeLockType::kScreen, mojom::blink::PermissionStatus::GRANTED);
+
+  MockWakeLock& screen_lock =
+      wake_lock_service.get_wake_lock(WakeLockType::kScreen);
+  auto* screen_resolver =
+      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
+  ScriptPromise screen_promise = screen_resolver->Promise();
+
+  auto* abort_signal = MakeGarbageCollected<AbortSignal>(context.GetDocument());
+  abort_signal->AddAlgorithm(WTF::Bind(
+      &WakeLockController::ReleaseWakeLock, WrapWeakPersistent(&controller),
+      WakeLockType::kScreen, WrapPersistent(screen_resolver)));
+
+  controller.RequestWakeLock(
+      WakeLockType::kScreen,
+      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState()),
+      abort_signal);
+
+  MockPermissionService& permission_service = context.GetPermissionService();
+
+  permission_service.WaitForPermissionRequest(WakeLockType::kScreen);
+  abort_signal->SignalAbort();
+
+  context.WaitForPromiseRejection(screen_promise);
+
+  EXPECT_EQ(v8::Promise::kRejected,
+            ScriptPromiseUtils::GetPromiseState(screen_promise));
+  EXPECT_FALSE(screen_lock.is_acquired());
+
+  DOMException* dom_exception =
+      ScriptPromiseUtils::GetPromiseResolutionAsDOMException(screen_promise);
+  ASSERT_NE(dom_exception, nullptr);
+  EXPECT_EQ("AbortError", dom_exception->name());
+}
+
 TEST(WakeLockControllerTest, AcquireScreenWakeLock) {
   MockWakeLockService wake_lock_service;
   WakeLockTestingContext context(&wake_lock_service);
@@ -31,7 +133,7 @@
   EXPECT_TRUE(screen_lock.is_acquired());
 }
 
-TEST(WakeLockController, AcquireSystemWakeLock) {
+TEST(WakeLockControllerTest, AcquireSystemWakeLock) {
   MockWakeLockService wake_lock_service;
   WakeLockTestingContext context(&wake_lock_service);
   auto& controller = WakeLockController::From(*context.GetDocument());
@@ -84,12 +186,14 @@
       MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
   ScriptPromise promise = resolver->Promise();
 
-  EXPECT_EQ(v8::Promise::kPending, GetScriptPromiseState(promise));
+  EXPECT_EQ(v8::Promise::kPending,
+            ScriptPromiseUtils::GetPromiseState(promise));
   controller.ReleaseWakeLock(WakeLockType::kScreen, resolver);
   context.WaitForPromiseRejection(promise);
 
   // The promise is always rejected, even if it is not in [[ActiveLocks]].
-  EXPECT_EQ(v8::Promise::kRejected, GetScriptPromiseState(promise));
+  EXPECT_EQ(v8::Promise::kRejected,
+            ScriptPromiseUtils::GetPromiseState(promise));
   EXPECT_FALSE(screen_lock.is_acquired());
 }
 
@@ -98,6 +202,9 @@
   WakeLockTestingContext context(&wake_lock_service);
   auto& controller = WakeLockController::From(*context.GetDocument());
 
+  context.GetPermissionService().SetPermissionResponse(
+      WakeLockType::kScreen, mojom::blink::PermissionStatus::GRANTED);
+
   MockWakeLock& screen_lock =
       wake_lock_service.get_wake_lock(WakeLockType::kScreen);
 
@@ -105,13 +212,15 @@
       MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
   ScriptPromise promise = resolver->Promise();
 
-  controller.AcquireWakeLock(WakeLockType::kScreen, resolver);
+  controller.RequestWakeLock(WakeLockType::kScreen, resolver,
+                             /*signal=*/nullptr);
   screen_lock.WaitForRequest();
   controller.ReleaseWakeLock(WakeLockType::kScreen, resolver);
   context.WaitForPromiseRejection(promise);
   screen_lock.WaitForCancelation();
 
-  EXPECT_EQ(v8::Promise::kRejected, GetScriptPromiseState(promise));
+  EXPECT_EQ(v8::Promise::kRejected,
+            ScriptPromiseUtils::GetPromiseState(promise));
   EXPECT_FALSE(screen_lock.is_acquired());
 }
 
@@ -122,6 +231,9 @@
   WakeLockTestingContext context(&wake_lock_service);
   auto& controller = WakeLockController::From(*context.GetDocument());
 
+  context.GetPermissionService().SetPermissionResponse(
+      WakeLockType::kScreen, mojom::blink::PermissionStatus::GRANTED);
+
   MockWakeLock& screen_lock =
       wake_lock_service.get_wake_lock(WakeLockType::kScreen);
   auto* screen_resolver =
@@ -133,14 +245,16 @@
       &WakeLockController::ReleaseWakeLock, WrapWeakPersistent(&controller),
       WakeLockType::kScreen, WrapPersistent(screen_resolver)));
 
-  controller.AcquireWakeLock(WakeLockType::kScreen, screen_resolver);
+  controller.RequestWakeLock(WakeLockType::kScreen, screen_resolver,
+                             /*signal=*/nullptr);
   screen_lock.WaitForRequest();
 
   abort_signal->SignalAbort();
   context.WaitForPromiseRejection(screen_promise);
   screen_lock.WaitForCancelation();
 
-  EXPECT_EQ(v8::Promise::kRejected, GetScriptPromiseState(screen_promise));
+  EXPECT_EQ(v8::Promise::kRejected,
+            ScriptPromiseUtils::GetPromiseState(screen_promise));
   EXPECT_FALSE(screen_lock.is_acquired());
 }
 
@@ -154,6 +268,10 @@
       wake_lock_service.get_wake_lock(WakeLockType::kScreen);
   MockWakeLock& system_lock =
       wake_lock_service.get_wake_lock(WakeLockType::kSystem);
+  context.GetPermissionService().SetPermissionResponse(
+      WakeLockType::kScreen, mojom::blink::PermissionStatus::GRANTED);
+  context.GetPermissionService().SetPermissionResponse(
+      WakeLockType::kSystem, mojom::blink::PermissionStatus::GRANTED);
 
   // First, acquire a handful of locks of different types.
   auto* screen_resolver1 =
@@ -165,10 +283,13 @@
   auto* system_resolver1 =
       MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
   ScriptPromise system_promise1 = system_resolver1->Promise();
-  controller.AcquireWakeLock(WakeLockType::kScreen, screen_resolver1);
-  controller.AcquireWakeLock(WakeLockType::kScreen, screen_resolver2);
+  controller.RequestWakeLock(WakeLockType::kScreen, screen_resolver1,
+                             /*signal=*/nullptr);
+  controller.RequestWakeLock(WakeLockType::kScreen, screen_resolver2,
+                             /*signal=*/nullptr);
   screen_lock.WaitForRequest();
-  controller.AcquireWakeLock(WakeLockType::kSystem, system_resolver1);
+  controller.RequestWakeLock(WakeLockType::kSystem, system_resolver1,
+                             /*signal=*/nullptr);
   system_lock.WaitForRequest();
 
   // Now shut down our Document and make sure all [[ActiveLocks]] slots have
@@ -189,6 +310,11 @@
   WakeLockTestingContext context(&wake_lock_service);
   auto& controller = WakeLockController::From(*context.GetDocument());
 
+  context.GetPermissionService().SetPermissionResponse(
+      WakeLockType::kScreen, mojom::blink::PermissionStatus::GRANTED);
+  context.GetPermissionService().SetPermissionResponse(
+      WakeLockType::kSystem, mojom::blink::PermissionStatus::GRANTED);
+
   MockWakeLock& screen_lock =
       wake_lock_service.get_wake_lock(WakeLockType::kScreen);
   auto* screen_resolver =
@@ -201,18 +327,80 @@
       MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
   ScriptPromise system_promise = system_resolver->Promise();
 
-  controller.AcquireWakeLock(WakeLockType::kScreen, screen_resolver);
+  controller.RequestWakeLock(WakeLockType::kScreen, screen_resolver,
+                             /*signal=*/nullptr);
+  controller.RequestWakeLock(WakeLockType::kSystem, system_resolver,
+                             /*signal=*/nullptr);
+
   screen_lock.WaitForRequest();
-  controller.AcquireWakeLock(WakeLockType::kSystem, system_resolver);
   system_lock.WaitForRequest();
 
   context.GetDocument()->GetPage()->SetIsHidden(true, false);
+
   context.WaitForPromiseRejection(screen_promise);
   screen_lock.WaitForCancelation();
 
-  EXPECT_EQ(v8::Promise::kRejected, GetScriptPromiseState(screen_promise));
+  EXPECT_EQ(v8::Promise::kRejected,
+            ScriptPromiseUtils::GetPromiseState(screen_promise));
+  DOMException* dom_exception =
+      ScriptPromiseUtils::GetPromiseResolutionAsDOMException(screen_promise);
+  ASSERT_NE(dom_exception, nullptr);
+  EXPECT_EQ("AbortError", dom_exception->name());
   EXPECT_FALSE(screen_lock.is_acquired());
-  EXPECT_EQ(v8::Promise::kPending, GetScriptPromiseState(system_promise));
+  EXPECT_EQ(v8::Promise::kPending,
+            ScriptPromiseUtils::GetPromiseState(system_promise));
+  EXPECT_TRUE(system_lock.is_acquired());
+
+  context.GetDocument()->GetPage()->SetIsHidden(false, false);
+
+  auto* other_resolver =
+      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
+  ScriptPromise other_promise = system_resolver->Promise();
+  controller.RequestWakeLock(WakeLockType::kScreen, other_resolver,
+                             /*signal=*/nullptr);
+  screen_lock.WaitForRequest();
+  EXPECT_EQ(v8::Promise::kPending,
+            ScriptPromiseUtils::GetPromiseState(other_promise));
+  EXPECT_TRUE(screen_lock.is_acquired());
+}
+
+// https://w3c.github.io/wake-lock/#handling-document-loss-of-visibility
+TEST(WakeLockControllerTest, PageVisibilityHiddenBeforeLockAcquisition) {
+  MockWakeLockService wake_lock_service;
+  WakeLockTestingContext context(&wake_lock_service);
+  auto& controller = WakeLockController::From(*context.GetDocument());
+
+  context.GetPermissionService().SetPermissionResponse(
+      WakeLockType::kScreen, mojom::blink::PermissionStatus::GRANTED);
+  context.GetPermissionService().SetPermissionResponse(
+      WakeLockType::kSystem, mojom::blink::PermissionStatus::GRANTED);
+
+  MockWakeLock& screen_lock =
+      wake_lock_service.get_wake_lock(WakeLockType::kScreen);
+  auto* screen_resolver =
+      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
+  ScriptPromise screen_promise = screen_resolver->Promise();
+
+  MockWakeLock& system_lock =
+      wake_lock_service.get_wake_lock(WakeLockType::kSystem);
+  auto* system_resolver =
+      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
+  ScriptPromise system_promise = system_resolver->Promise();
+
+  controller.RequestWakeLock(WakeLockType::kScreen, screen_resolver,
+                             /*signal=*/nullptr);
+  controller.RequestWakeLock(WakeLockType::kSystem, system_resolver,
+                             /*signal=*/nullptr);
+  context.GetDocument()->GetPage()->SetIsHidden(true, false);
+
+  context.WaitForPromiseRejection(screen_promise);
+  system_lock.WaitForRequest();
+
+  EXPECT_EQ(v8::Promise::kRejected,
+            ScriptPromiseUtils::GetPromiseState(screen_promise));
+  EXPECT_FALSE(screen_lock.is_acquired());
+  EXPECT_EQ(v8::Promise::kPending,
+            ScriptPromiseUtils::GetPromiseState(system_promise));
   EXPECT_TRUE(system_lock.is_acquired());
 }
 
@@ -223,6 +411,9 @@
   WakeLockTestingContext context(&wake_lock_service);
   auto& controller = WakeLockController::From(*context.GetDocument());
 
+  context.GetPermissionService().SetPermissionResponse(
+      WakeLockType::kScreen, mojom::blink::PermissionStatus::GRANTED);
+
   MockWakeLock& screen_lock =
       wake_lock_service.get_wake_lock(WakeLockType::kScreen);
   auto* screen_resolver =
@@ -234,14 +425,16 @@
       &WakeLockController::ReleaseWakeLock, WrapWeakPersistent(&controller),
       WakeLockType::kScreen, WrapPersistent(screen_resolver)));
 
-  controller.AcquireWakeLock(WakeLockType::kScreen, screen_resolver);
+  controller.RequestWakeLock(WakeLockType::kScreen, screen_resolver,
+                             /*signal=*/nullptr);
   screen_lock.WaitForRequest();
 
   context.GetDocument()->GetPage()->SetIsHidden(true, false);
   context.WaitForPromiseRejection(screen_promise);
   screen_lock.WaitForCancelation();
 
-  EXPECT_EQ(v8::Promise::kRejected, GetScriptPromiseState(screen_promise));
+  EXPECT_EQ(v8::Promise::kRejected,
+            ScriptPromiseUtils::GetPromiseState(screen_promise));
   EXPECT_FALSE(screen_lock.is_acquired());
 
   abort_signal->SignalAbort();
@@ -250,4 +443,46 @@
   EXPECT_FALSE(screen_lock.is_acquired());
 }
 
+TEST(WakeLockControllerTest, RequestPermissionGranted) {
+  MockWakeLockService wake_lock_service;
+  WakeLockTestingContext context(&wake_lock_service);
+  auto& controller = WakeLockController::From(*context.GetDocument());
+
+  context.GetPermissionService().SetPermissionResponse(
+      WakeLockType::kSystem, mojom::blink::PermissionStatus::GRANTED);
+
+  auto* system_resolver =
+      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
+  ScriptPromise system_promise = system_resolver->Promise();
+
+  controller.RequestPermission(WakeLockType::kSystem, system_resolver);
+  context.WaitForPromiseFulfillment(system_promise);
+
+  EXPECT_EQ(v8::Promise::kFulfilled,
+            ScriptPromiseUtils::GetPromiseState(system_promise));
+  EXPECT_EQ("granted",
+            ScriptPromiseUtils::GetPromiseResolutionAsString(system_promise));
+}
+
+TEST(WakeLockControllerTest, RequestPermissionDenied) {
+  MockWakeLockService wake_lock_service;
+  WakeLockTestingContext context(&wake_lock_service);
+  auto& controller = WakeLockController::From(*context.GetDocument());
+
+  context.GetPermissionService().SetPermissionResponse(
+      WakeLockType::kSystem, mojom::blink::PermissionStatus::DENIED);
+
+  auto* system_resolver =
+      MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
+  ScriptPromise system_promise = system_resolver->Promise();
+
+  controller.RequestPermission(WakeLockType::kSystem, system_resolver);
+  context.WaitForPromiseFulfillment(system_promise);
+
+  EXPECT_EQ(v8::Promise::kFulfilled,
+            ScriptPromiseUtils::GetPromiseState(system_promise));
+  EXPECT_EQ("denied",
+            ScriptPromiseUtils::GetPromiseResolutionAsString(system_promise));
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock_state_record_test.cc b/third_party/blink/renderer/modules/wake_lock/wake_lock_state_record_test.cc
index eae4954..3fea7bc 100644
--- a/third_party/blink/renderer/modules/wake_lock/wake_lock_state_record_test.cc
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock_state_record_test.cc
@@ -7,7 +7,6 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
-#include "third_party/blink/renderer/bindings/core/v8/v8_dom_exception.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/modules/wake_lock/wake_lock_test_utils.h"
@@ -24,11 +23,6 @@
   return MakeGarbageCollected<WakeLockStateRecord>(context.GetDocument(), type);
 }
 
-DOMException* GetDOMException(const ScriptPromise& promise) {
-  return V8DOMException::ToImplWithTypeCheck(
-      promise.GetIsolate(), promise.V8Value().As<v8::Promise>()->Result());
-}
-
 }  // namespace
 
 TEST(WakeLockStateRecordTest, AcquireWakeLock) {
@@ -51,8 +45,10 @@
   state_record->AcquireWakeLock(resolver2);
   screen_lock.WaitForRequest();
 
-  EXPECT_EQ(v8::Promise::kPending, GetScriptPromiseState(promise1));
-  EXPECT_EQ(v8::Promise::kPending, GetScriptPromiseState(promise2));
+  EXPECT_EQ(v8::Promise::kPending,
+            ScriptPromiseUtils::GetPromiseState(promise1));
+  EXPECT_EQ(v8::Promise::kPending,
+            ScriptPromiseUtils::GetPromiseState(promise2));
   EXPECT_TRUE(screen_lock.is_acquired());
   EXPECT_EQ(2U, state_record->active_locks_.size());
 }
@@ -72,7 +68,8 @@
   state_record->AcquireWakeLock(resolver);
   screen_lock.WaitForRequest();
 
-  EXPECT_EQ(v8::Promise::kPending, GetScriptPromiseState(promise));
+  EXPECT_EQ(v8::Promise::kPending,
+            ScriptPromiseUtils::GetPromiseState(promise));
   EXPECT_EQ(1U, state_record->active_locks_.size());
   EXPECT_TRUE(screen_lock.is_acquired());
 
@@ -80,8 +77,10 @@
   context.WaitForPromiseRejection(promise);
   screen_lock.WaitForCancelation();
 
-  EXPECT_EQ(v8::Promise::kRejected, GetScriptPromiseState(promise));
-  DOMException* dom_exception = GetDOMException(promise);
+  EXPECT_EQ(v8::Promise::kRejected,
+            ScriptPromiseUtils::GetPromiseState(promise));
+  DOMException* dom_exception =
+      ScriptPromiseUtils::GetPromiseResolutionAsDOMException(promise);
   ASSERT_NE(nullptr, dom_exception);
   EXPECT_EQ("AbortError", dom_exception->name());
 
@@ -111,14 +110,16 @@
   context.WaitForPromiseRejection(promise);
   screen_lock.WaitForCancelation();
 
-  EXPECT_EQ(v8::Promise::kRejected, GetScriptPromiseState(promise));
+  EXPECT_EQ(v8::Promise::kRejected,
+            ScriptPromiseUtils::GetPromiseState(promise));
   EXPECT_EQ(0U, state_record->active_locks_.size());
   EXPECT_FALSE(screen_lock.is_acquired());
 
   state_record->ReleaseWakeLock(resolver);
   test::RunPendingTasks();
 
-  EXPECT_EQ(v8::Promise::kRejected, GetScriptPromiseState(promise));
+  EXPECT_EQ(v8::Promise::kRejected,
+            ScriptPromiseUtils::GetPromiseState(promise));
   EXPECT_EQ(0U, state_record->active_locks_.size());
   EXPECT_FALSE(screen_lock.is_acquired());
 }
@@ -142,17 +143,22 @@
   state_record->AcquireWakeLock(resolver2);
   screen_lock.WaitForRequest();
 
-  EXPECT_EQ(v8::Promise::kPending, GetScriptPromiseState(promise1));
-  EXPECT_EQ(v8::Promise::kPending, GetScriptPromiseState(promise2));
+  EXPECT_EQ(v8::Promise::kPending,
+            ScriptPromiseUtils::GetPromiseState(promise1));
+  EXPECT_EQ(v8::Promise::kPending,
+            ScriptPromiseUtils::GetPromiseState(promise2));
   EXPECT_EQ(2U, state_record->active_locks_.size());
   EXPECT_TRUE(screen_lock.is_acquired());
 
   state_record->ReleaseWakeLock(resolver1);
   context.WaitForPromiseRejection(promise1);
 
-  EXPECT_EQ(v8::Promise::kRejected, GetScriptPromiseState(promise1));
-  EXPECT_EQ(v8::Promise::kPending, GetScriptPromiseState(promise2));
-  DOMException* dom_exception = GetDOMException(promise1);
+  EXPECT_EQ(v8::Promise::kRejected,
+            ScriptPromiseUtils::GetPromiseState(promise1));
+  EXPECT_EQ(v8::Promise::kPending,
+            ScriptPromiseUtils::GetPromiseState(promise2));
+  DOMException* dom_exception =
+      ScriptPromiseUtils::GetPromiseResolutionAsDOMException(promise1);
   ASSERT_NE(nullptr, dom_exception);
   EXPECT_EQ("AbortError", dom_exception->name());
 
@@ -172,12 +178,14 @@
       MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
   ScriptPromise promise = resolver->Promise();
 
-  EXPECT_EQ(v8::Promise::kPending, GetScriptPromiseState(promise));
+  EXPECT_EQ(v8::Promise::kPending,
+            ScriptPromiseUtils::GetPromiseState(promise));
 
   state_record->ReleaseWakeLock(resolver);
   context.WaitForPromiseRejection(promise);
 
-  EXPECT_EQ(v8::Promise::kRejected, GetScriptPromiseState(promise));
+  EXPECT_EQ(v8::Promise::kRejected,
+            ScriptPromiseUtils::GetPromiseState(promise));
   EXPECT_EQ(0U, state_record->active_locks_.size());
   EXPECT_FALSE(screen_lock.is_acquired());
 }
@@ -221,12 +229,16 @@
   context.WaitForPromiseRejection(promise2);
   system_lock.WaitForCancelation();
 
-  EXPECT_EQ(v8::Promise::kRejected, GetScriptPromiseState(promise1));
-  EXPECT_EQ(v8::Promise::kRejected, GetScriptPromiseState(promise2));
-  DOMException* dom_exception = GetDOMException(promise1);
+  EXPECT_EQ(v8::Promise::kRejected,
+            ScriptPromiseUtils::GetPromiseState(promise1));
+  EXPECT_EQ(v8::Promise::kRejected,
+            ScriptPromiseUtils::GetPromiseState(promise2));
+  DOMException* dom_exception =
+      ScriptPromiseUtils::GetPromiseResolutionAsDOMException(promise1);
   ASSERT_NE(nullptr, dom_exception);
   EXPECT_EQ("AbortError", dom_exception->name());
-  dom_exception = GetDOMException(promise2);
+  dom_exception =
+      ScriptPromiseUtils::GetPromiseResolutionAsDOMException(promise2);
   ASSERT_NE(nullptr, dom_exception);
   EXPECT_EQ("AbortError", dom_exception->name());
 
@@ -257,12 +269,16 @@
   context.WaitForPromiseRejection(promise1);
   context.WaitForPromiseRejection(promise2);
 
-  EXPECT_EQ(v8::Promise::kRejected, GetScriptPromiseState(promise1));
-  EXPECT_EQ(v8::Promise::kRejected, GetScriptPromiseState(promise2));
-  DOMException* dom_exception = GetDOMException(promise1);
+  EXPECT_EQ(v8::Promise::kRejected,
+            ScriptPromiseUtils::GetPromiseState(promise1));
+  EXPECT_EQ(v8::Promise::kRejected,
+            ScriptPromiseUtils::GetPromiseState(promise2));
+  DOMException* dom_exception =
+      ScriptPromiseUtils::GetPromiseResolutionAsDOMException(promise1);
   ASSERT_NE(nullptr, dom_exception);
   EXPECT_EQ("AbortError", dom_exception->name());
-  dom_exception = GetDOMException(promise2);
+  dom_exception =
+      ScriptPromiseUtils::GetPromiseResolutionAsDOMException(promise2);
   ASSERT_NE(nullptr, dom_exception);
   EXPECT_EQ("AbortError", dom_exception->name());
 
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock_test_utils.cc b/third_party/blink/renderer/modules/wake_lock/wake_lock_test_utils.cc
new file mode 100644
index 0000000..1e48ec2
--- /dev/null
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock_test_utils.cc
@@ -0,0 +1,144 @@
+// 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 "third_party/blink/renderer/modules/wake_lock/wake_lock_test_utils.h"
+
+#include "third_party/blink/renderer/bindings/core/v8/v8_dom_exception.h"
+#include "third_party/blink/renderer/platform/bindings/v8_binding.h"
+
+namespace blink {
+
+using mojom::blink::PermissionDescriptorPtr;
+using mojom::blink::PermissionStatus;
+
+// MockPermissionService
+
+MockPermissionService::MockPermissionService() = default;
+MockPermissionService::~MockPermissionService() = default;
+
+void MockPermissionService::BindRequest(mojo::ScopedMessagePipeHandle handle) {
+  DCHECK(!binding_.is_bound());
+  binding_.Bind(mojom::blink::PermissionServiceRequest(std::move(handle)));
+  binding_.set_connection_error_handler(WTF::Bind(
+      &MockPermissionService::OnConnectionError, WTF::Unretained(this)));
+}
+
+void MockPermissionService::SetPermissionResponse(WakeLockType type,
+                                                  PermissionStatus status) {
+  DCHECK(status == PermissionStatus::GRANTED ||
+         status == PermissionStatus::DENIED);
+  permission_responses_[static_cast<size_t>(type)] = status;
+}
+
+void MockPermissionService::OnConnectionError() {
+  binding_.Unbind();
+}
+
+bool MockPermissionService::GetWakeLockTypeFromDescriptor(
+    const PermissionDescriptorPtr& descriptor,
+    WakeLockType* output) {
+  if (!descriptor->extension || !descriptor->extension->is_wake_lock())
+    return false;
+  switch (descriptor->extension->get_wake_lock()->type) {
+    case mojom::blink::WakeLockType::kScreen:
+      *output = WakeLockType::kScreen;
+      return true;
+    case mojom::blink::WakeLockType::kSystem:
+      *output = WakeLockType::kSystem;
+      return true;
+    default:
+      return false;
+  }
+}
+
+void MockPermissionService::WaitForPermissionRequest(WakeLockType type) {
+  size_t pos = static_cast<size_t>(type);
+  DCHECK(!request_permission_callbacks_[pos]);
+  base::RunLoop run_loop;
+  request_permission_callbacks_[pos] = run_loop.QuitClosure();
+  run_loop.Run();
+}
+
+void MockPermissionService::HasPermission(PermissionDescriptorPtr permission,
+                                          HasPermissionCallback callback) {
+  WakeLockType type;
+  if (!GetWakeLockTypeFromDescriptor(permission, &type)) {
+    std::move(callback).Run(PermissionStatus::DENIED);
+    return;
+  }
+  size_t pos = static_cast<size_t>(type);
+  DCHECK(permission_responses_[pos].has_value());
+  std::move(callback).Run(
+      permission_responses_[pos].value_or(PermissionStatus::DENIED));
+}
+
+void MockPermissionService::RequestPermission(
+    PermissionDescriptorPtr permission,
+    bool user_gesture,
+    RequestPermissionCallback callback) {
+  WakeLockType type;
+  if (!GetWakeLockTypeFromDescriptor(permission, &type)) {
+    std::move(callback).Run(PermissionStatus::DENIED);
+    return;
+  }
+
+  size_t pos = static_cast<size_t>(type);
+  DCHECK(permission_responses_[pos].has_value());
+  if (request_permission_callbacks_[pos])
+    std::move(request_permission_callbacks_[pos]).Run();
+  std::move(callback).Run(
+      permission_responses_[pos].value_or(PermissionStatus::DENIED));
+}
+
+void MockPermissionService::RequestPermissions(
+    Vector<PermissionDescriptorPtr> permissions,
+    bool user_gesture,
+    mojom::blink::PermissionService::RequestPermissionsCallback) {
+  NOTREACHED();
+}
+
+void MockPermissionService::RevokePermission(PermissionDescriptorPtr permission,
+                                             RevokePermissionCallback) {
+  NOTREACHED();
+}
+
+void MockPermissionService::AddPermissionObserver(
+    PermissionDescriptorPtr permission,
+    PermissionStatus last_known_status,
+    mojom::blink::PermissionObserverPtr) {
+  NOTREACHED();
+}
+
+// ScriptPromiseUtils
+
+// static
+v8::Promise::PromiseState ScriptPromiseUtils::GetPromiseState(
+    const ScriptPromise& promise) {
+  return promise.V8Value().As<v8::Promise>()->State();
+}
+
+// static
+String ScriptPromiseUtils::GetPromiseResolutionAsString(
+    const ScriptPromise& promise) {
+  auto v8_promise = promise.V8Value().As<v8::Promise>();
+  if (v8_promise->State() == v8::Promise::kPending) {
+    return g_empty_string;
+  }
+  ScriptValue promise_result(promise.GetScriptValue().GetScriptState(),
+                             v8_promise->Result());
+  String value;
+  if (!promise_result.ToString(value)) {
+    return g_empty_string;
+  }
+  return value;
+}
+
+// static
+DOMException* ScriptPromiseUtils::GetPromiseResolutionAsDOMException(
+    const ScriptPromise& promise) {
+  return V8DOMException::ToImplWithTypeCheck(
+      promise.GetIsolate(), promise.V8Value().As<v8::Promise>()->Result());
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock_test_utils.h b/third_party/blink/renderer/modules/wake_lock/wake_lock_test_utils.h
index beba8a18..8b0d5e4 100644
--- a/third_party/blink/renderer/modules/wake_lock/wake_lock_test_utils.h
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock_test_utils.h
@@ -10,11 +10,13 @@
 
 #include "base/callback.h"
 #include "base/logging.h"
+#include "base/optional.h"
 #include "base/run_loop.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "services/device/public/mojom/wake_lock.mojom-blink.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
+#include "third_party/blink/public/mojom/permissions/permission.mojom-blink.h"
 #include "third_party/blink/public/mojom/wake_lock/wake_lock.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_function.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
@@ -134,11 +136,57 @@
     mock_wake_lock_[pos].Bind(std::move(request));
   }
 
-  MockWakeLock
-      mock_wake_lock_[static_cast<size_t>(WakeLockType::kMaxValue) + 1];
+  MockWakeLock mock_wake_lock_[kWakeLockTypeCount];
   mojo::BindingSet<mojom::blink::WakeLockService> bindings_;
 };
 
+// Mock PermissionService implementation. It only implements the bits required
+// by the Wake Lock code, and it mimics what we do in the content and chrome
+// layers: screen locks are always granted, system locks are always denied.
+class MockPermissionService final : public mojom::blink::PermissionService {
+ public:
+  MockPermissionService();
+  ~MockPermissionService() override;
+
+  void BindRequest(mojo::ScopedMessagePipeHandle handle);
+
+  void SetPermissionResponse(WakeLockType, mojom::blink::PermissionStatus);
+
+  void WaitForPermissionRequest(WakeLockType);
+
+ private:
+  bool GetWakeLockTypeFromDescriptor(
+      const mojom::blink::PermissionDescriptorPtr& descriptor,
+      WakeLockType* output);
+
+  // mojom::blink::PermissionService implementation
+  void HasPermission(mojom::blink::PermissionDescriptorPtr permission,
+                     HasPermissionCallback) override;
+  void RequestPermission(mojom::blink::PermissionDescriptorPtr permission,
+                         bool user_gesture,
+                         RequestPermissionCallback) override;
+  void RequestPermissions(
+      Vector<mojom::blink::PermissionDescriptorPtr> permissions,
+      bool user_gesture,
+      RequestPermissionsCallback) override;
+  void RevokePermission(mojom::blink::PermissionDescriptorPtr permission,
+                        RevokePermissionCallback) override;
+  void AddPermissionObserver(mojom::blink::PermissionDescriptorPtr permission,
+                             mojom::blink::PermissionStatus last_known_status,
+                             mojom::blink::PermissionObserverPtr) override;
+
+  void OnConnectionError();
+
+  mojo::Binding<mojom::blink::PermissionService> binding_{this};
+
+  base::Optional<mojom::blink::PermissionStatus>
+      permission_responses_[kWakeLockTypeCount];
+
+  base::OnceClosure request_permission_callbacks_[static_cast<size_t>(
+                                                      WakeLockType::kMaxValue) +
+                                                  1];
+};
+
 // Overrides requests for WakeLockService with MockWakeLockService instances.
 //
 // Usage:
@@ -161,11 +209,29 @@
         mojom::blink::WakeLockService::Name_,
         WTF::BindRepeating(&MockWakeLockService::BindRequest,
                            WTF::Unretained(mock_wake_lock_service)));
+    test_api.SetBinderForName(
+        mojom::blink::PermissionService::Name_,
+        WTF::BindRepeating(&MockPermissionService::BindRequest,
+                           WTF::Unretained(&permission_service_)));
   }
 
   Document* GetDocument() { return &testing_scope_.GetDocument(); }
   LocalFrame* Frame() { return &testing_scope_.GetFrame(); }
   ScriptState* GetScriptState() { return testing_scope_.GetScriptState(); }
+  MockPermissionService& GetPermissionService() { return permission_service_; }
+
+  // Synchronously waits for |promise| to be fulfilled.
+  ScriptPromise WaitForPromiseFulfillment(ScriptPromise promise) {
+    base::RunLoop run_loop;
+    ScriptPromise return_promise =
+        promise.Then(ClosureRunnerFunction::CreateFunction(
+            GetScriptState(), run_loop.QuitClosure()));
+    // Execute pending microtasks, otherwise it can take a few seconds for the
+    // promise to resolve.
+    v8::MicrotasksScope::PerformCheckpoint(GetScriptState()->GetIsolate());
+    run_loop.Run();
+    return return_promise;
+  }
 
   // Synchronously waits for |promise| to be rejected.
   void WaitForPromiseRejection(ScriptPromise promise) {
@@ -180,8 +246,9 @@
   }
 
  private:
-  // Helper class for WaitForPromiseRejection(). It provides a function that is
-  // invoked when a ScriptPromise is rejected that invokes |callback|.
+  // Helper class for WaitForPromise{Fulfillment,Rejection}(). It provides a
+  // function that is invoked when a ScriptPromise is rejected that invokes
+  // |callback|.
   class ClosureRunnerFunction final : public ScriptFunction {
    public:
     static v8::Local<v8::Function> CreateFunction(
@@ -206,14 +273,27 @@
     base::RepeatingClosure callback_;
   };
 
+  MockPermissionService permission_service_;
   V8TestingScope testing_scope_;
 };
 
-// Shorthand for getting a PromiseState out of a ScriptPromise.
-inline v8::Promise::PromiseState GetScriptPromiseState(
-    const ScriptPromise& promise) {
-  return promise.V8Value().As<v8::Promise>()->State();
-}
+// Utility functions to retrieve promise data out of a ScriptPromise.
+class ScriptPromiseUtils final {
+ public:
+  // Shorthand for getting a PromiseState out of a ScriptPromise.
+  static v8::Promise::PromiseState GetPromiseState(
+      const ScriptPromise& promise);
+
+  // Shorthand for getting a String out of a ScriptPromise. This assumes the
+  // promise has been resolved with a string. If anything wrong happens during
+  // the conversion, an empty string is returned.
+  static String GetPromiseResolutionAsString(const ScriptPromise&);
+
+  // Shorthand for getting a DOMException* out of a ScriptPromise. This assumes
+  // the promise has been resolved with a DOMException. If the conversion fails,
+  // nullptr is returned.
+  static DOMException* GetPromiseResolutionAsDOMException(const ScriptPromise&);
+};
 
 }  // namespace blink
 
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock_type.h b/third_party/blink/renderer/modules/wake_lock/wake_lock_type.h
index 26a6087..09fe8e6 100644
--- a/third_party/blink/renderer/modules/wake_lock/wake_lock_type.h
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock_type.h
@@ -26,6 +26,11 @@
 // https://w3c.github.io/wake-lock/#the-wakelocktype-enum
 enum class WakeLockType : int8_t { kScreen, kSystem, kMaxValue = kSystem };
 
+// Useful for creating arrays with size N, where N is the number of different
+// wake lock types.
+constexpr size_t kWakeLockTypeCount =
+    static_cast<size_t>(WakeLockType::kMaxValue) + 1;
+
 MODULES_EXPORT device::mojom::blink::WakeLockType ToMojomWakeLockType(
     WakeLockType type);
 
diff --git a/third_party/blink/renderer/modules/webaudio/analyser_node.idl b/third_party/blink/renderer/modules/webaudio/analyser_node.idl
index d6578a7..8a299b4 100644
--- a/third_party/blink/renderer/modules/webaudio/analyser_node.idl
+++ b/third_party/blink/renderer/modules/webaudio/analyser_node.idl
@@ -25,6 +25,7 @@
 
 // See https://webaudio.github.io/web-audio-api/#analysernode
 [
+    Exposed=Window,
     Constructor(BaseAudioContext context, optional AnalyserOptions options),
     RaisesException=Constructor,
     Measure
diff --git a/third_party/blink/renderer/modules/webaudio/audio_buffer.idl b/third_party/blink/renderer/modules/webaudio/audio_buffer.idl
index 650c785f..d5546d6 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_buffer.idl
+++ b/third_party/blink/renderer/modules/webaudio/audio_buffer.idl
@@ -28,6 +28,7 @@
 
 // See https://webaudio.github.io/web-audio-api/#AudioBuffer
 [
+    Exposed=Window,
     Constructor(AudioBufferOptions options),
     RaisesException=Constructor,
     Measure
diff --git a/third_party/blink/renderer/modules/webaudio/audio_buffer_source_node.idl b/third_party/blink/renderer/modules/webaudio/audio_buffer_source_node.idl
index ed3550e..e41167f6 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_buffer_source_node.idl
+++ b/third_party/blink/renderer/modules/webaudio/audio_buffer_source_node.idl
@@ -26,6 +26,7 @@
 // A cached (non-streamed), memory-resident audio source
 // See https://webaudio.github.io/web-audio-api/#AudioBufferSourceNode
 [
+    Exposed=Window,
     Constructor(BaseAudioContext context, optional AudioBufferSourceOptions options),
     RaisesException=Constructor,
     ActiveScriptWrappable,
diff --git a/third_party/blink/renderer/modules/webaudio/audio_context.idl b/third_party/blink/renderer/modules/webaudio/audio_context.idl
index e386af9..41fd88b 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_context.idl
+++ b/third_party/blink/renderer/modules/webaudio/audio_context.idl
@@ -32,6 +32,7 @@
 
 // See https://webaudio.github.io/web-audio-api/#AudioContext
 [
+    Exposed=Window,
     ActiveScriptWrappable,
     Constructor(optional AudioContextOptions contextOptions),
     ConstructorCallWith=Document,
diff --git a/third_party/blink/renderer/modules/webaudio/audio_destination_node.idl b/third_party/blink/renderer/modules/webaudio/audio_destination_node.idl
index 29911739..ff155bf 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_destination_node.idl
+++ b/third_party/blink/renderer/modules/webaudio/audio_destination_node.idl
@@ -24,6 +24,7 @@
  */
 
 // See https://webaudio.github.io/web-audio-api/#AudioDestinationNode
+[Exposed=Window]
 interface AudioDestinationNode : AudioNode {
     readonly attribute unsigned long maxChannelCount;
 };
diff --git a/third_party/blink/renderer/modules/webaudio/audio_listener.idl b/third_party/blink/renderer/modules/webaudio/audio_listener.idl
index de2f898..b48975c4 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_listener.idl
+++ b/third_party/blink/renderer/modules/webaudio/audio_listener.idl
@@ -27,6 +27,7 @@
  */
 
 // See https://webaudio.github.io/web-audio-api/#audiolistener
+[Exposed=Window]
 interface AudioListener {
     [RaisesException, MeasureAs=AudioListenerSetPosition] void setPosition(float x, float y, float z);
     [RaisesException, MeasureAs=AudioListenerSetOrientation] void setOrientation(float x, float y, float z, float xUp, float yUp, float zUp);
diff --git a/third_party/blink/renderer/modules/webaudio/audio_node.idl b/third_party/blink/renderer/modules/webaudio/audio_node.idl
index 3bf9da4..277884e 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_node.idl
+++ b/third_party/blink/renderer/modules/webaudio/audio_node.idl
@@ -36,6 +36,7 @@
     "discrete"
 };
 
+[Exposed=Window]
 interface AudioNode : EventTarget {
     [RaisesException, MeasureAs=AudioNodeConnectToAudioNode] AudioNode connect(AudioNode destination, optional unsigned long output = 0, optional unsigned long input = 0);
     [RaisesException, MeasureAs=AudioNodeConnectToAudioParam] void connect(AudioParam destination, optional unsigned long output = 0);
diff --git a/third_party/blink/renderer/modules/webaudio/audio_param.idl b/third_party/blink/renderer/modules/webaudio/audio_param.idl
index 002c9264..b21f6f81b 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_param.idl
+++ b/third_party/blink/renderer/modules/webaudio/audio_param.idl
@@ -32,6 +32,7 @@
    "k-rate"
 };
 
+[Exposed=Window]
 interface AudioParam {
     [RaisesException=Setter] attribute float value;
     [RaisesException=Setter] attribute AutomationRate automationRate;
diff --git a/third_party/blink/renderer/modules/webaudio/audio_param_map.idl b/third_party/blink/renderer/modules/webaudio/audio_param_map.idl
index 4055562..cfef676b 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_param_map.idl
+++ b/third_party/blink/renderer/modules/webaudio/audio_param_map.idl
@@ -4,6 +4,7 @@
 
 // See https://webaudio.github.io/web-audio-api/#audioparammap
 
+[Exposed=Window]
 interface AudioParamMap {
     readonly maplike<DOMString, AudioParam>;
 };
diff --git a/third_party/blink/renderer/modules/webaudio/audio_processing_event.idl b/third_party/blink/renderer/modules/webaudio/audio_processing_event.idl
index 947990e..8ca38a5 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_processing_event.idl
+++ b/third_party/blink/renderer/modules/webaudio/audio_processing_event.idl
@@ -25,6 +25,7 @@
 
 // See https://webaudio.github.io/web-audio-api/#audioprocessingevent
 [
+    Exposed=Window,
     Constructor(DOMString type, AudioProcessingEventInit eventInitDict)
 ]
 interface AudioProcessingEvent : Event {
diff --git a/third_party/blink/renderer/modules/webaudio/audio_scheduled_source_node.idl b/third_party/blink/renderer/modules/webaudio/audio_scheduled_source_node.idl
index 07466238..951c5a0 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_scheduled_source_node.idl
+++ b/third_party/blink/renderer/modules/webaudio/audio_scheduled_source_node.idl
@@ -4,6 +4,7 @@
 
 // See https://webaudio.github.io/web-audio-api/#AudioScheduledSourceNode
 [
+    Exposed=Window,
     ActiveScriptWrappable
 ]
 interface AudioScheduledSourceNode : AudioNode {
diff --git a/third_party/blink/renderer/modules/webaudio/audio_worklet.idl b/third_party/blink/renderer/modules/webaudio/audio_worklet.idl
index e24cf62..272c304 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_worklet.idl
+++ b/third_party/blink/renderer/modules/webaudio/audio_worklet.idl
@@ -5,6 +5,7 @@
 // See https://webaudio.github.io/web-audio-api/#audioworklet
 
 [
+    Exposed=Window,
     SecureContext
 ] interface AudioWorklet : Worklet {
 };
diff --git a/third_party/blink/renderer/modules/webaudio/audio_worklet_node.idl b/third_party/blink/renderer/modules/webaudio/audio_worklet_node.idl
index 4772e75..ac7e6dae 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_worklet_node.idl
+++ b/third_party/blink/renderer/modules/webaudio/audio_worklet_node.idl
@@ -5,6 +5,7 @@
 // See https://webaudio.github.io/web-audio-api/#audioworkletnode
 
 [
+    Exposed=Window,
     ActiveScriptWrappable,
     Constructor(BaseAudioContext context, DOMString name, optional AudioWorkletNodeOptions options),
     ConstructorCallWith=ScriptState,
diff --git a/third_party/blink/renderer/modules/webaudio/base_audio_context.idl b/third_party/blink/renderer/modules/webaudio/base_audio_context.idl
index a1d7cd6..3a9bd6a 100644
--- a/third_party/blink/renderer/modules/webaudio/base_audio_context.idl
+++ b/third_party/blink/renderer/modules/webaudio/base_audio_context.idl
@@ -14,6 +14,7 @@
 callback DecodeSuccessCallback = void (AudioBuffer decodedData);
 
 [
+    Exposed=Window,
     ActiveScriptWrappable
 ] interface BaseAudioContext : EventTarget {
     // All rendered audio ultimately connects to destination, which represents the audio hardware.
diff --git a/third_party/blink/renderer/modules/webaudio/biquad_filter_node.idl b/third_party/blink/renderer/modules/webaudio/biquad_filter_node.idl
index 63fb78f..f507394 100644
--- a/third_party/blink/renderer/modules/webaudio/biquad_filter_node.idl
+++ b/third_party/blink/renderer/modules/webaudio/biquad_filter_node.idl
@@ -36,6 +36,7 @@
 };
 
 [
+    Exposed=Window,
     Constructor(BaseAudioContext context, optional BiquadFilterOptions options),
     RaisesException=Constructor,
     Measure
diff --git a/third_party/blink/renderer/modules/webaudio/channel_merger_node.idl b/third_party/blink/renderer/modules/webaudio/channel_merger_node.idl
index 4998b14..f0639ea 100644
--- a/third_party/blink/renderer/modules/webaudio/channel_merger_node.idl
+++ b/third_party/blink/renderer/modules/webaudio/channel_merger_node.idl
@@ -28,6 +28,7 @@
 
 // See https://webaudio.github.io/web-audio-api/#channelmergernode
 [
+    Exposed=Window,
     Constructor(BaseAudioContext context, optional ChannelMergerOptions options),
     RaisesException=Constructor,
     Measure
diff --git a/third_party/blink/renderer/modules/webaudio/channel_splitter_node.idl b/third_party/blink/renderer/modules/webaudio/channel_splitter_node.idl
index 8f8d721..a91d7e76 100644
--- a/third_party/blink/renderer/modules/webaudio/channel_splitter_node.idl
+++ b/third_party/blink/renderer/modules/webaudio/channel_splitter_node.idl
@@ -25,6 +25,7 @@
 
 // See https://webaudio.github.io/web-audio-api/#channelsplitternode
 [
+    Exposed=Window,
     Constructor(BaseAudioContext context, optional ChannelSplitterOptions options),
     RaisesException=Constructor,
     Measure
diff --git a/third_party/blink/renderer/modules/webaudio/constant_source_node.idl b/third_party/blink/renderer/modules/webaudio/constant_source_node.idl
index 8342f2d..6a21393a 100644
--- a/third_party/blink/renderer/modules/webaudio/constant_source_node.idl
+++ b/third_party/blink/renderer/modules/webaudio/constant_source_node.idl
@@ -4,6 +4,7 @@
 
 // See https://webaudio.github.io/web-audio-api/#ConstantSourceNode
 [
+    Exposed=Window,
     Constructor(BaseAudioContext context, optional ConstantSourceOptions options),
     RaisesException=Constructor,
     ActiveScriptWrappable,
diff --git a/third_party/blink/renderer/modules/webaudio/convolver_node.idl b/third_party/blink/renderer/modules/webaudio/convolver_node.idl
index 53559c56..f1dcda3 100644
--- a/third_party/blink/renderer/modules/webaudio/convolver_node.idl
+++ b/third_party/blink/renderer/modules/webaudio/convolver_node.idl
@@ -26,6 +26,7 @@
 // A linear convolution effect
 // See https://webaudio.github.io/web-audio-api/#ConvolverNode
 [
+    Exposed=Window,
     Constructor(BaseAudioContext context, optional ConvolverOptions options),
     RaisesException=Constructor,
     Measure
diff --git a/third_party/blink/renderer/modules/webaudio/delay_node.idl b/third_party/blink/renderer/modules/webaudio/delay_node.idl
index cc9d24c..9dc6923 100644
--- a/third_party/blink/renderer/modules/webaudio/delay_node.idl
+++ b/third_party/blink/renderer/modules/webaudio/delay_node.idl
@@ -25,6 +25,7 @@
 
 // See https://webaudio.github.io/web-audio-api/#DelayNode
 [
+    Exposed=Window,
     Constructor(BaseAudioContext context, optional DelayOptions options),
     RaisesException=Constructor,
     Measure
diff --git a/third_party/blink/renderer/modules/webaudio/dynamics_compressor_node.idl b/third_party/blink/renderer/modules/webaudio/dynamics_compressor_node.idl
index 41c9008..adcbed3 100644
--- a/third_party/blink/renderer/modules/webaudio/dynamics_compressor_node.idl
+++ b/third_party/blink/renderer/modules/webaudio/dynamics_compressor_node.idl
@@ -25,6 +25,7 @@
 
 // See https://webaudio.github.io/web-audio-api/#dynamicscompressornode
 [
+    Exposed=Window,
     Constructor(BaseAudioContext context, optional DynamicsCompressorOptions options),
     RaisesException=Constructor,
     Measure
diff --git a/third_party/blink/renderer/modules/webaudio/gain_node.idl b/third_party/blink/renderer/modules/webaudio/gain_node.idl
index 4eb0275e..78a68d6 100644
--- a/third_party/blink/renderer/modules/webaudio/gain_node.idl
+++ b/third_party/blink/renderer/modules/webaudio/gain_node.idl
@@ -25,6 +25,7 @@
 
 // See https://webaudio.github.io/web-audio-api/#gainnode
 [
+    Exposed=Window,
     Constructor(BaseAudioContext context, optional GainOptions options),
     RaisesException=Constructor,
     Measure
diff --git a/third_party/blink/renderer/modules/webaudio/iir_filter_node.idl b/third_party/blink/renderer/modules/webaudio/iir_filter_node.idl
index b98d3196..6bd6e6d 100644
--- a/third_party/blink/renderer/modules/webaudio/iir_filter_node.idl
+++ b/third_party/blink/renderer/modules/webaudio/iir_filter_node.idl
@@ -4,6 +4,7 @@
 
 // See https://webaudio.github.io/web-audio-api/#iirfilternode
 [
+    Exposed=Window,
     Constructor(BaseAudioContext context, IIRFilterOptions options),
     RaisesException=Constructor,
     Measure
diff --git a/third_party/blink/renderer/modules/webaudio/media_element_audio_source_node.idl b/third_party/blink/renderer/modules/webaudio/media_element_audio_source_node.idl
index 9916261..22552cc8 100644
--- a/third_party/blink/renderer/modules/webaudio/media_element_audio_source_node.idl
+++ b/third_party/blink/renderer/modules/webaudio/media_element_audio_source_node.idl
@@ -25,6 +25,7 @@
 
 // See https://webaudio.github.io/web-audio-api/#mediaelementaudiosourcenode
 [
+    Exposed=Window,
     Constructor(AudioContext context, MediaElementAudioSourceOptions options),
     RaisesException=Constructor,
     Measure
diff --git a/third_party/blink/renderer/modules/webaudio/media_stream_audio_destination_node.idl b/third_party/blink/renderer/modules/webaudio/media_stream_audio_destination_node.idl
index d4bc924..c291a7e 100644
--- a/third_party/blink/renderer/modules/webaudio/media_stream_audio_destination_node.idl
+++ b/third_party/blink/renderer/modules/webaudio/media_stream_audio_destination_node.idl
@@ -25,6 +25,7 @@
 
 // See https://webaudio.github.io/web-audio-api/#mediastreamaudiodestinationnode
 [
+    Exposed=Window,
     Constructor(AudioContext context, optional AudioNodeOptions options),
     RaisesException=Constructor,
     Measure
diff --git a/third_party/blink/renderer/modules/webaudio/offline_audio_completion_event.idl b/third_party/blink/renderer/modules/webaudio/offline_audio_completion_event.idl
index 368ff9df..504d8fe2 100644
--- a/third_party/blink/renderer/modules/webaudio/offline_audio_completion_event.idl
+++ b/third_party/blink/renderer/modules/webaudio/offline_audio_completion_event.idl
@@ -25,6 +25,7 @@
 
 // See https://webaudio.github.io/web-audio-api/#offlineaudiocompletionevent
 [
+    Exposed=Window,
     Constructor(DOMString type, OfflineAudioCompletionEventInit eventInitDict)
 ]
 interface OfflineAudioCompletionEvent : Event {
diff --git a/third_party/blink/renderer/modules/webaudio/offline_audio_context.idl b/third_party/blink/renderer/modules/webaudio/offline_audio_context.idl
index 3c57739..7832c12 100644
--- a/third_party/blink/renderer/modules/webaudio/offline_audio_context.idl
+++ b/third_party/blink/renderer/modules/webaudio/offline_audio_context.idl
@@ -25,6 +25,7 @@
 
 // See https://webaudio.github.io/web-audio-api/#OfflineAudioContext
 [
+    Exposed=Window,
     Constructor(unsigned long numberOfChannels, unsigned long numberOfFrames, float sampleRate),
     Constructor(OfflineAudioContextOptions options),
     ConstructorCallWith=ExecutionContext,
diff --git a/third_party/blink/renderer/modules/webaudio/oscillator_node.idl b/third_party/blink/renderer/modules/webaudio/oscillator_node.idl
index d5cf7b82..328f042 100644
--- a/third_party/blink/renderer/modules/webaudio/oscillator_node.idl
+++ b/third_party/blink/renderer/modules/webaudio/oscillator_node.idl
@@ -34,6 +34,7 @@
 
 // OscillatorNode is an audio generator of periodic waveforms.
 [
+    Exposed=Window,
     Constructor(BaseAudioContext context, optional OscillatorOptions options),
     RaisesException=Constructor,
     Measure
diff --git a/third_party/blink/renderer/modules/webaudio/panner_node.idl b/third_party/blink/renderer/modules/webaudio/panner_node.idl
index 25a19fc..4a0d709 100644
--- a/third_party/blink/renderer/modules/webaudio/panner_node.idl
+++ b/third_party/blink/renderer/modules/webaudio/panner_node.idl
@@ -36,6 +36,7 @@
 };
 
 [
+    Exposed=Window,
     Constructor(BaseAudioContext context, optional PannerOptions options),
     RaisesException=Constructor,
     Measure
diff --git a/third_party/blink/renderer/modules/webaudio/periodic_wave.idl b/third_party/blink/renderer/modules/webaudio/periodic_wave.idl
index f640cfa..6dcbc504c11 100644
--- a/third_party/blink/renderer/modules/webaudio/periodic_wave.idl
+++ b/third_party/blink/renderer/modules/webaudio/periodic_wave.idl
@@ -26,6 +26,7 @@
 // PeriodicWave represents a periodic audio waveform given by its Fourier coefficients.
 // See https://webaudio.github.io/web-audio-api/#periodicwave
 [
+    Exposed=Window,
     Constructor(BaseAudioContext context, optional PeriodicWaveOptions options),
     RaisesException=Constructor,
     Measure
diff --git a/third_party/blink/renderer/modules/webaudio/script_processor_node.idl b/third_party/blink/renderer/modules/webaudio/script_processor_node.idl
index 43b8b00..2c10bca 100644
--- a/third_party/blink/renderer/modules/webaudio/script_processor_node.idl
+++ b/third_party/blink/renderer/modules/webaudio/script_processor_node.idl
@@ -26,6 +26,7 @@
 // See https://webaudio.github.io/web-audio-api/#scriptprocessornode
 // For real-time audio stream synthesis/processing in JavaScript
 [
+    Exposed=Window,
     ActiveScriptWrappable
 ] interface ScriptProcessorNode : AudioNode {
     // Rendering callback
diff --git a/third_party/blink/renderer/modules/webaudio/stereo_panner_node.idl b/third_party/blink/renderer/modules/webaudio/stereo_panner_node.idl
index 2753d91..ce9dedbf 100644
--- a/third_party/blink/renderer/modules/webaudio/stereo_panner_node.idl
+++ b/third_party/blink/renderer/modules/webaudio/stereo_panner_node.idl
@@ -4,6 +4,7 @@
 
 // See https://webaudio.github.io/web-audio-api/#stereopannernode
 [
+    Exposed=Window,
     Constructor(BaseAudioContext context, optional StereoPannerOptions options),
     RaisesException=Constructor,
     Measure
diff --git a/third_party/blink/renderer/modules/webaudio/wave_shaper_node.idl b/third_party/blink/renderer/modules/webaudio/wave_shaper_node.idl
index bfecb7e3..19f396c 100644
--- a/third_party/blink/renderer/modules/webaudio/wave_shaper_node.idl
+++ b/third_party/blink/renderer/modules/webaudio/wave_shaper_node.idl
@@ -31,6 +31,7 @@
 };
 
 [
+    Exposed=Window,
     Constructor(BaseAudioContext context, optional WaveShaperOptions options),
     RaisesException=Constructor,
     Measure
diff --git a/third_party/blink/renderer/platform/exported/web_runtime_features.cc b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
index 2a41297e..c162d66 100644
--- a/third_party/blink/renderer/platform/exported/web_runtime_features.cc
+++ b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
@@ -39,10 +39,6 @@
   RuntimeEnabledFeatures::SetHeapIncrementalMarkingEnabled(enable);
 }
 
-void WebRuntimeFeatures::EnableBloatedRendererDetection(bool enable) {
-  RuntimeEnabledFeatures::SetBloatedRendererDetectionEnabled(enable);
-}
-
 void WebRuntimeFeatures::EnableBlockingFocusWithoutUserActivation(bool enable) {
   RuntimeEnabledFeatures::SetBlockingFocusWithoutUserActivationEnabled(enable);
 }
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
index 3f423b6a..a02cc968 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
@@ -550,7 +550,7 @@
 
   scoped_refptr<StaticBitmapImage> Snapshot() override {
     TRACE_EVENT0("blink", "CanvasResourceProviderSharedImage::Snapshot");
-    if (IsGpuContextLost())
+    if (!IsValid())
       return nullptr;
 
     EndWriteAccess();
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
index 3102466a..8277835 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
@@ -317,7 +317,6 @@
       Pointee(DrawsRectangle(FloatRect(0, 0, 100, 100), Color::kWhite)));
   EXPECT_EQ(Translation(50, -50), child->ScreenSpaceTransform());
   EXPECT_EQ(gfx::Size(100, 100), child->bounds());
-  EXPECT_FALSE(GetTransformNode(child).transform_changed);
 }
 
 TEST_P(PaintArtifactCompositorTest, OneTransform) {
@@ -337,7 +336,6 @@
   ASSERT_EQ(2u, ContentLayerCount());
   {
     const cc::Layer* layer = ContentLayerAt(0);
-    EXPECT_FALSE(GetTransformNode(layer).transform_changed);
 
     Vector<RectWithColor> rects_with_color;
     rects_with_color.push_back(
@@ -353,7 +351,6 @@
   }
   {
     const cc::Layer* layer = ContentLayerAt(1);
-    EXPECT_FALSE(GetTransformNode(layer).transform_changed);
     EXPECT_THAT(
         layer->GetPicture(),
         Pointee(DrawsRectangle(FloatRect(0, 0, 100, 100), Color::kGray)));
@@ -379,7 +376,6 @@
   ASSERT_EQ(2u, ContentLayerCount());
   {
     const cc::Layer* layer = ContentLayerAt(0);
-    EXPECT_FALSE(GetTransformNode(layer).transform_changed);
 
     Vector<RectWithColor> rects_with_color;
     rects_with_color.push_back(
@@ -395,7 +391,6 @@
   }
   {
     const cc::Layer* layer = ContentLayerAt(1);
-    EXPECT_FALSE(GetTransformNode(layer).transform_changed);
     EXPECT_THAT(
         layer->GetPicture(),
         Pointee(DrawsRectangle(FloatRect(0, 0, 100, 100), Color::kGray)));
@@ -422,7 +417,6 @@
   ASSERT_EQ(2u, ContentLayerCount());
   {
     const cc::Layer* layer = ContentLayerAt(0);
-    EXPECT_FALSE(GetTransformNode(layer).transform_changed);
     EXPECT_THAT(
         layer->GetPicture(),
         Pointee(DrawsRectangle(FloatRect(0, 0, 300, 200), Color::kWhite)));
@@ -432,7 +426,6 @@
   }
   {
     const cc::Layer* layer = ContentLayerAt(1);
-    EXPECT_FALSE(GetTransformNode(layer).transform_changed);
     EXPECT_THAT(
         layer->GetPicture(),
         Pointee(DrawsRectangle(FloatRect(0, 0, 300, 200), Color::kBlack)));
diff --git a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
index f187b8a..674d927 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
@@ -106,6 +106,7 @@
                                                      origin.Z());
   }
   compositor_node.needs_local_transform_update = true;
+  compositor_node.transform_changed = true;
 }
 
 static void AdjustPageScaleToUsePostLocal(cc::TransformNode& page_scale) {
@@ -117,6 +118,7 @@
   page_scale.post_local.matrix() = page_scale.local.matrix();
   page_scale.pre_local.matrix().setIdentity();
   page_scale.local.matrix().setIdentity();
+  page_scale.transform_changed = true;
 }
 
 static void SetTransformTreePageScaleFactor(
@@ -176,7 +178,6 @@
   property_trees->scroll_tree.SetScrollOffset(
       scroll_node->GetCompositorElementId(), cc_transform->scroll_offset);
 
-  cc_transform->transform_changed = true;
   property_trees->transform_tree.set_needs_update(true);
   property_trees->scroll_tree.set_needs_update(true);
   return true;
@@ -201,7 +202,6 @@
   // flag, we should clear it to let the compositor respect the new value.
   cc_transform->is_currently_animating = false;
 
-  cc_transform->transform_changed = true;
   property_trees->transform_tree.set_needs_update(true);
   return true;
 }
@@ -221,7 +221,6 @@
 
   SetTransformTreePageScaleFactor(&property_trees->transform_tree,
                                   cc_transform);
-  cc_transform->transform_changed = true;
   property_trees->transform_tree.set_needs_update(true);
   return true;
 }
diff --git a/third_party/blink/renderer/platform/graphics/paint/cull_rect.cc b/third_party/blink/renderer/platform/graphics/paint/cull_rect.cc
index 0bf9ce6..a520aee 100644
--- a/third_party/blink/renderer/platform/graphics/paint/cull_rect.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/cull_rect.cc
@@ -103,7 +103,8 @@
   for (const auto* t = &destination; t != &source; t = t->Parent()) {
     if (!t) {
       // |source| is not an ancestor of |destination|. Simply map.
-      GeometryMapper::SourceToDestinationRect(source, destination, rect_);
+      if (!IsInfinite())
+        GeometryMapper::SourceToDestinationRect(source, destination, rect_);
       return;
     }
     if (t->ScrollNode())
diff --git a/third_party/blink/renderer/platform/graphics/paint/cull_rect_test.cc b/third_party/blink/renderer/platform/graphics/paint/cull_rect_test.cc
index 5045c74..e965991 100644
--- a/third_party/blink/renderer/platform/graphics/paint/cull_rect_test.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/cull_rect_test.cc
@@ -349,6 +349,10 @@
   // Should ignore old_cull_rect.
   cull_rect2.ApplyTransforms(*t2, *t1, old_cull_rect);
   EXPECT_EQ(cull_rect1, cull_rect2);
+
+  CullRect infinite = CullRect::Infinite();
+  infinite.ApplyTransforms(*t2, *t1, base::nullopt);
+  EXPECT_TRUE(infinite.IsInfinite());
 }
 
 TEST_F(CullRectTest, ApplyTransformsSmallScrollContentsAfterBigScrollContents) {
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 2c14fde..1fd5181 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -180,15 +180,6 @@
       name: "BlinkRuntimeCallStats",
     },
     {
-      name: "BloatedRendererDetection",
-      status: "experimental",
-    },
-    {
-      // Detects bloated renderers even if the uptime is small.
-      // Useful for local testing, not intended for production.
-      name: "BloatedRendererDetectionSkipUptimeCheck",
-    },
-    {
       // Adding simpler reading methods - stream(), text(), and arrayBuffer() -
       // to the Blob interface. See: https://github.com/w3c/FileAPI/pull/117
       name: "BlobReadMethods",
@@ -1152,7 +1143,7 @@
     },
     {
       name: "PauseExecutionContextOnBackgroundFreeze",
-      status: "experimental",
+      status: "stable",
     },
     {
       name: "PaymentApp",
@@ -1384,10 +1375,6 @@
       status: "stable",
     },
     {
-      name: "ScriptedTaskQueue",
-      status: "experimental"
-    },
-    {
       name: "ScriptStreamingOnPreload",
     },
     // Serialize and restore scroll anchors.
diff --git a/third_party/blink/renderer/platform/scheduler/common/thread.cc b/third_party/blink/renderer/platform/scheduler/common/thread.cc
index 09aadc79..efb9f22 100644
--- a/third_party/blink/renderer/platform/scheduler/common/thread.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/thread.cc
@@ -32,7 +32,7 @@
 // Controls whether we use ThreadPriority::DISPLAY for compositor thread.
 const base::Feature kBlinkCompositorUseDisplayThreadPriority {
   "BlinkCompositorUseDisplayThreadPriority",
-#if defined(OS_ANDROID) || defined(OS_CHROMEOS) || defined(USE_OZONE)
+#if defined(OS_ANDROID)
       base::FEATURE_ENABLED_BY_DEFAULT
 #else
       base::FEATURE_DISABLED_BY_DEFAULT
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
index e2704d4d..39c9b8c2 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
@@ -410,10 +410,6 @@
     case TaskType::kNetworkingControl:
       // Loading task queues are handled separately.
       return base::nullopt;
-    case TaskType::kExperimentalWebSchedulingUserInteraction:
-    case TaskType::kExperimentalWebSchedulingBestEffort:
-      // WebScheduling queues are handled separately.
-      return base::nullopt;
     // Throttling following tasks may break existing web pages, so tentatively
     // these are unthrottled.
     // TODO(nhiroki): Throttle them again after we're convinced that it's safe
@@ -522,14 +518,6 @@
       return frame_task_queue_controller_->InspectorTaskQueue();
     case TaskType::kInternalContentCapture:
       return frame_task_queue_controller_->BestEffortTaskQueue();
-    case TaskType::kExperimentalWebSchedulingUserInteraction:
-      return frame_task_queue_controller_->ExperimentalWebSchedulingTaskQueue(
-          FrameTaskQueueController::WebSchedulingTaskQueueType::
-              kWebSchedulingUserVisiblePriority);
-    case TaskType::kExperimentalWebSchedulingBestEffort:
-      return frame_task_queue_controller_->ExperimentalWebSchedulingTaskQueue(
-          FrameTaskQueueController::WebSchedulingTaskQueueType::
-              kWebSchedulingBestEffortPriority);
     default:
       // Non-loading task queue.
       DCHECK_LT(static_cast<size_t>(type),
@@ -994,16 +982,6 @@
     }
   }
 
-  if (task_queue->queue_type() ==
-      MainThreadTaskQueue::QueueType::kWebSchedulingUserInteraction) {
-    return TaskQueue::QueuePriority::kNormalPriority;
-  }
-
-  if (task_queue->queue_type() ==
-      MainThreadTaskQueue::QueueType::kWebSchedulingBestEffort) {
-    return TaskQueue::QueuePriority::kLowPriority;
-  }
-
   return task_queue->queue_type() ==
                  MainThreadTaskQueue::QueueType::kFrameLoadingControl
              ? TaskQueue::QueuePriority::kHighPriority
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller.cc
index 0526fc1..f1d31aa 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller.cc
@@ -10,7 +10,6 @@
 #include "base/callback.h"
 #include "base/logging.h"
 #include "base/trace_event/traced_value.h"
-#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/scheduler/common/tracing_helper.h"
 #include "third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h"
 #include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
@@ -90,16 +89,6 @@
 }
 
 scoped_refptr<MainThreadTaskQueue>
-FrameTaskQueueController::ExperimentalWebSchedulingTaskQueue(
-    WebSchedulingTaskQueueType task_queue_type) {
-  if (!web_scheduling_task_queues_[task_queue_type])
-    CreateWebSchedulingTaskQueue(task_queue_type);
-
-  DCHECK(web_scheduling_task_queues_[task_queue_type]);
-  return web_scheduling_task_queues_[task_queue_type];
-}
-
-scoped_refptr<MainThreadTaskQueue>
 FrameTaskQueueController::NonLoadingTaskQueue(
     MainThreadTaskQueue::QueueTraits queue_traits) {
   if (!non_loading_task_queues_.Contains(queue_traits.Key()))
@@ -124,42 +113,6 @@
   TaskQueueCreated(loading_task_queue_);
 }
 
-void FrameTaskQueueController::CreateWebSchedulingTaskQueue(
-    WebSchedulingTaskQueueType task_queue_type) {
-  DCHECK(RuntimeEnabledFeatures::ScriptedTaskQueueEnabled());
-  DCHECK(!web_scheduling_task_queues_[task_queue_type]);
-  // |main_thread_scheduler_impl_| can be null in unit tests.
-  DCHECK(main_thread_scheduler_impl_);
-
-  MainThreadTaskQueue::QueueType main_thread_queue_type =
-      MainThreadTaskQueue::QueueType::kDefault;
-  switch (task_queue_type) {
-    case kWebSchedulingUserVisiblePriority:
-      main_thread_queue_type =
-          MainThreadTaskQueue::QueueType::kWebSchedulingUserInteraction;
-      break;
-    case kWebSchedulingBestEffortPriority:
-      main_thread_queue_type =
-          MainThreadTaskQueue::QueueType::kWebSchedulingBestEffort;
-      break;
-    case kWebSchedulingPriorityCount:
-      NOTREACHED();
-  }
-
-  scoped_refptr<MainThreadTaskQueue> task_queue =
-      main_thread_scheduler_impl_->NewTaskQueue(
-          MainThreadTaskQueue::QueueCreationParams(main_thread_queue_type)
-              .SetCanBePaused(true)
-              .SetCanBeFrozen(true)
-              .SetCanBeDeferred(task_queue_type !=
-                                kWebSchedulingUserVisiblePriority)
-              .SetCanBeThrottled(true)
-              .SetFrameScheduler(frame_scheduler_impl_));
-
-  TaskQueueCreated(task_queue);
-  web_scheduling_task_queues_[task_queue_type] = task_queue;
-}
-
 void FrameTaskQueueController::CreateLoadingControlTaskQueue() {
   DCHECK(!loading_control_task_queue_);
   // |main_thread_scheduler_impl_| can be null in unit tests.
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller.h b/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller.h
index 77044023..41b8307 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller.h
@@ -82,15 +82,6 @@
   // exist.
   scoped_refptr<MainThreadTaskQueue> VeryHighPriorityTaskQueue();
 
-  enum WebSchedulingTaskQueueType : unsigned {
-    kWebSchedulingUserVisiblePriority,
-    kWebSchedulingBestEffortPriority,
-    kWebSchedulingPriorityCount
-  };
-  // Return the Scheduling API task queue for the given priority.
-  scoped_refptr<MainThreadTaskQueue> ExperimentalWebSchedulingTaskQueue(
-      WebSchedulingTaskQueueType);
-
   // Return the non-loading task queue associated with the given queue traits,
   // and created it if it doesn't exist.
   scoped_refptr<MainThreadTaskQueue> NonLoadingTaskQueue(
@@ -122,7 +113,6 @@
 
   void CreateLoadingTaskQueue();
   void CreateLoadingControlTaskQueue();
-  void CreateWebSchedulingTaskQueue(WebSchedulingTaskQueueType task_queue_type);
   void CreateNonLoadingTaskQueue(MainThreadTaskQueue::QueueTraits);
 
   void TaskQueueCreated(const scoped_refptr<MainThreadTaskQueue>&);
@@ -152,9 +142,6 @@
 
   scoped_refptr<MainThreadTaskQueue> very_high_priority_task_queue_;
 
-  scoped_refptr<MainThreadTaskQueue>
-      web_scheduling_task_queues_[kWebSchedulingPriorityCount];
-
   using NonLoadingTaskQueueMap =
       WTF::HashMap<MainThreadTaskQueue::QueueTraitsKeyType,
                    scoped_refptr<MainThreadTaskQueue>>;
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.cc
index d4ca899..89e9366 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.cc
@@ -56,10 +56,6 @@
       return "cleanup_tq";
     case MainThreadTaskQueue::QueueType::kOther:
       return "other_tq";
-    case MainThreadTaskQueue::QueueType::kWebSchedulingUserInteraction:
-      return "web_scheduling_user_interaction_tq";
-    case MainThreadTaskQueue::QueueType::kWebSchedulingBestEffort:
-      return "web_scheduling_background_tq";
     case MainThreadTaskQueue::QueueType::kCount:
       NOTREACHED();
       return nullptr;
@@ -87,8 +83,6 @@
     case QueueType::kFrameDeferrable:
     case QueueType::kFramePausable:
     case QueueType::kFrameUnpausable:
-    case QueueType::kWebSchedulingUserInteraction:
-    case QueueType::kWebSchedulingBestEffort:
       return QueueClass::kTimer;
     case QueueType::kCompositor:
     case QueueType::kInput:
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h
index 91d959c..6fc2ae30 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h
@@ -65,8 +65,8 @@
 
     kCleanup = 20,
 
-    kWebSchedulingUserInteraction = 21,
-    kWebSchedulingBestEffort = 22,
+    // 21 : kWebSchedulingUserInteraction, obsolete.
+    // 22 : kWebSchedulingBestEffort, obsolete.
 
     // Used to group multiple types when calculating Expected Queueing Time.
     kOther = 23,
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/task_type_names.cc b/third_party/blink/renderer/platform/scheduler/main_thread/task_type_names.cc
index 20ab732..3cffe58 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/task_type_names.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/task_type_names.cc
@@ -61,10 +61,6 @@
       return "IdleTask";
     case TaskType::kMiscPlatformAPI:
       return "MiscPlatformAPI";
-    case TaskType::kExperimentalWebSchedulingUserInteraction:
-      return "ExperimentalWebSchedulingUserInteraction";
-    case TaskType::kExperimentalWebSchedulingBestEffort:
-      return "ExperimentalWebSchedulingBackground";
     case TaskType::kFontLoading:
       return "FontLoading";
     case TaskType::kApplicationLifeCycle:
diff --git a/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler.cc b/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler.cc
index 8792794..a4b6cfc 100644
--- a/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler.cc
+++ b/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler.cc
@@ -202,8 +202,6 @@
     case TaskType::kWorkerThreadTaskQueueDefault:
     case TaskType::kWorkerThreadTaskQueueV8:
     case TaskType::kWorkerThreadTaskQueueCompositor:
-    case TaskType::kExperimentalWebSchedulingUserInteraction:
-    case TaskType::kExperimentalWebSchedulingBestEffort:
     case TaskType::kInternalTranslation:
     case TaskType::kServiceWorkerClientMessage:
     case TaskType::kInternalContentCapture:
diff --git a/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.cc b/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.cc
index c7e6070..7ef100eb 100644
--- a/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.cc
+++ b/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.cc
@@ -33,10 +33,6 @@
 using base::sequence_manager::TaskQueue;
 
 namespace {
-// Workers could be short-lived, set a shorter interval than
-// the renderer thread.
-constexpr base::TimeDelta kUnspecifiedWorkerThreadLoadTrackerReportingInterval =
-    base::TimeDelta::FromSeconds(1);
 
 // Worker throttling trial
 const char kWorkerThrottlingTrial[] = "BlinkSchedulerDedicatedWorkerThrottling";
@@ -49,14 +45,6 @@
 constexpr base::TimeDelta kDefaultMaxThrottlingDelay =
     base::TimeDelta::FromSeconds(60);
 
-void ReportWorkerTaskLoad(base::TimeTicks time, double load) {
-  int load_percentage = static_cast<int>(load * 100);
-  DCHECK_LE(load_percentage, 100);
-  // TODO(kinuko): Maybe we also want to separately log when the associated
-  // tab is in foreground and when not.
-  UMA_HISTOGRAM_PERCENTAGE("WorkerScheduler.WorkerThreadLoad", load_percentage);
-}
-
 base::Optional<base::TimeDelta> GetMaxBudgetLevel() {
   int max_budget_level_ms;
   if (!base::StringToInt(
@@ -108,9 +96,6 @@
                    "WorkerSchedulerIdlePeriod",
                    base::TimeDelta::FromMilliseconds(300),
                    helper()->NewTaskQueue(TaskQueue::Spec("worker_idle_tq"))),
-      load_tracker_(helper()->NowTicks(),
-                    base::BindRepeating(&ReportWorkerTaskLoad),
-                    kUnspecifiedWorkerThreadLoadTrackerReportingInterval),
       lifecycle_state_(proxy ? proxy->lifecycle_state()
                              : SchedulingLifecycleState::kNotThrottled),
       worker_metrics_helper_(thread_type, helper()->HasCPUTimingForEachTask()),
@@ -121,9 +106,6 @@
   if (connector_) {
     ukm_recorder_ = ukm::MojoUkmRecorder::Create(connector_.get());
   }
-  thread_start_time_ = helper()->NowTicks();
-  load_tracker_.Resume(thread_start_time_);
-  helper()->AddTaskTimeObserver(this);
 
   if (proxy && proxy->parent_frame_type())
     worker_metrics_helper_.SetParentFrameType(*proxy->parent_frame_type());
@@ -141,8 +123,6 @@
   TRACE_EVENT_OBJECT_DELETED_WITH_ID(
       TRACE_DISABLED_BY_DEFAULT("worker.scheduler"), "WorkerScheduler", this);
 
-  helper()->RemoveTaskTimeObserver(this);
-
   DCHECK(worker_schedulers_.empty());
 }
 
@@ -194,19 +174,8 @@
 
 void WorkerThreadScheduler::Shutdown() {
   DCHECK(initialized_);
-  load_tracker_.RecordIdle(helper()->NowTicks());
-  base::TimeTicks end_time = helper()->NowTicks();
-  base::TimeDelta delta = end_time - thread_start_time_;
-
-  // The lifetime could be radically different for different workers,
-  // some workers could be short-lived (but last at least 1 sec in
-  // Service Workers case) or could be around as long as the tab is open.
-  UMA_HISTOGRAM_CUSTOM_TIMES(
-      "WorkerThread.Runtime", delta, base::TimeDelta::FromSeconds(1),
-      base::TimeDelta::FromDays(1), 50 /* bucket count */);
   task_queue_throttler_.reset();
   idle_helper_.Shutdown();
-  helper()->RemoveTaskTimeObserver(this);
   helper()->Shutdown();
 }
 
@@ -258,13 +227,6 @@
   return idle_helper_.CurrentIdleTaskDeadline();
 }
 
-void WorkerThreadScheduler::WillProcessTask(base::TimeTicks start_time) {}
-
-void WorkerThreadScheduler::DidProcessTask(base::TimeTicks start_time,
-                                           base::TimeTicks end_time) {
-  load_tracker_.RecordTaskTime(start_time, end_time);
-}
-
 void WorkerThreadScheduler::OnLifecycleStateChanged(
     SchedulingLifecycleState lifecycle_state) {
   if (lifecycle_state_ == lifecycle_state)
diff --git a/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h b/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h
index a532cae..dd02d88 100644
--- a/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h
+++ b/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h
@@ -8,11 +8,9 @@
 #include "base/macros.h"
 #include "base/message_loop/message_loop.h"
 #include "base/single_thread_task_runner.h"
-#include "base/task/sequence_manager/task_time_observer.h"
 #include "components/scheduling_metrics/task_duration_metric_reporter.h"
 #include "third_party/blink/public/platform/web_thread_type.h"
 #include "third_party/blink/renderer/platform/scheduler/common/idle_helper.h"
-#include "third_party/blink/renderer/platform/scheduler/common/thread_load_tracker.h"
 #include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
 #include "third_party/blink/renderer/platform/scheduler/public/frame_status.h"
 #include "third_party/blink/renderer/platform/scheduler/worker/non_main_thread_scheduler_impl.h"
@@ -41,12 +39,10 @@
 class WakeUpBudgetPool;
 class CPUTimeBudgetPool;
 
-class PLATFORM_EXPORT WorkerThreadScheduler
-    : public NonMainThreadSchedulerImpl,
-      public IdleHelper::Delegate,
-      public base::sequence_manager::TaskTimeObserver {
+class PLATFORM_EXPORT WorkerThreadScheduler : public NonMainThreadSchedulerImpl,
+                                              public IdleHelper::Delegate {
  public:
-  // |sequence_manager|and |proxy| must remain valid for the entire lifetime of
+  // |sequence_manager| and |proxy| must remain valid for the entire lifetime of
   // this object.
   WorkerThreadScheduler(
       WebThreadType thread_type,
@@ -78,11 +74,6 @@
       base::sequence_manager::TaskQueue::TaskTiming* task_timing,
       base::sequence_manager::LazyNow* lazy_now) override;
 
-  // TaskTimeObserver implementation:
-  void WillProcessTask(base::TimeTicks start_time) override;
-  void DidProcessTask(base::TimeTicks start_time,
-                      base::TimeTicks end_time) override;
-
   SchedulerHelper* GetSchedulerHelperForTesting();
   base::TimeTicks CurrentIdleTaskDeadlineForTesting() const;
 
@@ -146,9 +137,7 @@
 
   const WebThreadType thread_type_;
   IdleHelper idle_helper_;
-  ThreadLoadTracker load_tracker_;
   bool initialized_;
-  base::TimeTicks thread_start_time_;
   scoped_refptr<NonMainThreadTaskQueue> control_task_queue_;
   scoped_refptr<base::SingleThreadTaskRunner> v8_task_runner_;
   scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner_;
diff --git a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint
index 49ab56e..cc1c846 100644
--- a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint
+++ b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint
@@ -82,7 +82,6 @@
 Bug(none) compositing/reflections/nested-reflection-mask-change.html [ Failure ]
 Bug(none) compositing/rtl/rtl-iframe-absolute.html [ Failure ]
 Bug(none) compositing/rtl/rtl-iframe-relative.html [ Failure ]
-Bug(none) compositing/scrollbars/nested-overlay-scrollbars.html [ Failure ]
 Bug(none) compositing/squashing/add-remove-squashed-layers.html [ Failure ]
 Bug(none) compositing/squashing/composited-bounds-for-negative-z.html [ Failure ]
 Bug(none) compositing/squashing/do-not-squash-non-self-painting-layer.html [ Failure ]
@@ -132,8 +131,6 @@
 
 crbug.com/907601 virtual/threaded/fast/scrolling/events/scrollend-event-fired-after-snap.html [ Skip ]
 
-crbug.com/959949 external/wpt/element-timing/invisible-images.html [ Failure Pass ]
-
 # Single pixel difference around one of the radiused borders.
 Bug(none) external/wpt/css/filter-effects/backdrop-filter-reference-filter.html [ Failure ]
 
@@ -157,6 +154,7 @@
 crbug.com/842356 paint/invalidation/compositing/containing-block-removed.html [ Failure ]
 
 # Extra raster invalidations.
+Bug(none) compositing/overflow/do-not-repaint-if-scrolling-composited-layers.html [ Failure ]
 Bug(none) paint/invalidation/compositing/clipping-should-not-repaint-composited-descendants.html [ Failure ]
 Bug(none) paint/invalidation/compositing/should-not-repaint-composited-filter.html [ Failure ]
 Bug(none) paint/invalidation/compositing/should-not-repaint-composited-opacity.html [ Failure ]
@@ -283,6 +281,7 @@
 crbug.com/931486 compositing/force-compositing-mode/overflow-iframe-enter-compositing.html [ Failure ]
 crbug.com/931486 compositing/force-compositing-mode/overflow-iframe-layer.html [ Failure ]
 crbug.com/931486 compositing/iframes/overlapped-iframe-iframe.html [ Failure ]
+crbug.com/931486 compositing/layer-creation/fixed-position-change-out-of-view-in-view.html [ Failure ]
 crbug.com/931486 compositing/overflow/accelerated-overflow-scroll-should-not-affect-perspective.html [ Failure ]
 crbug.com/931486 compositing/overflow/content-gains-scrollbars.html [ Failure ]
 crbug.com/931486 compositing/overflow/content-loses-scrollbars.html [ Failure ]
@@ -293,6 +292,7 @@
 crbug.com/931486 compositing/overflow/scroll-parent-with-non-stacking-context-composited-ancestor.html [ Failure ]
 crbug.com/931486 compositing/overflow/scrollbar-layer-placement-negative-z-index-child-positioned.html [ Failure ]
 crbug.com/931486 compositing/overflow/scrollbar-layer-placement.html [ Failure ]
+crbug.com/931486 compositing/layer-creation/fixed-position-in-fixed-overflow.html [ Failure ]
 crbug.com/931486 compositing/layer-creation/fixed-position-nonscrollable-body-mismatch-containers.html [ Failure ]
 crbug.com/931486 compositing/layer-creation/fixed-position-out-of-view-positioning.html [ Failure ]
 crbug.com/931486 compositing/layer-creation/overflow-scroll-overlap.html [ Failure ]
@@ -304,13 +304,16 @@
 crbug.com/931486 compositing/rtl/rtl-absolute-overflow.html [ Failure ]
 crbug.com/931486 compositing/rtl/rtl-fixed-overflow.html [ Failure ]
 crbug.com/931486 compositing/rtl/rtl-iframe-absolute-overflow.html [ Failure ]
+crbug.com/931486 compositing/rtl/rtl-iframe-fixed-overflow.html [ Failure ]
 crbug.com/931486 compositing/squashing/frame-clip-squashed-scrolled.html [ Failure ]
+crbug.com/931486 compositing/squashing/squash-above-fixed-1.html [ Failure ]
+crbug.com/931486 compositing/squashing/squash-above-fixed-3.html [ Failure ]
 crbug.com/931486 paint/invalidation/scroll/fixed-child-move-after-scroll.html [ Failure ]
 crbug.com/931486 paint/invalidation/scroll/fixed-child-of-fixed-move-after-scroll.html [ Failure ]
 crbug.com/931486 paint/invalidation/position/fixed-tranformed.html [ Failure ]
 crbug.com/931486 paint/invalidation/scroll/inline-style-change-in-scrolled-view.html [ Failure ]
 crbug.com/931486 paint/invalidation/background/background-image-paint-invalidation.html [ Failure ]
-crbug.com/931486 crbug.com/931491 paint/invalidation/compositing/fixed-pos-inside-composited-intermediate-layer.html [ Failure ]
+crbug.com/931486 paint/invalidation/compositing/fixed-pos-inside-composited-intermediate-layer.html [ Failure ]
 crbug.com/931486 crbug.com/931491 paint/invalidation/compositing/fixed-pos-with-abs-pos-child-scroll.html [ Failure ]
 crbug.com/931486 crbug.com/931491 paint/invalidation/compositing/fixed-scroll-in-empty-root-layer.html [ Failure ]
 crbug.com/931486 paint/invalidation/compositing/opacity-from-zero-to-non-zero-composited.html [ Failure ]
@@ -376,6 +379,7 @@
 crbug.com/909749 fast/events/touch/compositor-touch-hit-rects-non-composited-scroll.html [ Failure ]
 
 # Backdrop filter
+crbug.com/923429 compositing/layer-creation/fixed-position-out-of-view-with-backdrop-filter.html [ Failure ]
 crbug.com/923429 css3/filters/backdrop-filter-basic-blur.html [ Timeout Failure ]
 crbug.com/923429 css3/filters/backdrop-filter-boundary.html [ Failure ]
 crbug.com/923429 css3/filters/backdrop-filter-browser-zoom.html [ Failure ]
@@ -386,6 +390,7 @@
 crbug.com/923429 css3/filters/backdrop-filter-rendering-no-background.html [ Failure ]
 crbug.com/923429 css3/filters/backdrop-filter-transform.html [ Failure ]
 crbug.com/923429 css3/filters/backdrop-filter-edge-pixels.html [ Failure ]
+crbug.com/923429 css3/filters/backdrop-filter-clip-radius-zoom.html [ Failure ]
 crbug.com/923429 external/wpt/css/filter-effects/backdrop-filter-basic-background-color.html [ Failure ]
 crbug.com/923429 external/wpt/css/filter-effects/backdrop-filter-basic-opacity-2.html [ Failure ]
 crbug.com/923429 external/wpt/css/filter-effects/backdrop-filter-basic.html [ Failure ]
@@ -413,6 +418,7 @@
 crbug.com/923429 virtual/scalefactor200/css3/filters/backdrop-filter-rendering-no-background.html [ Failure ]
 crbug.com/923429 virtual/scalefactor200/css3/filters/backdrop-filter-rendering.html [ Failure ]
 crbug.com/923429 virtual/scalefactor200/css3/filters/backdrop-filter-transform.html [ Failure ]
+crbug.com/923429 virtual/scalefactor200/css3/filters/backdrop-filter-clip-radius-zoom.html [ Failure ]
 crbug.com/923429 virtual/scalefactor200/external/wpt/css/filter-effects/backdrop-filter-basic-background-color.html [ Failure ]
 crbug.com/923429 virtual/scalefactor200/external/wpt/css/filter-effects/backdrop-filter-basic-opacity-2.html [ Failure ]
 crbug.com/923429 virtual/scalefactor200/external/wpt/css/filter-effects/backdrop-filter-basic.html [ Failure ]
@@ -435,24 +441,6 @@
 crbug.com/940033 virtual/fractional_scrolling_threaded/fast/scrolling/wheel-scrolling-over-custom-scrollbar.html [ Failure ]
 crbug.com/940033 virtual/threaded/fast/scrolling/wheel-scrolling-over-custom-scrollbar.html [ Failure ]
 
-# Missing composited layer for fixed-position
-crbug.com/931491 compositing/layer-creation/fixed-position-change-out-of-view-in-view.html [ Failure ]
-crbug.com/931491 compositing/layer-creation/fixed-position-in-fixed-overflow.html [ Failure ]
-crbug.com/931491 compositing/layer-creation/fixed-position-out-of-view-scaled-iframe-scroll.html [ Failure ]
-crbug.com/931491 compositing/layer-creation/fixed-position-out-of-view-scaled-iframe.html [ Failure ]
-crbug.com/931491 compositing/layer-creation/fixed-position-out-of-view-scaled-scroll.html [ Failure ]
-crbug.com/931491 compositing/layer-creation/fixed-position-out-of-view-scaled.html [ Failure ]
-crbug.com/931491 compositing/layer-creation/fixed-position-out-of-view-with-backdrop-filter.html [ Failure ]
-crbug.com/931491 compositing/layer-creation/fixed-position-out-of-view.html [ Failure ]
-crbug.com/931491 compositing/layer-creation/fixed-position-under-transform.html [ Failure ]
-crbug.com/931491 compositing/rtl/rtl-iframe-fixed-overflow.html [ Failure ]
-crbug.com/931491 compositing/rtl/rtl-iframe-fixed.html [ Failure ]
-crbug.com/931491 compositing/squashing/no-squashing-into-fixed-position-that-clips.html [ Failure ]
-crbug.com/931491 compositing/squashing/squash-above-fixed-1.html [ Failure ]
-crbug.com/931491 compositing/squashing/squash-above-fixed-2.html [ Failure ]
-crbug.com/931491 compositing/squashing/squash-above-fixed-3.html [ Failure ]
-crbug.com/931491 compositing/squashing/squash-paint-invalidation-fixed-position.html [ Failure ]
-
 crbug.com/918155 scrollbars/overlay-scrollbar-over-child-layer.html [ Failure ]
 crbug.com/918155 virtual/prefer_compositing_to_lcd_text/scrollbars/overlay-scrollbar-over-child-layer.html [ Failure ]
 
@@ -478,71 +466,6 @@
 # Crash on non-contiguous effect on multiple columns
 Bug(none) fast/multicol/composited-layer-will-change.html [ Crash ]
 
-# Crash in CompositeorAnimations::CheckCanStartElementOnCompositor() etc.
-crbug.com/962191 animations/stability/base-render-style-crash.html [ Crash ]
-crbug.com/962191 animations/stability/option-element-crash.html [ Crash ]
-crbug.com/962191 animations/stability/option-opacity-inherit-crash.html [ Crash ]
-crbug.com/962191 animations/web-animations/animation-state-changes-positive-playback-rate.html [ Crash ]
-crbug.com/962191 compositing/overflow/do-not-repaint-if-scrolling-composited-layers.html [ Crash ]
-crbug.com/962191 external/wpt/css/css-animations/CSSPseudoElement-getAnimations.tentative.html [ Crash ]
-crbug.com/962191 external/wpt/css/css-animations/KeyframeEffect-target.tentative.html [ Crash ]
-crbug.com/962191 external/wpt/css/css-easing/cubic-bezier-timing-functions-output.html [ Crash ]
-crbug.com/962191 external/wpt/css/css-easing/step-timing-functions-output.html [ Crash ]
-crbug.com/962191 external/wpt/css/css-logical/animation-001.html [ Crash ]
-crbug.com/962191 external/wpt/css/css-transitions/KeyframeEffect-target.tentative.html [ Crash ]
-crbug.com/962191 external/wpt/scroll-animations/scroll-animation.html [ Crash ]
-crbug.com/962191 external/wpt/web-animations/animation-model/animation-types/accumulation-per-property.html [ Crash ]
-crbug.com/962191 external/wpt/web-animations/animation-model/animation-types/addition-per-property.html [ Crash ]
-crbug.com/962191 external/wpt/web-animations/animation-model/animation-types/discrete.html [ Crash ]
-crbug.com/962191 external/wpt/web-animations/animation-model/animation-types/interpolation-per-property.html [ Crash ]
-crbug.com/962191 external/wpt/web-animations/animation-model/animation-types/visibility.html [ Crash ]
-crbug.com/962191 external/wpt/web-animations/animation-model/combining-effects/effect-composition.html [ Crash ]
-crbug.com/962191 external/wpt/web-animations/animation-model/keyframe-effects/computed-keyframes-shorthands.html [ Crash ]
-crbug.com/962191 external/wpt/web-animations/animation-model/keyframe-effects/effect-value-context-filling.html [ Crash ]
-crbug.com/962191 external/wpt/web-animations/animation-model/keyframe-effects/effect-value-context.html [ Crash ]
-crbug.com/962191 external/wpt/web-animations/animation-model/keyframe-effects/effect-value-iteration-composite-operation.html [ Crash ]
-crbug.com/962191 external/wpt/web-animations/animation-model/keyframe-effects/effect-value-overlapping-keyframes.html [ Crash ]
-crbug.com/966981 external/wpt/web-animations/animation-model/keyframe-effects/effect-value-replaced-animations.html [ Crash ]
-crbug.com/962191 external/wpt/web-animations/animation-model/keyframe-effects/effect-value-transformed-distance.html [ Crash ]
-crbug.com/962191 external/wpt/web-animations/interfaces/Animatable/animate.html [ Crash ]
-crbug.com/962191 external/wpt/web-animations/interfaces/Animatable/getAnimations.html [ Crash ]
-crbug.com/966981 external/wpt/web-animations/interfaces/Animation/commitStyles.html [ Crash ]
-crbug.com/962191 external/wpt/web-animations/interfaces/Animation/finished.html [ Crash ]
-crbug.com/962191 external/wpt/web-animations/interfaces/Animation/id.html [ Crash ]
-crbug.com/966981 external/wpt/web-animations/interfaces/Animation/persist.html [ Crash ]
-crbug.com/962191 external/wpt/web-animations/interfaces/Animation/ready.html [ Crash ]
-crbug.com/962191 external/wpt/web-animations/interfaces/AnimationEffect/updateTiming.html [ Crash ]
-crbug.com/962191 external/wpt/web-animations/interfaces/Document/getAnimations.html [ Crash ]
-crbug.com/962191 external/wpt/web-animations/interfaces/KeyframeEffect/target.html [ Crash ]
-crbug.com/962191 external/wpt/web-animations/timing-model/animation-effects/active-time.html [ Crash ]
-crbug.com/962191 external/wpt/web-animations/timing-model/animation-effects/current-iteration.html [ Crash ]
-crbug.com/962191 external/wpt/web-animations/timing-model/animation-effects/phases-and-states.html [ Crash ]
-crbug.com/962191 external/wpt/web-animations/timing-model/animation-effects/simple-iteration-progress.html [ Crash ]
-crbug.com/962191 external/wpt/web-animations/timing-model/animations/canceling-an-animation.html [ Crash ]
-crbug.com/962191 external/wpt/web-animations/timing-model/animations/finishing-an-animation.html [ Crash ]
-crbug.com/962191 external/wpt/web-animations/timing-model/animations/play-states.html [ Crash ]
-crbug.com/962191 external/wpt/web-animations/timing-model/animations/playing-an-animation.html [ Crash ]
-crbug.com/962191 external/wpt/web-animations/timing-model/animations/reversing-an-animation.html [ Crash ]
-crbug.com/962191 external/wpt/web-animations/timing-model/animations/seamlessly-updating-the-playback-rate-of-an-animation.html [ Crash ]
-crbug.com/962191 external/wpt/web-animations/timing-model/animations/setting-the-current-time-of-an-animation.html [ Crash ]
-crbug.com/962191 external/wpt/web-animations/timing-model/animations/setting-the-playback-rate-of-an-animation.html [ Crash ]
-crbug.com/962191 external/wpt/web-animations/timing-model/animations/setting-the-start-time-of-an-animation.html [ Crash ]
-crbug.com/962191 external/wpt/web-animations/timing-model/animations/setting-the-timeline-of-an-animation.html [ Crash ]
-crbug.com/962191 external/wpt/web-animations/timing-model/animations/the-current-time-of-an-animation.html [ Crash ]
-crbug.com/962191 external/wpt/web-animations/timing-model/animations/updating-the-finished-state.html [ Crash ]
-crbug.com/962191 external/wpt/web-animations/timing-model/time-transformations/transformed-progress.html [ Crash ]
-crbug.com/962191 http/tests/devtools/animation/animation-timeline.js [ Crash ]
-crbug.com/962191 http/tests/devtools/elements/styles-3/spectrum.js [ Crash ]
-crbug.com/962191 http/tests/devtools/sources/bezier-swatch-position.js [ Crash ]
-crbug.com/962191 transitions/svg-transitions.html [ Crash ]
-crbug.com/962191 virtual/threaded/animations/stability/base-render-style-crash.html [ Crash ]
-crbug.com/962191 virtual/threaded/animations/stability/option-element-crash.html [ Crash ]
-crbug.com/962191 virtual/threaded/animations/stability/option-opacity-inherit-crash.html [ Crash ]
-crbug.com/962191 virtual/threaded/animations/web-animations/animation-state-changes-positive-playback-rate.html [ Crash ]
-crbug.com/962191 virtual/threaded/external/wpt/css/css-animations/CSSPseudoElement-getAnimations.tentative.html [ Crash ]
-crbug.com/962191 virtual/threaded/external/wpt/css/css-animations/KeyframeEffect-target.tentative.html [ Crash ]
-crbug.com/962191 virtual/threaded/transitions/svg-transitions.html [ Crash ]
-
 # Clip-path and mask.
 crbug.com/979369 compositing/images/direct-image-clip-path.html [ Failure ]
 crbug.com/979369 compositing/images/direct-image-dynamic-clip-path.html [ Failure ]
diff --git a/third_party/blink/web_tests/FlagExpectations/enable-gpu-rasterization b/third_party/blink/web_tests/FlagExpectations/enable-gpu-rasterization
new file mode 100644
index 0000000..d814edd8
--- /dev/null
+++ b/third_party/blink/web_tests/FlagExpectations/enable-gpu-rasterization
@@ -0,0 +1,143 @@
+# Blur
+crbug.com/972621 compositing/culling/scrolled-within-boxshadow.html [ Skip ]
+crbug.com/972621 compositing/culling/translated-boxshadow.html [ Skip ]
+crbug.com/972621 compositing/culling/unscrolled-within-boxshadow.html [ Skip ]
+crbug.com/972621 compositing/geometry/clipping-foreground.html [ Skip ]
+crbug.com/972621 compositing/geometry/foreground-layer.html [ Skip ]
+crbug.com/972621 compositing/geometry/vertical-scroll-composited.html [ Skip ]
+crbug.com/972621 compositing/iframes/composited-iframe-alignment.html [ Skip ]
+crbug.com/972621 compositing/overflow/border-radius-above-composited-subframe.html [ Skip ]
+crbug.com/972621 compositing/overflow/border-radius-composited-subframe.html [ Skip ]
+crbug.com/972621 compositing/overflow/scrollbar-layer-placement-negative-z-index-child-positioned.html [ Skip ]
+crbug.com/972621 compositing/overflow/scrollbar-layer-placement.html [ Skip ]
+crbug.com/972621 compositing/shadows/shadow-drawing.html [ Skip ]
+crbug.com/972621 css2.1/t090501-c414-flt-02-d-g.html [ Skip ]
+crbug.com/972621 css2.1/t090501-c414-flt-03-b-g.html [ Skip ]
+crbug.com/972621 css2.1/t100304-c43-rpl-bbx-00-d-g.html [ Skip ]
+crbug.com/972621 css2.1/t100304-c43-rpl-bbx-01-d-g.html [ Skip ]
+crbug.com/972621 css3/background/background-repeat-round-auto1.html [ Skip ]
+crbug.com/972621 css3/background/background-repeat-round-border.html [ Skip ]
+crbug.com/972621 css3/background/background-repeat-round-content.html [ Skip ]
+crbug.com/972621 css3/background/background-repeat-round-padding.html [ Skip ]
+crbug.com/972621 css3/filters/blur-filter-page-scroll-parents.html [ Skip ]
+crbug.com/972621 css3/filters/blur-filter-page-scroll.html [ Skip ]
+crbug.com/972621 css3/filters/crash-filter-change.html [ Skip ]
+crbug.com/972621 css3/filters/effect-blur-hw.html [ Skip ]
+crbug.com/972621 css3/filters/effect-blur.html [ Skip ]
+crbug.com/972621 css3/filters/effect-brightness-clamping.html [ Skip ]
+crbug.com/972621 css3/filters/effect-combined.html [ Skip ]
+crbug.com/972621 css3/filters/effect-drop-shadow.html [ Skip ]
+crbug.com/972621 css3/filters/regions-expanding.html [ Skip ]
+crbug.com/972621 images/rgb-png-with-cmyk-color-profile.html [ Skip ]
+crbug.com/972621 images/ycbcr-with-cmyk-color-profile.html [ Skip ]
+crbug.com/972621 media/video-poster-scale.html [ Skip ]
+crbug.com/972621 transforms/shadows.html [ Skip ]
+crbug.com/972621 transforms/svg-vs-css.xhtml [ Skip ]
+
+# Input timing doesn't match screenshots
+crbug.com/935970 compositing/gestures/gesture-tapHighlight-img-and-text.html [ Skip ]
+crbug.com/935970 compositing/gestures/gesture-tapHighlight-multicol.html [ Skip ]
+crbug.com/935970 compositing/gestures/gesture-tapHighlight-nested-cursor.html [ Skip ]
+crbug.com/935970 compositing/gestures/gesture-tapHighlight-nested-cursor3.html [ Skip ]
+crbug.com/935970 compositing/gestures/gesture-tapHighlight-simple-scaled-document.html [ Skip ]
+crbug.com/935970 compositing/gestures/gesture-tapHighlight-with-squashing.html [ Skip ]
+crbug.com/935970 compositing/iframes/layout-on-compositing-change.html [ Skip ]
+crbug.com/935970 compositing/squashing/squash-compositing-hover.html [ Skip ]
+crbug.com/935970 compositing/squashing/squash-transform-repainting-child.html [ Skip ]
+crbug.com/935970 compositing/squashing/squash-transform-repainting-transformed-child.html [ Skip ]
+crbug.com/935970 css3/viewport-percentage-lengths/vh-resize.html [ Skip ]
+crbug.com/935970 images/drag-image-2.html [ Skip ]
+crbug.com/935970 images/drag-image-descendant-iframe-composited.html [ Skip ]
+crbug.com/935970 images/drag-image-descendant-painting-sibling.html [ Skip ]
+crbug.com/935970 images/drag-image-transformed-child.html [ Skip ]
+crbug.com/935970 images/drag-image-transformed-parent.html [ Skip ]
+crbug.com/935970 images/drag-image.html [ Skip ]
+crbug.com/935970 images/server-side-imagemap.html [ Skip ]
+crbug.com/935970 media/color-profile-video-poster-image.html [ Skip ]
+crbug.com/935970 media/controls-drag-timebar.html [ Skip ]
+crbug.com/935970 media/controls-timeline.html [ Skip ]
+crbug.com/935970 media/controls/video-enter-exit-fullscreen-while-hovering-shows-controls.html [ Skip ]
+crbug.com/935970 media/controls/video-enter-exit-fullscreen-without-hovering-doesnt-show-controls.html [ Skip ]
+crbug.com/935970 media/media-controls-tap-show-controls-without-activating.html [ Skip ]
+crbug.com/935970 media/remoteplayback/prompt-twice-throws.html [ Skip ]
+crbug.com/935970 media/video-controls-always-visible-when-control-hovered.html [ Skip ]
+crbug.com/935970 media/video-controls-auto-hide-after-play-by-touch.html [ Skip ]
+crbug.com/935970 media/video-controls-fullscreen.html [ Skip ]
+crbug.com/935970 media/video-controls-hide-after-touch-on-control.html [ Skip ]
+crbug.com/935970 media/video-controls-hide-on-move-outside-controls.html [ Skip ]
+crbug.com/935970 media/video-controls-mouse-events-captured.html [ Skip ]
+crbug.com/935970 media/video-controls-overflow-menu-closed-captions-button.html [ Skip ]
+crbug.com/935970 media/video-controls-overflow-menu-fullscreen-button.html [ Skip ]
+crbug.com/935970 media/video-controls-overflow-menu-mute-button.html [ Skip ]
+crbug.com/935970 media/video-controls-overflow-menu-play-button.html [ Skip ]
+crbug.com/935970 media/video-controls-transformed.html [ Skip ]
+crbug.com/935970 media/video-controls-visibility-multimodal-mouse-after-touch.html [ Skip ]
+crbug.com/935970 media/video-controls-visibility-multimodal-touch-after-mouse.html [ Skip ]
+crbug.com/935970 media/video-controls-visible-audio-only.html [ Skip ]
+crbug.com/935970 media/video-persistence.html [ Skip ]
+crbug.com/935970 media/video-src-blob.html [ Skip ]
+crbug.com/935970 images/huge-image-viewport-scale.html [ Skip ]
+crbug.com/935970 images/image-map-multiple-xhtml.xhtml [ Skip ]
+crbug.com/935970 images/image-map-multiple.html [ Skip ]
+crbug.com/935970 media/audio-delete-while-slider-thumb-clicked.html [ Skip ]
+
+# Filter Rebaseline
+crbug.com/978605 compositing/masks/mask-with-added-filters.html [ Skip ]
+crbug.com/978605 compositing/masks/mask-with-removed-filters.html [ Skip ]
+
+# Tests fail even with a fuzzy pixel diff. They require a re-baseline.
+crbug.com/954328 compositing/fixed-background-after-style-recalc.html [ Failure ]
+crbug.com/954328 compositing/overflow/mask-with-filter.html [ Failure ]
+crbug.com/954328 css3/blending/background-blend-mode-crossfade-image-gradient.html [ Failure ]
+crbug.com/954328 css3/blending/background-blend-mode-default-value.html [ Failure ]
+crbug.com/954328 css3/blending/background-blend-mode-gradient-gradient.html [ Failure ]
+crbug.com/954328 css3/blending/background-blend-mode-gradient-image.html [ Failure ]
+crbug.com/954328 css3/blending/background-blend-mode-image-color.html [ Failure ]
+crbug.com/954328 css3/blending/background-blend-mode-image-image.html [ Failure ]
+crbug.com/954328 css3/blending/background-blend-mode-image-svg.html [ Failure ]
+crbug.com/954328 css3/blending/background-blend-mode-multiple-background-layers.html [ Failure ]
+crbug.com/954328 css3/blending/background-blend-mode-single-layer-no-blending.html [ Failure ]
+crbug.com/954328 css3/blending/background-blend-mode-svg-color.html [ Failure ]
+crbug.com/954328 css3/blending/background-blend-mode-tiled-gradient.html [ Failure ]
+crbug.com/954328 css3/blending/effect-background-blend-mode-stacking.html [ Failure ]
+crbug.com/954328 css3/blending/effect-background-blend-mode-tiled.html [ Failure ]
+crbug.com/954328 css3/blending/effect-background-blend-mode.html [ Failure ]
+crbug.com/954328 css3/blending/mix-blend-mode-isolated-group-1.html [ Failure ]
+crbug.com/954328 css3/blending/mix-blend-mode-isolated-group-2.html [ Failure ]
+crbug.com/954328 css3/blending/mix-blend-mode-isolated-group-3.html [ Failure ]
+crbug.com/954328 css3/filters/filter-change-repaint.html [ Failure ]
+crbug.com/954328 css3/filters/filter-repaint-blur.html [ Failure ]
+crbug.com/954328 css3/filters/filter-repaint-child-layers.html [ Failure ]
+crbug.com/954328 css3/filters/filter-repaint-shadow.html [ Failure ]
+crbug.com/954328 css3/filters/filter-repaint.html [ Failure ]
+crbug.com/954328 css3/masking/mask-luminance-png.html [ Failure ]
+crbug.com/954328 css3/masking/mask-repeat-round-auto1.html [ Failure ]
+crbug.com/954328 css3/masking/mask-repeat-round-border.html [ Failure ]
+crbug.com/954328 css3/masking/mask-repeat-round-content.html [ Failure ]
+crbug.com/954328 css3/masking/mask-repeat-round-padding.html [ Failure ]
+crbug.com/954328 css3/masking/mask-repeat-space-border.html [ Failure ]
+crbug.com/954328 images/color-profile-animate-rotate.html [ Failure ]
+crbug.com/954328 images/color-profile-animate.html [ Failure ]
+crbug.com/954328 images/color-profile-background-image-cover.html [ Failure ]
+crbug.com/954328 images/color-profile-background-image-cross-fade-png.html [ Failure ]
+crbug.com/954328 images/color-profile-background-image-cross-fade.html [ Failure ]
+crbug.com/954328 images/color-profile-background-image-repeat.html [ Failure ]
+crbug.com/954328 images/color-profile-background-image-space.html [ Failure ]
+crbug.com/954328 images/color-profile-border-fade.html [ Failure ]
+crbug.com/954328 images/color-profile-border-image.html [ Failure ]
+crbug.com/954328 images/color-profile-border-radius.html [ Failure ]
+crbug.com/954328 images/color-profile-filter.html [ Failure ]
+crbug.com/954328 images/color-profile-group.html [ Failure ]
+crbug.com/954328 images/color-profile-image-canvas-pattern.html [ Failure ]
+crbug.com/954328 images/color-profile-image-canvas.html [ Failure ]
+crbug.com/954328 images/color-profile-image-filter-all.html [ Failure ]
+crbug.com/954328 images/color-profile-image-profile-match.html [ Failure ]
+crbug.com/954328 images/color-profile-image.html [ Failure ]
+crbug.com/954328 images/color-profile-layer-filter.html [ Failure ]
+crbug.com/954328 images/color-profile-mask-image-svg.html [ Failure ]
+crbug.com/954328 images/color-profile-object.html [ Failure ]
+crbug.com/954328 images/color-profile-svg-fill-text.html [ Failure ]
+crbug.com/954328 images/color-profile-svg.html [ Failure ]
+crbug.com/954328 images/optimize-contrast-canvas.html [ Failure ]
+crbug.com/954328 images/optimize-contrast-image.html [ Failure ]
+crbug.com/954328 images/webp-color-profile-lossy.html [ Failure ]
diff --git a/third_party/blink/web_tests/NeverFixTests b/third_party/blink/web_tests/NeverFixTests
index ea4b31a..662a5ef 100644
--- a/third_party/blink/web_tests/NeverFixTests
+++ b/third_party/blink/web_tests/NeverFixTests
@@ -2272,3 +2272,4 @@
 external/wpt/web-share/share-image-manual.html [ WontFix ]
 external/wpt/uievents/order-of-events/mouse-events/wheel-basic-manual.html [ WontFix ]
 external/wpt/uievents/order-of-events/mouse-events/wheel-scrolling-manual.html [ WontFix ]
+external/wpt/html/webappapis/user-prompts/print-manual.html [ WontFix ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 0decafd..4b3244df 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -2689,6 +2689,7 @@
 
 crbug.com/802067 [ Mac ] external/wpt/pointerlock/movementX_Y_basic.html [ Failure ]
 crbug.com/802067 [ Mac ] external/wpt/pointerevents/pointerlock/pointerevent_movementxy.html [ Failure ]
+crbug.com/802067 [ Mac ] external/wpt/pointerevents/pointerlock/pointerevent_movementxy_with_pointerlock.html [ Failure ]
 
 # Since there is no compositor thread when running tests then there is no main thread event queue. Hence the
 # logic for this test will be skipped when running Layout tests.
@@ -6228,8 +6229,6 @@
 # Sheriff 2019-06-26
 crbug.com/978966 [ Mac ] paint/markers/ellipsis-mixed-text-in-ltr-flow-with-markers.html [ Pass Failure ]
 
-crbug.com/979253 external/wpt/css/css-pseudo/first-line-with-out-of-flow.html [ Failure ]
-
 # Sheriff 2019-06-27
 crbug.com/979193 [ Mac ] paint/invalidation/svg/relative-sized-use-on-symbol.xhtml [ Pass Failure ]
 crbug.com/979193 [ Mac ] paint/invalidation/svg/remove-background-property-on-root.html [ Pass Failure ]
diff --git a/third_party/blink/web_tests/compositing/layer-creation/fixed-position-out-of-view-scaled-iframe-scroll.html b/third_party/blink/web_tests/compositing/layer-creation/fixed-position-out-of-view-scaled-iframe-scroll.html
index c253bd9..7a3d821 100644
--- a/third_party/blink/web_tests/compositing/layer-creation/fixed-position-out-of-view-scaled-iframe-scroll.html
+++ b/third_party/blink/web_tests/compositing/layer-creation/fixed-position-out-of-view-scaled-iframe-scroll.html
@@ -13,14 +13,18 @@
     addEventListener("load", function() {
       internals.setPageScaleFactor(0.5);
       setTimeout(function() {
-        var layerTreeScaledDown = internals.layerTreeAsText(document.getElementById("iframe").contentDocument);
+        var layerTreeScaledDown = JSON.stringify(
+            JSON.parse(internals.layerTreeAsText(document.getElementById("iframe").contentDocument)).layers);
 
         internals.setPageScaleFactor(1.5);
         setTimeout(function() {
-          var layerTreeScaledUp = internals.layerTreeAsText(document.getElementById("iframe").contentDocument);
+          var layerTreeScaledUp = JSON.stringify(
+              JSON.parse(internals.layerTreeAsText(document.getElementById("iframe").contentDocument)).layers);
           // Because logical size of the frame is unchanged, the layer tree in the frame should not be affected by the page scale.
           document.getElementById("result").innerText =
-              layerTreeScaledUp == layerTreeScaledDown ? "PASS" : "FAIL";
+              layerTreeScaledUp == layerTreeScaledDown
+                  ? "PASS"
+                  : "FAIL\nScale 0.5:\n" + layerTreeScaledDown + "\nScale 1.5:\n" + layerTreeScaledUp;
           testRunner.notifyDone();
         }, 0);
       }, 0);
diff --git a/third_party/blink/web_tests/compositing/layer-creation/fixed-position-out-of-view-scaled-iframe.html b/third_party/blink/web_tests/compositing/layer-creation/fixed-position-out-of-view-scaled-iframe.html
index 76d6819..a4c84bf 100644
--- a/third_party/blink/web_tests/compositing/layer-creation/fixed-position-out-of-view-scaled-iframe.html
+++ b/third_party/blink/web_tests/compositing/layer-creation/fixed-position-out-of-view-scaled-iframe.html
@@ -13,14 +13,18 @@
     addEventListener("load", function() {
       internals.setPageScaleFactor(0.5);
       setTimeout(function() {
-        var layerTreeScaledDown = internals.layerTreeAsText(document.getElementById("iframe").contentDocument);
+        var layerTreeScaledDown = JSON.stringify(
+            JSON.parse(internals.layerTreeAsText(document.getElementById("iframe").contentDocument)).layers);
 
         internals.setPageScaleFactor(1.5);
         setTimeout(function() {
-          var layerTreeScaledUp = internals.layerTreeAsText(document.getElementById("iframe").contentDocument);
+          var layerTreeScaledUp = JSON.stringify(
+              JSON.parse(internals.layerTreeAsText(document.getElementById("iframe").contentDocument)).layers);
           // Because logical size of the frame is unchanged, the layer tree in the frame should not be affected by the page scale.
           document.getElementById("result").innerText =
-              layerTreeScaledUp == layerTreeScaledDown ? "PASS" : "FAIL";
+              layerTreeScaledUp == layerTreeScaledDown
+                  ? "PASS"
+                  : "FAIL\nScale 0.5:\n" + layerTreeScaledDown + "\nScale 1.5:\n" + layerTreeScaledUp;
           testRunner.notifyDone();
         }, 0);
       }, 0);
diff --git a/third_party/blink/web_tests/css3/filters/backdrop-filter-browser-zoom-expected.png b/third_party/blink/web_tests/css3/filters/backdrop-filter-browser-zoom-expected.png
index 66a21ab..d57edd36 100644
--- a/third_party/blink/web_tests/css3/filters/backdrop-filter-browser-zoom-expected.png
+++ b/third_party/blink/web_tests/css3/filters/backdrop-filter-browser-zoom-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/css3/filters/backdrop-filter-clip-radius-zoom-expected.png b/third_party/blink/web_tests/css3/filters/backdrop-filter-clip-radius-zoom-expected.png
new file mode 100644
index 0000000..6e558e6
--- /dev/null
+++ b/third_party/blink/web_tests/css3/filters/backdrop-filter-clip-radius-zoom-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/css3/filters/backdrop-filter-clip-radius-zoom.html b/third_party/blink/web_tests/css3/filters/backdrop-filter-clip-radius-zoom.html
new file mode 100644
index 0000000..7cd3464
--- /dev/null
+++ b/third_party/blink/web_tests/css3/filters/backdrop-filter-clip-radius-zoom.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<title>Backdrop filter clipping radius with zoom</title>
+<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
+
+<!--You should see two completely filled black rectangles with rounded corners.-->
+
+<!-- TODO(978481): Convert this back to WPT test. See crbug.com/978481 for
+  an implementation in WPT that is waiting for the WPT Fuzzy Matching feature.
+-->
+
+<div id='rect1'></div>
+<div id='rect2'></div>
+
+<style>
+body {
+  zoom: 5;
+}
+div {
+  width: 100px;
+  height: 20px;
+  backdrop-filter: invert(1);
+  border: 2px dashed green;
+}
+#rect1 {
+  border-radius: 20px / 5px;
+}
+#rect2 {
+  position: relative;
+  top:5px;
+  border-radius: 4px;
+}
+p {
+    font-size: 0.2em;
+}
+</style>
diff --git a/third_party/blink/web_tests/css3/filters/backdrop-filter-clip-rect-zoom-expected.png b/third_party/blink/web_tests/css3/filters/backdrop-filter-clip-rect-zoom-expected.png
index da57311..0a9456d 100644
--- a/third_party/blink/web_tests/css3/filters/backdrop-filter-clip-rect-zoom-expected.png
+++ b/third_party/blink/web_tests/css3/filters/backdrop-filter-clip-rect-zoom-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
index 7c18709..d4ae383c 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
@@ -5923,6 +5923,12 @@
      {}
     ]
    ],
+   "html/webappapis/user-prompts/print-manual.html": [
+    [
+     "html/webappapis/user-prompts/print-manual.html",
+     {}
+    ]
+   ],
    "input-events/input-events-cut-paste-manual.html": [
     [
      "input-events/input-events-cut-paste-manual.html",
@@ -36181,6 +36187,18 @@
      {}
     ]
    ],
+   "css/css-break/fieldset.html": [
+    [
+     "css/css-break/fieldset.html",
+     [
+      [
+       "/css/reference/ref-filled-green-100px-square.xht",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-break/floats-and-text-narrow-and-short-dynamic.html": [
     [
      "css/css-break/floats-and-text-narrow-and-short-dynamic.html",
@@ -59183,6 +59201,18 @@
      {}
     ]
    ],
+   "css/css-position/position-absolute-fieldset.html": [
+    [
+     "css/css-position/position-absolute-fieldset.html",
+     [
+      [
+       "/css/reference/ref-filled-green-100px-square.xht",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-position/position-relative-table-tbody-left-absolute-child.html": [
     [
      "css/css-position/position-relative-table-tbody-left-absolute-child.html",
@@ -60095,54 +60125,6 @@
      {}
     ]
    ],
-   "css/css-pseudo/first-line-change-inline-color-nested.html": [
-    [
-     "css/css-pseudo/first-line-change-inline-color-nested.html",
-     [
-      [
-       "/css/css-pseudo/first-line-change-inline-color-nested-ref.html",
-       "=="
-      ]
-     ],
-     {}
-    ]
-   ],
-   "css/css-pseudo/first-line-change-inline-color.html": [
-    [
-     "css/css-pseudo/first-line-change-inline-color.html",
-     [
-      [
-       "/css/css-pseudo/first-line-change-inline-color-ref.html",
-       "=="
-      ]
-     ],
-     {}
-    ]
-   ],
-   "css/css-pseudo/first-line-on-ancestor-block.html": [
-    [
-     "css/css-pseudo/first-line-on-ancestor-block.html",
-     [
-      [
-       "/css/css-pseudo/first-line-on-ancestor-block-ref.html",
-       "=="
-      ]
-     ],
-     {}
-    ]
-   ],
-   "css/css-pseudo/first-line-with-out-of-flow.html": [
-    [
-     "css/css-pseudo/first-line-with-out-of-flow.html",
-     [
-      [
-       "/css/css-pseudo/first-line-with-out-of-flow-ref.html",
-       "=="
-      ]
-     ],
-     {}
-    ]
-   ],
    "css/css-pseudo/marker-and-other-pseudo-elements.html": [
     [
      "css/css-pseudo/marker-and-other-pseudo-elements.html",
@@ -62687,6 +62669,18 @@
      {}
     ]
    ],
+   "css/css-sizing/auto-scrollbar-inside-stf-abspos.html": [
+    [
+     "css/css-sizing/auto-scrollbar-inside-stf-abspos.html",
+     [
+      [
+       "/css/css-sizing/auto-scrollbar-inside-stf-abspos-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-sizing/block-image-percentage-max-height-inside-inline.html": [
     [
      "css/css-sizing/block-image-percentage-max-height-inside-inline.html",
@@ -124322,6 +124316,12 @@
    "content-security-policy/reporting/report-only-in-meta.sub.html.sub.headers": [
     []
    ],
+   "content-security-policy/reporting/report-only-unsafe-eval-expected.txt": [
+    []
+   ],
+   "content-security-policy/reporting/report-only-unsafe-eval.html.sub.headers": [
+    []
+   ],
    "content-security-policy/reporting/report-original-url.sub.html.sub.headers": [
     []
    ],
@@ -138680,18 +138680,6 @@
    "css/css-pseudo/first-line-and-placeholder-ref.html": [
     []
    ],
-   "css/css-pseudo/first-line-change-inline-color-nested-ref.html": [
-    []
-   ],
-   "css/css-pseudo/first-line-change-inline-color-ref.html": [
-    []
-   ],
-   "css/css-pseudo/first-line-on-ancestor-block-ref.html": [
-    []
-   ],
-   "css/css-pseudo/first-line-with-out-of-flow-ref.html": [
-    []
-   ],
    "css/css-pseudo/idlharness-expected.txt": [
     []
    ],
@@ -139259,6 +139247,9 @@
    "css/css-sizing/META.yml": [
     []
    ],
+   "css/css-sizing/auto-scrollbar-inside-stf-abspos-ref.html": [
+    []
+   ],
    "css/css-sizing/clone-intrinsic-size-ref.html": [
     []
    ],
@@ -171923,9 +171914,6 @@
    "wake-lock/OWNERS": [
     []
    ],
-   "wake-lock/idlharness.https.any-expected.txt": [
-    []
-   ],
    "wake-lock/idlharness.https.any.worker-expected.txt": [
     []
    ],
@@ -194818,6 +194806,12 @@
      }
     ]
    ],
+   "content-security-policy/reporting/report-only-unsafe-eval.html": [
+    [
+     "content-security-policy/reporting/report-only-unsafe-eval.html",
+     {}
+    ]
+   ],
    "content-security-policy/reporting/report-original-url.sub.html": [
     [
      "content-security-policy/reporting/report-original-url.sub.html",
@@ -246592,6 +246586,12 @@
      }
     ]
    ],
+   "largest-contentful-paint/cross-origin-image.sub.html": [
+    [
+     "largest-contentful-paint/cross-origin-image.sub.html",
+     {}
+    ]
+   ],
    "largest-contentful-paint/observe-image.html": [
     [
      "largest-contentful-paint/observe-image.html",
@@ -284192,6 +284192,12 @@
      {}
     ]
    ],
+   "trusted-types/block-eval.tentative.html": [
+    [
+     "trusted-types/block-eval.tentative.html",
+     {}
+    ]
+   ],
    "trusted-types/block-string-assignment-to-DOMParser-parseFromString.tentative.html": [
     [
      "trusted-types/block-string-assignment-to-DOMParser-parseFromString.tentative.html",
@@ -284276,6 +284282,12 @@
      {}
     ]
    ],
+   "trusted-types/eval-with-permissive-csp.tentative.html": [
+    [
+     "trusted-types/eval-with-permissive-csp.tentative.html",
+     {}
+    ]
+   ],
    "trusted-types/idlharness.window.js": [
     [
      "trusted-types/idlharness.window.html",
@@ -320271,6 +320283,18 @@
    "b56292b4703acaee09b6dfb7b0f1bb674b788207",
    "support"
   ],
+  "content-security-policy/reporting/report-only-unsafe-eval-expected.txt": [
+   "c519bb48381748380014ee7c974b535aef9db1a2",
+   "support"
+  ],
+  "content-security-policy/reporting/report-only-unsafe-eval.html": [
+   "ebaf6941a898e88a96ec61e8742fd63375811c27",
+   "testharness"
+  ],
+  "content-security-policy/reporting/report-only-unsafe-eval.html.sub.headers": [
+   "549f3dbaa2e302777799d14c5ca1eb13a83493bf",
+   "support"
+  ],
   "content-security-policy/reporting/report-original-url.sub.html": [
    "45c1aeb2de86e64ec7de51bf0898c85f42d13320",
    "testharness"
@@ -343343,6 +343367,10 @@
    "fb80ec45bceec093481fa54513c606c5952628b1",
    "testharness"
   ],
+  "css/css-break/fieldset.html": [
+   "fe76988293b8d1d9a6226b23bb9b40e86fcc007a",
+   "reftest"
+  ],
   "css/css-break/floats-and-text-narrow-and-short-dynamic-ref.html": [
    "51855d2d7a724d44f44c1585708851fd62242fbe",
    "support"
@@ -344636,7 +344664,7 @@
    "reftest"
   ],
   "css/css-conditional/idlharness-expected.txt": [
-   "aa36ecb31338f12757bca6247de65a1488070c1a",
+   "bf46c15032c38cc21c3a6a50ca31e6edb1fff12b",
    "support"
   ],
   "css/css-conditional/idlharness.html": [
@@ -350856,7 +350884,7 @@
    "support"
   ],
   "css/css-fonts/math-script-level-and-math-style/math-script-level-001.tentative.html": [
-   "8856b2b14a910c34b04586123098896834bbd83b",
+   "6a353a635b82d89fb6a933aad2d7554059aa3da2",
    "testharness"
   ],
   "css/css-fonts/math-script-level-and-math-style/math-script-level-002.tentative-expected.txt": [
@@ -350864,7 +350892,7 @@
    "support"
   ],
   "css/css-fonts/math-script-level-and-math-style/math-script-level-002.tentative.html": [
-   "b3f56fefad7a5f242b7daafc96ff6723f328c8e1",
+   "0abce02e72d3687023a232d7398cd014aa0633f7",
    "testharness"
   ],
   "css/css-fonts/math-script-level-and-math-style/math-script-level-003.tentative-ref.html": [
@@ -350872,7 +350900,7 @@
    "support"
   ],
   "css/css-fonts/math-script-level-and-math-style/math-script-level-003.tentative.html": [
-   "501001243740a142ac8cad12a78b48caaa056be5",
+   "f5bf9982e27f0fde970761d0506eff7f5b6ec741",
    "reftest"
   ],
   "css/css-fonts/math-script-level-and-math-style/math-script-level-004.tentative-expected.txt": [
@@ -350880,7 +350908,7 @@
    "support"
   ],
   "css/css-fonts/math-script-level-and-math-style/math-script-level-004.tentative.html": [
-   "6fe35c2719e4e460e0c88f261ba3f5185a5f062d",
+   "cb82472a0c0af76af8174eaea43e1ab3e6346fd9",
    "testharness"
   ],
   "css/css-fonts/math-script-level-and-math-style/math-script-level-005.tentative-ref.html": [
@@ -350888,7 +350916,7 @@
    "support"
   ],
   "css/css-fonts/math-script-level-and-math-style/math-script-level-005.tentative.html": [
-   "7f289ffcd81daaf0fed81b4c02e7417ee75a7e18",
+   "34860d7a5c2734d08984ddd061520320aeaa46e1",
    "reftest"
   ],
   "css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-001.tentative-ref.html": [
@@ -350896,7 +350924,7 @@
    "support"
   ],
   "css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-001.tentative.html": [
-   "0765630fac3376b305504304c61aac1b1123663d",
+   "ad187b5dc254e672a66eedb2ae219ddf953156a6",
    "reftest"
   ],
   "css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-002.tentative-ref.html": [
@@ -350904,7 +350932,7 @@
    "support"
   ],
   "css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-002.tentative.html": [
-   "baff728800dad0002b663e85d26cfb8c6a2bb811",
+   "c111f2c38d5d0f5c614f125bde292c73727f754a",
    "reftest"
   ],
   "css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-003.tentative-ref.html": [
@@ -350912,7 +350940,7 @@
    "support"
   ],
   "css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-003.tentative.html": [
-   "827dbd41be42199c2d7ebc6e042e2d75b8dd9b50",
+   "4282481761207d8a947ddb8f462a9a2cd555ede8",
    "reftest"
   ],
   "css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-004.tentative-ref.html": [
@@ -350920,7 +350948,7 @@
    "support"
   ],
   "css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-004.tentative.html": [
-   "8917d68be11a55f373bd08918ae047b775f56b80",
+   "150d4285e9802beabe7b677c96465ba261633b1c",
    "reftest"
   ],
   "css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-005.tentative-ref.html": [
@@ -350928,7 +350956,7 @@
    "support"
   ],
   "css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-005.tentative.html": [
-   "90c09d50ffe3cbcf90218f612da077f87a9b8e34",
+   "718a564070d9b5c91023b1c3c054f6380527cbde",
    "reftest"
   ],
   "css/css-fonts/math-script-level-and-math-style/math-script-level-font-size-clamping-001.tentative-ref.html": [
@@ -350936,11 +350964,11 @@
    "support"
   ],
   "css/css-fonts/math-script-level-and-math-style/math-script-level-font-size-clamping-001.tentative.html": [
-   "6a70cf33a8b16e57ddf2e8acb3d87e10ecfbe6e6",
+   "e25ee56c3e334d83158149528193fb7ee734df81",
    "reftest"
   ],
   "css/css-fonts/math-script-level-and-math-style/math-style-001.tentative.html": [
-   "6df35170255c62bf391ce77b9629802c8969fa9b",
+   "56cb1eca09d7741275df03ea217e2b31acb04371",
    "testharness"
   ],
   "css/css-fonts/quoted-generic-ignored-ref.html": [
@@ -365943,6 +365971,10 @@
    "35d5f65055d09b377e55a7d3e5dcaf8d3b609a39",
    "reftest"
   ],
+  "css/css-position/position-absolute-fieldset.html": [
+   "b212e627ae13dc32080936512a85d72367d3dad3",
+   "reftest"
+  ],
   "css/css-position/position-absolute-in-inline-001.html": [
    "204260ee6784c9e648ab9f1e86b113f0d7227e22",
    "testharness"
@@ -366611,42 +366643,10 @@
    "2db3480feb5928c6a39fbf6084cf07bec0ba4767",
    "reftest"
   ],
-  "css/css-pseudo/first-line-change-inline-color-nested-ref.html": [
-   "84becd9a78d186ba3c0e658746fd56aef8039b74",
-   "support"
-  ],
-  "css/css-pseudo/first-line-change-inline-color-nested.html": [
-   "4a58f1ea5b623ffa5acd2993be16de399cd24127",
-   "reftest"
-  ],
-  "css/css-pseudo/first-line-change-inline-color-ref.html": [
-   "84becd9a78d186ba3c0e658746fd56aef8039b74",
-   "support"
-  ],
-  "css/css-pseudo/first-line-change-inline-color.html": [
-   "2a5be916b01d5d12eec1a4e81d912d95c7036916",
-   "reftest"
-  ],
   "css/css-pseudo/first-line-first-letter-insert-crash.html": [
    "17f035dedf1ef8df71918a1eacb01e1f0b80d46b",
    "testharness"
   ],
-  "css/css-pseudo/first-line-on-ancestor-block-ref.html": [
-   "7193bf25eb88c5443457480f7ce27782acd9473a",
-   "support"
-  ],
-  "css/css-pseudo/first-line-on-ancestor-block.html": [
-   "6b797107df80156b511b9dc58c5ee40d6114cfc4",
-   "reftest"
-  ],
-  "css/css-pseudo/first-line-with-out-of-flow-ref.html": [
-   "7193bf25eb88c5443457480f7ce27782acd9473a",
-   "support"
-  ],
-  "css/css-pseudo/first-line-with-out-of-flow.html": [
-   "798ecf8264e2ddb38257c6cb1174bcc069c6e6d1",
-   "reftest"
-  ],
   "css/css-pseudo/idlharness-expected.txt": [
    "977415282ae39a096e46008e91d55452f5244b02",
    "support"
@@ -369191,6 +369191,14 @@
    "0dbbb2f9404da7cfa89f2e75af3ca06cfbe4be7e",
    "testharness"
   ],
+  "css/css-sizing/auto-scrollbar-inside-stf-abspos-ref.html": [
+   "076893b2728772662f06b9474bcc1918c860b480",
+   "support"
+  ],
+  "css/css-sizing/auto-scrollbar-inside-stf-abspos.html": [
+   "9479b2eb3f51aed98741beadff4a8407b3ae9f33",
+   "reftest"
+  ],
   "css/css-sizing/block-image-percentage-max-height-inside-inline.html": [
    "2f6ad684c1fb0be5498dc6a5b938da67adfe3ec7",
    "reftest"
@@ -409004,7 +409012,7 @@
    "testharness"
   ],
   "element-timing/buffer-before-onload.html": [
-   "88a641bbb666816da9a636ed43d23c205bf0e8f9",
+   "68eeeefe15fdc587cdf0d4382f90c33ee3230290",
    "testharness"
   ],
   "element-timing/cross-origin-element.sub.html": [
@@ -411368,7 +411376,7 @@
    "support"
   ],
   "event-timing/bufferbeforeonload.html": [
-   "c91546ae41a499635573a2f7d7ea20b0283ca21f",
+   "c30c01aa96bb9bc835bb1ce93bff86f5e99789d7",
    "testharness"
   ],
   "event-timing/crossiframe.html": [
@@ -411388,15 +411396,15 @@
    "manual"
   ],
   "event-timing/observethenonload.html": [
-   "e1c36e302108b1bd886130c7df38d8e536dc4473",
+   "4145eae0a59a5da2fc5fbca95ee2721fc3b58b2b",
    "testharness"
   ],
   "event-timing/onloadthenobserve-firstInput.html": [
-   "1422ceaa95582200ba1250dcdd53aec502b59856",
+   "4efdfbbf006cdd8577b4d11b668974bfe20aaa84",
    "testharness"
   ],
   "event-timing/onloadthenobserve.html": [
-   "e2f9c932e6f5122af68ff84fd9495cf6fc216314",
+   "d97228577a187ddf10f5513697d9b0424aab4588",
    "testharness"
   ],
   "event-timing/only-observe-firstInput.html": [
@@ -411408,7 +411416,7 @@
    "testharness"
   ],
   "event-timing/resources/crossiframe-childframe.html": [
-   "7aa4ace73afad7878205736c02a81e863127bb43",
+   "ee9da6cad5411b477c3c11d6fc932ef132855bf7",
    "support"
   ],
   "event-timing/resources/event-timing-test-utils.js": [
@@ -435647,6 +435655,10 @@
    "55cb5ce527ea69fa23e6aba675719bb6524d7411",
    "manual"
   ],
+  "html/webappapis/user-prompts/print-manual.html": [
+   "3afef1d4fef99b589d060ea6742331f454325be7",
+   "manual"
+  ],
   "idle-detection/META.yml": [
    "7e5836bc3701366fba535a67a0ffc4032776b104",
    "support"
@@ -437679,12 +437691,16 @@
    "bbdc7aee84437ebd0e029556dd1d1fcb9670dc5d",
    "testharness"
   ],
+  "largest-contentful-paint/cross-origin-image.sub.html": [
+   "3716f9319227c0f5bdfbcf17a21a718cf7dd72e8",
+   "testharness"
+  ],
   "largest-contentful-paint/observe-image.html": [
-   "43cdfab074d62827fca5e55b8b334b9fafb6507b",
+   "8adf2150afcc74082d173f06f6aa929c33a52b87",
    "testharness"
   ],
   "largest-contentful-paint/observe-text.html": [
-   "77ed06e2aa1fb99d1199b9e3b6e99620037ef959",
+   "a9a0d750d66712eaf72fca9897d84794ae44c35d",
    "testharness"
   ],
   "layout-instability/buffer-layout-shift.html": [
@@ -437972,7 +437988,7 @@
    "support"
   ],
   "mathml/presentation-markup/direction/direction-overall.html": [
-   "7f377e540f4388088a401fb6995947d488d3f76f",
+   "5351ca51688d51b2e399a296d52e1b23543a0c96",
    "reftest"
   ],
   "mathml/presentation-markup/direction/direction-token-ref.html": [
@@ -437980,15 +437996,15 @@
    "support"
   ],
   "mathml/presentation-markup/direction/direction-token.html": [
-   "a02793cc84ac61c814b6f884869ae8a920a2b748",
+   "1896f3fe394d6a31f6f6aaff79f6bfc3d647c462",
    "reftest"
   ],
   "mathml/presentation-markup/direction/direction.html": [
-   "05ea8b74fb5fcdeb994cfc6d80dc88d2cd8ccde4",
+   "11d0515d7194050701cb82a12e96237d800ef741",
    "testharness"
   ],
   "mathml/presentation-markup/fractions/frac-1.html": [
-   "6b9fb2844298cbc228f91a23c6439e0f67f4f20b",
+   "6be38d5439da9181ee53464abcf22863a9d176ce",
    "testharness"
   ],
   "mathml/presentation-markup/fractions/frac-bar-001-ref.html": [
@@ -437996,7 +438012,7 @@
    "support"
   ],
   "mathml/presentation-markup/fractions/frac-bar-001.html": [
-   "d6151f62190768776e1a85b4c9b9bc6a8c0a69a6",
+   "887ff174cc40c9b06303d22e08cef59e9b72f382",
    "reftest"
   ],
   "mathml/presentation-markup/fractions/frac-color-001-notref.html": [
@@ -438004,7 +438020,7 @@
    "support"
   ],
   "mathml/presentation-markup/fractions/frac-color-001.html": [
-   "d1a3e6eac41ff23190225954016b7b37a8e71880",
+   "d0674c6c88afb875b2bf199c0931005acf34eba8",
    "reftest"
   ],
   "mathml/presentation-markup/fractions/frac-color-002-ref.html": [
@@ -438012,7 +438028,7 @@
    "support"
   ],
   "mathml/presentation-markup/fractions/frac-color-002.html": [
-   "597acab652c4ad9b94ec5c612b4fda53c422309c",
+   "4b45c8e1cfb7b4bb775832c03e5e692c22f19f90",
    "reftest"
   ],
   "mathml/presentation-markup/fractions/frac-created-dynamically-2-ref.html": [
@@ -438020,7 +438036,7 @@
    "support"
   ],
   "mathml/presentation-markup/fractions/frac-created-dynamically-2.html": [
-   "a9707071a0624c7701cef46ff7e3746fb3feeb00",
+   "2aa9675bb1f170640088a1071687890ee715523a",
    "reftest"
   ],
   "mathml/presentation-markup/fractions/frac-created-dynamically-3-ref.html": [
@@ -438028,7 +438044,7 @@
    "support"
   ],
   "mathml/presentation-markup/fractions/frac-created-dynamically-3.html": [
-   "cec71163c392ebc6891339f8f1d80bad8c3f2596",
+   "c90119d8ef5f5282331c94ae1c57a9a3c8284f9d",
    "reftest"
   ],
   "mathml/presentation-markup/fractions/frac-created-dynamically-ref.html": [
@@ -438036,7 +438052,7 @@
    "support"
   ],
   "mathml/presentation-markup/fractions/frac-created-dynamically.html": [
-   "3224eb4eaf188c32d6152339d86a87aa03cfa62e",
+   "7a555f20ea370b918bca2c64d5381cbab625732b",
    "reftest"
   ],
   "mathml/presentation-markup/fractions/frac-linethickness-001-ref.html": [
@@ -438044,7 +438060,7 @@
    "support"
   ],
   "mathml/presentation-markup/fractions/frac-linethickness-001.html": [
-   "eca82878caec21ea50ed0d3562ac3449e54c7567",
+   "4d6bda2c1670d9e615c7c3ffbc3891b90a22ecd4",
    "reftest"
   ],
   "mathml/presentation-markup/fractions/frac-linethickness-002-ref.html": [
@@ -438052,7 +438068,7 @@
    "support"
   ],
   "mathml/presentation-markup/fractions/frac-linethickness-002.html": [
-   "fd45994646938e4d22765524382004f19ef021b7",
+   "ce122f710160a71ac4eb7c9c7436447b7a4dd504",
    "reftest"
   ],
   "mathml/presentation-markup/fractions/frac-linethickness-003-notref.html": [
@@ -438060,7 +438076,7 @@
    "support"
   ],
   "mathml/presentation-markup/fractions/frac-linethickness-003.html": [
-   "b23cce80be40dd53ff898a5bc7d2246ae3770322",
+   "c9fa0f381749174ea1ac3222462574cb6232d95c",
    "reftest"
   ],
   "mathml/presentation-markup/fractions/frac-linethickness-004-ref.html": [
@@ -438068,7 +438084,7 @@
    "support"
   ],
   "mathml/presentation-markup/fractions/frac-linethickness-004.html": [
-   "ff96a3d753e226dcd2ecfa44808804f71ce02c45",
+   "d9689fcdc6813382d92ced5c5941181a47110b2c",
    "reftest"
   ],
   "mathml/presentation-markup/fractions/frac-mrow-001-ref.html": [
@@ -438076,7 +438092,7 @@
    "support"
   ],
   "mathml/presentation-markup/fractions/frac-mrow-001.html": [
-   "14476450008531dba09da4677b60ba65262dca0a",
+   "cb154c65edc38103a6ebc47b5fc9f1466655abb1",
    "reftest"
   ],
   "mathml/presentation-markup/fractions/frac-numalign-denomalign-001-ref.html": [
@@ -438084,15 +438100,15 @@
    "support"
   ],
   "mathml/presentation-markup/fractions/frac-numalign-denomalign-001.html": [
-   "d734f1d1f33922d5d29daa81d2ba7b1075415b23",
+   "59d663f6a4e455b2e66a1d22a6a141a7edc9a132",
    "reftest"
   ],
   "mathml/presentation-markup/fractions/frac-parameters-1.html": [
-   "59e335ae5a3df081e300705d6e5d020924af7bef",
+   "55404fa562c4dd304eb84702bee4578d02ae7925",
    "testharness"
   ],
   "mathml/presentation-markup/fractions/frac-parameters-2.html": [
-   "b1ee502ccdae6cf96d2f104d7daf1c2dbba390f7",
+   "63ab97760fc03161b9b3c63d09819463131864ad",
    "testharness"
   ],
   "mathml/presentation-markup/fractions/frac-parameters-gap-001-ref.html": [
@@ -438148,19 +438164,19 @@
    "support"
   ],
   "mathml/presentation-markup/fractions/frac-visibility-001.html": [
-   "8fbf5ac806e61e400214b2157180e8aac4fd2d31",
+   "65b95054db46339159fc6e0f8438144b02b19a09",
    "reftest"
   ],
   "mathml/presentation-markup/mrow/inferred-mrow-baseline.html": [
-   "0904d9f17ed2ed1e5af5134ea1ccfcb17674581b",
+   "672d90de93363331a418f33ecac8f2380bd5fb5f",
    "testharness"
   ],
   "mathml/presentation-markup/mrow/inferred-mrow-stretchy.html": [
-   "f75726c11d09d488e81041f4da79e30edc752895",
+   "75587d076c2790608133b7a81558a42dc2581cff",
    "testharness"
   ],
   "mathml/presentation-markup/operators/mo-axis-height-1.html": [
-   "c88484b9d59a7811f6cfde7065de4facbdf1f74a",
+   "63595b0278445db92e3e5bfe8b7cfb03fd9bdc6e",
    "testharness"
   ],
   "mathml/presentation-markup/operators/mo-form-dynamic-ref.html": [
@@ -438168,7 +438184,7 @@
    "support"
   ],
   "mathml/presentation-markup/operators/mo-form-dynamic.html": [
-   "dcd1ec53c31efb2435415e372786e7a1fae15775",
+   "fff3bd9ff336f52b7abb5efdf4b1fb767518ace7",
    "reftest"
   ],
   "mathml/presentation-markup/operators/mo-form-fallback-ref.html": [
@@ -438176,7 +438192,7 @@
    "support"
   ],
   "mathml/presentation-markup/operators/mo-form-fallback.html": [
-   "aab62087b595a5f784c0b98e6a6cd0e419ac3a17",
+   "b517dd574688fa0ab50dff4a120507de4a9797bf",
    "reftest"
   ],
   "mathml/presentation-markup/operators/mo-form-minus-plus-ref.html": [
@@ -438184,7 +438200,7 @@
    "support"
   ],
   "mathml/presentation-markup/operators/mo-form-minus-plus.html": [
-   "8a4ac3d737315cca5c2ad19a95e60146bd633cf1",
+   "487bca25eaa61d6fb0a110124e962e48fe55dd20",
    "reftest"
   ],
   "mathml/presentation-markup/operators/mo-form-ref.html": [
@@ -438192,7 +438208,7 @@
    "support"
   ],
   "mathml/presentation-markup/operators/mo-form.html": [
-   "7c11c14691162b69c1a2e854734ae6ac37db10e2",
+   "4f651d1c9b7ce43dd9e159793b630aa3c44b69af",
    "reftest"
   ],
   "mathml/presentation-markup/operators/mo-movablelimits-default-ref.html": [
@@ -438200,7 +438216,7 @@
    "support"
   ],
   "mathml/presentation-markup/operators/mo-movablelimits-default.html": [
-   "cd01fe47682ff1be6a09551589875088476f22c4",
+   "a05bef47ee76914d190af18141c966a4c3e12ef7",
    "reftest"
   ],
   "mathml/presentation-markup/operators/mo-movablelimits-dynamic-ref.html": [
@@ -438208,7 +438224,7 @@
    "support"
   ],
   "mathml/presentation-markup/operators/mo-movablelimits-dynamic.html": [
-   "a92c6c01dc9da3aed4012843eb156b36f877d050",
+   "53cf76c0588ff2dd664b3fb9e622c7e30b5c6eb7",
    "reftest"
   ],
   "mathml/presentation-markup/operators/mo-movablelimits-ref.html": [
@@ -438216,7 +438232,7 @@
    "support"
   ],
   "mathml/presentation-markup/operators/mo-movablelimits.html": [
-   "0855594100b2b5cac13fb464108bf130288c0e32",
+   "9be866fabee972bfa1608e2c2e2e64c439c07eea",
    "reftest"
   ],
   "mathml/presentation-markup/operators/mo-paint-lspace-rspace-ref.html": [
@@ -438224,59 +438240,59 @@
    "support"
   ],
   "mathml/presentation-markup/operators/mo-paint-lspace-rspace.html": [
-   "879962c4776181b6bb505d1e0072b5f5e58b667e",
+   "8fb095b61240d93caa3289f68fe52c37069f14c9",
    "reftest"
   ],
   "mathml/presentation-markup/radicals/root-parameters-1.html": [
-   "51d4713d4ea1c429e018c6a0914b130bc6dc1cd5",
+   "5ad0b7315e02b9d893937a376b0545ff7bb0eada",
    "testharness"
   ],
   "mathml/presentation-markup/scripts/subsup-1.html": [
-   "447aa66d4a30bc68f69fc17f91b08723229b14ff",
+   "01a6b0e1ed1fed2d069ac2b8b3e3716ffeee08b7",
    "testharness"
   ],
   "mathml/presentation-markup/scripts/subsup-2.html": [
-   "9e2b6dbef79089fe4c37ddbf6cbbdc4e7a3f1d36",
+   "2fd6963b6a7906aaeabd17a16a53a9ebbe56116f",
    "testharness"
   ],
   "mathml/presentation-markup/scripts/subsup-3.html": [
-   "742fa8cbce6463bcc2bb48bcdd98df488d1c086f",
+   "60df24a799027c0704be4622976ea8e2ae2a3d22",
    "testharness"
   ],
   "mathml/presentation-markup/scripts/subsup-4.html": [
-   "fc70fef9b801f41a32913ba4362927bedb94d0ef",
+   "6886766097cecff350ddcf3ce81bb87e70b4ce28",
    "testharness"
   ],
   "mathml/presentation-markup/scripts/subsup-5.html": [
-   "a67d68d5e031c1a8b31df877b213d255f002b935",
+   "2cc4e6d9554a1f6fde6c16e37fec2a39c441a0e8",
    "testharness"
   ],
   "mathml/presentation-markup/scripts/subsup-parameters-1.html": [
-   "208a0a870d6e61e3c7530fc620e650e00ca740da",
+   "7503ad166af33745701ab379cf51fd5cda8ce7dc",
    "testharness"
   ],
   "mathml/presentation-markup/scripts/subsup-parameters-2.html": [
-   "4a9db6618bcc1fa9fd459d9b0aa70f347bc62184",
+   "62aa7a9dc3be90a343dbcb4810e5cb479707be2e",
    "testharness"
   ],
   "mathml/presentation-markup/scripts/underover-1.html": [
-   "45367e3541b285bcc6256193ab7cd544baf422f3",
+   "b17835528a0ed8d78d643ef9f24dd7822b62f559",
    "testharness"
   ],
   "mathml/presentation-markup/scripts/underover-parameters-1.html": [
-   "cc09abb899ad629fb959aef9fc84f093ad0a6f76",
+   "d8a564a1159518195e889694aee8d0b167efec91",
    "testharness"
   ],
   "mathml/presentation-markup/scripts/underover-parameters-2.html": [
-   "d6d9185cc961c0d6d7e52825563b30c622f015a2",
+   "c10f77ee2c807b8a24566574f6ff6377736ae514",
    "testharness"
   ],
   "mathml/presentation-markup/scripts/underover-parameters-3.html": [
-   "23c7dfa744550ecf4032350fdf902f28863dac57",
+   "86562fd374bdb44983657954f5b554ae62db8984",
    "testharness"
   ],
   "mathml/presentation-markup/scripts/underover-parameters-4.html": [
-   "bfc3cafa913fb38dde5cecf83be0a4a710bfb763",
+   "f7fb389b59feb23276773a91b854fd6ab105fc15",
    "testharness"
   ],
   "mathml/presentation-markup/spaces/mspace-children-ref.html": [
@@ -438284,11 +438300,11 @@
    "support"
   ],
   "mathml/presentation-markup/spaces/mspace-children.html": [
-   "90d524ab418ed8c141da037eba9334c83fb5cced",
+   "8824c7967041dae2eb3d2943c79c4afff339ffb3",
    "reftest"
   ],
   "mathml/presentation-markup/spaces/space-1.html": [
-   "7bc5b8e5128d031c14d73b53939926a63265b3c9",
+   "53734deeee1126d83ee83fd4d5d8d37af4d6d6c9",
    "testharness"
   ],
   "mathml/presentation-markup/spaces/space-2-ref.html": [
@@ -438296,11 +438312,11 @@
    "support"
   ],
   "mathml/presentation-markup/spaces/space-2.html": [
-   "5b8351a1ed00d29e43d71e1f293fe63b1a0c2dc4",
+   "d7b38458998c7d4d96adf3f31ccaca9d9886469f",
    "reftest"
   ],
   "mathml/presentation-markup/tables/table-axis-height.html": [
-   "e723008b410342b087a0f066980f8520e55c4ccc",
+   "feb29077e8e34bcd3155f0661f4878bb93bdbf19",
    "testharness"
   ],
   "mathml/relations/css-styling/color-1-ref.html": [
@@ -438308,7 +438324,7 @@
    "support"
   ],
   "mathml/relations/css-styling/color-1.html": [
-   "8158cec6a86475d98042df02e8e89928516184d3",
+   "4d9d084a5a6b6414e01d4f4693237b4544e1ad48",
    "reftest"
   ],
   "mathml/relations/css-styling/display-1-ref.html": [
@@ -438316,11 +438332,11 @@
    "support"
   ],
   "mathml/relations/css-styling/display-1.html": [
-   "9ae6fe0b84f4d7b145b7fef0ed16168c3f4a74dd",
+   "38dd0bafdf84e5e20cfc904bc9b9f2a219a6b8ff",
    "reftest"
   ],
   "mathml/relations/css-styling/displaystyle-1.html": [
-   "ddb2173a2e01ff78b16bc9c86f4a0f2ad37935dc",
+   "c2ccb78dc9f49ed5dceb10a4a1f6eccf19ad3902",
    "testharness"
   ],
   "mathml/relations/css-styling/dynamic-dir-1-ref.html": [
@@ -438328,7 +438344,7 @@
    "support"
   ],
   "mathml/relations/css-styling/dynamic-dir-1.html": [
-   "c4c99d878c19305d02d0ed450e46da66802c0746",
+   "3667ece8c53ccdb4747794ff48dde79f5c405629",
    "reftest"
   ],
   "mathml/relations/css-styling/lengths-1-ref.html": [
@@ -438336,11 +438352,11 @@
    "support"
   ],
   "mathml/relations/css-styling/lengths-1.html": [
-   "30916d5a567a882eb9b0fb7c6a07b7c2e02416f2",
+   "9b0c27a26715dd2724360b841b3c581d0e967811",
    "reftest"
   ],
   "mathml/relations/css-styling/lengths-2.html": [
-   "aa38e9729de8569151b98307395ec8a2a5fe4b7f",
+   "73fd23bbfd91d0b5b274b546c53814eef66eb24a",
    "testharness"
   ],
   "mathml/relations/css-styling/mathsize-attribute-ref.html": [
@@ -438348,7 +438364,7 @@
    "support"
   ],
   "mathml/relations/css-styling/mathsize-attribute.html": [
-   "00d12e4839cb91e5294409af03d3ad3847eb4213",
+   "803f236a6a9dc241d978393c3305c9770ad4786d",
    "reftest"
   ],
   "mathml/relations/css-styling/mathvariant-bold-fraktur-ref.html": [
@@ -438356,7 +438372,7 @@
    "support"
   ],
   "mathml/relations/css-styling/mathvariant-bold-fraktur.html": [
-   "f4141810820518d0b1b3c2d832827dbaa77c6871",
+   "8085ad50832e0e14d3eee810cf32410e6c6d7707",
    "reftest"
   ],
   "mathml/relations/css-styling/mathvariant-bold-italic-ref.html": [
@@ -438364,7 +438380,7 @@
    "support"
   ],
   "mathml/relations/css-styling/mathvariant-bold-italic.html": [
-   "7b53e986d4f07796c8d44a8356081082ad238d96",
+   "1e1b0b0debd74e228f72df7e5a10040a9bcb3aea",
    "reftest"
   ],
   "mathml/relations/css-styling/mathvariant-bold-ref.html": [
@@ -438376,7 +438392,7 @@
    "support"
   ],
   "mathml/relations/css-styling/mathvariant-bold-sans-serif.html": [
-   "544db02f2eedc3229e57eac3016e3aa086061704",
+   "cd6cc7a0c5009d726f99c6a43b7304b8fd453be7",
    "reftest"
   ],
   "mathml/relations/css-styling/mathvariant-bold-script-ref.html": [
@@ -438384,11 +438400,11 @@
    "support"
   ],
   "mathml/relations/css-styling/mathvariant-bold-script.html": [
-   "dc48fbeaaf54ba184e02f062f074443de0e4835b",
+   "8ff7556dacd0496927b8d52e367d9e9150c4892d",
    "reftest"
   ],
   "mathml/relations/css-styling/mathvariant-bold.html": [
-   "a51be7b23fd2bdbc9364a29a0766b53dea67dad2",
+   "f11594f82707a86f964c9a0aa2c1cfede0fabdb0",
    "reftest"
   ],
   "mathml/relations/css-styling/mathvariant-double-struck-ref.html": [
@@ -438396,7 +438412,7 @@
    "support"
   ],
   "mathml/relations/css-styling/mathvariant-double-struck.html": [
-   "3090fdc85c04d037f391c34dec615ce8827c222c",
+   "856672bcecd8f80249ab910ced30ece8c48171ac",
    "reftest"
   ],
   "mathml/relations/css-styling/mathvariant-fraktur-ref.html": [
@@ -438404,7 +438420,7 @@
    "support"
   ],
   "mathml/relations/css-styling/mathvariant-fraktur.html": [
-   "8bd15a558bf77d652237a6470cd64401f1787134",
+   "8ef57a0e2b98c7daec4e7c9473803e18f81aa42f",
    "reftest"
   ],
   "mathml/relations/css-styling/mathvariant-initial-ref.html": [
@@ -438412,7 +438428,7 @@
    "support"
   ],
   "mathml/relations/css-styling/mathvariant-initial.html": [
-   "58751bd1266971584260bc5d2c25935da4951672",
+   "de4b89f7f5a5547eb3dccd66035125d604c64679",
    "reftest"
   ],
   "mathml/relations/css-styling/mathvariant-italic-ref.html": [
@@ -438420,7 +438436,7 @@
    "support"
   ],
   "mathml/relations/css-styling/mathvariant-italic.html": [
-   "5bcbd322de85c885987457b128ad71bd7de72601",
+   "4cf3de045630d50b933d0cd29b527ecdcb610d12",
    "reftest"
   ],
   "mathml/relations/css-styling/mathvariant-looped-ref.html": [
@@ -438428,7 +438444,7 @@
    "support"
   ],
   "mathml/relations/css-styling/mathvariant-looped.html": [
-   "0326e86e8f4faf907b3698d319160242949bb0e5",
+   "2a774c7716cb324f7627c3484af5e07228bccad6",
    "reftest"
   ],
   "mathml/relations/css-styling/mathvariant-monospace-ref.html": [
@@ -438436,7 +438452,7 @@
    "support"
   ],
   "mathml/relations/css-styling/mathvariant-monospace.html": [
-   "8d9a5d3df27c1a8e85e00c6fee00ff3ddb8ef066",
+   "1c23630ecf744436b83bd1baafd3e5f7e14fabb7",
    "reftest"
   ],
   "mathml/relations/css-styling/mathvariant-sans-serif-bold-italic-ref.html": [
@@ -438444,7 +438460,7 @@
    "support"
   ],
   "mathml/relations/css-styling/mathvariant-sans-serif-bold-italic.html": [
-   "21401bc84ac95a40069b471f824b4e05e116d171",
+   "f67a6ab6db254d01e8000e774d40f406e8c47b78",
    "reftest"
   ],
   "mathml/relations/css-styling/mathvariant-sans-serif-italic-ref.html": [
@@ -438452,7 +438468,7 @@
    "support"
   ],
   "mathml/relations/css-styling/mathvariant-sans-serif-italic.html": [
-   "2573872319148c1c0bdfb359858a5369c28cc176",
+   "ab43df50c568d1fbb48bd7e9a65bb0623eb29978",
    "reftest"
   ],
   "mathml/relations/css-styling/mathvariant-sans-serif-ref.html": [
@@ -438460,7 +438476,7 @@
    "support"
   ],
   "mathml/relations/css-styling/mathvariant-sans-serif.html": [
-   "513e0351cc3f3e714d76adc0379fd4421ac90080",
+   "bfee81c0cb6825c83ee7ddd14c3d96994fc28d6b",
    "reftest"
   ],
   "mathml/relations/css-styling/mathvariant-script-ref.html": [
@@ -438468,7 +438484,7 @@
    "support"
   ],
   "mathml/relations/css-styling/mathvariant-script.html": [
-   "efd3f7e8ff6925a7941ea387f65faf63c5186f01",
+   "dd7ab6a06db11746ce39a23ca11728090f23fefc",
    "reftest"
   ],
   "mathml/relations/css-styling/mathvariant-stretched-ref.html": [
@@ -438476,7 +438492,7 @@
    "support"
   ],
   "mathml/relations/css-styling/mathvariant-stretched.html": [
-   "20c5a39a83a75e1736bd4cdcb7baf81952667856",
+   "8530c6880481f1629482902b2fdcb7205841c0a8",
    "reftest"
   ],
   "mathml/relations/css-styling/mathvariant-tailed-ref.html": [
@@ -438484,7 +438500,7 @@
    "support"
   ],
   "mathml/relations/css-styling/mathvariant-tailed.html": [
-   "c88d433ce5f43314c1f1249aa48d36ca0141b4a0",
+   "823438ec12ae909a222f7b1a17872e6b577ce771",
    "reftest"
   ],
   "mathml/relations/css-styling/visibility-1-ref.html": [
@@ -438492,7 +438508,7 @@
    "support"
   ],
   "mathml/relations/css-styling/visibility-1.html": [
-   "c84f97aeeb84d79b7937338445de720986f246ce",
+   "7083ef70fb3c8b5e90b7e590364b5c2c3502aefe",
    "reftest"
   ],
   "mathml/relations/html5-tree/class-1-ref.html": [
@@ -438500,11 +438516,11 @@
    "support"
   ],
   "mathml/relations/html5-tree/class-1.html": [
-   "ee4b4b50d3f4d630c80fea170008b73d5b4b9851",
+   "a1efbd218462b05f99d78d9c04090df1457e575a",
    "reftest"
   ],
   "mathml/relations/html5-tree/class-2.html": [
-   "4e1ae6b707405d900e309ac11792606a6a6f295e",
+   "8d6515af9f97279e6c0ab036409f7f5a31757e8a",
    "testharness"
   ],
   "mathml/relations/html5-tree/color-attributes-1-ref.html": [
@@ -438512,11 +438528,11 @@
    "support"
   ],
   "mathml/relations/html5-tree/color-attributes-1.html": [
-   "6f9f93721a2a9173c9efb156bcc875bd61123edb",
+   "585aebaf393d85187b644a97f6e7f28535fa9095",
    "reftest"
   ],
   "mathml/relations/html5-tree/display-1.html": [
-   "77038ee223c5a319f8536f01532530093dc69c18",
+   "3a180f5d42bcd3d260019edabc6ecf455b8dcf8f",
    "testharness"
   ],
   "mathml/relations/html5-tree/dynamic-1-ref.html": [
@@ -438524,7 +438540,7 @@
    "support"
   ],
   "mathml/relations/html5-tree/dynamic-1.html": [
-   "48fdd25915598b63a849752260349b2bd2186720",
+   "b4d603a9aa6b59f243df113f79fc6bac89b1ec47",
    "reftest"
   ],
   "mathml/relations/html5-tree/href-click-1-ref.html": [
@@ -438532,7 +438548,7 @@
    "support"
   ],
   "mathml/relations/html5-tree/href-click-1.html": [
-   "dd6b7990a3c1719ff66c8e93d9d98b3ea97bfdf0",
+   "bf902b5e8f6ef20cbb55a2655a7b734b715bc05b",
    "reftest"
   ],
   "mathml/relations/html5-tree/href-click-2-ref.html": [
@@ -438540,11 +438556,11 @@
    "support"
   ],
   "mathml/relations/html5-tree/href-click-2.html": [
-   "4c5253df1596d4acf6208579fea731b058ac1a58",
+   "c23d87c2358c296ad331fb981ff58b6bea6253e9",
    "reftest"
   ],
   "mathml/relations/html5-tree/href-click-3.html": [
-   "f2863febc1f14d4e1170b1ebcddb9bf30aedfd24",
+   "a8475ea3ff801dfdb6f4d6d5a7e049a6eafe7c00",
    "testharness"
   ],
   "mathml/relations/html5-tree/integration-point-1-ref.html": [
@@ -438552,7 +438568,7 @@
    "support"
   ],
   "mathml/relations/html5-tree/integration-point-1.html": [
-   "3221a67503bc0cba619715c1fd25d1e2c2ce1081",
+   "4d10af86c72a7e2904ba92858456e6a7b6d3aaae",
    "reftest"
   ],
   "mathml/relations/html5-tree/integration-point-2-ref.html": [
@@ -438560,7 +438576,7 @@
    "support"
   ],
   "mathml/relations/html5-tree/integration-point-2.html": [
-   "d42eeb03ed21d133decebb8ee287c23ac08d9699",
+   "20d499d3f89f673a00ad22a53f8e9a116a2e6843",
    "reftest"
   ],
   "mathml/relations/html5-tree/integration-point-3-ref.html": [
@@ -438568,7 +438584,7 @@
    "support"
   ],
   "mathml/relations/html5-tree/integration-point-3.html": [
-   "1feb8317ff12252bc131dd7368c8162b79a0d803",
+   "1f62d8e630a1361ca65c5c76679f4e61bb2688dc",
    "reftest"
   ],
   "mathml/relations/html5-tree/required-extensions-2-ref.html": [
@@ -438576,7 +438592,7 @@
    "support"
   ],
   "mathml/relations/html5-tree/required-extensions-2.html": [
-   "1d7af04d9edcb76cd870e63145d3d7096bd59c09",
+   "e1d7a853704ce5f9edf2f2d6d6a414be51fe4e4b",
    "reftest"
   ],
   "mathml/relations/html5-tree/unique-identifier-1-iframe-1.html": [
@@ -438592,11 +438608,11 @@
    "support"
   ],
   "mathml/relations/html5-tree/unique-identifier-1.html": [
-   "42a547ac218f1d8590fd48a0d6331ef3b1c2c86d",
+   "e2315f149936fc5f23622533db63727b630c4e2f",
    "reftest"
   ],
   "mathml/relations/html5-tree/unique-identifier-2.html": [
-   "421ad7a86d03861084a98ea00b17e7b77e1365f5",
+   "2c3190c51e25769a2e83ea1812c3112c962d7ab4",
    "testharness"
   ],
   "mathml/relations/html5-tree/unique-identifier-3-ref.html": [
@@ -438604,7 +438620,7 @@
    "support"
   ],
   "mathml/relations/html5-tree/unique-identifier-3.html": [
-   "c58e5026cfad049bef38d825df256cc0111c7b8c",
+   "7cfac89e85b4cc0b39c2711756c0ce01194542df",
    "reftest"
   ],
   "mathml/relations/text-and-math/use-typo-metrics-1-ref.html": [
@@ -438612,7 +438628,7 @@
    "support"
   ],
   "mathml/relations/text-and-math/use-typo-metrics-1.html": [
-   "3797aef5fc0fa316cd8268accd8501be88cd6c44",
+   "4e92a846461b65d150cef9a9444de27abf40a26d",
    "reftest"
   ],
   "mathml/tools/axisheight.py": [
@@ -438636,7 +438652,7 @@
    "support"
   ],
   "mathml/tools/mathvariant-transforms.py": [
-   "ba99b595f0aa6338ba7a752801c4a32dbbefc9ef",
+   "51c7c187a170731aee47b3366bd8ce23a73f3611",
    "support"
   ],
   "mathml/tools/percentscaledown.py": [
@@ -475103,6 +475119,10 @@
    "6ae52639d1dc0d5b303cb4cdb6fb727481234be9",
    "testharness"
   ],
+  "trusted-types/block-eval.tentative.html": [
+   "e1a6a69accd1fa8ddaddd1c49970755a275bd8eb",
+   "testharness"
+  ],
   "trusted-types/block-string-assignment-to-DOMParser-parseFromString.tentative.html": [
    "4446a58836695355efd0fbf3f9f5a0bd6c60c8a3",
    "testharness"
@@ -475167,6 +475187,10 @@
    "a6aa061f7307330e027c3e8b26e6b931cee2bb7c",
    "testharness"
   ],
+  "trusted-types/eval-with-permissive-csp.tentative.html": [
+   "68d119a520121f2c5a3f88028abcf657799b9123",
+   "testharness"
+  ],
   "trusted-types/idlharness.window.js": [
    "de13697764ed487060de3dd425cd39cba73ff13b",
    "testharness"
@@ -476339,10 +476363,6 @@
    "776c59be658f749fcdd0a8ea6a92ac82c88d344e",
    "support"
   ],
-  "wake-lock/idlharness.https.any-expected.txt": [
-   "4c1af702441d331e3e39eb041911d60e10cb380c",
-   "support"
-  ],
   "wake-lock/idlharness.https.any.js": [
    "2ad9980dae53727ea328e942dead029a936875cc",
    "testharness"
diff --git a/third_party/blink/web_tests/external/wpt/content-security-policy/reporting/report-only-unsafe-eval-expected.txt b/third_party/blink/web_tests/external/wpt/content-security-policy/reporting/report-only-unsafe-eval-expected.txt
new file mode 100644
index 0000000..c519bb4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/content-security-policy/reporting/report-only-unsafe-eval-expected.txt
@@ -0,0 +1,6 @@
+This is a testharness.js-based test.
+PASS Eval is allowed because the CSP is report-only
+FAIL SPV event is still raised assert_unreached: SPV event has not been received Reached unreachable code
+FAIL Violation report status OK. assert_equals: No such report. expected "" but got "false"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/content-security-policy/reporting/report-only-unsafe-eval.html b/third_party/blink/web_tests/external/wpt/content-security-policy/reporting/report-only-unsafe-eval.html
new file mode 100644
index 0000000..ebaf694
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/content-security-policy/reporting/report-only-unsafe-eval.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <script nonce='abc' src="/resources/testharness.js"></script>
+    <script nonce='abc' src="/resources/testharnessreport.js"></script>
+    <!-- CSP headers
+Content-Security-Policy-Report-Only: script-src 'unsafe-inline' 'nonce-abc'; report-uri ../support/report.py?op=put&reportID={{$id}}
+-->
+</head>
+<body>
+    <script nonce='abc'>
+      var t = async_test("Eval is allowed because the CSP is report-only");
+
+      var t_spv = async_test("SPV event is still raised");
+      t_spv.step_timeout(t_spv.unreached_func("SPV event has not been received"), 3000);
+      document.addEventListener('securitypolicyviolation', t_spv.step_func(e => {
+        assert_equals(e.violatedDirective, "script-src");
+        assert_equals(e.blockedURI, "eval");
+      }));
+
+      try {
+        eval("t.done()");
+      } catch {
+        t.step(t.unreached_func("The eval should have executed succesfully"));
+        t_spv.step(tsv_unreached_func("The eval execution should have triggered a securitypolicyviolation event"));
+      }
+    </script>
+    <script nonce='abc' async defer src='../support/checkReport.sub.js?reportField=violated-directive&reportValue=script-src%20%27unsafe-inline%27'></script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/content-security-policy/reporting/report-only-unsafe-eval.html.sub.headers b/third_party/blink/web_tests/external/wpt/content-security-policy/reporting/report-only-unsafe-eval.html.sub.headers
new file mode 100644
index 0000000..549f3db
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/content-security-policy/reporting/report-only-unsafe-eval.html.sub.headers
@@ -0,0 +1,4 @@
+Cache-Control: no-store, no-cache, must-revalidate
+Pragma: no-cache
+Set-Cookie: report-only-unsafe-eval={{$id:uuid()}}; Path=/content-security-policy/reporting/
+Content-Security-Policy-Report-Only: script-src 'unsafe-inline' 'nonce-abc'; report-uri ../support/report.py?op=put&reportID={{$id}}
diff --git a/third_party/blink/web_tests/external/wpt/css/CSS2/linebox/inline-negative-margin-001.html b/third_party/blink/web_tests/external/wpt/css/CSS2/linebox/inline-negative-margin-001.html
new file mode 100644
index 0000000..e8a00ec
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/CSS2/linebox/inline-negative-margin-001.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<title>CSS Test: Check inline negative margin should not cause the line to wrap</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/check-layout-th.js"></script>
+<link rel="help" href="https://crbug.com/979894">
+<link rel="help" href="https://drafts.csswg.org/css2/visudet.html#inline-width">
+<link rel="author" title="Koji Ishii" href="mailto:kojii@chromium.org">
+<style>
+html, body { margin: 0; }
+div {
+  font-family: Ahem;
+  font-size: 10px;
+  line-height: 1;
+}
+#container {
+  display: inline-block;
+}
+span {
+  color: orange;
+}
+</style>
+<body>
+  <div id="container" data-expected-height=10>123 <span style="margin-left: -8ch">1234 </span></div>
+<script>checkLayout('#container');</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/fieldset.html b/third_party/blink/web_tests/external/wpt/css/css-break/fieldset.html
new file mode 100644
index 0000000..fe76988
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/fieldset.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/css-break-3/#possible-breaks">
+<link rel="match" href="../reference/ref-filled-green-100px-square.xht">
+<style>
+  fieldset { margin:0; border:none; padding:0; }
+  .block { break-inside:avoid; height:50px; background:green; }
+  .fail { background:red; }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="columns:2; column-fill:auto; width:100px; height:140px; column-gap:0; overflow:hidden;">
+  <fieldset>
+    <div class="block"></div>
+    <div class="block"></div>
+    <div class="block"></div>
+    <div class="block"></div>
+
+    <!-- No room for this - should be clipped: -->
+    <div class="block fail"></div>
+  </fieldset>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-001.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-001.tentative.html
index 8856b2b..6a353a6 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-001.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-001.tentative.html
@@ -4,6 +4,7 @@
     <title>math-script-level</title>
     <meta charset="utf-8">
     <link rel="help" href="https://github.com/w3c/csswg-drafts/issues/3746">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-math-script-level-property">
     <meta name="assert" content="Check the resolved value of math-script-level">
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-002.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-002.tentative.html
index b3f56fe..0abce02 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-002.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-002.tentative.html
@@ -4,6 +4,7 @@
     <title>math-script-level</title>
     <meta charset="utf-8">
     <link rel="help" href="https://github.com/w3c/csswg-drafts/issues/3746">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-math-script-level-property">
     <meta name="assert" content="Verify effect of math-script-level auto | add(<integer>) | <integer>, starting from different values of math-script-level.">
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-003.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-003.tentative.html
index 50100124..f5bf998 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-003.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-003.tentative.html
@@ -4,6 +4,7 @@
     <title>math-script-level</title>
     <meta charset="utf-8">
     <link rel="help" href="https://github.com/w3c/csswg-drafts/issues/3746">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-math-script-level-property">
     <meta name="assert" content="If font-size is specified or if the specified value of math-script-level is initial then math-script-level does not affect the computed value of font-size.">
     <link rel="match" href="math-script-level-003.tentative-ref.html">
     <link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
diff --git a/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-004.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-004.tentative.html
index 6fe35c2..cb82472a 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-004.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-004.tentative.html
@@ -4,6 +4,7 @@
     <title>math-script-level</title>
     <meta charset="utf-8">
     <link rel="help" href="https://github.com/w3c/csswg-drafts/issues/3746">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-math-script-level-property">
     <meta name="assert" content="Check the resolved value of math-script-level">
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-005.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-005.tentative.html
index 7f289ff..34860d7a 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-005.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-005.tentative.html
@@ -5,6 +5,7 @@
     <meta charset="utf-8">
     <link rel="help" href="https://github.com/w3c/csswg-drafts/issues/3746">
     <link rel="help" href="https://github.com/w3c/csswg-drafts/issues/3906">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-math-script-level-property">
     <meta name="assert" content="If the computed font-size is changed because of font-family change, math-script-level does not affect the computed value of font-size.">
     <link rel="match" href="math-script-level-005.tentative-ref.html">
   </head>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-001.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-001.tentative.html
index 0765630f..ad187b5d 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-001.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-001.tentative.html
@@ -4,6 +4,8 @@
     <title>math-script-level: auto and math-style</title>
     <meta charset="utf-8">
     <link rel="help" href="https://github.com/w3c/csswg-drafts/issues/3746">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-math-script-level-property">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-math-style-property">
     <meta name="assert" content="If math-script-level is 'auto' and the inherited value of math-style is 'display' then the computed value of math-script-level is the inherited value.">
     <link rel="match" href="math-script-level-auto-and-math-style-001.tentative-ref.html">
     <link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
diff --git a/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-002.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-002.tentative.html
index baff728..c111f2c3 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-002.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-002.tentative.html
@@ -4,6 +4,8 @@
     <title>math-script-level: auto and math-style</title>
     <meta charset="utf-8">
     <link rel="help" href="https://github.com/w3c/csswg-drafts/issues/3746">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-math-script-level-property">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-math-style-property">
     <meta name="assert" content="If math-script-level is 'auto' and the inherited value of math-style is 'inline' then the computed value of math-script-level is the inherited value plus one.">
     <link rel="match" href="math-script-level-auto-and-math-style-002.tentative-ref.html">
     <link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
diff --git a/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-003.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-003.tentative.html
index 827dbd4..428248176 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-003.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-003.tentative.html
@@ -4,6 +4,8 @@
     <title>math-script-level: auto and math-style</title>
     <meta charset="utf-8">
     <link rel="help" href="https://github.com/w3c/csswg-drafts/issues/3746">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-math-script-level-property">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-math-style-property">
     <meta name="assert" content="Initial value of math-style is 'inline'">
     <link rel="match" href="math-script-level-auto-and-math-style-003.tentative-ref.html">
     <link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
diff --git a/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-004.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-004.tentative.html
index 8917d68..150d428 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-004.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-004.tentative.html
@@ -4,6 +4,8 @@
     <title>math-script-level: auto and math-style</title>
     <meta charset="utf-8">
     <link rel="help" href="https://github.com/w3c/csswg-drafts/issues/3746">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-math-script-level-property">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-math-style-property">
     <meta name="assert" content="Initial value of math-style is 'inline'">
     <link rel="match" href="math-script-level-auto-and-math-style-004.tentative-ref.html">
     <link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
diff --git a/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-005.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-005.tentative.html
index 90c09d5..718a5640 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-005.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-005.tentative.html
@@ -4,6 +4,8 @@
     <title>math-script-level: auto and math-style</title>
     <meta charset="utf-8">
     <link rel="help" href="https://github.com/w3c/csswg-drafts/issues/3746">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-math-script-level-property">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-math-style-property">
     <meta name="assert" content="math-style is inherited">
     <link rel="match" href="math-script-level-auto-and-math-style-005.tentative-ref.html">
     <link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
diff --git a/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-font-size-clamping-001.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-font-size-clamping-001.tentative.html
index 6a70cf3..e25ee56 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-font-size-clamping-001.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-font-size-clamping-001.tentative.html
@@ -4,6 +4,7 @@
     <title>math-script-level clamping</title>
     <meta charset="utf-8">
     <link rel="help" href="https://github.com/w3c/csswg-drafts/issues/3739">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-math-script-level-property">
     <meta name="assert" content="Clamping due to browser's min font size only affects the used size.">
     <link rel="match" href="math-script-level-font-size-clamping-001.tentative-ref.html">
     <link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
diff --git a/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-style-001.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-style-001.tentative.html
index 6df3517..56cb1ec 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-style-001.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-fonts/math-script-level-and-math-style/math-style-001.tentative.html
@@ -4,6 +4,7 @@
     <title>math-style</title>
     <meta charset="utf-8">
     <link rel="help" href="https://github.com/w3c/csswg-drafts/issues/3746">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-math-style-property">
     <meta name="assert" content="Check the resolved value of math-style">
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-position/position-absolute-fieldset.html b/third_party/blink/web_tests/external/wpt/css/css-position/position-absolute-fieldset.html
new file mode 100644
index 0000000..b212e62
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-position/position-absolute-fieldset.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/CSS22/visudet.html#abs-non-replaced-width">
+<link rel="match" href="../reference/ref-filled-green-100px-square.xht">
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="position:relative; width:100px; height:100px; background:red;">
+  <fieldset style="position:absolute; left:0; top:0; right:0; bottom:0; margin:0; border:none; background:green;"></fieldset>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-pseudo/first-line-change-inline-color-nested-ref.html b/third_party/blink/web_tests/external/wpt/css/css-pseudo/first-line-change-inline-color-nested-ref.html
deleted file mode 100644
index 84becd9..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-pseudo/first-line-change-inline-color-nested-ref.html
+++ /dev/null
@@ -1,2 +0,0 @@
-<!DOCTYPE html>
-<p style="color: blue">Blue <span style="color: green">This text should be green.</span> Blue</p>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-pseudo/first-line-change-inline-color-nested.html b/third_party/blink/web_tests/external/wpt/css/css-pseudo/first-line-change-inline-color-nested.html
deleted file mode 100644
index 4a58f1e..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-pseudo/first-line-change-inline-color-nested.html
+++ /dev/null
@@ -1,23 +0,0 @@
-<!DOCTYPE html>
-<html class="reftest-wait">
-<link rel="help" href="https://drafts.csswg.org/css-pseudo-4/#first-line-pseudo">
-<link rel="match" href="first-line-change-inline-color-nested-ref.html">
-<style>
-  #block { color: green; }
-  #block::first-line { color: blue; }
-  .green { color: green; }
-</style>
-<div id="block">
-  <div>
-    <p>Blue <span id="target"><span>This text should be green.</span></span> Blue</p>
-  </div>
-</div>
-<script>
-requestAnimationFrame(() => {
-  requestAnimationFrame(() => {
-    target.className = 'green';
-    document.documentElement.removeAttribute('class');
-  });
-});
-</script>
-</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-pseudo/first-line-change-inline-color-ref.html b/third_party/blink/web_tests/external/wpt/css/css-pseudo/first-line-change-inline-color-ref.html
deleted file mode 100644
index 84becd9..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-pseudo/first-line-change-inline-color-ref.html
+++ /dev/null
@@ -1,2 +0,0 @@
-<!DOCTYPE html>
-<p style="color: blue">Blue <span style="color: green">This text should be green.</span> Blue</p>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-pseudo/first-line-change-inline-color.html b/third_party/blink/web_tests/external/wpt/css/css-pseudo/first-line-change-inline-color.html
deleted file mode 100644
index 2a5be916..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-pseudo/first-line-change-inline-color.html
+++ /dev/null
@@ -1,23 +0,0 @@
-<!DOCTYPE html>
-<html class="reftest-wait">
-<link rel="help" href="https://drafts.csswg.org/css-pseudo-4/#first-line-pseudo">
-<link rel="match" href="first-line-change-inline-color-ref.html">
-<style>
-  #block { color: green; }
-  #block::first-line { color: blue; }
-  .green { color: green; }
-</style>
-<div id="block">
-  <div>
-    <p>Blue <span id="target">This text should be green.</span> Blue</p>
-  </div>
-</div>
-<script>
-requestAnimationFrame(() => {
-  requestAnimationFrame(() => {
-    target.className = 'green';
-    document.documentElement.removeAttribute('class');
-  });
-});
-</script>
-</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-pseudo/first-line-on-ancestor-block-ref.html b/third_party/blink/web_tests/external/wpt/css/css-pseudo/first-line-on-ancestor-block-ref.html
deleted file mode 100644
index 7193bf25..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-pseudo/first-line-on-ancestor-block-ref.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<!DOCTYPE html>
-<div>
-  <span style="color: green">This text should be green.</span><br>
-  <span style="color: blue">This text should be blue.</span>
-</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-pseudo/first-line-on-ancestor-block.html b/third_party/blink/web_tests/external/wpt/css/css-pseudo/first-line-on-ancestor-block.html
deleted file mode 100644
index 6b797107..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-pseudo/first-line-on-ancestor-block.html
+++ /dev/null
@@ -1,16 +0,0 @@
-<!DOCTYPE html>
-<link rel="help" href="https://drafts.csswg.org/css-pseudo-4/#first-line-pseudo">
-<link rel="match" href="first-line-on-ancestor-block-ref.html">
-<style>
-  #block::first-line { color: green; }
-</style>
-<div id="block">
-  <div>
-    <div style="color: blue">
-      <div>
-        <span><span>This text should be green.</span></span><br>
-        This text should be blue.
-      </div>
-    </div>
-  </div>
-</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-pseudo/first-line-with-out-of-flow-ref.html b/third_party/blink/web_tests/external/wpt/css/css-pseudo/first-line-with-out-of-flow-ref.html
deleted file mode 100644
index 7193bf25..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-pseudo/first-line-with-out-of-flow-ref.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<!DOCTYPE html>
-<div>
-  <span style="color: green">This text should be green.</span><br>
-  <span style="color: blue">This text should be blue.</span>
-</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-pseudo/first-line-with-out-of-flow.html b/third_party/blink/web_tests/external/wpt/css/css-pseudo/first-line-with-out-of-flow.html
deleted file mode 100644
index 798ecf8..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-pseudo/first-line-with-out-of-flow.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<!DOCTYPE html>
-<link rel="help" href="https://drafts.csswg.org/css-pseudo-4/#first-line-pseudo">
-<link rel="match" href="first-line-with-out-of-flow-ref.html">
-<style>
-  #block::first-line { color: green; }
-</style>
-<div id="block">
-  <div style="position: absolute"><br></div>
-  <div style="float: right"><br></div>
-  <div>
-    <div style="position: absolute"><br></div>
-    <div style="float: right"><br></div>
-    <div style="color: blue">
-      <div>
-        <span><span>This text should be green.</span></span><br>
-        This text should be blue.
-      </div>
-    </div>
-  </div>
-</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/auto-scrollbar-inside-stf-abspos-ref.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/auto-scrollbar-inside-stf-abspos-ref.html
new file mode 100644
index 0000000..076893b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/auto-scrollbar-inside-stf-abspos-ref.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<style>
+  body { overflow:hidden; }
+</style>
+<p>The word PASS should be visible below.</p>
+<div style="position:absolute; height:5em; overflow-y:scroll;">
+  <div style="height:15em;">PASS</div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/auto-scrollbar-inside-stf-abspos.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/auto-scrollbar-inside-stf-abspos.html
new file mode 100644
index 0000000..9479b2eb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/auto-scrollbar-inside-stf-abspos.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/css-sizing-3/#valdef-width-fit-content-length-percentage">
+<link rel="match" href="auto-scrollbar-inside-stf-abspos-ref.html">
+<style>
+  /* Set non-auto overflow on the viewport, so that the UA is more likely to get
+     the size right the first time. Otherwise, a re-layout might hide the bug
+     that we're trying to test. */
+  body { overflow:hidden; }
+</style>
+<p>The word PASS should be visible below.</p>
+<div style="position:absolute;">
+  <div style="height:5em; overflow-y:auto;">
+    <div style="height:15em;">PASS</div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/buffer-before-onload.html b/third_party/blink/web_tests/external/wpt/element-timing/buffer-before-onload.html
index 88a641bb..68eeeef 100644
--- a/third_party/blink/web_tests/external/wpt/element-timing/buffer-before-onload.html
+++ b/third_party/blink/web_tests/external/wpt/element-timing/buffer-before-onload.html
@@ -10,8 +10,7 @@
 <script>
   /*
   In this test, a slow image is added to the frame to delay onload. The entry
-  for the other image should be available before onload, and thus delivered to
-  the performance timeline.
+  is available from the observer with the buffered flag set to true.
   */
   async_test(function(t) {
     if (!window.PerformanceElementTiming) {
@@ -23,18 +22,26 @@
     img.setAttribute('elementtiming', 'my_image');
     img.setAttribute('id', 'my_id');
     document.body.appendChild(img);
-    window.onload = t.step_func_done( () => {
-      const entries = performance.getEntriesByType('element');
-      assert_greater_than_equal(entries.length, 1);
-      assert_equals(performance.getEntries().filter(e => e.identifier === 'my_image').length, 1);
-      const entry = entries[0];
-      const index = window.location.href.lastIndexOf('/');
-      const pathname = window.location.href.substring(0, index) +
-          '/resources/square20.jpg';
-      checkElement(entry, pathname, 'my_image', 'my_id', beforeRender, img);
-      checkNaturalSize(entry, 20, 20);
+
+    // this PerformanceObserver should be notified about the previously
+    // buffered element entry
+    new PerformanceObserver(function (entryList, observer) {
+      assert_equals(entryList.getEntries().length, 1);
+      entryList.getEntries().forEach(function(entry) {
+        assert_equals(entry.entryType, "element");
+        const index = window.location.href.lastIndexOf('/');
+        const pathname = window.location.href.substring(0, index) +
+            '/resources/square20.jpg';
+        checkElement(entry, pathname, 'my_image', 'my_id', beforeRender, img);
+        checkNaturalSize(entry, 20, 20);
+        observer.disconnect();
+        t.done();
+      });
+    }).observe({
+      type: "element",
+      buffered: true
     });
-  }, "Element Timing: image loads before onload.");
+  }, "Element Timing: image loads before onload available from buffered flag.");
 
 </script>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/invisible-images.html b/third_party/blink/web_tests/external/wpt/element-timing/invisible-images.html
index eb53cd7c..06d9bfd0 100644
--- a/third_party/blink/web_tests/external/wpt/element-timing/invisible-images.html
+++ b/third_party/blink/web_tests/external/wpt/element-timing/invisible-images.html
@@ -20,9 +20,10 @@
       assert_unreached("PerformanceElementTiming is not implemented");
     }
     const observer = new PerformanceObserver(
-      t.step_func_done(() => {
+      t.step_func_done((entries) => {
         // The image should not have caused an entry, so fail test.
-        assert_unreached('Should not have received an entry!');
+        assert_unreached('Should not have received an entry! Received one with identifier '
+            + entries.getEntries()[0].identifier);
       })
     );
     observer.observe({entryTypes: ['element']});
@@ -46,6 +47,30 @@
       img3.setAttribute('elementtiming', 'my_image3');
       img3.setAttribute('id', 'displayNone');
       document.body.appendChild(img3);
+
+      const div = document.createElement('div');
+      div.setAttribute('id', 'opacity0');
+      const img4 = document.createElement('img');
+      img4.src = '/images/blue.png';
+      img4.setAttribute('elementtiming', 'my_image4');
+      div.appendChild(img4);
+      document.body.appendChild(div);
+
+      const div2 = document.createElement('div');
+      div2.setAttribute('id', 'visibilityHidden');
+      const img5 = document.createElement('img');
+      img5.src = '/images/blue.png';
+      img5.setAttribute('elementtiming', 'my_image5');
+      div.appendChild(img5);
+      document.body.appendChild(div2);
+
+      const div3 = document.createElement('div');
+      div3.setAttribute('id', 'displayNone');
+      const img6 = document.createElement('img');
+      img6.src = '/images/blue.png';
+      img6.setAttribute('elementtiming', 'my_image6');
+      div.appendChild(img6);
+      document.body.appendChild(div3);
       // Images have been added but should not cause entries to be dispatched.
       // Wait for 500ms and end test, ensuring no entry was created.
       t.step_timeout(() => {
diff --git a/third_party/blink/web_tests/external/wpt/event-timing/bufferbeforeonload.html b/third_party/blink/web_tests/external/wpt/event-timing/bufferbeforeonload.html
index c91546a..c30c01aa 100644
--- a/third_party/blink/web_tests/external/wpt/event-timing/bufferbeforeonload.html
+++ b/third_party/blink/web_tests/external/wpt/event-timing/bufferbeforeonload.html
@@ -27,9 +27,7 @@
       firstClickEnd = performance.now();
   }
 
-  function validateEntries() {
-    const entries = performance.getEntriesByName('mousedown', 'event');
-
+  function validateEntries(entries) {
     const entriesBeforeOnload = entries.filter(
         e => e.startTime < onloadStart);
     assert_equals(entriesBeforeOnload.length, 1,
@@ -48,8 +46,8 @@
 
     const entriesAfterOnload = entries.filter(
         e => e.startTime >= onloadStart);
-    assert_equals(entriesAfterOnload.length, 0,
-        "Events after onload shouldn't be buffered.");
+    assert_equals(entriesAfterOnload.length, 1,
+        "Events after onload should still be buffered.");
   }
 
   /* Timeline:
@@ -66,24 +64,31 @@
   async_test(function(t) {
     clickTimeMin = performance.now();
     clickAndBlockMain('button');
-    // Use a dummy observer to know when both clicks have been dispatched.
-    const observerPromise = new Promise((resolve, reject) => {
-      let entryCount = 0;
-      new PerformanceObserver(entryList => {
-        entryCount += entryList.getEntries().filter(
-          entry => entry.name === 'mousedown').length;
-        if (entryCount >= 2)
-          resolve();
-      }).observe({ entryTypes: ['event'] });
-    });
     // Event handlers will be dispatched asynchronously, so this will be called
     // before processing begins.
     processingStartMin = performance.now();
+    const bufferedEntries = [];
     on_event(window, 'load', e => {
       onloadStart = performance.now();
-      const clickPromise = clickAndBlockMain('button');
-      Promise.all([observerPromise, clickPromise]).then(
-          t.step_func_done(validateEntries));
+      // Register the observer after the page has been loaded
+      const observer = new PerformanceObserver(function (entryList, observer) {
+        entryList.getEntries().forEach(function(entry) {
+          assert_equals(entry.entryType, "event");
+          if (entry.name === 'mousedown') {
+            bufferedEntries.push(entry);
+          }
+          if (bufferedEntries.length == 2) {
+            validateEntries(bufferedEntries)
+            observer.disconnect();
+            t.done();
+          }
+        });
+      })
+      observer.observe({
+        type: "event",
+        buffered: true
+      });
+      clickAndBlockMain('button');
     });
   }, "Event Timing: click, onload.");
 
diff --git a/third_party/blink/web_tests/external/wpt/event-timing/observethenonload.html b/third_party/blink/web_tests/external/wpt/event-timing/observethenonload.html
index e1c36e3..4145eae 100644
--- a/third_party/blink/web_tests/external/wpt/event-timing/observethenonload.html
+++ b/third_party/blink/web_tests/external/wpt/event-timing/observethenonload.html
@@ -20,19 +20,6 @@
   let onloadStart;
   let observedEntries = [];
 
-  function verifyBuffer(bufferedEntries) {
-    assert_equals(bufferedEntries.length, 1,
-        "Only events before onload should be buffered.");
-    const entry = bufferedEntries[0];
-    assert_greater_than(onloadStart, entry.startTime,
-        "Onload should be later than entry's start time.");
-    assert_greater_than(entry.processingStart, timeBeforeFirstClick,
-        "The entry's processing start should be after timeBeforeFirstClick");
-    assert_less_than(entry.processingStart, timeAfterFirstClick,
-        "The entry's processing start should be before timeAfterFirstClick.");
-    verifyClickEvent(entry, true);
-  }
-
   function verifyObserverEntries(observedEntries) {
     const entriesAfterFirstClick = observedEntries.filter(
         e => e.startTime > timeAfterFirstClick);
@@ -60,6 +47,9 @@
         "entry2's processing start should be berfore timeAfterFirstClick.");
     assert_greater_than(timeAfterFirstClick, entry2.startTime,
         "timeAfterFirstClick should be later than entry2's start time.");
+    // This should happen before onLoad
+    assert_greater_than(onloadStart, entry2.startTime,
+        "Onload should be later than entry's start time.");
   }
 
   /* Timeline:
@@ -82,7 +72,7 @@
           entry => entry.name === 'mousedown'));
         if (observedEntries.length < 2) return;
         resolve(observedEntries);
-      }).observe({ entryTypes: ['event'] });
+      }).observe({ type: 'event' , buffered: true});
     });
     timeBeforeFirstClick = performance.now();
     clickAndBlockMain('button').then( () => {
@@ -95,7 +85,6 @@
       Promise.all([observerPromise, bufferPromise]).then((results) => {
         timeAfterSecondClick = performance.now();
         t.step(verifyObserverEntries.bind(null, results[0]));
-        t.step(verifyBuffer.bind(null, performance.getEntriesByName('mousedown', 'event')));
         t.done();
       });
     });
diff --git a/third_party/blink/web_tests/external/wpt/event-timing/onloadthenobserve-firstInput.html b/third_party/blink/web_tests/external/wpt/event-timing/onloadthenobserve-firstInput.html
index 1422cea..4efdfbb 100644
--- a/third_party/blink/web_tests/external/wpt/event-timing/onloadthenobserve-firstInput.html
+++ b/third_party/blink/web_tests/external/wpt/event-timing/onloadthenobserve-firstInput.html
@@ -23,30 +23,38 @@
   async_test(function(t) {
     let numFirstInputObserved = 0;
     let numEventsObserved = 0;
-    new PerformanceObserver(t.step_func((entryList, obs) => {
-        const observedEntries = entryList.getEntries().filter(
-            entry => entry.name === 'mousedown');
-        numEventsObserved += observedEntries.filter(entry =>
-            entry.entryType == 'event').length;
-        numFirstInputObserved += observedEntries.filter(entry =>
-            entry.entryType == 'firstInput').length;
+    let observedEventEntries = [];
+
+    const event_observer_promise = new Promise((resolve, reject) => {
+      new PerformanceObserver(function(entryList) {
+        observedEventEntries = entryList.getEntries().filter(
+          entry => entry.name === 'mousedown');
+        numEventsObserved += observedEventEntries.length;
         if (numEventsObserved >= 2) {
-          assert_equals(performance.getEntriesByType('event').length, 0,
-            "There should be no buffered event entries.");
-          assert_equals(performance.getEntriesByType('firstInput').length, 1,
-            "There should be a buffered firstInput entry.");
-          // There should be 2 event entries and one firstInput entry.
+          // There should be 2 event entries.
           assert_equals(numEventsObserved, 2,
             "There should be 2 observed event entries.");
-          assert_equals(numFirstInputObserved, 1,
-            "There should be only 1 observed firstInput entry.");
-          t.done();
+          resolve();
         }
-    })).observe({ entryTypes: ['event', 'firstInput'] });
-    on_event(window, 'load', () => {
-      clickAndBlockMain('button').then(() => {
+      }).observe({ type: 'event' , buffered: true});
+    });
+
+    const first_input_observer_promise = new Promise((resolve, reject) => {
+      new PerformanceObserver(function(entryList) {
+        assert_equals(entryList.getEntries().length, 1);
+        resolve();
+      }).observe({ type: 'firstInput' , buffered: true});
+    });
+
+    on_event(window, 'load', function(e) {
+      const click_promise = clickAndBlockMain('button').then(() => {
         clickAndBlockMain('button');
       });
+      Promise.all(
+          [event_observer_promise, first_input_observer_promise, click_promise]
+              ).then(() => {
+        t.done();
+      });
     });
   },
   "Event Timing: check firstInput after onload, observer, click, click."
diff --git a/third_party/blink/web_tests/external/wpt/event-timing/onloadthenobserve.html b/third_party/blink/web_tests/external/wpt/event-timing/onloadthenobserve.html
index e2f9c93..d9722857 100644
--- a/third_party/blink/web_tests/external/wpt/event-timing/onloadthenobserve.html
+++ b/third_party/blink/web_tests/external/wpt/event-timing/onloadthenobserve.html
@@ -17,16 +17,9 @@
   let observerStart;
   let processingStartMin;
 
-  function verifyBufferAndObserverEntries(observedEntries) {
-    // Verify buffer entries
-    const bufferedEntries = performance.getEntriesByName('mousedown', 'event');
-    const bufferedEntriesBeforeObserver = bufferedEntries.filter(e => e.startTime <
-      observerStart);
-    assert_equals(bufferedEntries.length, 0,
-      "Long latency events after onload should not be buffered."
-    );
-
-    // Verify observer entries
+  function verifyObserverEntries(observedEntries) {
+    // Verify observer entries.  Should not include first click since we didn't
+    // buffered to true.
     assert_equals(observedEntries.length, 1, "Long latency task after observer start should be observed.");
     const entry = observedEntries[0];
     verifyClickEvent(entry);
@@ -45,8 +38,8 @@
         callbackTime = performance.now();
         const observedEntries = entryList.getEntries().filter(
             entry => entry.name === 'mousedown');
-        verifyBufferAndObserverEntries(observedEntries);
-      })).observe({ entryTypes: ['event'] });
+        verifyObserverEntries(observedEntries);
+      })).observe({ type: 'event'});
     observerStart = performance.now();
   }
 
diff --git a/third_party/blink/web_tests/external/wpt/event-timing/resources/crossiframe-childframe.html b/third_party/blink/web_tests/external/wpt/event-timing/resources/crossiframe-childframe.html
index 7aa4ace..ee9da6c 100644
--- a/third_party/blink/web_tests/external/wpt/event-timing/resources/crossiframe-childframe.html
+++ b/third_party/blink/web_tests/external/wpt/event-timing/resources/crossiframe-childframe.html
@@ -2,7 +2,6 @@
 <html>
 <script src=event-timing-test-utils.js></script>
 <button id='button_child_frame'>Generate a 'click' event</button>
-<img src=slow-image.py>
 <script>
   const clickTimeMin = performance.now();
   clickAndBlockMain('button_child_frame');
@@ -10,7 +9,7 @@
   const observerPromise = new Promise((resolve, reject) => {
     new PerformanceObserver((entryList) => {
       resolve(entryList.getEntries().filter(entry => entry.name === 'mousedown'));
-    }).observe({ entryTypes: ['event'] });
+    }).observe({ type:'event', buffered: true });
   });
   window.addEventListener('load', e => {
     observerPromise.then((observedEntries) => {
diff --git a/third_party/blink/web_tests/external/wpt/html/webappapis/user-prompts/print-manual.html b/third_party/blink/web_tests/external/wpt/html/webappapis/user-prompts/print-manual.html
new file mode 100644
index 0000000..3afef1d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/webappapis/user-prompts/print-manual.html
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Printing</title>
+<link rel=help href="https://html.spec.whatwg.org/#printing">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+setup({explicit_timeout: true})
+</script>
+
+<p>Click on the button below.</p>
+<p>If the browser offers to print the current page, dismiss the prompt to
+return to this page. The following should then appear in the box marked as
+"Output":</p>
+<pre>
+before calling print()
+beforeprint
+afterprint
+after calling print()
+</pre>
+<p>If no user dialog appears, either the above or the following should be
+printed in the box marked as "Output":</p>
+<pre>
+before calling print()
+after calling print()
+</pre>
+<p>The test passes if the above criteria are satisfied and "PASS" appears in a
+table below this paragraph. The test fails otherwise.</p>
+
+<button onclick="testBtn(this)">Click here!</button>
+<h3>Output</h3>
+<pre id=output></pre>
+
+<script>
+"use strict";
+// This test is actually synchronous, but we use async_test()'s nice
+// infrastructure around t.step() to capture errors in event listeners. This is
+// necessary since it seems like in Chrome, exceptions in beforeprint/afterprint
+// event listeners are not propagated to error events. See
+// https://crbug.com/977828.
+const t = async_test();
+const log = [];
+function out(str) {
+  log.push(str);
+  output.appendChild(document.createTextNode(str + "\n"));
+}
+function testBtn(btn) {
+  btn.remove();
+  window.addEventListener("beforeprint", function (ev) {
+    // t.step_func() does not preserve `this`, which we test below.
+    t.step(() => {
+      out("beforeprint");
+      assert_equals(ev.__proto__, Event.prototype);
+      assert_equals(this, window);
+      assert_equals(ev.target, window);
+      assert_equals(ev.type, "beforeprint");
+    });
+  });
+  window.addEventListener("afterprint", function (ev) {
+    // t.step_func() does not preserve `this`, which we test below.
+    t.step(() => {
+      out("afterprint");
+      assert_equals(ev.__proto__, Event.prototype);
+      assert_equals(this, window);
+      assert_equals(ev.target, window);
+      assert_equals(ev.type, "afterprint");
+    });
+  });
+  out("before calling print()");
+  print();
+  out("after calling print()");
+  t.step(() => {
+    try {
+      assert_array_equals(log, [
+        "before calling print()",
+        "beforeprint",
+        "afterprint",
+        "after calling print()",
+      ]);
+    } catch (err) {
+      try {
+        assert_array_equals(log, [
+          "before calling print()",
+          "after calling print()",
+        ]);
+      } catch (err) {
+        assert_unreached("Output does not match either possibility");
+      }
+    }
+  });
+  t.done();
+}
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/largest-contentful-paint/cross-origin-image.sub.html b/third_party/blink/web_tests/external/wpt/largest-contentful-paint/cross-origin-image.sub.html
new file mode 100644
index 0000000..3716f93
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/largest-contentful-paint/cross-origin-image.sub.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<meta charset=utf-8>
+<title>Largest Contentful Paint: observe cross-origin images but without startTime.</title>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+  async_test(function (t) {
+    if (!window.LargestContentfulPaint) {
+      assert_unreached("LargestContentfulPaint is not implemented");
+    }
+    const observer = new PerformanceObserver(
+      t.step_func_done(function(entryList) {
+        assert_equals(entryList.getEntries().length, 1);
+        const entry = entryList.getEntries()[0];
+        assert_equals(entry.entryType, 'largestContentfulPaint');
+        assert_equals(entry.startTime, 0, 'The startTime value should be 0 for a cross origin image.');
+        assert_equals(entry.duration, 0);
+        // blue.png is 133 x 106.
+        assert_equals(entry.size, 14098);
+        assert_equals(entry.id, 'image_id');
+        const pathname = 'http://{{domains[www]}}:{{ports[http][1]}}/images/blue.png';
+        assert_equals(entry.url, pathname);
+        assert_equals(entry.responseEnd,
+            performance.getEntriesByName(pathname, 'resource')[0].responseEnd);
+        assert_equals(entry.element, document.getElementById('image_id'));
+      })
+    );
+    observer.observe({type: 'largestContentfulPaint', buffered: true});
+  }, 'Cross-origin image is observable, with startTime equal to 0.');
+</script>
+
+<img src='http://{{domains[www]}}:{{ports[http][1]}}/images/blue.png' id='image_id'/>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/largest-contentful-paint/observe-image.html b/third_party/blink/web_tests/external/wpt/largest-contentful-paint/observe-image.html
index 43cdfab0..8adf215 100644
--- a/third_party/blink/web_tests/external/wpt/largest-contentful-paint/observe-image.html
+++ b/third_party/blink/web_tests/external/wpt/largest-contentful-paint/observe-image.html
@@ -22,11 +22,19 @@
         assert_equals(entry.duration, 0);
         // blue.png is 133 x 106.
         assert_equals(entry.size, 14098);
+        assert_equals(entry.id, 'image_id');
+        // 25 is the length of "largest-contentful-paint/".
+        const index = window.location.href.lastIndexOf('/') - 25;
+        const pathname = window.location.href.substring(0, index) + '/images/blue.png';
+        assert_equals(entry.url, pathname);
+        assert_equals(entry.responseEnd,
+            performance.getEntriesByName(pathname, 'resource')[0].responseEnd);
+        assert_equals(entry.element, document.getElementById('image_id'));
       })
     );
     observer.observe({type: 'largestContentfulPaint', buffered: true});
-  }, 'Element with elementtiming attribute is observable.');
+  }, 'Same-origin image is observable.');
 </script>
 
-<img src='/images/blue.png'/>
+<img src='/images/blue.png' id='image_id'/>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/largest-contentful-paint/observe-text.html b/third_party/blink/web_tests/external/wpt/largest-contentful-paint/observe-text.html
index 77ed06e..a9a0d75 100644
--- a/third_party/blink/web_tests/external/wpt/largest-contentful-paint/observe-text.html
+++ b/third_party/blink/web_tests/external/wpt/largest-contentful-paint/observe-text.html
@@ -27,6 +27,10 @@
         // Width of at least 100 px.
         // TODO: find a good way to bound text width.
         assert_greater_than_equal(entry.size, 1200);
+        assert_equals(entry.responseEnd, 0);
+        assert_equals(entry.id, 'my_text');
+        assert_equals(entry.url, '');
+        assert_equals(entry.element, document.getElementById('my_text'));
       })
     );
     observer.observe({type: 'largestContentfulPaint', buffered: true});
@@ -34,5 +38,5 @@
   }, 'Element with elementtiming attribute is observable.');
 </script>
 
-<p>This is important text! :)</p>
+<p id='my_text'>This is important text! :)</p>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/layout-instability/observe-layout-shift.html b/third_party/blink/web_tests/external/wpt/layout-instability/observe-layout-shift.html
index db8d3ae4..25e4950f6 100644
--- a/third_party/blink/web_tests/external/wpt/layout-instability/observe-layout-shift.html
+++ b/third_party/blink/web_tests/external/wpt/layout-instability/observe-layout-shift.html
@@ -6,9 +6,39 @@
 #myDiv { position: relative; width: 300px; height: 100px; }
 </style>
 <div id='myDiv'></div>
+<button id='button'>Generate a 'click' event</button>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script>
+<script src=/resources/testdriver.js></script>
+<script src=/resources/testdriver-vendor.js></script><script>
+  let timeAfterClick;
+  function mainThreadBusy(duration) {
+    const now = performance.now();
+    while (performance.now() < now + duration);
+  }
+
+  function clickOnElement(id, callback) {
+    const element = document.getElementById(id);
+    const rect = element.getBoundingClientRect();
+    const xCenter = rect.x + rect.width / 2;
+    const yCenter = rect.y + rect.height / 2;
+    const leftButton = 0;
+    const clickHandler = () => {
+      mainThreadBusy(120);
+      if (callback)
+        callback();
+      element.removeEventListener("mousedown", clickHandler);
+    };
+    element.addEventListener("mousedown", clickHandler);
+    test_driver.click(element);
+  }
+
+  function clickAndBlockMain(id) {
+    return new Promise((resolve, reject) => {
+      clickOnElement(id, resolve);
+    });
+  }
+
   async_test(function (t) {
     const startTime = performance.now();
     const observer = new PerformanceObserver(
@@ -32,6 +62,37 @@
       document.getElementById('myDiv').style = "top: 60px";
     };
   }, 'Layout shift is observable via PerformanceObserver.');
+
+  async_test(function (t) {
+    const startTime = performance.now();
+    const observer = new PerformanceObserver(
+      t.step_func_done(function(entryList) {
+        const endTime = performance.now();
+        assert_equals(entryList.getEntries().length, 1);
+        const entry = entryList.getEntries()[0];
+        assert_equals(entry.entryType, "layoutShift");
+        assert_equals(entry.name, "");
+        assert_greater_than_equal(entry.startTime, startTime)
+        assert_less_than_equal(entry.startTime, endTime)
+        assert_equals(entry.duration, 0.0);
+        // The layout shift value should be: 300 * (100 + 60) / viewport size.
+        assert_equals(entry.value, 300 * (100 + 60) /
+          (document.documentElement.clientWidth * document.documentElement.clientHeight));
+        // We should see that there was a click input entry
+        assert_equals(entry.hadRecentInput, true);
+        assert_greater_than_equal(timeAfterClick, entry.lastInputTime);
+      })
+    );
+    observer.observe({entryTypes: ['layoutShift']});
+    window.onload = () => {
+      // User input event
+      clickAndBlockMain('button').then( () => {
+        timeAfterClick = performance.now();
+        // Modify the position of the div.
+        document.getElementById('myDiv').style = "top: 60px";
+      });
+    };
+  }, 'Layout shift within user input is observable via PerformanceObserver.');
 </script>
 
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/direction/direction-overall.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/direction/direction-overall.html
index 7f377e5..5351ca5 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/direction/direction-overall.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/direction/direction-overall.html
@@ -4,6 +4,11 @@
     <meta charset="utf-8"/>
     <title>Verify dir attribute on various containers</title>
     <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#attributes-common-to-html-and-mathml-elements">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#css-styling">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-top-level-math-element">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#horizontally-group-sub-expressions-mrow">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#style-change-mstyle">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#radicals-msqrt-mroot">
     <meta name="assert" content="Verify dir attribute on various containers.">
     <link rel="match" href="direction-overall-ref.html">
   </head>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/direction/direction-token.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/direction/direction-token.html
index a02793cc..1896f3f 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/direction/direction-token.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/direction/direction-token.html
@@ -4,6 +4,8 @@
     <meta charset="utf-8"/>
     <title>Verify dir attribute on token elements</title>
     <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#attributes-common-to-html-and-mathml-elements">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#css-styling">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#token-elements">
     <meta name="assert" content="Verify dir attribute on various token elements.">
     <link rel="match" href="direction-token-ref.html">
   </head>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/direction/direction.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/direction/direction.html
index 05ea8b7..11d0515 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/direction/direction.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/direction/direction.html
@@ -4,6 +4,7 @@
 <meta charset="utf-8"/>
 <title>Verify computed direction</title>
 <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#attributes-common-to-html-and-mathml-elements">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-top-level-math-element">
 <meta name="assert" content="Verify computed direction value.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-1.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-1.html
index 6b9fb28..6be38d5 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-1.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-1.html
@@ -3,7 +3,7 @@
 <head>
 <meta charset="utf-8">
 <title>Fraction</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#mfrac">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#fractions-mfrac">
 <meta name="assert" content="Verify fraction metrics for different sizes of numerator and denominator.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-bar-001.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-bar-001.html
index d6151f62..887ff17 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-bar-001.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-bar-001.html
@@ -3,7 +3,7 @@
   <head>
     <meta charset="utf-8">
     <title>fractions bar</title>
-    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#mfrac">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#fractions-mfrac">
     <meta name="assert" content="Verifies painting of the fraction bar">
     <link rel="match" href="frac-bar-001-ref.html">
     <style type="text/css">
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-color-001.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-color-001.html
index d1a3e6eac..d0674c6c 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-color-001.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-color-001.html
@@ -3,7 +3,8 @@
   <head>
     <meta charset="utf-8">
     <title>Fraction bar color</title>
-    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#mfrac">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#fractions-mfrac">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#css-styling">
     <meta name="assert" content="The CSS color property has an effect on the fraction bar.">
     <link rel="mismatch" href="frac-color-001-notref.html">
   </head>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-color-002.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-color-002.html
index 597acab..4b45c8e1 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-color-002.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-color-002.html
@@ -3,8 +3,9 @@
   <head>
     <meta charset="utf-8">
     <title>fractions color</title>
-    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#mfrac">
-    <meta name="assert" content="Verifies the color attribute affects the fraction bar">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#fractions-mfrac">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#css-styling">
+    <meta name="assert" content="Verifies the color property affects the fraction bar">
     <link rel="match" href="frac-color-002-ref.html">
   </head>
   <body>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-created-dynamically-2.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-created-dynamically-2.html
index a970707..2aa9675 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-created-dynamically-2.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-created-dynamically-2.html
@@ -3,7 +3,8 @@
 <head>
 <meta charset="utf-8">
 <title>mfrac created dynamically</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#mfrac">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#fractions-mfrac">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#dom-and-javascript">
 <meta name="assert" content="A dynamically added mfrac should render like the equivalent markup.">
 <link rel="match" href="frac-created-dynamically-2-ref.html">
 <script>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-created-dynamically-3.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-created-dynamically-3.html
index cec71163c..c90119d 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-created-dynamically-3.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-created-dynamically-3.html
@@ -3,7 +3,8 @@
 <head>
 <meta charset="utf-8">
 <title>mfrac created dynamically</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#mfrac">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#fractions-mfrac">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#dom-and-javascript">
 <meta name="assert" content="A dynamically added mfrac should render like the equivalent markup.">
 <link rel="match" href="frac-created-dynamically-3-ref.html">
 <script>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-created-dynamically.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-created-dynamically.html
index 3224eb4e..7a555f2 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-created-dynamically.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-created-dynamically.html
@@ -3,7 +3,8 @@
 <head>
 <meta charset="utf-8">
 <title>mfrac created dynamically</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#mfrac">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#fractions-mfrac">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#dom-and-javascript">
 <meta name="assert" content="A dynamically added mfrac should render like the equivalent markup.">
 <link rel="match" href="frac-created-dynamically-ref.html">
 <script>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-linethickness-001.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-linethickness-001.html
index eca8287..4d6bda2 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-linethickness-001.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-linethickness-001.html
@@ -3,7 +3,7 @@
   <head>
     <meta charset="utf-8">
     <title>fractions linethickness</title>
-    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#mfrac">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#fractions-mfrac">
     <meta name="assert" content="Verifies deprecated 'thin', 'medium', 'thick' and unitless values have no effect on the linethickness of the mfrac element">
     <link rel="match" href="frac-linethickness-001-ref.html">
     <style type="text/css">
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-linethickness-002.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-linethickness-002.html
index fd45994..ce122f7 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-linethickness-002.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-linethickness-002.html
@@ -3,7 +3,7 @@
   <head>
     <meta charset="utf-8">
     <title>fractions linethickness</title>
-    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#mfrac">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#fractions-mfrac">
     <meta name="assert" content="Verifies fraction with negative, percent and named space linethickness values.">
     <link rel="match" href="frac-linethickness-002-ref.html">
     <style type="text/css">
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-linethickness-003.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-linethickness-003.html
index b23cce8..c9fa0f3 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-linethickness-003.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-linethickness-003.html
@@ -3,7 +3,7 @@
   <head>
     <meta charset="utf-8">
     <title>fractions linethickness</title>
-    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#mfrac">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#fractions-mfrac">
     <meta name="assert" content="Verifies fraction with 0px bar.">
     <link rel="mismatch" href="frac-linethickness-003-notref.html">
   </head>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-linethickness-004.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-linethickness-004.html
index ff96a3d7..d9689fc 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-linethickness-004.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-linethickness-004.html
@@ -3,7 +3,7 @@
   <head>
     <meta charset="utf-8">
     <title>fractions linethickness</title>
-    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#mfrac">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#fractions-mfrac">
     <meta name="assert" content="Verifies that unitless value of zero causes no fraction bar to be painted">
     <link rel="match" href="frac-linethickness-004-ref.html">
     <style type="text/css">
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-mrow-001.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-mrow-001.html
index 1447645..cb154c6 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-mrow-001.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-mrow-001.html
@@ -3,8 +3,8 @@
   <head>
     <meta charset="utf-8">
     <title>Fraction mrow</title>
-    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#mfrac">
-    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#mrow">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#fractions-mfrac">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#horizontally-group-sub-expressions-mrow">
     <meta name="assert" content="This test that <mrow> elements can be used as numerator and denominator of fractions.">
     <link rel="match" href="frac-mrow-001-ref.html">
   </head>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-numalign-denomalign-001.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-numalign-denomalign-001.html
index d734f1d1..59d663f 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-numalign-denomalign-001.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-numalign-denomalign-001.html
@@ -3,7 +3,7 @@
   <head>
     <meta charset="utf-8">
     <title>Fraction numalign denomalign</title>
-    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#mfrac">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#fractions-mfrac">
     <meta name="assert" content="This fraction alignment with the numalign/denomalign attributes.">
     <link rel="match" href="frac-numalign-denomalign-001-ref.html">
   </head>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-parameters-1.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-parameters-1.html
index 59e335a..55404fa 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-parameters-1.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-parameters-1.html
@@ -3,7 +3,7 @@
 <head>
 <meta charset="utf-8">
 <title>Fraction parameters</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#mfrac">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#fractions-mfrac">
 <meta name="assert" content="Element mfrac correctly uses the fraction parameters from the MATH table.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-parameters-2.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-parameters-2.html
index b1ee502..63ab9776 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-parameters-2.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-parameters-2.html
@@ -3,7 +3,7 @@
 <head>
 <meta charset="utf-8">
 <title>Stack parameters</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#mfrac">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#fractions-mfrac">
 <meta name="assert" content="Element mfrac correctly uses the stack parameters from the MATH table.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-visibility-001.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-visibility-001.html
index 8fbf5ac..65b9505 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-visibility-001.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-visibility-001.html
@@ -3,7 +3,8 @@
   <head>
     <meta charset="utf-8">
     <title>Fraction bar visibility</title>
-    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#mfrac">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#fractions-mfrac">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#css-styling">
     <meta name="assert" content="The CSS visible property affects the rendering of the fraction bar.">
     <link rel="match" href="frac-visibility-001-ref.html">
   </head>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/mrow/inferred-mrow-baseline.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/mrow/inferred-mrow-baseline.html
index 0904d9f1..672d90d 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/mrow/inferred-mrow-baseline.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/mrow/inferred-mrow-baseline.html
@@ -3,7 +3,14 @@
 <head>
 <meta charset="utf-8">
 <title>Baseline of inferred mrows</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#mrow">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#horizontally-group-sub-expressions-mrow">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#radicals-msqrt-mroot">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#style-change-mstyle">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#error-message-merror">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#making-sub-expressions-invisible-mphantom">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-top-level-math-element">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#enclose-expression-inside-notation-menclose">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#adjust-space-around-content-mpadded">
 <meta name="assert" content="Baseline for mrow-like elements is correct.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/mrow/inferred-mrow-stretchy.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/mrow/inferred-mrow-stretchy.html
index f75726c1..75587d0 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/mrow/inferred-mrow-stretchy.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/mrow/inferred-mrow-stretchy.html
@@ -3,8 +3,16 @@
 <head>
 <meta charset="utf-8">
 <title>Stretchy in inferred mrows</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#mrow">
-<meta name="assert" content="Baseline for mrow-like elements is correct.">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#horizontally-group-sub-expressions-mrow">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#radicals-msqrt-mroot">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#style-change-mstyle">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#error-message-merror">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#making-sub-expressions-invisible-mphantom">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-top-level-math-element">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#enclose-expression-inside-notation-menclose">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#adjust-space-around-content-mpadded">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#operator-fence-separator-or-accent-mo">
+<meta name="assert" content="Operators can stretch inside mrow-like elements.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-axis-height-1.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-axis-height-1.html
index c88484b9..63595b02 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-axis-height-1.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-axis-height-1.html
@@ -3,7 +3,7 @@
 <head>
 <meta charset="utf-8">
 <title>mo axis height</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#tokenmo">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#operator-fence-separator-or-accent-mo">
 <meta name="assert" content="Element mo correctly uses the axis height parameter from the MATH table.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-form-dynamic.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-form-dynamic.html
index dcd1ec5..fff3bd9 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-form-dynamic.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-form-dynamic.html
@@ -3,7 +3,8 @@
   <head>
     <meta charset="utf-8"/>
     <title>&lt;mo&gt; dynamic form</title>
-    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#tokenmo">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#operator-fence-separator-or-accent-mo">
+     <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#dom-and-javascript">
     <meta name="assert" content="This test verifies that the form of the operators (and thus their spacing) is updated when you change the child list.">
     <link rel="match" href="mo-form-dynamic-ref.html">
 
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-form-fallback.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-form-fallback.html
index aab62087..b517dd5 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-form-fallback.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-form-fallback.html
@@ -3,7 +3,7 @@
   <head>
     <meta charset="utf-8"/>
     <title>form fallback</title>
-    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#tokenmo">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#operator-fence-separator-or-accent-mo">
     <meta name="assert" content="Verify fallback to postfix/prefix forms.">
     <link rel="match" href="mo-form-fallback-ref.html">
   </head>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-form-minus-plus.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-form-minus-plus.html
index 8a4ac3d..487bca25 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-form-minus-plus.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-form-minus-plus.html
@@ -3,7 +3,7 @@
   <head>
     <meta charset="utf-8"/>
     <title>form plus/minus</title>
-    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#tokenmo">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#operator-fence-separator-or-accent-mo">
     <meta name="assert" content="Verifies behavior of form of plus/minus etc.">
     <link rel="match" href="mo-form-minus-plus-ref.html">
   </head>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-form.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-form.html
index 7c11c14..4f651d1 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-form.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-form.html
@@ -3,7 +3,7 @@
   <head>
     <meta charset="utf-8"/>
     <title>&lt;mo&gt; form attribute</title>
-    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#tokenmo">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#operator-fence-separator-or-accent-mo">
     <meta name="assert" content="Verifies behavior of form attribute.">
     <link rel="match" href="mo-form-ref.html">
   </head>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-movablelimits-default.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-movablelimits-default.html
index cd01fe4..a05bef4 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-movablelimits-default.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-movablelimits-default.html
@@ -3,7 +3,7 @@
   <head>
     <meta charset="utf-8"/>
     <title>&lt;mo&gt; movablelimits default value</title>
-    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#tokenmo">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#operator-fence-separator-or-accent-mo">
     <meta name="assert" content="Verifies default value of movablelimits for some operators.">
     <link rel="match" href="mo-movablelimits-default-ref.html">
   </head>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-movablelimits-dynamic.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-movablelimits-dynamic.html
index a92c6c0..53cf76c 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-movablelimits-dynamic.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-movablelimits-dynamic.html
@@ -3,7 +3,8 @@
    <head>
      <meta charset="utf-8"/>
      <title>Test dynamically removing movablelimits attribute</title>
-     <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#tokenmo">
+     <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#operator-fence-separator-or-accent-mo">
+     <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#dom-and-javascript">
      <meta name="assert" content="Verifies dynamically removing movablelimits.">
      <link rel="match" href="mo-movablelimits-dynamic-ref.html">
      <script>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-movablelimits.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-movablelimits.html
index 08555941..9be866f 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-movablelimits.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-movablelimits.html
@@ -3,7 +3,7 @@
   <head>
     <meta charset="utf-8"/>
     <title>&lt;mo&gt; movablelimits</title>
-    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#tokenmo">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#operator-fence-separator-or-accent-mo">
     <meta name="assert" content="Verifies effect of movablelimits on mo in both displaystyle modes.">
     <link rel="match" href="mo-movablelimits-ref.html">
   </head>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-paint-lspace-rspace.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-paint-lspace-rspace.html
index 879962c4..8fb095b 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-paint-lspace-rspace.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-paint-lspace-rspace.html
@@ -3,7 +3,7 @@
   <head>
     <meta charset="utf-8">
     <title>&lt;mo&gt; paint lspace rspace</title>
-    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#tokenmo">
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#operator-fence-separator-or-accent-mo">
     <meta name="assert" content="Verifies values for lspace and rspace for element mo in LTR and RTL modes.">
     <link rel="match" href="mo-paint-lspace-rspace-ref.html">
   </head>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/radicals/root-parameters-1.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/radicals/root-parameters-1.html
index 51d4713..5ad0b73 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/radicals/root-parameters-1.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/radicals/root-parameters-1.html
@@ -3,7 +3,7 @@
 <head>
 <meta charset="utf-8">
 <title>Radical parameters</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#msqrt">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#radicals-msqrt-mroot">
 <meta name="assert" content="Elements msqrt and mroot correctly use the radical parameters from the MATH table.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-1.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-1.html
index 447aa66d..01a6b0e 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-1.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-1.html
@@ -3,7 +3,7 @@
 <head>
 <meta charset="utf-8">
 <title>Subscripts and Superscripts metrics</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#msubmsup">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#subscripts-and-superscripts-msub-msup-msubsup">
 <meta name="assert" content="Basic metrics for elements msub, msup and msubsup.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-2.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-2.html
index 9e2b6db..2fd6963 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-2.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-2.html
@@ -3,7 +3,7 @@
 <head>
 <meta charset="utf-8">
 <title>Subscripts and Superscripts metrics</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#msubmsup">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#subscripts-and-superscripts-msub-msup-msubsup">
 <meta name="assert" content="Basic metrics for the mmultiscript element.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-3.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-3.html
index 742fa8c..60df24a 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-3.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-3.html
@@ -3,7 +3,7 @@
 <head>
 <meta charset="utf-8">
 <title>Subscripts and Superscripts metrics</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#msubmsup">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#subscripts-and-superscripts-msub-msup-msubsup">
 <meta name="assert" content="Basic metrics for the mmultiscript element with many scripts.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-4.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-4.html
index fc70fef..68867660 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-4.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-4.html
@@ -3,7 +3,7 @@
 <head>
 <meta charset="utf-8">
 <title>Subscripts and Superscripts metrics</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#msubmsup">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#subscripts-and-superscripts-msub-msup-msubsup">
 <meta name="assert" content="Verify metrics of scripted elements for bases of different heights.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-5.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-5.html
index a67d68d..2cc4e6d9 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-5.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-5.html
@@ -3,7 +3,7 @@
 <head>
 <meta charset="utf-8">
 <title>Subscripts and Superscripts metrics</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#msubmsup">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#subscripts-and-superscripts-msub-msup-msubsup">
 <meta name="assert" content="Verify metrics of scripted elements with tall scripts.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-parameters-1.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-parameters-1.html
index 208a0a87..7503ad16 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-parameters-1.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-parameters-1.html
@@ -3,7 +3,7 @@
 <head>
 <meta charset="utf-8">
 <title>Subscripts and Superscripts parameters</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#msubmsup">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#subscripts-and-superscripts-msub-msup-msubsup">
 <meta name="assert" content="Elements msub, msup, subsup and msubsup correctly use the subscript and superscript parameters from the MATH table.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-parameters-2.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-parameters-2.html
index 4a9db661..62aa7a9d 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-parameters-2.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-parameters-2.html
@@ -3,7 +3,7 @@
 <head>
 <meta charset="utf-8">
 <title>Subscripts and Superscripts parameters</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#msubmsup">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#subscripts-and-superscripts-msub-msup-msubsup">
 <meta name="assert" content="Elements msub, msup, subsup and msubsup correctly use the italic correction from the MATH table.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-1.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-1.html
index 45367e3..b178355 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-1.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-1.html
@@ -3,7 +3,7 @@
 <head>
 <meta charset="utf-8">
 <title>Underscripts and Overscripts parameters</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#mundermover">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#underscripts-and-overscripts-munder-mover-munderover">
 <meta name="assert" content="Elements munder, mover, munderover correctly .">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-1.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-1.html
index cc09abb..d8a564a 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-1.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-1.html
@@ -3,7 +3,7 @@
 <head>
 <meta charset="utf-8">
 <title>Underscripts and Overscripts parameters</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#mundermover">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#underscripts-and-overscripts-munder-mover-munderover">
 <meta name="assert" content="Elements munder, mover, munderover correctly use the limit parameters from the MATH table.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-2.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-2.html
index d6d9185..c10f77e 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-2.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-2.html
@@ -3,7 +3,7 @@
 <head>
 <meta charset="utf-8">
 <title>Underscripts and Overscripts parameters</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#mundermover">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#underscripts-and-overscripts-munder-mover-munderover">
 <meta name="assert" content="Elements munder, mover, munderover correctly use the stretch stack parameters from the MATH table.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-3.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-3.html
index 23c7dfa..86562fd 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-3.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-3.html
@@ -3,7 +3,7 @@
 <head>
 <meta charset="utf-8">
 <title>Underscripts and Overscripts parameters</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#mundermover">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#underscripts-and-overscripts-munder-mover-munderover">
 <meta name="assert" content="Elements munder, mover, munderover correctly use underbar/overbar and AccentBaseHeight parameters from the MATH table.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-4.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-4.html
index bfc3caf..f7fb389 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-4.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-4.html
@@ -3,7 +3,7 @@
 <head>
 <meta charset="utf-8">
 <title>Underscripts and Overscripts parameters</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#mundermover">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#underscripts-and-overscripts-munder-mover-munderover">
 <meta name="assert" content="Elements munder, mover, munderover correctly use underbar/overbar and AccentBaseHeight parameters from the MATH table.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/spaces/mspace-children.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/spaces/mspace-children.html
index 90d524ab..8824c79 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/spaces/mspace-children.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/spaces/mspace-children.html
@@ -2,7 +2,7 @@
 <head>
 <meta charset="utf-8">
 <title>space</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#mspace">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#space-mspace">
 <link rel="match" href="mspace-children-ref.html"/>
 <meta name="assert" content="Verify mspace visual rendering of its children">
 </head>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/spaces/space-1.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/spaces/space-1.html
index 7bc5b8e..53734dee 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/spaces/space-1.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/spaces/space-1.html
@@ -3,7 +3,7 @@
 <head>
 <meta charset="utf-8">
 <title>Space</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#mspace">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#space-mspace">
 <meta name="assert" content="Verify mspace metrics for different values of height, depth and width">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/spaces/space-2.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/spaces/space-2.html
index 5b8351a1..d7b38458 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/spaces/space-2.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/spaces/space-2.html
@@ -3,7 +3,7 @@
 <head>
 <meta charset="utf-8">
 <title>space</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#mspace">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#space-mspace">
 <link rel="match" href="space-2-ref.html"/>
 <meta name="assert" content="Verify mspace visual rendering for different values of height, depth and width">
 </head>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/tables/table-axis-height.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/tables/table-axis-height.html
index e723008..feb2907 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/tables/table-axis-height.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/tables/table-axis-height.html
@@ -3,7 +3,7 @@
 <head>
 <meta charset="utf-8">
 <title>table axis height</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#tables">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#table-or-matrix-mtable">
 <meta name="assert" content="Element mtable correctly uses the axis height parameter from the MATH table.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/color-1.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/color-1.html
index 8158cec6..4d9d084 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/color-1.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/color-1.html
@@ -3,7 +3,11 @@
 <head>
 <meta charset="utf-8">
 <title>color</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#cssstyling">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#css-styling">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#text-mtext">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#fraction-with-nonzero-line-thickness">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#radical-symbol">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#enclose-expression-inside-notation-menclose">
 <link rel="match" href="color-1-ref.html"/>
 <meta name="assert" content="Verify that the color is used for text and graphical elements.">
 </head>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/display-1.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/display-1.html
index 9ae6fe0b..38dd0baf 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/display-1.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/display-1.html
@@ -3,7 +3,7 @@
 <head>
 <meta charset="utf-8">
 <title>display</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#cssstyling">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#css-styling">
 <link rel="match" href="display-1-ref.html"/>
 <meta name="assert" content="Verify that the 'display: none' property works on MathML elements.">
 </head>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/displaystyle-1.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/displaystyle-1.html
index ddb2173a..c2ccb78 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/displaystyle-1.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/displaystyle-1.html
@@ -3,7 +3,9 @@
 <head>
 <meta charset="utf-8">
 <title>displaystyle</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#cssstyling">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#css-styling">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-displaystyle-and-scriptlevel-attributes">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-math-style-property">
 <meta name="assert" content="Verify that the correct inheritance of the displaystyle value by measuring the size of large operators.">
 <style>
   @font-face {
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/dynamic-dir-1.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/dynamic-dir-1.html
index c4c99d87..3667ece8 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/dynamic-dir-1.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/dynamic-dir-1.html
@@ -2,9 +2,10 @@
 <html class="reftest-wait">
 <head>
   <meta charset="utf-8">
-  <title>Test dynamically changing dir attribute</title></head>
-  <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#cssproperties">
-  <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#dom">
+  <title>Test dynamically changing dir attribute</title>
+  <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#css-styling">
+  <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#attributes-common-to-html-and-mathml-elements">
+  <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#dom-and-javascript">
   <meta name="assert" content="The dir attribute should update direction map to css properties dynamically">
   <link rel="match" href="dynamic-dir-1-ref.html">
   <script>
@@ -16,10 +17,10 @@
         ["math", "mstyle", "mrow"].forEach((tag) => {
           let elements = document.getElementsByTagName(tag);
 
-          // set an explcit rtl where there was none
+          // set an explicit rtl where there was none
           elements[0].setAttribute("dir", "rtl");
 
-          // change explcit ltr to rtl
+          // change explicit ltr to rtl
           elements[1].setAttribute("dir", "rtl");
 
           // remove an explicitly set dir="rtl"
@@ -99,4 +100,4 @@
       </math>
     </p>
 </body>
-</html>
\ No newline at end of file
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/lengths-1.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/lengths-1.html
index 30916d5..9b0c27a 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/lengths-1.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/lengths-1.html
@@ -3,7 +3,10 @@
 <head>
 <meta charset="utf-8"/>
 <title>MathML lengths</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#cssstyling"/>
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#css-styling">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#types-for-mathml-attribute-values">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#legacy-mathml-style-attributes">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#space-mspace">
 <link rel="match" href="lengths-1-ref.html"/>
 <meta name="assert" content="Verify whether the different units are accepted for MathML lengths.">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/lengths-2.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/lengths-2.html
index aa38e97..73fd23bb 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/lengths-2.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/lengths-2.html
@@ -3,7 +3,10 @@
 <head>
 <meta charset="utf-8">
 <title>MathML lengths</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#cssstyling"/>
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#css-styling">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#types-for-mathml-attribute-values">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#legacy-mathml-style-attributes">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#space-mspace">
 <meta name="assert" content="Verify various cases of the MathML length syntax.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathsize-attribute.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathsize-attribute.html
index 00d12e4..803f236 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathsize-attribute.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathsize-attribute.html
@@ -3,6 +3,7 @@
   <head>
     <meta charset="utf-8"/>
     <title>Verify mathsize attribute</title>
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#css-styling">
     <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#legacy-mathml-style-attributes">
     <meta name="assert" content="Verify mathsize attribute values.">
     <link rel="match" href="mathsize-attribute-ref.html">
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-fraktur.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-fraktur.html
index f414181..8085ad5 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-fraktur.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-fraktur.html
@@ -3,7 +3,9 @@
 <head>
 <meta charset="utf-8"/>
 <title>mathvariant bold-fraktur</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#cssproperties"/>
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#css-styling">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-mathvariant-attribute">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#new-text-transform-values">
 <link rel="match" href="mathvariant-bold-fraktur-ref.html"/>
 <meta name="assert" content="Verify that a single-char <mtext> with a bold-fraktur mathvariant is equivalent to an <mtext> with the transformed unicode character.">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-italic.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-italic.html
index 7b53e986..1e1b0b0d 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-italic.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-italic.html
@@ -3,7 +3,9 @@
 <head>
 <meta charset="utf-8"/>
 <title>mathvariant bold-italic</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#cssproperties"/>
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#css-styling">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-mathvariant-attribute">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#new-text-transform-values">
 <link rel="match" href="mathvariant-bold-italic-ref.html"/>
 <meta name="assert" content="Verify that a single-char <mtext> with a bold-italic mathvariant is equivalent to an <mtext> with the transformed unicode character.">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-sans-serif.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-sans-serif.html
index 544db02f..cd6cc7a 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-sans-serif.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-sans-serif.html
@@ -3,7 +3,9 @@
 <head>
 <meta charset="utf-8"/>
 <title>mathvariant bold-sans-serif</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#cssproperties"/>
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#css-styling">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-mathvariant-attribute">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#new-text-transform-values">
 <link rel="match" href="mathvariant-bold-sans-serif-ref.html"/>
 <meta name="assert" content="Verify that a single-char <mtext> with a bold-sans-serif mathvariant is equivalent to an <mtext> with the transformed unicode character.">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-script.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-script.html
index dc48fbea..8ff7556da 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-script.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-script.html
@@ -3,7 +3,9 @@
 <head>
 <meta charset="utf-8"/>
 <title>mathvariant bold-script</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#cssproperties"/>
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#css-styling">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-mathvariant-attribute">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#new-text-transform-values">
 <link rel="match" href="mathvariant-bold-script-ref.html"/>
 <meta name="assert" content="Verify that a single-char <mtext> with a bold-script mathvariant is equivalent to an <mtext> with the transformed unicode character.">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold.html
index a51be7b..f11594f 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold.html
@@ -3,7 +3,9 @@
 <head>
 <meta charset="utf-8"/>
 <title>mathvariant bold</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#cssproperties"/>
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#css-styling">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-mathvariant-attribute">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#new-text-transform-values">
 <link rel="match" href="mathvariant-bold-ref.html"/>
 <meta name="assert" content="Verify that a single-char <mtext> with a bold mathvariant is equivalent to an <mtext> with the transformed unicode character.">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-double-struck.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-double-struck.html
index 3090fdc..856672b 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-double-struck.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-double-struck.html
@@ -3,7 +3,9 @@
 <head>
 <meta charset="utf-8"/>
 <title>mathvariant double-struck</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#cssproperties"/>
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#css-styling">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-mathvariant-attribute">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#new-text-transform-values">
 <link rel="match" href="mathvariant-double-struck-ref.html"/>
 <meta name="assert" content="Verify that a single-char <mtext> with a double-struck mathvariant is equivalent to an <mtext> with the transformed unicode character.">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-fraktur.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-fraktur.html
index 8bd15a55..8ef57a0 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-fraktur.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-fraktur.html
@@ -3,7 +3,9 @@
 <head>
 <meta charset="utf-8"/>
 <title>mathvariant fraktur</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#cssproperties"/>
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#css-styling">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-mathvariant-attribute">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#new-text-transform-values">
 <link rel="match" href="mathvariant-fraktur-ref.html"/>
 <meta name="assert" content="Verify that a single-char <mtext> with a fraktur mathvariant is equivalent to an <mtext> with the transformed unicode character.">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-initial.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-initial.html
index 58751bd1..de4b89f 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-initial.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-initial.html
@@ -3,7 +3,9 @@
 <head>
 <meta charset="utf-8"/>
 <title>mathvariant initial</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#cssproperties"/>
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#css-styling">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-mathvariant-attribute">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#new-text-transform-values">
 <link rel="match" href="mathvariant-initial-ref.html"/>
 <meta name="assert" content="Verify that a single-char <mtext> with a initial mathvariant is equivalent to an <mtext> with the transformed unicode character.">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-italic.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-italic.html
index 5bcbd32..4cf3de04 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-italic.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-italic.html
@@ -3,7 +3,9 @@
 <head>
 <meta charset="utf-8"/>
 <title>mathvariant italic</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#cssproperties"/>
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#css-styling">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-mathvariant-attribute">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#new-text-transform-values">
 <link rel="match" href="mathvariant-italic-ref.html"/>
 <meta name="assert" content="Verify that a single-char <mtext> with a italic mathvariant is equivalent to an <mtext> with the transformed unicode character.">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-looped.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-looped.html
index 0326e86e..2a774c7 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-looped.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-looped.html
@@ -3,7 +3,9 @@
 <head>
 <meta charset="utf-8"/>
 <title>mathvariant looped</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#cssproperties"/>
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#css-styling">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-mathvariant-attribute">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#new-text-transform-values">
 <link rel="match" href="mathvariant-looped-ref.html"/>
 <meta name="assert" content="Verify that a single-char <mtext> with a looped mathvariant is equivalent to an <mtext> with the transformed unicode character.">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-monospace.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-monospace.html
index 8d9a5d3..1c23630 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-monospace.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-monospace.html
@@ -3,7 +3,9 @@
 <head>
 <meta charset="utf-8"/>
 <title>mathvariant monospace</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#cssproperties"/>
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#css-styling">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-mathvariant-attribute">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#new-text-transform-values">
 <link rel="match" href="mathvariant-monospace-ref.html"/>
 <meta name="assert" content="Verify that a single-char <mtext> with a monospace mathvariant is equivalent to an <mtext> with the transformed unicode character.">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-sans-serif-bold-italic.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-sans-serif-bold-italic.html
index 21401bc..f67a6ab 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-sans-serif-bold-italic.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-sans-serif-bold-italic.html
@@ -3,7 +3,9 @@
 <head>
 <meta charset="utf-8"/>
 <title>mathvariant sans-serif-bold-italic</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#cssproperties"/>
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#css-styling">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-mathvariant-attribute">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#new-text-transform-values">
 <link rel="match" href="mathvariant-sans-serif-bold-italic-ref.html"/>
 <meta name="assert" content="Verify that a single-char <mtext> with a sans-serif-bold-italic mathvariant is equivalent to an <mtext> with the transformed unicode character.">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-sans-serif-italic.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-sans-serif-italic.html
index 2573872..ab43df5 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-sans-serif-italic.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-sans-serif-italic.html
@@ -3,7 +3,9 @@
 <head>
 <meta charset="utf-8"/>
 <title>mathvariant sans-serif-italic</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#cssproperties"/>
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#css-styling">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-mathvariant-attribute">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#new-text-transform-values">
 <link rel="match" href="mathvariant-sans-serif-italic-ref.html"/>
 <meta name="assert" content="Verify that a single-char <mtext> with a sans-serif-italic mathvariant is equivalent to an <mtext> with the transformed unicode character.">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-sans-serif.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-sans-serif.html
index 513e035..bfee81c0 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-sans-serif.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-sans-serif.html
@@ -3,7 +3,9 @@
 <head>
 <meta charset="utf-8"/>
 <title>mathvariant sans-serif</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#cssproperties"/>
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#css-styling">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-mathvariant-attribute">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#new-text-transform-values">
 <link rel="match" href="mathvariant-sans-serif-ref.html"/>
 <meta name="assert" content="Verify that a single-char <mtext> with a sans-serif mathvariant is equivalent to an <mtext> with the transformed unicode character.">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-script.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-script.html
index efd3f7e..dd7ab6a 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-script.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-script.html
@@ -3,7 +3,9 @@
 <head>
 <meta charset="utf-8"/>
 <title>mathvariant script</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#cssproperties"/>
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#css-styling">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-mathvariant-attribute">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#new-text-transform-values">
 <link rel="match" href="mathvariant-script-ref.html"/>
 <meta name="assert" content="Verify that a single-char <mtext> with a script mathvariant is equivalent to an <mtext> with the transformed unicode character.">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-stretched.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-stretched.html
index 20c5a39..8530c688 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-stretched.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-stretched.html
@@ -3,7 +3,9 @@
 <head>
 <meta charset="utf-8"/>
 <title>mathvariant stretched</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#cssproperties"/>
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#css-styling">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-mathvariant-attribute">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#new-text-transform-values">
 <link rel="match" href="mathvariant-stretched-ref.html"/>
 <meta name="assert" content="Verify that a single-char <mtext> with a stretched mathvariant is equivalent to an <mtext> with the transformed unicode character.">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-tailed.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-tailed.html
index c88d433c..823438e 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-tailed.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-tailed.html
@@ -3,7 +3,9 @@
 <head>
 <meta charset="utf-8"/>
 <title>mathvariant tailed</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#cssproperties"/>
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#css-styling">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-mathvariant-attribute">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#new-text-transform-values">
 <link rel="match" href="mathvariant-tailed-ref.html"/>
 <meta name="assert" content="Verify that a single-char <mtext> with a tailed mathvariant is equivalent to an <mtext> with the transformed unicode character.">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/visibility-1.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/visibility-1.html
index c84f97ae..7083ef7 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/visibility-1.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/visibility-1.html
@@ -3,7 +3,11 @@
 <head>
 <meta charset="utf-8">
 <title>visibility</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#cssstyling">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#css-styling">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#text-mtext">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#fraction-with-nonzero-line-thickness">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#radical-symbol">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#enclose-expression-inside-notation-menclose">
 <link rel="match" href="visibility-1-ref.html"/>
 <meta name="assert" content="Verify that visibility=hidden is used for text and graphical elements.">
 </head>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/class-1.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/class-1.html
index ee4b4b5..a1efbd2 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/class-1.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/class-1.html
@@ -3,7 +3,8 @@
 <head>
 <meta charset="utf-8"/>
 <title>Class</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#mathmltree"/>
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#attributes-common-to-html-and-mathml-elements">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#css-styling">
 <link rel="match" href="class-1-ref.html"/>
 <meta name="assert" content="Verify that the class attribute affects CSS selectors.">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/class-2.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/class-2.html
index 4e1ae6b..8d6515af 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/class-2.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/class-2.html
@@ -3,7 +3,8 @@
 <head>
 <meta charset="utf-8">
 <title>Class</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#mathmltree">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#attributes-common-to-html-and-mathml-elements">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#dom-and-javascript">
 <meta name="assert" content="Verify whether the getElementsByClassName() works for MathML elements.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/color-attributes-1.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/color-attributes-1.html
index 6f9f937..585aeba 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/color-attributes-1.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/color-attributes-1.html
@@ -3,7 +3,8 @@
 <head>
 <meta charset="utf-8"/>
 <title>Color Attributes</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#mathmltree"/>
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#css-styling">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#legacy-mathml-style-attributes">
 <meta name="assert" content="Verify that the mathcolor and mathbackground attributes are supported on the math element.">
 <link rel="match" href="color-attributes-1-ref.html"/>
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/display-1.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/display-1.html
index 77038ee2..3a180f5d 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/display-1.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/display-1.html
@@ -3,7 +3,8 @@
 <head>
 <meta charset="utf-8"/>
 <title>MathML display attribute</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#mathmltree">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#css-styling">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-top-level-math-element">
 <meta name="assert" content="Verify that the display attribute on the math element is supported and impacts centering and line breaking with surrounding content.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/dynamic-1.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/dynamic-1.html
index 48fdd2591..b4d603a9 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/dynamic-1.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/dynamic-1.html
@@ -3,7 +3,7 @@
 <head>
 <meta charset="utf-8"/>
 <title>Dynamic MathML DOM</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#dom"/>
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#dom-and-javascript"/>
 <link rel="match" href="dynamic-1-ref.html"/>
 <meta name="assert" content="Verify that the MathML DOM tree can be modified via javascript and that the rendering is correctly updated.">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/href-click-1.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/href-click-1.html
index dd6b7990..bf902b5e 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/href-click-1.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/href-click-1.html
@@ -3,7 +3,8 @@
 <head>
 <meta charset="utf-8"/>
 <title>href click</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#mathmltree">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#attributes-common-to-html-and-mathml-elements">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#dom-and-javascript">
 <link rel="match" href="href-click-1-ref.html"/>
 <meta name="assert" content="Verify that a click on a link moves to the target.">
 <script type="text/javascript">
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/href-click-2.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/href-click-2.html
index 4c5253d..c23d87c2 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/href-click-2.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/href-click-2.html
@@ -3,7 +3,8 @@
 <head>
 <meta charset="utf-8"/>
 <title>href click</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#mathmltree">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#attributes-common-to-html-and-mathml-elements">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#dom-and-javascript">
 <link rel="match" href="href-click-2-ref.html"/>
 <meta name="assert" content="Verify that a click on an element bubbles to an ancestor link.">
 <script type="text/javascript">
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/href-click-3.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/href-click-3.html
index f2863fe..a8475ea 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/href-click-3.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/href-click-3.html
@@ -3,7 +3,8 @@
 <head>
 <meta charset="utf-8">
 <title>href click</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#mathmltree">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#attributes-common-to-html-and-mathml-elements">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#dom-and-javascript">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/resources/testdriver.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/integration-point-1.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/integration-point-1.html
index 3221a67..4d10af8 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/integration-point-1.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/integration-point-1.html
@@ -3,7 +3,7 @@
 <head>
 <meta charset="utf-8"/>
 <title>MathML inside foreignObject</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#dom"/>
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#html-and-svg">
 <link rel="match" href="integration-point-1-ref.html"/>
 <meta name="assert" content="Verify that MathML can be used inside a foreignObject element.">
 </head>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/integration-point-2.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/integration-point-2.html
index d42eeb0..20d499d 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/integration-point-2.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/integration-point-2.html
@@ -3,7 +3,7 @@
 <head>
 <meta charset="utf-8"/>
 <title>MathML as a phrasing content</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#dom"/>
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#html-and-svg">
 <link rel="match" href="integration-point-2-ref.html"/>
 <meta name="assert" content="Verify that MathML can be used at positions where phrasing content is accepted.">
 <style type="text/css">
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/integration-point-3.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/integration-point-3.html
index 1feb8317..1f62d8e 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/integration-point-3.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/integration-point-3.html
@@ -3,7 +3,7 @@
 <head>
 <meta charset="utf-8"/>
 <title>phrasing content inside mtext</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#dom"/>
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#html-and-svg">
 <link rel="match" href="integration-point-3-ref.html"/>
 <meta name="assert" content="Verify that <mtext> can contain phrasing content">
 <style type="text/css">
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/required-extensions-2.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/required-extensions-2.html
index 1d7af04..e1d7a85 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/required-extensions-2.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/required-extensions-2.html
@@ -3,7 +3,7 @@
 <head>
 <meta charset="utf-8"/>
 <title>SVG requiredExtensions</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#dom"/>
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#html-and-svg">
 <link rel="match" href="required-extensions-2-ref.html"/>
 <meta name="assert" content="Verify that a foreignObject with MathML used as a requiredExtensions value is selected for display in a SVG switch element.">
 </head>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/unique-identifier-1.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/unique-identifier-1.html
index 42a547a..e2315f14 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/unique-identifier-1.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/unique-identifier-1.html
@@ -3,7 +3,7 @@
 <head>
 <meta charset="utf-8"/>
 <title>Unique identifier</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#mathmltree"/>
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#attributes-common-to-html-and-mathml-elements">
 <link rel="match" href="unique-identifier-1-ref.html"/>
 <meta name="assert" content="Verify that the id on a MathML element can be used as a fragment identifier in order to force initial scrolling.">
 </head>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/unique-identifier-2.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/unique-identifier-2.html
index 421ad7a..2c3190c 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/unique-identifier-2.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/unique-identifier-2.html
@@ -3,7 +3,8 @@
 <head>
 <meta charset="utf-8">
 <title>Unique Identifier</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#mathmltree">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#attributes-common-to-html-and-mathml-elements">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#dom-and-javascript">
 <meta name="assert" content="Verify whether the getElementById() works for MathML elements.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/unique-identifier-3.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/unique-identifier-3.html
index c58e502..7cfac89e 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/unique-identifier-3.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/unique-identifier-3.html
@@ -3,7 +3,8 @@
 <head>
 <meta charset="utf-8"/>
 <title>Unique Identifier</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#mathmltree"/>
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#attributes-common-to-html-and-mathml-elements">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#css-styling">
 <link rel="match" href="unique-identifier-3-ref.html"/>
 <meta name="assert" content="Verify that the id attribute affects CSS selectors.">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/text-and-math/use-typo-metrics-1.html b/third_party/blink/web_tests/external/wpt/mathml/relations/text-and-math/use-typo-metrics-1.html
index 3797aef5..4e92a84 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/relations/text-and-math/use-typo-metrics-1.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/text-and-math/use-typo-metrics-1.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8"/>
 <title>Open Font Format: USE_TYPO_METRICS</title>
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#openfontformat"/>
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#text-layout"/>
 <link rel="match" href="use-typo-metrics-1-ref.html"/>
 <meta name="assert" content="Verify that the USE_TYPO_METRICS flag from the OS/2 table is taken into account to calculate line height.">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/tools/mathvariant-transforms.py b/third_party/blink/web_tests/external/wpt/mathml/tools/mathvariant-transforms.py
index ba99b59..51c7c18 100755
--- a/third_party/blink/web_tests/external/wpt/mathml/tools/mathvariant-transforms.py
+++ b/third_party/blink/web_tests/external/wpt/mathml/tools/mathvariant-transforms.py
@@ -81,7 +81,9 @@
     reftest.write(source % mathvariant)
     reftestReference.write(source % ("%s (reference)" % mathvariant))
     source ='\
-<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#cssproperties"/>\n\
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#css-styling">\n\
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#the-mathvariant-attribute">\n\
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#new-text-transform-values">\n\
 <link rel="match" href="mathvariant-%s-ref.html"/>\n\
 <meta name="assert" content="Verify that a single-char <mtext> with a %s mathvariant is equivalent to an <mtext> with the transformed unicode character.">\n'
     reftest.write(source % (mathvariant, mathvariant))
diff --git a/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_support.js b/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_support.js
index 9a491dd6..ae9b55c 100644
--- a/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_support.js
+++ b/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_support.js
@@ -325,3 +325,31 @@
                    .pointerUp()
                    .send();
 }
+
+function pointerHoverInTarget(pointerType, target, direction) {
+    var x_delta = 0;
+    var y_delta = 0;
+    if (direction == "down") {
+        x_delta = 0;
+        y_delta = 10;
+    } else if (direction == "up") {
+        x_delta = 0;
+        y_delta = -10;
+    } else if (direction == "right") {
+        x_delta = 10;
+        y_delta = 0;
+    } else if (direction == "left") {
+        x_delta = -10;
+        y_delta = 0;
+    } else {
+        throw("drag direction '" + direction + "' is not expected, direction should be 'down', 'up', 'left' or 'right'");
+    }
+    var pointerId = pointerType + "Pointer1";
+    return new test_driver.Actions()
+                   .addPointer(pointerId, pointerType)
+                   .pointerMove(0, 0, {origin: target})
+                   .pointerMove(x_delta, y_delta, {origin: target})
+                   .pointerMove(2 * x_delta, 2 * y_delta, {origin: target})
+                   .pointerMove(3 * x_delta, 3 * y_delta, {origin: target})
+                   .send();
+}
diff --git a/third_party/blink/web_tests/external/wpt/pointerevents/pointerlock/pointerevent_movementxy_when_locked.html b/third_party/blink/web_tests/external/wpt/pointerevents/pointerlock/pointerevent_movementxy_when_locked.html
deleted file mode 100644
index bdad97df..0000000
--- a/third_party/blink/web_tests/external/wpt/pointerevents/pointerlock/pointerevent_movementxy_when_locked.html
+++ /dev/null
@@ -1,89 +0,0 @@
-<!doctype html>
-<html>
-    <head>
-        <title>Pointer Events pointer lock tests</title>
-        <meta name="viewport" content="width=device-width">
-        <link rel="stylesheet" type="text/css" href="/external/wpt/pointerevents/pointerevent_styles.css">
-        <script src="/resources/testharness.js"></script>
-        <script src="/resources/testharnessreport.js"></script>
-        <script src="/resources/testdriver.js"></script>
-        <script src="/resources/testdriver-actions.js"></script>
-        <script src="/resources/testdriver-vendor.js"></script>
-        <script type="text/javascript" src="../pointerevent_support.js"></script>
-        <style>
-          #testContainer {
-            touch-action: none;
-            user-select: none;
-            position: relative;
-          }
-        </style>
-        <script>
-            var lock_change_count = 0;
-            var mouseeventMovements = []
-            var pointereventMovements = []
-
-            function resetTestState() {
-            }
-
-            function run() {
-                var test_pointerEvent = setup_pointerevent_test("pointerevent movementX/Y when lock test", ['mouse']);
-                var div1 = document.getElementById("target");
-
-                on_event(div1, 'pointerdown', function(event) {
-                    if (lock_change_count == 0)
-                       div1.requestPointerLock();
-                });
-                on_event(div1, 'pointerup', function(event) {
-                    if (lock_change_count == 1)
-                        document.exitPointerLock();
-                });
-                on_event(div1, 'pointermove', function(event) {
-                    if (lock_change_count == 1) {
-                        pointereventMovements.push(`${event.movementX}, ${event.movementY}`);
-                    }
-                });
-                on_event(div1, 'mousemove', function(event) {
-                    if (lock_change_count == 1) {
-                        mouseeventMovements.push(`${event.movementX}, ${event.movementY}`);
-                    }
-                });
-                on_event(document, 'pointerlockchange', function(event) {
-                    lock_change_count++;
-                    if (lock_change_count == 1) {
-                        test_pointerEvent.step(function() {
-                            assert_equals(document.pointerLockElement, div1, "document.pointerLockElement should be div1.");
-                        });
-                    } else if (lock_change_count == 2) {
-                        test_pointerEvent.step(function() {
-                            assert_equals(document.pointerLockElement, null, "document.pointerLockElement should be null.");
-                            assert_not_equals(mouseeventMovements.length, 0);
-                            assert_array_equals(pointereventMovements, mouseeventMovements, "pointermove should have movementX/Y same as mousemove");
-                        });
-                        test_pointerEvent.done();
-                    }
-                });
-
-                // Inject mouse inputs.
-                pointerDragInTarget('mouse', target, 'right');
-            }
-        </script>
-    </head>
-    <body onload="run()">
-        <h1>Pointer Events movement in locked state test</h1>
-        <h2 id="pointerTypeDescription"></h2>
-        <h4>
-            Test Description: This test checks if pointermove.movementX/Y matches mousemove.movementX/Y when pointer is locked.
-            <ol>
-                 <li>Press left button down on the green rectangle and hold it.</li>
-                 <li>Move the mouse inside the green rectangle.</li>
-            </ol>
-            </ol>
-
-            Test passes if the proper behavior of the events is observed.
-        </h4>
-        <div id="testContainer">
-            <div id="target" style="width:800px;height:250px;background:green"></div>
-        </div>
-        <div class="spacer"></div>
-    </body>
-</html>
diff --git a/third_party/blink/web_tests/external/wpt/pointerevents/pointerlock/pointerevent_movementxy_with_pointerlock.html b/third_party/blink/web_tests/external/wpt/pointerevents/pointerlock/pointerevent_movementxy_with_pointerlock.html
new file mode 100644
index 0000000..376d0e6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/pointerevents/pointerlock/pointerevent_movementxy_with_pointerlock.html
@@ -0,0 +1,127 @@
+<!doctype html>
+<html>
+    <head>
+        <title>Pointer Events pointer lock tests</title>
+        <meta name="viewport" content="width=device-width">
+        <link rel="stylesheet" type="text/css" href="/external/wpt/pointerevents/pointerevent_styles.css">
+        <script src="/resources/testharness.js"></script>
+        <script src="/resources/testharnessreport.js"></script>
+        <script src="/resources/testdriver.js"></script>
+        <script src="/resources/testdriver-actions.js"></script>
+        <script src="/resources/testdriver-vendor.js"></script>
+        <script type="text/javascript" src="../pointerevent_support.js"></script>
+        <style>
+          #testContainer {
+            touch-action: none;
+            user-select: none;
+            position: relative;
+          }
+        </style>
+        <script>
+           PhaseEnum = {
+              BeforeLocked:     0,
+              PointerLocked:    1,
+              PointerUnlocked:  2,
+              Done:             3,
+            };
+            var phase = PhaseEnum.BeforeLocked;
+            var last_event;
+            var mouseeventMovements = []
+            var pointereventMovements = []
+
+            function resetTestState() {
+            }
+
+            function run() {
+                var test_pointerEvent = setup_pointerevent_test("pointerevent movementX/Y with pointerlock test", ['mouse']);
+
+                function testPointerMoves(event) {
+                    if (last_event) {
+                        if (phase == PhaseEnum.PointerLocked){
+                            test_pointerEvent.step(function() {
+                                assert_equals(event.screenX, last_event.screenX);
+                                assert_equals(event.screenY, last_event.screenY);
+                            });
+                        } else { // BeforeLocked || Unlocked
+                            test_pointerEvent.step(function() {
+                                assert_equals(event.screenX - last_event.screenX, event.movementX);
+                                assert_equals(event.screenY - last_event.screenY, event.movementY);
+                            });
+                        }
+                    }
+                    last_event = event;
+                }
+
+                on_event(target, 'click', function(event) {
+                    if (phase == PhaseEnum.BeforeLocked)
+                       target.requestPointerLock();
+                    else if (phase == PhaseEnum.PointerLocked)
+                       document.exitPointerLock();
+                    else if (phase == PhaseEnum.PointerUnlocked)
+                        test_pointerEvent.done();
+                });
+                on_event(target, 'pointermove', function(event) {
+                    if (phase == PhaseEnum.PointerLocked) {
+                        pointereventMovements.push(`${event.movementX}, ${event.movementY}`);
+                    }
+                    testPointerMoves(event);
+                });
+                on_event(target, 'mousemove', function(event) {
+                    if (phase == PhaseEnum.PointerLocked) {
+                        mouseeventMovements.push(`${event.movementX}, ${event.movementY}`);
+                    }
+                });
+                on_event(document, 'pointerlockchange', function(event) {
+                    phase++;
+                    if (phase == PhaseEnum.PointerLocked) {
+                        test_pointerEvent.step(function() {
+                            assert_equals(document.pointerLockElement, target, "document.pointerLockElement should be target.");
+                        });
+                    } else if (phase == PhaseEnum.PointerUnlocked) {
+                        test_pointerEvent.step(function() {
+                            assert_equals(document.pointerLockElement, null, "document.pointerLockElement should be null.");
+                            assert_not_equals(mouseeventMovements.length, 0);
+                            assert_array_equals(pointereventMovements, mouseeventMovements, "pointermove should have movementX/Y same as mousemove");
+                        });
+                    }
+                });
+
+                // Inject mouse inputs.
+                pointerHoverInTarget('mouse', target, 'right').then(function() {
+                    return clickInTarget("mouse", target);
+                }).then(function() {
+                    return pointerHoverInTarget('mouse', target, 'right');
+                }).then(function() {
+                    return clickInTarget("mouse", target);
+                }).then(function() {
+                    return pointerHoverInTarget('mouse', target, 'right');
+                }).then(function() {
+                    return clickInTarget("mouse", target);
+                });
+            }
+        </script>
+    </head>
+    <body onload="run()">
+        <h1>Pointer Events movement with pointerlock test</h1>
+        <h2 id="pointerTypeDescription"></h2>
+        <h4>
+            Test Description: This test checks pointerevent movementX/Y value with pointerlock.
+            It checks whether movement X/Y matches event.screenX/Y - last_event.screenX/Y when pointer is not locked;
+            And if pointermove.movementX/Y matches mousemove.movementX/Y when pointer is locked.
+            <ol>
+                 <li>Move the mouse inside the green rectangle.</li>
+                 <li>Click left button on the green rectangle.(Enter pointerlock)</li>
+                 <li>Move the mouse around.</li>
+                 <li>Click left button again</li>
+                 <li>Move the mouse inside the green rectangle.</li>
+                 <li>Click left button again to end the test.</li>
+            </ol>
+
+            Test passes if the proper behavior of the events is observed.
+        </h4>
+        <div id="testContainer">
+            <div id="target" style="width:800px;height:250px;background:green"></div>
+        </div>
+        <div class="spacer"></div>
+    </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/trusted-types/block-eval.tentative.html b/third_party/blink/web_tests/external/wpt/trusted-types/block-eval.tentative.html
new file mode 100644
index 0000000..e1a6a69
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/trusted-types/block-eval.tentative.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <script nonce="abc" src="/resources/testharness.js"></script>
+  <script nonce="abc" src="/resources/testharnessreport.js"></script>
+  <script nonce="abc" src="support/helper.sub.js"></script>
+
+  <!-- Note: Trusted Types enforcement, and a CSP that does not blanket-allow eval. -->
+  <meta http-equiv="Content-Security-Policy" content="script-src 'nonce-abc'; trusted-types *">
+</head>
+<body>
+<script nonce="abc">
+  let p = createScript_policy(window, 1);
+  test(t => {
+    assert_throws(new EvalError(), _ => {
+      eval('"hello there"') });
+  }, "eval with plain string throws.");
+
+  test(t => {
+    let s = eval(p.createScript('"Hello transformed string"'));
+    assert_equals(s, "Hello a cat string");
+  }, "eval with TrustedScript works.");
+
+  TrustedTypes.createPolicy("default", { createScript: createScriptJS }, true);
+  test(t => {
+    let s = eval('"Hello transformed untrusted string"');
+    assert_equals(s, "Hello a cat untrusted string");
+  }, "eval obeys default policy.");
+</script>
+
diff --git a/third_party/blink/web_tests/external/wpt/trusted-types/eval-with-permissive-csp.tentative.html b/third_party/blink/web_tests/external/wpt/trusted-types/eval-with-permissive-csp.tentative.html
new file mode 100644
index 0000000..68d119a5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/trusted-types/eval-with-permissive-csp.tentative.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <script nonce="abc" src="/resources/testharness.js"></script>
+  <script nonce="abc" src="/resources/testharnessreport.js"></script>
+  <script nonce="abc" src="support/helper.sub.js"></script>
+
+  <!-- Note: Trusted Types enforcement, and a CSP that allows all eval. -->
+  <meta http-equiv="Content-Security-Policy"
+        content="script-src 'nonce-abc' 'unsafe-eval'; trusted-types *">
+</head>
+<body>
+<script nonce="abc">
+  let p = createScript_policy(window, 1);
+  test(t => {
+    let s = eval('"hello there"');
+    assert_equals(s, "hello there");
+  }, "eval with plain string with Trusted Types and permissive CSP works.");
+
+  test(t => {
+    let s = eval(p.createScript('"Hello transformed string"'));
+    assert_equals("" + s, "Hello a cat string");
+  }, "eval with TrustedScript and permissive CSP works.");
+
+  TrustedTypes.createPolicy("default", { createScript: createScriptJS }, true);
+  test(t => {
+    let s = eval('"Hello transformed untrusted string"');
+    assert_equals(s, "Hello a cat untrusted string");
+  }, "eval with default policy and permissive CSP still obeys default policy.");
+</script>
+
diff --git a/third_party/blink/web_tests/external/wpt/wake-lock/idlharness.https.any-expected.txt b/third_party/blink/web_tests/external/wpt/wake-lock/idlharness.https.any-expected.txt
deleted file mode 100644
index 4c1af702..0000000
--- a/third_party/blink/web_tests/external/wpt/wake-lock/idlharness.https.any-expected.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-This is a testharness.js-based test.
-PASS idl_test setup
-PASS WakeLock interface: existence and properties of interface object
-PASS WakeLock interface object length
-PASS WakeLock interface object name
-PASS WakeLock interface: existence and properties of interface prototype object
-PASS WakeLock interface: existence and properties of interface prototype object's "constructor" property
-PASS WakeLock interface: existence and properties of interface prototype object's @@unscopables property
-FAIL WakeLock interface: operation requestPermission(WakeLockType) assert_own_property: interface object missing static operation expected property "requestPermission" missing
-PASS WakeLock interface: operation request(WakeLockType, WakeLockRequestOptions)
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/fast/canvas/canvas-fillText-getImageData-expected.html b/third_party/blink/web_tests/fast/canvas/canvas-fillText-getImageData-expected.html
new file mode 100644
index 0000000..303420e
--- /dev/null
+++ b/third_party/blink/web_tests/fast/canvas/canvas-fillText-getImageData-expected.html
@@ -0,0 +1,14 @@
+<html>
+<body>
+<canvas id="c" width=50 height=20></canvas>
+<script>
+var c = document.getElementById('c');
+var ctx = c.getContext("2d", {alpha: false});
+ctx.fillStyle = "white";
+ctx.fillRect(0,0,500,500);
+ctx.font = " 20px sans-serif";
+ctx.fillStyle = "black";
+ctx.fillText("Test", 0, 16);
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/fast/canvas/canvas-fillText-getImageData.html b/third_party/blink/web_tests/fast/canvas/canvas-fillText-getImageData.html
new file mode 100644
index 0000000..1956329c
--- /dev/null
+++ b/third_party/blink/web_tests/fast/canvas/canvas-fillText-getImageData.html
@@ -0,0 +1,15 @@
+<html>
+<body>
+<canvas id="c" width=50 height=20></canvas>
+<script>
+var c = document.getElementById('c');
+var ctx = c.getContext("2d", {alpha: false});
+ctx.fillStyle = "white";
+ctx.fillRect(0,0,500,500);
+ctx.font = " 20px sans-serif";
+ctx.fillStyle = "black";
+ctx.fillText("Test", 0, 16);
+var imdData = ctx.getImageData(0,0,10,10).data;
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/fast/canvas/canvas-fillText-todataUrl-expected.html b/third_party/blink/web_tests/fast/canvas/canvas-fillText-todataUrl-expected.html
new file mode 100644
index 0000000..303420e
--- /dev/null
+++ b/third_party/blink/web_tests/fast/canvas/canvas-fillText-todataUrl-expected.html
@@ -0,0 +1,14 @@
+<html>
+<body>
+<canvas id="c" width=50 height=20></canvas>
+<script>
+var c = document.getElementById('c');
+var ctx = c.getContext("2d", {alpha: false});
+ctx.fillStyle = "white";
+ctx.fillRect(0,0,500,500);
+ctx.font = " 20px sans-serif";
+ctx.fillStyle = "black";
+ctx.fillText("Test", 0, 16);
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/fast/canvas/canvas-fillText-todataUrl.html b/third_party/blink/web_tests/fast/canvas/canvas-fillText-todataUrl.html
new file mode 100644
index 0000000..932acc4a
--- /dev/null
+++ b/third_party/blink/web_tests/fast/canvas/canvas-fillText-todataUrl.html
@@ -0,0 +1,15 @@
+<html>
+<body>
+<canvas id="c" width=50 height=20></canvas>
+<script>
+var c = document.getElementById('c');
+var ctx = c.getContext("2d", {alpha: false});
+ctx.fillStyle = "white";
+ctx.fillRect(0,0,500,500);
+ctx.font = " 20px sans-serif";
+ctx.fillStyle = "black";
+ctx.fillText("Test", 0, 16);
+c.toDataURL();
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/fast/css/invalidation/first-line-change-inline-color-expected.html b/third_party/blink/web_tests/fast/css/invalidation/first-line-change-inline-color-expected.html
new file mode 100644
index 0000000..09c518e
--- /dev/null
+++ b/third_party/blink/web_tests/fast/css/invalidation/first-line-change-inline-color-expected.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<!-- This expectation is wrong because of crbug.com/562418.
+     The inner span should have style="color: green". -->
+<p style="color: red">Red <span>This text should be green.</span> Red</p>
diff --git a/third_party/blink/web_tests/fast/css/invalidation/first-line-change-inline-color-inherited-expected.html b/third_party/blink/web_tests/fast/css/invalidation/first-line-change-inline-color-inherited-expected.html
new file mode 100644
index 0000000..09c518e
--- /dev/null
+++ b/third_party/blink/web_tests/fast/css/invalidation/first-line-change-inline-color-inherited-expected.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<!-- This expectation is wrong because of crbug.com/562418.
+     The inner span should have style="color: green". -->
+<p style="color: red">Red <span>This text should be green.</span> Red</p>
diff --git a/third_party/blink/web_tests/fast/css/invalidation/first-line-change-inline-color-inherited.html b/third_party/blink/web_tests/fast/css/invalidation/first-line-change-inline-color-inherited.html
new file mode 100644
index 0000000..e902e79
--- /dev/null
+++ b/third_party/blink/web_tests/fast/css/invalidation/first-line-change-inline-color-inherited.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<style>
+  #block { color: green; }
+  #block::first-line { color: red; }
+  .green { color: green; }
+</style>
+<div id="block">
+  <div>
+    <p>Red <span id="t"><span>This text should be green.<span></span> Red</p>
+  </div>
+</div>
+<script src="../../../resources/run-after-layout-and-paint.js"></script>
+<script>
+runAfterLayoutAndPaint(function() {
+  document.getElementById('t').className = 'green';
+}, true);
+</script>
diff --git a/third_party/blink/web_tests/fast/css/invalidation/first-line-change-inline-color.html b/third_party/blink/web_tests/fast/css/invalidation/first-line-change-inline-color.html
new file mode 100644
index 0000000..eeab74e0
--- /dev/null
+++ b/third_party/blink/web_tests/fast/css/invalidation/first-line-change-inline-color.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<style>
+  #block { color: green; }
+  #block::first-line { color: red; }
+  .green { color: green; }
+</style>
+<div id="block">
+  <div>
+    <p>Red <span id="t">This text should be green.</span> Red</p>
+  </div>
+</div>
+<script src="../../../resources/run-after-layout-and-paint.js"></script>
+<script>
+runAfterLayoutAndPaint(function() {
+  document.getElementById('t').className = 'green';
+}, true);
+</script>
diff --git a/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-properties-after-frame-navigated-expected.txt b/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-properties-after-frame-navigated-expected.txt
index 909ae63..4b8110b 100644
--- a/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-properties-after-frame-navigated-expected.txt
+++ b/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-properties-after-frame-navigated-expected.txt
@@ -62,8 +62,6 @@
 PASS window.cached_navigator_userActivation.hasBeenActive is false
 PASS window.cached_navigator_userActivation.isActive is false
 PASS window.cached_navigator_xr.ondevicechange is null
-PASS window.cached_performance.onelementtimingbufferfull is null
-PASS window.cached_performance.oneventtimingbufferfull is null
 PASS window.cached_performance.onresourcetimingbufferfull is null
 PASS window.cached_performance_navigation.redirectCount is 0
 PASS window.cached_performance_navigation.type is 0
diff --git a/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-properties-after-frame-removed-and-gced-expected.txt b/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-properties-after-frame-removed-and-gced-expected.txt
index a2c9564e..7d4a89b 100644
--- a/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-properties-after-frame-removed-and-gced-expected.txt
+++ b/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-properties-after-frame-removed-and-gced-expected.txt
@@ -62,8 +62,6 @@
 PASS window.cached_navigator_userActivation.hasBeenActive is false
 PASS window.cached_navigator_userActivation.isActive is false
 PASS window.cached_navigator_xr.ondevicechange is null
-PASS window.cached_performance.onelementtimingbufferfull is null
-PASS window.cached_performance.oneventtimingbufferfull is null
 PASS window.cached_performance.onresourcetimingbufferfull is null
 PASS window.cached_performance_navigation.redirectCount is 0
 PASS window.cached_performance_navigation.type is 0
diff --git a/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-properties-after-frame-removed-expected.txt b/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-properties-after-frame-removed-expected.txt
index 6791306..a34fdf28 100644
--- a/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-properties-after-frame-removed-expected.txt
+++ b/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-properties-after-frame-removed-expected.txt
@@ -62,8 +62,6 @@
 PASS window.cached_navigator_userActivation.hasBeenActive is false
 PASS window.cached_navigator_userActivation.isActive is false
 PASS window.cached_navigator_xr.ondevicechange is null
-PASS window.cached_performance.onelementtimingbufferfull is null
-PASS window.cached_performance.oneventtimingbufferfull is null
 PASS window.cached_performance.onresourcetimingbufferfull is null
 PASS window.cached_performance_navigation.redirectCount is 0
 PASS window.cached_performance_navigation.type is 0
diff --git a/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt b/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt
index 624fdfa..d076f51e 100644
--- a/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt
+++ b/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt
@@ -191,8 +191,6 @@
 PASS oldChildWindow.pageYOffset is newChildWindow.pageYOffset
 PASS oldChildWindow.performance.navigation.redirectCount is newChildWindow.performance.navigation.redirectCount
 PASS oldChildWindow.performance.navigation.type is newChildWindow.performance.navigation.type
-PASS oldChildWindow.performance.onelementtimingbufferfull is newChildWindow.performance.onelementtimingbufferfull
-PASS oldChildWindow.performance.oneventtimingbufferfull is newChildWindow.performance.oneventtimingbufferfull
 PASS oldChildWindow.performance.onresourcetimingbufferfull is newChildWindow.performance.onresourcetimingbufferfull
 PASS oldChildWindow.performance.timing.connectEnd is newChildWindow.performance.timing.connectEnd
 PASS oldChildWindow.performance.timing.connectStart is newChildWindow.performance.timing.connectStart
diff --git a/third_party/blink/web_tests/fast/dom/Window/window-properties-performance-expected.txt b/third_party/blink/web_tests/fast/dom/Window/window-properties-performance-expected.txt
index a378d728..3c9f66b 100644
--- a/third_party/blink/web_tests/fast/dom/Window/window-properties-performance-expected.txt
+++ b/third_party/blink/web_tests/fast/dom/Window/window-properties-performance-expected.txt
@@ -2,8 +2,6 @@
 
 window.performance [object Performance]
 window.performance.addEventListener [function]
-window.performance.clearElementTimings [function]
-window.performance.clearEventTimings [function]
 window.performance.clearMarks [function]
 window.performance.clearMeasures [function]
 window.performance.clearResourceTimings [function]
@@ -26,13 +24,9 @@
 window.performance.navigation.toJSON [function]
 window.performance.navigation.type [number]
 window.performance.now [function]
-window.performance.onelementtimingbufferfull [null]
-window.performance.oneventtimingbufferfull [null]
 window.performance.onresourcetimingbufferfull [null]
 window.performance.profile [function]
 window.performance.removeEventListener [function]
-window.performance.setElementTimingBufferMaxSize [function]
-window.performance.setEventTimingBufferMaxSize [function]
 window.performance.setResourceTimingBufferSize [function]
 window.performance.timeOrigin [number]
 window.performance.timing [object PerformanceTiming]
diff --git a/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_mouse_pointercapture_inactivate_pointer.html b/third_party/blink/web_tests/fast/events/pointerevents/pointerevent_mouse_pointercapture_inactivate_pointer.html
similarity index 79%
rename from third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_mouse_pointercapture_inactivate_pointer.html
rename to third_party/blink/web_tests/fast/events/pointerevents/pointerevent_mouse_pointercapture_inactivate_pointer.html
index 524d19e..b50c96d 100644
--- a/third_party/blink/web_tests/external/wpt/pointerevents/pointerevent_mouse_pointercapture_inactivate_pointer.html
+++ b/third_party/blink/web_tests/fast/events/pointerevents/pointerevent_mouse_pointercapture_inactivate_pointer.html
@@ -1,9 +1,9 @@
 <!doctype html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/resources/testdriver.js"></script>
-<script src="/resources/testdriver-vendor.js"></script>
-<script src="/resources/testdriver-actions.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/testdriver.js"></script>
+<script src="../../../resources/testdriver-vendor.js"></script>
+<script src="../../../external/wpt/resources/testdriver-actions.js"></script>
 <style>
     iframe {
       width: 300px;
diff --git a/third_party/blink/web_tests/external/wpt/pointerevents/resources/pointerevent_mouse_pointercapture_inactivate_pointer-iframe.html b/third_party/blink/web_tests/fast/events/pointerevents/resources/pointerevent_mouse_pointercapture_inactivate_pointer-iframe.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/pointerevents/resources/pointerevent_mouse_pointercapture_inactivate_pointer-iframe.html
rename to third_party/blink/web_tests/fast/events/pointerevents/resources/pointerevent_mouse_pointercapture_inactivate_pointer-iframe.html
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/absolute-inside-out-of-view-fixed-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/absolute-inside-out-of-view-fixed-expected.txt
index 13bb291..740f2691 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/absolute-inside-out-of-view-fixed-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/absolute-inside-out-of-view-fixed-expected.txt
@@ -9,7 +9,14 @@
     },
     {
       "name": "VerticalScrollbar",
-      "bounds": [800, 600]
+      "position": [785, 0],
+      "bounds": [15, 600]
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000"
     }
   ],
   "transforms": [
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/layer-creation/fixed-position-out-of-view-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/layer-creation/fixed-position-out-of-view-expected.txt
new file mode 100644
index 0000000..167a2155
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/layer-creation/fixed-position-out-of-view-expected.txt
@@ -0,0 +1,72 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [785, 1021],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "VerticalScrollbar",
+      "position": [785, 0],
+      "bounds": [15, 600]
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
+      "bounds": [10, 10],
+      "contentsOpaque": true,
+      "backgroundColor": "#C0C0C0",
+      "transform": 1
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
+      "bounds": [10, 10],
+      "contentsOpaque": true,
+      "backgroundColor": "#C0C0C0",
+      "transform": 2
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
+      "bounds": [10, 10],
+      "contentsOpaque": true,
+      "backgroundColor": "#C0C0C0",
+      "transform": 3
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
+      "bounds": [10, 10],
+      "contentsOpaque": true,
+      "backgroundColor": "#C0C0C0"
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 1013, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, -100, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [1000, 0, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/layer-creation/fixed-position-out-of-view-scaled-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/layer-creation/fixed-position-out-of-view-scaled-expected.txt
new file mode 100644
index 0000000..1e83a3d
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/layer-creation/fixed-position-out-of-view-scaled-expected.txt
@@ -0,0 +1,303 @@
+Not scaled:
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [4008, 4016],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "transform": 1
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
+      "bounds": [10, 10],
+      "contentsOpaque": true,
+      "backgroundColor": "#C0C0C0",
+      "transform": 2
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
+      "bounds": [10, 10],
+      "contentsOpaque": true,
+      "backgroundColor": "#C0C0C0",
+      "transform": 3
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
+      "bounds": [10, 10],
+      "contentsOpaque": true,
+      "backgroundColor": "#C0C0C0",
+      "transform": 4
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
+      "bounds": [10, 10],
+      "contentsOpaque": true,
+      "backgroundColor": "#C0C0C0",
+      "transform": 5
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
+      "bounds": [10, 10],
+      "contentsOpaque": true,
+      "backgroundColor": "#C0C0C0",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [0.5, 0, 0, 0],
+        [0, 0.5, 0, 0],
+        [0, 0, 1, 0],
+        [0, 0, 0, 1]
+      ],
+      "origin": [0, 0],
+      "flattenInheritedTransform": false
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, -100, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 1008, 0, 1]
+      ]
+    },
+    {
+      "id": 4,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [1000, 0, 0, 1]
+      ]
+    },
+    {
+      "id": 5,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [600, 0, 0, 1]
+      ]
+    }
+  ]
+}
+
+Scale=0.5:
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [4008, 4016],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "transform": 1
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
+      "bounds": [10, 10],
+      "contentsOpaque": true,
+      "backgroundColor": "#C0C0C0",
+      "transform": 2
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
+      "bounds": [10, 10],
+      "contentsOpaque": true,
+      "backgroundColor": "#C0C0C0",
+      "transform": 3
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
+      "bounds": [10, 10],
+      "contentsOpaque": true,
+      "backgroundColor": "#C0C0C0",
+      "transform": 4
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
+      "bounds": [10, 10],
+      "contentsOpaque": true,
+      "backgroundColor": "#C0C0C0",
+      "transform": 5
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
+      "bounds": [10, 10],
+      "contentsOpaque": true,
+      "backgroundColor": "#C0C0C0",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [0.5, 0, 0, 0],
+        [0, 0.5, 0, 0],
+        [0, 0, 1, 0],
+        [0, 0, 0, 1]
+      ],
+      "origin": [0, 0],
+      "flattenInheritedTransform": false
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, -100, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 1008, 0, 1]
+      ]
+    },
+    {
+      "id": 4,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [1000, 0, 0, 1]
+      ]
+    },
+    {
+      "id": 5,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [600, 0, 0, 1]
+      ]
+    }
+  ]
+}
+
+Scale=1.5:
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [4008, 4016],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "transform": 1
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
+      "bounds": [10, 10],
+      "contentsOpaque": true,
+      "backgroundColor": "#C0C0C0",
+      "transform": 2
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
+      "bounds": [10, 10],
+      "contentsOpaque": true,
+      "backgroundColor": "#C0C0C0",
+      "transform": 3
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
+      "bounds": [10, 10],
+      "contentsOpaque": true,
+      "backgroundColor": "#C0C0C0",
+      "transform": 4
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
+      "bounds": [10, 10],
+      "contentsOpaque": true,
+      "backgroundColor": "#C0C0C0",
+      "transform": 5
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
+      "bounds": [10, 10],
+      "contentsOpaque": true,
+      "backgroundColor": "#C0C0C0",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1.5, 0, 0, 0],
+        [0, 1.5, 0, 0],
+        [0, 0, 1, 0],
+        [0, 0, 0, 1]
+      ],
+      "origin": [0, 0],
+      "flattenInheritedTransform": false
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, -100, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 1008, 0, 1]
+      ]
+    },
+    {
+      "id": 4,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [1000, 0, 0, 1]
+      ]
+    },
+    {
+      "id": 5,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [600, 0, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/layer-creation/fixed-position-out-of-view-scaled-scroll-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/layer-creation/fixed-position-out-of-view-scaled-scroll-expected.txt
new file mode 100644
index 0000000..416cf3f
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/layer-creation/fixed-position-out-of-view-scaled-scroll-expected.txt
@@ -0,0 +1,333 @@
+Not scaled:
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [4008, 4016],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "transform": 2
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
+      "bounds": [10, 10],
+      "contentsOpaque": true,
+      "backgroundColor": "#C0C0C0",
+      "transform": 3
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
+      "bounds": [10, 10],
+      "contentsOpaque": true,
+      "backgroundColor": "#C0C0C0",
+      "transform": 4
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
+      "bounds": [10, 10],
+      "contentsOpaque": true,
+      "backgroundColor": "#C0C0C0",
+      "transform": 5
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
+      "bounds": [10, 10],
+      "contentsOpaque": true,
+      "backgroundColor": "#C0C0C0",
+      "transform": 6
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
+      "bounds": [10, 10],
+      "contentsOpaque": true,
+      "backgroundColor": "#C0C0C0",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [0.5, 0, 0, 0],
+        [0, 0.5, 0, 0],
+        [0, 0, 1, 0],
+        [0, 0, 0, 1]
+      ],
+      "origin": [0, 0],
+      "flattenInheritedTransform": false
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [-100, -100, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, -100, 0, 1]
+      ]
+    },
+    {
+      "id": 4,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 1008, 0, 1]
+      ]
+    },
+    {
+      "id": 5,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [1000, 0, 0, 1]
+      ]
+    },
+    {
+      "id": 6,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [600, 0, 0, 1]
+      ]
+    }
+  ]
+}
+
+Scale=0.5:
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [4008, 4016],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "transform": 2
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
+      "bounds": [10, 10],
+      "contentsOpaque": true,
+      "backgroundColor": "#C0C0C0",
+      "transform": 3
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
+      "bounds": [10, 10],
+      "contentsOpaque": true,
+      "backgroundColor": "#C0C0C0",
+      "transform": 4
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
+      "bounds": [10, 10],
+      "contentsOpaque": true,
+      "backgroundColor": "#C0C0C0",
+      "transform": 5
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
+      "bounds": [10, 10],
+      "contentsOpaque": true,
+      "backgroundColor": "#C0C0C0",
+      "transform": 6
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
+      "bounds": [10, 10],
+      "contentsOpaque": true,
+      "backgroundColor": "#C0C0C0",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [0.5, 0, 0, 0],
+        [0, 0.5, 0, 0],
+        [0, 0, 1, 0],
+        [0, 0, 0, 1]
+      ],
+      "origin": [0, 0],
+      "flattenInheritedTransform": false
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [-100, -100, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, -100, 0, 1]
+      ]
+    },
+    {
+      "id": 4,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 1008, 0, 1]
+      ]
+    },
+    {
+      "id": 5,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [1000, 0, 0, 1]
+      ]
+    },
+    {
+      "id": 6,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [600, 0, 0, 1]
+      ]
+    }
+  ]
+}
+
+Scale=1.5:
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [4008, 4016],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "transform": 2
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
+      "bounds": [10, 10],
+      "contentsOpaque": true,
+      "backgroundColor": "#C0C0C0",
+      "transform": 3
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
+      "bounds": [10, 10],
+      "contentsOpaque": true,
+      "backgroundColor": "#C0C0C0",
+      "transform": 4
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
+      "bounds": [10, 10],
+      "contentsOpaque": true,
+      "backgroundColor": "#C0C0C0",
+      "transform": 5
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
+      "bounds": [10, 10],
+      "contentsOpaque": true,
+      "backgroundColor": "#C0C0C0",
+      "transform": 6
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
+      "bounds": [10, 10],
+      "contentsOpaque": true,
+      "backgroundColor": "#C0C0C0",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1.5, 0, 0, 0],
+        [0, 1.5, 0, 0],
+        [0, 0, 1, 0],
+        [0, 0, 0, 1]
+      ],
+      "origin": [0, 0],
+      "flattenInheritedTransform": false
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [-100, -100, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, -100, 0, 1]
+      ]
+    },
+    {
+      "id": 4,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 1008, 0, 1]
+      ]
+    },
+    {
+      "id": 5,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [1000, 0, 0, 1]
+      ]
+    },
+    {
+      "id": 6,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [600, 0, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/layer-creation/fixed-position-under-transform-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/layer-creation/fixed-position-under-transform-expected.txt
new file mode 100644
index 0000000..0a67a40
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/layer-creation/fixed-position-under-transform-expected.txt
@@ -0,0 +1,62 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [785, 5021],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "transform": 1
+    },
+    {
+      "name": "VerticalScrollbar",
+      "position": [785, 0],
+      "bounds": [15, 600]
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV id='indicator'",
+      "position": [100, 100],
+      "bounds": [256, 256],
+      "contentsOpaque": true,
+      "backgroundColor": "#FF0000",
+      "transform": 3
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV id='overlap'",
+      "bounds": [500, 500],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000"
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [0, -1000, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 13, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "parent": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [0, 1000, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/rtl/rtl-iframe-fixed-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/rtl/rtl-iframe-fixed-expected.txt
new file mode 100644
index 0000000..0d4c363
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/rtl/rtl-iframe-fixed-expected.txt
@@ -0,0 +1,29 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='positioned layer'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [50, 50, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/squashing/no-squashing-into-fixed-position-that-clips-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/squashing/no-squashing-into-fixed-position-that-clips-expected.txt
new file mode 100644
index 0000000..97a15e1
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/squashing/no-squashing-into-fixed-position-that-clips-expected.txt
@@ -0,0 +1,35 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='compositedlayer'",
+      "bounds": [24, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#D3D3D3",
+      "transform": 1
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV class='notsquashedelement'",
+      "bounds": [800, 60],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000"
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [400, 40, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/squashing/squash-above-fixed-2-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/squashing/squash-above-fixed-2-expected.txt
new file mode 100644
index 0000000..f701bb3
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/squashing/squash-above-fixed-2-expected.txt
@@ -0,0 +1,77 @@
+This scenario verifies that the cyan "container" element scrolls properly with squashing enabled. The "container" element should not squash into a composited layer mapping owned by the fixed position layer or its descendant, since this would make it behave like a fixed position element during composited scrolling.
+
+CASE 1, original layer tree:
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [785, 4050],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "VerticalScrollbar",
+      "position": [785, 0],
+      "bounds": [15, 600]
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV id='fixed'",
+      "bounds": [400, 200],
+      "contentsOpaque": true,
+      "backfaceVisibility": "hidden",
+      "backgroundColor": "#0000FF"
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV id='container'",
+      "position": [100, 50],
+      "bounds": [200, 4000],
+      "contentsOpaque": true,
+      "backgroundColor": "#00FFFF"
+    }
+  ]
+}
+
+CASE 2, scrolling y to 80, the "container" element should remain positioned with respect to the scrolled document, the fixed-pos layer compensates for the new scroll position:
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [785, 4050],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "transform": 1
+    },
+    {
+      "name": "VerticalScrollbar",
+      "position": [785, 0],
+      "bounds": [15, 600]
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV id='fixed'",
+      "bounds": [400, 200],
+      "contentsOpaque": true,
+      "backfaceVisibility": "hidden",
+      "backgroundColor": "#0000FF"
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV id='container'",
+      "position": [100, 50],
+      "bounds": [200, 4000],
+      "contentsOpaque": true,
+      "backgroundColor": "#00FFFF",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [0, -80, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/squashing/squash-paint-invalidation-fixed-position-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/squashing/squash-paint-invalidation-fixed-position-expected.txt
new file mode 100644
index 0000000..565ddf0
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/squashing/squash-paint-invalidation-fixed-position-expected.txt
@@ -0,0 +1,83 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV",
+      "bounds": [100, 5000],
+      "contentsOpaque": true,
+      "backgroundColor": "#ADD8E6",
+      "transform": 1
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV",
+      "position": [8, 25],
+      "bounds": [100, 125],
+      "backgroundColor": "#D3D3D3",
+      "paintInvalidations": [
+        {
+          "object": "LayoutNGBlockFlow (positioned) DIV id='foo'",
+          "rect": [0, 25, 100, 100],
+          "reason": "background"
+        }
+      ]
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
+    }
+  ]
+}
+ {
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV",
+      "bounds": [100, 5000],
+      "contentsOpaque": true,
+      "backgroundColor": "#ADD8E6",
+      "transform": 1
+    },
+    {
+      "name": "LayoutNGBlockFlow (positioned) DIV",
+      "position": [8, 25],
+      "bounds": [100, 125],
+      "backgroundColor": "#D3D3D3",
+      "paintInvalidations": [
+        {
+          "object": "LayoutNGBlockFlow (positioned) DIV id='foo'",
+          "rect": [0, 25, 100, 100],
+          "reason": "background"
+        }
+      ]
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/position/absolute-position-changed-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/position/absolute-position-changed-expected.txt
index 774089c..27c1b68 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/position/absolute-position-changed-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/position/absolute-position-changed-expected.txt
@@ -7,7 +7,7 @@
       "backgroundColor": "#FFFFFF",
       "paintInvalidations": [
         {
-          "object": "LayoutBlockFlow (positioned) DIV id='absoluteDiv' class='absolute green'",
+          "object": "LayoutNGBlockFlow (positioned) DIV id='absoluteDiv' class='absolute green'",
           "rect": [100, 500, 100, 100],
           "reason": "chunk disappeared"
         }
@@ -15,20 +15,20 @@
       "transform": 1
     },
     {
-      "name": "LayoutBlockFlow (positioned) DIV class='fixed red'",
-      "position": [100, 200],
+      "name": "LayoutNGBlockFlow (positioned) DIV class='fixed red'",
       "bounds": [100, 100],
-      "backgroundColor": "#FF0000"
+      "backgroundColor": "#FF0000",
+      "transform": 2
     },
     {
-      "name": "LayoutBlockFlow (positioned) DIV id='absoluteDiv' class='absolute green'",
+      "name": "LayoutNGBlockFlow (positioned) DIV id='absoluteDiv' class='absolute green'",
       "position": [100, 700],
       "bounds": [100, 100],
       "contentsOpaque": true,
       "backgroundColor": "#008000",
       "paintInvalidations": [
         {
-          "object": "LayoutBlockFlow (positioned) DIV id='absoluteDiv' class='absolute green'",
+          "object": "LayoutNGBlockFlow (positioned) DIV id='absoluteDiv' class='absolute green'",
           "rect": [0, 0, 100, 100],
           "reason": "full layer"
         }
@@ -45,6 +45,15 @@
         [0, 0, 1, 0],
         [0, -500, 0, 1]
       ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 200, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/http/tests/devtools/console/viewport-testing/console-key-navigation-expected.txt b/third_party/blink/web_tests/http/tests/devtools/console/viewport-testing/console-key-navigation-expected.txt
index 416d87ff..97e697c 100644
--- a/third_party/blink/web_tests/http/tests/devtools/console/viewport-testing/console-key-navigation-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/console/viewport-testing/console-key-navigation-expected.txt
@@ -4,7 +4,7 @@
 
 Running: testBetweenViewportAndExternal
 Setting focus in prompt:
-TEXTAREA:Code editor
+TEXTAREA:Console prompt
 
 Shift+Tab:
 DIV:console-key-navigation.js:20 Message #99
@@ -16,11 +16,11 @@
 DIV:console-key-navigation.js:20 Message #99
 
 Tab:
-TEXTAREA:Code editor
+TEXTAREA:Console prompt
 
 Running: testBetweenViewportAndExternalWithSelectedItemNotInDOM
 Setting focus in prompt:
-TEXTAREA:Code editor
+TEXTAREA:Console prompt
 
 Shift+Tab:
 DIV:console-key-navigation.js:20 Message #99
@@ -35,7 +35,7 @@
 DIV:console-key-navigation.js:20 Message #99
 
 Setting focus in prompt:
-TEXTAREA:Code editor
+TEXTAREA:Console prompt
 
 Shift+Tab:
 DIV:console-key-navigation.js:20 Message #99
@@ -44,7 +44,7 @@
 DIV.console-group.console-group-messages
 
 Tab:
-TEXTAREA:Code editor
+TEXTAREA:Console prompt
 
 Running: testMoveAcrossLogsWithinViewport
 
@@ -79,22 +79,22 @@
 Force selecting index 99
 DIV:console-key-navigation.js:20 Message #99
 Setting focus in prompt:
-TEXTAREA:Code editor
+TEXTAREA:Console prompt
 
 Scrolling to top of viewport
-TEXTAREA:Code editor
+TEXTAREA:Console prompt
 
 Scrolling to bottom of viewport
-TEXTAREA:Code editor
+TEXTAREA:Console prompt
 
 Running: testNewLogsShouldNotMoveFocus
 Setting focus in prompt:
-TEXTAREA:Code editor
+TEXTAREA:Console prompt
 Message count: 101
-TEXTAREA:Code editor
+TEXTAREA:Console prompt
 
 Running: testClearingConsoleFocusesPrompt
 
 Console cleared:
-TEXTAREA:Code editor
+TEXTAREA:Console prompt
 
diff --git a/third_party/blink/web_tests/http/tests/dom/scripted-task-queue-posttask.html b/third_party/blink/web_tests/http/tests/dom/scripted-task-queue-posttask.html
deleted file mode 100644
index 6d9e988..0000000
--- a/third_party/blink/web_tests/http/tests/dom/scripted-task-queue-posttask.html
+++ /dev/null
@@ -1,29 +0,0 @@
-<!DOCTYPE html>
-
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-
-<script>
-
-let resolved_promises = 0;
-
-function after_task() {
-  resolved_promises++;
-
-  if (resolved_promises == 10) {
-    done();
-  }
-}
-
-function queue_tasks(task_type) {
-  let task_queue = TaskQueue.default(task_type);
-  for (var i = 1; i < 11; i++) {
-    task_queue.postTask(function() {
-
-    }).then(after_task);
-  }
-}
-
-queue_tasks('user-interaction');
-
-</script>
diff --git a/third_party/blink/web_tests/http/tests/origin_trials/resources/origintrials.js b/third_party/blink/web_tests/http/tests/origin_trials/resources/origintrials.js
index 89f216a..2c2c8bf 100644
--- a/third_party/blink/web_tests/http/tests/origin_trials/resources/origintrials.js
+++ b/third_party/blink/web_tests/http/tests/origin_trials/resources/origintrials.js
@@ -1,5 +1,5 @@
 // The sample API integrates origin trial checks at various entry points.
-// References to "partial interface" mean that the [OriginTrialEnabled]
+// References to "partial interface" mean that the [RuntimeEnabled]
 // IDL attribute is applied to an entire partial interface, instead of
 // applied to individual IDL members.
 
@@ -202,7 +202,7 @@
 };
 
 // These tests should pass, regardless of the state of the trial. These are
-// control tests for IDL members without the [OriginTrialEnabled] extended
+// control tests for IDL members without the [RuntimeEnabled] extended
 // attribute. The control tests will vary for secure vs insecure context.
 expect_always_bindings = (insecure_context, opt_description_suffix) => {
   var description_suffix = opt_description_suffix || '';
@@ -298,7 +298,7 @@
 
   if (insecure_context) {
     // Origin trials only work in secure contexts, so tests cannot distinguish
-    // between [OriginTrialEnabled] or [SecureContext] preventing exposure of
+    // between [RuntimeEnabled] or [SecureContext] preventing exposure of
     // IDL members. These tests at least ensure IDL members are not exposed in
     // insecure contexts, regardless of reason.
     test(() => {
@@ -361,7 +361,7 @@
       expect_input_dictionary_member('normalBool');
     }, 'Method with input dictionary should access member value');
 
-  // Tests for [OriginTrialEnabled] on partial interfaces
+  // Tests for [RuntimeEnabled] on partial interfaces
   test(() => {
       expect_member('normalAttributePartial', (testObject) => {
           return testObject.normalAttributePartial;
@@ -392,7 +392,7 @@
         });
     }, 'Constant should exist on partial interface and return value');
 
-  // Tests for combination of [OriginTrialEnabled] and [SecureContext]
+  // Tests for combination of [RuntimeEnabled] and [SecureContext]
   test(() => {
       expect_member('secureAttribute', (testObject) => {
           return testObject.secureAttribute;
@@ -470,16 +470,16 @@
     }, 'Method with input dictionary should not access member value, with trial disabled');
 
 
-  // Tests for combination of [OriginTrialEnabled] and [SecureContext]
+  // Tests for combination of [RuntimeEnabled] and [SecureContext]
   if (insecure_context) {
     // Origin trials only work in secure contexts, so tests cannot distinguish
-    // between [OriginTrialEnabled] or [SecureContext] preventing exposure of
+    // between [RuntimeEnabled] or [SecureContext] preventing exposure of
     // IDL members. There are tests to ensure IDL members are not exposed in
     // insecure contexts in expect_success_bindings().
     return;
   }
 
-  // Tests for [OriginTrialEnabled] on partial interfaces
+  // Tests for [RuntimeEnabled] on partial interfaces
   test(() => {
       expect_member_fails('normalAttributePartial');
     }, 'Attribute should not exist on partial interface, with trial disabled');
@@ -496,7 +496,7 @@
       expect_static_member_fails('CONSTANT_PARTIAL');
     }, 'Constant should not exist on partial interface, with trial disabled');
 
-  // Tests for combination of [OriginTrialEnabled] and [SecureContext]
+  // Tests for combination of [RuntimeEnabled] and [SecureContext]
   test(() => {
       expect_member_fails('secureAttribute');
     }, 'Secure attribute should not exist, with trial disabled');
diff --git a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/eventtiming-origin-trial-interfaces.html b/third_party/blink/web_tests/http/tests/origin_trials/webexposed/eventtiming-origin-trial-interfaces.html
index 362286e7..0522c36c 100644
--- a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/eventtiming-origin-trial-interfaces.html
+++ b/third_party/blink/web_tests/http/tests/origin_trials/webexposed/eventtiming-origin-trial-interfaces.html
@@ -12,7 +12,6 @@
 test(t => {
   OriginTrialsHelper.check_properties_exist(this,
       {'PerformanceEventTiming': ['processingStart', 'cancelable'],
-       'Performance': ['clearEventTimings', 'setEventTimingBufferMaxSize', 'oneventtimingbufferfull'],
        });
 }, 'EventTiming API interfaces and properties in Origin-Trial enabled document.');
 </script>
diff --git a/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt b/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
index 989716d..c889d55 100644
--- a/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
+++ b/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
@@ -1004,12 +1004,8 @@
     method respondWith
 interface Performance : EventTarget
     attribute @@toStringTag
-    getter onelementtimingbufferfull
-    getter oneventtimingbufferfull
     getter onresourcetimingbufferfull
     getter timeOrigin
-    method clearElementTimings
-    method clearEventTimings
     method clearMarks
     method clearMeasures
     method clearResourceTimings
@@ -1021,12 +1017,8 @@
     method measure
     method now
     method profile
-    method setElementTimingBufferMaxSize
-    method setEventTimingBufferMaxSize
     method setResourceTimingBufferSize
     method toJSON
-    setter onelementtimingbufferfull
-    setter oneventtimingbufferfull
     setter onresourcetimingbufferfull
 interface PerformanceEntry
     attribute @@toStringTag
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/css3/filters/backdrop-filter-browser-zoom-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/css3/filters/backdrop-filter-browser-zoom-expected.png
new file mode 100644
index 0000000..d57edd36
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/css3/filters/backdrop-filter-browser-zoom-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png
deleted file mode 100644
index 3b4a8ecb..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png
deleted file mode 100644
index 3b4a8ecb..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png
deleted file mode 100644
index 3b4a8ecb..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png
deleted file mode 100644
index 3b4a8ecb..0000000
--- a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-browser-zoom-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-browser-zoom-expected.png
index f0aa6cb..1b486cf 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-browser-zoom-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-browser-zoom-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-clip-radius-zoom-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-clip-radius-zoom-expected.png
new file mode 100644
index 0000000..1066ca6a
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-clip-radius-zoom-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png
index 3b4a8ecb..f10b613 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-transform-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-transform-expected.png
deleted file mode 100644
index 9d425786..0000000
--- a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-transform-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/backdrop-filter-browser-zoom-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/backdrop-filter-browser-zoom-expected.png
new file mode 100644
index 0000000..e888571d
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/backdrop-filter-browser-zoom-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/backdrop-filter-clip-radius-zoom-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/backdrop-filter-clip-radius-zoom-expected.png
new file mode 100644
index 0000000..a892c65
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/backdrop-filter-clip-radius-zoom-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png
new file mode 100644
index 0000000..7a9d148
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/resources/gesture-util.js b/third_party/blink/web_tests/resources/gesture-util.js
index 36863d0..f691934 100644
--- a/third_party/blink/web_tests/resources/gesture-util.js
+++ b/third_party/blink/web_tests/resources/gesture-util.js
@@ -30,14 +30,14 @@
   });
 }
 
-// Returns a promise that resolves when the given condition holds for 100
-// animation frames or rejects if the condition changes to false within 100
+// Returns a promise that resolves when the given condition holds for 10
+// animation frames or rejects if the condition changes to false within 10
 // animation frames.
 function conditionHolds(condition, error_message = 'Condition is not true anymore.') {
-  const MAX_FRAME = 100;
+  const MAX_FRAME = 10;
   return new Promise((resolve, reject) => {
     function tick(frames) {
-      // We requestAnimationFrame either for 100 frames or until condition is
+      // We requestAnimationFrame either for 10 frames or until condition is
       // violated.
       if (frames >= MAX_FRAME)
         resolve();
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-browser-zoom-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-browser-zoom-expected.png
deleted file mode 100644
index 557b0fc..0000000
--- a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-browser-zoom-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png
deleted file mode 100644
index 4b960016..0000000
--- a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-transform-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-transform-expected.png
index 23174df..9d425786 100644
--- a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-transform-expected.png
+++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-transform-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/wake-lock/wakelock-document-hidden.https.html b/third_party/blink/web_tests/wake-lock/wakelock-document-hidden.https.html
index 9e63987..10cdcad 100644
--- a/third_party/blink/web_tests/wake-lock/wakelock-document-hidden.https.html
+++ b/third_party/blink/web_tests/wake-lock/wakelock-document-hidden.https.html
@@ -6,6 +6,8 @@
 <script>
 'use strict';
 
+self.testRunner.setPermission('wake-lock-screen', 'granted', location.origin, location.origin);
+
 promise_test(t => {
   window.testRunner.setPageVisibility('hidden');
   assert_true(document.hidden);
@@ -15,8 +17,7 @@
 promise_test(t => {
   window.testRunner.setPageVisibility('visible');
 
-  const controller = new AbortController();
-  const screenLock = WakeLock.request('screen', { signal: controller.signal });
+  const screenLock = WakeLock.request('screen');
   window.testRunner.setPageVisibility('hidden');
   assert_true(document.hidden);
   return promise_rejects(t, "AbortError", screenLock);
diff --git a/third_party/blink/web_tests/wake-lock/wakelock-request-denied.html b/third_party/blink/web_tests/wake-lock/wakelock-request-denied.html
new file mode 100644
index 0000000..adecbf0
--- /dev/null
+++ b/third_party/blink/web_tests/wake-lock/wakelock-request-denied.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<script>
+'use strict';
+
+promise_test(t => {
+  self.testRunner.setPermission('wake-lock-screen', 'denied', location.origin, location.origin);
+  return promise_rejects(t, "NotAllowedError", WakeLock.request('screen'));
+}, 'Denied requests should abort with NotAllowedError');
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/wake-lock/wakelock-requestPermission.https.html b/third_party/blink/web_tests/wake-lock/wakelock-requestPermission.https.html
new file mode 100644
index 0000000..be7df136
--- /dev/null
+++ b/third_party/blink/web_tests/wake-lock/wakelock-requestPermission.https.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<script>
+'use strict';
+
+promise_test(async t => {
+  self.testRunner.setPermission('wake-lock-screen', 'granted', location.origin, location.origin);
+  const status = await WakeLock.requestPermission('screen');
+  assert_equals(status, 'granted');
+}, 'WakeLock.requestPermission() returns "granted" for allowed requests');
+
+promise_test(async t => {
+  self.testRunner.setPermission('wake-lock-system', 'denied', location.origin, location.origin);
+  const status = await WakeLock.requestPermission('system');
+  assert_equals(status, 'denied');
+}, 'WakeLock.requestPermission() returns "denied" for blocked requests');
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
index 48253350..21bdea7 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
@@ -962,12 +962,8 @@
 [Worker]     method set
 [Worker] interface Performance : EventTarget
 [Worker]     attribute @@toStringTag
-[Worker]     getter onelementtimingbufferfull
-[Worker]     getter oneventtimingbufferfull
 [Worker]     getter onresourcetimingbufferfull
 [Worker]     getter timeOrigin
-[Worker]     method clearElementTimings
-[Worker]     method clearEventTimings
 [Worker]     method clearMarks
 [Worker]     method clearMeasures
 [Worker]     method clearResourceTimings
@@ -979,12 +975,8 @@
 [Worker]     method measure
 [Worker]     method now
 [Worker]     method profile
-[Worker]     method setElementTimingBufferMaxSize
-[Worker]     method setEventTimingBufferMaxSize
 [Worker]     method setResourceTimingBufferSize
 [Worker]     method toJSON
-[Worker]     setter onelementtimingbufferfull
-[Worker]     setter oneventtimingbufferfull
 [Worker]     setter onresourcetimingbufferfull
 [Worker] interface PerformanceEntry
 [Worker]     attribute @@toStringTag
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index 543c2222..69e48ef9 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -4398,11 +4398,17 @@
     setter target
 interface LargestContentfulPaint : PerformanceEntry
     attribute @@toStringTag
+    getter element
+    getter id
+    getter responseEnd
     getter size
+    getter url
     method constructor
     method toJSON
 interface LayoutShift : PerformanceEntry
     attribute @@toStringTag
+    getter hadRecentInput
+    getter lastInputTime
     getter value
     method constructor
     method toJSON
@@ -5368,13 +5374,9 @@
     attribute @@toStringTag
     getter memory
     getter navigation
-    getter onelementtimingbufferfull
-    getter oneventtimingbufferfull
     getter onresourcetimingbufferfull
     getter timeOrigin
     getter timing
-    method clearElementTimings
-    method clearEventTimings
     method clearMarks
     method clearMeasures
     method clearResourceTimings
@@ -5386,12 +5388,8 @@
     method measure
     method now
     method profile
-    method setElementTimingBufferMaxSize
-    method setEventTimingBufferMaxSize
     method setResourceTimingBufferSize
     method toJSON
-    setter onelementtimingbufferfull
-    setter oneventtimingbufferfull
     setter onresourcetimingbufferfull
 interface PerformanceElementTiming : PerformanceEntry
     attribute @@toStringTag
@@ -7298,14 +7296,6 @@
     getter onaudioprocess
     method constructor
     setter onaudioprocess
-interface ScriptedTaskQueue
-    attribute @@toStringTag
-    method constructor
-    method postTask
-interface ScriptedTaskQueueController
-    attribute @@toStringTag
-    method constructor
-    method default
 interface ScrollTimeline : AnimationTimeline
     attribute @@toStringTag
     getter endScrollOffset
@@ -8182,6 +8172,7 @@
     setter onscroll
 interface WakeLock
     static method request
+    static method requestPermission
     attribute @@toStringTag
     method constructor
 interface WaveShaperNode : AudioNode
@@ -10956,7 +10947,6 @@
     attribute textInputController
     attribute top
     attribute window
-    getter TaskQueue
     getter TrustedTypes
     getter applicationCache
     getter caches
@@ -11164,7 +11154,6 @@
     method webkitRequestAnimationFrame
     method webkitRequestFileSystem
     method webkitResolveLocalFileSystemURL
-    setter TaskQueue
     setter clientInformation
     setter cookieStore
     setter defaultStatus
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
index 185a304..54c127c 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
@@ -944,12 +944,8 @@
 [Worker]     method set
 [Worker] interface Performance : EventTarget
 [Worker]     attribute @@toStringTag
-[Worker]     getter onelementtimingbufferfull
-[Worker]     getter oneventtimingbufferfull
 [Worker]     getter onresourcetimingbufferfull
 [Worker]     getter timeOrigin
-[Worker]     method clearElementTimings
-[Worker]     method clearEventTimings
 [Worker]     method clearMarks
 [Worker]     method clearMeasures
 [Worker]     method clearResourceTimings
@@ -961,12 +957,8 @@
 [Worker]     method measure
 [Worker]     method now
 [Worker]     method profile
-[Worker]     method setElementTimingBufferMaxSize
-[Worker]     method setEventTimingBufferMaxSize
 [Worker]     method setResourceTimingBufferSize
 [Worker]     method toJSON
-[Worker]     setter onelementtimingbufferfull
-[Worker]     setter oneventtimingbufferfull
 [Worker]     setter onresourcetimingbufferfull
 [Worker] interface PerformanceEntry
 [Worker]     attribute @@toStringTag
diff --git a/third_party/ced/DEPS b/third_party/ced/DEPS
deleted file mode 100644
index e7074e0..0000000
--- a/third_party/ced/DEPS
+++ /dev/null
@@ -1,5 +0,0 @@
-specific_include_rules = {
-  'compact_enc_det_fuzzer\.cc': [
-    '+base/test/fuzzed_data_provider.h',
-  ],
-}
diff --git a/third_party/libprotobuf-mutator/BUILD.gn b/third_party/libprotobuf-mutator/BUILD.gn
index 733c5c1..021f988 100644
--- a/third_party/libprotobuf-mutator/BUILD.gn
+++ b/third_party/libprotobuf-mutator/BUILD.gn
@@ -36,8 +36,8 @@
   ]
 
   # Let ClusterFuzz builders know to not build targets that depend on
-  # libprotobuf-mutator for AFL.
-  if (use_afl) {
+  # libprotobuf-mutator for AFL or Chrome OS.
+  if (use_afl || current_toolchain == "//build/toolchain/cros:target") {
     all_dependent_configs = [ "//testing/libfuzzer:no_clusterfuzz" ]
   }
 }
diff --git a/third_party/libprotobuf-mutator/fuzzable_proto_library.gni b/third_party/libprotobuf-mutator/fuzzable_proto_library.gni
index 677e4eab..a24bf24 100644
--- a/third_party/libprotobuf-mutator/fuzzable_proto_library.gni
+++ b/third_party/libprotobuf-mutator/fuzzable_proto_library.gni
@@ -15,7 +15,9 @@
 import("//third_party/protobuf/proto_library.gni")
 
 template("fuzzable_proto_library") {
-  if (use_libfuzzer) {
+  # Only make the proto library fuzzable if we are doing a build that we can
+  # use LPM on (i.e. libFuzzer not on Chrome OS).
+  if (use_libfuzzer && current_toolchain != "//build/toolchain/cros:target") {
     proto_library("proto_library_" + target_name) {
       forward_variables_from(invoker, "*")
       assert(current_toolchain == host_toolchain)
diff --git a/third_party/libvpx/README.chromium b/third_party/libvpx/README.chromium
index b2f661e..4114164 100644
--- a/third_party/libvpx/README.chromium
+++ b/third_party/libvpx/README.chromium
@@ -5,9 +5,9 @@
 License File: source/libvpx/LICENSE
 Security Critical: yes
 
-Date: Wednesday June 26 2019
+Date: Monday July 01 2019
 Branch: master
-Commit: 30e7f9d856eb1cc6df895f6d9562493e04f6116d
+Commit: cd9f1763c861edfd86d2814e029a34f3ce821e72
 
 Description:
 Contains the sources used to compile libvpx binaries used by Google Chrome and
diff --git a/third_party/libvpx/source/config/vpx_version.h b/third_party/libvpx/source/config/vpx_version.h
index 144bceee..f6b1609 100644
--- a/third_party/libvpx/source/config/vpx_version.h
+++ b/third_party/libvpx/source/config/vpx_version.h
@@ -2,7 +2,7 @@
 #define VERSION_MAJOR  1
 #define VERSION_MINOR  8
 #define VERSION_PATCH  0
-#define VERSION_EXTRA  "575-g30e7f9d85"
+#define VERSION_EXTRA  "589-gcd9f1763c8"
 #define VERSION_PACKED ((VERSION_MAJOR<<16)|(VERSION_MINOR<<8)|(VERSION_PATCH))
-#define VERSION_STRING_NOSP "v1.8.0-575-g30e7f9d85"
-#define VERSION_STRING      " v1.8.0-575-g30e7f9d85"
+#define VERSION_STRING_NOSP "v1.8.0-589-gcd9f1763c8"
+#define VERSION_STRING      " v1.8.0-589-gcd9f1763c8"
diff --git a/tools/accessibility/DEPS b/tools/accessibility/DEPS
index 02d3a21c..8efc528 100644
--- a/tools/accessibility/DEPS
+++ b/tools/accessibility/DEPS
@@ -1,3 +1,4 @@
 include_rules = [
   "+content/browser/accessibility",
+  "+content/public/browser",
 ]
diff --git a/tools/accessibility/inspect/ax_tree_server.cc b/tools/accessibility/inspect/ax_tree_server.cc
index 58b30d1..fa7e29c 100644
--- a/tools/accessibility/inspect/ax_tree_server.cc
+++ b/tools/accessibility/inspect/ax_tree_server.cc
@@ -14,8 +14,10 @@
 #include "base/path_service.h"
 #include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_restrictions.h"
 
 namespace content {
 
diff --git a/tools/accessibility/inspect/ax_tree_server.h b/tools/accessibility/inspect/ax_tree_server.h
index 40ec00d..57d20c1 100644
--- a/tools/accessibility/inspect/ax_tree_server.h
+++ b/tools/accessibility/inspect/ax_tree_server.h
@@ -9,7 +9,7 @@
 
 #include "base/process/process_handle.h"
 #include "build/build_config.h"
-#include "content/browser/accessibility/accessibility_tree_formatter.h"
+#include "content/public/browser/accessibility_tree_formatter.h"
 
 #if defined(OS_WIN)
 #include "base/win/scoped_com_initializer.h"
diff --git a/tools/clang/stack_maps/OWNERS b/tools/clang/stack_maps/OWNERS
new file mode 100644
index 0000000..2212c51
--- /dev/null
+++ b/tools/clang/stack_maps/OWNERS
@@ -0,0 +1,4 @@
+mlippautz@chromium.org
+
+# TEAM: v8-dev@chromium.org
+# COMPONENT: Blink>JavaScript>GC
diff --git a/tools/clang/stack_maps/README b/tools/clang/stack_maps/README
new file mode 100644
index 0000000..c7219f88
--- /dev/null
+++ b/tools/clang/stack_maps/README
@@ -0,0 +1,4 @@
+Prototype and playground for generating stack maps to garbage-collected objects
+using clang/llvm infrastructure.
+
+Design doc: https://bit.ly/chromium-stack-maps
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index cc72d73..53b461c 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -87,16 +87,6 @@
       'x86 Emulator Tester': 'android_debug_static_bot_x86',
     },
 
-    # TODO(bpastene): Remove 'chromium.chrome' when it's been switched to 'chrome'
-    'chromium.chrome': {
-      'chromeos-amd64-generic-google-rel': 'official_cros_chrome_sdk',
-      'chromeos-betty-google-rel': 'official_cros_chrome_sdk_headless_ozone',
-      'linux-chromeos-google-rel': 'official_goma_chromeos_minimal_symbols',
-      'linux-google-rel': 'official_goma',
-      'mac-google-rel': 'official_goma',
-      'win-google-rel': 'official_goma_x86',
-    },
-
     'chromium.chromedriver': {
       'Win7': 'release_bot_x86',
       'Mac 10.6': 'release_bot',
@@ -687,12 +677,6 @@
       'win_chrome_official': 'official_goma_x86',
     },
 
-    'tryserver.chromium.chrome': {
-      'chromeos-betty-chrome': 'official_cros_chrome_sdk_headless_ozone',
-      'linux-chrome': 'official_goma',
-      'linux-chromeos-chrome': 'official_goma_chromeos_minimal_symbols',
-    },
-
     'tryserver.chromium.chromiumos': {
       # TODO(crbug.com/913750): Enable DCHECKS on the two amd64-generic bots
       # and two kevin bots when the PFQ has it enabled.
@@ -864,10 +848,6 @@
       'win_upload_clang': 'release_bot',
     },
 
-    'tryserver.chrome.win': {
-      'win_chrome_official': 'official_goma_x86',
-    },
-
     'tryserver.v8': {
       'v8_linux_blink_rel': 'release_trybot',
       'v8_linux_chromium_gn_rel': 'release_trybot',
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 3e6d2d0..22e9359 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -23799,6 +23799,18 @@
   <int value="2940" label="CSSAtRuleProperty"/>
   <int value="2941"
       label="ServiceWorkerInterceptedRequestFromOriginDirtyStyleSheet"/>
+  <int value="2942" label="WebkitMarginBeforeCollapseDiscard"/>
+  <int value="2943" label="WebkitMarginBeforeCollapseSeparate"/>
+  <int value="2944"
+      label="WebkitMarginBeforeCollapseSeparateMaybeDoesSomething"/>
+  <int value="2945" label="WebkitMarginAfterCollapseDiscard"/>
+  <int value="2946" label="WebkitMarginAfterCollapseSeparate"/>
+  <int value="2947"
+      label="WebkitMarginAfterCollapseSeparateMaybeDoesSomething"/>
+  <int value="2948" label="CredentialManagerCreateWithUVM"/>
+  <int value="2949" label="CredentialManagerGetWithUVM"/>
+  <int value="2950" label="CredentialManagerCreateSuccessWithUVM"/>
+  <int value="2951" label="CredentialManagerGetSuccessWithUVM"/>
 </enum>
 
 <enum name="FeaturePolicyAllowlistType">
@@ -29875,6 +29887,10 @@
   <int value="7" label="Cache startup eviction finished"/>
   <int value="8" label="Java in-memory cache hit"/>
   <int value="9" label="Java disk cache hit"/>
+  <int value="10" label="Image queued for transcoding was decoded."/>
+  <int value="11"
+      label="Image queued for transcoding was stored back to cache."/>
+  <int value="12" label="Load image metadata"/>
 </enum>
 
 <enum name="IMECommitType">
@@ -34805,6 +34821,7 @@
   <int value="-250822813" label="PwaImprovedSplashScreen:enabled"/>
   <int value="-250721831" label="AndroidAutofillAccessibility:disabled"/>
   <int value="-248223420" label="AutofillKeyboardAccessory:disabled"/>
+  <int value="-243323793" label="OmniboxOnDeviceHeadProvider:enabled"/>
   <int value="-241353344" label="MidiManagerWinrt:disabled"/>
   <int value="-240531943" label="ContextualSearchRankerQuery:disabled"/>
   <int value="-239616243" label="HighDynamicRange:enabled"/>
@@ -35011,6 +35028,7 @@
   <int value="44088203" label="ExpensiveBackgroundTimerThrottling:enabled"/>
   <int value="48159177" label="reduced-referrer-granularity"/>
   <int value="48223610" label="SiteSettings:disabled"/>
+  <int value="49113130" label="OmniboxOnDeviceHeadProvider:disabled"/>
   <int value="51793504" label="protect-sync-credential-on-reauth:disabled"/>
   <int value="52368742" label="enable-pixel-canvas-recording:disabled"/>
   <int value="54571864" label="EnableDisplayZoomSetting:enabled"/>
@@ -45916,6 +45934,30 @@
   <int value="2" label="Other Aborted"/>
 </enum>
 
+<enum name="PaymentRequestMissingContactFields">
+  <summary>
+    A bit field value that shows the missing fields of contact info section in
+    payment sheet. The cardinality is not too high since the total number of
+    used bits is less than 4.
+  </summary>
+</enum>
+
+<enum name="PaymentRequestMissingPaymentFields">
+  <summary>
+    A bit field value that shows the missing fields of payment section in
+    payment sheet. The cardinality is not too high since the total number of
+    used bits is less than 6.
+  </summary>
+</enum>
+
+<enum name="PaymentRequestMissingShippingFields">
+  <summary>
+    A bit field value that shows the missing fields of shipping section in
+    payment sheet. The cardinality is not too high since the total number of
+    used bits is less than 4.
+  </summary>
+</enum>
+
 <enum name="PaymentRequestNoShowReason">
   <int value="0" label="NoMatchingPaymentMethod"/>
   <int value="1" label="NoSupportedPaymentMethod"/>
@@ -53555,6 +53597,12 @@
   <int value="5" label="Authenticated channel dropped"/>
 </enum>
 
+<enum name="SMSReceiverOutcome">
+  <int value="0" label="Success"/>
+  <int value="1" label="Timed out"/>
+  <int value="2" label="Connection Error"/>
+</enum>
+
 <enum name="SnackbarIdentifier">
   <int value="-2" label="TEST_SNACKBAR"/>
   <int value="-1" label="UNKNOWN"/>
@@ -53590,6 +53638,7 @@
   <int value="29" label="UMA_AUTOFILL_ASSISTANT_STOP_UNDO"/>
   <int value="30" label="UMA_TAB_CLOSE_MULTIPLE_UNDO"/>
   <int value="31" label="UMA_SEARCH_ENGINE_CHOICE_NOTIFICATION"/>
+  <int value="32" label="UMA_TAB_GROUP_MANUAL_CREATION_UNDO"/>
 </enum>
 
 <enum name="SnippetOpenMethod">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index dbba78e6..a433eb10 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -1806,7 +1806,7 @@
 </histogram>
 
 <histogram name="Android.DarkTheme.EnabledReason" enum="DarkThemeEnabledReason"
-    expires_after="M78">
+    expires_after="M90">
   <owner>huayinz@chromium.org</owner>
   <owner>chrome-android-app@chromium.org</owner>
   <summary>
@@ -1816,7 +1816,7 @@
 </histogram>
 
 <histogram name="Android.DarkTheme.EnabledState" enum="BooleanEnabled"
-    expires_after="M78">
+    expires_after="M90">
   <owner>huayinz@chromium.org</owner>
   <owner>chrome-android-app@chromium.org</owner>
   <summary>
@@ -1826,7 +1826,7 @@
 </histogram>
 
 <histogram name="Android.DarkTheme.Preference.State"
-    enum="DarkThemePreferences" expires_after="M78">
+    enum="DarkThemePreferences" expires_after="M90">
   <owner>huayinz@chromium.org</owner>
   <owner>chrome-android-app@chromium.org</owner>
   <summary>
@@ -13916,6 +13916,22 @@
   </summary>
 </histogram>
 
+<histogram name="Blink.Sms.Receive.Outcome" enum="SMSReceiverOutcome">
+  <owner>goto@chromium.org</owner>
+  <owner>reillyg@chromium.org</owner>
+  <owner>ayui@chromium.org</owner>
+  <summary>Records the result of a call to the SmsReceiver API.</summary>
+</histogram>
+
+<histogram name="Blink.Sms.Receive.TimeSuccess" units="ms">
+  <owner>goto@chromium.org</owner>
+  <owner>reillyg@chromium.org</owner>
+  <owner>ayui@chromium.org</owner>
+  <summary>
+    Records how long it takes for the API to successfully receive the SMS.
+  </summary>
+</histogram>
+
 <histogram name="Blink.SpatialNavigation.Advance" units="microseconds">
   <owner>bokan@chromium.org</owner>
   <summary>
@@ -14095,9 +14111,9 @@
 </histogram>
 
 <histogram name="Blink.UseCounter.FeaturePolicy.ImageDownscalingRatio"
-    units="%" expires_after="M77">
-  <owner>loonybear@chromium.org</owner>
+    units="%" expires_after="M80">
   <owner>iclelland@chromium.org</owner>
+  <owner>feature-control@chromium.org</owner>
   <summary>
     Logs downscaling ratio in percentage for images enforced by feature policy
     oversized-images policy going into origin trials in M75. If an image's
@@ -14108,9 +14124,9 @@
 </histogram>
 
 <histogram name="Blink.UseCounter.FeaturePolicy.ImageFormats"
-    enum="FeaturePolicyImageCompressionFormat" expires_after="M77">
-  <owner>loonybear@chromium.org</owner>
+    enum="FeaturePolicyImageCompressionFormat" expires_after="M80">
   <owner>iclelland@chromium.org</owner>
+  <owner>feature-control@chromium.org</owner>
   <summary>
     Counts ImageFileFormats (lossy, lossless, webp animation, others) of images
     enforced by feature policy unoptimized-images policy going into origin
@@ -14119,9 +14135,9 @@
 </histogram>
 
 <histogram name="Blink.UseCounter.FeaturePolicy.LosslessImageCompression"
-    units="%" expires_after="M77">
-  <owner>loonybear@chromium.org</owner>
+    units="%" expires_after="M80">
   <owner>iclelland@chromium.org</owner>
+  <owner>feature-control@chromium.org</owner>
   <summary>
     Logs compression ratio in percentage with 1KB overhead for lossless type
     images enforced by feature policy unoptimized-lossless-images policy going
@@ -14133,9 +14149,9 @@
 </histogram>
 
 <histogram name="Blink.UseCounter.FeaturePolicy.LossyImageCompression"
-    units="%" expires_after="M77">
-  <owner>loonybear@chromium.org</owner>
+    units="%" expires_after="M80">
   <owner>iclelland@chromium.org</owner>
+  <owner>feature-control@chromium.org</owner>
   <summary>
     Logs compression ratio with 1KB overhead for lossy type images enforced by
     feature policy unoptimized-lossy-images policy going into origin trials in
@@ -14160,9 +14176,9 @@
 </histogram>
 
 <histogram name="Blink.UseCounter.FeaturePolicy.StrictLosslessImageCompression"
-    units="%" expires_after="M77">
-  <owner>loonybear@chromium.org</owner>
+    units="%" expires_after="M80">
   <owner>iclelland@chromium.org</owner>
+  <owner>feature-control@chromium.org</owner>
   <summary>
     Logs compression ratio with 10KB overhead for lossless type images enforced
     by feature policy unoptimized-lossless-images-strict policy going into
@@ -14701,6 +14717,9 @@
 
 <histogram name="BloatedRenderer.HandlingInBrowser"
     enum="BloatedRendererHandlingInBrowser">
+  <obsolete>
+    Obsolete as of 05/2019.
+  </obsolete>
   <owner>ulan@chromium.org</owner>
   <summary>
     Records how a bloated renderer was handled in the browser process.
@@ -14709,6 +14728,9 @@
 
 <histogram name="BloatedRenderer.HandlingInResourceCoordinator"
     enum="BloatedRendererHandlingInResourceCoordinator">
+  <obsolete>
+    Obsolete as of 05/2019.
+  </obsolete>
   <owner>ulan@chromium.org</owner>
   <summary>
     Records how a bloated renderer was handled in the resource coordinator
@@ -14718,6 +14740,9 @@
 
 <histogram name="BloatedRenderer.V8.NearV8HeapLimitHandling"
     enum="NearV8HeapLimitHandling">
+  <obsolete>
+    Obsolete as of 05/2019.
+  </obsolete>
   <owner>ulan@chromium.org</owner>
   <summary>
     Records how a bloated V8 heap was handled in the renderer process. It is
@@ -16492,6 +16517,15 @@
   </summary>
 </histogram>
 
+<histogram name="CachedImageFetcher.LoadImageMetadata" units="ms"
+    expires_after="2020-06-30">
+  <owner>fgorski@chromium.org</owner>
+  <owner>wylieb@chromium.org</owner>
+  <summary>
+    The time it takes to load an image's metadata from the metadata store.
+  </summary>
+</histogram>
+
 <histogram name="CachedImageFetcher.TimeSinceLastCacheLRUEviction" units="ms"
     expires_after="2019-12-01">
   <owner>fgorski@chromium.org</owner>
@@ -18132,7 +18166,7 @@
 </histogram>
 
 <histogram name="ChromiumAndroidLinker.BrowserLoadTime" units="ms"
-    expires_after="M77">
+    expires_after="M99">
   <owner>rsesek@chromium.org</owner>
   <summary>
     The amount of time it took to load the native libraries in the browser
@@ -18198,7 +18232,7 @@
 </histogram>
 
 <histogram name="ChromiumAndroidLinker.RendererLoadTime" units="ms"
-    expires_after="M77">
+    expires_after="M99">
   <owner>rsesek@chromium.org</owner>
   <summary>
     The amount of time it took to load the native libraries in the renderer
@@ -19178,6 +19212,19 @@
   </summary>
 </histogram>
 
+<histogram name="Compositing.Display.DrawToSwapUs" units="microseconds"
+    expires_after="2020-07-02">
+  <owner>backer@chromium.org</owner>
+  <owner>rjkroege@chromium.org</owner>
+  <summary>
+    This is logged once per frame, if the output surface provides timing
+    information. It measures the time from the display compositor starting a
+    draw on the Viz Compositor thread to issuing all related draw calls to the
+    driver on the Gpu Main thread. Only reported for platforms supporting high
+    resolution clocks.
+  </summary>
+</histogram>
+
 <histogram
     name="Compositing.DisplayListRecordingSource.UpdateInvalidatedAreaPerMs"
     units="pixels/ms">
@@ -23690,7 +23737,10 @@
   </summary>
 </histogram>
 
-<histogram name="Cryptohome.TimeToMountAsync" units="ms" expires_after="M77">
+<histogram name="Cryptohome.TimeToMountAsync" units="ms">
+  <obsolete>
+    Deprecated 07/2019 as it is not used since crrev.com/c/1393048
+  </obsolete>
   <owner>apronin@chromium.org</owner>
   <owner>cros-hwsec@chromium.org</owner>
   <summary>
@@ -53382,7 +53432,7 @@
 </histogram>
 
 <histogram name="ManagedUsers.FilteringResult"
-    enum="SupervisedUserSafetyFilterResult" expires_after="M77">
+    enum="SupervisedUserSafetyFilterResult" expires_after="M85">
   <owner>treib@chromium.org</owner>
   <owner>escordeiro@chromium.org</owner>
   <owner>menegola@chromium.org</owner>
@@ -53440,7 +53490,7 @@
 </histogram>
 
 <histogram name="ManagedUsers.SafetyFilter"
-    enum="SupervisedUserSafetyFilterResult" expires_after="M77">
+    enum="SupervisedUserSafetyFilterResult" expires_after="M85">
   <owner>treib@chromium.org</owner>
   <owner>escordeiro@chromium.org</owner>
   <owner>menegola@chromium.org</owner>
@@ -53454,7 +53504,7 @@
 </histogram>
 
 <histogram name="ManagedUsers.Whitelist.Count" units="whitelists"
-    expires_after="M77">
+    expires_after="M85">
   <owner>treib@chromium.org</owner>
   <owner>escordeiro@chromium.org</owner>
   <owner>menegola@chromium.org</owner>
@@ -72830,7 +72880,7 @@
 </histogram>
 
 <histogram name="Net.QuicSession.ConnectionIpPooled" enum="Boolean"
-    expires_after="2019-06-30">
+    expires_after="2020-06-30">
   <owner>renjietang@chromium.org</owner>
   <owner>rch@chromium.org</owner>
   <summary>
@@ -91566,6 +91616,9 @@
 
 <histogram name="PageSerialization.MhtmlGeneration.PopupOverlaySkipped"
     enum="BooleanSkipped" expires_after="2018-08-30">
+  <obsolete>
+    Deprecated 6/2019. No longer used.
+  </obsolete>
   <owner>jianli@chromium.org</owner>
   <owner>offline-dev@chromium.org</owner>
   <summary>
@@ -94232,6 +94285,16 @@
   </summary>
 </histogram>
 
+<histogram name="PasswordProtection.DomFeatureParsing" enum="BooleanSuccess"
+    expires_after="M78">
+  <owner>drubery@chromium.org</owner>
+  <owner>chrome-safebrowsing-alerts@google.com</owner>
+  <summary>
+    Records whether the DOM features were parsed successfully when returned from
+    the renderer. This is logged on every PhishGuard ping.
+  </summary>
+</histogram>
+
 <histogram
     name="PasswordProtection.GaiaPasswordReusesBeforeGaiaPasswordChanged"
     units="reuses">
@@ -94623,6 +94686,42 @@
   </summary>
 </histogram>
 
+<histogram name="PaymentRequest.MissingContactFields"
+    enum="PaymentRequestMissingContactFields">
+  <owner>sahel@chromium.org</owner>
+  <summary>
+    A bitfield representing different missing fields of the contact section in
+    payment sheet. This only gets recorded when no complete contact profile is
+    available. In case of multiple partially complete profiles, this is only
+    recorded for the most complete one which is also the first profile in the
+    suggestion list.
+  </summary>
+</histogram>
+
+<histogram name="PaymentRequest.MissingPaymentFields"
+    enum="PaymentRequestMissingPaymentFields">
+  <owner>sahel@chromium.org</owner>
+  <summary>
+    A bitfield representing different missing fields of the payment info section
+    in the payment sheet. This only gets recorded when no complete payment
+    instrument is available (SW based instruments are always considered as
+    complete). In case of multiple partially complete cards this is only
+    recorded for the first card in the suggestion list.
+  </summary>
+</histogram>
+
+<histogram name="PaymentRequest.MissingShippingFields"
+    enum="PaymentRequestMissingShippingFields">
+  <owner>sahel@chromium.org</owner>
+  <summary>
+    A bitfield representing different missing fields of the shipping section in
+    the payment sheet. This only gets recorded when no complete shipping profile
+    is available. In case of multiple partially complete profiles, this is only
+    recorded for the most complete one which is also the first profile in the
+    suggestion list.
+  </summary>
+</histogram>
+
 <histogram name="PaymentRequest.NumberOfSelectionAdds">
   <owner>sebsg@chromium.org</owner>
   <summary>
@@ -100971,6 +101070,9 @@
 
 <histogram name="PrefService.NetworkPredictionEnabled" enum="BooleanEnabled"
     expires_after="M77">
+  <obsolete>
+    Removed as of 7/2019. No longer needed.
+  </obsolete>
   <owner>petewil@chromium.org</owner>
   <summary>
     Count events where Network Prediction was enabled or disabled.
@@ -128953,6 +129055,9 @@
 
 <histogram name="SubresourceFilter.DocumentLoad.ActivationComputingDelay"
     units="microseconds" expires_after="M77">
+  <obsolete>
+    Deprecated July 2019.
+  </obsolete>
   <owner>csharrison@chromium.org</owner>
   <summary>
     Records the total time the activation state navigation throttle within a
@@ -128969,6 +129074,9 @@
 <histogram
     name="SubresourceFilter.DocumentLoad.ActivationComputingDelay.MainFrame"
     units="microseconds" expires_after="M78">
+  <obsolete>
+    Deprecated July 2019.
+  </obsolete>
   <owner>csharrison@chromium.org</owner>
   <summary>
     Records the total time the activation state navigation throttle within a
@@ -140927,7 +141035,7 @@
   </summary>
 </histogram>
 
-<histogram name="V8.SharedArrayAllocationSizes" units="MB" expires_after="M77">
+<histogram name="V8.SharedArrayAllocationSizes" units="MB" expires_after="M80">
   <owner>gdeepti@chromium.org</owner>
   <owner>titzer@chromium.org</owner>
   <summary>
@@ -149943,8 +150051,10 @@
   </summary>
 </histogram>
 
-<histogram name="WorkerScheduler.WorkerThreadLoad" units="%"
-    expires_after="M77">
+<histogram name="WorkerScheduler.WorkerThreadLoad" units="%">
+  <obsolete>
+    Removed July 2019.
+  </obsolete>
   <owner>kinuko@chromium.org</owner>
   <summary>
     Worker thread load, i.e. percentage of time spent on running tasks. This
@@ -149971,7 +150081,10 @@
   <summary>Records the exit code of WorkerThread.</summary>
 </histogram>
 
-<histogram name="WorkerThread.Runtime" units="ms" expires_after="M77">
+<histogram name="WorkerThread.Runtime" units="ms">
+  <obsolete>
+    Removed July 2019.
+  </obsolete>
   <owner>kinuko@chromium.org</owner>
   <summary>
     The amount of time a worker thread ran for. Starts recording when a worker
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index 744ad53a..8a09e8f5 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -2056,6 +2056,9 @@
 </event>
 
 <event name="BloatedRenderer">
+  <obsolete>
+    Deprecated as of 03/2019.
+  </obsolete>
   <owner>ulan@chromium.org</owner>
   <summary>
     Metrics related to a bloated renderer that is close to out-of-memory. They
@@ -2941,7 +2944,7 @@
   <summary>
     Logged when the FindInPage returns a user search request result.
   </summary>
-  <metric name="HasMatches">
+  <metric name="HasMatches" enum="Boolean">
     <summary>
       True if there were matches.
     </summary>
@@ -5900,6 +5903,20 @@
       that a non-zero value in this field implies previews_likely.
     </summary>
   </metric>
+  <metric name="defer_all_script" enum="Boolean">
+    <summary>
+      Set to 1 when a user is shown a DeferAllScript preview on a page load.
+    </summary>
+  </metric>
+  <metric name="defer_all_script_eligibility_reason"
+      enum="PreviewsEligibilityReason">
+    <summary>
+      Set to the value of the last known reason a DeferAllScript preview was not
+      eligible on this page load. The value of this metric corresponds to the
+      PreviewsEligibilityReason enum. This metric is only set when it is
+      non-zero.
+    </summary>
+  </metric>
   <metric name="lite_page">
     <summary>
       Set to 1 when a user is shown a lite page in page load.
diff --git a/tools/traffic_annotation/auditor/BUILD.gn b/tools/traffic_annotation/auditor/BUILD.gn
index 6e2e467..8c9f65c5 100644
--- a/tools/traffic_annotation/auditor/BUILD.gn
+++ b/tools/traffic_annotation/auditor/BUILD.gn
@@ -113,7 +113,6 @@
     "tests/extractor_outputs/good_partial_annotation.txt",
     "tests/extractor_outputs/good_test_annotation.txt",
     "tests/extractor_outputs/missing_annotation.txt",
-    "tests/extractor_outputs/no_annotation.txt",
     "tests/git_list.txt",
     "tests/gn_list_negative.txt",
     "tests/gn_list_positive.txt",
diff --git a/tools/traffic_annotation/auditor/auditor_result.h b/tools/traffic_annotation/auditor/auditor_result.h
index 880dda16..4b331864f 100644
--- a/tools/traffic_annotation/auditor/auditor_result.h
+++ b/tools/traffic_annotation/auditor/auditor_result.h
@@ -17,6 +17,8 @@
     ERROR_MISSING_TAG_USED,  // A function is called with
                              // MISSING_TRAFFIC_ANNOTATION tag.
     ERROR_NO_ANNOTATION,     // A function is called with NO_ANNOTATION tag.
+                             // Deprecated, as NO_ANNOTATION is now undefined on
+                             // supported platforms.
     ERROR_SYNTAX,            // Annotation syntax is not right.
     ERROR_RESERVED_ID_HASH_CODE,    // An id has a hash code equal to a reserved
                                     // word.
diff --git a/tools/traffic_annotation/auditor/instance.cc b/tools/traffic_annotation/auditor/instance.cc
index 140a01c..256d6d9a 100644
--- a/tools/traffic_annotation/auditor/instance.cc
+++ b/tools/traffic_annotation/auditor/instance.cc
@@ -171,14 +171,6 @@
                          file_path, line_number);
   }
 
-  // Process undefined tags.
-  if (unique_id_hash_code == NO_TRAFFIC_ANNOTATION_YET.unique_id_hash_code ||
-      unique_id_hash_code ==
-          NO_PARTIAL_TRAFFIC_ANNOTATION_YET.unique_id_hash_code) {
-    return AuditorResult(AuditorResult::Type::ERROR_NO_ANNOTATION, "",
-                         file_path, line_number);
-  }
-
   // Process missing tag.
   if (unique_id_hash_code == MISSING_TRAFFIC_ANNOTATION.unique_id_hash_code)
     return AuditorResult(AuditorResult::Type::ERROR_MISSING_TAG_USED, "",
diff --git a/tools/traffic_annotation/auditor/safe_list.txt b/tools/traffic_annotation/auditor/safe_list.txt
index c22b137..bc9d41a 100644
--- a/tools/traffic_annotation/auditor/safe_list.txt
+++ b/tools/traffic_annotation/auditor/safe_list.txt
@@ -1,8 +1,12 @@
 # This is a comma separated file, specifying the safe list for network traffic
 # anntotation auditor. Please refer to README.md for more details.
 all,tools/*,*test*,*fuzzer*,*mock*,*fake*
+missing,remoting/host/token_validator_factory_impl.cc
+missing,components/cronet/cronet_url_request.cc
+missing,net/tools/quic/quic_http_proxy_backend_stream.cc
 missing,net/url_request/url_fetcher.cc
 missing,net/url_request/url_request_context.cc
 mutable_tag,components/download/internal/background_service/proto_conversions.cc
 test_annotation,components/safe_search_api/stub_url_checker.cc
 test_annotation,net/quic/quic_chromium_client_session_peer.cc
+test_annotation,net/tools/quic/quic_http_proxy_backend_stream.cc
diff --git a/tools/traffic_annotation/auditor/tests/extractor_outputs/no_annotation.txt b/tools/traffic_annotation/auditor/tests/extractor_outputs/no_annotation.txt
deleted file mode 100644
index e8b2838..0000000
--- a/tools/traffic_annotation/auditor/tests/extractor_outputs/no_annotation.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-components/download/internal/controller_impl.cc
-download::ControllerImpl::UpdateDriverState
-656
-Definition
-undefined
-
-Nothing here yet.
\ No newline at end of file
diff --git a/tools/traffic_annotation/auditor/traffic_annotation_auditor.cc b/tools/traffic_annotation/auditor/traffic_annotation_auditor.cc
index efc1ac9..8eb0699c 100644
--- a/tools/traffic_annotation/auditor/traffic_annotation_auditor.cc
+++ b/tools/traffic_annotation/auditor/traffic_annotation_auditor.cc
@@ -36,7 +36,6 @@
 std::map<int, std::string> kReservedAnnotations = {
     {TRAFFIC_ANNOTATION_FOR_TESTS.unique_id_hash_code, "test"},
     {PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS.unique_id_hash_code, "test_partial"},
-    {NO_TRAFFIC_ANNOTATION_YET.unique_id_hash_code, "undefined"},
     {MISSING_TRAFFIC_ANNOTATION.unique_id_hash_code, "missing"}};
 
 struct AnnotationID {
diff --git a/tools/traffic_annotation/auditor/traffic_annotation_auditor_unittest.cc b/tools/traffic_annotation/auditor/traffic_annotation_auditor_unittest.cc
index 085cc5e6..7b0d3ad 100644
--- a/tools/traffic_annotation/auditor/traffic_annotation_auditor_unittest.cc
+++ b/tools/traffic_annotation/auditor/traffic_annotation_auditor_unittest.cc
@@ -306,7 +306,6 @@
        AnnotationInstance::Type::ANNOTATION_PARTIAL},
       {"good_test_annotation.txt", AuditorResult::Type::ERROR_TEST_ANNOTATION},
       {"missing_annotation.txt", AuditorResult::Type::ERROR_MISSING_TAG_USED},
-      {"no_annotation.txt", AuditorResult::Type::ERROR_NO_ANNOTATION},
       {"fatal_annotation1.txt", AuditorResult::Type::ERROR_FATAL},
       {"fatal_annotation2.txt", AuditorResult::Type::ERROR_FATAL},
       {"fatal_annotation3.txt", AuditorResult::Type::ERROR_FATAL},
@@ -402,8 +401,6 @@
   int expected_ids[] = {
       TRAFFIC_ANNOTATION_FOR_TESTS.unique_id_hash_code,
       PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS.unique_id_hash_code,
-      NO_TRAFFIC_ANNOTATION_YET.unique_id_hash_code,
-      NO_PARTIAL_TRAFFIC_ANNOTATION_YET.unique_id_hash_code,
       MISSING_TRAFFIC_ANNOTATION.unique_id_hash_code};
 
   std::map<int, std::string> reserved_words =
diff --git a/tools/traffic_annotation/bin/linux64/traffic_annotation_auditor.sha1 b/tools/traffic_annotation/bin/linux64/traffic_annotation_auditor.sha1
index 1b43dcba..36278a7b 100644
--- a/tools/traffic_annotation/bin/linux64/traffic_annotation_auditor.sha1
+++ b/tools/traffic_annotation/bin/linux64/traffic_annotation_auditor.sha1
@@ -1 +1 @@
-b7c7913c62ac2423106fe2f35e45d3f5dcdff190
\ No newline at end of file
+73004f4964def13577f9d060d77908d51c30d521
\ No newline at end of file
diff --git a/tools/traffic_annotation/bin/win32/traffic_annotation_auditor.exe.sha1 b/tools/traffic_annotation/bin/win32/traffic_annotation_auditor.exe.sha1
index f784177..8e82538 100644
--- a/tools/traffic_annotation/bin/win32/traffic_annotation_auditor.exe.sha1
+++ b/tools/traffic_annotation/bin/win32/traffic_annotation_auditor.exe.sha1
@@ -1 +1 @@
-e37f10baabd3f87f6d7236e0dc32b100af415f0d
\ No newline at end of file
+e3c00e2607fbe0d5758e60bc42c6802feacdea08
\ No newline at end of file
diff --git a/tools/traffic_annotation/scripts/extractor.py b/tools/traffic_annotation/scripts/extractor.py
index b76da3b..b2572af 100755
--- a/tools/traffic_annotation/scripts/extractor.py
+++ b/tools/traffic_annotation/scripts/extractor.py
@@ -23,7 +23,6 @@
     'CreateMutableNetworkTrafficAnnotationTag': 'Mutable',
 }
 
-
 # Regex that matches an annotation definition.
 CALL_DETECTION_REGEX = re.compile(r'''
   \b
@@ -37,16 +36,22 @@
   \(
 ''', re.VERBOSE | re.DOTALL)
 
+# Regex that matches an annotation that should only be used in test files.
+TEST_ANNOTATION_REGEX = re.compile(
+    r'\b(PARTIAL_)?TRAFFIC_ANNOTATION_FOR_TESTS\b')
+
+# Regex that matches a placeholder annotation for a few whitelisted files.
+MISSING_ANNOTATION_REGEX = re.compile(r'\bMISSING_TRAFFIC_ANNOTATION\b')
 
 class Annotation:
   """A network annotation definition in C++ code."""
 
-  def __init__(self, file_path, re_match):
-    """Parses the annotation and populates object fields.
+  def __init__(self, file_path, line_number, type_name='', unique_id='',
+               extra_id='', text=''):
+    """Constructs an Annotation object with the given field values.
 
     Args:
       file_path: Path to the file that contains this annotation.
-      re_match: A MatchObject obtained from CALL_DETECTION_REGEX.
     """
     self.file_path = file_path
     # TODO(crbug/966883): Remove function_name from here and from the clang
@@ -56,21 +61,28 @@
     # compatibility with the clang tool. Use an obviously wrong function name
     # in case this details ends up in auditor output.
     self.function_name = "XXX_UNIMPLEMENTED_XXX"
-    self.line_number = get_line_number_at(re_match.string, re_match.start())
+    self.line_number = line_number
+    self.type_name = type_name
+    self.unique_id = unique_id
+    self.extra_id = extra_id
+    self.text = text
 
+  def parse_definition(self, re_match):
+    """Parses the annotation and populates object fields.
+
+    Args:
+      file_path: Path to the file that contains this annotation.
+      re_match: A MatchObject obtained from CALL_DETECTION_REGEX.
+    """
     definition_function = re_match.group(1)
     self.type_name = ANNOTATION_TYPES[definition_function]
 
-    # These are populated by _parse_body().
-    self.unique_id = ''
-    self.extra_id = ''
-    self.text = ''
-
     # Parse the arguments given to the definition function, populating
     # |unique_id|, |text| and (possibly) |extra_id|.
     body = re_match.string[re_match.end():]
     self._parse_body(body)
 
+
   def clang_tool_output_string(self):
     """Returns a string formatted for clang-tool-style output."""
     return "\n".join(map(str, [
@@ -150,10 +162,49 @@
     contents = f.read()
 
   defs = []
+
+  # Check for function calls (e.g. DefineNetworkTrafficAnnotation(...))
   for re_match in CALL_DETECTION_REGEX.finditer(contents):
     if is_inside_comment(re_match.string, re_match.start()):
       continue
-    defs.append(Annotation(file_path, re_match))
+    line_number = get_line_number_at(contents, re_match.start())
+    annotation = Annotation(file_path, line_number)
+    annotation.parse_definition(re_match)
+    defs.append(annotation)
+
+  # Check for test annotations (e.g. TRAFFIC_ANNOTATION_FOR_TESTS)
+  for re_match in TEST_ANNOTATION_REGEX.finditer(contents):
+    if is_inside_comment(re_match.string, re_match.start()):
+      continue
+    line_number = get_line_number_at(contents, re_match.start())
+
+    is_partial = bool(re_match.group(1))
+    if is_partial:
+      type_name = 'Partial'
+      unique_id = 'test_partial'
+      extra_id = 'test'
+    else:
+      type_name = 'Definition'
+      unique_id = 'test'
+      extra_id = ''
+
+    annotation = Annotation(
+        file_path, line_number, type_name=type_name,
+        unique_id=unique_id, extra_id=extra_id,
+        text='Traffic annotation for unit, browser and other tests')
+    defs.append(annotation)
+
+  # Check for MISSING_TRAFFIC_ANNOTATION.
+  for re_match in MISSING_ANNOTATION_REGEX.finditer(contents):
+    if is_inside_comment(re_match.string, re_match.start()):
+      continue
+    line_number = get_line_number_at(contents, re_match.start())
+
+    annotation = Annotation(
+        file_path, line_number, type_name='Definition', unique_id='missing',
+        text='Function called without traffic annotation.')
+    defs.append(annotation)
+
   return defs
 
 
diff --git a/tools/traffic_annotation/scripts/test_data/valid_file-stdout.txt b/tools/traffic_annotation/scripts/test_data/valid_file-stdout.txt
index 51d4c2e..b98d267 100644
--- a/tools/traffic_annotation/scripts/test_data/valid_file-stdout.txt
+++ b/tools/traffic_annotation/scripts/test_data/valid_file-stdout.txt
@@ -78,9 +78,27 @@
 ==== NEW ANNOTATION ====
 valid_file.cc
 XXX_UNIMPLEMENTED_XXX
-121
+126
 Mutable
 
 
 
 ==== ANNOTATION ENDS ====
+==== NEW ANNOTATION ====
+valid_file.cc
+XXX_UNIMPLEMENTED_XXX
+86
+Definition
+test
+
+Traffic annotation for unit, browser and other tests
+==== ANNOTATION ENDS ====
+==== NEW ANNOTATION ====
+valid_file.cc
+XXX_UNIMPLEMENTED_XXX
+88
+Partial
+test_partial
+test
+Traffic annotation for unit, browser and other tests
+==== ANNOTATION ENDS ====
diff --git a/tools/traffic_annotation/scripts/test_data/valid_file.cc b/tools/traffic_annotation/scripts/test_data/valid_file.cc
index 32a1e9f5..fc03c18f 100644
--- a/tools/traffic_annotation/scripts/test_data/valid_file.cc
+++ b/tools/traffic_annotation/scripts/test_data/valid_file.cc
@@ -81,6 +81,11 @@
 
   net::URLFetcher::Create(0, GURL(), net::URLFetcher::RequestType::TEST_VALUE,
                           delegate, NO_TRAFFIC_ANNOTATION_YET);
+
+  net::URLFetcher::Create(GURL(), net::URLFetcher::RequestType::TEST_VALUE,
+                          delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
+
+  SetPartialNetworkTrafficAnnotation(PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS);
 }
 
 void TestCreateRequest() {
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index 6ab62f2..377c38a7 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -101,7 +101,7 @@
  <item id="external_policy_fetcher" hash_code="9459438" type="0" content_hash_code="64260484" os_list="linux,windows" file_path="components/policy/core/common/cloud/external_policy_data_fetcher.cc"/>
  <item id="family_info" hash_code="30913825" type="0" content_hash_code="25369370" os_list="linux,windows" file_path="chrome/browser/supervised_user/child_accounts/family_info_fetcher.cc"/>
  <item id="favicon_loader" hash_code="112189210" type="0" content_hash_code="70773116" os_list="linux,windows" file_path="content/renderer/loader/web_url_loader_impl.cc"/>
- <item id="favicon_request_handler_get_favicon" hash_code="18468467" type="0" content_hash_code="72011341" os_list="linux,windows" file_path="components/favicon/core/favicon_request_handler.cc"/>
+ <item id="history_ui_favicon_request_handler_get_favicon" hash_code="17562717" type="0" content_hash_code="64054629" os_list="linux,windows" file_path="components/favicon/core/history_ui_favicon_request_handler_impl.cc"/>
  <item id="feed_image_fetcher" hash_code="87439531" type="0" deprecated="2019-01-04" content_hash_code="26756208" file_path=""/>
  <item id="gaia_auth_check_connection_info" hash_code="4598626" type="0" content_hash_code="57347000" os_list="linux,windows" file_path="google_apis/gaia/gaia_auth_fetcher.cc"/>
  <item id="gaia_auth_exchange_cookies" hash_code="134289752" type="0" deprecated="2018-09-11" content_hash_code="66433230" file_path=""/>
diff --git a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
index 3c0b696..5c06ac2 100644
--- a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
+++ b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
@@ -423,6 +423,8 @@
     ui::AXNodeData line_break1_data;
     line_break1_data.id = 5;
     line_break1_data.role = ax::mojom::Role::kLineBreak;
+    line_break1_data.AddBoolAttribute(
+        ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
     line_break1_data.SetName("\n");
 
     ui::AXNodeData standalone_text_data;
@@ -440,6 +442,8 @@
     ui::AXNodeData line_break2_data;
     line_break2_data.id = 7;
     line_break2_data.role = ax::mojom::Role::kLineBreak;
+    line_break2_data.AddBoolAttribute(
+        ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
     line_break2_data.SetName("\n");
 
     group2_data.child_ids = {5, 6, 7};
@@ -466,6 +470,8 @@
     ui::AXNodeData paragraph1_data;
     paragraph1_data.id = 9;
     paragraph1_data.role = ax::mojom::Role::kParagraph;
+    paragraph1_data.AddBoolAttribute(
+        ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
 
     ui::AXNodeData paragraph1_text_data;
     paragraph1_text_data.id = 10;
@@ -483,6 +489,8 @@
     ui::AXNodeData paragraph2_data;
     paragraph2_data.id = 11;
     paragraph2_data.role = ax::mojom::Role::kParagraph;
+    paragraph2_data.AddBoolAttribute(
+        ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
 
     ui::AXNodeData paragraph2_text_data;
     paragraph2_text_data.id = 12;
@@ -1672,7 +1680,125 @@
   ComPtr<ITextRangeProvider> text_range_provider;
   GetTextRangeProviderFromTextNode(text_range_provider, root_node);
 
-  // TODO(https://crbug.com/928948): add tests
+  // Moving by 0 should have no effect.
+  EXPECT_UIA_MOVE(
+      text_range_provider, TextUnit_Paragraph,
+      /*count*/ 0,
+      /*expected_text*/
+      L"First line of text\nStandalone line\nbold textParagraph 1Paragraph 2",
+      /*expected_count*/ 0);
+
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph,
+      /*count*/ -4,
+      /*expected_text*/ L"First line of text\n",
+      /*expected_count*/ -4);
+
+  // Move forward.
+  EXPECT_UIA_MOVE(text_range_provider, TextUnit_Paragraph,
+                  /*count*/ 1,
+                  /*expected_text*/ L"Standalone line\n",
+                  /*expected_count*/ 1);
+  EXPECT_UIA_MOVE(text_range_provider, TextUnit_Paragraph,
+                  /*count*/ 2,
+                  /*expected_text*/ L"Paragraph 1",
+                  /*expected_count*/ 2);
+  EXPECT_UIA_MOVE(text_range_provider, TextUnit_Paragraph,
+                  /*count*/ 1,
+                  /*expected_text*/ L"Paragraph 2",
+                  /*expected_count*/ 1);
+
+  // Trying to move past the last format should have no effect.
+  EXPECT_UIA_MOVE(text_range_provider, TextUnit_Paragraph,
+                  /*count*/ 1,
+                  /*expected_text*/ L"Paragraph 2",
+                  /*expected_count*/ 0);
+
+  // Move backward.
+  EXPECT_UIA_MOVE(text_range_provider, TextUnit_Paragraph,
+                  /*count*/ -3,
+                  /*expected_text*/ L"Standalone line\n",
+                  /*expected_count*/ -3);
+  EXPECT_UIA_MOVE(text_range_provider, TextUnit_Paragraph,
+                  /*count*/ -1,
+                  /*expected_text*/ L"First line of text\n",
+                  /*expected_count*/ -1);
+
+  // Moving backward by any number of paragraphs at the start of document
+  // should have no effect.
+  EXPECT_UIA_MOVE(text_range_provider, TextUnit_Paragraph,
+                  /*count*/ -1,
+                  /*expected_text*/
+                  L"First line of text\n",
+                  /*expected_count*/ 0);
+
+  // Test degenerate range creation at the beginning of the document.
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph,
+      /*count*/ -1,
+      /*expected_text*/ L"",
+      /*expected_count*/ -1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph,
+      /*count*/ 1,
+      /*expected_text*/ L"First line of text\n",
+      /*expected_count*/ 1);
+
+  // Test degenerate range creation at the end of the document.
+  EXPECT_UIA_MOVE(text_range_provider, TextUnit_Paragraph,
+                  /*count*/ 5,
+                  /*expected_text*/ L"Paragraph 2",
+                  /*expected_count*/ 4);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph,
+      /*count*/ 1,
+      /*expected_text*/ L"",
+      /*expected_count*/ 1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph,
+      /*count*/ -1,
+      /*expected_text*/ L"Paragraph 2",
+      /*expected_count*/ -1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph,
+      /*count*/ 1,
+      /*expected_text*/ L"",
+      /*expected_count*/ 1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph,
+      /*count*/ -1,
+      /*expected_text*/ L"Paragraph 2",
+      /*expected_count*/ -1);
+
+  // Degenerate range moves.
+  EXPECT_UIA_MOVE(text_range_provider, TextUnit_Paragraph,
+                  /*count*/ -6,
+                  /*expected_text*/ L"First line of text\n",
+                  /*expected_count*/ -5);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph,
+      /*count*/ -1,
+      /*expected_text*/ L"",
+      /*expected_count*/ -1);
+  EXPECT_UIA_MOVE(text_range_provider, TextUnit_Paragraph,
+                  /*count*/ 3,
+                  /*expected_text*/ L"",
+                  /*expected_count*/ 3);
+  EXPECT_UIA_MOVE(text_range_provider, TextUnit_Paragraph,
+                  /*count*/ 70,
+                  /*expected_text*/ L"",
+                  /*expected_count*/ 2);
+
+  // Trying to move past the last paragraph should have no effect.
+  EXPECT_UIA_MOVE(text_range_provider, TextUnit_Paragraph,
+                  /*count*/ 70,
+                  /*expected_text*/ L"",
+                  /*expected_count*/ 0);
+  EXPECT_UIA_MOVE(text_range_provider, TextUnit_Paragraph,
+                  /*count*/ -2,
+                  /*expected_text*/ L"",
+                  /*expected_count*/ -2);
+
   AXNodePosition::SetTreeForTesting(nullptr);
 }
 
diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn
index f41661ae..68e24a1c 100644
--- a/ui/base/BUILD.gn
+++ b/ui/base/BUILD.gn
@@ -1055,7 +1055,8 @@
     ]
   }
 
-  if (use_aura || toolkit_views) {
+  # TODO(crbug.com/980371): Implement OSExchangeDataProvider for Fuchsia.
+  if ((use_aura || toolkit_views) && !is_fuchsia) {
     sources += [ "dragdrop/os_exchange_data_unittest.cc" ]
 
     deps += [
diff --git a/ui/base/dragdrop/os_exchange_data_provider_factory.cc b/ui/base/dragdrop/os_exchange_data_provider_factory.cc
index 30a259cb1d..4c5fbfb 100644
--- a/ui/base/dragdrop/os_exchange_data_provider_factory.cc
+++ b/ui/base/dragdrop/os_exchange_data_provider_factory.cc
@@ -30,7 +30,7 @@
 #elif defined(OS_WIN)
   return std::make_unique<OSExchangeDataProviderWin>();
 #elif defined(OS_FUCHSIA)
-  // TODO(fuchsia): Implement this when UI support is added. (crbug.com/750934)
+  // TODO(crbug.com/980371): Implement OSExchangeDataProvider for Fuchsia.
   NOTIMPLEMENTED();
   return nullptr;
 #else
diff --git a/ui/file_manager/file_manager/foreground/elements/files_format_dialog.html b/ui/file_manager/file_manager/foreground/elements/files_format_dialog.html
index 70995d5..465b05c 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_format_dialog.html
+++ b/ui/file_manager/file_manager/foreground/elements/files_format_dialog.html
@@ -45,7 +45,7 @@
     </style>
 
     <cr-dialog id="dialog" show-close-button
-        close-text="[[i18n('CLOSE_LABEL')]]">
+        close-text="[[i18n('CLOSE_LABEL')]]" exportparts="dialog">
       <div slot="title">
         [[i18n('FORMAT_DIALOG_TITLE', volumeInfo_.label)]]
       </div>
diff --git a/ui/file_manager/file_manager/main.html b/ui/file_manager/file_manager/main.html
index de86a70..2650ee9 100644
--- a/ui/file_manager/file_manager/main.html
+++ b/ui/file_manager/file_manager/main.html
@@ -74,10 +74,8 @@
           --files-ripple-bg-color: black;
         }
 
-        files-format-dialog {
-          --cr-dialog-native: {
-            border-radius: 2px;
-          }
+        files-format-dialog::part(dialog) {
+          border-radius: 2px;
         }
       </style>
     </custom-style>
diff --git a/ui/message_center/views/notification_control_buttons_view.cc b/ui/message_center/views/notification_control_buttons_view.cc
index 69b1fba..cff9ebb 100644
--- a/ui/message_center/views/notification_control_buttons_view.cc
+++ b/ui/message_center/views/notification_control_buttons_view.cc
@@ -97,9 +97,9 @@
         gfx::CreateVectorIcon(kNotificationSnoozeButtonIcon,
                               gfx::kChromeIconGrey));
     snooze_button_->SetAccessibleName(l10n_util::GetStringUTF16(
-        IDS_MESSAGE_NOTIFICATION_SETTINGS_BUTTON_ACCESSIBLE_NAME));
+        IDS_MESSAGE_CENTER_NOTIFICATION_SNOOZE_BUTTON_TOOLTIP));
     snooze_button_->SetTooltipText(l10n_util::GetStringUTF16(
-        IDS_MESSAGE_NOTIFICATION_SETTINGS_BUTTON_ACCESSIBLE_NAME));
+        IDS_MESSAGE_CENTER_NOTIFICATION_SNOOZE_BUTTON_TOOLTIP));
     snooze_button_->SetBackground(
         views::CreateSolidBackground(SK_ColorTRANSPARENT));
 
diff --git a/ui/ozone/demo/vulkan_renderer.cc b/ui/ozone/demo/vulkan_renderer.cc
index 945937f..2650398f 100644
--- a/ui/ozone/demo/vulkan_renderer.cc
+++ b/ui/ozone/demo/vulkan_renderer.cc
@@ -223,8 +223,8 @@
       /* .color = */ {/* .float32 = */ {.5f, 1.f - NextFraction(), .5f, 1.f}}};
 
   gpu::VulkanSwapChain* vulkan_swap_chain = vulkan_surface_->GetSwapChain();
-  const uint32_t image = vulkan_swap_chain->current_image();
   gpu::VulkanSwapChain::ScopedWrite scoped_write(vulkan_swap_chain);
+  const uint32_t image = scoped_write.image_index();
   {
     auto& framebuffer = framebuffers_[image];
     if (!framebuffer) {
@@ -305,7 +305,7 @@
     device_queue_->GetFenceHelper()->EnqueueSemaphoreCleanupForSubmittedWork(
         begin_semaphore);
   }
-  vulkan_swap_chain->SwapBuffers();
+  vulkan_surface_->SwapBuffers();
 
   PostRenderFrameTask();
 }
diff --git a/ui/ozone/platform/x11/DEPS b/ui/ozone/platform/x11/DEPS
index 8d59099..3b6346a6 100644
--- a/ui/ozone/platform/x11/DEPS
+++ b/ui/ozone/platform/x11/DEPS
@@ -1,3 +1,4 @@
 include_rules = [
   "+ui/base/x",
+  "+ui/base",
 ]
diff --git a/ui/ozone/platform/x11/x11_window_ozone.cc b/ui/ozone/platform/x11/x11_window_ozone.cc
index e9ce3e7..b02c487a9 100644
--- a/ui/ozone/platform/x11/x11_window_ozone.cc
+++ b/ui/ozone/platform/x11/x11_window_ozone.cc
@@ -4,36 +4,240 @@
 
 #include "ui/ozone/platform/x11/x11_window_ozone.h"
 
+#include <algorithm>
+#include <string>
+#include <vector>
+
 #include "base/bind.h"
+#include "base/strings/utf_string_conversions.h"
+#include "ui/base/platform_window_defaults.h"
+#include "ui/base/x/x11_util.h"
 #include "ui/events/event.h"
 #include "ui/events/event_utils.h"
 #include "ui/events/ozone/events_ozone.h"
 #include "ui/events/platform/x11/x11_event_source.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/x/x11.h"
+#include "ui/gfx/x/x11_atom_cache.h"
 #include "ui/ozone/platform/x11/x11_cursor_ozone.h"
 #include "ui/ozone/platform/x11/x11_window_manager_ozone.h"
 
 namespace ui {
 
+namespace {
+
+XID FindXEventTarget(const XEvent& xev) {
+  XID target = xev.xany.window;
+  if (xev.type == GenericEvent)
+    target = static_cast<XIDeviceEvent*>(xev.xcookie.data)->event;
+  return target;
+}
+
+}  // namespace
 X11WindowOzone::X11WindowOzone(X11WindowManagerOzone* window_manager,
                                PlatformWindowDelegate* delegate,
                                const gfx::Rect& bounds)
-    : X11WindowBase(delegate, bounds), window_manager_(window_manager) {
-  DCHECK(window_manager);
+    : window_manager_(window_manager),
+      delegate_(delegate),
+      xdisplay_(gfx::GetXDisplay()),
+      xroot_window_(DefaultRootWindow(xdisplay_)),
+      xwindow_(x11::None),
+      mapped_(false),
+      bounds_(bounds),
+      state_(ui::PlatformWindowState::PLATFORM_WINDOW_STATE_UNKNOWN) {
+  DCHECK(delegate_);
+  DCHECK(window_manager_);
+
+  Create();
+  pointer_barriers_.fill(x11::None);
+
   auto* event_source = X11EventSourceLibevent::GetInstance();
   if (event_source)
     event_source->AddXEventDispatcher(this);
 }
 
 X11WindowOzone::~X11WindowOzone() {
-  X11WindowOzone::PrepareForShutdown();
+  PrepareForShutdown();
+  UnconfineCursor();
+  Destroy();
 }
 
-void X11WindowOzone::PrepareForShutdown() {
-  auto* event_source = X11EventSourceLibevent::GetInstance();
-  if (event_source)
-    event_source->RemoveXEventDispatcher(this);
+void X11WindowOzone::Destroy() {
+  if (xwindow_ == x11::None)
+    return;
+
+  // Stop processing events.
+  XID xwindow = xwindow_;
+  XDisplay* xdisplay = xdisplay_;
+  SetXWindow(x11::None);
+
+  delegate_->OnClosed();
+  XDestroyWindow(xdisplay, xwindow);
+}
+
+void X11WindowOzone::Create() {
+  DCHECK(!bounds_.size().IsEmpty());
+  DCHECK_NE(xdisplay_, nullptr);
+
+  XID xwindow = CreateXWindow();
+  DCHECK(xwindow);
+
+  SetXWindow(xwindow);
+}
+
+XID X11WindowOzone::CreateXWindow() {
+  XID xwindow = x11::None;
+  XSetWindowAttributes swa;
+  memset(&swa, 0, sizeof(swa));
+  swa.background_pixmap = x11::None;
+  swa.bit_gravity = NorthWestGravity;
+  swa.override_redirect = UseTestConfigForPlatformWindows();
+  xwindow =
+      XCreateWindow(xdisplay_, xroot_window_, bounds_.x(), bounds_.y(),
+                    bounds_.width(), bounds_.height(),
+                    0,               // border width
+                    CopyFromParent,  // depth
+                    InputOutput,
+                    CopyFromParent,  // visual
+                    CWBackPixmap | CWBitGravity | CWOverrideRedirect, &swa);
+
+  // Setup XInput event mask.
+  long event_mask = ButtonPressMask | ButtonReleaseMask | FocusChangeMask |
+                    KeyPressMask | KeyReleaseMask | EnterWindowMask |
+                    LeaveWindowMask | ExposureMask | VisibilityChangeMask |
+                    StructureNotifyMask | PropertyChangeMask |
+                    PointerMotionMask;
+  xwindow_events_.reset(new ui::XScopedEventSelector(xwindow, event_mask));
+
+  // Setup XInput2 event mask.
+  unsigned char mask[XIMaskLen(XI_LASTEVENT)];
+  memset(mask, 0, sizeof(mask));
+
+  XISetMask(mask, XI_TouchBegin);
+  XISetMask(mask, XI_TouchUpdate);
+  XISetMask(mask, XI_TouchEnd);
+  XISetMask(mask, XI_ButtonPress);
+  XISetMask(mask, XI_ButtonRelease);
+  XISetMask(mask, XI_Motion);
+  XISetMask(mask, XI_KeyPress);
+  XISetMask(mask, XI_KeyRelease);
+  XISetMask(mask, XI_HierarchyChanged);
+
+  XIEventMask evmask;
+  evmask.deviceid = XIAllDevices;
+  evmask.mask_len = sizeof(mask);
+  evmask.mask = mask;
+  XISelectEvents(xdisplay_, xwindow, &evmask, 1);
+  XFlush(xdisplay_);
+
+  XAtom protocols[] = {gfx::GetAtom("WM_DELETE_WINDOW"),
+                       gfx::GetAtom("_NET_WM_PING")};
+  XSetWMProtocols(xdisplay_, xwindow, protocols, 2);
+
+  // We need a WM_CLIENT_MACHINE and WM_LOCALE_NAME value so we integrate with
+  // the desktop environment.
+  XSetWMProperties(xdisplay_, xwindow, NULL, NULL, NULL, 0, NULL, NULL, NULL);
+
+  // Likewise, the X server needs to know this window's pid so it knows which
+  // program to kill if the window hangs. XChangeProperty() expects "pid" to be
+  // long.
+  static_assert(sizeof(long) >= sizeof(pid_t),
+                "pid_t should not be larger than long");
+  long pid = getpid();
+  XChangeProperty(xdisplay_, xwindow, gfx::GetAtom("_NET_WM_PID"), XA_CARDINAL,
+                  32, PropModeReplace, reinterpret_cast<unsigned char*>(&pid),
+                  1);
+  // Before we map the window, set size hints. Otherwise, some window managers
+  // will ignore toplevel XMoveWindow commands.
+  XSizeHints size_hints;
+  size_hints.flags = PPosition | PWinGravity;
+  size_hints.x = bounds_.x();
+  size_hints.y = bounds_.y();
+  // Set StaticGravity so that the window position is not affected by the
+  // frame width when running with window manager.
+  size_hints.win_gravity = StaticGravity;
+  XSetWMNormalHints(xdisplay_, xwindow, &size_hints);
+
+  return xwindow;
+}
+
+void X11WindowOzone::Show() {
+  if (mapped_)
+    return;
+
+  XMapWindow(xdisplay_, xwindow_);
+  XFlush(xdisplay_);
+  mapped_ = true;
+}
+
+void X11WindowOzone::Hide() {
+  if (!mapped_)
+    return;
+
+  XWithdrawWindow(xdisplay_, xwindow_, 0);
+  mapped_ = false;
+}
+
+void X11WindowOzone::Close() {
+  Destroy();
+}
+
+void X11WindowOzone::SetBounds(const gfx::Rect& bounds) {
+  DCHECK(!bounds.size().IsEmpty());
+
+  if (xwindow_ != x11::None) {
+    XWindowChanges changes = {0};
+    unsigned value_mask = 0;
+
+    if (bounds_.size() != bounds.size()) {
+      changes.width = bounds.width();
+      changes.height = bounds.height();
+      value_mask |= CWHeight | CWWidth;
+    }
+
+    if (bounds_.origin() != bounds.origin()) {
+      changes.x = bounds.x();
+      changes.y = bounds.y();
+      value_mask |= CWX | CWY;
+    }
+
+    if (value_mask)
+      XConfigureWindow(xdisplay_, xwindow_, value_mask, &changes);
+  }
+
+  // Assume that the resize will go through as requested, which should be the
+  // case if we're running without a window manager.  If there's a window
+  // manager, it can modify or ignore the request, but (per ICCCM) we'll get a
+  // (possibly synthetic) ConfigureNotify about the actual size and correct
+  // |bounds_| later.
+  bounds_ = bounds;
+
+  // Even if the pixel bounds didn't change this call to the delegate should
+  // still happen. The device scale factor may have changed which effectively
+  // changes the bounds.
+  delegate_->OnBoundsChanged(bounds_);
+}
+
+gfx::Rect X11WindowOzone::GetBounds() {
+  return bounds_;
+}
+
+void X11WindowOzone::SetTitle(const base::string16& title) {
+  if (window_title_ == title)
+    return;
+  window_title_ = title;
+  std::string utf8str = base::UTF16ToUTF8(title);
+  XChangeProperty(xdisplay_, xwindow_, gfx::GetAtom("_NET_WM_NAME"),
+                  gfx::GetAtom("UTF8_STRING"), 8, PropModeReplace,
+                  reinterpret_cast<const unsigned char*>(utf8str.c_str()),
+                  utf8str.size());
+  XTextProperty xtp;
+  char* c_utf8_str = const_cast<char*>(utf8str.c_str());
+  if (Xutf8TextListToTextProperty(xdisplay_, &c_utf8_str, 1, XUTF8StringStyle,
+                                  &xtp) == x11::Success) {
+    XSetWMName(xdisplay_, xwindow_, &xtp);
+    XFree(xtp.value);
+  }
 }
 
 void X11WindowOzone::SetCapture() {
@@ -48,13 +252,107 @@
   return window_manager_->event_grabber() == this;
 }
 
+void X11WindowOzone::ToggleFullscreen() {
+  ui::SetWMSpecState(xwindow_, !IsFullscreen(),
+                     gfx::GetAtom("_NET_WM_STATE_FULLSCREEN"), x11::None);
+}
+
+void X11WindowOzone::Maximize() {
+  if (IsFullscreen())
+    ToggleFullscreen();
+
+  ui::SetWMSpecState(xwindow_, true,
+                     gfx::GetAtom("_NET_WM_STATE_MAXIMIZED_VERT"),
+                     gfx::GetAtom("_NET_WM_STATE_MAXIMIZED_HORZ"));
+}
+
+void X11WindowOzone::Minimize() {
+  XIconifyWindow(xdisplay_, xwindow_, 0);
+}
+
+void X11WindowOzone::Restore() {
+  if (IsFullscreen())
+    ToggleFullscreen();
+
+  if (IsMaximized()) {
+    ui::SetWMSpecState(xwindow_, false,
+                       gfx::GetAtom("_NET_WM_STATE_MAXIMIZED_VERT"),
+                       gfx::GetAtom("_NET_WM_STATE_MAXIMIZED_HORZ"));
+  }
+}
+
+PlatformWindowState X11WindowOzone::GetPlatformWindowState() const {
+  return state_;
+}
+
+void X11WindowOzone::MoveCursorTo(const gfx::Point& location) {
+  XWarpPointer(xdisplay_, x11::None, xroot_window_, 0, 0, 0, 0,
+               bounds_.x() + location.x(), bounds_.y() + location.y());
+}
+
+void X11WindowOzone::ConfineCursorToBounds(const gfx::Rect& bounds) {
+  UnconfineCursor();
+
+  if (bounds.IsEmpty())
+    return;
+
+  gfx::Rect barrier = bounds + bounds_.OffsetFromOrigin();
+
+  // Top horizontal barrier.
+  pointer_barriers_[0] = XFixesCreatePointerBarrier(
+      xdisplay_, xroot_window_, barrier.x(), barrier.y(), barrier.right(),
+      barrier.y(), BarrierPositiveY, 0, XIAllDevices);
+  // Bottom horizontal barrier.
+  pointer_barriers_[1] = XFixesCreatePointerBarrier(
+      xdisplay_, xroot_window_, barrier.x(), barrier.bottom(), barrier.right(),
+      barrier.bottom(), BarrierNegativeY, 0, XIAllDevices);
+  // Left vertical barrier.
+  pointer_barriers_[2] = XFixesCreatePointerBarrier(
+      xdisplay_, xroot_window_, barrier.x(), barrier.y(), barrier.x(),
+      barrier.bottom(), BarrierPositiveX, 0, XIAllDevices);
+  // Right vertical barrier.
+  pointer_barriers_[3] = XFixesCreatePointerBarrier(
+      xdisplay_, xroot_window_, barrier.right(), barrier.y(), barrier.right(),
+      barrier.bottom(), BarrierNegativeX, 0, XIAllDevices);
+
+  has_pointer_barriers_ = true;
+}
+
+void X11WindowOzone::SetRestoredBoundsInPixels(const gfx::Rect& bounds) {
+  // TODO(crbug.com/848131): Restore bounds on restart
+  NOTIMPLEMENTED_LOG_ONCE();
+}
+
+gfx::Rect X11WindowOzone::GetRestoredBoundsInPixels() const {
+  // TODO(crbug.com/848131): Restore bounds on restart
+  NOTIMPLEMENTED_LOG_ONCE();
+  return gfx::Rect();
+}
+
+void X11WindowOzone::UnconfineCursor() {
+  if (!has_pointer_barriers_)
+    return;
+
+  for (XID pointer_barrier : pointer_barriers_)
+    XFixesDestroyPointerBarrier(xdisplay_, pointer_barrier);
+  pointer_barriers_.fill(x11::None);
+
+  has_pointer_barriers_ = false;
+}
+
+void X11WindowOzone::PrepareForShutdown() {
+  auto* event_source = X11EventSourceLibevent::GetInstance();
+  if (event_source)
+    event_source->RemoveXEventDispatcher(this);
+}
+
 void X11WindowOzone::SetCursor(PlatformCursor cursor) {
   X11CursorOzone* cursor_ozone = static_cast<X11CursorOzone*>(cursor);
-  XDefineCursor(xdisplay(), xwindow(), cursor_ozone->xcursor());
+  XDefineCursor(xdisplay_, xwindow_, cursor_ozone->xcursor());
 }
 
 void X11WindowOzone::CheckCanDispatchNextPlatformEvent(XEvent* xev) {
-  handle_next_event_ = xwindow() == x11::None ? false : IsEventForXWindow(*xev);
+  handle_next_event_ = xwindow_ == x11::None ? false : IsEventForXWindow(*xev);
 }
 
 void X11WindowOzone::PlatformEventDispatchFinished() {
@@ -84,7 +382,7 @@
     // (eg. double click) are broken.
     DispatchEventFromNativeUiEvent(
         event, base::BindOnce(&PlatformWindowDelegate::DispatchEvent,
-                              base::Unretained(delegate())));
+                              base::Unretained(delegate_)));
     return POST_DISPATCH_STOP_PROPAGATION;
   }
 
@@ -99,7 +397,129 @@
 }
 
 void X11WindowOzone::OnLostCapture() {
-  delegate()->OnLostCapture();
+  delegate_->OnLostCapture();
+}
+
+// Private methods
+
+void X11WindowOzone::SetXWindow(XID xid) {
+  xwindow_ = xid;
+
+  // In spite of being defined in Xlib as `unsigned long`, XID (|xwindow_|'s
+  // type) is fixed at 32-bits (CARD32) in X11 Protocol, therefore can't be
+  // larger than 32 bits values on the wire (see https://crbug.com/607014 for
+  // more details). So, It's safe to use static_cast here.
+  widget_ = static_cast<gfx::AcceleratedWidget>(xwindow_);
+  if (widget_ != gfx::kNullAcceleratedWidget)
+    delegate_->OnAcceleratedWidgetAvailable(widget_);
+}
+
+bool X11WindowOzone::IsEventForXWindow(const XEvent& xev) const {
+  return xwindow_ != x11::None && FindXEventTarget(xev) == xwindow_;
+}
+
+void X11WindowOzone::ProcessXWindowEvent(XEvent* xev) {
+  switch (xev->type) {
+    case Expose: {
+      gfx::Rect damage_rect(xev->xexpose.x, xev->xexpose.y, xev->xexpose.width,
+                            xev->xexpose.height);
+      delegate_->OnDamageRect(damage_rect);
+      break;
+    }
+
+    case x11::FocusOut:
+      if (xev->xfocus.mode != NotifyGrab)
+        delegate_->OnLostCapture();
+      break;
+
+    case ConfigureNotify: {
+      DCHECK_EQ(xwindow_, xev->xconfigure.event);
+      DCHECK_EQ(xwindow_, xev->xconfigure.window);
+      // It's possible that the X window may be resized by some other means than
+      // from within aura (e.g. the X window manager can change the size). Make
+      // sure the root window size is maintained properly.
+      int translated_x_in_pixels = xev->xconfigure.x;
+      int translated_y_in_pixels = xev->xconfigure.y;
+      if (!xev->xconfigure.send_event && !xev->xconfigure.override_redirect) {
+        Window unused;
+        XTranslateCoordinates(xdisplay_, xwindow_, xroot_window_, 0, 0,
+                              &translated_x_in_pixels, &translated_y_in_pixels,
+                              &unused);
+      }
+      gfx::Rect bounds(translated_x_in_pixels, translated_y_in_pixels,
+                       xev->xconfigure.width, xev->xconfigure.height);
+      if (bounds_ != bounds) {
+        bounds_ = bounds;
+        delegate_->OnBoundsChanged(bounds_);
+      }
+      break;
+    }
+
+    case ClientMessage: {
+      Atom message = static_cast<Atom>(xev->xclient.data.l[0]);
+      if (message == gfx::GetAtom("WM_DELETE_WINDOW")) {
+        delegate_->OnCloseRequest();
+      } else if (message == gfx::GetAtom("_NET_WM_PING")) {
+        XEvent reply_event = *xev;
+        reply_event.xclient.window = xroot_window_;
+
+        XSendEvent(xdisplay_, reply_event.xclient.window, x11::False,
+                   SubstructureRedirectMask | SubstructureNotifyMask,
+                   &reply_event);
+        XFlush(xdisplay_);
+      }
+      break;
+    }
+    case PropertyNotify: {
+      XAtom changed_atom = xev->xproperty.atom;
+      if (changed_atom == gfx::GetAtom("_NET_WM_STATE"))
+        OnWMStateUpdated();
+      break;
+    }
+  }
+}
+
+void X11WindowOzone::OnWMStateUpdated() {
+  std::vector<XAtom> atom_list;
+  // Ignore the return value of ui::GetAtomArrayProperty(). Fluxbox removes the
+  // _NET_WM_STATE property when no _NET_WM_STATE atoms are set.
+  ui::GetAtomArrayProperty(xwindow_, "_NET_WM_STATE", &atom_list);
+
+  window_properties_.clear();
+  std::copy(atom_list.begin(), atom_list.end(),
+            inserter(window_properties_, window_properties_.begin()));
+
+  // Propagate the window state information to the client.
+  // Note that the order of checks is important here, because window can have
+  // several proprties at the same time.
+  ui::PlatformWindowState old_state = state_;
+  if (ui::HasWMSpecProperty(window_properties_,
+                            gfx::GetAtom("_NET_WM_STATE_HIDDEN"))) {
+    state_ = ui::PlatformWindowState::PLATFORM_WINDOW_STATE_MINIMIZED;
+  } else if (ui::HasWMSpecProperty(window_properties_,
+                                   gfx::GetAtom("_NET_WM_STATE_FULLSCREEN"))) {
+    state_ = ui::PlatformWindowState::PLATFORM_WINDOW_STATE_FULLSCREEN;
+  } else if (ui::HasWMSpecProperty(
+                 window_properties_,
+                 gfx::GetAtom("_NET_WM_STATE_MAXIMIZED_VERT")) &&
+             ui::HasWMSpecProperty(
+                 window_properties_,
+                 gfx::GetAtom("_NET_WM_STATE_MAXIMIZED_HORZ"))) {
+    state_ = ui::PlatformWindowState::PLATFORM_WINDOW_STATE_MAXIMIZED;
+  } else {
+    state_ = ui::PlatformWindowState::PLATFORM_WINDOW_STATE_NORMAL;
+  }
+
+  if (old_state != state_)
+    delegate_->OnWindowStateChanged(state_);
+}
+
+bool X11WindowOzone::IsMaximized() const {
+  return state_ == ui::PlatformWindowState::PLATFORM_WINDOW_STATE_MAXIMIZED;
+}
+
+bool X11WindowOzone::IsFullscreen() const {
+  return state_ == ui::PlatformWindowState::PLATFORM_WINDOW_STATE_FULLSCREEN;
 }
 
 }  // namespace ui
diff --git a/ui/ozone/platform/x11/x11_window_ozone.h b/ui/ozone/platform/x11/x11_window_ozone.h
index ab45885..6188274c 100644
--- a/ui/ozone/platform/x11/x11_window_ozone.h
+++ b/ui/ozone/platform/x11/x11_window_ozone.h
@@ -5,17 +5,24 @@
 #ifndef UI_OZONE_PLATFORM_X11_X11_WINDOW_OZONE_H_
 #define UI_OZONE_PLATFORM_X11_X11_WINDOW_OZONE_H_
 
+#include <array>
+#include <memory>
+
+#include "base/containers/flat_set.h"
 #include "base/macros.h"
+#include "ui/base/x/x11_window_event_manager.h"
 #include "ui/events/platform/platform_event_dispatcher.h"
 #include "ui/events/platform/x11/x11_event_source_libevent.h"
-#include "ui/platform_window/x11/x11_window_base.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/x/x11_types.h"
+#include "ui/platform_window/platform_window.h"
 
 namespace ui {
 
 class X11WindowManagerOzone;
 
 // PlatformWindow implementation for X11 Ozone. PlatformEvents are ui::Events.
-class X11WindowOzone : public X11WindowBase,
+class X11WindowOzone : public PlatformWindow,
                        public PlatformEventDispatcher,
                        public XEventDispatcher {
  public:
@@ -28,6 +35,21 @@
   void OnLostCapture();
 
   // PlatformWindow:
+  void Show() override;
+  void Hide() override;
+  void Close() override;
+  void SetBounds(const gfx::Rect& bounds) override;
+  gfx::Rect GetBounds() override;
+  void SetTitle(const base::string16& title) override;
+  void ToggleFullscreen() override;
+  void Maximize() override;
+  void Minimize() override;
+  void Restore() override;
+  PlatformWindowState GetPlatformWindowState() const override;
+  void MoveCursorTo(const gfx::Point& location) override;
+  void ConfineCursorToBounds(const gfx::Rect& bounds) override;
+  void SetRestoredBoundsInPixels(const gfx::Rect& bounds) override;
+  gfx::Rect GetRestoredBoundsInPixels() const override;
   void PrepareForShutdown() override;
   void SetCapture() override;
   void ReleaseCapture() override;
@@ -45,12 +67,39 @@
   bool CanDispatchEvent(const PlatformEvent& event) override;
   uint32_t DispatchEvent(const PlatformEvent& event) override;
 
+  void Create();
+  void Destroy();
+  XID CreateXWindow();
+  void SetXWindow(XID xwindow);
+  bool IsMaximized() const;
+  bool IsFullscreen() const;
+  bool IsEventForXWindow(const XEvent& xev) const;
+  void ProcessXWindowEvent(XEvent* xev);
+  void OnWMStateUpdated();
+  void UnconfineCursor();
+
   X11WindowManagerOzone* window_manager_;
+  PlatformWindowDelegate* const delegate_;
+  XDisplay* xdisplay_;
+  XID xroot_window_;
+  XID xwindow_;
+  bool mapped_;
+  gfx::Rect bounds_;
+  base::string16 window_title_;
+  ui::PlatformWindowState state_;
+  base::flat_set<XAtom> window_properties_;
 
   // Tells if this dispatcher can process next translated event based on a
   // previous check in ::CheckCanDispatchNextPlatformEvent based on a XID
   // target.
   bool handle_next_event_ = false;
+  std::unique_ptr<ui::XScopedEventSelector> xwindow_events_;
+
+  // Keep track of barriers to confine cursor.
+  bool has_pointer_barriers_ = false;
+  std::array<XID, 4> pointer_barriers_;
+
+  gfx::AcceleratedWidget widget_ = gfx::kNullAcceleratedWidget;
 
   DISALLOW_COPY_AND_ASSIGN(X11WindowOzone);
 };
diff --git a/ui/strings/ui_strings.grd b/ui/strings/ui_strings.grd
index 703cf1c2..f6d9c56 100644
--- a/ui/strings/ui_strings.grd
+++ b/ui/strings/ui_strings.grd
@@ -758,6 +758,9 @@
       <message name="IDS_MESSAGE_CENTER_CLOSE_NOTIFICATION_BUTTON_TOOLTIP" desc="The tooltip text for the close button in a notification.">
         Close
       </message>
+      <message name="IDS_MESSAGE_CENTER_NOTIFICATION_SNOOZE_BUTTON_TOOLTIP" desc="The tooltip and spoken feedback text for the snooze button in a notification. Usually 'button' is suffixed to this text automatically during spoken feedback.">
+        Snooze
+      </message>
       <message name="IDS_MESSAGE_NOTIFICATION_SETTINGS_BUTTON_ACCESSIBLE_NAME" desc="The spoken feedback text for the settings button in a notification. Usually 'button' is suffixed to this text automatically.">
         Notification settings
       </message>
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index a146912..864dac20 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -201,8 +201,11 @@
     "layout/flex_layout.h",
     "layout/flex_layout_types.h",
     "layout/grid_layout.h",
+    "layout/interpolating_layout_manager.h",
     "layout/layout_manager.h",
+    "layout/layout_manager_base.h",
     "layout/layout_provider.h",
+    "layout/layout_types.h",
     "masked_targeter_delegate.h",
     "metadata/metadata_cache.h",
     "metadata/metadata_header_macros.h",
@@ -405,8 +408,11 @@
     "layout/flex_layout_types.cc",
     "layout/flex_layout_types_internal.cc",
     "layout/grid_layout.cc",
+    "layout/interpolating_layout_manager.cc",
     "layout/layout_manager.cc",
+    "layout/layout_manager_base.cc",
     "layout/layout_provider.cc",
+    "layout/layout_types.cc",
     "masked_targeter_delegate.cc",
     "metadata/metadata_cache.cc",
     "metadata/metadata_types.cc",
@@ -1032,6 +1038,8 @@
     "layout/fill_layout_unittest.cc",
     "layout/flex_layout_unittest.cc",
     "layout/grid_layout_unittest.cc",
+    "layout/interpolating_layout_manager_unittest.cc",
+    "layout/layout_manager_base_unittest.cc",
     "metadata/metadata_unittest.cc",
     "metadata/type_conversion_unittest.cc",
     "paint_info_unittest.cc",
diff --git a/ui/views/layout/flex_layout_types.cc b/ui/views/layout/flex_layout_types.cc
index 7100e688..6950570 100644
--- a/ui/views/layout/flex_layout_types.cc
+++ b/ui/views/layout/flex_layout_types.cc
@@ -9,7 +9,6 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "base/strings/stringprintf.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/views/view.h"
 
@@ -17,12 +16,6 @@
 
 namespace {
 
-std::string OptionalToString(const base::Optional<int>& opt) {
-  if (!opt.has_value())
-    return "_";
-  return base::StringPrintf("%d", opt.value());
-}
-
 // Default Flex Rules ----------------------------------------------------------
 
 // Interpolates a size between minimum, preferred size, and upper bound based on
@@ -97,7 +90,9 @@
   // Note that this is an adjustment made for practical considerations, and may
   // not be "correct" in some absolute sense. Let's revisit at some point.
   const int preferred_height =
-      std::max(preferred.height(), view->GetHeightForWidth(width));
+      width >= preferred.width()
+          ? preferred.height()
+          : std::max(preferred.height(), view->GetHeightForWidth(width));
 
   if (!maximum_size.height()) {
     // Not having a maximum size is different from having a large available
@@ -120,44 +115,6 @@
 
 }  // namespace
 
-// SizeBounds ------------------------------------------------------------------
-
-SizeBounds::SizeBounds() = default;
-
-SizeBounds::SizeBounds(const base::Optional<int>& width,
-                       const base::Optional<int>& height)
-    : width_(width), height_(height) {}
-
-SizeBounds::SizeBounds(const SizeBounds& other)
-    : width_(other.width()), height_(other.height()) {}
-
-SizeBounds::SizeBounds(const gfx::Size& other)
-    : width_(other.width()), height_(other.height()) {}
-
-void SizeBounds::Enlarge(int width, int height) {
-  if (width_)
-    width_ = std::max(0, *width_ + width);
-  if (height_)
-    height_ = std::max(0, *height_ + height);
-}
-
-bool SizeBounds::operator==(const SizeBounds& other) const {
-  return width_ == other.width_ && height_ == other.height_;
-}
-
-bool SizeBounds::operator!=(const SizeBounds& other) const {
-  return !(*this == other);
-}
-
-bool SizeBounds::operator<(const SizeBounds& other) const {
-  return std::tie(height_, width_) < std::tie(other.height_, other.width_);
-}
-
-std::string SizeBounds::ToString() const {
-  return base::StringPrintf("%s x %s", OptionalToString(width()).c_str(),
-                            OptionalToString(height()).c_str());
-}
-
 // FlexSpecification -----------------------------------------------------------
 
 FlexSpecification::FlexSpecification() : rule_(GetDefaultFlexRule()) {}
@@ -193,4 +150,112 @@
   return FlexSpecification(rule_, order, weight_);
 }
 
+// Inset1D ---------------------------------------------------------------------
+
+void Inset1D::SetInsets(int leading, int trailing) {
+  leading_ = leading;
+  trailing_ = trailing;
+}
+
+void Inset1D::Expand(int leading, int trailing) {
+  leading_ += leading;
+  trailing_ += trailing;
+}
+
+bool Inset1D::operator==(const Inset1D& other) const {
+  return leading_ == other.leading_ && trailing_ == other.trailing_;
+}
+
+bool Inset1D::operator!=(const Inset1D& other) const {
+  return !(*this == other);
+}
+
+bool Inset1D::operator<(const Inset1D& other) const {
+  return std::tie(leading_, trailing_) <
+         std::tie(other.leading_, other.trailing_);
+}
+
+std::string Inset1D::ToString() const {
+  return base::StringPrintf("%d, %d", leading(), trailing());
+}
+
+// Span ------------------------------------------------------------------------
+
+void Span::SetSpan(int start, int length) {
+  start_ = start;
+  length_ = std::max(0, length);
+}
+
+void Span::Expand(int leading, int trailing) {
+  const int end = this->end();
+  set_start(start_ - leading);
+  set_end(end + trailing);
+}
+
+void Span::Inset(int leading, int trailing) {
+  Expand(-leading, -trailing);
+}
+
+void Span::Inset(const Inset1D& insets) {
+  Inset(insets.leading(), insets.trailing());
+}
+
+void Span::Center(const Span& container, const Inset1D& margins) {
+  int remaining = container.length() - length();
+
+  // Case 1: no room for any margins. Just center the span in the container,
+  // with equal overflow on each side.
+  if (remaining <= 0) {
+    set_start(container.start() + std::ceil(remaining * 0.5f));
+    return;
+  }
+
+  // Case 2: room for only part of the margins.
+  if (margins.size() > remaining) {
+    float scale = float{remaining} / float{margins.size()};
+    set_start(container.start() + std::roundf(scale * margins.leading()));
+    return;
+  }
+
+  // Case 3: room for both span and margins. Center the whole unit.
+  remaining -= margins.size();
+  set_start(container.start() + remaining / 2 + margins.leading());
+}
+
+void Span::Align(const Span& container,
+                 LayoutAlignment alignment,
+                 const Inset1D& margins) {
+  switch (alignment) {
+    case LayoutAlignment::kStart:
+      set_start(container.start() + margins.leading());
+      break;
+    case LayoutAlignment::kEnd:
+      set_start(container.end() - (margins.trailing() + length()));
+      break;
+    case LayoutAlignment::kCenter:
+      Center(container, margins);
+      break;
+    case LayoutAlignment::kStretch:
+      SetSpan(container.start() + margins.leading(),
+              std::max(0, container.length() - margins.size()));
+      break;
+  }
+}
+
+bool Span::operator==(const Span& other) const {
+  return start_ == other.start_ && length_ == other.length_;
+}
+
+bool Span::operator!=(const Span& other) const {
+  return !(*this == other);
+}
+
+bool Span::operator<(const Span& other) const {
+  return std::tie(start_, length_) < std::tie(other.start_, other.length_);
+}
+
+std::string Span::ToString() const {
+  return base::StringPrintf("%d [%d]", start(), length());
+}
+
 }  // namespace views
diff --git a/ui/views/layout/flex_layout_types.h b/ui/views/layout/flex_layout_types.h
index f018d6ab..d74f11ad 100644
--- a/ui/views/layout/flex_layout_types.h
+++ b/ui/views/layout/flex_layout_types.h
@@ -5,11 +5,13 @@
 #ifndef UI_VIEWS_LAYOUT_FLEX_LAYOUT_TYPES_H_
 #define UI_VIEWS_LAYOUT_FLEX_LAYOUT_TYPES_H_
 
+#include <algorithm>
 #include <memory>
 #include <string>
 
 #include "base/callback.h"
 #include "base/optional.h"
+#include "ui/views/layout/layout_types.h"
 #include "ui/views/views_export.h"
 
 namespace gfx {
@@ -20,46 +22,9 @@
 
 class View;
 
-// Whether a layout is oriented horizontally or vertically.
-enum class LayoutOrientation {
-  kHorizontal,
-  kVertical,
-};
-
 // Describes how elements should be aligned within a layout.
 enum class LayoutAlignment { kStart, kCenter, kEnd, kStretch };
 
-// Stores an optional width and height upper bound. Used when calculating the
-// preferred size of a layout pursuant to a maximum available size.
-class VIEWS_EXPORT SizeBounds {
- public:
-  SizeBounds();
-  SizeBounds(const base::Optional<int>& width,
-             const base::Optional<int>& height);
-  explicit SizeBounds(const gfx::Size& size);
-  SizeBounds(const SizeBounds& other);
-
-  const base::Optional<int>& width() const { return width_; }
-  void set_width(const base::Optional<int>& width) { width_ = width; }
-
-  const base::Optional<int>& height() const { return height_; }
-  void set_height(const base::Optional<int>& height) { height_ = height; }
-
-  // Enlarges (or shrinks, if negative) each upper bound that is present by the
-  // specified amounts.
-  void Enlarge(int width, int height);
-
-  bool operator==(const SizeBounds& other) const;
-  bool operator!=(const SizeBounds& other) const;
-  bool operator<(const SizeBounds& other) const;
-
-  std::string ToString() const;
-
- private:
-  base::Optional<int> width_;
-  base::Optional<int> height_;
-};
-
 // Callback used to specify the size of a child view based on its size bounds.
 // Create your own custom rules, or use the Minimum|MaximumFlexSizeRule
 // constants below for common behaviors.
@@ -159,6 +124,85 @@
   int weight_ = 0;
 };
 
+// Represents insets in a single dimension.
+class Inset1D {
+ public:
+  constexpr Inset1D() = default;
+  constexpr explicit Inset1D(int all) : leading_(all), trailing_(all) {}
+  constexpr Inset1D(int leading, int trailing)
+      : leading_(leading), trailing_(trailing) {}
+
+  constexpr int leading() const { return leading_; }
+  void set_leading(int leading) { leading_ = leading; }
+
+  constexpr int trailing() const { return trailing_; }
+  void set_trailing(int trailing) { trailing_ = trailing; }
+
+  constexpr int size() const { return leading_ + trailing_; }
+
+  void SetInsets(int leading, int trailing);
+  void Expand(int delta_leading, int delta_trailing);
+
+  constexpr bool is_empty() const { return leading_ == 0 && trailing_ == 0; }
+  bool operator==(const Inset1D& other) const;
+  bool operator!=(const Inset1D& other) const;
+  bool operator<(const Inset1D& other) const;
+
+  std::string ToString() const;
+
+ private:
+  int leading_ = 0;
+  int trailing_ = 0;
+};
+
+// Represents a line segment in one dimension with a starting point and length.
+class Span {
+ public:
+  constexpr Span() = default;
+  constexpr Span(int start, int length) : start_(start), length_(length) {}
+
+  constexpr int start() const { return start_; }
+  void set_start(int start) { start_ = start; }
+
+  constexpr int length() const { return length_; }
+  void set_length(int length) { length_ = std::max(0, length); }
+
+  constexpr int end() const { return start_ + length_; }
+  void set_end(int end) { set_length(end - start_); }
+
+  void SetSpan(int start, int length);
+
+  // Expands the span by |leading| at the front (reducing the value of start()
+  // if |leading| is positive) and by |trailing| at the end (increasing the
+  // value of end() if |trailing| is positive).
+  void Expand(int leading, int trailing);
+
+  // Opposite of Expand(). Shrinks each end of the span by the specified amount.
+  void Inset(int leading, int trailing);
+  void Inset(const Inset1D& insets);
+
+  // Centers the span in another span, with optional margins.
+  // Overflow is handled gracefully.
+  void Center(const Span& container, const Inset1D& margins = Inset1D());
+
+  // Aligns the span in another span, with optional margins, using the specified
+  // alignment. Overflow is handled gracefully.
+  void Align(const Span& container,
+             LayoutAlignment alignment,
+             const Inset1D& margins = Inset1D());
+
+  constexpr bool is_empty() const { return length_ == 0; }
+  bool operator==(const Span& other) const;
+  bool operator!=(const Span& other) const;
+  bool operator<(const Span& other) const;
+
+  std::string ToString() const;
+
+ private:
+  int start_ = 0;
+  int length_ = 0;
+};
+
 }  // namespace views
 
 #endif  // UI_VIEWS_LAYOUT_FLEX_LAYOUT_TYPES_H_
diff --git a/ui/views/layout/flex_layout_types_internal.cc b/ui/views/layout/flex_layout_types_internal.cc
index aef1542f..bb104c4 100644
--- a/ui/views/layout/flex_layout_types_internal.cc
+++ b/ui/views/layout/flex_layout_types_internal.cc
@@ -27,114 +27,6 @@
 
 }  // namespace
 
-// Span ------------------------------------------------------------------------
-
-void Span::SetSpan(int start, int length) {
-  start_ = start;
-  length_ = std::max(0, length);
-}
-
-void Span::Expand(int leading, int trailing) {
-  const int end = this->end();
-  set_start(start_ - leading);
-  set_end(end + trailing);
-}
-
-void Span::Inset(int leading, int trailing) {
-  Expand(-leading, -trailing);
-}
-
-void Span::Inset(const Inset1D& insets) {
-  Inset(insets.leading(), insets.trailing());
-}
-
-void Span::Center(const Span& container, const Inset1D& margins) {
-  int remaining = container.length() - length();
-
-  // Case 1: no room for any margins. Just center the span in the container,
-  // with equal overflow on each side.
-  if (remaining <= 0) {
-    set_start(std::ceil(remaining * 0.5f));
-    return;
-  }
-
-  // Case 2: room for only part of the margins.
-  if (margins.size() > remaining) {
-    float scale = float{remaining} / float{margins.size()};
-    set_start(std::roundf(scale * margins.leading()));
-    return;
-  }
-
-  // Case 3: room for both span and margins. Center the whole unit.
-  remaining -= margins.size();
-  set_start(remaining / 2 + margins.leading());
-}
-
-void Span::Align(const Span& container,
-                 LayoutAlignment alignment,
-                 const Inset1D& margins) {
-  switch (alignment) {
-    case LayoutAlignment::kStart:
-      set_start(container.start() + margins.leading());
-      break;
-    case LayoutAlignment::kEnd:
-      set_start(container.end() - (margins.trailing() + length()));
-      break;
-    case LayoutAlignment::kCenter:
-      Center(container, margins);
-      break;
-    case LayoutAlignment::kStretch:
-      SetSpan(container.start() + margins.leading(),
-              std::max(0, container.length() - margins.size()));
-      break;
-  }
-}
-
-bool Span::operator==(const Span& other) const {
-  return start_ == other.start_ && length_ == other.length_;
-}
-
-bool Span::operator!=(const Span& other) const {
-  return !(*this == other);
-}
-
-bool Span::operator<(const Span& other) const {
-  return std::tie(start_, length_) < std::tie(other.start_, other.length_);
-}
-
-std::string Span::ToString() const {
-  return base::StringPrintf("%d [%d]", start(), length());
-}
-
-// Inset1D ---------------------------------------------------------------------
-
-void Inset1D::SetInsets(int leading, int trailing) {
-  leading_ = leading;
-  trailing_ = trailing;
-}
-
-void Inset1D::Expand(int delta_leading, int delta_trailing) {
-  leading_ += delta_leading;
-  trailing_ += delta_trailing;
-}
-
-bool Inset1D::operator==(const Inset1D& other) const {
-  return leading_ == other.leading_ && trailing_ == other.trailing_;
-}
-
-bool Inset1D::operator!=(const Inset1D& other) const {
-  return !(*this == other);
-}
-
-bool Inset1D::operator<(const Inset1D& other) const {
-  return std::tie(leading_, trailing_) <
-         std::tie(other.leading_, other.trailing_);
-}
-
-std::string Inset1D::ToString() const {
-  return base::StringPrintf("%d, %d", leading(), trailing());
-}
-
 // NormalizedPoint -------------------------------------------------------------
 
 void NormalizedPoint::SetPoint(int main, int cross) {
diff --git a/ui/views/layout/flex_layout_types_internal.h b/ui/views/layout/flex_layout_types_internal.h
index 80b0231f..7a44d1f 100644
--- a/ui/views/layout/flex_layout_types_internal.h
+++ b/ui/views/layout/flex_layout_types_internal.h
@@ -23,91 +23,6 @@
 
 namespace internal {
 
-// Represents insets in a single dimension.
-class Inset1D {
- public:
-  constexpr Inset1D() = default;
-  constexpr explicit Inset1D(int all) : leading_(all), trailing_(all) {}
-  constexpr Inset1D(int leading, int trailing)
-      : leading_(leading), trailing_(trailing) {}
-
-  constexpr int leading() const { return leading_; }
-  void set_leading(int leading) { leading_ = leading; }
-
-  constexpr int trailing() const { return trailing_; }
-  void set_trailing(int trailing) { trailing_ = trailing; }
-
-  constexpr int size() const { return leading_ + trailing_; }
-
-  void SetInsets(int leading, int trailing);
-  void Expand(int delta_leading, int delta_trailing);
-
-  constexpr bool is_empty() const { return leading_ == 0 && trailing_ == 0; }
-  bool operator==(const Inset1D& other) const;
-  bool operator!=(const Inset1D& other) const;
-  bool operator<(const Inset1D& other) const;
-
-  std::string ToString() const;
-
- private:
-  int leading_ = 0;
-  int trailing_ = 0;
-};
-
-// Represents a line segment in one dimension with a starting point and length.
-class Span {
- public:
-  constexpr Span() = default;
-  constexpr Span(int start, int length) : start_(start), length_(length) {}
-
-  constexpr int start() const { return start_; }
-  void set_start(int start) { start_ = start; }
-
-  constexpr int length() const { return length_; }
-  void set_length(int length) {
-    DCHECK_GE(length, 0);
-    length_ = length;
-  }
-
-  constexpr int end() const { return start_ + length_; }
-  void set_end(int end) {
-    DCHECK_GE(end, start_);
-    length_ = end - start_;
-  }
-
-  void SetSpan(int start, int length);
-
-  // Expands the span by |leading| at the front (reducing the value of start()
-  // if |leading| is positive) and by |trailing| at the end (increasing the
-  // value of end() if |trailing| is positive).
-  void Expand(int leading, int trailing);
-
-  // Opposite of Expand(). Shrinks each end of the span by the specified amount.
-  void Inset(int leading, int trailing);
-  void Inset(const Inset1D& insets);
-
-  // Centers the span in another span, with optional margins.
-  // Overflow is handled gracefully.
-  void Center(const Span& container, const Inset1D& margins = Inset1D());
-
-  // Aligns the span in another span, with optional margins, using the specified
-  // alignment. Overflow is handled gracefully.
-  void Align(const Span& container,
-             LayoutAlignment alignment,
-             const Inset1D& margins = Inset1D());
-
-  constexpr bool is_empty() const { return length_ == 0; }
-  bool operator==(const Span& other) const;
-  bool operator!=(const Span& other) const;
-  bool operator<(const Span& other) const;
-
-  std::string ToString() const;
-
- private:
-  int start_ = 0;
-  int length_ = 0;
-};
-
 // Represents a point in layout space - that is, a point on the main and cross
 // axes of the layout (regardless of whether it is vertically or horizontally
 // oriented.
diff --git a/ui/views/layout/interpolating_layout_manager.cc b/ui/views/layout/interpolating_layout_manager.cc
new file mode 100644
index 0000000..65f126e5
--- /dev/null
+++ b/ui/views/layout/interpolating_layout_manager.cc
@@ -0,0 +1,165 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/views/layout/interpolating_layout_manager.h"
+
+#include <memory>
+#include <utility>
+
+#include "ui/views/view.h"
+
+namespace views {
+
+namespace {
+
+using ChildLayout = LayoutManagerBase::ChildLayout;
+using ProposedLayout = LayoutManagerBase::ProposedLayout;
+
+int InterpolateValue(int first, int second) {
+  return (first + second) / 2;
+}
+
+gfx::Size InterpolateSize(const gfx::Size& first, const gfx::Size& second) {
+  return gfx::Size(InterpolateValue(first.width(), second.width()),
+                   InterpolateValue(first.height(), second.height()));
+}
+
+gfx::Rect InterpolateRect(const gfx::Rect& first, const gfx::Rect& second) {
+  return gfx::Rect(InterpolateValue(first.x(), second.x()),
+                   InterpolateValue(first.y(), second.y()),
+                   InterpolateValue(first.width(), second.width()),
+                   InterpolateValue(first.height(), second.height()));
+}
+
+// static
+ProposedLayout Interpolate(const ProposedLayout& left,
+                           const ProposedLayout& right) {
+  ProposedLayout layout;
+  const size_t num_children = left.child_layouts.size();
+  DCHECK_EQ(num_children, right.child_layouts.size());
+  layout.host_size = InterpolateSize(left.host_size, right.host_size);
+  for (size_t i = 0; i < num_children; ++i) {
+    const ChildLayout& left_child = left.child_layouts[i];
+    const ChildLayout& right_child = right.child_layouts[i];
+    DCHECK_EQ(left_child.child_view, right_child.child_view);
+    layout.child_layouts.emplace_back(
+        ChildLayout{left_child.child_view,
+                    InterpolateRect(left_child.bounds, right_child.bounds),
+                    left_child.visible && right_child.visible});
+  }
+  return layout;
+}
+
+}  // namespace
+
+InterpolatingLayoutManager::InterpolatingLayoutManager() {}
+InterpolatingLayoutManager::~InterpolatingLayoutManager() = default;
+
+InterpolatingLayoutManager& InterpolatingLayoutManager::SetOrientation(
+    LayoutOrientation orientation) {
+  if (orientation_ != orientation) {
+    orientation_ = orientation;
+    InvalidateLayout();
+  }
+  return *this;
+}
+
+void InterpolatingLayoutManager::AddLayoutInternal(
+    std::unique_ptr<LayoutManagerBase> engine,
+    const Span& interpolation_range) {
+  DCHECK(engine);
+
+  SyncStateTo(engine.get());
+  auto result = embedded_layouts_.emplace(
+      std::make_pair(interpolation_range, std::move(engine)));
+  DCHECK(result.second) << "Cannot replace existing layout manager for "
+                        << interpolation_range.ToString();
+
+#if DCHECK_IS_ON()
+  // Sanity checking to ensure interpolation ranges do not overlap (we can only
+  // interpolate between two layouts currently).
+  auto next = result.first;
+  ++next;
+  if (next != embedded_layouts_.end())
+    DCHECK_GE(next->first.start(), interpolation_range.end());
+  if (result.first != embedded_layouts_.begin()) {
+    auto prev = result.first;
+    --prev;
+    DCHECK_LE(prev->first.end(), interpolation_range.start());
+  }
+#endif  // DCHECK_IS_ON()
+}
+
+LayoutManagerBase::ProposedLayout InterpolatingLayoutManager::GetProposedLayout(
+    const SizeBounds& size_bounds) const {
+  // Make sure a default engine is set.
+  DCHECK(embedded_layouts_.find({0, 0}) != embedded_layouts_.end());
+
+  ProposedLayout layout;
+
+  const base::Optional<int> dimension =
+      orientation_ == LayoutOrientation::kHorizontal ? size_bounds.width()
+                                                     : size_bounds.height();
+
+  // Find the larger layout that overlaps the target size.
+  auto right_match = dimension ? embedded_layouts_.upper_bound({*dimension, 0})
+                               : embedded_layouts_.end();
+  DCHECK(right_match != embedded_layouts_.begin());
+  --right_match;
+  ProposedLayout right = right_match->second->GetProposedLayout(size_bounds);
+
+  // If the target size falls in an interpolation range, get the other layout.
+  if (dimension && right_match->first.end() > *dimension) {
+    DCHECK(right_match != embedded_layouts_.begin());
+    auto left_match = --(right_match);
+    ProposedLayout left = left_match->second->GetProposedLayout(size_bounds);
+    layout = Interpolate(left, right);
+  } else {
+    layout = std::move(right);
+  }
+
+  return layout;
+}
+
+void InterpolatingLayoutManager::InvalidateLayout() {
+  LayoutManagerBase::InvalidateLayout();
+  for (auto& embedded : embedded_layouts_)
+    embedded.second->InvalidateLayout();
+}
+
+void InterpolatingLayoutManager::SetChildViewIgnoredByLayout(View* child_view,
+                                                             bool ignored) {
+  LayoutManagerBase::SetChildViewIgnoredByLayout(child_view, ignored);
+  for (auto& embedded : embedded_layouts_)
+    embedded.second->SetChildViewIgnoredByLayout(child_view, ignored);
+}
+
+void InterpolatingLayoutManager::Installed(View* host_view) {
+  LayoutManagerBase::Installed(host_view);
+  for (auto& embedded : embedded_layouts_)
+    embedded.second->Installed(host_view);
+}
+
+void InterpolatingLayoutManager::ViewAdded(View* host_view, View* child_view) {
+  LayoutManagerBase::ViewAdded(host_view, child_view);
+  for (auto& embedded : embedded_layouts_)
+    embedded.second->ViewAdded(host_view, child_view);
+}
+
+void InterpolatingLayoutManager::ViewRemoved(View* host_view,
+                                             View* child_view) {
+  LayoutManagerBase::ViewRemoved(host_view, child_view);
+  for (auto& embedded : embedded_layouts_)
+    embedded.second->ViewRemoved(host_view, child_view);
+}
+
+void InterpolatingLayoutManager::ViewVisibilitySet(View* host,
+                                                   View* view,
+                                                   bool visible) {
+  LayoutManagerBase::ViewVisibilitySet(host, view, visible);
+  for (auto& embedded : embedded_layouts_)
+    embedded.second->ViewVisibilitySet(host, view, visible);
+}
+
+}  // namespace views
diff --git a/ui/views/layout/interpolating_layout_manager.h b/ui/views/layout/interpolating_layout_manager.h
new file mode 100644
index 0000000..fbecde0
--- /dev/null
+++ b/ui/views/layout/interpolating_layout_manager.h
@@ -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.
+
+#ifndef UI_VIEWS_LAYOUT_INTERPOLATING_LAYOUT_MANAGER_H_
+#define UI_VIEWS_LAYOUT_INTERPOLATING_LAYOUT_MANAGER_H_
+
+#include <map>
+#include <memory>
+#include <utility>
+
+#include "ui/views/layout/flex_layout_types.h"
+#include "ui/views/layout/layout_manager_base.h"
+
+namespace views {
+
+// Layout which interpolates between multiple embedded LayoutManagerBase
+// layouts.
+//
+// An InterpolatingLayoutManager has a default layout, which applies at the
+// smallest layout size along the layout's major axis (defined by |orientation|)
+// and additional layouts, which phase in at some larger size. If only the
+// default layout is set, the layout is functionally equivalent to the default
+// layout.
+//
+// An example:
+//
+//   InterpolatingLayoutManager* e =
+//       new InterpolatingLayoutManager(LayoutOrientation::kHorizontal);
+//   e->SetDefaultLayout(std::make_unique<CompactLayout>());
+//   e->AddLayout(std::make_unique<NormalLayout>(), 50, 50);
+//   e->AddLayout(std::make_unique<SpaciousLayout>(), 100, 150);
+//
+// Now as the view expands, the different layouts are used:
+//
+// 0              50            100            150
+// |   Compact    |    Normal    | Norm <~> Spa |  Spacious ->
+//
+// In the range from 100 to 150 (exclusive), an interpolation of the Normal and
+// Spacious layouts is used. When interpolation happens in this way, the
+// visibility of views is the conjunction of the visibilities in each layout, so
+// if either layout hides a view then the interpolated layout also hides it.
+// Since this can produce some unwanted visual results, we recommend making sure
+// that over the interpolation range, visibility matches up between the layouts
+// on either side.
+//
+// Note that behavior when interpolation ranges overlap is undefined, but will
+// be guaranteed to at least be the result of mixing two adjacent layouts that
+// fall over the range in a way that is not completely irrational.
+class VIEWS_EXPORT InterpolatingLayoutManager : public LayoutManagerBase {
+ public:
+  InterpolatingLayoutManager();
+  ~InterpolatingLayoutManager() override;
+
+  InterpolatingLayoutManager& SetOrientation(LayoutOrientation orientation);
+  LayoutOrientation orientation() const { return orientation_; }
+
+  // Sets the default layout, which takes effect at zero size on the layout's
+  // main axis, and continues to be active until the next layout phases in.
+  //
+  // This object retains ownership of the layout engine, but the method returns
+  // a typed raw pointer to the added layout engine.
+  template <class T>
+  T* SetDefaultLayout(std::unique_ptr<T> layout_manager) {
+    T* const temp = layout_manager.get();
+    AddLayoutInternal(std::move(layout_manager), Span());
+    return temp;
+  }
+
+  // Adds an additional layout which starts and finished phasing in at
+  // |start_interpolation| and |end_interpolation|, respectively. Currently,
+  // having more than one layout's interpolation range overlapping results in
+  // undefined behavior.
+  //
+  // This object retains ownership of the layout engine, but the method returns
+  // a typed raw pointer to the added layout engine.
+  template <class T>
+  T* AddLayout(std::unique_ptr<T> layout_manager,
+               const Span& interpolation_range) {
+    T* const temp = layout_manager.get();
+    AddLayoutInternal(std::move(layout_manager), interpolation_range);
+    return temp;
+  }
+
+  // CachingLayout:
+  void InvalidateLayout() override;
+  void SetChildViewIgnoredByLayout(View* child_view, bool ignored) override;
+  void Installed(View* host_view) override;
+  void ViewAdded(View* host_view, View* child_view) override;
+  void ViewRemoved(View* host_view, View* child_view) override;
+  void ViewVisibilitySet(View* host, View* view, bool visible) override;
+  ProposedLayout GetProposedLayout(
+      const SizeBounds& size_bounds) const override;
+
+ private:
+  void AddLayoutInternal(std::unique_ptr<LayoutManagerBase> layout,
+                         const Span& interpolation_range);
+
+  LayoutOrientation orientation_ = LayoutOrientation::kHorizontal;
+
+  // Maps from interpolation range to embedded layout.
+  std::map<Span, std::unique_ptr<LayoutManagerBase>> embedded_layouts_;
+
+  DISALLOW_COPY_AND_ASSIGN(InterpolatingLayoutManager);
+};
+
+}  // namespace views
+
+#endif  // UI_VIEWS_LAYOUT_INTERPOLATING_LAYOUT_MANAGER_H_
diff --git a/ui/views/layout/interpolating_layout_manager_unittest.cc b/ui/views/layout/interpolating_layout_manager_unittest.cc
new file mode 100644
index 0000000..169544efb
--- /dev/null
+++ b/ui/views/layout/interpolating_layout_manager_unittest.cc
@@ -0,0 +1,186 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/views/layout/interpolating_layout_manager.h"
+
+#include <memory>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/views/test/test_views.h"
+#include "ui/views/view.h"
+
+namespace views {
+
+namespace {
+
+class TestLayout : public LayoutManagerBase {
+ public:
+  int num_layouts_generated() const { return num_layouts_generated_; }
+
+  ProposedLayout GetProposedLayout(
+      const SizeBounds& size_bounds) const override {
+    ++num_layouts_generated_;
+    ProposedLayout layout;
+    layout.host_size = gfx::Size(10, 11);
+    for (auto it = host_view()->children().begin();
+         it != host_view()->children().end(); ++it) {
+      if (!IsChildIncludedInLayout(*it))
+        continue;
+      ChildLayout child_layout;
+      child_layout.child_view = *it;
+      child_layout.visible = true;
+      child_layout.bounds = gfx::Rect(1, 1, 1, 1);
+      layout.child_layouts.push_back(child_layout);
+    }
+    return layout;
+  }
+
+ private:
+  mutable int num_layouts_generated_ = 0;
+};
+
+}  // anonymous namespace
+
+class InterpolatingLayoutManagerTest : public testing::Test {
+ public:
+  void SetUp() override {
+    host_view_ = std::make_unique<View>();
+    layout_manager_ = host_view_->SetLayoutManager(
+        std::make_unique<InterpolatingLayoutManager>());
+  }
+
+  InterpolatingLayoutManager* layout_manager() { return layout_manager_; }
+  View* host_view() { return host_view_.get(); }
+
+ private:
+  InterpolatingLayoutManager* layout_manager_ = nullptr;
+  std::unique_ptr<View> host_view_;
+};
+
+TEST_F(InterpolatingLayoutManagerTest, SetDefaultLayout) {
+  TestLayout* const default_layout =
+      layout_manager()->SetDefaultLayout(std::make_unique<TestLayout>());
+  EXPECT_EQ(0, default_layout->num_layouts_generated());
+  layout_manager()->GetProposedLayout(SizeBounds());
+  EXPECT_EQ(1, default_layout->num_layouts_generated());
+}
+
+TEST_F(InterpolatingLayoutManagerTest, AddLayout_CheckZeroAndUnbounded) {
+  TestLayout* const default_layout =
+      layout_manager()->SetDefaultLayout(std::make_unique<TestLayout>());
+  TestLayout* const other_layout =
+      layout_manager()->AddLayout(std::make_unique<TestLayout>(), {5, 0});
+  EXPECT_EQ(0, default_layout->num_layouts_generated());
+  EXPECT_EQ(0, other_layout->num_layouts_generated());
+  layout_manager()->GetProposedLayout(SizeBounds());
+  EXPECT_EQ(0, default_layout->num_layouts_generated());
+  EXPECT_EQ(1, other_layout->num_layouts_generated());
+  layout_manager()->GetProposedLayout(SizeBounds(0, 0));
+  EXPECT_EQ(1, default_layout->num_layouts_generated());
+  EXPECT_EQ(1, other_layout->num_layouts_generated());
+}
+
+TEST_F(InterpolatingLayoutManagerTest, GetProposedLayout_HardBoundary) {
+  TestLayout* const default_layout =
+      layout_manager()->SetDefaultLayout(std::make_unique<TestLayout>());
+  TestLayout* const other_layout =
+      layout_manager()->AddLayout(std::make_unique<TestLayout>(), {5, 0});
+  EXPECT_EQ(0, default_layout->num_layouts_generated());
+  EXPECT_EQ(0, other_layout->num_layouts_generated());
+  layout_manager()->GetProposedLayout(SizeBounds(5, 2));
+  EXPECT_EQ(0, default_layout->num_layouts_generated());
+  EXPECT_EQ(1, other_layout->num_layouts_generated());
+  layout_manager()->GetProposedLayout(SizeBounds(4, 2));
+  EXPECT_EQ(1, default_layout->num_layouts_generated());
+  EXPECT_EQ(1, other_layout->num_layouts_generated());
+}
+
+TEST_F(InterpolatingLayoutManagerTest, GetProposedLayout_SoftBoudnary) {
+  TestLayout* const default_layout =
+      layout_manager()->SetDefaultLayout(std::make_unique<TestLayout>());
+  TestLayout* const other_layout =
+      layout_manager()->AddLayout(std::make_unique<TestLayout>(), {4, 2});
+  EXPECT_EQ(0, default_layout->num_layouts_generated());
+  EXPECT_EQ(0, other_layout->num_layouts_generated());
+  layout_manager()->GetProposedLayout(SizeBounds(5, 2));
+  EXPECT_EQ(1, default_layout->num_layouts_generated());
+  EXPECT_EQ(1, other_layout->num_layouts_generated());
+  layout_manager()->GetProposedLayout(SizeBounds(4, 2));
+  EXPECT_EQ(2, default_layout->num_layouts_generated());
+  EXPECT_EQ(1, other_layout->num_layouts_generated());
+  layout_manager()->GetProposedLayout(SizeBounds(6, 6));
+  EXPECT_EQ(2, default_layout->num_layouts_generated());
+  EXPECT_EQ(2, other_layout->num_layouts_generated());
+}
+
+TEST_F(InterpolatingLayoutManagerTest, GetProposedLayout_MultipleLayouts) {
+  TestLayout* const default_layout =
+      layout_manager()->SetDefaultLayout(std::make_unique<TestLayout>());
+  TestLayout* const other_layout1 =
+      layout_manager()->AddLayout(std::make_unique<TestLayout>(), {4, 2});
+  TestLayout* const other_layout2 =
+      layout_manager()->AddLayout(std::make_unique<TestLayout>(), {6, 2});
+  EXPECT_EQ(0, default_layout->num_layouts_generated());
+  EXPECT_EQ(0, other_layout1->num_layouts_generated());
+  EXPECT_EQ(0, other_layout2->num_layouts_generated());
+  layout_manager()->GetProposedLayout(SizeBounds(5, 2));
+  EXPECT_EQ(1, default_layout->num_layouts_generated());
+  EXPECT_EQ(1, other_layout1->num_layouts_generated());
+  EXPECT_EQ(0, other_layout2->num_layouts_generated());
+  layout_manager()->GetProposedLayout(SizeBounds(6, 3));
+  EXPECT_EQ(1, default_layout->num_layouts_generated());
+  EXPECT_EQ(2, other_layout1->num_layouts_generated());
+  EXPECT_EQ(0, other_layout2->num_layouts_generated());
+  layout_manager()->GetProposedLayout(SizeBounds(7, 6));
+  EXPECT_EQ(1, default_layout->num_layouts_generated());
+  EXPECT_EQ(3, other_layout1->num_layouts_generated());
+  EXPECT_EQ(1, other_layout2->num_layouts_generated());
+  layout_manager()->GetProposedLayout(SizeBounds(20, 3));
+  EXPECT_EQ(1, default_layout->num_layouts_generated());
+  EXPECT_EQ(3, other_layout1->num_layouts_generated());
+  EXPECT_EQ(2, other_layout2->num_layouts_generated());
+}
+
+TEST_F(InterpolatingLayoutManagerTest, InvalidateLayout) {
+  static const gfx::Size kLayoutSize(5, 5);
+
+  TestLayout* const default_layout =
+      layout_manager()->SetDefaultLayout(std::make_unique<TestLayout>());
+  TestLayout* const other_layout =
+      layout_manager()->AddLayout(std::make_unique<TestLayout>(), {4, 2});
+  host_view()->SetSize(kLayoutSize);
+  EXPECT_EQ(1, default_layout->num_layouts_generated());
+  EXPECT_EQ(1, other_layout->num_layouts_generated());
+  host_view()->Layout();
+  EXPECT_EQ(1, default_layout->num_layouts_generated());
+  EXPECT_EQ(1, other_layout->num_layouts_generated());
+  layout_manager()->InvalidateLayout();
+  host_view()->Layout();
+  EXPECT_EQ(2, default_layout->num_layouts_generated());
+  EXPECT_EQ(2, other_layout->num_layouts_generated());
+  host_view()->Layout();
+  EXPECT_EQ(2, default_layout->num_layouts_generated());
+  EXPECT_EQ(2, other_layout->num_layouts_generated());
+}
+
+TEST_F(InterpolatingLayoutManagerTest, SetOrientation) {
+  TestLayout* const default_layout =
+      layout_manager()->SetDefaultLayout(std::make_unique<TestLayout>());
+  TestLayout* const other_layout =
+      layout_manager()->AddLayout(std::make_unique<TestLayout>(), {4, 2});
+  layout_manager()->SetOrientation(LayoutOrientation::kVertical);
+  EXPECT_EQ(0, default_layout->num_layouts_generated());
+  EXPECT_EQ(0, other_layout->num_layouts_generated());
+  layout_manager()->GetProposedLayout(SizeBounds(2, 6));
+  EXPECT_EQ(0, default_layout->num_layouts_generated());
+  EXPECT_EQ(1, other_layout->num_layouts_generated());
+  layout_manager()->GetProposedLayout(SizeBounds(3, 5));
+  EXPECT_EQ(1, default_layout->num_layouts_generated());
+  EXPECT_EQ(2, other_layout->num_layouts_generated());
+  layout_manager()->GetProposedLayout(SizeBounds(10, 3));
+  EXPECT_EQ(2, default_layout->num_layouts_generated());
+  EXPECT_EQ(2, other_layout->num_layouts_generated());
+}
+
+}  // namespace views
diff --git a/ui/views/layout/layout_manager_base.cc b/ui/views/layout/layout_manager_base.cc
new file mode 100644
index 0000000..49e95ed0e
--- /dev/null
+++ b/ui/views/layout/layout_manager_base.cc
@@ -0,0 +1,166 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/views/layout/layout_manager_base.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "ui/views/view.h"
+
+namespace views {
+
+LayoutManagerBase::ProposedLayout::ProposedLayout() = default;
+LayoutManagerBase::ProposedLayout::ProposedLayout(const ProposedLayout& other) =
+    default;
+LayoutManagerBase::ProposedLayout::ProposedLayout(ProposedLayout&& other) =
+    default;
+LayoutManagerBase::ProposedLayout::~ProposedLayout() = default;
+LayoutManagerBase::ProposedLayout& LayoutManagerBase::ProposedLayout::operator=(
+    const ProposedLayout& other) = default;
+LayoutManagerBase::ProposedLayout& LayoutManagerBase::ProposedLayout::operator=(
+    ProposedLayout&& other) = default;
+
+LayoutManagerBase::~LayoutManagerBase() = default;
+
+gfx::Size LayoutManagerBase::GetPreferredSize(const View* host) const {
+  DCHECK_EQ(host_view_, host);
+  if (!preferred_size_)
+    preferred_size_ = GetProposedLayout(SizeBounds()).host_size;
+  return *preferred_size_;
+}
+
+gfx::Size LayoutManagerBase::GetMinimumSize(const View* host) const {
+  DCHECK_EQ(host_view_, host);
+  if (!minimum_size_)
+    minimum_size_ = GetProposedLayout(SizeBounds(0, 0)).host_size;
+  return *minimum_size_;
+}
+
+int LayoutManagerBase::GetPreferredHeightForWidth(const View* host,
+                                                  int width) const {
+  if (!last_height_for_width_ || last_height_for_width_->width() != width) {
+    const int height =
+        GetProposedLayout(SizeBounds(width, base::nullopt)).host_size.height();
+    last_height_for_width_ = gfx::Size(width, height);
+  }
+
+  return last_height_for_width_->height();
+}
+
+void LayoutManagerBase::Layout(View* host) {
+  DCHECK_EQ(host_view_, host);
+  const gfx::Size size = host->size();
+  if (!last_requested_size_ || *last_requested_size_ != size) {
+    last_requested_size_ = size;
+    last_layout_ = GetProposedLayout(SizeBounds(size));
+  }
+  ApplyLayout(last_layout_);
+}
+
+void LayoutManagerBase::InvalidateLayout() {
+  minimum_size_.reset();
+  preferred_size_.reset();
+  last_height_for_width_.reset();
+  last_requested_size_.reset();
+}
+
+void LayoutManagerBase::SetChildViewIgnoredByLayout(View* child_view,
+                                                    bool ignored) {
+  auto it = child_infos_.find(child_view);
+  DCHECK(it != child_infos_.end());
+  if (it->second.ignored == ignored)
+    return;
+
+  it->second.ignored = ignored;
+  InvalidateLayout();
+}
+
+bool LayoutManagerBase::IsChildViewIgnoredByLayout(
+    const View* child_view) const {
+  auto it = child_infos_.find(child_view);
+  DCHECK(it != child_infos_.end());
+  return it->second.ignored;
+}
+
+void LayoutManagerBase::Installed(View* host_view) {
+  DCHECK(host_view);
+  DCHECK(!host_view_);
+  DCHECK(child_infos_.empty());
+
+  host_view_ = host_view;
+  for (auto it = host_view->children().begin();
+       it != host_view->children().end(); ++it) {
+    child_infos_.emplace(*it, ChildInfo{(*it)->GetVisible(), false});
+  }
+}
+
+void LayoutManagerBase::ViewAdded(View* host, View* view) {
+  DCHECK_EQ(host_view_, host);
+  DCHECK(!base::Contains(child_infos_, view));
+  ChildInfo to_add{view->GetVisible(), false};
+  child_infos_.emplace(view, to_add);
+  if (to_add.can_be_visible)
+    InvalidateLayout();
+}
+
+void LayoutManagerBase::ViewRemoved(View* host, View* view) {
+  DCHECK_EQ(host_view_, host);
+  auto it = child_infos_.find(view);
+  DCHECK(it != child_infos_.end());
+  const bool removed_visible = it->second.can_be_visible && !it->second.ignored;
+  child_infos_.erase(it);
+  if (removed_visible)
+    InvalidateLayout();
+}
+
+void LayoutManagerBase::ViewVisibilitySet(View* host,
+                                          View* view,
+                                          bool visible) {
+  DCHECK_EQ(host_view_, host);
+  auto it = child_infos_.find(view);
+  DCHECK(it != child_infos_.end());
+  if (it->second.can_be_visible == visible)
+    return;
+
+  it->second.can_be_visible = visible;
+  if (!it->second.ignored)
+    InvalidateLayout();
+}
+
+LayoutManagerBase::LayoutManagerBase() = default;
+
+bool LayoutManagerBase::IsChildIncludedInLayout(const View* child) const {
+  const auto it = child_infos_.find(child);
+  DCHECK(it != child_infos_.end());
+  return !it->second.ignored && it->second.can_be_visible;
+}
+
+void LayoutManagerBase::ApplyLayout(const ProposedLayout& layout) {
+  for (auto& child_layout : layout.child_layouts) {
+    DCHECK_EQ(host_view_, child_layout.child_view->parent());
+
+    // Since we have a non-const reference to the parent here, we can safely use
+    // a non-const reference to the child.
+    View* const child_view = child_layout.child_view;
+    if (child_view->GetVisible() != child_layout.visible)
+      SetViewVisibility(child_view, child_layout.visible);
+    if (child_layout.visible)
+      child_view->SetBoundsRect(child_layout.bounds);
+  }
+}
+
+void LayoutManagerBase::SyncStateTo(LayoutManagerBase* other) const {
+  if (host_view_) {
+    other->Installed(host_view_);
+    for (View* child_view : host_view_->children()) {
+      const ChildInfo& child_info = child_infos_.find(child_view)->second;
+      other->SetChildViewIgnoredByLayout(child_view, child_info.ignored);
+      other->ViewVisibilitySet(host_view_, child_view,
+                               child_info.can_be_visible);
+    }
+  }
+}
+
+}  // namespace views
diff --git a/ui/views/layout/layout_manager_base.h b/ui/views/layout/layout_manager_base.h
new file mode 100644
index 0000000..48421d1
--- /dev/null
+++ b/ui/views/layout/layout_manager_base.h
@@ -0,0 +1,122 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_VIEWS_LAYOUT_LAYOUT_MANAGER_BASE_H_
+#define UI_VIEWS_LAYOUT_LAYOUT_MANAGER_BASE_H_
+
+#include <map>
+#include <memory>
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "base/optional.h"
+#include "ui/gfx/geometry/insets.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/views/layout/layout_manager.h"
+#include "ui/views/layout/layout_types.h"
+#include "ui/views/views_export.h"
+
+namespace views {
+
+class View;
+
+// Base class for layout managers that can do layout calculation separately
+// from layout application. Derived classes must implement GetProposedLayout().
+// Used in interpolating and animating layouts.
+class VIEWS_EXPORT LayoutManagerBase : public LayoutManager {
+ public:
+  // Represents layout information for a child view within a host being laid
+  // out.
+  struct VIEWS_EXPORT ChildLayout {
+    View* child_view = nullptr;
+    gfx::Rect bounds;
+    bool visible = false;
+  };
+
+  // Contains a full layout specification for the children of the host view.
+  struct VIEWS_EXPORT ProposedLayout {
+    ProposedLayout();
+    ~ProposedLayout();
+    ProposedLayout(const ProposedLayout& other);
+    ProposedLayout(ProposedLayout&& other);
+    ProposedLayout& operator=(const ProposedLayout& other);
+    ProposedLayout& operator=(ProposedLayout&& other);
+
+    // The size of the host view given the size bounds for this layout. If both
+    // dimensions of the size bounds are specified, this will be the same size.
+    gfx::Size host_size;
+
+    // Contains an entry for each child view included in the layout.
+    std::vector<ChildLayout> child_layouts;
+  };
+
+  ~LayoutManagerBase() override;
+
+  View* host_view() { return host_view_; }
+  const View* host_view() const { return host_view_; }
+
+  // Creates a proposed layout for the host view, including bounds and
+  // visibility for all children currently included in the layout, or returns a
+  // cached layout that was previously created.
+  virtual ProposedLayout GetProposedLayout(
+      const SizeBounds& size_bounds) const = 0;
+
+  // Excludes a specific view from the layout when doing layout calculations.
+  // Useful when a child view is meant to be displayed but has its size and
+  // position managed elsewhere in code. By default, all child views are
+  // included in the layout unless they are hidden.
+  virtual void SetChildViewIgnoredByLayout(View* child_view, bool ignored);
+  virtual bool IsChildViewIgnoredByLayout(const View* child_view) const;
+
+  // LayoutManager:
+  gfx::Size GetPreferredSize(const View* host) const final;
+  gfx::Size GetMinimumSize(const View* host) const final;
+  int GetPreferredHeightForWidth(const View* host, int width) const final;
+  void Layout(View* host) final;
+  void InvalidateLayout() override;
+  void Installed(View* host) override;
+  void ViewAdded(View* host, View* view) override;
+  void ViewRemoved(View* host, View* view) override;
+  void ViewVisibilitySet(View* host, View* view, bool visible) override;
+
+ protected:
+  LayoutManagerBase();
+
+  bool IsChildIncludedInLayout(const View* child) const;
+
+  // Applies |layout| to the children of the host view.
+  void ApplyLayout(const ProposedLayout& layout);
+
+  // Can be used by derived classes to ensure that state is correctly
+  // transferred to child LayoutManagerBase instances in a composite layout
+  // (interpolating or animating layouts, etc.)
+  void SyncStateTo(LayoutManagerBase* other) const;
+
+ private:
+  // Holds bookkeeping data used to determine inclusion of children in the
+  // layout.
+  struct ChildInfo {
+    bool can_be_visible = true;
+    bool ignored = false;
+  };
+
+  View* host_view_ = nullptr;
+  std::map<const View*, ChildInfo> child_infos_;
+
+  // Do some really simple caching because layout generation can cost as much
+  // as 1ms or more for complex views.
+  mutable base::Optional<gfx::Size> minimum_size_;
+  mutable base::Optional<gfx::Size> preferred_size_;
+  mutable base::Optional<gfx::Size> last_height_for_width_;
+  mutable base::Optional<gfx::Size> last_requested_size_;
+  mutable ProposedLayout last_layout_;
+
+  DISALLOW_COPY_AND_ASSIGN(LayoutManagerBase);
+};
+
+}  // namespace views
+
+#endif  // UI_VIEWS_LAYOUT_LAYOUT_MANAGER_BASE_H_
diff --git a/ui/views/layout/layout_manager_base_unittest.cc b/ui/views/layout/layout_manager_base_unittest.cc
new file mode 100644
index 0000000..4d373e7
--- /dev/null
+++ b/ui/views/layout/layout_manager_base_unittest.cc
@@ -0,0 +1,479 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/views/layout/layout_manager_base.h"
+
+#include <algorithm>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/views/test/test_layout_manager.h"
+#include "ui/views/test/test_views.h"
+#include "ui/views/view.h"
+
+namespace views {
+
+// Test LayoutManagerBase-specific functionality:
+
+namespace {
+
+constexpr gfx::Size kMinimumSize(40, 50);
+constexpr gfx::Size kPreferredSize(100, 90);
+
+// Dummy class that minimally implements LayoutManagerBase for basic
+// functionality testing.
+class TestLayoutManagerBase : public LayoutManagerBase {
+ public:
+  std::vector<const View*> GetIncludedChildViews() const {
+    std::vector<const View*> included;
+    std::copy_if(host_view()->children().begin(), host_view()->children().end(),
+                 std::back_inserter(included), [=](const View* child) {
+                   return IsChildIncludedInLayout(child);
+                 });
+    return included;
+  }
+
+  // LayoutManagerBase:
+  ProposedLayout GetProposedLayout(
+      const SizeBounds& size_bounds) const override {
+    ProposedLayout layout;
+    layout.host_size.set_width(
+        std::max(kMinimumSize.width(),
+                 size_bounds.width().value_or(kPreferredSize.width())));
+    layout.host_size.set_height(
+        std::max(kMinimumSize.height(),
+                 size_bounds.height().value_or(kPreferredSize.height())));
+    return layout;
+  }
+};
+
+void ExpectSameViews(const std::vector<const View*>& expected,
+                     const std::vector<const View*>& actual) {
+  EXPECT_EQ(expected.size(), actual.size());
+  for (size_t i = 0; i < expected.size(); ++i) {
+    EXPECT_EQ(expected[i], actual[i]);
+  }
+}
+
+}  // namespace
+
+TEST(LayoutManagerBaseTest, GetMinimumSize) {
+  TestLayoutManagerBase layout;
+  EXPECT_EQ(kMinimumSize, layout.GetMinimumSize(nullptr));
+}
+
+TEST(LayoutManagerBaseTest, GetPreferredSize) {
+  TestLayoutManagerBase layout;
+  EXPECT_EQ(kPreferredSize, layout.GetPreferredSize(nullptr));
+}
+
+TEST(LayoutManagerBaseTest, GetPreferredHeightForWidth) {
+  constexpr int kWidth = 45;
+  TestLayoutManagerBase layout;
+  EXPECT_EQ(kPreferredSize.height(),
+            layout.GetPreferredHeightForWidth(nullptr, kWidth));
+}
+
+TEST(LayoutManagerBaseTest, Installed) {
+  TestLayoutManagerBase layout;
+  EXPECT_EQ(nullptr, layout.host_view());
+
+  View view;
+  layout.Installed(&view);
+  EXPECT_EQ(&view, layout.host_view());
+}
+
+TEST(LayoutManagerBaseTest, SetChildIncludedInLayout) {
+  View view;
+  View* const child1 = view.AddChildView(std::make_unique<View>());
+  View* const child2 = view.AddChildView(std::make_unique<View>());
+  View* const child3 = view.AddChildView(std::make_unique<View>());
+
+  TestLayoutManagerBase layout;
+  layout.Installed(&view);
+
+  // All views should be present.
+  ExpectSameViews({child1, child2, child3}, layout.GetIncludedChildViews());
+
+  // Remove one.
+  layout.SetChildViewIgnoredByLayout(child2, true);
+  ExpectSameViews({child1, child3}, layout.GetIncludedChildViews());
+
+  // Remove another.
+  layout.SetChildViewIgnoredByLayout(child1, true);
+  ExpectSameViews({child3}, layout.GetIncludedChildViews());
+
+  // Removing it again should have no effect.
+  layout.SetChildViewIgnoredByLayout(child1, true);
+  ExpectSameViews({child3}, layout.GetIncludedChildViews());
+
+  // Add one back.
+  layout.SetChildViewIgnoredByLayout(child1, false);
+  ExpectSameViews({child1, child3}, layout.GetIncludedChildViews());
+
+  // Adding it back again should have no effect.
+  layout.SetChildViewIgnoredByLayout(child1, false);
+  ExpectSameViews({child1, child3}, layout.GetIncludedChildViews());
+
+  // Add the other view back.
+  layout.SetChildViewIgnoredByLayout(child2, false);
+  ExpectSameViews({child1, child2, child3}, layout.GetIncludedChildViews());
+}
+
+// Test LayoutManager functionality of LayoutManagerBase:
+
+namespace {
+
+constexpr int kChildViewPadding = 5;
+constexpr gfx::Size kSquarishSize(10, 11);
+constexpr gfx::Size kLongSize(20, 8);
+constexpr gfx::Size kTallSize(4, 22);
+constexpr gfx::Size kLargeSize(30, 28);
+
+// This layout layout lays out included child views in the upper-left of the
+// host view with kChildViewPadding around them. Views that will not fit are
+// made invisible. Child views are expected to overlap as they all have the
+// same top-left corner.
+class MockLayoutManagerBase : public LayoutManagerBase {
+ public:
+  int num_invalidations() const { return num_invalidations_; }
+  int num_layouts_generated() const { return num_layouts_generated_; }
+
+  // LayoutManagerBase:
+  ProposedLayout GetProposedLayout(
+      const SizeBounds& size_bounds) const override {
+    ProposedLayout layout;
+    layout.host_size = {kChildViewPadding, kChildViewPadding};
+    for (auto it = host_view()->children().begin();
+         it != host_view()->children().end(); ++it) {
+      if (!IsChildIncludedInLayout(*it))
+        continue;
+      const gfx::Size preferred_size = (*it)->GetPreferredSize();
+      bool visible = false;
+      gfx::Rect bounds;
+      const int required_width = preferred_size.width() + 2 * kChildViewPadding;
+      const int required_height =
+          preferred_size.height() + 2 * kChildViewPadding;
+      if ((!size_bounds.width() || required_width <= *size_bounds.width()) &&
+          (!size_bounds.height() || required_height <= *size_bounds.height())) {
+        visible = true;
+        bounds = gfx::Rect(kChildViewPadding, kChildViewPadding,
+                           preferred_size.width(), preferred_size.height());
+        layout.host_size.set_width(std::max(
+            layout.host_size.width(), bounds.right() + kChildViewPadding));
+        layout.host_size.set_height(std::max(
+            layout.host_size.height(), bounds.bottom() + kChildViewPadding));
+      }
+      layout.child_layouts.push_back({*it, bounds, visible});
+    }
+    ++num_layouts_generated_;
+    return layout;
+  }
+
+  void InvalidateLayout() override {
+    LayoutManagerBase::InvalidateLayout();
+    ++num_invalidations_;
+  }
+
+  using LayoutManagerBase::ApplyLayout;
+
+ private:
+  mutable int num_layouts_generated_ = 0;
+  mutable int num_invalidations_ = 0;
+};
+
+// Base for tests that evaluate the LayoutManager functionality of
+// LayoutManagerBase (rather than the LayoutManagerBase-specific behavior).
+class LayoutManagerBaseManagerTest : public testing::Test {
+ public:
+  void SetUp() override {
+    host_view_ = std::make_unique<View>();
+    layout_manager_ =
+        host_view_->SetLayoutManager(std::make_unique<MockLayoutManagerBase>());
+  }
+
+  View* AddChildView(gfx::Size preferred_size) {
+    auto child = std::make_unique<StaticSizedView>(preferred_size);
+    return host_view_->AddChildView(std::move(child));
+  }
+
+  View* host_view() { return host_view_.get(); }
+  MockLayoutManagerBase* layout_manager() { return layout_manager_; }
+  View* child(int index) { return host_view_->children().at(index); }
+
+ private:
+  std::unique_ptr<View> host_view_;
+  MockLayoutManagerBase* layout_manager_;
+};
+
+}  // namespace
+
+TEST_F(LayoutManagerBaseManagerTest, ApplyLayout) {
+  AddChildView(gfx::Size());
+  AddChildView(gfx::Size());
+  AddChildView(gfx::Size());
+
+  // We don't want to set the size of the host view because it will trigger a
+  // superfluous layout, so we'll just keep the old size and make sure it
+  // doesn't change.
+  const gfx::Size old_size = host_view()->size();
+
+  LayoutManagerBase::ProposedLayout layout;
+  // This should be ignored.
+  layout.host_size = {123, 456};
+
+  // Set the child visibility and bounds.
+  constexpr gfx::Rect kChild1Bounds(3, 4, 10, 15);
+  constexpr gfx::Rect kChild3Bounds(20, 21, 12, 14);
+  layout.child_layouts.push_back(
+      LayoutManagerBase::ChildLayout{child(0), kChild1Bounds, true});
+  layout.child_layouts.push_back(
+      LayoutManagerBase::ChildLayout{child(1), gfx::Rect(), false});
+  layout.child_layouts.push_back(
+      LayoutManagerBase::ChildLayout{child(2), kChild3Bounds, true});
+
+  layout_manager()->ApplyLayout(layout);
+
+  EXPECT_TRUE(child(0)->GetVisible());
+  EXPECT_EQ(kChild1Bounds, child(0)->bounds());
+  EXPECT_FALSE(child(1)->GetVisible());
+  EXPECT_TRUE(child(2)->GetVisible());
+  EXPECT_EQ(kChild3Bounds, child(2)->bounds());
+  EXPECT_EQ(old_size, host_view()->size());
+}
+
+TEST_F(LayoutManagerBaseManagerTest, ApplyLayout_SkipsOmittedViews) {
+  AddChildView(gfx::Size());
+  AddChildView(gfx::Size());
+  AddChildView(gfx::Size());
+
+  LayoutManagerBase::ProposedLayout layout;
+  // Set the child visibility and bounds.
+  constexpr gfx::Rect kChild1Bounds(3, 4, 10, 15);
+  constexpr gfx::Rect kChild2Bounds(1, 2, 3, 4);
+  layout.child_layouts.push_back(
+      LayoutManagerBase::ChildLayout{child(0), kChild1Bounds, true});
+  layout.child_layouts.push_back(
+      LayoutManagerBase::ChildLayout{child(2), gfx::Rect(), false});
+
+  // We'll set the second child separately.
+  child(1)->SetVisible(true);
+  child(1)->SetBoundsRect(kChild2Bounds);
+
+  layout_manager()->ApplyLayout(layout);
+
+  EXPECT_TRUE(child(0)->GetVisible());
+  EXPECT_EQ(kChild1Bounds, child(0)->bounds());
+  EXPECT_TRUE(child(1)->GetVisible());
+  EXPECT_EQ(kChild2Bounds, child(1)->bounds());
+  EXPECT_FALSE(child(2)->GetVisible());
+}
+
+TEST_F(LayoutManagerBaseManagerTest, Install) {
+  EXPECT_EQ(host_view(), layout_manager()->host_view());
+}
+
+TEST_F(LayoutManagerBaseManagerTest, GetMinimumSize) {
+  AddChildView(kSquarishSize);
+  AddChildView(kLongSize);
+  AddChildView(kTallSize);
+  EXPECT_EQ(gfx::Size(kChildViewPadding, kChildViewPadding),
+            host_view()->GetMinimumSize());
+}
+
+TEST_F(LayoutManagerBaseManagerTest, GetPreferredSize) {
+  AddChildView(kSquarishSize);
+  AddChildView(kLongSize);
+  AddChildView(kTallSize);
+  const gfx::Size expected(kLongSize.width() + 2 * kChildViewPadding,
+                           kTallSize.height() + 2 * kChildViewPadding);
+  EXPECT_EQ(expected, host_view()->GetPreferredSize());
+}
+
+TEST_F(LayoutManagerBaseManagerTest, GetPreferredHeightForWidth) {
+  AddChildView(kSquarishSize);
+  AddChildView(kLargeSize);
+  const int expected = kSquarishSize.height() + 2 * kChildViewPadding;
+  EXPECT_EQ(expected,
+            layout_manager()->GetPreferredHeightForWidth(host_view(), 20));
+  EXPECT_EQ(1, layout_manager()->num_layouts_generated());
+  layout_manager()->GetPreferredHeightForWidth(host_view(), 20);
+  EXPECT_EQ(1, layout_manager()->num_layouts_generated());
+  layout_manager()->GetPreferredHeightForWidth(host_view(), 25);
+  EXPECT_EQ(2, layout_manager()->num_layouts_generated());
+}
+
+TEST_F(LayoutManagerBaseManagerTest, InvalidateLayout) {
+  // Some invalidation could have been triggered during setup.
+  const int old_num_invalidations = layout_manager()->num_invalidations();
+
+  host_view()->InvalidateLayout();
+  EXPECT_EQ(old_num_invalidations + 1, layout_manager()->num_invalidations());
+}
+
+TEST_F(LayoutManagerBaseManagerTest, Layout) {
+  constexpr gfx::Point kUpperLeft(kChildViewPadding, kChildViewPadding);
+  AddChildView(kSquarishSize);
+  AddChildView(kLongSize);
+  AddChildView(kTallSize);
+
+  // This should fit all of the child views and trigger layout.
+  host_view()->SetSize({40, 40});
+  EXPECT_EQ(1, layout_manager()->num_layouts_generated());
+  EXPECT_EQ(gfx::Rect(kUpperLeft, child(0)->GetPreferredSize()),
+            child(0)->bounds());
+  EXPECT_TRUE(child(0)->GetVisible());
+  EXPECT_EQ(gfx::Rect(kUpperLeft, child(1)->GetPreferredSize()),
+            child(1)->bounds());
+  EXPECT_TRUE(child(1)->GetVisible());
+  EXPECT_EQ(gfx::Rect(kUpperLeft, child(2)->GetPreferredSize()),
+            child(2)->bounds());
+  EXPECT_TRUE(child(2)->GetVisible());
+
+  // This should drop out some children.
+  host_view()->SetSize({25, 25});
+  EXPECT_EQ(2, layout_manager()->num_layouts_generated());
+  EXPECT_EQ(gfx::Rect(kUpperLeft, child(0)->GetPreferredSize()),
+            child(0)->bounds());
+  EXPECT_TRUE(child(0)->GetVisible());
+  EXPECT_FALSE(child(1)->GetVisible());
+  EXPECT_FALSE(child(2)->GetVisible());
+}
+
+TEST_F(LayoutManagerBaseManagerTest, ChildViewIgnoredByLayout) {
+  AddChildView(kSquarishSize);
+  AddChildView(kLongSize);
+  AddChildView(kTallSize);
+
+  EXPECT_FALSE(layout_manager()->IsChildViewIgnoredByLayout(child(0)));
+  EXPECT_FALSE(layout_manager()->IsChildViewIgnoredByLayout(child(1)));
+  EXPECT_FALSE(layout_manager()->IsChildViewIgnoredByLayout(child(2)));
+
+  layout_manager()->SetChildViewIgnoredByLayout(child(1), true);
+
+  EXPECT_FALSE(layout_manager()->IsChildViewIgnoredByLayout(child(0)));
+  EXPECT_TRUE(layout_manager()->IsChildViewIgnoredByLayout(child(1)));
+  EXPECT_FALSE(layout_manager()->IsChildViewIgnoredByLayout(child(2)));
+}
+
+TEST_F(LayoutManagerBaseManagerTest,
+       ChildViewIgnoredByLayout_IgnoresChildView) {
+  AddChildView(kSquarishSize);
+  AddChildView(kLongSize);
+  AddChildView(kTallSize);
+
+  layout_manager()->SetChildViewIgnoredByLayout(child(1), true);
+
+  child(1)->SetSize(kLargeSize);
+
+  // Makes enough room for all views, and triggers layout.
+  host_view()->SetSize({50, 50});
+
+  EXPECT_EQ(child(0)->GetPreferredSize(), child(0)->size());
+  EXPECT_EQ(kLargeSize, child(1)->size());
+  EXPECT_EQ(child(2)->GetPreferredSize(), child(2)->size());
+}
+
+TEST_F(LayoutManagerBaseManagerTest, ViewVisibilitySet) {
+  AddChildView(kSquarishSize);
+  AddChildView(kLongSize);
+  AddChildView(kTallSize);
+
+  child(1)->SetVisible(false);
+
+  // Makes enough room for all views, and triggers layout.
+  host_view()->SetSize({50, 50});
+
+  EXPECT_TRUE(child(0)->GetVisible());
+  EXPECT_EQ(child(0)->GetPreferredSize(), child(0)->size());
+  EXPECT_FALSE(child(1)->GetVisible());
+  EXPECT_TRUE(child(2)->GetVisible());
+  EXPECT_EQ(child(2)->GetPreferredSize(), child(2)->size());
+
+  // Turn the second child view back on and verify it's present in the layout
+  // again.
+  child(1)->SetVisible(true);
+  host_view()->Layout();
+
+  EXPECT_TRUE(child(0)->GetVisible());
+  EXPECT_EQ(child(0)->GetPreferredSize(), child(0)->size());
+  EXPECT_TRUE(child(1)->GetVisible());
+  EXPECT_EQ(child(1)->GetPreferredSize(), child(1)->size());
+  EXPECT_TRUE(child(2)->GetVisible());
+  EXPECT_EQ(child(2)->GetPreferredSize(), child(2)->size());
+}
+
+TEST_F(LayoutManagerBaseManagerTest, ViewAdded) {
+  AddChildView(kLongSize);
+  AddChildView(kTallSize);
+
+  // Makes enough room for all views, and triggers layout.
+  host_view()->SetSize({50, 50});
+
+  EXPECT_TRUE(child(0)->GetVisible());
+  EXPECT_EQ(child(0)->GetPreferredSize(), child(0)->size());
+  EXPECT_TRUE(child(1)->GetVisible());
+  EXPECT_EQ(child(1)->GetPreferredSize(), child(1)->size());
+
+  // Add a new view and verify it is being laid out.
+  View* new_view = AddChildView(kSquarishSize);
+  host_view()->Layout();
+
+  EXPECT_TRUE(new_view->GetVisible());
+  EXPECT_EQ(new_view->GetPreferredSize(), new_view->size());
+}
+
+TEST_F(LayoutManagerBaseManagerTest, ViewAdded_NotVisible) {
+  AddChildView(kLongSize);
+  AddChildView(kTallSize);
+
+  // Makes enough room for all views, and triggers layout.
+  host_view()->SetSize({50, 50});
+
+  EXPECT_TRUE(child(0)->GetVisible());
+  EXPECT_EQ(child(0)->GetPreferredSize(), child(0)->size());
+  EXPECT_TRUE(child(1)->GetVisible());
+  EXPECT_EQ(child(1)->GetPreferredSize(), child(1)->size());
+
+  // Add a new view that is not visible and ensure that the layout manager
+  // doesn't touch it during layout.
+  View* new_view = new StaticSizedView(kSquarishSize);
+  new_view->SetVisible(false);
+  host_view()->AddChildView(new_view);
+  host_view()->Layout();
+
+  EXPECT_FALSE(new_view->GetVisible());
+}
+
+TEST_F(LayoutManagerBaseManagerTest, ViewRemoved) {
+  AddChildView(kSquarishSize);
+  View* const child_view = AddChildView(kLongSize);
+  AddChildView(kTallSize);
+
+  // Makes enough room for all views, and triggers layout.
+  host_view()->SetSize({50, 50});
+
+  EXPECT_TRUE(child(0)->GetVisible());
+  EXPECT_EQ(child(0)->GetPreferredSize(), child(0)->size());
+  EXPECT_TRUE(child(1)->GetVisible());
+  EXPECT_EQ(child(1)->GetPreferredSize(), child(1)->size());
+  EXPECT_TRUE(child(2)->GetVisible());
+  EXPECT_EQ(child(2)->GetPreferredSize(), child(2)->size());
+
+  host_view()->RemoveChildView(child_view);
+  child_view->SetSize(kLargeSize);
+  host_view()->Layout();
+
+  EXPECT_TRUE(child(0)->GetVisible());
+  EXPECT_EQ(child(0)->GetPreferredSize(), child(0)->size());
+  EXPECT_TRUE(child(1)->GetVisible());
+  EXPECT_EQ(child(1)->GetPreferredSize(), child(1)->size());
+
+  EXPECT_TRUE(child_view->GetVisible());
+  EXPECT_EQ(kLargeSize, child_view->size());
+
+  // Required since we removed it from the parent view.
+  delete child_view;
+}
+
+}  // namespace views
diff --git a/ui/views/layout/layout_types.cc b/ui/views/layout/layout_types.cc
new file mode 100644
index 0000000..67e4a77
--- /dev/null
+++ b/ui/views/layout/layout_types.cc
@@ -0,0 +1,62 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/views/layout/layout_types.h"
+
+#include <algorithm>
+
+#include "base/strings/stringprintf.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace views {
+
+namespace {
+
+std::string OptionalToString(const base::Optional<int>& opt) {
+  if (!opt.has_value())
+    return "_";
+  return base::StringPrintf("%d", opt.value());
+}
+
+}  // namespace
+
+// SizeBounds ------------------------------------------------------------------
+
+SizeBounds::SizeBounds() = default;
+
+SizeBounds::SizeBounds(const base::Optional<int>& width,
+                       const base::Optional<int>& height)
+    : width_(width), height_(height) {}
+
+SizeBounds::SizeBounds(const SizeBounds& other)
+    : width_(other.width()), height_(other.height()) {}
+
+SizeBounds::SizeBounds(const gfx::Size& other)
+    : width_(other.width()), height_(other.height()) {}
+
+void SizeBounds::Enlarge(int width, int height) {
+  if (width_)
+    width_ = std::max(0, *width_ + width);
+  if (height_)
+    height_ = std::max(0, *height_ + height);
+}
+
+bool SizeBounds::operator==(const SizeBounds& other) const {
+  return width_ == other.width_ && height_ == other.height_;
+}
+
+bool SizeBounds::operator!=(const SizeBounds& other) const {
+  return !(*this == other);
+}
+
+bool SizeBounds::operator<(const SizeBounds& other) const {
+  return std::tie(height_, width_) < std::tie(other.height_, other.width_);
+}
+
+std::string SizeBounds::ToString() const {
+  return base::StringPrintf("%s x %s", OptionalToString(width()).c_str(),
+                            OptionalToString(height()).c_str());
+}
+
+}  // namespace views
diff --git a/ui/views/layout/layout_types.h b/ui/views/layout/layout_types.h
new file mode 100644
index 0000000..24b3383f
--- /dev/null
+++ b/ui/views/layout/layout_types.h
@@ -0,0 +1,58 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_VIEWS_LAYOUT_LAYOUT_TYPES_H_
+#define UI_VIEWS_LAYOUT_LAYOUT_TYPES_H_
+
+#include <string>
+
+#include "base/optional.h"
+#include "ui/views/views_export.h"
+
+namespace gfx {
+class Size;
+}  // namespace gfx
+
+namespace views {
+
+// Whether a layout is oriented horizontally or vertically.
+enum class LayoutOrientation {
+  kHorizontal,
+  kVertical,
+};
+
+// Stores an optional width and height upper bound. Used when calculating the
+// preferred size of a layout pursuant to a maximum available size.
+class VIEWS_EXPORT SizeBounds {
+ public:
+  SizeBounds();
+  SizeBounds(const base::Optional<int>& width,
+             const base::Optional<int>& height);
+  explicit SizeBounds(const gfx::Size& size);
+  SizeBounds(const SizeBounds& other);
+
+  const base::Optional<int>& width() const { return width_; }
+  void set_width(const base::Optional<int>& width) { width_ = width; }
+
+  const base::Optional<int>& height() const { return height_; }
+  void set_height(const base::Optional<int>& height) { height_ = height; }
+
+  // Enlarges (or shrinks, if negative) each upper bound that is present by the
+  // specified amounts.
+  void Enlarge(int width, int height);
+
+  bool operator==(const SizeBounds& other) const;
+  bool operator!=(const SizeBounds& other) const;
+  bool operator<(const SizeBounds& other) const;
+
+  std::string ToString() const;
+
+ private:
+  base::Optional<int> width_;
+  base::Optional<int> height_;
+};
+
+}  // namespace views
+
+#endif  // UI_VIEWS_LAYOUT_LAYOUT_TYPES_H_
diff --git a/ui/views_content_client/views_content_client_main_parts.cc b/ui/views_content_client/views_content_client_main_parts.cc
index c1a83aa..e7fa6743 100644
--- a/ui/views_content_client/views_content_client_main_parts.cc
+++ b/ui/views_content_client/views_content_client_main_parts.cc
@@ -33,7 +33,7 @@
 void ViewsContentClientMainParts::PreMainMessageLoopRun() {
   ui::MaterialDesignController::Initialize();
   ui::InitializeInputMethodForTesting();
-  browser_context_.reset(new content::ShellBrowserContext(false, NULL));
+  browser_context_.reset(new content::ShellBrowserContext(false));
 
   std::unique_ptr<views::TestViewsDelegate> test_views_delegate(
       new views::DesktopTestViewsDelegate);
diff --git a/ui/webui/resources/cr_elements/cr_slider/cr_slider.html b/ui/webui/resources/cr_elements/cr_slider/cr_slider.html
index 0d4ea45..c321370 100644
--- a/ui/webui/resources/cr_elements/cr_slider/cr_slider.html
+++ b/ui/webui/resources/cr_elements/cr_slider/cr_slider.html
@@ -114,7 +114,6 @@
 
       #bar {
         border-top-color: var(--cr-slider-active-color);
-        width: 0;
       }
 
       :host([transiting_]) #bar {
@@ -206,7 +205,7 @@
         box-shadow: unset;
       }
     </style>
-    <div id="container">
+    <div id="container" hidden>
       <div id="bar"></div>
       <div id="markers" hidden$="[[!markerCount]]">
         <template is="dom-repeat" items="[[getMarkers_(markerCount)]]">
diff --git a/ui/webui/resources/cr_elements/cr_slider/cr_slider.js b/ui/webui/resources/cr_elements/cr_slider/cr_slider.js
index 92471e2..2da0101c 100644
--- a/ui/webui/resources/cr_elements/cr_slider/cr_slider.js
+++ b/ui/webui/resources/cr_elements/cr_slider/cr_slider.js
@@ -132,10 +132,7 @@
         value: () => [],
       },
 
-      value: {
-        type: Number,
-        value: 0,
-      },
+      value: Number,
 
       /** @private */
       label_: {
@@ -179,7 +176,7 @@
     observers: [
       'onTicksChanged_(ticks.*)',
       'updateUi_(ticks.*, value, min, max)',
-      'updateValue_(value, min, max)',
+      'onValueMinMaxChange_(value, min, max)',
     ],
 
     listeners: {
@@ -211,7 +208,10 @@
       this.draggingEventTracker_ = new EventTracker();
     },
 
-    /** @private */
+    /**
+     * @return {boolean}
+     * @private
+     */
     computeDisabled_: function() {
       return this.disabled || this.ticks.length == 1;
     },
@@ -381,7 +381,9 @@
         this.max = this.ticks.length - 1;
         this.min = 0;
       }
-      this.updateValue_(this.value);
+      if (this.value !== undefined) {
+        this.updateValue_(this.value);
+      }
     },
 
     /** @private */
@@ -390,6 +392,15 @@
     },
 
     /** @private */
+    onValueMinMaxChange_: function() {
+      if (this.value == undefined || this.min == undefined ||
+          this.max == undefined) {
+        return;
+      }
+      this.updateValue_(this.value);
+    },
+
+    /** @private */
     updateUi_: function() {
       const percent = `${this.getRatio() * 100}%`;
       this.$.bar.style.width = percent;
@@ -420,6 +431,7 @@
      * @private
      */
     updateValue_: function(value) {
+      this.$.container.hidden = false;
       if (this.snaps) {
         // Skip update if |value| has not passed the next value .8 units away.
         // The value will update as the drag approaches the next value.