diff --git a/DEPS b/DEPS
index e8af060..a516c3a 100644
--- a/DEPS
+++ b/DEPS
@@ -138,11 +138,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': '6a07591a5577b63893dc4018a7c54fc4bb28dbb2',
+  'skia_revision': '09f5aedf2cc886296641658fdb800ced1e7f0b7e',
   # 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': '2cb25737c9eb7893ac752674e75d400b4419f67c',
+  'v8_revision': 'ddbb5499b8722f494df8daa5b5e21e3f5e2b6f64',
   # 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.
@@ -150,7 +150,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'f7106d16306ae3a98a37100ee028b5aaeca63586',
+  'angle_revision': 'c104b2d24a6f0593d5bf5ccce4b594e644a5abcb',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -158,7 +158,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '8fa6a209221b41c841c6b646bc4c55825c20682f',
+  'pdfium_revision': '8e6dcdbee832eca11a20f234216146ecb4884d0b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -201,7 +201,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '3d4e56f8f4efe9b095c61a4c10d11c20eeaa63f0',
+  'catapult_revision': '5b31e690a48d5f7a8ba38825a603a57bca282038',
   # 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,7 +273,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '839053b90c7718b9162a123ce040117d2d223cc3',
+  'dawn_revision': 'c2750abd0c62608f0ebed44aae3716257e8dec41',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -807,7 +807,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '09fad2edc8250ba00e3a4c20447e10104d205292',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'a3fa5283c9f7561d1e53a3dc009ea76b45ab68bb',
       'condition': 'checkout_linux',
   },
 
@@ -832,7 +832,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'cd28c22b7b65a63da1120592adb73c2ec6e9954a',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'c38806be7188121ad4778fb07ad396213fe69c94',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -1182,7 +1182,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '7f727d4068ec466c3b1f3ba5f178fe2f58f1d1d7',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '9e68bfc4b622e1d509f5767cd715cf059215eb43',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78',
@@ -1350,7 +1350,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '6f0b34abee8dba611c253738d955c59f703c147a',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '85b8ce2ab95bea71bebd5238f86df04183ac4025',
+    Var('webrtc_git') + '/src.git' + '@' + '114e8bb7a68f239b5611f71414ca99058ae02464',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1391,7 +1391,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@261cefc6693a5217e6f9af7b05b762c1cee2a433',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@64bd50cc7d890d38cafa0c0b81da34bcdc79b40d',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/browser/aw_metrics_service_client.cc b/android_webview/browser/aw_metrics_service_client.cc
index c37f068..d98a0a9c 100644
--- a/android_webview/browser/aw_metrics_service_client.cc
+++ b/android_webview/browser/aw_metrics_service_client.cc
@@ -5,27 +5,20 @@
 #include "android_webview/browser/aw_metrics_service_client.h"
 
 #include <jni.h>
-#include <stdint.h>
-#include <utility>
-#include <vector>
+#include <cstdint>
+#include <memory>
 
 #include "android_webview/browser/aw_feature_list.h"
 #include "android_webview/browser/aw_metrics_log_uploader.h"
 #include "android_webview/common/aw_switches.h"
 #include "android_webview/jni/AwMetricsServiceClient_jni.h"
-#include "base/android/build_info.h"
 #include "base/android/jni_android.h"
 #include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
-#include "base/bind.h"
-#include "base/files/file_util.h"
-#include "base/guid.h"
 #include "base/hash/hash.h"
 #include "base/i18n/rtl.h"
 #include "base/lazy_instance.h"
-#include "base/path_service.h"
 #include "base/strings/string16.h"
-#include "base/task/post_task.h"
 #include "components/metrics/call_stack_profile_metrics_provider.h"
 #include "components/metrics/enabled_state_provider.h"
 #include "components/metrics/gpu/gpu_metrics_provider.h"
@@ -35,12 +28,10 @@
 #include "components/metrics/metrics_state_manager.h"
 #include "components/metrics/net/network_metrics_provider.h"
 #include "components/metrics/ui/screen_info_metrics_provider.h"
-#include "components/metrics/url_constants.h"
 #include "components/metrics/version_utils.h"
 #include "components/prefs/pref_service.h"
 #include "components/version_info/android/channel_getter.h"
 #include "components/version_info/version_info.h"
-#include "content/public/browser/browser_thread.h"
 #include "content/public/browser/network_service_instance.h"
 
 namespace android_webview {
@@ -49,14 +40,6 @@
 
 namespace {
 
-const int kUploadIntervalMinutes = 30;
-
-// A GUID in text form is composed of 32 hex digits and 4 hyphens.
-const size_t kGuidSize = 32 + 4;
-// The legacy file where WebView used to store the client ID, before it was
-// moved to prefs.
-const char* const kGuidFileName = "metrics_guid";
-
 // Callbacks for metrics::MetricsStateManager::Create. Store/LoadClientInfo
 // allow Windows Chrome to back up ClientInfo. They're no-ops for WebView.
 
@@ -87,57 +70,27 @@
   return hash < UINT32_MAX / 50u;
 }
 
-// Load the client ID from the legacy file, if any, store it in |id|, and then
-// delete the file.
-// TODO(crbug/939002): Remove this after ~all clients have migrated the ID.
-void LoadLegacyClientId(std::unique_ptr<std::string>* id) {
-  base::FilePath path;
-  if (!internal::GetLegacyClientIdPath(&path))
-    return;
-  std::string contents;
-  if (base::ReadFileToStringWithMaxSize(path, &contents, kGuidSize)) {
-    if (base::IsValidGUID(contents))
-      *id = std::make_unique<std::string>(std::move(contents));
-  }
-  base::DeleteFile(path, /*recursive=*/false);
-}
-
-std::unique_ptr<::metrics::MetricsService> CreateMetricsService(
-    ::metrics::MetricsStateManager* state_manager,
-    ::metrics::MetricsServiceClient* client,
+std::unique_ptr<metrics::MetricsService> CreateMetricsService(
+    metrics::MetricsStateManager* state_manager,
+    metrics::MetricsServiceClient* client,
     PrefService* prefs) {
   auto service =
-      std::make_unique<::metrics::MetricsService>(state_manager, client, prefs);
+      std::make_unique<metrics::MetricsService>(state_manager, client, prefs);
   service->RegisterMetricsProvider(
-      std::make_unique<::metrics::NetworkMetricsProvider>(
+      std::make_unique<metrics::NetworkMetricsProvider>(
           content::CreateNetworkConnectionTrackerAsyncGetter()));
   service->RegisterMetricsProvider(
-      std::make_unique<::metrics::GPUMetricsProvider>());
+      std::make_unique<metrics::GPUMetricsProvider>());
   service->RegisterMetricsProvider(
-      std::make_unique<::metrics::ScreenInfoMetricsProvider>());
+      std::make_unique<metrics::ScreenInfoMetricsProvider>());
   service->RegisterMetricsProvider(
-      std::make_unique<::metrics::CallStackProfileMetricsProvider>());
+      std::make_unique<metrics::CallStackProfileMetricsProvider>());
   service->InitializeMetricsRecordingState();
   return service;
 }
 
 }  // namespace
 
-namespace internal {
-
-// Get the path to the file where WebView used to store the client ID, before
-// it was moved to prefs. Return true/false on success/failure.
-// TODO(crbug/939002): Remove this after ~all clients have migrated the ID.
-bool GetLegacyClientIdPath(base::FilePath* path) {
-  base::FilePath dir;
-  if (!base::PathService::Get(base::DIR_ANDROID_APP_DATA, &dir))
-    return false;
-  *path = dir.Append(FILE_PATH_LITERAL(kGuidFileName));
-  return true;
-}
-
-}  // namespace internal
-
 // static
 AwMetricsServiceClient* AwMetricsServiceClient::GetInstance() {
   AwMetricsServiceClient* client = g_lazy_instance_.Pointer();
@@ -150,7 +103,6 @@
 
 void AwMetricsServiceClient::Initialize(PrefService* pref_service) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(pref_service_ == nullptr);  // Initialize should only happen once.
   DCHECK(!init_finished_);
 
   pref_service_ = pref_service;
@@ -160,11 +112,8 @@
       base::BindRepeating(&StoreClientInfo),
       base::BindRepeating(&LoadClientInfo));
 
-  base::PostTaskWithTraitsAndReply(
-      FROM_HERE, {base::MayBlock()},
-      base::BindOnce(&LoadLegacyClientId, &legacy_client_id_),
-      base::BindOnce(&AwMetricsServiceClient::InitializeWithClientId,
-                     base::Unretained(this)));
+  init_finished_ = true;
+  MaybeStartMetrics();
 }
 
 void AwMetricsServiceClient::MaybeStartMetrics() {
@@ -223,7 +172,7 @@
 void AwMetricsServiceClient::SetMetricsClientId(const std::string& client_id) {}
 
 int32_t AwMetricsServiceClient::GetProduct() {
-  return ::metrics::ChromeUserMetricsExtension::ANDROID_WEBVIEW;
+  return metrics::ChromeUserMetricsExtension::ANDROID_WEBVIEW;
 }
 
 std::string AwMetricsServiceClient::GetApplicationLocale() {
@@ -255,17 +204,17 @@
     base::StringPiece mime_type,
     metrics::MetricsLogUploader::MetricServiceType service_type,
     const metrics::MetricsLogUploader::UploadCallback& on_upload_complete) {
-  // |server_url|, |insecure_server_url| and |mime_type| are unused because
-  // WebView uses the platform logging mechanism instead of the normal UMA
-  // server.
-  return std::unique_ptr<::metrics::MetricsLogUploader>(
-      new AwMetricsLogUploader(on_upload_complete));
+  // |server_url|, |insecure_server_url|, and |mime_type| are unused because
+  // WebView sends metrics to the platform logging mechanism rather than to
+  // Chrome's metrics server.
+  return std::make_unique<AwMetricsLogUploader>(on_upload_complete);
 }
 
 base::TimeDelta AwMetricsServiceClient::GetStandardUploadInterval() {
   // The platform logging mechanism is responsible for upload frequency; this
-  // just specifies how frequently to provide logs to the platform.
-  return base::TimeDelta::FromMinutes(kUploadIntervalMinutes);
+  // just specifies how frequently to provide logs to the platform. 30 minutes
+  // was chosen arbitrarily.
+  return base::TimeDelta::FromMinutes(30);
 }
 
 std::string AwMetricsServiceClient::GetAppPackageName() {
@@ -280,20 +229,6 @@
   return std::string();
 }
 
-void AwMetricsServiceClient::InitializeWithClientId() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(!init_finished_);
-
-  if (legacy_client_id_) {
-    pref_service_->SetString(metrics::prefs::kMetricsClientID,
-                             *legacy_client_id_);
-    legacy_client_id_.reset();
-  }
-
-  init_finished_ = true;
-  MaybeStartMetrics();
-}
-
 bool AwMetricsServiceClient::IsInSample() {
   // Called in MaybeStartMetrics(), after metrics_service_ is created.
   return ::android_webview::IsInSample(metrics_service_->GetClientId());
diff --git a/android_webview/browser/aw_metrics_service_client.h b/android_webview/browser/aw_metrics_service_client.h
index fb145dc..fda24ae 100644
--- a/android_webview/browser/aw_metrics_service_client.h
+++ b/android_webview/browser/aw_metrics_service_client.h
@@ -18,21 +18,12 @@
 
 class PrefService;
 
-namespace base {
-class FilePath;
-}
-
 namespace metrics {
 class MetricsStateManager;
 }
 
 namespace android_webview {
 
-// Exposed for testing.
-namespace internal {
-bool GetLegacyClientIdPath(base::FilePath* path);
-}
-
 // AwMetricsServiceClient is a singleton which manages WebView metrics
 // collection.
 //
@@ -44,17 +35,12 @@
 // SetHaveMetricsConsent(). The last is recorded in |is_in_sample_|.
 //
 // Metrics are pseudonymously identified by a randomly-generated "client ID".
-// WebView stores this in the app's data directory. There's a different such
-// directory for each user, for each app, on each device. So the ID should be
-// unique per (device, app, user) tuple.
-//
-// The client ID should be stored in prefs. But as a vestige from before WebView
-// persisted prefs across runs, it may be stored in a separate file named
-// "metrics_guid". If such a file is found, it should be deleted and the ID
-// moved into prefs.
+// WebView stores this in prefs, written to the app's data directory. There's a
+// different such directory for each user, for each app, on each device. So the
+// ID should be unique per (device, app, user) tuple.
 //
 // To avoid the appearance that we're doing anything sneaky, the client ID
-// should only be created or persisted when neither the user nor the app have
+// should only be created and retained when neither the user nor the app have
 // opted out. Otherwise, the presence of the ID could give the impression that
 // metrics were being collected.
 //
@@ -62,43 +48,36 @@
 //
 //   startup
 //      │
-//      ├──────────────────────────┐
-//      │                          ▼
-//      ▼                       query GMS for consent
-//   Initialize()                  │
-//      │                          │
-//      ▼                          │
-//   LoadLegacyClientId()          │
-//      │                          │
-//      ▼                          │
-//   InitializeWithClientId()      ▼
-//      │                       SetHaveMetricsConsent()
-//      │                          │
-//      │ ┌────────────────────────┘
+//      ├────────────┐
+//      │            ▼
+//      │         query GMS for consent
+//      ▼            │
+//   Initialize()    │
+//      │            ▼
+//      │         SetHaveMetricsConsent()
+//      │            │
+//      │ ┌──────────┘
 //      ▼ ▼
 //   MaybeStartMetrics()
 //      │
 //      ▼
 //   MetricsService::Start()
 //
-// LoadLegacyClientId() is the only function in this diagram that happens off
-// the UI thread. It checks for the legacy metrics_guid file. If it contains a
-// client ID, it stores the ID in |legacy_client_id_|. Then it deletes the file.
-// Once ~all clients have deleted the file, LoadLegacyClientId() can be removed,
-// and Initialize() and InitializeWithClientId() can be merged.
-//
-// Querying GMS is slow, so SetHaveMetricsConsent() typically happens after
-// InitializeWithClientId(). But it may happen before Initialize(), or between
-// Initialize() and InitializeWithClientId().
+// All the named functions in this diagram happen on the UI thread. Querying GMS
+// happens in the background, and the result is posted back to the UI thread, to
+// SetHaveMetricsConsent(). Querying GMS is slow, so SetHaveMetricsConsent()
+// typically happens after Initialize(), but it may happen before.
 //
 // Each path sets a flag, |init_finished_| or |set_consent_finished_|, to show
 // that path has finished, and then calls MaybeStartMetrics(). When
-// MaybeStartMetrics() is called the second time, it sees both flags true,
-// meaning we have both the client ID (if any) and the user/app opt-out status.
+// MaybeStartMetrics() is called the first time, it sees only one flag is true,
+// and does nothing. When MaybeStartMetrics() is called the second time, it
+// decides whether to start metrics.
 //
-// If consent is granted, MaybeStartMetrics() then determines sampling by
-// hashing the ID (generating a new ID if there was none), and may then enable
-// metrics. Otherwise, it clears the client ID.
+// If consent was granted, MaybeStartMetrics() determines sampling by hashing
+// the client ID (generating a new ID if there was none). If this client is in
+// the sample, it then calls MetricsService::Start(). If consent was not
+// granted, MaybeStartMetrics() instead clears the client ID, if any.
 class AwMetricsServiceClient : public metrics::MetricsServiceClient,
                                public metrics::EnabledStateProvider {
   friend struct base::LazyInstanceTraitsBase<AwMetricsServiceClient>;
@@ -138,18 +117,11 @@
   std::string GetAppPackageName() override;
 
  protected:
-  // virtual for testing
-  virtual void InitializeWithClientId();
-  virtual bool IsInSample();
+  virtual bool IsInSample();  // virtual for testing
 
  private:
   void MaybeStartMetrics();
 
-  // Temporarily stores a client ID loaded from the legacy file, to pass it from
-  // LoadLegacyClientId() to InitializeWithClientId().
-  // TODO(crbug/939002): Remove this after ~all clients have migrated the ID.
-  std::unique_ptr<std::string> legacy_client_id_;
-
   std::unique_ptr<metrics::MetricsStateManager> metrics_state_manager_;
   std::unique_ptr<metrics::MetricsService> metrics_service_;
   PrefService* pref_service_ = nullptr;
diff --git a/android_webview/browser/aw_metrics_service_client_unittest.cc b/android_webview/browser/aw_metrics_service_client_unittest.cc
index 0f1844b..0066f92 100644
--- a/android_webview/browser/aw_metrics_service_client_unittest.cc
+++ b/android_webview/browser/aw_metrics_service_client_unittest.cc
@@ -4,13 +4,9 @@
 
 #include "android_webview/browser/aw_metrics_service_client.h"
 
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
-#include "base/path_service.h"
 #include "base/run_loop.h"
-#include "base/test/scoped_path_override.h"
 #include "base/test/scoped_task_environment.h"
 #include "base/test/test_simple_task_runner.h"
 #include "components/metrics/metrics_pref_names.h"
@@ -23,21 +19,13 @@
 
 // For client ID format, see:
 // https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)
-const char kValidClientId[] = "01234567-89ab-40cd-80ef-0123456789ab";
-const char kInvalidClientId[] = "foo";
+const char kTestClientId[] = "01234567-89ab-40cd-80ef-0123456789ab";
 
 class TestClient : public AwMetricsServiceClient {
  public:
   TestClient() {}
   ~TestClient() override {}
 
-  void RunUntilInitialized() {
-    if (init_finished_)
-      return;
-    run_loop_.Run();
-    ASSERT_TRUE(init_finished_);
-  }
-
   bool IsRecordingActive() {
     auto* service = GetMetricsService();
     if (service)
@@ -46,18 +34,9 @@
   }
 
  protected:
-  void InitializeWithClientId() override {
-    AwMetricsServiceClient::InitializeWithClientId();
-    init_finished_ = true;
-    run_loop_.Quit();
-  }
-
   bool IsInSample() override { return true; }
 
  private:
-  base::RunLoop run_loop_;
-  bool init_finished_ = false;
-
   DISALLOW_COPY_AND_ASSIGN(TestClient);
 };
 
@@ -70,7 +49,6 @@
 std::unique_ptr<TestClient> CreateAndInitTestClient(PrefService* prefs) {
   auto client = std::make_unique<TestClient>();
   client->Initialize(prefs);
-  client->RunUntilInitialized();
   return client;
 }
 
@@ -98,9 +76,8 @@
   auto client = std::make_unique<TestClient>();
   client->SetHaveMetricsConsent(true);
   client->Initialize(prefs.get());
-  client->RunUntilInitialized();
   ASSERT_TRUE(client->IsRecordingActive());
-  ASSERT_TRUE(prefs->HasPrefPath(::metrics::prefs::kMetricsClientID));
+  ASSERT_TRUE(prefs->HasPrefPath(metrics::prefs::kMetricsClientID));
 }
 
 TEST_F(AwMetricsServiceClientTest, TestSetConsentFalseBeforeInit) {
@@ -108,29 +85,8 @@
   auto client = std::make_unique<TestClient>();
   client->SetHaveMetricsConsent(false);
   client->Initialize(prefs.get());
-  client->RunUntilInitialized();
   ASSERT_FALSE(client->IsRecordingActive());
-  ASSERT_FALSE(prefs->HasPrefPath(::metrics::prefs::kMetricsClientID));
-}
-
-TEST_F(AwMetricsServiceClientTest, TestSetConsentTrueDuringInit) {
-  auto prefs = CreateTestPrefs();
-  auto client = std::make_unique<TestClient>();
-  client->Initialize(prefs.get());
-  client->SetHaveMetricsConsent(true);
-  client->RunUntilInitialized();
-  ASSERT_TRUE(client->IsRecordingActive());
-  ASSERT_TRUE(prefs->HasPrefPath(::metrics::prefs::kMetricsClientID));
-}
-
-TEST_F(AwMetricsServiceClientTest, TestSetConsentFalseDuringInit) {
-  auto prefs = CreateTestPrefs();
-  auto client = std::make_unique<TestClient>();
-  client->Initialize(prefs.get());
-  client->SetHaveMetricsConsent(false);
-  client->RunUntilInitialized();
-  ASSERT_FALSE(client->IsRecordingActive());
-  ASSERT_FALSE(prefs->HasPrefPath(::metrics::prefs::kMetricsClientID));
+  ASSERT_FALSE(prefs->HasPrefPath(metrics::prefs::kMetricsClientID));
 }
 
 TEST_F(AwMetricsServiceClientTest, TestSetConsentTrueAfterInit) {
@@ -138,7 +94,7 @@
   auto client = CreateAndInitTestClient(prefs.get());
   client->SetHaveMetricsConsent(true);
   ASSERT_TRUE(client->IsRecordingActive());
-  ASSERT_TRUE(prefs->HasPrefPath(::metrics::prefs::kMetricsClientID));
+  ASSERT_TRUE(prefs->HasPrefPath(metrics::prefs::kMetricsClientID));
 }
 
 TEST_F(AwMetricsServiceClientTest, TestSetConsentFalseAfterInit) {
@@ -146,72 +102,27 @@
   auto client = CreateAndInitTestClient(prefs.get());
   client->SetHaveMetricsConsent(false);
   ASSERT_FALSE(client->IsRecordingActive());
-  ASSERT_FALSE(prefs->HasPrefPath(::metrics::prefs::kMetricsClientID));
+  ASSERT_FALSE(prefs->HasPrefPath(metrics::prefs::kMetricsClientID));
 }
 
 // If there is already a valid client ID, it should be reused.
-TEST_F(AwMetricsServiceClientTest, TestKeepValidClientId) {
+TEST_F(AwMetricsServiceClientTest, TestKeepExistingClientId) {
   auto prefs = CreateTestPrefs();
-  prefs->SetString(::metrics::prefs::kMetricsClientID, kValidClientId);
+  prefs->SetString(metrics::prefs::kMetricsClientID, kTestClientId);
   auto client = CreateAndInitTestClient(prefs.get());
   client->SetHaveMetricsConsent(true);
   ASSERT_TRUE(client->IsRecordingActive());
-  ASSERT_TRUE(prefs->HasPrefPath(::metrics::prefs::kMetricsClientID));
-  ASSERT_EQ(kValidClientId,
-            prefs->GetString(::metrics::prefs::kMetricsClientID));
+  ASSERT_TRUE(prefs->HasPrefPath(metrics::prefs::kMetricsClientID));
+  ASSERT_EQ(kTestClientId, prefs->GetString(metrics::prefs::kMetricsClientID));
 }
 
 TEST_F(AwMetricsServiceClientTest, TestSetConsentFalseClearsClientId) {
   auto prefs = CreateTestPrefs();
-  prefs->SetString(::metrics::prefs::kMetricsClientID, kValidClientId);
+  prefs->SetString(metrics::prefs::kMetricsClientID, kTestClientId);
   auto client = CreateAndInitTestClient(prefs.get());
   client->SetHaveMetricsConsent(false);
   ASSERT_FALSE(client->IsRecordingActive());
-  ASSERT_FALSE(prefs->HasPrefPath(::metrics::prefs::kMetricsClientID));
-}
-
-// TODO(crbug/939002): Remove this after ~all clients have migrated the ID.
-TEST_F(AwMetricsServiceClientTest, TestLoadAndDeleteLegacyClientId) {
-  // Write a valid client ID to the legacy client ID file.
-  base::ScopedPathOverride app_data_override(base::DIR_ANDROID_APP_DATA);
-  base::FilePath legacy_file_path;
-  ASSERT_TRUE(internal::GetLegacyClientIdPath(&legacy_file_path));
-  constexpr int len = base::size(kValidClientId) - 1;
-  ASSERT_EQ(len, base::WriteFile(legacy_file_path, kValidClientId, len));
-
-  // Exercise AwMetricsServiceClient.
-  auto prefs = CreateTestPrefs();
-  auto client = CreateAndInitTestClient(prefs.get());
-  client->SetHaveMetricsConsent(true);
-  ASSERT_TRUE(client->IsRecordingActive());
-  // The valid ID should have been stored in prefs.
-  ASSERT_TRUE(prefs->HasPrefPath(::metrics::prefs::kMetricsClientID));
-  ASSERT_EQ(kValidClientId,
-            prefs->GetString(::metrics::prefs::kMetricsClientID));
-  // The legacy file should have been deleted.
-  ASSERT_FALSE(base::PathExists(legacy_file_path));
-}
-
-// TODO(crbug/939002): Remove this after ~all clients have migrated the ID.
-TEST_F(AwMetricsServiceClientTest, TestDeleteInvalidLegacyClientId) {
-  // Write an invalid client ID to the legacy client ID file.
-  base::ScopedPathOverride app_data_override(base::DIR_ANDROID_APP_DATA);
-  base::FilePath legacy_file_path;
-  ASSERT_TRUE(internal::GetLegacyClientIdPath(&legacy_file_path));
-  constexpr int len = base::size(kInvalidClientId) - 1;
-  ASSERT_EQ(len, base::WriteFile(legacy_file_path, kInvalidClientId, len));
-
-  // Exercise AwMetricsServiceClient.
-  auto prefs = CreateTestPrefs();
-  auto client = CreateAndInitTestClient(prefs.get());
-  client->SetHaveMetricsConsent(true);
-  ASSERT_TRUE(client->IsRecordingActive());
-  // A new ID should have been generated and stored in prefs.
-  ASSERT_TRUE(prefs->HasPrefPath(::metrics::prefs::kMetricsClientID));
-  ASSERT_NE(kInvalidClientId,
-            prefs->GetString(::metrics::prefs::kMetricsClientID));
-  // The legacy file should have been deleted.
-  ASSERT_FALSE(base::PathExists(legacy_file_path));
+  ASSERT_FALSE(prefs->HasPrefPath(metrics::prefs::kMetricsClientID));
 }
 
 }  // namespace android_webview
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 02d2311..09daa9b 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -960,6 +960,8 @@
     "system/unified/page_indicator_view.h",
     "system/unified/quiet_mode_feature_pod_controller.cc",
     "system/unified/quiet_mode_feature_pod_controller.h",
+    "system/unified/rounded_label_button.cc",
+    "system/unified/rounded_label_button.h",
     "system/unified/sign_out_button.cc",
     "system/unified/sign_out_button.h",
     "system/unified/top_shortcut_button.cc",
@@ -1641,6 +1643,7 @@
     "keyboard/ash_keyboard_controller_unittest.cc",
     "keyboard/virtual_keyboard_controller_unittest.cc",
     "keyboard/virtual_keyboard_unittest.cc",
+    "kiosk_next/kiosk_next_home_controller_unittest.cc",
     "kiosk_next/kiosk_next_shell_controller_unittest.cc",
     "kiosk_next/kiosk_next_shell_test_util.cc",
     "kiosk_next/kiosk_next_shell_test_util.h",
diff --git a/ash/app_list/views/search_result_tile_item_view.cc b/ash/app_list/views/search_result_tile_item_view.cc
index 06b5527..662f9f0 100644
--- a/ash/app_list/views/search_result_tile_item_view.cc
+++ b/ash/app_list/views/search_result_tile_item_view.cc
@@ -205,7 +205,8 @@
   title_->SetMultiLine(
       (result()->display_type() == ash::SearchResultDisplayType::kTile ||
        (IsSuggestedAppTile() && !show_in_apps_page_)) &&
-      result()->result_type() == ash::SearchResultType::kInstalledApp);
+      (result()->result_type() == ash::SearchResultType::kInstalledApp ||
+       result()->result_type() == ash::SearchResultType::kArcAppShortcut));
 
   // If the new icon is null, it's being decoded asynchronously. Not updating it
   // now to prevent flickering from showing an empty icon while decoding.
diff --git a/ash/kiosk_next/kiosk_next_home_controller.cc b/ash/kiosk_next/kiosk_next_home_controller.cc
index 90b20e1e..c881428 100644
--- a/ash/kiosk_next/kiosk_next_home_controller.cc
+++ b/ash/kiosk_next/kiosk_next_home_controller.cc
@@ -11,13 +11,18 @@
 #include "ash/screen_util.h"
 #include "ash/shell.h"
 #include "ash/wm/container_finder.h"
+#include "ash/wm/overview/overview_controller.h"
+#include "ash/wm/toplevel_window_event_handler.h"
+#include "ash/wm/window_util.h"
 #include "ash/wm/wm_event.h"
 #include "base/logging.h"
 #include "ui/aura/window.h"
+#include "ui/base/hit_test.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/layer_animator.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/display/screen.h"
+#include "ui/events/event_target.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/transform.h"
 
@@ -111,10 +116,12 @@
 
   Shell::Get()->screen_orientation_controller()->LockOrientationForWindow(
       home_screen_window_, OrientationLockType::kLandscape);
+  Shell::Get()->AddPreTargetHandler(this, ui::EventTarget::Priority::kSystem);
 }
 
 void KioskNextHomeController::OnWillRemoveWindow(aura::Window* window) {
   DCHECK_EQ(home_screen_window_, window);
+  Shell::Get()->RemovePreTargetHandler(this);
   home_screen_window_ = nullptr;
 }
 
@@ -123,4 +130,30 @@
     home_screen_container_ = nullptr;
 }
 
+void KioskNextHomeController::OnGestureEvent(ui::GestureEvent* event) {
+  if (event->type() != ui::EventType::ET_GESTURE_SCROLL_BEGIN)
+    return;
+
+  aura::Window* target = static_cast<aura::Window*>(event->target());
+  int component = wm::GetNonClientComponent(target, event->location());
+
+  aura::Window* new_target =
+      ToplevelWindowEventHandler::GetTargetForClientAreaGesture(event, target);
+
+  if (new_target)
+    target = new_target;
+
+  if (target != home_screen_container_ && target != home_screen_window_)
+    return;
+
+  if (component == HTCLIENT) {
+    event->SetHandled();
+    event->StopPropagation();
+
+    auto* overview_controller = Shell::Get()->overview_controller();
+    if (!overview_controller->InOverviewSession())
+      overview_controller->ToggleOverview();
+  }
+}
+
 }  // namespace ash
diff --git a/ash/kiosk_next/kiosk_next_home_controller.h b/ash/kiosk_next/kiosk_next_home_controller.h
index aed0000..6aec00c 100644
--- a/ash/kiosk_next/kiosk_next_home_controller.h
+++ b/ash/kiosk_next/kiosk_next_home_controller.h
@@ -10,15 +10,22 @@
 #include "base/macros.h"
 #include "ui/aura/window_observer.h"
 #include "ui/display/display_observer.h"
+#include "ui/events/event_handler.h"
+
+namespace ui {
+class GestureEvent;
+}
 
 namespace ash {
 
 // KioskNextHomeController manages the Home window for the Kiosk Next shell.
-// TODO(michaelpg): Manage gestures on the Home window, such as dragging down
-// from the top for Overview mode.
+// TODO(michaelpg): Show a slide-down animation when opening Overview.
+// TODO(michaelpg): Suppress tap events in the Kiosk Next Home window when a
+// gesture event triggers Overview this way.
 class ASH_EXPORT KioskNextHomeController : public HomeScreenDelegate,
                                            public display::DisplayObserver,
-                                           public aura::WindowObserver {
+                                           public aura::WindowObserver,
+                                           public ui::EventHandler {
  public:
   KioskNextHomeController();
   ~KioskNextHomeController() override;
@@ -39,11 +46,14 @@
   void OnDisplayMetricsChanged(const display::Display& display,
                                uint32_t changed_metrics) override;
 
-  // WindowObserver:
+  // aura::WindowObserver:
   void OnWindowAdded(aura::Window* new_window) override;
   void OnWillRemoveWindow(aura::Window* window) override;
   void OnWindowDestroying(aura::Window* window) override;
 
+  // ui::EventHandler:
+  void OnGestureEvent(ui::GestureEvent* event) override;
+
  private:
   aura::Window* home_screen_container_ = nullptr;
   aura::Window* home_screen_window_ = nullptr;
diff --git a/ash/kiosk_next/kiosk_next_home_controller_unittest.cc b/ash/kiosk_next/kiosk_next_home_controller_unittest.cc
new file mode 100644
index 0000000..2cd7c988
--- /dev/null
+++ b/ash/kiosk_next/kiosk_next_home_controller_unittest.cc
@@ -0,0 +1,116 @@
+// 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 "ash/kiosk_next/kiosk_next_home_controller.h"
+
+#include "ash/home_screen/home_screen_controller.h"
+#include "ash/kiosk_next/kiosk_next_shell_test_util.h"
+#include "ash/kiosk_next/mock_kiosk_next_shell_client.h"
+#include "ash/public/cpp/app_types.h"
+#include "ash/public/cpp/ash_features.h"
+#include "ash/shell.h"
+#include "ash/test/ash_test_base.h"
+#include "ash/wm/overview/overview_controller.h"
+#include "ash/wm/window_state.h"
+#include "base/test/scoped_feature_list.h"
+#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/client/window_types.h"
+#include "ui/aura/test/test_window_delegate.h"
+#include "ui/base/hit_test.h"
+#include "ui/events/test/event_generator.h"
+
+namespace ash {
+namespace {
+
+class KioskNextHomeControllerTest : public AshTestBase {
+ public:
+  KioskNextHomeControllerTest() = default;
+  ~KioskNextHomeControllerTest() override = default;
+
+  void SetUp() override {
+    scoped_feature_list_.InitAndEnableFeature(features::kKioskNextShell);
+    set_start_session(false);
+    AshTestBase::SetUp();
+    client_ = BindMockKioskNextShellClient();
+    LogInKioskNextUser(GetSessionControllerClient());
+    SetUpHomeWindow();
+  }
+
+  void TearDown() override {
+    home_screen_window_.reset();
+    AshTestBase::TearDown();
+    client_.reset();
+  }
+
+  void SetUpHomeWindow() {
+    auto* delegate =
+        aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate();
+
+    home_screen_window_.reset(
+        CreateTestWindowInShellWithDelegate(delegate, 0, gfx::Rect()));
+    home_screen_window_->SetProperty(aura::client::kAppType,
+                                     static_cast<int>(AppType::CHROME_APP));
+    home_screen_window_->set_owned_by_parent(false);
+    Shell::GetContainer(Shell::GetPrimaryRootWindow(),
+                        kShellWindowId_HomeScreenContainer)
+        ->AddChild(home_screen_window_.get());
+
+    auto* window_state = wm::GetWindowState(home_screen_window_.get());
+    window_state->Maximize();
+    home_screen_window_->Show();
+  }
+
+  // TestWindowDelegate always returns its |window_component_| when
+  // TestWindowDelegate::GetNonClientComponent(const gfx::Point& point) is
+  // called, regardless of the location. Therefore individual tests have to set
+  // the |window_component_|. KioskNextHomeController's event handler starts
+  // overview only if the window component which the gesture event touches is
+  // HTCLIENT.
+  void SetWindowComponent(int component) {
+    auto* delegate = static_cast<aura::test::TestWindowDelegate*>(
+        home_screen_window_->delegate());
+    delegate->set_window_component(component);
+  }
+
+ protected:
+  std::unique_ptr<aura::Window> home_screen_window_;
+  std::unique_ptr<MockKioskNextShellClient> client_;
+  base::test::ScopedFeatureList scoped_feature_list_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(KioskNextHomeControllerTest);
+};
+
+TEST_F(KioskNextHomeControllerTest, CheckWindows) {
+  auto* kiosk_next_home_controller =
+      Shell::Get()->home_screen_controller()->delegate();
+
+  EXPECT_EQ(kiosk_next_home_controller->GetHomeScreenWindow(),
+            home_screen_window_.get());
+}
+
+TEST_F(KioskNextHomeControllerTest, TestGestureToOverview) {
+  SetWindowComponent(HTCLIENT);
+
+  ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
+
+  generator.GestureScrollSequence(gfx::Point(50, 0), gfx::Point(50, 20),
+                                  base::TimeDelta::FromMilliseconds(10), 10);
+
+  EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
+}
+
+TEST_F(KioskNextHomeControllerTest, TestGestureToNoOverview) {
+  SetWindowComponent(HTNOWHERE);
+
+  ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
+
+  generator.GestureScrollSequence(gfx::Point(50, 0), gfx::Point(50, 20),
+                                  base::TimeDelta::FromMilliseconds(10), 10);
+
+  EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
+}
+
+}  // namespace
+}  // namespace ash
diff --git a/ash/system/message_center/unified_message_center_view.cc b/ash/system/message_center/unified_message_center_view.cc
index 5ecd365..103083b 100644
--- a/ash/system/message_center/unified_message_center_view.cc
+++ b/ash/system/message_center/unified_message_center_view.cc
@@ -16,7 +16,7 @@
 #include "ash/system/message_center/unified_message_list_view.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/tray/tray_popup_utils.h"
-#include "ash/system/unified/sign_out_button.h"
+#include "ash/system/unified/rounded_label_button.h"
 #include "ash/system/unified/unified_system_tray_model.h"
 #include "ash/system/unified/unified_system_tray_view.h"
 #include "base/metrics/user_metrics.h"
diff --git a/ash/system/unified/notification_hidden_view.cc b/ash/system/unified/notification_hidden_view.cc
index f766bcf..128bcd03b 100644
--- a/ash/system/unified/notification_hidden_view.cc
+++ b/ash/system/unified/notification_hidden_view.cc
@@ -10,7 +10,7 @@
 #include "ash/system/message_center/ash_message_center_lock_screen_controller.h"
 #include "ash/system/message_center/message_center_controller.h"
 #include "ash/system/tray/tray_constants.h"
-#include "ash/system/unified/sign_out_button.h"
+#include "ash/system/unified/rounded_label_button.h"
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "ui/base/l10n/l10n_util.h"
diff --git a/ash/system/unified/rounded_label_button.cc b/ash/system/unified/rounded_label_button.cc
new file mode 100644
index 0000000..02ed15ca
--- /dev/null
+++ b/ash/system/unified/rounded_label_button.cc
@@ -0,0 +1,85 @@
+// 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 "ash/system/unified/rounded_label_button.h"
+
+#include "ash/system/tray/tray_constants.h"
+#include "ash/system/tray/tray_popup_utils.h"
+#include "ui/gfx/canvas.h"
+#include "ui/views/animation/ink_drop.h"
+#include "ui/views/animation/ink_drop_highlight.h"
+#include "ui/views/animation/ink_drop_mask.h"
+#include "ui/views/animation/ink_drop_ripple.h"
+#include "ui/views/border.h"
+#include "ui/views/view_class_properties.h"
+
+namespace ash {
+
+RoundedLabelButton::RoundedLabelButton(views::ButtonListener* listener,
+                                       const base::string16& text)
+    : views::LabelButton(listener, text) {
+  SetEnabledTextColors(kUnifiedMenuTextColor);
+  SetHorizontalAlignment(gfx::ALIGN_CENTER);
+  SetBorder(views::CreateEmptyBorder(gfx::Insets()));
+  label()->SetSubpixelRenderingEnabled(false);
+  label()->SetFontList(views::Label::GetDefaultFontList().Derive(
+      1, gfx::Font::NORMAL, gfx::Font::Weight::MEDIUM));
+  TrayPopupUtils::ConfigureTrayPopupButton(this);
+
+  auto path = std::make_unique<SkPath>();
+  path->addRoundRect(gfx::RectToSkRect(gfx::Rect(CalculatePreferredSize())),
+                     kTrayItemSize / 2, kTrayItemSize / 2);
+  SetProperty(views::kHighlightPathKey, path.release());
+}
+
+RoundedLabelButton::~RoundedLabelButton() = default;
+
+gfx::Size RoundedLabelButton::CalculatePreferredSize() const {
+  return gfx::Size(label()->GetPreferredSize().width() + kTrayItemSize,
+                   kTrayItemSize);
+}
+
+int RoundedLabelButton::GetHeightForWidth(int width) const {
+  return kTrayItemSize;
+}
+
+void RoundedLabelButton::PaintButtonContents(gfx::Canvas* canvas) {
+  gfx::Rect rect(GetContentsBounds());
+  cc::PaintFlags flags;
+  flags.setAntiAlias(true);
+  flags.setColor(kUnifiedMenuButtonColor);
+  flags.setStyle(cc::PaintFlags::kFill_Style);
+  canvas->DrawRoundRect(rect, kTrayItemSize / 2, flags);
+
+  views::LabelButton::PaintButtonContents(canvas);
+}
+
+std::unique_ptr<views::InkDrop> RoundedLabelButton::CreateInkDrop() {
+  return TrayPopupUtils::CreateInkDrop(this);
+}
+
+std::unique_ptr<views::InkDropRipple> RoundedLabelButton::CreateInkDropRipple()
+    const {
+  return TrayPopupUtils::CreateInkDropRipple(
+      TrayPopupInkDropStyle::FILL_BOUNDS, this,
+      GetInkDropCenterBasedOnLastEvent(), kUnifiedMenuIconColor);
+}
+
+std::unique_ptr<views::InkDropHighlight>
+RoundedLabelButton::CreateInkDropHighlight() const {
+  return TrayPopupUtils::CreateInkDropHighlight(
+      TrayPopupInkDropStyle::FILL_BOUNDS, this, kUnifiedMenuIconColor);
+}
+
+std::unique_ptr<views::InkDropMask> RoundedLabelButton::CreateInkDropMask()
+    const {
+  return std::make_unique<views::RoundRectInkDropMask>(size(), gfx::Insets(),
+                                                       kTrayItemSize / 2);
+}
+
+const char* RoundedLabelButton::GetClassName() const {
+  return "RoundedLabelButton";
+}
+
+}  // namespace ash
diff --git a/ash/system/unified/rounded_label_button.h b/ash/system/unified/rounded_label_button.h
new file mode 100644
index 0000000..add7cc5
--- /dev/null
+++ b/ash/system/unified/rounded_label_button.h
@@ -0,0 +1,42 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_SYSTEM_UNIFIED_ROUNDED_LABEL_BUTTON_H_
+#define ASH_SYSTEM_UNIFIED_ROUNDED_LABEL_BUTTON_H_
+
+#include "base/macros.h"
+#include "base/strings/string16.h"
+#include "ui/views/controls/button/label_button.h"
+
+namespace views {
+class ButtonListener;
+}  // namespace views
+
+namespace ash {
+
+// LabelButton that has a rounded shape with a Material Design ink drop.
+class RoundedLabelButton : public views::LabelButton {
+ public:
+  RoundedLabelButton(views::ButtonListener* listener,
+                     const base::string16& text);
+  ~RoundedLabelButton() override;
+
+  // views::LabelButton:
+  gfx::Size CalculatePreferredSize() const override;
+  int GetHeightForWidth(int width) const override;
+  void PaintButtonContents(gfx::Canvas* canvas) override;
+  std::unique_ptr<views::InkDrop> CreateInkDrop() override;
+  std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override;
+  std::unique_ptr<views::InkDropHighlight> CreateInkDropHighlight()
+      const override;
+  std::unique_ptr<views::InkDropMask> CreateInkDropMask() const override;
+  const char* GetClassName() const override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(RoundedLabelButton);
+};
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_UNIFIED_ROUNDED_LABEL_BUTTON_H_
diff --git a/ash/system/unified/sign_out_button.cc b/ash/system/unified/sign_out_button.cc
index fa8cd08c..bf23fc8 100644
--- a/ash/system/unified/sign_out_button.cc
+++ b/ash/system/unified/sign_out_button.cc
@@ -6,86 +6,10 @@
 
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
-#include "ash/system/tray/tray_constants.h"
-#include "ash/system/tray/tray_popup_utils.h"
 #include "ash/system/user/login_status.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/gfx/canvas.h"
-#include "ui/views/animation/flood_fill_ink_drop_ripple.h"
-#include "ui/views/animation/ink_drop_highlight.h"
-#include "ui/views/animation/ink_drop_impl.h"
-#include "ui/views/animation/ink_drop_mask.h"
-#include "ui/views/border.h"
-#include "ui/views/view_class_properties.h"
 
 namespace ash {
 
-RoundedLabelButton::RoundedLabelButton(views::ButtonListener* listener,
-                                       const base::string16& text)
-    : views::LabelButton(listener, text) {
-  SetEnabledTextColors(kUnifiedMenuTextColor);
-  SetHorizontalAlignment(gfx::ALIGN_CENTER);
-  SetBorder(views::CreateEmptyBorder(gfx::Insets()));
-  label()->SetSubpixelRenderingEnabled(false);
-  label()->SetFontList(views::Label::GetDefaultFontList().Derive(
-      1, gfx::Font::NORMAL, gfx::Font::Weight::MEDIUM));
-  TrayPopupUtils::ConfigureTrayPopupButton(this);
-
-  auto path = std::make_unique<SkPath>();
-  path->addRoundRect(gfx::RectToSkRect(gfx::Rect(CalculatePreferredSize())),
-                     kTrayItemSize / 2, kTrayItemSize / 2);
-  SetProperty(views::kHighlightPathKey, path.release());
-}
-
-RoundedLabelButton::~RoundedLabelButton() = default;
-
-gfx::Size RoundedLabelButton::CalculatePreferredSize() const {
-  return gfx::Size(label()->GetPreferredSize().width() + kTrayItemSize,
-                   kTrayItemSize);
-}
-
-int RoundedLabelButton::GetHeightForWidth(int width) const {
-  return kTrayItemSize;
-}
-
-void RoundedLabelButton::PaintButtonContents(gfx::Canvas* canvas) {
-  gfx::Rect rect(GetContentsBounds());
-  cc::PaintFlags flags;
-  flags.setAntiAlias(true);
-  flags.setColor(kUnifiedMenuButtonColor);
-  flags.setStyle(cc::PaintFlags::kFill_Style);
-  canvas->DrawRoundRect(rect, kTrayItemSize / 2, flags);
-
-  views::LabelButton::PaintButtonContents(canvas);
-}
-
-std::unique_ptr<views::InkDrop> RoundedLabelButton::CreateInkDrop() {
-  return TrayPopupUtils::CreateInkDrop(this);
-}
-
-std::unique_ptr<views::InkDropRipple> RoundedLabelButton::CreateInkDropRipple()
-    const {
-  return TrayPopupUtils::CreateInkDropRipple(
-      TrayPopupInkDropStyle::FILL_BOUNDS, this,
-      GetInkDropCenterBasedOnLastEvent(), kUnifiedMenuIconColor);
-}
-
-std::unique_ptr<views::InkDropHighlight>
-RoundedLabelButton::CreateInkDropHighlight() const {
-  return TrayPopupUtils::CreateInkDropHighlight(
-      TrayPopupInkDropStyle::FILL_BOUNDS, this, kUnifiedMenuIconColor);
-}
-
-std::unique_ptr<views::InkDropMask> RoundedLabelButton::CreateInkDropMask()
-    const {
-  return std::make_unique<views::RoundRectInkDropMask>(size(), gfx::Insets(),
-                                                       kTrayItemSize / 2);
-}
-
-const char* RoundedLabelButton::GetClassName() const {
-  return "RoundedLabelButton";
-}
-
 SignOutButton::SignOutButton(views::ButtonListener* listener)
     : RoundedLabelButton(listener,
                          user::GetLocalizedSignOutStringForStatus(
diff --git a/ash/system/unified/sign_out_button.h b/ash/system/unified/sign_out_button.h
index 501b9e4..dff18ae2 100644
--- a/ash/system/unified/sign_out_button.h
+++ b/ash/system/unified/sign_out_button.h
@@ -5,32 +5,15 @@
 #ifndef ASH_SYSTEM_UNIFIED_SIGN_OUT_BUTTON_H_
 #define ASH_SYSTEM_UNIFIED_SIGN_OUT_BUTTON_H_
 
-#include "ui/views/controls/button/label_button.h"
+#include "ash/system/unified/rounded_label_button.h"
+#include "base/macros.h"
+
+namespace views {
+class ButtonListener;
+}  // namespace views
 
 namespace ash {
 
-// LabelButton that has rounded shape with Material Design ink drop.
-class RoundedLabelButton : public views::LabelButton {
- public:
-  RoundedLabelButton(views::ButtonListener* listener,
-                     const base::string16& text);
-  ~RoundedLabelButton() override;
-
-  // views::LabelButton:
-  gfx::Size CalculatePreferredSize() const override;
-  int GetHeightForWidth(int width) const override;
-  void PaintButtonContents(gfx::Canvas* canvas) override;
-  std::unique_ptr<views::InkDrop> CreateInkDrop() override;
-  std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override;
-  std::unique_ptr<views::InkDropHighlight> CreateInkDropHighlight()
-      const override;
-  std::unique_ptr<views::InkDropMask> CreateInkDropMask() const override;
-  const char* GetClassName() const override;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(RoundedLabelButton);
-};
-
 // Sign out button shown in TopShortcutView with TopShortcutButtons.
 // Shows the label like "Sign out", "Exit guest", etc. depending on the session
 // status. Not visible when not signed in.
diff --git a/ash/wm/desks/OWNERS b/ash/wm/desks/OWNERS
new file mode 100644
index 0000000..111b3ba
--- /dev/null
+++ b/ash/wm/desks/OWNERS
@@ -0,0 +1 @@
+afakhry@chromium.org
diff --git a/ash/wm/desks/desk.cc b/ash/wm/desks/desk.cc
index 7d933aa..adc51fe 100644
--- a/ash/wm/desks/desk.cc
+++ b/ash/wm/desks/desk.cc
@@ -12,10 +12,34 @@
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_transient_descendant_iterator.h"
 #include "ash/wm/window_util.h"
+#include "ash/wm/workspace/backdrop_controller.h"
+#include "ash/wm/workspace/workspace_layout_manager.h"
+#include "ash/wm/workspace_controller.h"
 #include "ui/wm/core/window_util.h"
 
 namespace ash {
 
+namespace {
+
+void UpdateBackdropController(aura::Window* desk_container) {
+  auto* workspace_controller = GetWorkspaceController(desk_container);
+  DCHECK(workspace_controller);
+  WorkspaceLayoutManager* layout_manager =
+      workspace_controller->layout_manager();
+  BackdropController* backdrop_controller =
+      layout_manager->backdrop_controller();
+  backdrop_controller->OnDeskContentChanged();
+}
+
+// Returns true if |window| can be managed by the desk, and therefore can be
+// moved out of the desk when the desk is removed.
+bool CanMoveWindowOutOfDeskContainer(aura::Window* window) {
+  // TODO(afakhry): Is there a better way to filter windows?
+  return CanIncludeWindowInMruList(window);
+}
+
+}  // namespace
+
 class DeskContainerObserver : public aura::WindowObserver {
  public:
   DeskContainerObserver(Desk* owner, aura::Window* container)
@@ -62,7 +86,12 @@
 }
 
 Desk::~Desk() {
-  DCHECK(windows_.empty()) << "DesksController should remove my windows first.";
+  for (auto* window : windows_) {
+    DCHECK(!CanMoveWindowOutOfDeskContainer(window))
+        << "DesksController should remove this desk's application windows "
+           "first.";
+    window->RemoveObserver(this);
+  }
 
   for (auto& observer : observers_) {
     observers_.RemoveObserver(&observer);
@@ -173,10 +202,18 @@
     auto target_desk_throttled =
         target_desk->GetScopedNotifyContentChangedDisabler();
 
-    for (auto* window : windows_)
-      MoveWindowToDeskInternal(window, target_desk);
+    auto iter = windows_.begin();
+    while (iter != windows_.end()) {
+      aura::Window* window = *iter;
+      // Do not move non-desk windows.
+      if (!CanMoveWindowOutOfDeskContainer(window)) {
+        ++iter;
+        continue;
+      }
 
-    windows_.clear();
+      MoveWindowToDeskInternal(window, target_desk);
+      iter = windows_.erase(iter);
+    }
   }
 
   NotifyContentChanged();
@@ -237,13 +274,25 @@
   if (!should_notify_content_changed_)
     return;
 
+  // Update the backdrop availability and visibility first before notifying
+  // observers.
+  UpdateDeskBackdrops();
+
   for (auto& observer : observers_)
     observer.OnContentChanged();
 }
 
+void Desk::UpdateDeskBackdrops() {
+  for (auto* root : Shell::GetAllRootWindows())
+    UpdateBackdropController(GetDeskContainerForRoot(root));
+}
+
 void Desk::MoveWindowToDeskInternal(aura::Window* window, Desk* target_desk) {
   DCHECK(windows_.contains(window));
 
+  DCHECK(CanMoveWindowOutOfDeskContainer(window))
+      << "Non-desk windows are not allowed to move out of the container.";
+
   window->RemoveObserver(this);
 
   // Add the window to the target desk before reparenting such that when the
diff --git a/ash/wm/desks/desk.h b/ash/wm/desks/desk.h
index 2e38fb2..98d0cbe 100644
--- a/ash/wm/desks/desk.h
+++ b/ash/wm/desks/desk.h
@@ -92,6 +92,10 @@
 
   void NotifyContentChanged();
 
+  // Updates the backdrop availability and visibility on the containers (on all
+  // roots) associated with this desk.
+  void UpdateDeskBackdrops();
+
  private:
   void MoveWindowToDeskInternal(aura::Window* window, Desk* target_desk);
 
diff --git a/ash/wm/desks/desks_controller.cc b/ash/wm/desks/desks_controller.cc
index a7e61ad..234c65f 100644
--- a/ash/wm/desks/desks_controller.cc
+++ b/ash/wm/desks/desks_controller.cc
@@ -204,6 +204,13 @@
 
   available_container_ids_.push(removed_desk->container_id());
 
+  // Avoid having stale backdrop state as a desk is removed while in overview
+  // mode, since the backdrop controller won't update the backdrop window as
+  // the removed desk's windows move out from the container. Therefore, we need
+  // to update it manually.
+  if (in_overview)
+    removed_desk->UpdateDeskBackdrops();
+
   DCHECK_LE(available_container_ids_.size(), desks_util::kMaxNumberOfDesks);
 }
 
diff --git a/ash/wm/desks/desks_unittests.cc b/ash/wm/desks/desks_unittests.cc
index 5af15a0f..8694d4b 100644
--- a/ash/wm/desks/desks_unittests.cc
+++ b/ash/wm/desks/desks_unittests.cc
@@ -22,12 +22,18 @@
 #include "ash/wm/overview/overview_item.h"
 #include "ash/wm/overview/overview_session.h"
 #include "ash/wm/splitview/split_view_drag_indicators.h"
+#include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
+#include "ash/wm/workspace/backdrop_controller.h"
+#include "ash/wm/workspace/workspace_layout_manager.h"
+#include "ash/wm/workspace_controller.h"
 #include "base/stl_util.h"
 #include "base/test/scoped_feature_list.h"
 #include "ui/aura/client/window_parenting_client.h"
+#include "ui/compositor_extra/shadow.h"
 #include "ui/events/test/event_generator.h"
+#include "ui/wm/core/shadow_controller.h"
 #include "ui/wm/core/window_util.h"
 
 namespace ash {
@@ -106,6 +112,15 @@
     event_generator->ReleaseLeftButton();
 }
 
+BackdropController* GetDeskBackdropController(const Desk* desk,
+                                              aura::Window* root) {
+  auto* workspace_controller =
+      GetWorkspaceController(desk->GetDeskContainerForRoot(root));
+  WorkspaceLayoutManager* layout_manager =
+      workspace_controller->layout_manager();
+  return layout_manager->backdrop_controller();
+}
+
 // Defines an observer to test DesksController notifications.
 class TestObserver : public DesksController::Observer {
  public:
@@ -867,12 +882,20 @@
   wm::ActivateWindow(window.get());
   EXPECT_EQ(window.get(), wm::GetActiveWindow());
 
+  ui::Shadow* shadow = ::wm::ShadowController::GetShadowForWindow(window.get());
+  ASSERT_TRUE(shadow);
+  ASSERT_TRUE(shadow->layer());
+  EXPECT_TRUE(shadow->layer()->GetTargetVisibility());
+
   auto* overview_controller = Shell::Get()->overview_controller();
   overview_controller->ToggleOverview();
   EXPECT_TRUE(overview_controller->InOverviewSession());
   auto* overview_grid = GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
   EXPECT_EQ(1u, overview_grid->size());
 
+  // While in overview mode, the window's shadow is hidden.
+  EXPECT_FALSE(shadow->layer()->GetTargetVisibility());
+
   auto* overview_session = overview_controller->overview_session();
   auto* overview_item =
       overview_session->GetOverviewItemForWindow(window.get());
@@ -907,6 +930,10 @@
   EXPECT_TRUE(overview_session->no_windows_widget_for_testing());
   EXPECT_TRUE(desk_2->windows().contains(window.get()));
   EXPECT_FALSE(overview_grid->drop_target_widget());
+
+  // After the window is dropped onto another desk, its shadow should be
+  // restored properly.
+  EXPECT_TRUE(shadow->layer()->GetTargetVisibility());
 }
 
 TEST_F(DesksTest, DragWindowToNonMiniViewPoints) {
@@ -1032,6 +1059,130 @@
   EXPECT_EQ(nullptr, wm::GetActiveWindow());
 }
 
+TEST_F(DesksTest, TabletModeBackdrops) {
+  auto* controller = DesksController::Get();
+  controller->NewDesk();
+  ASSERT_EQ(2u, controller->desks().size());
+  const Desk* desk_1 = controller->desks()[0].get();
+  const Desk* desk_2 = controller->desks()[1].get();
+
+  auto window = CreateTestWindow(gfx::Rect(0, 0, 250, 100));
+  wm::ActivateWindow(window.get());
+  EXPECT_EQ(window.get(), wm::GetActiveWindow());
+
+  // Enter tablet mode and expect that the backdrop is created only for desk_1,
+  // since it's the one that has a window in it.
+  // Avoid TabletModeController::OnGetSwitchStates() from disabling tablet mode.
+  base::RunLoop().RunUntilIdle();
+  Shell::Get()->tablet_mode_controller()->EnableTabletModeWindowManager(true);
+  auto* desk_1_backdrop_controller =
+      GetDeskBackdropController(desk_1, Shell::GetPrimaryRootWindow());
+  auto* desk_2_backdrop_controller =
+      GetDeskBackdropController(desk_2, Shell::GetPrimaryRootWindow());
+  ASSERT_TRUE(desk_1_backdrop_controller->backdrop_window());
+  EXPECT_TRUE(desk_1_backdrop_controller->backdrop_window()->IsVisible());
+  EXPECT_FALSE(desk_2_backdrop_controller->backdrop_window());
+
+  // Enter overview and expect that the backdrop is still present for desk_1 but
+  // hidden.
+  auto* overview_controller = Shell::Get()->overview_controller();
+  overview_controller->ToggleOverview();
+  EXPECT_TRUE(overview_controller->InOverviewSession());
+  ASSERT_TRUE(desk_1_backdrop_controller->backdrop_window());
+  EXPECT_FALSE(desk_1_backdrop_controller->backdrop_window()->IsVisible());
+
+  auto* overview_grid = GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
+  EXPECT_EQ(1u, overview_grid->size());
+
+  auto* overview_session = overview_controller->overview_session();
+  auto* overview_item =
+      overview_session->GetOverviewItemForWindow(window.get());
+  ASSERT_TRUE(overview_item);
+  const auto* desks_bar_view = overview_grid->GetDesksBarViewForTesting();
+  ASSERT_TRUE(desks_bar_view);
+
+  // Now drag it to desk_2's mini_view, so that it moves to desk_2. Expect that
+  // desk_1's backdrop is destroyed, while created (but still hidden) for
+  // desk_2.
+  auto* desk_2_mini_view = desks_bar_view->mini_views()[1].get();
+  EXPECT_EQ(desk_2, desk_2_mini_view->desk());
+  DragItemToPoint(overview_item,
+                  desk_2_mini_view->GetBoundsInScreen().CenterPoint(),
+                  GetEventGenerator());
+  EXPECT_TRUE(overview_controller->InOverviewSession());
+  EXPECT_TRUE(desk_2->windows().contains(window.get()));
+  EXPECT_FALSE(desk_1_backdrop_controller->backdrop_window());
+  ASSERT_TRUE(desk_2_backdrop_controller->backdrop_window());
+  EXPECT_FALSE(desk_2_backdrop_controller->backdrop_window()->IsVisible());
+
+  // Exit overview, and expect that desk_2's backdrop remains hidden since the
+  // desk is not activated yet.
+  overview_controller->ToggleOverview(
+      OverviewSession::EnterExitOverviewType::kImmediateExit);
+  EXPECT_FALSE(overview_controller->InOverviewSession());
+  EXPECT_FALSE(desk_1_backdrop_controller->backdrop_window());
+  ASSERT_TRUE(desk_2_backdrop_controller->backdrop_window());
+  EXPECT_FALSE(desk_2_backdrop_controller->backdrop_window()->IsVisible());
+
+  // Activate desk_2 and expect that its backdrop is now visible.
+  ActivateDesk(desk_2);
+  EXPECT_FALSE(desk_1_backdrop_controller->backdrop_window());
+  ASSERT_TRUE(desk_2_backdrop_controller->backdrop_window());
+  EXPECT_TRUE(desk_2_backdrop_controller->backdrop_window()->IsVisible());
+
+  // No backdrops after exiting tablet mode.
+  Shell::Get()->tablet_mode_controller()->EnableTabletModeWindowManager(false);
+  EXPECT_FALSE(desk_1_backdrop_controller->backdrop_window());
+  EXPECT_FALSE(desk_2_backdrop_controller->backdrop_window());
+}
+
+TEST_F(DesksTest, TabletModeDesksCreationRemovalCycle) {
+  auto window = CreateTestWindow(gfx::Rect(0, 0, 250, 100));
+  wm::ActivateWindow(window.get());
+  EXPECT_EQ(window.get(), wm::GetActiveWindow());
+
+  // Enter tablet mode. Avoid TabletModeController::OnGetSwitchStates() from
+  // disabling tablet mode.
+  base::RunLoop().RunUntilIdle();
+  Shell::Get()->tablet_mode_controller()->EnableTabletModeWindowManager(true);
+
+  auto* overview_controller = Shell::Get()->overview_controller();
+  overview_controller->ToggleOverview();
+  EXPECT_TRUE(overview_controller->InOverviewSession());
+  auto* desks_controller = DesksController::Get();
+
+  // Create and remove desks in a cycle while in overview mode. Expect as the
+  // containers are reused for new desks, their backdrop state are always
+  // correct, and there are no crashes as desks are removed.
+  for (size_t i = 0; i < 2 * desks_util::kMaxNumberOfDesks; ++i) {
+    desks_controller->NewDesk();
+    ASSERT_EQ(2u, desks_controller->desks().size());
+    const Desk* desk_1 = desks_controller->desks()[0].get();
+    const Desk* desk_2 = desks_controller->desks()[1].get();
+    auto* desk_1_backdrop_controller =
+        GetDeskBackdropController(desk_1, Shell::GetPrimaryRootWindow());
+    auto* desk_2_backdrop_controller =
+        GetDeskBackdropController(desk_2, Shell::GetPrimaryRootWindow());
+    {
+      SCOPED_TRACE("Check backdrops after desk creation");
+      ASSERT_TRUE(desk_1_backdrop_controller->backdrop_window());
+      EXPECT_FALSE(desk_1_backdrop_controller->backdrop_window()->IsVisible());
+      EXPECT_FALSE(desk_2_backdrop_controller->backdrop_window());
+    }
+    // Remove the active desk, and expect that now desk_2 should have a hidden
+    // backdrop, while the container of the removed desk_1 should have none.
+    desks_controller->RemoveDesk(desk_1);
+    {
+      SCOPED_TRACE("Check backdrops after desk removal");
+      EXPECT_TRUE(desk_2->is_active());
+      EXPECT_TRUE(DoesActiveDeskContainWindow(window.get()));
+      EXPECT_FALSE(desk_1_backdrop_controller->backdrop_window());
+      ASSERT_TRUE(desk_2_backdrop_controller->backdrop_window());
+      EXPECT_FALSE(desk_2_backdrop_controller->backdrop_window()->IsVisible());
+    }
+  }
+}
+
 class DesksWithSplitViewTest : public AshTestBase {
  public:
   DesksWithSplitViewTest() = default;
diff --git a/ash/wm/mru_window_tracker.cc b/ash/wm/mru_window_tracker.cc
index e69d9c24..7be9e7f 100644
--- a/ash/wm/mru_window_tracker.cc
+++ b/ash/wm/mru_window_tracker.cc
@@ -69,13 +69,6 @@
   return window->CanFocus();
 }
 
-// A predicate that determines whether |window| can be included in the MRU
-// window list.
-bool CanIncludeWindowInMruList(aura::Window* window) {
-  return ::wm::CanActivateWindow(window) &&
-         !wm::GetWindowState(window)->IsPip();
-}
-
 // A predicate that determines whether |window| can be included in the list
 // built for cycling through windows (alt + tab).
 bool CanIncludeWindowInCycleList(aura::Window* window) {
@@ -176,6 +169,11 @@
 
 }  // namespace
 
+bool CanIncludeWindowInMruList(aura::Window* window) {
+  return ::wm::CanActivateWindow(window) &&
+         !wm::GetWindowState(window)->IsPip();
+}
+
 //////////////////////////////////////////////////////////////////////////////
 // MruWindowTracker, public:
 
diff --git a/ash/wm/mru_window_tracker.h b/ash/wm/mru_window_tracker.h
index b77e250..3f28ca1 100644
--- a/ash/wm/mru_window_tracker.h
+++ b/ash/wm/mru_window_tracker.h
@@ -24,6 +24,10 @@
   kActiveDesk,
 };
 
+// A predicate that determines whether |window| can be included in the MRU
+// window list.
+bool CanIncludeWindowInMruList(aura::Window* window);
+
 // Maintains a most recently used list of windows. This is used for window
 // cycling using Alt+Tab and overview mode.
 class ASH_EXPORT MruWindowTracker : public ::wm::ActivationChangeObserver,
diff --git a/ash/wm/overview/cleanup_animation_observer_unittest.cc b/ash/wm/overview/cleanup_animation_observer_unittest.cc
index 6e80b49..1dde603 100644
--- a/ash/wm/overview/cleanup_animation_observer_unittest.cc
+++ b/ash/wm/overview/cleanup_animation_observer_unittest.cc
@@ -33,7 +33,6 @@
   }
 
   // OverviewDelegate:
-  void EndOverview() override {}
   void AddExitAnimationObserver(
       std::unique_ptr<DelayedAnimationObserver> animation_observer) override {
     animation_observer->SetOwner(this);
diff --git a/ash/wm/overview/delayed_animation_observer_impl_unittest.cc b/ash/wm/overview/delayed_animation_observer_impl_unittest.cc
index 0ce1d2a8..00076b8 100644
--- a/ash/wm/overview/delayed_animation_observer_impl_unittest.cc
+++ b/ash/wm/overview/delayed_animation_observer_impl_unittest.cc
@@ -27,7 +27,6 @@
   ~TestOverviewDelegate() override = default;
 
   // OverviewDelegate:
-  void EndOverview() override {}
   void AddExitAnimationObserver(
       std::unique_ptr<DelayedAnimationObserver> animation_observer) override {
     animation_observer->SetOwner(this);
diff --git a/ash/wm/overview/overview_controller.cc b/ash/wm/overview/overview_controller.cc
index d89d22d..eba82c0 100644
--- a/ash/wm/overview/overview_controller.cc
+++ b/ash/wm/overview/overview_controller.cc
@@ -393,6 +393,49 @@
   return true;
 }
 
+// TODO(flackr): Make OverviewController observe the activation of
+// windows, so we can remove OverviewDelegate.
+// TODO(sammiequon): Refactor to use a single entry point for overview.
+void OverviewController::EndOverview() {
+  if (!InOverviewSession())
+    return;
+
+  if (!occlusion_tracker_pauser_)
+    PauseOcclusionTracker();
+
+  if (!start_animations_.empty())
+    OnStartingAnimationComplete(/*canceled=*/true);
+  start_animations_.clear();
+
+  overview_session_->set_is_shutting_down(true);
+  // Do not show mask and show during overview shutdown.
+  overview_session_->UpdateMaskAndShadow();
+
+  for (auto& observer : observers_)
+    observer.OnOverviewModeEnding(overview_session_.get());
+  overview_session_->Shutdown();
+
+#if DCHECK_IS_ON()
+  const auto enter_exit_type = overview_session_->enter_exit_overview_type();
+  if (enter_exit_type ==
+          OverviewSession::EnterExitOverviewType::kImmediateExit &&
+      !delayed_animations_.empty()) {
+    // Immediate exit type implies no delayed exit animations at all, if we get
+    // here then this is a bug.
+    NOTREACHED();
+  }
+#endif
+
+  // Don't delete |overview_session_| yet since the stack is still using it.
+  base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE,
+                                                  overview_session_.release());
+  last_overview_session_time_ = base::Time::Now();
+  for (auto& observer : observers_)
+    observer.OnOverviewModeEnded();
+  if (delayed_animations_.empty())
+    OnEndingAnimationComplete(/*canceled=*/false);
+}
+
 bool OverviewController::InOverviewSession() const {
   return overview_session_ && !overview_session_->is_shutting_down();
 }
@@ -539,49 +582,6 @@
                                 weak_ptr_factory_.GetWeakPtr()));
 }
 
-// TODO(flackr): Make OverviewController observe the activation of
-// windows, so we can remove OverviewDelegate.
-// TODO(sammiequon): Refactor to use a single entry point for overview.
-void OverviewController::EndOverview() {
-  if (!InOverviewSession())
-    return;
-
-  if (!occlusion_tracker_pauser_)
-    PauseOcclusionTracker();
-
-  if (!start_animations_.empty())
-    OnStartingAnimationComplete(/*canceled=*/true);
-  start_animations_.clear();
-
-  overview_session_->set_is_shutting_down(true);
-  // Do not show mask and show during overview shutdown.
-  overview_session_->UpdateMaskAndShadow();
-
-  for (auto& observer : observers_)
-    observer.OnOverviewModeEnding(overview_session_.get());
-  overview_session_->Shutdown();
-
-#if DCHECK_IS_ON()
-  const auto enter_exit_type = overview_session_->enter_exit_overview_type();
-  if (enter_exit_type ==
-          OverviewSession::EnterExitOverviewType::kImmediateExit &&
-      !delayed_animations_.empty()) {
-    // Immediate exit type implies no delayed exit animations at all, if we get
-    // here then this is a bug.
-    NOTREACHED();
-  }
-#endif
-
-  // Don't delete |overview_session_| yet since the stack is still using it.
-  base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE,
-                                                  overview_session_.release());
-  last_overview_session_time_ = base::Time::Now();
-  for (auto& observer : observers_)
-    observer.OnOverviewModeEnded();
-  if (delayed_animations_.empty())
-    OnEndingAnimationComplete(/*canceled=*/false);
-}
-
 void OverviewController::AddExitAnimationObserver(
     std::unique_ptr<DelayedAnimationObserver> animation_observer) {
   // No delayed animations should be created when overview mode is set to exit
diff --git a/ash/wm/overview/overview_controller.h b/ash/wm/overview/overview_controller.h
index d86b7fa5..74991a1 100644
--- a/ash/wm/overview/overview_controller.h
+++ b/ash/wm/overview/overview_controller.h
@@ -35,6 +35,8 @@
   bool ToggleOverview(OverviewSession::EnterExitOverviewType type =
                           OverviewSession::EnterExitOverviewType::kNormal);
 
+  void EndOverview();
+
   // Returns true if overview mode is active.
   bool InOverviewSession() const;
 
@@ -71,7 +73,6 @@
   void DelayedUpdateMaskAndShadow();
 
   // OverviewDelegate:
-  void EndOverview() override;
   void AddExitAnimationObserver(
       std::unique_ptr<DelayedAnimationObserver> animation) override;
   void RemoveAndDestroyExitAnimationObserver(
diff --git a/ash/wm/overview/overview_delegate.h b/ash/wm/overview/overview_delegate.h
index 451a08e..0d9dda4 100644
--- a/ash/wm/overview/overview_delegate.h
+++ b/ash/wm/overview/overview_delegate.h
@@ -12,11 +12,9 @@
 namespace ash {
 class DelayedAnimationObserver;
 
-// Implement this class to handle the selection event from OverviewSession.
+// Implement this class to handle adding and removing animation observers.
 class ASH_EXPORT OverviewDelegate {
  public:
-  virtual void EndOverview() = 0;
-
   // Passes ownership of |animation_observer| to |this| delegate.
   virtual void AddExitAnimationObserver(
       std::unique_ptr<DelayedAnimationObserver> animation_observer) = 0;
diff --git a/ash/wm/overview/overview_session.cc b/ash/wm/overview/overview_session.cc
index 6195a49..d7cfc1b 100644
--- a/ash/wm/overview/overview_session.cc
+++ b/ash/wm/overview/overview_session.cc
@@ -143,6 +143,10 @@
   return bounds;
 }
 
+void EndOverview() {
+  Shell::Get()->overview_controller()->EndOverview();
+}
+
 }  // namespace
 
 OverviewSession::OverviewSession(OverviewDelegate* delegate)
@@ -337,10 +341,6 @@
   }
 }
 
-void OverviewSession::CancelSelection() {
-  delegate_->EndOverview();
-}
-
 void OverviewSession::OnGridEmpty(OverviewGrid* grid) {
   size_t index = 0;
   // If there are no longer any items on any of the grids, shutdown,
@@ -376,7 +376,7 @@
       Move(LEFT, true);
   }
   if (grid_list_.empty())
-    CancelSelection();
+    EndOverview();
   else
     PositionWindows(/*animate=*/false);
 }
@@ -685,7 +685,7 @@
     // Cancel overview session and do not restore focus when active window is
     // set to nullptr. This happens when removing a display.
     ResetFocusRestoreWindow(false);
-    CancelSelection();
+    EndOverview();
     return;
   }
 
@@ -723,7 +723,7 @@
 
   if (iter != windows.end())
     selected_item_ = iter->get();
-  CancelSelection();
+  EndOverview();
 }
 
 aura::Window* OverviewSession::GetOverviewFocusWindow() {
@@ -753,7 +753,7 @@
 
 void OverviewSession::OnDisplayRemoved(const display::Display& display) {
   // TODO(flackr): Keep window selection active on remaining displays.
-  CancelSelection();
+  EndOverview();
 }
 
 void OverviewSession::OnDisplayMetricsChanged(const display::Display& display,
@@ -800,7 +800,7 @@
   if (wm::IsSwitchableContainer(new_window->parent()) &&
       !::wm::GetTransientParent(new_window)) {
     // The new window is in one of the switchable containers, abort overview.
-    CancelSelection();
+    EndOverview();
     return;
   }
 }
@@ -833,7 +833,7 @@
       // Cancel overview unless we're in single split mode with no overview
       // windows.
       if (!(IsEmpty() && shell->split_view_controller()->InSplitViewMode()))
-        CancelSelection();
+        EndOverview();
       break;
     case ui::VKEY_UP:
       num_key_presses_++;
@@ -888,7 +888,7 @@
 
 void OverviewSession::OnShellDestroying() {
   // Cancel selection will call |Shutodnw()|, which will remove observer.
-  CancelSelection();
+  EndOverview();
 }
 
 void OverviewSession::OnSplitViewStateChanged(SplitViewState previous_state,
@@ -913,7 +913,7 @@
   if (state == SplitViewState::kBothSnapped || unsnappable_window_activated ||
       (Shell::Get()->split_view_controller()->InClamshellSplitViewMode() &&
        IsEmpty())) {
-    CancelSelection();
+    EndOverview();
     return;
   }
 
diff --git a/ash/wm/overview/overview_session.h b/ash/wm/overview/overview_session.h
index 105fa0f3..c523c429 100644
--- a/ash/wm/overview/overview_session.h
+++ b/ash/wm/overview/overview_session.h
@@ -109,9 +109,6 @@
   // Perform cleanup that cannot be done in the destructor.
   void Shutdown();
 
-  // Cancels window selection.
-  void CancelSelection();
-
   // Called when the last overview item from a grid is deleted.
   void OnGridEmpty(OverviewGrid* grid);
 
diff --git a/ash/wm/toplevel_window_event_handler.cc b/ash/wm/toplevel_window_event_handler.cc
index 1dedbab3..491dd17 100644
--- a/ash/wm/toplevel_window_event_handler.cc
+++ b/ash/wm/toplevel_window_event_handler.cc
@@ -37,65 +37,6 @@
 // window from the top of the screen in tablet mode.
 constexpr int kDragStartTopEdgeInset = 8;
 
-// Returns the toplevel window that should be dragged for a gesture event that
-// occurs in the HTCLIENT area of a window. Returns null if there shouldn't be
-// special casing for this HTCLIENT area gesture. This is used to drag app
-// windows which are fullscreened/maximized in tablet mode from the top of the
-// screen, which don't have a window frame.
-aura::Window* GetTargetForClientAreaGesture(ui::GestureEvent* event,
-                                            aura::Window* target) {
-  if (event->type() != ui::ET_GESTURE_SCROLL_BEGIN)
-    return nullptr;
-
-  views::Widget* widget = views::Widget::GetTopLevelWidgetForNativeView(target);
-  if (!widget)
-    return nullptr;
-
-  aura::Window* toplevel = widget->GetNativeWindow();
-
-  if (!Shell::Get()
-           ->tablet_mode_controller()
-           ->IsTabletModeWindowManagerEnabled()) {
-    return nullptr;
-  }
-  wm::WindowState* window_state = wm::GetWindowState(toplevel);
-  if (!window_state ||
-      (!window_state->IsMaximized() && !window_state->IsFullscreen() &&
-       !window_state->IsSnapped())) {
-    return nullptr;
-  }
-
-  if (toplevel->GetProperty(aura::client::kAppType) ==
-      static_cast<int>(AppType::BROWSER)) {
-    return nullptr;
-  }
-
-  if (event->details().scroll_y_hint() < 0)
-    return nullptr;
-
-  const gfx::Point location_in_screen =
-      event->target()->GetScreenLocation(*event);
-  const gfx::Rect work_area_bounds =
-      display::Screen::GetScreen()
-          ->GetDisplayNearestWindow(static_cast<aura::Window*>(event->target()))
-          .work_area();
-
-  gfx::Rect hit_bounds_in_screen(work_area_bounds);
-  hit_bounds_in_screen.set_height(kDragStartTopEdgeInset);
-
-  // There may be a bezel sensor off screen logically above
-  // |hit_bounds_in_screen|. Handles the ET_GESTURE_SCROLL_BEGIN event
-  // triggered in the bezel area too.
-  bool in_bezel = location_in_screen.y() < hit_bounds_in_screen.y() &&
-                  location_in_screen.x() >= hit_bounds_in_screen.x() &&
-                  location_in_screen.x() < hit_bounds_in_screen.right();
-
-  if (hit_bounds_in_screen.Contains(location_in_screen) || in_bezel)
-    return toplevel;
-
-  return nullptr;
-}
-
 // Returns whether |window| can be moved via a two finger drag given
 // the hittest results of the two fingers.
 bool CanStartTwoFingerMove(aura::Window* window,
@@ -558,6 +499,61 @@
   CompleteDrag(DragResult::REVERT);
 }
 
+aura::Window* ToplevelWindowEventHandler::GetTargetForClientAreaGesture(
+    ui::GestureEvent* event,
+    aura::Window* target) {
+  if (event->type() != ui::ET_GESTURE_SCROLL_BEGIN)
+    return nullptr;
+
+  views::Widget* widget = views::Widget::GetTopLevelWidgetForNativeView(target);
+  if (!widget)
+    return nullptr;
+
+  aura::Window* toplevel = widget->GetNativeWindow();
+
+  if (!Shell::Get()
+           ->tablet_mode_controller()
+           ->IsTabletModeWindowManagerEnabled()) {
+    return nullptr;
+  }
+  wm::WindowState* window_state = wm::GetWindowState(toplevel);
+  if (!window_state ||
+      (!window_state->IsMaximized() && !window_state->IsFullscreen() &&
+       !window_state->IsSnapped())) {
+    return nullptr;
+  }
+
+  if (toplevel->GetProperty(aura::client::kAppType) ==
+      static_cast<int>(AppType::BROWSER)) {
+    return nullptr;
+  }
+
+  if (event->details().scroll_y_hint() < 0)
+    return nullptr;
+
+  const gfx::Point location_in_screen =
+      event->target()->GetScreenLocation(*event);
+  const gfx::Rect work_area_bounds =
+      display::Screen::GetScreen()
+          ->GetDisplayNearestWindow(static_cast<aura::Window*>(event->target()))
+          .work_area();
+
+  gfx::Rect hit_bounds_in_screen(work_area_bounds);
+  hit_bounds_in_screen.set_height(kDragStartTopEdgeInset);
+
+  // There may be a bezel sensor off screen logically above
+  // |hit_bounds_in_screen|. Handles the ET_GESTURE_SCROLL_BEGIN event
+  // triggered in the bezel area too.
+  bool in_bezel = location_in_screen.y() < hit_bounds_in_screen.y() &&
+                  location_in_screen.x() >= hit_bounds_in_screen.x() &&
+                  location_in_screen.x() < hit_bounds_in_screen.right();
+
+  if (hit_bounds_in_screen.Contains(location_in_screen) || in_bezel)
+    return toplevel;
+
+  return nullptr;
+}
+
 ::wm::WindowMoveResult ToplevelWindowEventHandler::RunMoveLoop(
     aura::Window* source,
     const gfx::Vector2d& drag_offset,
diff --git a/ash/wm/toplevel_window_event_handler.h b/ash/wm/toplevel_window_event_handler.h
index a9c48be..b3a85f0 100644
--- a/ash/wm/toplevel_window_event_handler.h
+++ b/ash/wm/toplevel_window_event_handler.h
@@ -89,6 +89,14 @@
   // Returns true if there is a drag in progress.
   bool is_drag_in_progress() const { return window_resizer_.get() != nullptr; }
 
+  // Returns the toplevel window that should be dragged for a gesture event that
+  // occurs in the HTCLIENT area of a window. Returns null if there shouldn't be
+  // special casing for this HTCLIENT area gesture. This is used to drag app
+  // windows which are fullscreened/maximized in tablet mode from the top of the
+  // screen, which don't have a window frame.
+  static aura::Window* GetTargetForClientAreaGesture(ui::GestureEvent* event,
+                                                     aura::Window* target);
+
   // Returns the window that is currently handling gesture events and its
   // location.
   aura::Window* gesture_target() { return gesture_target_; }
diff --git a/ash/wm/wm_shadow_controller_delegate.cc b/ash/wm/wm_shadow_controller_delegate.cc
index 6ea4f9f..ca72bd0 100644
--- a/ash/wm/wm_shadow_controller_delegate.cc
+++ b/ash/wm/wm_shadow_controller_delegate.cc
@@ -5,6 +5,7 @@
 #include "ash/wm/wm_shadow_controller_delegate.h"
 
 #include "ash/shell.h"
+#include "ash/wm/desks/desks_util.h"
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/overview/overview_session.h"
 #include "ash/wm/splitview/split_view_controller.h"
@@ -35,8 +36,14 @@
     OverviewSession* overview_session = overview_controller->overview_session();
     // InOverviewSession() being true implies |overview_session| exists.
     DCHECK(overview_session);
-    if (overview_session->IsWindowInOverview(window))
+    // The window may be still in overview mode, but it belongs to a non-active
+    // desk, as it has just been dragged and dropped onto a non-active desk's
+    // mini_view. In this case, we shouldn't disable its shadow, so that it may
+    // restored properly.
+    if (desks_util::BelongsToActiveDesk(const_cast<aura::Window*>(window)) &&
+        overview_session->IsWindowInOverview(window)) {
       return false;
+    }
   }
 
   // The shadow state will be updated when the window is added to a parent.
diff --git a/ash/wm/workspace/backdrop_controller.cc b/ash/wm/workspace/backdrop_controller.cc
index eb292da..706d5bf 100644
--- a/ash/wm/workspace/backdrop_controller.cc
+++ b/ash/wm/workspace/backdrop_controller.cc
@@ -68,6 +68,11 @@
   DISALLOW_COPY_AND_ASSIGN(BackdropEventHandler);
 };
 
+bool InOverviewSession() {
+  OverviewController* overview_controller = Shell::Get()->overview_controller();
+  return overview_controller && overview_controller->InOverviewSession();
+}
+
 }  // namespace
 
 BackdropController::BackdropController(aura::Window* container)
@@ -114,6 +119,15 @@
   UpdateBackdrop();
 }
 
+void BackdropController::OnDeskContentChanged() {
+  // Desk content changes may result in the need to update the backdrop even
+  // when overview is active, since the mini_view should show updated content.
+  // Example: when the last window needing backdrop is moved to another desk,
+  // the backdrop should be destroyed from the source desk, while created for
+  // the target desk, and the mini_views of both desks should be updated.
+  UpdateBackdropInternal();
+}
+
 void BackdropController::SetBackdropDelegate(
     std::unique_ptr<BackdropDelegate> delegate) {
   delegate_ = std::move(delegate);
@@ -121,46 +135,11 @@
 }
 
 void BackdropController::UpdateBackdrop() {
-  // No need to continue update for recursive calls or in overview mode.
-  OverviewController* overview_controller = Shell::Get()->overview_controller();
-  if (pause_update_ ||
-      (overview_controller && overview_controller->InOverviewSession())) {
-    return;
-  }
-
-  aura::Window* window = GetTopmostWindowWithBackdrop();
-  if (!window) {
-    // Destroy the backdrop since no suitable window was found.
-    Hide(/*destroy=*/true);
-    return;
-  }
-  // We are changing the order of windows which will cause recursion.
-  base::AutoReset<bool> lock(&pause_update_, true);
-  EnsureBackdropWidget();
-  UpdateAccessibilityMode();
-
-  if (window == backdrop_window_ && backdrop_->IsVisible()) {
-    Layout();
-    return;
-  }
-  if (window->GetRootWindow() != backdrop_window_->GetRootWindow())
+  // Skip updating while overview mode is active, since the backdrop is hidden.
+  if (pause_update_ || InOverviewSession())
     return;
 
-  // Update the animation type of |backdrop_window_| based on current top most
-  // window with backdrop.
-  SetBackdropAnimationType(wm::GetWindowState(window)->CanMaximize()
-                               ? wm::WINDOW_VISIBILITY_ANIMATION_TYPE_STEP_END
-                               : ::wm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE);
-
-  Show();
-
-  SetBackdropAnimationType(::wm::WINDOW_VISIBILITY_ANIMATION_TYPE_DEFAULT);
-
-  // Since the backdrop needs to be immediately behind the window and the
-  // stacking functions only guarantee a "it's above or below", we need
-  // to re-arrange the two windows twice.
-  container_->StackChildAbove(backdrop_window_, window);
-  container_->StackChildAbove(window, backdrop_window_);
+  UpdateBackdropInternal();
 }
 
 aura::Window* BackdropController::GetTopmostWindowWithBackdrop() {
@@ -244,6 +223,45 @@
   UpdateBackdrop();
 }
 
+void BackdropController::UpdateBackdropInternal() {
+  // Skip the recursive updates.
+  if (pause_update_)
+    return;
+
+  // We are either destroying the backdrop widget or changing the order of
+  // windows which will cause recursion.
+  base::AutoReset<bool> lock(&pause_update_, true);
+  aura::Window* window = GetTopmostWindowWithBackdrop();
+  if (!window) {
+    // Destroy the backdrop since no suitable window was found.
+    Hide(/*destroy=*/true);
+    return;
+  }
+
+  EnsureBackdropWidget();
+  UpdateAccessibilityMode();
+
+  if (window == backdrop_window_ && backdrop_->IsVisible()) {
+    Layout();
+    return;
+  }
+  if (window->GetRootWindow() != backdrop_window_->GetRootWindow())
+    return;
+
+  // Update the animation type of |backdrop_window_| based on current top most
+  // window with backdrop.
+  SetBackdropAnimationType(wm::GetWindowState(window)->CanMaximize()
+                               ? wm::WINDOW_VISIBILITY_ANIMATION_TYPE_STEP_END
+                               : ::wm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE);
+
+  Show();
+
+  SetBackdropAnimationType(::wm::WINDOW_VISIBILITY_ANIMATION_TYPE_DEFAULT);
+
+  // Backdrop needs to be immediately behind the window.
+  container_->StackChildBelow(backdrop_window_, window);
+}
+
 void BackdropController::EnsureBackdropWidget() {
   if (backdrop_)
     return;
@@ -311,7 +329,11 @@
 
 void BackdropController::Show() {
   Layout();
-  backdrop_->Show();
+
+  // When overview is active, the backdrop should never be shown. However, it
+  // must be laid out, since it should show up properly in the mini_views.
+  if (!InOverviewSession())
+    backdrop_->Show();
 }
 
 void BackdropController::Hide(bool destroy, bool animate) {
diff --git a/ash/wm/workspace/backdrop_controller.h b/ash/wm/workspace/backdrop_controller.h
index a2aaf0c..24a64fa 100644
--- a/ash/wm/workspace/backdrop_controller.h
+++ b/ash/wm/workspace/backdrop_controller.h
@@ -55,6 +55,10 @@
   void OnPostWindowStateTypeChange();
   void OnDisplayMetricsChanged();
 
+  // Called when the desk content is changed in order to update the state of the
+  // backdrop even if overview mode is active.
+  void OnDeskContentChanged();
+
   void SetBackdropDelegate(std::unique_ptr<BackdropDelegate> delegate);
 
   // Update the visibility of, and restack the backdrop relative to
@@ -89,6 +93,8 @@
  private:
   friend class WorkspaceControllerTestApi;
 
+  void UpdateBackdropInternal();
+
   void EnsureBackdropWidget();
 
   void UpdateAccessibilityMode();
diff --git a/base/process/process_metrics.h b/base/process/process_metrics.h
index 16e9785..ec40c3d 100644
--- a/base/process/process_metrics.h
+++ b/base/process/process_metrics.h
@@ -99,16 +99,10 @@
 #endif
 
 #if defined(OS_CHROMEOS)
-  // /proc/<pid>/totmaps is a syscall that returns memory summary statistics for
-  // the process.
-  // totmaps is a Linux specific concept, currently only being used on ChromeOS.
-  // Do not attempt to extend this to other platforms.
-  //
   struct TotalsSummary {
-    size_t private_clean_kb;
-    size_t private_dirty_kb;
     size_t swap_kb;
   };
+  // Returns memory stats for the process.
   BASE_EXPORT TotalsSummary GetTotalsSummary() const;
 #endif
 
diff --git a/base/process/process_metrics_linux.cc b/base/process/process_metrics_linux.cc
index 0c119bd..d4dc92c 100644
--- a/base/process/process_metrics_linux.cc
+++ b/base/process/process_metrics_linux.cc
@@ -308,61 +308,9 @@
 #endif
 
 #if defined(OS_CHROMEOS)
-// Private, Shared and Proportional working set sizes are obtained from
-// /proc/<pid>/totmaps
 ProcessMetrics::TotalsSummary ProcessMetrics::GetTotalsSummary() const {
-  // The format of /proc/<pid>/totmaps is:
-  //
-  // Rss:                6120 kB
-  // Pss:                3335 kB
-  // Shared_Clean:       1008 kB
-  // Shared_Dirty:       4012 kB
-  // Private_Clean:         4 kB
-  // Private_Dirty:      1096 kB
-  // Referenced:          XXX kB
-  // Anonymous:           XXX kB
-  // AnonHugePages:       XXX kB
-  // Swap:                XXX kB
-  // Locked:              XXX kB
   ProcessMetrics::TotalsSummary summary = {};
-
-  const size_t kPrivate_CleanIndex = (4 * 3) + 1;
-  const size_t kPrivate_DirtyIndex = (5 * 3) + 1;
-  const size_t kSwapIndex = (9 * 3) + 1;
-
-  std::string totmaps_data;
-  {
-    FilePath totmaps_file = internal::GetProcPidDir(process_).Append("totmaps");
-    ThreadRestrictions::ScopedAllowIO allow_io;
-    bool ret = ReadFileToString(totmaps_file, &totmaps_data);
-    if (!ret || totmaps_data.length() == 0)
-      return summary;
-  }
-
-  std::vector<std::string> totmaps_fields = SplitString(
-      totmaps_data, kWhitespaceASCII, KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY);
-
-  DCHECK_EQ("Private_Clean:", totmaps_fields[kPrivate_CleanIndex - 1]);
-  DCHECK_EQ("Private_Dirty:", totmaps_fields[kPrivate_DirtyIndex - 1]);
-  DCHECK_EQ("Swap:", totmaps_fields[kSwapIndex-1]);
-
-  int private_clean_kb = 0;
-  int private_dirty_kb = 0;
-  int swap_kb = 0;
-  bool success = true;
-  success &=
-      StringToInt(totmaps_fields[kPrivate_CleanIndex], &private_clean_kb);
-  success &=
-      StringToInt(totmaps_fields[kPrivate_DirtyIndex], &private_dirty_kb);
-  success &= StringToInt(totmaps_fields[kSwapIndex], &swap_kb);
-
-  if (!success)
-    return summary;
-
-  summary.private_clean_kb = private_clean_kb;
-  summary.private_dirty_kb = private_dirty_kb;
-  summary.swap_kb = swap_kb;
-
+  summary.swap_kb = GetVmSwapBytes() >> 10;
   return summary;
 }
 #endif
diff --git a/base/test/launcher/test_launcher.cc b/base/test/launcher/test_launcher.cc
index 675182b..b093ea1 100644
--- a/base/test/launcher/test_launcher.cc
+++ b/base/test/launcher/test_launcher.cc
@@ -799,7 +799,10 @@
     tests_to_retry_.insert(result.full_name);
   }
 
-  results_tracker_.AddTestResult(result);
+  // There are no results for this tests,
+  // most likley due to another test failing in the same batch.
+  if (result.status != TestResult::TEST_SKIPPED)
+    results_tracker_.AddTestResult(result);
 
   // TODO(phajdan.jr): Align counter (padding).
   std::string status_line(StringPrintf("[%zu/%zu] %s ", test_finished_count_,
@@ -826,11 +829,8 @@
   // We just printed a status line, reset the watchdog timer.
   watchdog_timer_.Reset();
 
-  // Do not waste time on timeouts. We include tests with unknown results here
-  // because sometimes (e.g. hang in between unit tests) that's how a timeout
-  // gets reported.
-  if (result.status == TestResult::TEST_TIMEOUT ||
-      result.status == TestResult::TEST_UNKNOWN) {
+  // Do not waste time on timeouts.
+  if (result.status == TestResult::TEST_TIMEOUT) {
     test_broken_count_++;
   }
   if (!force_run_broken_tests_ && test_broken_count_ >= broken_threshold_) {
@@ -1381,15 +1381,6 @@
   // Number of retries in this iteration.
   size_t retry_count = 0;
   while (!tests_to_retry_.empty() && retry_count < retry_limit_) {
-    if (!force_run_broken_tests_ &&
-        tests_to_retry_.size() >= broken_threshold_) {
-      fprintf(stdout, "Too many failing tests (%zu), skipping retries.\n",
-              tests_to_retry_.size());
-      fflush(stdout);
-
-      results_tracker_.AddGlobalTag("BROKEN_TEST_SKIPPED_RETRIES");
-      return false;
-    }
     std::vector<std::string> test_names(tests_to_retry_.begin(),
                                         tests_to_retry_.end());
     tests_to_retry_.clear();
diff --git a/base/test/launcher/test_launcher_nacl_nonsfi.cc b/base/test/launcher/test_launcher_nacl_nonsfi.cc
index 53bdb4f..43cf899 100644
--- a/base/test/launcher/test_launcher_nacl_nonsfi.cc
+++ b/base/test/launcher/test_launcher_nacl_nonsfi.cc
@@ -124,12 +124,6 @@
     return cmd_line;
   }
 
-  void RelaunchTests(base::TestLauncher* test_launcher,
-                     const std::vector<std::string>& test_names,
-                     int launch_flags) override {
-    RunUnitTestsBatch(test_launcher, this, test_names, launch_flags);
-  }
-
   base::FilePath test_path_;
 };
 
diff --git a/base/test/launcher/unit_test_launcher.cc b/base/test/launcher/unit_test_launcher.cc
index 2e63baa..b965736 100644
--- a/base/test/launcher/unit_test_launcher.cc
+++ b/base/test/launcher/unit_test_launcher.cc
@@ -208,139 +208,138 @@
 }
 #endif  // defined(OS_WIN)
 
-// Interprets test results and reports to the test launcher. Returns true
-// on success.
-bool ProcessTestResults(
-    TestLauncher* test_launcher,
-    const std::vector<std::string>& test_names,
-    const base::FilePath& output_file,
-    const std::string& output,
-    int exit_code,
-    bool was_timeout,
-    std::vector<std::string>* tests_to_relaunch) {
+// Called if there are no test results, populates results with UNKNOWN results.
+// If There is only one test, will try to determine status by exit_code and
+// was_timeout.
+void ProcessMissingTestResults(const std::vector<std::string>& test_names,
+                               const std::string& output,
+                               bool was_timeout,
+                               bool exit_code,
+                               std::vector<TestResult>* results) {
+  // We do not have reliable details about test results (parsing test
+  // stdout is known to be unreliable).
+  fprintf(stdout,
+          "Failed to get out-of-band test success data, "
+          "dumping full stdio below:\n%s\n",
+          output.c_str());
+  fflush(stdout);
+
+  // There is only one test and no results.
+  // Try to determine status by exit code.
+  if (test_names.size() == 1) {
+    const std::string& test_name = test_names.front();
+    TestResult test_result;
+    test_result.full_name = test_name;
+
+    if (was_timeout) {
+      test_result.status = TestResult::TEST_TIMEOUT;
+    } else if (exit_code != 0) {
+      test_result.status = TestResult::TEST_FAILURE;
+    } else {
+      // It's strange case when test executed successfully,
+      // but we failed to read machine-readable report for it.
+      test_result.status = TestResult::TEST_UNKNOWN;
+    }
+
+    results->push_back(test_result);
+    return;
+  }
+  for (auto& test_name : test_names) {
+    TestResult test_result;
+    test_result.full_name = test_name;
+    test_result.status = TestResult::TEST_SKIPPED;
+    results->push_back(test_result);
+  }
+}
+
+// Interprets test results and reports to the test launcher.
+void ProcessTestResults(TestLauncher* test_launcher,
+                        const std::vector<std::string>& test_names,
+                        const base::FilePath& output_file,
+                        const std::string& output,
+                        int exit_code,
+                        bool was_timeout) {
   std::vector<TestResult> test_results;
   bool crashed = false;
   bool have_test_results =
       ProcessGTestOutput(output_file, &test_results, &crashed);
 
-  bool called_any_callback = false;
-
-  if (have_test_results) {
-    // TODO(phajdan.jr): Check for duplicates and mismatches between
-    // the results we got from XML file and tests we intended to run.
-    std::map<std::string, TestResult> results_map;
-    for (const auto& i : test_results)
-      results_map[i.full_name] = i;
-
-    bool had_interrupted_test = false;
-
-    // Results to be reported back to the test launcher.
-    std::vector<TestResult> final_results;
-
-    for (const auto& i : test_names) {
-      if (ContainsKey(results_map, i)) {
-        TestResult test_result = results_map[i];
-        if (test_result.status == TestResult::TEST_CRASH) {
-          had_interrupted_test = true;
-
-          if (was_timeout) {
-            // Fix up the test status: we forcibly kill the child process
-            // after the timeout, so from XML results it looks just like
-            // a crash.
-            test_result.status = TestResult::TEST_TIMEOUT;
-          }
-        } else if (test_result.status == TestResult::TEST_SUCCESS ||
-                   test_result.status == TestResult::TEST_FAILURE) {
-          // We run multiple tests in a batch with a timeout applied
-          // to the entire batch. It is possible that with other tests
-          // running quickly some tests take longer than the per-test timeout.
-          // For consistent handling of tests independent of order and other
-          // factors, mark them as timing out.
-          if (test_result.elapsed_time >
-              TestTimeouts::test_launcher_timeout()) {
-            test_result.status = TestResult::TEST_TIMEOUT;
-          }
-        }
-        test_result.output_snippet = GetTestOutputSnippet(test_result, output);
-        final_results.push_back(test_result);
-      } else if (had_interrupted_test) {
-        tests_to_relaunch->push_back(i);
-      } else {
-        // TODO(phajdan.jr): Explicitly pass the info that the test didn't
-        // run for a mysterious reason.
-        LOG(ERROR) << "no test result for " << i;
-        TestResult test_result;
-        test_result.full_name = i;
-        test_result.status = TestResult::TEST_UNKNOWN;
-        test_result.output_snippet = GetTestOutputSnippet(test_result, output);
-        final_results.push_back(test_result);
-      }
-    }
-
-    // TODO(phajdan.jr): Handle the case where processing XML output
-    // indicates a crash but none of the test results is marked as crashing.
-
-    if (final_results.empty())
-      return false;
-
-    bool has_non_success_test = false;
-    for (const auto& i : final_results) {
-      if (i.status != TestResult::TEST_SUCCESS) {
-        has_non_success_test = true;
-        break;
-      }
-    }
-
-    if (!has_non_success_test && exit_code != 0) {
-      // This is a bit surprising case: all tests are marked as successful,
-      // but the exit code was not zero. This can happen e.g. under memory
-      // tools that report leaks this way. Mark all tests as a failure on exit,
-      // and for more precise info they'd need to be retried serially.
-      for (auto& i : final_results)
-        i.status = TestResult::TEST_FAILURE_ON_EXIT;
-    }
-
-    for (auto& i : final_results) {
-      // Fix the output snippet after possible changes to the test result.
-      i.output_snippet = GetTestOutputSnippet(i, output);
-      test_launcher->OnTestFinished(i);
-      called_any_callback = true;
-    }
-  } else {
-    fprintf(stdout,
-            "Failed to get out-of-band test success data, "
-            "dumping full stdio below:\n%s\n",
-            output.c_str());
-    fflush(stdout);
-
-    // We do not have reliable details about test results (parsing test
-    // stdout is known to be unreliable).
-    if (test_names.size() == 1) {
-      // There is only one test. Try to determine status by exit code.
-      const std::string& test_name = test_names.front();
-      TestResult test_result;
-      test_result.full_name = test_name;
-
-      if (was_timeout) {
-        test_result.status = TestResult::TEST_TIMEOUT;
-      } else if (exit_code != 0) {
-        test_result.status = TestResult::TEST_FAILURE;
-      } else {
-        // It's strange case when test executed successfully,
-        // but we failed to read machine-readable report for it.
-        test_result.status = TestResult::TEST_UNKNOWN;
-      }
-
+  if (!have_test_results) {
+    ProcessMissingTestResults(test_names, output, was_timeout, exit_code,
+                              &test_results);
+    for (auto& test_result : test_results)
       test_launcher->OnTestFinished(test_result);
-      called_any_callback = true;
+    return;
+  }
+
+  // TODO(phajdan.jr): Check for duplicates and mismatches between
+  // the results we got from XML file and tests we intended to run.
+  std::map<std::string, TestResult> results_map;
+  for (const auto& i : test_results)
+    results_map[i.full_name] = i;
+
+  // Results to be reported back to the test launcher.
+  std::vector<TestResult> final_results;
+
+  for (const auto& i : test_names) {
+    if (ContainsKey(results_map, i)) {
+      TestResult test_result = results_map[i];
+      if (test_result.status == TestResult::TEST_CRASH) {
+        if (was_timeout) {
+          // Fix up the test status: we forcibly kill the child process
+          // after the timeout, so from XML results it looks just like
+          // a crash.
+          test_result.status = TestResult::TEST_TIMEOUT;
+        }
+      } else if (test_result.status == TestResult::TEST_SUCCESS ||
+                 test_result.status == TestResult::TEST_FAILURE) {
+        // We run multiple tests in a batch with a timeout applied
+        // to the entire batch. It is possible that with other tests
+        // running quickly some tests take longer than the per-test timeout.
+        // For consistent handling of tests independent of order and other
+        // factors, mark them as timing out.
+        if (test_result.elapsed_time > TestTimeouts::test_launcher_timeout()) {
+          test_result.status = TestResult::TEST_TIMEOUT;
+        }
+      }
+      test_result.output_snippet = GetTestOutputSnippet(test_result, output);
+      final_results.push_back(test_result);
     } else {
-      // There is more than one test. Retry them individually.
-      for (const std::string& test_name : test_names)
-        tests_to_relaunch->push_back(test_name);
+      // TODO(phajdan.jr): Explicitly pass the info that the test didn't
+      // run for a mysterious reason.
+      LOG(ERROR) << "no test result for " << i;
+      TestResult test_result;
+      test_result.full_name = i;
+      test_result.status = TestResult::TEST_SKIPPED;
+      test_result.output_snippet = GetTestOutputSnippet(test_result, output);
+      final_results.push_back(test_result);
+    }
+  }
+  // TODO(phajdan.jr): Handle the case where processing XML output
+  // indicates a crash but none of the test results is marked as crashing.
+
+  bool has_non_success_test = false;
+  for (const auto& i : final_results) {
+    if (i.status != TestResult::TEST_SUCCESS) {
+      has_non_success_test = true;
+      break;
     }
   }
 
-  return called_any_callback;
+  if (!has_non_success_test && exit_code != 0) {
+    // This is a bit surprising case: all tests are marked as successful,
+    // but the exit code was not zero. This can happen e.g. under memory
+    // tools that report leaks this way. Mark all tests as a failure on exit,
+    // and for more precise info they'd need to be retried serially.
+    for (auto& i : final_results)
+      i.status = TestResult::TEST_FAILURE_ON_EXIT;
+  }
+
+  for (auto& i : final_results) {
+    // Fix the output snippet after possible changes to the test result.
+    i.output_snippet = GetTestOutputSnippet(i, output);
+    test_launcher->OnTestFinished(i);
+  }
 }
 
 class UnitTestProcessLifetimeObserver : public ProcessLifetimeObserver {
@@ -418,14 +417,8 @@
     bool was_timeout,
     const std::string& output) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  std::vector<std::string> tests_to_relaunch;
   ProcessTestResults(test_launcher(), test_names(), output_file(), output,
-                     exit_code, was_timeout, &tests_to_relaunch);
-
-  if (!tests_to_relaunch.empty()) {
-    platform_delegate()->RelaunchTests(test_launcher(), tests_to_relaunch,
-                                       launch_flags());
-  }
+                     exit_code, was_timeout);
 
   // The temporary file's directory is also temporary.
   DeleteFile(output_file().DirName(), true);
@@ -471,17 +464,8 @@
     bool was_timeout,
     const std::string& output) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  std::vector<std::string> tests_to_relaunch;
-  bool called_any_callbacks =
-      ProcessTestResults(test_launcher(), test_names(), output_file(), output,
-                         exit_code, was_timeout, &tests_to_relaunch);
-
-  // There is only one test, there cannot be other tests to relaunch
-  // due to a crash.
-  DCHECK(tests_to_relaunch.empty());
-
-  // There is only one test, we should have called back with its result.
-  DCHECK(called_any_callbacks);
+  ProcessTestResults(test_launcher(), test_names(), output_file(), output,
+                     exit_code, was_timeout);
 
   // The temporary file's directory is also temporary.
   DeleteFile(output_file().DirName(), true);
@@ -666,19 +650,6 @@
   return std::string();
 }
 
-void DefaultUnitTestPlatformDelegate::RelaunchTests(
-    TestLauncher* test_launcher,
-    const std::vector<std::string>& test_names,
-    int launch_flags) {
-  // Relaunch requested tests in parallel, but only use single
-  // test per batch for more precise results (crashes, etc).
-  for (const std::string& test_name : test_names) {
-    std::vector<std::string> batch;
-    batch.push_back(test_name);
-    RunUnitTestsBatch(test_launcher, this, batch, launch_flags);
-  }
-}
-
 UnitTestLauncherDelegate::UnitTestLauncherDelegate(
     UnitTestPlatformDelegate* platform_delegate,
     size_t batch_limit,
diff --git a/base/test/launcher/unit_test_launcher.h b/base/test/launcher/unit_test_launcher.h
index 32ba25d..595923ac 100644
--- a/base/test/launcher/unit_test_launcher.h
+++ b/base/test/launcher/unit_test_launcher.h
@@ -78,11 +78,6 @@
   // no wrapper.
   virtual std::string GetWrapperForChildGTestProcess() = 0;
 
-  // Relaunch tests, e.g. after a crash.
-  virtual void RelaunchTests(TestLauncher* test_launcher,
-                             const std::vector<std::string>& test_names,
-                             int launch_flags) = 0;
-
  protected:
   ~UnitTestPlatformDelegate() = default;
 };
@@ -111,10 +106,6 @@
 
   std::string GetWrapperForChildGTestProcess() override;
 
-  void RelaunchTests(TestLauncher* test_launcher,
-                     const std::vector<std::string>& test_names,
-                     int launch_flags) override;
-
   ScopedTempDir temp_dir_;
 
   DISALLOW_COPY_AND_ASSIGN(DefaultUnitTestPlatformDelegate);
diff --git a/build/android/constant_pool_refs_to_keep_rules.py b/build/android/constant_pool_refs_to_keep_rules.py
index 7a06efc5..b2452d7 100644
--- a/build/android/constant_pool_refs_to_keep_rules.py
+++ b/build/android/constant_pool_refs_to_keep_rules.py
@@ -19,6 +19,17 @@
 # system APIs and are already included in ProGuard configs.
 _IGNORED_PACKAGES = ['java', 'android', 'org.w3c', 'org.xml', 'dalvik']
 
+# Classes in _WHITELIST_PACKAGES are support libraries compiled into chrome
+# that must bypass the _IGNORED_PACKAGES.
+_WHITELIST_PACKAGES = ['android.support']
+
+# TODO(https://crbug.com/968769): Filter may be too broad.
+# Classes in _DFM_FEATURES will be excluded from "keep all members" rule.
+_DFM_FEATURES = [
+    'org.chromium.chrome.autofill_assistant', 'org.chromium.chrome.tab_ui',
+    'org.chromium.chrome.browser.tasks.tab_management', 'org.chromium.chrome.vr'
+]
+
 # Mapping for translating Java bytecode type identifiers to source code type
 # identifiers.
 _TYPE_IDENTIFIER_MAP = {
@@ -91,6 +102,22 @@
     dep_refs[class_name] = [keep_entry]
 
 
+def should_include_class_path(class_path):
+  """ Check whether a class_path should be added as keep rule.
+      Conditions:
+        - Class is auto-generated (Lambdas/Nested, for example $)
+        - Class is not in a DFM Module
+        - Class is not in a black/white listed package
+    """
+  nested_class = '$' in class_path
+  not_in_dfm = all(not class_path.startswith(f) for f in _DFM_FEATURES)
+  allowed_packages = not (any(
+      class_path.startswith(p)
+      for p in _IGNORED_PACKAGES) and all(not class_path.startswith(p)
+                                          for p in _WHITELIST_PACKAGES))
+  return nested_class or (not_in_dfm and allowed_packages)
+
+
 def main(argv):
   dep_refs = defaultdict(list)
   extended_and_implemented_classes = set()
@@ -109,8 +136,10 @@
   with open(args.input_file, 'r') as constant_pool_refs:
     for line in constant_pool_refs:
       line = line.rstrip().replace('/', '.')
-      # Ignore any references specified by the list of _IGNORED_PACKAGES.
-      if any(line.startswith(package) for package in _IGNORED_PACKAGES):
+      # Ignore any references specified by the list of
+      # _IGNORED_PACKAGES and not in _WHITELIST_PACKAGES.
+      if (any(line.startswith(p) for p in _IGNORED_PACKAGES)
+          and all(not line.startswith(p) for p in _WHITELIST_PACKAGES)):
         continue
 
       reflist = line.split(',')
@@ -139,6 +168,11 @@
       if class_name.startswith('['):
         continue
 
+      # Ignore R(esources) files that are from the same module.
+      if ('$' in class_name
+          and any(class_name.startswith(f) for f in _DFM_FEATURES)):
+        continue
+
       # If member_info starts with '(', member is a method, otherwise member
       # is a field.
       # Format keep entries as per ProGuard documentation
@@ -151,6 +185,18 @@
           return_type = ''
         else:
           return_type = translate_single_type(return_type)[0]
+
+        # Include types of function arguments.
+        for arg_type in args_list:
+          if should_include_class_path(arg_type):
+            extended_and_implemented_classes.add(arg_type)
+
+        # Include the actual class when it's a constructor.
+        if member_name == '<init>':
+          if should_include_class_path(class_name):
+            extended_and_implemented_classes.add(class_name)
+          continue
+
         keep_entry = '%s %s(%s);' % (return_type, member_name,
                                      ', '.join(args_list))
       else:
@@ -162,14 +208,14 @@
   with open(args.output_file, 'w') as keep_rules:
     # Write super classes and implemented interfaces to keep rules.
     for super_class in sorted(extended_and_implemented_classes):
-      keep_rules.write(
-          '-keep,allowobfuscation class %s { *; }\n' % (super_class.rstrip()))
+      keep_rules.write('-keep class %s { *; }\n' % (super_class.rstrip()))
       keep_rules.write('\n')
     # Write all other class references to keep rules.
     for c in sorted(dep_refs.iterkeys()):
+      if c in extended_and_implemented_classes:
+        continue
       class_keeps = '\n  '.join(dep_refs[c])
-      keep_rules.write(
-          '-keep,allowobfuscation class %s {\n  %s\n}\n' % (c, class_keeps))
+      keep_rules.write('-keep class %s {\n  %s\n}\n' % (c, class_keeps))
       keep_rules.write('\n')
 
 
diff --git a/build/android/constant_pool_refs_to_keep_rules_test.py b/build/android/constant_pool_refs_to_keep_rules_test.py
new file mode 100644
index 0000000..6f281e2
--- /dev/null
+++ b/build/android/constant_pool_refs_to_keep_rules_test.py
@@ -0,0 +1,91 @@
+# 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 unittest
+import re
+import os
+
+
+class TestProguardRuleGeneration(unittest.TestCase):
+  """
+  This script is used to test a ProGuard keep rules for the purposes
+  of maintaining compatibility between async DFMs and synchronously
+  proguarded modules.
+
+  The rules are often generated by constant_pool_refs_to_keep_rules.py
+
+  This test can be run manually. Example:
+  python build/android/constant_pool_refs_to_keep_rules_test.py -v
+  """
+
+  # Make sure this variable is set accordingly.
+  # It should point to a proguard file.
+  PROGUARD_FILE_PATH = os.path.join(
+      os.path.dirname(__file__),
+      "../../chrome/android/features/tab_ui/proguard_async.flags")
+
+  def test_TabUI_HasRules(self):
+    """
+    Ensures that a few of the rules used in tabs_ui module are included.
+    Although this is far from 100% deterministic, these rules are
+    created by code that exercise different parts of the rule generation code.
+    """
+
+    rules = set()
+    with open(self.PROGUARD_FILE_PATH, 'r') as proguard_rules:
+      for line in proguard_rules:
+        if line.startswith('-keep'):
+          rule = re.search('class (.+?) {', line).group(1)
+          rules.add(rule)
+
+    # The following rules test most of the use cases for
+    # rules that can be added automatically.
+    self.assertIn('org.chromium.ui.modelutil.PropertyModel', rules)
+    self.assertIn('org.chromium.ui.modelutil.PropertyModel', rules)
+    self.assertIn('org.chromium.ui.modelutil.PropertyKey', rules)
+    self.assertIn('org.chromium.chrome.browser.toolbar.ToolbarManager', rules)
+    self.assertIn('org.chromium.base.Supplier', rules)
+    self.assertIn('android.support.v7.widget.helper.ItemTouchHelper', rules)
+    self.assertIn(
+        'android.support.v7.widget.helper.ItemTouchHelper$SimpleCallback',
+        rules)
+    self.assertIn('android.support.v7.widget.helper.ItemTouchHelper$Callback',
+                  rules)
+    self.assertIn('android.support.v4.content.ContextCompat', rules)
+    self.assertIn('android.support.v7.widget.GridLayoutManager', rules)
+    self.assertIn('android.support.v4.content.res.ResourcesCompat', rules)
+    self.assertIn(
+        'org.chromium.chrome.browser.tasks.tabgroup.TabGroupModelFilter', rules)
+    self.assertIn('android.support.v7.widget.RecyclerView$ViewHolder', rules)
+    self.assertIn('android.support.v7.widget.RecyclerView', rules)
+    self.assertIn('org.chromium.ui.modelutil.SimpleRecyclerViewMcpBase', rules)
+    self.assertIn('org.chromium.ui.modelutil.RecyclerViewAdapter', rules)
+
+    # The following rules need to be added manually.
+    self.assertNotIn(
+        'org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager' +
+        '$FullscreenListener$$CC', rules)
+    self.assertNotIn(
+        'org.chromium.chrome.browser.widget.bottomsheet.BottomSheet' +
+        '$BottomSheetContent$$CC', rules)
+    self.assertNotIn('org.chromium.ui.widget.RoundedCornerImageView', rules)
+    self.assertNotIn(
+        'android.support.v4.graphics.drawable.RoundedBitmapDrawable', rules)
+
+  def test_TabUI_HasNoDuplicateRules(self):
+    """
+    Ensures that there are no duplicate keep rules
+    """
+
+    rules = set()
+    with open(self.PROGUARD_FILE_PATH, 'r') as proguard_rules:
+      for line in proguard_rules:
+        if line.startswith('-keep'):
+          rule = re.search('class (.+?) {', line).group(1)
+          self.assertNotIn(rule, rules)
+          rules.add(rule)
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index 5b476a2..9508d9c 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -4353,9 +4353,11 @@
               build_config = _module.build_config
               repackage_classes = "ap${_async_package_number}"
 
-              # Pass mapping file of synchronous proguarding run to async module proguarding
-              # runs to preserve compatibility.
-              apply_mapping = _sync_proguard_mapping_path
+              # TODO(https://crbug.com/952858): R8 currently doesn't handle
+              # applying mapping files.
+              # Pass mapping file of synchronous proguarding run to async
+              # module proguarding runs to preserve compatibility.
+              # apply_mapping = _sync_proguard_mapping_path
 
               deps = [
                 _module.module_target,
diff --git a/chrome/VERSION b/chrome/VERSION
index 4968a2b..e878b53f 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=77
 MINOR=0
-BUILD=3810
+BUILD=3813
 PATCH=0
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index db75a65..2239c19 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -12,6 +12,7 @@
 import("//chrome/android/chrome_public_apk_tmpl.gni")
 import(
     "//chrome/android/features/autofill_assistant/autofill_assistant_module_tmpl.gni")
+import("//chrome/android/features/tab_ui/buildflags.gni")
 import("//chrome/android/features/tab_ui/tab_management_java_sources.gni")
 import("//chrome/android/features/tab_ui/tab_ui_module_tmpl.gni")
 import("//chrome/android/features/vr/public_vr_java_sources.gni")
@@ -475,6 +476,12 @@
       "//chrome/android/features/vr/proguard_async_manual.flags",
     ]
   }
+  if (async_tab_ui) {
+    proguard_configs += [
+      "//chrome/android/features/tab_ui/proguard_async.flags",
+      "//chrome/android/features/tab_ui/proguard_async_manual.flags",
+    ]
+  }
 
   processor_args_javac = [ "dagger.fastInit=enabled" ]
 }
@@ -491,6 +498,10 @@
   if (disable_autofill_assistant_dfm) {
     deps += [ "//chrome/android/features/autofill_assistant:java" ]
   }
+
+  if (disable_tab_ui_dfm) {
+    deps += [ "//chrome/android/features/tab_ui:java" ]
+  }
 }
 
 # This is a list of all base module jni headers. New features should add their
@@ -2342,6 +2353,15 @@
         },
       ]
     }
+    if (!disable_tab_ui_dfm) {
+      extra_modules += [
+        {
+          name = "tab_ui"
+          module_target = ":${target_name}__tab_ui_bundle_module"
+          proguard_async = async_tab_ui
+        },
+      ]
+    }
   }
 }
 
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index aa68923..c785aa3 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -1464,6 +1464,7 @@
   "java/src/org/chromium/chrome/browser/suggestions/TileGroup.java",
   "java/src/org/chromium/chrome/browser/suggestions/TileGroupDelegateImpl.java",
   "java/src/org/chromium/chrome/browser/suggestions/TileRenderer.java",
+  "java/src/org/chromium/chrome/browser/suggestions/tile/TopSitesTileView.java",
   "java/src/org/chromium/chrome/browser/survey/ChromeSurveyController.java",
   "java/src/org/chromium/chrome/browser/survey/SurveyController.java",
   "java/src/org/chromium/chrome/browser/sync/GoogleServiceAuthError.java",
diff --git a/chrome/android/chrome_junit_test_java_sources.gni b/chrome/android/chrome_junit_test_java_sources.gni
index 5d4bbee7..3668ba2 100644
--- a/chrome/android/chrome_junit_test_java_sources.gni
+++ b/chrome/android/chrome_junit_test_java_sources.gni
@@ -183,6 +183,7 @@
   "junit/src/org/chromium/chrome/browser/tabstate/TabStateUnitTest.java",
   "junit/src/org/chromium/chrome/browser/tasks/EngagementTimeUtilTest.java",
   "junit/src/org/chromium/chrome/browser/tasks/JourneyManagerTest.java",
+  "junit/src/org/chromium/chrome/browser/tasks/tabgroup/TabGroupModelFilterUnitTest.java",
   "junit/src/org/chromium/chrome/browser/toolbar/ToolbarSecurityIconTest.java",
   "junit/src/org/chromium/chrome/browser/usage_stats/EventTrackerTest.java",
   "junit/src/org/chromium/chrome/browser/usage_stats/PageViewObserverTest.java",
diff --git a/chrome/android/chrome_public_apk_tmpl.gni b/chrome/android/chrome_public_apk_tmpl.gni
index 2919c64a..07a858e2 100644
--- a/chrome/android/chrome_public_apk_tmpl.gni
+++ b/chrome/android/chrome_public_apk_tmpl.gni
@@ -255,10 +255,7 @@
       deps += [ ":$_unwind_asset" ]
     }
 
-    deps += [
-      "//chrome/android:chrome_all_java",
-      "//chrome/android/features/tab_ui:java",
-    ]
+    deps += [ "//chrome/android:chrome_all_java" ]
 
     if (!defined(version_code)) {
       if (_is_trichrome) {
diff --git a/chrome/android/features/tab_ui/BUILD.gn b/chrome/android/features/tab_ui/BUILD.gn
index abf0a89..7780b92 100644
--- a/chrome/android/features/tab_ui/BUILD.gn
+++ b/chrome/android/features/tab_ui/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import("//build/config/android/rules.gni")
+import("//chrome/android/features/tab_ui/buildflags.gni")
 import("//chrome/common/features.gni")
 
 android_resources("java_resources") {
@@ -14,6 +15,10 @@
 }
 
 android_library("java") {
+  # Add this flag to prevent build hooks to be included in this module
+  # as it's already included in the base module
+  no_build_hooks = true
+
   java_files = [
     "java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupUtils.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherCoordinator.java",
@@ -73,4 +78,8 @@
     "//third_party/android_deps:com_android_support_recyclerview_v7_java",
     "//ui/android:ui_java",
   ]
+
+  if (async_tab_ui) {
+    proguard_configs = [ "//base/android/proguard/chromium_code.flags" ]
+  }
 }
diff --git a/chrome/android/features/tab_ui/buildflags.gni b/chrome/android/features/tab_ui/buildflags.gni
new file mode 100644
index 0000000..0637699
--- /dev/null
+++ b/chrome/android/features/tab_ui/buildflags.gni
@@ -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.
+
+declare_args() {
+  # Controls the feature being a DFM or not.
+  disable_tab_ui_dfm = true
+
+  # Whether to create tab_ui module as an asynchronous DFM.
+  async_tab_ui = false
+}
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 757354dc..21ee7c3 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
@@ -85,8 +85,10 @@
                 mMediator::getCreateGroupButtonOnClickListener, gridCardOnClickListenerProvider,
                 compositorViewHolder, compositorViewHolder.getDynamicResourceLoader(), true,
                 org.chromium.chrome.tab_ui.R.layout.grid_tab_switcher_layout, COMPONENT_NAME);
-        HistoryNavigationLayout navigation =
-                compositorViewHolder.findViewById(R.id.history_navigation);
+
+        HistoryNavigationLayout navigation = compositorViewHolder.findViewById(
+                org.chromium.chrome.tab_ui.R.id.history_navigation);
+
         navigation.setNavigationDelegate(HistoryNavigationDelegate.createForTabSwitcher(
                 context, backPress, tabModelSelector::getCurrentTab));
         mContainerViewChangeProcessor = PropertyModelChangeProcessor.create(containerViewModel,
@@ -113,6 +115,7 @@
 
     @Override
     public void postHiding() {
+        mTabGridCoordinator.postHiding();
         mMediator.postHiding();
     }
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogParent.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogParent.java
index 47c8ed0d..e543812 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogParent.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogParent.java
@@ -75,7 +75,7 @@
         mDialogContainerView = new LinearLayout(context);
         mDialogContainerView.setLayoutParams(mContainerParams);
         mDialogContainerView.setBackgroundColor(ApiCompatibilityUtils.getColor(
-                context.getResources(), R.color.modern_primary_color));
+                context.getResources(), org.chromium.chrome.R.color.modern_primary_color));
         mDialogContainerView.setOrientation(LinearLayout.VERTICAL);
         mScrimView = new ScrimView(context, null, backgroundView);
         mPopupWindow = new PopupWindow(backgroundView, 0, 0);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewHolder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewHolder.java
index 9b1bba9..ab77adc 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewHolder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewHolder.java
@@ -39,8 +39,8 @@
         this.thumbnail = itemView.findViewById(R.id.tab_thumbnail);
         this.title = itemView.findViewById(R.id.tab_title);
         // TODO(yuezhanggg): Remove this when the strip is properly tinted. (crbug/939915)
-        title.setTextColor(
-                ContextCompat.getColor(itemView.getContext(), R.color.default_text_color_dark));
+        title.setTextColor(ContextCompat.getColor(
+                itemView.getContext(), org.chromium.chrome.R.color.default_text_color_dark));
         this.favicon = itemView.findViewById(R.id.tab_favicon);
         this.closeButton = itemView.findViewById(R.id.close_button);
         this.createGroupButton = itemView.findViewById(R.id.create_group_button);
@@ -49,8 +49,8 @@
         if (sCloseButtonBitmapWeakRef == null || sCloseButtonBitmapWeakRef.get() == null) {
             int closeButtonSize =
                     (int) itemView.getResources().getDimension(R.dimen.tab_grid_close_button_size);
-            Bitmap bitmap =
-                    BitmapFactory.decodeResource(itemView.getResources(), R.drawable.btn_close);
+            Bitmap bitmap = BitmapFactory.decodeResource(
+                    itemView.getResources(), org.chromium.chrome.R.drawable.btn_close);
             sCloseButtonBitmapWeakRef = new WeakReference<>(
                     Bitmap.createScaledBitmap(bitmap, closeButtonSize, closeButtonSize, true));
             bitmap.recycle();
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java
index 551bee06..09ad36d 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java
@@ -97,14 +97,11 @@
             @Override
             public void willCloseTab(Tab tab, boolean animate) {
                 if (!mIsTabGroupUiVisible) return;
-                Tab currentTab = mTabModelSelector.getCurrentTab();
-                if (currentTab == null) mResetHandler.resetSheetWithListOfTabs(null);
-                int tabsCount = mTabModelSelector.getTabModelFilterProvider()
-                                        .getCurrentTabModelFilter()
-                                        .getRelatedTabList(currentTab.getId())
-                                        .size();
+                List<Tab> group = mTabModelSelector.getTabModelFilterProvider()
+                                          .getCurrentTabModelFilter()
+                                          .getRelatedTabList(tab.getId());
 
-                mIsClosingAGroup = tabsCount == 0;
+                mIsClosingAGroup = group.size() == 0;
             }
 
             @Override
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
index 6564eaf55..73e27002 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
@@ -196,6 +196,10 @@
         mRecyclerView.prepareOverview();
     }
 
+    void postHiding() {
+        mRecyclerView.postHiding();
+    }
+
     /**
      * Destroy any members that needs clean up.
      */
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 b84ca82..7951ff3 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
@@ -340,6 +340,9 @@
                         instanceof TabGroupModelFilter) {
             mTabGroupObserver = new TabGroupModelFilter.Observer() {
                 @Override
+                public void didMergeTabToGroup(Tab movedTab, int selectedTabIdInGroup) {}
+
+                @Override
                 public void didMoveWithinGroup(
                         Tab movedTab, int tabModelOldIndex, int tabModelNewIndex) {
                     int curPosition = mModel.indexFromId(movedTab.getId());
@@ -522,9 +525,8 @@
                 mModel.get(i).set(TabProperties.IS_SELECTED, isSelected);
 
                 if (mThumbnailProvider != null && isSelected) {
-                    // TODO(crbug.com/964406): should force update but it's too slow.
-                    ThumbnailFetcher callback =
-                            new ThumbnailFetcher(mThumbnailProvider, tab, false);
+                    // TODO(crbug.com/968829): make force updating faster.
+                    ThumbnailFetcher callback = new ThumbnailFetcher(mThumbnailProvider, tab, true);
                     mModel.get(i).set(TabProperties.THUMBNAIL_FETCHER, callback);
                 }
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java
index 361a7e9..98e0c9f6 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java
@@ -194,14 +194,18 @@
                 mFadeOutAnimator = null;
                 setVisibility(View.INVISIBLE);
                 mListener.finishedHiding();
-
-                if (mDynamicView != null) mDynamicView.dropCachedBitmap();
             }
         });
         mFadeOutAnimator.start();
         if (!animate) mFadeOutAnimator.end();
     }
 
+    void postHiding() {
+        if (mDynamicView != null) {
+            mDynamicView.dropCachedBitmap();
+        }
+    }
+
     private void endAllAnimations() {
         if (mFadeInAnimator != null) {
             mFadeInAnimator.end();
diff --git a/chrome/android/features/tab_ui/proguard_async.flags b/chrome/android/features/tab_ui/proguard_async.flags
new file mode 100644
index 0000000..a5c42ce
--- /dev/null
+++ b/chrome/android/features/tab_ui/proguard_async.flags
@@ -0,0 +1,1034 @@
+# 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.
+
+-keep class android.content.res.Resources$Theme { *; }
+
+-keep class android.support.v7.widget.GridLayoutManager { *; }
+
+-keep class android.support.v7.widget.LinearLayoutManager { *; }
+
+-keep class android.support.v7.widget.RecyclerView { *; }
+
+-keep class android.support.v7.widget.RecyclerView$Adapter { *; }
+
+-keep class android.support.v7.widget.RecyclerView$LayoutManager { *; }
+
+-keep class android.support.v7.widget.RecyclerView$ViewHolder { *; }
+
+-keep class android.support.v7.widget.helper.ItemTouchHelper { *; }
+
+-keep class android.support.v7.widget.helper.ItemTouchHelper$Callback { *; }
+
+-keep class android.support.v7.widget.helper.ItemTouchHelper$SimpleCallback { *; }
+
+-keep class android.view.View$OnClickListener { *; }
+
+-keep class android.widget.PopupWindow$OnDismissListener { *; }
+
+-keep class boolean { *; }
+
+-keep class float { *; }
+
+-keep class gen._chrome._android._features._tab_ui._java_resources.srcjar.R$anim { *; }
+
+-keep class gen._chrome._android._features._tab_ui._java_resources.srcjar.R$animator { *; }
+
+-keep class gen._chrome._android._features._tab_ui._java_resources.srcjar.R$array { *; }
+
+-keep class gen._chrome._android._features._tab_ui._java_resources.srcjar.R$attr { *; }
+
+-keep class gen._chrome._android._features._tab_ui._java_resources.srcjar.R$bool { *; }
+
+-keep class gen._chrome._android._features._tab_ui._java_resources.srcjar.R$color { *; }
+
+-keep class gen._chrome._android._features._tab_ui._java_resources.srcjar.R$dimen { *; }
+
+-keep class gen._chrome._android._features._tab_ui._java_resources.srcjar.R$drawable { *; }
+
+-keep class gen._chrome._android._features._tab_ui._java_resources.srcjar.R$font { *; }
+
+-keep class gen._chrome._android._features._tab_ui._java_resources.srcjar.R$fraction { *; }
+
+-keep class gen._chrome._android._features._tab_ui._java_resources.srcjar.R$id { *; }
+
+-keep class gen._chrome._android._features._tab_ui._java_resources.srcjar.R$integer { *; }
+
+-keep class gen._chrome._android._features._tab_ui._java_resources.srcjar.R$layout { *; }
+
+-keep class gen._chrome._android._features._tab_ui._java_resources.srcjar.R$menu { *; }
+
+-keep class gen._chrome._android._features._tab_ui._java_resources.srcjar.R$mipmap { *; }
+
+-keep class gen._chrome._android._features._tab_ui._java_resources.srcjar.R$plurals { *; }
+
+-keep class gen._chrome._android._features._tab_ui._java_resources.srcjar.R$string { *; }
+
+-keep class gen._chrome._android._features._tab_ui._java_resources.srcjar.R$style { *; }
+
+-keep class gen._chrome._android._features._tab_ui._java_resources.srcjar.R$styleable { *; }
+
+-keep class gen._chrome._android._features._tab_ui._java_resources.srcjar.R$transition { *; }
+
+-keep class gen._chrome._android._features._tab_ui._java_resources.srcjar.R$xml { *; }
+
+-keep class gen._chrome._android._monochrome_public_bundle__tab_ui_bundle_module__compile_resources.srcjar.R$dimen { *; }
+
+-keep class gen._chrome._android._monochrome_public_bundle__tab_ui_bundle_module__compile_resources.srcjar.R$drawable { *; }
+
+-keep class gen._chrome._android._monochrome_public_bundle__tab_ui_bundle_module__compile_resources.srcjar.R$id { *; }
+
+-keep class gen._chrome._android._monochrome_public_bundle__tab_ui_bundle_module__compile_resources.srcjar.R$layout { *; }
+
+-keep class int { *; }
+
+-keep class int[] { *; }
+
+-keep class long { *; }
+
+-keep class null { *; }
+
+-keep class org.apache.http.conn.scheme.LayeredSocketFactory { *; }
+
+-keep class org.apache.http.conn.scheme.SocketFactory { *; }
+
+-keep class org.apache.http.conn.ssl.AbstractVerifier { *; }
+
+-keep class org.apache.http.conn.ssl.X509HostnameVerifier { *; }
+
+-keep class org.apache.http.params.CoreConnectionPNames { *; }
+
+-keep class org.chromium.base.Callback { *; }
+
+-keep class org.chromium.base.ObserverList { *; }
+
+-keep class org.chromium.base.Supplier { *; }
+
+-keep class org.chromium.base.task.TaskTraits { *; }
+
+-keep class org.chromium.chrome.browser.ThemeColorProvider { *; }
+
+-keep class org.chromium.chrome.browser.ThemeColorProvider$ThemeColorObserver { *; }
+
+-keep class org.chromium.chrome.browser.ThemeColorProvider$TintObserver { *; }
+
+-keep class org.chromium.chrome.browser.compositor.CompositorViewHolder { *; }
+
+-keep class org.chromium.chrome.browser.compositor.layouts.EmptyOverviewModeObserver { *; }
+
+-keep class org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior { *; }
+
+-keep class org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior$OverviewModeObserver { *; }
+
+-keep class org.chromium.chrome.browser.compositor.layouts.content.TabContentManager { *; }
+
+-keep class org.chromium.chrome.browser.favicon.FaviconHelper { *; }
+
+-keep class org.chromium.chrome.browser.favicon.FaviconHelper$FaviconImageCallback { *; }
+
+-keep class org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager { *; }
+
+-keep class org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager$FullscreenListener { *; }
+
+-keep class org.chromium.chrome.browser.gesturenav.HistoryNavigationDelegate { *; }
+
+-keep class org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher { *; }
+
+-keep class org.chromium.chrome.browser.lifecycle.Destroyable { *; }
+
+-keep class org.chromium.chrome.browser.lifecycle.LifecycleObserver { *; }
+
+-keep class org.chromium.chrome.browser.lifecycle.PauseResumeWithNativeObserver { *; }
+
+-keep class org.chromium.chrome.browser.profiles.Profile { *; }
+
+-keep class org.chromium.chrome.browser.tab.EmptyTabObserver { *; }
+
+-keep class org.chromium.chrome.browser.tab.Tab { *; }
+
+-keep class org.chromium.chrome.browser.tab.TabObserver { *; }
+
+-keep class org.chromium.chrome.browser.tabmodel.EmptyTabModelObserver { *; }
+
+-keep class org.chromium.chrome.browser.tabmodel.EmptyTabModelSelectorObserver { *; }
+
+-keep class org.chromium.chrome.browser.tabmodel.TabCreatorManager { *; }
+
+-keep class org.chromium.chrome.browser.tabmodel.TabList { *; }
+
+-keep class org.chromium.chrome.browser.tabmodel.TabModel { *; }
+
+-keep class org.chromium.chrome.browser.tabmodel.TabModelObserver { *; }
+
+-keep class org.chromium.chrome.browser.tabmodel.TabModelSelector { *; }
+
+-keep class org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver { *; }
+
+-keep class org.chromium.chrome.browser.tabmodel.TabModelSelectorTabObserver { *; }
+
+-keep class org.chromium.chrome.browser.tasks.tab_groups.TabGroupUtils$1 { *; }
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.GridTabSwitcher { *; }
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.GridTabSwitcher$GridController { *; }
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.GridTabSwitcherMediator$ResetHandler { *; }
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.TabGridDialogMediator$ResetHandler { *; }
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.TabGridSheetMediator$ResetHandler { *; }
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.TabGridSheetViewBinder$ViewHolder { *; }
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.TabGroupUi { *; }
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.TabGroupUiMediator$ResetHandler { *; }
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.TabListMediator$CreateGroupButtonProvider { *; }
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.TabListMediator$GridCardOnClickListenerProvider { *; }
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.TabListMediator$IphProvider { *; }
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.TabListMediator$TabActionListener { *; }
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.TabListMediator$ThumbnailProvider { *; }
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.TabListMediator$TitleProvider { *; }
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.TabListRecyclerView$VisibilityListener { *; }
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.TabManagementDelegate { *; }
+
+-keep class org.chromium.chrome.browser.toolbar.ToolbarManager { *; }
+
+-keep class org.chromium.chrome.browser.toolbar.bottom.BottomControlsCoordinator$BottomControlsVisibilityController { *; }
+
+-keep class org.chromium.chrome.browser.widget.ScrimView { *; }
+
+-keep class org.chromium.chrome.browser.widget.ScrimView$ScrimObserver { *; }
+
+-keep class org.chromium.chrome.browser.widget.ScrimView$ScrimParams { *; }
+
+-keep class org.chromium.chrome.browser.widget.ScrimView$StatusBarScrimDelegate { *; }
+
+-keep class org.chromium.chrome.browser.widget.bottomsheet.BottomSheet$BottomSheetContent { *; }
+
+-keep class org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController { *; }
+
+-keep class org.chromium.chrome.browser.widget.bottomsheet.BottomSheetObserver { *; }
+
+-keep class org.chromium.chrome.browser.widget.bottomsheet.EmptyBottomSheetObserver { *; }
+
+-keep class org.chromium.chrome.browser.widget.textbubble.TextBubble { *; }
+
+-keep class org.chromium.components.feature_engagement.Tracker { *; }
+
+-keep class org.chromium.content_public.browser.LoadUrlParams { *; }
+
+-keep class org.chromium.ui.modelutil.ListModelBase { *; }
+
+-keep class org.chromium.ui.modelutil.PropertyKey { *; }
+
+-keep class org.chromium.ui.modelutil.PropertyKey[] { *; }
+
+-keep class org.chromium.ui.modelutil.PropertyListModel { *; }
+
+-keep class org.chromium.ui.modelutil.PropertyModel { *; }
+
+-keep class org.chromium.ui.modelutil.PropertyModel$Builder { *; }
+
+-keep class org.chromium.ui.modelutil.PropertyModel$ReadableBooleanPropertyKey { *; }
+
+-keep class org.chromium.ui.modelutil.PropertyModel$ReadableFloatPropertyKey { *; }
+
+-keep class org.chromium.ui.modelutil.PropertyModel$ReadableIntPropertyKey { *; }
+
+-keep class org.chromium.ui.modelutil.PropertyModel$ReadableObjectPropertyKey { *; }
+
+-keep class org.chromium.ui.modelutil.PropertyModel$WritableBooleanPropertyKey { *; }
+
+-keep class org.chromium.ui.modelutil.PropertyModel$WritableFloatPropertyKey { *; }
+
+-keep class org.chromium.ui.modelutil.PropertyModel$WritableIntPropertyKey { *; }
+
+-keep class org.chromium.ui.modelutil.PropertyModel$WritableObjectPropertyKey { *; }
+
+-keep class org.chromium.ui.modelutil.PropertyModelChangeProcessor$ViewBinder { *; }
+
+-keep class org.chromium.ui.modelutil.PropertyObservable { *; }
+
+-keep class org.chromium.ui.modelutil.RecyclerViewAdapter { *; }
+
+-keep class org.chromium.ui.modelutil.RecyclerViewAdapter$Delegate { *; }
+
+-keep class org.chromium.ui.modelutil.RecyclerViewAdapter$ViewHolderFactory { *; }
+
+-keep class org.chromium.ui.modelutil.SimpleRecyclerViewMcpBase { *; }
+
+-keep class org.chromium.ui.modelutil.SimpleRecyclerViewMcpBase$ItemViewTypeCallback { *; }
+
+-keep class org.chromium.ui.modelutil.SimpleRecyclerViewMcpBase$ViewBinder { *; }
+
+-keep class org.chromium.ui.resources.dynamics.DynamicResource { *; }
+
+-keep class org.chromium.ui.resources.dynamics.DynamicResourceLoader { *; }
+
+-keep class org.chromium.ui.resources.dynamics.ViewResourceAdapter { *; }
+
+-keep class org.chromium.ui.widget.RectProvider { *; }
+
+-keep class org.chromium.ui.widget.ViewRectProvider { *; }
+
+-keep class android.support.v4.content.ContextCompat {
+  int getColor(android.content.Context, int);
+}
+
+-keep class android.support.v4.content.res.ResourcesCompat {
+  android.graphics.drawable.Drawable getDrawable(android.content.res.Resources, int, android.content.res.Resources$Theme);
+}
+
+-keep class android.support.v7.content.res.AppCompatResources {
+  android.graphics.drawable.Drawable getDrawable(android.content.Context, int);
+}
+
+-keep class android.support.v7.widget.RecyclerView$ItemAnimator {
+  void setAddDuration(long);
+  long getAddDuration();
+}
+
+-keep class gen._chrome._android._features._tab_ui._java_resources.srcjar.R {
+  void onResourcesLoaded(int);
+  void onResourcesLoadedString(int);
+  void onResourcesLoadedDrawable(int);
+  void onResourcesLoadedMipmap(int);
+  void onResourcesLoadedStyleable(int);
+  void onResourcesLoadedInteger(int);
+  void onResourcesLoadedColor(int);
+  boolean sResourcesDidLoad;
+  void onResourcesLoadedMenu(int);
+  void onResourcesLoadedDimen(int);
+  void onResourcesLoadedXml(int);
+  void onResourcesLoadedLayout(int);
+  void onResourcesLoadedTransition(int);
+  void onResourcesLoadedBool(int);
+  void onResourcesLoadedFont(int);
+  void onResourcesLoadedAnim(int);
+  void onResourcesLoadedAnimator(int);
+  void onResourcesLoadedArray(int);
+  void onResourcesLoadedStyle(int);
+  void onResourcesLoadedFraction(int);
+  void onResourcesLoadedAttr(int);
+  void onResourcesLoadedId(int);
+  void onResourcesLoadedPlurals(int);
+}
+
+-keep class org.apache.http.conn.ssl.SSLSocketFactory {
+  org.apache.http.conn.ssl.X509HostnameVerifier ALLOW_ALL_HOSTNAME_VERIFIER;
+  org.apache.http.conn.ssl.X509HostnameVerifier STRICT_HOSTNAME_VERIFIER;
+  org.apache.http.conn.ssl.X509HostnameVerifier BROWSER_COMPATIBLE_HOSTNAME_VERIFIER;
+}
+
+-keep class org.chromium.base.ApiCompatibilityUtils {
+  void setImageTintList(android.widget.ImageView, android.content.res.ColorStateList);
+  int getColor(android.content.res.Resources, int);
+}
+
+-keep class org.chromium.base.ApplicationStatus {
+  android.app.Activity getLastTrackedFocusedActivity();
+}
+
+-keep class org.chromium.base.ContextUtils {
+  android.content.Context getApplicationContext();
+}
+
+-keep class org.chromium.base.Log {
+  void w(java.lang.String, java.lang.String, java.lang.Object[]);
+}
+
+-keep class org.chromium.base.metrics.RecordHistogram {
+  void recordSparseHistogram(java.lang.String, int);
+  void recordCountHistogram(java.lang.String, int);
+}
+
+-keep class org.chromium.base.metrics.RecordUserAction {
+  void record(java.lang.String);
+}
+
+-keep class org.chromium.base.task.PostTask {
+  void postTask(org.chromium.base.task.TaskTraits, java.lang.Runnable);
+}
+
+-keep class org.chromium.chrome.R$color {
+  int modern_grey_100;
+  int modern_grey_800_alpha_38;
+  int modern_primary_color;
+  int default_text_color_dark;
+  int modern_grey_300;
+}
+
+-keep class org.chromium.chrome.R$dimen {
+  int toolbar_height_no_shadow;
+  int control_container_height;
+  int compositor_tab_title_text_size;
+  int default_favicon_size;
+}
+
+-keep class org.chromium.chrome.R$drawable {
+  int btn_close;
+  int ic_globe_24dp;
+  int chromelogo16;
+}
+
+-keep class org.chromium.chrome.R$plurals {
+  int bottom_tab_grid_title_placeholder;
+}
+
+-keep class org.chromium.chrome.R$string {
+  int iph_tab_groups_your_tabs_together_text;
+  int bottom_tab_grid_opened_full;
+  int accessibility_tabstrip_tab;
+  int iph_tab_groups_quickly_compare_pages_text;
+  int iph_tab_groups_tap_to_see_another_tab_text;
+  int bottom_tab_grid_opened_half;
+  int iph_tab_groups_tap_to_see_another_tab_accessibility_text;
+  int accessibility_tabstrip_btn_close_tab;
+  int bottom_tab_grid_description;
+  int bottom_tab_grid_closed;
+}
+
+-keep class org.chromium.chrome.browser.ChromeActivity {
+  org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher getLifecycleDispatcher();
+  org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior getOverviewModeBehavior();
+  org.chromium.chrome.browser.toolbar.ToolbarManager getToolbarManager();
+  void onBackPressed();
+  org.chromium.chrome.browser.compositor.layouts.content.TabContentManager getTabContentManager();
+  org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager getFullscreenManager();
+  boolean isWarmOnResume();
+  org.chromium.chrome.browser.tabmodel.TabModelSelector getTabModelSelector();
+  org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController getBottomSheetController();
+  org.chromium.chrome.browser.compositor.CompositorViewHolder getCompositorViewHolder();
+}
+
+-keep class org.chromium.chrome.browser.ChromeFeatureList {
+  boolean isInitialized();
+  boolean isEnabled(java.lang.String);
+}
+
+-keep class org.chromium.chrome.browser.ChromeTabbedActivity {
+  org.chromium.chrome.browser.tabmodel.TabModelSelector getTabModelSelector();
+  org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior getOverviewModeBehavior();
+}
+
+-keep class org.chromium.chrome.browser.feature_engagement.TrackerFactory {
+  org.chromium.components.feature_engagement.Tracker getTrackerForProfile(org.chromium.chrome.browser.profiles.Profile);
+}
+
+-keep class org.chromium.chrome.browser.gesturenav.HistoryNavigationLayout {
+  void setNavigationDelegate(org.chromium.chrome.browser.gesturenav.HistoryNavigationDelegate);
+}
+
+-keep class org.chromium.chrome.browser.metrics.UmaSessionStats {
+  void registerSyntheticFieldTrial(java.lang.String, java.lang.String);
+}
+
+-keep class org.chromium.chrome.browser.native_page.NativePageFactory {
+  boolean isNativePageUrl(java.lang.String, boolean);
+}
+
+-keep class org.chromium.chrome.browser.tabmodel.TabCreatorManager$TabCreator {
+  org.chromium.chrome.browser.tab.Tab createNewTab(org.chromium.content_public.browser.LoadUrlParams, int, org.chromium.chrome.browser.tab.Tab);
+}
+
+-keep class org.chromium.chrome.browser.tabmodel.TabModelFilter {
+  int index();
+  int indexOf(org.chromium.chrome.browser.tab.Tab);
+  boolean isIncognito();
+  java.util.List getRelatedTabList(int);
+}
+
+-keep class org.chromium.chrome.browser.tabmodel.TabModelFilterProvider {
+  void removeTabModelFilterObserver(org.chromium.chrome.browser.tabmodel.TabModelObserver);
+  org.chromium.chrome.browser.tabmodel.TabModelFilter getTabModelFilter(boolean);
+  org.chromium.chrome.browser.tabmodel.TabModelFilter getCurrentTabModelFilter();
+  void addTabModelFilterObserver(org.chromium.chrome.browser.tabmodel.TabModelObserver);
+}
+
+-keep class org.chromium.chrome.browser.tabmodel.TabModelUtils {
+  int getTabIndexById(org.chromium.chrome.browser.tabmodel.TabList, int);
+  org.chromium.chrome.browser.tab.Tab getTabById(org.chromium.chrome.browser.tabmodel.TabList, int);
+}
+
+-keep class org.chromium.chrome.browser.tasks.ReturnToChromeExperimentsUtil {
+  boolean shouldShowOmniboxOnTabSwitcher();
+}
+
+-keep class org.chromium.chrome.browser.tasks.tab_groups.TabGroupUtils {
+  boolean isMoveInSameGroup(org.chromium.chrome.browser.tabmodel.TabModel, int, int);
+  boolean $assertionsDisabled;
+  void lambda$maybeShowIPH$0(org.chromium.components.feature_engagement.Tracker, java.lang.String);
+  void startObservingForTabGroupsIPH(org.chromium.chrome.browser.tabmodel.TabModelSelector);
+  void maybeShowIPH(java.lang.String, android.view.View);
+  org.chromium.chrome.browser.tabmodel.TabModelSelectorTabObserver access$000();
+  int getLastTabModelIndexForList(org.chromium.chrome.browser.tabmodel.TabModelSelector, java.util.List);
+  org.chromium.chrome.browser.tabmodel.TabModelSelectorTabObserver sTabModelSelectorTabObserver;
+  int getFirstTabModelIndexForList(org.chromium.chrome.browser.tabmodel.TabModelSelector, java.util.List);
+  org.chromium.chrome.browser.tab.Tab getSelectedTabInGroupForTab(org.chromium.chrome.browser.tabmodel.TabModelSelector, org.chromium.chrome.browser.tab.Tab);
+}
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.GridTabSwitcherCoordinator {
+  org.chromium.chrome.browser.tasks.tab_management.TabListCoordinator mTabGridCoordinator;
+  java.lang.String lambda$new$0(org.chromium.chrome.browser.tabmodel.TabModelSelector, android.content.Context, org.chromium.chrome.browser.tab.Tab);
+  org.chromium.ui.modelutil.PropertyModelChangeProcessor mContainerViewChangeProcessor;
+  org.chromium.chrome.browser.tasks.tab_management.TabGridDialogCoordinator mTabGridDialogCoordinator;
+  org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher mLifecycleDispatcher;
+  org.chromium.chrome.browser.tasks.tab_management.GridTabSwitcherMediator mMediator;
+  org.chromium.chrome.browser.tasks.tab_management.MultiThumbnailCardProvider mMultiThumbnailCardProvider;
+}
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.GridTabSwitcherMediator {
+  org.chromium.chrome.browser.tasks.tab_management.TabListMediator$TabActionListener getGridCardOnClickListener(org.chromium.chrome.browser.tab.Tab);
+  java.util.List getRelatedTabs(int);
+  org.chromium.chrome.browser.tasks.tab_management.GridTabSwitcherMediator$ResetHandler access$300(org.chromium.chrome.browser.tasks.tab_management.GridTabSwitcherMediator);
+  boolean mShouldIgnoreNextSelect;
+  org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager$FullscreenListener mFullscreenListener;
+  void lambda$getCreateGroupButtonOnClickListener$1(int);
+  org.chromium.chrome.browser.tasks.tab_management.TabGridDialogMediator$ResetHandler mTabGridDialogResetHandler;
+  org.chromium.chrome.browser.tabmodel.TabModelSelector mTabModelSelector;
+  void prepareOverview();
+  org.chromium.chrome.browser.tabmodel.TabModelObserver mTabModelObserver;
+  boolean ableToCreateGroup(org.chromium.chrome.browser.tab.Tab);
+  void setContentOverlayVisibility(boolean);
+  org.chromium.ui.modelutil.PropertyModel access$000(org.chromium.chrome.browser.tasks.tab_management.GridTabSwitcherMediator);
+  org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager mFullscreenManager;
+  boolean access$102(org.chromium.chrome.browser.tasks.tab_management.GridTabSwitcherMediator, boolean);
+  org.chromium.chrome.browser.tasks.tab_management.GridTabSwitcherMediator$ResetHandler mResetHandler;
+  org.chromium.chrome.browser.tasks.tab_management.TabListMediator$TabActionListener getCreateGroupButtonOnClickListener(org.chromium.chrome.browser.tab.Tab);
+  void destroy();
+  org.chromium.base.ObserverList mObservers;
+  boolean access$100(org.chromium.chrome.browser.tasks.tab_management.GridTabSwitcherMediator);
+  void setVisibility(boolean);
+  boolean ableToOpenDialog(org.chromium.chrome.browser.tab.Tab);
+  org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver mTabModelSelectorObserver;
+  void access$400(org.chromium.chrome.browser.tasks.tab_management.GridTabSwitcherMediator, boolean);
+  void lambda$getGridCardOnClickListener$0(int);
+  org.chromium.ui.modelutil.PropertyModel mContainerViewModel;
+  org.chromium.chrome.browser.compositor.CompositorViewHolder mCompositorViewHolder;
+  org.chromium.chrome.browser.tabmodel.TabModelSelector access$200(org.chromium.chrome.browser.tasks.tab_management.GridTabSwitcherMediator);
+}
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.MultiThumbnailCardProvider {
+  android.graphics.Paint access$1000(org.chromium.chrome.browser.tasks.tab_management.MultiThumbnailCardProvider);
+  org.chromium.chrome.browser.tasks.tab_management.TabListFaviconProvider access$1200(org.chromium.chrome.browser.tasks.tab_management.MultiThumbnailCardProvider);
+  org.chromium.chrome.browser.tasks.tab_management.TabListFaviconProvider mTabListFaviconProvider;
+  java.util.List mThumbnailRects;
+  float access$900(org.chromium.chrome.browser.tasks.tab_management.MultiThumbnailCardProvider);
+  android.graphics.Paint mTextPaint;
+  java.util.List access$800(org.chromium.chrome.browser.tasks.tab_management.MultiThumbnailCardProvider);
+  java.util.List mFaviconRects;
+  android.graphics.Paint mEmptyThumbnailPaint;
+  java.util.List access$1100(org.chromium.chrome.browser.tasks.tab_management.MultiThumbnailCardProvider);
+  float access$500(org.chromium.chrome.browser.tasks.tab_management.MultiThumbnailCardProvider);
+  java.util.List mFaviconBackgroundRects;
+  int mSize;
+  android.graphics.Paint access$700(org.chromium.chrome.browser.tasks.tab_management.MultiThumbnailCardProvider);
+  org.chromium.chrome.browser.tabmodel.TabModelSelector access$100(org.chromium.chrome.browser.tasks.tab_management.MultiThumbnailCardProvider);
+  org.chromium.chrome.browser.tabmodel.TabModelSelector mTabModelSelector;
+  android.graphics.Paint mThumbnailFramePaint;
+  org.chromium.chrome.browser.compositor.layouts.content.TabContentManager access$200(org.chromium.chrome.browser.tasks.tab_management.MultiThumbnailCardProvider);
+  android.graphics.Paint access$400(org.chromium.chrome.browser.tasks.tab_management.MultiThumbnailCardProvider);
+  java.util.List access$300(org.chromium.chrome.browser.tasks.tab_management.MultiThumbnailCardProvider);
+  float mRadius;
+  float mFaviconCirclePadding;
+  android.graphics.Paint mFaviconBackgroundPaint;
+  android.graphics.Paint access$600(org.chromium.chrome.browser.tasks.tab_management.MultiThumbnailCardProvider);
+  org.chromium.chrome.browser.compositor.layouts.content.TabContentManager mTabContentManager;
+  int access$000(org.chromium.chrome.browser.tasks.tab_management.MultiThumbnailCardProvider);
+}
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.TabGridContainerViewBinder {
+  void bind(org.chromium.ui.modelutil.PropertyModel, org.chromium.chrome.browser.tasks.tab_management.TabListRecyclerView, org.chromium.ui.modelutil.PropertyKey);
+}
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.TabGridDialogCoordinator {
+  org.chromium.ui.modelutil.PropertyModel mToolbarPropertyModel;
+  android.content.Context mContext;
+  org.chromium.chrome.browser.tasks.tab_management.TabGridDialogMediator mMediator;
+  void updateDialogContent(java.util.List);
+  org.chromium.chrome.browser.tasks.tab_management.TabGridSheetToolbarCoordinator mToolbarCoordinator;
+  org.chromium.chrome.browser.tasks.tab_management.TabGridDialogMediator$ResetHandler getResetHandler();
+  org.chromium.chrome.browser.tasks.tab_management.TabListCoordinator mTabListCoordinator;
+  void resetWithListOfTabs(java.util.List);
+  org.chromium.chrome.browser.tasks.tab_management.TabGridDialogParent mParentLayout;
+}
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.TabGridDialogMediator {
+  void setupToolbarClickHandlers();
+  org.chromium.chrome.browser.tabmodel.TabModelSelector mTabModelSelector;
+  void onReset(java.lang.Integer);
+  android.view.View$OnClickListener getCollapseButtonClickListener();
+  java.util.List getRelatedTabs(int);
+  void updateGridTabSwitcher();
+  org.chromium.ui.modelutil.PropertyModel mModel;
+  org.chromium.chrome.browser.tabmodel.TabModelObserver mTabModelObserver;
+  void lambda$getAddButtonClickListener$1(android.view.View);
+  void setupScrimViewObserver();
+  android.content.Context mContext;
+  void updateDialog();
+  org.chromium.chrome.browser.tasks.tab_management.TabGridDialogMediator$ResetHandler mDialogResetHandler;
+  org.chromium.chrome.browser.tabmodel.TabCreatorManager mTabCreatorManager;
+  boolean $assertionsDisabled;
+  void access$100(org.chromium.chrome.browser.tasks.tab_management.TabGridDialogMediator);
+  int access$400(org.chromium.chrome.browser.tasks.tab_management.TabGridDialogMediator);
+  int access$402(org.chromium.chrome.browser.tasks.tab_management.TabGridDialogMediator, int);
+  org.chromium.chrome.browser.tasks.tab_management.GridTabSwitcherMediator$ResetHandler mGridTabSwitcherResetHandler;
+  java.util.List access$300(org.chromium.chrome.browser.tasks.tab_management.TabGridDialogMediator, int);
+  org.chromium.ui.modelutil.PropertyModel access$200(org.chromium.chrome.browser.tasks.tab_management.TabGridDialogMediator);
+  void lambda$getCollapseButtonClickListener$0(android.view.View);
+  android.view.View$OnClickListener getAddButtonClickListener();
+  void access$000(org.chromium.chrome.browser.tasks.tab_management.TabGridDialogMediator);
+  int mCurrentTabId;
+  void destroy();
+}
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.TabGridDialogParent {
+  void showDialog();
+  org.chromium.chrome.browser.widget.ScrimView$ScrimParams mScrimParams;
+  android.widget.PopupWindow mPopupWindow;
+  void setupDialogAnimation();
+  void destroy();
+  int mSideMargin;
+  void updateDialogWithOrientation(android.content.Context, int);
+  android.content.ComponentCallbacks mComponentCallbacks;
+  android.widget.LinearLayout mDialogContainerView;
+  void access$000(org.chromium.chrome.browser.tasks.tab_management.TabGridDialogParent, android.content.Context, int);
+  org.chromium.chrome.browser.widget.ScrimView mScrimView;
+  android.animation.Animator access$102(org.chromium.chrome.browser.tasks.tab_management.TabGridDialogParent, android.animation.Animator);
+  void hideDialog();
+  android.animation.ValueAnimator mDialogFadeOut;
+  android.view.ViewGroup mParent;
+  android.animation.Animator mCurrentAnimator;
+  int mTopMargin;
+  void setupDialogContent(android.content.Context);
+  void resetDialog(android.view.View, android.view.View);
+  android.widget.PopupWindow access$200(org.chromium.chrome.browser.tasks.tab_management.TabGridDialogParent);
+  android.animation.ValueAnimator mDialogFadeIn;
+  int mStatusBarHeight;
+  android.widget.FrameLayout$LayoutParams mContainerParams;
+  void setScrimViewObserver(org.chromium.chrome.browser.widget.ScrimView$ScrimObserver);
+}
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.TabGridSheetContent {
+  android.view.View mToolbarView;
+  void destroy();
+  org.chromium.chrome.browser.tasks.tab_management.TabListRecyclerView mRecyclerView;
+}
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.TabGridSheetCoordinator {
+  void updateBottomSheetContent(java.util.List);
+  org.chromium.chrome.browser.tasks.tab_management.TabGridSheetToolbarCoordinator mToolbarCoordinator;
+  org.chromium.ui.modelutil.PropertyModel mToolbarPropertyModel;
+  org.chromium.chrome.browser.tasks.tab_management.TabGridSheetContent mBottomSheetContent;
+  org.chromium.chrome.browser.tasks.tab_management.TabGridSheetMediator mMediator;
+  void destroy();
+  org.chromium.chrome.browser.tasks.tab_management.TabListCoordinator mTabGridCoordinator;
+  void resetWithListOfTabs(java.util.List);
+  android.content.Context mContext;
+  void startObservingForCreationIPH();
+}
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.TabGridSheetMediator {
+  android.view.View$OnClickListener getAddButtonClickListener();
+  org.chromium.chrome.browser.tabmodel.TabCreatorManager mTabCreatorManager;
+  void lambda$getAddButtonClickListener$3(android.view.View);
+  org.chromium.chrome.browser.tasks.tab_management.TabGridSheetMediator$ResetHandler mResetHandler;
+  org.chromium.chrome.browser.tabmodel.TabModelObserver mTabModelObserver;
+  void lambda$new$1(android.content.res.ColorStateList, boolean);
+  org.chromium.chrome.browser.ThemeColorProvider$ThemeColorObserver mThemeColorObserver;
+  void lambda$new$0(int, boolean);
+  void onReset(org.chromium.chrome.browser.tasks.tab_management.TabGridSheetContent);
+  void lambda$getCollapseButtonClickListener$2(android.view.View);
+  org.chromium.chrome.browser.widget.bottomsheet.BottomSheet$BottomSheetContent getCurrentSheetContent();
+  boolean $assertionsDisabled;
+  void setupToolbarClickHandlers();
+  android.content.Context mContext;
+  android.view.View$OnClickListener getCollapseButtonClickListener();
+  void destroy();
+  void showTabGridSheet(org.chromium.chrome.browser.tasks.tab_management.TabGridSheetContent);
+  void updateBottomSheet();
+  org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController mBottomSheetController;
+  org.chromium.chrome.browser.ThemeColorProvider mThemeColorProvider;
+  void access$000(org.chromium.chrome.browser.tasks.tab_management.TabGridSheetMediator);
+  org.chromium.ui.modelutil.PropertyModel mModel;
+  org.chromium.chrome.browser.widget.bottomsheet.BottomSheetObserver mSheetObserver;
+  org.chromium.chrome.browser.ThemeColorProvider$TintObserver mTintObserver;
+  org.chromium.chrome.browser.tabmodel.TabModelSelector mTabModelSelector;
+  void hideTabGridSheet();
+}
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.TabGridSheetProperties {
+  org.chromium.ui.modelutil.PropertyModel$WritableObjectPropertyKey SCRIMVIEW_OBSERVER;
+  org.chromium.ui.modelutil.PropertyModel$WritableIntPropertyKey PRIMARY_COLOR;
+  org.chromium.ui.modelutil.PropertyModel$WritableObjectPropertyKey TINT;
+  org.chromium.ui.modelutil.PropertyModel$WritableObjectPropertyKey ADD_CLICK_LISTENER;
+  org.chromium.ui.modelutil.PropertyModel$WritableIntPropertyKey CONTENT_TOP_MARGIN;
+  org.chromium.ui.modelutil.PropertyModel$WritableBooleanPropertyKey IS_DIALOG_VISIBLE;
+  org.chromium.ui.modelutil.PropertyKey[] ALL_KEYS;
+  org.chromium.ui.modelutil.PropertyModel$WritableObjectPropertyKey COLLAPSE_CLICK_LISTENER;
+  org.chromium.ui.modelutil.PropertyModel$WritableObjectPropertyKey HEADER_TITLE;
+}
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.TabGridSheetToolbarCoordinator {
+  android.view.View getView();
+  org.chromium.chrome.browser.tasks.tab_management.TabGroupUiToolbarView mToolbarView;
+  org.chromium.ui.modelutil.PropertyModelChangeProcessor mModelChangeProcessor;
+  void destroy();
+}
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.TabGridSheetViewBinder {
+  void bind(org.chromium.ui.modelutil.PropertyModel, org.chromium.chrome.browser.tasks.tab_management.TabGridSheetViewBinder$ViewHolder, org.chromium.ui.modelutil.PropertyKey);
+}
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.TabGridViewBinder {
+  void lambda$onBindViewHolder$3(org.chromium.chrome.browser.tasks.tab_management.TabListMediator$TabActionListener, org.chromium.chrome.browser.tasks.tab_management.TabGridViewHolder, android.view.View);
+  void lambda$onBindViewHolder$2(org.chromium.chrome.browser.tasks.tab_management.TabGridViewHolder, android.graphics.Bitmap);
+  void lambda$onBindViewHolder$0(org.chromium.ui.modelutil.PropertyModel, org.chromium.chrome.browser.tasks.tab_management.TabGridViewHolder, android.view.View);
+  void lambda$onBindViewHolder$1(org.chromium.ui.modelutil.PropertyModel, org.chromium.chrome.browser.tasks.tab_management.TabGridViewHolder, android.view.View);
+  void onBindViewHolder(org.chromium.chrome.browser.tasks.tab_management.TabGridViewHolder, org.chromium.ui.modelutil.PropertyModel);
+  void onBindViewHolder(org.chromium.chrome.browser.tasks.tab_management.TabGridViewHolder, org.chromium.ui.modelutil.PropertyModel, org.chromium.ui.modelutil.PropertyKey);
+}
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.TabGridViewHolder {
+  android.widget.ImageView favicon;
+  int getTabId();
+  org.chromium.ui.widget.ButtonCompat createGroupButton;
+  android.widget.TextView title;
+  android.view.View backgroundView;
+  android.view.View itemView;
+  org.chromium.chrome.browser.tasks.tab_management.TabGridViewHolder create(android.view.ViewGroup, int);
+  void setTabId(int);
+  int mTabId;
+  java.lang.ref.WeakReference sCloseButtonBitmapWeakRef;
+  void resetThumbnail();
+  android.widget.ImageView thumbnail;
+  android.widget.ImageView closeButton;
+}
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.TabGroupUiCoordinator {
+  org.chromium.ui.modelutil.PropertyModel mTabStripToolbarModel;
+  org.chromium.chrome.browser.ThemeColorProvider mThemeColorProvider;
+  android.content.Context mContext;
+  org.chromium.chrome.browser.tasks.tab_management.TabGroupUiMediator mMediator;
+  org.chromium.chrome.browser.ChromeActivity mActivity;
+  org.chromium.chrome.browser.tasks.tab_management.TabGridSheetCoordinator mTabGridSheetCoordinator;
+  void recordSessionCount();
+  org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher mActivityLifecycleDispatcher;
+  org.chromium.chrome.browser.tasks.tab_management.TabListCoordinator mTabStripCoordinator;
+  boolean $assertionsDisabled;
+  org.chromium.chrome.browser.tasks.tab_management.TabStripToolbarCoordinator mTabStripToolbarCoordinator;
+}
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.TabGroupUiMediator {
+  org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior mOverviewModeBehavior;
+  boolean mIsTabGroupUiVisible;
+  org.chromium.chrome.browser.tabmodel.TabModelSelectorTabObserver mTabModelSelectorTabObserver;
+  org.chromium.chrome.browser.tabmodel.TabModelSelector access$400(org.chromium.chrome.browser.tasks.tab_management.TabGroupUiMediator);
+  org.chromium.chrome.browser.tasks.tab_management.TabGroupUiMediator$ResetHandler access$500(org.chromium.chrome.browser.tasks.tab_management.TabGroupUiMediator);
+  java.util.List getRelatedTabsForId(int);
+  org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior$OverviewModeObserver mOverviewModeObserver;
+  org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver mTabModelSelectorObserver;
+  boolean access$100(org.chromium.chrome.browser.tasks.tab_management.TabGroupUiMediator);
+  org.chromium.ui.modelutil.PropertyModel mToolbarPropertyModel;
+  org.chromium.chrome.browser.ThemeColorProvider$ThemeColorObserver mThemeColorObserver;
+  void lambda$setupToolbarClickHandlers$3(android.view.View);
+  org.chromium.chrome.browser.ThemeColorProvider mThemeColorProvider;
+  void lambda$setupToolbarClickHandlers$2(android.view.View);
+  void setupToolbarClickHandlers();
+  void lambda$new$1(android.content.res.ColorStateList, boolean);
+  void resetTabStripWithRelatedTabsForId(int);
+  org.chromium.chrome.browser.toolbar.bottom.BottomControlsCoordinator$BottomControlsVisibilityController mVisibilityController;
+  void destroy();
+  boolean $assertionsDisabled;
+  void lambda$new$0(int, boolean);
+  org.chromium.chrome.browser.tabmodel.TabModelObserver mTabModelObserver;
+  org.chromium.chrome.browser.ThemeColorProvider$TintObserver mTintObserver;
+  void access$300(org.chromium.chrome.browser.tasks.tab_management.TabGroupUiMediator, int);
+  boolean mIsClosingAGroup;
+  org.chromium.chrome.browser.tabmodel.TabCreatorManager mTabCreatorManager;
+  org.chromium.chrome.browser.tasks.tab_management.TabGroupUiMediator$ResetHandler mResetHandler;
+  org.chromium.chrome.browser.tabmodel.TabModelSelector mTabModelSelector;
+  boolean access$102(org.chromium.chrome.browser.tasks.tab_management.TabGroupUiMediator, boolean);
+  java.util.List access$200(org.chromium.chrome.browser.tasks.tab_management.TabGroupUiMediator, int);
+  boolean access$000(org.chromium.chrome.browser.tasks.tab_management.TabGroupUiMediator);
+}
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.TabGroupUiToolbarView {
+  android.view.View mMainContent;
+  void setTint(android.content.res.ColorStateList);
+  android.view.ViewGroup mContainerView;
+  org.chromium.ui.widget.ChromeImageView mLeftButton;
+  void setMainContentVisibility(boolean);
+  void setTitle(java.lang.String);
+  org.chromium.ui.widget.ChromeImageView mRightButton;
+  android.view.View findViewById(int);
+  android.widget.TextView mTitleTextView;
+  void setLeftButtonOnClickListener(android.view.View$OnClickListener);
+  android.view.ViewGroup getViewContainer();
+  void setRightButtonOnClickListener(android.view.View$OnClickListener);
+  void setPrimaryColor(int);
+}
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.TabGroupUiToolbarViewBinder {
+  void bind(org.chromium.ui.modelutil.PropertyModel, org.chromium.chrome.browser.tasks.tab_management.TabGroupUiToolbarView, org.chromium.ui.modelutil.PropertyKey);
+}
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.TabListContainerProperties {
+  org.chromium.ui.modelutil.PropertyModel$WritableIntPropertyKey TOP_PADDING;
+  org.chromium.ui.modelutil.PropertyModel$WritableObjectPropertyKey VISIBILITY_LISTENER;
+  org.chromium.ui.modelutil.PropertyModel$WritableIntPropertyKey INITIAL_SCROLL_INDEX;
+  org.chromium.ui.modelutil.PropertyModel$WritableBooleanPropertyKey ANIMATE_VISIBILITY_CHANGES;
+  org.chromium.ui.modelutil.PropertyKey[] ALL_KEYS;
+  org.chromium.ui.modelutil.PropertyModel$WritableIntPropertyKey BOTTOM_CONTROLS_HEIGHT;
+  org.chromium.ui.modelutil.PropertyModel$WritableBooleanPropertyKey IS_INCOGNITO;
+  org.chromium.ui.modelutil.PropertyModel$WritableBooleanPropertyKey IS_VISIBLE;
+  org.chromium.ui.modelutil.PropertyModel$WritableIntPropertyKey TOP_CONTROLS_HEIGHT;
+}
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.TabListCoordinator {
+  void updateThumbnailLocation();
+  org.chromium.chrome.browser.tabmodel.TabModelSelector mTabModelSelector;
+  android.graphics.Rect mThumbnailLocationOfCurrentTab;
+  org.chromium.chrome.browser.tasks.tab_management.TabListMediator mMediator;
+  org.chromium.chrome.browser.tasks.tab_management.TabListRecyclerView mRecyclerView;
+  int getResourceId();
+  android.graphics.Rect getThumbnailLocationOfCurrentTab();
+  int mMode;
+  void destroy();
+  org.chromium.ui.modelutil.SimpleRecyclerViewMcpBase mModelChangeProcessor;
+  org.chromium.chrome.browser.tasks.tab_management.TabListRecyclerView getContainerView();
+  void resetWithListOfTabs(java.util.List);
+  void prepareOverview();
+}
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.TabListFaviconProvider {
+  android.graphics.drawable.Drawable getDefaultFaviconDrawable();
+  android.graphics.drawable.Drawable getFaviconForUrlSync(java.lang.String, boolean, android.graphics.Bitmap);
+  android.graphics.drawable.Drawable sRoundedGlobeDrawable;
+  android.graphics.drawable.Drawable processBitmap(android.graphics.Bitmap);
+  int mFaviconSize;
+  void lambda$getFaviconForUrlAsync$0(org.chromium.base.Callback, android.graphics.Bitmap, java.lang.String);
+  android.graphics.drawable.Drawable sRoundedChromeDrawable;
+  org.chromium.chrome.browser.profiles.Profile mProfile;
+  void getFaviconForUrlAsync(java.lang.String, boolean, org.chromium.base.Callback);
+  org.chromium.chrome.browser.favicon.FaviconHelper mFaviconHelper;
+}
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.TabListMediator {
+  void addTabInfoToModel(org.chromium.chrome.browser.tab.Tab, int, boolean);
+  boolean isValidMovePosition(int);
+  android.content.ComponentCallbacks mComponentCallbacks;
+  org.chromium.chrome.browser.tasks.tab_management.TabListFaviconProvider mTabListFaviconProvider;
+  org.chromium.chrome.browser.tasks.tab_management.TabListMediator$GridCardOnClickListenerProvider mGridCardOnClickListenerProvider;
+  boolean mShownIPH;
+  org.chromium.chrome.browser.tasks.tab_management.TabListMediator$TabActionListener access$1300(org.chromium.chrome.browser.tasks.tab_management.TabListMediator);
+  java.util.Map access$800();
+  org.chromium.chrome.browser.tabmodel.TabModelSelector access$100(org.chromium.chrome.browser.tasks.tab_management.TabListMediator);
+  boolean access$600(org.chromium.chrome.browser.tasks.tab_management.TabListMediator);
+  java.util.List access$1000(org.chromium.chrome.browser.tasks.tab_management.TabListMediator, int);
+  void access$900(org.chromium.chrome.browser.tasks.tab_management.TabListMediator, org.chromium.chrome.browser.tab.Tab, int, int);
+  void registerOrientationListener(android.support.v7.widget.GridLayoutManager);
+  java.lang.String mComponentName;
+  boolean mCloseAllRelatedTabs;
+  java.lang.String access$200(org.chromium.chrome.browser.tasks.tab_management.TabListMediator);
+  org.chromium.chrome.browser.tasks.tab_management.TabListMediator$TitleProvider mTitleProvider;
+  android.support.v7.widget.helper.ItemTouchHelper$SimpleCallback getItemTouchHelperCallback(float);
+  void access$700(org.chromium.chrome.browser.tasks.tab_management.TabListMediator, org.chromium.chrome.browser.tab.Tab, boolean);
+  void onTabClosedFrom(int, java.lang.String);
+  boolean access$002(org.chromium.chrome.browser.tasks.tab_management.TabListMediator, boolean);
+  org.chromium.chrome.browser.tasks.tab_management.TabListMediator$IphProvider mIphProvider;
+  java.util.Map sTabClosedFromMapTabClosedFromMap;
+  void onTabAdded(org.chromium.chrome.browser.tab.Tab, boolean);
+  org.chromium.chrome.browser.tab.TabObserver mTabObserver;
+  org.chromium.chrome.browser.tasks.tab_management.TabListMediator$CreateGroupButtonProvider mCreateGroupButtonProvider;
+  java.util.List getRelatedTabsForId(int);
+  org.chromium.chrome.browser.tasks.tab_management.TabListFaviconProvider access$400(org.chromium.chrome.browser.tasks.tab_management.TabListMediator);
+  org.chromium.chrome.browser.tasks.tab_management.TabListMediator$TabActionListener mTabSelectedListener;
+  org.chromium.chrome.browser.tasks.tab_management.TabListMediator$TabActionListener mTabClosedListener;
+  void resetWithListOfTabs(java.util.List);
+  org.chromium.chrome.browser.tabmodel.TabModelSelector mTabModelSelector;
+  boolean access$000(org.chromium.chrome.browser.tasks.tab_management.TabListMediator);
+  void lambda$addTabInfoToModel$0(org.chromium.chrome.browser.tab.Tab, android.graphics.drawable.Drawable);
+  org.chromium.chrome.browser.tasks.tab_management.TabListMediator$ThumbnailProvider mThumbnailProvider;
+  void onGroupClosedFrom(int);
+  org.chromium.chrome.browser.tasks.tab_management.TabListModel mModel;
+  org.chromium.chrome.browser.tabmodel.TabModelObserver mTabModelObserver;
+  void onTabMoved(org.chromium.chrome.browser.tab.Tab, int, int);
+  void access$1200(org.chromium.chrome.browser.tasks.tab_management.TabListMediator, int, java.lang.String);
+  org.chromium.chrome.browser.tasks.tab_management.TabListModel access$300(org.chromium.chrome.browser.tasks.tab_management.TabListMediator);
+  org.chromium.chrome.browser.tasks.tab_management.TabListMediator$TitleProvider access$500(org.chromium.chrome.browser.tasks.tab_management.TabListMediator);
+  void access$1100(org.chromium.chrome.browser.tasks.tab_management.TabListMediator, int);
+  void destroy();
+}
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.TabListModel {
+  void add(int, org.chromium.ui.modelutil.PropertyObservable);
+  int size();
+  void add(org.chromium.ui.modelutil.PropertyObservable);
+  java.lang.Object get(int);
+  org.chromium.ui.modelutil.PropertyObservable removeAt(int);
+  void set(java.util.Collection);
+  int indexFromId(int);
+  void move(int, int);
+}
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.TabListRecyclerView {
+  int getId();
+  long access$200(org.chromium.chrome.browser.tasks.tab_management.TabListRecyclerView);
+  android.graphics.Rect getRectOfCurrentThumbnail(int);
+  void setHasFixedSize(boolean);
+  android.animation.ValueAnimator mFadeInAnimator;
+  int getPaddingLeft();
+  int getPaddingBottom();
+  void endAllAnimations();
+  long mOriginalAddDuration;
+  int getResourceId();
+  org.chromium.ui.resources.dynamics.ViewResourceAdapter access$300(org.chromium.chrome.browser.tasks.tab_management.TabListRecyclerView);
+  org.chromium.ui.resources.dynamics.ViewResourceAdapter mDynamicView;
+  int computeVerticalScrollOffset();
+  org.chromium.chrome.browser.tasks.tab_management.TabListRecyclerView$VisibilityListener mListener;
+  void startShowing(boolean);
+  android.animation.ValueAnimator access$002(org.chromium.chrome.browser.tasks.tab_management.TabListRecyclerView, android.animation.ValueAnimator);
+  void setBackgroundColor(int);
+  void setAlpha(float);
+  void setVisibilityListener(org.chromium.chrome.browser.tasks.tab_management.TabListRecyclerView$VisibilityListener);
+  android.animation.ValueAnimator mFadeOutAnimator;
+  void setVisibility(int);
+  android.view.ViewTreeObserver getViewTreeObserver();
+  void setPadding(int, int, int, int);
+  android.support.v7.widget.RecyclerView$LayoutManager getLayoutManager();
+  boolean $assertionsDisabled;
+  void createDynamicView(org.chromium.ui.resources.dynamics.DynamicResourceLoader);
+  android.content.res.Resources getResources();
+  android.animation.ValueAnimator access$402(org.chromium.chrome.browser.tasks.tab_management.TabListRecyclerView, android.animation.ValueAnimator);
+  void prepareOverview();
+  void setAdapter(android.support.v7.widget.RecyclerView$Adapter);
+  android.support.v7.widget.RecyclerView$ItemAnimator getItemAnimator();
+  void requestLayout();
+  android.view.ViewGroup$LayoutParams getLayoutParams();
+  android.support.v7.widget.RecyclerView$ViewHolder findViewHolderForAdapterPosition(int);
+  void setLayoutManager(android.support.v7.widget.RecyclerView$LayoutManager);
+  void getLocationInWindow(int[]);
+  void startHiding(boolean);
+  int getPaddingRight();
+  org.chromium.chrome.browser.tasks.tab_management.TabListRecyclerView$VisibilityListener access$100(org.chromium.chrome.browser.tasks.tab_management.TabListRecyclerView);
+}
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.TabProperties {
+  org.chromium.ui.modelutil.PropertyModel$ReadableIntPropertyKey TAB_ID;
+  org.chromium.ui.modelutil.PropertyModel$WritableObjectPropertyKey IPH_PROVIDER;
+  org.chromium.ui.modelutil.PropertyModel$WritableObjectPropertyKey CREATE_GROUP_LISTENER;
+  org.chromium.ui.modelutil.PropertyModel$WritableObjectPropertyKey TAB_SELECTED_LISTENER;
+  org.chromium.ui.modelutil.PropertyKey[] ALL_KEYS_TAB_GRID;
+  org.chromium.ui.modelutil.PropertyKey[] ALL_KEYS_TAB_STRIP;
+  org.chromium.ui.modelutil.PropertyModel$WritableObjectPropertyKey FAVICON;
+  org.chromium.ui.modelutil.PropertyModel$WritableBooleanPropertyKey IS_SELECTED;
+  org.chromium.ui.modelutil.PropertyModel$WritableObjectPropertyKey TITLE;
+  org.chromium.ui.modelutil.PropertyModel$WritableObjectPropertyKey TAB_CLOSED_LISTENER;
+  org.chromium.ui.modelutil.PropertyModel$WritableObjectPropertyKey THUMBNAIL_FETCHER;
+  org.chromium.ui.modelutil.PropertyModel$WritableFloatPropertyKey ALPHA;
+}
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.TabStripToolbarCoordinator {
+  android.view.ViewGroup getTabListContainerView();
+  org.chromium.ui.modelutil.PropertyModel mModel;
+  org.chromium.ui.modelutil.PropertyModelChangeProcessor mModelChangeProcessor;
+  org.chromium.chrome.browser.tasks.tab_management.TabGroupUiToolbarView mToolbarView;
+}
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.TabStripToolbarViewProperties {
+  org.chromium.ui.modelutil.PropertyModel$WritableIntPropertyKey PRIMARY_COLOR;
+  org.chromium.ui.modelutil.PropertyModel$WritableObjectPropertyKey ADD_CLICK_LISTENER;
+  org.chromium.ui.modelutil.PropertyModel$WritableObjectPropertyKey TINT;
+  org.chromium.ui.modelutil.PropertyKey[] ALL_KEYS;
+  org.chromium.ui.modelutil.PropertyModel$WritableObjectPropertyKey EXPAND_CLICK_LISTENER;
+  org.chromium.ui.modelutil.PropertyModel$WritableBooleanPropertyKey IS_MAIN_CONTENT_VISIBLE;
+}
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.TabStripViewBinder {
+  void lambda$onBindViewHolder$1(org.chromium.ui.modelutil.PropertyModel, org.chromium.chrome.browser.tasks.tab_management.TabStripViewHolder, android.view.View);
+  void lambda$onBindViewHolder$0(org.chromium.ui.modelutil.PropertyModel, org.chromium.chrome.browser.tasks.tab_management.TabStripViewHolder, android.view.View);
+  void onBindViewHolder(org.chromium.chrome.browser.tasks.tab_management.TabStripViewHolder, org.chromium.ui.modelutil.PropertyModel, org.chromium.ui.modelutil.PropertyKey);
+  void onBindViewHolder(org.chromium.chrome.browser.tasks.tab_management.TabStripViewHolder, org.chromium.ui.modelutil.PropertyModel);
+}
+
+-keep class org.chromium.chrome.browser.tasks.tab_management.TabStripViewHolder {
+  android.widget.ImageButton button;
+  int mTabId;
+  android.view.View itemView;
+  org.chromium.chrome.browser.tasks.tab_management.TabStripViewHolder create(android.view.ViewGroup, int);
+  void setTabId(int);
+  int getTabId();
+}
+
+-keep class org.chromium.chrome.browser.tasks.tabgroup.TabGroupModelFilter {
+  int indexOf(org.chromium.chrome.browser.tab.Tab);
+  int getTabGroupCount();
+  void recordSessionsCount(org.chromium.chrome.browser.tab.Tab);
+  void moveRelatedTabs(int, int);
+  org.chromium.chrome.browser.tab.Tab getTabAt(int);
+}
+
+-keep class org.chromium.chrome.browser.util.ColorUtils {
+  int getPrimaryBackgroundColor(android.content.res.Resources, boolean);
+}
+
+-keep class org.chromium.chrome.browser.util.FeatureUtilities {
+  boolean isTabGroupsAndroidEnabled();
+  boolean isTabGroupsAndroidUiImprovementsEnabled();
+}
+
+-keep class org.chromium.chrome.browser.util.ViewUtils {
+  android.support.v4.graphics.drawable.RoundedBitmapDrawable createRoundedBitmapDrawable(android.graphics.Bitmap, int);
+  int DEFAULT_FAVICON_CORNER_RADIUS;
+}
+
+-keep class org.chromium.chrome.browser.widget.bottomsheet.BottomSheet {
+  void addObserver(org.chromium.chrome.browser.widget.bottomsheet.BottomSheetObserver);
+  void removeObserver(org.chromium.chrome.browser.widget.bottomsheet.BottomSheetObserver);
+  org.chromium.chrome.browser.widget.bottomsheet.BottomSheet$BottomSheetContent getCurrentSheetContent();
+}
+
+-keep class org.chromium.content_public.browser.NavigationHandle {
+  boolean isSameDocument();
+  boolean isValidSearchFormUrl();
+  boolean isInMainFrame();
+  java.lang.Integer pageTransition();
+}
+
+-keep class org.chromium.content_public.browser.UiThreadTaskTraits {
+  org.chromium.base.task.TaskTraits USER_VISIBLE;
+}
+
+-keep class org.chromium.ui.interpolators.BakedBezierInterpolator {
+  org.chromium.ui.interpolators.BakedBezierInterpolator FADE_IN_CURVE;
+  org.chromium.ui.interpolators.BakedBezierInterpolator FADE_OUT_CURVE;
+}
+
+-keep class org.chromium.ui.modelutil.PropertyModelChangeProcessor {
+  void destroy();
+  org.chromium.ui.modelutil.PropertyModelChangeProcessor create(org.chromium.ui.modelutil.PropertyObservable, java.lang.Object, org.chromium.ui.modelutil.PropertyModelChangeProcessor$ViewBinder);
+}
+
+-keep class org.chromium.ui.widget.ButtonCompat {
+  void setVisibility(int);
+  void setOnClickListener(android.view.View$OnClickListener);
+}
+
+-keep class org.chromium.ui.widget.ChromeImageView {
+  void setOnClickListener(android.view.View$OnClickListener);
+}
+
+-keep class org.json.JSONObject {
+  java.lang.Object NULL;
+}
+
diff --git a/chrome/android/features/tab_ui/proguard_async_manual.flags b/chrome/android/features/tab_ui/proguard_async_manual.flags
new file mode 100644
index 0000000..654ee65
--- /dev/null
+++ b/chrome/android/features/tab_ui/proguard_async_manual.flags
@@ -0,0 +1,14 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Rules added manually due to types
+#   1) used and included in XML files (ie. layout),
+#   2) dynamically generated by the compiler (ie. lambda), and
+#   3) types not currently being included in our constant pool deps.
+
+-keep class android.support.v4.graphics.drawable.RoundedBitmapDrawable { *; }
+-keep class org.chromium.ui.widget.RoundedCornerImageView { *; }
+-keep class org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager$FullscreenListener$$CC { *; }
+-keep class org.chromium.chrome.browser.widget.bottomsheet.BottomSheet$BottomSheetContent$$CC { *; }
+-keep class org.chromium.chrome.browser.compositor.layouts.OverviewModeController { *; }
\ No newline at end of file
diff --git a/chrome/android/features/tab_ui/tab_ui_module_tmpl.gni b/chrome/android/features/tab_ui/tab_ui_module_tmpl.gni
index 4e10712..614aed0 100644
--- a/chrome/android/features/tab_ui/tab_ui_module_tmpl.gni
+++ b/chrome/android/features/tab_ui/tab_ui_module_tmpl.gni
@@ -5,6 +5,7 @@
 import("//build/config/android/rules.gni")
 import("//build/config/locales.gni")
 import("//chrome/android/features/dynamic_feature_modules.gni")
+import("//chrome/android/features/tab_ui/buildflags.gni")
 
 template("tab_ui_module_tmpl") {
   assert(defined(invoker.version_code))
@@ -39,5 +40,11 @@
     deps = [
       "//chrome/android/features/tab_ui:java",
     ]
+
+    # If tab_ui is set as async DFM, generate list of dependencies on base for keep
+    # rule generation.
+    if (async_tab_ui) {
+      enable_class_deps_output = "tabUiConstantPoolDeps.txt"
+    }
   }
 }
diff --git a/chrome/android/touchless/java/res/drawable/ic_apps_blue_24dp.xml b/chrome/android/java/res/drawable/ic_apps_blue_24dp.xml
similarity index 100%
rename from chrome/android/touchless/java/res/drawable/ic_apps_blue_24dp.xml
rename to chrome/android/java/res/drawable/ic_apps_blue_24dp.xml
diff --git a/chrome/android/java/res/layout/top_sites_tile_view.xml b/chrome/android/java/res/layout/top_sites_tile_view.xml
new file mode 100644
index 0000000..aca78aa7
--- /dev/null
+++ b/chrome/android/java/res/layout/top_sites_tile_view.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2019 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<!-- An icon tile. -->
+<org.chromium.chrome.browser.suggestions.tile.TopSitesTileView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="@dimen/tile_view_width"
+    android:layout_height="wrap_content"
+    android:paddingStart="@dimen/tile_view_padding"
+    android:paddingEnd="@dimen/tile_view_padding" >
+
+    <!-- The icon background. -->
+    <View
+        android:id="@+id/tile_view_icon_background"
+        android:layout_width="@dimen/tile_view_icon_size"
+        android:layout_height="@dimen/tile_view_icon_size"
+        android:layout_gravity="center_horizontal"
+        android:layout_marginTop="@dimen/tile_view_icon_background_margin_top_modern"
+        android:background="@drawable/tile_view_icon_background_modern" />
+
+    <!-- The main icon. -->
+    <ImageView
+        android:id="@+id/tile_view_icon"
+        android:layout_width="@dimen/tile_view_icon_size_modern"
+        android:layout_height="@dimen/tile_view_icon_size_modern"
+        android:layout_gravity="center_horizontal"
+        android:layout_marginTop="@dimen/tile_view_icon_margin_top_modern"
+        android:importantForAccessibility="no" />
+
+    <!-- The touch highlight. -->
+    <View
+        android:id="@+id/tile_view_highlight"
+        android:layout_width="@dimen/tile_view_icon_size"
+        android:layout_height="@dimen/tile_view_icon_size"
+        android:layout_gravity="center_horizontal"
+        android:layout_marginTop="@dimen/tile_view_icon_background_margin_top_modern"
+        android:background="@drawable/tile_view_highlight" />
+
+    <!-- The offline badge. -->
+    <ImageView
+        android:id="@+id/offline_badge"
+        android:layout_width="@dimen/tile_view_offline_badge_size_modern"
+        android:layout_height="@dimen/tile_view_offline_badge_size_modern"
+        android:layout_gravity="top|end"
+        android:layout_marginTop="@dimen/tile_view_offline_badge_margin_top_modern"
+        android:layout_marginEnd="@dimen/tile_view_offline_badge_margin_end_modern"
+        android:visibility="gone"
+        android:contentDescription="@string/accessibility_ntp_offline_badge"
+        app:srcCompat="@drawable/ic_offline_pin_blue_white" />
+
+    <!-- The title. -->
+    <TextView
+        android:id="@+id/tile_view_title"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/tile_view_title_margin_top_modern"
+        android:ellipsize="end"
+        android:gravity="center_horizontal"
+        android:lines="2"
+        android:lineSpacingMultiplier="1.17"
+        android:textAppearance="@style/TextAppearance.BlackCaption" />
+</org.chromium.chrome.browser.suggestions.tile.TopSitesTileView>
diff --git a/chrome/android/java/res/layout/top_sites_tile_view_condensed.xml b/chrome/android/java/res/layout/top_sites_tile_view_condensed.xml
new file mode 100644
index 0000000..1ca4fe9
--- /dev/null
+++ b/chrome/android/java/res/layout/top_sites_tile_view_condensed.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2019 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<!-- An icon tile. -->
+<org.chromium.chrome.browser.suggestions.tile.TopSitesTileView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="@dimen/tile_view_width_condensed"
+    android:layout_height="wrap_content"
+    android:paddingStart="@dimen/tile_view_padding"
+    android:paddingEnd="@dimen/tile_view_padding" >
+
+    <!-- The icon background. -->
+    <View
+        android:id="@+id/tile_view_icon_background"
+        android:layout_width="@dimen/tile_view_icon_size"
+        android:layout_height="@dimen/tile_view_icon_size"
+        android:layout_gravity="center_horizontal"
+        android:layout_marginTop="@dimen/tile_view_icon_background_margin_top_modern"
+        android:background="@drawable/tile_view_icon_background_modern" />
+
+    <!-- The main icon. -->
+    <ImageView
+        android:id="@+id/tile_view_icon"
+        android:layout_width="@dimen/tile_view_icon_size_modern"
+        android:layout_height="@dimen/tile_view_icon_size_modern"
+        android:layout_gravity="center_horizontal"
+        android:layout_marginTop="@dimen/tile_view_icon_margin_top_modern"
+        android:importantForAccessibility="no" />
+
+    <!-- The touch highlight. -->
+    <View
+        android:id="@+id/tile_view_highlight"
+        android:layout_width="@dimen/tile_view_icon_size"
+        android:layout_height="@dimen/tile_view_icon_size"
+        android:layout_gravity="center_horizontal"
+        android:layout_marginTop="@dimen/tile_view_icon_background_margin_top_modern"
+        android:background="@drawable/tile_view_highlight" />
+
+    <!-- The offline badge. -->
+    <ImageView
+        android:id="@+id/offline_badge"
+        android:layout_width="@dimen/tile_view_offline_badge_size_modern"
+        android:layout_height="@dimen/tile_view_offline_badge_size_modern"
+        android:layout_gravity="top|end"
+        android:layout_marginTop="@dimen/tile_view_offline_badge_margin_top_modern_condensed"
+        android:layout_marginEnd="@dimen/tile_view_offline_badge_margin_end_modern_condensed"
+        android:visibility="gone"
+        android:contentDescription="@string/accessibility_ntp_offline_badge"
+        app:srcCompat="@drawable/ic_offline_pin_blue_white" />
+
+    <TextView
+        android:id="@+id/tile_view_title"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/tile_view_title_margin_top_modern"
+        android:ellipsize="end"
+        android:gravity="center_horizontal"
+        android:lines="2"
+        android:lineSpacingMultiplier="1.17"
+        android:textAppearance="@style/TextAppearance.BlackCaption" />
+</org.chromium.chrome.browser.suggestions.tile.TopSitesTileView>
diff --git a/chrome/android/java/res/xml/legal_information_preferences.xml b/chrome/android/java/res/xml/legal_information_preferences.xml
index 216cd19..07fe1b6 100644
--- a/chrome/android/java/res/xml/legal_information_preferences.xml
+++ b/chrome/android/java/res/xml/legal_information_preferences.xml
@@ -3,7 +3,7 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 
-<PreferenceScreen
+<android.support.v7.preference.PreferenceScreen
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto">
     <org.chromium.chrome.browser.preferences.HyperlinkPreference
@@ -18,4 +18,4 @@
         android:key="privacy_notice"
         android:title="@string/privacy_notice_title"
         app:url="@string/chrome_privacy_notice_url" />
-</PreferenceScreen>
+</android.support.v7.preference.PreferenceScreen>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/content/TabContentManager.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/content/TabContentManager.java
index c6968d6..de886c57 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/content/TabContentManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/content/TabContentManager.java
@@ -10,6 +10,7 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Matrix;
+import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.view.View;
 import android.view.ViewGroup.MarginLayoutParams;
@@ -295,7 +296,7 @@
      * Cache the content of a tab as a thumbnail.
      * @param tab The tab whose content we will cache.
      */
-    public void cacheTabThumbnail(final Tab tab) {
+    public void cacheTabThumbnail(@NonNull final Tab tab) {
         cacheTabThumbnail(tab, null);
     }
 
@@ -304,7 +305,7 @@
      * @param tab The tab whose content we will cache.
      * @param callback The callback to send the {@link Bitmap} with.
      */
-    public void cacheTabThumbnail(final Tab tab, @Nullable Callback<Bitmap> callback) {
+    public void cacheTabThumbnail(@NonNull final Tab tab, @Nullable Callback<Bitmap> callback) {
         if (mNativeTabContentManager != 0 && mSnapshotsEnabled) {
             if (tab.getNativePage() != null || isNativeViewShowing(tab)) {
                 Bitmap nativeBitmap = readbackNativeBitmap(tab, mThumbnailScale);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/content_capture/ContentCaptureHistoryDeletionObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/content_capture/ContentCaptureHistoryDeletionObserver.java
index ef7e18bd..f322b34 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/content_capture/ContentCaptureHistoryDeletionObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/content_capture/ContentCaptureHistoryDeletionObserver.java
@@ -22,8 +22,10 @@
                                 != historyDeletionInfo.getTimeRangeEnd())) {
             contentCaptureController.clearAllContentCaptureData();
         } else {
-            contentCaptureController.clearContentCaptureDataForURLs(
-                    historyDeletionInfo.getDeletedURLs());
+            String[] deletedURLs = historyDeletionInfo.getDeletedURLs();
+            if (deletedURLs.length > 0) {
+                contentCaptureController.clearContentCaptureDataForURLs(deletedURLs);
+            }
         }
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
index 3e3e3d1..1d15b452 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
@@ -1993,7 +1993,10 @@
      */
     private void maybeRecordBackgroundDownload(
             @UmaBackgroundDownload int event, String downloadGuid) {
-        if (sBackgroundDownloadIds.remove(downloadGuid)) {
+        if (sBackgroundDownloadIds.contains(downloadGuid)) {
+            if (event != UmaBackgroundDownload.INTERRUPTED) {
+                sBackgroundDownloadIds.remove(downloadGuid);
+            }
             DownloadNotificationUmaHelper.recordBackgroundDownloadHistogram(event);
         }
         if (downloadGuid.equals(mFirstBackgroundDownloadId)) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesBridge.java
index dabcc83a..84688cd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesBridge.java
@@ -106,6 +106,15 @@
         return nativeGetVariation();
     }
 
+    /**
+     * Gets the current Finch variation for last MostLikely icon that is configured by flag or
+     * experiment.
+     */
+    @MostLikelyVariation
+    public static int getIconVariation() {
+        return nativeGetIconVariation();
+    }
+
     public static boolean isEnabled(@ExploreSitesVariation int variation) {
         return variation == ExploreSitesVariation.ENABLED
                 || variation == ExploreSitesVariation.PERSONALIZED
@@ -154,6 +163,7 @@
     }
 
     static native int nativeGetVariation();
+    static native int nativeGetIconVariation();
     private static native void nativeGetEspCatalog(Profile profile,
             List<ExploreSitesCategory> result, Callback<List<ExploreSitesCategory>> callback);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/HyperlinkPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/HyperlinkPreference.java
index b8c2310..488eee2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/HyperlinkPreference.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/HyperlinkPreference.java
@@ -6,7 +6,8 @@
 
 import android.content.Context;
 import android.content.res.TypedArray;
-import android.preference.Preference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceViewHolder;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.View.OnClickListener;
@@ -22,7 +23,6 @@
  */
 public class HyperlinkPreference extends Preference {
 
-    private final int mTitleResId;
     private final int mUrlResId;
     private final int mColor;
     private final boolean mImitateWebLink;
@@ -34,7 +34,6 @@
         mUrlResId = a.getResourceId(R.styleable.HyperlinkPreference_url, 0);
         mImitateWebLink = a.getBoolean(R.styleable.HyperlinkPreference_imitateWebLink, false);
         a.recycle();
-        mTitleResId = getTitleRes();
         mColor = ApiCompatibilityUtils.getColor(
                 context.getResources(), R.color.default_text_color_link);
     }
@@ -46,9 +45,9 @@
     }
 
     @Override
-    protected void onBindView(View view) {
-        super.onBindView(view);
-        TextView titleView = (TextView) view.findViewById(android.R.id.title);
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+        TextView titleView = (TextView) holder.findViewById(android.R.id.title);
         titleView.setSingleLine(false);
 
         if (mImitateWebLink) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/LegalInformationPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/LegalInformationPreferences.java
index 2739980..3def9a2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/LegalInformationPreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/LegalInformationPreferences.java
@@ -5,18 +5,16 @@
 package org.chromium.chrome.browser.preferences;
 
 import android.os.Bundle;
-import android.preference.PreferenceFragment;
+import android.support.v7.preference.PreferenceFragmentCompat;
 
 import org.chromium.chrome.R;
 
 /**
  * Fragment to display legal information about Chrome.
  */
-public class LegalInformationPreferences extends PreferenceFragment {
-
+public class LegalInformationPreferences extends PreferenceFragmentCompat {
     @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
+    public void onCreatePreferences(Bundle savedInstanceState, String s) {
         PreferenceUtils.addPreferencesFromResource(this, R.xml.legal_information_preferences);
         getActivity().setTitle(R.string.legal_information_title);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsTileView.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsTileView.java
index ecefd83..b20a7d91 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsTileView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsTileView.java
@@ -60,7 +60,7 @@
         setOfflineBadgeVisibility(tile.isOfflineAvailable());
     }
 
-    private void setIconViewLayoutParams(Tile tile) {
+    protected void setIconViewLayoutParams(Tile tile) {
         MarginLayoutParams params = (MarginLayoutParams) mIconView.getLayoutParams();
         Resources resources = getResources();
         if (tile.getType() == TileVisualType.ICON_COLOR
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileRenderer.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileRenderer.java
index 606acf7..8ae8ea4a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileRenderer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileRenderer.java
@@ -11,6 +11,7 @@
 import android.graphics.Color;
 import android.graphics.drawable.BitmapDrawable;
 import android.support.annotation.LayoutRes;
+import android.support.graphics.drawable.VectorDrawableCompat;
 import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
 import android.view.LayoutInflater;
 import android.view.ViewGroup;
@@ -20,11 +21,14 @@
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.task.AsyncTask;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.explore_sites.ExploreSitesBridge;
+import org.chromium.chrome.browser.explore_sites.MostLikelyVariation;
 import org.chromium.chrome.browser.favicon.IconType;
 import org.chromium.chrome.browser.favicon.LargeIconBridge;
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.suggestions.SuggestionsConfig.TileStyle;
+import org.chromium.chrome.browser.suggestions.tile.TopSitesTileView;
 import org.chromium.chrome.browser.util.ViewUtils;
 import org.chromium.chrome.browser.widget.RoundedIconGenerator;
 import org.chromium.components.feature_engagement.EventConstants;
@@ -44,6 +48,7 @@
     private final Resources mResources;
     private final ImageFetcher mImageFetcher;
     private final RoundedIconGenerator mIconGenerator;
+    private final Resources.Theme mTheme;
 
     @TileStyle
     private final int mStyle;
@@ -55,6 +60,9 @@
     @LayoutRes
     private final int mLayout;
 
+    @LayoutRes
+    private final int mTopSitesLayout;
+
     public TileRenderer(
             Context context, @TileStyle int style, int titleLines, ImageFetcher imageFetcher) {
         mImageFetcher = imageFetcher;
@@ -62,6 +70,7 @@
         mTitleLinesCount = titleLines;
 
         mResources = context.getResources();
+        mTheme = context.getTheme();
         mDesiredIconSize = mResources.getDimensionPixelSize(R.dimen.tile_view_icon_size);
         mIconCornerRadius = mResources.getDimension(R.dimen.tile_view_icon_corner_radius);
         int minIconSize = mResources.getDimensionPixelSize(R.dimen.tile_view_icon_min_size);
@@ -70,6 +79,7 @@
         mMinIconSize = Math.min(mDesiredIconSize, minIconSize);
 
         mLayout = getLayout();
+        mTopSitesLayout = getTopSitesLayout();
 
         int iconColor = ApiCompatibilityUtils.getColor(
                 mResources, R.color.default_favicon_background_color);
@@ -127,14 +137,46 @@
     @VisibleForTesting
     SuggestionsTileView buildTileView(
             Tile tile, ViewGroup parentView, TileGroup.TileSetupDelegate setupDelegate) {
-        SuggestionsTileView tileView =
-                (SuggestionsTileView) LayoutInflater.from(parentView.getContext())
-                        .inflate(mLayout, parentView, false);
+        SuggestionsTileView tileView;
+
+        if (tile.getSource() == TileSource.EXPLORE) {
+            tileView = (TopSitesTileView) LayoutInflater.from(parentView.getContext())
+                               .inflate(mTopSitesLayout, parentView, false);
+
+            int iconVariation = ExploreSitesBridge.getIconVariation();
+            if (iconVariation == MostLikelyVariation.ICON_ARROW) {
+                tile.setIcon(VectorDrawableCompat.create(
+                        mResources, R.drawable.ic_arrow_forward_blue_24dp, mTheme));
+                tile.setType(TileVisualType.ICON_REAL);
+            } else if (iconVariation == MostLikelyVariation.ICON_DOTS) {
+                tile.setIcon(VectorDrawableCompat.create(
+                        mResources, R.drawable.ic_apps_blue_24dp, mTheme));
+                tile.setType(TileVisualType.ICON_REAL);
+            } else if (iconVariation == MostLikelyVariation.ICON_GROUPED) {
+                tile.setIcon(VectorDrawableCompat.create(
+                        mResources, R.drawable.ic_apps_blue_24dp, mTheme));
+                tile.setType(TileVisualType.ICON_DEFAULT);
+
+                // One task to load actual icon.
+                LargeIconBridge.LargeIconCallback bridgeCallback =
+                        setupDelegate.createIconLoadCallback(tile);
+                ExploreSitesBridge.getSummaryImage(Profile.getLastUsedProfile(), mDesiredIconSize,
+                        (Bitmap img)
+                                -> bridgeCallback.onLargeIconAvailable(
+                                        img, Color.BLACK, false, IconType.FAVICON));
+            }
+        } else {
+            tileView = (SuggestionsTileView) LayoutInflater.from(parentView.getContext())
+                               .inflate(mLayout, parentView, false);
+        }
+
         tileView.initialize(tile, mTitleLinesCount);
 
         // Note: It is important that the callbacks below don't keep a reference to the tile or
         // modify them as there is no guarantee that the same tile would be used to update the view.
-        fetchIcon(tile.getData(), setupDelegate.createIconLoadCallback(tile));
+        if (tile.getSource() != TileSource.EXPLORE) {
+            fetchIcon(tile.getData(), setupDelegate.createIconLoadCallback(tile));
+        }
 
         TileGroup.TileInteractionDelegate delegate = setupDelegate.createInteractionDelegate(tile);
         if (tile.getSource() == TileSource.HOMEPAGE) {
@@ -182,8 +224,11 @@
     }
 
     public void setTileIconFromBitmap(Tile tile, Bitmap icon) {
-        RoundedBitmapDrawable roundedIcon = ViewUtils.createRoundedBitmapDrawable(
-                icon, Math.round(mIconCornerRadius * icon.getWidth() / mDesiredIconSize));
+        int radius = Math.round(mIconCornerRadius * icon.getWidth() / mDesiredIconSize);
+        if (tile.getSource() == TileSource.EXPLORE) {
+            radius = mDesiredIconSize / 2;
+        }
+        RoundedBitmapDrawable roundedIcon = ViewUtils.createRoundedBitmapDrawable(icon, radius);
         roundedIcon.setAntiAlias(true);
         roundedIcon.setFilterBitmap(true);
 
@@ -192,6 +237,10 @@
     }
 
     public void setTileIconFromColor(Tile tile, int fallbackColor, boolean isFallbackColorDefault) {
+        // Explore should not have generated icons.
+        if (tile.getSource() == TileSource.EXPLORE) {
+            return;
+        }
         mIconGenerator.setBackgroundColor(fallbackColor);
         Bitmap icon = mIconGenerator.generateIconForUrl(tile.getUrl());
         tile.setIcon(new BitmapDrawable(mResources, icon));
@@ -210,4 +259,16 @@
         assert false;
         return 0;
     }
+
+    @LayoutRes
+    private int getTopSitesLayout() {
+        switch (mStyle) {
+            case TileStyle.MODERN:
+                return R.layout.top_sites_tile_view;
+            case TileStyle.MODERN_CONDENSED:
+                return R.layout.top_sites_tile_view_condensed;
+        }
+        assert false;
+        return 0;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/OWNERS
new file mode 100644
index 0000000..125bc14
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/OWNERS
@@ -0,0 +1 @@
+file://chrome/browser/android/explore_sites/OWNERS
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TopSitesTileView.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TopSitesTileView.java
new file mode 100644
index 0000000..31b6c51e
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TopSitesTileView.java
@@ -0,0 +1,51 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.suggestions.tile;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.AttributeSet;
+
+import org.chromium.chrome.browser.explore_sites.ExploreSitesBridge;
+import org.chromium.chrome.browser.explore_sites.MostLikelyVariation;
+import org.chromium.chrome.browser.suggestions.SuggestionsTileView;
+import org.chromium.chrome.browser.suggestions.Tile;
+import org.chromium.chrome.browser.suggestions.TileVisualType;
+
+/**
+ * The view for a top sites tile. Displays the title of the site beneath a large icon.
+ */
+public class TopSitesTileView extends SuggestionsTileView {
+    /**
+     * Constructor for inflating from XML.
+     */
+    public TopSitesTileView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void setIconViewLayoutParams(Tile tile) {
+        MarginLayoutParams params = (MarginLayoutParams) mIconView.getLayoutParams();
+        Resources resources = getResources();
+        if (tile.getType() == TileVisualType.ICON_REAL
+                && ExploreSitesBridge.getIconVariation() == MostLikelyVariation.ICON_GROUPED) {
+            // Grouped icons have extra large size.
+            params.width = resources.getDimensionPixelOffset(
+                    org.chromium.chrome.R.dimen.tile_view_icon_size);
+            params.height = resources.getDimensionPixelSize(
+                    org.chromium.chrome.R.dimen.tile_view_icon_size);
+            params.topMargin = resources.getDimensionPixelSize(
+                    org.chromium.chrome.R.dimen.tile_view_icon_background_margin_top_modern);
+        } else {
+            params.width = resources.getDimensionPixelSize(
+                    org.chromium.chrome.R.dimen.tile_view_icon_size_modern);
+            params.height = resources.getDimensionPixelSize(
+                    org.chromium.chrome.R.dimen.tile_view_icon_size_modern);
+            params.topMargin = resources.getDimensionPixelSize(
+                    org.chromium.chrome.R.dimen.tile_view_icon_margin_top_modern);
+        }
+        mIconView.setLayoutParams(params);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabBrowserControlsOffsetHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabBrowserControlsOffsetHelper.java
index fcc734d0..fa26b8fb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabBrowserControlsOffsetHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabBrowserControlsOffsetHelper.java
@@ -103,7 +103,7 @@
         mPreviousContentOffsetY = contentOffsetY;
         mAreOffsetsInitialized = true;
 
-        if (FullscreenManager.from(mTab) == null) return;
+        if (mTab.isHidden()) return;
         if (SadTab.isShowing(mTab) || mTab.isNativePage()) {
             showAndroidControls(false);
         } else {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabBrowserControlsState.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabBrowserControlsState.java
index 6210f6a..ac4062b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabBrowserControlsState.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabBrowserControlsState.java
@@ -87,7 +87,7 @@
 
             @Override
             public void onRendererResponsiveStateChanged(Tab tab, boolean isResponsive) {
-                if (FullscreenManager.from(mTab) == null) return;
+                if (mTab.isHidden()) return;
                 if (isResponsive) {
                     updateEnabledState();
                 } else {
@@ -198,7 +198,7 @@
 
     @Override
     public void onNodeAttributeUpdated(boolean editable, boolean password) {
-        if (FullscreenManager.from(mTab) == null) return;
+        if (mTab.isHidden()) return;
         updateEnabledState();
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegate.java
index 243e5d9..8cf44b6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegate.java
@@ -159,7 +159,7 @@
         enableHidingBrowserControls &= !mTab.isShowingErrorPage();
         enableHidingBrowserControls &= !webContents.isShowingInterstitialPage();
         enableHidingBrowserControls &= !mTab.isRendererUnresponsive();
-        enableHidingBrowserControls &= (FullscreenManager.from(mTab) != null);
+        enableHidingBrowserControls &= !mTab.isHidden();
         enableHidingBrowserControls &= DeviceClassManager.enableFullscreen();
         enableHidingBrowserControls &= !mIsFullscreenWaitingForLoad;
         enableHidingBrowserControls &= !TabModalPresenter.isDialogShowing(mTab);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/EmptyTabModelFilter.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/EmptyTabModelFilter.java
index f2066924..3ed6e46 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/EmptyTabModelFilter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/EmptyTabModelFilter.java
@@ -28,6 +28,9 @@
     @Override
     protected void reorder() {}
 
+    @Override
+    protected void resetFilterStateInternal() {}
+
     // TabList implementation.
     @Override
     public boolean isIncognito() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelFilter.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelFilter.java
index 16823987..cfacc71 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelFilter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelFilter.java
@@ -110,6 +110,25 @@
      */
     protected abstract void reorder();
 
+    /**
+     * Concrete class requires to define what to clean up.
+     */
+    protected abstract void resetFilterStateInternal();
+
+    /**
+     * Calls {@code resetFilterStateInternal} method to clean up filter internal data, and resets
+     * the internal data based on the current {@link TabModel}.
+     */
+    protected void resetFilterState() {
+        resetFilterStateInternal();
+
+        TabModel tabModel = getTabModel();
+        for (int i = 0; i < tabModel.getCount(); i++) {
+            Tab tab = tabModel.getTabAt(i);
+            addTab(tab);
+        }
+    }
+
     // TODO(crbug.com/948518): This is a band-aid fix for not crashing when undo the last closed
     // tab, should remove later.
     /**
@@ -161,7 +180,6 @@
 
     @Override
     public void didMoveTab(Tab tab, int newIndex, int curIndex) {
-        reorder();
         for (TabModelObserver observer : mFilteredObservers) {
             observer.didMoveTab(tab, newIndex, curIndex);
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tasks/TaskRecognizer.java b/chrome/android/java/src/org/chromium/chrome/browser/tasks/TaskRecognizer.java
index c6f52d7..9048fca 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tasks/TaskRecognizer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tasks/TaskRecognizer.java
@@ -66,8 +66,8 @@
      * @param tab The tab that might be about a product.  Must be the current front tab.
      */
     private void tryToShowProduct(Tab tab) {
-        boolean isCurrentSelectedTab =
-                tab != null && tab.equals(tab.getActivity().getActivityTab());
+        boolean isCurrentSelectedTab = tab != null && tab.getActivity() != null
+                && tab.equals(tab.getActivity().getActivityTab());
         if (mTabInUse != null || !isCurrentSelectedTab) {
             return;
         }
@@ -131,6 +131,8 @@
      */
     private void createEphemeralTabFor(
             Tab activeTab, ResolvedSearchTerm resolvedSearchTerm, Uri searchUrl) {
+        if (activeTab == null || activeTab.getActivity() == null) return;
+
         EphemeralTabPanel displayPanel = activeTab.getActivity().getEphemeralTabPanel();
         if (displayPanel != null) {
             displayPanel.requestOpenPanel(searchUrl.toString(), resolvedSearchTerm.displayText(),
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 bec2314..860b12a4 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
@@ -48,6 +48,15 @@
      */
     public interface Observer {
         /**
+         * This method is called after a tab is moved to form a group or moved into an existed
+         * group.
+         * @param movedTab The {@link Tab} which has been moved. If a group is merged to a tab or
+         *                 another group, this is the last tab of the merged group.
+         * @param selectedTabIdInGroup The id of the selected {@link Tab} in group.
+         */
+        void didMergeTabToGroup(Tab movedTab, int selectedTabIdInGroup);
+
+        /**
          * This method is called after a group is moved.
          *
          * @param movedTab The tab which has been moved. This is the last tab within the group.
@@ -139,6 +148,7 @@
     // The number of groups with at least 2 tabs.
     private int mActualGroupCount;
     private Tab mAbsentSelectedTab;
+    private boolean mShouldRecordUma = true;
 
     public TabGroupModelFilter(TabModel tabModel) {
         super(tabModel);
@@ -223,6 +233,71 @@
         }
     }
 
+    /**
+     * This method merges the source group that contains the {@code sourceTabId} to the destination
+     * group that contains the {@code destinationTabId}. This method only operates if two groups are
+     * in the same {@code TabModel}.
+     *
+     * @param sourceTabId The id of the {@link Tab} to get the source group.
+     * @param destinationTabId The id of a {@link Tab} to get the destination group.
+     */
+    public void mergeTabsToGroup(int sourceTabId, int destinationTabId) {
+        Tab sourceTab = TabModelUtils.getTabById(getTabModel(), sourceTabId);
+        Tab destinationTab = TabModelUtils.getTabById(getTabModel(), destinationTabId);
+
+        assert sourceTab != null && destinationTab != null
+                && sourceTab.isIncognito()
+                        == destinationTab.isIncognito()
+            : "Attempting to merge groups from different model";
+
+        int destinationGroupId = destinationTab.getRootId();
+        List<Tab> tabsToMerge = getRelatedTabList(sourceTabId);
+        int sourceTabIndexInTabModel =
+                TabModelUtils.getTabIndexById(getTabModel(), sourceTab.getId());
+        int destinationIndexInTabModel = getTabModelDestinationIndex(destinationTab);
+        boolean isMovingBackward = sourceTabIndexInTabModel < destinationIndexInTabModel;
+
+        if (!needToUpdateTabModel(tabsToMerge, destinationIndexInTabModel)) {
+            for (int i = 0; i < tabsToMerge.size(); i++) {
+                Tab tab = tabsToMerge.get(i);
+                tab.setRootId(destinationGroupId);
+            }
+            resetFilterState();
+
+            Tab lastMergedTab = tabsToMerge.get(tabsToMerge.size() - 1);
+            TabGroup group = mGroupIdToGroupMap.get(lastMergedTab.getRootId());
+            for (Observer observer : mGroupFilterObserver) {
+                observer.didMergeTabToGroup(
+                        tabsToMerge.get(tabsToMerge.size() - 1), group.getLastShownTabId());
+            }
+        } else {
+            for (int i = 0; i < tabsToMerge.size(); i++) {
+                Tab tab = tabsToMerge.get(i);
+                tab.setRootId(destinationGroupId);
+                getTabModel().moveTab(tab.getId(),
+                        isMovingBackward ? destinationIndexInTabModel
+                                         : destinationIndexInTabModel++);
+            }
+        }
+    }
+
+    private int getTabModelDestinationIndex(Tab destinationTab) {
+        List<Integer> destinationGroupedTabIds =
+                mGroupIdToGroupMap.get(destinationTab.getRootId()).getTabIdList();
+        int destinationTabIndex = TabModelUtils.getTabIndexById(
+                getTabModel(), destinationGroupedTabIds.get(destinationGroupedTabIds.size() - 1));
+
+        return destinationTabIndex + 1;
+    }
+
+    private boolean needToUpdateTabModel(List<Tab> tabsToMerge, int destinationIndexInTabModel) {
+        assert tabsToMerge.size() > 0;
+
+        int firstTabIndexInTabModel =
+                TabModelUtils.getTabIndexById(getTabModel(), tabsToMerge.get(0).getId());
+        return firstTabIndexInTabModel != destinationIndexInTabModel;
+    }
+
     // TODO(crbug.com/951608): follow up with sessions count histogram for TabGroups.
     private int updateAndGetSessionsCount(int groupId) {
         ThreadUtils.assertOnBackgroundThread();
@@ -276,7 +351,8 @@
         if (mGroupIdToGroupMap.containsKey(groupId)) {
             if (mGroupIdToGroupMap.get(groupId).size() == 1) {
                 mActualGroupCount++;
-                if (tab.getLaunchType() == TabLaunchType.FROM_LONGPRESS_BACKGROUND) {
+                if (mShouldRecordUma
+                        && tab.getLaunchType() == TabLaunchType.FROM_LONGPRESS_BACKGROUND) {
                     RecordUserAction.record("TabGroup.Created.OpenInNewTab");
                 }
             }
@@ -377,24 +453,77 @@
     }
 
     @Override
+    protected void resetFilterStateInternal() {
+        mGroupIdToGroupIndexMap.clear();
+        mGroupIdToGroupMap.clear();
+        mActualGroupCount = 0;
+    }
+
+    @Override
+    protected void resetFilterState() {
+        mShouldRecordUma = false;
+        super.resetFilterState();
+
+        TabModel tabModel = getTabModel();
+        selectTab(tabModel.getTabAt(tabModel.index()));
+        mShouldRecordUma = true;
+    }
+
+    @Override
     protected boolean shouldNotifyObserversOnSetIndex() {
         return mAbsentSelectedTab == null;
     }
 
     @Override
     public void didMoveTab(Tab tab, int newIndex, int curIndex) {
-        super.didMoveTab(tab, newIndex, curIndex);
+        // Need to cache the flag before resetting the internal data map.
+        boolean isMergeTabToGroup = isMergeTabToGroup(tab);
+        int groupIdBeforeMove = getGroupIdBeforeMove(tab, isMergeTabToGroup);
+        assert groupIdBeforeMove != TabGroup.INVALID_GROUP_ID;
+        TabGroup groupBeforeMove = mGroupIdToGroupMap.get(groupIdBeforeMove);
 
-        if (isMoveWithinGroup(tab, curIndex, newIndex)) {
+        if (isMergeTabToGroup) {
+            resetFilterState();
+            if (groupBeforeMove != null && groupBeforeMove.size() != 1) return;
+
+            TabGroup group = mGroupIdToGroupMap.get(tab.getRootId());
             for (Observer observer : mGroupFilterObserver) {
-                observer.didMoveWithinGroup(tab, curIndex, newIndex);
+                observer.didMergeTabToGroup(tab, group.getLastShownTabId());
             }
         } else {
-            if (!hasFinishedMovingGroup(tab, newIndex)) return;
-            for (Observer observer : mGroupFilterObserver) {
-                observer.didMoveTabGroup(tab, curIndex, newIndex);
+            reorder();
+
+            if (isMoveWithinGroup(tab, curIndex, newIndex)) {
+                for (Observer observer : mGroupFilterObserver) {
+                    observer.didMoveWithinGroup(tab, curIndex, newIndex);
+                }
+            } else {
+                if (!hasFinishedMovingGroup(tab, newIndex)) return;
+                for (Observer observer : mGroupFilterObserver) {
+                    observer.didMoveTabGroup(tab, curIndex, newIndex);
+                }
             }
         }
+
+        super.didMoveTab(tab, newIndex, curIndex);
+    }
+
+    private boolean isMergeTabToGroup(Tab tab) {
+        TabGroup tabGroup = mGroupIdToGroupMap.get(tab.getRootId());
+        return !tabGroup.contains(tab.getId());
+    }
+
+    private int getGroupIdBeforeMove(Tab tabToMove, boolean isMoveToDifferentGroup) {
+        if (!isMoveToDifferentGroup) return tabToMove.getRootId();
+
+        Set<Integer> groupIdSet = mGroupIdToGroupMap.keySet();
+        for (Integer groupIdKey : groupIdSet) {
+            if (mGroupIdToGroupMap.get(groupIdKey).contains(tabToMove.getId())) {
+                return groupIdKey;
+            }
+        }
+
+        return TabGroup.INVALID_GROUP_ID;
     }
 
     private boolean isMoveWithinGroup(
@@ -409,8 +538,13 @@
 
     private boolean hasFinishedMovingGroup(Tab movedTab, int newIndexInTabModel) {
         TabGroup tabGroup = mGroupIdToGroupMap.get(movedTab.getRootId());
-        int offsetIndex = Math.abs(newIndexInTabModel - tabGroup.size() + 1);
-        return tabGroup.contains(getTabModel().getTabAt(offsetIndex).getId());
+        int offsetIndex = newIndexInTabModel - tabGroup.size() + 1;
+        if (offsetIndex < 0) return false;
+
+        for (int i = newIndexInTabModel; i >= offsetIndex; i--) {
+            if (getTabModel().getTabAt(i).getRootId() != movedTab.getRootId()) return false;
+        }
+        return true;
     }
 
     // TabList implementation.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/GridTabSwitcherLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/GridTabSwitcherLayout.java
index 7d0f412a..b2bf348 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/GridTabSwitcherLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/GridTabSwitcherLayout.java
@@ -172,7 +172,8 @@
     @Override
     public void onOverviewModeFinishedShowing() {
         doneShowing();
-        mTabContentManager.cacheTabThumbnail(mTabModelSelector.getCurrentTab());
+        Tab currentTab = mTabModelSelector.getCurrentTab();
+        if (currentTab != null) mTabContentManager.cacheTabThumbnail(currentTab);
     }
 
     @Override
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
index 0fab6ff..b6b401d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
@@ -2415,7 +2415,7 @@
     @SmallTest
     @Feature({"ContextualSearch"})
     @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
-    @DisableIf.Build(sdk_is_less_than = Build.VERSION_CODES.LOLLIPOP, message = "crbug.com/965706")
+    @DisableIf.Build(sdk_is_less_than = Build.VERSION_CODES.M, message = "crbug.com/965706")
     public void testTapMultipleSwipeOnlyLoadsContentOnce()
             throws InterruptedException, TimeoutException {
         // Simulate a tap and make sure Content is not visible.
@@ -2452,7 +2452,7 @@
     @SmallTest
     @Feature({"ContextualSearch"})
     @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
-    @DisableIf.Build(sdk_is_less_than = Build.VERSION_CODES.LOLLIPOP, message = "crbug.com/965706")
+    @DisableIf.Build(sdk_is_less_than = Build.VERSION_CODES.M, message = "crbug.com/965706")
     public void testLongPressMultipleSwipeOnlyLoadsContentOnce()
             throws InterruptedException, TimeoutException {
         // Simulate a long press and make sure no Content is created.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/widget/GridTabSwitcherLayoutPerfTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/GridTabSwitcherLayoutPerfTest.java
index f565907..e8a6f49 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/widget/GridTabSwitcherLayoutPerfTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/GridTabSwitcherLayoutPerfTest.java
@@ -120,6 +120,14 @@
 
     @Test
     @MediumTest
+    @CommandLineFlags.Add({"force-fieldtrial-params=Study.Group:downsampling-scale/1"})
+    public void testTabToGridFromLiveTabWith10TabsNoDownsample() throws InterruptedException {
+        prepareTabs(10, NTP_URL);
+        reportTabToGridPerf(mUrl, "Tab-to-Grid from live tab with 10 tabs (no downsample)");
+    }
+
+    @Test
+    @MediumTest
     public void testTabToGridFromLiveTabWith10TabsWithoutThumbnail() throws InterruptedException {
         // Note that most of the tabs won't have thumbnails.
         prepareTabs(10, null);
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
new file mode 100644
index 0000000..631f40e
--- /dev/null
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/tasks/tabgroup/TabGroupModelFilterUnitTest.java
@@ -0,0 +1,344 @@
+// 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.tabgroup;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.robolectric.annotation.Config;
+
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.RecordUserAction;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tabmodel.TabLaunchType;
+import org.chromium.chrome.browser.tabmodel.TabModel;
+import org.chromium.chrome.browser.tabmodel.TabModelObserver;
+import org.chromium.chrome.browser.tabmodel.TabModelUtils;
+import org.chromium.testing.local.LocalRobolectricTestRunner;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Tests for {@link TabGroupModelFilter}.
+ */
+@RunWith(LocalRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class TabGroupModelFilterUnitTest {
+    private static final int TAB1_ID = 456;
+    private static final int TAB2_ID = 789;
+    private static final int TAB3_ID = 123;
+    private static final int TAB4_ID = 147;
+    private static final int TAB5_ID = 258;
+    private static final int TAB6_ID = 369;
+    private static final int TAB1_ROOT_ID = TAB1_ID;
+    private static final int TAB2_ROOT_ID = TAB2_ID;
+    private static final int TAB3_ROOT_ID = TAB2_ID;
+    private static final int TAB4_ROOT_ID = TAB4_ID;
+    private static final int TAB5_ROOT_ID = TAB5_ID;
+    private static final int TAB6_ROOT_ID = TAB5_ID;
+    private static final int POSITION1 = 0;
+    private static final int POSITION2 = 1;
+    private static final int POSITION3 = 2;
+    private static final int POSITION4 = 3;
+    private static final int POSITION5 = 4;
+    private static final int POSITION6 = 5;
+
+    @Mock
+    TabModel mTabModel;
+
+    @Mock
+    TabGroupModelFilter.Observer mTabGroupModelFilterObserver;
+
+    @Captor
+    ArgumentCaptor<TabModelObserver> mTabModelObserverCaptor;
+
+    private Tab mTab1;
+    private Tab mTab2;
+    private Tab mTab3;
+    private Tab mTab4;
+    private Tab mTab5;
+    private Tab mTab6;
+    private List<Tab> mTabs = new ArrayList<>();
+
+    private TabGroupModelFilter mTabGroupModelFilter;
+
+    private Tab prepareTab(int tabId, int rootId) {
+        Tab tab = mock(Tab.class);
+
+        doAnswer(new Answer() {
+            @Override
+            public Object answer(InvocationOnMock invocation) throws Throwable {
+                int newRootId = invocation.getArgument(0);
+                doReturn(newRootId).when(tab).getRootId();
+                return null;
+            }
+        }).when(tab).setRootId(anyInt());
+
+        doReturn(tabId).when(tab).getId();
+        tab.setRootId(rootId);
+
+        return tab;
+    }
+
+    private void setRootId(Tab tab, int rootId) {
+        doAnswer(new Answer() {
+            @Override
+            public Object answer(InvocationOnMock invocation) throws Throwable {
+                int newRootId = invocation.getArgument(0);
+                doReturn(newRootId).when(tab).getRootId();
+                return null;
+            }
+        }).when(tab).setRootId(rootId);
+    }
+
+    private void setUpTab() {
+        mTab1 = prepareTab(TAB1_ID, TAB1_ROOT_ID);
+        mTab2 = prepareTab(TAB2_ID, TAB2_ROOT_ID);
+        mTab3 = prepareTab(TAB3_ID, TAB3_ROOT_ID);
+        mTab4 = prepareTab(TAB4_ID, TAB4_ROOT_ID);
+        mTab5 = prepareTab(TAB5_ID, TAB5_ROOT_ID);
+        mTab6 = prepareTab(TAB6_ID, TAB6_ROOT_ID);
+    }
+
+    private void setUpTabModel() {
+        doAnswer(new Answer() {
+            @Override
+            public Object answer(InvocationOnMock invocation) throws Throwable {
+                Tab tab = invocation.getArgument(0);
+                mTabs.add(tab);
+                return null;
+            }
+        }).when(mTabModel).addTab(any(Tab.class), anyInt(), anyInt());
+
+        doAnswer(new Answer() {
+            @Override
+            public Object answer(InvocationOnMock invocation) throws Throwable {
+                int movedTabId = invocation.getArgument(0);
+                int newIndex = invocation.getArgument(1);
+
+                int oldIndex = TabModelUtils.getTabIndexById(mTabModel, movedTabId);
+                Tab tab = TabModelUtils.getTabById(mTabModel, movedTabId);
+
+                mTabs.remove(tab);
+                if (oldIndex < newIndex) --newIndex;
+                mTabs.add(newIndex, tab);
+                mTabModelObserverCaptor.getValue().didMoveTab(tab, newIndex, oldIndex);
+                return null;
+            }
+        }).when(mTabModel).moveTab(anyInt(), anyInt());
+
+        doAnswer(new Answer() {
+            @Override
+            public Tab answer(InvocationOnMock invocation) throws Throwable {
+                int index = invocation.getArgument(0);
+                return mTabs.get(index);
+            }
+        }).when(mTabModel).getTabAt(anyInt());
+
+        doAnswer(new Answer() {
+            @Override
+            public Integer answer(InvocationOnMock invocation) throws Throwable {
+                Tab tab = invocation.getArgument(0);
+                return mTabs.indexOf(tab);
+            }
+        }).when(mTabModel).indexOf(any(Tab.class));
+
+        doAnswer(new Answer() {
+            @Override
+            public Integer answer(InvocationOnMock invocation) throws Throwable {
+                return mTabs.size();
+            }
+        }).when(mTabModel).getCount();
+
+        doReturn(0).when(mTabModel).index();
+    }
+
+    @Before
+    public void setUp() {
+        RecordUserAction.setDisabledForTests(true);
+        RecordHistogram.setDisabledForTests(true);
+
+        MockitoAnnotations.initMocks(this);
+
+        setUpTab();
+        setUpTabModel();
+
+        doNothing().when(mTabModel).addObserver(mTabModelObserverCaptor.capture());
+
+        mTabGroupModelFilter = new TabGroupModelFilter(mTabModel);
+        mTabGroupModelFilter.addTabGroupObserver(mTabGroupModelFilterObserver);
+
+        mTabModel.addTab(mTab1, -1, TabLaunchType.FROM_CHROME_UI);
+        mTabModelObserverCaptor.getValue().didAddTab(mTab1, TabLaunchType.FROM_CHROME_UI);
+
+        mTabModel.addTab(mTab2, -1, TabLaunchType.FROM_CHROME_UI);
+        mTabModelObserverCaptor.getValue().didAddTab(mTab2, TabLaunchType.FROM_CHROME_UI);
+
+        mTabModel.addTab(mTab3, -1, TabLaunchType.FROM_CHROME_UI);
+        mTabModelObserverCaptor.getValue().didAddTab(mTab3, TabLaunchType.FROM_CHROME_UI);
+
+        mTabModel.addTab(mTab4, -1, TabLaunchType.FROM_CHROME_UI);
+        mTabModelObserverCaptor.getValue().didAddTab(mTab4, TabLaunchType.FROM_CHROME_UI);
+
+        mTabModel.addTab(mTab5, -1, TabLaunchType.FROM_CHROME_UI);
+        mTabModelObserverCaptor.getValue().didAddTab(mTab5, TabLaunchType.FROM_CHROME_UI);
+
+        mTabModel.addTab(mTab6, -1, TabLaunchType.FROM_CHROME_UI);
+        mTabModelObserverCaptor.getValue().didAddTab(mTab6, TabLaunchType.FROM_CHROME_UI);
+    }
+
+    @After
+    public void tearDown() {
+        RecordUserAction.setDisabledForTests(false);
+        RecordHistogram.setDisabledForTests(false);
+    }
+
+    @Test
+    public void mergeTabToGroup_No_Update_TabModel() {
+        List<Tab> expectedGroup = new ArrayList<>(Arrays.asList(mTab2, mTab3, mTab4));
+
+        mTabGroupModelFilter.mergeTabsToGroup(mTab4.getId(), mTab2.getId());
+
+        verify(mTabModel, never()).moveTab(anyInt(), anyInt());
+        assertArrayEquals(mTabGroupModelFilter.getRelatedTabList(mTab4.getId()).toArray(),
+                expectedGroup.toArray());
+    }
+
+    @Test
+    public void mergeTabToGroup_Update_TabModel() {
+        mTabGroupModelFilter.mergeTabsToGroup(mTab5.getId(), mTab2.getId());
+        verify(mTabModel).moveTab(mTab5.getId(), POSITION3 + 1);
+    }
+
+    @Test
+    public void mergeOneTabToTab_Forward() {
+        List<Tab> expectedGroup = new ArrayList<>(Arrays.asList(mTab1, mTab4));
+        List<Tab> expectedTabModel =
+                new ArrayList<>(Arrays.asList(mTab1, mTab4, mTab2, mTab3, mTab5, mTab6));
+        int startIndex = POSITION1;
+
+        mTabGroupModelFilter.mergeTabsToGroup(mTab4.getId(), mTab1.getId());
+
+        verify(mTabModel).moveTab(mTab4.getId(), ++startIndex);
+        verify(mTabGroupModelFilterObserver).didMergeTabToGroup(mTab4, mTab1.getId());
+        assertArrayEquals(mTabGroupModelFilter.getRelatedTabList(mTab4.getId()).toArray(),
+                expectedGroup.toArray());
+        assertArrayEquals(mTabs.toArray(), expectedTabModel.toArray());
+    }
+
+    @Test
+    public void mergeGroupToTab_Forward() {
+        List<Tab> expectedGroup = new ArrayList<>(Arrays.asList(mTab1, mTab5, mTab6));
+        List<Tab> expectedTabModel =
+                new ArrayList<>(Arrays.asList(mTab1, mTab5, mTab6, mTab2, mTab3, mTab4));
+        int startIndex = POSITION1;
+
+        mTabGroupModelFilter.mergeTabsToGroup(mTab5.getId(), mTab1.getId());
+
+        verify(mTabModel).moveTab(mTab5.getId(), ++startIndex);
+        verify(mTabModel).moveTab(mTab6.getId(), ++startIndex);
+        verify(mTabGroupModelFilterObserver).didMergeTabToGroup(mTab6, mTab1.getId());
+        assertArrayEquals(mTabGroupModelFilter.getRelatedTabList(mTab5.getId()).toArray(),
+                expectedGroup.toArray());
+        assertArrayEquals(mTabs.toArray(), expectedTabModel.toArray());
+    }
+
+    @Test
+    public void mergeGroupToGroup_Forward() {
+        List<Tab> expectedGroup = new ArrayList<>(Arrays.asList(mTab2, mTab3, mTab5, mTab6));
+        List<Tab> expectedTabModel =
+                new ArrayList<>(Arrays.asList(mTab1, mTab2, mTab3, mTab5, mTab6, mTab4));
+        int startIndex = POSITION3;
+
+        mTabGroupModelFilter.mergeTabsToGroup(mTab5.getId(), mTab2.getId());
+
+        verify(mTabModel).moveTab(mTab5.getId(), ++startIndex);
+        verify(mTabModel).moveTab(mTab6.getId(), ++startIndex);
+        verify(mTabGroupModelFilterObserver).didMergeTabToGroup(mTab6, mTab2.getId());
+        assertArrayEquals(mTabGroupModelFilter.getRelatedTabList(mTab5.getId()).toArray(),
+                expectedGroup.toArray());
+        assertArrayEquals(mTabs.toArray(), expectedTabModel.toArray());
+    }
+
+    @Test
+    public void mergeOneTabToTab_Backward() {
+        List<Tab> expectedGroup = new ArrayList<>(Arrays.asList(mTab4, mTab1));
+        List<Tab> expectedTabModel =
+                new ArrayList<>(Arrays.asList(mTab2, mTab3, mTab4, mTab1, mTab5, mTab6));
+        int startIndex = POSITION4;
+
+        mTabGroupModelFilter.mergeTabsToGroup(mTab1.getId(), mTab4.getId());
+
+        verify(mTabModel).moveTab(mTab1.getId(), startIndex + 1);
+        verify(mTabGroupModelFilterObserver).didMergeTabToGroup(mTab1, mTab4.getId());
+        assertArrayEquals(mTabGroupModelFilter.getRelatedTabList(mTab1.getId()).toArray(),
+                expectedGroup.toArray());
+        assertArrayEquals(mTabs.toArray(), expectedTabModel.toArray());
+    }
+
+    @Test
+    public void mergeGroupToTab_Backward() {
+        List<Tab> expectedGroup = new ArrayList<>(Arrays.asList(mTab4, mTab2, mTab3));
+        List<Tab> expectedTabModel =
+                new ArrayList<>(Arrays.asList(mTab1, mTab4, mTab2, mTab3, mTab5, mTab6));
+        int startIndex = POSITION4;
+
+        mTabGroupModelFilter.mergeTabsToGroup(mTab2.getId(), mTab4.getId());
+
+        verify(mTabModel).moveTab(mTab2.getId(), startIndex + 1);
+        verify(mTabModel).moveTab(mTab3.getId(), startIndex + 1);
+        verify(mTabGroupModelFilterObserver).didMergeTabToGroup(mTab3, mTab4.getId());
+        assertArrayEquals(mTabGroupModelFilter.getRelatedTabList(mTab2.getId()).toArray(),
+                expectedGroup.toArray());
+        assertArrayEquals(mTabs.toArray(), expectedTabModel.toArray());
+    }
+
+    @Test
+    public void moveGroup_Backward() {
+        List<Tab> expectedTabModel =
+                new ArrayList<>(Arrays.asList(mTab1, mTab4, mTab2, mTab3, mTab5, mTab6));
+        int startIndex = POSITION4;
+
+        mTabGroupModelFilter.moveRelatedTabs(mTab2.getId(), startIndex + 1);
+
+        verify(mTabModel).moveTab(mTab2.getId(), startIndex + 1);
+        verify(mTabModel).moveTab(mTab3.getId(), startIndex + 1);
+        verify(mTabGroupModelFilterObserver).didMoveTabGroup(mTab3, POSITION3 - 1, startIndex);
+        assertArrayEquals(mTabs.toArray(), expectedTabModel.toArray());
+    }
+
+    @Test
+    public void moveGroup_Forward() {
+        List<Tab> expectedTabModel =
+                new ArrayList<>(Arrays.asList(mTab1, mTab2, mTab3, mTab5, mTab6, mTab4));
+        int startIndex = POSITION3;
+
+        mTabGroupModelFilter.moveRelatedTabs(mTab5.getId(), startIndex + 1);
+
+        verify(mTabModel).moveTab(mTab5.getId(), startIndex + 1);
+        verify(mTabModel).moveTab(mTab6.getId(), startIndex + 2);
+        verify(mTabGroupModelFilterObserver).didMoveTabGroup(mTab6, POSITION6, startIndex + 2);
+        assertArrayEquals(mTabs.toArray(), expectedTabModel.toArray());
+    }
+}
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index a871bf2a..b8f6bc1 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-76.0.3809.0_rc-r1-merged.afdo.bz2
\ No newline at end of file
+chromeos-chrome-amd64-77.0.3812.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/OpenLastTabMediator.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/OpenLastTabMediator.java
index 1cb7c16..c079824 100644
--- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/OpenLastTabMediator.java
+++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/OpenLastTabMediator.java
@@ -17,6 +17,7 @@
 import org.chromium.chrome.browser.history.BrowsingHistoryBridge;
 import org.chromium.chrome.browser.history.HistoryItem;
 import org.chromium.chrome.browser.history.HistoryProvider;
+import org.chromium.chrome.browser.native_page.ContextMenuManager;
 import org.chromium.chrome.browser.native_page.NativePageHost;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
@@ -137,12 +138,12 @@
         boolean willReturnIcon = mIconBridge.getLargeIconForUrl(item.getUrl(),
                 mContext.getResources().getDimensionPixelSize(R.dimen.open_last_tab_icon_size),
                 (icon, fallbackColor, isFallbackColorDefault, iconType) -> {
-                    setAndShowButton(item.getUrl(), icon, fallbackColor);
+                    setAndShowButton(item.getUrl(), icon, fallbackColor, title);
                 });
 
         // False if icon bridge won't call us back.
         if (!willReturnIcon) {
-            setAndShowButton(item.getUrl(), null, R.color.default_icon_color);
+            setAndShowButton(item.getUrl(), null, R.color.default_icon_color, title);
         }
         mModel.set(OpenLastTabProperties.OPEN_LAST_TAB_LOAD_SUCCESS, true);
     }
@@ -153,7 +154,7 @@
     @Override
     public void hasOtherFormsOfBrowsingData(boolean hasOtherForms) {}
 
-    private void setAndShowButton(String url, Bitmap icon, int fallbackColor) {
+    private void setAndShowButton(String url, Bitmap icon, int fallbackColor, String title) {
         mModel.set(OpenLastTabProperties.OPEN_LAST_TAB_ON_CLICK_LISTENER, view -> {
             mNativePageHost.loadUrl(new LoadUrlParams(url, PageTransition.AUTO_BOOKMARK),
                     /* Explore page is never off the record. */ false);
@@ -164,5 +165,36 @@
             icon = mIconGenerator.generateIconForUrl(url);
         }
         mModel.set(OpenLastTabProperties.OPEN_LAST_TAB_FAVICON, icon);
+
+        final Bitmap shortcutIcon = icon;
+        TouchlessContextMenuManager.Delegate delegate =
+                new TouchlessContextMenuManager.EmptyDelegate() {
+                    @Override
+                    public boolean isItemSupported(
+                            @ContextMenuManager.ContextMenuItemId int menuItemId) {
+                        return menuItemId == ContextMenuManager.ContextMenuItemId.ADD_TO_MY_APPS;
+                    }
+
+                    @Override
+                    public String getUrl() {
+                        return url;
+                    }
+
+                    @Override
+                    public String getContextMenuTitle() {
+                        return title;
+                    }
+
+                    @Override
+                    public String getTitle() {
+                        return title;
+                    }
+
+                    @Override
+                    public Bitmap getIconBitmap() {
+                        return shortcutIcon;
+                    }
+                };
+        mModel.set(OpenLastTabProperties.CONTEXT_MENU_DELEGATE, delegate);
     }
 }
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/OpenLastTabProperties.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/OpenLastTabProperties.java
index 6c2ee7e..3cde4fe 100644
--- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/OpenLastTabProperties.java
+++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/OpenLastTabProperties.java
@@ -8,6 +8,7 @@
 import android.view.View;
 
 import org.chromium.base.Callback;
+import org.chromium.chrome.browser.native_page.ContextMenuManager;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
 
@@ -41,9 +42,13 @@
             .ReadableObjectPropertyKey<Callback<View>> ASYNC_FOCUS_DELEGATE =
             new PropertyModel.ReadableObjectPropertyKey<>();
 
+    public static final PropertyModel
+            .WritableObjectPropertyKey<ContextMenuManager.Delegate> CONTEXT_MENU_DELEGATE =
+            new PropertyModel.WritableObjectPropertyKey<>();
+
     // Property keys for the open last tab button.
     public static final PropertyKey[] ALL_KEYS = {OPEN_LAST_TAB_ON_CLICK_LISTENER,
             OPEN_LAST_TAB_FAVICON, OPEN_LAST_TAB_TITLE, OPEN_LAST_TAB_TIMESTAMP,
             OPEN_LAST_TAB_LOAD_SUCCESS, OPEN_LAST_TAB_FIRST_LAUNCH, ON_FOCUS_CALLBACK,
-            SHOULD_FOCUS_VIEW, ASYNC_FOCUS_DELEGATE};
+            SHOULD_FOCUS_VIEW, ASYNC_FOCUS_DELEGATE, CONTEXT_MENU_DELEGATE};
 }
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/OpenLastTabView.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/OpenLastTabView.java
index e50e07d7..0a404821 100644
--- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/OpenLastTabView.java
+++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/OpenLastTabView.java
@@ -16,6 +16,7 @@
 import android.widget.TextView;
 
 import org.chromium.base.Callback;
+import org.chromium.chrome.browser.native_page.ContextMenuManager;
 import org.chromium.chrome.touchless.R;
 
 /**
@@ -102,4 +103,8 @@
     void setAsyncFocusDelegate(Callback<View> asyncFocusDelegate) {
         mAsyncFocusDelegate = asyncFocusDelegate;
     }
+
+    void setContextMenuDelegate(ContextMenuManager.Delegate delegate) {
+        ContextMenuManager.registerViewForTouchlessContextMenu(mLastTabView, delegate);
+    }
 }
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/OpenLastTabViewBinder.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/OpenLastTabViewBinder.java
index 76de5934..a448710 100644
--- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/OpenLastTabViewBinder.java
+++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/OpenLastTabViewBinder.java
@@ -32,6 +32,8 @@
             model.set(OpenLastTabProperties.SHOULD_FOCUS_VIEW, false);
         } else if (propertyKey == OpenLastTabProperties.ASYNC_FOCUS_DELEGATE) {
             view.setAsyncFocusDelegate(model.get(OpenLastTabProperties.ASYNC_FOCUS_DELEGATE));
+        } else if (propertyKey == OpenLastTabProperties.CONTEXT_MENU_DELEGATE) {
+            view.setContextMenuDelegate(model.get(OpenLastTabProperties.CONTEXT_MENU_DELEGATE));
         } else {
             assert false : "Unhandled property detected.";
         }
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessContextMenuManager.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessContextMenuManager.java
index 3230c33..cbbe04a 100644
--- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessContextMenuManager.java
+++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessContextMenuManager.java
@@ -43,6 +43,22 @@
         Bitmap getIconBitmap();
     }
 
+    /**
+     * Empty implementation of Delegate to allow derived classes to only implement methods they
+     * need.
+     */
+    public static class EmptyDelegate extends ContextMenuManager.EmptyDelegate implements Delegate {
+        @Override
+        public String getTitle() {
+            return null;
+        }
+
+        @Override
+        public Bitmap getIconBitmap() {
+            return null;
+        }
+    }
+
     private final Activity mActivity;
     private final ModalDialogManager mDialogManager;
     private PropertyModel mTouchlessMenuModel;
diff --git a/chrome/app_shim/app_shim_controller.mm b/chrome/app_shim/app_shim_controller.mm
index 14b4d209..5d11a28 100644
--- a/chrome/app_shim/app_shim_controller.mm
+++ b/chrome/app_shim/app_shim_controller.mm
@@ -307,8 +307,8 @@
     remote_cocoa::mojom::ApplicationAssociatedRequest request) {
   remote_cocoa::ApplicationBridge::Get()->BindRequest(std::move(request));
   remote_cocoa::ApplicationBridge::Get()->SetContentNSViewCreateCallbacks(
-      base::BindRepeating(content::CreateRenderWidgetHostNSView),
-      base::BindRepeating(content::CreateWebContentsNSView));
+      base::BindRepeating(remote_cocoa::CreateRenderWidgetHostNSView),
+      base::BindRepeating(remote_cocoa::CreateWebContentsNSView));
 }
 
 void AppShimController::CreateCommandDispatcherForWidget(uint64_t widget_id) {
diff --git a/chrome/browser/android/explore_sites/explore_sites_bridge.cc b/chrome/browser/android/explore_sites/explore_sites_bridge.cc
index 4ddaf1a..6131b04f 100644
--- a/chrome/browser/android/explore_sites/explore_sites_bridge.cc
+++ b/chrome/browser/android/explore_sites/explore_sites_bridge.cc
@@ -115,6 +115,12 @@
 }
 
 // static
+jint JNI_ExploreSitesBridge_GetIconVariation(JNIEnv* env) {
+  return static_cast<jint>(
+      chrome::android::explore_sites::GetMostLikelyVariation());
+}
+
+// static
 void JNI_ExploreSitesBridge_GetIcon(
     JNIEnv* env,
     const JavaParamRef<jobject>& j_profile,
diff --git a/chrome/browser/android/explore_sites/explore_sites_feature.h b/chrome/browser/android/explore_sites/explore_sites_feature.h
index 03e459f..48126d3 100644
--- a/chrome/browser/android/explore_sites/explore_sites_feature.h
+++ b/chrome/browser/android/explore_sites/explore_sites_feature.h
@@ -32,6 +32,7 @@
 };
 
 // A Java counterpart will be generated for this enum.
+// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.explore_sites
 enum class MostLikelyVariation { NONE, ICON_ARROW, ICON_DOTS, ICON_GROUPED };
 
 ExploreSitesVariation GetExploreSitesVariation();
diff --git a/chrome/browser/bad_message.h b/chrome/browser/bad_message.h
index ae0b395..96bed3f8 100644
--- a/chrome/browser/bad_message.h
+++ b/chrome/browser/bad_message.h
@@ -22,7 +22,8 @@
 // values in histograms.
 enum BadMessageReason {
   WRLHH_LOGGING_STOPPED_BAD_STATE = 0,
-  PPH_EXTRA_PREVIEW_MESSAGE,
+  PPH_EXTRA_PREVIEW_MESSAGE = 1,
+  PMF_INVALID_INITIATOR_ORIGIN = 2,
 
   // Please add new elements here. The naming convention is abbreviated class
   // name (e.g. RenderFrameHost becomes RFH) plus a unique description of the
diff --git a/chrome/browser/chrome_content_browser_client_browsertest.cc b/chrome/browser/chrome_content_browser_client_browsertest.cc
index c20e0df..570d735 100644
--- a/chrome/browser/chrome_content_browser_client_browsertest.cc
+++ b/chrome/browser/chrome_content_browser_client_browsertest.cc
@@ -676,4 +676,37 @@
 
 INSTANTIATE_TEST_SUITE_P(All, PrefersColorSchemeTest, testing::Bool());
 
+#if defined(OS_CHROMEOS)
+class ProtocolHandlerTest : public InProcessBrowserTest {
+ public:
+  ProtocolHandlerTest() = default;
+
+  void SetUpOnMainThread() override {
+    InProcessBrowserTest::SetUpOnMainThread();
+    host_resolver()->AddRule("*", "127.0.0.1");
+    ASSERT_TRUE(embedded_test_server()->Start());
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ProtocolHandlerTest);
+};
+
+// Tests that if a protocol handler is registered for a scheme, an external
+// program (another Chrome tab in this case) is not launched to handle the
+// navigation.
+IN_PROC_BROWSER_TEST_F(ProtocolHandlerTest, ExternalProgramNotLaunched) {
+  ui_test_utils::NavigateToURL(browser(), GURL("mailto:bob@example.com"));
+
+  // If an external program (Chrome) was launched, it will result in a second
+  // tab being opened.
+  EXPECT_EQ(1, browser()->tab_strip_model()->count());
+
+  // Make sure the protocol handler redirected the navigation.
+  base::string16 expected_title = base::ASCIIToUTF16("mail.google.com");
+  content::TitleWatcher title_watcher(
+      browser()->tab_strip_model()->GetActiveWebContents(), expected_title);
+  EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
+}
+#endif
+
 }  // namespace content
diff --git a/chrome/browser/chromeos/arc/tracing/arc_tracing_bridge.cc b/chrome/browser/chromeos/arc/tracing/arc_tracing_bridge.cc
index 65e0ae2..0268f68 100644
--- a/chrome/browser/chromeos/arc/tracing/arc_tracing_bridge.cc
+++ b/chrome/browser/chromeos/arc/tracing/arc_tracing_bridge.cc
@@ -28,7 +28,7 @@
 #include "services/tracing/public/cpp/perfetto/perfetto_traced_process.h"
 #include "services/tracing/public/mojom/constants.mojom.h"
 #include "services/tracing/public/mojom/perfetto_service.mojom.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/trace_writer.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_writer.h"
 #include "third_party/perfetto/protos/perfetto/trace/chrome/chrome_trace_event.pbzero.h"
 #include "third_party/perfetto/protos/perfetto/trace/trace_packet.pbzero.h"
 
diff --git a/chrome/browser/notifications/scheduler/impression_history_tracker.cc b/chrome/browser/notifications/scheduler/impression_history_tracker.cc
index 6defe73c..def4dc4 100644
--- a/chrome/browser/notifications/scheduler/impression_history_tracker.cc
+++ b/chrome/browser/notifications/scheduler/impression_history_tracker.cc
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/numerics/ranges.h"
 
 namespace notifications {
@@ -17,17 +18,35 @@
   return lhs.create_time < rhs.create_time;
 }
 
+std::string ToDatabaseKey(SchedulerClientType type) {
+  switch (type) {
+    case SchedulerClientType::kTest1:
+      return "Test1";
+    case SchedulerClientType::kTest2:
+      return "Test2";
+    case SchedulerClientType::kTest3:
+      return "Test3";
+    case SchedulerClientType::kUnknown:
+      NOTREACHED();
+      return std::string();
+  }
+}
+
 ImpressionHistoryTrackerImpl::ImpressionHistoryTrackerImpl(
     const SchedulerConfig& config,
     std::unique_ptr<CollectionStore<ClientState>> store)
     : store_(std::move(store)),
       config_(config),
       initialized_(false),
+      delegate_(nullptr),
       weak_ptr_factory_(this) {}
 
 ImpressionHistoryTrackerImpl::~ImpressionHistoryTrackerImpl() = default;
 
-void ImpressionHistoryTrackerImpl::Init(InitCallback callback) {
+void ImpressionHistoryTrackerImpl::Init(Delegate* delegate,
+                                        InitCallback callback) {
+  DCHECK(!delegate_ && delegate);
+  delegate_ = delegate;
   store_->InitAndLoad(
       base::BindOnce(&ImpressionHistoryTrackerImpl::OnStoreInitialized,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
@@ -36,11 +55,32 @@
 void ImpressionHistoryTrackerImpl::AnalyzeImpressionHistory() {
   for (auto& client_state : client_states_)
     AnalyzeImpressionHistory(client_state.second.get());
+  if (MaybeUpdateAllDb())
+    NotifyImpressionUpdate();
 }
 
-const ImpressionHistoryTracker::ClientStates&
-ImpressionHistoryTrackerImpl::GetClientStates() const {
-  return client_states_;
+void ImpressionHistoryTrackerImpl::GetClientStates(
+    std::map<SchedulerClientType, const ClientState*>* client_states) const {
+  DCHECK(client_states);
+  client_states->clear();
+  for (const auto& pair : client_states_) {
+    client_states->emplace(pair.first, pair.second.get());
+  }
+}
+
+void ImpressionHistoryTrackerImpl::OnClick(const std::string& notification_id) {
+  OnClickInternal(notification_id, true /*update_db*/);
+}
+
+void ImpressionHistoryTrackerImpl::OnActionClick(
+    const std::string& notification_id,
+    ActionButtonType button_type) {
+  OnButtonClickInternal(notification_id, button_type, true /*update_db*/);
+}
+
+void ImpressionHistoryTrackerImpl::OnDismiss(
+    const std::string& notification_id) {
+  OnDismissInternal(notification_id, true /*update_db*/);
 }
 
 void ImpressionHistoryTrackerImpl::OnStoreInitialized(
@@ -55,11 +95,16 @@
   initialized_ = true;
 
   // Load the data to memory, and sort the impression list.
+  // TODO(xingliu): Persist ClientState for new registered client and remove
+  // deprecated client. https://crbug.com/968606.
   for (auto it = entries.begin(); it != entries.end(); ++it) {
     auto& entry = (*it);
     auto type = entry->type;
     std::sort(entry->impressions.begin(), entry->impressions.end(),
               &CreateTimeCompare);
+    for (auto& impression : entry->impressions) {
+      impression_map_.emplace(impression.guid, &impression);
+    }
     client_states_.emplace(type, std::move(*it));
   }
 
@@ -78,33 +123,36 @@
 
     // Prune out expired impression.
     if (now - impression->create_time > config_.impression_expiration) {
+      impression_map_.erase(impression->guid);
       client_state->impressions.erase(it++);
+      SetNeedsUpdate(client_state->type, true);
       continue;
     } else {
       ++it;
     }
 
-    // TODO(xingliu): Use a delegate to get ImpressionResult from notification
-    // scheduler API consumer.
+    // TODO(xingliu): Use scheduling params to determine ImpressionResult.
+    // https://crbug.com/968880
     switch (impression->feedback) {
-      case UserFeedback::kNotHelpful:
-        // One unhelpful clicks results in suppression.
-        ApplyNegativeImpression(client_state, impression);
-        break;
       case UserFeedback::kDismiss:
         dismisses.emplace_back(impression);
-        PruneImpression(&dismisses,
-                        impression->create_time - config_.dismiss_duration);
+        PruneImpressionByCreateTime(
+            &dismisses, impression->create_time - config_.dismiss_duration);
 
         // Three consecutive dismisses will result in suppression.
         ApplyNegativeImpressions(client_state, &dismisses,
                                  config_.dismiss_count);
         break;
       case UserFeedback::kClick:
+        OnClickInternal(impression->guid, false /*update_db*/);
+        break;
       case UserFeedback::kHelpful:
-        // Any body click or helpful button click will increase maximum
-        // notification deliver.
-        ApplyPositiveImpression(client_state, impression);
+        OnButtonClickInternal(impression->guid, ActionButtonType::kHelpful,
+                              false /*update_db*/);
+        break;
+      case UserFeedback::kNotHelpful:
+        OnButtonClickInternal(impression->guid, ActionButtonType::kUnhelpful,
+                              false /*update_db*/);
         break;
       case UserFeedback::kIgnore:
         break;
@@ -117,12 +165,12 @@
     }
   }
 
-  // Perform suppression recovery.
-  SuppressionRecovery(client_state);
+  // Check suppression expiration.
+  CheckSuppressionExpiration(client_state);
 }
 
 // static
-void ImpressionHistoryTrackerImpl::PruneImpression(
+void ImpressionHistoryTrackerImpl::PruneImpressionByCreateTime(
     std::deque<Impression*>* impressions,
     const base::Time& start_time) {
   DCHECK(impressions);
@@ -143,9 +191,18 @@
   if (impression->integrated)
     return;
 
+  SetNeedsUpdate(client_state->type, true);
   impression->integrated = true;
   impression->impression = ImpressionResult::kPositive;
 
+  // A positive impression directly releases the suppression.
+  if (client_state->suppression_info.has_value()) {
+    client_state->current_max_daily_show =
+        client_state->suppression_info->recover_goal;
+    client_state->suppression_info.reset();
+    return;
+  }
+
   // Increase |current_max_daily_show| by 1.
   client_state->current_max_daily_show =
       base::ClampToRange(++client_state->current_max_daily_show, 0,
@@ -180,6 +237,7 @@
   if (impression->integrated)
     return;
 
+  SetNeedsUpdate(client_state->type, true);
   impression->integrated = true;
   impression->impression = ImpressionResult::kNegative;
 
@@ -191,8 +249,7 @@
   client_state->current_max_daily_show = 0;
 }
 
-// Recovers from suppression caused by negative impressions.
-void ImpressionHistoryTrackerImpl::SuppressionRecovery(
+void ImpressionHistoryTrackerImpl::CheckSuppressionExpiration(
     ClientState* client_state) {
   // No suppression to recover from.
   if (!client_state->suppression_info.has_value())
@@ -211,6 +268,125 @@
 
   // Clear suppression if fully recovered.
   client_state->suppression_info.reset();
+  SetNeedsUpdate(client_state->type, true);
+}
+
+bool ImpressionHistoryTrackerImpl::MaybeUpdateDb(SchedulerClientType type) {
+  auto it = client_states_.find(type);
+  if (it == client_states_.end())
+    return false;
+
+  bool db_updated = false;
+  if (NeedsUpdate(type)) {
+    store_->Update(ToDatabaseKey(type), *(it->second.get()), base::DoNothing());
+    db_updated = true;
+  }
+  SetNeedsUpdate(type, false);
+  return db_updated;
+}
+
+bool ImpressionHistoryTrackerImpl::MaybeUpdateAllDb() {
+  bool db_updated = false;
+  for (const auto& client_state : client_states_) {
+    auto type = client_state.second->type;
+    db_updated |= MaybeUpdateDb(type);
+  }
+
+  return db_updated;
+}
+
+void ImpressionHistoryTrackerImpl::SetNeedsUpdate(SchedulerClientType type,
+                                                  bool needs_update) {
+  need_update_db_[type] = needs_update;
+}
+
+bool ImpressionHistoryTrackerImpl::NeedsUpdate(SchedulerClientType type) const {
+  auto it = need_update_db_.find(type);
+  return it == need_update_db_.end() ? false : it->second;
+}
+
+void ImpressionHistoryTrackerImpl::NotifyImpressionUpdate() {
+  if (delegate_)
+    delegate_->OnImpressionUpdated();
+}
+
+Impression* ImpressionHistoryTrackerImpl::FindImpressionNeedsUpdate(
+    const std::string& notification_guid) {
+  auto it = impression_map_.find(notification_guid);
+  if (it == impression_map_.end())
+    return nullptr;
+  auto* impression = it->second;
+
+  if (impression->integrated)
+    return nullptr;
+
+  return it->second;
+}
+
+void ImpressionHistoryTrackerImpl::OnClickInternal(
+    const std::string& notification_guid,
+    bool update_db) {
+  auto* impression = FindImpressionNeedsUpdate(notification_guid);
+  if (!impression)
+    return;
+
+  auto it = client_states_.find(impression->type);
+  if (it == client_states_.end())
+    return;
+  ClientState* client_state = it->second.get();
+  ApplyPositiveImpression(client_state, impression);
+  impression->feedback = UserFeedback::kClick;
+
+  if (update_db && MaybeUpdateDb(client_state->type))
+    NotifyImpressionUpdate();
+}
+
+void ImpressionHistoryTrackerImpl::OnButtonClickInternal(
+    const std::string& notification_guid,
+    ActionButtonType button_type,
+    bool update_db) {
+  auto* impression = FindImpressionNeedsUpdate(notification_guid);
+  if (!impression)
+    return;
+  auto it = client_states_.find(impression->type);
+  if (it == client_states_.end())
+    return;
+
+  ClientState* client_state = it->second.get();
+  switch (button_type) {
+    case ActionButtonType::kHelpful:
+      ApplyPositiveImpression(client_state, impression);
+      impression->feedback = UserFeedback::kHelpful;
+      break;
+    case ActionButtonType::kUnhelpful:
+      ApplyNegativeImpression(client_state, impression);
+      impression->feedback = UserFeedback::kNotHelpful;
+      break;
+    case ActionButtonType::kUnknownAction:
+      NOTIMPLEMENTED();
+      break;
+  }
+
+  if (update_db && MaybeUpdateDb(client_state->type))
+    NotifyImpressionUpdate();
+}
+
+void ImpressionHistoryTrackerImpl::OnDismissInternal(
+    const std::string& notification_guid,
+    bool update_db) {
+  auto* impression = FindImpressionNeedsUpdate(notification_guid);
+  if (!impression)
+    return;
+
+  auto it = client_states_.find(impression->type);
+  if (it == client_states_.end())
+    return;
+  ClientState* client_state = it->second.get();
+
+  AnalyzeImpressionHistory(client_state);
+
+  if (update_db && MaybeUpdateDb(client_state->type))
+    NotifyImpressionUpdate();
 }
 
 }  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/impression_history_tracker.h b/chrome/browser/notifications/scheduler/impression_history_tracker.h
index 60a1401..e409fce 100644
--- a/chrome/browser/notifications/scheduler/impression_history_tracker.h
+++ b/chrome/browser/notifications/scheduler/impression_history_tracker.h
@@ -8,6 +8,7 @@
 #include <deque>
 #include <map>
 #include <memory>
+#include <string>
 
 #include "base/callback.h"
 #include "base/macros.h"
@@ -16,26 +17,41 @@
 #include "chrome/browser/notifications/scheduler/collection_store.h"
 #include "chrome/browser/notifications/scheduler/impression_types.h"
 #include "chrome/browser/notifications/scheduler/scheduler_config.h"
+#include "chrome/browser/notifications/scheduler/user_action_handler.h"
 
 namespace notifications {
 
 // Provides functionalities to update notification impression history and adjust
 // maximum daily notification shown to the user.
-class ImpressionHistoryTracker {
+class ImpressionHistoryTracker : public UserActionHandler {
  public:
   using ClientStates =
       std::map<SchedulerClientType, std::unique_ptr<ClientState>>;
   using InitCallback = base::OnceCallback<void(bool)>;
 
+  class Delegate {
+   public:
+    Delegate() = default;
+    virtual ~Delegate() = default;
+
+    // Called when the impression data is updated.
+    virtual void OnImpressionUpdated() = 0;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(Delegate);
+  };
+
   // Initializes the impression tracker.
-  virtual void Init(InitCallback callback) = 0;
+  virtual void Init(Delegate* delegate, InitCallback callback) = 0;
 
   // Analyzes the impression history for all notification clients, and adjusts
   // the |current_max_daily_show|.
   virtual void AnalyzeImpressionHistory() = 0;
 
   // Queries the client states.
-  virtual const ClientStates& GetClientStates() const = 0;
+  virtual void GetClientStates(
+      std::map<SchedulerClientType, const ClientState*>* client_states)
+      const = 0;
 
   virtual ~ImpressionHistoryTracker() = default;
 
@@ -56,9 +72,14 @@
 
  private:
   // ImpressionHistoryTracker implementation.
-  void Init(InitCallback callback) override;
+  void Init(Delegate* delegate, InitCallback callback) override;
   void AnalyzeImpressionHistory() override;
-  const ClientStates& GetClientStates() const override;
+  void GetClientStates(std::map<SchedulerClientType, const ClientState*>*
+                           client_states) const override;
+  void OnClick(const std::string& notification_id) override;
+  void OnActionClick(const std::string& notification_id,
+                     ActionButtonType button_type) override;
+  void OnDismiss(const std::string& notification_id) override;
 
   // Called after |store_| is initialized.
   void OnStoreInitialized(InitCallback callback,
@@ -67,8 +88,8 @@
 
   // Helper method to prune impressions created before |start_time|. Assumes
   // |impressions| are sorted by creation time.
-  static void PruneImpression(std::deque<Impression*>* impressions,
-                              const base::Time& start_time);
+  static void PruneImpressionByCreateTime(std::deque<Impression*>* impressions,
+                                          const base::Time& start_time);
 
   // Analyzes the impression history for a particular client.
   void AnalyzeImpressionHistory(ClientState* client_state);
@@ -87,13 +108,38 @@
   void ApplyNegativeImpression(ClientState* client_state,
                                Impression* impression);
 
-  // Recovers from suppression caused by negative impressions.
-  void SuppressionRecovery(ClientState* client_state);
+  // Checks if suppression is expired and recover to a certain daily quota.
+  void CheckSuppressionExpiration(ClientState* client_state);
+
+  // Tries to update the database records for |type|. Returns whether the db is
+  // actually updated.
+  bool MaybeUpdateDb(SchedulerClientType type);
+  bool MaybeUpdateAllDb();
+
+  // Sets/Gets the flag if impression data for |type| needs update in the
+  // database.
+  void SetNeedsUpdate(SchedulerClientType type, bool needs_update);
+  bool NeedsUpdate(SchedulerClientType type) const;
+
+  // Notifies the delegate about impression data update.
+  void NotifyImpressionUpdate();
+
+  // Finds an impression that needs to update based on notification id.
+  Impression* FindImpressionNeedsUpdate(const std::string& notification_guid);
+
+  void OnClickInternal(const std::string& notification_guid, bool update_db);
+  void OnButtonClickInternal(const std::string& notification_guid,
+                             ActionButtonType button_type,
+                             bool update_db);
+  void OnDismissInternal(const std::string& notification_guid, bool update_db);
 
   // Impression history and global states for all notification scheduler
   // clients.
   ClientStates client_states_;
 
+  // Notification guid to Impression map.
+  std::map<std::string, Impression*> impression_map_;
+
   // The storage that persists data.
   std::unique_ptr<CollectionStore<ClientState>> store_;
 
@@ -103,6 +149,11 @@
   // Whether the impression tracker is successfully initialized.
   bool initialized_;
 
+  // If the database needs an update when any of the impression data is updated.
+  std::map<SchedulerClientType, bool> need_update_db_;
+
+  Delegate* delegate_;
+
   base::WeakPtrFactory<ImpressionHistoryTrackerImpl> weak_ptr_factory_;
   DISALLOW_COPY_AND_ASSIGN(ImpressionHistoryTrackerImpl);
 };
diff --git a/chrome/browser/notifications/scheduler/impression_history_tracker_unittest.cc b/chrome/browser/notifications/scheduler/impression_history_tracker_unittest.cc
index fa9c124..0f06d1a 100644
--- a/chrome/browser/notifications/scheduler/impression_history_tracker_unittest.cc
+++ b/chrome/browser/notifications/scheduler/impression_history_tracker_unittest.cc
@@ -21,6 +21,8 @@
 namespace notifications {
 namespace {
 
+const char kGuid1[] = "guid1";
+
 struct TestCase {
   // Input data that will be pushed to the target class.
   std::vector<test::ImpressionTestData> input;
@@ -29,24 +31,25 @@
   std::vector<test::ImpressionTestData> expected;
 };
 
-// Verifies the |output|.
-void VerifyClientStates(
-    const std::vector<test::ImpressionTestData>& expected_test_data,
-    const ImpressionHistoryTracker::ClientStates& output) {
-  ImpressionHistoryTracker::ClientStates expected_client_states;
-  test::AddImpressionTestData(expected_test_data, &expected_client_states);
+Impression CreateImpression(const base::Time& create_time,
+                            const std::string& guid) {
+  return {create_time,
+          UserFeedback::kNoFeedback,
+          ImpressionResult::kInvalid,
+          false /* integrated */,
+          SchedulerTaskTime::kMorning,
+          guid,
+          SchedulerClientType::kTest1};
+}
 
-  DCHECK_EQ(expected_client_states.size(), output.size());
-  for (const auto& expected : expected_client_states) {
-    auto output_it = output.find(expected.first);
-    DCHECK(output_it != output.end());
-    EXPECT_EQ(*expected.second, *output_it->second)
-        << "Unmatch client states: \n"
-        << "Expected: \n"
-        << expected.second->DebugPrint() << " \n"
-        << "Acutual: \n"
-        << output_it->second->DebugPrint();
-  }
+TestCase CreateDefaultTestCase() {
+  TestCase test_case;
+  test_case.input = {{SchedulerClientType::kTest1,
+                      2 /* current_max_daily_show */,
+                      {},
+                      base::nullopt /* suppression_info */}};
+  test_case.expected = test_case.input;
+  return test_case;
 }
 
 class MockImpressionStore : public CollectionStore<ClientState> {
@@ -69,10 +72,20 @@
   DISALLOW_COPY_AND_ASSIGN(MockImpressionStore);
 };
 
-// TODO(xingliu): Add more test cases following the test doc.
-class ImpressionHistoryTrackerTest : public testing::Test {
+class MockDelegate : public ImpressionHistoryTracker::Delegate {
  public:
-  ImpressionHistoryTrackerTest() : store_(nullptr) {}
+  MockDelegate() = default;
+  ~MockDelegate() = default;
+  MOCK_METHOD0(OnImpressionUpdated, void());
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockDelegate);
+};
+
+// TODO(xingliu): Add more test cases following the test doc.
+class ImpressionHistoryTrackerTest : public ::testing::Test {
+ public:
+  ImpressionHistoryTrackerTest() : store_(nullptr), delegate_(nullptr) {}
   ~ImpressionHistoryTrackerTest() override = default;
 
   void SetUp() override {
@@ -81,24 +94,14 @@
   }
 
  protected:
-  void RunTestCase(TestCase test_case) {
-    // Prepare test input data.
-    StoreEntries input_states;
-    test::AddImpressionTestData(test_case.input, &input_states);
-
-    // Do stuff.
-    InitTrackerWithData(std::move(input_states));
-    impression_trakcer_->AnalyzeImpressionHistory();
-
-    // Verify output data.
-    VerifyClientStates(test_case.expected,
-                       impression_trakcer_->GetClientStates());
-  }
-
   // Creates the tracker and push in data.
-  void InitTrackerWithData(StoreEntries entries) {
+  void InitTrackerWithData(const TestCase& test_case) {
+    StoreEntries entries;
+    test::AddImpressionTestData(test_case.input, &entries);
+
     auto store = std::make_unique<MockImpressionStore>();
     store_ = store.get();
+    delegate_ = std::make_unique<MockDelegate>();
     impression_trakcer_ = std::make_unique<ImpressionHistoryTrackerImpl>(
         config_, std::move(store));
 
@@ -109,22 +112,48 @@
               std::move(cb).Run(true, std::move(entries));
             }));
     base::RunLoop loop;
-    impression_trakcer_->Init(base::BindOnce(
-        [](base::RepeatingClosure closure, bool success) {
-          EXPECT_TRUE(success);
-          std::move(closure).Run();
-        },
-        loop.QuitClosure()));
+    impression_trakcer_->Init(
+        delegate_.get(), base::BindOnce(
+                             [](base::RepeatingClosure closure, bool success) {
+                               EXPECT_TRUE(success);
+                               std::move(closure).Run();
+                             },
+                             loop.QuitClosure()));
     loop.Run();
   }
 
+  // Verifies the |expected_test_data| matches the internal states.
+  void VerifyClientStates(const TestCase& test_case) {
+    std::map<SchedulerClientType, const ClientState*> client_states;
+    impression_trakcer_->GetClientStates(&client_states);
+
+    ImpressionHistoryTracker::ClientStates expected_client_states;
+    test::AddImpressionTestData(test_case.expected, &expected_client_states);
+
+    DCHECK_EQ(expected_client_states.size(), client_states.size());
+    for (const auto& expected : expected_client_states) {
+      auto output_it = client_states.find(expected.first);
+      DCHECK(output_it != client_states.end());
+      EXPECT_EQ(*expected.second, *output_it->second)
+          << "Unmatch client states: \n"
+          << "Expected: \n"
+          << expected.second->DebugPrint() << " \n"
+          << "Acutual: \n"
+          << output_it->second->DebugPrint();
+    }
+  }
+
   const SchedulerConfig& config() const { return config_; }
+  MockImpressionStore* store() { return store_; }
+  MockDelegate* delegate() { return delegate_.get(); }
+  ImpressionHistoryTracker* tracker() { return impression_trakcer_.get(); }
 
  private:
   base::test::ScopedTaskEnvironment scoped_task_environment_;
   SchedulerConfig config_;
   std::unique_ptr<ImpressionHistoryTracker> impression_trakcer_;
   MockImpressionStore* store_;
+  std::unique_ptr<MockDelegate> delegate_;
 
   DISALLOW_COPY_AND_ASSIGN(ImpressionHistoryTrackerTest);
 };
@@ -136,10 +165,17 @@
                              config().impression_expiration;
   auto not_expired_time = base::Time::Now() + base::TimeDelta::FromDays(1) -
                           config().impression_expiration;
-  Impression expired{expired_create_time, UserFeedback::kNoFeedback,
-                     ImpressionResult::kInvalid, false /* integrated */};
-  Impression not_expired{not_expired_time, UserFeedback::kNoFeedback,
-                         ImpressionResult::kInvalid, false /* integrated */};
+  Impression expired{expired_create_time,         UserFeedback::kNoFeedback,
+                     ImpressionResult::kInvalid,  false /* integrated */,
+                     SchedulerTaskTime::kMorning, "guid1",
+                     SchedulerClientType::kTest1};
+  Impression not_expired{not_expired_time,
+                         UserFeedback::kNoFeedback,
+                         ImpressionResult::kInvalid,
+                         false /* integrated */,
+                         SchedulerTaskTime::kMorning,
+                         "guid2",
+                         SchedulerClientType::kTest1};
 
   // The impressions in the input should be sorted by creation time when gets
   // loaded to memory.
@@ -156,30 +192,83 @@
                          {not_expired},
                          base::nullopt /* suppression_info */}};
 
-  RunTestCase(std::move(test_case));
+  InitTrackerWithData(test_case);
+  EXPECT_CALL(*store(), Update(_, _, _));
+  EXPECT_CALL(*delegate(), OnImpressionUpdated());
+  tracker()->AnalyzeImpressionHistory();
+  VerifyClientStates(test_case);
 }
 
-// Verifies positive impression will increase the daily max.
-TEST_F(ImpressionHistoryTrackerTest, PositiveImpression) {
-  TestCase test_case;
-  base::Time create_time = base::Time::Now() - base::TimeDelta::FromSeconds(1);
-
-  test_case.input = {{SchedulerClientType::kTest1,
-                      2 /* current_max_daily_show */,
-                      {{create_time, UserFeedback::kHelpful,
-                        ImpressionResult::kInvalid, false /* integrated */}},
-                      base::nullopt /* suppression_info */}};
-
-  // Positive impression should bump |the current_max_daily_show| and update
-  // other data.
-  test_case.expected = {{SchedulerClientType::kTest1,
-                         3 /* current_max_daily_show */,
-                         {{create_time, UserFeedback::kHelpful,
-                           ImpressionResult::kPositive, true /* integrated */}},
-                         base::nullopt /* suppression_info */}};
-
-  RunTestCase(std::move(test_case));
+// If impression has been deleted, click should have no result.
+TEST_F(ImpressionHistoryTrackerTest, ClickNoImpression) {
+  TestCase test_case = CreateDefaultTestCase();
+  InitTrackerWithData(test_case);
+  EXPECT_CALL(*store(), Update(_, _, _)).Times(0);
+  EXPECT_CALL(*delegate(), OnImpressionUpdated()).Times(0);
+  tracker()->OnClick(kGuid1);
+  VerifyClientStates(test_case);
 }
 
+struct UserActionTestParam {
+  ImpressionResult impression_result = ImpressionResult::kInvalid;
+  UserFeedback user_feedback = UserFeedback::kNoFeedback;
+  int current_max_daily_show = 0;
+  base::Optional<ActionButtonType> button_type;
+  base::Optional<SuppressionInfo> suppression_info;
+};
+
+class ImpressionHistoryTrackerUserActionTest
+    : public ImpressionHistoryTrackerTest,
+      public ::testing::WithParamInterface<UserActionTestParam> {
+ public:
+  ImpressionHistoryTrackerUserActionTest() = default;
+  ~ImpressionHistoryTrackerUserActionTest() override = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ImpressionHistoryTrackerUserActionTest);
+};
+
+// TODO(xingliu): Add test for unhelpful/dismiss, need to use base::Clock to
+// mock base::Time::Now().
+const UserActionTestParam kUserActionTestParams[] = {
+    {ImpressionResult::kPositive, UserFeedback::kClick, 3, base::nullopt,
+     base::nullopt},
+    {ImpressionResult::kPositive, UserFeedback::kHelpful, 3,
+     ActionButtonType::kHelpful, base::nullopt}};
+
+// User actions like clicks should update the ClientState data accordingly.
+TEST_P(ImpressionHistoryTrackerUserActionTest, UserAction) {
+  TestCase test_case = CreateDefaultTestCase();
+  Impression impression = CreateImpression(base::Time::Now(), kGuid1);
+  DCHECK(!test_case.input.empty());
+  test_case.input.front().impressions.emplace_back(impression);
+
+  impression.impression = GetParam().impression_result;
+  impression.integrated = true;
+  impression.feedback = GetParam().user_feedback;
+
+  test_case.expected.front().current_max_daily_show =
+      GetParam().current_max_daily_show;
+  test_case.expected.front().impressions.emplace_back(impression);
+  test_case.expected.front().suppression_info = GetParam().suppression_info;
+
+  InitTrackerWithData(test_case);
+  EXPECT_CALL(*store(), Update(_, _, _));
+  EXPECT_CALL(*delegate(), OnImpressionUpdated());
+
+  // Trigger user action.
+  if (GetParam().user_feedback == UserFeedback::kClick)
+    tracker()->OnClick(kGuid1);
+  else if (GetParam().button_type.has_value())
+    tracker()->OnActionClick(kGuid1, GetParam().button_type.value());
+
+  VerifyClientStates(test_case);
+}
+
+INSTANTIATE_TEST_SUITE_P(,
+                         ImpressionHistoryTrackerUserActionTest,
+                         testing::ValuesIn(kUserActionTestParams));
+
 }  // namespace
+
 }  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/impression_types.cc b/chrome/browser/notifications/scheduler/impression_types.cc
index 10e2616f..12eeaf5 100644
--- a/chrome/browser/notifications/scheduler/impression_types.cc
+++ b/chrome/browser/notifications/scheduler/impression_types.cc
@@ -14,7 +14,8 @@
 bool Impression::operator==(const Impression& other) const {
   return create_time == other.create_time && feedback == other.feedback &&
          impression == other.impression && integrated == other.integrated &&
-         task_start_time == other.task_start_time && guid == other.guid;
+         task_start_time == other.task_start_time && guid == other.guid &&
+         type == other.type;
 }
 
 SuppressionInfo::SuppressionInfo(const base::Time& last_trigger,
@@ -55,14 +56,27 @@
 
   for (const auto& impression : impressions) {
     std::ostringstream stream;
-    stream << "Impression, create_time:" << impression.create_time << " \n"
-           << "feedback: " << static_cast<int>(impression.feedback) << " \n"
+    stream << "Impression, create_time:" << impression.create_time << "\n"
+           << " create_time in microseconds:"
+           << impression.create_time.ToDeltaSinceWindowsEpoch().InMicroseconds()
+           << "\n"
+           << "feedback: " << static_cast<int>(impression.feedback) << "\n"
            << "impression result: " << static_cast<int>(impression.impression)
            << " \n"
-           << "integrated: " << impression.integrated << " \n"
+           << "integrated: " << impression.integrated << "\n"
            << "task start time: "
            << static_cast<int>(impression.task_start_time) << "\n"
-           << "guid: " << impression.guid << "\n";
+           << "guid: " << impression.guid << "\n"
+           << "type: " << static_cast<int>(impression.type);
+    log += stream.str();
+  }
+
+  if (suppression_info.has_value()) {
+    std::ostringstream stream;
+    stream << "Suppression info, last_trigger_time:"
+           << suppression_info->last_trigger_time << "\n"
+           << "duration:" << suppression_info->duration << "\n"
+           << "recover_goal:" << suppression_info->recover_goal;
     log += stream.str();
   }
 
diff --git a/chrome/browser/notifications/scheduler/impression_types.h b/chrome/browser/notifications/scheduler/impression_types.h
index 607f0dd..2399547 100644
--- a/chrome/browser/notifications/scheduler/impression_types.h
+++ b/chrome/browser/notifications/scheduler/impression_types.h
@@ -48,6 +48,11 @@
 
   // The unique identifier of the notification.
   std::string guid;
+
+  // The type of the notification. Not persisted to disk, set after database
+  // initialized.
+  // TODO(xingliu): Consider to persist this as well.
+  SchedulerClientType type = SchedulerClientType::kUnknown;
 };
 
 // Contains details about supression and recovery after suppression expired.
diff --git a/chrome/browser/notifications/scheduler/notification_scheduler.cc b/chrome/browser/notifications/scheduler/notification_scheduler.cc
index 11f3a07..4c84d8bd 100644
--- a/chrome/browser/notifications/scheduler/notification_scheduler.cc
+++ b/chrome/browser/notifications/scheduler/notification_scheduler.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/notifications/scheduler/notification_scheduler.h"
 
+#include <string>
 #include <utility>
 #include <vector>
 
@@ -22,6 +23,7 @@
 #include "chrome/browser/notifications/scheduler/notification_scheduler_client_registrar.h"
 #include "chrome/browser/notifications/scheduler/notification_scheduler_context.h"
 #include "chrome/browser/notifications/scheduler/scheduled_notification_manager.h"
+#include "chrome/browser/notifications/scheduler/user_action_handler.h"
 
 namespace notifications {
 namespace {
@@ -40,19 +42,22 @@
   // Initializes subsystems in notification scheduler, |callback| will be
   // invoked if all initializations finished or anyone of them failed. The
   // object should be destroyed along with the |callback|.
-  void Init(NotificationSchedulerContext* context,
-            ScheduledNotificationManager::Delegate* delegate,
-            InitCallback callback) {
+  void Init(
+      NotificationSchedulerContext* context,
+      ScheduledNotificationManager::Delegate* notification_manager_delegate,
+      ImpressionHistoryTracker::Delegate* impression_tracker_delegate,
+      InitCallback callback) {
     callback_ = std::move(callback);
     context->icon_store()->Init(base::BindOnce(
         &InitHelper::OnIconStoreInitialized, weak_ptr_factory_.GetWeakPtr()));
     context->impression_tracker()->Init(
+        impression_tracker_delegate,
         base::BindOnce(&InitHelper::OnImpressionTrackerInitialized,
                        weak_ptr_factory_.GetWeakPtr()));
-
     context->notification_manager()->Init(
-        delegate, base::BindOnce(&InitHelper::OnNotificationManagerInitialized,
-                                 weak_ptr_factory_.GetWeakPtr()));
+        notification_manager_delegate,
+        base::BindOnce(&InitHelper::OnNotificationManagerInitialized,
+                       weak_ptr_factory_.GetWeakPtr()));
   }
 
  private:
@@ -98,7 +103,9 @@
 class NotificationSchedulerImpl
     : public NotificationScheduler,
       public NotificationBackgroundTaskScheduler::Handler,
-      public ScheduledNotificationManager::Delegate {
+      public ScheduledNotificationManager::Delegate,
+      public ImpressionHistoryTracker::Delegate,
+      public UserActionHandler {
  public:
   NotificationSchedulerImpl(
       std::unique_ptr<NotificationSchedulerContext> context)
@@ -112,7 +119,7 @@
     auto helper = std::make_unique<InitHelper>();
     auto* helper_ptr = helper.get();
     helper_ptr->Init(
-        context_.get(), this,
+        context_.get(), this, this,
         base::BindOnce(&NotificationSchedulerImpl::OnInitialized,
                        weak_ptr_factory_.GetWeakPtr(), std::move(helper),
                        std::move(init_callback)));
@@ -153,22 +160,23 @@
     NOTIMPLEMENTED();
   }
 
+  // ImpressionHistoryTracker::Delegate implementation.
+  void OnImpressionUpdated() override { ScheduleBackgroundTask(); }
+
   void FindNotificationToShow(SchedulerTaskTime task_start_time) {
     DisplayDecider::Results results;
     ScheduledNotificationManager::Notifications notifications;
     context_->notification_manager()->GetAllNotifications(&notifications);
-    const auto& client_states =
-        context_->impression_tracker()->GetClientStates();
-    DisplayDecider::ClientStates client_state_ptrs;
-    for (const auto& client_state : client_states) {
-      client_state_ptrs.emplace(client_state.first, client_state.second.get());
-    }
+
+    DisplayDecider::ClientStates client_states;
+    context_->impression_tracker()->GetClientStates(&client_states);
+
     std::vector<SchedulerClientType> clients;
     context_->client_registrar()->GetRegisteredClients(&clients);
 
     context_->display_decider()->FindNotificationsToShow(
         context_->config(), std::move(clients), DistributionPolicy::Create(),
-        task_start_time, std::move(notifications), std::move(client_state_ptrs),
+        task_start_time, std::move(notifications), std::move(client_states),
         &results);
 
     // TODO(xingliu): Update impression data after notification shown.
@@ -184,6 +192,19 @@
     NOTIMPLEMENTED();
   }
 
+  void OnClick(const std::string& notification_id) override {
+    context_->impression_tracker()->OnClick(notification_id);
+  }
+
+  void OnActionClick(const std::string& notification_id,
+                     ActionButtonType button_type) override {
+    context_->impression_tracker()->OnActionClick(notification_id, button_type);
+  }
+
+  void OnDismiss(const std::string& notification_id) override {
+    context_->impression_tracker()->OnDismiss(notification_id);
+  }
+
   std::unique_ptr<NotificationSchedulerContext> context_;
 
   base::WeakPtrFactory<NotificationSchedulerImpl> weak_ptr_factory_;
diff --git a/chrome/browser/notifications/scheduler/proto_conversion.cc b/chrome/browser/notifications/scheduler/proto_conversion.cc
index bd6b026..8322989 100644
--- a/chrome/browser/notifications/scheduler/proto_conversion.cc
+++ b/chrome/browser/notifications/scheduler/proto_conversion.cc
@@ -284,6 +284,7 @@
     impression.task_start_time =
         FromSchedulerTaskTime(proto_impression.task_start_time());
     impression.guid = proto_impression.guid();
+    impression.type = client_state->type;
     client_state->impressions.emplace_back(std::move(impression));
   }
 
diff --git a/chrome/browser/notifications/scheduler/proto_conversion_unittest.cc b/chrome/browser/notifications/scheduler/proto_conversion_unittest.cc
index d60c2299..d3b641c 100644
--- a/chrome/browser/notifications/scheduler/proto_conversion_unittest.cc
+++ b/chrome/browser/notifications/scheduler/proto_conversion_unittest.cc
@@ -96,12 +96,17 @@
 // Verifies impression proto conversion.
 TEST(ProtoConversionTest, ImpressionProtoConversion) {
   ClientState client_state;
+  client_state.type = SchedulerClientType::kTest1;
   base::Time create_time;
   bool success = base::Time::FromString("03/25/19 00:00:00 AM", &create_time);
   DCHECK(success);
-  Impression impression{
-      create_time, UserFeedback::kHelpful,      ImpressionResult::kPositive,
-      true,        SchedulerTaskTime::kMorning, kGuid};
+  Impression impression{create_time,
+                        UserFeedback::kHelpful,
+                        ImpressionResult::kPositive,
+                        true /*integrated*/,
+                        SchedulerTaskTime::kMorning,
+                        kGuid,
+                        SchedulerClientType::kTest1};
   client_state.impressions.emplace_back(impression);
   TestClientStateConversion(&client_state);
 
diff --git a/chrome/browser/prerender/prerender_contents.cc b/chrome/browser/prerender/prerender_contents.cc
index 99f676af7..b0ba960 100644
--- a/chrome/browser/prerender/prerender_contents.cc
+++ b/chrome/browser/prerender/prerender_contents.cc
@@ -63,9 +63,10 @@
       Profile* profile,
       const GURL& url,
       const content::Referrer& referrer,
+      const base::Optional<url::Origin>& initiator_origin,
       Origin origin) override {
     return new PrerenderContents(prerender_manager, profile, url, referrer,
-                                 origin);
+                                 initiator_origin, origin);
   }
 };
 
@@ -169,17 +170,20 @@
 
 PrerenderContents::Observer::~Observer() {}
 
-PrerenderContents::PrerenderContents(PrerenderManager* prerender_manager,
-                                     Profile* profile,
-                                     const GURL& url,
-                                     const content::Referrer& referrer,
-                                     Origin origin)
+PrerenderContents::PrerenderContents(
+    PrerenderManager* prerender_manager,
+    Profile* profile,
+    const GURL& url,
+    const content::Referrer& referrer,
+    const base::Optional<url::Origin>& initiator_origin,
+    Origin origin)
     : prerender_mode_(DEPRECATED_FULL_PRERENDER),
       prerendering_has_started_(false),
       prerender_canceler_binding_(this),
       prerender_manager_(prerender_manager),
       prerender_url_(url),
       referrer_(referrer),
+      initiator_origin_(initiator_origin),
       profile_(profile),
       has_finished_loading_(false),
       final_status_(FINAL_STATUS_UNKNOWN),
@@ -190,6 +194,24 @@
       origin_(origin),
       network_bytes_(0),
       weak_factory_(this) {
+  switch (origin) {
+    case ORIGIN_OMNIBOX:
+    case ORIGIN_EXTERNAL_REQUEST:
+    case ORIGIN_EXTERNAL_REQUEST_FORCED_PRERENDER:
+      DCHECK(!initiator_origin_.has_value());
+      break;
+
+    case ORIGIN_GWS_PRERENDER:
+    case ORIGIN_LINK_REL_PRERENDER_SAMEDOMAIN:
+    case ORIGIN_LINK_REL_PRERENDER_CROSSDOMAIN:
+    case ORIGIN_LINK_REL_NEXT:
+      DCHECK(initiator_origin_.has_value());
+      break;
+    case ORIGIN_NONE:
+    case ORIGIN_MAX:
+      NOTREACHED();
+  }
+
   DCHECK(prerender_manager);
   registry_.AddInterface(base::Bind(
       &PrerenderContents::OnPrerenderCancelerRequest, base::Unretained(this)));
@@ -281,6 +303,7 @@
   content::NavigationController::LoadURLParams load_url_params(
       prerender_url_);
   load_url_params.referrer = referrer_;
+  load_url_params.initiator_origin = initiator_origin_;
   load_url_params.transition_type = ui::PAGE_TRANSITION_LINK;
   if (origin_ == ORIGIN_OMNIBOX) {
     load_url_params.transition_type = ui::PageTransitionFromInt(
diff --git a/chrome/browser/prerender/prerender_contents.h b/chrome/browser/prerender/prerender_contents.h
index 9c19d7c..dca0c20 100644
--- a/chrome/browser/prerender/prerender_contents.h
+++ b/chrome/browser/prerender/prerender_contents.h
@@ -15,6 +15,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
+#include "base/optional.h"
 #include "base/time/time.h"
 #include "base/values.h"
 #include "chrome/browser/prerender/prerender_final_status.h"
@@ -28,6 +29,7 @@
 #include "mojo/public/cpp/bindings/binding.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
 #include "ui/gfx/geometry/rect.h"
+#include "url/origin.h"
 
 class Profile;
 
@@ -71,6 +73,7 @@
         Profile* profile,
         const GURL& url,
         const content::Referrer& referrer,
+        const base::Optional<url::Origin>& initiator_origin,
         Origin origin) = 0;
 
    private:
@@ -240,6 +243,7 @@
                     Profile* profile,
                     const GURL& url,
                     const content::Referrer& referrer,
+                    const base::Optional<url::Origin>& initiator_origin,
                     Origin origin);
 
   // Set the final status for how the PrerenderContents was used. This
@@ -311,6 +315,10 @@
   // The referrer.
   content::Referrer referrer_;
 
+  // The origin of the page requesting the prerender. Empty when the prerender
+  // is browser initiated.
+  base::Optional<url::Origin> initiator_origin_;
+
   // The profile being used
   Profile* profile_;
 
diff --git a/chrome/browser/prerender/prerender_link_manager.cc b/chrome/browser/prerender/prerender_link_manager.cc
index 74fcee9..b800643 100644
--- a/chrome/browser/prerender/prerender_link_manager.cc
+++ b/chrome/browser/prerender/prerender_link_manager.cc
@@ -28,6 +28,7 @@
 #include "third_party/blink/public/common/prerender/prerender_rel_type.h"
 #include "ui/gfx/geometry/size.h"
 #include "url/gurl.h"
+#include "url/origin.h"
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "components/guest_view/browser/guest_view_base.h"
@@ -136,6 +137,7 @@
                                           const GURL& url,
                                           uint32_t rel_types,
                                           const content::Referrer& referrer,
+                                          const url::Origin& initiator_origin,
                                           const gfx::Size& size,
                                           int render_view_route_id) {
   DCHECK_EQ(nullptr, FindByLauncherChildIdAndPrerenderId(launcher_child_id,
@@ -164,10 +166,10 @@
     return;
   }
 
-  LinkPrerender
-      prerender(launcher_child_id, prerender_id, url, rel_types, referrer, size,
-                render_view_route_id, manager_->GetCurrentTimeTicks(),
-                prerender_contents);
+  LinkPrerender prerender(launcher_child_id, prerender_id, url, rel_types,
+                          referrer, initiator_origin, size,
+                          render_view_route_id, manager_->GetCurrentTimeTicks(),
+                          prerender_contents);
   prerenders_.push_back(prerender);
   RecordLinkManagerAdded(rel_types);
   if (prerender_contents)
@@ -229,6 +231,7 @@
     const GURL& url,
     uint32_t rel_types,
     const content::Referrer& referrer,
+    const url::Origin& initiator_origin,
     const gfx::Size& size,
     int render_view_route_id,
     TimeTicks creation_time,
@@ -238,6 +241,7 @@
       url(url),
       rel_types(rel_types),
       referrer(referrer),
+      initiator_origin(initiator_origin),
       size(size),
       render_view_route_id(render_view_route_id),
       creation_time(creation_time),
@@ -355,7 +359,7 @@
     std::unique_ptr<PrerenderHandle> handle =
         manager_->AddPrerenderFromLinkRelPrerender(
             it->launcher_child_id, it->render_view_route_id, it->url,
-            it->rel_types, it->referrer, it->size);
+            it->rel_types, it->referrer, it->initiator_origin, it->size);
     if (!handle) {
       // This prerender couldn't be launched, it's gone.
       prerenders_.erase(it);
diff --git a/chrome/browser/prerender/prerender_link_manager.h b/chrome/browser/prerender/prerender_link_manager.h
index 02ea701..a63ac44 100644
--- a/chrome/browser/prerender/prerender_link_manager.h
+++ b/chrome/browser/prerender/prerender_link_manager.h
@@ -17,6 +17,7 @@
 #include "chrome/browser/prerender/prerender_handle.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "url/gurl.h"
+#include "url/origin.h"
 
 namespace content {
 struct Referrer;
@@ -51,6 +52,7 @@
                       const GURL& url,
                       uint32_t rel_types,
                       const content::Referrer& referrer,
+                      const url::Origin& initiator_origin,
                       const gfx::Size& size,
                       int render_view_route_id);
 
@@ -80,6 +82,7 @@
                   const GURL& url,
                   uint32_t rel_types,
                   const content::Referrer& referrer,
+                  const url::Origin& initiator_origin,
                   const gfx::Size& size,
                   int render_view_route_id,
                   base::TimeTicks creation_time,
@@ -93,6 +96,7 @@
     GURL url;
     uint32_t rel_types;
     content::Referrer referrer;
+    url::Origin initiator_origin;
     gfx::Size size;
     int render_view_route_id;
 
diff --git a/chrome/browser/prerender/prerender_manager.cc b/chrome/browser/prerender/prerender_manager.cc
index 4fd535c..00ab1b0 100644
--- a/chrome/browser/prerender/prerender_manager.cc
+++ b/chrome/browser/prerender/prerender_manager.cc
@@ -220,6 +220,7 @@
     const GURL& url,
     const uint32_t rel_types,
     const content::Referrer& referrer,
+    const url::Origin& initiator_origin,
     const gfx::Size& size) {
   Origin origin = rel_types & blink::kPrerenderRelTypePrerender
                       ? ORIGIN_LINK_REL_PRERENDER_CROSSDOMAIN
@@ -244,8 +245,9 @@
         source_web_contents->GetController()
             .GetDefaultSessionStorageNamespace();
   }
-  return AddPrerenderWithPreconnectFallback(
-      origin, url, referrer, gfx::Rect(size), session_storage_namespace);
+  return AddPrerenderWithPreconnectFallback(origin, url, referrer,
+                                            initiator_origin, gfx::Rect(size),
+                                            session_storage_namespace);
 }
 
 std::unique_ptr<PrerenderHandle> PrerenderManager::AddPrerenderFromOmnibox(
@@ -259,7 +261,7 @@
     return nullptr;
   }
   return AddPrerenderWithPreconnectFallback(
-      ORIGIN_OMNIBOX, url, content::Referrer(), gfx::Rect(size),
+      ORIGIN_OMNIBOX, url, content::Referrer(), base::nullopt, gfx::Rect(size),
       session_storage_namespace);
 }
 
@@ -270,7 +272,7 @@
     SessionStorageNamespace* session_storage_namespace,
     const gfx::Rect& bounds) {
   return AddPrerenderWithPreconnectFallback(ORIGIN_EXTERNAL_REQUEST, url,
-                                            referrer, bounds,
+                                            referrer, base::nullopt, bounds,
                                             session_storage_namespace);
 }
 
@@ -281,8 +283,8 @@
     SessionStorageNamespace* session_storage_namespace,
     const gfx::Rect& bounds) {
   return AddPrerenderWithPreconnectFallback(
-      ORIGIN_EXTERNAL_REQUEST_FORCED_PRERENDER, url, referrer, bounds,
-      session_storage_namespace);
+      ORIGIN_EXTERNAL_REQUEST_FORCED_PRERENDER, url, referrer, base::nullopt,
+      bounds, session_storage_namespace);
 }
 
 void PrerenderManager::CancelAllPrerenders() {
@@ -769,6 +771,7 @@
     Origin origin,
     const GURL& url_arg,
     const content::Referrer& referrer,
+    const base::Optional<url::Origin>& initiator_origin,
     const gfx::Rect& bounds,
     SessionStorageNamespace* session_storage_namespace) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -877,7 +880,7 @@
   }
 
   std::unique_ptr<PrerenderContents> prerender_contents =
-      CreatePrerenderContents(url, referrer, origin);
+      CreatePrerenderContents(url, referrer, initiator_origin, origin);
   DCHECK(prerender_contents);
   PrerenderContents* prerender_contents_ptr = prerender_contents.get();
   if (IsNoStatePrefetchEnabled())
@@ -1026,10 +1029,11 @@
 std::unique_ptr<PrerenderContents> PrerenderManager::CreatePrerenderContents(
     const GURL& url,
     const content::Referrer& referrer,
+    const base::Optional<url::Origin>& initiator_origin,
     Origin origin) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   return base::WrapUnique(prerender_contents_factory_->CreatePrerenderContents(
-      this, profile_, url, referrer, origin));
+      this, profile_, url, referrer, initiator_origin, origin));
 }
 
 void PrerenderManager::SortActivePrerenders() {
diff --git a/chrome/browser/prerender/prerender_manager.h b/chrome/browser/prerender/prerender_manager.h
index 7985195..0c87472f 100644
--- a/chrome/browser/prerender/prerender_manager.h
+++ b/chrome/browser/prerender/prerender_manager.h
@@ -14,6 +14,7 @@
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "base/optional.h"
 #include "base/time/clock.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
@@ -28,6 +29,7 @@
 #include "content/public/browser/notification_registrar.h"
 #include "content/public/browser/render_process_host_observer.h"
 #include "url/gurl.h"
+#include "url/origin.h"
 
 class Profile;
 
@@ -119,6 +121,7 @@
       const GURL& url,
       uint32_t rel_types,
       const content::Referrer& referrer,
+      const url::Origin& initiator_origin,
       const gfx::Size& size);
 
   // Adds a prerender for |url| if valid. As the prerender request is coming
@@ -444,6 +447,7 @@
       Origin origin,
       const GURL& url,
       const content::Referrer& referrer,
+      const base::Optional<url::Origin>& initiator_origin,
       const gfx::Rect& bounds,
       content::SessionStorageNamespace* session_storage_namespace);
 
@@ -474,6 +478,7 @@
   virtual std::unique_ptr<PrerenderContents> CreatePrerenderContents(
       const GURL& url,
       const content::Referrer& referrer,
+      const base::Optional<url::Origin>& initiator_origin,
       Origin origin);
 
   // Insures the |active_prerenders_| are sorted by increasing expiry time. Call
diff --git a/chrome/browser/prerender/prerender_message_filter.cc b/chrome/browser/prerender/prerender_message_filter.cc
index 658d3f6..f8986ee 100644
--- a/chrome/browser/prerender/prerender_message_filter.cc
+++ b/chrome/browser/prerender/prerender_message_filter.cc
@@ -8,6 +8,7 @@
 #include "base/macros.h"
 #include "base/memory/singleton.h"
 #include "base/task/post_task.h"
+#include "chrome/browser/bad_message.h"
 #include "chrome/browser/prerender/prerender_final_status.h"
 #include "chrome/browser/prerender/prerender_link_manager.h"
 #include "chrome/browser/prerender/prerender_link_manager_factory.h"
@@ -16,6 +17,7 @@
 #include "chrome/common/prerender_messages.h"
 #include "components/keyed_service/content/browser_context_keyed_service_shutdown_notifier_factory.h"
 #include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/child_process_security_policy.h"
 
 using content::BrowserThread;
 
@@ -112,15 +114,23 @@
     int prerender_id,
     const PrerenderAttributes& attributes,
     const content::Referrer& referrer,
+    const url::Origin& initiator_origin,
     const gfx::Size& size,
     int render_view_route_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (!prerender_link_manager_)
     return;
+  if (!initiator_origin.opaque() &&
+      !content::ChildProcessSecurityPolicy::GetInstance()
+           ->CanAccessDataForOrigin(render_process_id_,
+                                    initiator_origin.GetURL())) {
+    bad_message::ReceivedBadMessage(this,
+                                    bad_message::PMF_INVALID_INITIATOR_ORIGIN);
+    return;
+  }
   prerender_link_manager_->OnAddPrerender(
-      render_process_id_, prerender_id,
-      attributes.url, attributes.rel_types, referrer,
-      size, render_view_route_id);
+      render_process_id_, prerender_id, attributes.url, attributes.rel_types,
+      referrer, initiator_origin, size, render_view_route_id);
 }
 
 void PrerenderMessageFilter::OnCancelPrerender(
diff --git a/chrome/browser/prerender/prerender_message_filter.h b/chrome/browser/prerender/prerender_message_filter.h
index c2d5fca3..8f9d8bad 100644
--- a/chrome/browser/prerender/prerender_message_filter.h
+++ b/chrome/browser/prerender/prerender_message_filter.h
@@ -13,6 +13,7 @@
 #include "content/public/browser/browser_message_filter.h"
 #include "content/public/browser/browser_thread.h"
 #include "url/gurl.h"
+#include "url/origin.h"
 
 class Profile;
 struct PrerenderAttributes;
@@ -57,6 +58,7 @@
   void OnAddPrerender(int prerender_id,
                       const PrerenderAttributes& attributes,
                       const content::Referrer& referrer,
+                      const url::Origin& initiator_origin,
                       const gfx::Size& size,
                       int render_view_route_id);
   void OnCancelPrerender(int prerender_id);
diff --git a/chrome/browser/prerender/prerender_nostate_prefetch_browsertest.cc b/chrome/browser/prerender/prerender_nostate_prefetch_browsertest.cc
index e1edb86..63487af 100644
--- a/chrome/browser/prerender/prerender_nostate_prefetch_browsertest.cc
+++ b/chrome/browser/prerender/prerender_nostate_prefetch_browsertest.cc
@@ -359,6 +359,66 @@
   loop.Run();
 }
 
+// Check cookie loading for a cross-domain prefetched pages.
+IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest,
+                       PrefetchCookieCrossDomainSameSiteStrict) {
+  constexpr char kSecondaryDomain[] = "www.foo.com";
+  GURL cross_domain_url =
+      embedded_test_server()->GetURL(kSecondaryDomain, "/echoall");
+
+  EXPECT_TRUE(SetCookie(current_browser()->profile(), cross_domain_url,
+                        "cookie_A=A; SameSite=Strict;"));
+  EXPECT_TRUE(SetCookie(current_browser()->profile(), cross_domain_url,
+                        "cookie_B=B; SameSite=Lax;"));
+
+  std::unique_ptr<TestPrerender> test_prerender =
+      PrefetchFromURL(cross_domain_url, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
+
+  ui_test_utils::NavigateToURL(current_browser(), cross_domain_url);
+
+  EXPECT_TRUE(WaitForLoadStop(
+      current_browser()->tab_strip_model()->GetActiveWebContents()));
+
+  std::string html_content;
+  EXPECT_TRUE(content::ExecuteScriptAndExtractString(
+      current_browser()->tab_strip_model()->GetActiveWebContents(),
+      "domAutomationController.send(document.body.innerHTML)", &html_content));
+
+  // For any cross origin navigation (including prerender), SameSite Strict
+  // cookies should not be sent, but Lax should.
+  EXPECT_EQ(std::string::npos, html_content.find("cookie_A=A"));
+  EXPECT_NE(std::string::npos, html_content.find("cookie_B=B"));
+}
+
+// Check cookie loading for a same-domain prefetched pages.
+IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest,
+                       PrefetchCookieSameDomainSameSiteStrict) {
+  GURL same_domain_url = embedded_test_server()->GetURL("/echoall");
+
+  EXPECT_TRUE(SetCookie(current_browser()->profile(), same_domain_url,
+                        "cookie_A=A; SameSite=Strict;"));
+  EXPECT_TRUE(SetCookie(current_browser()->profile(), same_domain_url,
+                        "cookie_B=B; SameSite=Lax;"));
+
+  std::unique_ptr<TestPrerender> test_prerender =
+      PrefetchFromURL(same_domain_url, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
+
+  ui_test_utils::NavigateToURL(current_browser(), same_domain_url);
+
+  EXPECT_TRUE(WaitForLoadStop(
+      current_browser()->tab_strip_model()->GetActiveWebContents()));
+
+  std::string html_content;
+  EXPECT_TRUE(content::ExecuteScriptAndExtractString(
+      current_browser()->tab_strip_model()->GetActiveWebContents(),
+      "domAutomationController.send(document.body.innerHTML)", &html_content));
+
+  // For any same origin navigation (including prerender), SameSite Strict
+  // cookies should not be sent, but Lax should.
+  EXPECT_NE(std::string::npos, html_content.find("cookie_A=A"));
+  EXPECT_NE(std::string::npos, html_content.find("cookie_B=B"));
+}
+
 // Check that the LOAD_PREFETCH flag is set.
 IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, PrefetchLoadFlag) {
   GURL prefetch_page = src_server()->GetURL(kPrefetchPage);
diff --git a/chrome/browser/prerender/prerender_test_utils.cc b/chrome/browser/prerender/prerender_test_utils.cc
index 224d614..dcdf296 100644
--- a/chrome/browser/prerender/prerender_test_utils.cc
+++ b/chrome/browser/prerender/prerender_test_utils.cc
@@ -276,10 +276,16 @@
     Profile* profile,
     const GURL& url,
     const content::Referrer& referrer,
+    const base::Optional<url::Origin>& initiator_origin,
     Origin origin,
     FinalStatus expected_final_status,
     bool ignore_final_status)
-    : PrerenderContents(prerender_manager, profile, url, referrer, origin),
+    : PrerenderContents(prerender_manager,
+                        profile,
+                        url,
+                        referrer,
+                        initiator_origin,
+                        origin),
       expected_final_status_(expected_final_status),
       observer_(this),
       new_render_view_host_(nullptr),
@@ -529,15 +535,16 @@
     Profile* profile,
     const GURL& url,
     const content::Referrer& referrer,
+    const base::Optional<url::Origin>& initiator_origin,
     Origin origin) {
   ExpectedContents expected;
   if (!expected_contents_queue_.empty()) {
     expected = expected_contents_queue_.front();
     expected_contents_queue_.pop_front();
   }
-  TestPrerenderContents* contents =
-      new TestPrerenderContents(prerender_manager, profile, url, referrer,
-                                origin, expected.final_status, expected.ignore);
+  TestPrerenderContents* contents = new TestPrerenderContents(
+      prerender_manager, profile, url, referrer, initiator_origin, origin,
+      expected.final_status, expected.ignore);
   if (expected.handle)
     expected.handle->OnPrerenderCreated(contents);
   return contents;
diff --git a/chrome/browser/prerender/prerender_test_utils.h b/chrome/browser/prerender/prerender_test_utils.h
index 69b49a0..70bd70b 100644
--- a/chrome/browser/prerender/prerender_test_utils.h
+++ b/chrome/browser/prerender/prerender_test_utils.h
@@ -108,6 +108,7 @@
                         Profile* profile,
                         const GURL& url,
                         const content::Referrer& referrer,
+                        const base::Optional<url::Origin>& initiator_origin,
                         Origin origin,
                         FinalStatus expected_final_status,
                         bool ignore_final_status);
@@ -288,6 +289,7 @@
       Profile* profile,
       const GURL& url,
       const content::Referrer& referrer,
+      const base::Optional<url::Origin>& initiator_origin,
       Origin origin) override;
 
  private:
diff --git a/chrome/browser/prerender/prerender_unittest.cc b/chrome/browser/prerender/prerender_unittest.cc
index 71baedb6..dde12e0 100644
--- a/chrome/browser/prerender/prerender_unittest.cc
+++ b/chrome/browser/prerender/prerender_unittest.cc
@@ -70,6 +70,7 @@
   DummyPrerenderContents(UnitTestPrerenderManager* test_prerender_manager,
                          const GURL& url,
                          Origin origin,
+                         const base::Optional<url::Origin>& initiator_origin,
                          FinalStatus expected_final_status);
 
   ~DummyPrerenderContents() override;
@@ -190,15 +191,17 @@
       FinalStatus expected_final_status) {
     return SetNextPrerenderContents(std::make_unique<DummyPrerenderContents>(
         this, url, ORIGIN_LINK_REL_PRERENDER_CROSSDOMAIN,
+        url::Origin::Create(GURL("https://uniquedifferentorigin.com")),
         expected_final_status));
   }
 
   DummyPrerenderContents* CreateNextPrerenderContents(
       const GURL& url,
+      const base::Optional<url::Origin>& initiator_origin,
       Origin origin,
       FinalStatus expected_final_status) {
     return SetNextPrerenderContents(std::make_unique<DummyPrerenderContents>(
-        this, url, origin, expected_final_status));
+        this, url, origin, initiator_origin, expected_final_status));
   }
 
   DummyPrerenderContents* CreateNextPrerenderContents(
@@ -207,6 +210,7 @@
       FinalStatus expected_final_status) {
     auto prerender_contents = std::make_unique<DummyPrerenderContents>(
         this, url, ORIGIN_LINK_REL_PRERENDER_CROSSDOMAIN,
+        url::Origin::Create(GURL("https://uniquedifferentorigin.com")),
         expected_final_status);
     for (const GURL& alias : alias_urls)
       EXPECT_TRUE(prerender_contents->AddAliasURL(alias));
@@ -258,6 +262,7 @@
   std::unique_ptr<PrerenderContents> CreatePrerenderContents(
       const GURL& url,
       const Referrer& referrer,
+      const base::Optional<url::Origin>& initiator_origin,
       Origin origin) override {
     CHECK(next_prerender_contents_);
     EXPECT_EQ(url, next_prerender_contents_->prerender_url());
@@ -286,11 +291,13 @@
     UnitTestPrerenderManager* test_prerender_manager,
     const GURL& url,
     Origin origin,
+    const base::Optional<url::Origin>& initiator_origin,
     FinalStatus expected_final_status)
     : PrerenderContents(test_prerender_manager,
                         nullptr,
                         url,
                         Referrer(),
+                        initiator_origin,
                         origin),
       route_id_(g_next_route_id_++),
       test_prerender_manager_(test_prerender_manager),
@@ -388,7 +395,7 @@
   bool AddSimplePrerender(const GURL& url) {
     prerender_link_manager()->OnAddPrerender(
         kDefaultChildId, GetNextPrerenderID(), url, kDefaultRelTypes,
-        content::Referrer(), kSize, kDefaultRenderViewRouteId);
+        content::Referrer(), url::Origin(), kSize, kDefaultRenderViewRouteId);
     return LauncherHasRunningPrerender(kDefaultChildId, last_prerender_id());
   }
 
@@ -401,7 +408,7 @@
     referrer.url = GURL("https://www.google.com");
     prerender_link_manager()->OnAddPrerender(
         kDefaultChildId, GetNextPrerenderID(), url, kDefaultRelTypes, referrer,
-        kSize, kDefaultRenderViewRouteId);
+        url::Origin::Create(referrer.url), kSize, kDefaultRenderViewRouteId);
     return LauncherHasRunningPrerender(kDefaultChildId, last_prerender_id());
   }
 
@@ -496,7 +503,8 @@
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndEnableFeature(kGWSPrefetchHoldback);
   prerender_manager()->CreateNextPrerenderContents(
-      url, ORIGIN_GWS_PRERENDER, FINAL_STATUS_MANAGER_SHUTDOWN);
+      url, url::Origin::Create(GURL("www.google.com")), ORIGIN_GWS_PRERENDER,
+      FINAL_STATUS_MANAGER_SHUTDOWN);
 
   EXPECT_FALSE(AddSimpleGWSPrerender(url));
 }
@@ -526,7 +534,8 @@
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndDisableFeature(kGWSPrefetchHoldback);
   prerender_manager()->CreateNextPrerenderContents(
-      url, ORIGIN_GWS_PRERENDER, FINAL_STATUS_MANAGER_SHUTDOWN);
+      url, url::Origin::Create(GURL("www.google.com")), ORIGIN_GWS_PRERENDER,
+      FINAL_STATUS_MANAGER_SHUTDOWN);
 
   EXPECT_TRUE(AddSimpleGWSPrerender(url));
 }
@@ -760,15 +769,15 @@
   prerender_manager()->SetTickClockForTesting(tick_clock());
 
   // Prefetch the url once.
-  prerender_manager()->CreateNextPrerenderContents(kUrl, ORIGIN_OMNIBOX,
-                                                   FINAL_STATUS_CANCELLED);
+  prerender_manager()->CreateNextPrerenderContents(
+      kUrl, base::nullopt, ORIGIN_OMNIBOX, FINAL_STATUS_CANCELLED);
   EXPECT_TRUE(
       prerender_manager()->AddPrerenderFromOmnibox(kUrl, nullptr, gfx::Size()));
   // Cancel the prerender so that it is not reused.
   prerender_manager()->CancelAllPrerenders();
 
   prerender_manager()->CreateNextPrerenderContents(
-      kUrl, ORIGIN_OMNIBOX, FINAL_STATUS_MANAGER_SHUTDOWN);
+      kUrl, base::nullopt, ORIGIN_OMNIBOX, FINAL_STATUS_MANAGER_SHUTDOWN);
 
   // Prefetching again before time_to_live aborts, because it is a duplicate.
   tick_clock()->Advance(base::TimeDelta::FromSeconds(1));
@@ -937,12 +946,12 @@
   // Schedule a pending prerender launched from the prerender.
   DummyPrerenderContents* pending_prerender_contents =
       prerender_manager()->CreateNextPrerenderContents(
-          pending_url,
-          ORIGIN_GWS_PRERENDER,
-          FINAL_STATUS_USED);
+          pending_url, url::Origin::Create(GURL("https://www.google.com")),
+          ORIGIN_GWS_PRERENDER, FINAL_STATUS_USED);
   prerender_link_manager()->OnAddPrerender(
       child_id, GetNextPrerenderID(), pending_url, kDefaultRelTypes,
-      Referrer(url, network::mojom::ReferrerPolicy::kDefault), kSize, route_id);
+      Referrer(url, network::mojom::ReferrerPolicy::kDefault),
+      url::Origin::Create(pending_url), kSize, route_id);
   EXPECT_FALSE(LauncherHasRunningPrerender(child_id, last_prerender_id()));
   EXPECT_FALSE(pending_prerender_contents->prerendering_has_started());
 
@@ -979,12 +988,12 @@
   // Schedule a pending prerender launched from the prerender.
   DummyPrerenderContents* pending_prerender_contents =
       prerender_manager()->CreateNextPrerenderContents(
-          pending_url,
-          ORIGIN_GWS_PRERENDER,
-          FINAL_STATUS_UNSUPPORTED_SCHEME);
+          pending_url, url::Origin::Create(GURL("https://www.google.com")),
+          ORIGIN_GWS_PRERENDER, FINAL_STATUS_UNSUPPORTED_SCHEME);
   prerender_link_manager()->OnAddPrerender(
       child_id, GetNextPrerenderID(), pending_url, kDefaultRelTypes,
-      Referrer(url, network::mojom::ReferrerPolicy::kDefault), kSize, route_id);
+      Referrer(url, network::mojom::ReferrerPolicy::kDefault),
+      url::Origin::Create(pending_url), kSize, route_id);
   EXPECT_FALSE(LauncherHasRunningPrerender(child_id, last_prerender_id()));
   EXPECT_FALSE(pending_prerender_contents->prerendering_has_started());
 
@@ -1017,7 +1026,8 @@
   // Schedule a pending prerender launched from the prerender.
   prerender_link_manager()->OnAddPrerender(
       child_id, GetNextPrerenderID(), pending_url, kDefaultRelTypes,
-      Referrer(url, network::mojom::ReferrerPolicy::kDefault), kSize, route_id);
+      Referrer(url, network::mojom::ReferrerPolicy::kDefault),
+      url::Origin::Create(pending_url), kSize, route_id);
   EXPECT_FALSE(LauncherHasRunningPrerender(child_id, last_prerender_id()));
 
   // Cancel the pending prerender.
@@ -1042,7 +1052,8 @@
       url,
       FINAL_STATUS_MANAGER_SHUTDOWN);
   prerender_link_manager()->OnAddPrerender(
-      100, GetNextPrerenderID(), url, kDefaultRelTypes, Referrer(), kSize, 200);
+      100, GetNextPrerenderID(), url, kDefaultRelTypes, Referrer(),
+      url::Origin::Create(url), kSize, 200);
   EXPECT_FALSE(LauncherHasRunningPrerender(100, last_prerender_id()));
 }
 
@@ -1141,7 +1152,7 @@
 TEST_F(PrerenderTest, OmniboxAllowedWhenNotDisabled) {
   DummyPrerenderContents* prerender_contents =
       prerender_manager()->CreateNextPrerenderContents(
-          GURL("http://www.example.com"), ORIGIN_OMNIBOX,
+          GURL("http://www.example.com"), base::nullopt, ORIGIN_OMNIBOX,
           FINAL_STATUS_MANAGER_SHUTDOWN);
 
   EXPECT_TRUE(prerender_manager()->AddPrerenderFromOmnibox(
@@ -1205,7 +1216,8 @@
   GURL url("http://www.google.com/");
   DummyPrerenderContents* prerender_contents =
       prerender_manager()->CreateNextPrerenderContents(
-          url, ORIGIN_LINK_REL_PRERENDER_CROSSDOMAIN, FINAL_STATUS_USED);
+          url, url::Origin::Create(GURL("https://www.notgoogle.com")),
+          ORIGIN_LINK_REL_PRERENDER_CROSSDOMAIN, FINAL_STATUS_USED);
   EXPECT_TRUE(AddSimplePrerender(url));
   EXPECT_TRUE(prerender_contents->prerendering_has_started());
   std::unique_ptr<PrerenderContents> entry =
@@ -1222,7 +1234,8 @@
       net::NetworkChangeNotifier::GetConnectionType()));
   DummyPrerenderContents* prerender_contents =
       prerender_manager()->CreateNextPrerenderContents(
-          url, ORIGIN_LINK_REL_PRERENDER_CROSSDOMAIN, FINAL_STATUS_USED);
+          url, url::Origin::Create(GURL("https://www.notexample.com")),
+          ORIGIN_LINK_REL_PRERENDER_CROSSDOMAIN, FINAL_STATUS_USED);
   EXPECT_TRUE(AddSimplePrerender(url));
   EXPECT_TRUE(prerender_contents->prerendering_has_started());
   std::unique_ptr<PrerenderContents> entry =
@@ -1242,8 +1255,7 @@
   GURL url("http://www.google.com/");
   DummyPrerenderContents* prerender_contents =
       prerender_manager()->CreateNextPrerenderContents(
-          url,
-          ORIGIN_EXTERNAL_REQUEST,
+          url, base::nullopt, ORIGIN_EXTERNAL_REQUEST,
           FINAL_STATUS_MANAGER_SHUTDOWN);
   std::unique_ptr<PrerenderHandle> prerender_handle(
       prerender_manager()->AddPrerenderFromExternalRequest(
@@ -1271,7 +1283,7 @@
   GURL url("http://www.google.com/");
   DummyPrerenderContents* prerender_contents =
       prerender_manager()->CreateNextPrerenderContents(
-          url, ORIGIN_EXTERNAL_REQUEST, FINAL_STATUS_USED);
+          url, base::nullopt, ORIGIN_EXTERNAL_REQUEST, FINAL_STATUS_USED);
   std::unique_ptr<PrerenderHandle> prerender_handle(
       prerender_manager()->AddPrerenderFromExternalRequest(
           url, content::Referrer(), nullptr, gfx::Rect(kSize)));
@@ -1293,7 +1305,8 @@
   DummyPrerenderContents* prerender_contents = nullptr;
   std::unique_ptr<PrerenderHandle> prerender_handle;
   prerender_contents = prerender_manager()->CreateNextPrerenderContents(
-      url, ORIGIN_EXTERNAL_REQUEST_FORCED_PRERENDER, FINAL_STATUS_USED);
+      url, base::nullopt, ORIGIN_EXTERNAL_REQUEST_FORCED_PRERENDER,
+      FINAL_STATUS_USED);
   prerender_handle = prerender_manager()->AddForcedPrerenderFromExternalRequest(
       url, content::Referrer(), nullptr, gfx::Rect(kSize));
   EXPECT_TRUE(prerender_handle);
@@ -1700,7 +1713,7 @@
   GURL pending_url("http://www.neverlaunched.com");
   prerender_link_manager()->OnAddPrerender(
       child_id, GetNextPrerenderID(), pending_url, kDefaultRelTypes,
-      content::Referrer(), kSize, route_id);
+      content::Referrer(), url::Origin::Create(pending_url), kSize, route_id);
   const int second_prerender_id = last_prerender_id();
 
   EXPECT_FALSE(IsEmptyPrerenderLinkManager());
@@ -1841,7 +1854,7 @@
   GURL url("http://www.google.com/");
   DummyPrerenderContents* prerender_contents =
       prerender_manager()->CreateNextPrerenderContents(
-          url, ORIGIN_EXTERNAL_REQUEST_FORCED_PRERENDER,
+          url, base::nullopt, ORIGIN_EXTERNAL_REQUEST_FORCED_PRERENDER,
           FINAL_STATUS_MANAGER_SHUTDOWN);
   std::unique_ptr<PrerenderHandle> prerender_handle =
       prerender_manager()->AddForcedPrerenderFromExternalRequest(
diff --git a/chrome/browser/resources/extensions/activity_log/activity_log_history.html b/chrome/browser/resources/extensions/activity_log/activity_log_history.html
index e7eab6e4..87ae011 100644
--- a/chrome/browser/resources/extensions/activity_log/activity_log_history.html
+++ b/chrome/browser/resources/extensions/activity_log/activity_log_history.html
@@ -3,16 +3,15 @@
 <link rel="import" href="chrome://resources/html/cr.html">
 <link rel="import" href="chrome://resources/html/promise_resolver.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_action_menu/cr_action_menu.html">
+<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_search_field/cr_search_field.html">
-<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 <link rel="import" href="activity_log_history_item.html">
 <link rel="import" href="../shared_style.html">
 
 <dom-module id="activity-log-history">
   <template>
-    <style include="paper-button-style shared-style">
+    <style include="shared-style">
       :host {
         --activity-log-call-and-count-width: 514px;
         --activity-type-width: 85px;
@@ -56,10 +55,10 @@
       <cr-search-field label="$i18n{activityLogSearchLabel}"
         on-search-changed="onSearchChanged_">
       </cr-search-field >
-      <paper-button class="clear-activities-button"
+      <cr-button class="clear-activities-button"
           on-click="onClearActivitiesClick_">
         $i18n{clearActivities}
-      </paper-button>
+      </cr-button>
       <cr-icon-button id="more-actions" iron-icon="cr:more-vert"
           title="$i18n{activityLogMoreActionsLabel}"
           on-click="onMoreActionsClick_"></cr-icon-button>
diff --git a/chrome/browser/resources/extensions/activity_log/activity_log_stream.html b/chrome/browser/resources/extensions/activity_log/activity_log_stream.html
index 4ae7a45d..9f1c675 100644
--- a/chrome/browser/resources/extensions/activity_log/activity_log_stream.html
+++ b/chrome/browser/resources/extensions/activity_log/activity_log_stream.html
@@ -1,16 +1,15 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
 <link rel="import" href="chrome://resources/html/cr.html">
+<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_search_field/cr_search_field.html">
-<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-list/iron-list.html">
 <link rel="import" href="activity_log_stream_item.html">
 <link rel="import" href="../shared_style.html">
 
 <dom-module id="activity-log-stream">
   <template>
-    <style include="shared-style paper-button-style">
+    <style include="shared-style">
       :host {
         --activity-log-call-and-time-width: 575px;
         --activity-type-width: 85px;
@@ -50,18 +49,17 @@
       <cr-search-field label="$i18n{activityLogSearchLabel}"
         on-search-changed="onSearchChanged_">
       </cr-search-field >
-      <paper-button id="toggle-stream-button" on-click="onToggleButtonClick_">
+      <cr-button id="toggle-stream-button" on-click="onToggleButtonClick_">
         <span hidden$="[[isStreamOn_]]">
           $i18n{startActivityStream}
         </span>
         <span hidden$="[[!isStreamOn_]]">
           $i18n{stopActivityStream}
         </span>
-      </paper-button>
-      <paper-button class="clear-activities-button"
-          on-click="clearStream">
+      </cr-button>
+      <cr-button class="clear-activities-button" on-click="clearStream">
         $i18n{clearActivities}
-      </paper-button>
+      </cr-button>
     </div>
     <div id="empty-stream-message" class="activity-message"
         hidden$="[[!isStreamEmpty_(activityStream_.length)]]">
diff --git a/chrome/browser/resources/extensions/detail_view.html b/chrome/browser/resources/extensions/detail_view.html
index b021c518..c279068a 100644
--- a/chrome/browser/resources/extensions/detail_view.html
+++ b/chrome/browser/resources/extensions/detail_view.html
@@ -1,12 +1,12 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
 <link rel="import" href="chrome://resources/cr_elements/cr_container_shadow_behavior.html">
+<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_icons_css.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_link_row/cr_link_row.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_toggle/cr_toggle.html">
 <link rel="import" href="chrome://resources/cr_elements/icons.html">
-<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/policy/cr_tooltip_icon.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
@@ -16,7 +16,6 @@
 <link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-spinner/paper-spinner-lite.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
 <link rel="import" href="item_behavior.html">
@@ -31,7 +30,8 @@
 
 <dom-module id="extensions-detail-view">
   <template>
-    <style include="iron-flex cr-shared-style cr-icons action-link paper-button-style shared-style">
+    <style include="iron-flex cr-shared-style cr-icons action-link
+        shared-style">
       :host {
         --iron-icon-fill-color: var(--cr-secondary-text-color);
         display: block;
@@ -189,10 +189,10 @@
                 icon-aria-label="[[data.controlledInfo.type]]">
             </cr-tooltip-icon>
             <template is="dom-if" if="[[isTerminated_(data.state)]]">
-              <paper-button id="terminated-reload-button" class="action-button"
+              <cr-button id="terminated-reload-button" class="action-button"
                   on-click="onReloadTap_">
                 $i18n{itemReload}
-              </paper-button>
+              </cr-button>
             </template>
             <cr-toggle id="enable-toggle"
                 aria-label$="[[appOrExtension(
@@ -217,10 +217,10 @@
               </template>
             </span>
             <template is="dom-if" if="[[!isTerminated_(data.state)]]">
-              <paper-button id="warnings-reload-button" class="action-button"
+              <cr-button id="warnings-reload-button" class="action-button"
                   on-click="onReloadTap_">
                 $i18n{itemReload}
-              </paper-button>
+              </cr-button>
             </template>
           </div>
           <div class="section continuation warning" id="suspicious-warning"
@@ -238,10 +238,10 @@
               hidden$="[[!data.disableReasons.corruptInstall]]">
             <iron-icon class="warning-icon" icon="cr:warning"></iron-icon>
             <span>$i18n{itemCorruptInstall}</span>
-            <paper-button id="repair-button" class="action-button"
+            <cr-button id="repair-button" class="action-button"
                 on-click="onRepairTap_">
               $i18n{itemRepair}
-            </paper-button>
+            </cr-button>
           </div>
           <div class="section continuation warning" id="blacklisted-warning"
               hidden$="[[!data.blacklistText]]">
diff --git a/chrome/browser/resources/extensions/error_page.html b/chrome/browser/resources/extensions/error_page.html
index d5254f4..4e39a8b7 100644
--- a/chrome/browser/resources/extensions/error_page.html
+++ b/chrome/browser/resources/extensions/error_page.html
@@ -1,10 +1,10 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
+<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_container_shadow_behavior.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_icons_css.html">
 <link rel="import" href="chrome://resources/cr_elements/icons.html">
-<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="import" href="chrome://resources/html/cr.html">
@@ -12,7 +12,6 @@
 <link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-collapse/iron-collapse.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
 <link rel="import" href="code_section.html">
 <link rel="import" href="item_util.html">
@@ -21,7 +20,7 @@
 
 <dom-module id="extensions-error-page">
   <template>
-    <style include="paper-button-style cr-icons cr-shared-style shared-style">
+    <style include="cr-icons cr-shared-style shared-style">
       :host {
         display: block;
         height: 100%;
@@ -144,9 +143,9 @@
               aria-label="$i18n{back}" on-click="onCloseButtonTap_">
           </cr-icon-button>
           <span>$i18n{errorsPageHeading}</span>
-          <paper-button on-click="onClearAllTap_" hidden="[[!entries_.length]]">
+          <cr-button on-click="onClearAllTap_" hidden="[[!entries_.length]]">
             $i18n{clearAll}
-          </paper-button>
+          </cr-button>
         </div>
         <div class="section">
           <div id="errorsList">
diff --git a/chrome/browser/resources/extensions/install_warnings_dialog.html b/chrome/browser/resources/extensions/install_warnings_dialog.html
index 19b213005..3763bf59 100644
--- a/chrome/browser/resources/extensions/install_warnings_dialog.html
+++ b/chrome/browser/resources/extensions/install_warnings_dialog.html
@@ -1,17 +1,16 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
+<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
-<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
 <link rel="import" href="chrome://resources/html/assert.html">
 <link rel="import" href="chrome://resources/html/cr.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 <link rel="import" href="code_section.html">
 
 <dom-module id="extensions-install-warnings-dialog">
   <template>
-    <style include="cr-shared-style paper-button-style">
+    <style include="cr-shared-style">
       div[slot='body'] ul {
         background-color: var(--paper-red-50);
         margin: 0;
@@ -36,9 +35,9 @@
         </ul>
       </div>
       <div slot="button-container">
-        <paper-button class="action-button" on-click="onOkTap_">
+        <cr-button class="action-button" on-click="onOkTap_">
           $i18n{ok}
-        </paper-button>
+        </cr-button>
       </div>
     </cr-dialog>
   </template>
diff --git a/chrome/browser/resources/extensions/item.html b/chrome/browser/resources/extensions/item.html
index 5fa187a..6829b68 100644
--- a/chrome/browser/resources/extensions/item.html
+++ b/chrome/browser/resources/extensions/item.html
@@ -1,11 +1,11 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
+<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_icons_css.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_toggle/cr_toggle.html">
 <link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/icons.html">
-<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="import" href="chrome://resources/html/action_link.html">
 <link rel="import" href="chrome://resources/html/action_link_css.html">
@@ -25,7 +25,8 @@
 
 <dom-module id="extensions-item">
   <template>
-    <style include="iron-flex cr-hidden-style cr-icons action-link paper-button-style shared-style">
+    <style include="iron-flex cr-hidden-style cr-icons action-link
+        shared-style">
       .bounded-text,
       .multiline-clippable-text,
       .clippable-flex-text {
@@ -145,7 +146,7 @@
         padding-top: 8px;
       }
 
-      #button-strip paper-button {
+      #button-strip cr-button {
         margin-inline-start: 8px;
       }
 
@@ -304,20 +305,20 @@
       </div>
       <div id="button-strip" class="layout horizontal center">
         <div class="layout flex horizontal center">
-          <paper-button id="detailsButton" on-click="onDetailsTap_"
+          <cr-button id="detailsButton" on-click="onDetailsTap_"
               aria-describedby="a11yAssociation">
             $i18n{itemDetails}
-          </paper-button>
-          <paper-button id="remove-button" on-click="onRemoveTap_"
+          </cr-button>
+          <cr-button id="remove-button" on-click="onRemoveTap_"
               aria-describedby="a11yAssociation"
               hidden="[[isControlled_(data.controlledInfo)]]">
             $i18n{remove}
-          </paper-button>
+          </cr-button>
           <template is="dom-if" if="[[shouldShowErrorsButton_(data.*)]]">
-            <paper-button id="errors-button" on-click="onErrorsTap_"
+            <cr-button id="errors-button" on-click="onErrorsTap_"
                 aria-describedby="a11yAssociation">
               $i18n{itemErrors}
-            </paper-button>
+            </cr-button>
           </template>
         </div>
         <template is="dom-if" if="[[!computeDevReloadButtonHidden_(data.*)]]">
@@ -326,16 +327,16 @@
               on-click="onReloadTap_"></cr-icon-button>
         </template>
         <template is="dom-if" if="[[data.disableReasons.corruptInstall]]">
-          <paper-button id="repair-button" class="action-button"
+          <cr-button id="repair-button" class="action-button"
               aria-describedby="a11yAssociation" on-click="onRepairTap_">
             $i18n{itemRepair}
-          </paper-button>
+          </cr-button>
         </template>
         <template is="dom-if" if="[[isTerminated_(data.state)]]">
-          <paper-button id="terminated-reload-button" on-click="onReloadTap_"
+          <cr-button id="terminated-reload-button" on-click="onReloadTap_"
               aria-describedby="a11yAssociation" class="action-button">
             $i18n{itemReload}
-          </paper-button>
+          </cr-button>
         </template>
         <cr-toggle id="enable-toggle"
             aria-label$="[[appOrExtension(
diff --git a/chrome/browser/resources/extensions/kiosk_dialog.html b/chrome/browser/resources/extensions/kiosk_dialog.html
index 78eb1bae..7210150 100644
--- a/chrome/browser/resources/extensions/kiosk_dialog.html
+++ b/chrome/browser/resources/extensions/kiosk_dialog.html
@@ -1,23 +1,22 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
+<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_icons_css.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html">
-<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
 <link rel="import" href="chrome://resources/html/assert.html">
 <link rel="import" href="chrome://resources/html/cr.html">
 <link rel="import" href="chrome://resources/html/util.html">
 <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 <link rel="import" href="item_behavior.html">
 <link rel="import" href="kiosk_browser_proxy.html">
 
 <dom-module id="extensions-kiosk-dialog">
   <template>
-    <style include="cr-shared-style paper-button-style cr-icons">
+    <style include="cr-shared-style cr-icons">
       #add-kiosk-app {
         margin-bottom: 10px;
         margin-top: 20px;
@@ -27,7 +26,7 @@
         width: 350px;
       }
 
-      #add-kiosk-app paper-button {
+      #add-kiosk-app cr-button {
         margin-inline-start: 10px;
       }
 
@@ -88,12 +87,12 @@
                 </span>
               </div>
               <div class="item-controls">
-                <paper-button hidden="[[!canEditAutoLaunch_]]"
+                <cr-button hidden="[[!canEditAutoLaunch_]]"
                     on-click="onAutoLaunchButtonTap_">
                   [[getAutoLaunchButtonLabel_(item.autoLaunch,
                       '$i18nPolymer{kioskDisableAutoLaunch}',
                       '$i18nPolymer{kioskEnableAutoLaunch}')]]
-                </paper-button>
+                </cr-button>
                 <cr-icon-button class="icon-delete-gray"
                     on-click="onDeleteAppTap_"></cr-icon-button>
               </div>
@@ -106,10 +105,10 @@
               invalid="[[errorAppId_]]" on-keydown="clearInputInvalid_"
               error-message="[[getErrorMessage_(
                   '$i18nPolymer{kioskInvalidApp}', errorAppId_)]]">
-            <paper-button id="add-button" on-click="onAddAppTap_"
+            <cr-button id="add-button" on-click="onAddAppTap_"
                 disabled="[[!addAppInput_]]" slot="suffix">
               $i18n{add}
-            </paper-button>
+            </cr-button>
           </cr-input>
         </div>
         <cr-checkbox disabled="[[!canEditBailout_]]" id="bailout"
@@ -119,9 +118,9 @@
         </cr-checkbox>
       </div>
       <div slot="button-container">
-        <paper-button class="action-button" on-click="onDoneTap_">
+        <cr-button class="action-button" on-click="onDoneTap_">
           $i18n{done}
-        </paper-button>
+        </cr-button>
       </div>
     </cr-dialog>
     <cr-dialog id="confirm-dialog" close-text="$i18n{close}"
@@ -129,14 +128,12 @@
       <div slot="title">$i18n{kioskDisableBailoutWarningTitle}</div>
       <div slot="body">$i18n{kioskDisableBailoutWarningBody}</div>
       <div slot="button-container">
-        <paper-button class="cancel-button"
-            on-click="onBailoutDialogCancelTap_">
+        <cr-button class="cancel-button" on-click="onBailoutDialogCancelTap_">
           $i18n{cancel}
-        </paper-button>
-        <paper-button class="action-button"
-            on-click="onBailoutDialogConfirmTap_">
+        </cr-button>
+        <cr-button class="action-button" on-click="onBailoutDialogConfirmTap_">
           $i18n{confirm}
-        </paper-button>
+        </cr-button>
       </div>
     </cr-dialog>
   </template>
diff --git a/chrome/browser/resources/extensions/load_error.html b/chrome/browser/resources/extensions/load_error.html
index 77a0f3d1..682b4fb 100644
--- a/chrome/browser/resources/extensions/load_error.html
+++ b/chrome/browser/resources/extensions/load_error.html
@@ -1,17 +1,16 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
+<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
-<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
 <link rel="import" href="chrome://resources/html/assert.html">
 <link rel="import" href="chrome://resources/html/cr.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-spinner/paper-spinner-lite.html">
 <link rel="import" href="code_section.html">
 
 <dom-module id="extensions-load-error">
   <template>
-    <style include="cr-shared-style paper-button-style">
+    <style include="cr-shared-style">
       .description-row {
         display: flex;
       }
@@ -44,13 +43,13 @@
       </div>
       <div slot="button-container">
         <paper-spinner-lite active="[[retrying_]]"></paper-spinner-lite>
-        <paper-button class="cancel-button" on-click="close">
+        <cr-button class="cancel-button" on-click="close">
           $i18n{cancel}
-        </paper-button>
-        <paper-button class="action-button" disabled="[[retrying_]]"
+        </cr-button>
+        <cr-button class="action-button" disabled="[[retrying_]]"
             on-click="onRetryTap_">
           $i18n{loadErrorRetry}
-        </paper-button>
+        </cr-button>
       </div>
     </cr-dialog>
   </template>
diff --git a/chrome/browser/resources/extensions/pack_dialog.html b/chrome/browser/resources/extensions/pack_dialog.html
index 1a3c789..6718e23 100644
--- a/chrome/browser/resources/extensions/pack_dialog.html
+++ b/chrome/browser/resources/extensions/pack_dialog.html
@@ -1,25 +1,24 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
+<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html">
-<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="import" href="chrome://resources/html/assert.html">
 <link rel="import" href="chrome://resources/html/cr.html">
 <link rel="import" href="chrome://resources/html/util.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 <link rel="import" href="pack_dialog_alert.html">
 
 <dom-module id="extensions-pack-dialog">
   <template>
-    <style include="cr-shared-style paper-button-style">
+    <style include="cr-shared-style">
       cr-input {
         margin-top: var(--cr-form-field-bottom-spacing);
         --cr-input-error-display: none;
       }
 
-      paper-button[slot='suffix'] {
+      cr-button[slot='suffix'] {
         margin-inline-start: 10px;
       }
 
@@ -34,27 +33,27 @@
         <div>$i18n{packDialogContent}</div>
         <cr-input id="root-dir" label="$i18n{packDialogExtensionRoot}"
             value="{{packDirectory_}}">
-          <paper-button id="root-dir-browse" on-click="onRootBrowse_"
+          <cr-button id="root-dir-browse" on-click="onRootBrowse_"
               slot="suffix">
             $i18n{packDialogBrowse}
-          </paper-button>
+          </cr-button>
         </cr-input>
         <cr-input id="key-file" label="$i18n{packDialogKeyFile}"
             value="{{keyFile_}}">
-          <paper-button id="key-file-browse" on-click="onKeyBrowse_"
+          <cr-button id="key-file-browse" on-click="onKeyBrowse_"
               slot="suffix">
             $i18n{packDialogBrowse}
-          </paper-button>
+          </cr-button>
         </cr-input>
       </div>
       <div slot="button-container">
-        <paper-button class="cancel-button" on-click="onCancelTap_">
+        <cr-button class="cancel-button" on-click="onCancelTap_">
           $i18n{cancel}
-        </paper-button>
-        <paper-button class="action-button" on-click="onConfirmTap_"
+        </cr-button>
+        <cr-button class="action-button" on-click="onConfirmTap_"
             disabled="[[!packDirectory_]]">
           $i18n{packDialogConfirm}
-        </paper-button>
+        </cr-button>
       </div>
     </cr-dialog>
     <template is="dom-if" if="[[lastResponse_]]" restamp>
diff --git a/chrome/browser/resources/extensions/pack_dialog_alert.html b/chrome/browser/resources/extensions/pack_dialog_alert.html
index 79fa1a74..a08e9fe 100644
--- a/chrome/browser/resources/extensions/pack_dialog_alert.html
+++ b/chrome/browser/resources/extensions/pack_dialog_alert.html
@@ -1,15 +1,14 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
+<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
-<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
 <link rel="import" href="chrome://resources/html/assert.html">
 <link rel="import" href="chrome://resources/html/cr.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 
 <dom-module id="extensions-pack-dialog-alert">
   <template>
-    <style include="cr-shared-style paper-button-style">
+    <style include="cr-shared-style">
       .body {
         white-space: pre-wrap;
         word-break: break-word;
@@ -21,14 +20,14 @@
       <!-- No whitespace or new-lines allowed within the div.body tag. -->
       <div class="body" slot="body">[[model.message]]</div>
       <div class="button-container" slot="button-container">
-        <paper-button class$="[[getCancelButtonClass_(confirmLabel_)]]"
+        <cr-button class$="[[getCancelButtonClass_(confirmLabel_)]]"
             on-click="onCancelTap_" hidden="[[!cancelLabel_]]">
           [[cancelLabel_]]
-        </paper-button>
-        <paper-button class="action-button" on-click="onConfirmTap_"
+        </cr-button>
+        <cr-button class="action-button" on-click="onConfirmTap_"
             hidden="[[!confirmLabel_]]">
           [[confirmLabel_]]
-        </paper-button>
+        </cr-button>
       </div>
     </cr-dialog>
   </template>
diff --git a/chrome/browser/resources/extensions/runtime_host_permissions.html b/chrome/browser/resources/extensions/runtime_host_permissions.html
index 3b6c39f..5215c32 100644
--- a/chrome/browser/resources/extensions/runtime_host_permissions.html
+++ b/chrome/browser/resources/extensions/runtime_host_permissions.html
@@ -4,7 +4,6 @@
 <link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_radio_group/cr_radio_group.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_radio_button/cr_radio_button.html">
-<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/icons.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
@@ -14,15 +13,13 @@
 <link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html">
 <link rel="import" href="chrome://resources/html/md_select_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 <link rel="import" href="runtime_hosts_dialog.html">
 <link rel="import" href="shared_style.html">
 <link rel="import" href="strings.html">
 
 <dom-module id="extensions-runtime-host-permissions">
   <template>
-    <style include="cr-shared-style action-link md-select paper-button-style
-        shared-style">
+    <style include="cr-shared-style action-link md-select shared-style">
       iron-icon {
         --iron-icon-height: var(--cr-icon-size);
         --iron-icon-width: var(--cr-icon-size);
diff --git a/chrome/browser/resources/extensions/runtime_hosts_dialog.html b/chrome/browser/resources/extensions/runtime_hosts_dialog.html
index b639ed57..db6b162 100644
--- a/chrome/browser/resources/extensions/runtime_hosts_dialog.html
+++ b/chrome/browser/resources/extensions/runtime_hosts_dialog.html
@@ -1,15 +1,14 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
+<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html">
-<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 <link rel="import" href="strings.html">
 
 <dom-module id="extensions-runtime-hosts-dialog">
   <template>
-    <style include="cr-shared-style paper-button-style"></style>
+    <style include="cr-shared-style"></style>
     <cr-dialog id="dialog" close-text="$i18n{close}">
       <div slot="title">[[computeDialogTitle_(currentSite)]]</div>
       <div slot="body">
@@ -23,13 +22,13 @@
         </cr-input>
       </div>
       <div slot="button-container">
-        <paper-button class="cancel-button" on-click="onCancelTap_">
+        <cr-button class="cancel-button" on-click="onCancelTap_">
           $i18n{cancel}
-        </paper-button>
-        <paper-button class="action-button" id="submit" on-click="onSubmitTap_"
+        </cr-button>
+        <cr-button class="action-button" id="submit" on-click="onSubmitTap_"
             disabled="[[computeSubmitButtonDisabled_(inputInvalid_, site_)]]">
           [[computeSubmitButtonLabel_(currentSite)]]
-        </paper-button>
+        </cr-button>
       </div>
     </cr-dialog>
   </template>
diff --git a/chrome/browser/resources/extensions/toolbar.html b/chrome/browser/resources/extensions/toolbar.html
index 77ee1f8..3379fc6 100644
--- a/chrome/browser/resources/extensions/toolbar.html
+++ b/chrome/browser/resources/extensions/toolbar.html
@@ -1,10 +1,10 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
+<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_toast/cr_toast.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_toggle/cr_toggle.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_toolbar/cr_toolbar.html">
 <link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
-<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/policy/cr_tooltip_icon.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="import" href="chrome://resources/html/assert.html">
@@ -17,7 +17,7 @@
 
 <dom-module id="extensions-toolbar">
   <template>
-    <style include="cr-hidden-style paper-button-style">
+    <style include="cr-hidden-style">
       :host {
         --border-bottom-height: 1px;
         --button-row-height: calc(2 * var(--padding-top-bottom) +
@@ -74,7 +74,7 @@
         width: 100%;
       }
 
-      #buttonStrip paper-button {
+      #buttonStrip cr-button {
         margin-inline-end: 16px;
       }
 
@@ -120,22 +120,22 @@
     </template>
     <div id="devDrawer" expanded$="[[expanded_]]">
       <div id="buttonStrip">
-        <paper-button hidden$="[[!canLoadUnpacked]]" id="loadUnpacked"
+        <cr-button hidden$="[[!canLoadUnpacked]]" id="loadUnpacked"
             on-click="onLoadUnpackedTap_">
           $i18n{toolbarLoadUnpacked}
-        </paper-button>
-        <paper-button id="packExtensions" on-click="onPackTap_">
+        </cr-button>
+        <cr-button id="packExtensions" on-click="onPackTap_">
           $i18n{toolbarPack}
-        </paper-button>
-        <paper-button id="updateNow" on-click="onUpdateNowTap_"
+        </cr-button>
+        <cr-button id="updateNow" on-click="onUpdateNowTap_"
             title="$i18n{toolbarUpdateNowTooltip}">
           $i18n{toolbarUpdateNow}
-        </paper-button>
+        </cr-button>
 <if expr="chromeos">
-        <paper-button id="kioskExtensions" on-click="onKioskTap_"
+        <cr-button id="kioskExtensions" on-click="onKioskTap_"
             hidden$="[[!kioskEnabled]]">
           $i18n{manageKioskApp}
-        </paper-button>
+        </cr-button>
 </if>
       </div>
     </div>
diff --git a/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/viewer-zoom-button.html b/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/viewer-zoom-button.html
index 209d833..be487dc8 100644
--- a/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/viewer-zoom-button.html
+++ b/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/viewer-zoom-button.html
@@ -31,10 +31,10 @@
       }
 
       cr-icon-button {
-        --cr-icon-button-border-radius: 50%;
         --cr-icon-button-size: 36px;
         --cr-icon-button-icon-size: 20px;
         background-color: rgb(242, 242, 242);
+        border-radius: 50%;
         color: var(--paper-grey-700);
         overflow: visible;
         @apply --shadow-elevation-2dp;
diff --git a/chrome/browser/resources/usb_internals/descriptor_panel.js b/chrome/browser/resources/usb_internals/descriptor_panel.js
index f894ea5..baef3be 100644
--- a/chrome/browser/resources/usb_internals/descriptor_panel.js
+++ b/chrome/browser/resources/usb_internals/descriptor_panel.js
@@ -8,9 +8,15 @@
  */
 
 cr.define('descriptor_panel', function() {
+  const INPUT_TYPE_DECIMAL_WITH_DROPDOWN = 0;
+  const INPUT_TYPE_HEX_BYTE = 1;
+
   // Standard USB requests and descriptor types:
   const GET_DESCRIPTOR_REQUEST = 0x06;
 
+  const CONTROL_TRANSFER_DIRECTION_HOST_TO_DEVICE = 0;
+  const CONTROL_TRANSFER_DIRECTION_DEVICE_TO_HOST = 1;
+
   const DEVICE_DESCRIPTOR_TYPE = 0x01;
   const CONFIGURATION_DESCRIPTOR_TYPE = 0x02;
   const STRING_DESCRIPTOR_TYPE = 0x03;
@@ -408,12 +414,12 @@
     }
 
     /**
-     * Checks if the status of a descriptor read indicates success.
+     * Checks if the status of a control transfer indicates success.
      * @param {number} status
      * @param {string} defaultMessage
      * @private
      */
-    checkDescriptorGetSuccess_(status, defaultMessage) {
+    checkTransferSuccess_(status, defaultMessage) {
       let failReason = '';
       switch (status) {
         case device.mojom.UsbTransferStatus.COMPLETED:
@@ -465,6 +471,22 @@
     }
 
     /**
+     * Shows an warning message.
+     * @param {string} message
+     * @private
+     */
+    showWarn_(message) {
+      const warnTemplate = document.querySelector('#warn');
+
+      const clone = document.importNode(warnTemplate.content, true);
+
+      const warnText = clone.querySelector('warn');
+      warnText.textContent = message;
+
+      this.rootElement_.prepend(clone);
+    }
+
+    /**
      * Gets device descriptor of current device.
      * @return {!Uint8Array}
      * @private
@@ -484,7 +506,7 @@
           usbControlTransferParams, DEVICE_DESCRIPTOR_LENGTH,
           CONTROL_TRANSFER_TIMEOUT_MS);
 
-      this.checkDescriptorGetSuccess_(
+      this.checkTransferSuccess_(
           response.status, 'Failed to read the device descriptor.');
 
       return new Uint8Array(response.data);
@@ -621,7 +643,7 @@
           usbControlTransferParams, CONFIGURATION_DESCRIPTOR_LENGTH,
           CONTROL_TRANSFER_TIMEOUT_MS);
 
-      this.checkDescriptorGetSuccess_(
+      this.checkTransferSuccess_(
           response.status,
           'Failed to read the device configuration descriptor to determine ' +
               'the total descriptor length.');
@@ -633,7 +655,7 @@
       response = await this.usbDeviceProxy_.controlTransferIn(
           usbControlTransferParams, length, CONTROL_TRANSFER_TIMEOUT_MS);
 
-      this.checkDescriptorGetSuccess_(
+      this.checkTransferSuccess_(
           response.status,
           'Failed to read the complete configuration descriptor.');
 
@@ -1008,7 +1030,7 @@
             usbControlTransferParams, MAX_STRING_DESCRIPTOR_LENGTH,
             CONTROL_TRANSFER_TIMEOUT_MS);
 
-        this.checkDescriptorGetSuccess_(
+        this.checkTransferSuccess_(
             response.status,
             'Failed to read the device string descriptor to determine ' +
                 'all supported languages.');
@@ -1071,7 +1093,7 @@
           CONTROL_TRANSFER_TIMEOUT_MS);
 
       const languageCodeStr = parseLanguageCode(languageCode);
-      this.checkDescriptorGetSuccess_(
+      this.checkTransferSuccess_(
           response.status,
           `Failed to read the device string descriptor of index: ${
               index}, language: ${languageCodeStr}.`);
@@ -1208,12 +1230,13 @@
       button.addEventListener('click', () => {
         this.clearView();
         const index = Number.parseInt(this.indexInput_.value);
-        if (this.checkIndexValueValid_(index)) {
+        if (this.checkParamValid_(index, 'Index', 1, 255)) {
           if (languageCodeInput.value === 'All') {
             this.renderStringDescriptorForAllLanguages(index);
           } else {
             const languageCode = Number.parseInt(languageCodeInput.value);
-            if (this.checkLanguageCodeValueValid_(languageCode)) {
+            if (this.checkParamValid_(
+                    languageCode, 'Language Code', 0, 65535)) {
               this.renderStringDescriptorForLanguageCode(index, languageCode);
             }
           }
@@ -1231,36 +1254,6 @@
     }
 
     /**
-     * Checks if the user input index is a valid uint8 number.
-     * @param {number} index
-     * @return {boolean}
-     * @private
-     */
-    checkIndexValueValid_(index) {
-      // index is 8 bit. 0 is reserved to query all supported language codes.
-      if (Number.isNaN(index) || index < 1 || index > 255) {
-        this.showError_('Invalid Index.');
-        return false;
-      }
-      return true;
-    }
-
-    /**
-     * Checks if the user input language code is a valid uint16 number.
-     * @param {number} languageCode
-     * @return {boolean}
-     * @private
-     */
-    checkLanguageCodeValueValid_(languageCode) {
-      if (Number.isNaN(languageCode) || languageCode < 0 ||
-          languageCode > 65535) {
-        this.showError_('Invalid Language Code.');
-        return false;
-      }
-      return true;
-    }
-
-    /**
      * Gets the Binary device Object Store (BOS) descriptor of the current
      * device, which contains the WebUSB descriptor and Microsoft OS 2.0
      * descriptor.
@@ -1282,7 +1275,7 @@
           usbControlTransferParams, BOS_DESCRIPTOR_HEADER_LENGTH,
           CONTROL_TRANSFER_TIMEOUT_MS);
 
-      this.checkDescriptorGetSuccess_(
+      this.checkTransferSuccess_(
           response.status,
           'Failed to read the device BOS descriptor to determine ' +
               'the total descriptor length.');
@@ -1294,7 +1287,7 @@
       response = await this.usbDeviceProxy_.controlTransferIn(
           usbControlTransferParams, length, CONTROL_TRANSFER_TIMEOUT_MS);
 
-      this.checkDescriptorGetSuccess_(
+      this.checkTransferSuccess_(
           response.status, 'Failed to read the complete BOS descriptor.');
 
       return new Uint8Array(response.data);
@@ -1733,7 +1726,7 @@
             usbControlTransferParams, MAX_URL_DESCRIPTOR_LENGTH,
             CONTROL_TRANSFER_TIMEOUT_MS);
 
-        this.checkDescriptorGetSuccess_(
+        this.checkTransferSuccess_(
             urlResponse.status, 'Failed to read the device URL descriptor.');
       } catch (e) {
         this.showError_(e.message);
@@ -1792,7 +1785,7 @@
             usbControlTransferParams, msOs20DescriptorSetLength,
             CONTROL_TRANSFER_TIMEOUT_MS);
 
-        this.checkDescriptorGetSuccess_(
+        this.checkTransferSuccess_(
             response.status,
             'Failed to read the Microsoft OS 2.0 descriptor set.');
       } catch (e) {
@@ -1834,7 +1827,7 @@
         const response = await this.usbDeviceProxy_.controlTransferOut(
             usbControlTransferParams, [], CONTROL_TRANSFER_TIMEOUT_MS);
 
-        this.checkDescriptorGetSuccess_(
+        this.checkTransferSuccess_(
             response.status,
             'Failed to read the Microsoft OS 2.0 ' +
                 'descriptor alternate enumeration set.');
@@ -2552,6 +2545,340 @@
 
       return offset;
     }
+
+    /**
+     * Gets response of the given request.
+     * @param {!device.mojom.UsbControlTransferParams} usbControlTransferParams
+     * @param {number} length
+     * @param {number} direction
+     * @private
+     */
+    async sendTestingRequest_(usbControlTransferParams, length, direction) {
+      try {
+        await this.usbDeviceProxy_.open();
+
+        if (direction === 'Device-to-Host') {
+          const response = await this.usbDeviceProxy_.controlTransferIn(
+              usbControlTransferParams, length, CONTROL_TRANSFER_TIMEOUT_MS);
+          this.checkTransferSuccess_(
+              response.status, 'Failed to send request.');
+          this.renderTestingData_(new Uint8Array(response.data));
+        } else if (direction === 'Host-to-Device') {
+          const dataString = this.rootElement_.querySelector('textarea').value;
+
+          const data = [];
+          for (let i = 0; i < dataString.length; i += 2) {
+            data.push(Number.parseInt(dataString.substring(i, i + 2), 16));
+          }
+
+          const response = await this.usbDeviceProxy_.controlTransferOut(
+              usbControlTransferParams, new Uint8Array(data),
+              CONTROL_TRANSFER_TIMEOUT_MS);
+          this.checkTransferSuccess_(
+              response.status, 'Failed to send request.');
+        }
+      } catch (e) {
+        this.showError_(e.message);
+        return;
+      } finally {
+        await this.usbDeviceProxy_.close();
+      }
+    }
+
+    /**
+     * Renders a view to display response data in hex format.
+     * @param {!Uint8Array} rawData
+     * @private
+     */
+    async renderTestingData_(rawData) {
+      const displayElement = this.addNewDescriptorDisplayElement_();
+      /** @type {!cr.ui.Tree} */
+      const rawDataTreeRoot = displayElement.rawDataTreeRoot;
+      rawDataTreeRoot.style.display = 'none';
+      /** @type {!HTMLElement} */
+      const rawDataByteElement = displayElement.rawDataByteElement;
+      renderRawDataBytes(rawDataByteElement, rawData);
+    }
+
+    /**
+     * Initializes the testing tool panel for input and query functionality.
+     */
+    initialTestingToolPanel() {
+      this.showWarn_(
+          'Warning: This tool can send arbitrary commands to the device. ' +
+          'Invalid commands may cause unexpected results.');
+      const inputTableRows =
+          this.rootElement_.querySelector('tbody').querySelectorAll('tr');
+      const buttons =
+          this.rootElement_.querySelector('tbody').querySelectorAll('button');
+      const dataInputArea = this.rootElement_.querySelector('textarea');
+      dataInputArea.addEventListener('keypress', () => {
+        const index = dataInputArea.selectionStart;
+        dataInputArea.value = dataInputArea.value.substring(0, index) +
+            dataInputArea.value.substring(index + 1);
+        dataInputArea.selectionEnd = index;
+      });
+
+      const testingToolPanelInputTypeSelector =
+          this.rootElement_.querySelector('#input-type');
+      testingToolPanelInputTypeSelector.addEventListener('change', () => {
+        this.clearView();
+        const index = testingToolPanelInputTypeSelector.selectedIndex;
+        inputTableRows.forEach(row => row.hidden = true);
+        inputTableRows[index].hidden = false;
+
+        const direction = getRequestTypeDirection(inputTableRows[index], index);
+        const length = getRequestLength(inputTableRows[index], index);
+        this.rootElement_.querySelector('#data-input-area').hidden =
+            (direction !== 'Host-to-Device');
+        dataInputArea.value = '00'.repeat(length);
+        dataInputArea.maxLength = length * 2;
+      });
+
+      for (const [i, inputTableRow] of inputTableRows.entries()) {
+        let directionInputElement;
+        switch (i) {
+          case INPUT_TYPE_DECIMAL_WITH_DROPDOWN:
+            directionInputElement =
+                inputTableRow.querySelector('#transfer-direction');
+            break;
+          case INPUT_TYPE_HEX_BYTE:
+            directionInputElement =
+                inputTableRow.querySelector('#query-request-type');
+            break;
+        }
+        directionInputElement.addEventListener('change', () => {
+          this.rootElement_.querySelector('#data-input-area').hidden =
+              (getRequestTypeDirection(inputTableRow, i) !== 'Host-to-Device');
+        });
+
+        inputTableRow.querySelector('#query-length')
+            .addEventListener('blur', () => {
+              const length = getRequestLength(inputTableRow, i);
+              dataInputArea.value = '00'.repeat(length);
+              dataInputArea.maxLength = length * 2;
+            });
+      }
+
+      for (const [i, button] of buttons.entries()) {
+        button.addEventListener('click', () => {
+          this.clearView();
+
+          const direction = getRequestTypeDirection(inputTableRows[i], i);
+          const type = getRequestType(inputTableRows[i], i);
+          const recipient = getRequestTypeRecipient(inputTableRows[i], i);
+          const request = getRequestCode(inputTableRows[i], i);
+          const value = getRequestValue(inputTableRows[i], i);
+          const index = getRequestIndex(inputTableRows[i], i);
+          const dataLength = getRequestLength(inputTableRows[i], i);
+
+          if (this.checkEnumParamValid_(
+                  type, 'Transfer Type', device.mojom.UsbControlTransferType) &&
+              this.checkEnumParamValid_(
+                  recipient, 'Transfer Recipient',
+                  device.mojom.UsbControlTransferRecipient) &&
+              this.checkParamValid_(request, 'Transfer Request', 0, 255) &&
+              this.checkParamValid_(value, 'wValue', 0, 65535) &&
+              this.checkParamValid_(index, 'wIndex', 0, 65535) &&
+              this.checkParamValid_(dataLength, 'Length', 0, 65535)) {
+            /** @type {!device.mojom.UsbControlTransferParams} */
+            const usbControlTransferParams = {
+              type: device.mojom.UsbControlTransferType[type],
+              recipient: device.mojom.UsbControlTransferRecipient[recipient],
+              request,
+              value,
+              index,
+            };
+            this.sendTestingRequest_(
+                usbControlTransferParams, dataLength, direction);
+          }
+        });
+      }
+    }
+
+    /**
+     * Checks if the user input is a valid number.
+     * @param {number} paramValue
+     * @param {string} paramName
+     * @param {number} min
+     * @param {number} max
+     * @return {boolean}
+     * @private
+     */
+    checkParamValid_(paramValue, paramName, min, max) {
+      if (Number.isNaN(paramValue) || paramValue < min || paramValue > max) {
+        this.showError_(`Invalid ${paramName}.`);
+        return false;
+      }
+      return true;
+    }
+
+    /**
+     * Checks if the user input for a enum field is valid.
+     * @param {string} enumString
+     * @param {string} paramName
+     * @param {!Object} enumObject
+     * @return {boolean}
+     * @private
+     */
+    checkEnumParamValid_(enumString, paramName, enumObject) {
+      if (enumObject[enumString] !== undefined) {
+        return true;
+      }
+      this.showError_(`Invalid ${paramName}`);
+      return false;
+    }
+  }
+
+  /**
+   * Get the USB control transfer type.
+   * @param {!HTMLElement} inputRow
+   * @param {number} inputType
+   * @return {string}
+   */
+  function getRequestType(inputRow, inputType) {
+    switch (inputType) {
+      case INPUT_TYPE_DECIMAL_WITH_DROPDOWN:
+        return inputRow.querySelector('#transfer-type').value;
+      case INPUT_TYPE_HEX_BYTE:
+        const value = Number.parseInt(
+            inputRow.querySelector('#query-request-type').value, 16);
+        switch (value >> 5 & 0x03) {
+          case 0:
+            return 'STANDARD';
+          case 1:
+            return 'CLASS';
+          case 2:
+            return 'VENDOR';
+        }
+      default:
+        return '';
+    }
+  }
+
+  /**
+   * Get the USB control transfer recipient.
+   * @param {!HTMLElement} inputRow
+   * @param {number} inputType
+   * @return {string}
+   */
+  function getRequestTypeRecipient(inputRow, inputType) {
+    switch (inputType) {
+      case INPUT_TYPE_DECIMAL_WITH_DROPDOWN:
+        return inputRow.querySelector('#transfer-recipient').value;
+      case INPUT_TYPE_HEX_BYTE:
+        const value = Number.parseInt(
+            inputRow.querySelector('#query-request-type').value, 16);
+        switch (value & 0x1F) {
+          case 0:
+            return 'DEVICE';
+          case 1:
+            return 'INTERFACE';
+          case 2:
+            return 'ENDPOINT';
+          case 3:
+            return 'OTHER';
+        }
+      default:
+        return '';
+    }
+  }
+
+  /**
+   * Get the USB control transfer direction. 0 for device-to-host, 1 for
+   * host-to-device.
+   * @param {!HTMLElement} inputRow
+   * @param {number} inputType
+   * @return {number}
+   */
+  function getRequestTypeDirection(inputRow, inputType) {
+    switch (inputType) {
+      case INPUT_TYPE_DECIMAL_WITH_DROPDOWN:
+        return inputRow.querySelector('#transfer-direction').value;
+      case INPUT_TYPE_HEX_BYTE:
+        const value = Number.parseInt(
+            inputRow.querySelector('#query-request-type').value, 16);
+        switch (value >> 7) {
+          case CONTROL_TRANSFER_DIRECTION_HOST_TO_DEVICE:
+            return 'Host-to-Device';
+          case CONTROL_TRANSFER_DIRECTION_DEVICE_TO_HOST:
+            return 'Device-to-Host';
+        }
+      default:
+        return 'Device-to-Host';
+    }
+  }
+
+  /**
+   * Get the USB control transfer request code.
+   * @param {!HTMLElement} inputRow
+   * @param {number} inputType
+   * @return {number}
+   */
+  function getRequestCode(inputRow, inputType) {
+    switch (inputType) {
+      case INPUT_TYPE_DECIMAL_WITH_DROPDOWN:
+        return Number.parseInt(inputRow.querySelector('#query-request').value);
+      case INPUT_TYPE_HEX_BYTE:
+        return Number.parseInt(
+            inputRow.querySelector('#query-request').value, 16);
+      default:
+        return Number.NaN;
+    }
+  }
+
+  /**
+   * Get the value of USB control transfer request wValue field.
+   * @param {!HTMLElement} inputRow
+   * @param {number} inputType
+   * @return {number}
+   */
+  function getRequestValue(inputRow, inputType) {
+    switch (inputType) {
+      case INPUT_TYPE_DECIMAL_WITH_DROPDOWN:
+        return Number.parseInt(inputRow.querySelector('#query-value').value);
+      case INPUT_TYPE_HEX_BYTE:
+        return Number.parseInt(
+            inputRow.querySelector('#query-value').value, 16);
+      default:
+        return Number.NaN;
+    }
+  }
+
+  /**
+   * Get the value of USB control transfer request wIndex field.
+   * @param {!HTMLElement} inputRow
+   * @param {number} inputType
+   * @return {number}
+   */
+  function getRequestIndex(inputRow, inputType) {
+    switch (inputType) {
+      case INPUT_TYPE_DECIMAL_WITH_DROPDOWN:
+        return Number.parseInt(inputRow.querySelector('#query-index').value);
+      case INPUT_TYPE_HEX_BYTE:
+        return Number.parseInt(
+            inputRow.querySelector('#query-index').value, 16);
+      default:
+        return Number.NaN;
+    }
+  }
+
+  /**
+   * Get the length of the data transferred during USB control transfer.
+   * @param {!HTMLElement} inputRow
+   * @param {number} inputType
+   * @return {number}
+   */
+  function getRequestLength(inputRow, inputType) {
+    switch (inputType) {
+      case INPUT_TYPE_DECIMAL_WITH_DROPDOWN:
+        return Number.parseInt(inputRow.querySelector('#query-length').value);
+      case INPUT_TYPE_HEX_BYTE:
+        return Number.parseInt(
+            inputRow.querySelector('#query-length').value, 16);
+      default:
+        return Number.NaN;
+    }
   }
 
   /**
diff --git a/chrome/browser/resources/usb_internals/devices_page.js b/chrome/browser/resources/usb_internals/devices_page.js
index f6196e0..84cf946 100644
--- a/chrome/browser/resources/usb_internals/devices_page.js
+++ b/chrome/browser/resources/usb_internals/devices_page.js
@@ -225,6 +225,19 @@
           bosDescriptorPanel.renderBosDescriptor();
         }
       });
+
+      const testingToolPanelButton =
+          tabPanel.querySelector('#testing-tool-button');
+      const testingToolElement = tabPanel.querySelector('.testing-tool-panel');
+      const testingToolPanel = new descriptor_panel.DescriptorPanel(
+          usbDeviceProxy, testingToolElement);
+      testingToolPanel.initialTestingToolPanel();
+      testingToolPanelButton.addEventListener('click', () => {
+        testingToolElement.hidden = !testingToolElement.hidden;
+        // Clear the panel before rendering new data.
+        testingToolPanel.clearView();
+      });
+
       // window.deviceTabInitializedFn() provides a hook for the test suite to
       // perform test actions after the device tab query descriptors actions are
       // initialized.
diff --git a/chrome/browser/resources/usb_internals/usb_internals.css b/chrome/browser/resources/usb_internals/usb_internals.css
index 393e91fe..670b0eb 100644
--- a/chrome/browser/resources/usb_internals/usb_internals.css
+++ b/chrome/browser/resources/usb_internals/usb_internals.css
@@ -134,9 +134,29 @@
   margin-inline-start: 16px;
 }
 
+#input-type {
+  display: block;
+}
+
+#testing-tool tr td select {
+  display: block;
+  width: 100%;
+}
+
+textarea {
+  display: block;
+  font-family: monospace;
+}
+
 error {
   color: red;
   display: block;
   font-size: 16px;
   padding-inline-start: 20px;
+}
+
+warn {
+  color: red;
+  display: block;
+  font-size: 24px;
 }
\ No newline at end of file
diff --git a/chrome/browser/resources/usb_internals/usb_internals.html b/chrome/browser/resources/usb_internals/usb_internals.html
index 1450f51..80cbf6a9 100644
--- a/chrome/browser/resources/usb_internals/usb_internals.html
+++ b/chrome/browser/resources/usb_internals/usb_internals.html
@@ -166,6 +166,76 @@
         </button>
       </div>
       <div class="bos-descriptor-panel" hidden></div>
+      <div class="descriptor-button">
+        <button id="testing-tool-button">Testing Tool Panel</button>
+      </div>
+      <div class="testing-tool-panel" hidden>
+        <select id="input-type">
+          <option label="Decimal with Dropdown Menu"></option>
+          <option label="Hex Bytes"></option>
+        </select>
+        <table class="styled-table">
+          <thead>
+            <tr>
+              <th>bmRequestType</th>
+              <th>bRequest</th>
+              <th>wValue</th>
+              <th>wIndex</th>
+              <th>wLength</th>
+            </tr>
+          </thead>
+          <tbody id="testing-tool">
+            <tr>
+              <td>
+                <select id="transfer-direction">
+                  <option label="Host-to-Device" value="Host-to-Device">
+                  </option>
+                  <option label="Device-to-Host" value="Device-to-Host">
+                  </option>
+                </select>
+                <select id="transfer-type">
+                  <option label="Standard" value="STANDARD"></option>
+                  <option label="Class" value="CLASS"></option>
+                  <option label="Vendor" value="VENDOR"></option>
+                </select>
+                <select id="transfer-recipient">
+                  <option label="Device" value="DEVICE"></option>
+                  <option label="Interface" value="INTERFACE"></option>
+                  <option label="Endpoint" value="ENDPOINT"></option>
+                  <option label="Other" value="OTHER"></option>
+                </select>
+              </td>
+              <td><input id="query-request" type="number" value="0"></td>
+              <td><input id="query-value" type="number" value="0"></td>
+              <td><input id="query-index" type="number" value="0"></td>
+              <td><input id="query-length" type="number" value="0"></td>
+              <td><button>Send</button></td>
+            </tr>
+            <tr hidden>
+              <td>
+                0x<input id="query-request-type" type="number" placeholder="00">
+              </td>
+              <td>
+                0x<input id="query-request" type="number" placeholder="00">
+              </td>
+              <td>
+                0x<input id="query-value" type="number" placeholder="0000">
+              </td>
+              <td>
+                0x<input id="query-index" type="number" placeholder="0000">
+              </td>
+              <td>
+                0x<input id="query-length" type="number" placeholder="0000">
+              </td>
+              <td><button>Send</button></td>
+            </tr>
+          </tbody>
+        </table>
+        <div id='data-input-area'>
+          Data (in Hex):
+          <textarea cols="31"></textarea>
+        </div>
+      </div>
     </tabpanel>
   </template>
 
@@ -192,6 +262,10 @@
     <error></error>
   </template>
 
+  <template id="warn">
+    <warn></warn>
+  </template>
+
   <template id="descriptor-panel-title">
     <descriptorpaneltitle></descriptorpaneltitle>
   </template>
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
index 0f0ba6a..bbbab343 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
@@ -429,8 +429,7 @@
 
 struct BookmarkBarView::DropLocation {
   DropLocation()
-      : index(-1),
-        operation(ui::DragDropTypes::DRAG_NONE),
+      : operation(ui::DragDropTypes::DRAG_NONE),
         on(false),
         button_type(DROP_BOOKMARK) {}
 
@@ -440,7 +439,7 @@
   }
 
   // Index into the model the drop is over. This is relative to the root node.
-  int index;
+  base::Optional<size_t> index;
 
   // Drop constants.
   int operation;
@@ -634,8 +633,8 @@
   }
 
   // Then check the bookmark buttons.
-  for (int i = 0; i < GetBookmarkButtonCount(); ++i) {
-    views::View* child = GetBookmarkButton(i);
+  for (size_t i = 0; i < bookmark_buttons_.size(); ++i) {
+    views::View* child = bookmark_buttons_[i];
     if (!child->GetVisible())
       break;
     if (child->bounds().Contains(adjusted_loc))
@@ -669,7 +668,7 @@
   int index = model_->bookmark_bar_node()->GetIndexOf(node);
   if (index == -1 || !node->is_folder())
     return nullptr;
-  return static_cast<views::MenuButton*>(GetBookmarkButton(index));
+  return static_cast<views::MenuButton*>(bookmark_buttons_[size_t{index}]);
 }
 
 void BookmarkBarView::GetAnchorPositionForButton(
@@ -844,19 +843,21 @@
   if (model_->loaded() &&
       model_->bookmark_bar_node()->child_count() > 0) {
     bool last_visible = x < max_x;
-    int button_count = GetBookmarkButtonCount();
-    for (int i = 0; i <= button_count; ++i) {
+    size_t button_count = bookmark_buttons_.size();
+    for (size_t i = 0; i <= button_count; ++i) {
       if (i == button_count) {
         // Add another button if there is room for it (and there is another
         // button to load).
         if (!last_visible || !model_->loaded() ||
-            model_->bookmark_bar_node()->child_count() <= button_count)
+            model_->bookmark_bar_node()->children().size() <= button_count)
           break;
         InsertBookmarkButtonAtIndex(
-            CreateBookmarkButton(model_->bookmark_bar_node()->GetChild(i)), i);
-        button_count = GetBookmarkButtonCount();
+            CreateBookmarkButton(
+                model_->bookmark_bar_node()->children()[i].get()),
+            i);
+        button_count = bookmark_buttons_.size();
       }
-      views::View* child = GetBookmarkButton(i);
+      views::View* child = bookmark_buttons_[i];
       gfx::Size pref = child->GetPreferredSize();
       int next_x = x + pref.width() + bookmark_bar_button_padding;
       last_visible = next_x < max_x;
@@ -875,9 +876,9 @@
   overflow_button_->SetBounds(x, y, overflow_pref.width(), height);
   const bool show_overflow =
       model_->loaded() &&
-      (model_->bookmark_bar_node()->child_count() > GetBookmarkButtonCount() ||
-       (GetBookmarkButtonCount() > 0 &&
-        !GetBookmarkButton(GetBookmarkButtonCount() - 1)->GetVisible()));
+      (model_->bookmark_bar_node()->children().size() >
+           bookmark_buttons_.size() ||
+       (!bookmark_buttons_.empty() && !bookmark_buttons_.back()->GetVisible()));
   overflow_button_->SetVisible(show_overflow);
   x += overflow_pref.width();
 
@@ -918,17 +919,18 @@
   View::PaintChildren(paint_info);
 
   if (drop_info_.get() && drop_info_->valid &&
-      drop_info_->location.operation != 0 && drop_info_->location.index != -1 &&
+      drop_info_->location.operation != 0 &&
+      drop_info_->location.index.has_value() &&
       drop_info_->location.button_type != DROP_OVERFLOW &&
       !drop_info_->location.on) {
-    int index = drop_info_->location.index;
-    DCHECK(index <= GetBookmarkButtonCount());
+    size_t index = drop_info_->location.index.value();
+    DCHECK_LE(index, bookmark_buttons_.size());
     int x = 0;
     int y = 0;
     int h = height();
-    if (index == GetBookmarkButtonCount()) {
+    if (index == bookmark_buttons_.size()) {
       if (index != 0)
-        x = GetBookmarkButton(index - 1)->bounds().right();
+        x = bookmark_buttons_[index - 1]->bounds().right();
       else if (managed_bookmarks_button_->GetVisible())
         x = managed_bookmarks_button_->bounds().right();
       else if (apps_page_shortcut_->GetVisible())
@@ -936,11 +938,11 @@
       else
         x = kBookmarkBarHorizontalMargin;
     } else {
-      x = GetBookmarkButton(index)->x();
+      x = bookmark_buttons_[index]->x();
     }
-    if (GetBookmarkButtonCount() > 0 && GetBookmarkButton(0)->GetVisible()) {
-      y = GetBookmarkButton(0)->y();
-      h = GetBookmarkButton(0)->height();
+    if (!bookmark_buttons_.empty() && bookmark_buttons_.front()->GetVisible()) {
+      y = bookmark_buttons_.front()->y();
+      h = bookmark_buttons_.front()->height();
     }
 
     // Since the drop indicator is painted directly onto the canvas, we must
@@ -1025,12 +1027,14 @@
   if (location.on || location.button_type == DROP_OVERFLOW ||
       location.button_type == DROP_OTHER_FOLDER) {
     const BookmarkNode* node;
-    if (location.button_type == DROP_OTHER_FOLDER)
+    if (location.button_type == DROP_OTHER_FOLDER) {
       node = model_->other_node();
-    else if (location.button_type == DROP_OVERFLOW)
+    } else if (location.button_type == DROP_OVERFLOW) {
       node = model_->bookmark_bar_node();
-    else
-      node = model_->bookmark_bar_node()->GetChild(location.index);
+    } else {
+      node =
+          model_->bookmark_bar_node()->children()[location.index.value()].get();
+    }
     StartShowFolderDropMenuTimer(node);
   }
 
@@ -1045,7 +1049,7 @@
 
   drop_info_->valid = false;
 
-  if (drop_info_->location.index != -1) {
+  if (drop_info_->location.index.has_value()) {
     // TODO(sky): optimize the paint region.
     SchedulePaint();
   }
@@ -1065,21 +1069,23 @@
       (drop_info_->location.button_type == DROP_OTHER_FOLDER)
           ? model_->other_node()
           : model_->bookmark_bar_node();
-  int index = drop_info_->location.index;
 
-  if (index != -1) {
+  if (drop_info_->location.index.has_value()) {
     // TODO(sky): optimize the SchedulePaint region.
     SchedulePaint();
   }
+
   const BookmarkNode* parent_node;
+  int index;
   if (drop_info_->location.button_type == DROP_OTHER_FOLDER) {
     parent_node = root;
     index = parent_node->child_count();
   } else if (drop_info_->location.on) {
-    parent_node = root->GetChild(index);
+    parent_node = root->children()[drop_info_->location.index.value()].get();
     index = parent_node->child_count();
   } else {
     parent_node = root;
+    index = int{drop_info_->location.index.value()};
   }
   const BookmarkNodeData data = drop_info_->data;
   DCHECK(data.is_valid());
@@ -1151,7 +1157,7 @@
   // There should be no buttons. If non-zero it means Load was invoked more than
   // once, or we didn't properly clear things. Either of which shouldn't happen.
   // The actual bookmark buttons are added from Layout().
-  DCHECK_EQ(0, GetBookmarkButtonCount());
+  DCHECK(bookmark_buttons_.empty());
   DCHECK(model->other_node());
   other_bookmarks_button_->SetAccessibleName(model->other_node()->GetTitle());
   other_bookmarks_button_->SetText(model->other_node()->GetTitle());
@@ -1186,7 +1192,7 @@
       BookmarkNodeRemovedImpl(model, old_parent, old_index);
   if (BookmarkNodeAddedImpl(model, new_parent, new_index))
     needs_layout_and_paint = true;
-  if (was_throbbing && new_index < GetBookmarkButtonCount())
+  if (was_throbbing && new_index < int{bookmark_buttons_.size()})
     StartThrobbing(new_parent->GetChild(new_index), false);
   if (needs_layout_and_paint)
     LayoutAndPaint();
@@ -1244,7 +1250,7 @@
   bookmark_buttons_.clear();
 
   // Create the new buttons.
-  for (int i = 0, child_count = node->child_count(); i < child_count; ++i)
+  for (int i = 0; i < node->child_count(); ++i)
     InsertBookmarkButtonAtIndex(CreateBookmarkButton(node->GetChild(i)), i);
 
   LayoutAndPaint();
@@ -1260,28 +1266,22 @@
                                            ui::OSExchangeData* data) {
   base::RecordAction(UserMetricsAction("BookmarkBar_DragButton"));
 
-  for (int i = 0; i < GetBookmarkButtonCount(); ++i) {
-    if (sender == GetBookmarkButton(i)) {
-      const BookmarkNode* node = model_->bookmark_bar_node()->GetChild(i);
-      gfx::ImageSkia icon;
-      views::Widget* widget = sender->GetWidget();
-      if (node->is_url()) {
-        const gfx::Image& image = model_->GetFavicon(node);
-        icon = image.IsEmpty() ? favicon::GetDefaultFavicon().AsImageSkia()
-                               : image.AsImageSkia();
-      } else {
-        icon = chrome::GetBookmarkFolderIcon(
-            widget->GetNativeTheme()->GetSystemColor(
-                ui::NativeTheme::kColorId_LabelEnabledColor));
-      }
-
-      button_drag_utils::SetDragImage(node->url(), node->GetTitle(), icon,
-                                      &press_pt, *widget, data);
-      WriteBookmarkDragData(node, data);
-      return;
-    }
+  const auto* node = GetNodeForSender(sender);
+  gfx::ImageSkia icon;
+  views::Widget* widget = sender->GetWidget();
+  if (node->is_url()) {
+    const gfx::Image& image = model_->GetFavicon(node);
+    icon = image.IsEmpty() ? favicon::GetDefaultFavicon().AsImageSkia()
+                           : image.AsImageSkia();
+  } else {
+    icon =
+        chrome::GetBookmarkFolderIcon(widget->GetNativeTheme()->GetSystemColor(
+            ui::NativeTheme::kColorId_LabelEnabledColor));
   }
-  NOTREACHED();
+
+  button_drag_utils::SetDragImage(node->url(), node->GetTitle(), icon,
+                                  &press_pt, *widget, data);
+  WriteBookmarkDragData(node, data);
 }
 
 int BookmarkBarView::GetDragOperationsForView(View* sender,
@@ -1295,14 +1295,8 @@
     return ui::DragDropTypes::DRAG_NONE;
   }
 
-  for (int i = 0; i < GetBookmarkButtonCount(); ++i) {
-    if (sender == GetBookmarkButton(i)) {
-      return chrome::GetBookmarkDragOperation(
-          browser_->profile(), model_->bookmark_bar_node()->GetChild(i));
-    }
-  }
-  NOTREACHED();
-  return ui::DragDropTypes::DRAG_NONE;
+  return chrome::GetBookmarkDragOperation(browser_->profile(),
+                                          GetNodeForSender(sender));
 }
 
 bool BookmarkBarView::CanStartDragForView(views::View* sender,
@@ -1313,18 +1307,11 @@
   gfx::Vector2d move_offset = p - press_pt;
   gfx::Vector2d horizontal_offset(move_offset.x(), 0);
   if (!View::ExceededDragThreshold(horizontal_offset) && move_offset.y() > 0) {
-    for (int i = 0; i < GetBookmarkButtonCount(); ++i) {
-      if (sender == GetBookmarkButton(i)) {
-        const BookmarkNode* node = model_->bookmark_bar_node()->GetChild(i);
-        // If the folder button was dragged, show the menu instead.
-        if (node && node->is_folder()) {
-          views::MenuButton* menu_button =
-              static_cast<views::MenuButton*>(sender);
-          menu_button->Activate(nullptr);
-          return false;
-        }
-        break;
-      }
+    // If the folder button was dragged, show the menu instead.
+    const auto* node = GetNodeForSender(sender);
+    if (node->is_folder()) {
+      static_cast<views::MenuButton*>(sender)->Activate(nullptr);
+      return false;
     }
   }
   return true;
@@ -1420,8 +1407,8 @@
     // clicked on, except for the apps page shortcut, which must behave as if
     // the user clicked on the bookmark bar background.
     int bookmark_button_index = GetIndexForButton(source);
-    DCHECK(bookmark_button_index != -1 &&
-           bookmark_button_index < GetBookmarkButtonCount());
+    DCHECK_NE(-1, bookmark_button_index);
+    DCHECK_LT(size_t{bookmark_button_index}, bookmark_buttons_.size());
     const BookmarkNode* node =
         model_->bookmark_bar_node()->GetChild(bookmark_button_index);
     nodes.push_back(node);
@@ -1494,27 +1481,15 @@
   }
 }
 
-int BookmarkBarView::GetBookmarkButtonCount() const {
-  return bookmark_buttons_.size();
-}
-
-views::LabelButton* BookmarkBarView::GetBookmarkButton(int index) {
-  // CHECK as otherwise we may do the wrong cast.
-  CHECK(index >= 0 && index < GetBookmarkButtonCount());
-  return bookmark_buttons_[index];
-}
-
 BookmarkLaunchLocation BookmarkBarView::GetBookmarkLaunchLocation() const {
   return BOOKMARK_LAUNCH_LOCATION_ATTACHED_BAR;
 }
 
 int BookmarkBarView::GetFirstHiddenNodeIndex() {
-  const int bb_count = GetBookmarkButtonCount();
-  for (int i = 0; i < bb_count; ++i) {
-    if (!GetBookmarkButton(i)->GetVisible())
-      return i;
-  }
-  return bb_count;
+  const auto i =
+      std::find_if(bookmark_buttons_.cbegin(), bookmark_buttons_.cend(),
+                   [](const auto* button) { return !button->GetVisible(); });
+  return i - bookmark_buttons_.cbegin();
 }
 
 MenuButton* BookmarkBarView::CreateOtherBookmarksButton() {
@@ -1637,7 +1612,7 @@
   const bool needs_layout_and_paint = UpdateOtherAndManagedButtonsVisibility();
   if (parent != model->bookmark_bar_node())
     return needs_layout_and_paint;
-  if (index < GetBookmarkButtonCount()) {
+  if (index < int{bookmark_buttons_.size()}) {
     const BookmarkNode* node = parent->GetChild(index);
     InsertBookmarkButtonAtIndex(CreateBookmarkButton(node), index);
     return true;
@@ -1645,7 +1620,7 @@
   // If the new node was added after the last button we've created we may be
   // able to fit it. Assume we can by returning true, which forces a Layout()
   // and creation of the button (if it fits).
-  return index == GetBookmarkButtonCount();
+  return index == int{bookmark_buttons_.size()};
 }
 
 bool BookmarkBarView::BookmarkNodeRemovedImpl(BookmarkModel* model,
@@ -1661,10 +1636,10 @@
     // Only children of the bookmark_bar_node get buttons.
     return needs_layout;
   }
-  if (index >= GetBookmarkButtonCount())
+  if (size_t{index} >= bookmark_buttons_.size())
     return needs_layout;
 
-  views::LabelButton* button = GetBookmarkButton(index);
+  views::LabelButton* button = bookmark_buttons_[size_t{index}];
   bookmark_buttons_.erase(bookmark_buttons_.cbegin() + index);
   delete button;
   return true;
@@ -1686,9 +1661,9 @@
   }
   int index = model->bookmark_bar_node()->GetIndexOf(node);
   DCHECK_NE(-1, index);
-  if (index >= GetBookmarkButtonCount())
+  if (size_t{index} >= bookmark_buttons_.size())
     return;  // Buttons are created as needed.
-  views::LabelButton* button = GetBookmarkButton(index);
+  views::LabelButton* button = bookmark_buttons_[size_t{index}];
   const int old_pref_width = button->GetPreferredSize().width();
   ConfigureButton(node, button);
   if (old_pref_width != button->GetPreferredSize().width())
@@ -1766,7 +1741,7 @@
     location->button_type = DROP_OTHER_FOLDER;
     location->on = true;
     found = true;
-  } else if (!GetBookmarkButtonCount()) {
+  } else if (bookmark_buttons_.empty()) {
     // No bookmarks, accept the drop.
     location->index = 0;
     const BookmarkNode* node = data.GetFirstNode(model_, profile->GetPath());
@@ -1778,10 +1753,10 @@
     return;
   }
 
-  for (int i = 0; i < GetBookmarkButtonCount() &&
-                  GetBookmarkButton(i)->GetVisible() && !found;
+  for (size_t i = 0; i < bookmark_buttons_.size() &&
+                     bookmark_buttons_[i]->GetVisible() && !found;
        i++) {
-    views::LabelButton* button = GetBookmarkButton(i);
+    views::LabelButton* button = bookmark_buttons_[i];
     int button_x = mirrored_x - button->x();
     int button_w = button->width();
     if (button_x < button_w) {
@@ -1832,10 +1807,11 @@
   }
 
   if (location->on) {
-    const BookmarkNode* parent =
-        (location->button_type == DROP_OTHER_FOLDER)
-            ? model_->other_node()
-            : model_->bookmark_bar_node()->GetChild(location->index);
+    const BookmarkNode* parent = (location->button_type == DROP_OTHER_FOLDER)
+                                     ? model_->other_node()
+                                     : model_->bookmark_bar_node()
+                                           ->children()[location->index.value()]
+                                           .get();
     location->operation = chrome::GetBookmarkDropOperation(
         profile, event, data, parent, parent->child_count());
     if (!location->operation && !data.has_single_url() &&
@@ -1845,10 +1821,19 @@
     }
   } else {
     location->operation = chrome::GetBookmarkDropOperation(
-        profile, event, data, model_->bookmark_bar_node(), location->index);
+        profile, event, data, model_->bookmark_bar_node(),
+        location->index.value());
   }
 }
 
+const BookmarkNode* BookmarkBarView::GetNodeForSender(View* sender) const {
+  const auto i =
+      std::find(bookmark_buttons_.cbegin(), bookmark_buttons_.cend(), sender);
+  DCHECK(i != bookmark_buttons_.cend());
+  size_t child = i - bookmark_buttons_.cbegin();
+  return model_->bookmark_bar_node()->children()[child].get();
+}
+
 void BookmarkBarView::WriteBookmarkDragData(const BookmarkNode* node,
                                             ui::OSExchangeData* data) {
   DCHECK(node && data);
@@ -1876,7 +1861,7 @@
       // Node is hidden, animate the overflow button.
       throbbing_view_ = overflow_button_;
     } else if (!overflow_only) {
-      throbbing_view_ = static_cast<Button*>(GetBookmarkButton(index));
+      throbbing_view_ = static_cast<Button*>(bookmark_buttons_[size_t{index}]);
     }
   } else if (bookmarks::IsDescendantOf(node, managed_->managed_node())) {
     throbbing_view_ = managed_bookmarks_button_;
@@ -1908,7 +1893,7 @@
       // Node is hidden, animate the overflow button.
       return overflow_button_;
     }
-    return static_cast<Button*>(GetBookmarkButton(old_index_on_bb));
+    return static_cast<Button*>(bookmark_buttons_[size_t{old_index_on_bb}]);
   }
   if (bookmarks::IsDescendantOf(parent, managed_->managed_node()))
     return managed_bookmarks_button_;
@@ -1921,9 +1906,9 @@
   const ui::ThemeProvider* theme_provider = GetThemeProvider();
   if (!theme_provider)
     return;
-  for (int i = 0; i < GetBookmarkButtonCount(); ++i) {
-    ConfigureButton(model_->bookmark_bar_node()->GetChild(i),
-                    GetBookmarkButton(i));
+  for (size_t i = 0; i < bookmark_buttons_.size(); ++i) {
+    ConfigureButton(model_->bookmark_bar_node()->children()[i].get(),
+                    bookmark_buttons_[i]);
   }
 
   const SkColor color = GetBookmarkBarTextColor();
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.h b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.h
index b25c170f..c596fbe 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.h
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.h
@@ -253,23 +253,13 @@
   // calculating the preferred height.
   void Init();
 
-  // NOTE: unless otherwise stated all methods that take an int for an index are
-  // in terms of the bookmark bar view. Typically the view index and model index
-  // are the same, but they may differ during animations or drag and drop.
+  // NOTE: unless otherwise stated all methods that take an index are in terms
+  // of the bookmark bar view. Typically the view index and model index are the
+  // same, but they may differ during animations or drag and drop.
   //
   // It's easy to get the mapping wrong. For this reason all these methods are
   // private.
 
-  // Returns the number of bookmark bar url/folder buttons that have been
-  // created. This does not necessarily represent the number of bookmark bar
-  // nodes, nor the number of visible bookmark bar buttons. Buttons are created
-  // lazily to fill available space, and may be hidden for ordering or sizing
-  // changes.
-  int GetBookmarkButtonCount() const;
-
-  // Returns the button at the specified index.
-  views::LabelButton* GetBookmarkButton(int index);
-
   // Returns BOOKMARK_LAUNCH_LOCATION_DETACHED_BAR or
   // BOOKMARK_LAUNCH_LOCATION_ATTACHED_BAR based on detached node_data.
   BookmarkLaunchLocation GetBookmarkLaunchLocation() const;
@@ -329,6 +319,10 @@
                              const bookmarks::BookmarkNodeData& data,
                              DropLocation* location);
 
+  // Returns the node corresponding to |sender|, which is one of the
+  // |bookmark_buttons_|.
+  const bookmarks::BookmarkNode* GetNodeForSender(View* sender) const;
+
   // Writes a BookmarkNodeData for node to data.
   void WriteBookmarkDragData(const bookmarks::BookmarkNode* node,
                              ui::OSExchangeData* data);
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc
index 52d1af1..ae02766 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc
@@ -368,12 +368,12 @@
       size.set_width(size.width() - 25);
       bb_view_->SetBounds(0, 0, size.width(), size.height());
       bb_view_->Layout();
-    } while (bb_view_->GetBookmarkButton(6)->GetVisible());
+    } while (bb_view_->bookmark_buttons_[6]->GetVisible());
     return size;
   }
 
-  views::LabelButton* GetBookmarkButton(int view_index) {
-    return bb_view_->GetBookmarkButton(view_index);
+  views::LabelButton* GetBookmarkButton(size_t view_index) {
+    return bb_view_->bookmark_buttons_[view_index];
   }
 
   // See comment above class description for what this does.
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test_helper.h b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test_helper.h
index 8420c51c..060645d 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test_helper.h
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test_helper.h
@@ -14,10 +14,10 @@
   explicit BookmarkBarViewTestHelper(BookmarkBarView* bbv) : bbv_(bbv) {}
   ~BookmarkBarViewTestHelper() {}
 
-  int GetBookmarkButtonCount() { return bbv_->GetBookmarkButtonCount(); }
+  size_t GetBookmarkButtonCount() { return bbv_->bookmark_buttons_.size(); }
 
-  views::LabelButton* GetBookmarkButton(int index) {
-    return bbv_->GetBookmarkButton(index);
+  views::LabelButton* GetBookmarkButton(size_t index) {
+    return bbv_->bookmark_buttons_[index];
   }
 
   views::LabelButton* apps_page_shortcut() { return bbv_->apps_page_shortcut_; }
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_unittest.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_unittest.cc
index 26161f6b..e6d5547 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_unittest.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_unittest.cc
@@ -54,8 +54,8 @@
   // the bookmark bar. Each label is separated by a space.
   std::string GetStringForVisibleButtons() {
     std::string result;
-    for (int i = 0; i < test_helper_->GetBookmarkButtonCount() &&
-                    test_helper_->GetBookmarkButton(i)->GetVisible();
+    for (size_t i = 0; i < test_helper_->GetBookmarkButtonCount() &&
+                       test_helper_->GetBookmarkButton(i)->GetVisible();
          ++i) {
       if (i != 0)
         result += " ";
@@ -69,7 +69,7 @@
   // visible.
   // NOTE: if the model has more than |count| buttons this results in
   // |count| + 1 buttons.
-  void SizeUntilButtonsVisible(int count) {
+  void SizeUntilButtonsVisible(size_t count) {
     const int start_width = bookmark_bar_view_->width();
     const int height = bookmark_bar_view_->GetPreferredSize().height();
     for (int i = 0;
@@ -173,7 +173,7 @@
   EXPECT_TRUE(test_helper_->overflow_button()->GetVisible());
 
   SizeUntilButtonsVisible(1);
-  EXPECT_EQ(2, test_helper_->GetBookmarkButtonCount());
+  EXPECT_EQ(2u, test_helper_->GetBookmarkButtonCount());
   const int width_for_one = bookmark_bar_view_->bounds().width();
   EXPECT_TRUE(test_helper_->overflow_button()->GetVisible());
 
@@ -181,7 +181,7 @@
   bookmark_bar_view_->SetBounds(
       0, 0, 5000, bookmark_bar_view_->bounds().height());
   bookmark_bar_view_->Layout();
-  EXPECT_EQ(6, test_helper_->GetBookmarkButtonCount());
+  EXPECT_EQ(6u, test_helper_->GetBookmarkButtonCount());
   EXPECT_FALSE(test_helper_->overflow_button()->GetVisible());
 
   bookmark_bar_view_->SetBounds(
@@ -198,21 +198,21 @@
   EXPECT_TRUE(BookmarkModelFactory::GetForBrowserContext(profile())->loaded());
   AddNodesToBookmarkBarFromModelString("a b c d e f ");
   CreateBookmarkBarView();
-  EXPECT_EQ(0, test_helper_->GetBookmarkButtonCount());
+  EXPECT_EQ(0u, test_helper_->GetBookmarkButtonCount());
 
   SizeUntilButtonsVisible(1);
-  EXPECT_EQ(2, test_helper_->GetBookmarkButtonCount());
+  EXPECT_EQ(2u, test_helper_->GetBookmarkButtonCount());
 
   // Go really big, which should force all buttons to be added.
   bookmark_bar_view_->SetBounds(
       0, 0, 5000, bookmark_bar_view_->bounds().height());
   bookmark_bar_view_->Layout();
-  EXPECT_EQ(6, test_helper_->GetBookmarkButtonCount());
+  EXPECT_EQ(6u, test_helper_->GetBookmarkButtonCount());
 
   // Ensure buttons were added in the correct place.
   auto button_iter =
       bookmark_bar_view_->FindChild(test_helper_->managed_bookmarks_button());
-  for (int i = 0; i < test_helper_->GetBookmarkButtonCount(); ++i) {
+  for (size_t i = 0; i < test_helper_->GetBookmarkButtonCount(); ++i) {
     ++button_iter;
     ASSERT_NE(bookmark_bar_view_->children().cend(), button_iter);
     EXPECT_EQ(test_helper_->GetBookmarkButton(i), *button_iter);
@@ -224,19 +224,19 @@
   CreateBookmarkModelAndBookmarkBarView();
   EXPECT_TRUE(BookmarkModelFactory::GetForBrowserContext(profile())->loaded());
   AddNodesToBookmarkBarFromModelString("a b c d e f ");
-  EXPECT_EQ(0, test_helper_->GetBookmarkButtonCount());
+  EXPECT_EQ(0u, test_helper_->GetBookmarkButtonCount());
   SizeUntilButtonsVisible(1);
-  EXPECT_EQ(2, test_helper_->GetBookmarkButtonCount());
+  EXPECT_EQ(2u, test_helper_->GetBookmarkButtonCount());
 
   // Go really big, which should force all buttons to be added.
   bookmark_bar_view_->SetBounds(
       0, 0, 5000, bookmark_bar_view_->bounds().height());
   bookmark_bar_view_->Layout();
-  EXPECT_EQ(6, test_helper_->GetBookmarkButtonCount());
+  EXPECT_EQ(6u, test_helper_->GetBookmarkButtonCount());
   // Ensure buttons were added in the correct place.
   auto button_iter =
       bookmark_bar_view_->FindChild(test_helper_->managed_bookmarks_button());
-  for (int i = 0; i < test_helper_->GetBookmarkButtonCount(); ++i) {
+  for (size_t i = 0; i < test_helper_->GetBookmarkButtonCount(); ++i) {
     ++button_iter;
     ASSERT_NE(bookmark_bar_view_->children().cend(), button_iter);
     EXPECT_EQ(test_helper_->GetBookmarkButton(i), *button_iter);
@@ -258,9 +258,9 @@
   BookmarkModel* model = BookmarkModelFactory::GetForBrowserContext(profile());
   const BookmarkNode* bookmark_bar_node = model->bookmark_bar_node();
   AddNodesToBookmarkBarFromModelString("a b c d e f ");
-  EXPECT_EQ(0, test_helper_->GetBookmarkButtonCount());
+  EXPECT_EQ(0u, test_helper_->GetBookmarkButtonCount());
   SizeUntilButtonsVisible(1);
-  EXPECT_EQ(2, test_helper_->GetBookmarkButtonCount());
+  EXPECT_EQ(2u, test_helper_->GetBookmarkButtonCount());
 
   // Remove the 2nd node, should still only have 1 visible.
   model->Remove(bookmark_bar_node->GetChild(1));
@@ -277,11 +277,11 @@
   BookmarkModel* model = BookmarkModelFactory::GetForBrowserContext(profile());
   const BookmarkNode* bookmark_bar_node = model->bookmark_bar_node();
   AddNodesToBookmarkBarFromModelString("a b c d e f ");
-  EXPECT_EQ(0, test_helper_->GetBookmarkButtonCount());
+  EXPECT_EQ(0u, test_helper_->GetBookmarkButtonCount());
 
   // Move 'c' first resulting in 'c a b d e f'.
   model->Move(bookmark_bar_node->GetChild(2), bookmark_bar_node, 0);
-  EXPECT_EQ(0, test_helper_->GetBookmarkButtonCount());
+  EXPECT_EQ(0u, test_helper_->GetBookmarkButtonCount());
 
   // Make enough room for 1 node.
   SizeUntilButtonsVisible(1);
@@ -309,10 +309,10 @@
   BookmarkModel* model = BookmarkModelFactory::GetForBrowserContext(profile());
   const BookmarkNode* bookmark_bar_node = model->bookmark_bar_node();
   AddNodesToBookmarkBarFromModelString("a b c d e f ");
-  EXPECT_EQ(0, test_helper_->GetBookmarkButtonCount());
+  EXPECT_EQ(0u, test_helper_->GetBookmarkButtonCount());
 
   model->SetTitle(bookmark_bar_node->GetChild(0), base::ASCIIToUTF16("a1"));
-  EXPECT_EQ(0, test_helper_->GetBookmarkButtonCount());
+  EXPECT_EQ(0u, test_helper_->GetBookmarkButtonCount());
 
   // Make enough room for 1 node.
   SizeUntilButtonsVisible(1);
@@ -332,13 +332,13 @@
   EXPECT_EQ("a1 b1", GetStringForVisibleButtons());
   model->SetTitle(bookmark_bar_node->GetChild(0),
                   base::ASCIIToUTF16("a_really_long_title"));
-  EXPECT_LE(1, test_helper_->GetBookmarkButtonCount());
+  EXPECT_LE(1u, test_helper_->GetBookmarkButtonCount());
 
   // Change the title back and make sure the 2nd button is visible again. Don't
   // use GetStringForVisibleButtons() here as more buttons may have been
   // created.
   model->SetTitle(bookmark_bar_node->GetChild(0), base::ASCIIToUTF16("a1"));
-  ASSERT_LE(2, test_helper_->GetBookmarkButtonCount());
+  ASSERT_LE(2u, test_helper_->GetBookmarkButtonCount());
   EXPECT_TRUE(test_helper_->GetBookmarkButton(0)->GetVisible());
   EXPECT_TRUE(test_helper_->GetBookmarkButton(1)->GetVisible());
 
@@ -394,7 +394,7 @@
   bookmarks::test::AddNodesFromModelString(model, model->bookmark_bar_node(),
                                            "a b");
   SizeUntilButtonsVisible(1);
-  ASSERT_EQ(1, test_helper_->GetBookmarkButtonCount());
+  ASSERT_EQ(1u, test_helper_->GetBookmarkButtonCount());
 
   views::LabelButton* button = test_helper_->GetBookmarkButton(0);
   ASSERT_TRUE(button);
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
index 388df3a..a3695b93 100644
--- a/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
+++ b/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
@@ -558,9 +558,15 @@
 
 void PageInfoBubbleView::OnWidgetDestroying(views::Widget* widget) {
   PageInfoBubbleViewBase::OnWidgetDestroying(widget);
+
   bool reload_prompt;
   presenter_->OnUIClosing(&reload_prompt);
-  std::move(closing_callback_).Run(widget->closed_reason(), reload_prompt);
+
+  // This method mostly shouldn't be re-entrant but there are a few cases where
+  // it can be (see crbug/966308). In that case, we have already run the closing
+  // callback so should not attempt to do it again.
+  if (closing_callback_)
+    std::move(closing_callback_).Run(widget->closed_reason(), reload_prompt);
 }
 
 void PageInfoBubbleView::ButtonPressed(views::Button* button,
diff --git a/chrome/common/prerender_messages.h b/chrome/common/prerender_messages.h
index 0c00cc89..ca7d57d 100644
--- a/chrome/common/prerender_messages.h
+++ b/chrome/common/prerender_messages.h
@@ -15,6 +15,7 @@
 #include "ui/gfx/geometry/size.h"
 #include "url/gurl.h"
 #include "url/ipc/url_param_traits.h"
+#include "url/origin.h"
 
 #define IPC_MESSAGE_START PrerenderMsgStart
 
@@ -32,12 +33,13 @@
 
 // Notifies of the insertion of a <link rel=prerender> element in the
 // document.
-IPC_MESSAGE_CONTROL5(PrerenderHostMsg_AddLinkRelPrerender,
-                     int /* prerender_id, assigned by WebPrerendererClient */,
-                     PrerenderAttributes,
-                     content::Referrer,
-                     gfx::Size,
-                     int /* render_view_route_id of launcher */)
+IPC_MESSAGE_CONTROL(PrerenderHostMsg_AddLinkRelPrerender,
+                    int /* prerender_id, assigned by WebPrerendererClient */,
+                    PrerenderAttributes,
+                    content::Referrer,
+                    url::Origin /* initiator_origin */,
+                    gfx::Size,
+                    int /* render_view_route_id of launcher */)
 
 // Notifies on removal of a <link rel=prerender> element from the document.
 IPC_MESSAGE_CONTROL1(PrerenderHostMsg_CancelLinkRelPrerender,
diff --git a/chrome/renderer/prerender/prerender_dispatcher.cc b/chrome/renderer/prerender/prerender_dispatcher.cc
index f1ca010..a259c667 100644
--- a/chrome/renderer/prerender/prerender_dispatcher.cc
+++ b/chrome/renderer/prerender/prerender_dispatcher.cc
@@ -22,6 +22,7 @@
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/platform/web_url.h"
 #include "url/gurl.h"
+#include "url/origin.h"
 
 namespace prerender {
 
@@ -164,7 +165,8 @@
           GURL(prerender.Url()),
           content::Referrer(blink::WebStringToGURL(prerender.GetReferrer()),
                             prerender.GetReferrerPolicy())),
-      extra_data.size(), extra_data.render_view_route_id()));
+      prerender.SecurityOrigin(), extra_data.size(),
+      extra_data.render_view_route_id()));
 }
 
 void PrerenderDispatcher::Cancel(const WebPrerender& prerender) {
diff --git a/chrome/test/data/webui/cr_elements/cr_radio_button_test.js b/chrome/test/data/webui/cr_elements/cr_radio_button_test.js
index fddfcecd..d6ea3a8 100644
--- a/chrome/test/data/webui/cr_elements/cr_radio_button_test.js
+++ b/chrome/test/data/webui/cr_elements/cr_radio_button_test.js
@@ -67,7 +67,7 @@
     radioButton.fire('focus');
     assertTrue(!!radioButton.$$('paper-ripple'));
     assertTrue(radioButton.$$('paper-ripple').holdDown);
-    radioButton.fire('pointerup');
+    radioButton.fire('up');
     assertFalse(radioButton.$$('paper-ripple').holdDown);
   });
 });
diff --git a/chrome/test/data/webui/extensions/kiosk_mode_test.js b/chrome/test/data/webui/extensions/kiosk_mode_test.js
index b7a634cc5..ff47705 100644
--- a/chrome/test/data/webui/extensions/kiosk_mode_test.js
+++ b/chrome/test/data/webui/extensions/kiosk_mode_test.js
@@ -100,8 +100,8 @@
             expectTrue(items[0].querySelector('span').hidden);
             expectFalse(items[1].querySelector('span').hidden);
             // No permission to edit auto-launch so buttons should be hidden.
-            expectTrue(items[0].querySelector('paper-button').hidden);
-            expectTrue(items[1].querySelector('paper-button').hidden);
+            expectTrue(items[0].querySelector('cr-button').hidden);
+            expectTrue(items[1].querySelector('cr-button').hidden);
             // Bailout checkbox should be hidden when auto-launch editing
             // disabled.
             expectTrue(dialog.$$('cr-checkbox').hidden);
@@ -125,7 +125,7 @@
       return initPage()
           .then(() => {
             buttons =
-                dialog.shadowRoot.querySelectorAll('.list-item paper-button');
+                dialog.shadowRoot.querySelectorAll('.list-item cr-button');
             // Has permission to edit auto-launch so buttons should be seen.
             expectFalse(buttons[0].hidden);
             expectFalse(buttons[1].hidden);
diff --git a/chrome/test/media_router/BUILD.gn b/chrome/test/media_router/BUILD.gn
index 5950a99..cddd27a0 100644
--- a/chrome/test/media_router/BUILD.gn
+++ b/chrome/test/media_router/BUILD.gn
@@ -156,17 +156,7 @@
       "//tools/perf:perf",
     ]
     data_deps = [
-      ":telemetry_extension_resources",
+      "//tools/perf/contrib/media_router_benchmarks:telemetry_extension_resources",
     ]
   }
 }
-
-copy("telemetry_extension_resources") {
-  sources = [
-    "telemetry/extension/manifest.json",
-    "telemetry/extension/script.js",
-  ]
-  outputs = [
-    "$root_out_dir/media_router/telemetry_extension/{{source_file_part}}",
-  ]
-}
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 6ecc39c5..123fde0 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-12234.0.0
\ No newline at end of file
+12242.0.0
\ No newline at end of file
diff --git a/components/omnibox/browser/contextual_suggestions_service.cc b/components/omnibox/browser/contextual_suggestions_service.cc
index 9cec318..ec9f858f 100644
--- a/components/omnibox/browser/contextual_suggestions_service.cc
+++ b/components/omnibox/browser/contextual_suggestions_service.cc
@@ -89,7 +89,7 @@
   // stream_type = 1 corresponds to zero suggest suggestions.
   request->SetInteger("stream_type", 1);
   const int experiment_id =
-      OmniboxFieldTrial::GetZeroSuggestRedirectToChromeExperimentId();
+      OmniboxFieldTrial::GetOnFocusSuggestionsCustomEndpointExperimentId();
   if (experiment_id >= 0)
     request->SetInteger("experiment_id", experiment_id);
   std::string result;
@@ -176,7 +176,8 @@
     return GURL();
   }
 
-  if (!base::FeatureList::IsEnabled(omnibox::kZeroSuggestRedirectToChrome)) {
+  if (!base::FeatureList::IsEnabled(
+          omnibox::kOnFocusSuggestionsCustomEndpoint)) {
     return GURL();
   }
 
@@ -191,16 +192,16 @@
   }
 
   const std::string server_address_param =
-      OmniboxFieldTrial::GetZeroSuggestRedirectToChromeServerAddress();
+      OmniboxFieldTrial::GetOnFocusSuggestionsCustomEndpointURL();
   GURL suggest_url(server_address_param.empty()
                        ? kDefaultExperimentalServerAddress
                        : server_address_param);
-  // Check that the suggest URL for redirect to chrome field trial is valid.
+  // Check that the custom endpoint URL is valid.
   if (!suggest_url.is_valid()) {
     return GURL();
   }
 
-  // Check that the suggest URL for redirect to chrome is HTTPS.
+  // Check that the custom endpoint URL is HTTPS.
   if (!suggest_url.SchemeIsCryptographic()) {
     return GURL();
   }
diff --git a/components/omnibox/browser/contextual_suggestions_service.h b/components/omnibox/browser/contextual_suggestions_service.h
index d75b0e7..eaab5ee 100644
--- a/components/omnibox/browser/contextual_suggestions_service.h
+++ b/components/omnibox/browser/contextual_suggestions_service.h
@@ -31,7 +31,16 @@
 class SimpleURLLoader;
 }  // namespace network
 
-// A service to fetch suggestions from a remote endpoint given a URL.
+// A service to fetch suggestions from a remote endpoint. Usually, the remote
+// endpoint is the default search provider's suggest service. However, the
+// endpoint URL can be customized by field trial.
+//
+// This service is always sent the user's authentication state, so the
+// suggestions always can be personalized. This service is also sometimes sent
+// the user's current URL, so the suggestions are sometimes also contextual.
+//
+// TODO(tommycli): Probably we should just rename this RemoteSuggestionsService,
+// as the suggestions are not always contextual.
 class ContextualSuggestionsService : public KeyedService {
  public:
   // |identity_manager| may be null but only unauthenticated requests will
diff --git a/components/omnibox/browser/contextual_suggestions_service_unittest.cc b/components/omnibox/browser/contextual_suggestions_service_unittest.cc
index 24c15aa..5caac0aa 100644
--- a/components/omnibox/browser/contextual_suggestions_service_unittest.cc
+++ b/components/omnibox/browser/contextual_suggestions_service_unittest.cc
@@ -49,7 +49,7 @@
 TEST_F(ContextualSuggestionsServiceTest, EnsureAttachCookies) {
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndDisableFeature(
-      omnibox::kZeroSuggestRedirectToChrome);
+      omnibox::kOnFocusSuggestionsCustomEndpoint);
 
   network::ResourceRequest resource_request;
   test_url_loader_factory_.SetInterceptor(
diff --git a/components/omnibox/browser/omnibox_field_trial.cc b/components/omnibox/browser/omnibox_field_trial.cc
index 3baed92..1d09030 100644
--- a/components/omnibox/browser/omnibox_field_trial.cc
+++ b/components/omnibox/browser/omnibox_field_trial.cc
@@ -258,18 +258,18 @@
 }
 
 // static
-int OmniboxFieldTrial::GetZeroSuggestRedirectToChromeExperimentId() {
+int OmniboxFieldTrial::GetOnFocusSuggestionsCustomEndpointExperimentId() {
   return base::GetFieldTrialParamByFeatureAsInt(
-      omnibox::kZeroSuggestRedirectToChrome,
-      kZeroSuggestRedirectToChromeExperimentIdParam,
+      omnibox::kOnFocusSuggestionsCustomEndpoint,
+      kOnFocusSuggestionsEndpointExperimentIdParam,
       /*default_value=*/-1);
 }
 
 // static
-std::string OmniboxFieldTrial::GetZeroSuggestRedirectToChromeServerAddress() {
+std::string OmniboxFieldTrial::GetOnFocusSuggestionsCustomEndpointURL() {
   return base::GetFieldTrialParamValueByFeature(
-      omnibox::kZeroSuggestRedirectToChrome,
-      kZeroSuggestRedirectToChromeServerAddressParam);
+      omnibox::kOnFocusSuggestionsCustomEndpoint,
+      kOnFocusSuggestionsEndpointURLParam);
 }
 
 bool OmniboxFieldTrial::ShortcutsScoringMaxRelevance(
@@ -806,10 +806,10 @@
 const char OmniboxFieldTrial::kSimplifyHttpsIndicatorParameterKeepSecureChip[] =
     "keep-secure-chip";
 
-const char OmniboxFieldTrial::kZeroSuggestRedirectToChromeExperimentIdParam[] =
-    "ZeroSuggestRedirectToChromeExperimentID";
-const char OmniboxFieldTrial::kZeroSuggestRedirectToChromeServerAddressParam[] =
-    "ZeroSuggestRedirectToChromeServerAddress";
+const char OmniboxFieldTrial::kOnFocusSuggestionsEndpointExperimentIdParam[] =
+    "CustomEndpointExperimentID";
+const char OmniboxFieldTrial::kOnFocusSuggestionsEndpointURLParam[] =
+    "CustomEndpointURL";
 
 // static
 int OmniboxFieldTrial::kDefaultMinimumTimeBetweenSuggestQueriesMs = 100;
diff --git a/components/omnibox/browser/omnibox_field_trial.h b/components/omnibox/browser/omnibox_field_trial.h
index dfc9389..da99015 100644
--- a/components/omnibox/browser/omnibox_field_trial.h
+++ b/components/omnibox/browser/omnibox_field_trial.h
@@ -177,14 +177,14 @@
     metrics::OmniboxEventProto::PageClassification page_classification);
 
 // ---------------------------------------------------------
-// For the Zero Suggest Redirect to Chrome field trial.
+// For the On Focus Suggestions Custom Endpoint field trial.
 
 // Returns the server-side experiment ID to use for contextual suggestions.
 // Returns -1 if there is no associated experiment ID.
-int GetZeroSuggestRedirectToChromeExperimentId();
+int GetOnFocusSuggestionsCustomEndpointExperimentId();
 
 // Returns the server address associated with the current field trial.
-std::string GetZeroSuggestRedirectToChromeServerAddress();
+std::string GetOnFocusSuggestionsCustomEndpointURL();
 
 // ---------------------------------------------------------
 // For the ShortcutsScoringMaxRelevance experiment that's part of the
@@ -512,9 +512,9 @@
 extern const char kSimplifyHttpsIndicatorParameterBothToLock[];
 extern const char kSimplifyHttpsIndicatorParameterKeepSecureChip[];
 
-// Parameter names used by Zero Suggest Redirect to Chrome.
-extern const char kZeroSuggestRedirectToChromeExperimentIdParam[];
-extern const char kZeroSuggestRedirectToChromeServerAddressParam[];
+// Parameter names used by On Focus Suggestions Custom Endpoint.
+extern const char kOnFocusSuggestionsEndpointExperimentIdParam[];
+extern const char kOnFocusSuggestionsEndpointURLParam[];
 
 // The amount of time to wait before sending a new suggest request after the
 // previous one unless overridden by a field trial parameter.
diff --git a/components/omnibox/browser/zero_suggest_provider.cc b/components/omnibox/browser/zero_suggest_provider.cc
index 60d12d4..3f98b03 100644
--- a/components/omnibox/browser/zero_suggest_provider.cc
+++ b/components/omnibox/browser/zero_suggest_provider.cc
@@ -180,9 +180,8 @@
     return;
   }
 
-  const std::string current_url = result_type_running_ == DEFAULT_SERP_FOR_URL
-                                      ? current_query_
-                                      : std::string();
+  const std::string current_url =
+      result_type_running_ == REMOTE_SEND_URL ? current_query_ : std::string();
   // Create a request for suggestions, routing completion to
   // OnContextualSuggestionsLoaderAvailable.
   client()
@@ -350,7 +349,7 @@
 
   // When running the personalized service, we want to store suggestion
   // responses if non-empty.
-  if (result_type_running_ == DEFAULT_SERP && !json_data.empty()) {
+  if (result_type_running_ == REMOTE_NO_URL && !json_data.empty()) {
     client()->GetPrefs()->SetString(omnibox::kZeroSuggestCachedResults,
                                     json_data);
 
@@ -564,7 +563,7 @@
 }
 
 void ZeroSuggestProvider::MaybeUseCachedSuggestions() {
-  if (result_type_running_ != DEFAULT_SERP)
+  if (result_type_running_ != REMOTE_NO_URL)
     return;
 
   std::string json_data =
@@ -590,7 +589,6 @@
   const bool can_send_current_url = CanSendURL(
       current_url, suggest_url, default_provider, current_page_classification_,
       template_url_service->search_terms_data(), client());
-
   // Collect metrics on eligibility.
   GURL arbitrary_insecure_url(kArbitraryInsecureUrlString);
   ZeroSuggestEligibility eligibility = ZeroSuggestEligibility::ELIGIBLE;
@@ -614,7 +612,7 @@
 
   if (current_page_classification_ ==
       metrics::OmniboxEventProto::CHROMEOS_APP_LIST) {
-    return DEFAULT_SERP;
+    return REMOTE_NO_URL;
   }
 
   if (OmniboxFieldTrial::InZeroSuggestPersonalizedFieldTrial(
@@ -623,7 +621,7 @@
                client()->GetPrefs(), client()->IsAuthenticated(),
                template_url_service)
                ? MOST_VISITED
-               : DEFAULT_SERP;
+               : REMOTE_NO_URL;
   }
 
   if (OmniboxFieldTrial::InZeroSuggestMostVisitedWithoutSerpFieldTrial(
@@ -639,5 +637,5 @@
     return MOST_VISITED;
   }
 
-  return can_send_current_url ? DEFAULT_SERP_FOR_URL : NONE;
+  return can_send_current_url ? REMOTE_SEND_URL : NONE;
 }
diff --git a/components/omnibox/browser/zero_suggest_provider.h b/components/omnibox/browser/zero_suggest_provider.h
index 38e8e77..dbb0dfa 100644
--- a/components/omnibox/browser/zero_suggest_provider.h
+++ b/components/omnibox/browser/zero_suggest_provider.h
@@ -64,6 +64,7 @@
   void ResetSession() override;
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(ZeroSuggestProviderTest, TypeOfResultToRun);
   FRIEND_TEST_ALL_PREFIXES(ZeroSuggestProviderTest,
                            TestStartWillStopForSomeInput);
   ZeroSuggestProvider(AutocompleteProviderClient* client,
@@ -76,12 +77,19 @@
   // at any time.
   enum ResultType {
     NONE,
-    DEFAULT_SERP,          // The default search provider is queried for
-                           // zero-suggest suggestions.
-    DEFAULT_SERP_FOR_URL,  // The default search provider is queried for
-                           // zero-suggest suggestions that are specific
-                           // to the visited URL.
-    MOST_VISITED
+
+    // A remote endpoint (usually the default search provider) is queried for
+    // suggestions. The endpoint is sent the user's authentication state, but
+    // not sent the current URL.
+    REMOTE_NO_URL,
+
+    // A remote endpoint (usually the default search provider) is queried for
+    // suggestions. The endpoint is sent the user's authentication state and
+    // the current URL.
+    REMOTE_SEND_URL,
+
+    // Gets the most visited sites from local history.
+    MOST_VISITED,
   };
 
   // BaseSearchProvider:
diff --git a/components/omnibox/browser/zero_suggest_provider_unittest.cc b/components/omnibox/browser/zero_suggest_provider_unittest.cc
index 3ef6720..0b4287a 100644
--- a/components/omnibox/browser/zero_suggest_provider_unittest.cc
+++ b/components/omnibox/browser/zero_suggest_provider_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/scoped_task_environment.h"
+#include "build/build_config.h"
 #include "components/history/core/browser/top_sites.h"
 #include "components/omnibox/browser/autocomplete_provider_listener.h"
 #include "components/omnibox/browser/mock_autocomplete_provider_client.h"
@@ -155,7 +156,7 @@
   void SetZeroSuggestVariantForAllContexts(const std::string& variant);
 
   base::test::ScopedTaskEnvironment scoped_task_environment_;
-  base::test::ScopedFeatureList scoped_feature_list_;
+  std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list_;
 
   std::unique_ptr<FakeAutocompleteProviderClient> client_;
   scoped_refptr<ZeroSuggestProvider> provider_;
@@ -202,12 +203,55 @@
 
 void ZeroSuggestProviderTest::SetZeroSuggestVariantForAllContexts(
     const std::string& variant) {
-  scoped_feature_list_.InitAndEnableFeatureWithParameters(
+  scoped_feature_list_ = std::make_unique<base::test::ScopedFeatureList>();
+  scoped_feature_list_->InitAndEnableFeatureWithParameters(
       omnibox::kOnFocusSuggestions,
       {{std::string(OmniboxFieldTrial::kZeroSuggestVariantRule) + ":*:*",
         variant}});
 }
 
+TEST_F(ZeroSuggestProviderTest, TypeOfResultToRun) {
+  GURL current_url = GURL("https://example.com/");
+  GURL suggest_url = GURL("https://www.google.com/complete/?q={searchTerms}");
+
+  // Expect NONE by default if URL data collection is inactive.
+  EXPECT_CALL(*client_, IsPersonalizedUrlDataCollectionActive())
+      .WillRepeatedly(testing::Return(false));
+
+#if defined(OS_IOS) || defined(OS_ANDROID)
+  // iOS and Android both default to MOST_VISITED.
+  EXPECT_EQ(ZeroSuggestProvider::ResultType::MOST_VISITED,
+            provider_->TypeOfResultToRun(current_url, suggest_url));
+#else
+  // Expect REMOTE_SEND_URL type if client is authenticated and provides URLs.
+  EXPECT_EQ(ZeroSuggestProvider::ResultType::NONE,
+            provider_->TypeOfResultToRun(current_url, suggest_url));
+#endif
+
+  EXPECT_CALL(*client_, IsAuthenticated())
+      .WillRepeatedly(testing::Return(true));
+  EXPECT_CALL(*client_, IsPersonalizedUrlDataCollectionActive())
+      .WillRepeatedly(testing::Return(true));
+
+#if defined(OS_IOS) || defined(OS_ANDROID)
+  // iOS and Android both default to MOST_VISITED, even if authenticated.
+  EXPECT_EQ(ZeroSuggestProvider::ResultType::MOST_VISITED,
+            provider_->TypeOfResultToRun(current_url, suggest_url));
+#else
+  // Expect REMOTE_SEND_URL type if client is authenticated and provides URLs.
+  EXPECT_EQ(ZeroSuggestProvider::ResultType::REMOTE_SEND_URL,
+            provider_->TypeOfResultToRun(current_url, suggest_url));
+#endif
+
+  CreatePersonalizedFieldTrial();
+  EXPECT_EQ(ZeroSuggestProvider::ResultType::REMOTE_NO_URL,
+            provider_->TypeOfResultToRun(current_url, suggest_url));
+
+  CreateMostVisitedFieldTrial();
+  EXPECT_EQ(ZeroSuggestProvider::ResultType::MOST_VISITED,
+            provider_->TypeOfResultToRun(current_url, suggest_url));
+}
+
 TEST_F(ZeroSuggestProviderTest, TestDoesNotReturnMatchesForPrefix) {
   CreatePersonalizedFieldTrial();
 
@@ -468,7 +512,7 @@
             prefs->GetString(omnibox::kZeroSuggestCachedResults));
 }
 
-TEST_F(ZeroSuggestProviderTest, RedirectToChrome) {
+TEST_F(ZeroSuggestProviderTest, CustomEndpoint) {
   CreateContextualSuggestFieldTrial();
   // Coverage for the URL-specific page. (Regression test for a DCHECK).
   // This is exercising ContextualSuggestionsService::CreateExperimentalRequest,
@@ -478,11 +522,10 @@
   // redirect to chrome mode on.
   base::test::ScopedFeatureList features;
   std::map<std::string, std::string> params;
-  params[std::string(
-      OmniboxFieldTrial::kZeroSuggestRedirectToChromeServerAddressParam)] =
+  params[std::string(OmniboxFieldTrial::kOnFocusSuggestionsEndpointURLParam)] =
       "https://cuscochromeextension-pa.googleapis.com/v1/omniboxsuggestions";
   features.InitAndEnableFeatureWithParameters(
-      omnibox::kZeroSuggestRedirectToChrome, params);
+      omnibox::kOnFocusSuggestionsCustomEndpoint, params);
 
   EXPECT_CALL(*client_, IsAuthenticated())
       .WillRepeatedly(testing::Return(true));
diff --git a/components/omnibox/common/omnibox_features.cc b/components/omnibox/common/omnibox_features.cc
index 443a2c0b..a3777fb 100644
--- a/components/omnibox/common/omnibox_features.cc
+++ b/components/omnibox/common/omnibox_features.cc
@@ -285,7 +285,8 @@
 const base::Feature kOnFocusSuggestions{"OmniboxOnFocusSuggestions",
                                         base::FEATURE_ENABLED_BY_DEFAULT};
 
-// Feature used for the Zero Suggest Redirect to Chrome Field Trial.
+// Feature used to specify a custom endpoint URL for on-focus suggestions that
+// are sourced via RPC.
 //
 // This feature is *enabled* in order to *disable* all forms of suggestions
 // based on the URL on-focus (whether from "redirect to Chrome" or the
@@ -296,8 +297,9 @@
 // If this feature were not enabled, Chrome would use the default suggest
 // server for suggestions based on the current URL on focus.  There is no
 // code in Chrome to disable that, so that why we took this route.
-const base::Feature kZeroSuggestRedirectToChrome{
-    "ZeroSuggestRedirectToChrome", base::FEATURE_ENABLED_BY_DEFAULT};
+const base::Feature kOnFocusSuggestionsCustomEndpoint{
+    "OmniboxOnFocusSuggestionsCustomEndpoint",
+    base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Allow suggestions to be shown to the user on the New Tab Page upon focusing
 // URL bar (the omnibox).
diff --git a/components/omnibox/common/omnibox_features.h b/components/omnibox/common/omnibox_features.h
index b96a4f6..4ea8861 100644
--- a/components/omnibox/common/omnibox_features.h
+++ b/components/omnibox/common/omnibox_features.h
@@ -51,7 +51,7 @@
 
 // On-Focus Suggestions a.k.a. ZeroSuggest.
 extern const base::Feature kOnFocusSuggestions;
-extern const base::Feature kZeroSuggestRedirectToChrome;
+extern const base::Feature kOnFocusSuggestionsCustomEndpoint;
 extern const base::Feature kZeroSuggestionsOnNTP;
 
 }  // namespace omnibox
diff --git a/components/optimization_guide/proto/BUILD.gn b/components/optimization_guide/proto/BUILD.gn
index 07abdc94..a703e47 100644
--- a/components/optimization_guide/proto/BUILD.gn
+++ b/components/optimization_guide/proto/BUILD.gn
@@ -6,6 +6,8 @@
 
 proto_library("optimization_guide_proto") {
   sources = [
+    "common_types.proto",
     "hints.proto",
+    "previews_metadata.proto",
   ]
 }
diff --git a/components/optimization_guide/proto/common_types.proto b/components/optimization_guide/proto/common_types.proto
new file mode 100644
index 0000000..0f83460
--- /dev/null
+++ b/components/optimization_guide/proto/common_types.proto
@@ -0,0 +1,38 @@
+// 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.
+
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+
+package optimization_guide.proto;
+
+// The possible effective connection type values.
+//
+// The values should match those of //net/nqe/effective_connection_type.h in the
+// Chromium repository.
+enum EffectiveConnectionType {
+  // Effective connection type reported when the network quality is unknown.
+  EFFECTIVE_CONNECTION_TYPE_UNKNOWN = 0;
+
+  // Effective connection type reported when the Internet is unreachable,
+  // either because the device does not have a connection or because the
+  // connection is too slow to be usable.
+  EFFECTIVE_CONNECTION_TYPE_OFFLINE = 1;
+
+  // Effective connection type reported when the network has the quality of a
+  // poor 2G connection.
+  EFFECTIVE_CONNECTION_TYPE_SLOW_2G = 2;
+
+  // Effective connection type reported when the network has the quality of a
+  // faster 2G connection.
+  EFFECTIVE_CONNECTION_TYPE_2G = 3;
+
+  // Effective connection type reported when the network has the quality of a
+  // 3G connection.
+  EFFECTIVE_CONNECTION_TYPE_3G = 4;
+
+  // Effective connection type reported when the network has the quality of a
+  // 4G connection.
+  EFFECTIVE_CONNECTION_TYPE_4G = 5;
+}
diff --git a/components/optimization_guide/proto/hints.proto b/components/optimization_guide/proto/hints.proto
index a45de45..fd24217 100644
--- a/components/optimization_guide/proto/hints.proto
+++ b/components/optimization_guide/proto/hints.proto
@@ -7,6 +7,9 @@
 
 package optimization_guide.proto;
 
+import "common_types.proto";
+import "previews_metadata.proto";
+
 // Information about the hint that the client already has for a host.
 message MatchedHintInfo {
   // Describes the granularity of the key field.
@@ -127,23 +130,6 @@
   HOST_SUFFIX = 1;
 }
 
-enum LoadingOptimizationType {
-  LOADING_UNSPECIFIED = 0;
-  // The resource should not be loaded.
-  LOADING_BLOCK_RESOURCE = 1;
-}
-
-message ResourceLoadingHint {
-  // The pattern to match against the resource URL.
-  //
-  // The pattern may be a single substring to match against the URL or it may be
-  // an ordered set of substrings to match where the substrings are separated by
-  // the ‘*’ wildcard character (with an implicit ‘*’ at the beginning and end).
-  optional string resource_pattern = 1;
-  // The type of loading optimization to apply to the resource.
-  optional LoadingOptimizationType loading_optimization_type = 2;
-}
-
 message Optimization {
   // The type of optimization the hint applies to.
   optional OptimizationType optimization_type = 1;
@@ -155,13 +141,13 @@
   // Ex: If the received bytes is 100 and the inflation_percent is 30, the
   // inflated bytes calculated by the client will be 30 in order to have a total
   // consumed bytes value of 130.
-  optional int64 inflation_percent = 2;
+  optional int64 inflation_percent = 2 [deprecated = true];
   // An ordered set of resource loading hints for OptimizationType
   // RESOURCE_LOADING.
   //
   // Only the first ResourceLoadingHint record that matches a target resource
   // URL will be applied to that resource.
-  repeated ResourceLoadingHint resource_loading_hints = 3;
+  repeated ResourceLoadingHint resource_loading_hints = 3 [deprecated = true];
   // The experiment name that activates the optimization.
   //
   // If a non-empty name is provided, the optimization will be disabled unless
@@ -182,36 +168,11 @@
   // same name, the optimization will be disabled and skipped for that client.
   // An empty name is not experimental.
   optional string excluded_experiment_name = 5;
-}
-
-// The possible effective connection type values.
-//
-// The values should match those of //net/nqe/effective_connection_type.h in the
-// Chromium repository.
-enum EffectiveConnectionType {
-  // Effective connection type reported when the network quality is unknown.
-  EFFECTIVE_CONNECTION_TYPE_UNKNOWN = 0;
-
-  // Effective connection type reported when the Internet is unreachable,
-  // either because the device does not have a connection or because the
-  // connection is too slow to be usable.
-  EFFECTIVE_CONNECTION_TYPE_OFFLINE = 1;
-
-  // Effective connection type reported when the network has the quality of a
-  // poor 2G connection.
-  EFFECTIVE_CONNECTION_TYPE_SLOW_2G = 2;
-
-  // Effective connection type reported when the network has the quality of a
-  // faster 2G connection.
-  EFFECTIVE_CONNECTION_TYPE_2G = 3;
-
-  // Effective connection type reported when the network has the quality of a
-  // 3G connection.
-  EFFECTIVE_CONNECTION_TYPE_3G = 4;
-
-  // Effective connection type reported when the network has the quality of a
-  // 4G connection.
-  EFFECTIVE_CONNECTION_TYPE_4G = 5;
+  // The metadata associated with the optimization type.
+  //
+  // It is expected that the client and server have agreed upon the appropriate
+  // metadata type for the optimization type.
+  oneof metadata { PreviewsMetadata previews_metadata = 10; }
 }
 
 message PageHint {
@@ -224,7 +185,7 @@
   optional string page_pattern = 1;
   // The maximum effective connection type threshold for triggering the
   // optimization associated with this hint.
-  optional EffectiveConnectionType max_ect_trigger = 2;
+  optional EffectiveConnectionType max_ect_trigger = 2 [deprecated = true];
   // An ordered set of optimizations that should be whitelisted for this page
   // pattern.
   //
diff --git a/components/optimization_guide/proto/previews_metadata.proto b/components/optimization_guide/proto/previews_metadata.proto
new file mode 100644
index 0000000..c152bf6
--- /dev/null
+++ b/components/optimization_guide/proto/previews_metadata.proto
@@ -0,0 +1,52 @@
+// 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.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package optimization_guide.proto;
+
+import "common_types.proto";
+
+enum LoadingOptimizationType {
+  LOADING_UNSPECIFIED = 0;
+  // The resource should not be loaded.
+  LOADING_BLOCK_RESOURCE = 1;
+}
+
+message ResourceLoadingHint {
+  // The pattern to match against the resource URL.
+  //
+  // The pattern may be a single substring to match against the URL or it may be
+  // an ordered set of substrings to match where the substrings are separated by
+  // the ‘*’ wildcard character (with an implicit ‘*’ at the beginning and end).
+  optional string resource_pattern = 1;
+  // The type of loading optimization to apply to the resource.
+  optional LoadingOptimizationType loading_optimization_type = 2;
+}
+
+// Optimization metadata associated with Previews.
+//
+// Currently, this is just populated for optimization types NOSCRIPT and
+// RESOURCE_LOADING.
+message PreviewsMetadata {
+  // A percent value to inflate the number of received bytes by for the purposes
+  // of data savings calculations in the client.
+  //
+  // If this value is set to 0, the client should use its configured default.
+  //
+  // Ex: If the received bytes is 100 and the inflation_percent is 30, the
+  // inflated bytes calculated by the client will be 30 in order to have a total
+  // consumed bytes value of 130.
+  optional int64 inflation_percent = 1;
+  // The maximum effective connection type threshold for triggering the
+  // optimization associated with this optimization.
+  optional EffectiveConnectionType max_ect_trigger = 2;
+  // An ordered set of resource loading hints.
+  //
+  // Only the first ResourceLoadingHint record that matches a target resource
+  // URL will be applied to that resource.
+  repeated ResourceLoadingHint resource_loading_hints = 3;
+}
diff --git a/components/previews/content/previews_hints.cc b/components/previews/content/previews_hints.cc
index d52a110..12aa84c 100644
--- a/components/previews/content/previews_hints.cc
+++ b/components/previews/content/previews_hints.cc
@@ -422,10 +422,18 @@
       return false;
     }
     // |type| is the first whitelisted optimization this client supports.
-    *out_inflation_percent = optimization.inflation_percent();
-    if (matched_page_hint->has_max_ect_trigger()) {
+    // Extract any applicable metadata for it.
+    if (optimization.has_previews_metadata()) {
+      *out_inflation_percent =
+          optimization.previews_metadata().inflation_percent();
       *out_ect_threshold = ConvertProtoEffectiveConnectionType(
-          matched_page_hint->max_ect_trigger());
+          optimization.previews_metadata().max_ect_trigger());
+    } else {
+      *out_inflation_percent = optimization.inflation_percent();
+      if (matched_page_hint->has_max_ect_trigger()) {
+        *out_ect_threshold = ConvertProtoEffectiveConnectionType(
+            matched_page_hint->max_ect_trigger());
+      }
     }
 
     *out_serialized_hint_version_string = hint->version();
@@ -498,8 +506,17 @@
       continue;
     }
 
-    for (const auto& resource_loading_hint :
-         optimization.resource_loading_hints()) {
+    google::protobuf::RepeatedPtrField<
+        optimization_guide::proto::ResourceLoadingHint>
+        resource_loading_hints;
+    if (optimization.has_previews_metadata()) {
+      resource_loading_hints =
+          optimization.previews_metadata().resource_loading_hints();
+    } else {
+      resource_loading_hints = optimization.resource_loading_hints();
+    }
+
+    for (const auto& resource_loading_hint : resource_loading_hints) {
       if (!resource_loading_hint.resource_pattern().empty() &&
           resource_loading_hint.loading_optimization_type() ==
               optimization_guide::proto::LOADING_BLOCK_RESOURCE) {
diff --git a/components/previews/content/previews_hints_unittest.cc b/components/previews/content/previews_hints_unittest.cc
index 9ffcb4f8..cd91a15c 100644
--- a/components/previews/content/previews_hints_unittest.cc
+++ b/components/previews/content/previews_hints_unittest.cc
@@ -423,6 +423,35 @@
   resource_hint2b->set_loading_optimization_type(
       optimization_guide::proto::LOADING_BLOCK_RESOURCE);
   resource_hint2b->set_resource_pattern("resource2b.js");
+  // Page hint for "/has_metadata/" with all details provided in metadata and
+  // should take precedence over the details at the top-level.
+  optimization_guide::proto::PageHint* page_hint3 = hint1->add_page_hints();
+  page_hint3->set_page_pattern("/has_metadata/");
+  page_hint3->set_max_ect_trigger(
+      optimization_guide::proto::EffectiveConnectionType::
+          EFFECTIVE_CONNECTION_TYPE_4G);
+  optimization_guide::proto::Optimization* optimization_with_metadata =
+      page_hint3->add_whitelisted_optimizations();
+  optimization_with_metadata->set_optimization_type(
+      optimization_guide::proto::RESOURCE_LOADING);
+  optimization_with_metadata->set_inflation_percent(12345);
+  optimization_guide::proto::ResourceLoadingHint* unused_resource_hint =
+      optimization_with_metadata->add_resource_loading_hints();
+  unused_resource_hint->set_loading_optimization_type(
+      optimization_guide::proto::LOADING_BLOCK_RESOURCE);
+  unused_resource_hint->set_resource_pattern("unused_resource_hint.js");
+  optimization_guide::proto::PreviewsMetadata* previews_metadata =
+      optimization_with_metadata->mutable_previews_metadata();
+  previews_metadata->set_inflation_percent(123);
+  optimization_guide::proto::ResourceLoadingHint* resource_hint3 =
+      previews_metadata->add_resource_loading_hints();
+  resource_hint3->set_loading_optimization_type(
+      optimization_guide::proto::LOADING_BLOCK_RESOURCE);
+  resource_hint3->set_resource_pattern("resource3.js");
+  previews_metadata->set_max_ect_trigger(
+      optimization_guide::proto::EffectiveConnectionType::
+          EFFECTIVE_CONNECTION_TYPE_3G);
+
   ParseConfig(config);
 
   // Verify optimization providing inflation_percent and hints version.
@@ -467,6 +496,24 @@
   EXPECT_EQ(2ul, patterns_to_block2.size());
   EXPECT_EQ("resource2a.js", patterns_to_block2[0]);
   EXPECT_EQ("resource2b.js", patterns_to_block2[1]);
+
+  // Verify page hint having data stored in metadata.
+  inflation_percent = 0;
+  ect_threshold =
+      net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
+  serialized_hint_version_string = "";
+  EXPECT_TRUE(MaybeLoadHintAndCheckIsWhitelisted(
+      GURL("https://www.somedomain.org/has_metadata/"),
+      PreviewsType::RESOURCE_LOADING_HINTS, &inflation_percent, &ect_threshold,
+      &serialized_hint_version_string));
+  EXPECT_EQ(123, inflation_percent);
+  std::vector<std::string> patterns_to_block3;
+  previews_hints()->GetResourceLoadingHints(
+      GURL("https://www.somedomain.org/has_metadata/"), &patterns_to_block3);
+  EXPECT_EQ(1ul, patterns_to_block3.size());
+  EXPECT_EQ("resource3.js", patterns_to_block3[0]);
+  EXPECT_EQ(net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_3G,
+            ect_threshold);
 }
 
 TEST_F(PreviewsHintsTest,
diff --git a/components/previews/core/previews_constants.cc b/components/previews/core/previews_constants.cc
index 7597b091..cc90d62 100644
--- a/components/previews/core/previews_constants.cc
+++ b/components/previews/core/previews_constants.cc
@@ -12,4 +12,7 @@
 const char kPreviewsOptimizationGuideOnLoadedHintResultHistogramString[] =
     "PreviewsOptimizationGuide.OnLoadedHint.Result";
 
+extern const char kOptimizationGuideServiceDefaultURL[] =
+    "https://optimizationguide-pa.googleapis.com/v1:GetHints";
+
 }  // namespace previews
diff --git a/components/previews/core/previews_constants.h b/components/previews/core/previews_constants.h
index 87216f4..0a7ac8a1 100644
--- a/components/previews/core/previews_constants.h
+++ b/components/previews/core/previews_constants.h
@@ -15,6 +15,10 @@
 // finished loading.
 extern const char kPreviewsOptimizationGuideOnLoadedHintResultHistogramString[];
 
+// The remote Optimization Guide Service production server to fetcher hints
+// from.
+extern const char kOptimizationGuideServiceDefaultURL[];
+
 }  // namespace previews
 
 #endif  // COMPONENTS_PREVIEWS_CORE_PREVIEWS_CONSTANTS_H_
diff --git a/components/previews/core/previews_experiments.cc b/components/previews/core/previews_experiments.cc
index 9496cbd..87f6589 100644
--- a/components/previews/core/previews_experiments.cc
+++ b/components/previews/core/previews_experiments.cc
@@ -13,9 +13,11 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
+#include "components/previews/core/previews_constants.h"
 #include "components/previews/core/previews_features.h"
 #include "components/previews/core/previews_switches.h"
 #include "google_apis/google_api_keys.h"
+#include "net/base/url_util.h"
 
 namespace previews {
 
@@ -47,7 +49,6 @@
 const char kNoScriptInflationPercent[] = "NoScriptInflationPercent";
 const char kNoScriptInflationBytes[] = "NoScriptInflationBytes";
 
-const char kOptimizationGuideServiceURL[] = "";
 
 // Inflation parameters for estimating ResourceLoadingHints data savings.
 const char kResourceLoadingHintsInflationPercent[] =
@@ -251,8 +252,13 @@
 
   std::string url = base::GetFieldTrialParamValueByFeature(
       features::kOptimizationHintsFetching, "optimization_guide_service_url");
-  if (url.empty())
-    return GURL(kOptimizationGuideServiceURL);
+  if (url.empty() || !GURL(url).SchemeIs(url::kHttpsScheme)) {
+    if (!url.empty())
+      LOG(WARNING)
+          << "Empty or invalid optimization_guide_service_url provided: "
+          << url;
+    return GURL(previews::kOptimizationGuideServiceDefaultURL);
+  }
 
   return GURL(url);
 }
diff --git a/components/previews/core/previews_experiments_unittest.cc b/components/previews/core/previews_experiments_unittest.cc
index 4c28d43..c10af9a 100644
--- a/components/previews/core/previews_experiments_unittest.cc
+++ b/components/previews/core/previews_experiments_unittest.cc
@@ -15,6 +15,7 @@
 #include "base/strings/string_util.h"
 #include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
+#include "components/previews/core/previews_constants.h"
 #include "components/previews/core/previews_features.h"
 #include "components/variations/variations_associated_data.h"
 
@@ -227,6 +228,31 @@
   }
 }
 
+TEST(PreviewsExperimentsTest, TestGetOptimizationGuideServiceURLHTTPSOnly) {
+  base::test::ScopedFeatureList scoped_feature_list;
+
+  scoped_feature_list.InitAndEnableFeatureWithParameters(
+      features::kOptimizationHintsFetching,
+      {{"optimization_guide_service_url", "http://NotAnHTTPSServer.com"}});
+
+  EXPECT_EQ(params::GetOptimizationGuideServiceURL().spec(),
+            kOptimizationGuideServiceDefaultURL);
+  EXPECT_TRUE(
+      params::GetOptimizationGuideServiceURL().SchemeIs(url::kHttpsScheme));
+}
+
+TEST(PreviewsExperimentsTest, TestGetOptimizationGuideServiceURLViaFinch) {
+  base::test::ScopedFeatureList scoped_feature_list;
+
+  std::string optimization_guide_service_url = "https://finchserver.com/";
+  scoped_feature_list.InitAndEnableFeatureWithParameters(
+      features::kOptimizationHintsFetching,
+      {{"optimization_guide_service_url", optimization_guide_service_url}});
+
+  EXPECT_EQ(params::GetOptimizationGuideServiceURL().spec(),
+            optimization_guide_service_url);
+}
+
 }  // namespace
 
 }  // namespace previews
diff --git a/components/send_tab_to_self/OWNERS b/components/send_tab_to_self/OWNERS
index cbbc50e6..a49acc9 100644
--- a/components/send_tab_to_self/OWNERS
+++ b/components/send_tab_to_self/OWNERS
@@ -1,5 +1,6 @@
 hansberry@chromium.org
 jeffreycohen@chromium.org
 sebsg@chromium.org
+tgupta@chromium.org
 
 # COMPONENT: UI>Browser>Sharing
diff --git a/content/app_shim_remote_cocoa/ns_view_bridge_factory_impl.mm b/content/app_shim_remote_cocoa/ns_view_bridge_factory_impl.mm
index 98b4efb..f94cd81 100644
--- a/content/app_shim_remote_cocoa/ns_view_bridge_factory_impl.mm
+++ b/content/app_shim_remote_cocoa/ns_view_bridge_factory_impl.mm
@@ -10,8 +10,8 @@
 #include "base/bind.h"
 #include "base/macros.h"
 #include "base/no_destructor.h"
-#include "content/app_shim_remote_cocoa/render_widget_host_ns_view_bridge_local.h"
-#include "content/app_shim_remote_cocoa/render_widget_host_ns_view_client_helper.h"
+#include "content/app_shim_remote_cocoa/render_widget_host_ns_view_bridge.h"
+#include "content/app_shim_remote_cocoa/render_widget_host_ns_view_host_helper.h"
 #include "content/app_shim_remote_cocoa/web_contents_ns_view_bridge.h"
 #include "content/browser/renderer_host/input/web_input_event_builders_mac.h"
 #include "content/common/render_widget_host_ns_view.mojom.h"
@@ -21,21 +21,21 @@
 #include "ui/accelerated_widget_mac/window_resize_helper_mac.h"
 #include "ui/base/cocoa/remote_accessibility_api.h"
 
-namespace content {
+namespace remote_cocoa {
 
 namespace {
 
 class RenderWidgetHostNSViewBridgeOwner
-    : public RenderWidgetHostNSViewClientHelper {
+    : public RenderWidgetHostNSViewHostHelper {
  public:
   explicit RenderWidgetHostNSViewBridgeOwner(
-      mojom::RenderWidgetHostNSViewClientAssociatedPtr client,
-      mojom::RenderWidgetHostNSViewBridgeAssociatedRequest bridge_request)
-      : client_(std::move(client)) {
-    bridge_ = std::make_unique<RenderWidgetHostNSViewBridgeLocal>(client_.get(),
-                                                                  this);
+      mojom::RenderWidgetHostNSViewHostAssociatedPtr client,
+      mojom::RenderWidgetHostNSViewAssociatedRequest bridge_request)
+      : host_(std::move(client)) {
+    bridge_ = std::make_unique<remote_cocoa::RenderWidgetHostNSViewBridge>(
+        host_.get(), this);
     bridge_->BindRequest(std::move(bridge_request));
-    client_.set_connection_error_handler(
+    host_.set_connection_error_handler(
         base::BindOnce(&RenderWidgetHostNSViewBridgeOwner::OnConnectionError,
                        base::Unretained(this)));
   }
@@ -43,12 +43,12 @@
  private:
   void OnConnectionError() { delete this; }
 
-  std::unique_ptr<InputEvent> TranslateEvent(
+  std::unique_ptr<content::InputEvent> TranslateEvent(
       const blink::WebInputEvent& web_event) {
-    return std::make_unique<InputEvent>(web_event, ui::LatencyInfo());
+    return std::make_unique<content::InputEvent>(web_event, ui::LatencyInfo());
   }
 
-  // RenderWidgetHostNSViewClientHelper implementation.
+  // RenderWidgetHostNSViewHostHelper implementation.
   id GetRootBrowserAccessibilityElement() override {
     // The RenderWidgetHostViewCocoa in the app shim process does not
     // participate in the accessibility tree. Only the instance in the browser
@@ -60,112 +60,109 @@
     return nil;
   }
   void SetAccessibilityWindow(NSWindow* window) override {
-    client_->SetRemoteAccessibilityWindowToken(
+    host_->SetRemoteAccessibilityWindowToken(
         ui::RemoteAccessibility::GetTokenForLocalElement(window));
   }
 
-  void ForwardKeyboardEvent(const NativeWebKeyboardEvent& key_event,
+  void ForwardKeyboardEvent(const content::NativeWebKeyboardEvent& key_event,
                             const ui::LatencyInfo& latency_info) override {
     const blink::WebKeyboardEvent* web_event =
         static_cast<const blink::WebKeyboardEvent*>(&key_event);
-    std::unique_ptr<InputEvent> input_event =
-        std::make_unique<InputEvent>(*web_event, latency_info);
-    client_->ForwardKeyboardEvent(std::move(input_event),
-                                  key_event.skip_in_browser);
+    std::unique_ptr<content::InputEvent> input_event =
+        std::make_unique<content::InputEvent>(*web_event, latency_info);
+    host_->ForwardKeyboardEvent(std::move(input_event),
+                                key_event.skip_in_browser);
   }
   void ForwardKeyboardEventWithCommands(
-      const NativeWebKeyboardEvent& key_event,
+      const content::NativeWebKeyboardEvent& key_event,
       const ui::LatencyInfo& latency_info,
-      const std::vector<EditCommand>& commands) override {
+      const std::vector<content::EditCommand>& commands) override {
     const blink::WebKeyboardEvent* web_event =
         static_cast<const blink::WebKeyboardEvent*>(&key_event);
-    std::unique_ptr<InputEvent> input_event =
-        std::make_unique<InputEvent>(*web_event, latency_info);
-    client_->ForwardKeyboardEventWithCommands(
+    std::unique_ptr<content::InputEvent> input_event =
+        std::make_unique<content::InputEvent>(*web_event, latency_info);
+    host_->ForwardKeyboardEventWithCommands(
         std::move(input_event), key_event.skip_in_browser, commands);
   }
   void RouteOrProcessMouseEvent(
       const blink::WebMouseEvent& web_event) override {
-    client_->RouteOrProcessMouseEvent(TranslateEvent(web_event));
+    host_->RouteOrProcessMouseEvent(TranslateEvent(web_event));
   }
   void RouteOrProcessTouchEvent(
       const blink::WebTouchEvent& web_event) override {
-    client_->RouteOrProcessTouchEvent(TranslateEvent(web_event));
+    host_->RouteOrProcessTouchEvent(TranslateEvent(web_event));
   }
   void RouteOrProcessWheelEvent(
       const blink::WebMouseWheelEvent& web_event) override {
-    client_->RouteOrProcessWheelEvent(TranslateEvent(web_event));
+    host_->RouteOrProcessWheelEvent(TranslateEvent(web_event));
   }
   void ForwardMouseEvent(const blink::WebMouseEvent& web_event) override {
-    client_->ForwardMouseEvent(TranslateEvent(web_event));
+    host_->ForwardMouseEvent(TranslateEvent(web_event));
   }
   void ForwardWheelEvent(const blink::WebMouseWheelEvent& web_event) override {
-    client_->ForwardWheelEvent(TranslateEvent(web_event));
+    host_->ForwardWheelEvent(TranslateEvent(web_event));
   }
   void GestureBegin(blink::WebGestureEvent begin_event,
                     bool is_synthetically_injected) override {
     // The gesture type is not yet known, but assign a type to avoid
     // serialization asserts (the type will be stripped on the other side).
     begin_event.SetType(blink::WebInputEvent::kGestureScrollBegin);
-    client_->GestureBegin(TranslateEvent(begin_event),
-                          is_synthetically_injected);
+    host_->GestureBegin(TranslateEvent(begin_event), is_synthetically_injected);
   }
   void GestureUpdate(blink::WebGestureEvent update_event) override {
-    client_->GestureUpdate(TranslateEvent(update_event));
+    host_->GestureUpdate(TranslateEvent(update_event));
   }
   void GestureEnd(blink::WebGestureEvent end_event) override {
-    client_->GestureEnd(TranslateEvent(end_event));
+    host_->GestureEnd(TranslateEvent(end_event));
   }
   void SmartMagnify(const blink::WebGestureEvent& web_event) override {
-    client_->SmartMagnify(TranslateEvent(web_event));
+    host_->SmartMagnify(TranslateEvent(web_event));
   }
 
-  mojom::RenderWidgetHostNSViewClientAssociatedPtr client_;
-  std::unique_ptr<RenderWidgetHostNSViewBridgeLocal> bridge_;
+  mojom::RenderWidgetHostNSViewHostAssociatedPtr host_;
+  std::unique_ptr<RenderWidgetHostNSViewBridge> bridge_;
   base::scoped_nsobject<NSAccessibilityRemoteUIElement>
       remote_accessibility_element_;
 
   DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostNSViewBridgeOwner);
 };
-
 }
 
 void CreateRenderWidgetHostNSView(
     mojo::ScopedInterfaceEndpointHandle host_handle,
     mojo::ScopedInterfaceEndpointHandle view_request_handle) {
-  // Cast from the stub interface to the mojom::RenderWidgetHostNSViewClient
-  // and mojom::RenderWidgetHostNSViewBridge private interfaces.
+  // Cast from the stub interface to the mojom::RenderWidgetHostNSViewHost
+  // and mojom::RenderWidgetHostNSView private interfaces.
   // TODO(ccameron): Remove the need for this cast.
   // https://crbug.com/888290
-  mojom::RenderWidgetHostNSViewClientAssociatedPtr client(
-      mojo::AssociatedInterfacePtrInfo<mojom::RenderWidgetHostNSViewClient>(
+  mojom::RenderWidgetHostNSViewHostAssociatedPtr host(
+      mojo::AssociatedInterfacePtrInfo<mojom::RenderWidgetHostNSViewHost>(
           std::move(host_handle), 0));
-  mojom::RenderWidgetHostNSViewBridgeAssociatedRequest bridge_request(
+  mojom::RenderWidgetHostNSViewAssociatedRequest ns_view_request(
       std::move(view_request_handle));
 
   // Create a RenderWidgetHostNSViewBridgeOwner. The resulting object will be
   // destroyed when its underlying pipe is closed.
   ignore_result(new RenderWidgetHostNSViewBridgeOwner(
-      std::move(client), std::move(bridge_request)));
+      std::move(host), std::move(ns_view_request)));
 }
 
 void CreateWebContentsNSView(
     uint64_t view_id,
     mojo::ScopedInterfaceEndpointHandle host_handle,
     mojo::ScopedInterfaceEndpointHandle view_request_handle) {
-  mojom::WebContentsNSViewClientAssociatedPtr client(
-      mojo::AssociatedInterfacePtrInfo<mojom::WebContentsNSViewClient>(
+  mojom::WebContentsNSViewHostAssociatedPtr host(
+      mojo::AssociatedInterfacePtrInfo<mojom::WebContentsNSViewHost>(
           std::move(host_handle), 0));
-  mojom::WebContentsNSViewBridgeAssociatedRequest bridge_request(
+  mojom::WebContentsNSViewAssociatedRequest ns_view_request(
       std::move(view_request_handle));
   // Note that the resulting object will be destroyed when its underlying pipe
   // is closed.
   mojo::MakeStrongAssociatedBinding(
       std::make_unique<WebContentsNSViewBridge>(
-          view_id,
-          mojom::WebContentsNSViewClientAssociatedPtr(std::move(client))),
-      std::move(bridge_request),
+          view_id, mojom::WebContentsNSViewHostAssociatedPtr(std::move(host))),
+      std::move(ns_view_request),
       ui::WindowResizeHelperMac::Get()->task_runner());
 }
 
-}  // namespace content
+}  // namespace remote_cocoa
diff --git a/content/app_shim_remote_cocoa/popup_window_mac.h b/content/app_shim_remote_cocoa/popup_window_mac.h
index 518ffb8..92ffb12 100644
--- a/content/app_shim_remote_cocoa/popup_window_mac.h
+++ b/content/app_shim_remote_cocoa/popup_window_mac.h
@@ -13,7 +13,7 @@
 @class NSWindow;
 @class RenderWidgetHostViewCocoa;
 
-namespace content {
+namespace remote_cocoa {
 
 // Helper class for RHWVMacs that are initialized using InitAsPopup. Note that
 // this refers to UI that creates its own NSWindow, and does not refer to JS
@@ -35,6 +35,6 @@
   DISALLOW_COPY_AND_ASSIGN(PopupWindowMac);
 };
 
-}  // namespace content
+}  // namespace remote_cocoa
 
 #endif  // CONTENT_APP_SHIM_REMOTE_COCOA_POPUP_WINDOW_MAC_H_
diff --git a/content/app_shim_remote_cocoa/popup_window_mac.mm b/content/app_shim_remote_cocoa/popup_window_mac.mm
index 3125dfc..0a35063 100644
--- a/content/app_shim_remote_cocoa/popup_window_mac.mm
+++ b/content/app_shim_remote_cocoa/popup_window_mac.mm
@@ -80,7 +80,7 @@
 
 @end
 
-namespace content {
+namespace remote_cocoa {
 
 PopupWindowMac::PopupWindowMac(const gfx::Rect& content_rect,
                                RenderWidgetHostViewCocoa* cocoa_view)
@@ -115,4 +115,4 @@
   popup_window_.autorelease();
 }
 
-}  // namespace content
+}  // namespace remote_cocoa
diff --git a/content/app_shim_remote_cocoa/render_widget_host_ns_view_bridge_local.h b/content/app_shim_remote_cocoa/render_widget_host_ns_view_bridge.h
similarity index 80%
rename from content/app_shim_remote_cocoa/render_widget_host_ns_view_bridge_local.h
rename to content/app_shim_remote_cocoa/render_widget_host_ns_view_bridge.h
index 6f19cda..0ac0181 100644
--- a/content/app_shim_remote_cocoa/render_widget_host_ns_view_bridge_local.h
+++ b/content/app_shim_remote_cocoa/render_widget_host_ns_view_bridge.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_APP_SHIM_REMOTE_COCOA_RENDER_WIDGET_HOST_NS_VIEW_BRIDGE_LOCAL_H_
-#define CONTENT_APP_SHIM_REMOTE_COCOA_RENDER_WIDGET_HOST_NS_VIEW_BRIDGE_LOCAL_H_
+#ifndef CONTENT_APP_SHIM_REMOTE_COCOA_RENDER_WIDGET_HOST_NS_VIEW_BRIDGE_H_
+#define CONTENT_APP_SHIM_REMOTE_COCOA_RENDER_WIDGET_HOST_NS_VIEW_BRIDGE_H_
 
 #import <Cocoa/Cocoa.h>
 
@@ -16,31 +16,29 @@
 #include "ui/accelerated_widget_mac/display_ca_layer_tree.h"
 #include "ui/display/display_observer.h"
 
-namespace content {
+namespace remote_cocoa {
 
 // Mojo bridge for a RenderWidgetHostViewMac's NSView. This class may be
 // instantiated in the same process as its RenderWidgetHostViewMac, or it may
 // be in a different process.
-class RenderWidgetHostNSViewBridgeLocal
-    : public mojom::RenderWidgetHostNSViewBridge,
-      public display::DisplayObserver {
+class RenderWidgetHostNSViewBridge : public mojom::RenderWidgetHostNSView,
+                                     public display::DisplayObserver {
  public:
-  RenderWidgetHostNSViewBridgeLocal(
-      mojom::RenderWidgetHostNSViewClient* client,
-      RenderWidgetHostNSViewClientHelper* client_helper);
-  ~RenderWidgetHostNSViewBridgeLocal() override;
+  RenderWidgetHostNSViewBridge(mojom::RenderWidgetHostNSViewHost* client,
+                               RenderWidgetHostNSViewHostHelper* client_helper);
+  ~RenderWidgetHostNSViewBridge() override;
 
-  // Bind to a remote request for a bridge interface.
+  // Bind to a remote request for a mojo interface.
   void BindRequest(
-      mojom::RenderWidgetHostNSViewBridgeAssociatedRequest bridge_request);
+      mojom::RenderWidgetHostNSViewAssociatedRequest bridge_request);
 
   // TODO(ccameron): RenderWidgetHostViewMac and other functions currently use
   // this method to communicate directly with RenderWidgetHostViewCocoa. The
   // goal of this class is to eliminate this direct communication (so this
   // method is expected to go away).
-  RenderWidgetHostViewCocoa* GetRenderWidgetHostViewCocoa();
+  RenderWidgetHostViewCocoa* GetNSView();
 
-  // mojom::RenderWidgetHostNSViewBridge implementation.
+  // mojom::RenderWidgetHostNSView implementation.
   void InitAsPopup(const gfx::Rect& content_rect) override;
   void SetParentWebContentsNSView(uint64_t parent_ns_view_id) override;
   void DisableDisplay() override;
@@ -57,7 +55,7 @@
   void SetCompositionRangeInfo(const gfx::Range& range) override;
   void CancelComposition() override;
   void SetShowingContextMenu(bool showing) override;
-  void DisplayCursor(const WebCursor& cursor) override;
+  void DisplayCursor(const content::WebCursor& cursor) override;
   void SetCursorLocked(bool locked) override;
   void ShowDictionaryOverlayForSelection() override;
   void ShowDictionaryOverlay(
@@ -93,11 +91,11 @@
   base::string16 tooltip_text_;
 
   // The binding for this object (only used when remotely instantiated).
-  mojo::AssociatedBinding<mojom::RenderWidgetHostNSViewBridge> binding_;
+  mojo::AssociatedBinding<mojom::RenderWidgetHostNSView> binding_;
 
-  DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostNSViewBridgeLocal);
+  DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostNSViewBridge);
 };
 
-}  // namespace content
+}  // namespace remote_cocoa
 
-#endif  // CONTENT_APP_SHIM_REMOTE_COCOA_RENDER_WIDGET_HOST_NS_VIEW_BRIDGE_LOCAL_H_
+#endif  // CONTENT_APP_SHIM_REMOTE_COCOA_RENDER_WIDGET_HOST_NS_VIEW_BRIDGE_H_
diff --git a/content/app_shim_remote_cocoa/render_widget_host_ns_view_bridge_local.mm b/content/app_shim_remote_cocoa/render_widget_host_ns_view_bridge.mm
similarity index 77%
rename from content/app_shim_remote_cocoa/render_widget_host_ns_view_bridge_local.mm
rename to content/app_shim_remote_cocoa/render_widget_host_ns_view_bridge.mm
index 2aa04c3..758ed69e 100644
--- a/content/app_shim_remote_cocoa/render_widget_host_ns_view_bridge_local.mm
+++ b/content/app_shim_remote_cocoa/render_widget_host_ns_view_bridge.mm
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "content/app_shim_remote_cocoa/render_widget_host_ns_view_bridge_local.h"
+#import "content/app_shim_remote_cocoa/render_widget_host_ns_view_bridge.h"
 
 #include "base/mac/scoped_cftyperef.h"
 #include "base/strings/sys_string_conversions.h"
 #include "components/remote_cocoa/app_shim/ns_view_ids.h"
-#include "content/app_shim_remote_cocoa/render_widget_host_ns_view_client_helper.h"
+#include "content/app_shim_remote_cocoa/render_widget_host_ns_view_host_helper.h"
 #include "content/common/cursors/webcursor.h"
 #import "skia/ext/skia_utils_mac.h"
 #include "ui/accelerated_widget_mac/window_resize_helper_mac.h"
@@ -16,17 +16,17 @@
 #include "ui/events/keycodes/dom/dom_code.h"
 #include "ui/gfx/mac/coordinate_conversion.h"
 
-namespace content {
+namespace remote_cocoa {
 
-RenderWidgetHostNSViewBridgeLocal::RenderWidgetHostNSViewBridgeLocal(
-    mojom::RenderWidgetHostNSViewClient* client,
-    RenderWidgetHostNSViewClientHelper* client_helper)
+RenderWidgetHostNSViewBridge::RenderWidgetHostNSViewBridge(
+    mojom::RenderWidgetHostNSViewHost* host,
+    RenderWidgetHostNSViewHostHelper* host_helper)
     : binding_(this) {
   display::Screen::GetScreen()->AddObserver(this);
 
   cocoa_view_.reset([[RenderWidgetHostViewCocoa alloc]
-        initWithClient:client
-      withClientHelper:client_helper]);
+        initWithHost:host
+      withHostHelper:host_helper]);
 
   background_layer_.reset([[CALayer alloc] init]);
   display_ca_layer_tree_ =
@@ -35,8 +35,8 @@
   [cocoa_view_ setWantsLayer:YES];
 }
 
-RenderWidgetHostNSViewBridgeLocal::~RenderWidgetHostNSViewBridgeLocal() {
-  [cocoa_view_ setClientDisconnected];
+RenderWidgetHostNSViewBridge::~RenderWidgetHostNSViewBridge() {
+  [cocoa_view_ setHostDisconnected];
   // Do not immediately remove |cocoa_view_| from the NSView heirarchy, because
   // the call to -[NSView removeFromSuperview] may cause use to call into the
   // RWHVMac during tear-down, via WebContentsImpl::UpdateWebContentsVisibility.
@@ -49,23 +49,21 @@
   popup_window_.reset();
 }
 
-void RenderWidgetHostNSViewBridgeLocal::BindRequest(
-    mojom::RenderWidgetHostNSViewBridgeAssociatedRequest bridge_request) {
+void RenderWidgetHostNSViewBridge::BindRequest(
+    mojom::RenderWidgetHostNSViewAssociatedRequest bridge_request) {
   binding_.Bind(std::move(bridge_request),
                 ui::WindowResizeHelperMac::Get()->task_runner());
 }
 
-RenderWidgetHostViewCocoa*
-RenderWidgetHostNSViewBridgeLocal::GetRenderWidgetHostViewCocoa() {
+RenderWidgetHostViewCocoa* RenderWidgetHostNSViewBridge::GetNSView() {
   return cocoa_view_;
 }
 
-void RenderWidgetHostNSViewBridgeLocal::InitAsPopup(
-    const gfx::Rect& content_rect) {
+void RenderWidgetHostNSViewBridge::InitAsPopup(const gfx::Rect& content_rect) {
   popup_window_ = std::make_unique<PopupWindowMac>(content_rect, cocoa_view_);
 }
 
-void RenderWidgetHostNSViewBridgeLocal::SetParentWebContentsNSView(
+void RenderWidgetHostNSViewBridge::SetParentWebContentsNSView(
     uint64_t parent_ns_view_id) {
   NSView* parent_ns_view = remote_cocoa::GetNSViewFromId(parent_ns_view_id);
   // If the browser passed an invalid handle, then there is no recovery.
@@ -77,11 +75,11 @@
   [parent_ns_view addSubview:cocoa_view_];
 }
 
-void RenderWidgetHostNSViewBridgeLocal::MakeFirstResponder() {
+void RenderWidgetHostNSViewBridge::MakeFirstResponder() {
   [[cocoa_view_ window] makeFirstResponder:cocoa_view_];
 }
 
-void RenderWidgetHostNSViewBridgeLocal::DisableDisplay() {
+void RenderWidgetHostNSViewBridge::DisableDisplay() {
   if (display_disabled_)
     return;
   SetBackgroundColor(SK_ColorTRANSPARENT);
@@ -89,7 +87,7 @@
   display_disabled_ = true;
 }
 
-void RenderWidgetHostNSViewBridgeLocal::SetBounds(const gfx::Rect& rect) {
+void RenderWidgetHostNSViewBridge::SetBounds(const gfx::Rect& rect) {
   // |rect.size()| is view coordinates, |rect.origin| is screen coordinates,
   // TODO(thakis): fix, http://crbug.com/73362
 
@@ -130,14 +128,14 @@
   }
 }
 
-void RenderWidgetHostNSViewBridgeLocal::SetCALayerParams(
+void RenderWidgetHostNSViewBridge::SetCALayerParams(
     const gfx::CALayerParams& ca_layer_params) {
   if (display_disabled_)
     return;
   display_ca_layer_tree_->UpdateCALayerTree(ca_layer_params);
 }
 
-void RenderWidgetHostNSViewBridgeLocal::SetBackgroundColor(SkColor color) {
+void RenderWidgetHostNSViewBridge::SetBackgroundColor(SkColor color) {
   if (display_disabled_)
     return;
   ScopedCAActionDisabler disabler;
@@ -146,12 +144,12 @@
   [background_layer_ setBackgroundColor:cg_color];
 }
 
-void RenderWidgetHostNSViewBridgeLocal::SetVisible(bool visible) {
+void RenderWidgetHostNSViewBridge::SetVisible(bool visible) {
   ScopedCAActionDisabler disabler;
   [cocoa_view_ setHidden:!visible];
 }
 
-void RenderWidgetHostNSViewBridgeLocal::SetTooltipText(
+void RenderWidgetHostNSViewBridge::SetTooltipText(
     const base::string16& tooltip_text) {
   // Called from the renderer to tell us what the tooltip text should be. It
   // calls us frequently so we need to cache the value to prevent doing a lot
@@ -175,25 +173,24 @@
   [cocoa_view_ setToolTipAtMousePoint:tooltip_nsstring];
 }
 
-void RenderWidgetHostNSViewBridgeLocal::SetCompositionRangeInfo(
+void RenderWidgetHostNSViewBridge::SetCompositionRangeInfo(
     const gfx::Range& range) {
   [cocoa_view_ setCompositionRange:range];
   [cocoa_view_ setMarkedRange:range.ToNSRange()];
 }
 
-void RenderWidgetHostNSViewBridgeLocal::CancelComposition() {
+void RenderWidgetHostNSViewBridge::CancelComposition() {
   [cocoa_view_ cancelComposition];
 }
 
-void RenderWidgetHostNSViewBridgeLocal::SetTextInputType(
+void RenderWidgetHostNSViewBridge::SetTextInputType(
     ui::TextInputType text_input_type) {
   [cocoa_view_ setTextInputType:text_input_type];
 }
 
-void RenderWidgetHostNSViewBridgeLocal::SetTextSelection(
-    const base::string16& text,
-    uint64_t offset,
-    const gfx::Range& range) {
+void RenderWidgetHostNSViewBridge::SetTextSelection(const base::string16& text,
+                                                    uint64_t offset,
+                                                    const gfx::Range& range) {
   [cocoa_view_ setTextSelectionText:text offset:offset range:range];
   // Updates markedRange when there is no marked text so that retrieving
   // markedRange immediately after calling setMarkdText: returns the current
@@ -203,11 +200,11 @@
   }
 }
 
-void RenderWidgetHostNSViewBridgeLocal::SetShowingContextMenu(bool showing) {
+void RenderWidgetHostNSViewBridge::SetShowingContextMenu(bool showing) {
   [cocoa_view_ setShowingContextMenu:showing];
 }
 
-void RenderWidgetHostNSViewBridgeLocal::OnDisplayMetricsChanged(
+void RenderWidgetHostNSViewBridge::OnDisplayMetricsChanged(
     const display::Display& display,
     uint32_t changed_metrics) {
   // Note that -updateScreenProperties is also be called by the notification
@@ -216,21 +213,22 @@
   [cocoa_view_ updateScreenProperties];
 }
 
-void RenderWidgetHostNSViewBridgeLocal::DisplayCursor(const WebCursor& cursor) {
-  WebCursor non_const_cursor(cursor);
+void RenderWidgetHostNSViewBridge::DisplayCursor(
+    const content::WebCursor& cursor) {
+  content::WebCursor non_const_cursor(cursor);
   [cocoa_view_ updateCursor:non_const_cursor.GetNativeCursor()];
 }
 
-void RenderWidgetHostNSViewBridgeLocal::SetCursorLocked(bool locked) {
+void RenderWidgetHostNSViewBridge::SetCursorLocked(bool locked) {
   [cocoa_view_ setCursorLocked:locked];
 }
 
-void RenderWidgetHostNSViewBridgeLocal::ShowDictionaryOverlayForSelection() {
+void RenderWidgetHostNSViewBridge::ShowDictionaryOverlayForSelection() {
   NSRange selection_range = [cocoa_view_ selectedRange];
   [cocoa_view_ showLookUpDictionaryOverlayFromRange:selection_range];
 }
 
-void RenderWidgetHostNSViewBridgeLocal::ShowDictionaryOverlay(
+void RenderWidgetHostNSViewBridge::ShowDictionaryOverlay(
     const mac::AttributedStringCoder::EncodedString& encoded_string,
     const gfx::Point& baseline_point) {
   NSAttributedString* string =
@@ -238,13 +236,14 @@
   if ([string length] == 0)
     return;
   NSPoint flipped_baseline_point = {
-      baseline_point.x(), [cocoa_view_ frame].size.height - baseline_point.y(),
+      baseline_point.x(),
+      [cocoa_view_ frame].size.height - baseline_point.y(),
   };
   [cocoa_view_ showDefinitionForAttributedString:string
                                          atPoint:flipped_baseline_point];
 }
 
-void RenderWidgetHostNSViewBridgeLocal::LockKeyboard(
+void RenderWidgetHostNSViewBridge::LockKeyboard(
     const base::Optional<std::vector<uint32_t>>& uint_dom_codes) {
   base::Optional<base::flat_set<ui::DomCode>> dom_codes;
   if (uint_dom_codes) {
@@ -255,8 +254,8 @@
   [cocoa_view_ lockKeyboard:std::move(dom_codes)];
 }
 
-void RenderWidgetHostNSViewBridgeLocal::UnlockKeyboard() {
+void RenderWidgetHostNSViewBridge::UnlockKeyboard() {
   [cocoa_view_ unlockKeyboard];
 }
 
-}  // namespace content
+}  // namespace remote_cocoa
diff --git a/content/app_shim_remote_cocoa/render_widget_host_ns_view_client_helper.h b/content/app_shim_remote_cocoa/render_widget_host_ns_view_host_helper.h
similarity index 79%
rename from content/app_shim_remote_cocoa/render_widget_host_ns_view_client_helper.h
rename to content/app_shim_remote_cocoa/render_widget_host_ns_view_host_helper.h
index 757f0c4..9bbc9736 100644
--- a/content/app_shim_remote_cocoa/render_widget_host_ns_view_client_helper.h
+++ b/content/app_shim_remote_cocoa/render_widget_host_ns_view_host_helper.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_APP_SHIM_REMOTE_COCOA_RENDER_WIDGET_HOST_NS_VIEW_CLIENT_HELPER_H_
-#define CONTENT_APP_SHIM_REMOTE_COCOA_RENDER_WIDGET_HOST_NS_VIEW_CLIENT_HELPER_H_
+#ifndef CONTENT_APP_SHIM_REMOTE_COCOA_RENDER_WIDGET_HOST_NS_VIEW_HOST_HELPER_H_
+#define CONTENT_APP_SHIM_REMOTE_COCOA_RENDER_WIDGET_HOST_NS_VIEW_HOST_HELPER_H_
 
 #include "base/macros.h"
 
@@ -21,24 +21,26 @@
 }  // namespace ui
 
 namespace content {
-
-namespace mojom {
-class RenderWidgetHostNSViewClient;
-}  // namespace mojom
-
 struct EditCommand;
 struct NativeWebKeyboardEvent;
+}  // namespace content
+
+namespace remote_cocoa {
+
+namespace mojom {
+class RenderWidgetHostNSViewHost;
+}  // namespace mojom
 
 // An interface through which the NSView for a RenderWidgetHostViewMac is to
 // communicate with the RenderWidgetHostViewMac (potentially in another
-// process). Unlike mojom::RenderWidgetHostNSViewClient, this object is always
+// process). Unlike mojom::RenderWidgetHostNSViewHost, this object is always
 // instantiated in the local process. This is to implement functions that
 // cannot be sent across mojo or to avoid unnecessary translation of event
 // types.
-class RenderWidgetHostNSViewClientHelper {
+class RenderWidgetHostNSViewHostHelper {
  public:
-  RenderWidgetHostNSViewClientHelper() {}
-  virtual ~RenderWidgetHostNSViewClientHelper() {}
+  RenderWidgetHostNSViewHostHelper() {}
+  virtual ~RenderWidgetHostNSViewHostHelper() {}
 
   // Return the RenderWidget's BrowserAccessibilityManager's root accessibility
   // node.
@@ -52,12 +54,13 @@
 
   // Forward a keyboard event to the RenderWidgetHost that is currently handling
   // the key-down event.
-  virtual void ForwardKeyboardEvent(const NativeWebKeyboardEvent& key_event,
-                                    const ui::LatencyInfo& latency_info) = 0;
+  virtual void ForwardKeyboardEvent(
+      const content::NativeWebKeyboardEvent& key_event,
+      const ui::LatencyInfo& latency_info) = 0;
   virtual void ForwardKeyboardEventWithCommands(
-      const NativeWebKeyboardEvent& key_event,
+      const content::NativeWebKeyboardEvent& key_event,
       const ui::LatencyInfo& latency_info,
-      const std::vector<EditCommand>& commands) = 0;
+      const std::vector<content::EditCommand>& commands) = 0;
 
   // Forward events to the renderer or the input router, as appropriate.
   virtual void RouteOrProcessMouseEvent(
@@ -81,9 +84,9 @@
       const blink::WebGestureEvent& smart_magnify_event) = 0;
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostNSViewClientHelper);
+  DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostNSViewHostHelper);
 };
 
-}  // namespace content
+}  // namespace remote_cocoa
 
-#endif  // CONTENT_APP_SHIM_REMOTE_COCOA_RENDER_WIDGET_HOST_NS_VIEW_CLIENT_HELPER_H_
+#endif  // CONTENT_APP_SHIM_REMOTE_COCOA_RENDER_WIDGET_HOST_NS_VIEW_HOST_HELPER_H_
diff --git a/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.h b/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.h
index b8eb522..0013b7c 100644
--- a/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.h
+++ b/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.h
@@ -24,27 +24,29 @@
 
 namespace blink {
 class WebGestureEvent;
-}
+}  // namespace blink
 
 namespace content {
-namespace mojom {
-class RenderWidgetHostNSViewClient;
-}
-
-class RenderWidgetHostNSViewClientHelper;
 class RenderWidgetHostViewMac;
 class RenderWidgetHostViewMacEditCommandHelper;
-}
+}  // namespace content
+
+namespace remote_cocoa {
+namespace mojom {
+class RenderWidgetHostNSViewHost;
+}  // namespace mojom
+class RenderWidgetHostNSViewHostHelper;
+}  // namespace remote_cocoa
 
 namespace ui {
 enum class DomCode;
 struct DidOverscrollParams;
-}
+}  // namespace ui
 
 @protocol RenderWidgetHostViewMacDelegate;
 
-@protocol RenderWidgetHostNSViewClientOwner
-- (content::mojom::RenderWidgetHostNSViewClient*)renderWidgetHostNSViewClient;
+@protocol RenderWidgetHostNSViewHostOwner
+- (remote_cocoa::mojom::RenderWidgetHostNSViewHost*)renderWidgetHostNSViewHost;
 @end
 
 // This is the view that lives in the Cocoa view hierarchy. In Windows-land,
@@ -54,29 +56,29 @@
 // TODO(ccameron): Hide this interface behind RenderWidgetHostNSViewBridge.
 @interface RenderWidgetHostViewCocoa
     : ToolTipBaseView <CommandDispatcherTarget,
-                       RenderWidgetHostNSViewClientOwner,
+                       RenderWidgetHostNSViewHostOwner,
                        NSCandidateListTouchBarItemDelegate,
                        NSTextInputClient,
                        NSAccessibility> {
  @private
   // The communications channel to the RenderWidgetHostViewMac. This pointer is
-  // always valid. When the original client disconnects, |client_| is changed to
-  // point to |dummyClient_|, to avoid having to preface every dereference with
+  // always valid. When the original host disconnects, |host_| is changed to
+  // point to |dummyHost_|, to avoid having to preface every dereference with
   // a nullptr check.
-  content::mojom::RenderWidgetHostNSViewClient* client_;
+  remote_cocoa::mojom::RenderWidgetHostNSViewHost* host_;
 
-  // A separate client interface for the parts of the interface to
+  // A separate host interface for the parts of the interface to
   // RenderWidgetHostViewMac that cannot or should not be forwarded over mojo.
   // This includes events (where the extra translation is unnecessary or loses
   // information) and access to accessibility structures (only present in the
   // browser process).
-  content::RenderWidgetHostNSViewClientHelper* clientHelper_;
+  remote_cocoa::RenderWidgetHostNSViewHostHelper* hostHelper_;
 
-  // Dummy client and client helper that are always valid (see above comments
-  // about client_).
-  content::mojom::RenderWidgetHostNSViewClientPtr dummyClient_;
-  std::unique_ptr<content::RenderWidgetHostNSViewClientHelper>
-      dummyClientHelper_;
+  // Dummy host and host helper that are always valid (see above comments
+  // about host_).
+  remote_cocoa::mojom::RenderWidgetHostNSViewHostPtr dummyHost_;
+  std::unique_ptr<remote_cocoa::RenderWidgetHostNSViewHostHelper>
+      dummyHostHelper_;
 
   // This ivar is the cocoa delegate of the NSResponder.
   base::scoped_nsobject<NSObject<RenderWidgetHostViewMacDelegate>>
@@ -98,7 +100,7 @@
   // Controlled by setShowingContextMenu.
   BOOL showingContextMenu_;
 
-  // Set during -setFrame to avoid spamming client_ with origin and size
+  // Set during -setFrame to avoid spamming host_ with origin and size
   // changes.
   BOOL inSetFrame_;
 
@@ -228,8 +230,8 @@
 
 - (void)setCanBeKeyView:(BOOL)can;
 - (void)setCloseOnDeactivate:(BOOL)b;
-// Inidicate that the client was destroyed and can't be called back into.
-- (void)setClientDisconnected;
+// Inidicate that the host was destroyed and can't be called back into.
+- (void)setHostDisconnected;
 // True for always-on-top special windows (e.g. Balloons and Panels).
 - (BOOL)acceptsMouseEventsWhenInactive;
 // Cancel ongoing composition (abandon the marked text).
@@ -242,7 +244,7 @@
 - (void)showLookUpDictionaryOverlayFromRange:(NSRange)range;
 - (BOOL)suppressNextKeyUpForTesting:(int)keyCode;
 // Query the display::Display from the view's NSWindow's NSScreen and forward
-// it to the RenderWidgetHostNSViewClient (only if the screen is non-nil).
+// it to the RenderWidgetHostNSViewHost (only if the screen is non-nil).
 - (void)updateScreenProperties;
 // Indicate if the embedding WebContents is showing a web content context menu.
 - (void)setShowingContextMenu:(BOOL)showing;
@@ -268,8 +270,8 @@
 - (void)setAccessibilityParentElement:(id)accessibilityParent;
 
 // Methods previously marked as private.
-- (id)initWithClient:(content::mojom::RenderWidgetHostNSViewClient*)client
-    withClientHelper:(content::RenderWidgetHostNSViewClientHelper*)clientHelper;
+- (id)initWithHost:(remote_cocoa::mojom::RenderWidgetHostNSViewHost*)host
+    withHostHelper:(remote_cocoa::RenderWidgetHostNSViewHostHelper*)hostHelper;
 - (void)setResponderDelegate:
     (NSObject<RenderWidgetHostViewMacDelegate>*)delegate;
 - (void)processedGestureScrollEvent:(const blink::WebGestureEvent&)event
diff --git a/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm b/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm
index 8284524..188fe917 100644
--- a/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm
+++ b/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm
@@ -38,8 +38,6 @@
 using content::EditCommand;
 using content::InputEvent;
 using content::NativeWebKeyboardEvent;
-using content::mojom::RenderWidgetHostNSViewClient;
-using content::RenderWidgetHostNSViewClientHelper;
 using content::RenderWidgetHostViewMacEditCommandHelper;
 using content::WebGestureEventBuilder;
 using content::WebMouseEventBuilder;
@@ -50,17 +48,19 @@
 using blink::WebMouseWheelEvent;
 using blink::WebGestureEvent;
 using blink::WebTouchEvent;
+using remote_cocoa::mojom::RenderWidgetHostNSViewHost;
+using remote_cocoa::RenderWidgetHostNSViewHostHelper;
 
 namespace {
 
-// A dummy RenderWidgetHostNSViewClientHelper implementation which no-ops all
+// A dummy RenderWidgetHostNSViewHostHelper implementation which no-ops all
 // functions.
-class DummyClientHelper : public RenderWidgetHostNSViewClientHelper {
+class DummyHostHelper : public RenderWidgetHostNSViewHostHelper {
  public:
-  explicit DummyClientHelper() {}
+  explicit DummyHostHelper() {}
 
  private:
-  // RenderWidgetHostNSViewClientHelper implementation.
+  // RenderWidgetHostNSViewHostHelper implementation.
   id GetRootBrowserAccessibilityElement() override { return nil; }
   id GetFocusedBrowserAccessibilityElement() override { return nil; }
   void SetAccessibilityWindow(NSWindow* window) override {}
@@ -84,7 +84,7 @@
   void GestureEnd(blink::WebGestureEvent end_event) override {}
   void SmartMagnify(const blink::WebGestureEvent& web_event) override {}
 
-  DISALLOW_COPY_AND_ASSIGN(DummyClientHelper);
+  DISALLOW_COPY_AND_ASSIGN(DummyHostHelper);
 };
 
 // Touch bar identifier.
@@ -167,10 +167,10 @@
 - (void)windowChangedGlobalFrame:(NSNotification*)notification;
 - (void)windowDidBecomeKey:(NSNotification*)notification;
 - (void)windowDidResignKey:(NSNotification*)notification;
-- (void)sendViewBoundsInWindowToClient;
+- (void)sendViewBoundsInWindowToHost;
 - (void)requestTextSuggestions API_AVAILABLE(macos(10.12.2));
-- (void)sendWindowFrameInScreenToClient;
-- (bool)clientIsDisconnected;
+- (void)sendWindowFrameInScreenToHost;
+- (bool)hostIsDisconnected;
 - (void)invalidateTouchBar API_AVAILABLE(macos(10.12.2));
 
 // NSCandidateListTouchBarItemDelegate implementation
@@ -187,16 +187,16 @@
 @synthesize textInputType = textInputType_;
 @synthesize spellCheckerForTesting = spellCheckerForTesting_;
 
-- (id)initWithClient:(RenderWidgetHostNSViewClient*)client
-    withClientHelper:(RenderWidgetHostNSViewClientHelper*)clientHelper {
+- (id)initWithHost:(RenderWidgetHostNSViewHost*)host
+    withHostHelper:(RenderWidgetHostNSViewHostHelper*)hostHelper {
   self = [super initWithFrame:NSZeroRect];
   if (self) {
     self.acceptsTouchEvents = YES;
     editCommandHelper_.reset(new RenderWidgetHostViewMacEditCommandHelper);
     editCommandHelper_->AddEditingSelectorsToClass([self class]);
 
-    client_ = client;
-    clientHelper_ = clientHelper;
+    host_ = host;
+    hostHelper_ = hostHelper;
     canBeKeyView_ = YES;
     isStylusEnteringProximity_ = false;
     keyboardLockActive_ = false;
@@ -209,7 +209,7 @@
 }
 
 - (void)dealloc {
-  DCHECK([self clientIsDisconnected]);
+  DCHECK([self hostIsDisconnected]);
   [[NSNotificationCenter defaultCenter] removeObserver:self];
 
   // Update and cache the new input context. Otherwise,
@@ -222,16 +222,16 @@
   [super dealloc];
 }
 
-- (void)sendViewBoundsInWindowToClient {
+- (void)sendViewBoundsInWindowToHost {
   TRACE_EVENT0("browser",
-               "RenderWidgetHostViewCocoa::sendViewBoundsInWindowToClient");
+               "RenderWidgetHostViewCocoa::sendViewBoundsInWindowToHost");
   if (inSetFrame_)
     return;
 
   NSRect viewBoundsInView = [self bounds];
   NSWindow* enclosingWindow = [self window];
   if (!enclosingWindow) {
-    client_->OnBoundsInWindowChanged(gfx::Rect(viewBoundsInView), false);
+    host_->OnBoundsInWindowChanged(gfx::Rect(viewBoundsInView), false);
     return;
   }
 
@@ -239,7 +239,7 @@
   gfx::Rect gfxViewBoundsInWindow(viewBoundsInWindow);
   gfxViewBoundsInWindow.set_y(NSHeight([enclosingWindow frame]) -
                               NSMaxY(viewBoundsInWindow));
-  client_->OnBoundsInWindowChanged(gfxViewBoundsInWindow, true);
+  host_->OnBoundsInWindowChanged(gfxViewBoundsInWindow, true);
 }
 
 - (void)requestTextSuggestions {
@@ -333,13 +333,13 @@
   compositionRange_ = range;
 }
 
-- (void)sendWindowFrameInScreenToClient {
+- (void)sendWindowFrameInScreenToHost {
   TRACE_EVENT0("browser",
-               "RenderWidgetHostViewCocoa::sendWindowFrameInScreenToClient");
+               "RenderWidgetHostViewCocoa::sendWindowFrameInScreenToHost");
   NSWindow* enclosingWindow = [self window];
   if (!enclosingWindow)
     return;
-  client_->OnWindowFrameInScreenChanged(
+  host_->OnWindowFrameInScreenChanged(
       gfx::ScreenRectFromNSRect([enclosingWindow frame]));
 }
 
@@ -410,27 +410,27 @@
   closeOnDeactivate_ = b;
 }
 
-- (void)setClientDisconnected {
-  // Set the client to be an abandoned message pipe, and set the clientHelper
-  // to forward messages to that client.
-  content::mojom::RenderWidgetHostNSViewClientRequest dummyClientRequest =
-      mojo::MakeRequest(&dummyClient_);
-  dummyClientHelper_ = std::make_unique<DummyClientHelper>();
-  client_ = dummyClient_.get();
-  clientHelper_ = dummyClientHelper_.get();
+- (void)setHostDisconnected {
+  // Set the host to be an abandoned message pipe, and set the hostHelper
+  // to forward messages to that host.
+  remote_cocoa::mojom::RenderWidgetHostNSViewHostRequest dummyHostRequest =
+      mojo::MakeRequest(&dummyHost_);
+  dummyHostHelper_ = std::make_unique<DummyHostHelper>();
+  host_ = dummyHost_.get();
+  hostHelper_ = dummyHostHelper_.get();
 
   // |responderDelegate_| may attempt to access the RenderWidgetHostViewMac
   // through its internal pointers, so detach it here.
-  // TODO(ccameron): Force |responderDelegate_| to use the |client_| as well,
-  // and the viewGone method to clientGone.
+  // TODO(ccameron): Force |responderDelegate_| to use the |host_| as well,
+  // and the viewGone method to hostGone.
   if (responderDelegate_ &&
       [responderDelegate_ respondsToSelector:@selector(viewGone:)])
     [responderDelegate_ viewGone:self];
   responderDelegate_.reset();
 }
 
-- (bool)clientIsDisconnected {
-  return client_ == dummyClient_.get();
+- (bool)hostIsDisconnected {
+  return host_ == dummyHost_.get();
 }
 
 - (void)setShowingContextMenu:(BOOL)showing {
@@ -460,7 +460,7 @@
   WebMouseEvent web_event = WebMouseEventBuilder::Build(event, self);
   web_event.SetModifiers(web_event.GetModifiers() |
                          WebInputEvent::kRelativeMotionEvent);
-  clientHelper_->ForwardMouseEvent(web_event);
+  hostHelper_->ForwardMouseEvent(web_event);
 }
 
 - (BOOL)shouldIgnoreMouseEvent:(NSEvent*)theEvent {
@@ -556,7 +556,7 @@
           WebMouseEventBuilder::Build(theEvent, self, pointerType_);
       exitEvent.SetType(WebInputEvent::kMouseLeave);
       exitEvent.button = WebMouseEvent::Button::kNoButton;
-      clientHelper_->ForwardMouseEvent(exitEvent);
+      hostHelper_->ForwardMouseEvent(exitEvent);
     }
     mouseEventWasIgnored_ = YES;
     [self updateCursor:nil];
@@ -570,14 +570,14 @@
         WebMouseEventBuilder::Build(theEvent, self, pointerType_);
     enterEvent.SetType(WebInputEvent::kMouseMove);
     enterEvent.button = WebMouseEvent::Button::kNoButton;
-    clientHelper_->RouteOrProcessMouseEvent(enterEvent);
+    hostHelper_->RouteOrProcessMouseEvent(enterEvent);
   }
   mouseEventWasIgnored_ = NO;
 
   // Don't cancel child popups; killing them on a mouse click would prevent the
   // user from positioning the insertion point in the text field spawning the
   // popup. A click outside the text field would cause the text field to drop
-  // the focus, and then EditorClientImpl::textFieldDidEndEditing() would cancel
+  // the focus, and then EditorHostImpl::textFieldDidEndEditing() would cancel
   // the popup anyway, so we're OK.
   if (type == NSLeftMouseDown)
     hasOpenMouseDown_ = YES;
@@ -625,10 +625,10 @@
     WebMouseEvent event =
         WebMouseEventBuilder::Build(theEvent, self, pointerType_);
     last_mouse_screen_position_ = event.PositionInScreen();
-    clientHelper_->RouteOrProcessMouseEvent(event);
+    hostHelper_->RouteOrProcessMouseEvent(event);
   } else {
     WebTouchEvent event = WebTouchEventBuilder::Build(theEvent, self);
-    clientHelper_->RouteOrProcessTouchEvent(event);
+    hostHelper_->RouteOrProcessTouchEvent(event);
   }
 }
 
@@ -783,10 +783,10 @@
       return;
   }
 
-  // Tell the client that we are beginning a keyboard event. This ensures that
+  // Tell the host that we are beginning a keyboard event. This ensures that
   // all event and Ime messages target the same RenderWidgetHost throughout this
   // function call.
-  client_->BeginKeyboardEvent();
+  host_->BeginKeyboardEvent();
 
   bool shouldAutohideCursor = textInputType_ != ui::TEXT_INPUT_TYPE_NONE &&
                               eventType == NSKeyDown &&
@@ -794,7 +794,7 @@
 
   // We only handle key down events and just simply forward other events.
   if (eventType != NSKeyDown) {
-    clientHelper_->ForwardKeyboardEvent(event, latency_info);
+    hostHelper_->ForwardKeyboardEvent(event, latency_info);
 
     // Possibly autohide the cursor.
     if (shouldAutohideCursor) {
@@ -802,7 +802,7 @@
       cursorHidden_ = YES;
     }
 
-    client_->EndKeyboardEvent();
+    host_->EndKeyboardEvent();
     return;
   }
 
@@ -852,7 +852,7 @@
     NativeWebKeyboardEvent fakeEvent = event;
     fakeEvent.windows_key_code = 0xE5;  // VKEY_PROCESSKEY
     fakeEvent.skip_in_browser = true;
-    clientHelper_->ForwardKeyboardEvent(fakeEvent, latency_info);
+    hostHelper_->ForwardKeyboardEvent(fakeEvent, latency_info);
     // If this key event was handled by the input method, but
     // -doCommandBySelector: (invoked by the call to -interpretKeyEvents: above)
     // enqueued edit commands, then in order to let webkit handle them
@@ -863,8 +863,8 @@
     if (hasEditCommands_ && !hasMarkedText_)
       delayEventUntilAfterImeCompostion = YES;
   } else {
-    clientHelper_->ForwardKeyboardEventWithCommands(event, latency_info,
-                                                    editCommands_);
+    hostHelper_->ForwardKeyboardEventWithCommands(event, latency_info,
+                                                  editCommands_);
   }
 
   // Then send keypress and/or composition related events.
@@ -881,7 +881,7 @@
   BOOL textInserted = NO;
   if (textToBeInserted_.length() >
       ((hasMarkedText_ || oldHasMarkedText) ? 0u : 1u)) {
-    client_->ImeCommitText(textToBeInserted_, gfx::Range::InvalidRange());
+    host_->ImeCommitText(textToBeInserted_, gfx::Range::InvalidRange());
     textInserted = YES;
   }
 
@@ -892,15 +892,15 @@
     // composition node in WebKit.
     // When marked text is available, |markedTextSelectedRange_| will be the
     // range being selected inside the marked text.
-    client_->ImeSetComposition(markedText_, ime_text_spans_,
-                               setMarkedTextReplacementRange_,
-                               markedTextSelectedRange_.location,
-                               NSMaxRange(markedTextSelectedRange_));
+    host_->ImeSetComposition(markedText_, ime_text_spans_,
+                             setMarkedTextReplacementRange_,
+                             markedTextSelectedRange_.location,
+                             NSMaxRange(markedTextSelectedRange_));
   } else if (oldHasMarkedText && !hasMarkedText_ && !textInserted) {
     if (unmarkTextCalled_) {
-      client_->ImeFinishComposingText();
+      host_->ImeFinishComposingText();
     } else {
-      client_->ImeCancelCompositionFromCocoa();
+      host_->ImeCancelCompositionFromCocoa();
     }
   }
 
@@ -922,8 +922,8 @@
     fakeEvent.skip_in_browser = true;
     ui::LatencyInfo fake_event_latency_info = latency_info;
     fake_event_latency_info.set_source_event_type(ui::SourceEventType::OTHER);
-    clientHelper_->ForwardKeyboardEvent(fakeEvent, fake_event_latency_info);
-    clientHelper_->ForwardKeyboardEventWithCommands(
+    hostHelper_->ForwardKeyboardEvent(fakeEvent, fake_event_latency_info);
+    hostHelper_->ForwardKeyboardEventWithCommands(
         event, fake_event_latency_info, editCommands_);
   }
 
@@ -937,7 +937,7 @@
       event.text[0] = textToBeInserted_[0];
       event.text[1] = 0;
       event.skip_in_browser = true;
-      clientHelper_->ForwardKeyboardEvent(event, latency_info);
+      hostHelper_->ForwardKeyboardEvent(event, latency_info);
     } else if ((!textInserted || delayEventUntilAfterImeCompostion) &&
                event.text[0] != '\0' &&
                ((modifierFlags & kCtrlCmdKeyMask) ||
@@ -947,7 +947,7 @@
       // cases, unless the key event generated any other command.
       event.SetType(blink::WebInputEvent::kChar);
       event.skip_in_browser = true;
-      clientHelper_->ForwardKeyboardEvent(event, latency_info);
+      hostHelper_->ForwardKeyboardEvent(event, latency_info);
     }
   }
 
@@ -957,7 +957,7 @@
     cursorHidden_ = YES;
   }
 
-  client_->EndKeyboardEvent();
+  host_->EndKeyboardEvent();
 }
 
 - (BOOL)suppressNextKeyUpForTesting:(int)keyCode {
@@ -978,7 +978,7 @@
   // History-swiping is not possible if the logic reaches this point.
   WebMouseWheelEvent webEvent = WebMouseWheelEventBuilder::Build(event, self);
   webEvent.rails_mode = mouseWheelFilter_.UpdateRailsMode(webEvent);
-  clientHelper_->ForwardWheelEvent(webEvent);
+  hostHelper_->ForwardWheelEvent(webEvent);
 
   if (endWheelMonitor_) {
     [NSEvent removeMonitor:endWheelMonitor_];
@@ -992,7 +992,7 @@
 
   WebGestureEvent gestureBeginEvent(WebGestureEventBuilder::Build(event, self));
 
-  clientHelper_->GestureBegin(gestureBeginEvent, isSyntheticallyInjected);
+  hostHelper_->GestureBegin(gestureBeginEvent, isSyntheticallyInjected);
 }
 
 - (void)handleEndGestureWithEvent:(NSEvent*)event {
@@ -1007,7 +1007,7 @@
     endEvent.SetType(WebInputEvent::kGesturePinchEnd);
     endEvent.SetSourceDevice(blink::WebGestureDevice::kTouchpad);
     endEvent.SetNeedsWheelEvent(true);
-    clientHelper_->GestureEnd(endEvent);
+    hostHelper_->GestureEnd(endEvent);
   }
 }
 
@@ -1062,11 +1062,11 @@
 - (void)smartMagnifyWithEvent:(NSEvent*)event {
   const WebGestureEvent& smartMagnifyEvent =
       WebGestureEventBuilder::Build(event, self);
-  clientHelper_->SmartMagnify(smartMagnifyEvent);
+  hostHelper_->SmartMagnify(smartMagnifyEvent);
 }
 
 - (void)showLookUpDictionaryOverlayFromRange:(NSRange)range {
-  client_->LookUpDictionaryOverlayFromRange(gfx::Range(range));
+  host_->LookUpDictionaryOverlayFromRange(gfx::Range(range));
 }
 
 // This is invoked only on 10.8 or newer when the user taps a word using
@@ -1074,7 +1074,7 @@
 - (void)quickLookWithEvent:(NSEvent*)event {
   NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
   gfx::PointF rootPoint(point.x, NSHeight([self frame]) - point.y);
-  client_->LookUpDictionaryOverlayAtPoint(rootPoint);
+  host_->LookUpDictionaryOverlayAtPoint(rootPoint);
 }
 
 // This method handles 2 different types of hardware events.
@@ -1144,7 +1144,7 @@
   // This is responsible for content scrolling!
   WebMouseWheelEvent webEvent = WebMouseWheelEventBuilder::Build(event, self);
   webEvent.rails_mode = mouseWheelFilter_.UpdateRailsMode(webEvent);
-  clientHelper_->RouteOrProcessWheelEvent(webEvent);
+  hostHelper_->RouteOrProcessWheelEvent(webEvent);
 }
 
 // Called repeatedly during a pinch gesture, with incremental change values.
@@ -1177,7 +1177,7 @@
   }
 
   WebGestureEvent updateEvent = WebGestureEventBuilder::Build(event, self);
-  clientHelper_->GestureUpdate(updateEvent);
+  hostHelper_->GestureUpdate(updateEvent);
 }
 
 - (void)viewWillMoveToWindow:(NSWindow*)newWindow {
@@ -1228,8 +1228,8 @@
                              object:newWindow];
   }
 
-  clientHelper_->SetAccessibilityWindow(newWindow);
-  [self sendWindowFrameInScreenToClient];
+  hostHelper_->SetAccessibilityWindow(newWindow);
+  [self sendWindowFrameInScreenToHost];
 }
 
 - (void)updateScreenProperties {
@@ -1238,10 +1238,10 @@
     return;
 
   // TODO(ccameron): This will call [enclosingWindow screen], which may return
-  // nil. Do that call here to avoid sending bogus display info to the client.
+  // nil. Do that call here to avoid sending bogus display info to the host.
   display::Display display =
       display::Screen::GetScreen()->GetDisplayNearestView(self);
-  client_->OnDisplayChanged(display);
+  host_->OnDisplayChanged(display);
 }
 
 // This will be called when the NSView's NSWindow moves from one NSScreen to
@@ -1257,42 +1257,42 @@
 }
 
 - (void)windowChangedGlobalFrame:(NSNotification*)notification {
-  [self sendWindowFrameInScreenToClient];
+  [self sendWindowFrameInScreenToHost];
   // Update the view bounds relative to the window, as they may have changed
   // during layout, and we don't explicitly listen for re-layout of parent
   // views.
-  [self sendViewBoundsInWindowToClient];
+  [self sendViewBoundsInWindowToHost];
 }
 
 - (void)setFrame:(NSRect)r {
   // Note that -setFrame: calls through -setFrameSize: and -setFrameOrigin. To
-  // avoid spamming the client with transiently invalid states, only send one
+  // avoid spamming the host with transiently invalid states, only send one
   // message at the end.
   inSetFrame_ = YES;
   [super setFrame:r];
   inSetFrame_ = NO;
-  [self sendViewBoundsInWindowToClient];
+  [self sendViewBoundsInWindowToHost];
 }
 
 - (void)setFrameOrigin:(NSPoint)newOrigin {
   [super setFrameOrigin:newOrigin];
-  [self sendViewBoundsInWindowToClient];
+  [self sendViewBoundsInWindowToHost];
 }
 
 - (void)setFrameSize:(NSSize)newSize {
   [super setFrameSize:newSize];
-  [self sendViewBoundsInWindowToClient];
+  [self sendViewBoundsInWindowToHost];
 }
 
 - (BOOL)canBecomeKeyView {
-  if ([self clientIsDisconnected])
+  if ([self hostIsDisconnected])
     return NO;
 
   return canBeKeyView_;
 }
 
 - (BOOL)acceptsFirstResponder {
-  if ([self clientIsDisconnected])
+  if ([self hostIsDisconnected])
     return NO;
 
   return canBeKeyView_;
@@ -1304,7 +1304,7 @@
   if ([responderDelegate_ respondsToSelector:@selector(windowDidBecomeKey)])
     [responderDelegate_ windowDidBecomeKey];
   if ([self window].isKeyWindow)
-    client_->OnWindowIsKeyChanged(true);
+    host_->OnWindowIsKeyChanged(true);
 }
 
 - (void)windowDidResignKey:(NSNotification*)notification {
@@ -1318,16 +1318,16 @@
   if ([NSApp isActive] && ([NSApp keyWindow] == [self window]))
     return;
 
-  client_->OnWindowIsKeyChanged(false);
+  host_->OnWindowIsKeyChanged(false);
 }
 
 - (BOOL)becomeFirstResponder {
-  if ([self clientIsDisconnected])
+  if ([self hostIsDisconnected])
     return NO;
   if ([responderDelegate_ respondsToSelector:@selector(becomeFirstResponder)])
     [responderDelegate_ becomeFirstResponder];
 
-  client_->OnFirstResponderChanged(true);
+  host_->OnFirstResponderChanged(true);
 
   // Cancel any onging composition text which was left before we lost focus.
   // TODO(suzhe): We should do it in -resignFirstResponder: method, but
@@ -1351,10 +1351,10 @@
   if ([responderDelegate_ respondsToSelector:@selector(resignFirstResponder)])
     [responderDelegate_ resignFirstResponder];
 
-  client_->OnFirstResponderChanged(false);
+  host_->OnFirstResponderChanged(false);
   if (closeOnDeactivate_) {
     [self setHidden:YES];
-    client_->RequestShutdown();
+    host_->RequestShutdown();
   }
 
   // We should cancel any onging composition whenever RWH's Blur() method gets
@@ -1377,10 +1377,10 @@
   }
 
   bool is_for_main_frame = false;
-  client_->SyncIsWidgetForMainFrame(&is_for_main_frame);
+  host_->SyncIsWidgetForMainFrame(&is_for_main_frame);
 
   bool is_speaking = false;
-  client_->SyncIsSpeaking(&is_speaking);
+  host_->SyncIsSpeaking(&is_speaking);
 
   SEL action = [item action];
 
@@ -1403,8 +1403,8 @@
   return editCommandHelper_->IsMenuItemEnabled(action, self);
 }
 
-- (RenderWidgetHostNSViewClient*)renderWidgetHostNSViewClient {
-  return client_;
+- (RenderWidgetHostNSViewHost*)renderWidgetHostNSViewHost {
+  return host_;
 }
 
 - (void)setAccessibilityParentElement:(id)accessibilityParent {
@@ -1412,7 +1412,7 @@
 }
 
 - (id)accessibilityHitTest:(NSPoint)point {
-  id root_element = clientHelper_->GetRootBrowserAccessibilityElement();
+  id root_element = hostHelper_->GetRootBrowserAccessibilityElement();
   if (!root_element)
     return self;
   NSPoint pointInWindow =
@@ -1424,13 +1424,13 @@
 }
 
 - (id)accessibilityFocusedUIElement {
-  return clientHelper_->GetFocusedBrowserAccessibilityElement();
+  return hostHelper_->GetFocusedBrowserAccessibilityElement();
 }
 
 // NSAccessibility formal protocol:
 
 - (NSArray*)accessibilityChildren {
-  id root = clientHelper_->GetRootBrowserAccessibilityElement();
+  id root = hostHelper_->GetRootBrowserAccessibilityElement();
   if (root)
     return @[ root ];
   return nil;
@@ -1458,7 +1458,7 @@
 // [WebHTMLView keyDown] ->
 //     EventHandler::keyEvent() ->
 //     ...
-//     [WebEditorClient handleKeyboardEvent] ->
+//     [WebEditorHost handleKeyboardEvent] ->
 //     [WebHTMLView _interceptEditingKeyEvent] ->
 //     [NSResponder interpretKeyEvents] ->
 //     [WebHTMLView insertText] ->
@@ -1475,7 +1475,7 @@
 //     |Sync IPC (KeyDown)| (*1) ->
 //     EventHandler::keyEvent() (renderer) ->
 //     ...
-//     EditorClientImpl::handleKeyboardEvent() (renderer) ->
+//     EditorHostImpl::handleKeyboardEvent() (renderer) ->
 //     |Sync IPC| (*2) ->
 //     [RenderWidgetHostViewMac _interceptEditingKeyEvent] (browser) ->
 //     [self interpretKeyEvents] ->
@@ -1485,7 +1485,7 @@
 //
 // (*1) we need to wait until this call finishes since WebHTMLView uses the
 // result of EventHandler::keyEvent().
-// (*2) we need to wait until this call finishes since WebEditorClient uses
+// (*2) we need to wait until this call finishes since WebEditorHost uses
 // the result of [WebHTMLView _interceptEditingKeyEvent].
 //
 // This needs many sync IPC messages sent between a browser and a renderer for
@@ -1532,7 +1532,7 @@
   gfx::PointF rootPoint(thePoint.x, thePoint.y);
 
   uint32_t index = UINT32_MAX;
-  client_->SyncGetCharacterIndexAtPoint(rootPoint, &index);
+  host_->SyncGetCharacterIndexAtPoint(rootPoint, &index);
   // |index| could be WTF::notFound (-1) and its value is different from
   // NSNotFound so we need to convert it.
   if (index == UINT32_MAX)
@@ -1548,9 +1548,8 @@
   bool success = false;
   if (actualRange)
     gfxActualRange = gfx::Range(*actualRange);
-  client_->SyncGetFirstRectForRange(gfx::Range(theRange), gfxRect,
-                                    gfxActualRange, &gfxRect, &gfxActualRange,
-                                    &success);
+  host_->SyncGetFirstRectForRange(gfx::Range(theRange), gfxRect, gfxActualRange,
+                                  &gfxRect, &gfxActualRange, &success);
   if (!success) {
     // The call to cancelComposition comes from https://crrev.com/350261.
     [self cancelComposition];
@@ -1674,7 +1673,7 @@
   // If we are handling a key down event, then FinishComposingText() will be
   // called in keyEvent: method.
   if (!handlingKeyDown_) {
-    client_->ImeFinishComposingText();
+    host_->ImeFinishComposingText();
   } else {
     unmarkTextCalled_ = YES;
   }
@@ -1717,9 +1716,9 @@
   if (handlingKeyDown_) {
     setMarkedTextReplacementRange_ = gfx::Range(replacementRange);
   } else {
-    client_->ImeSetComposition(markedText_, ime_text_spans_,
-                               gfx::Range(replacementRange),
-                               newSelRange.location, NSMaxRange(newSelRange));
+    host_->ImeSetComposition(markedText_, ime_text_spans_,
+                             gfx::Range(replacementRange), newSelRange.location,
+                             NSMaxRange(newSelRange));
   }
 }
 
@@ -1745,7 +1744,7 @@
                           base::CompareCase::INSENSITIVE_ASCII))
       editCommands_.push_back(EditCommand(command, ""));
   } else {
-    client_->ExecuteEditCommand(command);
+    host_->ExecuteEditCommand(command);
   }
 }
 
@@ -1771,8 +1770,7 @@
     textToBeInserted_.append(base::SysNSStringToUTF16(im_text));
   } else {
     gfx::Range replacement_range(replacementRange);
-    client_->ImeCommitText(base::SysNSStringToUTF16(im_text),
-                           replacement_range);
+    host_->ImeCommitText(base::SysNSStringToUTF16(im_text), replacement_range);
   }
 
   // Inserting text will delete all marked text automatically.
@@ -1786,11 +1784,11 @@
 - (void)viewDidMoveToWindow {
   // Update the window's frame, the view's bounds, focus, and the display info,
   // as they have not been updated while unattached to a window.
-  [self sendWindowFrameInScreenToClient];
-  [self sendViewBoundsInWindowToClient];
+  [self sendWindowFrameInScreenToHost];
+  [self sendViewBoundsInWindowToHost];
   [self updateScreenProperties];
-  client_->OnWindowIsKeyChanged([[self window] isKeyWindow]);
-  client_->OnFirstResponderChanged([[self window] firstResponder] == self);
+  host_->OnWindowIsKeyChanged([[self window] isKeyWindow]);
+  host_->OnFirstResponderChanged([[self window] firstResponder] == self);
 
   // If we switch windows (or are removed from the view hierarchy), cancel any
   // open mouse-downs.
@@ -1798,37 +1796,37 @@
     WebMouseEvent event(WebInputEvent::kMouseUp, WebInputEvent::kNoModifiers,
                         ui::EventTimeForNow());
     event.button = WebMouseEvent::Button::kLeft;
-    clientHelper_->ForwardMouseEvent(event);
+    hostHelper_->ForwardMouseEvent(event);
     hasOpenMouseDown_ = NO;
   }
 }
 
 - (void)undo:(id)sender {
-  client_->Undo();
+  host_->Undo();
 }
 
 - (void)redo:(id)sender {
-  client_->Redo();
+  host_->Redo();
 }
 
 - (void)cut:(id)sender {
-  client_->Cut();
+  host_->Cut();
 }
 
 - (void)copy:(id)sender {
-  client_->Copy();
+  host_->Copy();
 }
 
 - (void)copyToFindPboard:(id)sender {
-  client_->CopyToFindPboard();
+  host_->CopyToFindPboard();
 }
 
 - (void)paste:(id)sender {
-  client_->Paste();
+  host_->Paste();
 }
 
 - (void)pasteAndMatchStyle:(id)sender {
-  client_->PasteAndMatchStyle();
+  host_->PasteAndMatchStyle();
 }
 
 - (void)selectAll:(id)sender {
@@ -1839,15 +1837,15 @@
   // menu handler, neither is true.
   // Explicitly call SelectAll() here to make sure the renderer returns
   // selection results.
-  client_->SelectAll();
+  host_->SelectAll();
 }
 
 - (void)startSpeaking:(id)sender {
-  client_->StartSpeaking();
+  host_->StartSpeaking();
 }
 
 - (void)stopSpeaking:(id)sender {
-  client_->StopSpeaking();
+  host_->StopSpeaking();
 }
 
 - (void)cancelComposition {
@@ -1866,7 +1864,7 @@
   if (!hasMarkedText_)
     return;
 
-  client_->ImeFinishComposingText();
+  host_->ImeFinishComposingText();
   [self cancelComposition];
 }
 
@@ -1926,7 +1924,7 @@
 
 - (void)popupWindowWillClose:(NSNotification*)notification {
   [self setHidden:YES];
-  client_->RequestShutdown();
+  host_->RequestShutdown();
 }
 
 - (void)invalidateTouchBar {
diff --git a/content/app_shim_remote_cocoa/web_contents_ns_view_bridge.h b/content/app_shim_remote_cocoa/web_contents_ns_view_bridge.h
index c7f2875..7f1d237 100644
--- a/content/app_shim_remote_cocoa/web_contents_ns_view_bridge.h
+++ b/content/app_shim_remote_cocoa/web_contents_ns_view_bridge.h
@@ -18,26 +18,27 @@
 @class WebContentsViewCocoa;
 
 namespace content {
-
 class WebContentsViewMac;
+}  // namespace content
+
+namespace remote_cocoa {
 
 // A wrapper around a WebContentsViewCocoa, to be accessed via the mojo
 // interface WebContentsNSViewBridge.
-class CONTENT_EXPORT WebContentsNSViewBridge
-    : public mojom::WebContentsNSViewBridge {
+class CONTENT_EXPORT WebContentsNSViewBridge : public mojom::WebContentsNSView {
  public:
   // Create a bridge that will access its client in another process via a mojo
   // interface.
   WebContentsNSViewBridge(uint64_t view_id,
-                          mojom::WebContentsNSViewClientAssociatedPtr client);
+                          mojom::WebContentsNSViewHostAssociatedPtr client);
   // Create a bridge that will access its client directly in-process.
   // TODO(ccameron): Change this to expose only the mojom::WebContentsNSView
   // when all communication is through mojo.
   WebContentsNSViewBridge(uint64_t view_id,
-                          WebContentsViewMac* web_contents_view);
+                          content::WebContentsViewMac* web_contents_view);
   ~WebContentsNSViewBridge() override;
 
-  WebContentsViewCocoa* cocoa_view() const { return cocoa_view_.get(); }
+  WebContentsViewCocoa* GetNSView() const { return ns_view_.get(); }
 
   // mojom::WebContentsNSViewBridge:
   void SetParentNSView(uint64_t parent_ns_view_id) override;
@@ -46,16 +47,16 @@
   void SetVisible(bool visible) override;
   void MakeFirstResponder() override;
   void TakeFocus(bool reverse) override;
-  void StartDrag(const DropData& drop_data,
+  void StartDrag(const content::DropData& drop_data,
                  uint32_t operation_mask,
                  const gfx::ImageSkia& image,
                  const gfx::Vector2d& image_offset) override;
 
  private:
-  base::scoped_nsobject<WebContentsViewCocoa> cocoa_view_;
-  mojom::WebContentsNSViewClientAssociatedPtr client_;
+  base::scoped_nsobject<WebContentsViewCocoa> ns_view_;
+  mojom::WebContentsNSViewHostAssociatedPtr host_;
 
-  std::unique_ptr<remote_cocoa::ScopedNSViewIdMapping> view_id_;
+  std::unique_ptr<ScopedNSViewIdMapping> view_id_;
 
   DISALLOW_COPY_AND_ASSIGN(WebContentsNSViewBridge);
 };
diff --git a/content/app_shim_remote_cocoa/web_contents_ns_view_bridge.mm b/content/app_shim_remote_cocoa/web_contents_ns_view_bridge.mm
index c5815d1..be4f275 100644
--- a/content/app_shim_remote_cocoa/web_contents_ns_view_bridge.mm
+++ b/content/app_shim_remote_cocoa/web_contents_ns_view_bridge.mm
@@ -9,27 +9,27 @@
 #include "content/browser/web_contents/web_contents_view_mac.h"
 #include "ui/gfx/image/image_skia_util_mac.h"
 
-namespace content {
+namespace remote_cocoa {
 
 WebContentsNSViewBridge::WebContentsNSViewBridge(
     uint64_t view_id,
-    mojom::WebContentsNSViewClientAssociatedPtr client)
-    : client_(std::move(client)) {
-  cocoa_view_.reset(
+    mojom::WebContentsNSViewHostAssociatedPtr client)
+    : host_(std::move(client)) {
+  ns_view_.reset(
       [[WebContentsViewCocoa alloc] initWithViewsHostableView:nullptr]);
-  [cocoa_view_ setClient:client_.get()];
+  [ns_view_ setHost:host_.get()];
   view_id_ = std::make_unique<remote_cocoa::ScopedNSViewIdMapping>(
-      view_id, cocoa_view_.get());
+      view_id, ns_view_.get());
 }
 
 WebContentsNSViewBridge::WebContentsNSViewBridge(
     uint64_t view_id,
-    WebContentsViewMac* web_contents_view) {
-  cocoa_view_.reset([[WebContentsViewCocoa alloc]
+    content::WebContentsViewMac* web_contents_view) {
+  ns_view_.reset([[WebContentsViewCocoa alloc]
       initWithViewsHostableView:web_contents_view]);
-  [cocoa_view_ setClient:web_contents_view];
+  [ns_view_ setHost:web_contents_view];
   view_id_ = std::make_unique<remote_cocoa::ScopedNSViewIdMapping>(
-      view_id, cocoa_view_.get());
+      view_id, ns_view_.get());
 }
 
 WebContentsNSViewBridge::~WebContentsNSViewBridge() {
@@ -37,24 +37,24 @@
   // while the user was operating a UI control which resulted in a
   // close.  In that case, the Cocoa view outlives the
   // WebContentsViewMac instance due to Cocoa retain count.
-  [cocoa_view_ setClient:nullptr];
-  [cocoa_view_ clearViewsHostableView];
-  [cocoa_view_ removeFromSuperview];
+  [ns_view_ setHost:nullptr];
+  [ns_view_ clearViewsHostableView];
+  [ns_view_ removeFromSuperview];
 }
 
 void WebContentsNSViewBridge::SetParentNSView(uint64_t parent_ns_view_id) {
   NSView* parent_ns_view = remote_cocoa::GetNSViewFromId(parent_ns_view_id);
   // If the browser passed an invalid handle, then there is no recovery.
   CHECK(parent_ns_view);
-  [parent_ns_view addSubview:cocoa_view_];
+  [parent_ns_view addSubview:ns_view_];
 }
 
 void WebContentsNSViewBridge::ResetParentNSView() {
-  [cocoa_view_ removeFromSuperview];
+  [ns_view_ removeFromSuperview];
 }
 
 void WebContentsNSViewBridge::SetBounds(const gfx::Rect& bounds_in_window) {
-  NSWindow* window = [cocoa_view_ window];
+  NSWindow* window = [ns_view_ window];
   NSRect window_content_rect = [window contentRectForFrameRect:[window frame]];
   NSRect ns_bounds_in_window =
       NSMakeRect(bounds_in_window.x(),
@@ -62,36 +62,36 @@
                      bounds_in_window.height(),
                  bounds_in_window.width(), bounds_in_window.height());
   NSRect ns_bounds_in_superview =
-      [[cocoa_view_ superview] convertRect:ns_bounds_in_window fromView:nil];
-  [cocoa_view_ setFrame:ns_bounds_in_superview];
+      [[ns_view_ superview] convertRect:ns_bounds_in_window fromView:nil];
+  [ns_view_ setFrame:ns_bounds_in_superview];
 }
 
 void WebContentsNSViewBridge::SetVisible(bool visible) {
-  [cocoa_view_ setHidden:!visible];
+  [ns_view_ setHidden:!visible];
 }
 
 void WebContentsNSViewBridge::MakeFirstResponder() {
-  if ([cocoa_view_ acceptsFirstResponder])
-    [[cocoa_view_ window] makeFirstResponder:cocoa_view_];
+  if ([ns_view_ acceptsFirstResponder])
+    [[ns_view_ window] makeFirstResponder:ns_view_];
 }
 
 void WebContentsNSViewBridge::TakeFocus(bool reverse) {
   if (reverse)
-    [[cocoa_view_ window] selectPreviousKeyView:cocoa_view_];
+    [[ns_view_ window] selectPreviousKeyView:ns_view_];
   else
-    [[cocoa_view_ window] selectNextKeyView:cocoa_view_];
+    [[ns_view_ window] selectNextKeyView:ns_view_];
 }
 
-void WebContentsNSViewBridge::StartDrag(const DropData& drop_data,
+void WebContentsNSViewBridge::StartDrag(const content::DropData& drop_data,
                                         uint32_t operation_mask,
                                         const gfx::ImageSkia& image,
                                         const gfx::Vector2d& image_offset) {
   NSPoint offset = NSPointFromCGPoint(
       gfx::PointAtOffsetFromOrigin(image_offset).ToCGPoint());
-  [cocoa_view_ startDragWithDropData:drop_data
-                   dragOperationMask:operation_mask
-                               image:gfx::NSImageFromImageSkia(image)
-                              offset:offset];
+  [ns_view_ startDragWithDropData:drop_data
+                dragOperationMask:operation_mask
+                            image:gfx::NSImageFromImageSkia(image)
+                           offset:offset];
 }
 
-}  // namespace content
+}  // namespace remote_cocoa
diff --git a/content/app_shim_remote_cocoa/web_contents_view_cocoa.h b/content/app_shim_remote_cocoa/web_contents_view_cocoa.h
index 48529f4..230e259 100644
--- a/content/app_shim_remote_cocoa/web_contents_view_cocoa.h
+++ b/content/app_shim_remote_cocoa/web_contents_view_cocoa.h
@@ -12,20 +12,23 @@
 
 namespace content {
 struct DropData;
-namespace mojom {
-class WebContentsNSViewClient;
-}  // namespace mojom
 }  // namespace content
 
+namespace remote_cocoa {
+namespace mojom {
+class WebContentsNSViewHost;
+}  // namespace mojom
+}  // namespace remote_cocoa
+
 @class WebDragSource;
 
 CONTENT_EXPORT
 @interface WebContentsViewCocoa : BaseView <ViewsHostable> {
  @private
-  // Instances of this class are owned by both client_ and AppKit. It is
-  // possible for an instance to outlive its webContentsView_. The client_ must
-  // call -clearClientAndView in its destructor.
-  content::mojom::WebContentsNSViewClient* client_;
+  // Instances of this class are owned by both host_ and AppKit. It is
+  // possible for an instance to outlive its webContentsView_. The host_ must
+  // call -clearHostAndView in its destructor.
+  remote_cocoa::mojom::WebContentsNSViewHost* host_;
 
   // The interface exported to views::Views that embed this as a sub-view.
   ui::ViewsHostableView* viewsHostableView_;
@@ -36,7 +39,7 @@
 
 // Set or un-set the mojo interface through which to communicate with the
 // browser process.
-- (void)setClient:(content::mojom::WebContentsNSViewClient*)client;
+- (void)setHost:(remote_cocoa::mojom::WebContentsNSViewHost*)host;
 
 - (void)setMouseDownCanMoveWindow:(BOOL)canMove;
 
diff --git a/content/app_shim_remote_cocoa/web_contents_view_cocoa.mm b/content/app_shim_remote_cocoa/web_contents_view_cocoa.mm
index 1bd2729..615fe67 100644
--- a/content/app_shim_remote_cocoa/web_contents_view_cocoa.mm
+++ b/content/app_shim_remote_cocoa/web_contents_view_cocoa.mm
@@ -15,7 +15,9 @@
 #include "ui/base/cocoa/cocoa_base_utils.h"
 #include "ui/base/dragdrop/cocoa_dnd_util.h"
 
-using content::mojom::DraggingInfo;
+using remote_cocoa::mojom::DraggingInfo;
+using remote_cocoa::mojom::SelectionDirection;
+using remote_cocoa::mojom::Visibility;
 using content::DropData;
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -92,10 +94,10 @@
 }
 
 - (void)mouseEvent:(NSEvent*)theEvent {
-  if (!client_)
+  if (!host_)
     return;
-  client_->OnMouseEvent([theEvent type] == NSMouseMoved,
-                        [theEvent type] == NSMouseExited);
+  host_->OnMouseEvent([theEvent type] == NSMouseMoved,
+                      [theEvent type] == NSMouseExited);
 }
 
 - (void)setMouseDownCanMoveWindow:(BOOL)canMove {
@@ -104,9 +106,9 @@
 
 - (BOOL)mouseDownCanMoveWindow {
   // This is needed to prevent mouseDowns from moving the window
-  // around.  The default implementation returns YES only for opaque
-  // views.  WebContentsViewCocoa does not draw itself in any way, but
-  // its subviews do paint their entire frames.  Returning NO here
+  // around. The default implementation returns YES only for opaque
+  // views. WebContentsViewCocoa does not draw itself in any way, but
+  // its subviews do paint their entire frames. Returning NO here
   // saves us the effort of overriding this method in every possible
   // subview.
   return mouseDownCanMoveWindow_;
@@ -120,10 +122,10 @@
             dragOperationMask:(NSDragOperation)operationMask
                         image:(NSImage*)image
                        offset:(NSPoint)offset {
-  if (!client_)
+  if (!host_)
     return;
   dragSource_.reset([[WebDragSource alloc]
-         initWithClient:client_
+           initWithHost:host_
                    view:self
                dropData:&dropData
                   image:image
@@ -174,45 +176,45 @@
 // NSDraggingDestination methods
 
 - (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender {
-  if (!client_)
+  if (!host_)
     return NSDragOperationNone;
 
   // Fill out a DropData from pasteboard.
   DropData dropData;
   content::PopulateDropDataFromPasteboard(&dropData,
                                           [sender draggingPasteboard]);
-  client_->SetDropData(dropData);
+  host_->SetDropData(dropData);
 
   auto draggingInfo = DraggingInfo::New();
   [self populateDraggingInfo:draggingInfo.get() fromNSDraggingInfo:sender];
   uint32_t result = 0;
-  client_->DraggingEntered(std::move(draggingInfo), &result);
+  host_->DraggingEntered(std::move(draggingInfo), &result);
   return result;
 }
 
 - (void)draggingExited:(id<NSDraggingInfo>)sender {
-  if (!client_)
+  if (!host_)
     return;
-  client_->DraggingExited();
+  host_->DraggingExited();
 }
 
 - (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender {
-  if (!client_)
+  if (!host_)
     return NSDragOperationNone;
   auto draggingInfo = DraggingInfo::New();
   [self populateDraggingInfo:draggingInfo.get() fromNSDraggingInfo:sender];
   uint32_t result = 0;
-  client_->DraggingUpdated(std::move(draggingInfo), &result);
+  host_->DraggingUpdated(std::move(draggingInfo), &result);
   return result;
 }
 
 - (BOOL)performDragOperation:(id<NSDraggingInfo>)sender {
-  if (!client_)
+  if (!host_)
     return NO;
   auto draggingInfo = DraggingInfo::New();
   [self populateDraggingInfo:draggingInfo.get() fromNSDraggingInfo:sender];
   bool result = false;
-  client_->PerformDragOperation(std::move(draggingInfo), &result);
+  host_->PerformDragOperation(std::move(draggingInfo), &result);
   return result;
 }
 
@@ -220,14 +222,14 @@
   viewsHostableView_ = nullptr;
 }
 
-- (void)setClient:(content::mojom::WebContentsNSViewClient*)client {
-  if (!client)
-    [dragSource_ clearClientAndWebContentsView];
-  client_ = client;
+- (void)setHost:(remote_cocoa::mojom::WebContentsNSViewHost*)host {
+  if (!host)
+    [dragSource_ clearHostAndWebContentsView];
+  host_ = host;
 }
 
 - (void)viewDidBecomeFirstResponder:(NSNotification*)notification {
-  if (!client_)
+  if (!host_)
     return;
 
   NSView* view = [notification object];
@@ -238,34 +240,34 @@
       static_cast<NSSelectionDirection>([[[notification userInfo]
           objectForKey:kSelectionDirection] unsignedIntegerValue]);
 
-  content::mojom::SelectionDirection direction;
+  SelectionDirection direction;
   switch (ns_direction) {
     case NSDirectSelection:
-      direction = content::mojom::SelectionDirection::kDirect;
+      direction = SelectionDirection::kDirect;
       break;
     case NSSelectingNext:
-      direction = content::mojom::SelectionDirection::kForward;
+      direction = SelectionDirection::kForward;
       break;
     case NSSelectingPrevious:
-      direction = content::mojom::SelectionDirection::kReverse;
+      direction = SelectionDirection::kReverse;
       break;
     default:
       return;
   }
-  client_->OnBecameFirstResponder(direction);
+  host_->OnBecameFirstResponder(direction);
 }
 
 - (void)updateWebContentsVisibility {
-  if (!client_)
+  if (!host_)
     return;
-  content::mojom::Visibility visibility = content::mojom::Visibility::kVisible;
+  Visibility visibility = Visibility::kVisible;
   if ([self isHiddenOrHasHiddenAncestor] || ![self window])
-    visibility = content::mojom::Visibility::kHidden;
+    visibility = Visibility::kHidden;
   else if ([[self window] occlusionState] & NSWindowOcclusionStateVisible)
-    visibility = content::mojom::Visibility::kVisible;
+    visibility = Visibility::kVisible;
   else
-    visibility = content::mojom::Visibility::kOccluded;
-  client_->OnWindowVisibilityChanged(visibility);
+    visibility = Visibility::kOccluded;
+  host_->OnWindowVisibilityChanged(visibility);
 }
 
 - (void)resizeSubviewsWithOldSize:(NSSize)oldBoundsSize {
diff --git a/content/app_shim_remote_cocoa/web_drag_source_mac.h b/content/app_shim_remote_cocoa/web_drag_source_mac.h
index 0a166e3f..3dc1b22 100644
--- a/content/app_shim_remote_cocoa/web_drag_source_mac.h
+++ b/content/app_shim_remote_cocoa/web_drag_source_mac.h
@@ -18,19 +18,22 @@
 
 namespace content {
 struct DropData;
-namespace mojom {
-class WebContentsNSViewClient;
-}  // namespace mojom
 }  // namespace content
 
+namespace remote_cocoa {
+namespace mojom {
+class WebContentsNSViewHost;
+}  // namespace mojom
+}  // namespace remote_cocoa
+
 // A class that handles tracking and event processing for a drag and drop
 // originating from the content area.
 CONTENT_EXPORT
 @interface WebDragSource : NSObject {
  @private
-  // The client through which to communicate with the WebContentsImpl. Owns
-  // |self| and resets |client_| via clearClientAndWebContentsView.
-  content::mojom::WebContentsNSViewClient* client_;
+  // The host through which to communicate with the WebContentsImpl. Owns
+  // |self| and resets |host_| via clearHostAndWebContentsView.
+  remote_cocoa::mojom::WebContentsNSViewHost* host_;
 
   // The view from which the drag was initiated. Weak reference.
   // An instance of this class may outlive |contentsView_|. The destructor of
@@ -65,16 +68,16 @@
 // Initialize a WebDragSource object for a drag (originating on the given
 // contentsView and with the given dropData and pboard). Fill the pasteboard
 // with data types appropriate for dropData.
-- (id)initWithClient:(content::mojom::WebContentsNSViewClient*)client
-                view:(NSView*)contentsView
-            dropData:(const content::DropData*)dropData
-               image:(NSImage*)image
-              offset:(NSPoint)offset
-          pasteboard:(NSPasteboard*)pboard
-   dragOperationMask:(NSDragOperation)dragOperationMask;
+- (id)initWithHost:(remote_cocoa::mojom::WebContentsNSViewHost*)host
+              view:(NSView*)contentsView
+          dropData:(const content::DropData*)dropData
+             image:(NSImage*)image
+            offset:(NSPoint)offset
+        pasteboard:(NSPasteboard*)pboard
+ dragOperationMask:(NSDragOperation)dragOperationMask;
 
 // Call when the web contents is gone.
-- (void)clearClientAndWebContentsView;
+- (void)clearHostAndWebContentsView;
 
 // Returns a mask of the allowed drag operations.
 - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal;
diff --git a/content/app_shim_remote_cocoa/web_drag_source_mac.mm b/content/app_shim_remote_cocoa/web_drag_source_mac.mm
index d6f709c..1234fee 100644
--- a/content/app_shim_remote_cocoa/web_drag_source_mac.mm
+++ b/content/app_shim_remote_cocoa/web_drag_source_mac.mm
@@ -49,15 +49,15 @@
 
 @implementation WebDragSource
 
-- (id)initWithClient:(content::mojom::WebContentsNSViewClient*)client
-                view:(NSView*)contentsView
-            dropData:(const DropData*)dropData
-               image:(NSImage*)image
-              offset:(NSPoint)offset
-          pasteboard:(NSPasteboard*)pboard
-   dragOperationMask:(NSDragOperation)dragOperationMask {
+- (id)initWithHost:(remote_cocoa::mojom::WebContentsNSViewHost*)host
+              view:(NSView*)contentsView
+          dropData:(const DropData*)dropData
+             image:(NSImage*)image
+            offset:(NSPoint)offset
+        pasteboard:(NSPasteboard*)pboard
+ dragOperationMask:(NSDragOperation)dragOperationMask {
   if ((self = [super init])) {
-    client_ = client;
+    host_ = host;
 
     contentsView_ = contentsView;
     DCHECK(contentsView_);
@@ -79,8 +79,8 @@
   return self;
 }
 
-- (void)clearClientAndWebContentsView {
-  client_ = nullptr;
+- (void)clearHostAndWebContentsView {
+  host_ = nullptr;
   contentsView_ = nil;
 }
 
@@ -206,7 +206,7 @@
 
 - (void)endDragAt:(NSPoint)screenPoint
         operation:(NSDragOperation)operation {
-  if (!client_ || !contentsView_)
+  if (!host_ || !contentsView_)
     return;
 
   if (dragImage_) {
@@ -229,7 +229,7 @@
   if (operation == (NSDragOperationMove | NSDragOperationCopy))
     operation &= ~NSDragOperationMove;
 
-  client_->EndDrag(
+  host_->EndDrag(
       operation,
       gfx::PointF(localPoint.x, viewFrame.size.height - localPoint.y),
       gfx::PointF(screenPoint.x, screenFrame.size.height - screenPoint.y));
@@ -239,7 +239,7 @@
 }
 
 - (NSString*)dragPromisedFileTo:(NSString*)path {
-  if (!client_)
+  if (!host_)
     return nil;
   // Be extra paranoid; avoid crashing.
   if (!dropData_) {
@@ -248,7 +248,7 @@
   }
   base::FilePath filePath(SysNSStringToUTF8(path));
   filePath = filePath.Append(downloadFileName_);
-  client_->DragPromisedFileTo(filePath, *dropData_, downloadURL_, &filePath);
+  host_->DragPromisedFileTo(filePath, *dropData_, downloadURL_, &filePath);
   return SysUTF8ToNSString(filePath.BaseName().value());
 }
 
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 5665846..f525fc6 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -2580,9 +2580,9 @@
       "../app_shim_remote_cocoa/ns_view_bridge_factory_impl.mm",
       "../app_shim_remote_cocoa/popup_window_mac.h",
       "../app_shim_remote_cocoa/popup_window_mac.mm",
-      "../app_shim_remote_cocoa/render_widget_host_ns_view_bridge_local.h",
-      "../app_shim_remote_cocoa/render_widget_host_ns_view_bridge_local.mm",
-      "../app_shim_remote_cocoa/render_widget_host_ns_view_client_helper.h",
+      "../app_shim_remote_cocoa/render_widget_host_ns_view_bridge.h",
+      "../app_shim_remote_cocoa/render_widget_host_ns_view_bridge.mm",
+      "../app_shim_remote_cocoa/render_widget_host_ns_view_host_helper.h",
       "../app_shim_remote_cocoa/render_widget_host_view_cocoa.h",
       "../app_shim_remote_cocoa/render_widget_host_view_cocoa.mm",
       "../app_shim_remote_cocoa/web_contents_view_cocoa.h",
diff --git a/content/browser/cookie_store/cookie_store_manager_unittest.cc b/content/browser/cookie_store/cookie_store_manager_unittest.cc
index 37f5d43..c624b3a8 100644
--- a/content/browser/cookie_store/cookie_store_manager_unittest.cc
+++ b/content/browser/cookie_store/cookie_store_manager_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/files/scoped_temp_dir.h"
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
+#include "base/test/bind_test_util.h"
 #include "content/browser/cookie_store/cookie_store_context.h"
 #include "content/browser/cookie_store/cookie_store_manager.h"
 #include "content/browser/service_worker/embedded_worker_test_helper.h"
@@ -45,12 +46,10 @@
     base::RunLoop run_loop;
     cookie_store_service_->AppendSubscriptions(
         service_worker_registration_id, std::move(subscriptions),
-        base::BindOnce(
-            [](base::RunLoop* run_loop, bool* success, bool service_success) {
-              *success = service_success;
-              run_loop->Quit();
-            },
-            &run_loop, &success));
+        base::BindLambdaForTesting([&](bool service_success) {
+          success = service_success;
+          run_loop.Quit();
+        }));
     run_loop.Run();
     return success;
   }
@@ -61,14 +60,12 @@
     base::RunLoop run_loop;
     cookie_store_service_->GetSubscriptions(
         service_worker_registration_id,
-        base::BindOnce(
-            [](base::RunLoop* run_loop, base::Optional<Subscriptions>* result,
-               Subscriptions service_result, bool service_success) {
+        base::BindLambdaForTesting(
+            [&](Subscriptions service_result, bool service_success) {
               if (service_success)
-                *result = std::move(service_result);
-              run_loop->Quit();
-            },
-            &run_loop, &result));
+                result = std::move(service_result);
+              run_loop.Quit();
+            }));
     run_loop.Run();
     return result;
   }
@@ -297,18 +294,15 @@
     base::RunLoop run_loop;
     worker_test_helper_->context()->RegisterServiceWorker(
         GURL(script_url), options,
-        base::BindOnce(
-            [](base::RunLoop* run_loop, bool* success, int64_t* registration_id,
-               blink::ServiceWorkerStatusCode status,
-               const std::string& status_message,
-               int64_t service_worker_registration_id) {
-              *success = (status == blink::ServiceWorkerStatusCode::kOk);
-              *registration_id = service_worker_registration_id;
-              EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, status)
-                  << blink::ServiceWorkerStatusToString(status);
-              run_loop->Quit();
-            },
-            &run_loop, &success, &registration_id));
+        base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status,
+                                       const std::string& status_message,
+                                       int64_t service_worker_registration_id) {
+          success = (status == blink::ServiceWorkerStatusCode::kOk);
+          registration_id = service_worker_registration_id;
+          EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, status)
+              << blink::ServiceWorkerStatusToString(status);
+          run_loop.Quit();
+        }));
     run_loop.Run();
     if (!success)
       return kInvalidRegistrationId;
@@ -325,14 +319,12 @@
     options.set_include_httponly();
     cookie_manager_->SetCanonicalCookie(
         cookie, "https", options,
-        base::BindOnce(
-            [](base::RunLoop* run_loop, bool* success,
-               net::CanonicalCookie::CookieInclusionStatus service_success) {
-              *success = (service_success ==
-                          net::CanonicalCookie::CookieInclusionStatus::INCLUDE);
-              run_loop->Quit();
-            },
-            &run_loop, &success));
+        base::BindLambdaForTesting(
+            [&](net::CanonicalCookie::CookieInclusionStatus service_success) {
+              success = (service_success ==
+                         net::CanonicalCookie::CookieInclusionStatus::INCLUDE);
+              run_loop.Quit();
+            }));
     run_loop.Run();
     return success;
   }
diff --git a/content/browser/frame_host/popup_menu_helper_mac.mm b/content/browser/frame_host/popup_menu_helper_mac.mm
index 097604d..76b42c7 100644
--- a/content/browser/frame_host/popup_menu_helper_mac.mm
+++ b/content/browser/frame_host/popup_menu_helper_mac.mm
@@ -63,7 +63,7 @@
   RenderWidgetHostViewMac* rwhvm =
       static_cast<RenderWidgetHostViewMac*>(GetRenderWidgetHostView());
   base::scoped_nsobject<RenderWidgetHostViewCocoa> cocoa_view(
-      [rwhvm->cocoa_view() retain]);
+      [rwhvm->GetInProcessNSView() retain]);
 
   // Display the menu.
   base::scoped_nsobject<WebMenuRunner> runner([[WebMenuRunner alloc]
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.h b/content/browser/renderer_host/render_widget_host_view_mac.h
index 8113209..23a4833 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac.h
+++ b/content/browser/renderer_host/render_widget_host_view_mac.h
@@ -15,7 +15,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
 #include "components/viz/common/surfaces/surface_id.h"
-#include "content/app_shim_remote_cocoa/render_widget_host_ns_view_client_helper.h"
+#include "content/app_shim_remote_cocoa/render_widget_host_ns_view_host_helper.h"
 #include "content/browser/renderer_host/browser_compositor_view_mac.h"
 #include "content/browser/renderer_host/input/mouse_wheel_phase_handler.h"
 #include "content/browser/renderer_host/render_widget_host_view_base.h"
@@ -34,6 +34,7 @@
 namespace mojom {
 class Application;
 }  // namespace mojom
+class RenderWidgetHostNSViewBridge;
 }  // namespace remote_cocoa
 
 namespace ui {
@@ -51,7 +52,6 @@
 
 class CursorManager;
 class RenderWidgetHost;
-class RenderWidgetHostNSViewBridgeLocal;
 class RenderWidgetHostViewMac;
 class WebContents;
 class WebCursor;
@@ -74,8 +74,8 @@
 // RenderWidgetHostView class hierarchy described in render_widget_host_view.h.
 class CONTENT_EXPORT RenderWidgetHostViewMac
     : public RenderWidgetHostViewBase,
-      public RenderWidgetHostNSViewClientHelper,
-      public mojom::RenderWidgetHostNSViewClient,
+      public remote_cocoa::RenderWidgetHostNSViewHostHelper,
+      public remote_cocoa::mojom::RenderWidgetHostNSViewHost,
       public BrowserCompositorMacClient,
       public TextInputManager::Observer,
       public ui::GestureProviderClient,
@@ -93,7 +93,7 @@
   // to use RWHVChildFrame (http://crbug.com/330264).
   RenderWidgetHostViewMac(RenderWidgetHost* widget, bool is_guest_view_hack);
 
-  RenderWidgetHostViewCocoa* cocoa_view() const;
+  RenderWidgetHostViewCocoa* GetInProcessNSView() const;
 
   // |delegate| is used to separate out the logic from the NSResponder delegate.
   // |delegate| is retained by this class.
@@ -304,7 +304,7 @@
   // RenderWidgetHostImpl as well.
   void UpdateNSViewAndDisplayProperties();
 
-  // RenderWidgetHostNSViewClientHelper implementation.
+  // RenderWidgetHostNSViewHostHelper implementation.
   id GetRootBrowserAccessibilityElement() override;
   id GetFocusedBrowserAccessibilityElement() override;
   void SetAccessibilityWindow(NSWindow* window) override;
@@ -326,7 +326,7 @@
   void GestureEnd(blink::WebGestureEvent end_event) override;
   void SmartMagnify(const blink::WebGestureEvent& smart_magnify_event) override;
 
-  // mojom::RenderWidgetHostNSViewClient implementation.
+  // mojom::RenderWidgetHostNSViewHost implementation.
   void SyncIsWidgetForMainFrame(
       SyncIsWidgetForMainFrameCallback callback) override;
   bool SyncIsWidgetForMainFrame(bool* is_for_main_frame) override;
@@ -522,21 +522,22 @@
   void GetPageTextForSpeech(SpeechCallback callback);
 
   // Interface through which the NSView is to be manipulated. This points either
-  // to |ns_view_bridge_local_| or to (to-be-added) |ns_view_bridge_remote_|.
-  mojom::RenderWidgetHostNSViewBridge* ns_view_bridge_ = nullptr;
+  // to |in_process_ns_view_bridge_| or to |remote_ns_view_ptr_|.
+  remote_cocoa::mojom::RenderWidgetHostNSView* ns_view_ = nullptr;
 
-  // If |ns_view_bridge_| is hosted in this process, then this will be non-null,
+  // If |ns_view_| is hosted in this process, then this will be non-null,
   // and may be used to query the actual RenderWidgetHostViewCocoa that is being
   // used for |this|. Any functionality that uses |new_view_bridge_local_| will
   // not work when the RenderWidgetHostViewCocoa is hosted in an app process.
-  std::unique_ptr<RenderWidgetHostNSViewBridgeLocal> ns_view_bridge_local_;
+  std::unique_ptr<remote_cocoa::RenderWidgetHostNSViewBridge>
+      in_process_ns_view_bridge_;
 
   // If the NSView is hosted in a remote process and accessed via mojo then
-  // - |ns_view_bridge_| will point to |ns_view_bridge_remote_|
-  // - |ns_view_client_binding_| is the binding provided to the bridge.
-  mojom::RenderWidgetHostNSViewBridgeAssociatedPtr ns_view_bridge_remote_;
-  mojo::AssociatedBinding<mojom::RenderWidgetHostNSViewClient>
-      ns_view_client_binding_;
+  // - |ns_view_| will point to |remote_ns_view_ptr_|
+  // - |remote_ns_view_client_binding_| is the binding provided to the bridge.
+  remote_cocoa::mojom::RenderWidgetHostNSViewAssociatedPtr remote_ns_view_ptr_;
+  mojo::AssociatedBinding<remote_cocoa::mojom::RenderWidgetHostNSViewHost>
+      remote_ns_view_client_binding_;
 
   // State tracked by Show/Hide/IsShowing.
   bool is_visible_ = false;
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm
index 16d28b6..d450d95 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac.mm
@@ -23,7 +23,7 @@
 #include "components/remote_cocoa/common/application.mojom.h"
 #include "components/viz/common/features.h"
 #include "components/viz/common/switches.h"
-#import "content/app_shim_remote_cocoa/render_widget_host_ns_view_bridge_local.h"
+#import "content/app_shim_remote_cocoa/render_widget_host_ns_view_bridge.h"
 #import "content/app_shim_remote_cocoa/render_widget_host_view_cocoa.h"
 #import "content/browser/accessibility/browser_accessibility_cocoa.h"
 #import "content/browser/accessibility/browser_accessibility_mac.h"
@@ -125,7 +125,7 @@
   const gfx::CALayerParams* ca_layer_params =
       browser_compositor_->GetLastCALayerParams();
   if (ca_layer_params)
-    ns_view_bridge_->SetCALayerParams(*ca_layer_params);
+    ns_view_->SetCALayerParams(*ca_layer_params);
 
   // Take this opportunity to update the VSync parameters, if needed.
   if (display_link_) {
@@ -173,7 +173,7 @@
     : RenderWidgetHostViewBase(widget),
       page_at_minimum_scale_(true),
       mouse_wheel_phase_handler_(this),
-      ns_view_client_binding_(this),
+      remote_ns_view_client_binding_(this),
       is_loading_(false),
       is_guest_view_hack_(is_guest_view_hack),
       popup_parent_host_view_(nullptr),
@@ -183,10 +183,10 @@
                         this),
       accessibility_focus_overrider_(this),
       weak_factory_(this) {
-  // The NSView is on the other side of |ns_view_bridge_|.
-  ns_view_bridge_local_ =
-      std::make_unique<RenderWidgetHostNSViewBridgeLocal>(this, this);
-  ns_view_bridge_ = ns_view_bridge_local_.get();
+  // The NSView is on the other side of |ns_view_|.
+  in_process_ns_view_bridge_ =
+      std::make_unique<remote_cocoa::RenderWidgetHostNSViewBridge>(this, this);
+  ns_view_ = in_process_ns_view_bridge_.get();
 
   // Guess that the initial screen we will be on is the screen of the current
   // window (since that's the best guess that we have, and is usually right).
@@ -200,7 +200,7 @@
 
   browser_compositor_.reset(new BrowserCompositorMac(
       this, this, host()->is_hidden(), display_, frame_sink_id));
-  DCHECK(![cocoa_view() window]);
+  DCHECK(![GetInProcessNSView() window]);
 
   if (!is_guest_view_hack_)
     host()->SetView(this);
@@ -248,9 +248,9 @@
 
   // Disconnect from the previous bridge (this will have the effect of
   // destroying the associated bridge), and close the binding (to allow it
-  // to be re-bound). Note that |ns_view_bridge_local_| remains valid.
-  ns_view_client_binding_.Close();
-  ns_view_bridge_remote_.reset();
+  // to be re-bound). Note that |in_process_ns_view_bridge_| remains valid.
+  remote_ns_view_client_binding_.Close();
+  remote_ns_view_ptr_.reset();
 
   // Enable accessibility focus overriding for remote NSViews.
   accessibility_focus_overrider_.SetAppIsRemote(remote_cocoa_application !=
@@ -258,16 +258,16 @@
 
   // If no host is specified, then use the locally hosted NSView.
   if (!remote_cocoa_application) {
-    ns_view_bridge_ = ns_view_bridge_local_.get();
+    ns_view_ = in_process_ns_view_bridge_.get();
     return;
   }
 
-  mojom::RenderWidgetHostNSViewClientAssociatedPtr client;
-  ns_view_client_binding_.Bind(mojo::MakeRequest(&client));
-  mojom::RenderWidgetHostNSViewBridgeAssociatedRequest bridge_request =
-      mojo::MakeRequest(&ns_view_bridge_remote_);
+  remote_cocoa::mojom::RenderWidgetHostNSViewHostAssociatedPtr client;
+  remote_ns_view_client_binding_.Bind(mojo::MakeRequest(&client));
+  remote_cocoa::mojom::RenderWidgetHostNSViewAssociatedRequest bridge_request =
+      mojo::MakeRequest(&remote_ns_view_ptr_);
 
-  // Cast from mojom::RenderWidgetHostNSViewClientPtr and
+  // Cast from mojom::RenderWidgetHostNSViewHostPtr and
   // mojom::RenderWidgetHostNSViewBridgeRequest to the public interfaces
   // accepted by the application.
   // TODO(ccameron): Remove the need for this cast.
@@ -280,8 +280,8 @@
   remote_cocoa_application->CreateRenderWidgetHostNSView(
       std::move(stub_client), std::move(stub_bridge_request));
 
-  ns_view_bridge_ = ns_view_bridge_remote_.get();
-  ns_view_bridge_remote_->SetParentWebContentsNSView(parent_ns_view_id);
+  ns_view_ = remote_ns_view_ptr_.get();
+  remote_ns_view_ptr_->SetParentWebContentsNSView(parent_ns_view_id);
 }
 
 void RenderWidgetHostViewMac::SetParentUiLayer(ui::Layer* parent_ui_layer) {
@@ -293,7 +293,7 @@
     // must be done lazily because not all code has been updated to use
     // ui::Views (e.g, content_shell).
     display_only_using_parent_ui_layer_ = true;
-    ns_view_bridge_->DisableDisplay();
+    ns_view_->DisableDisplay();
   }
   if (browser_compositor_)
     browser_compositor_->SetParentUiLayer(parent_ui_layer);
@@ -301,18 +301,19 @@
 
 void RenderWidgetHostViewMac::SetParentAccessibilityElement(
     id parent_accessibility_element) {
-  [cocoa_view() setAccessibilityParentElement:parent_accessibility_element];
+  [GetInProcessNSView()
+      setAccessibilityParentElement:parent_accessibility_element];
 }
 
-RenderWidgetHostViewCocoa* RenderWidgetHostViewMac::cocoa_view() const {
-  if (ns_view_bridge_local_)
-    return ns_view_bridge_local_->GetRenderWidgetHostViewCocoa();
+RenderWidgetHostViewCocoa* RenderWidgetHostViewMac::GetInProcessNSView() const {
+  if (in_process_ns_view_bridge_)
+    return in_process_ns_view_bridge_->GetNSView();
   return nullptr;
 }
 
 void RenderWidgetHostViewMac::SetDelegate(
     NSObject<RenderWidgetHostViewMacDelegate>* delegate) {
-  [cocoa_view() setResponderDelegate:delegate];
+  [GetInProcessNSView() setResponderDelegate:delegate];
 }
 
 ui::TextInputType RenderWidgetHostViewMac::GetTextInputType() {
@@ -363,7 +364,7 @@
   popup_parent_host_view_->popup_child_host_view_ = this;
 
   // This path is used by the time/date picker.
-  ns_view_bridge_->InitAsPopup(pos);
+  ns_view_->InitAsPopup(pos);
 }
 
 void RenderWidgetHostViewMac::InitAsFullscreen(
@@ -437,7 +438,7 @@
 
 void RenderWidgetHostViewMac::Show() {
   is_visible_ = true;
-  ns_view_bridge_->SetVisible(is_visible_);
+  ns_view_->SetVisible(is_visible_);
   browser_compositor_->SetViewVisible(is_visible_);
   browser_compositor_->SetRenderWidgetHostIsHidden(false);
 
@@ -446,7 +447,7 @@
 
 void RenderWidgetHostViewMac::Hide() {
   is_visible_ = false;
-  ns_view_bridge_->SetVisible(is_visible_);
+  ns_view_->SetVisible(is_visible_);
   browser_compositor_->SetViewVisible(is_visible_);
   host()->WasHidden();
   browser_compositor_->SetRenderWidgetHostIsHidden(true);
@@ -492,19 +493,19 @@
 }
 
 void RenderWidgetHostViewMac::SetBounds(const gfx::Rect& rect) {
-  ns_view_bridge_->SetBounds(rect);
+  ns_view_->SetBounds(rect);
 }
 
 gfx::NativeView RenderWidgetHostViewMac::GetNativeView() {
-  return cocoa_view();
+  return GetInProcessNSView();
 }
 
 gfx::NativeViewAccessible RenderWidgetHostViewMac::GetNativeViewAccessible() {
-  return cocoa_view();
+  return GetInProcessNSView();
 }
 
 void RenderWidgetHostViewMac::Focus() {
-  ns_view_bridge_->MakeFirstResponder();
+  ns_view_->MakeFirstResponder();
 }
 
 bool RenderWidgetHostViewMac::HasFocus() {
@@ -534,7 +535,7 @@
 }
 
 void RenderWidgetHostViewMac::DisplayCursor(const WebCursor& cursor) {
-  ns_view_bridge_->DisplayCursor(cursor);
+  ns_view_->DisplayCursor(cursor);
 }
 
 CursorManager* RenderWidgetHostViewMac::GetCursorManager() {
@@ -558,7 +559,7 @@
   if (!did_update_state)
     return;
 
-  ns_view_bridge_->SetTextInputType(GetTextInputType());
+  ns_view_->SetTextInputType(GetTextInputType());
 
   // |updated_view| is the last view to change its TextInputState which can be
   // used to start/stop monitoring composition info when it has a focused
@@ -593,7 +594,7 @@
 void RenderWidgetHostViewMac::OnImeCancelComposition(
     TextInputManager* text_input_manager,
     RenderWidgetHostViewBase* updated_view) {
-  ns_view_bridge_->CancelComposition();
+  ns_view_->CancelComposition();
 }
 
 void RenderWidgetHostViewMac::OnImeCompositionRangeChanged(
@@ -605,7 +606,7 @@
     return;
   // The RangeChanged message is only sent with valid values. The current
   // caret position (start == end) will be sent if there is no IME range.
-  ns_view_bridge_->SetCompositionRangeInfo(info->range);
+  ns_view_->SetCompositionRangeInfo(info->range);
 }
 
 void RenderWidgetHostViewMac::OnSelectionBoundsChanged(
@@ -650,8 +651,8 @@
   if (!selection)
     return;
 
-  ns_view_bridge_->SetTextSelection(selection->text(), selection->offset(),
-                                    selection->range());
+  ns_view_->SetTextSelection(selection->text(), selection->offset(),
+                             selection->range());
 }
 
 void RenderWidgetHostViewMac::OnGestureEvent(
@@ -687,15 +688,15 @@
   // it.
   if (mouse_locked_) {
     mouse_locked_ = false;
-    ns_view_bridge_->SetCursorLocked(false);
+    ns_view_->SetCursorLocked(false);
   }
 
   // Destroy the local and remote briges to the NSView. Note that the NSView on
-  // the other side of |ns_view_bridge_| may outlive us due to other retains.
-  ns_view_bridge_ = nullptr;
-  ns_view_bridge_local_.reset();
-  ns_view_client_binding_.Close();
-  ns_view_bridge_remote_.reset();
+  // the other side of |ns_view_| may outlive us due to other retains.
+  ns_view_ = nullptr;
+  in_process_ns_view_bridge_.reset();
+  remote_ns_view_client_binding_.Close();
+  remote_ns_view_ptr_.reset();
 
   // Delete the delegated frame state, which will reach back into
   // host().
@@ -723,7 +724,7 @@
 
 void RenderWidgetHostViewMac::DisplayTooltipText(
     const base::string16& tooltip_text) {
-  ns_view_bridge_->SetTooltipText(tooltip_text);
+  ns_view_->SetTooltipText(tooltip_text);
 }
 
 viz::ScopedSurfaceIdAllocator
@@ -820,7 +821,7 @@
 //
 
 void RenderWidgetHostViewMac::SetShowingContextMenu(bool showing) {
-  ns_view_bridge_->SetShowingContextMenu(showing);
+  ns_view_->SetShowingContextMenu(showing);
 }
 
 uint32_t RenderWidgetHostViewMac::GetCaptureSequenceNumber() const {
@@ -885,7 +886,7 @@
   const gfx::CALayerParams* ca_layer_params =
       view_mac->browser_compositor_->GetLastCALayerParams();
   if (ca_layer_params)
-    ns_view_bridge_->SetCALayerParams(*ca_layer_params);
+    ns_view_->SetCALayerParams(*ca_layer_params);
   browser_compositor_->TakeFallbackContentFrom(
       view_mac->browser_compositor_.get());
 }
@@ -1056,7 +1057,7 @@
 void RenderWidgetHostViewMac::FocusedNodeChanged(
     bool is_editable_node,
     const gfx::Rect& node_bounds_in_screen) {
-  ns_view_bridge_->CancelComposition();
+  ns_view_->CancelComposition();
 
   // If the Mac Zoom feature is enabled, update it with the bounds of the
   // current focused node so that it can ensure that it's scrolled into view.
@@ -1122,10 +1123,10 @@
   mouse_locked_ = true;
 
   // Lock position of mouse cursor and hide it.
-  ns_view_bridge_->SetCursorLocked(true);
+  ns_view_->SetCursorLocked(true);
 
   // Clear the tooltip window.
-  ns_view_bridge_->SetTooltipText(base::string16());
+  ns_view_->SetTooltipText(base::string16());
 
   return true;
 }
@@ -1134,7 +1135,7 @@
   if (!mouse_locked_)
     return;
   mouse_locked_ = false;
-  ns_view_bridge_->SetCursorLocked(false);
+  ns_view_->SetCursorLocked(false);
 
   if (host())
     host()->LostMouseLock();
@@ -1149,7 +1150,7 @@
       uint_dom_codes->push_back(static_cast<uint32_t>(dom_code));
   }
   is_keyboard_locked_ = true;
-  ns_view_bridge_->LockKeyboard(uint_dom_codes);
+  ns_view_->LockKeyboard(uint_dom_codes);
   return true;
 }
 
@@ -1158,7 +1159,7 @@
     return;
 
   is_keyboard_locked_ = false;
-  ns_view_bridge_->UnlockKeyboard();
+  ns_view_->UnlockKeyboard();
 }
 
 bool RenderWidgetHostViewMac::IsKeyboardLocked() {
@@ -1183,7 +1184,8 @@
     case WebInputEvent::kGestureScrollBegin:
     case WebInputEvent::kGestureScrollUpdate:
     case WebInputEvent::kGestureScrollEnd:
-      [cocoa_view() processedGestureScrollEvent:event consumed:consumed];
+      [GetInProcessNSView() processedGestureScrollEvent:event
+                                               consumed:consumed];
       return;
     default:
       break;
@@ -1210,7 +1212,7 @@
 
 void RenderWidgetHostViewMac::DidOverscroll(
     const ui::DidOverscrollParams& params) {
-  [cocoa_view() processedOverscroll:params];
+  [GetInProcessNSView() processedOverscroll:params];
 }
 
 std::unique_ptr<SyntheticGestureTarget>
@@ -1218,7 +1220,7 @@
   RenderWidgetHostImpl* host =
       RenderWidgetHostImpl::From(GetRenderWidgetHost());
   return std::unique_ptr<SyntheticGestureTarget>(
-      new SyntheticGestureTargetMac(host, cocoa_view()));
+      new SyntheticGestureTargetMac(host, GetInProcessNSView()));
 }
 
 const viz::LocalSurfaceIdAllocation&
@@ -1344,7 +1346,7 @@
 
 void RenderWidgetHostViewMac::ShowDefinitionForSelection() {
   // This will round-trip to the NSView to determine the selection range.
-  ns_view_bridge_->ShowDictionaryOverlayForSelection();
+  ns_view_->ShowDictionaryOverlayForSelection();
 }
 
 void RenderWidgetHostViewMac::UpdateBackgroundColor() {
@@ -1372,7 +1374,7 @@
   if (color == background_layer_color_)
     return;
   background_layer_color_ = color;
-  ns_view_bridge_->SetBackgroundColor(color);
+  ns_view_->SetBackgroundColor(color);
 }
 
 BrowserAccessibilityManager*
@@ -1384,14 +1386,14 @@
 
 gfx::NativeViewAccessible
 RenderWidgetHostViewMac::AccessibilityGetNativeViewAccessible() {
-  return cocoa_view();
+  return GetInProcessNSView();
 }
 
 gfx::NativeViewAccessible
 RenderWidgetHostViewMac::AccessibilityGetNativeViewAccessibleForWindow() {
   if (remote_window_accessible_)
     return remote_window_accessible_.get();
-  return [cocoa_view() window];
+  return [GetInProcessNSView() window];
 }
 
 void RenderWidgetHostViewMac::SetTextInputActive(bool active) {
@@ -1416,7 +1418,7 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-// RenderWidgetHostNSViewClientHelper and mojom::RenderWidgetHostNSViewClient
+// RenderWidgetHostNSViewHostHelper and mojom::RenderWidgetHostNSViewHost
 // implementation:
 
 id RenderWidgetHostViewMac::GetRootBrowserAccessibilityElement() {
@@ -1612,7 +1614,7 @@
     host()->ForwardMouseEvent(web_event);
 
   if (web_event.GetType() == WebInputEvent::kMouseLeave)
-    ns_view_bridge_->SetTooltipText(base::string16());
+    ns_view_->SetTooltipText(base::string16());
 }
 
 void RenderWidgetHostViewMac::ForwardWheelEvent(
@@ -1922,8 +1924,8 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-// mojom::RenderWidgetHostNSViewClient functions that translate events and
-// forward them to the RenderWidgetHostNSViewClientHelper implementation:
+// mojom::RenderWidgetHostNSViewHost functions that translate events and
+// forward them to the RenderWidgetHostNSViewHostHelper implementation:
 
 void RenderWidgetHostViewMac::ForwardKeyboardEvent(
     std::unique_ptr<InputEvent> input_event,
@@ -2112,7 +2114,7 @@
       if (auto* rwhv = widget_host->GetView())
         baseline_point = rwhv->TransformPointToRootCoordSpace(baseline_point);
     }
-    ns_view_bridge_->ShowDictionaryOverlay(encoded_string, baseline_point);
+    ns_view_->ShowDictionaryOverlay(encoded_string, baseline_point);
   }
 }
 
diff --git a/content/browser/renderer_host/render_widget_host_view_mac_browsertest.mm b/content/browser/renderer_host/render_widget_host_view_mac_browsertest.mm
index 4c51146..72ec2ae0 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac_browsertest.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac_browsertest.mm
@@ -75,9 +75,9 @@
   RenderWidgetHostViewMac* rwhv_mac =
       static_cast<RenderWidgetHostViewMac*>(rwhv);
 
-  NSRect rect =
-      [rwhv_mac->cocoa_view() firstRectForCharacterRange:NSMakeRange(2, 1)
-                                             actualRange:nullptr];
+  NSRect rect = [rwhv_mac->GetInProcessNSView()
+      firstRectForCharacterRange:NSMakeRange(2, 1)
+                     actualRange:nullptr];
   EXPECT_GT(NSMinX(rect), 0);
   EXPECT_GT(NSWidth(rect), 0);
   EXPECT_GT(NSHeight(rect), 0);
diff --git a/content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper.h b/content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper.h
index e48fbadb..d5eb9e9 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper.h
+++ b/content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper.h
@@ -43,7 +43,7 @@
   // Each selector is connected to a single c method which forwards the message
   // to WebCore's ExecuteEditCommand() function.
   // This method is idempotent.
-  // The class passed in must conform to the RenderWidgetHostNSViewClientOwner
+  // The class passed in must conform to the RenderWidgetHostNSViewHostOwner
   // protocol.
   void AddEditingSelectorsToClass(Class klass);
 
@@ -52,7 +52,7 @@
   // owner - An object we can retrieve a RenderWidgetHostViewMac from to
   // determine the command states.
   bool IsMenuItemEnabled(SEL item_action,
-                         id<RenderWidgetHostNSViewClientOwner> owner);
+                         id<RenderWidgetHostNSViewHostOwner> owner);
 
   // Converts an editing selector into a command name that can be sent to
   // webkit.
diff --git a/content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper.mm b/content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper.mm
index d0e908ba..680b0a8b 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper.mm
@@ -116,7 +116,7 @@
 // RenderWidgetHostViewMacEditCommandHelper::AddEditingSelectorsToClass().
 //
 // self - the object we're attached to; it must implement the
-// RenderWidgetHostNSViewClientOwner protocol.
+// RenderWidgetHostNSViewHostOwner protocol.
 // _cmd - the selector that fired.
 // sender - the id of the object that sent the message.
 //
@@ -129,8 +129,7 @@
 // The WebFrame is in the Chrome glue layer and forwards the message to WebCore.
 void EditCommandImp(id self, SEL _cmd, id sender) {
   // Make sure |self| is the right type.
-  DCHECK(
-      [self conformsToProtocol:@protocol(RenderWidgetHostNSViewClientOwner)]);
+  DCHECK([self conformsToProtocol:@protocol(RenderWidgetHostNSViewHostOwner)]);
 
   // SEL -> command name string.
   NSString* command_name_ns =
@@ -138,10 +137,10 @@
   std::string command([command_name_ns UTF8String]);
 
   // Forward the edit command string down the pipeline.
-  mojom::RenderWidgetHostNSViewClient* client = [(
-      id<RenderWidgetHostNSViewClientOwner>)self renderWidgetHostNSViewClient];
-  DCHECK(client);
-  client->ExecuteEditCommand(command);
+  remote_cocoa::mojom::RenderWidgetHostNSViewHost* host =
+      [(id<RenderWidgetHostNSViewHostOwner>)self renderWidgetHostNSViewHost];
+  DCHECK(host);
+  host->ExecuteEditCommand(command);
 }
 
 }  // namespace
@@ -208,7 +207,7 @@
 
 bool RenderWidgetHostViewMacEditCommandHelper::IsMenuItemEnabled(
     SEL item_action,
-    id<RenderWidgetHostNSViewClientOwner> owner) {
+    id<RenderWidgetHostNSViewHostOwner> owner) {
   const char* selector_name = sel_getName(item_action);
   // TODO(jeremy): The final form of this function will check state
   // associated with the Browser.
diff --git a/content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper_unittest.mm b/content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper_unittest.mm
index f27cb7c6..3fc406e 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper_unittest.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper_unittest.mm
@@ -36,15 +36,15 @@
 @end
 
 // Class that owns a RenderWidgetHostViewMac.
-@interface RenderWidgetHostNSViewClientOwner
-    : NSObject<RenderWidgetHostNSViewClientOwner> {
+@interface RenderWidgetHostNSViewHostOwner
+    : NSObject <RenderWidgetHostNSViewHostOwner> {
   RenderWidgetHostViewMac* rwhvm_;
 }
 
 - (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)rwhvm;
 @end
 
-@implementation RenderWidgetHostNSViewClientOwner
+@implementation RenderWidgetHostNSViewHostOwner
 
 - (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)rwhvm {
   if ((self = [super init])) {
@@ -53,7 +53,7 @@
   return self;
 }
 
-- (content::mojom::RenderWidgetHostNSViewClient*)renderWidgetHostNSViewClient {
+- (remote_cocoa::mojom::RenderWidgetHostNSViewHost*)renderWidgetHostNSViewHost {
   return rwhvm_;
 }
 
@@ -152,16 +152,16 @@
 
   ui::WindowResizeHelperMac::Get()->Init(base::ThreadTaskRunnerHandle::Get());
 
-  // Owned by its |cocoa_view()|, i.e. |rwhv_cocoa|.
+  // Owned by its |GetInProcessNSView()|, i.e. |rwhv_cocoa|.
   RenderWidgetHostViewMac* rwhv_mac = new RenderWidgetHostViewMac(
       render_widget, false);
   base::scoped_nsobject<RenderWidgetHostViewCocoa> rwhv_cocoa(
-      [rwhv_mac->cocoa_view() retain]);
+      [rwhv_mac->GetInProcessNSView() retain]);
 
   RenderWidgetHostViewMacEditCommandHelper helper;
   NSArray* edit_command_strings = helper.GetEditSelectorNames();
-  RenderWidgetHostNSViewClientOwner* rwhwvm_owner =
-      [[[RenderWidgetHostNSViewClientOwner alloc]
+  RenderWidgetHostNSViewHostOwner* rwhwvm_owner =
+      [[[RenderWidgetHostNSViewHostOwner alloc]
           initWithRenderWidgetHostViewMac:rwhv_mac] autorelease];
 
   helper.AddEditingSelectorsToClass([rwhwvm_owner class]);
@@ -215,8 +215,8 @@
 // Test RenderWidgetHostViewMacEditCommandHelper::IsMenuItemEnabled.
 TEST_F(RenderWidgetHostViewMacEditCommandHelperTest, TestMenuItemEnabling) {
   RenderWidgetHostViewMacEditCommandHelper helper;
-  RenderWidgetHostNSViewClientOwner* rwhvm_owner =
-      [[[RenderWidgetHostNSViewClientOwner alloc] init] autorelease];
+  RenderWidgetHostNSViewHostOwner* rwhvm_owner =
+      [[[RenderWidgetHostNSViewHostOwner alloc] init] autorelease];
 
   // The select all menu should always be enabled.
   SEL select_all = NSSelectorFromString(@"selectAll:");
diff --git a/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm b/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
index ecbabcc..859cb7c6 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
@@ -492,7 +492,7 @@
         &delegate_, process_host_.get(), process_host_->GetNextRoutingID()));
     host_->set_owner_delegate(&mock_owner_delegate_);
     rwhv_mac_ = new RenderWidgetHostViewMac(host_.get(), false);
-    rwhv_cocoa_.reset([rwhv_mac_->cocoa_view() retain]);
+    rwhv_cocoa_.reset([rwhv_mac_->GetInProcessNSView() retain]);
 
     window_.reset([[CocoaTestHelperWindow alloc] init]);
     window_.get().pretendIsKeyWindow = YES;
@@ -599,7 +599,7 @@
       host_->GetAndResetDispatchedMessages();
 
   EXPECT_EQ(0U, events.size());
-  [rwhv_mac_->cocoa_view()
+  [rwhv_mac_->GetInProcessNSView()
       keyEvent:cocoa_test_event_utils::KeyEventWithKeyCode(
                    0x7B, 0xF70F, NSKeyDown, NSControlKeyMask)];
   base::RunLoop().RunUntilIdle();
@@ -611,7 +611,7 @@
   // fire keypress event
   process_host_->sink().ClearMessages();
   EXPECT_EQ(0U, process_host_->sink().message_count());
-  [rwhv_mac_->cocoa_view()
+  [rwhv_mac_->GetInProcessNSView()
       keyEvent:cocoa_test_event_utils::KeyEventWithKeyCode(
                    0x2E, 0xF728, NSKeyDown, NSControlKeyMask)];
   base::RunLoop().RunUntilIdle();
@@ -619,7 +619,7 @@
   EXPECT_EQ("RawKeyDown", GetMessageNames(events));
 
   // Simulate a printable char, should generate keypress event
-  [rwhv_mac_->cocoa_view()
+  [rwhv_mac_->GetInProcessNSView()
       keyEvent:cocoa_test_event_utils::KeyEventWithKeyCode(0x58, 'x', NSKeyDown,
                                                            NSControlKeyMask)];
   base::RunLoop().RunUntilIdle();
@@ -632,8 +632,9 @@
 TEST_F(RenderWidgetHostViewMacTest, InvalidKeyCode) {
   // Simulate "Convert" key on JIS PC keyboard, will generate a |NSFlagsChanged|
   // NSEvent with |keyCode| == 0xFF.
-  [rwhv_mac_->cocoa_view() keyEvent:cocoa_test_event_utils::KeyEventWithKeyCode(
-                                        0xFF, 0, NSFlagsChanged, 0)];
+  [rwhv_mac_->GetInProcessNSView()
+      keyEvent:cocoa_test_event_utils::KeyEventWithKeyCode(0xFF, 0,
+                                                           NSFlagsChanged, 0)];
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(0U, host_->GetAndResetDispatchedMessages().size());
 }
@@ -906,7 +907,7 @@
 // |RenderWidgetHostImp::Focus()|.
 TEST_F(RenderWidgetHostViewMacTest, BlurAndFocusOnSetActive) {
   EXPECT_CALL(*host_, Focus());
-  [window_ makeFirstResponder:rwhv_mac_->cocoa_view()];
+  [window_ makeFirstResponder:rwhv_mac_->GetInProcessNSView()];
   testing::Mock::VerifyAndClearExpectations(host_.get());
 
   EXPECT_CALL(*host_, Blur());
@@ -935,7 +936,7 @@
   // Verifies that ui::INPUT_EVENT_LATENCY_UI_COMPONENT is added
   // properly in scrollWheel function.
   NSEvent* wheelEvent1 = MockScrollWheelEventWithPhase(@selector(phaseBegan),3);
-  [rwhv_mac_->cocoa_view() scrollWheel:wheelEvent1];
+  [rwhv_mac_->GetInProcessNSView() scrollWheel:wheelEvent1];
   ASSERT_TRUE(host_->lastWheelEventLatencyInfo.FindLatency(
       ui::INPUT_EVENT_LATENCY_UI_COMPONENT, nullptr));
 
@@ -944,7 +945,7 @@
   // properly in shortCircuitScrollWheelEvent function which is called
   // in scrollWheel.
   NSEvent* wheelEvent2 = MockScrollWheelEventWithPhase(@selector(phaseEnded),0);
-  [rwhv_mac_->cocoa_view() scrollWheel:wheelEvent2];
+  [rwhv_mac_->GetInProcessNSView() scrollWheel:wheelEvent2];
   ASSERT_TRUE(host_->lastWheelEventLatencyInfo.FindLatency(
       ui::INPUT_EVENT_LATENCY_UI_COMPONENT, nullptr));
 }
@@ -955,7 +956,7 @@
   // Send a wheel event for scrolling by 3 lines.
   // Verifies that SourceEventType exists in forwarded LatencyInfo object.
   NSEvent* wheelEvent = MockScrollWheelEventWithPhase(@selector(phaseBegan), 3);
-  [rwhv_mac_->cocoa_view() scrollWheel:wheelEvent];
+  [rwhv_mac_->GetInProcessNSView() scrollWheel:wheelEvent];
   ASSERT_TRUE(host_->lastWheelEventLatencyInfo.source_event_type() ==
               ui::SourceEventType::WHEEL);
 }
@@ -963,7 +964,7 @@
 TEST_F(RenderWidgetHostViewMacTest, ScrollWheelEndEventDelivery) {
   // Send an initial wheel event with NSEventPhaseBegan to the view.
   NSEvent* event1 = MockScrollWheelEventWithPhase(@selector(phaseBegan), 0);
-  [rwhv_mac_->cocoa_view() scrollWheel:event1];
+  [rwhv_mac_->GetInProcessNSView() scrollWheel:event1];
 
   // Flush and clear other messages (e.g. begin frames) the RWHVMac also sends.
   base::RunLoop().RunUntilIdle();
@@ -990,14 +991,14 @@
   // Send a NSEvent of NSTabletProximity type which has a device type of eraser.
   NSEvent* event = MockTabletEventWithParams(kCGEventTabletProximity, true,
                                              NSEraserPointingDevice);
-  [rwhv_mac_->cocoa_view() tabletEvent:event];
+  [rwhv_mac_->GetInProcessNSView() tabletEvent:event];
   // Flush and clear other messages (e.g. begin frames) the RWHVMac also sends.
   base::RunLoop().RunUntilIdle();
 
   event =
       MockMouseEventWithParams(kCGEventMouseMoved, {6, 9}, kCGMouseButtonLeft,
                                kCGEventMouseSubtypeTabletPoint);
-  [rwhv_mac_->cocoa_view() mouseEvent:event];
+  [rwhv_mac_->GetInProcessNSView() mouseEvent:event];
   base::RunLoop().RunUntilIdle();
   MockWidgetInputHandler::MessageVector events =
       host_->GetAndResetDispatchedMessages();
@@ -1010,14 +1011,14 @@
   // Send a NSEvent of NSTabletProximity type which has a device type of pen.
   NSEvent* event = MockTabletEventWithParams(kCGEventTabletProximity, true,
                                              NSPenPointingDevice);
-  [rwhv_mac_->cocoa_view() tabletEvent:event];
+  [rwhv_mac_->GetInProcessNSView() tabletEvent:event];
   // Flush and clear other messages (e.g. begin frames) the RWHVMac also sends.
   base::RunLoop().RunUntilIdle();
 
   event =
       MockMouseEventWithParams(kCGEventMouseMoved, {6, 9}, kCGMouseButtonLeft,
                                kCGEventMouseSubtypeTabletPoint);
-  [rwhv_mac_->cocoa_view() mouseEvent:event];
+  [rwhv_mac_->GetInProcessNSView() mouseEvent:event];
   base::RunLoop().RunUntilIdle();
   MockWidgetInputHandler::MessageVector events =
       host_->GetAndResetDispatchedMessages();
@@ -1032,7 +1033,7 @@
   NSEvent* event =
       MockMouseEventWithParams(kCGEventMouseMoved, {6, 9}, kCGMouseButtonLeft,
                                kCGEventMouseSubtypeTabletProximity, true);
-  [rwhv_mac_->cocoa_view() mouseEvent:event];
+  [rwhv_mac_->GetInProcessNSView() mouseEvent:event];
   base::RunLoop().RunUntilIdle();
   MockWidgetInputHandler::MessageVector events =
       host_->GetAndResetDispatchedMessages();
@@ -1042,7 +1043,7 @@
   events.clear();
 
   event = cocoa_test_event_utils::EnterEvent({1, 1}, window_);
-  [rwhv_mac_->cocoa_view() mouseEntered:event];
+  [rwhv_mac_->GetInProcessNSView() mouseEntered:event];
   base::RunLoop().RunUntilIdle();
   events = host_->GetAndResetDispatchedMessages();
   ASSERT_EQ("MouseMove", GetMessageNames(events));
@@ -1057,7 +1058,7 @@
   NSEvent* event =
       MockMouseEventWithParams(kCGEventMouseMoved, {6, 9}, kCGMouseButtonLeft,
                                kCGEventMouseSubtypeDefault);
-  [rwhv_mac_->cocoa_view() mouseEvent:event];
+  [rwhv_mac_->GetInProcessNSView() mouseEvent:event];
   base::RunLoop().RunUntilIdle();
   MockWidgetInputHandler::MessageVector events =
       host_->GetAndResetDispatchedMessages();
@@ -1070,7 +1071,7 @@
   // Send a NSEvent of NSTabletProximity type which has a device type of pen.
   NSEvent* event = MockTabletEventWithParams(kCGEventTabletProximity, true,
                                              NSPenPointingDevice);
-  [rwhv_mac_->cocoa_view() tabletEvent:event];
+  [rwhv_mac_->GetInProcessNSView() tabletEvent:event];
   // Flush and clear other messages (e.g. begin frames) the RWHVMac also sends.
   base::RunLoop().RunUntilIdle();
   static_cast<RenderWidgetHostImpl*>(rwhv_mac_->GetRenderWidgetHost())
@@ -1080,7 +1081,7 @@
   event = MockMouseEventWithParams(
       kCGEventLeftMouseDown, {6, 9}, kCGMouseButtonLeft,
       kCGEventMouseSubtypeTabletPoint, false, true);
-  [rwhv_mac_->cocoa_view() mouseEvent:event];
+  [rwhv_mac_->GetInProcessNSView() mouseEvent:event];
   base::RunLoop().RunUntilIdle();
   MockWidgetInputHandler::MessageVector events =
       host_->GetAndResetDispatchedMessages();
@@ -1094,7 +1095,7 @@
   event = MockMouseEventWithParams(
       kCGEventLeftMouseDragged, {16, 29}, kCGMouseButtonLeft,
       kCGEventMouseSubtypeTabletPoint, false, true);
-  [rwhv_mac_->cocoa_view() mouseEvent:event];
+  [rwhv_mac_->GetInProcessNSView() mouseEvent:event];
   base::RunLoop().RunUntilIdle();
   events = host_->GetAndResetDispatchedMessages();
   ASSERT_EQ("TouchMove", GetMessageNames(events));
@@ -1108,7 +1109,7 @@
   event = MockMouseEventWithParams(kCGEventLeftMouseUp, {16, 29},
                                    kCGMouseButtonLeft,
                                    kCGEventMouseSubtypeTabletPoint, false);
-  [rwhv_mac_->cocoa_view() mouseEvent:event];
+  [rwhv_mac_->GetInProcessNSView() mouseEvent:event];
   base::RunLoop().RunUntilIdle();
   events = host_->GetAndResetDispatchedMessages();
   ASSERT_EQ("TouchEnd GestureScrollEnd", GetMessageNames(events));
@@ -1125,7 +1126,7 @@
   event =
       MockMouseEventWithParams(kCGEventLeftMouseDown, {6, 9},
                                kCGMouseButtonLeft, kCGEventMouseSubtypeDefault);
-  [rwhv_mac_->cocoa_view() mouseEvent:event];
+  [rwhv_mac_->GetInProcessNSView() mouseEvent:event];
   base::RunLoop().RunUntilIdle();
   events = host_->GetAndResetDispatchedMessages();
   ASSERT_EQ("MouseDown", GetMessageNames(events));
@@ -1161,7 +1162,7 @@
 
   // Send an initial wheel event for scrolling by 3 lines.
   NSEvent* event1 = MockScrollWheelEventWithPhase(@selector(phaseBegan), 3);
-  [rwhv_mac_->cocoa_view() scrollWheel:event1];
+  [rwhv_mac_->GetInProcessNSView() scrollWheel:event1];
   base::RunLoop().RunUntilIdle();
 
   MockWidgetInputHandler::MessageVector events =
@@ -1187,7 +1188,7 @@
 
   // Send another wheel event, this time for scrolling by 0 lines (empty event).
   NSEvent* event2 = MockScrollWheelEventWithPhase(@selector(phaseChanged), 0);
-  [rwhv_mac_->cocoa_view() scrollWheel:event2];
+  [rwhv_mac_->GetInProcessNSView() scrollWheel:event2];
   base::RunLoop().RunUntilIdle();
   events = host_->GetAndResetDispatchedMessages();
   ASSERT_EQ("MouseWheel", GetMessageNames(events));
@@ -1206,7 +1207,7 @@
 TEST_F(RenderWidgetHostViewMacTest, GuestViewDoesNotLeak) {
   int32_t routing_id = process_host_->GetNextRoutingID();
 
-  // Owned by its |cocoa_view()|.
+  // Owned by its |GetInProcessNSView()|.
   MockRenderWidgetHostImpl* rwh = MockRenderWidgetHostImpl::Create(
       &delegate_, process_host_.get(), routing_id);
   RenderWidgetHostViewMac* view = new RenderWidgetHostViewMac(rwh, true);
@@ -1220,10 +1221,10 @@
       (RenderWidgetHostViewGuest::Create(rwh, nullptr, view->GetWeakPtr()))
           ->GetWeakPtr();
 
-  // Remove the cocoa_view() so |view| also goes away before |rwh|.
+  // Remove the GetInProcessNSView() so |view| also goes away before |rwh|.
   {
     base::scoped_nsobject<RenderWidgetHostViewCocoa> rwhv_cocoa;
-    rwhv_cocoa.reset([view->cocoa_view() retain]);
+    rwhv_cocoa.reset([view->GetInProcessNSView() retain]);
   }
   RecycleAndWait();
 
@@ -1284,7 +1285,7 @@
 
   // Send a wheel event without phase information for scrolling by 3 lines.
   NSEvent* wheelEvent = MockScrollWheelEventWithoutPhase(3);
-  [rwhv_mac_->cocoa_view() scrollWheel:wheelEvent];
+  [rwhv_mac_->GetInProcessNSView() scrollWheel:wheelEvent];
   base::RunLoop().RunUntilIdle();
   MockWidgetInputHandler::MessageVector events =
       host_->GetAndResetDispatchedMessages();
@@ -1336,7 +1337,7 @@
   // Send an initial wheel event for scrolling by 3 lines.
   NSEvent* wheelEvent1 =
       MockScrollWheelEventWithPhase(@selector(phaseBegan), 3);
-  [view->cocoa_view() scrollWheel:wheelEvent1];
+  [view->GetInProcessNSView() scrollWheel:wheelEvent1];
   base::RunLoop().RunUntilIdle();
   MockWidgetInputHandler::MessageVector events =
       host->GetAndResetDispatchedMessages();
@@ -1352,7 +1353,7 @@
   // mouse_wheel_end_dispatch_timer_ will start.
   NSEvent* wheelEvent2 =
       MockScrollWheelEventWithPhase(@selector(phaseEnded), 0);
-  [view->cocoa_view() scrollWheel:wheelEvent2];
+  [view->GetInProcessNSView() scrollWheel:wheelEvent2];
   base::RunLoop().RunUntilIdle();
   events = host->GetAndResetDispatchedMessages();
   ASSERT_EQ(0U, events.size());
@@ -1391,7 +1392,7 @@
   // Send an initial wheel event for scrolling by 3 lines.
   NSEvent* wheelEvent1 =
       MockScrollWheelEventWithPhase(@selector(phaseBegan), 3);
-  [view->cocoa_view() scrollWheel:wheelEvent1];
+  [view->GetInProcessNSView() scrollWheel:wheelEvent1];
   base::RunLoop().RunUntilIdle();
   MockWidgetInputHandler::MessageVector events =
       host->GetAndResetDispatchedMessages();
@@ -1408,7 +1409,7 @@
   // mouse_wheel_end_dispatch_timer_ will start.
   NSEvent* wheelEvent2 =
       MockScrollWheelEventWithPhase(@selector(phaseEnded), 0);
-  [view->cocoa_view() scrollWheel:wheelEvent2];
+  [view->GetInProcessNSView() scrollWheel:wheelEvent2];
   base::RunLoop().RunUntilIdle();
   events = host->GetAndResetDispatchedMessages();
   ASSERT_EQ(0U, events.size());
@@ -1419,7 +1420,7 @@
   NSEvent* wheelEvent3 =
       MockScrollWheelEventWithMomentumPhase(@selector(phaseBegan), 3);
   ASSERT_TRUE(wheelEvent3);
-  [view->cocoa_view() scrollWheel:wheelEvent3];
+  [view->GetInProcessNSView() scrollWheel:wheelEvent3];
   base::RunLoop().RunUntilIdle();
   events = host->GetAndResetDispatchedMessages();
   ASSERT_EQ("MouseWheel GestureScrollUpdate", GetMessageNames(events));
@@ -1447,7 +1448,7 @@
   // Send an initial wheel event for scrolling by 3 lines.
   NSEvent* wheelEvent1 =
       MockScrollWheelEventWithPhase(@selector(phaseBegan), 3);
-  [view->cocoa_view() scrollWheel:wheelEvent1];
+  [view->GetInProcessNSView() scrollWheel:wheelEvent1];
   base::RunLoop().RunUntilIdle();
   MockWidgetInputHandler::MessageVector events =
       host->GetAndResetDispatchedMessages();
@@ -1464,7 +1465,7 @@
   // mouse_wheel_end_dispatch_timer_ will start.
   NSEvent* wheelEvent2 =
       MockScrollWheelEventWithPhase(@selector(phaseEnded), 0);
-  [view->cocoa_view() scrollWheel:wheelEvent2];
+  [view->GetInProcessNSView() scrollWheel:wheelEvent2];
   base::RunLoop().RunUntilIdle();
   events = host->GetAndResetDispatchedMessages();
   ASSERT_EQ(0U, events.size());
@@ -1476,7 +1477,7 @@
   NSEvent* wheelEvent3 =
       MockScrollWheelEventWithPhase(@selector(phaseBegan), 3);
   ASSERT_TRUE(wheelEvent3);
-  [view->cocoa_view() scrollWheel:wheelEvent3];
+  [view->GetInProcessNSView() scrollWheel:wheelEvent3];
   base::RunLoop().RunUntilIdle();
   events = host->GetAndResetDispatchedMessages();
   ASSERT_EQ("MouseWheel GestureScrollEnd MouseWheel", GetMessageNames(events));
@@ -1705,7 +1706,7 @@
   // Send an initial wheel event for scrolling by 3 lines.
   // Verify that Event.Latency.OS.MOUSE_WHEEL histogram is computed properly.
   NSEvent* wheelEvent = MockScrollWheelEventWithPhase(@selector(phaseBegan),3);
-  [rwhv_mac_->cocoa_view() scrollWheel:wheelEvent];
+  [rwhv_mac_->GetInProcessNSView() scrollWheel:wheelEvent];
   histogram_tester.ExpectTotalCount("Event.Latency.OS.MOUSE_WHEEL", 1);
 }
 
@@ -1773,11 +1774,13 @@
   }
   RenderWidgetHostViewMac* tab_view() { return rwhv_mac_; }
   RenderWidgetHostImpl* tab_widget() { return host_.get(); }
-  RenderWidgetHostViewCocoa* tab_cocoa_view() { return rwhv_cocoa_.get(); }
+  RenderWidgetHostViewCocoa* tab_GetInProcessNSView() {
+    return rwhv_cocoa_.get();
+  }
 
   API_AVAILABLE(macos(10.12.2))
   NSCandidateListTouchBarItem* candidate_list_item() {
-    return [tab_cocoa_view().touchBar
+    return [tab_GetInProcessNSView().touchBar
         itemForIdentifier:NSTouchBarItemIdentifierCandidateList];
   }
 
@@ -1801,7 +1804,7 @@
   // tests as well). We should observe an IPC being sent to the |child_widget_|.
   SetTextInputType(child_view_, ui::TEXT_INPUT_TYPE_TEXT);
   EXPECT_EQ(child_widget_, text_input_manager()->GetActiveWidget());
-  [tab_cocoa_view() unmarkText];
+  [tab_GetInProcessNSView() unmarkText];
   base::RunLoop().RunUntilIdle();
   MockWidgetInputHandler::MessageVector events =
       child_widget_->GetAndResetDispatchedMessages();
@@ -1810,7 +1813,7 @@
   // Repeat the same steps for the tab's view .
   SetTextInputType(tab_view(), ui::TEXT_INPUT_TYPE_TEXT);
   EXPECT_EQ(tab_widget(), text_input_manager()->GetActiveWidget());
-  [tab_cocoa_view() unmarkText];
+  [tab_GetInProcessNSView() unmarkText];
   base::RunLoop().RunUntilIdle();
   events = host_->GetAndResetDispatchedMessages();
   EXPECT_EQ("FinishComposingText", GetMessageNames(events));
@@ -1829,9 +1832,9 @@
   // should observe an IPC being sent to the |child_widget_|.
   SetTextInputType(child_view_, ui::TEXT_INPUT_TYPE_TEXT);
   EXPECT_EQ(child_widget_, text_input_manager()->GetActiveWidget());
-  [tab_cocoa_view() setMarkedText:text
-                    selectedRange:selectedRange
-                 replacementRange:replacementRange];
+  [tab_GetInProcessNSView() setMarkedText:text
+                            selectedRange:selectedRange
+                         replacementRange:replacementRange];
   base::RunLoop().RunUntilIdle();
   MockWidgetInputHandler::MessageVector events =
       child_widget_->GetAndResetDispatchedMessages();
@@ -1840,9 +1843,9 @@
   // Repeat the same steps for the tab's view.
   SetTextInputType(tab_view(), ui::TEXT_INPUT_TYPE_TEXT);
   EXPECT_EQ(tab_widget(), text_input_manager()->GetActiveWidget());
-  [tab_cocoa_view() setMarkedText:text
-                    selectedRange:selectedRange
-                 replacementRange:replacementRange];
+  [tab_GetInProcessNSView() setMarkedText:text
+                            selectedRange:selectedRange
+                         replacementRange:replacementRange];
   base::RunLoop().RunUntilIdle();
   events = host_->GetAndResetDispatchedMessages();
   EXPECT_EQ("SetComposition", GetMessageNames(events));
@@ -1860,7 +1863,7 @@
   // should observe an IPC being sent to the |child_widget_|.
   SetTextInputType(child_view_, ui::TEXT_INPUT_TYPE_TEXT);
   EXPECT_EQ(child_widget_, text_input_manager()->GetActiveWidget());
-  [tab_cocoa_view() insertText:text replacementRange:replacementRange];
+  [tab_GetInProcessNSView() insertText:text replacementRange:replacementRange];
   base::RunLoop().RunUntilIdle();
   MockWidgetInputHandler::MessageVector events =
       child_widget_->GetAndResetDispatchedMessages();
@@ -1869,7 +1872,7 @@
   // Repeat the same steps for the tab's view.
   SetTextInputType(tab_view(), ui::TEXT_INPUT_TYPE_TEXT);
   EXPECT_EQ(tab_widget(), text_input_manager()->GetActiveWidget());
-  [tab_cocoa_view() insertText:text replacementRange:replacementRange];
+  [tab_GetInProcessNSView() insertText:text replacementRange:replacementRange];
   base::RunLoop().RunUntilIdle();
   events = host_->GetAndResetDispatchedMessages();
   EXPECT_EQ("CommitText", GetMessageNames(events));
@@ -1892,10 +1895,10 @@
   // we will first call setMarkedText on cocoa view. This would lead to a set
   // composition IPC in the sink, but it doesn't matter since we will be looking
   // for a finish composing text IPC for this test.
-  [tab_cocoa_view() setMarkedText:text
-                    selectedRange:selectedRange
-                 replacementRange:replacementRange];
-  [tab_cocoa_view() finishComposingText];
+  [tab_GetInProcessNSView() setMarkedText:text
+                            selectedRange:selectedRange
+                         replacementRange:replacementRange];
+  [tab_GetInProcessNSView() finishComposingText];
   base::RunLoop().RunUntilIdle();
   MockWidgetInputHandler::MessageVector events =
       child_widget_->GetAndResetDispatchedMessages();
@@ -1904,10 +1907,10 @@
   // Repeat the same steps for the tab's view.
   SetTextInputType(tab_view(), ui::TEXT_INPUT_TYPE_TEXT);
   EXPECT_EQ(tab_widget(), text_input_manager()->GetActiveWidget());
-  [tab_cocoa_view() setMarkedText:text
-                    selectedRange:selectedRange
-                 replacementRange:replacementRange];
-  [tab_cocoa_view() finishComposingText];
+  [tab_GetInProcessNSView() setMarkedText:text
+                            selectedRange:selectedRange
+                         replacementRange:replacementRange];
+  [tab_GetInProcessNSView() finishComposingText];
   base::RunLoop().RunUntilIdle();
   events = host_->GetAndResetDispatchedMessages();
   EXPECT_EQ("SetComposition FinishComposingText", GetMessageNames(events));
@@ -1922,7 +1925,7 @@
   EXPECT_CALL(*host_, Focus()).Times(::testing::AnyNumber());
   EXPECT_CALL(*host_, Blur()).Times(::testing::AnyNumber());
 
-  [window_ makeFirstResponder:tab_view()->cocoa_view()];
+  [window_ makeFirstResponder:tab_view()->GetInProcessNSView()];
 
   // Shouldn't enable secure input if it's not a password textfield.
   tab_view()->SetActive(true);
@@ -1952,20 +1955,20 @@
   NSRange replacementRange = NSMakeRange(0, 1);
 
   // Make Cocoa view assume there is marked text.
-  [tab_cocoa_view() setMarkedText:text
-                    selectedRange:selectedRange
-                 replacementRange:replacementRange];
-  EXPECT_TRUE([tab_cocoa_view() hasMarkedText]);
+  [tab_GetInProcessNSView() setMarkedText:text
+                            selectedRange:selectedRange
+                         replacementRange:replacementRange];
+  EXPECT_TRUE([tab_GetInProcessNSView() hasMarkedText]);
   child_view_->ImeCancelComposition();
-  EXPECT_FALSE([tab_cocoa_view() hasMarkedText]);
+  EXPECT_FALSE([tab_GetInProcessNSView() hasMarkedText]);
 
   // Repeat for the tab's view.
-  [tab_cocoa_view() setMarkedText:text
-                    selectedRange:selectedRange
-                 replacementRange:replacementRange];
-  EXPECT_TRUE([tab_cocoa_view() hasMarkedText]);
+  [tab_GetInProcessNSView() setMarkedText:text
+                            selectedRange:selectedRange
+                         replacementRange:replacementRange];
+  EXPECT_TRUE([tab_GetInProcessNSView() hasMarkedText]);
   tab_view()->ImeCancelComposition();
-  EXPECT_FALSE([tab_cocoa_view() hasMarkedText]);
+  EXPECT_FALSE([tab_GetInProcessNSView() hasMarkedText]);
 }
 
 // This test verifies that calling FocusedNodeChanged() on
@@ -1977,12 +1980,12 @@
   NSRange selectedRange = NSMakeRange(0, 1);
   NSRange replacementRange = NSMakeRange(0, 1);
 
-  [tab_cocoa_view() setMarkedText:text
-                    selectedRange:selectedRange
-                 replacementRange:replacementRange];
-  EXPECT_TRUE([tab_cocoa_view() hasMarkedText]);
+  [tab_GetInProcessNSView() setMarkedText:text
+                            selectedRange:selectedRange
+                         replacementRange:replacementRange];
+  EXPECT_TRUE([tab_GetInProcessNSView() hasMarkedText]);
   tab_view()->FocusedNodeChanged(true, gfx::Rect());
-  EXPECT_FALSE([tab_cocoa_view() hasMarkedText]);
+  EXPECT_FALSE([tab_GetInProcessNSView() hasMarkedText]);
 }
 
 // This test verifies that when a RenderWidgetHostView changes its
@@ -1994,7 +1997,7 @@
   // method RWHVMac::HasFocus() returns true. Then we can make sure that as long
   // as there is some TextInputState of non-NONE, the corresponding widget will
   // be asked to start monitoring composition info.
-  [window_ makeFirstResponder:tab_cocoa_view()];
+  [window_ makeFirstResponder:tab_GetInProcessNSView()];
   EXPECT_TRUE(tab_view()->HasFocus());
 
   TextInputState state;
@@ -2070,7 +2073,7 @@
   if (@available(macOS 10.12.2, *)) {
     base::scoped_nsobject<FakeSpellChecker> spellChecker(
         [[FakeSpellChecker alloc] init]);
-    tab_cocoa_view().spellCheckerForTesting =
+    tab_GetInProcessNSView().spellCheckerForTesting =
         static_cast<NSSpellChecker*>(spellChecker.get());
 
     SetTextInputType(tab_view(), ui::TEXT_INPUT_TYPE_TEXT);
@@ -2117,8 +2120,8 @@
     ASSERT_EQ("", GetMessageNames(events));
 
     // Now, select that result.
-    [tab_cocoa_view() candidateListTouchBarItem:candidate_list_item()
-                   endSelectingCandidateAtIndex:0];
+    [tab_GetInProcessNSView() candidateListTouchBarItem:candidate_list_item()
+                           endSelectingCandidateAtIndex:0];
     base::RunLoop().RunUntilIdle();
     events = host_->GetAndResetDispatchedMessages();
     ASSERT_EQ("CommitText", GetMessageNames(events));
@@ -2133,7 +2136,7 @@
   if (@available(macOS 10.12.2, *)) {
     base::scoped_nsobject<FakeSpellChecker> spellChecker(
         [[FakeSpellChecker alloc] init]);
-    tab_cocoa_view().spellCheckerForTesting =
+    tab_GetInProcessNSView().spellCheckerForTesting =
         static_cast<NSSpellChecker*>(spellChecker.get());
 
     SetTextInputType(tab_view(), ui::TEXT_INPUT_TYPE_TEXT);
@@ -2262,7 +2265,7 @@
 }
 
 TEST_F(RenderWidgetHostViewMacTest, AccessibilityParentTest) {
-  NSView* view = rwhv_mac_->cocoa_view();
+  NSView* view = rwhv_mac_->GetInProcessNSView();
 
   // NSBox so it participates in the a11y hierarchy.
   base::scoped_nsobject<NSView> parent_view([[NSBox alloc] init]);
@@ -2288,7 +2291,7 @@
 
 // Tests that when entering mouse lock, the cursor will lock to window center.
 TEST_F(RenderWidgetHostViewMacTest, PointerLockCenterPosition) {
-  NSView* view = rwhv_mac_->cocoa_view();
+  NSView* view = rwhv_mac_->GetInProcessNSView();
 
   NSRect bound = NSMakeRect(123, 234, 456, 678);
   [view setFrame:bound];
diff --git a/content/browser/site_per_process_mac_browsertest.mm b/content/browser/site_per_process_mac_browsertest.mm
index 919275be..0398390 100644
--- a/content/browser/site_per_process_mac_browsertest.mm
+++ b/content/browser/site_per_process_mac_browsertest.mm
@@ -255,7 +255,7 @@
     RenderWidgetHostViewBase*& router_touchpad_gesture_target,
     RenderWidgetHostViewBase* expected_target) {
   auto* root_view_mac = static_cast<RenderWidgetHostViewMac*>(root_view);
-  RenderWidgetHostViewCocoa* cocoa_view = root_view_mac->cocoa_view();
+  RenderWidgetHostViewCocoa* cocoa_view = root_view_mac->GetInProcessNSView();
 
   NSEvent* pinchBeginEvent =
       MockGestureEvent(NSEventTypeMagnify, 0, gesture_point.x(),
diff --git a/content/browser/tracing/cast_tracing_agent.cc b/content/browser/tracing/cast_tracing_agent.cc
index 1e48580..eafb905 100644
--- a/content/browser/tracing/cast_tracing_agent.cc
+++ b/content/browser/tracing/cast_tracing_agent.cc
@@ -20,7 +20,7 @@
 #include "services/tracing/public/cpp/perfetto/perfetto_traced_process.h"
 #include "services/tracing/public/mojom/constants.mojom.h"
 #include "services/tracing/public/mojom/perfetto_service.mojom.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/trace_writer.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_writer.h"
 #include "third_party/perfetto/protos/perfetto/trace/chrome/chrome_trace_event.pbzero.h"
 #include "third_party/perfetto/protos/perfetto/trace/trace_packet.pbzero.h"
 
diff --git a/content/browser/tracing/cros_tracing_agent.cc b/content/browser/tracing/cros_tracing_agent.cc
index 60de0db..173c1f7 100644
--- a/content/browser/tracing/cros_tracing_agent.cc
+++ b/content/browser/tracing/cros_tracing_agent.cc
@@ -23,7 +23,7 @@
 #include "services/tracing/public/cpp/perfetto/perfetto_traced_process.h"
 #include "services/tracing/public/mojom/constants.mojom.h"
 #include "services/tracing/public/mojom/perfetto_service.mojom.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/trace_writer.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_writer.h"
 #include "third_party/perfetto/protos/perfetto/trace/chrome/chrome_trace_event.pbzero.h"
 #include "third_party/perfetto/protos/perfetto/trace/trace_packet.pbzero.h"
 
diff --git a/content/browser/tracing/perfetto_file_tracer.cc b/content/browser/tracing/perfetto_file_tracer.cc
index 2f2f3f7..a9744ab4 100644
--- a/content/browser/tracing/perfetto_file_tracer.cc
+++ b/content/browser/tracing/perfetto_file_tracer.cc
@@ -19,7 +19,7 @@
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/tracing/public/cpp/perfetto/perfetto_config.h"
 #include "services/tracing/public/mojom/constants.mojom.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/trace_config.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_config.h"
 #include "third_party/perfetto/protos/perfetto/config/trace_config.pb.h"
 
 namespace content {
diff --git a/content/browser/web_contents/web_contents_view_mac.h b/content/browser/web_contents/web_contents_view_mac.h
index 1b577ea..35da7e5 100644
--- a/content/browser/web_contents/web_contents_view_mac.h
+++ b/content/browser/web_contents/web_contents_view_mac.h
@@ -30,7 +30,6 @@
 
 namespace content {
 class RenderWidgetHostViewMac;
-class WebContentsNSViewBridge;
 class WebContentsImpl;
 class WebContentsViewDelegate;
 class WebContentsViewMac;
@@ -40,6 +39,10 @@
 class Vector2d;
 }
 
+namespace remote_cocoa {
+class WebContentsNSViewBridge;
+}  // remote_cocoa
+
 namespace content {
 
 // Mac-specific implementation of the WebContentsView. It owns an NSView that
@@ -47,7 +50,7 @@
 class WebContentsViewMac : public WebContentsView,
                            public RenderViewHostDelegateView,
                            public PopupMenuHelper::Delegate,
-                           public mojom::WebContentsNSViewClient,
+                           public remote_cocoa::mojom::WebContentsNSViewHost,
                            public ui::ViewsHostableView {
  public:
   // The corresponding WebContentsImpl is passed in the constructor, and manages
@@ -137,19 +140,21 @@
       RenderWidgetHostViewCreateFunction create_render_widget_host_view);
 
  private:
-  WebContentsViewCocoa* cocoa_view() const;
+  WebContentsViewCocoa* GetInProcessNSView() const;
 
-  // mojom::WebContentsNSViewClient:
+  // remote_cocoa::mojom::WebContentsNSViewHost:
   void OnMouseEvent(bool motion, bool exited) override;
-  void OnBecameFirstResponder(mojom::SelectionDirection direction) override;
-  void OnWindowVisibilityChanged(mojom::Visibility visibility) override;
+  void OnBecameFirstResponder(
+      remote_cocoa::mojom::SelectionDirection direction) override;
+  void OnWindowVisibilityChanged(
+      remote_cocoa::mojom::Visibility visibility) override;
   void SetDropData(const DropData& drop_data) override;
-  bool DraggingEntered(mojom::DraggingInfoPtr dragging_info,
+  bool DraggingEntered(remote_cocoa::mojom::DraggingInfoPtr dragging_info,
                        uint32_t* out_result) override;
   void DraggingExited() override;
-  bool DraggingUpdated(mojom::DraggingInfoPtr dragging_info,
+  bool DraggingUpdated(remote_cocoa::mojom::DraggingInfoPtr dragging_info,
                        uint32_t* out_result) override;
-  bool PerformDragOperation(mojom::DraggingInfoPtr dragging_info,
+  bool PerformDragOperation(remote_cocoa::mojom::DraggingInfoPtr dragging_info,
                             bool* out_result) override;
   bool DragPromisedFileTo(const base::FilePath& file_path,
                           const DropData& drop_data,
@@ -159,12 +164,12 @@
                const gfx::PointF& local_point,
                const gfx::PointF& screen_point) override;
 
-  // mojom::WebContentsNSViewClient, synchronous methods:
-  void DraggingEntered(mojom::DraggingInfoPtr dragging_info,
+  // remote_cocoa::mojom::WebContentsNSViewHost, synchronous methods:
+  void DraggingEntered(remote_cocoa::mojom::DraggingInfoPtr dragging_info,
                        DraggingEnteredCallback callback) override;
-  void DraggingUpdated(mojom::DraggingInfoPtr dragging_info,
+  void DraggingUpdated(remote_cocoa::mojom::DraggingInfoPtr dragging_info,
                        DraggingUpdatedCallback callback) override;
-  void PerformDragOperation(mojom::DraggingInfoPtr dragging_info,
+  void PerformDragOperation(remote_cocoa::mojom::DraggingInfoPtr dragging_info,
                             PerformDragOperationCallback callback) override;
   void DragPromisedFileTo(const base::FilePath& file_path,
                           const DropData& drop_data,
@@ -208,12 +213,13 @@
   // The WebContentsViewCocoa that lives in the NSView hierarchy in this
   // process. This is always non-null, even when the view is being displayed
   // in another process.
-  std::unique_ptr<WebContentsNSViewBridge> ns_view_bridge_local_;
+  std::unique_ptr<remote_cocoa::WebContentsNSViewBridge>
+      in_process_ns_view_bridge_;
 
   // Mojo bindings for an out of process instance of this NSView.
-  mojom::WebContentsNSViewBridgeAssociatedPtr ns_view_bridge_remote_;
-  mojo::AssociatedBinding<mojom::WebContentsNSViewClient>
-      ns_view_client_binding_;
+  remote_cocoa::mojom::WebContentsNSViewAssociatedPtr remote_ns_view_;
+  mojo::AssociatedBinding<remote_cocoa::mojom::WebContentsNSViewHost>
+      remote_ns_view_host_binding_;
 
   // Used by CloseTabAfterEventTrackingIfNeeded.
   base::WeakPtrFactory<WebContentsViewMac> deferred_close_weak_ptr_factory_;
diff --git a/content/browser/web_contents/web_contents_view_mac.mm b/content/browser/web_contents/web_contents_view_mac.mm
index c9af9d83..1d1f44d 100644
--- a/content/browser/web_contents/web_contents_view_mac.mm
+++ b/content/browser/web_contents/web_contents_view_mac.mm
@@ -37,6 +37,8 @@
 
 using blink::WebDragOperation;
 using blink::WebDragOperationsMask;
+using remote_cocoa::mojom::DraggingInfoPtr;
+using remote_cocoa::mojom::SelectionDirection;
 
 // Ensure that the blink::WebDragOperation enum values stay in sync with
 // NSDragOperation constants, since the code below static_casts between 'em.
@@ -90,22 +92,23 @@
     : web_contents_(web_contents),
       delegate_(delegate),
       ns_view_id_(remote_cocoa::GetNewNSViewId()),
-      ns_view_client_binding_(this),
+      remote_ns_view_host_binding_(this),
       deferred_close_weak_ptr_factory_(this) {}
 
 WebContentsViewMac::~WebContentsViewMac() {
   if (views_host_)
     views_host_->OnHostableViewDestroying();
   DCHECK(!views_host_);
-  ns_view_bridge_local_.reset();
+  in_process_ns_view_bridge_.reset();
 }
 
-WebContentsViewCocoa* WebContentsViewMac::cocoa_view() const {
-  return ns_view_bridge_local_ ? ns_view_bridge_local_->cocoa_view() : nil;
+WebContentsViewCocoa* WebContentsViewMac::GetInProcessNSView() const {
+  return in_process_ns_view_bridge_ ? in_process_ns_view_bridge_->GetNSView()
+                                    : nil;
 }
 
 gfx::NativeView WebContentsViewMac::GetNativeView() const {
-  return cocoa_view();
+  return GetInProcessNSView();
 }
 
 gfx::NativeView WebContentsViewMac::GetContentNativeView() const {
@@ -116,16 +119,16 @@
 }
 
 gfx::NativeWindow WebContentsViewMac::GetTopLevelNativeWindow() const {
-  NSWindow* window = [cocoa_view() window];
+  NSWindow* window = [GetInProcessNSView() window];
   return window ? window : delegate_->GetNativeWindow();
 }
 
 void WebContentsViewMac::GetContainerBounds(gfx::Rect* out) const {
-  NSWindow* window = [cocoa_view() window];
-  NSRect bounds = [cocoa_view() bounds];
+  NSWindow* window = [GetInProcessNSView() window];
+  NSRect bounds = [GetInProcessNSView() bounds];
   if (window)  {
     // Convert bounds to window coordinate space.
-    bounds = [cocoa_view() convertRect:bounds toView:nil];
+    bounds = [GetInProcessNSView() convertRect:bounds toView:nil];
 
     // Convert bounds to screen coordinate space.
     bounds = [window convertRectToScreen:bounds];
@@ -156,13 +159,12 @@
   [drag_dest_ setDragStartTrackersForProcess:source_rwh->GetProcess()->GetID()];
   drag_source_start_rwh_ = source_rwh->GetWeakPtr();
 
-  if (ns_view_bridge_remote_) {
+  if (remote_ns_view_) {
     // TODO(https://crbug.com/898608): Non-trivial gfx::ImageSkias fail to
     // serialize.
-    ns_view_bridge_remote_->StartDrag(drop_data, mask, gfx::ImageSkia(),
-                                      image_offset);
+    remote_ns_view_->StartDrag(drop_data, mask, gfx::ImageSkia(), image_offset);
   } else {
-    ns_view_bridge_local_->StartDrag(drop_data, mask, image, image_offset);
+    in_process_ns_view_bridge_->StartDrag(drop_data, mask, image, image_offset);
   }
 }
 
@@ -262,12 +264,12 @@
   if (delegate() && delegate()->TakeFocus(reverse))
     return;
   if (reverse) {
-    [[cocoa_view() window] selectPreviousKeyView:cocoa_view()];
+    [[GetInProcessNSView() window] selectPreviousKeyView:GetInProcessNSView()];
   } else {
-    [[cocoa_view() window] selectNextKeyView:cocoa_view()];
+    [[GetInProcessNSView() window] selectNextKeyView:GetInProcessNSView()];
   }
-  if (ns_view_bridge_remote_)
-    ns_view_bridge_remote_->TakeFocus(reverse);
+  if (remote_ns_view_)
+    remote_ns_view_->TakeFocus(reverse);
 }
 
 void WebContentsViewMac::ShowContextMenu(
@@ -305,17 +307,19 @@
 }
 
 gfx::Rect WebContentsViewMac::GetViewBounds() const {
-  NSRect window_bounds = [cocoa_view() convertRect:[cocoa_view() bounds]
-                                            toView:nil];
+  NSRect window_bounds =
+      [GetInProcessNSView() convertRect:[GetInProcessNSView() bounds]
+                                 toView:nil];
   window_bounds.origin = ui::ConvertPointFromWindowToScreen(
-      [cocoa_view() window], window_bounds.origin);
+      [GetInProcessNSView() window], window_bounds.origin);
   return gfx::ScreenRectFromNSRect(window_bounds);
 }
 
 void WebContentsViewMac::CreateView(
     const gfx::Size& initial_size, gfx::NativeView context) {
-  ns_view_bridge_local_ =
-      std::make_unique<WebContentsNSViewBridge>(ns_view_id_, this);
+  in_process_ns_view_bridge_ =
+      std::make_unique<remote_cocoa::WebContentsNSViewBridge>(ns_view_id_,
+                                                              this);
 
   drag_dest_.reset([[WebDragDest alloc] initWithWebContentsImpl:web_contents_]);
   if (delegate_)
@@ -362,18 +366,20 @@
   // to make sure the content area is on the bottom so other things draw over
   // it.
   NSView* view_view = view->GetNativeView().GetNativeNSView();
-  [view_view setFrame:[cocoa_view() bounds]];
+  [view_view setFrame:[GetInProcessNSView() bounds]];
   [view_view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
   // Add the new view below all other views; this also keeps it below any
   // overlay view installed.
-  [cocoa_view() addSubview:view_view positioned:NSWindowBelow relativeTo:nil];
+  [GetInProcessNSView() addSubview:view_view
+                        positioned:NSWindowBelow
+                        relativeTo:nil];
   // For some reason known only to Cocoa, the autorecalculation of the key view
   // loop set on the window doesn't set the next key view when the subview is
   // added. On 10.6 things magically work fine; on 10.5 they fail
   // <http://crbug.com/61493>. Digging into Cocoa key view loop code yielded
   // madness; TODO(avi,rohit): look at this again and figure out what's really
   // going on.
-  [cocoa_view() setNextKeyView:view_view];
+  [GetInProcessNSView() setNextKeyView:view_view];
   return view;
 }
 
@@ -446,7 +452,7 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-// WebContentsViewMac, mojom::WebContentsNSViewClient:
+// WebContentsViewMac, mojom::WebContentsNSViewHost:
 
 void WebContentsViewMac::OnMouseEvent(bool motion, bool exited) {
   if (!web_contents_ || !web_contents_->GetDelegate())
@@ -456,32 +462,31 @@
                                                    exited);
 }
 
-void WebContentsViewMac::OnBecameFirstResponder(
-    mojom::SelectionDirection direction) {
+void WebContentsViewMac::OnBecameFirstResponder(SelectionDirection direction) {
   if (!web_contents_)
     return;
-  if (direction == mojom::SelectionDirection::kDirect)
+  if (direction == SelectionDirection::kDirect)
     return;
 
   web_contents_->FocusThroughTabTraversal(direction ==
-                                          mojom::SelectionDirection::kReverse);
+                                          SelectionDirection::kReverse);
 }
 
 void WebContentsViewMac::OnWindowVisibilityChanged(
-    mojom::Visibility mojo_visibility) {
+    remote_cocoa::mojom::Visibility mojo_visibility) {
   if (!web_contents_ || web_contents_->IsBeingDestroyed())
     return;
 
   // TODO: make content use the mojo type for visibility.
   Visibility visibility = Visibility::VISIBLE;
   switch (mojo_visibility) {
-    case mojom::Visibility::kVisible:
+    case remote_cocoa::mojom::Visibility::kVisible:
       visibility = Visibility::VISIBLE;
       break;
-    case mojom::Visibility::kOccluded:
+    case remote_cocoa::mojom::Visibility::kOccluded:
       visibility = Visibility::OCCLUDED;
       break;
-    case mojom::Visibility::kHidden:
+    case remote_cocoa::mojom::Visibility::kHidden:
       visibility = Visibility::HIDDEN;
       break;
   }
@@ -493,7 +498,7 @@
   [drag_dest_ setDropData:drop_data];
 }
 
-bool WebContentsViewMac::DraggingEntered(mojom::DraggingInfoPtr dragging_info,
+bool WebContentsViewMac::DraggingEntered(DraggingInfoPtr dragging_info,
                                          uint32_t* out_result) {
   *out_result = [drag_dest_ draggingEntered:dragging_info.get()];
   return true;
@@ -503,15 +508,14 @@
   [drag_dest_ draggingExited];
 }
 
-bool WebContentsViewMac::DraggingUpdated(mojom::DraggingInfoPtr dragging_info,
+bool WebContentsViewMac::DraggingUpdated(DraggingInfoPtr dragging_info,
                                          uint32_t* out_result) {
   *out_result = [drag_dest_ draggingUpdated:dragging_info.get()];
   return true;
 }
 
-bool WebContentsViewMac::PerformDragOperation(
-    mojom::DraggingInfoPtr dragging_info,
-    bool* out_result) {
+bool WebContentsViewMac::PerformDragOperation(DraggingInfoPtr dragging_info,
+                                              bool* out_result) {
   *out_result = [drag_dest_ performDragOperation:dragging_info.get()];
   return true;
 }
@@ -585,14 +589,14 @@
       drag_source_start_rwh_.get());
 }
 
-void WebContentsViewMac::DraggingEntered(mojom::DraggingInfoPtr dragging_info,
+void WebContentsViewMac::DraggingEntered(DraggingInfoPtr dragging_info,
                                          DraggingEnteredCallback callback) {
   uint32_t result = 0;
   DraggingEntered(std::move(dragging_info), &result);
   std::move(callback).Run(result);
 }
 
-void WebContentsViewMac::DraggingUpdated(mojom::DraggingInfoPtr dragging_info,
+void WebContentsViewMac::DraggingUpdated(DraggingInfoPtr dragging_info,
                                          DraggingUpdatedCallback callback) {
   uint32_t result = false;
   DraggingUpdated(std::move(dragging_info), &result);
@@ -600,7 +604,7 @@
 }
 
 void WebContentsViewMac::PerformDragOperation(
-    mojom::DraggingInfoPtr dragging_info,
+    DraggingInfoPtr dragging_info,
     PerformDragOperationCallback callback) {
   bool result = false;
   PerformDragOperation(std::move(dragging_info), &result);
@@ -620,41 +624,42 @@
 ////////////////////////////////////////////////////////////////////////////////
 // WebContentsViewMac, ViewsHostableView:
 
-void WebContentsViewMac::ViewsHostableAttach(ViewsHostableView::Host* host) {
-  views_host_ = host;
+void WebContentsViewMac::ViewsHostableAttach(
+    ViewsHostableView::Host* views_host) {
+  views_host_ = views_host;
   // Create an NSView in the target process, if one exists.
   auto* remote_cocoa_application = views_host_->GetRemoteCocoaApplication();
   if (remote_cocoa_application) {
-    mojom::WebContentsNSViewClientAssociatedPtr client;
-    ns_view_client_binding_.Bind(mojo::MakeRequest(&client));
-    mojom::WebContentsNSViewBridgeAssociatedRequest bridge_request =
-        mojo::MakeRequest(&ns_view_bridge_remote_);
+    remote_cocoa::mojom::WebContentsNSViewHostAssociatedPtr host;
+    remote_ns_view_host_binding_.Bind(mojo::MakeRequest(&host));
+    remote_cocoa::mojom::WebContentsNSViewAssociatedRequest ns_view_request =
+        mojo::MakeRequest(&remote_ns_view_);
 
-    // Cast from mojom::WebContentsNSViewClientPtr and
+    // Cast from mojom::WebContentsNSViewHostPtr and
     // mojom::WebContentsNSViewBridgeRequest to the public interfaces
     // accepted by the application.
     // TODO(ccameron): Remove the need for this cast.
     // https://crbug.com/888290
     mojo::AssociatedInterfacePtrInfo<remote_cocoa::mojom::StubInterface>
-        stub_client(client.PassInterface().PassHandle(), 0);
-    remote_cocoa::mojom::StubInterfaceAssociatedRequest stub_bridge_request(
-        bridge_request.PassHandle());
+        stub_host(host.PassInterface().PassHandle(), 0);
+    remote_cocoa::mojom::StubInterfaceAssociatedRequest stub_ns_view_request(
+        ns_view_request.PassHandle());
 
     remote_cocoa_application->CreateWebContentsNSView(
-        ns_view_id_, std::move(stub_client), std::move(stub_bridge_request));
-    ns_view_bridge_remote_->SetParentNSView(views_host_->GetNSViewId());
+        ns_view_id_, std::move(stub_host), std::move(stub_ns_view_request));
+    remote_ns_view_->SetParentNSView(views_host_->GetNSViewId());
 
     // Because this view is being displayed from a remote process, reset the
     // in-process NSView's client pointer, so that the in-process NSView will
     // not call back into |this|.
-    [cocoa_view() setClient:nullptr];
+    [GetInProcessNSView() setHost:nullptr];
   }
 
   // TODO(https://crbug.com/933679): WebContentsNSViewBridge::SetParentView
   // will look up the parent NSView by its id, but this has been observed to
   // fail in the field, so assume that the caller handles updating the NSView
   // hierarchy.
-  // ns_view_bridge_local_->SetParentNSView(views_host_->GetNSViewId());
+  // in_process_ns_view_bridge_->SetParentNSView(views_host_->GetNSViewId());
 
   for (auto* rwhv_mac : GetChildViews()) {
     rwhv_mac->MigrateNSViewBridge(remote_cocoa_application, ns_view_id_);
@@ -666,16 +671,16 @@
   DCHECK(views_host_);
   // Disconnect from the remote bridge, if it exists. This will have the effect
   // of destroying the associated bridge instance with its NSView.
-  if (ns_view_bridge_remote_) {
-    ns_view_bridge_remote_->SetVisible(false);
-    ns_view_bridge_remote_->ResetParentNSView();
-    ns_view_client_binding_.Close();
-    ns_view_bridge_remote_.reset();
+  if (remote_ns_view_) {
+    remote_ns_view_->SetVisible(false);
+    remote_ns_view_->ResetParentNSView();
+    remote_ns_view_host_binding_.Close();
+    remote_ns_view_.reset();
     // Permit the in-process NSView to call back into |this| again.
-    [cocoa_view() setClient:this];
+    [GetInProcessNSView() setHost:this];
   }
-  ns_view_bridge_local_->SetVisible(false);
-  ns_view_bridge_local_->ResetParentNSView();
+  in_process_ns_view_bridge_->SetVisible(false);
+  in_process_ns_view_bridge_->ResetParentNSView();
   views_host_ = nullptr;
 
   for (auto* rwhv_mac : GetChildViews()) {
@@ -688,24 +693,24 @@
 void WebContentsViewMac::ViewsHostableSetBounds(
     const gfx::Rect& bounds_in_window) {
   // Update both the in-process and out-of-process NSViews' bounds.
-  ns_view_bridge_local_->SetBounds(bounds_in_window);
-  if (ns_view_bridge_remote_)
-    ns_view_bridge_remote_->SetBounds(bounds_in_window);
+  in_process_ns_view_bridge_->SetBounds(bounds_in_window);
+  if (remote_ns_view_)
+    remote_ns_view_->SetBounds(bounds_in_window);
 }
 
 void WebContentsViewMac::ViewsHostableSetVisible(bool visible) {
   // Update both the in-process and out-of-process NSViews' visibility.
-  ns_view_bridge_local_->SetVisible(visible);
-  if (ns_view_bridge_remote_)
-    ns_view_bridge_remote_->SetVisible(visible);
+  in_process_ns_view_bridge_->SetVisible(visible);
+  if (remote_ns_view_)
+    remote_ns_view_->SetVisible(visible);
 }
 
 void WebContentsViewMac::ViewsHostableMakeFirstResponder() {
   // Only make the true NSView become the first responder.
-  if (ns_view_bridge_remote_)
-    ns_view_bridge_remote_->MakeFirstResponder();
+  if (remote_ns_view_)
+    remote_ns_view_->MakeFirstResponder();
   else
-    ns_view_bridge_local_->MakeFirstResponder();
+    in_process_ns_view_bridge_->MakeFirstResponder();
 }
 
 void WebContentsViewMac::ViewsHostableSetParentAccessible(
diff --git a/content/browser/web_contents/web_contents_view_mac_unittest.mm b/content/browser/web_contents/web_contents_view_mac_unittest.mm
index 0124707..08226ed6 100644
--- a/content/browser/web_contents/web_contents_view_mac_unittest.mm
+++ b/content/browser/web_contents/web_contents_view_mac_unittest.mm
@@ -19,12 +19,11 @@
 
 namespace {
 
-class WebContentsViewCocoaTest : public ui::CocoaTest {
-};
+class WebContentsNSViewTest : public ui::CocoaTest {};
 
 }  // namespace
 
-TEST_F(WebContentsViewCocoaTest, NonWebDragSourceTest) {
+TEST_F(WebContentsNSViewTest, NonWebDragSourceTest) {
   // The designated initializer is private but init should be fine in this case.
   base::scoped_nsobject<WebContentsViewCocoa> view(
       [[WebContentsViewCocoa alloc] init]);
diff --git a/content/browser/web_contents/web_drag_dest_mac.h b/content/browser/web_contents/web_drag_dest_mac.h
index 6e6ed54..28bdcc4 100644
--- a/content/browser/web_contents/web_drag_dest_mac.h
+++ b/content/browser/web_contents/web_drag_dest_mac.h
@@ -20,14 +20,17 @@
 class RenderWidgetHostImpl;
 class WebContentsImpl;
 class WebDragDestDelegate;
-namespace mojom {
-class DraggingInfo;
-}  // namespace mojom
 }  // namespace content
 
 // A typedef for a RenderViewHost used for comparison purposes only.
 typedef content::RenderViewHost* RenderViewHostIdentifier;
 
+namespace remote_cocoa {
+namespace mojom {
+class DraggingInfo;
+}  // namespace mojom
+}  // namespace remote_cocoa
+
 namespace content {
 
 // Given |data|, which should not be nil, fill it in using the contents of the
@@ -98,10 +101,12 @@
 // Messages to send during the tracking of a drag, ususally upon receiving
 // calls from the view system. Communicates the drag messages to WebCore.
 - (void)setDropData:(const content::DropData&)dropData;
-- (NSDragOperation)draggingEntered:(const content::mojom::DraggingInfo*)info;
+- (NSDragOperation)draggingEntered:
+    (const remote_cocoa::mojom::DraggingInfo*)info;
 - (void)draggingExited;
-- (NSDragOperation)draggingUpdated:(const content::mojom::DraggingInfo*)info;
-- (BOOL)performDragOperation:(const content::mojom::DraggingInfo*)info;
+- (NSDragOperation)draggingUpdated:
+    (const remote_cocoa::mojom::DraggingInfo*)info;
+- (BOOL)performDragOperation:(const remote_cocoa::mojom::DraggingInfo*)info;
 
 // Helper to call WebWidgetHostInputEventRouter::GetRenderWidgetHostAtPoint().
 - (content::RenderWidgetHostImpl*)
diff --git a/content/browser/web_contents/web_drag_dest_mac.mm b/content/browser/web_contents/web_drag_dest_mac.mm
index f425000..c4638ac6e 100644
--- a/content/browser/web_contents/web_drag_dest_mac.mm
+++ b/content/browser/web_contents/web_drag_dest_mac.mm
@@ -28,11 +28,11 @@
 #include "ui/gfx/geometry/point.h"
 
 using blink::WebDragOperationsMask;
-using content::mojom::DraggingInfo;
 using content::DropData;
 using content::OpenURLParams;
 using content::Referrer;
 using content::WebContentsImpl;
+using remote_cocoa::mojom::DraggingInfo;
 
 namespace {
 
diff --git a/content/browser/web_contents/web_drag_source_mac_unittest.mm b/content/browser/web_contents/web_drag_source_mac_unittest.mm
index 962a7343..10f6ddc 100644
--- a/content/browser/web_contents/web_drag_source_mac_unittest.mm
+++ b/content/browser/web_contents/web_drag_source_mac_unittest.mm
@@ -26,7 +26,7 @@
 
   scoped_refptr<ui::UniquePasteboard> pasteboard1 = new ui::UniquePasteboard;
   base::scoped_nsobject<WebDragSource> source([[WebDragSource alloc]
-         initWithClient:nullptr
+           initWithHost:nullptr
                    view:view
                dropData:dropData.get()
                   image:nil
diff --git a/content/common/render_widget_host_ns_view.mojom b/content/common/render_widget_host_ns_view.mojom
index 48c46bd..5659d670 100644
--- a/content/common/render_widget_host_ns_view.mojom
+++ b/content/common/render_widget_host_ns_view.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.
 
-module content.mojom;
+module remote_cocoa.mojom;
 
 import "content/common/native_types.mojom";
 import "content/common/input/input_handler.mojom";
@@ -19,7 +19,7 @@
 // RenderWidgetHostViewMac, sends messages to the app shim process, targeting
 // the RenderWidgetHostViewCocoa NSView. No synchronous communication is allowed
 // in this direction.
-interface RenderWidgetHostNSViewBridge {
+interface RenderWidgetHostNSView {
   // Specify that the NSView will a popup (e.g, date/time picker) that will
   // create its own NSWindow.
   InitAsPopup(gfx.mojom.Rect content_rect);
@@ -69,7 +69,7 @@
   SetShowingContextMenu(bool showing);
 
   // Set the cursor type to display.
-  DisplayCursor(WebCursor cursor);
+  DisplayCursor(content.mojom.WebCursor cursor);
 
   // Lock or unlock the cursor.
   SetCursorLocked(bool locked);
@@ -80,7 +80,7 @@
 
   // Open the dictionary overlay for the specified string at the specified
   // point.
-  ShowDictionaryOverlay(EncodedAttributedString attributed_string,
+  ShowDictionaryOverlay(content.mojom.EncodedAttributedString attributed_string,
                         gfx.mojom.Point baseline_point);
 
   // Start intercepting keyboard events for the specified codes.
@@ -94,11 +94,11 @@
 // shim process communicates to the RenderWidgetHostViewMac in the browser
 // process. Synchronous calls are allowed to be made through this interface.
 // TODO(ccameron): This corresponds almost one-to-one with the
-// content::RenderWidgetHostNSViewClient interface. It may be possible to merge
+// content::RenderWidgetHostNSViewHost interface. It may be possible to merge
 // these two interfaces, though that may come at the cost of extra work (e.g,
 // de-serializing and re-serializing all events).
 // https://crbug.com/821651
-interface RenderWidgetHostNSViewClient {
+interface RenderWidgetHostNSViewHost {
   // Synchronously query if the RenderWidgetHostView is for a main frame, and
   // return the result as |is_for_main_frame|.
   [Sync]
@@ -137,29 +137,29 @@
 
   // Forward a keyboard event to the RenderWidgetHost that is currently handling
   // the key-down event.
-  ForwardKeyboardEvent(Event event, bool skip_in_browser);
+  ForwardKeyboardEvent(content.mojom.Event event, bool skip_in_browser);
   ForwardKeyboardEventWithCommands(
-      Event event,
+      content.mojom.Event event,
       bool skip_in_browser,
       array<content.mojom.EditCommand> commands);
 
   // Forward events to the renderer or the input router, as appropriate.
-  RouteOrProcessMouseEvent(Event event);
-  RouteOrProcessTouchEvent(Event event);
-  RouteOrProcessWheelEvent(Event event);
+  RouteOrProcessMouseEvent(content.mojom.Event event);
+  RouteOrProcessTouchEvent(content.mojom.Event event);
+  RouteOrProcessWheelEvent(content.mojom.Event event);
 
   // Special case forwarding of synthetic events to the renderer.
-  ForwardMouseEvent(Event event);
-  ForwardWheelEvent(Event event);
+  ForwardMouseEvent(content.mojom.Event event);
+  ForwardWheelEvent(content.mojom.Event event);
 
   // Handling pinch gesture events. Note that for GestureBegin, the type of the
   // event is ignored, and is inferred from subsequent GestureUpdate calls.
-  GestureBegin(Event event, bool is_synthetically_injected);
-  GestureUpdate(Event event);
-  GestureEnd(Event event);
+  GestureBegin(content.mojom.Event event, bool is_synthetically_injected);
+  GestureUpdate(content.mojom.Event event);
+  GestureEnd(content.mojom.Event event);
 
   // Handle a double-tap magnify event.
-  SmartMagnify(Event event);
+  SmartMagnify(content.mojom.Event event);
 
   // Forward the corresponding Ime commands to the appropriate RenderWidgetHost.
   // Appropriate, has two meanings here. If this is during a key-down event,
diff --git a/content/common/web_contents_ns_view_bridge.mojom b/content/common/web_contents_ns_view_bridge.mojom
index d4190a8a..3ab01e0 100644
--- a/content/common/web_contents_ns_view_bridge.mojom
+++ b/content/common/web_contents_ns_view_bridge.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.
 
-module content.mojom;
+module remote_cocoa.mojom;
 
 import "content/public/common/drop_data.mojom";
 import "mojo/public/mojom/base/file_path.mojom";
@@ -12,7 +12,7 @@
 
 // Interface through which a WebContentsViewMac communicates with its NSView in
 // another process.
-interface WebContentsNSViewBridge {
+interface WebContentsNSView {
   // Set this to be a child NSView of the NSView mapped to by
   // |parent_ns_view_id|.
   SetParentNSView(uint64 parent_ns_view_id);
@@ -35,7 +35,7 @@
   TakeFocus(bool reverse);
 
   // Initiate a drag from the web contents area.
-  StartDrag(DropData drop_data,
+  StartDrag(content.mojom.DropData drop_data,
             uint32 operation_mask,
             gfx.mojom.ImageSkia? image,
             gfx.mojom.Vector2d image_offset);
@@ -83,7 +83,7 @@
 // Interface through which the NSView in another process communicates with its
 // owning WebContentsViewMac. This interface has no methods yet, but is included
 // for symmetry and future use.
-interface WebContentsNSViewClient {
+interface WebContentsNSViewHost {
   // Notification that there was a mouse event, along with the type of event.
   // If |motion| is true, this is a normal motion event. If |exited| is true,
   // the pointer left the contents area.
@@ -99,7 +99,7 @@
 
   // Transmit the data that is being dropped on the NSView. This is called prior
   // to DraggingEntered.
-  SetDropData(DropData drop_data);
+  SetDropData(content.mojom.DropData drop_data);
 
   // Called in response to the -[NSDraggingDestination draggingEntered] method
   // being called on the NSView. Returns the resulting operation in |result|.
@@ -126,7 +126,7 @@
   // destination file.
   [Sync]
   DragPromisedFileTo(mojo_base.mojom.FilePath file_path,
-                     DropData drop_data,
+                     content.mojom.DropData drop_data,
                      url.mojom.Url download_url) =>
                          (mojo_base.mojom.FilePath file_path);
 
diff --git a/content/public/browser/remote_cocoa.h b/content/public/browser/remote_cocoa.h
index 5506c5e..e514e0b 100644
--- a/content/public/browser/remote_cocoa.h
+++ b/content/public/browser/remote_cocoa.h
@@ -8,7 +8,7 @@
 #include "content/common/content_export.h"
 #include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
 
-namespace content {
+namespace remote_cocoa {
 
 // Create the NSView for a RenderWidgetHostView or WebContentsView. This is
 // called in the app shim process through an interface in remote_cocoa. These
@@ -24,6 +24,6 @@
     mojo::ScopedInterfaceEndpointHandle host_handle,
     mojo::ScopedInterfaceEndpointHandle view_request_handle);
 
-}  // namespace content
+}  // namespace remote_cocoa
 
 #endif  // CONTENT_PUBLIC_BROWSER_REMOTE_COCOA_H_
diff --git a/content/public/test/content_browser_test_utils_mac.mm b/content/public/test/content_browser_test_utils_mac.mm
index 156c8299..57fec6a 100644
--- a/content/public/test/content_browser_test_utils_mac.mm
+++ b/content/public/test/content_browser_test_utils_mac.mm
@@ -56,7 +56,7 @@
     if (!contents->GetBrowserPluginGuest()) {
       RenderWidgetHostViewMac* rwhv_mac = static_cast<RenderWidgetHostViewMac*>(
           contents->GetRenderWidgetHostView());
-      if (rwhv_mac->cocoa_view() == object)
+      if (rwhv_mac->GetInProcessNSView() == object)
         return rwhv_mac;
     }
   }
@@ -179,10 +179,10 @@
     return;
 
   NSRect bounds_in_cocoa_view =
-      [view convertRect:view.bounds toView:rwhv_mac->cocoa_view()];
+      [view convertRect:view.bounds toView:rwhv_mac->GetInProcessNSView()];
 
   gfx::Rect rect =
-      [rwhv_mac->cocoa_view() flipNSRectToRect:bounds_in_cocoa_view];
+      [rwhv_mac->GetInProcessNSView() flipNSRectToRect:bounds_in_cocoa_view];
 
   observer->DidAddSubviewWillBeDismissed(rect);
 
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
index e8b4342..95cabb2 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
@@ -110,6 +110,7 @@
 crbug.com/680754 [ d3d11 win nvidia-0x1cb3 ] deqp/functional/gles3/transformfeedback/random_separate_triangles.html [ Failure ]
 crbug.com/680754 [ d3d11 win nvidia-0x1cb3 ] deqp/functional/gles3/transformfeedback/interpolation_flat.html [ Failure ]
 crbug.com/966143 [ d3d11 win7 nvidia-0x1cb3 ] conformance/attribs/gl-vertex-attrib-unconsumed-out-of-bounds.html [ RetryOnFailure ]
+crbug.com/966143 [ d3d11 win7 nvidia-0x1cb3 ] conformance/attribs/gl-vertex-attrib-zero-issues.html [ RetryOnFailure ]
 crbug.com/728670 [ d3d11 win nvidia-0x1cb3 ] conformance/extensions/oes-texture-half-float-with-video.html [ RetryOnFailure ]
 crbug.com/728670 [ d3d11 win nvidia-0x1cb3 ] conformance/textures/image_bitmap_from_video/tex-2d-rgba-rgba-unsigned_short_5_5_5_1.html [ RetryOnFailure ]
 crbug.com/728670 [ d3d11 win nvidia-0x1cb3 ] conformance/textures/image_bitmap_from_video/tex-2d-rgba-rgba-unsigned_short_4_4_4_4.html [ RetryOnFailure ]
@@ -753,6 +754,7 @@
 crbug.com/906739 [ android qualcomm ] conformance2/extensions/ovr_multiview2.html [ Failure ]
 crbug.com/906742 [ android qualcomm ] conformance2/glsl3/compare-structs-containing-arrays.html [ Failure ]
 crbug.com/906735 [ android qualcomm ] conformance2/textures/video/tex-2d-rgba8ui-rgba_integer-unsigned_byte.html [ Failure ]
+crbug.com/949321 [ android qualcomm ] deqp/functional/gles3/framebufferblit/default_framebuffer_02.html [ RetryOnFailure ]
 
 # This test is failing on Android Pixel 2 and 3 (Qualcomm)
 # Seems to be an OpenGL ES bug.
diff --git a/device/bluetooth/bluetooth_adapter_winrt.cc b/device/bluetooth/bluetooth_adapter_winrt.cc
index d3b8d7829..48bd0eda 100644
--- a/device/bluetooth/bluetooth_adapter_winrt.cc
+++ b/device/bluetooth/bluetooth_adapter_winrt.cc
@@ -21,6 +21,7 @@
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/scoped_refptr.h"
+#include "base/scoped_native_library.h"
 #include "base/single_thread_task_runner.h"
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
@@ -467,13 +468,6 @@
                                   ExtractManufacturerData(advertisement.Get()));
 }
 
-decltype(&::RoGetAgileReference) LoadGetAgileReference() {
-  auto funcptr = reinterpret_cast<decltype(&::RoGetAgileReference)>(
-      ::GetProcAddress(::GetModuleHandle(L"Ole32.dll"), "RoGetAgileReference"));
-  CHECK(funcptr);
-  return funcptr;
-}
-
 }  // namespace
 
 std::string BluetoothAdapterWinrt::GetAddress() const {
@@ -686,27 +680,10 @@
   if (!radio_statics)
     statics.radio_statics->Resolve(IID_IRadioStatics, &radio_statics);
 
-  auto getAgileReferenceFunc = LoadGetAgileReference();
-
-  ComPtr<IAgileReference> radio_statics_agileref;
-  HRESULT hr =
-      getAgileReferenceFunc(AGILEREFERENCE_DEFAULT, IID_IRadioStatics,
-                            radio_statics.Get(), &radio_statics_agileref);
-  DCHECK(SUCCEEDED(hr));
-  ComPtr<IAgileReference> device_information_statics_agileref;
-  hr = getAgileReferenceFunc(
-      AGILEREFERENCE_DEFAULT, IID_IDeviceInformationStatics,
-      device_information_statics.Get(), &device_information_statics_agileref);
-  DCHECK(SUCCEEDED(hr));
-  ComPtr<IAgileReference> adapter_statics_agileref;
-  hr = getAgileReferenceFunc(
-      AGILEREFERENCE_DEFAULT, IID_IBluetoothAdapterStatics,
-      bluetooth_adapter_statics.Get(), &adapter_statics_agileref);
-  DCHECK(SUCCEEDED(hr));
-  CompleteInitAgile(std::move(init_cb),
-                    StaticsInterfaces(adapter_statics_agileref,
-                                      device_information_statics_agileref,
-                                      radio_statics_agileref));
+  StaticsInterfaces agile_statics = GetAgileReferencesForStatics(
+      std::move(bluetooth_adapter_statics),
+      std::move(device_information_statics), std::move(radio_statics));
+  CompleteInitAgile(std::move(init_cb), std::move(agile_statics));
 }
 
 // static
@@ -746,34 +723,51 @@
     return BluetoothAdapterWinrt::StaticsInterfaces();
   }
 
-  auto getAgileReferenceFunc = LoadGetAgileReference();
+  return GetAgileReferencesForStatics(std::move(adapter_statics),
+                                      std::move(device_information_statics),
+                                      std::move(radio_statics));
+}
 
-  ComPtr<IAgileReference> radio_statics_agileref;
-  hr = getAgileReferenceFunc(AGILEREFERENCE_DEFAULT,
-                             ABI::Windows::Devices::Radios::IID_IRadioStatics,
-                             radio_statics.Get(), &radio_statics_agileref);
-  if (FAILED(hr))
-    return BluetoothAdapterWinrt::StaticsInterfaces();
+// static
+BluetoothAdapterWinrt::StaticsInterfaces
+BluetoothAdapterWinrt::GetAgileReferencesForStatics(
+    ComPtr<IBluetoothAdapterStatics> adapter_statics,
+    ComPtr<IDeviceInformationStatics> device_information_statics,
+    ComPtr<IRadioStatics> radio_statics) {
+  base::ScopedNativeLibrary ole32_library(base::FilePath(L"Ole32.dll"));
+  CHECK(ole32_library.is_valid());
 
-  ComPtr<IAgileReference> device_information_statics_agileref;
-  hr = getAgileReferenceFunc(
-      AGILEREFERENCE_DEFAULT,
-      ABI::Windows::Devices::Enumeration::IID_IDeviceInformationStatics,
-      device_information_statics.Get(), &device_information_statics_agileref);
-  if (FAILED(hr))
-    return BluetoothAdapterWinrt::StaticsInterfaces();
+  auto ro_get_agile_reference =
+      reinterpret_cast<decltype(&::RoGetAgileReference)>(
+          ole32_library.GetFunctionPointer("RoGetAgileReference"));
+  CHECK(ro_get_agile_reference);
 
   ComPtr<IAgileReference> adapter_statics_agileref;
-  hr = getAgileReferenceFunc(
+  HRESULT hr = ro_get_agile_reference(
       AGILEREFERENCE_DEFAULT,
       ABI::Windows::Devices::Bluetooth::IID_IBluetoothAdapterStatics,
       adapter_statics.Get(), &adapter_statics_agileref);
   if (FAILED(hr))
-    return BluetoothAdapterWinrt::StaticsInterfaces();
+    return StaticsInterfaces();
 
-  return BluetoothAdapterWinrt::StaticsInterfaces(
-      adapter_statics_agileref, device_information_statics_agileref,
-      radio_statics_agileref);
+  ComPtr<IAgileReference> device_information_statics_agileref;
+  hr = ro_get_agile_reference(
+      AGILEREFERENCE_DEFAULT,
+      ABI::Windows::Devices::Enumeration::IID_IDeviceInformationStatics,
+      device_information_statics.Get(), &device_information_statics_agileref);
+  if (FAILED(hr))
+    return StaticsInterfaces();
+
+  ComPtr<IAgileReference> radio_statics_agileref;
+  hr = ro_get_agile_reference(AGILEREFERENCE_DEFAULT,
+                              ABI::Windows::Devices::Radios::IID_IRadioStatics,
+                              radio_statics.Get(), &radio_statics_agileref);
+  if (FAILED(hr))
+    return StaticsInterfaces();
+
+  return StaticsInterfaces(std::move(adapter_statics_agileref),
+                           std::move(device_information_statics_agileref),
+                           std::move(radio_statics_agileref));
 }
 
 void BluetoothAdapterWinrt::CompleteInitAgile(InitCallback init_cb,
diff --git a/device/bluetooth/bluetooth_adapter_winrt.h b/device/bluetooth/bluetooth_adapter_winrt.h
index 0ad217c..87dcade 100644
--- a/device/bluetooth/bluetooth_adapter_winrt.h
+++ b/device/bluetooth/bluetooth_adapter_winrt.h
@@ -136,6 +136,15 @@
   };
 
   static StaticsInterfaces PerformSlowInitTasks();
+  static StaticsInterfaces GetAgileReferencesForStatics(
+      Microsoft::WRL::ComPtr<
+          ABI::Windows::Devices::Bluetooth::IBluetoothAdapterStatics>
+          adapter_statics,
+      Microsoft::WRL::ComPtr<
+          ABI::Windows::Devices::Enumeration::IDeviceInformationStatics>
+          device_information_statics,
+      Microsoft::WRL::ComPtr<ABI::Windows::Devices::Radios::IRadioStatics>
+          radio_statics);
 
   // CompleteInitAgile is a proxy to CompleteInit that resolves agile
   // references.
diff --git a/headless/app/headless_shell.cc b/headless/app/headless_shell.cc
index b066add..6bfa269 100644
--- a/headless/app/headless_shell.cc
+++ b/headless/app/headless_shell.cc
@@ -70,7 +70,8 @@
 
 bool ParseWindowSize(const std::string& window_size,
                      gfx::Size* parsed_window_size) {
-  int width, height = 0;
+  int width = 0;
+  int height = 0;
   if (sscanf(window_size.c_str(), "%d%*[x,]%d", &width, &height) >= 2 &&
       width >= 0 && height >= 0) {
     parsed_window_size->set_width(width);
@@ -140,7 +141,7 @@
 #endif
 }
 
-#endif
+#endif  // !defined(CHROME_MULTIPLE_DLL_CHILD)
 
 int RunContentMain(
     HeadlessBrowser::Options options,
@@ -159,17 +160,63 @@
   // TODO(skyostil): Implement custom message pumps.
   DCHECK(!options.message_pump);
 
-#if !defined(CHROME_MULTIPLE_DLL_CHILD)
-  std::unique_ptr<HeadlessBrowserImpl> browser(new HeadlessBrowserImpl(
-      std::move(on_browser_start_callback), std::move(options)));
-  HeadlessContentMainDelegate delegate(std::move(browser));
-#else
+#if defined(CHROME_MULTIPLE_DLL_CHILD)
   HeadlessContentMainDelegate delegate(std::move(options));
+#else
+  auto browser = std::make_unique<HeadlessBrowserImpl>(
+      std::move(on_browser_start_callback), std::move(options));
+  HeadlessContentMainDelegate delegate(std::move(browser));
 #endif
   params.delegate = &delegate;
   return content::ContentMain(params);
 }
 
+bool ValidateCommandLine(const base::CommandLine& command_line) {
+  if (!command_line.HasSwitch(switches::kRemoteDebuggingPort) &&
+      !command_line.HasSwitch(switches::kRemoteDebuggingPipe)) {
+    if (command_line.GetArgs().size() <= 1)
+      return true;
+    LOG(ERROR) << "Open multiple tabs is only supported when "
+               << "remote debugging is enabled.";
+    return false;
+  }
+  if (command_line.HasSwitch(switches::kDefaultBackgroundColor)) {
+    LOG(ERROR) << "Setting default background color is disabled "
+               << "when remote debugging is enabled.";
+    return false;
+  }
+  if (command_line.HasSwitch(switches::kDumpDom)) {
+    LOG(ERROR) << "Dump DOM is disabled when remote debugging is enabled.";
+    return false;
+  }
+  if (command_line.HasSwitch(switches::kPrintToPDF)) {
+    LOG(ERROR) << "Print to PDF is disabled "
+               << "when remote debugging is enabled.";
+    return false;
+  }
+  if (command_line.HasSwitch(switches::kRepl)) {
+    LOG(ERROR) << "Evaluate Javascript is disabled "
+               << "when remote debugging is enabled.";
+    return false;
+  }
+  if (command_line.HasSwitch(switches::kScreenshot)) {
+    LOG(ERROR) << "Capture screenshot is disabled "
+               << "when remote debugging is enabled.";
+    return false;
+  }
+  if (command_line.HasSwitch(switches::kTimeout)) {
+    LOG(ERROR) << "Navigation timeout is disabled "
+               << "when remote debugging is enabled.";
+    return false;
+  }
+  if (command_line.HasSwitch(switches::kVirtualTimeBudget)) {
+    LOG(ERROR) << "Virtual time budget is disabled "
+               << "when remote debugging is enabled.";
+    return false;
+  }
+  return true;
+}
+
 }  // namespace
 
 HeadlessShell::HeadlessShell() : weak_factory_(this) {}
@@ -589,52 +636,6 @@
           command_line.HasSwitch(switches::kRemoteDebuggingPipe));
 }
 
-bool ValidateCommandLine(const base::CommandLine& command_line) {
-  if (!command_line.HasSwitch(switches::kRemoteDebuggingPort) &&
-      !command_line.HasSwitch(switches::kRemoteDebuggingPipe)) {
-    if (command_line.GetArgs().size() <= 1)
-      return true;
-    LOG(ERROR) << "Open multiple tabs is only supported when "
-               << "remote debugging is enabled.";
-    return false;
-  }
-  if (command_line.HasSwitch(switches::kDefaultBackgroundColor)) {
-    LOG(ERROR) << "Setting default background color is disabled "
-               << "when remote debugging is enabled.";
-    return false;
-  }
-  if (command_line.HasSwitch(switches::kDumpDom)) {
-    LOG(ERROR) << "Dump DOM is disabled when remote debugging is enabled.";
-    return false;
-  }
-  if (command_line.HasSwitch(switches::kPrintToPDF)) {
-    LOG(ERROR) << "Print to PDF is disabled "
-               << "when remote debugging is enabled.";
-    return false;
-  }
-  if (command_line.HasSwitch(switches::kRepl)) {
-    LOG(ERROR) << "Evaluate Javascript is disabled "
-               << "when remote debugging is enabled.";
-    return false;
-  }
-  if (command_line.HasSwitch(switches::kScreenshot)) {
-    LOG(ERROR) << "Capture screenshot is disabled "
-               << "when remote debugging is enabled.";
-    return false;
-  }
-  if (command_line.HasSwitch(switches::kTimeout)) {
-    LOG(ERROR) << "Navigation timeout is disabled "
-               << "when remote debugging is enabled.";
-    return false;
-  }
-  if (command_line.HasSwitch(switches::kVirtualTimeBudget)) {
-    LOG(ERROR) << "Virtual time budget is disabled "
-               << "when remote debugging is enabled.";
-    return false;
-  }
-  return true;
-}
-
 #if defined(OS_WIN)
 int HeadlessShellMain(HINSTANCE instance,
                       sandbox::SandboxInterfaceInfo* sandbox_info) {
diff --git a/ios/chrome/browser/signin/feature_flags.mm b/ios/chrome/browser/signin/feature_flags.mm
index b7e2e5e..21f76c8 100644
--- a/ios/chrome/browser/signin/feature_flags.mm
+++ b/ios/chrome/browser/signin/feature_flags.mm
@@ -16,7 +16,7 @@
     "UseNSURLSessionForGaiaSigninRequests", base::FEATURE_DISABLED_BY_DEFAULT};
 
 const base::Feature kIdentityDisc{"IdentityDisc",
-                                  base::FEATURE_DISABLED_BY_DEFAULT};
+                                  base::FEATURE_ENABLED_BY_DEFAULT};
 
 bool IsIdentityDiscFeatureEnabled() {
   // Checks feature flag and any dependencies. Display of Identity Disc depends
diff --git a/ios/testing/earl_grey/BUILD.gn b/ios/testing/earl_grey/BUILD.gn
index 7a5367b..22cf840 100644
--- a/ios/testing/earl_grey/BUILD.gn
+++ b/ios/testing/earl_grey/BUILD.gn
@@ -15,6 +15,8 @@
   ]
 
   sources = [
+    "app_launch_manager.h",
+    "app_launch_manager.mm",
     "base_earl_grey_test_case.h",
     "base_earl_grey_test_case.mm",
     "base_eg_test_helper_impl.h",
@@ -59,6 +61,8 @@
   testonly = true
 
   sources = [
+    "app_launch_manager.h",
+    "app_launch_manager.mm",
     "base_earl_grey_test_case.h",
     "base_earl_grey_test_case.mm",
     "base_eg_test_helper_impl.h",
diff --git a/ios/testing/earl_grey/app_launch_manager.h b/ios/testing/earl_grey/app_launch_manager.h
new file mode 100644
index 0000000..b03205e8
--- /dev/null
+++ b/ios/testing/earl_grey/app_launch_manager.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 IOS_TESTING_EARL_GREY_APP_LAUNCH_MANAGER_H_
+#define IOS_TESTING_EARL_GREY_APP_LAUNCH_MANAGER_H_
+
+#import <Foundation/Foundation.h>
+
+// Provides control of the single application-under-test to EarlGrey 2 tests.
+@interface AppLaunchManager : NSObject
+
+// Returns the singleton instance of this class.
++ (AppLaunchManager*)sharedManager;
+
+- (instancetype)init NS_UNAVAILABLE;
+
+// Makes sure the app has been started with the appropriate |arguments|.
+// In EG2, will launch the app if any of the following conditions are met:
+// * The app is not running
+// * The app is currently running with different arguments.
+// * |forceRestart| is YES
+// Otherwise, the app will be activated instead of (re)launched.
+// Will wait until app is activated or launched, and fail the test if it
+// fails to do so.
+// In EG1, this method is a no-op.
+- (void)ensureAppLaunchedWithArgs:(NSArray<NSString*>*)arguments
+                     forceRestart:(BOOL)forceRestart;
+
+@end
+
+#endif  // IOS_TESTING_EARL_GREY_APP_LAUNCH_MANAGER_H_
diff --git a/ios/testing/earl_grey/app_launch_manager.mm b/ios/testing/earl_grey/app_launch_manager.mm
new file mode 100644
index 0000000..7164036c
--- /dev/null
+++ b/ios/testing/earl_grey/app_launch_manager.mm
@@ -0,0 +1,65 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/testing/earl_grey/app_launch_manager.h"
+
+#import <XCTest/XCTest.h>
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+#if defined(CHROME_EARL_GREY_2)  // avoid unused function warning in EG1
+namespace {
+// Checks if two pairs of launch arguments are equivalent.
+bool LaunchArgumentsAreEqual(NSArray<NSString*>* args1,
+                             NSArray<NSString*>* args2) {
+  // isEqualToArray will only return true if both arrays are non-nil,
+  // so first check if both arrays are empty or nil
+  if (!args1.count && !args2.count) {
+    return true;
+  }
+
+  return [args1 isEqualToArray:args2];
+}
+}  // namespace
+#endif
+
+@interface AppLaunchManager ()
+@property(nonatomic) XCUIApplication* runningApplication;
+@property(nonatomic) NSArray<NSString*>* currentLaunchArgs;
+@end
+
+@implementation AppLaunchManager
+
++ (AppLaunchManager*)sharedManager {
+  static AppLaunchManager* instance = nil;
+  static dispatch_once_t guard;
+  dispatch_once(&guard, ^{
+    instance = [[AppLaunchManager alloc] init];
+  });
+  return instance;
+}
+
+- (void)ensureAppLaunchedWithArgs:(NSArray<NSString*>*)arguments
+                     forceRestart:(BOOL)forceRestart {
+#if defined(CHROME_EARL_GREY_2)
+  bool appNeedsLaunching =
+      forceRestart || !self.runningApplication ||
+      !LaunchArgumentsAreEqual(arguments, self.currentLaunchArgs);
+
+  if (!appNeedsLaunching) {
+    [self.runningApplication activate];
+    return;
+  }
+
+  XCUIApplication* application = [[XCUIApplication alloc] init];
+  application.launchArguments = arguments;
+
+  [application launch];
+  self.runningApplication = application;
+  self.currentLaunchArgs = arguments;
+#endif
+}
+@end
diff --git a/ios/testing/earl_grey/base_earl_grey_test_case.mm b/ios/testing/earl_grey/base_earl_grey_test_case.mm
index fe496a9..cb4f8a3 100644
--- a/ios/testing/earl_grey/base_earl_grey_test_case.mm
+++ b/ios/testing/earl_grey/base_earl_grey_test_case.mm
@@ -7,6 +7,7 @@
 #import <UIKit/UIKit.h>
 #import <objc/runtime.h>
 
+#import "ios/testing/earl_grey/app_launch_manager.h"
 #import "ios/testing/earl_grey/coverage_utils.h"
 #import "ios/testing/earl_grey/earl_grey_test.h"
 
@@ -41,11 +42,8 @@
 }
 
 - (void)launchAppForTestMethod {
-  static dispatch_once_t launchAppToken;
-  dispatch_once(&launchAppToken, ^{
-    XCUIApplication* application = [[XCUIApplication alloc] init];
-    [application launch];
-  });
+  [[AppLaunchManager sharedManager] ensureAppLaunchedWithArgs:nil
+                                                 forceRestart:false];
 }
 
 // Prevents tests inheriting from this class from putting logic in +setUp.
diff --git a/ios/web/find_in_page/BUILD.gn b/ios/web/find_in_page/BUILD.gn
index 51fc333..41665c5 100644
--- a/ios/web/find_in_page/BUILD.gn
+++ b/ios/web/find_in_page/BUILD.gn
@@ -18,6 +18,8 @@
     "find_in_page_manager_delegate_bridge.mm",
     "find_in_page_manager_impl.h",
     "find_in_page_manager_impl.mm",
+    "find_in_page_request.h",
+    "find_in_page_request.mm",
   ]
 
   configs += [ "//build/config/compiler:enable_arc" ]
@@ -40,6 +42,7 @@
   sources = [
     "find_in_page_manager_delegate_bridge_unittest.mm",
     "find_in_page_manger_impl_unittest.mm",
+    "find_in_page_request_unittest.mm",
   ]
 
   configs += [ "//build/config/compiler:enable_arc" ]
diff --git a/ios/web/find_in_page/find_in_page_manager_impl.h b/ios/web/find_in_page/find_in_page_manager_impl.h
index c80d9c6..ac5e357 100644
--- a/ios/web/find_in_page/find_in_page_manager_impl.h
+++ b/ios/web/find_in_page/find_in_page_manager_impl.h
@@ -5,11 +5,10 @@
 #ifndef IOS_WEB_FIND_IN_PAGE_FIND_IN_PAGE_MANAGER_IMPL_H_
 #define IOS_WEB_FIND_IN_PAGE_FIND_IN_PAGE_MANAGER_IMPL_H_
 
-#include <list>
-#include <map>
 #include <string>
 
 #include "base/memory/weak_ptr.h"
+#import "ios/web/find_in_page/find_in_page_request.h"
 #import "ios/web/public/find_in_page/find_in_page_manager.h"
 #include "ios/web/public/web_state/web_state_observer.h"
 
@@ -37,92 +36,6 @@
 
  private:
   friend class web::WebStateUserData<FindInPageManagerImpl>;
-  // Keeps track of the state of an ongoing Find() request.
-  struct FindRequest {
-    FindRequest();
-    ~FindRequest();
-    // Clears properties and sets new |query| and |pending_frame_call_count|.
-    void Reset(NSString* query, int pending_frame_call_count);
-    int GetTotalMatchCount() const;
-    int GetRequestId() const;
-    NSString* GetRequestQuery() const;
-    // Returns the index of the currently selected match for all matches on the
-    // page. If no match is selected, then returns -1.
-    int GetCurrentSelectedMatchPageIndex();
-    // Sets |index| as the currently selected index relative to the selected
-    // frame.
-    void SetCurrentSelectedMatchFrameIndex(int index);
-    // Returns the index of the currently selected match relative to the matches
-    // within its frame. If no match is selected, then returns -1.
-    int GetCurrentSelectedMatchFrameIndex() const;
-    // Returns the number of matches in |selected_frame_id|. If no match is
-    // currently selected, then returns -1;
-    int GetMatchCountForSelectedFrame();
-    // Returns the number of matches in |frame_id|. If |frame_id| is invalid,
-    // then returns -1.
-    int GetMatchCountForFrame(const std::string& frame_id);
-    // Sets |match_count| for |selected_frame_id|.
-    void SetMatchCountForSelectedFrame(int match_count);
-    // Sets |match_count| for |frame_id|.
-    void SetMatchCountForFrame(int match_count, const std::string& frame_id);
-    // Returns the id of the WebFrame containing the currently selected match.
-    // Returns empty string if no currently selected match.
-    std::string GetSelectedFrameId();
-    // Sets |selected_frame_id| and |selected_match_index_in_selected_frame| to
-    // the first match on the page. No-op if no known matches exist. Returns
-    // true if selected a match, false otherwise.
-    bool GoToFirstMatch();
-    // Sets |selected_frame_id| and |selected_match_index_in_selected_frame| to
-    // the next match on the page. No-op if no known matches exist. Returns true
-    // if selected a match, false otherwise.
-    bool GoToNextMatch();
-    // Sets |selected_frame_id| and |selected_match_index_in_selected_frame| to
-    // the previous match on the page. No-op if no known matches exist. Returns
-    // true if selected a match, false otherwise.
-    bool GoToPreviousMatch();
-    // Removes frame with Id |frame_id| from |frame_order| and
-    // |frame_match_count|. Resets |selected_frame_id| and
-    // |selected_match_index_in_selected_frame| if the frame with |frame_id|
-    // contains the currently selected match.
-    void RemoveFrame(const std::string& frame_id);
-    // Adds new frame to |frame_order_| and |frame_match_count_|.
-    void AddFrame(WebFrame* web_frame);
-    // After each frame's Find request has finished, call this method to
-    // decrement |pending_frame_counts| to indicate to the receiver of the
-    // request completion.
-    void DidReceiveFindResponseFromOneFrame();
-    // Returns true if there are no more pending Find requests, false
-    // otherwise.
-    bool AreAllFindResponsesReturned();
-    // Unique identifier for each find used to check that it is the most recent
-    // find. This ensures that an old find doesn't decrement
-    // |pending_frame_calls_count| after it has been reset by the new find.
-    int unique_id = 0;
-    // Query string of find request. NSString type to ensure query passed to
-    // delegate methods is the same type as what is passed into Find().
-    NSString* query = nil;
-    // Counter to keep track of pending frame JavaScript calls.
-    int pending_frame_call_count = 0;
-    // Holds number of matches found for each frame keyed by frame_id.
-    std::map<std::string, int> frame_match_count;
-    // List of frame_ids used for sorting matches.
-    std::list<std::string> frame_order;
-    // Id of frame which has the currently selected match. Set to
-    // frame_order.end() if there is no currently selected match. All matches
-    // from the last find will be highlighted. However, the match at
-    // |selected_match_index_in_selected_frame| will be highlighted in a
-    // visually unique manner. This match is referred to as the "selected match"
-    // and can be changed with the FindInPageNext and FindInPagePrevious
-    // commands.
-    std::list<std::string>::iterator selected_frame_id = frame_order.end();
-    // Index of the currently selected match or -1 if there is none.
-    int selected_match_index_in_selected_frame = -1;
-
-   private:
-    // Returns true if |frame_id| contains the currently selected match, false
-    // otherwise.
-    bool IsSelectedFrame(const std::string& frame_id);
-  };
 
   // Executes find logic for |FindInPageSearch| option.
   void StartSearch(NSString* query);
@@ -156,7 +69,7 @@
   void WebStateDestroyed(WebState* web_state) override;
 
   // Holds the state of the most recent find in page request.
-  FindRequest last_find_request_;
+  FindInPageRequest last_find_request_;
   FindInPageManagerDelegate* delegate_ = nullptr;
   web::WebState* web_state_ = nullptr;
   base::WeakPtrFactory<FindInPageManagerImpl> weak_factory_;
diff --git a/ios/web/find_in_page/find_in_page_manager_impl.mm b/ios/web/find_in_page/find_in_page_manager_impl.mm
index b402152..5a89a7bb 100644
--- a/ios/web/find_in_page/find_in_page_manager_impl.mm
+++ b/ios/web/find_in_page/find_in_page_manager_impl.mm
@@ -63,211 +63,6 @@
   delegate_ = delegate;
 }
 
-FindInPageManagerImpl::FindRequest::FindRequest() {}
-
-FindInPageManagerImpl::FindRequest::~FindRequest() {}
-
-void FindInPageManagerImpl::FindRequest::Reset(
-    NSString* new_query,
-    int new_pending_frame_call_count) {
-  unique_id++;
-  selected_frame_id = frame_order.end();
-  selected_match_index_in_selected_frame = -1;
-  query = [new_query copy];
-  pending_frame_call_count = new_pending_frame_call_count;
-  for (auto& pair : frame_match_count) {
-    pair.second = 0;
-  }
-}
-
-int FindInPageManagerImpl::FindRequest::GetTotalMatchCount() const {
-  int matches = 0;
-  for (auto pair : frame_match_count) {
-    matches += pair.second;
-  }
-  return matches;
-}
-
-int FindInPageManagerImpl::FindRequest::GetRequestId() const {
-  return unique_id;
-}
-
-NSString* FindInPageManagerImpl::FindRequest::GetRequestQuery() const {
-  return query;
-}
-
-int FindInPageManagerImpl::FindRequest::GetCurrentSelectedMatchPageIndex() {
-  if (selected_match_index_in_selected_frame == -1) {
-    return -1;
-  }
-  // Count all matches in frames that come before frame with id
-  // |selected_frame_id|.
-  int total_match_index = selected_match_index_in_selected_frame;
-  for (auto it = frame_order.begin(); it != selected_frame_id; ++it) {
-    total_match_index += frame_match_count[*it];
-  }
-  return total_match_index;
-}
-
-void FindInPageManagerImpl::FindRequest::SetCurrentSelectedMatchFrameIndex(
-    int index) {
-  selected_match_index_in_selected_frame = index;
-}
-
-int FindInPageManagerImpl::FindRequest::GetCurrentSelectedMatchFrameIndex()
-    const {
-  return selected_match_index_in_selected_frame;
-}
-
-void FindInPageManagerImpl::FindRequest::DidReceiveFindResponseFromOneFrame() {
-  pending_frame_call_count--;
-}
-
-bool FindInPageManagerImpl::FindRequest::AreAllFindResponsesReturned() {
-  return pending_frame_call_count == 0;
-}
-
-int FindInPageManagerImpl::FindRequest::GetMatchCountForSelectedFrame() {
-  if (selected_frame_id == frame_order.end()) {
-    return -1;
-  }
-  return frame_match_count[*selected_frame_id];
-}
-
-int FindInPageManagerImpl::FindRequest::GetMatchCountForFrame(
-    const std::string& frame_id) {
-  if (frame_match_count.find(frame_id) == frame_match_count.end()) {
-    return -1;
-  }
-  return frame_match_count[frame_id];
-}
-
-bool FindInPageManagerImpl::FindRequest::GoToFirstMatch() {
-  for (auto frame_id = frame_order.begin(); frame_id != frame_order.end();
-       ++frame_id) {
-    if (frame_match_count[*frame_id] > 0) {
-      selected_frame_id = frame_id;
-      selected_match_index_in_selected_frame = 0;
-      return true;
-    }
-  }
-  return false;
-}
-
-bool FindInPageManagerImpl::FindRequest::GoToNextMatch() {
-  if (GetTotalMatchCount() == 0) {
-    return false;
-  }
-  // No currently selected match, but there are matches. Move iterator to
-  // beginning. This can happen if a frame containing the currently selected
-  // match is removed from the page.
-  if (selected_frame_id == frame_order.end()) {
-    selected_frame_id = frame_order.begin();
-  }
-
-  bool next_match_is_in_selected_frame =
-      selected_match_index_in_selected_frame + 1 <
-      frame_match_count[*selected_frame_id];
-  if (next_match_is_in_selected_frame) {
-    selected_match_index_in_selected_frame++;
-  } else {
-    // Since the function returns early if there are no matches, an infinite
-    // loop should not be a risk.
-    do {
-      if (selected_frame_id == --frame_order.end()) {
-        selected_frame_id = frame_order.begin();
-      } else {
-        selected_frame_id++;
-      }
-    } while (frame_match_count[*selected_frame_id] == 0);
-    // Should have found new frame.
-    selected_match_index_in_selected_frame = 0;
-  }
-  return true;
-}
-
-bool FindInPageManagerImpl::FindRequest::GoToPreviousMatch() {
-  if (GetTotalMatchCount() == 0) {
-    return false;
-  }
-  // No currently selected match, but there are matches. Move iterator to
-  // beginning. This can happen if a frame containing the currently selected
-  // matchs is removed from the page.
-  if (selected_frame_id == frame_order.end()) {
-    selected_frame_id = frame_order.begin();
-  }
-
-  bool previous_match_is_in_selected_frame =
-      selected_match_index_in_selected_frame - 1 >= 0;
-  if (previous_match_is_in_selected_frame) {
-    selected_match_index_in_selected_frame--;
-  } else {
-    // Since the function returns early if there are no matches, an infinite
-    // loop should not be a risk.
-    do {
-      if (selected_frame_id == frame_order.begin()) {
-        selected_frame_id = --frame_order.end();
-      } else {
-        selected_frame_id--;
-      }
-    } while (frame_match_count[*selected_frame_id] == 0);
-    // Should have found new frame.
-    selected_match_index_in_selected_frame =
-        frame_match_count[*selected_frame_id] - 1;
-  }
-  return true;
-}
-
-void FindInPageManagerImpl::FindRequest::RemoveFrame(
-    const std::string& frame_id) {
-  if (IsSelectedFrame(frame_id)) {
-    // If currently selecting match in frame that will become unavailable,
-    // there will no longer be a selected match. Reset to unselected match
-    // state.
-    selected_frame_id = frame_order.end();
-    selected_match_index_in_selected_frame = -1;
-  }
-  frame_order.remove(frame_id);
-  frame_match_count.erase(frame_id);
-}
-
-void FindInPageManagerImpl::FindRequest::AddFrame(WebFrame* web_frame) {
-  frame_match_count[web_frame->GetFrameId()] = 0;
-  if (web_frame->IsMainFrame()) {
-    // Main frame matches should show up first.
-    frame_order.push_front(web_frame->GetFrameId());
-  } else {
-    // The order of iframes is not important.
-    frame_order.push_back(web_frame->GetFrameId());
-  }
-}
-
-void FindInPageManagerImpl::FindRequest::SetMatchCountForSelectedFrame(
-    int match_count) {
-  frame_match_count[*selected_frame_id] = match_count;
-}
-
-void FindInPageManagerImpl::FindRequest::SetMatchCountForFrame(
-    int match_count,
-    const std::string& frame_id) {
-  frame_match_count[frame_id] = match_count;
-}
-
-std::string FindInPageManagerImpl::FindRequest::GetSelectedFrameId() {
-  if (selected_frame_id == frame_order.end()) {
-    return std::string();
-  }
-  return *selected_frame_id;
-}
-
-bool FindInPageManagerImpl::FindRequest::IsSelectedFrame(
-    const std::string& frame_id) {
-  if (selected_frame_id == frame_order.end()) {
-    return false;
-  }
-  return *selected_frame_id == frame_id;
-}
-
 void FindInPageManagerImpl::WebFrameDidBecomeAvailable(WebState* web_state,
                                                        WebFrame* web_frame) {
   const std::string frame_id = web_frame->GetFrameId();
diff --git a/ios/web/find_in_page/find_in_page_request.h b/ios/web/find_in_page/find_in_page_request.h
new file mode 100644
index 0000000..4a15183
--- /dev/null
+++ b/ios/web/find_in_page/find_in_page_request.h
@@ -0,0 +1,117 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_WEB_FIND_IN_PAGE_FIND_IN_PAGE_REQUEST_H_
+#define IOS_WEB_FIND_IN_PAGE_FIND_IN_PAGE_REQUEST_H_
+
+#include <list>
+#include <map>
+#include <string>
+
+#import <Foundation/Foundation.h>
+
+@class NSString;
+
+namespace web {
+
+class WebFrame;
+
+// Keeps track of the state of a FindInPageManager::Find() request.
+class FindInPageRequest {
+ public:
+  FindInPageRequest();
+  ~FindInPageRequest();
+  // Clears properties and sets new |query| and |pending_frame_call_count|.
+  void Reset(NSString* query, int pending_frame_call_count);
+  int GetTotalMatchCount() const;
+  int GetRequestId() const;
+  NSString* GetRequestQuery() const;
+
+  // Sets |selected_frame_id| and |selected_match_index_in_selected_frame| to
+  // the first match on the page. No-op if no known matches exist. Returns
+  // true if selected a match, false otherwise.
+  bool GoToFirstMatch();
+  // Sets |selected_frame_id| and |selected_match_index_in_selected_frame| to
+  // the next match on the page. No-op if no known matches exist. Returns true
+  // if selected a match, false otherwise.
+  bool GoToNextMatch();
+  // Sets |selected_frame_id| and |selected_match_index_in_selected_frame| to
+  // the previous match on the page. No-op if no known matches exist. Returns
+  // true if selected a match, false otherwise.
+  bool GoToPreviousMatch();
+
+  // Returns the number of matches in |selected_frame_id|. If no match is
+  // currently selected, then returns -1;
+  int GetMatchCountForSelectedFrame();
+  // Sets |match_count| for |selected_frame_id|, if one exists.
+  void SetMatchCountForSelectedFrame(int match_count);
+
+  // Returns the index of the currently selected match for all matches on the
+  // page. If no match is selected, then returns -1.
+  int GetCurrentSelectedMatchPageIndex();
+  // Returns the id of the WebFrame containing the currently selected match.
+  // Returns empty string if no currently selected match.
+  std::string GetSelectedFrameId();
+
+  // Returns the index of the currently selected match relative to the matches
+  // within its frame. If no match is selected, then returns -1.
+  int GetCurrentSelectedMatchFrameIndex() const;
+  // Sets |index| as the currently selected index relative to the selected
+  // frame.
+  void SetCurrentSelectedMatchFrameIndex(int index);
+
+  // Returns the number of matches in |frame_id|. If |frame_id| is invalid,
+  // then returns -1.
+  int GetMatchCountForFrame(const std::string& frame_id);
+  // Sets |match_count| for |frame_id|.
+  void SetMatchCountForFrame(int match_count, const std::string& frame_id);
+
+  // Removes frame with Id |frame_id| from |frame_order| and
+  // |frame_match_count|. Resets |selected_frame_id| and
+  // |selected_match_index_in_selected_frame| if the frame with |frame_id|
+  // contains the currently selected match.
+  void RemoveFrame(const std::string& frame_id);
+  // Adds new frame to |frame_order_| and |frame_match_count_|.
+  void AddFrame(WebFrame* web_frame);
+
+  // After each frame's Find request has finished, call this method to
+  // decrement |pending_frame_counts| to indicate to the receiver of the
+  // request completion.
+  void DidReceiveFindResponseFromOneFrame();
+  // Returns true if there are no more pending Find requests, false
+  // otherwise.
+  bool AreAllFindResponsesReturned();
+
+ private:
+  // Unique identifier for each find used to check that it is the most recent
+  // find. This ensures that an old find doesn't decrement
+  // |pending_frame_calls_count| after it has been reset by the new find.
+  int unique_id_ = 0;
+  // Query string of find request. NSString type to ensure query passed to
+  // delegate methods is the same type as what is passed into Find().
+  NSString* query_ = nil;
+  // Counter to keep track of pending frame JavaScript calls.
+  int pending_frame_call_count_ = 0;
+  // Holds number of matches found for each frame keyed by frame_id.
+  std::map<std::string, int> frame_match_count_;
+  // List of frame_ids used for sorting matches.
+  std::list<std::string> frame_order_;
+  // Id of frame which has the currently selected match. Set to
+  // frame_order.end() if there is no currently selected match. All matches
+  // from the last find will be highlighted. However, the match at
+  // |selected_match_index_in_selected_frame| will be highlighted in a
+  // visually unique manner. This match is referred to as the "selected match"
+  // and can be changed with the FindInPageNext and FindInPagePrevious
+  // commands.
+  std::list<std::string>::iterator selected_frame_id_ = frame_order_.end();
+  // Index of the currently selected match or -1 if there is none.
+  int selected_match_index_in_selected_frame_ = -1;
+  // Returns true if |frame_id| contains the currently selected match, false
+  // otherwise.
+  bool IsSelectedFrame(const std::string& frame_id);
+};
+
+}  // namespace web
+
+#endif  // IOS_WEB_FIND_IN_PAGE_FIND_IN_PAGE_REQUEST_H_
diff --git a/ios/web/find_in_page/find_in_page_request.mm b/ios/web/find_in_page/find_in_page_request.mm
new file mode 100644
index 0000000..83493fd
--- /dev/null
+++ b/ios/web/find_in_page/find_in_page_request.mm
@@ -0,0 +1,216 @@
+// 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/web/find_in_page/find_in_page_request.h"
+
+#import <Foundation/Foundation.h>
+
+#import "ios/web/public/js_messaging/web_frame.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace web {
+
+FindInPageRequest::FindInPageRequest() {}
+
+FindInPageRequest::~FindInPageRequest() {}
+
+void FindInPageRequest::Reset(NSString* new_query_,
+                              int new_pending_frame_call_count) {
+  unique_id_++;
+  selected_frame_id_ = frame_order_.end();
+  selected_match_index_in_selected_frame_ = -1;
+  query_ = [new_query_ copy];
+  pending_frame_call_count_ = new_pending_frame_call_count;
+  for (auto& pair : frame_match_count_) {
+    pair.second = 0;
+  }
+}
+
+int FindInPageRequest::GetTotalMatchCount() const {
+  int matches = 0;
+  for (auto pair : frame_match_count_) {
+    matches += pair.second;
+  }
+  return matches;
+}
+
+int FindInPageRequest::GetRequestId() const {
+  return unique_id_;
+}
+NSString* FindInPageRequest::GetRequestQuery() const {
+  return query_;
+}
+
+bool FindInPageRequest::GoToFirstMatch() {
+  for (auto frame_id = frame_order_.begin(); frame_id != frame_order_.end();
+       ++frame_id) {
+    if (frame_match_count_[*frame_id] > 0) {
+      selected_frame_id_ = frame_id;
+      selected_match_index_in_selected_frame_ = 0;
+      return true;
+    }
+  }
+  return false;
+}
+
+bool FindInPageRequest::GoToNextMatch() {
+  if (GetTotalMatchCount() == 0) {
+    return false;
+  }
+  // No currently selected match, but there are matches. Move iterator to
+  // beginning. This can happen if a frame containing the currently selected
+  // match is removed from the page.
+  if (selected_frame_id_ == frame_order_.end()) {
+    selected_frame_id_ = frame_order_.begin();
+  }
+
+  bool next_match_is_in_selected_frame =
+      selected_match_index_in_selected_frame_ + 1 <
+      frame_match_count_[*selected_frame_id_];
+  if (next_match_is_in_selected_frame) {
+    selected_match_index_in_selected_frame_++;
+  } else {
+    // Since the function returns early if there are no matches, an infinite
+    // loop should not be a risk.
+    do {
+      if (selected_frame_id_ == --frame_order_.end()) {
+        selected_frame_id_ = frame_order_.begin();
+      } else {
+        selected_frame_id_++;
+      }
+    } while (frame_match_count_[*selected_frame_id_] == 0);
+    // Should have found new frame.
+    selected_match_index_in_selected_frame_ = 0;
+  }
+  return true;
+}
+
+bool FindInPageRequest::GoToPreviousMatch() {
+  if (GetTotalMatchCount() == 0) {
+    return false;
+  }
+  // No currently selected match, but there are matches. Move iterator to
+  // beginning. This can happen if a frame containing the currently selected
+  // matchs is removed from the page.
+  if (selected_frame_id_ == frame_order_.end()) {
+    selected_frame_id_ = frame_order_.begin();
+  }
+
+  bool previous_match_is_in_selected_frame =
+      selected_match_index_in_selected_frame_ - 1 >= 0;
+  if (previous_match_is_in_selected_frame) {
+    selected_match_index_in_selected_frame_--;
+  } else {
+    // Since the function returns early if there are no matches, an infinite
+    // loop should not be a risk.
+    do {
+      if (selected_frame_id_ == frame_order_.begin()) {
+        selected_frame_id_ = --frame_order_.end();
+      } else {
+        selected_frame_id_--;
+      }
+    } while (frame_match_count_[*selected_frame_id_] == 0);
+    // Should have found new frame.
+    selected_match_index_in_selected_frame_ =
+        frame_match_count_[*selected_frame_id_] - 1;
+  }
+  return true;
+}
+
+int FindInPageRequest::GetMatchCountForFrame(const std::string& frame_id) {
+  if (frame_match_count_.find(frame_id) == frame_match_count_.end()) {
+    return -1;
+  }
+  return frame_match_count_[frame_id];
+}
+
+void FindInPageRequest::SetMatchCountForFrame(int match_count,
+                                              const std::string& frame_id) {
+  frame_match_count_[frame_id] = match_count;
+}
+
+int FindInPageRequest::GetMatchCountForSelectedFrame() {
+  if (selected_frame_id_ == frame_order_.end()) {
+    return -1;
+  }
+  return frame_match_count_[*selected_frame_id_];
+}
+
+void FindInPageRequest::SetMatchCountForSelectedFrame(int match_count) {
+  if (selected_frame_id_ == frame_order_.end()) {
+    return;
+  }
+  frame_match_count_[*selected_frame_id_] = match_count;
+}
+
+int FindInPageRequest::GetCurrentSelectedMatchPageIndex() {
+  if (selected_match_index_in_selected_frame_ == -1) {
+    return -1;
+  }
+  // Count all matches in frames that come before frame with id
+  // |selected_frame_id|.
+  int total_match_index = selected_match_index_in_selected_frame_;
+  for (auto it = frame_order_.begin(); it != selected_frame_id_; ++it) {
+    total_match_index += frame_match_count_[*it];
+  }
+  return total_match_index;
+}
+
+std::string FindInPageRequest::GetSelectedFrameId() {
+  if (selected_frame_id_ == frame_order_.end()) {
+    return std::string();
+  }
+  return *selected_frame_id_;
+}
+
+int FindInPageRequest::GetCurrentSelectedMatchFrameIndex() const {
+  return selected_match_index_in_selected_frame_;
+}
+
+void FindInPageRequest::SetCurrentSelectedMatchFrameIndex(int index) {
+  selected_match_index_in_selected_frame_ = index;
+}
+
+void FindInPageRequest::RemoveFrame(const std::string& frame_id) {
+  if (IsSelectedFrame(frame_id)) {
+    // If currently selecting match in frame that will become unavailable,
+    // there will no longer be a selected match. Reset to unselected match
+    // state.
+    selected_frame_id_ = frame_order_.end();
+    selected_match_index_in_selected_frame_ = -1;
+  }
+  frame_order_.remove(frame_id);
+  frame_match_count_.erase(frame_id);
+}
+
+void FindInPageRequest::AddFrame(WebFrame* web_frame) {
+  frame_match_count_[web_frame->GetFrameId()] = 0;
+  if (web_frame->IsMainFrame()) {
+    // Main frame matches should show up first.
+    frame_order_.push_front(web_frame->GetFrameId());
+  } else {
+    // The order of iframes is not important.
+    frame_order_.push_back(web_frame->GetFrameId());
+  }
+}
+
+void FindInPageRequest::DidReceiveFindResponseFromOneFrame() {
+  pending_frame_call_count_--;
+}
+
+bool FindInPageRequest::AreAllFindResponsesReturned() {
+  return pending_frame_call_count_ == 0;
+}
+
+bool FindInPageRequest::IsSelectedFrame(const std::string& frame_id) {
+  if (selected_frame_id_ == frame_order_.end()) {
+    return false;
+  }
+  return *selected_frame_id_ == frame_id;
+}
+
+}  // namespace web
diff --git a/ios/web/find_in_page/find_in_page_request_unittest.mm b/ios/web/find_in_page/find_in_page_request_unittest.mm
new file mode 100644
index 0000000..87b0f31
--- /dev/null
+++ b/ios/web/find_in_page/find_in_page_request_unittest.mm
@@ -0,0 +1,195 @@
+// 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/web/find_in_page/find_in_page_request.h"
+
+#include "ios/web/public/test/fakes/fake_web_frame.h"
+#include "ios/web/public/test/web_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+const char kOneMatchFrameId[] = "frame_with_one_match";
+const char kTwoMatchesFrameId[] = "frame_with_two_matches";
+}  // namespace
+
+namespace web {
+
+class FindInPageRequestTest : public WebTest {
+ protected:
+  // Returns a FakeWebFrame with id |frame_id|.
+  std::unique_ptr<FakeWebFrame> CreateWebFrame(const std::string& frame_id,
+                                               bool is_main_frame) {
+    return std::make_unique<FakeWebFrame>(frame_id, is_main_frame,
+                                          GURL::EmptyGURL());
+  }
+
+  FindInPageRequestTest() {
+    auto main_frame = CreateWebFrame(kOneMatchFrameId,
+                                     /*is_main_frame=*/true);
+    request_.AddFrame(main_frame.get());
+    auto frame_with_two_matches =
+        CreateWebFrame(kTwoMatchesFrameId, /*is_main_frame=*/false);
+    request_.AddFrame(frame_with_two_matches.get());
+    request_.Reset(@"foo", 2);
+    request_.SetMatchCountForFrame(1, kOneMatchFrameId);
+    request_.SetMatchCountForFrame(2, kTwoMatchesFrameId);
+  }
+  FindInPageRequest request_;
+};
+
+// Tests that FindInPageRequest properly clears its properties in respond to a
+// Reset() call.
+TEST_F(FindInPageRequestTest, Reset) {
+  EXPECT_EQ(3, request_.GetTotalMatchCount());
+
+  EXPECT_TRUE(request_.GoToFirstMatch());
+
+  EXPECT_EQ(0, request_.GetCurrentSelectedMatchFrameIndex());
+  EXPECT_EQ(1, request_.GetMatchCountForSelectedFrame());
+
+  int request_id = request_.GetRequestId();
+  request_.Reset(@"foobar", 2);
+
+  EXPECT_GE(request_.GetRequestId(), request_id);
+  EXPECT_EQ(-1, request_.GetCurrentSelectedMatchFrameIndex());
+  EXPECT_EQ(@"foobar", request_.GetRequestQuery());
+  EXPECT_EQ(0, request_.GetTotalMatchCount());
+  EXPECT_EQ(-1, request_.GetMatchCountForSelectedFrame());
+}
+
+// Tests that FindinPageRequest properly decrements |pending_frame_call_count_|
+// properly.
+TEST_F(FindInPageRequestTest, AllFindResponsesReturned) {
+  request_.DidReceiveFindResponseFromOneFrame();
+  EXPECT_FALSE(request_.AreAllFindResponsesReturned());
+
+  request_.DidReceiveFindResponseFromOneFrame();
+  EXPECT_TRUE(request_.AreAllFindResponsesReturned());
+}
+
+// Tests that FindInPageRequest GoToNextMatch() is able to traverse all matches
+// in multiple frames.
+TEST_F(FindInPageRequestTest, GoToNext) {
+  request_.GoToFirstMatch();
+
+  EXPECT_EQ(0, request_.GetCurrentSelectedMatchFrameIndex());
+  EXPECT_EQ(0, request_.GetCurrentSelectedMatchPageIndex());
+
+  request_.GoToNextMatch();
+
+  EXPECT_EQ(0, request_.GetCurrentSelectedMatchFrameIndex());
+  EXPECT_EQ(1, request_.GetCurrentSelectedMatchPageIndex());
+
+  request_.GoToNextMatch();
+
+  EXPECT_EQ(1, request_.GetCurrentSelectedMatchFrameIndex());
+  EXPECT_EQ(2, request_.GetCurrentSelectedMatchPageIndex());
+
+  request_.GoToNextMatch();
+
+  EXPECT_EQ(0, request_.GetCurrentSelectedMatchFrameIndex());
+  EXPECT_EQ(0, request_.GetCurrentSelectedMatchPageIndex());
+}
+
+// Tests that FindInPageRequest GoToPreviousMatch() is able to traverse all
+// matches in multiple frames.
+TEST_F(FindInPageRequestTest, GoToPrevious) {
+  request_.GoToFirstMatch();
+
+  EXPECT_EQ(0, request_.GetCurrentSelectedMatchFrameIndex());
+  EXPECT_EQ(0, request_.GetCurrentSelectedMatchPageIndex());
+
+  request_.GoToPreviousMatch();
+
+  EXPECT_EQ(1, request_.GetCurrentSelectedMatchFrameIndex());
+  EXPECT_EQ(2, request_.GetCurrentSelectedMatchPageIndex());
+
+  request_.GoToPreviousMatch();
+
+  EXPECT_EQ(0, request_.GetCurrentSelectedMatchFrameIndex());
+  EXPECT_EQ(1, request_.GetCurrentSelectedMatchPageIndex());
+
+  request_.GoToPreviousMatch();
+
+  EXPECT_EQ(0, request_.GetCurrentSelectedMatchFrameIndex());
+  EXPECT_EQ(0, request_.GetCurrentSelectedMatchPageIndex());
+}
+
+// Tests that FindInPageRequest returns the correct relative match count within
+// a frame and total match count when traversing matches in multiple frames.
+TEST_F(FindInPageRequestTest, RelativeMatchCount) {
+  request_.GoToFirstMatch();
+
+  EXPECT_EQ(3, request_.GetTotalMatchCount());
+  EXPECT_EQ(1, request_.GetMatchCountForSelectedFrame());
+
+  request_.GoToNextMatch();
+
+  EXPECT_EQ(3, request_.GetTotalMatchCount());
+  EXPECT_EQ(2, request_.GetMatchCountForSelectedFrame());
+}
+
+// Tests that FindInPageRequest returns the correct relative match count within
+// a frame and total match count when a frame is removed. Also tests that going
+// to the next match after removing the currently selected frame produces the
+// expected relative and total selected match index.
+TEST_F(FindInPageRequestTest, RemoveFrame) {
+  request_.GoToFirstMatch();
+
+  EXPECT_EQ(3, request_.GetTotalMatchCount());
+  EXPECT_EQ(1, request_.GetMatchCountForSelectedFrame());
+
+  request_.RemoveFrame(kOneMatchFrameId);
+
+  EXPECT_EQ(2, request_.GetTotalMatchCount());
+
+  request_.GoToNextMatch();
+
+  EXPECT_EQ(0, request_.GetCurrentSelectedMatchFrameIndex());
+  EXPECT_EQ(0, request_.GetCurrentSelectedMatchPageIndex());
+}
+
+// Tests that FindInPageRequest returns the correct relative match count within
+// a frame and total match count when the match count for the currently selected
+// frame changes.
+TEST_F(FindInPageRequestTest, SetMatchCountForSelectedFrame) {
+  request_.GoToFirstMatch();
+  request_.SetMatchCountForSelectedFrame(5);
+
+  EXPECT_EQ(7, request_.GetTotalMatchCount());
+  EXPECT_EQ(5, request_.GetMatchCountForSelectedFrame());
+}
+
+// Tests that FindInPageRequest returns the currently selected match index
+// relative to the frame and the total are correct when the total matches and
+// the relative match index change.
+TEST_F(FindInPageRequestTest, SetCurrentSelectedMatchIndex) {
+  request_.GoToFirstMatch();
+  request_.SetMatchCountForSelectedFrame(5);
+  request_.SetCurrentSelectedMatchFrameIndex(1);
+
+  EXPECT_EQ(1, request_.GetCurrentSelectedMatchFrameIndex());
+  EXPECT_EQ(1, request_.GetCurrentSelectedMatchPageIndex());
+}
+
+// Tests that FindInPageRequest returns the correct match count within
+// a frame and total match count when the match count for a not currently
+// selected frame changes.
+TEST_F(FindInPageRequestTest, SetMatchCountForFrame) {
+  request_.GoToFirstMatch();
+
+  EXPECT_EQ(3, request_.GetTotalMatchCount());
+  EXPECT_EQ(1, request_.GetMatchCountForSelectedFrame());
+
+  request_.SetMatchCountForFrame(5, kTwoMatchesFrameId);
+
+  EXPECT_EQ(6, request_.GetTotalMatchCount());
+  EXPECT_EQ(1, request_.GetMatchCountForSelectedFrame());
+}
+
+}  // namespace web
diff --git a/media/filters/frame_processor.cc b/media/filters/frame_processor.cc
index 3d4e312..7de3796 100644
--- a/media/filters/frame_processor.cc
+++ b/media/filters/frame_processor.cc
@@ -653,6 +653,8 @@
         << "us";
 
     // Mark the overlapping portion of the buffer for discard.
+    // TODO(wolenetz): Is this correct to ignore any pre-existing discard
+    // padding (e.g. WebM discard padding)? See https://crbug.com/969195.
     buffer->set_discard_padding(std::make_pair(
         append_window_start - buffer->timestamp(), base::TimeDelta()));
 
@@ -678,6 +680,8 @@
                                             : "");
 
     // Mark the overlapping portion of the buffer for discard.
+    // TODO(wolenetz): Is this correct to ignore any pre-existing discard
+    // padding (e.g. WebM discard padding)? See https://crbug.com/969195.
     buffer->set_discard_padding(
         std::make_pair(buffer->discard_padding().first,
                        frame_end_timestamp - append_window_end));
diff --git a/media/formats/webm/webm_cluster_parser.cc b/media/formats/webm/webm_cluster_parser.cc
index 52cca8b..e2d7d95 100644
--- a/media/formats/webm/webm_cluster_parser.cc
+++ b/media/formats/webm/webm_cluster_parser.cc
@@ -422,8 +422,11 @@
 
       // Read in the big-endian integer.
       discard_padding_ = static_cast<int8_t>(data[0]);
-      for (int i = 1; i < size; ++i)
-        discard_padding_ = (discard_padding_ << 8) | data[i];
+      for (int i = 1; i < size; ++i) {
+        // Multiplying instead of shifting, since the padding may be negative,
+        // and shifting a negative value is undefined.
+        discard_padding_ = (discard_padding_ * 256) | data[i];
+      }
 
       return true;
     }
@@ -611,6 +614,8 @@
     buffer->set_duration(track->default_duration());
   }
 
+  // TODO(wolenetz): Is this correct for negative |discard_padding|? See
+  // https://crbug.com/969195.
   if (discard_padding != 0) {
     buffer->set_discard_padding(std::make_pair(
         base::TimeDelta(),
diff --git a/media/formats/webm/webm_colour_parser.cc b/media/formats/webm/webm_colour_parser.cc
index c8b65f0..a804931 100644
--- a/media/formats/webm/webm_colour_parser.cc
+++ b/media/formats/webm/webm_colour_parser.cc
@@ -190,17 +190,25 @@
   color_metadata.color_space = VideoColorSpace(
       primaries_, transfer_characteristics_, matrix_coefficients_, range_id);
 
-  if (max_content_light_level_ != -1)
-    color_metadata.hdr_metadata.max_content_light_level =
-        max_content_light_level_;
+  if (max_content_light_level_ != -1 || max_frame_average_light_level_ != -1 ||
+      mastering_metadata_parsed_) {
+    color_metadata.hdr_metadata = HDRMetadata();
 
-  if (max_frame_average_light_level_ != -1)
-    color_metadata.hdr_metadata.max_frame_average_light_level =
-        max_frame_average_light_level_;
+    if (max_content_light_level_ != -1) {
+      color_metadata.hdr_metadata->max_content_light_level =
+          max_content_light_level_;
+    }
 
-  if (mastering_metadata_parsed_)
-    color_metadata.hdr_metadata.mastering_metadata =
-        mastering_metadata_parser_.GetMasteringMetadata();
+    if (max_frame_average_light_level_ != -1) {
+      color_metadata.hdr_metadata->max_frame_average_light_level =
+          max_frame_average_light_level_;
+    }
+
+    if (mastering_metadata_parsed_) {
+      color_metadata.hdr_metadata->mastering_metadata =
+          mastering_metadata_parser_.GetMasteringMetadata();
+    }
+  }
 
   return color_metadata;
 }
diff --git a/media/formats/webm/webm_colour_parser.h b/media/formats/webm/webm_colour_parser.h
index 636bdc9..af05479 100644
--- a/media/formats/webm/webm_colour_parser.h
+++ b/media/formats/webm/webm_colour_parser.h
@@ -6,6 +6,7 @@
 #define MEDIA_FORMATS_WEBM_WEBM_COLOUR_PARSER_H_
 
 #include "base/macros.h"
+#include "base/optional.h"
 #include "media/base/hdr_metadata.h"
 #include "media/base/video_color_space.h"
 #include "media/formats/webm/webm_parser.h"
@@ -25,7 +26,7 @@
 
   VideoColorSpace color_space;
 
-  HDRMetadata hdr_metadata;
+  base::Optional<HDRMetadata> hdr_metadata;
 
   WebMColorMetadata();
   WebMColorMetadata(const WebMColorMetadata& rhs);
diff --git a/media/formats/webm/webm_video_client.cc b/media/formats/webm/webm_video_client.cc
index d2e5b8c8..9a986ba 100644
--- a/media/formats/webm/webm_video_client.cc
+++ b/media/formats/webm/webm_video_client.cc
@@ -13,14 +13,15 @@
 namespace {
 
 // Tries to parse |data| to extract the VP9 Profile ID, or returns Profile 0.
-media::VideoCodecProfile GetVP9CodecProfile(const std::vector<uint8_t>& data) {
+media::VideoCodecProfile GetVP9CodecProfile(const std::vector<uint8_t>& data,
+                                            bool is_probably_10bit) {
   // VP9 CodecPrivate (http://wiki.webmproject.org/vp9-codecprivate) might have
   // Profile information in the first field, if present.
   constexpr uint8_t kVP9ProfileFieldId = 0x01;
   constexpr uint8_t kVP9ProfileFieldLength = 1;
   if (data.size() < 3 || data[0] != kVP9ProfileFieldId ||
       data[1] != kVP9ProfileFieldLength || data[2] > 3) {
-    return VP9PROFILE_PROFILE0;
+    return is_probably_10bit ? VP9PROFILE_PROFILE2 : VP9PROFILE_PROFILE0;
   }
 
   return static_cast<VideoCodecProfile>(
@@ -56,6 +57,16 @@
     VideoDecoderConfig* config) {
   DCHECK(config);
 
+  bool is_8bit = true;
+  VideoColorSpace color_space = VideoColorSpace::REC709();
+  if (colour_parsed_) {
+    WebMColorMetadata color_metadata = colour_parser_.GetWebMColorMetadata();
+    color_space = color_metadata.color_space;
+    if (color_metadata.hdr_metadata.has_value())
+      config->set_hdr_metadata(*color_metadata.hdr_metadata);
+    is_8bit = color_metadata.BitsPerChannel <= 8;
+  }
+
   VideoCodec video_codec = kUnknownVideoCodec;
   VideoCodecProfile profile = VIDEO_CODEC_PROFILE_UNKNOWN;
   if (codec_id == "V_VP8") {
@@ -63,7 +74,9 @@
     profile = VP8PROFILE_ANY;
   } else if (codec_id == "V_VP9") {
     video_codec = kCodecVP9;
-    profile = GetVP9CodecProfile(codec_private);
+    profile = GetVP9CodecProfile(
+        codec_private, color_space.ToGfxColorSpace().IsHDR() ||
+                           config->hdr_metadata().has_value() || !is_8bit);
 #if BUILDFLAG(ENABLE_AV1_DECODER)
   } else if (codec_id == "V_AV1") {
     // TODO(dalecurtis): AV1 profiles in WebM are not finalized, this needs
@@ -121,12 +134,6 @@
   }
   gfx::Size natural_size = gfx::Size(display_width_, display_height_);
 
-  VideoColorSpace color_space = VideoColorSpace::REC709();
-  if (colour_parsed_) {
-    WebMColorMetadata color_metadata = colour_parser_.GetWebMColorMetadata();
-    color_space = color_metadata.color_space;
-    config->set_hdr_metadata(color_metadata.hdr_metadata);
-  }
   config->Initialize(video_codec, profile, format, color_space,
                      kNoTransformation, coded_size, visible_rect, natural_size,
                      codec_private, encryption_scheme);
diff --git a/media/formats/webm/webm_video_client_unittest.cc b/media/formats/webm/webm_video_client_unittest.cc
index d9f2dd7..b3ebf0b 100644
--- a/media/formats/webm/webm_video_client_unittest.cc
+++ b/media/formats/webm/webm_video_client_unittest.cc
@@ -41,12 +41,93 @@
     webm_video_client_.OnUInt(kWebMIdPixelHeight, kCodedSize.height());
   }
 
+  WebMParserClient* OnListStart(int id) {
+    return webm_video_client_.OnListStart(id);
+  }
+
+  void OnListEnd(int id) { webm_video_client_.OnListEnd(id); }
+
   testing::StrictMock<MockMediaLog> media_log_;
   WebMVideoClient webm_video_client_;
 
   DISALLOW_COPY_AND_ASSIGN(WebMVideoClientTest);
 };
 
+TEST_P(WebMVideoClientTest, AutodetectVp9Profile2NoDetection) {
+  const bool has_valid_codec_private = GetParam().codec_private.size() > 3;
+
+  auto* parser = OnListStart(kWebMIdColour);
+  // Set 8bit and SDR fields.
+  parser->OnUInt(kWebMIdBitsPerChannel, 8);
+  parser->OnUInt(kWebMIdTransferCharacteristics,
+                 static_cast<int64_t>(VideoColorSpace::TransferID::BT709));
+  OnListEnd(kWebMIdColour);
+
+  VideoDecoderConfig config;
+  EXPECT_TRUE(webm_video_client_.InitializeConfig(
+      "V_VP9", GetParam().codec_private, EncryptionScheme(), &config));
+
+  if (!has_valid_codec_private)
+    EXPECT_EQ(config.profile(), VP9PROFILE_PROFILE0);
+  else
+    EXPECT_EQ(config.profile(), GetParam().profile);
+}
+
+TEST_P(WebMVideoClientTest, AutodetectVp9Profile2BitsPerChannel) {
+  const bool has_valid_codec_private = GetParam().codec_private.size() > 3;
+
+  auto* parser = OnListStart(kWebMIdColour);
+  parser->OnUInt(kWebMIdBitsPerChannel, 10);
+  OnListEnd(kWebMIdColour);
+
+  VideoDecoderConfig config;
+  EXPECT_TRUE(webm_video_client_.InitializeConfig(
+      "V_VP9", GetParam().codec_private, EncryptionScheme(), &config));
+
+  if (!has_valid_codec_private)
+    EXPECT_EQ(config.profile(), VP9PROFILE_PROFILE2);
+  else
+    EXPECT_EQ(config.profile(), GetParam().profile);
+}
+
+TEST_P(WebMVideoClientTest, AutodetectVp9Profile2HDRMetaData) {
+  const bool has_valid_codec_private = GetParam().codec_private.size() > 3;
+
+  auto* color_parser = OnListStart(kWebMIdColour);
+  auto* metadata_parser = color_parser->OnListStart(kWebMIdMasteringMetadata);
+  metadata_parser->OnFloat(kWebMIdPrimaryRChromaticityX, 1.0);
+  color_parser->OnListEnd(kWebMIdMasteringMetadata);
+  OnListEnd(kWebMIdColour);
+
+  VideoDecoderConfig config;
+  EXPECT_TRUE(webm_video_client_.InitializeConfig(
+      "V_VP9", GetParam().codec_private, EncryptionScheme(), &config));
+
+  if (!has_valid_codec_private)
+    EXPECT_EQ(config.profile(), VP9PROFILE_PROFILE2);
+  else
+    EXPECT_EQ(config.profile(), GetParam().profile);
+}
+
+TEST_P(WebMVideoClientTest, AutodetectVp9Profile2HDRColorSpace) {
+  const bool has_valid_codec_private = GetParam().codec_private.size() > 3;
+
+  auto* parser = OnListStart(kWebMIdColour);
+  parser->OnUInt(
+      kWebMIdTransferCharacteristics,
+      static_cast<int64_t>(VideoColorSpace::TransferID::SMPTEST2084));
+  OnListEnd(kWebMIdColour);
+
+  VideoDecoderConfig config;
+  EXPECT_TRUE(webm_video_client_.InitializeConfig(
+      "V_VP9", GetParam().codec_private, EncryptionScheme(), &config));
+
+  if (!has_valid_codec_private)
+    EXPECT_EQ(config.profile(), VP9PROFILE_PROFILE2);
+  else
+    EXPECT_EQ(config.profile(), GetParam().profile);
+}
+
 TEST_P(WebMVideoClientTest, InitializeConfigVP9Profiles) {
   const std::string kCodecId = "V_VP9";
   const VideoCodecProfile profile = GetParam().profile;
diff --git a/media/gpu/windows/dxva_video_decode_accelerator_win.cc b/media/gpu/windows/dxva_video_decode_accelerator_win.cc
index 4bd4b2f..0bb051f 100644
--- a/media/gpu/windows/dxva_video_decode_accelerator_win.cc
+++ b/media/gpu/windows/dxva_video_decode_accelerator_win.cc
@@ -241,12 +241,13 @@
 // on the given |video_device|.
 bool IsResolutionSupportedForDevice(const gfx::Size& resolution_to_test,
                                     const GUID& decoder_guid,
-                                    ID3D11VideoDevice* video_device) {
+                                    ID3D11VideoDevice* video_device,
+                                    DXGI_FORMAT format) {
   D3D11_VIDEO_DECODER_DESC desc = {
       decoder_guid,                 // Guid
       resolution_to_test.width(),   // SampleWidth
       resolution_to_test.height(),  // SampleHeight
-      DXGI_FORMAT_NV12              // OutputFormat
+      format                        // OutputFormat
   };
 
   // We've chosen the least expensive test for identifying if a given resolution
@@ -273,7 +274,8 @@
     const gfx::Size& default_max,
     ID3D11VideoDevice* video_device,
     const std::vector<GUID>& valid_guids,
-    const std::vector<gfx::Size>& resolutions_to_test) {
+    const std::vector<gfx::Size>& resolutions_to_test,
+    DXGI_FORMAT format = DXGI_FORMAT_NV12) {
   TRACE_EVENT0("gpu,startup", "GetMaxResolutionsForGUIDs");
   ResolutionPair result(default_max, gfx::Size());
 
@@ -300,16 +302,20 @@
                         }));
 
   for (const auto& res : resolutions_to_test) {
-    if (!IsResolutionSupportedForDevice(res, decoder_guid, video_device))
+    if (!IsResolutionSupportedForDevice(res, decoder_guid, video_device,
+                                        format)) {
       break;
+    }
     result.first = res;
   }
 
   // The max supported portrait resolution should be just be a w/h flip of the
   // max supported landscape resolution.
   gfx::Size flipped(result.first.height(), result.first.width());
-  if (IsResolutionSupportedForDevice(flipped, decoder_guid, video_device))
+  if (IsResolutionSupportedForDevice(flipped, decoder_guid, video_device,
+                                     format)) {
     result.second = flipped;
+  }
 
   return result;
 }
@@ -1447,7 +1453,8 @@
               {D3D11_DECODER_PROFILE_VP9_VLD_10BIT_PROFILE2},
               {gfx::Size(4096, 2160), gfx::Size(4096, 2304),
                gfx::Size(7680, 4320), gfx::Size(8192, 4320),
-               gfx::Size(8192, 8192)});
+               gfx::Size(8192, 8192)},
+              DXGI_FORMAT_P010);
         }
       }
     }
diff --git a/net/base/network_change_notifier_fuchsia.cc b/net/base/network_change_notifier_fuchsia.cc
index f3d4853..7670814 100644
--- a/net/base/network_change_notifier_fuchsia.cc
+++ b/net/base/network_change_notifier_fuchsia.cc
@@ -36,6 +36,7 @@
   // and routing table synchronously to populate the initial state.
   fuchsia::netstack::NetstackSyncPtr sync_netstack;
   sync_netstack.Bind(netstack.Unbind());
+  DCHECK(sync_netstack);
 
   // Manually fetch the interfaces and routes.
   std::vector<fuchsia::netstack::NetInterface> interfaces;
@@ -48,6 +49,7 @@
 
   // Re-wrap Netstack back into an asynchronous pointer.
   netstack_.Bind(sync_netstack.Unbind());
+  DCHECK(netstack_);
   netstack_.set_error_handler([](zx_status_t status) {
     // TODO(https://crbug.com/901092): Unit tests that use NetworkService are
     // crashing because NetworkService does not clean up properly, and the
diff --git a/remoting/host/disconnect_window_mac.h b/remoting/host/disconnect_window_mac.h
index 7fd6115..f9be334 100644
--- a/remoting/host/disconnect_window_mac.h
+++ b/remoting/host/disconnect_window_mac.h
@@ -16,13 +16,13 @@
  @private
   base::Closure disconnect_callback_;
   base::string16 username_;
-  IBOutlet NSTextField* connectedToField_;
-  IBOutlet NSButton* disconnectButton_;
 }
 
 - (id)initWithCallback:(const base::Closure&)disconnect_callback
-              username:(const std::string&)username;
-- (IBAction)stopSharing:(id)sender;
+              username:(const std::string&)username
+                window:(NSWindow*)window;
+- (void)initializeWindow;
+- (void)stopSharing:(id)sender;
 @end
 
 // A floating window with a custom border. The custom border and background
diff --git a/remoting/host/disconnect_window_mac.mm b/remoting/host/disconnect_window_mac.mm
index 66c8a45..90358e1 100644
--- a/remoting/host/disconnect_window_mac.mm
+++ b/remoting/host/disconnect_window_mac.mm
@@ -23,6 +23,8 @@
 @interface DisconnectWindowController()
 - (BOOL)isRToL;
 - (void)Hide;
+@property(nonatomic, retain) NSTextField* connectedToField;
+@property(nonatomic, retain) NSButton* disconnectButton;
 @end
 
 const int kMaximumConnectedNameWidthInPixels = 600;
@@ -69,9 +71,18 @@
                  client_session_control, protocol::OK);
   std::string client_jid = client_session_control->client_jid();
   std::string username = client_jid.substr(0, client_jid.find('/'));
+
+  NSRect frame = NSMakeRect(0, 0, 466, 40);
+  DisconnectWindow* window =
+      [[[DisconnectWindow alloc] initWithContentRect:frame
+                                           styleMask:NSBorderlessWindowMask
+                                             backing:NSBackingStoreBuffered
+                                               defer:NO] autorelease];
   window_controller_ =
       [[DisconnectWindowController alloc] initWithCallback:disconnect_callback
-                                                  username:username];
+                                                  username:username
+                                                    window:window];
+  [window_controller_ initializeWindow];
   [window_controller_ showWindow:nil];
 }
 
@@ -83,9 +94,13 @@
 }  // namespace remoting
 
 @implementation DisconnectWindowController
+@synthesize connectedToField = connectedToField_;
+@synthesize disconnectButton = disconnectButton_;
+
 - (id)initWithCallback:(const base::Closure&)disconnect_callback
-              username:(const std::string&)username {
-  self = [super initWithWindowNibName:@"disconnect_window"];
+              username:(const std::string&)username
+                window:(NSWindow*)window {
+  self = [super initWithWindow:(NSWindow*)window];
   if (self) {
     disconnect_callback_ = disconnect_callback;
     username_ = base::UTF8ToUTF16(username);
@@ -112,7 +127,27 @@
   [self close];
 }
 
-- (void)windowDidLoad {
+- (void)initializeWindow {
+  self.window.contentView = [[[DisconnectView alloc]
+      initWithFrame:self.window.contentView.frame] autorelease];
+
+  self.connectedToField = [[[NSTextField alloc]
+      initWithFrame:NSMakeRect(26, 13, 240, 14)] autorelease];
+  self.connectedToField.drawsBackground = NO;
+  self.connectedToField.bezeled = NO;
+  self.connectedToField.editable = NO;
+  self.connectedToField.font = [NSFont systemFontOfSize:11];
+  [self.window.contentView addSubview:self.connectedToField];
+
+  self.disconnectButton = [[[NSButton alloc]
+      initWithFrame:NSMakeRect(271, 9, 182, 22)] autorelease];
+  self.disconnectButton.buttonType = NSButtonTypeMomentaryPushIn;
+  self.disconnectButton.bezelStyle = NSBezelStyleRegularSquare;
+  self.disconnectButton.font = [NSFont systemFontOfSize:11];
+  self.disconnectButton.action = @selector(stopSharing:);
+  self.disconnectButton.target = self;
+  [self.window.contentView addSubview:self.disconnectButton];
+
   [connectedToField_ setStringValue:l10n_util::GetNSStringF(IDS_MESSAGE_SHARED,
                                                             username_)];
   [disconnectButton_ setTitle:l10n_util::GetNSString(IDS_STOP_SHARING_BUTTON)];
@@ -137,6 +172,8 @@
 
   // Move the disconnect button appropriately.
   disconnectFrame.origin.x += newConnectedWidth - oldConnectedWidth;
+  disconnectFrame.origin.y =
+      (NSHeight(self.window.contentView.frame) - NSHeight(disconnectFrame)) / 2;
   [disconnectButton_ setFrame:disconnectFrame];
 
   // Then resize the window appropriately
diff --git a/remoting/host/heartbeat_sender.cc b/remoting/host/heartbeat_sender.cc
index 6e17980..fc9e95f8 100644
--- a/remoting/host/heartbeat_sender.cc
+++ b/remoting/host/heartbeat_sender.cc
@@ -41,8 +41,13 @@
     base::TimeDelta::FromSeconds(30);
 constexpr base::TimeDelta kResendDelayOnHostNotFound =
     base::TimeDelta::FromSeconds(10);
+constexpr base::TimeDelta kResendDelayOnUnauthenticated =
+    base::TimeDelta::FromSeconds(10);
 
-const int kMaxResendOnHostNotFoundCount = 12;  // 2 minutes (12 x 10 seconds).
+constexpr int kMaxResendOnHostNotFoundCount =
+    12;  // 2 minutes (12 x 10 seconds).
+constexpr int kMaxResendOnUnauthenticatedCount =
+    6;  // 1 minute (10 x 6 seconds).
 
 const net::BackoffEntry::Policy kBackoffPolicy = {
     // Number of initial errors (in sequence) to ignore before applying
@@ -105,17 +110,14 @@
 void HeartbeatSender::HeartbeatClient::Heartbeat(
     const apis::v1::HeartbeatRequest& request,
     HeartbeatResponseCallback callback) {
-  HOST_LOG << "Sending outgoing heartbeat:\n"
-           << "signature: " << request.signature() << "\n"
-           << "host_id: " << request.host_id() << "\n"
-           << "jabber_id: " << request.jabber_id() << "\n"
-           << "tachyon_id: " << request.tachyon_id() << "\n"
-           << "sequence_id: " << request.sequence_id() << "\n"
-           << "host_version: " << request.host_version() << "\n"
-           << "host_offline_reason: " << request.host_offline_reason() << "\n"
-           << "host_os_name: " << request.host_os_name() << "\n"
-           << "host_os_version: " << request.host_os_version() << "\n"
-           << "=========================================================";
+  std::string host_offline_reason_or_empty_log =
+      request.has_host_offline_reason()
+          ? (", host_offline_reason: " + request.host_offline_reason())
+          : "";
+  HOST_LOG << "Sending outgoing heartbeat."
+           << " jabber_id: " << request.jabber_id()
+           << ", tachyon_id: " << request.tachyon_id()
+           << host_offline_reason_or_empty_log;
 
   auto client_context = std::make_unique<grpc::ClientContext>();
   auto async_request = CreateGrpcAsyncUnaryRequest(
@@ -141,6 +143,7 @@
 HeartbeatSender::HeartbeatSender(
     base::OnceClosure on_heartbeat_successful_callback,
     base::OnceClosure on_unknown_host_id_error,
+    base::OnceClosure on_auth_error,
     const std::string& host_id,
     MuxingSignalStrategy* signal_strategy,
     const scoped_refptr<const RsaKeyPair>& host_key_pair,
@@ -149,11 +152,13 @@
     : on_heartbeat_successful_callback_(
           std::move(on_heartbeat_successful_callback)),
       on_unknown_host_id_error_(std::move(on_unknown_host_id_error)),
+      on_auth_error_(std::move(on_auth_error)),
       host_id_(host_id),
       signal_strategy_(signal_strategy),
       host_key_pair_(host_key_pair),
       client_(std::make_unique<HeartbeatClient>(oauth_token_getter)),
       log_to_server_(log_to_server),
+      oauth_token_getter_(oauth_token_getter),
       backoff_(&kBackoffPolicy) {
   DCHECK(host_key_pair_.get());
   DCHECK(log_to_server_);
@@ -294,6 +299,16 @@
     return;
   }
 
+  if (status.error_code() == grpc::StatusCode::UNAUTHENTICATED) {
+    oauth_token_getter_->InvalidateCache();
+    if (backoff_.failure_count() > kMaxResendOnUnauthenticatedCount) {
+      if (on_auth_error_) {
+        std::move(on_auth_error_).Run();
+      }
+      return;
+    }
+  }
+
   // Calculate delay before sending the next message.
   base::TimeDelta delay;
   // See CoreErrorDomainTranslator.java for the mapping
@@ -310,8 +325,9 @@
     case grpc::StatusCode::NOT_FOUND:
       delay = kResendDelayOnHostNotFound;
       break;
-    // TODO(yuweih): Handle sequence ID mismatch case. This is not implemented
-    // in the server yet.
+    case grpc::StatusCode::UNAUTHENTICATED:
+      delay = kResendDelayOnUnauthenticated;
+      break;
     default:
       delay = backoff_.GetTimeUntilRelease();
       LOG(ERROR) << "Heartbeat failed due to unexpected error: "
diff --git a/remoting/host/heartbeat_sender.h b/remoting/host/heartbeat_sender.h
index fcacf5b5..fb1bfac 100644
--- a/remoting/host/heartbeat_sender.h
+++ b/remoting/host/heartbeat_sender.h
@@ -70,8 +70,12 @@
   //
   // |on_unknown_host_id_error| is invoked when the host ID is permanently not
   // recognized by the server.
+  //
+  // |on_auth_error| is invoked when the heartbeat sender permanently fails to
+  // authenticate the requests.
   HeartbeatSender(base::OnceClosure on_heartbeat_successful_callback,
                   base::OnceClosure on_unknown_host_id_error,
+                  base::OnceClosure on_auth_error,
                   const std::string& host_id,
                   MuxingSignalStrategy* signal_strategy,
                   const scoped_refptr<const RsaKeyPair>& host_key_pair,
@@ -120,11 +124,13 @@
 
   base::OnceClosure on_heartbeat_successful_callback_;
   base::OnceClosure on_unknown_host_id_error_;
+  base::OnceClosure on_auth_error_;
   std::string host_id_;
   MuxingSignalStrategy* const signal_strategy_;
   scoped_refptr<const RsaKeyPair> host_key_pair_;
   std::unique_ptr<HeartbeatClient> client_;
   LogToServer* const log_to_server_;
+  OAuthTokenGetter* const oauth_token_getter_;
 
   base::OneShotTimer heartbeat_timer_;
 
diff --git a/remoting/host/heartbeat_sender_unittest.cc b/remoting/host/heartbeat_sender_unittest.cc
index ca06acd0..a2ece81 100644
--- a/remoting/host/heartbeat_sender_unittest.cc
+++ b/remoting/host/heartbeat_sender_unittest.cc
@@ -138,7 +138,8 @@
 
     heartbeat_sender_ = std::make_unique<HeartbeatSender>(
         mock_heartbeat_successful_callback_.Get(),
-        mock_unknown_host_id_error_callback_.Get(), kHostId,
+        mock_unknown_host_id_error_callback_.Get(),
+        mock_unauthenticated_error_callback_.Get(), kHostId,
         muxing_signal_strategy_.get(), key_pair, &oauth_token_getter_, this);
     heartbeat_sender_->SetGrpcChannelForTest(
         test_server_.CreateInProcessChannel());
@@ -160,6 +161,7 @@
 
   base::MockCallback<base::OnceClosure> mock_heartbeat_successful_callback_;
   base::MockCallback<base::OnceClosure> mock_unknown_host_id_error_callback_;
+  base::MockCallback<base::OnceClosure> mock_unauthenticated_error_callback_;
 
   std::vector<ServerLogEntry> received_log_entries_;
 
@@ -462,4 +464,34 @@
   ASSERT_EQ(0, GetBackoff().failure_count());
 }
 
+TEST_F(HeartbeatSenderTest, Unauthenticated) {
+  base::RunLoop run_loop;
+
+  int heartbeat_count = 0;
+  EXPECT_CALL(*test_server_, Heartbeat(_, _, _))
+      .WillRepeatedly([&](grpc::ServerContext*,
+                          const apis::v1::HeartbeatRequest* request,
+                          apis::v1::HeartbeatResponse* response) {
+        ValidateHeartbeat(*request, /* ftl */ true, /* xmpp */ true,
+                          kExpectedSequenceIdUnset);
+        heartbeat_count++;
+        return grpc::Status(grpc::StatusCode::UNAUTHENTICATED,
+                            "unauthenticated");
+      });
+
+  EXPECT_CALL(mock_unauthenticated_error_callback_, Run()).WillOnce([&]() {
+    run_loop.Quit();
+  });
+
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindLambdaForTesting([&]() {
+        ftl_signal_strategy_->Connect();
+        xmpp_signal_strategy_->Connect();
+      }));
+  run_loop.Run();
+
+  // Should retry heartbeating at least once.
+  ASSERT_LT(1, heartbeat_count);
+}
+
 }  // namespace remoting
diff --git a/remoting/host/installer/mac/BUILD.gn b/remoting/host/installer/mac/BUILD.gn
index 357b1f5..9949223 100644
--- a/remoting/host/installer/mac/BUILD.gn
+++ b/remoting/host/installer/mac/BUILD.gn
@@ -137,7 +137,6 @@
 
   deps = [
     ":remoting_host_uninstaller_resources",
-    ":remoting_host_uninstaller_xibs",
     "//base",
     "//remoting/host:remoting_infoplist_strings",
     "//remoting/host/mac:constants",
@@ -148,12 +147,6 @@
   }
 }
 
-mac_xib_bundle_data("remoting_host_uninstaller_xibs") {
-  sources = [
-    "uninstaller/remoting_uninstaller.xib",
-  ]
-}
-
 bundle_data("remoting_host_uninstaller_resources") {
   sources = [
     "uninstaller/remoting_uninstaller.icns",
diff --git a/remoting/host/installer/mac/uninstaller/remoting_uninstaller-Info.plist b/remoting/host/installer/mac/uninstaller/remoting_uninstaller-Info.plist
index 864a4ca..158bdf7 100644
--- a/remoting/host/installer/mac/uninstaller/remoting_uninstaller-Info.plist
+++ b/remoting/host/installer/mac/uninstaller/remoting_uninstaller-Info.plist
@@ -22,8 +22,6 @@
 	<string>${VERSION_FULL}</string>
 	<key>LSMinimumSystemVersion</key>
 	<string>${MACOSX_DEPLOYMENT_TARGET}</string>
-	<key>NSMainNibFile</key>
-	<string>remoting_uninstaller</string>
 	<key>NSPrincipalClass</key>
 	<string>NSApplication</string>
 </dict>
diff --git a/remoting/host/installer/mac/uninstaller/remoting_uninstaller.xib b/remoting/host/installer/mac/uninstaller/remoting_uninstaller.xib
deleted file mode 100644
index 71e2b81..0000000
--- a/remoting/host/installer/mac/uninstaller/remoting_uninstaller.xib
+++ /dev/null
@@ -1,169 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="5056" systemVersion="13F1077" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
-    <dependencies>
-        <deployment version="1060" identifier="macosx"/>
-        <development version="5100" identifier="xcode"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="5056"/>
-    </dependencies>
-    <objects>
-        <customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
-            <connections>
-                <outlet property="delegate" destination="494" id="495"/>
-            </connections>
-        </customObject>
-        <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
-        <customObject id="-3" userLabel="Application"/>
-        <menu title="AMainMenu" systemMenu="main" id="29">
-            <items>
-                <menuItem title="Chrome Remote Desktop Host Uninstaller" id="56">
-                    <menu key="submenu" title="Chrome Remote Desktop Host Uninstaller" systemMenu="apple" id="57">
-                        <items>
-                            <menuItem title="About Chrome Remote Desktop Uninstaller" id="58">
-                                <modifierMask key="keyEquivalentModifierMask"/>
-                                <connections>
-                                    <action selector="orderFrontStandardAboutPanel:" target="-2" id="142"/>
-                                </connections>
-                            </menuItem>
-                            <menuItem isSeparatorItem="YES" id="236">
-                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
-                            </menuItem>
-                            <menuItem title="Preferences…" keyEquivalent="," id="129"/>
-                            <menuItem isSeparatorItem="YES" id="143">
-                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
-                            </menuItem>
-                            <menuItem title="Services" id="131">
-                                <menu key="submenu" title="Services" systemMenu="services" id="130"/>
-                            </menuItem>
-                            <menuItem isSeparatorItem="YES" id="144">
-                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
-                            </menuItem>
-                            <menuItem title="Hide Chrome Remote Desktop Uninstaller" keyEquivalent="h" id="134">
-                                <connections>
-                                    <action selector="hide:" target="-1" id="367"/>
-                                </connections>
-                            </menuItem>
-                            <menuItem title="Hide Others" keyEquivalent="h" id="145">
-                                <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
-                                <connections>
-                                    <action selector="hideOtherApplications:" target="-1" id="368"/>
-                                </connections>
-                            </menuItem>
-                            <menuItem title="Show All" id="150">
-                                <connections>
-                                    <action selector="unhideAllApplications:" target="-1" id="370"/>
-                                </connections>
-                            </menuItem>
-                            <menuItem isSeparatorItem="YES" id="149">
-                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
-                            </menuItem>
-                            <menuItem title="Quit Chrome Remote Desktop Uninstaller" keyEquivalent="q" id="136">
-                                <connections>
-                                    <action selector="terminate:" target="-3" id="449"/>
-                                </connections>
-                            </menuItem>
-                        </items>
-                    </menu>
-                </menuItem>
-                <menuItem title="File" id="83">
-                    <menu key="submenu" title="File" id="81">
-                        <items>
-                            <menuItem title="New" keyEquivalent="n" id="82"/>
-                            <menuItem title="Open…" keyEquivalent="o" id="72"/>
-                            <menuItem isSeparatorItem="YES" id="79">
-                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
-                            </menuItem>
-                            <menuItem title="Close" keyEquivalent="w" id="73">
-                                <connections>
-                                    <action selector="handleMenuClose:" target="494" id="544"/>
-                                </connections>
-                            </menuItem>
-                        </items>
-                    </menu>
-                </menuItem>
-                <menuItem title="Edit" id="217">
-                    <menu key="submenu" title="Edit" id="205">
-                        <items>
-                            <menuItem title="Undo" keyEquivalent="z" id="207"/>
-                            <menuItem title="Redo" keyEquivalent="Z" id="215">
-                                <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
-                            </menuItem>
-                            <menuItem isSeparatorItem="YES" id="206">
-                                <modifierMask key="keyEquivalentModifierMask" command="YES"/>
-                            </menuItem>
-                            <menuItem title="Cut" keyEquivalent="x" id="199"/>
-                            <menuItem title="Copy" keyEquivalent="c" id="197"/>
-                            <menuItem title="Paste" keyEquivalent="v" id="203"/>
-                        </items>
-                    </menu>
-                </menuItem>
-                <menuItem title="Help" id="490">
-                    <modifierMask key="keyEquivalentModifierMask"/>
-                    <menu key="submenu" title="Help" systemMenu="help" id="491">
-                        <items>
-                            <menuItem title="Chrome Remote Desktop Uninstaller Help" keyEquivalent="?" id="492">
-                                <connections>
-                                    <action selector="showHelp:" target="-1" id="493"/>
-                                </connections>
-                            </menuItem>
-                        </items>
-                    </menu>
-                </menuItem>
-            </items>
-        </menu>
-        <window title="Chrome Remote Desktop Uninstaller" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" showsToolbarButton="NO" animationBehavior="default" id="371">
-            <windowStyleMask key="styleMask" titled="YES" closable="YES"/>
-            <rect key="contentRect" x="335" y="863" width="499" height="112"/>
-            <rect key="screenRect" x="0.0" y="0.0" width="2560" height="1578"/>
-            <view key="contentView" id="372">
-                <rect key="frame" x="0.0" y="0.0" width="499" height="112"/>
-                <autoresizingMask key="autoresizingMask"/>
-                <subviews>
-                    <textField verticalHuggingPriority="750" id="533">
-                        <rect key="frame" x="103" y="75" width="379" height="17"/>
-                        <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
-                        <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="This will completely remove Chrome Remote Desktop Host." id="534">
-                            <font key="font" metaFont="system"/>
-                            <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
-                            <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
-                        </textFieldCell>
-                    </textField>
-                    <button verticalHuggingPriority="750" id="537">
-                        <rect key="frame" x="390" y="13" width="95" height="32"/>
-                        <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
-                        <buttonCell key="cell" type="push" title="Uninstall" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="538">
-                            <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
-                            <font key="font" metaFont="system"/>
-                        </buttonCell>
-                        <connections>
-                            <action selector="uninstall:" target="494" id="541"/>
-                        </connections>
-                    </button>
-                    <button verticalHuggingPriority="750" id="539">
-                        <rect key="frame" x="308" y="13" width="82" height="32"/>
-                        <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
-                        <buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="540">
-                            <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
-                            <font key="font" metaFont="system"/>
-                            <string key="keyEquivalent" base64-UTF8="YES">
-Gw
-</string>
-                        </buttonCell>
-                        <connections>
-                            <action selector="cancel:" target="494" id="542"/>
-                        </connections>
-                    </button>
-                    <imageView id="548">
-                        <rect key="frame" x="8" y="24" width="76" height="68"/>
-                        <autoresizingMask key="autoresizingMask"/>
-                        <imageCell key="cell" refusesFirstResponder="YES" alignment="left" animates="YES" imageScaling="proportionallyDown" image="NSApplicationIcon" id="549"/>
-                    </imageView>
-                </subviews>
-            </view>
-        </window>
-        <customObject id="494" customClass="RemotingUninstallerAppDelegate"/>
-        <customObject id="420" customClass="NSFontManager"/>
-    </objects>
-    <resources>
-        <image name="NSApplicationIcon" width="128" height="128"/>
-    </resources>
-</document>
diff --git a/remoting/host/installer/mac/uninstaller/remoting_uninstaller_app.mm b/remoting/host/installer/mac/uninstaller/remoting_uninstaller_app.mm
index 7724eb5c..ec4b931 100644
--- a/remoting/host/installer/mac/uninstaller/remoting_uninstaller_app.mm
+++ b/remoting/host/installer/mac/uninstaller/remoting_uninstaller_app.mm
@@ -11,22 +11,100 @@
 #include "remoting/host/installer/mac/uninstaller/remoting_uninstaller.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 
+namespace {
+
+base::scoped_nsobject<NSMenu> BuildMainMenu() {
+  base::scoped_nsobject<NSMenu> main_menu([[NSMenu alloc] initWithTitle:@""]);
+
+  NSMenuItem* item = [[[NSMenuItem alloc] initWithTitle:@""
+                                                 action:NULL
+                                          keyEquivalent:@""] autorelease];
+
+  // The title is not used, as the title will always be the name of the App.
+  item.submenu = [[[NSMenu alloc] initWithTitle:@""] autorelease];
+  [item.submenu addItem:[[[NSMenuItem alloc] initWithTitle:@"Close"
+                                                    action:@selector(terminate:)
+                                             keyEquivalent:@"w"] autorelease]];
+  [item.submenu
+      addItem:[[[NSMenuItem alloc]
+                  initWithTitle:@"Quit Chrome Remote Desktop Uninstaller"
+                         action:@selector(terminate:)
+                  keyEquivalent:@"q"] autorelease]];
+  [main_menu addItem:item];
+
+  return main_menu;
+}
+
+}  // namespace
+
+@interface RemotingUninstallerAppDelegate () <NSApplicationDelegate>
+@property(nonatomic, retain) NSWindow* window;
+@end
+
 @implementation RemotingUninstallerAppDelegate
+@synthesize window = _window;
 
 - (void)dealloc {
   [super dealloc];
 }
 
 - (void)applicationDidFinishLaunching:(NSNotification*)aNotification {
+  base::scoped_nsobject<NSMenu> main_menu = BuildMainMenu();
+  [[NSApplication sharedApplication] setMainMenu:main_menu];
+
+  NSRect frame = NSMakeRect(0, 0, 499, 112);
+  self.window = [[[NSWindow alloc] initWithContentRect:frame
+                                             styleMask:NSTitledWindowMask
+                                               backing:NSBackingStoreBuffered
+                                                 defer:NO] autorelease];
+  self.window.title = @"Chrome Remote Desktop Uninstaller";
+
+  NSTextField* title = [[[NSTextField alloc]
+      initWithFrame:NSMakeRect(103, 75, 379, 17)] autorelease];
+  title.stringValue =
+      @"This will completely remove Chrome Remote Desktop Host.";
+  title.drawsBackground = NO;
+  title.bezeled = NO;
+  title.editable = NO;
+  title.font = [NSFont systemFontOfSize:13];
+  [self.window.contentView addSubview:title];
+
+  NSImageView* icon = [[[NSImageView alloc]
+      initWithFrame:NSMakeRect(8, 24, 76, 68)] autorelease];
+  icon.image = [[NSApplication sharedApplication] applicationIconImage];
+  [self.window.contentView addSubview:icon];
+
+  NSButton* cancelButton = [[[NSButton alloc]
+      initWithFrame:NSMakeRect(308, 13, 82, 32)] autorelease];
+  cancelButton.buttonType = NSButtonTypeMomentaryPushIn;
+  cancelButton.bezelStyle = NSRoundedBezelStyle;
+  cancelButton.title = @"Cancel";
+  cancelButton.action = @selector(cancel:);
+  cancelButton.target = self;
+  [self.window.contentView addSubview:cancelButton];
+
+  NSButton* uninstallButton = [[[NSButton alloc]
+      initWithFrame:NSMakeRect(390, 13, 95, 32)] autorelease];
+  uninstallButton.buttonType = NSButtonTypeMomentaryPushIn;
+  uninstallButton.bezelStyle = NSRoundedBezelStyle;
+  uninstallButton.title = @"Uninstall";
+  uninstallButton.action = @selector(uninstall:);
+  uninstallButton.target = self;
+  [self.window.contentView addSubview:uninstallButton];
+
+  [self.window makeKeyAndOrderFront:NSApp];
+  [self.window center];
 }
 
-- (void)showSuccess:(bool)success withMessage:(NSString*) message {
+- (void)showSuccess:(bool)success withMessage:(NSString*)message {
   NSString* summary = success ? @"Uninstall succeeded" : @"Uninstall failed";
   base::scoped_nsobject<NSAlert> alert([[NSAlert alloc] init]);
   [alert setMessageText:summary];
   [alert setInformativeText:message];
   [alert setAlertStyle:(success ? NSInformationalAlertStyle
                                 : NSCriticalAlertStyle)];
+  // This line crashes the app because ui::ResourceBundle::GetSharedInstance()
+  // cannot find a shared instance. https://crbug.com/968257.
   [alert addButtonWithTitle:l10n_util::GetNSString(IDS_OK)];
   [alert runModal];
 }
@@ -53,14 +131,13 @@
     } else {
       [NSException raise:@"AuthorizationCopyRights Failure"
                   format:@"Error during AuthorizationCopyRights status=%d",
-                             static_cast<int>(status)];
+                         static_cast<int>(status)];
     }
     if (message != nullptr) {
       NSLog(@"Uninstall %s: %@", success ? "succeeded" : "failed", message);
       [self showSuccess:success withMessage:message];
     }
-  }
-  @catch (NSException* exception) {
+  } @catch (NSException* exception) {
     NSLog(@"Exception %@ %@", [exception name], [exception reason]);
     NSString* message =
         @"Error! Unable to uninstall Chrome Remote Desktop Host.";
@@ -80,13 +157,12 @@
 
 @end
 
-int main(int argc, char* argv[])
-{
+int main(int argc, char* argv[]) {
   // The no-ui option skips the UI confirmation dialogs. This is provided as
   // a convenience for our automated testing.
   // There will still be an elevation prompt unless the command is run as root.
-  if (argc == 2 && !strcmp(argv[1], "--no-ui")) {
-    @autoreleasepool {
+  @autoreleasepool {
+    if (argc == 2 && !strcmp(argv[1], "--no-ui")) {
       NSLog(@"Chrome Remote Desktop uninstall starting.");
       NSLog(@"--no-ui : Suppressing UI");
 
@@ -97,9 +173,16 @@
       NSLog(@"Chrome Remote Desktop Host uninstall complete.");
       NSLog(@"Status = %d", static_cast<int>(status));
       return status != errAuthorizationSuccess;
+    } else {
+      RemotingUninstallerAppDelegate* delegate =
+          [[[RemotingUninstallerAppDelegate alloc] init] autorelease];
+
+      [NSApplication sharedApplication];
+      [NSApp setDelegate:delegate];
+      [NSApp activateIgnoringOtherApps:YES];
+      [NSApp run];
+
+      return EXIT_SUCCESS;
     }
-  } else {
-    return NSApplicationMain(argc, (const char**)argv);
   }
 }
-
diff --git a/remoting/host/it2me/BUILD.gn b/remoting/host/it2me/BUILD.gn
index 39dc4673..473ce933 100644
--- a/remoting/host/it2me/BUILD.gn
+++ b/remoting/host/it2me/BUILD.gn
@@ -170,12 +170,6 @@
         }
       }
 
-      mac_xib_bundle_data("remote_assistance_host_xibs") {
-        sources = [
-          "//remoting/host/mac/disconnect_window.xib",
-        ]
-      }
-
       if (icu_use_data_file) {
         bundle_data("remote_assistance_host_resources") {
           sources = [
@@ -238,7 +232,6 @@
         }
         deps += [
           ":remote_assistance_host_dialog_icon",
-          ":remote_assistance_host_xibs",
           "//remoting/host:remoting_infoplist_strings",
           "//remoting/resources:copy_locales",
         ]
diff --git a/remoting/host/mac/BUILD.gn b/remoting/host/mac/BUILD.gn
index 780de15..ba7dfe8 100644
--- a/remoting/host/mac/BUILD.gn
+++ b/remoting/host/mac/BUILD.gn
@@ -32,12 +32,6 @@
   }
 }
 
-mac_xib_bundle_data("remoting_host_xibs") {
-  sources = [
-    "disconnect_window.xib",
-  ]
-}
-
 bundle_data("remoting_host_resources") {
   sources = [
     "$root_gen_dir/remoting/CREDITS.txt",
@@ -94,7 +88,6 @@
   }
   deps += [
     ":remoting_host_resources",
-    ":remoting_host_xibs",
     "//remoting/host:remoting_infoplist_strings",
     "//remoting/resources:copy_locales",
   ]
diff --git a/remoting/host/mac/disconnect_window.xib b/remoting/host/mac/disconnect_window.xib
deleted file mode 100644
index 9791a5fe..0000000
--- a/remoting/host/mac/disconnect_window.xib
+++ /dev/null
@@ -1,54 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="5056" systemVersion="13F1077" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
-    <dependencies>
-        <deployment version="1060" identifier="macosx"/>
-        <development version="5100" identifier="xcode"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="5056"/>
-    </dependencies>
-    <objects>
-        <customObject id="-2" userLabel="File's Owner" customClass="DisconnectWindowController">
-            <connections>
-                <outlet property="connectedToField_" destination="23" id="25"/>
-                <outlet property="disconnectButton_" destination="13" id="26"/>
-                <outlet property="window" destination="3" id="17"/>
-            </connections>
-        </customObject>
-        <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
-        <customObject id="-3" userLabel="Application"/>
-        <window title="Remoting" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" visibleAtLaunch="NO" animationBehavior="default" id="3" customClass="DisconnectWindow">
-            <windowStyleMask key="styleMask" titled="YES" utility="YES"/>
-            <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
-            <rect key="contentRect" x="247" y="517" width="466" height="40"/>
-            <rect key="screenRect" x="0.0" y="0.0" width="2560" height="1578"/>
-            <view key="contentView" id="4" customClass="DisconnectView">
-                <rect key="frame" x="0.0" y="0.0" width="466" height="40"/>
-                <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
-                <subviews>
-                    <button id="13">
-                        <rect key="frame" x="271" y="9" width="182" height="22"/>
-                        <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
-                        <buttonCell key="cell" type="square" title="&lt;disconnect>" bezelStyle="shadowlessSquare" alignment="center" controlSize="small" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="14">
-                            <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
-                            <font key="font" metaFont="smallSystem"/>
-                        </buttonCell>
-                        <connections>
-                            <action selector="stopSharing:" target="-2" id="18"/>
-                        </connections>
-                    </button>
-                    <textField verticalHuggingPriority="750" id="23">
-                        <rect key="frame" x="26" y="13" width="240" height="14"/>
-                        <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
-                        <textFieldCell key="cell" controlSize="small" truncatesLastVisibleLine="YES" enabled="NO" sendsActionOnEndEditing="YES" title="&lt;connected-to>" id="24">
-                            <font key="font" metaFont="smallSystem"/>
-                            <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
-                            <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
-                        </textFieldCell>
-                    </textField>
-                </subviews>
-            </view>
-            <connections>
-                <outlet property="delegate" destination="-2" id="22"/>
-            </connections>
-        </window>
-    </objects>
-</document>
diff --git a/remoting/host/remoting_me2me_host.cc b/remoting/host/remoting_me2me_host.cc
index acab392..59bdbd6 100644
--- a/remoting/host/remoting_me2me_host.cc
+++ b/remoting/host/remoting_me2me_host.cc
@@ -1510,6 +1510,7 @@
                        base::Unretained(this)),
         base::BindOnce(&HostProcess::OnUnknownHostIdError,
                        base::Unretained(this)),
+        base::BindOnce(&HostProcess::OnAuthFailed, base::Unretained(this)),
         host_id_, muxing_signal_strategy.get(), key_pair_,
         oauth_token_getter_.get(), log_to_server_.get());
     ftl_host_change_notification_listener_ =
diff --git a/services/BUILD.gn b/services/BUILD.gn
index 773980e..d3e9aaf 100644
--- a/services/BUILD.gn
+++ b/services/BUILD.gn
@@ -76,6 +76,11 @@
     # standalone services must be added here.
     deps += [ "//services/video_capture:tests" ]
   }
+
+  if (is_fuchsia) {
+    # Enable the test HTTP server for use by NetworkServiceTestWithService.*
+    use_test_server = true
+  }
 }
 
 if (!is_ios) {
diff --git a/services/network/http_cache_data_counter_unittest.cc b/services/network/http_cache_data_counter_unittest.cc
index b82764e9..0f4a804 100644
--- a/services/network/http_cache_data_counter_unittest.cc
+++ b/services/network/http_cache_data_counter_unittest.cc
@@ -215,7 +215,8 @@
 
 // Return the sensible thing (0 bytes used) when there is no cache.
 TEST(HttpCacheDataCounterTestNoCache, BeSensible) {
-  base::test::ScopedTaskEnvironment scoped_task_environment;
+  base::test::ScopedTaskEnvironment scoped_task_environment(
+      base::test::ScopedTaskEnvironment::MainThreadType::IO);
   std::unique_ptr<NetworkService> network_service(
       NetworkService::CreateForTesting());
   std::unique_ptr<NetworkContext> network_context;
diff --git a/services/network/public/cpp/network_quality_tracker_unittest.cc b/services/network/public/cpp/network_quality_tracker_unittest.cc
index 9029ddf..e0e5eb74 100644
--- a/services/network/public/cpp/network_quality_tracker_unittest.cc
+++ b/services/network/public/cpp/network_quality_tracker_unittest.cc
@@ -166,7 +166,9 @@
 
 class NetworkQualityTrackerTest : public testing::Test {
  public:
-  NetworkQualityTrackerTest() {
+  NetworkQualityTrackerTest()
+      : scoped_task_environment_(
+            base::test::ScopedTaskEnvironment::MainThreadType::IO) {
     network::mojom::NetworkServicePtr network_service_ptr;
     network::mojom::NetworkServiceRequest network_service_request =
         mojo::MakeRequest(&network_service_ptr);
diff --git a/services/test/run_all_unittests.cc b/services/test/run_all_unittests.cc
index 864c7183..12e1abc99 100644
--- a/services/test/run_all_unittests.cc
+++ b/services/test/run_all_unittests.cc
@@ -49,7 +49,7 @@
 #if defined(OS_ANDROID)
     ASSERT_TRUE(base::PathService::Get(ui::DIR_RESOURCE_PAKS_ANDROID, &path));
 #else
-    ASSERT_TRUE(base::PathService::Get(base::DIR_MODULE, &path));
+    ASSERT_TRUE(base::PathService::Get(base::DIR_ASSETS, &path));
 #endif
     base::FilePath bluetooth_test_strings =
         path.Append(FILE_PATH_LITERAL("bluetooth_test_strings.pak"));
diff --git a/services/tracing/perfetto/consumer_host.cc b/services/tracing/perfetto/consumer_host.cc
index a8b515f..974dbad 100644
--- a/services/tracing/perfetto/consumer_host.cc
+++ b/services/tracing/perfetto/consumer_host.cc
@@ -24,10 +24,10 @@
 #include "services/tracing/perfetto/perfetto_service.h"
 #include "services/tracing/perfetto/track_event_json_exporter.h"
 #include "services/tracing/public/cpp/trace_event_args_whitelist.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/observable_events.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/trace_config.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/trace_packet.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/trace_stats.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/observable_events.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_config.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_packet.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_stats.h"
 #include "third_party/perfetto/protos/perfetto/config/trace_config.pb.h"
 
 namespace tracing {
diff --git a/services/tracing/perfetto/consumer_host.h b/services/tracing/perfetto/consumer_host.h
index afb26770..7cf3cb2 100644
--- a/services/tracing/perfetto/consumer_host.h
+++ b/services/tracing/perfetto/consumer_host.h
@@ -16,8 +16,8 @@
 #include "base/timer/timer.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "services/tracing/public/mojom/perfetto_service.mojom.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/consumer.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/tracing_service.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/consumer.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/tracing_service.h"
 
 namespace service_manager {
 struct BindSourceInfo;
diff --git a/services/tracing/perfetto/consumer_host_unittest.cc b/services/tracing/perfetto/consumer_host_unittest.cc
index 2670a78d..f3cd37dd 100644
--- a/services/tracing/perfetto/consumer_host_unittest.cc
+++ b/services/tracing/perfetto/consumer_host_unittest.cc
@@ -25,8 +25,8 @@
 #include "services/tracing/perfetto/perfetto_service.h"
 #include "services/tracing/perfetto/test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/trace_config.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/trace_packet.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_config.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_packet.h"
 #include "third_party/perfetto/protos/perfetto/config/trace_config.pb.h"
 #include "third_party/perfetto/protos/perfetto/trace/trace.pb.h"
 #include "third_party/perfetto/protos/perfetto/trace/trace_packet.pb.h"
diff --git a/services/tracing/perfetto/json_exporter_main.cc b/services/tracing/perfetto/json_exporter_main.cc
index ff2ba4d..50057ff6 100644
--- a/services/tracing/perfetto/json_exporter_main.cc
+++ b/services/tracing/perfetto/json_exporter_main.cc
@@ -10,7 +10,7 @@
 #include "base/logging.h"
 #include "services/tracing/perfetto/json_trace_exporter.h"
 #include "services/tracing/perfetto/track_event_json_exporter.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/trace_packet.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_packet.h"
 #include "third_party/perfetto/protos/perfetto/trace/trace.pbzero.h"
 
 // Tool to convert a given proto trace into json.
diff --git a/services/tracing/perfetto/json_trace_exporter.cc b/services/tracing/perfetto/json_trace_exporter.cc
index cf6dc4c..0b9d58e 100644
--- a/services/tracing/perfetto/json_trace_exporter.cc
+++ b/services/tracing/perfetto/json_trace_exporter.cc
@@ -11,7 +11,7 @@
 #include "base/json/json_writer.h"
 #include "base/json/string_escape.h"
 #include "base/trace_event/trace_event.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/trace_packet.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_packet.h"
 #include "third_party/perfetto/protos/perfetto/trace/chrome/chrome_trace_event.pbzero.h"
 #include "third_party/perfetto/protos/perfetto/trace/chrome/chrome_trace_packet.pb.h"
 
diff --git a/services/tracing/perfetto/json_trace_exporter.h b/services/tracing/perfetto/json_trace_exporter.h
index b682dd2e..596d778d 100644
--- a/services/tracing/perfetto/json_trace_exporter.h
+++ b/services/tracing/perfetto/json_trace_exporter.h
@@ -15,7 +15,7 @@
 #include "base/macros.h"
 #include "base/strings/stringprintf.h"
 #include "base/values.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/trace_packet.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_packet.h"
 
 namespace perfetto {
 namespace protos {
diff --git a/services/tracing/perfetto/json_trace_exporter_unittest.cc b/services/tracing/perfetto/json_trace_exporter_unittest.cc
index 9da5bb20..15a3bc9 100644
--- a/services/tracing/perfetto/json_trace_exporter_unittest.cc
+++ b/services/tracing/perfetto/json_trace_exporter_unittest.cc
@@ -20,8 +20,8 @@
 #include "base/values.h"
 #include "services/tracing/public/mojom/perfetto_service.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/trace_config.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/trace_packet.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_config.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_packet.h"
 #include "third_party/perfetto/protos/perfetto/trace/chrome/chrome_trace_event.pb.h"
 #include "third_party/perfetto/protos/perfetto/trace/trace_packet.pb.h"
 
diff --git a/services/tracing/perfetto/perfetto_service.cc b/services/tracing/perfetto/perfetto_service.cc
index 980aec9..b521b04c 100644
--- a/services/tracing/perfetto/perfetto_service.cc
+++ b/services/tracing/perfetto/perfetto_service.cc
@@ -15,7 +15,7 @@
 #include "services/tracing/perfetto/consumer_host.h"
 #include "services/tracing/perfetto/producer_host.h"
 #include "services/tracing/public/cpp/perfetto/shared_memory.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/tracing_service.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/tracing_service.h"
 
 namespace tracing {
 
diff --git a/services/tracing/perfetto/perfetto_tracing_coordinator.cc b/services/tracing/perfetto/perfetto_tracing_coordinator.cc
index f12f6a1..62204988 100644
--- a/services/tracing/perfetto/perfetto_tracing_coordinator.cc
+++ b/services/tracing/perfetto/perfetto_tracing_coordinator.cc
@@ -24,10 +24,10 @@
 #include "services/tracing/public/cpp/trace_event_args_whitelist.h"
 #include "services/tracing/public/mojom/constants.mojom.h"
 #include "services/tracing/public/mojom/perfetto_service.mojom.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/consumer.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/trace_config.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/trace_stats.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/tracing_service.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/consumer.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_config.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_stats.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/tracing_service.h"
 
 namespace tracing {
 
diff --git a/services/tracing/perfetto/producer_host.cc b/services/tracing/perfetto/producer_host.cc
index a8150a51..8fdd6e6 100644
--- a/services/tracing/perfetto/producer_host.cc
+++ b/services/tracing/perfetto/producer_host.cc
@@ -12,9 +12,9 @@
 #include "services/tracing/public/cpp/perfetto/producer_client.h"
 #include "services/tracing/public/cpp/perfetto/shared_memory.h"
 #include "services/tracing/public/cpp/tracing_features.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/commit_data_request.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/data_source_descriptor.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/trace_config.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/commit_data_request.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/data_source_descriptor.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_config.h"
 
 namespace tracing {
 
diff --git a/services/tracing/perfetto/producer_host.h b/services/tracing/perfetto/producer_host.h
index 9fe4616..91bb082 100644
--- a/services/tracing/perfetto/producer_host.h
+++ b/services/tracing/perfetto/producer_host.h
@@ -13,8 +13,8 @@
 #include "base/macros.h"
 #include "services/tracing/perfetto/producer_host.h"
 #include "services/tracing/public/mojom/perfetto_service.mojom.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/producer.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/tracing_service.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/producer.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/tracing_service.h"
 
 namespace perfetto {
 class CommitDataRequest;
diff --git a/services/tracing/perfetto/test_utils.cc b/services/tracing/perfetto/test_utils.cc
index 9ba987d9..b50d399 100644
--- a/services/tracing/perfetto/test_utils.cc
+++ b/services/tracing/perfetto/test_utils.cc
@@ -8,10 +8,10 @@
 #include "base/bind.h"
 #include "base/run_loop.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/commit_data_request.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/trace_config.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/trace_packet.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/trace_writer.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/commit_data_request.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_config.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_packet.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_writer.h"
 #include "third_party/perfetto/protos/perfetto/common/commit_data_request.pb.h"
 #include "third_party/perfetto/protos/perfetto/trace/test_event.pbzero.h"
 #include "third_party/perfetto/protos/perfetto/trace/trace_packet.pb.h"
diff --git a/services/tracing/perfetto/test_utils.h b/services/tracing/perfetto/test_utils.h
index 4838507..37ef3f8 100644
--- a/services/tracing/perfetto/test_utils.h
+++ b/services/tracing/perfetto/test_utils.h
@@ -13,7 +13,7 @@
 #include "services/tracing/perfetto/producer_host.h"
 #include "services/tracing/public/cpp/perfetto/perfetto_traced_process.h"
 #include "services/tracing/public/cpp/perfetto/producer_client.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/consumer.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/consumer.h"
 
 namespace tracing {
 
diff --git a/services/tracing/perfetto/track_event_json_exporter.cc b/services/tracing/perfetto/track_event_json_exporter.cc
index 01fbca4..8b18c30 100644
--- a/services/tracing/perfetto/track_event_json_exporter.cc
+++ b/services/tracing/perfetto/track_event_json_exporter.cc
@@ -316,13 +316,15 @@
   switch (thread.chrome_thread_type()) {
     // TODO(nuskos): As we add more thread types we will add handling here to
     // switch the enum to a string and call |emit_thread_name()|
-    case perfetto::protos::ThreadDescriptor::THREAD_UNSPECIFIED:
+    case perfetto::protos::ThreadDescriptor::THREAD_TYPE_UNSPECIFIED:
       // No thread type enum so check to see if a explicit thread name was
       // provided..
       if (thread.has_thread_name()) {
         emit_thread_name(thread.thread_name().c_str());
       }
       break;
+    default:
+      break;
   }
 }
 
diff --git a/services/tracing/perfetto/track_event_json_exporter_unittest.cc b/services/tracing/perfetto/track_event_json_exporter_unittest.cc
index 6524eec..fd3c5a280 100644
--- a/services/tracing/perfetto/track_event_json_exporter_unittest.cc
+++ b/services/tracing/perfetto/track_event_json_exporter_unittest.cc
@@ -18,8 +18,8 @@
 #include "base/values.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/trace_config.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/trace_packet.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_config.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_packet.h"
 #include "third_party/perfetto/protos/perfetto/trace/chrome/chrome_trace_event.pbzero.h"
 #include "third_party/perfetto/protos/perfetto/trace/trace_packet.pb.h"
 
@@ -494,9 +494,9 @@
   std::vector<perfetto::protos::TracePacket> trace_packet_protos;
   trace_analyzer::TraceEventVector events;
   AddThreadDescriptorPacket(/* sort_index = */ base::nullopt,
-                            ThreadDescriptor::THREAD_UNSPECIFIED, base::nullopt,
-                            kReferenceTimeUs, kReferenceThreadTimeUs,
-                            &trace_packet_protos);
+                            ThreadDescriptor::THREAD_TYPE_UNSPECIFIED,
+                            base::nullopt, kReferenceTimeUs,
+                            kReferenceThreadTimeUs, &trace_packet_protos);
   FinalizePackets(trace_packet_protos);
   // No traceEvents or data was emitted but a thread descriptor should be an
   // empty array and not cause crashes.
@@ -507,8 +507,9 @@
   std::vector<perfetto::protos::TracePacket> trace_packet_protos;
   trace_analyzer::TraceEventVector events;
   AddThreadDescriptorPacket(
-      /* sort_index = */ 2, ThreadDescriptor::THREAD_UNSPECIFIED, base::nullopt,
-      kReferenceTimeUs, kReferenceThreadTimeUs, &trace_packet_protos);
+      /* sort_index = */ 2, ThreadDescriptor::THREAD_TYPE_UNSPECIFIED,
+      base::nullopt, kReferenceTimeUs, kReferenceThreadTimeUs,
+      &trace_packet_protos);
   FinalizePackets(trace_packet_protos);
   ASSERT_EQ(1u,
             trace_analyzer()->FindEvents(
@@ -526,9 +527,9 @@
   std::vector<perfetto::protos::TracePacket> trace_packet_protos;
   trace_analyzer::TraceEventVector events;
   AddThreadDescriptorPacket(/* sort_index = */ base::nullopt,
-                            ThreadDescriptor::THREAD_UNSPECIFIED, kThreadName,
-                            kReferenceTimeUs, kReferenceThreadTimeUs,
-                            &trace_packet_protos);
+                            ThreadDescriptor::THREAD_TYPE_UNSPECIFIED,
+                            kThreadName, kReferenceTimeUs,
+                            kReferenceThreadTimeUs, &trace_packet_protos);
   FinalizePackets(trace_packet_protos);
   ASSERT_EQ(1u, trace_analyzer()->FindEvents(
                     Query(Query::EVENT_NAME) == Query::String("thread_name"),
@@ -545,22 +546,23 @@
   std::vector<perfetto::protos::TracePacket> trace_packet_protos;
   trace_analyzer::TraceEventVector events;
   AddThreadDescriptorPacket(
-      /* sort_index = */ 2, ThreadDescriptor::THREAD_UNSPECIFIED, kThreadName,
-      kReferenceTimeUs, kReferenceThreadTimeUs, &trace_packet_protos);
+      /* sort_index = */ 2, ThreadDescriptor::THREAD_TYPE_UNSPECIFIED,
+      kThreadName, kReferenceTimeUs, kReferenceThreadTimeUs,
+      &trace_packet_protos);
   // This packet will be ignored because we've already emitted the sort_index of
   // 2 and thread_name kThreadName so we suppress this metadata because it
   // isn't supposed to have changed (even if reset).
   ASSERT_NE("different_thread_name", kThreadName);
   AddThreadDescriptorPacket(
-      /* sort_index = */ 3, ThreadDescriptor::THREAD_UNSPECIFIED,
+      /* sort_index = */ 3, ThreadDescriptor::THREAD_TYPE_UNSPECIFIED,
       "different_thread_name", kReferenceTimeUs, kReferenceThreadTimeUs,
       &trace_packet_protos);
   trace_packet_protos.back().set_incremental_state_cleared(true);
   // Empty packet doesn't change anything.
   AddThreadDescriptorPacket(/* sort_index = */ base::nullopt,
-                            ThreadDescriptor::THREAD_UNSPECIFIED, base::nullopt,
-                            kReferenceTimeUs, kReferenceThreadTimeUs,
-                            &trace_packet_protos);
+                            ThreadDescriptor::THREAD_TYPE_UNSPECIFIED,
+                            base::nullopt, kReferenceTimeUs,
+                            kReferenceThreadTimeUs, &trace_packet_protos);
   FinalizePackets(trace_packet_protos);
   ASSERT_EQ(2u, trace_analyzer()->FindEvents(
                     Query(Query::EVENT_CATEGORY) == Query::String("__metadata"),
@@ -697,7 +699,7 @@
 
   // This provides the pid & tid, as well as the timestamps reference points.
   AddThreadDescriptorPacket(/* sort_index = */ base::nullopt,
-                            ThreadDescriptor::THREAD_UNSPECIFIED,
+                            ThreadDescriptor::THREAD_TYPE_UNSPECIFIED,
                             /* thread_name = */ base::nullopt, kReferenceTimeUs,
                             kReferenceThreadTimeUs, &trace_packet_protos);
   // To correctly use the state the thread descriptor has to come
@@ -730,7 +732,7 @@
   trace_analyzer::TraceEventVector events;
 
   AddThreadDescriptorPacket(/* sort_index = */ base::nullopt,
-                            ThreadDescriptor::THREAD_UNSPECIFIED,
+                            ThreadDescriptor::THREAD_TYPE_UNSPECIFIED,
                             /* thread_name = */ base::nullopt, kReferenceTimeUs,
                             kReferenceThreadTimeUs, &trace_packet_protos);
   AddInternedLegacyEventName(3, "legacy_event_name_3", &trace_packet_protos);
@@ -768,7 +770,7 @@
   trace_analyzer::TraceEventVector events;
 
   AddThreadDescriptorPacket(/* sort_index = */ base::nullopt,
-                            ThreadDescriptor::THREAD_UNSPECIFIED,
+                            ThreadDescriptor::THREAD_TYPE_UNSPECIFIED,
                             /* thread_name = */ base::nullopt, kReferenceTimeUs,
                             kReferenceThreadTimeUs, &trace_packet_protos);
   AddInternedLegacyEventName(3, "legacy_event_name_3", &trace_packet_protos);
@@ -803,7 +805,7 @@
   trace_analyzer::TraceEventVector events;
 
   AddThreadDescriptorPacket(/* sort_index = */ base::nullopt,
-                            ThreadDescriptor::THREAD_UNSPECIFIED,
+                            ThreadDescriptor::THREAD_TYPE_UNSPECIFIED,
                             /* thread_name = */ base::nullopt, kReferenceTimeUs,
                             kReferenceThreadTimeUs, &trace_packet_protos);
   AddInternedLegacyEventName(3, "legacy_event_name_3", &trace_packet_protos);
@@ -833,7 +835,7 @@
   trace_analyzer::TraceEventVector events;
 
   AddThreadDescriptorPacket(/* sort_index = */ base::nullopt,
-                            ThreadDescriptor::THREAD_UNSPECIFIED,
+                            ThreadDescriptor::THREAD_TYPE_UNSPECIFIED,
                             /* thread_name = */ base::nullopt, kReferenceTimeUs,
                             kReferenceThreadTimeUs, &trace_packet_protos);
   AddInternedLegacyEventName(3, "legacy_event_name_3", &trace_packet_protos);
@@ -914,7 +916,7 @@
   trace_analyzer::TraceEventVector events;
 
   AddThreadDescriptorPacket(/* sort_index = */ base::nullopt,
-                            ThreadDescriptor::THREAD_UNSPECIFIED,
+                            ThreadDescriptor::THREAD_TYPE_UNSPECIFIED,
                             /* thread_name = */ base::nullopt, kReferenceTimeUs,
                             kReferenceThreadTimeUs, &trace_packet_protos);
   AddInternedLegacyEventName(3, "legacy_event_name_3", &trace_packet_protos);
@@ -953,7 +955,7 @@
   trace_analyzer::TraceEventVector events;
 
   AddThreadDescriptorPacket(/* sort_index = */ base::nullopt,
-                            ThreadDescriptor::THREAD_UNSPECIFIED,
+                            ThreadDescriptor::THREAD_TYPE_UNSPECIFIED,
                             /* thread_name = */ base::nullopt, kReferenceTimeUs,
                             kReferenceThreadTimeUs, &trace_packet_protos);
   AddInternedLegacyEventName(3, "legacy_event_name_3", &trace_packet_protos);
@@ -999,7 +1001,7 @@
   trace_analyzer::TraceEventVector events;
 
   AddThreadDescriptorPacket(/* sort_index = */ base::nullopt,
-                            ThreadDescriptor::THREAD_UNSPECIFIED,
+                            ThreadDescriptor::THREAD_TYPE_UNSPECIFIED,
                             /* thread_name = */ base::nullopt, kReferenceTimeUs,
                             kReferenceThreadTimeUs, &trace_packet_protos);
   AddInternedLegacyEventName(3, "legacy_event_name_3", &trace_packet_protos);
@@ -1038,7 +1040,7 @@
   trace_analyzer::TraceEventVector events;
 
   AddThreadDescriptorPacket(/* sort_index = */ base::nullopt,
-                            ThreadDescriptor::THREAD_UNSPECIFIED,
+                            ThreadDescriptor::THREAD_TYPE_UNSPECIFIED,
                             /* thread_name = */ base::nullopt, kReferenceTimeUs,
                             kReferenceThreadTimeUs, &trace_packet_protos);
   AddInternedLegacyEventName(3, "legacy_event_name_3", &trace_packet_protos);
@@ -1083,7 +1085,7 @@
   trace_analyzer::TraceEventVector events;
 
   AddThreadDescriptorPacket(/* sort_index = */ base::nullopt,
-                            ThreadDescriptor::THREAD_UNSPECIFIED,
+                            ThreadDescriptor::THREAD_TYPE_UNSPECIFIED,
                             /* thread_name = */ base::nullopt, kReferenceTimeUs,
                             kReferenceThreadTimeUs, &trace_packet_protos);
   AddInternedLegacyEventName(3, "legacy_event_name_3", &trace_packet_protos);
@@ -1130,7 +1132,7 @@
   trace_analyzer::TraceEventVector events;
 
   AddThreadDescriptorPacket(/* sort_index = */ base::nullopt,
-                            ThreadDescriptor::THREAD_UNSPECIFIED,
+                            ThreadDescriptor::THREAD_TYPE_UNSPECIFIED,
                             /* thread_name = */ base::nullopt, kReferenceTimeUs,
                             kReferenceThreadTimeUs, &trace_packet_protos);
   AddInternedLegacyEventName(3, "legacy_event_name_3", &trace_packet_protos);
@@ -1187,7 +1189,7 @@
   trace_analyzer::TraceEventVector events;
 
   AddThreadDescriptorPacket(/* sort_index = */ base::nullopt,
-                            ThreadDescriptor::THREAD_UNSPECIFIED,
+                            ThreadDescriptor::THREAD_TYPE_UNSPECIFIED,
                             /* thread_name = */ base::nullopt, kReferenceTimeUs,
                             kReferenceThreadTimeUs, &trace_packet_protos);
   AddInternedLegacyEventName(3, "legacy_event_name_3", &trace_packet_protos);
@@ -1241,7 +1243,7 @@
   trace_analyzer::TraceEventVector events;
 
   AddThreadDescriptorPacket(/* sort_index = */ base::nullopt,
-                            ThreadDescriptor::THREAD_UNSPECIFIED,
+                            ThreadDescriptor::THREAD_TYPE_UNSPECIFIED,
                             /* thread_name = */ base::nullopt, kReferenceTimeUs,
                             kReferenceThreadTimeUs, &trace_packet_protos);
   AddInternedLegacyEventName(3, "legacy_event_name_3", &trace_packet_protos);
@@ -1267,7 +1269,7 @@
   trace_analyzer::TraceEventVector events;
 
   AddThreadDescriptorPacket(/* sort_index = */ base::nullopt,
-                            ThreadDescriptor::THREAD_UNSPECIFIED,
+                            ThreadDescriptor::THREAD_TYPE_UNSPECIFIED,
                             /* thread_name = */ base::nullopt, kReferenceTimeUs,
                             kReferenceThreadTimeUs, &trace_packet_protos);
   AddInternedLegacyEventName(3, "legacy_event_name_3", &trace_packet_protos);
@@ -1308,7 +1310,7 @@
   trace_analyzer::TraceEventVector events;
 
   AddThreadDescriptorPacket(/* sort_index = */ base::nullopt,
-                            ThreadDescriptor::THREAD_UNSPECIFIED,
+                            ThreadDescriptor::THREAD_TYPE_UNSPECIFIED,
                             /* thread_name = */ base::nullopt, kReferenceTimeUs,
                             kReferenceThreadTimeUs, &trace_packet_protos);
   AddInternedLegacyEventName(3, "legacy_event_name_3", &trace_packet_protos);
@@ -1343,7 +1345,7 @@
   trace_analyzer::TraceEventVector events;
 
   AddThreadDescriptorPacket(/* sort_index = */ base::nullopt,
-                            ThreadDescriptor::THREAD_UNSPECIFIED,
+                            ThreadDescriptor::THREAD_TYPE_UNSPECIFIED,
                             /* thread_name = */ base::nullopt, kReferenceTimeUs,
                             kReferenceThreadTimeUs, &trace_packet_protos);
   AddInternedLegacyEventName(3, "legacy_event_name_3", &trace_packet_protos);
@@ -1378,7 +1380,7 @@
   trace_analyzer::TraceEventVector events;
 
   AddThreadDescriptorPacket(/* sort_index = */ base::nullopt,
-                            ThreadDescriptor::THREAD_UNSPECIFIED,
+                            ThreadDescriptor::THREAD_TYPE_UNSPECIFIED,
                             /* thread_name = */ base::nullopt, kReferenceTimeUs,
                             kReferenceThreadTimeUs, &trace_packet_protos);
   AddInternedLegacyEventName(3, "legacy_event_name_3", &trace_packet_protos);
@@ -1424,7 +1426,7 @@
   trace_analyzer::TraceEventVector events;
 
   AddThreadDescriptorPacket(/* sort_index = */ base::nullopt,
-                            ThreadDescriptor::THREAD_UNSPECIFIED,
+                            ThreadDescriptor::THREAD_TYPE_UNSPECIFIED,
                             /* thread_name = */ base::nullopt, kReferenceTimeUs,
                             kReferenceThreadTimeUs, &trace_packet_protos);
   AddInternedLegacyEventName(3, "legacy_event_name_3", &trace_packet_protos);
@@ -1458,7 +1460,7 @@
   trace_analyzer::TraceEventVector events;
 
   AddThreadDescriptorPacket(/* sort_index = */ base::nullopt,
-                            ThreadDescriptor::THREAD_UNSPECIFIED,
+                            ThreadDescriptor::THREAD_TYPE_UNSPECIFIED,
                             /* thread_name = */ base::nullopt, kReferenceTimeUs,
                             kReferenceThreadTimeUs, &trace_packet_protos);
   AddInternedLegacyEventName(3, "legacy_event_name_3", &trace_packet_protos);
@@ -1492,7 +1494,7 @@
   trace_analyzer::TraceEventVector events;
 
   AddThreadDescriptorPacket(/* sort_index = */ base::nullopt,
-                            ThreadDescriptor::THREAD_UNSPECIFIED,
+                            ThreadDescriptor::THREAD_TYPE_UNSPECIFIED,
                             /* thread_name = */ base::nullopt, kReferenceTimeUs,
                             kReferenceThreadTimeUs, &trace_packet_protos);
   AddInternedLegacyEventName(3, "legacy_event_name_3", &trace_packet_protos);
@@ -1560,7 +1562,7 @@
   trace_analyzer::TraceEventVector events;
 
   AddThreadDescriptorPacket(/* sort_index = */ base::nullopt,
-                            ThreadDescriptor::THREAD_UNSPECIFIED,
+                            ThreadDescriptor::THREAD_TYPE_UNSPECIFIED,
                             /* thread_name = */ base::nullopt, kReferenceTimeUs,
                             kReferenceThreadTimeUs, &trace_packet_protos);
   AddInternedLegacyEventName(3, "legacy_event_name_3", &trace_packet_protos);
@@ -1730,7 +1732,7 @@
   // timestamps and one with an absolute timestamps 1 us further than the delta
   // events.
   AddThreadDescriptorPacket(/* sort_index = */ base::nullopt,
-                            ThreadDescriptor::THREAD_UNSPECIFIED,
+                            ThreadDescriptor::THREAD_TYPE_UNSPECIFIED,
                             /* thread_name = */ base::nullopt, kReferenceTimeUs,
                             kReferenceThreadTimeUs, &trace_packet_protos);
   AddInternedLegacyEventName(1, "sequence_1", &trace_packet_protos);
@@ -1755,7 +1757,7 @@
   // Sequence 2 alternates between emitting an event dropping packets and
   // clearing incremental state.
   AddThreadDescriptorPacket(/* sort_index = */ base::nullopt,
-                            ThreadDescriptor::THREAD_UNSPECIFIED,
+                            ThreadDescriptor::THREAD_TYPE_UNSPECIFIED,
                             /* thread_name = */ base::nullopt, kReferenceTimeUs,
                             kReferenceThreadTimeUs, &trace_packet_protos);
   AddInternedLegacyEventName(2, "sequence_2", &trace_packet_protos);
@@ -1780,7 +1782,8 @@
 
   // Reset the state.
   AddThreadDescriptorPacket(
-      /* sort_index = */ base::nullopt, ThreadDescriptor::THREAD_UNSPECIFIED,
+      /* sort_index = */ base::nullopt,
+      ThreadDescriptor::THREAD_TYPE_UNSPECIFIED,
       /* thread_name = */ base::nullopt, kReferenceTimeUs + 4 * 3,
       kReferenceThreadTimeUs + 3 * 3, &trace_packet_protos);
   trace_packet_protos.back().set_incremental_state_cleared(true);
@@ -1799,7 +1802,7 @@
   // Sequence 3 emits a single event to ensure that sequence 2 doesn't prevent
   // these events from being emitted.
   AddThreadDescriptorPacket(/* sort_index = */ base::nullopt,
-                            ThreadDescriptor::THREAD_UNSPECIFIED,
+                            ThreadDescriptor::THREAD_TYPE_UNSPECIFIED,
                             /* thread_name = */ base::nullopt, kReferenceTimeUs,
                             kReferenceThreadTimeUs, &trace_packet_protos);
   AddInternedLegacyEventName(3, "sequence_3", &trace_packet_protos);
diff --git a/services/tracing/public/cpp/perfetto/dummy_producer.h b/services/tracing/public/cpp/perfetto/dummy_producer.h
index a79383f4..898507c0 100644
--- a/services/tracing/public/cpp/perfetto/dummy_producer.h
+++ b/services/tracing/public/cpp/perfetto/dummy_producer.h
@@ -6,7 +6,7 @@
 #define SERVICES_TRACING_PUBLIC_CPP_PERFETTO_DUMMY_PRODUCER_H_
 
 #include "services/tracing/public/cpp/perfetto/system_producer.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/producer.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/producer.h"
 
 namespace tracing {
 
diff --git a/services/tracing/public/cpp/perfetto/perfetto_config.h b/services/tracing/public/cpp/perfetto/perfetto_config.h
index b461678..32ae0b6f 100644
--- a/services/tracing/public/cpp/perfetto/perfetto_config.h
+++ b/services/tracing/public/cpp/perfetto/perfetto_config.h
@@ -6,7 +6,7 @@
 #define SERVICES_TRACING_PUBLIC_CPP_PERFETTO_PERFETTO_CONFIG_H_
 
 #include "base/component_export.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/trace_config.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_config.h"
 
 namespace base {
 namespace trace_event {
diff --git a/services/tracing/public/cpp/perfetto/perfetto_producer.cc b/services/tracing/public/cpp/perfetto/perfetto_producer.cc
index f5a5952..ee87551 100644
--- a/services/tracing/public/cpp/perfetto/perfetto_producer.cc
+++ b/services/tracing/public/cpp/perfetto/perfetto_producer.cc
@@ -4,9 +4,9 @@
 
 #include "services/tracing/public/cpp/perfetto/perfetto_producer.h"
 
-#include "third_party/perfetto/include/perfetto/tracing/core/shared_memory_arbiter.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/startup_trace_writer_registry.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/trace_writer.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/shared_memory_arbiter.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/startup_trace_writer_registry.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_writer.h"
 
 namespace tracing {
 
diff --git a/services/tracing/public/cpp/perfetto/perfetto_producer.h b/services/tracing/public/cpp/perfetto/perfetto_producer.h
index d5a18aaf..b9dc035a 100644
--- a/services/tracing/public/cpp/perfetto/perfetto_producer.h
+++ b/services/tracing/public/cpp/perfetto/perfetto_producer.h
@@ -9,7 +9,7 @@
 
 #include "base/component_export.h"
 #include "services/tracing/public/cpp/perfetto/perfetto_traced_process.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/tracing_service.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/tracing_service.h"
 
 namespace perfetto {
 class SharedMemoryArbiter;
diff --git a/services/tracing/public/cpp/perfetto/producer_client.cc b/services/tracing/public/cpp/perfetto/producer_client.cc
index c6509fb0..8f274e00 100644
--- a/services/tracing/public/cpp/perfetto/producer_client.cc
+++ b/services/tracing/public/cpp/perfetto/producer_client.cc
@@ -13,10 +13,10 @@
 #include "services/tracing/public/cpp/perfetto/shared_memory.h"
 #include "services/tracing/public/cpp/perfetto/trace_event_data_source.h"
 #include "services/tracing/public/mojom/constants.mojom.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/commit_data_request.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/shared_memory_arbiter.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/startup_trace_writer_registry.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/trace_writer.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/commit_data_request.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/shared_memory_arbiter.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/startup_trace_writer_registry.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_writer.h"
 
 namespace tracing {
 
diff --git a/services/tracing/public/cpp/perfetto/shared_memory.cc b/services/tracing/public/cpp/perfetto/shared_memory.cc
index 84907fe..e2fa2c8a5 100644
--- a/services/tracing/public/cpp/perfetto/shared_memory.cc
+++ b/services/tracing/public/cpp/perfetto/shared_memory.cc
@@ -6,7 +6,7 @@
 
 #include <utility>
 
-#include "third_party/perfetto/include/perfetto/tracing/core/shared_memory.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/shared_memory.h"
 
 namespace tracing {
 
diff --git a/services/tracing/public/cpp/perfetto/shared_memory.h b/services/tracing/public/cpp/perfetto/shared_memory.h
index 2542cdf..548e64d 100644
--- a/services/tracing/public/cpp/perfetto/shared_memory.h
+++ b/services/tracing/public/cpp/perfetto/shared_memory.h
@@ -10,7 +10,7 @@
 #include "base/component_export.h"
 #include "base/macros.h"
 #include "mojo/public/cpp/system/platform_handle.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/shared_memory.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/shared_memory.h"
 
 namespace tracing {
 
diff --git a/services/tracing/public/cpp/perfetto/system_producer.h b/services/tracing/public/cpp/perfetto/system_producer.h
index e37274c..247afc03 100644
--- a/services/tracing/public/cpp/perfetto/system_producer.h
+++ b/services/tracing/public/cpp/perfetto/system_producer.h
@@ -6,7 +6,7 @@
 #define SERVICES_TRACING_PUBLIC_CPP_PERFETTO_SYSTEM_PRODUCER_H_
 
 #include "services/tracing/public/cpp/perfetto/perfetto_producer.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/producer.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/producer.h"
 
 namespace tracing {
 
diff --git a/services/tracing/public/cpp/perfetto/thread_local_event_sink.cc b/services/tracing/public/cpp/perfetto/thread_local_event_sink.cc
index 0aa7890..7ffdc56 100644
--- a/services/tracing/public/cpp/perfetto/thread_local_event_sink.cc
+++ b/services/tracing/public/cpp/perfetto/thread_local_event_sink.cc
@@ -7,7 +7,7 @@
 #include <utility>
 
 #include "services/tracing/public/cpp/perfetto/trace_event_data_source.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/startup_trace_writer.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/startup_trace_writer.h"
 
 namespace tracing {
 
diff --git a/services/tracing/public/cpp/perfetto/trace_event_data_source.cc b/services/tracing/public/cpp/perfetto/trace_event_data_source.cc
index 7ea0561..157ebe42 100644
--- a/services/tracing/public/cpp/perfetto/trace_event_data_source.cc
+++ b/services/tracing/public/cpp/perfetto/trace_event_data_source.cc
@@ -25,10 +25,10 @@
 #include "services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.h"
 #include "services/tracing/public/cpp/trace_event_args_whitelist.h"
 #include "services/tracing/public/mojom/constants.mojom.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/shared_memory_arbiter.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/startup_trace_writer.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/startup_trace_writer_registry.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/trace_writer.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/shared_memory_arbiter.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/startup_trace_writer.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/startup_trace_writer_registry.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_writer.h"
 #include "third_party/perfetto/protos/perfetto/trace/chrome/chrome_trace_event.pbzero.h"
 #include "third_party/perfetto/protos/perfetto/trace/trace_packet.pbzero.h"
 
diff --git a/services/tracing/public/cpp/perfetto/trace_event_data_source_unittest.cc b/services/tracing/public/cpp/perfetto/trace_event_data_source_unittest.cc
index a6e6847..67d6e02 100644
--- a/services/tracing/public/cpp/perfetto/trace_event_data_source_unittest.cc
+++ b/services/tracing/public/cpp/perfetto/trace_event_data_source_unittest.cc
@@ -20,9 +20,9 @@
 #include "components/tracing/common/tracing_switches.h"
 #include "services/tracing/public/mojom/perfetto_service.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_writer.h"
 #include "third_party/perfetto/include/perfetto/protozero/scattered_stream_null_delegate.h"
 #include "third_party/perfetto/include/perfetto/protozero/scattered_stream_writer.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/trace_writer.h"
 #include "third_party/perfetto/protos/perfetto/trace/trace_packet.pb.h"
 #include "third_party/perfetto/protos/perfetto/trace/trace_packet.pbzero.h"
 
diff --git a/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.cc b/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.cc
index 93b3e14..6bb3dec 100644
--- a/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.cc
+++ b/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.cc
@@ -12,7 +12,7 @@
 #include "services/tracing/public/cpp/perfetto/perfetto_traced_process.h"
 #include "services/tracing/public/cpp/perfetto/producer_client.h"
 #include "services/tracing/public/cpp/perfetto/traced_value_proto_writer.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/startup_trace_writer.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/startup_trace_writer.h"
 #include "third_party/perfetto/protos/perfetto/trace/interned_data/interned_data.pbzero.h"
 #include "third_party/perfetto/protos/perfetto/trace/trace_packet.pbzero.h"
 #include "third_party/perfetto/protos/perfetto/trace/track_event/debug_annotation.pbzero.h"
diff --git a/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.h b/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.h
index 7cae3bd7..4582cec 100644
--- a/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.h
+++ b/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.h
@@ -14,8 +14,8 @@
 #include "base/time/time.h"
 #include "services/tracing/public/cpp/perfetto/interning_index.h"
 #include "services/tracing/public/cpp/perfetto/thread_local_event_sink.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_writer.h"
 #include "third_party/perfetto/include/perfetto/protozero/message_handle.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/trace_writer.h"
 
 namespace perfetto {
 class StartupTraceWriter;
diff --git a/services/tracing/public/mojom/chrome_config_mojom_traits.h b/services/tracing/public/mojom/chrome_config_mojom_traits.h
index 1deafb3..ccdbba9 100644
--- a/services/tracing/public/mojom/chrome_config_mojom_traits.h
+++ b/services/tracing/public/mojom/chrome_config_mojom_traits.h
@@ -12,7 +12,7 @@
 
 #include "mojo/public/cpp/bindings/struct_traits.h"
 #include "services/tracing/public/mojom/perfetto_service.mojom.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/chrome_config.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/chrome_config.h"
 
 namespace mojo {
 template <>
diff --git a/services/tracing/public/mojom/commit_data_request_mojom_traits.h b/services/tracing/public/mojom/commit_data_request_mojom_traits.h
index 8b5c468..e10f485 100644
--- a/services/tracing/public/mojom/commit_data_request_mojom_traits.h
+++ b/services/tracing/public/mojom/commit_data_request_mojom_traits.h
@@ -13,7 +13,7 @@
 
 #include "mojo/public/cpp/bindings/struct_traits.h"
 #include "services/tracing/public/mojom/perfetto_service.mojom.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/commit_data_request.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/commit_data_request.h"
 
 namespace mojo {
 
diff --git a/services/tracing/public/mojom/data_source_config_mojom_traits.h b/services/tracing/public/mojom/data_source_config_mojom_traits.h
index e749282..4e357412 100644
--- a/services/tracing/public/mojom/data_source_config_mojom_traits.h
+++ b/services/tracing/public/mojom/data_source_config_mojom_traits.h
@@ -12,8 +12,8 @@
 
 #include "mojo/public/cpp/bindings/struct_traits.h"
 #include "services/tracing/public/mojom/perfetto_service.mojom.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/chrome_config.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/data_source_config.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/chrome_config.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/data_source_config.h"
 
 namespace mojo {
 template <>
diff --git a/services/tracing/public/mojom/data_source_descriptor_mojom_traits.h b/services/tracing/public/mojom/data_source_descriptor_mojom_traits.h
index b98db41..56c7992 100644
--- a/services/tracing/public/mojom/data_source_descriptor_mojom_traits.h
+++ b/services/tracing/public/mojom/data_source_descriptor_mojom_traits.h
@@ -12,7 +12,7 @@
 
 #include "mojo/public/cpp/bindings/struct_traits.h"
 #include "services/tracing/public/mojom/perfetto_service.mojom.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/data_source_descriptor.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/data_source_descriptor.h"
 
 namespace mojo {
 template <>
diff --git a/services/tracing/public/mojom/perfetto_service.typemap b/services/tracing/public/mojom/perfetto_service.typemap
index ac29824..58a062b 100644
--- a/services/tracing/public/mojom/perfetto_service.typemap
+++ b/services/tracing/public/mojom/perfetto_service.typemap
@@ -1,10 +1,10 @@
 mojom = "//services/tracing/public/mojom/perfetto_service.mojom"
 public_headers = [
-  "//third_party/perfetto/include/perfetto/tracing/core/commit_data_request.h",
-  "//third_party/perfetto/include/perfetto/tracing/core/data_source_config.h",
-  "//third_party/perfetto/include/perfetto/tracing/core/data_source_descriptor.h",
-  "//third_party/perfetto/include/perfetto/tracing/core/chrome_config.h",
-  "//third_party/perfetto/include/perfetto/tracing/core/trace_config.h",
+  "//third_party/perfetto/include/perfetto/ext/tracing/core/commit_data_request.h",
+  "//third_party/perfetto/include/perfetto/ext/tracing/core/data_source_config.h",
+  "//third_party/perfetto/include/perfetto/ext/tracing/core/data_source_descriptor.h",
+  "//third_party/perfetto/include/perfetto/ext/tracing/core/chrome_config.h",
+  "//third_party/perfetto/include/perfetto/ext/tracing/core/trace_config.h",
 ]
 traits_headers = [
   "//services/tracing/public/mojom/commit_data_request_mojom_traits.h",
diff --git a/services/tracing/public/mojom/trace_config_mojom_traits.h b/services/tracing/public/mojom/trace_config_mojom_traits.h
index 49fefba..bbd3b530 100644
--- a/services/tracing/public/mojom/trace_config_mojom_traits.h
+++ b/services/tracing/public/mojom/trace_config_mojom_traits.h
@@ -13,7 +13,7 @@
 
 #include "mojo/public/cpp/bindings/struct_traits.h"
 #include "services/tracing/public/mojom/perfetto_service.mojom.h"
-#include "third_party/perfetto/include/perfetto/tracing/core/trace_config.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_config.h"
 
 namespace mojo {
 
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 601e006c9..a2c2d5b 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -4672,6 +4672,19 @@
         "test": "service_manager_unittests"
       },
       {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.services_unittests.filter"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "services_unittests"
+      },
+      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5063,6 +5076,24 @@
         "test": "service_manager_unittests"
       },
       {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.services_unittests.filter"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "kvm": "1"
+            }
+          ]
+        },
+        "test": "services_unittests"
+      },
+      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5502,6 +5533,24 @@
         "test": "service_manager_unittests"
       },
       {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.services_unittests.filter"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "kvm": "1"
+            }
+          ]
+        },
+        "test": "services_unittests"
+      },
+      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
diff --git a/testing/buildbot/filters/BUILD.gn b/testing/buildbot/filters/BUILD.gn
index 73632a7..335afe2 100644
--- a/testing/buildbot/filters/BUILD.gn
+++ b/testing/buildbot/filters/BUILD.gn
@@ -67,6 +67,7 @@
     "//testing/buildbot/filters/fuchsia.mojo_unittests.filter",
     "//testing/buildbot/filters/fuchsia.net_perftests.filter",
     "//testing/buildbot/filters/fuchsia.net_unittests.filter",
+    "//testing/buildbot/filters/fuchsia.services_unittests.filter",
     "//testing/buildbot/filters/fuchsia.ui_base_unittests.filter",
   ]
 }
diff --git a/testing/buildbot/filters/fuchsia.services_unittests.filter b/testing/buildbot/filters/fuchsia.services_unittests.filter
new file mode 100644
index 0000000..b496cdb
--- /dev/null
+++ b/testing/buildbot/filters/fuchsia.services_unittests.filter
@@ -0,0 +1,25 @@
+# https://crbug.com/925652 - There are no |services_|.
+-HidServiceTest.GetDevices
+
+# https://crbug.com/925652 - |num_sockets| expectations mismatch.
+-NetworkContextTest.PreconnectFour
+-NetworkContextTest.PreconnectTwo
+
+# https://crbug.com/925652 - Fails FillOSMemoryDump().
+-OSMetricsTest.GivesNonZeroResults
+
+# https://crbug.com/925653 - IsTracingEnabled() expectations not met.
+-TracingConsumerTest.NotifiesOnTracingEnabledWaitsFor*
+
+# https://crbug.com/925653 - Flaky OOM and virtual method crashes.
+-TracingConsumerTest.FlushProducers
+-TracingConsumerTest.LargeDataSize
+-TracingConsumerTest.PrivacyFilterConfig
+-TracingConsumerTest.PrivacyFilterConfigInJson
+-TracingConsumerTest.TestConsumerPriority
+
+# https://crbug.com/925653 - Second socket receives too many packets.
+-UDPSocketTest.JoinMulticastGroup
+
+# https://crbug.com/925653 - Unexpected address-in-use.
+-TCPBoundSocketTest.ListenError
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 14a0007a..c4a0cd3 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -1232,6 +1232,8 @@
       # chromium.gpu.fyi
       # The face and barcode detection tests fail on the Mac Pros.
       'Mac Pro FYI Release (AMD)',
+      # chromium.linux
+      'Fuchsia x64',
       # chromium.memory
       'Linux ChromiumOS MSan Tests',  # https://crbug.com/831676
       'Linux MSan Tests',  # https://crbug.com/831676
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 84207b4..aa72baf 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -2670,6 +2670,11 @@
       },
       'perfetto_unittests': {},
       'service_manager_unittests': {},
+      'services_unittests': {
+        'args': [
+          '--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.services_unittests.filter',
+        ],
+      },
       'skia_unittests': {},
       'sql_unittests': {},
       'ui_base_unittests': {
diff --git a/third_party/blink/perf_tests/OWNERS b/third_party/blink/perf_tests/OWNERS
index 2ffc6398..7ae8de90 100644
--- a/third_party/blink/perf_tests/OWNERS
+++ b/third_party/blink/perf_tests/OWNERS
@@ -4,7 +4,6 @@
 haraken@chromium.org
 hayato@chromium.org
 kojii@chromium.org
-nednguyen@google.com
 crouleau@chromium.org
 
 # Team: benchmarking-dev@chromium.org
diff --git a/third_party/blink/public/platform/web_prerender.h b/third_party/blink/public/platform/web_prerender.h
index 4a3317ce..17f016c 100644
--- a/third_party/blink/public/platform/web_prerender.h
+++ b/third_party/blink/public/platform/web_prerender.h
@@ -36,6 +36,7 @@
 #include "third_party/blink/public/platform/web_private_ptr.h"
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/platform/web_url.h"
+#include "url/origin.h"
 
 namespace blink {
 
@@ -68,6 +69,7 @@
 
   BLINK_PLATFORM_EXPORT WebURL Url() const;
   BLINK_PLATFORM_EXPORT WebString GetReferrer() const;
+  BLINK_PLATFORM_EXPORT url::Origin SecurityOrigin() const;
   BLINK_PLATFORM_EXPORT unsigned RelTypes() const;
   BLINK_PLATFORM_EXPORT network::mojom::ReferrerPolicy GetReferrerPolicy()
       const;
diff --git a/third_party/blink/renderer/core/animation/css/css_animations_test.cc b/third_party/blink/renderer/core/animation/css/css_animations_test.cc
index d029e8a..2047d1e 100644
--- a/third_party/blink/renderer/core/animation/css/css_animations_test.cc
+++ b/third_party/blink/renderer/core/animation/css/css_animations_test.cc
@@ -81,13 +81,13 @@
   element->setAttribute(html_names::kClassAttr, "contrast2");
   GetPage().Animator().ServiceScriptedAnimations(CurrentTimeTicks());
   UpdateAllLifecyclePhasesForTest();
-  EXPECT_NEAR(0.6, GetContrastFilterAmount(element), 0.00000000001);
+  EXPECT_NEAR(0.6, GetContrastFilterAmount(element), 0.0000000001);
 
   // As it has been retargeted, advancing halfway should go to 0.3.
   AdvanceClockSeconds(0.5);
   GetPage().Animator().ServiceScriptedAnimations(CurrentTimeTicks());
   UpdateAllLifecyclePhasesForTest();
-  EXPECT_NEAR(0.3, GetContrastFilterAmount(element), 0.00000000001);
+  EXPECT_NEAR(0.3, GetContrastFilterAmount(element), 0.0000000001);
 }
 
 // Test that when an incompatible in progress compositor transition
diff --git a/third_party/blink/renderer/core/css/resolver/element_style_resources.cc b/third_party/blink/renderer/core/css/resolver/element_style_resources.cc
index f6c4696..69184f84f 100644
--- a/third_party/blink/renderer/core/css/resolver/element_style_resources.cc
+++ b/third_party/blink/renderer/core/css/resolver/element_style_resources.cc
@@ -207,7 +207,8 @@
             if (!BackgroundLayerMayBeSprite(*background_layer)) {
               if (element_->GetDocument()
                       .GetFrame()
-                      ->IsAutomaticLazyLoadingImageAllowed()) {
+                      ->GetLazyLoadImageEnabledState() ==
+                  LocalFrame::LazyLoadImageEnabledState::kEnabledAutomatic) {
                 image_request_optimization = FetchParameters::kDeferImageLoad;
               } else {
                 image_request_optimization = FetchParameters::kAllowPlaceholder;
diff --git a/third_party/blink/renderer/core/fileapi/file_reader_loader.cc b/third_party/blink/renderer/core/fileapi/file_reader_loader.cc
index 2d6f4196..4eee2b2 100644
--- a/third_party/blink/renderer/core/fileapi/file_reader_loader.cc
+++ b/third_party/blink/renderer/core/fileapi/file_reader_loader.cc
@@ -80,7 +80,7 @@
       weak_factory_(this) {
   // TODO(https://crbug.com/957651): Change this into a DCHECK once we figured
   // out where code is passing in a null task runner,
-  DCHECK(task_runner_);
+  CHECK(task_runner_);
 }
 
 FileReaderLoader::~FileReaderLoader() {
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index 4851cb4..06f9c9f 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -1290,25 +1290,23 @@
                          request, Client()->GetPreviewsStateForFrame());
 }
 
-bool LocalFrame::IsExplicitLazyLoadingImageAllowed() const {
+LocalFrame::LazyLoadImageEnabledState LocalFrame::GetLazyLoadImageEnabledState()
+    const {
   DCHECK(GetSettings());
-  return RuntimeEnabledFeatures::LazyImageLoadingEnabled() &&
-         GetSettings()->GetLazyLoadEnabled();
-}
-
-bool LocalFrame::IsAutomaticLazyLoadingImageAllowed() const {
-  if (!IsExplicitLazyLoadingImageAllowed())
-    return false;
+  if (!RuntimeEnabledFeatures::LazyImageLoadingEnabled() ||
+      !GetSettings()->GetLazyLoadEnabled()) {
+    return LocalFrame::LazyLoadImageEnabledState::kDisabled;
+  }
   if (!RuntimeEnabledFeatures::AutomaticLazyImageLoadingEnabled())
-    return false;
+    return LocalFrame::LazyLoadImageEnabledState::kEnabledExplicit;
   if (RuntimeEnabledFeatures::
           RestrictAutomaticLazyImageLoadingToDataSaverEnabled() &&
       !is_save_data_enabled_) {
-    return false;
+    return LocalFrame::LazyLoadImageEnabledState::kEnabledExplicit;
   }
   if (Owner() && !Owner()->ShouldLazyLoadChildren())
-    return false;
-  return true;
+    return LocalFrame::LazyLoadImageEnabledState::kEnabledExplicit;
+  return LocalFrame::LazyLoadImageEnabledState::kEnabledAutomatic;
 }
 
 WebURLLoaderFactory* LocalFrame::GetURLLoaderFactory() {
diff --git a/third_party/blink/renderer/core/frame/local_frame.h b/third_party/blink/renderer/core/frame/local_frame.h
index 44765c1..784ff44b 100644
--- a/third_party/blink/renderer/core/frame/local_frame.h
+++ b/third_party/blink/renderer/core/frame/local_frame.h
@@ -317,9 +317,13 @@
   // Returns true if Client Lo-Fi should be used for this request.
   bool IsClientLoFiAllowed(const ResourceRequest&) const;
 
-  // Returns true if lazyloading the image is possible.
-  bool IsExplicitLazyLoadingImageAllowed() const;
-  bool IsAutomaticLazyLoadingImageAllowed() const;
+  enum class LazyLoadImageEnabledState {
+    kDisabled,
+    kEnabledExplicit,
+    kEnabledAutomatic
+  };
+  // Returns the enabled state of lazyloading of images.
+  LazyLoadImageEnabledState GetLazyLoadImageEnabledState() const;
 
   // The returned value is a off-heap raw-ptr and should not be stored.
   WebURLLoaderFactory* GetURLLoaderFactory();
diff --git a/third_party/blink/renderer/core/frame/local_frame_test.cc b/third_party/blink/renderer/core/frame/local_frame_test.cc
index 4eb379e..7bf062b 100644
--- a/third_party/blink/renderer/core/frame/local_frame_test.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_test.cc
@@ -187,8 +187,8 @@
   auto page_holder = std::make_unique<DummyPageHolder>(
       IntSize(800, 600), nullptr, nullptr,
       &EnableLazyLoadAndDisableDataSaverHoldbackInSettings);
-  EXPECT_FALSE(page_holder->GetFrame().IsExplicitLazyLoadingImageAllowed());
-  EXPECT_FALSE(page_holder->GetFrame().IsAutomaticLazyLoadingImageAllowed());
+  EXPECT_EQ(LocalFrame::LazyLoadImageEnabledState::kDisabled,
+            page_holder->GetFrame().GetLazyLoadImageEnabledState());
 }
 
 TEST_F(LocalFrameTest, IsLazyLoadingImageAllowedWithSettingDisabled) {
@@ -196,8 +196,8 @@
   auto page_holder = std::make_unique<DummyPageHolder>(
       IntSize(800, 600), nullptr, nullptr,
       &DisableLazyLoadAndDisableDataSaverHoldbackInSettings);
-  EXPECT_FALSE(page_holder->GetFrame().IsExplicitLazyLoadingImageAllowed());
-  EXPECT_FALSE(page_holder->GetFrame().IsAutomaticLazyLoadingImageAllowed());
+  EXPECT_EQ(LocalFrame::LazyLoadImageEnabledState::kDisabled,
+            page_holder->GetFrame().GetLazyLoadImageEnabledState());
 }
 
 TEST_F(LocalFrameTest, IsLazyLoadingImageAllowedWithAutomaticDisabled) {
@@ -207,8 +207,8 @@
   auto page_holder = std::make_unique<DummyPageHolder>(
       IntSize(800, 600), nullptr, nullptr,
       &EnableLazyLoadAndDisableDataSaverHoldbackInSettings);
-  EXPECT_TRUE(page_holder->GetFrame().IsExplicitLazyLoadingImageAllowed());
-  EXPECT_FALSE(page_holder->GetFrame().IsAutomaticLazyLoadingImageAllowed());
+  EXPECT_EQ(LocalFrame::LazyLoadImageEnabledState::kEnabledExplicit,
+            page_holder->GetFrame().GetLazyLoadImageEnabledState());
 }
 
 TEST_F(LocalFrameTest, IsLazyLoadingImageAllowedWhenNotRestricted) {
@@ -221,8 +221,8 @@
   auto page_holder = std::make_unique<DummyPageHolder>(
       IntSize(800, 600), nullptr, nullptr,
       &EnableLazyLoadAndDisableDataSaverHoldbackInSettings);
-  EXPECT_TRUE(page_holder->GetFrame().IsExplicitLazyLoadingImageAllowed());
-  EXPECT_TRUE(page_holder->GetFrame().IsAutomaticLazyLoadingImageAllowed());
+  EXPECT_EQ(LocalFrame::LazyLoadImageEnabledState::kEnabledAutomatic,
+            page_holder->GetFrame().GetLazyLoadImageEnabledState());
 }
 
 TEST_F(LocalFrameTest,
@@ -236,8 +236,8 @@
   auto page_holder = std::make_unique<DummyPageHolder>(
       IntSize(800, 600), nullptr, nullptr,
       &EnableLazyLoadAndDisableDataSaverHoldbackInSettings);
-  EXPECT_TRUE(page_holder->GetFrame().IsExplicitLazyLoadingImageAllowed());
-  EXPECT_FALSE(page_holder->GetFrame().IsAutomaticLazyLoadingImageAllowed());
+  EXPECT_EQ(LocalFrame::LazyLoadImageEnabledState::kEnabledExplicit,
+            page_holder->GetFrame().GetLazyLoadImageEnabledState());
 }
 
 TEST_F(LocalFrameTest,
@@ -251,8 +251,8 @@
   auto page_holder = std::make_unique<DummyPageHolder>(
       IntSize(800, 600), nullptr, nullptr,
       &EnableLazyLoadAndEnableDataSaverHoldbackInSettings);
-  EXPECT_TRUE(page_holder->GetFrame().IsExplicitLazyLoadingImageAllowed());
-  EXPECT_FALSE(page_holder->GetFrame().IsAutomaticLazyLoadingImageAllowed());
+  EXPECT_EQ(LocalFrame::LazyLoadImageEnabledState::kEnabledExplicit,
+            page_holder->GetFrame().GetLazyLoadImageEnabledState());
 }
 
 TEST_F(LocalFrameTest,
@@ -266,8 +266,8 @@
   auto page_holder = std::make_unique<DummyPageHolder>(
       IntSize(800, 600), nullptr, nullptr,
       &EnableLazyLoadAndDisableDataSaverHoldbackInSettings);
-  EXPECT_TRUE(page_holder->GetFrame().IsExplicitLazyLoadingImageAllowed());
-  EXPECT_TRUE(page_holder->GetFrame().IsAutomaticLazyLoadingImageAllowed());
+  EXPECT_EQ(LocalFrame::LazyLoadImageEnabledState::kEnabledAutomatic,
+            page_holder->GetFrame().GetLazyLoadImageEnabledState());
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/reporting_context.cc b/third_party/blink/renderer/core/frame/reporting_context.cc
index 2702288..064cdaa 100644
--- a/third_party/blink/renderer/core/frame/reporting_context.cc
+++ b/third_party/blink/renderer/core/frame/reporting_context.cc
@@ -43,7 +43,7 @@
 
   // Buffer the report.
   if (!report_buffer_.Contains(report->type()))
-    report_buffer_.insert(report->type(), HeapListHashSet<Member<Report>>());
+    report_buffer_.insert(report->type(), HeapLinkedHashSet<Member<Report>>());
   report_buffer_.find(report->type())->value.insert(report);
 
   // Only the most recent 100 reports will remain buffered, per report type.
diff --git a/third_party/blink/renderer/core/frame/reporting_context.h b/third_party/blink/renderer/core/frame/reporting_context.h
index fab504ba..8a0b98f3 100644
--- a/third_party/blink/renderer/core/frame/reporting_context.h
+++ b/third_party/blink/renderer/core/frame/reporting_context.h
@@ -53,8 +53,8 @@
   // Send |report| via the Reporting API to |endpoint|.
   void SendToReportingAPI(Report* report, const String& endpoint) const;
 
-  HeapListHashSet<Member<ReportingObserver>> observers_;
-  HeapHashMap<String, HeapListHashSet<Member<Report>>> report_buffer_;
+  HeapLinkedHashSet<Member<ReportingObserver>> observers_;
+  HeapHashMap<String, HeapLinkedHashSet<Member<Report>>> report_buffer_;
   Member<ExecutionContext> execution_context_;
 
   // This is declared mutable so that the service endpoint can be cached by
diff --git a/third_party/blink/renderer/core/html/html_image_element.cc b/third_party/blink/renderer/core/html/html_image_element.cc
index 5941d76..c5f40e72 100644
--- a/third_party/blink/renderer/core/html/html_image_element.cc
+++ b/third_party/blink/renderer/core/html/html_image_element.cc
@@ -870,29 +870,38 @@
 // Minimum height or width of the image to start lazyloading.
 constexpr int kMinDimensionToLazyLoad = 10;
 
-bool HTMLImageElement::IsDimensionSmallAndAbsoluteForLazyLoad(
+HTMLImageElement::LazyLoadDimensionType
+HTMLImageElement::GetAttributeLazyLoadDimensionType(
     const String& attribute_value) {
   HTMLDimension dimension;
-  return ParseDimensionValue(attribute_value, dimension) &&
-         dimension.IsAbsolute() && dimension.Value() <= kMinDimensionToLazyLoad;
+  if (ParseDimensionValue(attribute_value, dimension) &&
+      dimension.IsAbsolute()) {
+    return dimension.Value() <= kMinDimensionToLazyLoad
+               ? LazyLoadDimensionType::kAbsoluteSmall
+               : LazyLoadDimensionType::kAbsoluteNotSmall;
+  }
+  return LazyLoadDimensionType::kNotAbsolute;
 }
 
-bool HTMLImageElement::IsInlineStyleDimensionsSmall(
+HTMLImageElement::LazyLoadDimensionType
+HTMLImageElement::GetInlineStyleDimensionsType(
     const CSSPropertyValueSet* property_set) {
   if (!property_set)
-    return false;
+    return LazyLoadDimensionType::kNotAbsolute;
   const CSSValue* height =
       property_set->GetPropertyCSSValue(CSSPropertyID::kHeight);
   const CSSValue* width =
       property_set->GetPropertyCSSValue(CSSPropertyID::kWidth);
   const auto* width_prim = DynamicTo<CSSPrimitiveValue>(width);
   const auto* height_prim = DynamicTo<CSSPrimitiveValue>(height);
-  if (!width_prim || !height_prim)
-    return false;
-  return height_prim->IsPx() &&
-         (height_prim->GetDoubleValue() <= kMinDimensionToLazyLoad) &&
-         width_prim->IsPx() &&
-         (width_prim->GetDoubleValue() <= kMinDimensionToLazyLoad);
+  if (!width_prim || !height_prim || !width_prim->IsPx() ||
+      !height_prim->IsPx()) {
+    return LazyLoadDimensionType::kNotAbsolute;
+  }
+  return (height_prim->GetDoubleValue() <= kMinDimensionToLazyLoad) &&
+                 (width_prim->GetDoubleValue() <= kMinDimensionToLazyLoad)
+             ? LazyLoadDimensionType::kAbsoluteSmall
+             : LazyLoadDimensionType::kAbsoluteNotSmall;
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/html_image_element.h b/third_party/blink/renderer/core/html/html_image_element.h
index b876101..25a0bb32 100644
--- a/third_party/blink/renderer/core/html/html_image_element.h
+++ b/third_party/blink/renderer/core/html/html_image_element.h
@@ -67,6 +67,19 @@
                                                   unsigned width,
                                                   unsigned height);
 
+  // Returns dimension type of the attribute value or inline dimensions usable
+  // for LazyLoad, whether the dimension is absolute or not and if the absolute
+  // value is small enough to be skipped for lazyloading.
+  enum class LazyLoadDimensionType {
+    kNotAbsolute,
+    kAbsoluteNotSmall,
+    kAbsoluteSmall,
+  };
+  static LazyLoadDimensionType GetAttributeLazyLoadDimensionType(
+      const String& attribute_value);
+  static LazyLoadDimensionType GetInlineStyleDimensionsType(
+      const CSSPropertyValueSet* property_set);
+
   HTMLImageElement(Document&, const CreateElementFlags);
   explicit HTMLImageElement(Document&, bool created_by_parser = false);
   ~HTMLImageElement() override;
@@ -159,10 +172,6 @@
     return *visible_load_time_metrics_;
   }
 
-  static bool IsDimensionSmallAndAbsoluteForLazyLoad(
-      const String& attribute_value);
-  static bool IsInlineStyleDimensionsSmall(const CSSPropertyValueSet*);
-
   // Updates if any optimized image policy is violated. When any policy is
   // violated, the image should be rendered as a placeholder image.
   void SetImagePolicyViolated() {
diff --git a/third_party/blink/renderer/core/html/html_image_element_test.cc b/third_party/blink/renderer/core/html/html_image_element_test.cc
index 55f39e8..cffc20f 100644
--- a/third_party/blink/renderer/core/html/html_image_element_test.cc
+++ b/third_party/blink/renderer/core/html/html_image_element_test.cc
@@ -6,6 +6,8 @@
 
 #include <memory>
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/core/css/css_property_value_set.h"
+#include "third_party/blink/renderer/core/css/parser/css_parser.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/core/testing/page_test_base.h"
@@ -44,4 +46,63 @@
   EXPECT_EQ(250, image->SourceSize(*image));
 }
 
+TEST_F(HTMLImageElementTest, attributeLazyLoadDimensionType) {
+  struct TestCase {
+    const char* attribute_value;
+    HTMLImageElement::LazyLoadDimensionType expected_dimension_type;
+  };
+  const TestCase test_cases[] = {
+      {"", HTMLImageElement::LazyLoadDimensionType::kNotAbsolute},
+      {"invalid", HTMLImageElement::LazyLoadDimensionType::kNotAbsolute},
+      {"10px", HTMLImageElement::LazyLoadDimensionType::kAbsoluteSmall},
+      {"10", HTMLImageElement::LazyLoadDimensionType::kAbsoluteSmall},
+      {"100px", HTMLImageElement::LazyLoadDimensionType::kAbsoluteNotSmall},
+      {"100", HTMLImageElement::LazyLoadDimensionType::kAbsoluteNotSmall},
+  };
+  for (const auto& test : test_cases) {
+    EXPECT_EQ(test.expected_dimension_type,
+              HTMLImageElement::GetAttributeLazyLoadDimensionType(
+                  test.attribute_value));
+  }
+}
+
+TEST_F(HTMLImageElementTest, inlineStyleLazyLoadDimensionType) {
+  struct TestCase {
+    const char* inline_style;
+    HTMLImageElement::LazyLoadDimensionType expected_dimension_type;
+  };
+  const TestCase test_cases[] = {
+      {"", HTMLImageElement::LazyLoadDimensionType::kNotAbsolute},
+      {"invalid", HTMLImageElement::LazyLoadDimensionType::kNotAbsolute},
+      {"height: 1px", HTMLImageElement::LazyLoadDimensionType::kNotAbsolute},
+      {"width: 1px", HTMLImageElement::LazyLoadDimensionType::kNotAbsolute},
+      {"height: 1; width: 1",
+       HTMLImageElement::LazyLoadDimensionType::kNotAbsolute},
+      {"height: 50%; width: 50%",
+       HTMLImageElement::LazyLoadDimensionType::kNotAbsolute},
+      {"height: 1px; width: 1px",
+       HTMLImageElement::LazyLoadDimensionType::kAbsoluteSmall},
+      {"height: 10px; width: 10px",
+       HTMLImageElement::LazyLoadDimensionType::kAbsoluteSmall},
+      {"height: 100px; width: 10px",
+       HTMLImageElement::LazyLoadDimensionType::kAbsoluteNotSmall},
+      {"height: 10px; width: 100px",
+       HTMLImageElement::LazyLoadDimensionType::kAbsoluteNotSmall},
+      {"height: 100px; width: 100px",
+       HTMLImageElement::LazyLoadDimensionType::kAbsoluteNotSmall},
+      {"height: 100; width: 100",
+       HTMLImageElement::LazyLoadDimensionType::kNotAbsolute},
+      {"height: 100%; width: 100%",
+       HTMLImageElement::LazyLoadDimensionType::kNotAbsolute},
+  };
+  for (const auto& test : test_cases) {
+    const ImmutableCSSPropertyValueSet* property_set =
+        CSSParser::ParseInlineStyleDeclaration(
+            test.inline_style, kHTMLStandardMode,
+            SecureContextMode::kInsecureContext);
+    EXPECT_EQ(test.expected_dimension_type,
+              HTMLImageElement::GetInlineStyleDimensionsType(property_set));
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/lazy_load_image_observer_test.cc b/third_party/blink/renderer/core/html/lazy_load_image_observer_test.cc
index 7610f15..6eb71c3 100644
--- a/third_party/blink/renderer/core/html/lazy_load_image_observer_test.cc
+++ b/third_party/blink/renderer/core/html/lazy_load_image_observer_test.cc
@@ -38,29 +38,22 @@
       ->CopyAs<Vector<char>>();
 }
 
-class LazyLoadCSSImagesTest : public SimTest {
+class LazyLoadImagesSimTest : public ::testing::WithParamInterface<bool>,
+                              public SimTest {
  protected:
-  LazyLoadCSSImagesTest()
-      : scoped_lazy_image_loading_for_test_(true),
-        scoped_automatic_lazy_image_loading_for_test_(true) {}
+  LazyLoadImagesSimTest()
+      : scoped_lazy_image_loading_for_test_(GetParam()),
+        scoped_automatic_lazy_image_loading_for_test_(GetParam()) {}
+
   void SetLazyLoadEnabled(bool enabled) {
     WebView().GetPage()->GetSettings().SetLazyLoadEnabled(enabled);
   }
 
-  void LoadMainResource() {
+  void LoadMainResource(const String& html_body) {
     SimRequest main_resource("https://example.com/", "text/html");
     LoadURL("https://example.com/");
 
-    main_resource.Complete(String::Format(R"HTML(
-          <style>
-          #deferred_image {
-            height:200px;
-            background-image: url('img.png');
-          }
-          </style>
-          <div style='height:10000px;'></div>
-          <div id="deferred_image"></div>
-        )HTML"));
+    main_resource.Complete(html_body);
     GetDocument().UpdateStyleAndLayoutTree();
   }
 
@@ -82,40 +75,108 @@
     EXPECT_TRUE(is_background_image_found);
   }
 
+  void VerifyImageElementWithDimensionDeferred(const char* img_attribute) {
+    bool is_lazyload_image_enabled = GetParam();
+    SetLazyLoadEnabled(is_lazyload_image_enabled);
+    SimRequest image_resource("https://example.com/img.png", "image/png");
+    LoadMainResource(String::Format(R"HTML(
+        <body onload='console.log("main body onload");'>
+          <div style='height:10000px;'></div>
+          <img src="img.png" %s
+               onload= 'console.log("deferred_image onload");'>
+        </body>)HTML",
+                                    img_attribute));
+
+    if (!is_lazyload_image_enabled)
+      image_resource.Complete(ReadTestImage());
+
+    Compositor().BeginFrame();
+    test::RunPendingTasks();
+
+    EXPECT_TRUE(ConsoleMessages().Contains("main body onload"));
+    if (!is_lazyload_image_enabled)
+      EXPECT_TRUE(ConsoleMessages().Contains("deferred_image onload"));
+
+    if (is_lazyload_image_enabled) {
+      // Scroll down until the image element is visible.
+      GetDocument().View()->LayoutViewport()->SetScrollOffset(
+          ScrollOffset(0, 10000), kProgrammaticScroll);
+      Compositor().BeginFrame();
+      test::RunPendingTasks();
+      image_resource.Complete(ReadTestImage());
+      test::RunPendingTasks();
+      EXPECT_TRUE(ConsoleMessages().Contains("deferred_image onload"));
+    }
+  }
+
  private:
   ScopedLazyImageLoadingForTest scoped_lazy_image_loading_for_test_;
   ScopedAutomaticLazyImageLoadingForTest
       scoped_automatic_lazy_image_loading_for_test_;
 };
 
-TEST_F(LazyLoadCSSImagesTest, CSSBackgroundImageLoadedWithoutLazyLoad) {
-  SetLazyLoadEnabled(false);
+TEST_P(LazyLoadImagesSimTest, CSSBackgroundImage) {
+  bool is_lazyload_image_enabled = GetParam();
+  SetLazyLoadEnabled(is_lazyload_image_enabled);
   SimRequest image_resource("https://example.com/img.png", "image/png");
-  LoadMainResource();
-  image_resource.Complete(ReadTestImage());
+  LoadMainResource(String::Format(R"HTML(
+        <style>
+        #deferred_image {
+          height:200px;
+          background-image: url('img.png');
+        }
+        </style>
+        <div style='height:10000px;'></div>
+        <div id="deferred_image"></div>
+      )HTML"));
+
+  if (!is_lazyload_image_enabled)
+    image_resource.Complete(ReadTestImage());
+
   Compositor().BeginFrame();
   test::RunPendingTasks();
-  ExpectCSSBackgroundImageDeferredState(false);
+  ExpectCSSBackgroundImageDeferredState(is_lazyload_image_enabled);
+
+  if (is_lazyload_image_enabled) {
+    // Scroll down until the background image is visible.
+    GetDocument().View()->LayoutViewport()->SetScrollOffset(
+        ScrollOffset(0, 10000), kProgrammaticScroll);
+    Compositor().BeginFrame();
+    test::RunPendingTasks();
+    image_resource.Complete(ReadTestImage());
+    ExpectCSSBackgroundImageDeferredState(false);
+  }
 }
 
-TEST_F(LazyLoadCSSImagesTest, CSSBackgroundImageDeferredWithLazyLoad) {
-  SetLazyLoadEnabled(true);
-  LoadMainResource();
-  Compositor().BeginFrame();
-  test::RunPendingTasks();
-
-  ExpectCSSBackgroundImageDeferredState(true);
-
-  // Scroll down until the background image is visible.
-  GetDocument().View()->LayoutViewport()->SetScrollOffset(
-      ScrollOffset(0, 10000), kProgrammaticScroll);
-  SimRequest image_resource("https://example.com/img.png", "image/png");
-  Compositor().BeginFrame();
-  test::RunPendingTasks();
-  image_resource.Complete(ReadTestImage());
-  ExpectCSSBackgroundImageDeferredState(false);
+TEST_P(LazyLoadImagesSimTest, LargeImageHeight100Width100) {
+  VerifyImageElementWithDimensionDeferred("height='100px' width='100px'");
 }
 
+TEST_P(LazyLoadImagesSimTest, LargeImageHeight1Width100) {
+  VerifyImageElementWithDimensionDeferred("height='1px' width='100px'");
+}
+
+TEST_P(LazyLoadImagesSimTest, LargeImageHeight100Width1) {
+  VerifyImageElementWithDimensionDeferred("height='100px' width='1px'");
+}
+
+TEST_P(LazyLoadImagesSimTest, LargeImageStyleHeight100Width100) {
+  VerifyImageElementWithDimensionDeferred(
+      "style='height: 100px; width: 100px;'");
+}
+
+TEST_P(LazyLoadImagesSimTest, LargeImageStyleHeight100Width1) {
+  VerifyImageElementWithDimensionDeferred("style='height: 100px; width: 1px;'");
+}
+
+TEST_P(LazyLoadImagesSimTest, LargeImageStyleHeight1Width100) {
+  VerifyImageElementWithDimensionDeferred("style='height: 1px; width: 100px;'");
+}
+
+INSTANTIATE_TEST_SUITE_P(,
+                         LazyLoadImagesSimTest,
+                         ::testing::Bool() /*is_lazyload_image_enabled*/);
+
 SimRequestBase::Params BuildRequestParamsForRangeResponse() {
   SimRequestBase::Params params;
   params.response_http_headers = {{"content-range", "bytes 0-2047/9311"}};
@@ -614,6 +675,30 @@
     EXPECT_FALSE(ConsoleMessages().Contains("image onload"));
   }
 
+  void TestLoadImageExpectingLazyLoadWithoutPlaceholder(
+      const char* image_attributes) {
+    SimSubresourceRequest full_resource("https://example.com/image.png",
+                                        "image/png");
+
+    LoadMainResourceWithImageFarFromViewport(image_attributes);
+
+    EXPECT_TRUE(ConsoleMessages().Contains("main body onload"));
+    EXPECT_FALSE(ConsoleMessages().Contains("image onload"));
+
+    // Scrolling down should trigger the fetch of the image.
+    GetDocument().View()->LayoutViewport()->SetScrollOffset(
+        ScrollOffset(0, kLoadingDistanceThreshold + kViewportHeight),
+        kProgrammaticScroll);
+    Compositor().BeginFrame();
+    full_resource.Complete(ReadTestImage());
+    ExpectResourceIsFullImage(GetDocument().Fetcher()->CachedResource(
+        KURL("https://example.com/image.png")));
+    test::RunPendingTasks();
+
+    EXPECT_TRUE(ConsoleMessages().Contains("main body onload"));
+    EXPECT_TRUE(ConsoleMessages().Contains("image onload"));
+  }
+
   void TestLoadImageExpectingFullImageLoad(const char* image_attributes) {
     SimSubresourceRequest full_resource("https://example.com/image.png",
                                         "image/png");
@@ -729,7 +814,7 @@
 }
 
 TEST_F(LazyLoadAutomaticImagesTest, TinyImageWidth1Height11) {
-  TestLoadImageExpectingLazyLoad("width='1px' height='11px'");
+  TestLoadImageExpectingLazyLoadWithoutPlaceholder("width='1px' height='11px'");
 }
 
 TEST_F(LazyLoadAutomaticImagesTest, TinyImageViaStyleWidth1Height1) {
@@ -741,7 +826,8 @@
 }
 
 TEST_F(LazyLoadAutomaticImagesTest, TinyImageViaStyleWidth11Height1) {
-  TestLoadImageExpectingLazyLoad("style='width:11px;height:1px;'");
+  TestLoadImageExpectingLazyLoadWithoutPlaceholder(
+      "style='width:11px;height:1px;'");
 }
 
 TEST_F(LazyLoadAutomaticImagesTest, JavascriptCreatedImageFarFromViewport) {
diff --git a/third_party/blink/renderer/core/html/parser/html_preload_scanner.cc b/third_party/blink/renderer/core/html/parser/html_preload_scanner.cc
index a29dc57e..826124fb 100644
--- a/third_party/blink/renderer/core/html/parser/html_preload_scanner.cc
+++ b/third_party/blink/renderer/core/html/parser/html_preload_scanner.cc
@@ -52,6 +52,7 @@
 #include "third_party/blink/renderer/core/html/parser/html_tokenizer.h"
 #include "third_party/blink/renderer/core/html_names.h"
 #include "third_party/blink/renderer/core/input_type_names.h"
+#include "third_party/blink/renderer/core/loader/document_loader.h"
 #include "third_party/blink/renderer/core/loader/importance_attribute.h"
 #include "third_party/blink/renderer/core/loader/preload_helper.h"
 #include "third_party/blink/renderer/core/loader/subresource_integrity_helper.h"
@@ -151,9 +152,12 @@
         integrity_attr_set_(false),
         integrity_features_(features),
         loading_attr_value_(LoadingAttrValue::kAuto),
-        width_attr_small_absolute_(false),
-        height_attr_small_absolute_(false),
-        inline_style_dimensions_small_(false),
+        width_attr_dimension_type_(
+            HTMLImageElement::LazyLoadDimensionType::kNotAbsolute),
+        height_attr_dimension_type_(
+            HTMLImageElement::LazyLoadDimensionType::kNotAbsolute),
+        inline_style_dimensions_type_(
+            HTMLImageElement::LazyLoadDimensionType::kNotAbsolute),
         scanner_type_(scanner_type),
         priority_hints_origin_trial_enabled_(
             priority_hints_origin_trial_enabled) {
@@ -295,26 +299,44 @@
         document_parameters.lazyload_policy_enforced) {
       effective_loading_attr_value = LoadingAttrValue::kAuto;
     }
+    bool is_lazy_load_image_enabled = false;
     switch (effective_loading_attr_value) {
       case LoadingAttrValue::kEager:
-        request->SetLazyLoadImageEligibility(
-            PreloadRequest::LazyLoadImageEligibility::kDisabled);
+        is_lazy_load_image_enabled = false;
         break;
       case LoadingAttrValue::kLazy:
-        request->SetLazyLoadImageEligibility(
-            PreloadRequest::LazyLoadImageEligibility::kEnabledExplicit);
+        is_lazy_load_image_enabled =
+            document_parameters.lazy_load_image_enabled_state !=
+            LocalFrame::LazyLoadImageEnabledState::kDisabled;
         break;
       case LoadingAttrValue::kAuto:
-        if ((width_attr_small_absolute_ && height_attr_small_absolute_) ||
-            inline_style_dimensions_small_) {
-          request->SetLazyLoadImageEligibility(
-              PreloadRequest::LazyLoadImageEligibility::kDisabled);
+        if ((width_attr_dimension_type_ ==
+                 HTMLImageElement::LazyLoadDimensionType::kAbsoluteSmall &&
+             height_attr_dimension_type_ ==
+                 HTMLImageElement::LazyLoadDimensionType::kAbsoluteSmall) ||
+            inline_style_dimensions_type_ ==
+                HTMLImageElement::LazyLoadDimensionType::kAbsoluteSmall) {
+          is_lazy_load_image_enabled = false;
         } else {
-          request->SetLazyLoadImageEligibility(
-              PreloadRequest::LazyLoadImageEligibility::kEnabledAutomatic);
+          is_lazy_load_image_enabled =
+              document_parameters.lazy_load_image_enabled_state ==
+              LocalFrame::LazyLoadImageEnabledState::kEnabledAutomatic;
         }
         break;
     }
+    // LazyLoad: Do not preload if absolute dimensions are mentioned in width
+    // and height attributes or in the inline style, and the dimensions are not
+    // small enough.
+    if (is_lazy_load_image_enabled &&
+        ((width_attr_dimension_type_ ==
+              HTMLImageElement::LazyLoadDimensionType::kAbsoluteNotSmall &&
+          height_attr_dimension_type_ ==
+              HTMLImageElement::LazyLoadDimensionType::kAbsoluteNotSmall) ||
+         inline_style_dimensions_type_ ==
+             HTMLImageElement::LazyLoadDimensionType::kAbsoluteNotSmall)) {
+      return nullptr;
+    }
+    request->SetIsLazyLoadImageEnabled(is_lazy_load_image_enabled);
 
     // The only link tags that should keep the integrity metadata are
     // stylesheets until crbug.com/677022 is resolved.
@@ -395,19 +417,20 @@
               : EqualIgnoringASCIICase(attribute_value, "lazy")
                     ? LoadingAttrValue::kLazy
                     : LoadingAttrValue::kAuto;
-    } else if (!width_attr_small_absolute_ &&
+    } else if (width_attr_dimension_type_ ==
+                   HTMLImageElement::LazyLoadDimensionType::kNotAbsolute &&
                Match(attribute_name, kWidthAttr) &&
                RuntimeEnabledFeatures::LazyImageLoadingEnabled()) {
-      width_attr_small_absolute_ =
-          HTMLImageElement::IsDimensionSmallAndAbsoluteForLazyLoad(
-              attribute_value);
-    } else if (!height_attr_small_absolute_ &&
+      width_attr_dimension_type_ =
+          HTMLImageElement::GetAttributeLazyLoadDimensionType(attribute_value);
+    } else if (height_attr_dimension_type_ ==
+                   HTMLImageElement::LazyLoadDimensionType::kNotAbsolute &&
                Match(attribute_name, kHeightAttr) &&
                RuntimeEnabledFeatures::LazyImageLoadingEnabled()) {
-      height_attr_small_absolute_ =
-          HTMLImageElement::IsDimensionSmallAndAbsoluteForLazyLoad(
-              attribute_value);
-    } else if (!inline_style_dimensions_small_ &&
+      height_attr_dimension_type_ =
+          HTMLImageElement::GetAttributeLazyLoadDimensionType(attribute_value);
+    } else if (inline_style_dimensions_type_ ==
+                   HTMLImageElement::LazyLoadDimensionType::kNotAbsolute &&
                Match(attribute_name, kStyleAttr) &&
                RuntimeEnabledFeatures::LazyImageLoadingEnabled()) {
       CSSParserMode mode =
@@ -415,8 +438,8 @@
       const ImmutableCSSPropertyValueSet* property_set =
           CSSParser::ParseInlineStyleDeclaration(
               attribute_value, mode, SecureContextMode::kInsecureContext);
-      inline_style_dimensions_small_ =
-          HTMLImageElement::IsInlineStyleDimensionsSmall(property_set);
+      inline_style_dimensions_type_ =
+          HTMLImageElement::GetInlineStyleDimensionsType(property_set);
     }
   }
 
@@ -717,9 +740,9 @@
   IntegrityMetadataSet integrity_metadata_;
   SubresourceIntegrity::IntegrityFeatures integrity_features_;
   LoadingAttrValue loading_attr_value_;
-  bool width_attr_small_absolute_;
-  bool height_attr_small_absolute_;
-  bool inline_style_dimensions_small_;
+  HTMLImageElement::LazyLoadDimensionType width_attr_dimension_type_;
+  HTMLImageElement::LazyLoadDimensionType height_attr_dimension_type_;
+  HTMLImageElement::LazyLoadDimensionType inline_style_dimensions_type_;
   TokenPreloadScanner::ScannerType scanner_type_;
   // For explanation, see TokenPreloadScanner's declaration.
   bool priority_hints_origin_trial_enabled_;
@@ -1063,6 +1086,13 @@
   referrer_policy = document->GetReferrerPolicy();
   integrity_features = SubresourceIntegrityHelper::GetFeatures(document);
   lazyload_policy_enforced = document->IsLazyLoadPolicyEnforced();
+  if (document->Loader() && document->Loader()->GetFrame()) {
+    lazy_load_image_enabled_state =
+        document->Loader()->GetFrame()->GetLazyLoadImageEnabledState();
+  } else {
+    lazy_load_image_enabled_state =
+        LocalFrame::LazyLoadImageEnabledState::kDisabled;
+  }
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/parser/html_preload_scanner.h b/third_party/blink/renderer/core/html/parser/html_preload_scanner.h
index 45f6493..90c3cfc8 100644
--- a/third_party/blink/renderer/core/html/parser/html_preload_scanner.h
+++ b/third_party/blink/renderer/core/html/parser/html_preload_scanner.h
@@ -35,6 +35,7 @@
 #include "base/optional.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/css/media_values_cached.h"
+#include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/html/parser/compact_html_token.h"
 #include "third_party/blink/renderer/core/html/parser/css_preload_scanner.h"
 #include "third_party/blink/renderer/core/html/parser/html_token.h"
@@ -55,6 +56,12 @@
   USING_FAST_MALLOC(CachedDocumentParameters);
 
  public:
+  enum class LazyLoadImageEnabledState {
+    kDisabled,
+    kEnabledExplicit,
+    kEnabledAutomatic
+  };
+
   explicit CachedDocumentParameters(Document*);
   CachedDocumentParameters() = default;
 
@@ -65,6 +72,7 @@
   network::mojom::ReferrerPolicy referrer_policy;
   SubresourceIntegrity::IntegrityFeatures integrity_features;
   bool lazyload_policy_enforced;
+  LocalFrame::LazyLoadImageEnabledState lazy_load_image_enabled_state;
 };
 
 class TokenPreloadScanner {
diff --git a/third_party/blink/renderer/core/html/parser/html_preload_scanner_test.cc b/third_party/blink/renderer/core/html/parser/html_preload_scanner_test.cc
index ab17347..81c536a 100644
--- a/third_party/blink/renderer/core/html/parser/html_preload_scanner_test.cc
+++ b/third_party/blink/renderer/core/html/parser/html_preload_scanner_test.cc
@@ -88,7 +88,7 @@
 
 struct LazyLoadImageTestCase {
   const char* input_html;
-  PreloadRequest::LazyLoadImageEligibility lazy_load_image_eligibility;
+  bool lazy_load_image_enabled;
 };
 
 class HTMLMockHTMLResourcePreloader : public ResourcePreloader {
@@ -191,11 +191,10 @@
     }
   }
 
-  void LazyLoadImageEligibilityVerification(
-      PreloadRequest::LazyLoadImageEligibility expected_eligibility) {
+  void LazyLoadImageEnabledVerification(bool expected_enabled) {
     ASSERT_TRUE(preload_request_.get());
-    EXPECT_EQ(expected_eligibility,
-              preload_request_->GetLazyLoadImageEligibilityForTesting());
+    EXPECT_EQ(expected_enabled,
+              preload_request_->IsLazyLoadImageEnabledForTesting());
   }
 
  protected:
@@ -374,8 +373,8 @@
     PreloadRequestStream requests =
         scanner_->Scan(base_url, nullptr, seen_csp_meta_tag_);
     preloader.TakeAndPreload(requests);
-    preloader.LazyLoadImageEligibilityVerification(
-        test_case.lazy_load_image_eligibility);
+    preloader.LazyLoadImageEnabledVerification(
+        test_case.lazy_load_image_enabled);
   }
 
  private:
@@ -1212,54 +1211,142 @@
     Test(test_case);
 }
 
-TEST_F(HTMLPreloadScannerTest, LazyLoadImageDisabledForSmallImages) {
+TEST_F(HTMLPreloadScannerTest, LazyLoadImage_DisabledForSmallImages) {
   ScopedLazyImageLoadingForTest scoped_lazy_image_loading_for_test(true);
+  ScopedAutomaticLazyImageLoadingForTest
+      scoped_automatic_lazy_image_loading_for_test(true);
+  GetDocument().GetSettings()->SetLazyLoadEnabled(true);
+  RunSetUp(kViewportEnabled);
   LazyLoadImageTestCase test_cases[] = {
-      {"<img src='foo.jpg'>",
-       PreloadRequest::LazyLoadImageEligibility::kEnabledAutomatic},
-      {"<img src='foo.jpg' height='1px' width='1px'>",
-       PreloadRequest::LazyLoadImageEligibility::kDisabled},
-      {"<img src='foo.jpg' style='height: 1px; width: 1px'>",
-       PreloadRequest::LazyLoadImageEligibility::kDisabled},
-      {"<img src='foo.jpg' height='10px' width='10px'>",
-       PreloadRequest::LazyLoadImageEligibility::kDisabled},
-      {"<img src='foo.jpg' style='height: 10px; width: 10px'>",
-       PreloadRequest::LazyLoadImageEligibility::kDisabled},
-      {"<img src='foo.jpg' height='20px' width='20px'>",
-       PreloadRequest::LazyLoadImageEligibility::kEnabledAutomatic},
-      {"<img src='foo.jpg' style='height: 20px; width: 20px'>",
-       PreloadRequest::LazyLoadImageEligibility::kEnabledAutomatic},
-      {"<img src='foo.jpg' height='1px'>",
-       PreloadRequest::LazyLoadImageEligibility::kEnabledAutomatic},
-      {"<img src='foo.jpg' style='height: 1px;'>",
-       PreloadRequest::LazyLoadImageEligibility::kEnabledAutomatic},
-      {"<img src='foo.jpg' width='1px'>",
-       PreloadRequest::LazyLoadImageEligibility::kEnabledAutomatic},
-      {"<img src='foo.jpg' style='width: 1px;'>",
-       PreloadRequest::LazyLoadImageEligibility::kEnabledAutomatic},
+      {"<img src='foo.jpg'>", true},
+      {"<img src='foo.jpg' height='1px' width='1px'>", false},
+      {"<img src='foo.jpg' style='height: 1px; width: 1px'>", false},
+      {"<img src='foo.jpg' height='10px' width='10px'>", false},
+      {"<img src='foo.jpg' style='height: 10px; width: 10px'>", false},
+      {"<img src='foo.jpg' height='1px'>", true},
+      {"<img src='foo.jpg' style='height: 1px;'>", true},
+      {"<img src='foo.jpg' width='1px'>", true},
+      {"<img src='foo.jpg' style='width: 1px;'>", true},
   };
 
   for (const auto& test_case : test_cases)
     Test(test_case);
 }
 
-TEST_F(HTMLPreloadScannerTest, LazyLoadImageAttributePassed) {
-  ScopedLazyImageLoadingForTest scoped_lazy_image_loading_for_test(true);
+TEST_F(HTMLPreloadScannerTest, LazyLoadImage_FeatureDisabledWithAttribute) {
+  GetDocument().GetSettings()->SetLazyLoadEnabled(true);
+  RunSetUp(kViewportEnabled);
   LazyLoadImageTestCase test_cases[] = {
-      {"<img src='foo.jpg' loading='auto'>",
-       PreloadRequest::LazyLoadImageEligibility::kEnabledAutomatic},
-      {"<img src='foo.jpg' loading='lazy'>",
-       PreloadRequest::LazyLoadImageEligibility::kEnabledExplicit},
-      {"<img src='foo.jpg' loading='eager'>",
-       PreloadRequest::LazyLoadImageEligibility::kDisabled},
-      // loading=lazy should override other conditions.
-      {"<img src='foo.jpg' style='height: 1px;' loading='lazy'>",
-       PreloadRequest::LazyLoadImageEligibility::kEnabledExplicit},
-      {"<img src='foo.jpg' style='height: 1px; width: 1px' loading='lazy'>",
-       PreloadRequest::LazyLoadImageEligibility::kEnabledExplicit},
+      {"<img src='foo.jpg' loading='auto'>", false},
+      {"<img src='foo.jpg' loading='lazy'>", false},
+      {"<img src='foo.jpg' loading='eager'>", false},
   };
   for (const auto& test_case : test_cases)
     Test(test_case);
 }
 
+TEST_F(HTMLPreloadScannerTest,
+       LazyLoadImage_FeatureAutomaticEnabledWithAttribute) {
+  ScopedLazyImageLoadingForTest scoped_lazy_image_loading_for_test(true);
+  ScopedAutomaticLazyImageLoadingForTest
+      scoped_automatic_lazy_image_loading_for_test(true);
+  GetDocument().GetSettings()->SetLazyLoadEnabled(true);
+  RunSetUp(kViewportEnabled);
+  LazyLoadImageTestCase test_cases[] = {
+      {"<img src='foo.jpg' loading='auto'>", true},
+      {"<img src='foo.jpg' loading='lazy'>", true},
+      {"<img src='foo.jpg' loading='eager'>", false},
+      // loading=lazy should override other conditions.
+      {"<img src='foo.jpg' style='height: 1px;' loading='lazy'>", true},
+      {"<img src='foo.jpg' style='height: 1px; width: 1px' loading='lazy'>",
+       true},
+  };
+  for (const auto& test_case : test_cases)
+    Test(test_case);
+}
+
+TEST_F(HTMLPreloadScannerTest,
+       LazyLoadImage_FeatureExplicitEnabledWithAttribute) {
+  ScopedLazyImageLoadingForTest scoped_lazy_image_loading_for_test(true);
+  GetDocument().GetSettings()->SetLazyLoadEnabled(true);
+  RunSetUp(kViewportEnabled);
+  LazyLoadImageTestCase test_cases[] = {
+      {"<img src='foo.jpg' loading='auto'>", false},
+      {"<img src='foo.jpg' loading='lazy'>", true},
+      {"<img src='foo.jpg' loading='eager'>", false},
+  };
+  for (const auto& test_case : test_cases)
+    Test(test_case);
+}
+
+TEST_F(HTMLPreloadScannerTest,
+       LazyLoadImage_FeatureAutomaticPreloadForLargeImages) {
+  // Large images should not be preloaded, when loading is auto or lazy.
+  ScopedLazyImageLoadingForTest scoped_lazy_image_loading_for_test(true);
+  ScopedAutomaticLazyImageLoadingForTest
+      scoped_automatic_lazy_image_loading_for_test(true);
+  GetDocument().GetSettings()->SetLazyLoadEnabled(true);
+  RunSetUp(kViewportEnabled);
+  PreloadScannerTestCase test_cases[] = {
+      {"http://example.test", "<img src='foo.jpg' height='20px' width='20px'>",
+       nullptr, "http://example.test/", ResourceType::kImage, 0},
+      {"http://example.test",
+       "<img src='foo.jpg' style='height: 20px; width: 20px'>", nullptr,
+       "http://example.test/", ResourceType::kImage, 0},
+      {"http://example.test",
+       "<img src='foo.jpg' height='20px' width='20px' loading='lazy'>", nullptr,
+       "http://example.test/", ResourceType::kImage, 0},
+      {"http://example.test",
+       "<img src='foo.jpg' height='20px' width='20px' loading='auto'>", nullptr,
+       "http://example.test/", ResourceType::kImage, 0},
+      {"http://example.test",
+       "<img src='foo.jpg' style='height: 20px; width: 20px' loading='lazy'>",
+       nullptr, "http://example.test/", ResourceType::kImage, 0},
+      {"http://example.test",
+       "<img src='foo.jpg' style='height: 20px; width: 20px' loading='auto'>",
+       nullptr, "http://example.test/", ResourceType::kImage, 0},
+  };
+  for (const auto& test_case : test_cases)
+    Test(test_case);
+
+  // When loading is eager, lazyload is disabled and preload happens.
+  LazyLoadImageTestCase test_cases_that_preload[] = {
+      {"<img src='foo.jpg' height='20px' width='20px' loading='eager'>", false},
+      {"<img src='foo.jpg' style='height: 20px; width: 20px' loading='eager'>",
+       false},
+  };
+  for (const auto& test_case : test_cases_that_preload)
+    Test(test_case);
+}
+
+TEST_F(HTMLPreloadScannerTest,
+       LazyLoadImage_FeatureExplicitPreloadForLargeImages) {
+  // Large images should not be preloaded, when loading is lazy.
+  ScopedLazyImageLoadingForTest scoped_lazy_image_loading_for_test(true);
+  GetDocument().GetSettings()->SetLazyLoadEnabled(true);
+  RunSetUp(kViewportEnabled);
+  PreloadScannerTestCase test_cases[] = {
+      {"http://example.test",
+       "<img src='foo.jpg' height='20px' width='20px' loading='lazy'>", nullptr,
+       "http://example.test/", ResourceType::kImage, 0},
+      {"http://example.test",
+       "<img src='foo.jpg' style='height: 20px; width: 20px' loading='lazy'>",
+       nullptr, "http://example.test/", ResourceType::kImage, 0},
+  };
+  for (const auto& test_case : test_cases)
+    Test(test_case);
+
+  // When loading is eager or auto, lazyload is disabled and preload happens.
+  LazyLoadImageTestCase test_cases_that_preload[] = {
+      {"<img src='foo.jpg' height='20px' width='20px' loading='eager'>", false},
+      {"<img src='foo.jpg' style='height: 20px; width: 20px' loading='eager'>",
+       false},
+      {"<img src='foo.jpg' height='20px' width='20px' loading='auto'>", false},
+      {"<img src='foo.jpg' style='height: 20px; width: 20px' loading='auto'>",
+       false},
+  };
+  for (const auto& test_case : test_cases_that_preload)
+    Test(test_case);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/parser/preload_request.cc b/third_party/blink/renderer/core/html/parser/preload_request.cc
index e61101f3..8302279 100644
--- a/third_party/blink/renderer/core/html/parser/preload_request.cc
+++ b/third_party/blink/renderer/core/html/parser/preload_request.cc
@@ -108,12 +108,7 @@
     if (const auto* frame = document->Loader()->GetFrame()) {
       if (frame->IsClientLoFiAllowed(params.GetResourceRequest())) {
         params.SetClientLoFiPlaceholder();
-      } else if ((lazy_load_image_eligibility_ ==
-                      LazyLoadImageEligibility::kEnabledExplicit &&
-                  frame->IsExplicitLazyLoadingImageAllowed()) ||
-                 (lazy_load_image_eligibility_ ==
-                      LazyLoadImageEligibility::kEnabledAutomatic &&
-                  frame->IsAutomaticLazyLoadingImageAllowed())) {
+      } else if (is_lazy_load_image_enabled_) {
         params.SetLazyImagePlaceholder();
       }
     }
diff --git a/third_party/blink/renderer/core/html/parser/preload_request.h b/third_party/blink/renderer/core/html/parser/preload_request.h
index 03206745..4894623 100644
--- a/third_party/blink/renderer/core/html/parser/preload_request.h
+++ b/third_party/blink/renderer/core/html/parser/preload_request.h
@@ -126,16 +126,11 @@
     return is_image_set_ == ResourceFetcher::kImageIsImageSet;
   }
 
-  enum class LazyLoadImageEligibility {
-    kDisabled,
-    kEnabledExplicit,
-    kEnabledAutomatic
-  };
-  void SetLazyLoadImageEligibility(LazyLoadImageEligibility eligibility) {
-    lazy_load_image_eligibility_ = eligibility;
+  void SetIsLazyLoadImageEnabled(bool is_enabled) {
+    is_lazy_load_image_enabled_ = is_enabled;
   }
-  LazyLoadImageEligibility GetLazyLoadImageEligibilityForTesting() const {
-    return lazy_load_image_eligibility_;
+  bool IsLazyLoadImageEnabledForTesting() {
+    return is_lazy_load_image_enabled_;
   }
 
  private:
@@ -166,7 +161,7 @@
         referrer_source_(referrer_source),
         from_insertion_scanner_(false),
         is_image_set_(is_image_set),
-        lazy_load_image_eligibility_(LazyLoadImageEligibility::kDisabled) {}
+        is_lazy_load_image_enabled_(false) {}
 
   KURL CompleteURL(Document*);
 
@@ -189,7 +184,7 @@
   IntegrityMetadataSet integrity_metadata_;
   bool from_insertion_scanner_;
   ResourceFetcher::IsImageSet is_image_set_;
-  LazyLoadImageEligibility lazy_load_image_eligibility_;
+  bool is_lazy_load_image_enabled_;
 };
 
 typedef Vector<std::unique_ptr<PreloadRequest>> PreloadRequestStream;
diff --git a/third_party/blink/renderer/core/inspector/browser_protocol.pdl b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
index a903962..4ece221 100644
--- a/third_party/blink/renderer/core/inspector/browser_protocol.pdl
+++ b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
@@ -2362,8 +2362,9 @@
       LayoutTreeSnapshot layout
       # The post-layout inline text nodes.
       TextBoxSnapshot textBoxes
-      # Scroll offsets.
+      # Horizontal scroll offset.
       optional number scrollOffsetX
+      # Vertical scroll offset.
       optional number scrollOffsetY
 
   # Table containing nodes.
@@ -2402,12 +2403,12 @@
       # The url of the script (if any) that generates this node.
       optional RareStringData originURL
 
-  # Details of an element in the DOM tree with a LayoutObject.
+  # Table of details of an element in the DOM tree with a LayoutObject.
   type LayoutTreeSnapshot extends object
     properties
-      # The index of the related DOM node in the `domNodes` array returned by `getSnapshot`.
+      # Index of the corresponding node in the `NodeTreeSnapshot` array returned by `captureSnapshot`.
       array of integer nodeIndex
-      # Index into the `computedStyles` array returned by `captureSnapshot`.
+      # Array of indexes specifying computed style strings, filtered according to the `computedStyles` parameter passed to `captureSnapshot`.
       array of ArrayOfStrings styles
       # The absolute position bounding box.
       array of Rectangle bounds
@@ -2416,11 +2417,11 @@
       # Stacking context information.
       RareBooleanData stackingContexts
 
-  # Details of post layout rendered text positions. The exact layout should not be regarded as
+  # Table of details of the post layout rendered text positions. The exact layout should not be regarded as
   # stable and may change between versions.
   type TextBoxSnapshot extends object
     properties
-      # Intex of th elayout tree node that owns this box collection.
+      # Index of the layout tree node that owns this box collection.
       array of integer layoutIndex
       # The absolute position bounding box.
       array of Rectangle bounds
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc
index b9ba0d0..39f4f0a 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc
@@ -640,6 +640,10 @@
   if (space <= 0)
     return false;
 
+  // Can't justify an empty string.
+  if (end_offset == line_info->StartOffset())
+    return false;
+
   // Construct the line text to compute spacing for.
   StringBuilder line_text_builder;
   line_text_builder.Append(StringView(line_info->ItemsData().text_content,
@@ -656,6 +660,8 @@
 
   // Compute the spacing to justify.
   String line_text = line_text_builder.ToString();
+  DCHECK_GT(line_text.length(), 0u);
+
   ShapeResultSpacing<String> spacing(line_text);
   spacing.SetExpansion(space, line_info->BaseDirection(),
                        line_info->LineStyle().GetTextJustify());
diff --git a/third_party/blink/renderer/core/loader/image_loader.cc b/third_party/blink/renderer/core/loader/image_loader.cc
index da591db..0ecee4c 100644
--- a/third_party/blink/renderer/core/loader/image_loader.cc
+++ b/third_party/blink/renderer/core/loader/image_loader.cc
@@ -101,21 +101,45 @@
 
   // Avoid automatically lazyloading if width and height attributes are small.
   // This heuristic helps avoid double fetching tracking pixels.
-  if (HTMLImageElement::IsDimensionSmallAndAbsoluteForLazyLoad(
-          html_image.FastGetAttribute(html_names::kWidthAttr)) &&
-      HTMLImageElement::IsDimensionSmallAndAbsoluteForLazyLoad(
-          html_image.FastGetAttribute(html_names::kHeightAttr))) {
+  if (HTMLImageElement::GetAttributeLazyLoadDimensionType(
+          html_image.FastGetAttribute(html_names::kWidthAttr)) ==
+          HTMLImageElement::LazyLoadDimensionType::kAbsoluteSmall &&
+      HTMLImageElement::GetAttributeLazyLoadDimensionType(
+          html_image.FastGetAttribute(html_names::kHeightAttr)) ==
+          HTMLImageElement::LazyLoadDimensionType::kAbsoluteSmall) {
     return LazyLoadImageEligibility::kDisabled;
   }
   // Avoid automatically lazyloading if width or height is specified in inline
   // style and is small enough. This heuristic helps avoid double fetching
   // tracking pixels.
-  if (HTMLImageElement::IsInlineStyleDimensionsSmall(html_image.InlineStyle()))
+  if (HTMLImageElement::GetInlineStyleDimensionsType(
+          html_image.InlineStyle()) ==
+      HTMLImageElement::LazyLoadDimensionType::kAbsoluteSmall) {
     return LazyLoadImageEligibility::kDisabled;
+  }
 
   return LazyLoadImageEligibility::kEnabledAutomatic;
 }
 
+// Returns true if absolute dimension is specified in the width and height
+// attributes or in the inline style.
+bool IsDimensionAbsoluteLarge(const HTMLImageElement& html_image) {
+  if (HTMLImageElement::GetAttributeLazyLoadDimensionType(
+          html_image.FastGetAttribute(html_names::kWidthAttr)) ==
+          HTMLImageElement::LazyLoadDimensionType::kAbsoluteNotSmall ||
+      HTMLImageElement::GetAttributeLazyLoadDimensionType(
+          html_image.FastGetAttribute(html_names::kHeightAttr)) ==
+          HTMLImageElement::LazyLoadDimensionType::kAbsoluteNotSmall) {
+    return true;
+  }
+  if (HTMLImageElement::GetInlineStyleDimensionsType(
+          html_image.InlineStyle()) ==
+      HTMLImageElement::LazyLoadDimensionType::kAbsoluteNotSmall) {
+    return true;
+  }
+  return false;
+}
+
 bool CheckForUnoptimizedImagePolicy(const Document& document,
                                     ImageResourceContent* new_image) {
   if (!new_image)
@@ -538,14 +562,22 @@
         const LazyLoadImageEligibility lazy_load_image_eligibility =
             DetermineLazyLoadImageEligibility(*frame, *html_image,
                                               params.Url());
+        const auto lazy_load_image_enabled_state =
+            frame->GetLazyLoadImageEnabledState();
 
         if ((lazy_load_image_eligibility ==
                  LazyLoadImageEligibility::kEnabledExplicit &&
-             frame->IsExplicitLazyLoadingImageAllowed()) ||
+             lazy_load_image_enabled_state !=
+                 LocalFrame::LazyLoadImageEnabledState::kDisabled) ||
             (lazy_load_image_eligibility ==
                  LazyLoadImageEligibility::kEnabledAutomatic &&
-             frame->IsAutomaticLazyLoadingImageAllowed())) {
-          params.SetLazyImagePlaceholder();
+             lazy_load_image_enabled_state ==
+                 LocalFrame::LazyLoadImageEnabledState::kEnabledAutomatic)) {
+          if (IsDimensionAbsoluteLarge(*html_image)) {
+            params.SetLazyImageDeferred();
+          } else {
+            params.SetLazyImagePlaceholder();
+          }
           lazy_image_load_state_ = LazyImageLoadState::kDeferred;
         }
 
diff --git a/third_party/blink/renderer/core/loader/image_loader.h b/third_party/blink/renderer/core/loader/image_loader.h
index 1cde362..26093ff 100644
--- a/third_party/blink/renderer/core/loader/image_loader.h
+++ b/third_party/blink/renderer/core/loader/image_loader.h
@@ -143,9 +143,10 @@
   // https://docs.google.com/document/d/1Ym0EOwyZJmaB5afnCVPu0SFb8EWLBj_facm2fK9kgC0/
   enum class LazyImageLoadState {
     kNone,      // LazyImages not active.
-    kDeferred,  // Placeholder is loading/loaded. Full image load not started.
-                // Once the placeholder is loaded, document load event is
-                // unblocked, but image load event is not fired yet.
+    kDeferred,  // Full image load not started, and image load event will not be
+                // fired. If image dimensions is present, document load event
+                // will be unblocked. Otherwise placeholder fetch will start,
+                // and once its done document load event is unblocked.
     kFullImage  // Full image is loading/loaded, due to element coming near the
                 // viewport or if a placeholder load actually fetched the full
                 // image. image_complete_ can differentiate if the fetch is
diff --git a/third_party/blink/renderer/core/loader/private/prerender_handle.cc b/third_party/blink/renderer/core/loader/private/prerender_handle.cc
index bc1ccb7..dfc2381 100644
--- a/third_party/blink/renderer/core/loader/private/prerender_handle.cc
+++ b/third_party/blink/renderer/core/loader/private/prerender_handle.cc
@@ -54,7 +54,8 @@
   auto* prerender = MakeGarbageCollected<Prerender>(
       client, url, prerender_rel_types,
       SecurityPolicy::GenerateReferrer(document.GetReferrerPolicy(), url,
-                                       document.OutgoingReferrer()));
+                                       document.OutgoingReferrer()),
+      document.GetSecurityOrigin());
 
   PrerendererClient* prerenderer_client =
       PrerendererClient::From(document.GetPage());
diff --git a/third_party/blink/renderer/core/paint/ellipsis_box_painter.cc b/third_party/blink/renderer/core/paint/ellipsis_box_painter.cc
index fb614bf4..73b6145 100644
--- a/third_party/blink/renderer/core/paint/ellipsis_box_painter.cc
+++ b/third_party/blink/renderer/core/paint/ellipsis_box_painter.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/core/paint/ellipsis_box_painter.h"
 
 #include "third_party/blink/renderer/core/content_capture/content_holder.h"
+#include "third_party/blink/renderer/core/layout/api/line_layout_api_shim.h"
 #include "third_party/blink/renderer/core/layout/api/line_layout_item.h"
 #include "third_party/blink/renderer/core/layout/line/ellipsis_box.h"
 #include "third_party/blink/renderer/core/layout/line/root_inline_box.h"
@@ -74,20 +75,15 @@
   // TODO(npm): Check that there are non-whitespace characters. See
   // crbug.com/788444.
   context.GetPaintController().SetTextPainted();
-  // We should consider using the text node as the tracking node, instead of
-  // the line layout item.
-  Node* node = ellipsis_box_.GetLineLayoutItem().GetNode();
-  if (!node)
-    return;
 
-  if ((RuntimeEnabledFeatures::FirstContentfulPaintPlusPlusEnabled() ||
-       RuntimeEnabledFeatures::ElementTimingEnabled(&node->GetDocument()))) {
+  if (RuntimeEnabledFeatures::FirstContentfulPaintPlusPlusEnabled() ||
+      RuntimeEnabledFeatures::ElementTimingEnabled(
+          &LineLayoutAPIShim::LayoutObjectFrom(
+               ellipsis_box_.GetLineLayoutItem())
+               ->GetDocument())) {
     if (!font.ShouldSkipDrawing() &&
-        paint_info.phase == PaintPhase::kForeground) {
-      PaintTimingDetector::NotifyTextPaint(
-          *node->GetLayoutObject(), paint_info.context.GetPaintController()
-                                        .CurrentPaintChunkProperties());
-    }
+        paint_info.phase == PaintPhase::kForeground)
+      PaintTimingDetector::NotifyTextPaint(ellipsis_box_.VisualRect());
   }
 }
 
diff --git a/third_party/blink/renderer/core/paint/file_upload_control_painter.cc b/third_party/blink/renderer/core/paint/file_upload_control_painter.cc
index e1325355..7e1e094 100644
--- a/third_party/blink/renderer/core/paint/file_upload_control_painter.cc
+++ b/third_party/blink/renderer/core/paint/file_upload_control_painter.cc
@@ -81,13 +81,14 @@
     if ((RuntimeEnabledFeatures::FirstContentfulPaintPlusPlusEnabled() ||
          RuntimeEnabledFeatures::ElementTimingEnabled(
              &layout_file_upload_control_.GetDocument()))) {
-      base::Optional<ScopedPaintTimingDetectorBlockPaintHook>
-          scoped_paint_timing_detector_block_paint_hook(
-              layout_file_upload_control_);
       if (!font.ShouldSkipDrawing()) {
+        ScopedPaintTimingDetectorBlockPaintHook
+            scoped_paint_timing_detector_block_paint_hook(
+                layout_file_upload_control_,
+                paint_info.context.GetPaintController()
+                    .CurrentPaintChunkProperties());
         PaintTimingDetector::NotifyTextPaint(
-            layout_file_upload_control_, paint_info.context.GetPaintController()
-                                             .CurrentPaintChunkProperties());
+            layout_file_upload_control_.FragmentsVisualRectBoundingBox());
       }
     }
   }
diff --git a/third_party/blink/renderer/core/paint/inline_painter.cc b/third_party/blink/renderer/core/paint/inline_painter.cc
index aa37ff2f..316a720 100644
--- a/third_party/blink/renderer/core/paint/inline_painter.cc
+++ b/third_party/blink/renderer/core/paint/inline_painter.cc
@@ -35,8 +35,11 @@
   if (RuntimeEnabledFeatures::FirstContentfulPaintPlusPlusEnabled() ||
       RuntimeEnabledFeatures::ElementTimingEnabled(
           &layout_inline_.GetDocument())) {
-    if (paint_info.phase == PaintPhase::kForeground)
-      scoped_paint_timing_detector_block_paint_hook.emplace(layout_inline_);
+    if (paint_info.phase == PaintPhase::kForeground) {
+      scoped_paint_timing_detector_block_paint_hook.emplace(
+          layout_inline_, paint_info.context.GetPaintController()
+                              .CurrentPaintChunkProperties());
+    }
   }
 
   if (layout_inline_.IsInLayoutNGInlineFormattingContext()) {
diff --git a/third_party/blink/renderer/core/paint/inline_text_box_painter.cc b/third_party/blink/renderer/core/paint/inline_text_box_painter.cc
index c0d4cd2..e52b149 100644
--- a/third_party/blink/renderer/core/paint/inline_text_box_painter.cc
+++ b/third_party/blink/renderer/core/paint/inline_text_box_painter.cc
@@ -435,11 +435,8 @@
        RuntimeEnabledFeatures::ElementTimingEnabled(
            &InlineLayoutObject().GetDocument()))) {
     if (!font.ShouldSkipDrawing() &&
-        paint_info.phase == PaintPhase::kForeground) {
-      PaintTimingDetector::NotifyTextPaint(
-          InlineLayoutObject(), paint_info.context.GetPaintController()
-                                    .CurrentPaintChunkProperties());
-    }
+        paint_info.phase == PaintPhase::kForeground)
+      PaintTimingDetector::NotifyTextPaint(inline_text_box_.VisualRect());
   }
 }
 
diff --git a/third_party/blink/renderer/core/paint/line_box_list_painter.cc b/third_party/blink/renderer/core/paint/line_box_list_painter.cc
index 1cb080a3..fc0b419 100644
--- a/third_party/blink/renderer/core/paint/line_box_list_painter.cc
+++ b/third_party/blink/renderer/core/paint/line_box_list_painter.cc
@@ -67,8 +67,11 @@
   if (RuntimeEnabledFeatures::FirstContentfulPaintPlusPlusEnabled() ||
       RuntimeEnabledFeatures::ElementTimingEnabled(
           &layout_object.GetDocument())) {
-    if (paint_info.phase == PaintPhase::kForeground)
-      scoped_paint_timing_detector_block_paint_hook.emplace(layout_object);
+    if (paint_info.phase == PaintPhase::kForeground) {
+      scoped_paint_timing_detector_block_paint_hook.emplace(
+          layout_object, paint_info.context.GetPaintController()
+                             .CurrentPaintChunkProperties());
+    }
   }
 
   // See if our root lines intersect with the dirty rect. If so, then we paint
diff --git a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
index 664392f..8a08e1e 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
@@ -720,7 +720,9 @@
   if (RuntimeEnabledFeatures::FirstContentfulPaintPlusPlusEnabled() ||
       RuntimeEnabledFeatures::ElementTimingEnabled(
           &layout_block.GetDocument())) {
-    scoped_paint_timing_detector_block_paint_hook.emplace(layout_block);
+    scoped_paint_timing_detector_block_paint_hook.emplace(
+        layout_block,
+        paint_info.context.GetPaintController().CurrentPaintChunkProperties());
   }
 
   const bool is_horizontal = box_fragment_.Style().IsHorizontalWritingMode();
diff --git a/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter.cc b/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter.cc
index 9270910..711f7e66 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter.cc
@@ -386,8 +386,8 @@
   // 2. Now paint the foreground, including text and decorations.
   int ascent = font_data ? font_data->GetFontMetrics().Ascent() : 0;
   PhysicalOffset text_origin(box_origin.left, box_origin.top + ascent);
-  NGTextPainter text_painter(context, font, text_fragment, text_origin,
-                             box_rect, text_fragment.IsHorizontal());
+  NGTextPainter text_painter(context, font, fragment_, text_origin, box_rect,
+                             text_fragment.IsHorizontal());
 
   if (style.GetTextEmphasisMark() != TextEmphasisMark::kNone) {
     text_painter.SetEmphasisMark(style.TextEmphasisMarkString(),
diff --git a/third_party/blink/renderer/core/paint/ng/ng_text_painter.cc b/third_party/blink/renderer/core/paint/ng/ng_text_painter.cc
index 7e15476..39369dbe 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_text_painter.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_text_painter.cc
@@ -64,15 +64,11 @@
     // TODO(npm): Check that there are non-whitespace characters. See
     // crbug.com/788444.
     graphics_context_.GetPaintController().SetTextPainted();
-    const LayoutObject& layout_object = *fragment_.GetLayoutObject();
-    if ((RuntimeEnabledFeatures::FirstContentfulPaintPlusPlusEnabled() ||
-         RuntimeEnabledFeatures::ElementTimingEnabled(
-             &layout_object.GetDocument()))) {
-      if (!font_.ShouldSkipDrawing()) {
-        PaintTimingDetector::NotifyTextPaint(
-            layout_object, graphics_context_.GetPaintController()
-                               .CurrentPaintChunkProperties());
-      }
+    if (RuntimeEnabledFeatures::FirstContentfulPaintPlusPlusEnabled() ||
+        RuntimeEnabledFeatures::ElementTimingEnabled(
+            &fragment_.GetLayoutObject()->GetDocument())) {
+      if (!font_.ShouldSkipDrawing())
+        PaintTimingDetector::NotifyTextPaint(paint_fragment_.VisualRect());
     }
   }
 }
diff --git a/third_party/blink/renderer/core/paint/ng/ng_text_painter.h b/third_party/blink/renderer/core/paint/ng/ng_text_painter.h
index a98af6a..4e448d1 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_text_painter.h
+++ b/third_party/blink/renderer/core/paint/ng/ng_text_painter.h
@@ -7,6 +7,7 @@
 
 #include "third_party/blink/renderer/core/content_capture/content_holder.h"
 #include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h"
 #include "third_party/blink/renderer/core/paint/text_painter_base.h"
 
 namespace blink {
@@ -25,12 +26,14 @@
  public:
   NGTextPainter(GraphicsContext& context,
                 const Font& font,
-                const NGPhysicalTextFragment& text_fragment,
+                const NGPaintFragment& paint_fragment,
                 const PhysicalOffset& text_origin,
                 const PhysicalRect& text_bounds,
                 bool horizontal)
       : TextPainterBase(context, font, text_origin, text_bounds, horizontal),
-        fragment_(text_fragment) {}
+        paint_fragment_(paint_fragment),
+        fragment_(
+            To<NGPhysicalTextFragment>(paint_fragment.PhysicalFragment())) {}
   ~NGTextPainter() = default;
 
   void ClipDecorationsStripe(float upper,
@@ -66,6 +69,7 @@
 
   void PaintEmphasisMarkForCombinedText();
 
+  const NGPaintFragment& paint_fragment_;
   const NGPhysicalTextFragment& fragment_;
 };
 
diff --git a/third_party/blink/renderer/core/paint/paint_timing_detector.cc b/third_party/blink/renderer/core/paint/paint_timing_detector.cc
index e6dd493..dbd1867 100644
--- a/third_party/blink/renderer/core/paint/paint_timing_detector.cc
+++ b/third_party/blink/renderer/core/paint/paint_timing_detector.cc
@@ -112,20 +112,6 @@
   }
 }
 
-// static
-void PaintTimingDetector::NotifyTextPaint(
-    const LayoutObject& object,
-    const PropertyTreeState& current_paint_chunk_properties) {
-  LocalFrameView* frame_view = object.GetFrameView();
-  if (!frame_view)
-    return;
-  PaintTimingDetector& detector = frame_view->GetPaintTimingDetector();
-  if (!detector.GetTextPaintTimingDetector())
-    return;
-  detector.GetTextPaintTimingDetector()->AggregateText(
-      object, current_paint_chunk_properties);
-}
-
 void PaintTimingDetector::NotifyNodeRemoved(const LayoutObject& object) {
   DOMNodeId node_id = DOMNodeIds::ExistingIdForNode(object.GetNode());
   if (node_id == kInvalidDOMNodeId)
@@ -260,27 +246,31 @@
   return FloatRect(layout_visual_rect);
 }
 
+ScopedPaintTimingDetectorBlockPaintHook*
+    ScopedPaintTimingDetectorBlockPaintHook::top_ = nullptr;
+
 ScopedPaintTimingDetectorBlockPaintHook::
     ScopedPaintTimingDetectorBlockPaintHook(
-        const LayoutBoxModelObject& text_aggregating_block)
-    : text_aggregating_block_(text_aggregating_block),
-      frame_view_(text_aggregating_block_.GetFrameView()) {
-  DCHECK(frame_view_);
-  TextPaintTimingDetector* detector =
-      frame_view_->GetPaintTimingDetector().GetTextPaintTimingDetector();
-  if (!detector)
-    return;
-  detector->WillWalkTextAggregatingNode();
-}
+        const LayoutBoxModelObject& aggregator,
+        const PropertyTreeState& property_tree_state)
+    : aggregator_(aggregator),
+      property_tree_state_(property_tree_state),
+      // This sets |top_| to |this|, and will restore |top_| to the previous
+      // value when this object destructs.
+      reset_top_(&top_, this),
+      detector_(aggregator.GetFrameView()
+                    ->GetPaintTimingDetector()
+                    .GetTextPaintTimingDetector()),
+      is_recording_(detector_ && detector_->ShouldWalkObject(aggregator)) {}
 
 ScopedPaintTimingDetectorBlockPaintHook::
     ~ScopedPaintTimingDetectorBlockPaintHook() {
-  DCHECK(frame_view_);
-  TextPaintTimingDetector* detector =
-      frame_view_->GetPaintTimingDetector().GetTextPaintTimingDetector();
-  if (!detector)
+  DCHECK_EQ(top_, this);
+  if (!is_recording_ || aggregated_visual_rect_.IsEmpty())
     return;
-  detector->DidWalkTextAggregatingNode(text_aggregating_block_);
+
+  detector_->RecordAggregatedText(aggregator_, aggregated_visual_rect_,
+                                  property_tree_state_);
 }
 
 void PaintTimingDetector::Trace(Visitor* visitor) {
@@ -288,4 +278,5 @@
   visitor->Trace(image_paint_timing_detector_);
   visitor->Trace(frame_view_);
 }
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/paint_timing_detector.h b/third_party/blink/renderer/core/paint/paint_timing_detector.h
index e7dd1120..7c8f152 100644
--- a/third_party/blink/renderer/core/paint/paint_timing_detector.h
+++ b/third_party/blink/renderer/core/paint/paint_timing_detector.h
@@ -46,8 +46,8 @@
       const IntSize& intrinsic_size,
       const ImageResourceContent* cached_image,
       const PropertyTreeState& current_paint_chunk_properties);
+  inline static void NotifyTextPaint(const IntRect&);
 
-  static void NotifyTextPaint(const LayoutObject&, const PropertyTreeState&);
   void NotifyNodeRemoved(const LayoutObject&);
   void NotifyBackgroundImageRemoved(const LayoutObject&,
                                     const ImageResourceContent*);
@@ -107,21 +107,39 @@
 // is their descendant. The hook should be placed right before visiting the
 // subtree of an container node, so that the constructor and the destructor can
 // tell the start and end of the visit.
+// TODO(crbug.com/960946): we should document the text aggregation.
 class ScopedPaintTimingDetectorBlockPaintHook {
   STACK_ALLOCATED();
 
  public:
-  ScopedPaintTimingDetectorBlockPaintHook(const LayoutBoxModelObject&);
+  ScopedPaintTimingDetectorBlockPaintHook(const LayoutBoxModelObject&,
+                                          const PropertyTreeState&);
 
   ~ScopedPaintTimingDetectorBlockPaintHook();
 
  private:
-  const LayoutBoxModelObject& text_aggregating_block_;
-  Member<LocalFrameView> frame_view_;
+  friend class PaintTimingDetector;
+
+  const LayoutBoxModelObject& aggregator_;
+  const PropertyTreeState& property_tree_state_;
+  IntRect aggregated_visual_rect_;
+  base::AutoReset<ScopedPaintTimingDetectorBlockPaintHook*> reset_top_;
+  Member<TextPaintTimingDetector> detector_;
+  bool is_recording_;
+  static ScopedPaintTimingDetectorBlockPaintHook* top_;
 
   DISALLOW_COPY_AND_ASSIGN(ScopedPaintTimingDetectorBlockPaintHook);
 };
 
+// static
+inline void PaintTimingDetector::NotifyTextPaint(
+    const IntRect& text_visual_rect) {
+  auto* top = ScopedPaintTimingDetectorBlockPaintHook::top_;
+  DCHECK(top);
+  if (top->is_recording_)
+    top->aggregated_visual_rect_.Unite(text_visual_rect);
+}
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_TIMING_DETECTOR_H_
diff --git a/third_party/blink/renderer/core/paint/svg_inline_text_box_painter.cc b/third_party/blink/renderer/core/paint/svg_inline_text_box_painter.cc
index 51d0dd6..03ec29b 100644
--- a/third_party/blink/renderer/core/paint/svg_inline_text_box_painter.cc
+++ b/third_party/blink/renderer/core/paint/svg_inline_text_box_painter.cc
@@ -470,13 +470,12 @@
   // TODO(npm): Check that there are non-whitespace characters. See
   // crbug.com/788444.
   context.GetPaintController().SetTextPainted();
-  if ((RuntimeEnabledFeatures::FirstContentfulPaintPlusPlusEnabled() ||
-       RuntimeEnabledFeatures::ElementTimingEnabled(
-           &InlineLayoutObject().GetDocument()))) {
+  if (RuntimeEnabledFeatures::FirstContentfulPaintPlusPlusEnabled() ||
+      RuntimeEnabledFeatures::ElementTimingEnabled(
+          &InlineLayoutObject().GetDocument())) {
     if (!scaled_font.ShouldSkipDrawing()) {
       PaintTimingDetector::NotifyTextPaint(
-          InlineLayoutObject(), paint_info.context.GetPaintController()
-                                    .CurrentPaintChunkProperties());
+          InlineLayoutObject().FragmentsVisualRectBoundingBox());
     }
   }
 }
diff --git a/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc b/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc
index f874ce4..cc6f96a 100644
--- a/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc
+++ b/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc
@@ -27,7 +27,6 @@
 // Calculate metrics candidate every 1 second after the first text pre-paint.
 static constexpr TimeDelta kTimerDelay = TimeDelta::FromSeconds(1);
 constexpr size_t kTextNodeNumberLimit = 5000;
-constexpr size_t kVisitedTextObjectsLimit = 10000;
 
 static bool LargeTextFirst(const base::WeakPtr<TextRecord>& a,
                            const base::WeakPtr<TextRecord>& b) {
@@ -165,65 +164,49 @@
   awaiting_swap_promise_ = false;
 }
 
-void TextPaintTimingDetector::AggregateText(
-    const LayoutObject& object,
-    const PropertyTreeState& current_paint_chunk_properties) {
+bool TextPaintTimingDetector::ShouldWalkObject(
+    const LayoutBoxModelObject& object) const {
   if (!is_recording_)
-    return;
-  // Text nodes are aggregated with the lowest of either block-level block
-  // element or self-painting inline-level element.
-  DCHECK_GT(walking_block_stack_.size(), 0u);
-  AggregateTextToClosestBlock(object, current_paint_chunk_properties);
-}
-
-void TextPaintTimingDetector::AggregateTextToClosestBlock(
-    const LayoutObject& text_object,
-    const PropertyTreeState& current_paint_chunk_properties) {
-  DCHECK_GT(walking_block_stack_.size(), 0u);
-
-  if (visited_text_objects_.Contains(&text_object) ||
-      visited_text_objects_.size() >= kVisitedTextObjectsLimit)
-    return;
-
-  IntRect visual_rect = text_object.FragmentsVisualRectBoundingBox();
-  if (visual_rect.IsEmpty())
-    return;
-
-  visited_text_objects_.insert(&text_object);
-  FloatRect mapped_visual_rect =
-      frame_view_->GetPaintTimingDetector().CalculateVisualRect(
-          visual_rect, current_paint_chunk_properties);
-  if (mapped_visual_rect.Size().Area() == 0)
-    return;
-  walking_block_stack_.back().Aggregate(mapped_visual_rect);
+    return false;
+  // TODO(crbug.com/933479): Use LayoutObject::GeneratingNode() to include
+  // anonymous objects' rect.
+  Node* node = object.GetNode();
+  if (!node)
+    return false;
+  DOMNodeId node_id = DOMNodeIds::ExistingIdForNode(node);
+  if (node_id == kInvalidDOMNodeId)
+    return true;
+  // This metric defines the size of a text block by its first size, so we
+  // should not walk the object if it has been recorded.
+  return !records_manager_.HasRecorded(node_id);
 }
 
 void TextPaintTimingDetector::RecordAggregatedText(
-    const LayoutObject& text_aggregating_object,
-    uint64_t aggregated_size) {
-  if (!is_recording_)
-    return;
+    const LayoutBoxModelObject& aggregator,
+    const IntRect& aggregated_visual_rect,
+    const PropertyTreeState& property_tree_state) {
+  DCHECK(ShouldWalkObject(aggregator));
   DCHECK(!records_manager_.HasTooManyNodes());
-  // TODO(crbug.com/933479): Use LayoutObject::GeneratingNode() to include
-  // anonymous objects' rect.
-  Node* node = text_aggregating_object.GetNode();
-  if (!node)
-    return;
+
+  Node* node = aggregator.GetNode();
+  DCHECK(node);
   DOMNodeId node_id = DOMNodeIds::IdForNode(node);
   DCHECK_NE(node_id, kInvalidDOMNodeId);
 
   records_manager_.MarkNodeReattachedIfNeeded(node_id);
 
-  // This metric defines the size of a text block by its first size. So it
-  // early-returns if the text block has been recorded.
-  if (records_manager_.HasRecorded(node_id))
-    return;
+  // The caller should check this.
+  DCHECK(!aggregated_visual_rect.IsEmpty());
+
+  FloatRect mapped_visual_rect =
+      frame_view_->GetPaintTimingDetector().CalculateVisualRect(
+          aggregated_visual_rect, property_tree_state);
+  uint64_t aggregated_size = mapped_visual_rect.Size().Area();
 
   if (aggregated_size == 0) {
     records_manager_.RecordInvisibleNode(node_id);
   } else {
-    records_manager_.RecordVisibleNode(node_id, aggregated_size,
-                                       text_aggregating_object);
+    records_manager_.RecordVisibleNode(node_id, aggregated_size, aggregator);
   }
 
   if (records_manager_.HasTooManyNodes()) {
@@ -376,15 +359,4 @@
   visitor->Trace(text_element_timing_);
 }
 
-void TextPaintTimingDetector::WillWalkTextAggregatingNode() {
-  walking_block_stack_.push_back(BlockInfo());
-}
-
-void TextPaintTimingDetector::DidWalkTextAggregatingNode(
-    const LayoutBoxModelObject& text_aggregating_block) {
-  uint64_t aggregated_size = walking_block_stack_.back().AggregatedTextSize();
-  if (aggregated_size > 0)
-    RecordAggregatedText(text_aggregating_block, aggregated_size);
-  walking_block_stack_.pop_back();
-}
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/text_paint_timing_detector.h b/third_party/blink/renderer/core/paint/text_paint_timing_detector.h
index eb001778..196e179 100644
--- a/third_party/blink/renderer/core/paint/text_paint_timing_detector.h
+++ b/third_party/blink/renderer/core/paint/text_paint_timing_detector.h
@@ -42,17 +42,6 @@
   DISALLOW_COPY_AND_ASSIGN(TextRecord);
 };
 
-class BlockInfo {
- public:
-  void Aggregate(FloatRect new_text_rect) {
-    aggregated_text_rect_.UniteIfNonZero(new_text_rect);
-  }
-  uint64_t AggregatedTextSize() { return aggregated_text_rect_.Size().Area(); }
-
- private:
-  FloatRect aggregated_text_rect_;
-};
-
 class TextRecordsManager {
   DISALLOW_NEW();
   using TextRecordSetComparator = bool (*)(const base::WeakPtr<TextRecord>&,
@@ -146,11 +135,10 @@
 
  public:
   TextPaintTimingDetector(LocalFrameView* frame_view);
-  // TODO(crbug.com/960946): we should document the text aggregation.
-  void AggregateText(const LayoutObject&, const PropertyTreeState&);
-  void WillWalkTextAggregatingNode();
-  void DidWalkTextAggregatingNode(
-      const LayoutBoxModelObject& text_aggregating_block);
+  bool ShouldWalkObject(const LayoutBoxModelObject&) const;
+  void RecordAggregatedText(const LayoutBoxModelObject& aggregator,
+                            const IntRect& aggregated_visual_rect,
+                            const PropertyTreeState&);
   void OnPaintFinished();
   void NotifyNodeRemoved(DOMNodeId);
   TextRecord* FindLargestPaintCandidate();
@@ -163,13 +151,9 @@
   void Trace(blink::Visitor*);
 
  private:
-  void AggregateTextToClosestBlock(const LayoutObject&,
-                                   const PropertyTreeState&);
   void PopulateTraceValue(TracedValue&, const TextRecord& first_text_paint);
   void TimerFired(TimerBase*);
   void UpdateCandidate();
-  void RecordAggregatedText(const LayoutObject& aggregating_object,
-                            uint64_t aggregated_size);
 
   void ReportSwapTime(WebWidgetClient::SwapResult result,
                       base::TimeTicks timestamp);
@@ -189,10 +173,6 @@
   bool has_records_changed_ = true;
   bool need_update_timing_at_frame_end_ = false;
 
-  HashSet<const LayoutObject*> visited_text_objects_;
-
-  Vector<BlockInfo> walking_block_stack_;
-
   TaskRunnerTimer<TextPaintTimingDetector> timer_;
   Member<LocalFrameView> frame_view_;
 
diff --git a/third_party/blink/renderer/modules/payments/can_make_payment_test.cc b/third_party/blink/renderer/modules/payments/can_make_payment_test.cc
index 07946ba..6b19d75 100644
--- a/third_party/blink/renderer/modules/payments/can_make_payment_test.cc
+++ b/third_party/blink/renderer/modules/payments/can_make_payment_test.cc
@@ -8,6 +8,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
 #include "third_party/blink/renderer/modules/payments/payment_request.h"
 #include "third_party/blink/renderer/modules/payments/payment_test_helper.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
 
 namespace blink {
 namespace {
@@ -17,11 +18,12 @@
 using payments::mojom::blink::PaymentErrorReason;
 using payments::mojom::blink::PaymentRequestClient;
 
-class HasEnrolledInstrumentTest : public testing::Test {
-  void SetUp() override {
-    testing::Test::SetUp();
-    RuntimeEnabledFeatures::SetPaymentRequestHasEnrolledInstrumentEnabled(true);
-  }
+class HasEnrolledInstrumentTest
+    : public testing::Test,
+      private ScopedPaymentRequestHasEnrolledInstrumentForTest {
+ public:
+  HasEnrolledInstrumentTest()
+      : ScopedPaymentRequestHasEnrolledInstrumentForTest(true) {}
 };
 
 TEST_F(HasEnrolledInstrumentTest, RejectPromiseOnUserCancel) {
@@ -123,12 +125,12 @@
 
 class CanMakePaymentTest
     : public testing::Test,
-      public testing::WithParamInterface<HasEnrolledInstrumentEnabled> {
-  void SetUp() override {
-    testing::Test::SetUp();
-    RuntimeEnabledFeatures::SetPaymentRequestHasEnrolledInstrumentEnabled(
-        GetParam() == HasEnrolledInstrumentEnabled::kYes);
-  }
+      public testing::WithParamInterface<HasEnrolledInstrumentEnabled>,
+      private ScopedPaymentRequestHasEnrolledInstrumentForTest {
+ public:
+  CanMakePaymentTest()
+      : ScopedPaymentRequestHasEnrolledInstrumentForTest(
+            GetParam() == HasEnrolledInstrumentEnabled::kYes) {}
 };
 
 TEST_P(CanMakePaymentTest, RejectPromiseOnUserCancel) {
diff --git a/third_party/blink/renderer/modules/webgpu/dawn_conversions.cc b/third_party/blink/renderer/modules/webgpu/dawn_conversions.cc
index 76ed331e..5941232 100644
--- a/third_party/blink/renderer/modules/webgpu/dawn_conversions.cc
+++ b/third_party/blink/renderer/modules/webgpu/dawn_conversions.cc
@@ -29,6 +29,9 @@
   if (webgpu_enum == "uniform-buffer") {
     return DAWN_BINDING_TYPE_UNIFORM_BUFFER;
   }
+  if (webgpu_enum == "dynamic-uniform-buffer") {
+    return DAWN_BINDING_TYPE_DYNAMIC_UNIFORM_BUFFER;
+  }
   NOTREACHED();
   return DAWN_BINDING_TYPE_FORCE32;
 }
diff --git a/third_party/blink/renderer/platform/exported/web_prerender.cc b/third_party/blink/renderer/platform/exported/web_prerender.cc
index fbe19674..36f7daee 100644
--- a/third_party/blink/renderer/platform/exported/web_prerender.cc
+++ b/third_party/blink/renderer/platform/exported/web_prerender.cc
@@ -35,6 +35,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/platform/prerender.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 
 namespace blink {
 
@@ -90,6 +91,11 @@
   return private_->GetReferrer();
 }
 
+url::Origin WebPrerender::SecurityOrigin() const {
+  auto* security_origin = private_->GetSecurityOrigin();
+  return security_origin ? security_origin->ToUrlOrigin() : url::Origin();
+}
+
 network::mojom::ReferrerPolicy WebPrerender::GetReferrerPolicy() const {
   return private_->GetReferrerPolicy();
 }
diff --git a/third_party/blink/renderer/platform/prerender.cc b/third_party/blink/renderer/platform/prerender.cc
index 610275eb..5f2e2ce8 100644
--- a/third_party/blink/renderer/platform/prerender.cc
+++ b/third_party/blink/renderer/platform/prerender.cc
@@ -40,8 +40,13 @@
 Prerender::Prerender(PrerenderClient* client,
                      const KURL& url,
                      const unsigned rel_types,
-                     const Referrer& referrer)
-    : client_(client), url_(url), rel_types_(rel_types), referrer_(referrer) {}
+                     const Referrer& referrer,
+                     const SecurityOrigin* security_origin)
+    : client_(client),
+      url_(url),
+      rel_types_(rel_types),
+      referrer_(referrer),
+      security_origin_(security_origin) {}
 
 Prerender::~Prerender() = default;
 
diff --git a/third_party/blink/renderer/platform/prerender.h b/third_party/blink/renderer/platform/prerender.h
index f5cf53f..753ee9a 100644
--- a/third_party/blink/renderer/platform/prerender.h
+++ b/third_party/blink/renderer/platform/prerender.h
@@ -39,6 +39,7 @@
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/weborigin/referrer.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 #include "third_party/blink/renderer/platform/wtf/ref_counted.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
@@ -56,7 +57,11 @@
     virtual ~ExtraData() = default;
   };
 
-  Prerender(PrerenderClient*, const KURL&, unsigned rel_types, const Referrer&);
+  Prerender(PrerenderClient*,
+            const KURL&,
+            unsigned rel_types,
+            const Referrer&,
+            const SecurityOrigin* security_origin);
   ~Prerender();
   void Trace(blink::Visitor*);
 
@@ -72,6 +77,7 @@
   network::mojom::ReferrerPolicy GetReferrerPolicy() const {
     return referrer_.referrer_policy;
   }
+  const SecurityOrigin* GetSecurityOrigin() const { return security_origin_; }
 
   void SetExtraData(scoped_refptr<ExtraData> extra_data) {
     extra_data_ = std::move(extra_data);
@@ -93,6 +99,7 @@
   const KURL url_;
   const unsigned rel_types_;
   const Referrer referrer_;
+  const SecurityOrigin* const security_origin_;
 
   scoped_refptr<ExtraData> extra_data_;
 };
diff --git a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
index 5f29b19..3c9327d 100644
--- a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
@@ -244,7 +244,7 @@
 crbug.com/591099 external/wpt/css/css-writing-modes/available-size-017.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-writing-modes/baseline-with-orthogonal-flow-001.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-writing-modes/block-flow-direction-vrl-009.xht [ Pass ]
-crbug.com/591099 external/wpt/css/css-writing-modes/img-intrinsic-size-contribution-002.html [ Failure ]
+crbug.com/591099 external/wpt/css/css-writing-modes/img-intrinsic-size-contribution-002.html [ Failure Pass ]
 crbug.com/591099 external/wpt/css/css-writing-modes/inline-box-border-vlr-001.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-writing-modes/line-box-direction-vrl-009.xht [ Pass ]
 crbug.com/591099 external/wpt/css/css-writing-modes/line-box-height-vlr-021.xht [ Pass ]
@@ -263,7 +263,6 @@
 crbug.com/953479 external/wpt/css/cssom-view/offsetTopLeft-empty-inline.html [ Failure ]
 crbug.com/953479 external/wpt/css/cssom-view/offsetTopLeft-leading-space-inline.html [ Failure ]
 crbug.com/953479 external/wpt/css/cssom-view/offsetTopLeft-trailing-space-inline.html [ Failure ]
-crbug.com/591099 external/wpt/css/filter-effects/backdrop-filter-clipped.html [ Pass ]
 crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-002.xhtml [ Pass ]
 crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-011.html [ Pass ]
 crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/ib-split/emptyspan-1.html [ Pass ]
@@ -286,11 +285,10 @@
 crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-padding-box-border-radius-002.html [ Pass ]
 crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/sizing/hori-block-size-small-or-larger-than-container-with-min-or-max-content-1.html [ Pass ]
 crbug.com/591099 external/wpt/fetch/api/request/request-keepalive-quota.html?include=slow-2 [ Pass ]
-crbug.com/591099 external/wpt/geolocation-API/PositionOptions.https.html [ Failure Pass ]
+crbug.com/591099 external/wpt/geolocation-API/PositionOptions.https.html [ Failure ]
 crbug.com/591099 external/wpt/html/user-activation/activation-transfer-without-click.tentative.html [ Pass ]
 crbug.com/591099 external/wpt/pointerevents/pointerevent_touch-action-pan-x-css_touch.html [ Pass ]
 crbug.com/591099 external/wpt/pointerevents/pointerevent_touch-action-pan-y-css_touch.html [ Pass ]
-crbug.com/591099 external/wpt/portals/portal-non-http-navigation.html [ Failure Pass ]
 crbug.com/845902 external/wpt/quirks/line-height-trailing-collapsable-whitespace.html [ Pass ]
 crbug.com/591099 external/wpt/referrer-policy/no-referrer/http-rp/same-origin/http-http/shared-worker/keep-origin-redirect/generic.http.html [ Pass ]
 crbug.com/591099 external/wpt/referrer-policy/no-referrer/http-rp/same-origin/http-http/shared-worker/no-redirect/generic.http.html [ Pass ]
@@ -309,7 +307,7 @@
 crbug.com/591099 fast/css/outline-offset-large.html [ Failure ]
 crbug.com/855279 fast/css/text-overflow-ellipsis-vertical-hittest.html [ Pass ]
 crbug.com/591099 fast/css3-text/css3-text-decoration/text-underline-position/text-underline-position-under.html [ Failure ]
-crbug.com/591099 fast/events/before-unload-return-value-from-listener.html [ Pass Timeout ]
+crbug.com/591099 fast/events/before-unload-return-value-from-listener.html [ Pass ]
 crbug.com/591099 fast/events/pointerevents/multi-touch-events.html [ Failure Pass ]
 crbug.com/591099 fast/events/touch/compositor-touch-hit-rects-continuation.html [ Failure ]
 crbug.com/591099 fast/events/touch/compositor-touch-hit-rects-list-translate.html [ Failure ]
@@ -335,7 +333,7 @@
 crbug.com/591099 http/tests/devtools/tracing-session-id.js [ Pass ]
 crbug.com/591099 http/tests/devtools/tracing/console-timeline.js [ Pass ]
 crbug.com/591099 http/tests/media/video-load-metadata-decode-error.html [ Pass ]
-crbug.com/591099 http/tests/security/inactive-document-with-empty-security-origin.html [ Pass Timeout ]
+crbug.com/591099 http/tests/security/inactive-document-with-empty-security-origin.html [ Pass ]
 crbug.com/591099 images/feature-policy-oversized-images-resize.html [ Pass ]
 crbug.com/591099 media/video-object-fit-change.html [ Failure Pass ]
 crbug.com/591099 paint/invalidation/flexbox/scrollbars-changed.html [ Failure ]
@@ -346,12 +344,13 @@
 crbug.com/591099 paint/invalidation/svg/svg-background-partial-redraw.html [ Failure ]
 crbug.com/591099 paint/invalidation/svg/transform-focus-ring-repaint.html [ Failure ]
 crbug.com/591099 storage/indexeddb/objectstore-cursor.html [ Pass ]
-crbug.com/591099 svg/zoom/page/zoom-svg-float-border-padding.xml [ Failure Pass ]
 crbug.com/591099 tables/mozilla/bugs/bug14159-1.html [ Pass ]
+crbug.com/591099 virtual/android/fullscreen/api/document-exit-fullscreen-vs-request.html [ Pass ]
 crbug.com/591099 virtual/android/rootscroller/set-root-scroller.html [ Pass ]
 crbug.com/591099 virtual/android/rootscroller/set-rootscroller-before-load.html [ Pass ]
 crbug.com/916511 virtual/composite-after-paint/paint/background/scrolling-background-with-negative-z-child.html [ Crash ]
 crbug.com/591099 virtual/composite-after-paint/paint/invalidation/box/margin.html [ Failure Pass ]
+crbug.com/591099 virtual/compositor_threaded_scrollbar_scrolling/fast/scrolling/scrollbars/scrollbar-button-gesture-target.html [ Failure Pass ]
 Bug(none) virtual/disable-blink-gen-property-trees/ [ Skip ]
 crbug.com/591099 virtual/exotic-color-space/ [ Skip ]
 crbug.com/591099 virtual/fractional_scrolling_threaded/fast/scrolling/unscrollable-layer-subpixel-size-with-negative-overflow.html [ Failure Pass ]
@@ -368,8 +367,6 @@
 crbug.com/591099 virtual/gpu/fast/canvas/canvas-filter-stroke-paint-pattern.html [ Pass ]
 crbug.com/591099 virtual/layout_ng/ [ Skip ]
 crbug.com/824918 virtual/layout_ng_experimental/ [ Skip ]
-crbug.com/591099 virtual/mouseevent_fractional/fast/events/pointerevents/multi-touch-events.html [ Failure Pass ]
-crbug.com/591099 virtual/mouseevent_fractional/fast/events/pointerevents/pointer-event-in-slop-region.html [ Pass ]
 crbug.com/591099 virtual/mouseevent_fractional/fast/events/touch/compositor-touch-hit-rects-continuation.html [ Failure ]
 crbug.com/591099 virtual/mouseevent_fractional/fast/events/touch/compositor-touch-hit-rects-list-translate.html [ Failure ]
 crbug.com/591099 virtual/mouseevent_fractional/fast/events/touch/compositor-touch-hit-rects.html [ Failure ]
diff --git a/third_party/blink/web_tests/editing/pasteboard/copy-standalone-image-crash-expected.txt b/third_party/blink/web_tests/editing/pasteboard/copy-standalone-image-crash-expected.txt
deleted file mode 100644
index 7ef22e9..0000000
--- a/third_party/blink/web_tests/editing/pasteboard/copy-standalone-image-crash-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-PASS
diff --git a/third_party/blink/web_tests/editing/pasteboard/copy-standalone-image-crash.html b/third_party/blink/web_tests/editing/pasteboard/copy-standalone-image-crash.html
deleted file mode 100644
index 4047f73..0000000
--- a/third_party/blink/web_tests/editing/pasteboard/copy-standalone-image-crash.html
+++ /dev/null
@@ -1,68 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<script>
-
-var actionitems;
-
-if (window.testRunner) {
-     testRunner.dumpAsText();
-     testRunner.waitUntilDone();
-}
-
-function doClick() {
-    for (var i = 0; i < actionitems.length; i++)
-    {
-        var title = actionitems[i].title;
-
-        if (!title)
-           break;
-
-        title = title.replace(/_/g,'');
-
-        if (title.match("Copy Image")) {
-           actionitems[i].click();
-           break;
-        }
-    }
-
-    document.body.innerHTML = "PASS";
-
-    testRunner.notifyDone();
-}
-
-function hideDiv() {
-    document.getElementById("DIV").style.display="none";
-}
-
-function doTest() {
-    if (!window.testRunner) {
-        document.body.addEventListener('mousedown', function () {setTimeout(hideDiv, 100)}, false);
-        return;
-    }
-
-    var image = document.getElementById("IMG");
-
-    x = image.offsetLeft + 10;
-    y = image.offsetTop + 10;
-
-    eventSender.mouseMoveTo(x, y);
-    actionitems = eventSender.contextClick();
-
-    hideDiv();
-
-    setTimeout(doClick, 10);
-}
-
-</script>
-</head>
-<body onload="doTest()">
-This is an automated test case for bug <a href="https://bugs.webkit.org/show_bug.cgi?id=31721">31721</a><br>
-If you wish to test manually, mouseover to image, activate context menu, wait for the image to disappear and then click copy image.<br>
-There should be no crash.
-<div ID="DIV">
-    <img id="IMG" src="resources/apple.gif"/>
-    </div>
-</body>
-
-</html>
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 6b82a4a..a1c6d3f 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
@@ -42379,6 +42379,18 @@
      {}
     ]
    ],
+   "css/css-flexbox/flex-item-and-percentage-abspos.html": [
+    [
+     "css/css-flexbox/flex-item-and-percentage-abspos.html",
+     [
+      [
+       "/css/reference/ref-filled-green-100px-square.xht",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-flexbox/flex-items-flexibility.html": [
     [
      "css/css-flexbox/flex-items-flexibility.html",
@@ -85233,6 +85245,18 @@
      {}
     ]
    ],
+   "css/css-ui/outline-022.html": [
+    [
+     "css/css-ui/outline-022.html",
+     [
+      [
+       "/css/reference/ref-filled-green-100px-square-only.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-ui/outline-color-001.html": [
     [
      "css/css-ui/outline-color-001.html",
@@ -114651,6 +114675,18 @@
      {}
     ]
    ],
+   "svg/extensibility/foreignObject/composited-inside-object.html": [
+    [
+     "svg/extensibility/foreignObject/composited-inside-object.html",
+     [
+      [
+       "/svg/extensibility/foreignObject/composited-inside-object-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "svg/extensibility/foreignObject/compositing-backface-visibility.html": [
     [
      "svg/extensibility/foreignObject/compositing-backface-visibility.html",
@@ -177434,6 +177470,11 @@
      {}
     ]
    ],
+   "html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/user-interaction-editing-designMode-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "html/editing/editing-0/spelling-and-grammar-checking/OWNERS": [
     [
      {}
@@ -182839,11 +182880,6 @@
      {}
     ]
    ],
-   "imagebitmap-renderingcontext/context-creation-with-alpha-expected.txt": [
-    [
-     {}
-    ]
-   ],
    "images/META.yml": [
     [
      {}
@@ -187659,6 +187695,11 @@
      {}
     ]
    ],
+   "portals/portals-referrer-inherit-header.html.headers": [
+    [
+     {}
+    ]
+   ],
    "portals/references/portals-rendering.html": [
     [
      {}
@@ -196144,6 +196185,11 @@
      {}
     ]
    ],
+   "svg/extensibility/foreignObject/composited-inside-object-ref.html": [
+    [
+     {}
+    ]
+   ],
    "svg/extensibility/foreignObject/compositing-backface-visibility-ref.html": [
     [
      {}
@@ -240916,6 +240962,24 @@
      {}
     ]
    ],
+   "css/css-transforms/parsing/backface-visibility-computed.html": [
+    [
+     "css/css-transforms/parsing/backface-visibility-computed.html",
+     {}
+    ]
+   ],
+   "css/css-transforms/parsing/backface-visibility-invalid.html": [
+    [
+     "css/css-transforms/parsing/backface-visibility-invalid.html",
+     {}
+    ]
+   ],
+   "css/css-transforms/parsing/backface-visibility-valid.html": [
+    [
+     "css/css-transforms/parsing/backface-visibility-valid.html",
+     {}
+    ]
+   ],
    "css/css-transforms/parsing/perspective-origin-parsing-invalid.html": [
     [
      "css/css-transforms/parsing/perspective-origin-parsing-invalid.html",
@@ -249653,6 +249717,12 @@
      {}
     ]
    ],
+   "editing/other/non-html-document.html": [
+    [
+     "editing/other/non-html-document.html",
+     {}
+    ]
+   ],
    "editing/other/restoration.html": [
     [
      "editing/other/restoration.html",
@@ -267558,6 +267628,18 @@
      {}
     ]
    ],
+   "html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/user-interaction-editing-designMode-svg.svg": [
+    [
+     "html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/user-interaction-editing-designMode-svg.svg",
+     {}
+    ]
+   ],
+   "html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/user-interaction-editing-designMode-xml.xml": [
+    [
+     "html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/user-interaction-editing-designMode-xml.xml",
+     {}
+    ]
+   ],
    "html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/user-interaction-editing-designMode.html": [
     [
      "html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/user-interaction-editing-designMode.html",
@@ -278655,6 +278737,18 @@
      {}
     ]
    ],
+   "imagebitmap-renderingcontext/context-creation-offscreen-with-alpha.html": [
+    [
+     "imagebitmap-renderingcontext/context-creation-offscreen-with-alpha.html",
+     {}
+    ]
+   ],
+   "imagebitmap-renderingcontext/context-creation-offscreen.html": [
+    [
+     "imagebitmap-renderingcontext/context-creation-offscreen.html",
+     {}
+    ]
+   ],
    "imagebitmap-renderingcontext/context-creation-with-alpha.html": [
     [
      "imagebitmap-renderingcontext/context-creation-with-alpha.html",
@@ -278673,6 +278767,30 @@
      {}
     ]
    ],
+   "imagebitmap-renderingcontext/toBlob-origin-clean-offscreen.sub.html": [
+    [
+     "imagebitmap-renderingcontext/toBlob-origin-clean-offscreen.sub.html",
+     {}
+    ]
+   ],
+   "imagebitmap-renderingcontext/tranferFromImageBitmap-ToBlob-offscreen.html": [
+    [
+     "imagebitmap-renderingcontext/tranferFromImageBitmap-ToBlob-offscreen.html",
+     {}
+    ]
+   ],
+   "imagebitmap-renderingcontext/tranferFromImageBitmap-TransferToImageBitmap-offscreen.html": [
+    [
+     "imagebitmap-renderingcontext/tranferFromImageBitmap-TransferToImageBitmap-offscreen.html",
+     {}
+    ]
+   ],
+   "imagebitmap-renderingcontext/tranferFromImageBitmap-null-offscreen.html": [
+    [
+     "imagebitmap-renderingcontext/tranferFromImageBitmap-null-offscreen.html",
+     {}
+    ]
+   ],
    "imagebitmap-renderingcontext/tranferFromImageBitmap-null.html": [
     [
      "imagebitmap-renderingcontext/tranferFromImageBitmap-null.html",
@@ -296031,12 +296149,6 @@
      {}
     ]
    ],
-   "portals/portals-no-referrer.html": [
-    [
-     "portals/portals-no-referrer.html",
-     {}
-    ]
-   ],
    "portals/portals-post-message.sub.html": [
     [
      "portals/portals-post-message.sub.html",
@@ -296046,6 +296158,24 @@
      }
     ]
    ],
+   "portals/portals-referrer-inherit-header.html": [
+    [
+     "portals/portals-referrer-inherit-header.html",
+     {}
+    ]
+   ],
+   "portals/portals-referrer-inherit-meta.html": [
+    [
+     "portals/portals-referrer-inherit-meta.html",
+     {}
+    ]
+   ],
+   "portals/portals-referrer.html": [
+    [
+     "portals/portals-referrer.html",
+     {}
+    ]
+   ],
    "preload/avoid-delaying-onload-link-preload.html": [
     [
      "preload/avoid-delaying-onload-link-preload.html",
@@ -379528,6 +379658,10 @@
    "04a3041ed5c5f9382d89d572fceaeb249b4e69f1",
    "reftest"
   ],
+  "css/css-flexbox/flex-item-and-percentage-abspos.html": [
+   "76bdff21b8658a34f10e3784951372ad3eb3a879",
+   "reftest"
+  ],
   "css/css-flexbox/flex-items-flexibility.html": [
    "0f7cbe725284cef1dcfcaab6992c5971958c1f31",
    "reftest"
@@ -410864,6 +410998,18 @@
    "6c368b3c1bd0a5d5afcd57a5da505178b4ba1a66",
    "reftest"
   ],
+  "css/css-transforms/parsing/backface-visibility-computed.html": [
+   "281186ae985ec80e20d2a1f61a9f0642573d4cfe",
+   "testharness"
+  ],
+  "css/css-transforms/parsing/backface-visibility-invalid.html": [
+   "c6c563124a92f2b4b2a318e64b4969281b4a88a1",
+   "testharness"
+  ],
+  "css/css-transforms/parsing/backface-visibility-valid.html": [
+   "eb7431078c42e08dbfe07e6de2e154ba9ecb2297",
+   "testharness"
+  ],
   "css/css-transforms/parsing/perspective-origin-parsing-invalid.html": [
    "2ed6721e091702a436217086eebd81172f359339",
    "testharness"
@@ -418808,6 +418954,10 @@
    "02e96ea9a67de331cdaf8962155ec4c0e02217b5",
    "reftest"
   ],
+  "css/css-ui/outline-022.html": [
+   "84cb516459471a1556e9f2b39d2f4190cfa3f02b",
+   "reftest"
+  ],
   "css/css-ui/outline-color-001.html": [
    "4245b2ceb7393a73850a223e498987e0e1bc4aa3",
    "reftest"
@@ -440364,6 +440514,10 @@
    "2cd1232d00b8bdcf3a48ba01b3f2cfd05e27f094",
    "testharness"
   ],
+  "editing/other/non-html-document.html": [
+   "5c4786e2fc1645f0b0c2f47a68736e55920cb610",
+   "testharness"
+  ],
   "editing/other/restoration-expected.txt": [
    "a8d74b3eb1e0a59f0c5b5989c60349720487a00b",
    "support"
@@ -451593,7 +451747,7 @@
    "support"
   ],
   "html/dom/interfaces.worker-expected.txt": [
-   "c2e30c2ea7e56bf6e4864ca4bb552e331e16ea48",
+   "74aec360f4de29a59765273e7dbfc517889fe3d2",
    "support"
   ],
   "html/dom/interfaces.worker.js": [
@@ -455148,8 +455302,20 @@
    "8b7c4b838c26b55b2bf21bf23d6d08634399f63a",
    "support"
   ],
+  "html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/user-interaction-editing-designMode-expected.txt": [
+   "dc65bb6f7616c13751ea6578e794ae49700a4f6c",
+   "support"
+  ],
+  "html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/user-interaction-editing-designMode-svg.svg": [
+   "6fb8a054481386dbaa160f0cab575b0bff0ccdb2",
+   "testharness"
+  ],
+  "html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/user-interaction-editing-designMode-xml.xml": [
+   "ea02ecdfd39e974877ddc4bd85ac0704ef560304",
+   "testharness"
+  ],
   "html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/user-interaction-editing-designMode.html": [
-   "f73ea73260e3c7250fac5af172d894c81be3d165",
+   "79d2dc8ba0d0bb2bab5dc2e3fd46f25a7635877e",
    "testharness"
   ],
   "html/editing/editing-0/spelling-and-grammar-checking/OWNERS": [
@@ -467344,12 +467510,16 @@
    "21c5ea48ad4cadff96c401972594083958f447f3",
    "testharness"
   ],
-  "imagebitmap-renderingcontext/context-creation-with-alpha-expected.txt": [
-   "f4a8ae2398153c9cb61dcc17e9ada6959624c221",
-   "support"
+  "imagebitmap-renderingcontext/context-creation-offscreen-with-alpha.html": [
+   "b565def964d0eb422702bc73054b6c4dfa0a3cb3",
+   "testharness"
+  ],
+  "imagebitmap-renderingcontext/context-creation-offscreen.html": [
+   "41cc6dc02a239cf26ea37bbdca1a252f19917ee7",
+   "testharness"
   ],
   "imagebitmap-renderingcontext/context-creation-with-alpha.html": [
-   "4354b9ac4188cd904f7e941001e724f78b0bde54",
+   "2c8fa08b9d340c9c0f1cb446a7a445fdc08cfff0",
    "testharness"
   ],
   "imagebitmap-renderingcontext/context-creation.html": [
@@ -467360,8 +467530,24 @@
    "eca7afe9ddd18f23e14f9cb2b16208713af7974b",
    "testharness"
   ],
+  "imagebitmap-renderingcontext/toBlob-origin-clean-offscreen.sub.html": [
+   "0a0e203249f13f45198cbc05316b81694377d588",
+   "testharness"
+  ],
+  "imagebitmap-renderingcontext/tranferFromImageBitmap-ToBlob-offscreen.html": [
+   "6a555fe25aad6b823a87282472345acb7d9dbf78",
+   "testharness"
+  ],
+  "imagebitmap-renderingcontext/tranferFromImageBitmap-TransferToImageBitmap-offscreen.html": [
+   "9f9c3395733a8a39de61ce8a616c3871e20e7e4f",
+   "testharness"
+  ],
+  "imagebitmap-renderingcontext/tranferFromImageBitmap-null-offscreen.html": [
+   "e05a623a2fe1d1e290391ca4688285b14b9537b5",
+   "testharness"
+  ],
   "imagebitmap-renderingcontext/tranferFromImageBitmap-null.html": [
-   "c12a8c93fdb0f83c371982958838b8fa29c4f91d",
+   "19d2f17ed3aff43da03836b8a62272e20bf12e55",
    "testharness"
   ],
   "imagebitmap-renderingcontext/transferFromImageBitmap-detached.html": [
@@ -468549,7 +468735,7 @@
    "support"
   ],
   "interfaces/html.idl": [
-   "5ae46f742922f4c455380745f124769399086eb9",
+   "b36c301428914e6b167e89fdf874c29a5fc8ea0e",
    "support"
   ],
   "interfaces/image-capture.idl": [
@@ -482652,14 +482838,26 @@
    "9d8d09be9b104b7fa0c58f58c5b19d641db6c0eb",
    "testharness"
   ],
-  "portals/portals-no-referrer.html": [
-   "1bd68a1c4a21ed3d2db559f7d7c62c187215e40c",
-   "testharness"
-  ],
   "portals/portals-post-message.sub.html": [
    "b37e8afa6db05218d8e128ce1bc352ed1f892273",
    "testharness"
   ],
+  "portals/portals-referrer-inherit-header.html": [
+   "0207474f37dea658ed9edb4352bd4e0719465558",
+   "testharness"
+  ],
+  "portals/portals-referrer-inherit-header.html.headers": [
+   "7ffbf17d6be5a59e5b3636adc0eb14f3e2e528c2",
+   "support"
+  ],
+  "portals/portals-referrer-inherit-meta.html": [
+   "cf0493fb6224233396f6169b59641c866096a82d",
+   "testharness"
+  ],
+  "portals/portals-referrer.html": [
+   "cb08f56def7dfecaf3f5ed83cd668ed7f3ab663a",
+   "testharness"
+  ],
   "portals/portals-rendering.html": [
    "0eae0ddfd6ef1e9de2251f50914a855f4142b9d5",
    "reftest"
@@ -499332,8 +499530,16 @@
    "120941444a4898197d6b6001f9908a6cd48b62ba",
    "support"
   ],
+  "svg/extensibility/foreignObject/composited-inside-object-ref.html": [
+   "079662bc745689e576fca029074fd8cfee987664",
+   "support"
+  ],
+  "svg/extensibility/foreignObject/composited-inside-object.html": [
+   "caa613bb46d35aeb72b18866c67c08b4e41b926d",
+   "reftest"
+  ],
   "svg/extensibility/foreignObject/compositing-backface-visibility-ref.html": [
-   "b0f504974a552545053a1a5d7c1e55eefa527fdb",
+   "790e90c635042a21626bd885fe8b773312b8bfab",
    "support"
   ],
   "svg/extensibility/foreignObject/compositing-backface-visibility.html": [
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/text-align/text-align-last-empty-inline.html b/third_party/blink/web_tests/external/wpt/css/css-text/text-align/text-align-last-empty-inline.html
new file mode 100644
index 0000000..07dcb3b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/text-align/text-align-last-empty-inline.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<title>Tests justification of empty inline element</title>
+<link rel="author" title="Emil A Eklund" href="eae@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#text-align-last-property" title="6.3. Last Line Alignment: the text-align-last property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+  #justify {
+    text-align-last: justify;
+    margin-bottom: 5px;
+  }
+  #justify > span, #reference > span {
+    padding: 1px;
+    background: black;
+  }
+</style>
+<body>
+  <div id="justify"><span></span></div>
+  <div id="reference"><span></span></div>
+  <p>
+    The two black line segments above should align.
+  </p>
+</body>
+<script>
+test(function() {
+  const justify_element = document.getElementById('justify');
+  const justify_rect = justify_element.firstElementChild.getBoundingClientRect();
+  const ref_element = document.getElementById('reference');
+  const ref_rect = ref_element.firstElementChild.getBoundingClientRect();
+  assert_equals(justify_rect.left, ref_rect.left);
+  assert_equals(justify_rect.right, ref_rect.right);
+}, 'Left and right edges of empty inlines should align.');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transforms/parsing/backface-visibility-computed.html b/third_party/blink/web_tests/external/wpt/css/css-transforms/parsing/backface-visibility-computed.html
new file mode 100644
index 0000000..281186ae
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-transforms/parsing/backface-visibility-computed.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Transform Module Level 2: getComputedValue().backfaceVisibility</title>
+<link rel="help" href="https://drafts.csswg.org/css-transforms-2/#backface-visibility-property">
+<meta name="assert" content="backface-visibility computed value is as specified.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+</head>
+<body>
+<div id="target"></div>
+<script>
+test_computed_value("backface-visibility", "visible");
+test_computed_value("backface-visibility", "hidden");
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transforms/parsing/backface-visibility-invalid.html b/third_party/blink/web_tests/external/wpt/css/css-transforms/parsing/backface-visibility-invalid.html
new file mode 100644
index 0000000..c6c5631
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-transforms/parsing/backface-visibility-invalid.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Transform Module Level 2: parsing backface-visibility with invalid values</title>
+<link rel="help" href="https://drafts.csswg.org/css-transforms-2/#backface-visibility-property">
+<meta name="assert" content="backface-visibility supports only the grammar 'visible | hidden'.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+</head>
+<body>
+<script>
+test_invalid_value("backface-visibility", "auto");
+test_invalid_value("backface-visibility", "visible hidden");
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transforms/parsing/backface-visibility-valid.html b/third_party/blink/web_tests/external/wpt/css/css-transforms/parsing/backface-visibility-valid.html
new file mode 100644
index 0000000..eb74310
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-transforms/parsing/backface-visibility-valid.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Transform Module Level 2: parsing backface-visibility with valid values</title>
+<link rel="help" href="https://drafts.csswg.org/css-transforms-2/#backface-visibility-property">
+<meta name="assert" content="backface-visibility supports the full grammar 'visible | hidden'.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+</head>
+<body>
+<script>
+test_valid_value("backface-visibility", "visible");
+test_valid_value("backface-visibility", "hidden");
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/editing/other/non-html-document.html b/third_party/blink/web_tests/external/wpt/editing/other/non-html-document.html
new file mode 100644
index 0000000..5c4786e2f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/editing/other/non-html-document.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Non-HTML document tests</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+
+test(function() {
+  let xmldoc =
+    document.implementation.createDocument("http://www.w3.org/1999/xlink",
+                                           "html", null);
+  for (let f of [
+         () => xmldoc.execCommand("bold"),
+         () => xmldoc.queryCommandEnabled("bold"),
+         () => xmldoc.queryCommandIndeterm("bold"),
+         () => xmldoc.queryCommandState("bold"),
+         () => xmldoc.queryCommandSupported("bold"),
+         () => xmldoc.queryCommandValue("bold"),
+       ]) {
+    assert_throws("InvalidStateError", f);
+  }
+}, "editing APIs on an XML document should be disabled");
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/user-interaction-editing-designMode-expected.txt b/third_party/blink/web_tests/external/wpt/html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/user-interaction-editing-designMode-expected.txt
new file mode 100644
index 0000000..dc65bb6f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/user-interaction-editing-designMode-expected.txt
@@ -0,0 +1,6 @@
+This is a testharness.js-based test.
+PASS initial designMode attribute
+FAIL set designMode = "on" assert_true: expected true got false
+PASS set designMode = "off"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/user-interaction-editing-designMode-svg.svg b/third_party/blink/web_tests/external/wpt/html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/user-interaction-editing-designMode-svg.svg
new file mode 100644
index 0000000..6fb8a054
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/user-interaction-editing-designMode-svg.svg
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg:svg xmlns:svg="http://www.w3.org/2000/svg"
+    xmlns="http://www.w3.org/1999/xhtml"
+    width="100%" height="100%" viewBox="0 0 800 600">
+ <svg:title>Editing: designMode attribute test</svg:title>
+ <head>
+  <link rel="author" title="Baidu" href="mailto: guopengcheng@baidu.com"/>
+  <link rel="help" href="https://html.spec.whatwg.org/multipage/#making-entire-documents-editable:-the-designmode-idl-attribute"/>
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <div id="log"></div>
+ </head>
+ <body>
+  <script type="text/javascript"><![CDATA[
+   test(function() {
+    assert_equals(document.designMode, "off", "check for designMode value");
+    assert_throws("InvalidStateError", function() { document.queryCommandSupported("delete") });
+    assert_throws("InvalidStateError", function() { document.queryCommandEnabled("delete") });
+   }, "initial designMode attribute");
+   document.designMode="on";
+   test(function() {
+    assert_equals(document.designMode, "on", "check for designMode value");
+    assert_throws("InvalidStateError", function() { document.queryCommandSupported("delete") });
+    assert_throws("InvalidStateError", function() { document.queryCommandEnabled("delete") });
+   }, "set designMode = \"on\"");
+   document.designMode="off";
+   test(function() {
+    assert_equals(document.designMode,"off", "check for designMode value");
+    assert_throws("InvalidStateError", function() { document.queryCommandSupported("delete") });
+    assert_throws("InvalidStateError", function() { document.queryCommandEnabled("delete") });
+   }, "set designMode = \"off\"");
+  ]]></script>
+ </body>
+</svg:svg>
diff --git a/third_party/blink/web_tests/external/wpt/html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/user-interaction-editing-designMode-xml.xml b/third_party/blink/web_tests/external/wpt/html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/user-interaction-editing-designMode-xml.xml
new file mode 100644
index 0000000..ea02ecd
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/user-interaction-editing-designMode-xml.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+  <title>Editing: designMode attribute test</title>
+  <link rel="author" title="Baidu" href="mailto: guopengcheng@baidu.com"/>
+  <link rel="help" href="https://html.spec.whatwg.org/multipage/#making-entire-documents-editable:-the-designmode-idl-attribute"/>
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <div id="log"></div>
+ </head>
+ <body>
+  <script type="text/javascript"><![CDATA[
+   test(function() {
+    assert_equals(document.designMode, "off", "check for designMode value");
+    assert_throws("InvalidStateError", function() { document.queryCommandSupported("delete") });
+    assert_throws("InvalidStateError", function() { document.queryCommandEnabled("delete") });
+   }, "initial designMode attribute");
+   document.designMode="on";
+   test(function() {
+    assert_equals(document.designMode, "on", "check for designMode value");
+    assert_throws("InvalidStateError", function() { document.queryCommandSupported("delete") });
+    assert_throws("InvalidStateError", function() { document.queryCommandEnabled("delete") });
+   }, "set designMode = \"on\"");
+   document.designMode="off";
+   test(function() {
+    assert_equals(document.designMode,"off", "check for designMode value");
+    assert_throws("InvalidStateError", function() { document.queryCommandSupported("delete") });
+    assert_throws("InvalidStateError", function() { document.queryCommandEnabled("delete") });
+   }, "set designMode = \"off\"");
+  ]]></script>
+ </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/user-interaction-editing-designMode.html b/third_party/blink/web_tests/external/wpt/html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/user-interaction-editing-designMode.html
index f73ea732..79d2dc8 100644
--- a/third_party/blink/web_tests/external/wpt/html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/user-interaction-editing-designMode.html
+++ b/third_party/blink/web_tests/external/wpt/html/editing/editing-0/making-entire-documents-editable-the-designmode-idl-attribute/user-interaction-editing-designMode.html
@@ -12,14 +12,20 @@
   <script type="text/javascript">
    test(function() {
     assert_equals(document.designMode, "off", "check for designMode value");
+    assert_true(document.queryCommandSupported("delete"));
+    assert_false(document.queryCommandEnabled("delete"));
    }, "initial designMode attribute");
    document.designMode="on";
    test(function() {
     assert_equals(document.designMode, "on", "check for designMode value");
+    assert_true(document.queryCommandSupported("delete"));
+    assert_true(document.queryCommandEnabled("delete"));
    }, "set designMode = \"on\"");
    document.designMode="off";
    test(function() {
     assert_equals(document.designMode,"off", "check for designMode value");
+    assert_true(document.queryCommandSupported("delete"));
+    assert_false(document.queryCommandEnabled("delete"));
    }, "set designMode = \"off\"");
   </script>
  </body>
diff --git a/third_party/libusb/BUILD.gn b/third_party/libusb/BUILD.gn
index 4e0e07f..5bb0b03 100644
--- a/third_party/libusb/BUILD.gn
+++ b/third_party/libusb/BUILD.gn
@@ -11,6 +11,18 @@
   include_dirs = [ "src/libusb" ]
 }
 
+config("libusb_linker_config") {
+  if (is_win) {
+    ldflags = [
+      "/DELAYLOAD:advapi32.dll",
+      "/DELAYLOAD:cfgmgr32.dll",
+      "/DELAYLOAD:ole32.dll",
+      "/DELAYLOAD:setupapi.dll",
+      "/DELAYLOAD:winusb.dll",
+    ]
+  }
+}
+
 config("libusb_warnings") {
   visibility = [ ":*" ]
   if (is_clang) {
@@ -71,6 +83,7 @@
   ]
 
   public_configs = [ ":libusb_config" ]
+  all_dependent_configs = [ ":libusb_linker_config" ]
 
   if (is_posix) {
     defines = [
@@ -137,7 +150,12 @@
       "src/libusb/os/poll_posix.c",
       "src/libusb/os/threads_posix.c",
     ]
-    libs = [ "setupapi.lib" ]
+    libs = [
+      "advapi32.lib",
+      "ole32.lib",
+      "setupapi.lib",
+      "winusb.lib",
+    ]
   } else {
     include_dirs += [ "src" ]
     sources -= [
diff --git a/third_party/libusb/src/libusb/os/windows_common.h b/third_party/libusb/src/libusb/os/windows_common.h
index 1da72bd9..1ca688c 100644
--- a/third_party/libusb/src/libusb/os/windows_common.h
+++ b/third_party/libusb/src/libusb/os/windows_common.h
@@ -61,48 +61,3 @@
 #define ERR_BUFFER_SIZE             256
 #define TIMER_REQUEST_RETRY_MS      100
 #define MAX_TIMER_SEMAPHORES        128
-
-
-/*
- * API macros - from libusb-win32 1.x
- */
-#define DLL_DECLARE_PREFIXNAME(api, ret, prefixname, name, args)    \
-	typedef ret (api * __dll_##name##_t)args;                       \
-	static __dll_##name##_t prefixname = NULL
-
-#ifndef _WIN32_WCE
-#define DLL_STRINGIFY(dll) #dll
-#define DLL_GET_MODULE_HANDLE(dll) GetModuleHandleA(DLL_STRINGIFY(dll))
-#define DLL_LOAD_LIBRARY(dll) LoadLibraryA(DLL_STRINGIFY(dll))
-#else
-#define DLL_STRINGIFY(dll) L#dll
-#define DLL_GET_MODULE_HANDLE(dll) GetModuleHandle(DLL_STRINGIFY(dll))
-#define DLL_LOAD_LIBRARY(dll) LoadLibrary(DLL_STRINGIFY(dll))
-#endif
-
-#define DLL_LOAD_PREFIXNAME(dll, prefixname, name, ret_on_failure) \
-	do {                                                           \
-		HMODULE h = DLL_GET_MODULE_HANDLE(dll);                    \
-	if (!h)                                                        \
-		h = DLL_LOAD_LIBRARY(dll);                                 \
-	if (!h) {                                                      \
-		if (ret_on_failure) { return LIBUSB_ERROR_NOT_FOUND; }     \
-		else { break; }                                            \
-	}                                                              \
-	prefixname = (__dll_##name##_t)GetProcAddress(h,               \
-	                        DLL_STRINGIFY(name));                  \
-	if (prefixname) break;                                         \
-	prefixname = (__dll_##name##_t)GetProcAddress(h,               \
-	                        DLL_STRINGIFY(name) DLL_STRINGIFY(A)); \
-	if (prefixname) break;                                         \
-	prefixname = (__dll_##name##_t)GetProcAddress(h,               \
-	                        DLL_STRINGIFY(name) DLL_STRINGIFY(W)); \
-	if (prefixname) break;                                         \
-	if(ret_on_failure)                                             \
-		return LIBUSB_ERROR_NOT_FOUND;                             \
-	} while(0)
-
-#define DLL_DECLARE(api, ret, name, args)   DLL_DECLARE_PREFIXNAME(api, ret, name, name, args)
-#define DLL_LOAD(dll, name, ret_on_failure) DLL_LOAD_PREFIXNAME(dll, name, name, ret_on_failure)
-#define DLL_DECLARE_PREFIXED(api, ret, prefix, name, args)   DLL_DECLARE_PREFIXNAME(api, ret, prefix##name, name, args)
-#define DLL_LOAD_PREFIXED(dll, prefix, name, ret_on_failure) DLL_LOAD_PREFIXNAME(dll, prefix##name, name, ret_on_failure)
diff --git a/third_party/libusb/src/libusb/os/windows_usb.c b/third_party/libusb/src/libusb/os/windows_usb.c
index 9caab76..481bfd3 100644
--- a/third_party/libusb/src/libusb/os/windows_usb.c
+++ b/third_party/libusb/src/libusb/os/windows_usb.c
@@ -22,9 +22,13 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+#define INITGUID
 #include <config.h>
 #include <windows.h>
+#include <cfgmgr32.h>
 #include <setupapi.h>
+#include <usbioctl.h>
+#include <winusb.h>
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -99,9 +103,6 @@
 HANDLE timer_request[2] = { NULL, NULL };
 HANDLE timer_response = NULL;
 // API globals
-#define CHECK_WINUSBX_AVAILABLE(sub_api) do { if (sub_api == SUB_API_NOTSET) sub_api = priv->sub_api; \
-	if (!WinUSBX[sub_api].initialized) return LIBUSB_ERROR_ACCESS; } while(0)
-static struct winusb_interface WinUSBX[SUB_API_MAX];
 const char* sub_api_name[SUB_API_MAX] = WINUSBX_DRV_NAMES;
 
 static inline BOOLEAN guid_eq(const GUID *guid1, const GUID *guid2) {
@@ -206,30 +207,6 @@
 }
 
 /*
- * Cfgmgr32, OLE32 and SetupAPI DLL functions
- */
-static int init_dlls(void)
-{
-	DLL_LOAD(Cfgmgr32.dll, CM_Get_Parent, TRUE);
-	DLL_LOAD(Cfgmgr32.dll, CM_Get_Child, TRUE);
-	DLL_LOAD(Cfgmgr32.dll, CM_Get_Sibling, TRUE);
-	DLL_LOAD(Cfgmgr32.dll, CM_Get_Device_IDA, TRUE);
-	// Prefixed to avoid conflict with header files
-	DLL_LOAD_PREFIXED(OLE32.dll, p, CLSIDFromString, TRUE);
-	DLL_LOAD_PREFIXED(SetupAPI.dll, p, SetupDiGetClassDevsA, TRUE);
-	DLL_LOAD_PREFIXED(SetupAPI.dll, p, SetupDiEnumDeviceInfo, TRUE);
-	DLL_LOAD_PREFIXED(SetupAPI.dll, p, SetupDiEnumDeviceInterfaces, TRUE);
-	DLL_LOAD_PREFIXED(SetupAPI.dll, p, SetupDiGetDeviceInterfaceDetailA, TRUE);
-	DLL_LOAD_PREFIXED(SetupAPI.dll, p, SetupDiDestroyDeviceInfoList, TRUE);
-	DLL_LOAD_PREFIXED(SetupAPI.dll, p, SetupDiOpenDevRegKey, TRUE);
-	DLL_LOAD_PREFIXED(SetupAPI.dll, p, SetupDiGetDeviceRegistryPropertyA, TRUE);
-	DLL_LOAD_PREFIXED(SetupAPI.dll, p, SetupDiOpenDeviceInterfaceRegKey, TRUE);
-	DLL_LOAD_PREFIXED(AdvAPI32.dll, p, RegQueryValueExW, TRUE);
-	DLL_LOAD_PREFIXED(AdvAPI32.dll, p, RegCloseKey, TRUE);
-	return LIBUSB_SUCCESS;
-}
-
-/*
  * enumerate interfaces for the whole USB class
  *
  * Parameters:
@@ -246,19 +223,19 @@
 	HDEVINFO *dev_info, SP_DEVINFO_DATA *dev_info_data, const char* usb_class, unsigned _index)
 {
 	if (_index <= 0) {
-		*dev_info = pSetupDiGetClassDevsA(NULL, usb_class, NULL, DIGCF_PRESENT|DIGCF_ALLCLASSES);
+		*dev_info = SetupDiGetClassDevsA(NULL, usb_class, NULL, DIGCF_PRESENT|DIGCF_ALLCLASSES);
 		if (*dev_info == INVALID_HANDLE_VALUE) {
 			return false;
 		}
 	}
 
 	dev_info_data->cbSize = sizeof(SP_DEVINFO_DATA);
-	if (!pSetupDiEnumDeviceInfo(*dev_info, _index, dev_info_data)) {
+	if (!SetupDiEnumDeviceInfo(*dev_info, _index, dev_info_data)) {
 		if (GetLastError() != ERROR_NO_MORE_ITEMS) {
 			usbi_err(ctx, "Could not obtain device info data for index %u: %s",
 				_index, windows_error_str(0));
 		}
-		pSetupDiDestroyDeviceInfoList(*dev_info);
+		SetupDiDestroyDeviceInfoList(*dev_info);
 		*dev_info = INVALID_HANDLE_VALUE;
 		return false;
 	}
@@ -286,35 +263,35 @@
 	DWORD size;
 
 	if (_index <= 0) {
-		*dev_info = pSetupDiGetClassDevsA(guid, NULL, NULL, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);
+		*dev_info = SetupDiGetClassDevsA(guid, NULL, NULL, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);
 	}
 
 	if (dev_info_data != NULL) {
 		dev_info_data->cbSize = sizeof(SP_DEVINFO_DATA);
-		if (!pSetupDiEnumDeviceInfo(*dev_info, _index, dev_info_data)) {
+		if (!SetupDiEnumDeviceInfo(*dev_info, _index, dev_info_data)) {
 			if (GetLastError() != ERROR_NO_MORE_ITEMS) {
 				usbi_err(ctx, "Could not obtain device info data for index %u: %s",
 					_index, windows_error_str(0));
 			}
-			pSetupDiDestroyDeviceInfoList(*dev_info);
+			SetupDiDestroyDeviceInfoList(*dev_info);
 			*dev_info = INVALID_HANDLE_VALUE;
 			return NULL;
 		}
 	}
 
 	dev_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
-	if (!pSetupDiEnumDeviceInterfaces(*dev_info, NULL, guid, _index, &dev_interface_data)) {
+	if (!SetupDiEnumDeviceInterfaces(*dev_info, NULL, guid, _index, &dev_interface_data)) {
 		if (GetLastError() != ERROR_NO_MORE_ITEMS) {
 			usbi_err(ctx, "Could not obtain interface data for index %u: %s",
 				_index, windows_error_str(0));
 		}
-		pSetupDiDestroyDeviceInfoList(*dev_info);
+		SetupDiDestroyDeviceInfoList(*dev_info);
 		*dev_info = INVALID_HANDLE_VALUE;
 		return NULL;
 	}
 
 	// Read interface data (dummy + actual) to access the device path
-	if (!pSetupDiGetDeviceInterfaceDetailA(*dev_info, &dev_interface_data, NULL, 0, &size, NULL)) {
+	if (!SetupDiGetDeviceInterfaceDetailA(*dev_info, &dev_interface_data, NULL, 0, &size, NULL)) {
 		// The dummy call should fail with ERROR_INSUFFICIENT_BUFFER
 		if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
 			usbi_err(ctx, "could not access interface data (dummy) for index %u: %s",
@@ -332,7 +309,7 @@
 	}
 
 	dev_interface_details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);
-	if (!pSetupDiGetDeviceInterfaceDetailA(*dev_info, &dev_interface_data,
+	if (!SetupDiGetDeviceInterfaceDetailA(*dev_info, &dev_interface_data,
 		dev_interface_details, size, &size, NULL)) {
 		usbi_err(ctx, "could not access interface data (actual) for index %u: %s",
 			_index, windows_error_str(0));
@@ -341,7 +318,7 @@
 	return dev_interface_details;
 
 err_exit:
-	pSetupDiDestroyDeviceInfoList(*dev_info);
+	SetupDiDestroyDeviceInfoList(*dev_info);
 	*dev_info = INVALID_HANDLE_VALUE;
 	return NULL;
 }
@@ -353,32 +330,32 @@
 	SP_DEVICE_INTERFACE_DETAIL_DATA_A *dev_interface_details = NULL;
 	DWORD size;
 	if (_index <= 0) {
-		*dev_info = pSetupDiGetClassDevsA(guid, NULL, NULL, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);
+		*dev_info = SetupDiGetClassDevsA(guid, NULL, NULL, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);
 	}
 	if (dev_info_data != NULL) {
 		dev_info_data->cbSize = sizeof(SP_DEVINFO_DATA);
-		if (!pSetupDiEnumDeviceInfo(*dev_info, _index, dev_info_data)) {
+		if (!SetupDiEnumDeviceInfo(*dev_info, _index, dev_info_data)) {
 			if (GetLastError() != ERROR_NO_MORE_ITEMS) {
 				usbi_err(ctx, "Could not obtain device info data for index %u: %s",
 					_index, windows_error_str(0));
 			}
-			pSetupDiDestroyDeviceInfoList(*dev_info);
+			SetupDiDestroyDeviceInfoList(*dev_info);
 			*dev_info = INVALID_HANDLE_VALUE;
 			return NULL;
 		}
 	}
 	dev_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
-	if (!pSetupDiEnumDeviceInterfaces(*dev_info, NULL, guid, _index, &dev_interface_data)) {
+	if (!SetupDiEnumDeviceInterfaces(*dev_info, NULL, guid, _index, &dev_interface_data)) {
 		if (GetLastError() != ERROR_NO_MORE_ITEMS) {
 			usbi_err(ctx, "Could not obtain interface data for index %u: %s",
 				_index, windows_error_str(0));
 		}
-		pSetupDiDestroyDeviceInfoList(*dev_info);
+		SetupDiDestroyDeviceInfoList(*dev_info);
 		*dev_info = INVALID_HANDLE_VALUE;
 		return NULL;
 	}
 	// Read interface data (dummy + actual) to access the device path
-	if (!pSetupDiGetDeviceInterfaceDetailA(*dev_info, &dev_interface_data, NULL, 0, &size, NULL)) {
+	if (!SetupDiGetDeviceInterfaceDetailA(*dev_info, &dev_interface_data, NULL, 0, &size, NULL)) {
 		// The dummy call should fail with ERROR_INSUFFICIENT_BUFFER
 		if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
 			usbi_err(ctx, "could not access interface data (dummy) for index %u: %s",
@@ -394,20 +371,20 @@
 		goto err_exit;
 	}
 	dev_interface_details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);
-	if (!pSetupDiGetDeviceInterfaceDetailA(*dev_info, &dev_interface_data,
+	if (!SetupDiGetDeviceInterfaceDetailA(*dev_info, &dev_interface_data,
 		dev_interface_details, size, &size, NULL)) {
 		usbi_err(ctx, "could not access interface data (actual) for index %u: %s",
 			_index, windows_error_str(0));
 	}
 	// [trobinso] lookup the libusb0 symbolic index.
 	if (dev_interface_details) {
-		HKEY hkey_device_interface=pSetupDiOpenDeviceInterfaceRegKey(*dev_info,&dev_interface_data,0,KEY_READ);
+		HKEY hkey_device_interface=SetupDiOpenDeviceInterfaceRegKey(*dev_info,&dev_interface_data,0,KEY_READ);
 		if (hkey_device_interface != INVALID_HANDLE_VALUE) {
 			DWORD libusb0_symboliclink_index=0;
 			DWORD value_length=sizeof(DWORD);
 			DWORD value_type=0;
 			LONG status;
-			status = pRegQueryValueExW(hkey_device_interface, L"LUsb0", NULL, &value_type,
+			status = RegQueryValueExW(hkey_device_interface, L"LUsb0", NULL, &value_type,
 				(LPBYTE) &libusb0_symboliclink_index, &value_length);
 			if (status == ERROR_SUCCESS) {
 				if (libusb0_symboliclink_index < 256) {
@@ -419,12 +396,12 @@
 					// libusb0.sys was connected to this device instance at one time; but not anymore.
 				}
 			}
-			pRegCloseKey(hkey_device_interface);
+			RegCloseKey(hkey_device_interface);
 		}
 	}
 	return dev_interface_details;
 err_exit:
-	pSetupDiDestroyDeviceInfoList(*dev_info);
+	SetupDiDestroyDeviceInfoList(*dev_info);
 	*dev_info = INVALID_HANDLE_VALUE;
 	return NULL;}
 
@@ -836,12 +813,6 @@
 		// Initialize pollable file descriptors
 		init_polling();
 
-		// Load DLL imports
-		if (init_dlls() != LIBUSB_SUCCESS) {
-			usbi_err(ctx, "could not resolve DLL functions");
-			return LIBUSB_ERROR_NOT_FOUND;
-		}
-
 		// Initialize the low level APIs (we don't care about errors at this stage)
 		for (i=0; i<USB_API_MAX; i++) {
 			usb_api_backend[i].init(SUB_API_NOTSET, ctx);
@@ -1194,7 +1165,7 @@
 	*sub_api = SUB_API_NOTSET;
 	// Check the service & filter names to know the API we should use
 	for (k=0; k<3; k++) {
-		if (pSetupDiGetDeviceRegistryPropertyA(*dev_info, dev_info_data, lookup[k].reg_prop,
+		if (SetupDiGetDeviceRegistryPropertyA(*dev_info, dev_info_data, lookup[k].reg_prop,
 			&reg_type, (BYTE*)lookup[k].list, MAX_KEY_LENGTH, &size)) {
 			// Turn the REG_SZ SPDRP_SERVICE into REG_MULTI_SZ
 			if (lookup[k].reg_prop == SPDRP_SERVICE) {
@@ -1399,7 +1370,7 @@
 			// The SPDRP_ADDRESS for USB devices is the device port number on the hub
 			port_nr = 0;
 			if ((pass >= HUB_PASS) && (pass <= GEN_PASS)) {
-				if ( (!pSetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_ADDRESS,
+				if ( (!SetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_ADDRESS,
 					&reg_type, (BYTE*)&port_nr, 4, &size))
 				  || (size != 4) ) {
 					usbi_warn(ctx, "could not retrieve port number for device '%s', skipping: %s",
@@ -1417,18 +1388,18 @@
 			case GEN_PASS:
 				// We use the GEN pass to detect driverless devices...
 				size = sizeof(strbuf);
-				if (!pSetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_DRIVER,
+				if (!SetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_DRIVER,
 					&reg_type, (BYTE*)strbuf, size, &size)) {
 						usbi_info(ctx, "The following device has no driver: '%s'", dev_id_path);
 						usbi_info(ctx, "libusbx will not be able to access it.");
 				}
 				// ...and to add the additional device interface GUIDs
-				key = pSetupDiOpenDevRegKey(dev_info, &dev_info_data, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
+				key = SetupDiOpenDevRegKey(dev_info, &dev_info_data, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
 				if (key != INVALID_HANDLE_VALUE) {
 					size = sizeof(guid_string_w);
-					s = pRegQueryValueExW(key, L"DeviceInterfaceGUIDs", NULL, &reg_type,
+					s = RegQueryValueExW(key, L"DeviceInterfaceGUIDs", NULL, &reg_type,
 						(BYTE*)guid_string_w, &size);
-					pRegCloseKey(key);
+					RegCloseKey(key);
 					if (s == ERROR_SUCCESS) {
 						if (nb_guids >= MAX_ENUM_GUIDS) {
 							// If this assert is ever reported, grow a GUID table dynamically
@@ -1436,7 +1407,7 @@
 							LOOP_BREAK(LIBUSB_ERROR_OVERFLOW);
 						}
 						if_guid = (GUID*) calloc(1, sizeof(GUID));
-						pCLSIDFromString(guid_string_w, if_guid);
+						CLSIDFromString(guid_string_w, if_guid);
 						guid[nb_guids++] = if_guid;
 						usbi_dbg("extra GUID: %s", guid_to_string(if_guid));
 					}
@@ -1444,7 +1415,7 @@
 				break;
 			default:
 				// Get the API type (after checking that the driver installation is OK)
-				if ( (!pSetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_INSTALL_STATE,
+				if ( (!SetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_INSTALL_STATE,
 					&reg_type, (BYTE*)&install_state, 4, &size))
 				  || (size != 4) ){
 					usbi_warn(ctx, "could not detect installation state of driver for '%s': %s",
@@ -2405,79 +2376,9 @@
 /*
  * WinUSB-like (WinUSB, libusb0/libusbK through libusbk DLL) API functions
  */
-#define WinUSBX_Set(fn) do { if (native_winusb) WinUSBX[i].fn = (WinUsb_##fn##_t) GetProcAddress(h, "WinUsb_" #fn); \
-	else pLibK_GetProcAddress((PVOID*)&WinUSBX[i].fn, i, KUSB_FNID_##fn); } while (0)
 
 static int winusbx_init(int sub_api, struct libusb_context *ctx)
 {
-	HMODULE h = NULL;
-	bool native_winusb = false;
-	int i;
-	KLIB_VERSION LibK_Version;
-	LibK_GetProcAddress_t pLibK_GetProcAddress = NULL;
-	LibK_GetVersion_t pLibK_GetVersion = NULL;
-
-	h = GetModuleHandleA("libusbK");
-	if (h == NULL) {
-		h = LoadLibraryA("libusbK");
-	}
-	if (h == NULL) {
-		usbi_info(ctx, "libusbK DLL is not available, will use native WinUSB");
-		h = GetModuleHandleA("WinUSB");
-		if (h == NULL) {
-			h = LoadLibraryA("WinUSB");
-		}		if (h == NULL) {
-			usbi_warn(ctx, "WinUSB DLL is not available either,\n"
-				"you will not be able to access devices outside of enumeration");
-			return LIBUSB_ERROR_NOT_FOUND;
-		}
-	} else {
-		usbi_dbg("using libusbK DLL for universal access");
-		pLibK_GetVersion = (LibK_GetVersion_t) GetProcAddress(h, "LibK_GetVersion");
-		if (pLibK_GetVersion != NULL) {
-			pLibK_GetVersion(&LibK_Version);
-			usbi_dbg("libusbK version: %d.%d.%d.%d", LibK_Version.Major, LibK_Version.Minor,
-				LibK_Version.Micro, LibK_Version.Nano);
-		}
-		pLibK_GetProcAddress = (LibK_GetProcAddress_t) GetProcAddress(h, "LibK_GetProcAddress");
-		if (pLibK_GetProcAddress == NULL) {
-			usbi_err(ctx, "LibK_GetProcAddress() not found in libusbK DLL");
-			return LIBUSB_ERROR_NOT_FOUND;
-		}
-	}
-	native_winusb = (pLibK_GetProcAddress == NULL);
-	for (i=SUB_API_LIBUSBK; i<SUB_API_MAX; i++) {
-		WinUSBX_Set(AbortPipe);
-		WinUSBX_Set(ControlTransfer);
-		WinUSBX_Set(FlushPipe);
-		WinUSBX_Set(Free);
-		WinUSBX_Set(GetAssociatedInterface);
-		WinUSBX_Set(GetCurrentAlternateSetting);
-		WinUSBX_Set(GetDescriptor);
-		WinUSBX_Set(GetOverlappedResult);
-		WinUSBX_Set(GetPipePolicy);
-		WinUSBX_Set(GetPowerPolicy);
-		WinUSBX_Set(Initialize);
-		WinUSBX_Set(QueryDeviceInformation);
-		WinUSBX_Set(QueryInterfaceSettings);
-		WinUSBX_Set(QueryPipe);
-		WinUSBX_Set(ReadPipe);
-		WinUSBX_Set(ResetPipe);
-		WinUSBX_Set(SetCurrentAlternateSetting);
-		WinUSBX_Set(SetPipePolicy);
-		WinUSBX_Set(SetPowerPolicy);
-		WinUSBX_Set(WritePipe);
-		if (!native_winusb) {
-			WinUSBX_Set(ResetDevice);
-		}
-		if (WinUSBX[i].Initialize != NULL) {
-			WinUSBX[i].initialized = true;
-			usbi_dbg("initalized sub API %s", sub_api_name[i]);
-		} else {
-			usbi_warn(ctx, "Failed to initalize sub API %s", sub_api_name[i]);
-			WinUSBX[i].initialized = false;
-		}
-	}
 	return LIBUSB_SUCCESS;
 }
 
@@ -2498,8 +2399,6 @@
 	HANDLE file_handle;
 	int i;
 
-	CHECK_WINUSBX_AVAILABLE(sub_api);
-
 	// WinUSB requires a seperate handle for each interface
 	for (i = 0; i < USB_MAXINTERFACES; i++) {
 		if ( (priv->usb_interface[i].path != NULL)
@@ -2533,8 +2432,6 @@
 
 	if (sub_api == SUB_API_NOTSET)
 		sub_api = priv->sub_api;
-	if (!WinUSBX[sub_api].initialized)
-		return;
 
 	for (i = 0; i < USB_MAXINTERFACES; i++) {
 		if (priv->usb_interface[i].apib->id == USB_API_WINUSBX) {
@@ -2556,13 +2453,11 @@
 	uint8_t endpoint_address;
 	int i;
 
-	CHECK_WINUSBX_AVAILABLE(sub_api);
-
 	// With handle and enpoints set (in parent), we can setup the default pipe properties
 	// see http://download.microsoft.com/download/D/1/D/D1DD7745-426B-4CC3-A269-ABBBE427C0EF/DVC-T705_DDC08.pptx
 	for (i=-1; i<priv->usb_interface[iface].nb_endpoints; i++) {
 		endpoint_address =(i==-1)?0:priv->usb_interface[iface].endpoint[i];
-		if (!WinUSBX[sub_api].SetPipePolicy(winusb_handle, endpoint_address,
+		if (!WinUsb_SetPipePolicy(winusb_handle, endpoint_address,
 			PIPE_TRANSFER_TIMEOUT, sizeof(ULONG), &timeout)) {
 			usbi_dbg("failed to set PIPE_TRANSFER_TIMEOUT for control endpoint %02X", endpoint_address);
 		}
@@ -2570,22 +2465,22 @@
 			continue;	// Other policies don't apply to control endpoint or libusb0
 		}
 		policy = false;
-		if (!WinUSBX[sub_api].SetPipePolicy(winusb_handle, endpoint_address,
+		if (!WinUsb_SetPipePolicy(winusb_handle, endpoint_address,
 			SHORT_PACKET_TERMINATE, sizeof(UCHAR), &policy)) {
 			usbi_dbg("failed to disable SHORT_PACKET_TERMINATE for endpoint %02X", endpoint_address);
 		}
-		if (!WinUSBX[sub_api].SetPipePolicy(winusb_handle, endpoint_address,
+		if (!WinUsb_SetPipePolicy(winusb_handle, endpoint_address,
 			IGNORE_SHORT_PACKETS, sizeof(UCHAR), &policy)) {
 			usbi_dbg("failed to disable IGNORE_SHORT_PACKETS for endpoint %02X", endpoint_address);
 		}
 		policy = true;
 		/* ALLOW_PARTIAL_READS must be enabled due to likely libusbK bug. See:
 		   https://sourceforge.net/mailarchive/message.php?msg_id=29736015 */
-		if (!WinUSBX[sub_api].SetPipePolicy(winusb_handle, endpoint_address,
+		if (!WinUsb_SetPipePolicy(winusb_handle, endpoint_address,
 			ALLOW_PARTIAL_READS, sizeof(UCHAR), &policy)) {
 			usbi_dbg("failed to enable ALLOW_PARTIAL_READS for endpoint %02X", endpoint_address);
 		}
-		if (!WinUSBX[sub_api].SetPipePolicy(winusb_handle, endpoint_address,
+		if (!WinUsb_SetPipePolicy(winusb_handle, endpoint_address,
 			AUTO_CLEAR_STALL, sizeof(UCHAR), &policy)) {
 			usbi_dbg("failed to enable AUTO_CLEAR_STALL for endpoint %02X", endpoint_address);
 		}
@@ -2610,8 +2505,6 @@
 	char filter_path[] = "\\\\.\\libusb0-0000";
 	bool found_filter = false;
 
-	CHECK_WINUSBX_AVAILABLE(sub_api);
-
 	// If the device is composite, but using the default Windows composite parent driver (usbccgp)
 	// or if it's the first WinUSB-like interface, we get a handle through Initialize().
 	if ((is_using_usbccgp) || (iface == 0)) {
@@ -2621,7 +2514,7 @@
 			return LIBUSB_ERROR_NOT_FOUND;
 		}
 
-		if (!WinUSBX[sub_api].Initialize(file_handle, &winusb_handle)) {
+		if (!WinUsb_Initialize(file_handle, &winusb_handle)) {
 			handle_priv->interface_handle[iface].api_handle = INVALID_HANDLE_VALUE;
 			err = GetLastError();
 			switch(err) {
@@ -2647,8 +2540,8 @@
 						if (file_handle == INVALID_HANDLE_VALUE) {
 							usbi_err(ctx, "could not open device %s: %s", filter_path, windows_error_str(0));
 						} else {
-							WinUSBX[sub_api].Free(winusb_handle);
-							if (!WinUSBX[sub_api].Initialize(file_handle, &winusb_handle)) {
+							WinUsb_Free(winusb_handle);
+							if (!WinUsb_Initialize(file_handle, &winusb_handle)) {
 								continue;
 							}
 							found_filter = true;
@@ -2670,7 +2563,7 @@
 		// must first claim the first interface before you claim the others
 		if ((winusb_handle == 0) || (winusb_handle == INVALID_HANDLE_VALUE)) {
 			file_handle = handle_priv->interface_handle[0].dev_handle;
-			if (WinUSBX[sub_api].Initialize(file_handle, &winusb_handle)) {
+			if (WinUsb_Initialize(file_handle, &winusb_handle)) {
 				handle_priv->interface_handle[0].api_handle = winusb_handle;
 				usbi_warn(ctx, "auto-claimed interface 0 (required to claim %d with WinUSB)", iface);
 			} else {
@@ -2678,7 +2571,7 @@
 				return LIBUSB_ERROR_ACCESS;
 			}
 		}
-		if (!WinUSBX[sub_api].GetAssociatedInterface(winusb_handle, (UCHAR)(iface-1),
+		if (!WinUsb_GetAssociatedInterface(winusb_handle, (UCHAR)(iface-1),
 			&handle_priv->interface_handle[iface].api_handle)) {
 			handle_priv->interface_handle[iface].api_handle = INVALID_HANDLE_VALUE;
 			switch(GetLastError()) {
@@ -2706,14 +2599,12 @@
 	struct windows_device_priv *priv = _device_priv(dev_handle->dev);
 	HANDLE winusb_handle;
 
-	CHECK_WINUSBX_AVAILABLE(sub_api);
-
 	winusb_handle = handle_priv->interface_handle[iface].api_handle;
 	if ((winusb_handle == 0) || (winusb_handle == INVALID_HANDLE_VALUE)) {
 		return LIBUSB_ERROR_NOT_FOUND;
 	}
 
-	WinUSBX[sub_api].Free(winusb_handle);
+	WinUsb_Free(winusb_handle);
 	handle_priv->interface_handle[iface].api_handle = INVALID_HANDLE_VALUE;
 
 	return LIBUSB_SUCCESS;
@@ -2782,8 +2673,6 @@
 	int current_interface;
 	struct winfd wfd;
 
-	CHECK_WINUSBX_AVAILABLE(sub_api);
-
 	transfer_priv->pollable_fd = INVALID_WINFD;
 	size = transfer->length - LIBUSB_CONTROL_SETUP_SIZE;
 
@@ -2807,9 +2696,9 @@
 	}
 
 	// Sending of set configuration control requests from WinUSB creates issues
-	if ( ((setup->request_type & (0x03 << 5)) == LIBUSB_REQUEST_TYPE_STANDARD)
-	  && (setup->request == LIBUSB_REQUEST_SET_CONFIGURATION) ) {
-		if (setup->value != priv->active_config) {
+	if ( ((setup->RequestType & (0x03 << 5)) == LIBUSB_REQUEST_TYPE_STANDARD)
+	  && (setup->Request == LIBUSB_REQUEST_SET_CONFIGURATION) ) {
+		if (setup->Value != priv->active_config) {
 			usbi_warn(ctx, "cannot set configuration other than the default one");
 			usbi_free_fd(&wfd);
 			return LIBUSB_ERROR_INVALID_PARAM;
@@ -2817,7 +2706,7 @@
 		wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY;
 		wfd.overlapped->InternalHigh = 0;
 	} else {
-		if (!WinUSBX[sub_api].ControlTransfer(wfd.handle, *setup, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, size, NULL, wfd.overlapped)) {
+		if (!WinUsb_ControlTransfer(wfd.handle, *setup, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, size, NULL, wfd.overlapped)) {
 			if(GetLastError() != ERROR_IO_PENDING) {
 				usbi_warn(ctx, "ControlTransfer failed: %s", windows_error_str(0));
 				usbi_free_fd(&wfd);
@@ -2843,8 +2732,6 @@
 	struct windows_device_priv *priv = _device_priv(dev_handle->dev);
 	HANDLE winusb_handle;
 
-	CHECK_WINUSBX_AVAILABLE(sub_api);
-
 	if (altsetting > 255) {
 		return LIBUSB_ERROR_INVALID_PARAM;
 	}
@@ -2855,7 +2742,7 @@
 		return LIBUSB_ERROR_NOT_FOUND;
 	}
 
-	if (!WinUSBX[sub_api].SetCurrentAlternateSetting(winusb_handle, (UCHAR)altsetting)) {
+	if (!WinUsb_SetCurrentAlternateSetting(winusb_handle, (UCHAR)altsetting)) {
 		usbi_err(ctx, "SetCurrentAlternateSetting failed: %s", windows_error_str(0));
 		return LIBUSB_ERROR_IO;
 	}
@@ -2875,8 +2762,6 @@
 	int current_interface;
 	struct winfd wfd;
 
-	CHECK_WINUSBX_AVAILABLE(sub_api);
-
 	transfer_priv->pollable_fd = INVALID_WINFD;
 
 	current_interface = interface_by_endpoint(priv, handle_priv, transfer->endpoint);
@@ -2897,10 +2782,10 @@
 
 	if (IS_XFERIN(transfer)) {
 		usbi_dbg("reading %d bytes", transfer->length);
-		ret = WinUSBX[sub_api].ReadPipe(wfd.handle, transfer->endpoint, transfer->buffer, transfer->length, NULL, wfd.overlapped);
+		ret = WinUsb_ReadPipe(wfd.handle, transfer->endpoint, transfer->buffer, transfer->length, NULL, wfd.overlapped);
 	} else {
 		usbi_dbg("writing %d bytes", transfer->length);
-		ret = WinUSBX[sub_api].WritePipe(wfd.handle, transfer->endpoint, transfer->buffer, transfer->length, NULL, wfd.overlapped);
+		ret = WinUsb_WritePipe(wfd.handle, transfer->endpoint, transfer->buffer, transfer->length, NULL, wfd.overlapped);
 	}
 	if (!ret) {
 		if(GetLastError() != ERROR_IO_PENDING) {
@@ -2927,8 +2812,6 @@
 	HANDLE winusb_handle;
 	int current_interface;
 
-	CHECK_WINUSBX_AVAILABLE(sub_api);
-
 	current_interface = interface_by_endpoint(priv, handle_priv, endpoint);
 	if (current_interface < 0) {
 		usbi_err(ctx, "unable to match endpoint to an open interface - cannot clear");
@@ -2938,7 +2821,7 @@
 	usbi_dbg("matched endpoint %02X with interface %d", endpoint, current_interface);
 	winusb_handle = handle_priv->interface_handle[current_interface].api_handle;
 
-	if (!WinUSBX[sub_api].ResetPipe(winusb_handle, endpoint)) {
+	if (!WinUsb_ResetPipe(winusb_handle, endpoint)) {
 		usbi_err(ctx, "ResetPipe failed: %s", windows_error_str(0));
 		return LIBUSB_ERROR_NO_DEVICE;
 	}
@@ -2968,8 +2851,6 @@
 	HANDLE winusb_handle;
 	int current_interface;
 
-	CHECK_WINUSBX_AVAILABLE(sub_api);
-
 	current_interface = transfer_priv->interface_number;
 	if ((current_interface < 0) || (current_interface >= USB_MAXINTERFACES)) {
 		usbi_err(ctx, "program assertion failed: invalid interface_number");
@@ -2979,7 +2860,7 @@
 
 	winusb_handle = handle_priv->interface_handle[current_interface].api_handle;
 
-	if (!WinUSBX[sub_api].AbortPipe(winusb_handle, transfer->endpoint)) {
+	if (!WinUsb_AbortPipe(winusb_handle, transfer->endpoint)) {
 		usbi_err(ctx, "AbortPipe failed: %s", windows_error_str(0));
 		return LIBUSB_ERROR_NO_DEVICE;
 	}
@@ -3005,8 +2886,6 @@
 	HANDLE winusb_handle;
 	int i, j;
 
-	CHECK_WINUSBX_AVAILABLE(sub_api);
-
 	// Reset any available pipe (except control)
 	for (i=0; i<USB_MAXINTERFACES; i++) {
 		winusb_handle = handle_priv->interface_handle[i].api_handle;
@@ -3021,17 +2900,17 @@
 		if ( (winusb_handle != 0) && (winusb_handle != INVALID_HANDLE_VALUE)) {
 			for (j=0; j<priv->usb_interface[i].nb_endpoints; j++) {
 				usbi_dbg("resetting ep %02X", priv->usb_interface[i].endpoint[j]);
-				if (!WinUSBX[sub_api].AbortPipe(winusb_handle, priv->usb_interface[i].endpoint[j])) {
+				if (!WinUsb_AbortPipe(winusb_handle, priv->usb_interface[i].endpoint[j])) {
 					usbi_err(ctx, "AbortPipe (pipe address %02X) failed: %s",
 						priv->usb_interface[i].endpoint[j], windows_error_str(0));
 				}
 				// FlushPipe seems to fail on OUT pipes
 				if (IS_EPIN(priv->usb_interface[i].endpoint[j])
-				  && (!WinUSBX[sub_api].FlushPipe(winusb_handle, priv->usb_interface[i].endpoint[j])) ) {
+				  && (!WinUsb_FlushPipe(winusb_handle, priv->usb_interface[i].endpoint[j])) ) {
 					usbi_err(ctx, "FlushPipe (pipe address %02X) failed: %s",
 						priv->usb_interface[i].endpoint[j], windows_error_str(0));
 				}
-				if (!WinUSBX[sub_api].ResetPipe(winusb_handle, priv->usb_interface[i].endpoint[j])) {
+				if (!WinUsb_ResetPipe(winusb_handle, priv->usb_interface[i].endpoint[j])) {
 					usbi_err(ctx, "ResetPipe (pipe address %02X) failed: %s",
 						priv->usb_interface[i].endpoint[j], windows_error_str(0));
 				}
@@ -3039,13 +2918,6 @@
 		}
 	}
 
-	// libusbK & libusb0 have the ability to issue an actual device reset
-	if (WinUSBX[sub_api].ResetDevice != NULL) {
-		winusb_handle = handle_priv->interface_handle[0].api_handle;
-		if ( (winusb_handle != 0) && (winusb_handle != INVALID_HANDLE_VALUE)) {
-			WinUSBX[sub_api].ResetDevice(winusb_handle);
-		}
-	}
 	return LIBUSB_SUCCESS;
 }
 
diff --git a/third_party/libusb/src/libusb/os/windows_usb.h b/third_party/libusb/src/libusb/os/windows_usb.h
index 97c8047c..e02a6dc 100644
--- a/third_party/libusb/src/libusb/os/windows_usb.h
+++ b/third_party/libusb/src/libusb/os/windows_usb.h
@@ -58,18 +58,6 @@
 #define LIST_SEPARATOR              ';'
 #define HTAB_SIZE                   1021
 
-// http://msdn.microsoft.com/en-us/library/ff545978.aspx
-// http://msdn.microsoft.com/en-us/library/ff545972.aspx
-// http://msdn.microsoft.com/en-us/library/ff545982.aspx
-#if !defined(GUID_DEVINTERFACE_USB_HOST_CONTROLLER)
-const GUID GUID_DEVINTERFACE_USB_HOST_CONTROLLER = { 0x3ABF6F2D, 0x71C4, 0x462A, {0x8A, 0x92, 0x1E, 0x68, 0x61, 0xE6, 0xAF, 0x27} };
-#endif
-#if !defined(GUID_DEVINTERFACE_USB_DEVICE)
-const GUID GUID_DEVINTERFACE_USB_DEVICE = { 0xA5DCBF10, 0x6530, 0x11D2, {0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED} };
-#endif
-#if !defined(GUID_DEVINTERFACE_USB_HUB)
-const GUID GUID_DEVINTERFACE_USB_HUB = { 0xF18A0E88, 0xC30C, 0x11D0, {0x88, 0x15, 0x00, 0xA0, 0xC9, 0x06, 0xBE, 0xD8} };
-#endif
 #if !defined(GUID_DEVINTERFACE_LIBUSB0_FILTER)
 const GUID GUID_DEVINTERFACE_LIBUSB0_FILTER = { 0xF9F3FF14, 0xAE21, 0x48A0, {0x8A, 0x25, 0x80, 0x11, 0xA7, 0xA9, 0x31, 0xD9} };
 #endif
@@ -136,7 +124,6 @@
 #define LIBUSB_REQ_IN(request_type) ((request_type) & LIBUSB_ENDPOINT_IN)
 #define LIBUSB_REQ_OUT(request_type) (!LIBUSB_REQ_IN(request_type))
 
-typedef struct libusb_device_descriptor USB_DEVICE_DESCRIPTOR, *PUSB_DEVICE_DESCRIPTOR;
 struct windows_device_priv {
 	uint8_t depth;						// distance to HCD
 	uint8_t port;						// port number on the hub
@@ -229,24 +216,6 @@
 	const char* designation;	// internal designation (for debug output)
 };
 
-/* OLE32 dependency */
-DLL_DECLARE_PREFIXED(WINAPI, HRESULT, p, CLSIDFromString, (LPCOLESTR, LPCLSID));
-
-/* SetupAPI dependencies */
-DLL_DECLARE_PREFIXED(WINAPI, HDEVINFO, p, SetupDiGetClassDevsA, (const GUID*, PCSTR, HWND, DWORD));
-DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, SetupDiEnumDeviceInfo, (HDEVINFO, DWORD, PSP_DEVINFO_DATA));
-DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, SetupDiEnumDeviceInterfaces, (HDEVINFO, PSP_DEVINFO_DATA,
-			const GUID*, DWORD, PSP_DEVICE_INTERFACE_DATA));
-DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, SetupDiGetDeviceInterfaceDetailA, (HDEVINFO, PSP_DEVICE_INTERFACE_DATA,
-			PSP_DEVICE_INTERFACE_DETAIL_DATA_A, DWORD, PDWORD, PSP_DEVINFO_DATA));
-DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, SetupDiDestroyDeviceInfoList, (HDEVINFO));
-DLL_DECLARE_PREFIXED(WINAPI, HKEY, p, SetupDiOpenDevRegKey, (HDEVINFO, PSP_DEVINFO_DATA, DWORD, DWORD, DWORD, REGSAM));
-DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, SetupDiGetDeviceRegistryPropertyA, (HDEVINFO,
-			PSP_DEVINFO_DATA, DWORD, PDWORD, PBYTE, DWORD, PDWORD));
-DLL_DECLARE_PREFIXED(WINAPI, HKEY, p, SetupDiOpenDeviceInterfaceRegKey, (HDEVINFO, PSP_DEVICE_INTERFACE_DATA, DWORD, DWORD));
-DLL_DECLARE_PREFIXED(WINAPI, LONG, p, RegQueryValueExW, (HKEY, LPCWSTR, LPDWORD, LPDWORD, LPBYTE, LPDWORD));
-DLL_DECLARE_PREFIXED(WINAPI, LONG, p, RegCloseKey, (HKEY));
-
 /*
  * Windows DDK API definitions. Most of it copied from MinGW's includes
  */
@@ -255,27 +224,6 @@
 typedef DWORD RETURN_TYPE;
 typedef RETURN_TYPE CONFIGRET;
 
-#define CR_SUCCESS                              0x00000000
-#define CR_NO_SUCH_DEVNODE                      0x0000000D
-
-#define USB_DEVICE_DESCRIPTOR_TYPE              LIBUSB_DT_DEVICE
-#define USB_CONFIGURATION_DESCRIPTOR_TYPE       LIBUSB_DT_CONFIG
-#define USB_STRING_DESCRIPTOR_TYPE              LIBUSB_DT_STRING
-#define USB_INTERFACE_DESCRIPTOR_TYPE           LIBUSB_DT_INTERFACE
-#define USB_ENDPOINT_DESCRIPTOR_TYPE            LIBUSB_DT_ENDPOINT
-
-#define USB_REQUEST_GET_STATUS                  LIBUSB_REQUEST_GET_STATUS
-#define USB_REQUEST_CLEAR_FEATURE               LIBUSB_REQUEST_CLEAR_FEATURE
-#define USB_REQUEST_SET_FEATURE                 LIBUSB_REQUEST_SET_FEATURE
-#define USB_REQUEST_SET_ADDRESS                 LIBUSB_REQUEST_SET_ADDRESS
-#define USB_REQUEST_GET_DESCRIPTOR              LIBUSB_REQUEST_GET_DESCRIPTOR
-#define USB_REQUEST_SET_DESCRIPTOR              LIBUSB_REQUEST_SET_DESCRIPTOR
-#define USB_REQUEST_GET_CONFIGURATION           LIBUSB_REQUEST_GET_CONFIGURATION
-#define USB_REQUEST_SET_CONFIGURATION           LIBUSB_REQUEST_SET_CONFIGURATION
-#define USB_REQUEST_GET_INTERFACE               LIBUSB_REQUEST_GET_INTERFACE
-#define USB_REQUEST_SET_INTERFACE               LIBUSB_REQUEST_SET_INTERFACE
-#define USB_REQUEST_SYNC_FRAME                  LIBUSB_REQUEST_SYNCH_FRAME
-
 #define USB_GET_NODE_INFORMATION                258
 #define USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION 260
 #define USB_GET_NODE_CONNECTION_NAME            261
@@ -305,32 +253,6 @@
   ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
 #endif
 
-typedef enum USB_CONNECTION_STATUS {
-	NoDeviceConnected,
-	DeviceConnected,
-	DeviceFailedEnumeration,
-	DeviceGeneralFailure,
-	DeviceCausedOvercurrent,
-	DeviceNotEnoughPower,
-	DeviceNotEnoughBandwidth,
-	DeviceHubNestedTooDeeply,
-	DeviceInLegacyHub
-} USB_CONNECTION_STATUS, *PUSB_CONNECTION_STATUS;
-
-typedef enum USB_HUB_NODE {
-	UsbHub,
-	UsbMIParent
-} USB_HUB_NODE;
-
-/* Cfgmgr32.dll interface */
-DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Parent, (PDEVINST, DEVINST, ULONG));
-DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Child, (PDEVINST, DEVINST, ULONG));
-DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Sibling, (PDEVINST, DEVINST, ULONG));
-DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Device_IDA, (DEVINST, PCHAR, ULONG, ULONG));
-
-#define IOCTL_USB_GET_HUB_CAPABILITIES_EX \
-  CTL_CODE( FILE_DEVICE_USB, USB_GET_HUB_CAPABILITIES_EX, METHOD_BUFFERED, FILE_ANY_ACCESS)
-
 #define IOCTL_USB_GET_HUB_CAPABILITIES \
   CTL_CODE(FILE_DEVICE_USB, USB_GET_HUB_CAPABILITIES, METHOD_BUFFERED, FILE_ANY_ACCESS)
 
@@ -355,397 +277,23 @@
 // Most of the structures below need to be packed
 #pragma pack(push, 1)
 
-typedef struct USB_INTERFACE_DESCRIPTOR {
-  UCHAR  bLength;
-  UCHAR  bDescriptorType;
-  UCHAR  bInterfaceNumber;
-  UCHAR  bAlternateSetting;
-  UCHAR  bNumEndpoints;
-  UCHAR  bInterfaceClass;
-  UCHAR  bInterfaceSubClass;
-  UCHAR  bInterfaceProtocol;
-  UCHAR  iInterface;
-} USB_INTERFACE_DESCRIPTOR, *PUSB_INTERFACE_DESCRIPTOR;
-
-typedef struct USB_CONFIGURATION_DESCRIPTOR {
-  UCHAR  bLength;
-  UCHAR  bDescriptorType;
-  USHORT wTotalLength;
-  UCHAR  bNumInterfaces;
-  UCHAR  bConfigurationValue;
-  UCHAR  iConfiguration;
-  UCHAR  bmAttributes;
-  UCHAR  MaxPower;
-} USB_CONFIGURATION_DESCRIPTOR, *PUSB_CONFIGURATION_DESCRIPTOR;
-
 typedef struct USB_CONFIGURATION_DESCRIPTOR_SHORT {
-	struct {
-		ULONG ConnectionIndex;
-		struct {
-			UCHAR bmRequest;
-			UCHAR bRequest;
-			USHORT wValue;
-			USHORT wIndex;
-			USHORT wLength;
-		} SetupPacket;
-	} req;
-	USB_CONFIGURATION_DESCRIPTOR data;
+  struct {
+    ULONG ConnectionIndex;
+    struct {
+      UCHAR bmRequest;
+      UCHAR bRequest;
+      USHORT wValue;
+      USHORT wIndex;
+      USHORT wLength;
+    } SetupPacket;
+  } req;
+  USB_CONFIGURATION_DESCRIPTOR data;
 } USB_CONFIGURATION_DESCRIPTOR_SHORT;
 
-typedef struct USB_ENDPOINT_DESCRIPTOR {
-  UCHAR  bLength;
-  UCHAR  bDescriptorType;
-  UCHAR  bEndpointAddress;
-  UCHAR  bmAttributes;
-  USHORT  wMaxPacketSize;
-  UCHAR  bInterval;
-} USB_ENDPOINT_DESCRIPTOR, *PUSB_ENDPOINT_DESCRIPTOR;
-
-typedef struct USB_DESCRIPTOR_REQUEST {
-	ULONG  ConnectionIndex;
-	struct {
-		UCHAR  bmRequest;
-		UCHAR  bRequest;
-		USHORT  wValue;
-		USHORT  wIndex;
-		USHORT  wLength;
-	} SetupPacket;
-//	UCHAR  Data[0];
-} USB_DESCRIPTOR_REQUEST, *PUSB_DESCRIPTOR_REQUEST;
-
-typedef struct USB_HUB_DESCRIPTOR {
-	UCHAR  bDescriptorLength;
-	UCHAR  bDescriptorType;
-	UCHAR  bNumberOfPorts;
-	USHORT  wHubCharacteristics;
-	UCHAR  bPowerOnToPowerGood;
-	UCHAR  bHubControlCurrent;
-	UCHAR  bRemoveAndPowerMask[64];
-} USB_HUB_DESCRIPTOR, *PUSB_HUB_DESCRIPTOR;
-
-typedef struct USB_ROOT_HUB_NAME {
-	ULONG  ActualLength;
-	WCHAR  RootHubName[1];
-} USB_ROOT_HUB_NAME, *PUSB_ROOT_HUB_NAME;
-
 typedef struct USB_ROOT_HUB_NAME_FIXED {
 	ULONG ActualLength;
 	WCHAR RootHubName[MAX_PATH_LENGTH];
 } USB_ROOT_HUB_NAME_FIXED;
 
-typedef struct USB_NODE_CONNECTION_NAME {
-	ULONG  ConnectionIndex;
-	ULONG  ActualLength;
-	WCHAR  NodeName[1];
-} USB_NODE_CONNECTION_NAME, *PUSB_NODE_CONNECTION_NAME;
-
-typedef struct USB_NODE_CONNECTION_NAME_FIXED {
-	ULONG ConnectionIndex;
-	ULONG ActualLength;
-	WCHAR NodeName[MAX_PATH_LENGTH];
-} USB_NODE_CONNECTION_NAME_FIXED;
-
-typedef struct USB_HUB_NAME_FIXED {
-	union {
-		USB_ROOT_HUB_NAME_FIXED root;
-		USB_NODE_CONNECTION_NAME_FIXED node;
-	} u;
-} USB_HUB_NAME_FIXED;
-
-typedef struct USB_HUB_INFORMATION {
-	USB_HUB_DESCRIPTOR  HubDescriptor;
-	BOOLEAN  HubIsBusPowered;
-} USB_HUB_INFORMATION, *PUSB_HUB_INFORMATION;
-
-typedef struct USB_MI_PARENT_INFORMATION {
-  ULONG  NumberOfInterfaces;
-} USB_MI_PARENT_INFORMATION, *PUSB_MI_PARENT_INFORMATION;
-
-typedef struct USB_NODE_INFORMATION {
-	USB_HUB_NODE  NodeType;
-	union {
-		USB_HUB_INFORMATION  HubInformation;
-		USB_MI_PARENT_INFORMATION  MiParentInformation;
-	} u;
-} USB_NODE_INFORMATION, *PUSB_NODE_INFORMATION;
-
-typedef struct USB_PIPE_INFO {
-	USB_ENDPOINT_DESCRIPTOR  EndpointDescriptor;
-	ULONG  ScheduleOffset;
-} USB_PIPE_INFO, *PUSB_PIPE_INFO;
-
-typedef struct USB_NODE_CONNECTION_INFORMATION_EX {
-	ULONG  ConnectionIndex;
-	USB_DEVICE_DESCRIPTOR  DeviceDescriptor;
-	UCHAR  CurrentConfigurationValue;
-	UCHAR  Speed;
-	BOOLEAN  DeviceIsHub;
-	USHORT  DeviceAddress;
-	ULONG  NumberOfOpenPipes;
-	USB_CONNECTION_STATUS  ConnectionStatus;
-//	USB_PIPE_INFO  PipeList[0];
-} USB_NODE_CONNECTION_INFORMATION_EX, *PUSB_NODE_CONNECTION_INFORMATION_EX;
-
-typedef struct USB_HUB_CAP_FLAGS {
-	ULONG HubIsHighSpeedCapable:1;
-	ULONG HubIsHighSpeed:1;
-	ULONG HubIsMultiTtCapable:1;
-	ULONG HubIsMultiTt:1;
-	ULONG HubIsRoot:1;
-	ULONG HubIsArmedWakeOnConnect:1;
-	ULONG ReservedMBZ:26;
-} USB_HUB_CAP_FLAGS, *PUSB_HUB_CAP_FLAGS;
-
-typedef struct USB_HUB_CAPABILITIES {
-  ULONG  HubIs2xCapable : 1;
-} USB_HUB_CAPABILITIES, *PUSB_HUB_CAPABILITIES;
-
-typedef struct USB_HUB_CAPABILITIES_EX {
-	USB_HUB_CAP_FLAGS CapabilityFlags;
-} USB_HUB_CAPABILITIES_EX, *PUSB_HUB_CAPABILITIES_EX;
-
 #pragma pack(pop)
-
-/* winusb.dll interface */
-
-#define SHORT_PACKET_TERMINATE  0x01
-#define AUTO_CLEAR_STALL        0x02
-#define PIPE_TRANSFER_TIMEOUT   0x03
-#define IGNORE_SHORT_PACKETS    0x04
-#define ALLOW_PARTIAL_READS     0x05
-#define AUTO_FLUSH              0x06
-#define RAW_IO                  0x07
-#define MAXIMUM_TRANSFER_SIZE   0x08
-#define AUTO_SUSPEND            0x81
-#define SUSPEND_DELAY           0x83
-#define DEVICE_SPEED            0x01
-#define LowSpeed                0x01
-#define FullSpeed               0x02
-#define HighSpeed               0x03
-
-typedef enum USBD_PIPE_TYPE {
-	UsbdPipeTypeControl,
-	UsbdPipeTypeIsochronous,
-	UsbdPipeTypeBulk,
-	UsbdPipeTypeInterrupt
-} USBD_PIPE_TYPE;
-
-typedef struct {
-  USBD_PIPE_TYPE PipeType;
-  UCHAR          PipeId;
-  USHORT         MaximumPacketSize;
-  UCHAR          Interval;
-} WINUSB_PIPE_INFORMATION, *PWINUSB_PIPE_INFORMATION;
-
-#pragma pack(1)
-typedef struct {
-  UCHAR  request_type;
-  UCHAR  request;
-  USHORT value;
-  USHORT index;
-  USHORT length;
-} WINUSB_SETUP_PACKET, *PWINUSB_SETUP_PACKET;
-#pragma pack()
-
-typedef void *WINUSB_INTERFACE_HANDLE, *PWINUSB_INTERFACE_HANDLE;
-
-typedef BOOL (WINAPI *WinUsb_AbortPipe_t)(
-	WINUSB_INTERFACE_HANDLE InterfaceHandle,
-	UCHAR PipeID
-);
-typedef BOOL (WINAPI *WinUsb_ControlTransfer_t)(
-	WINUSB_INTERFACE_HANDLE InterfaceHandle,
-	WINUSB_SETUP_PACKET SetupPacket,
-	PUCHAR Buffer,
-	ULONG BufferLength,
-	PULONG LengthTransferred,
-	LPOVERLAPPED Overlapped
-);
-typedef BOOL (WINAPI *WinUsb_FlushPipe_t)(
-	WINUSB_INTERFACE_HANDLE InterfaceHandle,
-	UCHAR PipeID
-);
-typedef BOOL (WINAPI *WinUsb_Free_t)(
-	WINUSB_INTERFACE_HANDLE InterfaceHandle
-);
-typedef BOOL (WINAPI *WinUsb_GetAssociatedInterface_t)(
-	WINUSB_INTERFACE_HANDLE InterfaceHandle,
-	UCHAR AssociatedInterfaceIndex,
-	PWINUSB_INTERFACE_HANDLE AssociatedInterfaceHandle
-);
-typedef BOOL (WINAPI *WinUsb_GetCurrentAlternateSetting_t)(
-	WINUSB_INTERFACE_HANDLE InterfaceHandle,
-	PUCHAR AlternateSetting
-);
-typedef BOOL (WINAPI *WinUsb_GetDescriptor_t)(
-	WINUSB_INTERFACE_HANDLE InterfaceHandle,
-	UCHAR DescriptorType,
-	UCHAR Index,
-	USHORT LanguageID,
-	PUCHAR Buffer,
-	ULONG BufferLength,
-	PULONG LengthTransferred
-);
-typedef BOOL (WINAPI *WinUsb_GetOverlappedResult_t)(
-	WINUSB_INTERFACE_HANDLE InterfaceHandle,
-	LPOVERLAPPED lpOverlapped,
-	LPDWORD lpNumberOfBytesTransferred,
-	BOOL bWait
-);
-typedef BOOL (WINAPI *WinUsb_GetPipePolicy_t)(
-	WINUSB_INTERFACE_HANDLE InterfaceHandle,
-	UCHAR PipeID,
-	ULONG PolicyType,
-	PULONG ValueLength,
-	PVOID Value
-);
-typedef BOOL (WINAPI *WinUsb_GetPowerPolicy_t)(
-	WINUSB_INTERFACE_HANDLE InterfaceHandle,
-	ULONG PolicyType,
-	PULONG ValueLength,
-	PVOID Value
-);
-typedef BOOL (WINAPI *WinUsb_Initialize_t)(
-	HANDLE DeviceHandle,
-	PWINUSB_INTERFACE_HANDLE InterfaceHandle
-);
-typedef BOOL (WINAPI *WinUsb_QueryDeviceInformation_t)(
-	WINUSB_INTERFACE_HANDLE InterfaceHandle,
-	ULONG InformationType,
-	PULONG BufferLength,
-	PVOID Buffer
-);
-typedef BOOL (WINAPI *WinUsb_QueryInterfaceSettings_t)(
-	WINUSB_INTERFACE_HANDLE InterfaceHandle,
-	UCHAR AlternateSettingNumber,
-	PUSB_INTERFACE_DESCRIPTOR UsbAltInterfaceDescriptor
-);
-typedef BOOL (WINAPI *WinUsb_QueryPipe_t)(
-	WINUSB_INTERFACE_HANDLE InterfaceHandle,
-	UCHAR AlternateInterfaceNumber,
-	UCHAR PipeIndex,
-	PWINUSB_PIPE_INFORMATION PipeInformation
-);
-typedef BOOL (WINAPI *WinUsb_ReadPipe_t)(
-	WINUSB_INTERFACE_HANDLE InterfaceHandle,
-	UCHAR PipeID,
-	PUCHAR Buffer,
-	ULONG BufferLength,
-	PULONG LengthTransferred,
-	LPOVERLAPPED Overlapped
-);
-typedef BOOL (WINAPI *WinUsb_ResetPipe_t)(
-	WINUSB_INTERFACE_HANDLE InterfaceHandle,
-	UCHAR PipeID
-);
-typedef BOOL (WINAPI *WinUsb_SetCurrentAlternateSetting_t)(
-	WINUSB_INTERFACE_HANDLE InterfaceHandle,
-	UCHAR AlternateSetting
-);
-typedef BOOL (WINAPI *WinUsb_SetPipePolicy_t)(
-	WINUSB_INTERFACE_HANDLE InterfaceHandle,
-	UCHAR PipeID,
-	ULONG PolicyType,
-	ULONG ValueLength,
-	PVOID Value
-);
-typedef BOOL (WINAPI *WinUsb_SetPowerPolicy_t)(
-	WINUSB_INTERFACE_HANDLE InterfaceHandle,
-	ULONG PolicyType,
-	ULONG ValueLength,
-	PVOID Value
-);
-typedef BOOL (WINAPI *WinUsb_WritePipe_t)(
-	WINUSB_INTERFACE_HANDLE InterfaceHandle,
-	UCHAR PipeID,
-	PUCHAR Buffer,
-	ULONG BufferLength,
-	PULONG LengthTransferred,
-	LPOVERLAPPED Overlapped
-);
-typedef BOOL (WINAPI *WinUsb_ResetDevice_t)(
-	WINUSB_INTERFACE_HANDLE InterfaceHandle
-);
-
-/* /!\ These must match the ones from the official libusbk.h */
-typedef enum _KUSB_FNID
-{
-	KUSB_FNID_Init,
-	KUSB_FNID_Free,
-	KUSB_FNID_ClaimInterface,
-	KUSB_FNID_ReleaseInterface,
-	KUSB_FNID_SetAltInterface,
-	KUSB_FNID_GetAltInterface,
-	KUSB_FNID_GetDescriptor,
-	KUSB_FNID_ControlTransfer,
-	KUSB_FNID_SetPowerPolicy,
-	KUSB_FNID_GetPowerPolicy,
-	KUSB_FNID_SetConfiguration,
-	KUSB_FNID_GetConfiguration,
-	KUSB_FNID_ResetDevice,
-	KUSB_FNID_Initialize,
-	KUSB_FNID_SelectInterface,
-	KUSB_FNID_GetAssociatedInterface,
-	KUSB_FNID_Clone,
-	KUSB_FNID_QueryInterfaceSettings,
-	KUSB_FNID_QueryDeviceInformation,
-	KUSB_FNID_SetCurrentAlternateSetting,
-	KUSB_FNID_GetCurrentAlternateSetting,
-	KUSB_FNID_QueryPipe,
-	KUSB_FNID_SetPipePolicy,
-	KUSB_FNID_GetPipePolicy,
-	KUSB_FNID_ReadPipe,
-	KUSB_FNID_WritePipe,
-	KUSB_FNID_ResetPipe,
-	KUSB_FNID_AbortPipe,
-	KUSB_FNID_FlushPipe,
-	KUSB_FNID_IsoReadPipe,
-	KUSB_FNID_IsoWritePipe,
-	KUSB_FNID_GetCurrentFrameNumber,
-	KUSB_FNID_GetOverlappedResult,
-	KUSB_FNID_GetProperty,
-	KUSB_FNID_COUNT,
-} KUSB_FNID; 
-
-typedef struct _KLIB_VERSION {
-	INT Major;
-	INT Minor;
-	INT Micro;
-	INT Nano;
-} KLIB_VERSION;
-typedef KLIB_VERSION* PKLIB_VERSION;
-
-typedef BOOL (WINAPI *LibK_GetProcAddress_t)(
-	PVOID* ProcAddress,
-	ULONG DriverID,
-	ULONG FunctionID
-);
-
-typedef VOID (WINAPI *LibK_GetVersion_t)(
-	PKLIB_VERSION Version
-);
-
-struct winusb_interface {
-	bool initialized;
-	WinUsb_AbortPipe_t AbortPipe;
-	WinUsb_ControlTransfer_t ControlTransfer;
-	WinUsb_FlushPipe_t FlushPipe;
-	WinUsb_Free_t Free;
-	WinUsb_GetAssociatedInterface_t GetAssociatedInterface;
-	WinUsb_GetCurrentAlternateSetting_t GetCurrentAlternateSetting;
-	WinUsb_GetDescriptor_t GetDescriptor;
-	WinUsb_GetOverlappedResult_t GetOverlappedResult;
-	WinUsb_GetPipePolicy_t GetPipePolicy;
-	WinUsb_GetPowerPolicy_t GetPowerPolicy;
-	WinUsb_Initialize_t Initialize;
-	WinUsb_QueryDeviceInformation_t QueryDeviceInformation;
-	WinUsb_QueryInterfaceSettings_t QueryInterfaceSettings;
-	WinUsb_QueryPipe_t QueryPipe;
-	WinUsb_ReadPipe_t ReadPipe;
-	WinUsb_ResetPipe_t ResetPipe;
-	WinUsb_SetCurrentAlternateSetting_t SetCurrentAlternateSetting;
-	WinUsb_SetPipePolicy_t SetPipePolicy;
-	WinUsb_SetPowerPolicy_t SetPowerPolicy;
-	WinUsb_WritePipe_t WritePipe;
-	WinUsb_ResetDevice_t ResetDevice;
-};
\ No newline at end of file
diff --git a/third_party/polymer/v1_0/components-chromium/paper-ripple/paper-ripple-extracted.js b/third_party/polymer/v1_0/components-chromium/paper-ripple/paper-ripple-extracted.js
index 74089a2a..72abdd0 100644
--- a/third_party/polymer/v1_0/components-chromium/paper-ripple/paper-ripple-extracted.js
+++ b/third_party/polymer/v1_0/components-chromium/paper-ripple/paper-ripple-extracted.js
@@ -79,6 +79,11 @@
     this.debounce('show ripple', function() { this.__showRipple(e); }, 1);
   },
 
+  clear: function() {
+    this.__hideRipple();
+    this.holdDown = false;
+  },
+
   showAndHoldDown: function() {
     this.ripples.forEach(ripple => {
       ripple.remove();
diff --git a/tools/md_browser/gitiles_autolink.py b/tools/md_browser/gitiles_autolink.py
index 5cfeb213..eb77ebb51 100644
--- a/tools/md_browser/gitiles_autolink.py
+++ b/tools/md_browser/gitiles_autolink.py
@@ -6,14 +6,20 @@
 
 This extention auto links basic URLs that aren't bracketed by <...>.
 
-https://gerrit.googlesource.com/gitiles/+/master/gitiles-servlet/src/main/java/com/google/gitiles/Linkifier.java
+https://gerrit.googlesource.com/gitiles/+/master/java/com/google/gitiles/Linkifier.java
 """
 
 from markdown.inlinepatterns import (AutolinkPattern, Pattern)
 from markdown.extensions import Extension
 
 
-AUTOLINK_RE = r'([Hh][Tt][Tt][Pp][Ss]?://[^>]*)'
+# Best effort attempt to match URLs without matching past the end of the URL.
+# The first "[]" is copied from Linkifier.java (safe, reserved, and unsafe
+# characters). The second "[]" is similar to the first, but with English
+# punctuation removed, since the gitiles parser treats these as punction in the
+# sentence, rather than the final character of the URL.
+AUTOLINK_RE = (r'(https?://[a-zA-Z0-9$_.+!*\',%;:@=?#/~<>-]+'
+               r'[a-zA-Z0-9$_+*\'%@=#/~<-])')
 
 
 class _GitilesSmartQuotesExtension(Extension):
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index d63be05b..71f5907 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -4434,6 +4434,7 @@
 
   <int value="0" label="WRLHH_LOGGING_STOPPED_BAD_STATE"/>
   <int value="1" label="PPH_EXTRA_PREVIEW_MESSAGE"/>
+  <int value="2" label="PMF_INVALID_INITIATOR_ORIGIN"/>
 </enum>
 
 <enum name="BadMessageReasonContent">
diff --git a/tools/perf/contrib/cluster_telemetry/OWNERS b/tools/perf/contrib/cluster_telemetry/OWNERS
index 36e9836..b913167 100644
--- a/tools/perf/contrib/cluster_telemetry/OWNERS
+++ b/tools/perf/contrib/cluster_telemetry/OWNERS
@@ -5,6 +5,5 @@
 per-file loading_ct.py=dproy@chromium.org
 per-file repaint*=wkorman@chromium.org
 per-file leak_detection_ct.py=yuzus@chromium.org
-per-file memory_ct.py=nednguyen@google.com
 per-file memory_ct.py=erikchen@chromium.org
 per-file memory_ct.py=perezju@chromium.org
diff --git a/tools/perf/contrib/media_router_benchmarks/BUILD.gn b/tools/perf/contrib/media_router_benchmarks/BUILD.gn
index 5d1554b..3026eb8 100644
--- a/tools/perf/contrib/media_router_benchmarks/BUILD.gn
+++ b/tools/perf/contrib/media_router_benchmarks/BUILD.gn
@@ -2,12 +2,12 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-copy("media_router_telemetry_extension") {
+copy("telemetry_extension_resources") {
   sources = [
     "extension/manifest.json",
     "extension/script.js",
   ]
   outputs = [
-    "$root_out_dir/media_router/media_router_telemetry_extension/{{source_file_part}}",
+    "$root_out_dir/media_router/telemetry_extension/{{source_file_part}}",
   ]
 }
diff --git a/ui/aura/test/test_screen.cc b/ui/aura/test/test_screen.cc
index c7acadf7..01be576b 100644
--- a/ui/aura/test/test_screen.cc
+++ b/ui/aura/test/test_screen.cc
@@ -7,6 +7,7 @@
 #include <stdint.h>
 
 #include "base/logging.h"
+#include "build/build_config.h"
 #include "ui/aura/env.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_event_dispatcher.h"
@@ -17,6 +18,10 @@
 #include "ui/gfx/native_widget_types.h"
 #include "ui/platform_window/platform_window_init_properties.h"
 
+#if defined(OS_FUCHSIA)
+#include "ui/platform_window/fuchsia/initialize_presenter_api_view.h"
+#endif
+
 namespace aura {
 
 namespace {
@@ -42,9 +47,12 @@
 
 WindowTreeHost* TestScreen::CreateHostForPrimaryDisplay() {
   DCHECK(!host_);
-  host_ = WindowTreeHost::Create(ui::PlatformWindowInitProperties{gfx::Rect(
-                                     GetPrimaryDisplay().GetSizeInPixel())})
-              .release();
+  ui::PlatformWindowInitProperties properties(
+      gfx::Rect(GetPrimaryDisplay().GetSizeInPixel()));
+#if defined(OS_FUCHSIA)
+  ui::fuchsia::InitializeViewTokenAndPresentView(&properties);
+#endif
+  host_ = WindowTreeHost::Create(std::move(properties)).release();
   // Some tests don't correctly manage window focus/activation states.
   // Makes sure InputMethod is default focused so that IME basics can work.
   host_->GetInputMethod()->OnFocus();
diff --git a/ui/events/ozone/evdev/input_controller_evdev.cc b/ui/events/ozone/evdev/input_controller_evdev.cc
index 400c1c6..0fe1851 100644
--- a/ui/events/ozone/evdev/input_controller_evdev.cc
+++ b/ui/events/ozone/evdev/input_controller_evdev.cc
@@ -179,6 +179,12 @@
     std::move(reply).Run(std::vector<base::FilePath>());
 }
 
+void InputControllerEvdev::GetGesturePropertiesService(
+    ozone::mojom::GesturePropertiesServiceRequest request) {
+  if (input_device_factory_)
+    input_device_factory_->GetGesturePropertiesService(std::move(request));
+}
+
 void InputControllerEvdev::ScheduleUpdateDeviceSettings() {
   if (!input_device_factory_ || settings_update_pending_)
     return;
diff --git a/ui/events/ozone/evdev/input_controller_evdev.h b/ui/events/ozone/evdev/input_controller_evdev.h
index 947e84c..bddfbc6c 100644
--- a/ui/events/ozone/evdev/input_controller_evdev.h
+++ b/ui/events/ozone/evdev/input_controller_evdev.h
@@ -67,6 +67,8 @@
   void SetTouchscreensEnabled(bool enabled) override;
   void SetInternalKeyboardFilter(bool enable_filter,
                                  std::vector<DomCode> allowed_keys) override;
+  void GetGesturePropertiesService(
+      ozone::mojom::GesturePropertiesServiceRequest request) override;
 
  private:
   // Post task to update settings.
diff --git a/ui/events/ozone/evdev/input_device_factory_evdev.cc b/ui/events/ozone/evdev/input_device_factory_evdev.cc
index 8d9714a..2aead6ff4 100644
--- a/ui/events/ozone/evdev/input_device_factory_evdev.cc
+++ b/ui/events/ozone/evdev/input_device_factory_evdev.cc
@@ -295,6 +295,14 @@
 #endif
 }
 
+void InputDeviceFactoryEvdev::GetGesturePropertiesService(
+    ozone::mojom::GesturePropertiesServiceRequest request) {
+#if defined(USE_EVDEV_GESTURES)
+  gesture_properties_service_ = std::make_unique<GesturePropertiesService>(
+      gesture_property_provider_.get(), std::move(request));
+#endif
+}
+
 base::WeakPtr<InputDeviceFactoryEvdev> InputDeviceFactoryEvdev::GetWeakPtr() {
   return weak_ptr_factory_.GetWeakPtr();
 }
diff --git a/ui/events/ozone/evdev/input_device_factory_evdev.h b/ui/events/ozone/evdev/input_device_factory_evdev.h
index 085908f3..b078fb2 100644
--- a/ui/events/ozone/evdev/input_device_factory_evdev.h
+++ b/ui/events/ozone/evdev/input_device_factory_evdev.h
@@ -23,6 +23,10 @@
 #include "ui/events/ozone/evdev/input_device_settings_evdev.h"
 #include "ui/ozone/public/input_controller.h"
 
+#if defined(USE_EVDEV_GESTURES)
+#include "ui/events/ozone/chromeos/gesture_properties_service.h"
+#endif
+
 namespace ui {
 
 class CursorDelegateEvdev;
@@ -62,6 +66,9 @@
   void GetTouchEventLog(const base::FilePath& out_dir,
                         InputController::GetTouchEventLogReply reply);
 
+  void GetGesturePropertiesService(
+      ozone::mojom::GesturePropertiesServiceRequest request);
+
   base::WeakPtr<InputDeviceFactoryEvdev> GetWeakPtr();
 
  private:
@@ -106,6 +113,7 @@
 #if defined(USE_EVDEV_GESTURES)
   // Gesture library property provider (used by touchpads/mice).
   std::unique_ptr<GesturePropertyProvider> gesture_property_provider_;
+  std::unique_ptr<GesturePropertiesService> gesture_properties_service_;
 #endif
 
   // Dispatcher for events.
diff --git a/ui/events/ozone/evdev/input_device_factory_evdev_proxy.cc b/ui/events/ozone/evdev/input_device_factory_evdev_proxy.cc
index af6a0f7..02822e2 100644
--- a/ui/events/ozone/evdev/input_device_factory_evdev_proxy.cc
+++ b/ui/events/ozone/evdev/input_device_factory_evdev_proxy.cc
@@ -98,4 +98,12 @@
                                     std::move(reply))));
 }
 
+void InputDeviceFactoryEvdevProxy::GetGesturePropertiesService(
+    ozone::mojom::GesturePropertiesServiceRequest request) {
+  task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&InputDeviceFactoryEvdev::GetGesturePropertiesService,
+                     input_device_factory_, std::move(request)));
+}
+
 }  // namespace ui
diff --git a/ui/events/ozone/evdev/input_device_factory_evdev_proxy.h b/ui/events/ozone/evdev/input_device_factory_evdev_proxy.h
index c7c07070..30739e1 100644
--- a/ui/events/ozone/evdev/input_device_factory_evdev_proxy.h
+++ b/ui/events/ozone/evdev/input_device_factory_evdev_proxy.h
@@ -46,6 +46,8 @@
   void GetTouchDeviceStatus(InputController::GetTouchDeviceStatusReply reply);
   void GetTouchEventLog(const base::FilePath& out_dir,
                         InputController::GetTouchEventLogReply reply);
+  void GetGesturePropertiesService(
+      ozone::mojom::GesturePropertiesServiceRequest request);
 
  private:
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
diff --git a/ui/ozone/BUILD.gn b/ui/ozone/BUILD.gn
index 24d7a0c..66463bc9 100644
--- a/ui/ozone/BUILD.gn
+++ b/ui/ozone/BUILD.gn
@@ -117,6 +117,7 @@
     "//ui/gfx/ipc/geometry",
     "//ui/gfx/ipc/skia",
     "//ui/gl",
+    "//ui/ozone/public/interfaces:gesture_properties_service",
   ]
 
   if (enable_vulkan) {
diff --git a/ui/ozone/public/input_controller.cc b/ui/ozone/public/input_controller.cc
index daacfbb..e7443f8 100644
--- a/ui/ozone/public/input_controller.cc
+++ b/ui/ozone/public/input_controller.cc
@@ -56,6 +56,8 @@
   void SetTouchscreensEnabled(bool enabled) override {}
   void SetInternalKeyboardFilter(bool enable_filter,
                                  std::vector<DomCode> allowed_keys) override {}
+  void GetGesturePropertiesService(
+      ui::ozone::mojom::GesturePropertiesServiceRequest request) override {}
 
  private:
   DISALLOW_COPY_AND_ASSIGN(StubInputController);
diff --git a/ui/ozone/public/input_controller.h b/ui/ozone/public/input_controller.h
index f791a8b..711ee67 100644
--- a/ui/ozone/public/input_controller.h
+++ b/ui/ozone/public/input_controller.h
@@ -14,6 +14,7 @@
 #include "base/files/file_path.h"
 #include "base/macros.h"
 #include "ui/ozone/ozone_base_export.h"
+#include "ui/ozone/public/interfaces/gesture_properties_service.mojom.h"
 
 namespace base {
 class TimeDelta;
@@ -88,6 +89,9 @@
   virtual void SetInternalKeyboardFilter(bool enable_filter,
                                          std::vector<DomCode> allowed_keys) = 0;
 
+  virtual void GetGesturePropertiesService(
+      ui::ozone::mojom::GesturePropertiesServiceRequest request) = 0;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(InputController);
 };
diff --git a/ui/webui/resources/cr_elements/cr_button/cr_button.html b/ui/webui/resources/cr_elements/cr_button/cr_button.html
index ee4fa7b..9ea3a2b 100644
--- a/ui/webui/resources/cr_elements/cr_button/cr_button.html
+++ b/ui/webui/resources/cr_elements/cr_button/cr_button.html
@@ -64,6 +64,7 @@
         color: var(--text-color);
         cursor: pointer;
         display: inline-flex;
+        flex-shrink: 0;
         font-weight: 500;
         height: var(--cr-button-height);
         min-width: 5.14em;
@@ -71,7 +72,6 @@
         padding: 8px 16px;
         position: relative;
         user-select: none;
-        white-space: nowrap;
       }
 
       :host-context(.focus-outline-visible):host(:focus) {
diff --git a/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.js b/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.js
index 87ddccae..886ad1c 100644
--- a/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.js
+++ b/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.js
@@ -85,12 +85,12 @@
 
   /** @private */
   showRipple_: function() {
-    this.getRipple().holdDown = true;
+    this.getRipple().showAndHoldDown();
   },
 
   /** @private */
   hideRipple_: function() {
-    this.getRipple().holdDown = false;
+    this.getRipple().clear();
   },
 
   /**
diff --git a/ui/webui/resources/cr_elements/cr_icon_button/cr_icon_button.html b/ui/webui/resources/cr_elements/cr_icon_button/cr_icon_button.html
index c1686cb..605b2567 100644
--- a/ui/webui/resources/cr_elements/cr_icon_button/cr_icon_button.html
+++ b/ui/webui/resources/cr_elements/cr_icon_button/cr_icon_button.html
@@ -13,10 +13,11 @@
             var(--cr-icon-button-icon-size)) / 2);
         --cr-icon-button-icon-size: 20px;
         --cr-icon-button-size: 36px;
+        /* Copied from paper-fab.html. Prevents square touch highlight. */
+        -webkit-tap-highlight-color: transparent;
         background-position: center;
         background-repeat: no-repeat;
         background-size: var(--cr-icon-button-icon-size);
-        border-radius: var(--cr-icon-button-border-radius);
         color: var(--cr-icon-button-color);
         cursor: pointer;
         display: inline-flex;
@@ -54,7 +55,6 @@
       }
 
       #icon {
-        border-radius: var(--cr-icon-button-border-radius);
         display: flex;
         height: var(--cr-icon-button-size);
         /* The |_rippleContainer| must be position relative. */
@@ -65,7 +65,6 @@
       iron-icon {
         --iron-icon-height: var(--cr-icon-button-icon-size);
         --iron-icon-width: var(--cr-icon-button-icon-size);
-        border-radius: var(--cr-icon-button-border-radius);
         padding: var(--cr-icon-button-icon-padding);
       }
     </style>
diff --git a/ui/webui/resources/cr_elements/cr_icon_button/cr_icon_button.js b/ui/webui/resources/cr_elements/cr_icon_button/cr_icon_button.js
index af92b8a..c55bd02 100644
--- a/ui/webui/resources/cr_elements/cr_icon_button/cr_icon_button.js
+++ b/ui/webui/resources/cr_elements/cr_icon_button/cr_icon_button.js
@@ -64,7 +64,7 @@
 
   /** @private */
   hideRipple_: function() {
-    this.getRipple().holdDown = false;
+    this.getRipple().clear();
   },
 
   /** @private */
diff --git a/ui/webui/resources/cr_elements/cr_radio_button/cr_radio_button_behavior.js b/ui/webui/resources/cr_elements/cr_radio_button/cr_radio_button_behavior.js
index e8ed45e2..8ccc382 100644
--- a/ui/webui/resources/cr_elements/cr_radio_button/cr_radio_button_behavior.js
+++ b/ui/webui/resources/cr_elements/cr_radio_button/cr_radio_button_behavior.js
@@ -35,22 +35,20 @@
   },
 
   listeners: {
-    blur: 'cancelRipple_',
+    blur: 'hideRipple_',
     focus: 'onFocus_',
-    pointerup: 'cancelRipple_',
+    up: 'hideRipple_',
   },
 
   /** @private */
   onFocus_: function() {
-    this.ensureRipple();
-    this.$$('paper-ripple').holdDown = true;
+    this.getRipple().showAndHoldDown();
     this.$.button.focus();
   },
 
   /** @private */
-  cancelRipple_: function() {
-    this.ensureRipple();
-    this.$$('paper-ripple').holdDown = false;
+  hideRipple_: function() {
+    this.getRipple().clear();
   },
 
   /** @private */
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 a45cc16..14ede13 100644
--- a/ui/webui/resources/cr_elements/cr_slider/cr_slider.html
+++ b/ui/webui/resources/cr_elements/cr_slider/cr_slider.html
@@ -161,7 +161,7 @@
       }
 
       :host(:hover) #label,
-      :host([hold-down_]) #label {
+      :host([show-label_]) #label {
         opacity: 1;
       }
 
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 1169812..e73ea04e 100644
--- a/ui/webui/resources/cr_elements/cr_slider/cr_slider.js
+++ b/ui/webui/resources/cr_elements/cr_slider/cr_slider.js
@@ -138,20 +138,19 @@
       },
 
       /** @private */
-      holdDown_: {
-        type: Boolean,
-        value: false,
-        observer: 'onHoldDownChanged_',
-        reflectToAttribute: true,
-      },
-
-      /** @private */
       label_: {
         type: String,
         value: '',
       },
 
       /** @private */
+      showLabel_: {
+        type: Boolean,
+        value: false,
+        reflectToAttribute: true,
+      },
+
+      /** @private */
       isRtl_: {
         type: Boolean,
         value: false,
@@ -184,8 +183,8 @@
     ],
 
     listeners: {
-      focus: 'onFocus_',
-      blur: 'onBlur_',
+      blur: 'hideRipple_',
+      focus: 'showRipple_',
       keydown: 'onKeyDown_',
       keyup: 'onKeyUp_',
       pointerdown: 'onPointerDown_',
@@ -260,16 +259,19 @@
       this.draggingEventTracker_.removeAll();
       this.releasePointerCapture(pointerId);
       this.dragging = false;
-      // If there is a ripple animation in progress, setTimeout will hold off
-      // on updating |holdDown_|.
-      setTimeout(() => {
-        this.holdDown_ = false;
-      });
+      this.hideRipple_();
     },
 
     /** @private */
-    onBlur_: function() {
-      this.holdDown_ = false;
+    hideRipple_: function() {
+      this.getRipple().clear();
+      this.showLabel_ = false;
+    },
+
+    /** @private */
+    showRipple_: function() {
+      this.getRipple().showAndHoldDown();
+      this.showLabel_ = true;
     },
 
     /** @private */
@@ -278,20 +280,6 @@
       this.blur();
     },
 
-    /** @private */
-    onFocus_: function() {
-      this.holdDown_ = true;
-
-      if (this.shadowRoot.activeElement == this.$.knob) {
-        return;
-      }
-    },
-
-    /** @private */
-    onHoldDownChanged_: function() {
-      this.getRipple().holdDown = this.holdDown_;
-    },
-
     /**
      * @param {!Event} event
      * @private
@@ -325,9 +313,7 @@
       }
       event.preventDefault();
       event.stopPropagation();
-      setTimeout(() => {
-        this.holdDown_ = true;
-      });
+      this.showRipple_();
     },
 
     /**
@@ -358,12 +344,7 @@
       this.dragging = true;
       this.transiting_ = true;
       this.updateValueFromClientX_(event.clientX);
-      // If there is a ripple animation in progress, setTimeout will hold off on
-      // updating |holdDown_|.
-      setTimeout(() => {
-        this.$.knob.focus();
-        this.holdDown_ = true;
-      });
+      this.showRipple_();
 
       this.setPointerCapture(event.pointerId);
       const stopDragging = this.stopDragging_.bind(this, event.pointerId);
diff --git a/ui/webui/resources/cr_elements/cr_toggle/cr_toggle.js b/ui/webui/resources/cr_elements/cr_toggle/cr_toggle.js
index 092a31b..3f0f7e6b 100644
--- a/ui/webui/resources/cr_elements/cr_toggle/cr_toggle.js
+++ b/ui/webui/resources/cr_elements/cr_toggle/cr_toggle.js
@@ -38,12 +38,12 @@
   },
 
   listeners: {
-    'pointerdown': 'onPointerDown_',
-    'pointerup': 'onPointerUp_',
-    'click': 'onTap_',
-    'keypress': 'onKeyPress_',
-    'focus': 'onFocus_',
-    'blur': 'onBlur_',
+    blur: 'hideRipple_',
+    click: 'onClick_',
+    focus: 'onFocus_',
+    keypress: 'onKeyPress_',
+    pointerdown: 'onPointerDown_',
+    pointerup: 'onPointerUp_',
   },
 
   /** @private {?Function} */
@@ -101,19 +101,18 @@
 
   /** @private */
   onFocus_: function() {
-    this.ensureRipple();
-    this.$$('paper-ripple').holdDown = true;
+    this.getRipple().showAndHoldDown();
   },
 
   /** @private */
-  onBlur_: function() {
-    this.ensureRipple();
-    this.$$('paper-ripple').holdDown = false;
+  hideRipple_: function() {
+    this.getRipple().clear();
   },
 
   /** @private */
-  onPointerUp_: function(e) {
+  onPointerUp_: function() {
     this.removeEventListener('pointermove', this.boundPointerMove_);
+    this.hideRipple_();
   },
 
   /**
@@ -134,8 +133,11 @@
     this.addEventListener('pointermove', this.boundPointerMove_);
   },
 
-  /** @private */
-  onTap_: function(e) {
+  /**
+   * @param {!Event} e
+   * @private
+   */
+  onClick_: function(e) {
     // Prevent |click| event from bubbling. It can cause parents of this
     // elements to erroneously re-toggle this control.
     e.stopPropagation();
@@ -166,8 +168,7 @@
     this.checked = !this.checked;
 
     if (!fromKeyboard) {
-      this.ensureRipple();
-      this.$$('paper-ripple').holdDown = false;
+      this.hideRipple_();
     }
 
     this.fire('change', this.checked);
diff --git a/ui/webui/resources/webui_resources.grd b/ui/webui/resources/webui_resources.grd
index 4307d81..4afe117 100644
--- a/ui/webui/resources/webui_resources.grd
+++ b/ui/webui/resources/webui_resources.grd
@@ -327,9 +327,6 @@
       <structure name="IDR_WEBUI_JS_CR_UI_ARRAY_DATA_MODEL"
                  file="js/cr/ui/array_data_model.js"
                  type="chrome_html" compress="gzip" flattenhtml="true" />
-      <structure name="IDR_WEBUI_JS_CR_UI_AUTOCOMPLETE_LIST"
-                 file="js/cr/ui/autocomplete_list.js"
-                 type="chrome_html" compress="gzip" />
       <structure name="IDR_WEBUI_JS_CR_UI_BUBBLE"
                  file="js/cr/ui/bubble.js" type="chrome_html"
                  compress="gzip" />
@@ -414,23 +411,6 @@
                  type="chrome_html" compress="gzip" />
       <structure name="IDR_WEBUI_JS_CR_UI_GRID"
                  file="js/cr/ui/grid.js" type="chrome_html" compress="gzip" />
-      <structure name="IDR_WEBUI_JS_CR_UI_TABLE"
-                 file="js/cr/ui/table.js" type="chrome_html" compress="gzip" />
-      <structure name="IDR_WEBUI_JS_CR_UI_TABLE_COLUMN"
-                 file="js/cr/ui/table/table_column.js"
-                 type="chrome_html" compress="gzip" />
-      <structure name="IDR_WEBUI_JS_CR_UI_TABLE_COLUMN_MODEL"
-                 file="js/cr/ui/table/table_column_model.js"
-                 type="chrome_html" compress="gzip" />
-      <structure name="IDR_WEBUI_JS_CR_UI_TABLE_HEADER"
-                 file="js/cr/ui/table/table_header.js"
-                 type="chrome_html" compress="gzip" />
-      <structure name="IDR_WEBUI_JS_CR_UI_TABLE_LIST"
-                 file="js/cr/ui/table/table_list.js"
-                 type="chrome_html" compress="gzip" />
-      <structure name="IDR_WEBUI_JS_CR_UI_TABLE_SPLITTER"
-                 file="js/cr/ui/table/table_splitter.js"
-                 type="chrome_html" compress="gzip" />
       <structure name="IDR_WEBUI_JS_CR_UI_TABS"
                  file="js/cr/ui/tabs.js" type="chrome_html" compress="gzip" />
       <structure name="IDR_WEBUI_JS_CR_UI_TREE"