diff --git a/.gn b/.gn
index 01085ea8..e26556c 100644
--- a/.gn
+++ b/.gn
@@ -666,6 +666,8 @@
       # in the Chromium repo outside of //build.
       "//build_overrides/build.gni",
 
+      "//chrome/android/webapk/shell_apk/prepare_upload_dir/BUILD.gn",
+
       # TODO(dgn): Layer violation but breaks the build otherwise, see
       # https://crbug.com/474506.
       "//clank/java/BUILD.gn",
diff --git a/AUTHORS b/AUTHORS
index 42b0db5..a37a5a4 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -214,6 +214,7 @@
 David McAllister <mcdavid@amazon.com>
 David Michael Barr <david.barr@samsung.com>
 David Spellman <dspell@amazon.com>
+David Valachovic <adenflorian@gmail.com>
 Dax Kelson <dkelson@gurulabs.com>
 Debashish Samantaray <d.samantaray@samsung.com>
 Debug Wang <debugwang@tencent.com>
diff --git a/BUILD.gn b/BUILD.gn
index 8209523b..2adee9d2 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -299,6 +299,7 @@
       "//build/android/stacktrace:java_deobfuscate",
       "//chrome/android/webapk/shell_apk:maps_go_webapk",
       "//chrome/android/webapk/shell_apk:webapk",
+      "//chrome/android/webapk/shell_apk/prepare_upload_dir:prepare_webapk_shell_upload_dir",
       "//chrome/test/vr/perf:motopho_latency_test",
       "//components/invalidation/impl:components_invalidation_impl_junit_tests",
       "//components/journey:journey_info_fetcher",
diff --git a/DEPS b/DEPS
index 72dfa18..18a9eb3 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': 'd6199ec7236b18853f58b51c54faae0b8c6cb582',
+  'skia_revision': 'e713a3c7b4b88bf443a885f55e092143ae6542a6',
   # 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': '7b60ccba767a2cde33badc5c35e05e67e8723275',
+  'v8_revision': '19dcbe7f9890eb6816abd69cd80741e6cf99b171',
   # 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,15 +150,15 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'c75d6dc843460627e63709dbf17be70b24df8ddc',
+  'angle_revision': 'a029ce47a966f141d4a49458856186918f057e76',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '0a9f0179ddef5b1ed14ff19ca2ac7f340b7272d5',
+  'swiftshader_revision': '8565e772c67aba70ce53c2672ac03558f3d4ccb2',
   # 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': 'bfc6f146c3434be0116fcdfb573b1f174da3c288',
+  'pdfium_revision': '9010e568478352fba3589e00e47bce925e5b2f04',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -257,7 +257,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'spv_tools_revision': '208d3132e6e8facd91aa7706e4f57bdcd7ce1ec9',
+  'spv_tools_revision': '9c0830133b07203a47ddc101fa4b298bab4438d8',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -273,11 +273,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '05f7ad5ac8f9945d2f52209d6c7c1c64a56e06fb',
+  'dawn_revision': '903fc2b049cbef83ebe87cd007cf826ce33bfe55',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'quiche_revision': '014f56d6a10b0f4806d13e109dc9a1e933ec19fb',
+  'quiche_revision': '91f97e6aab9340363be59458637536ae924496ee',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ios_webkit
   # and whatever else without interference from each other.
@@ -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' + '@' + '457b2fb698ea3075a2d5d3dac02d057f2d297ecd',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'ffcb22f196f4de652086c7eed94fb1084abb3d26',
       'condition': 'checkout_linux',
   },
 
@@ -1355,7 +1355,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '6f0b34abee8dba611c253738d955c59f703c147a',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'ab6fc1154f37e712ffdd729bb1fcdd3e91b2bada',
+    Var('webrtc_git') + '/src.git' + '@' + 'f2b813a95188fe4de904aae842291702ee911e45',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1396,7 +1396,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@9a234fca6ba5fe6f45b7b0264c0b1ed8d0f5c107',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@5e1bff37b5bee5485537587e360e7a0f262b1385',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/browser/cookie_manager.cc b/android_webview/browser/cookie_manager.cc
index 7d5c708..f2d89a9 100644
--- a/android_webview/browser/cookie_manager.cc
+++ b/android_webview/browser/cookie_manager.cc
@@ -23,6 +23,7 @@
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/memory/ref_counted.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/path_service.h"
 #include "base/single_thread_task_runner.h"
 #include "base/synchronization/lock.h"
@@ -66,83 +67,84 @@
 
 namespace android_webview {
 
-// Holds a Java BooleanCookieCallback, knows how to invoke it and turn it
-// into a base callback.
-class BoolCookieCallbackHolder {
- public:
-  BoolCookieCallbackHolder(JNIEnv* env, jobject callback) {
-    callback_.Reset(env, callback);
-    DCHECK(callback_);
-  }
-
-  void Invoke(bool result) {
-    base::android::RunBooleanCallbackAndroid(callback_, result);
-  }
-
-  static base::RepeatingCallback<void(bool)> ConvertToCallback(
-      std::unique_ptr<BoolCookieCallbackHolder> me) {
-    return base::BindRepeating(&BoolCookieCallbackHolder::Invoke,
-                               base::Owned(me.release()));
-  }
-
- private:
-  ScopedJavaGlobalRef<jobject> callback_;
-  DISALLOW_COPY_AND_ASSIGN(BoolCookieCallbackHolder);
-};
-
 namespace {
 
-// TODO(ntfschr): see if we can turn this into OnceCallback.
-// http://crbug.com/932535.
-void MaybeRunCookieCallback(base::RepeatingCallback<void(bool)> callback,
+void MaybeRunCookieCallback(base::OnceCallback<void(bool)> callback,
                             const bool& result) {
   if (callback)
     std::move(callback).Run(result);
 }
 
+const char kSecureCookieHistogramName[] = "Android.WebView.SecureCookieAction";
+
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class SecureCookieAction {
+  kInvalidUrl = 0,
+  kAlreadySecureScheme = 1,
+  kInvalidCookie = 2,
+  kNotASecureCookie = 3,
+  kFixedUp = 4,
+  kMaxValue = kFixedUp,
+};
+
 GURL MaybeFixUpSchemeForSecureCookie(const GURL& host,
                                      const std::string& value) {
   // Log message for catching strict secure cookies related bugs.
-  // TODO(sgurun) temporary. Add UMA stats to monitor, and remove afterwards.
-  // http://crbug.com/933981.
-  if (host.is_valid() &&
-      (!host.has_scheme() || host.SchemeIs(url::kHttpScheme))) {
-    net::ParsedCookie parsed_cookie(value);
-    if (parsed_cookie.IsValid() && parsed_cookie.IsSecure()) {
-      LOG(WARNING) << "Strict Secure Cookie policy does not allow setting a "
-                      "secure cookie for "
-                   << host.spec();
-      GURL::Replacements replace_host;
-      replace_host.SetSchemeStr("https");
-      return host.ReplaceComponents(replace_host);
-    }
+  // TODO(ntfschr): try to remove this, based on UMA stats
+  // (https://crbug.com/933981)
+  if (!host.is_valid()) {
+    base::UmaHistogramEnumeration(kSecureCookieHistogramName,
+                                  SecureCookieAction::kInvalidUrl);
+    return host;
   }
-  return host;
+  if (host.has_scheme() && !host.SchemeIs(url::kHttpScheme)) {
+    base::UmaHistogramEnumeration(kSecureCookieHistogramName,
+                                  SecureCookieAction::kAlreadySecureScheme);
+    return host;
+  }
+  net::ParsedCookie parsed_cookie(value);
+  if (!parsed_cookie.IsValid()) {
+    base::UmaHistogramEnumeration(kSecureCookieHistogramName,
+                                  SecureCookieAction::kInvalidCookie);
+    return host;
+  }
+  if (!parsed_cookie.IsSecure()) {
+    base::UmaHistogramEnumeration(kSecureCookieHistogramName,
+                                  SecureCookieAction::kNotASecureCookie);
+    return host;
+  }
+
+  LOG(WARNING) << "Strict Secure Cookie policy does not allow setting a "
+                  "secure cookie for "
+               << host.spec();
+  base::UmaHistogramEnumeration(kSecureCookieHistogramName,
+                                SecureCookieAction::kFixedUp);
+  GURL::Replacements replace_host;
+  replace_host.SetSchemeStr(url::kHttpsScheme);
+  return host.ReplaceComponents(replace_host);
 }
 
 // Construct a closure which signals a waitable event if and when the closure
 // is called the waitable event must still exist.
-static base::RepeatingClosure SignalEventClosure(WaitableEvent* completion) {
-  return base::BindRepeating(&WaitableEvent::Signal,
-                             base::Unretained(completion));
+static base::OnceClosure SignalEventClosure(WaitableEvent* completion) {
+  return base::BindOnce(&WaitableEvent::Signal, base::Unretained(completion));
 }
 
-static void DiscardBool(base::RepeatingClosure f, bool b) {
-  f.Run();
+static void DiscardBool(base::OnceClosure f, bool b) {
+  std::move(f).Run();
 }
 
-static base::RepeatingCallback<void(bool)> BoolCallbackAdapter(
-    base::RepeatingClosure f) {
-  return base::BindRepeating(&DiscardBool, std::move(f));
+static base::OnceCallback<void(bool)> BoolCallbackAdapter(base::OnceClosure f) {
+  return base::BindOnce(&DiscardBool, std::move(f));
 }
 
-static void DiscardInt(base::RepeatingClosure f, int i) {
-  f.Run();
+static void DiscardInt(base::OnceClosure f, int i) {
+  std::move(f).Run();
 }
 
-static base::RepeatingCallback<void(int)> IntCallbackAdapter(
-    base::RepeatingClosure f) {
-  return base::BindRepeating(&DiscardInt, std::move(f));
+static base::OnceCallback<void(int)> IntCallbackAdapter(base::OnceClosure f) {
+  return base::BindOnce(&DiscardInt, std::move(f));
 }
 
 // Are cookies allowed for file:// URLs by default?
@@ -184,7 +186,7 @@
 //
 // Ignore a bool callback.
 void CookieManager::ExecCookieTaskSync(
-    base::OnceCallback<void(base::RepeatingCallback<void(bool)>)> task) {
+    base::OnceCallback<void(base::OnceCallback<void(bool)>)> task) {
   WaitableEvent completion(base::WaitableEvent::ResetPolicy::AUTOMATIC,
                            base::WaitableEvent::InitialState::NOT_SIGNALED);
   ExecCookieTask(base::BindOnce(
@@ -198,7 +200,7 @@
 
 // Ignore an int callback.
 void CookieManager::ExecCookieTaskSync(
-    base::OnceCallback<void(base::RepeatingCallback<void(int)>)> task) {
+    base::OnceCallback<void(base::OnceCallback<void(int)>)> task) {
   WaitableEvent completion(base::WaitableEvent::ResetPolicy::AUTOMATIC,
                            base::WaitableEvent::InitialState::NOT_SIGNALED);
   ExecCookieTask(base::BindOnce(
@@ -342,15 +344,12 @@
   return AwCookieAccessPolicy::GetInstance()->GetShouldAcceptCookies();
 }
 
-void CookieManager::SetCookie(
-    const GURL& host,
-    const std::string& cookie_value,
-    std::unique_ptr<BoolCookieCallbackHolder> callback_holder) {
-  base::RepeatingCallback<void(bool)> callback =
-      BoolCookieCallbackHolder::ConvertToCallback(std::move(callback_holder));
+void CookieManager::SetCookie(const GURL& host,
+                              const std::string& cookie_value,
+                              base::OnceCallback<void(bool)> callback) {
   ExecCookieTask(base::BindOnce(&CookieManager::SetCookieHelper,
                                 base::Unretained(this), host, cookie_value,
-                                callback));
+                                std::move(callback)));
 }
 
 void CookieManager::SetCookieSync(const GURL& host,
@@ -360,10 +359,9 @@
                                     cookie_value));
 }
 
-void CookieManager::SetCookieHelper(
-    const GURL& host,
-    const std::string& value,
-    const base::RepeatingCallback<void(bool)> callback) {
+void CookieManager::SetCookieHelper(const GURL& host,
+                                    const std::string& value,
+                                    base::OnceCallback<void(bool)> callback) {
   net::CookieOptions options;
   options.set_include_httponly();
 
@@ -388,11 +386,13 @@
     // will make a copy before our smart pointer goes out of scope.
     GetMojoCookieManager()->SetCanonicalCookie(
         *cc.get(), new_host.scheme(), options,
-        net::cookie_util::AdaptCookieInclusionStatusToBool(callback));
+        net::cookie_util::AdaptCookieInclusionStatusToBool(
+            std::move(callback)));
   } else {
     GetCookieStore()->SetCanonicalCookieAsync(
         std::move(cc), new_host.scheme(), options,
-        net::cookie_util::AdaptCookieInclusionStatusToBool(callback));
+        net::cookie_util::AdaptCookieInclusionStatusToBool(
+            std::move(callback)));
   }
 }
 
@@ -435,11 +435,9 @@
 }
 
 void CookieManager::RemoveSessionCookies(
-    std::unique_ptr<BoolCookieCallbackHolder> callback_holder) {
-  base::RepeatingCallback<void(bool)> callback =
-      BoolCookieCallbackHolder::ConvertToCallback(std::move(callback_holder));
+    base::OnceCallback<void(bool)> callback) {
   ExecCookieTask(base::BindOnce(&CookieManager::RemoveSessionCookiesHelper,
-                                base::Unretained(this), callback));
+                                base::Unretained(this), std::move(callback)));
 }
 
 void CookieManager::RemoveSessionCookiesSync() {
@@ -448,7 +446,7 @@
 }
 
 void CookieManager::RemoveSessionCookiesHelper(
-    base::RepeatingCallback<void(bool)> callback) {
+    base::OnceCallback<void(bool)> callback) {
   if (GetMojoCookieManager()) {
     auto match_session_cookies = network::mojom::CookieDeletionFilter::New();
     match_session_cookies->session_control =
@@ -456,26 +454,23 @@
     GetMojoCookieManager()->DeleteCookies(
         std::move(match_session_cookies),
         base::BindOnce(&CookieManager::RemoveCookiesCompleted,
-                       base::Unretained(this), callback));
+                       base::Unretained(this), std::move(callback)));
   } else {
     GetCookieStore()->DeleteSessionCookiesAsync(
         base::BindOnce(&CookieManager::RemoveCookiesCompleted,
-                       base::Unretained(this), callback));
+                       base::Unretained(this), std::move(callback)));
   }
 }
 
 void CookieManager::RemoveCookiesCompleted(
-    base::RepeatingCallback<void(bool)> callback,
+    base::OnceCallback<void(bool)> callback,
     uint32_t num_deleted) {
-  callback.Run(num_deleted > 0u);
+  std::move(callback).Run(num_deleted > 0u);
 }
 
-void CookieManager::RemoveAllCookies(
-    std::unique_ptr<BoolCookieCallbackHolder> callback_holder) {
-  base::RepeatingCallback<void(bool)> callback =
-      BoolCookieCallbackHolder::ConvertToCallback(std::move(callback_holder));
+void CookieManager::RemoveAllCookies(base::OnceCallback<void(bool)> callback) {
   ExecCookieTask(base::BindOnce(&CookieManager::RemoveAllCookiesHelper,
-                                base::Unretained(this), callback));
+                                base::Unretained(this), std::move(callback)));
 }
 
 void CookieManager::RemoveAllCookiesSync() {
@@ -484,18 +479,18 @@
 }
 
 void CookieManager::RemoveAllCookiesHelper(
-    const base::RepeatingCallback<void(bool)> callback) {
+    base::OnceCallback<void(bool)> callback) {
   if (GetMojoCookieManager()) {
     // An empty filter matches all cookies.
     auto match_all_cookies = network::mojom::CookieDeletionFilter::New();
     GetMojoCookieManager()->DeleteCookies(
         std::move(match_all_cookies),
         base::BindOnce(&CookieManager::RemoveCookiesCompleted,
-                       base::Unretained(this), callback));
+                       base::Unretained(this), std::move(callback)));
   } else {
     GetCookieStore()->DeleteAllAsync(
         base::BindOnce(&CookieManager::RemoveCookiesCompleted,
-                       base::Unretained(this), callback));
+                       base::Unretained(this), std::move(callback)));
   }
 }
 
@@ -618,12 +613,13 @@
     const JavaParamRef<jstring>& url,
     const JavaParamRef<jstring>& value,
     const JavaParamRef<jobject>& java_callback) {
+  DCHECK(java_callback) << "Unexpected null Java callback";
   GURL host(ConvertJavaStringToUTF16(env, url));
   std::string cookie_value(ConvertJavaStringToUTF8(env, value));
-  std::unique_ptr<BoolCookieCallbackHolder> callback(
-      new BoolCookieCallbackHolder(env, java_callback));
-  CookieManager::GetInstance()->SetCookie(host, cookie_value,
-                                          std::move(callback));
+  CookieManager::GetInstance()->SetCookie(
+      host, cookie_value,
+      base::BindOnce(&base::android::RunBooleanCallbackAndroid,
+                     ScopedJavaGlobalRef<jobject>(java_callback)));
 }
 
 static void JNI_AwCookieManager_SetCookieSync(
@@ -651,9 +647,10 @@
     JNIEnv* env,
     const JavaParamRef<jobject>& obj,
     const JavaParamRef<jobject>& java_callback) {
-  std::unique_ptr<BoolCookieCallbackHolder> callback(
-      new BoolCookieCallbackHolder(env, java_callback));
-  CookieManager::GetInstance()->RemoveSessionCookies(std::move(callback));
+  DCHECK(java_callback) << "Unexpected null Java callback";
+  CookieManager::GetInstance()->RemoveSessionCookies(
+      base::BindOnce(&base::android::RunBooleanCallbackAndroid,
+                     ScopedJavaGlobalRef<jobject>(java_callback)));
 }
 
 static void JNI_AwCookieManager_RemoveSessionCookiesSync(
@@ -666,9 +663,10 @@
     JNIEnv* env,
     const JavaParamRef<jobject>& obj,
     const JavaParamRef<jobject>& java_callback) {
-  std::unique_ptr<BoolCookieCallbackHolder> callback(
-      new BoolCookieCallbackHolder(env, java_callback));
-  CookieManager::GetInstance()->RemoveAllCookies(std::move(callback));
+  DCHECK(java_callback) << "Unexpected null Java callback";
+  CookieManager::GetInstance()->RemoveAllCookies(
+      base::BindOnce(&base::android::RunBooleanCallbackAndroid,
+                     ScopedJavaGlobalRef<jobject>(java_callback)));
 }
 
 static void JNI_AwCookieManager_RemoveAllCookiesSync(
diff --git a/android_webview/browser/cookie_manager.h b/android_webview/browser/cookie_manager.h
index 7469994..fc6891a 100644
--- a/android_webview/browser/cookie_manager.h
+++ b/android_webview/browser/cookie_manager.h
@@ -31,8 +31,6 @@
 
 namespace android_webview {
 
-class BoolCookieCallbackHolder;
-
 // CookieManager creates and owns Webview's CookieStore, in addition to handling
 // calls into the CookieStore from Java.
 //
@@ -60,11 +58,11 @@
   bool GetShouldAcceptCookies();
   void SetCookie(const GURL& host,
                  const std::string& cookie_value,
-                 std::unique_ptr<BoolCookieCallbackHolder> callback);
+                 base::OnceCallback<void(bool)> callback);
   void SetCookieSync(const GURL& host, const std::string& cookie_value);
   std::string GetCookie(const GURL& host);
-  void RemoveSessionCookies(std::unique_ptr<BoolCookieCallbackHolder> callback);
-  void RemoveAllCookies(std::unique_ptr<BoolCookieCallbackHolder> callback);
+  void RemoveSessionCookies(base::OnceCallback<void(bool)> callback);
+  void RemoveAllCookies(base::OnceCallback<void(bool)> callback);
   void RemoveAllCookiesSync();
   void RemoveSessionCookiesSync();
   void RemoveExpiredCookies();
@@ -87,9 +85,9 @@
   network::mojom::CookieManager* GetMojoCookieManager();
 
   void ExecCookieTaskSync(
-      base::OnceCallback<void(base::RepeatingCallback<void(bool)>)> task);
+      base::OnceCallback<void(base::OnceCallback<void(bool)>)> task);
   void ExecCookieTaskSync(
-      base::OnceCallback<void(base::RepeatingCallback<void(int)>)> task);
+      base::OnceCallback<void(base::OnceCallback<void(int)>)> task);
   void ExecCookieTaskSync(base::OnceCallback<void(base::OnceClosure)> task);
   void ExecCookieTask(base::OnceClosure task);
   // Runs all queued-up cookie tasks in |tasks_|.
@@ -97,7 +95,7 @@
 
   void SetCookieHelper(const GURL& host,
                        const std::string& value,
-                       base::RepeatingCallback<void(bool)> callback);
+                       base::OnceCallback<void(bool)> callback);
 
   void GotCookies(const std::vector<net::CanonicalCookie>& cookies);
   void GetCookieListAsyncHelper(const GURL& host,
@@ -108,9 +106,9 @@
                               const net::CookieList& value,
                               const net::CookieStatusList& excluded_cookies);
 
-  void RemoveSessionCookiesHelper(base::RepeatingCallback<void(bool)> callback);
-  void RemoveAllCookiesHelper(base::RepeatingCallback<void(bool)> callback);
-  void RemoveCookiesCompleted(base::RepeatingCallback<void(bool)> callback,
+  void RemoveSessionCookiesHelper(base::OnceCallback<void(bool)> callback);
+  void RemoveAllCookiesHelper(base::OnceCallback<void(bool)> callback);
+  void RemoveCookiesCompleted(base::OnceCallback<void(bool)> callback,
                               uint32_t num_deleted);
 
   void FlushCookieStoreAsyncHelper(base::OnceClosure complete);
diff --git a/android_webview/browser/gfx/browser_view_renderer.cc b/android_webview/browser/gfx/browser_view_renderer.cc
index 38d599a..c2be94eb 100644
--- a/android_webview/browser/gfx/browser_view_renderer.cc
+++ b/android_webview/browser/gfx/browser_view_renderer.cc
@@ -314,16 +314,16 @@
 
 bool BrowserViewRenderer::DoUpdateParentDrawData() {
   ParentCompositorDrawConstraints new_constraints;
-  viz::PresentationFeedbackMap new_presentation_feedbacks;
+  viz::FrameTimingDetailsMap new_timing_details;
   CompositorID id;
   uint32_t frame_token = 0u;
   current_compositor_frame_consumer_->TakeParentDrawDataOnUI(
-      &new_constraints, &id, &new_presentation_feedbacks, &frame_token);
+      &new_constraints, &id, &new_timing_details, &frame_token);
 
   content::SynchronousCompositor* compositor = FindCompositor(id);
   if (compositor) {
-    compositor_->DidPresentCompositorFrames(
-        std::move(new_presentation_feedbacks), frame_token);
+    compositor_->DidPresentCompositorFrames(std::move(new_timing_details),
+                                            frame_token);
   }
 
   if (external_draw_constraints_ == new_constraints)
diff --git a/android_webview/browser/gfx/browser_view_renderer_unittest.cc b/android_webview/browser/gfx/browser_view_renderer_unittest.cc
index b6f09c5..0a3f028 100644
--- a/android_webview/browser/gfx/browser_view_renderer_unittest.cc
+++ b/android_webview/browser/gfx/browser_view_renderer_unittest.cc
@@ -219,10 +219,10 @@
   void OnParentDrawDataUpdated() override {
     ParentCompositorDrawConstraints constraints;
     CompositorID id;
-    viz::PresentationFeedbackMap presentation_feedbacks;
+    viz::FrameTimingDetailsMap timing_details;
     uint32_t frame_token = 0u;
     GetCompositorFrameConsumer()->TakeParentDrawDataOnUI(
-        &constraints, &id, &presentation_feedbacks, &frame_token);
+        &constraints, &id, &timing_details, &frame_token);
     switch (on_draw_count_) {
       case 0u:
         // This OnParentDrawDataUpdated is generated by
diff --git a/android_webview/browser/gfx/compositor_frame_consumer.h b/android_webview/browser/gfx/compositor_frame_consumer.h
index f4f7405..3b3cf00 100644
--- a/android_webview/browser/gfx/compositor_frame_consumer.h
+++ b/android_webview/browser/gfx/compositor_frame_consumer.h
@@ -8,7 +8,7 @@
 #include "android_webview/browser/gfx/child_frame.h"
 #include "android_webview/browser/gfx/compositor_id.h"
 #include "android_webview/browser/gfx/parent_compositor_draw_constraints.h"
-#include "components/viz/common/presentation_feedback_map.h"
+#include "components/viz/common/frame_timing_details_map.h"
 #include "ui/gfx/geometry/vector2d.h"
 
 namespace android_webview {
@@ -34,7 +34,7 @@
   virtual void TakeParentDrawDataOnUI(
       ParentCompositorDrawConstraints* constraints,
       CompositorID* compositor_id,
-      viz::PresentationFeedbackMap* presentation_feedbacks,
+      viz::FrameTimingDetailsMap* timing_details,
       uint32_t* frame_token) = 0;
   virtual ChildFrameQueue PassUncommittedFrameOnUI() = 0;
 
diff --git a/android_webview/browser/gfx/hardware_renderer.cc b/android_webview/browser/gfx/hardware_renderer.cc
index 3557c1b..ab08d974 100644
--- a/android_webview/browser/gfx/hardware_renderer.cc
+++ b/android_webview/browser/gfx/hardware_renderer.cc
@@ -57,7 +57,7 @@
   // Reset draw constraints.
   render_thread_manager_->PostParentDrawDataToChildCompositorOnRT(
       ParentCompositorDrawConstraints(), compositor_id_,
-      viz::PresentationFeedbackMap(), 0u);
+      viz::FrameTimingDetailsMap(), 0u);
   for (auto& child_frame : child_frame_queue_) {
     child_frame->WaitOnFutureIfNeeded();
     ReturnChildFrame(std::move(child_frame));
@@ -166,7 +166,7 @@
   // presentation feedback to return as well.
   if (need_to_update_draw_constraints && !submitted_new_frame) {
     render_thread_manager_->PostParentDrawDataToChildCompositorOnRT(
-        draw_constraints, compositor_id_, viz::PresentationFeedbackMap(), 0u);
+        draw_constraints, compositor_id_, viz::FrameTimingDetailsMap(), 0u);
   }
 
   if (!child_id_.is_valid())
@@ -184,11 +184,12 @@
   surfaces_->DrawAndSwap(viewport, clip, transform, surface_size_,
                          viz::SurfaceId(frame_sink_id_, child_id_),
                          device_scale_factor_, params->color_space);
-  viz::PresentationFeedbackMap feedbacks =
-      support_->TakePresentationFeedbacks();
+  viz::FrameTimingDetailsMap timing_details =
+      support_->TakeFrameTimingDetailsMap();
   if (submitted_new_frame) {
     render_thread_manager_->PostParentDrawDataToChildCompositorOnRT(
-        draw_constraints, compositor_id_, std::move(feedbacks), frame_token);
+        draw_constraints, compositor_id_, std::move(timing_details),
+        frame_token);
   }
 }
 
@@ -218,7 +219,7 @@
 
 void HardwareRenderer::OnBeginFrame(
     const viz::BeginFrameArgs& args,
-    const viz::PresentationFeedbackMap& feedbacks) {
+    const viz::FrameTimingDetailsMap& timing_details) {
   NOTREACHED();
 }
 
diff --git a/android_webview/browser/gfx/hardware_renderer.h b/android_webview/browser/gfx/hardware_renderer.h
index 13b3bbf9..dec087b 100644
--- a/android_webview/browser/gfx/hardware_renderer.h
+++ b/android_webview/browser/gfx/hardware_renderer.h
@@ -11,7 +11,7 @@
 #include "android_webview/browser/gfx/compositor_id.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "components/viz/common/presentation_feedback_map.h"
+#include "components/viz/common/frame_timing_details_map.h"
 #include "components/viz/common/surfaces/frame_sink_id.h"
 #include "services/viz/public/interfaces/compositing/compositor_frame_sink.mojom.h"
 #include "ui/gfx/color_space.h"
@@ -65,7 +65,7 @@
   void DidReceiveCompositorFrameAck(
       const std::vector<viz::ReturnedResource>& resources) override;
   void OnBeginFrame(const viz::BeginFrameArgs& args,
-                    const viz::PresentationFeedbackMap& feedbacks) override;
+                    const viz::FrameTimingDetailsMap& timing_details) override;
   void ReclaimResources(
       const std::vector<viz::ReturnedResource>& resources) override;
   void OnBeginFramePausedChanged(bool paused) override;
diff --git a/android_webview/browser/gfx/render_thread_manager.cc b/android_webview/browser/gfx/render_thread_manager.cc
index 982b64fe..89f4eab9 100644
--- a/android_webview/browser/gfx/render_thread_manager.cc
+++ b/android_webview/browser/gfx/render_thread_manager.cc
@@ -109,15 +109,15 @@
 void RenderThreadManager::PostParentDrawDataToChildCompositorOnRT(
     const ParentCompositorDrawConstraints& parent_draw_constraints,
     const CompositorID& compositor_id,
-    viz::PresentationFeedbackMap presentation_feedbacks,
+    viz::FrameTimingDetailsMap timing_details,
     uint32_t frame_token) {
   {
     base::AutoLock lock(lock_);
     parent_draw_constraints_ = parent_draw_constraints;
-    // Presentation feedbacks are a sequence and it's ok to drop something in
-    // the middle of the sequence. This also means its ok to drop the feedbacks
+    // FrameTimingDetails are a sequence and it's ok to drop something in
+    // the middle of the sequence. This also means its ok to drop the details
     // from early returned frames from WaitAndPruneFrameQueue as well.
-    presentation_feedbacks_ = std::move(presentation_feedbacks);
+    timing_details_ = std::move(timing_details);
     presented_frame_token_ = frame_token;
     compositor_id_for_presentation_feedbacks_ = compositor_id;
   }
@@ -132,15 +132,15 @@
 void RenderThreadManager::TakeParentDrawDataOnUI(
     ParentCompositorDrawConstraints* constraints,
     CompositorID* compositor_id,
-    viz::PresentationFeedbackMap* presentation_feedbacks,
+    viz::FrameTimingDetailsMap* timing_details,
     uint32_t* frame_token) {
   DCHECK(ui_loop_->BelongsToCurrentThread());
-  DCHECK(presentation_feedbacks->empty());
+  DCHECK(timing_details->empty());
   CheckUiCallsAllowed();
   base::AutoLock lock(lock_);
   *constraints = parent_draw_constraints_;
   *compositor_id = compositor_id_for_presentation_feedbacks_;
-  presentation_feedbacks_.swap(*presentation_feedbacks);
+  timing_details_.swap(*timing_details);
   *frame_token = presented_frame_token_;
 }
 
diff --git a/android_webview/browser/gfx/render_thread_manager.h b/android_webview/browser/gfx/render_thread_manager.h
index f0d62ea..c1d69d10 100644
--- a/android_webview/browser/gfx/render_thread_manager.h
+++ b/android_webview/browser/gfx/render_thread_manager.h
@@ -37,11 +37,10 @@
   void SetScrollOffsetOnUI(gfx::Vector2d scroll_offset) override;
   std::unique_ptr<ChildFrame> SetFrameOnUI(
       std::unique_ptr<ChildFrame> frame) override;
-  void TakeParentDrawDataOnUI(
-      ParentCompositorDrawConstraints* constraints,
-      CompositorID* compositor_id,
-      viz::PresentationFeedbackMap* presentation_feedbacks,
-      uint32_t* frame_token) override;
+  void TakeParentDrawDataOnUI(ParentCompositorDrawConstraints* constraints,
+                              CompositorID* compositor_id,
+                              viz::FrameTimingDetailsMap* timing_details,
+                              uint32_t* frame_token) override;
   ChildFrameQueue PassUncommittedFrameOnUI() override;
 
   void RemoveFromCompositorFrameProducerOnUI();
@@ -52,7 +51,7 @@
   void PostParentDrawDataToChildCompositorOnRT(
       const ParentCompositorDrawConstraints& parent_draw_constraints,
       const CompositorID& compositor_id,
-      viz::PresentationFeedbackMap presentation_feedbacks,
+      viz::FrameTimingDetailsMap timing_details,
       uint32_t frame_token);
   void InsertReturnedResourcesOnRT(
       const std::vector<viz::ReturnedResource>& resources,
@@ -116,7 +115,7 @@
   bool mark_hardware_release_;
   ParentCompositorDrawConstraints parent_draw_constraints_;
   CompositorID compositor_id_for_presentation_feedbacks_;
-  viz::PresentationFeedbackMap presentation_feedbacks_;
+  viz::FrameTimingDetailsMap timing_details_;
   uint32_t presented_frame_token_ = 0u;
 
   base::WeakPtrFactory<RenderThreadManager> weak_factory_on_ui_thread_;
diff --git a/android_webview/browser/gfx/surfaces_instance.cc b/android_webview/browser/gfx/surfaces_instance.cc
index fa9864d..79b257ef 100644
--- a/android_webview/browser/gfx/surfaces_instance.cc
+++ b/android_webview/browser/gfx/surfaces_instance.cc
@@ -442,7 +442,7 @@
 
 void SurfacesInstance::OnBeginFrame(
     const viz::BeginFrameArgs& args,
-    const viz::PresentationFeedbackMap& feedbacks) {}
+    const viz::FrameTimingDetailsMap& timing_details) {}
 
 void SurfacesInstance::ReclaimResources(
     const std::vector<viz::ReturnedResource>& resources) {
diff --git a/android_webview/browser/gfx/surfaces_instance.h b/android_webview/browser/gfx/surfaces_instance.h
index 39a74c2e..bf616a70 100644
--- a/android_webview/browser/gfx/surfaces_instance.h
+++ b/android_webview/browser/gfx/surfaces_instance.h
@@ -10,7 +10,7 @@
 
 #include "android_webview/browser/gfx/aw_gl_surface.h"
 #include "base/memory/ref_counted.h"
-#include "components/viz/common/presentation_feedback_map.h"
+#include "components/viz/common/frame_timing_details_map.h"
 #include "components/viz/common/surfaces/frame_sink_id.h"
 #include "components/viz/common/surfaces/frame_sink_id_allocator.h"
 #include "components/viz/common/surfaces/local_surface_id_allocation.h"
@@ -80,7 +80,7 @@
   void DidReceiveCompositorFrameAck(
       const std::vector<viz::ReturnedResource>& resources) override;
   void OnBeginFrame(const viz::BeginFrameArgs& args,
-                    const viz::PresentationFeedbackMap& feedbacks) override;
+                    const viz::FrameTimingDetailsMap& timing_details) override;
   void OnBeginFramePausedChanged(bool paused) override;
   void ReclaimResources(
       const std::vector<viz::ReturnedResource>& resources) override;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java
index de05149d..841c389 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java
@@ -24,6 +24,7 @@
 import org.chromium.android_webview.test.util.CookieUtils.TestCallback;
 import org.chromium.android_webview.test.util.JSUtils;
 import org.chromium.base.Callback;
+import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Feature;
 import org.chromium.content_public.browser.WebContents;
@@ -69,6 +70,8 @@
     private TestAwContentsClient mContentsClient;
     private AwContents mAwContents;
 
+    private final static String SECURE_COOKIE_HISTOGRAM_NAME = "Android.WebView.SecureCookieAction";
+
     @Before
     public void setUp() throws Exception {
         mCookieManager = new AwCookieManager();
@@ -305,10 +308,17 @@
     @MediumTest
     @Feature({"AndroidWebView", "Privacy"})
     public void testSetCookie() throws Throwable {
+        Assert.assertEquals(
+                0, RecordHistogram.getHistogramTotalCountForTesting(SECURE_COOKIE_HISTOGRAM_NAME));
         String url = "http://www.example.com";
         String cookie = "name=test";
         mCookieManager.setCookie(url, cookie);
         assertCookieEquals(cookie, url);
+        Assert.assertEquals(
+                1, RecordHistogram.getHistogramTotalCountForTesting(SECURE_COOKIE_HISTOGRAM_NAME));
+        Assert.assertEquals(1,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        SECURE_COOKIE_HISTOGRAM_NAME, 3 /* kNotASecureCookie */));
     }
 
     @Test
@@ -355,11 +365,35 @@
     @MediumTest
     @Feature({"AndroidWebView", "Privacy"})
     public void testSetSecureCookieForHttpUrl() throws Throwable {
+        Assert.assertEquals(
+                0, RecordHistogram.getHistogramTotalCountForTesting(SECURE_COOKIE_HISTOGRAM_NAME));
         String url = "http://www.example.com";
         String secureUrl = "https://www.example.com";
         String cookie = "name=test";
         mCookieManager.setCookie(url, cookie + ";secure");
         assertCookieEquals(cookie, secureUrl);
+        Assert.assertEquals(
+                1, RecordHistogram.getHistogramTotalCountForTesting(SECURE_COOKIE_HISTOGRAM_NAME));
+        Assert.assertEquals(1,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        SECURE_COOKIE_HISTOGRAM_NAME, 4 /* kFixedUp */));
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"AndroidWebView", "Privacy"})
+    public void testSetSecureCookieForHttpsUrl() throws Throwable {
+        Assert.assertEquals(
+                0, RecordHistogram.getHistogramTotalCountForTesting(SECURE_COOKIE_HISTOGRAM_NAME));
+        String secureUrl = "https://www.example.com";
+        String cookie = "name=test";
+        mCookieManager.setCookie(secureUrl, cookie + ";secure");
+        assertCookieEquals(cookie, secureUrl);
+        Assert.assertEquals(
+                1, RecordHistogram.getHistogramTotalCountForTesting(SECURE_COOKIE_HISTOGRAM_NAME));
+        Assert.assertEquals(1,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        SECURE_COOKIE_HISTOGRAM_NAME, 1 /* kAlreadySecureScheme */));
     }
 
     @Test
@@ -401,6 +435,11 @@
         callback.getOnResultHelper().waitForCallback(callCount);
         Assert.assertFalse("Cookie should not be set for bad URLs", callback.getValue());
         assertNoCookies(brokenUrl);
+        Assert.assertEquals(
+                1, RecordHistogram.getHistogramTotalCountForTesting(SECURE_COOKIE_HISTOGRAM_NAME));
+        Assert.assertEquals(1,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        SECURE_COOKIE_HISTOGRAM_NAME, 0 /* kInvalidUrl */));
     }
 
     @Test
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 242a01c..636f84ac2 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -2081,7 +2081,6 @@
     "lock_screen_action/test_lock_screen_action_background_controller.cc",
     "lock_screen_action/test_lock_screen_action_background_controller.h",
     "login/login_screen_test_api.cc",
-    "login/login_screen_test_api.h",
     "metrics/task_switch_time_tracker_test_api.cc",
     "metrics/task_switch_time_tracker_test_api.h",
     "metrics/user_metrics_recorder_test_api.cc",
diff --git a/ash/accessibility/touch_exploration_manager.h b/ash/accessibility/touch_exploration_manager.h
index 9d49b2a0..4f11f4e 100644
--- a/ash/accessibility/touch_exploration_manager.h
+++ b/ash/accessibility/touch_exploration_manager.h
@@ -15,6 +15,7 @@
 #include "ash/shell_observer.h"
 #include "base/macros.h"
 #include "base/scoped_observer.h"
+#include "ui/aura/window_observer.h"
 #include "ui/display/display_observer.h"
 #include "ui/wm/public/activation_change_observer.h"
 
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc
index 44b1b19..90072d1 100644
--- a/ash/app_list/app_list_controller_impl.cc
+++ b/ash/app_list/app_list_controller_impl.cc
@@ -25,6 +25,7 @@
 #include "ash/assistant/util/deep_link_util.h"
 #include "ash/home_screen/home_launcher_gesture_handler.h"
 #include "ash/home_screen/home_screen_controller.h"
+#include "ash/keyboard/ui/keyboard_controller.h"
 #include "ash/public/cpp/app_list/app_list_client.h"
 #include "ash/public/cpp/app_list/app_list_features.h"
 #include "ash/public/cpp/app_list/app_list_metrics.h"
diff --git a/ash/app_list/app_list_metrics.cc b/ash/app_list/app_list_metrics.cc
index a4c008aa..03e897c 100644
--- a/ash/app_list/app_list_metrics.cc
+++ b/ash/app_list/app_list_metrics.cc
@@ -147,6 +147,9 @@
     case ui::ET_SCROLL_FLING_START:
       source = kFlingAppGrid;
       break;
+    case ui::ET_MOUSE_RELEASED:
+      source = kMouseDrag;
+      break;
     default:
       NOTREACHED();
       return;
diff --git a/ash/app_list/app_list_metrics.h b/ash/app_list/app_list_metrics.h
index 842684c..caa3941 100644
--- a/ash/app_list/app_list_metrics.h
+++ b/ash/app_list/app_list_metrics.h
@@ -199,7 +199,8 @@
   kMousePadScroll = 5,
   kDragAppToBorder = 6,
   kMoveAppWithKeyboard = 7,
-  kMaxAppListPageSwitcherSource = 8,
+  kMouseDrag = 8,
+  kMaxAppListPageSwitcherSource = 9,
 };
 
 // The different ways to move an app in app list's apps grid. These values are
diff --git a/ash/app_list/app_list_presenter_delegate_unittest.cc b/ash/app_list/app_list_presenter_delegate_unittest.cc
index 60182166..3feff6d 100644
--- a/ash/app_list/app_list_presenter_delegate_unittest.cc
+++ b/ash/app_list/app_list_presenter_delegate_unittest.cc
@@ -17,6 +17,7 @@
 #include "ash/app_list/views/apps_container_view.h"
 #include "ash/app_list/views/apps_grid_view.h"
 #include "ash/app_list/views/contents_view.h"
+#include "ash/app_list/views/expand_arrow_view.h"
 #include "ash/app_list/views/search_box_view.h"
 #include "ash/app_list/views/test/apps_grid_view_test_api.h"
 #include "ash/keyboard/ui/keyboard_controller.h"
@@ -52,6 +53,7 @@
 #include "base/command_line.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/test/test_windows.h"
@@ -179,9 +181,6 @@
   ~PopulatedAppListTest() override = default;
 
   void SetUp() override {
-    app_list::AppListView::SetShortAnimationForTesting(true);
-    base::CommandLine::ForCurrentProcess()->AppendSwitch(
-        keyboard::switches::kEnableVirtualKeyboard);
     AshTestBase::SetUp();
 
     // Make the display big enough to hold the app list.
@@ -205,6 +204,19 @@
     app_list_view_->Show(false /*is_side_shelf*/, false /*is_tablet_mode*/);
   }
 
+  void ShowAppListInAppsFullScreen() {
+    // Press the ExpandArrowView and check that the AppListView is in
+    // fullscreen.
+    gfx::Point click_point = app_list_view_->app_list_main_view()
+                                 ->contents_view()
+                                 ->expand_arrow_view()
+                                 ->GetBoundsInScreen()
+                                 .CenterPoint();
+    GetEventGenerator()->GestureTapAt(click_point);
+    EXPECT_EQ(AppListViewState::kFullscreenAllApps,
+              app_list_view_->app_list_state());
+  }
+
   void InitializeAppsGrid() {
     if (!app_list_view_)
       CreateAndOpenAppList();
@@ -229,6 +241,21 @@
       nullptr;  // Owned by |app_list_view_|.
 };
 
+// Subclass of PopuplatedAppListTest which enables the animation and the virtual
+// keyboard.
+class PopulatedAppListWithVKEnabledTest : public PopulatedAppListTest {
+ public:
+  PopulatedAppListWithVKEnabledTest() = default;
+  ~PopulatedAppListWithVKEnabledTest() override = default;
+
+  void SetUp() override {
+    app_list::AppListView::SetShortAnimationForTesting(true);
+    base::CommandLine::ForCurrentProcess()->AppendSwitch(
+        keyboard::switches::kEnableVirtualKeyboard);
+    PopulatedAppListTest::SetUp();
+  }
+};
+
 // Instantiate the Boolean which is used to toggle mouse and touch events in
 // the parameterized tests.
 INSTANTIATE_TEST_SUITE_P(, AppListPresenterDelegateTest, testing::Bool());
@@ -280,7 +307,65 @@
   GetAppListTestHelper()->CheckState(AppListViewState::kFullscreenAllApps);
 }
 
-TEST_F(PopulatedAppListTest, TappingAppsGridClosesVirtualKeyboard) {
+// Verifies that the downward mouse drag on AppsGridView's first page should
+// be handled by AppList.
+TEST_F(PopulatedAppListTest, MouseDragAppsGridViewHandledByAppList) {
+  InitializeAppsGrid();
+  app_list_test_model_->PopulateApps(2);
+  ShowAppListInAppsFullScreen();
+
+  // Calculate the drag start/end points.
+  gfx::Point drag_start_point = apps_grid_view_->GetBoundsInScreen().origin();
+  gfx::Point target_point = GetPrimaryDisplay().bounds().bottom_left();
+  target_point.set_x(drag_start_point.x());
+
+  // Drag AppsGridView downward by mouse. Check the following things:
+  // (1) Mouse events are processed by AppsGridView, including mouse press,
+  // mouse drag and mouse release.
+  // (2) AppList is closed after mouse drag.
+  ui::test::EventGenerator* event_generator = GetEventGenerator();
+  event_generator->MoveMouseTo(drag_start_point);
+  event_generator->DragMouseTo(target_point);
+  event_generator->ReleaseLeftButton();
+  EXPECT_EQ(AppListViewState::kClosed, app_list_view_->app_list_state());
+}
+
+// Verifies that the upward mouse drag on AppsGridView's first page should
+// be handled by PaginationController.
+TEST_F(PopulatedAppListTest,
+       MouseDragAppsGridViewHandledByPaginationController) {
+  InitializeAppsGrid();
+  app_list_test_model_->PopulateApps(apps_grid_test_api_->TilesPerPage(0) + 1);
+  EXPECT_EQ(2, apps_grid_view_->pagination_model()->total_pages());
+  ShowAppListInAppsFullScreen();
+
+  // Calculate the drag start/end points. |drag_start_point| is between the
+  // first and the second AppListItem. Because in this test case, we want
+  // AppsGridView to receive mouse events instead of AppListItemView.
+  gfx::Point right_side =
+      apps_grid_view_->GetItemViewAt(0)->GetBoundsInScreen().right_center();
+  gfx::Point left_side =
+      apps_grid_view_->GetItemViewAt(1)->GetBoundsInScreen().left_center();
+  ASSERT_EQ(left_side.y(), right_side.y());
+  gfx::Point drag_start_point((right_side.x() + left_side.x()) / 2,
+                              right_side.y());
+  gfx::Point target_point = GetPrimaryDisplay().bounds().top_right();
+  target_point.set_x(drag_start_point.x());
+
+  // Drag AppsGridView downward by mouse. Checks that PaginationController
+  // records the mouse drag.
+  base::HistogramTester histogram_tester;
+  ui::test::EventGenerator* event_generator = GetEventGenerator();
+  event_generator->MoveMouseTo(drag_start_point);
+  event_generator->DragMouseTo(target_point);
+  event_generator->ReleaseLeftButton();
+  histogram_tester.ExpectUniqueSample(
+      app_list::kAppListPageSwitcherSourceHistogramInClamshell,
+      app_list::AppListPageSwitcherSource::kMouseDrag, 1);
+}
+
+TEST_F(PopulatedAppListWithVKEnabledTest,
+       TappingAppsGridClosesVirtualKeyboard) {
   InitializeAppsGrid();
   app_list_test_model_->PopulateApps(2);
   gfx::Point between_apps = GetItemRectOnCurrentPageAt(0, 0).right_center();
diff --git a/ash/app_list/views/app_list_view.cc b/ash/app_list/views/app_list_view.cc
index cc3a70d..17f713f 100644
--- a/ash/app_list/views/app_list_view.cc
+++ b/ash/app_list/views/app_list_view.cc
@@ -1258,7 +1258,8 @@
       if (!is_in_drag_ && event->IsOnlyLeftMouseButton()) {
         // Calculate the mouse drag offset to determine whether AppListView is
         // in drag.
-        gfx::Vector2d drag_distance = event->location() - initial_drag_point_;
+        gfx::Vector2d drag_distance =
+            event->location() - initial_mouse_drag_point_;
         if (abs(drag_distance.y()) < ash::kMouseDragThreshold)
           return;
 
diff --git a/ash/app_list/views/apps_container_view.cc b/ash/app_list/views/apps_container_view.cc
index ba4f568..15f9396 100644
--- a/ash/app_list/views/apps_container_view.cc
+++ b/ash/app_list/views/apps_container_view.cc
@@ -334,8 +334,9 @@
 void AppsContainerView::OnGestureEvent(ui::GestureEvent* event) {
   // Ignore tap/long-press, allow those to pass to the ancestor view.
   if (event->type() == ui::ET_GESTURE_TAP ||
-      event->type() == ui::ET_GESTURE_LONG_PRESS)
+      event->type() == ui::ET_GESTURE_LONG_PRESS) {
     return;
+  }
 
   // Will forward events to |apps_grid_view_| if they occur in the same y-region
   if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN &&
diff --git a/ash/app_list/views/apps_grid_view.cc b/ash/app_list/views/apps_grid_view.cc
index fc1264e..b2f7b8e 100644
--- a/ash/app_list/views/apps_grid_view.cc
+++ b/ash/app_list/views/apps_grid_view.cc
@@ -1046,19 +1046,9 @@
     return;
   }
 
-  // If |pagination_model_| is empty, don't handle scroll events.
-  if (pagination_model_.total_pages() <= 0)
+  if (!ShouldHandleDragEvent(*event))
     return;
 
-  // If the event is a scroll down in clamshell mode on the first page, don't
-  // let |pagination_controller_| handle it. Unless it occurs in a folder.
-  if (!folder_delegate_ && event->type() == ui::ET_GESTURE_SCROLL_BEGIN &&
-      !contents_view_->app_list_view()->is_tablet_mode() &&
-      pagination_model_.selected_page() == 0 &&
-      event->details().scroll_y_hint() > 0) {
-    return;
-  }
-
   // Scroll begin events should not be passed to ancestor views from apps grid
   // in our current design. This prevents both ignoring horizontal scrolls in
   // app list, and closing open folders.
@@ -1068,9 +1058,91 @@
   }
 }
 
-bool AppsGridView::OnMousePressed(const ui::MouseEvent& event) {
-  return !contents_view_->app_list_view()->is_tablet_mode() &&
-         event.IsLeftMouseButton() && EventIsBetweenOccupiedTiles(&event);
+void AppsGridView::OnMouseEvent(ui::MouseEvent* event) {
+  if (contents_view_->app_list_view()->is_tablet_mode() ||
+      !event->IsLeftMouseButton()) {
+    return;
+  }
+
+  gfx::Point point_in_screen = event->location();
+  ConvertPointToScreen(this, &point_in_screen);
+
+  switch (event->type()) {
+    case ui::ET_MOUSE_PRESSED:
+      if (!EventIsBetweenOccupiedTiles(event))
+        break;
+      event->SetHandled();
+      mouse_drag_start_point_ = point_in_screen;
+      last_mouse_drag_point_ = point_in_screen;
+      break;
+    case ui::ET_MOUSE_DRAGGED:
+      if (!ShouldHandleDragEvent(*event)) {
+        // We need to send mouse drag/release events to AppListView explicitly
+        // because AppsGridView handles the mouse press event and gets captured.
+        // Then AppListView cannot receive mouse drag/release events implcitly.
+
+        // Send the fabricated mouse press event to AppListView if AppsGridView
+        // is not in mouse drag yet.
+        gfx::Point drag_location_in_app_list;
+        if (!is_in_mouse_drag_) {
+          ui::MouseEvent press_event(
+              *event, static_cast<views::View*>(this),
+              static_cast<views::View*>(contents_view_->app_list_view()),
+              ui::ET_MOUSE_PRESSED, event->flags());
+          contents_view_->app_list_view()->OnMouseEvent(&press_event);
+
+          is_in_mouse_drag_ = true;
+        }
+
+        drag_location_in_app_list = event->location();
+        ConvertPointToTarget(this, contents_view_->app_list_view(),
+                             &drag_location_in_app_list);
+        event->set_location(drag_location_in_app_list);
+        contents_view_->app_list_view()->OnMouseEvent(event);
+        break;
+      }
+      event->SetHandled();
+      if (!is_in_mouse_drag_) {
+        if (abs(point_in_screen.y() - mouse_drag_start_point_.y()) <
+            ash::kMouseDragThreshold) {
+          break;
+        }
+        pagination_controller_->StartMouseDrag(point_in_screen -
+                                               mouse_drag_start_point_);
+        is_in_mouse_drag_ = true;
+      }
+
+      if (!is_in_mouse_drag_)
+        break;
+
+      pagination_controller_->UpdateMouseDrag(
+          point_in_screen - last_mouse_drag_point_, GetContentsBounds());
+      last_mouse_drag_point_ = point_in_screen;
+      break;
+    case ui::ET_MOUSE_RELEASED: {
+      // Calculate |should_handle| before resetting |mouse_drag_start_point_|
+      // because ShouldHandleDragEvent depends on its value.
+      const bool should_handle = ShouldHandleDragEvent(*event);
+
+      is_in_mouse_drag_ = false;
+      mouse_drag_start_point_ = gfx::Point();
+      last_mouse_drag_point_ = gfx::Point();
+
+      if (!should_handle) {
+        gfx::Point drag_location_in_app_list = event->location();
+        ConvertPointToTarget(this, contents_view_->app_list_view(),
+                             &drag_location_in_app_list);
+        event->set_location(drag_location_in_app_list);
+        contents_view_->app_list_view()->OnMouseEvent(event);
+        break;
+      }
+      event->SetHandled();
+      pagination_controller_->EndMouseDrag(*event);
+      break;
+    }
+    default:
+      return;
+  }
 }
 
 bool AppsGridView::EventIsBetweenOccupiedTiles(const ui::LocatedEvent* event) {
@@ -3255,4 +3327,30 @@
     current_ghost_view_->FadeOut();
 }
 
+bool AppsGridView::ShouldHandleDragEvent(const ui::LocatedEvent& event) {
+  // If |pagination_model_| is empty, don't handle scroll events.
+  if (pagination_model_.total_pages() <= 0)
+    return false;
+
+  DCHECK(event.IsGestureEvent() || event.IsMouseEvent());
+
+  // If the event is a scroll down in clamshell mode on the first page, don't
+  // let |pagination_controller_| handle it. Unless it occurs in a folder.
+  auto calculate_offset = [this](const ui::LocatedEvent& event) -> int {
+    if (event.IsGestureEvent())
+      return event.AsGestureEvent()->details().scroll_y_hint();
+    gfx::Point screen_location = event.location();
+    ConvertPointToScreen(this, &screen_location);
+    return screen_location.y() - mouse_drag_start_point_.y();
+  };
+  if (!folder_delegate_ &&
+      (event.IsMouseEvent() || event.type() == ui::ET_GESTURE_SCROLL_BEGIN) &&
+      !contents_view_->app_list_view()->is_tablet_mode() &&
+      pagination_model_.selected_page() == 0 && calculate_offset(event) > 0) {
+    return false;
+  }
+
+  return true;
+}
+
 }  // namespace app_list
diff --git a/ash/app_list/views/apps_grid_view.h b/ash/app_list/views/apps_grid_view.h
index 9cfe494..48879a6 100644
--- a/ash/app_list/views/apps_grid_view.h
+++ b/ash/app_list/views/apps_grid_view.h
@@ -200,7 +200,7 @@
 
   // Overridden from ui::EventHandler:
   void OnGestureEvent(ui::GestureEvent* event) override;
-  bool OnMousePressed(const ui::MouseEvent& event) override;
+  void OnMouseEvent(ui::MouseEvent* event) override;
 
   // Returns true if a touch or click lies between two occupied tiles.
   bool EventIsBetweenOccupiedTiles(const ui::LocatedEvent* event);
@@ -665,6 +665,10 @@
 
   void BeginHideCurrentGhostImageView();
 
+  // Indicates whether the drag event (from the gesture or mouse) should be
+  // handled by AppsGridView.
+  bool ShouldHandleDragEvent(const ui::LocatedEvent& event);
+
   AppListModel* model_ = nullptr;         // Owned by AppListView.
   AppListItemList* item_list_ = nullptr;  // Not owned.
 
@@ -804,6 +808,18 @@
   // Records the presentation time for apps grid dragging.
   std::unique_ptr<ash::PresentationTimeRecorder> presentation_time_recorder_;
 
+  // Indicates whether the AppsGridView is in mouse drag.
+  bool is_in_mouse_drag_ = false;
+
+  // The initial mouse drag location in screen coordinate. Updates when drag
+  // on AppsGridView starts.
+  gfx::Point mouse_drag_start_point_;
+
+  // The last mouse drag location in screen coordinate. Different from
+  // |last_drag_point_|, |last_mouse_drag_point_| is the location of the most
+  // recent drag on AppsGridView instead of the app icon.
+  gfx::Point last_mouse_drag_point_;
+
   DISALLOW_COPY_AND_ASSIGN(AppsGridView);
 };
 
diff --git a/ash/app_list/views/search_result_answer_card_view.cc b/ash/app_list/views/search_result_answer_card_view.cc
index e46fdfe9..13d0147 100644
--- a/ash/app_list/views/search_result_answer_card_view.cc
+++ b/ash/app_list/views/search_result_answer_card_view.cc
@@ -262,6 +262,10 @@
     OnVisibilityChanged(true /* is_visible */);
     views::View* content_view = contents_->GetView()->view();
     if (children().empty()) {
+      // Focusability is handled on SearchResultAnswerCardView so we explicitly
+      // disable it on the embedded view (for which it is enabled by default).
+      content_view->SetFocusBehavior(FocusBehavior::NEVER);
+
       AddChildView(content_view);
       ExcludeCardFromEventHandling(contents_->GetView()->native_view());
 
diff --git a/ash/assistant/assistant_controller.cc b/ash/assistant/assistant_controller.cc
index 8d5263558..cd5c4814 100644
--- a/ash/assistant/assistant_controller.cc
+++ b/ash/assistant/assistant_controller.cc
@@ -229,7 +229,9 @@
       Shell::Get()->accessibility_controller()->spoken_feedback_enabled());
 }
 
-void AssistantController::OpenUrl(const GURL& url, bool from_server) {
+void AssistantController::OpenUrl(const GURL& url,
+                                  bool in_background,
+                                  bool from_server) {
   auto* android_helper = AndroidIntentHelper::GetInstance();
   if (url.SchemeIs(kAndroidIntentScheme) && android_helper) {
     android_helper->LaunchAndroidIntent(url.spec());
@@ -243,10 +245,14 @@
 
   // Give observers an opportunity to perform any necessary handling before we
   // open the specified |url| in a new browser tab.
-  NotifyOpeningUrl(url, from_server);
+  NotifyOpeningUrl(url, in_background, from_server);
 
   // The new tab should be opened with a user activation since the user
-  // interacted with the Assistant to open the url.
+  // interacted with the Assistant to open the url. |in_background| describes
+  // the relationship between |url| and Assistant UI, not the browser. As such,
+  // the browser will always be instructed to open |url| in a new browser tab
+  // and Assistant UI state will be updated downstream to respect
+  // |in_background|.
   NewWindowDelegate::GetInstance()->NewTabWithUrl(
       url, /*from_user_interaction=*/true);
   NotifyUrlOpened(url, from_server);
@@ -304,9 +310,11 @@
   view_delegate_.NotifyDeepLinkReceived(type, params);
 }
 
-void AssistantController::NotifyOpeningUrl(const GURL& url, bool from_server) {
+void AssistantController::NotifyOpeningUrl(const GURL& url,
+                                           bool in_background,
+                                           bool from_server) {
   for (AssistantControllerObserver& observer : observers_)
-    observer.OnOpeningUrl(url, from_server);
+    observer.OnOpeningUrl(url, in_background, from_server);
 }
 
 void AssistantController::NotifyUrlOpened(const GURL& url, bool from_server) {
diff --git a/ash/assistant/assistant_controller.h b/ash/assistant/assistant_controller.h
index a996c8b..7a87655 100644
--- a/ash/assistant/assistant_controller.h
+++ b/ash/assistant/assistant_controller.h
@@ -103,7 +103,9 @@
 
   // Opens the specified |url| in a new browser tab. Special handling is applied
   // to deep links which may cause deviation from this behavior.
-  void OpenUrl(const GURL& url, bool from_server = false);
+  void OpenUrl(const GURL& url,
+               bool in_background = false,
+               bool from_server = false);
 
   // Acquires a NavigableContentsFactory from the Content Service to allow
   // Assistant to display embedded web contents.
@@ -146,7 +148,7 @@
   void NotifyConstructed();
   void NotifyDestroying();
   void NotifyDeepLinkReceived(const GURL& deep_link);
-  void NotifyOpeningUrl(const GURL& url, bool from_server);
+  void NotifyOpeningUrl(const GURL& url, bool in_background, bool from_server);
   void NotifyUrlOpened(const GURL& url, bool from_server);
 
   // mojom::VoiceInteractionObserver:
diff --git a/ash/assistant/assistant_controller_observer.h b/ash/assistant/assistant_controller_observer.h
index 93645c7..8c2ec3c 100644
--- a/ash/assistant/assistant_controller_observer.h
+++ b/ash/assistant/assistant_controller_observer.h
@@ -43,7 +43,9 @@
   // Invoked when the specified |url| is about to be opened by Assistant in a
   // new tab. If |from_server| is true, this event was triggered by a server
   // response. Note that this event immediately precedes |OnUrlOpened|.
-  virtual void OnOpeningUrl(const GURL& url, bool from_server) {}
+  virtual void OnOpeningUrl(const GURL& url,
+                            bool in_background,
+                            bool from_server) {}
 
   // Invoked when the specified |url| is opened by Assistant in a new tab. If
   // |from_server| is true, this event was triggered by a server response.
diff --git a/ash/assistant/assistant_interaction_controller.cc b/ash/assistant/assistant_interaction_controller.cc
index 09e80fe..b42d61d3 100644
--- a/ash/assistant/assistant_interaction_controller.cc
+++ b/ash/assistant/assistant_interaction_controller.cc
@@ -487,7 +487,8 @@
         FROM_HERE,
         base::BindOnce(&AssistantController::OpenUrl,
                        assistant_controller_->GetWeakPtr(),
-                       suggestion->action_url, /*from_server=*/false));
+                       suggestion->action_url, /*in_background=*/false,
+                       /*from_server=*/false));
     return;
   }
 
@@ -621,14 +622,14 @@
   OnProcessPendingResponse();
 }
 
-void AssistantInteractionController::OnOpenUrlResponse(const GURL& url) {
-  if (model_.interaction_state() != InteractionState::kActive) {
+void AssistantInteractionController::OnOpenUrlResponse(const GURL& url,
+                                                       bool in_background) {
+  if (model_.interaction_state() != InteractionState::kActive)
     return;
-  }
   // We need to indicate that the navigation attempt is occurring as a result of
   // a server response so that we can differentiate from navigation attempts
   // initiated by direct user interaction.
-  assistant_controller_->OpenUrl(url, /*from_server=*/true);
+  assistant_controller_->OpenUrl(url, in_background, /*from_server=*/true);
 }
 
 void AssistantInteractionController::OnDialogPlateButtonPressed(
diff --git a/ash/assistant/assistant_interaction_controller.h b/ash/assistant/assistant_interaction_controller.h
index c9e4c25..ce08808ec 100644
--- a/ash/assistant/assistant_interaction_controller.h
+++ b/ash/assistant/assistant_interaction_controller.h
@@ -91,7 +91,7 @@
   void OnSuggestionsResponse(
       std::vector<AssistantSuggestionPtr> response) override;
   void OnTextResponse(const std::string& response) override;
-  void OnOpenUrlResponse(const GURL& url) override;
+  void OnOpenUrlResponse(const GURL& url, bool in_background) override;
   void OnSpeechRecognitionStarted() override;
   void OnSpeechRecognitionIntermediateResult(
       const std::string& high_confidence_text,
@@ -124,7 +124,6 @@
   void StartVoiceInteraction();
   void StopActiveInteraction(bool cancel_conversation);
 
-  void OpenUrl(const GURL& url);
 
   AssistantController* const assistant_controller_;  // Owned by Shell.
 
diff --git a/ash/assistant/assistant_ui_controller.cc b/ash/assistant/assistant_ui_controller.cc
index 8652f5e5..aa06df86 100644
--- a/ash/assistant/assistant_ui_controller.cc
+++ b/ash/assistant/assistant_ui_controller.cc
@@ -12,6 +12,7 @@
 #include "ash/assistant/util/assistant_util.h"
 #include "ash/assistant/util/deep_link_util.h"
 #include "ash/assistant/util/histogram_util.h"
+#include "ash/keyboard/ui/keyboard_controller.h"
 #include "ash/multi_user/multi_user_window_manager_impl.h"
 #include "ash/public/cpp/app_list/app_list_features.h"
 #include "ash/public/cpp/toast_data.h"
@@ -253,17 +254,26 @@
   UpdateUiMode(AssistantUiMode::kWebUi);
 }
 
-void AssistantUiController::OnOpeningUrl(const GURL& url, bool from_server) {
+void AssistantUiController::OnOpeningUrl(const GURL& url,
+                                         bool in_background,
+                                         bool from_server) {
   if (model_.visibility() != AssistantVisibility::kVisible)
     return;
 
+  // If the specified |url| should be opening |in_background| with respect to
+  // Assistant UI, we transition into mini state so as not to obstruct the
+  // browser.
+  // TODO(b/134931713): Desired behavior is not yet defined when Assistant is
+  // embedded in the launcher.
   // We close the Assistant UI entirely when opening a new browser tab if the
   // navigation was initiated by a server response. Otherwise the navigation
   // was user initiated so we only hide the UI to retain session state. That way
   // the user can choose to resume their session if they are so inclined.
   // However, we close the UI if the feature |IsEmbeddedAssistantUIEnabled| is
   // enabled, where we only maintain |kVisible| and |kClosed| two states.
-  if (from_server)
+  if (in_background && !app_list_features::IsEmbeddedAssistantUIEnabled())
+    UpdateUiMode(AssistantUiMode::kMiniUi);
+  else if (from_server)
     CloseUi(AssistantExitPoint::kNewBrowserTabFromServer);
   else if (app_list_features::IsEmbeddedAssistantUIEnabled())
     CloseUi(AssistantExitPoint::kNewBrowserTabFromUser);
diff --git a/ash/assistant/assistant_ui_controller.h b/ash/assistant/assistant_ui_controller.h
index eddf2be..1a075bd7 100644
--- a/ash/assistant/assistant_ui_controller.h
+++ b/ash/assistant/assistant_ui_controller.h
@@ -101,7 +101,9 @@
   void OnDeepLinkReceived(
       assistant::util::DeepLinkType type,
       const std::map<std::string, std::string>& params) override;
-  void OnOpeningUrl(const GURL& url, bool from_server) override;
+  void OnOpeningUrl(const GURL& url,
+                    bool in_background,
+                    bool from_server) override;
 
   // AssistantUiModelObserver:
   void OnUiVisibilityChanged(
diff --git a/ash/keyboard/ash_keyboard_controller.h b/ash/keyboard/ash_keyboard_controller.h
index 24a4bb1..8d42172 100644
--- a/ash/keyboard/ash_keyboard_controller.h
+++ b/ash/keyboard/ash_keyboard_controller.h
@@ -8,10 +8,12 @@
 #include <memory>
 
 #include "ash/ash_export.h"
+#include "ash/keyboard/ui/keyboard_controller.h"
 #include "ash/keyboard/ui/keyboard_controller_observer.h"
 #include "ash/public/cpp/keyboard/keyboard_controller.h"
 #include "ash/session/session_observer.h"
 #include "base/macros.h"
+#include "base/optional.h"
 
 namespace gfx {
 class Rect;
diff --git a/ash/keyboard/ui/keyboard_controller_observer.h b/ash/keyboard/ui/keyboard_controller_observer.h
index 7b6ae7c..58c7e02 100644
--- a/ash/keyboard/ui/keyboard_controller_observer.h
+++ b/ash/keyboard/ui/keyboard_controller_observer.h
@@ -7,12 +7,9 @@
 
 #include <set>
 
-#include "ash/keyboard/ui/keyboard_controller.h"
 #include "ash/keyboard/ui/keyboard_export.h"
-
-namespace gfx {
-class Rect;
-}
+#include "ash/public/cpp/keyboard/keyboard_types.h"
+#include "ui/gfx/geometry/rect.h"
 
 namespace keyboard {
 
diff --git a/ash/login/login_screen_controller.cc b/ash/login/login_screen_controller.cc
index 60c2ecf..9b795ec8 100644
--- a/ash/login/login_screen_controller.cc
+++ b/ash/login/login_screen_controller.cc
@@ -350,9 +350,10 @@
 
 void LoginScreenController::ShowParentAccessWidget(
     const AccountId& child_account_id,
-    base::RepeatingCallback<void(bool success)> callback) {
-  parent_access_widget_ =
-      std::make_unique<ash::ParentAccessWidget>(child_account_id, callback);
+    base::RepeatingCallback<void(bool success)> callback,
+    ParentAccessRequestReason reason) {
+  parent_access_widget_ = std::make_unique<ash::ParentAccessWidget>(
+      child_account_id, callback, reason);
 }
 
 void LoginScreenController::SetAllowLoginAsGuest(bool allow_guest) {
diff --git a/ash/login/login_screen_controller.h b/ash/login/login_screen_controller.h
index 3f90718..0334ae15 100644
--- a/ash/login/login_screen_controller.h
+++ b/ash/login/login_screen_controller.h
@@ -115,7 +115,8 @@
   void ShowParentAccessButton(bool show) override;
   void ShowParentAccessWidget(
       const AccountId& child_account_id,
-      base::RepeatingCallback<void(bool success)> callback) override;
+      base::RepeatingCallback<void(bool success)> callback,
+      ParentAccessRequestReason reason) override;
   void SetAllowLoginAsGuest(bool allow_guest) override;
 
   // KioskAppMenu:
diff --git a/ash/login/login_screen_test_api.cc b/ash/login/login_screen_test_api.cc
index 1372249..a892df6 100644
--- a/ash/login/login_screen_test_api.cc
+++ b/ash/login/login_screen_test_api.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/login/login_screen_test_api.h"
+#include "ash/public/cpp/login_screen_test_api.h"
 
 #include <memory>
 #include <utility>
@@ -17,7 +17,7 @@
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "base/run_loop.h"
 #include "ui/views/controls/textfield/textfield.h"
 
 namespace ash {
@@ -47,8 +47,6 @@
 
 class ShelfTestUiUpdateDelegate : public LoginShelfView::TestUiUpdateDelegate {
  public:
-  using Callback = mojom::LoginScreenTestApi::WaitForUiUpdateCallback;
-
   // Returns instance owned by LoginShelfView. Installs instance of
   // ShelfTestUiUpdateDelegate when needed.
   static ShelfTestUiUpdateDelegate* Get(LoginShelfView* shelf) {
@@ -63,7 +61,7 @@
   ShelfTestUiUpdateDelegate() = default;
   ~ShelfTestUiUpdateDelegate() override {
     for (PendingCallback& entry : heap_)
-      std::move(entry.callback).Run(false);
+      std::move(entry.callback).Run();
   }
 
   // Returns UI update count.
@@ -73,9 +71,9 @@
   // |previous_update_count|. Note |callback| could be invoked synchronously
   // when the current ui update count is already greater than
   // |previous_update_count|.
-  void AddCallback(int64_t previous_update_count, Callback callback) {
+  void AddCallback(int64_t previous_update_count, base::OnceClosure callback) {
     if (previous_update_count < ui_update_count_) {
-      std::move(callback).Run(true);
+      std::move(callback).Run();
     } else {
       heap_.emplace_back(previous_update_count, std::move(callback));
       std::push_heap(heap_.begin(), heap_.end());
@@ -86,7 +84,7 @@
   void OnUiUpdate() override {
     ++ui_update_count_;
     while (!heap_.empty() && heap_.front().old_count < ui_update_count_) {
-      std::move(heap_.front().callback).Run(true);
+      std::move(heap_.front().callback).Run();
       std::pop_heap(heap_.begin(), heap_.end());
       heap_.pop_back();
     }
@@ -94,7 +92,7 @@
 
  private:
   struct PendingCallback {
-    PendingCallback(int64_t old_count, Callback callback)
+    PendingCallback(int64_t old_count, base::OnceClosure callback)
         : old_count(old_count), callback(std::move(callback)) {}
 
     bool operator<(const PendingCallback& right) const {
@@ -104,7 +102,7 @@
     }
 
     int64_t old_count = 0;
-    Callback callback;
+    base::OnceClosure callback;
   };
 
   std::vector<PendingCallback> heap_;
@@ -115,61 +113,48 @@
 };
 
 // static
-void LoginScreenTestApi::BindRequest(mojom::LoginScreenTestApiRequest request) {
-  mojo::MakeStrongBinding(std::make_unique<LoginScreenTestApi>(),
-                          std::move(request));
+bool LoginScreenTestApi::IsLockShown() {
+  return LockScreen::HasInstance() && LockScreen::Get()->is_shown() &&
+         LockScreen::Get()->screen_type() == LockScreen::ScreenType::kLock;
 }
 
-LoginScreenTestApi::LoginScreenTestApi() = default;
-
-LoginScreenTestApi::~LoginScreenTestApi() = default;
-
-void LoginScreenTestApi::IsLockShown(IsLockShownCallback callback) {
-  std::move(callback).Run(
-      LockScreen::HasInstance() && LockScreen::Get()->is_shown() &&
-      LockScreen::Get()->screen_type() == LockScreen::ScreenType::kLock);
-}
-
-void LoginScreenTestApi::IsLoginShelfShown(IsLoginShelfShownCallback callback) {
+// static
+bool LoginScreenTestApi::IsLoginShelfShown() {
   LoginShelfView* view = GetLoginShelfView();
-  std::move(callback).Run(view && view->GetVisible());
+  return view && view->GetVisible();
 }
 
-void LoginScreenTestApi::IsRestartButtonShown(
-    IsRestartButtonShownCallback callback) {
-  std::move(callback).Run(
-      IsLoginShelfViewButtonShown(LoginShelfView::kRestart));
+// static
+bool LoginScreenTestApi::IsRestartButtonShown() {
+  return IsLoginShelfViewButtonShown(LoginShelfView::kRestart);
 }
 
-void LoginScreenTestApi::IsShutdownButtonShown(
-    IsShutdownButtonShownCallback callback) {
-  std::move(callback).Run(
-      IsLoginShelfViewButtonShown(LoginShelfView::kShutdown));
+// static
+bool LoginScreenTestApi::IsShutdownButtonShown() {
+  return IsLoginShelfViewButtonShown(LoginShelfView::kShutdown);
 }
 
-void LoginScreenTestApi::IsAuthErrorBubbleShown(
-    IsAuthErrorBubbleShownCallback callback) {
+// static
+bool LoginScreenTestApi::IsAuthErrorBubbleShown() {
   ash::LockScreen::TestApi lock_screen_test(ash::LockScreen::Get());
   ash::LockContentsView::TestApi lock_contents_test(
       lock_screen_test.contents_view());
-  std::move(callback).Run(lock_contents_test.auth_error_bubble()->GetVisible());
+  return lock_contents_test.auth_error_bubble()->GetVisible();
 }
 
-void LoginScreenTestApi::IsGuestButtonShown(
-    IsGuestButtonShownCallback callback) {
-  std::move(callback).Run(
-      IsLoginShelfViewButtonShown(LoginShelfView::kBrowseAsGuest));
+// static
+bool LoginScreenTestApi::IsGuestButtonShown() {
+  return IsLoginShelfViewButtonShown(LoginShelfView::kBrowseAsGuest);
 }
 
-void LoginScreenTestApi::IsAddUserButtonShown(
-    IsAddUserButtonShownCallback callback) {
-  std::move(callback).Run(
-      IsLoginShelfViewButtonShown(LoginShelfView::kAddUser));
+// static
+bool LoginScreenTestApi::IsAddUserButtonShown() {
+  return IsLoginShelfViewButtonShown(LoginShelfView::kAddUser);
 }
 
+// static
 void LoginScreenTestApi::SubmitPassword(const AccountId& account_id,
-                                        const std::string& password,
-                                        SubmitPasswordCallback callback) {
+                                        const std::string& password) {
   // It'd be better to generate keyevents dynamically and dispatch them instead
   // of reaching into the views structure, but at the time of writing I could
   // not find a good way to do this. If you know of a way feel free to change
@@ -188,46 +173,44 @@
            auth_test.user_view()->current_user().basic_user_info.account_id);
 
   password_test.SubmitPassword(password);
-
-  std::move(callback).Run();
 }
 
-void LoginScreenTestApi::GetUiUpdateCount(GetUiUpdateCountCallback callback) {
+// static
+int64_t LoginScreenTestApi::GetUiUpdateCount() {
   LoginShelfView* view = GetLoginShelfView();
-
-  std::move(callback).Run(
-      view ? ShelfTestUiUpdateDelegate::Get(view)->ui_update_count() : 0);
+  return view ? ShelfTestUiUpdateDelegate::Get(view)->ui_update_count() : 0;
 }
 
-void LoginScreenTestApi::LaunchApp(const std::string& app_id,
-                                   LaunchAppCallback callback) {
+// static
+bool LoginScreenTestApi::LaunchApp(const std::string& app_id) {
   LoginShelfView* view = GetLoginShelfView();
-
-  std::move(callback).Run(view && view->LaunchAppForTesting(app_id));
+  return view && view->LaunchAppForTesting(app_id);
 }
 
-void LoginScreenTestApi::ClickAddUserButton(
-    ClickAddUserButtonCallback callback) {
+// static
+bool LoginScreenTestApi::ClickAddUserButton() {
   LoginShelfView* view = GetLoginShelfView();
-
-  std::move(callback).Run(view && view->SimulateAddUserButtonForTesting());
+  return view && view->SimulateAddUserButtonForTesting();
 }
 
-void LoginScreenTestApi::ClickGuestButton(ClickGuestButtonCallback callback) {
+// static
+bool LoginScreenTestApi::ClickGuestButton() {
   LoginShelfView* view = GetLoginShelfView();
-
-  std::move(callback).Run(view && view->SimulateGuestButtonForTesting());
+  return view && view->SimulateGuestButtonForTesting();
 }
 
-void LoginScreenTestApi::WaitForUiUpdate(int64_t previous_update_count,
-                                         WaitForUiUpdateCallback callback) {
+// static
+bool LoginScreenTestApi::WaitForUiUpdate(int64_t previous_update_count) {
   LoginShelfView* view = GetLoginShelfView();
   if (view) {
+    base::RunLoop run_loop;
     ShelfTestUiUpdateDelegate::Get(view)->AddCallback(previous_update_count,
-                                                      std::move(callback));
-  } else {
-    std::move(callback).Run(false);
+                                                      run_loop.QuitClosure());
+    run_loop.Run();
+    return true;
   }
+
+  return false;
 }
 
 }  // namespace ash
diff --git a/ash/login/login_screen_test_api.h b/ash/login/login_screen_test_api.h
deleted file mode 100644
index 249141d..0000000
--- a/ash/login/login_screen_test_api.h
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_LOGIN_LOGIN_SCREEN_TEST_API_H_
-#define ASH_LOGIN_LOGIN_SCREEN_TEST_API_H_
-
-#include <string>
-
-#include "ash/public/interfaces/login_screen_test_api.test-mojom.h"
-#include "base/macros.h"
-#include "components/account_id/account_id.h"
-
-namespace ash {
-
-// Allows tests to access private state of the login/lock screens.
-class LoginScreenTestApi : public mojom::LoginScreenTestApi {
- public:
-  // Creates and binds an instance from a remote request (e.g. from chrome).
-  static void BindRequest(mojom::LoginScreenTestApiRequest request);
-
-  LoginScreenTestApi();
-  ~LoginScreenTestApi() override;
-
-  // mojom::LoginScreen:
-  void IsLockShown(IsLockShownCallback callback) override;
-  void IsLoginShelfShown(IsLoginShelfShownCallback callback) override;
-  void IsRestartButtonShown(IsRestartButtonShownCallback callback) override;
-  void IsShutdownButtonShown(IsShutdownButtonShownCallback callback) override;
-  void IsAuthErrorBubbleShown(IsAuthErrorBubbleShownCallback callback) override;
-  void IsGuestButtonShown(IsGuestButtonShownCallback callback) override;
-  void IsAddUserButtonShown(IsAddUserButtonShownCallback callback) override;
-  void SubmitPassword(const AccountId& account_id,
-                      const std::string& password,
-                      SubmitPasswordCallback callback) override;
-  void GetUiUpdateCount(GetUiUpdateCountCallback callback) override;
-  void LaunchApp(const std::string& app_id,
-                 LaunchAppCallback callback) override;
-  void ClickAddUserButton(ClickAddUserButtonCallback callback) override;
-  void ClickGuestButton(ClickGuestButtonCallback callback) override;
-  // This blocks until UI update number becomes greater than the
-  // |previous_update_count|.  Where |previous_update_count| presumably is
-  // coming from GetUiUpdateCount().  This way test remembers the "current" UI
-  // update version, performs some actions, and then waits until UI switches to
-  // the new version.
-  void WaitForUiUpdate(int64_t previous_update_count,
-                       WaitForUiUpdateCallback callback) override;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(LoginScreenTestApi);
-};
-
-}  // namespace ash
-
-#endif  // ASH_LOGIN_LOGIN_SCREEN_TEST_API_H_
diff --git a/ash/login/ui/login_big_user_view.cc b/ash/login/ui/login_big_user_view.cc
index c1e4a105..130fc8ad 100644
--- a/ash/login/ui/login_big_user_view.cc
+++ b/ash/login/ui/login_big_user_view.cc
@@ -90,7 +90,7 @@
   DCHECK(IsChildAccountUser(auth_user_->current_user()));
   parent_access_ = new ParentAccessView(
       auth_user_->current_user().basic_user_info.account_id,
-      parent_access_callbacks_);
+      parent_access_callbacks_, ParentAccessRequestReason::kUnlockTimeLimits);
   RemoveChildView(auth_user_);
   AddChildView(parent_access_);
   RequestFocus();
diff --git a/ash/login/ui/parent_access_view.cc b/ash/login/ui/parent_access_view.cc
index 1efe760..e5b725a3 100644
--- a/ash/login/ui/parent_access_view.cc
+++ b/ash/login/ui/parent_access_view.cc
@@ -12,7 +12,9 @@
 #include "ash/login/ui/arrow_button_view.h"
 #include "ash/login/ui/login_button.h"
 #include "ash/login/ui/login_pin_view.h"
+#include "ash/public/cpp/login_types.h"
 #include "ash/resources/vector_icons/vector_icons.h"
+#include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/wallpaper/wallpaper_controller_impl.h"
@@ -104,6 +106,32 @@
                                   : kParentAccessViewHeightDp);
 }
 
+base::string16 GetTitle(ParentAccessRequestReason reason) {
+  int title_id;
+  switch (reason) {
+    case ParentAccessRequestReason::kUnlockTimeLimits:
+      title_id = IDS_ASH_LOGIN_PARENT_ACCESS_TITLE;
+      break;
+    case ParentAccessRequestReason::kChangeTime:
+      title_id = IDS_ASH_LOGIN_PARENT_ACCESS_TITLE_CHANGE_TIME;
+      break;
+  }
+  return l10n_util::GetStringUTF16(title_id);
+}
+
+base::string16 GetDescription(ParentAccessRequestReason reason) {
+  int description_id;
+  switch (reason) {
+    case ParentAccessRequestReason::kUnlockTimeLimits:
+      description_id = IDS_ASH_LOGIN_PARENT_ACCESS_DESCRIPTION;
+      break;
+    case ParentAccessRequestReason::kChangeTime:
+      description_id = IDS_ASH_LOGIN_PARENT_ACCESS_GENERIC_DESCRIPTION;
+      break;
+  }
+  return l10n_util::GetStringUTF16(description_id);
+}
+
 // Accessible input field. Customizes field description and focus behavior.
 class AccessibleInputField : public views::Textfield {
  public:
@@ -392,7 +420,8 @@
 ParentAccessView::Callbacks::~Callbacks() = default;
 
 ParentAccessView::ParentAccessView(const AccountId& account_id,
-                                   const Callbacks& callbacks)
+                                   const Callbacks& callbacks,
+                                   ParentAccessRequestReason reason)
     : NonAccessibleView(kParentAccessViewClassName),
       callbacks_(callbacks),
       account_id_(account_id) {
@@ -463,9 +492,8 @@
   };
 
   // Main view title.
-  title_label_ = new views::Label(
-      l10n_util::GetStringUTF16(IDS_ASH_LOGIN_PARENT_ACCESS_TITLE),
-      views::style::CONTEXT_LABEL, views::style::STYLE_PRIMARY);
+  title_label_ = new views::Label(GetTitle(reason), views::style::CONTEXT_LABEL,
+                                  views::style::STYLE_PRIMARY);
   title_label_->SetFontList(gfx::FontList().Derive(
       kTitleFontSizeDeltaDp, gfx::Font::NORMAL, gfx::Font::Weight::MEDIUM));
   decorate_label(title_label_);
@@ -474,9 +502,10 @@
   add_spacer(kTitleToDescriptionDistanceDp);
 
   // Main view description.
-  description_label_ = new views::Label(
-      l10n_util::GetStringUTF16(IDS_ASH_LOGIN_PARENT_ACCESS_DESCRIPTION),
-      views::style::CONTEXT_LABEL, views::style::STYLE_PRIMARY);
+  // TODO(crbug.com/970223): Add learn more link after description.
+  description_label_ =
+      new views::Label(GetDescription(reason), views::style::CONTEXT_LABEL,
+                       views::style::STYLE_PRIMARY);
   description_label_->SetMultiLine(true);
   description_label_->SetLineHeight(kDescriptionTextLineHeightDp);
   description_label_->SetFontList(
@@ -566,13 +595,21 @@
 void ParentAccessView::OnPaint(gfx::Canvas* canvas) {
   views::View::OnPaint(canvas);
 
+  SkColor color = gfx::kGoogleGrey900;
+  if (Shell::Get()->session_controller()->GetSessionState() !=
+      session_manager::SessionState::ACTIVE) {
+    SkColor extracted_color =
+        Shell::Get()->wallpaper_controller()->GetProminentColor(
+            color_utils::ColorProfile(color_utils::LumaRange::DARK,
+                                      color_utils::SaturationRange::MUTED));
+    if (extracted_color != kInvalidWallpaperColor &&
+        extracted_color != SK_ColorTRANSPARENT) {
+      color = extracted_color;
+    }
+  }
+
   cc::PaintFlags flags;
   flags.setStyle(cc::PaintFlags::kFill_Style);
-  SkColor color = Shell::Get()->wallpaper_controller()->GetProminentColor(
-      color_utils::ColorProfile(color_utils::LumaRange::DARK,
-                                color_utils::SaturationRange::MUTED));
-  if (color == kInvalidWallpaperColor || color == SK_ColorTRANSPARENT)
-    color = gfx::kGoogleGrey900;
   flags.setColor(color);
   canvas->DrawRoundRect(GetContentsBounds(),
                         kParentAccessViewRoundedCornerRadiusDp, flags);
diff --git a/ash/login/ui/parent_access_view.h b/ash/login/ui/parent_access_view.h
index ef21b82..a3bc54d 100644
--- a/ash/login/ui/parent_access_view.h
+++ b/ash/login/ui/parent_access_view.h
@@ -28,6 +28,8 @@
 class LoginButton;
 class LoginPinView;
 
+enum class ParentAccessRequestReason;
+
 // The view that allows for input of parent access code to authorize certain
 // actions on child's device.
 class ASH_EXPORT ParentAccessView : public NonAccessibleView,
@@ -78,8 +80,12 @@
   // Creates parent access view that will validate the parent access code for a
   // specific child, when |account_id| is set, or to any child signed in the
   // device, when it is empty. |callbacks| will be called when user performs
-  // certain actions.
-  ParentAccessView(const AccountId& account_id, const Callbacks& callbacks);
+  // certain actions. |reason| contains information about why the parent access
+  // view is necessary, it is used to modify the view appearance by changing the
+  // title and description strings and background color.
+  ParentAccessView(const AccountId& account_id,
+                   const Callbacks& callbacks,
+                   ParentAccessRequestReason reason);
   ~ParentAccessView() override;
 
   // views::View:
diff --git a/ash/login/ui/parent_access_view_unittest.cc b/ash/login/ui/parent_access_view_unittest.cc
index fc6f6da..7ced1ea 100644
--- a/ash/login/ui/parent_access_view_unittest.cc
+++ b/ash/login/ui/parent_access_view_unittest.cc
@@ -14,11 +14,14 @@
 #include "ash/login/ui/login_test_base.h"
 #include "ash/login/ui/login_test_utils.h"
 #include "ash/shell.h"
+#include "ash/strings/grit/ash_strings.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "base/bind.h"
 #include "base/macros.h"
 #include "base/optional.h"
+#include "base/test/bind_test_util.h"
 #include "components/account_id/account_id.h"
+#include "ui/base/l10n/l10n_util.h"
 #include "ui/events/base_event_utils.h"
 #include "ui/events/event.h"
 #include "ui/events/test/event_generator.h"
@@ -39,14 +42,6 @@
   // LoginScreenTest:
   void SetUp() override {
     LoginTestBase::SetUp();
-
-    ParentAccessView::Callbacks callbacks;
-    callbacks.on_finished = base::BindRepeating(
-        &ParentAccessViewTest::OnFinished, base::Unretained(this));
-
-    view_ = new ParentAccessView(account_id_, callbacks);
-    SetWidget(CreateWidgetWithContent(view_));
-
     login_client_ = std::make_unique<MockLoginScreenClient>();
   }
 
@@ -69,6 +64,16 @@
     access_granted ? ++successful_validation_ : ++back_action_;
   }
 
+  void StartView(ParentAccessRequestReason reason =
+                     ParentAccessRequestReason::kUnlockTimeLimits) {
+    ParentAccessView::Callbacks callbacks;
+    callbacks.on_finished = base::BindRepeating(
+        &ParentAccessViewTest::OnFinished, base::Unretained(this));
+
+    view_ = new ParentAccessView(account_id_, callbacks, reason);
+    SetWidget(CreateWidgetWithContent(view_));
+  }
+
   const AccountId account_id_;
   std::unique_ptr<MockLoginScreenClient> login_client_;
 
@@ -86,8 +91,33 @@
 
 }  // namespace
 
+// Tests that title and description are correctly set when reason is unlock time
+// limits.
+TEST_F(ParentAccessViewTest, UnlockTimeLimitsStrings) {
+  StartView(ParentAccessRequestReason::kUnlockTimeLimits);
+  ParentAccessView::TestApi test_api(view_);
+  EXPECT_EQ(l10n_util::GetStringUTF16(IDS_ASH_LOGIN_PARENT_ACCESS_TITLE),
+            test_api.title_label()->text());
+  EXPECT_EQ(l10n_util::GetStringUTF16(IDS_ASH_LOGIN_PARENT_ACCESS_DESCRIPTION),
+            test_api.description_label()->text());
+}
+
+// Tests that title and description are correctly set when reason is change
+// time.
+TEST_F(ParentAccessViewTest, ChangeTimeStrings) {
+  StartView(ParentAccessRequestReason::kChangeTime);
+  ParentAccessView::TestApi test_api(view_);
+  EXPECT_EQ(
+      l10n_util::GetStringUTF16(IDS_ASH_LOGIN_PARENT_ACCESS_TITLE_CHANGE_TIME),
+      test_api.title_label()->text());
+  EXPECT_EQ(l10n_util::GetStringUTF16(
+                IDS_ASH_LOGIN_PARENT_ACCESS_GENERIC_DESCRIPTION),
+            test_api.description_label()->text());
+}
+
 // Tests that back button works.
 TEST_F(ParentAccessViewTest, BackButton) {
+  StartView();
   ParentAccessView::TestApi test_api(view_);
   EXPECT_TRUE(test_api.back_button()->GetEnabled());
   EXPECT_EQ(0, back_action_);
@@ -100,6 +130,7 @@
 
 // Tests that submit button submits code from code input.
 TEST_F(ParentAccessViewTest, SubmitButton) {
+  StartView();
   ParentAccessView::TestApi test_api(view_);
   EXPECT_FALSE(test_api.submit_button()->GetEnabled());
 
@@ -121,6 +152,7 @@
 
 // Tests that access code can be entered with numpad.
 TEST_F(ParentAccessViewTest, Numpad) {
+  StartView();
   ParentAccessView::TestApi test_api(view_);
 
   ui::test::EventGenerator* generator = GetEventGenerator();
@@ -139,6 +171,7 @@
 
 // Tests that access code can be submitted with press of 'enter' key.
 TEST_F(ParentAccessViewTest, SubmitWithEnter) {
+  StartView();
   ParentAccessView::TestApi test_api(view_);
   EXPECT_FALSE(test_api.submit_button()->GetEnabled());
 
@@ -160,6 +193,7 @@
 
 // Tests that 'enter' key does not submit incomplete code.
 TEST_F(ParentAccessViewTest, PressEnterOnIncompleteCode) {
+  StartView();
   ParentAccessView::TestApi test_api(view_);
   EXPECT_FALSE(test_api.submit_button()->GetEnabled());
 
@@ -195,6 +229,7 @@
 
 // Tests that backspace button works.
 TEST_F(ParentAccessViewTest, Backspace) {
+  StartView();
   ParentAccessView::TestApi test_api(view_);
   EXPECT_FALSE(test_api.submit_button()->GetEnabled());
 
@@ -231,6 +266,7 @@
 
 // Tests input with virtual pin keyboard.
 TEST_F(ParentAccessViewTest, PinKeyboard) {
+  StartView();
   Shell::Get()->tablet_mode_controller()->EnableTabletModeWindowManager(true);
 
   ParentAccessView::TestApi test_api(view_);
@@ -252,6 +288,7 @@
 
 // Tests that pin keyboard visibility changes upon tablet mode changes.
 TEST_F(ParentAccessViewTest, PinKeyboardVisibilityChange) {
+  StartView();
   ParentAccessView::TestApi test_api(view_);
   LoginPinView::TestApi test_pin_keyboard(test_api.pin_keyboard_view());
   EXPECT_FALSE(test_api.pin_keyboard_view()->GetVisible());
@@ -265,6 +302,7 @@
 
 // Tests that error state is shown and cleared when neccesary.
 TEST_F(ParentAccessViewTest, ErrorState) {
+  StartView();
   ParentAccessView::TestApi test_api(view_);
   EXPECT_EQ(ParentAccessView::State::kNormal, test_api.state());
 
@@ -299,6 +337,7 @@
 
 // Tests children views traversal with tab key.
 TEST_F(ParentAccessViewTest, TabKeyTraversal) {
+  StartView();
   ParentAccessView::TestApi test_api(view_);
   EXPECT_TRUE(HasFocusInAnyChildView(test_api.access_code_view()));
 
@@ -328,6 +367,7 @@
 
 // Tests children views backwards traversal with tab key.
 TEST_F(ParentAccessViewTest, BackwardTabKeyTraversal) {
+  StartView();
   ParentAccessView::TestApi test_api(view_);
   EXPECT_TRUE(HasFocusInAnyChildView(test_api.access_code_view()));
 
diff --git a/ash/login/ui/parent_access_widget.cc b/ash/login/ui/parent_access_widget.cc
index 8069d6bb..2b3dea7 100644
--- a/ash/login/ui/parent_access_widget.cc
+++ b/ash/login/ui/parent_access_widget.cc
@@ -16,7 +16,8 @@
 namespace ash {
 
 ParentAccessWidget::ParentAccessWidget(const AccountId& account_id,
-                                       const OnExitCallback& callback)
+                                       const OnExitCallback& callback,
+                                       ParentAccessRequestReason reason)
     : callback_(callback) {
   views::Widget::InitParams widget_params;
   // Using window frameless to be able to get focus on the view input fields,
@@ -43,7 +44,7 @@
   callbacks.on_finished = base::BindRepeating(&ParentAccessWidget::OnExit,
                                               weak_factory_.GetWeakPtr());
 
-  widget_->SetContentsView(new ParentAccessView(account_id, callbacks));
+  widget_->SetContentsView(new ParentAccessView(account_id, callbacks, reason));
   widget_->CenterWindow(widget_->GetContentsView()->GetPreferredSize());
   widget_->Show();
   widget_->GetContentsView()->RequestFocus();
diff --git a/ash/login/ui/parent_access_widget.h b/ash/login/ui/parent_access_widget.h
index 42e1f1ef..c11400ff 100644
--- a/ash/login/ui/parent_access_widget.h
+++ b/ash/login/ui/parent_access_widget.h
@@ -20,6 +20,8 @@
 
 namespace ash {
 
+enum class ParentAccessRequestReason;
+
 // Widget to display the Parent Access View in a standalone container.
 class ParentAccessWidget {
  public:
@@ -29,9 +31,13 @@
   // access code is validated using the configuration for the provided account,
   // when it is empty it tries to validate the access code to any child signed
   // in the device. The |callback| is called when (a) the validation is
-  // successful or (b) the back button is pressed.
+  // successful or (b) the back button is pressed. |reason| contains information
+  // about why the parent access view is necessary, it is used to modify the
+  // widget appearance by changing the title and description strings and
+  // background color
   ParentAccessWidget(const AccountId& account_id,
-                     const OnExitCallback& callback);
+                     const OnExitCallback& callback,
+                     ParentAccessRequestReason reason);
 
   ~ParentAccessWidget();
 
diff --git a/ash/mojo_test_interface_factory.cc b/ash/mojo_test_interface_factory.cc
index c9617008..ae67861 100644
--- a/ash/mojo_test_interface_factory.cc
+++ b/ash/mojo_test_interface_factory.cc
@@ -6,8 +6,6 @@
 
 #include <utility>
 
-#include "ash/login/login_screen_test_api.h"
-#include "ash/public/interfaces/login_screen_test_api.test-mojom.h"
 #include "ash/public/interfaces/status_area_widget_test_api.test-mojom.h"
 #include "ash/system/status_area_widget_test_api.h"
 #include "base/bind.h"
@@ -20,11 +18,6 @@
 // These functions aren't strictly necessary, but exist to make threading and
 // arguments clearer.
 
-void BindLoginScreenTestApiOnMainThread(
-    mojom::LoginScreenTestApiRequest request) {
-  LoginScreenTestApi::BindRequest(std::move(request));
-}
-
 void BindStatusAreaWidgetTestApiOnMainThread(
     mojom::StatusAreaWidgetTestApiRequest request) {
   StatusAreaWidgetTestApi::BindRequest(std::move(request));
@@ -35,8 +28,6 @@
 void RegisterInterfaces(
     service_manager::BinderRegistry* registry,
     scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner) {
-  registry->AddInterface(base::Bind(&BindLoginScreenTestApiOnMainThread),
-                         main_thread_task_runner);
   registry->AddInterface(base::Bind(&BindStatusAreaWidgetTestApiOnMainThread),
                          main_thread_task_runner);
 }
diff --git a/ash/public/cpp/BUILD.gn b/ash/public/cpp/BUILD.gn
index 4562da8f..c8e20cf 100644
--- a/ash/public/cpp/BUILD.gn
+++ b/ash/public/cpp/BUILD.gn
@@ -108,6 +108,7 @@
     "login_screen_client.h",
     "login_screen_model.cc",
     "login_screen_model.h",
+    "login_screen_test_api.h",
     "login_types.cc",
     "login_types.h",
     "media_client.h",
diff --git a/ash/public/cpp/login_screen.h b/ash/public/cpp/login_screen.h
index b63dc66e..14473377 100644
--- a/ash/public/cpp/login_screen.h
+++ b/ash/public/cpp/login_screen.h
@@ -17,6 +17,8 @@
 class LoginScreenClient;
 class LoginScreenModel;
 
+enum class ParentAccessRequestReason;
+
 // Allows clients (e.g. the browser process) to send messages to the ash
 // login/lock/user-add screens.
 // TODO(estade): move more of mojom::LoginScreen here.
@@ -61,11 +63,15 @@
   // validates the parent access code for that child only, when it is  empty it
   // validates the code for any child signed in the device. |callback| is
   // invoked when the back button is clicked or the correct code was entered.
-  // Note: this is intended for children only. If a non child account id is
-  // provided, the validation will necessarily fail.
+  // |reason| contains information about why the parent access view is
+  // necessary, it is used to modify the view appearance by changing the title
+  // and description strings and background color. Note: this is intended for
+  // children only. If a non child account id is provided, the validation will
+  // necessarily fail.
   virtual void ShowParentAccessWidget(
       const AccountId& child_account_id,
-      base::RepeatingCallback<void(bool success)> callback) = 0;
+      base::RepeatingCallback<void(bool success)> callback,
+      ParentAccessRequestReason reason) = 0;
 
   // Sets if the guest button on the login shelf can be shown. Even if set to
   // true the button may still not be visible.
diff --git a/ash/public/cpp/login_screen_test_api.h b/ash/public/cpp/login_screen_test_api.h
new file mode 100644
index 0000000..cfb9e2b
--- /dev/null
+++ b/ash/public/cpp/login_screen_test_api.h
@@ -0,0 +1,40 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_PUBLIC_CPP_LOGIN_SCREEN_TEST_API_H_
+#define ASH_PUBLIC_CPP_LOGIN_SCREEN_TEST_API_H_
+
+#include <string>
+
+#include "ash/public/cpp/ash_public_export.h"
+#include "base/macros.h"
+
+class AccountId;
+
+namespace ash {
+
+class ASH_PUBLIC_EXPORT LoginScreenTestApi {
+ public:
+  static bool IsLockShown();
+  static bool IsLoginShelfShown();
+  static bool IsRestartButtonShown();
+  static bool IsShutdownButtonShown();
+  static bool IsAuthErrorBubbleShown();
+  static bool IsGuestButtonShown();
+  static bool IsAddUserButtonShown();
+  static void SubmitPassword(const AccountId& account_id,
+                             const std::string& password);
+  static int64_t GetUiUpdateCount();
+  static bool LaunchApp(const std::string& app_id);
+  static bool ClickAddUserButton();
+  static bool ClickGuestButton();
+  static bool WaitForUiUpdate(int64_t previous_update_count);
+
+ private:
+  DISALLOW_IMPLICIT_CONSTRUCTORS(LoginScreenTestApi);
+};
+
+}  // namespace ash
+
+#endif  // ASH_PUBLIC_CPP_LOGIN_SCREEN_TEST_API_H_
diff --git a/ash/public/cpp/login_types.h b/ash/public/cpp/login_types.h
index 575b5ac8..343dcc68 100644
--- a/ash/public/cpp/login_types.h
+++ b/ash/public/cpp/login_types.h
@@ -286,6 +286,16 @@
   base::TimeDelta device_used_time;
 };
 
+// Possible reasons why the parent access code is required. This corresponds to
+// actions that children can't perform on a Chromebook, but their parents can on
+// their behalf.
+enum class ParentAccessRequestReason {
+  // Unlock a Chromebook that is locked due to a Time Limit policy.
+  kUnlockTimeLimits,
+  // Change time or timezone of the Chromebook.
+  kChangeTime,
+};
+
 }  // namespace ash
 
 #endif  // ASH_PUBLIC_CPP_LOGIN_TYPES_H_
diff --git a/ash/public/cpp/pagination/pagination_controller.cc b/ash/public/cpp/pagination/pagination_controller.cc
index 68bc424f..5ef677c1 100644
--- a/ash/public/cpp/pagination/pagination_controller.cc
+++ b/ash/public/cpp/pagination/pagination_controller.cc
@@ -70,35 +70,16 @@
       float scroll = scroll_axis_ == SCROLL_AXIS_HORIZONTAL
                          ? details.scroll_x_hint()
                          : details.scroll_y_hint();
-      if (scroll == 0)
-        return false;
-      pagination_model_->StartScroll();
-      return true;
+      return StartDrag(scroll);
     }
     case ui::ET_GESTURE_SCROLL_UPDATE: {
       float scroll = scroll_axis_ == SCROLL_AXIS_HORIZONTAL
                          ? details.scroll_x()
                          : details.scroll_y();
-      if (!pagination_model_->IsValidPageRelative(scroll < 0 ? 1 : -1) &&
-          !pagination_model_->has_transition()) {
-        // scroll > 0 means moving contents right or down. That is,
-        // transitioning to the previous page. If scrolling to an invalid page,
-        // ignore the event until movement continues in a valid direction.
-        return true;
-      }
-      int width = scroll_axis_ == SCROLL_AXIS_HORIZONTAL ? bounds.width()
-                                                         : bounds.height();
-      pagination_model_->UpdateScroll(scroll / width);
-      return true;
+      return UpdateDrag(scroll, bounds);
     }
     case ui::ET_GESTURE_SCROLL_END: {
-      const bool cancel_transition =
-          pagination_model_->transition().progress < kFinishTransitionThreshold;
-      pagination_model_->EndScroll(cancel_transition);
-      if (!cancel_transition) {
-        record_metrics_.Run(event.type(), is_tablet_mode_);
-      }
-      return true;
+      return EndDrag(event);
     }
     case ui::ET_SCROLL_FLING_START: {
       float velocity = scroll_axis_ == SCROLL_AXIS_HORIZONTAL
@@ -120,4 +101,54 @@
   }
 }
 
+void PaginationController::StartMouseDrag(const gfx::Vector2d& offset) {
+  float scroll =
+      scroll_axis_ == SCROLL_AXIS_HORIZONTAL ? offset.x() : offset.y();
+  StartDrag(scroll);
+}
+
+void PaginationController::UpdateMouseDrag(const gfx::Vector2d& offset,
+                                           const gfx::Rect& bounds) {
+  float scroll =
+      scroll_axis_ == SCROLL_AXIS_HORIZONTAL ? offset.x() : offset.y();
+  UpdateDrag(scroll, bounds);
+}
+
+void PaginationController::EndMouseDrag(const ui::MouseEvent& event) {
+  EndDrag(event);
+}
+
+bool PaginationController::StartDrag(float scroll) {
+  if (scroll == 0)
+    return false;
+  pagination_model_->StartScroll();
+  return true;
+}
+
+bool PaginationController::UpdateDrag(float scroll, const gfx::Rect& bounds) {
+  if (!pagination_model_->IsValidPageRelative(scroll < 0 ? 1 : -1) &&
+      !pagination_model_->has_transition()) {
+    // scroll > 0 means moving contents right or down. That is,
+    // transitioning to the previous page. If scrolling to an invalid page,
+    // ignore the event until movement continues in a valid direction.
+    return true;
+  }
+  int width =
+      scroll_axis_ == SCROLL_AXIS_HORIZONTAL ? bounds.width() : bounds.height();
+
+  pagination_model_->UpdateScroll(scroll / width);
+  return true;
+}
+
+bool PaginationController::EndDrag(const ui::LocatedEvent& event) {
+  const bool cancel_transition =
+      pagination_model_->transition().progress < kFinishTransitionThreshold;
+  pagination_model_->EndScroll(cancel_transition);
+
+  if (!cancel_transition)
+    record_metrics_.Run(event.type(), is_tablet_mode_);
+
+  return true;
+}
+
 }  // namespace ash
diff --git a/ash/public/cpp/pagination/pagination_controller.h b/ash/public/cpp/pagination/pagination_controller.h
index aea9b2e..60fc4a0 100644
--- a/ash/public/cpp/pagination/pagination_controller.h
+++ b/ash/public/cpp/pagination/pagination_controller.h
@@ -47,9 +47,21 @@
   // PaginationModel. Returns true if the event was captured.
   bool OnGestureEvent(const ui::GestureEvent& event, const gfx::Rect& bounds);
 
+  // Handles a mouse event in the area represented by the PaginationModel.
+  // |drag_offset| should be in screen coordinates.
+  void StartMouseDrag(const gfx::Vector2d& drag_offset);
+  void UpdateMouseDrag(const gfx::Vector2d& drag_offset,
+                       const gfx::Rect& bounds);
+  void EndMouseDrag(const ui::MouseEvent& event);
+
   void set_is_tablet_mode(bool started) { is_tablet_mode_ = started; }
 
  private:
+  // Drag related functions. Utilized by both gesture drag and mouse drag:
+  bool StartDrag(float scroll);
+  bool UpdateDrag(float scroll, const gfx::Rect& bounds);
+  bool EndDrag(const ui::LocatedEvent& event);
+
   PaginationModel* pagination_model_;  // Not owned.
   ScrollAxis scroll_axis_;
 
diff --git a/ash/public/cpp/test_manifest.cc b/ash/public/cpp/test_manifest.cc
index 6330ebe..95c0028 100644
--- a/ash/public/cpp/test_manifest.cc
+++ b/ash/public/cpp/test_manifest.cc
@@ -4,7 +4,6 @@
 
 #include "ash/public/cpp/test_manifest.h"
 
-#include "ash/public/interfaces/login_screen_test_api.test-mojom.h"
 #include "ash/public/interfaces/status_area_widget_test_api.test-mojom.h"
 #include "base/no_destructor.h"
 #include "services/service_manager/public/cpp/manifest_builder.h"
@@ -14,10 +13,8 @@
 const service_manager::Manifest& GetManifestOverlayForTesting() {
   static base::NoDestructor<service_manager::Manifest> manifest{
       service_manager::ManifestBuilder()
-          .ExposeCapability(
-              "test",
-              service_manager::Manifest::InterfaceList<
-                  mojom::LoginScreenTestApi, mojom::StatusAreaWidgetTestApi>())
+          .ExposeCapability("test", service_manager::Manifest::InterfaceList<
+                                        mojom::StatusAreaWidgetTestApi>())
           .Build()};
   return *manifest;
 }
diff --git a/ash/public/interfaces/BUILD.gn b/ash/public/interfaces/BUILD.gn
index 12d9fd51..e797225 100644
--- a/ash/public/interfaces/BUILD.gn
+++ b/ash/public/interfaces/BUILD.gn
@@ -56,7 +56,6 @@
   testonly = true
   disable_variants = true
   sources = [
-    "login_screen_test_api.test-mojom",
     "status_area_widget_test_api.test-mojom",
   ]
   deps = [
diff --git a/ash/public/interfaces/ash_message_center_controller.mojom b/ash/public/interfaces/ash_message_center_controller.mojom
index a17e5e9..d28a2ac 100644
--- a/ash/public/interfaces/ash_message_center_controller.mojom
+++ b/ash/public/interfaces/ash_message_center_controller.mojom
@@ -38,14 +38,6 @@
   // Sets the ARC notification instance.
   SetArcNotificationsInstance(arc.mojom.NotificationsInstance instance);
 
-  // Shows a notification. |display_token| will be used to reference this
-  // notification for AshMessageCenterClient::Close().
-  ShowClientNotification(message_center.mojom.Notification notification,
-                         mojo_base.mojom.UnguessableToken display_token);
-
-  // Closes a notification by the notification ID.
-  CloseClientNotification(string id);
-
   UpdateNotifierIcon(message_center.mojom.NotifierId notifier_id,
                      gfx.mojom.ImageSkia icon);
 
@@ -65,26 +57,6 @@
 // |notification| in ShowClientNotification and the format of the ID is up to
 // the client.
 interface AshMessageCenterClient {
-  // Called when a notification previously displayed by the client is closed.
-  HandleNotificationClosed(mojo_base.mojom.UnguessableToken display_token,
-                           bool by_user);
-
-  // Called when the body of a notification is clicked.
-  HandleNotificationClicked(string id);
-
-  // Called when a notification that has buttons (e.g., "Learn more") receives a
-  // click on one of the buttons. |reply| is a user-provided string for cases
-  // where the button had a text input field attached to it.
-  HandleNotificationButtonClicked(string id, int32 button_index,
-                                  mojo_base.mojom.String16? reply);
-
-  // Called when a notification's settings button has been pressed (and the
-  // handler is SettingsButtonHandler::DELEGATE).
-  HandleNotificationSettingsButtonClicked(string id);
-
-  // Called when a notification has been disabled (via inline settings).
-  DisableNotification(string id);
-
   // Called when a user enables or disables notifications from the given
   // notifier.
   SetNotifierEnabled(message_center.mojom.NotifierId notifier_id, bool enabled);
diff --git a/ash/public/interfaces/login_screen_test_api.test-mojom b/ash/public/interfaces/login_screen_test_api.test-mojom
deleted file mode 100644
index eeab1ae1..0000000
--- a/ash/public/interfaces/login_screen_test_api.test-mojom
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module ash.mojom;
-
-import "components/account_id/interfaces/account_id.mojom";
-
-// Provides a high-level test API for controlling the login/lock screen.
-interface LoginScreenTestApi {
-  // Returns true if the lock screen is currently being shown.
-  IsLockShown() => (bool is_shown);
-
-  // Returns true if the login shelf is currently being shown.
-  IsLoginShelfShown() => (bool is_shown);
-
-  // Returns true if Restart button is currently being shown.
-  IsRestartButtonShown() => (bool is_shown);
-
-  // Returns true if Shutdown button is currently being shown.
-  IsShutdownButtonShown() => (bool is_shown);
-
-  // Returns true if Auth Error Button is currently being shown.
-  IsAuthErrorBubbleShown() => (bool is_shown);
-
-  // Returns true if Guest button is currently being shown.
-  IsGuestButtonShown() => (bool is_shown);
-
-  // Returns true if Add User button is currently being shown.
-  IsAddUserButtonShown() => (bool is_shown);
-
-  // Submit |password| for |account_id|.
-  SubmitPassword(signin.mojom.AccountId account_id, string password) => ();
-
-  // Fetches current UI update count.
-  GetUiUpdateCount() => (int64 count);
-
-  // Simulates Kiosk App launch. Returns true if launch attempt was successful.
-  // (I.e. app was found, and launch event was generated.)
-  LaunchApp(string app_id) => (bool found);
-
-  // Simulate Click on AddUser button.
-  // Returns true if request was successful.
-  ClickAddUserButton() => (bool success);
-
-  // Simulate click on the guest login button.
-  // Returns whether the request was succesful.
-  ClickGuestButton() => (bool success);
-
-  // Blocks until UI update counter is greated than |previous_update_count|.
-  // Returns true on success, false on error.
-  WaitForUiUpdate(int64 previous_update_count) => (bool success);
-};
diff --git a/ash/system/message_center/message_center_controller.cc b/ash/system/message_center/message_center_controller.cc
index 624764b..b224293 100644
--- a/ash/system/message_center/message_center_controller.cc
+++ b/ash/system/message_center/message_center_controller.cc
@@ -60,54 +60,6 @@
   DISALLOW_COPY_AND_ASSIGN(PopupNotificationBlocker);
 };
 
-// This notification delegate passes actions back to the client that asked for
-// the notification (Chrome).
-class AshClientNotificationDelegate
-    : public message_center::NotificationDelegate {
- public:
-  AshClientNotificationDelegate(const std::string& notification_id,
-                                const base::UnguessableToken& display_token,
-                                mojom::AshMessageCenterClient* client)
-      : notification_id_(notification_id),
-        display_token_(display_token),
-        client_(client) {}
-
-  void Close(bool by_user) override {
-    client_->HandleNotificationClosed(display_token_, by_user);
-  }
-
-  void Click(const base::Optional<int>& button_index,
-             const base::Optional<base::string16>& reply) override {
-    if (button_index) {
-      client_->HandleNotificationButtonClicked(notification_id_, *button_index,
-                                               reply);
-    } else {
-      client_->HandleNotificationClicked(notification_id_);
-    }
-  }
-
-  void SettingsClick() override {
-    client_->HandleNotificationSettingsButtonClicked(notification_id_);
-  }
-
-  void DisableNotification() override {
-    client_->DisableNotification(notification_id_);
-  }
-
- private:
-  ~AshClientNotificationDelegate() override = default;
-
-  // The ID of the notification.
-  const std::string notification_id_;
-
-  // The token that was generated for the ShowClientNotification() call.
-  const base::UnguessableToken display_token_;
-
-  mojom::AshMessageCenterClient* client_;
-
-  DISALLOW_COPY_AND_ASSIGN(AshClientNotificationDelegate);
-};
-
 }  // namespace
 
 MessageCenterController::MessageCenterController() {
@@ -201,22 +153,6 @@
   arc_notification_manager_->SetInstance(std::move(arc_notification_instance));
 }
 
-void MessageCenterController::ShowClientNotification(
-    const message_center::Notification& notification,
-    const base::UnguessableToken& display_token) {
-  DCHECK(client_.is_bound());
-  auto message_center_notification =
-      std::make_unique<message_center::Notification>(notification);
-  message_center_notification->set_delegate(
-      base::WrapRefCounted(new AshClientNotificationDelegate(
-          notification.id(), display_token, client_.get())));
-  MessageCenter::Get()->AddNotification(std::move(message_center_notification));
-}
-
-void MessageCenterController::CloseClientNotification(const std::string& id) {
-  MessageCenter::Get()->RemoveNotification(id, false /* by_user */);
-}
-
 void MessageCenterController::UpdateNotifierIcon(const NotifierId& notifier_id,
                                                  const gfx::ImageSkia& icon) {
   for (auto& listener : notifier_settings_listeners_)
diff --git a/ash/system/message_center/message_center_controller.h b/ash/system/message_center/message_center_controller.h
index 60581717..b948dbd2 100644
--- a/ash/system/message_center/message_center_controller.h
+++ b/ash/system/message_center/message_center_controller.h
@@ -49,10 +49,6 @@
       mojom::AshMessageCenterClientAssociatedPtrInfo client) override;
   void SetArcNotificationsInstance(
       arc::mojom::NotificationsInstancePtr arc_notification_instance) override;
-  void ShowClientNotification(
-      const message_center::Notification& notification,
-      const base::UnguessableToken& display_token) override;
-  void CloseClientNotification(const std::string& id) override;
   void UpdateNotifierIcon(const message_center::NotifierId& notifier_id,
                           const gfx::ImageSkia& icon) override;
   void NotifierEnabledChanged(const message_center::NotifierId& notifier_id,
diff --git a/ash/system/message_center/notifier_settings_view.cc b/ash/system/message_center/notifier_settings_view.cc
index b82f467..6223e9c0b 100644
--- a/ash/system/message_center/notifier_settings_view.cc
+++ b/ash/system/message_center/notifier_settings_view.cc
@@ -301,27 +301,33 @@
 NotifierSettingsView::NotifierButton::NotifierButton(
     const mojom::NotifierUiData& notifier_ui_data,
     views::ButtonListener* listener)
-    : views::Button(listener),
-      notifier_id_(notifier_ui_data.notifier_id),
-      icon_view_(new views::ImageView()),
-      name_view_(new views::Label(notifier_ui_data.name)),
-      checkbox_(new views::Checkbox(base::string16(), this /* listener */)) {
-  name_view_->SetAutoColorReadabilityEnabled(false);
-  name_view_->SetEnabledColor(kUnifiedMenuTextColor);
-  name_view_->SetSubpixelRenderingEnabled(false);
+    : views::Button(listener), notifier_id_(notifier_ui_data.notifier_id) {
+  auto icon_view = std::make_unique<views::ImageView>();
+  auto name_view = std::make_unique<views::Label>(notifier_ui_data.name);
+  auto checkbox =
+      std::make_unique<views::Checkbox>(base::string16(), this /* listener */);
+  name_view->SetAutoColorReadabilityEnabled(false);
+  name_view->SetEnabledColor(kUnifiedMenuTextColor);
+  name_view->SetSubpixelRenderingEnabled(false);
   // "Roboto-Regular, 13sp" is specified in the mock.
-  name_view_->SetFontList(
+  name_view->SetFontList(
       gfx::FontList().DeriveWithSizeDelta(kLabelFontSizeDelta));
 
-  checkbox_->SetChecked(notifier_ui_data.enabled);
-  checkbox_->SetFocusBehavior(FocusBehavior::NEVER);
-  checkbox_->SetAccessibleName(notifier_ui_data.name);
+  checkbox->SetChecked(notifier_ui_data.enabled);
+  checkbox->SetFocusBehavior(FocusBehavior::NEVER);
+  checkbox->SetAccessibleName(notifier_ui_data.name);
 
   if (notifier_ui_data.enforced) {
     Button::SetEnabled(false);
-    checkbox_->SetEnabled(false);
+    checkbox->SetEnabled(false);
   }
 
+  // Add the views before the layout is assigned. Because GridChanged() may be
+  // called multiple times, these views should already be child views.
+  checkbox_ = AddChildView(std::move(checkbox));
+  icon_view_ = AddChildView(std::move(icon_view));
+  name_view_ = AddChildView(std::move(name_view));
+
   UpdateIconImage(notifier_ui_data.icon);
 }
 
@@ -421,25 +427,26 @@
   SetPaintToLayer();
   layer()->SetFillsBoundsOpaquely(false);
 
-  header_view_ = new views::View;
-  header_view_->SetLayoutManager(std::make_unique<views::BoxLayout>(
+  auto header_view = std::make_unique<views::View>();
+  header_view->SetLayoutManager(std::make_unique<views::BoxLayout>(
       views::BoxLayout::kVertical, kHeaderViewPadding, 0));
-  header_view_->SetBorder(
+  header_view->SetBorder(
       views::CreateSolidSidedBorder(1, 0, 0, 0, kTopBorderColor));
 
-  views::View* quiet_mode_view = new views::View;
+  auto quiet_mode_view = std::make_unique<views::View>();
 
   auto* quiet_mode_layout =
       quiet_mode_view->SetLayoutManager(std::make_unique<views::BoxLayout>(
           views::BoxLayout::kHorizontal, kQuietModeViewPadding,
           kQuietModeViewSpacing));
 
-  quiet_mode_icon_ = new views::ImageView();
-  quiet_mode_icon_->SetBorder(views::CreateEmptyBorder(kQuietModeLabelPadding));
-  quiet_mode_view->AddChildView(quiet_mode_icon_);
+  auto quiet_mode_icon = std::make_unique<views::ImageView>();
+  quiet_mode_icon->SetBorder(views::CreateEmptyBorder(kQuietModeLabelPadding));
+  quiet_mode_icon_ = quiet_mode_view->AddChildView(std::move(quiet_mode_icon));
 
-  views::Label* quiet_mode_label = new views::Label(l10n_util::GetStringUTF16(
-      IDS_ASH_MESSAGE_CENTER_QUIET_MODE_BUTTON_TOOLTIP));
+  auto quiet_mode_label =
+      std::make_unique<views::Label>(l10n_util::GetStringUTF16(
+          IDS_ASH_MESSAGE_CENTER_QUIET_MODE_BUTTON_TOOLTIP));
   quiet_mode_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
   // "Roboto-Regular, 13sp" is specified in the mock.
   quiet_mode_label->SetFontList(
@@ -448,43 +455,44 @@
   quiet_mode_label->SetEnabledColor(kUnifiedMenuTextColor);
   quiet_mode_label->SetSubpixelRenderingEnabled(false);
   quiet_mode_label->SetBorder(views::CreateEmptyBorder(kQuietModeLabelPadding));
-  quiet_mode_view->AddChildView(quiet_mode_label);
-  quiet_mode_layout->SetFlexForView(quiet_mode_label, 1);
+  auto* quiet_mode_label_ptr =
+      quiet_mode_view->AddChildView(std::move(quiet_mode_label));
+  quiet_mode_layout->SetFlexForView(quiet_mode_label_ptr, 1);
 
-  quiet_mode_toggle_ = new views::ToggleButton(this);
-  quiet_mode_toggle_->SetAccessibleName(l10n_util::GetStringUTF16(
+  auto quiet_mode_toggle = std::make_unique<views::ToggleButton>(this);
+  quiet_mode_toggle->SetAccessibleName(l10n_util::GetStringUTF16(
       IDS_ASH_MESSAGE_CENTER_QUIET_MODE_BUTTON_TOOLTIP));
-  quiet_mode_toggle_->SetBorder(
+  quiet_mode_toggle->SetBorder(
       views::CreateEmptyBorder(kQuietModeTogglePadding));
-  quiet_mode_toggle_->EnableCanvasFlippingForRTLUI(true);
+  quiet_mode_toggle->EnableCanvasFlippingForRTLUI(true);
+  quiet_mode_toggle_ =
+      quiet_mode_view->AddChildView(std::move(quiet_mode_toggle));
   SetQuietModeState(MessageCenter::Get()->IsQuietMode());
-  quiet_mode_view->AddChildView(quiet_mode_toggle_);
-  header_view_->AddChildView(quiet_mode_view);
+  header_view->AddChildView(std::move(quiet_mode_view));
 
-  top_label_ = new views::Label(l10n_util::GetStringUTF16(
+  auto top_label = std::make_unique<views::Label>(l10n_util::GetStringUTF16(
       IDS_ASH_MESSAGE_CENTER_SETTINGS_DIALOG_DESCRIPTION));
-  top_label_->SetBorder(views::CreateEmptyBorder(kTopLabelPadding));
+  top_label->SetBorder(views::CreateEmptyBorder(kTopLabelPadding));
   // "Roboto-Medium, 13sp" is specified in the mock.
-  top_label_->SetFontList(gfx::FontList().Derive(
+  top_label->SetFontList(gfx::FontList().Derive(
       kLabelFontSizeDelta, gfx::Font::NORMAL, gfx::Font::Weight::MEDIUM));
-  top_label_->SetAutoColorReadabilityEnabled(false);
-  top_label_->SetEnabledColor(kUnifiedMenuTextColor);
-  top_label_->SetSubpixelRenderingEnabled(false);
-  top_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-  top_label_->SetMultiLine(true);
-  header_view_->AddChildView(top_label_);
+  top_label->SetAutoColorReadabilityEnabled(false);
+  top_label->SetEnabledColor(kUnifiedMenuTextColor);
+  top_label->SetSubpixelRenderingEnabled(false);
+  top_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+  top_label->SetMultiLine(true);
+  top_label_ = header_view->AddChildView(std::move(top_label));
 
-  AddChildView(header_view_);
+  header_view_ = AddChildView(std::move(header_view));
 
-  scroller_ = new views::ScrollView();
-  scroller_->SetBackgroundColor(SK_ColorTRANSPARENT);
-  scroller_->SetVerticalScrollBar(new views::OverlayScrollBar(false));
-  scroller_->SetHorizontalScrollBar(new views::OverlayScrollBar(true));
-  scroller_->set_draw_overflow_indicator(false);
-  AddChildView(scroller_);
+  auto scroller = std::make_unique<views::ScrollView>();
+  scroller->SetBackgroundColor(SK_ColorTRANSPARENT);
+  scroller->SetVerticalScrollBar(new views::OverlayScrollBar(false));
+  scroller->SetHorizontalScrollBar(new views::OverlayScrollBar(true));
+  scroller->set_draw_overflow_indicator(false);
+  scroller_ = AddChildView(std::move(scroller));
 
-  no_notifiers_view_ = new EmptyNotifierView();
-  AddChildView(no_notifiers_view_);
+  no_notifiers_view_ = AddChildView(std::make_unique<EmptyNotifierView>());
 
   OnNotifierListUpdated({});
   Shell::Get()->message_center_controller()->AddNotifierSettingsListener(this);
diff --git a/ash/system/message_center/notifier_settings_view.h b/ash/system/message_center/notifier_settings_view.h
index 538bb58..169bf23e 100644
--- a/ash/system/message_center/notifier_settings_view.h
+++ b/ash/system/message_center/notifier_settings_view.h
@@ -79,9 +79,9 @@
     void GridChanged();
 
     message_center::NotifierId notifier_id_;
-    views::ImageView* icon_view_;
-    views::Label* name_view_;
-    views::Checkbox* checkbox_;
+    views::ImageView* icon_view_ = nullptr;
+    views::Label* name_view_ = nullptr;
+    views::Checkbox* checkbox_ = nullptr;
 
     DISALLOW_COPY_AND_ASSIGN(NotifierButton);
   };
diff --git a/ash/system/message_center/notifier_settings_view_unittest.cc b/ash/system/message_center/notifier_settings_view_unittest.cc
index 18eb027..ab793e8 100644
--- a/ash/system/message_center/notifier_settings_view_unittest.cc
+++ b/ash/system/message_center/notifier_settings_view_unittest.cc
@@ -38,17 +38,6 @@
   }
 
   // mojom::AshMessageCenterClient:
-  void HandleNotificationClosed(const base::UnguessableToken& token,
-                                bool by_user) override {}
-  void HandleNotificationClicked(const std::string& id) override {}
-  void HandleNotificationButtonClicked(
-      const std::string& id,
-      int button_index,
-      const base::Optional<base::string16>& reply) override {}
-  void HandleNotificationSettingsButtonClicked(const std::string& id) override {
-  }
-  void DisableNotification(const std::string& id) override {}
-
   void SetNotifierEnabled(const NotifierId& notifier_id,
                           bool enabled) override {}
 
diff --git a/ash/system/status_area_widget.cc b/ash/system/status_area_widget.cc
index 6f938c6..255ef29 100644
--- a/ash/system/status_area_widget.cc
+++ b/ash/system/status_area_widget.cc
@@ -4,6 +4,7 @@
 
 #include "ash/system/status_area_widget.h"
 
+#include "ash/keyboard/ui/keyboard_controller.h"
 #include "ash/kiosk_next/kiosk_next_shell_controller_impl.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shelf/shelf.h"
diff --git a/ash/system/toast/toast_overlay.cc b/ash/system/toast/toast_overlay.cc
index 6272550..685dbf4 100644
--- a/ash/system/toast/toast_overlay.cc
+++ b/ash/system/toast/toast_overlay.cc
@@ -4,6 +4,7 @@
 
 #include "ash/system/toast/toast_overlay.h"
 
+#include "ash/keyboard/ui/keyboard_controller.h"
 #include "ash/public/cpp/ash_typography.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/root_window_controller.h"
diff --git a/ash/wm/collision_detection/collision_detection_utils.cc b/ash/wm/collision_detection/collision_detection_utils.cc
index 80928dd..ec69c12 100644
--- a/ash/wm/collision_detection/collision_detection_utils.cc
+++ b/ash/wm/collision_detection/collision_detection_utils.cc
@@ -4,6 +4,7 @@
 
 #include "ash/wm/collision_detection/collision_detection_utils.h"
 
+#include "ash/keyboard/ui/keyboard_controller.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shell.h"
diff --git a/ash/wm/overview/overview_controller.cc b/ash/wm/overview/overview_controller.cc
index 2d037f9b..0f58602 100644
--- a/ash/wm/overview/overview_controller.cc
+++ b/ash/wm/overview/overview_controller.cc
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "ash/app_list/app_list_controller_impl.h"
+#include "ash/keyboard/ui/keyboard_controller.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/root_window_controller.h"
 #include "ash/scoped_animation_disabler.h"
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 65c63b46..7a4ab9a 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -3020,16 +3020,6 @@
 
   # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
   configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
-
-  # Symbols for crashes when running tests on swarming.
-  if (symbol_level > 0) {
-    if (is_win) {
-      data += [ "$root_out_dir/base_unittests.exe.pdb" ]
-    } else if (is_mac) {
-      # TODO(crbug.com/330301): make this conditional on mac_strip_release.
-      # data += [ "$root_out_dir/base_unittests.dSYM/" ]
-    }
-  }
 }
 
 action("build_date") {
diff --git a/build/android/lint/suppressions.xml b/build/android/lint/suppressions.xml
index 03d8749b..24a62ee7 100644
--- a/build/android/lint/suppressions.xml
+++ b/build/android/lint/suppressions.xml
@@ -127,6 +127,8 @@
   </issue>
   <issue id="IconDuplicates" severity="Error">
     <ignore regexp="chromecast/internal"/>
+    <!-- mipmap version is used for app launcher only. drawables used for notifications -->
+    <ignore regexp="chromecast/browser/android/apk/res/mipmap-.*/app_icon.png"/>
   </issue>
   <issue id="IconDuplicatesConfig" severity="Error">
     <ignore regexp="chromecast/internal"/>
diff --git a/build/android/pylib/gtest/gtest_test_instance.py b/build/android/pylib/gtest/gtest_test_instance.py
index d3bedee..3d386861 100644
--- a/build/android/pylib/gtest/gtest_test_instance.py
+++ b/build/android/pylib/gtest/gtest_test_instance.py
@@ -23,8 +23,9 @@
 
 
 BROWSER_TEST_SUITES = [
-  'components_browsertests',
-  'content_browsertests',
+    'android_browsertests',
+    'components_browsertests',
+    'content_browsertests',
 ]
 
 RUN_IN_SUB_THREAD_TEST_SUITES = [
diff --git a/build/config/android/copy_ex.gni b/build/config/android/copy_ex.gni
new file mode 100644
index 0000000..14fbedb
--- /dev/null
+++ b/build/config/android/copy_ex.gni
@@ -0,0 +1,72 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+#
+# Copy a list of file into a destination directory. Potentially renaming
+# files are they are copied. This also ensures that symlinks are followed
+# during the copy (i.e. the symlinks are never copied, only their content).
+#
+# Variables:
+#  dest: Destination directory path.
+#  sources: List of source files or directories to copy to dest.
+#  renaming_sources: Optional list of source file paths that will be renamed
+#    during the copy operation. If provided, renaming_destinations is required.
+#  renaming_destinations: Optional list of destination file paths, required
+#    when renaming_sources is provided. Both lists should have the same size
+#    and matching entries.
+#  args: Optional. Additionnal arguments to the copy_ex.py script.
+#
+#  The following variables have the usual GN meaning: data, deps, inputs,
+#  outputs, testonly, visibility.
+
+import("//build/config/python.gni")
+
+template("copy_ex") {
+  set_sources_assignment_filter([])
+  action_with_pydeps(target_name) {
+    forward_variables_from(invoker,
+                           [
+                             "data",
+                             "deps",
+                             "testonly",
+                             "visibility",
+                           ])
+    sources = []
+    if (defined(invoker.sources)) {
+      sources += invoker.sources
+    }
+    outputs = []
+    if (defined(invoker.outputs)) {
+      outputs += invoker.outputs
+    }
+    if (defined(invoker.inputs)) {
+      inputs = invoker.inputs
+    }
+
+    script = "//build/android/gyp/copy_ex.py"
+
+    args = [
+      "--dest",
+      rebase_path(invoker.dest, root_build_dir),
+    ]
+    rebased_sources = rebase_path(sources, root_build_dir)
+    args += [ "--files=$rebased_sources" ]
+
+    if (defined(invoker.args)) {
+      args += invoker.args
+    }
+
+    if (defined(invoker.renaming_sources) &&
+        defined(invoker.renaming_destinations)) {
+      sources += invoker.renaming_sources
+      renaming_destinations = invoker.renaming_destinations
+      outputs +=
+          get_path_info(rebase_path(renaming_destinations, ".", invoker.dest),
+                        "abspath")
+      rebased_renaming_sources =
+          rebase_path(invoker.renaming_sources, root_build_dir)
+      args += [ "--renaming-sources=$rebased_renaming_sources" ]
+      args += [ "--renaming-destinations=$renaming_destinations" ]
+    }
+  }
+}
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index 010202bd..ebbfca6 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -5,6 +5,7 @@
 # Do not add any imports to non-//build directories here.
 # Some projects (e.g. V8) do not have non-build directories DEPS'ed in.
 import("//build/config/android/config.gni")
+import("//build/config/android/copy_ex.gni")
 import("//build/config/dcheck_always_on.gni")
 import("//build/config/python.gni")
 import("//build/config/sanitizers/sanitizers.gni")
@@ -544,68 +545,6 @@
   }
 }
 
-# Copy a list of file into a destination directory. Potentially renaming
-# files are they are copied. This also ensures that symlinks are followed
-# during the copy (i.e. the symlinks are never copied, only their content).
-#
-# Variables:
-#  dest: Destination directory path.
-#  sources: List of source files or directories to copy to dest.
-#  renaming_sources: Optional list of source file paths that will be renamed
-#    during the copy operation. If provided, renaming_destinations is required.
-#  renaming_destinations: Optional list of destination file paths, required
-#    when renaming_sources is provided. Both lists should have the same size
-#    and matching entries.
-#  args: Optional. Additionnal arguments to the copy_ex.py script.
-#
-#  The following variables have the usual GN meaning: data, deps, inputs,
-#  outputs, testonly, visibility.
-#
-template("copy_ex") {
-  set_sources_assignment_filter([])
-  action_with_pydeps(target_name) {
-    forward_variables_from(invoker,
-                           [
-                             "data",
-                             "deps",
-                             "outputs",
-                             "testonly",
-                             "visibility",
-                           ])
-    sources = []
-    if (defined(invoker.sources)) {
-      sources += invoker.sources
-    }
-    if (defined(invoker.inputs)) {
-      inputs = invoker.inputs
-    }
-
-    script = "//build/android/gyp/copy_ex.py"
-
-    args = [
-      "--dest",
-      rebase_path(invoker.dest, root_build_dir),
-    ]
-    rebased_sources = rebase_path(sources, root_build_dir)
-    args += [ "--files=$rebased_sources" ]
-
-    if (defined(invoker.args)) {
-      args += invoker.args
-    }
-
-    if (defined(invoker.renaming_sources) &&
-        defined(invoker.renaming_destinations)) {
-      sources += invoker.renaming_sources
-      rebased_renaming_sources =
-          rebase_path(invoker.renaming_sources, root_build_dir)
-      args += [ "--renaming-sources=$rebased_renaming_sources" ]
-
-      renaming_destinations = invoker.renaming_destinations
-      args += [ "--renaming-destinations=$renaming_destinations" ]
-    }
-  }
-}
-
 template("generate_android_wrapper") {
   generate_wrapper(target_name) {
     forward_variables_from(invoker, "*")
diff --git a/build/config/fuchsia/testing_sandbox_policy b/build/config/fuchsia/testing_sandbox_policy
index ac8d89f..59796e8 100644
--- a/build/config/fuchsia/testing_sandbox_policy
+++ b/build/config/fuchsia/testing_sandbox_policy
@@ -10,6 +10,7 @@
       "fuchsia.logger.Log",
       "fuchsia.logger.LogSink",
       "fuchsia.media.Audio",
+      "fuchsia.media.drm.WidevineContentDecryptionModule",
       "fuchsia.mediacodec.CodecFactory",
       "fuchsia.net.SocketProvider",
       "fuchsia.netstack.Netstack",
diff --git a/build/toolchain/gcc_toolchain.gni b/build/toolchain/gcc_toolchain.gni
index bfb59b5e..b716154 100644
--- a/build/toolchain/gcc_toolchain.gni
+++ b/build/toolchain/gcc_toolchain.gni
@@ -535,15 +535,6 @@
         unstripped_outfile = "{{root_out_dir}}/exe.unstripped/$exename"
       }
 
-      # Generate a map file to be used for binary size analysis.
-      # Map file adds ~10% to the link time on a z620.
-      # With target_os="android", libchrome.so.map.gz is ~20MB.
-      map_switch = ""
-      if (enable_linker_map && is_official_build) {
-        map_file = "$unstripped_outfile.map.gz"
-        map_switch = " --map-file \"$map_file\""
-      }
-
       start_group_flag = ""
       end_group_flag = ""
       if (current_os != "aix") {
@@ -553,15 +544,28 @@
       }
       link_command = "$ld {{ldflags}}${extra_ldflags} -o \"$unstripped_outfile\" $start_group_flag @\"$rspfile\" {{solibs}} $end_group_flag $libs_section_prefix {{libs}} $libs_section_postfix"
 
-      strip_switch = ""
+      # Generate a map file to be used for binary size analysis.
+      # Map file adds ~10% to the link time on a z620.
+      # With target_os="android", libchrome.so.map.gz is ~20MB.
+      map_switch = ""
+      if (enable_linker_map && is_official_build) {
+        map_file = "$unstripped_outfile.map.gz"
+        map_switch = " --map-file \"$map_file\""
+      }
 
+      strip_switch = ""
       if (defined(invoker.strip)) {
         strip_switch = " --strip=\"${invoker.strip}\" --unstripped-file=\"$unstripped_outfile\""
       }
 
-      link_wrapper =
-          rebase_path("//build/toolchain/gcc_link_wrapper.py", root_build_dir)
-      command = "$python_path \"$link_wrapper\" --output=\"$outfile\"$strip_switch$map_switch -- $link_command"
+      if (strip_switch != "" || map_switch != "") {
+        link_wrapper =
+            rebase_path("//build/toolchain/gcc_link_wrapper.py", root_build_dir)
+        command = "$python_path \"$link_wrapper\" --output=\"$outfile\"$strip_switch$map_switch -- $link_command"
+      } else {
+        command = link_command
+      }
+
       description = "LINK $outfile"
       rspfile_content = "{{inputs}}"
       outputs = [
diff --git a/cc/animation/keyframe_model.cc b/cc/animation/keyframe_model.cc
index d4b89dda..50b3cef 100644
--- a/cc/animation/keyframe_model.cc
+++ b/cc/animation/keyframe_model.cc
@@ -12,6 +12,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/trace_event/trace_event.h"
 #include "cc/animation/animation_curve.h"
+#include "cc/trees/target_property.h"
 
 namespace {
 
@@ -50,9 +51,11 @@
     std::unique_ptr<AnimationCurve> curve,
     int keyframe_model_id,
     int group_id,
-    int target_property_id) {
+    int target_property_id,
+    const std::string& custom_property_name) {
   return base::WrapUnique(new KeyframeModel(std::move(curve), keyframe_model_id,
-                                            group_id, target_property_id));
+                                            group_id, target_property_id,
+                                            custom_property_name));
 }
 
 std::unique_ptr<KeyframeModel> KeyframeModel::CreateImplInstance(
@@ -61,7 +64,8 @@
   // creating multiple controlling instances.
   DCHECK(!is_controlling_instance_);
   std::unique_ptr<KeyframeModel> to_return(
-      new KeyframeModel(curve_->Clone(), id_, group_, target_property_id_));
+      new KeyframeModel(curve_->Clone(), id_, group_, target_property_id_,
+                        custom_property_name_));
   to_return->element_id_ = element_id_;
   to_return->run_state_ = initial_run_state;
   to_return->iterations_ = iterations_;
@@ -81,7 +85,8 @@
 KeyframeModel::KeyframeModel(std::unique_ptr<AnimationCurve> curve,
                              int keyframe_model_id,
                              int group_id,
-                             int target_property_id)
+                             int target_property_id,
+                             const std::string& custom_property_name)
     : curve_(std::move(curve)),
       id_(keyframe_model_id),
       group_(group_id),
@@ -97,7 +102,11 @@
       is_controlling_instance_(false),
       is_impl_only_(false),
       affects_active_elements_(true),
-      affects_pending_elements_(true) {}
+      affects_pending_elements_(true),
+      custom_property_name_(custom_property_name) {
+  DCHECK(custom_property_name_.empty() ||
+         target_property_id_ == TargetProperty::CSS_CUSTOM_PROPERTY);
+}
 
 KeyframeModel::~KeyframeModel() {
   if (run_state_ == RUNNING || run_state_ == PAUSED)
diff --git a/cc/animation/keyframe_model.h b/cc/animation/keyframe_model.h
index 43a22d88..9b14ff9 100644
--- a/cc/animation/keyframe_model.h
+++ b/cc/animation/keyframe_model.h
@@ -57,11 +57,15 @@
 
   enum class Phase { BEFORE, ACTIVE, AFTER };
 
+  // The |custom_property_name| has a default value of an empty string,
+  // indicating that the animated property is a native property. When it is an
+  // animated custom property, it should be the property name.
   static std::unique_ptr<KeyframeModel> Create(
       std::unique_ptr<AnimationCurve> curve,
       int keyframe_model_id,
       int group_id,
-      int target_property_id);
+      int target_property_id,
+      const std::string& custom_property_name = "");
 
   std::unique_ptr<KeyframeModel> CreateImplInstance(
       RunState initial_run_state) const;
@@ -177,11 +181,16 @@
   KeyframeModel::Phase CalculatePhaseForTesting(
       base::TimeDelta local_time) const;
 
+  const std::string& GetCustomPropertyNameForTesting() {
+    return custom_property_name_;
+  }
+
  private:
   KeyframeModel(std::unique_ptr<AnimationCurve> curve,
                 int keyframe_model_id,
                 int group_id,
-                int target_property_id);
+                int target_property_id,
+                const std::string& custom_property_name);
 
   // Return local time for this keyframe model given the absolute monotonic
   // time.
@@ -281,6 +290,10 @@
   // longer affect any elements, and are deleted.
   bool affects_active_elements_;
   bool affects_pending_elements_;
+
+  // Name of the animated custom property. Empty if it is an animated native
+  // property.
+  std::string custom_property_name_;
 };
 
 }  // namespace cc
diff --git a/cc/animation/keyframe_model_unittest.cc b/cc/animation/keyframe_model_unittest.cc
index 5c853eb..b5648c0 100644
--- a/cc/animation/keyframe_model_unittest.cc
+++ b/cc/animation/keyframe_model_unittest.cc
@@ -1384,5 +1384,19 @@
       keyframe_model->ToString());
 }
 
+TEST(KeyframeModelTest, CustomPropertyKeyframe) {
+  std::unique_ptr<KeyframeModel> keyframe_model =
+      KeyframeModel::Create(std::make_unique<FakeFloatAnimationCurve>(1), 1, 1,
+                            TargetProperty::CSS_CUSTOM_PROPERTY, "foo");
+  EXPECT_EQ(keyframe_model->GetCustomPropertyNameForTesting(), "foo");
+}
+
+TEST(KeyframeModelTest, NonCustomPropertyKeyframe) {
+  std::unique_ptr<KeyframeModel> keyframe_model =
+      KeyframeModel::Create(std::make_unique<FakeFloatAnimationCurve>(1), 1, 1,
+                            TargetProperty::TRANSFORM);
+  EXPECT_EQ(keyframe_model->GetCustomPropertyNameForTesting(), "");
+}
+
 }  // namespace
 }  // namespace cc
diff --git a/cc/mojo_embedder/async_layer_tree_frame_sink.cc b/cc/mojo_embedder/async_layer_tree_frame_sink.cc
index f34b638..0b40b20 100644
--- a/cc/mojo_embedder/async_layer_tree_frame_sink.cc
+++ b/cc/mojo_embedder/async_layer_tree_frame_sink.cc
@@ -319,9 +319,10 @@
 
 void AsyncLayerTreeFrameSink::OnBeginFrame(
     const viz::BeginFrameArgs& args,
-    const viz::PresentationFeedbackMap& feedbacks) {
-  for (const auto& pair : feedbacks) {
-    client_->DidPresentCompositorFrame(pair.first, pair.second);
+    const viz::FrameTimingDetailsMap& timing_details) {
+  for (const auto& pair : timing_details) {
+    client_->DidPresentCompositorFrame(pair.first,
+                                       pair.second.presentation_feedback);
   }
 
   DCHECK_LE(pipeline_reporting_frame_times_.size(), 25u);
diff --git a/cc/mojo_embedder/async_layer_tree_frame_sink.h b/cc/mojo_embedder/async_layer_tree_frame_sink.h
index 95d4c6d..12323d4 100644
--- a/cc/mojo_embedder/async_layer_tree_frame_sink.h
+++ b/cc/mojo_embedder/async_layer_tree_frame_sink.h
@@ -15,8 +15,8 @@
 #include "cc/mojo_embedder/mojo_embedder_export.h"
 #include "cc/trees/layer_tree_frame_sink.h"
 #include "components/viz/common/frame_sinks/begin_frame_source.h"
+#include "components/viz/common/frame_timing_details_map.h"
 #include "components/viz/common/gpu/context_provider.h"
-#include "components/viz/common/presentation_feedback_map.h"
 #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
 #include "components/viz/common/surfaces/surface_id.h"
 #include "mojo/public/cpp/bindings/binding.h"
@@ -138,7 +138,7 @@
   void DidReceiveCompositorFrameAck(
       const std::vector<viz::ReturnedResource>& resources) override;
   void OnBeginFrame(const viz::BeginFrameArgs& begin_frame_args,
-                    const viz::PresentationFeedbackMap& feedbacks) override;
+                    const viz::FrameTimingDetailsMap& timing_details) override;
   void OnBeginFramePausedChanged(bool paused) override;
   void ReclaimResources(
       const std::vector<viz::ReturnedResource>& resources) override;
diff --git a/cc/test/test_layer_tree_frame_sink.cc b/cc/test/test_layer_tree_frame_sink.cc
index da1736f..b1afacb 100644
--- a/cc/test/test_layer_tree_frame_sink.cc
+++ b/cc/test/test_layer_tree_frame_sink.cc
@@ -230,9 +230,10 @@
 
 void TestLayerTreeFrameSink::OnBeginFrame(
     const viz::BeginFrameArgs& args,
-    const viz::PresentationFeedbackMap& feedbacks) {
-  for (const auto& pair : feedbacks)
-    client_->DidPresentCompositorFrame(pair.first, pair.second);
+    const viz::FrameTimingDetailsMap& timing_details) {
+  for (const auto& pair : timing_details)
+    client_->DidPresentCompositorFrame(pair.first,
+                                       pair.second.presentation_feedback);
   external_begin_frame_source_.OnBeginFrame(args);
 }
 
diff --git a/cc/test/test_layer_tree_frame_sink.h b/cc/test/test_layer_tree_frame_sink.h
index fe43725..5aecbc4 100644
--- a/cc/test/test_layer_tree_frame_sink.h
+++ b/cc/test/test_layer_tree_frame_sink.h
@@ -10,7 +10,7 @@
 #include "cc/trees/layer_tree_frame_sink.h"
 #include "components/viz/common/display/renderer_settings.h"
 #include "components/viz/common/frame_sinks/begin_frame_source.h"
-#include "components/viz/common/presentation_feedback_map.h"
+#include "components/viz/common/frame_timing_details_map.h"
 #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
 #include "components/viz/service/display/display.h"
 #include "components/viz/service/display/display_client.h"
@@ -102,7 +102,7 @@
   void DidReceiveCompositorFrameAck(
       const std::vector<viz::ReturnedResource>& resources) override;
   void OnBeginFrame(const viz::BeginFrameArgs& args,
-                    const viz::PresentationFeedbackMap& feedbacks) override;
+                    const viz::FrameTimingDetailsMap& timing_details) override;
   void ReclaimResources(
       const std::vector<viz::ReturnedResource>& resources) override;
   void OnBeginFramePausedChanged(bool paused) override;
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 11344f4..0269b91 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -49,6 +49,7 @@
   "java/src/org/chromium/chrome/browser/KeyboardShortcuts.java",
   "java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java",
   "java/src/org/chromium/chrome/browser/LauncherShortcutActivity.java",
+  "java/src/org/chromium/chrome/browser/MenuOrKeyboardActionController.java",
   "java/src/org/chromium/chrome/browser/NavigationPopup.java",
   "java/src/org/chromium/chrome/browser/NearOomMonitor.java",
   "java/src/org/chromium/chrome/browser/PowerBroadcastReceiver.java",
@@ -1205,8 +1206,10 @@
   "java/src/org/chromium/chrome/browser/preferences/AboutChromePreferences.java",
   "java/src/org/chromium/chrome/browser/preferences/AccessibilityPreferences.java",
   "java/src/org/chromium/chrome/browser/preferences/ButtonPreference.java",
+  "java/src/org/chromium/chrome/browser/preferences/ButtonPreferenceCompat.java",
   "java/src/org/chromium/chrome/browser/preferences/ChromeBaseCheckBoxPreference.java",
   "java/src/org/chromium/chrome/browser/preferences/ChromeBaseListPreference.java",
+  "java/src/org/chromium/chrome/browser/preferences/ChromeBaseListPreferenceCompat.java",
   "java/src/org/chromium/chrome/browser/preferences/ChromeBasePreference.java",
   "java/src/org/chromium/chrome/browser/preferences/ChromeBasePreferenceCompat.java",
   "java/src/org/chromium/chrome/browser/preferences/ChromeImageViewPreference.java",
diff --git a/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_button_filled.xml b/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_button_filled.xml
index 3b56107..233aa12 100644
--- a/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_button_filled.xml
+++ b/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_button_filled.xml
@@ -7,11 +7,12 @@
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:minHeight="@dimen/autofill_assistant_button_height"
+    android:minHeight="@dimen/min_touch_target_size"
     app:chipColor="@color/filled_button_bg"
     app:rippleColor="@color/filled_button_ripple_color"
     app:cornerRadius="@dimen/autofill_assistant_button_corner_radius"
     app:primaryTextAppearance="@style/TextAppearance.WhiteButtonText"
     app:iconWidth="24dp"
     app:iconHeight="24dp"
-    app:chipBorderWidth="@dimen/autofill_assistant_filled_button_border_width"/>
+    app:chipBorderWidth="@dimen/autofill_assistant_filled_button_border_width"
+    app:verticalInset="@dimen/autofill_assistant_button_bg_vertical_inset" />
diff --git a/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_button_hairline.xml b/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_button_hairline.xml
index 11af3b35..341d8bce4 100644
--- a/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_button_hairline.xml
+++ b/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_button_hairline.xml
@@ -7,10 +7,11 @@
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:minHeight="@dimen/autofill_assistant_button_height"
+    android:minHeight="@dimen/min_touch_target_size"
     style="@style/AssistiveChip"
     app:chipColor="@color/default_text_color_inverse"
     app:cornerRadius="@dimen/autofill_assistant_button_corner_radius"
     app:iconWidth="24dp"
-    app:iconHeight="24dp"/>
+    app:iconHeight="24dp"
+    app:verticalInset="@dimen/autofill_assistant_button_bg_vertical_inset" />
 
diff --git a/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_onboarding.xml b/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_onboarding.xml
index b5e7eae..29f09ae 100644
--- a/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_onboarding.xml
+++ b/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_onboarding.xml
@@ -103,7 +103,6 @@
         android:id="@+id/button_init_not_ok"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:minHeight="36dp"
         android:minWidth="120dp"
         android:singleLine="true"
         android:gravity="center"
@@ -120,7 +119,6 @@
         android:id="@+id/button_init_ok"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:minHeight="36dp"
         android:minWidth="120dp"
         android:singleLine="true"
         android:gravity="center"
diff --git a/chrome/android/features/autofill_assistant/java/res/values-v17/attrs.xml b/chrome/android/features/autofill_assistant/java/res/values-v17/attrs.xml
index c6abaad..58829db 100644
--- a/chrome/android/features/autofill_assistant/java/res/values-v17/attrs.xml
+++ b/chrome/android/features/autofill_assistant/java/res/values-v17/attrs.xml
@@ -31,5 +31,6 @@
         <attr name="secondaryTextAppearance" format="reference"/>
         <attr name="rippleColor"/>
         <attr name="chipBorderWidth" format="reference"/>
+        <attr name="verticalInset"/>
     </declare-styleable>
 </resources>
\ No newline at end of file
diff --git a/chrome/android/features/autofill_assistant/java/res/values-v17/dimens.xml b/chrome/android/features/autofill_assistant/java/res/values-v17/dimens.xml
index 8000fd3..216ec02 100644
--- a/chrome/android/features/autofill_assistant/java/res/values-v17/dimens.xml
+++ b/chrome/android/features/autofill_assistant/java/res/values-v17/dimens.xml
@@ -11,8 +11,10 @@
     <dimen name="autofill_assistant_info_box_spacing">16dp</dimen>
     <dimen name="autofill_assistant_poodle_view_size">34dp</dimen>
     <dimen name="autofill_assistant_filled_button_border_width">0dp</dimen>
-    <dimen name="autofill_assistant_button_height">40dp</dimen>
     <dimen name="autofill_assistant_button_corner_radius">40dp</dimen>
+    <!-- Sets drawable inset to 4dp so that min height of the button background drawable is 40dp
+         while maintaining a 48dp min height of the button. -->
+    <dimen name="autofill_assistant_button_bg_vertical_inset">4dp</dimen>
     <dimen name="autofill_assistant_suggestions_spacing">16dp</dimen>
     <dimen name="autofill_assistant_actions_spacing">8dp</dimen>
     <dimen name="autofill_assistant_actions_shadow_width">8dp</dimen>
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantActionsCarouselCoordinator.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantActionsCarouselCoordinator.java
index 5d8b7f6..aa68516 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantActionsCarouselCoordinator.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantActionsCarouselCoordinator.java
@@ -52,8 +52,7 @@
         // height of the view. We add the sheet vertical spacing twice as the item decoration will
         // add this space above and below each chip.
         mView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
-                context.getResources().getDimensionPixelSize(
-                        R.dimen.autofill_assistant_button_height)
+                context.getResources().getDimensionPixelSize(R.dimen.min_touch_target_size)
                         + 2
                                 * context.getResources().getDimensionPixelSize(
                                         org.chromium.chrome.autofill_assistant.R.dimen
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/ButtonView.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/ButtonView.java
index 8a07831..172737d 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/ButtonView.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/ButtonView.java
@@ -78,6 +78,9 @@
                 org.chromium.chrome.R.style.TextAppearance_ChipText);
         int borderWidth = a.getResourceId(R.styleable.ButtonView_chipBorderWidth,
                 org.chromium.chrome.R.dimen.chip_border_width);
+        int verticalInset = a.getDimensionPixelSize(R.styleable.ButtonView_verticalInset,
+                getResources().getDimensionPixelSize(
+                        org.chromium.ui.R.dimen.chip_bg_vertical_inset));
         a.recycle();
 
         mIcon = new ChromeImageView(getContext());
@@ -94,8 +97,9 @@
         setPrimaryTextMargins(4);
 
         // Reset icon and background:
-        mRippleBackgroundHelper = new RippleBackgroundHelper(this, chipColorId, rippleColorId,
-                cornerRadius, org.chromium.chrome.R.color.chip_stroke_color, borderWidth);
+        mRippleBackgroundHelper =
+                new RippleBackgroundHelper(this, chipColorId, rippleColorId, cornerRadius,
+                        org.chromium.chrome.R.color.chip_stroke_color, borderWidth, verticalInset);
         setIcon(INVALID_ICON_ID, false);
     }
 
diff --git a/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_action.xml b/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_action.xml
index da345ac..da7a6d2 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_action.xml
+++ b/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_action.xml
@@ -6,11 +6,9 @@
 <org.chromium.ui.widget.ButtonCompat
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:gravity="center"
-    android:layout_height="@dimen/keyboard_accessory_bar_item_height"
+    android:layout_height="wrap_content"
     android:layout_width="wrap_content"
     android:paddingEnd="@dimen/keyboard_accessory_suggestion_padding"
     android:paddingStart="@dimen/keyboard_accessory_suggestion_padding"
-    android:layout_marginBottom="@dimen/keyboard_accessory_half_padding"
-    android:layout_marginTop="@dimen/keyboard_accessory_half_padding"
     android:textAlignment="center"
     style="@style/FilledButton.Flat" />
diff --git a/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_action_modern.xml b/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_action_modern.xml
index 918d9c9..31a8353 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_action_modern.xml
+++ b/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_action_modern.xml
@@ -6,14 +6,11 @@
 <org.chromium.ui.widget.ButtonCompat
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:gravity="center"
-    android:layout_height="@dimen/keyboard_accessory_bar_item_height"
+    android:layout_height="wrap_content"
     android:layout_width="wrap_content"
-    android:minHeight="0dp"
     android:minWidth="0dp"
     android:paddingEnd="@dimen/keyboard_accessory_suggestion_padding"
     android:paddingStart="@dimen/keyboard_accessory_suggestion_padding"
-    android:layout_marginBottom="@dimen/keyboard_accessory_half_padding"
-    android:layout_marginTop="@dimen/keyboard_accessory_half_padding"
     android:layout_marginEnd="@dimen/keyboard_accessory_bar_item_padding"
     android:textAlignment="center"
     style="@style/FilledButton.Flat" />
diff --git a/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_chip.xml b/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_chip.xml
index 92a1b80e0..97e1545 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_chip.xml
+++ b/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_chip.xml
@@ -13,11 +13,11 @@
     android:minHeight="0dp"
     android:minWidth="0dp"
     android:paddingBottom="0dp"
-    android:paddingEnd="@dimen/keyboard_accessory_half_padding"
-    android:paddingStart="@dimen/keyboard_accessory_half_padding"
+    android:paddingEnd="@dimen/keyboard_accessory_horizontal_padding"
+    android:paddingStart="@dimen/keyboard_accessory_horizontal_padding"
     android:paddingTop="0dp"
-    android:layout_marginBottom="@dimen/keyboard_accessory_half_padding"
-    android:layout_marginTop="@dimen/keyboard_accessory_half_padding"
+    android:layout_marginBottom="@dimen/keyboard_accessory_chip_vertical_margin"
+    android:layout_marginTop="@dimen/keyboard_accessory_chip_vertical_margin"
     android:elevation="2dp"
     android:textAppearance="@style/TextAppearance.BlackTitle2"
     android:background="@drawable/autofill_chip_inset"/>
diff --git a/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_modern.xml b/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_modern.xml
index 9f4ab02..ae51e39 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_modern.xml
+++ b/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_modern.xml
@@ -30,7 +30,7 @@
     <LinearLayout
         android:id="@+id/accessory_bar_contents"
         android:layout_width="match_parent"
-        android:minHeight="@dimen/keyboard_accessory_bar_item_height"
+        android:minHeight="@dimen/keyboard_accessory_height"
         android:layout_height="wrap_content"
         android:layout_gravity="start|bottom"
         android:orientation="horizontal"
diff --git a/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_sheet_tab_address_info.xml b/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_sheet_tab_address_info.xml
index 25b1ddf1..fcac328 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_sheet_tab_address_info.xml
+++ b/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_sheet_tab_address_info.xml
@@ -12,15 +12,13 @@
     android:paddingStart="@dimen/keyboard_accessory_suggestion_padding"
     android:paddingEnd="@dimen/keyboard_accessory_suggestion_padding"
     android:layout_marginTop="@dimen/keyboard_accessory_sheet_top_margin"
-    android:layout_marginBottom="@dimen/keyboard_accessory_sheet_padding"
+    android:layout_marginBottom="@dimen/keyboard_accessory_sheet_bottom_margin"
     android:orientation="vertical">
 
     <LinearLayout
         android:gravity="center_vertical|start"
         android:fillViewport="true"
         android:layout_height="@dimen/keyboard_accessory_suggestion_height"
-        android:paddingTop="@dimen/keyboard_accessory_sheet_padding"
-        android:paddingBottom="@dimen/keyboard_accessory_sheet_padding"
         android:layout_width="match_parent"
         android:orientation="horizontal">
 
@@ -50,8 +48,6 @@
     <org.chromium.ui.widget.ChipView
         android:id="@+id/company_name"
         android:gravity="center_vertical|start"
-        android:layout_marginTop="@dimen/keyboard_accessory_sheet_padding"
-        android:layout_marginBottom="@dimen/keyboard_accessory_sheet_padding"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         style="@style/InputChip" />
@@ -59,8 +55,6 @@
     <org.chromium.ui.widget.ChipView
         android:id="@+id/address_home_line_1"
         android:gravity="center_vertical|start"
-        android:layout_marginTop="@dimen/keyboard_accessory_sheet_padding"
-        android:layout_marginBottom="@dimen/keyboard_accessory_sheet_padding"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         style="@style/InputChip" />
@@ -68,8 +62,6 @@
     <org.chromium.ui.widget.ChipView
         android:id="@+id/address_home_line_2"
         android:gravity="center_vertical|start"
-        android:layout_marginTop="@dimen/keyboard_accessory_sheet_padding"
-        android:layout_marginBottom="@dimen/keyboard_accessory_sheet_padding"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         style="@style/InputChip" />
@@ -78,8 +70,6 @@
         android:gravity="center_vertical|start"
         android:fillViewport="true"
         android:layout_height="@dimen/keyboard_accessory_suggestion_height"
-        android:paddingTop="@dimen/keyboard_accessory_sheet_padding"
-        android:paddingBottom="@dimen/keyboard_accessory_sheet_padding"
         android:layout_width="match_parent"
         android:orientation="horizontal">
 
@@ -116,8 +106,6 @@
     <org.chromium.ui.widget.ChipView
         android:id="@+id/phone_home_whole_number"
         android:gravity="center_vertical|start"
-        android:layout_marginTop="@dimen/keyboard_accessory_sheet_padding"
-        android:layout_marginBottom="@dimen/keyboard_accessory_sheet_padding"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         style="@style/InputChip" />
@@ -125,8 +113,6 @@
     <org.chromium.ui.widget.ChipView
         android:id="@+id/email_address"
         android:gravity="center_vertical|start"
-        android:layout_marginTop="@dimen/keyboard_accessory_sheet_padding"
-        android:layout_marginBottom="@dimen/keyboard_accessory_sheet_padding"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         style="@style/InputChip" />
diff --git a/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_sheet_tab_credit_card_info.xml b/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_sheet_tab_credit_card_info.xml
index 7da04b9..ee24da8d 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_sheet_tab_credit_card_info.xml
+++ b/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_sheet_tab_credit_card_info.xml
@@ -13,15 +13,13 @@
     android:paddingStart="@dimen/keyboard_accessory_suggestion_padding"
     android:paddingEnd="@dimen/keyboard_accessory_suggestion_padding"
     android:layout_marginTop="@dimen/keyboard_accessory_sheet_top_margin"
-    android:layout_marginBottom="@dimen/keyboard_accessory_sheet_padding"
+    android:layout_marginBottom="@dimen/keyboard_accessory_sheet_bottom_margin"
     android:orientation="horizontal">
 
     <LinearLayout
         android:gravity="center_vertical|start"
         android:fillViewport="true"
         android:layout_height="wrap_content"
-        android:paddingTop="@dimen/keyboard_accessory_sheet_padding"
-        android:paddingBottom="@dimen/keyboard_accessory_sheet_padding"
         android:layout_width="0dp"
         android:layout_weight="1"
         android:orientation="vertical">
@@ -37,8 +35,6 @@
             android:gravity="center_vertical|start"
             android:fillViewport="true"
             android:layout_height="@dimen/keyboard_accessory_suggestion_height"
-            android:paddingTop="@dimen/keyboard_accessory_sheet_padding"
-            android:paddingBottom="@dimen/keyboard_accessory_sheet_padding"
             android:layout_width="match_parent"
             android:orientation="horizontal">
 
diff --git a/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_sheet_tab_password_info.xml b/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_sheet_tab_password_info.xml
index 053f28bd..87ad1f8 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_sheet_tab_password_info.xml
+++ b/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_sheet_tab_password_info.xml
@@ -12,7 +12,7 @@
     android:paddingStart="@dimen/keyboard_accessory_suggestion_padding"
     android:paddingEnd="@dimen/keyboard_accessory_suggestion_padding"
     android:layout_marginTop="@dimen/keyboard_accessory_sheet_top_margin"
-    android:layout_marginBottom="@dimen/keyboard_accessory_sheet_padding"
+    android:layout_marginBottom="@dimen/keyboard_accessory_sheet_bottom_margin"
     android:orientation="vertical">
 
 
@@ -20,8 +20,6 @@
         android:gravity="center_vertical|start"
         android:fillViewport="true"
         android:layout_height="@dimen/keyboard_accessory_suggestion_height"
-        android:paddingTop="@dimen/keyboard_accessory_sheet_padding"
-        android:paddingBottom="@dimen/keyboard_accessory_sheet_padding"
         android:layout_width="match_parent"
         android:orientation="horizontal">
 
@@ -51,8 +49,6 @@
         android:gravity="center_vertical|start"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginTop="@dimen/keyboard_accessory_sheet_padding"
-        android:layout_marginBottom="@dimen/keyboard_accessory_sheet_padding"
         style="@style/InputChip" />
 
 </org.chromium.chrome.browser.keyboard_accessory.sheet_tabs.PasswordAccessoryInfoView>
diff --git a/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_suggestion.xml b/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_suggestion.xml
index 84f19bb..0012c95 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_suggestion.xml
+++ b/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_suggestion.xml
@@ -9,7 +9,5 @@
     android:gravity="center"
     android:layout_height="wrap_content"
     android:layout_width="wrap_content"
-    android:layout_marginBottom="@dimen/keyboard_accessory_suggestion_top_bottom_margin"
-    android:layout_marginTop="@dimen/keyboard_accessory_suggestion_top_bottom_margin"
     app:iconWidth="@dimen/keyboard_accessory_bar_item_cc_icon_width"
     style="@style/AssistiveChip" />
diff --git a/chrome/android/features/keyboard_accessory/internal/java/res/values/dimens.xml b/chrome/android/features/keyboard_accessory/internal/java/res/values/dimens.xml
index 4e9b898..39fa865 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/res/values/dimens.xml
+++ b/chrome/android/features/keyboard_accessory/internal/java/res/values/dimens.xml
@@ -7,16 +7,17 @@
     <!-- Autofill keyboard accessory dimensions -->
     <dimen name="keyboard_accessory_bar_item_height">36dp</dimen>
     <dimen name="keyboard_accessory_bar_item_padding">8dp</dimen>
-    <dimen name="keyboard_accessory_half_padding">6dp</dimen>
+    <dimen name="keyboard_accessory_horizontal_padding">6dp</dimen>
     <dimen name="keyboard_accessory_height">48dp</dimen>
     <!--dimen name="keyboard_accessory_height_with_shadow">56dp</dimen-->
     <dimen name="keyboard_accessory_image_top_padding">12dp</dimen>
     <dimen name="keyboard_accessory_shadow">5dp</dimen>
+    <dimen name="keyboard_accessory_chip_vertical_margin">8dp</dimen>
     <!--dimen name="keyboard_accessory_sheet_height">330dp</dimen-->
     <dimen name="keyboard_accessory_sheet_padding">8dp</dimen>
     <dimen name="keyboard_accessory_sheet_top_margin">16dp</dimen>
+    <dimen name="keyboard_accessory_sheet_bottom_margin">8dp</dimen>
     <dimen name="keyboard_accessory_suggestion_padding">16dp</dimen>
-    <dimen name="keyboard_accessory_suggestion_top_bottom_margin">8dp</dimen>
     <dimen name="keyboard_accessory_suggestion_offset">12dp</dimen>
     <dimen name="keyboard_accessory_suggestion_height">48dp</dimen>
     <dimen name="keyboard_accessory_suggestion_icon_size">20dp</dimen>
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryView.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryView.java
index 3ba0672..8d88420 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryView.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryView.java
@@ -122,7 +122,8 @@
         recyclerView.setLayoutManager(
                 new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false));
 
-        int pad = getResources().getDimensionPixelSize(R.dimen.keyboard_accessory_half_padding);
+        int pad =
+                getResources().getDimensionPixelSize(R.dimen.keyboard_accessory_horizontal_padding);
         // Create margins between every element.
         if (!ChromeFeatureList.isEnabled(ChromeFeatureList.AUTOFILL_KEYBOARD_ACCESSORY)) {
             recyclerView.addItemDecoration(new HorizontalDividerItemDecoration(pad));
diff --git a/chrome/android/features/tab_ui/BUILD.gn b/chrome/android/features/tab_ui/BUILD.gn
index e813b76..065d4b578 100644
--- a/chrome/android/features/tab_ui/BUILD.gn
+++ b/chrome/android/features/tab_ui/BUILD.gn
@@ -53,6 +53,7 @@
     "java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegateImpl.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/TabProperties.java",
+    "java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorCoordinator.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripToolbarCoordinator.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripToolbarViewProperties.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripViewBinder.java",
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 9651292..c0019425 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
@@ -10,6 +10,7 @@
 import android.support.annotation.Nullable;
 
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.MenuOrKeyboardActionController;
 import org.chromium.chrome.browser.compositor.CompositorViewHolder;
 import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
@@ -36,21 +37,37 @@
     final static String COMPONENT_NAME = "GridTabSwitcher";
     private final PropertyModelChangeProcessor mContainerViewChangeProcessor;
     private final ActivityLifecycleDispatcher mLifecycleDispatcher;
+    private final MenuOrKeyboardActionController mMenuOrKeyboardActionController;
     private final TabListCoordinator mTabGridCoordinator;
     private final GridTabSwitcherMediator mMediator;
     private final MultiThumbnailCardProvider mMultiThumbnailCardProvider;
     private final TabGridDialogCoordinator mTabGridDialogCoordinator;
+    private final TabSelectionEditorCoordinator mTabSelectionEditorCoordinator;
+
+    private final MenuOrKeyboardActionController
+            .MenuOrKeyboardActionHandler mGridTabSwitcherMenuActionHandler =
+            new MenuOrKeyboardActionController.MenuOrKeyboardActionHandler() {
+                @Override
+                public boolean onMenuOrKeyboardAction(int id, boolean fromMenu) {
+                    if (id == R.id.menu_group_tabs) {
+                        mTabSelectionEditorCoordinator.getController().show();
+                        return true;
+                    }
+                    return false;
+                }
+            };
 
     public GridTabSwitcherCoordinator(Context context,
             ActivityLifecycleDispatcher lifecycleDispatcher, TabModelSelector tabModelSelector,
             TabContentManager tabContentManager, CompositorViewHolder compositorViewHolder,
             ChromeFullscreenManager fullscreenManager, TabCreatorManager tabCreatorManager,
-            Runnable backPress) {
+            MenuOrKeyboardActionController menuOrKeyboardActionController, Runnable backPress) {
         PropertyModel containerViewModel = new PropertyModel(TabListContainerProperties.ALL_KEYS);
         TabListMediator.GridCardOnClickListenerProvider gridCardOnClickListenerProvider;
         if (FeatureUtilities.isTabGroupsAndroidUiImprovementsEnabled()) {
             mTabGridDialogCoordinator = new TabGridDialogCoordinator(context, tabModelSelector,
-                    tabContentManager, tabCreatorManager, new CompositorViewHolder(context), this);
+                    tabContentManager, tabCreatorManager, new CompositorViewHolder(context), this,
+                    this::getTabGridCardPosition);
 
             mMediator = new GridTabSwitcherMediator(this, containerViewModel, tabModelSelector,
                     fullscreenManager, compositorViewHolder,
@@ -93,6 +110,13 @@
         mContainerViewChangeProcessor = PropertyModelChangeProcessor.create(containerViewModel,
                 mTabGridCoordinator.getContainerView(), TabGridContainerViewBinder::bind);
 
+        mTabSelectionEditorCoordinator = new TabSelectionEditorCoordinator(
+                context, compositorViewHolder, tabModelSelector, tabContentManager);
+
+        mMenuOrKeyboardActionController = menuOrKeyboardActionController;
+        mMenuOrKeyboardActionController.registerMenuOrKeyboardActionHandler(
+                mGridTabSwitcherMenuActionHandler);
+
         mLifecycleDispatcher = lifecycleDispatcher;
         mLifecycleDispatcher.register(this);
     }
@@ -146,6 +170,10 @@
         return mTabGridCoordinator.resetWithListOfTabs(tabs, quickMode);
     }
 
+    private Rect getTabGridCardPosition(int index) {
+        return mTabGridCoordinator.getContainerView().getTabPosition(index);
+    }
+
     @Override
     public void softCleanup() {
         mTabGridCoordinator.softCleanup();
@@ -154,6 +182,8 @@
     // ResetHandler implementation.
     @Override
     public void destroy() {
+        mMenuOrKeyboardActionController.unregisterMenuOrKeyboardActionHandler(
+                mGridTabSwitcherMenuActionHandler);
         mTabGridCoordinator.destroy();
         mContainerViewChangeProcessor.destroy();
         mMediator.destroy();
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java
index ab644b2..7745039 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java
@@ -34,7 +34,8 @@
     TabGridDialogCoordinator(Context context, TabModelSelector tabModelSelector,
             TabContentManager tabContentManager, TabCreatorManager tabCreatorManager,
             CompositorViewHolder compositorViewHolder,
-            GridTabSwitcherMediator.ResetHandler resetHandler) {
+            GridTabSwitcherMediator.ResetHandler resetHandler,
+            TabGridDialogMediator.AnimationOriginProvider animationOriginProvider) {
         mContext = context;
 
         mToolbarPropertyModel = new PropertyModel(TabGridSheetProperties.ALL_KEYS);
@@ -44,8 +45,9 @@
                 null, compositorViewHolder, null, false, R.layout.tab_list_recycler_view_layout,
                 COMPONENT_NAME);
 
-        mMediator = new TabGridDialogMediator(context, this::resetWithListOfTabs,
-                mToolbarPropertyModel, tabModelSelector, tabCreatorManager, resetHandler);
+        mMediator =
+                new TabGridDialogMediator(context, this::resetWithListOfTabs, mToolbarPropertyModel,
+                        tabModelSelector, tabCreatorManager, resetHandler, animationOriginProvider);
 
         mParentLayout = new TabGridDialogParent(context, compositorViewHolder);
     }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
index 79ad0ba..f70cea2c 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.tasks.tab_management;
 
 import android.content.Context;
+import android.graphics.Rect;
 import android.support.annotation.Nullable;
 import android.view.View;
 
@@ -14,7 +15,9 @@
 import org.chromium.chrome.browser.tabmodel.TabLaunchType;
 import org.chromium.chrome.browser.tabmodel.TabModelObserver;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
+import org.chromium.chrome.browser.tabmodel.TabModelUtils;
 import org.chromium.chrome.browser.tabmodel.TabSelectionType;
+import org.chromium.chrome.browser.tasks.tabgroup.TabGroupModelFilter;
 import org.chromium.chrome.browser.util.UrlConstants;
 import org.chromium.chrome.browser.widget.ScrimView;
 import org.chromium.content_public.browser.LoadUrlParams;
@@ -41,6 +44,21 @@
         void resetWithListOfTabs(@Nullable List<Tab> tabs);
     }
 
+    /**
+     * Defines an interface for a {@link TabGridDialogMediator} to get the position on the screen
+     * of the tab grid card related to TabGridDialog in order to prepare animation.
+     */
+    interface AnimationOriginProvider {
+        /**
+         * Provide position of a tab card in GridTabSwitcher as the originate position of a
+         * animation.
+         *
+         * @param index Index in GridTabSwitcher of the tab whose position is requested.
+         * @return  A {@link Rect} that contains position information of the tab card.
+         */
+        Rect getAnimationOriginRect(int index);
+    }
+
     private final Context mContext;
     private final PropertyModel mModel;
     private final TabModelSelector mTabModelSelector;
@@ -48,18 +66,21 @@
     private final TabCreatorManager mTabCreatorManager;
     private final TabGridDialogMediator.ResetHandler mDialogResetHandler;
     private final GridTabSwitcherMediator.ResetHandler mGridTabSwitcherResetHandler;
+    private final AnimationOriginProvider mAnimationOriginProvider;
     private int mCurrentTabId = Tab.INVALID_TAB_ID;
 
     TabGridDialogMediator(Context context, TabGridDialogMediator.ResetHandler dialogResetHandler,
             PropertyModel model, TabModelSelector tabModelSelector,
             TabCreatorManager tabCreatorManager,
-            GridTabSwitcherMediator.ResetHandler gridTabSwitcherResetHandler) {
+            GridTabSwitcherMediator.ResetHandler gridTabSwitcherResetHandler,
+            AnimationOriginProvider animationOriginProvider) {
         mContext = context;
         mModel = model;
         mTabModelSelector = tabModelSelector;
         mTabCreatorManager = tabCreatorManager;
         mDialogResetHandler = dialogResetHandler;
         mGridTabSwitcherResetHandler = gridTabSwitcherResetHandler;
+        mAnimationOriginProvider = animationOriginProvider;
 
         // Register for tab model.
         mTabModelObserver = new EmptyTabModelObserver() {
@@ -78,6 +99,8 @@
             @Override
             public void didSelectTab(Tab tab, int type, int lastId) {
                 if (type == TabSelectionType.FROM_USER)
+                    // Cancel the zooming into tab grid card animation.
+                    mModel.set(TabGridSheetProperties.ANIMATION_SOURCE_RECT, null);
                     mModel.set(TabGridSheetProperties.IS_DIALOG_VISIBLE, false);
             }
 
@@ -106,7 +129,14 @@
     void onReset(Integer tabId) {
         if (tabId != null) {
             mCurrentTabId = tabId;
+            TabGroupModelFilter filter =
+                    (TabGroupModelFilter) mTabModelSelector.getTabModelFilterProvider()
+                            .getCurrentTabModelFilter();
+            int index = filter.indexOf(
+                    TabModelUtils.getTabById(mTabModelSelector.getCurrentModel(), tabId));
+            Rect rect = mAnimationOriginProvider.getAnimationOriginRect(index);
             updateDialog();
+            mModel.set(TabGridSheetProperties.ANIMATION_SOURCE_RECT, rect);
             mModel.set(TabGridSheetProperties.IS_DIALOG_VISIBLE, true);
         } else {
             mModel.set(TabGridSheetProperties.IS_DIALOG_VISIBLE, false);
@@ -169,6 +199,8 @@
 
     private View.OnClickListener getAddButtonClickListener() {
         return view -> {
+            mModel.set(TabGridSheetProperties.ANIMATION_SOURCE_RECT, null);
+            mModel.set(TabGridSheetProperties.IS_DIALOG_VISIBLE, false);
             Tab currentTab = mTabModelSelector.getTabById(mCurrentTabId);
             List<Tab> relatedTabs = getRelatedTabs(currentTab.getId());
 
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 e543812..69f09c7a 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
@@ -6,19 +6,20 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
 import android.content.ComponentCallbacks;
 import android.content.Context;
 import android.content.res.Configuration;
+import android.graphics.Rect;
 import android.util.DisplayMetrics;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowManager;
 import android.widget.FrameLayout;
-import android.widget.LinearLayout;
 import android.widget.PopupWindow;
+import android.widget.RelativeLayout;
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.ContextUtils;
@@ -32,28 +33,39 @@
  * show/hide dialog.
  */
 public class TabGridDialogParent {
-    private PopupWindow mPopupWindow;
-    private LinearLayout mDialogContainerView;
-    private ScrimView mScrimView;
-    private ScrimView.ScrimParams mScrimParams;
-    private ValueAnimator mDialogFadeIn;
-    private ValueAnimator mDialogFadeOut;
-    private Animator mCurrentAnimator;
+    private static final int DIALOG_ANIMATION_DURATION = 300;
     private final ComponentCallbacks mComponentCallbacks;
     private final FrameLayout.LayoutParams mContainerParams;
     private final ViewGroup mParent;
-    private final int mSideMargin;
-    private final int mTopMargin;
     private final int mStatusBarHeight;
+    private final int mToolbarHeight;
+    private final float mTabGridCardPadding;
+    private PopupWindow mPopupWindow;
+    private RelativeLayout mDialogContainerView;
+    private ScrimView mScrimView;
+    private ScrimView.ScrimParams mScrimParams;
+    private View mBlockView;
+    private Animator mCurrentAnimator;
+    private ObjectAnimator mBasicFadeOut;
+    private AnimatorSet mShowDialogAnimation;
+    private AnimatorSet mHideDialogAnimation;
+    private AnimatorListenerAdapter mShowDialogAnimationListener;
+    private AnimatorListenerAdapter mHideDialogAnimationListener;
+    private int mDialogWidth;
+    private int mDialogHeight;
+    private int mSideMargin;
+    private int mTopMargin;
 
     TabGridDialogParent(Context context, ViewGroup parent) {
         mParent = parent;
-        mSideMargin =
-                (int) context.getResources().getDimension(R.dimen.tab_grid_dialog_side_margin);
-        mTopMargin = (int) context.getResources().getDimension(R.dimen.tab_grid_dialog_top_margin);
+        mShowDialogAnimation = new AnimatorSet();
+        mHideDialogAnimation = new AnimatorSet();
         int statusBarHeightResourceId =
                 context.getResources().getIdentifier("status_bar_height", "dimen", "android");
         mStatusBarHeight = context.getResources().getDimensionPixelSize(statusBarHeightResourceId);
+        mTabGridCardPadding = context.getResources().getDimension(R.dimen.tab_list_card_padding);
+        mToolbarHeight =
+                (int) context.getResources().getDimension(R.dimen.bottom_sheet_peek_height);
         mContainerParams = new FrameLayout.LayoutParams(
                 ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
         mComponentCallbacks = new ComponentCallbacks() {
@@ -67,54 +79,113 @@
         };
         ContextUtils.getApplicationContext().registerComponentCallbacks(mComponentCallbacks);
         setupDialogContent(context);
-        setupDialogAnimation();
+        prepareAnimation();
     }
 
     private void setupDialogContent(Context context) {
         FrameLayout backgroundView = new FrameLayout(context);
-        mDialogContainerView = new LinearLayout(context);
+        mDialogContainerView = new RelativeLayout(context);
         mDialogContainerView.setLayoutParams(mContainerParams);
         mDialogContainerView.setBackgroundColor(ApiCompatibilityUtils.getColor(
                 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);
+        mBlockView = new View(context);
+        mBlockView.setLayoutParams(new RelativeLayout.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
+        mBlockView.setOnClickListener(null);
         backgroundView.addView(mDialogContainerView);
         updateDialogWithOrientation(context, context.getResources().getConfiguration().orientation);
     }
 
-    private void setupDialogAnimation() {
-        mDialogFadeIn = ObjectAnimator.ofFloat(mDialogContainerView, View.ALPHA, 0f, 1f);
-        mDialogFadeIn.setInterpolator(BakedBezierInterpolator.FADE_IN_CURVE);
-        mDialogFadeIn.setDuration(TabListRecyclerView.BASE_ANIMATION_DURATION_MS);
-        mDialogFadeIn.addListener(new AnimatorListenerAdapter() {
+    private void prepareAnimation() {
+        mBasicFadeOut = ObjectAnimator.ofFloat(mDialogContainerView, View.ALPHA, 1f, 0f);
+        mBasicFadeOut.setInterpolator(BakedBezierInterpolator.FADE_OUT_CURVE);
+        mBasicFadeOut.setDuration(DIALOG_ANIMATION_DURATION);
+        mShowDialogAnimationListener = new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
                 mCurrentAnimator = null;
             }
-
-            @Override
-            public void onAnimationCancel(Animator animation) {
-                mCurrentAnimator = null;
-            }
-        });
-
-        mDialogFadeOut = ObjectAnimator.ofFloat(mDialogContainerView, View.ALPHA, 1f, 0f);
-        mDialogFadeOut.setInterpolator(BakedBezierInterpolator.FADE_OUT_CURVE);
-        mDialogFadeOut.setDuration(TabListRecyclerView.BASE_ANIMATION_DURATION_MS);
-        mDialogFadeOut.addListener(new AnimatorListenerAdapter() {
+        };
+        mHideDialogAnimationListener = new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
                 mPopupWindow.dismiss();
                 mCurrentAnimator = null;
+                mDialogContainerView.setAlpha(1f);
+                mDialogContainerView.removeView(mBlockView);
             }
+        };
+    }
 
-            @Override
-            public void onAnimationCancel(Animator animation) {
-                mPopupWindow.dismiss();
-                mCurrentAnimator = null;
-            }
-        });
+    void setupDialogAnimation(Rect rect) {
+        // In case where user jumps to a new page from dialog, clean existing animations in
+        // mHideDialogAnimation and play basic fade out instead of zooming back to corresponding tab
+        // grid card.
+        if (rect == null) {
+            mHideDialogAnimation = new AnimatorSet();
+            mHideDialogAnimation.play(mBasicFadeOut);
+            mHideDialogAnimation.removeAllListeners();
+            mHideDialogAnimation.addListener(mHideDialogAnimationListener);
+            return;
+        }
+
+        float sourceLeft = rect.left + mTabGridCardPadding;
+        float sourceTop = rect.top - mStatusBarHeight + mTabGridCardPadding;
+        float sourceHeight = rect.height() - 2 * mTabGridCardPadding;
+        float sourceWidth = rect.width() - 2 * mTabGridCardPadding;
+
+        float initYPosition = -(mDialogHeight / 2 + mTopMargin - sourceHeight / 2 - sourceTop);
+        float initXPosition = -(mDialogWidth / 2 + mSideMargin - sourceWidth / 2 - sourceLeft);
+        float initYScale = sourceHeight / mDialogHeight;
+        float initXScale = sourceWidth / mDialogWidth;
+        float scaleOffset = 0.02f;
+
+        final ObjectAnimator showMoveYAnimator =
+                ObjectAnimator.ofFloat(mDialogContainerView, "translationY", initYPosition, 0f);
+        showMoveYAnimator.setDuration(DIALOG_ANIMATION_DURATION);
+        final ObjectAnimator showMoveXAnimator =
+                ObjectAnimator.ofFloat(mDialogContainerView, "translationX", initXPosition, 0f);
+        showMoveXAnimator.setDuration(DIALOG_ANIMATION_DURATION);
+        final ObjectAnimator showScaleYAnimator = ObjectAnimator.ofFloat(
+                mDialogContainerView, View.SCALE_Y, initYScale - scaleOffset, 1f);
+        showScaleYAnimator.setDuration(DIALOG_ANIMATION_DURATION);
+        final ObjectAnimator showScaleXAnimator = ObjectAnimator.ofFloat(
+                mDialogContainerView, View.SCALE_X, initXScale - scaleOffset, 1f);
+        showScaleXAnimator.setDuration(DIALOG_ANIMATION_DURATION);
+        mShowDialogAnimation = new AnimatorSet();
+        mShowDialogAnimation.play(showMoveXAnimator)
+                .with(showMoveYAnimator)
+                .with(showScaleXAnimator)
+                .with(showScaleYAnimator);
+        mShowDialogAnimation.addListener(mShowDialogAnimationListener);
+
+        final ObjectAnimator hideMoveYAnimator =
+                ObjectAnimator.ofFloat(mDialogContainerView, "translationY", 0f, initYPosition);
+        hideMoveYAnimator.setDuration(DIALOG_ANIMATION_DURATION);
+        final ObjectAnimator hideMoveXAnimator =
+                ObjectAnimator.ofFloat(mDialogContainerView, "translationX", 0f, initXPosition);
+        hideMoveXAnimator.setDuration(DIALOG_ANIMATION_DURATION);
+        final ObjectAnimator hideScaleYAnimator = ObjectAnimator.ofFloat(
+                mDialogContainerView, View.SCALE_Y, 1f, initYScale - scaleOffset);
+        hideScaleYAnimator.setDuration(DIALOG_ANIMATION_DURATION);
+        final ObjectAnimator hideScaleXAnimator = ObjectAnimator.ofFloat(
+                mDialogContainerView, View.SCALE_X, 1f, initXScale - scaleOffset);
+        hideScaleXAnimator.setDuration(DIALOG_ANIMATION_DURATION);
+
+        final ObjectAnimator scaleAnimator =
+                ObjectAnimator.ofFloat(mDialogContainerView, View.ALPHA, 1f, 0f);
+        scaleAnimator.setDuration(TabListRecyclerView.BASE_ANIMATION_DURATION_MS);
+        scaleAnimator.setStartDelay(DIALOG_ANIMATION_DURATION);
+        mHideDialogAnimation = new AnimatorSet();
+        mHideDialogAnimation.play(hideMoveXAnimator)
+                .with(hideMoveYAnimator)
+                .with(hideScaleXAnimator)
+                .with(hideScaleYAnimator)
+                .with(scaleAnimator);
+        mHideDialogAnimation.removeAllListeners();
+        mHideDialogAnimation.addListener(mHideDialogAnimationListener);
     }
 
     private void updateDialogWithOrientation(Context context, int orientation) {
@@ -122,19 +193,30 @@
         ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE))
                 .getDefaultDisplay()
                 .getMetrics(displayMetrics);
-        mPopupWindow.setHeight(displayMetrics.heightPixels);
-        mPopupWindow.setWidth(displayMetrics.widthPixels);
+        int screenHeight = displayMetrics.heightPixels;
+        int screenWidth = displayMetrics.widthPixels;
+        mPopupWindow.setHeight(screenHeight);
+        mPopupWindow.setWidth(screenWidth);
         if (mPopupWindow != null && mPopupWindow.isShowing()) {
             mPopupWindow.dismiss();
             mPopupWindow.showAtLocation(mParent, Gravity.CENTER, 0, 0);
         }
         if (orientation == Configuration.ORIENTATION_PORTRAIT) {
-            mContainerParams.setMargins(
-                    mSideMargin, mTopMargin, mSideMargin, mTopMargin + mStatusBarHeight);
+            mSideMargin =
+                    (int) context.getResources().getDimension(R.dimen.tab_grid_dialog_side_margin);
+            mTopMargin =
+                    (int) context.getResources().getDimension(R.dimen.tab_grid_dialog_top_margin);
+
         } else {
-            mContainerParams.setMargins(
-                    mTopMargin, mSideMargin, mTopMargin, mSideMargin + mStatusBarHeight);
+            mSideMargin =
+                    (int) context.getResources().getDimension(R.dimen.tab_grid_dialog_top_margin);
+            mTopMargin =
+                    (int) context.getResources().getDimension(R.dimen.tab_grid_dialog_side_margin);
         }
+        mContainerParams.setMargins(
+                mSideMargin, mTopMargin, mSideMargin, mTopMargin + mStatusBarHeight);
+        mDialogHeight = screenHeight - 2 * mTopMargin - mStatusBarHeight;
+        mDialogWidth = screenWidth - 2 * mSideMargin;
     }
 
     /**
@@ -157,6 +239,9 @@
         mDialogContainerView.removeAllViews();
         mDialogContainerView.addView(toolbarView);
         mDialogContainerView.addView(recyclerView);
+        RelativeLayout.LayoutParams params =
+                (RelativeLayout.LayoutParams) recyclerView.getLayoutParams();
+        params.setMargins(0, mToolbarHeight, 0, 0);
         recyclerView.setVisibility(View.VISIBLE);
     }
 
@@ -164,25 +249,31 @@
      * Show {@link PopupWindow} for dialog with animation.
      */
     void showDialog() {
-        if (mCurrentAnimator != null && mCurrentAnimator != mDialogFadeIn) {
-            mCurrentAnimator.cancel();
+        if (mCurrentAnimator != null && mCurrentAnimator != mShowDialogAnimation) {
+            mCurrentAnimator.end();
         }
-        mPopupWindow.showAtLocation(mParent, Gravity.CENTER, 0, 0);
+        mCurrentAnimator = mShowDialogAnimation;
         mScrimView.showScrim(mScrimParams);
-        mDialogFadeIn.start();
-        mCurrentAnimator = mDialogFadeIn;
+        mPopupWindow.showAtLocation(mParent, Gravity.CENTER, 0, 0);
+        mShowDialogAnimation.start();
     }
 
     /**
      * Hide {@link PopupWindow} for dialog with animation.
      */
     void hideDialog() {
-        if (mCurrentAnimator != null && mCurrentAnimator != mDialogFadeOut) {
-            mCurrentAnimator.cancel();
+        if (mCurrentAnimator != null && mCurrentAnimator != mHideDialogAnimation) {
+            mCurrentAnimator.end();
         }
+        mCurrentAnimator = mHideDialogAnimation;
         mScrimView.hideScrim(true);
-        mDialogFadeOut.start();
-        mCurrentAnimator = mDialogFadeOut;
+        // Use mBlockView to disable all click behaviors on dialog when the hide animation is
+        // playing.
+        if (mBlockView.getParent() == null) {
+            mDialogContainerView.addView(mBlockView);
+        }
+        mDialogContainerView.bringChildToFront(mBlockView);
+        mHideDialogAnimation.start();
     }
 
     /**
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetProperties.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetProperties.java
index 6f94201..e4370ea 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetProperties.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetProperties.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.tasks.tab_management;
 
 import android.content.res.ColorStateList;
+import android.graphics.Rect;
 import android.view.View.OnClickListener;
 
 import org.chromium.chrome.browser.widget.ScrimView;
@@ -31,8 +32,9 @@
     public static final PropertyModel
             .WritableObjectPropertyKey<ScrimView.ScrimObserver> SCRIMVIEW_OBSERVER =
             new PropertyModel.WritableObjectPropertyKey<>();
-
-    public static final PropertyKey[] ALL_KEYS =
-            new PropertyKey[] {COLLAPSE_CLICK_LISTENER, ADD_CLICK_LISTENER, HEADER_TITLE,
-                    CONTENT_TOP_MARGIN, PRIMARY_COLOR, TINT, IS_DIALOG_VISIBLE, SCRIMVIEW_OBSERVER};
+    public static final PropertyModel.WritableObjectPropertyKey<Rect> ANIMATION_SOURCE_RECT =
+            new PropertyModel.WritableObjectPropertyKey<>();
+    public static final PropertyKey[] ALL_KEYS = new PropertyKey[] {COLLAPSE_CLICK_LISTENER,
+            ADD_CLICK_LISTENER, HEADER_TITLE, CONTENT_TOP_MARGIN, PRIMARY_COLOR, TINT,
+            IS_DIALOG_VISIBLE, SCRIMVIEW_OBSERVER, ANIMATION_SOURCE_RECT};
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetViewBinder.java
index d82278c9..890007c6 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetViewBinder.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.tasks.tab_management;
 
 import static org.chromium.chrome.browser.tasks.tab_management.TabGridSheetProperties.ADD_CLICK_LISTENER;
+import static org.chromium.chrome.browser.tasks.tab_management.TabGridSheetProperties.ANIMATION_SOURCE_RECT;
 import static org.chromium.chrome.browser.tasks.tab_management.TabGridSheetProperties.COLLAPSE_CLICK_LISTENER;
 import static org.chromium.chrome.browser.tasks.tab_management.TabGridSheetProperties.CONTENT_TOP_MARGIN;
 import static org.chromium.chrome.browser.tasks.tab_management.TabGridSheetProperties.HEADER_TITLE;
@@ -72,6 +73,8 @@
             } else {
                 viewHolder.dialogView.hideDialog();
             }
+        } else if (ANIMATION_SOURCE_RECT == propertyKey) {
+            viewHolder.dialogView.setupDialogAnimation(model.get(ANIMATION_SOURCE_RECT));
         }
     }
 }
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 e4895b7..ec73af0 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
@@ -287,4 +287,17 @@
             float left1, float top1, float left2, float top2, float threshold) {
         return Math.abs(left1 - left2) < threshold && Math.abs(top1 - top2) < threshold;
     }
+
+    /**
+     * This method gets the position of certain item in the {@link TabListRecyclerView}.
+     *
+     * @param index  The index of the item whose position is requested.
+     * @return The {@link Rect} that contains the position information.
+     */
+    Rect getTabPosition(int index) {
+        Rect rect = new Rect();
+        View holder = findViewHolderForAdapterPosition(index).itemView;
+        holder.getGlobalVisibleRect(rect);
+        return rect;
+    }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegateImpl.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegateImpl.java
index edc2f22..ee063ae 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegateImpl.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegateImpl.java
@@ -39,7 +39,7 @@
         return new GridTabSwitcherCoordinator(activity, activity.getLifecycleDispatcher(),
                 activity.getTabModelSelector(), activity.getTabContentManager(),
                 activity.getCompositorViewHolder(), activity.getFullscreenManager(), activity,
-                activity::onBackPressed);
+                activity.getMenuOrKeyboardActionController(), activity::onBackPressed);
     }
 
     @Override
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorCoordinator.java
new file mode 100644
index 0000000..e9ee189
--- /dev/null
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorCoordinator.java
@@ -0,0 +1,90 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.tasks.tab_management;
+
+import android.content.Context;
+import android.support.annotation.Nullable;
+import android.view.View;
+
+import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tabmodel.TabModelFilter;
+import org.chromium.chrome.browser.tabmodel.TabModelSelector;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class is a coordinator for TabSelectionEditor component. It manages the communication with
+ * {@link TabListCoordinator} as well as the life-cycle of shared component.
+ */
+class TabSelectionEditorCoordinator {
+    static final String COMPONENT_NAME = "TabSelectionEditor";
+
+    /**
+     * An interface to control the TabSelectionEditor.
+     */
+    interface TabSelectionEditorController {
+        /**
+         * Shows the TabSelectionEditor.
+         */
+        void show();
+
+        /**
+         * Hides the TabSelectionEditor.
+         */
+        void hide();
+
+        /**
+         * @return Whether the TabSelectionEditor is visible.
+         */
+        boolean isEditorVisible();
+    }
+
+    private final Context mContext;
+    private final View mParentView;
+    private final TabModelSelector mTabModelSelector;
+
+    public TabSelectionEditorCoordinator(Context context, View parentView,
+            TabModelSelector tabModelSelector, TabContentManager tabContentManager) {
+        mContext = context;
+        mParentView = parentView;
+        mTabModelSelector = tabModelSelector;
+    }
+
+    private void resetWithListOfTabs(@Nullable List<Tab> tab) {
+        // TODO(meiliang): reset TabListCoordinator and TabSelectionEditorMediator
+    }
+
+    TabSelectionEditorController getController() {
+        // TODO(meiliang): move this to TabSelectionEditorMediator.
+        return new TabSelectionEditorController() {
+            @Override
+            public void show() {
+                List<Tab> tabs = new ArrayList<>();
+                TabModelFilter tabModelFilter =
+                        mTabModelSelector.getTabModelFilterProvider().getCurrentTabModelFilter();
+                for (int i = 0; i < tabModelFilter.getCount(); i++) {
+                    Tab tab = tabModelFilter.getTabAt(i);
+                    if (tabModelFilter.getRelatedTabList(tab.getId()).size() == 1) {
+                        tabs.add(tab);
+                    }
+                }
+                resetWithListOfTabs(tabs);
+            }
+
+            @Override
+            public void hide() {
+                resetWithListOfTabs(null);
+            }
+
+            @Override
+            public boolean isEditorVisible() {
+                // TODO(meiliang): get visibility from PropertyModel
+                return false;
+            }
+        };
+    }
+}
diff --git a/chrome/android/java/res/layout/account_signin_view.xml b/chrome/android/java/res/layout/account_signin_view.xml
index 359b8433..0eae8c6 100644
--- a/chrome/android/java/res/layout/account_signin_view.xml
+++ b/chrome/android/java/res/layout/account_signin_view.xml
@@ -186,7 +186,7 @@
         <org.chromium.ui.widget.ButtonCompat
             android:id="@+id/negative_button"
             android:layout_width="wrap_content"
-            android:layout_height="36dp"
+            android:layout_height="wrap_content"
             android:paddingStart="@dimen/fre_button_padding"
             android:paddingEnd="@dimen/fre_button_padding"
             style="@style/TextButton" />
@@ -201,7 +201,7 @@
         <org.chromium.ui.widget.ButtonCompat
             android:id="@+id/positive_button"
             android:layout_width="wrap_content"
-            android:layout_height="36dp"
+            android:layout_height="wrap_content"
             android:paddingStart="@dimen/fre_button_padding"
             android:paddingEnd="@dimen/fre_button_padding"
             style="@style/FilledButton.Flat" />
@@ -209,7 +209,7 @@
         <org.chromium.ui.widget.ButtonCompat
             android:id="@+id/more_button"
             android:layout_width="wrap_content"
-            android:layout_height="36dp"
+            android:layout_height="wrap_content"
             android:drawableEnd="@drawable/down_arrow"
             android:drawablePadding="8dp"
             android:visibility="gone"
diff --git a/chrome/android/java/res/layout/autofill_editor_base_buttons.xml b/chrome/android/java/res/layout/autofill_editor_base_buttons.xml
index 926edb9..48f8327 100644
--- a/chrome/android/java/res/layout/autofill_editor_base_buttons.xml
+++ b/chrome/android/java/res/layout/autofill_editor_base_buttons.xml
@@ -16,8 +16,8 @@
         android:layout_height="wrap_content"
         android:paddingStart="@dimen/pref_autofill_field_horizontal_padding"
         android:paddingEnd="@dimen/pref_autofill_field_horizontal_padding"
-        android:paddingTop="@dimen/editor_dialog_section_large_spacing"
-        android:paddingBottom="@dimen/editor_dialog_section_large_spacing"
+        android:paddingTop="@dimen/editor_dialog_section_buttons_vertical_padding"
+        android:paddingBottom="@dimen/editor_dialog_section_buttons_vertical_padding"
         android:background="@color/modern_primary_color"
         app:stackedMargin="@dimen/infobar_margin_between_stacked_buttons"
         app:primaryButtonText="@string/done"
diff --git a/chrome/android/java/res/layout/button_preference_button.xml b/chrome/android/java/res/layout/button_preference_button.xml
index 29a5807..f1fdeece 100644
--- a/chrome/android/java/res/layout/button_preference_button.xml
+++ b/chrome/android/java/res/layout/button_preference_button.xml
@@ -11,6 +11,5 @@
     android:id="@+id/button_preference"
     android:layout_height="wrap_content"
     android:layout_width="wrap_content"
-    android:minHeight="40dp"
     android:focusable="false"
     style="@style/FilledButton" />
diff --git a/chrome/android/java/res/layout/default_search_engine_first_run_fragment.xml b/chrome/android/java/res/layout/default_search_engine_first_run_fragment.xml
index 2e9df33..ce0662f 100644
--- a/chrome/android/java/res/layout/default_search_engine_first_run_fragment.xml
+++ b/chrome/android/java/res/layout/default_search_engine_first_run_fragment.xml
@@ -69,9 +69,9 @@
     <org.chromium.ui.widget.ButtonCompat
         android:id="@+id/button_primary"
         android:layout_width="wrap_content"
-        android:layout_height="36dp"
+        android:layout_height="wrap_content"
         android:layout_gravity="end"
-        android:layout_marginBottom="16dp"
+        android:layout_marginBottom="10dp"
         android:layout_marginStart="16dp"
         android:layout_marginEnd="22dp"
         android:paddingStart="@dimen/fre_button_padding"
diff --git a/chrome/android/java/res/layout/fre_data_reduction_proxy_lite_mode.xml b/chrome/android/java/res/layout/fre_data_reduction_proxy_lite_mode.xml
index ac0c95cb..aabc00d1 100644
--- a/chrome/android/java/res/layout/fre_data_reduction_proxy_lite_mode.xml
+++ b/chrome/android/java/res/layout/fre_data_reduction_proxy_lite_mode.xml
@@ -83,8 +83,8 @@
     <org.chromium.ui.widget.ButtonCompat
         android:id="@+id/next_button"
         android:layout_width="wrap_content"
-        android:layout_height="36dp"
-        android:layout_marginBottom="16dp"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="10dp"
         android:layout_gravity="bottom|center_horizontal"
         android:paddingStart="@dimen/fre_button_padding"
         android:paddingEnd="@dimen/fre_button_padding"
diff --git a/chrome/android/java/res/layout/fre_tosanduma.xml b/chrome/android/java/res/layout/fre_tosanduma.xml
index 2ef4e39..c5a64da4 100644
--- a/chrome/android/java/res/layout/fre_tosanduma.xml
+++ b/chrome/android/java/res/layout/fre_tosanduma.xml
@@ -80,8 +80,8 @@
     <org.chromium.ui.widget.ButtonCompat
         android:id="@+id/terms_accept"
         android:layout_width="wrap_content"
-        android:layout_height="36dp"
-        android:layout_marginBottom="16dp"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="10dp"
         android:layout_gravity="bottom|center_horizontal"
         android:paddingStart="@dimen/fre_button_padding"
         android:paddingEnd="@dimen/fre_button_padding"
diff --git a/chrome/android/java/res/layout/history_clear_browsing_data_header.xml b/chrome/android/java/res/layout/history_clear_browsing_data_header.xml
index d2bec89..6b7d4a7 100644
--- a/chrome/android/java/res/layout/history_clear_browsing_data_header.xml
+++ b/chrome/android/java/res/layout/history_clear_browsing_data_header.xml
@@ -6,8 +6,9 @@
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
     android:orientation="vertical"
-    android:paddingBottom="8dp" >
+    android:paddingBottom="8dp">
 
         <!-- Sets edge padding separately because android:padding doesn't override
              android:paddingStart. -->
@@ -22,6 +23,7 @@
             android:paddingEnd="@dimen/list_item_default_margin"
             android:gravity="center_vertical|start"
             android:text="@string/open_clear_browsing_data_dialog_button"
+            app:verticalInset="0dp"
             style="@style/TextButton" />
 
         <View style="@style/HorizontalDivider" />
diff --git a/chrome/android/java/res/layout/item_chooser_dialog.xml b/chrome/android/java/res/layout/item_chooser_dialog.xml
index 118a82c2..a237f83 100644
--- a/chrome/android/java/res/layout/item_chooser_dialog.xml
+++ b/chrome/android/java/res/layout/item_chooser_dialog.xml
@@ -74,7 +74,7 @@
         android:layout_height="wrap_content"
         android:layout_width="wrap_content"
         android:layout_gravity="end"
-        android:layout_marginTop="12dp"
+        android:layout_marginTop="6dp"
         android:layout_marginEnd="12dp"
         android:paddingStart="16dp"
         android:paddingEnd="16dp"
diff --git a/chrome/android/java/res/layout/modal_dialog_view.xml b/chrome/android/java/res/layout/modal_dialog_view.xml
index 4f990e9e..cdc630d7 100644
--- a/chrome/android/java/res/layout/modal_dialog_view.xml
+++ b/chrome/android/java/res/layout/modal_dialog_view.xml
@@ -52,7 +52,10 @@
         android:id="@+id/button_bar"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:padding="@dimen/modal_dialog_control_padding"
+        android:paddingTop="@dimen/modal_dialog_control_vertical_padding"
+        android:paddingBottom="@dimen/modal_dialog_control_vertical_padding"
+        android:paddingStart="@dimen/modal_dialog_control_horizontal_padding"
+        android:paddingEnd="@dimen/modal_dialog_control_horizontal_padding"
         app:stackedMargin="@dimen/button_bar_stacked_margin"
         app:buttonAlignment="end">
 
diff --git a/chrome/android/java/res/layout/page_info.xml b/chrome/android/java/res/layout/page_info.xml
index dbe14e94..faa040f 100644
--- a/chrome/android/java/res/layout/page_info.xml
+++ b/chrome/android/java/res/layout/page_info.xml
@@ -108,11 +108,11 @@
     <org.chromium.ui.widget.ButtonCompat
         android:id="@+id/page_info_site_settings_button"
         android:layout_width="wrap_content"
-        android:layout_height="@dimen/page_info_popup_button_height"
+        android:layout_height="wrap_content"
         android:layout_gravity="end"
         android:layout_marginEnd="@dimen/page_info_popup_padding_sides"
         android:layout_marginStart="@dimen/page_info_popup_padding_sides"
-        android:layout_marginTop="8dp"
+        android:layout_marginTop="2dp"
         android:paddingEnd="@dimen/page_info_popup_button_padding_sides"
         android:paddingStart="@dimen/page_info_popup_button_padding_sides"
         android:text="@string/page_info_site_settings_button"
@@ -121,11 +121,11 @@
     <org.chromium.ui.widget.ButtonCompat
         android:id="@+id/page_info_open_online_button"
         android:layout_width="wrap_content"
-        android:layout_height="@dimen/page_info_popup_button_height"
+        android:layout_height="wrap_content"
         android:layout_gravity="end"
         android:layout_marginEnd="@dimen/page_info_popup_padding_sides"
         android:layout_marginStart="@dimen/page_info_popup_padding_sides"
-        android:layout_marginTop="8dp"
+        android:layout_marginTop="2dp"
         android:paddingEnd="@dimen/page_info_popup_button_padding_sides"
         android:paddingStart="@dimen/page_info_popup_button_padding_sides"
         android:text="@string/page_info_open_online_button"
diff --git a/chrome/android/java/res/layout/payment_request_bottom_bar.xml b/chrome/android/java/res/layout/payment_request_bottom_bar.xml
index 7e29649..f91344db 100644
--- a/chrome/android/java/res/layout/payment_request_bottom_bar.xml
+++ b/chrome/android/java/res/layout/payment_request_bottom_bar.xml
@@ -10,7 +10,10 @@
     android:id="@+id/bottom_bar"
     android:layout_height="wrap_content"
     android:layout_width="match_parent"
-    android:padding="@dimen/editor_dialog_section_large_spacing"
+    android:paddingTop="@dimen/payments_request_bottom_bar_vertical_padding"
+    android:paddingBottom="@dimen/payments_request_bottom_bar_vertical_padding"
+    android:paddingStart="@dimen/payments_request_bottom_bar_horizontal_padding"
+    android:paddingEnd="@dimen/payments_request_bottom_bar_horizontal_padding"
     android:background="@color/payment_request_bg"
     android:visibility="gone" >
 
@@ -36,14 +39,14 @@
     <org.chromium.ui.widget.ButtonCompat
         android:id="@+id/button_secondary"
         android:layout_width="wrap_content"
-        android:layout_height="36dp"
+        android:layout_height="wrap_content"
         android:text="@string/payments_edit_button"
         style="@style/TextButton" />
 
     <org.chromium.ui.widget.ButtonCompat
         android:id="@+id/button_primary"
         android:layout_width="wrap_content"
-        android:layout_height="36dp"
+        android:layout_height="wrap_content"
         android:text="@string/payments_pay_button"
         style="@style/FilledButton.Flat" />
 </org.chromium.chrome.browser.payments.ui.PaymentRequestBottomBar>
\ No newline at end of file
diff --git a/chrome/android/java/res/layout/personalized_signin_promo_view_body.xml b/chrome/android/java/res/layout/personalized_signin_promo_view_body.xml
index fc7d12b..69cbce4 100644
--- a/chrome/android/java/res/layout/personalized_signin_promo_view_body.xml
+++ b/chrome/android/java/res/layout/personalized_signin_promo_view_body.xml
@@ -22,11 +22,9 @@
     <org.chromium.ui.widget.ButtonCompat
         android:id="@+id/signin_promo_signin_button"
         android:layout_width="match_parent"
-        android:layout_height="36dp"
-        android:layout_marginBottom="6dp"
+        android:layout_height="wrap_content"
         android:layout_marginEnd="24dp"
         android:layout_marginStart="24dp"
-        android:layout_marginTop="6dp"
         android:ellipsize="end"
         android:singleLine="true"
         android:text="@string/signin_promo_continue_as"
@@ -36,11 +34,9 @@
         android:id="@+id/signin_promo_choose_account_button"
         style="@style/SigninButtonBorderlessRegular"
         android:layout_width="match_parent"
-        android:layout_height="36dp"
-        android:layout_marginBottom="6dp"
+        android:layout_height="wrap_content"
         android:layout_marginEnd="24dp"
         android:layout_marginStart="24dp"
-        android:layout_marginTop="6dp"
         android:ellipsize="end"
         android:singleLine="true"
         android:text="@string/signin_promo_choose_account"/>
diff --git a/chrome/android/java/res/layout/snackbar.xml b/chrome/android/java/res/layout/snackbar.xml
index 4346c44..f13fbc33 100644
--- a/chrome/android/java/res/layout/snackbar.xml
+++ b/chrome/android/java/res/layout/snackbar.xml
@@ -73,7 +73,7 @@
         <org.chromium.ui.widget.ButtonCompat
             android:id="@+id/snackbar_button"
             android:layout_width="wrap_content"
-            android:layout_height="@dimen/snackbar_min_height"
+            android:layout_height="wrap_content"
             android:layout_gravity="center_vertical"
             android:paddingEnd="24dp"
             android:paddingStart="24dp"
diff --git a/chrome/android/java/res/layout/storage_preferences.xml b/chrome/android/java/res/layout/storage_preferences.xml
index 5eecae7c..1eddfbb8 100644
--- a/chrome/android/java/res/layout/storage_preferences.xml
+++ b/chrome/android/java/res/layout/storage_preferences.xml
@@ -6,6 +6,7 @@
 <!-- TODO(https://crbug.com/929743): Use standard layout and specify only bottom button here. -->
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
     android:orientation="vertical"
     android:layout_height="match_parent"
     android:layout_width="match_parent">
@@ -44,5 +45,6 @@
         android:paddingTop="16dp"
         android:paddingBottom="16dp"
         android:text="@string/storage_clear_button_title"
+        app:verticalInset="0dp"
         style="@style/TextButton" />
 </LinearLayout>
\ No newline at end of file
diff --git a/chrome/android/java/res/layout/suspended_tab.xml b/chrome/android/java/res/layout/suspended_tab.xml
index ec3a69d..7e9ee89 100644
--- a/chrome/android/java/res/layout/suspended_tab.xml
+++ b/chrome/android/java/res/layout/suspended_tab.xml
@@ -7,7 +7,6 @@
 -->
 <ScrollView 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="match_parent"
         android:layout_height="match_parent"
         android:fillViewport="true">
@@ -58,10 +57,10 @@
             android:id="@+id/suspended_tab_settings_button"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:minHeight="48dp"
             android:layout_gravity="end"
             android:gravity="center"
             android:text="@string/preferences"
+            app:verticalInset="0dp"
             style="@style/TextButton" />
     </LinearLayout>
 </ScrollView>
\ No newline at end of file
diff --git a/chrome/android/java/res/menu/main_menu.xml b/chrome/android/java/res/menu/main_menu.xml
index 3b08e7b..4580a99 100644
--- a/chrome/android/java/res/menu/main_menu.xml
+++ b/chrome/android/java/res/menu/main_menu.xml
@@ -93,6 +93,8 @@
              android:title="@string/menu_close_all_tabs" />
          <item android:id="@+id/close_all_incognito_tabs_menu_id"
              android:title="@string/menu_close_all_incognito_tabs" />
+         <item android:id="@+id/menu_group_tabs"
+             android:title="@string/menu_group_tabs" />
          <item android:id="@id/preferences_id"
              android:title="@string/menu_preferences" />
     </group>
diff --git a/chrome/android/java/res/values-v17/styles.xml b/chrome/android/java/res/values-v17/styles.xml
index 8050e38..a19acaa 100644
--- a/chrome/android/java/res/values-v17/styles.xml
+++ b/chrome/android/java/res/values-v17/styles.xml
@@ -245,6 +245,7 @@
         <item name="preferenceStyle">@style/Theme.Chromium.PreferenceItem</item>
         <item name="preferenceFragmentCompatStyle">@style/Theme.Chromium.PreferenceFragment</item>
         <item name="preferenceFragmentListStyle">@style/Theme.Chromium.PreferenceFragmentList</item>
+        <item name="dialogPreferenceStyle">@style/Theme.Chromium.DialogPreference</item>
     </style>
 
     <style name="Theme.Chromium.PreferenceFragment">
@@ -265,6 +266,23 @@
         <item name="allowDividerBelow">false</item>
     </style>
 
+    <style name="Theme.Chromium.DialogPreference">
+        <item name="android:layout">@layout/preference_compat</item>
+        <item name="android:negativeButtonText">@android:string/cancel</item>
+    </style>
+
+    <style name="Theme.Chromium.Preferences.AlertDialog" parent="Theme.Chromium.AlertDialog">
+        <item name="buttonBarButtonStyle">@style/TextAppearance.DialogButtonCompat</item>
+    </style>
+
+    <!-- TODO(chouinard): Using all caps for support library preference dialog buttons to match
+         existing non-compat style, however we should later reexamine whether this is still the
+         right choice since it looks like most of our other AlertDialogs use the default title case.
+         -->
+    <style name="TextAppearance.DialogButtonCompat" parent="AlertDialogButtonStyle">
+        <item name="android:textAllCaps">true</item>
+    </style>
+
     <style name="PreferenceActionBarModern" parent="@style/Widget.AppCompat.Light.ActionBar.Solid">
       <item name="titleTextStyle">@style/TextAppearance.BlackHeadline</item>
     </style>
diff --git a/chrome/android/java/res/values-v21/styles.xml b/chrome/android/java/res/values-v21/styles.xml
index 2272d729..e5a2602 100644
--- a/chrome/android/java/res/values-v21/styles.xml
+++ b/chrome/android/java/res/values-v21/styles.xml
@@ -22,6 +22,9 @@
         <!-- Overriding alertDialogTheme pre-v21 with our custom AlertDialog theme causes bad visual
              states on ListPreference because we don't use the support library ListPreference. -->
         <item name="android:alertDialogTheme">@style/Theme.Chromium.AlertDialog</item>
+        <!-- TODO(crbug.com/967022): Clean up the unnecessary preference alert dialog styles once
+             all dialog preferences are migrated to the support library. -->
+        <item name="alertDialogTheme">@style/Theme.Chromium.Preferences.AlertDialog</item>
         <item name="preferenceTheme">@style/Theme.Chromium.PreferenceTheme</item>
     </style>
 
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml
index 0ef4e15..ea718076 100644
--- a/chrome/android/java/res/values/dimens.xml
+++ b/chrome/android/java/res/values/dimens.xml
@@ -10,7 +10,6 @@
 
     <!-- Page Info Popup Dimensions -->
     <dimen name="page_info_popup_padding_sides">16dp</dimen>
-    <dimen name="page_info_popup_button_height">36dp</dimen>
     <dimen name="page_info_popup_button_padding_sides">8dp</dimen>
     <dimen name="page_info_popup_permission_icon_size">24dp</dimen>
 
@@ -142,9 +141,6 @@
     <!-- Password generation dialog dimensions -->
     <dimen name="password_generation_dialog_padding">24dp</dimen>
 
-    <!-- Minimum height/width for a touchable item -->
-    <dimen name="min_touch_target_size">48dp</dimen>
-
     <!-- Promo dialogs -->
     <dimen name="promo_dialog_illustration_margin">24dp</dimen>
     <dimen name="promo_dialog_illustration_width">150dp</dimen>
@@ -204,7 +200,8 @@
     <dimen name="dialog_max_width">600dp</dimen>
 
     <!-- ModalDialogView dimensions -->
-    <dimen name="modal_dialog_control_padding">8dp</dimen>
+    <dimen name="modal_dialog_control_vertical_padding">2dp</dimen>
+    <dimen name="modal_dialog_control_horizontal_padding">8dp</dimen>
     <dimen name="tab_modal_scrim_vertical_margin">16dp</dimen>
 
     <!-- Tab Strip Dimensions -->
@@ -450,6 +447,7 @@
     <!-- Editor dialog UI -->
     <dimen name="editor_dialog_section_small_spacing">8dp</dimen>
     <dimen name="editor_dialog_section_large_spacing">16dp</dimen>
+    <dimen name="editor_dialog_section_buttons_vertical_padding">10dp</dimen>
     <dimen name="editable_option_section_logo_width">38dp</dimen>
 
     <!-- Payments UI
@@ -468,6 +466,8 @@
     <dimen name="payments_ui_translation">100dp</dimen>
     <dimen name="payments_favicon_size">24dp</dimen>
     <dimen name="payments_handler_window_minimum_height">500dp</dimen>
+    <dimen name="payments_request_bottom_bar_vertical_padding">10dp</dimen>
+    <dimen name="payments_request_bottom_bar_horizontal_padding">16dp</dimen>
 
     <!-- Preferences dimensions
          pref_autofill_field_horizontal_padding exists because TextInputLayouts have an internal
@@ -603,7 +603,8 @@
 
     <!-- History Navigation UI Item -->
     <dimen name="navigation_bubble_border_width">1dp</dimen>
-    <dimen name="navigation_bubble_default_height">32dp</dimen>
+    <dimen name="navigation_bubble_default_height">48dp</dimen>
+    <dimen name="navigation_bubble_bg_vertical_inset">8dp</dimen>
     <dimen name="navigation_bubble_vertical_padding">4dp</dimen>
     <dimen name="navigation_bubble_horizontal_padding">8dp</dimen>
     <dimen name="navigation_bubble_icon_size">20dp</dimen>
diff --git a/chrome/android/java/res/xml/tracing_preferences.xml b/chrome/android/java/res/xml/tracing_preferences.xml
index 78dbf3b..fa63ae0 100644
--- a/chrome/android/java/res/xml/tracing_preferences.xml
+++ b/chrome/android/java/res/xml/tracing_preferences.xml
@@ -3,23 +3,24 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" android:orderingFromXml="true">
-    <Preference
+<android.support.v7.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" android:orderingFromXml="true">
+    <android.support.v7.preference.Preference
         android:fragment="org.chromium.chrome.browser.preferences.developer.TracingCategoriesPreferences"
         android:key="default_categories"
         android:title="Default categories"/><!-- developer strings are not translated -->
-    <Preference
+    <android.support.v7.preference.Preference
         android:fragment="org.chromium.chrome.browser.preferences.developer.TracingCategoriesPreferences"
         android:key="non_default_categories"
         android:title="Disabled-by-default categories"/>
-    <org.chromium.chrome.browser.preferences.ChromeBaseListPreference
+    <org.chromium.chrome.browser.preferences.ChromeBaseListPreferenceCompat
         android:key="mode"
         android:title="Tracing mode"
         android:persistent="false"/>
-    <org.chromium.chrome.browser.preferences.ButtonPreference
+    <org.chromium.chrome.browser.preferences.ButtonPreferenceCompat
         android:key="start_recording"/>
-    <org.chromium.chrome.browser.preferences.TextMessagePreference
+    <org.chromium.chrome.browser.preferences.TextMessagePreferenceCompat
+        style="@style/Theme.Chromium.PreferenceItemNoDividers"
         android:key="tracing_status"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"/>
-</PreferenceScreen>
+</android.support.v7.preference.PreferenceScreen>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
index 09e445c..6fbff6b8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -201,8 +201,8 @@
         extends AsyncInitializationActivity
         implements TabCreatorManager, AccessibilityStateChangeListener, PolicyChangeListener,
                    ContextualSearchTabPromotionDelegate, SnackbarManageable, SceneChangeObserver,
-                   StatusBarColorController.StatusBarColorProvider, AppMenuDelegate,
-                   AppMenuBlocker {
+                   StatusBarColorController.StatusBarColorProvider, AppMenuDelegate, AppMenuBlocker,
+                   MenuOrKeyboardActionController {
     /**
      * No control container to inflate during initialization.
      */
@@ -224,22 +224,6 @@
     }
 
     /**
-     * A handler for menu or keyboard actions. Register via
-     * {@link #registerMenuOrKeyboardActionHandler(MenuOrKeyboardActionHandler)}.
-     */
-    public interface MenuOrKeyboardActionHandler {
-        /**
-         * Handles menu item selection and keyboard shortcuts.
-         *
-         * @param id The ID of the selected menu item (defined in main_menu.xml) or
-         *           keyboard shortcut (defined in values.xml).
-         * @param fromMenu Whether this was triggered from the menu.
-         * @return Whether the action was handled.
-         */
-        boolean onMenuOrKeyboardAction(int id, boolean fromMenu);
-    }
-
-    /**
      * No toolbar layout to inflate during initialization.
      */
     static final int NO_TOOLBAR_LAYOUT = -1;
@@ -357,7 +341,9 @@
      */
     private TouchlessUiCoordinator mTouchlessUiCoordinator;
 
-    private List<MenuOrKeyboardActionHandler> mMenuActionHandlers = new ArrayList<>();
+    // TODO(972867): Pull MenuOrKeyboardActionController out of ChromeActivity.
+    private List<MenuOrKeyboardActionController.MenuOrKeyboardActionHandler> mMenuActionHandlers =
+            new ArrayList<>();
 
     @Override
     protected ActivityWindowAndroid createWindowAndroid() {
@@ -2093,15 +2079,19 @@
     }
 
     /**
-     * @param handler A new {@link MenuOrKeyboardActionHandler} to register.
+     * @return The {@link MenuOrKeyboardActionController} for registering menu or keyboard action
+     *         handler for this activity.
      */
+    public MenuOrKeyboardActionController getMenuOrKeyboardActionController() {
+        return this;
+    }
+
+    @Override
     public void registerMenuOrKeyboardActionHandler(MenuOrKeyboardActionHandler handler) {
         mMenuActionHandlers.add(handler);
     }
 
-    /**
-     * @param handler A {@link MenuOrKeyboardActionHandler} to unregister.
-     */
+    @Override
     public void unregisterMenuOrKeyboardActionHandler(MenuOrKeyboardActionHandler handler) {
         mMenuActionHandlers.remove(handler);
     }
@@ -2115,7 +2105,8 @@
      * @return Whether the action was handled.
      */
     public boolean onMenuOrKeyboardAction(int id, boolean fromMenu) {
-        for (MenuOrKeyboardActionHandler handler : mMenuActionHandlers) {
+        for (MenuOrKeyboardActionController.MenuOrKeyboardActionHandler handler :
+                mMenuActionHandlers) {
             if (handler.onMenuOrKeyboardAction(id, fromMenu)) return true;
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/MenuOrKeyboardActionController.java b/chrome/android/java/src/org/chromium/chrome/browser/MenuOrKeyboardActionController.java
new file mode 100644
index 0000000..25c544f
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/MenuOrKeyboardActionController.java
@@ -0,0 +1,37 @@
+// 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;
+
+/**
+ * A controller to register/unregister {@link MenuOrKeyboardActionHandler} for menu or keyboard
+ * actions.
+ */
+public interface MenuOrKeyboardActionController {
+    /**
+     * A handler for menu or keyboard actions. Register via
+     * {@link #registerMenuOrKeyboardActionHandler(MenuOrKeyboardActionHandler)}.
+     */
+    interface MenuOrKeyboardActionHandler {
+        /**
+         * Handles menu item selection and keyboard shortcuts.
+         *
+         * @param id The ID of the selected menu item (defined in main_menu.xml) or keyboard
+         *           shortcut (defined in values.xml).
+         * @param fromMenu Whether this was triggered from the menu.
+         * @return Whether the action was handled.
+         */
+        boolean onMenuOrKeyboardAction(int id, boolean fromMenu);
+    }
+
+    /**
+     * @param handler A new {@link MenuOrKeyboardActionHandler} to register.
+     */
+    void registerMenuOrKeyboardActionHandler(MenuOrKeyboardActionHandler handler);
+
+    /**
+     * @param handler A {@link MenuOrKeyboardActionHandler} to unregister.
+     */
+    void unregisterMenuOrKeyboardActionHandler(MenuOrKeyboardActionHandler handler);
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegateImpl.java
index 8870e3b..89837d55 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegateImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegateImpl.java
@@ -41,6 +41,7 @@
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.toolbar.ToolbarManager;
 import org.chromium.chrome.browser.translate.TranslateBridge;
+import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.chrome.browser.util.UrlConstants;
 import org.chromium.components.dom_distiller.core.DomDistillerUrlUtils;
 import org.chromium.ui.base.DeviceFormFactor;
@@ -263,6 +264,9 @@
                 menu.findItem(R.id.close_all_tabs_menu_id)
                         .setEnabled(mTabModelSelector.getTotalTabCount() > 0);
             }
+            if (!FeatureUtilities.isTabGroupsAndroidUiImprovementsEnabled()) {
+                menu.findItem(R.id.menu_group_tabs).setVisible(false);
+            }
         }
 
         // We have to iterate all menu items since same menu item ID may be associated with more
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationBubble.java b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationBubble.java
index 3f36639..c354f3b4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationBubble.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationBubble.java
@@ -42,7 +42,8 @@
         mRippleBackgroundHelper = new RippleBackgroundHelper(this,
                 R.color.navigation_bubble_background_color, R.color.navigation_bubble_ripple_color,
                 getResources().getDimensionPixelSize(R.dimen.navigation_bubble_default_height),
-                R.color.navigation_bubble_stroke_color, R.dimen.navigation_bubble_border_width);
+                R.color.navigation_bubble_stroke_color, R.dimen.navigation_bubble_border_width,
+                getResources().getDimensionPixelSize(R.dimen.navigation_bubble_bg_vertical_inset));
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ButtonPreferenceCompat.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ButtonPreferenceCompat.java
new file mode 100644
index 0000000..80bbabc7
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ButtonPreferenceCompat.java
@@ -0,0 +1,49 @@
+// 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.preferences;
+
+import android.content.Context;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.util.AttributeSet;
+import android.widget.Button;
+
+import org.chromium.chrome.R;
+
+/**
+ * A {@link Preference} that provides button functionality.
+ *
+ * Preference.getOnPreferenceClickListener().onPreferenceClick() is called when the button is
+ * clicked.
+ *
+ * TODO(crbug.com/967022): Remove {@link ButtonPreference} when Preference Support Library
+ * migration is complete in favor of this class.
+ */
+public class ButtonPreferenceCompat extends Preference {
+    /**
+     * Constructor for inflating from XML
+     */
+    public ButtonPreferenceCompat(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setLayoutResource(R.layout.button_preference_layout);
+        setWidgetLayoutResource(R.layout.button_preference_button);
+        setSelectable(true);
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+        Button button = (Button) holder.findViewById(R.id.button_preference);
+        button.setText(getTitle());
+        button.setOnClickListener(v -> {
+            if (getOnPreferenceClickListener() != null) {
+                getOnPreferenceClickListener().onPreferenceClick(ButtonPreferenceCompat.this);
+            }
+        });
+
+        // Prevent triggering an event after tapping any part of the view that is not the button.
+        holder.itemView.setClickable(false);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeBaseListPreferenceCompat.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeBaseListPreferenceCompat.java
new file mode 100644
index 0000000..d55e4d8
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromeBaseListPreferenceCompat.java
@@ -0,0 +1,48 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.preferences;
+
+import android.content.Context;
+import android.support.v7.preference.ListPreference;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.util.AttributeSet;
+
+/**
+ * Contains the basic functionality that should be shared by all ListPreference in Chrome.
+ *
+ * TODO(crbug.com/967022): Remove {@link ChromeBaseListPreference} when Preference Support Library
+ * migration is complete in favor of this class.
+ */
+public class ChromeBaseListPreferenceCompat extends ListPreference {
+    private ManagedPreferenceDelegateCompat mManagedPrefDelegate;
+
+    /**
+     * Constructor for inflating from XML.
+     */
+    public ChromeBaseListPreferenceCompat(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    /**
+     * Sets the ManagedPreferenceDelegate which will determine whether this preference is managed.
+     */
+    public void setManagedPreferenceDelegate(ManagedPreferenceDelegateCompat delegate) {
+        mManagedPrefDelegate = delegate;
+        ManagedPreferencesUtils.initPreference(mManagedPrefDelegate, this);
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+        setSingleLineTitle(false);
+        ManagedPreferencesUtils.onBindViewToPreference(mManagedPrefDelegate, this, holder.itemView);
+    }
+
+    @Override
+    protected void onClick() {
+        if (ManagedPreferencesUtils.onClickPreference(mManagedPrefDelegate, this)) return;
+        super.onClick();
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/Preferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/Preferences.java
index 4abe4bd..ed2070bd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/Preferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/Preferences.java
@@ -318,7 +318,8 @@
      * #getMainFragment()} returned null, which may indicate that the main fragment is a Support
      * Library fragment.
      */
-    private android.support.v4.app.Fragment getMainFragmentCompat() {
+    @VisibleForTesting
+    public android.support.v4.app.Fragment getMainFragmentCompat() {
         return getSupportFragmentManager().findFragmentById(android.R.id.content);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/developer/TracingPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/developer/TracingPreferences.java
index e2417bb..3abdfd42 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/developer/TracingPreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/developer/TracingPreferences.java
@@ -5,10 +5,10 @@
 package org.chromium.chrome.browser.preferences.developer;
 
 import android.os.Bundle;
-import android.preference.ListPreference;
-import android.preference.Preference;
-import android.preference.PreferenceFragment;
 import android.support.annotation.IntDef;
+import android.support.v7.preference.ListPreference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceFragmentCompat;
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.VisibleForTesting;
@@ -27,7 +27,8 @@
 /**
  * Settings fragment that shows options for recording a performance trace.
  */
-public class TracingPreferences extends PreferenceFragment implements TracingController.Observer {
+public class TracingPreferences
+        extends PreferenceFragmentCompat implements TracingController.Observer {
     static final String NON_DEFAULT_CATEGORY_PREFIX = "disabled-by-default-";
 
     @VisibleForTesting
@@ -177,8 +178,7 @@
     }
 
     @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
+    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
         getActivity().setTitle(MSG_TRACING_TITLE);
         PreferenceUtils.addPreferencesFromResource(this, R.xml.tracing_preferences);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
index a83dcc9..83ebebba 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
@@ -9,6 +9,7 @@
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.MenuOrKeyboardActionController;
 import org.chromium.chrome.browser.appmenu.AppMenuBlocker;
 import org.chromium.chrome.browser.appmenu.AppMenuCoordinator;
 import org.chromium.chrome.browser.appmenu.AppMenuCoordinatorFactory;
@@ -23,11 +24,12 @@
  * The specific things this component will manage and how it will hook into Chrome*Activity are
  * still being discussed See https://crbug.com/931496.
  */
-public class RootUiCoordinator implements Destroyable, InflationObserver,
-                                          ChromeActivity.MenuOrKeyboardActionHandler,
-                                          AppMenuBlocker {
+public class RootUiCoordinator
+        implements Destroyable, InflationObserver,
+                   MenuOrKeyboardActionController.MenuOrKeyboardActionHandler, AppMenuBlocker {
     protected ChromeActivity mActivity;
     protected @Nullable AppMenuCoordinator mAppMenuCoordinator;
+    private final MenuOrKeyboardActionController mMenuOrKeyboardActionController;
 
     /**
      * Create a new {@link RootUiCoordinator} for the given activity.
@@ -37,12 +39,13 @@
     public RootUiCoordinator(ChromeActivity activity) {
         mActivity = activity;
         mActivity.getLifecycleDispatcher().register(this);
-        mActivity.registerMenuOrKeyboardActionHandler(this);
+        mMenuOrKeyboardActionController = mActivity.getMenuOrKeyboardActionController();
+        mMenuOrKeyboardActionController.registerMenuOrKeyboardActionHandler(this);
     }
 
     @Override
     public void destroy() {
-        mActivity.unregisterMenuOrKeyboardActionHandler(this);
+        mMenuOrKeyboardActionController.unregisterMenuOrKeyboardActionHandler(this);
         mActivity = null;
         if (mAppMenuCoordinator != null) {
             mAppMenuCoordinator.unregisterAppMenuBlocker(this);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/developer/TracingPreferencesTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/developer/TracingPreferencesTest.java
index 8e3a0d8e3..8f10ccb 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/developer/TracingPreferencesTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/developer/TracingPreferencesTest.java
@@ -8,19 +8,19 @@
 
 import static org.chromium.base.test.util.ScalableTimeout.scaleTimeout;
 
-import android.app.AlertDialog;
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
 import android.preference.CheckBoxPreference;
-import android.preference.ListPreference;
-import android.preference.Preference;
 import android.preference.PreferenceFragment;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
 import android.support.test.filters.SmallTest;
 import android.support.v4.app.NotificationCompat;
+import android.support.v7.preference.ListPreference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceFragmentCompat;
 import android.util.Pair;
 
 import org.junit.After;
@@ -30,18 +30,17 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import org.chromium.base.ContextUtils;
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.DisableIf;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeSwitches;
-import org.chromium.chrome.browser.preferences.ButtonPreference;
+import org.chromium.chrome.browser.preferences.ButtonPreferenceCompat;
 import org.chromium.chrome.browser.preferences.Preferences;
 import org.chromium.chrome.browser.preferences.PreferencesLauncher;
 import org.chromium.chrome.browser.preferences.PreferencesTest;
-import org.chromium.chrome.browser.preferences.TextMessagePreference;
+import org.chromium.chrome.browser.preferences.TextMessagePreferenceCompat;
 import org.chromium.chrome.browser.tracing.TracingController;
 import org.chromium.chrome.browser.tracing.TracingNotificationManager;
 import org.chromium.chrome.test.ChromeActivityTestRule;
@@ -106,7 +105,7 @@
         }, scaleTimeout(15000) /* maxTimeoutMs */, 50 /* checkIntervalMs */);
     }
 
-    private void waitForTracingControllerInitialization(PreferenceFragment fragment)
+    private void waitForTracingControllerInitialization(PreferenceFragmentCompat fragment)
             throws Exception {
         final Preference defaultCategoriesPref =
                 fragment.findPreference(TracingPreferences.UI_PREF_DEFAULT_CATEGORIES);
@@ -146,13 +145,14 @@
     @Feature({"Preferences"})
     @DisableIf.Build(sdk_is_less_than = 21, message = "crbug.com/899894")
     public void testRecordTrace() throws Exception {
-        Context context = ContextUtils.getApplicationContext();
         mActivityTestRule.startMainActivityOnBlankPage();
         Preferences activity =
                 mActivityTestRule.startPreferences(TracingPreferences.class.getName());
-        final PreferenceFragment fragment = (PreferenceFragment) activity.getMainFragment();
-        final ButtonPreference startTracingButton = (ButtonPreference) fragment.findPreference(
-                TracingPreferences.UI_PREF_START_RECORDING);
+        final PreferenceFragmentCompat fragment =
+                (PreferenceFragmentCompat) activity.getMainFragmentCompat();
+        final ButtonPreferenceCompat startTracingButton =
+                (ButtonPreferenceCompat) fragment.findPreference(
+                        TracingPreferences.UI_PREF_START_RECORDING);
 
         waitForTracingControllerInitialization(fragment);
 
@@ -168,7 +168,7 @@
             Assert.assertEquals(TracingPreferences.MSG_START, startTracingButton.getTitle());
 
             // Tap the button to start recording a trace.
-            PreferencesTest.clickPreference(fragment, startTracingButton);
+            startTracingButton.performClick();
 
             Assert.assertEquals(
                     TracingController.State.STARTING, TracingController.getInstance().getState());
@@ -247,14 +247,15 @@
     public void testNotificationsDisabledMessage() throws Exception {
         mMockNotificationManager.setNotificationsEnabled(false);
 
-        Context context = ContextUtils.getApplicationContext();
         Preferences activity =
                 mActivityTestRule.startPreferences(TracingPreferences.class.getName());
-        final PreferenceFragment fragment = (PreferenceFragment) activity.getMainFragment();
-        final ButtonPreference startTracingButton = (ButtonPreference) fragment.findPreference(
-                TracingPreferences.UI_PREF_START_RECORDING);
-        final TextMessagePreference statusPreference =
-                (TextMessagePreference) fragment.findPreference(
+        final PreferenceFragmentCompat fragment =
+                (PreferenceFragmentCompat) activity.getMainFragmentCompat();
+        final ButtonPreferenceCompat startTracingButton =
+                (ButtonPreferenceCompat) fragment.findPreference(
+                        TracingPreferences.UI_PREF_START_RECORDING);
+        final TextMessagePreferenceCompat statusPreference =
+                (TextMessagePreferenceCompat) fragment.findPreference(
                         TracingPreferences.UI_PREF_TRACING_STATUS);
 
         waitForTracingControllerInitialization(fragment);
@@ -274,7 +275,8 @@
         mActivityTestRule.startMainActivityOnBlankPage();
         Preferences activity =
                 mActivityTestRule.startPreferences(TracingPreferences.class.getName());
-        final PreferenceFragment fragment = (PreferenceFragment) activity.getMainFragment();
+        final PreferenceFragmentCompat fragment =
+                (PreferenceFragmentCompat) activity.getMainFragmentCompat();
         final Preference defaultCategoriesPref =
                 fragment.findPreference(TracingPreferences.UI_PREF_DEFAULT_CATEGORIES);
         final Preference nonDefaultCategoriesPref =
@@ -333,10 +335,10 @@
     @SmallTest
     @Feature({"Preferences"})
     public void testSelectMode() throws Exception {
-        Context context = ContextUtils.getApplicationContext();
         Preferences activity =
                 mActivityTestRule.startPreferences(TracingPreferences.class.getName());
-        final PreferenceFragment fragment = (PreferenceFragment) activity.getMainFragment();
+        final PreferenceFragmentCompat fragment =
+                (PreferenceFragmentCompat) activity.getMainFragmentCompat();
         final ListPreference modePref =
                 (ListPreference) fragment.findPreference(TracingPreferences.UI_PREF_MODE);
 
@@ -348,12 +350,8 @@
             // By default, the "record-until-full" mode is selected.
             Assert.assertEquals("record-until-full", TracingPreferences.getSelectedTracingMode());
 
-            // Clicking the pref should open a dialog.
-            PreferencesTest.clickPreference(fragment, modePref);
-            Assert.assertNotNull(modePref.getDialog());
-            AlertDialog dialog = (AlertDialog) modePref.getDialog();
-            Assert.assertEquals(3, dialog.getListView().getAdapter().getCount());
-            modePref.getDialog().dismiss();
+            // Dialog should contain 3 entries.
+            Assert.assertEquals(3, modePref.getEntries().length);
 
             // Simulate changing the mode.
             modePref.getOnPreferenceChangeListener().onPreferenceChange(
diff --git a/chrome/android/webapk/shell_apk/BUILD.gn b/chrome/android/webapk/shell_apk/BUILD.gn
index 414e3eb0..d6f80ad 100644
--- a/chrome/android/webapk/shell_apk/BUILD.gn
+++ b/chrome/android/webapk/shell_apk/BUILD.gn
@@ -4,10 +4,9 @@
 
 import("//build/config/android/rules.gni")
 import("current_version/current_version.gni")
+import("generate_manifest_for_upload_outputs.gni")
 import("mustache_pass.gni")
 
-webapk_manifest_to_upload_output =
-    "${target_gen_dir}/webapk_manifest_to_upload/AndroidManifest.xml"
 h2o_junit_manifest_output =
     "${target_gen_dir}/junit_manifest/AndroidManifest.xml"
 
@@ -21,6 +20,24 @@
   java_files = [ "src/org/chromium/webapk/lib/runtime_library/IWebApkApi.java" ]
 }
 
+mustache_pass("generate_old_style_manifest_for_upload") {
+  input = "AndroidManifest.xml"
+  output = generate_old_style_manifest_for_upload_output
+  extra_variables = [
+    "shell_apk_version=$current_shell_apk_version",
+    "use_new_splash=false",
+  ]
+}
+
+mustache_pass("generate_new_style_manifest_for_upload") {
+  input = "AndroidManifest.xml"
+  output = generate_new_style_manifest_for_upload_output
+  extra_variables = [
+    "shell_apk_version=$current_shell_apk_version",
+    "use_new_splash=true",
+  ]
+}
+
 # |webapk_java| is split from |webapk_with_service_java| for the sake of instrumentation tests.
 # |chrome_public_test_apk| cannot depend on |compiled_in_runtime_library_java| due to a class
 # name conflict.
@@ -78,9 +95,7 @@
 }
 
 template("webapk_tmpl") {
-  _manifest_to_upload_target_name = "${target_name}_manifest_to_upload"
-  _manifest_to_upload_output =
-      "${target_gen_dir}/${_manifest_to_upload_target_name}/AndroidManifest.xml"
+  _manifest_to_upload_dep = invoker.manifest_to_upload_dep
   _manifest_target_name = "${target_name}_generate_manifest"
   _manifest_output =
       "${target_gen_dir}/${_manifest_target_name}/AndroidManifest.xml"
@@ -93,14 +108,6 @@
       "${target_gen_dir}/${_generate_res_background_xml_target_name}/res"
   _resources_target_name = "${target_name}_resources"
 
-  _use_new_splash = false
-  if (defined(invoker.use_new_splash)) {
-    _use_new_splash = invoker.use_new_splash
-  }
-
-  if (defined(invoker.manifest_to_upload_output)) {
-    _manifest_to_upload_output = invoker.manifest_to_upload_output
-  }
   if (defined(invoker.manifest_output)) {
     _manifest_output = invoker.manifest_output
   }
@@ -108,17 +115,6 @@
     _manifest_target_name = invoker.manifest_target_name
   }
 
-  # Generate manifest to upload to WebAPK server. Fills in all of the fields
-  # that the server cannot customize on a per-site basis.
-  mustache_pass(_manifest_to_upload_target_name) {
-    input = "AndroidManifest.xml"
-    output = _manifest_to_upload_output
-    extra_variables = [
-      "shell_apk_version=$current_shell_apk_version",
-      "use_new_splash=$_use_new_splash",
-    ]
-  }
-
   # Generate manifest with test values.
   mustache_pass(_manifest_target_name) {
     forward_variables_from(invoker,
@@ -128,11 +124,12 @@
                              "delta_config_file",
                            ])
 
-    input = _manifest_to_upload_output
+    input = get_target_outputs(_manifest_to_upload_dep)
+    input = input[0]
     output = _manifest_output
     extra_variables = [ "manifest_package=$apk_package_name" ]
     deps = [
-      ":$_manifest_to_upload_target_name",
+      _manifest_to_upload_dep,
     ]
   }
 
@@ -265,26 +262,28 @@
 # - App icon is extracted from the website and added to the APK's resources.
 webapk_tmpl("webapk") {
   config_file = "manifest/bound_manifest_config.json"
+  manifest_to_upload_dep = ":generate_old_style_manifest_for_upload"
   apk_name = "WebApk"
   apk_package_name = "org.chromium.webapk"
-  manifest_to_upload_output = webapk_manifest_to_upload_output
 }
 
 webapk_tmpl("maps_go_webapk") {
   config_file = "manifest/maps_go_manifest_config.json"
+  manifest_to_upload_dep = ":generate_old_style_manifest_for_upload"
   apk_name = "MapsWebApk"
   apk_package_name = "org.chromium.maps_go_webapk"
 }
 
 webapk_tmpl("unbound_webapk") {
   config_file = "manifest/unbound_manifest_config.json"
+  manifest_to_upload_dep = ":generate_old_style_manifest_for_upload"
   apk_name = "UnboundWebApk"
   apk_package_name = "org.chromium.arbitrarypackage"
 }
 
 webapk_tmpl("new_splash_webapk") {
-  use_new_splash = true
   config_file = "manifest/bound_manifest_config.json"
+  manifest_to_upload_dep = ":generate_new_style_manifest_for_upload"
   apk_name = "NewSplashWebApk"
   apk_package_name = "org.chromium.webapk.new.splash"
 }
@@ -292,6 +291,7 @@
 # Used by javatests
 webapk_tmpl("javatests_webapk") {
   config_file = "manifest/bound_manifest_config.json"
+  manifest_to_upload_dep = ":generate_old_style_manifest_for_upload"
   delta_config_file = "manifest/javatest_manifest_config_delta.json"
   apk_name = "JavatestsWebApk"
   apk_package_name = "org.chromium.webapk.test"
@@ -299,8 +299,8 @@
 
 # Used by webapk_shell_apk_h2o_junit_tests
 webapk_tmpl("h2o_j_unit_webapk") {
-  use_new_splash = true
   config_file = "manifest/bound_manifest_config.json"
+  manifest_to_upload_dep = ":generate_new_style_manifest_for_upload"
   apk_name = "H2OJUnitWebApk"
   apk_package_name = "org.chromium.webapk.h2o.junit_webapk"
   manifest_output = h2o_junit_manifest_output
diff --git a/chrome/android/webapk/shell_apk/generate_manifest_for_upload_outputs.gni b/chrome/android/webapk/shell_apk/generate_manifest_for_upload_outputs.gni
new file mode 100644
index 0000000..a4528ae
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/generate_manifest_for_upload_outputs.gni
@@ -0,0 +1,9 @@
+# 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.
+
+# Output for ":generate_old_style_manifest_for_upload"
+generate_old_style_manifest_for_upload_output = "${root_gen_dir}/chrome/android/webapk/shell_apk/webapk_manifest_to_upload/AndroidManifest.xml"
+
+# Output for ":generate_new_style_manifest_for_upload"
+generate_new_style_manifest_for_upload_output = "${root_gen_dir}/chrome/android/webapk/shell_apk/webapk_new_style_manifest_to_upload/AndroidManifest.xml"
diff --git a/chrome/android/webapk/shell_apk/prepare_upload_dir/BUILD.gn b/chrome/android/webapk/shell_apk/prepare_upload_dir/BUILD.gn
new file mode 100644
index 0000000..6dbef58
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/prepare_upload_dir/BUILD.gn
@@ -0,0 +1,67 @@
+# 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("//build/config/android/copy_ex.gni")
+import(
+    "//chrome/android/webapk/shell_apk/generate_manifest_for_upload_outputs.gni")
+
+upload_dir = "${target_gen_dir}/upload_to_build_bucket"
+
+# Copies |source_dir| to |destination_dir|.
+template("copy_dir") {
+  copy_ex(target_name) {
+    source_dir = invoker.source_dir
+    dest = invoker.destination_dir
+
+    _sources_rel_build_rel =
+        exec_script("//build/android/gyp/find.py",
+                    [ rebase_path(source_dir, root_build_dir) ],
+                    "list lines")
+    renaming_sources = rebase_path(_sources_rel_build_rel, ".", root_build_dir)
+    renaming_destinations = rebase_path(renaming_sources, source_dir)
+  }
+}
+
+copy_dir("copy_res_to_upload_dir") {
+  source_dir = "//chrome/android/webapk/shell_apk/res"
+  destination_dir = "${upload_dir}/res"
+}
+
+copy_dir("copy_res_template_to_upload_dir") {
+  source_dir = "//chrome/android/webapk/shell_apk/res_template"
+  destination_dir = "${upload_dir}/res"
+}
+
+copy_dir("copy_libs_common_res_splash_to_upload_dir") {
+  source_dir = "//chrome/android/webapk/libs/common/res_splash"
+  destination_dir = "${upload_dir}/res"
+}
+
+copy_ex("copy_extra_files_to_upload_dir") {
+  dest = upload_dir
+
+  renaming_sources = [ generate_old_style_manifest_for_upload_output ]
+  renaming_destinations = [ "AndroidManifest.xml" ]
+
+  renaming_sources += [ generate_new_style_manifest_for_upload_output ]
+  renaming_destinations += [ "new_style_AndroidManifest.xml" ]
+
+  renaming_sources += [ "${root_out_dir}/resource_zips/chrome/android/webapk/shell_apk/webapk_strings_grd.resources.zip" ]
+  renaming_destinations += [ "res/strings.zip" ]
+
+  deps = [
+    "//chrome/android/webapk/shell_apk:generate_new_style_manifest_for_upload",
+    "//chrome/android/webapk/shell_apk:generate_old_style_manifest_for_upload",
+    "//chrome/android/webapk/shell_apk:webapk_strings_grd",
+  ]
+}
+
+group("prepare_webapk_shell_upload_dir") {
+  deps = [
+    ":copy_extra_files_to_upload_dir",
+    ":copy_libs_common_res_splash_to_upload_dir",
+    ":copy_res_template_to_upload_dir",
+    ":copy_res_to_upload_dir",
+  ]
+}
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 5954305..e1a1c6e 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -4574,20 +4574,6 @@
          Updated just now
       </message>
 
-      <!-- Page Capping InfoBar -->
-      <message name="IDS_PAGE_CAPPING_TITLE" desc="The text of the infobar notifying the user that the page uses a lot of network data.">
-        This page will use a lot of data.
-      </message>
-      <message name="IDS_PAGE_CAPPING_STOPPED_TITLE" desc="The text of the infobar indicating that the user has chosen to pause resource loading on the page. This infobar is shown in response to clicking on a link in a different infobar that informed the user the page used a lot of data.">
-         Page paused.
-      </message>
-      <message name="IDS_PAGE_CAPPING_CONTINUE_MESSAGE" desc="A link on an infobar that allows the resource loading to continue on the page. Clicking the link resumes resource loading on the page. This infobar is shown in response to clicking on a link in a different infobar that informed the user the page used a lot of data.">
-         Continue
-      </message>
-      <message name="IDS_PAGE_CAPPING_STOP_MESSAGE" desc="A link on the of infobar notifying the user that data usage on the page is beyond a certain threshold. Clicking the link pauses the resource loading on the page.">
-         Stop using data on this page
-      </message>
-
       <!-- HTTPS Server Previews InfoBar -->
       <message name="IDS_LITE_PAGE_PREVIEWS_MESSAGE" desc="The text of the infobar notifying the user that Chrome's Lite mode will now also apply to HTTPS pages.">
         Lite mode now makes browsing faster on all pages, including HTTPS.
diff --git a/chrome/app/theme/chrome_unscaled_resources.grd b/chrome/app/theme/chrome_unscaled_resources.grd
index d3c43a7..096b39b 100644
--- a/chrome/app/theme/chrome_unscaled_resources.grd
+++ b/chrome/app/theme/chrome_unscaled_resources.grd
@@ -46,6 +46,21 @@
           <include name="IDR_WEBSTORE_APP_ICON_16" file="google_chrome/chromeos/webstore_app_icon_16.png" type="BINDATA" />
           <include name="IDR_WEBSTORE_APP_ICON_128" file="google_chrome/chromeos/webstore_app_icon_128.png" type="BINDATA" />
         </if>
+        <if expr="not chromeos">
+          <include name="IDR_WELCOME_MODULE_ICONS_GOOGLE_DARK" file="google_chrome/welcome/module_icons/google_dark.svg" type="BINDATA" />
+          <include name="IDR_WELCOME_MODULE_ICONS_GOOGLE_LIGHT" file="google_chrome/welcome/module_icons/google_light.svg" type="BINDATA" />
+          <include name="IDR_WELCOME_MODULE_ICONS_SET_DEFAULT_DARK" file="google_chrome/welcome/module_icons/set_default_dark.svg" type="BINDATA" />
+          <include name="IDR_WELCOME_MODULE_ICONS_SET_DEFAULT_LIGHT" file="google_chrome/welcome/module_icons/set_default_light.svg" type="BINDATA" />
+          <include name="IDR_WELCOME_MODULE_ICONS_WALLPAPER_DARK" file="google_chrome/welcome/module_icons/wallpaper_dark.svg" type="BINDATA" />
+          <include name="IDR_WELCOME_MODULE_ICONS_WALLPAPER_LIGHT" file="google_chrome/welcome/module_icons/wallpaper_light.svg" type="BINDATA" />
+          <include name="IDR_WELCOME_NTP_THUMBNAILS_ART" file="google_chrome/welcome/ntp_thumbnails/art.jpg" type="BINDATA" />
+          <include name="IDR_WELCOME_NTP_THUMBNAILS_CITYSCAPE" file="google_chrome/welcome/ntp_thumbnails/cityscape.jpg" type="BINDATA" />
+          <include name="IDR_WELCOME_NTP_THUMBNAILS_EARTH" file="google_chrome/welcome/ntp_thumbnails/earth.jpg" type="BINDATA" />
+          <include name="IDR_WELCOME_NTP_THUMBNAILS_GEOMETRIC_SHAPES" file="google_chrome/welcome/ntp_thumbnails/geometric_shapes.jpg" type="BINDATA" />
+          <include name="IDR_WELCOME_NTP_THUMBNAILS_LANDSCAPE" file="google_chrome/welcome/ntp_thumbnails/landscape.jpg" type="BINDATA" />
+          <include name="IDR_WELCOME_SET_DEFAULT_DARK" file="google_chrome/welcome/set_default_dark.svg" type="BINDATA" />
+          <include name="IDR_WELCOME_SET_DEFAULT_LIGHT" file="google_chrome/welcome/set_default_light.svg" type="BINDATA" />
+        </if>
       </if>
       <if expr="not _google_chrome">
         <include name="IDR_PRODUCT_LOGO_64" file="chromium/product_logo_64.png" type="BINDATA" />
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index e3a635c..fea6204 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -953,8 +953,6 @@
     "page_load_metrics/observers/loading_predictor_page_load_metrics_observer.h",
     "page_load_metrics/observers/local_network_requests_page_load_metrics_observer.cc",
     "page_load_metrics/observers/local_network_requests_page_load_metrics_observer.h",
-    "page_load_metrics/observers/lofi_page_load_metrics_observer.cc",
-    "page_load_metrics/observers/lofi_page_load_metrics_observer.h",
     "page_load_metrics/observers/media_page_load_metrics_observer.cc",
     "page_load_metrics/observers/media_page_load_metrics_observer.h",
     "page_load_metrics/observers/multi_tab_loading_page_load_metrics_observer.cc",
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index fdc6c18..4ba72dca 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -92,8 +92,8 @@
   "+services/strings",
   "+services/tracing/public/cpp",
   "+services/video_capture/public",
+  "+services/viz/public",
   "+services/viz/privileged",
-  "+services/viz/public/interfaces",
   "+services/ws/public",
   "+skia/ext",
   "+third_party/boringssl/src/include",
diff --git a/chrome/browser/android/download/download_media_parser.cc b/chrome/browser/android/download/download_media_parser.cc
index defe079a..4d72b46 100644
--- a/chrome/browser/android/download/download_media_parser.cc
+++ b/chrome/browser/android/download/download_media_parser.cc
@@ -24,7 +24,7 @@
 #include "media/renderers/paint_canvas_video_renderer.h"
 #include "media/video/gpu_video_accelerator_factories.h"
 #include "services/service_manager/public/cpp/connector.h"
-#include "services/ws/public/cpp/gpu/context_provider_command_buffer.h"
+#include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 
 namespace {
diff --git a/chrome/browser/android/vr/BUILD.gn b/chrome/browser/android/vr/BUILD.gn
index cb2da4c..384c9c5 100644
--- a/chrome/browser/android/vr/BUILD.gn
+++ b/chrome/browser/android/vr/BUILD.gn
@@ -111,7 +111,7 @@
     "//device/vr/buildflags:buildflags",
     "//services/device/public/mojom",
     "//services/metrics/public/cpp:ukm_builders",
-    "//services/ws/public/cpp/gpu",
+    "//services/viz/public/cpp/gpu",
     "//third_party/gvr-android-sdk:gvr_shim",
     "//ui/android",
     "//ui/base",
diff --git a/chrome/browser/android/vr/arcore_device/arcore.h b/chrome/browser/android/vr/arcore_device/arcore.h
index 2964849..4e0136a 100644
--- a/chrome/browser/android/vr/arcore_device/arcore.h
+++ b/chrome/browser/android/vr/arcore_device/arcore.h
@@ -44,8 +44,8 @@
   // when the camera image was updated successfully.
   virtual mojom::VRPosePtr Update(bool* camera_updated) = 0;
 
-  // Returns all planes detected in the current frame.
-  virtual std::vector<mojom::XRPlaneDataPtr> GetDetectedPlanes() = 0;
+  // Returns information about all planes detected in the current frame.
+  virtual mojom::XRPlaneDetectionDataPtr GetDetectedPlanesData() = 0;
 
   virtual bool RequestHitTest(
       const mojom::XRRayPtr& ray,
diff --git a/chrome/browser/android/vr/arcore_device/arcore_gl.cc b/chrome/browser/android/vr/arcore_device/arcore_gl.cc
index 9478c21..1476dd0 100644
--- a/chrome/browser/android/vr/arcore_device/arcore_gl.cc
+++ b/chrome/browser/android/vr/arcore_device/arcore_gl.cc
@@ -399,9 +399,7 @@
   frame_data->time_delta = base::TimeTicks::Now() - base::TimeTicks();
 
   if (options && options->include_plane_data) {
-    frame_data->detected_planes = arcore_->GetDetectedPlanes();
-    DVLOG(3) << __func__ << ": detected planes size="
-             << frame_data->detected_planes.value().size();
+    frame_data->detected_planes_data = arcore_->GetDetectedPlanesData();
   }
 
   fps_meter_.AddFrame(base::TimeTicks::Now());
diff --git a/chrome/browser/android/vr/arcore_device/arcore_impl.cc b/chrome/browser/android/vr/arcore_device/arcore_impl.cc
index c4dd405..4b02fd4 100644
--- a/chrome/browser/android/vr/arcore_device/arcore_impl.cc
+++ b/chrome/browser/android/vr/arcore_device/arcore_impl.cc
@@ -186,9 +186,7 @@
   return GetMojomPoseFromArPose(arcore_session_.get(), std::move(arcore_pose));
 }
 
-std::vector<mojom::XRPlaneDataPtr> ArCoreImpl::GetDetectedPlanes() {
-  std::vector<mojom::XRPlaneDataPtr> result;
-
+void ArCoreImpl::EnsureArCorePlanesList() {
   if (!arcore_planes_.is_valid()) {
     ArTrackableList_create(
         arcore_session_.get(),
@@ -196,10 +194,11 @@
             .get());
     DCHECK(arcore_planes_.is_valid());
   }
+}
 
-  ArTrackableType plane_tracked_type = AR_TRACKABLE_PLANE;
-  ArSession_getAllTrackables(arcore_session_.get(), plane_tracked_type,
-                             arcore_planes_.get());
+template <typename FunctionType>
+void ArCoreImpl::ForEachArCorePlane(FunctionType fn) {
+  DCHECK(arcore_planes_.is_valid());
 
   int32_t trackable_list_size;
   ArTrackableList_getSize(arcore_session_.get(), arcore_planes_.get(),
@@ -220,6 +219,13 @@
       continue;
     }
 
+    if (DCHECK_IS_ON()) {
+      ArTrackableType type;
+      ArTrackable_getType(arcore_session_.get(), trackable.get(), &type);
+      DCHECK(type == ArTrackableType::AR_TRACKABLE_PLANE)
+          << "arcore_planes_ contains a trackable that is not an ArPlane!";
+    }
+
     ArPlane* ar_plane =
         ArAsPlane(trackable.get());  // Naked pointer is fine here, ArAsPlane
                                      // does not increase ref count.
@@ -236,6 +242,20 @@
       continue;
     }
 
+    fn(ar_plane);
+  }
+}
+
+std::vector<mojom::XRPlaneDataPtr> ArCoreImpl::GetUpdatedPlanesData() {
+  EnsureArCorePlanesList();
+
+  std::vector<mojom::XRPlaneDataPtr> result;
+
+  ArTrackableType plane_tracked_type = AR_TRACKABLE_PLANE;
+  ArFrame_getUpdatedTrackables(arcore_session_.get(), arcore_frame_.get(),
+                               plane_tracked_type, arcore_planes_.get());
+
+  ForEachArCorePlane([this, &result](ArPlane* ar_plane) {
     // orientation
     ArPlaneType plane_type;
     ArPlane_getType(arcore_session_.get(), ar_plane, &plane_type);
@@ -267,30 +287,66 @@
     }
 
     // id
-    int32_t plane_id = CreateOrGetPlaneId(ar_plane);
+    int32_t plane_id;
+    bool created;
+    std::tie(plane_id, created) = CreateOrGetPlaneId(ar_plane);
 
     result.push_back(mojom::XRPlaneData::New(
         plane_id,
         mojo::ConvertTo<device::mojom::XRPlaneOrientation>(plane_type),
         std::move(pose), std::move(vertices)));
-  }
+  });
 
   return result;
 }
 
-int32_t ArCoreImpl::CreateOrGetPlaneId(void* plane_address) {
+std::vector<int32_t> ArCoreImpl::GetAllPlaneIds() {
+  EnsureArCorePlanesList();
+
+  std::vector<int32_t> result;
+
+  ArTrackableType plane_tracked_type = AR_TRACKABLE_PLANE;
+  ArSession_getAllTrackables(arcore_session_.get(), plane_tracked_type,
+                             arcore_planes_.get());
+
+  ForEachArCorePlane([this, &result](ArPlane* ar_plane) {
+    // id
+    int32_t plane_id;
+    bool created;
+    std::tie(plane_id, created) = CreateOrGetPlaneId(ar_plane);
+
+    // Newly detected planes should be handled by GetUpdatedPlanesData().
+    DCHECK(!created);
+
+    result.emplace_back(plane_id);
+  });
+
+  return result;
+}
+
+mojom::XRPlaneDetectionDataPtr ArCoreImpl::GetDetectedPlanesData() {
+  TRACE_EVENT0("gpu", __FUNCTION__);
+
+  std::vector<mojom::XRPlaneDataPtr> updated_planes = GetUpdatedPlanesData();
+  std::vector<int32_t> all_plane_ids = GetAllPlaneIds();
+
+  return mojom::XRPlaneDetectionData::New(all_plane_ids,
+                                          std::move(updated_planes));
+}
+
+std::pair<int32_t, bool> ArCoreImpl::CreateOrGetPlaneId(void* plane_address) {
   auto it = ar_plane_address_to_id_.find(plane_address);
   if (it != ar_plane_address_to_id_.end()) {
-    return it->second;
+    return std::make_pair(it->second, false);
   }
 
   // Make sure that incrementing next_id_ won't cause an overflow.
   CHECK(next_id_ != std::numeric_limits<int32_t>::max());
 
   int32_t current_id = next_id_++;
-  ar_plane_address_to_id_[plane_address] = current_id;
+  ar_plane_address_to_id_.emplace(plane_address, current_id);
 
-  return current_id;
+  return std::make_pair(current_id, true);
 }
 
 void ArCoreImpl::Pause() {
diff --git a/chrome/browser/android/vr/arcore_device/arcore_impl.h b/chrome/browser/android/vr/arcore_device/arcore_impl.h
index 03806fb..013418d 100644
--- a/chrome/browser/android/vr/arcore_device/arcore_impl.h
+++ b/chrome/browser/android/vr/arcore_device/arcore_impl.h
@@ -98,7 +98,7 @@
       const base::span<const float> uvs) override;
   gfx::Transform GetProjectionMatrix(float near, float far) override;
   mojom::VRPosePtr Update(bool* camera_updated) override;
-  std::vector<mojom::XRPlaneDataPtr> GetDetectedPlanes() override;
+  mojom::XRPlaneDetectionDataPtr GetDetectedPlanesData() override;
   void Pause() override;
   void Resume() override;
   bool RequestHitTest(const mojom::XRRayPtr& ray,
@@ -118,14 +118,36 @@
   internal::ScopedArCoreObject<ArSession*> arcore_session_;
   internal::ScopedArCoreObject<ArFrame*> arcore_frame_;
 
-  // List of trackables - used for retrieving planes detected by ARCore. May be
-  // null.
+  // List of trackables - used for retrieving planes detected by ARCore. The
+  // list will initially be null - call EnsureArCorePlanesList() before using
+  // it. Instead of creating it in every call to GetUpdatedPlanesData(), the
+  // list can be reused as it will be cleaned by calls to ArCore SDK.
   internal::ScopedArCoreObject<ArTrackableList*> arcore_planes_;
 
+  // Initializes |arcore_planes_| list.
+  void EnsureArCorePlanesList();
+
+  // Returns vector containing information about all planes updated in current
+  // frame, assigning IDs for newly detected planes as needed.
+  std::vector<mojom::XRPlaneDataPtr> GetUpdatedPlanesData();
+
+  // This must be called after GetUpdatedPlanesData as it assumes that all
+  // planes already have an ID assigned. The result includes freshly assigned
+  // IDs for newly detected planes along with previously known IDs for updated
+  // and unchanged planes. It excludes planes that are no longer being tracked.
+  std::vector<int32_t> GetAllPlaneIds();
+
   int32_t next_id_ = 1;
   std::unordered_map<void*, int32_t> ar_plane_address_to_id_;
 
-  int32_t CreateOrGetPlaneId(void* plane_address);
+  // Returns tuple containing plane id and a boolean signifying that the plane
+  // was created.
+  std::pair<int32_t, bool> CreateOrGetPlaneId(void* plane_address);
+
+  // Executes |fn| for each still tracked, non-subsumed plane present in
+  // |arcore_planes_|.
+  template <typename FunctionType>
+  void ForEachArCorePlane(FunctionType fn);
 
   // Must be last.
   base::WeakPtrFactory<ArCoreImpl> weak_ptr_factory_;
diff --git a/chrome/browser/android/vr/arcore_device/arcore_shim.cc b/chrome/browser/android/vr/arcore_device/arcore_shim.cc
index f97825c0..f6b01197 100644
--- a/chrome/browser/android/vr/arcore_device/arcore_shim.cc
+++ b/chrome/browser/android/vr/arcore_device/arcore_shim.cc
@@ -22,6 +22,7 @@
   CALL(ArFrame_acquireCamera)           \
   CALL(ArFrame_create)                  \
   CALL(ArFrame_destroy)                 \
+  CALL(ArFrame_getUpdatedTrackables)    \
   CALL(ArFrame_hitTestRay)              \
   CALL(ArFrame_transformCoordinates2d)  \
   CALL(ArHitResult_acquireTrackable)    \
@@ -162,6 +163,14 @@
   arcore_api->impl_ArFrame_destroy(frame);
 }
 
+void ArFrame_getUpdatedTrackables(const ArSession* session,
+                                  const ArFrame* frame,
+                                  ArTrackableType filter_type,
+                                  ArTrackableList* out_trackable_list) {
+  arcore_api->impl_ArFrame_getUpdatedTrackables(session, frame, filter_type,
+                                                out_trackable_list);
+}
+
 void ArFrame_hitTestRay(const ArSession* session,
                         const ArFrame* frame,
                         const float* ray_origin_3,
diff --git a/chrome/browser/android/vr/arcore_device/fake_arcore.cc b/chrome/browser/android/vr/arcore_device/fake_arcore.cc
index 27e1451..de1dd36 100644
--- a/chrome/browser/android/vr/arcore_device/fake_arcore.cc
+++ b/chrome/browser/android/vr/arcore_device/fake_arcore.cc
@@ -218,7 +218,7 @@
   return true;
 }
 
-std::vector<mojom::XRPlaneDataPtr> FakeArCore::GetDetectedPlanes() {
+mojom::XRPlaneDetectionDataPtr FakeArCore::GetDetectedPlanesData() {
   std::vector<mojom::XRPlaneDataPtr> result;
 
   // 1m ahead of the origin, neutral orientation facing forward.
@@ -242,7 +242,9 @@
   result.push_back(
       mojom::XRPlaneData::New(1, device::mojom::XRPlaneOrientation::HORIZONTAL,
                               std::move(pose), std::move(vertices)));
-  return result;
+
+  return mojom::XRPlaneDetectionData::New(std::vector<int32_t>{1},
+                                          std::move(result));
 }
 
 void FakeArCore::Pause() {
diff --git a/chrome/browser/android/vr/arcore_device/fake_arcore.h b/chrome/browser/android/vr/arcore_device/fake_arcore.h
index 2f5e8c2d..d906268 100644
--- a/chrome/browser/android/vr/arcore_device/fake_arcore.h
+++ b/chrome/browser/android/vr/arcore_device/fake_arcore.h
@@ -37,7 +37,7 @@
   bool RequestHitTest(const mojom::XRRayPtr& ray,
                       std::vector<mojom::XRHitResultPtr>* hit_results) override;
 
-  std::vector<mojom::XRPlaneDataPtr> GetDetectedPlanes() override;
+  mojom::XRPlaneDetectionDataPtr GetDetectedPlanesData() override;
 
   void SetCameraAspect(float aspect) { camera_aspect_ = aspect; }
 
diff --git a/chrome/browser/android/vr/mailbox_to_surface_bridge.cc b/chrome/browser/android/vr/mailbox_to_surface_bridge.cc
index 50cfde24..6dd3872d 100644
--- a/chrome/browser/android/vr/mailbox_to_surface_bridge.cc
+++ b/chrome/browser/android/vr/mailbox_to_surface_bridge.cc
@@ -27,7 +27,7 @@
 #include "gpu/ipc/client/gpu_channel_host.h"
 #include "gpu/ipc/common/gpu_memory_buffer_impl_android_hardware_buffer.h"
 #include "gpu/ipc/common/gpu_surface_tracker.h"
-#include "services/ws/public/cpp/gpu/context_provider_command_buffer.h"
+#include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
 #include "ui/gl/android/surface_texture.h"
 
 #include <android/native_window_jni.h>
diff --git a/chrome/browser/apps/guest_view/app_view_browsertest.cc b/chrome/browser/apps/guest_view/app_view_browsertest.cc
index 26e78a0f..00e553e 100644
--- a/chrome/browser/apps/guest_view/app_view_browsertest.cc
+++ b/chrome/browser/apps/guest_view/app_view_browsertest.cc
@@ -19,6 +19,7 @@
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/test/browser_test_utils.h"
+#include "content/public/test/no_renderer_crashes_assertion.h"
 #include "content/public/test/test_utils.h"
 #include "extensions/browser/api/app_runtime/app_runtime_api.h"
 #include "extensions/browser/api/extensions_api_client.h"
@@ -75,6 +76,7 @@
   content::RenderProcessHost* observed_host_;
   scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
   base::TerminationStatus status_;
+  content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes_;
 
   DISALLOW_COPY_AND_ASSIGN(RenderProcessHostObserverForExit);
 };
diff --git a/chrome/browser/apps/guest_view/web_view_browsertest.cc b/chrome/browser/apps/guest_view/web_view_browsertest.cc
index 049d4c3..06ffc48 100644
--- a/chrome/browser/apps/guest_view/web_view_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_browsertest.cc
@@ -81,6 +81,7 @@
 #include "content/public/test/fake_speech_recognition_manager.h"
 #include "content/public/test/find_test_utils.h"
 #include "content/public/test/hit_test_region_observer.h"
+#include "content/public/test/no_renderer_crashes_assertion.h"
 #include "content/public/test/test_file_error_injector.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "content/public/test/test_renderer_host.h"
@@ -1431,6 +1432,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestEventName) {
+  content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
   TestHelper("testEventName", "web_view/shim", NO_TEST_SERVER);
 }
 
@@ -1500,6 +1502,7 @@
 IN_PROC_BROWSER_TEST_F(
     WebViewTest,
     Shim_TestContentScriptIsInjectedAfterTerminateAndReloadWebView) {
+  content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
   TestHelper("testContentScriptIsInjectedAfterTerminateAndReloadWebView",
              "web_view/shim", NEEDS_TEST_SERVER);
 }
@@ -1548,10 +1551,12 @@
 }
 
 IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestTerminateAfterExit) {
+  content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
   TestHelper("testTerminateAfterExit", "web_view/shim", NO_TEST_SERVER);
 }
 
 IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestAssignSrcAfterCrash) {
+  content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
   TestHelper("testAssignSrcAfterCrash", "web_view/shim", NO_TEST_SERVER);
 }
 
@@ -1774,6 +1779,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestReloadAfterTerminate) {
+  content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
   TestHelper("testReloadAfterTerminate", "web_view/shim", NO_TEST_SERVER);
 }
 
@@ -1786,6 +1792,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestRemoveWebviewOnExit) {
+  content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
   ASSERT_TRUE(StartEmbeddedTestServer());  // For serving guest pages.
 
   // Launch the app and wait until it's ready to load a test.
diff --git a/chrome/browser/apps/platform_apps/api/media_galleries/media_galleries_apitest.cc b/chrome/browser/apps/platform_apps/api/media_galleries/media_galleries_apitest.cc
index 915d672..38e5b5a 100644
--- a/chrome/browser/apps/platform_apps/api/media_galleries/media_galleries_apitest.cc
+++ b/chrome/browser/apps/platform_apps/api/media_galleries/media_galleries_apitest.cc
@@ -271,7 +271,7 @@
 
   extensions::ResultCatcher catcher;
   AppLaunchParams params(
-      browser()->profile(), extension, extensions::LAUNCH_CONTAINER_NONE,
+      browser()->profile(), extension->id(), extensions::LAUNCH_CONTAINER_NONE,
       WindowOpenDisposition::NEW_WINDOW, extensions::SOURCE_TEST);
   params.command_line = *base::CommandLine::ForCurrentProcess();
   OpenApplication(params);
diff --git a/chrome/browser/apps/platform_apps/app_browsertest.cc b/chrome/browser/apps/platform_apps/app_browsertest.cc
index d2c92413e..9819327 100644
--- a/chrome/browser/apps/platform_apps/app_browsertest.cc
+++ b/chrome/browser/apps/platform_apps/app_browsertest.cc
@@ -224,9 +224,10 @@
       return false;
     }
 
-    AppLaunchParams params(
-        browser()->profile(), extension, extensions::LAUNCH_CONTAINER_NONE,
-        WindowOpenDisposition::NEW_WINDOW, extensions::SOURCE_TEST);
+    AppLaunchParams params(browser()->profile(), extension->id(),
+                           extensions::LAUNCH_CONTAINER_NONE,
+                           WindowOpenDisposition::NEW_WINDOW,
+                           extensions::SOURCE_TEST);
     params.command_line = command_line;
     params.current_directory = test_data_dir_;
     OpenApplication(params);
@@ -864,7 +865,7 @@
         content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
         content::NotificationService::AllSources());
     OpenApplication(AppLaunchParams(
-        browser()->profile(), extension, LAUNCH_CONTAINER_NONE,
+        browser()->profile(), extension->id(), LAUNCH_CONTAINER_NONE,
         WindowOpenDisposition::NEW_WINDOW, extensions::SOURCE_TEST));
     app_loaded_observer.Wait();
     window = GetFirstAppWindow();
@@ -1010,7 +1011,7 @@
 
   ExtensionTestMessageListener launched_listener("Launched", false);
   OpenApplication(AppLaunchParams(
-      browser()->profile(), extension, LAUNCH_CONTAINER_NONE,
+      browser()->profile(), extension->id(), LAUNCH_CONTAINER_NONE,
       WindowOpenDisposition::NEW_WINDOW, extensions::SOURCE_TEST));
 
   ASSERT_TRUE(launched_listener.WaitUntilSatisfied());
@@ -1032,7 +1033,7 @@
 
   ExtensionTestMessageListener launched_listener("Launched", false);
   OpenApplication(AppLaunchParams(
-      browser()->profile(), extension, LAUNCH_CONTAINER_NONE,
+      browser()->profile(), extension->id(), LAUNCH_CONTAINER_NONE,
       WindowOpenDisposition::NEW_WINDOW, extensions::SOURCE_TEST));
 
   ASSERT_TRUE(launched_listener.WaitUntilSatisfied());
@@ -1070,7 +1071,7 @@
 
   ExtensionTestMessageListener launched_listener("Launched", false);
   OpenApplication(AppLaunchParams(
-      browser()->profile(), extension, LAUNCH_CONTAINER_NONE,
+      browser()->profile(), extension->id(), LAUNCH_CONTAINER_NONE,
       WindowOpenDisposition::NEW_WINDOW, extensions::SOURCE_TEST));
 
   ASSERT_TRUE(launched_listener.WaitUntilSatisfied());
@@ -1095,7 +1096,7 @@
   {
     ExtensionTestMessageListener launched_listener("Launched", false);
     OpenApplication(AppLaunchParams(
-        browser()->profile(), extension, LAUNCH_CONTAINER_NONE,
+        browser()->profile(), extension->id(), LAUNCH_CONTAINER_NONE,
         WindowOpenDisposition::NEW_WINDOW, extensions::SOURCE_TEST));
     ASSERT_TRUE(launched_listener.WaitUntilSatisfied());
   }
diff --git a/chrome/browser/apps/platform_apps/app_browsertest_util.cc b/chrome/browser/apps/platform_apps/app_browsertest_util.cc
index 4403bdc..e64772a 100644
--- a/chrome/browser/apps/platform_apps/app_browsertest_util.cc
+++ b/chrome/browser/apps/platform_apps/app_browsertest_util.cc
@@ -155,7 +155,7 @@
 
 void PlatformAppBrowserTest::LaunchPlatformApp(const Extension* extension) {
   OpenApplication(AppLaunchParams(
-      browser()->profile(), extension, LAUNCH_CONTAINER_NONE,
+      browser()->profile(), extension->id(), LAUNCH_CONTAINER_NONE,
       WindowOpenDisposition::NEW_WINDOW, extensions::SOURCE_TEST));
 }
 
diff --git a/chrome/browser/apps/platform_apps/app_window_browsertest.cc b/chrome/browser/apps/platform_apps/app_window_browsertest.cc
index 06242268..a766da452 100644
--- a/chrome/browser/apps/platform_apps/app_window_browsertest.cc
+++ b/chrome/browser/apps/platform_apps/app_window_browsertest.cc
@@ -239,7 +239,7 @@
   EXPECT_TRUE(extension);
 
   OpenApplication(AppLaunchParams(
-      browser()->profile(), extension, extensions::LAUNCH_CONTAINER_NONE,
+      browser()->profile(), extension->id(), extensions::LAUNCH_CONTAINER_NONE,
       WindowOpenDisposition::NEW_WINDOW, extensions::SOURCE_TEST));
 
   ExtensionTestMessageListener geometry_listener("ListenGeometryChange", true);
diff --git a/chrome/browser/apps/platform_apps/platform_app_launch.cc b/chrome/browser/apps/platform_apps/platform_app_launch.cc
index 34ac465..e7ff9ec 100644
--- a/chrome/browser/apps/platform_apps/platform_app_launch.cc
+++ b/chrome/browser/apps/platform_apps/platform_app_launch.cc
@@ -77,7 +77,7 @@
 
   RecordCmdLineAppHistogram(app->GetType());
 
-  ::AppLaunchParams params(profile, app, launch_container,
+  ::AppLaunchParams params(profile, app_id, launch_container,
                            WindowOpenDisposition::NEW_WINDOW,
                            extensions::SOURCE_COMMAND_LINE);
   params.command_line = command_line;
@@ -101,7 +101,7 @@
   RecordCmdLineAppHistogram(app->GetType());
 
   content::WebContents* app_tab = ::OpenApplication(
-      ::AppLaunchParams(profile, app, extensions::LAUNCH_CONTAINER_TAB,
+      ::AppLaunchParams(profile, app_id, extensions::LAUNCH_CONTAINER_TAB,
                         WindowOpenDisposition::NEW_FOREGROUND_TAB,
                         extensions::SOURCE_COMMAND_LINE));
   return app_tab != nullptr;
@@ -112,12 +112,11 @@
     const std::string& app_id,
     const base::CommandLine& command_line,
     const base::FilePath& current_directory) {
-  const extensions::Extension* app = GetPlatformApp(profile, app_id);
-  if (!app)
+  if (!GetPlatformApp(profile, app_id))
     return false;
 
   RecordCmdLineAppHistogram(extensions::Manifest::TYPE_PLATFORM_APP);
-  ::AppLaunchParams params(profile, app, extensions::LAUNCH_CONTAINER_NONE,
+  ::AppLaunchParams params(profile, app_id, extensions::LAUNCH_CONTAINER_NONE,
                            WindowOpenDisposition::NEW_WINDOW,
                            extensions::SOURCE_COMMAND_LINE);
   params.command_line = command_line;
diff --git a/chrome/browser/chrome_content_browser_client_browsertest.cc b/chrome/browser/chrome_content_browser_client_browsertest.cc
index 958f997..f117243d 100644
--- a/chrome/browser/chrome_content_browser_client_browsertest.cc
+++ b/chrome/browser/chrome_content_browser_client_browsertest.cc
@@ -34,6 +34,7 @@
 #include "components/network_session_configurator/common/network_switches.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
 #include "components/prefs/pref_service.h"
+#include "content/public/browser/child_process_security_policy.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/render_frame_host.h"
@@ -254,7 +255,7 @@
 
  protected:
   // These are the origins we expect to be returned by
-  // content::SiteIsolationPolicy::GetIsolatedOrigins() even if
+  // content::ChildProcessSecurityPolicy::GetIsolatedOrigins() even if
   // ContentBrowserClient::ShouldDisableSiteIsolation() returns true.
   const std::vector<url::Origin> kExpectedEmbedderOrigins = {
 #if !defined(OS_ANDROID)
@@ -410,9 +411,10 @@
   isolated_origins_feature.InitAndEnableFeatureWithParameters(
       features::kIsolateOrigins, {{features::kIsolateOriginsFieldTrialParamName,
                                    trial_origin.Serialize()}});
+  SiteIsolationPolicy::ApplyGlobalIsolatedOrigins();
 
-  std::vector<url::Origin> isolated_origins =
-      content::SiteIsolationPolicy::GetIsolatedOrigins();
+  auto* cpsp = content::ChildProcessSecurityPolicy::GetInstance();
+  std::vector<url::Origin> isolated_origins = cpsp->GetIsolatedOrigins();
   EXPECT_EQ(kExpectedTrialOrigins, isolated_origins.size());
 
   // Verify that the expected embedder origins are present even though site
@@ -442,9 +444,10 @@
   isolated_origins_feature.InitAndEnableFeatureWithParameters(
       features::kIsolateOrigins, {{features::kIsolateOriginsFieldTrialParamName,
                                    trial_origin.Serialize()}});
+  SiteIsolationPolicy::ApplyGlobalIsolatedOrigins();
 
-  std::vector<url::Origin> isolated_origins =
-      content::SiteIsolationPolicy::GetIsolatedOrigins();
+  auto* cpsp = content::ChildProcessSecurityPolicy::GetInstance();
+  std::vector<url::Origin> isolated_origins = cpsp->GetIsolatedOrigins();
   EXPECT_EQ(1u + kExpectedEmbedderOrigins.size(), isolated_origins.size());
   EXPECT_THAT(kExpectedEmbedderOrigins,
               ::testing::IsSubsetOf(isolated_origins));
@@ -463,9 +466,10 @@
   isolated_origins_feature.InitAndEnableFeatureWithParameters(
       features::kIsolateOrigins, {{features::kIsolateOriginsFieldTrialParamName,
                                    trial_origin.Serialize()}});
+  SiteIsolationPolicy::ApplyGlobalIsolatedOrigins();
 
-  std::vector<url::Origin> isolated_origins =
-      content::SiteIsolationPolicy::GetIsolatedOrigins();
+  auto* cpsp = content::ChildProcessSecurityPolicy::GetInstance();
+  std::vector<url::Origin> isolated_origins = cpsp->GetIsolatedOrigins();
   EXPECT_EQ(kExpectedTrialOrigins + kExpectedEmbedderOrigins.size(),
             isolated_origins.size());
   EXPECT_THAT(kExpectedEmbedderOrigins,
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 7a4c6c4..4e7106bb 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -1606,6 +1606,8 @@
     "policy/device_policy_decoder_chromeos.cc",
     "policy/device_policy_decoder_chromeos.h",
     "policy/device_policy_remover.h",
+    "policy/device_scheduled_update_checker.cc",
+    "policy/device_scheduled_update_checker.h",
     "policy/device_wallpaper_image_handler.cc",
     "policy/device_wallpaper_image_handler.h",
     "policy/device_wifi_allowed_handler.cc",
@@ -2559,6 +2561,7 @@
     "policy/device_local_account_policy_service_unittest.cc",
     "policy/device_native_printers_handler_unittest.cc",
     "policy/device_policy_decoder_chromeos_unittest.cc",
+    "policy/device_scheduled_update_checker_unittest.cc",
     "policy/dm_token_storage_unittest.cc",
     "policy/extension_cache_unittest.cc",
     "policy/fake_affiliated_invalidation_service_provider.cc",
diff --git a/chrome/browser/chromeos/android_sms/android_sms_app_manager_impl.cc b/chrome/browser/chromeos/android_sms/android_sms_app_manager_impl.cc
index 2cdfed2..2d6d6df6 100644
--- a/chrome/browser/chromeos/android_sms/android_sms_app_manager_impl.cc
+++ b/chrome/browser/chromeos/android_sms/android_sms_app_manager_impl.cc
@@ -232,8 +232,9 @@
                   << "Launching Messages PWA.";
   pwa_delegate_->OpenApp(AppLaunchParams(
       profile_,
-      setup_controller_->GetPwa(
-          GetAndroidMessagesURL(true /* use_install_url */, *domain)),
+      setup_controller_
+          ->GetPwa(GetAndroidMessagesURL(true /* use_install_url */, *domain))
+          ->id(),
       extensions::LAUNCH_CONTAINER_WINDOW, WindowOpenDisposition::NEW_WINDOW,
       extensions::SOURCE_CHROME_INTERNAL));
 }
diff --git a/chrome/browser/chromeos/android_sms/android_sms_app_manager_impl_unittest.cc b/chrome/browser/chromeos/android_sms/android_sms_app_manager_impl_unittest.cc
index 4d4131d..e621a5e 100644
--- a/chrome/browser/chromeos/android_sms/android_sms_app_manager_impl_unittest.cc
+++ b/chrome/browser/chromeos/android_sms/android_sms_app_manager_impl_unittest.cc
@@ -71,7 +71,7 @@
 
     // AndroidSmsAppManagerImpl::PwaDelegate:
     content::WebContents* OpenApp(const AppLaunchParams& params) override {
-      opened_app_ids_.push_back(params.extension_id);
+      opened_app_ids_.push_back(params.app_id);
       return nullptr;
     }
 
diff --git a/chrome/browser/chromeos/app_mode/startup_app_launcher.cc b/chrome/browser/chromeos/app_mode/startup_app_launcher.cc
index 3e5d2b9..5294b73a 100644
--- a/chrome/browser/chromeos/app_mode/startup_app_launcher.cc
+++ b/chrome/browser/chromeos/app_mode/startup_app_launcher.cc
@@ -428,7 +428,7 @@
 
   // Always open the app in a window.
   OpenApplication(AppLaunchParams(
-      profile_, extension, extensions::LAUNCH_CONTAINER_WINDOW,
+      profile_, extension->id(), extensions::LAUNCH_CONTAINER_WINDOW,
       WindowOpenDisposition::NEW_WINDOW, extensions::SOURCE_KIOSK));
 
   KioskAppManager::Get()->InitSession(profile_, app_id_);
diff --git a/chrome/browser/chromeos/arc/pip/arc_picture_in_picture_window_controller_impl.cc b/chrome/browser/chromeos/arc/pip/arc_picture_in_picture_window_controller_impl.cc
index 6ade5a5..4949089 100644
--- a/chrome/browser/chromeos/arc/pip/arc_picture_in_picture_window_controller_impl.cc
+++ b/chrome/browser/chromeos/arc/pip/arc_picture_in_picture_window_controller_impl.cc
@@ -17,9 +17,8 @@
   Close(false);
 }
 
-gfx::Size ArcPictureInPictureWindowControllerImpl::Show() {
+void ArcPictureInPictureWindowControllerImpl::Show() {
   // Should be a no-op on ARC. This is managed on the Android side.
-  return gfx::Size();
 }
 
 void ArcPictureInPictureWindowControllerImpl::Close(bool should_pause_video) {
diff --git a/chrome/browser/chromeos/arc/pip/arc_picture_in_picture_window_controller_impl.h b/chrome/browser/chromeos/arc/pip/arc_picture_in_picture_window_controller_impl.h
index 1c5ff704..e97e4b5 100644
--- a/chrome/browser/chromeos/arc/pip/arc_picture_in_picture_window_controller_impl.h
+++ b/chrome/browser/chromeos/arc/pip/arc_picture_in_picture_window_controller_impl.h
@@ -30,7 +30,7 @@
   ~ArcPictureInPictureWindowControllerImpl() override;
 
   // PictureInPictureWindowController:
-  gfx::Size Show() override;
+  void Show() override;
   void Close(bool should_pause_video) override;
   void CloseAndFocusInitiator() override;
   void OnWindowDestroyed() override;
diff --git a/chrome/browser/chromeos/crostini/crostini_manager.cc b/chrome/browser/chromeos/crostini/crostini_manager.cc
index c2a88f5..d5231fd0 100644
--- a/chrome/browser/chromeos/crostini/crostini_manager.cc
+++ b/chrome/browser/chromeos/crostini/crostini_manager.cc
@@ -1515,12 +1515,8 @@
 // static
 AppLaunchParams CrostiniManager::GenerateTerminalAppLaunchParams(
     Profile* profile) {
-  const extensions::Extension* crosh_extension =
-      extensions::ExtensionRegistry::Get(profile)->GetInstalledExtension(
-          kCrostiniCroshBuiltinAppId);
-
   AppLaunchParams launch_params(
-      profile, crosh_extension, extensions::LAUNCH_CONTAINER_WINDOW,
+      profile, kCrostiniCroshBuiltinAppId, extensions::LAUNCH_CONTAINER_WINDOW,
       WindowOpenDisposition::NEW_WINDOW, extensions::SOURCE_APP_LAUNCHER);
   launch_params.override_app_name =
       AppNameFromCrostiniAppId(kCrostiniTerminalId);
diff --git a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h
index 72b03db4..40fc7ca 100644
--- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h
@@ -515,7 +515,7 @@
   void OnInteractionStarted(bool is_voice_interaction) override {}
   void OnSuggestionsResponse(
       std::vector<AssistantSuggestionPtr> response) override {}
-  void OnOpenUrlResponse(const GURL& url) override {}
+  void OnOpenUrlResponse(const GURL& url, bool in_background) override {}
   void OnSpeechRecognitionStarted() override {}
   void OnSpeechRecognitionIntermediateResult(
       const std::string& high_confidence_text,
diff --git a/chrome/browser/chromeos/file_manager/file_tasks.cc b/chrome/browser/chromeos/file_manager/file_tasks.cc
index 2b0e758..e0ebb882 100644
--- a/chrome/browser/chromeos/file_manager/file_tasks.cc
+++ b/chrome/browser/chromeos/file_manager/file_tasks.cc
@@ -427,7 +427,7 @@
           extensions::GetLaunchContainer(
               extensions::ExtensionPrefs::Get(extension_task_profile),
               extension);
-      AppLaunchParams params(extension_task_profile, extension,
+      AppLaunchParams params(extension_task_profile, task.app_id,
                              launch_container,
                              WindowOpenDisposition::NEW_FOREGROUND_TAB,
                              extensions::AppLaunchSource::SOURCE_FILE_HANDLER);
diff --git a/chrome/browser/chromeos/first_run/first_run.cc b/chrome/browser/chromeos/first_run/first_run.cc
index 525b479a..b10ec76 100644
--- a/chrome/browser/chromeos/first_run/first_run.cc
+++ b/chrome/browser/chromeos/first_run/first_run.cc
@@ -59,7 +59,7 @@
     return;
 
   OpenApplication(AppLaunchParams(
-      profile, extension, extensions::LAUNCH_CONTAINER_WINDOW,
+      profile, extension->id(), extensions::LAUNCH_CONTAINER_WINDOW,
       WindowOpenDisposition::NEW_WINDOW, extensions::SOURCE_CHROME_INTERNAL));
   profile->GetPrefs()->SetBoolean(prefs::kFirstRunTutorialShown, true);
 }
diff --git a/chrome/browser/chromeos/input_method/input_method_manager_impl.cc b/chrome/browser/chromeos/input_method/input_method_manager_impl.cc
index f5a777e..98c9196 100644
--- a/chrome/browser/chromeos/input_method/input_method_manager_impl.cc
+++ b/chrome/browser/chromeos/input_method/input_method_manager_impl.cc
@@ -44,7 +44,7 @@
 #include "ui/base/ime/chromeos/extension_ime_util.h"
 #include "ui/base/ime/chromeos/fake_ime_keyboard.h"
 #include "ui/base/ime/chromeos/ime_keyboard.h"
-#include "ui/base/ime/chromeos/ime_keyboard_mus.h"
+#include "ui/base/ime/chromeos/ime_keyboard_impl.h"
 #include "ui/base/ime/chromeos/input_method_delegate.h"
 #include "ui/base/ime/ime_bridge.h"
 #include "ui/base/ui_base_features.h"
@@ -909,19 +909,14 @@
     bool enable_extension_loading)
     : delegate_(std::move(delegate)),
       ui_session_(STATE_LOGIN_SCREEN),
-      state_(NULL),
       util_(delegate_.get()),
       component_extension_ime_manager_(new ComponentExtensionIMEManager()),
       enable_extension_loading_(enable_extension_loading),
-      is_ime_menu_activated_(false),
       features_enabled_state_(InputMethodManager::FEATURE_ALL) {
-  if (IsRunningAsSystemCompositor()) {
-    keyboard_ = std::make_unique<ImeKeyboardMus>(
-        g_browser_process->platform_part()->GetInputDeviceControllerClient());
-  } else {
-    keyboard_.reset(new FakeImeKeyboard());
-  }
-
+  if (IsRunningAsSystemCompositor())
+    keyboard_ = std::make_unique<ImeKeyboardImpl>();
+  else
+    keyboard_ = std::make_unique<FakeImeKeyboard>();
   // Initializes the system IME list.
   std::unique_ptr<ComponentExtensionIMEManagerDelegate> comp_delegate(
       new ComponentExtensionIMEManagerImpl());
diff --git a/chrome/browser/chromeos/input_method/input_method_manager_impl.h b/chrome/browser/chromeos/input_method/input_method_manager_impl.h
index 94b652d..4bbf8a1 100644
--- a/chrome/browser/chromeos/input_method/input_method_manager_impl.h
+++ b/chrome/browser/chromeos/input_method/input_method_manager_impl.h
@@ -309,7 +309,7 @@
   bool enable_extension_loading_;
 
   // Whether the expanded IME menu is activated.
-  bool is_ime_menu_activated_;
+  bool is_ime_menu_activated_ = false;
 
   // The enabled state of keyboard features.
   uint32_t features_enabled_state_;
diff --git a/chrome/browser/chromeos/login/demo_mode/demo_app_launcher.cc b/chrome/browser/chromeos/login/demo_mode/demo_app_launcher.cc
index 6057db9..0a8abd2 100644
--- a/chrome/browser/chromeos/login/demo_mode/demo_app_launcher.cc
+++ b/chrome/browser/chromeos/login/demo_mode/demo_app_launcher.cc
@@ -92,7 +92,7 @@
       NetworkTypePattern::Physical(), false,
       chromeos::network_handler::ErrorCallback());
 
-  OpenApplication(AppLaunchParams(profile, extension,
+  OpenApplication(AppLaunchParams(profile, extension_id,
                                   extensions::LAUNCH_CONTAINER_WINDOW,
                                   WindowOpenDisposition::NEW_WINDOW,
                                   extensions::SOURCE_CHROME_INTERNAL, true));
diff --git a/chrome/browser/chromeos/login/demo_mode/demo_session.cc b/chrome/browser/chromeos/login/demo_mode/demo_session.cc
index 9344e58..c2f5585 100644
--- a/chrome/browser/chromeos/login/demo_mode/demo_session.cc
+++ b/chrome/browser/chromeos/login/demo_mode/demo_session.cc
@@ -580,7 +580,7 @@
   Profile* profile = ProfileManager::GetActiveUserProfile();
   DCHECK(profile);
   OpenApplication(AppLaunchParams(
-      profile, extension, extensions::LAUNCH_CONTAINER_WINDOW,
+      profile, extension->id(), extensions::LAUNCH_CONTAINER_WINDOW,
       WindowOpenDisposition::NEW_WINDOW, extensions::SOURCE_CHROME_INTERNAL));
 }
 
diff --git a/chrome/browser/chromeos/login/error_screen_browsertest.cc b/chrome/browser/chromeos/login/error_screen_browsertest.cc
index 8114975..e07c19f 100644
--- a/chrome/browser/chromeos/login/error_screen_browsertest.cc
+++ b/chrome/browser/chromeos/login/error_screen_browsertest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "ash/public/cpp/login_screen_test_api.h"
 #include "base/run_loop.h"
 #include "base/strings/strcat.h"
 #include "base/test/bind_test_util.h"
@@ -16,7 +17,6 @@
 #include "chrome/browser/chromeos/login/test/embedded_test_server_mixin.h"
 #include "chrome/browser/chromeos/login/test/js_checker.h"
 #include "chrome/browser/chromeos/login/test/login_manager_mixin.h"
-#include "chrome/browser/chromeos/login/test/login_screen_tester.h"
 #include "chrome/browser/chromeos/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/chromeos/login/wizard_controller.h"
 #include "chrome/browser/ui/webui/chromeos/login/app_launch_splash_screen_handler.h"
@@ -329,7 +329,7 @@
 // Verify that certificate manager dialog opens.
 IN_PROC_BROWSER_TEST_F(KioskErrorScreenTest, OpenCertificateConfig) {
   apps_loaded_waiter()->Wait();
-  EXPECT_TRUE(test::LoginScreenTester().LaunchApp(kTestKioskAppId));
+  EXPECT_TRUE(ash::LoginScreenTestApi::LaunchApp(kTestKioskAppId));
 
   OobeScreenWaiter(ErrorScreenView::kScreenId).Wait();
 
diff --git a/chrome/browser/chromeos/login/existing_user_controller_browsertest.cc b/chrome/browser/chromeos/login/existing_user_controller_browsertest.cc
index c84027bc..5bb3bf2 100644
--- a/chrome/browser/chromeos/login/existing_user_controller_browsertest.cc
+++ b/chrome/browser/chromeos/login/existing_user_controller_browsertest.cc
@@ -5,6 +5,7 @@
 #include <string>
 #include <vector>
 
+#include "ash/public/cpp/login_screen_test_api.h"
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/callback.h"
@@ -27,7 +28,6 @@
 #include "chrome/browser/chromeos/login/session/user_session_manager_test_api.h"
 #include "chrome/browser/chromeos/login/test/js_checker.h"
 #include "chrome/browser/chromeos/login/test/login_manager_mixin.h"
-#include "chrome/browser/chromeos/login/test/login_screen_tester.h"
 #include "chrome/browser/chromeos/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/chromeos/login/ui/mock_login_display.h"
 #include "chrome/browser/chromeos/login/ui/mock_login_display_host.h"
@@ -1103,7 +1103,7 @@
     // login, the login attempt cannot be shortcut by login manager mixin API -
     // it has to go through login UI.
     const std::string& password = user_context.GetKey()->GetSecret();
-    test::LoginScreenTester().SubmitPassword(test_user_.account_id, password);
+    ash::LoginScreenTestApi::SubmitPassword(test_user_.account_id, password);
   }
 
   void SetUpStubAuthenticatorAndAttemptLoginWithWrongPassword() {
@@ -1114,22 +1114,14 @@
     test::UserSessionManagerTestApi(UserSessionManager::GetInstance())
         .InjectAuthenticatorBuilder(std::move(authenticator_builder));
 
-    test::LoginScreenTester().SubmitPassword(test_user_.account_id,
-                                             "wrong!!!!!");
+    ash::LoginScreenTestApi::SubmitPassword(test_user_.account_id,
+                                            "wrong!!!!!");
   }
 
-  // Waits for auth error message to be shown in login UI. It runs
-  // LoginScreenTester::WaitForUiUpdate until
-  // LoginScreenTester::IsAuthErrorBubbleShown returns true.
+  // Waits for auth error message to be shown in login UI.
   void WaitForAuthErrorMessage() {
-    test::LoginScreenTester login_tester;
-
-    int ui_version = login_tester.GetUiUpdateCount();
-
-    while (!login_tester.IsAuthErrorBubbleShown()) {
-      ASSERT_TRUE(login_tester.WaitForUiUpdate(ui_version));
-      ui_version = login_tester.GetUiUpdateCount();
-    }
+    base::RunLoop().RunUntilIdle();
+    EXPECT_TRUE(ash::LoginScreenTestApi::IsAuthErrorBubbleShown());
   }
 
  protected:
diff --git a/chrome/browser/chromeos/login/guest_login_browsertest.cc b/chrome/browser/chromeos/login/guest_login_browsertest.cc
index 35e79fae..3ce717dc 100644
--- a/chrome/browser/chromeos/login/guest_login_browsertest.cc
+++ b/chrome/browser/chromeos/login/guest_login_browsertest.cc
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "ash/public/cpp/login_screen_test_api.h"
 #include "chrome/browser/chromeos/login/mixin_based_in_process_browser_test.h"
 #include "chrome/browser/chromeos/login/test/login_manager_mixin.h"
-#include "chrome/browser/chromeos/login/test/login_screen_tester.h"
 #include "chromeos/dbus/session_manager/fake_session_manager_client.h"
 #include "components/user_manager/user_manager.h"
 
@@ -53,7 +53,7 @@
   FakeSessionManagerClient::Get()->set_restart_job_callback(
       restart_job_waiter.QuitClosure());
 
-  ASSERT_TRUE(test::LoginScreenTester().ClickGuestButton());
+  ASSERT_TRUE(ash::LoginScreenTestApi::ClickGuestButton());
 
   restart_job_waiter.Run();
   EXPECT_TRUE(FakeSessionManagerClient::Get()->restart_job_argv().has_value());
@@ -71,7 +71,7 @@
   FakeSessionManagerClient::Get()->set_restart_job_callback(
       restart_job_waiter.QuitClosure());
 
-  ASSERT_TRUE(test::LoginScreenTester().ClickGuestButton());
+  ASSERT_TRUE(ash::LoginScreenTestApi::ClickGuestButton());
 
   restart_job_waiter.Run();
   EXPECT_TRUE(FakeSessionManagerClient::Get()->restart_job_argv().has_value());
diff --git a/chrome/browser/chromeos/login/kiosk_browsertest.cc b/chrome/browser/chromeos/login/kiosk_browsertest.cc
index dfdedf6..834fe1a 100644
--- a/chrome/browser/chromeos/login/kiosk_browsertest.cc
+++ b/chrome/browser/chromeos/login/kiosk_browsertest.cc
@@ -8,8 +8,8 @@
 #include "apps/test/app_window_waiter.h"
 #include "ash/public/cpp/ash_switches.h"
 #include "ash/public/cpp/keyboard/keyboard_switches.h"
+#include "ash/public/cpp/login_screen_test_api.h"
 #include "ash/public/cpp/wallpaper_controller_observer.h"
-#include "ash/public/interfaces/login_screen_test_api.test-mojom-test-utils.h"
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/json/json_reader.h"
@@ -548,14 +548,7 @@
     // TODO(https://crbug.com/932323): Implement or remove diagnostic mode.
     if (diagnostic_mode)
       return false;
-    ash::mojom::LoginScreenTestApiPtr test_api_;
-    content::ServiceManagerConnection::GetForProcess()
-        ->GetConnector()
-        ->BindInterface(ash::mojom::kServiceName, &test_api_);
-    ash::mojom::LoginScreenTestApiAsyncWaiter login_screen(test_api_.get());
-    bool found;
-    login_screen.LaunchApp(app_id, &found);
-    return found;
+    return ash::LoginScreenTestApi::LaunchApp(app_id);
   }
 
   void ReloadKioskApps() {
diff --git a/chrome/browser/chromeos/login/lock/screen_locker_tester.cc b/chrome/browser/chromeos/login/lock/screen_locker_tester.cc
index 29e9997..b84ead8 100644
--- a/chrome/browser/chromeos/login/lock/screen_locker_tester.cc
+++ b/chrome/browser/chromeos/login/lock/screen_locker_tester.cc
@@ -7,10 +7,7 @@
 #include <cstdint>
 #include <string>
 
-#include "ash/public/cpp/ash_switches.h"
-#include "ash/public/interfaces/constants.mojom.h"
-#include "ash/public/interfaces/login_screen_test_api.test-mojom-test-utils.h"
-#include "base/command_line.h"
+#include "ash/public/cpp/login_screen_test_api.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
@@ -28,9 +25,7 @@
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
-#include "content/public/common/service_manager_connection.h"
 #include "content/public/test/test_utils.h"
-#include "services/service_manager/public/cpp/connector.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace chromeos {
@@ -84,11 +79,7 @@
 
 }  // namespace
 
-ScreenLockerTester::ScreenLockerTester() {
-  content::ServiceManagerConnection::GetForProcess()
-      ->GetConnector()
-      ->BindInterface(ash::mojom::kServiceName, &test_api_);
-}
+ScreenLockerTester::ScreenLockerTester() = default;
 
 ScreenLockerTester::~ScreenLockerTester() = default;
 
@@ -118,53 +109,28 @@
 }
 
 bool ScreenLockerTester::IsLocked() {
-  if (!IsScreenLockerLocked())
-    return false;
-
-  // Check from ash's perspective.
-  ash::mojom::LoginScreenTestApiAsyncWaiter login_screen(test_api_.get());
-  bool is_ui_shown;
-  login_screen.IsLockShown(&is_ui_shown);
-  return IsScreenLockerLocked() && is_ui_shown;
+  return IsScreenLockerLocked() && ash::LoginScreenTestApi::IsLockShown();
 }
 
 bool ScreenLockerTester::IsLockRestartButtonShown() {
-  if (!IsScreenLockerLocked())
-    return false;
-
-  return login_screen_tester_.IsRestartButtonShown() && IsScreenLockerLocked();
+  return IsScreenLockerLocked() &&
+         ash::LoginScreenTestApi::IsRestartButtonShown();
 }
 
 bool ScreenLockerTester::IsLockShutdownButtonShown() {
-  if (!IsScreenLockerLocked())
-    return false;
-
-  bool is_shutdown_button_shown = login_screen_tester_.IsShutdownButtonShown();
-  return IsScreenLockerLocked() && is_shutdown_button_shown;
+  return IsScreenLockerLocked() &&
+         ash::LoginScreenTestApi::IsShutdownButtonShown();
 }
 
 bool ScreenLockerTester::IsAuthErrorBubbleShown() {
-  if (!IsScreenLockerLocked())
-    return false;
-
-  bool is_auth_error_button_shown =
-      login_screen_tester_.IsAuthErrorBubbleShown();
-  return IsScreenLockerLocked() && is_auth_error_button_shown;
+  return IsScreenLockerLocked() &&
+         ash::LoginScreenTestApi::IsAuthErrorBubbleShown();
 }
 
 void ScreenLockerTester::UnlockWithPassword(const AccountId& account_id,
                                             const std::string& password) {
-  ash::mojom::LoginScreenTestApiAsyncWaiter login_screen(test_api_.get());
-  login_screen.SubmitPassword(account_id, password);
+  ash::LoginScreenTestApi::SubmitPassword(account_id, password);
   base::RunLoop().RunUntilIdle();
 }
 
-int64_t ScreenLockerTester::GetUiUpdateCount() {
-  return login_screen_tester_.GetUiUpdateCount();
-}
-
-void ScreenLockerTester::WaitForUiUpdate(int64_t previous_update_count) {
-  login_screen_tester_.WaitForUiUpdate(previous_update_count);
-}
-
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/lock/screen_locker_tester.h b/chrome/browser/chromeos/login/lock/screen_locker_tester.h
index 815ec310..e1d0af15 100644
--- a/chrome/browser/chromeos/login/lock/screen_locker_tester.h
+++ b/chrome/browser/chromeos/login/lock/screen_locker_tester.h
@@ -5,11 +5,9 @@
 #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_LOCK_SCREEN_LOCKER_TESTER_H_
 #define CHROME_BROWSER_CHROMEOS_LOGIN_LOCK_SCREEN_LOCKER_TESTER_H_
 
-#include <memory>
 #include <string>
 
-#include "ash/public/interfaces/login_screen_test_api.test-mojom.h"
-#include "chrome/browser/chromeos/login/test/login_screen_tester.h"
+#include "base/macros.h"
 
 class AccountId;
 
@@ -44,14 +42,7 @@
   void UnlockWithPassword(const AccountId& account_id,
                           const std::string& password);
 
-  // LoginScreenTester proxy methods:
-  int64_t GetUiUpdateCount();
-  void WaitForUiUpdate(int64_t previous_update_count);
-
  private:
-  test::LoginScreenTester login_screen_tester_;
-  ash::mojom::LoginScreenTestApiPtr test_api_;
-
   DISALLOW_COPY_AND_ASSIGN(ScreenLockerTester);
 };
 
diff --git a/chrome/browser/chromeos/login/login_shelf_test_helper.cc b/chrome/browser/chromeos/login/login_shelf_test_helper.cc
deleted file mode 100644
index dc11466..0000000
--- a/chrome/browser/chromeos/login/login_shelf_test_helper.cc
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/login/login_shelf_test_helper.h"
-
-#include "ash/public/interfaces/constants.mojom.h"
-#include "ash/public/interfaces/login_screen_test_api.test-mojom-test-utils.h"
-#include "content/public/common/service_manager_connection.h"
-#include "services/service_manager/public/cpp/connector.h"
-
-namespace chromeos {
-
-LoginShelfTestHelper::LoginShelfTestHelper() {
-  content::ServiceManagerConnection::GetForProcess()
-      ->GetConnector()
-      ->BindInterface(ash::mojom::kServiceName, &login_screen_);
-}
-
-LoginShelfTestHelper::~LoginShelfTestHelper() = default;
-
-bool LoginShelfTestHelper::IsLoginShelfShown() {
-  ash::mojom::LoginScreenTestApiAsyncWaiter waiter(login_screen_.get());
-  bool is_shown;
-  waiter.IsLoginShelfShown(&is_shown);
-  return is_shown;
-}
-
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/login_shelf_test_helper.h b/chrome/browser/chromeos/login/login_shelf_test_helper.h
deleted file mode 100644
index dba4530..0000000
--- a/chrome/browser/chromeos/login/login_shelf_test_helper.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_CHROMEOS_LOGIN_LOGIN_SHELF_TEST_HELPER_H_
-#define CHROME_BROWSER_CHROMEOS_LOGIN_LOGIN_SHELF_TEST_HELPER_H_
-#include "ash/public/interfaces/login_screen_test_api.test-mojom.h"
-
-namespace chromeos {
-
-class LoginShelfTestHelper {
- public:
-  LoginShelfTestHelper();
-  ~LoginShelfTestHelper();
-
-  // Returns true if the login shelf is visible.
-  bool IsLoginShelfShown();
-
- private:
-  ash::mojom::LoginScreenTestApiPtr login_screen_;
-
-  DISALLOW_COPY_AND_ASSIGN(LoginShelfTestHelper);
-};
-
-}  // namespace chromeos
-
-#endif  // CHROME_BROWSER_CHROMEOS_LOGIN_LOGIN_SHELF_TEST_HELPER_H_
diff --git a/chrome/browser/chromeos/login/login_ui_keyboard_browsertest.cc b/chrome/browser/chromeos/login/login_ui_keyboard_browsertest.cc
index 44cb1e5..1580773 100644
--- a/chrome/browser/chromeos/login/login_ui_keyboard_browsertest.cc
+++ b/chrome/browser/chromeos/login/login_ui_keyboard_browsertest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "ash/public/cpp/login_screen_test_api.h"
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/location.h"
@@ -14,7 +15,6 @@
 #include "chrome/browser/chromeos/login/login_manager_test.h"
 #include "chrome/browser/chromeos/login/startup_utils.h"
 #include "chrome/browser/chromeos/login/test/js_checker.h"
-#include "chrome/browser/chromeos/login/test/login_screen_tester.h"
 #include "chrome/browser/chromeos/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/chromeos/login/ui/login_display_host.h"
 #include "chrome/browser/chromeos/settings/scoped_testing_cros_settings.h"
@@ -266,7 +266,7 @@
                                         ->GetActiveInputMethodIds());
 
   // Switch to Gaia.
-  ASSERT_TRUE(test::LoginScreenTester().ClickAddUserButton());
+  ASSERT_TRUE(ash::LoginScreenTestApi::ClickAddUserButton());
   OobeScreenWaiter(GaiaView::kScreenId).Wait();
   CheckGaiaKeyboard();
 
diff --git a/chrome/browser/chromeos/login/login_ui_shelf_visibility_browsertest.cc b/chrome/browser/chromeos/login/login_ui_shelf_visibility_browsertest.cc
index d344ed8..16a5ac4 100644
--- a/chrome/browser/chromeos/login/login_ui_shelf_visibility_browsertest.cc
+++ b/chrome/browser/chromeos/login/login_ui_shelf_visibility_browsertest.cc
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "ash/public/cpp/login_screen_test_api.h"
 #include "chrome/browser/chromeos/login/test/embedded_test_server_mixin.h"
 #include "chrome/browser/chromeos/login/test/fake_gaia_mixin.h"
 #include "chrome/browser/chromeos/login/test/login_manager_mixin.h"
-#include "chrome/browser/chromeos/login/test/login_screen_tester.h"
 #include "chrome/browser/chromeos/login/test/oobe_auth_page_waiter.h"
 #include "chrome/browser/chromeos/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/chromeos/login/ui/login_display_host.h"
@@ -49,24 +49,24 @@
 
 // Verifies that shelf buttons are shown by default on login screen.
 IN_PROC_BROWSER_TEST_F(LoginUIShelfVisibilityTest, DefaultVisibility) {
-  EXPECT_TRUE(test::LoginScreenTester().IsGuestButtonShown());
-  EXPECT_TRUE(test::LoginScreenTester().IsAddUserButtonShown());
+  EXPECT_TRUE(ash::LoginScreenTestApi::IsGuestButtonShown());
+  EXPECT_TRUE(ash::LoginScreenTestApi::IsAddUserButtonShown());
 }
 
 // Verifies that guest button and add user button are hidden when Gaia
 // dialog is shown.
 IN_PROC_BROWSER_TEST_F(LoginUIShelfVisibilityTest, GaiaDialogOpen) {
-  EXPECT_TRUE(test::LoginScreenTester().ClickAddUserButton());
+  EXPECT_TRUE(ash::LoginScreenTestApi::ClickAddUserButton());
   test::OobeGaiaPageWaiter().WaitUntilReady();
-  EXPECT_FALSE(test::LoginScreenTester().IsGuestButtonShown());
-  EXPECT_FALSE(test::LoginScreenTester().IsAddUserButtonShown());
+  EXPECT_FALSE(ash::LoginScreenTestApi::IsGuestButtonShown());
+  EXPECT_FALSE(ash::LoginScreenTestApi::IsAddUserButtonShown());
 }
 
 // Verifies that guest button and add user button are hidden on post-login
 // screens, after a user session is started.
 IN_PROC_BROWSER_TEST_F(LoginUIShelfVisibilityTest, PostLoginScreen) {
   auto override = WizardController::ForceOfficialBuildForTesting();
-  EXPECT_TRUE(test::LoginScreenTester().ClickAddUserButton());
+  EXPECT_TRUE(ash::LoginScreenTestApi::ClickAddUserButton());
   test::OobeGaiaPageWaiter().WaitUntilReady();
   LoginDisplayHost::default_host()
       ->GetOobeUI()
@@ -77,8 +77,8 @@
   // Sync consent is the first post-login screen shown when a new user signs in.
   OobeScreenWaiter(SyncConsentScreenView::kScreenId).Wait();
 
-  EXPECT_FALSE(test::LoginScreenTester().IsGuestButtonShown());
-  EXPECT_FALSE(test::LoginScreenTester().IsAddUserButtonShown());
+  EXPECT_FALSE(ash::LoginScreenTestApi::IsGuestButtonShown());
+  EXPECT_FALSE(ash::LoginScreenTestApi::IsAddUserButtonShown());
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/proxy_auth_dialog_browsertest.cc b/chrome/browser/chromeos/login/proxy_auth_dialog_browsertest.cc
index 0b37992..5f0115e 100644
--- a/chrome/browser/chromeos/login/proxy_auth_dialog_browsertest.cc
+++ b/chrome/browser/chromeos/login/proxy_auth_dialog_browsertest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "ash/public/cpp/login_screen_test_api.h"
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
@@ -10,7 +11,6 @@
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/chromeos/login/login_manager_test.h"
 #include "chrome/browser/chromeos/login/startup_utils.h"
-#include "chrome/browser/chromeos/login/test/login_screen_tester.h"
 #include "chrome/browser/chromeos/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/chromeos/login/ui/login_display_host.h"
 #include "chrome/browser/chromeos/login/ui/webui_login_view.h"
@@ -111,7 +111,7 @@
   {
     OobeScreenWaiter screen_waiter(GaiaView::kScreenId);
     ProxyAuthDialogWaiter auth_dialog_waiter;
-    ASSERT_TRUE(test::LoginScreenTester().ClickAddUserButton());
+    ASSERT_TRUE(ash::LoginScreenTestApi::ClickAddUserButton());
     screen_waiter.Wait();
     auth_dialog_waiter.Wait();
     ASSERT_TRUE(auth_dialog_waiter.login_handler());
diff --git a/chrome/browser/chromeos/login/saml/saml_browsertest.cc b/chrome/browser/chromeos/login/saml/saml_browsertest.cc
index 690bc27..307187b 100644
--- a/chrome/browser/chromeos/login/saml/saml_browsertest.cc
+++ b/chrome/browser/chromeos/login/saml/saml_browsertest.cc
@@ -9,6 +9,7 @@
 
 #include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/ash_switches.h"
+#include "ash/public/cpp/login_screen_test_api.h"
 #include "base/base64.h"
 #include "base/bind.h"
 #include "base/bind_helpers.h"
@@ -39,7 +40,6 @@
 #include "chrome/browser/chromeos/login/test/js_checker.h"
 #include "chrome/browser/chromeos/login/test/local_policy_test_server_mixin.h"
 #include "chrome/browser/chromeos/login/test/login_manager_mixin.h"
-#include "chrome/browser/chromeos/login/test/login_screen_tester.h"
 #include "chrome/browser/chromeos/login/test/oobe_base_test.h"
 #include "chrome/browser/chromeos/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/chromeos/login/ui/login_display_host.h"
@@ -1100,7 +1100,7 @@
       "$('gaia-signin').authenticator_.addEventListener('ready', function() {"
       "  window.domAutomationController.send('ready');"
       "});"));
-  ASSERT_TRUE(test::LoginScreenTester().ClickAddUserButton());
+  ASSERT_TRUE(ash::LoginScreenTestApi::ClickAddUserButton());
   std::string message;
   do {
     ASSERT_TRUE(message_queue.WaitForMessage(&message));
@@ -1129,7 +1129,7 @@
 
   content::DOMMessageQueue message_queue;
   ASSERT_TRUE(content::ExecuteScript(GetLoginUI()->GetWebContents(), js));
-  ASSERT_TRUE(test::LoginScreenTester().ClickAddUserButton());
+  ASSERT_TRUE(ash::LoginScreenTestApi::ClickAddUserButton());
 
   std::string message;
   do {
diff --git a/chrome/browser/chromeos/login/screens/error_screen.cc b/chrome/browser/chromeos/login/screens/error_screen.cc
index d8acc5bf..d73b193 100644
--- a/chrome/browser/chromeos/login/screens/error_screen.cc
+++ b/chrome/browser/chromeos/login/screens/error_screen.cc
@@ -293,10 +293,8 @@
       IDR_CONNECTIVITY_DIAGNOSTICS_MANIFEST,
       base::FilePath(extension_misc::kConnectivityDiagnosticsPath));
 
-  const extensions::Extension* extension =
-      extension_service->GetExtensionById(extension_id, true);
   OpenApplication(AppLaunchParams(
-      profile, extension, extensions::LAUNCH_CONTAINER_WINDOW,
+      profile, extension_id, extensions::LAUNCH_CONTAINER_WINDOW,
       WindowOpenDisposition::NEW_WINDOW, extensions::SOURCE_CHROME_INTERNAL));
   KioskAppManager::Get()->InitSession(profile, extension_id);
 
diff --git a/chrome/browser/chromeos/login/screens/supervision_transition_screen_browsertest.cc b/chrome/browser/chromeos/login/screens/supervision_transition_screen_browsertest.cc
index 163be70..9a48344 100644
--- a/chrome/browser/chromeos/login/screens/supervision_transition_screen_browsertest.cc
+++ b/chrome/browser/chromeos/login/screens/supervision_transition_screen_browsertest.cc
@@ -5,6 +5,7 @@
 #include <memory>
 #include <string>
 
+#include "ash/public/cpp/login_screen_test_api.h"
 #include "base/run_loop.h"
 #include "base/timer/timer.h"
 #include "chrome/browser/chromeos/arc/arc_service_launcher.h"
@@ -16,7 +17,6 @@
 #include "chrome/browser/chromeos/login/test/js_checker.h"
 #include "chrome/browser/chromeos/login/test/local_policy_test_server_mixin.h"
 #include "chrome/browser/chromeos/login/test/login_manager_mixin.h"
-#include "chrome/browser/chromeos/login/test/login_screen_tester.h"
 #include "chrome/browser/chromeos/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/chromeos/login/test/user_policy_mixin.h"
 #include "chrome/browser/chromeos/login/ui/login_display_host.h"
@@ -121,8 +121,8 @@
   test::OobeJS().ExpectHiddenPath(
       {"supervision-transition-md", "supervisionTransitionErrorDialog"});
 
-  EXPECT_FALSE(test::LoginScreenTester().IsGuestButtonShown());
-  EXPECT_FALSE(test::LoginScreenTester().IsAddUserButtonShown());
+  EXPECT_FALSE(ash::LoginScreenTestApi::IsGuestButtonShown());
+  EXPECT_FALSE(ash::LoginScreenTestApi::IsAddUserButtonShown());
 
   ProfileManager::GetPrimaryUserProfile()->GetPrefs()->SetInteger(
       arc::prefs::kArcSupervisionTransition,
@@ -155,8 +155,8 @@
   test::OobeJS().ExpectHiddenPath(
       {"supervision-transition-md", "supervisionTransitionErrorDialog"});
 
-  EXPECT_FALSE(test::LoginScreenTester().IsGuestButtonShown());
-  EXPECT_FALSE(test::LoginScreenTester().IsAddUserButtonShown());
+  EXPECT_FALSE(ash::LoginScreenTestApi::IsGuestButtonShown());
+  EXPECT_FALSE(ash::LoginScreenTestApi::IsAddUserButtonShown());
 
   base::OneShotTimer* timer =
       LoginDisplayHost::default_host()
@@ -176,8 +176,8 @@
   test::OobeJS().ExpectHiddenPath(
       {"supervision-transition-md", "supervisionTransitionDialog"});
 
-  EXPECT_FALSE(test::LoginScreenTester().IsGuestButtonShown());
-  EXPECT_FALSE(test::LoginScreenTester().IsAddUserButtonShown());
+  EXPECT_FALSE(ash::LoginScreenTestApi::IsGuestButtonShown());
+  EXPECT_FALSE(ash::LoginScreenTestApi::IsAddUserButtonShown());
 
   test::OobeJS().TapOnPath({"supervision-transition-md", "accept-button"});
 
diff --git a/chrome/browser/chromeos/login/test/active_directory_login_mixin.cc b/chrome/browser/chromeos/login/test/active_directory_login_mixin.cc
index 280f37b..63d3a9f6 100644
--- a/chrome/browser/chromeos/login/test/active_directory_login_mixin.cc
+++ b/chrome/browser/chromeos/login/test/active_directory_login_mixin.cc
@@ -6,8 +6,8 @@
 
 #include <initializer_list>
 
+#include "ash/public/cpp/login_screen_test_api.h"
 #include "base/strings/string_piece.h"
-#include "chrome/browser/chromeos/login/login_shelf_test_helper.h"
 #include "chrome/browser/chromeos/login/test/js_checker.h"
 #include "chrome/browser/chromeos/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/chromeos/login/ui/login_display_host.h"
@@ -127,7 +127,7 @@
           ".innerText.trim()",
       autocomplete_realm_);
 
-  EXPECT_TRUE(LoginShelfTestHelper().IsLoginShelfShown());
+  EXPECT_TRUE(ash::LoginScreenTestApi::IsLoginShelfShown());
 }
 
 // Checks if Active Directory password change screen is shown.
diff --git a/chrome/browser/chromeos/login/test/login_screen_tester.cc b/chrome/browser/chromeos/login/test/login_screen_tester.cc
deleted file mode 100644
index 260e0c5..0000000
--- a/chrome/browser/chromeos/login/test/login_screen_tester.cc
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/login/test/login_screen_tester.h"
-
-#include "ash/public/interfaces/constants.mojom.h"
-#include "ash/public/interfaces/login_screen_test_api.test-mojom-test-utils.h"
-#include "content/public/common/service_manager_connection.h"
-#include "services/service_manager/public/cpp/connector.h"
-
-namespace chromeos {
-namespace test {
-
-LoginScreenTester::LoginScreenTester() {
-  content::ServiceManagerConnection::GetForProcess()
-      ->GetConnector()
-      ->BindInterface(ash::mojom::kServiceName, &test_api_);
-}
-
-LoginScreenTester::~LoginScreenTester() = default;
-
-int64_t LoginScreenTester::GetUiUpdateCount() {
-  ash::mojom::LoginScreenTestApiAsyncWaiter login_screen(test_api_.get());
-  int64_t ui_update_count = 0;
-  login_screen.GetUiUpdateCount(&ui_update_count);
-  return ui_update_count;
-}
-
-bool LoginScreenTester::IsRestartButtonShown() {
-  ash::mojom::LoginScreenTestApiAsyncWaiter login_screen(test_api_.get());
-  bool is_restart_button_shown;
-  login_screen.IsRestartButtonShown(&is_restart_button_shown);
-  return is_restart_button_shown;
-}
-
-bool LoginScreenTester::IsShutdownButtonShown() {
-  ash::mojom::LoginScreenTestApiAsyncWaiter login_screen(test_api_.get());
-  bool is_shutdown_button_shown;
-  login_screen.IsShutdownButtonShown(&is_shutdown_button_shown);
-  return is_shutdown_button_shown;
-}
-
-bool LoginScreenTester::IsAuthErrorBubbleShown() {
-  ash::mojom::LoginScreenTestApiAsyncWaiter login_screen(test_api_.get());
-  bool is_auth_error_button_shown;
-  login_screen.IsAuthErrorBubbleShown(&is_auth_error_button_shown);
-  return is_auth_error_button_shown;
-}
-
-bool LoginScreenTester::IsGuestButtonShown() {
-  ash::mojom::LoginScreenTestApiAsyncWaiter login_screen(test_api_.get());
-  bool is_guest_button_shown;
-  login_screen.IsGuestButtonShown(&is_guest_button_shown);
-  return is_guest_button_shown;
-}
-
-bool LoginScreenTester::IsAddUserButtonShown() {
-  ash::mojom::LoginScreenTestApiAsyncWaiter login_screen(test_api_.get());
-  bool is_add_user_button_shown;
-  login_screen.IsAddUserButtonShown(&is_add_user_button_shown);
-  return is_add_user_button_shown;
-}
-
-bool LoginScreenTester::ClickAddUserButton() {
-  ash::mojom::LoginScreenTestApiAsyncWaiter login_screen(test_api_.get());
-  bool success;
-  login_screen.ClickAddUserButton(&success);
-  return success;
-}
-
-bool LoginScreenTester::ClickGuestButton() {
-  ash::mojom::LoginScreenTestApiAsyncWaiter login_screen(test_api_.get());
-  bool success;
-  login_screen.ClickGuestButton(&success);
-  return success;
-}
-
-void LoginScreenTester::SubmitPassword(const AccountId& account_id,
-                                       const std::string& password) {
-  ash::mojom::LoginScreenTestApiAsyncWaiter login_screen(test_api_.get());
-  login_screen.SubmitPassword(account_id, password);
-}
-
-bool LoginScreenTester::WaitForUiUpdate(int64_t previous_update_count) {
-  ash::mojom::LoginScreenTestApiAsyncWaiter login_screen(test_api_.get());
-  bool success;
-  login_screen.WaitForUiUpdate(previous_update_count, &success);
-  return success;
-}
-
-bool LoginScreenTester::LaunchApp(const std::string& app_id) {
-  ash::mojom::LoginScreenTestApiAsyncWaiter login_screen(test_api_.get());
-  bool success;
-  login_screen.LaunchApp(app_id, &success);
-  return success;
-}
-
-}  // namespace test
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/test/login_screen_tester.h b/chrome/browser/chromeos/login/test/login_screen_tester.h
deleted file mode 100644
index ef66c33..0000000
--- a/chrome/browser/chromeos/login/test/login_screen_tester.h
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_CHROMEOS_LOGIN_TEST_LOGIN_SCREEN_TESTER_H_
-#define CHROME_BROWSER_CHROMEOS_LOGIN_TEST_LOGIN_SCREEN_TESTER_H_
-
-#include <cstdint>
-#include <string>
-
-#include "ash/public/interfaces/login_screen_test_api.test-mojom-test-utils.h"
-#include "base/macros.h"
-
-class AccountId;
-
-namespace chromeos {
-namespace test {
-
-// High-level API to ash::mojom::LoginScreenTestApi.
-class LoginScreenTester {
- public:
-  LoginScreenTester();
-  ~LoginScreenTester();
-
-  // Blocking mojo calls.
-  int64_t GetUiUpdateCount();
-  bool IsRestartButtonShown();
-  bool IsShutdownButtonShown();
-  bool IsAuthErrorBubbleShown();
-  bool IsGuestButtonShown();
-  bool IsAddUserButtonShown();
-
-  // Returns true on success (i.e. button is  not disabled).
-  bool ClickAddUserButton();
-  bool ClickGuestButton();
-
-  // Submits password for an existing user.
-  void SubmitPassword(const AccountId& account_id, const std::string& password);
-
-  // Blocks until LoginShelfView::ui_update_count() is greater then
-  // |previous_update_count|. Returns true on success, false on error.
-  bool WaitForUiUpdate(int64_t previous_update_count);
-
-  // Starts kiosk app.
-  bool LaunchApp(const std::string& app_id);
-
- private:
-  ash::mojom::LoginScreenTestApiPtr test_api_;
-
-  DISALLOW_COPY_AND_ASSIGN(LoginScreenTester);
-};
-
-}  // namespace test
-}  // namespace chromeos
-
-#endif  // CHROME_BROWSER_CHROMEOS_LOGIN_TEST_LOGIN_SCREEN_TESTER_H_
diff --git a/chrome/browser/chromeos/login/wizard_controller_browsertest.cc b/chrome/browser/chromeos/login/wizard_controller_browsertest.cc
index 6b317f8..cb29489f 100644
--- a/chrome/browser/chromeos/login/wizard_controller_browsertest.cc
+++ b/chrome/browser/chromeos/login/wizard_controller_browsertest.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/chromeos/login/wizard_controller.h"
 
+#include "ash/public/cpp/login_screen_test_api.h"
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/compiler_specific.h"
@@ -27,7 +28,6 @@
 #include "chrome/browser/chromeos/login/enrollment/mock_auto_enrollment_check_screen.h"
 #include "chrome/browser/chromeos/login/enrollment/mock_enrollment_screen.h"
 #include "chrome/browser/chromeos/login/existing_user_controller.h"
-#include "chrome/browser/chromeos/login/login_shelf_test_helper.h"
 #include "chrome/browser/chromeos/login/login_wizard.h"
 #include "chrome/browser/chromeos/login/mixin_based_in_process_browser_test.h"
 #include "chrome/browser/chromeos/login/oobe_screen.h"
@@ -702,8 +702,7 @@
           }
         }));
 
-    LoginShelfTestHelper shelf_helper;
-    ASSERT_TRUE(shelf_helper.IsLoginShelfShown());
+    ASSERT_TRUE(ash::LoginScreenTestApi::IsLoginShelfShown());
 
     EXPECT_CALL(*mock_welcome_screen_, Hide()).Times(1);
     EXPECT_CALL(*mock_welcome_screen_, SetConfiguration(IsNull())).Times(1);
@@ -717,7 +716,7 @@
 
     CheckCurrentScreen(EulaView::kScreenId);
     // Login shelf should still be visible.
-    EXPECT_TRUE(shelf_helper.IsLoginShelfShown());
+    EXPECT_TRUE(ash::LoginScreenTestApi::IsLoginShelfShown());
 
     EXPECT_CALL(*mock_eula_screen_, Hide()).Times(1);
     EXPECT_CALL(*mock_update_screen_, Show()).Times(1);
diff --git a/chrome/browser/chromeos/plugin_vm/plugin_vm_image_manager.cc b/chrome/browser/chromeos/plugin_vm/plugin_vm_image_manager.cc
index 4aadc6da..7a9acee 100644
--- a/chrome/browser/chromeos/plugin_vm/plugin_vm_image_manager.cc
+++ b/chrome/browser/chromeos/plugin_vm/plugin_vm_image_manager.cc
@@ -461,12 +461,33 @@
   params.guid = base::GenerateGUID();
   params.callback = base::BindRepeating(&PluginVmImageManager::OnStartDownload,
                                         weak_ptr_factory_.GetWeakPtr());
-  // TODO(https://crbug.com/966399): Create annotation.
+
+  net::NetworkTrafficAnnotationTag traffic_annotation =
+      net::DefineNetworkTrafficAnnotation("plugin_vm_image_download", R"(
+        semantics {
+          sender: "Plugin VM image manager"
+          description: "Request to download Plugin VM image is sent in order "
+            "to allow user to run Plugin VM."
+          trigger: "User clicking on Plugin VM icon when Plugin VM is not yet "
+            "installed."
+          data: "Request to download Plugin VM image. Sends cookies to "
+            "authenticate the user."
+          destination: WEBSITE
+        }
+        policy {
+          cookies_allowed: YES
+          cookies_store: "user"
+          chrome_policy {
+            PluginVmImage {
+              PluginVmImage: "{'url': 'example.com', 'hash': 'sha256hash'}"
+            }
+          }
+        }
+      )");
   params.traffic_annotation =
-      net::MutableNetworkTrafficAnnotationTag(NO_TRAFFIC_ANNOTATION_YET);
+      net::MutableNetworkTrafficAnnotationTag(traffic_annotation);
 
   // RequestParams
-
   params.request_params.url = url;
   params.request_params.method = "GET";
 
diff --git a/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.cc b/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.cc
index 6f8ebe1..fc8874f 100644
--- a/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.cc
+++ b/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.cc
@@ -32,6 +32,7 @@
 #include "chrome/browser/chromeos/policy/device_native_printers_handler.h"
 #include "chrome/browser/chromeos/policy/device_network_configuration_updater.h"
 #include "chrome/browser/chromeos/policy/device_policy_cloud_external_data_manager.h"
+#include "chrome/browser/chromeos/policy/device_scheduled_update_checker.h"
 #include "chrome/browser/chromeos/policy/device_wallpaper_image_handler.h"
 #include "chrome/browser/chromeos/policy/device_wifi_allowed_handler.h"
 #include "chrome/browser/chromeos/policy/device_wilco_dtc_configuration_handler.h"
@@ -250,6 +251,10 @@
   tpm_auto_update_mode_policy_handler_ =
       std::make_unique<TPMAutoUpdateModePolicyHandler>(
           chromeos::CrosSettings::Get(), local_state);
+
+  device_scheduled_update_checker_ =
+      std::make_unique<DeviceScheduledUpdateChecker>(
+          chromeos::CrosSettings::Get());
 }
 
 void BrowserPolicyConnectorChromeOS::PreShutdown() {
diff --git a/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h b/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h
index cc4305a..8746506 100644
--- a/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h
+++ b/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h
@@ -8,6 +8,7 @@
 #include <memory>
 #include <set>
 #include <string>
+#include <vector>
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
@@ -54,6 +55,7 @@
 class ServerBackedStateKeysBroker;
 class DeviceWilcoDtcConfigurationHandler;
 class TPMAutoUpdateModePolicyHandler;
+class DeviceScheduledUpdateChecker;
 
 // Extends ChromeBrowserPolicyConnector with the setup specific to Chrome OS.
 class BrowserPolicyConnectorChromeOS
@@ -243,6 +245,8 @@
   std::unique_ptr<DeviceWiFiAllowedHandler> device_wifi_allowed_handler_;
   std::unique_ptr<TPMAutoUpdateModePolicyHandler>
       tpm_auto_update_mode_policy_handler_;
+  std::unique_ptr<DeviceScheduledUpdateChecker>
+      device_scheduled_update_checker_;
 
   // This policy provider is used on Chrome OS to feed user policy into the
   // global PolicyService instance. This works by installing the cloud policy
diff --git a/chrome/browser/chromeos/policy/device_local_account_browsertest.cc b/chrome/browser/chromeos/policy/device_local_account_browsertest.cc
index fd8d880..7fdc643 100644
--- a/chrome/browser/chromeos/policy/device_local_account_browsertest.cc
+++ b/chrome/browser/chromeos/policy/device_local_account_browsertest.cc
@@ -1599,7 +1599,7 @@
   // Start the platform app, causing it to open a window.
   run_loop_.reset(new base::RunLoop);
   OpenApplication(AppLaunchParams(
-      profile, app, extensions::LAUNCH_CONTAINER_NONE,
+      profile, app->id(), extensions::LAUNCH_CONTAINER_NONE,
       WindowOpenDisposition::NEW_WINDOW, extensions::SOURCE_TEST));
   run_loop_->Run();
   EXPECT_EQ(1U, app_window_registry->app_windows().size());
diff --git a/chrome/browser/chromeos/policy/device_scheduled_update_checker.cc b/chrome/browser/chromeos/policy/device_scheduled_update_checker.cc
new file mode 100644
index 0000000..6fd27e5
--- /dev/null
+++ b/chrome/browser/chromeos/policy/device_scheduled_update_checker.cc
@@ -0,0 +1,502 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/policy/device_scheduled_update_checker.h"
+
+#include <time.h>
+
+#include <string>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/sequenced_task_runner.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/values.h"
+#include "chromeos/settings/cros_settings_names.h"
+
+namespace policy {
+
+namespace update_checker_internal {
+
+base::Optional<base::Time> IncrementMonthAndSetDayOfMonth(
+    base::Time::Exploded local_exploded_time,
+    int day_of_month) {
+  ++local_exploded_time.month;
+  if (local_exploded_time.month == 13) {
+    local_exploded_time.month = 1;
+    ++local_exploded_time.year;
+  }
+  local_exploded_time.day_of_month = day_of_month;
+  DCHECK(local_exploded_time.HasValidValues());
+
+  // This can fail if there is a concurrent DST or time zone change.
+  base::Time time;
+  if (!base::Time::FromLocalExploded(local_exploded_time, &time))
+    return base::nullopt;
+  return time;
+}
+
+}  // namespace update_checker_internal
+
+namespace {
+
+// Number of days in a week.
+constexpr int kDaysInAWeek = 7;
+
+// The tag associated to register |update_check_timer_|.
+constexpr char kUpdateCheckTimerTag[] = "DeviceScheduledUpdateChecker";
+
+// The tag associated to register |start_update_check_retry_timer_|.
+constexpr char kStartUpdateCheckRetryTimerTag[] =
+    "DeviceScheduledUpdateCheckerRetry";
+
+DeviceScheduledUpdateChecker::ScheduledUpdateCheckData::Frequency GetFrequency(
+    const std::string& frequency) {
+  if (frequency == "DAILY") {
+    return DeviceScheduledUpdateChecker::ScheduledUpdateCheckData::Frequency::
+        kDaily;
+  }
+
+  if (frequency == "WEEKLY") {
+    return DeviceScheduledUpdateChecker::ScheduledUpdateCheckData::Frequency::
+        kWeekly;
+  }
+
+  DCHECK_EQ(frequency, "MONTHLY");
+  return DeviceScheduledUpdateChecker::ScheduledUpdateCheckData::Frequency::
+      kMonthly;
+}
+
+// Convert the string day of week to an integer value suitable for
+// |base::Time::Exploded::day_of_week|.
+int StringDayOfWeekToExplodedTimeDayOfWeek(const std::string& day_of_week) {
+  if (day_of_week == "SUNDAY")
+    return 0;
+  if (day_of_week == "MONDAY")
+    return 1;
+  if (day_of_week == "TUESDAY")
+    return 2;
+  if (day_of_week == "WEDNESDAY")
+    return 3;
+  if (day_of_week == "THURSDAY")
+    return 4;
+  if (day_of_week == "FRIDAY")
+    return 5;
+  DCHECK_EQ(day_of_week, "SATURDAY");
+  return 6;
+}
+
+// Parses |value| into a |ScheduledUpdateCheckData|. Returns nullopt if there
+// is any error while parsing |value|.
+base::Optional<DeviceScheduledUpdateChecker::ScheduledUpdateCheckData>
+ParseScheduledUpdate(const base::Value* value) {
+  DeviceScheduledUpdateChecker::ScheduledUpdateCheckData result;
+  // Parse mandatory values first i.e. hour, minute and frequency of update
+  // check. These should always be present due to schema validation at higher
+  // layers.
+  const base::Value* hour_value = value->FindPathOfType(
+      {"update_check_time", "hour"}, base::Value::Type::INTEGER);
+  DCHECK(hour_value);
+  int hour = hour_value->GetInt();
+  // Validated by schema validation at higher layers.
+  DCHECK(hour >= 0 && hour <= 23);
+  result.hour = hour;
+
+  const base::Value* minute_value = value->FindPathOfType(
+      {"update_check_time", "minute"}, base::Value::Type::INTEGER);
+  DCHECK(minute_value);
+  int minute = minute_value->GetInt();
+  // Validated by schema validation at higher layers.
+  DCHECK(minute >= 0 && minute <= 59);
+  result.minute = minute;
+
+  // Validated by schema validation at higher layers.
+  const std::string* frequency = value->FindStringKey({"frequency"});
+  DCHECK(frequency);
+  result.frequency = GetFrequency(*frequency);
+
+  // Parse extra fields for weekly and monthly frequencies.
+  switch (result.frequency) {
+    case DeviceScheduledUpdateChecker::ScheduledUpdateCheckData::Frequency::
+        kDaily:
+      break;
+
+    case DeviceScheduledUpdateChecker::ScheduledUpdateCheckData::Frequency::
+        kWeekly: {
+      const std::string* day_of_week = value->FindStringKey({"day_of_week"});
+      if (!day_of_week) {
+        LOG(ERROR) << "Day of week missing";
+        return base::nullopt;
+      }
+
+      // Validated by schema validation at higher layers.
+      result.day_of_week = StringDayOfWeekToExplodedTimeDayOfWeek(*day_of_week);
+      break;
+    }
+
+    case DeviceScheduledUpdateChecker::ScheduledUpdateCheckData::Frequency::
+        kMonthly: {
+      base::Optional<int> day_of_month = value->FindIntKey({"day_of_month"});
+      if (!day_of_month) {
+        LOG(ERROR) << "Day of month missing";
+        return base::nullopt;
+      }
+
+      // Validated by schema validation at higher layers.
+      // TODO(crbug.com/924762): Currently |day_of_month| is restricted to 28 to
+      // make month rollover calculations easier. Fix it to be smart enough to
+      // handle all 31 days and month and year rollovers.
+      DCHECK_LE(day_of_month.value(), 28);
+      result.day_of_month = day_of_month.value();
+      break;
+    }
+  }
+
+  return result;
+}
+
+// Calculates the next update check time in a weekly policy. This function
+// assumes the local time uses |kDaysInAWeek| days in a week.
+base::Optional<base::Time> CalculateNextUpdateCheckWeeklyTime(
+    const base::Time cur_time,
+    int hour,
+    int minute,
+    int day_of_week) {
+  // Calculate delay to get to next |day_of_week|.
+  base::Time::Exploded cur_local_exploded_time;
+  cur_time.LocalExplode(&cur_local_exploded_time);
+  base::TimeDelta delay_from_cur;
+  if (day_of_week < cur_local_exploded_time.day_of_week) {
+    delay_from_cur = base::TimeDelta::FromDays(
+        day_of_week + kDaysInAWeek - cur_local_exploded_time.day_of_week);
+  } else {
+    delay_from_cur = base::TimeDelta::FromDays(
+        day_of_week - cur_local_exploded_time.day_of_week);
+  }
+
+  base::Time update_check_time = cur_time + delay_from_cur;
+  base::Time::Exploded update_check_local_exploded_time;
+  update_check_time.LocalExplode(&update_check_local_exploded_time);
+  update_check_local_exploded_time.hour = hour;
+  update_check_local_exploded_time.minute = minute;
+  // This can fail if the timezone changed and the exploded time isn't valid
+  // anymore i.e. a time 01:30 is not valid if the local time jumped from
+  // 01:00 to 02:00.
+  if (!base::Time::FromLocalExploded(update_check_local_exploded_time,
+                                     &update_check_time)) {
+    return base::nullopt;
+  }
+
+  // The greater than case can happen if the update is supposed to happen today
+  // but at an earlier hour i.e today is Sunday 9 AM and the update check is
+  // supposed to happen at Sunday 8 AM. The equal to case can happen when a
+  // policy is set for today at the exact same time or when |UpdateCheck| calls
+  // |StartUpdateCheckTimer|. In both cases advance the time to the next weekly
+  // time.
+  //
+  // TODO(crbug.com/924762): A DST or TZ change can occur right before the call
+  // to calculate |update_check_time| above which would make this next condition
+  // correct. Add observers for both those changes and then recalculate update
+  // check time again.
+  if (cur_time >= update_check_time)
+    update_check_time += base::TimeDelta::FromDays(kDaysInAWeek);
+
+  return update_check_time;
+}
+
+// Calculates the next update check time in a monthly policy.
+base::Optional<base::Time> CalculateNextUpdateCheckMonthlyTime(
+    const base::Time cur_time,
+    int hour,
+    int minute,
+    int day_of_month) {
+  // Calculate delay to get to next |day_of_month|.
+  base::Time::Exploded cur_local_exploded_time;
+  cur_time.LocalExplode(&cur_local_exploded_time);
+
+  base::Time update_check_time;
+  if (day_of_month < cur_local_exploded_time.day_of_month) {
+    base::Optional<base::Time> result =
+        update_checker_internal::IncrementMonthAndSetDayOfMonth(
+            cur_local_exploded_time, day_of_month);
+    if (!result)
+      return base::nullopt;
+    update_check_time = result.value();
+  } else {
+    update_check_time =
+        cur_time + base::TimeDelta::FromDays(
+                       day_of_month - cur_local_exploded_time.day_of_month);
+  }
+
+  // Set hour and minute to get the final update check time.
+  base::Time::Exploded update_check_local_exploded_time;
+  update_check_time.LocalExplode(&update_check_local_exploded_time);
+  update_check_local_exploded_time.hour = hour;
+  update_check_local_exploded_time.minute = minute;
+  if (!base::Time::FromLocalExploded(update_check_local_exploded_time,
+                                     &update_check_time)) {
+    return base::nullopt;
+  }
+
+  // The greater than case can happen if the update is supposed to happen today
+  // but at an earlier hour i.e today is Sunday 9 AM and the update check is
+  // supposed to happen at Sunday 8 AM. The equal to case can happen when a
+  // policy is set for today at the exact same time or when |UpdateCheck| calls
+  // |StartUpdateCheckTimer|. In both cases advance the time to the next monthly
+  // time.
+  if (cur_time >= update_check_time) {
+    base::Optional<base::Time> result =
+        update_checker_internal::IncrementMonthAndSetDayOfMonth(
+            cur_local_exploded_time, day_of_month);
+    if (!result)
+      return base::nullopt;
+    update_check_time = result.value();
+  }
+
+  return update_check_time;
+}
+
+}  // namespace
+
+// |cros_settings_observer_| will be destroyed as part of this object
+// guaranteeing to not run |OnScheduledUpdateCheckDataChanged| after its
+// destruction. Therefore, it's safe to use "this" while adding this observer.
+DeviceScheduledUpdateChecker::DeviceScheduledUpdateChecker(
+    chromeos::CrosSettings* cros_settings)
+    : cros_settings_(cros_settings),
+      cros_settings_observer_(cros_settings_->AddSettingsObserver(
+          chromeos::kDeviceScheduledUpdateCheck,
+          base::BindRepeating(
+              &DeviceScheduledUpdateChecker::OnScheduledUpdateCheckDataChanged,
+              base::Unretained(this)))) {
+  // Check if policy already exists.
+  OnScheduledUpdateCheckDataChanged();
+}
+
+DeviceScheduledUpdateChecker::~DeviceScheduledUpdateChecker() = default;
+
+DeviceScheduledUpdateChecker::ScheduledUpdateCheckData::
+    ScheduledUpdateCheckData() = default;
+DeviceScheduledUpdateChecker::ScheduledUpdateCheckData::
+    ScheduledUpdateCheckData(const ScheduledUpdateCheckData&) = default;
+DeviceScheduledUpdateChecker::ScheduledUpdateCheckData::
+    ~ScheduledUpdateCheckData() = default;
+
+void DeviceScheduledUpdateChecker::UpdateCheck() {
+  // TODO(crbug.com/924762): Add trigger to do the actual update check.
+  // If a policy exists, schedule the next update check timer.
+  if (!scheduled_update_check_data_)
+    return;
+  StartUpdateCheckTimer();
+}
+
+void DeviceScheduledUpdateChecker::OnScheduledUpdateCheckDataChanged() {
+  // If the policy is removed then reset all state.
+  const base::Value* value =
+      cros_settings_->GetPref(chromeos::kDeviceScheduledUpdateCheck);
+  if (!value) {
+    ResetState();
+    return;
+  }
+
+  // Keep any old policy timers running if a new policy is ill-formed and can't
+  // be used to set a new timer.
+  base::Optional<ScheduledUpdateCheckData> scheduled_update_check_data =
+      ParseScheduledUpdate(value);
+  if (!scheduled_update_check_data) {
+    LOG(ERROR) << "Failed to parse policy";
+    return;
+  }
+  scheduled_update_check_data_ = std::move(scheduled_update_check_data);
+
+  // Policy has been updated, calculate and set |update_check_timer_| again.
+  StartUpdateCheckTimer();
+}
+
+base::Optional<base::Time>
+DeviceScheduledUpdateChecker::CalculateNextUpdateCheckTime(
+    base::Time cur_time) {
+  DCHECK(scheduled_update_check_data_);
+
+  // In order to calculate the next update check time first get the current
+  // time and then modify it based on the policy set.
+  base::Time update_check_time;
+  switch (scheduled_update_check_data_->frequency) {
+    case ScheduledUpdateCheckData::Frequency::kDaily: {
+      base::Time::Exploded update_check_local_exploded_time;
+      cur_time.LocalExplode(&update_check_local_exploded_time);
+      update_check_local_exploded_time.hour =
+          scheduled_update_check_data_->hour;
+      update_check_local_exploded_time.minute =
+          scheduled_update_check_data_->minute;
+      DCHECK(update_check_local_exploded_time.HasValidValues());
+      // This can fail if the timezone changed and the exploded time isn't valid
+      // anymore i.e. a time 01:30 is not valid if the local time jumped from
+      // 01:00 to 02:00.
+      if (!base::Time::FromLocalExploded(update_check_local_exploded_time,
+                                         &update_check_time)) {
+        LOG(ERROR) << "Failed to calculate next daily update check time";
+        return base::nullopt;
+      }
+
+      // If the time has passed for today then set the same time for the next
+      // day.
+      //
+      // TODO(crbug.com/924762): A DST or TZ change can occur right before the
+      // call to calculate |update_check_time| above which would make this next
+      // condition correct. Add observers for both those changes and then
+      // recalculate update check time again.
+      if (cur_time >= update_check_time)
+        update_check_time += base::TimeDelta::FromDays(1);
+
+      break;
+    }
+
+    case ScheduledUpdateCheckData::Frequency::kWeekly: {
+      DCHECK(scheduled_update_check_data_->day_of_week);
+      base::Optional<base::Time> result = CalculateNextUpdateCheckWeeklyTime(
+          cur_time, scheduled_update_check_data_->hour,
+          scheduled_update_check_data_->minute,
+          scheduled_update_check_data_->day_of_week.value());
+      if (!result) {
+        LOG(ERROR) << "Failed to calculate next weekly update check time";
+        return base::nullopt;
+      }
+      update_check_time = result.value();
+
+      break;
+    }
+
+    case ScheduledUpdateCheckData::Frequency::kMonthly: {
+      DCHECK(scheduled_update_check_data_->day_of_month);
+      base::Optional<base::Time> result = CalculateNextUpdateCheckMonthlyTime(
+          cur_time, scheduled_update_check_data_->hour,
+          scheduled_update_check_data_->minute,
+          scheduled_update_check_data_->day_of_month.value());
+      if (!result) {
+        LOG(ERROR) << "Failed to calculate next monthly update check time";
+        return base::nullopt;
+      }
+      update_check_time = result.value();
+
+      break;
+    }
+  }
+
+  DCHECK_NE(update_check_time, base::Time());
+  return update_check_time;
+}
+
+void DeviceScheduledUpdateChecker::StartUpdateCheckTimer() {
+  // Cancel any pending calls to |StartUpdateCheckTimer| to avoid redundant
+  // work, one could be lingering due to a call to
+  // |RetryStartUpdateCheckTimer|. If an error occurs while starting the
+  // timer it will be retried again in this function.
+  start_update_check_retry_timer_.reset();
+
+  // For accuracy of the next update check, capture current time as close to the
+  // start of this function as possible.
+  const base::TimeTicks cur_ticks = GetTicksSinceBoot();
+  const base::Time cur_time = GetCurrentTime();
+
+  // Calculate the next update check time. In case there is an error while
+  // calculating, due to concurrent DST or Time Zone changes, then reschedule
+  // this function and try to schedule the update check again. There should only
+  // be one outstanding task to start the timer.
+  base::Optional<base::Time> update_check_time =
+      CalculateNextUpdateCheckTime(cur_time);
+  if (!update_check_time) {
+    RetryStartUpdateCheckTimer();
+    return;
+  }
+  scheduled_update_check_data_->next_update_check_time_ticks =
+      cur_ticks + (update_check_time.value() - cur_time);
+
+  // The timer could be destroyed in |OnScheduledUpdateCheckDataChanged|.
+  if (!update_check_timer_) {
+    update_check_timer_ =
+        std::make_unique<chromeos::NativeTimer>(kUpdateCheckTimerTag);
+  }
+
+  // |update_check_timer_| will be destroyed as part of this object and is
+  // guaranteed to not run callbacks after its destruction. Therefore, it's safe
+  // to use "this" while starting the timer.
+  update_check_timer_->Start(
+      scheduled_update_check_data_->next_update_check_time_ticks,
+      base::BindOnce(&DeviceScheduledUpdateChecker::UpdateCheck,
+                     base::Unretained(this)),
+      base::BindOnce(&DeviceScheduledUpdateChecker::OnTimerStartResult,
+                     base::Unretained(this)));
+}
+
+void DeviceScheduledUpdateChecker::OnTimerStartResult(bool result) {
+  if (!result) {
+    LOG(ERROR) << "Failed to start update check timer";
+    // This method runs either due to |update_check_timer_|'s start operation
+    // failing or |start_update_check_timer_|'s start operation failing. In both
+    // cases it's called by |NativeTimer| and it's best to schedule
+    // |RetryStartUpdateCheckTimer| as a separate task as it destroys the same
+    // |NativeTimer| object inside it.
+    base::SequencedTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::BindOnce(
+            &DeviceScheduledUpdateChecker::RetryStartUpdateCheckTimer,
+            weak_factory_.GetWeakPtr()));
+    return;
+  }
+}
+
+void DeviceScheduledUpdateChecker::RetryStartUpdateCheckTimer() {
+  // Retrying has a limit. In the unlikely scenario this is met, reset all
+  // state. Now an update check can only happen when a new policy comes in or
+  // Chrome is restarted.
+  if (update_check_timer_start_attempts_ >=
+      update_checker_internal::kMaxRetryUpdateCheckIterations) {
+    LOG(ERROR) << "Aborting attempts to start update check timer";
+    ResetState();
+    return;
+  }
+
+  // There can only be one pending call to |StartUpdateCheckTimer| at any given
+  // time. For easier state maintenance, instantiate fresh timers for each
+  // retry attempt. The old timer must be destroyed before creating a new timer
+  // with the same tag as per the semantics of |NativeTimer|. That's why using
+  // std::make_unique with the assignment operator would not have worked here.
+  update_check_timer_.reset();
+  ++update_check_timer_start_attempts_;
+  start_update_check_retry_timer_.reset();
+  start_update_check_retry_timer_ =
+      std::make_unique<chromeos::NativeTimer>(kStartUpdateCheckRetryTimerTag);
+  start_update_check_retry_timer_->Start(
+      GetTicksSinceBoot() +
+          update_checker_internal::kStartUpdateCheckTimerRetryTime,
+      base::BindOnce(&DeviceScheduledUpdateChecker::StartUpdateCheckTimer,
+                     base::Unretained(this)),
+      base::BindOnce(&DeviceScheduledUpdateChecker::OnTimerStartResult,
+                     base::Unretained(this)));
+}
+
+void DeviceScheduledUpdateChecker::ResetState() {
+  weak_factory_.InvalidateWeakPtrs();
+  update_check_timer_start_attempts_ = 0;
+  start_update_check_retry_timer_.reset();
+  update_check_timer_.reset();
+  scheduled_update_check_data_ = base::nullopt;
+}
+
+base::Time DeviceScheduledUpdateChecker::GetCurrentTime() {
+  return base::Time::Now();
+}
+
+base::TimeTicks DeviceScheduledUpdateChecker::GetTicksSinceBoot() {
+  struct timespec ts = {};
+  int ret = clock_gettime(CLOCK_BOOTTIME, &ts);
+  DCHECK_NE(ret, 0);
+  return base::TimeTicks() + base::TimeDelta::FromTimeSpec(ts);
+}
+
+}  // namespace policy
diff --git a/chrome/browser/chromeos/policy/device_scheduled_update_checker.h b/chrome/browser/chromeos/policy/device_scheduled_update_checker.h
new file mode 100644
index 0000000..869902c
--- /dev/null
+++ b/chrome/browser/chromeos/policy/device_scheduled_update_checker.h
@@ -0,0 +1,148 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_POLICY_DEVICE_SCHEDULED_UPDATE_CHECKER_H_
+#define CHROME_BROWSER_CHROMEOS_POLICY_DEVICE_SCHEDULED_UPDATE_CHECKER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "base/time/time.h"
+#include "chrome/browser/chromeos/settings/cros_settings.h"
+#include "chromeos/dbus/power/native_timer.h"
+
+namespace policy {
+
+namespace update_checker_internal {
+
+// Increments month (taking care of year rollovers) and sets day_of_month field
+// to |month| in |local_exploded_time| and returns the corresponding base::Time.
+// |local_exploded_time| should be local time. In case of an error, due to
+// concurrent DST or time zone change, returns base::nullopt.
+base::Optional<base::Time> IncrementMonthAndSetDayOfMonth(
+    base::Time::Exploded exploded_time,
+    int day_of_month);
+
+// The maximum iterations allowed to start an update check timer if the
+// operation fails.
+constexpr int kMaxRetryUpdateCheckIterations = 5;
+
+// Time to call |StartUpdateCheckTimer| again in case it failed.
+constexpr base::TimeDelta kStartUpdateCheckTimerRetryTime =
+    base::TimeDelta::FromMinutes(5);
+
+}  // namespace update_checker_internal
+
+// This class listens for changes in the scheduled update check policy and then
+// manages recurring update checks based on the policy.
+class DeviceScheduledUpdateChecker {
+ public:
+  explicit DeviceScheduledUpdateChecker(chromeos::CrosSettings* cros_settings);
+  virtual ~DeviceScheduledUpdateChecker();
+
+  // Holds the data associated with the current scheduled update check policy.
+  struct ScheduledUpdateCheckData {
+    ScheduledUpdateCheckData();
+    ScheduledUpdateCheckData(const ScheduledUpdateCheckData&);
+    ~ScheduledUpdateCheckData();
+
+    enum class Frequency {
+      kDaily,
+      kWeekly,
+      kMonthly,
+    };
+
+    int hour;
+
+    int minute;
+
+    Frequency frequency;
+
+    // Only set when frequency is |kWeekly|. Corresponds to day_of_week in
+    // base::Time::Exploded. Values between 0 (SUNDAY) to 6 (SATURDAY).
+    base::Optional<int> day_of_week;
+
+    // Only set when frequency is |kMonthly|. Corresponds to day_of_month in
+    // base::Time::Exploded i.e. values between 1 to 28.
+    base::Optional<int> day_of_month;
+
+    // Absolute time ticks when the next update check (i.e. |UpdateCheck|) will
+    // happen.
+    base::TimeTicks next_update_check_time_ticks;
+  };
+
+ protected:
+  // Called when |update_check_timer_| fires. Triggers an update check and
+  // schedules the next update check based on |scheduled_update_check_data_|.
+  virtual void UpdateCheck();
+
+  // Calculates next update check time based on |scheduled_update_check_data_|
+  // and |cur_local_time|. Returns |base::nullopt| if calculation failed due to
+  // a concurrent DST or Time Zone change. Requires
+  // |scheduled_update_check_data_| to be set.
+  virtual base::Optional<base::Time> CalculateNextUpdateCheckTime(
+      base::Time cur_local_time);
+
+ private:
+  // Callback triggered when scheduled update check setting has changed.
+  void OnScheduledUpdateCheckDataChanged();
+
+  // Sets |update_check_timer_| based on |scheduled_update_check_data_|. If the
+  // |update_check_timer_| can't be started due to an error in
+  // |CalculateNextUpdateCheckTime| then reschedules itself to try again.
+  // Requires |scheduled_update_check_data_| to be set.
+  void StartUpdateCheckTimer();
+
+  // Called upon starting |update_check_timer_| or
+  // |start_update_check_retry_timer_|. Indicates whether or not the timer was
+  // started successfully.
+  void OnTimerStartResult(bool result);
+
+  // Cancels any pending |StartUpdateCheckTimer| calls and reschedules it after
+  // a delay.
+  void RetryStartUpdateCheckTimer();
+
+  // Reset all state and cancel all pending tasks.
+  void ResetState();
+
+  // Returns current time.
+  virtual base::Time GetCurrentTime();
+
+  // Returns time ticks from boot including time ticks spent during sleeping.
+  virtual base::TimeTicks GetTicksSinceBoot();
+
+  // The number of attempts for which |update_check_timer_| has tried to be
+  // started.
+  int update_check_timer_start_attempts_ = 0;
+
+  // Used to retrieve Chrome OS settings. Not owned.
+  chromeos::CrosSettings* const cros_settings_;
+
+  std::unique_ptr<chromeos::CrosSettings::ObserverSubscription>
+      cros_settings_observer_;
+
+  // Currently active scheduled update check policy.
+  base::Optional<ScheduledUpdateCheckData> scheduled_update_check_data_;
+
+  // Timer that is scheduled to check for updates.
+  std::unique_ptr<chromeos::NativeTimer> update_check_timer_;
+
+  // Timer that retries |StartUpdateCheckTimer| in case it fails to start
+  // |update_check_timer_|. This needs to be suspend aware as well because the
+  // retry needs to be done before the time for the next update check and
+  // between that time the device maybe suspended indefinitely. Consequently, if
+  // this timer doesn't run in suspend then there is a chance to miss the next
+  // update check.
+  std::unique_ptr<chromeos::NativeTimer> start_update_check_retry_timer_;
+
+  base::WeakPtrFactory<DeviceScheduledUpdateChecker> weak_factory_{this};
+
+  DISALLOW_COPY_AND_ASSIGN(DeviceScheduledUpdateChecker);
+};
+
+}  // namespace policy
+
+#endif  // CHROME_BROWSER_CHROMEOS_POLICY_DEVICE_SCHEDULED_UPDATE_CHECKER_H_
diff --git a/chrome/browser/chromeos/policy/device_scheduled_update_checker_unittest.cc b/chrome/browser/chromeos/policy/device_scheduled_update_checker_unittest.cc
new file mode 100644
index 0000000..c9e8e7c
--- /dev/null
+++ b/chrome/browser/chromeos/policy/device_scheduled_update_checker_unittest.cc
@@ -0,0 +1,410 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/policy/device_scheduled_update_checker.h"
+
+#include <sstream>
+#include <string>
+#include <utility>
+
+#include "base/json/json_reader.h"
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/time/clock.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/settings/scoped_testing_cros_settings.h"
+#include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h"
+#include "chromeos/dbus/power/fake_power_manager_client.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace policy {
+
+namespace {
+
+void DecodeJsonStringAndNormalize(const std::string& json_string,
+                                  base::Value* value) {
+  base::JSONReader reader(base::JSON_ALLOW_TRAILING_COMMAS);
+  base::Optional<base::Value> read_value = reader.ReadToValue(json_string);
+  ASSERT_EQ(reader.GetErrorMessage(), "");
+  ASSERT_TRUE(read_value.has_value());
+  *value = std::move(read_value.value());
+}
+
+// Creates a JSON policy for daily device scheduled update checks.
+std::string CreateDailyScheduledUpdateCheckPolicyJson(int hour, int minute) {
+  return base::StringPrintf(
+      "{\"update_check_time\": {\"hour\": %d, \"minute\":  %d}, \"frequency\": "
+      "\"DAILY\"}",
+      hour, minute);
+}
+
+// Creates a JSON policy for weekly device scheduled update checks.
+std::string CreateWeeklyScheduledUpdateCheckPolicyJson(
+    int hour,
+    int minute,
+    const std::string& day_of_week) {
+  return base::StringPrintf(
+      "{\"update_check_time\": {\"hour\": %d, \"minute\":  %d}, \"frequency\": "
+      "\"WEEKLY\", \"day_of_week\": \"%s\"}",
+      hour, minute, day_of_week.c_str());
+}
+
+// Creates a JSON policy for monthly device scheduled update checks.
+std::string CreateMonthlyScheduledUpdateCheckPolicyJson(int hour,
+                                                        int minute,
+                                                        int day_of_month) {
+  return base::StringPrintf(
+      "{\"update_check_time\": {\"hour\": %d, \"minute\":  %d}, \"frequency\": "
+      "\"MONTHLY\", \"day_of_month\": %d}",
+      hour, minute, day_of_month);
+}
+
+// Convert the string day of week to an integer value suitable for
+// |base::Time::Exploded::day_of_week|. Returns false if |day_of_week| is
+// invalid.
+std::string ExplodedTimeDayOfWeekToStringDayOfWeek(int day_of_week) {
+  if (day_of_week == 0)
+    return "SUNDAY";
+  if (day_of_week == 1)
+    return "MONDAY";
+  if (day_of_week == 2)
+    return "TUESDAY";
+  if (day_of_week == 3)
+    return "WEDNESDAY";
+  if (day_of_week == 4)
+    return "THURSDAY";
+  if (day_of_week == 5)
+    return "FRIDAY";
+  return "SATURDAY";
+}
+
+// Increment |input_base_time| by |hours| and put the result in
+// |output_base_time| and |output_exploded_time|.
+void IncrementTimeByHours(const base::Time& input_time,
+                          int hours,
+                          base::Time* output_time,
+                          base::Time::Exploded* output_exploded_time) {
+  *output_time = input_time + base::TimeDelta::FromHours(hours);
+  output_time->LocalExplode(output_exploded_time);
+}
+
+}  // namespace
+
+class DeviceScheduledUpdateCheckerForTest
+    : public DeviceScheduledUpdateChecker {
+ public:
+  DeviceScheduledUpdateCheckerForTest(chromeos::CrosSettings* cros_settings,
+                                      const base::Clock* clock,
+                                      const base::TickClock* tick_clock)
+      : DeviceScheduledUpdateChecker(cros_settings),
+        clock_(clock),
+        tick_clock_(tick_clock) {}
+  ~DeviceScheduledUpdateCheckerForTest() override = default;
+
+  int GetUpdateChecks() const { return update_checks_; }
+
+  void SimulateCalculateNextUpdateCheckFailure(bool simulate) {
+    simulate_calculate_next_update_check_failure_ = simulate;
+  }
+
+ private:
+  void UpdateCheck() override {
+    ++update_checks_;
+    DeviceScheduledUpdateChecker::UpdateCheck();
+  }
+
+  base::Optional<base::Time> CalculateNextUpdateCheckTime(
+      base::Time cur_local_time) override {
+    if (simulate_calculate_next_update_check_failure_)
+      return base::nullopt;
+    return DeviceScheduledUpdateChecker::CalculateNextUpdateCheckTime(
+        cur_local_time);
+  }
+
+  base::Time GetCurrentTime() override { return clock_->Now(); }
+
+  base::TimeTicks GetTicksSinceBoot() override {
+    return tick_clock_->NowTicks();
+  }
+
+  // Clock to use to get current time.
+  const base::Clock* const clock_;
+
+  // Clock to use to calculate time ticks.
+  const base::TickClock* const tick_clock_;
+
+  // Number of times |UpdateCheck| is called.
+  int update_checks_ = 0;
+
+  // If set, then |CalculateNextUpdateCheckTime| returns base::nullopt.
+  bool simulate_calculate_next_update_check_failure_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(DeviceScheduledUpdateCheckerForTest);
+};
+
+class DeviceScheduledUpdateCheckerTest : public testing::Test {
+ protected:
+  DeviceScheduledUpdateCheckerTest()
+      : scoped_task_environment_(
+            base::test::ScopedTaskEnvironment::MainThreadType::IO_MOCK_TIME) {
+    chromeos::PowerManagerClient::InitializeFake();
+    chromeos::FakePowerManagerClient::Get()->set_tick_clock(
+        scoped_task_environment_.GetMockTickClock());
+    device_scheduled_update_checker_ =
+        std::make_unique<DeviceScheduledUpdateCheckerForTest>(
+            chromeos::CrosSettings::Get(),
+            scoped_task_environment_.GetMockClock(),
+            scoped_task_environment_.GetMockTickClock());
+  }
+
+  ~DeviceScheduledUpdateCheckerTest() override {
+    device_scheduled_update_checker_.reset();
+    chromeos::PowerManagerClient::Shutdown();
+  }
+
+  // Sets a daily update check policy and returns true iff it's scheduled
+  // correctly. |hours_from_now| must be > 0.
+  bool CheckDailyUpdateCheck(int hours_fom_now) {
+    DCHECK_GT(hours_fom_now, 0);
+    // Calculate time from one hour from now and set the update check policy to
+    // happen daily at that time.
+    base::Time update_check_time;
+    base::Time::Exploded update_check_exploded_time;
+    const int increment_update_check_hours_by = hours_fom_now;
+    base::TimeDelta delay_from_now =
+        base::TimeDelta::FromHours(increment_update_check_hours_by);
+    IncrementTimeByHours(scoped_task_environment_.GetMockClock()->Now(),
+                         increment_update_check_hours_by, &update_check_time,
+                         &update_check_exploded_time);
+
+    base::Value scheduled_update_check_value;
+    DecodeJsonStringAndNormalize(
+        CreateDailyScheduledUpdateCheckPolicyJson(
+            update_check_exploded_time.hour, update_check_exploded_time.minute),
+        &scheduled_update_check_value);
+
+    // Set a new scheduled update setting, fast forward to right before the
+    // expected update and then check if an update check is not scheduled.
+    const base::TimeDelta small_delay = base::TimeDelta::FromMilliseconds(1);
+    cros_settings_.device_settings()->Set(chromeos::kDeviceScheduledUpdateCheck,
+                                          scheduled_update_check_value);
+    int update_checks = device_scheduled_update_checker_->GetUpdateChecks();
+    scoped_task_environment_.FastForwardBy(delay_from_now - small_delay);
+    if (device_scheduled_update_checker_->GetUpdateChecks() != update_checks)
+      return false;
+
+    // Fast forward to the expected update check time and then check if the
+    // update check is scheduled.
+    update_checks = device_scheduled_update_checker_->GetUpdateChecks();
+    scoped_task_environment_.FastForwardBy(small_delay);
+    if (device_scheduled_update_checker_->GetUpdateChecks() !=
+        (update_checks + 1)) {
+      return false;
+    }
+
+    // An update check should happen every day since the policy is set to daily.
+    update_checks = device_scheduled_update_checker_->GetUpdateChecks();
+    const int days = 5;
+    scoped_task_environment_.FastForwardBy(base::TimeDelta::FromDays(days));
+    if (device_scheduled_update_checker_->GetUpdateChecks() !=
+        (update_checks + days)) {
+      return false;
+    }
+
+    return true;
+  }
+
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  std::unique_ptr<DeviceScheduledUpdateCheckerForTest>
+      device_scheduled_update_checker_;
+  chromeos::ScopedTestingCrosSettings cros_settings_;
+
+  DISALLOW_COPY_AND_ASSIGN(DeviceScheduledUpdateCheckerTest);
+};
+
+TEST_F(DeviceScheduledUpdateCheckerTest, CheckIfDailyUpdateCheckIsScheduled) {
+  EXPECT_TRUE(CheckDailyUpdateCheck(1 /* hours_from_now */));
+}
+
+TEST_F(DeviceScheduledUpdateCheckerTest, CheckIfWeeklyUpdateCheckIsScheduled) {
+  // Set the first update check to happen 49 hours from now (i.e. 1 hour from 2
+  // days from now) and then weekly after.
+  base::Time update_check_time;
+  base::Time::Exploded update_check_exploded_time;
+  const int increment_update_check_hours_by = 49;
+  base::TimeDelta delay_from_now =
+      base::TimeDelta::FromHours(increment_update_check_hours_by);
+  IncrementTimeByHours(scoped_task_environment_.GetMockClock()->Now(),
+                       increment_update_check_hours_by, &update_check_time,
+                       &update_check_exploded_time);
+
+  base::Value scheduled_update_check_value;
+  ASSERT_NO_FATAL_FAILURE(DecodeJsonStringAndNormalize(
+      CreateWeeklyScheduledUpdateCheckPolicyJson(
+          update_check_exploded_time.hour, update_check_exploded_time.minute,
+          ExplodedTimeDayOfWeekToStringDayOfWeek(
+              update_check_exploded_time.day_of_week)),
+      &scheduled_update_check_value));
+
+  // Set a new scheduled update setting, fast forward to right before the
+  // expected update and then check if an update check is not scheduled.
+  const base::TimeDelta small_delay = base::TimeDelta::FromMilliseconds(1);
+  cros_settings_.device_settings()->Set(chromeos::kDeviceScheduledUpdateCheck,
+                                        scheduled_update_check_value);
+  scoped_task_environment_.FastForwardBy(delay_from_now - small_delay);
+  EXPECT_EQ(device_scheduled_update_checker_->GetUpdateChecks(), 0);
+
+  // Fast forward to the expected update check time and then check if the update
+  // check is scheduled.
+  scoped_task_environment_.FastForwardBy(small_delay);
+  EXPECT_EQ(device_scheduled_update_checker_->GetUpdateChecks(), 1);
+
+  // An update check should happen weekly since the policy is set to weekly.
+  scoped_task_environment_.FastForwardBy(base::TimeDelta::FromDays(7));
+  EXPECT_EQ(device_scheduled_update_checker_->GetUpdateChecks(), 2);
+}
+
+TEST_F(DeviceScheduledUpdateCheckerTest, CheckIfMonthlyUpdateCheckIsScheduled) {
+  // Set the first update check to happen 49 hours from now (i.e. 1 hour from 2
+  // days from now) and then monthly after.
+  base::Time update_check_time;
+  base::Time::Exploded update_check_exploded_time;
+  const int increment_update_check_hours_by = 49;
+  base::TimeDelta delay_from_now =
+      base::TimeDelta::FromHours(increment_update_check_hours_by);
+  IncrementTimeByHours(scoped_task_environment_.GetMockClock()->Now(),
+                       increment_update_check_hours_by, &update_check_time,
+                       &update_check_exploded_time);
+
+  base::Value scheduled_update_check_value;
+  ASSERT_NO_FATAL_FAILURE(DecodeJsonStringAndNormalize(
+      CreateMonthlyScheduledUpdateCheckPolicyJson(
+          update_check_exploded_time.hour, update_check_exploded_time.minute,
+          update_check_exploded_time.day_of_month),
+      &scheduled_update_check_value));
+
+  // Set a new scheduled update setting, fast forward to right before the
+  // expected update and then check if an update check is not scheduled.
+  const base::TimeDelta small_delay = base::TimeDelta::FromMilliseconds(1);
+  cros_settings_.device_settings()->Set(chromeos::kDeviceScheduledUpdateCheck,
+                                        scheduled_update_check_value);
+  scoped_task_environment_.FastForwardBy(delay_from_now - small_delay);
+  EXPECT_EQ(device_scheduled_update_checker_->GetUpdateChecks(), 0);
+
+  // Fast forward to the expected update check time and then check if the update
+  // check is scheduled.
+  scoped_task_environment_.FastForwardBy(small_delay);
+  EXPECT_EQ(device_scheduled_update_checker_->GetUpdateChecks(), 1);
+
+  // The next update check should happen at the same day of month next month.
+  base::Optional<base::Time> next_check_time =
+      update_checker_internal::IncrementMonthAndSetDayOfMonth(
+          update_check_exploded_time, update_check_exploded_time.day_of_month);
+  // This should be always set in a virtual time environment.
+  EXPECT_TRUE(next_check_time);
+  delay_from_now =
+      next_check_time.value() - scoped_task_environment_.GetMockClock()->Now();
+  base::Time::Exploded next_check_exploded_time;
+  next_check_time->LocalExplode(&next_check_exploded_time);
+  scoped_task_environment_.FastForwardBy(delay_from_now);
+  EXPECT_EQ(device_scheduled_update_checker_->GetUpdateChecks(), 2);
+}
+
+// Checks if an update check timer can't be started, retries are scheduled to
+// recover from transient errors.
+TEST_F(DeviceScheduledUpdateCheckerTest, CheckRetryLogicEventualSuccess) {
+  // This will simulate an error while calculating the next update check time
+  // and will result in no update checks happening till its set.
+  device_scheduled_update_checker_->SimulateCalculateNextUpdateCheckFailure(
+      true);
+
+  // Calculate time from one hour from now and set the update check policy to
+  // happen daily at that time.
+  base::Time update_check_time;
+  base::Time::Exploded update_check_exploded_time;
+  const int increment_update_check_hours_by = 1;
+  IncrementTimeByHours(scoped_task_environment_.GetMockClock()->Now(),
+                       increment_update_check_hours_by, &update_check_time,
+                       &update_check_exploded_time);
+
+  base::Value scheduled_update_check_value;
+  ASSERT_NO_FATAL_FAILURE(DecodeJsonStringAndNormalize(
+      CreateDailyScheduledUpdateCheckPolicyJson(
+          update_check_exploded_time.hour, update_check_exploded_time.minute),
+      &scheduled_update_check_value));
+
+  // Fast forward time by less than (max retries * retry period) and check that
+  // no update has occurred due to failure being simulated.
+  cros_settings_.device_settings()->Set(chromeos::kDeviceScheduledUpdateCheck,
+                                        scheduled_update_check_value);
+  scoped_task_environment_.FastForwardBy(
+      (update_checker_internal::kMaxRetryUpdateCheckIterations - 2) *
+      update_checker_internal::kStartUpdateCheckTimerRetryTime);
+  EXPECT_EQ(device_scheduled_update_checker_->GetUpdateChecks(), 0);
+
+  // Reset failure mode and fast forward by the retry period. This time it
+  // should succeed in setting an update check timer. No update checks should
+  // happen yet but a check has just been scheduled.
+  device_scheduled_update_checker_->SimulateCalculateNextUpdateCheckFailure(
+      false);
+  scoped_task_environment_.FastForwardBy(
+      update_checker_internal::kStartUpdateCheckTimerRetryTime);
+  EXPECT_EQ(device_scheduled_update_checker_->GetUpdateChecks(), 0);
+
+  // Check if update checks happen daily from now on.
+  const int days = 2;
+  scoped_task_environment_.FastForwardBy(base::TimeDelta::FromDays(days));
+  EXPECT_EQ(device_scheduled_update_checker_->GetUpdateChecks(), days);
+}
+
+// Checks if an update check timer can't be started due to a calculation
+// failure, retries are capped.
+TEST_F(DeviceScheduledUpdateCheckerTest,
+       CheckRetryLogicCapWithCalculationFailure) {
+  // This will simulate an error while calculating the next update check time
+  // and will result in no update checks happening till its set.
+  device_scheduled_update_checker_->SimulateCalculateNextUpdateCheckFailure(
+      true);
+  EXPECT_FALSE(CheckDailyUpdateCheck(1 /* hours_from_now */));
+
+  // Fast forward by max retries * retry period and check that no update has
+  // happened since failure mode is still set.
+  scoped_task_environment_.FastForwardBy(
+      update_checker_internal::kMaxRetryUpdateCheckIterations *
+      update_checker_internal::kStartUpdateCheckTimerRetryTime);
+  EXPECT_EQ(device_scheduled_update_checker_->GetUpdateChecks(), 0);
+
+  // At this point all state has been reset. Reset failure mode and check if
+  // daily update checks happen.
+  device_scheduled_update_checker_->SimulateCalculateNextUpdateCheckFailure(
+      false);
+  EXPECT_TRUE(CheckDailyUpdateCheck(1 /* hours_from_now */));
+}
+
+// Checks if an update check timer can't be started due to a timer start
+// failure, retries are capped.
+TEST_F(DeviceScheduledUpdateCheckerTest,
+       CheckRetryLogicCapWithTimerStartFailure) {
+  // This will simulate an error while starting the update check timer.
+  // and will result in no update checks happening till its set.
+  chromeos::FakePowerManagerClient::Get()->simulate_start_arc_timer_failure(
+      true);
+  EXPECT_FALSE(CheckDailyUpdateCheck(1 /* hours_from_now */));
+
+  // Fast forward by max retries * retry period and check that no update has
+  // happened since failure mode is still set.
+  scoped_task_environment_.FastForwardBy(
+      update_checker_internal::kMaxRetryUpdateCheckIterations *
+      update_checker_internal::kStartUpdateCheckTimerRetryTime);
+  EXPECT_EQ(device_scheduled_update_checker_->GetUpdateChecks(), 0);
+
+  // At this point all state has been reset. Reset failure mode and check if
+  // daily update checks happen.
+  chromeos::FakePowerManagerClient::Get()->simulate_start_arc_timer_failure(
+      false);
+  EXPECT_TRUE(CheckDailyUpdateCheck(1 /* hours_from_now */));
+}
+
+}  // namespace policy
diff --git a/chrome/browser/chromeos/settings/device_settings_provider_unittest.cc b/chrome/browser/chromeos/settings/device_settings_provider_unittest.cc
index c3c22ad..4372e66 100644
--- a/chrome/browser/chromeos/settings/device_settings_provider_unittest.cc
+++ b/chrome/browser/chromeos/settings/device_settings_provider_unittest.cc
@@ -36,10 +36,10 @@
 
 namespace chromeos {
 
-using ::testing::AtLeast;
-using ::testing::AnyNumber;
-using ::testing::Mock;
 using ::testing::_;
+using ::testing::AnyNumber;
+using ::testing::AtLeast;
+using ::testing::Mock;
 
 namespace {
 
@@ -253,6 +253,14 @@
     BuildAndInstallDevicePolicy();
   }
 
+  // Helper routine that sets the device DeviceScheduledUpdateCheck policy
+  void SetDeviceScheduledUpdateCheck(const std::string& json_string) {
+    em::DeviceScheduledUpdateCheckProto* proto =
+        device_policy_->payload().mutable_device_scheduled_update_check();
+    proto->set_device_scheduled_update_check_settings(json_string);
+    BuildAndInstallDevicePolicy();
+  }
+
   void SetPluginVmAllowedSetting(bool plugin_vm_allowed) {
     em::PluginVmAllowedProto* proto =
         device_policy_->payload().mutable_plugin_vm_allowed();
@@ -704,6 +712,22 @@
   VerifyPolicyValue(kDeviceAutoUpdateTimeRestrictions, &test_list);
 }
 
+// Check valid JSON for DeviceScheduledUpdateCheck.
+TEST_F(DeviceSettingsProviderTest, DeviceScheduledUpdateCheckTests) {
+  const std::string json_string =
+      "{\"update_check_time\": {\"hour\": 23, \"minute\": 35}, "
+      "\"frequency\": \"DAILY\", \"day_of_week\": \"MONDAY\",  "
+      "\"day_of_month\": 15}";
+  base::DictionaryValue expected_val;
+  expected_val.SetPath({"update_check_time", "hour"}, base::Value(23));
+  expected_val.SetPath({"update_check_time", "minute"}, base::Value(35));
+  expected_val.Set("frequency", std::make_unique<base::Value>("DAILY"));
+  expected_val.Set("day_of_week", std::make_unique<base::Value>("MONDAY"));
+  expected_val.Set("day_of_month", std::make_unique<base::Value>(15));
+  SetDeviceScheduledUpdateCheck(json_string);
+  VerifyPolicyValue(kDeviceScheduledUpdateCheck, &expected_val);
+}
+
 TEST_F(DeviceSettingsProviderTest, DecodePluginVmAllowedSetting) {
   SetPluginVmAllowedSetting(true);
   EXPECT_EQ(base::Value(true), *provider_->Get(kPluginVmAllowed));
diff --git a/chrome/browser/chromeos/shutdown_policy_browsertest.cc b/chrome/browser/chromeos/shutdown_policy_browsertest.cc
index 185b422..840e90cd 100644
--- a/chrome/browser/chromeos/shutdown_policy_browsertest.cc
+++ b/chrome/browser/chromeos/shutdown_policy_browsertest.cc
@@ -8,6 +8,7 @@
 #include "ash/login_status.h"
 #include "ash/public/cpp/ash_switches.h"
 #include "ash/public/cpp/ash_view_ids.h"
+#include "ash/public/cpp/login_screen_test_api.h"
 #include "ash/public/cpp/system_tray_test_api.h"
 #include "base/bind.h"
 #include "base/command_line.h"
@@ -21,7 +22,6 @@
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/chromeos/login/lock/screen_locker.h"
 #include "chrome/browser/chromeos/login/lock/screen_locker_tester.h"
-#include "chrome/browser/chromeos/login/test/login_screen_tester.h"
 #include "chrome/browser/chromeos/login/ui/login_display_host.h"
 #include "chrome/browser/chromeos/login/ui/webui_login_view.h"
 #include "chrome/browser/chromeos/policy/device_policy_builder.h"
@@ -44,6 +44,18 @@
 
 namespace chromeos {
 
+namespace {
+
+void WaitForShutdownButtonVisibility(bool visible) {
+  int ui_update_count = ash::LoginScreenTestApi::GetUiUpdateCount();
+  while (ash::LoginScreenTestApi::IsShutdownButtonShown() != visible) {
+    ash::LoginScreenTestApi::WaitForUiUpdate(ui_update_count);
+    ui_update_count = ash::LoginScreenTestApi::GetUiUpdateCount();
+  }
+}
+
+}  // namespace
+
 class ShutdownPolicyBaseTest
     : public policy::DevicePolicyCrosBrowserTest,
       public DeviceSettingsService::Observer {
@@ -176,15 +188,6 @@
     ShutdownPolicyBaseTest::TearDownOnMainThread();
   }
 
-  void WaitForShutdownButtonVisibility(bool visible) {
-    ScreenLockerTester tester;
-    int ui_update_count = tester.GetUiUpdateCount();
-    while (tester.IsLockShutdownButtonShown() != visible) {
-      tester.WaitForUiUpdate(ui_update_count);
-      ui_update_count = tester.GetUiUpdateCount();
-    }
-  }
-
  private:
   std::unique_ptr<ui::ScopedAnimationDurationScaleMode> zero_duration_mode_;
 
@@ -244,36 +247,25 @@
     }
   }
 
-  void WaitForShutdownButtonVisibility(bool visible) {
-    test::LoginScreenTester tester;
-    int ui_update_count = tester.GetUiUpdateCount();
-    while (tester.IsShutdownButtonShown() != visible) {
-      tester.WaitForUiUpdate(ui_update_count);
-      ui_update_count = tester.GetUiUpdateCount();
-    }
-  }
-
  private:
   DISALLOW_COPY_AND_ASSIGN(ShutdownPolicyLoginTest);
 };
 
 IN_PROC_BROWSER_TEST_F(ShutdownPolicyLoginTest, PolicyNotSet) {
-  test::LoginScreenTester tester;
-  EXPECT_FALSE(tester.IsRestartButtonShown());
-  EXPECT_TRUE(tester.IsShutdownButtonShown());
+  EXPECT_FALSE(ash::LoginScreenTestApi::IsRestartButtonShown());
+  EXPECT_TRUE(ash::LoginScreenTestApi::IsShutdownButtonShown());
 }
 
 IN_PROC_BROWSER_TEST_F(ShutdownPolicyLoginTest, PolicyChange) {
-  test::LoginScreenTester tester;
   UpdateRebootOnShutdownPolicy(true);
   RefreshDevicePolicy();
   WaitForShutdownButtonVisibility(false);
-  EXPECT_TRUE(tester.IsRestartButtonShown());
+  EXPECT_TRUE(ash::LoginScreenTestApi::IsRestartButtonShown());
 
   UpdateRebootOnShutdownPolicy(false);
   RefreshDevicePolicy();
   WaitForShutdownButtonVisibility(true);
-  EXPECT_FALSE(tester.IsRestartButtonShown());
+  EXPECT_FALSE(ash::LoginScreenTestApi::IsRestartButtonShown());
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/supervision/kiosk_next_flow_observer.cc b/chrome/browser/chromeos/supervision/kiosk_next_flow_observer.cc
index c97e702..81bcc51 100644
--- a/chrome/browser/chromeos/supervision/kiosk_next_flow_observer.cc
+++ b/chrome/browser/chromeos/supervision/kiosk_next_flow_observer.cc
@@ -5,9 +5,12 @@
 #include "chrome/browser/chromeos/supervision/kiosk_next_flow_observer.h"
 
 #include "ash/public/cpp/ash_pref_names.h"
+#include "chrome/browser/extensions/component_loader.h"
+#include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "components/prefs/pref_service.h"
+#include "extensions/browser/extension_system.h"
 
 namespace chromeos {
 namespace supervision {
@@ -32,7 +35,16 @@
     OnboardingFlowModel::Step step,
     OnboardingFlowModel::ExitReason reason) {
   if (reason == OnboardingFlowModel::ExitReason::kUserReachedEnd)
-    profile_->GetPrefs()->SetBoolean(ash::prefs::kKioskNextShellEnabled, true);
+    EnableKioskNext();
+}
+
+void KioskNextFlowObserver::EnableKioskNext() {
+  profile_->GetPrefs()->SetBoolean(ash::prefs::kKioskNextShellEnabled, true);
+
+  extensions::ExtensionSystem::Get(profile_)
+      ->extension_service()
+      ->component_loader()
+      ->AddKioskNextExtension();
 }
 
 }  // namespace supervision
diff --git a/chrome/browser/chromeos/supervision/kiosk_next_flow_observer.h b/chrome/browser/chromeos/supervision/kiosk_next_flow_observer.h
index 3d072da8..a165e36 100644
--- a/chrome/browser/chromeos/supervision/kiosk_next_flow_observer.h
+++ b/chrome/browser/chromeos/supervision/kiosk_next_flow_observer.h
@@ -28,6 +28,10 @@
   void WillExitFlow(OnboardingFlowModel::Step step,
                     OnboardingFlowModel::ExitReason reason) override;
 
+  // Enables Kiosk Next by loading its extension and setting the relevant
+  // prefs.
+  void EnableKioskNext();
+
   Profile* profile_;
   OnboardingFlowModel* flow_model_;
 
diff --git a/chrome/browser/chromeos/supervision/onboarding_controller_impl_unittest.cc b/chrome/browser/chromeos/supervision/onboarding_controller_impl_unittest.cc
index ea1a7f3..b4b0c6f 100644
--- a/chrome/browser/chromeos/supervision/onboarding_controller_impl_unittest.cc
+++ b/chrome/browser/chromeos/supervision/onboarding_controller_impl_unittest.cc
@@ -12,6 +12,8 @@
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/chromeos/supervision/mojom/onboarding_controller.mojom.h"
 #include "chrome/browser/chromeos/supervision/onboarding_constants.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/test_extension_system.h"
 #include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chromeos/constants/chromeos_features.h"
@@ -112,14 +114,22 @@
   void SetUp() override {
     profile_ = IdentityTestEnvironmentProfileAdaptor::
         CreateProfileForIdentityTestEnvironment();
-    identity_test_env_adaptor_ =
-        std::make_unique<IdentityTestEnvironmentProfileAdaptor>(profile_.get());
 
+    identity_test_env_adaptor_ =
+        std::make_unique<IdentityTestEnvironmentProfileAdaptor>(profile());
     identity_test_env()->MakeAccountAvailable(kTestAccountId);
     identity_test_env()->SetPrimaryAccount(kTestAccountId);
 
-    controller_impl_ = std::make_unique<OnboardingControllerImpl>(profile());
+    // Setting up an extension service, because some observers need to load
+    // extensions.
+    extensions::TestExtensionSystem* extension_system(
+        static_cast<extensions::TestExtensionSystem*>(
+            extensions::ExtensionSystem::Get(profile())));
+    extension_system->CreateExtensionService(
+        base::CommandLine::ForCurrentProcess(), base::FilePath(),
+        /*autoupdate_enabled=*/false);
 
+    controller_impl_ = std::make_unique<OnboardingControllerImpl>(profile());
     controller_impl_->BindRequest(mojo::MakeRequest(&controller_));
   }
 
diff --git a/chrome/browser/component_updater/cros_component_installer_chromeos.cc b/chrome/browser/component_updater/cros_component_installer_chromeos.cc
index f12a4b12..dd2744f5 100644
--- a/chrome/browser/component_updater/cros_component_installer_chromeos.cc
+++ b/chrome/browser/component_updater/cros_component_installer_chromeos.cc
@@ -38,9 +38,9 @@
      "1913a5e0a6cad30b6f03e176177e0d7ed62c5d6700a9c66da556d7c3f5d6a47e"},
     {"cros-termina", "770.1",
      "e9d960f84f628e1f42d05de4046bb5b3154b6f1f65c08412c6af57a29aecaffb"},
-    {"rtanalytics-light", "11.0",
+    {"rtanalytics-light", "12.0",
      "69f09d33c439c2ab55bbbe24b47ab55cb3f6c0bd1f1ef46eefea3216ec925038"},
-    {"rtanalytics-full", "11.0",
+    {"rtanalytics-full", "12.0",
      "c93c3e1013c52100a20038b405ac854d69fa889f6dc4fa6f188267051e05e444"},
     {"star-cups-driver", "1.1",
      "6d24de30f671da5aee6d463d9e446cafe9ddac672800a9defe86877dcde6c466"},
diff --git a/chrome/browser/download/save_page_browsertest.cc b/chrome/browser/download/save_page_browsertest.cc
index 53ae183e..74d78f6 100644
--- a/chrome/browser/download/save_page_browsertest.cc
+++ b/chrome/browser/download/save_page_browsertest.cc
@@ -59,6 +59,7 @@
 #include "content/public/common/url_constants.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/download_test_observer.h"
+#include "content/public/test/no_renderer_crashes_assertion.h"
 #include "content/public/test/test_utils.h"
 #include "net/base/filename_util.h"
 #include "net/dns/mock_host_resolver.h"
@@ -507,6 +508,10 @@
 }
 
 IN_PROC_BROWSER_TEST_F(SavePageBrowserTest, SaveViewSourceHTMLOnly) {
+  // TODO(lukasza): https://crbug.com/971811: Disallow renderer crashes once the
+  // bug is fixed.
+  content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
+
   GURL mock_url = embedded_test_server()->GetURL("/save_page/a.htm");
   GURL view_source_url =
       GURL(content::kViewSourceScheme + std::string(":") + mock_url.spec());
diff --git a/chrome/browser/extensions/api/extension_action/extension_action_apitest.cc b/chrome/browser/extensions/api/extension_action/extension_action_apitest.cc
index 57dbca8..f46b801 100644
--- a/chrome/browser/extensions/api/extension_action/extension_action_apitest.cc
+++ b/chrome/browser/extensions/api/extension_action/extension_action_apitest.cc
@@ -21,10 +21,14 @@
 #include "chrome/common/extensions/extension_test_util.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/version_info/channel.h"
+#include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_utils.h"
 #include "extensions/browser/browsertest_util.h"
+#include "extensions/browser/process_manager.h"
 #include "extensions/browser/state_store.h"
+#include "extensions/common/extension.h"
 #include "extensions/common/features/feature_channel.h"
 #include "extensions/common/manifest_constants.h"
 #include "extensions/test/extension_test_message_listener.h"
@@ -115,6 +119,34 @@
       : current_channel_(
             extension_test_util::GetOverrideChannelForActionType(GetParam())) {}
 
+  // Returns true if the |action| has whatever state its default is on the
+  // tab with the given |tab_id|.
+  bool ActionHasDefaultState(const ExtensionAction& action, int tab_id) const {
+    bool is_visible = action.GetIsVisible(tab_id);
+    bool default_is_visible =
+        action.default_state() == ActionInfo::STATE_ENABLED;
+    return is_visible == default_is_visible;
+  }
+
+  // Ensures the |action| is enabled on the tab with the given |tab_id|.
+  void EnsureActionIsEnabled(ExtensionAction* action, int tab_id) const {
+    if (!action->GetIsVisible(tab_id))
+      action->SetIsVisible(tab_id, true);
+  }
+
+  // Returns the id of the currently-active tab.
+  int GetActiveTabId() const {
+    content::WebContents* web_contents =
+        browser()->tab_strip_model()->GetActiveWebContents();
+    return SessionTabHelper::IdForTab(web_contents).id();
+  }
+
+  // Returns the action associated with |extension|.
+  ExtensionAction* GetExtensionAction(const Extension& extension) {
+    auto* action_manager = ExtensionActionManager::Get(profile());
+    return action_manager->GetExtensionAction(extension);
+  }
+
  private:
   std::unique_ptr<ScopedCurrentChannel> current_channel_;
 
@@ -322,25 +354,83 @@
   ASSERT_EQ(1, toolbar_helper->NumberOfBrowserActions());
   EXPECT_EQ(extension->id(), toolbar_helper->GetExtensionId(0));
 
-  auto* action_manager = ExtensionActionManager::Get(profile());
-  ExtensionAction* action = action_manager->GetExtensionAction(*extension);
+  ExtensionAction* action = GetExtensionAction(*extension);
   ASSERT_TRUE(action);
 
-  const int tab_id = SessionTabHelper::IdForTab(
-                         browser()->tab_strip_model()->GetActiveWebContents())
-                         .id();
-  bool is_visible = action->GetIsVisible(tab_id);
-  // The action should only be disabled if that was its default state.
-  EXPECT_EQ(is_visible ? ActionInfo::STATE_ENABLED : ActionInfo::STATE_DISABLED,
-            action->default_state());
-  if (!is_visible)
-    action->SetIsVisible(tab_id, true);
+  const int tab_id = GetActiveTabId();
+  EXPECT_TRUE(ActionHasDefaultState(*action, tab_id));
+  EnsureActionIsEnabled(action, tab_id);
+  EXPECT_FALSE(action->HasPopup(tab_id));
 
   ResultCatcher result_catcher;
   toolbar_helper->Press(0);
   ASSERT_TRUE(result_catcher.GetNextResult()) << result_catcher.message();
 }
 
+// Tests the creation of a popup when one is specified in the manifest.
+IN_PROC_BROWSER_TEST_P(MultiActionAPITest, PopupCreation) {
+  constexpr char kManifestTemplate[] =
+      R"({
+           "name": "Test Clicking",
+           "manifest_version": 2,
+           "version": "0.1",
+           "%s": {
+             "default_popup": "popup.html"
+           }
+         })";
+
+  constexpr char kPopupHtml[] =
+      R"(<!doctype html>
+         <html>
+           <script src="popup.js"></script>
+         </html>)";
+  constexpr char kPopupJs[] =
+      "window.onload = function() { chrome.test.notifyPass(); };";
+
+  TestExtensionDir test_dir;
+  test_dir.WriteManifest(
+      base::StringPrintf(kManifestTemplate, GetManifestKey(GetParam())));
+  test_dir.WriteFile(FILE_PATH_LITERAL("popup.html"), kPopupHtml);
+  test_dir.WriteFile(FILE_PATH_LITERAL("popup.js"), kPopupJs);
+
+  const Extension* extension = LoadExtension(test_dir.UnpackedPath());
+  ASSERT_TRUE(extension);
+
+  std::unique_ptr<BrowserActionTestUtil> toolbar_helper =
+      BrowserActionTestUtil::Create(browser());
+
+  ExtensionAction* action = GetExtensionAction(*extension);
+  ASSERT_TRUE(action);
+
+  const int tab_id = GetActiveTabId();
+  EXPECT_TRUE(ActionHasDefaultState(*action, tab_id));
+  EnsureActionIsEnabled(action, tab_id);
+  EXPECT_TRUE(action->HasPopup(tab_id));
+
+  ResultCatcher result_catcher;
+  toolbar_helper->Press(0);
+  EXPECT_TRUE(result_catcher.GetNextResult()) << result_catcher.message();
+
+  ProcessManager* process_manager = ProcessManager::Get(profile());
+  ProcessManager::FrameSet frames =
+      process_manager->GetRenderFrameHostsForExtension(extension->id());
+  ASSERT_EQ(1u, frames.size());
+  content::RenderFrameHost* render_frame_host = *frames.begin();
+  EXPECT_EQ(extension->GetResourceURL("popup.html"),
+            render_frame_host->GetLastCommittedURL());
+
+  content::WebContents* popup_contents =
+      content::WebContents::FromRenderFrameHost(render_frame_host);
+  ASSERT_TRUE(popup_contents);
+
+  content::WebContentsDestroyedWatcher contents_destroyed(popup_contents);
+  EXPECT_TRUE(content::ExecuteScript(popup_contents, "window.close()"));
+  contents_destroyed.Wait();
+
+  frames = process_manager->GetRenderFrameHostsForExtension(extension->id());
+  EXPECT_EQ(0u, frames.size());
+}
+
 INSTANTIATE_TEST_SUITE_P(,
                          MultiActionAPITest,
                          testing::Values(ActionInfo::TYPE_ACTION,
diff --git a/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc b/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc
index 75d783e8..c67db6d 100644
--- a/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc
+++ b/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc
@@ -259,7 +259,7 @@
   extensions::LaunchContainer launch_container =
       GetLaunchContainer(extensions::ExtensionPrefs::Get(context), extension);
   OpenApplication(AppLaunchParams(Profile::FromBrowserContext(context),
-                                  extension, launch_container,
+                                  extension->id(), launch_container,
                                   WindowOpenDisposition::NEW_FOREGROUND_TAB,
                                   extensions::SOURCE_MANAGEMENT_API));
 
diff --git a/chrome/browser/extensions/api/notifications/notifications_apitest.cc b/chrome/browser/extensions/api/notifications/notifications_apitest.cc
index 599e8ca..70674c4 100644
--- a/chrome/browser/extensions/api/notifications/notifications_apitest.cc
+++ b/chrome/browser/extensions/api/notifications/notifications_apitest.cc
@@ -205,9 +205,10 @@
   }
 
   void LaunchPlatformApp(const Extension* extension) {
-    OpenApplication(AppLaunchParams(
-        browser()->profile(), extension, extensions::LAUNCH_CONTAINER_NONE,
-        WindowOpenDisposition::NEW_WINDOW, extensions::SOURCE_TEST));
+    OpenApplication(AppLaunchParams(browser()->profile(), extension->id(),
+                                    extensions::LAUNCH_CONTAINER_NONE,
+                                    WindowOpenDisposition::NEW_WINDOW,
+                                    extensions::SOURCE_TEST));
   }
 
   std::unique_ptr<NotificationDisplayServiceTester> display_service_tester_;
diff --git a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc
index d07bac6..d793222b 100644
--- a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc
+++ b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc
@@ -89,10 +89,7 @@
     event.SetStringKey(kKeyUrl, params.url);
     event.SetStringKey(kKeyUserName, params.user_name);
     event.SetBoolKey(kKeyIsPhishingUrl, params.is_phishing_url);
-    if (identity_manager_->HasPrimaryAccount()) {
-      event.SetStringKey(kKeyProfileUserName,
-                         identity_manager_->GetPrimaryAccountInfo().email);
-    }
+    event.SetStringKey(kKeyProfileUserName, GetProfileUserName());
     ReportRealtimeEvent(kKeyPasswordReuseEvent, std::move(event));
   }
 }
@@ -115,10 +112,7 @@
     // Convert |params| to a real-time event dictionary and report it.
     base::Value event(base::Value::Type::DICTIONARY);
     event.SetStringKey(kKeyUserName, user_name);
-    if (identity_manager_->HasPrimaryAccount()) {
-      event.SetStringKey(kKeyProfileUserName,
-                         identity_manager_->GetPrimaryAccountInfo().email);
-    }
+    event.SetStringKey(kKeyProfileUserName, GetProfileUserName());
     ReportRealtimeEvent(kKeyPasswordChangedEvent, std::move(event));
   }
 }
@@ -126,13 +120,12 @@
 void SafeBrowsingPrivateEventRouter::OnDangerousDownloadOpened(
     const GURL& url,
     const std::string& file_name,
-    const std::string& download_digest_sha256,
-    const std::string& user_name) {
+    const std::string& download_digest_sha256) {
   api::safe_browsing_private::DangerousDownloadInfo params;
   params.url = url.spec();
   params.file_name = file_name;
   params.download_digest_sha256 = download_digest_sha256;
-  params.user_name = user_name;
+  params.user_name = GetProfileUserName();
 
   // |event_router_| can be null in tests.
   if (event_router_) {
@@ -151,12 +144,8 @@
     base::Value event(base::Value::Type::DICTIONARY);
     event.SetStringKey(kKeyUrl, params.url);
     event.SetStringKey(kKeyFileName, params.file_name);
-    event.SetStringKey(kKeyUserName, params.user_name);
     event.SetStringKey(kKeyDownloadDigestSha256, params.download_digest_sha256);
-    if (identity_manager_->HasPrimaryAccount()) {
-      event.SetStringKey(kKeyProfileUserName,
-                         identity_manager_->GetPrimaryAccountInfo().email);
-    }
+    event.SetStringKey(kKeyProfileUserName, params.user_name);
     ReportRealtimeEvent(kKeyDangerousDownloadEvent, std::move(event));
   }
 }
@@ -164,8 +153,7 @@
 void SafeBrowsingPrivateEventRouter::OnSecurityInterstitialShown(
     const GURL& url,
     const std::string& reason,
-    int net_error_code,
-    const std::string& user_name) {
+    int net_error_code) {
   api::safe_browsing_private::InterstitialInfo params;
   params.url = url.spec();
   params.reason = reason;
@@ -173,7 +161,7 @@
     params.net_error_code =
         std::make_unique<std::string>(base::NumberToString(net_error_code));
   }
-  params.user_name = user_name;
+  params.user_name = GetProfileUserName();
 
   // |event_router_| can be null in tests.
   if (event_router_) {
@@ -193,11 +181,7 @@
     event.SetStringKey(kKeyUrl, params.url);
     event.SetStringKey(kKeyReason, params.reason);
     event.SetIntKey(kKeyNetErrorCode, net_error_code);
-    event.SetStringKey(kKeyUserName, params.user_name);
-    if (identity_manager_->HasPrimaryAccount()) {
-      event.SetStringKey(kKeyProfileUserName,
-                         identity_manager_->GetPrimaryAccountInfo().email);
-    }
+    event.SetStringKey(kKeyProfileUserName, params.user_name);
     event.SetBoolKey(kKeyClickedThrough, false);
     ReportRealtimeEvent(kKeyInterstitialEvent, std::move(event));
   }
@@ -206,8 +190,7 @@
 void SafeBrowsingPrivateEventRouter::OnSecurityInterstitialProceeded(
     const GURL& url,
     const std::string& reason,
-    int net_error_code,
-    const std::string& user_name) {
+    int net_error_code) {
   api::safe_browsing_private::InterstitialInfo params;
   params.url = url.spec();
   params.reason = reason;
@@ -215,7 +198,7 @@
     params.net_error_code =
         std::make_unique<std::string>(base::NumberToString(net_error_code));
   }
-  params.user_name = user_name;
+  params.user_name = GetProfileUserName();
 
   // |event_router_| can be null in tests.
   if (event_router_) {
@@ -235,11 +218,7 @@
     event.SetStringKey(kKeyUrl, params.url);
     event.SetStringKey(kKeyReason, params.reason);
     event.SetIntKey(kKeyNetErrorCode, net_error_code);
-    event.SetStringKey(kKeyUserName, params.user_name);
-    if (identity_manager_->HasPrimaryAccount()) {
-      event.SetStringKey(kKeyProfileUserName,
-                         identity_manager_->GetPrimaryAccountInfo().email);
-    }
+    event.SetStringKey(kKeyProfileUserName, params.user_name);
     event.SetBoolKey(kKeyClickedThrough, true);
     ReportRealtimeEvent(kKeyInterstitialEvent, std::move(event));
   }
@@ -324,4 +303,11 @@
   client_->UploadRealtimeReport(std::move(wrapper), base::DoNothing());
 }
 
+std::string SafeBrowsingPrivateEventRouter::GetProfileUserName() {
+  // |identity_manager_| may be null is some tests.
+  return identity_manager_ && identity_manager_->HasPrimaryAccount()
+             ? identity_manager_->GetPrimaryAccountInfo().email
+             : std::string();
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h
index f7974bf..f029351 100644
--- a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h
+++ b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h
@@ -74,20 +74,17 @@
   // Notifies listeners that the user just opened a dangerous download.
   void OnDangerousDownloadOpened(const GURL& url,
                                  const std::string& file_name,
-                                 const std::string& download_digest_sha256,
-                                 const std::string& user_name);
+                                 const std::string& download_digest_sha256);
 
   // Notifies listeners that the user saw a security interstitial.
   void OnSecurityInterstitialShown(const GURL& url,
                                    const std::string& reason,
-                                   int net_error_code,
-                                   const std::string& user_name);
+                                   int net_error_code);
 
   // Notifies listeners that the user clicked-through a security interstitial.
   void OnSecurityInterstitialProceeded(const GURL& url,
                                        const std::string& reason,
-                                       int net_error_code,
-                                       const std::string& user_name);
+                                       int net_error_code);
 
   void SetCloudPolicyClientForTesting(
       std::unique_ptr<policy::CloudPolicyClient> client);
@@ -101,6 +98,10 @@
   // Report safe browsing event through real-time reporting channel, if enabled.
   void ReportRealtimeEvent(const char* name, base::Value event);
 
+  // Returns the Gaia email address of the account signed in to the profile or
+  // an empty string if the profile is not signed in.
+  std::string GetProfileUserName();
+
   content::BrowserContext* context_;
   identity::IdentityManager* identity_manager_ = nullptr;
   EventRouter* event_router_ = nullptr;
diff --git a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_unittest.cc b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_unittest.cc
index 5be2d3e..bf670267 100644
--- a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_unittest.cc
+++ b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_unittest.cc
@@ -86,19 +86,19 @@
     SafeBrowsingPrivateEventRouterFactory::GetForProfile(&profile_)
         ->OnDangerousDownloadOpened(GURL("https://evil.com/malware.exe"),
                                     "/path/to/malware.exe",
-                                    "sha256_or_malware_exe", "user_name");
+                                    "sha256_or_malware_exe");
   }
 
   void TriggerOnSecurityInterstitialShownEvent() {
     SafeBrowsingPrivateEventRouterFactory::GetForProfile(&profile_)
         ->OnSecurityInterstitialShown(GURL("https://phishing.com/"), "PHISHING",
-                                      0, "user_name");
+                                      0);
   }
 
   void TriggerOnSecurityInterstitialProceededEvent() {
     SafeBrowsingPrivateEventRouterFactory::GetForProfile(&profile_)
         ->OnSecurityInterstitialProceeded(GURL("https://phishing.com/"),
-                                          "PHISHING", -201, "user_name");
+                                          "PHISHING", -201);
   }
 
   void SetUpRouters() {
@@ -202,7 +202,7 @@
             captured_args.FindKey("url")->GetString());
   EXPECT_EQ("/path/to/malware.exe",
             captured_args.FindKey("fileName")->GetString());
-  EXPECT_EQ("user_name", captured_args.FindKey("userName")->GetString());
+  EXPECT_EQ("", captured_args.FindKey("userName")->GetString());
   EXPECT_EQ("sha256_or_malware_exe",
             captured_args.FindKey("downloadDigestSha256")->GetString());
 
@@ -211,8 +211,6 @@
   base::Value* event = wrapper.FindKey(
       SafeBrowsingPrivateEventRouter::kKeyDangerousDownloadEvent);
   EXPECT_NE(nullptr, event);
-  EXPECT_EQ("user_name", *event->FindStringKey(
-                             SafeBrowsingPrivateEventRouter::kKeyUserName));
   EXPECT_EQ(
       "/path/to/malware.exe",
       *event->FindStringKey(SafeBrowsingPrivateEventRouter::kKeyFileName));
@@ -236,7 +234,7 @@
   EXPECT_EQ("https://phishing.com/", captured_args.FindKey("url")->GetString());
   EXPECT_EQ("PHISHING", captured_args.FindKey("reason")->GetString());
   EXPECT_EQ("-201", captured_args.FindKey("netErrorCode")->GetString());
-  EXPECT_EQ("user_name", captured_args.FindKey("userName")->GetString());
+  EXPECT_EQ("", captured_args.FindKey("userName")->GetString());
 
   Mock::VerifyAndClearExpectations(client_);
   EXPECT_EQ(base::Value::Type::DICTIONARY, wrapper.type());
@@ -247,8 +245,6 @@
             *event->FindStringKey(SafeBrowsingPrivateEventRouter::kKeyReason));
   EXPECT_EQ(-201, *event->FindIntKey(
                       SafeBrowsingPrivateEventRouter::kKeyNetErrorCode));
-  EXPECT_EQ("user_name", *event->FindStringKey(
-                             SafeBrowsingPrivateEventRouter::kKeyUserName));
   EXPECT_TRUE(
       *event->FindBoolKey(SafeBrowsingPrivateEventRouter::kKeyClickedThrough));
 }
@@ -270,7 +266,7 @@
   EXPECT_EQ("https://phishing.com/", captured_args.FindKey("url")->GetString());
   EXPECT_EQ("PHISHING", captured_args.FindKey("reason")->GetString());
   EXPECT_FALSE(captured_args.FindKey("netErrorCode"));
-  EXPECT_EQ("user_name", captured_args.FindKey("userName")->GetString());
+  EXPECT_EQ("", captured_args.FindKey("userName")->GetString());
 
   Mock::VerifyAndClearExpectations(client_);
   EXPECT_EQ(base::Value::Type::DICTIONARY, wrapper.type());
@@ -281,8 +277,6 @@
             *event->FindStringKey(SafeBrowsingPrivateEventRouter::kKeyReason));
   EXPECT_EQ(
       0, *event->FindIntKey(SafeBrowsingPrivateEventRouter::kKeyNetErrorCode));
-  EXPECT_EQ("user_name", *event->FindStringKey(
-                             SafeBrowsingPrivateEventRouter::kKeyUserName));
   EXPECT_FALSE(
       *event->FindBoolKey(SafeBrowsingPrivateEventRouter::kKeyClickedThrough));
 }
diff --git a/chrome/browser/extensions/bookmark_app_experimental_navigation_throttle.cc b/chrome/browser/extensions/bookmark_app_experimental_navigation_throttle.cc
index 10a5070..c7cfb6b 100644
--- a/chrome/browser/extensions/bookmark_app_experimental_navigation_throttle.cc
+++ b/chrome/browser/extensions/bookmark_app_experimental_navigation_throttle.cc
@@ -453,7 +453,7 @@
   content::BrowserContext* browser_context = source->GetBrowserContext();
   Profile* profile = Profile::FromBrowserContext(browser_context);
   AppLaunchParams launch_params(
-      profile, bookmark_app.get(), extensions::LAUNCH_CONTAINER_WINDOW,
+      profile, bookmark_app->id(), extensions::LAUNCH_CONTAINER_WINDOW,
       WindowOpenDisposition::CURRENT_TAB, extensions::SOURCE_URL_HANDLER);
   launch_params.override_url = navigation_handle()->GetURL();
   launch_params.opener = source->GetOpener();
diff --git a/chrome/browser/extensions/browsertest_util.cc b/chrome/browser/extensions/browsertest_util.cc
index 76ffadd0..0dc9089 100644
--- a/chrome/browser/extensions/browsertest_util.cc
+++ b/chrome/browser/extensions/browsertest_util.cc
@@ -98,7 +98,7 @@
 
 Browser* LaunchAppBrowser(Profile* profile, const Extension* extension_app) {
   EXPECT_TRUE(OpenApplication(
-      AppLaunchParams(profile, extension_app, LAUNCH_CONTAINER_WINDOW,
+      AppLaunchParams(profile, extension_app->id(), LAUNCH_CONTAINER_WINDOW,
                       WindowOpenDisposition::CURRENT_TAB, SOURCE_TEST)));
 
   Browser* browser = chrome::FindLastActive();
@@ -113,7 +113,7 @@
 Browser* LaunchBrowserForAppInTab(Profile* profile,
                                   const Extension* extension_app) {
   content::WebContents* web_contents = OpenApplication(
-      AppLaunchParams(profile, extension_app, LAUNCH_CONTAINER_TAB,
+      AppLaunchParams(profile, extension_app->id(), LAUNCH_CONTAINER_TAB,
                       WindowOpenDisposition::NEW_FOREGROUND_TAB, SOURCE_TEST));
   DCHECK(web_contents);
 
diff --git a/chrome/browser/extensions/chrome_extension_host_delegate.cc b/chrome/browser/extensions/chrome_extension_host_delegate.cc
index c8f51bcc..4bd293f 100644
--- a/chrome/browser/extensions/chrome_extension_host_delegate.cc
+++ b/chrome/browser/extensions/chrome_extension_host_delegate.cc
@@ -109,7 +109,8 @@
   return g_queue.Get().queue.get();
 }
 
-gfx::Size ChromeExtensionHostDelegate::EnterPictureInPicture(
+content::PictureInPictureResult
+ChromeExtensionHostDelegate::EnterPictureInPicture(
     content::WebContents* web_contents,
     const viz::SurfaceId& surface_id,
     const gfx::Size& natural_size) {
diff --git a/chrome/browser/extensions/chrome_extension_host_delegate.h b/chrome/browser/extensions/chrome_extension_host_delegate.h
index f83b368..552f245f 100644
--- a/chrome/browser/extensions/chrome_extension_host_delegate.h
+++ b/chrome/browser/extensions/chrome_extension_host_delegate.h
@@ -33,9 +33,10 @@
                                   blink::mojom::MediaStreamType type,
                                   const Extension* extension) override;
   ExtensionHostQueue* GetExtensionHostQueue() const override;
-  gfx::Size EnterPictureInPicture(content::WebContents* web_contents,
-                                  const viz::SurfaceId& surface_id,
-                                  const gfx::Size& natural_size) override;
+  content::PictureInPictureResult EnterPictureInPicture(
+      content::WebContents* web_contents,
+      const viz::SurfaceId& surface_id,
+      const gfx::Size& natural_size) override;
   void ExitPictureInPicture() override;
 };
 
diff --git a/chrome/browser/extensions/component_loader.cc b/chrome/browser/extensions/component_loader.cc
index cc96331..d72a6d6 100644
--- a/chrome/browser/extensions/component_loader.cc
+++ b/chrome/browser/extensions/component_loader.cc
@@ -541,17 +541,7 @@
     AddZipArchiverExtension();
 #endif  // BUILDFLAG(ENABLE_NACL)
 
-#if defined(KIOSK_NEXT)
-    if (base::FeatureList::IsEnabled(ash::features::kKioskNextShell)) {
-      // Always install KioskNextHome for testing if the feature is enabled.
-      if (enable_background_extensions_during_testing ||
-          profile_->GetPrefs()->GetBoolean(
-              ash::prefs::kKioskNextShellEnabled)) {
-        Add(IDR_KIOSK_NEXT_HOME_MANIFEST,
-            base::FilePath(FILE_PATH_LITERAL("chromeos/kiosk_next_home")));
-      }
-    }
-#endif  // defined(KIOSK_NEXT)
+    AddKioskNextExtension();
 
 #if defined(GOOGLE_CHROME_BUILD)
     std::string id = Add(IDR_QUICKOFFICE_MANIFEST,
@@ -668,6 +658,19 @@
                           extension_misc::kEspeakSpeechSynthesisExtensionId));
 }
 
+void ComponentLoader::AddKioskNextExtension() {
+#if defined(KIOSK_NEXT)
+  if (base::FeatureList::IsEnabled(ash::features::kKioskNextShell)) {
+    // Always install KioskNextHome for testing if the feature is enabled.
+    if (enable_background_extensions_during_testing ||
+        profile_->GetPrefs()->GetBoolean(ash::prefs::kKioskNextShellEnabled)) {
+      Add(IDR_KIOSK_NEXT_HOME_MANIFEST,
+          base::FilePath(FILE_PATH_LITERAL("chromeos/kiosk_next_home")));
+    }
+  }
+#endif  // defined(KIOSK_NEXT)
+}
+
 void ComponentLoader::EnableFileSystemInGuestMode(const std::string& id) {
   if (!IsNormalSession()) {
     // TODO(dpolukhin): Hack to enable HTML5 temporary file system for
diff --git a/chrome/browser/extensions/component_loader.h b/chrome/browser/extensions/component_loader.h
index d45b98a..e45408b4 100644
--- a/chrome/browser/extensions/component_loader.h
+++ b/chrome/browser/extensions/component_loader.h
@@ -109,6 +109,10 @@
                                         const std::string& description_string);
 
   void AddChromeOsSpeechSynthesisExtensions();
+
+  // Loads the Kiosk Next extension or adds it to the load list. If this device
+  // doesn't support Kiosk Next, this method is a no-op.
+  void AddKioskNextExtension();
 #endif
 
   void set_ignore_whitelist_for_testing(bool value) {
diff --git a/chrome/browser/extensions/cross_origin_read_blocking_browsertest.cc b/chrome/browser/extensions/cross_origin_read_blocking_browsertest.cc
index 4f0886e..b555f46 100644
--- a/chrome/browser/extensions/cross_origin_read_blocking_browsertest.cc
+++ b/chrome/browser/extensions/cross_origin_read_blocking_browsertest.cc
@@ -1008,7 +1008,7 @@
   {
     content::WebContentsAddedObserver new_contents_observer;
     OpenApplication(AppLaunchParams(
-        browser()->profile(), app, LAUNCH_CONTAINER_NONE,
+        browser()->profile(), app->id(), LAUNCH_CONTAINER_NONE,
         WindowOpenDisposition::NEW_WINDOW, extensions::SOURCE_TEST));
     app_contents = new_contents_observer.GetWebContents();
   }
diff --git a/chrome/browser/extensions/extension_apitest.cc b/chrome/browser/extensions/extension_apitest.cc
index f8981cc..dfe81e4f 100644
--- a/chrome/browser/extensions/extension_apitest.cc
+++ b/chrome/browser/extensions/extension_apitest.cc
@@ -293,7 +293,7 @@
     else
       ui_test_utils::NavigateToURL(browser(), url);
   } else if (launch_platform_app) {
-    AppLaunchParams params(browser()->profile(), extension,
+    AppLaunchParams params(browser()->profile(), extension->id(),
                            LAUNCH_CONTAINER_NONE,
                            WindowOpenDisposition::NEW_WINDOW, SOURCE_TEST);
     params.command_line = *base::CommandLine::ForCurrentProcess();
diff --git a/chrome/browser/extensions/extension_browsertest.cc b/chrome/browser/extensions/extension_browsertest.cc
index 5a63e5a..4a50970 100644
--- a/chrome/browser/extensions/extension_browsertest.cc
+++ b/chrome/browser/extensions/extension_browsertest.cc
@@ -424,7 +424,7 @@
   content::WindowedNotificationObserver app_loaded_observer(
       content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
       content::NotificationService::AllSources());
-  AppLaunchParams params(profile(), app, LAUNCH_CONTAINER_NONE,
+  AppLaunchParams params(profile(), app->id(), LAUNCH_CONTAINER_NONE,
                          WindowOpenDisposition::NEW_WINDOW, SOURCE_TEST);
   params.command_line = *base::CommandLine::ForCurrentProcess();
   OpenApplication(params);
diff --git a/chrome/browser/interstitials/enterprise_util.cc b/chrome/browser/interstitials/enterprise_util.cc
index 81ba4a6a..edc03d35 100644
--- a/chrome/browser/interstitials/enterprise_util.cc
+++ b/chrome/browser/interstitials/enterprise_util.cc
@@ -7,10 +7,8 @@
 #include "chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h"
 #include "chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_factory.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/signin/identity_manager_factory.h"
 #include "content/public/browser/web_contents.h"
 #include "extensions/buildflags/buildflags.h"
-#include "services/identity/public/cpp/identity_manager.h"
 
 namespace {
 
@@ -28,15 +26,6 @@
   return extensions::SafeBrowsingPrivateEventRouterFactory::GetForProfile(
       browser_context);
 }
-
-std::string GetUserName(content::WebContents* web_contents) {
-  Profile* profile =
-      Profile::FromBrowserContext(web_contents->GetBrowserContext());
-  auto* identity_manager =
-      IdentityManagerFactory::GetForProfileIfExists(profile);
-  return identity_manager ? identity_manager->GetPrimaryAccountInfo().email
-                          : std::string();
-}
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
 }  // namespace
@@ -51,8 +40,7 @@
       GetEventRouter(web_contents);
   if (!event_router)
     return;
-  event_router->OnSecurityInterstitialShown(page_url, reason, net_error_code,
-                                            GetUserName(web_contents));
+  event_router->OnSecurityInterstitialShown(page_url, reason, net_error_code);
 #endif
 }
 
@@ -66,8 +54,8 @@
       GetEventRouter(web_contents);
   if (!event_router)
     return;
-  event_router->OnSecurityInterstitialProceeded(
-      page_url, reason, net_error_code, GetUserName(web_contents));
+  event_router->OnSecurityInterstitialProceeded(page_url, reason,
+                                                net_error_code);
 #endif
 }
 
diff --git a/chrome/browser/media/cast_mirroring_service_host.cc b/chrome/browser/media/cast_mirroring_service_host.cc
index 151e886..f587d9f 100644
--- a/chrome/browser/media/cast_mirroring_service_host.cc
+++ b/chrome/browser/media/cast_mirroring_service_host.cc
@@ -10,6 +10,7 @@
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/memory/ref_counted.h"
+#include "base/optional.h"
 #include "base/single_thread_task_runner.h"
 #include "base/task/post_task.h"
 #include "chrome/browser/browser_process.h"
@@ -42,6 +43,9 @@
 
 namespace mirroring {
 
+// Default resolution constraint.
+constexpr gfx::Size kMaxResolution(1920, 1080);
+
 namespace {
 
 void CreateVideoCaptureHostOnIO(const std::string& device_id,
@@ -99,35 +103,15 @@
   return media_id;
 }
 
-// Clamped resolution constraint to the screen size.
-gfx::Size GetCaptureResolutionConstraint() {
-  // Default resolution constraint.
-  constexpr gfx::Size kMaxResolution(1920, 1080);
+// Returns the size of the primary display in pixels, or base::nullopt if it
+// cannot be determined.
+base::Optional<gfx::Size> GetScreenResolution() {
   display::Screen* screen = display::Screen::GetScreen();
   if (!screen) {
     DVLOG(1) << "Cannot get the Screen object.";
-    return kMaxResolution;
+    return base::nullopt;
   }
-  const gfx::Size screen_resolution = screen->GetPrimaryDisplay().size();
-  const int width_step = 160;
-  const int height_step = 90;
-  int clamped_width = 0;
-  int clamped_height = 0;
-  if (kMaxResolution.height() * screen_resolution.width() <
-      kMaxResolution.width() * screen_resolution.height()) {
-    clamped_width = std::min(kMaxResolution.width(), screen_resolution.width());
-    clamped_width = clamped_width - (clamped_width % width_step);
-    clamped_height = clamped_width * height_step / width_step;
-  } else {
-    clamped_height =
-        std::min(kMaxResolution.height(), screen_resolution.height());
-    clamped_height = clamped_height - (clamped_height % height_step);
-    clamped_width = clamped_height * width_step / height_step;
-  }
-
-  clamped_width = std::max(clamped_width, width_step);
-  clamped_height = std::max(clamped_height, height_step);
-  return gfx::Size(clamped_width, clamped_height);
+  return screen->GetPrimaryDisplay().GetSizeInPixel();
 }
 
 }  // namespace
@@ -216,6 +200,45 @@
   ShowCaptureIndicator();
 }
 
+// static
+gfx::Size CastMirroringServiceHost::GetCaptureResolutionConstraint() {
+  base::Optional<gfx::Size> screen_resolution = GetScreenResolution();
+  if (screen_resolution) {
+    return GetClampedResolution(screen_resolution.value());
+  } else {
+    return kMaxResolution;
+  }
+}
+
+// static
+gfx::Size CastMirroringServiceHost::GetClampedResolution(
+    gfx::Size screen_resolution) {
+  // Use landscape mode dimensions for screens in portrait mode.
+  if (screen_resolution.height() > screen_resolution.width()) {
+    screen_resolution =
+        gfx::Size(screen_resolution.height(), screen_resolution.width());
+  }
+  const int width_step = 160;
+  const int height_step = 90;
+  int clamped_width = 0;
+  int clamped_height = 0;
+  if (kMaxResolution.height() * screen_resolution.width() <
+      kMaxResolution.width() * screen_resolution.height()) {
+    clamped_width = std::min(kMaxResolution.width(), screen_resolution.width());
+    clamped_width = clamped_width - (clamped_width % width_step);
+    clamped_height = clamped_width * height_step / width_step;
+  } else {
+    clamped_height =
+        std::min(kMaxResolution.height(), screen_resolution.height());
+    clamped_height = clamped_height - (clamped_height % height_step);
+    clamped_width = clamped_height * width_step / height_step;
+  }
+
+  clamped_width = std::max(clamped_width, width_step);
+  clamped_height = std::max(clamped_height, height_step);
+  return gfx::Size(clamped_width, clamped_height);
+}
+
 void CastMirroringServiceHost::GetVideoCaptureHost(
     media::mojom::VideoCaptureHostRequest request) {
   base::PostTaskWithTraits(
diff --git a/chrome/browser/media/cast_mirroring_service_host.h b/chrome/browser/media/cast_mirroring_service_host.h
index bfd0a0a..981df711 100644
--- a/chrome/browser/media/cast_mirroring_service_host.h
+++ b/chrome/browser/media/cast_mirroring_service_host.h
@@ -8,6 +8,7 @@
 #include <memory>
 #include <string>
 
+#include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "components/mirroring/mojom/mirroring_service.mojom.h"
 #include "components/mirroring/mojom/mirroring_service_host.mojom.h"
@@ -17,6 +18,7 @@
 #include "content/public/browser/web_contents_observer.h"
 #include "extensions/buildflags/buildflags.h"
 #include "mojo/public/cpp/bindings/binding.h"
+#include "ui/gfx/geometry/size.h"
 
 // TODO(https://crbug.com/879012): Remove the build flag. OffscreenTab should
 // not only be defined when extension is enabled.
@@ -66,6 +68,12 @@
 
  private:
   friend class CastMirroringServiceHostBrowserTest;
+  FRIEND_TEST_ALL_PREFIXES(CastMirroringServiceHostTest,
+                           TestGetClampedResolution);
+
+  static gfx::Size GetCaptureResolutionConstraint();
+  // Clamp resolution constraint to the screen size.
+  static gfx::Size GetClampedResolution(gfx::Size screen_resolution);
 
   // ResourceProvider implementation.
   void GetVideoCaptureHost(
diff --git a/chrome/browser/media/cast_mirroring_service_host_unittest.cc b/chrome/browser/media/cast_mirroring_service_host_unittest.cc
new file mode 100644
index 0000000..db93c63
--- /dev/null
+++ b/chrome/browser/media/cast_mirroring_service_host_unittest.cc
@@ -0,0 +1,30 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/media/cast_mirroring_service_host.h"
+
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mirroring {
+
+TEST(CastMirroringServiceHostTest, TestGetClampedResolution) {
+  // {screen width, screen height, capture width, capture height}
+  constexpr int test_cases[][4] = {
+      {1280, 800, 1280, 720},   {1366, 768, 1280, 720},
+      {768, 1366, 1280, 720},   {1920, 1080, 1920, 1080},
+      {1546, 2048, 1920, 1080}, {2399, 1598, 1920, 1080},
+      {2560, 1080, 1920, 1080}, {2560, 1600, 1920, 1080},
+      {3840, 2160, 1920, 1080}};
+
+  for (int i = 0; i < 9; i++) {
+    DVLOG(1) << "Testing resolution " << test_cases[i][0] << " x "
+             << test_cases[i][1];
+    EXPECT_EQ(CastMirroringServiceHost::GetClampedResolution(
+                  gfx::Size(test_cases[i][0], test_cases[i][1])),
+              gfx::Size(test_cases[i][2], test_cases[i][3]));
+  }
+}
+
+}  // namespace mirroring
diff --git a/chrome/browser/media/webrtc/system_media_capture_permissions_mac.mm b/chrome/browser/media/webrtc/system_media_capture_permissions_mac.mm
index 90cd4aa..b8c450bc 100644
--- a/chrome/browser/media/webrtc/system_media_capture_permissions_mac.mm
+++ b/chrome/browser/media/webrtc/system_media_capture_permissions_mac.mm
@@ -21,6 +21,7 @@
 #include "base/bind_helpers.h"
 #include "base/callback.h"
 #include "base/callback_helpers.h"
+#include "base/command_line.h"
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/no_destructor.h"
@@ -29,11 +30,17 @@
 #include "chrome/browser/media/webrtc/media_authorization_wrapper_mac.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
+#include "media/base/media_switches.h"
 
 namespace system_media_permissions {
 
 namespace {
 
+bool UsingFakeMediaDevices() {
+  return base::CommandLine::ForCurrentProcess()->HasSwitch(
+      switches::kUseFakeDeviceForMediaStream);
+}
+
 // Pointer to OS call wrapper that tests can set.
 MediaAuthorizationWrapper* g_media_authorization_wrapper_for_tests = nullptr;
 
@@ -109,6 +116,9 @@
 }
 
 SystemPermission CheckSystemMediaCapturePermission(NSString* media_type) {
+  if (UsingFakeMediaDevices())
+    return SystemPermission::kAllowed;
+
   if (@available(macOS 10.14, *)) {
     NSInteger auth_status = MediaAuthorizationStatus(media_type);
     switch (auth_status) {
@@ -135,6 +145,11 @@
 void RequestSystemMediaCapturePermission(NSString* media_type,
                                          base::RepeatingClosure callback,
                                          const base::TaskTraits& traits) {
+  if (UsingFakeMediaDevices()) {
+    base::PostTaskWithTraits(FROM_HERE, traits, std::move(callback));
+    return;
+  }
+
   if (@available(macOS 10.14, *)) {
     GetMediaAuthorizationWrapper().RequestAccessForMediaType(
         media_type, std::move(callback), traits);
diff --git a/chrome/browser/net/reporting_browsertest.cc b/chrome/browser/net/reporting_browsertest.cc
index d672a4d..9a28209 100644
--- a/chrome/browser/net/reporting_browsertest.cc
+++ b/chrome/browser/net/reporting_browsertest.cc
@@ -21,6 +21,7 @@
 #include "content/public/browser/render_process_host.h"
 #include "content/public/common/result_codes.h"
 #include "content/public/common/url_constants.h"
+#include "content/public/test/no_renderer_crashes_assertion.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/reporting/reporting_policy.h"
@@ -202,6 +203,7 @@
   navigation_observer.Wait();
 
   // Simulate a crash on the page.
+  content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
   contents->GetController().LoadURL(GURL(content::kChromeUICrashURL),
                                     content::Referrer(),
                                     ui::PAGE_TRANSITION_TYPED, std::string());
@@ -241,6 +243,7 @@
   navigation_observer.Wait();
 
   // Simulate the page being killed due to being unresponsive.
+  content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
   contents->GetMainFrame()->GetProcess()->Shutdown(content::RESULT_CODE_HUNG);
 
   upload_response()->WaitForRequest();
diff --git a/chrome/browser/notifications/chrome_ash_message_center_client.cc b/chrome/browser/notifications/chrome_ash_message_center_client.cc
index d1374f63..1d27e4a 100644
--- a/chrome/browser/notifications/chrome_ash_message_center_client.cc
+++ b/chrome/browser/notifications/chrome_ash_message_center_client.cc
@@ -21,9 +21,11 @@
 #include "content/public/browser/notification_service.h"
 #include "content/public/common/service_manager_connection.h"
 #include "services/service_manager/public/cpp/connector.h"
+#include "ui/message_center/message_center.h"
 #include "ui/message_center/public/cpp/notifier_id.h"
 
 using ash::mojom::NotifierUiDataPtr;
+using message_center::MessageCenter;
 using message_center::NotifierId;
 
 namespace {
@@ -57,6 +59,49 @@
   icu::Collator* collator_;
 };
 
+// This delegate forwards NotificationDelegate methods to their equivalent in
+// NotificationPlatformBridgeDelegate.
+class ForwardingNotificationDelegate
+    : public message_center::NotificationDelegate {
+ public:
+  ForwardingNotificationDelegate(const std::string& notification_id,
+                                 NotificationPlatformBridgeDelegate* delegate)
+      : notification_id_(notification_id), delegate_(delegate) {}
+
+  // message_center::NotificationDelegate:
+  void Close(bool by_user) override {
+    delegate_->HandleNotificationClosed(notification_id_, by_user);
+  }
+
+  void Click(const base::Optional<int>& button_index,
+             const base::Optional<base::string16>& reply) override {
+    if (button_index) {
+      delegate_->HandleNotificationButtonClicked(notification_id_,
+                                                 *button_index, reply);
+    } else {
+      delegate_->HandleNotificationClicked(notification_id_);
+    }
+  }
+
+  void SettingsClick() override {
+    delegate_->HandleNotificationSettingsButtonClicked(notification_id_);
+  }
+
+  void DisableNotification() override {
+    delegate_->DisableNotification(notification_id_);
+  }
+
+ private:
+  ~ForwardingNotificationDelegate() override = default;
+
+  // The ID of the notification.
+  const std::string notification_id_;
+
+  NotificationPlatformBridgeDelegate* delegate_;
+
+  DISALLOW_COPY_AND_ASSIGN(ForwardingNotificationDelegate);
+};
+
 }  // namespace
 
 ChromeAshMessageCenterClient::ChromeAshMessageCenterClient(
@@ -101,59 +146,23 @@
 
 void ChromeAshMessageCenterClient::Display(
     const message_center::Notification& notification) {
-  // Null in unit tests.
-  if (!controller_)
-    return;
-
-  // Remove any previous mapping to |notification.id()| before inserting a new
-  // one.
-  base::EraseIf(
-      displayed_notifications_,
-      [notification](
-          const std::pair<base::UnguessableToken, std::string>& pair) {
-        return pair.second == notification.id();
-      });
-
-  base::UnguessableToken token = base::UnguessableToken::Create();
-  displayed_notifications_[token] = notification.id();
-  controller_->ShowClientNotification(notification, token);
+  auto message_center_notification =
+      std::make_unique<message_center::Notification>(
+          base::WrapRefCounted(
+              new ForwardingNotificationDelegate(notification.id(), delegate_)),
+          notification);
+  MessageCenter::Get()->AddNotification(std::move(message_center_notification));
 }
 
 void ChromeAshMessageCenterClient::Close(const std::string& notification_id) {
-  controller_->CloseClientNotification(notification_id);
-}
-
-void ChromeAshMessageCenterClient::HandleNotificationClosed(
-    const base::UnguessableToken& display_token,
-    bool by_user) {
-  auto entry = displayed_notifications_.find(display_token);
-  if (entry != displayed_notifications_.end()) {
-    delegate_->HandleNotificationClosed(entry->second, by_user);
-    displayed_notifications_.erase(entry);
+  // During shutdown, Ash is destroyed before |this|, taking the MessageCenter
+  // with it.
+  if (MessageCenter::Get()) {
+    MessageCenter::Get()->RemoveNotification(notification_id,
+                                             false /* by_user */);
   }
 }
 
-void ChromeAshMessageCenterClient::HandleNotificationClicked(
-    const std::string& id) {
-  delegate_->HandleNotificationClicked(id);
-}
-
-void ChromeAshMessageCenterClient::HandleNotificationButtonClicked(
-    const std::string& id,
-    int button_index,
-    const base::Optional<base::string16>& reply) {
-  delegate_->HandleNotificationButtonClicked(id, button_index, reply);
-}
-
-void ChromeAshMessageCenterClient::HandleNotificationSettingsButtonClicked(
-    const std::string& id) {
-  delegate_->HandleNotificationSettingsButtonClicked(id);
-}
-
-void ChromeAshMessageCenterClient::DisableNotification(const std::string& id) {
-  delegate_->DisableNotification(id);
-}
-
 void ChromeAshMessageCenterClient::SetNotifierEnabled(
     const NotifierId& notifier_id,
     bool enabled) {
diff --git a/chrome/browser/notifications/chrome_ash_message_center_client.h b/chrome/browser/notifications/chrome_ash_message_center_client.h
index 112f944..9f5cff0 100644
--- a/chrome/browser/notifications/chrome_ash_message_center_client.h
+++ b/chrome/browser/notifications/chrome_ash_message_center_client.h
@@ -6,7 +6,6 @@
 #define CHROME_BROWSER_NOTIFICATIONS_CHROME_ASH_MESSAGE_CENTER_CLIENT_H_
 
 #include "ash/public/interfaces/ash_message_center_controller.mojom.h"
-#include "base/unguessable_token.h"
 #include "chrome/browser/notifications/notification_platform_bridge.h"
 #include "chrome/browser/notifications/notification_platform_bridge_chromeos.h"
 #include "chrome/browser/notifications/notifier_controller.h"
@@ -33,15 +32,6 @@
   void Close(const std::string& notification_id);
 
   // ash::mojom::AshMessageCenterClient:
-  void HandleNotificationClosed(const base::UnguessableToken& display_token,
-                                bool by_user) override;
-  void HandleNotificationClicked(const std::string& id) override;
-  void HandleNotificationButtonClicked(
-      const std::string& id,
-      int button_index,
-      const base::Optional<base::string16>& reply) override;
-  void HandleNotificationSettingsButtonClicked(const std::string& id) override;
-  void DisableNotification(const std::string& id) override;
   void SetNotifierEnabled(const message_center::NotifierId& notifier_id,
                           bool enabled) override;
   void GetNotifierList(GetNotifierListCallback callback) override;
@@ -70,14 +60,6 @@
 
   NotificationPlatformBridgeDelegate* delegate_;
 
-  // A mapping from display token to notification ID. The display token is
-  // generated each time a notification is shown (even if a notification is
-  // displayed more than once). This allows |this| to drop out-of-order
-  // HandleNotificationClosed() calls (i.e. those that arrive after the
-  // notification has already been re-displayed/updated and refer to an earlier
-  // notification).
-  std::map<base::UnguessableToken, std::string> displayed_notifications_;
-
   // Notifier source for each notifier type.
   std::map<message_center::NotifierType, std::unique_ptr<NotifierController>>
       sources_;
diff --git a/chrome/browser/notifications/chrome_ash_message_center_client_browsertest.cc b/chrome/browser/notifications/chrome_ash_message_center_client_browsertest.cc
deleted file mode 100644
index 32fd511..0000000
--- a/chrome/browser/notifications/chrome_ash_message_center_client_browsertest.cc
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/notifications/chrome_ash_message_center_client.h"
-
-#include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/notifications/notification_display_service.h"
-#include "chrome/browser/notifications/notification_test_util.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "ui/message_center/public/cpp/notification_delegate.h"
-
-namespace {
-
-using ChromeAshMessageCenterClientBrowserTest = InProcessBrowserTest;
-
-class TestNotificationDelegate : public message_center::NotificationDelegate {
- public:
-  TestNotificationDelegate() {}
-
-  void Wait() { run_loop_.Run(); }
-
-  void Close(bool by_user) override {
-    close_count_++;
-    run_loop_.Quit();
-  }
-
-  int close_count() const { return close_count_; }
-
- private:
-  ~TestNotificationDelegate() override {}
-
-  base::RunLoop run_loop_;
-  int close_count_ = 0;
-
-  DISALLOW_COPY_AND_ASSIGN(TestNotificationDelegate);
-};
-
-// Regression test for https://crbug.com/825141 that verifies out-of-order
-// Display/Close pairs are handled correctly.
-IN_PROC_BROWSER_TEST_F(ChromeAshMessageCenterClientBrowserTest,
-                       DisplayCloseOrdering) {
-  auto delegate = base::MakeRefCounted<TestNotificationDelegate>();
-  const std::string id("notification_identifier");
-  message_center::Notification notification(
-      message_center::NOTIFICATION_TYPE_SIMPLE, id, base::string16(),
-      base::string16(), gfx::Image(), base::ASCIIToUTF16("display_source"),
-      GURL(),
-      message_center::NotifierId(message_center::NotifierType::SYSTEM_COMPONENT,
-                                 "notifier_id"),
-      {}, delegate);
-
-  auto* display_service =
-      NotificationDisplayService::GetForProfile(browser()->profile());
-  display_service->Display(NotificationHandler::Type::TRANSIENT, notification,
-                           /*metadata=*/nullptr);
-  display_service->Close(NotificationHandler::Type::TRANSIENT,
-                         notification.id());
-  // The Close callback should be fired asynchronously, so there is no close
-  // yet.
-  EXPECT_EQ(0, delegate->close_count());
-
-  display_service->Display(NotificationHandler::Type::TRANSIENT, notification,
-                           /*metadata=*/nullptr);
-  display_service->Close(NotificationHandler::Type::TRANSIENT,
-                         notification.id());
-  ChromeAshMessageCenterClient::FlushForTesting();
-
-  // Only one close logged because Display was called again before the first
-  // close arrived.
-  EXPECT_EQ(1, delegate->close_count());
-}
-
-}  // namespace
diff --git a/chrome/browser/notifications/notification_platform_bridge_chromeos_unittest.cc b/chrome/browser/notifications/notification_platform_bridge_chromeos_unittest.cc
index b62f7c3..12ff3d9 100644
--- a/chrome/browser/notifications/notification_platform_bridge_chromeos_unittest.cc
+++ b/chrome/browser/notifications/notification_platform_bridge_chromeos_unittest.cc
@@ -9,11 +9,13 @@
 #include "chrome/test/base/testing_profile.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/message_center/message_center.h"
 #include "ui/message_center/public/cpp/notification.h"
 #include "ui/message_center/public/cpp/notification_delegate.h"
 
 // Regression test for https://crbug.com/840105
 TEST(NotificationPlatformBridgeChromeOsTest, Update) {
+  message_center::MessageCenter::Initialize();
   content::TestBrowserThreadBundle thread_bundle;
   TestingProfile profile;
   NotificationPlatformBridgeChromeOs bridge;
@@ -57,4 +59,6 @@
   bridge.HandleNotificationClicked(permuted_notification.notification().id());
   EXPECT_EQ(1, initial_delegate_clicks);
   EXPECT_EQ(1, updated_delegate_clicks);
+
+  message_center::MessageCenter::Shutdown();
 }
diff --git a/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc b/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc
index e0fa2ea..de5ae4b 100644
--- a/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc
+++ b/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc
@@ -32,7 +32,6 @@
 #include "content/public/common/resource_load_info.mojom.h"
 #include "net/base/ip_endpoint.h"
 #include "net/base/net_errors.h"
-#include "services/network/public/cpp/features.h"
 #include "ui/base/page_transition_types.h"
 
 namespace page_load_metrics {
@@ -312,8 +311,6 @@
     content::RenderFrameHost* render_frame_host,
     const content::GlobalRequestID& request_id,
     const content::mojom::ResourceLoadInfo& resource_load_info) {
-  if (!base::FeatureList::IsEnabled(network::features::kNetworkService))
-    return;
 
   if (!resource_load_info.url.SchemeIsHTTPOrHTTPS())
     return;
@@ -367,39 +364,6 @@
     committed_load_->FrameSizeChanged(render_frame_host, frame_size);
 }
 
-void MetricsWebContentsObserver::OnRequestComplete(
-    const GURL& url,
-    const net::IPEndPoint& remote_endpoint,
-    int frame_tree_node_id,
-    const content::GlobalRequestID& request_id,
-    content::RenderFrameHost* render_frame_host_or_null,
-    content::ResourceType resource_type,
-    bool was_cached,
-    std::unique_ptr<data_reduction_proxy::DataReductionProxyData>
-        data_reduction_proxy_data,
-    int64_t raw_body_bytes,
-    int64_t original_content_length,
-    base::TimeTicks creation_time,
-    int net_error,
-    std::unique_ptr<net::LoadTimingInfo> load_timing_info) {
-  DCHECK(!base::FeatureList::IsEnabled(network::features::kNetworkService));
-
-  // Ignore non-HTTP(S) resources (blobs, data uris, etc).
-  if (!url.SchemeIsHTTPOrHTTPS())
-    return;
-
-  PageLoadTracker* tracker = GetTrackerOrNullForRequest(
-      request_id, render_frame_host_or_null, resource_type, creation_time);
-  if (tracker) {
-    ExtraRequestCompleteInfo extra_request_complete_info(
-        url, remote_endpoint, frame_tree_node_id, was_cached, raw_body_bytes,
-        was_cached ? 0 : original_content_length,
-        std::move(data_reduction_proxy_data), resource_type, net_error,
-        std::move(load_timing_info));
-    tracker->OnLoadedResource(extra_request_complete_info);
-  }
-}
-
 const PageLoadExtraInfo
 MetricsWebContentsObserver::GetPageLoadExtraInfoForCommittedLoad() {
   DCHECK(committed_load_);
diff --git a/chrome/browser/page_load_metrics/metrics_web_contents_observer.h b/chrome/browser/page_load_metrics/metrics_web_contents_observer.h
index 6454be50..9c732092 100644
--- a/chrome/browser/page_load_metrics/metrics_web_contents_observer.h
+++ b/chrome/browser/page_load_metrics/metrics_web_contents_observer.h
@@ -24,10 +24,6 @@
 #include "content/public/common/resource_type.h"
 #include "third_party/blink/public/platform/web_input_event.h"
 
-namespace net {
-class IPEndPoint;
-}  // namespace net
-
 namespace content {
 class NavigationHandle;
 class RenderFrameHost;
@@ -126,25 +122,6 @@
   void WillProcessNavigationResponse(
       content::NavigationHandle* navigation_handle);
 
-  // A resource request completed on the IO thread. This method is invoked on
-  // the UI thread. |render_frame_host_or_null will| be null for main or sub
-  // frame requests when browser-side navigation is enabled.
-  void OnRequestComplete(
-      const GURL& url,
-      const net::IPEndPoint& remote_endpoint,
-      int frame_tree_node_id,
-      const content::GlobalRequestID& request_id,
-      content::RenderFrameHost* render_frame_host_or_null,
-      content::ResourceType resource_type,
-      bool was_cached,
-      std::unique_ptr<data_reduction_proxy::DataReductionProxyData>
-          data_reduction_proxy_data,
-      int64_t raw_body_bytes,
-      int64_t original_content_length,
-      base::TimeTicks creation_time,
-      int net_error,
-      std::unique_ptr<net::LoadTimingInfo> load_timing_info);
-
   // Flush any buffered metrics, as part of the metrics subsystem persisting
   // metrics as the application goes into the background. The application may be
   // killed at any time after this method is invoked without further
diff --git a/chrome/browser/page_load_metrics/metrics_web_contents_observer_unittest.cc b/chrome/browser/page_load_metrics/metrics_web_contents_observer_unittest.cc
index 34002abb..843669f 100644
--- a/chrome/browser/page_load_metrics/metrics_web_contents_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/metrics_web_contents_observer_unittest.cc
@@ -22,6 +22,7 @@
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_frame_host.h"
+#include "content/public/common/resource_load_info.mojom.h"
 #include "content/public/test/navigation_simulator.h"
 #include "content/public/test/test_renderer_host.h"
 #include "content/public/test/web_contents_tester.h"
@@ -244,6 +245,22 @@
   timing->document_timing->first_layout = base::TimeDelta::FromMilliseconds(30);
 }
 
+content::mojom::ResourceLoadInfoPtr CreateResourceLoadInfo(
+    const GURL& url,
+    content::ResourceType resource_type) {
+  content::mojom::ResourceLoadInfoPtr resource_load_info =
+      content::mojom::ResourceLoadInfo::New();
+  resource_load_info->url = url;
+  resource_load_info->resource_type = resource_type;
+  resource_load_info->was_cached = false;
+  resource_load_info->raw_body_bytes = 0;
+  resource_load_info->net_error = net::OK;
+  resource_load_info->network_info = content::mojom::CommonNetworkInfo::New();
+  resource_load_info->network_info->remote_endpoint = net::IPEndPoint();
+  resource_load_info->load_timing_info.request_start = base::TimeTicks::Now();
+  return resource_load_info;
+}
+
 }  //  namespace
 
 class MetricsWebContentsObserverTest : public ChromeRenderViewHostTestHarness {
@@ -1530,16 +1547,14 @@
       content::NavigationSimulator::CreateRendererInitiated(
           main_resource_url, web_contents()->GetMainFrame());
   navigation_simulator->Start();
-  int frame_tree_node_id =
-      navigation_simulator->GetNavigationHandle()->GetFrameTreeNodeId();
   navigation_simulator->Commit();
 
   const auto request_id = navigation_simulator->GetGlobalRequestID();
 
-  observer()->OnRequestComplete(
-      main_resource_url, net::IPEndPoint(), frame_tree_node_id, request_id,
-      web_contents()->GetMainFrame(), content::ResourceType::kMainFrame, false,
-      nullptr, 0, 0, base::TimeTicks::Now(), net::OK, nullptr);
+  observer()->ResourceLoadComplete(
+      web_contents()->GetMainFrame(), request_id,
+      *CreateResourceLoadInfo(main_resource_url,
+                              content::ResourceType::kMainFrame));
   EXPECT_EQ(1u, loaded_resources().size());
   EXPECT_EQ(main_resource_url, loaded_resources().back().url);
 
@@ -1547,10 +1562,10 @@
 
   // Deliver a second main frame resource. This one should be ignored, since the
   // specified |request_id| is no longer associated with any tracked page loads.
-  observer()->OnRequestComplete(
-      main_resource_url, net::IPEndPoint(), frame_tree_node_id, request_id,
-      web_contents()->GetMainFrame(), content::ResourceType::kMainFrame, false,
-      nullptr, 0, 0, base::TimeTicks::Now(), net::OK, nullptr);
+  observer()->ResourceLoadComplete(
+      web_contents()->GetMainFrame(), request_id,
+      *CreateResourceLoadInfo(main_resource_url,
+                              content::ResourceType::kMainFrame));
   EXPECT_EQ(1u, loaded_resources().size());
   EXPECT_EQ(main_resource_url, loaded_resources().back().url);
 }
@@ -1560,12 +1575,10 @@
       content::WebContentsTester::For(web_contents());
   web_contents_tester->NavigateAndCommit(GURL(kDefaultTestUrl));
   GURL loaded_resource_url("http://www.other.com/");
-  observer()->OnRequestComplete(
-      loaded_resource_url, net::IPEndPoint(),
-      web_contents()->GetMainFrame()->GetFrameTreeNodeId(),
-      content::GlobalRequestID(), web_contents()->GetMainFrame(),
-      content::ResourceType::kScript, false, nullptr, 0, 0,
-      base::TimeTicks::Now(), net::OK, nullptr);
+  observer()->ResourceLoadComplete(
+      web_contents()->GetMainFrame(), content::GlobalRequestID(),
+      *CreateResourceLoadInfo(loaded_resource_url,
+                              content::ResourceType::kScript));
 
   EXPECT_EQ(1u, loaded_resources().size());
   EXPECT_EQ(loaded_resource_url, loaded_resources().back().url);
@@ -1586,12 +1599,10 @@
   std::unique_ptr<content::WebContents> other_web_contents(
       content::WebContentsTester::CreateTestWebContents(browser_context(),
                                                         nullptr));
-  observer()->OnRequestComplete(
-      GURL("http://www.other.com/"), net::IPEndPoint(),
-      other_web_contents->GetMainFrame()->GetFrameTreeNodeId(),
-      content::GlobalRequestID(), other_web_contents->GetMainFrame(),
-      content::ResourceType::kScript, false, nullptr, 0, 0,
-      base::TimeTicks::Now(), net::OK, nullptr);
+  observer()->ResourceLoadComplete(
+      other_web_contents->GetMainFrame(), content::GlobalRequestID(),
+      *CreateResourceLoadInfo(GURL("http://www.other.com/"),
+                              content::ResourceType::kScript));
 
   EXPECT_TRUE(loaded_resources().empty());
 }
@@ -1602,12 +1613,10 @@
       content::WebContentsTester::For(web_contents());
   web_contents_tester->NavigateAndCommit(GURL(kDefaultTestUrl));
   GURL loaded_resource_url("data:text/html,Hello world");
-  observer()->OnRequestComplete(
-      loaded_resource_url, net::IPEndPoint(),
-      web_contents()->GetMainFrame()->GetFrameTreeNodeId(),
-      content::GlobalRequestID(), web_contents()->GetMainFrame(),
-      content::ResourceType::kScript, false, nullptr, 0, 0,
-      base::TimeTicks::Now(), net::OK, nullptr);
+  observer()->ResourceLoadComplete(
+      web_contents()->GetMainFrame(), content::GlobalRequestID(),
+      *CreateResourceLoadInfo(loaded_resource_url,
+                              content::ResourceType::kScript));
 
   EXPECT_TRUE(loaded_resources().empty());
 }
diff --git a/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_base_unittest.cc b/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_base_unittest.cc
index d0edf45..d0a5695 100644
--- a/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_base_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_base_unittest.cc
@@ -165,7 +165,6 @@
   timing()->document_timing->load_event_start = base::nullopt;
   RunTestAndNavigateToUntrackedUrl(true, false, false);
   ValidateTimes();
-  ValidateLoFiInPingback(false);
 
   ResetTest();
   // Verify that when an opt out occurs, that it is reported in the pingback.
@@ -174,34 +173,6 @@
   observer()->BroadcastEventToObservers(PreviewsUITabHelper::OptOutEventKey());
   NavigateToUntrackedUrl();
   ValidateTimes();
-  ValidateLoFiInPingback(false);
-
-  ResetTest();
-  std::unique_ptr<DataReductionProxyData> data =
-      std::make_unique<DataReductionProxyData>();
-  data->set_used_data_reduction_proxy(true);
-  data->set_request_url(GURL(kDefaultTestUrl));
-  data->set_lofi_received(true);
-
-  // Verify LoFi is tracked when a LoFi response is received.
-  page_load_metrics::ExtraRequestCompleteInfo resource = {
-      GURL(kResourceUrl),
-      net::IPEndPoint(),
-      -1 /* frame_tree_node_id */,
-      true /*was_cached*/,
-      1024 * 40 /* raw_body_bytes */,
-      0 /* original_network_content_length */,
-      std::move(data),
-      content::ResourceType::kScript,
-      0,
-      {} /* load_timing()info */};
-
-  RunTest(true, false, false, false);
-  SimulateLoadedResource(resource);
-  NavigateToUntrackedUrl();
-  ValidateTimes();
-  ValidateLoFiInPingback(true);
-  ValidateBlackListInPingback(false);
 
   ResetTest();
   RunTest(true, false, false, true);
diff --git a/chrome/browser/page_load_metrics/observers/lofi_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/lofi_page_load_metrics_observer.cc
deleted file mode 100644
index 564595d..0000000
--- a/chrome/browser/page_load_metrics/observers/lofi_page_load_metrics_observer.cc
+++ /dev/null
@@ -1,145 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/page_load_metrics/observers/lofi_page_load_metrics_observer.h"
-
-#include "base/optional.h"
-#include "base/time/time.h"
-#include "chrome/browser/page_load_metrics/page_load_metrics_observer.h"
-#include "chrome/browser/page_load_metrics/page_load_metrics_util.h"
-#include "chrome/common/page_load_metrics/page_load_timing.h"
-#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_data.h"
-
-namespace data_reduction_proxy {
-
-namespace lofi_names {
-
-const char kNavigationToLoadEvent[] =
-    "PageLoad.Clients.LoFi.DocumentTiming.NavigationToLoadEventFired";
-const char kNavigationToFirstContentfulPaint[] =
-    "PageLoad.Clients.LoFi.PaintTiming.NavigationToFirstContentfulPaint";
-const char kNavigationToFirstMeaningfulPaint[] =
-    "PageLoad.Clients.LoFi.Experimental.PaintTiming."
-    "NavigationToFirstMeaningfulPaint";
-const char kNavigationToFirstImagePaint[] =
-    "PageLoad.Clients.LoFi.PaintTiming.NavigationToFirstImagePaint";
-const char kParseBlockedOnScriptLoad[] =
-    "PageLoad.Clients.LoFi.ParseTiming.ParseBlockedOnScriptLoad";
-const char kParseDuration[] = "PageLoad.Clients.LoFi.ParseTiming.ParseDuration";
-
-const char kNumNetworkResources[] =
-    "PageLoad.Clients.LoFi.Experimental.CompletedResources.Network";
-const char kNumNetworkLoFiResources[] =
-    "PageLoad.Clients.LoFi.Experimental.CompletedResources.Network.LoFi";
-const char kNetworkBytes[] = "PageLoad.Clients.LoFi.Experimental.Bytes.Network";
-const char kLoFiNetworkBytes[] =
-    "PageLoad.Clients.LoFi.Experimental.Bytes.Network.LoFi";
-
-}  // namespace lofi_names
-
-LoFiPageLoadMetricsObserver::LoFiPageLoadMetricsObserver()
-    : num_network_resources_(0),
-      num_network_lofi_resources_(0),
-      original_network_bytes_(0),
-      network_bytes_(0),
-      lofi_network_bytes_(0) {}
-
-LoFiPageLoadMetricsObserver::~LoFiPageLoadMetricsObserver() {}
-
-page_load_metrics::PageLoadMetricsObserver::ObservePolicy
-LoFiPageLoadMetricsObserver::OnStart(
-    content::NavigationHandle* navigation_handle,
-    const GURL& currently_committed_url,
-    bool started_in_foreground) {
-  if (!started_in_foreground)
-    return STOP_OBSERVING;
-  return CONTINUE_OBSERVING;
-}
-
-page_load_metrics::PageLoadMetricsObserver::ObservePolicy
-LoFiPageLoadMetricsObserver::FlushMetricsOnAppEnterBackground(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
-  // FlushMetricsOnAppEnterBackground is invoked on Android in cases where the
-  // app is about to be backgrounded, as part of the Activity.onPause()
-  // flow. After this method is invoked, Chrome may be killed without further
-  // notification.
-  if (num_network_lofi_resources_ > 0 && info.did_commit) {
-    RecordPageSizeUMA();
-    RecordTimingMetrics(timing, info);
-  }
-  return STOP_OBSERVING;
-}
-
-void LoFiPageLoadMetricsObserver::OnComplete(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
-  if (num_network_lofi_resources_ == 0)
-    return;
-  RecordPageSizeUMA();
-  RecordTimingMetrics(timing, info);
-}
-
-void LoFiPageLoadMetricsObserver::RecordPageSizeUMA() const {
-  PAGE_RESOURCE_COUNT_HISTOGRAM(lofi_names::kNumNetworkResources,
-                                num_network_resources_);
-  PAGE_RESOURCE_COUNT_HISTOGRAM(lofi_names::kNumNetworkLoFiResources,
-                                num_network_lofi_resources_);
-  PAGE_BYTES_HISTOGRAM(lofi_names::kNetworkBytes, network_bytes_);
-  PAGE_BYTES_HISTOGRAM(lofi_names::kLoFiNetworkBytes, lofi_network_bytes_);
-}
-
-void LoFiPageLoadMetricsObserver::RecordTimingMetrics(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
-  if (WasStartedInForegroundOptionalEventInForeground(
-          timing.document_timing->load_event_start, info)) {
-    PAGE_LOAD_HISTOGRAM(lofi_names::kNavigationToLoadEvent,
-                        timing.document_timing->load_event_start.value());
-  }
-  if (WasStartedInForegroundOptionalEventInForeground(
-          timing.paint_timing->first_contentful_paint, info)) {
-    PAGE_LOAD_HISTOGRAM(lofi_names::kNavigationToFirstContentfulPaint,
-                        timing.paint_timing->first_contentful_paint.value());
-  }
-  if (WasStartedInForegroundOptionalEventInForeground(
-          timing.paint_timing->first_meaningful_paint, info)) {
-    PAGE_LOAD_HISTOGRAM(lofi_names::kNavigationToFirstMeaningfulPaint,
-                        timing.paint_timing->first_meaningful_paint.value());
-  }
-  if (WasStartedInForegroundOptionalEventInForeground(
-          timing.paint_timing->first_image_paint, info)) {
-    PAGE_LOAD_HISTOGRAM(lofi_names::kNavigationToFirstImagePaint,
-                        timing.paint_timing->first_image_paint.value());
-  }
-  if (WasStartedInForegroundOptionalEventInForeground(
-          timing.parse_timing->parse_stop, info)) {
-    PAGE_LOAD_HISTOGRAM(
-        lofi_names::kParseBlockedOnScriptLoad,
-        timing.parse_timing->parse_blocked_on_script_load_duration.value());
-    PAGE_LOAD_HISTOGRAM(lofi_names::kParseDuration,
-                        timing.parse_timing->parse_stop.value() -
-                            timing.parse_timing->parse_start.value());
-  }
-}
-
-void LoFiPageLoadMetricsObserver::OnLoadedResource(
-    const page_load_metrics::ExtraRequestCompleteInfo&
-        extra_request_complete_info) {
-  if (extra_request_complete_info.was_cached)
-    return;
-  ++num_network_resources_;
-  network_bytes_ += extra_request_complete_info.raw_body_bytes;
-  original_network_bytes_ +=
-      extra_request_complete_info.original_network_content_length;
-  if (extra_request_complete_info.data_reduction_proxy_data &&
-      (extra_request_complete_info.data_reduction_proxy_data->lofi_received() ||
-       extra_request_complete_info.data_reduction_proxy_data
-           ->client_lofi_requested())) {
-    ++num_network_lofi_resources_;
-    lofi_network_bytes_ += extra_request_complete_info.raw_body_bytes;
-  }
-}
-
-}  // namespace data_reduction_proxy
diff --git a/chrome/browser/page_load_metrics/observers/lofi_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/lofi_page_load_metrics_observer.h
deleted file mode 100644
index ecab44a..0000000
--- a/chrome/browser/page_load_metrics/observers/lofi_page_load_metrics_observer.h
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_LOFI_PAGE_LOAD_METRICS_OBSERVER_H_
-#define CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_LOFI_PAGE_LOAD_METRICS_OBSERVER_H_
-
-#include <stdint.h>
-
-#include "base/macros.h"
-#include "chrome/browser/page_load_metrics/page_load_metrics_observer.h"
-
-namespace content {
-class NavigationHandle;
-}
-
-namespace data_reduction_proxy {
-
-namespace lofi_names {
-
-extern const char kNavigationToLoadEvent[];
-extern const char kNavigationToFirstContentfulPaint[];
-extern const char kNavigationToFirstMeaningfulPaint[];
-extern const char kNavigationToFirstImagePaint[];
-extern const char kParseBlockedOnScriptLoad[];
-extern const char kParseDuration[];
-
-extern const char kNumNetworkResources[];
-extern const char kNumNetworkLoFiResources[];
-extern const char kNetworkBytes[];
-extern const char kLoFiNetworkBytes[];
-
-}  // namespace lofi_names
-
-// Observer responsible for recording page load metrics when a LoFi image is
-// loaded in the page. LoFi can happen via the data saver proxy or via a client
-// optimization that requests a range header.
-class LoFiPageLoadMetricsObserver
-    : public page_load_metrics::PageLoadMetricsObserver {
- public:
-  LoFiPageLoadMetricsObserver();
-  ~LoFiPageLoadMetricsObserver() override;
-
-  // page_load_metrics::PageLoadMetricsObserver:
-  ObservePolicy OnStart(content::NavigationHandle* navigation_handle,
-                        const GURL& currently_committed_url,
-                        bool started_in_foreground) override;
-  ObservePolicy FlushMetricsOnAppEnterBackground(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info) override;
-  void OnComplete(const page_load_metrics::mojom::PageLoadTiming& timing,
-                  const page_load_metrics::PageLoadExtraInfo& info) override;
-  void OnLoadedResource(const page_load_metrics::ExtraRequestCompleteInfo&
-                            extra_request_complete_info) override;
-
- private:
-  void RecordTimingMetrics(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& info);
-
-  // Records UMA of page size when the observer is about to be deleted.
-  void RecordPageSizeUMA() const;
-
-  int64_t num_network_resources_;
-  int64_t num_network_lofi_resources_;
-  int64_t original_network_bytes_;
-  int64_t network_bytes_;
-  int64_t lofi_network_bytes_;
-
-  DISALLOW_COPY_AND_ASSIGN(LoFiPageLoadMetricsObserver);
-};
-
-}  // namespace data_reduction_proxy
-
-#endif  // CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_LOFI_PAGE_LOAD_METRICS_OBSERVER_H_
diff --git a/chrome/browser/page_load_metrics/observers/lofi_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/lofi_page_load_metrics_observer_unittest.cc
deleted file mode 100644
index 776fb962..0000000
--- a/chrome/browser/page_load_metrics/observers/lofi_page_load_metrics_observer_unittest.cc
+++ /dev/null
@@ -1,377 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/page_load_metrics/observers/lofi_page_load_metrics_observer.h"
-
-#include <stdint.h>
-
-#include <memory>
-
-#include "base/macros.h"
-#include "base/optional.h"
-#include "base/time/time.h"
-#include "chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.h"
-#include "chrome/browser/page_load_metrics/page_load_metrics_observer.h"
-#include "chrome/browser/page_load_metrics/page_load_tracker.h"
-#include "chrome/common/page_load_metrics/page_load_timing.h"
-#include "chrome/common/page_load_metrics/test/page_load_metrics_test_util.h"
-#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_data.h"
-#include "net/base/ip_endpoint.h"
-
-namespace data_reduction_proxy {
-
-namespace {
-
-const char kDefaultTestUrl[] = "https://www.google.com";
-
-class LoFiPageLoadMetricsObserverTest
-    : public page_load_metrics::PageLoadMetricsObserverTestHarness {
- public:
-  LoFiPageLoadMetricsObserverTest() {}
-  ~LoFiPageLoadMetricsObserverTest() override {}
-
-  void ResetTest() {
-    page_load_metrics::InitPageLoadTimingForTest(&timing_);
-    // Reset to the default testing state. Does not reset histogram state.
-    timing_.navigation_start = base::Time::FromDoubleT(1);
-    timing_.response_start = base::TimeDelta::FromSeconds(2);
-    timing_.parse_timing->parse_start = base::TimeDelta::FromSeconds(3);
-    timing_.paint_timing->first_contentful_paint =
-        base::TimeDelta::FromSeconds(4);
-    timing_.paint_timing->first_paint = base::TimeDelta::FromSeconds(4);
-    timing_.paint_timing->first_meaningful_paint =
-        base::TimeDelta::FromSeconds(8);
-    timing_.paint_timing->first_image_paint = base::TimeDelta::FromSeconds(5);
-    timing_.document_timing->load_event_start = base::TimeDelta::FromSeconds(7);
-    timing_.parse_timing->parse_stop = base::TimeDelta::FromSeconds(4);
-    timing_.parse_timing->parse_blocked_on_script_load_duration =
-        base::TimeDelta::FromSeconds(1);
-    PopulateRequiredTimingFields(&timing_);
-  }
-
-  void RunTest() {
-    NavigateAndCommit(GURL(kDefaultTestUrl));
-    SimulateTimingUpdate(timing_);
-  }
-
-  void ValidateTimingHistograms(bool lofi_request_sent) {
-    ValidateTimingHistogram(lofi_names::kNavigationToLoadEvent,
-                            timing_.document_timing->load_event_start,
-                            lofi_request_sent);
-    ValidateTimingHistogram(lofi_names::kNavigationToFirstContentfulPaint,
-                            timing_.paint_timing->first_contentful_paint,
-                            lofi_request_sent);
-    ValidateTimingHistogram(lofi_names::kNavigationToFirstMeaningfulPaint,
-                            timing_.paint_timing->first_meaningful_paint,
-                            lofi_request_sent);
-    ValidateTimingHistogram(lofi_names::kNavigationToFirstImagePaint,
-                            timing_.paint_timing->first_image_paint,
-                            lofi_request_sent);
-    ValidateTimingHistogram(
-        lofi_names::kParseBlockedOnScriptLoad,
-        timing_.parse_timing->parse_blocked_on_script_load_duration,
-        lofi_request_sent);
-    ValidateTimingHistogram(lofi_names::kParseDuration,
-                            timing_.parse_timing->parse_stop.value() -
-                                timing_.parse_timing->parse_start.value(),
-                            lofi_request_sent);
-  }
-
-  void ValidateTimingHistogram(const std::string& histogram,
-                               const base::Optional<base::TimeDelta>& event,
-                               bool lofi_request_sent) {
-    histogram_tester().ExpectTotalCount(histogram, lofi_request_sent ? 1 : 0);
-    if (!lofi_request_sent)
-      return;
-    histogram_tester().ExpectUniqueSample(
-        histogram,
-        static_cast<base::HistogramBase::Sample>(
-            event.value().InMilliseconds()),
-        1);
-  }
-
-  void ValidateDataHistograms(int network_resources,
-                              int lofi_resources,
-                              int64_t network_bytes,
-                              int64_t lofi_bytes) {
-    if (lofi_resources > 0) {
-      histogram_tester().ExpectUniqueSample(lofi_names::kNumNetworkResources,
-                                            network_resources, 1);
-      histogram_tester().ExpectUniqueSample(
-          lofi_names::kNumNetworkLoFiResources, lofi_resources, 1);
-      histogram_tester().ExpectUniqueSample(
-          lofi_names::kNetworkBytes, static_cast<int>(network_bytes / 1024), 1);
-      histogram_tester().ExpectUniqueSample(lofi_names::kLoFiNetworkBytes,
-                                            static_cast<int>(lofi_bytes / 1024),
-                                            1);
-    } else {
-      histogram_tester().ExpectTotalCount(lofi_names::kNumNetworkResources, 0);
-      histogram_tester().ExpectTotalCount(lofi_names::kNumNetworkLoFiResources,
-                                          0);
-      histogram_tester().ExpectTotalCount(lofi_names::kNetworkBytes, 0);
-      histogram_tester().ExpectTotalCount(lofi_names::kLoFiNetworkBytes, 0);
-    }
-  }
-
- protected:
-  void RegisterObservers(page_load_metrics::PageLoadTracker* tracker) override {
-    tracker->AddObserver(std::make_unique<LoFiPageLoadMetricsObserver>());
-  }
-
-  page_load_metrics::mojom::PageLoadTiming timing_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(LoFiPageLoadMetricsObserverTest);
-};
-
-TEST_F(LoFiPageLoadMetricsObserverTest, LoFiNotSeen) {
-  ResetTest();
-  RunTest();
-
-  std::unique_ptr<DataReductionProxyData> data =
-      std::make_unique<DataReductionProxyData>();
-  data->set_used_data_reduction_proxy(true);
-
-  // Prepare 4 resources of varying size and configurations, none of which have
-  // LoFi set.
-  page_load_metrics::ExtraRequestCompleteInfo resources[] = {
-      // Cached request.
-      {GURL(kResourceUrl), net::IPEndPoint(), -1, true /*was_cached*/,
-       1024 * 40 /* raw_body_bytes */, 0 /* original_network_content_length */,
-       nullptr /* data_reduction_proxy_data */, content::ResourceType::kScript,
-       0, nullptr /* load_timing_info */},
-      // Uncached non-proxied request.
-      {GURL(kResourceUrl), net::IPEndPoint(), -1, false /*was_cached*/,
-       1024 * 40 /* raw_body_bytes */,
-       1024 * 40 /* original_network_content_length */,
-       nullptr /* data_reduction_proxy_data */, content::ResourceType::kImage,
-       0, nullptr /* load_timing_info */},
-      // Uncached proxied request with .1 compression ratio.
-      {GURL(kResourceUrl), net::IPEndPoint(), -1, false /*was_cached*/,
-       1024 * 40 /* raw_body_bytes */,
-       1024 * 40 * 10 /* original_network_content_length */, data->DeepCopy(),
-       content::ResourceType::kImage, 0, nullptr /* load_timing_info */},
-      // Uncached proxied request with .5 compression ratio.
-      {GURL(kResourceUrl), net::IPEndPoint(), -1, false /*was_cached*/,
-       1024 * 40 /* raw_body_bytes */,
-       1024 * 40 * 5 /* original_network_content_length */, std::move(data),
-       content::ResourceType::kImage, 0, nullptr /* load_timing_info */},
-  };
-
-  int network_resources = 0;
-  int lofi_resources = 0;
-  int64_t network_bytes = 0;
-  int64_t lofi_bytes = 0;
-
-  for (const auto& request : resources) {
-    SimulateLoadedResource(request);
-    if (!request.was_cached) {
-      network_bytes += request.raw_body_bytes;
-      ++network_resources;
-    }
-    if (request.data_reduction_proxy_data &&
-        (request.data_reduction_proxy_data->lofi_received() ||
-         request.data_reduction_proxy_data->client_lofi_requested())) {
-      lofi_bytes += request.raw_body_bytes;
-      ++lofi_resources;
-    }
-  }
-
-  NavigateToUntrackedUrl();
-
-  ValidateTimingHistograms(false);
-  ValidateDataHistograms(network_resources, lofi_resources, network_bytes,
-                         lofi_bytes);
-}
-
-TEST_F(LoFiPageLoadMetricsObserverTest, ClientLoFiSeen) {
-  ResetTest();
-  RunTest();
-
-  std::unique_ptr<DataReductionProxyData> data =
-      std::make_unique<DataReductionProxyData>();
-  data->set_client_lofi_requested(true);
-
-  // Prepare 4 resources of varying size and configurations, 2 of which have
-  // client LoFi set.
-  page_load_metrics::ExtraRequestCompleteInfo resources[] = {
-      // Cached request.
-      {GURL(kResourceUrl), net::IPEndPoint(), -1, true /*was_cached*/,
-       1024 * 40 /* raw_body_bytes */, 0 /* original_network_content_length */,
-       nullptr /* data_reduction_proxy_data */, content::ResourceType::kScript,
-       0, nullptr /* load_timing_info */},
-      // Uncached non-proxied request.
-      {GURL(kResourceUrl), net::IPEndPoint(), -1, false /*was_cached*/,
-       1024 * 40 /* raw_body_bytes */,
-       1024 * 40 /* original_network_content_length */,
-       nullptr /* data_reduction_proxy_data */, content::ResourceType::kImage,
-       0, nullptr /* load_timing_info */},
-      // Uncached proxied request with .1 compression ratio.
-      {GURL(kResourceUrl), net::IPEndPoint(), -1, false /*was_cached*/,
-       1024 * 40 /* raw_body_bytes */,
-       1024 * 40 * 10 /* original_network_content_length */, data->DeepCopy(),
-       content::ResourceType::kImage, 0, nullptr /* load_timing_info */},
-      // Uncached proxied request with .5 compression ratio.
-      {GURL(kResourceUrl), net::IPEndPoint(), -1, false /*was_cached*/,
-       1024 * 40 /* raw_body_bytes */,
-       1024 * 40 * 5 /* original_network_content_length */, std::move(data),
-       content::ResourceType::kImage, 0, nullptr /* load_timing_info */},
-  };
-
-  int network_resources = 0;
-  int lofi_resources = 0;
-  int64_t network_bytes = 0;
-  int64_t lofi_bytes = 0;
-
-  for (const auto& request : resources) {
-    SimulateLoadedResource(request);
-    if (!request.was_cached) {
-      network_bytes += request.raw_body_bytes;
-      ++network_resources;
-    }
-    if (request.data_reduction_proxy_data &&
-        (request.data_reduction_proxy_data->lofi_received() ||
-         request.data_reduction_proxy_data->client_lofi_requested())) {
-      lofi_bytes += request.raw_body_bytes;
-      ++lofi_resources;
-    }
-  }
-
-  NavigateToUntrackedUrl();
-
-  ValidateTimingHistograms(true);
-  ValidateDataHistograms(network_resources, lofi_resources, network_bytes,
-                         lofi_bytes);
-}
-
-TEST_F(LoFiPageLoadMetricsObserverTest, ServerLoFiSeen) {
-  ResetTest();
-  RunTest();
-
-  std::unique_ptr<DataReductionProxyData> data =
-      std::make_unique<DataReductionProxyData>();
-  data->set_used_data_reduction_proxy(true);
-  data->set_lofi_received(true);
-
-  // Prepare 4 resources of varying size and configurations, 2 of which have
-  // server LoFi set.
-  page_load_metrics::ExtraRequestCompleteInfo resources[] = {
-      // Cached request.
-      {GURL(kResourceUrl), net::IPEndPoint(), -1, true /*was_cached*/,
-       1024 * 40 /* raw_body_bytes */, 0 /* original_network_content_length */,
-       nullptr /* data_reduction_proxy_data */, content::ResourceType::kScript,
-       0, nullptr /* load_timing_info */},
-      // Uncached non-proxied request.
-      {GURL(kResourceUrl), net::IPEndPoint(), -1, false /*was_cached*/,
-       1024 * 40 /* raw_body_bytes */,
-       1024 * 40 /* original_network_content_length */,
-       nullptr /* data_reduction_proxy_data */, content::ResourceType::kImage,
-       0, nullptr /* load_timing_info */},
-      // Uncached proxied request with .1 compression ratio.
-      {GURL(kResourceUrl), net::IPEndPoint(), -1, false /*was_cached*/,
-       1024 * 40 /* raw_body_bytes */,
-       1024 * 40 * 10 /* original_network_content_length */, data->DeepCopy(),
-       content::ResourceType::kImage, 0, nullptr /* load_timing_info */},
-      // Uncached proxied request with .5 compression ratio.
-      {GURL(kResourceUrl), net::IPEndPoint(), -1, false /*was_cached*/,
-       1024 * 40 /* raw_body_bytes */,
-       1024 * 40 * 5 /* original_network_content_length */, std::move(data),
-       content::ResourceType::kImage, 0, nullptr /* load_timing_info */},
-  };
-
-  int network_resources = 0;
-  int lofi_resources = 0;
-  int64_t network_bytes = 0;
-  int64_t lofi_bytes = 0;
-
-  for (const auto& request : resources) {
-    SimulateLoadedResource(request);
-    if (!request.was_cached) {
-      network_bytes += request.raw_body_bytes;
-      ++network_resources;
-    }
-    if (request.data_reduction_proxy_data &&
-        (request.data_reduction_proxy_data->lofi_received() ||
-         request.data_reduction_proxy_data->client_lofi_requested())) {
-      lofi_bytes += request.raw_body_bytes;
-      ++lofi_resources;
-    }
-  }
-
-  NavigateToUntrackedUrl();
-
-  ValidateTimingHistograms(true);
-  ValidateDataHistograms(network_resources, lofi_resources, network_bytes,
-                         lofi_bytes);
-}
-
-TEST_F(LoFiPageLoadMetricsObserverTest, BothLoFiSeen) {
-  ResetTest();
-  RunTest();
-
-  std::unique_ptr<DataReductionProxyData> data1 =
-      std::make_unique<DataReductionProxyData>();
-  data1->set_used_data_reduction_proxy(true);
-  data1->set_lofi_received(true);
-
-  std::unique_ptr<DataReductionProxyData> data2 =
-      std::make_unique<DataReductionProxyData>();
-  data2->set_used_data_reduction_proxy(true);
-  data2->set_client_lofi_requested(true);
-
-  // Prepare 4 resources of varying size and configurations, 1 has Client LoFi,
-  // 1 has Server LoFi.
-  page_load_metrics::ExtraRequestCompleteInfo resources[] = {
-      // Cached request.
-      {GURL(kResourceUrl), net::IPEndPoint(), -1, true /*was_cached*/,
-       1024 * 40 /* raw_body_bytes */, 0 /* original_network_content_length */,
-       nullptr /* data_reduction_proxy_data */, content::ResourceType::kScript,
-       0, nullptr /* load_timing_info */},
-      // Uncached non-proxied request.
-      {GURL(kResourceUrl), net::IPEndPoint(), -1, false /*was_cached*/,
-       1024 * 40 /* raw_body_bytes */,
-       1024 * 40 /* original_network_content_length */,
-       nullptr /* data_reduction_proxy_data */, content::ResourceType::kImage,
-       0, nullptr /* load_timing_info */},
-      // Uncached proxied request with .1 compression ratio.
-      {GURL(kResourceUrl), net::IPEndPoint(), -1, false /*was_cached*/,
-       1024 * 40 /* raw_body_bytes */,
-       1024 * 40 * 10 /* original_network_content_length */, std::move(data1),
-       content::ResourceType::kImage, 0, nullptr /* load_timing_info */},
-      // Uncached proxied request with .5 compression ratio.
-      {GURL(kResourceUrl), net::IPEndPoint(), -1, false /*was_cached*/,
-       1024 * 40 /* raw_body_bytes */,
-       1024 * 40 * 5 /* original_network_content_length */, std::move(data2),
-       content::ResourceType::kImage, 0, nullptr /* load_timing_info */},
-  };
-
-  int network_resources = 0;
-  int lofi_resources = 0;
-  int64_t network_bytes = 0;
-  int64_t lofi_bytes = 0;
-
-  for (const auto& request : resources) {
-    SimulateLoadedResource(request);
-    if (!request.was_cached) {
-      network_bytes += request.raw_body_bytes;
-      ++network_resources;
-    }
-    if (request.data_reduction_proxy_data &&
-        (request.data_reduction_proxy_data->lofi_received() ||
-         request.data_reduction_proxy_data->client_lofi_requested())) {
-      lofi_bytes += request.raw_body_bytes;
-      ++lofi_resources;
-    }
-  }
-
-  NavigateToUntrackedUrl();
-
-  ValidateTimingHistograms(true);
-  ValidateDataHistograms(network_resources, lofi_resources, network_bytes,
-                         lofi_bytes);
-}
-
-}  // namespace
-
-}  //  namespace data_reduction_proxy
diff --git a/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_tester.cc b/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_tester.cc
index 39c467d..284aa08 100644
--- a/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_tester.cc
+++ b/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_tester.cc
@@ -17,6 +17,7 @@
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
+#include "content/public/common/resource_load_info.mojom.h"
 #include "content/public/common/resource_type.h"
 #include "net/base/ip_endpoint.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -192,25 +193,23 @@
         << "Main frame resources must have a GlobalRequestID.";
   }
 
-  // For consistency with browser-side navigation, we provide a null RFH for
-  // main frame and sub frame resources.
-  content::RenderFrameHost* render_frame_host_or_null =
-      (info.resource_type == content::ResourceType::kMainFrame ||
-       info.resource_type == content::ResourceType::kSubFrame)
-          ? nullptr
-          : web_contents()->GetMainFrame();
+  content::mojom::ResourceLoadInfo resource_load_info;
+  resource_load_info.url = info.url;
+  resource_load_info.was_cached = info.was_cached;
+  resource_load_info.raw_body_bytes = info.raw_body_bytes;
+  resource_load_info.total_received_bytes =
+      info.original_network_content_length;
+  resource_load_info.resource_type = info.resource_type;
+  resource_load_info.net_error = info.net_error;
+  resource_load_info.network_info = content::mojom::CommonNetworkInfo::New();
+  resource_load_info.network_info->remote_endpoint = info.remote_endpoint;
+  if (info.load_timing_info)
+    resource_load_info.load_timing_info = *info.load_timing_info;
+  else
+    resource_load_info.load_timing_info.request_start = base::TimeTicks::Now();
 
-  observer_->OnRequestComplete(
-      info.url, info.remote_endpoint, info.frame_tree_node_id, request_id,
-      render_frame_host_or_null, info.resource_type, info.was_cached,
-      info.data_reduction_proxy_data
-          ? info.data_reduction_proxy_data->DeepCopy()
-          : nullptr,
-      info.raw_body_bytes, info.original_network_content_length,
-      base::TimeTicks::Now(), info.net_error,
-      info.load_timing_info
-          ? std::make_unique<net::LoadTimingInfo>(*info.load_timing_info)
-          : nullptr);
+  observer_->ResourceLoadComplete(web_contents()->GetMainFrame(), request_id,
+                                  resource_load_info);
 }
 
 void PageLoadMetricsObserverTester::SimulateFrameReceivedFirstUserActivation(
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc b/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
index fb6772e..cef3475 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
+++ b/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
@@ -26,7 +26,6 @@
 #include "chrome/browser/page_load_metrics/observers/live_tab_count_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/loading_predictor_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/local_network_requests_page_load_metrics_observer.h"
-#include "chrome/browser/page_load_metrics/observers/lofi_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/multi_tab_loading_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/no_state_prefetch_page_load_metrics_observer.h"
@@ -99,8 +98,6 @@
         std::make_unique<
             data_reduction_proxy::DataReductionProxyMetricsObserver>());
     tracker->AddObserver(std::make_unique<SchemePageLoadMetricsObserver>());
-    tracker->AddObserver(
-        std::make_unique<data_reduction_proxy::LoFiPageLoadMetricsObserver>());
     tracker->AddObserver(std::make_unique<FromGWSPageLoadMetricsObserver>());
     tracker->AddObserver(std::make_unique<ForegroundDurationUKMObserver>());
     tracker->AddObserver(
diff --git a/chrome/browser/performance_manager/graph/graph_impl.cc b/chrome/browser/performance_manager/graph/graph_impl.cc
index 9d63889..8c397fd 100644
--- a/chrome/browser/performance_manager/graph/graph_impl.cc
+++ b/chrome/browser/performance_manager/graph/graph_impl.cc
@@ -378,4 +378,24 @@
   system_node_.reset();
 }
 
+template <>
+const std::vector<FrameNodeObserver*>& GraphImpl::GetObservers() const {
+  return frame_node_observers_;
+}
+
+template <>
+const std::vector<PageNodeObserver*>& GraphImpl::GetObservers() const {
+  return page_node_observers_;
+}
+
+template <>
+const std::vector<ProcessNodeObserver*>& GraphImpl::GetObservers() const {
+  return process_node_observers_;
+}
+
+template <>
+const std::vector<SystemNodeObserver*>& GraphImpl::GetObservers() const {
+  return system_node_observers_;
+}
+
 }  // namespace performance_manager
diff --git a/chrome/browser/performance_manager/graph/graph_impl.h b/chrome/browser/performance_manager/graph/graph_impl.h
index 15a36747..780ef83 100644
--- a/chrome/browser/performance_manager/graph/graph_impl.h
+++ b/chrome/browser/performance_manager/graph/graph_impl.h
@@ -113,22 +113,6 @@
   // TypedNodeBase.
   template <typename Observer>
   const std::vector<Observer*>& GetObservers() const;
-  template <>
-  const std::vector<FrameNodeObserver*>& GetObservers() const {
-    return frame_node_observers_;
-  }
-  template <>
-  const std::vector<PageNodeObserver*>& GetObservers() const {
-    return page_node_observers_;
-  }
-  template <>
-  const std::vector<ProcessNodeObserver*>& GetObservers() const {
-    return process_node_observers_;
-  }
-  template <>
-  const std::vector<SystemNodeObserver*>& GetObservers() const {
-    return system_node_observers_;
-  }
 
  private:
   using ProcessByPidMap = std::unordered_map<base::ProcessId, ProcessNodeImpl*>;
diff --git a/chrome/browser/picture_in_picture/picture_in_picture_window_controller_browsertest.cc b/chrome/browser/picture_in_picture/picture_in_picture_window_controller_browsertest.cc
index 372792e..6a131f29 100644
--- a/chrome/browser/picture_in_picture/picture_in_picture_window_controller_browsertest.cc
+++ b/chrome/browser/picture_in_picture/picture_in_picture_window_controller_browsertest.cc
@@ -68,7 +68,7 @@
   MockPictureInPictureWindowController() = default;
 
   // PictureInPictureWindowController:
-  MOCK_METHOD0(Show, gfx::Size());
+  MOCK_METHOD0(Show, void());
   MOCK_METHOD1(Close, void(bool));
   MOCK_METHOD0(CloseAndFocusInitiator, void());
   MOCK_METHOD0(OnWindowDestroyed, void());
diff --git a/chrome/browser/picture_in_picture/picture_in_picture_window_manager.cc b/chrome/browser/picture_in_picture/picture_in_picture_window_manager.cc
index c2b0ec0..d75388a 100644
--- a/chrome/browser/picture_in_picture/picture_in_picture_window_manager.cc
+++ b/chrome/browser/picture_in_picture/picture_in_picture_window_manager.cc
@@ -7,6 +7,7 @@
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/picture_in_picture_window_controller.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_delegate.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "ui/gfx/geometry/size.h"
 
@@ -52,7 +53,8 @@
   pip_window_controller_->Show();
 }
 
-gfx::Size PictureInPictureWindowManager::EnterPictureInPicture(
+content::PictureInPictureResult
+PictureInPictureWindowManager::EnterPictureInPicture(
     content::WebContents* web_contents,
     const viz::SurfaceId& surface_id,
     const gfx::Size& natural_size) {
@@ -69,7 +71,9 @@
   }
 
   pip_window_controller_->EmbedSurface(surface_id, natural_size);
-  return pip_window_controller_->Show();
+  pip_window_controller_->Show();
+
+  return content::PictureInPictureResult::kSuccess;
 }
 
 void PictureInPictureWindowManager::ExitPictureInPicture() {
diff --git a/chrome/browser/picture_in_picture/picture_in_picture_window_manager.h b/chrome/browser/picture_in_picture/picture_in_picture_window_manager.h
index 97abe26..c019db4 100644
--- a/chrome/browser/picture_in_picture/picture_in_picture_window_manager.h
+++ b/chrome/browser/picture_in_picture/picture_in_picture_window_manager.h
@@ -8,6 +8,7 @@
 #include "base/memory/singleton.h"
 
 namespace content {
+enum class PictureInPictureResult;
 class PictureInPictureWindowController;
 class WebContents;
 }  // namespace content
@@ -34,9 +35,9 @@
   // controller directly.
   void EnterPictureInPictureWithController(
       content::PictureInPictureWindowController* pip_window_controller);
-  gfx::Size EnterPictureInPicture(content::WebContents*,
-                                  const viz::SurfaceId&,
-                                  const gfx::Size&);
+  content::PictureInPictureResult EnterPictureInPicture(content::WebContents*,
+                                                        const viz::SurfaceId&,
+                                                        const gfx::Size&);
   void ExitPictureInPicture();
 
   content::WebContents* GetWebContents();
diff --git a/chrome/browser/policy/cloud/user_policy_signin_service.cc b/chrome/browser/policy/cloud/user_policy_signin_service.cc
index fccda2ba..15f9822 100644
--- a/chrome/browser/policy/cloud/user_policy_signin_service.cc
+++ b/chrome/browser/policy/cloud/user_policy_signin_service.cc
@@ -62,36 +62,6 @@
   UserPolicySigninServiceBase::PrepareForUserCloudPolicyManagerShutdown();
 }
 
-void UserPolicySigninService::RegisterForPolicyWithLoginToken(
-    const std::string& username,
-    const std::string& oauth2_refresh_token,
-    const PolicyRegistrationCallback& callback) {
-  DCHECK(!oauth2_refresh_token.empty());
-
-  // Create a new CloudPolicyClient for fetching the DMToken.
-  std::unique_ptr<CloudPolicyClient> policy_client =
-      CreateClientForRegistrationOnly(username);
-  if (!policy_client) {
-    callback.Run(std::string(), std::string());
-    return;
-  }
-
-  // Fire off the registration process. Callback keeps the CloudPolicyClient
-  // alive for the length of the registration process. Use the system
-  // request context because the user is not signed in to this profile yet
-  // (we are just doing a test registration to see if policy is supported for
-  // this user).
-  registration_helper_ = std::make_unique<CloudPolicyClientRegistrationHelper>(
-      policy_client.get(),
-      enterprise_management::DeviceRegisterRequest::BROWSER);
-  registration_helper_->StartRegistrationWithLoginToken(
-      oauth2_refresh_token,
-      base::Bind(&UserPolicySigninService::CallPolicyRegistrationCallback,
-                 base::Unretained(this),
-                 base::Passed(&policy_client),
-                 callback));
-}
-
 void UserPolicySigninService::RegisterForPolicyWithAccountId(
     const std::string& username,
     const std::string& account_id,
diff --git a/chrome/browser/policy/cloud/user_policy_signin_service.h b/chrome/browser/policy/cloud/user_policy_signin_service.h
index 264b44f..34e1333 100644
--- a/chrome/browser/policy/cloud/user_policy_signin_service.h
+++ b/chrome/browser/policy/cloud/user_policy_signin_service.h
@@ -40,16 +40,6 @@
       scoped_refptr<network::SharedURLLoaderFactory> system_url_loader_factory);
   ~UserPolicySigninService() override;
 
-  // Registers a CloudPolicyClient for fetching policy for a user. The
-  // |oauth2_login_token| and |username| are explicitly passed because
-  // the user is not signed in yet (ProfileOAuth2TokenService does not have
-  // any tokens yet to prevent services from using it until after we've fetched
-  // policy).
-  void RegisterForPolicyWithLoginToken(
-      const std::string& username,
-      const std::string& oauth2_login_token,
-      const PolicyRegistrationCallback& callback);
-
   // Registers a CloudPolicyClient for fetching policy for a user. |username| is
   // explicitly passed because the user is not yet authenticated, but the token
   // service has a refresh token available for |account_id|.
diff --git a/chrome/browser/policy/cloud/user_policy_signin_service_unittest.cc b/chrome/browser/policy/cloud/user_policy_signin_service_unittest.cc
index 1c37898..8c4127f 100644
--- a/chrome/browser/policy/cloud/user_policy_signin_service_unittest.cc
+++ b/chrome/browser/policy/cloud/user_policy_signin_service_unittest.cc
@@ -69,15 +69,6 @@
 
 constexpr char kTestUser[] = "testuser@test.com";
 
-#if !defined(OS_ANDROID)
-constexpr char kValidTokenResponse[] = R"(
-    {
-      "access_token": "at1",
-      "expires_in": 3600,
-      "token_type": "Bearer"
-    })";
-#endif
-
 constexpr char kHostedDomainResponse[] = R"(
     {
       "hd": "test.com"
@@ -113,23 +104,14 @@
   }
 
   void RegisterPolicyClientWithCallback(UserPolicySigninService* service) {
-    // Policy client registration on Android depends on Token Service having
-    // a valid login token, while on other platforms, the login refresh token
-    // is specified directly.
     UserPolicySigninServiceBase::PolicyRegistrationCallback callback =
         base::Bind(&UserPolicySigninServiceTest::OnRegisterCompleted,
                    base::Unretained(this));
-#if defined(OS_ANDROID)
     AccountInfo account_info =
         identity_test_env()->MakeAccountAvailable(kTestUser);
     service->RegisterForPolicyWithAccountId(kTestUser, account_info.gaia,
                                             callback);
     ASSERT_TRUE(IsRequestActive());
-#else
-    service->RegisterForPolicyWithLoginToken(kTestUser, "mock_oauth_token",
-                                             callback);
-    ASSERT_TRUE(IsRequestActive());
-#endif
   }
 
   void SetUp() override {
@@ -216,32 +198,17 @@
   }
 
   void MakeOAuthTokenFetchSucceed() {
-#if defined(OS_ANDROID)
     ASSERT_TRUE(IsRequestActive());
     identity_test_env()
         ->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
             "access_token", base::Time::Now());
-#else
-    ASSERT_TRUE(IsRequestActive());
-    test_url_loader_factory_.AddResponse(
-        GaiaUrls::GetInstance()->oauth2_token_url().spec(),
-        kValidTokenResponse);
-    base::RunLoop().RunUntilIdle();
-    test_url_loader_factory_.ClearResponses();
-#endif
   }
 
   void MakeOAuthTokenFetchFail() {
-#if defined(OS_ANDROID)
     ASSERT_TRUE(identity_test_env()->IsAccessTokenRequestPending());
     identity_test_env()
         ->WaitForAccessTokenRequestIfNecessaryAndRespondWithError(
             GoogleServiceAuthError::FromServiceError("fail"));
-#else
-    ASSERT_TRUE(test_url_loader_factory_.SimulateResponseForPendingRequest(
-        GaiaUrls::GetInstance()->oauth2_token_url().spec(), "",
-        net::HTTP_BAD_REQUEST));
-#endif
   }
 
   void ReportHostedDomainStatus(bool is_hosted_domain) {
@@ -294,53 +261,8 @@
         &job_control, net::OK, DeviceManagementService::kSuccess,
         registration_response);
 
-    // UserCloudPolicyManager should not be initialized yet.
-    ASSERT_FALSE(manager_->core()->service());
     EXPECT_TRUE(register_completed_);
     EXPECT_EQ(dm_token_, expected_dm_token);
-
-    // Now call to fetch policy - this should fire off a fetch request.
-    EXPECT_CALL(device_management_service_, StartJob(_))
-        .WillOnce(DoAll(
-            device_management_service_.CaptureJobType(&job_type),
-            device_management_service_.StartJobFullControl(&job_control)));
-
-    signin_service->FetchPolicyForSignedInUser(
-        test_account_id_, dm_token_, client_id_,
-        test_url_loader_factory_.GetSafeWeakWrapper(),
-        base::Bind(&UserPolicySigninServiceTest::OnPolicyRefresh,
-                   base::Unretained(this)));
-
-    Mock::VerifyAndClearExpectations(this);
-    ASSERT_NE(nullptr, job_control);
-    EXPECT_EQ(DeviceManagementService::JobConfiguration::TYPE_POLICY_FETCH,
-              job_type);
-
-    // UserCloudPolicyManager should now be initialized.
-    EXPECT_EQ(mock_store_->signin_account_id(), test_account_id_);
-    ASSERT_TRUE(manager_->core()->service());
-
-    // Make the policy fetch succeed - this should result in a write to the
-    // store and ultimately result in a call to OnPolicyRefresh().
-    EXPECT_CALL(*mock_store_, Store(_));
-    EXPECT_CALL(*this, OnPolicyRefresh(true)).Times(1);
-
-    // Create a fake policy blob to deliver to the client.
-    em::DeviceManagementResponse fetch_response;
-    em::PolicyData policy_data;
-    policy_data.set_policy_type(dm_protocol::kChromeUserPolicyType);
-    em::PolicyFetchResponse* policy_response =
-        fetch_response.mutable_policy_response()->add_responses();
-    ASSERT_TRUE(
-        policy_data.SerializeToString(policy_response->mutable_policy_data()));
-    device_management_service_.DoURLCompletion(
-        &job_control, net::OK, DeviceManagementService::kSuccess,
-        fetch_response);
-
-    // Complete the store which should cause the policy fetch callback to be
-    // invoked.
-    mock_store_->NotifyStoreLoaded();
-    Mock::VerifyAndClearExpectations(this);
   }
 
   identity::IdentityTestEnvironment* identity_test_env() {
@@ -401,7 +323,7 @@
   ASSERT_FALSE(manager_->core()->service());
 }
 
-#if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
+#if !defined(OS_ANDROID)
 TEST_F(UserPolicySigninServiceTest, InitRefreshTokenAvailableBeforeSignin) {
   // Make sure user is not signed in.
   ASSERT_FALSE(identity_test_env()->identity_manager()->HasPrimaryAccount());
@@ -426,7 +348,7 @@
   EXPECT_EQ(mock_store_->signin_account_id(), test_account_id_);
   ASSERT_TRUE(IsRequestActive());
 }
-#endif  // !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
+#endif  // !defined(OS_ANDROID)
 
 // TODO(joaodasilva): these tests rely on issuing the OAuth2 login refresh
 // token after signin. Revisit this after figuring how to handle that on
@@ -746,42 +668,6 @@
   ASSERT_FALSE(manager_->core()->service());
 }
 
-TEST_F(UserPolicySigninServiceTest, FetchPolicyFailed) {
-  // Initiate a policy fetch request.
-  DeviceManagementService::JobConfiguration::JobType job_type =
-      DeviceManagementService::JobConfiguration::TYPE_INVALID;
-  DeviceManagementService::JobControl* job_control = nullptr;
-  EXPECT_CALL(device_management_service_, StartJob(_))
-      .WillOnce(
-          DoAll(device_management_service_.CaptureJobType(&job_type),
-                device_management_service_.StartJobFullControl(&job_control)));
-  UserPolicySigninService* signin_service =
-      UserPolicySigninServiceFactory::GetForProfile(profile_.get());
-  signin_service->FetchPolicyForSignedInUser(
-      test_account_id_, "mock_dm_token", "mock_client_id",
-      test_url_loader_factory_.GetSafeWeakWrapper(),
-      base::Bind(&UserPolicySigninServiceTest::OnPolicyRefresh,
-                 base::Unretained(this)));
-  ASSERT_NE(nullptr, job_control);
-  EXPECT_EQ(DeviceManagementService::JobConfiguration::TYPE_POLICY_FETCH,
-            job_type);
-
-  // Make the policy fetch fail.
-  EXPECT_CALL(*this, OnPolicyRefresh(false)).Times(1);
-  device_management_service_.DoURLCompletion(
-      &job_control, net::HTTP_BAD_REQUEST, DeviceManagementService::kSuccess,
-      em::DeviceManagementResponse());
-
-  // UserCloudPolicyManager should be initialized.
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(mock_store_->signin_account_id(), test_account_id_);
-  ASSERT_TRUE(manager_->core()->service());
-}
-
-TEST_F(UserPolicySigninServiceTest, FetchPolicySuccess) {
-  ASSERT_NO_FATAL_FAILURE(TestSuccessfulSignin());
-}
-
 TEST_F(UserPolicySigninServiceTest, SignOutThenSignInAgain) {
   // Explicitly forcing this call is necessary for the clearing of the primary
   // account to result in the account being fully removed in this testing
@@ -799,74 +685,6 @@
   ASSERT_NO_FATAL_FAILURE(TestSuccessfulSignin());
 }
 
-TEST_F(UserPolicySigninServiceTest, PolicyFetchFailureTemporary) {
-  ASSERT_NO_FATAL_FAILURE(TestSuccessfulSignin());
-
-  ASSERT_TRUE(manager_->IsClientRegistered());
-
-  // Kick off another policy fetch.
-  DeviceManagementService::JobConfiguration::JobType job_type =
-      DeviceManagementService::JobConfiguration::TYPE_INVALID;
-  DeviceManagementService::JobControl* job_control = nullptr;
-  EXPECT_CALL(device_management_service_, StartJob(_))
-      .WillOnce(
-          DoAll(device_management_service_.CaptureJobType(&job_type),
-                device_management_service_.StartJobFullControl(&job_control)));
-  manager_->RefreshPolicies();
-
-  Mock::VerifyAndClearExpectations(this);
-  ASSERT_NE(nullptr, job_control);
-  EXPECT_EQ(DeviceManagementService::JobConfiguration::TYPE_POLICY_FETCH,
-            job_type);
-
-  // Now, fake a transient error from the server on this policy fetch. This
-  // should have no impact on the cached policy.
-  device_management_service_.DoURLCompletion(
-      &job_control, net::HTTP_BAD_REQUEST, DeviceManagementService::kSuccess,
-      em::DeviceManagementResponse());
-
-  base::RunLoop().RunUntilIdle();
-  ASSERT_TRUE(manager_->IsClientRegistered());
-}
-
-TEST_F(UserPolicySigninServiceTest, PolicyFetchFailureDisableManagement) {
-  ASSERT_NO_FATAL_FAILURE(TestSuccessfulSignin());
-
-  EXPECT_TRUE(manager_->IsClientRegistered());
-#if !defined(OS_ANDROID)
-  EXPECT_FALSE(signin_util::IsUserSignoutAllowedForProfile(profile_.get()));
-#endif
-
-  // Kick off another policy fetch.
-  DeviceManagementService::JobConfiguration::JobType job_type =
-      DeviceManagementService::JobConfiguration::TYPE_INVALID;
-  DeviceManagementService::JobControl* job_control = nullptr;
-  EXPECT_CALL(device_management_service_, StartJob(_))
-      .WillOnce(
-          DoAll(device_management_service_.CaptureJobType(&job_type),
-                device_management_service_.StartJobFullControl(&job_control)));
-  manager_->RefreshPolicies();
-  Mock::VerifyAndClearExpectations(this);
-  ASSERT_NE(nullptr, job_control);
-  EXPECT_EQ(DeviceManagementService::JobConfiguration::TYPE_POLICY_FETCH,
-            job_type);
-
-  // Now, fake a SC_FORBIDDEN error from the server on this policy fetch.  This
-  // indicates that chrome management is disabled and will result in the cached
-  // policy being removed and the manager shut down.
-  EXPECT_CALL(*mock_store_, Clear());
-  device_management_service_.DoURLCompletion(
-      &job_control, net::OK,
-      DeviceManagementService::kDeviceManagementNotAllowed,
-      em::DeviceManagementResponse());
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_FALSE(manager_->IsClientRegistered());
-#if !defined(OS_ANDROID)
-  EXPECT_TRUE(signin_util::IsUserSignoutAllowedForProfile(profile_.get()));
-#endif
-}
-
 }  // namespace
 
 }  // namespace policy
diff --git a/chrome/browser/policy/policy_browsertest.cc b/chrome/browser/policy/policy_browsertest.cc
index 41d2cf6..6229be9 100644
--- a/chrome/browser/policy/policy_browsertest.cc
+++ b/chrome/browser/policy/policy_browsertest.cc
@@ -3645,7 +3645,7 @@
   TestAddAppWindowObserver add_window_observer(
       extensions::AppWindowRegistry::Get(browser()->profile()));
   OpenApplication(AppLaunchParams(
-      browser()->profile(), extension.get(), extensions::LAUNCH_CONTAINER_NONE,
+      browser()->profile(), extension->id(), extensions::LAUNCH_CONTAINER_NONE,
       WindowOpenDisposition::NEW_WINDOW, extensions::SOURCE_TEST));
   extensions::AppWindow* window = add_window_observer.WaitForAppWindow();
   ASSERT_TRUE(window);
diff --git a/chrome/browser/profiles/profile_manager.cc b/chrome/browser/profiles/profile_manager.cc
index aeabf03..eaa250c 100644
--- a/chrome/browser/profiles/profile_manager.cc
+++ b/chrome/browser/profiles/profile_manager.cc
@@ -1694,6 +1694,9 @@
 #if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
       // Sign out if force-sign-in policy is enabled and profile is not signed
       // in.
+      VLOG(1) << "ForceSigninCheck: " << signin_util::IsForceSigninEnabled()
+              << ", " << was_authenticated_status << ", "
+              << !entry->IsAuthenticated();
       if (signin_util::IsForceSigninEnabled() && was_authenticated_status &&
           !entry->IsAuthenticated()) {
         auto* account_mutator = identity_manager->GetPrimaryAccountMutator();
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
index 31ff9d1..12293731 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -2577,7 +2577,7 @@
     return;
 
   AppLaunchParams launch_params(
-      GetProfile(), pwa, extensions::LAUNCH_CONTAINER_WINDOW,
+      GetProfile(), pwa->id(), extensions::LAUNCH_CONTAINER_WINDOW,
       WindowOpenDisposition::CURRENT_TAB, extensions::SOURCE_CONTEXT_MENU);
   launch_params.override_url = params_.link_url;
   OpenApplication(launch_params);
diff --git a/chrome/browser/resources/local_ntp/customize.js b/chrome/browser/resources/local_ntp/customize.js
index 15d1206..9086715 100644
--- a/chrome/browser/resources/local_ntp/customize.js
+++ b/chrome/browser/resources/local_ntp/customize.js
@@ -6,8 +6,9 @@
 'use strict';
 
 // TODO(crbug.com/937570): After the RP launches this should be renamed to
-// customizationMenu along with the file, and large parts can be refactored/removed.
-const customBackgrounds = {};
+// customizationMenu along with the file, and large parts can be
+// refactored/removed.
+const customize = {};
 
 /**
  * The browser embeddedSearch.newTabPage object.
@@ -23,7 +24,7 @@
  * @enum {number}
  * @const
  */
-const BACKGROUND_CUSTOMIZATION_LOG_TYPE = {
+customize.BACKGROUND_CUSTOMIZATION_LOG_TYPE = {
   // The 'Chrome backgrounds' menu item was clicked.
   NTP_CUSTOMIZE_CHROME_BACKGROUNDS_CLICKED: 40,
   // The 'Upload an image' menu item was clicked.
@@ -53,7 +54,7 @@
  * @enum {number}
  * @const
  */
-customBackgrounds.KEYCODES = {
+customize.KEYCODES = {
   BACKSPACE: 8,
   DOWN: 40,
   ENTER: 13,
@@ -70,7 +71,7 @@
  * @enum {string}
  * @const
  */
-customBackgrounds.IDS = {
+customize.IDS = {
   ATTR1: 'attr1',
   ATTR2: 'attr2',
   ATTRIBUTIONS: 'custom-bg-attr',
@@ -120,7 +121,7 @@
  * @enum {string}
  * @const
  */
-customBackgrounds.CLASSES = {
+customize.CLASSES = {
   ATTR_SMALL: 'attr-small',
   ATTR_COMMON: 'attr-common',
   ATTR_LINK: 'attr-link',
@@ -149,10 +150,9 @@
  * @enum {number}
  * @const
  */
-customBackgrounds.SOURCES = {
+customize.SOURCES = {
   NONE: -1,
   CHROME_BACKGROUNDS: 0,
-  IMAGE_UPLOAD: 1,
 };
 
 /**
@@ -160,79 +160,79 @@
  * @enum {number}
  * @const
  */
-customBackgrounds.MENU_ENTRIES = {
+customize.MENU_ENTRIES = {
   CHROME_BACKGROUNDS: 0,
   UPLOAD_IMAGE: 1,
   CUSTOM_LINKS_RESTORE_DEFAULT: 2,
   RESTORE_DEFAULT: 3,
 };
 
-customBackgrounds.CUSTOM_BACKGROUND_OVERLAY =
+customize.CUSTOM_BACKGROUND_OVERLAY =
     'linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.3))';
 
 // These shound match the corresponding values in local_ntp.js, that control the
 // mv-notice element.
-customBackgrounds.delayedHideNotification = -1;
-customBackgrounds.NOTIFICATION_TIMEOUT = 10000;
+customize.delayedHideNotification = -1;
+customize.NOTIFICATION_TIMEOUT = 10000;
 
 /* Were the background tiles already created.
  * @type {bool}
  */
-customBackgrounds.builtTiles = false;
+customize.builtTiles = false;
 
 /* Tile that was selected by the user.
  * @type {HTMLElement}
  */
-customBackgrounds.selectedTile = null;
+customize.selectedTile = null;
 
 /**
  * Number of rows in the custom background dialog to preload.
  * @type {number}
  * @const
  */
-customBackgrounds.ROWS_TO_PRELOAD = 3;
+customize.ROWS_TO_PRELOAD = 3;
 
 /* Type of collection that is being browsed, needed in order
  * to return from the image dialog.
  * @type {number}
  */
-customBackgrounds.dialogCollectionsSource = customBackgrounds.SOURCES.NONE;
+customize.dialogCollectionsSource = customize.SOURCES.NONE;
 
 /*
  * Called when the error notification should be shown.
  * @type {?Function}
  * @private
  */
-customBackgrounds.showErrorNotification = null;
+customize.showErrorNotification = null;
 
 /*
  * Called when the custom link notification should be hidden.
  * @type {?Function}
  * @private
  */
-customBackgrounds.hideCustomLinkNotification = null;
+customize.hideCustomLinkNotification = null;
 
 /*
  * The currently selected option in the richer picker.
  * @type {?Element}
  * @private
  */
-customBackgrounds.richerPicker_selectedOption = null;
+customize.richerPicker_selectedOption = null;
 
 /**
  * Sets the visibility of the settings menu and individual options depending on
  * their respective features.
  */
-customBackgrounds.setMenuVisibility = function() {
+customize.setMenuVisibility = function() {
   // Reset all hidden values.
-  $(customBackgrounds.IDS.EDIT_BG).hidden = false;
-  $(customBackgrounds.IDS.DEFAULT_WALLPAPERS).hidden = false;
-  $(customBackgrounds.IDS.UPLOAD_IMAGE).hidden = false;
-  $(customBackgrounds.IDS.RESTORE_DEFAULT).hidden = false;
-  $(customBackgrounds.IDS.EDIT_BG_DIVIDER).hidden = false;
-  $(customBackgrounds.IDS.CUSTOM_LINKS_RESTORE_DEFAULT).hidden =
+  $(customize.IDS.EDIT_BG).hidden = false;
+  $(customize.IDS.DEFAULT_WALLPAPERS).hidden = false;
+  $(customize.IDS.UPLOAD_IMAGE).hidden = false;
+  $(customize.IDS.RESTORE_DEFAULT).hidden = false;
+  $(customize.IDS.EDIT_BG_DIVIDER).hidden = false;
+  $(customize.IDS.CUSTOM_LINKS_RESTORE_DEFAULT).hidden =
       configData.hideShortcuts;
-  $(customBackgrounds.IDS.COLORS_BUTTON).hidden = !configData.chromeColors;
+  $(customize.IDS.COLORS_BUTTON).hidden = !configData.chromeColors;
 };
 
 /**
@@ -241,51 +241,51 @@
  * @param {string} attributionLine2 Second line of attribution.
  * @param {string} attributionActionUrl Url to learn more about the image.
  */
-customBackgrounds.setAttribution = function(
+customize.setAttribution = function(
     attributionLine1, attributionLine2, attributionActionUrl) {
-  const attributionBox = $(customBackgrounds.IDS.ATTRIBUTIONS);
+  const attributionBox = $(customize.IDS.ATTRIBUTIONS);
   const attr1 = document.createElement('span');
-  attr1.id = customBackgrounds.IDS.ATTR1;
+  attr1.id = customize.IDS.ATTR1;
   const attr2 = document.createElement('span');
-  attr2.id = customBackgrounds.IDS.ATTR2;
+  attr2.id = customize.IDS.ATTR2;
 
   if (attributionLine1 !== '') {
     // Shouldn't be changed from textContent for security assurances.
     attr1.textContent = attributionLine1;
-    attr1.classList.add(customBackgrounds.CLASSES.ATTR_COMMON);
-    $(customBackgrounds.IDS.ATTRIBUTIONS).appendChild(attr1);
+    attr1.classList.add(customize.CLASSES.ATTR_COMMON);
+    $(customize.IDS.ATTRIBUTIONS).appendChild(attr1);
   }
   if (attributionLine2 !== '') {
     // Shouldn't be changed from textContent for security assurances.
     attr2.textContent = attributionLine2;
-    attr2.classList.add(customBackgrounds.CLASSES.ATTR_SMALL);
-    attr2.classList.add(customBackgrounds.CLASSES.ATTR_COMMON);
+    attr2.classList.add(customize.CLASSES.ATTR_SMALL);
+    attr2.classList.add(customize.CLASSES.ATTR_COMMON);
     attributionBox.appendChild(attr2);
   }
   if (attributionActionUrl !== '') {
     const attr = (attributionLine2 !== '' ? attr2 : attr1);
-    attr.classList.add(customBackgrounds.CLASSES.ATTR_LINK);
+    attr.classList.add(customize.CLASSES.ATTR_LINK);
 
     const linkIcon = document.createElement('div');
-    linkIcon.id = customBackgrounds.IDS.LINK_ICON;
+    linkIcon.id = customize.IDS.LINK_ICON;
     // Enlarge link-icon when there is only one line of attribution
     if (attributionLine2 === '') {
-      linkIcon.classList.add(customBackgrounds.CLASSES.SINGLE_ATTR);
+      linkIcon.classList.add(customize.CLASSES.SINGLE_ATTR);
     }
     attr.insertBefore(linkIcon, attr.firstChild);
 
-    attributionBox.classList.add(customBackgrounds.CLASSES.ATTR_LINK);
+    attributionBox.classList.add(customize.CLASSES.ATTR_LINK);
     attributionBox.href = attributionActionUrl;
     attributionBox.onclick = function() {
-      ntpApiHandle.logEvent(
-          BACKGROUND_CUSTOMIZATION_LOG_TYPE.NTP_CUSTOMIZE_ATTRIBUTION_CLICKED);
+      ntpApiHandle.logEvent(customize.BACKGROUND_CUSTOMIZATION_LOG_TYPE
+                                .NTP_CUSTOMIZE_ATTRIBUTION_CLICKED);
     };
     attributionBox.style.cursor = 'pointer';
   }
 };
 
-customBackgrounds.clearAttribution = function() {
-  const attributions = $(customBackgrounds.IDS.ATTRIBUTIONS);
+customize.clearAttribution = function() {
+  const attributions = $(customize.IDS.ATTRIBUTIONS);
   attributions.removeAttribute('href');
   attributions.className = '';
   attributions.style.cursor = 'none';
@@ -294,23 +294,23 @@
   }
 };
 
-customBackgrounds.unselectTile = function() {
-  $(customBackgrounds.IDS.DONE).disabled = true;
-  customBackgrounds.selectedTile = null;
-  $(customBackgrounds.IDS.DONE).tabIndex = -1;
+customize.unselectTile = function() {
+  $(customize.IDS.DONE).disabled = true;
+  customize.selectedTile = null;
+  $(customize.IDS.DONE).tabIndex = -1;
 };
 
 /**
  * Remove all collection tiles from the container when the dialog
  * is closed.
  */
-customBackgrounds.resetSelectionDialog = function() {
-  $(customBackgrounds.IDS.TILES).scrollTop = 0;
-  const tileContainer = $(customBackgrounds.IDS.TILES);
+customize.resetSelectionDialog = function() {
+  $(customize.IDS.TILES).scrollTop = 0;
+  const tileContainer = $(customize.IDS.TILES);
   while (tileContainer.firstChild) {
     tileContainer.removeChild(tileContainer.firstChild);
   }
-  customBackgrounds.unselectTile();
+  customize.unselectTile();
 };
 
 /**
@@ -318,59 +318,58 @@
  * @param {?Element} button The button element to apply styling to.
  * @param {?Element} menu The menu element to apply styling to.
  */
-customBackgrounds.richerPicker_selectMenuOption = function(button, menu) {
+customize.richerPicker_selectMenuOption = function(button, menu) {
   if (!button || !menu) {
     return;
   }
-  button.classList.toggle(customBackgrounds.CLASSES.SELECTED, true);
-  customBackgrounds.richerPicker_selectedOption = button;
-  menu.classList.toggle(customBackgrounds.CLASSES.MENU_SHOWN, true);
+  button.classList.toggle(customize.CLASSES.SELECTED, true);
+  customize.richerPicker_selectedOption = button;
+  menu.classList.toggle(customize.CLASSES.MENU_SHOWN, true);
 };
 
 /**
  * Remove image tiles and maybe swap back to main background menu.
  * @param {boolean} showMenu Whether the main background menu should be shown.
  */
-customBackgrounds.richerPicker_resetImageMenu = function(showMenu) {
-  const backgroundMenu = $(customBackgrounds.IDS.BACKGROUNDS_MENU);
-  const imageMenu = $(customBackgrounds.IDS.BACKGROUNDS_IMAGE_MENU);
-  const menu = $(customBackgrounds.IDS.CUSTOMIZATION_MENU);
-  const menuTitle = $(customBackgrounds.IDS.MENU_TITLE);
+customize.richerPicker_resetImageMenu = function(showMenu) {
+  const backgroundMenu = $(customize.IDS.BACKGROUNDS_MENU);
+  const imageMenu = $(customize.IDS.BACKGROUNDS_IMAGE_MENU);
+  const menu = $(customize.IDS.CUSTOMIZATION_MENU);
+  const menuTitle = $(customize.IDS.MENU_TITLE);
 
   imageMenu.innerHTML = '';
-  imageMenu.classList.toggle(customBackgrounds.CLASSES.MENU_SHOWN, false);
+  imageMenu.classList.toggle(customize.CLASSES.MENU_SHOWN, false);
   menuTitle.textContent = menuTitle.dataset.mainTitle;
-  menu.classList.toggle(customBackgrounds.CLASSES.ON_IMAGE_MENU, false);
-  backgroundMenu.classList.toggle(
-      customBackgrounds.CLASSES.MENU_SHOWN, showMenu);
+  menu.classList.toggle(customize.CLASSES.ON_IMAGE_MENU, false);
+  backgroundMenu.classList.toggle(customize.CLASSES.MENU_SHOWN, showMenu);
   backgroundMenu.scrollTop = 0;
 
   // Reset done button state.
-  $(customBackgrounds.IDS.MENU_DONE).disabled = true;
-  customBackgrounds.richerPicker_deselectTile(customBackgrounds.selectedTile);
-  customBackgrounds.selectedTile = null;
-  $(customBackgrounds.IDS.MENU_DONE).tabIndex = -1;
+  $(customize.IDS.MENU_DONE).disabled = true;
+  customize.richerPicker_deselectTile(customize.selectedTile);
+  customize.selectedTile = null;
+  $(customize.IDS.MENU_DONE).tabIndex = -1;
 };
 
 /* Close the collection selection dialog and cleanup the state
  * @param {dialog} menu The dialog to be closed
  */
-customBackgrounds.closeCollectionDialog = function(menu) {
+customize.closeCollectionDialog = function(menu) {
   menu.close();
-  customBackgrounds.dialogCollectionsSource = customBackgrounds.SOURCES.NONE;
-  customBackgrounds.resetSelectionDialog();
+  customize.dialogCollectionsSource = customize.SOURCES.NONE;
+  customize.resetSelectionDialog();
 };
 
 /* Close and reset the dialog, and set the background.
  * @param {string} url The url of the selected background.
  */
-customBackgrounds.setBackground = function(
+customize.setBackground = function(
     url, attributionLine1, attributionLine2, attributionActionUrl) {
   if (configData.richerPicker) {
-    $(customBackgrounds.IDS.CUSTOMIZATION_MENU).close();
-    customBackgrounds.richerPicker_resetImageMenu(false);
+    $(customize.IDS.CUSTOMIZATION_MENU).close();
+    customize.richerPicker_resetImageMenu(false);
   } else {
-    customBackgrounds.closeCollectionDialog($(customBackgrounds.IDS.MENU));
+    customize.closeCollectionDialog($(customize.IDS.MENU));
   }
   window.chrome.embeddedSearch.newTabPage.setBackgroundURLWithAttributions(
       url, attributionLine1, attributionLine2, attributionActionUrl);
@@ -379,12 +378,12 @@
 /**
  * Create a tile for a Chrome Backgrounds collection.
  */
-customBackgrounds.createChromeBackgroundTile = function(data) {
+customize.createChromeBackgroundTile = function(data) {
   const tile = document.createElement('div');
   tile.style.backgroundImage = 'url(' + data.previewImageUrl + ')';
   tile.dataset.id = data.collectionId;
   tile.dataset.name = data.collectionName;
-  fadeInImageTile(tile, data.previewImageUrl, null);
+  customize.fadeInImageTile(tile, data.previewImageUrl, null);
   return tile;
 };
 
@@ -392,11 +391,11 @@
  * Get the number of tiles in a row according to current window width.
  * @return {number} the number of tiles per row
  */
-customBackgrounds.getTilesWide = function() {
+customize.getTilesWide = function() {
   // Browser window can only fit two columns. Should match "#bg-sel-menu" width.
-  if ($(customBackgrounds.IDS.MENU).offsetWidth < 517) {
+  if ($(customize.IDS.MENU).offsetWidth < 517) {
     return 2;
-  } else if ($(customBackgrounds.IDS.MENU).offsetWidth < 356) {
+  } else if ($(customize.IDS.MENU).offsetWidth < 356) {
     // Browser window can only fit one column. Should match @media (max-width:
     // 356) "#bg-sel-menu" width.
     return 1;
@@ -411,10 +410,10 @@
  * @param {number} deltaY Change in the y direction.
  * @param {string} current Number of the current tile.
  */
-customBackgrounds.getNextTile = function(deltaX, deltaY, current) {
+customize.getNextTile = function(deltaX, deltaY, current) {
   let idPrefix = 'coll_tile_';
-  if ($(customBackgrounds.IDS.MENU)
-          .classList.contains(customBackgrounds.CLASSES.IMAGE_DIALOG)) {
+  if ($(customize.IDS.MENU)
+          .classList.contains(customize.CLASSES.IMAGE_DIALOG)) {
     idPrefix = 'img_tile_';
   }
 
@@ -443,44 +442,42 @@
  * @param {number} collectionsSource The enum value of the source to fetch
  *              collection data from.
  */
-customBackgrounds.showCollectionSelectionDialog = function(collectionsSource) {
+customize.showCollectionSelectionDialog = function(collectionsSource) {
   const tileContainer = configData.richerPicker ?
-      $(customBackgrounds.IDS.BACKGROUNDS_MENU) :
-      $(customBackgrounds.IDS.TILES);
-  if (configData.richerPicker && customBackgrounds.builtTiles) {
+      $(customize.IDS.BACKGROUNDS_MENU) :
+      $(customize.IDS.TILES);
+  if (configData.richerPicker && customize.builtTiles) {
     return;
   }
-  customBackgrounds.builtTiles = true;
-  const menu = configData.richerPicker ?
-      $(customBackgrounds.IDS.CUSTOMIZATION_MENU) :
-      $(customBackgrounds.IDS.MENU);
-  if (collectionsSource != customBackgrounds.SOURCES.CHROME_BACKGROUNDS) {
+  customize.builtTiles = true;
+  const menu = configData.richerPicker ? $(customize.IDS.CUSTOMIZATION_MENU) :
+                                         $(customize.IDS.MENU);
+  if (collectionsSource != customize.SOURCES.CHROME_BACKGROUNDS) {
     console.log(
         'showCollectionSelectionDialog() called with invalid source=' +
         collectionsSource);
     return;
   }
-  customBackgrounds.dialogCollectionsSource = collectionsSource;
+  customize.dialogCollectionsSource = collectionsSource;
 
   if (!menu.open) {
     menu.showModal();
   }
 
   // Create dialog header.
-  $(customBackgrounds.IDS.TITLE).textContent =
+  $(customize.IDS.TITLE).textContent =
       configData.translatedStrings.selectChromeWallpaper;
   if (!configData.richerPicker) {
-    menu.classList.toggle(customBackgrounds.CLASSES.COLLECTION_DIALOG);
-    menu.classList.remove(customBackgrounds.CLASSES.IMAGE_DIALOG);
+    menu.classList.toggle(customize.CLASSES.COLLECTION_DIALOG);
+    menu.classList.remove(customize.CLASSES.IMAGE_DIALOG);
   }
 
   // Create dialog tiles.
   for (let i = 0; i < coll.length; ++i) {
     const tileBackground = document.createElement('div');
-    tileBackground.classList.add(
-        customBackgrounds.CLASSES.COLLECTION_TILE_BG);
-    const tile = customBackgrounds.createChromeBackgroundTile(coll[i]);
-    tile.classList.add(customBackgrounds.CLASSES.COLLECTION_TILE);
+    tileBackground.classList.add(customize.CLASSES.COLLECTION_TILE_BG);
+    const tile = customize.createChromeBackgroundTile(coll[i]);
+    tile.classList.add(customize.CLASSES.COLLECTION_TILE);
     tile.id = 'coll_tile_' + i;
     tile.dataset.tile_num = i;
     tile.tabIndex = -1;
@@ -488,12 +485,12 @@
     tile.setAttribute('role', 'button');
 
     const title = document.createElement('div');
-    title.classList.add(customBackgrounds.CLASSES.COLLECTION_TITLE);
+    title.classList.add(customize.CLASSES.COLLECTION_TITLE);
     title.textContent = tile.dataset.name;
 
     const tileInteraction = function(event) {
       let tile = event.target;
-      if (tile.classList.contains(customBackgrounds.CLASSES.COLLECTION_TITLE)) {
+      if (tile.classList.contains(customize.CLASSES.COLLECTION_TITLE)) {
         tile = tile.parentNode;
       }
 
@@ -507,7 +504,7 @@
       imgScript.src = 'chrome-search://local-ntp/ntp-background-images.js?' +
           'collection_id=' + tile.dataset.id;
       ntpApiHandle.logEvent(
-          BACKGROUND_CUSTOMIZATION_LOG_TYPE
+          customize.BACKGROUND_CUSTOMIZATION_LOG_TYPE
               .NTP_CUSTOMIZE_CHROME_BACKGROUND_SELECT_COLLECTION);
 
       document.body.appendChild(imgScript);
@@ -520,55 +517,54 @@
         // Dependent upon the success of the load, populate the image selection
         // dialog or close the current dialog.
         if (imageDataLoaded) {
-          $(customBackgrounds.IDS.BACKGROUNDS_MENU)
-              .classList.toggle(customBackgrounds.CLASSES.MENU_SHOWN, false);
-          $(customBackgrounds.IDS.BACKGROUNDS_IMAGE_MENU)
-              .classList.toggle(customBackgrounds.CLASSES.MENU_SHOWN, true);
+          $(customize.IDS.BACKGROUNDS_MENU)
+              .classList.toggle(customize.CLASSES.MENU_SHOWN, false);
+          $(customize.IDS.BACKGROUNDS_IMAGE_MENU)
+              .classList.toggle(customize.CLASSES.MENU_SHOWN, true);
 
           // In the RP the upload or default tile may be selected.
           if (configData.richerPicker) {
-            customBackgrounds.richerPicker_deselectTile(
-                customBackgrounds.selectedTile);
+            customize.richerPicker_deselectTile(customize.selectedTile);
           } else {
-            customBackgrounds.resetSelectionDialog();
+            customize.resetSelectionDialog();
           }
-          customBackgrounds.showImageSelectionDialog(tile.dataset.name);
+          customize.showImageSelectionDialog(tile.dataset.name);
         } else {
-          customBackgrounds.handleError(collImgErrors);
+          customize.handleError(collImgErrors);
         }
       };
     };
 
     tile.onclick = tileInteraction;
     tile.onkeydown = function(event) {
-      if (event.keyCode === customBackgrounds.KEYCODES.ENTER) {
+      if (event.keyCode === customize.KEYCODES.ENTER) {
         event.preventDefault();
         event.stopPropagation();
         tileInteraction(event);
       } else if (
-          event.keyCode === customBackgrounds.KEYCODES.LEFT ||
-          event.keyCode === customBackgrounds.KEYCODES.UP ||
-          event.keyCode === customBackgrounds.KEYCODES.RIGHT ||
-          event.keyCode === customBackgrounds.KEYCODES.DOWN) {
+          event.keyCode === customize.KEYCODES.LEFT ||
+          event.keyCode === customize.KEYCODES.UP ||
+          event.keyCode === customize.KEYCODES.RIGHT ||
+          event.keyCode === customize.KEYCODES.DOWN) {
         // Handle arrow key navigation.
         event.preventDefault();
         event.stopPropagation();
 
         let target = null;
-        if (event.keyCode === customBackgrounds.KEYCODES.LEFT) {
-          target = customBackgrounds.getNextTile(
+        if (event.keyCode === customize.KEYCODES.LEFT) {
+          target = customize.getNextTile(
               document.documentElement.classList.contains('rtl') ? 1 : -1, 0,
               event.currentTarget.dataset.tile_num);
-        } else if (event.keyCode === customBackgrounds.KEYCODES.UP) {
-          target = customBackgrounds.getNextTile(
+        } else if (event.keyCode === customize.KEYCODES.UP) {
+          target = customize.getNextTile(
               0, -1, event.currentTarget.dataset.tile_num);
-        } else if (event.keyCode === customBackgrounds.KEYCODES.RIGHT) {
-          target = customBackgrounds.getNextTile(
+        } else if (event.keyCode === customize.KEYCODES.RIGHT) {
+          target = customize.getNextTile(
               document.documentElement.classList.contains('rtl') ? -1 : 1, 0,
               event.currentTarget.dataset.tile_num);
-        } else if (event.keyCode === customBackgrounds.KEYCODES.DOWN) {
-          target = customBackgrounds.getNextTile(
-              0, 1, event.currentTarget.dataset.tile_num);
+        } else if (event.keyCode === customize.KEYCODES.DOWN) {
+          target =
+              customize.getNextTile(0, 1, event.currentTarget.dataset.tile_num);
         }
         if (target) {
           target.focus();
@@ -583,7 +579,7 @@
     tileContainer.appendChild(tileBackground);
   }
 
-  $(customBackgrounds.IDS.TILES).focus();
+  $(customize.IDS.TILES).focus();
 };
 
 /**
@@ -591,20 +587,20 @@
  * button.
  * @param {?Element} tile The tile to apply styling to.
  */
-customBackgrounds.richerPicker_selectTile = function(tile) {
+customize.richerPicker_selectTile = function(tile) {
   if (!tile) {
     return;
   }
-  tile.parentElement.classList.toggle(customBackgrounds.CLASSES.SELECTED, true);
-  $(customBackgrounds.IDS.MENU_DONE).disabled = false;
-  customBackgrounds.selectedTile = tile;
-  $(customBackgrounds.IDS.MENU_DONE).tabIndex = 0;
+  tile.parentElement.classList.toggle(customize.CLASSES.SELECTED, true);
+  $(customize.IDS.MENU_DONE).disabled = false;
+  customize.selectedTile = tile;
+  $(customize.IDS.MENU_DONE).tabIndex = 0;
 
   // Create and append selected check.
   const selectedCircle = document.createElement('div');
   const selectedCheck = document.createElement('div');
-  selectedCircle.classList.add(customBackgrounds.CLASSES.SELECTED_CIRCLE);
-  selectedCheck.classList.add(customBackgrounds.CLASSES.SELECTED_CHECK);
+  selectedCircle.classList.add(customize.CLASSES.SELECTED_CIRCLE);
+  selectedCheck.classList.add(customize.CLASSES.SELECTED_CHECK);
   selectedCircle.appendChild(selectedCheck);
   tile.appendChild(selectedCircle);
 };
@@ -614,22 +610,20 @@
  * done button.
  * @param {?Element} tile The tile to remove styling from.
  */
-customBackgrounds.richerPicker_deselectTile = function(tile) {
+customize.richerPicker_deselectTile = function(tile) {
   if (!tile) {
     return;
   }
-  tile.parentElement.classList.toggle(
-      customBackgrounds.CLASSES.SELECTED, false);
-  $(customBackgrounds.IDS.MENU_DONE).disabled = true;
-  customBackgrounds.selectedTile = null;
-  $(customBackgrounds.IDS.MENU_DONE).tabIndex = -1;
+  tile.parentElement.classList.toggle(customize.CLASSES.SELECTED, false);
+  $(customize.IDS.MENU_DONE).disabled = true;
+  customize.selectedTile = null;
+  $(customize.IDS.MENU_DONE).tabIndex = -1;
 
   // Remove selected check and circle.
   for (let i = 0; i < tile.children.length; ++i) {
-    if (tile.children[i].classList.contains(
-            customBackgrounds.CLASSES.SELECTED_CHECK) ||
+    if (tile.children[i].classList.contains(customize.CLASSES.SELECTED_CHECK) ||
         tile.children[i].classList.contains(
-            customBackgrounds.CLASSES.SELECTED_CIRCLE)) {
+            customize.CLASSES.SELECTED_CIRCLE)) {
       tile.removeChild(tile.children[i]);
       --i;
     }
@@ -641,29 +635,29 @@
  * the done button.
  * @param {?Element} option The option to apply styling to.
  */
-customBackgrounds.richerPicker_selectShortcutOption = function(option) {
-  if (!option || customBackgrounds.selectedTile === option) {
+customize.richerPicker_selectShortcutOption = function(option) {
+  if (!option || customize.selectedTile === option) {
     return;  // The option has already been selected.
   }
   // Clear the previous selection, if any.
-  if (customBackgrounds.selectedTile) {
-    customBackgrounds.richerPicker_deselectTile(customBackgrounds.selectedTile);
+  if (customize.selectedTile) {
+    customize.richerPicker_deselectTile(customize.selectedTile);
   }
-  customBackgrounds.richerPicker_selectTile(option);
+  customize.richerPicker_selectTile(option);
 };
 
 /**
  * Apply border and checkmark when a tile is selected
  * @param {!Element} tile The tile to apply styling to.
  */
-customBackgrounds.applySelectedState = function(tile) {
-  tile.classList.add(customBackgrounds.CLASSES.COLLECTION_SELECTED);
+customize.applySelectedState = function(tile) {
+  tile.classList.add(customize.CLASSES.COLLECTION_SELECTED);
   const selectedBorder = document.createElement('div');
   const selectedCircle = document.createElement('div');
   const selectedCheck = document.createElement('div');
-  selectedBorder.classList.add(customBackgrounds.CLASSES.SELECTED_BORDER);
-  selectedCircle.classList.add(customBackgrounds.CLASSES.SELECTED_CIRCLE);
-  selectedCheck.classList.add(customBackgrounds.CLASSES.SELECTED_CHECK);
+  selectedBorder.classList.add(customize.CLASSES.SELECTED_BORDER);
+  selectedCircle.classList.add(customize.CLASSES.SELECTED_CIRCLE);
+  selectedCheck.classList.add(customize.CLASSES.SELECTED_CHECK);
   selectedBorder.appendChild(selectedCircle);
   selectedBorder.appendChild(selectedCheck);
   tile.appendChild(selectedBorder);
@@ -677,8 +671,8 @@
  * Remove border and checkmark when a tile is un-selected
  * @param {!Element} tile The tile to remove styling from.
  */
-customBackgrounds.removeSelectedState = function(tile) {
-  tile.classList.remove(customBackgrounds.CLASSES.COLLECTION_SELECTED);
+customize.removeSelectedState = function(tile) {
+  tile.classList.remove(customize.CLASSES.COLLECTION_SELECTED);
   tile.removeChild(tile.firstChild);
   tile.setAttribute('aria-label', tile.dataset.oldLabel);
 };
@@ -690,23 +684,21 @@
  * @param {string} dialogTitle The title to be displayed at the top of the
  *                 dialog.
  */
-customBackgrounds.showImageSelectionDialog = function(dialogTitle) {
-  const firstNTile = customBackgrounds.ROWS_TO_PRELOAD
-      * customBackgrounds.getTilesWide();
+customize.showImageSelectionDialog = function(dialogTitle) {
+  const firstNTile = customize.ROWS_TO_PRELOAD * customize.getTilesWide();
   const tileContainer = configData.richerPicker ?
-      $(customBackgrounds.IDS.BACKGROUNDS_IMAGE_MENU) :
-      $(customBackgrounds.IDS.TILES);
-  const menu = configData.richerPicker ?
-      $(customBackgrounds.IDS.CUSTOMIZATION_MENU) :
-      $(customBackgrounds.IDS.MENU);
+      $(customize.IDS.BACKGROUNDS_IMAGE_MENU) :
+      $(customize.IDS.TILES);
+  const menu = configData.richerPicker ? $(customize.IDS.CUSTOMIZATION_MENU) :
+                                         $(customize.IDS.MENU);
 
   if (configData.richerPicker) {
-    $(customBackgrounds.IDS.MENU_TITLE).textContent = dialogTitle;
-    menu.classList.toggle(customBackgrounds.CLASSES.ON_IMAGE_MENU, true);
+    $(customize.IDS.MENU_TITLE).textContent = dialogTitle;
+    menu.classList.toggle(customize.CLASSES.ON_IMAGE_MENU, true);
   } else {
-    $(customBackgrounds.IDS.TITLE).textContent = dialogTitle;
-    menu.classList.remove(customBackgrounds.CLASSES.COLLECTION_DIALOG);
-    menu.classList.add(customBackgrounds.CLASSES.IMAGE_DIALOG);
+    $(customize.IDS.TITLE).textContent = dialogTitle;
+    menu.classList.remove(customize.CLASSES.COLLECTION_DIALOG);
+    menu.classList.add(customize.CLASSES.IMAGE_DIALOG);
   }
 
   const preLoadTiles = [];
@@ -714,10 +706,9 @@
 
   for (let i = 0; i < collImg.length; ++i) {
     const tileBackground = document.createElement('div');
-    tileBackground.classList.add(
-        customBackgrounds.CLASSES.COLLECTION_TILE_BG);
+    tileBackground.classList.add(customize.CLASSES.COLLECTION_TILE_BG);
     const tile = document.createElement('div');
-    tile.classList.add(customBackgrounds.CLASSES.COLLECTION_TILE);
+    tile.classList.add(customize.CLASSES.COLLECTION_TILE);
     // Accessibility support for screen readers.
     tile.setAttribute('role', 'button');
 
@@ -753,35 +744,34 @@
     }
 
     const tileInteraction = function(tile) {
-      if (customBackgrounds.selectedTile) {
+      if (customize.selectedTile) {
         if (configData.richerPicker) {
-          const id = customBackgrounds.selectedTile.id;
-          customBackgrounds.richerPicker_deselectTile(
-              customBackgrounds.selectedTile);
+          const id = customize.selectedTile.id;
+          customize.richerPicker_deselectTile(customize.selectedTile);
           if (id === tile.id) {
             return;
           }
         } else {
-          customBackgrounds.removeSelectedState(customBackgrounds.selectedTile);
-          if (customBackgrounds.selectedTile.id === tile.id) {
-            customBackgrounds.unselectTile();
+          customize.removeSelectedState(customize.selectedTile);
+          if (customize.selectedTile.id === tile.id) {
+            customize.unselectTile();
             return;
           }
         }
       }
 
       if (configData.richerPicker) {
-        customBackgrounds.richerPicker_selectTile(tile);
+        customize.richerPicker_selectTile(tile);
       } else {
-        customBackgrounds.applySelectedState(tile);
-        customBackgrounds.selectedTile = tile;
+        customize.applySelectedState(tile);
+        customize.selectedTile = tile;
       }
 
-      $(customBackgrounds.IDS.DONE).tabIndex = 0;
+      $(customize.IDS.DONE).tabIndex = 0;
 
       // Turn toggle off when an image is selected.
-      $(customBackgrounds.IDS.DONE).disabled = false;
-      ntpApiHandle.logEvent(BACKGROUND_CUSTOMIZATION_LOG_TYPE
+      $(customize.IDS.DONE).disabled = false;
+      ntpApiHandle.logEvent(customize.BACKGROUND_CUSTOMIZATION_LOG_TYPE
                                 .NTP_CUSTOMIZE_CHROME_BACKGROUND_SELECT_IMAGE);
     };
 
@@ -791,9 +781,8 @@
       if (clickCount <= 1) {
         tileInteraction(event.currentTarget);
       } else if (
-          clickCount === 2 &&
-          customBackgrounds.selectedTile === event.currentTarget) {
-        customBackgrounds.setBackground(
+          clickCount === 2 && customize.selectedTile === event.currentTarget) {
+        customize.setBackground(
             event.currentTarget.dataset.url,
             event.currentTarget.dataset.attributionLine1,
             event.currentTarget.dataset.attributionLine2,
@@ -801,35 +790,34 @@
       }
     };
     tile.onkeydown = function(event) {
-
-      if (event.keyCode === customBackgrounds.KEYCODES.ENTER) {
+      if (event.keyCode === customize.KEYCODES.ENTER) {
         event.preventDefault();
         event.stopPropagation();
         tileInteraction(event.currentTarget);
       } else if (
-          event.keyCode === customBackgrounds.KEYCODES.LEFT ||
-          event.keyCode === customBackgrounds.KEYCODES.UP ||
-          event.keyCode === customBackgrounds.KEYCODES.RIGHT ||
-          event.keyCode === customBackgrounds.KEYCODES.DOWN) {
+          event.keyCode === customize.KEYCODES.LEFT ||
+          event.keyCode === customize.KEYCODES.UP ||
+          event.keyCode === customize.KEYCODES.RIGHT ||
+          event.keyCode === customize.KEYCODES.DOWN) {
         // Handle arrow key navigation.
         event.preventDefault();
         event.stopPropagation();
 
         let target = null;
-        if (event.keyCode == customBackgrounds.KEYCODES.LEFT) {
-          target = customBackgrounds.getNextTile(
+        if (event.keyCode == customize.KEYCODES.LEFT) {
+          target = customize.getNextTile(
               document.documentElement.classList.contains('rtl') ? 1 : -1, 0,
               event.currentTarget.dataset.tile_num);
-        } else if (event.keyCode == customBackgrounds.KEYCODES.UP) {
-          target = customBackgrounds.getNextTile(
+        } else if (event.keyCode == customize.KEYCODES.UP) {
+          target = customize.getNextTile(
               0, -1, event.currentTarget.dataset.tile_num);
-        } else if (event.keyCode == customBackgrounds.KEYCODES.RIGHT) {
-          target = customBackgrounds.getNextTile(
+        } else if (event.keyCode == customize.KEYCODES.RIGHT) {
+          target = customize.getNextTile(
               document.documentElement.classList.contains('rtl') ? -1 : 1, 0,
               event.currentTarget.dataset.tile_num);
-        } else if (event.keyCode == customBackgrounds.KEYCODES.DOWN) {
-          target = customBackgrounds.getNextTile(
-              0, 1, event.currentTarget.dataset.tile_num);
+        } else if (event.keyCode == customize.KEYCODES.DOWN) {
+          target =
+              customize.getNextTile(0, 1, event.currentTarget.dataset.tile_num);
         }
         if (target) {
           target.focus();
@@ -844,16 +832,17 @@
   }
   let tileGetsLoaded = 0;
   for (const tile of preLoadTiles) {
-    loadTile(tile, collImg, () => {
+    customize.loadTile(tile, collImg, () => {
       // After the preloaded tiles finish loading, the rest of the tiles start
       // loading.
       if (++tileGetsLoaded === preLoadTiles.length) {
-        postLoadTiles.forEach((tile) => loadTile(tile, collImg, null));
+        postLoadTiles.forEach(
+            (tile) => customize.loadTile(tile, collImg, null));
       }
     });
   }
 
-  $(customBackgrounds.IDS.TILES).focus();
+  $(customize.IDS.TILES).focus();
 };
 
 /**
@@ -864,16 +853,17 @@
  * @param {?Function} countLoad If not null, called after the tile finishes
  * loading.
  */
-const loadTile = function(tile, imageData, countLoad) {
+customize.loadTile = function(tile, imageData, countLoad) {
   if (imageData[tile.dataset.tile_num].collectionId === 'solidcolors') {
-    tile.style.backgroundImage = [customBackgrounds.CUSTOM_BACKGROUND_OVERLAY,
-      'url(' + imageData[tile.dataset.tile_num].thumbnailImageUrl + ')'].join(
-        ',').trim();
+    tile.style.backgroundImage = [
+      customize.CUSTOM_BACKGROUND_OVERLAY,
+      'url(' + imageData[tile.dataset.tile_num].thumbnailImageUrl + ')'
+    ].join(',').trim();
   } else {
     tile.style.backgroundImage =
         'url(' + imageData[tile.dataset.tile_num].thumbnailImageUrl + ')';
   }
-  fadeInImageTile(
+  customize.fadeInImageTile(
       tile, imageData[tile.dataset.tile_num].thumbnailImageUrl, countLoad);
 };
 
@@ -886,7 +876,7 @@
  * @param {?Function} countLoad If not null, called after the tile finishes
  * loading.
  */
-const fadeInImageTile = function(tile, imageUrl, countLoad) {
+customize.fadeInImageTile = function(tile, imageUrl, countLoad) {
   const image = new Image();
   image.onload = () => {
     tile.style.opacity = '1';
@@ -902,7 +892,7 @@
  * variable name "coll" which is a dict of background collections data.
  * @private
  */
-customBackgrounds.loadChromeBackgrounds = function() {
+customize.loadChromeBackgrounds = function() {
   const collElement = $('ntp-collection-loader');
   if (collElement) {
     collElement.parentNode.removeChild(collElement);
@@ -913,19 +903,19 @@
       'collection_type=background';
   collScript.onload = function() {
     if (configData.richerPicker) {
-      customBackgrounds.showCollectionSelectionDialog(
-          customBackgrounds.SOURCES.CHROME_BACKGROUNDS);
+      customize.showCollectionSelectionDialog(
+          customize.SOURCES.CHROME_BACKGROUNDS);
     }
   };
   document.body.appendChild(collScript);
 };
 
 /* Close dialog when an image is selected via the file picker. */
-customBackgrounds.closeCustomizationDialog = function() {
+customize.closeCustomizationDialog = function() {
   if (configData.richerPicker) {
-    $(customBackgrounds.IDS.CUSTOMIZATION_MENU).close();
+    $(customize.IDS.CUSTOMIZATION_MENU).close();
   } else {
-    $(customBackgrounds.IDS.EDIT_BG_DIALOG).close();
+    $(customize.IDS.EDIT_BG_DIALOG).close();
   }
 };
 
@@ -935,14 +925,14 @@
  * @param {number} current_index Index of the option the key press occurred on.
  * @param {number} deltaY Direction to search in, -1 for up, 1 for down.
  */
-customBackgrounds.getNextOption = function(current_index, deltaY) {
+customize.getNextOption = function(current_index, deltaY) {
   // Create array corresponding to the menu. Important that this is in the same
   // order as the MENU_ENTRIES enum, so we can index into it.
   const entries = [];
-  entries.push($(customBackgrounds.IDS.DEFAULT_WALLPAPERS));
-  entries.push($(customBackgrounds.IDS.UPLOAD_IMAGE));
-  entries.push($(customBackgrounds.IDS.CUSTOM_LINKS_RESTORE_DEFAULT));
-  entries.push($(customBackgrounds.IDS.RESTORE_DEFAULT));
+  entries.push($(customize.IDS.DEFAULT_WALLPAPERS));
+  entries.push($(customize.IDS.UPLOAD_IMAGE));
+  entries.push($(customize.IDS.CUSTOM_LINKS_RESTORE_DEFAULT));
+  entries.push($(customize.IDS.RESTORE_DEFAULT));
 
   let idx = current_index;
   do {
@@ -953,45 +943,45 @@
     if (idx === 4) {
       idx = 0;
     }
-  } while (idx !== current_index && (entries[idx].hidden ||
-           entries[idx].classList.contains(
-               customBackgrounds.CLASSES.OPTION_DISABLED)));
+  } while (
+      idx !== current_index &&
+      (entries[idx].hidden ||
+       entries[idx].classList.contains(customize.CLASSES.OPTION_DISABLED)));
   return entries[idx];
 };
 
 /* Hide custom background options based on the network state
  * @param {bool} online The current state of the network
  */
-customBackgrounds.networkStateChanged = function(online) {
-  $(customBackgrounds.IDS.DEFAULT_WALLPAPERS).hidden = !online;
+customize.networkStateChanged = function(online) {
+  $(customize.IDS.DEFAULT_WALLPAPERS).hidden = !online;
 };
 
 /**
  * Set customization menu to default options (custom backgrounds).
  */
-customBackgrounds.richerPicker_setCustomizationMenuToDefaultState = function() {
-  customBackgrounds.richerPicker_resetCustomizationMenu();
-  $(customBackgrounds.IDS.BACKGROUNDS_MENU)
-      .classList.toggle(customBackgrounds.CLASSES.MENU_SHOWN, true);
-  customBackgrounds.richerPicker_selectedOption =
-      $(customBackgrounds.IDS.BACKGROUNDS_BUTTON);
+customize.richerPicker_setCustomizationMenuToDefaultState = function() {
+  customize.richerPicker_resetCustomizationMenu();
+  $(customize.IDS.BACKGROUNDS_MENU)
+      .classList.toggle(customize.CLASSES.MENU_SHOWN, true);
+  customize.richerPicker_selectedOption = $(customize.IDS.BACKGROUNDS_BUTTON);
 };
 
 /**
  * Resets customization menu options.
  */
-customBackgrounds.richerPicker_resetCustomizationMenu = function() {
-  customBackgrounds.richerPicker_resetImageMenu(false);
-  $(customBackgrounds.IDS.BACKGROUNDS_MENU)
-      .classList.toggle(customBackgrounds.CLASSES.MENU_SHOWN, false);
-  $(customBackgrounds.IDS.SHORTCUTS_MENU)
-      .classList.toggle(customBackgrounds.CLASSES.MENU_SHOWN, false);
-  $(customBackgrounds.IDS.COLORS_MENU)
-      .classList.toggle(customBackgrounds.CLASSES.MENU_SHOWN, false);
-  if (customBackgrounds.richerPicker_selectedOption) {
-    customBackgrounds.richerPicker_selectedOption.classList.toggle(
-        customBackgrounds.CLASSES.SELECTED, false);
-    customBackgrounds.richerPicker_selectedOption = null;
+customize.richerPicker_resetCustomizationMenu = function() {
+  customize.richerPicker_resetImageMenu(false);
+  $(customize.IDS.BACKGROUNDS_MENU)
+      .classList.toggle(customize.CLASSES.MENU_SHOWN, false);
+  $(customize.IDS.SHORTCUTS_MENU)
+      .classList.toggle(customize.CLASSES.MENU_SHOWN, false);
+  $(customize.IDS.COLORS_MENU)
+      .classList.toggle(customize.CLASSES.MENU_SHOWN, false);
+  if (customize.richerPicker_selectedOption) {
+    customize.richerPicker_selectedOption.classList.toggle(
+        customize.CLASSES.SELECTED, false);
+    customize.richerPicker_selectedOption = null;
   }
 };
 
@@ -1004,67 +994,66 @@
  * @param {!Function} hideCustomLinkNotification Called when the custom link
  *                    notification should be hidden.
  */
-customBackgrounds.init = function(
-    showErrorNotification, hideCustomLinkNotification) {
+customize.init = function(showErrorNotification, hideCustomLinkNotification) {
   ntpApiHandle = window.chrome.embeddedSearch.newTabPage;
-  const editDialog = $(customBackgrounds.IDS.EDIT_BG_DIALOG);
-  const menu = $(customBackgrounds.IDS.MENU);
+  const editDialog = $(customize.IDS.EDIT_BG_DIALOG);
+  const menu = $(customize.IDS.MENU);
 
-  $(customBackgrounds.IDS.OPTIONS_TITLE).textContent =
+  $(customize.IDS.OPTIONS_TITLE).textContent =
       configData.translatedStrings.customizeBackground;
 
   // Store the main menu title so it can be restored if needed.
-  $(customBackgrounds.IDS.MENU_TITLE).dataset.mainTitle =
-      $(customBackgrounds.IDS.MENU_TITLE).textContent;
+  $(customize.IDS.MENU_TITLE).dataset.mainTitle =
+      $(customize.IDS.MENU_TITLE).textContent;
 
-  $(customBackgrounds.IDS.EDIT_BG_ICON)
+  $(customize.IDS.EDIT_BG_ICON)
       .setAttribute(
           'aria-label', configData.translatedStrings.customizeThisPage);
 
-  $(customBackgrounds.IDS.EDIT_BG_ICON)
+  $(customize.IDS.EDIT_BG_ICON)
       .setAttribute('title', configData.translatedStrings.customizeBackground);
 
   // Edit gear icon interaction events.
   const editBackgroundInteraction = function() {
     if (configData.richerPicker) {
-      customBackgrounds.richerPicker_setCustomizationMenuToDefaultState();
-      customBackgrounds.loadChromeBackgrounds();
-      $(customBackgrounds.IDS.CUSTOMIZATION_MENU).showModal();
+      customize.richerPicker_setCustomizationMenuToDefaultState();
+      customize.loadChromeBackgrounds();
+      $(customize.IDS.CUSTOMIZATION_MENU).showModal();
     } else {
       editDialog.showModal();
     }
   };
-  $(customBackgrounds.IDS.EDIT_BG).onclick = function(event) {
-    editDialog.classList.add(customBackgrounds.CLASSES.MOUSE_NAV);
+  $(customize.IDS.EDIT_BG).onclick = function(event) {
+    editDialog.classList.add(customize.CLASSES.MOUSE_NAV);
     editBackgroundInteraction();
   };
 
-  $(customBackgrounds.IDS.MENU_CANCEL).onclick = function(event) {
-    $(customBackgrounds.IDS.CUSTOMIZATION_MENU).close();
-    customBackgrounds.richerPicker_resetCustomizationMenu();
+  $(customize.IDS.MENU_CANCEL).onclick = function(event) {
+    $(customize.IDS.CUSTOMIZATION_MENU).close();
+    customize.richerPicker_resetCustomizationMenu();
   };
 
 
   // Find the first menu option that is not hidden or disabled.
   const findFirstMenuOption = () => {
-    const editMenu = $(customBackgrounds.IDS.EDIT_BG_MENU);
+    const editMenu = $(customize.IDS.EDIT_BG_MENU);
     for (let i = 1; i < editMenu.children.length; i++) {
       const option = editMenu.children[i];
-      if (option.classList.contains(customBackgrounds.CLASSES.OPTION)
-          && !option.hidden && !option.classList.contains(
-              customBackgrounds.CLASSES.OPTION_DISABLED)) {
+      if (option.classList.contains(customize.CLASSES.OPTION) &&
+          !option.hidden &&
+          !option.classList.contains(customize.CLASSES.OPTION_DISABLED)) {
         option.focus();
         return;
       }
     }
   };
 
-  $(customBackgrounds.IDS.EDIT_BG).onkeydown = function(event) {
-    if (event.keyCode === customBackgrounds.KEYCODES.ENTER ||
-        event.keyCode === customBackgrounds.KEYCODES.SPACE) {
+  $(customize.IDS.EDIT_BG).onkeydown = function(event) {
+    if (event.keyCode === customize.KEYCODES.ENTER ||
+        event.keyCode === customize.KEYCODES.SPACE) {
       // no default behavior for ENTER
       event.preventDefault();
-      editDialog.classList.remove(customBackgrounds.CLASSES.MOUSE_NAV);
+      editDialog.classList.remove(customize.CLASSES.MOUSE_NAV);
       editBackgroundInteraction();
       findFirstMenuOption();
     }
@@ -1075,39 +1064,39 @@
     editDialog.close();
   };
   editDialog.onclick = function(event) {
-    editDialog.classList.add(customBackgrounds.CLASSES.MOUSE_NAV);
+    editDialog.classList.add(customize.CLASSES.MOUSE_NAV);
     if (event.target === editDialog) {
       editDialogInteraction();
     }
   };
   editDialog.onkeydown = function(event) {
-    if (event.keyCode === customBackgrounds.KEYCODES.ESC) {
+    if (event.keyCode === customize.KEYCODES.ESC) {
       editDialogInteraction();
     } else if (
-        editDialog.classList.contains(customBackgrounds.CLASSES.MOUSE_NAV) &&
-        (event.keyCode === customBackgrounds.KEYCODES.TAB ||
-         event.keyCode === customBackgrounds.KEYCODES.UP ||
-         event.keyCode === customBackgrounds.KEYCODES.DOWN)) {
+        editDialog.classList.contains(customize.CLASSES.MOUSE_NAV) &&
+        (event.keyCode === customize.KEYCODES.TAB ||
+         event.keyCode === customize.KEYCODES.UP ||
+         event.keyCode === customize.KEYCODES.DOWN)) {
       // When using tab in mouse navigation mode, select the first option
       // available.
       event.preventDefault();
       findFirstMenuOption();
-      editDialog.classList.remove(customBackgrounds.CLASSES.MOUSE_NAV);
-    } else if (event.keyCode === customBackgrounds.KEYCODES.TAB) {
+      editDialog.classList.remove(customize.CLASSES.MOUSE_NAV);
+    } else if (event.keyCode === customize.KEYCODES.TAB) {
       // If keyboard navigation is attempted, remove mouse-only mode.
-      editDialog.classList.remove(customBackgrounds.CLASSES.MOUSE_NAV);
+      editDialog.classList.remove(customize.CLASSES.MOUSE_NAV);
     } else if (
-        event.keyCode === customBackgrounds.KEYCODES.LEFT ||
-        event.keyCode === customBackgrounds.KEYCODES.UP ||
-        event.keyCode === customBackgrounds.KEYCODES.RIGHT ||
-        event.keyCode === customBackgrounds.KEYCODES.DOWN) {
+        event.keyCode === customize.KEYCODES.LEFT ||
+        event.keyCode === customize.KEYCODES.UP ||
+        event.keyCode === customize.KEYCODES.RIGHT ||
+        event.keyCode === customize.KEYCODES.DOWN) {
       event.preventDefault();
-      editDialog.classList.remove(customBackgrounds.CLASSES.MOUSE_NAV);
+      editDialog.classList.remove(customize.CLASSES.MOUSE_NAV);
     }
   };
 
-  customBackgrounds.initCustomLinksItems(hideCustomLinkNotification);
-  customBackgrounds.initCustomBackgrounds(showErrorNotification);
+  customize.initCustomLinksItems(hideCustomLinkNotification);
+  customize.initCustomBackgrounds(showErrorNotification);
 };
 
 /**
@@ -1116,45 +1105,43 @@
  * @param {!Function} hideCustomLinkNotification Called when the custom link
  *                    notification should be hidden.
  */
-customBackgrounds.initCustomLinksItems = function(hideCustomLinkNotification) {
-  customBackgrounds.hideCustomLinkNotification = hideCustomLinkNotification;
+customize.initCustomLinksItems = function(hideCustomLinkNotification) {
+  customize.hideCustomLinkNotification = hideCustomLinkNotification;
 
-  const editDialog = $(customBackgrounds.IDS.EDIT_BG_DIALOG);
-  const menu = $(customBackgrounds.IDS.MENU);
+  const editDialog = $(customize.IDS.EDIT_BG_DIALOG);
+  const menu = $(customize.IDS.MENU);
 
-  $(customBackgrounds.IDS.CUSTOM_LINKS_RESTORE_DEFAULT_TEXT).textContent =
+  $(customize.IDS.CUSTOM_LINKS_RESTORE_DEFAULT_TEXT).textContent =
       configData.translatedStrings.restoreDefaultLinks;
 
   // Interactions with the "Restore default shortcuts" option.
   const customLinksRestoreDefaultInteraction = function() {
     editDialog.close();
-    customBackgrounds.hideCustomLinkNotification();
+    customize.hideCustomLinkNotification();
     window.chrome.embeddedSearch.newTabPage.resetCustomLinks();
-    ntpApiHandle.logEvent(BACKGROUND_CUSTOMIZATION_LOG_TYPE
+    ntpApiHandle.logEvent(customize.BACKGROUND_CUSTOMIZATION_LOG_TYPE
                               .NTP_CUSTOMIZE_RESTORE_SHORTCUTS_CLICKED);
   };
-  $(customBackgrounds.IDS.CUSTOM_LINKS_RESTORE_DEFAULT).onclick = () => {
-    if (!$(customBackgrounds.IDS.CUSTOM_LINKS_RESTORE_DEFAULT).classList.
-        contains(customBackgrounds.CLASSES.OPTION_DISABLED)) {
+  $(customize.IDS.CUSTOM_LINKS_RESTORE_DEFAULT).onclick = () => {
+    if (!$(customize.IDS.CUSTOM_LINKS_RESTORE_DEFAULT)
+             .classList.contains(customize.CLASSES.OPTION_DISABLED)) {
       customLinksRestoreDefaultInteraction();
     }
   };
-  $(customBackgrounds.IDS.CUSTOM_LINKS_RESTORE_DEFAULT).onkeydown = function(
-      event) {
-    if (event.keyCode === customBackgrounds.KEYCODES.ENTER) {
+  $(customize.IDS.CUSTOM_LINKS_RESTORE_DEFAULT).onkeydown = function(event) {
+    if (event.keyCode === customize.KEYCODES.ENTER) {
       customLinksRestoreDefaultInteraction();
-    } else if (event.keyCode === customBackgrounds.KEYCODES.UP) {
+    } else if (event.keyCode === customize.KEYCODES.UP) {
       // Handle arrow key navigation.
       event.preventDefault();
-      customBackgrounds
+      customize
           .getNextOption(
-              customBackgrounds.MENU_ENTRIES.CUSTOM_LINKS_RESTORE_DEFAULT, -1)
+              customize.MENU_ENTRIES.CUSTOM_LINKS_RESTORE_DEFAULT, -1)
           .focus();
-    } else if (event.keyCode === customBackgrounds.KEYCODES.DOWN) {
+    } else if (event.keyCode === customize.KEYCODES.DOWN) {
       event.preventDefault();
-      customBackgrounds
-          .getNextOption(
-              customBackgrounds.MENU_ENTRIES.CUSTOM_LINKS_RESTORE_DEFAULT, 1)
+      customize
+          .getNextOption(customize.MENU_ENTRIES.CUSTOM_LINKS_RESTORE_DEFAULT, 1)
           .focus();
     }
   };
@@ -1166,256 +1153,242 @@
  * @param {!Function} showErrorNotification Called when the error notification
  *                    should be displayed.
  */
-customBackgrounds.initCustomBackgrounds = function(showErrorNotification) {
-  customBackgrounds.showErrorNotification = showErrorNotification;
+customize.initCustomBackgrounds = function(showErrorNotification) {
+  customize.showErrorNotification = showErrorNotification;
 
-  const editDialog = $(customBackgrounds.IDS.EDIT_BG_DIALOG);
-  const menu = $(customBackgrounds.IDS.MENU);
+  const editDialog = $(customize.IDS.EDIT_BG_DIALOG);
+  const menu = $(customize.IDS.MENU);
 
-  $(customBackgrounds.IDS.DEFAULT_WALLPAPERS_TEXT).textContent =
+  $(customize.IDS.DEFAULT_WALLPAPERS_TEXT).textContent =
       configData.translatedStrings.defaultWallpapers;
-  $(customBackgrounds.IDS.UPLOAD_IMAGE_TEXT).textContent =
+  $(customize.IDS.UPLOAD_IMAGE_TEXT).textContent =
       configData.translatedStrings.uploadImage;
-  $(customBackgrounds.IDS.RESTORE_DEFAULT_TEXT).textContent =
+  $(customize.IDS.RESTORE_DEFAULT_TEXT).textContent =
       configData.translatedStrings.restoreDefaultBackground;
-  $(customBackgrounds.IDS.DONE).textContent =
+  $(customize.IDS.DONE).textContent =
       configData.translatedStrings.selectionDone;
-  $(customBackgrounds.IDS.CANCEL).textContent =
+  $(customize.IDS.CANCEL).textContent =
       configData.translatedStrings.selectionCancel;
 
   window.addEventListener('online', function(event) {
-    customBackgrounds.networkStateChanged(true);
+    customize.networkStateChanged(true);
   });
 
   window.addEventListener('offline', function(event) {
-    customBackgrounds.networkStateChanged(false);
+    customize.networkStateChanged(false);
   });
 
   if (!window.navigator.onLine) {
-    customBackgrounds.networkStateChanged(false);
+    customize.networkStateChanged(false);
   }
 
-  $(customBackgrounds.IDS.BACK_CIRCLE)
+  $(customize.IDS.BACK_CIRCLE)
       .setAttribute('aria-label', configData.translatedStrings.backLabel);
-  $(customBackgrounds.IDS.CANCEL)
+  $(customize.IDS.CANCEL)
       .setAttribute('aria-label', configData.translatedStrings.selectionCancel);
-  $(customBackgrounds.IDS.DONE)
+  $(customize.IDS.DONE)
       .setAttribute('aria-label', configData.translatedStrings.selectionDone);
 
-  $(customBackgrounds.IDS.DONE).disabled = true;
+  $(customize.IDS.DONE).disabled = true;
 
   // Interactions with the "Upload an image" option.
   const uploadImageInteraction = function() {
     window.chrome.embeddedSearch.newTabPage.selectLocalBackgroundImage();
-    ntpApiHandle.logEvent(
-        BACKGROUND_CUSTOMIZATION_LOG_TYPE.NTP_CUSTOMIZE_LOCAL_IMAGE_CLICKED);
+    ntpApiHandle.logEvent(customize.BACKGROUND_CUSTOMIZATION_LOG_TYPE
+                              .NTP_CUSTOMIZE_LOCAL_IMAGE_CLICKED);
   };
 
-  $(customBackgrounds.IDS.UPLOAD_IMAGE).onclick = (event) => {
-    if (!$(customBackgrounds.IDS.UPLOAD_IMAGE).classList.contains(
-        customBackgrounds.CLASSES.OPTION_DISABLED)) {
+  $(customize.IDS.UPLOAD_IMAGE).onclick = (event) => {
+    if (!$(customize.IDS.UPLOAD_IMAGE)
+             .classList.contains(customize.CLASSES.OPTION_DISABLED)) {
       uploadImageInteraction();
     }
   };
-  $(customBackgrounds.IDS.UPLOAD_IMAGE).onkeydown = function(event) {
-    if (event.keyCode === customBackgrounds.KEYCODES.ENTER) {
+  $(customize.IDS.UPLOAD_IMAGE).onkeydown = function(event) {
+    if (event.keyCode === customize.KEYCODES.ENTER) {
       uploadImageInteraction();
     }
 
     // Handle arrow key navigation.
-    if (event.keyCode === customBackgrounds.KEYCODES.UP) {
+    if (event.keyCode === customize.KEYCODES.UP) {
       event.preventDefault();
-      customBackgrounds
-          .getNextOption(customBackgrounds.MENU_ENTRIES.UPLOAD_IMAGE, -1)
-          .focus();
+      customize.getNextOption(customize.MENU_ENTRIES.UPLOAD_IMAGE, -1).focus();
     }
-    if (event.keyCode === customBackgrounds.KEYCODES.DOWN) {
+    if (event.keyCode === customize.KEYCODES.DOWN) {
       event.preventDefault();
-      customBackgrounds
-          .getNextOption(customBackgrounds.MENU_ENTRIES.UPLOAD_IMAGE, 1)
-          .focus();
+      customize.getNextOption(customize.MENU_ENTRIES.UPLOAD_IMAGE, 1).focus();
     }
   };
 
   // Interactions with the "Restore default background" option.
   const restoreDefaultInteraction = function() {
     editDialog.close();
-    customBackgrounds.clearAttribution();
+    customize.clearAttribution();
     window.chrome.embeddedSearch.newTabPage.setBackgroundURL('');
-    ntpApiHandle.logEvent(BACKGROUND_CUSTOMIZATION_LOG_TYPE
+    ntpApiHandle.logEvent(customize.BACKGROUND_CUSTOMIZATION_LOG_TYPE
                               .NTP_CUSTOMIZE_RESTORE_BACKGROUND_CLICKED);
   };
-  $(customBackgrounds.IDS.RESTORE_DEFAULT).onclick = (event) => {
-    if (!$(customBackgrounds.IDS.RESTORE_DEFAULT).classList.contains(
-        customBackgrounds.CLASSES.OPTION_DISABLED)) {
+  $(customize.IDS.RESTORE_DEFAULT).onclick = (event) => {
+    if (!$(customize.IDS.RESTORE_DEFAULT)
+             .classList.contains(customize.CLASSES.OPTION_DISABLED)) {
       restoreDefaultInteraction();
     }
   };
-  $(customBackgrounds.IDS.RESTORE_DEFAULT).onkeydown = function(event) {
-    if (event.keyCode === customBackgrounds.KEYCODES.ENTER) {
+  $(customize.IDS.RESTORE_DEFAULT).onkeydown = function(event) {
+    if (event.keyCode === customize.KEYCODES.ENTER) {
       restoreDefaultInteraction();
     }
 
     // Handle arrow key navigation.
-    if (event.keyCode === customBackgrounds.KEYCODES.UP) {
+    if (event.keyCode === customize.KEYCODES.UP) {
       event.preventDefault();
-      customBackgrounds
-          .getNextOption(customBackgrounds.MENU_ENTRIES.RESTORE_DEFAULT, -1)
+      customize.getNextOption(customize.MENU_ENTRIES.RESTORE_DEFAULT, -1)
           .focus();
     }
-    if (event.keyCode === customBackgrounds.KEYCODES.DOWN) {
+    if (event.keyCode === customize.KEYCODES.DOWN) {
       event.preventDefault();
-      customBackgrounds
-          .getNextOption(customBackgrounds.MENU_ENTRIES.RESTORE_DEFAULT, 1)
+      customize.getNextOption(customize.MENU_ENTRIES.RESTORE_DEFAULT, 1)
           .focus();
     }
   };
 
   // Interactions with the "Chrome backgrounds" option.
   const defaultWallpapersInteraction = function(event) {
-    customBackgrounds.loadChromeBackgrounds();
+    customize.loadChromeBackgrounds();
     $('ntp-collection-loader').onload = function() {
       editDialog.close();
       if (typeof coll != 'undefined' && coll.length > 0) {
-        customBackgrounds.showCollectionSelectionDialog(
-            customBackgrounds.SOURCES.CHROME_BACKGROUNDS);
+        customize.showCollectionSelectionDialog(
+            customize.SOURCES.CHROME_BACKGROUNDS);
       } else {
-        customBackgrounds.handleError(collErrors);
+        customize.handleError(collErrors);
       }
     };
-    ntpApiHandle.logEvent(BACKGROUND_CUSTOMIZATION_LOG_TYPE
+    ntpApiHandle.logEvent(customize.BACKGROUND_CUSTOMIZATION_LOG_TYPE
                               .NTP_CUSTOMIZE_CHROME_BACKGROUNDS_CLICKED);
   };
-  $(customBackgrounds.IDS.DEFAULT_WALLPAPERS).onclick = function(event) {
-    $(customBackgrounds.IDS.MENU)
-        .classList.add(customBackgrounds.CLASSES.MOUSE_NAV);
+  $(customize.IDS.DEFAULT_WALLPAPERS).onclick = function(event) {
+    $(customize.IDS.MENU).classList.add(customize.CLASSES.MOUSE_NAV);
     defaultWallpapersInteraction(event);
   };
-  $(customBackgrounds.IDS.DEFAULT_WALLPAPERS).onkeydown = function(event) {
-    if (event.keyCode === customBackgrounds.KEYCODES.ENTER) {
-      $(customBackgrounds.IDS.MENU)
-          .classList.remove(customBackgrounds.CLASSES.MOUSE_NAV);
+  $(customize.IDS.DEFAULT_WALLPAPERS).onkeydown = function(event) {
+    if (event.keyCode === customize.KEYCODES.ENTER) {
+      $(customize.IDS.MENU).classList.remove(customize.CLASSES.MOUSE_NAV);
       defaultWallpapersInteraction(event);
     }
 
     // Handle arrow key navigation.
-    if (event.keyCode === customBackgrounds.KEYCODES.UP) {
+    if (event.keyCode === customize.KEYCODES.UP) {
       event.preventDefault();
-      customBackgrounds
-          .getNextOption(customBackgrounds.MENU_ENTRIES.CHROME_BACKGROUNDS, -1)
+      customize.getNextOption(customize.MENU_ENTRIES.CHROME_BACKGROUNDS, -1)
           .focus();
     }
-    if (event.keyCode === customBackgrounds.KEYCODES.DOWN) {
+    if (event.keyCode === customize.KEYCODES.DOWN) {
       event.preventDefault();
-      customBackgrounds
-          .getNextOption(customBackgrounds.MENU_ENTRIES.CHROME_BACKGROUNDS, 1)
+      customize.getNextOption(customize.MENU_ENTRIES.CHROME_BACKGROUNDS, 1)
           .focus();
     }
   };
 
   // Escape and Backspace handling for the background picker dialog.
   menu.onkeydown = function(event) {
-    if (event.keyCode === customBackgrounds.KEYCODES.SPACE) {
-      $(customBackgrounds.IDS.TILES).scrollTop +=
-          $(customBackgrounds.IDS.TILES).offsetHeight;
+    if (event.keyCode === customize.KEYCODES.SPACE) {
+      $(customize.IDS.TILES).scrollTop += $(customize.IDS.TILES).offsetHeight;
       event.stopPropagation();
       event.preventDefault();
     }
-    if (event.keyCode === customBackgrounds.KEYCODES.ESC ||
-        event.keyCode === customBackgrounds.KEYCODES.BACKSPACE) {
+    if (event.keyCode === customize.KEYCODES.ESC ||
+        event.keyCode === customize.KEYCODES.BACKSPACE) {
       event.preventDefault();
       event.stopPropagation();
-      if (menu.classList.contains(
-              customBackgrounds.CLASSES.COLLECTION_DIALOG)) {
+      if (menu.classList.contains(customize.CLASSES.COLLECTION_DIALOG)) {
         menu.close();
-        customBackgrounds.resetSelectionDialog();
+        customize.resetSelectionDialog();
       } else {
-        customBackgrounds.resetSelectionDialog();
-        customBackgrounds.showCollectionSelectionDialog(
-            customBackgrounds.dialogCollectionsSource);
+        customize.resetSelectionDialog();
+        customize.showCollectionSelectionDialog(
+            customize.dialogCollectionsSource);
       }
     }
 
     // If keyboard navigation is attempted, remove mouse-only mode.
-    if (event.keyCode === customBackgrounds.KEYCODES.TAB ||
-        event.keyCode === customBackgrounds.KEYCODES.LEFT ||
-        event.keyCode === customBackgrounds.KEYCODES.UP ||
-        event.keyCode === customBackgrounds.KEYCODES.RIGHT ||
-        event.keyCode === customBackgrounds.KEYCODES.DOWN) {
-      menu.classList.remove(customBackgrounds.CLASSES.MOUSE_NAV);
+    if (event.keyCode === customize.KEYCODES.TAB ||
+        event.keyCode === customize.KEYCODES.LEFT ||
+        event.keyCode === customize.KEYCODES.UP ||
+        event.keyCode === customize.KEYCODES.RIGHT ||
+        event.keyCode === customize.KEYCODES.DOWN) {
+      menu.classList.remove(customize.CLASSES.MOUSE_NAV);
     }
   };
 
   // Interactions with the back arrow on the image selection dialog.
   const backInteraction = function(event) {
     if (configData.richerPicker) {
-      customBackgrounds.richerPicker_resetImageMenu(true);
+      customize.richerPicker_resetImageMenu(true);
     }
-    customBackgrounds.resetSelectionDialog();
-    customBackgrounds.showCollectionSelectionDialog(
-        customBackgrounds.dialogCollectionsSource);
+    customize.resetSelectionDialog();
+    customize.showCollectionSelectionDialog(customize.dialogCollectionsSource);
   };
-  $(customBackgrounds.IDS.BACK_CIRCLE).onclick = backInteraction;
-  $(customBackgrounds.IDS.MENU_BACK_CIRCLE).onclick = backInteraction;
-  $(customBackgrounds.IDS.BACK_CIRCLE).onkeyup = function(event) {
-    if (event.keyCode === customBackgrounds.KEYCODES.ENTER ||
-        event.keyCode === customBackgrounds.KEYCODES.SPACE) {
+  $(customize.IDS.BACK_CIRCLE).onclick = backInteraction;
+  $(customize.IDS.MENU_BACK_CIRCLE).onclick = backInteraction;
+  $(customize.IDS.BACK_CIRCLE).onkeyup = function(event) {
+    if (event.keyCode === customize.KEYCODES.ENTER ||
+        event.keyCode === customize.KEYCODES.SPACE) {
       backInteraction(event);
     }
   };
   // Pressing Spacebar on the back arrow shouldn't scroll the dialog.
-  $(customBackgrounds.IDS.BACK_CIRCLE).onkeydown = function(event) {
-    if (event.keyCode === customBackgrounds.KEYCODES.SPACE) {
+  $(customize.IDS.BACK_CIRCLE).onkeydown = function(event) {
+    if (event.keyCode === customize.KEYCODES.SPACE) {
       event.stopPropagation();
     }
   };
 
   // Interactions with the cancel button on the background picker dialog.
-  $(customBackgrounds.IDS.CANCEL).onclick = function(event) {
-    customBackgrounds.closeCollectionDialog(menu);
-    ntpApiHandle.logEvent(BACKGROUND_CUSTOMIZATION_LOG_TYPE
+  $(customize.IDS.CANCEL).onclick = function(event) {
+    customize.closeCollectionDialog(menu);
+    ntpApiHandle.logEvent(customize.BACKGROUND_CUSTOMIZATION_LOG_TYPE
                               .NTP_CUSTOMIZE_CHROME_BACKGROUND_CANCEL);
   };
-  $(customBackgrounds.IDS.CANCEL).onkeydown = function(event) {
-    if (event.keyCode === customBackgrounds.KEYCODES.ENTER ||
-        event.keyCode === customBackgrounds.KEYCODES.SPACE) {
-      customBackgrounds.closeCollectionDialog(menu);
-      ntpApiHandle.logEvent(BACKGROUND_CUSTOMIZATION_LOG_TYPE
+  $(customize.IDS.CANCEL).onkeydown = function(event) {
+    if (event.keyCode === customize.KEYCODES.ENTER ||
+        event.keyCode === customize.KEYCODES.SPACE) {
+      customize.closeCollectionDialog(menu);
+      ntpApiHandle.logEvent(customize.BACKGROUND_CUSTOMIZATION_LOG_TYPE
                                 .NTP_CUSTOMIZE_CHROME_BACKGROUND_CANCEL);
     }
   };
 
   // Interactions with the done button on the background picker dialog.
   const doneInteraction = function(event) {
-    const done = configData.richerPicker ? $(customBackgrounds.IDS.MENU_DONE) :
-                                           $(customBackgrounds.IDS.DONE);
+    const done = configData.richerPicker ? $(customize.IDS.MENU_DONE) :
+                                           $(customize.IDS.DONE);
     if (done.disabled) {
       return;
     }
-    customBackgrounds.setBackground(
-        customBackgrounds.selectedTile.dataset.url,
-        customBackgrounds.selectedTile.dataset.attributionLine1,
-        customBackgrounds.selectedTile.dataset.attributionLine2,
-        customBackgrounds.selectedTile.dataset.attributionActionUrl);
+    customize.setBackground(
+        customize.selectedTile.dataset.url,
+        customize.selectedTile.dataset.attributionLine1,
+        customize.selectedTile.dataset.attributionLine2,
+        customize.selectedTile.dataset.attributionActionUrl);
   };
-  $(customBackgrounds.IDS.DONE).onclick = doneInteraction;
-  $(customBackgrounds.IDS.MENU_DONE).onclick = doneInteraction;
-  $(customBackgrounds.IDS.DONE).onkeyup = function(event) {
-    if (event.keyCode === customBackgrounds.KEYCODES.ENTER) {
+  $(customize.IDS.DONE).onclick = doneInteraction;
+  $(customize.IDS.MENU_DONE).onclick = doneInteraction;
+  $(customize.IDS.DONE).onkeyup = function(event) {
+    if (event.keyCode === customize.KEYCODES.ENTER) {
       doneInteraction(event);
     }
   };
 
   // On any arrow key event in the tiles area, focus the first tile.
-  $(customBackgrounds.IDS.TILES).onkeydown = function(event) {
-    if (event.keyCode === customBackgrounds.KEYCODES.LEFT ||
-        event.keyCode === customBackgrounds.KEYCODES.UP ||
-        event.keyCode === customBackgrounds.KEYCODES.RIGHT ||
-        event.keyCode === customBackgrounds.KEYCODES.DOWN) {
+  $(customize.IDS.TILES).onkeydown = function(event) {
+    if (event.keyCode === customize.KEYCODES.LEFT ||
+        event.keyCode === customize.KEYCODES.UP ||
+        event.keyCode === customize.KEYCODES.RIGHT ||
+        event.keyCode === customize.KEYCODES.DOWN) {
       event.preventDefault();
-      if ($(customBackgrounds.IDS.MENU)
-              .classList.contains(
-                  customBackgrounds.CLASSES.COLLECTION_DIALOG)) {
+      if ($(customize.IDS.MENU)
+              .classList.contains(customize.CLASSES.COLLECTION_DIALOG)) {
         $('coll_tile_0').focus();
       } else {
         $('img_tile_0').focus();
@@ -1423,85 +1396,81 @@
     }
   };
 
-  $(customBackgrounds.IDS.BACKGROUNDS_UPLOAD).onclick = uploadImageInteraction;
-  $(customBackgrounds.IDS.BACKGROUNDS_UPLOAD).onkeydown = function(event) {
-    if (event.keyCode === customBackgrounds.KEYCODES.ENTER ||
-        event.keyCode === customBackgrounds.KEYCODES.SPACE) {
+  $(customize.IDS.BACKGROUNDS_UPLOAD).onclick = uploadImageInteraction;
+  $(customize.IDS.BACKGROUNDS_UPLOAD).onkeydown = function(event) {
+    if (event.keyCode === customize.KEYCODES.ENTER ||
+        event.keyCode === customize.KEYCODES.SPACE) {
       uploadImageInteraction();
     }
   };
 
-  $(customBackgrounds.IDS.BACKGROUNDS_DEFAULT).onclick = function() {
-    const tile = $(customBackgrounds.IDS.BACKGROUNDS_DEFAULT_ICON);
+  $(customize.IDS.BACKGROUNDS_DEFAULT).onclick = function() {
+    const tile = $(customize.IDS.BACKGROUNDS_DEFAULT_ICON);
     tile.dataset.url = '';
     tile.dataset.attributionLine1 = '';
     tile.dataset.attributionLine2 = '';
     tile.dataset.attributionActionUrl = '';
-    customBackgrounds.richerPicker_selectTile(tile);
+    customize.richerPicker_selectTile(tile);
   };
 
   const richerPickerOpenBackgrounds = function() {
-    customBackgrounds.richerPicker_resetCustomizationMenu();
-    customBackgrounds.richerPicker_selectMenuOption(
-        $(customBackgrounds.IDS.BACKGROUNDS_BUTTON),
-        $(customBackgrounds.IDS.BACKGROUNDS_MENU));
+    customize.richerPicker_resetCustomizationMenu();
+    customize.richerPicker_selectMenuOption(
+        $(customize.IDS.BACKGROUNDS_BUTTON), $(customize.IDS.BACKGROUNDS_MENU));
   };
 
-  $(customBackgrounds.IDS.BACKGROUNDS_BUTTON).onclick =
-      richerPickerOpenBackgrounds;
-  $(customBackgrounds.IDS.BACKGROUNDS_BUTTON).onkeydown = function(event) {
-    if (event.keyCode === customBackgrounds.KEYCODES.ENTER ||
-        event.keyCode === customBackgrounds.KEYCODES.SPACE) {
+  $(customize.IDS.BACKGROUNDS_BUTTON).onclick = richerPickerOpenBackgrounds;
+  $(customize.IDS.BACKGROUNDS_BUTTON).onkeydown = function(event) {
+    if (event.keyCode === customize.KEYCODES.ENTER ||
+        event.keyCode === customize.KEYCODES.SPACE) {
       richerPickerOpenBackgrounds();
     }
   };
 
-  const clOption = $(customBackgrounds.IDS.SHORTCUTS_OPTION_CUSTOM_LINKS);
+  const clOption = $(customize.IDS.SHORTCUTS_OPTION_CUSTOM_LINKS);
   clOption.onclick = function() {
-    customBackgrounds.richerPicker_selectShortcutOption(clOption);
+    customize.richerPicker_selectShortcutOption(clOption);
   };
   clOption.onkeydown = function(event) {
-    if (event.keyCode === customBackgrounds.KEYCODES.ENTER ||
-        event.keyCode === customBackgrounds.KEYCODES.SPACE) {
-      customBackgrounds.richerPicker_selectShortcutOption(clOption);
+    if (event.keyCode === customize.KEYCODES.ENTER ||
+        event.keyCode === customize.KEYCODES.SPACE) {
+      customize.richerPicker_selectShortcutOption(clOption);
     }
   };
 
-  const mvOption = $(customBackgrounds.IDS.SHORTCUTS_OPTION_MOST_VISITED);
+  const mvOption = $(customize.IDS.SHORTCUTS_OPTION_MOST_VISITED);
   mvOption.onclick = function() {
-    customBackgrounds.richerPicker_selectShortcutOption(mvOption);
+    customize.richerPicker_selectShortcutOption(mvOption);
   };
   mvOption.onkeydown = function(event) {
-    if (event.keyCode === customBackgrounds.KEYCODES.ENTER ||
-        event.keyCode === customBackgrounds.KEYCODES.SPACE) {
-      customBackgrounds.richerPicker_selectShortcutOption(mvOption);
+    if (event.keyCode === customize.KEYCODES.ENTER ||
+        event.keyCode === customize.KEYCODES.SPACE) {
+      customize.richerPicker_selectShortcutOption(mvOption);
     }
   };
 
   const richerPickerOpenShortcuts = function() {
-    customBackgrounds.richerPicker_resetCustomizationMenu();
-    customBackgrounds.richerPicker_selectMenuOption(
-        $(customBackgrounds.IDS.SHORTCUTS_BUTTON),
-        $(customBackgrounds.IDS.SHORTCUTS_MENU));
+    customize.richerPicker_resetCustomizationMenu();
+    customize.richerPicker_selectMenuOption(
+        $(customize.IDS.SHORTCUTS_BUTTON), $(customize.IDS.SHORTCUTS_MENU));
   };
 
-  $(customBackgrounds.IDS.SHORTCUTS_BUTTON).onclick = richerPickerOpenShortcuts;
-  $(customBackgrounds.IDS.SHORTCUTS_BUTTON).onkeydown = function(event) {
-    if (event.keyCode === customBackgrounds.KEYCODES.ENTER ||
-        event.keyCode === customBackgrounds.KEYCODES.SPACE) {
+  $(customize.IDS.SHORTCUTS_BUTTON).onclick = richerPickerOpenShortcuts;
+  $(customize.IDS.SHORTCUTS_BUTTON).onkeydown = function(event) {
+    if (event.keyCode === customize.KEYCODES.ENTER ||
+        event.keyCode === customize.KEYCODES.SPACE) {
       richerPickerOpenShortcuts();
     }
   };
 
-  $(customBackgrounds.IDS.COLORS_BUTTON).onclick = function() {
-    customBackgrounds.richerPicker_resetCustomizationMenu();
-    customBackgrounds.richerPicker_selectMenuOption(
-        $(customBackgrounds.IDS.COLORS_BUTTON),
-        $(customBackgrounds.IDS.COLORS_MENU));
+  $(customize.IDS.COLORS_BUTTON).onclick = function() {
+    customize.richerPicker_resetCustomizationMenu();
+    customize.richerPicker_selectMenuOption(
+        $(customize.IDS.COLORS_BUTTON), $(customize.IDS.COLORS_MENU));
   };
 };
 
-customBackgrounds.handleError = function(errors) {
+customize.handleError = function(errors) {
   const unavailableString = configData.translatedStrings.backgroundsUnavailable;
 
   if (errors != 'undefined') {
@@ -1513,19 +1482,19 @@
               'https://chrome://network-error/' + errors.net_error_no,
               '_blank');
         };
-        customBackgrounds.showErrorNotification(
+        customize.showErrorNotification(
             configData.translatedStrings.connectionError,
             configData.translatedStrings.moreInfo, onClick);
       } else {
-        customBackgrounds.showErrorNotification(
+        customize.showErrorNotification(
             configData.translatedStrings.connectionErrorNoPeriod);
       }
     } else if (errors.service_error) {  // Service errors.
-      customBackgrounds.showErrorNotification(unavailableString);
+      customize.showErrorNotification(unavailableString);
     }
     return;
   }
 
   // Generic error when we can't tell what went wrong.
-  customBackgrounds.showErrorNotification(unavailableString);
+  customize.showErrorNotification(unavailableString);
 };
diff --git a/chrome/browser/resources/local_ntp/local_ntp.js b/chrome/browser/resources/local_ntp/local_ntp.js
index c1a8d7c..d80140b 100644
--- a/chrome/browser/resources/local_ntp/local_ntp.js
+++ b/chrome/browser/resources/local_ntp/local_ntp.js
@@ -427,12 +427,12 @@
 
   if (info.customBackgroundConfigured) {
     const imageWithOverlay = [
-      customBackgrounds.CUSTOM_BACKGROUND_OVERLAY, 'url(' + info.imageUrl + ')'
+      customize.CUSTOM_BACKGROUND_OVERLAY, 'url(' + info.imageUrl + ')'
     ].join(',').trim();
 
     if (imageWithOverlay != $(IDS.CUSTOM_BG).style.backgroundImage) {
-      customBackgrounds.closeCustomizationDialog();
-      customBackgrounds.clearAttribution();
+      customize.closeCustomizationDialog();
+      customize.clearAttribution();
     }
 
     // |image| and |imageWithOverlay| use the same url as their source. Waiting
@@ -445,32 +445,31 @@
     };
     image.src = info.imageUrl;
 
-    customBackgrounds.setAttribution(
+    customize.setAttribution(
         info.attribution1, info.attribution2, info.attributionActionUrl);
   } else {
     $(IDS.CUSTOM_BG).style.opacity = '0';
     window.setTimeout(function() {
       $(IDS.CUSTOM_BG).style.backgroundImage = '';
     }, 1000);
-    customBackgrounds.clearAttribution();
+    customize.clearAttribution();
   }
 
-  $(customBackgrounds.IDS.RESTORE_DEFAULT)
+  $(customize.IDS.RESTORE_DEFAULT)
       .classList.toggle(
-          customBackgrounds.CLASSES.OPTION_DISABLED,
-          !info.customBackgroundConfigured);
-  $(customBackgrounds.IDS.RESTORE_DEFAULT).tabIndex =
+          customize.CLASSES.OPTION_DISABLED, !info.customBackgroundConfigured);
+  $(customize.IDS.RESTORE_DEFAULT).tabIndex =
       (info.customBackgroundConfigured ? 0 : -1);
 
-  $(customBackgrounds.IDS.EDIT_BG)
+  $(customize.IDS.EDIT_BG)
       .classList.toggle(
-          customBackgrounds.CLASSES.ENTRY_POINT_ENHANCED,
+          customize.CLASSES.ENTRY_POINT_ENHANCED,
           !info.customBackgroundConfigured);
 
   if (configData.isGooglePage) {
     // Hide the settings menu or individual options if the related features are
     // disabled.
-    customBackgrounds.setMenuVisibility();
+    customize.setMenuVisibility();
   }
 }
 
@@ -1013,11 +1012,10 @@
         $(IDS.PROMO).classList.add(CLASSES.SHOW_ELEMENT);
       }
       if (!configData.hideShortcuts) {
-        $(customBackgrounds.IDS.CUSTOM_LINKS_RESTORE_DEFAULT)
+        $(customize.IDS.CUSTOM_LINKS_RESTORE_DEFAULT)
             .classList.toggle(
-                customBackgrounds.CLASSES.OPTION_DISABLED,
-                !args.showRestoreDefault);
-        $(customBackgrounds.IDS.CUSTOM_LINKS_RESTORE_DEFAULT).tabIndex =
+                customize.CLASSES.OPTION_DISABLED, !args.showRestoreDefault);
+        $(customize.IDS.CUSTOM_LINKS_RESTORE_DEFAULT).tabIndex =
             (args.showRestoreDefault ? 0 : -1);
       }
       $(IDS.OGB).classList.add(CLASSES.SHOW_ELEMENT);
@@ -1148,7 +1146,7 @@
     ntpApiHandle.onupdatecustomlinkdone = onUpdateCustomLinkDone;
     ntpApiHandle.ondeletecustomlinkdone = onDeleteCustomLinkDone;
 
-    customBackgrounds.init(showErrorNotification, hideNotification);
+    customize.init(showErrorNotification, hideNotification);
 
     if (configData.alternateFakebox) {
       document.body.classList.add(CLASSES.ALTERNATE_FAKEBOX);
@@ -1234,7 +1232,7 @@
 
     doodles.init();
 
-    $(customBackgrounds.IDS.EDIT_BG_TEXT).textContent =
+    $(customize.IDS.EDIT_BG_TEXT).textContent =
         configData.translatedStrings.customizeButtonLabel;
   } else {
     document.body.classList.add(CLASSES.NON_GOOGLE_PAGE);
@@ -1256,7 +1254,7 @@
   }
 
   utils.setPlatformClass(document.body);
-  utils.disableOutlineOnMouseClick($(customBackgrounds.IDS.EDIT_BG));
+  utils.disableOutlineOnMouseClick($(customize.IDS.EDIT_BG));
   document.body.classList.add(CLASSES.INITED);
 }
 
@@ -1405,6 +1403,11 @@
       ntpApiHandle.logEvent(LOG_TYPE.NTP_MIDDLE_SLOT_PROMO_LINK_CLICKED);
     };
   }
+
+  // The the MV tiles are already loaded show the promo immediately.
+  if (tilesAreLoaded) {
+    promoContainer.classList.add(CLASSES.SHOW_ELEMENT);
+  }
 }
 
 
diff --git a/chrome/browser/resources/local_ntp/most_visited_single.js b/chrome/browser/resources/local_ntp/most_visited_single.js
index 23055b8..8feca592 100644
--- a/chrome/browser/resources/local_ntp/most_visited_single.js
+++ b/chrome/browser/resources/local_ntp/most_visited_single.js
@@ -82,12 +82,6 @@
 const LOG_TYPE = {
   // All NTP tiles have finished loading (successfully or failing).
   NTP_ALL_TILES_LOADED: 11,
-  // The data for all NTP tiles (title, URL, etc, but not the thumbnail image)
-  // has been received. In contrast to NTP_ALL_TILES_LOADED, this is recorded
-  // before the actual DOM elements have loaded (in particular the thumbnail
-  // images).
-  NTP_ALL_TILES_RECEIVED: 12,
-
   // Shortcuts have been customized.
   NTP_SHORTCUT_CUSTOMIZED: 39,
   // The 'Add shortcut' link was clicked.
@@ -863,7 +857,6 @@
  * @param {!Object} info Data received in the message.
  */
 function showTiles(info) {
-  logEvent(LOG_TYPE.NTP_ALL_TILES_RECEIVED);
   utils.setPlatformClass(document.body);
   countLoad();
 }
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/module_icons/google_dark.svg b/chrome/browser/resources/welcome/onboarding_welcome/images/module_icons/google_dark.svg
deleted file mode 100644
index 40991d56..0000000
--- a/chrome/browser/resources/welcome/onboarding_welcome/images/module_icons/google_dark.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg width="42" height="38" xmlns="http://www.w3.org/2000/svg"><g transform="translate(-2 -2)" fill="none" fill-rule="evenodd"><path d="M31.235 26.057l11.432-.566a1.157 1.157 0 0 1 1.157 1.16 1.142 1.142 0 0 1-.128.515L38.52 37.192a1.174 1.174 0 0 1-1.977.163l-6.257-9.461a1.144 1.144 0 0 1 .247-1.612c.203-.148.45-.227.702-.225z" fill="#EE675C"/><path d="M23.239 25.28v-4.464h11.455c.171.756.306 1.464.306 2.46C35 30.128 30.313 35 23.25 35 16.484 35 11 29.624 11 23s5.483-12 12.239-12c3.304 0 6.07 1.188 8.187 3.132l-3.475 3.312c-.882-.816-2.411-1.788-4.712-1.788-4.051 0-7.356 3.3-7.356 7.344 0 4.044 3.305 7.344 7.356 7.344 4.687 0 6.413-3.18 6.73-5.064h-6.73z" fill="#8AB4F8" fill-rule="nonzero"/><path d="M7 7h32v32H7z"/><circle fill="#81C995" cx="10" cy="37.398" r="2"/><path d="M6.592 5.039c4.904-4.183 12.017-3.946 16.689.284.464.42 1.274 1.412-.187 2.658L6.321 22.291c-1.324 1.13-1.914.198-2.202-.254C.655 16.597 1.616 9.284 6.592 5.039z" fill="#FDD663" style="mix-blend-mode:screen"/></g></svg>
\ No newline at end of file
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/module_icons/google_light.svg b/chrome/browser/resources/welcome/onboarding_welcome/images/module_icons/google_light.svg
deleted file mode 100644
index a0bed3b..0000000
--- a/chrome/browser/resources/welcome/onboarding_welcome/images/module_icons/google_light.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg width="42" height="38" xmlns="http://www.w3.org/2000/svg"><g transform="translate(-2 -2)" fill="none" fill-rule="evenodd"><path d="M31.235 26.057l11.432-.566a1.157 1.157 0 0 1 1.157 1.16 1.142 1.142 0 0 1-.128.515L38.52 37.192a1.174 1.174 0 0 1-1.977.163l-6.257-9.461a1.144 1.144 0 0 1 .247-1.612c.203-.148.45-.227.702-.225z" fill="#E74133"/><path d="M23.239 25.678v-4.464h11.455c.171.756.306 1.464.306 2.46 0 6.852-4.687 11.724-11.75 11.724-6.767 0-12.25-5.376-12.25-12s5.483-12 12.239-12c3.304 0 6.07 1.188 8.187 3.132l-3.475 3.312c-.882-.816-2.411-1.788-4.712-1.788-4.051 0-7.356 3.3-7.356 7.344 0 4.044 3.305 7.344 7.356 7.344 4.687 0 6.413-3.18 6.73-5.064h-6.73z" fill="#1A73E8" fill-rule="nonzero"/><path d="M7 7.398h32v32H7z"/><circle fill="#31A753" cx="10" cy="37.398" r="2"/><path d="M6.592 5.039c4.904-4.183 12.017-3.946 16.689.284.464.42 1.274 1.412-.187 2.658L6.321 22.291c-1.324 1.13-1.914.198-2.202-.254C.655 16.597 1.616 9.284 6.592 5.039z" fill="#FACF4C" style="mix-blend-mode:multiply"/></g></svg>
\ No newline at end of file
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/module_icons/set_default_dark.svg b/chrome/browser/resources/welcome/onboarding_welcome/images/module_icons/set_default_dark.svg
deleted file mode 100644
index 414dff5..0000000
--- a/chrome/browser/resources/welcome/onboarding_welcome/images/module_icons/set_default_dark.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg width="44" height="39" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path d="M14 3C7.929 3 3 7.929 3 14s4.929 11 11 11 11-4.929 11-11S20.071 3 14 3zm0-3c7.728 0 14 6.272 14 14s-6.272 14-14 14S0 21.728 0 14 6.272 0 14 0z" id="a"/><path d="M14 .667C21.36.667 27.333 6.64 27.333 14S21.36 27.333 14 27.333.667 21.36.667 14 6.64.667 14 .667zM3.333 14H9.2c4.543.029 6.562 2.308 6.058 6.836H10.65v3.293c1.054.349 2.18.538 3.35.538 5.887 0 10.667-4.78 10.667-10.667 0-.217-.007-.433-.02-.647-.876 1.32-2.203 1.98-3.98 1.98-2.85 0-4.275-1.222-4.275-3.665h-4.998c-.365-3.638.91-5.457 3.827-5.457 0-1.3.436-2.13 1.082-2.628A10.69 10.69 0 0 0 14 3.333C8.113 3.333 3.333 8.113 3.333 14z" id="c"/></defs><g transform="translate(-2 -2)" fill="none" fill-rule="evenodd"><path d="M33.235 28.659l11.432-.566a1.157 1.157 0 0 1 1.157 1.16 1.142 1.142 0 0 1-.128.515L40.52 39.794a1.174 1.174 0 0 1-1.977.163l-6.257-9.461a1.144 1.144 0 0 1 .247-1.612c.203-.148.45-.227.702-.225z" fill="#EE675C"/><g transform="translate(11 9)"><mask id="b" fill="#fff"><use xlink:href="#a"/></mask><use fill="#000" fill-rule="nonzero" xlink:href="#a"/><g mask="url(#b)" fill="#1A73E8"><path d="M-2-2h32v32H-2z"/></g><use fill="#8AB4F8" fill-rule="nonzero" xlink:href="#c"/></g><circle fill="#81C995" cx="9" cy="38.398" r="2"/><path d="M6.592 5.039c4.904-4.183 12.017-3.946 16.689.284.464.42 1.274 1.412-.187 2.658L6.321 22.291c-1.324 1.13-1.914.198-2.202-.254C.655 16.597 1.616 9.284 6.592 5.039z" fill="#FDDF8B" style="mix-blend-mode:screen"/></g></svg>
\ No newline at end of file
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/module_icons/set_default_light.svg b/chrome/browser/resources/welcome/onboarding_welcome/images/module_icons/set_default_light.svg
deleted file mode 100644
index 2a0c6eb..0000000
--- a/chrome/browser/resources/welcome/onboarding_welcome/images/module_icons/set_default_light.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg width="44" height="39" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path d="M16 5C9.929 5 5 9.929 5 16s4.929 11 11 11 11-4.929 11-11S22.071 5 16 5zm0-3c7.728 0 14 6.272 14 14s-6.272 14-14 14S2 23.728 2 16 8.272 2 16 2z" id="a"/><path d="M16 2.667c7.36 0 13.333 5.973 13.333 13.333S23.36 29.333 16 29.333 2.667 23.36 2.667 16 8.64 2.667 16 2.667zM5.333 16H11.2c4.543.029 6.562 2.308 6.058 6.836H12.65v3.293c1.054.349 2.18.538 3.35.538 5.887 0 10.667-4.78 10.667-10.667 0-.217-.007-.433-.02-.647-.876 1.32-2.203 1.98-3.98 1.98-2.85 0-4.275-1.222-4.275-3.665h-4.998c-.365-3.638.91-5.457 3.827-5.457 0-1.3.436-2.13 1.082-2.628A10.69 10.69 0 0 0 16 5.333c-5.887 0-10.667 4.78-10.667 10.667z" id="c"/></defs><g transform="translate(-2 -2)" fill="none" fill-rule="evenodd"><path d="M33.235 28.659l11.432-.566a1.157 1.157 0 0 1 1.157 1.16 1.142 1.142 0 0 1-.128.515L40.52 39.794a1.174 1.174 0 0 1-1.977.163l-6.257-9.461a1.144 1.144 0 0 1 .247-1.612c.203-.148.45-.227.702-.225z" fill="#E74133"/><g transform="translate(9 7.398)"><mask id="b" fill="#fff"><use xlink:href="#a"/></mask><use fill="#000" fill-rule="nonzero" xlink:href="#a"/><g mask="url(#b)" fill="#1A73E8"><path d="M0 0h32v32H0z"/></g><mask id="d" fill="#fff"><use xlink:href="#c"/></mask><use fill="#000" fill-rule="nonzero" xlink:href="#c"/><g mask="url(#d)" fill="#1A73E8"><path d="M0 0h32v32H0z"/></g></g><circle fill="#31A753" cx="9" cy="38.398" r="2"/><path d="M6.592 5.039c4.904-4.183 12.017-3.946 16.689.284.464.42 1.274 1.412-.187 2.658L6.321 22.291c-1.324 1.13-1.914.198-2.202-.254C.655 16.597 1.616 9.284 6.592 5.039z" fill="#FACF4C" style="mix-blend-mode:multiply"/></g></svg>
\ No newline at end of file
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/module_icons/wallpaper_dark.svg b/chrome/browser/resources/welcome/onboarding_welcome/images/module_icons/wallpaper_dark.svg
deleted file mode 100644
index 45cfa17..0000000
--- a/chrome/browser/resources/welcome/onboarding_welcome/images/module_icons/wallpaper_dark.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg width="44" height="39" xmlns="http://www.w3.org/2000/svg"><g transform="translate(-2 -2)" fill="none" fill-rule="evenodd"><path d="M33.235 28.659l11.432-.566a1.157 1.157 0 0 1 1.157 1.16 1.142 1.142 0 0 1-.128.515L40.52 39.794a1.174 1.174 0 0 1-1.977.163l-6.257-9.461a1.144 1.144 0 0 1 .247-1.612c.203-.148.45-.227.702-.225z" fill="#EE675C"/><circle fill="#31A753" cx="9" cy="38.398" r="2"/><path d="M9 7h32v32H9z"/><path d="M34.333 13.667v18.666H15.667V13.667h18.666zm0-2.667H15.667A2.675 2.675 0 0 0 13 13.667v18.666C13 33.8 14.2 35 15.667 35h18.666C35.8 35 37 33.8 37 32.333V13.667C37 12.2 35.8 11 34.333 11zm-6.48 11.813l-4 5.16L21 24.52l-4 5.147h16l-5.147-6.854z" fill="#8AB4F8" fill-rule="nonzero"/><path d="M6.592 5.039c4.904-4.183 12.017-3.946 16.689.284.464.42 1.274 1.412-.187 2.658L6.321 22.291c-1.324 1.13-1.914.198-2.202-.254C.655 16.597 1.616 9.284 6.592 5.039z" fill="#FDDB79" style="mix-blend-mode:screen"/></g></svg>
\ No newline at end of file
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/module_icons/wallpaper_light.svg b/chrome/browser/resources/welcome/onboarding_welcome/images/module_icons/wallpaper_light.svg
deleted file mode 100644
index 300ae22..0000000
--- a/chrome/browser/resources/welcome/onboarding_welcome/images/module_icons/wallpaper_light.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg width="44" height="39" xmlns="http://www.w3.org/2000/svg"><g transform="translate(-2 -2)" fill="none" fill-rule="evenodd"><path d="M33.235 28.659l11.432-.566a1.157 1.157 0 0 1 1.157 1.16 1.142 1.142 0 0 1-.128.515L40.52 39.794a1.174 1.174 0 0 1-1.977.163l-6.257-9.461a1.144 1.144 0 0 1 .247-1.612c.203-.148.45-.227.702-.225z" fill="#E74133"/><circle fill="#31A753" cx="9" cy="38.398" r="2"/><path d="M6.592 5.039c4.904-4.183 12.017-3.946 16.689.284.464.42 1.274 1.412-.187 2.658L6.321 22.291c-1.324 1.13-1.914.198-2.202-.254C.655 16.597 1.616 9.284 6.592 5.039z" fill="#FACF4C" style="mix-blend-mode:multiply"/><path d="M9 7h32v32H9z"/><path d="M34.333 13.667v18.666H15.667V13.667h18.666zm0-2.667H15.667A2.675 2.675 0 0 0 13 13.667v18.666C13 33.8 14.2 35 15.667 35h18.666C35.8 35 37 33.8 37 32.333V13.667C37 12.2 35.8 11 34.333 11zm-6.48 11.813l-4 5.16L21 24.52l-4 5.147h16l-5.147-6.854z" fill="#1A73E8" fill-rule="nonzero"/></g></svg>
\ No newline at end of file
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/ntp_thumbnails/art.jpg b/chrome/browser/resources/welcome/onboarding_welcome/images/ntp_thumbnails/art.jpg
deleted file mode 100644
index 2514b17e..0000000
--- a/chrome/browser/resources/welcome/onboarding_welcome/images/ntp_thumbnails/art.jpg
+++ /dev/null
Binary files differ
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/ntp_thumbnails/cityscape.jpg b/chrome/browser/resources/welcome/onboarding_welcome/images/ntp_thumbnails/cityscape.jpg
deleted file mode 100644
index 4c36cf1..0000000
--- a/chrome/browser/resources/welcome/onboarding_welcome/images/ntp_thumbnails/cityscape.jpg
+++ /dev/null
Binary files differ
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/ntp_thumbnails/earth.jpg b/chrome/browser/resources/welcome/onboarding_welcome/images/ntp_thumbnails/earth.jpg
deleted file mode 100644
index c4cbdc6..0000000
--- a/chrome/browser/resources/welcome/onboarding_welcome/images/ntp_thumbnails/earth.jpg
+++ /dev/null
Binary files differ
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/ntp_thumbnails/geometric_shapes.jpg b/chrome/browser/resources/welcome/onboarding_welcome/images/ntp_thumbnails/geometric_shapes.jpg
deleted file mode 100644
index 6092436..0000000
--- a/chrome/browser/resources/welcome/onboarding_welcome/images/ntp_thumbnails/geometric_shapes.jpg
+++ /dev/null
Binary files differ
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/ntp_thumbnails/landscape.jpg b/chrome/browser/resources/welcome/onboarding_welcome/images/ntp_thumbnails/landscape.jpg
deleted file mode 100644
index 340ddff..0000000
--- a/chrome/browser/resources/welcome/onboarding_welcome/images/ntp_thumbnails/landscape.jpg
+++ /dev/null
Binary files differ
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/set_default_dark.svg b/chrome/browser/resources/welcome/onboarding_welcome/images/set_default_dark.svg
deleted file mode 100644
index c17a6050..0000000
--- a/chrome/browser/resources/welcome/onboarding_welcome/images/set_default_dark.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg width="454" height="185" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path id="a" d="M.707.43h56.32v56.32H.707z"/><path d="M7.527 0C3.668 0 .528 3.14.528 7v126c0 3.86 3.14 7 6.999 7h213c3.86 0 7-3.14 7-7V7c0-3.86-3.14-7-7-7" id="c"/><path d="M7.527 0C3.668 0 .528 3.14.528 7v126c0 3.86 3.14 7 6.999 7h213c3.86 0 7-3.14 7-7V7c0-3.86-3.14-7-7-7h-213z" id="e"/><path d="M7.527 0C3.668 0 .528 3.14.528 7v126c0 3.86 3.14 7 6.999 7h213c3.86 0 7-3.14 7-7V7c0-3.86-3.14-7-7-7" id="g"/><path d="M7.527 0C3.668 0 .528 3.14.528 7v126c0 3.86 3.14 7 6.999 7h213c3.86 0 7-3.14 7-7V7c0-3.86-3.14-7-7-7" id="i"/></defs><g fill="none" fill-rule="evenodd"><path d="M292.172 9.494l-38.26 10.251c-2.134.572-2.848 3.24-1.286 4.802l28.008 28.008c1.562 1.562 4.23.847 4.802-1.287l10.252-38.259c.57-2.134-1.382-4.087-3.516-3.515l-38.26 10.251c-2.134.572-2.848 3.24-1.286 4.802l28.008 28.008c1.562 1.562 4.23.847 4.802-1.287l10.252-38.259c.57-2.134-1.382-4.087-3.516-3.515" fill="#F1F3F4"/><path d="M90.398 148.926c-1.172.677-1.51 2.225-.714 3.32 7.039 9.677 20.395 12.674 30.99 6.557 10.595-6.117 14.678-19.182 9.815-30.117-.549-1.237-2.059-1.718-3.231-1.04l-36.86 21.28zM359.03 138.41l-28.754 28.755a3.115 3.115 0 0 1-4.406 0l-28.754-28.754a3.115 3.115 0 0 1 0-4.406l28.754-28.754a3.117 3.117 0 0 1 4.406 0l28.754 28.754a3.115 3.115 0 0 1 0 4.406" fill="#3C4043"/><g transform="translate(116 -.43)"><mask id="b" fill="#fff"><use xlink:href="#a"/></mask><path d="M57.027 28.59c0 15.553-12.607 28.16-28.16 28.16-15.552 0-28.16-12.607-28.16-28.16C.707 13.038 13.315.43 28.867.43c15.553 0 28.16 12.608 28.16 28.16" fill="#3C4043" mask="url(#b)"/></g><path d="M112.527 168.57c-3.859 0-7-3.14-7-7v-126c0-3.86 3.141-7 7-7h213c3.86 0 7 3.14 7 7v126c0 3.86-3.14 7-7 7h-213z" fill="#202124"/><g><g transform="translate(105 28.57)"><mask id="d" fill="#fff"><use xlink:href="#c"/></mask><path d="M186.408-14.069L152.47-4.976c-1.892.508-2.527 2.874-1.14 4.26l24.843 24.844c1.386 1.386 3.753.751 4.26-1.142l9.093-33.937c.507-1.893-1.225-3.625-3.118-3.118L152.47-4.976c-1.892.508-2.527 2.874-1.14 4.26l24.843 24.844c1.386 1.386 3.753.751 4.26-1.142l9.093-33.937c.507-1.893-1.225-3.625-3.118-3.118" fill="#E74133" mask="url(#d)"/></g><g transform="translate(105 28.57)"><mask id="f" fill="#fff"><use xlink:href="#e"/></mask><path d="M-15.803 120.8c-1.191.688-1.535 2.261-.726 3.373 7.154 9.836 20.727 12.881 31.495 6.665 10.768-6.217 14.917-19.495 9.976-30.608-.56-1.257-2.093-1.745-3.284-1.058L-15.803 120.8z" fill="#81C995" mask="url(#f)"/></g><g transform="translate(105 28.57)"><mask id="h" fill="#fff"><use xlink:href="#g"/></mask><path d="M252.702 109.755l-28.26 28.261a3.062 3.062 0 0 1-4.332 0l-28.262-28.26a3.064 3.064 0 0 1 0-4.332l28.262-28.26a3.062 3.062 0 0 1 4.331 0l28.261 28.26a3.064 3.064 0 0 1 0 4.331" fill="#FDDB79" mask="url(#h)"/></g><g transform="translate(105 28.57)"><mask id="j" fill="#fff"><use xlink:href="#i"/></mask><path d="M68.027-.25c0 15.464-12.536 28-28 28s-28-12.536-28-28 12.536-28 28-28 28 12.536 28 28" fill="#8AB4F8" mask="url(#j)"/></g></g><g><path d="M328.527 161.57a3 3 0 0 1-3 3h-213a3 3 0 0 1-3-3v-126a3 3 0 0 1 3-3h213a3 3 0 0 1 3 3v126zm4.5-141.5h-228a6.508 6.508 0 0 0-6.5 6.5v153.5h241V26.57c0-3.584-2.916-6.5-6.5-6.5z" fill="#3C4043"/><path d="M98.527 180.07V26.57c0-3.584 2.916-6.5 6.5-6.5h228c3.584 0 6.5 2.916 6.5 6.5v153.5h-241z" stroke="#202124" stroke-width="2"/><path d="M220.527 26.57a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0" fill="#DADCE0"/><path d="M325.527 176.57h-12a1.5 1.5 0 0 0-1.5 1.5h15a1.5 1.5 0 0 0-1.5-1.5M307.527 176.57h-12a1.5 1.5 0 0 0-1.5 1.5h15a1.5 1.5 0 0 0-1.5-1.5M289.527 176.57h-12a1.5 1.5 0 0 0-1.5 1.5h15a1.5 1.5 0 0 0-1.5-1.5M124.527 176.57h-12a1.5 1.5 0 0 0-1.5 1.5h15a1.5 1.5 0 0 0-1.5-1.5M142.527 176.57h-12a1.5 1.5 0 0 0-1.5 1.5h15a1.5 1.5 0 0 0-1.5-1.5M160.527 176.57h-12a1.5 1.5 0 0 0-1.5 1.5h15a1.5 1.5 0 0 0-1.5-1.5M271.527 176.57h-105a1.5 1.5 0 0 0-1.5 1.5h108a1.5 1.5 0 0 0-1.5-1.5" fill="#202124"/><path d="M222.027 86.382c6.316 0 11.438 5.121 11.438 11.438s-5.122 11.438-11.438 11.438c-6.316 0-11.438-5.121-11.438-11.438s5.122-11.438 11.438-11.438" fill="#F1F1F1"/><path d="M217.527 97.82a7.5 7.5 0 0 1 7.5-7.5h14.701c-2.73-5.342-8.289-9-14.7-9-5.826 0-10.945 3.019-13.882 7.576v12.674h7.386l.001-.001a7.455 7.455 0 0 1-1.006-3.749" fill="#E74133"/><path d="M225.027 105.32a7.496 7.496 0 0 1-6.494-3.75l-7.389-12.671a16.424 16.424 0 0 0-2.617 8.921c0 8.853 6.973 16.077 15.726 16.481l7.271-7.271v-5.46h-.003a7.496 7.496 0 0 1-6.494 3.75" fill="#32A753"/><path d="M239.729 90.32h-14.701a7.5 7.5 0 0 1 6.495 11.248l.002.002-7.28 12.731c.259.012.52.019.783.019 9.112 0 16.5-7.387 16.5-16.5 0-2.701-.65-5.25-1.8-7.5" fill="#F9BB00"/><path d="M225.027 91.82a6 6 0 0 1 0 12 6 6 0 0 1 0-12" fill="#4285F4"/><path d="M81.027 184.57c-1.103 0-2-.897-2-2v-3a.5.5 0 0 1 .5-.5h279a.5.5 0 0 1 .5.5v3c0 1.103-.897 2-2 2h-276zM376.788 48.278c-1.15 1.04-1.092 2.885.145 3.822 5.88 4.453 14.278 4.246 19.948-.884 5.67-5.13 6.714-13.466 2.87-19.76-.81-1.324-2.64-1.567-3.79-.526l-19.173 17.348zM423.668 162.996c-.307 6.134-5.528 10.86-11.663 10.552-6.134-.306-10.858-5.528-10.552-11.662.307-6.135 5.528-10.859 11.662-10.553 6.135.307 10.859 5.53 10.553 11.663" fill="#3C4043"/><path d="M373.426 95.324l.34-6.812c.077-1.538-1.54-2.582-2.91-1.88l-6.07 3.111c-1.37.703-1.465 2.625-.172 3.46l5.729 3.701c1.293.836 3.006-.042 3.083-1.58l.34-6.812c.077-1.538-1.54-2.582-2.91-1.88l-6.07 3.111c-1.37.703-1.465 2.625-.172 3.46l5.729 3.701c1.293.836 3.006-.042 3.083-1.58" fill="#4285F4"/><path d="M67.022 136.045a4.5 4.5 0 1 1-8.99-.45 4.5 4.5 0 0 1 8.99.45" fill="#81C995"/><path d="M28.022 68.045a4.5 4.5 0 1 1-8.99-.45 4.5 4.5 0 0 1 8.99.45" fill="#FDDB79"/><path d="M426.024 87.47a2.999 2.999 0 0 1-3.147 2.846 2.999 2.999 0 1 1 3.146-2.846" fill="#3C4043"/><path d="M441.376 117.08a4 4 0 1 0 5.412 5.892l5.891-5.412a4 4 0 0 0-5.412-5.892l-5.891 5.413z" fill="#EE675C"/><path d="M13.507 125.467c4.518-2.411 6.563-7.644 5.12-12.363a1.701 1.701 0 0 0-2.427-1.013L.897 120.256a1.703 1.703 0 0 0-.511 2.58c3.117 3.827 8.602 5.042 13.12 2.631M67.06 68.092l-4.362-4.362c-2.266-2.266-2.266-5.973 0-8.24 2.266-2.265 5.973-2.265 8.24 0l4.361 4.363c2.266 2.265 2.266 5.973 0 8.239-2.266 2.266-5.973 2.266-8.239 0" fill="#3C4043"/></g></g></svg>
\ No newline at end of file
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/images/set_default_light.svg b/chrome/browser/resources/welcome/onboarding_welcome/images/set_default_light.svg
deleted file mode 100644
index e2cf756..0000000
--- a/chrome/browser/resources/welcome/onboarding_welcome/images/set_default_light.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="454" height="185"><defs><path id="a" d="M.707.43h56.32v56.32H.707z"/><path id="c" d="M7.527 0C3.668 0 .528 3.14.528 7v126c0 3.86 3.14 7 6.999 7h213c3.86 0 7-3.14 7-7V7c0-3.86-3.14-7-7-7"/><path id="e" d="M7.527 0C3.668 0 .528 3.14.528 7v126c0 3.86 3.14 7 6.999 7h213c3.86 0 7-3.14 7-7V7c0-3.86-3.14-7-7-7h-213z"/><path id="g" d="M7.527 0C3.668 0 .528 3.14.528 7v126c0 3.86 3.14 7 6.999 7h213c3.86 0 7-3.14 7-7V7c0-3.86-3.14-7-7-7"/><path id="i" d="M7.527 0C3.668 0 .528 3.14.528 7v126c0 3.86 3.14 7 6.999 7h213c3.86 0 7-3.14 7-7V7c0-3.86-3.14-7-7-7"/></defs><g fill="none" fill-rule="evenodd"><path fill="#F1F3F4" d="M292.172 9.494l-38.26 10.251c-2.134.572-2.848 3.24-1.286 4.802l28.008 28.008c1.562 1.562 4.23.847 4.802-1.287l10.252-38.259c.57-2.134-1.382-4.087-3.516-3.515l-38.26 10.251c-2.134.572-2.848 3.24-1.286 4.802l28.008 28.008c1.562 1.562 4.23.847 4.802-1.287l10.252-38.259c.57-2.134-1.382-4.087-3.516-3.515M90.398 148.926c-1.172.677-1.51 2.225-.714 3.32 7.039 9.677 20.395 12.674 30.99 6.557 10.595-6.117 14.678-19.182 9.815-30.117-.549-1.237-2.059-1.718-3.231-1.04l-36.86 21.28zm268.632-10.515l-28.754 28.754a3.115 3.115 0 0 1-4.406 0l-28.754-28.754a3.115 3.115 0 0 1 0-4.406l28.754-28.754a3.117 3.117 0 0 1 4.406 0l28.754 28.754a3.115 3.115 0 0 1 0 4.406"/><g transform="translate(116 -.43)"><mask id="b" fill="#fff"><use xlink:href="#a"/></mask><path fill="#F1F3F4" d="M57.027 28.59c0 15.553-12.607 28.16-28.16 28.16-15.552 0-28.16-12.607-28.16-28.16C.707 13.038 13.315.43 28.867.43c15.553 0 28.16 12.608 28.16 28.16" mask="url(#b)"/></g><path fill="#FFF" d="M112.527 168.57c-3.859 0-7-3.14-7-7v-126c0-3.86 3.141-7 7-7h213c3.86 0 7 3.14 7 7v126c0 3.86-3.14 7-7 7h-213z"/><g transform="translate(105 28.57)"><mask id="d" fill="#fff"><use xlink:href="#c"/></mask><path fill="#E74133" d="M186.408-14.069L152.47-4.976c-1.892.508-2.527 2.874-1.14 4.26l24.843 24.844c1.386 1.386 3.753.751 4.26-1.142l9.093-33.937c.507-1.893-1.225-3.625-3.118-3.118L152.47-4.976c-1.892.508-2.527 2.874-1.14 4.26l24.843 24.844c1.386 1.386 3.753.751 4.26-1.142l9.093-33.937c.507-1.893-1.225-3.625-3.118-3.118" mask="url(#d)"/><mask id="f" fill="#fff"><use xlink:href="#e"/></mask><path fill="#32A753" d="M-15.803 120.8c-1.191.688-1.535 2.261-.726 3.373 7.154 9.836 20.727 12.881 31.495 6.665 10.768-6.217 14.917-19.495 9.976-30.608-.56-1.257-2.093-1.745-3.284-1.058L-15.803 120.8z" mask="url(#f)"/><mask id="h" fill="#fff"><use xlink:href="#g"/></mask><path fill="#F9BB00" d="M252.702 109.755l-28.26 28.261a3.062 3.062 0 0 1-4.332 0l-28.262-28.26a3.064 3.064 0 0 1 0-4.332l28.262-28.26a3.062 3.062 0 0 1 4.331 0l28.261 28.26a3.064 3.064 0 0 1 0 4.331" mask="url(#h)"/><mask id="j" fill="#fff"><use xlink:href="#i"/></mask><path fill="#4285F4" d="M68.027-.25c0 15.464-12.536 28-28 28s-28-12.536-28-28 12.536-28 28-28 28 12.536 28 28" mask="url(#j)"/></g><g><path fill="#F1F3F4" d="M328.527 161.57a3 3 0 0 1-3 3h-213a3 3 0 0 1-3-3v-126a3 3 0 0 1 3-3h213a3 3 0 0 1 3 3v126zm4.5-141.5h-228a6.508 6.508 0 0 0-6.5 6.5v153.5h241V26.57c0-3.584-2.916-6.5-6.5-6.5z"/><path stroke="#DADCE0" stroke-width="2" d="M98.527 180.07V26.57c0-3.584 2.916-6.5 6.5-6.5h228c3.584 0 6.5 2.916 6.5 6.5v153.5h-241z"/><path fill="#DADCE0" d="M220.527 26.57a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0m105 150h-12a1.5 1.5 0 0 0-1.5 1.5h15a1.5 1.5 0 0 0-1.5-1.5m-18 0h-12a1.5 1.5 0 0 0-1.5 1.5h15a1.5 1.5 0 0 0-1.5-1.5m-18 0h-12a1.5 1.5 0 0 0-1.5 1.5h15a1.5 1.5 0 0 0-1.5-1.5m-165 0h-12a1.5 1.5 0 0 0-1.5 1.5h15a1.5 1.5 0 0 0-1.5-1.5m18 0h-12a1.5 1.5 0 0 0-1.5 1.5h15a1.5 1.5 0 0 0-1.5-1.5m18 0h-12a1.5 1.5 0 0 0-1.5 1.5h15a1.5 1.5 0 0 0-1.5-1.5m111 0h-105a1.5 1.5 0 0 0-1.5 1.5h108a1.5 1.5 0 0 0-1.5-1.5"/><path fill="#F1F1F1" d="M219.027 86.382c6.316 0 11.438 5.121 11.438 11.438s-5.122 11.438-11.438 11.438c-6.316 0-11.438-5.121-11.438-11.438s5.122-11.438 11.438-11.438"/><path fill="#E74133" d="M217.527 97.82a7.5 7.5 0 0 1 7.5-7.5h14.701c-2.73-5.342-8.289-9-14.7-9-5.826 0-10.945 3.019-13.882 7.576v12.674h7.386l.001-.001a7.455 7.455 0 0 1-1.006-3.749"/><path fill="#32A753" d="M225.027 105.32a7.496 7.496 0 0 1-6.494-3.75l-7.389-12.671a16.424 16.424 0 0 0-2.617 8.921c0 8.853 6.973 16.077 15.726 16.481l7.271-7.271v-5.46h-.003a7.496 7.496 0 0 1-6.494 3.75"/><path fill="#F9BB00" d="M239.729 90.32h-14.701a7.5 7.5 0 0 1 6.495 11.248l.002.002-7.28 12.731c.259.012.52.019.783.019 9.112 0 16.5-7.387 16.5-16.5 0-2.701-.65-5.25-1.8-7.5"/><path fill="#4285F4" d="M225.027 91.82a6 6 0 0 1 0 12 6 6 0 0 1 0-12"/><path fill="#F1F3F4" d="M81.027 184.57c-1.103 0-2-.897-2-2v-3a.5.5 0 0 1 .5-.5h279a.5.5 0 0 1 .5.5v3c0 1.103-.897 2-2 2h-276zM376.788 48.278c-1.15 1.04-1.092 2.884.145 3.821 5.88 4.453 14.278 4.246 19.948-.884 5.67-5.13 6.714-13.466 2.87-19.76-.81-1.324-2.64-1.567-3.79-.526l-19.173 17.348zm46.88 114.718c-.307 6.134-5.528 10.86-11.663 10.552-6.134-.306-10.858-5.528-10.552-11.662.307-6.135 5.528-10.859 11.662-10.553 6.135.307 10.859 5.53 10.553 11.663"/><path fill="#4285F4" d="M373.426 95.324l.34-6.812c.077-1.538-1.54-2.582-2.91-1.88l-6.07 3.111c-1.37.703-1.465 2.625-.172 3.46l5.729 3.701c1.293.836 3.006-.042 3.083-1.58l.34-6.812c.077-1.538-1.54-2.582-2.91-1.88l-6.07 3.111c-1.37.703-1.465 2.625-.172 3.46l5.729 3.701c1.293.836 3.006-.042 3.083-1.58"/><path fill="#32A753" d="M67.022 136.045a4.5 4.5 0 1 1-8.99-.45 4.5 4.5 0 0 1 8.99.45"/><path fill="#F9BB00" d="M28.022 68.045a4.5 4.5 0 1 1-8.99-.45 4.5 4.5 0 0 1 8.99.45"/><path fill="#F1F3F4" d="M426.024 87.47a2.999 2.999 0 0 1-3.147 2.846 2.999 2.999 0 1 1 3.146-2.846"/><path fill="#E74133" d="M441.376 117.08a4 4 0 1 0 5.412 5.892l5.891-5.412a4 4 0 0 0-5.412-5.892l-5.891 5.413z"/><path fill="#F1F3F4" d="M13.507 125.467c4.518-2.411 6.563-7.644 5.12-12.363a1.701 1.701 0 0 0-2.427-1.013L.897 120.256a1.703 1.703 0 0 0-.511 2.58c3.117 3.827 8.602 5.042 13.12 2.631M67.06 68.092l-4.362-4.362c-2.266-2.266-2.266-5.973 0-8.24 2.266-2.265 5.973-2.265 8.24 0l4.361 4.363c2.266 2.265 2.266 5.973 0 8.239-2.266 2.266-5.973 2.266-8.239 0"/></g></g></svg>
\ No newline at end of file
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/onboarding_welcome_resources.grd b/chrome/browser/resources/welcome/onboarding_welcome/onboarding_welcome_resources.grd
index 4793fd5..a857eff 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/onboarding_welcome_resources.grd
+++ b/chrome/browser/resources/welcome/onboarding_welcome/onboarding_welcome_resources.grd
@@ -14,53 +14,6 @@
   </outputs>
   <release seq="1">
     <includes>
-      <include name="IDR_NUX_MODULE_ICONS_GOOGLE_DARK"
-               file="images/module_icons/google_dark.svg"
-               compress="gzip"
-               type="BINDATA" />
-      <include name="IDR_NUX_MODULE_ICONS_GOOGLE_LIGHT"
-               file="images/module_icons/google_light.svg"
-               compress="gzip"
-               type="BINDATA" />
-      <include name="IDR_NUX_MODULE_ICONS_SET_DEFAULT_DARK"
-               file="images/module_icons/set_default_dark.svg"
-               compress="gzip"
-               type="BINDATA" />
-      <include name="IDR_NUX_MODULE_ICONS_SET_DEFAULT_LIGHT"
-               file="images/module_icons/set_default_light.svg"
-               compress="gzip"
-               type="BINDATA" />
-      <include name="IDR_NUX_MODULE_ICONS_WALLPAPER_DARK"
-               file="images/module_icons/wallpaper_dark.svg"
-               compress="gzip"
-               type="BINDATA" />
-      <include name="IDR_NUX_MODULE_ICONS_WALLPAPER_LIGHT"
-               file="images/module_icons/wallpaper_light.svg"
-               compress="gzip"
-               type="BINDATA" />
-      <include name="IDR_NUX_NTP_BACKGROUND_THUMBNAIL_ART"
-               file="images/ntp_thumbnails/art.jpg"
-               type="BINDATA" />
-      <include name="IDR_NUX_NTP_BACKGROUND_THUMBNAIL_CITYSCAPE"
-               file="images/ntp_thumbnails/cityscape.jpg"
-               type="BINDATA" />
-      <include name="IDR_NUX_NTP_BACKGROUND_THUMBNAIL_EARTH"
-               file="images/ntp_thumbnails/earth.jpg"
-               type="BINDATA" />
-      <include name="IDR_NUX_NTP_BACKGROUND_THUMBNAIL_GEOMETRIC_SHAPES"
-               file="images/ntp_thumbnails/geometric_shapes.jpg"
-               type="BINDATA" />
-      <include name="IDR_NUX_NTP_BACKGROUND_THUMBNAIL_LANDSCAPE"
-               file="images/ntp_thumbnails/landscape.jpg"
-               type="BINDATA" />
-      <include name="IDR_NUX_SET_DEFAULT_DARK"
-               file="images\set_default_dark.svg"
-               compress="gzip"
-               type="BINDATA" />
-      <include name="IDR_NUX_SET_DEFAULT_LIGHT"
-               file="images\set_default_light.svg"
-               compress="gzip"
-               type="BINDATA" />
       <include name="IDR_WELCOME_ONBOARDING_WELCOME_IMAGES_BACKGROUND_SVGS_BLUE_CIRCLE_SVG"
                file="images/background_svgs/blue_circle.svg"
                compress="gzip"
diff --git a/chrome/browser/safe_browsing/download_protection/download_protection_service.cc b/chrome/browser/safe_browsing/download_protection/download_protection_service.cc
index ee592221..d764c17 100644
--- a/chrome/browser/safe_browsing/download_protection/download_protection_service.cc
+++ b/chrome/browser/safe_browsing/download_protection/download_protection_service.cc
@@ -19,7 +19,6 @@
 #include "chrome/browser/safe_browsing/download_protection/download_url_sb_client.h"
 #include "chrome/browser/safe_browsing/download_protection/ppapi_download_request.h"
 #include "chrome/browser/sessions/session_tab_helper.h"
-#include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/common/safe_browsing/binary_feature_extractor.h"
 #include "chrome/common/url_constants.h"
 #include "components/google/core/common/google_util.h"
@@ -31,7 +30,6 @@
 #include "content/public/browser/web_contents.h"
 #include "net/base/url_util.h"
 #include "net/cert/x509_util.h"
-#include "services/identity/public/cpp/identity_manager.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
 using content::BrowserThread;
@@ -447,18 +445,11 @@
 void DownloadProtectionService::OnDangerousDownloadOpened(
     const download::DownloadItem* item,
     Profile* profile) {
-  identity::IdentityManager* identity_manager =
-      IdentityManagerFactory::GetForProfileIfExists(profile);
-  std::string username = identity_manager
-                             ? identity_manager->GetPrimaryAccountInfo().email
-                             : std::string();
-
   std::string raw_digest_sha256 = item->GetHash();
   extensions::SafeBrowsingPrivateEventRouterFactory::GetForProfile(profile)
       ->OnDangerousDownloadOpened(
           item->GetURL(), item->GetTargetFilePath().AsUTF8Unsafe(),
-          base::HexEncode(raw_digest_sha256.data(), raw_digest_sha256.size()),
-          username);
+          base::HexEncode(raw_digest_sha256.data(), raw_digest_sha256.size()));
 }
 
 bool DownloadProtectionService::MaybeBeginFeedbackForDownload(
diff --git a/chrome/browser/search/instant_service.cc b/chrome/browser/search/instant_service.cc
index 91f409d..d93277b 100644
--- a/chrome/browser/search/instant_service.cc
+++ b/chrome/browser/search/instant_service.cc
@@ -399,8 +399,8 @@
   UpdateThemeInfo();
 }
 
-void InstantService::UpdateMostVisitedItemsInfo() {
-  NotifyAboutMostVisitedItems();
+void InstantService::UpdateMostVisitedInfo() {
+  NotifyAboutMostVisitedInfo();
 }
 
 void InstantService::SendNewTabPageURLToRenderer(
@@ -569,14 +569,14 @@
   most_visited_info_->items_are_custom_links =
       (most_visited_sites_ && most_visited_sites_->IsCustomLinksInitialized());
 
-  NotifyAboutMostVisitedItems();
+  NotifyAboutMostVisitedInfo();
 }
 
 void InstantService::OnIconMadeAvailable(const GURL& site_url) {}
 
-void InstantService::NotifyAboutMostVisitedItems() {
+void InstantService::NotifyAboutMostVisitedInfo() {
   for (InstantServiceObserver& observer : observers_)
-    observer.MostVisitedItemsChanged(*most_visited_info_);
+    observer.MostVisitedInfoChanged(*most_visited_info_);
 }
 
 void InstantService::NotifyAboutThemeInfo() {
diff --git a/chrome/browser/search/instant_service.h b/chrome/browser/search/instant_service.h
index 1b337b1..e0375d1 100644
--- a/chrome/browser/search/instant_service.h
+++ b/chrome/browser/search/instant_service.h
@@ -123,7 +123,7 @@
 
   // Invoked by the InstantController to update most visited items details for
   // NTP.
-  void UpdateMostVisitedItemsInfo();
+  void UpdateMostVisitedInfo();
 
   // Sends the current NTP URL to a renderer process.
   void SendNewTabPageURLToRenderer(content::RenderProcessHost* rph);
@@ -207,7 +207,7 @@
           sections) override;
   void OnIconMadeAvailable(const GURL& site_url) override;
 
-  void NotifyAboutMostVisitedItems();
+  void NotifyAboutMostVisitedInfo();
   void NotifyAboutThemeInfo();
 
   // Returns true if this is a Google NTP and the user has chosen to show custom
diff --git a/chrome/browser/search/instant_service_observer.cc b/chrome/browser/search/instant_service_observer.cc
index aaf8db4a..99bd74b7 100644
--- a/chrome/browser/search/instant_service_observer.cc
+++ b/chrome/browser/search/instant_service_observer.cc
@@ -7,5 +7,5 @@
 void InstantServiceObserver::ThemeInfoChanged(const ThemeBackgroundInfo&) {
 }
 
-void InstantServiceObserver::MostVisitedItemsChanged(
+void InstantServiceObserver::MostVisitedInfoChanged(
     const InstantMostVisitedInfo&) {}
diff --git a/chrome/browser/search/instant_service_observer.h b/chrome/browser/search/instant_service_observer.h
index 0660a17..6edce68 100644
--- a/chrome/browser/search/instant_service_observer.h
+++ b/chrome/browser/search/instant_service_observer.h
@@ -23,7 +23,7 @@
   virtual void ThemeInfoChanged(const ThemeBackgroundInfo&);
 
   // Indicates that the most visited items have changed in some way.
-  virtual void MostVisitedItemsChanged(const InstantMostVisitedInfo&);
+  virtual void MostVisitedInfoChanged(const InstantMostVisitedInfo&);
 
  protected:
   virtual ~InstantServiceObserver() {}
diff --git a/chrome/browser/search/instant_service_unittest.cc b/chrome/browser/search/instant_service_unittest.cc
index 3113cc3..128abf2 100644
--- a/chrome/browser/search/instant_service_unittest.cc
+++ b/chrome/browser/search/instant_service_unittest.cc
@@ -32,7 +32,7 @@
 class MockInstantServiceObserver : public InstantServiceObserver {
  public:
   MOCK_METHOD1(ThemeInfoChanged, void(const ThemeBackgroundInfo&));
-  MOCK_METHOD1(MostVisitedItemsChanged, void(const InstantMostVisitedInfo&));
+  MOCK_METHOD1(MostVisitedInfoChanged, void(const InstantMostVisitedInfo&));
 };
 
 base::DictionaryValue GetBackgroundInfoAsDict(const GURL& background_url) {
diff --git a/chrome/browser/search_engines/template_url_fetcher_unittest.cc b/chrome/browser/search_engines/template_url_fetcher_unittest.cc
index 632be4c8..60d7a31 100644
--- a/chrome/browser/search_engines/template_url_fetcher_unittest.cc
+++ b/chrome/browser/search_engines/template_url_fetcher_unittest.cc
@@ -24,7 +24,7 @@
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/test/test_browser_thread_bundle.h"
-#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "content/public/test/url_loader_interceptor.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
@@ -33,6 +33,17 @@
 
 using base::ASCIIToUTF16;
 
+bool GetTestFilePath(const std::string& file_name, base::FilePath* path) {
+  if (!base::PathService::Get(base::DIR_SOURCE_ROOT, path))
+    return false;
+  *path = path->AppendASCII("components")
+              .AppendASCII("test")
+              .AppendASCII("data")
+              .AppendASCII("search_engines")
+              .AppendASCII(file_name);
+  return true;
+}
+
 class TestTemplateUrlFetcher : public TemplateURLFetcher {
  public:
   TestTemplateUrlFetcher(TemplateURLService* template_url_service,
@@ -64,12 +75,6 @@
         test_util_.model(),
         base::Bind(&TemplateURLFetcherTest::RequestCompletedCallback,
                    base::Unretained(this))));
-
-    ASSERT_TRUE(test_server_.Start());
-  }
-
-  void TearDown() override {
-    ASSERT_TRUE(test_server_.ShutdownAndWaitUntilComplete());
   }
 
   // Called when a request completes.
@@ -80,6 +85,14 @@
                      const std::string& osdd_file_name,
                      bool check_that_file_exists);
 
+  // Handles an incoming request.
+  bool HandleRequest(content::URLLoaderInterceptor::RequestParams* params) {
+    base::FilePath path;
+    CHECK(GetTestFilePath(params->url_request.url.ExtractFileName(), &path));
+    content::URLLoaderInterceptor::WriteResponse(path, params->client.get());
+    return true;
+  }
+
   // Waits for any downloads to finish.
   void WaitForDownloadToFinish();
 
@@ -93,7 +106,7 @@
   content::TestBrowserThreadBundle thread_bundle_;  // To set up BrowserThreads.
   TemplateURLServiceTestUtil test_util_;
   std::unique_ptr<TemplateURLFetcher> template_url_fetcher_;
-  net::EmbeddedTestServer test_server_;
+  content::URLLoaderInterceptor url_loader_interceptor_;
 
   // How many TemplateURKFetcher::RequestDelegate requests have completed.
   int requests_completed_;
@@ -106,24 +119,13 @@
   DISALLOW_COPY_AND_ASSIGN(TemplateURLFetcherTest);
 };
 
-bool GetTestDataDir(base::FilePath* dir) {
-  if (!base::PathService::Get(base::DIR_SOURCE_ROOT, dir))
-    return false;
-  *dir = dir->AppendASCII("components")
-             .AppendASCII("test")
-             .AppendASCII("data")
-             .AppendASCII("search_engines");
-  return true;
-}
-
 TemplateURLFetcherTest::TemplateURLFetcherTest()
     : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
+      url_loader_interceptor_(
+          base::BindRepeating(&TemplateURLFetcherTest::HandleRequest,
+                              base::Unretained(this))),
       requests_completed_(0),
-      waiting_for_download_(false) {
-  base::FilePath test_data_dir;
-  CHECK(GetTestDataDir(&test_data_dir));
-  test_server_.ServeFilesFromDirectory(test_data_dir);
-}
+      waiting_for_download_(false) {}
 
 void TemplateURLFetcherTest::RequestCompletedCallback() {
   requests_completed_++;
@@ -137,14 +139,13 @@
     bool check_that_file_exists) {
   if (check_that_file_exists) {
     base::FilePath osdd_full_path;
-    ASSERT_TRUE(GetTestDataDir(&osdd_full_path));
-    osdd_full_path = osdd_full_path.AppendASCII(osdd_file_name);
+    ASSERT_TRUE(GetTestFilePath(osdd_file_name, &osdd_full_path));
     ASSERT_TRUE(base::PathExists(osdd_full_path));
     ASSERT_FALSE(base::DirectoryExists(osdd_full_path));
   }
 
   // Start the fetch.
-  GURL osdd_url = test_server_.GetURL("/" + osdd_file_name);
+  GURL osdd_url("http://some.url/" + osdd_file_name);
   GURL favicon_url;
 
   TestingProfile* profile = test_util_.profile();
diff --git a/chrome/browser/site_isolation/prefs_observer.cc b/chrome/browser/site_isolation/prefs_observer.cc
index bdb6702f6..87ff2f8 100644
--- a/chrome/browser/site_isolation/prefs_observer.cc
+++ b/chrome/browser/site_isolation/prefs_observer.cc
@@ -49,5 +49,6 @@
   auto* policy = content::ChildProcessSecurityPolicy::GetInstance();
   policy->AddIsolatedOrigins(
       content::SiteIsolationPolicy::ParseIsolatedOrigins(isolated_origins),
+      content::ChildProcessSecurityPolicy::IsolatedOriginSource::POLICY,
       /* browser_context = */ nullptr);
 }
diff --git a/chrome/browser/site_isolation/site_isolation_policy.cc b/chrome/browser/site_isolation/site_isolation_policy.cc
index f3abbac2..d15ee29c 100644
--- a/chrome/browser/site_isolation/site_isolation_policy.cc
+++ b/chrome/browser/site_isolation/site_isolation_policy.cc
@@ -58,7 +58,10 @@
 
   if (!origins.empty()) {
     auto* policy = content::ChildProcessSecurityPolicy::GetInstance();
-    policy->AddIsolatedOrigins(origins, /* browser_context = */ profile);
+    using IsolatedOriginSource =
+        content::ChildProcessSecurityPolicy::IsolatedOriginSource;
+    policy->AddIsolatedOrigins(origins, IsolatedOriginSource::USER_TRIGGERED,
+                               /* browser_context = */ profile);
   }
 
   UMA_HISTOGRAM_COUNTS_1000(
diff --git a/chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc b/chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc
index defe0a1d..550ed9e 100644
--- a/chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc
@@ -458,9 +458,8 @@
   ExpectNavigationChain({first_url, second_url});
 }
 
-// TODO(crbug.com/972929): The test is slow and times out often.
 IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest,
-                       DISABLED_NavigationChainAlteredDestructively) {
+                       NavigationChainAlteredDestructively) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(CheckInitialState(0));
 
diff --git a/chrome/browser/ui/app_list/app_list_client_impl_browsertest.cc b/chrome/browser/ui/app_list/app_list_client_impl_browsertest.cc
index b2420f48..7180b22 100644
--- a/chrome/browser/ui/app_list/app_list_client_impl_browsertest.cc
+++ b/chrome/browser/ui/app_list/app_list_client_impl_browsertest.cc
@@ -72,7 +72,7 @@
         content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
         content::NotificationService::AllSources());
     OpenApplication(AppLaunchParams(
-        profile(), extension_app, extensions::LAUNCH_CONTAINER_WINDOW,
+        profile(), extension_app->id(), extensions::LAUNCH_CONTAINER_WINDOW,
         WindowOpenDisposition::NEW_WINDOW, extensions::SOURCE_TEST));
     app_loaded_observer.Wait();
   }
diff --git a/chrome/browser/ui/apps/chrome_app_delegate.cc b/chrome/browser/ui/apps/chrome_app_delegate.cc
index 598ba66..01760bc 100644
--- a/chrome/browser/ui/apps/chrome_app_delegate.cc
+++ b/chrome/browser/ui/apps/chrome_app_delegate.cc
@@ -375,7 +375,7 @@
 #endif
 }
 
-gfx::Size ChromeAppDelegate::EnterPictureInPicture(
+content::PictureInPictureResult ChromeAppDelegate::EnterPictureInPicture(
     content::WebContents* web_contents,
     const viz::SurfaceId& surface_id,
     const gfx::Size& natural_size) {
diff --git a/chrome/browser/ui/apps/chrome_app_delegate.h b/chrome/browser/ui/apps/chrome_app_delegate.h
index 5109b38..ee81fb8c 100644
--- a/chrome/browser/ui/apps/chrome_app_delegate.h
+++ b/chrome/browser/ui/apps/chrome_app_delegate.h
@@ -75,9 +75,10 @@
   void OnHide() override;
   void OnShow() override;
   bool TakeFocus(content::WebContents* web_contents, bool reverse) override;
-  gfx::Size EnterPictureInPicture(content::WebContents* web_contents,
-                                  const viz::SurfaceId& surface_id,
-                                  const gfx::Size& natural_size) override;
+  content::PictureInPictureResult EnterPictureInPicture(
+      content::WebContents* web_contents,
+      const viz::SurfaceId& surface_id,
+      const gfx::Size& natural_size) override;
   void ExitPictureInPicture() override;
 
   // content::NotificationObserver:
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
index 97122a8..1323b8e 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
@@ -224,7 +224,7 @@
         service->GetExtensionById(last_loaded_extension_id(), false);
     EXPECT_TRUE(extension);
 
-    OpenApplication(AppLaunchParams(profile(), extension, container,
+    OpenApplication(AppLaunchParams(profile(), extension->id(), container,
                                     disposition, extensions::SOURCE_TEST));
     return extension;
   }
@@ -1176,13 +1176,14 @@
   const Extension* extension =
       LoadAndLaunchExtension("app1", extensions::LAUNCH_CONTAINER_WINDOW,
                              WindowOpenDisposition::NEW_WINDOW);
+  ASSERT_TRUE(extension);
 
   // No new browser should get detected, even though one more is running.
   EXPECT_EQ(0u, NumberOfDetectedLauncherBrowsers(false));
   EXPECT_EQ(++running_browser, chrome::GetTotalBrowserCount());
 
   OpenApplication(AppLaunchParams(
-      profile(), extension, extensions::LAUNCH_CONTAINER_TAB,
+      profile(), extension->id(), extensions::LAUNCH_CONTAINER_TAB,
       WindowOpenDisposition::NEW_WINDOW, extensions::SOURCE_TEST));
 
   // A new browser should get detected and one more should be running.
diff --git a/chrome/browser/ui/ash/test_login_screen.cc b/chrome/browser/ui/ash/test_login_screen.cc
index 7275ab0..dde25b3 100644
--- a/chrome/browser/ui/ash/test_login_screen.cc
+++ b/chrome/browser/ui/ash/test_login_screen.cc
@@ -36,6 +36,7 @@
 
 void TestLoginScreen::ShowParentAccessWidget(
     const AccountId& child_account_id,
-    base::RepeatingCallback<void(bool success)> callback) {}
+    base::RepeatingCallback<void(bool success)> callback,
+    ash::ParentAccessRequestReason reason) {}
 
 void TestLoginScreen::SetAllowLoginAsGuest(bool allow_guest) {}
diff --git a/chrome/browser/ui/ash/test_login_screen.h b/chrome/browser/ui/ash/test_login_screen.h
index 4bbb101..a073d932 100644
--- a/chrome/browser/ui/ash/test_login_screen.h
+++ b/chrome/browser/ui/ash/test_login_screen.h
@@ -39,7 +39,8 @@
   void ShowParentAccessButton(bool show) override;
   void ShowParentAccessWidget(
       const AccountId& child_account_id,
-      base::RepeatingCallback<void(bool success)> callback) override;
+      base::RepeatingCallback<void(bool success)> callback,
+      ash::ParentAccessRequestReason reason) override;
   void SetAllowLoginAsGuest(bool allow_guest) override;
 
  private:
diff --git a/chrome/browser/ui/ash/wallpaper_controller_client.cc b/chrome/browser/ui/ash/wallpaper_controller_client.cc
index 4d2a755..298ba684 100644
--- a/chrome/browser/ui/ash/wallpaper_controller_client.cc
+++ b/chrome/browser/ui/ash/wallpaper_controller_client.cc
@@ -490,7 +490,7 @@
     return;
 
   OpenApplication(AppLaunchParams(
-      profile, extension, extensions::LAUNCH_CONTAINER_WINDOW,
+      profile, extension->id(), extensions::LAUNCH_CONTAINER_WINDOW,
       WindowOpenDisposition::NEW_WINDOW, extensions::SOURCE_CHROME_INTERNAL));
 }
 
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index 66a5381..c4c42a3 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -1278,9 +1278,10 @@
   }
 }
 
-gfx::Size Browser::EnterPictureInPicture(content::WebContents* web_contents,
-                                         const viz::SurfaceId& surface_id,
-                                         const gfx::Size& natural_size) {
+content::PictureInPictureResult Browser::EnterPictureInPicture(
+    content::WebContents* web_contents,
+    const viz::SurfaceId& surface_id,
+    const gfx::Size& natural_size) {
   return PictureInPictureWindowManager::GetInstance()->EnterPictureInPicture(
       web_contents, surface_id, natural_size);
 }
diff --git a/chrome/browser/ui/browser.h b/chrome/browser/ui/browser.h
index 5483fef1..f3220390 100644
--- a/chrome/browser/ui/browser.h
+++ b/chrome/browser/ui/browser.h
@@ -553,9 +553,10 @@
                                          const GURL& resource_url) override;
   void OnDidBlockFramebust(content::WebContents* web_contents,
                            const GURL& url) override;
-  gfx::Size EnterPictureInPicture(content::WebContents* web_contents,
-                                  const viz::SurfaceId&,
-                                  const gfx::Size&) override;
+  content::PictureInPictureResult EnterPictureInPicture(
+      content::WebContents* web_contents,
+      const viz::SurfaceId&,
+      const gfx::Size&) override;
   void ExitPictureInPicture() override;
   std::unique_ptr<content::WebContents> SwapWebContents(
       content::WebContents* old_contents,
diff --git a/chrome/browser/ui/browser_browsertest.cc b/chrome/browser/ui/browser_browsertest.cc
index 9762938..f622de1 100644
--- a/chrome/browser/ui/browser_browsertest.cc
+++ b/chrome/browser/ui/browser_browsertest.cc
@@ -1236,8 +1236,9 @@
 
   // Launch it in a window, as AppLauncherHandler::HandleLaunchApp() would.
   WebContents* app_window = OpenApplication(AppLaunchParams(
-      browser()->profile(), extension_app, extensions::LAUNCH_CONTAINER_WINDOW,
-      WindowOpenDisposition::NEW_WINDOW, extensions::SOURCE_TEST));
+      browser()->profile(), extension_app->id(),
+      extensions::LAUNCH_CONTAINER_WINDOW, WindowOpenDisposition::NEW_WINDOW,
+      extensions::SOURCE_TEST));
   ASSERT_TRUE(app_window);
 
   DevToolsWindow* devtools_window =
@@ -1400,11 +1401,13 @@
   // Load an app
   ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app/")));
   const Extension* extension_app = GetExtension();
+  ASSERT_TRUE(extension_app);
 
   // Launch it in a window, as AppLauncherHandler::HandleLaunchApp() would.
   WebContents* app_window = OpenApplication(AppLaunchParams(
-      browser()->profile(), extension_app, extensions::LAUNCH_CONTAINER_WINDOW,
-      WindowOpenDisposition::NEW_WINDOW, extensions::SOURCE_TEST));
+      browser()->profile(), extension_app->id(),
+      extensions::LAUNCH_CONTAINER_WINDOW, WindowOpenDisposition::NEW_WINDOW,
+      extensions::SOURCE_TEST));
   ASSERT_TRUE(app_window);
 
   // Apps launched in a window from the NTP have an extensions tab helper with
diff --git a/chrome/browser/ui/cocoa/apps/native_app_window_cocoa_browsertest.mm b/chrome/browser/ui/cocoa/apps/native_app_window_cocoa_browsertest.mm
index 3d2295a..423a790 100644
--- a/chrome/browser/ui/cocoa/apps/native_app_window_cocoa_browsertest.mm
+++ b/chrome/browser/ui/cocoa/apps/native_app_window_cocoa_browsertest.mm
@@ -60,7 +60,7 @@
           content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
           content::NotificationService::AllSources());
       OpenApplication(AppLaunchParams(
-          profile(), app_, extensions::LAUNCH_CONTAINER_NONE,
+          profile(), app_->id(), extensions::LAUNCH_CONTAINER_NONE,
           WindowOpenDisposition::NEW_WINDOW, extensions::SOURCE_TEST));
       app_loaded_observer.Wait();
     }
diff --git a/chrome/browser/ui/cocoa/task_manager_mac_browsertest.mm b/chrome/browser/ui/cocoa/task_manager_mac_browsertest.mm
index 974d8a69..98a27cf 100644
--- a/chrome/browser/ui/cocoa/task_manager_mac_browsertest.mm
+++ b/chrome/browser/ui/cocoa/task_manager_mac_browsertest.mm
@@ -28,6 +28,7 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_delegate.h"
 #include "content/public/test/browser_test_utils.h"
+#include "content/public/test/no_renderer_crashes_assertion.h"
 #include "content/public/test/test_utils.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
@@ -273,11 +274,15 @@
   EXPECT_EQ(TableFirstSelectedRow(), FindRowForTab(tabs[1]));
   EXPECT_EQ(2, [GetTable() numberOfSelectedRows]);
 
-  // Press the button, which kills the process of the selected row.
-  PressKillButton();
+  {
+    content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
 
-  // Two rows should disappear.
-  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows((rows -= 2), pattern));
+    // Press the button, which kills the process of the selected row.
+    PressKillButton();
+
+    // Two rows should disappear.
+    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows((rows -= 2), pattern));
+  }
 
   // No row should now be selected.
   ASSERT_EQ(-1, TableFirstSelectedRow());
diff --git a/chrome/browser/ui/extensions/app_launch_params.cc b/chrome/browser/ui/extensions/app_launch_params.cc
index 2246589..7683d3d 100644
--- a/chrome/browser/ui/extensions/app_launch_params.cc
+++ b/chrome/browser/ui/extensions/app_launch_params.cc
@@ -19,20 +19,19 @@
 using extensions::ExtensionPrefs;
 
 AppLaunchParams::AppLaunchParams(Profile* profile,
-                                 const extensions::Extension* extension,
+                                 const web_app::AppId& app_id,
                                  extensions::LaunchContainer container,
                                  WindowOpenDisposition disposition,
                                  extensions::AppLaunchSource source,
                                  int64_t display_id)
     : profile(profile),
-      extension_id(extension ? extension->id() : std::string()),
+      app_id(app_id),
       container(container),
       disposition(disposition),
       command_line(base::CommandLine::NO_PROGRAM),
       source(source),
       display_id(display_id),
-      opener(nullptr) {
-}
+      opener(nullptr) {}
 
 AppLaunchParams::AppLaunchParams(const AppLaunchParams& other) = default;
 
@@ -47,7 +46,8 @@
   // is to launch as a regular tab.
   extensions::LaunchContainer container =
       extensions::GetLaunchContainer(ExtensionPrefs::Get(profile), extension);
-  return AppLaunchParams(profile, extension, container, disposition, source);
+  return AppLaunchParams(profile, extension->id(), container, disposition,
+                         source);
 }
 
 AppLaunchParams CreateAppLaunchParamsWithEventFlags(
@@ -75,6 +75,6 @@
         extensions::GetLaunchContainer(ExtensionPrefs::Get(profile), extension);
     disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
   }
-  return AppLaunchParams(profile, extension, container, disposition, source,
-                         display_id);
+  return AppLaunchParams(profile, extension->id(), container, disposition,
+                         source, display_id);
 }
diff --git a/chrome/browser/ui/extensions/app_launch_params.h b/chrome/browser/ui/extensions/app_launch_params.h
index d0d41a7..ff3aad2 100644
--- a/chrome/browser/ui/extensions/app_launch_params.h
+++ b/chrome/browser/ui/extensions/app_launch_params.h
@@ -9,6 +9,7 @@
 
 #include "base/command_line.h"
 #include "base/files/file_path.h"
+#include "chrome/browser/web_applications/components/web_app_helpers.h"
 #include "chrome/common/extensions/extension_constants.h"
 #include "extensions/common/constants.h"
 #include "ui/base/window_open_disposition.h"
@@ -28,7 +29,7 @@
 
 struct AppLaunchParams {
   AppLaunchParams(Profile* profile,
-                  const extensions::Extension* extension,
+                  const web_app::AppId& app_id,
                   extensions::LaunchContainer container,
                   WindowOpenDisposition disposition,
                   extensions::AppLaunchSource source,
@@ -41,8 +42,8 @@
   // The profile to load the application from.
   Profile* profile;
 
-  // The extension to load.
-  std::string extension_id;
+  // The app to launch.
+  web_app::AppId app_id;
 
   // An id that can be passed to an app when launched in order to support
   // multiple shelf items per app.
diff --git a/chrome/browser/ui/extensions/application_launch.cc b/chrome/browser/ui/extensions/application_launch.cc
index 335a225b..420562c6 100644
--- a/chrome/browser/ui/extensions/application_launch.cc
+++ b/chrome/browser/ui/extensions/application_launch.cc
@@ -105,13 +105,12 @@
 };
 
 const Extension* GetExtension(const AppLaunchParams& params) {
-  if (params.extension_id.empty())
+  if (params.app_id.empty())
     return NULL;
   ExtensionRegistry* registry = ExtensionRegistry::Get(params.profile);
-  return registry->GetExtensionById(params.extension_id,
-                                    ExtensionRegistry::ENABLED |
-                                        ExtensionRegistry::DISABLED |
-                                        ExtensionRegistry::TERMINATED);
+  return registry->GetExtensionById(
+      params.app_id, ExtensionRegistry::ENABLED | ExtensionRegistry::DISABLED |
+                         ExtensionRegistry::TERMINATED);
 }
 
 bool IsAllowedToOverrideURL(const extensions::Extension* extension,
@@ -467,7 +466,7 @@
 WebContents* OpenAppShortcutWindow(Profile* profile,
                                    const GURL& url) {
   AppLaunchParams launch_params(profile,
-                                NULL,  // this is a URL app.  No extension.
+                                std::string(),  // this is a URL app. No app id.
                                 extensions::LAUNCH_CONTAINER_WINDOW,
                                 WindowOpenDisposition::NEW_WINDOW,
                                 extensions::SOURCE_COMMAND_LINE);
diff --git a/chrome/browser/ui/extensions/hosted_app_browsertest.cc b/chrome/browser/ui/extensions/hosted_app_browsertest.cc
index a8239db..608111868 100644
--- a/chrome/browser/ui/extensions/hosted_app_browsertest.cc
+++ b/chrome/browser/ui/extensions/hosted_app_browsertest.cc
@@ -945,7 +945,7 @@
 
   const extensions::Extension* app = InstallBookmarkApp(web_app_info);
 
-  AppLaunchParams params(browser()->profile(), app,
+  AppLaunchParams params(browser()->profile(), app->id(),
                          extensions::LaunchContainer::LAUNCH_CONTAINER_WINDOW,
                          WindowOpenDisposition::NEW_WINDOW,
                          extensions::AppLaunchSource::SOURCE_FILE_HANDLER);
diff --git a/chrome/browser/ui/login/login_handler.cc b/chrome/browser/ui/login/login_handler.cc
index 1aeb9b3..52435cc 100644
--- a/chrome/browser/ui/login/login_handler.cc
+++ b/chrome/browser/ui/login/login_handler.cc
@@ -461,16 +461,44 @@
                  content::NotificationService::AllBrowserContextsAndSources());
   registrar_.Add(this, chrome::NOTIFICATION_AUTH_CANCELLED,
                  content::NotificationService::AllBrowserContextsAndSources());
-  prompt_started_ = true;
 
   // When committed interstitials are enabled, a login prompt is triggered once
   // the interstitial commits (that is, when |mode| is POST_COMMIT).
-  if (base::FeatureList::IsEnabled(features::kHTTPAuthCommittedInterstitials) &&
-      mode == POST_COMMIT) {
-    ShowLoginPrompt(request_url);
-    return;
+  if (base::FeatureList::IsEnabled(features::kHTTPAuthCommittedInterstitials)) {
+    if (mode == POST_COMMIT) {
+      prompt_started_ = true;
+      ShowLoginPrompt(request_url);
+      return;
+    }
+
+    // In PRE_COMMIT mode, always cancel main frame requests that receive auth
+    // challenges. An interstitial will be committed as the result of the
+    // cancellation, and the login prompt will be shown on top of it in
+    // POST_COMMIT mode once the interstitial commits.
+    //
+    // Strictly speaking, it is not necessary to show an interstitial for all
+    // main-frame navigations, just cross-origin ones. However, we show an
+    // interstitial for all main-frame navigations for simplicity. Otherwise,
+    // it's difficult to prevent repeated prompts on cancellation. For example,
+    // imagine that we navigate from http://a.com/1 to http://a.com/2 and show a
+    // login prompt without committing an interstitial. If the prompt is
+    // cancelled, the request will then be resumed to read the 401 body and
+    // commit the navigation. But the committed 401 error looks
+    // indistinguishable from what we commit in the case of a cross-origin
+    // navigation, so LoginHandler will run in POST_COMMIT mode and show another
+    // login prompt. For simplicity, and because same-origin auth prompts should
+    // be relatively rare due to credential caching, we commit an interstitial
+    // for all main-frame navigations.
+    if (is_request_for_main_frame) {
+      DCHECK(mode == PRE_COMMIT);
+      RecordHttpAuthPromptType(AUTH_PROMPT_TYPE_WITH_INTERSTITIAL);
+      CancelAuth();
+      return;
+    }
   }
 
+  prompt_started_ = true;
+
   // Check if this is a main frame navigation and
   // (a) if the request is cross origin or
   // (b) if an interstitial is already being shown or
@@ -505,19 +533,10 @@
        auth_info().is_proxy) &&
       web_contents()->GetDelegate()->GetDisplayMode(web_contents()) !=
           blink::kWebDisplayModeStandalone) {
+    DCHECK(!base::FeatureList::IsEnabled(
+        features::kHTTPAuthCommittedInterstitials));
     RecordHttpAuthPromptType(AUTH_PROMPT_TYPE_WITH_INTERSTITIAL);
 
-    // When committed interstitials are enabled, cancel the request in order to
-    // commit an interstitial; the login prompt will be shown in POST_COMMIT
-    // mode once the interstitial commits.
-    if (base::FeatureList::IsEnabled(
-            features::kHTTPAuthCommittedInterstitials) &&
-        mode == PRE_COMMIT) {
-      prompt_started_ = false;
-      CancelAuth();
-      return;
-    }
-
     // Show a blank interstitial for main-frame, cross origin requests
     // so that the correct URL is shown in the omnibox.
     base::OnceClosure callback =
diff --git a/chrome/browser/ui/login/login_handler_browsertest.cc b/chrome/browser/ui/login/login_handler_browsertest.cc
index ae8250c..605b3ea9 100644
--- a/chrome/browser/ui/login/login_handler_browsertest.cc
+++ b/chrome/browser/ui/login/login_handler_browsertest.cc
@@ -52,28 +52,8 @@
 
 namespace {
 
-bool AreSSLCommittedInterstitialsEnabled() {
-  return base::FeatureList::IsEnabled(features::kSSLCommittedInterstitials);
-}
-
-content::InterstitialPageDelegate* GetInterstitialDelegate(
-    content::WebContents* tab) {
-  if (AreSSLCommittedInterstitialsEnabled()) {
-    security_interstitials::SecurityInterstitialTabHelper* helper =
-        security_interstitials::SecurityInterstitialTabHelper::FromWebContents(
-            tab);
-    if (!helper)
-      return nullptr;
-    return helper->GetBlockingPageForCurrentlyCommittedNavigationForTesting();
-  }
-  content::InterstitialPage* interstitial_page = tab->GetInterstitialPage();
-  if (!interstitial_page)
-    return nullptr;
-  return interstitial_page->GetDelegateForTesting();
-}
-
 // Tests that a cross origin navigation triggering a login prompt should cause:
-// - A blank login interstitial being displayed.
+// - A login interstitial being displayed.
 // - The destination URL being shown in the omnibox.
 // Navigates to |visit_url| which triggers an HTTP auth dialog, and checks if
 // the URL displayed in the omnibox is equal to |expected_url| after all
@@ -92,32 +72,19 @@
 
   // Load a page which will trigger a login prompt.
   WindowedAuthNeededObserver auth_needed_waiter(controller);
-  browser->OpenURL(OpenURLParams(visit_url, Referrer(),
-                                 WindowOpenDisposition::CURRENT_TAB,
-                                 ui::PAGE_TRANSITION_TYPED, false));
-  ASSERT_EQ(visit_url.host(), contents->GetVisibleURL().host());
+  ui_test_utils::NavigateToURL(browser, visit_url);
   auth_needed_waiter.Wait();
   ASSERT_EQ(1u, observer.handlers().size());
-  content::WaitForInterstitialAttach(contents);
 
   // The omnibox should show the correct origin for the new page when the
   // login prompt is shown.
   EXPECT_EQ(expected_hostname, contents->GetVisibleURL().host());
-  EXPECT_TRUE(contents->ShowingInterstitialPage());
-  EXPECT_EQ(LoginInterstitialDelegate::kTypeForTesting,
-            contents->GetInterstitialPage()
-                ->GetDelegateForTesting()
-                ->GetTypeForTesting());
 
   if (cancel_prompt) {
     // Cancel and wait for the interstitial to detach.
     LoginHandler* handler = *observer.handlers().begin();
-    content::RunTaskAndWaitForInterstitialDetach(
-        contents,
-        base::BindOnce(&LoginHandler::CancelAuth, base::Unretained(handler)));
-
+    handler->CancelAuth();
     EXPECT_EQ(expected_hostname, contents->GetVisibleURL().host());
-    EXPECT_FALSE(contents->ShowingInterstitialPage());
   }
 }
 
@@ -136,6 +103,8 @@
 
   void SetUpOnMainThread() override {
     host_resolver()->AddRule("*", "127.0.0.1");
+    scoped_feature_list.InitAndEnableFeature(
+        features::kHTTPAuthCommittedInterstitials);
   }
 
  protected:
@@ -160,6 +129,7 @@
   std::string password_;
   std::string username_basic_;
   std::string username_digest_;
+  base::test::ScopedFeatureList scoped_feature_list;
 };
 
 void LoginPromptBrowserTest::SetAuthFor(LoginHandler* handler) {
@@ -189,7 +159,6 @@
 // Navigating to non-existing pages caused flakes in the past
 // (https://crbug.com/636875).
 const char kNoAuthPage1[] = "/simple.html";
-const char kNoAuthPage2[] = "/form.html";
 
 base::string16 ExpectedTitleFromAuth(const base::string16& username,
                                      const base::string16& password) {
@@ -455,18 +424,15 @@
   LoginPromptBrowserTestObserver observer;
   observer.Register(content::Source<NavigationController>(controller));
 
-  // One LOAD_STOP event for kAuthURL and second  for kNoAuthURL.
+  // One LOAD_STOP event for kAuthURL and second for kNoAuthURL.
   WindowedLoadStopObserver load_stop_waiter(controller, 2);
   WindowedAuthNeededObserver auth_needed_waiter(controller);
   browser()->OpenURL(OpenURLParams(kAuthURL, Referrer(),
                                    WindowOpenDisposition::CURRENT_TAB,
                                    ui::PAGE_TRANSITION_TYPED, false));
-  auth_needed_waiter.Wait();
   WindowedAuthCancelledObserver auth_cancelled_waiter(controller);
   // Navigating while auth is requested is the same as cancelling.
-  browser()->OpenURL(OpenURLParams(kNoAuthURL, Referrer(),
-                                   WindowOpenDisposition::CURRENT_TAB,
-                                   ui::PAGE_TRANSITION_TYPED, false));
+  ui_test_utils::NavigateToURL(browser(), kNoAuthURL);
   auth_cancelled_waiter.Wait();
   load_stop_waiter.Wait();
   EXPECT_TRUE(observer.handlers().empty());
@@ -508,7 +474,6 @@
   ASSERT_TRUE(embedded_test_server()->Start());
   const GURL kAuthURL = embedded_test_server()->GetURL(kAuthBasicPage);
   const GURL kNoAuthURL1 = embedded_test_server()->GetURL(kNoAuthPage1);
-  const GURL kNoAuthURL2 = embedded_test_server()->GetURL(kNoAuthPage2);
 
   NavigationController* controller =
       &browser()->tab_strip_model()->GetActiveWebContents()->GetController();
@@ -516,28 +481,18 @@
   LoginPromptBrowserTestObserver observer;
   observer.Register(content::Source<NavigationController>(controller));
 
+  ui_test_utils::NavigateToURL(browser(), kAuthURL);
   ui_test_utils::NavigateToURL(browser(), kNoAuthURL1);
-
-  // Now add a page and go back, so we have something to go forward to.
-  ui_test_utils::NavigateToURL(browser(), kNoAuthURL2);
-  {
-    WindowedLoadStopObserver load_stop_waiter(controller, 1);
-    ASSERT_TRUE(controller->CanGoBack());
-    controller->GoBack();
-    load_stop_waiter.Wait();
-  }
-
-  WindowedLoadStopObserver load_stop_waiter(controller, 1);
+  ASSERT_TRUE(controller->CanGoBack());
   WindowedAuthNeededObserver auth_needed_waiter(controller);
-  browser()->OpenURL(OpenURLParams(kAuthURL, Referrer(),
-                                   WindowOpenDisposition::CURRENT_TAB,
-                                   ui::PAGE_TRANSITION_TYPED, false));
+  controller->GoBack();
   auth_needed_waiter.Wait();
+
+  // Go forward and test that the login prompt is cancelled.
   WindowedAuthCancelledObserver auth_cancelled_waiter(controller);
   ASSERT_TRUE(controller->CanGoForward());
   controller->GoForward();
   auth_cancelled_waiter.Wait();
-  load_stop_waiter.Wait();
   EXPECT_TRUE(observer.handlers().empty());
 }
 
@@ -1354,30 +1309,24 @@
   // www.a.com and end up displaying an auth interstitial and the URL for
   // www.b.com.
   WindowedAuthCancelledObserver auth_cancelled_waiter(controller);
-  EXPECT_TRUE(content::ExecuteScript(
-      contents, std::string("document.location='") + page2.spec() + "';"));
-  auth_cancelled_waiter.Wait();
-  content::WaitForInterstitialDetach(contents);
-  // Wait for the auth dialog and the interstitial for www.b.com.
-  WindowedAuthNeededObserver auth_needed_waiter(controller);
-  auth_needed_waiter.Wait();
-  ASSERT_EQ(1u, observer.handlers().size());
-  content::WaitForInterstitialAttach(contents);
+  {
+    WindowedLoadStopObserver load_stop_observer(controller, 1);
+    EXPECT_TRUE(content::ExecuteScript(
+        contents, std::string("document.location='") + page2.spec() + "';"));
+    auth_cancelled_waiter.Wait();
+    // Wait for the auth dialog and the interstitial for www.b.com.
+    WindowedAuthNeededObserver auth_needed_waiter(controller);
+    auth_needed_waiter.Wait();
+    ASSERT_EQ(1u, observer.handlers().size());
+    load_stop_observer.Wait();
+  }
 
-  EXPECT_TRUE(contents->ShowingInterstitialPage());
-  EXPECT_EQ(LoginInterstitialDelegate::kTypeForTesting,
-            contents->GetInterstitialPage()
-                ->GetDelegateForTesting()
-                ->GetTypeForTesting());
   EXPECT_EQ("www.b.com", contents->GetVisibleURL().host());
 
-  // Cancel auth dialog for www.b.com and wait for the interstitial to detach.
+  // Cancel auth dialog for www.b.com.
   LoginHandler* handler = *observer.handlers().begin();
-  content::RunTaskAndWaitForInterstitialDetach(
-      contents,
-      base::BindOnce(&LoginHandler::CancelAuth, base::Unretained(handler)));
+  handler->CancelAuth();
   EXPECT_EQ("www.b.com", contents->GetVisibleURL().host());
-  EXPECT_FALSE(contents->ShowingInterstitialPage());
 }
 
 // Test the scenario where proceeding through a different type of interstitial
@@ -1424,11 +1373,6 @@
     // The omnibox should show the correct origin while the login prompt is
     // being displayed.
     EXPECT_EQ("127.0.0.1", contents->GetVisibleURL().host());
-    EXPECT_TRUE(contents->ShowingInterstitialPage());
-    EXPECT_EQ(LoginInterstitialDelegate::kTypeForTesting,
-              contents->GetInterstitialPage()
-                  ->GetDelegateForTesting()
-                  ->GetTypeForTesting());
 
     // Cancelling the login prompt should detach the interstitial while keeping
     // the correct origin.
@@ -1484,19 +1428,10 @@
     ASSERT_TRUE(contents->GetURL().SchemeIs("http"));
     auth_needed_waiter.Wait();
     ASSERT_EQ(1u, observer.handlers().size());
-    content::WaitForInterstitialAttach(contents);
-    ASSERT_TRUE(contents->ShowingInterstitialPage());
-    EXPECT_EQ(LoginInterstitialDelegate::kTypeForTesting,
-              contents->GetInterstitialPage()
-                  ->GetDelegateForTesting()
-                  ->GetTypeForTesting());
     // Cancel the auth prompt. This commits the navigation.
     LoginHandler* handler = *observer.handlers().begin();
-    content::RunTaskAndWaitForInterstitialDetach(
-        contents,
-        base::BindOnce(&LoginHandler::CancelAuth, base::Unretained(handler)));
+    handler->CancelAuth();
     EXPECT_EQ("127.0.0.1", contents->GetVisibleURL().host());
-    EXPECT_FALSE(contents->ShowingInterstitialPage());
     EXPECT_EQ(auth_url, contents->GetLastCommittedURL());
   }
 
@@ -1507,17 +1442,7 @@
     ui_test_utils::NavigateToURL(browser(), broken_ssl_page);
     ASSERT_EQ("127.0.0.1", contents->GetURL().host());
     ASSERT_TRUE(contents->GetURL().SchemeIs("https"));
-    if (AreSSLCommittedInterstitialsEnabled()) {
-      ASSERT_TRUE(WaitForRenderFrameReady(contents->GetMainFrame()));
-    } else {
-      content::WaitForInterstitialAttach(contents);
-      EXPECT_TRUE(contents->ShowingInterstitialPage());
-      EXPECT_EQ(SSLBlockingPage::kTypeForTesting,
-                contents->GetInterstitialPage()
-                    ->GetDelegateForTesting()
-                    ->GetTypeForTesting());
-      EXPECT_EQ(auth_url, contents->GetLastCommittedURL());
-    }
+    ASSERT_TRUE(WaitForRenderFrameReady(contents->GetMainFrame()));
   }
 
   // An overrideable SSL interstitial is now being displayed. Navigate to the
@@ -1533,15 +1458,13 @@
     ui_test_utils::NavigateToURL(browser(), auth_url);
     ASSERT_EQ("127.0.0.1", contents->GetURL().host());
     ASSERT_TRUE(contents->GetURL().SchemeIs("http"));
-    ASSERT_TRUE(contents->ShowingInterstitialPage());
 
     auth_needed_waiter.Wait();
     ASSERT_EQ(1u, observer.handlers().size());
-    content::WaitForInterstitialAttach(contents);
-    EXPECT_EQ(LoginInterstitialDelegate::kTypeForTesting,
-              contents->GetInterstitialPage()
-                  ->GetDelegateForTesting()
-                  ->GetTypeForTesting());
+    const base::string16 kExpectedTitle =
+        base::ASCIIToUTF16("Denied: Missing Authorization Header");
+    content::TitleWatcher title_watcher(contents, kExpectedTitle);
+    EXPECT_EQ(kExpectedTitle, title_watcher.WaitAndGetTitle());
   }
 }
 
@@ -1580,30 +1503,18 @@
 
   // Redirect to a broken SSL page. This redirect should not accidentally
   // proceed through the SSL interstitial.
-  if (AreSSLCommittedInterstitialsEnabled()) {
-    content::TestNavigationObserver observer(contents);
-    EXPECT_TRUE(content::ExecuteScript(
-        browser()->tab_strip_model()->GetActiveWebContents(),
-        std::string("window.location = '") + broken_ssl_page.spec() + "'"));
-    observer.Wait();
-  } else {
-    WindowedAuthCancelledObserver auth_cancelled_waiter(controller);
-    EXPECT_TRUE(content::ExecuteScript(
-        browser()->tab_strip_model()->GetActiveWebContents(),
-        std::string("window.location = '") + broken_ssl_page.spec() + "'"));
-    content::WaitForInterstitialAttach(contents);
-    auth_cancelled_waiter.Wait();
-    // If the interstitial was accidentally clicked through, this wait may time
-    // out.
-    EXPECT_TRUE(WaitForRenderFrameReady(
-        contents->GetInterstitialPage()->GetMainFrame()));
-  }
-
-  content::InterstitialPageDelegate* delegate =
-      GetInterstitialDelegate(contents);
-
-  EXPECT_TRUE(delegate);
-  EXPECT_EQ(SSLBlockingPage::kTypeForTesting, delegate->GetTypeForTesting());
+  content::TestNavigationObserver nav_observer(contents);
+  EXPECT_TRUE(content::ExecuteScript(
+      browser()->tab_strip_model()->GetActiveWebContents(),
+      std::string("window.location = '") + broken_ssl_page.spec() + "'"));
+  nav_observer.Wait();
+  security_interstitials::SecurityInterstitialTabHelper* helper =
+      security_interstitials::SecurityInterstitialTabHelper::FromWebContents(
+          contents);
+  ASSERT_TRUE(helper);
+  EXPECT_EQ(SSLBlockingPage::kTypeForTesting,
+            helper->GetBlockingPageForCurrentlyCommittedNavigationForTesting()
+                ->GetTypeForTesting());
 }
 
 // Test where Basic HTTP authentication is disabled.
@@ -1651,9 +1562,6 @@
 IN_PROC_BROWSER_TEST_F(
     LoginPromptBrowserTest,
     TestAuthChallengeCancelsNavigationWithCommittedInterstitials) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kHTTPAuthCommittedInterstitials);
   ASSERT_TRUE(embedded_test_server()->Start());
 
   content::WebContents* contents =
@@ -1677,10 +1585,7 @@
 // prompt is shown on top of a committed error page when there is a cross-origin
 // main-frame auth challenge.
 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest,
-                       PromptShowsWithCommittedInterstitials) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kHTTPAuthCommittedInterstitials);
+                       PromptWithCommittedInterstitials) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
   content::WebContents* contents =
@@ -1693,6 +1598,7 @@
   GURL test_page = embedded_test_server()->GetURL(kAuthBasicPage);
   ui_test_utils::NavigateToURL(browser(), test_page);
 
+  // Test that the 401 error page commits underneath the login prompt.
   const base::string16 kExpectedTitle =
       base::ASCIIToUTF16("Denied: Missing Authorization Header");
   content::TitleWatcher title_watcher(contents, kExpectedTitle);
@@ -1700,11 +1606,27 @@
 
   auth_needed_waiter.Wait();
   ASSERT_EQ(1u, observer.handlers().size());
+
+  // Test that credentials are handled correctly.
+  WindowedAuthSuppliedObserver auth_supplied_waiter(controller);
+  LoginHandler* handler = *observer.handlers().begin();
+  SetAuthFor(handler);
+  auth_supplied_waiter.Wait();
+
+  base::string16 expected_title = ExpectedTitleFromAuth(
+      base::ASCIIToUTF16("basicuser"), base::ASCIIToUTF16("secret"));
+  content::TitleWatcher auth_supplied_title_watcher(contents, expected_title);
+  EXPECT_EQ(expected_title, auth_supplied_title_watcher.WaitAndGetTitle());
 }
 
 // Tests that FTP auth prompts do not appear when credentials have been
 // previously entered and cached.
 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, FtpAuthWithCache) {
+  // TODO(https://crbug.com/972188): support FTP auth with committed
+  // interstitials.
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndDisableFeature(
+      features::kHTTPAuthCommittedInterstitials);
   net::SpawnedTestServer ftp_server(
       net::SpawnedTestServer::TYPE_FTP,
       base::FilePath(FILE_PATH_LITERAL("chrome/test/data/ftp")));
diff --git a/chrome/browser/ui/login/login_tab_helper.cc b/chrome/browser/ui/login/login_tab_helper.cc
index 0afa787..4172184 100644
--- a/chrome/browser/ui/login/login_tab_helper.cc
+++ b/chrome/browser/ui/login/login_tab_helper.cc
@@ -7,18 +7,28 @@
 #include "base/feature_list.h"
 #include "chrome/browser/ui/login/login_handler.h"
 #include "chrome/common/chrome_features.h"
+#include "content/public/browser/browser_context.h"
 #include "content/public/browser/login_delegate.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/storage_partition.h"
 #include "content/public/browser/web_contents.h"
 #include "net/http/http_status_code.h"
 
 LoginTabHelper::~LoginTabHelper() {}
 
+void LoginTabHelper::DidStartNavigation(
+    content::NavigationHandle* navigation_handle) {
+  // When navigating away, the LoginHandler for the previous navigation (if any)
+  // should get cleared.
+  delegate_.reset();
+}
+
 void LoginTabHelper::DidFinishNavigation(
     content::NavigationHandle* navigation_handle) {
   DCHECK(
       base::FeatureList::IsEnabled(features::kHTTPAuthCommittedInterstitials));
+
   if (!navigation_handle->GetAuthChallengeInfo()) {
     return;
   }
@@ -33,6 +43,8 @@
     return;
   }
 
+  challenge_ = navigation_handle->GetAuthChallengeInfo().value();
+
   delegate_ = CreateLoginPrompt(
       navigation_handle->GetAuthChallengeInfo().value(),
       navigation_handle->GetWebContents(),
@@ -42,16 +54,36 @@
       // they are only used for passing the request to extensions, and that
       // doesn't happen in POST_COMMIT mode. This API needs to be cleaned up.
       nullptr, LoginHandler::POST_COMMIT,
-      base::BindOnce(&LoginTabHelper::HandleCredentials,
-                     base::Unretained(this)));
+      base::BindOnce(
+          &LoginTabHelper::HandleCredentials,
+          // Since the LoginTabHelper owns the |delegate_| that calls this
+          // callback, it's safe to use base::Unretained here; the |delegate_|
+          // cannot outlive its owning LoginTabHelper.
+          base::Unretained(this)));
 }
 
 LoginTabHelper::LoginTabHelper(content::WebContents* web_contents)
-    : content::WebContentsObserver(web_contents) {}
+    : content::WebContentsObserver(web_contents), weak_ptr_factory_(this) {}
 
 void LoginTabHelper::HandleCredentials(
     const base::Optional<net::AuthCredentials>& credentials) {
-  // TODO(https://crbug.com/963314): handle user-entered credentials here.
+  if (!credentials.has_value()) {
+    delegate_.reset();
+    return;
+  }
+  // Pass a weak pointer for the callback, as the WebContents (and thus this
+  // LoginTabHelper) could be destroyed while the network service is processing
+  // the new cache entry.
+  content::BrowserContext::GetDefaultStoragePartition(
+      web_contents()->GetBrowserContext())
+      ->GetNetworkContext()
+      ->AddAuthCacheEntry(challenge_, credentials.value(),
+                          base::BindOnce(&LoginTabHelper::Reload,
+                                         weak_ptr_factory_.GetWeakPtr()));
+}
+
+void LoginTabHelper::Reload() {
+  web_contents()->GetController().Reload(content::ReloadType::NORMAL, true);
 }
 
 WEB_CONTENTS_USER_DATA_KEY_IMPL(LoginTabHelper)
diff --git a/chrome/browser/ui/login/login_tab_helper.h b/chrome/browser/ui/login/login_tab_helper.h
index 8798e4df..c67be75 100644
--- a/chrome/browser/ui/login/login_tab_helper.h
+++ b/chrome/browser/ui/login/login_tab_helper.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_UI_LOGIN_LOGIN_TAB_HELPER_H_
 #define CHROME_BROWSER_UI_LOGIN_LOGIN_TAB_HELPER_H_
 
+#include "base/memory/weak_ptr.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
 
@@ -23,6 +24,8 @@
   ~LoginTabHelper() override;
 
   // content::WebContentsObserver:
+  void DidStartNavigation(
+      content::NavigationHandle* navigation_handle) override;
   void DidFinishNavigation(
       content::NavigationHandle* navigation_handle) override;
 
@@ -34,8 +37,18 @@
   void HandleCredentials(
       const base::Optional<net::AuthCredentials>& credentials);
 
+  // When the user enters credentials into the login prompt, they are populated
+  // in the auth cache and then page is reloaded to re-send the request with the
+  // cached credentials. This method is passed as the callback to the call that
+  // places the credentials into the cache.
+  void Reload();
+
   std::unique_ptr<content::LoginDelegate> delegate_;
 
+  net::AuthChallengeInfo challenge_;
+
+  base::WeakPtrFactory<LoginTabHelper> weak_ptr_factory_;
+
   WEB_CONTENTS_USER_DATA_KEY_DECL();
 
   DISALLOW_COPY_AND_ASSIGN(LoginTabHelper);
diff --git a/chrome/browser/ui/permission_bubble/permission_bubble_browser_test_util.cc b/chrome/browser/ui/permission_bubble/permission_bubble_browser_test_util.cc
index 29133cd..b9d1fab 100644
--- a/chrome/browser/ui/permission_bubble/permission_bubble_browser_test_util.cc
+++ b/chrome/browser/ui/permission_bubble/permission_bubble_browser_test_util.cc
@@ -59,7 +59,7 @@
       LoadExtension(test_data_dir_.AppendASCII("app_with_panel_container/"));
   CHECK(extension);
 
-  AppLaunchParams params(browser()->profile(), extension,
+  AppLaunchParams params(browser()->profile(), extension->id(),
                          extensions::LAUNCH_CONTAINER_PANEL_DEPRECATED,
                          WindowOpenDisposition::NEW_WINDOW,
                          extensions::SOURCE_TEST);
diff --git a/chrome/browser/ui/search/instant_controller.cc b/chrome/browser/ui/search/instant_controller.cc
index f2fe01a..e146612 100644
--- a/chrome/browser/ui/search/instant_controller.cc
+++ b/chrome/browser/ui/search/instant_controller.cc
@@ -84,6 +84,6 @@
       InstantServiceFactory::GetForProfile(profile_);
   if (instant_service) {
     instant_service->UpdateThemeInfo();
-    instant_service->UpdateMostVisitedItemsInfo();
+    instant_service->UpdateMostVisitedInfo();
   }
 }
diff --git a/chrome/browser/ui/search/instant_theme_browsertest.cc b/chrome/browser/ui/search/instant_theme_browsertest.cc
index dbc1b9d..8ede9f3 100644
--- a/chrome/browser/ui/search/instant_theme_browsertest.cc
+++ b/chrome/browser/ui/search/instant_theme_browsertest.cc
@@ -62,7 +62,7 @@
     }
   }
 
-  void MostVisitedItemsChanged(const InstantMostVisitedInfo&) override {}
+  void MostVisitedInfoChanged(const InstantMostVisitedInfo&) override {}
 
   InstantService* const service_;
 
diff --git a/chrome/browser/ui/search/local_ntp_browsertest.cc b/chrome/browser/ui/search/local_ntp_browsertest.cc
index abe95954..a222156 100644
--- a/chrome/browser/ui/search/local_ntp_browsertest.cc
+++ b/chrome/browser/ui/search/local_ntp_browsertest.cc
@@ -118,7 +118,7 @@
     TestInstantServiceObserver mv_observer(instant_service);
     // Make sure the observer knows about the current items. Typically, this
     // gets triggered by navigating to an NTP.
-    instant_service->UpdateMostVisitedItemsInfo();
+    instant_service->UpdateMostVisitedInfo();
     const int numDefaultMVItems =
         kDefaultMostVisitedItemCount +
         (base::FeatureList::IsEnabled(ntp_tiles::kDefaultSearchShortcut) ? 1
@@ -405,9 +405,6 @@
   histograms.ExpectTotalCount("NewTabPage.LoadTime.LocalNTP", 1);
   histograms.ExpectTotalCount("NewTabPage.LoadTime.LocalNTP.Google", 1);
   histograms.ExpectTotalCount("NewTabPage.LoadTime.MostVisited", 1);
-  histograms.ExpectTotalCount("NewTabPage.TilesReceivedTime", 1);
-  histograms.ExpectTotalCount("NewTabPage.TilesReceivedTime.LocalNTP", 1);
-  histograms.ExpectTotalCount("NewTabPage.TilesReceivedTime.MostVisited", 1);
 
   // Make sure impression metrics were recorded. There should be 1 tile, the
   // default prepopulated TopSites (see history::PrepopulatedPage).
@@ -457,9 +454,6 @@
   histograms.ExpectTotalCount("NewTabPage.LoadTime.LocalNTP", 1);
   histograms.ExpectTotalCount("NewTabPage.LoadTime.LocalNTP.Other", 1);
   histograms.ExpectTotalCount("NewTabPage.LoadTime.MostVisited", 1);
-  histograms.ExpectTotalCount("NewTabPage.TilesReceivedTime", 1);
-  histograms.ExpectTotalCount("NewTabPage.TilesReceivedTime.LocalNTP", 1);
-  histograms.ExpectTotalCount("NewTabPage.TilesReceivedTime.MostVisited", 1);
 
   // Make sure impression metrics were recorded. There should be 1 tile, the
   // default prepopulated TopSites (see history::PrepopulatedPage).
diff --git a/chrome/browser/ui/search/local_ntp_browsertest_base.cc b/chrome/browser/ui/search/local_ntp_browsertest_base.cc
index 01fafad..6a8971fa 100644
--- a/chrome/browser/ui/search/local_ntp_browsertest_base.cc
+++ b/chrome/browser/ui/search/local_ntp_browsertest_base.cc
@@ -124,7 +124,7 @@
   }
 }
 
-void TestInstantServiceObserver::MostVisitedItemsChanged(
+void TestInstantServiceObserver::MostVisitedInfoChanged(
     const InstantMostVisitedInfo& most_visited_info) {
   items_ = most_visited_info.items;
 
diff --git a/chrome/browser/ui/search/local_ntp_browsertest_base.h b/chrome/browser/ui/search/local_ntp_browsertest_base.h
index 33a2540d..dac42532 100644
--- a/chrome/browser/ui/search/local_ntp_browsertest_base.h
+++ b/chrome/browser/ui/search/local_ntp_browsertest_base.h
@@ -37,7 +37,7 @@
  private:
   void ThemeInfoChanged(const ThemeBackgroundInfo& theme_info) override;
 
-  void MostVisitedItemsChanged(
+  void MostVisitedInfoChanged(
       const InstantMostVisitedInfo& most_visited_info) override;
 
   InstantService* const service_;
diff --git a/chrome/browser/ui/search/local_ntp_js_browsertest.cc b/chrome/browser/ui/search/local_ntp_js_browsertest.cc
index c29829a..b639240 100644
--- a/chrome/browser/ui/search/local_ntp_js_browsertest.cc
+++ b/chrome/browser/ui/search/local_ntp_js_browsertest.cc
@@ -80,7 +80,7 @@
   // Run the tests.
   bool success = false;
   ASSERT_TRUE(instant_test_utils::GetBoolFromJS(
-      active_tab, "!!runSimpleTests('customBackgrounds')", &success));
+      active_tab, "!!runSimpleTests('customize')", &success));
   EXPECT_TRUE(success);
 }
 
diff --git a/chrome/browser/ui/search/ntp_user_data_logger.cc b/chrome/browser/ui/search/ntp_user_data_logger.cc
index 90f6733..eaaa481 100644
--- a/chrome/browser/ui/search/ntp_user_data_logger.cc
+++ b/chrome/browser/ui/search/ntp_user_data_logger.cc
@@ -336,9 +336,6 @@
   }
 
   switch (event) {
-    case NTP_ALL_TILES_RECEIVED:
-      tiles_received_time_ = time;
-      break;
     case NTP_ALL_TILES_LOADED:
       // permitted above for non-Google search providers
       break;
@@ -495,7 +492,6 @@
   if (from.is_valid() && to.is_valid() && (to == ntp_url_)) {
     DVLOG(1) << "Returning to New Tab Page";
     logged_impressions_.fill(base::nullopt);
-    tiles_received_time_ = base::TimeDelta();
     has_emitted_ = false;
     should_record_doodle_load_time_ = true;
   }
@@ -538,17 +534,12 @@
   DVLOG(1) << "Emitting NTP load time: " << load_time << ", "
            << "number of tiles: " << tiles_count;
 
-  UMA_HISTOGRAM_LOAD_TIME("NewTabPage.TilesReceivedTime", tiles_received_time_);
   UMA_HISTOGRAM_LOAD_TIME("NewTabPage.LoadTime", load_time);
 
   // Split between ML (aka SuggestionsService) and MV (aka TopSites).
   if (has_server_side_suggestions) {
-    UMA_HISTOGRAM_LOAD_TIME("NewTabPage.TilesReceivedTime.MostLikely",
-                            tiles_received_time_);
     UMA_HISTOGRAM_LOAD_TIME("NewTabPage.LoadTime.MostLikely", load_time);
   } else {
-    UMA_HISTOGRAM_LOAD_TIME("NewTabPage.TilesReceivedTime.MostVisited",
-                            tiles_received_time_);
     UMA_HISTOGRAM_LOAD_TIME("NewTabPage.LoadTime.MostVisited", load_time);
   }
 
@@ -559,14 +550,10 @@
 
   // Split between Web and Local.
   if (ntp_url_.SchemeIsHTTPOrHTTPS()) {
-    UMA_HISTOGRAM_LOAD_TIME("NewTabPage.TilesReceivedTime.Web",
-                            tiles_received_time_);
     UMA_HISTOGRAM_LOAD_TIME("NewTabPage.LoadTime.Web", load_time);
     // Only third-party NTPs can be loaded from the web.
     UMA_HISTOGRAM_LOAD_TIME("NewTabPage.LoadTime.Web.Other", load_time);
   } else {
-    UMA_HISTOGRAM_LOAD_TIME("NewTabPage.TilesReceivedTime.LocalNTP",
-                            tiles_received_time_);
     UMA_HISTOGRAM_LOAD_TIME("NewTabPage.LoadTime.LocalNTP", load_time);
     // Further split between Google and non-Google.
     if (is_google) {
@@ -578,12 +565,8 @@
 
   // Split between Startup and non-startup.
   if (during_startup_) {
-    UMA_HISTOGRAM_LOAD_TIME("NewTabPage.TilesReceivedTime.Startup",
-                            tiles_received_time_);
     UMA_HISTOGRAM_LOAD_TIME("NewTabPage.LoadTime.Startup", load_time);
   } else {
-    UMA_HISTOGRAM_LOAD_TIME("NewTabPage.TilesReceivedTime.NewTab",
-                            tiles_received_time_);
     UMA_HISTOGRAM_LOAD_TIME("NewTabPage.LoadTime.NewTab", load_time);
   }
 
diff --git a/chrome/browser/ui/search/ntp_user_data_logger.h b/chrome/browser/ui/search/ntp_user_data_logger.h
index 898c58f..538e683 100644
--- a/chrome/browser/ui/search/ntp_user_data_logger.h
+++ b/chrome/browser/ui/search/ntp_user_data_logger.h
@@ -115,9 +115,6 @@
              ntp_tiles::kMaxNumTiles>
       logged_impressions_;
 
-  // The time we received the NTP_ALL_TILES_RECEIVED event.
-  base::TimeDelta tiles_received_time_;
-
   // Whether we have already emitted NTP stats for this web contents.
   bool has_emitted_;
 
diff --git a/chrome/browser/ui/search/ntp_user_data_logger_unittest.cc b/chrome/browser/ui/search/ntp_user_data_logger_unittest.cc
index 8973fe4b..fdaea20 100644
--- a/chrome/browser/ui/search/ntp_user_data_logger_unittest.cc
+++ b/chrome/browser/ui/search/ntp_user_data_logger_unittest.cc
@@ -618,12 +618,8 @@
 
   TestNTPUserDataLogger logger(GURL("chrome://newtab/"));
 
-  base::TimeDelta delta_tiles_received = base::TimeDelta::FromMilliseconds(10);
   base::TimeDelta delta_tiles_loaded = base::TimeDelta::FromMilliseconds(100);
 
-  // Send the ALL_TILES_RECEIVED event.
-  logger.LogEvent(NTP_ALL_TILES_RECEIVED, delta_tiles_received);
-
   // Log a TOP_SITES impression (for the .MostVisited vs .MostLikely split in
   // the time histograms).
   logger.LogMostVisitedImpression(
@@ -633,34 +629,19 @@
   // Send the ALL_TILES_LOADED event, this should trigger emitting histograms.
   logger.LogEvent(NTP_ALL_TILES_LOADED, delta_tiles_loaded);
 
-  EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TilesReceivedTime"),
-              SizeIs(1));
-  EXPECT_THAT(histogram_tester.GetAllSamples(
-                  "NewTabPage.TilesReceivedTime.MostVisited"),
-              SizeIs(1));
-  EXPECT_THAT(
-      histogram_tester.GetAllSamples("NewTabPage.TilesReceivedTime.MostLikely"),
-      IsEmpty());
   EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.LoadTime"), SizeIs(1));
   EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.LoadTime.MostVisited"),
               SizeIs(1));
   EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.LoadTime.MostLikely"),
               IsEmpty());
 
-  histogram_tester.ExpectTimeBucketCount("NewTabPage.TilesReceivedTime",
-                                         delta_tiles_received, 1);
-  histogram_tester.ExpectTimeBucketCount(
-      "NewTabPage.TilesReceivedTime.MostVisited", delta_tiles_received, 1);
   histogram_tester.ExpectTimeBucketCount("NewTabPage.LoadTime",
                                          delta_tiles_loaded, 1);
   histogram_tester.ExpectTimeBucketCount("NewTabPage.LoadTime.MostVisited",
                                          delta_tiles_loaded, 1);
 
   // We should not log again for the same NTP.
-  logger.LogEvent(NTP_ALL_TILES_RECEIVED, delta_tiles_received);
   logger.LogEvent(NTP_ALL_TILES_LOADED, delta_tiles_loaded);
-  histogram_tester.ExpectTimeBucketCount("NewTabPage.TilesReceivedTime",
-                                         delta_tiles_received, 1);
   histogram_tester.ExpectTimeBucketCount("NewTabPage.LoadTime",
                                          delta_tiles_loaded, 1);
 
@@ -676,29 +657,15 @@
       0, TileSource::SUGGESTIONS_SERVICE, TileTitleSource::UNKNOWN,
       TileVisualType::THUMBNAIL));
 
-  base::TimeDelta delta_tiles_received2 = base::TimeDelta::FromMilliseconds(50);
   base::TimeDelta delta_tiles_loaded2 = base::TimeDelta::FromMilliseconds(500);
-  logger.LogEvent(NTP_ALL_TILES_RECEIVED, delta_tiles_received2);
   logger.LogEvent(NTP_ALL_TILES_LOADED, delta_tiles_loaded2);
 
-  EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TilesReceivedTime"),
-              SizeIs(2));
-  EXPECT_THAT(histogram_tester.GetAllSamples(
-                  "NewTabPage.TilesReceivedTime.MostVisited"),
-              SizeIs(1));
-  EXPECT_THAT(
-      histogram_tester.GetAllSamples("NewTabPage.TilesReceivedTime.MostLikely"),
-      SizeIs(1));
   EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.LoadTime"), SizeIs(2));
   EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.LoadTime.MostVisited"),
               SizeIs(1));
   EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.LoadTime.MostLikely"),
               SizeIs(1));
 
-  histogram_tester.ExpectTimeBucketCount("NewTabPage.TilesReceivedTime",
-                                         delta_tiles_received2, 1);
-  histogram_tester.ExpectTimeBucketCount(
-      "NewTabPage.TilesReceivedTime.MostLikely", delta_tiles_received2, 1);
   histogram_tester.ExpectTimeBucketCount("NewTabPage.LoadTime",
                                          delta_tiles_loaded2, 1);
   histogram_tester.ExpectTimeBucketCount("NewTabPage.LoadTime.MostLikely",
@@ -711,12 +678,8 @@
   TestNTPUserDataLogger logger((GURL(chrome::kChromeSearchLocalNtpUrl)));
   logger.is_google_ = true;
 
-  base::TimeDelta delta_tiles_received = base::TimeDelta::FromMilliseconds(10);
   base::TimeDelta delta_tiles_loaded = base::TimeDelta::FromMilliseconds(100);
 
-  // Send the ALL_TILES_RECEIVED event.
-  logger.LogEvent(NTP_ALL_TILES_RECEIVED, delta_tiles_received);
-
   // Send the ALL_TILES_LOADED event, this should trigger emitting histograms.
   logger.LogEvent(NTP_ALL_TILES_LOADED, delta_tiles_loaded);
 
@@ -754,12 +717,8 @@
   TestNTPUserDataLogger logger((GURL(chrome::kChromeSearchLocalNtpUrl)));
   logger.is_google_ = false;
 
-  base::TimeDelta delta_tiles_received = base::TimeDelta::FromMilliseconds(10);
   base::TimeDelta delta_tiles_loaded = base::TimeDelta::FromMilliseconds(100);
 
-  // Send the ALL_TILES_RECEIVED event.
-  logger.LogEvent(NTP_ALL_TILES_RECEIVED, delta_tiles_received);
-
   // Send the ALL_TILES_LOADED event, this should trigger emitting histograms.
   logger.LogEvent(NTP_ALL_TILES_LOADED, delta_tiles_loaded);
 
@@ -797,12 +756,8 @@
   TestNTPUserDataLogger logger(GURL("https://www.notgoogle.com/newtab"));
   logger.is_google_ = false;
 
-  base::TimeDelta delta_tiles_received = base::TimeDelta::FromMilliseconds(10);
   base::TimeDelta delta_tiles_loaded = base::TimeDelta::FromMilliseconds(100);
 
-  // Send the ALL_TILES_RECEIVED event.
-  logger.LogEvent(NTP_ALL_TILES_RECEIVED, delta_tiles_received);
-
   // Send the ALL_TILES_LOADED event, this should trigger emitting histograms.
   logger.LogEvent(NTP_ALL_TILES_LOADED, delta_tiles_loaded);
 
@@ -863,12 +818,8 @@
   TestNTPUserDataLogger logger((GURL(chrome::kChromeSearchLocalNtpUrl)));
   logger.is_custom_background_configured_ = true;
 
-  base::TimeDelta delta_tiles_received = base::TimeDelta::FromMilliseconds(10);
   base::TimeDelta delta_tiles_loaded = base::TimeDelta::FromMilliseconds(100);
 
-  // Send the ALL_TILES_RECEIVED event.
-  logger.LogEvent(NTP_ALL_TILES_RECEIVED, delta_tiles_received);
-
   // Send the ALL_TILES_LOADED event, this should trigger emitting histograms.
   logger.LogEvent(NTP_ALL_TILES_LOADED, delta_tiles_loaded);
 
@@ -913,12 +864,8 @@
   TestNTPUserDataLogger logger(local_ntp);
   logger.is_google_ = true;
 
-  base::TimeDelta delta_tiles_received = base::TimeDelta::FromMilliseconds(10);
   base::TimeDelta delta_tiles_loaded = base::TimeDelta::FromMilliseconds(100);
 
-  // Send the ALL_TILES_RECEIVED event.
-  logger.LogEvent(NTP_ALL_TILES_RECEIVED, delta_tiles_received);
-
   // Send the ALL_TILES_LOADED event, this should trigger emitting histograms.
   logger.LogEvent(NTP_ALL_TILES_LOADED, delta_tiles_loaded);
 
@@ -966,12 +913,8 @@
   TestNTPUserDataLogger logger(GURL("https://www.notgoogle.com/newtab"));
   logger.is_google_ = false;
 
-  base::TimeDelta delta_tiles_received = base::TimeDelta::FromMilliseconds(10);
   base::TimeDelta delta_tiles_loaded = base::TimeDelta::FromMilliseconds(100);
 
-  // Send the ALL_TILES_RECEIVED event.
-  logger.LogEvent(NTP_ALL_TILES_RECEIVED, delta_tiles_received);
-
   // Send the ALL_TILES_LOADED event, this should trigger emitting histograms.
   logger.LogEvent(NTP_ALL_TILES_LOADED, delta_tiles_loaded);
 
diff --git a/chrome/browser/ui/search/search_ipc_router.cc b/chrome/browser/ui/search/search_ipc_router.cc
index ecf7379..33b5703 100644
--- a/chrome/browser/ui/search/search_ipc_router.cc
+++ b/chrome/browser/ui/search/search_ipc_router.cc
@@ -123,12 +123,12 @@
   embedded_search_client()->FocusChanged(state, reason);
 }
 
-void SearchIPCRouter::SendMostVisitedItems(
+void SearchIPCRouter::SendMostVisitedInfo(
     const InstantMostVisitedInfo& most_visited_info) {
-  if (!policy_->ShouldSendMostVisitedItems())
+  if (!policy_->ShouldSendMostVisitedInfo())
     return;
 
-  embedded_search_client()->MostVisitedChanged(most_visited_info);
+  embedded_search_client()->MostVisitedInfoChanged(most_visited_info);
 }
 
 void SearchIPCRouter::SendThemeBackgroundInfo(
diff --git a/chrome/browser/ui/search/search_ipc_router.h b/chrome/browser/ui/search/search_ipc_router.h
index 37d27c9a..7a8c911 100644
--- a/chrome/browser/ui/search/search_ipc_router.h
+++ b/chrome/browser/ui/search/search_ipc_router.h
@@ -176,7 +176,7 @@
     virtual bool ShouldProcessPasteIntoOmnibox(bool is_active_tab) = 0;
     virtual bool ShouldSendSetInputInProgress(bool is_active_tab) = 0;
     virtual bool ShouldSendOmniboxFocusChanged() = 0;
-    virtual bool ShouldSendMostVisitedItems() = 0;
+    virtual bool ShouldSendMostVisitedInfo() = 0;
     virtual bool ShouldSendThemeBackgroundInfo() = 0;
     virtual bool ShouldProcessSetCustomBackgroundURLWithAttributions() = 0;
     virtual bool ShouldProcessSelectLocalBackgroundImage() = 0;
@@ -216,7 +216,7 @@
                            OmniboxFocusChangeReason reason);
 
   // Tells the renderer about the most visited items.
-  void SendMostVisitedItems(const InstantMostVisitedInfo& most_visited_info);
+  void SendMostVisitedInfo(const InstantMostVisitedInfo& most_visited_info);
 
   // Tells the renderer about the current theme background.
   void SendThemeBackgroundInfo(const ThemeBackgroundInfo& theme_info);
diff --git a/chrome/browser/ui/search/search_ipc_router_policy_impl.cc b/chrome/browser/ui/search/search_ipc_router_policy_impl.cc
index dd3b8c5..b8215336 100644
--- a/chrome/browser/ui/search/search_ipc_router_policy_impl.cc
+++ b/chrome/browser/ui/search/search_ipc_router_policy_impl.cc
@@ -87,7 +87,7 @@
   return !is_incognito_;
 }
 
-bool SearchIPCRouterPolicyImpl::ShouldSendMostVisitedItems() {
+bool SearchIPCRouterPolicyImpl::ShouldSendMostVisitedInfo() {
   return !is_incognito_ && search::IsInstantNTP(web_contents_);
 }
 
diff --git a/chrome/browser/ui/search/search_ipc_router_policy_impl.h b/chrome/browser/ui/search/search_ipc_router_policy_impl.h
index 7ea8e9a..5e88ce6 100644
--- a/chrome/browser/ui/search/search_ipc_router_policy_impl.h
+++ b/chrome/browser/ui/search/search_ipc_router_policy_impl.h
@@ -43,7 +43,7 @@
   bool ShouldProcessPasteIntoOmnibox(bool is_active_tab) override;
   bool ShouldSendSetInputInProgress(bool is_active_tab) override;
   bool ShouldSendOmniboxFocusChanged() override;
-  bool ShouldSendMostVisitedItems() override;
+  bool ShouldSendMostVisitedInfo() override;
   bool ShouldSendThemeBackgroundInfo() override;
   bool ShouldProcessSetCustomBackgroundURLWithAttributions() override;
   bool ShouldProcessSelectLocalBackgroundImage() override;
diff --git a/chrome/browser/ui/search/search_ipc_router_policy_unittest.cc b/chrome/browser/ui/search/search_ipc_router_policy_unittest.cc
index 4a6194a8..dc9a6529 100644
--- a/chrome/browser/ui/search/search_ipc_router_policy_unittest.cc
+++ b/chrome/browser/ui/search/search_ipc_router_policy_unittest.cc
@@ -141,20 +141,20 @@
 
   SearchIPCRouter::Policy* router_policy = GetSearchIPCRouterPolicy();
   EXPECT_FALSE(router_policy->ShouldSendThemeBackgroundInfo());
-  EXPECT_FALSE(router_policy->ShouldSendMostVisitedItems());
+  EXPECT_FALSE(router_policy->ShouldSendMostVisitedInfo());
   EXPECT_FALSE(router_policy->ShouldSendSetInputInProgress(true));
   EXPECT_FALSE(router_policy->ShouldSendOmniboxFocusChanged());
 }
 
-TEST_F(SearchIPCRouterPolicyTest, SendMostVisitedItems) {
+TEST_F(SearchIPCRouterPolicyTest, SendMostVisitedInfo) {
   NavigateAndCommitActiveTab(GURL(chrome::kChromeSearchLocalNtpUrl));
-  EXPECT_TRUE(GetSearchIPCRouterPolicy()->ShouldSendMostVisitedItems());
+  EXPECT_TRUE(GetSearchIPCRouterPolicy()->ShouldSendMostVisitedInfo());
 }
 
-TEST_F(SearchIPCRouterPolicyTest, DoNotSendMostVisitedItems) {
+TEST_F(SearchIPCRouterPolicyTest, DoNotSendMostVisitedInfo) {
   // Send most visited items only if the current tab is an Instant NTP.
   NavigateAndCommitActiveTab(GURL("chrome-search://foo/bar"));
-  EXPECT_FALSE(GetSearchIPCRouterPolicy()->ShouldSendMostVisitedItems());
+  EXPECT_FALSE(GetSearchIPCRouterPolicy()->ShouldSendMostVisitedInfo());
 }
 
 TEST_F(SearchIPCRouterPolicyTest, SendThemeBackgroundInfo) {
diff --git a/chrome/browser/ui/search/search_ipc_router_unittest.cc b/chrome/browser/ui/search/search_ipc_router_unittest.cc
index 0c5dc150..c81fe96 100644
--- a/chrome/browser/ui/search/search_ipc_router_unittest.cc
+++ b/chrome/browser/ui/search/search_ipc_router_unittest.cc
@@ -130,7 +130,7 @@
   MOCK_METHOD0(ShouldProcessOptOutOfSearchSuggestions, bool());
   MOCK_METHOD1(ShouldSendSetInputInProgress, bool(bool));
   MOCK_METHOD0(ShouldSendOmniboxFocusChanged, bool());
-  MOCK_METHOD0(ShouldSendMostVisitedItems, bool());
+  MOCK_METHOD0(ShouldSendMostVisitedInfo, bool());
   MOCK_METHOD0(ShouldSendThemeBackgroundInfo, bool());
   MOCK_METHOD0(ShouldProcessThemeChangeMessages, bool());
 };
@@ -766,28 +766,29 @@
   GetSearchIPCRouter().SetInputInProgress(true);
 }
 
-TEST_F(SearchIPCRouterTest, SendMostVisitedItemsMsg) {
+TEST_F(SearchIPCRouterTest, SendMostVisitedInfoMsg) {
   NavigateAndCommitActiveTab(GURL(chrome::kChromeSearchLocalNtpUrl));
   SetupMockDelegateAndPolicy();
   MockSearchIPCRouterPolicy* policy = GetSearchIPCRouterPolicy();
-  EXPECT_CALL(*policy, ShouldSendMostVisitedItems())
+  EXPECT_CALL(*policy, ShouldSendMostVisitedInfo())
       .Times(1)
       .WillOnce(Return(true));
 
-  EXPECT_CALL(*mock_embedded_search_client(), MostVisitedChanged(_));
-  GetSearchIPCRouter().SendMostVisitedItems(InstantMostVisitedInfo());
+  EXPECT_CALL(*mock_embedded_search_client(), MostVisitedInfoChanged(_));
+  GetSearchIPCRouter().SendMostVisitedInfo(InstantMostVisitedInfo());
 }
 
-TEST_F(SearchIPCRouterTest, DoNotSendMostVisitedItemsMsg) {
+TEST_F(SearchIPCRouterTest, DoNotSendMostVisitedInfoMsg) {
   NavigateAndCommitActiveTab(GURL(chrome::kChromeSearchLocalNtpUrl));
   SetupMockDelegateAndPolicy();
   MockSearchIPCRouterPolicy* policy = GetSearchIPCRouterPolicy();
-  EXPECT_CALL(*policy, ShouldSendMostVisitedItems())
+  EXPECT_CALL(*policy, ShouldSendMostVisitedInfo())
       .Times(1)
       .WillOnce(Return(false));
 
-  EXPECT_CALL(*mock_embedded_search_client(), MostVisitedChanged(_)).Times(0);
-  GetSearchIPCRouter().SendMostVisitedItems(InstantMostVisitedInfo());
+  EXPECT_CALL(*mock_embedded_search_client(), MostVisitedInfoChanged(_))
+      .Times(0);
+  GetSearchIPCRouter().SendMostVisitedInfo(InstantMostVisitedInfo());
 }
 
 TEST_F(SearchIPCRouterTest, SendThemeBackgroundInfoMsg) {
diff --git a/chrome/browser/ui/search/search_tab_helper.cc b/chrome/browser/ui/search/search_tab_helper.cc
index d9d3264..3b6dc891 100644
--- a/chrome/browser/ui/search/search_tab_helper.cc
+++ b/chrome/browser/ui/search/search_tab_helper.cc
@@ -237,9 +237,9 @@
   ipc_router_.SendThemeBackgroundInfo(theme_info);
 }
 
-void SearchTabHelper::MostVisitedItemsChanged(
+void SearchTabHelper::MostVisitedInfoChanged(
     const InstantMostVisitedInfo& most_visited_info) {
-  ipc_router_.SendMostVisitedItems(most_visited_info);
+  ipc_router_.SendMostVisitedInfo(most_visited_info);
 }
 
 void SearchTabHelper::FocusOmnibox(bool focus) {
diff --git a/chrome/browser/ui/search/search_tab_helper.h b/chrome/browser/ui/search/search_tab_helper.h
index 1e16472..97a6380 100644
--- a/chrome/browser/ui/search/search_tab_helper.h
+++ b/chrome/browser/ui/search/search_tab_helper.h
@@ -131,7 +131,7 @@
 
   // Overridden from InstantServiceObserver:
   void ThemeInfoChanged(const ThemeBackgroundInfo& theme_info) override;
-  void MostVisitedItemsChanged(
+  void MostVisitedInfoChanged(
       const InstantMostVisitedInfo& most_visited_info) override;
 
   // Overridden from SelectFileDialog::Listener:
diff --git a/chrome/browser/ui/views/autofill/payments/card_unmask_prompt_views.cc b/chrome/browser/ui/views/autofill/payments/card_unmask_prompt_views.cc
index cc4390f..b5a226d 100644
--- a/chrome/browser/ui/views/autofill/payments/card_unmask_prompt_views.cc
+++ b/chrome/browser/ui/views/autofill/payments/card_unmask_prompt_views.cc
@@ -47,6 +47,26 @@
 
 namespace autofill {
 
+namespace {
+
+static views::GridLayout* ResetOverlayLayout(views::View* overlay) {
+  views::GridLayout* overlay_layout =
+      overlay->SetLayoutManager(std::make_unique<views::GridLayout>(overlay));
+  views::ColumnSet* columns = overlay_layout->AddColumnSet(0);
+  // The throbber's checkmark is 18dp.
+  columns->AddColumn(views::GridLayout::TRAILING, views::GridLayout::CENTER,
+                     0.5, views::GridLayout::FIXED, 18, 0);
+  columns->AddPaddingColumn(views::GridLayout::kFixedSize,
+                            ChromeLayoutProvider::Get()->GetDistanceMetric(
+                                views::DISTANCE_RELATED_LABEL_HORIZONTAL));
+  columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER, 0.5,
+                     views::GridLayout::USE_PREF, 0, 0);
+  overlay_layout->StartRow(1.0, 0);
+  return overlay_layout;
+}
+
+}  // namespace
+
 CardUnmaskPromptViews::CardUnmaskPromptViews(
     CardUnmaskPromptController* controller,
     content::WebContents* web_contents)
@@ -115,7 +135,7 @@
 
       // Rows cannot be replaced in GridLayout, so we reset it.
       overlay_->RemoveAllChildViews(/*delete_children=*/true);
-      views::GridLayout* layout = ResetOverlayLayout();
+      views::GridLayout* layout = ResetOverlayLayout(overlay_);
 
       // The label of the overlay will now show the error in red.
       views::Label* error_label = new views::Label(error_message);
@@ -357,65 +377,66 @@
   set_margins(ChromeLayoutProvider::Get()->GetDialogInsetsForContentType(
       views::TEXT, views::CONTROL));
 
-  controls_container_ = new views::View();
-  controls_container_->SetLayoutManager(std::make_unique<views::BoxLayout>(
+  auto controls_container = std::make_unique<views::View>();
+  controls_container->SetLayoutManager(std::make_unique<views::BoxLayout>(
       views::BoxLayout::kVertical, gfx::Insets(),
       provider->GetDistanceMetric(views::DISTANCE_UNRELATED_CONTROL_VERTICAL)));
-  AddChildView(controls_container_);
+  controls_container_ = AddChildView(std::move(controls_container));
 
   // Instruction text of the dialog.
-  instructions_ = new views::Label(controller_->GetInstructionsMessage());
-  instructions_->SetEnabledColor(views::style::GetColor(
-      *instructions_, ChromeTextContext::CONTEXT_BODY_TEXT_LARGE,
+  auto instructions =
+      std::make_unique<views::Label>(controller_->GetInstructionsMessage());
+  instructions->SetEnabledColor(views::style::GetColor(
+      *instructions.get(), ChromeTextContext::CONTEXT_BODY_TEXT_LARGE,
       STYLE_SECONDARY));
-  instructions_->SetMultiLine(true);
-  instructions_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-  controls_container_->AddChildView(instructions_);
+  instructions->SetMultiLine(true);
+  instructions->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+  instructions_ = controls_container_->AddChildView(std::move(instructions));
 
   // The input container is a vertical box layout containing the input row and
   // the temporary error label. They are separated by a related distance.
-  views::View* input_container = new views::View();
+  auto input_container = std::make_unique<views::View>();
   input_container->SetLayoutManager(std::make_unique<views::BoxLayout>(
       views::BoxLayout::kVertical, gfx::Insets(),
       provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL)));
 
   // Input row, containing month/year dropdowns if needed and the CVC field.
-  input_row_ = new views::View();
-  input_row_->SetLayoutManager(std::make_unique<views::BoxLayout>(
+  auto input_row = std::make_unique<views::View>();
+  input_row->SetLayoutManager(std::make_unique<views::BoxLayout>(
       views::BoxLayout::kHorizontal, gfx::Insets(),
       provider->GetDistanceMetric(DISTANCE_RELATED_CONTROL_HORIZONTAL_SMALL)));
 
   // Add the month and year comboboxes if the expiration date is needed.
-  month_input_ = new views::Combobox(&month_combobox_model_);
-  month_input_->set_listener(this);
-  month_input_->SetAccessibleName(
+  auto month_input = std::make_unique<views::Combobox>(&month_combobox_model_);
+  month_input->set_listener(this);
+  month_input->SetAccessibleName(
       l10n_util::GetStringUTF16(IDS_AUTOFILL_CARD_UNMASK_EXPIRATION_MONTH));
-  input_row_->AddChildView(month_input_);
-  year_input_ = new views::Combobox(&year_combobox_model_);
-  year_input_->set_listener(this);
-  year_input_->SetAccessibleName(
+  month_input_ = input_row->AddChildView(std::move(month_input));
+  auto year_input = std::make_unique<views::Combobox>(&year_combobox_model_);
+  year_input->set_listener(this);
+  year_input->SetAccessibleName(
       l10n_util::GetStringUTF16(IDS_AUTOFILL_CARD_UNMASK_EXPIRATION_YEAR));
-  input_row_->AddChildView(year_input_);
+  year_input_ = input_row->AddChildView(std::move(year_input));
   if (!controller_->ShouldRequestExpirationDate()) {
     month_input_->SetVisible(false);
     year_input_->SetVisible(false);
   }
 
-  cvc_input_ = CreateCvcTextfield();
-  cvc_input_->set_controller(this);
-  input_row_->AddChildView(cvc_input_);
+  std::unique_ptr<views::Textfield> cvc_input = CreateCvcTextfield();
+  cvc_input->set_controller(this);
+  cvc_input_ = input_row->AddChildView(std::move(cvc_input));
 
-  views::ImageView* cvc_image = new views::ImageView();
+  auto cvc_image = std::make_unique<views::ImageView>();
   cvc_image->SetImage(rb.GetImageSkiaNamed(controller_->GetCvcImageRid()));
   cvc_image->set_tooltip_text(l10n_util::GetStringUTF16(
       IDS_AUTOFILL_CARD_UNMASK_CVC_IMAGE_DESCRIPTION));
-  input_row_->AddChildView(cvc_image);
-  input_container->AddChildView(input_row_);
+  input_row->AddChildView(std::move(cvc_image));
+  input_row_ = input_container->AddChildView(std::move(input_row));
 
   // Temporary error view, just below the input field(s).
-  temporary_error_ = new views::View();
+  auto temporary_error = std::make_unique<views::View>();
   auto* temporary_error_layout =
-      temporary_error_->SetLayoutManager(std::make_unique<views::BoxLayout>(
+      temporary_error->SetLayoutManager(std::make_unique<views::BoxLayout>(
           views::BoxLayout::kHorizontal, gfx::Insets(),
           provider->GetDistanceMetric(
               views::DISTANCE_RELATED_LABEL_HORIZONTAL)));
@@ -424,26 +445,26 @@
 
   const SkColor warning_text_color = views::style::GetColor(
       *instructions_, ChromeTextContext::CONTEXT_BODY_TEXT_SMALL, STYLE_RED);
-  views::ImageView* error_icon = new views::ImageView();
+  auto error_icon = std::make_unique<views::ImageView>();
   error_icon->SetImage(
       gfx::CreateVectorIcon(kBrowserToolsErrorIcon, warning_text_color));
-  temporary_error_->SetVisible(false);
-  temporary_error_->AddChildView(error_icon);
+  temporary_error->SetVisible(false);
+  temporary_error->AddChildView(std::move(error_icon));
 
-  error_label_ = new views::Label();
-  error_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-  error_label_->SetEnabledColor(warning_text_color);
-  temporary_error_->AddChildView(error_label_);
+  auto error_label = std::make_unique<views::Label>();
+  error_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+  error_label->SetEnabledColor(warning_text_color);
+  error_label_ = temporary_error->AddChildView(std::move(error_label));
   temporary_error_layout->SetFlexForView(error_label_, 1);
-  input_container->AddChildView(temporary_error_);
+  temporary_error_ = input_container->AddChildView(std::move(temporary_error));
 
-  controls_container_->AddChildView(input_container);
+  controls_container_->AddChildView(std::move(input_container));
 
   // On top of the main contents, we add the progress/error overlay and hide it.
   // A child view will be added to it when about to be shown.
-  overlay_ = new views::View();
-  views::GridLayout* overlay_layout = ResetOverlayLayout();
-  overlay_->SetVisible(false);
+  auto overlay = std::make_unique<views::View>();
+  views::GridLayout* overlay_layout = ResetOverlayLayout(overlay.get());
+  overlay->SetVisible(false);
 
   progress_throbber_ = new views::Throbber();
   overlay_layout->AddView(progress_throbber_);
@@ -455,7 +476,7 @@
           ui::NativeTheme::kColorId_ThrobberSpinningColor));
   overlay_layout->AddView(overlay_label_);
 
-  AddChildView(overlay_);
+  overlay_ = AddChildView(std::move(overlay));
 }
 
 bool CardUnmaskPromptViews::ExpirationDateIsValid() const {
@@ -471,22 +492,6 @@
   GetWidget()->Close();
 }
 
-views::GridLayout* CardUnmaskPromptViews::ResetOverlayLayout() {
-  views::GridLayout* overlay_layout =
-      overlay_->SetLayoutManager(std::make_unique<views::GridLayout>(overlay_));
-  views::ColumnSet* columns = overlay_layout->AddColumnSet(0);
-  // The throbber's checkmark is 18dp.
-  columns->AddColumn(views::GridLayout::TRAILING, views::GridLayout::CENTER,
-                     0.5, views::GridLayout::FIXED, 18, 0);
-  columns->AddPaddingColumn(views::GridLayout::kFixedSize,
-                            ChromeLayoutProvider::Get()->GetDistanceMetric(
-                                views::DISTANCE_RELATED_LABEL_HORIZONTAL));
-  columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER, 0.5,
-                     views::GridLayout::USE_PREF, 0, 0);
-  overlay_layout->StartRow(1.0, 0);
-  return overlay_layout;
-}
-
 CardUnmaskPromptView* CreateCardUnmaskPromptView(
     CardUnmaskPromptController* controller,
     content::WebContents* web_contents) {
diff --git a/chrome/browser/ui/views/autofill/payments/card_unmask_prompt_views.h b/chrome/browser/ui/views/autofill/payments/card_unmask_prompt_views.h
index e898960..e319998 100644
--- a/chrome/browser/ui/views/autofill/payments/card_unmask_prompt_views.h
+++ b/chrome/browser/ui/views/autofill/payments/card_unmask_prompt_views.h
@@ -22,7 +22,6 @@
 
 namespace views {
 class Checkbox;
-class GridLayout;
 class Label;
 class Link;
 class Textfield;
@@ -88,7 +87,6 @@
   void SetInputsEnabled(bool enabled);
   void ShowNewCardLink();
   void ClosePrompt();
-  views::GridLayout* ResetOverlayLayout();
 
   CardUnmaskPromptController* controller_;
   content::WebContents* web_contents_;
diff --git a/chrome/browser/ui/views/autofill/payments/payments_view_util.cc b/chrome/browser/ui/views/autofill/payments/payments_view_util.cc
index abdf78f..1c97543 100644
--- a/chrome/browser/ui/views/autofill/payments/payments_view_util.cc
+++ b/chrome/browser/ui/views/autofill/payments/payments_view_util.cc
@@ -4,8 +4,6 @@
 
 #include "chrome/browser/ui/views/autofill/payments/payments_view_util.h"
 
-#include <memory>
-
 #include "chrome/app/vector_icons/vector_icons.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
 #include "chrome/browser/ui/views/chrome_typography.h"
@@ -120,8 +118,8 @@
   return gfx::Size(0, 0);
 }
 
-views::Textfield* CreateCvcTextfield() {
-  views::Textfield* textfield = new views::Textfield();
+std::unique_ptr<views::Textfield> CreateCvcTextfield() {
+  auto textfield = std::make_unique<views::Textfield>();
   textfield->set_placeholder_text(
       l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_CVC));
   textfield->SetDefaultWidthInChars(8);
diff --git a/chrome/browser/ui/views/autofill/payments/payments_view_util.h b/chrome/browser/ui/views/autofill/payments/payments_view_util.h
index 6be97fee..c32f0720 100644
--- a/chrome/browser/ui/views/autofill/payments/payments_view_util.h
+++ b/chrome/browser/ui/views/autofill/payments/payments_view_util.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_UI_VIEWS_AUTOFILL_PAYMENTS_PAYMENTS_VIEW_UTIL_H_
 #define CHROME_BROWSER_UI_VIEWS_AUTOFILL_PAYMENTS_PAYMENTS_VIEW_UTIL_H_
 
+#include <memory>
+
 #include "base/strings/string16.h"
 #include "components/autofill/core/browser/payments/legal_message_line.h"
 #include "content/public/browser/web_contents.h"
@@ -35,7 +37,7 @@
 };
 
 // Creates and returns a small Textfield intended to be used for CVC entry.
-views::Textfield* CreateCvcTextfield();
+std::unique_ptr<views::Textfield> CreateCvcTextfield();
 
 // Defines a view with legal message. This class handles the legal message
 // parsing and the links clicking events.
diff --git a/chrome/browser/ui/views/chrome_browser_main_extra_parts_views.cc b/chrome/browser/ui/views/chrome_browser_main_extra_parts_views.cc
index 0661f59d..80b302e 100644
--- a/chrome/browser/ui/views/chrome_browser_main_extra_parts_views.cc
+++ b/chrome/browser/ui/views/chrome_browser_main_extra_parts_views.cc
@@ -26,7 +26,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/common/service_manager_connection.h"
 #include "services/service_manager/public/cpp/connector.h"
-#include "services/ws/public/cpp/gpu/gpu.h"  // nogncheck
+#include "services/viz/public/cpp/gpu/gpu.h"  // nogncheck
 #include "ui/display/screen.h"
 #include "ui/views/widget/desktop_aura/desktop_screen.h"
 #include "ui/wm/core/wm_state.h"
diff --git a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc
index 3397120..7af42ae 100644
--- a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc
+++ b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc
@@ -61,10 +61,7 @@
     DesktopMediaPickerViews* parent,
     std::vector<std::unique_ptr<DesktopMediaList>> source_lists)
     : parent_(parent),
-      modality_(params.modality),
-      description_label_(new views::Label()),
-      audio_share_checkbox_(nullptr),
-      tabbed_pane_(nullptr) {
+      modality_(params.modality) {
   const ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
 
   SetLayoutManager(std::make_unique<views::BoxLayout>(
@@ -72,9 +69,10 @@
       provider->GetDialogInsetsForContentType(views::TEXT, views::CONTROL),
       provider->GetDistanceMetric(DISTANCE_RELATED_CONTROL_VERTICAL_SMALL)));
 
-  description_label_->SetMultiLine(true);
-  description_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-  AddChildView(description_label_);
+  auto description_label = std::make_unique<views::Label>();
+  description_label->SetMultiLine(true);
+  description_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+  description_label_ = AddChildView(std::move(description_label));
 
   std::vector<std::pair<base::string16, std::unique_ptr<View>>> panes;
 
diff --git a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.h b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.h
index a3b58e50..f6f6ba1 100644
--- a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.h
+++ b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.h
@@ -67,11 +67,11 @@
   DesktopMediaPickerViews* parent_;
   ui::ModalType modality_;
 
-  views::Label* description_label_;
+  views::Label* description_label_ = nullptr;
 
-  views::Checkbox* audio_share_checkbox_;
+  views::Checkbox* audio_share_checkbox_ = nullptr;
 
-  views::TabbedPane* tabbed_pane_;
+  views::TabbedPane* tabbed_pane_ = nullptr;
   std::vector<std::unique_ptr<DesktopMediaListController>> list_controllers_;
   std::vector<content::DesktopMediaID::Type> source_types_;
 
diff --git a/chrome/browser/ui/views/frame/browser_window_property_manager_browsertest_win.cc b/chrome/browser/ui/views/frame/browser_window_property_manager_browsertest_win.cc
index c9d947e..ef8cb02 100644
--- a/chrome/browser/ui/views/frame/browser_window_property_manager_browsertest_win.cc
+++ b/chrome/browser/ui/views/frame/browser_window_property_manager_browsertest_win.cc
@@ -198,9 +198,10 @@
       LoadExtension(test_data_dir_.AppendASCII("app/"));
   EXPECT_TRUE(extension);
 
-  OpenApplication(AppLaunchParams(
-      browser()->profile(), extension, extensions::LAUNCH_CONTAINER_WINDOW,
-      WindowOpenDisposition::NEW_FOREGROUND_TAB, extensions::SOURCE_TEST));
+  OpenApplication(AppLaunchParams(browser()->profile(), extension->id(),
+                                  extensions::LAUNCH_CONTAINER_WINDOW,
+                                  WindowOpenDisposition::NEW_FOREGROUND_TAB,
+                                  extensions::SOURCE_TEST));
 
   // Check that the new browser has an app name.
   // The launch should have created a new browser.
diff --git a/chrome/browser/ui/views/intent_picker_bubble_view.cc b/chrome/browser/ui/views/intent_picker_bubble_view.cc
index f9b7e33a..acb60dc 100644
--- a/chrome/browser/ui/views/intent_picker_bubble_view.cc
+++ b/chrome/browser/ui/views/intent_picker_bubble_view.cc
@@ -346,10 +346,10 @@
       continue;
     }
 #endif  // defined(OS_CHROMEOS)
-    IntentPickerLabelButton* app_button = new IntentPickerLabelButton(
+    auto app_button = std::make_unique<IntentPickerLabelButton>(
         this, &app_info.icon, app_info.launch_name, app_info.display_name);
     app_button->set_tag(i);
-    scrollable_view->AddChildViewAt(app_button, i++);
+    scrollable_view->AddChildViewAt(std::move(app_button), i++);
   }
 
   // We should delete at most one entry, this is the case when Chrome is listed
diff --git a/chrome/browser/ui/views/payments/editor_view_controller.cc b/chrome/browser/ui/views/payments/editor_view_controller.cc
index e9b978a9..bd56d9ff 100644
--- a/chrome/browser/ui/views/payments/editor_view_controller.cc
+++ b/chrome/browser/ui/views/payments/editor_view_controller.cc
@@ -67,7 +67,7 @@
   error_label->SetMultiLine(true);
   error_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
 
-  view->AddChildView(error_label.release());
+  view->AddChildView(std::move(error_label));
   return view;
 }
 
diff --git a/chrome/browser/ui/views/profiles/user_manager_view.cc b/chrome/browser/ui/views/profiles/user_manager_view.cc
index 6d210a08..93ad5ff 100644
--- a/chrome/browser/ui/views/profiles/user_manager_view.cc
+++ b/chrome/browser/ui/views/profiles/user_manager_view.cc
@@ -54,7 +54,7 @@
 namespace {
 
 // An open User Manager window. There can only be one open at a time. This
-// is reset to NULL when the window is closed.
+// is reset to nullptr when the window is closed.
 UserManagerView* g_user_manager_view = nullptr;
 base::Closure* g_user_manager_shown_callback_for_testing = nullptr;
 bool g_is_user_manager_view_under_construction = false;
@@ -406,7 +406,7 @@
       ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE));
 
 #if defined(OS_WIN)
-  // Set the app id for the task manager to the app id of its parent
+  // Set the app id for the user manager to the app id of its parent.
   ui::win::SetAppIdForWindow(
       shell_integration::win::GetChromiumModelIdForProfile(
           system_profile->GetPath()),
@@ -470,7 +470,7 @@
   // (WindowClosing comes in asynchronously from the call to Close() and we
   // may have already opened a new instance).
   if (g_user_manager_view == this)
-    g_user_manager_view = NULL;
+    g_user_manager_view = nullptr;
 }
 
 bool UserManagerView::ShouldUseCustomFrame() const {
diff --git a/chrome/browser/ui/views/profiles/user_manager_view.h b/chrome/browser/ui/views/profiles/user_manager_view.h
index 8f1ccd2..2756550 100644
--- a/chrome/browser/ui/views/profiles/user_manager_view.h
+++ b/chrome/browser/ui/views/profiles/user_manager_view.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_UI_VIEWS_PROFILES_USER_MANAGER_VIEW_H_
 
 #include <memory>
+#include <string>
 
 #include "base/auto_reset.h"
 #include "base/macros.h"
diff --git a/chrome/browser/ui/views/task_manager_view_browsertest.cc b/chrome/browser/ui/views/task_manager_view_browsertest.cc
index 51c44a7..a8090b6 100644
--- a/chrome/browser/ui/views/task_manager_view_browsertest.cc
+++ b/chrome/browser/ui/views/task_manager_view_browsertest.cc
@@ -31,6 +31,7 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_delegate.h"
 #include "content/public/test/browser_test_utils.h"
+#include "content/public/test/no_renderer_crashes_assertion.h"
 #include "content/public/test/test_utils.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
@@ -310,11 +311,15 @@
   EXPECT_EQ(GetTable()->FirstSelectedRow(), FindRowForTab(tabs[1]));
   EXPECT_EQ(1UL, GetTable()->selection_model().size());
 
-  // Press the button, which kills the process of the selected row.
-  PressKillButton();
+  {
+    content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
 
-  // Two rows should disappear.
-  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows((rows -= 2), pattern));
+    // Press the button, which kills the process of the selected row.
+    PressKillButton();
+
+    // Two rows should disappear.
+    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows((rows -= 2), pattern));
+  }
 
   // A later row should now be selected. The selection should be after the 4
   // rows sharing the tabs[0] process, and it should be at or before
diff --git a/chrome/browser/ui/webui/chromeos/set_time_ui.cc b/chrome/browser/ui/webui/chromeos/set_time_ui.cc
index 9b872f0..9565984 100644
--- a/chrome/browser/ui/webui/chromeos/set_time_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/set_time_ui.cc
@@ -9,6 +9,7 @@
 #include <memory>
 
 #include "ash/public/cpp/login_screen.h"
+#include "ash/public/cpp/login_types.h"
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/build_time.h"
@@ -135,7 +136,8 @@
     ash::LoginScreen::Get()->ShowParentAccessWidget(
         account_id,
         base::BindRepeating(&SetTimeMessageHandler::OnParentAccessValidation,
-                            weak_factory_.GetWeakPtr()));
+                            weak_factory_.GetWeakPtr()),
+        ash::ParentAccessRequestReason::kChangeTime);
   }
 
   void OnParentAccessValidation(bool success) {
diff --git a/chrome/browser/ui/webui/ntp/app_launcher_handler.cc b/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
index d52d5c7..7566eb77 100644
--- a/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
+++ b/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
@@ -521,7 +521,7 @@
       disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB ||
       disposition == WindowOpenDisposition::NEW_WINDOW) {
     // TODO(jamescook): Proper support for background tabs.
-    AppLaunchParams params(profile, extension,
+    AppLaunchParams params(profile, extension_id,
                            disposition == WindowOpenDisposition::NEW_WINDOW
                                ? extensions::LAUNCH_CONTAINER_WINDOW
                                : extensions::LAUNCH_CONTAINER_TAB,
diff --git a/chrome/browser/ui/webui/welcome/welcome_ui.cc b/chrome/browser/ui/webui/welcome/welcome_ui.cc
index 6677f1d..a7b4644 100644
--- a/chrome/browser/ui/webui/welcome/welcome_ui.cc
+++ b/chrome/browser/ui/webui/welcome/welcome_ui.cc
@@ -166,6 +166,36 @@
                                    kOnboardingWelcomeResources[i].value);
     }
 
+#if defined(GOOGLE_CHROME_BUILD)
+    // Load unscaled images.
+    html_source->AddResourcePath("images/module_icons/google_dark.svg",
+                                 IDR_WELCOME_MODULE_ICONS_GOOGLE_DARK);
+    html_source->AddResourcePath("images/module_icons/google_light.svg",
+                                 IDR_WELCOME_MODULE_ICONS_GOOGLE_LIGHT);
+    html_source->AddResourcePath("images/module_icons/set_default_dark.svg",
+                                 IDR_WELCOME_MODULE_ICONS_SET_DEFAULT_DARK);
+    html_source->AddResourcePath("images/module_icons/set_default_light.svg",
+                                 IDR_WELCOME_MODULE_ICONS_SET_DEFAULT_LIGHT);
+    html_source->AddResourcePath("images/module_icons/wallpaper_dark.svg",
+                                 IDR_WELCOME_MODULE_ICONS_WALLPAPER_DARK);
+    html_source->AddResourcePath("images/module_icons/wallpaper_light.svg",
+                                 IDR_WELCOME_MODULE_ICONS_WALLPAPER_LIGHT);
+    html_source->AddResourcePath("images/ntp_thumbnails/art.jpg",
+                                 IDR_WELCOME_NTP_THUMBNAILS_ART);
+    html_source->AddResourcePath("images/ntp_thumbnails/cityscape.jpg",
+                                 IDR_WELCOME_NTP_THUMBNAILS_CITYSCAPE);
+    html_source->AddResourcePath("images/ntp_thumbnails/earth.jpg",
+                                 IDR_WELCOME_NTP_THUMBNAILS_EARTH);
+    html_source->AddResourcePath("images/ntp_thumbnails/geometric_shapes.jpg",
+                                 IDR_WELCOME_NTP_THUMBNAILS_GEOMETRIC_SHAPES);
+    html_source->AddResourcePath("images/ntp_thumbnails/landscape.jpg",
+                                 IDR_WELCOME_NTP_THUMBNAILS_LANDSCAPE);
+    html_source->AddResourcePath("images/set_default_dark.svg",
+                                 IDR_WELCOME_SET_DEFAULT_DARK);
+    html_source->AddResourcePath("images/set_default_light.svg",
+                                 IDR_WELCOME_SET_DEFAULT_LIGHT);
+#endif  // defined(GOOGLE_CHROME_BUILD)
+
     // chrome://welcome
     html_source->SetDefaultResource(
         IDR_WELCOME_ONBOARDING_WELCOME_WELCOME_HTML);
diff --git a/chrome/browser/vr/BUILD.gn b/chrome/browser/vr/BUILD.gn
index 18ad1f17..405edd18 100644
--- a/chrome/browser/vr/BUILD.gn
+++ b/chrome/browser/vr/BUILD.gn
@@ -309,7 +309,7 @@
       "win/vr_browser_renderer_thread_win.h",
     ]
 
-    deps += [ "//services/ws/public/cpp/gpu" ]
+    deps += [ "//services/viz/public/cpp/gpu" ]
 
     # Windows doesn't have app bundles, so include the UI directly.
     deps += [ ":vr_ui" ]
diff --git a/chrome/browser/vr/win/graphics_delegate_win.cc b/chrome/browser/vr/win/graphics_delegate_win.cc
index 2a6627e..8d3e2eb 100644
--- a/chrome/browser/vr/win/graphics_delegate_win.cc
+++ b/chrome/browser/vr/win/graphics_delegate_win.cc
@@ -38,13 +38,13 @@
   attributes.sample_buffers = 0;
   attributes.bind_generates_resource = false;
 
-  context_provider_ = base::MakeRefCounted<ws::ContextProviderCommandBuffer>(
+  context_provider_ = base::MakeRefCounted<viz::ContextProviderCommandBuffer>(
       host, factory->GetGpuMemoryBufferManager(), content::kGpuStreamIdDefault,
       content::kGpuStreamPriorityUI, gpu::kNullSurfaceHandle,
       GURL(std::string("chrome://gpu/VrUiWin")), false /* automatic flushes */,
       false /* support locking */, false /* support grcontext */,
       gpu::SharedMemoryLimits::ForMailboxContext(), attributes,
-      ws::command_buffer_metrics::ContextType::XR_COMPOSITING);
+      viz::command_buffer_metrics::ContextType::XR_COMPOSITING);
   gpu_memory_buffer_manager_ = factory->GetGpuMemoryBufferManager();
   return true;
 }
diff --git a/chrome/browser/vr/win/graphics_delegate_win.h b/chrome/browser/vr/win/graphics_delegate_win.h
index 993562b..211c40a 100644
--- a/chrome/browser/vr/win/graphics_delegate_win.h
+++ b/chrome/browser/vr/win/graphics_delegate_win.h
@@ -17,7 +17,7 @@
 #include "gpu/command_buffer/common/context_creation_attribs.h"
 #include "gpu/ipc/client/gpu_channel_host.h"
 #include "mojo/public/cpp/system/handle.h"
-#include "services/ws/public/cpp/gpu/context_provider_command_buffer.h"
+#include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
 #include "ui/gfx/geometry/rect_f.h"
 
 namespace gpu {
@@ -85,7 +85,7 @@
 
   device::mojom::VRDisplayInfoPtr info_;
 
-  scoped_refptr<ws::ContextProviderCommandBuffer> context_provider_;
+  scoped_refptr<viz::ContextProviderCommandBuffer> context_provider_;
   gpu::gles2::GLES2Interface* gl_ = nullptr;
   int last_width_ = 0;
   int last_height_ = 0;
diff --git a/chrome/chrome_cleaner/engines/broker/scanner_sandbox_interface.cc b/chrome/chrome_cleaner/engines/broker/scanner_sandbox_interface.cc
index 8e1592d9d..97715ac 100644
--- a/chrome/chrome_cleaner/engines/broker/scanner_sandbox_interface.cc
+++ b/chrome/chrome_cleaner/engines/broker/scanner_sandbox_interface.cc
@@ -168,8 +168,17 @@
   // of null terminating characters.
   PUNICODE_STRING command_line =
       reinterpret_cast<PUNICODE_STRING>(buffer.data());
-  size_t max_data_length = bytes_read - sizeof(UNICODE_STRING);
 
+  // NtQueryInformationProcess can return a buffer filled with 0's (for example
+  // when querying LsaIso.exe, an Isolated User Mode process). When we cast a
+  // buffer of 0's to a UNICODE_STRING struct, all members including the Buffer
+  // pointer becomed 0.
+  if (command_line->Buffer == nullptr) {
+    LOG(ERROR) << "Invalid command line buffer";
+    return false;
+  }
+
+  size_t max_data_length = bytes_read - sizeof(UNICODE_STRING);
   if (command_line->Length % sizeof(WCHAR) ||
       command_line->Length > max_data_length) {
     LOG(ERROR) << "Invalid command line length";
diff --git a/chrome/common/search.mojom b/chrome/common/search.mojom
index faf74f2..27615f8 100644
--- a/chrome/common/search.mojom
+++ b/chrome/common/search.mojom
@@ -156,7 +156,7 @@
   FocusChanged(OmniboxFocusState new_focus_state,
                OmniboxFocusChangeReason reason);
 
-  MostVisitedChanged(InstantMostVisitedInfo most_visited_info);
+  MostVisitedInfoChanged(InstantMostVisitedInfo most_visited_info);
 
   SetInputInProgress(bool input_in_progress);
 
diff --git a/chrome/common/search/mock_embedded_search_client.h b/chrome/common/search/mock_embedded_search_client.h
index f9e0f38c..5a8ab75 100644
--- a/chrome/common/search/mock_embedded_search_client.h
+++ b/chrome/common/search/mock_embedded_search_client.h
@@ -15,7 +15,7 @@
 
   MOCK_METHOD1(SetPageSequenceNumber, void(int));
   MOCK_METHOD2(FocusChanged, void(OmniboxFocusState, OmniboxFocusChangeReason));
-  MOCK_METHOD1(MostVisitedChanged, void(const InstantMostVisitedInfo&));
+  MOCK_METHOD1(MostVisitedInfoChanged, void(const InstantMostVisitedInfo&));
   MOCK_METHOD1(SetInputInProgress, void(bool));
   MOCK_METHOD1(ThemeChanged, void(const ThemeBackgroundInfo&));
   MOCK_METHOD0(SelectLocalImageSuccess, void());
diff --git a/chrome/common/search/ntp_logging_events.h b/chrome/common/search/ntp_logging_events.h
index 40873c4c..e23dc4df 100644
--- a/chrome/common/search/ntp_logging_events.h
+++ b/chrome/common/search/ntp_logging_events.h
@@ -22,18 +22,12 @@
   // Deleted: NTP_EXTERNAL_TILE_FALLBACK = 8,
   // Deleted: NTP_MOUSEOVER = 9
   // Deleted: NTP_TILE_LOADED = 10,
+  // Deleted: NTP_ALL_TILES_RECEIVED = 12,
 
   // All NTP tiles have finished loading (successfully or failing). Logged only
   // by the single-iframe version of the NTP.
   NTP_ALL_TILES_LOADED = 11,
 
-  // The data for all NTP tiles (title, URL, etc, but not the thumbnail image)
-  // has been received by the most visited iframe. In contrast to
-  // NTP_ALL_TILES_LOADED, this is recorded before the actual DOM elements have
-  // loaded (in particular the thumbnail images). Logged only by the
-  // single-iframe version of the NTP.
-  NTP_ALL_TILES_RECEIVED = 12,
-
   // Activated by clicking on the fakebox icon. Logged by Voice Search.
   NTP_VOICE_ACTION_ACTIVATE_FAKEBOX = 13,
   // Activated by keyboard shortcut.
diff --git a/chrome/renderer/searchbox/searchbox.cc b/chrome/renderer/searchbox/searchbox.cc
index 3df0dd5..3dddf9b 100644
--- a/chrome/renderer/searchbox/searchbox.cc
+++ b/chrome/renderer/searchbox/searchbox.cc
@@ -475,7 +475,7 @@
   }
 }
 
-void SearchBox::MostVisitedChanged(
+void SearchBox::MostVisitedInfoChanged(
     const InstantMostVisitedInfo& most_visited_info) {
   has_received_most_visited_ = true;
   items_are_custom_links_ = most_visited_info.items_are_custom_links;
diff --git a/chrome/renderer/searchbox/searchbox.h b/chrome/renderer/searchbox/searchbox.h
index 742573a..37790839 100644
--- a/chrome/renderer/searchbox/searchbox.h
+++ b/chrome/renderer/searchbox/searchbox.h
@@ -195,7 +195,7 @@
   void SetPageSequenceNumber(int page_seq_no) override;
   void FocusChanged(OmniboxFocusState new_focus_state,
                     OmniboxFocusChangeReason reason) override;
-  void MostVisitedChanged(
+  void MostVisitedInfoChanged(
       const InstantMostVisitedInfo& most_visited_info) override;
   void SetInputInProgress(bool input_in_progress) override;
   void ThemeChanged(const ThemeBackgroundInfo& theme_info) override;
diff --git a/chrome/services/cups_ipp_parser/public/cpp/BUILD.gn b/chrome/services/cups_ipp_parser/public/cpp/BUILD.gn
index 29bd299..6bf1de6e 100644
--- a/chrome/services/cups_ipp_parser/public/cpp/BUILD.gn
+++ b/chrome/services/cups_ipp_parser/public/cpp/BUILD.gn
@@ -12,9 +12,12 @@
       "ipp_converter.h",
     ]
 
+    public_deps = [
+      "//chrome/services/cups_ipp_parser/public/mojom",
+    ]
+
     deps = [
       "//base",
-      "//chrome/services/cups_ipp_parser/public/mojom",
       "//net",
       "//printing",
     ]
diff --git a/chrome/services/cups_proxy/BUILD.gn b/chrome/services/cups_proxy/BUILD.gn
index 224996e..eb84fa9 100644
--- a/chrome/services/cups_proxy/BUILD.gn
+++ b/chrome/services/cups_proxy/BUILD.gn
@@ -30,6 +30,8 @@
   if (use_cups) {
     configs += [ "//printing:cups" ]
     sources += [
+      "ipp_validator.cc",
+      "ipp_validator.h",
       "printer_installer.cc",
       "printer_installer.h",
       "socket_manager.cc",
@@ -60,6 +62,7 @@
   # Target is empty unless libCUPS is available.
   if (use_cups) {
     sources = [
+      "ipp_validator_unittest.cc",
       "printer_installer_unittest.cc",
       "socket_manager_unittest.cc",
     ]
diff --git a/chrome/services/cups_proxy/DEPS b/chrome/services/cups_proxy/DEPS
index 4694dc2..69f0dbb 100644
--- a/chrome/services/cups_proxy/DEPS
+++ b/chrome/services/cups_proxy/DEPS
@@ -1,3 +1,4 @@
 include_rules = [
   "+chromeos/printing",
+  "+chrome/services/cups_ipp_parser/public",
 ]
diff --git a/chrome/services/cups_proxy/ipp_validator.cc b/chrome/services/cups_proxy/ipp_validator.cc
new file mode 100644
index 0000000..d8d93c2
--- /dev/null
+++ b/chrome/services/cups_proxy/ipp_validator.cc
@@ -0,0 +1,307 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/services/cups_proxy/ipp_validator.h"
+
+#include <cups/cups.h>
+
+#include <algorithm>
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/containers/span.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "net/http/http_util.h"
+#include "printing/backend/cups_ipp_util.h"
+
+namespace cups_proxy {
+namespace {
+
+// Initial version only supports english lcoales.
+// TODO(crbug.com/945409): Extending to supporting arbitrary locales.
+const char kLocaleEnglish[] = "en";
+
+// Following ParseXxx methods translate mojom objects representing IPP
+// attribute values to formats libCUPS APIs accept.
+
+// Converting to vector<char> for libCUPS API:
+// ippAddBooleans(..., int num_values, const char *values)
+// TODO(crbug.com/945409): Convert chrome::mojom::Value to using
+// mojo_base::Value.
+base::Optional<std::vector<char>> ParseBooleans(
+    const std::vector<chrome::mojom::ValuePtr>& values) {
+  std::vector<char> ret;
+  for (auto& value : values) {
+    if (!value->is_bool_value()) {
+      return base::nullopt;
+    }
+
+    ret.push_back(value->get_bool_value() ? 1 : 0);
+  }
+  return ret;
+}
+
+// Converting to vector<int> for libCUPS API:
+// ippAddIntegers(..., int num_values, const int *values)
+base::Optional<std::vector<int>> ParseIntegers(
+    const std::vector<chrome::mojom::ValuePtr>& values) {
+  std::vector<int> ret;
+  for (auto& value : values) {
+    if (!value->is_int_value()) {
+      return base::nullopt;
+    }
+
+    ret.push_back(value->get_int_value());
+  }
+  return ret;
+}
+
+// Converting to vector<const char*> for libCUPS API:
+// ippAddStrings(..., int num_values, const char *const *values)
+base::Optional<std::vector<const char*>> ParseStrings(
+    const std::vector<chrome::mojom::ValuePtr>& values) {
+  std::vector<const char*> ret;
+  for (auto& value : values) {
+    if (!value->is_string_value()) {
+      return base::nullopt;
+    }
+
+    // ret's cstrings reference |values| strings, so |values| MUST outlive it.
+    ret.push_back(value->get_string_value().c_str());
+  }
+  return ret;
+}
+
+}  // namespace
+
+// Verifies that |method|, |endpoint|, and |http_version| form a valid HTTP
+// request-line. On success, returns a wrapper obj containing the verified
+// request-line.
+base::Optional<HttpRequestLine> IppValidator::ValidateHttpRequestLine(
+    base::StringPiece method,
+    base::StringPiece endpoint,
+    base::StringPiece http_version) {
+  if (method != "POST") {
+    return base::nullopt;
+  }
+  if (http_version != "HTTP/1.1") {
+    return base::nullopt;
+  }
+
+  // Ensure endpoint is either default('/') or known printer
+  auto printer = delegate_->GetPrinter(endpoint.as_string());
+  if (endpoint != "/" && !printer) {
+    return base::nullopt;
+  }
+
+  return HttpRequestLine{method.as_string(), endpoint.as_string(),
+                         http_version.as_string()};
+}
+
+base::Optional<std::vector<ipp_converter::HttpHeader>>
+IppValidator::ValidateHttpHeaders(
+    const base::flat_map<std::string, std::string>& headers) {
+  // Sane, character-set checks.
+  for (const auto& header : headers) {
+    if (!net::HttpUtil::IsValidHeaderName(header.first) ||
+        !net::HttpUtil::IsValidHeaderValue(header.second)) {
+      return base::nullopt;
+    }
+  }
+
+  return std::vector<ipp_converter::HttpHeader>(headers.begin(), headers.end());
+}
+
+ipp_t* IppValidator::ValidateIppMessage(
+    chrome::mojom::IppMessagePtr ipp_message) {
+  printing::ScopedIppPtr ipp = printing::WrapIpp(ippNew());
+
+  // Fill ids.
+  if (!ippSetVersion(ipp.get(), ipp_message->major_version,
+                     ipp_message->minor_version)) {
+    return nullptr;
+  }
+
+  if (!ippSetOperation(ipp.get(),
+                       static_cast<ipp_op_t>(ipp_message->operation_id))) {
+    return nullptr;
+  }
+
+  if (!ippSetRequestId(ipp.get(), ipp_message->request_id)) {
+    return nullptr;
+  }
+
+  // Fill attributes.
+  for (size_t i = 0; i < ipp_message->attributes.size(); ++i) {
+    chrome::mojom::IppAttributePtr attribute =
+        std::move(ipp_message->attributes[i]);
+
+    switch (attribute->type) {
+      case chrome::mojom::ValueType::BOOLEAN: {
+        base::Optional<std::vector<char>> values =
+            ParseBooleans(attribute->values);
+        if (!values.has_value()) {
+          return nullptr;
+        }
+
+        auto* attr = ippAddBooleans(
+            ipp.get(), static_cast<ipp_tag_t>(attribute->group_tag),
+            attribute->name.c_str(), values->size(), values->data());
+        if (!attr) {
+          return nullptr;
+        }
+        break;
+      }
+      // TODO(crbug.com/945409): Include for multiple value checking.
+      case chrome::mojom::ValueType::DATE: {
+        if (!attribute->values.front()->is_char_value()) {
+          return nullptr;
+        }
+
+        auto date = attribute->values.front()->get_char_value();
+        auto* attr = ippAddDate(
+            ipp.get(), static_cast<ipp_tag_t>(attribute->group_tag),
+            attribute->name.c_str(), static_cast<ipp_uchar_t*>(&date));
+        if (!attr) {
+          return nullptr;
+        }
+        break;
+      }
+      case chrome::mojom::ValueType::INTEGER: {
+        base::Optional<std::vector<int>> values =
+            ParseIntegers(attribute->values);
+        if (!values.has_value()) {
+          return nullptr;
+        }
+
+        auto* attr = ippAddIntegers(
+            ipp.get(), static_cast<ipp_tag_t>(attribute->group_tag),
+            static_cast<ipp_tag_t>(attribute->value_tag),
+            attribute->name.c_str(), values->size(), values->data());
+        if (!attr) {
+          return nullptr;
+        }
+        break;
+      }
+      case chrome::mojom::ValueType::STRING: {
+        // Note: cstrings_values references attribute->values, i.e.
+        // cstrings_values MUST outlive attribute->values.
+        base::Optional<std::vector<const char*>> cstrings_values =
+            ParseStrings(attribute->values);
+        if (!cstrings_values.has_value()) {
+          return nullptr;
+        }
+
+        auto* attr = ippAddStrings(
+            ipp.get(), static_cast<ipp_tag_t>(attribute->group_tag),
+            static_cast<ipp_tag_t>(attribute->value_tag),
+            attribute->name.c_str(), cstrings_values->size(), kLocaleEnglish,
+            cstrings_values->data());
+        if (!attr) {
+          return nullptr;
+        }
+        break;
+      }
+      default:
+        NOTREACHED() << "Unknown IPP attribute type found.";
+    }
+  }
+
+  // Validate Attributes.
+  if (!ippValidateAttributes(ipp.get())) {
+    return nullptr;
+  }
+
+  // Return built ipp object.
+  return ipp.release();
+}
+
+// Requires IPP data portion is either empty or looks like a pdf.
+bool IppValidator::ValidateIppData(const std::vector<uint8_t>& ipp_data) {
+  const int pdf_magic_bytes_size = 4;
+  constexpr std::array<uint8_t, pdf_magic_bytes_size> pdf_magic_bytes = {
+      0x25, 0x50, 0x44, 0x46};  // { %PDF }
+
+  // Empty IPP data portion.
+  if (ipp_data.empty()) {
+    return true;
+  }
+
+  if (ipp_data.size() < pdf_magic_bytes_size) {
+    return false;
+  }
+
+  // Check that |ipp_data| starts with pdf_magic_bytes.
+  return std::equal(ipp_data.begin(), ipp_data.begin() + pdf_magic_bytes_size,
+                    pdf_magic_bytes.begin());
+}
+
+IppValidator::IppValidator(
+    base::WeakPtr<chromeos::printing::CupsProxyServiceDelegate> delegate)
+    : delegate_(std::move(delegate)) {
+  DETACH_FROM_SEQUENCE(sequence_checker_);
+}
+
+IppValidator::~IppValidator() = default;
+
+base::Optional<IppRequest> IppValidator::ValidateIppRequest(
+    chrome::mojom::IppRequestPtr to_validate) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (!delegate_) {
+    // TODO(crbug/495409): Add fatal error option to bring down service.
+    return base::nullopt;
+  }
+
+  // Build request line.
+  auto request_line = ValidateHttpRequestLine(
+      to_validate->method, to_validate->endpoint, to_validate->http_version);
+  if (!request_line.has_value()) {
+    return base::nullopt;
+  }
+
+  // Build headers.
+  auto headers = ValidateHttpHeaders(to_validate->headers);
+  if (!headers.has_value()) {
+    return base::nullopt;
+  }
+
+  // Build ipp message.
+  // Note: Moving ipp here, to_validate->ipp no longer valid below.
+  printing::ScopedIppPtr ipp =
+      printing::WrapIpp(ValidateIppMessage(std::move(to_validate->ipp)));
+  if (ipp == nullptr) {
+    return base::nullopt;
+  }
+
+  // Validate ipp data.
+  // TODO(crbug/894607): Validate ippData (pdf).
+  if (!ValidateIppData(to_validate->data)) {
+    return base::nullopt;
+  }
+
+  // Marshall request
+  IppRequest ret;
+  ret.request_line = std::move(*request_line);
+  ret.headers = std::move(*headers);
+  ret.ipp = std::move(ipp);
+  ret.ipp_data = std::move(to_validate->data);
+
+  // Build parsed request buffer.
+  auto request_buffer = ipp_converter::BuildIppRequest(
+      ret.request_line.method, ret.request_line.endpoint,
+      ret.request_line.http_version, ret.headers, ret.ipp.get(), ret.ipp_data);
+  if (!request_buffer.has_value()) {
+    return base::nullopt;
+  }
+
+  ret.buffer = std::move(*request_buffer);
+  return ret;
+}
+
+}  // namespace cups_proxy
diff --git a/chrome/services/cups_proxy/ipp_validator.h b/chrome/services/cups_proxy/ipp_validator.h
new file mode 100644
index 0000000..23a0c6e
--- /dev/null
+++ b/chrome/services/cups_proxy/ipp_validator.h
@@ -0,0 +1,60 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_SERVICES_CUPS_PROXY_IPP_VALIDATOR_H_
+#define CHROME_SERVICES_CUPS_PROXY_IPP_VALIDATOR_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "base/strings/string_piece_forward.h"
+#include "chrome/services/cups_ipp_parser/public/cpp/ipp_converter.h"
+#include "chrome/services/cups_ipp_parser/public/mojom/ipp_parser.mojom.h"
+#include "chrome/services/cups_proxy/cups_proxy_service_delegate.h"
+#include "chrome/services/cups_proxy/public/cpp/ipp_messages.h"
+
+namespace cups_proxy {
+
+struct IppRequest;
+
+// This class fully validates incoming parsed IPP requests. HTTP metadata
+// validation is handled with net/http. IPP metadata validation is handled
+// largely via libCUPS. This class can be created anywhere, but must be
+// accessed from a sequenced context.
+class IppValidator {
+ public:
+  explicit IppValidator(
+      base::WeakPtr<chromeos::printing::CupsProxyServiceDelegate> delegate);
+  ~IppValidator();
+
+  // Validates each of |to_validate|'s fields and returns a POD representation
+  // of the IPP request. Returns empty Optional on failure.
+  base::Optional<IppRequest> ValidateIppRequest(
+      chrome::mojom::IppRequestPtr to_validate);
+
+ private:
+  base::Optional<HttpRequestLine> ValidateHttpRequestLine(
+      base::StringPiece method,
+      base::StringPiece endpoint,
+      base::StringPiece http_version);
+
+  base::Optional<std::vector<ipp_converter::HttpHeader>> ValidateHttpHeaders(
+      const base::flat_map<std::string, std::string>& headers);
+
+  ipp_t* ValidateIppMessage(chrome::mojom::IppMessagePtr ipp_message);
+
+  bool ValidateIppData(const std::vector<uint8_t>& ipp_data);
+
+  // Delegate providing necessary Profile dependencies.
+  base::WeakPtr<chromeos::printing::CupsProxyServiceDelegate> delegate_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+};
+
+}  // namespace cups_proxy
+
+#endif  // CHROME_SERVICES_CUPS_PROXY_IPP_VALIDATOR_H_
diff --git a/chrome/services/cups_proxy/ipp_validator_unittest.cc b/chrome/services/cups_proxy/ipp_validator_unittest.cc
new file mode 100644
index 0000000..c407593
--- /dev/null
+++ b/chrome/services/cups_proxy/ipp_validator_unittest.cc
@@ -0,0 +1,179 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/services/cups_proxy/ipp_validator.h"
+
+#include <cups/cups.h>
+
+#include <set>
+#include <string>
+#include <utility>
+
+#include "chrome/services/cups_proxy/fake_cups_proxy_service_delegate.h"
+#include "chrome/services/cups_proxy/public/cpp/ipp_messages.h"
+#include "printing/backend/cups_ipp_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cups_proxy {
+namespace {
+
+// Mojom using statements to remove chrome::mojom everywhere...
+using chrome::mojom::IppAttributePtr;
+using chrome::mojom::IppMessagePtr;
+using chrome::mojom::IppRequestPtr;
+using chrome::mojom::Value;
+using chrome::mojom::ValueType;
+
+using Printer = chromeos::Printer;
+
+// Fake backend for CupsProxyServiceDelegate.
+class FakeServiceDelegate
+    : public chromeos::printing::FakeCupsProxyServiceDelegate {
+ public:
+  FakeServiceDelegate() = default;
+  ~FakeServiceDelegate() override = default;
+
+  void AddPrinter(const std::string& printer_id) {
+    known_printers_.insert(printer_id);
+  }
+
+  base::Optional<Printer> GetPrinter(const std::string& id) override {
+    if (!base::ContainsKey(known_printers_, id)) {
+      return base::nullopt;
+    }
+
+    return Printer(id);
+  }
+
+ private:
+  std::set<std::string> known_printers_;
+};
+
+class IppValidatorTest : public testing::Test {
+ public:
+  IppValidatorTest() {
+    delegate_ = std::make_unique<FakeServiceDelegate>();
+    ipp_validator_ = std::make_unique<IppValidator>(delegate_->GetWeakPtr());
+  }
+
+  ~IppValidatorTest() override = default;
+
+  base::Optional<IppRequest> RunValidateIppRequest(
+      const IppRequestPtr& request) {
+    return ipp_validator_->ValidateIppRequest(request.Clone());
+  }
+
+ protected:
+  // Fake delegate driving the IppValidator.
+  std::unique_ptr<FakeServiceDelegate> delegate_;
+
+  // The manager being tested. This must be declared, and thus initialized,
+  // after the fakes.
+  std::unique_ptr<IppValidator> ipp_validator_;
+};
+
+IppAttributePtr BuildAttributePtr(std::string name,
+                                  ipp_tag_t group_tag,
+                                  ipp_tag_t value_tag) {
+  auto ret = chrome::mojom::IppAttribute::New();
+  ret->name = name;
+  ret->group_tag = group_tag;
+  ret->value_tag = value_tag;
+  return ret;
+}
+
+// Returns a mojom representation of a standard IPP request.
+IppRequestPtr GetBasicIppRequest() {
+  IppRequestPtr ret = chrome::mojom::IppRequest::New();
+
+  // Request line.
+  ret->method = "POST";
+  ret->endpoint = "/";
+  ret->http_version = "HTTP/1.1";
+
+  // Map of Http headers.
+  ret->headers = std::vector<ipp_converter::HttpHeader>{
+      {"Content-Length", "72"},
+      {"Content-Type", "application/ipp"},
+      {"Date", "Thu, 04 Oct 2018 20:25:59 GMT"},
+      {"Host", "localhost:0"},
+      {"User-Agent",
+       "CUPS/2.3b1 (Linux 4.4.159-15303-g65f4b5a7b3d3; i686) IPP/2.0"}};
+
+  // IppMessage.
+  IppMessagePtr ipp_message = chrome::mojom::IppMessage::New();
+  ipp_message->major_version = 2;
+  ipp_message->minor_version = 0;
+  ipp_message->operation_id = IPP_OP_CUPS_GET_DEFAULT;
+  ipp_message->request_id = 1;
+
+  // Setup each attribute.
+  IppAttributePtr attr_charset = BuildAttributePtr(
+      "attributes-charset", IPP_TAG_OPERATION, IPP_TAG_CHARSET);
+  attr_charset->type = ValueType::STRING;
+  attr_charset->values.push_back(Value::NewStringValue("utf-8"));
+
+  IppAttributePtr attr_natlang = BuildAttributePtr(
+      "attributes-natural-language", IPP_TAG_OPERATION, IPP_TAG_LANGUAGE);
+  attr_natlang->type = ValueType::STRING;
+  attr_natlang->values.push_back(Value::NewStringValue("en"));
+
+  ipp_message->attributes.push_back(std::move(attr_charset));
+  ipp_message->attributes.push_back(std::move(attr_natlang));
+  ret->ipp = std::move(ipp_message);
+  return ret;
+}
+
+// Ensure basic IPP request passes validation.
+TEST_F(IppValidatorTest, SimpleSanityTest) {
+  auto request = GetBasicIppRequest();
+  auto validated_request = RunValidateIppRequest(request);
+  EXPECT_TRUE(RunValidateIppRequest(request));
+}
+
+// Ensure printer endpoints are printers known to Chrome.
+TEST_F(IppValidatorTest, EndpointIsKnownPrinter) {
+  auto request = GetBasicIppRequest();
+  std::string printer_id = "abc";
+
+  request->endpoint = printer_id;
+  EXPECT_FALSE(RunValidateIppRequest(request));
+
+  // Should succeed now that |delegate_| knows about the printer.
+  delegate_->AddPrinter(printer_id);
+  EXPECT_TRUE(RunValidateIppRequest(request));
+}
+
+TEST_F(IppValidatorTest, IncorrectHttpMethod) {
+  auto request = GetBasicIppRequest();
+  request->method = "GET";
+  EXPECT_FALSE(RunValidateIppRequest(request));
+}
+
+TEST_F(IppValidatorTest, IncorrectHttpVersion) {
+  auto request = GetBasicIppRequest();
+  request->http_version = "HTTP/2.0";
+  EXPECT_FALSE(RunValidateIppRequest(request));
+}
+
+TEST_F(IppValidatorTest, MissingHeaderName) {
+  auto request = GetBasicIppRequest();
+
+  // Adds new header with an empty name.
+  request->headers[""] = "arbitrary valid header value";
+  EXPECT_FALSE(RunValidateIppRequest(request));
+}
+
+TEST_F(IppValidatorTest, MissingHeaderValue) {
+  auto request = GetBasicIppRequest();
+
+  // Adds new header with an empty name.
+  request->headers["arbitrary_valid_header_name"] = "";
+  EXPECT_TRUE(RunValidateIppRequest(request));
+}
+
+// TODO(luum): Test IPP validation.
+
+}  // namespace
+}  // namespace cups_proxy
diff --git a/chrome/services/cups_proxy/public/cpp/BUILD.gn b/chrome/services/cups_proxy/public/cpp/BUILD.gn
index 3170bc0..0c35dc6 100644
--- a/chrome/services/cups_proxy/public/cpp/BUILD.gn
+++ b/chrome/services/cups_proxy/public/cpp/BUILD.gn
@@ -13,17 +13,23 @@
     "type_conversions.h",
   ]
 
+  deps = [
+    "//base",
+  ]
+
   if (use_cups) {
     configs += [ "//printing:cups" ]
     sources += [
       "cups_util.cc",
       "cups_util.h",
+      "ipp_messages.cc",
+      "ipp_messages.h",
+    ]
+    public_deps = [
+      "//chrome/services/cups_ipp_parser/public/cpp",
+      "//printing",
     ]
   }
-
-  deps = [
-    "//base",
-  ]
 }
 
 source_set("manifest") {
diff --git a/chrome/services/cups_proxy/public/cpp/OWNERS b/chrome/services/cups_proxy/public/cpp/OWNERS
index 6faeaa47..cff5fd99 100644
--- a/chrome/services/cups_proxy/public/cpp/OWNERS
+++ b/chrome/services/cups_proxy/public/cpp/OWNERS
@@ -2,3 +2,8 @@
 per-file manifest.cc=file://ipc/SECURITY_OWNERS
 per-file manifest.h=set noparent
 per-file manifest.h=file://ipc/SECURITY_OWNERS
+
+per-file *_messages.cc=set noparent
+per-file *_messages.cc=file://ipc/SECURITY_OWNERS
+per-file *_messages*.h=set noparent
+per-file *_messages*.h=file://ipc/SECURITY_OWNERS
diff --git a/chrome/services/cups_proxy/public/cpp/ipp_messages.cc b/chrome/services/cups_proxy/public/cpp/ipp_messages.cc
new file mode 100644
index 0000000..5da352d
--- /dev/null
+++ b/chrome/services/cups_proxy/public/cpp/ipp_messages.cc
@@ -0,0 +1,21 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/services/cups_proxy/public/cpp/ipp_messages.h"
+
+#include "base/strings/string_piece.h"
+
+namespace cups_proxy {
+
+// Defaults for IppRequest.
+IppRequest::IppRequest() : ipp(printing::WrapIpp(nullptr)) {}
+IppRequest::IppRequest(IppRequest&& other) = default;
+IppRequest::~IppRequest() = default;
+
+// Defaults for IppResponse.
+IppResponse::IppResponse() : ipp(printing::WrapIpp(nullptr)) {}
+IppResponse::IppResponse(IppResponse&& other) = default;
+IppResponse::~IppResponse() = default;
+
+}  // namespace cups_proxy
diff --git a/chrome/services/cups_proxy/public/cpp/ipp_messages.h b/chrome/services/cups_proxy/public/cpp/ipp_messages.h
new file mode 100644
index 0000000..1654c8f
--- /dev/null
+++ b/chrome/services/cups_proxy/public/cpp/ipp_messages.h
@@ -0,0 +1,79 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_SERVICES_CUPS_PROXY_PUBLIC_CPP_IPP_MESSAGES_H_
+#define CHROME_SERVICES_CUPS_PROXY_PUBLIC_CPP_IPP_MESSAGES_H_
+
+#include <cups/cups.h>
+#include <stddef.h>
+
+#include <string>
+#include <vector>
+
+#include "base/optional.h"
+#include "chrome/services/cups_ipp_parser/public/cpp/ipp_converter.h"
+#include "printing/backend/cups_ipp_util.h"
+
+// POD representations of HTTP/IPP objects.
+namespace cups_proxy {
+
+// Helpful wrapper for a HTTP Request request-line.
+struct HttpRequestLine {
+  std::string method;
+  std::string endpoint;
+  std::string http_version;
+};
+
+// POD representation of an IPP request and assorted metadata.
+struct IppRequest {
+  // Explicitly declared/defined defaults since [chromium-style] flagged this as
+  // a complex struct.
+  IppRequest();
+  IppRequest(IppRequest&& other);
+  ~IppRequest();
+
+  // Implicitly deleted by DISALLOW, so adding back in.
+  IppRequest& operator=(IppRequest&& other) = default;
+
+  std::vector<uint8_t> buffer;
+
+  HttpRequestLine request_line;
+  std::vector<ipp_converter::HttpHeader> headers;
+  printing::ScopedIppPtr ipp;
+  std::vector<uint8_t> ipp_data;
+
+  DISALLOW_COPY_AND_ASSIGN(IppRequest);
+};
+
+// Helpful wrapper for a HTTP Response status-line.
+struct HttpStatusLine {
+  std::string http_version;
+  std::string status_code;
+  std::string reason_phrase;
+};
+
+// POD representation of an IPP response and assorted metadata.
+struct IppResponse {
+  // Explicitly declared/defined defaults since [chromium-style] flagged this as
+  // a complex struct.
+  IppResponse();
+  IppResponse(IppResponse&& other);
+  ~IppResponse();
+
+  // Implicitly deleted by DISALLOW, so adding back in.
+  IppResponse& operator=(IppResponse&& other) = default;
+
+  std::vector<uint8_t> buffer;
+
+  HttpStatusLine status_line;
+  std::vector<ipp_converter::HttpHeader> headers;
+  printing::ScopedIppPtr ipp;
+  std::vector<uint8_t> ipp_data;
+
+  DISALLOW_COPY_AND_ASSIGN(IppResponse);
+};
+
+}  // namespace cups_proxy
+
+#endif  // CHROME_SERVICES_CUPS_PROXY_PUBLIC_CPP_IPP_MESSAGES_H_
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index d19a918..9ddf7fa 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -263,8 +263,6 @@
     sources += [
       "../browser/chromeos/accessibility/speech_monitor.cc",
       "../browser/chromeos/accessibility/speech_monitor.h",
-      "../browser/chromeos/login/login_shelf_test_helper.cc",
-      "../browser/chromeos/login/login_shelf_test_helper.h",
       "../browser/chromeos/ownership/fake_owner_settings_service.cc",
       "../browser/chromeos/ownership/fake_owner_settings_service.h",
       "../browser/chromeos/settings/scoped_cros_settings_test_helper.cc",
@@ -2048,8 +2046,6 @@
         "../browser/chromeos/login/test/local_policy_test_server_mixin.h",
         "../browser/chromeos/login/test/login_manager_mixin.cc",
         "../browser/chromeos/login/test/login_manager_mixin.h",
-        "../browser/chromeos/login/test/login_screen_tester.cc",
-        "../browser/chromeos/login/test/login_screen_tester.h",
         "../browser/chromeos/login/test/network_portal_detector_mixin.cc",
         "../browser/chromeos/login/test/network_portal_detector_mixin.h",
         "../browser/chromeos/login/test/offline_gaia_test_mixin.cc",
@@ -2133,7 +2129,6 @@
         "../browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc",
         "../browser/extensions/api/vpn_provider/vpn_provider_apitest.cc",
         "../browser/extensions/chromeos_component_extensions_browsertest.cc",
-        "../browser/notifications/chrome_ash_message_center_client_browsertest.cc",
         "../browser/notifications/notification_platform_bridge_chromeos_browsertest.cc",
         "../browser/resources/chromeos/zip_archiver/test/zip_archiver_jstest.cc",
         "../browser/signin/chromeos_mirror_account_consistency_browsertest.cc",
@@ -2839,6 +2834,7 @@
     "../browser/mac/exception_processor_unittest.mm",
     "../browser/mac/keystone_glue_unittest.mm",
     "../browser/media/android/router/media_router_android_unittest.cc",
+    "../browser/media/cast_mirroring_service_host_unittest.cc",
     "../browser/media/media_engagement_contents_observer_unittest.cc",
     "../browser/media/media_engagement_preloaded_list_unittest.cc",
     "../browser/media/media_engagement_score_unittest.cc",
@@ -2903,7 +2899,6 @@
     "../browser/page_load_metrics/observers/live_tab_count_page_load_metrics_observer_unittest.cc",
     "../browser/page_load_metrics/observers/loading_predictor_page_load_metrics_observer_unittest.cc",
     "../browser/page_load_metrics/observers/local_network_requests_page_load_metrics_observer_unittest.cc",
-    "../browser/page_load_metrics/observers/lofi_page_load_metrics_observer_unittest.cc",
     "../browser/page_load_metrics/observers/media_page_load_metrics_observer_unittest.cc",
     "../browser/page_load_metrics/observers/multi_tab_loading_page_load_metrics_observer_unittest.cc",
     "../browser/page_load_metrics/observers/offline_page_previews_page_load_metrics_observer_unittest.cc",
diff --git a/chrome/test/chromedriver/chrome/adb_impl.cc b/chrome/test/chromedriver/chrome/adb_impl.cc
index 77be2f60..e63f809c 100644
--- a/chrome/test/chromedriver/chrome/adb_impl.cc
+++ b/chrome/test/chromedriver/chrome/adb_impl.cc
@@ -9,6 +9,7 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
+#include "base/environment.h"
 #include "base/json/string_escape.h"
 #include "base/logging.h"
 #include "base/memory/ref_counted.h"
@@ -98,6 +99,12 @@
       base::Bind(&ResponseBuffer::OnResponse, response_buffer));
 }
 
+std::string GetSerialFromEnvironment() {
+  std::unique_ptr<base::Environment> env(base::Environment::Create());
+  std::string serial;
+  return env->GetVar("ANDROID_SERIAL", &serial) ? serial : "";
+}
+
 }  // namespace
 
 AdbImpl::AdbImpl(
@@ -110,6 +117,7 @@
 AdbImpl::~AdbImpl() {}
 
 Status AdbImpl::GetDevices(std::vector<std::string>* devices) {
+  const std::string& serial_from_env = GetSerialFromEnvironment();
   std::string response;
   Status status = ExecuteCommand("host:devices", &response);
   if (!status.IsOk())
@@ -120,7 +128,12 @@
         lines.token_piece(), base::kWhitespaceASCII,
         base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
     if (fields.size() == 2 && fields[1] == "device") {
-      devices->push_back(fields[0]);
+      if (!serial_from_env.empty() && fields[0] == serial_from_env) {
+        // Move device with matching ANDROID_SERIAL to the top.
+        devices->insert(devices->begin(), fields[0]);
+      } else {
+        devices->push_back(fields[0]);
+      }
     }
   }
   return Status(kOk);
diff --git a/chrome/test/chromedriver/chrome/navigation_tracker.cc b/chrome/test/chromedriver/chrome/navigation_tracker.cc
index 230423d..5366478 100644
--- a/chrome/test/chromedriver/chrome/navigation_tracker.cc
+++ b/chrome/test/chromedriver/chrome/navigation_tracker.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/test/chromedriver/chrome/navigation_tracker.h"
 
+#include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/values.h"
 #include "chrome/test/chromedriver/chrome/browser_info.h"
@@ -207,6 +208,25 @@
 Status NavigationTracker::OnEvent(DevToolsClient* client,
                                   const std::string& method,
                                   const base::DictionaryValue& params) {
+  Status status = Status(kOk);
+  if (method == "Page.loadEventFired") {
+    load_event_fired_ = true;
+  } else if (base::StartsWith(method, "Runtime.executionContext",
+                              base::CompareCase::SENSITIVE)) {
+    status = HandleRuntimeEvent(client, method, params);
+  } else if (base::StartsWith(method, "Page.frame",
+                              base::CompareCase::SENSITIVE)) {
+    status = HandlePageFrameEvent(client, method, params);
+  } else if (method == "Inspector.targetCrashed") {
+    ResetLoadingState(kNotLoading);
+  }
+  return status;
+}
+
+Status NavigationTracker::HandlePageFrameEvent(
+    DevToolsClient* client,
+    const std::string& method,
+    const base::DictionaryValue& params) {
   if (method == "Page.frameStartedLoading") {
     std::string frame_id;
     if (!params.GetString("frameId", &frame_id))
@@ -285,7 +305,15 @@
       if (name == kDummyFrameName && url == kDummyFrameUrl)
         params.GetString("frame.id", &dummy_frame_id_);
     }
-  } else if (method == "Runtime.executionContextsCleared") {
+  }
+  return Status(kOk);
+}
+
+Status NavigationTracker::HandleRuntimeEvent(
+    DevToolsClient* client,
+    const std::string& method,
+    const base::DictionaryValue& params) {
+  if (method == "Runtime.executionContextsCleared") {
     execution_context_set_.clear();
     load_event_fired_ = false;
     // As of crrev.com/382211, DevTools sends an executionContextsCleared
@@ -320,10 +348,6 @@
         dummy_execution_context_id_ = 0;
       }
     }
-  } else if (method == "Page.loadEventFired") {
-    load_event_fired_ = true;
-  } else if (method == "Inspector.targetCrashed") {
-    ResetLoadingState(kNotLoading);
   }
   return Status(kOk);
 }
diff --git a/chrome/test/chromedriver/chrome/navigation_tracker.h b/chrome/test/chromedriver/chrome/navigation_tracker.h
index 0e52b44..8e3af36 100644
--- a/chrome/test/chromedriver/chrome/navigation_tracker.h
+++ b/chrome/test/chromedriver/chrome/navigation_tracker.h
@@ -62,6 +62,12 @@
                           const Timeout& command_timeout) override;
 
  private:
+  Status HandlePageFrameEvent(DevToolsClient* client,
+                              const std::string& method,
+                              const base::DictionaryValue& params);
+  Status HandleRuntimeEvent(DevToolsClient* client,
+                            const std::string& method,
+                            const base::DictionaryValue& params);
   DevToolsClient* client_;
   LoadingState loading_state_;
   const JavaScriptDialogManager* dialog_manager_;
diff --git a/chrome/test/chromedriver/test/run_webdriver_tests.py b/chrome/test/chromedriver/test/run_webdriver_tests.py
index 764de2a..dcd3598 100644
--- a/chrome/test/chromedriver/test/run_webdriver_tests.py
+++ b/chrome/test/chromedriver/test/run_webdriver_tests.py
@@ -9,6 +9,7 @@
 import argparse
 import sys
 import json
+import tempfile
 import time
 import logging
 
@@ -182,6 +183,9 @@
   parser.add_argument(
       '--chrome', help='Path to chrome binary')
   parser.add_argument(
+      '--output-dir',
+      help='Output directory for misc logs (e.g. wptserve)')
+  parser.add_argument(
       '--isolated-script-test-output',
       help='JSON output file used by swarming')
   parser.add_argument(
@@ -211,6 +215,12 @@
 
   host = Host()
   port = host.port_factory.get()
+  if options.output_dir:
+    port.set_option_default('results_directory', options.output_dir)
+  else:
+    output_dir = tempfile.mkdtemp('webdriver_tests')
+    _log.info('Using a temporary output dir %s', output_dir)
+    port.set_option_default('results_directory', output_dir)
   path_finder = PathFinder(host.filesystem)
 
   # Starts WPT Serve to serve the WPT WebDriver test content.
diff --git a/chrome/test/data/extensions/api_test/automation/sites/detected_language.html b/chrome/test/data/extensions/api_test/automation/sites/detected_language.html
index a932801..1450098 100644
--- a/chrome/test/data/extensions/api_test/automation/sites/detected_language.html
+++ b/chrome/test/data/extensions/api_test/automation/sites/detected_language.html
@@ -5,12 +5,17 @@
 -->
 <html>
 <head>
-<title>Automation Tests - Language Detection</title>
+<title>Automation Tests - Language Detection and Switching</title>
 <meta charset="utf-8">
 </head>
 <body>
   <div lang="fr" class="magritte">“The famous pipe. How people reproached me for it! And yet, could you stuff my pipe? No, it’s just a representation, is it not? So if I had written on my picture ‘This is a pipe,’ I’d have been lying!"</div>
 
   <div lang="en" class="magritte">« La fameuse pipe, me l’a-t-on assez reprochée ! Et pourtant, pouvez-vous la bourrer ma pipe ? Non, n’est-ce pas, elle n’est qu’une représentation. Donc si j’avais écrit sous mon tableau « ceci est une pipe », j’aurais menti ! »</div>
+
+  <p>δ</p>
+
+  <p>Hello everyone, it's a pleasure to meet you. どうぞよろしくお願いします.Let's include more English to ensure
+  that we're splitting indices correctly.</p>
 </body>
 </html>
diff --git a/chrome/test/data/extensions/api_test/automation/tests/tabs/detected_language.js b/chrome/test/data/extensions/api_test/automation/tests/tabs/detected_language.js
index 166a8e9d..e092054 100644
--- a/chrome/test/data/extensions/api_test/automation/tests/tabs/detected_language.js
+++ b/chrome/test/data/extensions/api_test/automation/tests/tabs/detected_language.js
@@ -19,7 +19,104 @@
     assertEq('fr', second.detectedLanguage,
       'detected language should be French');
     chrome.test.succeed();
+  },
+
+  function detectedLanguageForInvalidAttribute() {
+    var item = rootNode.children[2].children[0];
+    var langAnnotation =
+      item.languageAnnotationForStringAttribute('invalid attribute');
+    assertEq(0, langAnnotation.length);
+    chrome.test.succeed();
+  },
+
+  function detectedLanguageCharacter() {
+    var item = rootNode.children[2].children[0];
+    var langAnnotation = item.languageAnnotationForStringAttribute('name');
+    var name = item.name;
+    assertEq(1, langAnnotation.length);
+    assertEq('el', langAnnotation[0].language);
+    var startIndex = langAnnotation[0].startIndex;
+    var endIndex = langAnnotation[0].endIndex;
+    assertEq(name,buildOutputString(name,startIndex,endIndex));
+    chrome.test.succeed();
+  },
+
+  function detectedLanguageMultiple() {
+    var item = rootNode.children[3].children[0];
+    var langAnnotation = item.languageAnnotationForStringAttribute('name');
+    var name = item.name;
+    assertEq(3, langAnnotation.length);
+    assertEq('en', langAnnotation[0].language);
+    assertEq('ja', langAnnotation[1].language);
+    assertEq('en', langAnnotation[2].language);
+
+    // Build substrings.
+    var actualSubstrings = [];
+    for (var q = 0; q < langAnnotation.length; ++q) {
+      var output = '';
+      var startIndex = langAnnotation[q].startIndex;
+      var endIndex = langAnnotation[q].endIndex;
+      actualSubstrings.push(buildOutputString(name,startIndex,endIndex));
+    }
+
+    // Can assertEq on the two English strings.
+    assertEq('Hello everyone, it\'s a pleasure to meet you. ',
+      actualSubstrings[0]);
+    assertEq('Let\'s include more English to ensure that we\'re splitting ' +
+      'indices correctly.', actualSubstrings[2]);
+    // Compare unicode code points to ensure correct symbols for Japanese
+    // substring.
+    // correctJapaneseCodePoints contains the codePoints for the string:
+    // どうぞよろしくお願いします.
+    var correctJapaneseCodePoints = [12393, 12358, 12382, 12424, 12429, 12375,
+      12367, 12362, 39000, 12356, 12375, 12414, 12377, 46];
+    var japaneseSubstring = actualSubstrings[1];
+    var japaneseSubstringSymbolArray = [...japaneseSubstring];
+    assertEq(japaneseSubstringSymbolArray.length,
+      correctJapaneseCodePoints.length);
+    for (var i = 0; i < japaneseSubstringSymbolArray.length; ++i) {
+      var codePoint = japaneseSubstring.codePointAt(i);
+      assertEq(correctJapaneseCodePoints[i], codePoint);
+    }
+    chrome.test.succeed();
+  },
+  // This function ensures correct behavior when building substrings that
+  // contain unicode surrogate pairs.
+  function testBuildOutputStringOnSurrogatePair() {
+    // containsSurrogatePair = '𝛀 is bold omega'.
+    var containsSurrogatePair = '\u{1D6C0} is bold omega';
+    var symbolArray = [...containsSurrogatePair];
+    // To humans, the length of 𝛀 is 1, but to the computer, it actually has a
+    // length of 2.
+    // Bold omega is a single unicode code point which is 2 code units in Utf16.
+    // The first assertion below confirms this, as the string.length method
+    // is not unicode-aware.
+    assertEq(16,containsSurrogatePair.length);
+    // However, the second assertion is unicode aware, which is the length most
+    // people would expect given the above string.
+    assertEq(15,symbolArray.length);
+
+    // Extract only the omega from the original string.
+    // Notice that when using the string.substring method, we need to pass in
+    // endIndex = 2 to extract the 𝛀.
+    // However, the buildOutputString function is unicode-aware, so to
+    // extract the 𝛀, we can set endIndex = 1, instead of 2.
+    var omega = containsSurrogatePair.substring(0,2);
+    assertEq('1d6c0',omega.codePointAt(0).toString(16));
+    omega = buildOutputString(containsSurrogatePair,0,1);
+    assertEq('1d6c0',omega.codePointAt(0).toString(16));
+    chrome.test.succeed();
   }
 ];
 
+// Returns unicode-aware substring of text from startIndex to endIndex.
+function buildOutputString(text,startIndex,endIndex) {
+  var result = '';
+  var textSymbolArray = [...text];
+  for (var i = startIndex; i < endIndex; ++i) {
+    result += textSymbolArray[i];
+  }
+  return result;
+}
+
 setUpAndRunTests(allTests, 'detected_language.html');
diff --git a/chrome/test/data/local_ntp/custom_backgrounds_browsertest.js b/chrome/test/data/local_ntp/custom_backgrounds_browsertest.js
index 3b589fa..ebda68b 100644
--- a/chrome/test/data/local_ntp/custom_backgrounds_browsertest.js
+++ b/chrome/test/data/local_ntp/custom_backgrounds_browsertest.js
@@ -11,13 +11,13 @@
 /**
  * Local NTP's object for test and setup functions.
  */
-test.customBackgrounds = {};
+test.customize = {};
 
 
 /**
  * Sets up the page for each individual test.
  */
-test.customBackgrounds.setUp = function() {
+test.customize.setUp = function() {
   setUpPage('local-ntp-template');
 };
 
@@ -31,7 +31,7 @@
  * Tests that the edit custom background button is visible if both the flag is
  * enabled and no custom theme is being used.
  */
-test.customBackgrounds.testShowEditCustomBackground = function() {
+test.customize.testShowEditCustomBackground = function() {
   initLocalNTP(/*isGooglePage=*/true);
 
   assertTrue(elementIsVisible($('edit-bg')));
@@ -41,7 +41,7 @@
 /**
  * Tests that clicking on the gear icon opens the background option dialog.
  */
-test.customBackgrounds.testClickGearIcon = function() {
+test.customize.testClickGearIcon = function() {
   initLocalNTP(/*isGooglePage=*/true);
 
   $('edit-bg').click();
@@ -54,7 +54,7 @@
  * Test that clicking on the "Chrome backgrounds" option results in a correct
  * selection dialog.
  */
-test.customBackgrounds.testClickChromeBackgrounds = function() {
+test.customize.testClickChromeBackgrounds = function() {
   initLocalNTP(/*isGooglePage=*/true);
 
   $('edit-bg').click();
@@ -69,7 +69,7 @@
  * Test that clicking the cancel button on the collection selection dialog
  * closes the dialog.
  */
-test.customBackgrounds.testCollectionDialogCancel = function() {
+test.customize.testCollectionDialogCancel = function() {
   initLocalNTP(/*isGooglePage=*/true);
 
   $('edit-bg').click();
@@ -85,7 +85,7 @@
  * Test that clicking the done button on the collection selection dialog does
  * nothing.
  */
-test.customBackgrounds.testCollectionDialogDone = function() {
+test.customize.testCollectionDialogDone = function() {
   initLocalNTP(/*isGooglePage=*/true);
 
   $('edit-bg').click();
@@ -101,7 +101,7 @@
  * Test that clicking on a collection tile opens and loads the image selection
  * dialog.
  */
-test.customBackgrounds.testCollectionDialogTileClick = function() {
+test.customize.testCollectionDialogTileClick = function() {
   initLocalNTP(/*isGooglePage=*/true);
 
   $('edit-bg').click();
@@ -117,7 +117,7 @@
 /**
  * Test that clicking cancel on the image selection dialog closes the dialog.
  */
-test.customBackgrounds.testImageDialogCancel = function() {
+test.customize.testImageDialogCancel = function() {
   initLocalNTP(/*isGooglePage=*/true);
 
   $('edit-bg').click();
@@ -136,7 +136,7 @@
  * Test that clicking the back button on the image selection dialog results in
  * the collection selection dialog being displayed.
  */
-test.customBackgrounds.testImageDialogBack = function() {
+test.customize.testImageDialogBack = function() {
   initLocalNTP(/*isGooglePage=*/true);
 
   $('edit-bg').click();
@@ -154,7 +154,7 @@
 /**
  * Test that clicking on an image tile applies the selected styling.
  */
-test.customBackgrounds.testImageTileClick = function() {
+test.customize.testImageTileClick = function() {
   initLocalNTP(/*isGooglePage=*/true);
 
   $('edit-bg').click();
@@ -172,7 +172,7 @@
 /**
  * Test that clicking done with no image selected does nothing.
  */
-test.customBackgrounds.testImageDoneClickNoneSelected = function() {
+test.customize.testImageDoneClickNoneSelected = function() {
   initLocalNTP(/*isGooglePage=*/true);
 
   $('edit-bg').click();
@@ -190,7 +190,7 @@
 /**
  * Test that clicking done with an image selected closes the dialog.
  */
-test.customBackgrounds.testImageDoneClick = function() {
+test.customize.testImageDoneClick = function() {
   initLocalNTP(/*isGooglePage=*/true);
 
   $('edit-bg').click();
@@ -208,7 +208,7 @@
 /**
  * Test that no custom background option will be shown when offline.
  */
-test.customBackgrounds.testHideCustomBackgroundOffline = function() {
+test.customize.testHideCustomBackgroundOffline = function() {
   initLocalNTP(/*isGooglePage=*/true);
 
   let event = new Event('offline', {});
@@ -222,7 +222,7 @@
  * Test that clicking collection when offline will trigger an error
  * notification.
  */
-test.customBackgrounds.testClickCollectionOfflineShowErrorMsg = function() {
+test.customize.testClickCollectionOfflineShowErrorMsg = function() {
   initLocalNTP(/*isGooglePage=*/true);
 
   $('edit-bg').click();
@@ -250,8 +250,7 @@
  */
 setupFakeAsyncCollectionLoad = function() {
   // Override the collection loading script.
-  customBackgrounds.loadChromeBackgrounds =
-      function() {
+  customize.loadChromeBackgrounds = function() {
     var collScript = document.createElement('script');
     collScript.id = 'ntp-collection-loader';
     document.body.appendChild(collScript);
diff --git a/chrome/test/ppapi/ppapi_browsertest.cc b/chrome/test/ppapi/ppapi_browsertest.cc
index 2794eb6..6af106d 100644
--- a/chrome/test/ppapi/ppapi_browsertest.cc
+++ b/chrome/test/ppapi/ppapi_browsertest.cc
@@ -2188,9 +2188,10 @@
     const extensions::Extension* extension = LoadExtension(app_dir);
     ASSERT_TRUE(extension);
 
-    AppLaunchParams params(
-        browser()->profile(), extension, extensions::LAUNCH_CONTAINER_NONE,
-        WindowOpenDisposition::NEW_WINDOW, extensions::SOURCE_TEST);
+    AppLaunchParams params(browser()->profile(), extension->id(),
+                           extensions::LAUNCH_CONTAINER_NONE,
+                           WindowOpenDisposition::NEW_WINDOW,
+                           extensions::SOURCE_TEST);
     params.command_line = *base::CommandLine::ForCurrentProcess();
     OpenApplication(params);
   }
diff --git a/chrome/test/remoting/remote_desktop_browsertest.cc b/chrome/test/remoting/remote_desktop_browsertest.cc
index 2cf56e3..388f6da0 100644
--- a/chrome/test/remoting/remote_desktop_browsertest.cc
+++ b/chrome/test/remoting/remote_desktop_browsertest.cc
@@ -222,12 +222,11 @@
     window_open_disposition = WindowOpenDisposition::NEW_WINDOW;
   }
 
-  OpenApplication(AppLaunchParams(browser()->profile(), extension_,
-                                  is_platform_app()
-                                      ? extensions::LAUNCH_CONTAINER_NONE
-                                      : extensions::LAUNCH_CONTAINER_TAB,
-                                  window_open_disposition,
-                                  extensions::SOURCE_TEST));
+  OpenApplication(
+      AppLaunchParams(browser()->profile(), extension_->id(),
+                      is_platform_app() ? extensions::LAUNCH_CONTAINER_NONE
+                                        : extensions::LAUNCH_CONTAINER_TAB,
+                      window_open_disposition, extensions::SOURCE_TEST));
 
   observer.Wait();
 
diff --git a/chrome/updater/BUILD.gn b/chrome/updater/BUILD.gn
index 1d13162..edc9353 100644
--- a/chrome/updater/BUILD.gn
+++ b/chrome/updater/BUILD.gn
@@ -78,6 +78,7 @@
     ]
 
     deps = [
+      ":common",
       ":updater",
       "//base/test:test_support",
       "//testing/gtest",
diff --git a/chrome/updater/DEPS b/chrome/updater/DEPS
index 5aa062e..84242f7 100644
--- a/chrome/updater/DEPS
+++ b/chrome/updater/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+chrome/installer/util",
   "+components/crash/core/common",
   "+components/crx_file",
   "+components/prefs",
diff --git a/chrome/updater/crash_client.cc b/chrome/updater/crash_client.cc
index 9419856..d1d9fc0 100644
--- a/chrome/updater/crash_client.cc
+++ b/chrome/updater/crash_client.cc
@@ -55,7 +55,7 @@
   base::PathService::Get(base::FILE_EXE, &handler_path);
 
   base::FilePath database_path;
-  if (!GetProductDataDirectory(&database_path)) {
+  if (!GetProductDirectory(&database_path)) {
     LOG(ERROR) << "Failed to get the database path.";
     return false;
   }
diff --git a/chrome/updater/crash_reporter.cc b/chrome/updater/crash_reporter.cc
index db47c55..5607fb7 100644
--- a/chrome/updater/crash_reporter.cc
+++ b/chrome/updater/crash_reporter.cc
@@ -61,7 +61,7 @@
   base::PathService::Get(base::FILE_EXE, &handler_path);
 
   base::FilePath database_path;
-  if (!GetProductDataDirectory(&database_path)) {
+  if (!GetProductDirectory(&database_path)) {
     LOG(DFATAL) << "Failed to get the database path.";
     return;
   }
diff --git a/chrome/updater/installer.cc b/chrome/updater/installer.cc
index 8b2807a..904c9ed 100644
--- a/chrome/updater/installer.cc
+++ b/chrome/updater/installer.cc
@@ -23,6 +23,17 @@
 // Version "0" corresponds to no installed version.
 const char kNullVersion[] = "0.0.0.0";
 
+// Returns the full path to the installation directory for the application
+// identified by the |crx_id|.
+base::FilePath GetAppInstallDir(const std::string& crx_id) {
+  base::FilePath app_install_dir;
+  if (GetProductDirectory(&app_install_dir)) {
+    app_install_dir = app_install_dir.AppendASCII(kAppsDir);
+    app_install_dir = app_install_dir.AppendASCII(crx_id);
+  }
+  return app_install_dir;
+}
+
 }  // namespace
 
 Installer::InstallInfo::InstallInfo() : version(kNullVersion) {}
@@ -51,13 +62,8 @@
 void Installer::FindInstallOfApp() {
   VLOG(1) << __func__ << " for " << crx_id_;
 
-  base::FilePath root_install_path;
-  if (!GetProductDataDirectory(&root_install_path)) {
-    install_info_ = std::make_unique<InstallInfo>();
-    return;
-  }
-  root_install_path = root_install_path.AppendASCII(crx_id_);
-  if (!base::PathExists(root_install_path)) {
+  const base::FilePath app_install_dir = GetAppInstallDir(crx_id_);
+  if (app_install_dir.empty() || !base::PathExists(app_install_dir)) {
     install_info_ = std::make_unique<InstallInfo>();
     return;
   }
@@ -65,7 +71,7 @@
   base::Version latest_version(kNullVersion);
   base::FilePath latest_path;
   std::vector<base::FilePath> older_paths;
-  base::FileEnumerator file_enumerator(root_install_path, false,
+  base::FileEnumerator file_enumerator(app_install_dir, false,
                                        base::FileEnumerator::DIRECTORIES);
   for (auto path = file_enumerator.Next(); !path.value().empty();
        path = file_enumerator.Next()) {
@@ -118,40 +124,38 @@
   if (install_info_->version.CompareTo(manifest_version) > 0)
     return Result(update_client::InstallError::VERSION_NOT_UPGRADED);
 
-  base::FilePath root_install_path;
-  if (!GetProductDataDirectory(&root_install_path))
+  const base::FilePath app_install_dir = GetAppInstallDir(crx_id_);
+  if (app_install_dir.empty())
     return Result(update_client::InstallError::NO_DIR_COMPONENT_USER);
-
-  root_install_path = root_install_path.AppendASCII(crx_id_);
-  if (!base::CreateDirectory(root_install_path)) {
+  if (!base::CreateDirectory(app_install_dir)) {
     return Result(
         static_cast<int>(update_client::InstallError::CUSTOM_ERROR_BASE) +
         kCustomInstallErrorCreateAppInstallDirectory);
   }
 
-  const auto versioned_install_path =
-      root_install_path.AppendASCII(manifest_version.GetString());
-  if (base::PathExists(versioned_install_path)) {
-    if (!base::DeleteFile(versioned_install_path, true))
+  const auto versioned_install_dir =
+      app_install_dir.AppendASCII(manifest_version.GetString());
+  if (base::PathExists(versioned_install_dir)) {
+    if (!base::DeleteFile(versioned_install_dir, true))
       return Result(update_client::InstallError::CLEAN_INSTALL_DIR_FAILED);
   }
 
-  VLOG(1) << "Install_path=" << versioned_install_path.AsUTF8Unsafe();
+  VLOG(1) << "Install_path=" << versioned_install_dir.AsUTF8Unsafe();
 
-  if (!base::Move(unpack_path, versioned_install_path)) {
+  if (!base::Move(unpack_path, versioned_install_dir)) {
     PLOG(ERROR) << "Move failed.";
-    base::DeleteFile(versioned_install_path, true);
+    base::DeleteFile(versioned_install_dir, true);
     return Result(update_client::InstallError::MOVE_FILES_ERROR);
   }
 
   DCHECK(!base::PathExists(unpack_path));
-  DCHECK(base::PathExists(versioned_install_path));
+  DCHECK(base::PathExists(versioned_install_dir));
 
   install_info_->manifest = std::move(local_manifest);
   install_info_->version = manifest_version;
-  install_info_->install_dir = versioned_install_path;
+  install_info_->install_dir = versioned_install_dir;
   base::ReadFileToString(
-      versioned_install_path.AppendASCII("manifest.fingerprint"),
+      versioned_install_dir.AppendASCII("manifest.fingerprint"),
       &install_info_->fingerprint);
 
   return Result(update_client::InstallError::NONE);
diff --git a/chrome/updater/prefs.cc b/chrome/updater/prefs.cc
index c7406e7cf..eb9f6b4 100644
--- a/chrome/updater/prefs.cc
+++ b/chrome/updater/prefs.cc
@@ -17,7 +17,7 @@
 
 std::unique_ptr<PrefService> CreatePrefService() {
   base::FilePath product_data_dir;
-  if (!GetProductDataDirectory(&product_data_dir))
+  if (!GetProductDirectory(&product_data_dir))
     return nullptr;
 
   PrefServiceFactory pref_service_factory;
diff --git a/chrome/updater/updater.cc b/chrome/updater/updater.cc
index 65171a6..bb04c80 100644
--- a/chrome/updater/updater.cc
+++ b/chrome/updater/updater.cc
@@ -29,6 +29,7 @@
 #include "base/threading/thread_restrictions.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
+#include "build/build_config.h"
 #include "chrome/updater/configurator.h"
 #include "chrome/updater/crash_client.h"
 #include "chrome/updater/crash_reporter.h"
@@ -41,6 +42,18 @@
 #include "components/update_client/crx_update_item.h"
 #include "components/update_client/update_client.h"
 
+#if defined(OS_WIN)
+#include "chrome/updater/win/setup/setup.h"
+#include "chrome/updater/win/setup/uninstall.h"
+#endif
+
+// To install the updater, run:
+// "updater.exe --install --enable-logging --v=1 --vmodule=*/chrome/updater/*"
+// from the build directory. The program needs a number of dependencies which
+// are available in the build out directory.
+// To uninstall, run "updater.exe --uninstall" from its install directory or
+// from the build out directory. Doing this will make the program delete its
+// install directory using a shim cmd script.
 namespace updater {
 
 namespace {
@@ -88,7 +101,7 @@
 void InitLogging(const base::CommandLine& command_line) {
   logging::LoggingSettings settings;
   base::FilePath log_dir;
-  GetProductDataDirectory(&log_dir);
+  GetProductDirectory(&log_dir);
   const auto log_file = log_dir.Append(FILE_PATH_LITERAL("updater.log"));
   settings.log_file = log_file.value().c_str();
   settings.logging_dest = logging::LOG_TO_ALL;
@@ -121,6 +134,22 @@
   ThreadPoolStop();
 }
 
+int UpdaterInstall() {
+#if defined(OS_WIN)
+  return Setup();
+#else
+  return -1;
+#endif
+}
+
+int UpdaterUninstall() {
+#if defined(OS_WIN)
+  return updater::Uninstall();
+#else
+  return -1;
+#endif
+}
+
 }  // namespace
 
 int UpdaterMain(int argc, const char* const* argv) {
@@ -134,9 +163,8 @@
 
   InitLogging(*command_line);
 
-  if (command_line->HasSwitch(kCrashHandlerSwitch)) {
+  if (command_line->HasSwitch(kCrashHandlerSwitch))
     return CrashReporterMain();
-  }
 
   InitializeUpdaterMain();
 
@@ -145,6 +173,14 @@
     return *ptr;
   }
 
+  if (command_line->HasSwitch(kInstall)) {
+    return UpdaterInstall();
+  }
+
+  if (command_line->HasSwitch(kUninstall)) {
+    return UpdaterUninstall();
+  }
+
   auto installer = base::MakeRefCounted<Installer>(
       std::vector<uint8_t>(std::cbegin(mimo_hash), std::cend(mimo_hash)));
   installer->FindInstallOfApp();
diff --git a/chrome/updater/updater_constants.cc b/chrome/updater/updater_constants.cc
index 4a54f771..ed9df7edd 100644
--- a/chrome/updater/updater_constants.cc
+++ b/chrome/updater/updater_constants.cc
@@ -8,6 +8,8 @@
 
 const char kCrashMeSwitch[] = "crash-me";
 const char kCrashHandlerSwitch[] = "crash-handler";
+const char kInstall[] = "install";
+const char kUninstall[] = "uninstall";
 const char kTestSwitch[] = "test";
 
 const char kNoRateLimit[] = "--no-rate-limit";
@@ -18,4 +20,7 @@
 const char kCrashStagingUploadURL[] =
     "https://clients2.google.com/cr/staging_report";
 
+extern const char kAppsDir[] = "apps";
+extern const char kUninstallScript[] = "uninstall.cmd";
+
 }  // namespace updater
diff --git a/chrome/updater/updater_constants.h b/chrome/updater/updater_constants.h
index 235cddd..5fc5e69 100644
--- a/chrome/updater/updater_constants.h
+++ b/chrome/updater/updater_constants.h
@@ -15,6 +15,12 @@
 // Runs as the Crashpad handler.
 extern const char kCrashHandlerSwitch[];
 
+// Installs the updater.
+extern const char kInstall[];
+
+// Uninstalls the updater.
+extern const char kUninstall[];
+
 // Runs in test mode. Currently, it exits right away.
 extern const char kTestSwitch[];
 
@@ -31,6 +37,16 @@
 extern const char kCrashUploadURL[];
 extern const char kCrashStagingUploadURL[];
 
+// Paths.
+//
+// The directory name where CRX apps get installed. This is provided for demo
+// purposes, since products installed by this updater will be installed in
+// their specific locations.
+extern const char kAppsDir[];
+
+// The name of the uninstall script which is invoked by the --uninstall switch.
+extern const char kUninstallScript[];
+
 // Errors.
 //
 // The install directory for the application could not be created.
diff --git a/chrome/updater/util.cc b/chrome/updater/util.cc
index f55df3e1..4f3f696 100644
--- a/chrome/updater/util.cc
+++ b/chrome/updater/util.cc
@@ -14,7 +14,7 @@
 
 namespace updater {
 
-bool GetProductDataDirectory(base::FilePath* path) {
+bool GetProductDirectory(base::FilePath* path) {
   constexpr int kPathKey =
 #if defined(OS_WIN)
       base::DIR_LOCAL_APP_DATA;
diff --git a/chrome/updater/util.h b/chrome/updater/util.h
index 686b403..da5861e 100644
--- a/chrome/updater/util.h
+++ b/chrome/updater/util.h
@@ -11,8 +11,8 @@
 
 namespace updater {
 
-// Returns a directory where updater or user data is stored.
-bool GetProductDataDirectory(base::FilePath* path);
+// Returns a directory where updater files or its data is stored.
+bool GetProductDirectory(base::FilePath* path);
 
 }  // namespace updater
 
diff --git a/chrome/updater/win/BUILD.gn b/chrome/updater/win/BUILD.gn
index 8a86844..d99451251 100644
--- a/chrome/updater/win/BUILD.gn
+++ b/chrome/updater/win/BUILD.gn
@@ -28,6 +28,15 @@
   ]
 }
 
+copy("uninstall.cmd") {
+  sources = [
+    "setup/uninstall.cmd",
+  ]
+  outputs = [
+    "$target_gen_dir/uninstall.cmd",
+  ]
+}
+
 process_version_rc_template("version_resources") {
   sources = [
     "updater.ver",
@@ -45,14 +54,27 @@
     "net/network_winhttp.cc",
     "net/network_winhttp.h",
     "net/scoped_hinternet.h",
+    "setup/setup.cc",
+    "setup/setup.h",
+    "setup/uninstall.cc",
+    "setup/uninstall.h",
     "util.cc",
     "util.h",
   ]
 
   deps = [
     "//base",
+    "//chrome/installer/util:with_no_strings",
     "//components/update_client",
   ]
+
+  data = [
+    "setup/uninstall.cmd",
+  ]
+
+  data_deps = [
+    ":uninstall.cmd",
+  ]
 }
 
 source_set("unittest") {
diff --git a/chrome/updater/win/setup/setup.cc b/chrome/updater/win/setup/setup.cc
new file mode 100644
index 0000000..2d7ab4e
--- /dev/null
+++ b/chrome/updater/win/setup/setup.cc
@@ -0,0 +1,88 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/updater/win/setup/setup.h"
+
+#include <memory>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "chrome/installer/util/copy_tree_work_item.h"
+#include "chrome/installer/util/self_cleaning_temp_dir.h"
+#include "chrome/installer/util/work_item_list.h"
+#include "chrome/updater/updater_constants.h"
+#include "chrome/updater/util.h"
+
+namespace updater {
+
+namespace {
+
+constexpr char kBuildGenWinDir[] = "gen\\chrome\\updater\\win";
+
+const base::char16* kUpdaterFiles[] = {
+    L"updater.exe",
+#if defined(COMPONENT_BUILD)
+    L"base.dll",    L"boringssl.dll", L"crcrypto.dll",      L"icuuc.dll",
+    L"libc++.dll",  L"prefs.dll",     L"protobuf_lite.dll", L"updater.exe",
+    L"url_lib.dll", L"zlib.dll",
+#endif
+};
+
+}  // namespace
+
+int Setup() {
+  VLOG(1) << __func__;
+
+  base::FilePath temp_dir;
+  if (!base::GetTempDir(&temp_dir)) {
+    LOG(ERROR) << "GetTempDir failed.";
+    return -1;
+  }
+  base::FilePath product_dir;
+  if (!GetProductDirectory(&product_dir)) {
+    LOG(ERROR) << "GetProductDirectory failed.";
+    return -1;
+  }
+  base::FilePath exe_path;
+  if (!base::PathService::Get(base::FILE_EXE, &exe_path)) {
+    LOG(ERROR) << "PathService failed.";
+    return -1;
+  }
+
+  installer::SelfCleaningTempDir backup_dir;
+  if (!backup_dir.Initialize(temp_dir, L"updater-backup")) {
+    LOG(ERROR) << "Failed to initialize the backup dir.";
+    return -1;
+  }
+
+  const base::FilePath source_dir = exe_path.DirName();
+
+  std::unique_ptr<WorkItemList> install_list(WorkItem::CreateWorkItemList());
+  for (const auto* file : kUpdaterFiles) {
+    const base::FilePath target_path = product_dir.Append(file);
+    const base::FilePath source_path = source_dir.Append(file);
+    install_list->AddWorkItem(
+        WorkItem::CreateCopyTreeWorkItem(source_path, target_path, temp_dir,
+                                         WorkItem::ALWAYS, base::FilePath()));
+  }
+  install_list->AddWorkItem(WorkItem::CreateCopyTreeWorkItem(
+      source_dir.AppendASCII(kBuildGenWinDir).AppendASCII(kUninstallScript),
+      product_dir.AppendASCII(kUninstallScript), temp_dir, WorkItem::ALWAYS,
+      base::FilePath()));
+
+  if (!install_list->Do()) {
+    LOG(ERROR) << "Install failed, rolling back...";
+    install_list->Rollback();
+    LOG(ERROR) << "Rollback complete.";
+    return -1;
+  }
+
+  VLOG(1) << "Setup succeeded.";
+  return 0;
+}
+
+}  // namespace updater
diff --git a/chrome/updater/win/setup/setup.h b/chrome/updater/win/setup/setup.h
new file mode 100644
index 0000000..2a306ba
--- /dev/null
+++ b/chrome/updater/win/setup/setup.h
@@ -0,0 +1,14 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_UPDATER_WIN_SETUP_SETUP_H_
+#define CHROME_UPDATER_WIN_SETUP_SETUP_H_
+
+namespace updater {
+
+int Setup();
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_WIN_SETUP_SETUP_H_
diff --git a/chrome/updater/win/setup/uninstall.cc b/chrome/updater/win/setup/uninstall.cc
new file mode 100644
index 0000000..ca5332c
--- /dev/null
+++ b/chrome/updater/win/setup/uninstall.cc
@@ -0,0 +1,60 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/updater/win/setup/uninstall.h"
+
+#include <windows.h>
+#include <memory>
+
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/process/launch.h"
+#include "base/process/process.h"
+#include "base/stl_util.h"
+#include "base/strings/string16.h"
+#include "base/strings/stringprintf.h"
+#include "chrome/updater/updater_constants.h"
+#include "chrome/updater/util.h"
+
+namespace updater {
+
+// Runs the uninstall script in the install directory of the updater. This is
+// a best effort uninstall. The execution of this function and the script race
+// each other but the script loops and wait in between iterations trying to
+// delete the install directory.
+int Uninstall() {
+  VLOG(1) << __func__;
+
+  base::FilePath product_dir;
+  if (!GetProductDirectory(&product_dir)) {
+    LOG(ERROR) << "GetProductDirectory failed.";
+    return -1;
+  }
+
+  base::char16 cmd_path[MAX_PATH] = {0};
+  auto size = ExpandEnvironmentStrings(L"%SystemRoot%\\System32\\cmd.exe",
+                                       cmd_path, base::size(cmd_path));
+  if (!size || size >= MAX_PATH)
+    return -1;
+
+  base::FilePath script_path = product_dir.AppendASCII(kUninstallScript);
+
+  base::string16 cmdline = cmd_path;
+  base::StringAppendF(&cmdline, L" /Q /C \"%ls\"",
+                      script_path.AsUTF16Unsafe().c_str());
+  base::LaunchOptions options;
+  options.start_hidden = true;
+
+  VLOG(1) << "Running " << cmdline;
+
+  auto process = base::LaunchProcess(cmdline, options);
+  if (!process.IsValid()) {
+    LOG(ERROR) << "Failed to create process " << cmdline;
+    return -1;
+  }
+
+  return 0;
+}
+
+}  // namespace updater
diff --git a/chrome/updater/win/setup/uninstall.cmd b/chrome/updater/win/setup/uninstall.cmd
new file mode 100644
index 0000000..9f3ba4286
--- /dev/null
+++ b/chrome/updater/win/setup/uninstall.cmd
@@ -0,0 +1,13 @@
+rem Deletes the script's parent directory if \AppData\Local\ChromeUpdater\ is

+rem anywhere in the directory path. Sleeps 3 seconds and tries 3 times to

+rem delete the directory.

+@echo off

+set Directory=%~dp0

+@echo %Directory% | FindStr /R \\AppData\\Local\\ChromeUpdater\\ > nul

+IF %ERRORLEVEL% NEQ 0 exit 1

+@echo Deleting "%Directory%"...

+for /L %%G IN (1,1,3) do (

+  rmdir "%Directory%" /s /q > nul

+  if not exist "%Directory%" exit 0

+  ping -n 3 127.0.0.1 > nul

+)

diff --git a/chrome/updater/win/setup/uninstall.h b/chrome/updater/win/setup/uninstall.h
new file mode 100644
index 0000000..1080629
--- /dev/null
+++ b/chrome/updater/win/setup/uninstall.h
@@ -0,0 +1,14 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_UPDATER_WIN_SETUP_UNINSTALL_H_
+#define CHROME_UPDATER_WIN_SETUP_UNINSTALL_H_
+
+namespace updater {
+
+int Uninstall();
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_WIN_SETUP_UNINSTALL_H_
diff --git a/chromecast/browser/android/BUILD.gn b/chromecast/browser/android/BUILD.gn
index 05bb46f..2bd24ea 100644
--- a/chromecast/browser/android/BUILD.gn
+++ b/chromecast/browser/android/BUILD.gn
@@ -147,6 +147,7 @@
     "//content/public/android:content_java",
     "//media/base/android:media_java",
     "//net/android:net_java",
+    "//third_party/android_deps:com_android_support_support_compat_java",
 
     # TODO(slan): We may need to pass this in as a parameter.
     "//third_party/android_deps:com_android_support_support_core_utils_java",
diff --git a/chromecast/browser/android/apk/res/drawable-hdpi/ic_settings_cast.png b/chromecast/browser/android/apk/res/drawable-hdpi/ic_settings_cast.png
new file mode 100644
index 0000000..72d8318
--- /dev/null
+++ b/chromecast/browser/android/apk/res/drawable-hdpi/ic_settings_cast.png
Binary files differ
diff --git a/chromecast/browser/android/apk/res/drawable-mdpi/ic_settings_cast.png b/chromecast/browser/android/apk/res/drawable-mdpi/ic_settings_cast.png
new file mode 100644
index 0000000..4076d5d
--- /dev/null
+++ b/chromecast/browser/android/apk/res/drawable-mdpi/ic_settings_cast.png
Binary files differ
diff --git a/chromecast/browser/android/apk/res/drawable-xhdpi/ic_settings_cast.png b/chromecast/browser/android/apk/res/drawable-xhdpi/ic_settings_cast.png
new file mode 100644
index 0000000..c7f092c
--- /dev/null
+++ b/chromecast/browser/android/apk/res/drawable-xhdpi/ic_settings_cast.png
Binary files differ
diff --git a/chromecast/browser/android/apk/res/drawable-xxhdpi/ic_settings_cast.png b/chromecast/browser/android/apk/res/drawable-xxhdpi/ic_settings_cast.png
new file mode 100644
index 0000000..9addc3b
--- /dev/null
+++ b/chromecast/browser/android/apk/res/drawable-xxhdpi/ic_settings_cast.png
Binary files differ
diff --git a/chromecast/browser/android/apk/res/drawable-xxxhdpi/ic_settings_cast.png b/chromecast/browser/android/apk/res/drawable-xxxhdpi/ic_settings_cast.png
new file mode 100644
index 0000000..dfbc10f3
--- /dev/null
+++ b/chromecast/browser/android/apk/res/drawable-xxxhdpi/ic_settings_cast.png
Binary files differ
diff --git a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsService.java b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsService.java
index 8fd841d..bc68308e 100644
--- a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsService.java
+++ b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsService.java
@@ -5,10 +5,15 @@
 package org.chromium.chromecast.shell;
 
 import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
 import android.app.Service;
+import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
+import android.os.Build;
 import android.os.IBinder;
+import android.support.v4.app.NotificationCompat;
 import android.widget.Toast;
 
 import org.chromium.base.Log;
@@ -30,6 +35,8 @@
     private static final String TAG = "cr_CastWebService";
     private static final boolean DEBUG = true;
     private static final int CAST_NOTIFICATION_ID = 100;
+    private static final String NOTIFICATION_CHANNEL_ID =
+            "org.chromium.chromecast.shell.CastWebContentsService.channel";
 
     private final Controller<Intent> mIntentState = new Controller<>();
     private final Observable<WebContents> mWebContentsState =
@@ -41,14 +48,17 @@
     {
         // React to web contents by presenting them in a headless view.
         mWebContentsState.subscribe(CastWebContentsScopes.withoutLayout(this));
-        mWebContentsState.subscribe(x -> {
-            // TODO(thoren): Notification.Builder(Context) is deprecated in O. Use the
-            // (Context, String) constructor when CastWebContentsService starts supporting O.
-            Notification notification = new Notification.Builder(this).build();
+        mWebContentsState.subscribe(webContents -> {
+            Notification notification =
+                    new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
+                            .setContentTitle(webContents.getTitle())
+                            .setContentText("A Google Cast app is running in the background")
+                            .setSmallIcon(R.drawable.ic_settings_cast)
+                            .build();
             startForeground(CAST_NOTIFICATION_ID, notification);
             return () -> stopForeground(true /*removeNotification*/);
         });
-        mWebContentsState.map(this ::getMediaSessionImpl)
+        mWebContentsState.map(this::getMediaSessionImpl)
                 .subscribe(Observers.onEnter(MediaSessionImpl::requestSystemAudioFocus));
         // Inform CastContentWindowAndroid we're detaching.
         Observable<String> instanceIdState = mIntentState.map(Intent::getData).map(Uri::getPath);
@@ -71,6 +81,7 @@
                     .show();
             stopSelf();
         }
+        createNotificationChannel();
     }
 
     @Override
@@ -101,4 +112,14 @@
     void setMediaSessionImplGetterForTesting(Function<WebContents, MediaSessionImpl> getter) {
         mMediaSessionGetter = getter;
     }
+
+    private void createNotificationChannel() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID,
+                    "Cast Audio Apps", NotificationManager.IMPORTANCE_NONE);
+            NotificationManager notificationManager =
+                    (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+            notificationManager.createNotificationChannel(channel);
+        }
+    }
 }
diff --git a/chromecast/browser/android/junit/src/org/chromium/chromecast/shell/CastWebContentsServiceTest.java b/chromecast/browser/android/junit/src/org/chromium/chromecast/shell/CastWebContentsServiceTest.java
index 9c33df175..fde4fa0 100644
--- a/chromecast/browser/android/junit/src/org/chromium/chromecast/shell/CastWebContentsServiceTest.java
+++ b/chromecast/browser/android/junit/src/org/chromium/chromecast/shell/CastWebContentsServiceTest.java
@@ -11,7 +11,9 @@
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
+import android.app.Notification;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -41,6 +43,8 @@
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
 public class CastWebContentsServiceTest {
+    private static final String WEBCONTENTS_TITLE = "CastWebContentsServiceTest_title";
+
     private @Mock WebContents mWebContents;
     private @Mock MediaSessionImpl mMediaSessionImpl;
     private String mInstanceId;
@@ -73,6 +77,7 @@
     @Before
     public void setUp() {
         mWebContents = mock(WebContents.class);
+        when(mWebContents.getTitle()).thenReturn(WEBCONTENTS_TITLE);
         mMediaSessionImpl = mock(MediaSessionImpl.class);
         mInstanceId = "1";
         mIntent = CastWebContentsIntentUtils.requestStartCastService(
@@ -96,6 +101,32 @@
     }
 
     @Test
+    public void testForegroundNotificationHasUniqueChannelId() {
+        mServiceLifecycle.bind();
+        Notification notification = mShadowService.getLastForegroundNotification();
+        String notificationChannelId = notification.getChannelId();
+        assertNotNull(notificationChannelId);
+        assertEquals("org.chromium.chromecast.shell.CastWebContentsService.channel",
+                notificationChannelId);
+    }
+
+    @Test
+    public void testForegroundNotificationHasCorrectSmallIcon() {
+        mServiceLifecycle.bind();
+        Notification notification = mShadowService.getLastForegroundNotification();
+        assertNotNull(notification.getSmallIcon());
+        assertEquals(R.drawable.ic_settings_cast, notification.getSmallIcon().getResId());
+    }
+
+    @Test
+    public void testNotificationTitleMatchesWebContentsTitle() {
+        mServiceLifecycle.bind();
+        Notification notification = mShadowService.getLastForegroundNotification();
+        assertEquals(notification.extras.getCharSequence(Notification.EXTRA_TITLE).toString(),
+                WEBCONTENTS_TITLE);
+    }
+
+    @Test
     public void testBackgroundedAfterUnbind() {
         mServiceLifecycle.bind().unbind();
         assertTrue(mShadowService.getNotificationShouldRemoved());
diff --git a/chromecast/browser/extensions/cast_extension_host_delegate.cc b/chromecast/browser/extensions/cast_extension_host_delegate.cc
index 3bf69fc6..863805d 100644
--- a/chromecast/browser/extensions/cast_extension_host_delegate.cc
+++ b/chromecast/browser/extensions/cast_extension_host_delegate.cc
@@ -7,6 +7,7 @@
 #include "base/logging.h"
 #include "base/no_destructor.h"
 #include "chromecast/browser/extensions/cast_extension_web_contents_observer.h"
+#include "content/public/browser/web_contents_delegate.h"
 #include "extensions/browser/media_capture_util.h"
 #include "extensions/browser/serial_extension_host_queue.h"
 
@@ -62,12 +63,13 @@
   return queue.get();
 }
 
-gfx::Size CastExtensionHostDelegate::EnterPictureInPicture(
+content::PictureInPictureResult
+CastExtensionHostDelegate::EnterPictureInPicture(
     content::WebContents* web_contents,
     const viz::SurfaceId& surface_id,
     const gfx::Size& natural_size) {
   NOTREACHED();
-  return gfx::Size();
+  return content::PictureInPictureResult::kNotSupported;
 }
 
 void CastExtensionHostDelegate::ExitPictureInPicture() {
diff --git a/chromecast/browser/extensions/cast_extension_host_delegate.h b/chromecast/browser/extensions/cast_extension_host_delegate.h
index a15a6ff..447e3d9 100644
--- a/chromecast/browser/extensions/cast_extension_host_delegate.h
+++ b/chromecast/browser/extensions/cast_extension_host_delegate.h
@@ -34,9 +34,10 @@
                                   blink::mojom::MediaStreamType type,
                                   const Extension* extension) override;
   ExtensionHostQueue* GetExtensionHostQueue() const override;
-  gfx::Size EnterPictureInPicture(content::WebContents* web_contents,
-                                  const viz::SurfaceId& surface_id,
-                                  const gfx::Size& natural_size) override;
+  content::PictureInPictureResult EnterPictureInPicture(
+      content::WebContents* web_contents,
+      const viz::SurfaceId& surface_id,
+      const gfx::Size& natural_size) override;
   void ExitPictureInPicture() override;
 
  private:
diff --git a/chromeos/dbus/power/fake_power_manager_client.cc b/chromeos/dbus/power/fake_power_manager_client.cc
index d5088fe..1081ea74 100644
--- a/chromeos/dbus/power/fake_power_manager_client.cc
+++ b/chromeos/dbus/power/fake_power_manager_client.cc
@@ -321,6 +321,12 @@
     TimerId timer_id,
     base::TimeTicks absolute_expiration_time,
     VoidDBusMethodCallback callback) {
+  if (simulate_start_arc_timer_failure_) {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::BindOnce(std::move(callback), false));
+    return;
+  }
+
   auto it = timer_expiration_fds_.find(timer_id);
   if (it == timer_expiration_fds_.end()) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
diff --git a/chromeos/dbus/power/fake_power_manager_client.h b/chromeos/dbus/power/fake_power_manager_client.h
index 2e1e57c..390dd58 100644
--- a/chromeos/dbus/power/fake_power_manager_client.h
+++ b/chromeos/dbus/power/fake_power_manager_client.h
@@ -195,6 +195,10 @@
     tick_clock_ = tick_clock;
   }
 
+  void simulate_start_arc_timer_failure(bool simulate) {
+    simulate_start_arc_timer_failure_ = simulate;
+  }
+
  private:
   // Notifies |observers_| that |props_| has been updated.
   void NotifyObservers();
@@ -291,6 +295,9 @@
   // Clock to use to calculate time ticks. Used for ArcTimer related APIs.
   const base::TickClock* tick_clock_;
 
+  // If set then |StartArcTimer| returns failure.
+  bool simulate_start_arc_timer_failure_ = false;
+
   // Note: This should remain the last member so it'll be destroyed and
   // invalidate its weak pointers before any other members are destroyed.
   base::WeakPtrFactory<FakePowerManagerClient> weak_ptr_factory_{this};
diff --git a/chromeos/services/assistant/assistant_manager_service_impl.cc b/chromeos/services/assistant/assistant_manager_service_impl.cc
index 7d02a553..9212862c 100644
--- a/chromeos/services/assistant/assistant_manager_service_impl.cc
+++ b/chromeos/services/assistant/assistant_manager_service_impl.cc
@@ -572,11 +572,12 @@
                      weak_factory_.GetWeakPtr(), text));
 }
 
-void AssistantManagerServiceImpl::OnOpenUrl(const std::string& url) {
+void AssistantManagerServiceImpl::OnOpenUrl(const std::string& url,
+                                            bool is_background) {
   service_->main_task_runner()->PostTask(
       FROM_HERE,
       base::BindOnce(&AssistantManagerServiceImpl::OnOpenUrlOnMainThread,
-                     weak_factory_.GetWeakPtr(), url));
+                     weak_factory_.GetWeakPtr(), url, is_background));
 }
 
 void AssistantManagerServiceImpl::OnShowNotification(
@@ -660,7 +661,7 @@
     std::string url = GetWebUrlFromMediaArgs(play_media_args_proto);
     // Fallack to web URL.
     if (!url.empty())
-      OnOpenUrlOnMainThread(url);
+      OnOpenUrlOnMainThread(url, /*in_background=*/false);
   }
 }
 
@@ -1242,12 +1243,14 @@
       [&text](auto* ptr) { ptr->OnTextResponse(text); });
 }
 
-void AssistantManagerServiceImpl::OnOpenUrlOnMainThread(
-    const std::string& url) {
+void AssistantManagerServiceImpl::OnOpenUrlOnMainThread(const std::string& url,
+                                                        bool in_background) {
   receive_url_response_ = url;
+  const GURL gurl = GURL(url);
 
-  interaction_subscribers_.ForAllPtrs(
-      [&url](auto* ptr) { ptr->OnOpenUrlResponse(GURL(url)); });
+  interaction_subscribers_.ForAllPtrs([&gurl, in_background](auto* ptr) {
+    ptr->OnOpenUrlResponse(gurl, in_background);
+  });
 }
 
 void AssistantManagerServiceImpl::OnPlaybackStateChange(
diff --git a/chromeos/services/assistant/assistant_manager_service_impl.h b/chromeos/services/assistant/assistant_manager_service_impl.h
index 7e55d63..b5feb1fe 100644
--- a/chromeos/services/assistant/assistant_manager_service_impl.h
+++ b/chromeos/services/assistant/assistant_manager_service_impl.h
@@ -145,7 +145,7 @@
   void OnShowSuggestions(
       const std::vector<action::Suggestion>& suggestions) override;
   void OnShowText(const std::string& text) override;
-  void OnOpenUrl(const std::string& url) override;
+  void OnOpenUrl(const std::string& url, bool in_background) override;
   void OnPlaybackStateChange(
       const assistant_client::MediaStatus& status) override;
   void OnShowNotification(const action::Notification& notification) override;
@@ -232,7 +232,7 @@
   void OnShowSuggestionsOnMainThread(
       const std::vector<mojom::AssistantSuggestionPtr>& suggestions);
   void OnShowTextOnMainThread(const std::string& text);
-  void OnOpenUrlOnMainThread(const std::string& url);
+  void OnOpenUrlOnMainThread(const std::string& url, bool in_background);
   void OnShowNotificationOnMainThread(
       const mojom::AssistantNotificationPtr& notification);
   void OnNotificationRemovedOnMainThread(const std::string& grouping_id);
diff --git a/chromeos/services/assistant/public/mojom/assistant.mojom b/chromeos/services/assistant/public/mojom/assistant.mojom
index 5e31f49..58b5344 100644
--- a/chromeos/services/assistant/public/mojom/assistant.mojom
+++ b/chromeos/services/assistant/public/mojom/assistant.mojom
@@ -111,8 +111,9 @@
   // Assistant got text response from server.
   OnTextResponse(string response);
 
-  // Assistant got open URL response from server.
-  OnOpenUrlResponse(url.mojom.Url url);
+  // Assistant got open URL response from server. |in_background| refers to
+  // being in background of Assistant UI, not in background of browser.
+  OnOpenUrlResponse(url.mojom.Url url, bool in_background);
 
   // Assistant speech recognition has started.
   OnSpeechRecognitionStarted();
diff --git a/components/arc/common/BUILD.gn b/components/arc/common/BUILD.gn
index 9ae776a..eab31cdd 100644
--- a/components/arc/common/BUILD.gn
+++ b/components/arc/common/BUILD.gn
@@ -88,9 +88,9 @@
     ]
   }
 
-  # Media related mojo interfaces. There are used by //services/ws/public/mojom.
-  # We have this separate mojom target to avoid pulling in unnecessary
-  # interfaces.
+  # Media related mojo interfaces. These are used by
+  # //services/viz/public/interfaces. We have this separate mojom target to
+  # avoid pulling in unnecessary interfaces.
   mojom("media") {
     sources = [
       "gfx.mojom",
diff --git a/components/arc/ime/arc_ime_service.h b/components/arc/ime/arc_ime_service.h
index a31d416..53eb8b5 100644
--- a/components/arc/ime/arc_ime_service.h
+++ b/components/arc/ime/arc_ime_service.h
@@ -9,6 +9,7 @@
 
 #include "ash/keyboard/ui/keyboard_controller_observer.h"
 #include "base/macros.h"
+#include "base/optional.h"
 #include "components/arc/ime/arc_ime_bridge.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "ui/aura/client/focus_change_observer.h"
diff --git a/components/autofill/core/browser/data_model/autofill_profile.cc b/components/autofill/core/browser/data_model/autofill_profile.cc
index c4b83cf..c27a78c 100644
--- a/components/autofill/core/browser/data_model/autofill_profile.cc
+++ b/components/autofill/core/browser/data_model/autofill_profile.cc
@@ -544,8 +544,7 @@
                      app_locale)) {
         return false;
       }
-    } else if (!comparator.MatchesAfterNormalization(
-                   value, profile.GetInfo(type, app_locale))) {
+    } else if (!comparator.Compare(value, profile.GetInfo(type, app_locale))) {
       return false;
     }
   }
diff --git a/components/autofill/core/browser/data_model/autofill_profile_comparator.cc b/components/autofill/core/browser/data_model/autofill_profile_comparator.cc
index 46604ba..ae4f0e9 100644
--- a/components/autofill/core/browser/data_model/autofill_profile_comparator.cc
+++ b/components/autofill/core/browser/data_model/autofill_profile_comparator.cc
@@ -48,6 +48,142 @@
   return os;
 }
 
+bool IsPunctuationOrWhitespace(const int8_t character) {
+  switch (character) {
+    // Punctuation
+    case U_DASH_PUNCTUATION:
+    case U_START_PUNCTUATION:
+    case U_END_PUNCTUATION:
+    case U_CONNECTOR_PUNCTUATION:
+    case U_OTHER_PUNCTUATION:
+    // Whitespace
+    case U_CONTROL_CHAR:  // To escape the '\n' character.
+    case U_SPACE_SEPARATOR:
+    case U_LINE_SEPARATOR:
+    case U_PARAGRAPH_SEPARATOR:
+      return true;
+
+    default:
+      return false;
+  }
+}
+
+// Iterator for a string that processes punctuation and white space according to
+// |collapse_skippable_|.
+class NormalizingIterator {
+ public:
+  NormalizingIterator(
+      const base::StringPiece16& text,
+      AutofillProfileComparator::WhitespaceSpec whitespace_spec);
+  ~NormalizingIterator();
+
+  // Advances to the next non-skippable character in the string. Whether a
+  // punctuation or white space character is skippable depends on
+  // |collapse_skippable_|. Returns false if the end of the string has been
+  // reached.
+  void Advance();
+
+  // Returns true if the iterator has reached the end of the string.
+  bool End();
+
+  // Returns true if the iterator ends in skippable characters or if the
+  // iterator has reached the end of the string. Has the side effect of
+  // advancing the iterator to either the first skippable character or to the
+  // end of the string.
+  bool EndsInSkippableCharacters();
+
+  // Returns the next character that should be considered.
+  int32_t GetNextChar();
+
+ private:
+  // When |collapse_skippable_| is false, this member is initialized to false
+  // and is not updated.
+  //
+  // When |collapse_skippable_| is true, this member indicates whether the
+  // previous character was punctuation or white space so that one or more
+  // consecutive embedded punctuation and white space characters can be
+  // collapsed to a single white space.
+  bool previous_was_skippable_;
+
+  // True if punctuation and white space within the string should be collapsed
+  // to a single white space.
+  bool collapse_skippable_;
+
+  base::i18n::UTF16CharIterator iter_;
+};
+
+NormalizingIterator::NormalizingIterator(
+    const base::StringPiece16& text,
+    AutofillProfileComparator::WhitespaceSpec whitespace_spec)
+    : previous_was_skippable_(false),
+      collapse_skippable_(whitespace_spec ==
+                          AutofillProfileComparator::RETAIN_WHITESPACE),
+      iter_(base::i18n::UTF16CharIterator(text.data(), text.length())) {
+  int32_t character = iter_.get();
+
+  while (!iter_.end() && IsPunctuationOrWhitespace(u_charType(character))) {
+    iter_.Advance();
+    character = iter_.get();
+  }
+}
+
+NormalizingIterator::~NormalizingIterator() = default;
+
+void NormalizingIterator::Advance() {
+  if (!iter_.Advance()) {
+    return;
+  }
+
+  while (!End()) {
+    int32_t character = iter_.get();
+    bool is_punctuation_or_whitespace =
+        IsPunctuationOrWhitespace(u_charType(character));
+
+    if (!is_punctuation_or_whitespace) {
+      previous_was_skippable_ = false;
+      return;
+    }
+
+    if (is_punctuation_or_whitespace && !previous_was_skippable_ &&
+        collapse_skippable_) {
+      // Punctuation or white space within the string was found, e.g. the "," in
+      // the string "Hotel Schmotel, 3 Old Rd", and is after a non-skippable
+      // character.
+      previous_was_skippable_ = true;
+      return;
+    }
+
+    iter_.Advance();
+  }
+}
+
+bool NormalizingIterator::End() {
+  return iter_.end();
+}
+
+bool NormalizingIterator::EndsInSkippableCharacters() {
+  while (!End()) {
+    int32_t character = iter_.get();
+    if (!IsPunctuationOrWhitespace(u_charType(character))) {
+      return false;
+    }
+    iter_.Advance();
+  }
+  return true;
+}
+
+int32_t NormalizingIterator::GetNextChar() {
+  if (End()) {
+    return 0;
+  }
+
+  if (previous_was_skippable_) {
+    return ' ';
+  }
+
+  return iter_.get();
+}
+
 }  // namespace
 
 AutofillProfileComparator::AutofillProfileComparator(
@@ -71,15 +207,55 @@
 
 AutofillProfileComparator::~AutofillProfileComparator() {}
 
+bool AutofillProfileComparator::Compare(base::StringPiece16 text1,
+                                        base::StringPiece16 text2,
+                                        WhitespaceSpec whitespace_spec) const {
+  if (text1.empty() && text2.empty()) {
+    return true;
+  }
+
+  NormalizingIterator normalizing_iter1{text1, whitespace_spec};
+  NormalizingIterator normalizing_iter2{text2, whitespace_spec};
+
+  while (!normalizing_iter1.End() && !normalizing_iter2.End()) {
+    icu::UnicodeString char1 =
+        icu::UnicodeString(normalizing_iter1.GetNextChar());
+    icu::UnicodeString char2 =
+        icu::UnicodeString(normalizing_iter2.GetNextChar());
+
+    if (transliterator_ == nullptr) {
+      if (char1.toLower() != char2.toLower()) {
+        return false;
+      }
+    } else {
+      transliterator_->transliterate(char1);
+      transliterator_->transliterate(char2);
+      if (char1 != char2) {
+        return false;
+      }
+    }
+    normalizing_iter1.Advance();
+    normalizing_iter2.Advance();
+  }
+
+  if (normalizing_iter1.EndsInSkippableCharacters() &&
+      normalizing_iter2.EndsInSkippableCharacters()) {
+    return true;
+  }
+
+  return false;
+}
+
 base::string16 AutofillProfileComparator::NormalizeForComparison(
     base::StringPiece16 text,
     AutofillProfileComparator::WhitespaceSpec whitespace_spec) const {
   // This algorithm is not designed to be perfect, we could get arbitrarily
   // fancy here trying to canonicalize address lines. Instead, this is designed
   // to handle common cases for all types of data (addresses and names) without
-  // the need of domain-specific logic.
+  // needing domain-specific logic.
   //
-  // 1. Convert punctuation to spaces and normalize all whitespace to spaces.
+  // 1. Convert punctuation to spaces and normalize all whitespace to spaces if
+  //    |whitespace_spec| is RETAIN_WHITESPACE.
   //    This will convert "Mid-Island Plz." -> "Mid Island Plz " (the trailing
   //    space will be trimmed off outside of the end of the loop).
   //
@@ -94,28 +270,14 @@
   bool previous_was_whitespace = (whitespace_spec == RETAIN_WHITESPACE);
   for (base::i18n::UTF16CharIterator iter(text.data(), text.length());
        !iter.end(); iter.Advance()) {
-    switch (u_charType(iter.get())) {
-      // Punctuation
-      case U_DASH_PUNCTUATION:
-      case U_START_PUNCTUATION:
-      case U_END_PUNCTUATION:
-      case U_CONNECTOR_PUNCTUATION:
-      case U_OTHER_PUNCTUATION:
-      // Whitespace
-      case U_CONTROL_CHAR:  // To escape the '\n' character.
-      case U_SPACE_SEPARATOR:
-      case U_LINE_SEPARATOR:
-      case U_PARAGRAPH_SEPARATOR:
-        if (!previous_was_whitespace && whitespace_spec == RETAIN_WHITESPACE) {
-          result.push_back(' ');
-          previous_was_whitespace = true;
-        }
-        break;
-
-      default:
-        previous_was_whitespace = false;
-        base::WriteUnicodeCharacter(iter.get(), &result);
-        break;
+    if (IsPunctuationOrWhitespace(u_charType(iter.get()))) {
+      if (!previous_was_whitespace && whitespace_spec == RETAIN_WHITESPACE) {
+        result.push_back(' ');
+        previous_was_whitespace = true;
+      }
+    } else {
+      previous_was_whitespace = false;
+      base::WriteUnicodeCharacter(iter.get(), &result);
     }
   }
 
@@ -131,17 +293,6 @@
   return base::i18n::UnicodeStringToString16(value);
 }
 
-bool AutofillProfileComparator::MatchesAfterNormalization(
-    base::StringPiece16 text1,
-    base::StringPiece16 text2) const {
-  return NormalizeForComparison(
-             text1,
-             AutofillProfileComparator::WhitespaceSpec::DISCARD_WHITESPACE) ==
-         NormalizeForComparison(
-             text2,
-             AutofillProfileComparator::WhitespaceSpec::DISCARD_WHITESPACE);
-}
-
 bool AutofillProfileComparator::AreMergeable(const AutofillProfile& p1,
                                              const AutofillProfile& p2) const {
   // Sorted in order to relative expense of the tests to fail early and cheaply
@@ -684,8 +835,8 @@
   std::set<base::StringPiece16> t1 = UniqueTokens(s1);
   std::set<base::StringPiece16> t2 = UniqueTokens(s2);
 
-  // Does s1 contains all of the tokens in s2? As a special case, return 0 if
-  // the two sets are exactly the same.
+  // Does s1 contain all of the tokens in s2? As a special case, return 0 if the
+  // two sets are exactly the same.
   if (std::includes(t1.begin(), t1.end(), t2.begin(), t2.end()))
     return t1.size() == t2.size() ? SAME_TOKENS : S1_CONTAINS_S2;
 
diff --git a/components/autofill/core/browser/data_model/autofill_profile_comparator.h b/components/autofill/core/browser/data_model/autofill_profile_comparator.h
index ad5e4c2..178a3dd 100644
--- a/components/autofill/core/browser/data_model/autofill_profile_comparator.h
+++ b/components/autofill/core/browser/data_model/autofill_profile_comparator.h
@@ -26,6 +26,24 @@
 
   enum WhitespaceSpec { RETAIN_WHITESPACE, DISCARD_WHITESPACE };
 
+  // Returns true if |text1| matches |text2|. The following normalization
+  // techniques are applied to the given texts before comparing.
+  //
+  // (1) Diacritics are removed, e.g. حَ to ح, ビ to ヒ, and é to e;
+  // (2) Leading and trailing whitespace and punctuation are ignored; and
+  // (3) Characters are converted to lowercase.
+  //
+  // If |whitespace_spec| is DISCARD_WHITESPACE, then punctuation and whitespace
+  // are discarded. For example, the postal codes "B15 3TR" and "B153TR" and
+  // street addresses "16 Bridge St."" and "16 Bridge St" are considered equal.
+  //
+  // If |whitespace_spec| is RETAIN_WHITESPACE, then the postal codes "B15 3TR"
+  // and "B153TR" are not considered equal, but "16 Bridge St."" and "16 Bridge
+  // St" are because trailing whitespace and punctuation are ignored.
+  bool Compare(base::StringPiece16 text1,
+               base::StringPiece16 text2,
+               WhitespaceSpec whitespace_spec = DISCARD_WHITESPACE) const;
+
   // Returns a copy of |text| with uppercase converted to lowercase and
   // diacritics removed.
   //
@@ -39,12 +57,6 @@
       base::StringPiece16 text,
       WhitespaceSpec whitespace_spec = RETAIN_WHITESPACE) const;
 
-  // Returns true if |text1| matches |text2| when the texts are normalized and
-  // white space and punctuation are removed. For example, if |text1| is H3B 2Y5
-  // and |text2| is H3B2Y5, then returns true.
-  bool MatchesAfterNormalization(base::StringPiece16 text1,
-                                 base::StringPiece16 text2) const;
-
   // Returns true if |p1| and |p2| are viable merge candidates. This means that
   // their names, addresses, email addreses, company names, and phone numbers
   // are all pairwise equivalent or mergeable.
diff --git a/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc b/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc
index 7994f84e..af44b882 100644
--- a/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc
+++ b/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc
@@ -298,6 +298,90 @@
             comparator_.CompareTokens(kHelloThereBob, kHelloThereAlice));
 }
 
+TEST_F(AutofillProfileComparatorTest, Compare) {
+  // Checks the empty case.
+  EXPECT_TRUE(
+      comparator_.Compare(base::string16(), base::string16(),
+                          AutofillProfileComparator::RETAIN_WHITESPACE));
+  EXPECT_TRUE(
+      comparator_.Compare(base::string16(), base::string16(),
+                          AutofillProfileComparator::DISCARD_WHITESPACE));
+
+  // Checks that leading punctuation and white space are ignored.
+  EXPECT_TRUE(
+      comparator_.Compare(UTF8ToUTF16(".,  -()."), UTF8ToUTF16(""),
+                          AutofillProfileComparator::RETAIN_WHITESPACE));
+  EXPECT_TRUE(
+      comparator_.Compare(UTF8ToUTF16(".,  -()."), UTF8ToUTF16(""),
+                          AutofillProfileComparator::DISCARD_WHITESPACE));
+
+  // Checks that trailing punctuation and white space are ignored.
+  EXPECT_TRUE(
+      comparator_.Compare(UTF8ToUTF16("a ., "), UTF8ToUTF16("a"),
+                          AutofillProfileComparator::RETAIN_WHITESPACE));
+  EXPECT_TRUE(
+      comparator_.Compare(UTF8ToUTF16("a ., "), UTF8ToUTF16("a"),
+                          AutofillProfileComparator::DISCARD_WHITESPACE));
+
+  // Checks that embedded punctuation and white space is collapsed to a single
+  // white space with RETAIN_WHITESPACE and is ignored with DISCARD_WHITESPACE.
+  EXPECT_TRUE(
+      comparator_.Compare(UTF8ToUTF16("a() -  a"), UTF8ToUTF16("a a"),
+                          AutofillProfileComparator::RETAIN_WHITESPACE));
+  EXPECT_TRUE(
+      comparator_.Compare(UTF8ToUTF16("a() -  a"), UTF8ToUTF16("aa"),
+                          AutofillProfileComparator::DISCARD_WHITESPACE));
+
+  // Checks that characters such as 'œ' respect the status quo established by
+  // NormalizeForComparison.
+  EXPECT_FALSE(comparator_.Compare(UTF8ToUTF16("œil"), UTF8ToUTF16("oeil")));
+
+  // Checks that a substring of the string is not considered equal.
+  EXPECT_FALSE(comparator_.Compare(UTF8ToUTF16("A"), UTF8ToUTF16("Anna")));
+
+  EXPECT_FALSE(comparator_.Compare(UTF8ToUTF16("Anna"), UTF8ToUTF16("A")));
+
+  // Checks that Compare behaves like NormalizeForComparison. Also, checks that
+  // diacritics are removed.
+  EXPECT_TRUE(
+      comparator_.Compare(UTF8ToUTF16("Timothé"), UTF8ToUTF16("timothe"),
+                          AutofillProfileComparator::RETAIN_WHITESPACE));
+  EXPECT_TRUE(
+      comparator_.Compare(UTF8ToUTF16(" sven-åke "), UTF8ToUTF16("sven ake"),
+                          AutofillProfileComparator::RETAIN_WHITESPACE));
+  EXPECT_TRUE(
+      comparator_.Compare(UTF8ToUTF16("Ç 㸐"), UTF8ToUTF16("c 㸐"),
+                          AutofillProfileComparator::RETAIN_WHITESPACE));
+  EXPECT_TRUE(
+      comparator_.Compare(UTF8ToUTF16("902103214"), UTF8ToUTF16("90210-3214"),
+                          AutofillProfileComparator::DISCARD_WHITESPACE));
+  EXPECT_TRUE(
+      comparator_.Compare(UTF8ToUTF16("Timothé-Noël Étienne Périer"),
+                          UTF8ToUTF16("timothe noel etienne perier"),
+                          AutofillProfileComparator::RETAIN_WHITESPACE));
+  EXPECT_TRUE(
+      comparator_.Compare(UTF8ToUTF16("1600 Amphitheatre, Pkwy."),
+                          UTF8ToUTF16("1600 amphitheatre pkwy"),
+                          AutofillProfileComparator::RETAIN_WHITESPACE));
+  EXPECT_TRUE(
+      comparator_.Compare(base::WideToUTF16(L"Mid\x2013Island\x2003 Plaza"),
+                          UTF8ToUTF16("mid island plaza"),
+                          AutofillProfileComparator::RETAIN_WHITESPACE));
+  EXPECT_TRUE(
+      comparator_.Compare(UTF8ToUTF16("1600 amphitheatre pkwy \n App. 2"),
+                          UTF8ToUTF16("1600 amphitheatre pkwy app 2"),
+                          AutofillProfileComparator::RETAIN_WHITESPACE));
+  EXPECT_TRUE(
+      comparator_.Compare(UTF8ToUTF16("まéÖä정"), UTF8ToUTF16("まeoa정"),
+                          AutofillProfileComparator::RETAIN_WHITESPACE));
+  EXPECT_TRUE(
+      comparator_.Compare(UTF8ToUTF16("유재석"), UTF8ToUTF16("유 재석"),
+                          AutofillProfileComparator::DISCARD_WHITESPACE));
+  EXPECT_TRUE(comparator_.Compare(
+      UTF8ToUTF16("ビルゲイツ"), UTF8ToUTF16("ヒル・ケイツ"),
+      AutofillProfileComparator::DISCARD_WHITESPACE));
+}
+
 TEST_F(AutofillProfileComparatorTest, NormalizeForComparison) {
   EXPECT_EQ(UTF8ToUTF16("timothe"),
             comparator_.NormalizeForComparison(UTF8ToUTF16("Timothé")));
@@ -348,15 +432,6 @@
                 AutofillProfileComparator::DISCARD_WHITESPACE));
 }
 
-TEST_F(AutofillProfileComparatorTest, MatchesAfterNormalization) {
-  EXPECT_TRUE(comparator_.MatchesAfterNormalization(
-      base::ASCIIToUTF16("H3B 2Y5"), base::ASCIIToUTF16("H3B2Y5")));
-  EXPECT_TRUE(comparator_.MatchesAfterNormalization(
-      base::UTF8ToUTF16("Jean-François"), base::UTF8ToUTF16("Jean François")));
-  EXPECT_TRUE(comparator_.MatchesAfterNormalization(
-      base::ASCIIToUTF16("(234) 567-8900"), base::ASCIIToUTF16("2345678900")));
-}
-
 TEST_F(AutofillProfileComparatorTest, GetNamePartVariants) {
   std::set<base::string16> expected_variants = {
       UTF8ToUTF16("timothe noel"),
diff --git a/components/autofill/core/browser/ui/suggestion_selection.cc b/components/autofill/core/browser/ui/suggestion_selection.cc
index 6d8ac3e..e89743f 100644
--- a/components/autofill/core/browser/ui/suggestion_selection.cc
+++ b/components/autofill/core/browser/ui/suggestion_selection.cc
@@ -163,8 +163,7 @@
       AutofillProfile* profile_b = matched_profiles[j];
       // Check if profile A is a subset of profile B. If not, continue.
       if (i == j ||
-          !comparator.MatchesAfterNormalization(suggestions[i].value,
-                                                suggestions[j].value) ||
+          !comparator.Compare(suggestions[i].value, suggestions[j].value) ||
           !profile_a->IsSubsetOfForFieldSet(comparator, *profile_b, app_locale,
                                             types)) {
         continue;
diff --git a/components/autofill/core/common/autofill_regex_constants.cc b/components/autofill/core/common/autofill_regex_constants.cc
index a98aaf8..7443330 100644
--- a/components/autofill/core/common/autofill_regex_constants.cc
+++ b/components/autofill/core/common/autofill_regex_constants.cc
@@ -79,11 +79,12 @@
 const char kAddressLookupRe[] = "lookup";
 const char kCountryRe[] =
     "country|countries"
-    "|país|pais"       // es
-    "|(?<!(入|出))国"  // ja-JP
-    "|国家"            // zh-CN
-    "|국가|나라"       // ko-KR
-    "|کشور";           // fa
+    "|país|pais"           // es
+    "|land(?!.*(mark.*))"  // de-DE landmark is another field type in India.
+    "|(?<!(入|出))国"      // ja-JP
+    "|国家"                // zh-CN
+    "|국가|나라"           // ko-KR
+    "|کشور";               // fa
 const char kCountryLocationRe[] = "location";
 const char kZipCodeRe[] =
     "zip|postal|post.*code|pcode"
@@ -117,7 +118,6 @@
     "|^시[^도·・]|시[·・]?군[·・]?구";       // ko-KR
 const char kStateRe[] =
     "(?<!(united|hist|history).?)state|county|region|province"
-    "|land(?!.*(mark.*))"   // de-DE landmark is another field type in India.
     "|county|principality"  // en-UK
     "|都道府県"             // ja-JP
     "|estado|provincia"     // pt-BR, pt-PT
diff --git a/components/download/internal/common/download_job_factory.cc b/components/download/internal/common/download_job_factory.cc
index 7d62075..87da385 100644
--- a/components/download/internal/common/download_job_factory.cc
+++ b/components/download/internal/common/download_job_factory.cc
@@ -10,6 +10,7 @@
 #include "components/download/internal/common/parallel_download_job.h"
 #include "components/download/internal/common/parallel_download_utils.h"
 #include "components/download/internal/common/save_package_download_job.h"
+#include "components/download/public/common/download_features.h"
 #include "components/download/public/common/download_item.h"
 #include "components/download/public/common/download_stats.h"
 #include "components/download/public/common/download_url_loader_factory_getter.h"
@@ -47,11 +48,15 @@
       create_info.method == "GET" && create_info.url().SchemeIsHTTPOrHTTPS();
   bool partial_response_success =
       download_item->GetReceivedSlices().empty() || create_info.offset != 0;
-  bool is_parallelizable =
-      has_strong_validator &&
-      create_info.accept_range == RangeRequestSupportType::kSupport &&
-      has_content_length && satisfy_min_file_size && satisfy_connection_type &&
-      http_get_method && partial_response_success;
+  bool range_support_allowed =
+      create_info.accept_range == RangeRequestSupportType::kSupport ||
+      (base::FeatureList::IsEnabled(
+           features::kUseParallelRequestsForUnknwonRangeSupport) &&
+       create_info.accept_range == RangeRequestSupportType::kUnknown);
+  bool is_parallelizable = has_strong_validator && range_support_allowed &&
+                           has_content_length && satisfy_min_file_size &&
+                           satisfy_connection_type && http_get_method &&
+                           partial_response_success;
 
   if (!IsParallelDownloadEnabled())
     return is_parallelizable;
@@ -65,9 +70,13 @@
     RecordParallelDownloadCreationEvent(
         ParallelDownloadCreationEvent::FALLBACK_REASON_STRONG_VALIDATORS);
   }
-  if (create_info.accept_range != RangeRequestSupportType::kSupport) {
+  if (!range_support_allowed) {
     RecordParallelDownloadCreationEvent(
         ParallelDownloadCreationEvent::FALLBACK_REASON_ACCEPT_RANGE_HEADER);
+    if (create_info.accept_range == RangeRequestSupportType::kUnknown) {
+      RecordParallelDownloadCreationEvent(
+          ParallelDownloadCreationEvent::FALLBACK_REASON_UNKNOWN_RANGE_SUPPORT);
+    }
   }
   if (!has_content_length) {
     RecordParallelDownloadCreationEvent(
diff --git a/components/download/internal/common/download_stats.cc b/components/download/internal/common/download_stats.cc
index 1a91d9e..1fe8b19 100644
--- a/components/download/internal/common/download_stats.cc
+++ b/components/download/internal/common/download_stats.cc
@@ -1054,8 +1054,16 @@
                               request_count, 1, 10, 11);
 }
 
-void RecordParallelDownloadAddStreamSuccess(bool success) {
-  UMA_HISTOGRAM_BOOLEAN("Download.ParallelDownloadAddStreamSuccess", success);
+void RecordParallelDownloadAddStreamSuccess(bool success,
+                                            bool support_range_request) {
+  if (support_range_request) {
+    base::UmaHistogramBoolean("Download.ParallelDownloadAddStreamSuccess",
+                              success);
+  } else {
+    base::UmaHistogramBoolean(
+        "Download.ParallelDownloadAddStreamSuccess.NoAcceptRangesHeader",
+        success);
+  }
 }
 
 void RecordParallelizableContentLength(int64_t content_length) {
diff --git a/components/download/internal/common/parallel_download_job.cc b/components/download/internal/common/parallel_download_job.cc
index 1ac7dbbb..763116b 100644
--- a/components/download/internal/common/parallel_download_job.cc
+++ b/components/download/internal/common/parallel_download_job.cc
@@ -35,6 +35,7 @@
       content_length_(create_info.total_bytes),
       requests_sent_(false),
       is_canceled_(false),
+      range_support_(create_info.accept_range),
       url_loader_factory_getter_(std::move(url_loader_factory_getter)),
       url_request_context_getter_(url_request_context_getter),
       connector_(connector) {}
@@ -134,7 +135,9 @@
     success = DownloadJob::AddInputStream(std::move(input_stream),
                                           worker->offset(), worker->length());
   }
-  RecordParallelDownloadAddStreamSuccess(success);
+
+  RecordParallelDownloadAddStreamSuccess(
+      success, range_support_ == RangeRequestSupportType::kSupport);
 
   // Destroy the request if the sink is gone.
   if (!success) {
diff --git a/components/download/internal/common/parallel_download_job.h b/components/download/internal/common/parallel_download_job.h
index 71c45c86..ca41083 100644
--- a/components/download/internal/common/parallel_download_job.h
+++ b/components/download/internal/common/parallel_download_job.h
@@ -14,6 +14,7 @@
 #include "base/timer/timer.h"
 #include "components/download/internal/common/download_job_impl.h"
 #include "components/download/internal/common/download_worker.h"
+#include "components/download/public/common/download_create_info.h"
 #include "components/download/public/common/download_export.h"
 #include "components/download/public/common/parallel_download_configs.h"
 
@@ -119,6 +120,9 @@
   // If the download progress is canceled.
   bool is_canceled_;
 
+  // Whether the server accepts range requests.
+  RangeRequestSupportType range_support_;
+
   // URLLoaderFactory getter to issue network requests with network service
   scoped_refptr<download::DownloadURLLoaderFactoryGetter>
       url_loader_factory_getter_;
diff --git a/components/download/public/common/download_features.cc b/components/download/public/common/download_features.cc
index 7e945dd..4692f756 100644
--- a/components/download/public/common/download_features.cc
+++ b/components/download/public/common/download_features.cc
@@ -48,6 +48,11 @@
 const base::Feature kAllowDownloadResumptionWithoutStrongValidators{
     "AllowDownloadResumptionWithoutStrongValidators",
     base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kUseParallelRequestsForUnknwonRangeSupport{
+    "UseParallelRequestForUnknwonRangeSupport",
+    base::FEATURE_DISABLED_BY_DEFAULT};
+
 }  // namespace features
 
 }  // namespace download
diff --git a/components/download/public/common/download_features.h b/components/download/public/common/download_features.h
index a3d7fda7..5dcfc4f 100644
--- a/components/download/public/common/download_features.h
+++ b/components/download/public/common/download_features.h
@@ -45,7 +45,12 @@
 // Whether download resumption is allowed when there are no strong validators.
 COMPONENTS_DOWNLOAD_EXPORT extern const base::Feature
     kAllowDownloadResumptionWithoutStrongValidators;
+
+// Whether download resumption is allowed when there are no strong validators.
+COMPONENTS_DOWNLOAD_EXPORT extern const base::Feature
+    kUseParallelRequestsForUnknwonRangeSupport;
 }  // namespace features
+
 }  // namespace download
 
 #endif  // COMPONENTS_DOWNLOAD_PUBLIC_COMMON_DOWNLOAD_FEATURES_H_
diff --git a/components/download/public/common/download_stats.h b/components/download/public/common/download_stats.h
index b1b3947412..447e8fd 100644
--- a/components/download/public/common/download_stats.h
+++ b/components/download/public/common/download_stats.h
@@ -216,6 +216,9 @@
   // The http method or url scheme does not meet the requirement.
   FALLBACK_REASON_HTTP_METHOD,
 
+  // Range support is unknown from the response.
+  FALLBACK_REASON_UNKNOWN_RANGE_SUPPORT,
+
   // Last entry of the enum.
   COUNT,
 };
@@ -323,8 +326,11 @@
     int request_count);
 
 // Records if each byte stream is successfully added to download sink.
+// |support_range_request| indicates whether the server strongly supports range
+// requests.
 COMPONENTS_DOWNLOAD_EXPORT void RecordParallelDownloadAddStreamSuccess(
-    bool success);
+    bool success,
+    bool support_range_request);
 
 // Records the bandwidth for parallelizable download and estimates the saved
 // time at the file end. Does not count in any hash computation or file
diff --git a/components/exo/text_input.h b/components/exo/text_input.h
index 3612bed..c71e477 100644
--- a/components/exo/text_input.h
+++ b/components/exo/text_input.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_EXO_TEXT_INPUT_H_
 
 #include "ash/keyboard/ui/keyboard_controller_observer.h"
+#include "base/optional.h"
 #include "ui/base/ime/text_input_client.h"
 #include "ui/base/ime/text_input_flags.h"
 #include "ui/base/ime/text_input_mode.h"
diff --git a/components/exo/wayland/zwp_text_input_manager.cc b/components/exo/wayland/zwp_text_input_manager.cc
index 73ae15f..38bd505 100644
--- a/components/exo/wayland/zwp_text_input_manager.cc
+++ b/components/exo/wayland/zwp_text_input_manager.cc
@@ -11,6 +11,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "components/exo/text_input.h"
 #include "components/exo/wayland/server_util.h"
+#include "ui/events/event.h"
 #include "ui/events/keycodes/dom/keycode_converter.h"
 
 #if defined(OS_CHROMEOS)
diff --git a/components/mirroring/service/BUILD.gn b/components/mirroring/service/BUILD.gn
index 881987b..759ef85a 100644
--- a/components/mirroring/service/BUILD.gn
+++ b/components/mirroring/service/BUILD.gn
@@ -64,7 +64,7 @@
     "//services/network/public/cpp",
     "//services/network/public/mojom",
     "//services/service_manager/public/cpp:cpp",
-    "//services/ws/public/cpp/gpu",
+    "//services/viz/public/cpp/gpu",
     "//ui/base",
     "//ui/gfx",
   ]
@@ -109,7 +109,7 @@
     "//net",
     "//services/network:test_support",
     "//services/network/public/mojom",
-    "//services/ws/public/cpp/gpu",
+    "//services/viz/public/cpp/gpu",
     "//testing/gmock",
     "//testing/gtest",
   ]
diff --git a/components/mirroring/service/DEPS b/components/mirroring/service/DEPS
index ae980f9..a50f12e 100644
--- a/components/mirroring/service/DEPS
+++ b/components/mirroring/service/DEPS
@@ -10,6 +10,6 @@
   "+services/network/public",
   "+services/network/test",
   "+services/service_manager",
-  "+services/ws/public",
+  "+services/viz/public",
   "+ui/base",
 ]
diff --git a/components/mirroring/service/mirroring_service.cc b/components/mirroring/service/mirroring_service.cc
index 4bb1e26..168c3fe 100644
--- a/components/mirroring/service/mirroring_service.cc
+++ b/components/mirroring/service/mirroring_service.cc
@@ -7,8 +7,7 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "components/mirroring/service/session.h"
-#include "services/ws/public/cpp/gpu/gpu.h"
-#include "ui/base/ui_base_features.h"
+#include "services/viz/public/cpp/gpu/gpu.h"
 
 namespace mirroring {
 
@@ -56,12 +55,10 @@
                              mojom::CastMessageChannelPtr outbound_channel,
                              mojom::CastMessageChannelRequest inbound_channel) {
   session_.reset();  // Stops the current session if active.
-  std::unique_ptr<ws::Gpu> gpu = nullptr;
+  std::unique_ptr<viz::Gpu> gpu;
   if (params->type != mojom::SessionType::AUDIO_ONLY) {
-    gpu = ws::Gpu::Create(
-        service_binding_.GetConnector(),
-        features::IsUsingWindowService() ? "ui" : "content_system",
-        io_task_runner_);
+    gpu = viz::Gpu::Create(service_binding_.GetConnector(), "content_system",
+                           io_task_runner_);
   }
   session_ = std::make_unique<Session>(
       std::move(params), max_resolution, std::move(observer),
diff --git a/components/mirroring/service/session.cc b/components/mirroring/service/session.cc
index daca22b5a0..3f92752e 100644
--- a/components/mirroring/service/session.cc
+++ b/components/mirroring/service/session.cc
@@ -42,7 +42,7 @@
 #include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/public/cpp/system/platform_handle.h"
 #include "net/base/ip_endpoint.h"
-#include "services/ws/public/cpp/gpu/gpu.h"
+#include "services/viz/public/cpp/gpu/gpu.h"
 
 using media::cast::FrameSenderConfig;
 using media::cast::RtpPayloadType;
@@ -378,7 +378,7 @@
                  mojom::ResourceProviderPtr resource_provider,
                  mojom::CastMessageChannelPtr outbound_channel,
                  mojom::CastMessageChannelRequest inbound_channel,
-                 std::unique_ptr<ws::Gpu> gpu)
+                 std::unique_ptr<viz::Gpu> gpu)
     : session_params_(*session_params),
       state_(MIRRORING),
       observer_(std::move(observer)),
diff --git a/components/mirroring/service/session.h b/components/mirroring/service/session.h
index c70c62c..c6d8860 100644
--- a/components/mirroring/service/session.h
+++ b/components/mirroring/service/session.h
@@ -36,9 +36,9 @@
 class GpuChannelHost;
 }  // namespace gpu
 
-namespace ws {
+namespace viz {
 class Gpu;
-}  // namespace ws
+}  // namespace viz
 
 namespace mirroring {
 
@@ -63,7 +63,7 @@
           mojom::ResourceProviderPtr resource_provider,
           mojom::CastMessageChannelPtr outbound_channel,
           mojom::CastMessageChannelRequest inbound_channel,
-          std::unique_ptr<ws::Gpu> gpu);
+          std::unique_ptr<viz::Gpu> gpu);
 
   ~Session() override;
 
@@ -173,7 +173,7 @@
   std::unique_ptr<AudioCapturingCallback> audio_capturing_callback_;
   scoped_refptr<media::AudioInputDevice> audio_input_device_;
   std::unique_ptr<MediaRemoter> media_remoter_;
-  std::unique_ptr<ws::Gpu> gpu_;
+  std::unique_ptr<viz::Gpu> gpu_;
   scoped_refptr<gpu::GpuChannelHost> gpu_channel_host_;
   gpu::VideoEncodeAcceleratorSupportedProfiles supported_profiles_;
   media::mojom::VideoEncodeAcceleratorProviderPtr vea_provider_;
diff --git a/components/mirroring/service/session_unittest.cc b/components/mirroring/service/session_unittest.cc
index 5cf0f03..b170fea8 100644
--- a/components/mirroring/service/session_unittest.cc
+++ b/components/mirroring/service/session_unittest.cc
@@ -21,7 +21,7 @@
 #include "media/cast/test/utility/net_utility.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "net/base/ip_address.h"
-#include "services/ws/public/cpp/gpu/gpu.h"
+#include "services/viz/public/cpp/gpu/gpu.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/components/password_manager/core/browser/sync/password_sync_bridge.cc b/components/password_manager/core/browser/sync/password_sync_bridge.cc
index 3d0745daa..6566e2a 100644
--- a/components/password_manager/core/browser/sync/password_sync_bridge.cc
+++ b/components/password_manager/core/browser/sync/password_sync_bridge.cc
@@ -454,7 +454,7 @@
         // move on.
         if (add_login_error == AddLoginError::kConstraintViolation) {
           change_processor()->UntrackEntityForClientTagHash(
-              client_tag_of_remote_password);
+              entity_change->data().client_tag_hash);
           continue;
         }
         // For all other types of error, we should stop syncing.
@@ -548,7 +548,7 @@
             // and move on.
             if (add_login_error == AddLoginError::kConstraintViolation) {
               change_processor()->UntrackEntityForClientTagHash(
-                  GetClientTag(entity_change->data()));
+                  entity_change->data().client_tag_hash);
               continue;
             }
             // For all other types of error, we should stop syncing.
@@ -590,6 +590,13 @@
           base::UmaHistogramEnumeration(
               "PasswordManager.ApplySyncChanges.UpdateLoginSyncError",
               update_login_error);
+          // If there are no entries to update, direct the processor to ignore
+          // and move on.
+          if (update_login_error == UpdateLoginError::kNoUpdatedRecords) {
+            change_processor()->UntrackEntityForClientTagHash(
+                entity_change->data().client_tag_hash);
+            continue;
+          }
           if (changes.empty()) {
             metrics_util::LogApplySyncChangesState(
                 metrics_util::ApplySyncChangesState::kApplyUpdateFailed);
diff --git a/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc b/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc
index 726ddae..fb2b72b 100644
--- a/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc
+++ b/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/bind_test_util.h"
 #include "components/password_manager/core/browser/password_store_sync.h"
+#include "components/sync/base/hash_util.h"
 #include "components/sync/model/data_batch.h"
 #include "components/sync/model/entity_change.h"
 #include "components/sync/model/metadata_batch.h"
@@ -93,14 +94,6 @@
   return form;
 }
 
-// Creates an EntityData around a copy of the given specifics.
-std::unique_ptr<syncer::EntityData> SpecificsToEntity(
-    const sync_pb::PasswordSpecifics& specifics) {
-  auto data = std::make_unique<syncer::EntityData>();
-  *data->specifics.mutable_password() = specifics;
-  return data;
-}
-
 // A mini database class the supports Add/Update/Remove functionality. It also
 // supports an auto increment primary key that starts from 1. It will be used to
 // empower the MockPasswordStoreSync be forwarding all database calls to an
@@ -262,6 +255,16 @@
         .WillByDefault(testing::Return(true));
   }
 
+  // Creates an EntityData around a copy of the given specifics.
+  std::unique_ptr<syncer::EntityData> SpecificsToEntity(
+      const sync_pb::PasswordSpecifics& specifics) {
+    auto data = std::make_unique<syncer::EntityData>();
+    *data->specifics.mutable_password() = specifics;
+    data->client_tag_hash = syncer::GenerateSyncableHash(
+        syncer::PASSWORDS, bridge()->GetClientTag(*data));
+    return data;
+  }
+
   ~PasswordSyncBridgeTest() override {}
 
   base::Optional<sync_pb::PasswordSpecifics> GetDataFromBridge(
@@ -422,10 +425,10 @@
 
   sync_pb::PasswordSpecifics specifics =
       CreateSpecificsWithSignonRealm(kSignonRealm1);
-  const std::string client_tag =
-      bridge()->GetClientTag(*SpecificsToEntity(specifics));
 
-  EXPECT_CALL(mock_processor(), UntrackEntityForClientTagHash(client_tag));
+  EXPECT_CALL(mock_processor(),
+              UntrackEntityForClientTagHash(
+                  SpecificsToEntity(specifics)->client_tag_hash));
 
   syncer::EntityChangeList entity_change_list;
   entity_change_list.push_back(syncer::EntityChange::CreateAdd(
@@ -718,10 +721,10 @@
 
   sync_pb::PasswordSpecifics specifics =
       CreateSpecificsWithSignonRealm(kSignonRealm1);
-  const std::string client_tag =
-      bridge()->GetClientTag(*SpecificsToEntity(specifics));
 
-  EXPECT_CALL(mock_processor(), UntrackEntityForClientTagHash(client_tag));
+  EXPECT_CALL(mock_processor(),
+              UntrackEntityForClientTagHash(
+                  SpecificsToEntity(specifics)->client_tag_hash));
 
   syncer::EntityChangeList entity_change_list;
   entity_change_list.push_back(syncer::EntityChange::CreateAdd(
diff --git a/components/policy/core/common/cloud/cloud_policy_client_registration_helper.cc b/components/policy/core/common/cloud/cloud_policy_client_registration_helper.cc
index fc80557..785a86e 100644
--- a/components/policy/core/common/cloud/cloud_policy_client_registration_helper.cc
+++ b/components/policy/core/common/cloud/cloud_policy_client_registration_helper.cc
@@ -20,11 +20,6 @@
 #include "services/identity/public/cpp/scope_set.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
-#if !defined(OS_ANDROID)
-#include "google_apis/gaia/oauth2_access_token_consumer.h"
-#include "google_apis/gaia/oauth2_access_token_fetcher_impl.h"
-#endif
-
 namespace policy {
 
 // The key under which the hosted-domain value is stored in the UserInfo
@@ -34,9 +29,6 @@
 typedef base::Callback<void(const std::string&)> StringCallback;
 
 // This class fetches an OAuth2 token scoped for the userinfo and DM services.
-// On Android, we use a special API to allow us to fetch a token for an account
-// that is not yet logged in to allow fetching the token before the sign-in
-// process is finished.
 class CloudPolicyClientRegistrationHelper::IdentityManagerHelper {
  public:
   IdentityManagerHelper() = default;
@@ -88,64 +80,6 @@
     callback_.Run("");
 }
 
-#if !defined(OS_ANDROID)
-// This class fetches the OAuth2 token scoped for the userinfo and DM services.
-// It uses an OAuth2AccessTokenFetcher to fetch it, given a login refresh token
-// that can be used to authorize that request. This class is not needed on
-// Android because we can use OAuth2TokenService to fetch tokens for accounts
-// even before they are signed in.
-class CloudPolicyClientRegistrationHelper::LoginTokenHelper
-    : public OAuth2AccessTokenConsumer {
- public:
-  LoginTokenHelper();
-
-  void FetchAccessToken(
-      const std::string& login_refresh_token,
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      const StringCallback& callback);
-
- private:
-  // OAuth2AccessTokenConsumer implementation:
-  void OnGetTokenSuccess(
-      const OAuth2AccessTokenConsumer::TokenResponse& token_response) override;
-  void OnGetTokenFailure(const GoogleServiceAuthError& error) override;
-
-  StringCallback callback_;
-  std::unique_ptr<OAuth2AccessTokenFetcher> oauth2_access_token_fetcher_;
-};
-
-CloudPolicyClientRegistrationHelper::LoginTokenHelper::LoginTokenHelper() {}
-
-void CloudPolicyClientRegistrationHelper::LoginTokenHelper::FetchAccessToken(
-    const std::string& login_refresh_token,
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    const StringCallback& callback) {
-  DCHECK(!oauth2_access_token_fetcher_);
-  callback_ = callback;
-
-  // Start fetching an OAuth2 access token for the device management and
-  // userinfo services.
-  oauth2_access_token_fetcher_.reset(new OAuth2AccessTokenFetcherImpl(
-      this, url_loader_factory, login_refresh_token));
-  GaiaUrls* gaia_urls = GaiaUrls::GetInstance();
-  oauth2_access_token_fetcher_->Start(
-      gaia_urls->oauth2_chrome_client_id(),
-      gaia_urls->oauth2_chrome_client_secret(),
-      GetScopes());
-}
-
-void CloudPolicyClientRegistrationHelper::LoginTokenHelper::OnGetTokenSuccess(
-    const OAuth2AccessTokenConsumer::TokenResponse& token_response) {
-  callback_.Run(token_response.access_token);
-}
-
-void CloudPolicyClientRegistrationHelper::LoginTokenHelper::OnGetTokenFailure(
-    const GoogleServiceAuthError& error) {
-  callback_.Run("");
-}
-
-#endif
-
 CloudPolicyClientRegistrationHelper::CloudPolicyClientRegistrationHelper(
     CloudPolicyClient* client,
     enterprise_management::DeviceRegisterRequest::Type registration_type)
@@ -187,38 +121,8 @@
   client_->RegisterWithToken(token, client_id);
 }
 
-#if !defined(OS_ANDROID)
-void CloudPolicyClientRegistrationHelper::StartRegistrationWithLoginToken(
-    const std::string& login_refresh_token,
-    const base::Closure& callback) {
-  DVLOG(1) << "Starting registration process with login token";
-  DCHECK(!client_->is_registered());
-  callback_ = callback;
-  client_->AddObserver(this);
-
-  login_token_helper_.reset(
-      new CloudPolicyClientRegistrationHelper::LoginTokenHelper());
-  login_token_helper_->FetchAccessToken(
-      login_refresh_token, client_->GetURLLoaderFactory(),
-      base::Bind(&CloudPolicyClientRegistrationHelper::OnTokenFetched,
-                 base::Unretained(this)));
-}
-
-// static
-std::vector<std::string>
-CloudPolicyClientRegistrationHelper::GetScopes() {
-  std::vector<std::string> scopes;
-  scopes.push_back(GaiaConstants::kDeviceManagementServiceOAuth);
-  scopes.push_back(GaiaConstants::kOAuthWrapBridgeUserInfoScope);
-  return scopes;
-}
-#endif
-
 void CloudPolicyClientRegistrationHelper::OnTokenFetched(
     const std::string& access_token) {
-#if !defined(OS_ANDROID)
-  login_token_helper_.reset();
-#endif
   identity_manager_helper_.reset();
 
   if (access_token.empty()) {
diff --git a/components/policy/core/common/cloud/cloud_policy_client_registration_helper.h b/components/policy/core/common/cloud/cloud_policy_client_registration_helper.h
index 2caa073..b66ad78 100644
--- a/components/policy/core/common/cloud/cloud_policy_client_registration_helper.h
+++ b/components/policy/core/common/cloud/cloud_policy_client_registration_helper.h
@@ -56,22 +56,8 @@
                                             const std::string& client_id,
                                             const base::Closure& callback);
 
-#if !defined(OS_ANDROID)
-  // Starts the client registration process. The |login_refresh_token| is used
-  // to mint a new token for the userinfo and DM services.
-  // |callback| is invoked when the registration is complete.
-  void StartRegistrationWithLoginToken(const std::string& login_refresh_token,
-                                       const base::Closure& callback);
-
-  // Returns the scopes required for policy client registration.
-  static std::vector<std::string> GetScopes();
-#endif
-
  private:
   class IdentityManagerHelper;
-#if !defined(OS_ANDROID)
-  class LoginTokenHelper;
-#endif
 
   void OnTokenFetched(const std::string& oauth_access_token);
 
@@ -88,18 +74,9 @@
   void RequestCompleted();
 
   // Internal helper class that uses IdentityManager to fetch an OAuth
-  // access token. On desktop, this is only used after the user has signed in -
-  // desktop platforms use LoginTokenHelper for policy fetches performed before
-  // signin is complete.
+  // access token.
   std::unique_ptr<IdentityManagerHelper> identity_manager_helper_;
 
-#if !defined(OS_ANDROID)
-  // Special desktop-only helper to fetch an OAuth access token prior to
-  // the completion of signin. Not used on Android since all token fetching
-  // is done via OAuth2TokenService.
-  std::unique_ptr<LoginTokenHelper> login_token_helper_;
-#endif
-
   // Helper class for fetching information from GAIA about the currently
   // signed-in user.
   std::unique_ptr<UserInfoFetcher> user_info_fetcher_;
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 7716495..64e522cf 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -15903,10 +15903,10 @@
             '$ref': 'WeekDay'
           },
           'day_of_month': {
-            'description': '''Day of month [1-31] when the update check should happen, interpreted in the device's local time zone. Only used when 'frequency' is 'MONTHLY'. If this is more than the maximum number of days in a given month then the last day of the month will be chosen.''',
+            'description': '''Day of month [1-28] when the update check should happen, interpreted in the device's local time zone. Only used when 'frequency' is 'MONTHLY'. If this is more than the maximum number of days in a given month then the last day of the month will be chosen. Currently it is restricted to 28 to resolve month rollover ambiguities.''',
             'type': 'integer',
             'minimum': 1,
-            'maximum': 31
+            'maximum': 28
           }
         },
         'required': ['update_check_time', 'frequency']
diff --git a/components/signin/core/browser/primary_account_manager.cc b/components/signin/core/browser/primary_account_manager.cc
index 2b4d399..4c790db3e 100644
--- a/components/signin/core/browser/primary_account_manager.cc
+++ b/components/signin/core/browser/primary_account_manager.cc
@@ -304,6 +304,9 @@
     signin_metrics::ProfileSignout signout_source_metric,
     signin_metrics::SignoutDelete signout_delete_metric,
     RemoveAccountsOption remove_option) {
+  VLOG(1) << "StartSignOut: " << static_cast<int>(signout_source_metric) << ", "
+          << static_cast<int>(signout_delete_metric) << ", "
+          << static_cast<int>(remove_option);
   signin_client()->PreSignOut(
       base::BindOnce(&PrimaryAccountManager::OnSignoutDecisionReached,
                      base::Unretained(this), signout_source_metric,
@@ -318,6 +321,8 @@
     SigninClient::SignoutDecision signout_decision) {
   DCHECK(IsInitialized());
 
+  VLOG(1) << "OnSignoutDecisionReached: "
+          << (signout_decision == SigninClient::SignoutDecision::ALLOW_SIGNOUT);
   signin_metrics::LogSignout(signout_source_metric, signout_delete_metric);
   if (!IsAuthenticated()) {
     return;
@@ -326,7 +331,7 @@
   // TODO(crbug.com/887756): Consider moving this higher up, or document why
   // the above blocks are exempt from the |signout_decision| early return.
   if (signout_decision == SigninClient::SignoutDecision::DISALLOW_SIGNOUT) {
-    DVLOG(1) << "Ignoring attempt to sign out while signout disallowed";
+    VLOG(1) << "Ignoring attempt to sign out while signout disallowed";
     return;
   }
 
@@ -395,8 +400,8 @@
     for (const auto& account : accounts_in_tracker_service) {
       if (GetAuthenticatedAccountId() != account.account_id &&
           !token_service_->RefreshTokenIsAvailable(account.account_id)) {
-        DVLOG(0) << "Removed account from account tracker service: "
-                 << account.account_id;
+        VLOG(0) << "Removed account from account tracker service: "
+                << account.account_id;
         account_tracker_service_->RemoveAccount(account.account_id);
       }
     }
diff --git a/components/signin/core/browser/primary_account_manager.h b/components/signin/core/browser/primary_account_manager.h
index 5056924..2999661 100644
--- a/components/signin/core/browser/primary_account_manager.h
+++ b/components/signin/core/browser/primary_account_manager.h
@@ -85,7 +85,7 @@
   // Used to remove accounts from the token service and the account tracker.
   enum class RemoveAccountsOption {
     // Do not remove accounts.
-    kKeepAllAccounts,
+    kKeepAllAccounts = 0,
     // Remove all the accounts.
     kRemoveAllAccounts,
     // Removes the authenticated account if it is in authentication error.
diff --git a/components/test/data/autofill/heuristics/output/000_i18n_de.out b/components/test/data/autofill/heuristics/output/000_i18n_de.out
index 35b7ecd..f74f6f9f 100644
--- a/components/test/data/autofill/heuristics/output/000_i18n_de.out
+++ b/components/test/data/autofill/heuristics/output/000_i18n_de.out
@@ -5,7 +5,7 @@
 ADDRESS_HOME_LINE2 | a2 | Adresszusatz: |  | fn_1-default
 ADDRESS_HOME_CITY | ct | Stadt: |  | fn_1-default
 ADDRESS_HOME_ZIP | zc | Postleitzahl: |  | fn_1-default
-ADDRESS_HOME_STATE | st | Land: |  | fn_1-default
+ADDRESS_HOME_COUNTRY | st | Land: |  | fn_1-default
 UNKNOWN_TYPE | aa | Landmark |  | fn_1-default
 UNKNOWN_TYPE | bb | Landmarks: |  | fn_1-default
 UNKNOWN_TYPE | cc | Land-mark: |  | fn_1-default
diff --git a/components/test/data/autofill/heuristics/output/001_i18n_de2.out b/components/test/data/autofill/heuristics/output/001_i18n_de2.out
index 312288f..a383d92 100644
--- a/components/test/data/autofill/heuristics/output/001_i18n_de2.out
+++ b/components/test/data/autofill/heuristics/output/001_i18n_de2.out
@@ -5,7 +5,7 @@
 ADDRESS_HOME_LINE2 | a2 | Adresszusatz: |  | ln_1-default
 ADDRESS_HOME_CITY | ct | Stadt: |  | ln_1-default
 ADDRESS_HOME_ZIP | zc | Postleitzahl: |  | ln_1-default
-ADDRESS_HOME_STATE | st | Land: |  | ln_1-default
+ADDRESS_HOME_COUNTRY | st | Land: |  | ln_1-default
 EMAIL_ADDRESS | em | E-Mail-Adresse: |  | ln_1-default
 PHONE_HOME_WHOLE_NUMBER | ph | Telefonnummer: |  | ln_1-default
 CREDIT_CARD_NAME_FULL | c1 | Karteninhaber: |  | credit-card-cc
diff --git a/components/translate/content/browser/content_translate_driver.cc b/components/translate/content/browser/content_translate_driver.cc
index 21ffaf3..e949bc6 100644
--- a/components/translate/content/browser/content_translate_driver.cc
+++ b/components/translate/content/browser/content_translate_driver.cc
@@ -136,7 +136,11 @@
   network::mojom::URLLoaderFactoryPtr factory;
   url::Origin origin = url::Origin::Create(GetTranslateSecurityOrigin());
   network::mojom::TrustedURLLoaderHeaderClientPtrInfo null_header_client;
-  process->CreateURLLoaderFactory(origin, std::move(null_header_client),
+
+  // TODO(crbug.com/940068): Since this factory will be removed, sending an
+  // empty network isolation key for now.
+  process->CreateURLLoaderFactory(origin, net::NetworkIsolationKey(),
+                                  std::move(null_header_client),
                                   mojo::MakeRequest(&factory));
   return factory;
 }
diff --git a/components/viz/common/BUILD.gn b/components/viz/common/BUILD.gn
index f7c4173..e3371df 100644
--- a/components/viz/common/BUILD.gn
+++ b/components/viz/common/BUILD.gn
@@ -132,6 +132,8 @@
     "frame_sinks/copy_output_util.h",
     "frame_sinks/delay_based_time_source.cc",
     "frame_sinks/delay_based_time_source.h",
+    "frame_timing_details.h",
+    "frame_timing_details_map.h",
     "gl_helper.cc",
     "gl_helper.h",
     "gl_helper_scaling.cc",
@@ -155,7 +157,6 @@
     "hit_test/hit_test_data_builder.h",
     "hit_test/hit_test_region_list.cc",
     "hit_test/hit_test_region_list.h",
-    "presentation_feedback_map.h",
     "quads/compositor_frame.cc",
     "quads/compositor_frame.h",
     "quads/compositor_frame_metadata.cc",
diff --git a/components/viz/common/frame_timing_details.h b/components/viz/common/frame_timing_details.h
new file mode 100644
index 0000000..b901f5f
--- /dev/null
+++ b/components/viz/common/frame_timing_details.h
@@ -0,0 +1,18 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_COMMON_FRAME_TIMING_DETAILS_H_
+#define COMPONENTS_VIZ_COMMON_FRAME_TIMING_DETAILS_H_
+
+#include "ui/gfx/presentation_feedback.h"
+
+namespace viz {
+
+struct FrameTimingDetails {
+  gfx::PresentationFeedback presentation_feedback;
+};
+
+}  // namespace viz
+
+#endif  // COMPONENTS_VIZ_COMMON_FRAME_TIMING_DETAILS_H_
diff --git a/components/viz/common/frame_timing_details_map.h b/components/viz/common/frame_timing_details_map.h
new file mode 100644
index 0000000..d72fab2
--- /dev/null
+++ b/components/viz/common/frame_timing_details_map.h
@@ -0,0 +1,19 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_COMMON_FRAME_TIMING_DETAILS_MAP_H_
+#define COMPONENTS_VIZ_COMMON_FRAME_TIMING_DETAILS_MAP_H_
+
+#include "base/containers/flat_map.h"
+#include "components/viz/common/frame_timing_details.h"
+
+namespace viz {
+
+// The uint32_t is the frame_token that the FrameTimingDetails is associated
+// with.
+using FrameTimingDetailsMap = base::flat_map<uint32_t, FrameTimingDetails>;
+
+}  // namespace viz
+
+#endif  // COMPONENTS_VIZ_COMMON_FRAME_TIMING_DETAILS_MAP_H_
diff --git a/components/viz/common/presentation_feedback_map.h b/components/viz/common/presentation_feedback_map.h
deleted file mode 100644
index 1b89241..0000000
--- a/components/viz/common/presentation_feedback_map.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_VIZ_COMMON_PRESENTATION_FEEDBACK_MAP_H_
-#define COMPONENTS_VIZ_COMMON_PRESENTATION_FEEDBACK_MAP_H_
-
-#include "base/containers/flat_map.h"
-#include "ui/gfx/presentation_feedback.h"
-
-namespace viz {
-
-using PresentationFeedbackMap =
-    base::flat_map<uint32_t, gfx::PresentationFeedback>;
-
-}
-
-#endif  // COMPONENTS_VIZ_COMMON_PRESENTATION_FEEDBACK_MAP_H_
diff --git a/components/viz/demo/client/demo_client.cc b/components/viz/demo/client/demo_client.cc
index a03e1f1c..fc1908bce 100644
--- a/components/viz/demo/client/demo_client.cc
+++ b/components/viz/demo/client/demo_client.cc
@@ -170,8 +170,9 @@
   // See documentation in mojom for how this can be used.
 }
 
-void DemoClient::OnBeginFrame(const viz::BeginFrameArgs& args,
-                              const viz::PresentationFeedbackMap& feedbacks) {
+void DemoClient::OnBeginFrame(
+    const viz::BeginFrameArgs& args,
+    const viz::FrameTimingDetailsMap& timing_details) {
   // Generate a new compositor-frame for each begin-frame. This demo client
   // generates and submits the compositor-frame immediately. But it is possible
   // for the client to delay sending the compositor-frame. |args| includes the
diff --git a/components/viz/demo/client/demo_client.h b/components/viz/demo/client/demo_client.h
index 5d1631a..fbe3a53 100644
--- a/components/viz/demo/client/demo_client.h
+++ b/components/viz/demo/client/demo_client.h
@@ -10,7 +10,7 @@
 
 #include "base/synchronization/lock.h"
 #include "base/threading/thread.h"
-#include "components/viz/common/presentation_feedback_map.h"
+#include "components/viz/common/frame_timing_details_map.h"
 #include "components/viz/common/quads/compositor_frame_metadata.h"
 #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
 #include "mojo/public/cpp/bindings/binding.h"
@@ -89,7 +89,7 @@
   void DidReceiveCompositorFrameAck(
       const std::vector<viz::ReturnedResource>& resources) override;
   void OnBeginFrame(const viz::BeginFrameArgs& args,
-                    const viz::PresentationFeedbackMap& feedbacks) override;
+                    const viz::FrameTimingDetailsMap& timing_details) override;
   void OnBeginFramePausedChanged(bool paused) override;
   void ReclaimResources(
       const std::vector<viz::ReturnedResource>& resources) override;
diff --git a/components/viz/host/BUILD.gn b/components/viz/host/BUILD.gn
index a2d540d4..57e211c 100644
--- a/components/viz/host/BUILD.gn
+++ b/components/viz/host/BUILD.gn
@@ -58,7 +58,6 @@
     "//gpu/ipc/host",
     "//services/viz/privileged/interfaces/compositing",
     "//services/viz/public/interfaces",
-    "//services/ws/public/mojom",
     "//ui/gfx/geometry",
   ]
 
diff --git a/components/viz/host/DEPS b/components/viz/host/DEPS
index 6c42755..de4fd0f 100644
--- a/components/viz/host/DEPS
+++ b/components/viz/host/DEPS
@@ -17,7 +17,6 @@
   "+services/viz/privileged/interfaces",
   "+services/viz/public/interfaces",
   "+services/viz/public/interfaces/hit_test",
-  "+services/ws/public/mojom",
   "+skia",
   "+third_party/skia",
   "+ui/accelerated_widget_mac",
diff --git a/components/viz/host/gpu_client.cc b/components/viz/host/gpu_client.cc
index 1894350..58020bc 100644
--- a/components/viz/host/gpu_client.cc
+++ b/components/viz/host/gpu_client.cc
@@ -44,7 +44,7 @@
   OnError(ErrorReason::kInDestructor);
 }
 
-void GpuClient::Add(ws::mojom::GpuRequest request) {
+void GpuClient::Add(mojom::GpuRequest request) {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
   gpu_bindings_.AddBinding(this, std::move(request));
 }
@@ -188,7 +188,7 @@
     const gfx::Size& size,
     gfx::BufferFormat format,
     gfx::BufferUsage usage,
-    ws::mojom::GpuMemoryBufferFactory::CreateGpuMemoryBufferCallback callback) {
+    mojom::GpuMemoryBufferFactory::CreateGpuMemoryBufferCallback callback) {
   auto* gpu_memory_buffer_manager = delegate_->GetGpuMemoryBufferManager();
 
   if (!gpu_memory_buffer_manager || !IsSizeValid(size)) {
@@ -212,7 +212,7 @@
 }
 
 void GpuClient::CreateGpuMemoryBufferFactory(
-    ws::mojom::GpuMemoryBufferFactoryRequest request) {
+    mojom::GpuMemoryBufferFactoryRequest request) {
   gpu_memory_buffer_factory_bindings_.AddBinding(this, std::move(request));
 }
 
diff --git a/components/viz/host/gpu_client.h b/components/viz/host/gpu_client.h
index fbfbfc4e..1727713 100644
--- a/components/viz/host/gpu_client.h
+++ b/components/viz/host/gpu_client.h
@@ -11,12 +11,12 @@
 #include "components/viz/host/gpu_host_impl.h"
 #include "components/viz/host/viz_host_export.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
-#include "services/ws/public/mojom/gpu.mojom.h"
+#include "services/viz/public/interfaces/gpu.mojom.h"
 
 namespace viz {
 
-class VIZ_HOST_EXPORT GpuClient : public ws::mojom::GpuMemoryBufferFactory,
-                                  public ws::mojom::Gpu {
+class VIZ_HOST_EXPORT GpuClient : public mojom::GpuMemoryBufferFactory,
+                                  public mojom::Gpu {
  public:
   using ConnectionErrorHandlerClosure =
       base::OnceCallback<void(GpuClient* client)>;
@@ -30,7 +30,7 @@
   ~GpuClient() override;
 
   // This needs to be run on the thread associated with |task_runner_|.
-  void Add(ws::mojom::GpuRequest request);
+  void Add(mojom::GpuRequest request);
 
   void PreEstablishGpuChannel();
 
@@ -39,20 +39,20 @@
 
   base::WeakPtr<GpuClient> GetWeakPtr();
 
-  // ws::mojom::GpuMemoryBufferFactory overrides:
+  // mojom::GpuMemoryBufferFactory overrides:
   void CreateGpuMemoryBuffer(
       gfx::GpuMemoryBufferId id,
       const gfx::Size& size,
       gfx::BufferFormat format,
       gfx::BufferUsage usage,
-      ws::mojom::GpuMemoryBufferFactory::CreateGpuMemoryBufferCallback callback)
+      mojom::GpuMemoryBufferFactory::CreateGpuMemoryBufferCallback callback)
       override;
   void DestroyGpuMemoryBuffer(gfx::GpuMemoryBufferId id,
                               const gpu::SyncToken& sync_token) override;
 
-  // ws::mojom::Gpu overrides:
+  // mojom::Gpu overrides:
   void CreateGpuMemoryBufferFactory(
-      ws::mojom::GpuMemoryBufferFactoryRequest request) override;
+      mojom::GpuMemoryBufferFactoryRequest request) override;
   void EstablishGpuChannel(EstablishGpuChannelCallback callback) override;
 
 #if defined(OS_CHROMEOS)
@@ -83,9 +83,9 @@
   std::unique_ptr<GpuClientDelegate> delegate_;
   const int client_id_;
   const uint64_t client_tracing_id_;
-  mojo::BindingSet<ws::mojom::GpuMemoryBufferFactory>
+  mojo::BindingSet<mojom::GpuMemoryBufferFactory>
       gpu_memory_buffer_factory_bindings_;
-  mojo::BindingSet<ws::mojom::Gpu> gpu_bindings_;
+  mojo::BindingSet<mojom::Gpu> gpu_bindings_;
   bool gpu_channel_requested_ = false;
   EstablishGpuChannelCallback callback_;
   mojo::ScopedMessagePipeHandle channel_handle_;
diff --git a/components/viz/service/BUILD.gn b/components/viz/service/BUILD.gn
index 94a1676..d51b7cf 100644
--- a/components/viz/service/BUILD.gn
+++ b/components/viz/service/BUILD.gn
@@ -451,7 +451,6 @@
     "//media",
     "//media/capture:capture_lib",
     "//services/viz/public/interfaces",
-    "//services/ws/public/mojom",
     "//skia",
     "//testing/gmock",
     "//testing/gtest",
diff --git a/components/viz/service/display/display_unittest.cc b/components/viz/service/display/display_unittest.cc
index 40ca7db..3b42ed4 100644
--- a/components/viz/service/display/display_unittest.cc
+++ b/components/viz/service/display/display_unittest.cc
@@ -200,7 +200,7 @@
   void UpdateBeginFrameTime(CompositorFrameSinkSupport* support,
                             base::TimeTicks frame_time) {
     support->last_frame_time_ = frame_time;
-    support->presentation_feedbacks_.clear();
+    support->frame_timing_details_.clear();
   }
 
  protected:
@@ -3363,9 +3363,9 @@
     RunAllPendingInMessageLoop();
 
     // Both frames with frame-tokens 1 and 2 requested presentation-feedback.
-    ASSERT_EQ(2u, sub_support->presentation_feedbacks().size());
-    EXPECT_EQ(sub_support->presentation_feedbacks().count(frame_token_1), 1u);
-    EXPECT_EQ(sub_support->presentation_feedbacks().count(frame_token_2), 1u);
+    ASSERT_EQ(2u, sub_support->timing_details().size());
+    EXPECT_EQ(sub_support->timing_details().count(frame_token_1), 1u);
+    EXPECT_EQ(sub_support->timing_details().count(frame_token_2), 1u);
   }
 
   {
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support.cc b/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
index a6a6c94..c4bc261c 100644
--- a/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
+++ b/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
@@ -48,7 +48,7 @@
   // BeginFrameSource does not call into |this| after it's deleted.
   callback_received_begin_frame_ = true;
   callback_received_receive_ack_ = true;
-  presentation_feedbacks_.clear();
+  frame_timing_details_.clear();
   SetNeedsBeginFrame(false);
 
   // For display root surfaces the surface is no longer going to be visible.
@@ -79,10 +79,9 @@
     begin_frame_source_->RemoveObserver(this);
 }
 
-PresentationFeedbackMap
-CompositorFrameSinkSupport::TakePresentationFeedbacks() {
-  PresentationFeedbackMap map;
-  map.swap(presentation_feedbacks_);
+FrameTimingDetailsMap CompositorFrameSinkSupport::TakeFrameTimingDetailsMap() {
+  FrameTimingDetailsMap map;
+  map.swap(frame_timing_details_);
   return map;
 }
 
@@ -578,7 +577,9 @@
     uint32_t presentation_token,
     const gfx::PresentationFeedback& feedback) {
   DCHECK(presentation_token);
-  presentation_feedbacks_.emplace(presentation_token, feedback);
+  FrameTimingDetails details;
+  details.presentation_feedback = feedback;
+  frame_timing_details_.emplace(presentation_token, details);
   UpdateNeedsBeginFramesInternal();
 }
 
@@ -629,8 +630,8 @@
                            TRACE_EVENT_FLAG_FLOW_OUT, "step",
                            "IssueBeginFrame");
     last_frame_time_ = args.frame_time;
-    client_->OnBeginFrame(copy_args, std::move(presentation_feedbacks_));
-    presentation_feedbacks_.clear();
+    client_->OnBeginFrame(copy_args, std::move(frame_timing_details_));
+    frame_timing_details_.clear();
     UpdateNeedsBeginFramesInternal();
   }
 }
@@ -650,9 +651,9 @@
     return;
 
   // We require a begin frame if there's a callback pending, or if the client
-  // requested it, or if the client needs to get some presentation feedbacks.
+  // requested it, or if the client needs to get some frame timing details.
   bool needs_begin_frame =
-      client_needs_begin_frame_ || !presentation_feedbacks_.empty() ||
+      client_needs_begin_frame_ || !frame_timing_details_.empty() ||
       !pending_surfaces_.empty() ||
       (compositor_frame_callback_ && !callback_received_begin_frame_);
 
@@ -769,9 +770,9 @@
 
 bool CompositorFrameSinkSupport::ShouldSendBeginFrame(
     base::TimeTicks frame_time) {
-  // If there are pending presentation feedbacks from the previous frame(s),
+  // If there are pending timing details from the previous frame(s),
   // then the client needs to receive the begin-frame.
-  if (!presentation_feedbacks_.empty())
+  if (!frame_timing_details_.empty())
     return true;
 
   if (!client_needs_begin_frame_)
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support.h b/components/viz/service/frame_sinks/compositor_frame_sink_support.h
index 723b061..3d75b58 100644
--- a/components/viz/service/frame_sinks/compositor_frame_sink_support.h
+++ b/components/viz/service/frame_sinks/compositor_frame_sink_support.h
@@ -15,7 +15,7 @@
 #include "base/optional.h"
 #include "base/time/time.h"
 #include "components/viz/common/frame_sinks/begin_frame_source.h"
-#include "components/viz/common/presentation_feedback_map.h"
+#include "components/viz/common/frame_timing_details_map.h"
 #include "components/viz/common/quads/compositor_frame.h"
 #include "components/viz/common/surfaces/surface_info.h"
 #include "components/viz/common/surfaces/surface_range.h"
@@ -90,11 +90,11 @@
 
   FrameSinkManagerImpl* frame_sink_manager() { return frame_sink_manager_; }
 
-  const PresentationFeedbackMap& presentation_feedbacks() {
-    return presentation_feedbacks_;
+  const FrameTimingDetailsMap& timing_details() {
+    return frame_timing_details_;
   }
 
-  PresentationFeedbackMap TakePresentationFeedbacks() WARN_UNUSED_RESULT;
+  FrameTimingDetailsMap TakeFrameTimingDetailsMap() WARN_UNUSED_RESULT;
 
   // Viz hit-test setup is only called when |is_root_| is true (except on
   // android webview).
@@ -324,7 +324,7 @@
   bool callback_received_receive_ack_ = true;
   uint32_t trace_sequence_ = 0;
 
-  PresentationFeedbackMap presentation_feedbacks_;
+  FrameTimingDetailsMap frame_timing_details_;
   LocalSurfaceId last_evicted_local_surface_id_;
 
   base::TimeTicks last_frame_time_;
diff --git a/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.cc b/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.cc
index bc7ad62f..dfa184a 100644
--- a/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.cc
+++ b/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.cc
@@ -266,9 +266,10 @@
 
 void DirectLayerTreeFrameSink::OnBeginFrame(
     const BeginFrameArgs& args,
-    const PresentationFeedbackMap& feedbacks) {
-  for (const auto& pair : feedbacks)
-    client_->DidPresentCompositorFrame(pair.first, pair.second);
+    const FrameTimingDetailsMap& timing_details) {
+  for (const auto& pair : timing_details)
+    client_->DidPresentCompositorFrame(pair.first,
+                                       pair.second.presentation_feedback);
 
   DCHECK_LE(pipeline_reporting_frame_times_.size(), 25u);
   // Note that client_name is constant during the lifetime of the process and
diff --git a/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.h b/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.h
index 15ae36b..28231b18 100644
--- a/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.h
+++ b/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.h
@@ -11,7 +11,7 @@
 #include "base/threading/thread_checker.h"
 #include "cc/trees/layer_tree_frame_sink.h"
 #include "components/viz/common/frame_sinks/begin_frame_source.h"
-#include "components/viz/common/presentation_feedback_map.h"
+#include "components/viz/common/frame_timing_details_map.h"
 #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
 #include "components/viz/service/display/display_client.h"
 #include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
@@ -95,7 +95,7 @@
   void DidReceiveCompositorFrameAck(
       const std::vector<ReturnedResource>& resources) override;
   void OnBeginFrame(const BeginFrameArgs& args,
-                    const PresentationFeedbackMap& feedbacks) override;
+                    const FrameTimingDetailsMap& timing_details) override;
   void ReclaimResources(
       const std::vector<ReturnedResource>& resources) override;
   void OnBeginFramePausedChanged(bool paused) override;
diff --git a/components/viz/service/gl/DEPS b/components/viz/service/gl/DEPS
index 3651d67..14263ed 100644
--- a/components/viz/service/gl/DEPS
+++ b/components/viz/service/gl/DEPS
@@ -18,9 +18,3 @@
   "+services/viz/privileged/interfaces",
   "+ui/gl",
 ]
-
-specific_include_rules = {
-  "gpu_service_impl_unittest\.cc": [
-    "+services/ws/public/mojom",
-  ]
-}
diff --git a/components/viz/service/gl/gpu_service_impl_unittest.cc b/components/viz/service/gl/gpu_service_impl_unittest.cc
index 399eaaa0..e0602da3 100644
--- a/components/viz/service/gl/gpu_service_impl_unittest.cc
+++ b/components/viz/service/gl/gpu_service_impl_unittest.cc
@@ -15,7 +15,7 @@
 #include "base/single_thread_task_runner.h"
 #include "gpu/config/gpu_info.h"
 #include "gpu/ipc/service/gpu_watchdog_thread.h"
-#include "services/ws/public/mojom/gpu.mojom.h"
+#include "services/viz/public/interfaces/gpu.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace viz {
diff --git a/components/viz/service/surfaces/surface_unittest.cc b/components/viz/service/surfaces/surface_unittest.cc
index 122abfbb..6185c56 100644
--- a/components/viz/service/surfaces/surface_unittest.cc
+++ b/components/viz/service/surfaces/surface_unittest.cc
@@ -57,8 +57,8 @@
             .Build();
     EXPECT_CALL(client, DidReceiveCompositorFrameAck(testing::_)).Times(1);
     support->SubmitCompositorFrame(local_surface_id, std::move(frame));
-    ASSERT_EQ(1u, support->presentation_feedbacks().size());
-    EXPECT_EQ(frame_token, support->presentation_feedbacks().begin()->first);
+    ASSERT_EQ(1u, support->timing_details().size());
+    EXPECT_EQ(frame_token, support->timing_details().begin()->first);
     testing::Mock::VerifyAndClearExpectations(&client);
   }
 }
diff --git a/components/viz/test/fake_compositor_frame_sink_client.cc b/components/viz/test/fake_compositor_frame_sink_client.cc
index 7000e282..d549972 100644
--- a/components/viz/test/fake_compositor_frame_sink_client.cc
+++ b/components/viz/test/fake_compositor_frame_sink_client.cc
@@ -17,7 +17,7 @@
 
 void FakeCompositorFrameSinkClient::OnBeginFrame(
     const BeginFrameArgs& args,
-    const PresentationFeedbackMap& feedbacks) {}
+    const FrameTimingDetailsMap& timing_details) {}
 
 void FakeCompositorFrameSinkClient::ReclaimResources(
     const std::vector<ReturnedResource>& resources) {
diff --git a/components/viz/test/fake_compositor_frame_sink_client.h b/components/viz/test/fake_compositor_frame_sink_client.h
index 7077c21..29fb4ba 100644
--- a/components/viz/test/fake_compositor_frame_sink_client.h
+++ b/components/viz/test/fake_compositor_frame_sink_client.h
@@ -7,7 +7,7 @@
 
 #include <vector>
 
-#include "components/viz/common/presentation_feedback_map.h"
+#include "components/viz/common/frame_timing_details_map.h"
 #include "components/viz/common/surfaces/local_surface_id.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "services/viz/public/interfaces/compositing/compositor_frame_sink.mojom.h"
@@ -25,7 +25,7 @@
   void DidReceiveCompositorFrameAck(
       const std::vector<ReturnedResource>& resources) override;
   void OnBeginFrame(const BeginFrameArgs& args,
-                    const PresentationFeedbackMap& feedbacks) override;
+                    const FrameTimingDetailsMap& timing_details) override;
   void ReclaimResources(
       const std::vector<ReturnedResource>& resources) override;
   void OnBeginFramePausedChanged(bool paused) override;
diff --git a/components/viz/test/mock_compositor_frame_sink_client.h b/components/viz/test/mock_compositor_frame_sink_client.h
index a02882a..caf0a016 100644
--- a/components/viz/test/mock_compositor_frame_sink_client.h
+++ b/components/viz/test/mock_compositor_frame_sink_client.h
@@ -6,7 +6,7 @@
 #define COMPONENTS_VIZ_TEST_MOCK_COMPOSITOR_FRAME_SINK_CLIENT_H_
 
 #include "base/callback.h"
-#include "components/viz/common/presentation_feedback_map.h"
+#include "components/viz/common/frame_timing_details_map.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "services/viz/public/interfaces/compositing/compositor_frame_sink.mojom.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -29,7 +29,7 @@
   MOCK_METHOD1(DidReceiveCompositorFrameAck,
                void(const std::vector<ReturnedResource>&));
   MOCK_METHOD2(OnBeginFrame,
-               void(const BeginFrameArgs&, const PresentationFeedbackMap&));
+               void(const BeginFrameArgs&, const FrameTimingDetailsMap&));
   MOCK_METHOD1(ReclaimResources, void(const std::vector<ReturnedResource>&));
   MOCK_METHOD2(WillDrawSurface, void(const LocalSurfaceId&, const gfx::Rect&));
   MOCK_METHOD1(OnBeginFramePausedChanged, void(bool paused));
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index a00d400..b76a431 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -187,8 +187,8 @@
     "//services/video_capture/public/uma",
     "//services/viz/privileged/interfaces",
     "//services/viz/public/cpp:manifest",
+    "//services/viz/public/cpp/gpu",
     "//services/viz/public/interfaces",
-    "//services/ws/public/cpp/gpu",
     "//skia",
     "//sql",
     "//storage/browser",
@@ -2608,8 +2608,6 @@
 
   if (use_aura) {
     deps += [
-      "//services/ws/public/cpp/host",
-      "//services/ws/public/mojom",
       "//ui/aura",
       "//ui/aura_extra",
       "//ui/strings",
@@ -2617,8 +2615,6 @@
       "//ui/wm/public",
     ]
     sources += [
-      "gpu_interface_provider.cc",
-      "gpu_interface_provider.h",
       "renderer_host/delegated_frame_host_client_aura.cc",
       "renderer_host/delegated_frame_host_client_aura.h",
       "renderer_host/render_widget_host_view_event_handler.cc",
diff --git a/content/browser/accessibility/dump_accessibility_events_browsertest.cc b/content/browser/accessibility/dump_accessibility_events_browsertest.cc
index 3154970..21e6f18 100644
--- a/content/browser/accessibility/dump_accessibility_events_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_events_browsertest.cc
@@ -298,6 +298,11 @@
 }
 
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityEventsTest,
+                       AccessibilityEventsAriaMenuItemFocus) {
+  RunEventTest(FILE_PATH_LITERAL("aria-menuitem-focus.html"));
+}
+
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityEventsTest,
                        AccessibilityEventsAriaMultilineChanged) {
   RunEventTest(FILE_PATH_LITERAL("aria-multiline-changed.html"));
 }
diff --git a/content/browser/android/synchronous_compositor_host.cc b/content/browser/android/synchronous_compositor_host.cc
index 56b6796..e9e5a093 100644
--- a/content/browser/android/synchronous_compositor_host.cc
+++ b/content/browser/android/synchronous_compositor_host.cc
@@ -414,9 +414,9 @@
 }
 
 void SynchronousCompositorHost::DidPresentCompositorFrames(
-    viz::PresentationFeedbackMap feedbacks,
+    viz::FrameTimingDetailsMap timing_details,
     uint32_t frame_token) {
-  rwhva_->DidPresentCompositorFrames(feedbacks);
+  rwhva_->DidPresentCompositorFrames(timing_details);
   UpdatePresentedFrameToken(frame_token);
 }
 
@@ -494,13 +494,13 @@
 void SynchronousCompositorHost::BeginFrame(
     ui::WindowAndroid* window_android,
     const viz::BeginFrameArgs& args,
-    const viz::PresentationFeedbackMap& feedbacks) {
+    const viz::FrameTimingDetailsMap& timing_details) {
   compute_scroll_needs_synchronous_draw_ = false;
   if (!bridge_->WaitAfterVSyncOnUIThread(window_android))
     return;
   mojom::SynchronousCompositor* compositor = GetSynchronousCompositor();
   DCHECK(compositor);
-  compositor->BeginFrame(args, feedbacks);
+  compositor->BeginFrame(args, timing_details);
 }
 
 void SynchronousCompositorHost::SetBeginFramePaused(bool paused) {
diff --git a/content/browser/android/synchronous_compositor_host.h b/content/browser/android/synchronous_compositor_host.h
index 920f49da..8d510f2 100644
--- a/content/browser/android/synchronous_compositor_host.h
+++ b/content/browser/android/synchronous_compositor_host.h
@@ -54,7 +54,7 @@
   void ReturnResources(
       uint32_t layer_tree_frame_sink_id,
       const std::vector<viz::ReturnedResource>& resources) override;
-  void DidPresentCompositorFrames(viz::PresentationFeedbackMap feedbacks,
+  void DidPresentCompositorFrames(viz::FrameTimingDetailsMap timing_details,
                                   uint32_t frame_token) override;
   void SetMemoryPolicy(size_t bytes_limit) override;
   void DidBecomeActive() override;
@@ -67,7 +67,7 @@
   void DidOverscroll(const ui::DidOverscrollParams& over_scroll_params);
   void BeginFrame(ui::WindowAndroid* window_android,
                   const viz::BeginFrameArgs& args,
-                  const viz::PresentationFeedbackMap& feedbacks);
+                  const viz::FrameTimingDetailsMap& timing_details);
   void SetBeginFramePaused(bool paused);
 
   // Called by SynchronousCompositorSyncCallBridge.
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index 55b5f75..9d491aa 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -849,12 +849,10 @@
     RenderProcessHost::SetRunRendererInProcess(true);
 #endif
 
-  // Initialize origins that are whitelisted for process isolation.  Must be
-  // done after base::FeatureList is initialized, but before any navigations
-  // can happen.
-  ChildProcessSecurityPolicyImpl* policy =
-      ChildProcessSecurityPolicyImpl::GetInstance();
-  policy->AddIsolatedOrigins(SiteIsolationPolicy::GetIsolatedOrigins());
+  // Initialize origins that require process isolation.  Must be done
+  // after base::FeatureList is initialized, but before any navigations can
+  // happen.
+  SiteIsolationPolicy::ApplyGlobalIsolatedOrigins();
 
   // Record metrics about which site isolation flags have been turned on.
   SiteIsolationPolicy::StartRecordingSiteIsolationFlagUsage();
diff --git a/content/browser/cache_storage/legacy/legacy_cache_storage_cache.cc b/content/browser/cache_storage/legacy/legacy_cache_storage_cache.cc
index c196ca8..43eeb5e9 100644
--- a/content/browser/cache_storage/legacy/legacy_cache_storage_cache.cc
+++ b/content/browser/cache_storage/legacy/legacy_cache_storage_cache.cc
@@ -21,6 +21,7 @@
 #include "base/guid.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/numerics/checked_math.h"
 #include "base/strings/string_split.h"
@@ -1669,6 +1670,8 @@
   // via WritingCompleted.
   put_context->cache_entry.reset(*entry_ptr);
 
+  base::UmaHistogramSparse("ServiceWorkerCache.DiskCacheCreateEntryResult",
+                           std::abs(rv));
   if (rv != net::OK) {
     PutComplete(std::move(put_context), CacheStorageError::kErrorExists);
     return;
diff --git a/content/browser/child_process_launcher_browsertest.cc b/content/browser/child_process_launcher_browsertest.cc
index c9dbb380..eaa7cce 100644
--- a/content/browser/child_process_launcher_browsertest.cc
+++ b/content/browser/child_process_launcher_browsertest.cc
@@ -12,6 +12,7 @@
 #include "content/public/common/content_switches.h"
 #include "content/public/test/content_browser_test.h"
 #include "content/public/test/content_browser_test_utils.h"
+#include "content/public/test/no_renderer_crashes_assertion.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "content/shell/browser/shell.h"
 
@@ -49,6 +50,8 @@
 class ChildProcessLauncherBrowserTest : public ContentBrowserTest {};
 
 IN_PROC_BROWSER_TEST_F(ChildProcessLauncherBrowserTest, ChildSpawnFail) {
+  ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
+
   GURL url("about:blank");
   Shell* window = shell();
   MockChildProcessLauncherClient* client(nullptr);
diff --git a/content/browser/child_process_security_policy_impl.cc b/content/browser/child_process_security_policy_impl.cc
index b1dc760..7a6ddd8 100644
--- a/content/browser/child_process_security_policy_impl.cc
+++ b/content/browser/child_process_security_policy_impl.cc
@@ -435,12 +435,14 @@
     BrowsingInstanceId min_browsing_instance_id,
     BrowserContext* browser_context,
     ResourceContext* resource_context,
-    bool isolate_all_subdomains)
+    bool isolate_all_subdomains,
+    IsolatedOriginSource source)
     : origin_(origin),
       min_browsing_instance_id_(min_browsing_instance_id),
       browser_context_(browser_context),
       resource_context_(resource_context),
-      isolate_all_subdomains_(isolate_all_subdomains) {
+      isolate_all_subdomains_(isolate_all_subdomains),
+      source_(source) {
   // If there is a BrowserContext, there must also be a ResourceContext
   // associated with this entry.
   DCHECK_EQ(!browser_context, !resource_context);
@@ -1391,6 +1393,7 @@
 
 void ChildProcessSecurityPolicyImpl::AddIsolatedOrigins(
     const std::vector<url::Origin>& origins_to_add,
+    IsolatedOriginSource source,
     BrowserContext* browser_context) {
   std::vector<IsolatedOriginPattern> patterns;
   patterns.reserve(origins_to_add.size());
@@ -1399,11 +1402,12 @@
                  [](const url::Origin& o) -> IsolatedOriginPattern {
                    return IsolatedOriginPattern(o);
                  });
-  AddIsolatedOrigins(patterns, browser_context);
+  AddIsolatedOrigins(patterns, source, browser_context);
 }
 
 void ChildProcessSecurityPolicyImpl::AddIsolatedOrigins(
     const std::vector<IsolatedOriginPattern>& patterns,
+    IsolatedOriginSource source,
     BrowserContext* browser_context) {
   // This can only be called from the UI thread, as it reads state that's only
   // available (and is only safe to be retrieved) on the UI thread, such as
@@ -1466,7 +1470,7 @@
           browser_context ? browser_context->GetResourceContext() : nullptr;
       IsolatedOriginEntry entry(
           std::move(origin_to_add), min_browsing_instance_id, browser_context,
-          resource_context, pattern.isolate_all_subdomains());
+          resource_context, pattern.isolate_all_subdomains(), source);
       isolated_origins_[key].emplace_back(std::move(entry));
     }
   }
@@ -1506,6 +1510,33 @@
   return IsIsolatedOrigin(isolation_context, origin);
 }
 
+std::vector<url::Origin> ChildProcessSecurityPolicyImpl::GetIsolatedOrigins(
+    base::Optional<IsolatedOriginSource> source,
+    BrowserContext* browser_context) {
+  std::vector<url::Origin> origins;
+  base::AutoLock isolated_origins_lock(isolated_origins_lock_);
+  for (const auto& iter : isolated_origins_) {
+    for (const auto& isolated_origin_entry : iter.second) {
+      if (source && source.value() != isolated_origin_entry.source())
+        continue;
+
+      // If browser_context is specified, ensure that the entry matches it.  If
+      // the browser_context is not specified, only consider entries that are
+      // not associated with a profile (i.e., which apply globally to the
+      // entire browser).
+      bool matches_profile =
+          browser_context ? isolated_origin_entry.MatchesProfile(
+                                BrowserOrResourceContext(browser_context))
+                          : isolated_origin_entry.AppliesToAllBrowserContexts();
+      if (!matches_profile)
+        continue;
+
+      origins.push_back(isolated_origin_entry.origin());
+    }
+  }
+  return origins;
+}
+
 bool ChildProcessSecurityPolicyImpl::GetMatchingIsolatedOrigin(
     const IsolationContext& isolation_context,
     const url::Origin& origin,
diff --git a/content/browser/child_process_security_policy_impl.h b/content/browser/child_process_security_policy_impl.h
index 552d3fa..9ab0af96 100644
--- a/content/browser/child_process_security_policy_impl.h
+++ b/content/browser/child_process_security_policy_impl.h
@@ -102,10 +102,15 @@
   void GrantSendMidiSysExMessage(int child_id) override;
   bool CanAccessDataForOrigin(int child_id, const GURL& url) override;
   void AddIsolatedOrigins(const std::vector<url::Origin>& origins,
+                          IsolatedOriginSource source,
                           BrowserContext* browser_context = nullptr) override;
   void AddIsolatedOrigins(const std::vector<IsolatedOriginPattern>& patterns,
+                          IsolatedOriginSource source,
                           BrowserContext* browser_context = nullptr) override;
   bool IsGloballyIsolatedOriginForTesting(const url::Origin& origin) override;
+  std::vector<url::Origin> GetIsolatedOrigins(
+      base::Optional<IsolatedOriginSource> source = base::nullopt,
+      BrowserContext* browser_context = nullptr) override;
 
   // Identical to the above method, but takes url::Origin as input.
   bool CanAccessDataForOrigin(int child_id, const url::Origin& origin);
@@ -360,8 +365,8 @@
                         BrowsingInstanceId min_browsing_instance_id,
                         BrowserContext* browser_context,
                         ResourceContext* resource_context,
-                        bool isolate_all_subdomains);
-
+                        bool isolate_all_subdomains,
+                        IsolatedOriginSource source);
     // Copyable and movable.
     IsolatedOriginEntry(const IsolatedOriginEntry& other);
     IsolatedOriginEntry& operator=(const IsolatedOriginEntry& other);
@@ -372,10 +377,10 @@
     // Allow this class to be used as a key in STL.
     bool operator<(const IsolatedOriginEntry& other) const {
       return std::tie(origin_, min_browsing_instance_id_, browser_context_,
-                      resource_context_, isolate_all_subdomains_) <
+                      resource_context_, isolate_all_subdomains_, source_) <
              std::tie(other.origin_, other.min_browsing_instance_id_,
                       other.browser_context_, other.resource_context_,
-                      other.isolate_all_subdomains_);
+                      other.isolate_all_subdomains_, source_);
     }
 
     bool operator==(const IsolatedOriginEntry& other) const {
@@ -383,7 +388,8 @@
              min_browsing_instance_id_ == other.min_browsing_instance_id_ &&
              browser_context_ == other.browser_context_ &&
              resource_context_ == other.resource_context_ &&
-             isolate_all_subdomains_ == other.isolate_all_subdomains_;
+             isolate_all_subdomains_ == other.isolate_all_subdomains_ &&
+             source_ == other.source_;
     }
 
     // True if this isolated origin applies globally to all profiles.
@@ -405,6 +411,8 @@
 
     bool isolate_all_subdomains() const { return isolate_all_subdomains_; }
 
+    IsolatedOriginSource source() const { return source_; }
+
    private:
     url::Origin origin_;
     BrowsingInstanceId min_browsing_instance_id_;
@@ -424,9 +432,10 @@
     // considered isolated origins.
     bool isolate_all_subdomains_;
 
-    // TODO(alexmos): Track the source of each isolated origin entry, e.g., to
+    // This tracks the source of each isolated origin entry, e.g., to
     // distinguish those that should be displayed to the user from those that
     // should not.  See https://crbug.com/920911.
+    IsolatedOriginSource source_;
   };
 
   // Obtain an instance of ChildProcessSecurityPolicyImpl via GetInstance().
diff --git a/content/browser/child_process_security_policy_unittest.cc b/content/browser/child_process_security_policy_unittest.cc
index 92a30f6..32d7a1a2 100644
--- a/content/browser/child_process_security_policy_unittest.cc
+++ b/content/browser/child_process_security_policy_unittest.cc
@@ -33,6 +33,8 @@
 namespace content {
 namespace {
 
+using IsolatedOriginSource = ChildProcessSecurityPolicy::IsolatedOriginSource;
+
 const int kRendererID = 42;
 
 #if defined(FILE_PATH_USES_DRIVE_LETTERS)
@@ -107,7 +109,8 @@
         {IsolatedOriginEntry(
             origin,
             BrowsingInstanceId::FromUnsafeValue(min_browsing_instance_id),
-            nullptr, nullptr, isolate_all_subdomains)});
+            nullptr, nullptr, isolate_all_subdomains,
+            IsolatedOriginSource::TEST)});
   }
   // Converts |origin| -> (site_url, {entry})
   //     where site_url is created from |origin| and
@@ -134,10 +137,12 @@
         SiteInstanceImpl::GetSiteForOrigin(origin1),
         {IsolatedOriginEntry(origin1,
                              SiteInstanceImpl::NextBrowsingInstanceId(),
-                             nullptr, nullptr, origin1_isolate_all_subdomains),
-         IsolatedOriginEntry(
-             origin2, SiteInstanceImpl::NextBrowsingInstanceId(), nullptr,
-             nullptr, origin2_isolate_all_subdomains)});
+                             nullptr, nullptr, origin1_isolate_all_subdomains,
+                             IsolatedOriginSource::TEST),
+         IsolatedOriginEntry(origin2,
+                             SiteInstanceImpl::NextBrowsingInstanceId(),
+                             nullptr, nullptr, origin2_isolate_all_subdomains,
+                             IsolatedOriginSource::TEST)});
   }
 
   bool IsIsolatedOrigin(BrowserContext* context,
@@ -1225,7 +1230,8 @@
   EXPECT_TRUE(p->CanAccessDataForOrigin(kRendererID, http2_url));
 
   // Isolate |http_url| so we can't get a default SiteInstance.
-  p->AddIsolatedOrigins({url::Origin::Create(http_url)}, &browser_context);
+  p->AddIsolatedOrigins({url::Origin::Create(http_url)},
+                        IsolatedOriginSource::TEST, &browser_context);
 
   // Lock process to |http_url| origin.
   scoped_refptr<SiteInstanceImpl> foo_instance =
@@ -1353,14 +1359,14 @@
                      testing::IsEmpty());
 
   // Verify deduplication of the argument.
-  p->AddIsolatedOrigins({foo, bar, bar});
+  p->AddIsolatedOrigins({foo, bar, bar}, IsolatedOriginSource::TEST);
   LOCKED_EXPECT_THAT(
       p->isolated_origins_lock_, p->isolated_origins_,
       testing::UnorderedElementsAre(GetIsolatedOriginEntry(foo),
                                     GetIsolatedOriginEntry(bar)));
 
   // Verify that the old set is extended (not replaced).
-  p->AddIsolatedOrigins({baz});
+  p->AddIsolatedOrigins({baz}, IsolatedOriginSource::TEST);
   LOCKED_EXPECT_THAT(
       p->isolated_origins_lock_, p->isolated_origins_,
       testing::UnorderedElementsAre(GetIsolatedOriginEntry(foo),
@@ -1368,7 +1374,7 @@
                                     GetIsolatedOriginEntry(baz)));
 
   // Verify deduplication against the old set.
-  p->AddIsolatedOrigins({foo});
+  p->AddIsolatedOrigins({foo}, IsolatedOriginSource::TEST);
   LOCKED_EXPECT_THAT(
       p->isolated_origins_lock_, p->isolated_origins_,
       testing::UnorderedElementsAre(GetIsolatedOriginEntry(foo),
@@ -1377,7 +1383,8 @@
 
   // Verify deduplication considers scheme and port differences.  Note that
   // origins that differ only in ports map to the same key.
-  p->AddIsolatedOrigins({baz, baz_http_8000, baz_https_8000});
+  p->AddIsolatedOrigins({baz, baz_http_8000, baz_https_8000},
+                        IsolatedOriginSource::TEST);
   LOCKED_EXPECT_THAT(
       p->isolated_origins_lock_, p->isolated_origins_,
       testing::UnorderedElementsAre(
@@ -1398,7 +1405,7 @@
         .Times(1);
 
     mock_log.StartCapturingLogs();
-    p->AddIsolatedOrigins({quxfoo, invalid_etld});
+    p->AddIsolatedOrigins({quxfoo, invalid_etld}, IsolatedOriginSource::TEST);
     LOCKED_EXPECT_THAT(
         p->isolated_origins_lock_, p->isolated_origins_,
         testing::UnorderedElementsAre(
@@ -1428,21 +1435,21 @@
       ChildProcessSecurityPolicyImpl::GetInstance();
 
   // Check we can add a single wildcard origin.
-  p->AddIsolatedOrigins({etld1_wild});
+  p->AddIsolatedOrigins({etld1_wild}, IsolatedOriginSource::TEST);
 
   LOCKED_EXPECT_THAT(
       p->isolated_origins_lock_, p->isolated_origins_,
       testing::UnorderedElementsAre(GetIsolatedOriginEntry(etld1, true)));
 
   // Add a conventional origin and check they can live side by side.
-  p->AddIsolatedOrigins({qux});
+  p->AddIsolatedOrigins({qux}, IsolatedOriginSource::TEST);
   LOCKED_EXPECT_THAT(
       p->isolated_origins_lock_, p->isolated_origins_,
       testing::UnorderedElementsAre(GetIsolatedOriginEntry(etld1, true),
                                     GetIsolatedOriginEntry(qux, false)));
 
   // Check that a wildcard domain within another wildcard domain can be added.
-  p->AddIsolatedOrigins({etld2_wild});
+  p->AddIsolatedOrigins({etld2_wild}, IsolatedOriginSource::TEST);
   LOCKED_EXPECT_THAT(p->isolated_origins_lock_, p->isolated_origins_,
                      testing::UnorderedElementsAre(
                          GetIsolatedOriginEntry(etld1, etld2, true, true),
@@ -1506,14 +1513,14 @@
   // the existing behavior of |isolated_url| and |inner_isolated_url| remains
   // unaffected, while all subdomains of wildcard.com are returned as unique
   // sites.
-  p->AddIsolatedOrigins({wildcard});
+  p->AddIsolatedOrigins({wildcard}, IsolatedOriginSource::TEST);
   origins_site_test_map[inner_wildcard_url] = inner_wildcard_url;
   origins_site_test_map[host_inner_wildcard_url] = host_inner_wildcard_url;
   CheckGetSiteForURL(browser_context(), origins_site_test_map);
 
   // Add |inner_isolated|, then verify that querying for |inner_isolated_url|
   // returns |inner_isolated_url| while leaving the wildcard origins unaffected.
-  p->AddIsolatedOrigins({inner_isolated});
+  p->AddIsolatedOrigins({inner_isolated}, IsolatedOriginSource::TEST);
   origins_site_test_map[inner_isolated_url] = inner_isolated_url;
   origins_site_test_map[host_inner_isolated_url] = inner_isolated_url;
   CheckGetSiteForURL(browser_context(), origins_site_test_map);
@@ -1521,7 +1528,7 @@
   // Add |inner_wildcard|. This should not change the behavior of the test
   // above as all subdomains of |inner_wildcard| are contained within
   // |wildcard|.
-  p->AddIsolatedOrigins({inner_wildcard});
+  p->AddIsolatedOrigins({inner_wildcard}, IsolatedOriginSource::TEST);
   CheckGetSiteForURL(browser_context(), origins_site_test_map);
 
   p->RemoveIsolatedOriginForTesting(wildcard.origin());
@@ -1552,7 +1559,8 @@
     GURL wildcard_isolated_url("https://wildcard.isolated.com");
     GURL a_wildcard_isolated_url("https://a.wildcard.isolated.com");
 
-    p->AddIsolatedOrigins({isolated, wildcard_isolated});
+    p->AddIsolatedOrigins({isolated, wildcard_isolated},
+                          IsolatedOriginSource::TEST);
     std::map<GURL, GURL> origin_site_map{
         {isolated_url, isolated_url},
         {a_isolated_url, isolated_url},
@@ -1581,7 +1589,8 @@
     GURL isolated_wildcard_url("https://isolated.wildcard.com");
     GURL a_isolated_wildcard_url("https://a.isolated.wildcard.com");
 
-    p->AddIsolatedOrigins({wildcard, isolated_wildcard});
+    p->AddIsolatedOrigins({wildcard, isolated_wildcard},
+                          IsolatedOriginSource::TEST);
     std::map<GURL, GURL> origin_site_map{
         {wildcard_url, wildcard_url},
         {a_wildcard_url, a_wildcard_url},
@@ -1609,7 +1618,7 @@
     GURL inner_url("https://inner.outer.com");
     GURL a_inner_url("https://a.inner.outer.com");
 
-    p->AddIsolatedOrigins({inner, outer});
+    p->AddIsolatedOrigins({inner, outer}, IsolatedOriginSource::TEST);
 
     std::map<GURL, GURL> origin_site_map{
         {outer_url, outer_url},
@@ -1635,14 +1644,14 @@
 
     GURL host_url("https://host.bar.foo.com");
 
-    p->AddIsolatedOrigins({wild});
+    p->AddIsolatedOrigins({wild}, IsolatedOriginSource::TEST);
     std::map<GURL, GURL> origin_site_map{
         {host_url, host_url},
     };
 
     CheckGetSiteForURL(browser_context(), origin_site_map);
 
-    p->AddIsolatedOrigins({single});
+    p->AddIsolatedOrigins({single}, IsolatedOriginSource::TEST);
 
     CheckGetSiteForURL(browser_context(), origin_site_map);
 
@@ -1662,14 +1671,14 @@
     GURL host_url("https://host.bar.foo.com");
     GURL domain_url("https://bar.foo.com");
 
-    p->AddIsolatedOrigins({single});
+    p->AddIsolatedOrigins({single}, IsolatedOriginSource::TEST);
     std::map<GURL, GURL> origin_site_map{
         {host_url, domain_url},
     };
 
     CheckGetSiteForURL(browser_context(), origin_site_map);
 
-    p->AddIsolatedOrigins({wild});
+    p->AddIsolatedOrigins({wild}, IsolatedOriginSource::TEST);
 
     CheckGetSiteForURL(browser_context(), origin_site_map);
 
@@ -1701,14 +1710,14 @@
   int initial_id(SiteInstanceImpl::NextBrowsingInstanceId().GetUnsafeValue());
 
   // Isolate foo.com and bar.com.
-  p->AddIsolatedOrigins({foo, bar});
+  p->AddIsolatedOrigins({foo, bar}, IsolatedOriginSource::TEST);
   LOCKED_EXPECT_THAT(
       p->isolated_origins_lock_, p->isolated_origins_,
       testing::UnorderedElementsAre(GetIsolatedOriginEntry(initial_id, foo),
                                     GetIsolatedOriginEntry(initial_id, bar)));
 
   // Isolating bar.com again should have no effect.
-  p->AddIsolatedOrigins({bar});
+  p->AddIsolatedOrigins({bar}, IsolatedOriginSource::TEST);
   LOCKED_EXPECT_THAT(
       p->isolated_origins_lock_, p->isolated_origins_,
       testing::UnorderedElementsAre(GetIsolatedOriginEntry(initial_id, foo),
@@ -1725,7 +1734,7 @@
 
   // Isolate baz.com.  This will apply to BrowsingInstances with IDs
   // |initial_id + 1| and above.
-  p->AddIsolatedOrigins({baz});
+  p->AddIsolatedOrigins({baz}, IsolatedOriginSource::TEST);
   LOCKED_EXPECT_THAT(p->isolated_origins_lock_, p->isolated_origins_,
                      testing::UnorderedElementsAre(
                          GetIsolatedOriginEntry(initial_id, foo),
@@ -1733,7 +1742,7 @@
                          GetIsolatedOriginEntry(initial_id + 1, baz)));
 
   // Isolating bar.com again should not update the old BrowsingInstance ID.
-  p->AddIsolatedOrigins({bar});
+  p->AddIsolatedOrigins({bar}, IsolatedOriginSource::TEST);
   LOCKED_EXPECT_THAT(p->isolated_origins_lock_, p->isolated_origins_,
                      testing::UnorderedElementsAre(
                          GetIsolatedOriginEntry(initial_id, foo),
@@ -1749,7 +1758,7 @@
             SiteInstanceImpl::NextBrowsingInstanceId());
 
   // Isolate qux.com.
-  p->AddIsolatedOrigins({qux});
+  p->AddIsolatedOrigins({qux}, IsolatedOriginSource::TEST);
   LOCKED_EXPECT_THAT(p->isolated_origins_lock_, p->isolated_origins_,
                      testing::UnorderedElementsAre(
                          GetIsolatedOriginEntry(initial_id, foo),
@@ -1830,12 +1839,12 @@
   int initial_id(SiteInstanceImpl::NextBrowsingInstanceId().GetUnsafeValue());
 
   // Isolate foo.com globally (for all BrowserContexts).
-  p->AddIsolatedOrigins({foo});
+  p->AddIsolatedOrigins({foo}, IsolatedOriginSource::TEST);
 
   TestBrowserContext context1, context2;
 
   // Isolate bar.com in |context1|.
-  p->AddIsolatedOrigins({bar}, &context1);
+  p->AddIsolatedOrigins({bar}, IsolatedOriginSource::TEST, &context1);
 
   // bar.com should be isolated for |context1|, but not |context2|. foo.com
   // should be isolated for all contexts.
@@ -1861,14 +1870,14 @@
   // important, e.g. for persisting profile-specific isolated origins across
   // restarts.
   EXPECT_EQ(1, GetIsolatedOriginEntryCount(foo));
-  p->AddIsolatedOrigins({foo}, &context1);
+  p->AddIsolatedOrigins({foo}, IsolatedOriginSource::TEST, &context1);
   EXPECT_EQ(2, GetIsolatedOriginEntryCount(foo));
   EXPECT_TRUE(IsIsolatedOrigin(&context1, initial_id, foo));
   EXPECT_TRUE(IsIsolatedOrigin(&context2, initial_id, foo));
 
   // Isolating bar.com in |context1| again should have no effect.
   EXPECT_EQ(1, GetIsolatedOriginEntryCount(bar));
-  p->AddIsolatedOrigins({bar}, &context1);
+  p->AddIsolatedOrigins({bar}, IsolatedOriginSource::TEST, &context1);
   EXPECT_EQ(1, GetIsolatedOriginEntryCount(bar));
   EXPECT_TRUE(IsIsolatedOrigin(&context1, initial_id, bar));
   EXPECT_FALSE(IsIsolatedOrigin(&context2, initial_id, bar));
@@ -1876,7 +1885,7 @@
   // Isolate bar.com for |context2|, which should add a new
   // IsolatedOriginEntry.  Verify that the isolation took effect for
   // |initial_id + 1| (the current BrowsingInstance ID cutoff) only.
-  p->AddIsolatedOrigins({bar}, &context2);
+  p->AddIsolatedOrigins({bar}, IsolatedOriginSource::TEST, &context2);
   EXPECT_EQ(2, GetIsolatedOriginEntryCount(bar));
   EXPECT_FALSE(IsIsolatedOrigin(&context2, initial_id, bar));
   EXPECT_TRUE(IsIsolatedOrigin(&context2, initial_id + 1, bar));
@@ -1896,7 +1905,7 @@
   // Now, add bar.com as a globally isolated origin.  This should make it apply
   // to context3 as well, but only in initial_id + 1 (the current
   // BrowsingInstance ID cutoff).
-  p->AddIsolatedOrigins({bar});
+  p->AddIsolatedOrigins({bar}, IsolatedOriginSource::TEST);
   EXPECT_EQ(3, GetIsolatedOriginEntryCount(bar));
   EXPECT_FALSE(IsIsolatedOrigin(&context3, initial_id, bar));
   EXPECT_TRUE(IsIsolatedOrigin(&context3, initial_id + 1, bar));
@@ -1905,7 +1914,7 @@
   // IsolatedOriginEntry, though it wouldn't provide any additional isolation,
   // since bar.com is already isolated globally.
   TestBrowserContext context4;
-  p->AddIsolatedOrigins({bar}, &context4);
+  p->AddIsolatedOrigins({bar}, IsolatedOriginSource::TEST, &context4);
   EXPECT_EQ(4, GetIsolatedOriginEntryCount(bar));
 
   p->RemoveIsolatedOriginForTesting(foo);
@@ -1938,7 +1947,7 @@
 
   // Isolate foo.com in |context1|.  Note that sub.foo.com should also be
   // considered isolated in |context1|, since it's a subdomain of foo.com.
-  p->AddIsolatedOrigins({foo}, context1.get());
+  p->AddIsolatedOrigins({foo}, IsolatedOriginSource::TEST, context1.get());
   EXPECT_EQ(1, GetIsolatedOriginEntryCount(foo));
   EXPECT_TRUE(IsIsolatedOrigin(context1.get(), initial_id, foo));
   EXPECT_TRUE(IsIsolatedOrigin(context1.get(), initial_id, sub_foo));
@@ -1946,7 +1955,8 @@
   EXPECT_FALSE(IsIsolatedOrigin(context2.get(), initial_id, sub_foo));
 
   // Isolate sub.foo.com and bar.com in |context2|.
-  p->AddIsolatedOrigins({sub_foo, bar}, context2.get());
+  p->AddIsolatedOrigins({sub_foo, bar}, IsolatedOriginSource::TEST,
+                        context2.get());
   EXPECT_EQ(1, GetIsolatedOriginEntryCount(sub_foo));
   EXPECT_EQ(1, GetIsolatedOriginEntryCount(bar));
   EXPECT_TRUE(IsIsolatedOrigin(context2.get(), initial_id, sub_foo));
@@ -1954,8 +1964,8 @@
   EXPECT_FALSE(IsIsolatedOrigin(context2.get(), initial_id, foo));
 
   // Isolate baz.com in both BrowserContexts.
-  p->AddIsolatedOrigins({baz}, context1.get());
-  p->AddIsolatedOrigins({baz}, context2.get());
+  p->AddIsolatedOrigins({baz}, IsolatedOriginSource::TEST, context1.get());
+  p->AddIsolatedOrigins({baz}, IsolatedOriginSource::TEST, context2.get());
 
   EXPECT_EQ(2, GetIsolatedOriginEntryCount(baz));
   EXPECT_TRUE(IsIsolatedOrigin(context1.get(), initial_id, baz));
@@ -2171,4 +2181,106 @@
   EXPECT_FALSE(p.is_valid());
 }
 
+// This test adds isolated origins from various sources and verifies that
+// GetIsolatedOrigins() properly restricts lookups by source.
+TEST_F(ChildProcessSecurityPolicyTest, GetIsolatedOrigins) {
+  url::Origin foo = url::Origin::Create(GURL("https://foo.com/"));
+  url::Origin bar = url::Origin::Create(GURL("https://bar.com/"));
+  url::Origin baz = url::Origin::Create(GURL("https://baz.com/"));
+  url::Origin qux = url::Origin::Create(GURL("https://qux.com/"));
+  ChildProcessSecurityPolicyImpl* p =
+      ChildProcessSecurityPolicyImpl::GetInstance();
+
+  // Initially there should be no isolated origins.
+  EXPECT_THAT(p->GetIsolatedOrigins(), testing::IsEmpty());
+
+  // Add isolated origins from various sources, and verify that
+  // GetIsolatedOrigins properly restricts lookups by source.
+  p->AddIsolatedOrigins({foo}, IsolatedOriginSource::TEST);
+  p->AddIsolatedOrigins({bar}, IsolatedOriginSource::FIELD_TRIAL);
+
+  EXPECT_THAT(p->GetIsolatedOrigins(), testing::UnorderedElementsAre(foo, bar));
+  EXPECT_THAT(p->GetIsolatedOrigins(IsolatedOriginSource::TEST),
+              testing::UnorderedElementsAre(foo));
+  EXPECT_THAT(p->GetIsolatedOrigins(IsolatedOriginSource::FIELD_TRIAL),
+              testing::UnorderedElementsAre(bar));
+
+  p->AddIsolatedOrigins({baz}, IsolatedOriginSource::POLICY);
+  p->AddIsolatedOrigins({qux}, IsolatedOriginSource::COMMAND_LINE);
+
+  EXPECT_THAT(p->GetIsolatedOrigins(),
+              testing::UnorderedElementsAre(foo, bar, baz, qux));
+  EXPECT_THAT(p->GetIsolatedOrigins(IsolatedOriginSource::TEST),
+              testing::UnorderedElementsAre(foo));
+  EXPECT_THAT(p->GetIsolatedOrigins(IsolatedOriginSource::FIELD_TRIAL),
+              testing::UnorderedElementsAre(bar));
+  EXPECT_THAT(p->GetIsolatedOrigins(IsolatedOriginSource::POLICY),
+              testing::UnorderedElementsAre(baz));
+  EXPECT_THAT(p->GetIsolatedOrigins(IsolatedOriginSource::COMMAND_LINE),
+              testing::UnorderedElementsAre(qux));
+
+  p->RemoveIsolatedOriginForTesting(foo);
+  p->RemoveIsolatedOriginForTesting(bar);
+  p->RemoveIsolatedOriginForTesting(baz);
+  p->RemoveIsolatedOriginForTesting(qux);
+  EXPECT_THAT(p->GetIsolatedOrigins(), testing::IsEmpty());
+}
+
+// This test adds isolated origins from various sources as well as restricted
+// to particular profiles, and verifies that GetIsolatedOrigins() properly
+// restricts lookups by both source and profile.
+TEST_F(ChildProcessSecurityPolicyTest, GetIsolatedOriginsWithProfile) {
+  url::Origin foo = url::Origin::Create(GURL("https://foo.com/"));
+  url::Origin bar = url::Origin::Create(GURL("https://bar.com/"));
+  url::Origin baz = url::Origin::Create(GURL("https://baz.com/"));
+  url::Origin qux = url::Origin::Create(GURL("https://qux.com/"));
+  ChildProcessSecurityPolicyImpl* p =
+      ChildProcessSecurityPolicyImpl::GetInstance();
+  TestBrowserContext context1, context2;
+
+  // Initially there should be no isolated origins.
+  EXPECT_THAT(p->GetIsolatedOrigins(), testing::IsEmpty());
+
+  // Add a global isolated origin.  Note that since it applies to all profiles,
+  // GetIsolatedOrigins() should return it for any passed-in profile.
+  p->AddIsolatedOrigins({foo}, IsolatedOriginSource::TEST);
+
+  // Add some per-profile isolated origins.
+  p->AddIsolatedOrigins({bar}, IsolatedOriginSource::USER_TRIGGERED, &context1);
+  p->AddIsolatedOrigins({baz}, IsolatedOriginSource::POLICY, &context2);
+  p->AddIsolatedOrigins({qux}, IsolatedOriginSource::USER_TRIGGERED, &context1);
+  p->AddIsolatedOrigins({qux}, IsolatedOriginSource::USER_TRIGGERED, &context2);
+
+  EXPECT_THAT(p->GetIsolatedOrigins(), testing::UnorderedElementsAre(foo));
+
+  EXPECT_THAT(p->GetIsolatedOrigins(IsolatedOriginSource::TEST),
+              testing::UnorderedElementsAre(foo));
+  EXPECT_THAT(p->GetIsolatedOrigins(IsolatedOriginSource::TEST, &context1),
+              testing::UnorderedElementsAre(foo));
+  EXPECT_THAT(p->GetIsolatedOrigins(IsolatedOriginSource::TEST, &context2),
+              testing::UnorderedElementsAre(foo));
+
+  EXPECT_THAT(p->GetIsolatedOrigins(IsolatedOriginSource::USER_TRIGGERED),
+              testing::IsEmpty());
+  EXPECT_THAT(
+      p->GetIsolatedOrigins(IsolatedOriginSource::USER_TRIGGERED, &context1),
+      testing::UnorderedElementsAre(bar, qux));
+  EXPECT_THAT(
+      p->GetIsolatedOrigins(IsolatedOriginSource::USER_TRIGGERED, &context2),
+      testing::UnorderedElementsAre(qux));
+
+  EXPECT_THAT(p->GetIsolatedOrigins(IsolatedOriginSource::POLICY),
+              testing::IsEmpty());
+  EXPECT_THAT(p->GetIsolatedOrigins(IsolatedOriginSource::POLICY, &context1),
+              testing::IsEmpty());
+  EXPECT_THAT(p->GetIsolatedOrigins(IsolatedOriginSource::POLICY, &context2),
+              testing::UnorderedElementsAre(baz));
+
+  p->RemoveIsolatedOriginForTesting(foo);
+  p->RemoveIsolatedOriginForTesting(bar);
+  p->RemoveIsolatedOriginForTesting(baz);
+  p->RemoveIsolatedOriginForTesting(qux);
+  EXPECT_THAT(p->GetIsolatedOrigins(), testing::IsEmpty());
+}
+
 }  // namespace content
diff --git a/content/browser/compositor/browser_compositor_output_surface.cc b/content/browser/compositor/browser_compositor_output_surface.cc
index 79b2ae7..0942954 100644
--- a/content/browser/compositor/browser_compositor_output_surface.cc
+++ b/content/browser/compositor/browser_compositor_output_surface.cc
@@ -14,7 +14,7 @@
 #include "components/viz/service/display/output_surface_client.h"
 #include "components/viz/service/display/overlay_candidate_validator.h"
 #include "content/browser/compositor/reflector_impl.h"
-#include "services/ws/public/cpp/gpu/context_provider_command_buffer.h"
+#include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
 
 namespace content {
 
diff --git a/content/browser/compositor/gpu_browser_compositor_output_surface.cc b/content/browser/compositor/gpu_browser_compositor_output_surface.cc
index 05cf2f7..15c1b4de 100644
--- a/content/browser/compositor/gpu_browser_compositor_output_surface.cc
+++ b/content/browser/compositor/gpu_browser_compositor_output_surface.cc
@@ -19,13 +19,13 @@
 #include "gpu/command_buffer/common/swap_buffers_complete_params.h"
 #include "gpu/command_buffer/common/swap_buffers_flags.h"
 #include "gpu/ipc/client/command_buffer_proxy_impl.h"
-#include "services/ws/public/cpp/gpu/context_provider_command_buffer.h"
+#include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
 #include "ui/gl/color_space_utils.h"
 
 namespace content {
 
 GpuBrowserCompositorOutputSurface::GpuBrowserCompositorOutputSurface(
-    scoped_refptr<ws::ContextProviderCommandBuffer> context,
+    scoped_refptr<viz::ContextProviderCommandBuffer> context,
     std::unique_ptr<viz::OverlayCandidateValidator> overlay_candidate_validator)
     : BrowserCompositorOutputSurface(std::move(context),
                                      std::move(overlay_candidate_validator)),
@@ -142,7 +142,8 @@
 }
 
 uint32_t GpuBrowserCompositorOutputSurface::GetFramebufferCopyTextureFormat() {
-  auto* gl = static_cast<ws::ContextProviderCommandBuffer*>(context_provider());
+  auto* gl =
+      static_cast<viz::ContextProviderCommandBuffer*>(context_provider());
   return gl->GetCopyTextureInternalFormat();
 }
 
@@ -190,8 +191,8 @@
 
 gpu::CommandBufferProxyImpl*
 GpuBrowserCompositorOutputSurface::GetCommandBufferProxy() {
-  ws::ContextProviderCommandBuffer* provider_command_buffer =
-      static_cast<ws::ContextProviderCommandBuffer*>(context_provider_.get());
+  viz::ContextProviderCommandBuffer* provider_command_buffer =
+      static_cast<viz::ContextProviderCommandBuffer*>(context_provider_.get());
   gpu::CommandBufferProxyImpl* command_buffer_proxy =
       provider_command_buffer->GetCommandBufferProxy();
   DCHECK(command_buffer_proxy);
diff --git a/content/browser/compositor/gpu_browser_compositor_output_surface.h b/content/browser/compositor/gpu_browser_compositor_output_surface.h
index 8c068b85..76a22bc 100644
--- a/content/browser/compositor/gpu_browser_compositor_output_surface.h
+++ b/content/browser/compositor/gpu_browser_compositor_output_surface.h
@@ -26,7 +26,7 @@
 struct SwapBuffersCompleteParams;
 }
 
-namespace ws {
+namespace viz {
 class ContextProviderCommandBuffer;
 }
 
@@ -40,7 +40,7 @@
     : public BrowserCompositorOutputSurface {
  public:
   GpuBrowserCompositorOutputSurface(
-      scoped_refptr<ws::ContextProviderCommandBuffer> context,
+      scoped_refptr<viz::ContextProviderCommandBuffer> context,
       std::unique_ptr<viz::OverlayCandidateValidator>
           overlay_candidate_validator);
 
diff --git a/content/browser/compositor/gpu_process_transport_factory.cc b/content/browser/compositor/gpu_process_transport_factory.cc
index 7e3574c..757011d 100644
--- a/content/browser/compositor/gpu_process_transport_factory.cc
+++ b/content/browser/compositor/gpu_process_transport_factory.cc
@@ -64,7 +64,7 @@
 #include "gpu/ipc/client/gpu_channel_host.h"
 #include "gpu/ipc/host/gpu_memory_buffer_support.h"
 #include "gpu/vulkan/buildflags.h"
-#include "services/ws/public/cpp/gpu/context_provider_command_buffer.h"
+#include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
 #include "third_party/khronos/GLES2/gl2.h"
 #include "ui/base/ui_base_switches_util.h"
 #include "ui/compositor/compositor.h"
@@ -181,7 +181,7 @@
           kStreamId,
           kStreamPriority,
           GURL(kIdentityUrl),
-          ws::command_buffer_metrics::ContextType::BROWSER_WORKER),
+          viz::command_buffer_metrics::ContextType::BROWSER_WORKER),
       gpu_channel_factory_(gpu_channel_factory),
       compositing_mode_reporter_(compositing_mode_reporter),
       server_shared_bitmap_manager_(server_shared_bitmap_manager),
@@ -331,7 +331,7 @@
   support_stencil = true;
 #endif
 
-  scoped_refptr<ws::ContextProviderCommandBuffer> context_provider;
+  scoped_refptr<viz::ContextProviderCommandBuffer> context_provider;
 
   if (!use_gpu_compositing) {
     // If not using GL compositing, don't keep the old shared worker context.
@@ -369,7 +369,7 @@
           std::move(gpu_channel_host), surface_handle, need_alpha_channel,
           support_stencil, support_locking, support_gles2_interface,
           support_raster_interface, support_grcontext,
-          ws::command_buffer_metrics::ContextType::BROWSER_COMPOSITOR);
+          viz::command_buffer_metrics::ContextType::BROWSER_COMPOSITOR);
       // On Mac, GpuCommandBufferMsg_SwapBuffersCompleted must be handled in
       // a nested run loop during resize.
       context_provider->SetDefaultTaskRunner(resize_task_runner_);
@@ -850,7 +850,7 @@
       std::move(gpu_channel_host), gpu::kNullSurfaceHandle, need_alpha_channel,
       false, support_locking, support_gles2_interface, support_raster_interface,
       support_grcontext,
-      ws::command_buffer_metrics::ContextType::BROWSER_MAIN_THREAD);
+      viz::command_buffer_metrics::ContextType::BROWSER_MAIN_THREAD);
   shared_main_thread_contexts_->AddObserver(this);
   auto result = shared_main_thread_contexts_->BindToCurrentThread();
   if (result != gpu::ContextResult::kSuccess) {
@@ -925,7 +925,7 @@
                      callback_factory_.GetWeakPtr()));
 }
 
-scoped_refptr<ws::ContextProviderCommandBuffer>
+scoped_refptr<viz::ContextProviderCommandBuffer>
 GpuProcessTransportFactory::CreateContextCommon(
     scoped_refptr<gpu::GpuChannelHost> gpu_channel_host,
     gpu::SurfaceHandle surface_handle,
@@ -935,7 +935,7 @@
     bool support_gles2_interface,
     bool support_raster_interface,
     bool support_grcontext,
-    ws::command_buffer_metrics::ContextType type) {
+    viz::command_buffer_metrics::ContextType type) {
   DCHECK(gpu_channel_host);
   DCHECK(!is_gpu_compositing_disabled_);
 
@@ -970,7 +970,7 @@
 
   constexpr bool automatic_flushes = false;
 
-  return base::MakeRefCounted<ws::ContextProviderCommandBuffer>(
+  return base::MakeRefCounted<viz::ContextProviderCommandBuffer>(
       std::move(gpu_channel_host), GetGpuMemoryBufferManager(), kStreamId,
       kStreamPriority, surface_handle, GURL(kIdentityUrl), automatic_flushes,
       support_locking, support_grcontext, memory_limits, attributes, type);
diff --git a/content/browser/compositor/gpu_process_transport_factory.h b/content/browser/compositor/gpu_process_transport_factory.h
index 4a78bde..0ed56b6 100644
--- a/content/browser/compositor/gpu_process_transport_factory.h
+++ b/content/browser/compositor/gpu_process_transport_factory.h
@@ -21,8 +21,8 @@
 #include "components/viz/host/host_frame_sink_manager.h"
 #include "content/browser/compositor/image_transport_factory.h"
 #include "gpu/ipc/client/gpu_channel_host.h"
-#include "services/ws/public/cpp/gpu/command_buffer_metrics.h"
-#include "services/ws/public/cpp/gpu/shared_worker_context_provider_factory.h"
+#include "services/viz/public/cpp/gpu/command_buffer_metrics.h"
+#include "services/viz/public/cpp/gpu/shared_worker_context_provider_factory.h"
 #include "ui/compositor/compositor.h"
 
 namespace base {
@@ -45,7 +45,7 @@
 class SoftwareOutputDevice;
 }
 
-namespace ws {
+namespace viz {
 class ContextProviderCommandBuffer;
 }
 
@@ -132,7 +132,7 @@
   // viz::ContextLostObserver implementation.
   void OnContextLost() override;
 
-  scoped_refptr<ws::ContextProviderCommandBuffer> CreateContextCommon(
+  scoped_refptr<viz::ContextProviderCommandBuffer> CreateContextCommon(
       scoped_refptr<gpu::GpuChannelHost> gpu_channel_host,
       gpu::SurfaceHandle surface_handle,
       bool need_alpha_channel,
@@ -141,7 +141,7 @@
       bool support_gles2_interface,
       bool support_raster_interface,
       bool support_grcontext,
-      ws::command_buffer_metrics::ContextType type);
+      viz::command_buffer_metrics::ContextType type);
 
   viz::FrameSinkIdAllocator frame_sink_id_allocator_;
 
@@ -150,11 +150,11 @@
       PerCompositorDataMap;
   PerCompositorDataMap per_compositor_data_;
 
-  scoped_refptr<ws::ContextProviderCommandBuffer> shared_main_thread_contexts_;
+  scoped_refptr<viz::ContextProviderCommandBuffer> shared_main_thread_contexts_;
   base::ObserverList<ui::ContextFactoryObserver>::Unchecked observer_list_;
   scoped_refptr<base::SingleThreadTaskRunner> resize_task_runner_;
   std::unique_ptr<cc::SingleThreadTaskGraphRunner> task_graph_runner_;
-  ws::SharedWorkerContextProviderFactory
+  viz::SharedWorkerContextProviderFactory
       shared_worker_context_provider_factory_;
 
   bool is_gpu_compositing_disabled_ = false;
diff --git a/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.cc b/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.cc
index eed5a25..69464e15 100644
--- a/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.cc
+++ b/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.cc
@@ -14,13 +14,13 @@
 #include "gpu/GLES2/gl2extchromium.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
 #include "gpu/command_buffer/common/swap_buffers_complete_params.h"
-#include "services/ws/public/cpp/gpu/context_provider_command_buffer.h"
+#include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
 
 namespace content {
 
 GpuSurfacelessBrowserCompositorOutputSurface::
     GpuSurfacelessBrowserCompositorOutputSurface(
-        scoped_refptr<ws::ContextProviderCommandBuffer> context,
+        scoped_refptr<viz::ContextProviderCommandBuffer> context,
         gpu::SurfaceHandle surface_handle,
         std::unique_ptr<viz::OverlayCandidateValidator>
             overlay_candidate_validator,
diff --git a/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.h b/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.h
index 238c2bc..639d10e7 100644
--- a/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.h
+++ b/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.h
@@ -25,7 +25,7 @@
     : public GpuBrowserCompositorOutputSurface {
  public:
   GpuSurfacelessBrowserCompositorOutputSurface(
-      scoped_refptr<ws::ContextProviderCommandBuffer> context,
+      scoped_refptr<viz::ContextProviderCommandBuffer> context,
       gpu::SurfaceHandle surface_handle,
       std::unique_ptr<viz::OverlayCandidateValidator>
           overlay_candidate_validator,
diff --git a/content/browser/compositor/offscreen_browser_compositor_output_surface.cc b/content/browser/compositor/offscreen_browser_compositor_output_surface.cc
index c38198d..befb6bd 100644
--- a/content/browser/compositor/offscreen_browser_compositor_output_surface.cc
+++ b/content/browser/compositor/offscreen_browser_compositor_output_surface.cc
@@ -19,7 +19,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "gpu/command_buffer/client/context_support.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
-#include "services/ws/public/cpp/gpu/context_provider_command_buffer.h"
+#include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
 #include "third_party/khronos/GLES2/gl2.h"
 #include "third_party/khronos/GLES2/gl2ext.h"
 
@@ -31,7 +31,7 @@
 
 OffscreenBrowserCompositorOutputSurface::
     OffscreenBrowserCompositorOutputSurface(
-        scoped_refptr<ws::ContextProviderCommandBuffer> context,
+        scoped_refptr<viz::ContextProviderCommandBuffer> context,
         std::unique_ptr<viz::OverlayCandidateValidator>
             overlay_candidate_validator)
     : BrowserCompositorOutputSurface(std::move(context),
diff --git a/content/browser/compositor/offscreen_browser_compositor_output_surface.h b/content/browser/compositor/offscreen_browser_compositor_output_surface.h
index 9756ca6..29fdcaa 100644
--- a/content/browser/compositor/offscreen_browser_compositor_output_surface.h
+++ b/content/browser/compositor/offscreen_browser_compositor_output_surface.h
@@ -17,7 +17,7 @@
 #include "ui/latency/latency_info.h"
 #include "ui/latency/latency_tracker.h"
 
-namespace ws {
+namespace viz {
 class ContextProviderCommandBuffer;
 }
 
@@ -28,7 +28,7 @@
     : public BrowserCompositorOutputSurface {
  public:
   OffscreenBrowserCompositorOutputSurface(
-      scoped_refptr<ws::ContextProviderCommandBuffer> context,
+      scoped_refptr<viz::ContextProviderCommandBuffer> context,
       std::unique_ptr<viz::OverlayCandidateValidator>
           overlay_candidate_validator);
 
diff --git a/content/browser/compositor/reflector_texture.cc b/content/browser/compositor/reflector_texture.cc
index cc6de8b..05ffc31 100644
--- a/content/browser/compositor/reflector_texture.cc
+++ b/content/browser/compositor/reflector_texture.cc
@@ -7,7 +7,7 @@
 #include "content/browser/compositor/owned_mailbox.h"
 #include "gpu/command_buffer/client/context_support.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
-#include "services/ws/public/cpp/gpu/context_provider_command_buffer.h"
+#include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
 
 namespace content {
 
diff --git a/content/browser/compositor/viz_process_transport_factory.cc b/content/browser/compositor/viz_process_transport_factory.cc
index dc46b51..ddccbc3d 100644
--- a/content/browser/compositor/viz_process_transport_factory.cc
+++ b/content/browser/compositor/viz_process_transport_factory.cc
@@ -35,7 +35,7 @@
 #include "gpu/command_buffer/client/gles2_interface.h"
 #include "gpu/command_buffer/client/raster_interface.h"
 #include "gpu/ipc/client/gpu_channel_host.h"
-#include "services/ws/public/cpp/gpu/context_provider_command_buffer.h"
+#include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/compositor/reflector.h"
 
@@ -50,7 +50,7 @@
 // child process client id.
 constexpr uint32_t kBrowserClientId = 0u;
 
-scoped_refptr<ws::ContextProviderCommandBuffer> CreateContextProviderImpl(
+scoped_refptr<viz::ContextProviderCommandBuffer> CreateContextProviderImpl(
     scoped_refptr<gpu::GpuChannelHost> gpu_channel_host,
     gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
     bool support_locking,
@@ -58,7 +58,7 @@
     bool support_raster_interface,
     bool support_grcontext,
     bool support_oop_rasterization,
-    ws::command_buffer_metrics::ContextType type) {
+    viz::command_buffer_metrics::ContextType type) {
   constexpr bool kAutomaticFlushes = false;
 
   gpu::ContextCreationAttribs attributes;
@@ -78,7 +78,7 @@
       gpu::SharedMemoryLimits::ForDisplayCompositor();
 
   GURL url("chrome://gpu/VizProcessTransportFactory::CreateContextProvider");
-  return base::MakeRefCounted<ws::ContextProviderCommandBuffer>(
+  return base::MakeRefCounted<viz::ContextProviderCommandBuffer>(
       std::move(gpu_channel_host), gpu_memory_buffer_manager,
       kGpuStreamIdDefault, kGpuStreamPriorityUI, gpu::kNullSurfaceHandle,
       std::move(url), kAutomaticFlushes, support_locking, support_grcontext,
@@ -450,7 +450,7 @@
         kSharedWorkerContextSupportsLocking, kSharedWorkerContextSupportsGLES2,
         kSharedWorkerContextSupportsRaster,
         kSharedWorkerContextSupportsGrContext, kSharedWorkerContextSupportsOOPR,
-        ws::command_buffer_metrics::ContextType::BROWSER_WORKER);
+        viz::command_buffer_metrics::ContextType::BROWSER_WORKER);
 
     // Don't observer context loss on |worker_context_provider_| here, that is
     // already observered by LayerTreeFrameSink. The lost context will be caught
@@ -479,7 +479,7 @@
         kCompositorContextSupportsLocking, kCompositorContextSupportsGLES2,
         kCompositorContextSupportsRaster, kCompositorContextSupportsGrContext,
         kCompositorContextSupportsOOPR,
-        ws::command_buffer_metrics::ContextType::BROWSER_MAIN_THREAD);
+        viz::command_buffer_metrics::ContextType::BROWSER_MAIN_THREAD);
     main_context_provider_->SetDefaultTaskRunner(
         context_factory_private_.resize_task_runner());
 
diff --git a/content/browser/compositor/viz_process_transport_factory.h b/content/browser/compositor/viz_process_transport_factory.h
index 4447840..1b96114 100644
--- a/content/browser/compositor/viz_process_transport_factory.h
+++ b/content/browser/compositor/viz_process_transport_factory.h
@@ -36,7 +36,7 @@
 class RasterContextProvider;
 }
 
-namespace ws {
+namespace viz {
 class ContextProviderCommandBuffer;
 }
 
@@ -125,7 +125,7 @@
 
   // ContextProvider used on the main thread. Shared by ui::Compositors and also
   // returned from GetSharedMainThreadContextProvider().
-  scoped_refptr<ws::ContextProviderCommandBuffer> main_context_provider_;
+  scoped_refptr<viz::ContextProviderCommandBuffer> main_context_provider_;
 
   std::unique_ptr<cc::SingleThreadTaskGraphRunner> task_graph_runner_;
 
diff --git a/content/browser/devtools/devtools_agent_host_impl.cc b/content/browser/devtools/devtools_agent_host_impl.cc
index e2678a5db..cc914d2 100644
--- a/content/browser/devtools/devtools_agent_host_impl.cc
+++ b/content/browser/devtools/devtools_agent_host_impl.cc
@@ -9,9 +9,11 @@
 
 #include "base/bind.h"
 #include "base/lazy_instance.h"
+#include "base/memory/ref_counted_memory.h"
 #include "base/observer_list.h"
 #include "base/stl_util.h"
 #include "content/browser/devtools/devtools_manager.h"
+#include "content/browser/devtools/devtools_stream_file.h"
 #include "content/browser/devtools/forwarding_agent_host.h"
 #include "content/browser/devtools/protocol/page.h"
 #include "content/browser/devtools/protocol/security_handler.h"
@@ -199,6 +201,15 @@
   return id_;
 }
 
+std::string DevToolsAgentHostImpl::CreateIOStreamFromData(
+    scoped_refptr<base::RefCountedMemory> data) {
+  scoped_refptr<DevToolsStreamFile> stream =
+      DevToolsStreamFile::Create(GetIOContext(), true /* binary */);
+  std::string text(reinterpret_cast<const char*>(data->front()), data->size());
+  stream->Append(std::make_unique<std::string>(text));
+  return stream->handle();
+}
+
 std::string DevToolsAgentHostImpl::GetParentId() {
   return std::string();
 }
diff --git a/content/browser/devtools/devtools_agent_host_impl.h b/content/browser/devtools/devtools_agent_host_impl.h
index 6881212..51826f6 100644
--- a/content/browser/devtools/devtools_agent_host_impl.h
+++ b/content/browser/devtools/devtools_agent_host_impl.h
@@ -36,6 +36,8 @@
   bool IsAttached() override;
   void InspectElement(RenderFrameHost* frame_host, int x, int y) override;
   std::string GetId() override;
+  std::string CreateIOStreamFromData(
+      scoped_refptr<base::RefCountedMemory> data) override;
   std::string GetParentId() override;
   std::string GetOpenerId() override;
   std::string GetDescription() override;
diff --git a/content/browser/devtools/protocol/page_handler.cc b/content/browser/devtools/protocol/page_handler.cc
index a1898c0..ba6a36e 100644
--- a/content/browser/devtools/protocol/page_handler.cc
+++ b/content/browser/devtools/protocol/page_handler.cc
@@ -768,6 +768,7 @@
                              Maybe<String> header_template,
                              Maybe<String> footer_template,
                              Maybe<bool> prefer_css_page_size,
+                             Maybe<String> transfer_mode,
                              std::unique_ptr<PrintToPDFCallback> callback) {
   callback->sendFailure(Response::Error("PrintToPDF is not implemented"));
   return;
diff --git a/content/browser/devtools/protocol/page_handler.h b/content/browser/devtools/protocol/page_handler.h
index 8d22209f..677f479 100644
--- a/content/browser/devtools/protocol/page_handler.h
+++ b/content/browser/devtools/protocol/page_handler.h
@@ -138,6 +138,7 @@
                   Maybe<String> header_template,
                   Maybe<String> footer_template,
                   Maybe<bool> prefer_css_page_size,
+                  Maybe<String> transfer_mode,
                   std::unique_ptr<PrintToPDFCallback> callback) override;
   Response StartScreencast(Maybe<std::string> format,
                            Maybe<int> quality,
diff --git a/content/browser/dom_storage/session_storage_namespace_impl_mojo_unittest.cc b/content/browser/dom_storage/session_storage_namespace_impl_mojo_unittest.cc
index 7202610..1cd2568 100644
--- a/content/browser/dom_storage/session_storage_namespace_impl_mojo_unittest.cc
+++ b/content/browser/dom_storage/session_storage_namespace_impl_mojo_unittest.cc
@@ -77,7 +77,8 @@
     security_policy->Add(kTestProcessIdAllOrigins, &browser_context_);
     security_policy->Add(kTestProcessIdOrigin3, &browser_context_);
     security_policy->AddIsolatedOrigins(
-        {test_origin1_, test_origin2_, test_origin3_});
+        {test_origin1_, test_origin2_, test_origin3_},
+        ChildProcessSecurityPolicy::IsolatedOriginSource::TEST);
     security_policy->LockToOrigin(IsolationContext(&browser_context_),
                                   kTestProcessIdOrigin1,
                                   test_origin1_.GetURL());
diff --git a/content/browser/frame_host/navigator_impl_unittest.cc b/content/browser/frame_host/navigator_impl_unittest.cc
index 0c79b97..0a1b166 100644
--- a/content/browser/frame_host/navigator_impl_unittest.cc
+++ b/content/browser/frame_host/navigator_impl_unittest.cc
@@ -994,9 +994,8 @@
   // Isolate one of the sites so the both can't be mapped to the default
   // site instance.
   ChildProcessSecurityPolicy::GetInstance()->AddIsolatedOrigins(
-      {
-          url::Origin::Create(kUrl1),
-      },
+      {url::Origin::Create(kUrl1)},
+      ChildProcessSecurityPolicy::IsolatedOriginSource::TEST,
       browser_context());
   contents()->NavigateAndCommit(kUrl1);
   SiteInstance* current_instance = main_test_rfh()->GetSiteInstance();
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index c7245425..248c304 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -4756,6 +4756,21 @@
   const bool is_same_document =
       FrameMsg_Navigate_Type::IsSameDocument(common_params.navigation_type);
 
+  // Network isolation key should be filled before the URLLoaderFactory for
+  // sub-resources is created. Only update for cross document navigations since
+  // for opaque origin same document navigations, a new origin should not be
+  // created as that would be different from the original.
+  // TODO(crbug.com/971796): For about:blank and other such urls,
+  // common_params.url currently leads to an opaque origin to be created. Once
+  // this is fixed, the origin which these navigations eventually get committed
+  // to will be available here as well.
+  if (!is_same_document) {
+    base::Optional<url::Origin> top_frame_origin =
+        frame_tree_node_->IsMainFrame() ? url::Origin::Create(common_params.url)
+                                        : frame_tree_->root()->current_origin();
+    network_isolation_key_ = net::NetworkIsolationKey(top_frame_origin);
+  }
+
   std::unique_ptr<blink::URLLoaderFactoryBundleInfo>
       subresource_loader_factories;
   if (base::FeatureList::IsEnabled(network::features::kNetworkService) &&
@@ -5658,11 +5673,13 @@
 
   // Create the URLLoaderFactory - either via ContentBrowserClient or ourselves.
   if (GetCreateNetworkFactoryCallbackForRenderFrame().is_null()) {
-    GetProcess()->CreateURLLoaderFactory(origin, std::move(header_client),
+    GetProcess()->CreateURLLoaderFactory(origin, network_isolation_key_,
+                                         std::move(header_client),
                                          std::move(default_factory_request));
   } else {
     network::mojom::URLLoaderFactoryPtr original_factory;
-    GetProcess()->CreateURLLoaderFactory(origin, std::move(header_client),
+    GetProcess()->CreateURLLoaderFactory(origin, network_isolation_key_,
+                                         std::move(header_client),
                                          mojo::MakeRequest(&original_factory));
     GetCreateNetworkFactoryCallbackForRenderFrame().Run(
         std::move(default_factory_request), GetProcess()->GetID(),
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index 75ddbef..59863c3b 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -58,6 +58,7 @@
 #include "media/mojo/interfaces/interface_factory.mojom.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "mojo/public/cpp/system/data_pipe.h"
+#include "net/base/network_isolation_key.h"
 #include "net/http/http_response_headers.h"
 #include "services/device/public/mojom/wake_lock_context.mojom.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
@@ -1006,6 +1007,7 @@
   friend class RenderFrameHostFeaturePolicyTest;
   friend class TestRenderFrameHost;
   friend class TestRenderViewHost;
+  friend class WebContentsSplitCacheBrowserTest;
 
   FRIEND_TEST_ALL_PREFIXES(NavigatorTest, TwoNavigationsRacingCommit);
   FRIEND_TEST_ALL_PREFIXES(RenderFrameHostImplBeforeUnloadBrowserTest,
@@ -2141,6 +2143,11 @@
   scoped_refptr<PrefetchedSignedExchangeCache>
       prefetched_signed_exchange_cache_;
 
+  // Network isolation key to be used for subresources from the currently
+  // committed navigation. This is specific to a document and should be reset on
+  // every document commit.
+  net::NetworkIsolationKey network_isolation_key_;
+
   // NOTE: This must be the last member.
   base::WeakPtrFactory<RenderFrameHostImpl> weak_ptr_factory_;
 
diff --git a/content/browser/gpu/gpu_client.cc b/content/browser/gpu/gpu_client.cc
index abd2475..68416a08 100644
--- a/content/browser/gpu/gpu_client.cc
+++ b/content/browser/gpu/gpu_client.cc
@@ -10,7 +10,7 @@
 namespace content {
 
 std::unique_ptr<viz::GpuClient, base::OnTaskRunnerDeleter> CreateGpuClient(
-    ws::mojom::GpuRequest request,
+    viz::mojom::GpuRequest request,
     viz::GpuClient::ConnectionErrorHandlerClosure connection_error_handler,
     scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
   const int client_id = ChildProcessHostImpl::GenerateChildProcessUniqueId();
diff --git a/content/browser/gpu/gpu_ipc_browsertests.cc b/content/browser/gpu/gpu_ipc_browsertests.cc
index e1af590..6ff8b04 100644
--- a/content/browser/gpu/gpu_ipc_browsertests.cc
+++ b/content/browser/gpu/gpu_ipc_browsertests.cc
@@ -20,7 +20,7 @@
 #include "gpu/ipc/client/command_buffer_proxy_impl.h"
 #include "gpu/ipc/client/gpu_channel_host.h"
 #include "services/viz/privileged/interfaces/gl/gpu_service.mojom.h"
-#include "services/ws/public/cpp/gpu/context_provider_command_buffer.h"
+#include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkPaint.h"
 #include "third_party/skia/include/core/SkSurface.h"
@@ -83,7 +83,7 @@
   gpu::ContextSupport* context_support_ = nullptr;
 
  private:
-  scoped_refptr<ws::ContextProviderCommandBuffer> provider_;
+  scoped_refptr<viz::ContextProviderCommandBuffer> provider_;
 };
 
 }  // namespace
@@ -191,7 +191,7 @@
                        MAYBE_GrContextKeepsGpuChannelAlive) {
   // Test for crbug.com/551143
   // This test verifies that holding a reference to the GrContext created by
-  // a ws::ContextProviderCommandBuffer will keep the gpu channel alive after
+  // a viz::ContextProviderCommandBuffer will keep the gpu channel alive after
   // the
   // provider has been destroyed. Without this behavior, user code would have
   // to be careful to destroy objects in the right order to avoid using freed
@@ -201,7 +201,7 @@
 
   // Step 2: verify that holding onto the provider's GrContext will
   // retain the host after provider is destroyed.
-  scoped_refptr<ws::ContextProviderCommandBuffer> provider =
+  scoped_refptr<viz::ContextProviderCommandBuffer> provider =
       content::GpuBrowsertestCreateContext(GetGpuChannel());
   ASSERT_EQ(provider->BindToCurrentThread(), gpu::ContextResult::kSuccess);
 
@@ -248,7 +248,7 @@
   EstablishAndWait();
   scoped_refptr<gpu::GpuChannelHost> host = GetGpuChannel();
 
-  scoped_refptr<ws::ContextProviderCommandBuffer> provider =
+  scoped_refptr<viz::ContextProviderCommandBuffer> provider =
       content::GpuBrowsertestCreateContext(GetGpuChannel());
   ContextLostRunLoop run_loop(provider.get());
   ASSERT_EQ(provider->BindToCurrentThread(), gpu::ContextResult::kSuccess);
diff --git a/content/browser/gpu_interface_provider.cc b/content/browser/gpu_interface_provider.cc
deleted file mode 100644
index f2f5e382..0000000
--- a/content/browser/gpu_interface_provider.cc
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/gpu_interface_provider.h"
-
-#include "base/bind.h"
-#include "base/containers/unique_ptr_adapters.h"
-#include "base/memory/ref_counted.h"
-#include "base/task/post_task.h"
-#include "components/discardable_memory/public/interfaces/discardable_shared_memory_manager.mojom.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/gpu_client.h"
-#include "content/public/browser/gpu_interface_provider_factory.h"
-#include "content/public/browser/gpu_service_registry.h"
-#include "services/ws/public/mojom/gpu.mojom.h"
-
-namespace content {
-
-// InterfaceBinderImpl handles the actual binding. The binding has to happen on
-// the IO thread.
-class GpuInterfaceProvider::InterfaceBinderImpl
-    : public base::RefCountedThreadSafe<InterfaceBinderImpl> {
- public:
-  InterfaceBinderImpl() = default;
-
-  void BindGpuRequestOnGpuTaskRunner(ws::mojom::GpuRequest request) {
-    // The GPU task runner is bound to the IO thread.
-    DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-    auto gpu_client = content::CreateGpuClient(
-        std::move(request),
-        base::BindOnce(&InterfaceBinderImpl::OnGpuClientConnectionError, this),
-        base::CreateSingleThreadTaskRunnerWithTraits(
-            {content::BrowserThread::IO}));
-    gpu_clients_.push_back(std::move(gpu_client));
-  }
-
-  void BindDiscardableSharedMemoryManagerOnGpuTaskRunner(
-      discardable_memory::mojom::DiscardableSharedMemoryManagerRequest
-          request) {
-    content::BindInterfaceInGpuProcess(std::move(request));
-  }
-
- private:
-  friend class base::RefCountedThreadSafe<InterfaceBinderImpl>;
-  ~InterfaceBinderImpl() = default;
-
-  void OnGpuClientConnectionError(viz::GpuClient* client) {
-    base::EraseIf(
-        gpu_clients_,
-        base::UniquePtrMatcher<viz::GpuClient, base::OnTaskRunnerDeleter>(
-            client));
-  }
-
-  std::vector<std::unique_ptr<viz::GpuClient, base::OnTaskRunnerDeleter>>
-      gpu_clients_;
-
-  DISALLOW_COPY_AND_ASSIGN(InterfaceBinderImpl);
-};
-
-GpuInterfaceProvider::GpuInterfaceProvider()
-    : interface_binder_impl_(base::MakeRefCounted<InterfaceBinderImpl>()) {}
-
-GpuInterfaceProvider::~GpuInterfaceProvider() = default;
-
-void GpuInterfaceProvider::RegisterGpuInterfaces(
-    service_manager::BinderRegistry* registry) {
-  scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner =
-      base::CreateSingleThreadTaskRunnerWithTraits(
-          {content::BrowserThread::IO});
-  registry->AddInterface(
-      base::BindRepeating(&InterfaceBinderImpl::
-                              BindDiscardableSharedMemoryManagerOnGpuTaskRunner,
-                          interface_binder_impl_),
-      gpu_task_runner);
-  registry->AddInterface(
-      base::BindRepeating(&InterfaceBinderImpl::BindGpuRequestOnGpuTaskRunner,
-                          interface_binder_impl_),
-      gpu_task_runner);
-}
-
-#if defined(USE_OZONE)
-void GpuInterfaceProvider::BindOzoneGpuInterface(
-    const std::string& interface_name,
-    mojo::ScopedMessagePipeHandle handle) {
-  // This function is only used in multi-process mash with in-process viz to
-  // bind gpu-related ozone interfaces. It should not be invoked here.
-  NOTREACHED();
-}
-#endif
-
-// Factory.
-std::unique_ptr<ws::GpuInterfaceProvider> CreateGpuInterfaceProvider() {
-  return std::make_unique<GpuInterfaceProvider>();
-}
-
-}  // namespace content
diff --git a/content/browser/gpu_interface_provider.h b/content/browser/gpu_interface_provider.h
deleted file mode 100644
index 6d958bcc0..0000000
--- a/content/browser/gpu_interface_provider.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_GPU_INTERFACE_PROVIDER_H_
-#define CONTENT_BROWSER_GPU_INTERFACE_PROVIDER_H_
-
-#include "base/macros.h"
-#include "base/memory/scoped_refptr.h"
-#include "content/common/content_export.h"
-#include "services/ws/public/cpp/host/gpu_interface_provider.h"
-
-namespace content {
-
-// An implementation of GpuInterfaceProvider that forwards to the Gpu
-// implementation in content.
-class CONTENT_EXPORT GpuInterfaceProvider : public ws::GpuInterfaceProvider {
- public:
-  GpuInterfaceProvider();
-  ~GpuInterfaceProvider() override;
-
-  // ws::GpuInterfaceProvider:
-  void RegisterGpuInterfaces(
-      service_manager::BinderRegistry* registry) override;
-#if defined(USE_OZONE)
-  void BindOzoneGpuInterface(const std::string& interface_name,
-                             mojo::ScopedMessagePipeHandle handle) override;
-#endif
-
- private:
-  class InterfaceBinderImpl;
-
-  scoped_refptr<InterfaceBinderImpl> interface_binder_impl_;
-
-  DISALLOW_COPY_AND_ASSIGN(GpuInterfaceProvider);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_GPU_INTERFACE_PROVIDER_H_
diff --git a/content/browser/isolated_origin_browsertest.cc b/content/browser/isolated_origin_browsertest.cc
index 7d1d7c93..ed39b8e 100644
--- a/content/browser/isolated_origin_browsertest.cc
+++ b/content/browser/isolated_origin_browsertest.cc
@@ -42,6 +42,8 @@
 
 namespace content {
 
+using IsolatedOriginSource = ChildProcessSecurityPolicy::IsolatedOriginSource;
+
 // This is a base class for all tests in this class.  It does not isolate any
 // origins and only provides common helper functions to the other test classes.
 class IsolatedOriginTestBase : public ContentBrowserTest {
@@ -1686,7 +1688,8 @@
 
   // Start isolating foo.com.
   auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
-  policy->AddIsolatedOrigins({url::Origin::Create(foo_url)});
+  policy->AddIsolatedOrigins({url::Origin::Create(foo_url)},
+                             IsolatedOriginSource::TEST);
 
   // The isolation shouldn't take effect in the current frame tree, so that it
   // doesn't break same-site scripting.  Navigate iframe to a foo.com URL and
@@ -1774,7 +1777,8 @@
   // Start isolating bar.com.
   GURL bar_url(embedded_test_server()->GetURL("bar.com", "/title2.html"));
   auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
-  policy->AddIsolatedOrigins({url::Origin::Create(bar_url)});
+  policy->AddIsolatedOrigins({url::Origin::Create(bar_url)},
+                             IsolatedOriginSource::TEST);
 
   // Do a renderer-initiated navigation in each of the existing three windows.
   // None of them should swap to a new process, since bar.com shouldn't be
@@ -1847,7 +1851,8 @@
                         root->current_frame_host()->GetProcess()->GetID()));
 
   // Start isolating foo.com.
-  policy->AddIsolatedOrigins({url::Origin::Create(foo_url)});
+  policy->AddIsolatedOrigins({url::Origin::Create(foo_url)},
+                             IsolatedOriginSource::TEST);
 
   // Create an unrelated window, which will be in a new BrowsingInstance.
   // foo.com will become an isolated origin in that window.
@@ -1880,7 +1885,8 @@
             second_root->current_frame_host()->GetProcess()->GetID());
 
   // Now, start isolating sub.foo.com.
-  policy->AddIsolatedOrigins({url::Origin::Create(sub_foo_url)});
+  policy->AddIsolatedOrigins({url::Origin::Create(sub_foo_url)},
+                             IsolatedOriginSource::TEST);
 
   // Make sure the process locked to foo.com, which currently has sub.foo.com
   // committed in it, can still access sub.foo.com cookies.
@@ -1923,7 +1929,8 @@
   GURL sub_foo_url(
       embedded_test_server()->GetURL("sub.foo.com", "/title1.html"));
   auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
-  policy->AddIsolatedOrigins({url::Origin::Create(sub_foo_url)});
+  policy->AddIsolatedOrigins({url::Origin::Create(sub_foo_url)},
+                             IsolatedOriginSource::TEST);
 
   // Navigate to foo.com and then to sub.foo.com in a new BrowsingInstance.
   // foo.com and sub.foo.com should now be considered cross-site for the
@@ -1987,7 +1994,8 @@
 
   // Start isolating bar.com.
   auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
-  policy->AddIsolatedOrigins({url::Origin::Create(bar_url)});
+  policy->AddIsolatedOrigins({url::Origin::Create(bar_url)},
+                             IsolatedOriginSource::TEST);
 
   // Open a new window in a new BrowsingInstance.  Navigate to foo.com and
   // check that the old foo.com process is reused.
@@ -2060,7 +2068,8 @@
 
   // Start isolating foo.com.
   auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
-  policy->AddIsolatedOrigins({url::Origin::Create(foo_url)});
+  policy->AddIsolatedOrigins({url::Origin::Create(foo_url)},
+                             IsolatedOriginSource::TEST);
 
   // Create a new window, forcing a new BrowsingInstance, and navigate it to
   // foo.com, which will spin up a process locked to foo.com.
@@ -2107,7 +2116,8 @@
   GURL foo_url(
       embedded_test_server()->GetURL("foo.com", "/page_with_iframe.html"));
   auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
-  policy->AddIsolatedOrigins({url::Origin::Create(foo_url)}, other_context);
+  policy->AddIsolatedOrigins({url::Origin::Create(foo_url)},
+                             IsolatedOriginSource::TEST, other_context);
 
   // Verify that foo.com is indeed isolated in |other_shell|, by navigating to
   // it in a new BrowsingInstance and checking that a bar.com subframe becomes
@@ -2163,7 +2173,8 @@
 
   // Start isolating foo.com.
   BrowserContext* context = shell()->web_contents()->GetBrowserContext();
-  policy->AddIsolatedOrigins({url::Origin::Create(foo_url)}, context);
+  policy->AddIsolatedOrigins({url::Origin::Create(foo_url)},
+                             IsolatedOriginSource::TEST, context);
 
   // Try navigating to another foo URL.
   GURL foo2_url(embedded_test_server()->GetURL(
@@ -2214,7 +2225,8 @@
 
   // Start isolating foo.com.
   BrowserContext* context = shell()->web_contents()->GetBrowserContext();
-  policy->AddIsolatedOrigins({url::Origin::Create(foo_url)}, context);
+  policy->AddIsolatedOrigins({url::Origin::Create(foo_url)},
+                             IsolatedOriginSource::TEST, context);
 
   // Do a renderer-initiated navigation to another foo URL.
   GURL foo2_url(embedded_test_server()->GetURL(
@@ -2259,7 +2271,8 @@
   // Start isolating foo.com.
   BrowserContext* context = shell()->web_contents()->GetBrowserContext();
   auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
-  policy->AddIsolatedOrigins({url::Origin::Create(foo_url)}, context);
+  policy->AddIsolatedOrigins({url::Origin::Create(foo_url)},
+                             IsolatedOriginSource::TEST, context);
 
   // Open a popup.
   GURL popup_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
@@ -2303,7 +2316,8 @@
   // Start isolating foo.com.
   BrowserContext* context = shell()->web_contents()->GetBrowserContext();
   auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
-  policy->AddIsolatedOrigins({url::Origin::Create(foo_url)}, context);
+  policy->AddIsolatedOrigins({url::Origin::Create(foo_url)},
+                             IsolatedOriginSource::TEST, context);
 
   // Navigate the main frame to another foo URL.
   GURL foo2_url(embedded_test_server()->GetURL("foo.com", "/title2.html"));
diff --git a/content/browser/media/android/browser_gpu_video_accelerator_factories.cc b/content/browser/media/android/browser_gpu_video_accelerator_factories.cc
index 95063f7..063edef 100644
--- a/content/browser/media/android/browser_gpu_video_accelerator_factories.cc
+++ b/content/browser/media/android/browser_gpu_video_accelerator_factories.cc
@@ -14,7 +14,7 @@
 #include "gpu/ipc/client/gpu_channel_host.h"
 #include "media/gpu/gpu_video_accelerator_util.h"
 #include "media/gpu/ipc/common/media_messages.h"
-#include "services/ws/public/cpp/gpu/context_provider_command_buffer.h"
+#include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
 
 namespace content {
 
@@ -45,7 +45,7 @@
   constexpr bool support_grcontext = true;
 
   auto context_provider =
-      base::MakeRefCounted<ws::ContextProviderCommandBuffer>(
+      base::MakeRefCounted<viz::ContextProviderCommandBuffer>(
           std::move(gpu_channel_host), factory->GetGpuMemoryBufferManager(),
           stream_id, stream_priority, gpu::kNullSurfaceHandle,
           GURL(std::string("chrome://gpu/"
@@ -53,7 +53,7 @@
                            "CreateGpuVideoAcceleratorFactories")),
           automatic_flushes, support_locking, support_grcontext,
           gpu::SharedMemoryLimits::ForMailboxContext(), attributes,
-          ws::command_buffer_metrics::ContextType::UNKNOWN);
+          viz::command_buffer_metrics::ContextType::UNKNOWN);
 
   // TODO(xingliu): This is on main thread, move to another thread?
   context_provider->BindToCurrentThread();
@@ -74,7 +74,7 @@
 }
 
 BrowserGpuVideoAcceleratorFactories::BrowserGpuVideoAcceleratorFactories(
-    scoped_refptr<ws::ContextProviderCommandBuffer> context_provider)
+    scoped_refptr<viz::ContextProviderCommandBuffer> context_provider)
     : context_provider_(std::move(context_provider)) {}
 
 BrowserGpuVideoAcceleratorFactories::~BrowserGpuVideoAcceleratorFactories() =
@@ -197,7 +197,7 @@
   return media::VideoEncodeAccelerator::SupportedProfiles();
 }
 
-scoped_refptr<ws::ContextProviderCommandBuffer>
+scoped_refptr<viz::ContextProviderCommandBuffer>
 BrowserGpuVideoAcceleratorFactories::GetMediaContextProvider() {
   return context_provider_;
 }
diff --git a/content/browser/media/android/browser_gpu_video_accelerator_factories.h b/content/browser/media/android/browser_gpu_video_accelerator_factories.h
index 49f9b15e..b4c9a5c 100644
--- a/content/browser/media/android/browser_gpu_video_accelerator_factories.h
+++ b/content/browser/media/android/browser_gpu_video_accelerator_factories.h
@@ -16,7 +16,7 @@
     : public media::GpuVideoAcceleratorFactories {
  public:
   explicit BrowserGpuVideoAcceleratorFactories(
-      scoped_refptr<ws::ContextProviderCommandBuffer>);
+      scoped_refptr<viz::ContextProviderCommandBuffer>);
   ~BrowserGpuVideoAcceleratorFactories() override;
 
  private:
@@ -60,12 +60,12 @@
   scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner() override;
   media::VideoEncodeAccelerator::SupportedProfiles
   GetVideoEncodeAcceleratorSupportedProfiles() override;
-  scoped_refptr<ws::ContextProviderCommandBuffer> GetMediaContextProvider()
+  scoped_refptr<viz::ContextProviderCommandBuffer> GetMediaContextProvider()
       override;
   gpu::ContextSupport* GetMediaContextProviderContextSupport() override;
   void SetRenderingColorSpace(const gfx::ColorSpace& color_space) override;
 
-  scoped_refptr<ws::ContextProviderCommandBuffer> context_provider_;
+  scoped_refptr<viz::ContextProviderCommandBuffer> context_provider_;
   base::UnguessableToken channel_token_;
 
   DISALLOW_COPY_AND_ASSIGN(BrowserGpuVideoAcceleratorFactories);
diff --git a/content/browser/navigation_browsertest.cc b/content/browser/navigation_browsertest.cc
index e119d8b..7f766e0 100644
--- a/content/browser/navigation_browsertest.cc
+++ b/content/browser/navigation_browsertest.cc
@@ -7,6 +7,7 @@
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/files/scoped_temp_dir.h"
+#include "base/macros.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/post_task.h"
@@ -42,6 +43,7 @@
 #include "content/public/test/content_browser_test_utils.h"
 #include "content/public/test/download_test_observer.h"
 #include "content/public/test/navigation_handle_observer.h"
+#include "content/public/test/no_renderer_crashes_assertion.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "content/public/test/test_navigation_throttle.h"
 #include "content/public/test/test_navigation_throttle_inserter.h"
@@ -1906,6 +1908,10 @@
 
   GURL url_;
   WebContents* first_web_contents_;
+
+  ScopedAllowRendererCrashes scoped_allow_renderer_crashes_;
+
+  DISALLOW_COPY_AND_ASSIGN(CreateWebContentsOnCrashObserver);
 };
 
 // This test simulates android webview's behavior in apps that handle
diff --git a/content/browser/network_service_browsertest.cc b/content/browser/network_service_browsertest.cc
index 076a684a..90754d3 100644
--- a/content/browser/network_service_browsertest.cc
+++ b/content/browser/network_service_browsertest.cc
@@ -25,6 +25,7 @@
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_browser_test.h"
 #include "content/public/test/content_browser_test_utils.h"
+#include "content/public/test/no_renderer_crashes_assertion.h"
 #include "content/public/test/simple_url_loader_test_helper.h"
 #include "content/public/test/test_utils.h"
 #include "content/shell/browser/shell.h"
@@ -68,6 +69,7 @@
   }
 
  private:
+  ScopedAllowRendererCrashes scoped_allow_renderer_crashes_;
   bool killed_ = false;
 
   // Used to wait for the render process being killed. Android doesn't
diff --git a/content/browser/picture_in_picture/picture_in_picture_service_impl_unittest.cc b/content/browser/picture_in_picture/picture_in_picture_service_impl_unittest.cc
index 92fbc9c6..8651f9f 100644
--- a/content/browser/picture_in_picture/picture_in_picture_service_impl_unittest.cc
+++ b/content/browser/picture_in_picture/picture_in_picture_service_impl_unittest.cc
@@ -39,9 +39,9 @@
   PictureInPictureDelegate() = default;
 
   MOCK_METHOD3(EnterPictureInPicture,
-               gfx::Size(WebContents*,
-                         const viz::SurfaceId&,
-                         const gfx::Size&));
+               PictureInPictureResult(WebContents*,
+                                      const viz::SurfaceId&,
+                                      const gfx::Size&));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(PictureInPictureDelegate);
diff --git a/content/browser/picture_in_picture/picture_in_picture_session.cc b/content/browser/picture_in_picture/picture_in_picture_session.cc
index 0e6c05f..74a817c 100644
--- a/content/browser/picture_in_picture/picture_in_picture_session.cc
+++ b/content/browser/picture_in_picture/picture_in_picture_session.cc
@@ -7,6 +7,7 @@
 #include "content/browser/picture_in_picture/picture_in_picture_service_impl.h"
 #include "content/browser/picture_in_picture/picture_in_picture_window_controller_impl.h"
 #include "content/browser/web_contents/web_contents_impl.h"
+#include "content/public/browser/web_contents_delegate.h"
 
 namespace content {
 
@@ -33,12 +34,16 @@
   if (controller)
     controller->SetActiveSession(this);
 
-  *window_size = GetWebContentsImpl()->EnterPictureInPicture(surface_id.value(),
-                                                             natural_size);
+  // TODO(beccahughes): Pass PictureInPictureResult::kNotSupported back to
+  // Blink.
+  auto result = GetWebContentsImpl()->EnterPictureInPicture(surface_id.value(),
+                                                            natural_size);
+  DCHECK_EQ(PictureInPictureResult::kSuccess, result);
 
   if (controller) {
     controller->SetAlwaysHidePlayPauseButton(show_play_pause_button);
     controller->SetAlwaysHideMuteButton(show_mute_button);
+    *window_size = controller->GetSize();
   }
 }
 
diff --git a/content/browser/picture_in_picture/picture_in_picture_window_controller_impl.cc b/content/browser/picture_in_picture/picture_in_picture_window_controller_impl.cc
index 3a86ab1..0b609df 100644
--- a/content/browser/picture_in_picture/picture_in_picture_window_controller_impl.cc
+++ b/content/browser/picture_in_picture/picture_in_picture_window_controller_impl.cc
@@ -65,7 +65,7 @@
   DCHECK(window_) << "Picture in Picture requires a valid window.";
 }
 
-gfx::Size PictureInPictureWindowControllerImpl::Show() {
+void PictureInPictureWindowControllerImpl::Show() {
   DCHECK(window_);
   DCHECK(surface_id_.is_valid());
 
@@ -90,8 +90,6 @@
       media_session_action_previous_track_handled_);
   window_->ShowInactive();
   initiator_->SetHasPictureInPictureVideo(true);
-
-  return window_->GetBounds().size();
 }
 
 void PictureInPictureWindowControllerImpl::Close(bool should_pause_video) {
@@ -305,6 +303,10 @@
       media_session_action_previous_track_handled_);
 }
 
+gfx::Size PictureInPictureWindowControllerImpl::GetSize() {
+  return window_->GetBounds().size();
+}
+
 void PictureInPictureWindowControllerImpl::MediaStartedPlaying(
     const MediaPlayerInfo&,
     const MediaPlayerId& media_player_id) {
diff --git a/content/browser/picture_in_picture/picture_in_picture_window_controller_impl.h b/content/browser/picture_in_picture/picture_in_picture_window_controller_impl.h
index 166dfd74..7ecd12ff 100644
--- a/content/browser/picture_in_picture/picture_in_picture_window_controller_impl.h
+++ b/content/browser/picture_in_picture/picture_in_picture_window_controller_impl.h
@@ -46,7 +46,7 @@
   using MutedMediaPlayerMap = std::map<RenderFrameHost*, PlayerSet>;
 
   // PictureInPictureWindowController:
-  CONTENT_EXPORT gfx::Size Show() override;
+  CONTENT_EXPORT void Show() override;
   CONTENT_EXPORT void Close(bool should_pause_video) override;
   CONTENT_EXPORT void CloseAndFocusInitiator() override;
   CONTENT_EXPORT void OnWindowDestroyed() override;
@@ -71,6 +71,8 @@
   CONTENT_EXPORT void MediaSessionActionsChanged(
       const std::set<media_session::mojom::MediaSessionAction>& actions);
 
+  gfx::Size GetSize();
+
   // WebContentsObserver:
   void MediaStartedPlaying(const MediaPlayerInfo&,
                            const MediaPlayerId&) override;
diff --git a/content/browser/process_internals/process_internals_handler_impl.cc b/content/browser/process_internals/process_internals_handler_impl.cc
index 0e38a96..98cdc589 100644
--- a/content/browser/process_internals/process_internals_handler_impl.cc
+++ b/content/browser/process_internals/process_internals_handler_impl.cc
@@ -81,7 +81,8 @@
 
 void ProcessInternalsHandlerImpl::GetIsolatedOriginsSize(
     GetIsolatedOriginsSizeCallback callback) {
-  size_t size = SiteIsolationPolicy::GetIsolatedOrigins().size();
+  auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
+  size_t size = policy->GetIsolatedOrigins().size();
   std::move(callback).Run(size);
 }
 
diff --git a/content/browser/renderer_host/compositor_impl_android.cc b/content/browser/renderer_host/compositor_impl_android.cc
index b21ad69..1b0cc815 100644
--- a/content/browser/renderer_host/compositor_impl_android.cc
+++ b/content/browser/renderer_host/compositor_impl_android.cc
@@ -73,8 +73,8 @@
 #include "gpu/ipc/client/command_buffer_proxy_impl.h"
 #include "gpu/ipc/client/gpu_channel_host.h"
 #include "gpu/ipc/common/gpu_surface_tracker.h"
+#include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
 #include "services/viz/public/interfaces/compositing/compositor_frame_sink.mojom.h"
-#include "services/ws/public/cpp/gpu/context_provider_command_buffer.h"
 #include "third_party/khronos/GLES2/gl2.h"
 #include "third_party/khronos/GLES2/gl2ext.h"
 #include "third_party/skia/include/core/SkMallocPixelRef.h"
@@ -203,20 +203,20 @@
   constexpr bool support_grcontext = false;
 
   auto context_provider =
-      base::MakeRefCounted<ws::ContextProviderCommandBuffer>(
+      base::MakeRefCounted<viz::ContextProviderCommandBuffer>(
           std::move(gpu_channel_host), factory->GetGpuMemoryBufferManager(),
           stream_id, stream_priority, handle,
           GURL(std::string("chrome://gpu/Compositor::CreateContextProvider")),
           automatic_flushes, support_locking, support_grcontext,
           shared_memory_limits, attributes,
-          ws::command_buffer_metrics::ContextType::UNKNOWN);
+          viz::command_buffer_metrics::ContextType::UNKNOWN);
   callback.Run(std::move(context_provider));
 }
 
 class AndroidOutputSurface : public viz::OutputSurface {
  public:
   AndroidOutputSurface(
-      scoped_refptr<ws::ContextProviderCommandBuffer> context_provider,
+      scoped_refptr<viz::ContextProviderCommandBuffer> context_provider,
       base::RepeatingCallback<void(const gfx::Size&)> swap_buffers_callback)
       : viz::OutputSurface(std::move(context_provider)),
         swap_buffers_callback_(std::move(swap_buffers_callback)),
@@ -290,7 +290,7 @@
 
   uint32_t GetFramebufferCopyTextureFormat() override {
     auto* gl =
-        static_cast<ws::ContextProviderCommandBuffer*>(context_provider());
+        static_cast<viz::ContextProviderCommandBuffer*>(context_provider());
     return gl->GetCopyTextureInternalFormat();
   }
 
@@ -306,8 +306,9 @@
 
  private:
   gpu::CommandBufferProxyImpl* GetCommandBufferProxy() {
-    ws::ContextProviderCommandBuffer* provider_command_buffer =
-        static_cast<ws::ContextProviderCommandBuffer*>(context_provider_.get());
+    viz::ContextProviderCommandBuffer* provider_command_buffer =
+        static_cast<viz::ContextProviderCommandBuffer*>(
+            context_provider_.get());
     gpu::CommandBufferProxyImpl* command_buffer_proxy =
         provider_command_buffer->GetCommandBufferProxy();
     DCHECK(command_buffer_proxy);
@@ -786,7 +787,7 @@
   gpu::SurfaceHandle surface_handle =
       enable_viz_ ? gpu::kNullSurfaceHandle : surface_handle_;
   auto context_provider =
-      base::MakeRefCounted<ws::ContextProviderCommandBuffer>(
+      base::MakeRefCounted<viz::ContextProviderCommandBuffer>(
           std::move(gpu_channel_host), factory->GetGpuMemoryBufferManager(),
           stream_id, stream_priority, surface_handle,
           GURL(std::string("chrome://gpu/CompositorImpl::") +
@@ -795,7 +796,7 @@
           GetCompositorContextSharedMemoryLimits(root_window_),
           GetCompositorContextAttributes(display_color_space_,
                                          requires_alpha_channel_),
-          ws::command_buffer_metrics::ContextType::BROWSER_COMPOSITOR);
+          viz::command_buffer_metrics::ContextType::BROWSER_COMPOSITOR);
   auto result = context_provider->BindToCurrentThread();
   if (result != gpu::ContextResult::kSuccess) {
     if (gpu::IsFatalOrSurfaceFailure(result))
@@ -1023,7 +1024,7 @@
 }
 
 void CompositorImpl::InitializeVizLayerTreeFrameSink(
-    scoped_refptr<ws::ContextProviderCommandBuffer> context_provider) {
+    scoped_refptr<viz::ContextProviderCommandBuffer> context_provider) {
   DCHECK(enable_viz_);
   DCHECK(root_window_);
 
diff --git a/content/browser/renderer_host/compositor_impl_android.h b/content/browser/renderer_host/compositor_impl_android.h
index 765c73f..f9937fd 100644
--- a/content/browser/renderer_host/compositor_impl_android.h
+++ b/content/browser/renderer_host/compositor_impl_android.h
@@ -30,7 +30,7 @@
 #include "gpu/command_buffer/common/capabilities.h"
 #include "gpu/ipc/common/surface_handle.h"
 #include "services/viz/privileged/interfaces/compositing/display_private.mojom.h"
-#include "services/ws/public/cpp/gpu/context_provider_command_buffer.h"
+#include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
 #include "third_party/khronos/GLES2/gl2.h"
 #include "ui/android/resources/resource_manager_impl.h"
 #include "ui/android/resources/ui_resource_provider.h"
@@ -209,7 +209,7 @@
 
   // Viz specific functions:
   void InitializeVizLayerTreeFrameSink(
-      scoped_refptr<ws::ContextProviderCommandBuffer> context_provider);
+      scoped_refptr<viz::ContextProviderCommandBuffer> context_provider);
 
   viz::FrameSinkId frame_sink_id_;
 
diff --git a/content/browser/renderer_host/delegated_frame_host.cc b/content/browser/renderer_host/delegated_frame_host.cc
index 3a5efa97..51d3755 100644
--- a/content/browser/renderer_host/delegated_frame_host.cc
+++ b/content/browser/renderer_host/delegated_frame_host.cc
@@ -367,9 +367,9 @@
 
 void DelegatedFrameHost::OnBeginFrame(
     const viz::BeginFrameArgs& args,
-    const viz::PresentationFeedbackMap& feedbacks) {
+    const viz::FrameTimingDetailsMap& timing_details) {
   if (renderer_compositor_frame_sink_)
-    renderer_compositor_frame_sink_->OnBeginFrame(args, feedbacks);
+    renderer_compositor_frame_sink_->OnBeginFrame(args, timing_details);
   client_->OnBeginFrame(args.frame_time);
 }
 
diff --git a/content/browser/renderer_host/delegated_frame_host.h b/content/browser/renderer_host/delegated_frame_host.h
index 3e69e97..a55a1b22 100644
--- a/content/browser/renderer_host/delegated_frame_host.h
+++ b/content/browser/renderer_host/delegated_frame_host.h
@@ -15,7 +15,7 @@
 #include "components/viz/client/frame_evictor.h"
 #include "components/viz/common/frame_sinks/begin_frame_args.h"
 #include "components/viz/common/frame_sinks/begin_frame_source.h"
-#include "components/viz/common/presentation_feedback_map.h"
+#include "components/viz/common/frame_timing_details_map.h"
 #include "components/viz/host/hit_test/hit_test_query.h"
 #include "components/viz/host/host_frame_sink_client.h"
 #include "components/viz/host/host_frame_sink_manager.h"
@@ -90,7 +90,7 @@
   void DidReceiveCompositorFrameAck(
       const std::vector<viz::ReturnedResource>& resources) override;
   void OnBeginFrame(const viz::BeginFrameArgs& args,
-                    const viz::PresentationFeedbackMap& feedbacks) override;
+                    const viz::FrameTimingDetailsMap& timing_details) override;
   void ReclaimResources(
       const std::vector<viz::ReturnedResource>& resources) override;
   void OnBeginFramePausedChanged(bool paused) override;
diff --git a/content/browser/renderer_host/delegated_frame_host_client_android.cc b/content/browser/renderer_host/delegated_frame_host_client_android.cc
index 05a2ea8..ed5e6a7 100644
--- a/content/browser/renderer_host/delegated_frame_host_client_android.cc
+++ b/content/browser/renderer_host/delegated_frame_host_client_android.cc
@@ -26,8 +26,8 @@
 }
 
 void DelegatedFrameHostClientAndroid::DidPresentCompositorFrames(
-    const viz::PresentationFeedbackMap& feedbacks) {
-  render_widget_host_view_->DidPresentCompositorFrames(feedbacks);
+    const viz::FrameTimingDetailsMap& timing_details) {
+  render_widget_host_view_->DidPresentCompositorFrames(timing_details);
 }
 
 void DelegatedFrameHostClientAndroid::ReclaimResources(
diff --git a/content/browser/renderer_host/delegated_frame_host_client_android.h b/content/browser/renderer_host/delegated_frame_host_client_android.h
index e0d7968a..823c313 100644
--- a/content/browser/renderer_host/delegated_frame_host_client_android.h
+++ b/content/browser/renderer_host/delegated_frame_host_client_android.h
@@ -6,7 +6,7 @@
 #define CONTENT_BROWSER_RENDERER_HOST_DELEGATED_FRAME_HOST_CLIENT_ANDROID_H_
 
 #include "base/macros.h"
-#include "components/viz/common/presentation_feedback_map.h"
+#include "components/viz/common/frame_timing_details_map.h"
 #include "content/common/content_export.h"
 #include "ui/android/delegated_frame_host_android.h"
 
@@ -25,7 +25,7 @@
   // DelegatedFrameHostAndroid::Client implementation.
   void SetBeginFrameSource(viz::BeginFrameSource* begin_frame_source) override;
   void DidPresentCompositorFrames(
-      const viz::PresentationFeedbackMap& feedbacks) override;
+      const viz::FrameTimingDetailsMap& timing_details) override;
   void DidReceiveCompositorFrameAck(
       const std::vector<viz::ReturnedResource>& resources) override;
   void ReclaimResources(
diff --git a/content/browser/renderer_host/media/service_video_capture_provider.h b/content/browser/renderer_host/media/service_video_capture_provider.h
index cdf2386c..2a25dbcb 100644
--- a/content/browser/renderer_host/media/service_video_capture_provider.h
+++ b/content/browser/renderer_host/media/service_video_capture_provider.h
@@ -27,9 +27,8 @@
     : public VideoCaptureProvider,
       public service_manager::mojom::ServiceManagerListener {
  public:
-
   // This constructor uses a default factory for instances of
-  // ws::mojom::Gpu which produces instances of class content::GpuClient.
+  // viz::mojom::Gpu which produces instances of class content::GpuClient.
   ServiceVideoCaptureProvider(
       service_manager::Connector* connector,
       base::RepeatingCallback<void(const std::string&)> emit_log_message_cb);
@@ -38,7 +37,7 @@
   using CreateAcceleratorFactoryCallback = base::RepeatingCallback<
       std::unique_ptr<video_capture::mojom::AcceleratorFactory>()>;
   // Lets clients provide a custom mojo::Connector and factory method for
-  // creating instances of ws::mojom::Gpu.
+  // creating instances of viz::mojom::Gpu.
   ServiceVideoCaptureProvider(
       CreateAcceleratorFactoryCallback create_accelerator_factory_cb,
       service_manager::Connector* connector,
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 98c3bb3..c046b10 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -2440,12 +2440,17 @@
         SiteInstanceImpl::GetRequestInitiatorSiteLock(process_lock);
   }
 
+  // Since this function is about to get deprecated (crbug.com/891872), it
+  // should be fine to not add support for network isolation thus sending empty
+  // key.
   CreateURLLoaderFactory(request_initiator_site_lock,
+                         net::NetworkIsolationKey(),
                          nullptr /* header_client */, std::move(request));
 }
 
 void RenderProcessHostImpl::CreateURLLoaderFactory(
     const base::Optional<url::Origin>& origin,
+    const net::NetworkIsolationKey& network_isolation_key,
     network::mojom::TrustedURLLoaderHeaderClientPtrInfo header_client,
     network::mojom::URLLoaderFactoryRequest request) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -2482,6 +2487,7 @@
     params->disable_web_security =
         base::CommandLine::ForCurrentProcess()->HasSwitch(
             switches::kDisableWebSecurity);
+    params->network_isolation_key = network_isolation_key;
     SiteIsolationPolicy::PopulateURLLoaderFactoryParamsPtrForCORB(params.get());
     params->header_client = std::move(header_client);
     network_context->CreateURLLoaderFactory(std::move(request),
diff --git a/content/browser/renderer_host/render_process_host_impl.h b/content/browser/renderer_host/render_process_host_impl.h
index 2695f800..a949f32 100644
--- a/content/browser/renderer_host/render_process_host_impl.h
+++ b/content/browser/renderer_host/render_process_host_impl.h
@@ -51,12 +51,13 @@
 #include "mojo/public/cpp/bindings/associated_binding_set.h"
 #include "mojo/public/cpp/bindings/interface_ptr.h"
 #include "mojo/public/cpp/system/invitation.h"
+#include "net/base/network_isolation_key.h"
 #include "services/network/public/mojom/mdns_responder.mojom.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
 #include "services/service_manager/public/mojom/service.mojom.h"
 #include "services/viz/public/interfaces/compositing/compositing_mode_watcher.mojom.h"
-#include "services/ws/public/mojom/gpu.mojom.h"
+#include "services/viz/public/interfaces/gpu.mojom.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
 #include "third_party/blink/public/mojom/associated_interfaces/associated_interfaces.mojom.h"
 #include "third_party/blink/public/mojom/broadcastchannel/broadcast_channel.mojom.h"
@@ -217,6 +218,7 @@
   mojom::Renderer* GetRendererInterface() override;
   void CreateURLLoaderFactory(
       const base::Optional<url::Origin>& origin,
+      const net::NetworkIsolationKey& network_isolation_key,
       network::mojom::TrustedURLLoaderHeaderClientPtrInfo header_client,
       network::mojom::URLLoaderFactoryRequest request) override;
 
diff --git a/content/browser/renderer_host/render_process_host_unittest.cc b/content/browser/renderer_host/render_process_host_unittest.cc
index ce4231a..560fa2a 100644
--- a/content/browser/renderer_host/render_process_host_unittest.cc
+++ b/content/browser/renderer_host/render_process_host_unittest.cc
@@ -418,7 +418,9 @@
 
   // Isolate |kUrl1| so we can't get a default SiteInstance for it.
   ChildProcessSecurityPolicyImpl::GetInstance()->AddIsolatedOrigins(
-      {url::Origin::Create(kUrl1)}, browser_context());
+      {url::Origin::Create(kUrl1)},
+      ChildProcessSecurityPolicy::IsolatedOriginSource::TEST,
+      browser_context());
 
   // At first, trying to get a RenderProcessHost with the
   // REUSE_PENDING_OR_COMMITTED_SITE policy should return a new process.
diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc
index 212a792..2cf928d 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.cc
+++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -954,9 +954,9 @@
 }
 
 void RenderWidgetHostViewAndroid::DidPresentCompositorFrames(
-    const viz::PresentationFeedbackMap& feedbacks) {
-  presentation_feedbacks_ = feedbacks;
-  if (!presentation_feedbacks_.empty())
+    const viz::FrameTimingDetailsMap& timing_details) {
+  timing_details_ = timing_details;
+  if (!timing_details_.empty())
     AddBeginFrameRequest(BEGIN_FRAME);
 }
 
@@ -1573,12 +1573,11 @@
   : args.frame_time + (args.interval * 0.6);
   if (sync_compositor_) {
     sync_compositor_->BeginFrame(view_.GetWindowAndroid(), args,
-                                 presentation_feedbacks_);
+                                 timing_details_);
   } else if (renderer_compositor_frame_sink_) {
-    renderer_compositor_frame_sink_->OnBeginFrame(args,
-                                                  presentation_feedbacks_);
+    renderer_compositor_frame_sink_->OnBeginFrame(args, timing_details_);
   }
-  presentation_feedbacks_.clear();
+  timing_details_.clear();
 }
 
 bool RenderWidgetHostViewAndroid::Animate(base::TimeTicks frame_time) {
diff --git a/content/browser/renderer_host/render_widget_host_view_android.h b/content/browser/renderer_host/render_widget_host_view_android.h
index c1cfc4c..3053d08 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.h
+++ b/content/browser/renderer_host/render_widget_host_view_android.h
@@ -21,7 +21,7 @@
 #include "base/time/time.h"
 #include "components/viz/common/frame_sinks/begin_frame_args.h"
 #include "components/viz/common/frame_sinks/begin_frame_source.h"
-#include "components/viz/common/presentation_feedback_map.h"
+#include "components/viz/common/frame_timing_details_map.h"
 #include "components/viz/common/quads/selection.h"
 #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
 #include "content/browser/devtools/devtools_frame_metadata.h"
@@ -245,7 +245,7 @@
   // Used by DelegatedFrameHostClientAndroid.
   void SetBeginFrameSource(viz::BeginFrameSource* begin_frame_source);
   void DidPresentCompositorFrames(
-      const viz::PresentationFeedbackMap& feedbacks);
+      const viz::FrameTimingDetailsMap& timing_details);
   void DidReceiveCompositorFrameAck(
       const std::vector<viz::ReturnedResource>& resources);
   void ReclaimResources(const std::vector<viz::ReturnedResource>& resources);
@@ -537,7 +537,7 @@
   // If true, then the next allocated surface should be embedded.
   bool navigation_while_hidden_ = false;
 
-  viz::PresentationFeedbackMap presentation_feedbacks_;
+  viz::FrameTimingDetailsMap timing_details_;
 
   // Tracks whether we are in SynchronousCopyContents to avoid repeated calls
   // into DevTools capture logic.
diff --git a/content/browser/renderer_host/render_widget_host_view_child_frame.cc b/content/browser/renderer_host/render_widget_host_view_child_frame.cc
index a04ca192..f09d356 100644
--- a/content/browser/renderer_host/render_widget_host_view_child_frame.cc
+++ b/content/browser/renderer_host/render_widget_host_view_child_frame.cc
@@ -860,10 +860,10 @@
 
 void RenderWidgetHostViewChildFrame::OnBeginFrame(
     const viz::BeginFrameArgs& args,
-    const viz::PresentationFeedbackMap& feedbacks) {
+    const viz::FrameTimingDetailsMap& timing_details) {
   host_->ProgressFlingIfNeeded(args.frame_time);
   if (renderer_compositor_frame_sink_)
-    renderer_compositor_frame_sink_->OnBeginFrame(args, feedbacks);
+    renderer_compositor_frame_sink_->OnBeginFrame(args, timing_details);
 }
 
 void RenderWidgetHostViewChildFrame::OnBeginFramePausedChanged(bool paused) {
diff --git a/content/browser/renderer_host/render_widget_host_view_child_frame.h b/content/browser/renderer_host/render_widget_host_view_child_frame.h
index 6668af1aa..e3f7971 100644
--- a/content/browser/renderer_host/render_widget_host_view_child_frame.h
+++ b/content/browser/renderer_host/render_widget_host_view_child_frame.h
@@ -17,7 +17,7 @@
 #include "build/build_config.h"
 #include "cc/input/touch_action.h"
 #include "components/viz/common/frame_sinks/begin_frame_source.h"
-#include "components/viz/common/presentation_feedback_map.h"
+#include "components/viz/common/frame_timing_details_map.h"
 #include "components/viz/common/resources/returned_resource.h"
 #include "components/viz/common/surfaces/surface_info.h"
 #include "components/viz/host/host_frame_sink_client.h"
@@ -194,7 +194,7 @@
   void DidReceiveCompositorFrameAck(
       const std::vector<viz::ReturnedResource>& resources) override;
   void OnBeginFrame(const viz::BeginFrameArgs& args,
-                    const viz::PresentationFeedbackMap& feedbacks) override;
+                    const viz::FrameTimingDetailsMap& timing_details) override;
   void ReclaimResources(
       const std::vector<viz::ReturnedResource>& resources) override;
   void OnBeginFramePausedChanged(bool paused) override;
diff --git a/content/browser/renderer_host/web_database_host_impl_unittest.cc b/content/browser/renderer_host/web_database_host_impl_unittest.cc
index 7f7c77bb..28a728e 100644
--- a/content/browser/renderer_host/web_database_host_impl_unittest.cc
+++ b/content/browser/renderer_host/web_database_host_impl_unittest.cc
@@ -113,7 +113,9 @@
       ConstructVfsFileName(incorrect_origin, db_name, suffix);
 
   auto* security_policy = ChildProcessSecurityPolicyImpl::GetInstance();
-  security_policy->AddIsolatedOrigins({correct_origin, incorrect_origin});
+  security_policy->AddIsolatedOrigins(
+      {correct_origin, incorrect_origin},
+      ChildProcessSecurityPolicy::IsolatedOriginSource::TEST);
 
   security_policy->LockToOrigin(IsolationContext(browser_context()),
                                 process_id(), correct_origin.GetURL());
@@ -198,7 +200,9 @@
       ConstructVfsFileName(incorrect_origin, db_name, suffix);
 
   auto* security_policy = ChildProcessSecurityPolicyImpl::GetInstance();
-  security_policy->AddIsolatedOrigins({correct_origin, incorrect_origin});
+  security_policy->AddIsolatedOrigins(
+      {correct_origin, incorrect_origin},
+      ChildProcessSecurityPolicy::IsolatedOriginSource::TEST);
   security_policy->LockToOrigin(IsolationContext(browser_context()),
                                 process_id(), correct_origin.GetURL());
 
diff --git a/content/browser/security_exploit_browsertest.cc b/content/browser/security_exploit_browsertest.cc
index 1762c57..c622b33 100644
--- a/content/browser/security_exploit_browsertest.cc
+++ b/content/browser/security_exploit_browsertest.cc
@@ -56,6 +56,7 @@
 #include "mojo/core/embedder/embedder.h"
 #include "mojo/public/cpp/bindings/strong_associated_binding.h"
 #include "mojo/public/cpp/test_support/test_utils.h"
+#include "net/base/network_isolation_key.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/test/url_request/url_request_slow_download_job.h"
@@ -306,9 +307,9 @@
       const network::ResourceRequest& resource_request,
       network::mojom::URLLoaderClientPtrInfo client) {
     network::mojom::URLLoaderFactoryPtr factory;
-    frame->GetProcess()->CreateURLLoaderFactory(frame->GetLastCommittedOrigin(),
-                                                nullptr /* header_client */,
-                                                mojo::MakeRequest(&factory));
+    frame->GetProcess()->CreateURLLoaderFactory(
+        frame->GetLastCommittedOrigin(), net::NetworkIsolationKey(),
+        nullptr /* header_client */, mojo::MakeRequest(&factory));
     factory->CreateLoaderAndStart(
         std::move(request), route_id, request_id,
         network::mojom::kURLLoadOptionNone, resource_request,
diff --git a/content/browser/service_manager/common_browser_interfaces.cc b/content/browser/service_manager/common_browser_interfaces.cc
index 91716b6..248c8cec 100644
--- a/content/browser/service_manager/common_browser_interfaces.cc
+++ b/content/browser/service_manager/common_browser_interfaces.cc
@@ -27,7 +27,7 @@
 #include "content/public/common/service_names.mojom.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
-#include "services/ws/public/mojom/gpu.mojom.h"
+#include "services/viz/public/interfaces/gpu.mojom.h"
 
 #if defined(OS_WIN)
 #include "content/browser/renderer_host/dwrite_font_proxy_impl_win.h"
@@ -82,16 +82,16 @@
                        const std::string& interface_name,
                        mojo::ScopedMessagePipeHandle* interface_pipe,
                        service_manager::Connector* connector) override {
-    // Ignore ws::mojom::Gpu interface request from Renderer process.
+    // Ignore viz::mojom::Gpu interface request from Renderer process.
     // The request will be handled in RenderProcessHostImpl.
     if (source_info.identity.name() == mojom::kRendererServiceName &&
-        interface_name == ws::mojom::Gpu::Name_)
+        interface_name == viz::mojom::Gpu::Name_)
       return;
 
     registry_.TryBindInterface(interface_name, interface_pipe, source_info);
   }
 
-  void BindGpuRequest(ws::mojom::GpuRequest request,
+  void BindGpuRequest(viz::mojom::GpuRequest request,
                       const service_manager::BindSourceInfo& source_info) {
     DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
diff --git a/content/browser/service_worker/embedded_worker_instance.cc b/content/browser/service_worker/embedded_worker_instance.cc
index 028b4d6d..6558cc1 100644
--- a/content/browser/service_worker/embedded_worker_instance.cc
+++ b/content/browser/service_worker/embedded_worker_instance.cc
@@ -992,13 +992,17 @@
         rph, routing_id, &default_factory_request);
   }
 
+  // TODO(crbug.com/955476): network isolation key to be created using worker
+  // script's origin.
   if (GetNetworkFactoryCallbackForTest().is_null()) {
-    rph->CreateURLLoaderFactory(origin, std::move(default_header_client),
-                                std::move(default_factory_request));
+    rph->CreateURLLoaderFactory(
+        origin, net::NetworkIsolationKey() /* network_isolation_key */,
+        std::move(default_header_client), std::move(default_factory_request));
   } else {
     network::mojom::URLLoaderFactoryPtr original_factory;
-    rph->CreateURLLoaderFactory(origin, std::move(default_header_client),
-                                mojo::MakeRequest(&original_factory));
+    rph->CreateURLLoaderFactory(
+        origin, net::NetworkIsolationKey() /* network_isolation_key */,
+        std::move(default_header_client), mojo::MakeRequest(&original_factory));
     GetNetworkFactoryCallbackForTest().Run(std::move(default_factory_request),
                                            rph->GetID(),
                                            original_factory.PassInterface());
diff --git a/content/browser/site_instance_impl.cc b/content/browser/site_instance_impl.cc
index 0fffada..f346b89 100644
--- a/content/browser/site_instance_impl.cc
+++ b/content/browser/site_instance_impl.cc
@@ -992,7 +992,10 @@
   ChildProcessSecurityPolicyImpl* policy =
       ChildProcessSecurityPolicyImpl::GetInstance();
   url::Origin site_origin(url::Origin::Create(site));
-  policy->AddIsolatedOrigins({site_origin}, context);
+  policy->AddIsolatedOrigins(
+      {site_origin},
+      ChildProcessSecurityPolicy::IsolatedOriginSource::USER_TRIGGERED,
+      context);
 
   // This function currently assumes the new isolated site should persist
   // across restarts, so ask the embedder to save it, excluding off-the-record
diff --git a/content/browser/site_instance_impl_unittest.cc b/content/browser/site_instance_impl_unittest.cc
index e4869f3..b2ebd278 100644
--- a/content/browser/site_instance_impl_unittest.cc
+++ b/content/browser/site_instance_impl_unittest.cc
@@ -48,6 +48,8 @@
 namespace content {
 namespace {
 
+using IsolatedOriginSource = ChildProcessSecurityPolicy::IsolatedOriginSource;
+
 bool IsSameWebSite(BrowserContext* context,
                    const GURL& url1,
                    const GURL& url2) {
@@ -1025,7 +1027,8 @@
   EXPECT_FALSE(IsIsolatedOrigin(isolated_foo_url));
   EXPECT_TRUE(IsSameWebSite(context(), foo_url, isolated_foo_url));
 
-  policy->AddIsolatedOrigins({url::Origin::Create(isolated_foo_url)});
+  policy->AddIsolatedOrigins({url::Origin::Create(isolated_foo_url)},
+                             IsolatedOriginSource::TEST);
   EXPECT_TRUE(IsIsolatedOrigin(isolated_foo_url));
   EXPECT_FALSE(IsIsolatedOrigin(foo_url));
   EXPECT_FALSE(IsIsolatedOrigin(GURL("http://foo.com")));
@@ -1039,7 +1042,8 @@
   // Different port.
   EXPECT_TRUE(IsIsolatedOrigin(GURL("http://isolated.foo.com:12345")));
 
-  policy->AddIsolatedOrigins({url::Origin::Create(isolated_bar_url)});
+  policy->AddIsolatedOrigins({url::Origin::Create(isolated_bar_url)},
+                             IsolatedOriginSource::TEST);
   EXPECT_TRUE(IsIsolatedOrigin(isolated_bar_url));
 
   // IsSameWebSite should compare origins rather than sites if either URL is an
@@ -1105,7 +1109,8 @@
         .Times(1);
     mock_log.StartCapturingLogs();
 
-    policy->AddIsolatedOrigins({url::Origin::Create(isolated_foo_with_port)});
+    policy->AddIsolatedOrigins({url::Origin::Create(isolated_foo_with_port)},
+                               IsolatedOriginSource::TEST);
   }
 
   EXPECT_TRUE(IsIsolatedOrigin(isolated_foo_url));
@@ -1166,7 +1171,8 @@
   GURL foo_isolated_url("http://foo.isolated.com");
 
   auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
-  policy->AddIsolatedOrigins({url::Origin::Create(isolated_url)});
+  policy->AddIsolatedOrigins({url::Origin::Create(isolated_url)},
+                             IsolatedOriginSource::TEST);
 
   EXPECT_TRUE(IsIsolatedOrigin(isolated_url));
   EXPECT_TRUE(IsIsolatedOrigin(foo_isolated_url));
@@ -1196,7 +1202,8 @@
 
   // Don't try to match subdomains on IP addresses.
   GURL isolated_ip("http://127.0.0.1");
-  policy->AddIsolatedOrigins({url::Origin::Create(isolated_ip)});
+  policy->AddIsolatedOrigins({url::Origin::Create(isolated_ip)},
+                             IsolatedOriginSource::TEST);
   EXPECT_TRUE(IsIsolatedOrigin(isolated_ip));
   EXPECT_FALSE(IsIsolatedOrigin(GURL("http://42.127.0.0.1")));
 
@@ -1212,7 +1219,8 @@
   GURL baz_isolated_foo_url("http://baz.isolated.foo.com");
 
   auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
-  policy->AddIsolatedOrigins({url::Origin::Create(isolated_foo_url)});
+  policy->AddIsolatedOrigins({url::Origin::Create(isolated_foo_url)},
+                             IsolatedOriginSource::TEST);
 
   EXPECT_FALSE(IsIsolatedOrigin(foo_url));
   EXPECT_TRUE(IsIsolatedOrigin(isolated_foo_url));
@@ -1264,7 +1272,8 @@
   IsolationContext isolation_context(context());
   auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
   policy->AddIsolatedOrigins(
-      {url::Origin::Create(foo_url), url::Origin::Create(baz_bar_foo_url)});
+      {url::Origin::Create(foo_url), url::Origin::Create(baz_bar_foo_url)},
+      IsolatedOriginSource::TEST);
 
   EXPECT_TRUE(IsIsolatedOrigin(foo_url));
   EXPECT_TRUE(IsIsolatedOrigin(bar_foo_url));
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 7b97736..62e3998 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -6800,12 +6800,12 @@
     outermost->SetAsFocusedWebContentsIfNecessary();
 }
 
-gfx::Size WebContentsImpl::EnterPictureInPicture(
+PictureInPictureResult WebContentsImpl::EnterPictureInPicture(
     const viz::SurfaceId& surface_id,
     const gfx::Size& natural_size) {
   return delegate_
              ? delegate_->EnterPictureInPicture(this, surface_id, natural_size)
-             : gfx::Size();
+             : PictureInPictureResult::kNotSupported;
 }
 
 void WebContentsImpl::ExitPictureInPicture() {
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 9aca7efe..21b1a8c5 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -79,6 +79,7 @@
 }
 
 namespace content {
+enum class PictureInPictureResult;
 class BrowserPluginEmbedder;
 class BrowserPluginGuest;
 class DateTimeChooserAndroid;
@@ -981,9 +982,9 @@
 
   // Notifies the Picture-in-Picture controller that there is a new player
   // entering Picture-in-Picture.
-  // Returns the size of the Picture-in-Picture window.
-  gfx::Size EnterPictureInPicture(const viz::SurfaceId&,
-                                  const gfx::Size& natural_size);
+  // Returns the result of the enter request.
+  PictureInPictureResult EnterPictureInPicture(const viz::SurfaceId&,
+                                               const gfx::Size& natural_size);
 
   // Updates the Picture-in-Picture controller with a signal that
   // Picture-in-Picture mode has ended.
diff --git a/content/browser/web_contents/web_contents_impl_browsertest.cc b/content/browser/web_contents/web_contents_impl_browsertest.cc
index 84ca6c56..56eba27 100644
--- a/content/browser/web_contents/web_contents_impl_browsertest.cc
+++ b/content/browser/web_contents/web_contents_impl_browsertest.cc
@@ -52,6 +52,7 @@
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_browser_test.h"
 #include "content/public/test/content_browser_test_utils.h"
+#include "content/public/test/no_renderer_crashes_assertion.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "content/public/test/test_utils.h"
 #include "content/public/test/url_loader_interceptor.h"
@@ -60,6 +61,7 @@
 #include "content/test/test_content_browser_client.h"
 #include "net/base/features.h"
 #include "net/base/ip_endpoint.h"
+#include "net/base/network_isolation_key.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/controllable_http_response.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
@@ -873,6 +875,7 @@
 
   // Kill the renderer process so when the navigate again, it will be a fresh
   // renderer with an empty in-memory cache.
+  ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
   NavigateToURL(shell(), GetWebUIURL("crash"));
 
   // Reload that URL, the subresource should be served from the network cache.
@@ -974,10 +977,20 @@
   }
 
  protected:
+  // Terminates the renderer to clear the in-memory cache.
+  void TerminateRenderer() {
+    RenderProcessHost* process =
+        shell()->web_contents()->GetMainFrame()->GetProcess();
+    RenderProcessHostWatcher process_watcher(
+        process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
+    ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
+    NavigateToURL(shell(), GetWebUIURL("crash"));
+    process_watcher.Wait();
+  }
+
   bool TestResourceLoadFromDedicatedWorker(const GURL& url,
                                            const GURL& worker) {
-    // Kill the renderer to clear the in-memory cache.
-    NavigateToURL(shell(), GURL("chrome:crash"));
+    TerminateRenderer();
 
     // Observe network requests.
     ResourceLoadObserver observer(shell());
@@ -998,8 +1011,7 @@
   // Loads 3p.com/script on page |url|, optionally from |sub_frame| if it's
   // valid and return if the script was cached or not.
   bool TestResourceLoad(const GURL& url, const GURL& sub_frame) {
-    // Kill the renderer to clear the in-memory cache.
-    NavigateToURL(shell(), GetWebUIURL("crash"));
+    TerminateRenderer();
 
     // Observe network requests.
     ResourceLoadObserver observer(shell());
@@ -1008,6 +1020,8 @@
 
     RenderFrameHost* host_to_load_resource =
         shell()->web_contents()->GetMainFrame();
+    RenderFrameHostImpl* main_frame =
+        static_cast<RenderFrameHostImpl*>(host_to_load_resource);
 
     // If there is supposed to be a sub-frame, create it.
     if (sub_frame.is_valid()) {
@@ -1039,6 +1053,18 @@
 
     EXPECT_TRUE(ExecuteScript(host_to_load_resource, loader_script));
     observer.WaitForResourceCompletion(resource);
+
+    // Test the network isolation key.
+    RenderFrameHostImpl* frame_host =
+        static_cast<RenderFrameHostImpl*>(host_to_load_resource);
+    url::Origin top_frame_origin =
+        main_frame->frame_tree_node()->current_origin();
+
+    if (!top_frame_origin.opaque()) {
+      EXPECT_EQ(net::NetworkIsolationKey(top_frame_origin),
+                frame_host->network_isolation_key_);
+    }
+
     return (*observer.FindResource(resource))->was_cached;
   }
 
@@ -1120,8 +1146,20 @@
 
   // Load the same resource from the same data url, it shouldn't be cached
   // because the origin should be unique.
-
   EXPECT_FALSE(TestResourceLoad(data_url, GURL()));
+
+  // Load the resource from a document that points to about:blank.
+  GURL blank_url("about:blank");
+  EXPECT_FALSE(TestResourceLoad(blank_url, GURL()));
+
+  // Load the same resource from about:blank url again, it shouldn't be cached
+  // because the origin is unique. TODO(crbug.com/888079) will change this
+  // behavior and about:blank main frame pages will inherit the origin of the
+  // page that opened it.
+  EXPECT_FALSE(TestResourceLoad(blank_url, GURL()));
+
+  // TODO(crbug.com/950069): Add tests for about:blank, about:srcdoc subframes
+  // when the cache key also starts using subframe origin.
 }
 
 IN_PROC_BROWSER_TEST_F(WebContentsSplitCacheBrowserTestDisabled,
diff --git a/content/browser/worker_host/dedicated_worker_host.cc b/content/browser/worker_host/dedicated_worker_host.cc
index 620e412..8b8fbeaa 100644
--- a/content/browser/worker_host/dedicated_worker_host.cc
+++ b/content/browser/worker_host/dedicated_worker_host.cc
@@ -21,6 +21,7 @@
 #include "content/public/browser/render_process_host.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "mojo/public/cpp/system/message_pipe.h"
+#include "net/base/network_isolation_key.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
 #include "services/service_manager/public/mojom/interface_provider.mojom.h"
 #include "third_party/blink/public/common/features.h"
@@ -213,8 +214,12 @@
                             RenderProcessHost* process) {
     DCHECK_CURRENTLY_ON(BrowserThread::UI);
     network::mojom::TrustedURLLoaderHeaderClientPtrInfo no_header_client;
-    process->CreateURLLoaderFactory(origin_, std::move(no_header_client),
-                                    std::move(request));
+
+    // TODO(crbug.com/955476): Create network_isolation_key cache key using
+    // worker script's origin.
+    process->CreateURLLoaderFactory(
+        origin_, net::NetworkIsolationKey() /* network_isolation_key */,
+        std::move(no_header_client), std::move(request));
   }
 
   void CreateWebUsbService(blink::mojom::WebUsbServiceRequest request) {
diff --git a/content/browser/worker_host/shared_worker_host.cc b/content/browser/worker_host/shared_worker_host.cc
index 96d6821..277f89f 100644
--- a/content/browser/worker_host/shared_worker_host.cc
+++ b/content/browser/worker_host/shared_worker_host.cc
@@ -26,6 +26,7 @@
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/common/content_client.h"
+#include "net/base/network_isolation_key.h"
 #include "services/network/public/cpp/features.h"
 #include "third_party/blink/public/common/loader/url_loader_factory_bundle.h"
 #include "third_party/blink/public/common/messaging/message_port_channel.h"
@@ -316,12 +317,17 @@
   RenderProcessHost* process = RenderProcessHost::FromID(process_id_);
   url::Origin origin = instance_->constructor_origin();
   network::mojom::TrustedURLLoaderHeaderClientPtrInfo no_header_client;
+
+  // TODO(crbug.com/955476): network_isolation_key to be created using worker
+  // script's origin.
   if (GetCreateNetworkFactoryCallbackForSharedWorker().is_null()) {
-    process->CreateURLLoaderFactory(origin, std::move(no_header_client),
+    process->CreateURLLoaderFactory(origin, net::NetworkIsolationKey(),
+                                    std::move(no_header_client),
                                     std::move(request));
   } else {
     network::mojom::URLLoaderFactoryPtr original_factory;
-    process->CreateURLLoaderFactory(origin, std::move(no_header_client),
+    process->CreateURLLoaderFactory(origin, net::NetworkIsolationKey(),
+                                    std::move(no_header_client),
                                     mojo::MakeRequest(&original_factory));
     GetCreateNetworkFactoryCallbackForSharedWorker().Run(
         std::move(request), process_id_, original_factory.PassInterface());
diff --git a/content/browser/worker_host/worker_script_fetch_initiator.cc b/content/browser/worker_host/worker_script_fetch_initiator.cc
index 3e13bdc7..a6fb43ef 100644
--- a/content/browser/worker_host/worker_script_fetch_initiator.cc
+++ b/content/browser/worker_host/worker_script_fetch_initiator.cc
@@ -37,6 +37,7 @@
 #include "content/public/common/origin_util.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "net/base/load_flags.h"
+#include "net/base/network_isolation_key.h"
 #include "services/network/loader_util.h"
 #include "services/network/public/cpp/features.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
@@ -208,8 +209,11 @@
     const url::Origin kSafeOrigin = url::Origin();
 
     network::mojom::URLLoaderFactoryPtr default_factory;
+
+    // TODO(crbug.com/955476): Populate the network isolation key.
     RenderProcessHost::FromID(process_id)
-        ->CreateURLLoaderFactory(kSafeOrigin, nullptr /* header_client */,
+        ->CreateURLLoaderFactory(kSafeOrigin, net::NetworkIsolationKey(),
+                                 nullptr /* header_client */,
                                  mojo::MakeRequest(&default_factory));
     factory_bundle->default_factory_info() = default_factory.PassInterface();
   }
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index b4779ea..5bc8b83 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -315,7 +315,6 @@
     "//services/service_manager/zygote:zygote_buildflags",
     "//services/video_capture/public/mojom",
     "//services/viz/public/interfaces",
-    "//services/ws/public/mojom",
     "//skia",
     "//storage/common",
     "//third_party/angle:angle_gpu_info_util",
@@ -514,7 +513,6 @@
     "//services/service_manager/public/mojom",
     "//services/video_capture/public/mojom",
     "//services/viz/public/interfaces",
-    "//services/ws/public/mojom",
     "//skia/public/interfaces",
     "//third_party/blink/public/mojom:mojom_core",
     "//third_party/blink/public/mojom:web_feature_mojo_bindings",
diff --git a/content/common/input/synchronous_compositor.mojom b/content/common/input/synchronous_compositor.mojom
index aa01db13..5aee5034 100644
--- a/content/common/input/synchronous_compositor.mojom
+++ b/content/common/input/synchronous_compositor.mojom
@@ -9,9 +9,9 @@
 import "services/viz/public/interfaces/compositing/begin_frame_args.mojom";
 import "services/viz/public/interfaces/compositing/compositor_frame.mojom";
 import "services/viz/public/interfaces/compositing/compositor_frame_metadata.mojom";
+import "services/viz/public/interfaces/compositing/frame_timing_details.mojom";
 import "services/viz/public/interfaces/compositing/returned_resource.mojom";
 import "ui/gfx/geometry/mojo/geometry.mojom";
-import "ui/gfx/mojo/presentation_feedback.mojom";
 
 [Native]
 struct SyncCompositorDemandDrawHwParams;
@@ -86,16 +86,17 @@
 
   // BeginFrame, update will be pushed via SynchronousCompositorControlHost
   // BeginFrameResponse.
-  // |presentations| is a map from frame token to PresentationFeedback.
+  // |timing_details| is a map from frame token to FrameTimingDetails.
   // Frame token is an incrementing id generated by untrusted viz client
   // (renderer) and sent to viz service (browser) in a frame (see
-  // CompositorFrameMetadata). PresentationFeedback contains info of viz server
-  // displaying frames, such as time of display. Note that the viz server might
-  // choose to skip display some of the previously submitted frames; however,
-  // feedback about all previously submitted frames will be sent back once a
-  // new frame is displayed.
+  // CompositorFrameMetadata). FrameTimingDetails contains info of viz server
+  // displaying frames, such as time of display. The index of the map is the
+  // frame token of the previously submitted frame that has been displayed.
+  // Note that the viz server might choose to skip display some of the
+  // previously submitted frames; however, feedback about all previously
+  // submitted frames will be sent back once a new frame is displayed.
   BeginFrame(viz.mojom.BeginFrameArgs args,
-             map<uint32, gfx.mojom.PresentationFeedback> presentations);
+             map<uint32, viz.mojom.FrameTimingDetails> timing_details);
 
   // Indicates BeginFrame messages are paused.
   SetBeginFrameSourcePaused(bool paused);
diff --git a/content/public/app/content_browser_manifest.cc b/content/public/app/content_browser_manifest.cc
index 2961f26f..f53c440 100644
--- a/content/public/app/content_browser_manifest.cc
+++ b/content/public/app/content_browser_manifest.cc
@@ -38,7 +38,7 @@
               "plugin",
               std::set<const char*>{
                   "discardable_memory.mojom.DiscardableSharedMemoryManager",
-                  "ws.mojom.Gpu",
+                  "viz.mojom.Gpu",
               })
           .ExposeCapability(
               "app",
@@ -91,11 +91,11 @@
                   "network.mojom.URLLoaderFactory",
                   "resource_coordinator.mojom.ProcessCoordinationUnit",
                   "viz.mojom.CompositingModeReporter",
-                  "ws.mojom.Gpu",
+                  "viz.mojom.Gpu",
               })
           .ExposeCapability("gpu_client",
                             std::set<const char*>{
-                                "ws.mojom.Gpu",
+                                "viz.mojom.Gpu",
                             })
           .ExposeCapability(
               "gpu",
@@ -144,7 +144,6 @@
           .RequireCapability("unzip_service", "unzip_file")
           .RequireCapability("tracing", "tracing")
           .RequireCapability("patch_service", "patch_file")
-          .RequireCapability("ui", "arc_manager")
           .RequireCapability("audio", "info")
           .RequireCapability("audio", "debug_recording")
           .RequireCapability("audio", "device_notifier")
@@ -268,7 +267,7 @@
                   "shape_detection.mojom.BarcodeDetectionProvider",
                   "shape_detection.mojom.FaceDetectionProvider",
                   "shape_detection.mojom.TextDetection",
-                  "ws.mojom.Gpu"})
+                  "viz.mojom.Gpu"})
           .RequireInterfaceFilterCapability_Deprecated(
               mojom::kRendererServiceName, "navigation:frame", "browser")
           .PackageService(content::GetManifest())
diff --git a/content/public/browser/BUILD.gn b/content/public/browser/BUILD.gn
index 12bdb7b..d4f4920 100644
--- a/content/public/browser/BUILD.gn
+++ b/content/public/browser/BUILD.gn
@@ -385,7 +385,7 @@
     "//services/resource_coordinator/public/cpp:resource_coordinator_cpp",
     "//services/service_manager/public/cpp",
     "//services/tracing/public/cpp",
-    "//services/ws/public/mojom",
+    "//services/viz/public/interfaces",
     "//third_party/webrtc/modules/desktop_capture",
 
     # We expose skia headers in the public API.
@@ -433,14 +433,6 @@
     sources += [ "context_factory.h" ]
   }
 
-  if (use_aura) {
-    sources += [ "gpu_interface_provider_factory.h" ]
-    deps += [
-      "//services/ws/public/cpp/host",
-      "//ui/aura",
-    ]
-  }
-
   if (is_mac) {
     sources += [ "remote_cocoa.h" ]
   }
diff --git a/content/public/browser/DEPS b/content/public/browser/DEPS
index 995eedcc..c77149d 100644
--- a/content/public/browser/DEPS
+++ b/content/public/browser/DEPS
@@ -10,7 +10,7 @@
   "+services/network/public/cpp",
   "+services/service_manager/sandbox",
   "+services/video_capture/public/mojom",
-  "+services/ws/public/cpp/host",
+  "+services/viz/public/interfaces",
   "+services/ws/public/mojom",
 ]
 
diff --git a/content/public/browser/android/synchronous_compositor.h b/content/public/browser/android/synchronous_compositor.h
index 59d2651..95a2e0b 100644
--- a/content/public/browser/android/synchronous_compositor.h
+++ b/content/public/browser/android/synchronous_compositor.h
@@ -12,7 +12,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/time/time.h"
-#include "components/viz/common/presentation_feedback_map.h"
+#include "components/viz/common/frame_timing_details_map.h"
 #include "components/viz/common/resources/returned_resource.h"
 #include "content/common/content_export.h"
 #include "ui/gfx/geometry/rect.h"
@@ -92,7 +92,7 @@
       const std::vector<viz::ReturnedResource>& resources) = 0;
 
   virtual void DidPresentCompositorFrames(
-      viz::PresentationFeedbackMap feedbacks,
+      viz::FrameTimingDetailsMap timing_details,
       uint32_t frame_token) = 0;
 
   // "On demand" SW draw, into the supplied canvas (observing the transform
diff --git a/content/public/browser/child_process_security_policy.h b/content/public/browser/child_process_security_policy.h
index e761298..235013c 100644
--- a/content/public/browser/child_process_security_policy.h
+++ b/content/public/browser/child_process_security_policy.h
@@ -220,6 +220,26 @@
   // whether precursor of opaque origins also matches the process lock).
   virtual bool CanAccessDataForOrigin(int child_id, const GURL& url) = 0;
 
+  // Defines available sources of isolated origins.  This should be specified
+  // when adding isolated origins with the AddIsolatedOrigins() call below.
+  enum class IsolatedOriginSource {
+    // Used for origins that are hardcoded into the browser.
+    BUILT_IN,
+    // Used for origins that are specified from the command line, i.e.
+    // --isolate-origins.
+    COMMAND_LINE,
+    // Used for origins that are configured through field trials.
+    FIELD_TRIAL,
+    // Used for origins defined by an administrator (e.g., via enterprise
+    // policy).
+    POLICY,
+    // Used for origins that are isolated based on user-triggered runtime
+    // heuristics.
+    USER_TRIGGERED,
+    // Used for testing purposes.
+    TEST
+  };
+
   // Add |origins| to the list of origins that require process isolation.  When
   // making process model decisions for such origins, the scheme+host tuple
   // rather than scheme and eTLD+1 will be used.  SiteInstances for these
@@ -253,6 +273,9 @@
   // isolated prior to calling this, it is ignored, and its threshold is not
   // updated.
   //
+  // |source| describes the context/reason for adding the new isolated origins;
+  // see comments on IsolatedOriginSource.
+  //
   // If |browser_context| is non-null, the new isolated origins added via this
   // function will apply only within that BrowserContext.  If |browser_context|
   // is null, the new isolated origins will apply globally in *all*
@@ -266,6 +289,7 @@
   // ignored.
   virtual void AddIsolatedOrigins(
       const std::vector<url::Origin>& origins,
+      IsolatedOriginSource source,
       BrowserContext* browser_context = nullptr) = 0;
 
   // Semantically identical to the above, but accepts IsolatedOriginPatterns, a
@@ -274,11 +298,28 @@
   // to be given their own isolated process.
   virtual void AddIsolatedOrigins(
       const std::vector<IsolatedOriginPattern>& patterns,
+      IsolatedOriginSource source,
       BrowserContext* browser_context = nullptr) = 0;
 
   // Returns true if |origin| is a globally (not per-profile) isolated origin.
   virtual bool IsGloballyIsolatedOriginForTesting(
       const url::Origin& origin) = 0;
+
+  // Returns the set of currently active isolated origins, optionally filtered
+  // by the source of how they were added and/or by BrowserContext.
+  //
+  // If |source| is provided, only origins that were added with the same source
+  // will be returned; if |source| is base::nullopt, origins from all sources
+  // will be returned.
+  //
+  // If |browser_context| is null, only globally applicable origins will be
+  // returned.  If |browser_context| is non-null, only origins that apply
+  // within that particular BrowserContext will be returned (note that this
+  // includes both matching per-profile isolated origins as well as globally
+  // applicable origins which apply to |browser_context| by definition).
+  virtual std::vector<url::Origin> GetIsolatedOrigins(
+      base::Optional<IsolatedOriginSource> source = base::nullopt,
+      BrowserContext* browser_context = nullptr) = 0;
 };
 
 }  // namespace content
diff --git a/content/public/browser/devtools_agent_host.h b/content/public/browser/devtools_agent_host.h
index 86d07c8..096b6c2 100644
--- a/content/public/browser/devtools_agent_host.h
+++ b/content/public/browser/devtools_agent_host.h
@@ -19,6 +19,7 @@
 #include "url/gurl.h"
 
 namespace base {
+class RefCountedMemory;
 class SingleThreadTaskRunner;
 }
 
@@ -113,6 +114,12 @@
   static void AddObserver(DevToolsAgentHostObserver*);
   static void RemoveObserver(DevToolsAgentHostObserver*);
 
+  // Create a DevTools IO Stream from data.
+  // Returns a DevTools IO Stream handle that can be used to read and close the
+  // stream.
+  virtual std::string CreateIOStreamFromData(
+      scoped_refptr<base::RefCountedMemory>) = 0;
+
   // Attaches |client| to this agent host to start debugging.
   // Returns |true| on success. Note that some policies defined by
   // embedder or |client| itself may prevent attaching.
diff --git a/content/public/browser/gpu_client.h b/content/public/browser/gpu_client.h
index c5616d1a..ab57aeb 100644
--- a/content/public/browser/gpu_client.h
+++ b/content/public/browser/gpu_client.h
@@ -9,13 +9,13 @@
 
 #include "components/viz/host/gpu_client.h"
 #include "content/common/content_export.h"
-#include "services/ws/public/mojom/gpu.mojom.h"
+#include "services/viz/public/interfaces/gpu.mojom.h"
 
 namespace content {
 
 CONTENT_EXPORT
 std::unique_ptr<viz::GpuClient, base::OnTaskRunnerDeleter> CreateGpuClient(
-    ws::mojom::GpuRequest request,
+    viz::mojom::GpuRequest request,
     viz::GpuClient::ConnectionErrorHandlerClosure connection_error_handler,
     scoped_refptr<base::SingleThreadTaskRunner> task_runner);
 
diff --git a/content/public/browser/gpu_interface_provider_factory.h b/content/public/browser/gpu_interface_provider_factory.h
deleted file mode 100644
index 9b81ede..0000000
--- a/content/public/browser/gpu_interface_provider_factory.h
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_PUBLIC_BROWSER_GPU_INTERFACE_PROVIDER_FACTORY_H_
-#define CONTENT_PUBLIC_BROWSER_GPU_INTERFACE_PROVIDER_FACTORY_H_
-
-#include <memory>
-
-#include "content/common/content_export.h"
-#include "services/ws/public/cpp/host/gpu_interface_provider.h"
-
-namespace content {
-
-// Creates a GpuInterfaceProvider that forwards to the Gpu implementation in
-// content.
-CONTENT_EXPORT std::unique_ptr<ws::GpuInterfaceProvider>
-CreateGpuInterfaceProvider();
-
-}  // namespace content
-
-#endif  // CONTENT_PUBLIC_BROWSER_GPU_INTERFACE_PROVIDER_FACTORY_H_
diff --git a/content/public/browser/picture_in_picture_window_controller.h b/content/public/browser/picture_in_picture_window_controller.h
index 8ed12997..2c03c0d 100644
--- a/content/public/browser/picture_in_picture_window_controller.h
+++ b/content/public/browser/picture_in_picture_window_controller.h
@@ -34,8 +34,7 @@
   virtual ~PictureInPictureWindowController() = default;
 
   // Shows the Picture-in-Picture window.
-  // Returns the size of the window in pixels.
-  virtual gfx::Size Show() = 0;
+  virtual void Show() = 0;
 
   // Called to notify the controller that the window was requested to be closed
   // by the user or the content.
diff --git a/content/public/browser/render_process_host.h b/content/public/browser/render_process_host.h
index 3b3eea6..c3a2830 100644
--- a/content/public/browser/render_process_host.h
+++ b/content/public/browser/render_process_host.h
@@ -24,6 +24,7 @@
 #include "ipc/ipc_channel_proxy.h"
 #include "ipc/ipc_sender.h"
 #include "media/media_buildflags.h"
+#include "net/base/network_isolation_key.h"
 #include "services/network/public/mojom/network_context.mojom-forward.h"
 #include "services/network/public/mojom/url_loader_factory.mojom-forward.h"
 #include "third_party/blink/public/mojom/cache_storage/cache_storage.mojom-forward.h"
@@ -410,9 +411,15 @@
   // |header_client| will be used in URLLoaderFactoryParams when creating the
   // factory.
   //
+  // |network_isolation_key| will be used in URLLoaderFactoryParams when
+  // creating the factory. All resource requests through this factory will
+  // propagate the key to the network stack so that resources with different
+  // keys do not share network resources like the http cache.
+  //
   // TODO(lukasza, nasko): https://crbug.com/888079: Make |origin| mandatory.
   virtual void CreateURLLoaderFactory(
       const base::Optional<url::Origin>& origin,
+      const net::NetworkIsolationKey& network_isolation_key,
       network::mojom::TrustedURLLoaderHeaderClientPtrInfo header_client,
       network::mojom::URLLoaderFactoryRequest request) = 0;
 
diff --git a/content/public/browser/site_isolation_policy.cc b/content/public/browser/site_isolation_policy.cc
index 683579d..a6d0318 100644
--- a/content/public/browser/site_isolation_policy.cc
+++ b/content/public/browser/site_isolation_policy.cc
@@ -19,6 +19,7 @@
 #include "base/strings/string_split.h"
 #include "base/timer/timer.h"
 #include "build/build_config.h"
+#include "content/public/browser/child_process_security_policy.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_features.h"
@@ -137,50 +138,62 @@
 
 // static
 std::vector<url::Origin>
-SiteIsolationPolicy::GetIsolatedOriginsFromEnvironment() {
+SiteIsolationPolicy::GetIsolatedOriginsFromCommandLine() {
   std::string cmdline_arg =
       base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
           switches::kIsolateOrigins);
   std::vector<url::Origin> origins;
+  // Note that --isolate-origins trumps the opt-out flag, so the command-line
+  // origins are returned regardless of IsSiteIsolationDisabled().
   if (!cmdline_arg.empty()) {
     origins = ParseIsolatedOrigins(cmdline_arg);
     UMA_HISTOGRAM_COUNTS_1000("SiteIsolation.IsolateOrigins.Size",
                               origins.size());
   }
+  return origins;
+}
 
-  // --isolate-origins (both command-line flag and enterprise policy) trumps
-  // the opt-out flag.
+std::vector<url::Origin>
+SiteIsolationPolicy::GetIsolatedOriginsFromFieldTrial() {
+  std::vector<url::Origin> origins;
+
+  // Check if site isolation modes are turned off (e.g., due to an opt-out
+  // flag).
   if (IsSiteIsolationDisabled())
     return origins;
 
-  // The feature needs to be checked last, because checking the feature
-  // activates the field trial and assigns the client either to a control or an
-  // experiment group - such assignment should be final.
+  // The feature needs to be checked after the opt-out, because checking the
+  // feature activates the field trial and assigns the client either to a
+  // control or an experiment group - such assignment should be final.
   if (base::FeatureList::IsEnabled(features::kIsolateOrigins)) {
     std::string field_trial_arg = base::GetFieldTrialParamValueByFeature(
         features::kIsolateOrigins,
         features::kIsolateOriginsFieldTrialParamName);
-    std::vector<url::Origin> field_trial_origins =
-        ParseIsolatedOrigins(field_trial_arg);
-    origins.reserve(origins.size() + field_trial_origins.size());
-    std::move(field_trial_origins.begin(), field_trial_origins.end(),
-              std::back_inserter(origins));
+    origins = ParseIsolatedOrigins(field_trial_arg);
   }
+
   return origins;
 }
 
-// static
-std::vector<url::Origin> SiteIsolationPolicy::GetIsolatedOrigins() {
-  std::vector<url::Origin> from_environment =
-      GetIsolatedOriginsFromEnvironment();
+void SiteIsolationPolicy::ApplyGlobalIsolatedOrigins() {
+  ChildProcessSecurityPolicy* policy =
+      ChildProcessSecurityPolicy::GetInstance();
+
+  std::vector<url::Origin> from_cmdline = GetIsolatedOriginsFromCommandLine();
+  policy->AddIsolatedOrigins(
+      from_cmdline,
+      ChildProcessSecurityPolicy::IsolatedOriginSource::COMMAND_LINE);
+
+  std::vector<url::Origin> from_trial = GetIsolatedOriginsFromFieldTrial();
+  policy->AddIsolatedOrigins(
+      from_trial,
+      ChildProcessSecurityPolicy::IsolatedOriginSource::FIELD_TRIAL);
+
   std::vector<url::Origin> from_embedder =
       GetContentClient()->browser()->GetOriginsRequiringDedicatedProcess();
-
-  std::vector<url::Origin> result = std::move(from_environment);
-  result.reserve(result.size() + from_embedder.size());
-  std::move(from_embedder.begin(), from_embedder.end(),
-            std::back_inserter(result));
-  return result;
+  policy->AddIsolatedOrigins(
+      from_embedder,
+      ChildProcessSecurityPolicy::IsolatedOriginSource::BUILT_IN);
 }
 
 // static
diff --git a/content/public/browser/site_isolation_policy.h b/content/public/browser/site_isolation_policy.h
index 4a6dd8e..8ad505e 100644
--- a/content/public/browser/site_isolation_policy.h
+++ b/content/public/browser/site_isolation_policy.h
@@ -57,9 +57,12 @@
   // opting itself into isolation via a header.
   static bool AreDynamicIsolatedOriginsEnabled();
 
-  // Returns the origins to isolate.  See also AreIsolatedOriginsEnabled.
-  // This list applies globally to the whole browser in all profiles.
-  static std::vector<url::Origin> GetIsolatedOrigins();
+  // Applies isolated origins from all available sources, including the
+  // command-line switch, field trials, enterprise policy, and the embedder.
+  // See also AreIsolatedOriginsEnabled. These origins apply globally to the
+  // whole browser in all profiles.  This should be called once on browser
+  // startup.
+  static void ApplyGlobalIsolatedOrigins();
 
   // Records metrics about which site isolation command-line flags are present,
   // and sets up a timer to keep recording them every 24 hours.  This should be
@@ -73,7 +76,8 @@
   SiteIsolationPolicy();  // Not instantiable.
 
   // Gets isolated origins from cmdline and/or from field trial param.
-  static std::vector<url::Origin> GetIsolatedOriginsFromEnvironment();
+  static std::vector<url::Origin> GetIsolatedOriginsFromCommandLine();
+  static std::vector<url::Origin> GetIsolatedOriginsFromFieldTrial();
 
   // Records metrics about which site isolation command-line flags are present.
   static void RecordSiteIsolationFlagUsage();
diff --git a/content/public/browser/web_contents_delegate.cc b/content/public/browser/web_contents_delegate.cc
index 276af7b..1f20105 100644
--- a/content/public/browser/web_contents_delegate.cc
+++ b/content/public/browser/web_contents_delegate.cc
@@ -288,10 +288,11 @@
   return false;
 }
 
-gfx::Size WebContentsDelegate::EnterPictureInPicture(WebContents* web_contents,
-                                                     const viz::SurfaceId&,
-                                                     const gfx::Size&) {
-  return gfx::Size();
+PictureInPictureResult WebContentsDelegate::EnterPictureInPicture(
+    WebContents* web_contents,
+    const viz::SurfaceId&,
+    const gfx::Size&) {
+  return PictureInPictureResult::kNotSupported;
 }
 
 bool WebContentsDelegate::ShouldAllowLazyLoad() {
diff --git a/content/public/browser/web_contents_delegate.h b/content/public/browser/web_contents_delegate.h
index 5c24dc8dd..1a9bc7c 100644
--- a/content/public/browser/web_contents_delegate.h
+++ b/content/public/browser/web_contents_delegate.h
@@ -91,6 +91,15 @@
 
 enum class KeyboardEventProcessingResult;
 
+// Result of an EnterPictureInPicture request.
+enum class PictureInPictureResult {
+  // The request was successful.
+  kSuccess,
+
+  // Picture-in-Picture is not supported by the embedder.
+  kNotSupported,
+};
+
 // Objects implement this interface to get notified about changes in the
 // WebContents and to provide necessary functionality.
 class CONTENT_EXPORT WebContentsDelegate {
@@ -618,10 +627,11 @@
 
   // Notifies the Picture-in-Picture controller that there is a new player
   // entering Picture-in-Picture.
-  // Returns the size of the Picture-in-Picture window.
-  virtual gfx::Size EnterPictureInPicture(WebContents* web_contents,
-                                          const viz::SurfaceId&,
-                                          const gfx::Size& natural_size);
+  // Returns the result of the enter request.
+  virtual PictureInPictureResult EnterPictureInPicture(
+      WebContents* web_contents,
+      const viz::SurfaceId&,
+      const gfx::Size& natural_size);
 
   // Updates the Picture-in-Picture controller with a signal that
   // Picture-in-Picture mode has ended.
diff --git a/content/public/test/content_browser_test_utils.cc b/content/public/test/content_browser_test_utils.cc
index 48138adad..d4f4c17 100644
--- a/content/public/test/content_browser_test_utils.cc
+++ b/content/public/test/content_browser_test_utils.cc
@@ -193,7 +193,9 @@
   }
 
   auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
-  policy->AddIsolatedOrigins(origins_to_isolate);
+  policy->AddIsolatedOrigins(
+      origins_to_isolate,
+      ChildProcessSecurityPolicy::IsolatedOriginSource::TEST);
 
   // Force a BrowsingInstance swap by navigating cross-site (the newly
   // isolated origin only affects *future* BrowsingInstances).
diff --git a/content/public/test/mock_render_process_host.cc b/content/public/test/mock_render_process_host.cc
index ae31f45..3a214589 100644
--- a/content/public/test/mock_render_process_host.cc
+++ b/content/public/test/mock_render_process_host.cc
@@ -393,6 +393,7 @@
 
 void MockRenderProcessHost::CreateURLLoaderFactory(
     const base::Optional<url::Origin>& origin,
+    const net::NetworkIsolationKey& network_isolation_key,
     network::mojom::TrustedURLLoaderHeaderClientPtrInfo header_client,
     network::mojom::URLLoaderFactoryRequest request) {
   url_loader_factory_->Clone(std::move(request));
diff --git a/content/public/test/mock_render_process_host.h b/content/public/test/mock_render_process_host.h
index 1919c8a..9db4fc0 100644
--- a/content/public/test/mock_render_process_host.h
+++ b/content/public/test/mock_render_process_host.h
@@ -26,6 +26,7 @@
 #include "ipc/ipc_test_sink.h"
 #include "media/media_buildflags.h"
 #include "mojo/public/cpp/bindings/associated_interface_ptr.h"
+#include "net/base/network_isolation_key.h"
 #include "services/service_manager/public/cpp/identity.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 
@@ -144,6 +145,7 @@
   mojom::Renderer* GetRendererInterface() override;
   void CreateURLLoaderFactory(
       const base::Optional<url::Origin>& origin,
+      const net::NetworkIsolationKey& network_isolation_key,
       network::mojom::TrustedURLLoaderHeaderClientPtrInfo header_client,
       network::mojom::URLLoaderFactoryRequest request) override;
 
diff --git a/content/public/test/test_synchronous_compositor_android.h b/content/public/test/test_synchronous_compositor_android.h
index dfa3b45..047a586a 100644
--- a/content/public/test/test_synchronous_compositor_android.h
+++ b/content/public/test/test_synchronous_compositor_android.h
@@ -30,7 +30,7 @@
   void ReturnResources(
       uint32_t layer_tree_frame_sink_id,
       const std::vector<viz::ReturnedResource>& resources) override;
-  void DidPresentCompositorFrames(viz::PresentationFeedbackMap feedbacks,
+  void DidPresentCompositorFrames(viz::FrameTimingDetailsMap timing_details,
                                   uint32_t frame_token) override {}
   bool DemandDrawSw(SkCanvas* canvas) override;
   void SetMemoryPolicy(size_t bytes_limit) override {}
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index c489bd6..68ad2d8e 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -564,7 +564,7 @@
     "//services/service_manager/public/cpp",
     "//services/service_manager/public/mojom",
     "//services/tracing/public/cpp",
-    "//services/ws/public/cpp/gpu",
+    "//services/viz/public/cpp/gpu",
     "//skia",
     "//storage/common",
     "//third_party/blink/public:blink",
diff --git a/content/renderer/android/synchronous_compositor_proxy.cc b/content/renderer/android/synchronous_compositor_proxy.cc
index 0ca3132..86853a50 100644
--- a/content/renderer/android/synchronous_compositor_proxy.cc
+++ b/content/renderer/android/synchronous_compositor_proxy.cc
@@ -289,13 +289,13 @@
 
 void SynchronousCompositorProxy::BeginFrame(
     const viz::BeginFrameArgs& args,
-    const viz::PresentationFeedbackMap& presentation_feedbacks) {
+    const viz::FrameTimingDetailsMap& timing_details) {
   if (needs_begin_frame_for_animate_input_) {
     needs_begin_frame_for_animate_input_ = false;
     input_handler_proxy_->SynchronouslyAnimate(args.frame_time);
   }
   if (layer_tree_frame_sink_) {
-    layer_tree_frame_sink_->DidPresentCompositorFrame(presentation_feedbacks);
+    layer_tree_frame_sink_->DidPresentCompositorFrame(timing_details);
     if (needs_begin_frame_for_frame_sink_)
       layer_tree_frame_sink_->BeginFrame(args);
   }
diff --git a/content/renderer/android/synchronous_compositor_proxy.h b/content/renderer/android/synchronous_compositor_proxy.h
index 21c401f..8d52395 100644
--- a/content/renderer/android/synchronous_compositor_proxy.h
+++ b/content/renderer/android/synchronous_compositor_proxy.h
@@ -12,7 +12,7 @@
 #include "base/macros.h"
 #include "base/memory/writable_shared_memory_region.h"
 #include "base/optional.h"
-#include "components/viz/common/presentation_feedback_map.h"
+#include "components/viz/common/frame_timing_details_map.h"
 #include "content/common/input/synchronous_compositor.mojom.h"
 #include "content/public/common/input_event_ack_state.h"
 #include "content/renderer/android/synchronous_layer_tree_frame_sink.h"
@@ -87,9 +87,8 @@
       uint32_t layer_tree_frame_sink_id,
       const std::vector<viz::ReturnedResource>& resources) final;
   void SetScroll(const gfx::ScrollOffset& total_scroll_offset) final;
-  void BeginFrame(
-      const viz::BeginFrameArgs& args,
-      const viz::PresentationFeedbackMap& presentation_feedbacks) final;
+  void BeginFrame(const viz::BeginFrameArgs& args,
+                  const viz::FrameTimingDetailsMap& timing_details) final;
   void SetBeginFrameSourcePaused(bool paused) final;
 
  protected:
diff --git a/content/renderer/android/synchronous_layer_tree_frame_sink.cc b/content/renderer/android/synchronous_layer_tree_frame_sink.cc
index 20d62c2..ddcb13d 100644
--- a/content/renderer/android/synchronous_layer_tree_frame_sink.cc
+++ b/content/renderer/android/synchronous_layer_tree_frame_sink.cc
@@ -537,7 +537,7 @@
 
 void SynchronousLayerTreeFrameSink::OnBeginFrame(
     const viz::BeginFrameArgs& args,
-    const viz::PresentationFeedbackMap& feedbacks) {}
+    const viz::FrameTimingDetailsMap& timing_details) {}
 
 void SynchronousLayerTreeFrameSink::ReclaimResources(
     const std::vector<viz::ReturnedResource>& resources) {
@@ -555,11 +555,12 @@
 }
 
 void SynchronousLayerTreeFrameSink::DidPresentCompositorFrame(
-    const viz::PresentationFeedbackMap& presentation_feedbacks) {
+    const viz::FrameTimingDetailsMap& timing_details) {
   if (!client_)
     return;
-  for (const auto& pair : presentation_feedbacks) {
-    client_->DidPresentCompositorFrame(pair.first, pair.second);
+  for (const auto& pair : timing_details) {
+    client_->DidPresentCompositorFrame(pair.first,
+                                       pair.second.presentation_feedback);
   }
 }
 
diff --git a/content/renderer/android/synchronous_layer_tree_frame_sink.h b/content/renderer/android/synchronous_layer_tree_frame_sink.h
index a41f849..bfabbb8 100644
--- a/content/renderer/android/synchronous_layer_tree_frame_sink.h
+++ b/content/renderer/android/synchronous_layer_tree_frame_sink.h
@@ -20,7 +20,7 @@
 #include "cc/trees/layer_tree_frame_sink.h"
 #include "cc/trees/managed_memory_policy.h"
 #include "components/viz/common/frame_sinks/begin_frame_source.h"
-#include "components/viz/common/presentation_feedback_map.h"
+#include "components/viz/common/frame_timing_details_map.h"
 #include "components/viz/common/quads/compositor_frame.h"
 #include "components/viz/common/surfaces/local_surface_id_allocation.h"
 #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
@@ -115,7 +115,7 @@
   void DidReceiveCompositorFrameAck(
       const std::vector<viz::ReturnedResource>& resources) override;
   void OnBeginFrame(const viz::BeginFrameArgs& args,
-                    const viz::PresentationFeedbackMap& feedbacks) override;
+                    const viz::FrameTimingDetailsMap& timing_details) override;
   void ReclaimResources(
       const std::vector<viz::ReturnedResource>& resources) override;
   void OnBeginFramePausedChanged(bool paused) override;
@@ -124,7 +124,7 @@
   void OnNeedsBeginFrames(bool needs_begin_frames) override;
 
   void DidPresentCompositorFrame(
-      const viz::PresentationFeedbackMap& presentation_feedbacks);
+      const viz::FrameTimingDetailsMap& timing_details);
   void BeginFrame(const viz::BeginFrameArgs& args);
   void SetBeginFrameSourcePaused(bool paused);
   void SetMemoryPolicy(size_t bytes_limit);
diff --git a/content/renderer/media/android/stream_texture_factory.cc b/content/renderer/media/android/stream_texture_factory.cc
index 75af46e..a2ee9ff 100644
--- a/content/renderer/media/android/stream_texture_factory.cc
+++ b/content/renderer/media/android/stream_texture_factory.cc
@@ -10,7 +10,7 @@
 #include "gpu/ipc/client/command_buffer_proxy_impl.h"
 #include "gpu/ipc/client/gpu_channel_host.h"
 #include "gpu/ipc/common/gpu_messages.h"
-#include "services/ws/public/cpp/gpu/context_provider_command_buffer.h"
+#include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
 #include "ui/gfx/geometry/size.h"
 
 namespace content {
@@ -82,12 +82,12 @@
 
 // static
 scoped_refptr<StreamTextureFactory> StreamTextureFactory::Create(
-    scoped_refptr<ws::ContextProviderCommandBuffer> context_provider) {
+    scoped_refptr<viz::ContextProviderCommandBuffer> context_provider) {
   return new StreamTextureFactory(std::move(context_provider));
 }
 
 StreamTextureFactory::StreamTextureFactory(
-    scoped_refptr<ws::ContextProviderCommandBuffer> context_provider)
+    scoped_refptr<viz::ContextProviderCommandBuffer> context_provider)
     : context_provider_(std::move(context_provider)),
       channel_(context_provider_->GetCommandBufferProxy()->channel()) {
   DCHECK(channel_);
diff --git a/content/renderer/media/android/stream_texture_factory.h b/content/renderer/media/android/stream_texture_factory.h
index 9e54672..38ce94d 100644
--- a/content/renderer/media/android/stream_texture_factory.h
+++ b/content/renderer/media/android/stream_texture_factory.h
@@ -26,7 +26,7 @@
 class GpuChannelHost;
 }  // namespace gpu
 
-namespace ws {
+namespace viz {
 class ContextProviderCommandBuffer;
 }
 
@@ -90,7 +90,7 @@
     : public base::RefCounted<StreamTextureFactory> {
  public:
   static scoped_refptr<StreamTextureFactory> Create(
-      scoped_refptr<ws::ContextProviderCommandBuffer> context_provider);
+      scoped_refptr<viz::ContextProviderCommandBuffer> context_provider);
 
   // Create the StreamTextureProxy object. This internally calls
   // CreateSteamTexture with the recieved arguments. CreateSteamTexture
@@ -108,7 +108,7 @@
  private:
   friend class base::RefCounted<StreamTextureFactory>;
   StreamTextureFactory(
-      scoped_refptr<ws::ContextProviderCommandBuffer> context_provider);
+      scoped_refptr<viz::ContextProviderCommandBuffer> context_provider);
   ~StreamTextureFactory();
   // Creates a gpu::StreamTexture and returns its id.  Sets |*texture_id| to the
   // client-side id of the gpu::StreamTexture. The texture is produced into
@@ -116,7 +116,7 @@
   unsigned CreateStreamTexture(unsigned* texture_id,
                                gpu::Mailbox* texture_mailbox);
 
-  scoped_refptr<ws::ContextProviderCommandBuffer> context_provider_;
+  scoped_refptr<viz::ContextProviderCommandBuffer> context_provider_;
   scoped_refptr<gpu::GpuChannelHost> channel_;
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(StreamTextureFactory);
diff --git a/content/renderer/media/gpu/gpu_video_accelerator_factories_impl.cc b/content/renderer/media/gpu/gpu_video_accelerator_factories_impl.cc
index 7dea447..491b692 100644
--- a/content/renderer/media/gpu/gpu_video_accelerator_factories_impl.cc
+++ b/content/renderer/media/gpu/gpu_video_accelerator_factories_impl.cc
@@ -31,7 +31,7 @@
 #include "media/mojo/clients/mojo_video_encode_accelerator.h"
 #include "media/video/video_encode_accelerator.h"
 #include "services/service_manager/public/cpp/connector.h"
-#include "services/ws/public/cpp/gpu/context_provider_command_buffer.h"
+#include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
 #include "third_party/skia/include/core/SkPostConfig.h"
 
 namespace content {
@@ -58,7 +58,7 @@
     scoped_refptr<gpu::GpuChannelHost> gpu_channel_host,
     const scoped_refptr<base::SingleThreadTaskRunner>& main_thread_task_runner,
     const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
-    const scoped_refptr<ws::ContextProviderCommandBuffer>& context_provider,
+    const scoped_refptr<viz::ContextProviderCommandBuffer>& context_provider,
     bool enable_video_gpu_memory_buffers,
     bool enable_media_stream_gpu_memory_buffers,
     bool enable_video_accelerator,
@@ -77,7 +77,7 @@
     scoped_refptr<gpu::GpuChannelHost> gpu_channel_host,
     const scoped_refptr<base::SingleThreadTaskRunner>& main_thread_task_runner,
     const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
-    const scoped_refptr<ws::ContextProviderCommandBuffer>& context_provider,
+    const scoped_refptr<viz::ContextProviderCommandBuffer>& context_provider,
     bool enable_video_gpu_memory_buffers,
     bool enable_media_stream_gpu_memory_buffers,
     bool enable_video_accelerator,
@@ -467,7 +467,7 @@
           .video_encode_accelerator_supported_profiles);
 }
 
-scoped_refptr<ws::ContextProviderCommandBuffer>
+scoped_refptr<viz::ContextProviderCommandBuffer>
 GpuVideoAcceleratorFactoriesImpl::GetMediaContextProvider() {
   return CheckContextLost() ? nullptr : context_provider_;
 }
diff --git a/content/renderer/media/gpu/gpu_video_accelerator_factories_impl.h b/content/renderer/media/gpu/gpu_video_accelerator_factories_impl.h
index 7aa4724..d71fb02 100644
--- a/content/renderer/media/gpu/gpu_video_accelerator_factories_impl.h
+++ b/content/renderer/media/gpu/gpu_video_accelerator_factories_impl.h
@@ -59,7 +59,7 @@
       const scoped_refptr<base::SingleThreadTaskRunner>&
           main_thread_task_runner,
       const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
-      const scoped_refptr<ws::ContextProviderCommandBuffer>& context_provider,
+      const scoped_refptr<viz::ContextProviderCommandBuffer>& context_provider,
       bool enable_video_gpu_memory_buffers,
       bool enable_media_stream_gpu_memory_buffers,
       bool enable_video_accelerator,
@@ -126,7 +126,7 @@
   std::vector<media::VideoEncodeAccelerator::SupportedProfile>
   GetVideoEncodeAcceleratorSupportedProfiles() override;
 
-  scoped_refptr<ws::ContextProviderCommandBuffer> GetMediaContextProvider()
+  scoped_refptr<viz::ContextProviderCommandBuffer> GetMediaContextProvider()
       override;
   gpu::ContextSupport* GetMediaContextProviderContextSupport() override;
 
@@ -145,7 +145,7 @@
       const scoped_refptr<base::SingleThreadTaskRunner>&
           main_thread_task_runner,
       const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
-      const scoped_refptr<ws::ContextProviderCommandBuffer>& context_provider,
+      const scoped_refptr<viz::ContextProviderCommandBuffer>& context_provider,
       bool enable_gpu_memory_buffer_video_frames_for_video,
       bool enable_gpu_memory_buffer_video_frames_for_media_stream,
       bool enable_video_accelerator,
@@ -169,7 +169,7 @@
   // Shared pointer to a shared context provider. It is initially set on main
   // thread, but all subsequent access and destruction should happen only on the
   // media thread.
-  scoped_refptr<ws::ContextProviderCommandBuffer> context_provider_;
+  scoped_refptr<viz::ContextProviderCommandBuffer> context_provider_;
   // Signals if |context_provider_| is alive on the media thread. For use on the
   // main thread.
   bool context_provider_lost_ = false;
diff --git a/content/renderer/media/media_factory.cc b/content/renderer/media/media_factory.cc
index dfad0ae3..77832a3 100644
--- a/content/renderer/media/media_factory.cc
+++ b/content/renderer/media/media_factory.cc
@@ -44,7 +44,7 @@
 #include "mojo/public/cpp/bindings/associated_interface_ptr.h"
 #include "services/service_manager/public/cpp/connect.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
-#include "services/ws/public/cpp/gpu/context_provider_command_buffer.h"
+#include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
 #include "third_party/blink/public/platform/web_surface_layer_bridge.h"
 #include "third_party/blink/public/platform/web_video_frame_submitter.h"
 #include "third_party/blink/public/web/blink.h"
diff --git a/content/renderer/media/stream/webmediaplayer_ms.cc b/content/renderer/media/stream/webmediaplayer_ms.cc
index b7a85e9..481d603 100644
--- a/content/renderer/media/stream/webmediaplayer_ms.cc
+++ b/content/renderer/media/stream/webmediaplayer_ms.cc
@@ -28,7 +28,7 @@
 #include "media/base/video_types.h"
 #include "media/blink/webmediaplayer_util.h"
 #include "media/video/gpu_memory_buffer_video_frame_pool.h"
-#include "services/ws/public/cpp/gpu/context_provider_command_buffer.h"
+#include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
 #include "third_party/blink/public/platform/modules/mediastream/media_stream_audio_track.h"
 #include "third_party/blink/public/platform/modules/mediastream/web_media_stream_audio_renderer.h"
 #include "third_party/blink/public/platform/modules/mediastream/web_media_stream_renderer_factory.h"
diff --git a/content/renderer/media/stream/webmediaplayer_ms_compositor.cc b/content/renderer/media/stream/webmediaplayer_ms_compositor.cc
index 023faa7..aa9b457f 100644
--- a/content/renderer/media/stream/webmediaplayer_ms_compositor.cc
+++ b/content/renderer/media/stream/webmediaplayer_ms_compositor.cc
@@ -24,7 +24,7 @@
 #include "media/base/video_util.h"
 #include "media/filters/video_renderer_algorithm.h"
 #include "media/renderers/paint_canvas_video_renderer.h"
-#include "services/ws/public/cpp/gpu/context_provider_command_buffer.h"
+#include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
 #include "skia/ext/platform_canvas.h"
 #include "third_party/blink/public/platform/web_media_stream.h"
 #include "third_party/blink/public/platform/web_media_stream_source.h"
@@ -54,7 +54,7 @@
         media::PIXEL_FORMAT_I420, frame->coded_size(), frame->visible_rect(),
         frame->natural_size(), frame->timestamp());
 
-    ws::ContextProviderCommandBuffer* const provider =
+    viz::ContextProviderCommandBuffer* const provider =
         RenderThreadImpl::current()->SharedMainThreadContextProvider().get();
     if (!provider) {
       // Return a black frame (yuv = {0, 0x80, 0x80}).
diff --git a/content/renderer/media_recorder/video_track_recorder.cc b/content/renderer/media_recorder/video_track_recorder.cc
index 2894e77..8eedba3 100644
--- a/content/renderer/media_recorder/video_track_recorder.cc
+++ b/content/renderer/media_recorder/video_track_recorder.cc
@@ -24,7 +24,7 @@
 #include "media/base/video_frame.h"
 #include "media/base/video_util.h"
 #include "media/renderers/paint_canvas_video_renderer.h"
-#include "services/ws/public/cpp/gpu/context_provider_command_buffer.h"
+#include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
 #include "skia/ext/platform_canvas.h"
 #include "third_party/libyuv/include/libyuv.h"
 #include "ui/gfx/geometry/size.h"
@@ -267,7 +267,7 @@
   scoped_refptr<media::VideoFrame> frame;
 
   // |context_provider| is null if the GPU process has crashed or isn't there.
-  ws::ContextProviderCommandBuffer* const context_provider =
+  viz::ContextProviderCommandBuffer* const context_provider =
       RenderThreadImpl::current()->SharedMainThreadContextProvider().get();
   if (!context_provider) {
     // Send black frames (yuv = {0, 127, 127}).
diff --git a/content/renderer/pepper/pepper_graphics_2d_host.cc b/content/renderer/pepper/pepper_graphics_2d_host.cc
index c793140..0f024e6c 100644
--- a/content/renderer/pepper/pepper_graphics_2d_host.cc
+++ b/content/renderer/pepper/pepper_graphics_2d_host.cc
@@ -46,7 +46,7 @@
 #include "ppapi/proxy/ppapi_messages.h"
 #include "ppapi/shared_impl/ppb_view_shared.h"
 #include "ppapi/thunk/enter.h"
-#include "services/ws/public/cpp/gpu/context_provider_command_buffer.h"
+#include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
 #include "skia/ext/platform_canvas.h"
 #include "third_party/khronos/GLES2/gl2.h"
 #include "third_party/khronos/GLES2/gl2ext.h"
diff --git a/content/renderer/pepper/video_decoder_shim.cc b/content/renderer/pepper/video_decoder_shim.cc
index 325fd13c..00359563 100644
--- a/content/renderer/pepper/video_decoder_shim.cc
+++ b/content/renderer/pepper/video_decoder_shim.cc
@@ -35,7 +35,7 @@
 #include "media/video/picture.h"
 #include "media/video/video_decode_accelerator.h"
 #include "ppapi/c/pp_errors.h"
-#include "services/ws/public/cpp/gpu/context_provider_command_buffer.h"
+#include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
 #include "third_party/skia/include/gpu/GrTypes.h"
 
 namespace content {
@@ -60,7 +60,7 @@
 // YUV->RGB converter class using a shader and FBO.
 class VideoDecoderShim::YUVConverter {
  public:
-  YUVConverter(scoped_refptr<ws::ContextProviderCommandBuffer>);
+  YUVConverter(scoped_refptr<viz::ContextProviderCommandBuffer>);
   ~YUVConverter();
   bool Initialize();
   void Convert(const media::VideoFrame* frame, GLuint tex_out);
@@ -71,7 +71,7 @@
   GLuint CreateProgram(const char* name, GLuint vshader, GLuint fshader);
   GLuint CreateTexture();
 
-  scoped_refptr<ws::ContextProviderCommandBuffer> context_provider_;
+  scoped_refptr<viz::ContextProviderCommandBuffer> context_provider_;
   gpu::gles2::GLES2Interface* gl_;
   GLuint frame_buffer_;
   GLuint vertex_buffer_;
@@ -101,7 +101,7 @@
 };
 
 VideoDecoderShim::YUVConverter::YUVConverter(
-    scoped_refptr<ws::ContextProviderCommandBuffer> context_provider)
+    scoped_refptr<viz::ContextProviderCommandBuffer> context_provider)
     : context_provider_(std::move(context_provider)),
       gl_(context_provider_->ContextGL()),
       frame_buffer_(0),
diff --git a/content/renderer/pepper/video_decoder_shim.h b/content/renderer/pepper/video_decoder_shim.h
index 99502fa..0136910 100644
--- a/content/renderer/pepper/video_decoder_shim.h
+++ b/content/renderer/pepper/video_decoder_shim.h
@@ -23,7 +23,7 @@
 class SingleThreadTaskRunner;
 }
 
-namespace ws {
+namespace viz {
 class ContextProviderCommandBuffer;
 }
 
@@ -80,7 +80,7 @@
 
   PepperVideoDecoderHost* host_;
   scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_;
-  scoped_refptr<ws::ContextProviderCommandBuffer> context_provider_;
+  scoped_refptr<viz::ContextProviderCommandBuffer> context_provider_;
 
   // The current decoded frame size.
   gfx::Size texture_size_;
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index f432575..474b6ec 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -162,7 +162,7 @@
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "services/service_manager/public/mojom/interface_provider.mojom.h"
-#include "services/ws/public/cpp/gpu/context_provider_command_buffer.h"
+#include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/frame/sandbox_flags.h"
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index 0fa36e7..22a1362 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -135,8 +135,8 @@
 #include "services/network/public/cpp/network_switches.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
-#include "services/ws/public/cpp/gpu/context_provider_command_buffer.h"
-#include "services/ws/public/cpp/gpu/gpu.h"
+#include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
+#include "services/viz/public/cpp/gpu/gpu.h"
 #include "skia/ext/skia_memory_dump_provider.h"
 #include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h"
 #include "third_party/blink/public/platform/web_cache.h"
@@ -313,7 +313,7 @@
                           std::move(request));
 }
 
-scoped_refptr<ws::ContextProviderCommandBuffer> CreateOffscreenContext(
+scoped_refptr<viz::ContextProviderCommandBuffer> CreateOffscreenContext(
     scoped_refptr<gpu::GpuChannelHost> gpu_channel_host,
     gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
     const gpu::SharedMemoryLimits& limits,
@@ -323,7 +323,7 @@
     bool support_oop_rasterization,
     bool support_grcontext,
     bool automatic_flushes,
-    ws::command_buffer_metrics::ContextType type,
+    viz::command_buffer_metrics::ContextType type,
     int32_t stream_id,
     gpu::SchedulingPriority stream_priority) {
   DCHECK(gpu_channel_host);
@@ -348,11 +348,11 @@
   attributes.enable_oop_rasterization = support_oop_rasterization &&
                                         support_raster_interface &&
                                         !support_gles2_interface;
-  return base::MakeRefCounted<ws::ContextProviderCommandBuffer>(
+  return base::MakeRefCounted<viz::ContextProviderCommandBuffer>(
       std::move(gpu_channel_host), gpu_memory_buffer_manager, stream_id,
       stream_priority, gpu::kNullSurfaceHandle,
       GURL("chrome://gpu/RenderThreadImpl::CreateOffscreenContext/" +
-           ws::command_buffer_metrics::ContextTypeToString(type)),
+           viz::command_buffer_metrics::ContextTypeToString(type)),
       automatic_flushes, support_locking, support_grcontext, limits, attributes,
       type);
 }
@@ -731,8 +731,8 @@
       base::BindRepeating(&CreateSingleSampleMetricsProvider,
                           main_thread_runner(), GetConnector()));
 
-  gpu_ = ws::Gpu::Create(GetConnector(), mojom::kBrowserServiceName,
-                         GetIOTaskRunner());
+  gpu_ = viz::Gpu::Create(GetConnector(), mojom::kBrowserServiceName,
+                          GetIOTaskRunner());
 
   resource_dispatcher_.reset(new ResourceDispatcher());
   url_loader_throttle_provider_ =
@@ -1288,12 +1288,12 @@
   bool support_oop_rasterization = false;
   bool support_grcontext = false;
   bool automatic_flushes = false;
-  scoped_refptr<ws::ContextProviderCommandBuffer> media_context_provider =
+  scoped_refptr<viz::ContextProviderCommandBuffer> media_context_provider =
       CreateOffscreenContext(
           gpu_channel_host, GetGpuMemoryBufferManager(), limits,
           support_locking, support_gles2_interface, support_raster_interface,
           support_oop_rasterization, support_grcontext, automatic_flushes,
-          ws::command_buffer_metrics::ContextType::MEDIA, kGpuStreamIdMedia,
+          viz::command_buffer_metrics::ContextType::MEDIA, kGpuStreamIdMedia,
           kGpuStreamPriorityMedia);
 
   const bool enable_video_accelerator =
@@ -1370,12 +1370,12 @@
       gpu_channel_host, GetGpuMemoryBufferManager(), limits, support_locking,
       support_gles2_interface, support_raster_interface,
       support_oop_rasterization, support_grcontext, automatic_flushes,
-      ws::command_buffer_metrics::ContextType::RENDER_COMPOSITOR,
+      viz::command_buffer_metrics::ContextType::RENDER_COMPOSITOR,
       kGpuStreamIdMedia, kGpuStreamPriorityMedia);
   return video_frame_compositor_context_provider_;
 }
 
-scoped_refptr<ws::ContextProviderCommandBuffer>
+scoped_refptr<viz::ContextProviderCommandBuffer>
 RenderThreadImpl::SharedMainThreadContextProvider() {
   DCHECK(IsMainThread());
   if (shared_main_thread_contexts_ &&
@@ -1403,7 +1403,7 @@
       gpu::SharedMemoryLimits(), support_locking, support_gles2_interface,
       support_raster_interface, support_oop_rasterization, support_grcontext,
       automatic_flushes,
-      ws::command_buffer_metrics::ContextType::RENDERER_MAIN_THREAD,
+      viz::command_buffer_metrics::ContextType::RENDERER_MAIN_THREAD,
       kGpuStreamIdDefault, kGpuStreamPriorityDefault);
   auto result = shared_main_thread_contexts_->BindToCurrentThread();
   if (result != gpu::ContextResult::kSuccess)
@@ -1418,7 +1418,7 @@
   if (!stream_texture_factory_.get() ||
       stream_texture_factory_->ContextGL()->GetGraphicsResetStatusKHR() !=
           GL_NO_ERROR) {
-    scoped_refptr<ws::ContextProviderCommandBuffer> shared_context_provider =
+    scoped_refptr<viz::ContextProviderCommandBuffer> shared_context_provider =
         SharedMainThreadContextProvider();
     if (!shared_context_provider) {
       stream_texture_factory_ = nullptr;
@@ -1942,13 +1942,13 @@
   constexpr bool support_locking = false;
   constexpr bool support_grcontext = true;
 
-  scoped_refptr<ws::ContextProviderCommandBuffer> context_provider(
-      new ws::ContextProviderCommandBuffer(
+  scoped_refptr<viz::ContextProviderCommandBuffer> context_provider(
+      new viz::ContextProviderCommandBuffer(
           gpu_channel_host, GetGpuMemoryBufferManager(), kGpuStreamIdDefault,
           kGpuStreamPriorityDefault, gpu::kNullSurfaceHandle, url,
           automatic_flushes, support_locking, support_grcontext, limits,
           attributes,
-          ws::command_buffer_metrics::ContextType::RENDER_COMPOSITOR));
+          viz::command_buffer_metrics::ContextType::RENDER_COMPOSITOR));
 
 #if defined(OS_ANDROID)
   if (GetContentClient()->UsingSynchronousCompositing()) {
@@ -2264,7 +2264,8 @@
       std::move(gpu_channel_host), GetGpuMemoryBufferManager(),
       shared_memory_limits, support_locking, support_gles2_interface,
       support_raster_interface, support_oop_rasterization, support_grcontext,
-      automatic_flushes, ws::command_buffer_metrics::ContextType::RENDER_WORKER,
+      automatic_flushes,
+      viz::command_buffer_metrics::ContextType::RENDER_WORKER,
       kGpuStreamIdWorker, kGpuStreamPriorityWorker);
   auto result = shared_worker_context_provider_->BindToCurrentThread();
   if (result != gpu::ContextResult::kSuccess) {
diff --git a/content/renderer/render_thread_impl.h b/content/renderer/render_thread_impl.h
index e9e0b11..4edeaffb 100644
--- a/content/renderer/render_thread_impl.h
+++ b/content/renderer/render_thread_impl.h
@@ -105,14 +105,11 @@
 
 namespace viz {
 class BeginFrameSource;
-class RasterContextProvider;
-class SyntheticBeginFrameSource;
-}
-
-namespace ws {
 class ContextProviderCommandBuffer;
 class Gpu;
-}  // namespace ws
+class RasterContextProvider;
+class SyntheticBeginFrameSource;
+}  // namespace viz
 
 namespace content {
 class AecDumpMessageFilter;
@@ -361,7 +358,7 @@
 
   media::GpuVideoAcceleratorFactories* GetGpuFactories();
 
-  scoped_refptr<ws::ContextProviderCommandBuffer>
+  scoped_refptr<viz::ContextProviderCommandBuffer>
   SharedMainThreadContextProvider();
 
   // AudioRendererMixerManager instance which manages renderer side mixer
@@ -627,7 +624,7 @@
   scoped_refptr<StreamTextureFactory> stream_texture_factory_;
 #endif
 
-  scoped_refptr<ws::ContextProviderCommandBuffer> shared_main_thread_contexts_;
+  scoped_refptr<viz::ContextProviderCommandBuffer> shared_main_thread_contexts_;
 
   base::ObserverList<RenderThreadObserver>::Unchecked observers_;
 
@@ -646,7 +643,7 @@
   // memory saving mode.
   std::unique_ptr<LowMemoryModeController> low_memory_mode_controller_;
 
-  std::unique_ptr<ws::Gpu> gpu_;
+  std::unique_ptr<viz::Gpu> gpu_;
 
   scoped_refptr<base::SingleThreadTaskRunner>
       main_thread_compositor_task_runner_;
diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc
index 72eb182..bcd0c3e 100644
--- a/content/renderer/renderer_blink_platform_impl.cc
+++ b/content/renderer/renderer_blink_platform_impl.cc
@@ -75,7 +75,7 @@
 #include "services/network/public/cpp/wrapper_shared_url_loader_factory.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
-#include "services/ws/public/cpp/gpu/context_provider_command_buffer.h"
+#include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
 #include "storage/common/database/database_identifier.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/origin_trials/trial_token_validator.h"
@@ -802,15 +802,15 @@
   constexpr bool automatic_flushes = true;
   constexpr bool support_locking = false;
 
-  scoped_refptr<ws::ContextProviderCommandBuffer> provider(
-      new ws::ContextProviderCommandBuffer(
+  scoped_refptr<viz::ContextProviderCommandBuffer> provider(
+      new viz::ContextProviderCommandBuffer(
           std::move(gpu_channel_host),
           RenderThreadImpl::current()->GetGpuMemoryBufferManager(),
           kGpuStreamIdDefault, kGpuStreamPriorityDefault,
           gpu::kNullSurfaceHandle, GURL(top_document_web_url),
           automatic_flushes, support_locking, web_attributes.support_grcontext,
           gpu::SharedMemoryLimits(), attributes,
-          ws::command_buffer_metrics::ContextType::WEBGL));
+          viz::command_buffer_metrics::ContextType::WEBGL));
   return std::make_unique<WebGraphicsContext3DProviderImpl>(
       std::move(provider));
 }
@@ -821,7 +821,7 @@
 RendererBlinkPlatformImpl::CreateSharedOffscreenGraphicsContext3DProvider() {
   auto* thread = RenderThreadImpl::current();
 
-  scoped_refptr<ws::ContextProviderCommandBuffer> provider =
+  scoped_refptr<viz::ContextProviderCommandBuffer> provider =
       thread->SharedMainThreadContextProvider();
   if (!provider)
     return nullptr;
@@ -869,15 +869,15 @@
   constexpr bool support_locking = false;
   constexpr bool support_grcontext = false;
 
-  scoped_refptr<ws::ContextProviderCommandBuffer> provider(
-      new ws::ContextProviderCommandBuffer(
+  scoped_refptr<viz::ContextProviderCommandBuffer> provider(
+      new viz::ContextProviderCommandBuffer(
           std::move(gpu_channel_host),
           RenderThreadImpl::current()->GetGpuMemoryBufferManager(),
           kGpuStreamIdDefault, kGpuStreamPriorityDefault,
           gpu::kNullSurfaceHandle, GURL(top_document_web_url),
           automatic_flushes, support_locking, support_grcontext,
           gpu::SharedMemoryLimits::ForWebGPUContext(), attributes,
-          ws::command_buffer_metrics::ContextType::WEBGPU));
+          viz::command_buffer_metrics::ContextType::WEBGPU));
   return std::make_unique<WebGraphicsContext3DProviderImpl>(
       std::move(provider));
 #endif
diff --git a/content/renderer/webgraphicscontext3d_provider_impl.cc b/content/renderer/webgraphicscontext3d_provider_impl.cc
index 53468d4..2817c4c9 100644
--- a/content/renderer/webgraphicscontext3d_provider_impl.cc
+++ b/content/renderer/webgraphicscontext3d_provider_impl.cc
@@ -13,13 +13,13 @@
 #include "content/public/common/content_switches.h"
 #include "gpu/command_buffer/client/context_support.h"
 #include "gpu/config/gpu_feature_info.h"
-#include "services/ws/public/cpp/gpu/context_provider_command_buffer.h"
+#include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
 #include "third_party/skia/include/gpu/GrContext.h"
 
 namespace content {
 
 WebGraphicsContext3DProviderImpl::WebGraphicsContext3DProviderImpl(
-    scoped_refptr<ws::ContextProviderCommandBuffer> provider)
+    scoped_refptr<viz::ContextProviderCommandBuffer> provider)
     : provider_(std::move(provider)) {}
 
 WebGraphicsContext3DProviderImpl::~WebGraphicsContext3DProviderImpl() {
diff --git a/content/renderer/webgraphicscontext3d_provider_impl.h b/content/renderer/webgraphicscontext3d_provider_impl.h
index b6c6295..1907def 100644
--- a/content/renderer/webgraphicscontext3d_provider_impl.h
+++ b/content/renderer/webgraphicscontext3d_provider_impl.h
@@ -23,13 +23,10 @@
 }  // namespace gpu
 
 namespace viz {
+class ContextProviderCommandBuffer;
 class GLHelper;
 }  // namespace viz
 
-namespace ws {
-class ContextProviderCommandBuffer;
-}  // namespace ws
-
 namespace content {
 
 class CONTENT_EXPORT WebGraphicsContext3DProviderImpl
@@ -37,7 +34,7 @@
       public viz::ContextLostObserver {
  public:
   WebGraphicsContext3DProviderImpl(
-      scoped_refptr<ws::ContextProviderCommandBuffer> provider);
+      scoped_refptr<viz::ContextProviderCommandBuffer> provider);
   ~WebGraphicsContext3DProviderImpl() override;
 
   // WebGraphicsContext3DProvider implementation.
@@ -55,7 +52,7 @@
   cc::ImageDecodeCache* ImageDecodeCache(SkColorType color_type) override;
   gpu::SharedImageInterface* SharedImageInterface() override;
 
-  ws::ContextProviderCommandBuffer* context_provider() const {
+  viz::ContextProviderCommandBuffer* context_provider() const {
     return provider_.get();
   }
 
@@ -63,7 +60,7 @@
   // viz::ContextLostObserver implementation.
   void OnContextLost() override;
 
-  scoped_refptr<ws::ContextProviderCommandBuffer> provider_;
+  scoped_refptr<viz::ContextProviderCommandBuffer> provider_;
   std::unique_ptr<viz::GLHelper> gl_helper_;
   base::RepeatingClosure context_lost_callback_;
   base::flat_map<SkColorType, std::unique_ptr<cc::ImageDecodeCache>>
diff --git a/content/shell/browser/shell.cc b/content/shell/browser/shell.cc
index 4f881c6..92ec8d4e 100644
--- a/content/shell/browser/shell.cc
+++ b/content/shell/browser/shell.cc
@@ -25,6 +25,7 @@
 #include "content/public/browser/devtools_agent_host.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/picture_in_picture_window_controller.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/render_widget_host.h"
@@ -618,13 +619,20 @@
   return allowed_per_prefs || allowed_by_test;
 }
 
-gfx::Size Shell::EnterPictureInPicture(content::WebContents* web_contents,
-                                       const viz::SurfaceId& surface_id,
-                                       const gfx::Size& natural_size) {
-  // During tests, returning a fake window size (same aspect ratio) to pretend
-  // the window was created and allow tests to run accordingly.
-  return switches::IsRunWebTestsSwitchPresent() ? natural_size
-                                                : gfx::Size(0, 0);
+PictureInPictureResult Shell::EnterPictureInPicture(
+    content::WebContents* web_contents,
+    const viz::SurfaceId& surface_id,
+    const gfx::Size& natural_size) {
+  // During tests, returning success to pretend the window was created and allow
+  // tests to run accordingly.
+  if (!switches::IsRunWebTestsSwitchPresent())
+    return PictureInPictureResult::kNotSupported;
+
+  auto* controller =
+      PictureInPictureWindowController::GetOrCreateForWebContents(web_contents);
+  controller->EmbedSurface(surface_id, natural_size);
+
+  return PictureInPictureResult::kSuccess;
 }
 
 bool Shell::ShouldResumeRequestsForCreatedWindow() {
diff --git a/content/shell/browser/shell.h b/content/shell/browser/shell.h
index c9103f60f..4499fbe 100644
--- a/content/shell/browser/shell.h
+++ b/content/shell/browser/shell.h
@@ -197,9 +197,10 @@
                                          bool allowed_per_prefs,
                                          const url::Origin& origin,
                                          const GURL& resource_url) override;
-  gfx::Size EnterPictureInPicture(content::WebContents* web_contents,
-                                  const viz::SurfaceId&,
-                                  const gfx::Size& natural_size) override;
+  PictureInPictureResult EnterPictureInPicture(
+      content::WebContents* web_contents,
+      const viz::SurfaceId&,
+      const gfx::Size& natural_size) override;
   bool ShouldResumeRequestsForCreatedWindow() override;
 
   static gfx::Size GetShellDefaultSize();
diff --git a/content/shell/browser/shell_content_browser_client.cc b/content/shell/browser/shell_content_browser_client.cc
index 3920f7a8..854c4d3 100644
--- a/content/shell/browser/shell_content_browser_client.cc
+++ b/content/shell/browser/shell_content_browser_client.cc
@@ -76,9 +76,7 @@
 #endif
 
 #if defined(OS_CHROMEOS)
-// TODO(https://crbug.com/784179): Remove nogncheck.
 #include "content/public/browser/context_factory.h"
-#include "content/public/browser/gpu_interface_provider_factory.h"
 #endif
 
 #if defined(OS_LINUX) || defined(OS_ANDROID)
diff --git a/content/shell/browser/web_test/web_test_content_browser_client.cc b/content/shell/browser/web_test/web_test_content_browser_client.cc
index 0c8320a..b84c8f5 100644
--- a/content/shell/browser/web_test/web_test_content_browser_client.cc
+++ b/content/shell/browser/web_test/web_test_content_browser_client.cc
@@ -69,8 +69,10 @@
   void Hide() override {}
   bool IsVisible() const override { return false; }
   bool IsAlwaysOnTop() const override { return false; }
-  gfx::Rect GetBounds() const override { return gfx::Rect(); }
-  void UpdateVideoSize(const gfx::Size& natural_size) override {}
+  gfx::Rect GetBounds() const override { return gfx::Rect(size_); }
+  void UpdateVideoSize(const gfx::Size& natural_size) override {
+    size_ = natural_size;
+  }
   void SetPlaybackState(PlaybackState playback_state) override {}
   void SetAlwaysHidePlayPauseButton(bool is_visible) override {}
   void SetMutedState(MutedState muted_state) override {}
@@ -81,6 +83,8 @@
   cc::Layer* GetLayerForTesting() override { return nullptr; }
 
  private:
+  gfx::Size size_;
+
   DISALLOW_COPY_AND_ASSIGN(TestOverlayWindow);
 };
 
diff --git a/content/shell/renderer/web_test/blink_test_runner.cc b/content/shell/renderer/web_test/blink_test_runner.cc
index 45347247..4b5c304 100644
--- a/content/shell/renderer/web_test/blink_test_runner.cc
+++ b/content/shell/renderer/web_test/blink_test_runner.cc
@@ -535,12 +535,14 @@
 }
 
 void BlinkTestRunner::OnLayoutDumpCompleted(std::string completed_layout_dump) {
+  CHECK(waiting_for_layout_dump_results_);
   dump_result_->layout.emplace(completed_layout_dump);
   waiting_for_layout_dump_results_ = false;
   CaptureDumpComplete();
 }
 
 void BlinkTestRunner::OnPixelsDumpCompleted(const SkBitmap& snapshot) {
+  CHECK(waiting_for_pixels_dump_result_);
   DCHECK_NE(0, snapshot.info().width());
   DCHECK_NE(0, snapshot.info().height());
 
diff --git a/content/shell/test_runner/test_runner.cc b/content/shell/test_runner/test_runner.cc
index 53aed26..691d9b3 100644
--- a/content/shell/test_runner/test_runner.cc
+++ b/content/shell/test_runner/test_runner.cc
@@ -623,6 +623,7 @@
 }
 
 void TestRunnerBindings::LogToStderr(const std::string& output) {
+  TRACE_EVENT1("shell", "TestRunner::LogToStderr", "output", output);
   LOG(ERROR) << output;
 }
 
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 0190f31de..cae9058 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -380,7 +380,6 @@
     "//net:test_support",
     "//services/audio/public/mojom",
     "//services/device/public/mojom",
-    "//services/ws/public/cpp/gpu",
 
     # TODO(jam): remove this by adding a public header for the NetworkContext
     # public testing method.
@@ -389,6 +388,7 @@
     "//services/network/public/mojom",
     "//services/proxy_resolver:lib",
     "//services/service_manager/public/cpp",
+    "//services/viz/public/cpp/gpu",
     "//services/viz/public/interfaces",
     "//skia",
     "//storage/browser",
@@ -1081,7 +1081,7 @@
     "//services/video_capture/public/cpp:mocks",
     "//services/video_capture/public/mojom:constants",
     "//services/viz/privileged/interfaces",
-    "//services/ws/public/cpp/gpu",
+    "//services/viz/public/cpp/gpu",
     "//storage/browser",
     "//storage/browser:test_support",
     "//testing/gmock",
diff --git a/content/test/DEPS b/content/test/DEPS
index e743fb9..e133e65 100644
--- a/content/test/DEPS
+++ b/content/test/DEPS
@@ -45,10 +45,10 @@
   "web_test_support.cc": [
     "+content/shell/test_runner",
     "+content/shell/common/shell_switches.h",
-    "+services/ws/public/cpp/gpu",
+    "+services/viz/public/cpp/gpu",
   ],
   "gpu_browsertest_helpers.cc": [
-    "+services/ws/public/cpp/gpu/command_buffer_metrics.h",
-    "+services/ws/public/cpp/gpu/context_provider_command_buffer.h",
+    "+services/viz/public/cpp/gpu/command_buffer_metrics.h",
+    "+services/viz/public/cpp/gpu/context_provider_command_buffer.h",
   ],
 }
diff --git a/content/test/data/accessibility/event/aria-menuitem-focus-expected-auralinux.txt b/content/test/data/accessibility/event/aria-menuitem-focus-expected-auralinux.txt
new file mode 100644
index 0000000..ff58601
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-menuitem-focus-expected-auralinux.txt
@@ -0,0 +1,2 @@
+FOCUS-EVENT role=ROLE_MENU_ITEM name='Edit' ENABLED,EXPANDABLE,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE
+STATE-CHANGE:FOCUSED:TRUE role=ROLE_MENU_ITEM name='Edit' ENABLED,EXPANDABLE,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE
diff --git a/content/test/data/accessibility/event/aria-menuitem-focus.html b/content/test/data/accessibility/event/aria-menuitem-focus.html
new file mode 100644
index 0000000..b00a8125
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-menuitem-focus.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<body>
+<div id="menubar" role="menubar" tabindex="0" aria-activedescendant="file">
+  <span id="file" role="menuitem" aria-expanded="false">File</span>
+  <span id="edit" role="menuitem" aria-expanded="false">Edit</span>
+</div>
+<script>
+  var menubar = document.getElementById('menubar');
+  menubar.focus();
+  function go() {
+    menubar.setAttribute('aria-activedescendant', 'edit');
+  }
+</script>
+</body>
+</html>
diff --git a/content/test/fake_renderer_compositor_frame_sink.h b/content/test/fake_renderer_compositor_frame_sink.h
index 65d479c..b03770b5 100644
--- a/content/test/fake_renderer_compositor_frame_sink.h
+++ b/content/test/fake_renderer_compositor_frame_sink.h
@@ -5,7 +5,7 @@
 #ifndef CONTENT_TEST_FAKE_RENDERER_COMPOSITOR_FRAME_SINK_H_
 #define CONTENT_TEST_FAKE_RENDERER_COMPOSITOR_FRAME_SINK_H_
 
-#include "components/viz/common/presentation_feedback_map.h"
+#include "components/viz/common/frame_timing_details_map.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "services/viz/public/interfaces/compositing/compositor_frame_sink.mojom.h"
 
@@ -30,7 +30,8 @@
   void DidReceiveCompositorFrameAck(
       const std::vector<viz::ReturnedResource>& resources) override;
   void OnBeginFrame(const viz::BeginFrameArgs& args,
-                    const viz::PresentationFeedbackMap& feedbacks) override {}
+                    const viz::FrameTimingDetailsMap& timing_details) override {
+  }
   void OnBeginFramePausedChanged(bool paused) override {}
   void ReclaimResources(
       const std::vector<viz::ReturnedResource>& resources) override;
diff --git a/content/test/gpu_browsertest_helpers.cc b/content/test/gpu_browsertest_helpers.cc
index e2e3b6c..94e0ffc 100644
--- a/content/test/gpu_browsertest_helpers.cc
+++ b/content/test/gpu_browsertest_helpers.cc
@@ -15,8 +15,8 @@
 #include "gpu/command_buffer/common/context_creation_attribs.h"
 #include "gpu/ipc/client/gpu_channel_host.h"
 #include "gpu/ipc/common/surface_handle.h"
-#include "services/ws/public/cpp/gpu/command_buffer_metrics.h"
-#include "services/ws/public/cpp/gpu/context_provider_command_buffer.h"
+#include "services/viz/public/cpp/gpu/command_buffer_metrics.h"
+#include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
 #include "url/gurl.h"
 
 namespace content {
@@ -47,7 +47,7 @@
   return gpu_channel_host;
 }
 
-scoped_refptr<ws::ContextProviderCommandBuffer> GpuBrowsertestCreateContext(
+scoped_refptr<viz::ContextProviderCommandBuffer> GpuBrowsertestCreateContext(
     scoped_refptr<gpu::GpuChannelHost> gpu_channel_host) {
   gpu::GpuChannelEstablishFactory* factory =
       content::BrowserMainLoop::GetInstance()->gpu_channel_establish_factory();
@@ -63,12 +63,12 @@
   constexpr bool automatic_flushes = false;
   constexpr bool support_locking = false;
   constexpr bool support_grcontext = true;
-  return base::MakeRefCounted<ws::ContextProviderCommandBuffer>(
+  return base::MakeRefCounted<viz::ContextProviderCommandBuffer>(
       std::move(gpu_channel_host), factory->GetGpuMemoryBufferManager(),
       content::kGpuStreamIdDefault, content::kGpuStreamPriorityDefault,
       gpu::kNullSurfaceHandle, GURL(), automatic_flushes, support_locking,
       support_grcontext, gpu::SharedMemoryLimits(), attributes,
-      ws::command_buffer_metrics::ContextType::FOR_TESTING);
+      viz::command_buffer_metrics::ContextType::FOR_TESTING);
 }
 
 }  // namespace content
diff --git a/content/test/gpu_browsertest_helpers.h b/content/test/gpu_browsertest_helpers.h
index 8654d62..aa48873 100644
--- a/content/test/gpu_browsertest_helpers.h
+++ b/content/test/gpu_browsertest_helpers.h
@@ -11,7 +11,7 @@
 class GpuChannelHost;
 }
 
-namespace ws {
+namespace viz {
 class ContextProviderCommandBuffer;
 }
 
@@ -24,7 +24,7 @@
 
 // Creates a new ContextProviderCommandBuffer using the provided
 // GpuChannelHost.
-scoped_refptr<ws::ContextProviderCommandBuffer> GpuBrowsertestCreateContext(
+scoped_refptr<viz::ContextProviderCommandBuffer> GpuBrowsertestCreateContext(
     scoped_refptr<gpu::GpuChannelHost> gpu_channel_host);
 
 }  // namespace content
diff --git a/device/vr/public/mojom/vr_service.mojom b/device/vr/public/mojom/vr_service.mojom
index a284fa9..188028036 100644
--- a/device/vr/public/mojom/vr_service.mojom
+++ b/device/vr/public/mojom/vr_service.mojom
@@ -266,6 +266,17 @@
   array<XRPlanePointData> polygon;
 };
 
+// Struct containing information about existing & updated planes in frame N.
+// If the plane was removed between frame N-1 and N, its ID will not be present
+// in all_plane_ids.
+struct XRPlaneDetectionData {
+  // Array with ids of all planes.
+  array<int32> all_planes_ids;
+
+  // Array with plane data for updated planes.
+  array<XRPlaneData> updated_planes_data;
+};
+
 // The data needed for each animation frame of an XRSession.
 struct XRFrameData {
   // General XRSession value
@@ -310,9 +321,8 @@
   bool stage_parameters_updated;
   VRStageParameters? stage_parameters;
 
-  // Array containing detected planes data. If plane was detected in frame N and
-  // is no longer detected in frame N+1, it will not be present in the array.
-  array<XRPlaneData>? detected_planes;
+  // Detected plane information. Only present if plane detection is enabled.
+  XRPlaneDetectionData? detected_planes_data;
 };
 
 enum VRDisplayEventReason {
diff --git a/docs/design/gpu_synchronization.md b/docs/design/gpu_synchronization.md
index a4cf597..fff5d9b 100644
--- a/docs/design/gpu_synchronization.md
+++ b/docs/design/gpu_synchronization.md
@@ -129,7 +129,7 @@
 Command scheduling granuarity is at the stream level, and a client can choose to
 create and use multiple streams with different stream priorities. Stream IDs are
 arbitrary integers assigned by the client at creation time, see for example the
-[ws::ContextProviderCommandBuffer](/services/ws/public/cpp/gpu/context_provider_command_buffer.h)
+[viz::ContextProviderCommandBuffer](/services/viz/public/cpp/gpu/context_provider_command_buffer.h)
 constructor.
 
 The CHROMIUM sync token is intended to order operations among command buffer GL
diff --git a/extensions/browser/api/web_request/web_request_permissions_unittest.cc b/extensions/browser/api/web_request/web_request_permissions_unittest.cc
index 812ef51..129a291b 100644
--- a/extensions/browser/api/web_request/web_request_permissions_unittest.cc
+++ b/extensions/browser/api/web_request/web_request_permissions_unittest.cc
@@ -33,6 +33,10 @@
                HIDE_MAIN_FRAME_NAVIGATION | HIDE_BROWSER_SUB_RESOURCE_REQUEST,
   };
 
+  // The InfoMap requires methods to be called on the IO thread. Fake it.
+  content::TestBrowserThreadBundle thread_bundle(
+      content::TestBrowserThreadBundle::IO_MAINLOOP);
+
   ExtensionsAPIClient api_client;
   auto info_map = base::MakeRefCounted<extensions::InfoMap>();
 
diff --git a/extensions/browser/app_window/app_delegate.h b/extensions/browser/app_window/app_delegate.h
index 1218ec59..be2019a9 100644
--- a/extensions/browser/app_window/app_delegate.h
+++ b/extensions/browser/app_window/app_delegate.h
@@ -18,6 +18,7 @@
 }  // namespace blink
 
 namespace content {
+enum class PictureInPictureResult;
 class BrowserContext;
 class ColorChooser;
 class FileSelectListener;
@@ -105,10 +106,11 @@
 
   // Notifies the Picture-in-Picture controller that there is a new player
   // entering Picture-in-Picture.
-  // Returns the size of the Picture-in-Picture window.
-  virtual gfx::Size EnterPictureInPicture(content::WebContents* web_contents,
-                                          const viz::SurfaceId& surface_id,
-                                          const gfx::Size& natural_size) = 0;
+  // Returns the result of the enter request.
+  virtual content::PictureInPictureResult EnterPictureInPicture(
+      content::WebContents* web_contents,
+      const viz::SurfaceId& surface_id,
+      const gfx::Size& natural_size) = 0;
 
   // Updates the Picture-in-Picture controller with a signal that
   // Picture-in-Picture mode has ended.
diff --git a/extensions/browser/app_window/app_window.cc b/extensions/browser/app_window/app_window.cc
index f104301..6961d34 100644
--- a/extensions/browser/app_window/app_window.cc
+++ b/extensions/browser/app_window/app_window.cc
@@ -444,9 +444,10 @@
   return app_delegate_->TakeFocus(source, reverse);
 }
 
-gfx::Size AppWindow::EnterPictureInPicture(content::WebContents* web_contents,
-                                           const viz::SurfaceId& surface_id,
-                                           const gfx::Size& natural_size) {
+content::PictureInPictureResult AppWindow::EnterPictureInPicture(
+    content::WebContents* web_contents,
+    const viz::SurfaceId& surface_id,
+    const gfx::Size& natural_size) {
   return app_delegate_->EnterPictureInPicture(web_contents, surface_id,
                                               natural_size);
 }
diff --git a/extensions/browser/app_window/app_window.h b/extensions/browser/app_window/app_window.h
index 1ace1a10..5152b343 100644
--- a/extensions/browser/app_window/app_window.h
+++ b/extensions/browser/app_window/app_window.h
@@ -446,9 +446,10 @@
       content::RenderFrameHost* frame,
       const content::BluetoothChooser::EventHandler& event_handler) override;
   bool TakeFocus(content::WebContents* source, bool reverse) override;
-  gfx::Size EnterPictureInPicture(content::WebContents* web_contents,
-                                  const viz::SurfaceId& surface_id,
-                                  const gfx::Size& natural_size) override;
+  content::PictureInPictureResult EnterPictureInPicture(
+      content::WebContents* web_contents,
+      const viz::SurfaceId& surface_id,
+      const gfx::Size& natural_size) override;
   void ExitPictureInPicture() override;
 
   // content::WebContentsObserver implementation.
diff --git a/extensions/browser/extension_host.cc b/extensions/browser/extension_host.cc
index b126f42..5dc2d56 100644
--- a/extensions/browser/extension_host.cc
+++ b/extensions/browser/extension_host.cc
@@ -463,7 +463,7 @@
   return view_type == extensions::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE;
 }
 
-gfx::Size ExtensionHost::EnterPictureInPicture(
+content::PictureInPictureResult ExtensionHost::EnterPictureInPicture(
     content::WebContents* web_contents,
     const viz::SurfaceId& surface_id,
     const gfx::Size& natural_size) {
diff --git a/extensions/browser/extension_host.h b/extensions/browser/extension_host.h
index f4bbe0b1..c0f80fd 100644
--- a/extensions/browser/extension_host.h
+++ b/extensions/browser/extension_host.h
@@ -128,9 +128,10 @@
                                   const GURL& security_origin,
                                   blink::mojom::MediaStreamType type) override;
   bool IsNeverVisible(content::WebContents* web_contents) override;
-  gfx::Size EnterPictureInPicture(content::WebContents* web_contents,
-                                  const viz::SurfaceId& surface_id,
-                                  const gfx::Size& natural_size) override;
+  content::PictureInPictureResult EnterPictureInPicture(
+      content::WebContents* web_contents,
+      const viz::SurfaceId& surface_id,
+      const gfx::Size& natural_size) override;
   void ExitPictureInPicture() override;
 
   // ExtensionRegistryObserver:
diff --git a/extensions/browser/extension_host_delegate.h b/extensions/browser/extension_host_delegate.h
index f075bb4..6e96eb3 100644
--- a/extensions/browser/extension_host_delegate.h
+++ b/extensions/browser/extension_host_delegate.h
@@ -12,6 +12,7 @@
 #include "ui/base/window_open_disposition.h"
 
 namespace content {
+enum class PictureInPictureResult;
 class JavaScriptDialogManager;
 class RenderFrameHost;
 class WebContents;
@@ -80,10 +81,11 @@
 
   // Notifies the Picture-in-Picture controller that there is a new player
   // entering Picture-in-Picture.
-  // Returns the size of the Picture-in-Picture window.
-  virtual gfx::Size EnterPictureInPicture(content::WebContents* web_contents,
-                                          const viz::SurfaceId& surface_id,
-                                          const gfx::Size& natural_size) = 0;
+  // Returns the result of the enter request.
+  virtual content::PictureInPictureResult EnterPictureInPicture(
+      content::WebContents* web_contents,
+      const viz::SurfaceId& surface_id,
+      const gfx::Size& natural_size) = 0;
 
   // Updates the Picture-in-Picture controller with a signal that
   // Picture-in-Picture mode has ended.
diff --git a/extensions/browser/info_map.cc b/extensions/browser/info_map.cc
index 7f2ba3d..83b139b6 100644
--- a/extensions/browser/info_map.cc
+++ b/extensions/browser/info_map.cc
@@ -238,11 +238,6 @@
   process_map_.set_is_lock_screen_context(is_lock_screen_context);
 }
 
-InfoMap::~InfoMap() {
-  if (quota_service_) {
-    BrowserThread::DeleteSoon(
-        BrowserThread::IO, FROM_HERE, quota_service_.release());
-  }
-}
+InfoMap::~InfoMap() = default;
 
 }  // namespace extensions
diff --git a/extensions/browser/info_map.h b/extensions/browser/info_map.h
index f130818..c2f0fac 100644
--- a/extensions/browser/info_map.h
+++ b/extensions/browser/info_map.h
@@ -10,6 +10,7 @@
 
 #include "base/memory/ref_counted.h"
 #include "base/time/time.h"
+#include "content/public/browser/browser_thread.h"
 #include "extensions/browser/api/declarative_net_request/ruleset_manager.h"
 #include "extensions/browser/process_map.h"
 #include "extensions/browser/quota_service.h"
@@ -25,9 +26,11 @@
 class Extension;
 
 // Contains extension data that needs to be accessed on the IO thread. It can
-// be created/destroyed on any thread, but all other methods must be called on
-// the IO thread.
-class InfoMap : public base::RefCountedThreadSafe<InfoMap> {
+// be created on any thread, but all other methods and destructor must be called
+// on the IO thread.
+class InfoMap : public base::RefCountedThreadSafe<
+                    InfoMap,
+                    content::BrowserThread::DeleteOnIOThread> {
  public:
   InfoMap();
 
@@ -95,7 +98,9 @@
   void SetIsLockScreenContext(bool is_lock_screen_context);
 
  private:
-  friend class base::RefCountedThreadSafe<InfoMap>;
+  friend struct content::BrowserThread::DeleteOnThread<
+      content::BrowserThread::IO>;
+  friend class base::DeleteHelper<InfoMap>;
 
   // Extra dynamic data related to an extension.
   struct ExtraData;
diff --git a/extensions/common/api/automation.idl b/extensions/common/api/automation.idl
index 7ecec92..1a4a24a75 100644
--- a/extensions/common/api/automation.idl
+++ b/extensions/common/api/automation.idl
@@ -506,6 +506,22 @@
     DOMString description;
   };
 
+  // The string which the indices are relative to is not included in this
+  // structure. See documentation on $(ref:languageAnnotationForStringAttribute)
+  // for details on how to associate this object with a string.
+  // Also, the start and end indices always point to the first code unit of a
+  // valid code-point.
+  dictionary LanguageSpan {
+    // Inclusive start index of substring that contains language.
+    long startIndex;
+    // Exclusive end index of substring that contains language.
+    long endIndex;
+    // Detected language for substring.
+    DOMString language;
+    // Probability that language is correct.
+    double probability;
+  };
+
   // A single node in an Automation tree.
   [nocompile, noinline_doc] dictionary AutomationNode {
     // The root node of the tree containing this AutomationNode.
@@ -1127,5 +1143,16 @@
     // the value where the selection starts or ends, respectively.
     [nocompile] static void setDocumentSelection(
         SetDocumentSelectionParams params);
+
+    // Returns the detected languages for the provided string attribute as an
+    // array of LanguageSpan objects. There are several guarantees about the
+    // format of the LanguageSpan array:
+    // 1. Is either empty or contains LanguageSpans that cover all indices in
+    // the associated string attribute value.
+    // 2. Is sorted by increasing startIndex (those with smaller startIndex
+    // appear first).
+    // 3. LanguageSpans are non-overlapping and contain exactly one language.
+    [nocompile] LanguageSpan[] languageAnnotationForStringAttribute(
+        DOMString attribute);
   };
 };
diff --git a/extensions/renderer/api/automation/automation_internal_custom_bindings.cc b/extensions/renderer/api/automation/automation_internal_custom_bindings.cc
index 57d19cbd..fc96fa2 100644
--- a/extensions/renderer/api/automation/automation_internal_custom_bindings.cc
+++ b/extensions/renderer/api/automation/automation_internal_custom_bindings.cc
@@ -16,6 +16,7 @@
 #include "base/bind.h"
 #include "base/i18n/string_search.h"
 #include "base/macros.h"
+#include "base/strings/utf_offset_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/values.h"
@@ -37,6 +38,7 @@
 #include "ui/accessibility/ax_enum_util.h"
 #include "ui/accessibility/ax_enums.mojom.h"
 #include "ui/accessibility/ax_event.h"
+#include "ui/accessibility/ax_language_info.h"
 #include "ui/accessibility/ax_node.h"
 #include "ui/accessibility/ax_role_properties.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -1094,6 +1096,50 @@
                                            v8::NewStringType::kNormal)
                        .ToLocalChecked());
       });
+
+  RouteNodeIDPlusAttributeFunction(
+      "GetLanguageAnnotationForStringAttribute",
+      [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
+         ui::AXTree* tree, ui::AXNode* node,
+         const std::string& attribute_name) {
+        ax::mojom::StringAttribute attr =
+            ui::ParseStringAttribute(attribute_name.c_str());
+        if (attr == ax::mojom::StringAttribute::kNone) {
+          // Set result as empty array.
+          result.Set(v8::Array::New(isolate, 0));
+          return;
+        }
+        std::vector<ui::LanguageSpan> language_annotation =
+            node->GetLanguageAnnotationForStringAttribute(attr);
+        const std::string& attribute_value = node->GetStringAttribute(attr);
+        // Build array.
+        v8::Local<v8::Context> context = isolate->GetCurrentContext();
+        v8::Local<v8::Array> array_result(
+            v8::Array::New(isolate, language_annotation.size()));
+        std::vector<size_t> offsets_for_adjustment(2, 0);
+        for (size_t i = 0; i < language_annotation.size(); ++i) {
+          offsets_for_adjustment[0] =
+              static_cast<size_t>(language_annotation[i].start_index);
+          offsets_for_adjustment[1] =
+              static_cast<size_t>(language_annotation[i].end_index);
+          // Convert UTF-8 offsets into UTF-16 offsets, since these objects
+          // will be used in Javascript.
+          base::UTF8ToUTF16AndAdjustOffsets(attribute_value,
+                                            &offsets_for_adjustment);
+
+          gin::DataObjectBuilder span(isolate);
+          span.Set("startIndex", static_cast<int>(offsets_for_adjustment[0]));
+          span.Set("endIndex", static_cast<int>(offsets_for_adjustment[1]));
+          span.Set("language", language_annotation[i].language);
+          span.Set("probability", language_annotation[i].probability);
+          array_result
+              ->CreateDataProperty(context, static_cast<uint32_t>(i),
+                                   span.Build())
+              .Check();
+        }
+        result.Set(array_result);
+      });
+
   RouteNodeIDFunction(
       "GetCustomActions",
       [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
diff --git a/extensions/renderer/resources/automation/automation_node.js b/extensions/renderer/resources/automation/automation_node.js
index 85d1174..e2d6107 100644
--- a/extensions/renderer/resources/automation/automation_node.js
+++ b/extensions/renderer/resources/automation/automation_node.js
@@ -456,6 +456,16 @@
  */
 var GetDetectedLanguage = natives.GetDetectedLanguage;
 
+/**
+ * @param {string} axTreeId The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @param {string} attr The name of the string attribute.
+ * @return {!Array<{startIndex: number, endIndex: number, language: string,
+ * probability: number}>}
+ */
+var GetLanguageAnnotationForStringAttribute =
+    natives.GetLanguageAnnotationForStringAttribute;
+
 var logging = requireNative('logging');
 var utils = require('utils');
 
@@ -648,6 +658,11 @@
     return GetDetectedLanguage(this.treeID, this.id)
   },
 
+  languageAnnotationForStringAttribute: function(attributeName) {
+    return GetLanguageAnnotationForStringAttribute(this.treeID,
+        this.id, attributeName);
+  },
+
   get customActions() {
     return GetCustomActions(this.treeID, this.id);
   },
@@ -1663,6 +1678,7 @@
     'domQuerySelector',
     'toString',
     'boundsForRange',
+    'languageAnnotationForStringAttribute',
   ],
   readonly: $Array.concat(
       publicAttributes,
diff --git a/extensions/shell/browser/shell_app_delegate.cc b/extensions/shell/browser/shell_app_delegate.cc
index e4f1f252..e47bc20 100644
--- a/extensions/shell/browser/shell_app_delegate.cc
+++ b/extensions/shell/browser/shell_app_delegate.cc
@@ -6,6 +6,7 @@
 
 #include "content/public/browser/file_select_listener.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_delegate.h"
 #include "extensions/browser/media_capture_util.h"
 #include "extensions/common/constants.h"
 #include "extensions/shell/browser/shell_extension_web_contents_observer.h"
@@ -109,12 +110,12 @@
   return false;
 }
 
-gfx::Size ShellAppDelegate::EnterPictureInPicture(
+content::PictureInPictureResult ShellAppDelegate::EnterPictureInPicture(
     content::WebContents* web_contents,
     const viz::SurfaceId& surface_id,
     const gfx::Size& natural_size) {
   NOTREACHED();
-  return gfx::Size();
+  return content::PictureInPictureResult::kNotSupported;
 }
 
 void ShellAppDelegate::ExitPictureInPicture() {
diff --git a/extensions/shell/browser/shell_app_delegate.h b/extensions/shell/browser/shell_app_delegate.h
index 5cf5d83..f03a9c5 100644
--- a/extensions/shell/browser/shell_app_delegate.h
+++ b/extensions/shell/browser/shell_app_delegate.h
@@ -53,9 +53,10 @@
   void OnHide() override {}
   void OnShow() override {}
   bool TakeFocus(content::WebContents* web_contents, bool reverse) override;
-  gfx::Size EnterPictureInPicture(content::WebContents* web_contents,
-                                  const viz::SurfaceId& surface_id,
-                                  const gfx::Size& natural_size) override;
+  content::PictureInPictureResult EnterPictureInPicture(
+      content::WebContents* web_contents,
+      const viz::SurfaceId& surface_id,
+      const gfx::Size& natural_size) override;
   void ExitPictureInPicture() override;
 
  private:
diff --git a/extensions/shell/browser/shell_extension_host_delegate.cc b/extensions/shell/browser/shell_extension_host_delegate.cc
index 4818ae4..26c2522 100644
--- a/extensions/shell/browser/shell_extension_host_delegate.cc
+++ b/extensions/shell/browser/shell_extension_host_delegate.cc
@@ -6,6 +6,7 @@
 
 #include "base/lazy_instance.h"
 #include "base/logging.h"
+#include "content/public/browser/web_contents_delegate.h"
 #include "extensions/browser/media_capture_util.h"
 #include "extensions/browser/serial_extension_host_queue.h"
 #include "extensions/shell/browser/shell_extension_web_contents_observer.h"
@@ -71,12 +72,13 @@
   return g_queue.Pointer();
 }
 
-gfx::Size ShellExtensionHostDelegate::EnterPictureInPicture(
+content::PictureInPictureResult
+ShellExtensionHostDelegate::EnterPictureInPicture(
     content::WebContents* web_contents,
     const viz::SurfaceId& surface_id,
     const gfx::Size& natural_size) {
   NOTREACHED();
-  return gfx::Size();
+  return content::PictureInPictureResult::kNotSupported;
 }
 
 void ShellExtensionHostDelegate::ExitPictureInPicture() {
diff --git a/extensions/shell/browser/shell_extension_host_delegate.h b/extensions/shell/browser/shell_extension_host_delegate.h
index f8fb8f4..bb279554 100644
--- a/extensions/shell/browser/shell_extension_host_delegate.h
+++ b/extensions/shell/browser/shell_extension_host_delegate.h
@@ -34,9 +34,10 @@
                                   blink::mojom::MediaStreamType type,
                                   const Extension* extension) override;
   ExtensionHostQueue* GetExtensionHostQueue() const override;
-  gfx::Size EnterPictureInPicture(content::WebContents* web_contents,
-                                  const viz::SurfaceId& surface_id,
-                                  const gfx::Size& natural_size) override;
+  content::PictureInPictureResult EnterPictureInPicture(
+      content::WebContents* web_contents,
+      const viz::SurfaceId& surface_id,
+      const gfx::Size& natural_size) override;
   void ExitPictureInPicture() override;
 
  private:
diff --git a/fuchsia/runners/cast/sandbox_policy b/fuchsia/runners/cast/sandbox_policy
index 35825e8..0883374 100644
--- a/fuchsia/runners/cast/sandbox_policy
+++ b/fuchsia/runners/cast/sandbox_policy
@@ -5,6 +5,7 @@
       "fuchsia.fonts.Provider",
       "fuchsia.logger.LogSink",
       "fuchsia.media.Audio",
+      "fuchsia.media.drm.WidevineContentDecryptionModule",
       "fuchsia.mediacodec.CodecFactory",
       "fuchsia.net.SocketProvider",
       "fuchsia.netstack.Netstack",
diff --git a/fuchsia/runners/web/sandbox_policy b/fuchsia/runners/web/sandbox_policy
index 136856dc..46083c65 100644
--- a/fuchsia/runners/web/sandbox_policy
+++ b/fuchsia/runners/web/sandbox_policy
@@ -6,6 +6,7 @@
       "fuchsia.fonts.Provider",
       "fuchsia.logger.LogSink",
       "fuchsia.media.Audio",
+      "fuchsia.media.drm.WidevineContentDecryptionModule",
       "fuchsia.mediacodec.CodecFactory",
       "fuchsia.net.SocketProvider",
       "fuchsia.netstack.Netstack",
diff --git a/google_apis/gcm/engine/mcs_client_unittest.cc b/google_apis/gcm/engine/mcs_client_unittest.cc
index 350097f..39c7fd1 100644
--- a/google_apis/gcm/engine/mcs_client_unittest.cc
+++ b/google_apis/gcm/engine/mcs_client_unittest.cc
@@ -15,9 +15,9 @@
 #include "base/command_line.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/memory/ptr_util.h"
-#include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/test/scoped_task_environment.h"
 #include "base/test/simple_test_clock.h"
 #include "base/timer/timer.h"
 #include "google_apis/gcm/base/fake_encryptor.h"
@@ -173,7 +173,7 @@
   base::SimpleTestClock clock_;
 
   base::ScopedTempDir temp_directory_;
-  base::MessageLoop message_loop_;
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
   std::unique_ptr<base::RunLoop> run_loop_;
   std::unique_ptr<GCMStore> gcm_store_;
 
@@ -210,7 +210,8 @@
 
 void MCSClientTest::BuildMCSClient() {
   gcm_store_.reset(
-      new GCMStoreImpl(temp_directory_.GetPath(), message_loop_.task_runner(),
+      new GCMStoreImpl(temp_directory_.GetPath(),
+                       scoped_task_environment_.GetMainThreadTaskRunner(),
                        base::WrapUnique<Encryptor>(new FakeEncryptor)));
   mcs_client_.reset(
       new TestMCSClient(&clock_, &connection_factory_, gcm_store_.get(),
diff --git a/gpu/config/gpu_driver_bug_list.json b/gpu/config/gpu_driver_bug_list.json
index 9f264b9..80ef980 100644
--- a/gpu/config/gpu_driver_bug_list.json
+++ b/gpu/config/gpu_driver_bug_list.json
@@ -3295,6 +3295,18 @@
       "features": [
         "disable_program_caching_for_transform_feedback"
       ]
+    },
+    {
+      "id": 307,
+      "cr_bugs": [833975],
+      "description": "Workaround for broken EGL_IMAGE_EXTERNAL_FLUSH_EXT implementation on NVIDIA",
+      "os": {
+        "type": "chromeos"
+      },
+      "gl_vendor": "NVIDIA.*",
+      "features": [
+        "force_gl_flush_on_swap_buffers"
+      ]
     }
   ]
 }
diff --git a/gpu/config/gpu_workaround_list.txt b/gpu/config/gpu_workaround_list.txt
index 8e346571..0532f81 100644
--- a/gpu/config/gpu_workaround_list.txt
+++ b/gpu/config/gpu_workaround_list.txt
@@ -57,6 +57,7 @@
 flush_on_framebuffer_change
 force_cube_complete
 force_cube_map_positive_x_allocation
+force_gl_flush_on_swap_buffers
 force_high_performance_gpu
 force_int_or_srgb_cube_texture_complete
 force_low_power_gpu
diff --git a/gpu/vulkan/OWNERS b/gpu/vulkan/OWNERS
index 211e038..5bcd0b50 100644
--- a/gpu/vulkan/OWNERS
+++ b/gpu/vulkan/OWNERS
@@ -1,4 +1,5 @@
 piman@chromium.org
 vmiura@chromium.org
+penghuang@chromium.org
 
 # COMPONENT: Internals>GPU>Internals
diff --git a/gpu/vulkan/vulkan_command_buffer.cc b/gpu/vulkan/vulkan_command_buffer.cc
index ba776e45..4f14c85 100644
--- a/gpu/vulkan/vulkan_command_buffer.cc
+++ b/gpu/vulkan/vulkan_command_buffer.cc
@@ -207,21 +207,20 @@
 void VulkanCommandBuffer::TransitionImageLayout(VkImage image,
                                                 VkImageLayout old_layout,
                                                 VkImageLayout new_layout) {
-  VkImageMemoryBarrier barrier = {
-      .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
-      .srcAccessMask = GetAccessMask(old_layout),
-      .dstAccessMask = GetAccessMask(new_layout),
-      .oldLayout = old_layout,
-      .newLayout = new_layout,
-      .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
-      .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
-      .image = image,
-      .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
-      .subresourceRange.baseMipLevel = 0,
-      .subresourceRange.levelCount = 1,
-      .subresourceRange.baseArrayLayer = 0,
-      .subresourceRange.layerCount = 1,
-  };
+  VkImageMemoryBarrier barrier = {};
+  barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+  barrier.srcAccessMask = GetAccessMask(old_layout);
+  barrier.dstAccessMask = GetAccessMask(new_layout);
+  barrier.oldLayout = old_layout;
+  barrier.newLayout = new_layout;
+  barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+  barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+  barrier.image = image;
+  barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+  barrier.subresourceRange.baseMipLevel = 0;
+  barrier.subresourceRange.levelCount = 1;
+  barrier.subresourceRange.baseArrayLayer = 0;
+  barrier.subresourceRange.layerCount = 1;
   vkCmdPipelineBarrier(command_buffer_, GetPipelineStageFlags(old_layout),
                        GetPipelineStageFlags(new_layout), 0, 0, nullptr, 0,
                        nullptr, 1, &barrier);
@@ -233,17 +232,16 @@
                                             uint32_t buffer_height,
                                             uint32_t width,
                                             uint32_t height) {
-  VkBufferImageCopy region = {
-      .bufferOffset = 0,
-      .bufferRowLength = buffer_width,
-      .bufferImageHeight = buffer_height,
-      .imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
-      .imageSubresource.mipLevel = 0,
-      .imageSubresource.baseArrayLayer = 0,
-      .imageSubresource.layerCount = 1,
-      .imageOffset = {0, 0, 0},
-      .imageExtent = {width, height, 1},
-  };
+  VkBufferImageCopy region = {};
+  region.bufferOffset = 0;
+  region.bufferRowLength = buffer_width;
+  region.bufferImageHeight = buffer_height;
+  region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+  region.imageSubresource.mipLevel = 0;
+  region.imageSubresource.baseArrayLayer = 0;
+  region.imageSubresource.layerCount = 1;
+  region.imageOffset = {0, 0, 0};
+  region.imageExtent = {width, height, 1};
   vkCmdCopyBufferToImage(command_buffer_, buffer, image,
                          VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
 }
diff --git a/headless/lib/browser/protocol/headless_devtools_session.cc b/headless/lib/browser/protocol/headless_devtools_session.cc
index e99d8a1..51715fb 100644
--- a/headless/lib/browser/protocol/headless_devtools_session.cc
+++ b/headless/lib/browser/protocol/headless_devtools_session.cc
@@ -26,8 +26,8 @@
       agent_host->GetType() == content::DevToolsAgentHost::kTypePage) {
     AddHandler(std::make_unique<HeadlessHandler>(browser_,
                                                  agent_host->GetWebContents()));
-    AddHandler(
-        std::make_unique<PageHandler>(browser_, agent_host->GetWebContents()));
+    AddHandler(std::make_unique<PageHandler>(agent_host, browser_,
+                                             agent_host->GetWebContents()));
   }
   if (client->MayAttachToBrowser())
     AddHandler(std::make_unique<BrowserHandler>(browser_, agent_host->GetId()));
diff --git a/headless/lib/browser/protocol/page_handler.cc b/headless/lib/browser/protocol/page_handler.cc
index 10b09f7..79fc6b62 100644
--- a/headless/lib/browser/protocol/page_handler.cc
+++ b/headless/lib/browser/protocol/page_handler.cc
@@ -19,26 +19,19 @@
 const double kScaleMaxVal = 200;
 const double kScaleMinVal = 10;
 
-void PDFCreated(std::unique_ptr<PageHandler::PrintToPDFCallback> callback,
-                HeadlessPrintManager::PrintResult print_result,
-                scoped_refptr<base::RefCountedMemory> data) {
-  std::unique_ptr<base::DictionaryValue> response;
-  if (print_result == HeadlessPrintManager::PRINT_SUCCESS) {
-    callback->sendSuccess(protocol::Binary::fromRefCounted(data));
-  } else {
-    callback->sendFailure(Response::Error(
-        HeadlessPrintManager::PrintResultToString(print_result)));
-  }
-}
 
 }  // namespace
 #endif  // BUILDFLAG(ENABLE_PRINTING)
 
-PageHandler::PageHandler(base::WeakPtr<HeadlessBrowserImpl> browser,
+PageHandler::PageHandler(scoped_refptr<content::DevToolsAgentHost> agent_host,
+                         base::WeakPtr<HeadlessBrowserImpl> browser,
                          content::WebContents* web_contents)
     : DomainHandler(Page::Metainfo::domainName, browser),
-      web_contents_(web_contents) {
+      agent_host_(agent_host),
+      web_contents_(web_contents),
+      weak_factory_(this) {
   DCHECK(web_contents_);
+  DCHECK(agent_host_);
 }
 
 PageHandler::~PageHandler() = default;
@@ -62,6 +55,7 @@
                              Maybe<String> header_template,
                              Maybe<String> footer_template,
                              Maybe<bool> prefer_css_page_size,
+                             Maybe<String> transfer_mode,
                              std::unique_ptr<PrintToPDFCallback> callback) {
 #if BUILDFLAG(ENABLE_PRINTING)
   HeadlessPrintSettings settings;
@@ -136,14 +130,41 @@
       margin_right_in_inch * printing::kPointsPerInch;
   settings.prefer_css_page_size = prefer_css_page_size.fromMaybe(false);
 
+  bool return_as_stream = transfer_mode.fromMaybe("") ==
+                          Page::PrintToPDF::TransferModeEnum::ReturnAsStream;
   HeadlessPrintManager::FromWebContents(web_contents_)
-      ->GetPDFContents(web_contents_->GetMainFrame(), settings,
-                       base::BindOnce(&PDFCreated, std::move(callback)));
+      ->GetPDFContents(
+          web_contents_->GetMainFrame(), settings,
+          base::BindOnce(&PageHandler::PDFCreated, weak_factory_.GetWeakPtr(),
+                         return_as_stream, std::move(callback)));
 #else
   callback->sendFailure(Response::Error("Printing is not enabled"));
   return;
 #endif  // BUILDFLAG(ENABLE_PRINTING)
 }
 
+#if BUILDFLAG(ENABLE_PRINTING)
+void PageHandler::PDFCreated(
+    bool returnAsStream,
+    std::unique_ptr<PageHandler::PrintToPDFCallback> callback,
+    HeadlessPrintManager::PrintResult print_result,
+    scoped_refptr<base::RefCountedMemory> data) {
+  std::unique_ptr<base::DictionaryValue> response;
+  if (print_result != HeadlessPrintManager::PRINT_SUCCESS) {
+    callback->sendFailure(Response::Error(
+        HeadlessPrintManager::PrintResultToString(print_result)));
+    return;
+  }
+
+  if (!returnAsStream) {
+    callback->sendSuccess(protocol::Binary::fromRefCounted(data),
+                          Maybe<std::string>());
+    return;
+  }
+  std::string handle = agent_host_->CreateIOStreamFromData(data);
+  callback->sendSuccess(protocol::Binary(), handle);
+}
+#endif  // BUILDFLAG(ENABLE_PRINTING)
+
 }  // namespace protocol
 }  // namespace headless
diff --git a/headless/lib/browser/protocol/page_handler.h b/headless/lib/browser/protocol/page_handler.h
index f0ab147..7ea5bdd 100644
--- a/headless/lib/browser/protocol/page_handler.h
+++ b/headless/lib/browser/protocol/page_handler.h
@@ -5,6 +5,8 @@
 #ifndef HEADLESS_LIB_BROWSER_PROTOCOL_PAGE_HANDLER_H_
 #define HEADLESS_LIB_BROWSER_PROTOCOL_PAGE_HANDLER_H_
 
+#include "base/memory/weak_ptr.h"
+#include "content/public/browser/devtools_agent_host.h"
 #include "headless/lib/browser/protocol/domain_handler.h"
 #include "headless/lib/browser/protocol/dp_page.h"
 #include "printing/buildflags/buildflags.h"
@@ -23,7 +25,8 @@
 
 class PageHandler : public DomainHandler, public Page::Backend {
  public:
-  PageHandler(base::WeakPtr<HeadlessBrowserImpl> browser,
+  PageHandler(scoped_refptr<content::DevToolsAgentHost> agent_host,
+              base::WeakPtr<HeadlessBrowserImpl> browser,
               content::WebContents* web_contents);
   ~PageHandler() override;
 
@@ -45,10 +48,19 @@
                   Maybe<String> header_template,
                   Maybe<String> footer_template,
                   Maybe<bool> prefer_css_page_size,
+                  Maybe<String> transfer_mode,
                   std::unique_ptr<PrintToPDFCallback> callback) override;
 
  private:
+#if BUILDFLAG(ENABLE_PRINTING)
+  void PDFCreated(bool returnAsStream,
+                  std::unique_ptr<PageHandler::PrintToPDFCallback> callback,
+                  HeadlessPrintManager::PrintResult print_result,
+                  scoped_refptr<base::RefCountedMemory> data);
+#endif
+  scoped_refptr<content::DevToolsAgentHost> agent_host_;
   content::WebContents* web_contents_;
+  base::WeakPtrFactory<PageHandler> weak_factory_;
   DISALLOW_COPY_AND_ASSIGN(PageHandler);
 };
 
diff --git a/headless/lib/headless_web_contents_browsertest.cc b/headless/lib/headless_web_contents_browsertest.cc
index 18349c06..1a00c8f 100644
--- a/headless/lib/headless_web_contents_browsertest.cc
+++ b/headless/lib/headless_web_contents_browsertest.cc
@@ -25,6 +25,7 @@
 #include "headless/public/devtools/domains/dom_snapshot.h"
 #include "headless/public/devtools/domains/emulation.h"
 #include "headless/public/devtools/domains/headless_experimental.h"
+#include "headless/public/devtools/domains/io.h"
 #include "headless/public/devtools/domains/page.h"
 #include "headless/public/devtools/domains/runtime.h"
 #include "headless/public/devtools/domains/security.h"
@@ -339,6 +340,82 @@
 };
 
 HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessWebContentsPDFTest);
+
+class HeadlessWebContentsPDFStreamTest
+    : public HeadlessAsyncDevTooledBrowserTest {
+ public:
+  const double kPaperWidth = 10;
+  const double kPaperHeight = 15;
+  const double kDocHeight = 50;
+
+  void RunDevTooledTest() override {
+    std::string height_expression = "document.body.style.height = '" +
+                                    base::NumberToString(kDocHeight) + "in'";
+    std::unique_ptr<runtime::EvaluateParams> params =
+        runtime::EvaluateParams::Builder()
+            .SetExpression(height_expression)
+            .Build();
+    devtools_client_->GetRuntime()->Evaluate(
+        std::move(params),
+        base::BindOnce(&HeadlessWebContentsPDFStreamTest::OnPageSetupCompleted,
+                       base::Unretained(this)));
+  }
+
+  void OnPageSetupCompleted(std::unique_ptr<runtime::EvaluateResult> result) {
+    devtools_client_->GetPage()->GetExperimental()->PrintToPDF(
+        page::PrintToPDFParams::Builder()
+            .SetTransferMode(page::PrintToPDFTransferMode::RETURN_AS_STREAM)
+            .SetPaperHeight(kPaperHeight)
+            .SetPaperWidth(kPaperWidth)
+            .SetMarginTop(0)
+            .SetMarginBottom(0)
+            .SetMarginLeft(0)
+            .SetMarginRight(0)
+            .Build(),
+        base::BindOnce(&HeadlessWebContentsPDFStreamTest::OnPDFCreated,
+                       base::Unretained(this)));
+  }
+
+  void OnPDFCreated(std::unique_ptr<page::PrintToPDFResult> result) {
+    EXPECT_EQ(result->GetData().size(), 0U);
+    stream_ = result->GetStream();
+    devtools_client_->GetIO()->Read(
+        stream_, base::BindOnce(&HeadlessWebContentsPDFStreamTest::OnReadChunk,
+                                base::Unretained(this)));
+  }
+
+  void OnReadChunk(std::unique_ptr<io::ReadResult> result) {
+    base64_data_ = base64_data_ + result->GetData();
+    if (result->GetEof()) {
+      OnPDFLoaded();
+    } else {
+      devtools_client_->GetIO()->Read(
+          stream_,
+          base::BindOnce(&HeadlessWebContentsPDFStreamTest::OnReadChunk,
+                         base::Unretained(this)));
+    }
+  }
+
+  void OnPDFLoaded() {
+    EXPECT_GT(base64_data_.size(), 0U);
+    bool success;
+    protocol::Binary pdf_data =
+        protocol::Binary::fromBase64(base64_data_, &success);
+    EXPECT_TRUE(success);
+    EXPECT_GT(pdf_data.size(), 0U);
+    auto pdf_span = base::make_span(pdf_data.data(), pdf_data.size());
+    int num_pages;
+    EXPECT_TRUE(chrome_pdf::GetPDFDocInfo(pdf_span, &num_pages, nullptr));
+    EXPECT_EQ(std::ceil(kDocHeight / kPaperHeight), num_pages);
+    FinishAsynchronousTest();
+  }
+
+ private:
+  std::string stream_;
+  std::string base64_data_;
+};
+
+HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessWebContentsPDFStreamTest);
 #endif
 
 class HeadlessWebContentsSecurityTest
diff --git a/infra/config/luci-notify.cfg b/infra/config/luci-notify.cfg
index fa56dd4..b6ab893 100644
--- a/infra/config/luci-notify.cfg
+++ b/infra/config/luci-notify.cfg
@@ -204,7 +204,7 @@
     on_change: true
     email {
       recipients: "cronet-sheriff-oncall1@google.com"
-      recipients: "cronet-sheriff-oncall2@google.com
+      recipients: "cronet-sheriff-oncall2@google.com"
       recipients: "cronet-sheriff+bot@google.com"
     }
   }
diff --git a/ios/chrome/browser/autofill/automation/automation_action_egtest.mm b/ios/chrome/browser/autofill/automation/automation_action_egtest.mm
index 3da273b..c7511ef8 100644
--- a/ios/chrome/browser/autofill/automation/automation_action_egtest.mm
+++ b/ios/chrome/browser/autofill/automation/automation_action_egtest.mm
@@ -35,8 +35,7 @@
       base::FilePath(FILE_PATH_LITERAL(kTestPageDirectory)));
   XCTAssertTrue(self.testServer->Start());
 
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey loadURL:self.testServer->GetURL(kTestPageUrl)]);
+  [ChromeEarlGrey loadURL:self.testServer->GetURL(kTestPageUrl)];
 }
 
 // Tests the click action, by clicking a button that populates the web page,
diff --git a/ios/chrome/browser/autofill/automation/automation_egtest.mm b/ios/chrome/browser/autofill/automation/automation_egtest.mm
index 6fb78f5..c3b215b 100644
--- a/ios/chrome/browser/autofill/automation/automation_egtest.mm
+++ b/ios/chrome/browser/autofill/automation/automation_egtest.mm
@@ -9,25 +9,21 @@
 #include "base/files/file_util.h"
 #include "base/guid.h"
 #include "base/json/json_reader.h"
-#include "base/values.h"
-#import "ios/chrome/browser/autofill/automation/automation_action.h"
-#import "ios/chrome/test/app/chrome_test_util.h"
-#import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
-#import "ios/chrome/test/earl_grey/chrome_error_util.h"
-#import "ios/chrome/test/earl_grey/chrome_test_case.h"
-
-#include "base/guid.h"
 #include "base/mac/foundation_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #import "base/test/ios/wait_util.h"
+#include "base/values.h"
 #include "components/autofill/core/browser/autofill_manager.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill/ios/browser/autofill_driver_ios.h"
+#import "ios/chrome/browser/autofill/automation/automation_action.h"
 #import "ios/chrome/browser/autofill/form_suggestion_label.h"
 #import "ios/chrome/test/app/chrome_test_util.h"
+#import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
 #import "ios/chrome/test/earl_grey/chrome_error_util.h"
+#import "ios/chrome/test/earl_grey/chrome_test_case.h"
 #include "ios/web/public/js_messaging/web_frame_util.h"
 #import "ios/web/public/js_messaging/web_frames_manager.h"
 #import "ios/web/public/test/earl_grey/web_view_actions.h"
@@ -287,7 +283,7 @@
 - (bool)runActionsOnce {
   @try {
     // Load the initial page of the recipe.
-    CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:startUrl]);
+    [ChromeEarlGrey loadURL:startUrl];
 
     for (AutomationAction* action in actions_) {
       CHROME_EG_ASSERT_NO_ERROR([action execute]);
diff --git a/ios/chrome/browser/autofill/form_input_egtest.mm b/ios/chrome/browser/autofill/form_input_egtest.mm
index 6c1e347..eb1dd1f 100644
--- a/ios/chrome/browser/autofill/form_input_egtest.mm
+++ b/ios/chrome/browser/autofill/form_input_egtest.mm
@@ -13,7 +13,6 @@
 #include "ios/chrome/grit/ios_strings.h"
 #import "ios/chrome/test/app/chrome_test_util.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
-#import "ios/chrome/test/earl_grey/chrome_error_util.h"
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
 #import "ios/web/public/deprecated/crw_js_injection_receiver.h"
@@ -89,10 +88,9 @@
   web::test::SetUpFileBasedHttpServer();
   GURL URL = web::test::HttpServer::MakeUrl(
       "http://ios/testing/data/http_server_files/multi_field_form.html");
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL]);
+  [ChromeEarlGrey loadURL:URL];
 
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:"hello!"]);
+  [ChromeEarlGrey waitForWebStateContainingText:"hello!"];
 
   // Opening the keyboard from a webview blocks EarlGrey's synchronization.
   [[GREYConfiguration sharedInstance]
diff --git a/ios/chrome/browser/context_menu/context_menu_egtest.mm b/ios/chrome/browser/context_menu/context_menu_egtest.mm
index 34b559e..bc3277e 100644
--- a/ios/chrome/browser/context_menu/context_menu_egtest.mm
+++ b/ios/chrome/browser/context_menu/context_menu_egtest.mm
@@ -16,7 +16,6 @@
 #import "ios/chrome/test/earl_grey/chrome_actions.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
-#import "ios/chrome/test/earl_grey/chrome_error_util.h"
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
 #include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
@@ -166,13 +165,11 @@
 
 + (void)setUp {
   [super setUp];
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey setContentSettings:CONTENT_SETTING_ALLOW]);
+  [ChromeEarlGrey setContentSettings:CONTENT_SETTING_ALLOW];
 }
 
 + (void)tearDown {
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey setContentSettings:CONTENT_SETTING_DEFAULT]);
+  [ChromeEarlGrey setContentSettings:CONTENT_SETTING_DEFAULT];
   [super tearDown];
 }
 
@@ -189,13 +186,12 @@
 // image in the current tab.
 - (void)testOpenImageInCurrentTabFromContextMenu {
   const GURL pageURL = self.testServer->GetURL(kLogoPagePath);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:pageURL]);
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:kLogoPageText]);
+  [ChromeEarlGrey loadURL:pageURL];
+  [ChromeEarlGrey waitForWebStateContainingText:kLogoPageText];
 
   LongPressElement(kLogoPageChromiumImageId);
   TapOnContextMenuButton(OpenImageButton());
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey waitForPageToFinishLoading]);
+  [ChromeEarlGrey waitForPageToFinishLoading];
 
   // Verify url.
   const GURL imageURL = self.testServer->GetURL(kLogoPageImageSourcePath);
@@ -207,16 +203,15 @@
 // opens the image in a new background tab.
 - (void)testOpenImageInNewTabFromContextMenu {
   const GURL pageURL = self.testServer->GetURL(kLogoPagePath);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:pageURL]);
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:kLogoPageText]);
+  [ChromeEarlGrey loadURL:pageURL];
+  [ChromeEarlGrey waitForWebStateContainingText:kLogoPageText];
 
   LongPressElement(kLogoPageChromiumImageId);
   TapOnContextMenuButton(OpenImageInNewTabButton());
 
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey waitForMainTabCount:2]);
+  [ChromeEarlGrey waitForMainTabCount:2];
   SelectTabAtIndexInCurrentMode(1U);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey waitForPageToFinishLoading]);
+  [ChromeEarlGrey waitForPageToFinishLoading];
 
   // Verify url.
   const GURL imageURL = self.testServer->GetURL(kLogoPageImageSourcePath);
@@ -227,17 +222,16 @@
 // Tests "Open in New Tab" on context menu.
 - (void)testContextMenuOpenInNewTab {
   const GURL initialURL = self.testServer->GetURL(kInitialPageUrl);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:initialURL]);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey
-      waitForWebStateContainingText:kInitialPageDestinationLinkText]);
+  [ChromeEarlGrey loadURL:initialURL];
+  [ChromeEarlGrey
+      waitForWebStateContainingText:kInitialPageDestinationLinkText];
 
   LongPressElement(kInitialPageDestinationLinkId);
   TapOnContextMenuButton(OpenLinkInNewTabButton());
 
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey waitForMainTabCount:2]);
+  [ChromeEarlGrey waitForMainTabCount:2];
   SelectTabAtIndexInCurrentMode(1U);
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:kDestinationPageText]);
+  [ChromeEarlGrey waitForWebStateContainingText:kDestinationPageText];
 
   // Verify url.
   const GURL destinationURL = self.testServer->GetURL(kDestinationPageUrl);
@@ -248,7 +242,7 @@
 // Tests that the context menu is displayed for an image url.
 - (void)testContextMenuDisplayedOnImage {
   const GURL imageURL = self.testServer->GetURL(kLogoPageImageSourcePath);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:imageURL]);
+  [ChromeEarlGrey loadURL:imageURL];
 
   // Calculate a point inside the displayed image. Javascript can not be used to
   // find the element because no DOM exists.  If the viewport is adjusted using
@@ -270,9 +264,9 @@
                         point, kGREYLongPressDefaultDuration)];
 
   TapOnContextMenuButton(OpenImageInNewTabButton());
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey waitForMainTabCount:2]);
+  [ChromeEarlGrey waitForMainTabCount:2];
   SelectTabAtIndexInCurrentMode(1U);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey waitForPageToFinishLoading]);
+  [ChromeEarlGrey waitForPageToFinishLoading];
 
   // Verify url.
   [[EarlGrey selectElementWithMatcher:OmniboxText(imageURL.GetContent())]
@@ -284,13 +278,12 @@
   chrome_test_util::HistogramTester histogramTester;
 
   const GURL pageURL = self.testServer->GetURL(kLogoPagePath);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:pageURL]);
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:kLogoPageText]);
+  [ChromeEarlGrey loadURL:pageURL];
+  [ChromeEarlGrey waitForWebStateContainingText:kLogoPageText];
 
   LongPressElement(kLogoPageChromiumImageId);
   TapOnContextMenuButton(OpenImageButton());
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey waitForPageToFinishLoading]);
+  [ChromeEarlGrey waitForPageToFinishLoading];
 
   histogramTester.ExpectTotalCount("ContextMenu.DOMElementFetchDuration", 1,
                                    ^(NSString* error) {
@@ -303,13 +296,12 @@
   chrome_test_util::HistogramTester histogramTester;
 
   const GURL pageURL = self.testServer->GetURL(kLogoPagePath);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:pageURL]);
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:kLogoPageText]);
+  [ChromeEarlGrey loadURL:pageURL];
+  [ChromeEarlGrey waitForWebStateContainingText:kLogoPageText];
 
   LongPressElement(kLogoPageChromiumImageId);
   TapOnContextMenuButton(OpenImageButton());
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey waitForPageToFinishLoading]);
+  [ChromeEarlGrey waitForPageToFinishLoading];
 
   // Verify that system touches were cancelled.
   histogramTester.ExpectTotalCount("ContextMenu.CancelSystemTouches", 1,
@@ -326,9 +318,8 @@
   // Load the destination page directly because it has a plain text message on
   // it.
   const GURL destinationURL = self.testServer->GetURL(kDestinationPageUrl);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:destinationURL]);
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:kDestinationPageText]);
+  [ChromeEarlGrey loadURL:destinationURL];
+  [ChromeEarlGrey waitForWebStateContainingText:kDestinationPageText];
 
   LongPressElement(kDestinationPageTextId);
 
@@ -350,9 +341,9 @@
 // Tests cancelling the context menu.
 - (void)testDismissContextMenu {
   const GURL initialURL = self.testServer->GetURL(kInitialPageUrl);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:initialURL]);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey
-      waitForWebStateContainingText:kInitialPageDestinationLinkText]);
+  [ChromeEarlGrey loadURL:initialURL];
+  [ChromeEarlGrey
+      waitForWebStateContainingText:kInitialPageDestinationLinkText];
 
   // Display the context menu twice.
   for (NSInteger i = 0; i < 2; i++) {
@@ -386,9 +377,9 @@
 // Checks that all the options are displayed in the context menu.
 - (void)testAppropriateContextMenu {
   const GURL initialURL = self.testServer->GetURL(kInitialPageUrl);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:initialURL]);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey
-      waitForWebStateContainingText:kInitialPageDestinationLinkText]);
+  [ChromeEarlGrey loadURL:initialURL];
+  [ChromeEarlGrey
+      waitForWebStateContainingText:kInitialPageDestinationLinkText];
 
   LongPressElement(kInitialPageDestinationLinkId);
 
diff --git a/ios/chrome/browser/device_sharing/handoff_manager_egtest.mm b/ios/chrome/browser/device_sharing/handoff_manager_egtest.mm
index 4629acbb..3f53f97 100644
--- a/ios/chrome/browser/device_sharing/handoff_manager_egtest.mm
+++ b/ios/chrome/browser/device_sharing/handoff_manager_egtest.mm
@@ -10,7 +10,6 @@
 #include "ios/chrome/browser/ui/util/ui_util.h"
 #import "ios/chrome/test/app/chrome_test_util.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
-#import "ios/chrome/test/earl_grey/chrome_error_util.h"
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
 #include "ios/web/public/test/http_server/http_server.h"
 #include "ios/web/public/test/http_server/http_server_util.h"
@@ -65,33 +64,33 @@
 - (void)testTypicalURL {
   const GURL destinationUrl = web::test::HttpServer::MakeUrl(
       "http://ios/testing/data/http_server_files/destination.html");
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:destinationUrl]);
+  [ChromeEarlGrey loadURL:destinationUrl];
   AssertHandoffURL(destinationUrl);
 }
 
 // Tests Handoff URL for a new tab.
 - (void)testTypicalURLInNewTab {
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey openNewTab]);
+  [ChromeEarlGrey openNewTab];
   const GURL destinationUrl = web::test::HttpServer::MakeUrl(
       "http://ios/testing/data/http_server_files/pony.html");
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:destinationUrl]);
+  [ChromeEarlGrey loadURL:destinationUrl];
   AssertHandoffURL(destinationUrl);
 }
 
 // Tests that Handoff URL should never be set for an incognito tab.
 - (void)testTypicalURLInNewIncognitoTab {
   // Opens an incognito tab and loads a web page. Check that Handoff URL is nil.
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey openNewIncognitoTab]);
+  [ChromeEarlGrey openNewIncognitoTab];
   const GURL destinationUrl = web::test::HttpServer::MakeUrl(
       "http://ios/testing/data/http_server_files/destination.html");
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:destinationUrl]);
+  [ChromeEarlGrey loadURL:destinationUrl];
   AssertHandoffURL(GURL());
 
   // Loads a second URL on the same incognito tab. Handoff URL should still be
   // nil.
   const GURL destinationUrl2 = web::test::HttpServer::MakeUrl(
       "http://ios/testing/data/http_server_files/pony.html");
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:destinationUrl2]);
+  [ChromeEarlGrey loadURL:destinationUrl2];
   AssertHandoffURL(GURL());
 }
 
@@ -106,11 +105,11 @@
       "http://ios/testing/data/http_server_files/chromium_logo_page.html");
 
   // Sets up the state for 3 tabs.
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:tab1URL]);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey openNewTab]);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:tab2URL]);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey openNewTab]);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:tab3URL]);
+  [ChromeEarlGrey loadURL:tab1URL];
+  [ChromeEarlGrey openNewTab];
+  [ChromeEarlGrey loadURL:tab2URL];
+  [ChromeEarlGrey openNewTab];
+  [ChromeEarlGrey loadURL:tab3URL];
 
   // When tab 3 is closed, tab 2 is front and Handoff URL should be the URL for
   // tab 2.
@@ -133,16 +132,16 @@
       "http://ios/testing/data/http_server_files/chromium_logo_page.html");
 
   // Loads one page.
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:tab1URL]);
+  [ChromeEarlGrey loadURL:tab1URL];
   // Loads page two in incognito and verifies that Handoff URL is nil.
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey openNewIncognitoTab]);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:tab2URL]);
+  [ChromeEarlGrey openNewIncognitoTab];
+  [ChromeEarlGrey loadURL:tab2URL];
   AssertHandoffURL(GURL());
 
   // Loads page three in a new normal tab and verify that Handoff URL is not
   // nil.
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey openNewTab]);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:tab3URL]);
+  [ChromeEarlGrey openNewTab];
+  [ChromeEarlGrey loadURL:tab3URL];
   AssertHandoffURL(tab3URL);
 }
 
diff --git a/ios/chrome/browser/feature_engagement/feature_engagement_egtest.mm b/ios/chrome/browser/feature_engagement/feature_engagement_egtest.mm
index 037b513..7088e97 100644
--- a/ios/chrome/browser/feature_engagement/feature_engagement_egtest.mm
+++ b/ios/chrome/browser/feature_engagement/feature_engagement_egtest.mm
@@ -25,7 +25,6 @@
 #import "ios/chrome/test/app/chrome_test_util.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
-#import "ios/chrome/test/earl_grey/chrome_error_util.h"
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
@@ -420,8 +419,7 @@
   GREYAssertTrue(self.testServer->Start(), @"Test server failed to start");
 
   // Load a URL with french text so that language detection is performed.
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey loadURL:self.testServer->GetURL(kFrenchPageURLPath)]);
+  [ChromeEarlGrey loadURL:self.testServer->GetURL(kFrenchPageURLPath)];
 
   base::test::ScopedFeatureList scoped_feature_list;
   EnableBadgedTranslateManualTrigger(scoped_feature_list);
@@ -466,7 +464,7 @@
 
   // Navigate to a page other than the NTP to allow for the New Tab Tip to
   // appear.
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:GURL("chrome://version")]);
+  [ChromeEarlGrey loadURL:GURL("chrome://version")];
 
   // Open and close the tab switcher to trigger the New Tab tip.
   OpenAndCloseTabSwitcher();
diff --git a/ios/chrome/browser/net/cookies_egtest.mm b/ios/chrome/browser/net/cookies_egtest.mm
index 5d87caf..b8694be5 100644
--- a/ios/chrome/browser/net/cookies_egtest.mm
+++ b/ios/chrome/browser/net/cookies_egtest.mm
@@ -13,7 +13,6 @@
 #include "base/strings/sys_string_conversions.h"
 #import "ios/chrome/test/app/chrome_test_util.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
-#import "ios/chrome/test/earl_grey/chrome_error_util.h"
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
 #include "ios/web/public/test/http_server/html_response_provider.h"
 #import "ios/web/public/test/http_server/http_server.h"
@@ -76,8 +75,8 @@
 
 // Clear cookies to make sure that tests do not interfere each other.
 - (void)tearDown {
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey
-      loadURL:web::test::HttpServer::MakeUrl(kTestUrlNormalBrowsing)]);
+  [ChromeEarlGrey
+      loadURL:web::test::HttpServer::MakeUrl(kTestUrlNormalBrowsing)];
   NSString* const clearCookieScript =
       @"var cookies = document.cookie.split(';');"
        "for (var i = 0; i < cookies.length; i++) {"
@@ -101,8 +100,8 @@
 - (void)testClearIncognitoFromMain {
   // Loads a dummy page in normal tab. Sets a normal test cookie. Verifies that
   // the incognito test cookie is not found.
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey
-      loadURL:web::test::HttpServer::MakeUrl(kTestUrlNormalSetCookie)]);
+  [ChromeEarlGrey
+      loadURL:web::test::HttpServer::MakeUrl(kTestUrlNormalSetCookie)];
   NSDictionary* cookies = [ChromeEarlGrey cookies];
   GREYAssertEqualObjects(kNormalCookieValue, cookies[kNormalCookieName],
                          @"Failed to set normal cookie in normal mode.");
@@ -111,9 +110,9 @@
 
   // Opens an incognito tab, loads the dummy page, and sets incognito test
   // cookie.
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey openNewIncognitoTab]);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey
-      loadURL:web::test::HttpServer::MakeUrl(kTestUrlIncognitoSetCookie)]);
+  [ChromeEarlGrey openNewIncognitoTab];
+  [ChromeEarlGrey
+      loadURL:web::test::HttpServer::MakeUrl(kTestUrlIncognitoSetCookie)];
   cookies = [ChromeEarlGrey cookies];
   GREYAssertEqualObjects(kIncognitoCookieValue, cookies[kIncognitoCookieName],
                          @"Failed to set incognito cookie in incognito mode.");
@@ -122,9 +121,9 @@
 
   // Switches back to normal profile by opening up a new tab. Test cookie
   // should not be found.
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey openNewTab]);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey
-      loadURL:web::test::HttpServer::MakeUrl(kTestUrlNormalBrowsing)]);
+  [ChromeEarlGrey openNewTab];
+  [ChromeEarlGrey
+      loadURL:web::test::HttpServer::MakeUrl(kTestUrlNormalBrowsing)];
   cookies = [ChromeEarlGrey cookies];
   GREYAssertEqualObjects(kNormalCookieValue, cookies[kNormalCookieName],
                          @"Normal cookie should still exist in normal mode.");
@@ -133,10 +132,10 @@
 
   // Finally, closes all incognito tabs while still in normal tab.
   // Checks that incognito cookie is gone.
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey closeAllIncognitoTabs]);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey openNewTab]);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey
-      loadURL:web::test::HttpServer::MakeUrl(kTestUrlIncognitoBrowsing)]);
+  [ChromeEarlGrey closeAllIncognitoTabs];
+  [ChromeEarlGrey openNewTab];
+  [ChromeEarlGrey
+      loadURL:web::test::HttpServer::MakeUrl(kTestUrlIncognitoBrowsing)];
   cookies = [ChromeEarlGrey cookies];
   GREYAssertEqual(0U, cookies.count,
                   @"Incognito cookie should be gone from normal mode.");
@@ -147,13 +146,13 @@
 // not reappear.
 - (void)testClearIncognitoFromIncognito {
   // Loads a page in normal tab.
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey
-      loadURL:web::test::HttpServer::MakeUrl(kTestUrlNormalBrowsing)]);
+  [ChromeEarlGrey
+      loadURL:web::test::HttpServer::MakeUrl(kTestUrlNormalBrowsing)];
 
   // Opens an incognito tab, loads a page, and sets an incognito cookie.
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey openNewIncognitoTab]);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey
-      loadURL:web::test::HttpServer::MakeUrl(kTestUrlIncognitoSetCookie)]);
+  [ChromeEarlGrey openNewIncognitoTab];
+  [ChromeEarlGrey
+      loadURL:web::test::HttpServer::MakeUrl(kTestUrlIncognitoSetCookie)];
   NSDictionary* cookies = [ChromeEarlGrey cookies];
   GREYAssertEqualObjects(kIncognitoCookieValue, cookies[kIncognitoCookieName],
                          @"Failed to set incognito cookie in incognito mode.");
@@ -161,23 +160,23 @@
                   @"Only one cookie should be found in incognito mode.");
 
   // Closes all incognito tabs and switch back to a normal tab.
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey closeAllIncognitoTabs]);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey openNewTab]);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey
-      loadURL:web::test::HttpServer::MakeUrl(kTestUrlNormalBrowsing)]);
+  [ChromeEarlGrey closeAllIncognitoTabs];
+  [ChromeEarlGrey openNewTab];
+  [ChromeEarlGrey
+      loadURL:web::test::HttpServer::MakeUrl(kTestUrlNormalBrowsing)];
 
   // Opens a new incognito tab and verify that the previously set cookie
   // is no longer there.
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey openNewIncognitoTab]);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey
-      loadURL:web::test::HttpServer::MakeUrl(kTestUrlIncognitoBrowsing)]);
+  [ChromeEarlGrey openNewIncognitoTab];
+  [ChromeEarlGrey
+      loadURL:web::test::HttpServer::MakeUrl(kTestUrlIncognitoBrowsing)];
   cookies = [ChromeEarlGrey cookies];
   GREYAssertEqual(0U, cookies.count,
                   @"Incognito cookie should be gone from incognito mode.");
 
   // Verifies that new incognito cookies can be set.
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey
-      loadURL:web::test::HttpServer::MakeUrl(kTestUrlIncognitoSetCookie)]);
+  [ChromeEarlGrey
+      loadURL:web::test::HttpServer::MakeUrl(kTestUrlIncognitoSetCookie)];
   cookies = [ChromeEarlGrey cookies];
   GREYAssertEqualObjects(kIncognitoCookieValue, cookies[kIncognitoCookieName],
                          @"Failed to set incognito cookie in incognito mode.");
@@ -188,8 +187,8 @@
 // Tests that a cookie set in normal tab is not available in an incognito tab.
 - (void)testSwitchToIncognito {
   // Sets cookie in normal tab.
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey
-      loadURL:web::test::HttpServer::MakeUrl(kTestUrlNormalSetCookie)]);
+  [ChromeEarlGrey
+      loadURL:web::test::HttpServer::MakeUrl(kTestUrlNormalSetCookie)];
   NSDictionary* cookies = [ChromeEarlGrey cookies];
   GREYAssertEqualObjects(kNormalCookieValue, cookies[kNormalCookieName],
                          @"Normal cookie should still exist in normal mode.");
@@ -197,19 +196,19 @@
                   @"Only one cookie should be found in normal mode.");
 
   // Switches to a new incognito tab and verifies that cookie is not there.
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey openNewIncognitoTab]);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey
-      loadURL:web::test::HttpServer::MakeUrl(kTestUrlIncognitoBrowsing)]);
+  [ChromeEarlGrey openNewIncognitoTab];
+  [ChromeEarlGrey
+      loadURL:web::test::HttpServer::MakeUrl(kTestUrlIncognitoBrowsing)];
   cookies = [ChromeEarlGrey cookies];
   GREYAssertEqual(0U, cookies.count,
                   @"Normal cookie should not be found in incognito mode.");
 
   // Closes all incognito tabs and then switching back to a normal tab. Verifies
   // that the cookie set earlier is still there.
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey closeAllIncognitoTabs]);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey openNewTab]);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey
-      loadURL:web::test::HttpServer::MakeUrl(kTestUrlNormalBrowsing)]);
+  [ChromeEarlGrey closeAllIncognitoTabs];
+  [ChromeEarlGrey openNewTab];
+  [ChromeEarlGrey
+      loadURL:web::test::HttpServer::MakeUrl(kTestUrlNormalBrowsing)];
   cookies = [ChromeEarlGrey cookies];
   GREYAssertEqualObjects(
       kNormalCookieValue, cookies[kNormalCookieName],
@@ -223,11 +222,11 @@
 - (void)testSwitchToMain {
   // Loads a page in normal tab and then switches to a new incognito tab. Sets
   // cookie in incognito tab.
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey
-      loadURL:web::test::HttpServer::MakeUrl(kTestUrlNormalBrowsing)]);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey openNewIncognitoTab]);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey
-      loadURL:web::test::HttpServer::MakeUrl(kTestUrlIncognitoSetCookie)]);
+  [ChromeEarlGrey
+      loadURL:web::test::HttpServer::MakeUrl(kTestUrlNormalBrowsing)];
+  [ChromeEarlGrey openNewIncognitoTab];
+  [ChromeEarlGrey
+      loadURL:web::test::HttpServer::MakeUrl(kTestUrlIncognitoSetCookie)];
   NSDictionary* cookies = [ChromeEarlGrey cookies];
   GREYAssertEqualObjects(kIncognitoCookieValue, cookies[kIncognitoCookieName],
                          @"Failed to set incognito cookie in incognito mode.");
@@ -236,17 +235,17 @@
 
   // Switches back to a normal tab and verifies that cookie set in incognito tab
   // is not available.
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey openNewTab]);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey
-      loadURL:web::test::HttpServer::MakeUrl(kTestUrlNormalBrowsing)]);
+  [ChromeEarlGrey openNewTab];
+  [ChromeEarlGrey
+      loadURL:web::test::HttpServer::MakeUrl(kTestUrlNormalBrowsing)];
   cookies = [ChromeEarlGrey cookies];
   GREYAssertEqual(0U, cookies.count,
                   @"Incognito cookie should not be found in normal mode.");
 
   // Returns back to Incognito tab and cookie is still there.
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey openNewIncognitoTab]);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey
-      loadURL:web::test::HttpServer::MakeUrl(kTestUrlIncognitoBrowsing)]);
+  [ChromeEarlGrey openNewIncognitoTab];
+  [ChromeEarlGrey
+      loadURL:web::test::HttpServer::MakeUrl(kTestUrlIncognitoBrowsing)];
   cookies = [ChromeEarlGrey cookies];
   GREYAssertEqualObjects(
       kIncognitoCookieValue, cookies[kIncognitoCookieName],
@@ -258,8 +257,8 @@
 // Tests that a cookie set in a normal tab can be found in another normal tab.
 - (void)testShareCookiesBetweenTabs {
   // Loads page and sets cookie in first normal tab.
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey
-      loadURL:web::test::HttpServer::MakeUrl(kTestUrlNormalSetCookie)]);
+  [ChromeEarlGrey
+      loadURL:web::test::HttpServer::MakeUrl(kTestUrlNormalSetCookie)];
   NSDictionary* cookies = [ChromeEarlGrey cookies];
   GREYAssertEqualObjects(kNormalCookieValue, cookies[kNormalCookieName],
                          @"Failed to set normal cookie in normal mode.");
@@ -267,9 +266,9 @@
                   @"Only one cookie should be found in normal mode.");
 
   // Creates another normal tab and verifies that the cookie is also there.
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey openNewTab]);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey
-      loadURL:web::test::HttpServer::MakeUrl(kTestUrlNormalBrowsing)]);
+  [ChromeEarlGrey openNewTab];
+  [ChromeEarlGrey
+      loadURL:web::test::HttpServer::MakeUrl(kTestUrlNormalBrowsing)];
   cookies = [ChromeEarlGrey cookies];
   GREYAssertEqualObjects(
       kNormalCookieValue, cookies[kNormalCookieName],
diff --git a/ios/chrome/browser/overlays/overlay_request_queue_impl.h b/ios/chrome/browser/overlays/overlay_request_queue_impl.h
index 83bb2b4c..91f9081 100644
--- a/ios/chrome/browser/overlays/overlay_request_queue_impl.h
+++ b/ios/chrome/browser/overlays/overlay_request_queue_impl.h
@@ -71,12 +71,10 @@
   void PopFrontRequest();
   void PopBackRequest();
 
-  // Cancels the UI for all requests in the queue then empties the queue.
-  void CancelAllRequests();
-
   // OverlayRequestQueue:
   void AddRequest(std::unique_ptr<OverlayRequest> request) override;
   OverlayRequest* front_request() const override;
+  void CancelAllRequests() override;
 
  private:
   // Private constructor called by container.
diff --git a/ios/chrome/browser/overlays/overlay_request_queue_impl.mm b/ios/chrome/browser/overlays/overlay_request_queue_impl.mm
index 86c2ec23..b5cd770 100644
--- a/ios/chrome/browser/overlays/overlay_request_queue_impl.mm
+++ b/ios/chrome/browser/overlays/overlay_request_queue_impl.mm
@@ -71,17 +71,6 @@
   requests_.pop_back();
 }
 
-void OverlayRequestQueueImpl::CancelAllRequests() {
-  while (!empty()) {
-    // Requests are cancelled in reverse order to prevent attempting to present
-    // subsequent requests after the dismissal of the front request's UI.
-    for (auto& observer : observers_) {
-      observer.QueuedRequestCancelled(this, requests_.back().get());
-    }
-    PopBackRequest();
-  }
-}
-
 #pragma mark OverlayRequestQueue
 
 void OverlayRequestQueueImpl::AddRequest(
@@ -96,6 +85,17 @@
   return requests_.empty() ? nullptr : requests_.front().get();
 }
 
+void OverlayRequestQueueImpl::CancelAllRequests() {
+  while (!empty()) {
+    // Requests are cancelled in reverse order to prevent attempting to present
+    // subsequent requests after the dismissal of the front request's UI.
+    for (auto& observer : observers_) {
+      observer.QueuedRequestCancelled(this, requests_.back().get());
+    }
+    PopBackRequest();
+  }
+}
+
 #pragma mark RequestCancellationHelper
 
 OverlayRequestQueueImpl::RequestCancellationHelper::RequestCancellationHelper(
diff --git a/ios/chrome/browser/overlays/public/overlay_request_queue.h b/ios/chrome/browser/overlays/public/overlay_request_queue.h
index 0fb1b394..c239f44 100644
--- a/ios/chrome/browser/overlays/public/overlay_request_queue.h
+++ b/ios/chrome/browser/overlays/public/overlay_request_queue.h
@@ -30,6 +30,9 @@
   // queue is updated.
   virtual OverlayRequest* front_request() const = 0;
 
+  // Cancels the UI for all requests in the queue then empties the queue.
+  virtual void CancelAllRequests() = 0;
+
  protected:
   OverlayRequestQueue() = default;
 
diff --git a/ios/chrome/browser/passwords/credential_manager_egtest.mm b/ios/chrome/browser/passwords/credential_manager_egtest.mm
index 7ab718d..982e59b 100644
--- a/ios/chrome/browser/passwords/credential_manager_egtest.mm
+++ b/ios/chrome/browser/passwords/credential_manager_egtest.mm
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 #include "ios/chrome/browser/passwords/credential_manager.h"
-#import "ios/chrome/test/earl_grey/chrome_error_util.h"
 
 #import <EarlGrey/EarlGrey.h>
 #import <UIKit/UIKit.h>
@@ -99,9 +98,8 @@
 - (void)loadSimplePageAndStoreACredential {
   // Loads simple page. It is on localhost so it is considered a secure context.
   const GURL URL = self.testServer->GetURL("/example");
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL]);
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:"You are here."]);
+  [ChromeEarlGrey loadURL:URL];
+  [ChromeEarlGrey waitForWebStateContainingText:"You are here."];
 
   // Obtain a PasswordStore.
   scoped_refptr<password_manager::PasswordStore> passwordStore =
@@ -186,7 +184,7 @@
 
   // Open new tab.
   [ChromeEarlGreyUI openNewTab];
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey waitForMainTabCount:2]);
+  [ChromeEarlGrey waitForMainTabCount:2];
 
   // Execute JavaScript from inactive tab.
   webState->ExecuteJavaScript(
diff --git a/ios/chrome/browser/prerender/prerender_egtest.mm b/ios/chrome/browser/prerender/prerender_egtest.mm
index 80755c4..85c6a2b 100644
--- a/ios/chrome/browser/prerender/prerender_egtest.mm
+++ b/ios/chrome/browser/prerender/prerender_egtest.mm
@@ -13,7 +13,6 @@
 #import "ios/chrome/browser/ui/omnibox/popup/omnibox_popup_truncating_label.h"
 #include "ios/chrome/browser/ui/util/ui_util.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
-#import "ios/chrome/test/earl_grey/chrome_error_util.h"
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
 #include "net/test/embedded_test_server/http_request.h"
@@ -69,28 +68,26 @@
   NSString* pageString = base::SysUTF8ToNSString(pageURL.GetContent());
 
   // Go to the page a couple of time so it shows as suggestion.
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:pageURL]);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey goBack]);
+  [ChromeEarlGrey loadURL:pageURL];
+  [ChromeEarlGrey goBack];
   [[self class] closeAllTabs];
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey openNewTab]);
+  [ChromeEarlGrey openNewTab];
 
   [[EarlGrey selectElementWithMatcher:chrome_test_util::FakeOmnibox()]
       performAction:grey_tap()];
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForSufficientlyVisibleElementWithMatcher:
-                          chrome_test_util::Omnibox()]);
+  [ChromeEarlGrey
+      waitForSufficientlyVisibleElementWithMatcher:chrome_test_util::Omnibox()];
   [[EarlGrey selectElementWithMatcher:chrome_test_util::Omnibox()]
       performAction:grey_typeText([pageString stringByAppendingString:@"\n"])];
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey waitForPageToFinishLoading]);
+  [ChromeEarlGrey waitForPageToFinishLoading];
   [[self class] closeAllTabs];
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey openNewTab]);
+  [ChromeEarlGrey openNewTab];
 
   // Type the begining of the address to have the autocomplete suggestion.
   [[EarlGrey selectElementWithMatcher:chrome_test_util::FakeOmnibox()]
       performAction:grey_tap()];
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForSufficientlyVisibleElementWithMatcher:
-                          chrome_test_util::Omnibox()]);
+  [ChromeEarlGrey
+      waitForSufficientlyVisibleElementWithMatcher:chrome_test_util::Omnibox()];
   [[EarlGrey selectElementWithMatcher:chrome_test_util::Omnibox()]
       performAction:grey_typeText(
                         [pageString substringToIndex:[pageString length] - 6])];
diff --git a/ios/chrome/browser/translate/legacy_translate_infobar_egtest.mm b/ios/chrome/browser/translate/legacy_translate_infobar_egtest.mm
index 005acce..8b6f772 100644
--- a/ios/chrome/browser/translate/legacy_translate_infobar_egtest.mm
+++ b/ios/chrome/browser/translate/legacy_translate_infobar_egtest.mm
@@ -28,7 +28,6 @@
 #include "ios/chrome/browser/ui/translate/language_selection_view_controller.h"
 #import "ios/chrome/test/app/chrome_test_util.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
-#import "ios/chrome/test/earl_grey/chrome_error_util.h"
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
 #import "ios/web/public/test/earl_grey/js_test_util.h"
@@ -319,7 +318,7 @@
   // A page with French text.
   responses[URL] = GetFrenchPageHtml(kHtmlAttribute, "");
   web::test::SetUpSimpleHttpServer(responses);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL]);
+  [ChromeEarlGrey loadURL:URL];
 
   // Check that the "Before Translate" infobar is displayed.
   [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabel(@"English")]
@@ -384,7 +383,7 @@
   }
 
   // Open a new webpage.
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL]);
+  [ChromeEarlGrey loadURL:URL];
   [self simulateTranslationFromSpanishToEnglish];
 
   // Check that the "Always Translate" switch is displayed in the infobar.
@@ -433,8 +432,8 @@
   }
 
   // Do a translation in incognito
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey openNewIncognitoTab]);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL]);
+  [ChromeEarlGrey openNewIncognitoTab];
+  [ChromeEarlGrey loadURL:URL];
   [self simulateTranslationFromSpanishToEnglish];
   // Check that the infobar does not contain the "Always Translate" switch.
   NSString* switchLabel = GetTranslateInfobarSwitchLabel("Spanish");
@@ -469,19 +468,17 @@
   // Translate the page with the link.
   GURL frenchPageURL = web::test::HttpServer::MakeUrl(
       base::StringPrintf("http://%s", kFrenchPageWithLinkPath));
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:frenchPageURL]);
+  [ChromeEarlGrey loadURL:frenchPageURL];
   [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabelId(
                                           IDS_TRANSLATE_INFOBAR_ACCEPT)]
       performAction:grey_tap()];
 
   // Check that the translation happened.
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:"Translated"]);
+  [ChromeEarlGrey waitForWebStateContainingText:"Translated"];
 
   // Click on the link.
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey tapWebStateElementWithID:@"link"]);
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateNotContainingText:"link"]);
+  [ChromeEarlGrey tapWebStateElementWithID:@"link"];
+  [ChromeEarlGrey waitForWebStateNotContainingText:"link"];
 
   GURL frenchPagePathURL = web::test::HttpServer::MakeUrl(
       base::StringPrintf("http://%s", kFrenchPagePath));
@@ -490,8 +487,7 @@
       assertWithMatcher:grey_notNil()];
 
   // Check that the auto-translation happened.
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:"Translated"]);
+  [ChromeEarlGrey waitForWebStateContainingText:"Translated"];
 }
 
 #pragma mark - Utility methods
@@ -507,9 +503,8 @@
 
   // The infobar is presented with an animation. Wait for the "Done" button
   // to become visibile before considering the animation as complete.
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForSufficientlyVisibleElementWithMatcher:
-                          ButtonWithAccessibilityLabelId(IDS_DONE)]);
+  [ChromeEarlGrey waitForSufficientlyVisibleElementWithMatcher:
+                      ButtonWithAccessibilityLabelId(IDS_DONE)];
 
   // Assert that the infobar is visible.
   [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabelId(IDS_DONE)]
diff --git a/ios/chrome/browser/translate/translate_egtest.mm b/ios/chrome/browser/translate/translate_egtest.mm
index a9d953ad..950eec5a 100644
--- a/ios/chrome/browser/translate/translate_egtest.mm
+++ b/ios/chrome/browser/translate/translate_egtest.mm
@@ -39,7 +39,6 @@
 #import "ios/chrome/test/app/chrome_test_util.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
-#import "ios/chrome/test/earl_grey/chrome_error_util.h"
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
 #import "ios/chrome/test/fakes/fake_language_detection_tab_helper_observer.h"
@@ -529,7 +528,7 @@
   expectedLanguageDetails.html_root_language = "de";
   expectedLanguageDetails.adopted_language = translate::kUnknownLanguageCode;
 
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL]);
+  [ChromeEarlGrey loadURL:URL];
   [self assertLanguageDetails:expectedLanguageDetails];
 }
 
@@ -543,7 +542,7 @@
       "<html><body style='display:none'>%s</body></html>", kFrenchText);
   web::test::SetUpSimpleHttpServer(responses);
 
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL]);
+  [ChromeEarlGrey loadURL:URL];
   // Check for no language detected.
   translate::LanguageDetectionDetails expectedLanguageDetails;
   expectedLanguageDetails.adopted_language = translate::kUnknownLanguageCode;
@@ -563,7 +562,7 @@
       base::StringPrintf("http://%s", kFrenchPageNoTranslateValue));
 
   // Load some french page with |content="notranslate"| meta tag.
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:noTranslateContentURL]);
+  [ChromeEarlGrey loadURL:noTranslateContentURL];
 
   // Check that no language has been detected.
   GREYAssert(
@@ -571,7 +570,7 @@
       @"A language has been detected");
 
   // Load some french page with |value="notranslate"| meta tag.
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:noTranslateValueURL]);
+  [ChromeEarlGrey loadURL:noTranslateValueURL];
 
   // Check that no language has been detected.
   GREYAssert(
@@ -588,7 +587,7 @@
   responses[URL] = "<html><body>Blahrg :)</body></html>";
   web::test::SetUpSimpleHttpServer(responses);
 
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL]);
+  [ChromeEarlGrey loadURL:URL];
   // Check for no language detected.
   translate::LanguageDetectionDetails expectedLanguageDetails;
   expectedLanguageDetails.adopted_language = "und";
@@ -627,13 +626,13 @@
   responses[URL] = html;
   web::test::SetUpSimpleHttpServer(responses);
 
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL]);
+  [ChromeEarlGrey loadURL:URL];
   // Check that language has been detected.
   translate::LanguageDetectionDetails expectedLanguageDetails;
   expectedLanguageDetails.adopted_language = "fr";
   [self assertLanguageDetails:expectedLanguageDetails];
   // Trigger the hash change.
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey tapWebStateElementWithID:@"Hash"]);
+  [ChromeEarlGrey tapWebStateElementWithID:@"Hash"];
   // Check that language detection has been re-run.
   expectedLanguageDetails.adopted_language = "en";
   [self assertLanguageDetails:expectedLanguageDetails];
@@ -648,7 +647,7 @@
   // The HTTP header is detected.
   GURL URL = web::test::HttpServer::MakeUrl(std::string("http://") +
                                             kLanguagePath + "?http=fr");
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL]);
+  [ChromeEarlGrey loadURL:URL];
   translate::LanguageDetectionDetails expectedLanguageDetails;
   expectedLanguageDetails.content_language = "fr";
   expectedLanguageDetails.adopted_language = "fr";
@@ -657,7 +656,7 @@
   // Everything after the comma is truncated.
   URL = web::test::HttpServer::MakeUrl(std::string("http://") + kLanguagePath +
                                        "?http=fr,ornot");
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL]);
+  [ChromeEarlGrey loadURL:URL];
   expectedLanguageDetails.content_language = "fr";
   expectedLanguageDetails.adopted_language = "fr";
   [self assertLanguageDetails:expectedLanguageDetails];
@@ -665,7 +664,7 @@
   // The HTTP header is overriden by meta tag.
   URL = web::test::HttpServer::MakeUrl(std::string("http://") + kLanguagePath +
                                        "?http=fr&meta=it");
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL]);
+  [ChromeEarlGrey loadURL:URL];
   expectedLanguageDetails.content_language = "it";
   expectedLanguageDetails.adopted_language = "it";
   [self assertLanguageDetails:expectedLanguageDetails];
@@ -673,7 +672,7 @@
   // Only the header of the main page is detected.
   URL =
       web::test::HttpServer::MakeUrl(std::string("http://") + kSubresourcePath);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL]);
+  [ChromeEarlGrey loadURL:URL];
   expectedLanguageDetails.content_language = "fr";
   expectedLanguageDetails.adopted_language = "fr";
   [self assertLanguageDetails:expectedLanguageDetails];
@@ -688,10 +687,9 @@
   // Detection works when clicking on a link.
   GURL URL = web::test::HttpServer::MakeUrl(std::string("http://") + kLinkPath);
   GURL someLanguageURL = web::test::HttpServer::MakeUrl(kSomeLanguageUrl);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL]);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey tapWebStateElementWithID:@"click"]);
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:kLanguagePathText]);
+  [ChromeEarlGrey loadURL:URL];
+  [ChromeEarlGrey tapWebStateElementWithID:@"click"];
+  [ChromeEarlGrey waitForWebStateContainingText:kLanguagePathText];
   [[EarlGrey selectElementWithMatcher:chrome_test_util::OmniboxText(
                                           someLanguageURL.GetContent())]
       assertWithMatcher:grey_notNil()];
@@ -720,7 +718,7 @@
       web::test::HttpServer::MakeUrl("http://languageDetectionLargePage");
   responses[URL] = html;
   web::test::SetUpSimpleHttpServer(responses);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL]);
+  [ChromeEarlGrey loadURL:URL];
 
   // Check that language has been detected.
   translate::LanguageDetectionDetails expectedLanguageDetails;
@@ -744,7 +742,7 @@
       prefs::kOfferTranslateEnabled, NO);
 
   // Open some webpage.
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL]);
+  [ChromeEarlGrey loadURL:URL];
   // Check that no language has been detected.
   GREYAssert(
       !language_detection_tab_helper_observer_->GetLanguageDetectionDetails(),
@@ -766,7 +764,7 @@
   // Load a page with French text.
   GURL URL = web::test::HttpServer::MakeUrl(
       base::StringPrintf("http://%s", kFrenchPagePath));
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL]);
+  [ChromeEarlGrey loadURL:URL];
 
   [self assertTranslateInfobarIsVisible];
 
@@ -812,7 +810,7 @@
   // Load a page with French text.
   GURL URL = web::test::HttpServer::MakeUrl(
       base::StringPrintf("http://%s", kFrenchPagePath));
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL]);
+  [ChromeEarlGrey loadURL:URL];
 
   [self assertTranslateInfobarIsVisible];
 
@@ -845,7 +843,7 @@
   // Load a page with French text.
   GURL URL = web::test::HttpServer::MakeUrl(
       base::StringPrintf("http://%s", kFrenchPagePath));
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL]);
+  [ChromeEarlGrey loadURL:URL];
 
   [self translateThenRevert];
 }
@@ -863,8 +861,8 @@
   // Stop observing the current IOSLanguageDetectionTabHelper before opening the
   // incognito tab.
   language_detection_tab_helper_observer_.reset();
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey openNewIncognitoTab]);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL]);
+  [ChromeEarlGrey openNewIncognitoTab];
+  [ChromeEarlGrey loadURL:URL];
 
   // Needed for the incognito WebState.
   [self setUpMockScriptManager];
@@ -877,8 +875,7 @@
   [self assertTranslateInfobarIsVisible];
 
   // Make sure the page is not translated.
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateNotContainingText:"Translated"]);
+  [ChromeEarlGrey waitForWebStateNotContainingText:"Translated"];
 
   // The source language tab must be selected and the target language tab must
   // not. Translate the page by tapping the target language tab.
@@ -888,8 +885,7 @@
       assertWithMatcher:ElementIsSelected(NO)] performAction:grey_tap()];
 
   // Make sure the page is translated.
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:"Translated"]);
+  [ChromeEarlGrey waitForWebStateContainingText:"Translated"];
 
   // The target language tab must be selected and the source language tab must
   // not. Revert the translation by tapping the source language tab.
@@ -899,8 +895,7 @@
       assertWithMatcher:ElementIsSelected(NO)] performAction:grey_tap()];
 
   // Make sure the translation is reverted.
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateNotContainingText:"Translated"]);
+  [ChromeEarlGrey waitForWebStateNotContainingText:"Translated"];
 
   // The source language tab must be selected and the target language tab must
   // not.
@@ -920,13 +915,12 @@
   // Load a page with French text and a link.
   GURL URL = web::test::HttpServer::MakeUrl(
       base::StringPrintf("http://%s", kFrenchPageWithLinkPath));
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL]);
+  [ChromeEarlGrey loadURL:URL];
 
   [self assertTranslateInfobarIsVisible];
 
   // Make sure the page is not translated.
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateNotContainingText:"Translated"]);
+  [ChromeEarlGrey waitForWebStateNotContainingText:"Translated"];
 
   // The target language tab must not be selected. Translate the page by
   // tapping the target language tab.
@@ -934,11 +928,10 @@
       assertWithMatcher:ElementIsSelected(NO)] performAction:grey_tap()];
 
   // Make sure the page is translated.
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:"Translated"]);
+  [ChromeEarlGrey waitForWebStateContainingText:"Translated"];
 
   // Click on the link.
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey tapWebStateElementWithID:@"link"]);
+  [ChromeEarlGrey tapWebStateElementWithID:@"link"];
 
   // Make sure the navigation is completed.
   GURL frenchPagePathURL = web::test::HttpServer::MakeUrl(
@@ -948,8 +941,7 @@
       assertWithMatcher:grey_notNil()];
 
   // Make sure the page is automatically translated.
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:"Translated"]);
+  [ChromeEarlGrey waitForWebStateContainingText:"Translated"];
 }
 
 // Tests that the source and the target languages can be changed.
@@ -961,7 +953,7 @@
   // Load a page with French text.
   GURL URL = web::test::HttpServer::MakeUrl(
       base::StringPrintf("http://%s", kFrenchPagePath));
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL]);
+  [ChromeEarlGrey loadURL:URL];
 
   [self assertTranslateInfobarIsVisible];
 
@@ -1002,8 +994,7 @@
       assertWithMatcher:grey_nil()];
 
   // Make sure the page is translated.
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:"Translated"]);
+  [ChromeEarlGrey waitForWebStateContainingText:"Translated"];
 
   // Make sure the target language changes to "Dutch". The target language
   // tab must be selected and the source language tab must not. Revert the
@@ -1014,8 +1005,7 @@
       assertWithMatcher:ElementIsSelected(NO)] performAction:grey_tap()];
 
   // Make sure the translation is reverted.
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateNotContainingText:"Translated"]);
+  [ChromeEarlGrey waitForWebStateNotContainingText:"Translated"];
 
   // Open the translate options menu.
   [[EarlGrey selectElementWithMatcher:OptionsButton()]
@@ -1039,8 +1029,7 @@
       onElementWithMatcher:LanguagesMenu()] performAction:grey_tap()];
 
   // Make sure the page is translated.
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:"Translated"]);
+  [ChromeEarlGrey waitForWebStateContainingText:"Translated"];
 
   // Make sure the source language changes to "English". The target language
   // tab must be selected and the source language tab must not.
@@ -1060,7 +1049,7 @@
   // Load a page with French text.
   GURL URL = web::test::HttpServer::MakeUrl(
       base::StringPrintf("http://%s", kFrenchPagePath));
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL]);
+  [ChromeEarlGrey loadURL:URL];
 
   [self assertTranslateInfobarIsVisible];
 
@@ -1084,8 +1073,7 @@
       assertWithMatcher:grey_nil()];
 
   // Make sure the page is not translated yet.
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateNotContainingText:"Translated"]);
+  [ChromeEarlGrey waitForWebStateNotContainingText:"Translated"];
 
   // Make sure that French to English translation is not whitelisted yet.
   GREYAssert(!translatePrefs->IsLanguagePairWhitelisted("fr", "en"),
@@ -1100,8 +1088,7 @@
       performAction:grey_tap()];
 
   // Make sure the page is translated after the snackbar is dismissed.
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:"Translated"]);
+  [ChromeEarlGrey waitForWebStateContainingText:"Translated"];
 
   // Make sure that French to English translation is whitelisted after the
   // snackbar is dismissed.
@@ -1109,13 +1096,12 @@
              @"French to English translation is not whitelisted");
 
   // Reload the page.
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey reload]);
+  [ChromeEarlGrey reload];
 
   [self assertTranslateInfobarIsVisible];
 
   // Make sure the page is translated.
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:"Translated"]);
+  [ChromeEarlGrey waitForWebStateContainingText:"Translated"];
 
   // The target language tab must be selected and the source language tab must
   // not.
@@ -1170,7 +1156,7 @@
   // Load a page with French text.
   GURL URL = web::test::HttpServer::MakeUrl(
       base::StringPrintf("http://%s", kFrenchPagePath));
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL]);
+  [ChromeEarlGrey loadURL:URL];
 
   [self assertTranslateInfobarIsVisible];
 
@@ -1219,7 +1205,7 @@
   // Load a page with French text.
   GURL URL = web::test::HttpServer::MakeUrl(
       base::StringPrintf("http://%s", kFrenchPagePath));
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL]);
+  [ChromeEarlGrey loadURL:URL];
 
   [self assertTranslateInfobarIsVisible];
 
@@ -1277,7 +1263,7 @@
   // Load a page with French text.
   GURL URL = web::test::HttpServer::MakeUrl(
       base::StringPrintf("http://%s", kFrenchPagePath));
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL]);
+  [ChromeEarlGrey loadURL:URL];
 
   [self assertTranslateInfobarIsVisible];
 
@@ -1336,7 +1322,7 @@
              @"Translation from French is not blocked");
 
   // Reload the page.
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey reload]);
+  [ChromeEarlGrey reload];
 
   // Make sure the translate infobar does not appear.
   GREYAssertFalse([self waitForElementToAppearOrTimeout:TranslateInfobar()],
@@ -1353,7 +1339,7 @@
   // Load a page with French text.
   GURL URL = web::test::HttpServer::MakeUrl(
       base::StringPrintf("http://%s", kFrenchPagePath));
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL]);
+  [ChromeEarlGrey loadURL:URL];
 
   [self assertTranslateInfobarIsVisible];
 
@@ -1369,7 +1355,7 @@
   for (int i = 0;
        i < translate::TranslateInfoBarDelegate::GetAutoNeverThreshold(); i++) {
     // Reload the page.
-    CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey reload]);
+    [ChromeEarlGrey reload];
 
     [self assertTranslateInfobarIsVisible];
 
@@ -1412,7 +1398,7 @@
   // Load a page with French text.
   GURL URL = web::test::HttpServer::MakeUrl(
       base::StringPrintf("http://%s", kFrenchPagePath));
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL]);
+  [ChromeEarlGrey loadURL:URL];
 
   [self assertTranslateInfobarIsVisible];
 
@@ -1433,7 +1419,7 @@
          j < translate::TranslateInfoBarDelegate::GetAutoNeverThreshold();
          j++) {
       // Reload the page.
-      CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey reload]);
+      [ChromeEarlGrey reload];
 
       [self assertTranslateInfobarIsVisible];
 
@@ -1454,7 +1440,7 @@
   for (int i = 0;
        i < translate::TranslateInfoBarDelegate::GetAutoNeverThreshold(); i++) {
     // Reload the page.
-    CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey reload]);
+    [ChromeEarlGrey reload];
 
     [self assertTranslateInfobarIsVisible];
 
@@ -1482,7 +1468,7 @@
   // Load a page with French text.
   GURL URL = web::test::HttpServer::MakeUrl(
       base::StringPrintf("http://%s", kFrenchPagePath));
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL]);
+  [ChromeEarlGrey loadURL:URL];
 
   [self assertTranslateInfobarIsVisible];
 
@@ -1540,7 +1526,7 @@
              @"Translate infobar failed to disappear.");
 
   // Reload the page.
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey reload]);
+  [ChromeEarlGrey reload];
 
   // Make sure the translate infobar does not appear.
   GREYAssertFalse([self waitForElementToAppearOrTimeout:TranslateInfobar()],
@@ -1561,7 +1547,7 @@
   // Load a page with French text.
   GURL URL = web::test::HttpServer::MakeUrl(
       base::StringPrintf("http://%s", kFrenchPagePath));
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL]);
+  [ChromeEarlGrey loadURL:URL];
 
   [self assertTranslateInfobarIsVisible];
 
@@ -1606,8 +1592,7 @@
       assertWithMatcher:ElementIsSelected(NO)];
 
   // Make sure the page is translated.
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:"Translated"]);
+  [ChromeEarlGrey waitForWebStateContainingText:"Translated"];
 
   // Dismiss the translate infobar.
   [[EarlGrey selectElementWithMatcher:CloseButton()] performAction:grey_tap()];
@@ -1645,7 +1630,7 @@
   // Load a page with French text.
   GURL URL = web::test::HttpServer::MakeUrl(
       base::StringPrintf("http://%s", kFrenchPagePath));
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL]);
+  [ChromeEarlGrey loadURL:URL];
 
   [self assertTranslateInfobarIsVisible];
 
@@ -1669,7 +1654,7 @@
              @"Translate infobar failed to disappear.");
 
   // Reload the page.
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey reload]);
+  [ChromeEarlGrey reload];
 
   // Make sure the translate infobar does not appear.
   GREYAssertFalse([self waitForElementToAppearOrTimeout:TranslateInfobar()],
@@ -1698,7 +1683,7 @@
   // Load a page with French text.
   GURL URL = web::test::HttpServer::MakeUrl(
       base::StringPrintf("http://%s", kFrenchPagePath));
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL]);
+  [ChromeEarlGrey loadURL:URL];
 
   [self assertTranslateInfobarIsVisible];
 
@@ -1721,7 +1706,7 @@
              @"Translate infobar failed to disappear.");
 
   // Reload the page.
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey reload]);
+  [ChromeEarlGrey reload];
 
   // Make sure the translate infobar does not appear.
   GREYAssertFalse([self waitForElementToAppearOrTimeout:TranslateInfobar()],
@@ -1749,7 +1734,7 @@
   // Load a page with French text with |content="notranslate"| meta tag.
   GURL noTranslateContentURL = web::test::HttpServer::MakeUrl(
       base::StringPrintf("http://%s", kFrenchPageNoTranslateContent));
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:noTranslateContentURL]);
+  [ChromeEarlGrey loadURL:noTranslateContentURL];
 
   // Make sure no language has been detected.
   GREYAssert(
@@ -1769,7 +1754,7 @@
   // Load a page with French text with |value="notranslate"| meta tag.
   GURL noTranslateValueURL = web::test::HttpServer::MakeUrl(
       base::StringPrintf("http://%s", kFrenchPageNoTranslateValue));
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:noTranslateValueURL]);
+  [ChromeEarlGrey loadURL:noTranslateValueURL];
 
   // Make sure no language has been detected.
   GREYAssert(
@@ -1788,7 +1773,7 @@
 
   // Load a chrome:// page.
   GURL URL = web::test::HttpServer::MakeUrl("chrome://something-internal");
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL]);
+  [ChromeEarlGrey loadURL:URL];
 
   // Make sure the Translate manual trigger button is not enabled.
   [ChromeEarlGreyUI openToolsMenu];
@@ -1813,7 +1798,7 @@
   // Load a page with French text.
   GURL URL = web::test::HttpServer::MakeUrl(
       base::StringPrintf("http://%s", kFrenchPagePath));
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL]);
+  [ChromeEarlGrey loadURL:URL];
 
   [self assertTranslateInfobarIsVisible];
 
diff --git a/ios/chrome/browser/ui/browser_container/BUILD.gn b/ios/chrome/browser/ui/browser_container/BUILD.gn
index 888fc87..b16cbdc 100644
--- a/ios/chrome/browser/ui/browser_container/BUILD.gn
+++ b/ios/chrome/browser/ui/browser_container/BUILD.gn
@@ -14,6 +14,7 @@
     ":ui",
     "//base",
     "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
+    "//ios/chrome/browser/ui/overlays",
   ]
 }
 
diff --git a/ios/chrome/browser/ui/browser_container/browser_container_coordinator.mm b/ios/chrome/browser/ui/browser_container/browser_container_coordinator.mm
index d2b9495..f3bd8da 100644
--- a/ios/chrome/browser/ui/browser_container/browser_container_coordinator.mm
+++ b/ios/chrome/browser/ui/browser_container/browser_container_coordinator.mm
@@ -6,13 +6,19 @@
 
 #include "base/logging.h"
 #import "ios/chrome/browser/ui/browser_container/browser_container_view_controller.h"
+#import "ios/chrome/browser/ui/overlays/overlay_container_coordinator.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
+@interface BrowserContainerCoordinator ()
+// The overlay container coordinator for OverlayModality::kWebContentArea.
+@property(nonatomic, strong)
+    OverlayContainerCoordinator* webContentAreaOverlayContainerCoordinator;
+@end
+
 @implementation BrowserContainerCoordinator
-@synthesize viewController = _viewController;
 
 #pragma mark - ChromeCoordinator
 
@@ -20,10 +26,17 @@
   DCHECK(self.browserState);
   DCHECK(!_viewController);
   _viewController = [[BrowserContainerViewController alloc] init];
+  self.webContentAreaOverlayContainerCoordinator =
+      [[OverlayContainerCoordinator alloc]
+          initWithBaseViewController:_viewController
+                             browser:self.browser
+                            modality:OverlayModality::kWebContentArea];
+  [self.webContentAreaOverlayContainerCoordinator start];
   [super start];
 }
 
 - (void)stop {
+  [self.webContentAreaOverlayContainerCoordinator stop];
   _viewController = nil;
   [super stop];
 }
diff --git a/ios/chrome/browser/ui/browser_view/BUILD.gn b/ios/chrome/browser/ui/browser_view/BUILD.gn
index 51a4b16..40d0c52 100644
--- a/ios/chrome/browser/ui/browser_view/BUILD.gn
+++ b/ios/chrome/browser/ui/browser_view/BUILD.gn
@@ -90,6 +90,7 @@
     "//ios/chrome/browser/ui/context_menu",
     "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
     "//ios/chrome/browser/ui/dialogs:dialogs_internal",
+    "//ios/chrome/browser/ui/dialogs:feature_flags",
     "//ios/chrome/browser/ui/download",
     "//ios/chrome/browser/ui/elements:elements_internal",
     "//ios/chrome/browser/ui/find_bar",
diff --git a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
index e60d668..06b84a2 100644
--- a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
@@ -103,8 +103,10 @@
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_constant.h"
 #import "ios/chrome/browser/ui/context_menu/context_menu_coordinator.h"
 #import "ios/chrome/browser/ui/context_menu/context_menu_item.h"
+#import "ios/chrome/browser/ui/dialogs/dialog_features.h"
 #import "ios/chrome/browser/ui/dialogs/dialog_presenter.h"
 #import "ios/chrome/browser/ui/dialogs/java_script_dialog_presenter_impl.h"
+#import "ios/chrome/browser/ui/dialogs/overlay_java_script_dialog_presenter.h"
 #import "ios/chrome/browser/ui/download/download_manager_coordinator.h"
 #import "ios/chrome/browser/ui/elements/activity_overlay_coordinator.h"
 #import "ios/chrome/browser/ui/find_bar/find_bar_controller_ios.h"
@@ -423,7 +425,7 @@
   DialogPresenter* _dialogPresenter;
 
   // Handles presentation of JavaScript dialogs.
-  std::unique_ptr<JavaScriptDialogPresenterImpl> _javaScriptDialogPresenter;
+  std::unique_ptr<web::JavaScriptDialogPresenter> _javaScriptDialogPresenter;
 
   // Keyboard commands provider.  It offloads most of the keyboard commands
   // management off of the BVC.
@@ -799,8 +801,6 @@
 
     _browserContainerViewController = browserContainerViewController;
     _dependencyFactory = factory;
-    _dialogPresenter = [[DialogPresenter alloc] initWithDelegate:self
-                                        presentingViewController:self];
     self.commandDispatcher = commandDispatcher;
     [self.commandDispatcher
         startDispatchingToTarget:self
@@ -837,8 +837,15 @@
     _downloadManagerCoordinator.presenter =
         [[VerticalAnimationContainer alloc] init];
 
-    _javaScriptDialogPresenter.reset(
-        new JavaScriptDialogPresenterImpl(_dialogPresenter));
+    if (base::FeatureList::IsEnabled(dialogs::kNonModalDialogs)) {
+      _javaScriptDialogPresenter =
+          std::make_unique<OverlayJavaScriptDialogPresenter>();
+    } else {
+      _dialogPresenter = [[DialogPresenter alloc] initWithDelegate:self
+                                          presentingViewController:self];
+      _javaScriptDialogPresenter =
+          std::make_unique<JavaScriptDialogPresenterImpl>(_dialogPresenter);
+    }
     _webStateDelegate.reset(new web::WebStateDelegateBridge(self));
     _inNewTabAnimation = NO;
 
diff --git a/ios/chrome/browser/ui/dialogs/BUILD.gn b/ios/chrome/browser/ui/dialogs/BUILD.gn
index 60589a8..e5b8ba0 100644
--- a/ios/chrome/browser/ui/dialogs/BUILD.gn
+++ b/ios/chrome/browser/ui/dialogs/BUILD.gn
@@ -56,6 +56,8 @@
     "java_script_dialog_presenter_impl.mm",
     "nsurl_protection_space_util.h",
     "nsurl_protection_space_util.mm",
+    "overlay_java_script_dialog_presenter.h",
+    "overlay_java_script_dialog_presenter.mm",
   ]
   deps = [
     ":completion_block_util",
@@ -64,6 +66,8 @@
     "//components/strings",
     "//components/url_formatter",
     "//ios/chrome/app/strings",
+    "//ios/chrome/browser/overlays",
+    "//ios/chrome/browser/overlays/public/web_content_area",
     "//ios/chrome/browser/ui/alert_coordinator",
     "//ios/chrome/browser/ui/alert_coordinator",
     "//ios/chrome/browser/ui/dialogs/non_modal",
diff --git a/ios/chrome/browser/ui/dialogs/dialog_features.mm b/ios/chrome/browser/ui/dialogs/dialog_features.mm
index 8cc9ccb..0807bd2 100644
--- a/ios/chrome/browser/ui/dialogs/dialog_features.mm
+++ b/ios/chrome/browser/ui/dialogs/dialog_features.mm
@@ -11,6 +11,6 @@
 namespace dialogs {
 
 const base::Feature kNonModalDialogs{"kNonModalDialogs",
-                                     base::FEATURE_ENABLED_BY_DEFAULT};
+                                     base::FEATURE_DISABLED_BY_DEFAULT};
 
 }  // namespace dialogs
diff --git a/ios/chrome/browser/ui/dialogs/java_script_dialog_presenter_impl.h b/ios/chrome/browser/ui/dialogs/java_script_dialog_presenter_impl.h
index 2de7b9fc..cdb134c 100644
--- a/ios/chrome/browser/ui/dialogs/java_script_dialog_presenter_impl.h
+++ b/ios/chrome/browser/ui/dialogs/java_script_dialog_presenter_impl.h
@@ -17,7 +17,7 @@
     : public web::JavaScriptDialogPresenter {
  public:
   explicit JavaScriptDialogPresenterImpl(DialogPresenter* dialogPresenter);
-  ~JavaScriptDialogPresenterImpl();
+  ~JavaScriptDialogPresenterImpl() override;
 
   void RunJavaScriptDialog(web::WebState* web_state,
                            const GURL& origin_url,
diff --git a/ios/chrome/browser/ui/dialogs/overlay_java_script_dialog_presenter.h b/ios/chrome/browser/ui/dialogs/overlay_java_script_dialog_presenter.h
new file mode 100644
index 0000000..b599a7c
--- /dev/null
+++ b/ios/chrome/browser/ui/dialogs/overlay_java_script_dialog_presenter.h
@@ -0,0 +1,41 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_DIALOGS_OVERLAY_JAVA_SCRIPT_DIALOG_PRESENTER_H_
+#define IOS_CHROME_BROWSER_UI_DIALOGS_OVERLAY_JAVA_SCRIPT_DIALOG_PRESENTER_H_
+
+#include "base/memory/weak_ptr.h"
+#include "ios/web/public/java_script_dialog_presenter.h"
+
+class OverlayResponse;
+
+// Implementation of JavaScriptDialogPresenter that uses OverlayPresenter to run
+// JavaScript dialogs.
+class OverlayJavaScriptDialogPresenter final
+    : public web::JavaScriptDialogPresenter {
+ public:
+  OverlayJavaScriptDialogPresenter();
+  ~OverlayJavaScriptDialogPresenter() override;
+
+  // web::JavaScriptDialogPresenter:
+  void RunJavaScriptDialog(web::WebState* web_state,
+                           const GURL& origin_url,
+                           web::JavaScriptDialogType dialog_type,
+                           NSString* message_text,
+                           NSString* default_prompt_text,
+                           web::DialogClosedCallback callback) override;
+  void CancelDialogs(web::WebState* web_state) override;
+
+ private:
+  // Executes |callback| using the user interaction information from |response|.
+  void HandleJavaScriptDialogResponse(web::DialogClosedCallback callback,
+                                      web::JavaScriptDialogType dialog_type,
+                                      OverlayResponse* response);
+
+  DISALLOW_COPY_AND_ASSIGN(OverlayJavaScriptDialogPresenter);
+
+  base::WeakPtrFactory<OverlayJavaScriptDialogPresenter> weak_factory_;
+};
+
+#endif  // IOS_CHROME_BROWSER_UI_DIALOGS_OVERLAY_JAVA_SCRIPT_DIALOG_PRESENTER_H_
diff --git a/ios/chrome/browser/ui/dialogs/overlay_java_script_dialog_presenter.mm b/ios/chrome/browser/ui/dialogs/overlay_java_script_dialog_presenter.mm
new file mode 100644
index 0000000..84f164e6
--- /dev/null
+++ b/ios/chrome/browser/ui/dialogs/overlay_java_script_dialog_presenter.mm
@@ -0,0 +1,99 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/dialogs/overlay_java_script_dialog_presenter.h"
+
+#include "base/bind.h"
+#import "base/strings/sys_string_conversions.h"
+#import "ios/chrome/browser/overlays/public/overlay_request.h"
+#import "ios/chrome/browser/overlays/public/overlay_request_queue.h"
+#import "ios/chrome/browser/overlays/public/overlay_response.h"
+#import "ios/chrome/browser/overlays/public/web_content_area/java_script_alert_overlay.h"
+#import "ios/chrome/browser/overlays/public/web_content_area/java_script_confirmation_overlay.h"
+#import "ios/chrome/browser/overlays/public/web_content_area/java_script_prompt_overlay.h"
+#import "ios/web/public/web_state/web_state.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+OverlayJavaScriptDialogPresenter::OverlayJavaScriptDialogPresenter()
+    : weak_factory_(this) {}
+
+OverlayJavaScriptDialogPresenter::~OverlayJavaScriptDialogPresenter() = default;
+
+void OverlayJavaScriptDialogPresenter::RunJavaScriptDialog(
+    web::WebState* web_state,
+    const GURL& origin_url,
+    web::JavaScriptDialogType dialog_type,
+    NSString* message_text,
+    NSString* default_prompt_text,
+    web::DialogClosedCallback callback) {
+  std::unique_ptr<OverlayRequest> request;
+  bool from_main_frame_origin =
+      origin_url.GetOrigin() == web_state->GetLastCommittedURL().GetOrigin();
+  switch (dialog_type) {
+    case web::JAVASCRIPT_DIALOG_TYPE_ALERT:
+      request =
+          OverlayRequest::CreateWithConfig<JavaScriptAlertOverlayRequestConfig>(
+              origin_url, from_main_frame_origin,
+              base::SysNSStringToUTF8(message_text));
+      break;
+    case web::JAVASCRIPT_DIALOG_TYPE_CONFIRM:
+      request = OverlayRequest::CreateWithConfig<
+          JavaScriptConfirmationOverlayRequestConfig>(
+          origin_url, from_main_frame_origin,
+          base::SysNSStringToUTF8(message_text));
+      break;
+    case web::JAVASCRIPT_DIALOG_TYPE_PROMPT:
+      request = OverlayRequest::CreateWithConfig<
+          JavaScriptPromptOverlayRequestConfig>(
+          origin_url, from_main_frame_origin,
+          base::SysNSStringToUTF8(message_text),
+          base::SysNSStringToUTF8(default_prompt_text));
+      break;
+  }
+  request->set_callback(base::BindOnce(
+      &OverlayJavaScriptDialogPresenter::HandleJavaScriptDialogResponse,
+      weak_factory_.GetWeakPtr(), std::move(callback), dialog_type));
+  OverlayRequestQueue::FromWebState(web_state, OverlayModality::kWebContentArea)
+      ->AddRequest(std::move(request));
+}
+
+void OverlayJavaScriptDialogPresenter::CancelDialogs(web::WebState* web_state) {
+  OverlayRequestQueue::FromWebState(web_state, OverlayModality::kWebContentArea)
+      ->CancelAllRequests();
+}
+
+void OverlayJavaScriptDialogPresenter::HandleJavaScriptDialogResponse(
+    web::DialogClosedCallback callback,
+    web::JavaScriptDialogType dialog_type,
+    OverlayResponse* response) {
+  if (!response) {
+    std::move(callback).Run(false, nil);
+    return;
+  }
+
+  bool success = false;
+  NSString* user_input = nil;
+  switch (dialog_type) {
+    case web::JAVASCRIPT_DIALOG_TYPE_ALERT:
+      success = true;
+      break;
+    case web::JAVASCRIPT_DIALOG_TYPE_CONFIRM: {
+      JavaScriptConfirmationOverlayResponseInfo* confirmation_info =
+          response->GetInfo<JavaScriptConfirmationOverlayResponseInfo>();
+      success = confirmation_info && confirmation_info->dialog_confirmed();
+    } break;
+    case web::JAVASCRIPT_DIALOG_TYPE_PROMPT: {
+      JavaScriptPromptOverlayResponseInfo* prompt_info =
+          response->GetInfo<JavaScriptPromptOverlayResponseInfo>();
+      if (prompt_info) {
+        success = true;
+        user_input = base::SysUTF8ToNSString(prompt_info->text_input());
+      }
+    } break;
+  }
+  std::move(callback).Run(success, user_input);
+}
diff --git a/ios/chrome/browser/ui/keyboard/keyboard_commands_egtest.mm b/ios/chrome/browser/ui/keyboard/keyboard_commands_egtest.mm
index 46e9e0920..8c7f46f 100644
--- a/ios/chrome/browser/ui/keyboard/keyboard_commands_egtest.mm
+++ b/ios/chrome/browser/ui/keyboard/keyboard_commands_egtest.mm
@@ -17,7 +17,6 @@
 #import "ios/chrome/test/app/chrome_test_util.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
-#import "ios/chrome/test/earl_grey/chrome_error_util.h"
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
 #import "ios/web/public/test/http_server/http_server.h"
@@ -114,14 +113,14 @@
 // Tests that keyboard commands are not registered when the bookmark UI is
 // shown.
 - (void)testKeyboardCommandsNotRegistered_AddBookmarkPresented {
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey waitForBookmarksToFinishLoading]);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey clearBookmarks]);
+  [ChromeEarlGrey waitForBookmarksToFinishLoading];
+  [ChromeEarlGrey clearBookmarks];
 
   // Load a webpage because the NTP is not always bookmarkable.
   web::test::SetUpFileBasedHttpServer();
   GURL URL = web::test::HttpServer::MakeUrl(
       "http://ios/testing/data/http_server_files/pony.html");
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL]);
+  [ChromeEarlGrey loadURL:URL];
 
   // Bookmark page
   if (IsIPadIdiom()) {
@@ -190,7 +189,7 @@
   web::test::SetUpFileBasedHttpServer();
   GURL URL = web::test::HttpServer::MakeUrl(
       "http://ios/testing/data/http_server_files/pony.html");
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL]);
+  [ChromeEarlGrey loadURL:URL];
 
   [self verifyKeyboardCommandsAreRegistered];
 
diff --git a/ios/chrome/browser/ui/popup_menu/popup_menu_egtest.mm b/ios/chrome/browser/ui/popup_menu/popup_menu_egtest.mm
index 22575cb..5613455 100644
--- a/ios/chrome/browser/ui/popup_menu/popup_menu_egtest.mm
+++ b/ios/chrome/browser/ui/popup_menu/popup_menu_egtest.mm
@@ -12,7 +12,6 @@
 #include "ios/chrome/test/earl_grey/accessibility_util.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
-#import "ios/chrome/test/earl_grey/chrome_error_util.h"
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
 #import "ios/web/public/test/http_server/http_server.h"
@@ -68,10 +67,10 @@
   web::test::SetUpSimpleHttpServer(responses);
 
   // Load 4 pages.
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL1]);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL2]);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL3]);
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL4]);
+  [ChromeEarlGrey loadURL:URL1];
+  [ChromeEarlGrey loadURL:URL2];
+  [ChromeEarlGrey loadURL:URL3];
+  [ChromeEarlGrey loadURL:URL4];
 
   // Long press on back button.
   [[EarlGrey selectElementWithMatcher:chrome_test_util::BackButton()]
@@ -158,7 +157,7 @@
   const GURL URL = web::test::HttpServer::MakeUrl(kPDFURL);
 
   // Navigate to a mock pdf and verify that the find button is disabled.
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:URL]);
+  [ChromeEarlGrey loadURL:URL];
   [ChromeEarlGreyUI openToolsMenu];
   [[EarlGrey
       selectElementWithMatcher:grey_accessibilityID(kToolsMenuFindInPageId)]
diff --git a/ios/chrome/browser/ui/popup_menu/request_desktop_mobile_site_egtest.mm b/ios/chrome/browser/ui/popup_menu/request_desktop_mobile_site_egtest.mm
index b452545..ecc6ce2 100644
--- a/ios/chrome/browser/ui/popup_menu/request_desktop_mobile_site_egtest.mm
+++ b/ios/chrome/browser/ui/popup_menu/request_desktop_mobile_site_egtest.mm
@@ -14,7 +14,6 @@
 #import "ios/chrome/test/earl_grey/accessibility_util.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
-#import "ios/chrome/test/earl_grey/chrome_error_util.h"
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
 #include "ios/web/public/test/http_server/data_response_provider.h"
@@ -104,23 +103,18 @@
       new UserAgentResponseProvider());
   web::test::SetUpHttpServer(std::move(provider));
 
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey loadURL:web::test::HttpServer::MakeUrl("http://1.com")]);
+  [ChromeEarlGrey loadURL:web::test::HttpServer::MakeUrl("http://1.com")];
   // Verify initial reception of the mobile site.
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:kMobileSiteLabel]);
+  [ChromeEarlGrey waitForWebStateContainingText:kMobileSiteLabel];
 
   // Request and verify reception of the desktop site.
   [ChromeEarlGreyUI openToolsMenu];
   [RequestDesktopButton() performAction:grey_tap()];
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:kDesktopSiteLabel]);
+  [ChromeEarlGrey waitForWebStateContainingText:kDesktopSiteLabel];
 
   // Verify that desktop user agent propagates.
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey loadURL:web::test::HttpServer::MakeUrl("http://2.com")]);
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:kDesktopSiteLabel]);
+  [ChromeEarlGrey loadURL:web::test::HttpServer::MakeUrl("http://2.com")];
+  [ChromeEarlGrey waitForWebStateContainingText:kDesktopSiteLabel];
 }
 
 // Tests that requesting desktop site of a page works and desktop user agent
@@ -130,24 +124,19 @@
       new UserAgentResponseProvider());
   web::test::SetUpHttpServer(std::move(provider));
 
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey loadURL:web::test::HttpServer::MakeUrl("http://1.com")]);
+  [ChromeEarlGrey loadURL:web::test::HttpServer::MakeUrl("http://1.com")];
   // Verify initial reception of the mobile site.
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:kMobileSiteLabel]);
+  [ChromeEarlGrey waitForWebStateContainingText:kMobileSiteLabel];
 
   // Request and verify reception of the desktop site.
   [ChromeEarlGreyUI openToolsMenu];
   [RequestDesktopButton() performAction:grey_tap()];
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:kDesktopSiteLabel]);
+  [ChromeEarlGrey waitForWebStateContainingText:kDesktopSiteLabel];
 
   // Verify that desktop user agent does not propagate to new tab.
   [ChromeEarlGreyUI openNewTab];
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey loadURL:web::test::HttpServer::MakeUrl("http://2.com")]);
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:kMobileSiteLabel]);
+  [ChromeEarlGrey loadURL:web::test::HttpServer::MakeUrl("http://2.com")];
+  [ChromeEarlGrey waitForWebStateContainingText:kMobileSiteLabel];
 }
 
 // Tests that requesting desktop site of a page works and going back re-opens
@@ -157,23 +146,19 @@
       new UserAgentResponseProvider());
   web::test::SetUpHttpServer(std::move(provider));
 
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey loadURL:web::test::HttpServer::MakeUrl("http://1.com")]);
+  [ChromeEarlGrey loadURL:web::test::HttpServer::MakeUrl("http://1.com")];
   // Verify initial reception of the mobile site.
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:kMobileSiteLabel]);
+  [ChromeEarlGrey waitForWebStateContainingText:kMobileSiteLabel];
 
   // Request and verify reception of the desktop site.
   [ChromeEarlGreyUI openToolsMenu];
   [RequestDesktopButton() performAction:grey_tap()];
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:kDesktopSiteLabel]);
+  [ChromeEarlGrey waitForWebStateContainingText:kDesktopSiteLabel];
 
   // Verify that going back returns to the mobile site.
   [[EarlGrey selectElementWithMatcher:chrome_test_util::BackButton()]
       performAction:grey_tap()];
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:kMobileSiteLabel]);
+  [ChromeEarlGrey waitForWebStateContainingText:kMobileSiteLabel];
 }
 
 // Tests that requesting mobile site of a page works and the user agent
@@ -183,29 +168,23 @@
       new UserAgentResponseProvider());
   web::test::SetUpHttpServer(std::move(provider));
 
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey loadURL:web::test::HttpServer::MakeUrl("http://1.com")]);
+  [ChromeEarlGrey loadURL:web::test::HttpServer::MakeUrl("http://1.com")];
   // Verify initial reception of the mobile site.
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:kMobileSiteLabel]);
+  [ChromeEarlGrey waitForWebStateContainingText:kMobileSiteLabel];
 
   // Request and verify reception of the desktop site.
   [ChromeEarlGreyUI openToolsMenu];
   [RequestDesktopButton() performAction:grey_tap()];
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:kDesktopSiteLabel]);
+  [ChromeEarlGrey waitForWebStateContainingText:kDesktopSiteLabel];
 
   // Request and verify reception of the mobile site.
   [ChromeEarlGreyUI openToolsMenu];
   [RequestMobileButton() performAction:grey_tap()];
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:kMobileSiteLabel]);
+  [ChromeEarlGrey waitForWebStateContainingText:kMobileSiteLabel];
 
   // Verify that mobile user agent propagates.
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey loadURL:web::test::HttpServer::MakeUrl("http://2.com")]);
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:kMobileSiteLabel]);
+  [ChromeEarlGrey loadURL:web::test::HttpServer::MakeUrl("http://2.com")];
+  [ChromeEarlGrey waitForWebStateContainingText:kMobileSiteLabel];
 }
 
 // Tests that requesting mobile site of a page works and going back re-opens
@@ -215,29 +194,24 @@
       new UserAgentResponseProvider());
   web::test::SetUpHttpServer(std::move(provider));
 
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey loadURL:web::test::HttpServer::MakeUrl("http://1.com")]);
+  [ChromeEarlGrey loadURL:web::test::HttpServer::MakeUrl("http://1.com")];
   // Verify initial reception of the mobile site.
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:kMobileSiteLabel]);
+  [ChromeEarlGrey waitForWebStateContainingText:kMobileSiteLabel];
 
   // Request and verify reception of the desktop site.
   [ChromeEarlGreyUI openToolsMenu];
   [RequestDesktopButton() performAction:grey_tap()];
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:kDesktopSiteLabel]);
+  [ChromeEarlGrey waitForWebStateContainingText:kDesktopSiteLabel];
 
   // Request and verify reception of the mobile site.
   [ChromeEarlGreyUI openToolsMenu];
   [RequestMobileButton() performAction:grey_tap()];
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:kMobileSiteLabel]);
+  [ChromeEarlGrey waitForWebStateContainingText:kMobileSiteLabel];
 
   // Verify that going back returns to the desktop site.
   [[EarlGrey selectElementWithMatcher:chrome_test_util::BackButton()]
       performAction:grey_tap()];
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:kDesktopSiteLabel]);
+  [ChromeEarlGrey waitForWebStateContainingText:kDesktopSiteLabel];
 }
 
 // Tests that requesting desktop site button is not enabled on new tab pages.
@@ -251,7 +225,7 @@
 
 // Tests that requesting desktop site button is not enabled on WebUI pages.
 - (void)testRequestDesktopSiteNotEnabledOnWebUIPage {
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey loadURL:GURL("chrome://version")]);
+  [ChromeEarlGrey loadURL:GURL("chrome://version")];
 
   // Verify tapping on request desktop button is no-op.
   [ChromeEarlGreyUI openToolsMenu];
@@ -264,40 +238,33 @@
 // desktop User Agent.
 - (void)testAppVersionJSAPIWithDesktopUserAgent {
   web::test::SetUpFileBasedHttpServer();
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey
-      loadURL:web::test::HttpServer::MakeUrl(kUserAgentTestURL)]);
+  [ChromeEarlGrey loadURL:web::test::HttpServer::MakeUrl(kUserAgentTestURL)];
   // Verify initial reception of the mobile site.
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:kMobileSiteLabel]);
+  [ChromeEarlGrey waitForWebStateContainingText:kMobileSiteLabel];
 
   // Request and verify reception of the desktop site.
   [ChromeEarlGreyUI openToolsMenu];
   [RequestDesktopButton() performAction:grey_tap()];
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:kDesktopSiteLabel]);
+  [ChromeEarlGrey waitForWebStateContainingText:kDesktopSiteLabel];
 }
 
 // Tests that navigator.appVersion JavaScript API returns correct string for
 // mobile User Agent.
 - (void)testAppVersionJSAPIWithMobileUserAgent {
   web::test::SetUpFileBasedHttpServer();
-  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey
-      loadURL:web::test::HttpServer::MakeUrl(kUserAgentTestURL)]);
+  [ChromeEarlGrey loadURL:web::test::HttpServer::MakeUrl(kUserAgentTestURL)];
   // Verify initial reception of the mobile site.
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:kMobileSiteLabel]);
+  [ChromeEarlGrey waitForWebStateContainingText:kMobileSiteLabel];
 
   // Request and verify reception of the desktop site.
   [ChromeEarlGreyUI openToolsMenu];
   [RequestDesktopButton() performAction:grey_tap()];
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:kDesktopSiteLabel]);
+  [ChromeEarlGrey waitForWebStateContainingText:kDesktopSiteLabel];
 
   // Request and verify reception of the mobile site.
   [ChromeEarlGreyUI openToolsMenu];
   [RequestMobileButton() performAction:grey_tap()];
-  CHROME_EG_ASSERT_NO_ERROR(
-      [ChromeEarlGrey waitForWebStateContainingText:kMobileSiteLabel]);
+  [ChromeEarlGrey waitForWebStateContainingText:kMobileSiteLabel];
 }
 
 @end
diff --git a/ios/chrome/test/earl_grey/OWNERS b/ios/chrome/test/earl_grey/OWNERS
index bd6fe7d6..aad2329 100644
--- a/ios/chrome/test/earl_grey/OWNERS
+++ b/ios/chrome/test/earl_grey/OWNERS
@@ -1,4 +1,8 @@
 eugenebut@chromium.org
 
+# This is for the common case of adding or renaming files. If you're doing
+# structural changes, use usual OWNERS rules.
+per-file BUILD.gn=*
+
 # TEAM: ios-directory-owners@chromium.org
 # OS: iOS
diff --git a/ios/web/public/java_script_dialog_presenter.h b/ios/web/public/java_script_dialog_presenter.h
index f4033a3..23d8134 100644
--- a/ios/web/public/java_script_dialog_presenter.h
+++ b/ios/web/public/java_script_dialog_presenter.h
@@ -17,6 +17,8 @@
 
 class JavaScriptDialogPresenter {
  public:
+  virtual ~JavaScriptDialogPresenter() = default;
+
   // Requests presentation of a JavaScript dialog. Clients must always call
   // |callback| even if they choose not to present the dialog.
   virtual void RunJavaScriptDialog(WebState* web_state,
diff --git a/ios/web/public/test/fakes/test_java_script_dialog_presenter.h b/ios/web/public/test/fakes/test_java_script_dialog_presenter.h
index 82c375b2..f5db3b3 100644
--- a/ios/web/public/test/fakes/test_java_script_dialog_presenter.h
+++ b/ios/web/public/test/fakes/test_java_script_dialog_presenter.h
@@ -29,7 +29,7 @@
 class TestJavaScriptDialogPresenter : public JavaScriptDialogPresenter {
  public:
   TestJavaScriptDialogPresenter();
-  ~TestJavaScriptDialogPresenter();
+  ~TestJavaScriptDialogPresenter() override;
 
   // JavaScriptDialogPresenter overrides:
   void RunJavaScriptDialog(WebState* web_state,
diff --git a/ios/web_view/internal/web_view_java_script_dialog_presenter.h b/ios/web_view/internal/web_view_java_script_dialog_presenter.h
index c57174a4..ddeb802 100644
--- a/ios/web_view/internal/web_view_java_script_dialog_presenter.h
+++ b/ios/web_view/internal/web_view_java_script_dialog_presenter.h
@@ -19,7 +19,7 @@
  public:
   WebViewJavaScriptDialogPresenter(CWVWebView* web_view,
                                    id<CWVUIDelegate> ui_delegate);
-  ~WebViewJavaScriptDialogPresenter();
+  ~WebViewJavaScriptDialogPresenter() override;
 
   void SetUIDelegate(id<CWVUIDelegate> ui_delegate);
 
diff --git a/media/DEPS b/media/DEPS
index 096cc14..208e272 100644
--- a/media/DEPS
+++ b/media/DEPS
@@ -10,7 +10,7 @@
   "+mojo/public/cpp/bindings/callback_helpers.h",
   "+mojo/public/cpp/system/platform_handle.h",
   "+services/device/public",
-  "+services/ws/public/cpp/gpu/context_provider_command_buffer.h",
+  "+services/viz/public/cpp/gpu/context_provider_command_buffer.h",
   "+skia/ext",
   "+third_party/dav1d",
   "+third_party/ffmpeg",
diff --git a/media/gpu/vaapi/vaapi_video_decode_accelerator.cc b/media/gpu/vaapi/vaapi_video_decode_accelerator.cc
index 5a4fdbf..1149d3c8 100644
--- a/media/gpu/vaapi/vaapi_video_decode_accelerator.cc
+++ b/media/gpu/vaapi/vaapi_video_decode_accelerator.cc
@@ -145,6 +145,7 @@
       vaapi_picture_factory_(new VaapiPictureFactory()),
       buffer_allocation_mode_(BufferAllocationMode::kNormal),
       surfaces_available_(&lock_),
+      va_surface_format_(VA_INVALID_ID),
       task_runner_(base::ThreadTaskRunnerHandle::Get()),
       decoder_thread_("VaapiDecoderThread"),
       finish_flush_pending_(false),
@@ -631,7 +632,7 @@
       << ", requested " << requested_num_pics_ << ")", INVALID_ARGUMENT, );
   DCHECK(requested_pic_size_ == buffers[0].size());
 
-  const unsigned int va_format = GetVaFormatForVideoCodecProfile(profile_);
+  va_surface_format_ = GetVaFormatForVideoCodecProfile(profile_);
   std::vector<VASurfaceID> va_surface_ids;
 
   // If we aren't in BufferAllocationMode::kNone, we have to allocate a
@@ -684,7 +685,7 @@
   if (buffer_allocation_mode_ == BufferAllocationMode::kNone) {
     DCHECK(!va_surface_ids.empty());
     RETURN_AND_NOTIFY_ON_FAILURE(
-        vaapi_wrapper_->CreateContext(va_format, requested_pic_size_),
+        vaapi_wrapper_->CreateContext(va_surface_format_, requested_pic_size_),
         "Failed creating VA Context", PLATFORM_FAILURE, );
     DCHECK_EQ(va_surface_ids.size(), buffers.size());
   } else {
@@ -695,7 +696,7 @@
     CHECK_NE(requested_num_surfaces, 0u);
     va_surface_ids.clear();
     RETURN_AND_NOTIFY_ON_FAILURE(vaapi_wrapper_->CreateContextAndSurfaces(
-                                     va_format, requested_pic_size_,
+                                     va_surface_format_, requested_pic_size_,
                                      requested_num_surfaces, &va_surface_ids),
                                  "Failed creating VA Surfaces",
                                  PLATFORM_FAILURE, );
@@ -1011,6 +1012,7 @@
   if (available_va_surfaces_.empty())
     return nullptr;
 
+  DCHECK_NE(VA_INVALID_ID, va_surface_format_);
   DCHECK(!awaiting_va_surfaces_recycle_);
   if (buffer_allocation_mode_ != BufferAllocationMode::kNone) {
     const VASurfaceID id = available_va_surfaces_.front();
@@ -1023,8 +1025,7 @@
                           available_va_surfaces_.size(),
                       "available", available_va_surfaces_.size());
 
-    return new VASurface(id, requested_pic_size_,
-                         vaapi_wrapper_->va_surface_format(),
+    return new VASurface(id, requested_pic_size_, va_surface_format_,
                          base::BindOnce(va_surface_release_cb_));
   }
 
@@ -1039,7 +1040,7 @@
         // to return a new VASurface.
         base::Erase(available_va_surfaces_, va_surface_id);
         return new VASurface(va_surface_id, requested_pic_size_,
-                             vaapi_wrapper_->va_surface_format(),
+                             va_surface_format_,
                              base::BindOnce(va_surface_release_cb_));
       }
     }
@@ -1132,12 +1133,11 @@
 
   constexpr float kNumBytesPerPixelYUV420 = 12.0 / 8;
   constexpr float kNumBytesPerPixelYUV420_10bpp = 2 * kNumBytesPerPixelYUV420;
-  unsigned int va_surface_format = GetVaFormatForVideoCodecProfile(profile_);
-  DCHECK(va_surface_format == VA_RT_FORMAT_YUV420 ||
-         va_surface_format == VA_RT_FORMAT_YUV420_10BPP);
+  DCHECK(va_surface_format_ == VA_RT_FORMAT_YUV420 ||
+         va_surface_format_ == VA_RT_FORMAT_YUV420_10BPP);
   const float va_surface_bytes_per_pixel =
-      va_surface_format == VA_RT_FORMAT_YUV420 ? kNumBytesPerPixelYUV420
-                                               : kNumBytesPerPixelYUV420_10bpp;
+      va_surface_format_ == VA_RT_FORMAT_YUV420 ? kNumBytesPerPixelYUV420
+                                                : kNumBytesPerPixelYUV420_10bpp;
   // Report |requested_num_surfaces| and the associated memory size. The
   // calculated size is an estimation since we don't know the internal VA
   // strides, texture compression, headers, etc, but is a good lower boundary.
diff --git a/media/gpu/vaapi/vaapi_video_decode_accelerator.h b/media/gpu/vaapi/vaapi_video_decode_accelerator.h
index 2d7fb0b0..3ba6e1ef 100644
--- a/media/gpu/vaapi/vaapi_video_decode_accelerator.h
+++ b/media/gpu/vaapi/vaapi_video_decode_accelerator.h
@@ -269,6 +269,8 @@
   std::list<VASurfaceID> available_va_surfaces_ GUARDED_BY(lock_);
   // Signalled when output surfaces are queued into |available_va_surfaces_|.
   base::ConditionVariable surfaces_available_;
+  // VASurfaceIDs format, filled in when created.
+  unsigned int va_surface_format_;
 
   // Pending output requests from the decoder. When it indicates that we should
   // output a surface and we have an available Picture (i.e. texture) ready
diff --git a/media/gpu/vaapi/vaapi_video_encode_accelerator.cc b/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
index 48a703d..0fca64e 100644
--- a/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
+++ b/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
@@ -384,7 +384,7 @@
       (native_input_mode_ ? 0 : kNumSurfacesPerInputVideoFrame);
 
   if (!vaapi_wrapper_->CreateContextAndSurfaces(
-          VA_RT_FORMAT_YUV420, coded_size_,
+          kVaSurfaceFormat, coded_size_,
           (num_frames_in_flight + 1) * va_surfaces_per_video_frame_,
           &available_va_surface_ids_)) {
     NOTIFY_ERROR(kPlatformFailureError, "Failed creating VASurfaces");
@@ -581,14 +581,13 @@
   }
 
   scoped_refptr<VASurface> input_surface = new VASurface(
-      va_input_surface_id, coded_size_, vaapi_wrapper_->va_surface_format(),
+      va_input_surface_id, coded_size_, kVaSurfaceFormat,
       native_input_mode_ ? base::DoNothing()
                          : base::BindOnce(va_surface_release_cb_));
 
   scoped_refptr<VASurface> reconstructed_surface =
       new VASurface(available_va_surface_ids_.back(), coded_size_,
-                    vaapi_wrapper_->va_surface_format(),
-                    base::BindOnce(va_surface_release_cb_));
+                    kVaSurfaceFormat, base::BindOnce(va_surface_release_cb_));
   available_va_surface_ids_.pop_back();
 
   auto job = base::MakeRefCounted<VaapiEncodeJob>(
diff --git a/media/gpu/vaapi/vaapi_video_encode_accelerator.h b/media/gpu/vaapi/vaapi_video_encode_accelerator.h
index 9aef0e0..8ff9ba8 100644
--- a/media/gpu/vaapi/vaapi_video_encode_accelerator.h
+++ b/media/gpu/vaapi/vaapi_video_encode_accelerator.h
@@ -181,6 +181,9 @@
   // VA surfaces available for reuse.
   std::vector<VASurfaceID> available_va_surface_ids_;
 
+  // VASurfaceIDs internal format.
+  static constexpr unsigned int kVaSurfaceFormat = VA_RT_FORMAT_YUV420;
+
   // VA buffers for coded frames.
   std::vector<VABufferID> available_va_buffer_ids_;
 
diff --git a/media/gpu/vaapi/vaapi_wrapper.cc b/media/gpu/vaapi/vaapi_wrapper.cc
index e1d29a9..e9cac02 100644
--- a/media/gpu/vaapi/vaapi_wrapper.cc
+++ b/media/gpu/vaapi/vaapi_wrapper.cc
@@ -1216,8 +1216,9 @@
   DVLOG(2) << "Creating " << num_surfaces << " surfaces";
   DCHECK(va_surfaces->empty());
 
-  if (va_surface_format_ != 0u) {
-    LOG(ERROR) << "Surfaces should be destroyed before creating new surfaces";
+  if (va_context_id_ != VA_INVALID_ID) {
+    LOG(ERROR)
+        << "The current context should be destroyed before creating a new one";
     return false;
   }
 
@@ -1250,10 +1251,7 @@
       vaCreateContext(va_display_, va_config_id_, size.width(), size.height(),
                       VA_PROGRESSIVE, empty_va_surfaces_ids_pointer,
                       empty_va_surfaces_ids_size, &va_context_id_);
-
   VA_LOG_ON_ERROR(va_res, "vaCreateContext failed");
-  if (va_res == VA_STATUS_SUCCESS)
-    va_surface_format_ = va_format;
   return va_res == VA_STATUS_SUCCESS;
 }
 
@@ -1711,7 +1709,6 @@
 
 VaapiWrapper::VaapiWrapper()
     : va_lock_(VADisplayState::Get()->va_lock()),
-      va_surface_format_(0),
       va_display_(NULL),
       va_config_id_(VA_INVALID_ID),
       va_context_id_(VA_INVALID_ID) {}
@@ -1808,7 +1805,6 @@
   }
 
   va_context_id_ = VA_INVALID_ID;
-  va_surface_format_ = 0;
 }
 
 bool VaapiWrapper::CreateSurfaces(unsigned int va_format,
diff --git a/media/gpu/vaapi/vaapi_wrapper.h b/media/gpu/vaapi/vaapi_wrapper.h
index 0f27d76..0c03759c 100644
--- a/media/gpu/vaapi/vaapi_wrapper.h
+++ b/media/gpu/vaapi/vaapi_wrapper.h
@@ -158,8 +158,7 @@
   // The client is responsible for releasing it via DestroyContext() or
   // DestroyContextAndSurfaces(), or it will be released on dtor.
   virtual bool CreateContext(unsigned int va_format, const gfx::Size& size);
-  // Destroys the context identified by |va_context_id_| and clears the local
-  // associated |va_surface_format_|.
+  // Destroys the context identified by |va_context_id_|.
   void DestroyContext();
 
   // Creates a self-releasing VASurface from |pixmap|. The ownership of the
@@ -266,9 +265,6 @@
   // Initialize static data before sandbox is enabled.
   static void PreSandboxInitialization();
 
-  // Get the created surfaces format. TODO(crbug.com/971891): remove.
-  unsigned int va_surface_format() const { return va_surface_format_; }
-
  protected:
   VaapiWrapper();
   virtual ~VaapiWrapper();
@@ -309,9 +305,6 @@
   // the lifetime of VaapiWrapper.
   base::Lock* va_lock_;
 
-  // VA format of allocated surfaces. TODO(crbug.com/971891): remove.
-  unsigned int va_surface_format_;
-
   // VA handles.
   // All valid after successful Initialize() and until Deinitialize().
   VADisplay va_display_ GUARDED_BY(va_lock_);
diff --git a/media/video/BUILD.gn b/media/video/BUILD.gn
index 10172be..2a971e9b 100644
--- a/media/video/BUILD.gn
+++ b/media/video/BUILD.gn
@@ -91,7 +91,7 @@
     "//gpu/command_buffer/client:gles2_interface",
     "//gpu/command_buffer/common",
     "//media/base:test_support",
-    "//services/ws/public/cpp/gpu",
+    "//services/viz/public/cpp/gpu",
     "//testing/gmock",
     "//ui/gfx",
   ]
diff --git a/media/video/gpu_video_accelerator_factories.h b/media/video/gpu_video_accelerator_factories.h
index cc399fc..2f3da2c 100644
--- a/media/video/gpu_video_accelerator_factories.h
+++ b/media/video/gpu_video_accelerator_factories.h
@@ -41,9 +41,9 @@
 struct SyncToken;
 }
 
-namespace ws {
+namespace viz {
 class ContextProviderCommandBuffer;
-}  // namespace ws
+}  // namespace viz
 
 namespace media {
 
@@ -158,7 +158,7 @@
   virtual VideoEncodeAccelerator::SupportedProfiles
   GetVideoEncodeAcceleratorSupportedProfiles() = 0;
 
-  virtual scoped_refptr<ws::ContextProviderCommandBuffer>
+  virtual scoped_refptr<viz::ContextProviderCommandBuffer>
   GetMediaContextProvider() = 0;
   virtual gpu::ContextSupport* GetMediaContextProviderContextSupport() = 0;
 
diff --git a/media/video/mock_gpu_video_accelerator_factories.h b/media/video/mock_gpu_video_accelerator_factories.h
index e42d7da0..bd44869b 100644
--- a/media/video/mock_gpu_video_accelerator_factories.h
+++ b/media/video/mock_gpu_video_accelerator_factories.h
@@ -16,7 +16,7 @@
 #include "base/single_thread_task_runner.h"
 #include "media/video/gpu_video_accelerator_factories.h"
 #include "media/video/video_encode_accelerator.h"
-#include "services/ws/public/cpp/gpu/context_provider_command_buffer.h"
+#include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace base {
@@ -64,7 +64,7 @@
   MOCK_METHOD0(GetVideoEncodeAcceleratorSupportedProfiles,
                VideoEncodeAccelerator::SupportedProfiles());
   MOCK_METHOD0(GetMediaContextProvider,
-               scoped_refptr<ws::ContextProviderCommandBuffer>());
+               scoped_refptr<viz::ContextProviderCommandBuffer>());
   MOCK_METHOD0(GetMediaContextProviderContextSupport, gpu::ContextSupport*());
   MOCK_METHOD1(SetRenderingColorSpace, void(const gfx::ColorSpace&));
 
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module-forward.h.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module-forward.h.tmpl
index 007e89e..5df13c5 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/module-forward.h.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/module-forward.h.tmpl
@@ -190,17 +190,21 @@
 {#--- Interface Forward Declarations -#}
 {%  for interface in interfaces %}
 class {{interface.name}};
+{{ kythe_annotation("%s.%s"|format(module_prefix, interface.name)) }}
 using {{interface.name}}Ptr = mojo::InterfacePtr<{{interface.name}}>;
 using {{interface.name}}PtrInfo = mojo::InterfacePtrInfo<{{interface.name}}>;
 using ThreadSafe{{interface.name}}Ptr =
     mojo::ThreadSafeInterfacePtr<{{interface.name}}>;
+{{ kythe_annotation("%s.%s"|format(module_prefix, interface.name)) }}
 using {{interface.name}}Request = mojo::InterfaceRequest<{{interface.name}}>;
+{{ kythe_annotation("%s.%s"|format(module_prefix, interface.name)) }}
 using {{interface.name}}AssociatedPtr =
     mojo::AssociatedInterfacePtr<{{interface.name}}>;
 using ThreadSafe{{interface.name}}AssociatedPtr =
     mojo::ThreadSafeAssociatedInterfacePtr<{{interface.name}}>;
 using {{interface.name}}AssociatedPtrInfo =
     mojo::AssociatedInterfacePtrInfo<{{interface.name}}>;
+{{ kythe_annotation("%s.%s"|format(module_prefix, interface.name)) }}
 using {{interface.name}}AssociatedRequest =
     mojo::AssociatedInterfaceRequest<{{interface.name}}>;
 {%  endfor %}
diff --git a/net/base/network_isolation_key.cc b/net/base/network_isolation_key.cc
index 183c87f..009c93c 100644
--- a/net/base/network_isolation_key.cc
+++ b/net/base/network_isolation_key.cc
@@ -45,4 +45,8 @@
   return top_frame_origin_->opaque();
 }
 
+bool NetworkIsolationKey::IsEmpty() const {
+  return !top_frame_origin_.has_value();
+}
+
 }  // namespace net
diff --git a/net/base/network_isolation_key.h b/net/base/network_isolation_key.h
index 68f3784..46e28c7c 100644
--- a/net/base/network_isolation_key.h
+++ b/net/base/network_isolation_key.h
@@ -18,7 +18,8 @@
 // the context on which they were made.
 class NET_EXPORT NetworkIsolationKey {
  public:
-  NetworkIsolationKey(const base::Optional<url::Origin>& top_frame_origin);
+  explicit NetworkIsolationKey(
+      const base::Optional<url::Origin>& top_frame_origin);
 
   // Construct an empty key.
   NetworkIsolationKey();
@@ -60,6 +61,13 @@
   // to persist state to disk related to it (e.g., disk cache).
   bool IsTransient() const;
 
+  // APIs for serialization to and from the mojo structure.
+  const base::Optional<url::Origin>& GetTopFrameOrigin() const {
+    return top_frame_origin_;
+  }
+  // Returns true if all parts of the key are empty.
+  bool IsEmpty() const;
+
  private:
   // The origin of the top frame of the request (if applicable).
   base::Optional<url::Origin> top_frame_origin_;
diff --git a/net/cert_net/cert_net_fetcher_impl.cc b/net/cert_net/cert_net_fetcher_impl.cc
index 11a1166..349c656 100644
--- a/net/cert_net/cert_net_fetcher_impl.cc
+++ b/net/cert_net/cert_net_fetcher_impl.cc
@@ -135,21 +135,19 @@
   void Fetch(std::unique_ptr<RequestParams> request_params,
              scoped_refptr<RequestCore> request);
 
+  // Removes |job| from the in progress jobs and transfers ownership to the
+  // caller.
+  std::unique_ptr<Job> RemoveJob(Job* job);
+
   // Cancels outstanding jobs, which stops network requests and signals the
   // corresponding RequestCores that the requests have completed.
   void Shutdown();
 
  private:
-  friend class Job;
-
   // Finds a job with a matching RequestPararms or returns nullptr if there was
   // no match.
   Job* FindJob(const RequestParams& params);
 
-  // Removes |job| from the in progress jobs and transfers ownership to the
-  // caller.
-  std::unique_ptr<Job> RemoveJob(Job* job);
-
   // The in-progress jobs. This set does not contain the job which is actively
   // invoking callbacks (OnJobCompleted).
   JobSet jobs_;
diff --git a/net/http/http_auth.cc b/net/http/http_auth.cc
index 338aabb..0e6b88c 100644
--- a/net/http/http_auth.cc
+++ b/net/http/http_auth.cc
@@ -23,6 +23,12 @@
 
 namespace net {
 
+namespace {
+const char* const kSchemeNames[] = {kBasicAuthScheme,     kDigestAuthScheme,
+                                    kNtlmAuthScheme,      kNegotiateAuthScheme,
+                                    kSpdyProxyAuthScheme, kMockAuthScheme};
+}  // namespace
+
 HttpAuth::Identity::Identity() : source(IDENT_SRC_NONE), invalid(true) {}
 
 // static
@@ -137,9 +143,6 @@
 
 // static
 const char* HttpAuth::SchemeToString(Scheme scheme) {
-  static const char* const kSchemeNames[] = {
-      kBasicAuthScheme,     kDigestAuthScheme,    kNtlmAuthScheme,
-      kNegotiateAuthScheme, kSpdyProxyAuthScheme, kMockAuthScheme};
   static_assert(base::size(kSchemeNames) == AUTH_SCHEME_MAX,
                 "http auth scheme names incorrect size");
   if (scheme < AUTH_SCHEME_BASIC || scheme >= AUTH_SCHEME_MAX) {
@@ -150,6 +153,16 @@
 }
 
 // static
+HttpAuth::Scheme HttpAuth::StringToScheme(const std::string& str) {
+  for (uint8_t i = 0; i < base::size(kSchemeNames); i++) {
+    if (str == kSchemeNames[i])
+      return static_cast<Scheme>(i);
+  }
+  NOTREACHED();
+  return AUTH_SCHEME_MAX;
+}
+
+// static
 const char* HttpAuth::AuthorizationResultToString(
     AuthorizationResult authorization_result) {
   switch (authorization_result) {
diff --git a/net/http/http_auth.h b/net/http/http_auth.h
index 90841cd..fe64502 100644
--- a/net/http/http_auth.h
+++ b/net/http/http_auth.h
@@ -141,6 +141,10 @@
   // Returns a string representation of an authentication Scheme.
   static const char* SchemeToString(Scheme scheme);
 
+  // Returns an authentication Scheme from a string which was produced by
+  // SchemeToString().
+  static Scheme StringToScheme(const std::string& str);
+
   // Returns a string representation of an authorization result.
   static const char* AuthorizationResultToString(
       AuthorizationResult authorization_result);
diff --git a/net/quic/platform/impl/quic_socket_address_impl.cc b/net/quic/platform/impl/quic_socket_address_impl.cc
index b511ab3..9b7a6708 100644
--- a/net/quic/platform/impl/quic_socket_address_impl.cc
+++ b/net/quic/platform/impl/quic_socket_address_impl.cc
@@ -31,11 +31,12 @@
   }
 }
 
-QuicSocketAddressImpl::QuicSocketAddressImpl(const struct sockaddr& saddr) {
-  if (saddr.sa_family == AF_INET) {
-    CHECK(socket_address_.FromSockAddr(&saddr, sizeof(struct sockaddr_in)));
-  } else if (saddr.sa_family == AF_INET6) {
-    CHECK(socket_address_.FromSockAddr(&saddr, sizeof(struct sockaddr_in6)));
+QuicSocketAddressImpl::QuicSocketAddressImpl(const sockaddr* saddr,
+                                             socklen_t len) {
+  if (saddr->sa_family == AF_INET) {
+    CHECK(socket_address_.FromSockAddr(saddr, len));
+  } else if (saddr->sa_family == AF_INET6) {
+    CHECK(socket_address_.FromSockAddr(saddr, len));
   }
 }
 
diff --git a/net/quic/platform/impl/quic_socket_address_impl.h b/net/quic/platform/impl/quic_socket_address_impl.h
index 056501c..e6006ba 100644
--- a/net/quic/platform/impl/quic_socket_address_impl.h
+++ b/net/quic/platform/impl/quic_socket_address_impl.h
@@ -17,7 +17,7 @@
   explicit QuicSocketAddressImpl(const net::IPEndPoint& addr);
   QuicSocketAddressImpl(QuicIpAddress address, uint16_t port);
   explicit QuicSocketAddressImpl(const struct sockaddr_storage& saddr);
-  explicit QuicSocketAddressImpl(const struct sockaddr& saddr);
+  explicit QuicSocketAddressImpl(const sockaddr* saddr, socklen_t len);
   QuicSocketAddressImpl(const QuicSocketAddressImpl& other) = default;
   QuicSocketAddressImpl& operator=(const QuicSocketAddressImpl& other) =
       default;
diff --git a/net/spdy/buffered_spdy_framer.h b/net/spdy/buffered_spdy_framer.h
index ca497b9..1d0851a 100644
--- a/net/spdy/buffered_spdy_framer.h
+++ b/net/spdy/buffered_spdy_framer.h
@@ -240,7 +240,7 @@
   http2::Http2DecoderAdapter deframer_;
   BufferedSpdyFramerVisitorInterface* visitor_;
 
-  int frames_received_;
+  int frames_received_ = 0;
 
   // Collection of fields from control frames that we need to
   // buffer up from the spdy framer.
@@ -248,16 +248,16 @@
     ControlFrameFields();
 
     spdy::SpdyFrameType type;
-    spdy::SpdyStreamId stream_id;
-    spdy::SpdyStreamId associated_stream_id;
-    spdy::SpdyStreamId promised_stream_id;
-    bool has_priority;
-    spdy::SpdyPriority priority;
-    int weight;
-    spdy::SpdyStreamId parent_stream_id;
-    bool exclusive;
-    bool fin;
-    bool unidirectional;
+    spdy::SpdyStreamId stream_id = 0U;
+    spdy::SpdyStreamId associated_stream_id = 0U;
+    spdy::SpdyStreamId promised_stream_id = 0U;
+    bool has_priority = false;
+    spdy::SpdyPriority priority = 0U;
+    int weight = 0;
+    spdy::SpdyStreamId parent_stream_id = 0U;
+    bool exclusive = false;
+    bool fin = false;
+    bool unidirectional = false;
     base::TimeTicks recv_first_byte_time;
   };
   std::unique_ptr<ControlFrameFields> control_frame_fields_;
diff --git a/net/url_request/url_request.cc b/net/url_request/url_request.cc
index d11742c..f5711c7 100644
--- a/net/url_request/url_request.cc
+++ b/net/url_request/url_request.cc
@@ -862,6 +862,10 @@
   if (status.status() != URLRequestStatus::SUCCESS)
     set_status(status);
 
+  // |status_| should not be ERR_IO_PENDING when calling into the
+  // URLRequest::Delegate().
+  DCHECK(!status_.is_io_pending());
+
   int net_error = OK;
   if (!status_.is_success())
     net_error = status_.error();
diff --git a/net/url_request/url_request_http_job.cc b/net/url_request/url_request_http_job.cc
index 94883ec..8d6d3f1 100644
--- a/net/url_request/url_request_http_job.cc
+++ b/net/url_request/url_request_http_job.cc
@@ -323,10 +323,13 @@
   request_info_.url = request_->url();
   request_info_.method = request_->method();
 
+  request_info_.network_isolation_key = request_->network_isolation_key();
   // TODO(crbug.com/963476): Remove this when network_isolation_key is being set
-  // in request_.
-  request_info_.network_isolation_key =
-      NetworkIsolationKey(request_->top_frame_origin());
+  // in request_ for most cases.
+  if (!request_info_.network_isolation_key.IsFullyPopulated()) {
+    request_info_.network_isolation_key =
+        NetworkIsolationKey(request_->top_frame_origin());
+  }
 
   request_info_.load_flags = request_->load_flags();
   request_info_.traffic_annotation =
@@ -1233,7 +1236,6 @@
 }
 
 void URLRequestHttpJob::CancelAuth() {
-  // Proxy gets set first, then WWW.
   if (proxy_auth_state_ == AUTH_STATE_NEED_AUTH) {
     proxy_auth_state_ = AUTH_STATE_CANCELED;
   } else {
@@ -1241,26 +1243,18 @@
     server_auth_state_ = AUTH_STATE_CANCELED;
   }
 
-  // These will be reset in OnStartCompleted.
-  response_info_ = nullptr;
-  receive_headers_end_ = base::TimeTicks::Now();
-  // TODO(davidben,mmenke): We should either reset override_response_headers_
-  // here or not call NotifyHeadersReceived a second time on the same response
-  // headers. See https://crbug.com/810063.
+  // The above lines should ensure this is the case.
+  DCHECK(!NeedsAuth());
 
-  ResetTimer();
-
-  // OK, let the consumer read the error page...
+  // Let the consumer read the HTTP error page. NeedsAuth() should now return
+  // false, so NotifyHeadersComplete() should not request auth from the client
+  // again.
   //
-  // Because we set the AUTH_STATE_CANCELED flag, NeedsAuth will return false,
-  // which will cause the consumer to receive OnResponseStarted instead of
-  // OnAuthRequired.
-  //
-  // We have to do this via InvokeLater to avoid "recursing" the consumer.
-  //
+  // Have to do this via PostTask to avoid re-entrantly calling into the
+  // consumer.
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(&URLRequestHttpJob::OnStartCompleted,
-                                weak_factory_.GetWeakPtr(), OK));
+      FROM_HERE, base::BindOnce(&URLRequestHttpJob::NotifyFinalHeadersReceived,
+                                weak_factory_.GetWeakPtr()));
 }
 
 void URLRequestHttpJob::ContinueWithCertificate(
diff --git a/net/url_request/url_request_job.cc b/net/url_request/url_request_job.cc
index 3a7efea..18d56f1e 100644
--- a/net/url_request/url_request_job.cc
+++ b/net/url_request/url_request_job.cc
@@ -433,6 +433,22 @@
     }
   }
 
+  NotifyFinalHeadersReceived();
+  // |this| may be destroyed at this point.
+}
+
+void URLRequestJob::NotifyFinalHeadersReceived() {
+  DCHECK(!NeedsAuth() || !GetAuthChallengeInfo());
+
+  if (has_handled_response_)
+    return;
+
+  // While the request's status is normally updated in NotifyHeadersComplete(),
+  // URLRequestHttpJob::CancelAuth() posts a task to invoke this method
+  // directly, which bypasses that logic.
+  if (request_->status().is_io_pending())
+    request_->set_status(URLRequestStatus());
+
   has_handled_response_ = true;
   if (request_->status().is_success()) {
     DCHECK(!source_stream_);
@@ -463,7 +479,6 @@
   }
 
   request_->NotifyResponseStarted(URLRequestStatus());
-
   // |this| may be destroyed at this point.
 }
 
diff --git a/net/url_request/url_request_job.h b/net/url_request/url_request_job.h
index 5e8e307..df3eda2 100644
--- a/net/url_request/url_request_job.h
+++ b/net/url_request/url_request_job.h
@@ -276,6 +276,10 @@
   // Notifies the job that headers have been received.
   void NotifyHeadersComplete();
 
+  // Called when the final set headers have been received (no more redirects to
+  // follow, and no more auth challenges that will be responded to).
+  void NotifyFinalHeadersReceived();
+
   // Notifies the request that a start error has occurred.
   void NotifyStartError(const URLRequestStatus& status);
 
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index ecea7cb..3b23b56 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -43,6 +43,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/bind_test_util.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -290,6 +291,19 @@
 }
 #endif
 
+CookieList GetAllCookies(URLRequestContext* request_context) {
+  CookieList cookie_list;
+  base::RunLoop run_loop;
+  request_context->cookie_store()->GetAllCookiesAsync(
+      base::BindLambdaForTesting([&](const CookieList& cookies,
+                                     const CookieStatusList& excluded_list) {
+        cookie_list = cookies;
+        run_loop.Quit();
+      }));
+  run_loop.Run();
+  return cookie_list;
+}
+
 void TestLoadTimingCacheHitNoNetwork(
     const LoadTimingInfo& load_timing_info) {
   EXPECT_FALSE(load_timing_info.socket_reused);
@@ -8539,6 +8553,51 @@
   }
 }
 
+TEST_F(URLRequestTestHTTP, BasicAuthWithCookiesCancelAuth) {
+  ASSERT_TRUE(http_test_server()->Start());
+
+  GURL url_requiring_auth =
+      http_test_server()->GetURL("/auth-basic?set-cookie-if-challenged");
+
+  // Request a page that will give a 401 containing a Set-Cookie header.
+  // Verify that cookies are set before credentials are provided, and then
+  // cancelling auth does not result in setting the cookies again.
+  TestNetworkDelegate network_delegate;  // Must outlive URLRequest.
+  TestURLRequestContext context(true);
+  context.set_network_delegate(&network_delegate);
+  context.Init();
+
+  TestDelegate d;
+
+  EXPECT_TRUE(GetAllCookies(&context).empty());
+
+  std::unique_ptr<URLRequest> r(context.CreateRequest(
+      url_requiring_auth, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
+  r->set_site_for_cookies(url_requiring_auth);
+  r->Start();
+  d.RunUntilAuthRequired();
+
+  // Cookie should have been set.
+  EXPECT_EQ(1, network_delegate.set_cookie_count());
+  CookieList cookies = GetAllCookies(&context);
+  ASSERT_EQ(1u, cookies.size());
+  EXPECT_EQ("got_challenged", cookies[0].Name());
+  EXPECT_EQ("true", cookies[0].Value());
+
+  // Delete cookie.
+  context.cookie_store()->DeleteAllAsync(CookieStore::DeleteCallback());
+
+  // Cancel auth and continue the request.
+  r->CancelAuth();
+  d.RunUntilComplete();
+  ASSERT_TRUE(r->response_headers());
+  EXPECT_EQ(401, r->response_headers()->response_code());
+
+  // Cookie should not have been set again.
+  EXPECT_TRUE(GetAllCookies(&context).empty());
+  EXPECT_EQ(1, network_delegate.set_cookie_count());
+}
+
 TEST_F(URLRequestTest, CatchFilteredCookies) {
   HttpTestServer test_server;
   ASSERT_TRUE(test_server.Start());
diff --git a/services/BUILD.gn b/services/BUILD.gn
index 1c26629..3cb6a04 100644
--- a/services/BUILD.gn
+++ b/services/BUILD.gn
@@ -46,15 +46,13 @@
       "//services/viz",
       "//services/viz/privileged/interfaces:unit_tests",
       "//services/viz/public/cpp/compositing:tests",
+      "//services/viz/public/cpp/gpu:tests",
       "//services/viz/public/cpp/hit_test:tests",
     ]
   }
 
   if (use_aura) {
-    deps += [
-      "//services/ws/input_devices:tests",
-      "//services/ws/public/cpp/tests",
-    ]
+    deps += [ "//services/ws/input_devices:tests" ]
   }
 
   if (is_android) {
diff --git a/services/content/public/cpp/navigable_contents_view.cc b/services/content/public/cpp/navigable_contents_view.cc
index b2a150a9..3c2036f 100644
--- a/services/content/public/cpp/navigable_contents_view.cc
+++ b/services/content/public/cpp/navigable_contents_view.cc
@@ -22,6 +22,8 @@
 #endif  // defined(TOOLKIT_VIEWS)
 
 #if defined(USE_AURA)
+#include "ui/aura/client/focus_change_observer.h"  // nogncheck
+#include "ui/aura/client/focus_client.h"           // nogncheck
 #include "ui/aura/layout_manager.h"  // nogncheck
 #include "ui/aura/window.h"          // nogncheck
 #endif
@@ -75,14 +77,25 @@
 
 // Owns an Aura window which parents another Aura window in the same process,
 // corresponding to a web contents view hosted in the process.
-class LocalViewHost : public views::NativeViewHost {
+class LocalViewHost : public views::NativeViewHost,
+                      public aura::WindowObserver,
+                      public aura::client::FocusChangeObserver {
  public:
   LocalViewHost(aura::Window* window, NavigableContents* contents)
       : window_(window), contents_(contents) {
     window_->SetLayoutManager(new LocalWindowLayoutManager(window_));
+    window_->AddObserver(this);
+
+    // We set focus behavior to |ALWAYS| to ensure that we receive window focus
+    // change events when our hosted WebContents takes focus. We utilize this
+    // change event to keep LocalViewHost and WebContents focus state in sync.
+    SetFocusBehavior(FocusBehavior::ALWAYS);
   }
 
-  ~LocalViewHost() override = default;
+  ~LocalViewHost() override {
+    if (!window_destroyed_)
+      window_->RemoveObserver(this);
+  }
 
   // views::View:
   void AddedToWidget() override {
@@ -103,10 +116,54 @@
     }
   }
 
+  // aura::WindowObserver:
+  void OnWindowAddedToRootWindow(aura::Window* window) override {
+    // Once our |window_| has been added to its root window, we can obtain a
+    // reference to the root window's focus client which we observe to detect
+    // window focus changed events.
+    auto* focus_client = aura::client::GetFocusClient(window_);
+    if (focus_client)
+      focus_client->AddObserver(this);
+  }
+
+  void OnWindowRemovingFromRootWindow(aura::Window* window,
+                                      aura::Window* root_window) override {
+    // We need to stop observing the root window's focus client before our
+    // |window_| is removed. Otherwise, we will leak a pointer to LocalViewHost
+    // to the focus client that will persist even after LocalViewHost is
+    // destroyed. This will cause a crash when the focus client attempts to
+    // notify our destroyed instance of focus changed events.
+    auto* focus_client = aura::client::GetFocusClient(window_);
+    if (focus_client)
+      focus_client->RemoveObserver(this);
+  }
+
+  void OnWindowDestroying(aura::Window* window) override {
+    window_->RemoveObserver(this);
+    window_destroyed_ = true;
+  }
+
+  // aura::client::FocusChangeObserver:
+  void OnWindowFocused(aura::Window* gained_focus,
+                       aura::Window* lost_focus) override {
+    // We need to ensure that LocalViewHost's focus state is synced with that of
+    // |window_|. This only needs to be done when gaining focus and when
+    // LocalViewHost doesn't already have focus.
+    if (!gained_focus || HasFocus())
+      return;
+
+    // When the window gaining focus is contained within |window_|, we need to
+    // request focus on LocalHostView to remain in sync.
+    if (window_->Contains(gained_focus))
+      RequestFocus();
+  }
+
  private:
   aura::Window* const window_;
   NavigableContents* const contents_;
 
+  bool window_destroyed_ = false;
+
   DISALLOW_COPY_AND_ASSIGN(LocalViewHost);
 };
 
diff --git a/services/network/network_context.cc b/services/network/network_context.cc
index 28ca367..86f9a2a9 100644
--- a/services/network/network_context.cc
+++ b/services/network/network_context.cc
@@ -99,6 +99,10 @@
 #include "services/network/expect_ct_reporter.h"
 #endif  // BUILDFLAG(IS_CT_SUPPORTED)
 
+#if !BUILDFLAG(DISABLE_FTP_SUPPORT)
+#include "net/ftp/ftp_auth_cache.h"
+#endif  // !BUILDFLAG(DISABLE_FTP_SUPPORT)
+
 #if defined(OS_CHROMEOS)
 #include "crypto/nss_util_internal.h"
 #include "net/cert/caching_cert_verifier.h"
@@ -1657,6 +1661,28 @@
   std::move(callback).Run();
 }
 
+void NetworkContext::AddAuthCacheEntry(const net::AuthChallengeInfo& challenge,
+                                       const net::AuthCredentials& credentials,
+                                       AddAuthCacheEntryCallback callback) {
+  if (challenge.challenger.scheme() == url::kFtpScheme) {
+#if !BUILDFLAG(DISABLE_FTP_SUPPORT)
+    net::FtpAuthCache* auth_cache = url_request_context_->ftp_auth_cache();
+    auth_cache->Add(challenge.challenger.GetURL(), credentials);
+#else
+    NOTREACHED();
+#endif  // BUILDFLAG(DISABLE_FTP_SUPPORT)
+  } else {
+    net::HttpAuthCache* http_auth_cache =
+        url_request_context_->http_transaction_factory()
+            ->GetSession()
+            ->http_auth_cache();
+    http_auth_cache->Add(challenge.challenger.GetURL(), challenge.realm,
+                         net::HttpAuth::StringToScheme(challenge.scheme),
+                         challenge.challenge, credentials, challenge.path);
+  }
+  std::move(callback).Run();
+}
+
 void NetworkContext::LookupBasicAuthCredentials(
     const GURL& url,
     LookupBasicAuthCredentialsCallback callback) {
diff --git a/services/network/network_context.h b/services/network/network_context.h
index 700ddb2..1b0d3a1 100644
--- a/services/network/network_context.h
+++ b/services/network/network_context.h
@@ -345,6 +345,9 @@
   void SaveHttpAuthCache(SaveHttpAuthCacheCallback callback) override;
   void LoadHttpAuthCache(const base::UnguessableToken& cache_key,
                          LoadHttpAuthCacheCallback callback) override;
+  void AddAuthCacheEntry(const net::AuthChallengeInfo& challenge,
+                         const net::AuthCredentials& credentials,
+                         AddAuthCacheEntryCallback callback) override;
   void LookupBasicAuthCredentials(
       const GURL& url,
       LookupBasicAuthCredentialsCallback callback) override;
diff --git a/services/network/network_context_unittest.cc b/services/network/network_context_unittest.cc
index 25f61f4..82a84cc 100644
--- a/services/network/network_context_unittest.cc
+++ b/services/network/network_context_unittest.cc
@@ -113,6 +113,10 @@
 #include "url/scheme_host_port.h"
 #include "url/url_constants.h"
 
+#if !BUILDFLAG(DISABLE_FTP_SUPPORT)
+#include "net/ftp/ftp_auth_cache.h"
+#endif  // !BUILDFLAG(DISABLE_FTP_SUPPORT)
+
 #if BUILDFLAG(ENABLE_REPORTING)
 #include "net/network_error_logging/network_error_logging_service.h"
 #include "net/reporting/reporting_cache.h"
@@ -5573,6 +5577,75 @@
   EXPECT_EQ("None", response_body);
 }
 
+#if !BUILDFLAG(DISABLE_FTP_SUPPORT)
+TEST_F(NetworkContextTest, AddFtpAuthCacheEntry) {
+  GURL url("ftp://example.test/");
+  const char kUsername[] = "test_user";
+  const char kPassword[] = "test_pass";
+  mojom::NetworkContextParamsPtr params = CreateContextParams();
+  params->enable_ftp_url_support = true;
+  std::unique_ptr<NetworkContext> network_context =
+      CreateContextWithParams(std::move(params));
+  net::AuthChallengeInfo challenge;
+  challenge.is_proxy = false;
+  challenge.challenger = url::Origin::Create(url);
+
+  ASSERT_TRUE(network_context->url_request_context()->ftp_auth_cache());
+  ASSERT_FALSE(
+      network_context->url_request_context()->ftp_auth_cache()->Lookup(url));
+  base::RunLoop run_loop;
+  network_context->AddAuthCacheEntry(
+      challenge,
+      net::AuthCredentials(base::ASCIIToUTF16(kUsername),
+                           base::ASCIIToUTF16(kPassword)),
+      run_loop.QuitClosure());
+  run_loop.Run();
+  net::FtpAuthCache::Entry* entry =
+      network_context->url_request_context()->ftp_auth_cache()->Lookup(url);
+  ASSERT_TRUE(entry);
+  EXPECT_EQ(url, entry->origin);
+  EXPECT_EQ(base::ASCIIToUTF16(kUsername), entry->credentials.username());
+  EXPECT_EQ(base::ASCIIToUTF16(kPassword), entry->credentials.password());
+}
+#endif  // !BUILDFLAG(DISABLE_FTP_SUPPORT)
+
+TEST_F(NetworkContextTest, AddHttpAuthCacheEntry) {
+  GURL url("http://example.test/");
+  std::unique_ptr<NetworkContext> network_context =
+      CreateContextWithParams(CreateContextParams());
+  net::AuthChallengeInfo challenge;
+  challenge.is_proxy = false;
+  challenge.challenger = url::Origin::Create(url);
+  challenge.scheme = "basic";
+  challenge.realm = "testrealm";
+  const char kUsername[] = "test_user";
+  const char kPassword[] = "test_pass";
+
+  net::HttpAuthCache* cache = network_context->url_request_context()
+                                  ->http_transaction_factory()
+                                  ->GetSession()
+                                  ->http_auth_cache();
+  ASSERT_TRUE(cache);
+
+  ASSERT_FALSE(
+      cache->Lookup(url, challenge.realm, net::HttpAuth::AUTH_SCHEME_BASIC));
+  base::RunLoop run_loop;
+  network_context->AddAuthCacheEntry(
+      challenge,
+      net::AuthCredentials(base::ASCIIToUTF16(kUsername),
+                           base::ASCIIToUTF16(kPassword)),
+      run_loop.QuitClosure());
+  run_loop.Run();
+  net::HttpAuthCache::Entry* entry =
+      cache->Lookup(url, challenge.realm, net::HttpAuth::AUTH_SCHEME_BASIC);
+  ASSERT_TRUE(entry);
+  EXPECT_EQ(url, entry->origin());
+  EXPECT_EQ(challenge.realm, entry->realm());
+  EXPECT_EQ(net::HttpAuth::StringToScheme(challenge.scheme), entry->scheme());
+  EXPECT_EQ(base::ASCIIToUTF16(kUsername), entry->credentials().username());
+  EXPECT_EQ(base::ASCIIToUTF16(kPassword), entry->credentials().password());
+}
+
 }  // namespace
 
 }  // namespace network
diff --git a/services/network/public/cpp/BUILD.gn b/services/network/public/cpp/BUILD.gn
index d27d3c4..474a692 100644
--- a/services/network/public/cpp/BUILD.gn
+++ b/services/network/public/cpp/BUILD.gn
@@ -115,6 +115,7 @@
     "net_ipc_param_traits.h",
     "network_ipc_param_traits.cc",
     "network_ipc_param_traits.h",
+    "network_isolation_key_mojom_traits.h",
     "p2p_param_traits.cc",
     "p2p_param_traits.h",
     "p2p_socket_type.h",
@@ -194,6 +195,7 @@
     "mutable_network_traffic_annotation_tag_mojom_traits_unittest.cc",
     "mutable_partial_network_traffic_annotation_tag_mojom_traits_unittest.cc",
     "network_connection_tracker_unittest.cc",
+    "network_isolation_key_mojom_traits_unittest.cc",
     "network_mojom_traits_unittest.cc",
     "network_quality_tracker_unittest.cc",
     "proxy_config_mojom_traits_unittest.cc",
diff --git a/services/network/public/cpp/network_isolation_key.typemap b/services/network/public/cpp/network_isolation_key.typemap
new file mode 100644
index 0000000..22e1f7c
--- /dev/null
+++ b/services/network/public/cpp/network_isolation_key.typemap
@@ -0,0 +1,12 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.↵
+
+mojom = "//services/network/public/mojom/network_isolation_key.mojom"
+public_headers = [ "//net/base/network_isolation_key.h" ]
+traits_headers =
+    [ "//services/network/public/cpp/network_isolation_key_mojom_traits.h" ]
+public_deps = [
+  "//net",
+]
+type_mappings = [ "network.mojom.NetworkIsolationKey=net::NetworkIsolationKey" ]
diff --git a/services/network/public/cpp/network_isolation_key_mojom_traits.h b/services/network/public/cpp/network_isolation_key_mojom_traits.h
new file mode 100644
index 0000000..8a2dcfe
--- /dev/null
+++ b/services/network/public/cpp/network_isolation_key_mojom_traits.h
@@ -0,0 +1,44 @@
+// 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 SERVICES_NETWORK_PUBLIC_CPP_NETWORK_ISOLATION_KEY_MOJOM_TRAITS_H_
+#define SERVICES_NETWORK_PUBLIC_CPP_NETWORK_ISOLATION_KEY_MOJOM_TRAITS_H_
+
+#include "mojo/public/cpp/bindings/struct_traits.h"
+#include "net/base/network_isolation_key.h"
+#include "services/network/public/mojom/network_isolation_key.mojom-shared.h"
+#include "url/mojom/origin_mojom_traits.h"
+#include "url/origin.h"
+
+namespace mojo {
+
+template <>
+struct StructTraits<network::mojom::NetworkIsolationKeyDataView,
+                    net::NetworkIsolationKey> {
+  static bool IsNull(const net::NetworkIsolationKey& input) {
+    return input.IsEmpty();
+  }
+
+  static void SetToNull(net::NetworkIsolationKey* out) {
+    *out = net::NetworkIsolationKey();
+  }
+
+  static const base::Optional<url::Origin>& top_frame_origin(
+      const net::NetworkIsolationKey& input) {
+    return input.GetTopFrameOrigin();
+  }
+
+  static bool Read(network::mojom::NetworkIsolationKeyDataView data,
+                   net::NetworkIsolationKey* out) {
+    base::Optional<url::Origin> top_frame_origin;
+    if (!data.ReadTopFrameOrigin(&top_frame_origin))
+      return false;
+    *out = net::NetworkIsolationKey(top_frame_origin);
+    return true;
+  }
+};
+
+}  // namespace mojo
+
+#endif  // SERVICES_NETWORK_PUBLIC_CPP_NETWORK_ISOLATION_KEY_MOJOM_TRAITS_H_
diff --git a/services/network/public/cpp/network_isolation_key_mojom_traits_unittest.cc b/services/network/public/cpp/network_isolation_key_mojom_traits_unittest.cc
new file mode 100644
index 0000000..c5a0f8fa
--- /dev/null
+++ b/services/network/public/cpp/network_isolation_key_mojom_traits_unittest.cc
@@ -0,0 +1,29 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/network/public/cpp/network_isolation_key_mojom_traits.h"
+
+#include "base/stl_util.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
+#include "services/network/public/mojom/network_isolation_key.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+#include "url/origin.h"
+
+namespace mojo {
+
+TEST(NetworkIsolationKeyMojomTraitsTest, SerializeAndDeserialize) {
+  std::vector<net::NetworkIsolationKey> keys = {
+      net::NetworkIsolationKey(),
+      net::NetworkIsolationKey(url::Origin::Create(GURL("http://a.test/")))};
+
+  for (auto original : keys) {
+    net::NetworkIsolationKey copied;
+    EXPECT_TRUE(mojo::test::SerializeAndDeserialize<
+                network::mojom::NetworkIsolationKey>(&original, &copied));
+    EXPECT_EQ(original, copied);
+  }
+}
+
+}  // namespace mojo
diff --git a/services/network/public/cpp/typemaps.gni b/services/network/public/cpp/typemaps.gni
index b0677ef..200e74d1 100644
--- a/services/network/public/cpp/typemaps.gni
+++ b/services/network/public/cpp/typemaps.gni
@@ -18,6 +18,7 @@
   "//services/network/public/cpp/mutable_partial_network_traffic_annotation_tag.typemap",
   "//services/network/public/cpp/net_log.typemap",
   "//services/network/public/cpp/network_interface.typemap",
+  "//services/network/public/cpp/network_isolation_key.typemap",
   "//services/network/public/cpp/network_param.typemap",
   "//services/network/public/cpp/network_types.typemap",
   "//services/network/public/cpp/p2p.typemap",
diff --git a/services/network/public/mojom/BUILD.gn b/services/network/public/mojom/BUILD.gn
index 036bcc4..1cb1141 100644
--- a/services/network/public/mojom/BUILD.gn
+++ b/services/network/public/mojom/BUILD.gn
@@ -84,6 +84,7 @@
     "network_change_manager.mojom",
     "network_context.mojom",
     "network_interface.mojom",
+    "network_isolation_key.mojom",
     "network_quality_estimator_manager.mojom",
     "network_service.mojom",
     "network_service_test.mojom",
diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom
index 18ccc3dd..1d0ab84 100644
--- a/services/network/public/mojom/network_context.mojom
+++ b/services/network/public/mojom/network_context.mojom
@@ -19,6 +19,7 @@
 import "services/network/public/mojom/mdns_responder.mojom";
 import "services/network/public/mojom/mutable_network_traffic_annotation_tag.mojom";
 import "services/network/public/mojom/net_log.mojom";
+import "services/network/public/mojom/network_isolation_key.mojom";
 import "services/network/public/mojom/network_param.mojom";
 import "services/network/public/mojom/origin_policy_manager.mojom";
 import "services/network/public/mojom/p2p.mojom";
@@ -481,6 +482,9 @@
   // in order to bind the allow patterns to the factory specific initiator
   // origin.
   array<CorsOriginPattern> factory_bound_allow_patterns;
+
+  // Key used to isolate shared network resources like the cache.
+  NetworkIsolationKey? network_isolation_key;
 };
 
 // Callback interface for NetworkContext when routing identifiers aren't
@@ -931,6 +935,13 @@
   // copies its contents into this NetworkContext's HttpAuthCache.
   LoadHttpAuthCache(mojo_base.mojom.UnguessableToken cache_key) => ();
 
+  // Adds an entry to the HttpAuthCache or FtpAuthCache (determined by whether
+  // the |challenger| field within |challenge| is an ftp:// URL). |challenge|
+  // may not necessarily contain a stateful challenge that requires a persistent
+  // connection, allowing the cache to be pre-populated.
+  AddAuthCacheEntry(AuthChallengeInfo challenge,
+                    AuthCredentials credentials) => ();
+
   // Looks up credentials in the HttpAuthCache using the origin and path from
   // |url|. Only supports basic auth scheme.
   LookupBasicAuthCredentials(url.mojom.Url url)
diff --git a/services/network/public/mojom/network_isolation_key.mojom b/services/network/public/mojom/network_isolation_key.mojom
new file mode 100644
index 0000000..2964809
--- /dev/null
+++ b/services/network/public/mojom/network_isolation_key.mojom
@@ -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.
+
+module network.mojom;
+
+import "url/mojom/origin.mojom";
+
+// Mapped to net::NetworkIsolationKey.
+struct NetworkIsolationKey {
+  // Keeping optional to allow clients that do not populate top frame origin.
+  // TODO(crbug.com/910721): This will eventually always be populated.
+  url.mojom.Origin? top_frame_origin;
+};
diff --git a/services/network/test/test_network_context.h b/services/network/test/test_network_context.h
index 98b59d4f..4aa4146 100644
--- a/services/network/test/test_network_context.h
+++ b/services/network/test/test_network_context.h
@@ -209,6 +209,9 @@
   void SaveHttpAuthCache(SaveHttpAuthCacheCallback callback) override {}
   void LoadHttpAuthCache(const base::UnguessableToken& cache_key,
                          LoadHttpAuthCacheCallback callback) override {}
+  void AddAuthCacheEntry(const net::AuthChallengeInfo& challenge,
+                         const net::AuthCredentials& credentials,
+                         AddAuthCacheEntryCallback callback) override {}
   void LookupBasicAuthCredentials(
       const GURL& url,
       LookupBasicAuthCredentialsCallback callback) override {}
diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc
index ddd35b8..23780c6 100644
--- a/services/network/url_loader.cc
+++ b/services/network/url_loader.cc
@@ -390,6 +390,11 @@
   url_request_->set_referrer_policy(request.referrer_policy);
   url_request_->set_upgrade_if_insecure(request.upgrade_if_insecure);
 
+  if (factory_params_->network_isolation_key) {
+    url_request_->set_network_isolation_key(
+        factory_params_->network_isolation_key.value());
+  }
+
   // |cors_excempt_headers| must be merged here to avoid breaking CORS checks.
   // They are non-empty when the values are given by the UA code, therefore
   // they should be ignored by CORS checks.
diff --git a/services/network/url_loader_unittest.cc b/services/network/url_loader_unittest.cc
index b9e599ab..b918634 100644
--- a/services/network/url_loader_unittest.cc
+++ b/services/network/url_loader_unittest.cc
@@ -25,6 +25,7 @@
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/gtest_util.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/scoped_task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
@@ -34,6 +35,7 @@
 #include "mojo/public/cpp/system/data_pipe_utils.h"
 #include "mojo/public/cpp/system/wait.h"
 #include "net/base/escape.h"
+#include "net/base/features.h"
 #include "net/base/io_buffer.h"
 #include "net/base/load_flags.h"
 #include "net/base/mime_sniffer.h"
@@ -733,6 +735,47 @@
   TestURLLoaderClient client_;
 };
 
+class URLLoaderNetworkIsolationTest : public URLLoaderTest {
+ protected:
+  void SetUp() override {
+    feature_list_.InitAndEnableFeature(
+        net::features::kSplitCacheByTopFrameOrigin);
+    URLLoaderTest::SetUp();
+  }
+
+  void LoadAndVerifyCached(const GURL& url,
+                           const net::NetworkIsolationKey& key,
+                           bool was_cached) {
+    ResourceRequest request = CreateResourceRequest("GET", url);
+    request.load_flags |= net::LOAD_SKIP_CACHE_VALIDATION;
+
+    TestURLLoaderClient client;
+    base::RunLoop delete_run_loop;
+    mojom::URLLoaderPtr loader;
+    std::unique_ptr<URLLoader> url_loader;
+    static mojom::URLLoaderFactoryParams params;
+    params.process_id = mojom::kBrowserProcessId;
+    params.is_corb_enabled = false;
+    params.network_isolation_key = key;
+    url_loader = std::make_unique<URLLoader>(
+        context(), nullptr /* network_service_client */,
+        DeleteLoaderCallback(&delete_run_loop, &url_loader),
+        mojo::MakeRequest(&loader), 0, request, client.CreateInterfacePtr(),
+        TRAFFIC_ANNOTATION_FOR_TESTS, &params, 0 /* request_id */,
+        resource_scheduler_client(), nullptr,
+        nullptr /* network_usage_accumulator */, nullptr /* header_client */);
+
+    client.RunUntilComplete();
+    delete_run_loop.Run();
+
+    EXPECT_EQ(net::OK, client.completion_status().error_code);
+    EXPECT_EQ(was_cached, client.completion_status().exists_in_cache);
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
 constexpr int URLLoaderTest::kProcessId;
 constexpr int URLLoaderTest::kRouteId;
 
@@ -2780,6 +2823,22 @@
   delete_run_loop.Run();
 }
 
+TEST_F(URLLoaderNetworkIsolationTest, CachedUsingNetworkIsolationKey) {
+  GURL url = test_server()->GetURL("/resource");
+  url::Origin origin_a = url::Origin::Create(GURL("http://a.test/"));
+  net::NetworkIsolationKey key_a(origin_a);
+  LoadAndVerifyCached(url, key_a, false /* was_cached */);
+
+  // Load again with a different isolation key. The cached entry should not be
+  // loaded.
+  url::Origin origin_b = url::Origin::Create(GURL("http://b.test/"));
+  net::NetworkIsolationKey key_b(origin_b);
+  LoadAndVerifyCached(url, key_b, false /* was_cached */);
+
+  // Load again with the same isolation key. The cached entry should be loaded.
+  LoadAndVerifyCached(url, key_b, true /* was_cached */);
+}
+
 class TestSSLPrivateKey : public net::SSLPrivateKey {
  public:
   explicit TestSSLPrivateKey(scoped_refptr<net::SSLPrivateKey> key)
diff --git a/services/video_capture/BUILD.gn b/services/video_capture/BUILD.gn
index 0d429eef..e8aa895 100644
--- a/services/video_capture/BUILD.gn
+++ b/services/video_capture/BUILD.gn
@@ -64,7 +64,7 @@
     "//services/video_capture/public/mojom",
     "//services/video_capture/public/mojom:constants",
     "//services/video_capture/public/uma",
-    "//services/ws/public/cpp/gpu:gpu",
+    "//services/viz/public/cpp/gpu",
   ]
 
   if (is_chromeos) {
diff --git a/services/video_capture/DEPS b/services/video_capture/DEPS
index 5f88ce6..16163ad 100644
--- a/services/video_capture/DEPS
+++ b/services/video_capture/DEPS
@@ -4,5 +4,5 @@
   "+media/mojo",
   "+media/capture",
   "+ui/gfx/geometry",
-  "+services/ws/public/cpp/gpu",
+  "+services/viz/public/cpp/gpu",
 ]
diff --git a/services/video_capture/device_factory_provider_impl.cc b/services/video_capture/device_factory_provider_impl.cc
index 2c22e7c..154a045 100644
--- a/services/video_capture/device_factory_provider_impl.cc
+++ b/services/video_capture/device_factory_provider_impl.cc
@@ -18,7 +18,7 @@
 #include "services/video_capture/device_factory_media_to_mojo_adapter.h"
 #include "services/video_capture/video_source_provider_impl.h"
 #include "services/video_capture/virtual_device_enabled_device_factory.h"
-#include "services/ws/public/cpp/gpu/gpu.h"
+#include "services/viz/public/cpp/gpu/gpu.h"
 
 #if defined(OS_MACOSX)
 #include "media/capture/video/mac/video_capture_device_factory_mac.h"
diff --git a/services/video_capture/public/mojom/BUILD.gn b/services/video_capture/public/mojom/BUILD.gn
index a7b43a5..908532a 100644
--- a/services/video_capture/public/mojom/BUILD.gn
+++ b/services/video_capture/public/mojom/BUILD.gn
@@ -23,7 +23,6 @@
     "//media/capture/mojom:image_capture",
     "//media/capture/mojom:video_capture",
     "//media/mojo/interfaces",
-    "//services/ws/public/mojom",
     "//ui/gfx/geometry/mojo",
   ]
 
diff --git a/services/viz/public/cpp/compositing/copy_output_result_struct_traits.cc b/services/viz/public/cpp/compositing/copy_output_result_struct_traits.cc
index da6b5143..4e7ded9 100644
--- a/services/viz/public/cpp/compositing/copy_output_result_struct_traits.cc
+++ b/services/viz/public/cpp/compositing/copy_output_result_struct_traits.cc
@@ -169,22 +169,20 @@
     return false;
   }
 
+  if (rect.IsEmpty()) {
+    // An empty rect implies an empty result.
+    *out_p = std::make_unique<viz::CopyOutputResult>(format, gfx::Rect());
+    return true;
+  }
+
   switch (format) {
     case viz::CopyOutputResult::Format::RGBA_BITMAP: {
       SkBitmap bitmap;
-      if (!data.ReadBitmap(&bitmap)) {
+      if (!data.ReadBitmap(&bitmap) || !bitmap.readyToDraw()) {
         LOG(ERROR) << "CopyOutputResult: Failed to read |bitmap|";
         return false;
       }
 
-      bool has_bitmap = bitmap.readyToDraw();
-
-      // The rect should be empty iff there is no bitmap.
-      if (has_bitmap == rect.IsEmpty()) {
-        LOG(ERROR) << "CopyOutputResult: has_bitmap == rect.IsEmpty()";
-        return false;
-      }
-
       *out_p = std::make_unique<viz::CopyOutputSkBitmapResult>(
           rect, std::move(bitmap));
       return true;
@@ -207,15 +205,7 @@
         return false;
       }
 
-      bool has_mailbox = !mailbox->IsZero();
-
-      // The rect should be empty iff there is no texture.
-      if (has_mailbox == rect.IsEmpty()) {
-        LOG(ERROR) << "CopyOutputResult: has_mailbox == rect.IsEmpty()";
-        return false;
-      }
-
-      if (!has_mailbox) {
+      if (mailbox->IsZero()) {
         // Returns an empty result.
         *out_p = std::make_unique<viz::CopyOutputResult>(
             viz::CopyOutputResult::Format::RGBA_TEXTURE, gfx::Rect());
diff --git a/services/viz/public/cpp/compositing/frame_timing_details.typemap b/services/viz/public/cpp/compositing/frame_timing_details.typemap
new file mode 100644
index 0000000..ad11150
--- /dev/null
+++ b/services/viz/public/cpp/compositing/frame_timing_details.typemap
@@ -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.
+
+mojom =
+    "//services/viz/public/interfaces/compositing/frame_timing_details.mojom"
+public_headers = [ "//components/viz/common/frame_timing_details.h" ]
+deps = [
+  "//components/viz/common",
+]
+traits_headers = [
+  "//services/viz/public/cpp/compositing/frame_timing_details_struct_traits.h",
+]
+type_mappings = [ "viz.mojom.FrameTimingDetails=viz::FrameTimingDetails" ]
diff --git a/services/viz/public/cpp/compositing/frame_timing_details_struct_traits.h b/services/viz/public/cpp/compositing/frame_timing_details_struct_traits.h
new file mode 100644
index 0000000..ab11278
--- /dev/null
+++ b/services/viz/public/cpp/compositing/frame_timing_details_struct_traits.h
@@ -0,0 +1,31 @@
+// 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 SERVICES_VIZ_PUBLIC_CPP_COMPOSITING_FRAME_TIMING_DETAILS_STRUCT_TRAITS_H_
+#define SERVICES_VIZ_PUBLIC_CPP_COMPOSITING_FRAME_TIMING_DETAILS_STRUCT_TRAITS_H_
+
+#include "components/viz/common/frame_timing_details.h"
+#include "services/viz/public/interfaces/compositing/frame_timing_details.mojom-shared.h"
+
+#include "ui/gfx/presentation_feedback.h"
+
+namespace mojo {
+
+template <>
+struct StructTraits<viz::mojom::FrameTimingDetailsDataView,
+                    viz::FrameTimingDetails> {
+  static gfx::PresentationFeedback presentation_feedback(
+      const viz::FrameTimingDetails& frame_timing_details) {
+    return frame_timing_details.presentation_feedback;
+  }
+
+  static bool Read(viz::mojom::FrameTimingDetailsDataView data,
+                   viz::FrameTimingDetails* out) {
+    return data.ReadPresentationFeedback(&out->presentation_feedback);
+  }
+};
+
+}  // namespace mojo
+
+#endif  // SERVICES_VIZ_PUBLIC_CPP_COMPOSITING_FRAME_TIMING_DETAILS_STRUCT_TRAITS_H_
diff --git a/services/viz/public/cpp/compositing/struct_traits_unittest.cc b/services/viz/public/cpp/compositing/struct_traits_unittest.cc
index 8d23671..53eb703d4 100644
--- a/services/viz/public/cpp/compositing/struct_traits_unittest.cc
+++ b/services/viz/public/cpp/compositing/struct_traits_unittest.cc
@@ -1197,7 +1197,7 @@
   EXPECT_EQ(protected_video_type, out_quad->protected_video_type);
 }
 
-TEST_F(StructTraitsTest, CopyOutputResult_Empty) {
+TEST_F(StructTraitsTest, CopyOutputResult_EmptyBitmap) {
   auto input = std::make_unique<CopyOutputResult>(
       CopyOutputResult::Format::RGBA_BITMAP, gfx::Rect());
   std::unique_ptr<CopyOutputResult> output;
@@ -1210,6 +1210,22 @@
   EXPECT_EQ(output->GetTextureResult(), nullptr);
 }
 
+TEST_F(StructTraitsTest, CopyOutputResult_EmptyTexture) {
+  base::test::ScopedTaskEnvironment scoped_task_environment;
+
+  auto input = std::make_unique<CopyOutputResult>(
+      CopyOutputResult::Format::RGBA_TEXTURE, gfx::Rect());
+  EXPECT_TRUE(input->IsEmpty());
+
+  std::unique_ptr<CopyOutputResult> output;
+  mojo::test::SerializeAndDeserialize<mojom::CopyOutputResult>(&input, &output);
+
+  EXPECT_TRUE(output->IsEmpty());
+  EXPECT_EQ(output->format(), CopyOutputResult::Format::RGBA_TEXTURE);
+  EXPECT_TRUE(output->rect().IsEmpty());
+  EXPECT_EQ(output->GetTextureResult(), nullptr);
+}
+
 TEST_F(StructTraitsTest, CopyOutputResult_Bitmap) {
   const gfx::Rect result_rect(42, 43, 7, 8);
   SkBitmap bitmap;
diff --git a/services/viz/public/cpp/compositing/typemaps.gni b/services/viz/public/cpp/compositing/typemaps.gni
index a51b5648..240834f 100644
--- a/services/viz/public/cpp/compositing/typemaps.gni
+++ b/services/viz/public/cpp/compositing/typemaps.gni
@@ -12,6 +12,7 @@
   "//services/viz/public/cpp/compositing/copy_output_request.typemap",
   "//services/viz/public/cpp/compositing/copy_output_result.typemap",
   "//services/viz/public/cpp/compositing/frame_sink_id.typemap",
+  "//services/viz/public/cpp/compositing/frame_timing_details.typemap",
   "//services/viz/public/cpp/compositing/local_surface_id.typemap",
   "//services/viz/public/cpp/compositing/local_surface_id_allocation.typemap",
   "//services/viz/public/cpp/compositing/paint_filter.typemap",
diff --git a/services/ws/public/cpp/gpu/BUILD.gn b/services/viz/public/cpp/gpu/BUILD.gn
similarity index 71%
rename from services/ws/public/cpp/gpu/BUILD.gn
rename to services/viz/public/cpp/gpu/BUILD.gn
index 382dc28..cbb1e82 100644
--- a/services/ws/public/cpp/gpu/BUILD.gn
+++ b/services/viz/public/cpp/gpu/BUILD.gn
@@ -24,7 +24,7 @@
     "//gpu/command_buffer/common",
     "//gpu/ipc/client",
     "//gpu/ipc/common",
-    "//services/ws/public/mojom",
+    "//services/viz/public/interfaces",
     "//url",
   ]
 
@@ -41,7 +41,7 @@
     "//gpu/skia_bindings",
     "//mojo/public/cpp/system",
     "//services/service_manager/public/cpp",
-    "//services/ws/public/mojom",
+    "//services/viz/public/interfaces",
     "//ui/base:features",
     "//ui/gl",
   ]
@@ -52,3 +52,28 @@
     sources += [ "shared_worker_context_provider_factory.cc" ]
   }
 }
+
+source_set("tests") {
+  testonly = true
+
+  sources = [
+    "gpu_unittest.cc",
+  ]
+
+  deps = [
+    "//base",
+    "//base/test:test_support",
+    "//mojo/core/embedder",
+    "//mojo/public/cpp/system",
+    "//services/service_manager/public/cpp",
+    "//services/viz/public/cpp/gpu",
+    "//testing/gtest",
+    "//ui/gfx:test_support",
+    "//ui/gfx/geometry",
+    "//ui/gfx/geometry/mojo",
+  ]
+
+  if (use_x11) {
+    deps += [ "//ui/gfx/x" ]
+  }
+}
diff --git a/services/viz/public/cpp/gpu/DEPS b/services/viz/public/cpp/gpu/DEPS
new file mode 100644
index 0000000..6531f8c
--- /dev/null
+++ b/services/viz/public/cpp/gpu/DEPS
@@ -0,0 +1,8 @@
+include_rules = [
+  "+components/viz/common/gpu",
+  "+gpu",
+  "+third_party/skia",
+  "+ui/base/ui_base_features.h",
+  "+ui/gfx",
+  "+ui/gl",
+]
diff --git a/services/ws/public/cpp/gpu/OWNERS b/services/viz/public/cpp/gpu/OWNERS
similarity index 100%
rename from services/ws/public/cpp/gpu/OWNERS
rename to services/viz/public/cpp/gpu/OWNERS
diff --git a/services/ws/public/cpp/gpu/client_gpu_memory_buffer_manager.cc b/services/viz/public/cpp/gpu/client_gpu_memory_buffer_manager.cc
similarity index 97%
rename from services/ws/public/cpp/gpu/client_gpu_memory_buffer_manager.cc
rename to services/viz/public/cpp/gpu/client_gpu_memory_buffer_manager.cc
index 499c2a35..1d02f26 100644
--- a/services/ws/public/cpp/gpu/client_gpu_memory_buffer_manager.cc
+++ b/services/viz/public/cpp/gpu/client_gpu_memory_buffer_manager.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "services/ws/public/cpp/gpu/client_gpu_memory_buffer_manager.h"
+#include "services/viz/public/cpp/gpu/client_gpu_memory_buffer_manager.h"
 
 #include <utility>
 
@@ -18,7 +18,7 @@
 #include "services/service_manager/public/cpp/connector.h"
 #include "ui/gfx/buffer_format_util.h"
 
-namespace ws {
+namespace viz {
 namespace {
 
 void NotifyDestructionOnCorrectThread(
@@ -173,4 +173,4 @@
       sync_token);
 }
 
-}  // namespace ws
+}  // namespace viz
diff --git a/services/ws/public/cpp/gpu/client_gpu_memory_buffer_manager.h b/services/viz/public/cpp/gpu/client_gpu_memory_buffer_manager.h
similarity index 88%
rename from services/ws/public/cpp/gpu/client_gpu_memory_buffer_manager.h
rename to services/viz/public/cpp/gpu/client_gpu_memory_buffer_manager.h
index 51ebe19..4cbb6e6 100644
--- a/services/ws/public/cpp/gpu/client_gpu_memory_buffer_manager.h
+++ b/services/viz/public/cpp/gpu/client_gpu_memory_buffer_manager.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 SERVICES_WS_PUBLIC_CPP_GPU_CLIENT_GPU_MEMORY_BUFFER_MANAGER_H_
-#define SERVICES_WS_PUBLIC_CPP_GPU_CLIENT_GPU_MEMORY_BUFFER_MANAGER_H_
+#ifndef SERVICES_VIZ_PUBLIC_CPP_GPU_CLIENT_GPU_MEMORY_BUFFER_MANAGER_H_
+#define SERVICES_VIZ_PUBLIC_CPP_GPU_CLIENT_GPU_MEMORY_BUFFER_MANAGER_H_
 
 #include <memory>
 #include <set>
@@ -14,7 +14,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/threading/thread.h"
 #include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
-#include "services/ws/public/mojom/gpu.mojom.h"
+#include "services/viz/public/interfaces/gpu.mojom.h"
 
 namespace base {
 class WaitableEvent;
@@ -24,7 +24,7 @@
 class GpuMemoryBufferSupport;
 }
 
-namespace ws {
+namespace viz {
 
 // Implements gpu::GpuMemoryBufferManager based on a given
 // mojom::GpuMemoryBufferFactory
@@ -70,6 +70,6 @@
   DISALLOW_COPY_AND_ASSIGN(ClientGpuMemoryBufferManager);
 };
 
-}  // namespace ws
+}  // namespace viz
 
-#endif  // SERVICES_WS_PUBLIC_CPP_GPU_CLIENT_GPU_MEMORY_BUFFER_MANAGER_H_
+#endif  // SERVICES_VIZ_PUBLIC_CPP_GPU_CLIENT_GPU_MEMORY_BUFFER_MANAGER_H_
diff --git a/services/ws/public/cpp/gpu/command_buffer_metrics.cc b/services/viz/public/cpp/gpu/command_buffer_metrics.cc
similarity index 91%
rename from services/ws/public/cpp/gpu/command_buffer_metrics.cc
rename to services/viz/public/cpp/gpu/command_buffer_metrics.cc
index 2671ff8..a85a154 100644
--- a/services/ws/public/cpp/gpu/command_buffer_metrics.cc
+++ b/services/viz/public/cpp/gpu/command_buffer_metrics.cc
@@ -2,17 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "services/ws/public/cpp/gpu/command_buffer_metrics.h"
+#include "services/viz/public/cpp/gpu/command_buffer_metrics.h"
 
 #include "base/metrics/histogram_macros.h"
 #include "components/viz/common/gpu/context_lost_reason.h"
 
-namespace ws {
+namespace viz {
 namespace command_buffer_metrics {
 
 namespace {
 
-void RecordContextLost(ContextType type, viz::ContextLostReason reason) {
+void RecordContextLost(ContextType type, ContextLostReason reason) {
   switch (type) {
     case ContextType::BROWSER_COMPOSITOR:
       UMA_HISTOGRAM_ENUMERATION("GPU.ContextLost.BrowserCompositor", reason);
@@ -100,16 +100,15 @@
 }
 
 void UmaRecordContextInitFailed(ContextType type) {
-  RecordContextLost(type, viz::CONTEXT_INIT_FAILED);
+  RecordContextLost(type, CONTEXT_INIT_FAILED);
 }
 
 void UmaRecordContextLost(ContextType type,
                           gpu::error::Error error,
                           gpu::error::ContextLostReason reason) {
-  viz::ContextLostReason converted_reason =
-      viz::GetContextLostReason(error, reason);
+  ContextLostReason converted_reason = GetContextLostReason(error, reason);
   RecordContextLost(type, converted_reason);
 }
 
 }  // namespace command_buffer_metrics
-}  // namespace ws
+}  // namespace viz
diff --git a/services/ws/public/cpp/gpu/command_buffer_metrics.h b/services/viz/public/cpp/gpu/command_buffer_metrics.h
similarity index 82%
rename from services/ws/public/cpp/gpu/command_buffer_metrics.h
rename to services/viz/public/cpp/gpu/command_buffer_metrics.h
index 8bb757c..b16f61b 100644
--- a/services/ws/public/cpp/gpu/command_buffer_metrics.h
+++ b/services/viz/public/cpp/gpu/command_buffer_metrics.h
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef SERVICES_WS_PUBLIC_CPP_GPU_COMMAND_BUFFER_METRICS_H_
-#define SERVICES_WS_PUBLIC_CPP_GPU_COMMAND_BUFFER_METRICS_H_
+#ifndef SERVICES_VIZ_PUBLIC_CPP_GPU_COMMAND_BUFFER_METRICS_H_
+#define SERVICES_VIZ_PUBLIC_CPP_GPU_COMMAND_BUFFER_METRICS_H_
 
 #include <string>
 
 #include "gpu/command_buffer/common/constants.h"
 
-namespace ws {
+namespace viz {
 namespace command_buffer_metrics {
 
 // A rough classification for what the context is used for. These enum types
@@ -43,6 +43,6 @@
                           gpu::error::ContextLostReason reason);
 
 }  // namespace command_buffer_metrics
-}  // namespace ws
+}  // namespace viz
 
-#endif  // SERVICES_WS_PUBLIC_CPP_GPU_COMMAND_BUFFER_METRICS_H_
+#endif  // SERVICES_VIZ_PUBLIC_CPP_GPU_COMMAND_BUFFER_METRICS_H_
diff --git a/services/ws/public/cpp/gpu/context_provider_command_buffer.cc b/services/viz/public/cpp/gpu/context_provider_command_buffer.cc
similarity index 97%
rename from services/ws/public/cpp/gpu/context_provider_command_buffer.cc
rename to services/viz/public/cpp/gpu/context_provider_command_buffer.cc
index f1225d8..bb1dfb4 100644
--- a/services/ws/public/cpp/gpu/context_provider_command_buffer.cc
+++ b/services/viz/public/cpp/gpu/context_provider_command_buffer.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "services/ws/public/cpp/gpu/context_provider_command_buffer.h"
+#include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
 
 #include <stddef.h>
 
@@ -39,14 +39,14 @@
 #include "gpu/ipc/client/gpu_channel_host.h"
 #include "gpu/skia_bindings/gles2_implementation_with_grcontext_support.h"
 #include "gpu/skia_bindings/grcontext_for_gles2_interface.h"
-#include "services/ws/public/cpp/gpu/command_buffer_metrics.h"
+#include "services/viz/public/cpp/gpu/command_buffer_metrics.h"
 #include "third_party/skia/include/core/SkTraceMemoryDump.h"
 #include "third_party/skia/include/gpu/GrContext.h"
 #include "ui/gl/trace_util.h"
 
 class SkDiscardableMemory;
 
-namespace ws {
+namespace viz {
 
 ContextProviderCommandBuffer::ContextProviderCommandBuffer(
     scoped_refptr<gpu::GpuChannelHost> channel,
@@ -283,7 +283,7 @@
   }
 
   cache_controller_ =
-      std::make_unique<viz::ContextCacheController>(impl_, task_runner);
+      std::make_unique<ContextCacheController>(impl_, task_runner);
 
   // TODO(crbug.com/868192): SetLostContextCallback should probably work on
   // WebGPU contexts too.
@@ -415,7 +415,7 @@
   return command_buffer_->channel()->shared_image_interface();
 }
 
-viz::ContextCacheController* ContextProviderCommandBuffer::CacheController() {
+ContextCacheController* ContextProviderCommandBuffer::CacheController() {
   CheckValidThreadOrLockAcquired();
   return cache_controller_.get();
 }
@@ -467,13 +467,12 @@
                                                state.context_lost_reason);
 }
 
-void ContextProviderCommandBuffer::AddObserver(viz::ContextLostObserver* obs) {
+void ContextProviderCommandBuffer::AddObserver(ContextLostObserver* obs) {
   CheckValidThreadOrLockAcquired();
   observers_.AddObserver(obs);
 }
 
-void ContextProviderCommandBuffer::RemoveObserver(
-    viz::ContextLostObserver* obs) {
+void ContextProviderCommandBuffer::RemoveObserver(ContextLostObserver* obs) {
   CheckValidThreadOrLockAcquired();
   observers_.RemoveObserver(obs);
 }
@@ -506,4 +505,4 @@
   return true;
 }
 
-}  // namespace ws
+}  // namespace viz
diff --git a/services/ws/public/cpp/gpu/context_provider_command_buffer.h b/services/viz/public/cpp/gpu/context_provider_command_buffer.h
similarity index 85%
rename from services/ws/public/cpp/gpu/context_provider_command_buffer.h
rename to services/viz/public/cpp/gpu/context_provider_command_buffer.h
index 7598338..77db6d6 100644
--- a/services/ws/public/cpp/gpu/context_provider_command_buffer.h
+++ b/services/viz/public/cpp/gpu/context_provider_command_buffer.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 SERVICES_WS_PUBLIC_CPP_GPU_CONTEXT_PROVIDER_COMMAND_BUFFER_H_
-#define SERVICES_WS_PUBLIC_CPP_GPU_CONTEXT_PROVIDER_COMMAND_BUFFER_H_
+#ifndef SERVICES_VIZ_PUBLIC_CPP_GPU_CONTEXT_PROVIDER_COMMAND_BUFFER_H_
+#define SERVICES_VIZ_PUBLIC_CPP_GPU_CONTEXT_PROVIDER_COMMAND_BUFFER_H_
 
 #include <stdint.h>
 
@@ -22,7 +22,7 @@
 #include "gpu/command_buffer/common/context_creation_attribs.h"
 #include "gpu/command_buffer/common/scheduling_priority.h"
 #include "gpu/ipc/common/surface_handle.h"
-#include "services/ws/public/cpp/gpu/command_buffer_metrics.h"
+#include "services/viz/public/cpp/gpu/command_buffer_metrics.h"
 #include "ui/gl/gpu_preference.h"
 #include "url/gurl.h"
 
@@ -53,14 +53,14 @@
 class GrContextForGLES2Interface;
 }
 
-namespace ws {
+namespace viz {
 
-// Implementation of viz::ContextProvider that provides a GL implementation
+// Implementation of ContextProvider that provides a GL implementation
 // over command buffer to the GPU process.
 class ContextProviderCommandBuffer
     : public base::RefCountedThreadSafe<ContextProviderCommandBuffer>,
-      public viz::ContextProvider,
-      public viz::RasterContextProvider,
+      public ContextProvider,
+      public RasterContextProvider,
       public base::trace_event::MemoryDumpProvider {
  public:
   ContextProviderCommandBuffer(
@@ -82,7 +82,7 @@
   // on the default framebuffer.
   uint32_t GetCopyTextureInternalFormat();
 
-  // viz::ContextProvider / viz::RasterContextProvider implementation.
+  // ContextProvider / RasterContextProvider implementation.
   void AddRef() const override;
   void Release() const override;
   gpu::ContextResult BindToCurrentThread() override;
@@ -91,12 +91,12 @@
   gpu::ContextSupport* ContextSupport() override;
   class GrContext* GrContext() override;
   gpu::SharedImageInterface* SharedImageInterface() override;
-  viz::ContextCacheController* CacheController() override;
+  ContextCacheController* CacheController() override;
   base::Lock* GetLock() override;
   const gpu::Capabilities& ContextCapabilities() const override;
   const gpu::GpuFeatureInfo& GetGpuFeatureInfo() const override;
-  void AddObserver(viz::ContextLostObserver* obs) override;
-  void RemoveObserver(viz::ContextLostObserver* obs) override;
+  void AddObserver(ContextLostObserver* obs) override;
+  void RemoveObserver(ContextLostObserver* obs) override;
 
   gpu::webgpu::WebGPUInterface* WebGPUInterface();
 
@@ -161,11 +161,11 @@
   std::unique_ptr<gpu::webgpu::WebGPUInterface> webgpu_interface_;
 
   std::unique_ptr<skia_bindings::GrContextForGLES2Interface> gr_context_;
-  std::unique_ptr<viz::ContextCacheController> cache_controller_;
+  std::unique_ptr<ContextCacheController> cache_controller_;
 
-  base::ObserverList<viz::ContextLostObserver>::Unchecked observers_;
+  base::ObserverList<ContextLostObserver>::Unchecked observers_;
 };
 
-}  // namespace ws
+}  // namespace viz
 
-#endif  // SERVICES_WS_PUBLIC_CPP_GPU_CONTEXT_PROVIDER_COMMAND_BUFFER_H_
+#endif  // SERVICES_VIZ_PUBLIC_CPP_GPU_CONTEXT_PROVIDER_COMMAND_BUFFER_H_
diff --git a/services/ws/public/cpp/gpu/gpu.cc b/services/viz/public/cpp/gpu/gpu.cc
similarity index 97%
rename from services/ws/public/cpp/gpu/gpu.cc
rename to services/viz/public/cpp/gpu/gpu.cc
index 0a55112..72868bbb5 100644
--- a/services/ws/public/cpp/gpu/gpu.cc
+++ b/services/viz/public/cpp/gpu/gpu.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "services/ws/public/cpp/gpu/gpu.h"
+#include "services/viz/public/cpp/gpu/gpu.h"
 
 #include <memory>
 #include <string>
@@ -17,11 +17,11 @@
 #include "gpu/command_buffer/common/scheduling_priority.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "services/service_manager/public/cpp/connector.h"
-#include "services/ws/public/cpp/gpu/client_gpu_memory_buffer_manager.h"
-#include "services/ws/public/cpp/gpu/context_provider_command_buffer.h"
-#include "services/ws/public/mojom/gpu.mojom.h"
+#include "services/viz/public/cpp/gpu/client_gpu_memory_buffer_manager.h"
+#include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
+#include "services/viz/public/interfaces/gpu.mojom.h"
 
-namespace ws {
+namespace viz {
 
 // Encapsulates a mojom::GpuPtr object that will be used on the IO thread. This
 // is required because we can't install an error handler on a
@@ -277,7 +277,7 @@
   return base::WrapUnique(new Gpu(std::move(gpu_ptr), std::move(task_runner)));
 }
 
-scoped_refptr<ws::ContextProviderCommandBuffer> Gpu::CreateContextProvider(
+scoped_refptr<ContextProviderCommandBuffer> Gpu::CreateContextProvider(
     scoped_refptr<gpu::GpuChannelHost> gpu_channel) {
   int32_t stream_id = 0;
   gpu::SchedulingPriority stream_priority = gpu::SchedulingPriority::kNormal;
@@ -401,4 +401,4 @@
     std::move(callback).Run(gpu_channel_);
 }
 
-}  // namespace ws
+}  // namespace viz
diff --git a/services/ws/public/cpp/gpu/gpu.h b/services/viz/public/cpp/gpu/gpu.h
similarity index 87%
rename from services/ws/public/cpp/gpu/gpu.h
rename to services/viz/public/cpp/gpu/gpu.h
index 7bc40058..9fd796ee 100644
--- a/services/ws/public/cpp/gpu/gpu.h
+++ b/services/viz/public/cpp/gpu/gpu.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 SERVICES_WS_PUBLIC_CPP_GPU_GPU_H_
-#define SERVICES_WS_PUBLIC_CPP_GPU_GPU_H_
+#ifndef SERVICES_VIZ_PUBLIC_CPP_GPU_GPU_H_
+#define SERVICES_VIZ_PUBLIC_CPP_GPU_GPU_H_
 
 #include <stdint.h>
 #include <vector>
@@ -13,14 +13,14 @@
 #include "base/single_thread_task_runner.h"
 #include "components/viz/common/gpu/context_provider.h"
 #include "gpu/ipc/client/gpu_channel_host.h"
-#include "services/ws/public/cpp/gpu/client_gpu_memory_buffer_manager.h"
-#include "services/ws/public/mojom/gpu.mojom.h"
+#include "services/viz/public/cpp/gpu/client_gpu_memory_buffer_manager.h"
+#include "services/viz/public/interfaces/gpu.mojom.h"
 
 namespace service_manager {
 class Connector;
 }
 
-namespace ws {
+namespace viz {
 
 class ContextProviderCommandBuffer;
 
@@ -39,7 +39,7 @@
     return gpu_memory_buffer_manager_.get();
   }
 
-  scoped_refptr<ws::ContextProviderCommandBuffer> CreateContextProvider(
+  scoped_refptr<ContextProviderCommandBuffer> CreateContextProvider(
       scoped_refptr<gpu::GpuChannelHost> gpu_channel);
 
 #if defined(OS_CHROMEOS)
@@ -87,6 +87,6 @@
   DISALLOW_COPY_AND_ASSIGN(Gpu);
 };
 
-}  // namespace ws
+}  // namespace viz
 
-#endif  // SERVICES_WS_PUBLIC_CPP_GPU_GPU_H_
+#endif  // SERVICES_VIZ_PUBLIC_CPP_GPU_GPU_H_
diff --git a/services/ws/public/cpp/tests/gpu_unittest.cc b/services/viz/public/cpp/gpu/gpu_unittest.cc
similarity index 98%
rename from services/ws/public/cpp/tests/gpu_unittest.cc
rename to services/viz/public/cpp/gpu/gpu_unittest.cc
index 553f093..87f302b 100644
--- a/services/ws/public/cpp/tests/gpu_unittest.cc
+++ b/services/viz/public/cpp/gpu/gpu_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "services/ws/public/cpp/gpu/gpu.h"
+#include "services/viz/public/cpp/gpu/gpu.h"
 
 #include "base/bind.h"
 #include "base/callback_helpers.h"
@@ -16,7 +16,7 @@
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace ws {
+namespace viz {
 
 namespace {
 
@@ -349,4 +349,4 @@
   EXPECT_EQ(0, counter);
 }
 
-}  // namespace ws
+}  // namespace viz
diff --git a/services/ws/public/cpp/gpu/shared_worker_context_provider_factory.cc b/services/viz/public/cpp/gpu/shared_worker_context_provider_factory.cc
similarity index 86%
rename from services/ws/public/cpp/gpu/shared_worker_context_provider_factory.cc
rename to services/viz/public/cpp/gpu/shared_worker_context_provider_factory.cc
index fcc123aa..e18196c0 100644
--- a/services/ws/public/cpp/gpu/shared_worker_context_provider_factory.cc
+++ b/services/viz/public/cpp/gpu/shared_worker_context_provider_factory.cc
@@ -2,23 +2,23 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "services/ws/public/cpp/gpu/shared_worker_context_provider_factory.h"
+#include "services/viz/public/cpp/gpu/shared_worker_context_provider_factory.h"
 
 #include "components/viz/common/gpu/raster_context_provider.h"
 #include "gpu/command_buffer/client/raster_interface.h"
 #include "gpu/command_buffer/common/context_result.h"
 #include "gpu/ipc/client/gpu_channel_host.h"
-#include "services/ws/public/cpp/gpu/context_provider_command_buffer.h"
+#include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
 #include "ui/base/ui_base_features.h"
 
-namespace ws {
+namespace viz {
 namespace {
 
-bool CheckWorkerContextLost(viz::RasterContextProvider* context_provider) {
+bool CheckWorkerContextLost(RasterContextProvider* context_provider) {
   if (!context_provider)
     return false;
 
-  viz::RasterContextProvider::ScopedRasterContextLock lock(context_provider);
+  RasterContextProvider::ScopedRasterContextLock lock(context_provider);
   return lock.RasterInterface()->GetGraphicsResetStatusKHR() != GL_NO_ERROR;
 }
 
@@ -28,7 +28,7 @@
     int32_t stream_id,
     gpu::SchedulingPriority priority,
     const GURL& identifying_url,
-    ws::command_buffer_metrics::ContextType context_type)
+    command_buffer_metrics::ContextType context_type)
     : stream_id_(stream_id),
       priority_(priority),
       identifying_url_(identifying_url),
@@ -68,7 +68,7 @@
   return result;
 }
 
-scoped_refptr<viz::RasterContextProvider>
+scoped_refptr<RasterContextProvider>
 SharedWorkerContextProviderFactory::CreateContextProvider(
     scoped_refptr<gpu::GpuChannelHost> gpu_channel_host,
     gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
@@ -79,7 +79,7 @@
     bool support_gles2_interface,
     bool support_raster_interface,
     bool support_grcontext,
-    ws::command_buffer_metrics::ContextType type) {
+    command_buffer_metrics::ContextType type) {
   DCHECK(gpu_channel_host);
 
   gpu::ContextCreationAttribs attributes;
@@ -99,10 +99,10 @@
 
   constexpr bool automatic_flushes = false;
 
-  return base::MakeRefCounted<ws::ContextProviderCommandBuffer>(
+  return base::MakeRefCounted<ContextProviderCommandBuffer>(
       std::move(gpu_channel_host), gpu_memory_buffer_manager, stream_id_,
       priority_, surface_handle, identifying_url_, automatic_flushes,
       support_locking, support_grcontext, memory_limits, attributes, type);
 }
 
-}  // namespace ws
+}  // namespace viz
diff --git a/services/ws/public/cpp/gpu/shared_worker_context_provider_factory.h b/services/viz/public/cpp/gpu/shared_worker_context_provider_factory.h
similarity index 73%
rename from services/ws/public/cpp/gpu/shared_worker_context_provider_factory.h
rename to services/viz/public/cpp/gpu/shared_worker_context_provider_factory.h
index d5139d20..5f8960a3 100644
--- a/services/ws/public/cpp/gpu/shared_worker_context_provider_factory.h
+++ b/services/viz/public/cpp/gpu/shared_worker_context_provider_factory.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 SERVICES_WS_PUBLIC_CPP_GPU_SHARED_WORKER_CONTEXT_PROVIDER_FACTORY_H_
-#define SERVICES_WS_PUBLIC_CPP_GPU_SHARED_WORKER_CONTEXT_PROVIDER_FACTORY_H_
+#ifndef SERVICES_VIZ_PUBLIC_CPP_GPU_SHARED_WORKER_CONTEXT_PROVIDER_FACTORY_H_
+#define SERVICES_VIZ_PUBLIC_CPP_GPU_SHARED_WORKER_CONTEXT_PROVIDER_FACTORY_H_
 
 #include <stdint.h>
 
@@ -21,24 +21,22 @@
 }  // namespace gpu
 
 namespace viz {
-class RasterContextProvider;
-}
-
-namespace ws {
 
 namespace command_buffer_metrics {
 enum class ContextType;
 }
 
+class RasterContextProvider;
+
 // SharedWorkerContextProviderFactory is responsible for creation, and owning
-// viz::RasterContextProvider.
+// RasterContextProvider.
 class SharedWorkerContextProviderFactory {
  public:
   SharedWorkerContextProviderFactory(
       int32_t stream_id,
       gpu::SchedulingPriority priority,
       const GURL& identifying_url,
-      ws::command_buffer_metrics::ContextType context_type);
+      command_buffer_metrics::ContextType context_type);
   ~SharedWorkerContextProviderFactory();
 
   // Drops the reference to |provider_|. This ensures the next time Validate()
@@ -52,10 +50,10 @@
       scoped_refptr<gpu::GpuChannelHost> gpu_channel_host,
       gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager);
 
-  scoped_refptr<viz::RasterContextProvider> provider() { return provider_; }
+  scoped_refptr<RasterContextProvider> provider() { return provider_; }
 
  private:
-  scoped_refptr<viz::RasterContextProvider> CreateContextProvider(
+  scoped_refptr<RasterContextProvider> CreateContextProvider(
       scoped_refptr<gpu::GpuChannelHost> gpu_channel_host,
       gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
       gpu::SurfaceHandle surface_handle,
@@ -65,17 +63,17 @@
       bool support_gles2_interface,
       bool support_raster_interface,
       bool support_grcontext,
-      ws::command_buffer_metrics::ContextType type);
+      command_buffer_metrics::ContextType type);
 
   const int32_t stream_id_;
   const gpu::SchedulingPriority priority_;
   const GURL identifying_url_;
-  const ws::command_buffer_metrics::ContextType context_type_;
-  scoped_refptr<viz::RasterContextProvider> provider_;
+  const command_buffer_metrics::ContextType context_type_;
+  scoped_refptr<RasterContextProvider> provider_;
 
   DISALLOW_COPY_AND_ASSIGN(SharedWorkerContextProviderFactory);
 };
 
-}  // namespace ws
+}  // namespace viz
 
-#endif  // SERVICES_WS_PUBLIC_CPP_GPU_SHARED_WORKER_CONTEXT_PROVIDER_FACTORY_H_
+#endif  // SERVICES_VIZ_PUBLIC_CPP_GPU_SHARED_WORKER_CONTEXT_PROVIDER_FACTORY_H_
diff --git a/services/viz/public/interfaces/BUILD.gn b/services/viz/public/interfaces/BUILD.gn
index def435b..0cfeb5f 100644
--- a/services/viz/public/interfaces/BUILD.gn
+++ b/services/viz/public/interfaces/BUILD.gn
@@ -17,6 +17,7 @@
     "compositing/filter_operations.mojom",
     "compositing/frame_deadline.mojom",
     "compositing/frame_sink_id.mojom",
+    "compositing/frame_timing_details.mojom",
     "compositing/local_surface_id.mojom",
     "compositing/local_surface_id_allocation.mojom",
     "compositing/paint_filter.mojom",
@@ -32,6 +33,7 @@
     "compositing/texture_releaser.mojom",
     "compositing/transferable_resource.mojom",
     "compositing/video_detector_observer.mojom",
+    "gpu.mojom",
     "hit_test/aggregated_hit_test_region.mojom",
     "hit_test/hit_test_region_list.mojom",
     "hit_test/input_target_client.mojom",
@@ -40,12 +42,17 @@
   public_deps = [
     ":constants",
     "//gpu/ipc/common:interfaces",
+    "//media/mojo/interfaces",
     "//mojo/public/mojom/base",
     "//skia/public/interfaces",
     "//ui/gfx/geometry/mojo",
     "//ui/gfx/mojo",
     "//ui/latency/mojo:interfaces",
   ]
+
+  if (is_chromeos) {
+    public_deps += [ "//components/chromeos_camera/common" ]
+  }
 }
 
 mojom("constants") {
diff --git a/services/viz/public/interfaces/compositing/compositor_frame_sink.mojom b/services/viz/public/interfaces/compositing/compositor_frame_sink.mojom
index 2c767f91..c75b69a 100644
--- a/services/viz/public/interfaces/compositing/compositor_frame_sink.mojom
+++ b/services/viz/public/interfaces/compositing/compositor_frame_sink.mojom
@@ -9,10 +9,10 @@
 import "services/viz/public/interfaces/compositing/begin_frame_args.mojom";
 import "services/viz/public/interfaces/compositing/compositor_frame.mojom";
 import "services/viz/public/interfaces/compositing/local_surface_id.mojom";
+import "services/viz/public/interfaces/compositing/frame_timing_details.mojom";
 import "services/viz/public/interfaces/compositing/returned_resource.mojom";
 import "services/viz/public/interfaces/hit_test/hit_test_region_list.mojom";
 import "ui/gfx/geometry/mojo/geometry.mojom";
-import "ui/gfx/mojo/presentation_feedback.mojom";
 import "gpu/ipc/common/mailbox.mojom";
 
 
@@ -90,7 +90,7 @@
 
   // Notification for the client to generate a CompositorFrame.
   OnBeginFrame(BeginFrameArgs args,
-               map<uint32, gfx.mojom.PresentationFeedback> presentations);
+               map<uint32, FrameTimingDetails> details);
 
   // Inform the client that OnBeginFrame may not be called for some time.
   OnBeginFramePausedChanged(bool paused);
diff --git a/services/viz/public/interfaces/compositing/frame_timing_details.mojom b/services/viz/public/interfaces/compositing/frame_timing_details.mojom
new file mode 100644
index 0000000..cc27dbb
--- /dev/null
+++ b/services/viz/public/interfaces/compositing/frame_timing_details.mojom
@@ -0,0 +1,12 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module viz.mojom;
+
+import "ui/gfx/mojo/presentation_feedback.mojom";
+
+// viz::FrameTimingDetails
+struct FrameTimingDetails {
+  gfx.mojom.PresentationFeedback presentation_feedback;
+};
diff --git a/services/ws/public/mojom/gpu.mojom b/services/viz/public/interfaces/gpu.mojom
similarity index 98%
rename from services/ws/public/mojom/gpu.mojom
rename to services/viz/public/interfaces/gpu.mojom
index 28b70c6..4385609 100644
--- a/services/ws/public/mojom/gpu.mojom
+++ b/services/viz/public/interfaces/gpu.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 ws.mojom;
+module viz.mojom;
 
 import "gpu/ipc/common/gpu_feature_info.mojom";
 import "gpu/ipc/common/gpu_info.mojom";
diff --git a/services/ws/gpu_host/BUILD.gn b/services/ws/gpu_host/BUILD.gn
deleted file mode 100644
index 2720968..0000000
--- a/services/ws/gpu_host/BUILD.gn
+++ /dev/null
@@ -1,109 +0,0 @@
-# Copyright 2018 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//media/gpu/args.gni")
-
-source_set("gpu_host") {
-  sources = [
-    "gpu_host.cc",
-    "gpu_host.h",
-    "gpu_host_delegate.h",
-  ]
-
-  deps = [
-    "//base",
-    "//components/discardable_memory/service",
-    "//components/viz/common",
-    "//components/viz/host",
-    "//components/viz/service/main",  # TODO(crbug/912221): Remove after GPU process split.
-    "//gpu/command_buffer/client",
-    "//gpu/command_buffer/client:gles2_interface",
-    "//gpu/command_buffer/service",
-    "//gpu/ipc/client",
-    "//gpu/ipc/common",
-    "//gpu/ipc/host",
-    "//gpu/ipc/service",  # TODO(crbug/912221): Remove after GPU process split.
-    "//media/gpu:buildflags",
-    "//mojo/public/cpp/bindings",
-    "//mojo/public/cpp/system",
-    "//services/service_manager/public/cpp",
-    "//ui/gfx",
-    "//ui/gl",
-  ]
-
-  public_deps = [
-    "//services/viz/privileged/interfaces",
-    "//services/viz/public/interfaces",
-    "//services/ws/public/mojom",
-  ]
-
-  if (use_vaapi) {
-    deps += [ "//media/gpu/vaapi" ]
-  }
-
-  # ui service should not depend on below components.
-  assert_no_deps = [
-    "//ash",
-    "//content/public/browser",
-    "//content/public/common",
-    "//ui/aura",
-    "//ui/views",
-  ]
-}
-
-source_set("test_support") {
-  testonly = true
-
-  sources = [
-    "gpu_host_test_api.cc",
-    "gpu_host_test_api.h",
-  ]
-
-  deps = [
-    ":gpu_host",
-    "//base",
-    "//components/viz/host",
-    "//components/viz/test:test_support",
-    "//services/viz/privileged/interfaces",
-  ]
-}
-
-source_set("tests") {
-  testonly = true
-
-  sources = [
-    "gpu_host_unittest.cc",
-  ]
-
-  deps = [
-    ":gpu_host",
-    ":test_support",
-    "//base",
-    "//base/test:test_config",
-    "//base/test:test_support",
-    "//components/discardable_memory/service",
-    "//components/viz/host",
-    "//components/viz/service",
-    "//components/viz/service/main",
-    "//components/viz/test:test_support",
-    "//gpu/ipc/client",
-    "//mojo/public/cpp/bindings:bindings",
-    "//services/service_manager/public/cpp",
-    "//services/service_manager/public/mojom",
-    "//services/ws/common",
-    "//services/ws/common:task_runner_test_base",
-    "//services/ws/public/mojom",
-    "//testing/gtest",
-    "//third_party/mesa_headers",
-    "//ui/aura",
-    "//ui/aura:test_support",
-    "//ui/events",
-    "//ui/gfx",
-    "//ui/gfx:test_support",
-    "//ui/gfx/geometry",
-    "//ui/gfx/geometry/mojo",
-    "//ui/gl",
-    "//ui/gl/init",
-  ]
-}
diff --git a/services/ws/gpu_host/DEPS b/services/ws/gpu_host/DEPS
deleted file mode 100644
index 8ffd047..0000000
--- a/services/ws/gpu_host/DEPS
+++ /dev/null
@@ -1,34 +0,0 @@
-include_rules = [
-  "+base",
-  "+components/viz/common",
-  "+components/viz/host",
-  "+components/viz/test",
-  "+gpu/command_buffer/client",
-  "+gpu/command_buffer/service/gpu_switches.h",
-  "+gpu/config",
-  "+gpu/ipc/client",
-  "+gpu/ipc/common",
-  "+gpu/ipc/host",
-  "+media/gpu",
-  "+mojo/public",
-  "+services/viz/privileged/interfaces",
-  "+services/viz/public/interfaces",
-  "+services/ws/public",
-  "+services/ws/gpu_host",
-  "+ui",
-]
-
-specific_include_rules = {
-  # TODO(crbug.com/912221): This goes away after the gpu process split in mash.
-  "gpu_host.cc": [
-    "+components/viz/service/main/viz_main_impl.h",
-    "+gpu/ipc/service/gpu_init.h",
-  ],
-  "gpu_host_unittest.cc": [
-    "+components/viz/service/gl/gpu_service_impl.h",
-    "+gpu/ipc/service/gpu_watchdog_thread.h",
-  ],
-  ".*_(unit|pixel|perf)test.*\.cc": [
-    "+components/viz/test",
-  ],
-}
diff --git a/services/ws/gpu_host/gpu_host.cc b/services/ws/gpu_host/gpu_host.cc
deleted file mode 100644
index bf0ba08..0000000
--- a/services/ws/gpu_host/gpu_host.cc
+++ /dev/null
@@ -1,300 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "services/ws/gpu_host/gpu_host.h"
-
-#include "base/bind.h"
-#include "base/command_line.h"
-#include "base/memory/shared_memory.h"
-#include "base/run_loop.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "components/discardable_memory/service/discardable_shared_memory_manager.h"
-#include "components/viz/common/frame_sinks/begin_frame_source.h"
-#include "components/viz/common/switches.h"
-#include "components/viz/host/gpu_client.h"
-#include "components/viz/host/gpu_client_delegate.h"
-#include "components/viz/host/host_gpu_memory_buffer_manager.h"
-#include "components/viz/service/main/viz_main_impl.h"
-#include "gpu/command_buffer/service/gpu_switches.h"
-#include "gpu/config/gpu_preferences.h"
-#include "gpu/ipc/client/gpu_channel_host.h"
-#include "gpu/ipc/common/gpu_memory_buffer_impl_shared_memory.h"
-#include "gpu/ipc/common/gpu_memory_buffer_support.h"
-#include "gpu/ipc/host/shader_disk_cache.h"
-#include "gpu/ipc/service/gpu_init.h"
-#include "media/gpu/buildflags.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
-#include "mojo/public/cpp/system/buffer.h"
-#include "mojo/public/cpp/system/platform_handle.h"
-#include "services/viz/privileged/interfaces/viz_main.mojom.h"
-#include "services/viz/public/interfaces/constants.mojom.h"
-#include "services/ws/gpu_host/gpu_host_delegate.h"
-#include "ui/base/ui_base_features.h"
-#include "ui/gfx/buffer_format_util.h"
-
-#if defined(OS_WIN)
-#include "ui/gfx/win/rendering_window_manager.h"
-#endif
-
-#if defined(OS_CHROMEOS)
-#include "services/ws/gpu_host/arc_gpu_client.h"
-#endif
-
-#if defined(OS_CHROMEOS) && BUILDFLAG(USE_VAAPI)
-#include "media/gpu/vaapi/vaapi_wrapper.h"
-#endif
-
-namespace ws {
-namespace gpu_host {
-
-namespace {
-
-// The client Id 1 is reserved for the frame sink manager.
-const int32_t kInternalGpuChannelClientId = 2;
-
-class GpuClientDelegate : public viz::GpuClientDelegate {
- public:
-  GpuClientDelegate(viz::GpuHostImpl* gpu_host_impl,
-                    viz::HostGpuMemoryBufferManager* gpu_memory_buffer_manager);
-  ~GpuClientDelegate() override;
-
-  // viz::GpuClientDelegate:
-  viz::GpuHostImpl* EnsureGpuHost() override;
-  viz::HostGpuMemoryBufferManager* GetGpuMemoryBufferManager() override;
-
- private:
-  viz::GpuHostImpl* gpu_host_impl_;
-  viz::HostGpuMemoryBufferManager* gpu_memory_buffer_manager_;
-
-  DISALLOW_COPY_AND_ASSIGN(GpuClientDelegate);
-};
-
-GpuClientDelegate::GpuClientDelegate(
-    viz::GpuHostImpl* gpu_host_impl,
-    viz::HostGpuMemoryBufferManager* gpu_memory_buffer_manager)
-    : gpu_host_impl_(gpu_host_impl),
-      gpu_memory_buffer_manager_(gpu_memory_buffer_manager) {}
-
-GpuClientDelegate::~GpuClientDelegate() = default;
-
-viz::GpuHostImpl* GpuClientDelegate::EnsureGpuHost() {
-  return gpu_host_impl_;
-}
-
-viz::HostGpuMemoryBufferManager*
-GpuClientDelegate::GetGpuMemoryBufferManager() {
-  return gpu_memory_buffer_manager_;
-}
-
-}  // namespace
-
-GpuHost::GpuHost(GpuHostDelegate* delegate,
-                 discardable_memory::DiscardableSharedMemoryManager*
-                     discardable_shared_memory_manager)
-    : delegate_(delegate),
-      discardable_shared_memory_manager_(discardable_shared_memory_manager),
-      next_client_id_(kInternalGpuChannelClientId + 1),
-      main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
-      gpu_thread_("GpuThread") {
-  DCHECK(discardable_shared_memory_manager_);
-
-  viz::GpuHostImpl::InitFontRenderParams(
-      gfx::GetFontRenderParams(gfx::FontRenderParamsQuery(), nullptr));
-
-  viz::mojom::VizMainPtr viz_main_ptr;
-  gpu_thread_.Start();
-  gpu_thread_.task_runner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&GpuHost::InitializeVizMain, base::Unretained(this),
-                     base::Passed(MakeRequest(&viz_main_ptr))));
-
-  viz::GpuHostImpl::InitParams params;
-  params.restart_id = viz::BeginFrameSource::kNotRestartableId + 1;
-  params.in_process = true;
-  params.disable_gpu_shader_disk_cache =
-      base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kDisableGpuShaderDiskCache);
-  params.deadline_to_synchronize_surfaces =
-      switches::GetDeadlineToSynchronizeSurfaces();
-  params.main_thread_task_runner = main_thread_task_runner_;
-  gpu_host_impl_ = std::make_unique<viz::GpuHostImpl>(
-      this, std::make_unique<viz::VizMainWrapper>(std::move(viz_main_ptr)),
-      std::move(params));
-
-#if defined(OS_WIN)
-  // For OS_WIN the process id for GPU is needed. Using GetCurrentProcessId()
-  // only works with in-process GPU, which is fine because GpuHost isn't used
-  // outside of tests.
-  gpu_host_impl_->OnProcessLaunched(::GetCurrentProcessId());
-#endif
-
-  gpu_memory_buffer_manager_ =
-      std::make_unique<viz::HostGpuMemoryBufferManager>(
-          base::BindRepeating(
-              [](viz::mojom::GpuService* gpu_service,
-                 base::OnceClosure connection_error_handler) {
-                return gpu_service;
-              },
-              gpu_host_impl_->gpu_service()),
-          next_client_id_++, std::make_unique<gpu::GpuMemoryBufferSupport>(),
-          main_thread_task_runner_);
-
-  shader_cache_factory_ = std::make_unique<gpu::ShaderCacheFactory>();
-}
-
-GpuHost::~GpuHost() {
-  // TODO(crbug.com/912221): This goes away after the gpu process split in mash.
-  if (gpu_thread_.IsRunning()) {
-    // Stop() will return after |viz_main_impl_| has been destroyed.
-    gpu_thread_.task_runner()->PostTask(
-        FROM_HERE,
-        base::BindOnce(&GpuHost::DestroyVizMain, base::Unretained(this)));
-    gpu_thread_.Stop();
-  }
-
-  viz::GpuHostImpl::ResetFontRenderParams();
-}
-
-void GpuHost::CreateFrameSinkManager(
-    viz::mojom::FrameSinkManagerRequest request,
-    viz::mojom::FrameSinkManagerClientPtrInfo client) {
-  gpu_host_impl_->ConnectFrameSinkManager(std::move(request),
-                                          std::move(client));
-}
-
-void GpuHost::Shutdown() {
-  gpu_clients_.clear();
-  gpu_host_impl_.reset();
-}
-
-void GpuHost::Add(mojom::GpuRequest request) {
-  const int client_id = next_client_id_++;
-  const uint64_t client_tracing_id = 0;
-  auto client = std::make_unique<viz::GpuClient>(
-      std::make_unique<GpuClientDelegate>(gpu_host_impl_.get(),
-                                          gpu_memory_buffer_manager_.get()),
-      client_id, client_tracing_id, main_thread_task_runner_);
-  client->Add(std::move(request));
-  gpu_clients_.push_back(std::move(client));
-}
-
-#if defined(USE_OZONE)
-void GpuHost::BindOzoneGpuInterface(
-    const std::string& interface_name,
-    mojo::ScopedMessagePipeHandle interface_pipe) {
-  // This is only used when viz is run in-process.
-  DCHECK(gpu_thread_.IsRunning());
-
-  // Interfaces should be bound on gpu thread.
-  if (!gpu_thread_.task_runner()->BelongsToCurrentThread()) {
-    gpu_thread_.task_runner()->PostTask(
-        FROM_HERE,
-        base::BindOnce(&GpuHost::BindOzoneGpuInterface, base::Unretained(this),
-                       interface_name, std::move(interface_pipe)));
-    return;
-  }
-  DCHECK(viz_main_impl_);
-  viz_main_impl_->BindInterface(interface_name, std::move(interface_pipe));
-}
-#endif  // defined(USE_OZONE)
-
-void GpuHost::OnBadMessageFromGpu() {
-  // TODO(sad): Received some unexpected message from the gpu process. We
-  // should kill the process and restart it.
-  NOTIMPLEMENTED();
-}
-
-void GpuHost::InitializeVizMain(viz::mojom::VizMainRequest request) {
-  gpu::GpuPreferences gpu_preferences;
-  gpu_preferences.gpu_program_cache_size =
-      gpu::ShaderDiskCache::CacheSizeBytes();
-  gpu_preferences.texture_target_exception_list =
-      gpu::CreateBufferUsageAndFormatExceptionList();
-
-#if defined(OS_CHROMEOS) && BUILDFLAG(USE_VAAPI)
-  // Initialize media codec. The UI service is running in a privileged process.
-  // We don't need care when to initialize media codec.
-  media::VaapiWrapper::PreSandboxInitialization();
-#endif
-
-  auto gpu_init = std::make_unique<gpu::GpuInit>();
-  gpu_init->InitializeInProcess(base::CommandLine::ForCurrentProcess(),
-                                gpu_preferences);
-
-  viz::VizMainImpl::ExternalDependencies deps;
-  deps.create_display_compositor = true;
-  viz_main_impl_ = std::make_unique<viz::VizMainImpl>(nullptr, std::move(deps),
-                                                      std::move(gpu_init));
-  viz_main_impl_->Bind(std::move(request));
-}
-
-void GpuHost::DestroyVizMain() {
-  DCHECK(viz_main_impl_);
-  viz_main_impl_.reset();
-}
-
-gpu::GPUInfo GpuHost::GetGPUInfo() const {
-  return gpu_info_;
-}
-
-gpu::GpuFeatureInfo GpuHost::GetGpuFeatureInfo() const {
-  return gpu_feature_info_;
-}
-
-void GpuHost::DidInitialize(
-    const gpu::GPUInfo& gpu_info,
-    const gpu::GpuFeatureInfo& gpu_feature_info,
-    const base::Optional<gpu::GPUInfo>& gpu_info_for_hardware_gpu,
-    const base::Optional<gpu::GpuFeatureInfo>&
-        gpu_feature_info_for_hardware_gpu) {
-  gpu_info_ = gpu_info;
-  gpu_feature_info_ = gpu_feature_info;
-  delegate_->OnGpuServiceInitialized();
-}
-
-void GpuHost::DidFailInitialize() {}
-
-void GpuHost::DidCreateContextSuccessfully() {}
-
-void GpuHost::BlockDomainFrom3DAPIs(const GURL& url, gpu::DomainGuilt guilt) {}
-
-void GpuHost::DisableGpuCompositing() {}
-
-bool GpuHost::GpuAccessAllowed() const {
-  return true;
-}
-
-gpu::ShaderCacheFactory* GpuHost::GetShaderCacheFactory() {
-  return shader_cache_factory_.get();
-}
-
-void GpuHost::RecordLogMessage(int32_t severity,
-                               const std::string& header,
-                               const std::string& message) {}
-
-void GpuHost::BindDiscardableMemoryRequest(
-    discardable_memory::mojom::DiscardableSharedMemoryManagerRequest request) {
-  service_manager::BindSourceInfo source_info;
-  discardable_shared_memory_manager_->Bind(std::move(request), source_info);
-}
-
-void GpuHost::BindInterface(const std::string& interface_name,
-                            mojo::ScopedMessagePipeHandle interface_pipe) {
-  NOTREACHED();
-}
-
-void GpuHost::RunService(
-    const std::string& service_name,
-    mojo::PendingReceiver<service_manager::mojom::Service> receiver) {
-  NOTREACHED();
-}
-
-#if defined(USE_OZONE)
-void GpuHost::TerminateGpuProcess(const std::string& message) {}
-
-void GpuHost::SendGpuProcessMessage(IPC::Message* message) {}
-#endif
-
-}  // namespace gpu_host
-}  // namespace ws
diff --git a/services/ws/gpu_host/gpu_host.h b/services/ws/gpu_host/gpu_host.h
deleted file mode 100644
index 033e20f..0000000
--- a/services/ws/gpu_host/gpu_host.h
+++ /dev/null
@@ -1,129 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef SERVICES_WS_GPU_HOST_GPU_HOST_H_
-#define SERVICES_WS_GPU_HOST_GPU_HOST_H_
-
-#include "base/threading/thread.h"
-#include "build/build_config.h"
-#include "components/viz/host/gpu_host_impl.h"
-#include "gpu/config/gpu_feature_info.h"
-#include "gpu/config/gpu_info.h"
-#include "mojo/public/cpp/bindings/strong_binding_set.h"
-#include "services/ws/public/mojom/gpu.mojom.h"
-
-namespace base {
-class SingleThreadTaskRunner;
-}
-
-namespace discardable_memory {
-class DiscardableSharedMemoryManager;
-}
-
-namespace gpu {
-class ShaderCacheFactory;
-}
-
-namespace viz {
-class GpuClient;
-class GpuHostImpl;
-class HostGpuMemoryBufferManager;
-class VizMainImpl;
-}
-
-namespace ws {
-namespace gpu_host {
-class GpuHostDelegate;
-
-// GpuHost sets up connection from clients to the real service implementation in
-// the GPU process.
-class GpuHost : public viz::GpuHostImpl::Delegate {
- public:
-  GpuHost(GpuHostDelegate* delegate,
-          discardable_memory::DiscardableSharedMemoryManager*
-              discardable_shared_memory_manager);
-  ~GpuHost() override;
-
-  void CreateFrameSinkManager(viz::mojom::FrameSinkManagerRequest request,
-                              viz::mojom::FrameSinkManagerClientPtrInfo client);
-
-  void Shutdown();
-
-  void Add(mojom::GpuRequest request);
-
-#if defined(USE_OZONE)
-  void BindOzoneGpuInterface(const std::string& interface_name,
-                             mojo::ScopedMessagePipeHandle interface_pipe);
-#endif
-
- private:
-  friend class GpuHostTestApi;
-
-  void OnBadMessageFromGpu();
-
-  // TODO(crbug.com/912221): This goes away after the gpu process split in mash.
-  void InitializeVizMain(viz::mojom::VizMainRequest request);
-  void DestroyVizMain();
-
-  // viz::GpuHostImpl::Delegate:
-  gpu::GPUInfo GetGPUInfo() const override;
-  gpu::GpuFeatureInfo GetGpuFeatureInfo() const override;
-  void DidInitialize(
-      const gpu::GPUInfo& gpu_info,
-      const gpu::GpuFeatureInfo& gpu_feature_info,
-      const base::Optional<gpu::GPUInfo>& gpu_info_for_hardware_gpu,
-      const base::Optional<gpu::GpuFeatureInfo>&
-          gpu_feature_info_for_hardware_gpu) override;
-  void DidFailInitialize() override;
-  void DidCreateContextSuccessfully() override;
-  void BlockDomainFrom3DAPIs(const GURL& url, gpu::DomainGuilt guilt) override;
-  void DisableGpuCompositing() override;
-  bool GpuAccessAllowed() const override;
-  gpu::ShaderCacheFactory* GetShaderCacheFactory() override;
-  void RecordLogMessage(int32_t severity,
-                        const std::string& header,
-                        const std::string& message) override;
-  void BindDiscardableMemoryRequest(
-      discardable_memory::mojom::DiscardableSharedMemoryManagerRequest request)
-      override;
-  void BindInterface(const std::string& interface_name,
-                     mojo::ScopedMessagePipeHandle interface_pipe) override;
-  void RunService(
-      const std::string& service_name,
-      mojo::PendingReceiver<service_manager::mojom::Service> receiver) override;
-#if defined(USE_OZONE)
-  void TerminateGpuProcess(const std::string& message) override;
-  void SendGpuProcessMessage(IPC::Message* message) override;
-#endif
-
-  GpuHostDelegate* const delegate_;
-  discardable_memory::DiscardableSharedMemoryManager*
-      discardable_shared_memory_manager_;
-  int32_t next_client_id_;
-  scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
-  std::unique_ptr<viz::GpuHostImpl> gpu_host_impl_;
-  gpu::GPUInfo gpu_info_;
-  gpu::GpuFeatureInfo gpu_feature_info_;
-
-  std::unique_ptr<viz::HostGpuMemoryBufferManager> gpu_memory_buffer_manager_;
-
-  std::unique_ptr<gpu::ShaderCacheFactory> shader_cache_factory_;
-
-  std::vector<std::unique_ptr<viz::GpuClient>> gpu_clients_;
-
-  // TODO(crbug.com/912221): This goes away after the gpu process split in mash.
-  base::Thread gpu_thread_;
-  std::unique_ptr<viz::VizMainImpl> viz_main_impl_;
-
-#if defined(OS_CHROMEOS)
-  mojo::StrongBindingSet<mojom::ArcGpu> arc_gpu_bindings_;
-#endif  // defined(OS_CHROMEOS)
-
-  DISALLOW_COPY_AND_ASSIGN(GpuHost);
-};
-
-}  // namespace gpu_host
-}  // namespace ws
-
-#endif  // SERVICES_WS_GPU_HOST_GPU_HOST_H_
diff --git a/services/ws/gpu_host/gpu_host_delegate.h b/services/ws/gpu_host/gpu_host_delegate.h
deleted file mode 100644
index 3886fc4..0000000
--- a/services/ws/gpu_host/gpu_host_delegate.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef SERVICES_WS_GPU_HOST_GPU_HOST_DELEGATE_H_
-#define SERVICES_WS_GPU_HOST_GPU_HOST_DELEGATE_H_
-
-#include "base/memory/ref_counted.h"
-
-namespace ws {
-namespace gpu_host {
-
-class GpuHostDelegate {
- public:
-  virtual ~GpuHostDelegate() {}
-
-  virtual void OnGpuServiceInitialized() = 0;
-};
-
-}  // namespace gpu_host
-}  // namespace ws
-
-#endif  // SERVICES_WS_GPU_HOST_GPU_HOST_DELEGATE_H_
diff --git a/services/ws/gpu_host/gpu_host_test_api.cc b/services/ws/gpu_host/gpu_host_test_api.cc
deleted file mode 100644
index a89f9bf..0000000
--- a/services/ws/gpu_host/gpu_host_test_api.cc
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "services/ws/gpu_host/gpu_host_test_api.h"
-
-#include <algorithm>
-
-#include "components/viz/host/gpu_client.h"
-#include "components/viz/test/gpu_host_impl_test_api.h"
-#include "services/ws/gpu_host/gpu_host.h"
-
-namespace ws {
-namespace gpu_host {
-
-GpuHostTestApi::GpuHostTestApi(GpuHost* gpu_host) : gpu_host_(gpu_host) {}
-
-GpuHostTestApi::~GpuHostTestApi() = default;
-
-void GpuHostTestApi::SetGpuService(viz::mojom::GpuServicePtr gpu_service) {
-  return viz::GpuHostImplTestApi(gpu_host_->gpu_host_impl_.get())
-      .SetGpuService(std::move(gpu_service));
-}
-
-base::WeakPtr<viz::GpuClient> GpuHostTestApi::GetLastGpuClient() {
-  if (gpu_host_->gpu_clients_.empty())
-    return nullptr;
-  return gpu_host_->gpu_clients_.back()->GetWeakPtr();
-}
-
-}  // namespace gpu_host
-}  // namespace ws
diff --git a/services/ws/gpu_host/gpu_host_test_api.h b/services/ws/gpu_host/gpu_host_test_api.h
deleted file mode 100644
index c4a5f60..0000000
--- a/services/ws/gpu_host/gpu_host_test_api.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef SERVICES_WS_GPU_HOST_GPU_HOST_TEST_API_H_
-#define SERVICES_WS_GPU_HOST_GPU_HOST_TEST_API_H_
-
-#include "base/memory/weak_ptr.h"
-#include "services/viz/privileged/interfaces/gl/gpu_service.mojom.h"
-
-namespace viz {
-class GpuClient;
-}
-
-namespace ws {
-namespace gpu_host {
-class GpuHost;
-
-class GpuHostTestApi {
- public:
-  GpuHostTestApi(GpuHost* gpu_host);
-  ~GpuHostTestApi();
-
-  void SetGpuService(viz::mojom::GpuServicePtr gpu_service);
-  base::WeakPtr<viz::GpuClient> GetLastGpuClient();
-
- private:
-  GpuHost* gpu_host_;
-
-  DISALLOW_COPY_AND_ASSIGN(GpuHostTestApi);
-};
-
-}  // namespace gpu_host
-}  // namespace ws
-
-#endif  // SERVICES_WS_GPU_HOST_GPU_HOST_TEST_API_H_
diff --git a/services/ws/gpu_host/gpu_host_unittest.cc b/services/ws/gpu_host/gpu_host_unittest.cc
deleted file mode 100644
index dba606b..0000000
--- a/services/ws/gpu_host/gpu_host_unittest.cc
+++ /dev/null
@@ -1,205 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "services/ws/gpu_host/gpu_host.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "base/single_thread_task_runner.h"
-#include "base/test/scoped_task_environment.h"
-#include "components/discardable_memory/service/discardable_shared_memory_manager.h"
-#include "components/viz/host/gpu_client.h"
-#include "components/viz/service/gl/gpu_service_impl.h"
-#include "components/viz/test/gpu_host_impl_test_api.h"
-#include "gpu/config/gpu_info.h"
-#include "gpu/ipc/service/gpu_watchdog_thread.h"
-#include "services/ws/gpu_host/gpu_host_delegate.h"
-#include "services/ws/gpu_host/gpu_host_test_api.h"
-#include "services/ws/public/mojom/gpu.mojom.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gl/init/gl_factory.h"
-
-namespace ws {
-namespace gpu_host {
-namespace test {
-namespace {
-
-// No-opt implementation of GpuHostDelegate.
-class TestGpuHostDelegate : public GpuHostDelegate {
- public:
-  TestGpuHostDelegate() {}
-  ~TestGpuHostDelegate() override {}
-
-  // GpuHostDelegate:
-  void OnGpuServiceInitialized() override {}
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(TestGpuHostDelegate);
-};
-
-// Test implementation of GpuService. For testing behaviour of calls made by
-// viz::GpuClient.
-class TestGpuService : public viz::GpuServiceImpl {
- public:
-  explicit TestGpuService(
-      scoped_refptr<base::SingleThreadTaskRunner> io_runner);
-  ~TestGpuService() override {}
-
-  // viz::GpuServiceImpl:
-  void EstablishGpuChannel(int32_t client_id,
-                           uint64_t client_tracing_id,
-                           bool is_gpu_host,
-                           bool cache_shaders_on_disk,
-                           EstablishGpuChannelCallback callback) override;
-
-  int channel_requests() const { return channel_requests_; }
-
- private:
-  int channel_requests_ = 0;
-
-  DISALLOW_COPY_AND_ASSIGN(TestGpuService);
-};
-
-TestGpuService::TestGpuService(
-    scoped_refptr<base::SingleThreadTaskRunner> io_runner)
-    : GpuServiceImpl(gpu::GPUInfo(),
-                     nullptr /* watchdog_thread */,
-                     std::move(io_runner),
-                     gpu::GpuFeatureInfo(),
-                     gpu::GpuPreferences(),
-                     base::nullopt,
-                     base::nullopt,
-                     nullptr /* vulkan_implementation */,
-                     /*exit_callback=*/base::DoNothing()) {}
-
-void TestGpuService::EstablishGpuChannel(int32_t client_id,
-                                         uint64_t client_tracing_id,
-                                         bool is_gpu_host,
-                                         bool cache_shaders_on_disk,
-                                         EstablishGpuChannelCallback callback) {
-  channel_requests_++;
-  viz::GpuServiceImpl::EstablishGpuChannel(client_id, client_tracing_id,
-                                           is_gpu_host, cache_shaders_on_disk,
-                                           std::move(callback));
-}
-
-}  // namespace
-
-class GpuHostTest : public testing::Test {
- public:
-  GpuHostTest() : io_thread_("IOThread") {
-    CHECK(io_thread_.Start());
-    gpu_service_ = std::make_unique<TestGpuService>(io_thread_.task_runner());
-  }
-  ~GpuHostTest() override {
-    gpu_service_ = nullptr;
-    io_thread_.Stop();
-  }
-
-  base::WeakPtr<viz::GpuClient> AddGpuClient();
-  void DestroyHost();
-  void ShutdownHost();
-
-  // testing::Test
-  void SetUp() override;
-  void TearDown() override;
-
-  // Flushes |io_thread_| tasks and returns the number of channel requests.
-  int GetChannelRequests();
-
- private:
-  base::test::ScopedTaskEnvironment scoped_task_environment_;
-
-  base::Thread io_thread_;
-  TestGpuHostDelegate gpu_host_delegate_;
-  discardable_memory::DiscardableSharedMemoryManager
-      discardable_memory_manager_;
-  std::unique_ptr<TestGpuService> gpu_service_;
-  std::unique_ptr<GpuHost> gpu_host_;
-
-  DISALLOW_COPY_AND_ASSIGN(GpuHostTest);
-};
-
-base::WeakPtr<viz::GpuClient> GpuHostTest::AddGpuClient() {
-  gpu_host_->Add(mojom::GpuRequest());
-  return GpuHostTestApi(gpu_host_.get()).GetLastGpuClient();
-}
-
-void GpuHostTest::DestroyHost() {
-  gpu_host_.reset();
-}
-
-void GpuHostTest::ShutdownHost() {
-  gpu_host_->Shutdown();
-}
-
-void GpuHostTest::SetUp() {
-  testing::Test::SetUp();
-  gpu_host_ = std::make_unique<GpuHost>(&gpu_host_delegate_,
-                                        &discardable_memory_manager_);
-  viz::mojom::GpuServicePtr gpu_service_ptr;
-  gpu_service_->Bind(mojo::MakeRequest(&gpu_service_ptr));
-  GpuHostTestApi(gpu_host_.get()).SetGpuService(std::move(gpu_service_ptr));
-}
-
-void GpuHostTest::TearDown() {
-  gpu_host_ = nullptr;
-  gl::init::ShutdownGL(false);
-  testing::Test::TearDown();
-}
-
-int GpuHostTest::GetChannelRequests() {
-  io_thread_.FlushForTesting();
-  return gpu_service_->channel_requests();
-}
-
-// Tests to verify, that if a GpuHost is deleted before viz::GpuClient receives
-// a callback, that viz::GpuClient is torn down and does not attempt to use
-// GpuInfo after deletion. This should not crash on asan-builds.
-TEST_F(GpuHostTest, GpuClientDestructionOrder) {
-  base::WeakPtr<viz::GpuClient> client_ref = AddGpuClient();
-  EXPECT_NE(nullptr, client_ref);
-  DestroyHost();
-  EXPECT_EQ(nullptr, client_ref);
-}
-
-TEST_F(GpuHostTest, GpuClientDestroyedWhileChannelRequestInFlight) {
-  base::WeakPtr<viz::GpuClient> client_ref = AddGpuClient();
-  mojom::Gpu* gpu = client_ref.get();
-  bool callback_called = false;
-  gpu->EstablishGpuChannel(
-      base::Bind([](bool* callback_called, int, mojo::ScopedMessagePipeHandle,
-                    const gpu::GPUInfo&,
-                    const gpu::GpuFeatureInfo&) { *callback_called = true; },
-                 &callback_called));
-  EXPECT_FALSE(callback_called);
-  DestroyHost();
-  EXPECT_TRUE(callback_called);
-}
-
-// Verifies that shutting down GpuHost while a channel request is in flight does
-// not retry that request.
-TEST_F(GpuHostTest, GpuHostShutdownWhileChannelRequestInFlight) {
-  base::WeakPtr<viz::GpuClient> client_ref = AddGpuClient();
-  mojom::Gpu* gpu = client_ref.get();
-
-  // Initially, there should be no channel requests.
-  EXPECT_EQ(0, GetChannelRequests());
-
-  // Send a channel request and verfiy it is received by gpu service.
-  gpu->EstablishGpuChannel(base::DoNothing());
-  EXPECT_EQ(1, GetChannelRequests());
-
-  // Shutting down host should not retry the pending channel request.
-  ShutdownHost();
-  EXPECT_EQ(1, GetChannelRequests());
-}
-
-}  // namespace test
-}  // namespace gpu_host
-}  // namespace ws
diff --git a/services/ws/public/cpp/gpu/DEPS b/services/ws/public/cpp/gpu/DEPS
deleted file mode 100644
index a7fb2a5..0000000
--- a/services/ws/public/cpp/gpu/DEPS
+++ /dev/null
@@ -1,3 +0,0 @@
-include_rules = [
-  "-services/ws/common",
-]
diff --git a/services/ws/public/cpp/host/BUILD.gn b/services/ws/public/cpp/host/BUILD.gn
deleted file mode 100644
index 113afe0..0000000
--- a/services/ws/public/cpp/host/BUILD.gn
+++ /dev/null
@@ -1,13 +0,0 @@
-# Copyright 2018 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-source_set("host") {
-  sources = [
-    "gpu_interface_provider.h",
-  ]
-
-  deps = [
-    "//services/service_manager/public/cpp",
-  ]
-}
diff --git a/services/ws/public/cpp/host/gpu_interface_provider.h b/services/ws/public/cpp/host/gpu_interface_provider.h
deleted file mode 100644
index 68a2ecf0..0000000
--- a/services/ws/public/cpp/host/gpu_interface_provider.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef SERVICES_WS_PUBLIC_CPP_HOST_GPU_INTERFACE_PROVIDER_H_
-#define SERVICES_WS_PUBLIC_CPP_HOST_GPU_INTERFACE_PROVIDER_H_
-
-#include <string>
-
-#include "services/service_manager/public/cpp/binder_registry.h"
-
-#if defined(USE_OZONE)
-#include "mojo/public/cpp/system/message_pipe.h"
-#endif
-
-namespace ws {
-
-// GpuInterfaceProvider is responsible for providing the Gpu related interfaces.
-// The implementation of these varies depending upon where the WindowService is
-// hosted.
-class GpuInterfaceProvider {
- public:
-  virtual ~GpuInterfaceProvider() {}
-
-  // Registers the gpu-related interfaces, specifically
-  // discardable_memory::mojom::DiscardableSharedMemoryManagerRequest and
-  // mojom::GpuRequest.
-  virtual void RegisterGpuInterfaces(
-      service_manager::BinderRegistry* registry) = 0;
-
-#if defined(USE_OZONE)
-  // Binds a gpu-related interface needed by Ozone.
-  virtual void BindOzoneGpuInterface(const std::string& interface_name,
-                                     mojo::ScopedMessagePipeHandle handle) = 0;
-#endif
-};
-
-}  // namespace ws
-
-#endif  // SERVICES_WS_PUBLIC_CPP_HOST_GPU_INTERFACE_PROVIDER_H_
diff --git a/services/ws/public/cpp/tests/BUILD.gn b/services/ws/public/cpp/tests/BUILD.gn
deleted file mode 100644
index 1ce9c7a..0000000
--- a/services/ws/public/cpp/tests/BUILD.gn
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright 2014 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("//build/config/ui.gni")
-import("//testing/test.gni")
-
-source_set("tests") {
-  testonly = true
-
-  sources = [
-    "gpu_unittest.cc",
-  ]
-
-  deps = [
-    "//base",
-    "//base/test:test_support",
-    "//mojo/core/embedder",
-    "//mojo/public/cpp/system",
-    "//services/service_manager/public/cpp",
-    "//services/ws/public/cpp/gpu",
-    "//testing/gtest",
-    "//ui/gfx:test_support",
-    "//ui/gfx/geometry",
-    "//ui/gfx/geometry/mojo",
-  ]
-
-  if (use_x11) {
-    deps += [ "//ui/gfx/x" ]
-  }
-}
diff --git a/services/ws/public/cpp/tests/OWNERS b/services/ws/public/cpp/tests/OWNERS
deleted file mode 100644
index a166098..0000000
--- a/services/ws/public/cpp/tests/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-per-file *_type_converter*.*=set noparent
-per-file *_type_converter*.*=file://ipc/SECURITY_OWNERS
diff --git a/services/ws/public/mojom/BUILD.gn b/services/ws/public/mojom/BUILD.gn
index f19658b..0841445 100644
--- a/services/ws/public/mojom/BUILD.gn
+++ b/services/ws/public/mojom/BUILD.gn
@@ -5,30 +5,6 @@
 import("//mojo/public/tools/bindings/mojom.gni")
 import("//testing/test.gni")
 
-mojom("mojom") {
-  sources = [
-    "gpu.mojom",
-  ]
-
-  import_dirs = [
-    get_path_info("../../../..", "abspath"),
-    "//mojo/services",
-  ]
-
-  public_deps = [
-    ":constants",
-    "//gpu/ipc/common:interfaces",
-    "//media/mojo/interfaces",
-    "//mojo/public/mojom/base",
-    "//ui/gfx/geometry/mojo",
-    "//ui/gfx/mojo",
-  ]
-
-  if (is_chromeos) {
-    public_deps += [ "//components/chromeos_camera/common" ]
-  }
-}
-
 mojom("constants") {
   sources = [
     "constants.mojom",
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 080fd11..1f43f44 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -25910,8 +25910,8 @@
     "gtest_tests": [
       {
         "args": [
-          "--build-revision",
-          "${got_revision}"
+          "--build-revision=${got_revision}",
+          "--enable-logging"
         ],
         "experiment_percentage": 100,
         "merge": {
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index 71b07a1..fa60a29df 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -640,7 +640,7 @@
       "../../chrome/test/chromedriver/test/run_webdriver_tests.py",
       "-v",
       "--chromedriver=chromedriver",
-      "--isolated-script-test-output=${ISOLATED_OUTDIR}/results.json",
+      "--output-dir=${ISOLATED_OUTDIR}",
       "--test-path=../../third_party/blink/web_tests/external/wpt/webdriver/tests/",
     ],
   },
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 144b6e4..688e281 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -4126,8 +4126,8 @@
       'pixel_browser_tests': {
         'name': 'pixel_browser_tests',
         'args': [
-          '--build-revision',
-          '${got_revision}',
+          '--build-revision=${got_revision}',
+          '--enable-logging',
         ],
         'experiment_percentage': 100,
         'swarming': {
diff --git a/testing/scripts/run_performance_tests.py b/testing/scripts/run_performance_tests.py
index 19bc7b4..b99d4ad 100755
--- a/testing/scripts/run_performance_tests.py
+++ b/testing/scripts/run_performance_tests.py
@@ -479,19 +479,9 @@
     elif options.test_shard_map_filename:
       # First determine what shard we are running on to know how to
       # index into the bot map to get list of telemetry benchmarks to run.
-      total_shards = None
       shard_index = None
       shard_map_path = os.path.join(SHARD_MAPS_DIRECTORY,
                                     options.test_shard_map_filename)
-      env = os.environ.copy()
-      if 'GTEST_TOTAL_SHARDS' in env:
-        total_shards = env['GTEST_TOTAL_SHARDS']
-      if 'GTEST_SHARD_INDEX' in env:
-        shard_index = env['GTEST_SHARD_INDEX']
-      if not total_shards or not shard_index:
-        raise Exception(
-            'Sharded Telemetry perf tests must either specify --benchmarks '
-            'list or have shard indicator environment variables present.')
       # Copy sharding map file to isolated_out_dir so that the merge script
       # can collect it later.
       # TODO(crouleau): Move this step over to merge script
@@ -501,6 +491,19 @@
           os.path.join(isolated_out_dir, 'benchmarks_shard_map.json'))
       with open(shard_map_path) as f:
         shard_map = json.load(f)
+      env = os.environ.copy()
+      if 'GTEST_SHARD_INDEX' in env:
+        shard_index = env['GTEST_SHARD_INDEX']
+      # TODO(crbug.com/972844): shard environment variables are not specified
+      # for single-shard shard runs.
+      if not shard_index:
+        shard_map_has_multiple_shards = bool(shard_map.get('1', False))
+        if not shard_map_has_multiple_shards:
+          shard_index = '0'
+      if not shard_index:
+        raise Exception(
+            'Sharded Telemetry perf tests must either specify --benchmarks '
+            'list or have GTEST_SHARD_INDEX environment variable present.')
       benchmarks_and_stories = shard_map[shard_index]['benchmarks']
 
       for benchmark, stories in benchmarks_and_stories.iteritems():
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 0f444d5..49f8ee5 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -2371,7 +2371,7 @@
             ]
         }
     ],
-    "GwpAsanPartitionAlloc": [
+    "GwpAsanPartitionAllocLaunch": [
         {
             "platforms": [
                 "mac",
@@ -4973,6 +4973,25 @@
             ]
         }
     ],
+    "SpellingServiceRestEndpoint": [
+        {
+            "platforms": [
+                "android",
+                "chromeos",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "SpellingServiceRestApi"
+                    ]
+                }
+            ]
+        }
+    ],
     "SqlSkipPreload": [
         {
             "platforms": [
diff --git a/third_party/android_crazy_linker/BUILD.gn b/third_party/android_crazy_linker/BUILD.gn
index a7c21e02..d09d573 100644
--- a/third_party/android_crazy_linker/BUILD.gn
+++ b/third_party/android_crazy_linker/BUILD.gn
@@ -29,12 +29,15 @@
       ":crazy_linker_test_dl_wrappers",
       ":crazy_linker_test_dl_wrappers_recursive",
       ":crazy_linker_test_dl_wrappers_valid_handles",
+      ":crazy_linker_test_dl_wrappers_with_android_dlopen_ext",
       ":crazy_linker_test_dl_wrappers_with_system_handle",
       ":crazy_linker_test_jni_hooks",
       ":crazy_linker_test_load_library",
       ":crazy_linker_test_load_library_depends",
+      ":crazy_linker_test_load_library_with_fd",
       ":crazy_linker_test_load_library_with_gnu_hash_table",
       ":crazy_linker_test_load_library_with_relr_relocations",
+      ":crazy_linker_test_load_library_with_reserved_map",
       ":crazy_linker_test_relocated_shared_relro",
       ":crazy_linker_test_search_path_list",
       ":crazy_linker_test_shared_relro",
@@ -85,6 +88,7 @@
         "src/src/crazy_linker_library_view.h",
         "src/src/crazy_linker_line_reader.cpp",
         "src/src/crazy_linker_line_reader.h",
+        "src/src/crazy_linker_load_params.h",
         "src/src/crazy_linker_memory_mapping.cpp",
         "src/src/crazy_linker_memory_mapping.h",
         "src/src/crazy_linker_pointer_set.cpp",
@@ -309,6 +313,14 @@
     ]
   }
 
+  crazy_linker_test_library(
+      "crazy_linker_tests_libzoo_with_android_dlopen_ext") {
+    sources = [
+      "src/tests/zoo_with_android_dlopen_ext.cpp",
+    ]
+    libs = [ "dl" ]
+  }
+
   crazy_linker_test_library("crazy_linker_tests_libzoo_with_dlopen_handle") {
     sources = [
       "src/tests/zoo_with_dlopen_handle.cpp",
@@ -389,6 +401,30 @@
     ]
   }
 
+  executable("crazy_linker_test_load_library_with_fd") {
+    sources = [
+      "src/tests/test_load_library_with_fd.cpp",
+    ]
+    data_deps = [
+      ":crazy_linker_tests_libfoo",
+    ]
+    deps = [
+      ":android_crazy_linker",
+    ]
+  }
+
+  executable("crazy_linker_test_load_library_with_reserved_map") {
+    sources = [
+      "src/tests/test_load_library_with_reserved_map.cpp",
+    ]
+    data_deps = [
+      ":crazy_linker_tests_libfoo",
+    ]
+    deps = [
+      ":android_crazy_linker",
+    ]
+  }
+
   executable("crazy_linker_test_load_library_with_relr_relocations") {
     sources = [
       "src/tests/test_load_library.cpp",
@@ -477,6 +513,18 @@
     ]
   }
 
+  executable("crazy_linker_test_dl_wrappers_with_android_dlopen_ext") {
+    sources = [
+      "src/tests/test_dl_wrappers_with_android_dlopen_ext.cpp",
+    ]
+    data_deps = [
+      ":crazy_linker_tests_libzoo_with_android_dlopen_ext",
+    ]
+    deps = [
+      ":android_crazy_linker",
+    ]
+  }
+
   executable("crazy_linker_test_dl_wrappers_valid_handles") {
     sources = [
       "src/tests/test_dl_wrappers_valid_handles.cpp",
diff --git a/third_party/android_crazy_linker/src/include/crazy_linker.h b/third_party/android_crazy_linker/src/include/crazy_linker.h
index c439379..c29e72b 100644
--- a/third_party/android_crazy_linker/src/include/crazy_linker.h
+++ b/third_party/android_crazy_linker/src/include/crazy_linker.h
@@ -75,13 +75,35 @@
 void crazy_context_clear_error(crazy_context_t* context) _CRAZY_PUBLIC;
 
 // Set the explicit load address in a context object. Value 0 means
-// the address is randomized.
+// the address is randomized. NOTE: This will achieve a best-effort load,
+// if the address range is reserved, the library will still be loaded at
+// a different address. Use crazy_context_set_reserved_map() if you want
+// to ensure that the library can only be loaded at a fixed address.
 void crazy_context_set_load_address(crazy_context_t* context,
                                     size_t load_address) _CRAZY_PUBLIC;
 
 // Return the current load address in a context.
 size_t crazy_context_get_load_address(crazy_context_t* context) _CRAZY_PUBLIC;
 
+// Set the explicit library file descriptor in a context object. Values >= 0
+// will be used during the next crazy_library_open() call to read the library
+// file, instead of opening it using its path.
+void crazy_context_set_library_fd(crazy_context_t* context,
+                                  int fd) _CRAZY_PUBLIC;
+
+// Return the current library file descriptor in a context.
+int crazy_context_get_library_fd(crazy_context_t* context) _CRAZY_PUBLIC;
+
+// Set an explicit reserved memory mapping to be used on the next library
+// load. |reserved_address| is the page-aligned reserved address,
+// |reserved_size| is the page-aligned reserved size, and if |load_fallback|
+// is true, then the linker will try to load the library at a different
+// address if it fails to load its segments at the reserved address range.
+void crazy_context_set_reserved_map(crazy_context_t* context,
+                                    uintptr_t reserved_address,
+                                    size_t reserved_size,
+                                    bool load_fallback) _CRAZY_PUBLIC;
+
 // Destroy a given context object.
 void crazy_context_destroy(crazy_context_t* context) _CRAZY_PUBLIC;
 
diff --git a/third_party/android_crazy_linker/src/run_tests.sh b/third_party/android_crazy_linker/src/run_tests.sh
index fb8ae0c8..1642a40 100755
--- a/third_party/android_crazy_linker/src/run_tests.sh
+++ b/third_party/android_crazy_linker/src/run_tests.sh
@@ -24,6 +24,8 @@
   fi
 }
 
+ADB=${ADB:-adb}
+
 # Run a command through adb shell, strip the extra \r from the output
 # and return the correct status code to detect failures. This assumes
 # that the adb shell command prints a final \n to stdout.
@@ -34,7 +36,6 @@
 adb_shell () {
   local TMPOUT="$(mktemp)"
   local LASTLINE RET
-  local ADB=${ADB:-adb}
 
   # The weird sed rule is to strip the final \r on each output line
   # Since 'adb shell' never returns the command's proper exit/status code,
@@ -156,6 +157,7 @@
 libcrazy_linker_tests_libzoo_dlopen_in_initializer.so \
 libcrazy_linker_tests_libzoo_dlopen_in_initializer_inner.so \
 libcrazy_linker_tests_libzoo_with_dlopen_handle.so \
+libcrazy_linker_tests_libzoo_with_android_dlopen_ext.so \
 "
 
 TEST_FILES="\
@@ -163,13 +165,16 @@
 crazy_linker_test_constructors_destructors \
 crazy_linker_test_dl_wrappers \
 crazy_linker_test_dl_wrappers_recursive \
-crazy_linker_test_dl_wrappers_with_system_handle \
 crazy_linker_test_dl_wrappers_valid_handles \
+crazy_linker_test_dl_wrappers_with_android_dlopen_ext \
+crazy_linker_test_dl_wrappers_with_system_handle \
 crazy_linker_test_jni_hooks \
 crazy_linker_test_load_library \
 crazy_linker_test_load_library_depends \
+crazy_linker_test_load_library_with_fd \
 crazy_linker_test_load_library_with_gnu_hash_table \
 crazy_linker_test_load_library_with_relr_relocations \
+crazy_linker_test_load_library_with_reserved_map \
 crazy_linker_test_relocated_shared_relro \
 crazy_linker_test_search_path_list \
 crazy_linker_test_shared_relro \
@@ -197,7 +202,14 @@
 run_test () {
   local TEST_NAME=$1
   shift
-  run adb_shell LD_LIBRARY_PATH=$RUN_DIR $RUN_DIR/$TEST_NAME "$@"
+  if [ "$VERBOSE" -ge 1 ]; then
+    # Using adb_shell doesn't print stderr, but it gives a status code.
+    # Consider that this is lesser important when debugging the test execution
+    # and run "adb shell" from the command-line instead.
+    echo "cd $RUN_DIR && LD_LIBRARY_PATH=. ./$TEST_NAME $@" | "$ADB" shell
+  else
+    run adb_shell "cd $RUN_DIR && LD_LIBRARY_PATH=. ./$TEST_NAME $@"
+  fi
 }
 
 if [ -n "$DO_UNIT_TESTS" ]; then
diff --git a/third_party/android_crazy_linker/src/src/crazy_linker_api.cpp b/third_party/android_crazy_linker/src/src/crazy_linker_api.cpp
index eeda97c..5dfb808 100644
--- a/third_party/android_crazy_linker/src/src/crazy_linker_api.cpp
+++ b/third_party/android_crazy_linker/src/src/crazy_linker_api.cpp
@@ -32,6 +32,9 @@
 
 struct crazy_context_t {
   size_t load_address = 0;
+  int library_fd = -1;
+  size_t reserved_size = 0;
+  bool reserved_load_fallback = false;
   Error error;
 };
 
@@ -58,12 +61,30 @@
 void crazy_context_set_load_address(crazy_context_t* context,
                                     size_t load_address) {
   context->load_address = load_address;
+  context->reserved_load_fallback = true;
 }
 
 size_t crazy_context_get_load_address(crazy_context_t* context) {
   return context->load_address;
 }
 
+void crazy_context_set_library_fd(crazy_context_t* context, int fd) {
+  context->library_fd = fd;
+}
+
+int crazy_context_get_library_fd(crazy_context_t* context) {
+  return context->library_fd;
+}
+
+void crazy_context_set_reserved_map(crazy_context_t* context,
+                                    uintptr_t reserved_address,
+                                    size_t reserved_size,
+                                    bool load_fallback) {
+  context->load_address = reserved_address;
+  context->reserved_size = reserved_size;
+  context->reserved_load_fallback = load_fallback;
+}
+
 void crazy_context_destroy(crazy_context_t* context) {
   delete context;
 }
@@ -109,9 +130,35 @@
                                   const char* lib_name,
                                   crazy_context_t* context) {
   ScopedLockedGlobals globals;
-  LibraryView* view = globals->libraries()->LoadLibrary(
-      lib_name, context->load_address, globals->search_path_list(),
-      &context->error);
+  crazy::LibraryList* libs = globals->libraries();
+  crazy::LoadParams params;
+  params.wanted_address = context->load_address;
+  params.reserved_size = context->reserved_size;
+  params.reserved_load_fallback = context->reserved_load_fallback;
+  crazy::Expected<LibraryView*> found =
+      libs->FindAndCheckLoadedLibrary(lib_name, params, &context->error);
+  if (!found.has_value())
+    return CRAZY_STATUS_FAILURE;
+
+  LibraryView* view = found.value();
+  if (!view) {
+    if (context->library_fd >= 0) {
+      params.library_path = lib_name;
+      params.library_fd = context->library_fd;
+    } else {
+      if (!libs->LocateLibraryFile(lib_name, *globals->search_path_list(),
+                                   &params, &context->error)) {
+        return CRAZY_STATUS_FAILURE;
+      }
+    }
+    view = libs->LoadLibraryInternal(params, &context->error);
+
+    // Cleanup context.
+    context->library_fd = -1;
+    context->load_address = 0;
+    context->reserved_size = 0;
+    context->reserved_load_fallback = false;
+  }
 
   if (!view)
     return CRAZY_STATUS_FAILURE;
diff --git a/third_party/android_crazy_linker/src/src/crazy_linker_elf_loader.cpp b/third_party/android_crazy_linker/src/src/crazy_linker_elf_loader.cpp
index d219b3c..0be677a 100644
--- a/third_party/android_crazy_linker/src/src/crazy_linker_elf_loader.cpp
+++ b/third_party/android_crazy_linker/src/src/crazy_linker_elf_loader.cpp
@@ -23,14 +23,36 @@
 
 namespace {
 
+// A FileDescriptor sub-class that can also avoid closing the descriptor
+// on scope exit if DontCloseOnExit() is called.
+class LibraryFd : public FileDescriptor {
+ public:
+  LibraryFd() = default;
+
+  explicit LibraryFd(int fd) : FileDescriptor(fd) {}
+
+  explicit LibraryFd(const char* path) : FileDescriptor(path) {}
+
+  ~LibraryFd() {
+    if (!close_on_exit_)
+      Release();
+  }
+
+  LibraryFd(LibraryFd&& other) = default;
+  LibraryFd& operator=(LibraryFd&& other) = default;
+
+  // Ensure the file descriptor is not closed in the destructor.
+  void DontCloseOnExit() { close_on_exit_ = false; }
+
+ private:
+  bool close_on_exit_ = true;
+};
+
 class InternalElfLoader {
  public:
   ~InternalElfLoader();
 
-  bool LoadAt(const char* lib_path,
-              off_t file_offset,
-              uintptr_t wanted_address,
-              Error* error);
+  bool LoadAt(const LoadParams& params, Error* error);
 
   // Only call the following functions after a successful LoadAt() call.
 
@@ -45,7 +67,7 @@
   MemoryMapping ReleaseMapping() { return std::move(reserved_map_); }
 
  private:
-  FileDescriptor fd_;
+  LibraryFd fd_;
   const char* path_ = nullptr;
 
   ELF::Ehdr header_ = {};
@@ -56,7 +78,6 @@
   ELF::Addr phdr_size_ = 0;  // and its size.
 
   off_t file_offset_ = 0;
-  void* wanted_load_address_ = nullptr;
   void* load_start_ = nullptr;  // First page of reserved address space.
   ELF::Addr load_size_ = 0;     // Size in bytes of reserved address space.
   ELF::Addr load_bias_ = 0;     // load_bias, add this value to all "vaddr"
@@ -71,7 +92,7 @@
   // Individual steps used by ::LoadAt()
   bool ReadElfHeader(Error* error);
   bool ReadProgramHeader(Error* error);
-  bool ReserveAddressSpace(Error* error);
+  bool ReserveAddressSpace(const LoadParams& params, Error* error);
   bool LoadSegments(Error* error);
   bool FindPhdr(Error* error);
   bool CheckPhdr(ELF::Addr, Error* error);
@@ -84,33 +105,55 @@
   }
 }
 
-bool InternalElfLoader::LoadAt(const char* lib_path,
-                               off_t file_offset,
-                               uintptr_t wanted_address,
-                               Error* error) {
-  LOG("lib_path='%s', file_offset=%p, load_address=%p", lib_path, file_offset,
-      wanted_address);
+bool InternalElfLoader::LoadAt(const LoadParams& params, Error* error) {
+  const char* lib_path = params.library_path.c_str();
+  LOG("lib_path='%s', file_fd=%d, file_offset=%p, load_address=%lx "
+      "reserved_size=%lx reserved_load_fallback=%s",
+      lib_path, params.library_fd, params.library_offset,
+      static_cast<unsigned long>(params.wanted_address),
+      static_cast<unsigned long>(params.reserved_size),
+      params.reserved_load_fallback ? "true" : "false");
 
   // Check that the load address is properly page-aligned.
+  uintptr_t wanted_address = params.wanted_address;
   if (wanted_address != PAGE_START(wanted_address)) {
     error->Format("Load address is not page aligned (%08x)", wanted_address);
     return false;
   }
-  wanted_load_address_ = reinterpret_cast<void*>(wanted_address);
+
+  if (params.reserved_size != 0) {
+    if (!wanted_address) {
+      error->Format("Reserved size 0x%08lx has not reserved address",
+                    static_cast<unsigned long>(params.reserved_size));
+      return false;
+    }
+    if (params.reserved_size != PAGE_START(params.reserved_size)) {
+      error->Format("Reserved size 0x%08lx is not page-aligned",
+                    static_cast<unsigned long>(params.reserved_size));
+      return false;
+    }
+  }
 
   // Check that the file offset is also properly page-aligned.
   // PAGE_START() can't be used here due to the compiler complaining about
   // comparing signed (off_t) and unsigned (size_t) values.
+  off_t file_offset = params.library_offset;
   if ((file_offset & static_cast<off_t>(PAGE_SIZE - 1)) != 0) {
-    error->Format("File offset is not page aligned (%08x)", file_offset);
+    error->Format("File offset is not page aligned (%08lx)",
+                  static_cast<unsigned long>(file_offset));
     return false;
   }
   file_offset_ = file_offset;
 
   // Open the file.
-  if (!fd_.OpenReadOnly(lib_path)) {
-    error->Format("Can't open file: %s", strerror(errno));
-    return false;
+  if (params.library_fd >= 0) {
+    fd_ = LibraryFd(params.library_fd);
+    fd_.DontCloseOnExit();
+  } else {
+    if (!fd_.OpenReadOnly(lib_path)) {
+      error->Format("Can't open file: %s", strerror(errno));
+      return false;
+    }
   }
 
   if (file_offset && fd_.SeekTo(file_offset) < 0) {
@@ -122,17 +165,11 @@
   path_ = lib_path;
 
   if (!ReadElfHeader(error) || !ReadProgramHeader(error) ||
-      !ReserveAddressSpace(error)) {
-    return false;
-  }
-
-  if (!LoadSegments(error) || !FindPhdr(error)) {
-    // An error occured, cleanup the address space by un-mapping the
-    // range that was reserved by ReserveAddressSpace().
+      !ReserveAddressSpace(params, error) || !LoadSegments(error) ||
+      !FindPhdr(error)) {
     reserved_map_.Deallocate();
     return false;
   }
-
   return true;
 }
 
@@ -220,7 +257,8 @@
 // This will use the wanted_load_address_ value. Fails if the requested
 // address range cannot be reserved. Typically this would be because
 // it overlaps an existing, possibly system, mapping.
-bool InternalElfLoader::ReserveAddressSpace(Error* error) {
+bool InternalElfLoader::ReserveAddressSpace(const LoadParams& params,
+                                            Error* error) {
   ELF::Addr min_vaddr;
   load_size_ =
       phdr_table_get_load_size(phdr_table_, phdr_num_, &min_vaddr, NULL);
@@ -229,36 +267,68 @@
     return false;
   }
 
-  uint8_t* addr = NULL;
+  void* addr = nullptr;
   int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS;
 
   // Support loading at a fixed address.
-  if (wanted_load_address_) {
-    addr = static_cast<uint8_t*>(wanted_load_address_);
+  if (params.wanted_address) {
+    addr = reinterpret_cast<void*>(params.wanted_address);
+    if (!params.reserved_load_fallback) {
+      mmap_flags |= MAP_FIXED;
+    }
   }
 
-  size_t reserved_size = load_size_;
+  void* start = reinterpret_cast<void*>(params.wanted_address);
+  size_t reserved_size = params.reserved_size;
 
-  LOG("address=%p size=%p", addr, reserved_size);
-  void* start = mmap(addr, reserved_size, PROT_NONE, mmap_flags, -1, 0);
-  if (start == MAP_FAILED) {
-    error->Format("Could not reserve %d bytes of address space", reserved_size);
+  if (reserved_size > 0 && reserved_size < load_size_ &&
+      params.reserved_load_fallback) {
+    LOG("Reserved size is too small (%ld < %ld), allocating new mapping!",
+        static_cast<unsigned long>(reserved_size),
+        static_cast<unsigned long>(load_size_));
+    reserved_size = 0;
+    addr = nullptr;
+    mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS;
+  }
+
+  if (!reserved_size) {
+    // Reserve the area ourselves.
+    reserved_size = load_size_;
+    LOG("Trying to reserve memory address=%p size=%lu (0x%lx)", addr,
+        static_cast<unsigned long>(load_size_),
+        static_cast<unsigned long>(load_size_));
+
+    start = mmap(addr, reserved_size, PROT_NONE, mmap_flags, -1, 0);
+    if (start == MAP_FAILED) {
+      error->Format("Could not reserve %lu bytes of address space",
+                    static_cast<unsigned long>(reserved_size));
+      return false;
+    }
+    if (addr && start != addr) {
+      error->Format("Could not map at %p requested, backing out", addr);
+      munmap(start, reserved_size);
+      return false;
+    }
+    // Take ownership of the mapping here.
+    reserved_map_ = MemoryMapping(start, reserved_size);
+  } else if (reserved_size < load_size_) {
+    error->Format("Reserved map size is too small 0x%lx (0x%lx required)",
+                  static_cast<unsigned long>(reserved_size),
+                  static_cast<unsigned long>(load_size_));
     return false;
+  } else {
+    LOG("Using client-allocated mapping!");
   }
-  if (addr && start != addr) {
-    error->Format("Could not map at %p requested, backing out", addr);
-    munmap(start, reserved_size);
-    return false;
-  }
-
-  // Take ownership of the mapping here.
-  reserved_map_ = MemoryMapping(start, reserved_size);
-  LOG("reserved start=%p", reserved_map_.address());
 
   load_start_ = start;
-  load_bias_ = reinterpret_cast<ELF::Addr>(start) - min_vaddr;
+  load_bias_ = reinterpret_cast<ELF::Addr>(load_start_) - min_vaddr;
 
-  LOG("load start=%p, bias=%p", load_start_, load_bias_);
+  LOG("Reserved memory address=%p, size=%lu (0x%lx), bias=%lu (0x%lx)",
+      load_start_, static_cast<unsigned long>(load_size_),
+      static_cast<unsigned long>(load_size_),
+      static_cast<unsigned long>(load_bias_),
+      static_cast<unsigned long>(load_bias_));
+
   return true;
 }
 
@@ -390,13 +460,10 @@
 }  // namespace
 
 // static
-ElfLoader::Result ElfLoader::LoadAt(const char* lib_path,
-                                    off_t file_offset,
-                                    uintptr_t wanted_address,
-                                    Error* error) {
+ElfLoader::Result ElfLoader::LoadAt(const LoadParams& params, Error* error) {
   InternalElfLoader loader;
   Result result;
-  if (loader.LoadAt(lib_path, file_offset, wanted_address, error)) {
+  if (loader.LoadAt(params, error)) {
     result.load_start = reinterpret_cast<ELF::Addr>(loader.load_start());
     result.load_size = loader.load_size();
     result.load_bias = loader.load_bias();
diff --git a/third_party/android_crazy_linker/src/src/crazy_linker_elf_loader.h b/third_party/android_crazy_linker/src/src/crazy_linker_elf_loader.h
index bf4016c..03924e0b 100644
--- a/third_party/android_crazy_linker/src/src/crazy_linker_elf_loader.h
+++ b/third_party/android_crazy_linker/src/src/crazy_linker_elf_loader.h
@@ -6,6 +6,7 @@
 #define CRAZY_LINKER_ELF_LOADER_H
 
 #include "crazy_linker_error.h"
+#include "crazy_linker_load_params.h"
 #include "crazy_linker_memory_mapping.h"
 #include "crazy_linker_system.h"  // For ScopedFileDescriptor
 #include "elf_traits.h"
@@ -29,32 +30,13 @@
     const ELF::Phdr* phdr = nullptr;
     size_t phdr_count = 0;
     MemoryMapping reserved_mapping;
-    Error error;  // empty in case of success.
 
     constexpr bool IsValid() const { return this->load_start != 0; }
   };
 
-  // Try to load a library at a given address. On failure, this will
-  // update the linker error message and returns false.
-  //
-  // |lib_path| is the full library path, and |wanted_address| should
-  // be the desired load address, or 0 to enable randomization.
-  //
-  // |file_offset| is an offset in the file where the ELF header will
-  // be looked for.
-  //
-  // |wanted_address| is the wanted load address (of the first loadable
-  // segment), or 0 to enable randomization.
-  //
-  // On success, returns a valid Result instance, where |reserved_mapping| will
-  // map the single range of reserved memory addresses for the ELF object
-  // (including the breakpad guard regions).
-  //
-  // On failure, return an invalid Result instance, and sets |*error|.
-  static Result LoadAt(const char* lib_path,
-                       off_t file_offset,
-                       uintptr_t wanted_address,
-                       Error* error);
+  // Try to load a library at a given address. On failure, return an
+  // invalid Result instance, and sets |*error|.
+  static Result LoadAt(const LoadParams& params, Error* error);
 };
 
 }  // namespace crazy
diff --git a/third_party/android_crazy_linker/src/src/crazy_linker_library_list.cpp b/third_party/android_crazy_linker/src/src/crazy_linker_library_list.cpp
index 99d0a209..817f906a 100644
--- a/third_party/android_crazy_linker/src/src/crazy_linker_library_list.cpp
+++ b/third_party/android_crazy_linker/src/src/crazy_linker_library_list.cpp
@@ -53,6 +53,38 @@
   }
 };
 
+// Checks that |params| is compatible with a system library load.
+// On success return true. On failure, set |*error| and return false.
+// |lib_name| is either the library name, or nullptr, in which case
+// |params.library_path| will be used for the error message.
+bool CheckSystemLibraryLoadParams(const char* lib_name,
+                                  const LoadParams& params,
+                                  Error* error) {
+  if (!lib_name)
+    lib_name = params.library_path.c_str();
+
+  if (params.library_fd >= 0) {
+    error->Format("Cannot load system library from fd %d: %s",
+                  params.library_fd, lib_name);
+    return false;
+  }
+  if (params.library_offset != 0) {
+    error->Format("Cannot load system library from offset 0x%08lx: %s",
+                  static_cast<unsigned long>(params.library_offset), lib_name);
+    return false;
+  }
+  if (params.wanted_address != 0) {
+    error->Format("Cannot load system library at address 0x%08lx: %s",
+                  static_cast<unsigned long>(params.wanted_address), lib_name);
+    return false;
+  }
+  if (params.reserved_size != 0) {
+    error->Format("Cannot load system library in reserved memory map: %s",
+                  lib_name);
+  }
+  return true;
+}
+
 }  // namespace
 
 LibraryList::LibraryList() {
@@ -295,11 +327,8 @@
                                                       Error* error) {
   // First check whether a library with the same base name was
   // already loaded.
-  LibraryView* view = FindKnownLibrary(lib_name);
-  if (view) {
-    view->AddRef();
-    return view;
-  }
+  ASSERT(!FindKnownLibrary(lib_name),
+         "System library already loaded: ", lib_name);
 
   void* system_lib = SystemLinker::Open(lib_name, dlopen_mode);
   if (!system_lib) {
@@ -309,86 +338,120 @@
   }
 
   // Can't really find the DT_SONAME of this library, assume if is its basename.
-  view = new LibraryView(system_lib, GetBaseNamePtr(lib_name));
+  LibraryView* view = new LibraryView(system_lib, GetBaseNamePtr(lib_name));
   known_libraries_.PushBack(view);
 
   LOG("System library %s loaded at %p", lib_name, view);
   return view;
 }
 
-LibraryView* LibraryList::LoadLibrary(const char* lib_name,
-                                      uintptr_t load_address,
-                                      SearchPathList* search_path_list,
-                                      Error* error) {
-  const char* base_name = GetBaseNamePtr(lib_name);
+Expected<LibraryView*> LibraryList::FindAndCheckLoadedLibrary(
+    const char* lib_name,
+    const LoadParams& params,
+    Error* error) {
+  // First check whether a library with the same base name was already loaded.
+  LibraryView* view = FindKnownLibrary(lib_name);
+  if (!view)
+    return nullptr;
 
-  LOG("lib_name='%s'", lib_name);
-
-  // First check whether a library with the same base name was
-  // already loaded.
-  LibraryView* view = FindKnownLibrary(base_name);
-  if (view) {
-    if (load_address) {
+  if (view->IsSystem()) {
+    if (!CheckSystemLibraryLoadParams(lib_name, params, error))
+      return error;
+  } else {
+    const SharedLibrary* crazy_lib = view->GetCrazy();
+    ASSERT(crazy_lib != nullptr, "Not a crazy library");
+    if (params.wanted_address) {
       // Check that this is a crazy library and that is was loaded at
       // the correct address.
-      if (!view->IsCrazy()) {
-        error->Format("System library can't be loaded at fixed address %08x",
-                      load_address);
-        return nullptr;
-      }
-      uintptr_t actual_address = view->GetCrazy()->load_address();
-      if (actual_address != load_address) {
-        error->Format("Library already loaded at @%08x, can't load it at @%08x",
-                      actual_address,
-                      load_address);
-        return nullptr;
+      uintptr_t actual_address = crazy_lib->load_address();
+      if (actual_address != params.wanted_address) {
+        error->Format(
+            "Library already loaded at address 0x%08lx, can't load it at "
+            "0x%08lx: %s",
+            static_cast<unsigned long>(actual_address),
+            static_cast<unsigned long>(params.wanted_address), lib_name);
+        return error;
       }
     }
-    view->AddRef();
-    return view;
   }
 
-  // Find the full library path.
-  String full_path;
+  view->AddRef();
+  return view;
+}
 
+// static
+bool LibraryList::LocateLibraryFile(const char* lib_name,
+                                    const SearchPathList& search_path_list,
+                                    LoadParams* params,
+                                    Error* error) {
   LOG("Looking through the search path list");
-  SearchPathList::Result probe = search_path_list->FindFile(lib_name);
+  SearchPathList::Result probe = search_path_list.FindFile(lib_name);
   if (!probe.IsValid()) {
     error->Format("Can't find library file %s", lib_name);
-    return nullptr;
+    return false;
   }
   LOG("Found library: path %s @ 0x%x", probe.path.c_str(), probe.offset);
+  params->library_path = std::move(probe.path);
+  params->library_offset = probe.offset;
+  return true;
+}
 
-  if (IsSystemLibraryPath(probe.path.c_str())) {
-    return LoadLibraryWithSystemLinker(probe.path.c_str(), RTLD_NOW, error);
+LibraryView* LibraryList::LoadLibrary(const char* lib_name,
+                                      const LoadParams& params,
+                                      Error* error) {
+  Expected<LibraryView*> found =
+      FindAndCheckLoadedLibrary(lib_name, params, error);
+  if (!found.has_value())
+    return nullptr;
+  if (found.value())
+    return found.value();
+  return LoadLibraryInternal(params, error);
+}
+
+LibraryView* LibraryList::LoadLibraryInternal(const LoadParams& params,
+                                              Error* error) {
+  // Load the library with the system linker if necessary.
+  const char* lib_path = params.library_path.c_str();
+  if (IsSystemLibraryPath(lib_path)) {
+    if (!CheckSystemLibraryLoadParams(lib_path, params, error))
+      return nullptr;
+    return LoadLibraryWithSystemLinker(lib_path, RTLD_NOW, error);
   }
 
   // Load the library with the crazy linker.
   ScopedPtr<SharedLibrary> lib(new SharedLibrary());
-  if (!lib->Load(probe.path.c_str(), load_address, probe.offset, error))
+  if (!lib->Load(params, error))
     return nullptr;
 
   // Load all dependendent libraries.
+  const char* base_name = GetBaseNamePtr(lib_path);
   LOG("Loading dependencies of %s", base_name);
   SharedLibrary::DependencyIterator iter(lib.Get());
   Vector<LibraryView*> dependencies;
   while (iter.GetNext()) {
     Error dep_error;
-    // TODO(digit): Call LoadLibrary recursively instead when properly
-    // detecting system vs Chromium libraries (http://crbug.com/843987).
-    LibraryView* dependency =
-        LoadLibraryWithSystemLinker(iter.GetName(), RTLD_NOW, &dep_error);
+    // TODO(digit): Better library dependency loading that isn't limited
+    // to system libraries. This would allow the linker to load anything
+    // without the caller having to load all dependencies before hand in
+    // reverse topological order.
+    const char* dependency_name = iter.GetName();
+    LibraryView* dependency = FindKnownLibrary(dependency_name);
     if (!dependency) {
-      error->Format("When loading %s: %s", base_name, dep_error.c_str());
-      return nullptr;
+      dependency =
+          LoadLibraryWithSystemLinker(dependency_name, RTLD_NOW, &dep_error);
+      if (!dependency) {
+        error->Format("When loading %s: %s", base_name, dep_error.c_str());
+        // TODO(digit): Unload all dependencies that were loaded so far.
+        return nullptr;
+      }
     }
     dependencies.PushBack(dependency);
   }
   if (CRAZY_DEBUG) {
     LOG("Dependencies loaded for %s", base_name);
     for (const LibraryView* dep : dependencies)
-      LOG("  ... %p %s\n", dep, dep->GetName());
-    LOG("    dependencies @%p\n", &dependencies);
+      LOG("  ... %p %s", dep, dep->GetName());
+    LOG("    dependencies @%p", &dependencies);
   }
 
   // Relocate the library.
@@ -412,7 +475,7 @@
   head_ = lib.Get();
 
   // Then create a new LibraryView for it.
-  view = new LibraryView(lib.Release());
+  LibraryView* view = new LibraryView(lib.Release());
   known_libraries_.PushBack(view);
 
   LOG("Running constructors for %s", base_name);
diff --git a/third_party/android_crazy_linker/src/src/crazy_linker_library_list.h b/third_party/android_crazy_linker/src/src/crazy_linker_library_list.h
index 27e6f31..20a6ab6d7f 100644
--- a/third_party/android_crazy_linker/src/src/crazy_linker_library_list.h
+++ b/third_party/android_crazy_linker/src/src/crazy_linker_library_list.h
@@ -8,6 +8,8 @@
 #include <link.h>
 
 #include "crazy_linker_error.h"
+#include "crazy_linker_expected.h"
+#include "crazy_linker_load_params.h"
 #include "crazy_linker_search_path_list.h"
 #include "elf_traits.h"
 
@@ -62,13 +64,42 @@
   int IteratePhdr(PhdrIterationCallback callback, void* data);
 #endif
 
-  // Try to load a library, possibly at a fixed address.
-  // On failure, returns NULL and sets the |error| message.
-  LibraryView* LoadLibrary(const char* path,
-                           uintptr_t load_address,
-                           SearchPathList* search_path_list,
+  // Find whether a library identified by |name| has already been loaded.
+  // Note that |name| should correspond to the library's unique soname, which
+  // comes from its DT_SONAME entry, and typically, but not necessarily
+  // matches its base name.
+  LibraryView* FindKnownLibrary(const char* name);
+
+  // Check whether |lib_path| matches an already loaded library, compatible
+  // with the content of |load_params| (except its |library_path| field).
+  // On failure, i.e. if the load parameters are incompatible, set |*error|
+  // and return its address. On success, return either nullptr (if the library
+  // was not previously loaded, or a LibraryView* pointer after incrementing
+  // its reference count).
+  Expected<LibraryView*> FindAndCheckLoadedLibrary(
+      const char* lib_path,
+      const LoadParams& load_params,
+      Error* error);
+
+  // Locate library |lib_name| using |search_path_list|. On success, update
+  // |params->library_path| and |params->library_offset| and return true. On
+  // failure, set |*error| and return false.
+  static bool LocateLibraryFile(const char* lib_name,
+                                const SearchPathList& search_path_list,
+                                LoadParams* params,
+                                Error* error);
+
+  // Try to load a library, according to |load_params|. On failure, returns
+  // nullptr and sets the |error| message.
+  LibraryView* LoadLibrary(const char* lib_name,
+                           const LoadParams& load_params,
                            Error* error);
 
+  // Try to load a library, according to |load_params|.
+  // On failure, return nullptr and sets the |error| message.
+  // Note: this will fail if the library is already loaded.
+  LibraryView* LoadLibraryInternal(const LoadParams& load_params, Error* error);
+
   // Unload a given shared library. This really decrements the library's
   // internal reference count. When it reaches zero, the library's
   // destructors are run, its dependencies are unloaded, then the
@@ -103,12 +134,6 @@
   // The list of all known libraries.
   Vector<LibraryView*> known_libraries_;
 
-  // Find whether a library identified by |name| has already been loaded.
-  // Note that |name| should correspond to the library's unique soname, which
-  // comes from its DT_SONAME entry, and typically, but not necessarily
-  // matches its base name.
-  LibraryView* FindKnownLibrary(const char* name);
-
   // The list of all libraries loaded by the crazy linker.
   // This does _not_ include system libraries present in known_libraries_.
   SharedLibrary* head_ = nullptr;
diff --git a/third_party/android_crazy_linker/src/src/crazy_linker_load_params.h b/third_party/android_crazy_linker/src/src/crazy_linker_load_params.h
new file mode 100644
index 0000000..fd885e5
--- /dev/null
+++ b/third_party/android_crazy_linker/src/src/crazy_linker_load_params.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 CRAZY_LINKER_LOAD_PARAMS_H
+#define CRAZY_LINKER_LOAD_PARAMS_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "crazy_linker_util.h"
+
+namespace crazy {
+
+// A structure used to hold parameters related to loading an ELF library
+// into the current process' address space.
+//
+// |library_path| is either the full library path.
+// |library_fd| is a file descriptor. If >= 0, the |library_path| is ignored.
+// |library_offset| is the page-aligned offset where the library starts in
+//   its input file (typically > 0 when reading from Android APKs).
+// |wanted_address| is either 0, or the address where the library should
+//   be loaded.
+// |reserved_size| is either 0, or a page-aligned size in bytes corresponding
+//   to a reserved memory area where to load the library, starting from
+//   |wanted_address|.
+// |reserved_load_fallback| is ignored if |reserved_size| is 0. Otherwise, a
+//   value of true means that if the load fails at the reserved address range,
+//   the linker will try again at a different address.
+struct LoadParams {
+  String library_path;
+  int library_fd = -1;
+  off_t library_offset = 0;
+  uintptr_t wanted_address = 0;
+  uintptr_t reserved_size = 0;
+  bool reserved_load_fallback = false;
+};
+
+}  // namespace crazy
+
+#endif  // CRAZY_LINKER_LOAD_PARAMS_H
diff --git a/third_party/android_crazy_linker/src/src/crazy_linker_shared_library.cpp b/third_party/android_crazy_linker/src/src/crazy_linker_shared_library.cpp
index de136c4..f769f7d 100644
--- a/third_party/android_crazy_linker/src/src/crazy_linker_shared_library.cpp
+++ b/third_party/android_crazy_linker/src/src/crazy_linker_shared_library.cpp
@@ -15,6 +15,7 @@
 #include "crazy_linker_globals.h"
 #include "crazy_linker_library_list.h"
 #include "crazy_linker_library_view.h"
+#include "crazy_linker_load_params.h"
 #include "crazy_linker_memory_mapping.h"
 #include "crazy_linker_system_linker.h"
 #include "crazy_linker_thread_data.h"
@@ -225,21 +226,22 @@
 
 SharedLibrary::~SharedLibrary() = default;
 
-bool SharedLibrary::Load(const char* full_path,
-                         size_t load_address,
-                         size_t file_offset,
-                         Error* error) {
+bool SharedLibrary::Load(const LoadParams& params, Error* error) {
   // First, record the path.
-  LOG("full path '%s'", full_path);
-
-  size_t full_path_len = strlen(full_path);
-  if (full_path_len >= sizeof(full_path_)) {
-    error->Format("Path too long: %s", full_path);
-    return false;
+  const char* full_path = params.library_path.c_str();
+  if (params.library_fd >= 0) {
+    snprintf(full_path_, sizeof(full_path_), "fd(%d):%s", params.library_fd,
+             full_path);
+  } else {
+    size_t full_path_len = strlen(full_path);
+    if (full_path_len >= sizeof(full_path_)) {
+      error->Format("Path too long: %s", full_path);
+      return false;
+    }
+    strlcpy(full_path_, full_path, sizeof(full_path_));
   }
-
-  strlcpy(full_path_, full_path, sizeof(full_path_));
   base_name_ = GetBaseNamePtr(full_path_);
+  LOG("full path '%s'", full_path_);
 
   // Default value of |soname_| will be |base_name_| unless overidden
   // by a DT_SONAME entry. This helps deal with broken libraries that don't
@@ -252,8 +254,7 @@
   LOG("Loading ELF segments for %s", base_name_);
 
   {
-    ElfLoader::Result ret =
-        ElfLoader::LoadAt(full_path_, file_offset, load_address, error);
+    ElfLoader::Result ret = ElfLoader::LoadAt(params, error);
     if (!ret.IsValid() ||
         !view_.InitUnmapped(ret.load_start, ret.phdr, ret.phdr_count, error)) {
       return false;
@@ -265,6 +266,9 @@
     }
 
     reserved_map_ = std::move(ret.reserved_mapping);
+
+    LOG("Reserved mapping %p size=0x%lx", reserved_map_.address(),
+        static_cast<unsigned long>(reserved_map_.size()));
   }
 
   if (phdr_table_get_relro_info(view_.phdr(),
diff --git a/third_party/android_crazy_linker/src/src/crazy_linker_shared_library.h b/third_party/android_crazy_linker/src/src/crazy_linker_shared_library.h
index b0c7b89..5a32665 100644
--- a/third_party/android_crazy_linker/src/src/crazy_linker_shared_library.h
+++ b/third_party/android_crazy_linker/src/src/crazy_linker_shared_library.h
@@ -13,6 +13,7 @@
 #include "crazy_linker_elf_symbols.h"
 #include "crazy_linker_elf_view.h"
 #include "crazy_linker_error.h"
+#include "crazy_linker_load_params.h"
 #include "crazy_linker_memory_mapping.h"
 #include "crazy_linker_rdebug.h"
 #include "crazy_linker_util.h"
@@ -50,17 +51,13 @@
   // Load a library (without its dependents) from an ELF file.
   // Note: This does not apply relocations, nor runs constructors.
   // |full_path| if the file full path.
-  // |load_address| is the page-aligned load address in memory, or 0.
-  // |file_offset| is the page-aligned file offset.
+  // |params| are the load parameters for this operation.
   // On failure, return false and set |error| message.
   //
   // After this, the caller should load all library dependencies,
   // Then call Relocate() and CallConstructors() to complete the
   // operation.
-  bool Load(const char* full_path,
-            size_t load_address,
-            size_t file_offset,
-            Error* error);
+  bool Load(const LoadParams& params, Error* error);
 
   // Relocate this library, assuming all its dependencies are already
   // loaded in |lib_list|. On failure, return false and set |error|
diff --git a/third_party/android_crazy_linker/src/src/crazy_linker_system.cpp b/third_party/android_crazy_linker/src/src/crazy_linker_system.cpp
index bb1b2035..8732968 100644
--- a/third_party/android_crazy_linker/src/src/crazy_linker_system.cpp
+++ b/third_party/android_crazy_linker/src/src/crazy_linker_system.cpp
@@ -77,12 +77,6 @@
   return false;
 }
 
-}  // namespace crazy
-
-#ifndef UNIT_TEST
-
-namespace crazy {
-
 FileDescriptor& FileDescriptor::operator=(FileDescriptor&& other) noexcept {
   if (this != &other) {
     if (fd_ != kEmptyFD) {
@@ -94,6 +88,12 @@
   return *this;
 }
 
+}  // namespace crazy
+
+#ifndef UNIT_TEST
+
+namespace crazy {
+
 ssize_t FileDescriptor::Read(void* buffer, size_t buffer_size) {
   return TEMP_FAILURE_RETRY(::read(fd_, buffer, buffer_size));
 }
diff --git a/third_party/android_crazy_linker/src/src/crazy_linker_system_linker.cpp b/third_party/android_crazy_linker/src/src/crazy_linker_system_linker.cpp
index 162805a..be5d6110 100644
--- a/third_party/android_crazy_linker/src/src/crazy_linker_system_linker.cpp
+++ b/third_party/android_crazy_linker/src/src/crazy_linker_system_linker.cpp
@@ -10,6 +10,13 @@
 
 namespace crazy {
 
+// <android/dlext.h> does not declare android_dlopen_ext() if __ANDROID_API__
+// is smaller than 21, so declare it here as a weak function. This will allow
+// detecting its availability at runtime. For API level 21 or higher, the
+// attribute is ignored due to the previous declaration.
+extern "C" void* android_dlopen_ext(const char*, int, const android_dlextinfo*)
+    __attribute__((weak));
+
 // static
 void* SystemLinker::Open(const char* path, int mode) {
   // NOTE: The system linker will likely modify the global _r_debug link map
@@ -19,6 +26,27 @@
   return ::dlopen(path, mode);
 }
 
+#ifdef __ANDROID__
+// static
+bool SystemLinker::HasAndroidOpenExt() {
+  return android_dlopen_ext != nullptr;
+}
+
+// static
+void* SystemLinker::AndroidOpenExt(const char* path,
+                                   int mode,
+                                   const android_dlextinfo* info) {
+  // NOTE: The system linker will likely modify the global _r_debug link map
+  // so ensure this doesn't conflict with other threads performing delayed
+  // updates on it.
+  ScopedLinkMapLocker locker;
+  if (android_dlopen_ext != nullptr) {
+    return android_dlopen_ext(path, mode, info);
+  }
+  return nullptr;
+}
+#endif  // __ANDROID__
+
 // static
 int SystemLinker::Close(void* handle) {
   // Similarly, though unlikely, this operation may modify the global link map.
diff --git a/third_party/android_crazy_linker/src/src/crazy_linker_system_linker.h b/third_party/android_crazy_linker/src/src/crazy_linker_system_linker.h
index 538c6ee..2e676430 100644
--- a/third_party/android_crazy_linker/src/src/crazy_linker_system_linker.h
+++ b/third_party/android_crazy_linker/src/src/crazy_linker_system_linker.h
@@ -5,6 +5,10 @@
 #ifndef CRAZY_LINKER_SYSTEM_LINKER_H
 #define CRAZY_LINKER_SYSTEM_LINKER_H
 
+#ifdef __ANDROID__
+#include <android/dlext.h>
+#endif
+
 #include <dlfcn.h>
 
 namespace crazy {
@@ -18,6 +22,17 @@
   // Wrapper for dlopen().
   static void* Open(const char* path, int flags);
 
+#ifdef __ANDROID__
+  // Returns true iff this system linker provides android_dlopen_ext().
+  static bool HasAndroidOpenExt();
+
+  // Calls android_dlopen_ext() if available, returns nullptr if it is not
+  // available otherwise.
+  static void* AndroidOpenExt(const char* path,
+                              int flags,
+                              const android_dlextinfo* info);
+#endif  // __ANDROID__
+
   // Wrapper for dlclose().
   static int Close(void* handle);
 
diff --git a/third_party/android_crazy_linker/src/src/crazy_linker_wrappers.cpp b/third_party/android_crazy_linker/src/src/crazy_linker_wrappers.cpp
index f25ffaea..660fdcb2 100644
--- a/third_party/android_crazy_linker/src/src/crazy_linker_wrappers.cpp
+++ b/third_party/android_crazy_linker/src/src/crazy_linker_wrappers.cpp
@@ -15,6 +15,10 @@
 #include "crazy_linker_thread_data.h"
 #include "crazy_linker_util.h"
 
+#ifdef __ANDROID__
+#include <android/dlext.h>
+#endif
+
 #ifdef __arm__
 // On ARM, this function is exported by the dynamic linker but never
 // declared in any official header. It is used at runtime to
@@ -89,17 +93,26 @@
 
 void* WrapDlopen(const char* path, int mode) {
   ScopedLockedGlobals globals;
+  LibraryList* libs = globals->libraries();
 
   // NOTE: If |path| is NULL, the wrapper should return a handle
   // corresponding to the current executable. This can't be a crazy
   // library, so don't try to handle it with the crazy linker.
   if (path) {
-    Error error;
-    LibraryView* wrap = globals->libraries()->LoadLibrary(
-        path, 0U /* load_address */, globals->search_path_list(), &error);
-    if (wrap) {
-      globals->valid_handles()->Add(wrap);
-      return wrap;
+    LibraryView* view = libs->FindKnownLibrary(path);
+    if (!view) {
+      Error error;
+      LoadParams params;
+      if (libs->LocateLibraryFile(path, *globals->search_path_list(), &params,
+                                  &error)) {
+        view = libs->LoadLibraryInternal(params, &error);
+        if (!view) {
+          SetLinkerError("%s: %s", "dlopen", error.c_str());
+          return nullptr;
+        }
+        globals->valid_handles()->Add(view);
+        return view;
+      }
     }
   }
 
@@ -110,12 +123,70 @@
     return nullptr;
   }
 
-  auto* wrap_lib = new LibraryView(system_lib, path ? path : "<executable>");
-  globals->libraries()->AddLibrary(wrap_lib);
-  globals->valid_handles()->Add(wrap_lib);
-  return wrap_lib;
+  auto* view = new LibraryView(system_lib, path ? path : "<executable>");
+  libs->AddLibrary(view);
+  globals->valid_handles()->Add(view);
+  return view;
 }
 
+#ifdef __ANDROID__
+// Prepare LoadParams according to |path| and |info|.
+static LoadParams PrepareLoadParamsFrom(const char* path,
+                                        const android_dlextinfo* info) {
+  LoadParams params;
+  if (info->flags & ANDROID_DLEXT_USE_LIBRARY_FD) {
+    params.library_fd = info->library_fd;
+    if (info->flags & ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET)
+      params.library_offset = info->library_fd_offset;
+  } else {
+    params.library_path = path;
+  }
+  if (info->flags & ANDROID_DLEXT_RESERVED_ADDRESS) {
+    params.wanted_address = reinterpret_cast<uintptr_t>(info->reserved_addr);
+    params.reserved_size = info->reserved_size;
+  }
+  if (info->flags & ANDROID_DLEXT_RESERVED_ADDRESS_HINT)
+    params.reserved_load_fallback = true;
+
+  return params;
+}
+
+void* WrapAndroidDlopenExt(const char* path,
+                           int mode,
+                           const android_dlextinfo* info) {
+  if (!info)
+    return WrapDlopen(path, mode);
+
+  ScopedLockedGlobals globals;
+
+  const uint64_t kSupportedFlags =
+      ANDROID_DLEXT_USE_LIBRARY_FD | ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET |
+      ANDROID_DLEXT_RESERVED_ADDRESS | ANDROID_DLEXT_RESERVED_ADDRESS_HINT;
+  const uint64_t unsupported_flags = (info->flags & ~kSupportedFlags);
+  if (unsupported_flags) {
+    SetLinkerError("%s", "unsupported android_dlextinfo flags %08llx",
+                   "android_dlopen_ext",
+                   static_cast<unsigned long long>(unsupported_flags));
+    return nullptr;
+  }
+
+  if (!path && !(info->flags & ANDROID_DLEXT_USE_LIBRARY_FD)) {
+    SetLinkerError("%s: missing path or file descriptor.",
+                   "android_dlopen_ext");
+    return nullptr;
+  }
+  Error error;
+  LibraryView* view = globals->libraries()->LoadLibraryInternal(
+      PrepareLoadParamsFrom(path, info), &error);
+  if (!view) {
+    SetLinkerError("%s: %s", "android_dlopen_ext", error.c_str());
+    return nullptr;
+  }
+  globals->valid_handles()->Add(view);
+  return view;
+}
+#endif  // __ANDROID__
+
 void* WrapDlsym(void* lib_handle, const char* symbol_name) {
   if (!symbol_name) {
     SetLinkerError("dlsym: NULL symbol name");
@@ -305,6 +376,10 @@
     if (name[0] == '_' && !strcmp("__aeabi_atexit", name))
       return reinterpret_cast<void*>(&__aeabi_atexit);
 #endif
+#ifdef __ANDROID__
+    if (name[0] == 'a' && !strcmp("android_dlopen_ext", name))
+      return reinterpret_cast<void*>(&WrapAndroidDlopenExt);
+#endif
     return NULL;
   }
 
diff --git a/third_party/android_crazy_linker/src/tests/test_dl_wrappers_with_android_dlopen_ext.cpp b/third_party/android_crazy_linker/src/tests/test_dl_wrappers_with_android_dlopen_ext.cpp
new file mode 100644
index 0000000..71faae9
--- /dev/null
+++ b/third_party/android_crazy_linker/src/tests/test_dl_wrappers_with_android_dlopen_ext.cpp
@@ -0,0 +1,142 @@
+// 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.
+
+// A crazy linker test to:
+// - Load a library (LIB_NAME) with the linker.
+// - Find the address of the "OpenExtFindRunClose" function in LIB_NAME.
+// - Call the OpenExtFindRunClose() function, which will use
+// android_dlopen_ext() /
+//   dlsym() to find libbar.so (which depends on libfoo.so).
+// - Close the library.
+
+// This tests the android_dlopen_ext/dlsym/dlclose wrappers provided by the
+// crazy linker to loaded libraries.
+
+#include <crazy_linker.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/mman.h>
+
+#ifdef __ANDROID__
+#include <android/dlext.h>
+#else
+#error "This source file only compiles for Android!"
+#endif
+
+#include "test_util.h"
+
+typedef bool (*OpenExtFindRunCloseFunctionPtr)(const char* lib_name,
+                                               const char* func_name,
+                                               const android_dlextinfo* info);
+
+#define LIB_NAME "libcrazy_linker_tests_libzoo_with_android_dlopen_ext.so"
+#define FUNC_NAME "OpenExtFindRunClose"
+
+#define LIB2_NAME "libcrazy_linker_tests_libbar.so"
+#define FUNC2_NAME "Bar"
+
+int main() {
+  crazy_context_t* context = crazy_context_create();
+  crazy_library_t* library;
+
+  // Load LIB_NAME
+  if (!crazy_library_open(&library, LIB_NAME, context)) {
+    Panic("Could not open library: %s\n", crazy_context_get_error(context));
+  }
+
+  // Find the "OpenExtFindRunClose" symbol from LIB_NAME
+  OpenExtFindRunCloseFunctionPtr lib_func;
+  if (!crazy_library_find_symbol(library, "OpenExtFindRunClose",
+                                 reinterpret_cast<void**>(&lib_func))) {
+    Panic("Could not find '" FUNC_NAME "' in " LIB_NAME "\n");
+  }
+
+  bool ret;
+  // Call it, without dlext info.
+  printf("////////////////////////// FIRST CALL WITHOUT DLEXT INFO\n");
+  ret = (*lib_func)(LIB2_NAME, FUNC2_NAME, nullptr);
+  if (!ret)
+    Panic("'" FUNC_NAME "' function failed!");
+
+  // Call it again, this time load the library from a file descriptor.
+  printf("////////////////////////// SECOND CALL WITH LIBRARY FD\n");
+  {
+    android_dlextinfo info = {};
+    info.flags = ANDROID_DLEXT_USE_LIBRARY_FD;
+    info.library_fd = ::open(LIB2_NAME, O_RDONLY | O_CLOEXEC);
+    if (info.library_fd < 0)
+      PanicErrno("Could not open library file directly");
+
+    ret = (*lib_func)(nullptr, FUNC2_NAME, &info);
+    if (!ret)
+      Panic("'" FUNC_NAME "' function failed with file descriptor!");
+
+    close(info.library_fd);
+  }
+
+  fflush(stdout);
+  printf("////////////////////////// THIRD CALL WITH RESERVED MAP\n");
+  {
+    android_dlextinfo info = {};
+    info.flags = ANDROID_DLEXT_RESERVED_ADDRESS;
+    info.reserved_size = 64 * 4096;
+    info.reserved_addr = ::mmap(nullptr, info.reserved_size, PROT_NONE,
+                                MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+    if (info.reserved_addr == MAP_FAILED)
+      PanicErrno("Could not reserve address map region");
+
+    ret = (*lib_func)(LIB2_NAME, FUNC2_NAME, &info);
+    if (!ret)
+      Panic("'" FUNC_NAME "' function failed with reserved map!");
+
+    ::munmap(info.reserved_addr, info.reserved_size);
+  }
+
+  fflush(stdout);
+  printf("////////////////////////// FOURTH CALL WITH TINY RESERVED MAP\n");
+  {
+    android_dlextinfo info = {};
+    info.flags = ANDROID_DLEXT_RESERVED_ADDRESS;
+    info.reserved_size = 1 * 4096;
+    info.reserved_addr = ::mmap(nullptr, info.reserved_size, PROT_NONE,
+                                MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+    if (info.reserved_addr == MAP_FAILED)
+      PanicErrno("Could not reserve address map region");
+
+    ret = (*lib_func)(LIB2_NAME, FUNC2_NAME, &info);
+    if (ret)
+      Panic("'" FUNC_NAME "' function succeeded with tiny reserved map!");
+
+    ::munmap(info.reserved_addr, info.reserved_size);
+  }
+
+  fflush(stdout);
+  printf("////////////////////////// FIFTH CALL WITH RESERVED MAP + HINT\n");
+  {
+    android_dlextinfo info = {};
+    info.flags =
+        ANDROID_DLEXT_RESERVED_ADDRESS | ANDROID_DLEXT_RESERVED_ADDRESS_HINT;
+    info.reserved_size = 1 * 4096;
+    info.reserved_addr = ::mmap(nullptr, info.reserved_size, PROT_NONE,
+                                MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+    if (info.reserved_addr == MAP_FAILED)
+      PanicErrno("Could not reserve address map region");
+
+    ret = (*lib_func)(LIB2_NAME, FUNC2_NAME, &info);
+    if (!ret)
+      Panic("'" FUNC_NAME "' failed with tiny reserved map and hint!");
+
+    ::munmap(info.reserved_addr, info.reserved_size);
+  }
+
+  fflush(stdout);
+  printf("//////////////////////////\n");
+  // Close the library.
+  printf("Closing " LIB_NAME "\n");
+  crazy_library_close(library);
+
+  crazy_context_destroy(context);
+  printf("OK\n");
+  return 0;
+}
diff --git a/third_party/android_crazy_linker/src/tests/test_load_library_with_fd.cpp b/third_party/android_crazy_linker/src/tests/test_load_library_with_fd.cpp
new file mode 100644
index 0000000..5a38fe1c
--- /dev/null
+++ b/third_party/android_crazy_linker/src/tests/test_load_library_with_fd.cpp
@@ -0,0 +1,91 @@
+// Copyright 2014 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.
+
+// A crazy linker test to:
+// - Load a library (libfoo.so) with the linker.
+// - Find the address of the "Foo" function in it.
+// - Call the function.
+// - Close the library.
+#include <crazy_linker.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <string>
+
+#include "test_util.h"
+
+typedef void (*FunctionPtr)();
+
+#ifndef LIB_NAME
+#define LIB_NAME "libcrazy_linker_tests_libfoo.so"
+#endif
+
+std::string GetProgramDirectory(const char* argv0) {
+  const char* sep = strrchr(argv0, '/');
+  if (!sep) {
+    return ".";
+  }
+  return std::string(argv0, sep - argv0);
+}
+
+int main(int argc, const char** argv) {
+  crazy_context_t* context = crazy_context_create();
+  crazy_library_t* library;
+
+  // DEBUG
+  crazy_context_set_load_address(context, 0x20000000);
+
+  // Assume library file is in the same directory as this executable.
+  std::string program_dir = GetProgramDirectory(argv[0]);
+  std::string lib_path = program_dir + "/" LIB_NAME;
+  int fd = open(lib_path.c_str(), O_RDONLY | O_CLOEXEC);
+  if (fd < 0) {
+    Panic("Could not find library file %s: %s\n", LIB_NAME, strerror(errno));
+  }
+
+  crazy_context_set_library_fd(context, fd);
+
+  int context_fd = crazy_context_get_library_fd(context);
+  if (context_fd != fd)
+    Panic("Invalid context fd %d (expected %d)\n", context_fd, fd);
+
+  // Load libfoo.so
+  if (!crazy_library_open(&library, LIB_NAME, context)) {
+    Panic("Could not open library: %s\n", crazy_context_get_error(context));
+  }
+
+  // Find the "Foo" symbol.
+  FunctionPtr foo_func;
+  if (!crazy_library_find_symbol(library, "Foo",
+                                 reinterpret_cast<void**>(&foo_func))) {
+    Panic("Could not find 'Foo' in %s\n", LIB_NAME);
+  }
+
+  // Call it.
+  (*foo_func)();
+
+  // Close the library.
+  printf("Closing %s\n", LIB_NAME);
+  crazy_library_close(library);
+
+  // Check that the descriptor is not closed, by trying to read from it.
+  char header[4];
+  if (TEMP_FAILURE_RETRY(lseek(fd, 0, SEEK_SET)) < 0 ||
+      TEMP_FAILURE_RETRY(read(fd, header, 4)) < 0) {
+    Panic("Could not read from file descriptor after library close!: %s",
+          strerror(errno));
+  }
+  close(fd);
+
+  context_fd = crazy_context_get_library_fd(context);
+  if (context_fd != -1)
+    Panic("Invalid context fd after library load %d (expected -1)", context_fd);
+
+  crazy_context_destroy(context);
+  printf("OK\n");
+  return 0;
+}
diff --git a/third_party/android_crazy_linker/src/tests/test_load_library_with_reserved_map.cpp b/third_party/android_crazy_linker/src/tests/test_load_library_with_reserved_map.cpp
new file mode 100644
index 0000000..1915d6d
--- /dev/null
+++ b/third_party/android_crazy_linker/src/tests/test_load_library_with_reserved_map.cpp
@@ -0,0 +1,98 @@
+// Copyright 2014 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.
+
+// A crazy linker test to:
+// - Load a library (libfoo.so) with the linker.
+// - Find the address of the "Foo" function in it.
+// - Call the function.
+// - Close the library.
+#include <crazy_linker.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <string>
+
+#include "test_util.h"
+
+typedef void (*FunctionPtr)();
+
+#ifndef LIB_NAME
+#define LIB_NAME "libcrazy_linker_tests_libfoo.so"
+#endif
+
+std::string GetProgramDirectory(const char* argv0) {
+  const char* sep = strrchr(argv0, '/');
+  if (!sep) {
+    return ".";
+  }
+  return std::string(argv0, sep - argv0);
+}
+
+int main(int argc, const char** argv) {
+  crazy_context_t* context = crazy_context_create();
+  crazy_library_t* library;
+
+  // Allocate a memory map range large enough for our library.
+  const size_t kMapSize = 1024 * 1024;  // 1 MiB should be enough.
+  void* reserved_map =
+      mmap(nullptr, kMapSize, PROT_READ | PROT_WRITE | PROT_EXEC,
+           MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  if (reserved_map == MAP_FAILED)
+    Panic("mmap() failed: %s", strerror(errno));
+
+  crazy_context_set_reserved_map(
+      context, reinterpret_cast<uintptr_t>(reserved_map), kMapSize, false);
+
+  // Load libfoo.so
+  if (!crazy_library_open(&library, LIB_NAME, context)) {
+    Panic("Could not open library: %s\n", crazy_context_get_error(context));
+  }
+
+  // Find the "Foo" symbol.
+  FunctionPtr foo_func;
+  if (!crazy_library_find_symbol(library, "Foo",
+                                 reinterpret_cast<void**>(&foo_func))) {
+    Panic("Could not find 'Foo' in %s\n", LIB_NAME);
+  }
+
+  // Call it.
+  (*foo_func)();
+
+  // Close the library.
+  printf("Closing %s\n", LIB_NAME);
+  crazy_library_close(library);
+
+  // Check that the memory map is still there by writing to it.
+  printf("Erasing memory map\n");
+  if (mprotect(reserved_map, kMapSize, PROT_WRITE) < 0)
+    Panic("Could not mprotect() range: %s", strerror(errno));
+  memset(reserved_map, 1, kMapSize);
+
+  printf("Trying to reload inside smaller reserved map (no fallback).\n");
+  // Try to load the library again at the same address, without a reserved
+  // size. This should fail.
+  crazy_context_set_reserved_map(
+      context, reinterpret_cast<uintptr_t>(reserved_map), 4096, false);
+
+  if (crazy_library_open(&library, LIB_NAME, context)) {
+    Panic("Could unexpectedly load library in small mapping!");
+  }
+
+  printf("Trying to reload inside smaller reserved map (with fallback)\n.");
+  if (!crazy_library_open(&library, LIB_NAME, context)) {
+    Panic("Could not reload library with fallback in small mapping!");
+  }
+  crazy_library_close(library);
+
+  printf("Unmapping reserved mapping.\n");
+  if (munmap(reserved_map, kMapSize) < 0)
+    Panic("Could not unmap reserved mapping: %s", strerror(errno));
+
+  crazy_context_destroy(context);
+  printf("OK\n");
+  return 0;
+}
diff --git a/third_party/android_crazy_linker/src/tests/zoo_with_android_dlopen_ext.cpp b/third_party/android_crazy_linker/src/tests/zoo_with_android_dlopen_ext.cpp
new file mode 100644
index 0000000..f050d554
--- /dev/null
+++ b/third_party/android_crazy_linker/src/tests/zoo_with_android_dlopen_ext.cpp
@@ -0,0 +1,75 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <dlfcn.h>
+#include <stdio.h>
+
+// This source file is used to exercise the dlopen()/dlsym() wrappers
+// inside a library that was loaded by the crazy linker. It should be compiled
+// into a standalone library (e.g. libzoo.so) that is loaded with the crazy
+// linker.
+
+#ifdef __ANDROID__
+#include <android/dlext.h>
+
+// <android/dlext.h> does not declare android_dlopen_ext() if __ANDROID_API__
+// is smaller than 21, so declare it here as a weak function. This will allow
+// detecting its availability at runtime. For API level 21 or higher, the
+// attribute is ignored due to the previous declaration.
+
+// NOTE: The crazy linker always provides android_dlopen_ext(), even on older
+//       platforms, this is required to compile this source, and will allow
+//       us to check that the crazy linker resolves weak symbol imports
+//       to itself properly.
+extern "C" void* android_dlopen_ext(const char*, int, const android_dlextinfo*)
+    __attribute__((weak));
+
+#else
+#error "This source file can only build for Android."
+#endif
+
+// Try to load library |lib_name| with android_dlopen_ext(), then locate
+// function |func_name| in it to run it, then close the library.
+// Return true in case of success, false otherwise.
+extern "C" bool OpenExtFindRunClose(const char* lib_name,
+                                    const char* func_name,
+                                    const android_dlextinfo* android_info) {
+  printf("%s: Entering\n", __FUNCTION__);
+  if (!android_dlopen_ext) {
+    fprintf(stderr, "Cannot find android_dlopen_ext symbol!");
+    return false;
+  }
+
+  void* lib_handle = android_dlopen_ext(lib_name, RTLD_NOW, android_info);
+  if (!lib_handle) {
+    fprintf(stderr, "Could not open %s: %s\n", lib_name ? lib_name : "(NULL)",
+            dlerror());
+    return false;
+  }
+  if (!lib_name)
+    lib_name = "(FROM_FD)";
+
+  printf("%s: Opened %s @%p\n", __FUNCTION__, lib_name, lib_handle);
+
+  auto* func_ptr = reinterpret_cast<void (*)()>(dlsym(lib_handle, func_name));
+  if (!func_ptr) {
+    fprintf(stderr, "Could not find 'Bar' symbol in %s: %s\n", lib_name,
+            dlerror());
+    return false;
+  }
+  printf("%s: Found '%s' symbol at @%p\n", __FUNCTION__, func_name, func_ptr);
+
+  // Call it.
+  printf("%s: Calling %s\n", __FUNCTION__, func_name);
+  (*func_ptr)();
+
+  printf("%s: Closing %s\n", __FUNCTION__, lib_name);
+  int ret = dlclose(lib_handle);
+  if (ret != 0) {
+    printf("ERROR: Failed to close library: %s\n", dlerror());
+    return false;
+  }
+  printf("%s: Exiting\n", __FUNCTION__);
+  return true;
+}
diff --git a/third_party/blink/public/mojom/web_feature/web_feature.mojom b/third_party/blink/public/mojom/web_feature/web_feature.mojom
index a6f850b..7ef3bc4e 100644
--- a/third_party/blink/public/mojom/web_feature/web_feature.mojom
+++ b/third_party/blink/public/mojom/web_feature/web_feature.mojom
@@ -2192,7 +2192,6 @@
   kV8UserActivation_HasBeenActive_AttributeGetter = 2785,
   kV8UserActivation_IsActive_AttributeGetter = 2786,
   kTextEncoderEncodeInto = 2787,
-  kInvalidBasicCardMethodData = 2788,
   kClientHintsUA = 2789,
   kClientHintsUAArch = 2790,
   kClientHintsUAPlatform = 2791,
diff --git a/third_party/blink/renderer/core/animation/animation_test_helper.cc b/third_party/blink/renderer/core/animation/animation_test_helper.cc
index a913190..5e90ee4 100644
--- a/third_party/blink/renderer/core/animation/animation_test_helper.cc
+++ b/third_party/blink/renderer/core/animation/animation_test_helper.cc
@@ -43,7 +43,7 @@
   // require our callers to propertly register every animation they pass in
   // here, which the current tests do not do.
   auto style = ComputedStyle::Create();
-  StyleResolverState state(document, element, nullptr /* pseudo_element */,
+  StyleResolverState state(document, *element, nullptr /* pseudo_element */,
                            style.get(), style.get());
   state.SetStyle(style);
   CSSInterpolationTypesMap map(state.GetDocument().GetPropertyRegistry(),
diff --git a/third_party/blink/renderer/core/animation/compositor_animations.cc b/third_party/blink/renderer/core/animation/compositor_animations.cc
index ba98368..f077ded5 100644
--- a/third_party/blink/renderer/core/animation/compositor_animations.cc
+++ b/third_party/blink/renderer/core/animation/compositor_animations.cc
@@ -649,6 +649,7 @@
   PropertyHandleSet properties = effect.Properties();
   DCHECK(!properties.IsEmpty());
   for (const auto& property : properties) {
+    AtomicString custom_property_name = "";
     // If the animation duration is infinite, it doesn't make sense to scale
     // the keyframe offset, so use a scale of 1.0. This is connected to
     // the known issue of how the Web Animations spec handles infinite
@@ -701,6 +702,7 @@
       }
       case CSSPropertyID::kVariable: {
         DCHECK(RuntimeEnabledFeatures::OffMainThreadCSSPaintEnabled());
+        custom_property_name = property.CustomPropertyName();
         target_property = compositor_target_property::CSS_CUSTOM_PROPERTY;
         // TODO(kevers): Extend support to non-float types.
         auto float_curve = std::make_unique<CompositorFloatAnimationCurve>();
@@ -717,7 +719,7 @@
     DCHECK(curve.get());
 
     auto keyframe_model = std::make_unique<CompositorKeyframeModel>(
-        *curve, target_property, 0, group);
+        *curve, target_property, 0, group, std::move(custom_property_name));
 
     if (start_time)
       keyframe_model->SetStartTime(start_time.value());
diff --git a/third_party/blink/renderer/core/animation/compositor_animations_test.cc b/third_party/blink/renderer/core/animation/compositor_animations_test.cc
index 2a1d764..72aacdd 100644
--- a/third_party/blink/renderer/core/animation/compositor_animations_test.cc
+++ b/third_party/blink/renderer/core/animation/compositor_animations_test.cc
@@ -1519,6 +1519,26 @@
 }
 
 TEST_P(AnimationCompositorAnimationsTest,
+       CreateCustomFloatPropertyAnimationWithNonAsciiName) {
+  ScopedOffMainThreadCSSPaintForTest off_main_thread_css_paint(true);
+
+  String property_name = "--東京都";
+  RegisterProperty(GetDocument(), property_name, "<number>", "0", false);
+  SetCustomProperty(property_name, "10");
+
+  StringKeyframeEffectModel* effect = CreateKeyframeEffectModel(
+      CreateReplaceOpKeyframe(property_name, "10", 0),
+      CreateReplaceOpKeyframe(property_name, "20", 1.0));
+
+  std::unique_ptr<CompositorKeyframeModel> keyframe_model =
+      ConvertToCompositorAnimation(*effect);
+  EXPECT_EQ(compositor_target_property::CSS_CUSTOM_PROPERTY,
+            keyframe_model->TargetProperty());
+  EXPECT_EQ(keyframe_model->GetCustomPropertyNameForTesting(),
+            property_name.Utf8().data());
+}
+
+TEST_P(AnimationCompositorAnimationsTest,
        CreateSimpleCustomFloatPropertyAnimation) {
   ScopedOffMainThreadCSSPaintForTest off_main_thread_css_paint(true);
 
diff --git a/third_party/blink/renderer/core/css/element_rule_collector.cc b/third_party/blink/renderer/core/css/element_rule_collector.cc
index 7e07c1d..f13672c 100644
--- a/third_party/blink/renderer/core/css/element_rule_collector.cc
+++ b/third_party/blink/renderer/core/css/element_rule_collector.cc
@@ -130,7 +130,7 @@
   init.part_names = part_names;
   SelectorChecker checker(init);
   SelectorChecker::SelectorCheckingContext context(
-      context_.GetElement(), SelectorChecker::kVisitedMatchEnabled);
+      &context_.GetElement(), SelectorChecker::kVisitedMatchEnabled);
   context.scope = match_request.scope;
   context.pseudo_id = pseudo_style_request_.pseudo_id;
 
@@ -175,7 +175,7 @@
   }
 
   StyleEngine& style_engine =
-      context_.GetElement()->GetDocument().GetStyleEngine();
+      context_.GetElement().GetDocument().GetStyleEngine();
   if (!style_engine.Stats())
     return;
 
@@ -191,9 +191,8 @@
     ShadowV0CascadeOrder cascade_order,
     bool matching_tree_boundary_rules) {
   DCHECK(match_request.rule_set);
-  DCHECK(context_.GetElement());
 
-  Element& element = *context_.GetElement();
+  Element& element = context_.GetElement();
   const AtomicString& pseudo_id = element.ShadowPseudoId();
   if (!pseudo_id.IsEmpty()) {
     DCHECK(element.IsStyledElement());
diff --git a/third_party/blink/renderer/core/css/font_face_set_document.cc b/third_party/blink/renderer/core/css/font_face_set_document.cc
index 69a67ba4..22c04c72 100644
--- a/third_party/blink/renderer/core/css/font_face_set_document.cc
+++ b/third_party/blink/renderer/core/css/font_face_set_document.cc
@@ -32,6 +32,7 @@
 #include "third_party/blink/renderer/core/css/font_face_cache.h"
 #include "third_party/blink/renderer/core/css/font_face_set_load_event.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser.h"
+#include "third_party/blink/renderer/core/css/resolver/font_style_resolver.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
 #include "third_party/blink/renderer/core/css/style_engine.h"
 #include "third_party/blink/renderer/core/dom/document.h"
@@ -152,6 +153,15 @@
   if (font_value == "inherit" || font_value == "initial")
     return false;
 
+  if (!GetDocument()->documentElement()) {
+    auto* font_selector = GetDocument()->GetStyleEngine().GetFontSelector();
+    FontDescription description =
+        FontStyleResolver::ComputeFont(*parsed_style, font_selector);
+    font = Font(description);
+    font.Update(font_selector);
+    return true;
+  }
+
   scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
 
   FontFamily font_family;
@@ -167,7 +177,8 @@
   style->GetFont().Update(style->GetFont().GetFontSelector());
 
   GetDocument()->UpdateActiveStyle();
-  GetDocument()->EnsureStyleResolver().ComputeFont(style.get(), *parsed_style);
+  GetDocument()->EnsureStyleResolver().ComputeFont(
+      *GetDocument()->documentElement(), style.get(), *parsed_style);
 
   font = style->GetFont();
   font.Update(GetFontSelector());
diff --git a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
index 4cb4a01..4ae7592 100644
--- a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
+++ b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
@@ -2428,7 +2428,7 @@
             g_null_atom,
             To<CSSCustomIdentValue>(function_value->Item(0)).Value(),
             g_null_atom);
-        const AtomicString& attr_value = state.GetElement()->getAttribute(attr);
+        const AtomicString& attr_value = state.GetElement().getAttribute(attr);
         string = attr_value.IsNull() ? g_empty_string : attr_value.GetString();
       } else {
         string = To<CSSStringValue>(*item).Value();
diff --git a/third_party/blink/renderer/core/css/resolver/css_variable_resolver.cc b/third_party/blink/renderer/core/css/resolver/css_variable_resolver.cc
index a99c4f0..cb05df7e 100644
--- a/third_party/blink/renderer/core/css/resolver/css_variable_resolver.cc
+++ b/third_party/blink/renderer/core/css/resolver/css_variable_resolver.cc
@@ -357,8 +357,7 @@
 
   if (id == CSSPropertyID::kFontSize) {
     bool is_root =
-        state_.GetElement() &&
-        state_.GetElement() == state_.GetDocument().documentElement();
+        &state_.GetElement() == state_.GetDocument().documentElement();
     options.disallow_registered_font_units = true;
     options.disallow_registered_root_font_units = is_root;
   }
diff --git a/third_party/blink/renderer/core/css/resolver/css_variable_resolver_test.cc b/third_party/blink/renderer/core/css/resolver/css_variable_resolver_test.cc
index eb6ff4ed..5abadf99 100644
--- a/third_party/blink/renderer/core/css/resolver/css_variable_resolver_test.cc
+++ b/third_party/blink/renderer/core/css/resolver/css_variable_resolver_test.cc
@@ -275,7 +275,7 @@
 
 TEST_F(CSSVariableResolverTest, NeedsResolutionClearedByResolver) {
   const ComputedStyle* initial = &ComputedStyle::InitialStyle();
-  StyleResolverState state(GetDocument(), nullptr /* element */,
+  StyleResolverState state(GetDocument(), *GetDocument().documentElement(),
                            nullptr /* pseudo_element */, initial, initial);
 
   scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
@@ -463,7 +463,7 @@
   using CSSUnsetValue = cssvalue::CSSUnsetValue;
 
   const ComputedStyle* initial = &ComputedStyle::InitialStyle();
-  StyleResolverState state(GetDocument(), nullptr /* element */,
+  StyleResolverState state(GetDocument(), *GetDocument().documentElement(),
                            nullptr /* pseudo_element */, initial, initial);
 
   scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
diff --git a/third_party/blink/renderer/core/css/resolver/element_resolve_context.cc b/third_party/blink/renderer/core/css/resolver/element_resolve_context.cc
index b33131b..3e755ed 100644
--- a/third_party/blink/renderer/core/css/resolver/element_resolve_context.cc
+++ b/third_party/blink/renderer/core/css/resolver/element_resolve_context.cc
@@ -30,32 +30,23 @@
 
 namespace blink {
 
-ElementResolveContext::ElementResolveContext(const Document& document)
-    : element_(nullptr),
-      parent_node_(nullptr),
-      layout_parent_(nullptr),
-      root_element_style_(document.documentElement()
-                              ? document.documentElement()->GetComputedStyle()
-                              : document.GetComputedStyle()),
-      element_link_state_(EInsideLink::kNotInsideLink),
-      distributed_to_insertion_point_(false) {}
-
 ElementResolveContext::ElementResolveContext(Element& element)
     : element_(&element),
       element_link_state_(
           element.GetDocument().GetVisitedLinkState().DetermineLinkState(
               element)),
       distributed_to_insertion_point_(false) {
-  LayoutTreeBuilderTraversal::ParentDetails parent_details;
-  if (element.CanParticipateInFlatTree()) {
+  if (!element.NeedsDistributionRecalc() &&
+      element.CanParticipateInFlatTree()) {
+    LayoutTreeBuilderTraversal::ParentDetails parent_details;
     parent_node_ = LayoutTreeBuilderTraversal::Parent(element);
     layout_parent_ =
         LayoutTreeBuilderTraversal::LayoutParent(element, &parent_details);
+    distributed_to_insertion_point_ = parent_details.GetInsertionPoint();
   } else {
     parent_node_ = nullptr;
     layout_parent_ = nullptr;
   }
-  distributed_to_insertion_point_ = parent_details.GetInsertionPoint();
 
   const Document& document = element.GetDocument();
   Node* document_element = document.documentElement();
diff --git a/third_party/blink/renderer/core/css/resolver/element_resolve_context.h b/third_party/blink/renderer/core/css/resolver/element_resolve_context.h
index b7b00858..d1984700 100644
--- a/third_party/blink/renderer/core/css/resolver/element_resolve_context.h
+++ b/third_party/blink/renderer/core/css/resolver/element_resolve_context.h
@@ -29,7 +29,6 @@
 namespace blink {
 
 class ContainerNode;
-class Document;
 class Element;
 class ComputedStyle;
 
@@ -39,11 +38,9 @@
   STACK_ALLOCATED();
 
  public:
-  explicit ElementResolveContext(const Document&);
-
   explicit ElementResolveContext(Element&);
 
-  Element* GetElement() const { return element_; }
+  Element& GetElement() const { return *element_; }
   const ContainerNode* ParentNode() const { return parent_node_; }
   const ContainerNode* LayoutParent() const { return layout_parent_; }
   const ComputedStyle* RootElementStyle() const {
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver.cc b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
index c685cb2..df03012 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
@@ -556,16 +556,16 @@
 
   // Now check author rules, beginning first with presentational attributes
   // mapped from HTML.
-  if (state.GetElement()->IsStyledElement()) {
+  if (state.GetElement().IsStyledElement()) {
     collector.AddElementStyleProperties(
-        state.GetElement()->PresentationAttributeStyle());
+        state.GetElement().PresentationAttributeStyle());
 
     // Now we check additional mapped declarations.
     // Tables and table cells share an additional mapped rule that must be
     // applied after all attributes, since their mapped style depends on the
     // values of multiple attributes.
     collector.AddElementStyleProperties(
-        state.GetElement()->AdditionalPresentationAttributeStyle());
+        state.GetElement().AdditionalPresentationAttributeStyle());
 
     if (auto* html_element = DynamicTo<HTMLElement>(state.GetElement())) {
       bool is_auto;
@@ -580,18 +580,18 @@
     }
   }
 
-  MatchAuthorRules(*state.GetElement(), collector);
+  MatchAuthorRules(state.GetElement(), collector);
 
-  if (state.GetElement()->IsStyledElement()) {
+  if (state.GetElement().IsStyledElement()) {
     // For Shadow DOM V1, inline style is already collected in
     // matchScopedRules().
     if (GetDocument().GetShadowCascadeOrder() ==
             ShadowCascadeOrder::kShadowCascadeV0 &&
-        state.GetElement()->InlineStyle()) {
+        state.GetElement().InlineStyle()) {
       // Inline style is immutable as long as there is no CSSOM wrapper.
       bool is_inline_style_cacheable =
-          !state.GetElement()->InlineStyle()->IsMutable();
-      collector.AddElementStyleProperties(state.GetElement()->InlineStyle(),
+          !state.GetElement().InlineStyle()->IsMutable();
+      collector.AddElementStyleProperties(state.GetElement().InlineStyle(),
                                           is_inline_style_cacheable);
     }
 
@@ -861,7 +861,7 @@
     const CSSValue* value) {
   // TODO(alancutter): Avoid creating a StyleResolverState just to apply a
   // single value on a ComputedStyle.
-  StyleResolverState state(element.GetDocument(), &element,
+  StyleResolverState state(element.GetDocument(), element,
                            nullptr /* pseudo_element */, parent_style,
                            parent_style);
   state.SetStyle(ComputedStyle::Clone(base_style));
@@ -916,7 +916,7 @@
 
     MatchUARules(collector);
     MatchUserRules(collector);
-    MatchAuthorRules(*state.GetElement(), collector);
+    MatchAuthorRules(state.GetElement(), collector);
     collector.FinishAddingAuthorRulesForTreeScope();
 
     if (tracker_)
@@ -965,7 +965,7 @@
     return nullptr;
 
   StyleResolverState state(
-      GetDocument(), element,
+      GetDocument(), *element,
       element->GetPseudoElement(pseudo_style_request.pseudo_id), parent_style,
       parent_layout_object_style);
   if (!PseudoStyleForElementInternal(*element, pseudo_style_request, state)) {
@@ -985,7 +985,10 @@
 scoped_refptr<const ComputedStyle> StyleResolver::StyleForPage(int page_index) {
   scoped_refptr<const ComputedStyle> initial_style =
       InitialStyleForElement(GetDocument());
-  StyleResolverState state(GetDocument(), GetDocument().documentElement(),
+  if (!GetDocument().documentElement())
+    return initial_style;
+
+  StyleResolverState state(GetDocument(), *GetDocument().documentElement(),
                            nullptr /* pseudo_element */, initial_style.get(),
                            initial_style.get());
 
@@ -1082,7 +1085,7 @@
 StyleRuleList* StyleResolver::StyleRulesForElement(Element* element,
                                                    unsigned rules_to_include) {
   DCHECK(element);
-  StyleResolverState state(GetDocument(), element,
+  StyleResolverState state(GetDocument(), *element,
                            nullptr /* pseudo_element */);
   ElementRuleCollector collector(state.ElementContext(), selector_filter_,
                                  state.Style());
@@ -1097,7 +1100,7 @@
     PseudoId pseudo_id,
     unsigned rules_to_include) {
   DCHECK(element);
-  StyleResolverState state(GetDocument(), element,
+  StyleResolverState state(GetDocument(), *element,
                            nullptr /* pseudo_element */);
   ElementRuleCollector collector(state.ElementContext(), selector_filter_,
                                  state.Style());
@@ -1137,14 +1140,13 @@
 bool StyleResolver::ApplyAnimatedStandardProperties(
     StyleResolverState& state,
     const Element* animating_element) {
-  Element* element = state.GetElement();
-  DCHECK(element);
+  Element& element = state.GetElement();
 
   // The animating element may be this element, the pseudo element we are
   // resolving style for, or null if we are resolving style for a pseudo
   // element which is not represented by a PseudoElement like scrollbar pseudo
   // elements.
-  DCHECK(animating_element == element || !animating_element ||
+  DCHECK(animating_element == &element || !animating_element ||
          animating_element->ParentOrShadowHostElement() == element);
 
   if (state.Style()->Animations() ||
@@ -1156,14 +1158,14 @@
   }
 
   CSSAnimations::CalculateCompositorAnimationUpdate(
-      state.AnimationUpdate(), animating_element, *element, *state.Style(),
+      state.AnimationUpdate(), animating_element, element, *state.Style(),
       state.ParentStyle(), WasViewportResized());
   CSSAnimations::CalculateTransitionUpdate(
       state.AnimationUpdate(), CSSAnimations::PropertyPass::kStandard,
       animating_element, *state.Style());
 
   CSSAnimations::SnapshotCompositorKeyframes(
-      *element, state.AnimationUpdate(), *state.Style(), state.ParentStyle());
+      element, state.AnimationUpdate(), *state.Style(), state.ParentStyle());
 
   if (state.AnimationUpdate().IsEmpty())
     return false;
@@ -1642,8 +1644,7 @@
 StyleResolver::CacheSuccess StyleResolver::ApplyMatchedCache(
     StyleResolverState& state,
     const MatchResult& match_result) {
-  const Element* element = state.GetElement();
-  DCHECK(element);
+  const Element& element = state.GetElement();
 
   unsigned cache_hash = match_result.IsCacheable()
                             ? ComputeMatchedPropertiesHash(
@@ -1669,8 +1670,8 @@
         *cached_matched_properties->computed_style);
     if (state.ParentStyle()->InheritedDataShared(
             *cached_matched_properties->parent_computed_style) &&
-        !IsAtShadowBoundary(element) &&
-        (!state.DistributedToV0InsertionPoint() || element->AssignedSlot() ||
+        !IsAtShadowBoundary(&element) &&
+        (!state.DistributedToV0InsertionPoint() || element.AssignedSlot() ||
          state.Style()->UserModify() == EUserModify::kReadOnly)) {
       INCREMENT_STYLE_STATS_COUNTER(GetDocument().GetStyleEngine(),
                                     matched_property_cache_inherited_hit, 1);
@@ -1742,7 +1743,7 @@
   DCHECK(!state.IsAnimationInterpolationMapReady());
 
   CSSAnimations::CalculateAnimationUpdate(
-      state.AnimationUpdate(), animating_element, *state.GetElement(),
+      state.AnimationUpdate(), animating_element, state.GetElement(),
       *state.Style(), state.ParentStyle(), this);
   CSSAnimations::CalculateTransitionUpdate(state.AnimationUpdate(),
                                            CSSAnimations::PropertyPass::kCustom,
@@ -1977,7 +1978,8 @@
 // Font properties are also handled by FontStyleResolver outside the main
 // thread. If you add/remove properties here, make sure they are also properly
 // handled by FontStyleResolver.
-void StyleResolver::ComputeFont(ComputedStyle* style,
+void StyleResolver::ComputeFont(Element& element,
+                                ComputedStyle* style,
                                 const CSSPropertyValueSet& property_set) {
   static const CSSProperty* properties[7] = {
       &GetCSSPropertyFontSize(),        &GetCSSPropertyFontFamily(),
@@ -1987,8 +1989,8 @@
   };
 
   // TODO(timloh): This is weird, the style is being used as its own parent
-  StyleResolverState state(GetDocument(), nullptr /* element */,
-                           nullptr /* pseudo_element */, style, style);
+  StyleResolverState state(GetDocument(), element, nullptr /* pseudo_element */,
+                           style, style);
   state.SetStyle(style);
 
   for (const CSSProperty* property : properties) {
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver.h b/third_party/blink/renderer/core/css/resolver/style_resolver.h
index 1de2c23d..d00f5fe 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver.h
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver.h
@@ -120,7 +120,7 @@
       unsigned rules_to_include = kAllButEmptyCSSRules);
   StyleRuleList* StyleRulesForElement(Element*, unsigned rules_to_include);
 
-  void ComputeFont(ComputedStyle*, const CSSPropertyValueSet&);
+  void ComputeFont(Element&, ComputedStyle*, const CSSPropertyValueSet&);
 
   // FIXME: Rename to reflect the purpose, like didChangeFontSize or something.
   void InvalidateMatchedPropertiesCache();
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver_state.cc b/third_party/blink/renderer/core/css/resolver/style_resolver_state.cc
index 9726e5e..25c4156 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver_state.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver_state.cc
@@ -47,7 +47,7 @@
       apply_property_to_visited_link_style_(false),
       has_dir_auto_attribute_(false),
       font_builder_(&document),
-      element_style_resources_(*GetElement(),
+      element_style_resources_(GetElement(),
                                document.DevicePixelRatio(),
                                pseudo_element) {
   DCHECK(!!parent_style_ == !!layout_parent_style_);
@@ -66,13 +66,12 @@
 }
 
 StyleResolverState::StyleResolverState(Document& document,
-                                       Element* element,
+                                       Element& element,
                                        PseudoElement* pseudo_element,
                                        const ComputedStyle* parent_style,
                                        const ComputedStyle* layout_parent_style)
     : StyleResolverState(document,
-                         element ? ElementResolveContext(*element)
-                                 : ElementResolveContext(document),
+                         ElementResolveContext(element),
                          pseudo_element,
                          parent_style,
                          layout_parent_style) {}
@@ -84,7 +83,7 @@
 }
 
 TreeScope& StyleResolverState::GetTreeScope() const {
-  return GetElement() ? GetElement()->GetTreeScope() : GetDocument();
+  return GetElement().GetTreeScope();
 }
 
 void StyleResolverState::SetStyle(scoped_refptr<ComputedStyle> style) {
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver_state.h b/third_party/blink/renderer/core/css/resolver/style_resolver_state.h
index e56cdd3..68de630d 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver_state.h
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver_state.h
@@ -57,7 +57,7 @@
                      const ComputedStyle* parent_style,
                      const ComputedStyle* layout_parent_style);
   StyleResolverState(Document&,
-                     Element*,
+                     Element&,
                      PseudoElement* pseudo_element,
                      const ComputedStyle* parent_style = nullptr,
                      const ComputedStyle* layout_parent_style = nullptr);
@@ -68,7 +68,7 @@
   // separately.
   Document& GetDocument() const { return *document_; }
   // These are all just pass-through methods to ElementResolveContext.
-  Element* GetElement() const { return element_context_.GetElement(); }
+  Element& GetElement() const { return element_context_.GetElement(); }
   TreeScope& GetTreeScope() const;
   const ContainerNode* ParentNode() const {
     return element_context_.ParentNode();
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index a2d09a03..12c3460 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -277,7 +277,6 @@
 #include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h"
 #include "third_party/blink/renderer/platform/bindings/v8_dom_wrapper.h"
 #include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
-#include "third_party/blink/renderer/platform/date_components.h"
 #include "third_party/blink/renderer/platform/geometry/length_functions.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/histogram.h"
@@ -296,6 +295,7 @@
 #include "third_party/blink/renderer/platform/scheduler/public/frame_or_worker_scheduler.h"
 #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
+#include "third_party/blink/renderer/platform/text/date_components.h"
 #include "third_party/blink/renderer/platform/text/platform_locale.h"
 #include "third_party/blink/renderer/platform/weborigin/origin_access_entry.h"
 #include "third_party/blink/renderer/platform/weborigin/scheme_registry.h"
diff --git a/third_party/blink/renderer/core/frame/mhtml_archive_test.cc b/third_party/blink/renderer/core/frame/mhtml_archive_test.cc
index 0a4f0f48..96fa0ee 100644
--- a/third_party/blink/renderer/core/frame/mhtml_archive_test.cc
+++ b/third_party/blink/renderer/core/frame/mhtml_archive_test.cc
@@ -33,7 +33,6 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/renderer/platform/date_components.h"
 #include "third_party/blink/renderer/platform/mhtml/mhtml_archive.h"
 #include "third_party/blink/renderer/platform/mhtml/mhtml_parser.h"
 #include "third_party/blink/renderer/platform/mhtml/serialized_resource.h"
@@ -41,6 +40,7 @@
 #include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
+#include "third_party/blink/renderer/platform/text/date_components.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/weborigin/scheme_registry.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_font_cache.cc b/third_party/blink/renderer/core/html/canvas/canvas_font_cache.cc
index 228a7da..6477b786 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_font_cache.cc
+++ b/third_party/blink/renderer/core/html/canvas/canvas_font_cache.cc
@@ -8,6 +8,7 @@
 #include "third_party/blink/renderer/core/css/parser/css_parser.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
 #include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/html/canvas/html_canvas_element.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
 #include "third_party/blink/renderer/platform/fonts/font_cache.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
@@ -56,7 +57,8 @@
                                     : CanvasFontCacheHardMaxFonts);
 }
 
-bool CanvasFontCache::GetFontUsingDefaultStyle(const String& font_string,
+bool CanvasFontCache::GetFontUsingDefaultStyle(HTMLCanvasElement& element,
+                                               const String& font_string,
                                                Font& resolved_font) {
   HashMap<String, Font>::iterator i =
       fonts_resolved_using_default_style_.find(font_string);
@@ -74,7 +76,8 @@
 
   scoped_refptr<ComputedStyle> font_style =
       ComputedStyle::Clone(*default_font_style_.get());
-  document_->EnsureStyleResolver().ComputeFont(font_style.get(), *parsed_style);
+  document_->EnsureStyleResolver().ComputeFont(element, font_style.get(),
+                                               *parsed_style);
   fonts_resolved_using_default_style_.insert(font_string,
                                              font_style->GetFont());
   resolved_font = fonts_resolved_using_default_style_.find(font_string)->value;
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_font_cache.h b/third_party/blink/renderer/core/html/canvas/canvas_font_cache.h
index 2e41d95..c742018 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_font_cache.h
+++ b/third_party/blink/renderer/core/html/canvas/canvas_font_cache.h
@@ -20,6 +20,7 @@
 class ComputedStyle;
 class Document;
 class FontCachePurgePreventer;
+class HTMLCanvasElement;
 
 class CORE_EXPORT CanvasFontCache final
     : public GarbageCollectedFinalized<CanvasFontCache>,
@@ -39,7 +40,9 @@
   unsigned HardMaxFonts();
 
   void WillUseCurrentFont() { SchedulePruningIfNeeded(); }
-  bool GetFontUsingDefaultStyle(const String&, Font&);
+  bool GetFontUsingDefaultStyle(HTMLCanvasElement& canvas,
+                                const String&,
+                                Font&);
 
   // TaskObserver implementation
   void DidProcessTask(const base::PendingTask&) override;
diff --git a/third_party/blink/renderer/core/html/forms/base_temporal_input_type.h b/third_party/blink/renderer/core/html/forms/base_temporal_input_type.h
index c48bf30..a142ad16 100644
--- a/third_party/blink/renderer/core/html/forms/base_temporal_input_type.h
+++ b/third_party/blink/renderer/core/html/forms/base_temporal_input_type.h
@@ -33,7 +33,7 @@
 
 #include "third_party/blink/renderer/core/html/forms/date_time_edit_element.h"
 #include "third_party/blink/renderer/core/html/forms/input_type.h"
-#include "third_party/blink/renderer/platform/date_components.h"
+#include "third_party/blink/renderer/platform/text/date_components.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/core/html/forms/date_input_type.cc b/third_party/blink/renderer/core/html/forms/date_input_type.cc
index 999f224..6f22f11f 100644
--- a/third_party/blink/renderer/core/html/forms/date_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/date_input_type.cc
@@ -36,7 +36,7 @@
 #include "third_party/blink/renderer/core/html/forms/html_input_element.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/platform/date_components.h"
+#include "third_party/blink/renderer/platform/text/date_components.h"
 #include "third_party/blink/renderer/platform/text/platform_locale.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/core/html/forms/date_time_chooser_impl.cc b/third_party/blink/renderer/core/html/forms/date_time_chooser_impl.cc
index f87435b..8b37c16 100644
--- a/third_party/blink/renderer/core/html/forms/date_time_chooser_impl.cc
+++ b/third_party/blink/renderer/core/html/forms/date_time_chooser_impl.cc
@@ -38,8 +38,8 @@
 #include "third_party/blink/renderer/core/layout/layout_theme.h"
 #include "third_party/blink/renderer/core/page/chrome_client.h"
 #include "third_party/blink/renderer/core/page/page_popup.h"
-#include "third_party/blink/renderer/platform/date_components.h"
 #include "third_party/blink/renderer/platform/language.h"
+#include "third_party/blink/renderer/platform/text/date_components.h"
 #include "third_party/blink/renderer/platform/text/platform_locale.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/core/html/forms/date_time_edit_element.h b/third_party/blink/renderer/core/html/forms/date_time_edit_element.h
index 05cf99d..b8b6899 100644
--- a/third_party/blink/renderer/core/html/forms/date_time_edit_element.h
+++ b/third_party/blink/renderer/core/html/forms/date_time_edit_element.h
@@ -30,7 +30,7 @@
 #include "third_party/blink/public/platform/web_focus_type.h"
 #include "third_party/blink/renderer/core/html/forms/date_time_field_element.h"
 #include "third_party/blink/renderer/core/html/forms/step_range.h"
-#include "third_party/blink/renderer/platform/date_components.h"
+#include "third_party/blink/renderer/platform/text/date_components.h"
 #include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/core/html/forms/date_time_field_elements.cc b/third_party/blink/renderer/core/html/forms/date_time_field_elements.cc
index ae5485f..35de0956 100644
--- a/third_party/blink/renderer/core/html/forms/date_time_field_elements.cc
+++ b/third_party/blink/renderer/core/html/forms/date_time_field_elements.cc
@@ -26,7 +26,7 @@
 #include "third_party/blink/renderer/core/html/forms/date_time_field_elements.h"
 
 #include "third_party/blink/renderer/core/html/forms/date_time_fields_state.h"
-#include "third_party/blink/renderer/platform/date_components.h"
+#include "third_party/blink/renderer/platform/text/date_components.h"
 #include "third_party/blink/renderer/platform/text/platform_locale.h"
 #include "third_party/blink/renderer/platform/wtf/date_math.h"
 #include "third_party/blink/renderer/platform/wtf/time.h"
diff --git a/third_party/blink/renderer/core/html/forms/date_time_local_input_type.cc b/third_party/blink/renderer/core/html/forms/date_time_local_input_type.cc
index 0f12fd7..b0bcc91e 100644
--- a/third_party/blink/renderer/core/html/forms/date_time_local_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/date_time_local_input_type.cc
@@ -36,7 +36,7 @@
 #include "third_party/blink/renderer/core/html_names.h"
 #include "third_party/blink/renderer/core/input_type_names.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
-#include "third_party/blink/renderer/platform/date_components.h"
+#include "third_party/blink/renderer/platform/text/date_components.h"
 #include "third_party/blink/renderer/platform/text/platform_locale.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
diff --git a/third_party/blink/renderer/core/html/forms/html_input_element.cc b/third_party/blink/renderer/core/html/forms/html_input_element.cc
index a80b8e5..974f1a6 100644
--- a/third_party/blink/renderer/core/html/forms/html_input_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_input_element.cc
@@ -1652,8 +1652,7 @@
     return false;
   HTMLDataListOptionsCollection* options = data_list->options();
   for (unsigned i = 0; HTMLOptionElement* option = options->Item(i); ++i) {
-    if (!option->value().IsEmpty() && !option->IsDisabledFormControl() &&
-        IsValidValue(option->value()))
+    if (!option->value().IsEmpty() && !option->IsDisabledFormControl())
       return true;
   }
   return false;
@@ -1687,9 +1686,7 @@
           option->label().FoldCase().Find(value) == kNotFound)
         continue;
     }
-    // TODO(tkent): Should allow invalid strings. crbug.com/607097.
-    if (option->value().IsEmpty() || option->IsDisabledFormControl() ||
-        !IsValidValue(option->value()))
+    if (option->value().IsEmpty() || option->IsDisabledFormControl())
       continue;
     filtered.push_back(option);
   }
diff --git a/third_party/blink/renderer/core/html/forms/month_input_type.cc b/third_party/blink/renderer/core/html/forms/month_input_type.cc
index 32b4dc8..00fc06f 100644
--- a/third_party/blink/renderer/core/html/forms/month_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/month_input_type.cc
@@ -35,7 +35,7 @@
 #include "third_party/blink/renderer/core/html/forms/html_input_element.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/platform/date_components.h"
+#include "third_party/blink/renderer/platform/text/date_components.h"
 #include "third_party/blink/renderer/platform/text/platform_locale.h"
 #include "third_party/blink/renderer/platform/wtf/date_math.h"
 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
diff --git a/third_party/blink/renderer/core/html/forms/multiple_fields_temporal_input_type_view.cc b/third_party/blink/renderer/core/html/forms/multiple_fields_temporal_input_type_view.cc
index ac76a2c..b9b75086 100644
--- a/third_party/blink/renderer/core/html/forms/multiple_fields_temporal_input_type_view.cc
+++ b/third_party/blink/renderer/core/html/forms/multiple_fields_temporal_input_type_view.cc
@@ -48,8 +48,8 @@
 #include "third_party/blink/renderer/core/page/focus_controller.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
-#include "third_party/blink/renderer/platform/date_components.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/text/date_components.h"
 #include "third_party/blink/renderer/platform/text/date_time_format.h"
 #include "third_party/blink/renderer/platform/text/platform_locale.h"
 #include "third_party/blink/renderer/platform/wtf/date_math.h"
diff --git a/third_party/blink/renderer/core/html/forms/resources/calendarPicker.js b/third_party/blink/renderer/core/html/forms/resources/calendarPicker.js
index 9d50db7e..85fafbf 100644
--- a/third_party/blink/renderer/core/html/forms/resources/calendarPicker.js
+++ b/third_party/blink/renderer/core/html/forms/resources/calendarPicker.js
@@ -356,7 +356,7 @@
   return Day.formatter.format(this.startDate());
 };
 
-// See platform/date_components.h.
+// See platform/text/date_components.h.
 Day.Minimum = Day.createFromValue(-62135596800000.0);
 Day.Maximum = Day.createFromValue(8640000000000000.0);
 
@@ -391,7 +391,7 @@
 
 Week.ISOStringRegExp = /^(\d+)-[wW](\d+)$/;
 
-// See platform/date_components.h.
+// See platform/text/date_components.h.
 Week.Minimum = new Week(1, 1);
 Week.Maximum = new Week(275760, 37);
 
@@ -613,7 +613,7 @@
 
 Month.ISOStringRegExp = /^(\d+)-(\d+)$/;
 
-// See platform/date_components.h.
+// See platform/text/date_components.h.
 Month.Minimum = new Month(1, 0);
 Month.Maximum = new Month(275760, 8);
 
diff --git a/third_party/blink/renderer/core/html/forms/time_input_type.cc b/third_party/blink/renderer/core/html/forms/time_input_type.cc
index eaabf37..9d8170a 100644
--- a/third_party/blink/renderer/core/html/forms/time_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/time_input_type.cc
@@ -37,7 +37,7 @@
 #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/inspector/console_message.h"
-#include "third_party/blink/renderer/platform/date_components.h"
+#include "third_party/blink/renderer/platform/text/date_components.h"
 #include "third_party/blink/renderer/platform/text/platform_locale.h"
 #include "third_party/blink/renderer/platform/wtf/date_math.h"
 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
diff --git a/third_party/blink/renderer/core/html/forms/week_input_type.cc b/third_party/blink/renderer/core/html/forms/week_input_type.cc
index c78dbcdd..c6567554 100644
--- a/third_party/blink/renderer/core/html/forms/week_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/week_input_type.cc
@@ -35,7 +35,7 @@
 #include "third_party/blink/renderer/core/html/forms/html_input_element.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/platform/date_components.h"
+#include "third_party/blink/renderer/platform/text/date_components.h"
 #include "third_party/blink/renderer/platform/text/platform_locale.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
diff --git a/third_party/blink/renderer/core/inspector/browser_protocol.pdl b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
index a76a91f..39afbf7 100644
--- a/third_party/blink/renderer/core/inspector/browser_protocol.pdl
+++ b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
@@ -4892,6 +4892,7 @@
 domain Page
   depends on Debugger
   depends on DOM
+  depends on IO
   depends on Network
   depends on Runtime
 
@@ -5351,9 +5352,15 @@
       # Whether or not to prefer page size as defined by css. Defaults to false,
       # in which case the content will be scaled to fit the paper size.
       optional boolean preferCSSPageSize
+      # return as stream
+      experimental optional enum transferMode
+        ReturnAsBase64
+        ReturnAsStream
     returns
-      # Base64-encoded pdf data.
+      # Base64-encoded pdf data. Empty if |returnAsStream| is specified.
       binary data
+      # A handle of the stream that holds resulting PDF data.
+      experimental optional IO.StreamHandle stream
 
   # Reloads given page optionally ignoring the cache.
   command reload
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.cc b/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.cc
index 505907e0..09c595a 100644
--- a/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.cc
+++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.cc
@@ -98,7 +98,7 @@
   frame_->GetDocument()->Markers().RemoveMarkersOfTypes(
       DocumentMarker::MarkerTypes::TextMatch());
 
-  if (user_scrolled_)
+  if (user_scrolled_ && !did_scroll_into_view_)
     metrics_->ScrollCancelled();
 
   first_match_needs_scroll_ = !user_scrolled_;
@@ -173,6 +173,7 @@
             WebScrollIntoViewParams(ScrollAlignment::kAlignCenterIfNeeded,
                                     ScrollAlignment::kAlignCenterIfNeeded,
                                     kProgrammaticScroll));
+    did_scroll_into_view_ = true;
     metrics_->DidScroll();
 
     // We scrolled the text into view if the main document scrolled or the text
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.h b/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.h
index c140a99..74250da9c 100644
--- a/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.h
+++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.h
@@ -53,8 +53,14 @@
   Member<LocalFrame> frame_;
 
   bool search_finished_ = false;
+  // Whether the user has scrolled the page.
   bool user_scrolled_ = false;
+  // Indicates that we should scroll into view the first match that we find, set
+  // to true each time the anchor is invoked if the user hasn't scrolled.
   bool first_match_needs_scroll_ = false;
+  // Whether we successfully scrolled into view a match at least once, used for
+  // metrics reporting.
+  bool did_scroll_into_view_ = false;
 
   Member<TextFragmentAnchorMetrics> metrics_;
 
diff --git a/third_party/blink/renderer/core/style/computed_style_test.cc b/third_party/blink/renderer/core/style/computed_style_test.cc
index 8ad4313..cb14340 100644
--- a/third_party/blink/renderer/core/style/computed_style_test.cc
+++ b/third_party/blink/renderer/core/style/computed_style_test.cc
@@ -504,8 +504,8 @@
   dummy_page_holder_->GetDocument().GetSettings()->SetPreferredColorScheme(
       PreferredColorScheme::kDark);
   StyleResolverState state(dummy_page_holder_->GetDocument(),
-                           nullptr /* element */, nullptr /* pseudo_element */,
-                           initial, initial);
+                           *dummy_page_holder_->GetDocument().documentElement(),
+                           nullptr /* pseudo_element */, initial, initial);
 
   scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
   state.SetStyle(style);
diff --git a/third_party/blink/renderer/devtools/front_end/axe_core_test_runner/AxeCoreTestRunner.js b/third_party/blink/renderer/devtools/front_end/axe_core_test_runner/AxeCoreTestRunner.js
index ee8ae85..1901d8b 100644
--- a/third_party/blink/renderer/devtools/front_end/axe_core_test_runner/AxeCoreTestRunner.js
+++ b/third_party/blink/renderer/devtools/front_end/axe_core_test_runner/AxeCoreTestRunner.js
@@ -7,6 +7,18 @@
  * @suppress {accessControls}
  */
 
+const DEFAULT_CONFIG = {
+  checks: [
+    // This is a workaround for a bug in our version of axe-core
+    // which does not support aria-placeholder.
+    // Any attribute included in the options array will be
+    // ignored by the 'aria-valid-attr' rule.
+    // This should be removed after axe-core is updated.
+    // See: https://github.com/dequelabs/axe-core/issues/1457
+    {id: 'aria-valid-attr', options: ['aria-placeholder']}
+  ]
+};
+
 AxeCoreTestRunner.processAxeResult = function(violations) {
   const result = violations.map(function(rule) {
     return {
@@ -30,7 +42,9 @@
   return list;
 };
 
-AxeCoreTestRunner.runValidation = async function(element, rules) {
+AxeCoreTestRunner.runValidation = async function(element, rules, config) {
+  axe.configure(Object.assign({}, DEFAULT_CONFIG, config));
+
   try {
     const results = await axe.run(element, {rules});
     const violations = AxeCoreTestRunner.processAxeResult(results.violations);
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
index 13ce7f0e..4b4daa8 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
@@ -539,7 +539,7 @@
       font_style->SetFontDescription(element_font_description);
       font_style->GetFont().Update(font_style->GetFont().GetFontSelector());
       canvas()->GetDocument().EnsureStyleResolver().ComputeFont(
-          font_style.get(), *parsed_style);
+          *canvas(), font_style.get(), *parsed_style);
 
       // We need to reset Computed and Adjusted size so we skip zoom and
       // minimum font size.
@@ -558,7 +558,8 @@
     }
   } else {
     Font resolved_font;
-    if (!canvas_font_cache->GetFontUsingDefaultStyle(new_font, resolved_font))
+    if (!canvas_font_cache->GetFontUsingDefaultStyle(*canvas(), new_font,
+                                                     resolved_font))
       return;
 
     // We need to reset Computed and Adjusted size so we skip zoom and
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.cc
index 9f03fb2..03258447 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.cc
@@ -337,7 +337,7 @@
     filter_style->SetFont(font_for_filter_);
 
     StyleResolverState resolver_state(
-        style_resolution_host->GetDocument(), style_resolution_host,
+        style_resolution_host->GetDocument(), *style_resolution_host,
         nullptr /* pseudo_element */, filter_style.get(), filter_style.get());
     resolver_state.SetStyle(filter_style);
 
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_timeline_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_timeline_element.cc
index 009191a..059c6411 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_timeline_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_timeline_element.cc
@@ -218,7 +218,7 @@
   // Convert 6px into ratio respect to progress bar width since
   // current_position is range from 0 to 1
   double width = TrackWidth() / ZoomFactor();
-  if (width != 0 && current_position != 0) {
+  if (width != 0 && current_position != 0 && !MediaElement().ended()) {
     double offset = kThumbRadius / width;
     current_position += offset - (2 * offset * current_position);
   }
diff --git a/third_party/blink/renderer/modules/mediastream/video_track_adapter.h b/third_party/blink/renderer/modules/mediastream/video_track_adapter.h
index 21d5847..5f4fe83 100644
--- a/third_party/blink/renderer/modules/mediastream/video_track_adapter.h
+++ b/third_party/blink/renderer/modules/mediastream/video_track_adapter.h
@@ -15,8 +15,8 @@
 #include "base/time/time.h"
 #include "media/base/video_frame.h"
 #include "third_party/blink/public/platform/modules/mediastream/media_stream_types.h"
-#include "third_party/blink/public/platform/web_common.h"
 #include "third_party/blink/public/web/modules/mediastream/media_stream_video_track.h"
+#include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
 #include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
@@ -34,7 +34,7 @@
 // the IO-thread.
 // Adaptations is done by wrapping the original media::VideoFrame in a new
 // media::VideoFrame with a new visible_rect and natural_size.
-class BLINK_EXPORT VideoTrackAdapter
+class MODULES_EXPORT VideoTrackAdapter
     : public WTF::ThreadSafeRefCounted<VideoTrackAdapter> {
  public:
   using OnMutedCallback = base::RepeatingCallback<void(bool mute_state)>;
diff --git a/third_party/blink/renderer/modules/payments/payment_request.cc b/third_party/blink/renderer/modules/payments/payment_request.cc
index f6392fd..9aba1d0c 100644
--- a/third_party/blink/renderer/modules/payments/payment_request.cc
+++ b/third_party/blink/renderer/modules/payments/payment_request.cc
@@ -416,11 +416,6 @@
 
   if (supported_method == "basic-card") {
     SetBasicCardMethodData(input, output, exception_state);
-    if (exception_state.HadException()) {
-      UseCounter::Count(&execution_context,
-                        WebFeature::kInvalidBasicCardMethodData);
-      exception_state.ClearException();
-    }
   }
 }
 
diff --git a/third_party/blink/renderer/modules/webaudio/audio_param_timeline.cc b/third_party/blink/renderer/modules/webaudio/audio_param_timeline.cc
index a1a5567..847f68c 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_param_timeline.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_param_timeline.cc
@@ -1337,6 +1337,7 @@
     double time1 = current_event->Time();
 
     switch (current_event->GetType()) {
+      case ParamEvent::kCancelValues:
       case ParamEvent::kLinearRampToValue:
       case ParamEvent::kExponentialRampToValue:
       case ParamEvent::kSetValueCurveEnd:
@@ -1398,9 +1399,8 @@
         // done.
         break;
       case ParamEvent::kSetTarget:
-      case ParamEvent::kCancelValues:
-        // Nothing special needs to be done for SetTarget or
-        // CancelValues followed by CancelValues.
+        // Nothing special needs to be done for SetTarget
+        // followed by CancelValues.
         break;
       case ParamEvent::kLastType:
         NOTREACHED();
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_buffer.cc b/third_party/blink/renderer/modules/webgpu/gpu_buffer.cc
index 9658796..4ceebf6c 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_buffer.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_buffer.cc
@@ -29,7 +29,6 @@
 constexpr size_t kLargestMappableSize = v8::TypedArray::kMaxLength;
 
 bool ValidateMapSize(uint64_t buffer_size,
-                     ScriptPromiseResolver* resolver,
                      ExceptionState& exception_state) {
   if (buffer_size > kLargestMappableSize) {
     WTF::StringBuilder message_builder;
@@ -40,32 +39,79 @@
     WTF::String message = message_builder.ToString();
 
     exception_state.ThrowRangeError(message);
-    resolver->Reject(MakeGarbageCollected<DOMException>(
-        DOMExceptionCode::kOperationError, message));
-
     return false;
   }
   return true;
 }
 
+DawnBufferDescriptor AsDawnType(const GPUBufferDescriptor* webgpu_desc) {
+  DCHECK(webgpu_desc);
+
+  DawnBufferDescriptor dawn_desc;
+  dawn_desc.nextInChain = nullptr;
+  dawn_desc.usage = AsDawnEnum<DawnBufferUsageBit>(webgpu_desc->usage());
+  dawn_desc.size = webgpu_desc->size();
+
+  return dawn_desc;
+}
+
+DOMArrayBuffer* CreateArrayBufferForMappedData(void* data, size_t data_length) {
+  DCHECK(data);
+
+  WTF::ArrayBufferContents::DataHandle handle(
+      data, data_length,
+      [](void* data, size_t length, void* info) {
+        // DataDeleter does nothing because Dawn wire owns the memory.
+      },
+      nullptr);
+
+  WTF::ArrayBufferContents contents(
+      std::move(handle), WTF::ArrayBufferContents::SharingType::kNotShared);
+
+  return DOMArrayBuffer::Create(contents);
+}
+
 }  // namespace
 
 // static
 GPUBuffer* GPUBuffer::Create(GPUDevice* device,
                              const GPUBufferDescriptor* webgpu_desc) {
   DCHECK(device);
-  DCHECK(webgpu_desc);
 
-  DawnBufferDescriptor dawn_desc;
-  dawn_desc.nextInChain = nullptr;
-  dawn_desc.usage = AsDawnEnum<DawnBufferUsageBit>(webgpu_desc->usage());
-  dawn_desc.size = webgpu_desc->size();
-
+  DawnBufferDescriptor dawn_desc = AsDawnType(webgpu_desc);
   return MakeGarbageCollected<GPUBuffer>(
       device, dawn_desc.size,
       device->GetProcs().deviceCreateBuffer(device->GetHandle(), &dawn_desc));
 }
 
+// static
+std::pair<GPUBuffer*, DOMArrayBuffer*> GPUBuffer::CreateMapped(
+    GPUDevice* device,
+    const GPUBufferDescriptor* webgpu_desc,
+    ExceptionState& exception_state) {
+  DCHECK(device);
+
+  DawnBufferDescriptor dawn_desc = AsDawnType(webgpu_desc);
+
+  if (!ValidateMapSize(dawn_desc.size, exception_state)) {
+    return std::make_pair(nullptr, nullptr);
+  }
+
+  DawnCreateBufferMappedResult result =
+      device->GetProcs().deviceCreateBufferMapped(device->GetHandle(),
+                                                  &dawn_desc);
+
+  GPUBuffer* gpu_buffer =
+      MakeGarbageCollected<GPUBuffer>(device, dawn_desc.size, result.buffer);
+
+  DCHECK_LE(result.dataLength, kLargestMappableSize);
+  DCHECK(result.data);
+  gpu_buffer->mapped_buffer_ = CreateArrayBufferForMappedData(
+      result.data, static_cast<size_t>(result.dataLength));
+
+  return std::make_pair(gpu_buffer, gpu_buffer->mapped_buffer_);
+}
+
 GPUBuffer::GPUBuffer(GPUDevice* device, uint64_t size, DawnBuffer buffer)
     : DawnObject<DawnBuffer>(device, buffer), size_(size) {}
 
@@ -114,21 +160,9 @@
     case DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS:
       DCHECK(data);
       DCHECK_LE(data_length, kLargestMappableSize);
-      {
-        WTF::ArrayBufferContents::DataHandle handle(
-            data, static_cast<size_t>(data_length),
-            [](void* data, size_t length, void* info) {
-              // DataDeleter does nothing because Dawn wire owns the memory.
-            },
-            nullptr);
-
-        WTF::ArrayBufferContents contents(
-            std::move(handle),
-            WTF::ArrayBufferContents::SharingType::kNotShared);
-
-        mapped_buffer_ = DOMArrayBuffer::Create(contents);
-        resolver->Resolve(mapped_buffer_);
-      }
+      mapped_buffer_ = CreateArrayBufferForMappedData(
+          data, static_cast<size_t>(data_length));
+      resolver->Resolve(mapped_buffer_);
       break;
     case DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR:
       resolver->Reject(MakeGarbageCollected<DOMException>(
@@ -153,7 +187,8 @@
       MakeGarbageCollected<ScriptPromiseResolver>(script_state);
   ScriptPromise promise = resolver->Promise();
 
-  if (!ValidateMapSize(size_, resolver, exception_state)) {
+  if (!ValidateMapSize(size_, exception_state)) {
+    resolver->Reject(exception_state);
     return promise;
   }
 
@@ -187,7 +222,8 @@
       MakeGarbageCollected<ScriptPromiseResolver>(script_state);
   ScriptPromise promise = resolver->Promise();
 
-  if (!ValidateMapSize(size_, resolver, exception_state)) {
+  if (!ValidateMapSize(size_, exception_state)) {
+    resolver->Reject(exception_state);
     return promise;
   }
 
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_buffer.h b/third_party/blink/renderer/modules/webgpu/gpu_buffer.h
index 15547fa..c81f857 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_buffer.h
+++ b/third_party/blink/renderer/modules/webgpu/gpu_buffer.h
@@ -22,6 +22,10 @@
  public:
   static GPUBuffer* Create(GPUDevice* device,
                            const GPUBufferDescriptor* webgpu_desc);
+  static std::pair<GPUBuffer*, DOMArrayBuffer*> CreateMapped(
+      GPUDevice* device,
+      const GPUBufferDescriptor* webgpu_desc,
+      ExceptionState& exception_state);
   explicit GPUBuffer(GPUDevice* device, uint64_t size, DawnBuffer buffer);
   ~GPUBuffer() override;
 
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_device.cc b/third_party/blink/renderer/modules/webgpu/gpu_device.cc
index 3b76bec..e7d7f420 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_device.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_device.cc
@@ -6,6 +6,7 @@
 
 #include "gpu/command_buffer/client/webgpu_interface.h"
 #include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
 #include "third_party/blink/renderer/modules/webgpu/gpu_adapter.h"
@@ -78,6 +79,61 @@
   return GPUBuffer::Create(this, descriptor);
 }
 
+WTF::Vector<ScriptValue> GPUDevice::createBufferMapped(
+    ScriptState* script_state,
+    const GPUBufferDescriptor* descriptor,
+    ExceptionState& exception_state) {
+  GPUBuffer* gpu_buffer;
+  DOMArrayBuffer* array_buffer;
+  std::tie(gpu_buffer, array_buffer) =
+      GPUBuffer::CreateMapped(this, descriptor, exception_state);
+
+  v8::Isolate* isolate = script_state->GetIsolate();
+  v8::Local<v8::Object> creation_context = script_state->GetContext()->Global();
+
+  return WTF::Vector<ScriptValue>({
+      ScriptValue(script_state, ToV8(gpu_buffer, creation_context, isolate)),
+      ScriptValue(script_state, ToV8(array_buffer, creation_context, isolate)),
+  });
+}
+
+ScriptPromise GPUDevice::createBufferMappedAsync(
+    ScriptState* script_state,
+    const GPUBufferDescriptor* descriptor,
+    ExceptionState& exception_state) {
+  GPUBuffer* gpu_buffer;
+  DOMArrayBuffer* array_buffer;
+  std::tie(gpu_buffer, array_buffer) =
+      GPUBuffer::CreateMapped(this, descriptor, exception_state);
+
+  v8::Isolate* isolate = script_state->GetIsolate();
+  v8::Local<v8::Object> creation_context = script_state->GetContext()->Global();
+
+  v8::Local<v8::Value> elements[] = {
+      ToV8(gpu_buffer, creation_context, isolate),
+      ToV8(array_buffer, creation_context, isolate),
+  };
+
+  ScriptValue result(script_state, v8::Array::New(isolate, elements, 2));
+
+  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
+  ScriptPromise promise = resolver->Promise();
+  // TODO(enga): CreateBufferMappedAsync is intended to spend more time to
+  // create an optimal mapping for the buffer. It resolves the promise when the
+  // mapping is complete. Currently, there is always a staging buffer in the
+  // wire so this is already the optimal path and the promise is immediately
+  // resolved. When we can create a buffer such that the memory is mapped
+  // directly in the renderer process, this promise should be resolved
+  // asynchronously.
+
+  if (exception_state.HadException()) {
+    resolver->Reject(exception_state);
+  } else {
+    resolver->Resolve(result);
+  }
+  return promise;
+}
+
 GPUTexture* GPUDevice::createTexture(const GPUTextureDescriptor* descriptor) {
   return GPUTexture::Create(this, descriptor);
 }
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_device.h b/third_party/blink/renderer/modules/webgpu/gpu_device.h
index d3a1c26..e0026831 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_device.h
+++ b/third_party/blink/renderer/modules/webgpu/gpu_device.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGPU_GPU_DEVICE_H_
 
 #include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/modules/webgpu/dawn_callback.h"
 #include "third_party/blink/renderer/modules/webgpu/dawn_object.h"
 
@@ -59,6 +60,13 @@
   GPUAdapter* adapter() const;
 
   GPUBuffer* createBuffer(const GPUBufferDescriptor* descriptor);
+  WTF::Vector<ScriptValue> createBufferMapped(
+      ScriptState* script_state,
+      const GPUBufferDescriptor* descriptor,
+      ExceptionState& exception_state);
+  ScriptPromise createBufferMappedAsync(ScriptState* script_state,
+                                        const GPUBufferDescriptor* descriptor,
+                                        ExceptionState& exception_state);
   GPUTexture* createTexture(const GPUTextureDescriptor* descriptor);
   GPUSampler* createSampler(const GPUSamplerDescriptor* descriptor);
 
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_device.idl b/third_party/blink/renderer/modules/webgpu/gpu_device.idl
index acdb3f1..b44cdc4 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_device.idl
+++ b/third_party/blink/renderer/modules/webgpu/gpu_device.idl
@@ -10,6 +10,8 @@
     readonly attribute GPUAdapter adapter;
 
     GPUBuffer createBuffer(GPUBufferDescriptor descriptor);
+    [CallWith=ScriptState, RaisesException] GPUMappedBuffer createBufferMapped(GPUBufferDescriptor descriptor);
+    [CallWith=ScriptState, RaisesException] Promise<GPUMappedBuffer> createBufferMappedAsync(GPUBufferDescriptor descriptor);
     GPUTexture createTexture(GPUTextureDescriptor descriptor);
     GPUSampler createSampler(GPUSamplerDescriptor descriptor);
 
@@ -26,4 +28,5 @@
     GPUQueue getQueue();
 };
 
+typedef sequence<any> GPUMappedBuffer;  // [GPUBuffer, ArrayBuffer]
 typedef (GPUOutOfMemoryError or GPUValidationError) GPUError;
diff --git a/third_party/blink/renderer/modules/xr/xr_frame_provider.cc b/third_party/blink/renderer/modules/xr/xr_frame_provider.cc
index bfcbad5..b1740af 100644
--- a/third_party/blink/renderer/modules/xr/xr_frame_provider.cc
+++ b/third_party/blink/renderer/modules/xr/xr_frame_provider.cc
@@ -399,10 +399,10 @@
     if (frame_data) {
       immersive_session_->OnFrame(high_res_now_ms, std::move(pose_matrix),
                                   buffer_mailbox_holder_,
-                                  frame_data->detected_planes);
+                                  frame_data->detected_planes_data);
     } else {
       immersive_session_->OnFrame(high_res_now_ms, std::move(pose_matrix),
-                                  buffer_mailbox_holder_, base::nullopt);
+                                  buffer_mailbox_holder_, nullptr);
     }
   } else {
     // In the process of fulfilling the frame requests for each session they are
@@ -428,10 +428,10 @@
           getPoseMatrix(frame_pose_);
       if (frame_data) {
         session->OnFrame(high_res_now_ms, std::move(pose_matrix), base::nullopt,
-                         frame_data->detected_planes);
+                         frame_data->detected_planes_data);
       } else {
         session->OnFrame(high_res_now_ms, std::move(pose_matrix), base::nullopt,
-                         base::nullopt);
+                         nullptr);
       }
     }
 
diff --git a/third_party/blink/renderer/modules/xr/xr_plane.cc b/third_party/blink/renderer/modules/xr/xr_plane.cc
index 2e9543f..06ad1bb 100644
--- a/third_party/blink/renderer/modules/xr/xr_plane.cc
+++ b/third_party/blink/renderer/modules/xr/xr_plane.cc
@@ -11,22 +11,26 @@
 namespace blink {
 
 XRPlane::XRPlane(XRSession* session,
-                 const device::mojom::blink::XRPlaneDataPtr& plane_data)
+                 const device::mojom::blink::XRPlaneDataPtr& plane_data,
+                 double timestamp)
     : XRPlane(session,
               mojo::ConvertTo<base::Optional<blink::XRPlane::Orientation>>(
                   plane_data->orientation),
               mojo::ConvertTo<blink::TransformationMatrix>(plane_data->pose),
               mojo::ConvertTo<HeapVector<Member<DOMPointReadOnly>>>(
-                  plane_data->polygon)) {}
+                  plane_data->polygon),
+              timestamp) {}
 
 XRPlane::XRPlane(XRSession* session,
                  const base::Optional<Orientation>& orientation,
                  const TransformationMatrix& pose_matrix,
-                 const HeapVector<Member<DOMPointReadOnly>>& polygon)
+                 const HeapVector<Member<DOMPointReadOnly>>& polygon,
+                 double timestamp)
     : polygon_(polygon),
       orientation_(orientation),
       pose_matrix_(std::make_unique<TransformationMatrix>(pose_matrix)),
-      session_(session) {
+      session_(session),
+      last_changed_time_(timestamp) {
   DVLOG(3) << __func__;
 }
 
@@ -49,6 +53,10 @@
   return "";
 }
 
+double XRPlane::lastChangedTime() const {
+  return last_changed_time_;
+}
+
 HeapVector<Member<DOMPointReadOnly>> XRPlane::polygon() const {
   // Returns copy of a vector - by design. This way, JavaScript code could
   // store the state of the plane's polygon in frame N just by storing the
@@ -57,9 +65,12 @@
   return polygon_;
 }
 
-void XRPlane::Update(const device::mojom::blink::XRPlaneDataPtr& plane_data) {
+void XRPlane::Update(const device::mojom::blink::XRPlaneDataPtr& plane_data,
+                     double timestamp) {
   DVLOG(3) << __func__;
 
+  last_changed_time_ = timestamp;
+
   orientation_ = mojo::ConvertTo<base::Optional<blink::XRPlane::Orientation>>(
       plane_data->orientation);
   pose_matrix_ = std::make_unique<TransformationMatrix>(
diff --git a/third_party/blink/renderer/modules/xr/xr_plane.h b/third_party/blink/renderer/modules/xr/xr_plane.h
index 3f7098d..69e98588 100644
--- a/third_party/blink/renderer/modules/xr/xr_plane.h
+++ b/third_party/blink/renderer/modules/xr/xr_plane.h
@@ -26,22 +26,26 @@
   enum Orientation { kHorizontal, kVertical };
 
   XRPlane(XRSession* session,
-          const device::mojom::blink::XRPlaneDataPtr& plane_data);
+          const device::mojom::blink::XRPlaneDataPtr& plane_data,
+          double timestamp);
   XRPlane(XRSession* session,
           const base::Optional<Orientation>& orientation,
           const TransformationMatrix& pose_matrix,
-          const HeapVector<Member<DOMPointReadOnly>>& polygon);
+          const HeapVector<Member<DOMPointReadOnly>>& polygon,
+          double timestamp);
 
   // Returns a pose expressed in passed in reference space.
   XRPose* getPose(XRReferenceSpace* reference_space) const;
 
   String orientation() const;
   HeapVector<Member<DOMPointReadOnly>> polygon() const;
+  double lastChangedTime() const;
 
   // Updates plane data from passed in |plane_data|. The resulting instance
   // should be equivalent to the instance that would be create by calling
   // XRPlane(plane_data).
-  void Update(const device::mojom::blink::XRPlaneDataPtr& plane_data);
+  void Update(const device::mojom::blink::XRPlaneDataPtr& plane_data,
+              double timestamp);
 
   void Trace(blink::Visitor* visitor) override;
 
@@ -53,6 +57,8 @@
   std::unique_ptr<TransformationMatrix> pose_matrix_;
 
   Member<XRSession> session_;
+
+  double last_changed_time_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/xr/xr_plane.idl b/third_party/blink/renderer/modules/xr/xr_plane.idl
index 32540277..50c0586 100644
--- a/third_party/blink/renderer/modules/xr/xr_plane.idl
+++ b/third_party/blink/renderer/modules/xr/xr_plane.idl
@@ -18,4 +18,5 @@
     XRPose getPose(XRReferenceSpace referenceSpace);
     readonly attribute FrozenArray<DOMPointReadOnly> polygon;
     readonly attribute XRPlaneOrientation? orientation;
+    readonly attribute DOMHighResTimeStamp lastChangedTime;
 };
diff --git a/third_party/blink/renderer/modules/xr/xr_session.cc b/third_party/blink/renderer/modules/xr/xr_session.cc
index ea4dec3..2051c835 100644
--- a/third_party/blink/renderer/modules/xr/xr_session.cc
+++ b/third_party/blink/renderer/modules/xr/xr_session.cc
@@ -632,8 +632,7 @@
     double timestamp,
     std::unique_ptr<TransformationMatrix> base_pose_matrix,
     const base::Optional<gpu::MailboxHolder>& output_mailbox_holder,
-    const base::Optional<WTF::Vector<device::mojom::blink::XRPlaneDataPtr>>&
-        detected_planes) {
+    const device::mojom::blink::XRPlaneDetectionDataPtr& detected_planes_data) {
   TRACE_EVENT0("gpu", __FUNCTION__);
   DVLOG(2) << __FUNCTION__;
   // Don't process any outstanding frames once the session is ended.
@@ -645,7 +644,7 @@
   // If there are pending render state changes, apply them now.
   ApplyPendingRenderState();
 
-  world_information_->ProcessPlaneInformation(detected_planes);
+  world_information_->ProcessPlaneInformation(detected_planes_data, timestamp);
 
   if (pending_frame_) {
     pending_frame_ = false;
diff --git a/third_party/blink/renderer/modules/xr/xr_session.h b/third_party/blink/renderer/modules/xr/xr_session.h
index 6dbfeb1..1e84876 100644
--- a/third_party/blink/renderer/modules/xr/xr_session.h
+++ b/third_party/blink/renderer/modules/xr/xr_session.h
@@ -124,12 +124,12 @@
   const AtomicString& InterfaceName() const override;
 
   void OnFocusChanged();
-  void OnFrame(
-      double timestamp,
-      std::unique_ptr<TransformationMatrix>,
-      const base::Optional<gpu::MailboxHolder>& output_mailbox_holder,
-      const base::Optional<WTF::Vector<device::mojom::blink::XRPlaneDataPtr>>&
-          detected_planes);
+  void OnFrame(double timestamp,
+               std::unique_ptr<TransformationMatrix>,
+               const base::Optional<gpu::MailboxHolder>& output_mailbox_holder,
+               const device::mojom::blink::XRPlaneDetectionDataPtr&
+                   detected_planes_data);
+
   void OnInputStateChange(
       int16_t frame_id,
       const WTF::Vector<device::mojom::blink::XRInputSourceStatePtr>&);
diff --git a/third_party/blink/renderer/modules/xr/xr_world_information.cc b/third_party/blink/renderer/modules/xr/xr_world_information.cc
index 78608602..36ae87fc 100644
--- a/third_party/blink/renderer/modules/xr/xr_world_information.cc
+++ b/third_party/blink/renderer/modules/xr/xr_world_information.cc
@@ -35,10 +35,12 @@
 }
 
 void XRWorldInformation::ProcessPlaneInformation(
-    const base::Optional<WTF::Vector<device::mojom::blink::XRPlaneDataPtr>>&
-        detected_planes) {
-  if (!detected_planes.has_value()) {
-    DVLOG(3) << __func__ << ": detected_planes is null";
+    const device::mojom::blink::XRPlaneDetectionDataPtr& detected_planes_data,
+    double timestamp) {
+  TRACE_EVENT0("xr", __FUNCTION__);
+
+  if (!detected_planes_data) {
+    DVLOG(3) << __func__ << ": detected_planes_data is null";
 
     // We have received a nullopt - plane detection is not supported or
     // disabled. Mark detected_planes as null & clear stored planes.
@@ -47,19 +49,42 @@
     return;
   }
 
-  DVLOG(3) << __func__ << ": detected_planes size=" << detected_planes->size();
+  TRACE_COUNTER2("xr", "Plane statistics", "All planes",
+                 detected_planes_data->all_planes_ids.size(), "Updated planes",
+                 detected_planes_data->updated_planes_data.size());
+
+  DVLOG(3) << __func__ << ": updated planes size="
+           << detected_planes_data->updated_planes_data.size()
+           << ", all planes size="
+           << detected_planes_data->all_planes_ids.size();
 
   is_detected_planes_null_ = false;
 
   HeapHashMap<int32_t, Member<XRPlane>> updated_planes;
-  for (const auto& plane : *detected_planes) {
+
+  // First, process all planes that had their information updated (new planes
+  // are also processed here).
+  for (const auto& plane : detected_planes_data->updated_planes_data) {
     auto it = plane_ids_to_planes_.find(plane->id);
     if (it != plane_ids_to_planes_.end()) {
       updated_planes.insert(plane->id, it->value);
-      it->value->Update(plane);
+      it->value->Update(plane, timestamp);
     } else {
-      updated_planes.insert(plane->id,
-                            MakeGarbageCollected<XRPlane>(session_, plane));
+      updated_planes.insert(
+          plane->id, MakeGarbageCollected<XRPlane>(session_, plane, timestamp));
+    }
+  }
+
+  // Then, copy over the planes that were not updated but are still present.
+  for (const auto& plane_id : detected_planes_data->all_planes_ids) {
+    auto it_updated = updated_planes.find(plane_id);
+
+    // If the plane was already updated, there is nothing to do as it was
+    // already moved to |updated_planes|. Otherwise just copy it over as-is.
+    if (it_updated == updated_planes.end()) {
+      auto it = plane_ids_to_planes_.find(plane_id);
+      DCHECK(it != plane_ids_to_planes_.end());
+      updated_planes.insert(plane_id, it->value);
     }
   }
 
diff --git a/third_party/blink/renderer/modules/xr/xr_world_information.h b/third_party/blink/renderer/modules/xr/xr_world_information.h
index 801ef99..8807472 100644
--- a/third_party/blink/renderer/modules/xr/xr_world_information.h
+++ b/third_party/blink/renderer/modules/xr/xr_world_information.h
@@ -28,8 +28,8 @@
   // the received frame data. This will update the contents of
   // plane_ids_to_planes_.
   void ProcessPlaneInformation(
-      const base::Optional<WTF::Vector<device::mojom::blink::XRPlaneDataPtr>>&
-          detected_planes);
+      const device::mojom::blink::XRPlaneDetectionDataPtr& detected_planes_data,
+      double timestamp);
 
  private:
   // Signifies if we should return null from `detectedPlanes()`.
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index a29cdce..1f23d692 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -465,8 +465,6 @@
     "cursor.h",
     "data_resource_helper.cc",
     "data_resource_helper.h",
-    "date_components.cc",
-    "date_components.h",
     "exported/file_path_conversion.cc",
     "exported/interface_registry.cc",
     "exported/mediastream/media_stream_audio_level_calculator.cc",
@@ -1249,6 +1247,8 @@
     "text/character_emoji.cc",
     "text/character_property.h",
     "text/character_property_data.h",
+    "text/date_components.cc",
+    "text/date_components.h",
     "text/date_time_format.cc",
     "text/date_time_format.h",
     "text/decode_escape_sequences.h",
@@ -1422,7 +1422,7 @@
     "//mojo/public/cpp/bindings",
     "//mojo/public/cpp/bindings:wtf_support",
     "//services/service_manager/public/cpp",
-    "//services/ws/public/cpp/gpu",
+    "//services/viz/public/cpp/gpu",
     "//skia:skcms",
     "//third_party:freetype_harfbuzz",
     "//third_party/blink/public/common",
diff --git a/third_party/blink/renderer/platform/animation/compositor_keyframe_model.cc b/third_party/blink/renderer/platform/animation/compositor_keyframe_model.cc
index ac68226f..95d2534 100644
--- a/third_party/blink/renderer/platform/animation/compositor_keyframe_model.cc
+++ b/third_party/blink/renderer/platform/animation/compositor_keyframe_model.cc
@@ -24,15 +24,16 @@
     const CompositorAnimationCurve& curve,
     compositor_target_property::Type target_property,
     int keyframe_model_id,
-    int group_id) {
+    int group_id,
+    const AtomicString& custom_property_name) {
   if (!keyframe_model_id)
     keyframe_model_id = AnimationIdProvider::NextKeyframeModelId();
   if (!group_id)
     group_id = AnimationIdProvider::NextGroupId();
 
-  keyframe_model_ =
-      KeyframeModel::Create(curve.CloneToAnimationCurve(), keyframe_model_id,
-                            group_id, target_property);
+  keyframe_model_ = KeyframeModel::Create(
+      curve.CloneToAnimationCurve(), keyframe_model_id, group_id,
+      target_property, custom_property_name.Utf8().data());
 }
 
 CompositorKeyframeModel::~CompositorKeyframeModel() = default;
diff --git a/third_party/blink/renderer/platform/animation/compositor_keyframe_model.h b/third_party/blink/renderer/platform/animation/compositor_keyframe_model.h
index 673cbb0..52db455 100644
--- a/third_party/blink/renderer/platform/animation/compositor_keyframe_model.h
+++ b/third_party/blink/renderer/platform/animation/compositor_keyframe_model.h
@@ -14,6 +14,7 @@
 #include "third_party/blink/renderer/platform/graphics/compositor_element_id.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
 
 namespace cc {
 class KeyframeModel;
@@ -32,10 +33,14 @@
   using Direction = cc::KeyframeModel::Direction;
   using FillMode = cc::KeyframeModel::FillMode;
 
+  // The |custom_property_name| has a default value of an empty string,
+  // indicating that the animated property is a native property. When it is an
+  // animated custom property, it should be the property name.
   CompositorKeyframeModel(const CompositorAnimationCurve&,
                           compositor_target_property::Type,
                           int keyframe_model_id,
-                          int group_id);
+                          int group_id,
+                          const AtomicString& custom_property_name = "");
   ~CompositorKeyframeModel();
 
   // An id must be unique.
@@ -74,6 +79,10 @@
 
   std::unique_ptr<CompositorFloatAnimationCurve> FloatCurveForTesting() const;
 
+  const std::string& GetCustomPropertyNameForTesting() const {
+    return keyframe_model_->GetCustomPropertyNameForTesting();
+  }
+
  private:
   std::unique_ptr<cc::KeyframeModel> keyframe_model_;
 
diff --git a/third_party/blink/renderer/platform/graphics/DEPS b/third_party/blink/renderer/platform/graphics/DEPS
index d82a5d9a..3407da6 100644
--- a/third_party/blink/renderer/platform/graphics/DEPS
+++ b/third_party/blink/renderer/platform/graphics/DEPS
@@ -28,7 +28,7 @@
     "+media/base/video_types.h",
     "+media/renderers/video_resource_updater.h",
     "+services/viz/public/interfaces",
-    "+services/ws/public/cpp/gpu/context_provider_command_buffer.h",
+    "+services/viz/public/cpp/gpu/context_provider_command_buffer.h",
     "+third_party/blink/renderer/platform/cpu/mips/common_macros_msa.h",
     "+third_party/blink/renderer/platform/fonts",
     "+third_party/blink/renderer/platform/geometry",
diff --git a/third_party/blink/renderer/platform/graphics/begin_frame_provider.cc b/third_party/blink/renderer/platform/graphics/begin_frame_provider.cc
index e071c92..cd8c3e8 100644
--- a/third_party/blink/renderer/platform/graphics/begin_frame_provider.cc
+++ b/third_party/blink/renderer/platform/graphics/begin_frame_provider.cc
@@ -6,6 +6,7 @@
 
 #include "base/bind.h"
 #include "base/single_thread_task_runner.h"
+#include "services/viz/public/interfaces/compositing/frame_timing_details.mojom-blink.h"
 #include "third_party/blink/public/platform/interface_provider.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
@@ -95,7 +96,7 @@
 
 void BeginFrameProvider::OnBeginFrame(
     const viz::BeginFrameArgs& args,
-    WTF::HashMap<uint32_t, ::gfx::mojom::blink::PresentationFeedbackPtr>) {
+    WTF::HashMap<uint32_t, ::viz::mojom::blink::FrameTimingDetailsPtr>) {
   // If there was no need for a BeginFrame, just skip it.
   if (needs_begin_frame_ && requested_needs_begin_frame_) {
     requested_needs_begin_frame_ = false;
diff --git a/third_party/blink/renderer/platform/graphics/begin_frame_provider.h b/third_party/blink/renderer/platform/graphics/begin_frame_provider.h
index 5d6457d..2c0edcf 100644
--- a/third_party/blink/renderer/platform/graphics/begin_frame_provider.h
+++ b/third_party/blink/renderer/platform/graphics/begin_frame_provider.h
@@ -43,8 +43,7 @@
   }
   void OnBeginFrame(
       const viz::BeginFrameArgs&,
-      WTF::HashMap<uint32_t, ::gfx::mojom::blink::PresentationFeedbackPtr>)
-      final;
+      WTF::HashMap<uint32_t, ::viz::mojom::blink::FrameTimingDetailsPtr>) final;
   void OnBeginFramePausedChanged(bool paused) final {}
   void ReclaimResources(
       const WTF::Vector<viz::ReturnedResource>& resources) final {
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc
index 33357b7e..b21bda3 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc
@@ -11,6 +11,7 @@
 #include "components/viz/common/quads/texture_draw_quad.h"
 #include "components/viz/common/resources/resource_format.h"
 #include "components/viz/common/resources/single_release_callback.h"
+#include "services/viz/public/interfaces/compositing/frame_timing_details.mojom-blink.h"
 #include "services/viz/public/interfaces/hit_test/hit_test_region_list.mojom-blink.h"
 #include "third_party/blink/public/mojom/frame_sinks/embedded_frame_sink.mojom-blink.h"
 #include "third_party/blink/public/platform/interface_provider.h"
@@ -329,7 +330,7 @@
 
 void CanvasResourceDispatcher::OnBeginFrame(
     const viz::BeginFrameArgs& begin_frame_args,
-    WTF::HashMap<uint32_t, ::gfx::mojom::blink::PresentationFeedbackPtr>) {
+    WTF::HashMap<uint32_t, ::viz::mojom::blink::FrameTimingDetailsPtr>) {
   current_begin_frame_ack_ = viz::BeginFrameAck(begin_frame_args, false);
   if (pending_compositor_frames_ >= kMaxPendingCompositorFrames ||
       (begin_frame_args.type == viz::BeginFrameArgs::MISSED &&
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h
index 323e932d..6abe9a4 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h
@@ -66,8 +66,7 @@
       const WTF::Vector<viz::ReturnedResource>& resources) final;
   void OnBeginFrame(
       const viz::BeginFrameArgs&,
-      WTF::HashMap<uint32_t, ::gfx::mojom::blink::PresentationFeedbackPtr>)
-      final;
+      WTF::HashMap<uint32_t, ::viz::mojom::blink::FrameTimingDetailsPtr>) final;
   void OnBeginFramePausedChanged(bool paused) final {}
   void ReclaimResources(
       const WTF::Vector<viz::ReturnedResource>& resources) final;
diff --git a/third_party/blink/renderer/platform/graphics/dark_mode_color_classifier.cc b/third_party/blink/renderer/platform/graphics/dark_mode_color_classifier.cc
index 5f95f43..1eec760 100644
--- a/third_party/blink/renderer/platform/graphics/dark_mode_color_classifier.cc
+++ b/third_party/blink/renderer/platform/graphics/dark_mode_color_classifier.cc
@@ -5,6 +5,39 @@
 #include "third_party/blink/renderer/platform/graphics/dark_mode_color_classifier.h"
 
 namespace blink {
+namespace {
+
+class SimpleColorClassifier : public DarkModeColorClassifier {
+ public:
+  static std::unique_ptr<SimpleColorClassifier> NeverInvert() {
+    return std::unique_ptr<SimpleColorClassifier>(
+        new SimpleColorClassifier(false));
+  }
+
+  static std::unique_ptr<SimpleColorClassifier> AlwaysInvert() {
+    return std::unique_ptr<SimpleColorClassifier>(
+        new SimpleColorClassifier(true));
+  }
+
+  bool ShouldInvertColor(const Color& color) override { return value_; }
+
+ private:
+  SimpleColorClassifier(bool value) : value_(value) {}
+
+  bool value_;
+};
+
+class InvertLowBrightnessColorsClassifier : public DarkModeColorClassifier {
+ public:
+  bool ShouldInvertColor(const Color& color) override {
+    if (color == Color::kWhite) {
+      return false;
+    }
+    return true;
+  }
+};
+
+}  // namespace
 
 // Values below which a color is considered sufficiently transparent that a
 // lighter color behind it would make the final color as seen by the user light.
@@ -27,4 +60,18 @@
   return lightness > 0.5;
 }
 
+std::unique_ptr<DarkModeColorClassifier>
+DarkModeColorClassifier::MakeTextColorClassifier(
+    const DarkModeSettings& settings) {
+  if (settings.text_policy == DarkModeTextPolicy::kInvertAll)
+    return SimpleColorClassifier::AlwaysInvert();
+
+  // Throw an error in debug mode if new values are added to the enum without
+  // updating this method.
+  DCHECK_EQ(settings.text_policy, DarkModeTextPolicy::kInvertDarkOnly);
+  return std::make_unique<InvertLowBrightnessColorsClassifier>();
+}
+
+DarkModeColorClassifier::~DarkModeColorClassifier() {}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/dark_mode_color_classifier.h b/third_party/blink/renderer/platform/graphics/dark_mode_color_classifier.h
index 1378e18..1d89b283 100644
--- a/third_party/blink/renderer/platform/graphics/dark_mode_color_classifier.h
+++ b/third_party/blink/renderer/platform/graphics/dark_mode_color_classifier.h
@@ -5,13 +5,32 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_DARK_MODE_COLOR_CLASSIFIER_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_DARK_MODE_COLOR_CLASSIFIER_H_
 
+#include <memory>
+
 #include "third_party/blink/renderer/platform/graphics/color.h"
+#include "third_party/blink/renderer/platform/graphics/dark_mode_settings.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 
 namespace blink {
 
 bool PLATFORM_EXPORT IsLight(const Color& color);
 
+class DarkModeColorClassifier {
+ public:
+  // TODO(https://crbug.com/968340): Add methods to create classifiers for other
+  // types of elements/shapes.
+  static std::unique_ptr<DarkModeColorClassifier> MakeTextColorClassifier(
+      const DarkModeSettings& settings);
+
+  virtual ~DarkModeColorClassifier();
+
+  // TODO(https://crbug.com/968340): Include element opacity when determining
+  // whether to invert a color. The background is likely to be dark, so a lower
+  // opacity will usually decrease the effective brightness of both the original
+  // and the inverted colors.
+  virtual bool ShouldInvertColor(const Color& color) = 0;
+};
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_DARK_MODE_COLOR_CLASSIFIER_H_
diff --git a/third_party/blink/renderer/platform/graphics/dark_mode_filter.cc b/third_party/blink/renderer/platform/graphics/dark_mode_filter.cc
index f621340..b065b37 100644
--- a/third_party/blink/renderer/platform/graphics/dark_mode_filter.cc
+++ b/third_party/blink/renderer/platform/graphics/dark_mode_filter.cc
@@ -8,6 +8,7 @@
 
 #include "base/logging.h"
 #include "base/optional.h"
+#include "third_party/blink/renderer/platform/graphics/dark_mode_color_classifier.h"
 #include "third_party/blink/renderer/platform/graphics/dark_mode_color_filter.h"
 #include "third_party/skia/include/core/SkColorFilter.h"
 #include "third_party/skia/include/effects/SkColorMatrix.h"
@@ -65,7 +66,9 @@
 }  // namespace
 
 DarkModeFilter::DarkModeFilter()
-    : color_filter_(nullptr), image_filter_(nullptr) {
+    : text_classifier_(nullptr),
+      color_filter_(nullptr),
+      image_filter_(nullptr) {
   DarkModeSettings default_settings;
   default_settings.mode = DarkMode::kOff;
   UpdateSettings(default_settings);
@@ -96,6 +99,9 @@
     image_filter_ = MakeGrayscaleFilter(settings_.image_grayscale_percent);
   else
     image_filter_ = color_filter_->ToSkColorFilter();
+
+  text_classifier_ =
+      DarkModeColorClassifier::MakeTextColorClassifier(settings_);
 }
 
 Color DarkModeFilter::InvertColorIfNeeded(const Color& color) {
@@ -134,19 +140,11 @@
 }
 
 bool DarkModeFilter::ShouldInvertTextColor(const Color& color) const {
-  if (!IsDarkModeActive())
-    return false;
-
-  if (settings_.text_policy == DarkModeTextPolicy::kInvertAll)
-    return true;
-
-  // Throw an error in debug mode if new values are added to the enum without
-  // updating this method.
-  DCHECK_EQ(settings_.text_policy, DarkModeTextPolicy::kInvertDarkOnly);
-  if (color == Color::kWhite) {
-    return false;
+  if (IsDarkModeActive()) {
+    DCHECK(text_classifier_);
+    return text_classifier_->ShouldInvertColor(color);
   }
-  return true;
+  return false;
 }
 
 bool DarkModeFilter::IsDarkModeActive() const {
diff --git a/third_party/blink/renderer/platform/graphics/dark_mode_filter.h b/third_party/blink/renderer/platform/graphics/dark_mode_filter.h
index 2c6700d8..b328ed3 100644
--- a/third_party/blink/renderer/platform/graphics/dark_mode_filter.h
+++ b/third_party/blink/renderer/platform/graphics/dark_mode_filter.h
@@ -19,6 +19,7 @@
 
 namespace blink {
 
+class DarkModeColorClassifier;
 class DarkModeColorFilter;
 
 class PLATFORM_EXPORT DarkModeFilter {
@@ -48,6 +49,8 @@
  private:
   DarkModeSettings settings_;
 
+  std::unique_ptr<DarkModeColorClassifier> text_classifier_;
+
   std::unique_ptr<DarkModeColorFilter> color_filter_;
   sk_sp<SkColorFilter> image_filter_;
 };
diff --git a/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc b/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc
index cf35222..1ea24ca4 100644
--- a/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc
+++ b/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc
@@ -14,9 +14,9 @@
 #include "components/viz/common/resources/returned_resource.h"
 #include "media/base/video_frame.h"
 #include "media/base/video_types.h"
+#include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
 #include "services/viz/public/interfaces/compositing/compositor_frame_sink.mojom-blink.h"
 #include "services/viz/public/interfaces/hit_test/hit_test_region_list.mojom-blink.h"
-#include "services/ws/public/cpp/gpu/context_provider_command_buffer.h"
 #include "third_party/blink/public/mojom/frame_sinks/embedded_frame_sink.mojom-blink.h"
 #include "third_party/blink/public/platform/interface_provider.h"
 #include "third_party/blink/public/platform/platform.h"
@@ -172,16 +172,17 @@
 
 void VideoFrameSubmitter::OnBeginFrame(
     const viz::BeginFrameArgs& args,
-    WTF::HashMap<uint32_t, ::gfx::mojom::blink::PresentationFeedbackPtr>
-        feedbacks) {
+    WTF::HashMap<uint32_t, ::viz::mojom::blink::FrameTimingDetailsPtr>
+        timing_details) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   TRACE_EVENT0("media", "VideoFrameSubmitter::OnBeginFrame");
 
-  for (const auto& pair : feedbacks) {
+  for (const auto& pair : timing_details) {
     if (viz::FrameTokenGT(pair.key, *next_frame_token_))
       continue;
-    TRACE_EVENT_ASYNC_END_WITH_TIMESTAMP0("media", "VideoFrameSubmitter",
-                                          pair.key, pair.value->timestamp);
+    TRACE_EVENT_ASYNC_END_WITH_TIMESTAMP0(
+        "media", "VideoFrameSubmitter", pair.key,
+        pair.value->presentation_feedback->timestamp);
   }
 
   // Don't call UpdateCurrentFrame() for MISSED BeginFrames. Also don't call it
diff --git a/third_party/blink/renderer/platform/graphics/video_frame_submitter.h b/third_party/blink/renderer/platform/graphics/video_frame_submitter.h
index 54c6a06..9861caf 100644
--- a/third_party/blink/renderer/platform/graphics/video_frame_submitter.h
+++ b/third_party/blink/renderer/platform/graphics/video_frame_submitter.h
@@ -19,12 +19,12 @@
 #include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/public/cpp/system/buffer.h"
 #include "services/viz/public/interfaces/compositing/compositor_frame_sink.mojom-blink.h"
+#include "services/viz/public/interfaces/compositing/frame_timing_details.mojom-blink.h"
 #include "third_party/blink/public/mojom/frame_sinks/embedded_frame_sink.mojom-blink.h"
 #include "third_party/blink/public/platform/web_video_frame_submitter.h"
 #include "third_party/blink/renderer/platform/graphics/video_frame_resource_provider.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
-#include "ui/gfx/mojo/presentation_feedback.mojom-blink.h"
 
 namespace blink {
 
@@ -69,7 +69,7 @@
       const WTF::Vector<viz::ReturnedResource>& resources) override;
   void OnBeginFrame(
       const viz::BeginFrameArgs&,
-      WTF::HashMap<uint32_t, ::gfx::mojom::blink::PresentationFeedbackPtr>)
+      WTF::HashMap<uint32_t, ::viz::mojom::blink::FrameTimingDetailsPtr>)
       override;
   void OnBeginFramePausedChanged(bool paused) override {}
   void ReclaimResources(
diff --git a/third_party/blink/renderer/platform/mhtml/DEPS b/third_party/blink/renderer/platform/mhtml/DEPS
index 85ca52b..3b99ec5 100644
--- a/third_party/blink/renderer/platform/mhtml/DEPS
+++ b/third_party/blink/renderer/platform/mhtml/DEPS
@@ -6,7 +6,7 @@
     "+third_party/blink/renderer/platform/mhtml",
 
     # Dependencies.
-    "+third_party/blink/renderer/platform/date_components.h",
+    "+third_party/blink/renderer/platform/text/date_components.h",
     "+third_party/blink/renderer/platform/heap",
     "+third_party/blink/renderer/platform/network",
     "+third_party/blink/renderer/platform/serialized_resource.h",
diff --git a/third_party/blink/renderer/platform/mhtml/mhtml_archive.cc b/third_party/blink/renderer/platform/mhtml/mhtml_archive.cc
index 280de7ea..c0531a5 100644
--- a/third_party/blink/renderer/platform/mhtml/mhtml_archive.cc
+++ b/third_party/blink/renderer/platform/mhtml/mhtml_archive.cc
@@ -33,12 +33,12 @@
 #include <stddef.h>
 #include "base/metrics/histogram_macros.h"
 #include "build/build_config.h"
-#include "third_party/blink/renderer/platform/date_components.h"
 #include "third_party/blink/renderer/platform/mhtml/archive_resource.h"
 #include "third_party/blink/renderer/platform/mhtml/mhtml_parser.h"
 #include "third_party/blink/renderer/platform/mhtml/serialized_resource.h"
 #include "third_party/blink/renderer/platform/network/mime/mime_type_registry.h"
 #include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/text/date_components.h"
 #include "third_party/blink/renderer/platform/weborigin/scheme_registry.h"
 #include "third_party/blink/renderer/platform/wtf/assertions.h"
 #include "third_party/blink/renderer/platform/wtf/date_math.h"
diff --git a/third_party/blink/renderer/platform/text/DEPS b/third_party/blink/renderer/platform/text/DEPS
index e8f9276..e6709d4 100644
--- a/third_party/blink/renderer/platform/text/DEPS
+++ b/third_party/blink/renderer/platform/text/DEPS
@@ -7,7 +7,7 @@
 
     # Dependencies.
     "+base/mac",
-    "+third_party/blink/renderer/platform/date_components.h",
+    "+third_party/blink/renderer/platform/text/date_components.h",
     "+third_party/blink/renderer/platform/heap",
     "+third_party/blink/renderer/platform/language.h",
     "+third_party/blink/renderer/platform/platform_export.h",
diff --git a/third_party/blink/renderer/platform/date_components.cc b/third_party/blink/renderer/platform/text/date_components.cc
similarity index 99%
rename from third_party/blink/renderer/platform/date_components.cc
rename to third_party/blink/renderer/platform/text/date_components.cc
index ac4c15dd..512348d 100644
--- a/third_party/blink/renderer/platform/date_components.cc
+++ b/third_party/blink/renderer/platform/text/date_components.cc
@@ -28,7 +28,7 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "third_party/blink/renderer/platform/date_components.h"
+#include "third_party/blink/renderer/platform/text/date_components.h"
 
 #include <limits.h>
 #include "third_party/blink/renderer/platform/wtf/date_math.h"
diff --git a/third_party/blink/renderer/platform/date_components.h b/third_party/blink/renderer/platform/text/date_components.h
similarity index 97%
rename from third_party/blink/renderer/platform/date_components.h
rename to third_party/blink/renderer/platform/text/date_components.h
index 00335ee..556559a7e 100644
--- a/third_party/blink/renderer/platform/date_components.h
+++ b/third_party/blink/renderer/platform/text/date_components.h
@@ -28,8 +28,8 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_DATE_COMPONENTS_H_
-#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_DATE_COMPONENTS_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_DATE_COMPONENTS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_DATE_COMPONENTS_H_
 
 #include <limits>
 #include "third_party/blink/renderer/platform/platform_export.h"
@@ -235,4 +235,4 @@
 
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_DATE_COMPONENTS_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_DATE_COMPONENTS_H_
diff --git a/third_party/blink/renderer/platform/text/locale_icu.h b/third_party/blink/renderer/platform/text/locale_icu.h
index 1798a60..ec2cbfc 100644
--- a/third_party/blink/renderer/platform/text/locale_icu.h
+++ b/third_party/blink/renderer/platform/text/locale_icu.h
@@ -34,7 +34,7 @@
 #include <unicode/udat.h>
 #include <unicode/unum.h>
 #include <memory>
-#include "third_party/blink/renderer/platform/date_components.h"
+#include "third_party/blink/renderer/platform/text/date_components.h"
 #include "third_party/blink/renderer/platform/text/platform_locale.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
 #include "third_party/blink/renderer/platform/wtf/text/cstring.h"
diff --git a/third_party/blink/renderer/platform/text/locale_mac_test.mm b/third_party/blink/renderer/platform/text/locale_mac_test.mm
index 1418c101..94481b16 100644
--- a/third_party/blink/renderer/platform/text/locale_mac_test.mm
+++ b/third_party/blink/renderer/platform/text/locale_mac_test.mm
@@ -29,8 +29,8 @@
 #include "base/mac/mac_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/platform.h"
-#include "third_party/blink/renderer/platform/date_components.h"
 #include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
+#include "third_party/blink/renderer/platform/text/date_components.h"
 #include "third_party/blink/renderer/platform/wtf/date_math.h"
 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
 #include "third_party/blink/renderer/platform/wtf/text/cstring.h"
diff --git a/third_party/blink/renderer/platform/text/locale_win.cc b/third_party/blink/renderer/platform/text/locale_win.cc
index adbe562..b2858e4 100644
--- a/third_party/blink/renderer/platform/text/locale_win.cc
+++ b/third_party/blink/renderer/platform/text/locale_win.cc
@@ -35,8 +35,8 @@
 
 #include "base/memory/ptr_util.h"
 #include "base/stl_util.h"
-#include "third_party/blink/renderer/platform/date_components.h"
 #include "third_party/blink/renderer/platform/language.h"
+#include "third_party/blink/renderer/platform/text/date_components.h"
 #include "third_party/blink/renderer/platform/text/date_time_format.h"
 #include "third_party/blink/renderer/platform/web_test_support.h"
 #include "third_party/blink/renderer/platform/wtf/date_math.h"
diff --git a/third_party/blink/renderer/platform/text/locale_win_test.cc b/third_party/blink/renderer/platform/text/locale_win_test.cc
index 341fe53e..1ea7ca5 100644
--- a/third_party/blink/renderer/platform/text/locale_win_test.cc
+++ b/third_party/blink/renderer/platform/text/locale_win_test.cc
@@ -32,7 +32,7 @@
 
 #include <memory>
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/renderer/platform/date_components.h"
+#include "third_party/blink/renderer/platform/text/date_components.h"
 #include "third_party/blink/renderer/platform/wtf/date_math.h"
 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
 #include "third_party/blink/renderer/platform/wtf/text/cstring.h"
diff --git a/third_party/blink/renderer/platform/text/platform_locale.h b/third_party/blink/renderer/platform/text/platform_locale.h
index 132d2cab..0d299762 100644
--- a/third_party/blink/renderer/platform/text/platform_locale.h
+++ b/third_party/blink/renderer/platform/text/platform_locale.h
@@ -30,8 +30,8 @@
 
 #include "base/macros.h"
 #include "third_party/blink/public/platform/web_localized_string.h"
-#include "third_party/blink/renderer/platform/date_components.h"
 #include "third_party/blink/renderer/platform/language.h"
+#include "third_party/blink/renderer/platform/text/date_components.h"
 #include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
diff --git a/third_party/blink/tools/blinkpy/w3c/test_importer.py b/third_party/blink/tools/blinkpy/w3c/test_importer.py
index 944516761..f7126e3 100644
--- a/third_party/blink/tools/blinkpy/w3c/test_importer.py
+++ b/third_party/blink/tools/blinkpy/w3c/test_importer.py
@@ -556,6 +556,9 @@
             username = self._fetch_ecosystem_infra_sheriff_username()
         except (IOError, KeyError, ValueError) as error:
             _log.error('Exception while fetching current sheriff: %s', error)
+        if username in ['kyleju']:
+            _log.warning('Cannot TBR by %s: not a committer', username)
+            username = ''
         return username or TBR_FALLBACK
 
     def _fetch_ecosystem_infra_sheriff_username(self):
diff --git a/third_party/blink/tools/blinkpy/w3c/test_importer_unittest.py b/third_party/blink/tools/blinkpy/w3c/test_importer_unittest.py
index 543f647..976b517 100644
--- a/third_party/blink/tools/blinkpy/w3c/test_importer_unittest.py
+++ b/third_party/blink/tools/blinkpy/w3c/test_importer_unittest.py
@@ -496,6 +496,13 @@
         self.assertEqual('external@example.com', importer.tbr_reviewer())
         self.assertLog([])
 
+    def test_tbr_reviewer_skips_non_committer(self):
+        host = MockHost()
+        importer = TestImporter(host)
+        importer._fetch_ecosystem_infra_sheriff_username = lambda: 'kyleju'
+        self.assertEqual(TBR_FALLBACK, importer.tbr_reviewer())
+        self.assertLog(['WARNING: Cannot TBR by kyleju: not a committer\n'])
+
     def test_generate_manifest_successful_run(self):
         # This test doesn't test any aspect of the real manifest script, it just
         # asserts that TestImporter._generate_manifest would invoke the script.
diff --git a/third_party/blink/tools/blinkpy/web_tests/servers/wptserve.py b/third_party/blink/tools/blinkpy/web_tests/servers/wptserve.py
index 0ddde8d1..c0e6671 100644
--- a/third_party/blink/tools/blinkpy/web_tests/servers/wptserve.py
+++ b/third_party/blink/tools/blinkpy/web_tests/servers/wptserve.py
@@ -45,8 +45,12 @@
         start_cmd = [self._port_obj.host.executable,
                      '-u', wpt_script, 'serve',
                      '--config', self._config_file,
-                     '--doc_root', path_to_wpt_tests,
-                     '--ws_doc_root', path_to_ws_handlers]
+                     '--doc_root', path_to_wpt_tests]
+
+        # Some users (e.g. run_webdriver_tests.py) do not need WebSocket
+        # handlers, so we only add the flag if the directory exists.
+        if self._port_obj.host.filesystem.exists(path_to_ws_handlers):
+            start_cmd += ['--ws_doc_root', path_to_ws_handlers]
 
         # TODO(burnik): We should stop setting the CWD once WPT can be run without it.
         self._cwd = path_to_wpt_root
diff --git a/third_party/blink/tools/blinkpy/web_tests/servers/wptserve_unittest.py b/third_party/blink/tools/blinkpy/web_tests/servers/wptserve_unittest.py
index 4078cba..dac55b2c 100644
--- a/third_party/blink/tools/blinkpy/web_tests/servers/wptserve_unittest.py
+++ b/third_party/blink/tools/blinkpy/web_tests/servers/wptserve_unittest.py
@@ -25,7 +25,24 @@
 
     # pylint: disable=protected-access
 
-    def test_init_start_cmd(self):
+    def test_init_start_cmd_without_ws_handlers(self):
+        server = WPTServe(self.port, '/foo')
+        self.assertEqual(
+            server._start_cmd,  # pylint: disable=protected-access
+            [
+                'python',
+                '-u',
+                '/mock-checkout/third_party/blink/tools/blinkpy/third_party/wpt/wpt/wpt',
+                'serve',
+                '--config',
+                server._config_file,
+                '--doc_root',
+                '/test.checkout/wtests/external/wpt',
+            ])
+
+    def test_init_start_cmd_with_ws_handlers(self):
+        self.host.filesystem.maybe_make_directory(
+            '/test.checkout/wtests/external/wpt/websockets/handlers')
         server = WPTServe(self.port, '/foo')
         self.assertEqual(
             server._start_cmd,  # pylint: disable=protected-access
@@ -39,7 +56,7 @@
                 '--doc_root',
                 '/test.checkout/wtests/external/wpt',
                 '--ws_doc_root',
-                '/test.checkout/wtests/external/wpt/websockets/handlers'
+                '/test.checkout/wtests/external/wpt/websockets/handlers',
             ])
 
     def test_init_gen_config(self):
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 8b10c0c..9a2fab5 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -693,12 +693,16 @@
   {
     "prefix" : "autoupgrade-optionally-blockable-mixed-content",
     "base": "http/tests/mixed-autoupgrade/optionally",
-    "args": ["--enable-features=AutoupgradeMixedContent<AU --force-fieldtrials=AU/G1 --force-fieldtrial-params=AU.G1:mode/optionally-blockable"]
+    "args": ["--enable-features=AutoupgradeMixedContent<AU",
+             "--force-fieldtrials=AU/G1",
+             "--force-fieldtrial-params=AU.G1:mode/optionally-blockable"]
   },
   {
     "prefix" : "autoupgrade-blockable-mixed-content",
     "base": "http/tests/mixed-autoupgrade/blockable",
-    "args": ["--enable-features=AutoupgradeMixedContent<AU --force-fieldtrials=AU/G1 --force-fieldtrial-params=AU.G1:mode/blockable"]
+    "args": ["--enable-features=AutoupgradeMixedContent<AU",
+             "--force-fieldtrials=AU/G1",
+	     "--force-fieldtrial-params=AU.G1:mode/blockable"]
   },
   {
     "prefix" : "autoupgrade-all-mixed-content",
diff --git a/third_party/blink/web_tests/external/wpt/css/css-font-loading/fontfaceset-no-root-element.html b/third_party/blink/web_tests/external/wpt/css/css-font-loading/fontfaceset-no-root-element.html
new file mode 100644
index 0000000..1300191
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-font-loading/fontfaceset-no-root-element.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<title>CSS Font Loading test: Load Document FontFaceSet font without documentElement</title>
+<link rel="help" href="https://drafts.csswg.org/css-font-loading/#font-face-set-load">
+<link rel="help" href="https://crbug.com/971035">
+<script>
+  test(() => {
+    document.documentElement.remove();
+    var face = new FontFace("Ahem", "url(/fonts/Ahem.ttf)");
+    document.fonts.add(face);
+    document.fonts.load("12px Ahem");
+  }, "Trigger font load after removing documentElement. Should not crash.");
+</script>
diff --git a/third_party/blink/web_tests/fast/forms/number/number-appearance-datalist-dynamic-expected.html b/third_party/blink/web_tests/fast/forms/number/number-appearance-datalist-dynamic-expected.html
index 327bf9b..c87d31f 100644
--- a/third_party/blink/web_tests/fast/forms/number/number-appearance-datalist-dynamic-expected.html
+++ b/third_party/blink/web_tests/fast/forms/number/number-appearance-datalist-dynamic-expected.html
@@ -16,7 +16,7 @@
 <dt>Empty datalist:
 <dd><input type="number">
 <dt>Datalist without valid options:
-<dd><input type="number">
+<dd><input type="number" list="dl1">
 <dt>Empty datalist becomes non-empty:
 <dd><input type="number" list="dl1">
 <dt>Datalist becomes empty:
diff --git a/third_party/blink/web_tests/fast/forms/text/text-appearance-datalist-dynamic-expected.html b/third_party/blink/web_tests/fast/forms/text/text-appearance-datalist-dynamic-expected.html
index c2c0fba..f49d59d 100644
--- a/third_party/blink/web_tests/fast/forms/text/text-appearance-datalist-dynamic-expected.html
+++ b/third_party/blink/web_tests/fast/forms/text/text-appearance-datalist-dynamic-expected.html
@@ -15,7 +15,7 @@
 <dt>Empty datalist:
 <dd><input>
 <dt>Datalist without valid options:
-<dd><input>
+<dd><input list="dl1">
 <dt>Empty datalist becomes non-empty:
 <dd><input list="dl1">
 <dt>Datalist becomes empty:
diff --git a/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/console-a11y-test-expected.txt b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/console-a11y-test-expected.txt
new file mode 100644
index 0000000..41fdf3b
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/console-a11y-test-expected.txt
@@ -0,0 +1,4 @@
+Tests accessibility in the console using the axe-core linter.
+aXe violations: []
+
+
diff --git a/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/console-a11y-test.js b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/console-a11y-test.js
new file mode 100644
index 0000000..38ebeeb
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/console-a11y-test.js
@@ -0,0 +1,15 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(async function() {
+  await TestRunner.loadModule('axe_core_test_runner');
+  TestRunner.addResult(
+      'Tests accessibility in the console using the axe-core linter.');
+
+  await UI.viewManager.showView('console');
+  const widget = await UI.viewManager.view('console').widget();
+
+  await AxeCoreTestRunner.runValidation(widget.element);
+  TestRunner.completeTest();
+})();
diff --git a/third_party/blink/web_tests/media/controls/progress-bar-repaint-on-size-change.html b/third_party/blink/web_tests/media/controls/progress-bar-repaint-on-size-change.html
index 1a135b88..5e4314c1 100644
--- a/third_party/blink/web_tests/media/controls/progress-bar-repaint-on-size-change.html
+++ b/third_party/blink/web_tests/media/controls/progress-bar-repaint-on-size-change.html
@@ -36,11 +36,13 @@
   }
 
   function progressBarShouldMeetThumb() {
-    const delta = 1.0;
-    let progress = segmentBefore.getBoundingClientRect().right;
-    let thumbPosition = elementCoordinates(thumb)[0];
-    assert_less_than(progress, thumbPosition + delta, 'progress bar should meet thumb');
-    assert_greater_than(progress, thumbPosition - delta, 'progress bar should meet thumb');
+    const progress = segmentBefore.getBoundingClientRect().right;
+    const thumbRect = thumb.getBoundingClientRect();
+    const thumbLeft = thumbRect.left;
+    const thumbRight = thumbRect.right;
+
+    assert_greater_than_equal(progress, thumbLeft, 'progress bar should reach the thumb');
+    assert_less_than_equal(progress, thumbRight, 'progress bar should not go beyond the thumb');
   }
 
   video.onloadedmetadata = t.step_func(() => {
diff --git a/third_party/blink/web_tests/payments/payment-request-interface.html b/third_party/blink/web_tests/payments/payment-request-interface.html
index 315b91b..bba2dc85 100644
--- a/third_party/blink/web_tests/payments/payment-request-interface.html
+++ b/third_party/blink/web_tests/payments/payment-request-interface.html
@@ -254,10 +254,6 @@
 }, 'Invalid basic card parameters should not throw when method name is not "basic-card".');
 
 test(function() {
-  new PaymentRequest([{'supportedMethods': 'basic-card', 'data': {'supportedTypes': 0, 'supportedNetworks': 'foo'}}], buildDetails());
-}, 'Invalid basic card parameters should not throw even when method name is "basic-card".');
-
-test(function() {
     new PaymentRequest([{'supportedMethods': 'https://android.com/pay', 'data': {'merchantName': 'Merchant Inc', 'merchantId': '123', 'allowedCardNetworks': ['AMEX', 'DISCOVER', 'MASTERCARD', 'VISA'], 'paymentMethodTokenizationParameters': {'tokenizationType': 'NETWORK_TOKEN', 'parameters': {'key': 'value'}}}}], buildDetails());
 }, 'Android Pay parameters for network token without environment key should not throw.');
 
@@ -353,6 +349,9 @@
     }],
     ['Empty string for payment method specific data parameter should throw', new TypeError(), function() {
         new PaymentRequest([{'supportedMethods': 'foo', 'data': ''}], buildDetails())
+    }],
+    ['PaymentRequest constructor should throw for invalid "basic-card" parameters.', new TypeError(), function() {
+        new PaymentRequest([{'supportedMethods': 'basic-card', 'data': {'supportedTypes': 0, 'supportedNetworks': 'foo'}}], buildDetails())
     }]
 ]);
 
diff --git a/third_party/blink/web_tests/platform/linux/http/tests/media/video-buffered-range-contains-currentTime-expected.png b/third_party/blink/web_tests/platform/linux/http/tests/media/video-buffered-range-contains-currentTime-expected.png
index 83da9e6..8304973 100644
--- a/third_party/blink/web_tests/platform/linux/http/tests/media/video-buffered-range-contains-currentTime-expected.png
+++ b/third_party/blink/web_tests/platform/linux/http/tests/media/video-buffered-range-contains-currentTime-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/http/tests/media/video-buffered-range-contains-currentTime-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/http/tests/media/video-buffered-range-contains-currentTime-expected.png
index 5110bcd2..ee3eca99 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/http/tests/media/video-buffered-range-contains-currentTime-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/http/tests/media/video-buffered-range-contains-currentTime-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/http/tests/media/video-buffered-range-contains-currentTime-expected.png b/third_party/blink/web_tests/platform/mac/http/tests/media/video-buffered-range-contains-currentTime-expected.png
index e3b7f6e..7a6bd35 100644
--- a/third_party/blink/web_tests/platform/mac/http/tests/media/video-buffered-range-contains-currentTime-expected.png
+++ b/third_party/blink/web_tests/platform/mac/http/tests/media/video-buffered-range-contains-currentTime-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/http/tests/media/video-buffered-range-contains-currentTime-expected.png b/third_party/blink/web_tests/platform/win/http/tests/media/video-buffered-range-contains-currentTime-expected.png
index b586933..b48330e 100644
--- a/third_party/blink/web_tests/platform/win/http/tests/media/video-buffered-range-contains-currentTime-expected.png
+++ b/third_party/blink/web_tests/platform/win/http/tests/media/video-buffered-range-contains-currentTime-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/resources/testdriver-vendor.js b/third_party/blink/web_tests/resources/testdriver-vendor.js
index 2dc45ac..a164f489 100644
--- a/third_party/blink/web_tests/resources/testdriver-vendor.js
+++ b/third_party/blink/web_tests/resources/testdriver-vendor.js
@@ -86,40 +86,42 @@
       var first_pointer_down = false;
       for (let j = 0; j < actions[i].actions.length; j++) {
         if ('origin' in actions[i].actions[j]) {
-          if (actions[i].actions[j].origin == "viewport") {
-            last_x_position = actions[i].actions[j].x;
-            last_y_position = actions[i].actions[j].y;
-            continue;
+          if (typeof(actions[i].actions[j].origin) === 'string') {
+             if (actions[i].actions[j].origin == "viewport") {
+               last_x_position = actions[i].actions[j].x;
+               last_y_position = actions[i].actions[j].y;
+             } else if (actions[i].actions[j].origin == "pointer") {
+               return Promise.reject(new Error("pointer origin is not supported right now"));
+             } else {
+               return Promise.reject(new Error("pointer origin is not given correctly"));
+             }
+          } else {
+            let element = actions[i].actions[j].origin;
+            if (!window.document.contains(element)) {
+              return Promise.reject(new Error("element in different document or shadow tree"));
+            }
+
+            if (!inView(element)) {
+              if (didScrollIntoView)
+                return Promise.reject(new Error("already scrolled into view, the element is not found"));
+
+              element.scrollIntoView({behavior: "instant",
+                                      block: "end",
+                                      inline: "nearest"});
+              didScrollIntoView = true;
+            }
+
+            var pointerInteractablePaintTree = getPointerInteractablePaintTree(element);
+            if (pointerInteractablePaintTree.length === 0 ||
+                !element.contains(pointerInteractablePaintTree[0])) {
+              return Promise.reject(new Error("element click intercepted error"));
+            }
+
+            var rect = element.getClientRects()[0];
+            var centerPoint = getInViewCenterPoint(rect);
+            last_x_position = actions[i].actions[j].x + centerPoint[0];
+            last_y_position = actions[i].actions[j].y + centerPoint[1];
           }
-
-          if (actions[i].actions[j].origin == "pointer")
-            return Promise.reject(new Error("pointer origin is not supported right now"));
-
-          let element = actions[i].actions[j].origin;
-          if (!window.document.contains(element)) {
-            return Promise.reject(new Error("element in different document or shadow tree"));
-          }
-
-          if (!inView(element)) {
-            if (didScrollIntoView)
-              return Promise.reject(new Error("already scrolled into view, the element is not found"));
-
-            element.scrollIntoView({behavior: "instant",
-                                    block: "end",
-                                    inline: "nearest"});
-            didScrollIntoView = true;
-          }
-
-          var pointerInteractablePaintTree = getPointerInteractablePaintTree(element);
-          if (pointerInteractablePaintTree.length === 0 ||
-              !element.contains(pointerInteractablePaintTree[0])) {
-            return Promise.reject(new Error("element click intercepted error"));
-          }
-
-          var rect = element.getClientRects()[0];
-          var centerPoint = getInViewCenterPoint(rect);
-          last_x_position = actions[i].actions[j].x + centerPoint[0];
-          last_y_position = actions[i].actions[j].y + centerPoint[1];
         }
 
         if (actions[i].actions[j].type == "pointerDown" || actions[i].actions[j].type == "pointerMove") {
diff --git a/third_party/blink/web_tests/webaudio/AudioParam/audioparam-cancel-and-hold.html b/third_party/blink/web_tests/webaudio/AudioParam/audioparam-cancel-and-hold.html
index 8147dcb..2853af2 100644
--- a/third_party/blink/web_tests/webaudio/AudioParam/audioparam-cancel-and-hold.html
+++ b/third_party/blink/web_tests/webaudio/AudioParam/audioparam-cancel-and-hold.html
@@ -6,6 +6,7 @@
     </title>
     <script src="../../resources/testharness.js"></script>
     <script src="../../resources/testharnessreport.js"></script>
+    <script src="../resources/audio-param.js"></script>
     <script src="../resources/audit-util.js"></script>
     <script src="../resources/audit.js"></script>
   </head>
@@ -451,6 +452,52 @@
             .then(task.done.bind(task));
       });
 
+      audit.define(
+        {
+          label: 'linear, cancel, linear, cancel, linear',
+          description: 'Schedules 3 linear ramps, cancelling 2 of them, '
+            + 'so that we end up with 2 cancel events next to each other'
+        },
+        (task, should) => {
+          cancelTest2(
+            should,
+            linearRampTest('1st linearRamp'),
+            {valueThreshold: 0, curveThreshold: 0},
+            (g, cancelTime, expectedConstant, cancelTime2) => {
+              // Ramp from first cancel time to the end will be cancelled at
+              // second cancel time.
+              const v1 = expectedConstant;
+              const t1 = cancelTime;
+              const v2 = 2;
+              const t2 = renderDuration;
+              g[0].gain.linearRampToValueAtTime(v2, t2);
+              g[2].gain.setValueAtTime(v1, t1);
+              g[2].gain.linearRampToValueAtTime(v2, t2);
+
+              const expectedConstant2 =
+                audioParamLinearRamp(cancelTime2, v1, t1, v2, t2);
+
+              return {
+                constantEndTime: cancelTime,
+                message: `2nd linearRamp(${v2}, ${t2})`,
+                expectedConstant2
+              };
+            },
+            (g, cancelTime2, expectedConstant2) => {
+              // Ramp from second cancel time to the end.
+              const v3 = 0;
+              const t3 = renderDuration;
+              g[0].gain.linearRampToValueAtTime(v3, t3);
+              g[3].gain.setValueAtTime(expectedConstant2, cancelTime2);
+              g[3].gain.linearRampToValueAtTime(v3, t3);
+              return {
+                constantEndTime2: cancelTime2,
+                message2: `3rd linearRamp(${v3}, ${t3})`,
+              };
+            })
+            .then(() => task.done());
+        });
+
       audit.run();
 
       // Common function for doing a linearRamp test.  This just does a linear
@@ -484,7 +531,7 @@
       // Run the cancellation test. A set of automations is created and
       // canceled.
       //
-      // |testFunction| is a function that generates the automation to be
+      // |testerFunction| is a function that generates the automation to be
       // tested.  It is given an array of 3 gain nodes, the value and time of an
       // initial linear ramp, and the time where the cancellation should occur.
       // The function must do the automations for the first two gain nodes.  It
@@ -637,6 +684,140 @@
           }
         });
       }
+
+      // Similar to cancelTest, but does 2 cancels.
+      function cancelTest2(
+          should, testerFunction, thresholdOptions,
+          postCancelTest, postCancelTest2) {
+        // Channel 0: Actual output that includes the cancellation of events.
+        // Channel 1: Expected data up to the first cancellation.
+        // Channel 2: Expected data from 1st cancellation to 2nd cancellation.
+        // Channel 3: Expected data from 2nd cancellation to the end.
+        const context =
+          new OfflineAudioContext(4, renderDuration * sampleRate, sampleRate);
+
+        const src = context.createConstantSource();
+
+        // g0: Actual gain which will have cancelAndHoldAtTime called on it
+        // twice.
+        // g1: Expected gain from start to the 1st cancel.
+        // g2: Expected gain from 1st cancel to the 2nd cancel.
+        // g3: Expected gain from the 2nd cancel to the end.
+        const g0 = context.createGain();
+        const g1 = context.createGain();
+        const g2 = context.createGain();
+        const g3 = context.createGain();
+        const v0 = 1;
+        const t0 = 0.01;
+
+        const cancelTime1 = renderDuration * 0.5;
+        const cancelTime2 = renderDuration * 0.75;
+
+        // Run testerFunction to generate the 1st ramp.
+        const {
+          expectedConstant, autoMessage, summaryMessage} =
+            testerFunction([g0, g1, g2], v0, t0, cancelTime1);
+
+        // 1st cancel, cancelling the 1st ramp.
+        g0.gain.cancelAndHoldAtTime(cancelTime1);
+
+        // Run postCancelTest to generate the 2nd ramp.
+        const {
+          constantEndTime, message, errorThreshold = 0, expectedConstant2} =
+            postCancelTest(
+              [g0, g1, g2], cancelTime1, expectedConstant, cancelTime2);
+
+        // 2nd cancel, cancelling the 2nd ramp.
+        g0.gain.cancelAndHoldAtTime(cancelTime2);
+
+        // Run postCancelTest2 to generate the 3rd ramp.
+        const {constantEndTime2, message2} =
+          postCancelTest2([g0, g1, g2, g3], cancelTime2, expectedConstant2);
+
+        // Connect everything together
+        src.connect(g0);
+        src.connect(g1);
+        src.connect(g2);
+        src.connect(g3);
+        const merger = context.createChannelMerger(4);
+        g0.connect(merger, 0, 0);
+        g1.connect(merger, 0, 1);
+        g2.connect(merger, 0, 2);
+        g3.connect(merger, 0, 3);
+        merger.connect(context.destination);
+
+        // Go!
+        src.start();
+
+        return context.startRendering().then(function (buffer) {
+          const actual = buffer.getChannelData(0);
+          const expected1 = buffer.getChannelData(1);
+          const expected2 = buffer.getChannelData(2);
+          const expected3 = buffer.getChannelData(3);
+
+          const cancelFrame1 = Math.ceil(cancelTime1 * sampleRate);
+          const cancelFrame2 = Math.ceil(cancelTime2 * sampleRate);
+
+          const constantEndFrame1 = Math.ceil(constantEndTime * sampleRate);
+          const constantEndFrame2 = Math.ceil(constantEndTime2 * sampleRate);
+
+          const actualTail1 = actual.slice(cancelFrame1, constantEndFrame1);
+          const actualTail2 = actual.slice(cancelFrame2, constantEndFrame2);
+
+          const actualConstant1 = actual[cancelFrame1];
+          const actualConstant2 = actual[cancelFrame2];
+
+          // Verify first section curve
+          should(
+            actual.slice(0, cancelFrame1),
+            autoMessage + ' up to time ' + cancelTime1)
+            .beCloseToArray(
+              expected1.slice(0, cancelFrame1),
+              {absoluteThreshold: thresholdOptions.curveThreshold});
+
+          // Verify that a value was held after 1st cancel
+          should(
+            actualTail1,
+            'Cancelling ' + autoMessage + ' at time ' + cancelTime1)
+            .beConstantValueOf(actualConstant1);
+
+          // Verify that held value after 1st cancel was correct
+          should(
+            actualConstant1,
+            'Expected value for cancelling ' + autoMessage + ' at time ' +
+            cancelTime1)
+            .beCloseTo(
+              expectedConstant,
+              {threshold: thresholdOptions.valueThreshold});
+
+          // Verify middle section curve
+          should(actual.slice(constantEndFrame1, cancelFrame2), message)
+            .beCloseToArray(
+              expected2.slice(constantEndFrame1, cancelFrame2),
+              {absoluteThreshold: errorThreshold});
+
+          // Verify that a value was held after 2nd cancel
+          should(
+            actualTail2,
+            'Cancelling ' + message + ' at time ' + cancelTime2)
+            .beConstantValueOf(actualConstant2);
+
+          // Verify that held value after 2nd cancel was correct
+          should(
+            actualConstant2,
+            'Expected value for cancelling ' + message + ' at time ' +
+            cancelTime2)
+            .beCloseTo(
+              expectedConstant2,
+              {threshold: thresholdOptions.valueThreshold});
+
+          // Verify end section curve
+          should(actual.slice(constantEndFrame2), message2)
+            .beCloseToArray(
+              expected3.slice(constantEndFrame2),
+              {absoluteThreshold: errorThreshold || 0});
+        });
+      }
     </script>
   </body>
 </html>
diff --git a/third_party/blink/web_tests/webaudio/Oscillator/osc-sweep-snr-sawtooth.html b/third_party/blink/web_tests/webaudio/Oscillator/osc-sweep-snr-sawtooth.html
index b13ccb7..f4d0d3c 100644
--- a/third_party/blink/web_tests/webaudio/Oscillator/osc-sweep-snr-sawtooth.html
+++ b/third_party/blink/web_tests/webaudio/Oscillator/osc-sweep-snr-sawtooth.html
@@ -22,7 +22,7 @@
             1, tester.sampleRate * tester.lengthInSeconds, tester.sampleRate);
 
         // The thresholds are experimentally determined.
-        tester.setThresholds({snr: 134.41, maxDiff: 1.8925e-6});
+        tester.setThresholds({snr: 134.39, maxDiff: 1.8925e-6});
         tester.runTest(
             context, 'sawtooth', 'Sawtooth Oscillator with Exponential Sweep',
             task, should);
diff --git a/third_party/blink/web_tests/webaudio/Oscillator/osc-sweep-snr-triangle.html b/third_party/blink/web_tests/webaudio/Oscillator/osc-sweep-snr-triangle.html
index 5770eb0..2c70a73 100644
--- a/third_party/blink/web_tests/webaudio/Oscillator/osc-sweep-snr-triangle.html
+++ b/third_party/blink/web_tests/webaudio/Oscillator/osc-sweep-snr-triangle.html
@@ -22,7 +22,7 @@
             1, tester.sampleRate * tester.lengthInSeconds, tester.sampleRate);
 
         // The thresholds are experimentally determined.
-        tester.setThresholds({snr: 140.51, maxDiff: 2.8313e-6});
+        tester.setThresholds({snr: 140.09, maxDiff: 2.8313e-6});
         tester.runTest(
             context, 'triangle', 'Triangle Oscillator with Exponential Sweep ',
             task, should);
diff --git a/third_party/blink/web_tests/webgpu/buffer_mapping.html b/third_party/blink/web_tests/webgpu/buffer_mapping.html
index 54b91745c..cbebe87 100644
--- a/third_party/blink/web_tests/webgpu/buffer_mapping.html
+++ b/third_party/blink/web_tests/webgpu/buffer_mapping.html
@@ -125,6 +125,320 @@
       assert_equals(data[i], actual[i]);
     }
 
+    {
+      // Test simple CreateBufferMapped
+      const [buffer, arrayBuffer] = device.createBufferMapped({
+        size: 12,
+        usage: GPUBufferUsage.TRANSFER_SRC | GPUBufferUsage.MAP_WRITE,
+      });
+
+      checkMapWriteResult(arrayBuffer, 12);
+
+      const view = new Uint32Array(arrayBuffer);
+      view[1] = 7;
+      buffer.unmap();
+
+      // Array buffer should be detached.
+      assert_equals(arrayBuffer.byteLength, 0);
+      assert_equals(view.byteLength, 0);
+
+      await expectContents(buffer, new Uint32Array([0, 7, 0]));
+    }
+
+    {
+      // Test large CreateBufferMapped
+      const size = 512 * 1024;
+
+      const [buffer, arrayBuffer] = device.createBufferMapped({
+        size,
+        usage: GPUBufferUsage.TRANSFER_SRC | GPUBufferUsage.MAP_WRITE,
+      });
+
+      checkMapWriteResult(arrayBuffer, size);
+
+      const view = new Uint32Array(arrayBuffer);
+      assert_equals(view.byteLength, size);
+      for (let i = 0; i < view.length; ++i) {
+        view[i] = i;
+      }
+      const expected = view.slice();
+      buffer.unmap();
+
+      // Array buffer should be detached.
+      assert_equals(arrayBuffer.byteLength, 0);
+      assert_equals(view.byteLength, 0);
+
+      await expectContents(buffer, expected);
+    }
+
+    {
+      // Test simple non-mappable CreateBufferMapped
+      const [buffer, arrayBuffer] = device.createBufferMapped({
+        size: 12,
+        usage: GPUBufferUsage.TRANSFER_SRC,
+      });
+
+      checkMapWriteResult(arrayBuffer, 12);
+
+      const view = new Uint32Array(arrayBuffer);
+      view[1] = 7;
+      buffer.unmap();
+
+      // Array buffer should be detached.
+      assert_equals(arrayBuffer.byteLength, 0);
+      assert_equals(view.byteLength, 0);
+
+      await expectContents(buffer, new Uint32Array([0, 7, 0]));
+    }
+
+    {
+      // Test large non-mappable CreateBufferMapped
+      const size = 512 * 1024;
+
+      const [buffer, arrayBuffer] = device.createBufferMapped({
+        size,
+        usage: GPUBufferUsage.TRANSFER_SRC,
+      });
+
+      checkMapWriteResult(arrayBuffer, size);
+
+      const view = new Uint32Array(arrayBuffer);
+      assert_equals(view.byteLength, size);
+      for (let i = 0; i < view.length; ++i) {
+        view[i] = i;
+      }
+      const expected = view.slice();
+      buffer.unmap();
+
+      // Array buffer should be detached.
+      assert_equals(arrayBuffer.byteLength, 0);
+      assert_equals(view.byteLength, 0);
+
+      await expectContents(buffer, expected);
+    }
+
+    {
+      // Test simple CreateBufferMappedAsync
+      const [buffer, arrayBuffer] = await device.createBufferMappedAsync({
+        size: 12,
+        usage: GPUBufferUsage.TRANSFER_SRC | GPUBufferUsage.MAP_WRITE,
+      });
+
+      checkMapWriteResult(arrayBuffer, 12);
+
+      const view = new Uint32Array(arrayBuffer);
+      view[1] = 7;
+      buffer.unmap();
+
+      // Array buffer should be detached.
+      assert_equals(arrayBuffer.byteLength, 0);
+      assert_equals(view.byteLength, 0);
+
+      await expectContents(buffer, new Uint32Array([0, 7, 0]));
+    }
+
+    {
+      // Test large CreateBufferMappedAsync
+      const size = 512 * 1024;
+
+      const [buffer, arrayBuffer] = await device.createBufferMappedAsync({
+        size,
+        usage: GPUBufferUsage.TRANSFER_SRC | GPUBufferUsage.MAP_WRITE,
+      });
+
+      checkMapWriteResult(arrayBuffer, size);
+
+      const view = new Uint32Array(arrayBuffer);
+      assert_equals(view.byteLength, size);
+      for (let i = 0; i < view.length; ++i) {
+        view[i] = i;
+      }
+      const expected = view.slice();
+      buffer.unmap();
+
+      // Array buffer should be detached.
+      assert_equals(arrayBuffer.byteLength, 0);
+      assert_equals(view.byteLength, 0);
+
+      await expectContents(buffer, expected);
+    }
+
+    {
+      // Test simple non-mappable CreateBufferMappedAsync
+      const [buffer, arrayBuffer] = await device.createBufferMappedAsync({
+        size: 12,
+        usage: GPUBufferUsage.TRANSFER_SRC,
+      });
+
+      checkMapWriteResult(arrayBuffer, 12);
+
+      const view = new Uint32Array(arrayBuffer);
+      view[1] = 7;
+      buffer.unmap();
+
+      // Array buffer should be detached.
+      assert_equals(arrayBuffer.byteLength, 0);
+      assert_equals(view.byteLength, 0);
+
+      await expectContents(buffer, new Uint32Array([0, 7, 0]));
+    }
+
+    {
+      // Test large non-mappable CreateBufferMappedAsync
+      const size = 512 * 1024;
+
+      const [buffer, arrayBuffer] = await device.createBufferMappedAsync({
+        size,
+        usage: GPUBufferUsage.TRANSFER_SRC,
+      });
+
+      checkMapWriteResult(arrayBuffer, size);
+
+      const view = new Uint32Array(arrayBuffer);
+      assert_equals(view.byteLength, size);
+      for (let i = 0; i < view.length; ++i) {
+        view[i] = i;
+      }
+      const expected = view.slice();
+      buffer.unmap();
+
+      // Array buffer should be detached.
+      assert_equals(arrayBuffer.byteLength, 0);
+      assert_equals(view.byteLength, 0);
+
+      await expectContents(buffer, expected);
+    }
+
+    {
+      // Test that ArrayBuffers are detached after unmap().
+      {
+        const buffer = device.createBuffer({
+          size: 4,
+          usage: GPUBufferUsage.MAP_WRITE,
+        });
+        const arrayBuffer = await buffer.mapWriteAsync();
+        assert_equals(arrayBuffer.byteLength, 4);
+        buffer.unmap();
+        assert_equals(arrayBuffer.byteLength, 0, "Array buffer is detached.");
+      }
+      {
+        const buffer = device.createBuffer({
+          size: 4,
+          usage: GPUBufferUsage.MAP_READ,
+        });
+        const arrayBuffer = await buffer.mapReadAsync();
+        assert_equals(arrayBuffer.byteLength, 4);
+        buffer.unmap();
+        assert_equals(arrayBuffer.byteLength, 0, "Array buffer is detached.");
+      }
+      {
+        const [buffer, arrayBuffer] = device.createBufferMapped({
+          size: 4,
+          usage: GPUBufferUsage.MAP_WRITE,
+        });
+        assert_equals(arrayBuffer.byteLength, 4);
+        buffer.unmap();
+        assert_equals(arrayBuffer.byteLength, 0, "Array buffer is detached.");
+      }
+      {
+        const [buffer, arrayBuffer] = await device.createBufferMappedAsync({
+          size: 4,
+          usage: GPUBufferUsage.MAP_WRITE,
+        });
+        assert_equals(arrayBuffer.byteLength, 4);
+        buffer.unmap();
+        assert_equals(arrayBuffer.byteLength, 0, "Array buffer is detached.");
+      }
+    }
+
+    {
+      // Test that ArrayBuffers are detached after destroy().
+      {
+        const buffer = device.createBuffer({
+          size: 4,
+          usage: GPUBufferUsage.MAP_WRITE,
+        });
+        const arrayBuffer = await buffer.mapWriteAsync();
+        assert_equals(arrayBuffer.byteLength, 4);
+        buffer.destroy();
+        assert_equals(arrayBuffer.byteLength, 0, "Array buffer is detached.");
+      }
+      {
+        const buffer = device.createBuffer({
+          size: 4,
+          usage: GPUBufferUsage.MAP_READ,
+        });
+        const arrayBuffer = await buffer.mapReadAsync();
+        assert_equals(arrayBuffer.byteLength, 4);
+        buffer.destroy();
+        assert_equals(arrayBuffer.byteLength, 0, "Array buffer is detached.");
+      }
+      {
+        const [buffer, arrayBuffer] = device.createBufferMapped({
+          size: 4,
+          usage: GPUBufferUsage.MAP_WRITE,
+        });
+        assert_equals(arrayBuffer.byteLength, 4);
+        buffer.destroy();
+        assert_equals(arrayBuffer.byteLength, 0, "Array buffer is detached.");
+      }
+      {
+        const [buffer, arrayBuffer] = await device.createBufferMappedAsync({
+          size: 4,
+          usage: GPUBufferUsage.MAP_WRITE,
+        });
+        assert_equals(arrayBuffer.byteLength, 4);
+        buffer.destroy();
+        assert_equals(arrayBuffer.byteLength, 0, "Array buffer is detached.");
+      }
+    }
+
+    {
+      // Test OOM buffer mapping errors.
+      {
+        let didReject = false;
+        await device.createBuffer({
+          size: Number.MAX_SAFE_INTEGER,
+          usage: GPUBufferUsage.MAP_WRITE,
+        }).mapWriteAsync().catch(() => {
+          didReject = true;
+        });
+        assert_true(didReject, "mapWriteAsync promise rejected");
+      }
+      {
+        let didReject = false;
+        await device.createBuffer({
+          size: Number.MAX_SAFE_INTEGER,
+          usage: GPUBufferUsage.MAP_READ,
+        }).mapReadAsync().catch(() => {
+          didReject = true;
+        });
+        assert_true(didReject, "mapReadAsync promise rejected");
+      }
+      {
+        let didReject = false;
+        await device.createBufferMappedAsync({
+          size: Number.MAX_SAFE_INTEGER,
+          usage: GPUBufferUsage.TRANSFER_DST,
+        }).catch(() => {
+          didReject = true;
+        });
+        assert_true(didReject, "createBufferMappedAsync promise rejected");
+      }
+      {
+        let didThrow = false;
+        try {
+          device.createBufferMapped({
+            size: Number.MAX_SAFE_INTEGER,
+            usage: GPUBufferUsage.TRANSFER_DST,
+          });
+        } catch (err) {
+          didThrow = true;
+        }
+        assert_true(didThrow);
+      }
+    }
+
   }
 
 }, "Test WebGPU buffer mapping");
diff --git a/third_party/closure_compiler/externs/automation.js b/third_party/closure_compiler/externs/automation.js
index d4259b5..7d0199b 100644
--- a/third_party/closure_compiler/externs/automation.js
+++ b/third_party/closure_compiler/externs/automation.js
@@ -7,8 +7,7 @@
 // NOTE: The format of types has changed. 'FooType' is now
 //   'chrome.automation.FooType'.
 // Please run the closure compiler before committing changes.
-// See
-// https://chromium.googlesource.com/chromium/src/+/master/docs/closure_compilation.md
+// See https://chromium.googlesource.com/chromium/src/+/master/docs/closure_compilation.md
 
 /** @fileoverview Externs generated from namespace: automation */
 
@@ -505,6 +504,17 @@
 chrome.automation.CustomAction;
 
 /**
+ * @typedef {{
+ *   startIndex: number,
+ *   endIndex: number,
+ *   language: string,
+ *   probability: number
+ * }}
+ * @see https://developer.chrome.com/extensions/automation#type-LanguageSpan
+ */
+chrome.automation.LanguageSpan;
+
+/**
  * @constructor
  * @private
  * @see https://developer.chrome.com/extensions/automation#type-AutomationNode
@@ -903,8 +913,7 @@
 chrome.automation.AutomationNode.prototype.markerTypes;
 
 /**
- * If a selection is present, whether the anchor of the selection comes after
- * its focus in the accessibility tree.
+ * If a selection is present, whether the anchor of the selection comes after its focus in the accessibility tree.
  * @type {(boolean|undefined)}
  * @see https://developer.chrome.com/extensions/automation#type-isSelectionBackward
  */
@@ -1749,3 +1758,16 @@
  * @see https://developer.chrome.com/extensions/automation#method-setDocumentSelection
  */
 chrome.automation.setDocumentSelection = function(params) {};
+
+/**
+ * Returns the detected languages for the provided string attribute as an array
+ * of LanguageSpan objects. There are several guarantees about the format of the
+ * LanguageSpan array: 1. Is either empty or contains LanguageSpans that cover
+ * all indices in the associated string attribute value. 2. Is sorted by
+ * increasing startIndex (those with smaller startIndex appear first). 3.
+ * LanguageSpans are non-overlapping and contain exactly one language.
+ * @param {string} attribute
+ * @return {!Array<!chrome.automation.LanguageSpan>}
+ * @see https://developer.chrome.com/extensions/automation#method-languageAnnotationForStringAttribute
+ */
+chrome.automation.languageAnnotationForStringAttribute = function(attribute) {};
diff --git a/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-plane-detection.html b/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-plane-detection.html
index 59b0e43f..5fcbee3 100644
--- a/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-plane-detection.html
+++ b/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-plane-detection.html
@@ -344,7 +344,7 @@
         }
       }
 
-      function addPlaneToScene(plane) {
+      function addPlaneToScene(plane, timestamp) {
         if(typeof XRPlane.counter == 'undefined') {
           XRPlane.counter = 1;
           XRPlane.colors = [
@@ -399,9 +399,9 @@
 
           scene.addNode(plane.scene_node);
         }
-        else
+        else if(plane.lastChangedTime == timestamp)
         {
-          // old plane
+          // old plane that was updated in current frame
           plane.scene_node.onPlaneChanged(plane.polygon);
           plane.scene_node.matrix = plane.getPose(xrRefSpace).transform.matrix;
           plane.extended_polygon = extendPolygon(plane.polygon);
@@ -420,7 +420,7 @@
         let previous_planes = new Set(all_planes);
         let detected_planes = frame.worldInformation.detectedPlanes;
         detected_planes.forEach(plane => {
-          addPlaneToScene(plane);
+          addPlaneToScene(plane, t);
           if(previous_planes.has(plane)) {
             previous_planes.delete(plane);
           }
diff --git a/third_party/webxr_test_pages/webxr-samples/spectator-mode.html b/third_party/webxr_test_pages/webxr-samples/spectator-mode.html
index 3f83e99..5ee97ca 100644
--- a/third_party/webxr_test_pages/webxr-samples/spectator-mode.html
+++ b/third_party/webxr_test_pages/webxr-samples/spectator-mode.html
@@ -155,10 +155,6 @@
           spectatorButton.innerHTML = 'Enable spectator mode';
           spectatorButton.addEventListener('click', onEnableSpectatorMode);
           mainElement.appendChild(spectatorButton);
-
-          // Hide the inline session's canvas so that we can see
-          // the button.
-          gl.canvas.style.display = 'none';
         });
       }
 
@@ -167,7 +163,14 @@
 
         initGL();
 
-        session.updateRenderState({ baseLayer: new XRWebGLLayer(session, gl) });
+        // In order for an inline session to be used we must set the GL layer's
+        // compositionDisabled option to "true", which indicates that WebGL
+        // commands will draw to the canvas like normal.
+        session.updateRenderState({
+          baseLayer: new XRWebGLLayer(session, gl, {
+              compositionDisabled: session.mode == 'inline'
+            })
+        });
 
         session.requestReferenceSpace('local-floor').then((refSpace) => {
           return refSpace;
diff --git a/tools/code_coverage/coverage.py b/tools/code_coverage/coverage.py
index b9a99e1..8d71110 100755
--- a/tools/code_coverage/coverage.py
+++ b/tools/code_coverage/coverage.py
@@ -799,6 +799,16 @@
   os.makedirs(coverage_utils.GetCoverageReportRootDirPath(OUTPUT_DIR))
 
 
+def _SetMacXcodePath():
+  """Set DEVELOPER_DIR to the path to hermetic Xcode.app on Mac OS X."""
+  if sys.platform != 'darwin':
+    return
+
+  xcode_path = os.path.join(SRC_ROOT_PATH, 'build', 'mac_files', 'Xcode.app')
+  if os.path.exists(xcode_path):
+    os.environ['DEVELOPER_DIR'] = xcode_path
+
+
 def _ParseCommandArguments():
   """Adds and parses relevant arguments for tool comands.
 
@@ -986,6 +996,11 @@
     profdata_file_path = args.profdata_file
     binary_paths = _GetBinaryPathsFromTargets(args.targets, args.build_dir)
 
+  # DEVELOPER_DIR needs to be set when Xcode isn't in a standard location
+  # and xcode-select wasn't run.  This path needs to be set prior to calling
+  # otool which happens on mac in coverage_utils.GetSharedLibraries().
+  _SetMacXcodePath()
+
   binary_paths.extend(
       coverage_utils.GetSharedLibraries(binary_paths, BUILD_DIR))
 
diff --git a/tools/code_coverage/coverage_utils.py b/tools/code_coverage/coverage_utils.py
index f97a1cb..fab56ce 100644
--- a/tools/code_coverage/coverage_utils.py
+++ b/tools/code_coverage/coverage_utils.py
@@ -678,9 +678,6 @@
     shared_library_re = re.compile(r'.*\.so[.0-9]*\s=>\s(.*' + build_dir +
                                    r'.*\.so[.0-9]*)\s.*')
   elif sys.platform.startswith('darwin'):
-    # 'otool' is installed as part of CommandLineTools. This script makes the
-    # assumption that 'otool' is available and does not specify an explicit
-    # path.
     cmd.extend(['otool', '-L'])
     shared_library_re = re.compile(r'\s+(@rpath/.*\.dylib)\s.*')
   else:
diff --git a/tools/gritsettings/resource_ids b/tools/gritsettings/resource_ids
index 2a7cb2d..3b5ef81 100644
--- a/tools/gritsettings/resource_ids
+++ b/tools/gritsettings/resource_ids
@@ -74,7 +74,7 @@
 
   # Leave space for theme_resources since it has many structures.
   "chrome/app/theme/theme_resources.grd": {
-    "structures": [10210],
+    "structures": [10250],
   },
   # END chrome/app section.
 
diff --git a/tools/mb/mb.py b/tools/mb/mb.py
index c0489d6..be01f29 100755
--- a/tools/mb/mb.py
+++ b/tools/mb/mb.py
@@ -1064,7 +1064,7 @@
       pprint.pformat({
         'variables': {
           'command': command,
-          'files': sorted(runtime_deps + extra_files),
+          'files': sorted(set(runtime_deps + extra_files)),
         }
       }) + '\n')
 
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 445e8cf..0208f43 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -2140,6 +2140,8 @@
   <int value="4" label="Mousewheel Scroll"/>
   <int value="5" label="Mousepad Scroll"/>
   <int value="6" label="Dragged App To Border"/>
+  <int value="7" label="Move App With Keyboard"/>
+  <int value="8" label="Mouse Drag"/>
 </enum>
 
 <enum name="AppListPeekingToFullscreenSource">
@@ -44356,6 +44358,7 @@
       label="Remaining download time does not meet the requirement."/>
   <int value="8"
       label="HTTP method or url scheme does not meet the requirement."/>
+  <int value="9" label="Unknown range support from the response header."/>
 </enum>
 
 <enum name="ParentFrameKnown">
@@ -51426,6 +51429,24 @@
              start)"/>
 </enum>
 
+<enum name="SecureCookieAction">
+  <int value="0"
+      label="The provided URL was invalid, so no URL-modification was
+             performed"/>
+  <int value="1"
+      label="Cookie was already for an 'HTTPS' URL, so no URL-modification
+             was performed"/>
+  <int value="2"
+      label="Cookie data was formatted incorrectly, so no URL-modification
+             was performed"/>
+  <int value="3"
+      label="Cookie was not marked as 'Secure', so no URL-modification was
+             performed"/>
+  <int value="4"
+      label="The URL's scheme was fixed up 'HTTPS' for backwards
+             compatibility"/>
+</enum>
+
 <enum name="SecurityInterstitialDecision">
   <int value="0" label="SHOW"/>
   <int value="1" label="PROCEED"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index dd0b8cc..2e5c250 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -3411,6 +3411,16 @@
   </summary>
 </histogram>
 
+<histogram name="Android.WebView.SecureCookieAction" enum="SecureCookieAction"
+    expires_after="2020-05-14">
+  <owner>ntfschr@chromium.org</owner>
+  <owner>timvolodine@chromium.org</owner>
+  <summary>
+    Records the action WebView took (if any) to &quot;fix up&quot; secure
+    cookies for backwards compatibility.
+  </summary>
+</histogram>
+
 <histogram name="Android.WebView.ShouldInterceptRequest.InterceptionType"
     enum="InterceptionType" expires_after="2020-02-11">
   <owner>timvolodine@chromium.org</owner>
@@ -37981,14 +37991,18 @@
   </summary>
 </histogram>
 
-<histogram name="Extensions.Disabled" expires_after="M77">
+<histogram name="Extensions.Disabled" expires_after="never">
+<!-- expires-never: Used for monitoring user extension usage. -->
+
   <owner>rdevlin.cronin@chromium.org</owner>
   <summary>
     The number of extensions that are disabled at browser startup.
   </summary>
 </histogram>
 
-<histogram name="Extensions.DisabledForPermissions" expires_after="M77">
+<histogram name="Extensions.DisabledForPermissions" expires_after="never">
+<!-- expires-never: Used for monitoring user extension usage. -->
+
   <owner>rdevlin.cronin@chromium.org</owner>
   <summary>
     The number of extensions that are disabled at browser startup due to
@@ -80203,6 +80217,9 @@
 </histogram>
 
 <histogram name="NewTabPage.TilesReceivedTime" units="ms" expires_after="M77">
+  <obsolete>
+    Deprecated 06/2019. No longer used.
+  </obsolete>
   <owner>treib@chromium.org</owner>
   <summary>
     Histogram of the time, in milliseconds since navigation start, it took for
@@ -117326,6 +117343,16 @@
   </summary>
 </histogram>
 
+<histogram name="ServiceWorkerCache.DiskCacheCreateEntryResult"
+    enum="NetErrorCodes" expires_after="2019-12-31">
+  <owner>wanderview@chromium.org</owner>
+  <owner>chrome-owp-storage@google.com</owner>
+  <summary>
+    The network result code produced by disk_cache::Backend::CreateEntry() when
+    cache_storage is attempting to put a new request/response pair on disk.
+  </summary>
+</histogram>
+
 <histogram name="ServiceWorkerCache.ErrorStorageType"
     enum="CacheStorageErrorStorageType">
   <owner>wanderview@chromium.org</owner>
@@ -155485,7 +155512,11 @@
   <suffix name="Startup" label="NTP loaded during browser startup."/>
   <suffix name="Web" label="Loaded server-side NTP."/>
   <affected-histogram name="NewTabPage.LoadTime"/>
-  <affected-histogram name="NewTabPage.TilesReceivedTime"/>
+  <affected-histogram name="NewTabPage.TilesReceivedTime">
+    <obsolete>
+      Deprecated 06/2019.
+    </obsolete>
+  </affected-histogram>
 </histogram_suffixes>
 
 <histogram_suffixes name="NewTabPageTimingsIsGoogle" separator=".">
@@ -155509,6 +155540,12 @@
   <affected-histogram name="Tabs.StateTransfer.Time_Inactive"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="NoAcceptRangesHeader" separator=".">
+  <suffix name="NoAcceptRangesHeader"
+      label="For parallel requests created without accept-ranges header."/>
+  <affected-histogram name="Download.ParallelDownloadAddStreamSuccess"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="NotificationDisplayExperiment" separator="_">
   <obsolete>
     Deprecated October 2017 (feature enabled by default).
diff --git a/tools/traffic_annotation/auditor/traffic_annotation_auditor.cc b/tools/traffic_annotation/auditor/traffic_annotation_auditor.cc
index 281d832..c8e0151 100644
--- a/tools/traffic_annotation/auditor/traffic_annotation_auditor.cc
+++ b/tools/traffic_annotation/auditor/traffic_annotation_auditor.cc
@@ -83,6 +83,12 @@
         .Append(FILE_PATH_LITERAL("scripts"))
         .Append(FILE_PATH_LITERAL("run_tool.py"));
 
+const base::FilePath kExtractorScript =
+    base::FilePath(FILE_PATH_LITERAL("tools"))
+        .Append(FILE_PATH_LITERAL("traffic_annotation"))
+        .Append(FILE_PATH_LITERAL("scripts"))
+        .Append(FILE_PATH_LITERAL("extractor.py"));
+
 // Checks if the list of |path_filters| include the given |file_path|, or there
 // are path filters which are a folder (don't have a '.' in their name), and
 // match the file name.
@@ -176,12 +182,16 @@
       .Next();
 }
 
-bool TrafficAnnotationAuditor::RunClangTool(
+bool TrafficAnnotationAuditor::RunExtractor(
+    ExtractorBackend backend,
     const std::vector<std::string>& path_filters,
     bool filter_files_based_on_heuristics,
     bool use_compile_commands,
     bool rerun_on_errors,
     const base::FilePath& errors_file) {
+  DCHECK(backend == ExtractorBackend::CLANG_TOOL ||
+         backend == ExtractorBackend::PYTHON_SCRIPT);
+
   if (!safe_list_loaded_ && !LoadSafeList())
     return false;
 
@@ -204,51 +214,41 @@
     return false;
   }
 
-  // As the checked out clang tool may be in a directory different from the
-  // default one (third_party/llvm-buid/Release+Asserts/bin), its path and
-  // clang's library folder should be passed to the run_tool.py script.
-  fprintf(
-      options_file,
-      "--generate-compdb --tool=traffic_annotation_extractor -p=%s "
-      "--tool-path=%s "
-      "--tool-arg=--extra-arg=-resource-dir=%s ",
-      build_path_.MaybeAsASCII().c_str(),
-      base::MakeAbsoluteFilePath(clang_tool_path_).MaybeAsASCII().c_str(),
-      base::MakeAbsoluteFilePath(GetClangLibraryPath()).MaybeAsASCII().c_str());
+  // Write some options to the file, which depends on the backend used.
+  if (backend == ExtractorBackend::CLANG_TOOL)
+    WriteClangToolOptions(options_file, use_compile_commands);
+  else if (backend == ExtractorBackend::PYTHON_SCRIPT)
+    WritePythonScriptOptions(options_file);
 
-  for (const std::string& item : clang_tool_switches_)
-    fprintf(options_file, "--tool-arg=--extra-arg=%s ", item.c_str());
-
-  if (use_compile_commands)
-    fprintf(options_file, "--all ");
-
+  // Write the file paths regardless of backend.
   for (const std::string& file_path : file_paths)
     fprintf(options_file, "%s ", file_path.c_str());
 
   base::CloseFile(options_file);
 
+  const base::FilePath& script_path =
+      (backend == ExtractorBackend::CLANG_TOOL ? kRunToolScript
+                                               : kExtractorScript);
   base::CommandLine cmdline(
-      base::MakeAbsoluteFilePath(source_path_.Append(kRunToolScript)));
-
+      base::MakeAbsoluteFilePath(source_path_.Append(script_path)));
 #if defined(OS_WIN)
   cmdline.PrependWrapper(L"python");
 #endif
-
   cmdline.AppendArg(base::StringPrintf(
       "--options-file=%s", options_filepath.MaybeAsASCII().c_str()));
 
-  // Change current folder to source before running run_tool.py as it expects to
+  // Change current folder to source before running the command as it expects to
   // be run from there.
   base::FilePath original_path;
   base::GetCurrentDirectory(&original_path);
   base::SetCurrentDirectory(source_path_);
-  bool result = base::GetAppOutput(cmdline, &clang_tool_raw_output_);
+  bool result = base::GetAppOutput(cmdline, &extractor_raw_output_);
 
-  // If running clang tool had no output, it means that the script running it
-  // could not perform the task.
-  if (clang_tool_raw_output_.empty()) {
+  // If the extractor had no output, it means that the script running it could
+  // not perform the task.
+  if (extractor_raw_output_.empty()) {
     result = false;
-  } else if (!result) {
+  } else if (backend == ExtractorBackend::CLANG_TOOL && !result) {
     // If clang tool had errors but also returned results, the errors can be
     // ignored as we do not separate platform specific files here and processing
     // them fails. This is a post-build test and if there exists any actual
@@ -258,7 +258,8 @@
   }
 
   if (!result) {
-    if (use_compile_commands && !clang_tool_raw_output_.empty()) {
+    if (backend == ExtractorBackend::CLANG_TOOL && use_compile_commands &&
+        !extractor_raw_output_.empty()) {
       printf(
           "\nWARNING: Ignoring clang tool error as it is called using "
           "compile_commands.json which will result in processing some "
@@ -305,6 +306,33 @@
   return result;
 }
 
+void TrafficAnnotationAuditor::WriteClangToolOptions(
+    FILE* options_file,
+    bool use_compile_commands) {
+  // As the checked out clang tool may be in a directory different from the
+  // default one (third_party/llvm-buid/Release+Asserts/bin), its path and
+  // clang's library folder should be passed to the run_tool.py script.
+  fprintf(
+      options_file,
+      "--generate-compdb --tool=traffic_annotation_extractor -p=%s "
+      "--tool-path=%s "
+      "--tool-arg=--extra-arg=-resource-dir=%s ",
+      build_path_.MaybeAsASCII().c_str(),
+      base::MakeAbsoluteFilePath(clang_tool_path_).MaybeAsASCII().c_str(),
+      base::MakeAbsoluteFilePath(GetClangLibraryPath()).MaybeAsASCII().c_str());
+
+  for (const std::string& item : clang_tool_switches_)
+    fprintf(options_file, "--tool-arg=--extra-arg=%s ", item.c_str());
+
+  if (use_compile_commands)
+    fprintf(options_file, "--all ");
+}
+
+void TrafficAnnotationAuditor::WritePythonScriptOptions(FILE* options_file) {
+  fprintf(options_file, "--generate-compdb --build-path=%s ",
+          build_path_.MaybeAsASCII().c_str());
+}
+
 void TrafficAnnotationAuditor::GenerateFilesListForClangTool(
     const std::vector<std::string>& path_filters,
     bool filter_files_based_on_heuristics,
@@ -400,7 +428,7 @@
     return false;
   // Remove possible carriage return characters before splitting lines.
   std::string temp_string;
-  base::RemoveChars(clang_tool_raw_output_, "\r", &temp_string);
+  base::RemoveChars(extractor_raw_output_, "\r", &temp_string);
   std::vector<std::string> lines = base::SplitString(
       temp_string, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
   for (unsigned int current = 0; current < lines.size(); current++) {
diff --git a/tools/traffic_annotation/auditor/traffic_annotation_auditor.h b/tools/traffic_annotation/auditor/traffic_annotation_auditor.h
index c95e5f3a..27cbc46 100644
--- a/tools/traffic_annotation/auditor/traffic_annotation_auditor.h
+++ b/tools/traffic_annotation/auditor/traffic_annotation_auditor.h
@@ -14,6 +14,12 @@
 #include "tools/traffic_annotation/auditor/traffic_annotation_exporter.h"
 #include "tools/traffic_annotation/traffic_annotation.pb.h"
 
+enum class ExtractorBackend {
+  CLANG_TOOL,
+  PYTHON_SCRIPT,
+  INVALID,
+};
+
 // Holds an item of safe list rules for auditor.
 struct AuditorException {
   enum class ExceptionType {
@@ -53,23 +59,23 @@
                            const base::FilePath& clang_tool_path);
   ~TrafficAnnotationAuditor();
 
-  // Runs traffic_annotation_extractor clang tool and puts its output in
-  // |clang_tool_raw_output_|. If |filter_files_based_on_heuristics| flag is
-  // set, the list of files will be received from repository and heuristically
-  // filtered to only process the relevant files. If |use_compile_commands| flag
-  // is set, the list of files is extracted from compile_commands.json instead
-  // of git and will not be filtered.
-  // If clang tool returns error, and |rerun_on_errors| is true, the tool is run
-  // again to record errors.
-  // Errors are written to |errors_file| if it is not empty, otherwise
-  // LOG(ERROR).
-  bool RunClangTool(const std::vector<std::string>& path_filters,
+  // Runs traffic_annotation_extractor clang tool (or extractor.py script) and
+  // puts its output in |extractor_raw_output_|. If
+  // |filter_files_based_on_heuristics| flag is set, the list of files will be
+  // received from repository and heuristically filtered to only process the
+  // relevant files. If |use_compile_commands| flag is set, the list of files is
+  // extracted from compile_commands.json instead of git and will not be
+  // filtered.  If clang tool returns error, and |rerun_on_errors| is true, the
+  // tool is run again to record errors.  Errors are written to |errors_file| if
+  // it is not empty, otherwise LOG(ERROR).
+  bool RunExtractor(ExtractorBackend backend,
+                    const std::vector<std::string>& path_filters,
                     bool filter_files_based_on_heuristics,
                     bool use_compile_commands,
                     bool rerun_on_errors,
                     const base::FilePath& errors_file);
 
-  // Parses the output of clang tool (|clang_tool_raw_output_|) and populates
+  // Parses the output of clang tool (|extractor_raw_output_|) and populates
   // |extracted_annotations_|, |extracted_calls_|, and |errors_|.
   // Errors include not finding the file, incorrect content, or missing or not
   // provided annotations.
@@ -117,10 +123,10 @@
   // net/traffic_annotation/network_traffic_annotation_test_helper.h
   static std::set<int> GetReservedIDsSet();
 
-  std::string clang_tool_raw_output() const { return clang_tool_raw_output_; }
+  std::string extractor_raw_output() const { return extractor_raw_output_; }
 
-  void set_clang_tool_raw_output(const std::string& raw_output) {
-    clang_tool_raw_output_ = raw_output;
+  void set_extractor_raw_output(const std::string& raw_output) {
+    extractor_raw_output_ = raw_output;
   }
 
   const std::vector<AnnotationInstance>& extracted_annotations() const {
@@ -168,7 +174,7 @@
 
   TrafficAnnotationExporter exporter_;
 
-  std::string clang_tool_raw_output_;
+  std::string extractor_raw_output_;
   std::vector<AnnotationInstance> extracted_annotations_;
   std::vector<CallInstance> extracted_calls_;
   std::vector<AuditorResult> errors_;
@@ -187,7 +193,7 @@
   //  3- Path matches an item in |path_filters|.
   void AddMissingAnnotations(const std::vector<std::string>& path_filters);
 
-  // Generates files list to Run clang tool on. Please refer to RunClangTool
+  // Generates files list to Run clang tool on. Please refer to RunExtractor
   // function's comment.
   void GenerateFilesListForClangTool(
       const std::vector<std::string>& path_filters,
@@ -195,6 +201,10 @@
       bool use_compile_commands,
       std::vector<std::string>* file_paths);
 
+  // Write flags to the options file, for RunExtractor.
+  void WriteClangToolOptions(FILE* options_file, bool use_compile_commands);
+  void WritePythonScriptOptions(FILE* options_file);
+
   base::FilePath gn_file_for_test_;
   std::map<std::string, bool> checked_dependencies_;
 };
diff --git a/tools/traffic_annotation/auditor/traffic_annotation_auditor_ui.cc b/tools/traffic_annotation/auditor/traffic_annotation_auditor_ui.cc
index 5bcc9d0..be06e48 100644
--- a/tools/traffic_annotation/auditor/traffic_annotation_auditor_ui.cc
+++ b/tools/traffic_annotation/auditor/traffic_annotation_auditor_ui.cc
@@ -64,6 +64,10 @@
   --error-resilient   Optional flag, stating not to return error in exit code if
                       auditor fails to perform the tests. This flag can be used
                       for trybots to avoid spamming when tests cannot run.
+  --extractor-backend=[clang_tool,python_script]
+                      Optional flag specifying which backend to use for
+                      extracting annotation definitions from source code (Clang
+                      Tool or extractor.py). Defaults to "clang_tool".
   path_filters        Optional paths to filter which files the tool is run on.
                       It can also include deleted files names when auditor is
                       run on a partial repository.
@@ -128,6 +132,14 @@
   return text;
 }
 
+ExtractorBackend GetExtractorBackend(const std::string& backend_switch) {
+  if (backend_switch.empty() || backend_switch == "clang_tool")
+    return ExtractorBackend::CLANG_TOOL;
+  if (backend_switch == "python_script")
+    return ExtractorBackend::PYTHON_SCRIPT;
+  return ExtractorBackend::INVALID;
+}
+
 // TODO(rhalavati): Update this function to extract the policy name and value
 // directly from the ChromeSettingsProto object (gen/components/policy/proto/
 // chrome_settings.proto). Since ChromeSettingsProto has over 300+
@@ -368,7 +380,15 @@
 
   // Extract annotations.
   if (extractor_input.empty()) {
-    if (!auditor.RunClangTool(path_filters, filter_files, all_files,
+    std::string backend_switch =
+        command_line.GetSwitchValueASCII("extractor-backend");
+    ExtractorBackend backend = GetExtractorBackend(backend_switch);
+    if (backend == ExtractorBackend::INVALID) {
+      LOG(ERROR) << "Unrecognized extractor backend '" << backend_switch << "'";
+      return error_value;
+    }
+
+    if (!auditor.RunExtractor(backend, path_filters, filter_files, all_files,
                               !error_resilient, errors_file)) {
       LOG(ERROR) << "Failed to run clang tool.";
       return error_value;
@@ -376,7 +396,7 @@
 
     // Write extractor output if requested.
     if (!extractor_output.empty()) {
-      std::string raw_output = auditor.clang_tool_raw_output();
+      std::string raw_output = auditor.extractor_raw_output();
       base::WriteFile(extractor_output, raw_output.c_str(),
                       raw_output.length());
     }
@@ -387,7 +407,7 @@
                  << extractor_input.value().c_str();
       return error_value;
     } else {
-      auditor.set_clang_tool_raw_output(raw_output);
+      auditor.set_extractor_raw_output(raw_output);
     }
   }
 
@@ -469,4 +489,4 @@
     printf("Traffic annotations are all OK.\n");
 
   return static_cast<int>(errors.size());
-}
\ No newline at end of file
+}
diff --git a/tools/traffic_annotation/bin/linux64/traffic_annotation_auditor.sha1 b/tools/traffic_annotation/bin/linux64/traffic_annotation_auditor.sha1
index 3af4147f8..5a4fdb23 100644
--- a/tools/traffic_annotation/bin/linux64/traffic_annotation_auditor.sha1
+++ b/tools/traffic_annotation/bin/linux64/traffic_annotation_auditor.sha1
@@ -1 +1 @@
-d9314aa2e3fab7ef7eb3fb38a392b40877cf5826
\ No newline at end of file
+ee8d3da6d373117d1167ab7776b05497b6162061
\ No newline at end of file
diff --git a/tools/traffic_annotation/bin/win32/traffic_annotation_auditor.exe.sha1 b/tools/traffic_annotation/bin/win32/traffic_annotation_auditor.exe.sha1
index 969d5d55..14efb6e 100644
--- a/tools/traffic_annotation/bin/win32/traffic_annotation_auditor.exe.sha1
+++ b/tools/traffic_annotation/bin/win32/traffic_annotation_auditor.exe.sha1
@@ -1 +1 @@
-7164ebdf3c7b5e7d886ba415792493a71924c0a8
\ No newline at end of file
+beaf7c163468e648aa19354fc6332386328dc74f
\ No newline at end of file
diff --git a/tools/traffic_annotation/scripts/annotation_tools.py b/tools/traffic_annotation/scripts/annotation_tools.py
index 8180f4d..68d4436 100644
--- a/tools/traffic_annotation/scripts/annotation_tools.py
+++ b/tools/traffic_annotation/scripts/annotation_tools.py
@@ -4,6 +4,7 @@
 
 """Tools for annotation test scripts."""
 
+import json
 import os
 import subprocess
 import sys
@@ -66,13 +67,23 @@
     return all(os.path.exists(
         os.path.join(path, item)) for item in ('gen', 'build.ninja'))
 
-  def GetCompDBFiles(self):
+  def GetCompDBFiles(self, generate_compdb):
     """Gets the list of files.
 
+    Args:
+      generate_compdb: if true, generate a new compdb and write it to
+                       compile_commands.json.
+
     Returns:
       A set of absolute filepaths, with all compile-able C++ files (based on the
       compilation database).
     """
+    if generate_compdb:
+      compile_commands = compile_db.GenerateWithNinja(self.build_path)
+      compdb_path = os.path.join(self.build_path, 'compile_commands.json')
+      with open(compdb_path, 'w') as f:
+        f.write(json.dumps(compile_commands, indent=2))
+
     compdb = compile_db.Read(self.build_path)
     return set(
         os.path.abspath(os.path.join(self.build_path, e['file']))
diff --git a/tools/traffic_annotation/scripts/extractor.py b/tools/traffic_annotation/scripts/extractor.py
index 0b2c2f7..df61f493 100755
--- a/tools/traffic_annotation/scripts/extractor.py
+++ b/tools/traffic_annotation/scripts/extractor.py
@@ -149,21 +149,30 @@
 
 
 def main():
-  parser = argparse.ArgumentParser(
-      description="Network Traffic Annotation Extractor.")
+  parser = argparse.ArgumentParser()
+  parser.add_argument(
+      '--options-file',
+      help='optional file to read options from')
+  args, argv = parser.parse_known_args()
+  if args.options_file:
+    argv = open(args.options_file).read().split()
+
   parser.add_argument(
       '--build-path',
       help='Specifies a compiled build directory, e.g. out/Debug.')
   parser.add_argument(
+      '--generate-compdb', action='store_true',
+      help='Generate a new compile_commands.json before running')
+  parser.add_argument(
       '--no-filter', action='store_true',
       help='Do not filter files based on compdb entries')
   parser.add_argument(
       'file_paths', nargs='+', help='List of files to process.')
 
-  args = parser.parse_args()
+  args = parser.parse_args(argv)
 
   tools = NetworkTrafficAnnotationTools(args.build_path)
-  compdb_files = tools.GetCompDBFiles()
+  compdb_files = tools.GetCompDBFiles(args.generate_compdb)
 
   annotation_definitions = []
 
diff --git a/tools/traffic_annotation/scripts/extractor_test.py b/tools/traffic_annotation/scripts/extractor_test.py
index d3c58e4..531ab2b 100755
--- a/tools/traffic_annotation/scripts/extractor_test.py
+++ b/tools/traffic_annotation/scripts/extractor_test.py
@@ -27,6 +27,11 @@
   return (stdout_file, stderr_file)
 
 
+def dos2unix(str):
+  """Convers CRLF to LF."""
+  return str.replace('\r\n', '\n')
+
+
 def remove_tracebacks(str):
   """Removes python tracebacks from the string."""
   regex = re.compile(
@@ -46,12 +51,12 @@
       print("Running test on %s..." % source_file)
       (stdout_file, stderr_file) = get_expected_files(source_file)
       with open(stdout_file) as f:
-        expected_stdout = f.read()
+        expected_stdout = dos2unix(f.read())
       with open(stderr_file) as f:
-        expected_stderr = f.read()
+        expected_stderr = dos2unix(f.read())
 
       proc = run_extractor(source_file)
-      (stdout, stderr) = proc.communicate()
+      (stdout, stderr) = map(dos2unix, proc.communicate())
 
       self.assertEqual(expected_stderr, remove_tracebacks(stderr))
       self.assertEqual(int(bool(expected_stderr)), proc.returncode)
diff --git a/tools/traffic_annotation/scripts/test_data/valid_file-stdout.txt b/tools/traffic_annotation/scripts/test_data/valid_file-stdout.txt
index 749d5c2..7d92dcf 100644
--- a/tools/traffic_annotation/scripts/test_data/valid_file-stdout.txt
+++ b/tools/traffic_annotation/scripts/test_data/valid_file-stdout.txt
@@ -1,5 +1,5 @@
 ==== NEW ANNOTATION ====
-source_file.cc
+valid_file.cc
 XXX_UNIMPLEMENTED_XXX
 14
 Definition
@@ -25,7 +25,7 @@
         comments: "comment1"
 ==== ANNOTATION ENDS ====
 ==== NEW ANNOTATION ====
-source_file.cc
+valid_file.cc
 XXX_UNIMPLEMENTED_XXX
 36
 Partial
@@ -41,7 +41,7 @@
         }
 ==== ANNOTATION ENDS ====
 ==== NEW ANNOTATION ====
-source_file.cc
+valid_file.cc
 XXX_UNIMPLEMENTED_XXX
 46
 Completing
@@ -61,7 +61,7 @@
         comments: "comment3"
 ==== ANNOTATION ENDS ====
 ==== NEW ANNOTATION ====
-source_file.cc
+valid_file.cc
 XXX_UNIMPLEMENTED_XXX
 61
 BranchedCompleting
diff --git a/ui/accessibility/ax_language_info.cc b/ui/accessibility/ax_language_info.cc
index f56da0f..622a8eb 100644
--- a/ui/accessibility/ax_language_info.cc
+++ b/ui/accessibility/ax_language_info.cc
@@ -7,6 +7,8 @@
 #include <functional>
 
 #include "base/command_line.h"
+#include "base/i18n/unicodestring.h"
+#include "base/strings/utf_string_conversions.h"
 #include "ui/accessibility/accessibility_switches.h"
 #include "ui/accessibility/ax_enums.mojom.h"
 #include "ui/accessibility/ax_tree.h"
@@ -22,12 +24,20 @@
 // input we give it, 3 was recommended to us by the ML team as a good
 // starting point.
 const auto kMaxDetectedLanguagesPerSpan = 3;
+
+const auto kShortTextIdentifierMinByteLength = 1;
+// TODO(https://bugs.chromium.org/p/chromium/issues/detail?id=971360):
+// Determine appropriate value for kShortTextIdentifierMaxByteLength.
+const auto kShortTextIdentifierMaxByteLength = 1000;
 }  // namespace
 
 AXLanguageInfo::AXLanguageInfo() {}
 AXLanguageInfo::~AXLanguageInfo() {}
 
-AXLanguageInfoStats::AXLanguageInfoStats() : top_results_valid_(false) {}
+AXLanguageInfoStats::AXLanguageInfoStats()
+    : top_results_valid_(false),
+      short_text_language_identifier_(kShortTextIdentifierMinByteLength,
+                                      kShortTextIdentifierMaxByteLength) {}
 AXLanguageInfoStats::~AXLanguageInfoStats() = default;
 
 chrome_lang_id::NNetLanguageIdentifier&
@@ -98,7 +108,6 @@
 // Detect language for a subtree rooted at the given node.
 void DetectLanguageForSubtree(AXNode* subtree_root, class AXTree* tree) {
   TRACE_EVENT0("accessibility", "AXLanguageInfo::DetectLanguageForSubtree");
-
   DCHECK(subtree_root);
   DCHECK(tree);
   if (!::switches::IsExperimentalAccessibilityLanguageDetectionEnabled()) {
@@ -252,4 +261,56 @@
   }
 }
 
+std::vector<LanguageSpan>
+AXLanguageInfoStats::GetLanguageAnnotationForStringAttribute(
+    const AXNode& node,
+    ax::mojom::StringAttribute attr) {
+  std::vector<LanguageSpan> language_annotation;
+  if (!node.HasStringAttribute(attr))
+    return language_annotation;
+
+  std::string attr_value = node.GetStringAttribute(attr);
+
+  // Use author-provided language if present.
+  if (node.HasStringAttribute(ax::mojom::StringAttribute::kLanguage)) {
+    // Use author-provided language if present.
+    language_annotation.push_back(
+        LanguageSpan{0 /* start_index */, attr_value.length() /* end_index */,
+                     node.GetStringAttribute(
+                         ax::mojom::StringAttribute::kLanguage) /* language */,
+                     1 /* probability */});
+    return language_annotation;
+  }
+  // Calculate top 3 languages.
+  // TODO(akihiroota): What's a reasonable number of languages to have
+  // cld_3 find? Should vary.
+  std::vector<chrome_lang_id::NNetLanguageIdentifier::Result> top_languages =
+      short_text_language_identifier_.FindTopNMostFreqLangs(
+          attr_value, kMaxDetectedLanguagesPerPage);
+  // Create vector of LanguageSpans.
+  for (const auto& result : top_languages) {
+    std::vector<chrome_lang_id::NNetLanguageIdentifier::SpanInfo> ranges =
+        result.byte_ranges;
+    for (const auto& span_info : ranges) {
+      language_annotation.push_back(
+          LanguageSpan{span_info.start_index, span_info.end_index,
+                       result.language, span_info.probability});
+    }
+  }
+  // Sort Language Annotations by increasing start index. LanguageAnnotations
+  // with lower start index should appear earlier in the vector.
+  std::sort(language_annotation.begin(), language_annotation.end(),
+            [](const LanguageSpan& left, const LanguageSpan& right) -> bool {
+              return left.start_index <= right.start_index;
+            });
+  // Ensure that LanguageSpans do not overlap.
+  for (size_t i = 0; i < language_annotation.size(); ++i) {
+    if (i > 0) {
+      DCHECK(language_annotation[i].start_index <=
+             language_annotation[i - 1].end_index);
+    }
+  }
+  return language_annotation;
+}
+
 }  // namespace ui
diff --git a/ui/accessibility/ax_language_info.h b/ui/accessibility/ax_language_info.h
index cf09156..32e10308 100644
--- a/ui/accessibility/ax_language_info.h
+++ b/ui/accessibility/ax_language_info.h
@@ -11,9 +11,9 @@
 #include <vector>
 
 #include "base/macros.h"
-#include "ui/accessibility/ax_export.h"
-
 #include "third_party/cld_3/src/src/nnet_language_identifier.h"
+#include "ui/accessibility/ax_enums.mojom.h"
+#include "ui/accessibility/ax_export.h"
 
 namespace ui {
 
@@ -29,8 +29,12 @@
 //   AXLanguageInfoStats represents the 'global' (tree-level) language detection
 //                       data for all nodes within an AXTree.
 //
-// Language detection is implemented as a two-pass process to reduce the
-// assignment of spurious languages.
+// Language detection is separated into two use cases: page-level and
+// inner-node-level.
+//
+//
+// Language detection at the page-level is implemented as a two-pass process to
+// reduce the assignment of spurious languages.
 //
 // After the first pass no languages have been assigned to AXNode(s), this is
 // left to the second pass so that we can take use tree-level statistics to
@@ -46,6 +50,15 @@
 // subtree from a given AXNode and attempts to find an appropriate language to
 // associate with each AXNode based on a combination of the local detection
 // results (AXLanguageInfo) and the global stats (AXLanguageInfoStats).
+//
+//
+// Language detection at the inner-node level is different from that at the
+// page-level because in this case, we operate on much smaller pieces of text.
+// For this use case, we would like to detect languages that may only occur
+// once throughout the entire document. Inner-node-level language detection
+// is performed by using a language identifier constructed with a byte minimum
+// of kShortTextIdentifierMinByteLength. This way, it can potentially detect the
+// language of strings that are as short as one character in length.
 
 // An instance of AXLanguageInfo is used to record the detected and assigned
 // languages for a single AXNode, this data is entirely local to the AXNode.
@@ -80,6 +93,20 @@
   std::vector<std::string> detected_languages;
 };
 
+// Each LanguageSpan contains a language, a probability, and start and end
+// indices. The indices are used to specify the substring that contains the
+// associated language. The string which the indices are relative to is not
+// included in this structure.
+// Also, the indices are relative to a Utf8 string.
+// See documentation on GetLanguageAnnotationForStringAttribute for details
+// on how to associate this object with a string.
+struct AX_EXPORT LanguageSpan {
+  int start_index;
+  int end_index;
+  std::string language;
+  float probability;
+};
+
 // A single AXLanguageInfoStats instance is stored on each AXTree and represents
 // the language detection statistics for every AXNode within that AXTree.
 //
@@ -104,6 +131,14 @@
 
   chrome_lang_id::NNetLanguageIdentifier& GetLanguageIdentifier();
 
+  // Detect and return languages for string attribute.
+  // For example, if a node has name: "My name is Fred", then calling
+  // GetLanguageAnnotationForStringAttribute(*node, ax::mojom::StringAttribute::
+  // kName) would return language detection information about "My name is Fred".
+  std::vector<LanguageSpan> GetLanguageAnnotationForStringAttribute(
+      const AXNode& node,
+      ax::mojom::StringAttribute attr);
+
  private:
   // Store a count of the occurrences of a given language.
   std::unordered_map<std::string, unsigned int> lang_counts_;
@@ -121,8 +156,16 @@
   // Populate top_results_.
   void GenerateTopResults();
 
+  // This language identifier is constructed with a default minimum byte length
+  // of chrome_lang_id::NNetLanguageIdentifier::kMinNumBytesToConsider and is
+  // used for detecting page-level languages.
   chrome_lang_id::NNetLanguageIdentifier language_identifier_;
 
+  // This language identifier is constructed with a minimum byte length of
+  // kShortTextIdentifierMinByteLength so it can be used for detecting languages
+  // of shorter text (e.g. one character).
+  chrome_lang_id::NNetLanguageIdentifier short_text_language_identifier_;
+
   DISALLOW_COPY_AND_ASSIGN(AXLanguageInfoStats);
 };
 
@@ -141,7 +184,6 @@
 // returns boolean indicating success.
 AX_EXPORT bool LabelLanguageForSubtree(AXNode* subtree_root,
                                        class AXTree* tree);
-
 }  // namespace ui
 
 #endif  // UI_ACCESSIBILITY_AX_LANGUAGE_INFO
diff --git a/ui/accessibility/ax_language_info_unittest.cc b/ui/accessibility/ax_language_info_unittest.cc
index 2158a5f..b7c6b68 100644
--- a/ui/accessibility/ax_language_info_unittest.cc
+++ b/ui/accessibility/ax_language_info_unittest.cc
@@ -679,4 +679,126 @@
   EXPECT_FALSE(stats.CheckLanguageWithinTop("zz"));
 }
 
+TEST(AXLanguageInfoTest, ShortLanguageDetectorLabeledTest) {
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(
+      ::switches::kEnableExperimentalAccessibilityLanguageDetection);
+  AXTreeUpdate initial_state;
+  initial_state.root_id = 1;
+  initial_state.nodes.resize(2);
+  initial_state.nodes[0].id = 1;
+  initial_state.nodes[0].child_ids = {2};
+  initial_state.nodes[1].id = 2;
+  initial_state.nodes[1].AddStringAttribute(ax::mojom::StringAttribute::kName,
+                                            "Hello");
+  initial_state.nodes[1].AddStringAttribute(
+      ax::mojom::StringAttribute::kLanguage, "en");
+  AXTree tree(initial_state);
+
+  AXNode* item = tree.GetFromId(2);
+  std::vector<LanguageSpan> annotation;
+  // Empty output.
+  annotation = item->GetLanguageAnnotationForStringAttribute(
+      ax::mojom::StringAttribute::kInnerHtml);
+  ASSERT_EQ(0, (int)annotation.size());
+  // Returns single LanguageSpan.
+  annotation = item->GetLanguageAnnotationForStringAttribute(
+      ax::mojom::StringAttribute::kName);
+  ASSERT_EQ(1, (int)annotation.size());
+  LanguageSpan* lang_span = &annotation[0];
+  ASSERT_EQ("en", lang_span->language);
+  std::string name =
+      item->GetStringAttribute(ax::mojom::StringAttribute::kName);
+  ASSERT_EQ("Hello",
+            name.substr(lang_span->start_index,
+                        lang_span->end_index - lang_span->start_index));
+}
+
+TEST(AXLanguageInfoTest, ShortLanguageDetectorCharacterTest) {
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(
+      ::switches::kEnableExperimentalAccessibilityLanguageDetection);
+  AXTreeUpdate initial_state;
+  initial_state.root_id = 1;
+  initial_state.nodes.resize(2);
+  initial_state.nodes[0].id = 1;
+  initial_state.nodes[0].child_ids = {2};
+  initial_state.nodes[1].id = 2;
+  initial_state.nodes[1].AddStringAttribute(ax::mojom::StringAttribute::kName,
+                                            "δ");
+  AXTree tree(initial_state);
+
+  AXNode* item = tree.GetFromId(2);
+  std::vector<LanguageSpan> annotation;
+  // Returns single LanguageSpan.
+  annotation = item->GetLanguageAnnotationForStringAttribute(
+      ax::mojom::StringAttribute::kName);
+  ASSERT_EQ(1, (int)annotation.size());
+  LanguageSpan* lang_span = &annotation[0];
+  ASSERT_EQ("el", lang_span->language);
+  std::string name =
+      item->GetStringAttribute(ax::mojom::StringAttribute::kName);
+  ASSERT_EQ("δ", name.substr(lang_span->start_index,
+                             lang_span->end_index - lang_span->start_index));
+}
+
+TEST(AXLanguageInfoTest, ShortLanguageDetectorMultipleLanguagesTest) {
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(
+      ::switches::kEnableExperimentalAccessibilityLanguageDetection);
+  AXTreeUpdate initial_state;
+  initial_state.root_id = 1;
+  initial_state.nodes.resize(2);
+  initial_state.nodes[0].id = 1;
+  initial_state.nodes[0].child_ids = {2};
+  initial_state.nodes[1].id = 2;
+  initial_state.nodes[1].AddStringAttribute(
+      ax::mojom::StringAttribute::kName,
+      "This text should be read in English. 차에 한하여 중임할 수. Followed "
+      "by English.");
+  AXTree tree(initial_state);
+
+  AXNode* item = tree.GetFromId(2);
+  std::vector<LanguageSpan> annotation =
+      item->GetLanguageAnnotationForStringAttribute(
+          ax::mojom::StringAttribute::kName);
+  ASSERT_EQ(3, (int)annotation.size());
+  std::string name =
+      item->GetStringAttribute(ax::mojom::StringAttribute::kName);
+  LanguageSpan* lang_span = &annotation[0];
+  ASSERT_EQ("This text should be read in English. ",
+            name.substr(lang_span->start_index,
+                        lang_span->end_index - lang_span->start_index));
+  lang_span = &annotation[1];
+  ASSERT_EQ("차에 한하여 중임할 수. ",
+            name.substr(lang_span->start_index,
+                        lang_span->end_index - lang_span->start_index));
+  lang_span = &annotation[2];
+  ASSERT_EQ("Followed by English.",
+            name.substr(lang_span->start_index,
+                        lang_span->end_index - lang_span->start_index));
+}
+
+// Assert that GetLanguageAnnotationForStringAttribute works for attributes
+// other than kName.
+TEST(AXLanguageInfoTest, DetectLanguageForRoleTest) {
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(
+      ::switches::kEnableExperimentalAccessibilityLanguageDetection);
+  AXTreeUpdate initial_state;
+  initial_state.root_id = 1;
+  initial_state.nodes.resize(1);
+  initial_state.nodes[0].id = 1;
+  initial_state.nodes[0].AddStringAttribute(ax::mojom::StringAttribute::kValue,
+                                            "どうぞよろしくお願いします.");
+  AXTree tree(initial_state);
+  AXNode* item = tree.GetFromId(1);
+  std::vector<LanguageSpan> annotation =
+      item->GetLanguageAnnotationForStringAttribute(
+          ax::mojom::StringAttribute::kValue);
+  ASSERT_EQ(1, (int)annotation.size());
+  std::string value =
+      item->GetStringAttribute(ax::mojom::StringAttribute::kValue);
+  LanguageSpan* lang_span = &annotation[0];
+  ASSERT_EQ("どうぞよろしくお願いします.",
+            value.substr(lang_span->start_index,
+                         lang_span->end_index - lang_span->start_index));
+}
+
 }  // namespace ui
diff --git a/ui/accessibility/ax_node.cc b/ui/accessibility/ax_node.cc
index 1f79991b..9310963 100644
--- a/ui/accessibility/ax_node.cc
+++ b/ui/accessibility/ax_node.cc
@@ -710,4 +710,9 @@
   return result;
 }
 
+std::vector<LanguageSpan> AXNode::GetLanguageAnnotationForStringAttribute(
+    ax::mojom::StringAttribute attr) const {
+  return tree_->GetLanguageAnnotationForStringAttribute(*this, attr);
+}
+
 }  // namespace ui
diff --git a/ui/accessibility/ax_node.h b/ui/accessibility/ax_node.h
index 68a05367..102d47dc 100644
--- a/ui/accessibility/ax_node.h
+++ b/ui/accessibility/ax_node.h
@@ -20,6 +20,7 @@
 
 class AXTableInfo;
 struct AXLanguageInfo;
+struct LanguageSpan;
 
 // One node in an AXTree.
 class AX_EXPORT AXNode final {
@@ -40,6 +41,10 @@
     virtual int32_t GetSetSize(const AXNode& node,
                                const AXNode* ordered_set) = 0;
     virtual bool GetTreeUpdateInProgressState() const = 0;
+
+    virtual std::vector<LanguageSpan> GetLanguageAnnotationForStringAttribute(
+        const AXNode& node,
+        ax::mojom::StringAttribute attr) const = 0;
   };
 
   // The constructor requires a parent, id, and index in parent, but
@@ -298,6 +303,9 @@
   // part of the language detection feature.
   void SetLanguageInfo(std::unique_ptr<AXLanguageInfo> lang_info);
 
+  std::vector<LanguageSpan> GetLanguageAnnotationForStringAttribute(
+      ax::mojom::StringAttribute) const;
+
  private:
   // Computes the text offset where each line starts by traversing all child
   // leaf nodes.
diff --git a/ui/accessibility/ax_tree.cc b/ui/accessibility/ax_tree.cc
index a58715a..00b5250 100644
--- a/ui/accessibility/ax_tree.cc
+++ b/ui/accessibility/ax_tree.cc
@@ -160,10 +160,14 @@
   initial_state.root_id = -1;
   initial_state.nodes.push_back(root);
   CHECK(Unserialize(initial_state)) << error();
+  DCHECK(!language_info_stats);
+  language_info_stats.reset(new AXLanguageInfoStats());
 }
 
 AXTree::AXTree(const AXTreeUpdate& initial_state) {
   CHECK(Unserialize(initial_state)) << error();
+  DCHECK(!language_info_stats);
+  language_info_stats.reset(new AXLanguageInfoStats());
 }
 
 AXTree::~AXTree() {
@@ -1182,4 +1186,11 @@
   tree_update_in_progress_ = set_tree_update_value;
 }
 
+std::vector<LanguageSpan> AXTree::GetLanguageAnnotationForStringAttribute(
+    const AXNode& node,
+    ax::mojom::StringAttribute attr) const {
+  return language_info_stats->GetLanguageAnnotationForStringAttribute(node,
+                                                                      attr);
+}
+
 }  // namespace ui
diff --git a/ui/accessibility/ax_tree.h b/ui/accessibility/ax_tree.h
index afc6982..c64b36d 100644
--- a/ui/accessibility/ax_tree.h
+++ b/ui/accessibility/ax_tree.h
@@ -25,6 +25,7 @@
 class AXTreeObserver;
 struct AXTreeUpdateState;
 class AXLanguageInfoStats;
+struct LanguageSpan;
 
 // AXTree is a live, managed tree of AXNode objects that can receive
 // updates from another AXTreeSource via AXTreeUpdates, and it can be
@@ -150,6 +151,10 @@
   bool GetTreeUpdateInProgressState() const override;
   void SetTreeUpdateInProgressState(bool set_tree_update_value);
 
+  std::vector<LanguageSpan> GetLanguageAnnotationForStringAttribute(
+      const AXNode& node,
+      ax::mojom::StringAttribute attr) const override;
+
   // Language detection statistics
   std::unique_ptr<AXLanguageInfoStats> language_info_stats;
 
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.cc b/ui/accessibility/platform/ax_platform_node_auralinux.cc
index 586123f..2d6a776 100644
--- a/ui/accessibility/platform/ax_platform_node_auralinux.cc
+++ b/ui/accessibility/platform/ax_platform_node_auralinux.cc
@@ -2959,8 +2959,12 @@
 bool AXPlatformNodeAuraLinux::SelectionAndFocusAreTheSame() {
   if (AXPlatformNodeBase* container = GetSelectionContainer()) {
     ax::mojom::Role role = container->GetData().role;
+
+    // In the browser UI, menus and their descendants emit selection-related
+    // events only, but we also want to emit platform focus-related events,
+    // so we treat selection and focus the same for browser UI.
     if (role == ax::mojom::Role::kMenuBar || role == ax::mojom::Role::kMenu)
-      return true;
+      return !GetDelegate()->IsWebContent();
     if (role == ax::mojom::Role::kListBox &&
         !container->GetData().HasState(ax::mojom::State::kMultiselectable)) {
       return container->GetDelegate()->GetFocus() ==
diff --git a/ui/android/delegated_frame_host_android.cc b/ui/android/delegated_frame_host_android.cc
index 32c726f..99d6b7cf 100644
--- a/ui/android/delegated_frame_host_android.cc
+++ b/ui/android/delegated_frame_host_android.cc
@@ -387,8 +387,8 @@
 
 void DelegatedFrameHostAndroid::OnBeginFrame(
     const viz::BeginFrameArgs& args,
-    const viz::PresentationFeedbackMap& feedbacks) {
-  client_->DidPresentCompositorFrames(feedbacks);
+    const viz::FrameTimingDetailsMap& timing_details) {
+  client_->DidPresentCompositorFrames(timing_details);
   if (enable_viz_) {
     NOTREACHED();
     return;
diff --git a/ui/android/delegated_frame_host_android.h b/ui/android/delegated_frame_host_android.h
index 3aefe5f9..9de56e6d 100644
--- a/ui/android/delegated_frame_host_android.h
+++ b/ui/android/delegated_frame_host_android.h
@@ -10,7 +10,7 @@
 #include "cc/layers/deadline_policy.h"
 #include "components/viz/client/frame_evictor.h"
 #include "components/viz/common/frame_sinks/copy_output_request.h"
-#include "components/viz/common/presentation_feedback_map.h"
+#include "components/viz/common/frame_timing_details_map.h"
 #include "components/viz/common/resources/returned_resource.h"
 #include "components/viz/common/surfaces/surface_info.h"
 #include "components/viz/host/host_frame_sink_client.h"
@@ -46,7 +46,7 @@
     virtual void SetBeginFrameSource(
         viz::BeginFrameSource* begin_frame_source) = 0;
     virtual void DidPresentCompositorFrames(
-        const viz::PresentationFeedbackMap& feedbacks) = 0;
+        const viz::FrameTimingDetailsMap& timing_details) = 0;
     virtual void DidReceiveCompositorFrameAck(
         const std::vector<viz::ReturnedResource>& resources) = 0;
     virtual void ReclaimResources(
@@ -148,7 +148,7 @@
   void DidReceiveCompositorFrameAck(
       const std::vector<viz::ReturnedResource>& resources) override;
   void OnBeginFrame(const viz::BeginFrameArgs& args,
-                    const viz::PresentationFeedbackMap& feedbacks) override;
+                    const viz::FrameTimingDetailsMap& timing_details) override;
   void ReclaimResources(
       const std::vector<viz::ReturnedResource>& resources) override;
   void OnBeginFramePausedChanged(bool paused) override;
diff --git a/ui/android/delegated_frame_host_android_unittest.cc b/ui/android/delegated_frame_host_android_unittest.cc
index a9985d4..0b747d04 100644
--- a/ui/android/delegated_frame_host_android_unittest.cc
+++ b/ui/android/delegated_frame_host_android_unittest.cc
@@ -13,8 +13,8 @@
 #include "cc/layers/surface_layer.h"
 #include "cc/trees/layer_tree_host.h"
 #include "components/viz/common/features.h"
+#include "components/viz/common/frame_timing_details_map.h"
 #include "components/viz/common/hit_test/hit_test_region_list.h"
-#include "components/viz/common/presentation_feedback_map.h"
 #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
 #include "components/viz/host/host_frame_sink_manager.h"
 #include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
@@ -43,7 +43,7 @@
   MOCK_METHOD1(ReclaimResources,
                void(const std::vector<viz::ReturnedResource>&));
   MOCK_METHOD1(DidPresentCompositorFrames,
-               void(const viz::PresentationFeedbackMap&));
+               void(const viz::FrameTimingDetailsMap&));
   MOCK_METHOD1(OnFrameTokenChanged, void(uint32_t));
   MOCK_METHOD0(WasEvicted, void());
 };
diff --git a/ui/android/java/res/values-v17/styles.xml b/ui/android/java/res/values-v17/styles.xml
index 0bd0715..11c4ec2d 100644
--- a/ui/android/java/res/values-v17/styles.xml
+++ b/ui/android/java/res/values-v17/styles.xml
@@ -23,8 +23,8 @@
     </style>
 
     <style name="ButtonCompatBase">
-        <item name="android:minWidth">88dp</item>
-        <item name="android:minHeight">36dp</item>
+        <item name="android:minWidth">@dimen/button_min_width</item>
+        <item name="android:minHeight">@dimen/min_touch_target_size</item>
         <item name="android:paddingStart">20dp</item>
         <item name="android:paddingEnd">20dp</item>
         <item name="android:paddingTop">5dp</item>
@@ -32,6 +32,7 @@
         <item name="android:focusable">true</item>
         <item name="android:clickable">true</item>
         <item name="android:gravity">center_vertical|center_horizontal</item>
+        <item name="verticalInset">@dimen/button_bg_vertical_inset</item>
     </style>
     <style name="FilledButton" parent="ButtonCompatBase" tools:ignore="UnusedResources">
         <item name="android:paddingStart">24dp</item>
@@ -59,11 +60,12 @@
     </style>
 
     <style name="Chip">
-        <item name="android:minHeight">@dimen/chip_default_height</item>
+        <item name="android:minHeight">@dimen/min_touch_target_size</item>
         <item name="android:gravity">center_vertical</item>
         <item name="android:orientation">horizontal</item>
         <item name="chipColor">@color/chip_background_color</item>
         <item name="rippleColor">@color/chip_ripple_color</item>
+        <item name="verticalInset">@dimen/chip_bg_vertical_inset</item>
     </style>
     <style name="SuggestionChip" parent="Chip" tools:ignore="UnusedResources">
         <item name="primaryTextAppearance">@style/TextAppearance.ChipText</item>
diff --git a/ui/android/java/res/values/attrs.xml b/ui/android/java/res/values/attrs.xml
index f82f8f00d..538638b5 100644
--- a/ui/android/java/res/values/attrs.xml
+++ b/ui/android/java/res/values/attrs.xml
@@ -3,7 +3,10 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 <resources>
+    <!-- The color of the ripple effect on clickable elements. -->
     <attr name="rippleColor" format="color"/>
+    <!-- The top or bottom inset of a drawable. -->
+    <attr name="verticalInset" format="reference|dimension"/>
 
     <declare-styleable name="DualControlLayout">
         <attr name="stackedMargin" format="reference"/>
@@ -17,9 +20,13 @@
     </declare-styleable>
 
     <declare-styleable name="ButtonCompat">
+        <!-- The color of the button background. -->
         <attr name="buttonColor" format="color"/>
         <attr name="rippleColor"/>
+        <!-- Whether the button is elevated. -->
         <attr name="buttonRaised" format="boolean"/>
+        <!-- The vertical inset of the button background drawable. -->
+        <attr name="verticalInset"/>
     </declare-styleable>
 
     <declare-styleable name="ChipView">
@@ -31,6 +38,7 @@
         <attr name="primaryTextAppearance" format="reference"/>
         <attr name="secondaryTextAppearance" format="reference"/>
         <attr name="rippleColor"/>
+        <attr name="verticalInset"/>
     </declare-styleable>
 
     <declare-styleable name="TextViewWithLeading">
diff --git a/ui/android/java/res/values/dimens.xml b/ui/android/java/res/values/dimens.xml
index 03350355..7ba94dd 100644
--- a/ui/android/java/res/values/dimens.xml
+++ b/ui/android/java/res/values/dimens.xml
@@ -23,12 +23,20 @@
     <item name="default_focused_alpha" format="float" type="dimen">0.06</item>
     <item name="default_hovered_alpha" format="float" type="dimen">0.04</item>
 
+    <!-- Minimum height/width for a touchable item -->
+    <dimen name="min_touch_target_size">48dp</dimen>
+
+    <!-- Button default measures -->
+    <dimen name="button_min_width">88dp</dimen>
+    <dimen name="button_bg_vertical_inset">6dp</dimen>
+
     <!-- Chips default measures -->
     <dimen name="chip_border_width">1dp</dimen>
     <dimen name="chip_corner_radius">8dp</dimen>
     <dimen name="chip_default_height">32dp</dimen>
     <dimen name="chip_end_padding">16dp</dimen>
     <dimen name="chip_element_leading_padding">8dp</dimen>
+    <dimen name="chip_bg_vertical_inset">8dp</dimen>
     <item name="chip_background_selected_focused_alpha" format="float" type="dimen">0.12</item>
     <item name="chip_background_selected_alpha" format="float" type="dimen">0.06</item>
     <dimen name="chip_icon_size">20dp</dimen>
diff --git a/ui/android/java/src/org/chromium/ui/widget/ButtonCompat.java b/ui/android/java/src/org/chromium/ui/widget/ButtonCompat.java
index f83ca7c..970e088 100644
--- a/ui/android/java/src/org/chromium/ui/widget/ButtonCompat.java
+++ b/ui/android/java/src/org/chromium/ui/widget/ButtonCompat.java
@@ -35,6 +35,8 @@
  *
  * Note: To ensure the button's shadow is fully visible, you may need to set
  * android:clipToPadding="false" on the button's parent view.
+ *
+ * See {@link R.styleable#ButtonCompat ButtonCompat Attributes}.
  */
 public class ButtonCompat extends AppCompatButton {
     private RippleBackgroundHelper mRippleBackgroundHelper;
@@ -66,10 +68,13 @@
         int rippleColorId = a.getResourceId(
                 R.styleable.ButtonCompat_rippleColor, R.color.filled_button_ripple_color);
         boolean buttonRaised = a.getBoolean(R.styleable.ButtonCompat_buttonRaised, true);
+        int verticalInset = a.getDimensionPixelSize(R.styleable.ButtonCompat_verticalInset,
+                getResources().getDimensionPixelSize(R.dimen.button_bg_vertical_inset));
         a.recycle();
 
         mRippleBackgroundHelper = new RippleBackgroundHelper(this, buttonColorId, rippleColorId,
-                getResources().getDimensionPixelSize(R.dimen.button_compat_corner_radius));
+                getResources().getDimensionPixelSize(R.dimen.button_compat_corner_radius),
+                verticalInset);
         setRaised(buttonRaised);
     }
 
diff --git a/ui/android/java/src/org/chromium/ui/widget/ChipView.java b/ui/android/java/src/org/chromium/ui/widget/ChipView.java
index e01ae7bc..de55d50 100644
--- a/ui/android/java/src/org/chromium/ui/widget/ChipView.java
+++ b/ui/android/java/src/org/chromium/ui/widget/ChipView.java
@@ -69,6 +69,8 @@
                 R.styleable.ChipView_primaryTextAppearance, R.style.TextAppearance_ChipText);
         mSecondaryTextAppearanceId = a.getResourceId(
                 R.styleable.ChipView_secondaryTextAppearance, R.style.TextAppearance_ChipText);
+        int verticalInset = a.getDimensionPixelSize(R.styleable.ChipView_verticalInset,
+                getResources().getDimensionPixelSize(R.dimen.chip_bg_vertical_inset));
         a.recycle();
 
         mIcon = new ChromeImageView(getContext());
@@ -85,7 +87,7 @@
 
         // Reset icon and background:
         mRippleBackgroundHelper = new RippleBackgroundHelper(this, chipColorId, rippleColorId,
-                cornerRadius, R.color.chip_stroke_color, R.dimen.chip_border_width);
+                cornerRadius, R.color.chip_stroke_color, R.dimen.chip_border_width, verticalInset);
         setIcon(INVALID_ICON_ID, false);
     }
 
diff --git a/ui/android/java/src/org/chromium/ui/widget/RippleBackgroundHelper.java b/ui/android/java/src/org/chromium/ui/widget/RippleBackgroundHelper.java
index b83b955..b028425 100644
--- a/ui/android/java/src/org/chromium/ui/widget/RippleBackgroundHelper.java
+++ b/ui/android/java/src/org/chromium/ui/widget/RippleBackgroundHelper.java
@@ -9,6 +9,7 @@
 import android.graphics.Color;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.InsetDrawable;
 import android.graphics.drawable.LayerDrawable;
 import android.graphics.drawable.RippleDrawable;
 import android.os.Build;
@@ -54,11 +55,13 @@
      * @param backgroundColorResId The resource id of the background color.
      * @param rippleColorResId The resource id of the ripple color.
      * @param cornerRadius The corner radius in pixels of the background drawable.
+     * @param verticalInset The vertical inset of the background drawable.
      */
     RippleBackgroundHelper(View view, @ColorRes int backgroundColorResId,
-            @ColorRes int rippleColorResId, @Px int cornerRadius) {
+            @ColorRes int rippleColorResId, @Px int cornerRadius, @Px int verticalInset) {
         this(view, backgroundColorResId, rippleColorResId, cornerRadius,
-                android.R.color.transparent, R.dimen.default_ripple_background_border_size);
+                android.R.color.transparent, R.dimen.default_ripple_background_border_size,
+                verticalInset);
     }
 
     /**
@@ -68,12 +71,13 @@
      * @param cornerRadius The corner radius in pixels of the background drawable.
      * @param borderColorResId The resource id of the border color.
      * @param borderSizeDimenId The resource id of the border size.
+     * @param verticalInset The vertical inset of the background drawable.
      */
     // TODO(jdemeulenaere): Make this constructor package-private once it is not accessed by {@link
     // org.chromium.chrome.browser.autofill_assistant.carousel.ButtonView} anymore.
     public RippleBackgroundHelper(View view, @ColorRes int backgroundColorResId,
             @ColorRes int rippleColorResId, @Px int cornerRadius, @ColorRes int borderColorResId,
-            @DimenRes int borderSizeDimenId) {
+            @DimenRes int borderSizeDimenId, @Px int verticalInset) {
         mView = view;
 
         int paddingStart = ViewCompat.getPaddingStart(mView);
@@ -83,7 +87,8 @@
         mView.setBackground(createBackgroundDrawable(
                 AppCompatResources.getColorStateList(view.getContext(), rippleColorResId),
                 AppCompatResources.getColorStateList(view.getContext(), borderColorResId),
-                view.getResources().getDimensionPixelSize(borderSizeDimenId), cornerRadius));
+                view.getResources().getDimensionPixelSize(borderSizeDimenId), cornerRadius,
+                verticalInset));
         setBackgroundColor(
                 AppCompatResources.getColorStateList(view.getContext(), backgroundColorResId));
 
@@ -101,10 +106,12 @@
      * @param borderColorList A {@link ColorStateList} that is used for the border.
      * @param borderSize The border width in pixels.
      * @param cornerRadius The corner radius in pixels.
+     * @param verticalInset The vertical inset of the background drawable.
      * @return The {@link GradientDrawable}/{@link LayerDrawable} to be used as ripple background.
      */
     private Drawable createBackgroundDrawable(ColorStateList rippleColorList,
-            ColorStateList borderColorList, @Px int borderSize, @Px int cornerRadius) {
+            ColorStateList borderColorList, @Px int borderSize, @Px int cornerRadius,
+            @Px int verticalInset) {
         mBackgroundGradient = new GradientDrawable();
         mBackgroundGradient.setCornerRadius(cornerRadius);
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
@@ -112,8 +119,10 @@
             GradientDrawable mask = new GradientDrawable();
             mask.setCornerRadius(cornerRadius);
             mask.setColor(Color.WHITE);
-            return new RippleDrawable(
-                    convertToRippleDrawableColorList(rippleColorList), mBackgroundGradient, mask);
+            return wrapDrawableWithInsets(
+                    new RippleDrawable(convertToRippleDrawableColorList(rippleColorList),
+                            mBackgroundGradient, mask),
+                    verticalInset);
         }
 
         // Pre-L, create a background drawable and overlay it by a ripple drawable.
@@ -123,7 +132,9 @@
         mRippleDrawablePreL = DrawableCompat.wrap(rippleGradient);
         DrawableCompat.setTintList(mRippleDrawablePreL, rippleColorList);
         if (borderSize == 0) {
-            return new LayerDrawable(new Drawable[] {mBackgroundDrawablePreL, mRippleDrawablePreL});
+            return wrapDrawableWithInsets(new LayerDrawable(new Drawable[] {
+                                                  mBackgroundDrawablePreL, mRippleDrawablePreL}),
+                    verticalInset);
         }
 
         // If the background is overlaid by a border. The border is in a separate GradientDrawable
@@ -136,8 +147,19 @@
                         mView.getDrawableState(), borderColorList.getDefaultColor()));
         mBorderDrawablePreL = DrawableCompat.wrap(borderGradient);
         DrawableCompat.setTintList(mBorderDrawablePreL, borderColorList);
-        return new LayerDrawable(
-                new Drawable[] {mBackgroundDrawablePreL, mBorderDrawablePreL, mRippleDrawablePreL});
+        return wrapDrawableWithInsets(new LayerDrawable(new Drawable[] {mBackgroundDrawablePreL,
+                                              mBorderDrawablePreL, mRippleDrawablePreL}),
+                verticalInset);
+    }
+
+    /**
+     * @param drawable The {@link Drawable} that needs to be wrapped with insets.
+     * @param verticalInset The vertical inset for the specified drawable.
+     * @return A {@link Drawable} that wraps the specified drawable with the specified inset.
+     */
+    private static Drawable wrapDrawableWithInsets(Drawable drawable, @Px int verticalInset) {
+        if (verticalInset == 0) return drawable;
+        return new InsetDrawable(drawable, 0, verticalInset, 0, verticalInset);
     }
 
     /**
diff --git a/ui/base/dragdrop/os_exchange_data_provider_win.cc b/ui/base/dragdrop/os_exchange_data_provider_win.cc
index 291b9501..85a41ec 100644
--- a/ui/base/dragdrop/os_exchange_data_provider_win.cc
+++ b/ui/base/dragdrop/os_exchange_data_provider_win.cc
@@ -477,8 +477,8 @@
     if (storage_for_contents->pstm) {
       // A properly implemented IDataObject::GetData moves the stream pointer
       // to end.
-      const LARGE_INTEGER zero_displacement = {};
-      HRESULT hr = storage_for_contents->pstm->Seek(zero_displacement,
+      const LARGE_INTEGER kZeroDisplacement = {};
+      HRESULT hr = storage_for_contents->pstm->Seek(kZeroDisplacement,
                                                     STREAM_SEEK_END, nullptr);
       if (SUCCEEDED(hr))
         storage_for_contents->tymed = TYMED_ISTREAM;
@@ -1137,11 +1137,11 @@
     *validated = title;
     base::i18n::ReplaceIllegalCharactersInPath(validated, '-');
   }
-  static const wchar_t extension[] = L".url";
-  static const size_t max_length = MAX_PATH - base::size(extension);
-  if (validated->size() > max_length)
-    validated->erase(max_length);
-  *validated += extension;
+  static const wchar_t kExtension[] = L".url";
+  static const size_t kMaxLength = MAX_PATH - base::size(kExtension);
+  if (validated->size() > kMaxLength)
+    validated->erase(kMaxLength);
+  *validated += kExtension;
 }
 
 static STGMEDIUM* GetStorageForFileNames(
diff --git a/ui/base/dragdrop/os_exchange_data_win_unittest.cc b/ui/base/dragdrop/os_exchange_data_win_unittest.cc
index 3fbcc72..641bfff 100644
--- a/ui/base/dragdrop/os_exchange_data_win_unittest.cc
+++ b/ui/base/dragdrop/os_exchange_data_win_unittest.cc
@@ -343,10 +343,10 @@
 }
 
 TEST_F(OSExchangeDataWinTest, VirtualFiles) {
-  const base::FilePath path_placeholder(FILE_PATH_LITERAL("temp.tmp"));
+  const base::FilePath kPathPlaceholder(FILE_PATH_LITERAL("temp.tmp"));
 
   const std::vector<std::pair<base::FilePath, std::string>>
-      kTestFilenames_and_Contents = {
+      kTestFilenamesAndContents = {
           {base::FilePath(FILE_PATH_LITERAL("filename.txt")),
            std::string("just some data")},
           {base::FilePath(FILE_PATH_LITERAL("another filename.txt")),
@@ -357,17 +357,16 @@
 
   for (const auto& tymed : kStorageMediaTypesForVirtualFiles) {
     OSExchangeData data;
-    data.provider().SetVirtualFileContentsForTesting(
-        kTestFilenames_and_Contents, tymed);
+    data.provider().SetVirtualFileContentsForTesting(kTestFilenamesAndContents,
+                                                     tymed);
 
     OSExchangeData copy(data.provider().Clone());
     std::vector<FileInfo> file_infos;
     EXPECT_TRUE(copy.GetVirtualFilenames(&file_infos));
-    EXPECT_EQ(kTestFilenames_and_Contents.size(), file_infos.size());
+    EXPECT_EQ(kTestFilenamesAndContents.size(), file_infos.size());
     for (size_t i = 0; i < file_infos.size(); i++) {
-      EXPECT_EQ(kTestFilenames_and_Contents[i].first,
-                file_infos[i].display_name);
-      EXPECT_EQ(path_placeholder, file_infos[i].path);
+      EXPECT_EQ(kTestFilenamesAndContents[i].first, file_infos[i].display_name);
+      EXPECT_EQ(kPathPlaceholder, file_infos[i].path);
     }
 
     base::FilePath temp_dir;
@@ -384,10 +383,10 @@
     // RunUntilIdle assures all async tasks are run.
     scoped_task_environment_.RunUntilIdle();
 
-    EXPECT_EQ(kTestFilenames_and_Contents.size(),
+    EXPECT_EQ(kTestFilenamesAndContents.size(),
               retrieved_virtual_files_.size());
     for (size_t i = 0; i < retrieved_virtual_files_.size(); i++) {
-      EXPECT_EQ(kTestFilenames_and_Contents[i].first,
+      EXPECT_EQ(kTestFilenamesAndContents[i].first,
                 retrieved_virtual_files_[i].display_name);
       // Check if the temp files that back the virtual files are actually
       // created in the temp directory. Need to compare long file paths here
@@ -396,17 +395,17 @@
       EXPECT_EQ(
           base::MakeLongFilePath(temp_dir),
           base::MakeLongFilePath(retrieved_virtual_files_[i].path.DirName()));
-      EXPECT_EQ(kTestFilenames_and_Contents[i].first.Extension(),
+      EXPECT_EQ(kTestFilenamesAndContents[i].first.Extension(),
                 retrieved_virtual_files_[i].path.Extension());
       std::string read_contents;
       EXPECT_TRUE(base::ReadFileToString(retrieved_virtual_files_[i].path,
                                          &read_contents));
       if (tymed != TYMED_ISTORAGE) {
-        EXPECT_EQ(kTestFilenames_and_Contents[i].second, read_contents);
+        EXPECT_EQ(kTestFilenamesAndContents[i].second, read_contents);
       } else {
         // IStorage uses compound files, so temp files won't be flat text files.
         // Just make sure the original contents appears in the compound files.
-        EXPECT_TRUE(read_contents.find(kTestFilenames_and_Contents[i].second) !=
+        EXPECT_TRUE(read_contents.find(kTestFilenamesAndContents[i].second) !=
                     std::string::npos);
       }
     }
@@ -423,7 +422,7 @@
   };
 
   const std::vector<std::pair<base::FilePath, std::string>>
-      kTestFilenames_and_Contents = {
+      kTestFilenamesAndContents = {
           {base::FilePath(FILE_PATH_LITERAL("filename.txt")),
            std::string("just some data")},
           {base::FilePath(FILE_PATH_LITERAL("another filename.txt")),
@@ -435,8 +434,8 @@
   for (const auto& tymed : kStorageMediaTypesForVirtualFiles) {
     OSExchangeData data;
     data.SetFilenames(kTestFilenames);
-    data.provider().SetVirtualFileContentsForTesting(
-        kTestFilenames_and_Contents, tymed);
+    data.provider().SetVirtualFileContentsForTesting(kTestFilenamesAndContents,
+                                                     tymed);
 
     OSExchangeData copy(data.provider().Clone());
 
@@ -466,7 +465,7 @@
 
 TEST_F(OSExchangeDataWinTest, VirtualFilesDuplicateNames) {
   const std::vector<std::pair<base::FilePath, std::string>>
-      kTestFilenames_and_Contents = {
+      kTestFilenamesAndContents = {
           {base::FilePath(FILE_PATH_LITERAL("A (1) (2).txt")),
            std::string("just some data")},
           {base::FilePath(FILE_PATH_LITERAL("A.txt")),
@@ -479,13 +478,13 @@
 
   for (const auto& tymed : kStorageMediaTypesForVirtualFiles) {
     OSExchangeData data;
-    data.provider().SetVirtualFileContentsForTesting(
-        kTestFilenames_and_Contents, tymed);
+    data.provider().SetVirtualFileContentsForTesting(kTestFilenamesAndContents,
+                                                     tymed);
 
     OSExchangeData copy(data.provider().Clone());
     std::vector<FileInfo> file_infos;
     EXPECT_TRUE(copy.GetVirtualFilenames(&file_infos));
-    EXPECT_EQ(kTestFilenames_and_Contents.size(), file_infos.size());
+    EXPECT_EQ(kTestFilenamesAndContents.size(), file_infos.size());
     for (size_t i = 0; i < file_infos.size(); i++) {
       // Check that display name is unique.
       for (size_t j = 0; j < i; j++) {
@@ -509,7 +508,7 @@
     // RunUntilIdle assures all async tasks are run.
     scoped_task_environment_.RunUntilIdle();
 
-    EXPECT_EQ(kTestFilenames_and_Contents.size(), file_infos.size());
+    EXPECT_EQ(kTestFilenamesAndContents.size(), file_infos.size());
     for (size_t i = 0; i < retrieved_virtual_files_.size(); i++) {
       // Check that display name is unique.
       for (size_t j = 0; j < i; j++) {
@@ -532,17 +531,17 @@
       EXPECT_EQ(
           base::MakeLongFilePath(temp_dir),
           base::MakeLongFilePath(retrieved_virtual_files_[i].path.DirName()));
-      EXPECT_EQ(kTestFilenames_and_Contents[i].first.Extension(),
+      EXPECT_EQ(kTestFilenamesAndContents[i].first.Extension(),
                 retrieved_virtual_files_[i].path.Extension());
       std::string read_contents;
       EXPECT_TRUE(base::ReadFileToString(retrieved_virtual_files_[i].path,
                                          &read_contents));
       if (tymed != TYMED_ISTORAGE) {
-        EXPECT_EQ(kTestFilenames_and_Contents[i].second, read_contents);
+        EXPECT_EQ(kTestFilenamesAndContents[i].second, read_contents);
       } else {
         // IStorage uses compound files, so temp files won't be flat text files.
         // Just make sure the original contents appears in the compound files.
-        EXPECT_TRUE(read_contents.find(kTestFilenames_and_Contents[i].second) !=
+        EXPECT_TRUE(read_contents.find(kTestFilenamesAndContents[i].second) !=
                     std::string::npos);
       }
     }
@@ -551,7 +550,7 @@
 
 TEST_F(OSExchangeDataWinTest, VirtualFilesDuplicateNamesCaseInsensitivity) {
   const std::vector<std::pair<base::FilePath, std::string>>
-      kTestFilenames_and_Contents = {
+      kTestFilenamesAndContents = {
           {base::FilePath(FILE_PATH_LITERAL("a.txt")),
            std::string("just some data")},
           {base::FilePath(FILE_PATH_LITERAL("B.txt")),
@@ -562,13 +561,13 @@
 
   for (const auto& tymed : kStorageMediaTypesForVirtualFiles) {
     OSExchangeData data;
-    data.provider().SetVirtualFileContentsForTesting(
-        kTestFilenames_and_Contents, tymed);
+    data.provider().SetVirtualFileContentsForTesting(kTestFilenamesAndContents,
+                                                     tymed);
 
     OSExchangeData copy(data.provider().Clone());
     std::vector<FileInfo> file_infos;
     EXPECT_TRUE(copy.GetVirtualFilenames(&file_infos));
-    EXPECT_EQ(kTestFilenames_and_Contents.size(), file_infos.size());
+    EXPECT_EQ(kTestFilenamesAndContents.size(), file_infos.size());
     for (size_t i = 0; i < file_infos.size(); i++) {
       // Check that display name is unique.
       for (size_t j = 0; j < i; j++) {
@@ -592,7 +591,7 @@
     // RunUntilIdle assures all async tasks are run.
     scoped_task_environment_.RunUntilIdle();
 
-    EXPECT_EQ(kTestFilenames_and_Contents.size(), file_infos.size());
+    EXPECT_EQ(kTestFilenamesAndContents.size(), file_infos.size());
     for (size_t i = 0; i < retrieved_virtual_files_.size(); i++) {
       // Check that display name is unique.
       for (size_t j = 0; j < i; j++) {
@@ -615,17 +614,17 @@
       EXPECT_EQ(
           base::MakeLongFilePath(temp_dir),
           base::MakeLongFilePath(retrieved_virtual_files_[i].path.DirName()));
-      EXPECT_EQ(kTestFilenames_and_Contents[i].first.Extension(),
+      EXPECT_EQ(kTestFilenamesAndContents[i].first.Extension(),
                 retrieved_virtual_files_[i].path.Extension());
       std::string read_contents;
       EXPECT_TRUE(base::ReadFileToString(retrieved_virtual_files_[i].path,
                                          &read_contents));
       if (tymed != TYMED_ISTORAGE) {
-        EXPECT_EQ(kTestFilenames_and_Contents[i].second, read_contents);
+        EXPECT_EQ(kTestFilenamesAndContents[i].second, read_contents);
       } else {
         // IStorage uses compound files, so temp files won't be flat text files.
         // Just make sure the original contents appears in the compound files.
-        EXPECT_TRUE(read_contents.find(kTestFilenames_and_Contents[i].second) !=
+        EXPECT_TRUE(read_contents.find(kTestFilenamesAndContents[i].second) !=
                     std::string::npos);
       }
     }
@@ -637,45 +636,45 @@
       FILE_PATH_LITERAL("\\/:*?\"<>|"));
   const base::string16 kInvalidFilePathCharacters(
       FILE_PATH_LITERAL("/*?\"<>|"));
-  const base::FilePath pathWithInvalidFileNameCharacters =
+  const base::FilePath kPathWithInvalidFileNameCharacters =
       base::FilePath(kInvalidFileNameCharacters)
           .AddExtension(FILE_PATH_LITERAL("txt"));
-  const base::FilePath empty_display_name(FILE_PATH_LITERAL(""));
-  const base::FilePath maxpath_display_name =
+  const base::FilePath kEmptyDisplayName(FILE_PATH_LITERAL(""));
+  const base::FilePath kMaxPathDisplayName =
       base::FilePath(base::string16(MAX_PATH - 5, L'a'))
           .AddExtension(FILE_PATH_LITERAL("txt"));
 
   const std::vector<std::pair<base::FilePath, std::string>>
-      kTestFilenames_and_Contents = {
-          {pathWithInvalidFileNameCharacters, std::string("just some data")},
-          {pathWithInvalidFileNameCharacters,
+      kTestFilenamesAndContents = {
+          {kPathWithInvalidFileNameCharacters, std::string("just some data")},
+          {kPathWithInvalidFileNameCharacters,
            std::string("just some data\0with\0nulls", 25)},
           {// Test that still get a unique name if a previous uniquified
            // name is a duplicate of this one.
-           pathWithInvalidFileNameCharacters.InsertBeforeExtension(
+           kPathWithInvalidFileNameCharacters.InsertBeforeExtension(
                FILE_PATH_LITERAL(" (1)")),
            std::string("just some more data")},
           // Expect a default display name to be generated ("download" if it
           // matters).
-          {empty_display_name, std::string("data for an empty display name")},
-          {empty_display_name,
+          {kEmptyDisplayName, std::string("data for an empty display name")},
+          {kEmptyDisplayName,
            std::string("data for another empty display name")},
           // Expect good behavior if the display name length exceeds MAX_PATH.
-          {maxpath_display_name,
+          {kMaxPathDisplayName,
            std::string("data for a >MAX_PATH display name")},
-          {maxpath_display_name,
+          {kMaxPathDisplayName,
            std::string("data for another >MAX_PATH display name")},
       };
 
   for (const auto& tymed : kStorageMediaTypesForVirtualFiles) {
     OSExchangeData data;
-    data.provider().SetVirtualFileContentsForTesting(
-        kTestFilenames_and_Contents, tymed);
+    data.provider().SetVirtualFileContentsForTesting(kTestFilenamesAndContents,
+                                                     tymed);
 
     OSExchangeData copy(data.provider().Clone());
     std::vector<FileInfo> file_infos;
     EXPECT_TRUE(copy.GetVirtualFilenames(&file_infos));
-    EXPECT_EQ(kTestFilenames_and_Contents.size(), file_infos.size());
+    EXPECT_EQ(kTestFilenamesAndContents.size(), file_infos.size());
     for (size_t i = 0; i < file_infos.size(); i++) {
       // Check that display name does not contain invalid characters.
       EXPECT_EQ(std::string::npos,
@@ -703,7 +702,7 @@
     // RunUntilIdle assures all async tasks are run.
     scoped_task_environment_.RunUntilIdle();
 
-    EXPECT_EQ(kTestFilenames_and_Contents.size(), file_infos.size());
+    EXPECT_EQ(kTestFilenamesAndContents.size(), file_infos.size());
     for (size_t i = 0; i < retrieved_virtual_files_.size(); i++) {
       // Check that display name does not contain invalid characters.
       EXPECT_EQ(std::string::npos,
@@ -734,7 +733,7 @@
       EXPECT_EQ(
           base::MakeLongFilePath(temp_dir),
           base::MakeLongFilePath(retrieved_virtual_files_[i].path.DirName()));
-      EXPECT_EQ(kTestFilenames_and_Contents[i].first.Extension(),
+      EXPECT_EQ(kTestFilenamesAndContents[i].first.Extension(),
                 retrieved_virtual_files_[i].path.Extension());
       std::string read_contents;
       // Ability to read the contents implies a temp file was successfully
@@ -743,11 +742,11 @@
       EXPECT_TRUE(base::ReadFileToString(retrieved_virtual_files_[i].path,
                                          &read_contents));
       if (tymed != TYMED_ISTORAGE) {
-        EXPECT_EQ(kTestFilenames_and_Contents[i].second, read_contents);
+        EXPECT_EQ(kTestFilenamesAndContents[i].second, read_contents);
       } else {
         // IStorage uses compound files, so temp files won't be flat text files.
         // Just make sure the original contents appears in the compound files.
-        EXPECT_TRUE(read_contents.find(kTestFilenames_and_Contents[i].second) !=
+        EXPECT_TRUE(read_contents.find(kTestFilenamesAndContents[i].second) !=
                     std::string::npos);
       }
     }
@@ -756,23 +755,22 @@
 
 TEST_F(OSExchangeDataWinTest, VirtualFilesEmptyContents) {
   const std::vector<std::pair<base::FilePath, std::string>>
-      kTestFilenames_and_Contents = {
+      kTestFilenamesAndContents = {
           {base::FilePath(FILE_PATH_LITERAL("file_with_no_contents.txt")),
            std::string()},
       };
 
   for (const auto& tymed : kStorageMediaTypesForVirtualFiles) {
     OSExchangeData data;
-    data.provider().SetVirtualFileContentsForTesting(
-        kTestFilenames_and_Contents, tymed);
+    data.provider().SetVirtualFileContentsForTesting(kTestFilenamesAndContents,
+                                                     tymed);
 
     OSExchangeData copy(data.provider().Clone());
     std::vector<FileInfo> file_infos;
     EXPECT_TRUE(copy.GetVirtualFilenames(&file_infos));
-    EXPECT_EQ(kTestFilenames_and_Contents.size(), file_infos.size());
+    EXPECT_EQ(kTestFilenamesAndContents.size(), file_infos.size());
     for (size_t i = 0; i < file_infos.size(); i++) {
-      EXPECT_EQ(kTestFilenames_and_Contents[i].first,
-                file_infos[i].display_name);
+      EXPECT_EQ(kTestFilenamesAndContents[i].first, file_infos[i].display_name);
     }
 
     base::FilePath temp_dir;
@@ -789,10 +787,10 @@
     // RunUntilIdle assures all async tasks are run.
     scoped_task_environment_.RunUntilIdle();
 
-    EXPECT_EQ(kTestFilenames_and_Contents.size(),
+    EXPECT_EQ(kTestFilenamesAndContents.size(),
               retrieved_virtual_files_.size());
     for (size_t i = 0; i < retrieved_virtual_files_.size(); i++) {
-      EXPECT_EQ(kTestFilenames_and_Contents[i].first,
+      EXPECT_EQ(kTestFilenamesAndContents[i].first,
                 retrieved_virtual_files_[i].display_name);
 
       // Check if the temp files that back the virtual files are actually
@@ -802,7 +800,7 @@
       EXPECT_EQ(
           base::MakeLongFilePath(temp_dir),
           base::MakeLongFilePath(retrieved_virtual_files_[i].path.DirName()));
-      EXPECT_EQ(kTestFilenames_and_Contents[i].first.Extension(),
+      EXPECT_EQ(kTestFilenamesAndContents[i].first.Extension(),
                 retrieved_virtual_files_[i].path.Extension());
       std::string read_contents;
       EXPECT_TRUE(base::ReadFileToString(retrieved_virtual_files_[i].path,
@@ -810,7 +808,7 @@
       // IStorage uses compound files, so temp files won't be flat text files.
       // Just make sure the original contents appear in the compound files.
       if (tymed != TYMED_ISTORAGE) {
-        EXPECT_EQ(kTestFilenames_and_Contents[i].second, read_contents);
+        EXPECT_EQ(kTestFilenamesAndContents[i].second, read_contents);
         EXPECT_EQ(static_cast<size_t>(0), read_contents.length());
       }
     }
diff --git a/ui/base/ime/chromeos/BUILD.gn b/ui/base/ime/chromeos/BUILD.gn
index bdc721c..3675b42 100644
--- a/ui/base/ime/chromeos/BUILD.gn
+++ b/ui/base/ime/chromeos/BUILD.gn
@@ -19,9 +19,8 @@
     "fake_input_method_delegate.cc",
     "fake_input_method_delegate.h",
     "ime_keyboard.cc",
-    "ime_keyboard.h",
-    "ime_keyboard_mus.cc",
-    "ime_keyboard_mus.h",
+    "ime_keyboard_impl.cc",
+    "ime_keyboard_impl.h",
     "ime_keymap.cc",
     "ime_keymap.h",
     "input_method_chromeos.cc",
@@ -55,10 +54,9 @@
     "//chromeos/constants",
     "//chromeos/ime:gencode",
     "//chromeos/system",
-    "//services/ws/public/cpp/input_devices",
     "//third_party/icu",
     "//ui/base",
-    "//ui/base/ime/mojo",
     "//ui/chromeos/strings",
+    "//ui/ozone",
   ]
 }
diff --git a/ui/base/ime/chromeos/DEPS b/ui/base/ime/chromeos/DEPS
index 675a9ae..e2f2bda 100644
--- a/ui/base/ime/chromeos/DEPS
+++ b/ui/base/ime/chromeos/DEPS
@@ -3,4 +3,5 @@
   "+chromeos/ime",
   "+services/ws/public/cpp/input_devices",
   "+ui/chromeos/strings",
+  "+ui/ozone/public",
 ]
diff --git a/ui/base/ime/chromeos/ime_keyboard_impl.cc b/ui/base/ime/chromeos/ime_keyboard_impl.cc
new file mode 100644
index 0000000..5a35b01
--- /dev/null
+++ b/ui/base/ime/chromeos/ime_keyboard_impl.cc
@@ -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.
+
+#include "ui/base/ime/chromeos/ime_keyboard_impl.h"
+
+#include "ui/ozone/public/input_controller.h"
+#include "ui/ozone/public/ozone_platform.h"
+
+namespace chromeos {
+namespace input_method {
+
+ImeKeyboardImpl::ImeKeyboardImpl()
+    : input_controller_(
+          ui::OzonePlatform::GetInstance()->GetInputController()) {}
+
+ImeKeyboardImpl::~ImeKeyboardImpl() = default;
+
+bool ImeKeyboardImpl::SetCurrentKeyboardLayoutByName(
+    const std::string& layout_name) {
+  ImeKeyboard::SetCurrentKeyboardLayoutByName(layout_name);
+  last_layout_ = layout_name;
+  input_controller_->SetCurrentLayoutByName(layout_name);
+  return true;
+}
+
+bool ImeKeyboardImpl::SetAutoRepeatRate(const AutoRepeatRate& rate) {
+  input_controller_->SetAutoRepeatRate(
+      base::TimeDelta::FromMilliseconds(rate.initial_delay_in_ms),
+      base::TimeDelta::FromMilliseconds(rate.repeat_interval_in_ms));
+  return true;
+}
+
+bool ImeKeyboardImpl::SetAutoRepeatEnabled(bool enabled) {
+  input_controller_->SetAutoRepeatEnabled(enabled);
+  return true;
+}
+
+bool ImeKeyboardImpl::GetAutoRepeatEnabled() {
+  return input_controller_->IsAutoRepeatEnabled();
+}
+
+bool ImeKeyboardImpl::ReapplyCurrentKeyboardLayout() {
+  return SetCurrentKeyboardLayoutByName(last_layout_);
+}
+
+void ImeKeyboardImpl::ReapplyCurrentModifierLockStatus() {}
+
+void ImeKeyboardImpl::DisableNumLock() {
+  input_controller_->SetNumLockEnabled(false);
+}
+
+void ImeKeyboardImpl::SetCapsLockEnabled(bool enable_caps_lock) {
+  // Inform ImeKeyboard of caps lock state.
+  ImeKeyboard::SetCapsLockEnabled(enable_caps_lock);
+  // Inform Ozone InputController input of caps lock state.
+  input_controller_->SetCapsLockEnabled(enable_caps_lock);
+}
+
+bool ImeKeyboardImpl::CapsLockIsEnabled() {
+  return input_controller_->IsCapsLockEnabled();
+}
+
+}  // namespace input_method
+}  // namespace chromeos
diff --git a/ui/base/ime/chromeos/ime_keyboard_mus.h b/ui/base/ime/chromeos/ime_keyboard_impl.h
similarity index 60%
rename from ui/base/ime/chromeos/ime_keyboard_mus.h
rename to ui/base/ime/chromeos/ime_keyboard_impl.h
index fe3e03d2..a06dcef 100644
--- a/ui/base/ime/chromeos/ime_keyboard_mus.h
+++ b/ui/base/ime/chromeos/ime_keyboard_impl.h
@@ -1,27 +1,27 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef UI_BASE_IME_CHROMEOS_IME_KEYBOARD_MUS_H_
-#define UI_BASE_IME_CHROMEOS_IME_KEYBOARD_MUS_H_
+#ifndef UI_BASE_IME_CHROMEOS_IME_KEYBOARD_IMPL_H_
+#define UI_BASE_IME_CHROMEOS_IME_KEYBOARD_IMPL_H_
 
 #include "base/component_export.h"
 #include "base/macros.h"
 #include "ui/base/ime/chromeos/ime_keyboard.h"
 
-namespace ws {
-class InputDeviceControllerClient;
+namespace ui {
+class InputController;
 }
 
 namespace chromeos {
 namespace input_method {
 
-class COMPONENT_EXPORT(UI_BASE_IME_CHROMEOS) ImeKeyboardMus
+// Version of ImeKeyboard used when chrome is run on device.
+class COMPONENT_EXPORT(UI_BASE_IME_CHROMEOS) ImeKeyboardImpl
     : public ImeKeyboard {
  public:
-  explicit ImeKeyboardMus(
-      ws::InputDeviceControllerClient* input_device_controller_client);
-  ~ImeKeyboardMus() override;
+  ImeKeyboardImpl();
+  ~ImeKeyboardImpl() override;
 
   // ImeKeyboard:
   bool SetCurrentKeyboardLayoutByName(const std::string& layout_name) override;
@@ -35,12 +35,12 @@
   bool CapsLockIsEnabled() override;
 
  private:
-  ws::InputDeviceControllerClient* input_device_controller_client_;
+  ui::InputController* input_controller_;
 
-  DISALLOW_COPY_AND_ASSIGN(ImeKeyboardMus);
+  DISALLOW_COPY_AND_ASSIGN(ImeKeyboardImpl);
 };
 
 }  // namespace input_method
 }  // namespace chromeos
 
-#endif  // UI_BASE_IME_CHROMEOS_IME_KEYBOARD_MUS_H_
+#endif  // UI_BASE_IME_CHROMEOS_IME_KEYBOARD_IMPL_H_
diff --git a/ui/base/ime/chromeos/ime_keyboard_mus.cc b/ui/base/ime/chromeos/ime_keyboard_mus.cc
deleted file mode 100644
index 8d0f349..0000000
--- a/ui/base/ime/chromeos/ime_keyboard_mus.cc
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/base/ime/chromeos/ime_keyboard_mus.h"
-
-#include "services/ws/public/cpp/input_devices/input_device_controller_client.h"
-
-namespace chromeos {
-namespace input_method {
-
-ImeKeyboardMus::ImeKeyboardMus(
-    ws::InputDeviceControllerClient* input_device_controller_client)
-    : input_device_controller_client_(input_device_controller_client) {
-  ImeKeyboard::SetCapsLockEnabled(CapsLockIsEnabled());
-}
-
-ImeKeyboardMus::~ImeKeyboardMus() = default;
-
-bool ImeKeyboardMus::SetCurrentKeyboardLayoutByName(
-    const std::string& layout_name) {
-  ImeKeyboard::SetCurrentKeyboardLayoutByName(layout_name);
-  last_layout_ = layout_name;
-  input_device_controller_client_->SetKeyboardLayoutByName(layout_name);
-  return true;
-}
-
-bool ImeKeyboardMus::SetAutoRepeatRate(const AutoRepeatRate& rate) {
-  input_device_controller_client_->SetAutoRepeatRate(
-      base::TimeDelta::FromMilliseconds(rate.initial_delay_in_ms),
-      base::TimeDelta::FromMilliseconds(rate.repeat_interval_in_ms));
-  return true;
-}
-
-bool ImeKeyboardMus::SetAutoRepeatEnabled(bool enabled) {
-  input_device_controller_client_->SetAutoRepeatEnabled(enabled);
-  return true;
-}
-
-bool ImeKeyboardMus::GetAutoRepeatEnabled() {
-  return input_device_controller_client_->IsAutoRepeatEnabled();
-}
-
-bool ImeKeyboardMus::ReapplyCurrentKeyboardLayout() {
-  return SetCurrentKeyboardLayoutByName(last_layout_);
-}
-
-void ImeKeyboardMus::ReapplyCurrentModifierLockStatus() {}
-
-void ImeKeyboardMus::DisableNumLock() {
-  input_device_controller_client_->SetNumLockEnabled(false);
-}
-
-void ImeKeyboardMus::SetCapsLockEnabled(bool enable_caps_lock) {
-  // Inform ImeKeyboard of caps lock state.
-  ImeKeyboard::SetCapsLockEnabled(enable_caps_lock);
-  // Inform Ozone InputController input of caps lock state.
-  input_device_controller_client_->SetCapsLockEnabled(enable_caps_lock);
-}
-
-bool ImeKeyboardMus::CapsLockIsEnabled() {
-  return input_device_controller_client_->IsCapsLockEnabled();
-}
-
-}  // namespace input_method
-}  // namespace chromeos
diff --git a/ui/file_manager/gallery/css/gallery.css b/ui/file_manager/gallery/css/gallery.css
index 2710c4e..1e0e03c 100644
--- a/ui/file_manager/gallery/css/gallery.css
+++ b/ui/file_manager/gallery/css/gallery.css
@@ -546,7 +546,7 @@
   justify-content: flex-end;
 }
 
-.gallery .edit-mode-toolbar .exit-button-spacer paper-button {
+.gallery .edit-mode-toolbar .exit-button-spacer cr-button {
   color: white;
   font-weight: bold;
   margin-inline-end: 8px;
@@ -617,7 +617,7 @@
   visibility: visible;
 }
 
-.gallery > .toolbar paper-button,
+.gallery > .toolbar cr-button,
 .gallery > .toolbar button {
   border-radius: 2px;
   margin: 0 8px;
@@ -638,7 +638,7 @@
   position: relative;
 }
 
-.gallery > .toolbar paper-button:focus,
+.gallery > .toolbar cr-button:focus,
 .gallery > .toolbar button:focus {
   background-color: rgba(255, 255, 255, 0.2);
 }
@@ -717,7 +717,7 @@
 
 .gallery[mode='slide'] .icon.slide-mode,
 .gallery[mode='thumbnail'] .icon.thumbnail-mode,
-paper-button[disabled],
+cr-button[disabled],
 button[disabled] {
   display: none;
 }
diff --git a/ui/file_manager/gallery/gallery.html b/ui/file_manager/gallery/gallery.html
index 5c422aa4..bb38438 100644
--- a/ui/file_manager/gallery/gallery.html
+++ b/ui/file_manager/gallery/gallery.html
@@ -16,11 +16,11 @@
 
   <script src="chrome://resources/js/util.js"></script>
 
+  <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_slider/cr_slider.html">
   <link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html">
   <link rel="import" href="chrome://resources/cr_elements/shared_vars_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/paper-progress/paper-progress.html">
   <link rel="import" href="chrome://resources/polymer/v1_0/paper-ripple/paper-ripple.html">
   <link rel="import" href="chrome-extension://hhaomjibdihmijegdhdafkllkbggdgoj/foreground/elements/files_ripple.html">
@@ -56,8 +56,10 @@
         --paper-progress-container-color: rgba(0, 0, 0, 0.3);
         --paper-progress-active-color: #1AC222;
       }
-      paper-button {
-        --paper-button-ink-color: black;
+      cr-button {
+        --ink-color: black;
+        border: none;
+        box-shadow: none;
       }
       files-toggle-ripple {
         --files-toggle-ripple-activated: {
@@ -167,7 +169,7 @@
         </div>
         <div class="edit-bar-spacer"></div>
         <div class="exit-button-spacer">
-          <paper-button class="exit" i18n-content="GALLERY_EXIT"></paper-button>
+          <cr-button class="exit" i18n-content="GALLERY_EXIT"></cr-button>
         </div>
       </div>
       <div class="bubble" hidden>
diff --git a/ui/file_manager/gallery/js/image_editor/image_editor.js b/ui/file_manager/gallery/js/image_editor/image_editor.js
index d4891f3..ba9bd118 100644
--- a/ui/file_manager/gallery/js/image_editor/image_editor.js
+++ b/ui/file_manager/gallery/js/image_editor/image_editor.js
@@ -115,7 +115,7 @@
      * @const
      */
     this.exitButton_ = /** @type {!HTMLElement} */
-        (queryRequiredElement('.edit-mode-toolbar paper-button.exit'));
+        (queryRequiredElement('.edit-mode-toolbar cr-button.exit'));
     this.exitButton_.addEventListener('click', this.onExitClicked_.bind(this));
 
     /**
diff --git a/ui/file_manager/gallery/js/image_editor/image_editor_toolbar.js b/ui/file_manager/gallery/js/image_editor/image_editor_toolbar.js
index 3a802ec..f30d8e1 100644
--- a/ui/file_manager/gallery/js/image_editor/image_editor_toolbar.js
+++ b/ui/file_manager/gallery/js/image_editor/image_editor_toolbar.js
@@ -338,7 +338,13 @@
       var input = this.container_.querySelector(
           // Crop aspect ratio buttons should not be focused immediately
           // crbug.com/655943
-          'button:not(.crop-aspect-ratio), paper-button, input, cr-slider, cr-input');
+          [
+            'button:not(.crop-aspect-ratio)',
+            'cr-button',
+            'input',
+            'cr-slider',
+            'cr-input',
+          ].join(', '));
       if (input) {
         input.focus();
         // Fix for crbug/914741 set selection to the end (> 32-bit int)
diff --git a/ui/gl/gl_surface.cc b/ui/gl/gl_surface.cc
index ef44af82..2e8417d 100644
--- a/ui/gl/gl_surface.cc
+++ b/ui/gl/gl_surface.cc
@@ -218,6 +218,12 @@
   // It is fine to ignore this call in those cases.
 }
 
+void GLSurface::SetForceGlFlushOnSwapBuffers() {
+  // Some GLSurface derived classes might not implement this workaround while
+  // still being allocated on devices where the workaround is enabled.
+  // It is fine to ignore this call in those cases.
+}
+
 bool GLSurface::SupportsSwapTimestamps() const {
   return false;
 }
@@ -478,6 +484,10 @@
   surface_->SetRelyOnImplicitSync();
 }
 
+void GLSurfaceAdapter::SetForceGlFlushOnSwapBuffers() {
+  surface_->SetForceGlFlushOnSwapBuffers();
+}
+
 bool GLSurfaceAdapter::SupportsSwapTimestamps() const {
   return surface_->SupportsSwapTimestamps();
 }
diff --git a/ui/gl/gl_surface.h b/ui/gl/gl_surface.h
index 5a8d34b..77f4f53 100644
--- a/ui/gl/gl_surface.h
+++ b/ui/gl/gl_surface.h
@@ -278,6 +278,9 @@
   // Tells the surface to rely on implicit sync when swapping buffers.
   virtual void SetRelyOnImplicitSync();
 
+  // Tells the surface to perform a glFlush() before swapping buffers.
+  virtual void SetForceGlFlushOnSwapBuffers();
+
   // Support for eglGetFrameTimestamps.
   virtual bool SupportsSwapTimestamps() const;
   virtual void SetEnableSwapTimestamps();
@@ -385,6 +388,7 @@
   bool SetDrawRectangle(const gfx::Rect& rect) override;
   gfx::Vector2d GetDrawOffset() const override;
   void SetRelyOnImplicitSync() override;
+  void SetForceGlFlushOnSwapBuffers() override;
   bool SupportsSwapTimestamps() const override;
   void SetEnableSwapTimestamps() override;
   bool SupportsPlaneGpuFences() const override;
diff --git a/ui/message_center/public/cpp/notification.cc b/ui/message_center/public/cpp/notification.cc
index 1f58c7f..e124346 100644
--- a/ui/message_center/public/cpp/notification.cc
+++ b/ui/message_center/public/cpp/notification.cc
@@ -84,6 +84,12 @@
       serial_number_(g_next_serial_number++),
       delegate_(std::move(delegate)) {}
 
+Notification::Notification(scoped_refptr<NotificationDelegate> delegate,
+                           const Notification& other)
+    : Notification(other) {
+  delegate_ = delegate;
+}
+
 Notification::Notification(const std::string& id, const Notification& other)
     : Notification(other) {
   id_ = id;
diff --git a/ui/message_center/public/cpp/notification.h b/ui/message_center/public/cpp/notification.h
index f4afa82..db9cf82 100644
--- a/ui/message_center/public/cpp/notification.h
+++ b/ui/message_center/public/cpp/notification.h
@@ -224,6 +224,11 @@
   // will be replaced by the given value.
   Notification(const std::string& id, const Notification& other);
 
+  // Creates a copy of the |other| notification. The delegate will be replaced
+  // by |delegate|.
+  Notification(scoped_refptr<NotificationDelegate> delegate,
+               const Notification& other);
+
   // Creates a copy of the |other| notification. The delegate, if any, will be
   // identical for both the Notification instances.
   Notification(const Notification& other);
diff --git a/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc b/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc
index 94b2aa607..dd5d9e8 100644
--- a/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc
+++ b/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc
@@ -40,6 +40,8 @@
       widget_(widget),
       has_implicit_external_sync_(
           HasEGLExtension("EGL_ARM_implicit_external_sync")),
+      has_image_flush_external_(
+          HasEGLExtension("EGL_EXT_image_flush_external")),
       weak_factory_(this) {
   surface_factory_->RegisterSurface(window_->widget(), this);
   supports_plane_gpu_fences_ = window_->SupportsGpuFences();
@@ -114,9 +116,11 @@
     return;
   }
 
-  // TODO(dcastagna): remove glFlush since eglImageFlushExternalEXT called on
-  // the image should be enough (crbug.com/720045).
-  glFlush();
+  if ((!has_image_flush_external_ && !supports_plane_gpu_fences_) ||
+      requires_gl_flush_on_swap_buffers_) {
+    glFlush();
+  }
+
   unsubmitted_frames_.back()->Flush();
 
   PendingFrame* frame = unsubmitted_frames_.back().get();
@@ -195,6 +199,10 @@
   use_egl_fence_sync_ = false;
 }
 
+void GbmSurfaceless::SetForceGlFlushOnSwapBuffers() {
+  requires_gl_flush_on_swap_buffers_ = true;
+}
+
 GbmSurfaceless::~GbmSurfaceless() {
   Destroy();  // The EGL surface must be destroyed before SurfaceOzone.
   surface_factory_->UnregisterSurface(window_->widget());
diff --git a/ui/ozone/platform/drm/gpu/gbm_surfaceless.h b/ui/ozone/platform/drm/gpu/gbm_surfaceless.h
index 598ee4b..403e900 100644
--- a/ui/ozone/platform/drm/gpu/gbm_surfaceless.h
+++ b/ui/ozone/platform/drm/gpu/gbm_surfaceless.h
@@ -63,6 +63,7 @@
                           PresentationCallback presentation_callback) override;
   EGLConfig GetConfig() override;
   void SetRelyOnImplicitSync() override;
+  void SetForceGlFlushOnSwapBuffers() override;
 
  protected:
   ~GbmSurfaceless() override;
@@ -104,6 +105,7 @@
   std::vector<std::unique_ptr<PendingFrame>> unsubmitted_frames_;
   std::unique_ptr<PendingFrame> submitted_frame_;
   const bool has_implicit_external_sync_;
+  const bool has_image_flush_external_;
   bool last_swap_buffers_result_ = true;
   bool supports_plane_gpu_fences_ = false;
   bool use_egl_fence_sync_ = true;
@@ -111,6 +113,7 @@
   // Conservatively assume we begin on a device that requires
   // explicit synchronization.
   bool is_on_external_drm_device_ = true;
+  bool requires_gl_flush_on_swap_buffers_ = false;
 
   base::WeakPtrFactory<GbmSurfaceless> weak_factory_;
 
diff --git a/ui/views/color_chooser/color_chooser_view.cc b/ui/views/color_chooser/color_chooser_view.cc
index 252fb7c..6bdd26a 100644
--- a/ui/views/color_chooser/color_chooser_view.cc
+++ b/ui/views/color_chooser/color_chooser_view.cc
@@ -4,6 +4,9 @@
 
 #include "ui/views/color_chooser/color_chooser_view.h"
 
+#include <memory>
+#include <utility>
+
 #include <stdint.h>
 
 #include "base/logging.h"
@@ -369,18 +372,17 @@
   SetLayoutManager(std::make_unique<BoxLayout>(
       BoxLayout::kVertical, gfx::Insets(kMarginWidth), kMarginWidth));
 
-  View* container = new View();
+  auto container = std::make_unique<View>();
   container->SetLayoutManager(std::make_unique<BoxLayout>(
       BoxLayout::kHorizontal, gfx::Insets(), kMarginWidth));
-  saturation_value_ = new SaturationValueView(this);
-  container->AddChildView(saturation_value_);
-  hue_ = new HueView(this);
-  container->AddChildView(hue_);
-  AddChildView(container);
+  saturation_value_ =
+      container->AddChildView(std::make_unique<SaturationValueView>(this));
+  hue_ = container->AddChildView(std::make_unique<HueView>(this));
+  AddChildView(std::move(container));
 
-  View* container2 = new View();
+  auto container2 = std::make_unique<View>();
   GridLayout* layout = container2->SetLayoutManager(
-      std::make_unique<views::GridLayout>(container2));
+      std::make_unique<views::GridLayout>(container2.get()));
   ColumnSet* columns = layout->AddColumnSet(0);
   columns->AddColumn(
       GridLayout::LEADING, GridLayout::FILL, 0, GridLayout::USE_PREF, 0, 0);
@@ -396,7 +398,7 @@
   layout->AddView(textfield_);
   selected_color_patch_ = new SelectedColorPatchView();
   layout->AddView(selected_color_patch_);
-  AddChildView(container2);
+  AddChildView(std::move(container2));
 
   OnColorChanged(initial_color);
 }
diff --git a/ui/views/controls/scrollbar/scroll_bar.cc b/ui/views/controls/scrollbar/scroll_bar.cc
index 098033e2..b272d26 100644
--- a/ui/views/controls/scrollbar/scroll_bar.cc
+++ b/ui/views/controls/scrollbar/scroll_bar.cc
@@ -458,6 +458,9 @@
 
 BEGIN_METADATA(ScrollBar)
 METADATA_PARENT_CLASS(View)
+ADD_READONLY_PROPERTY_METADATA(ScrollBar, int, MaxPosition)
+ADD_READONLY_PROPERTY_METADATA(ScrollBar, int, MinPosition)
+ADD_READONLY_PROPERTY_METADATA(ScrollBar, int, Position)
 END_METADATA()
 
 }  // namespace views
diff --git a/ui/views/controls/tabbed_pane/tabbed_pane.cc b/ui/views/controls/tabbed_pane/tabbed_pane.cc
index 047da5a..5167f6d 100644
--- a/ui/views/controls/tabbed_pane/tabbed_pane.cc
+++ b/ui/views/controls/tabbed_pane/tabbed_pane.cc
@@ -385,10 +385,8 @@
   // Do not draw focus ring in kHighlight mode.
   if (tabbed_pane()->GetStyle() != TabbedPane::TabStripStyle::kHighlight) {
     SetBorder(CreateSolidBorder(
-        GetInsets().top(),
-        SkColorSetA(GetNativeTheme()->GetSystemColor(
-                        ui::NativeTheme::kColorId_FocusedBorderColor),
-                    0x66)));
+        GetInsets().top(), GetNativeTheme()->GetSystemColor(
+                               ui::NativeTheme::kColorId_FocusedBorderColor)));
   }
 
   // When the tab gains focus, send an accessibility event indicating that the
@@ -670,8 +668,10 @@
     rect = gfx::Rect(max_cross_axis - kSelectedBorderThickness, min_main_axis,
                      kSelectedBorderThickness, max_main_axis - min_main_axis);
   }
-  canvas->FillRect(rect, GetNativeTheme()->GetSystemColor(
-                             ui::NativeTheme::kColorId_FocusedBorderColor));
+  canvas->FillRect(
+      rect, SkColorSetA(GetNativeTheme()->GetSystemColor(
+                            ui::NativeTheme::kColorId_FocusedBorderColor),
+                        SK_AlphaOPAQUE));
 }
 
 void MdTabStrip::AnimationProgressed(const gfx::Animation* animation) {
diff --git a/ui/views/controls/webview/webview.cc b/ui/views/controls/webview/webview.cc
index 13f2f862d..3b523e4 100644
--- a/ui/views/controls/webview/webview.cc
+++ b/ui/views/controls/webview/webview.cc
@@ -19,7 +19,6 @@
 #include "ui/accessibility/ax_enums.mojom.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/events/event.h"
-#include "ui/views/controls/native/native_view_host.h"
 #include "ui/views/focus/focus_manager.h"
 #include "ui/views/views_delegate.h"
 
@@ -50,9 +49,7 @@
 // WebView, public:
 
 WebView::WebView(content::BrowserContext* browser_context)
-    : holder_(new NativeViewHost()), browser_context_(browser_context) {
-  AddChildView(holder_);  // Takes ownership of |holder_|.
-}
+    : browser_context_(browser_context) {}
 
 WebView::~WebView() {
   SetWebContents(nullptr);  // Make sure all necessary tear-down takes place.
diff --git a/ui/views/controls/webview/webview.h b/ui/views/controls/webview/webview.h
index 48ce5978..9ba9464 100644
--- a/ui/views/controls/webview/webview.h
+++ b/ui/views/controls/webview/webview.h
@@ -14,13 +14,12 @@
 #include "content/public/browser/web_contents_delegate.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "ui/gfx/native_widget_types.h"
+#include "ui/views/controls/native/native_view_host.h"
 #include "ui/views/controls/webview/webview_export.h"
 #include "ui/views/view.h"
 
 namespace views {
 
-class NativeViewHost;
-
 // Provides a view of a WebContents instance.  WebView can be used standalone,
 // creating and displaying an internally-owned WebContents; or within a full
 // browser where the browser swaps its own WebContents instances in/out (e.g.,
@@ -178,7 +177,8 @@
   std::unique_ptr<content::WebContents> CreateWebContents(
       content::BrowserContext* browser_context);
 
-  NativeViewHost* const holder_;
+  NativeViewHost* const holder_ =
+      AddChildView(std::make_unique<NativeViewHost>());
   // Non-NULL if |web_contents()| was created and is owned by this WebView.
   std::unique_ptr<content::WebContents> wc_owner_;
   // When true, WebView auto-embeds fullscreen widgets as a child view.
diff --git a/ui/views/examples/bubble_example.cc b/ui/views/examples/bubble_example.cc
index a2d94d57..26c8d9e 100644
--- a/ui/views/examples/bubble_example.cc
+++ b/ui/views/examples/bubble_example.cc
@@ -75,8 +75,6 @@
 BubbleExample::~BubbleExample() = default;
 
 void BubbleExample::CreateExampleView(View* container) {
-  PrintStatus("Click with optional modifiers: [Ctrl] for set_arrow(NONE), "
-     "[Alt] for set_arrow(FLOAT), or [Shift] to reverse the arrow iteration.");
   container->SetLayoutManager(
       std::make_unique<BoxLayout>(BoxLayout::kHorizontal, gfx::Insets(), 10));
   no_shadow_ = new LabelButton(this, ASCIIToUTF16("No Shadow"));
@@ -123,6 +121,8 @@
   BubbleDialogDelegateView::CreateBubble(bubble);
 
   bubble->GetWidget()->Show();
+  PrintStatus("Click with optional modifiers: [Ctrl] for set_arrow(NONE), "
+     "[Alt] for set_arrow(FLOAT), or [Shift] to reverse the arrow iteration.");
 }
 
 }  // namespace examples
diff --git a/ui/views/examples/dialog_example.cc b/ui/views/examples/dialog_example.cc
index 152a8a5e..e812aea 100644
--- a/ui/views/examples/dialog_example.cc
+++ b/ui/views/examples/dialog_example.cc
@@ -182,11 +182,6 @@
       MdTextButton::CreateSecondaryUiButton(this, base::ASCIIToUTF16("Show"))
           .release();
   layout->AddView(show_);
-
-  // Grow the dialog a bit when this example is first selected, so it all fits.
-  gfx::Size dialog_size = container->GetWidget()->GetRestoredBounds().size();
-  dialog_size.set_height(dialog_size.height() + 80);
-  container->GetWidget()->SetSize(dialog_size);
 }
 
 void DialogExample::StartRowWithLabel(GridLayout* layout, const char* label) {
@@ -272,10 +267,10 @@
       // be created as MODAL_TYPE_WINDOW without specifying a parent.
       gfx::NativeView parent = nullptr;
       if (mode_->GetSelectedIndex() != kFakeModeless)
-        parent = container()->GetWidget()->GetNativeView();
+        parent = example_view()->GetWidget()->GetNativeView();
 
       DialogDelegate::CreateDialogWidget(
-          dialog, container()->GetWidget()->GetNativeWindow(), parent);
+          dialog, example_view()->GetWidget()->GetNativeWindow(), parent);
     }
     last_dialog_->GetWidget()->Show();
     return;
diff --git a/ui/views/examples/example_base.cc b/ui/views/examples/example_base.cc
index bc296aee..878ac700 100644
--- a/ui/views/examples/example_base.cc
+++ b/ui/views/examples/example_base.cc
@@ -17,43 +17,10 @@
 // This function can only be called if there is a visible examples window.
 void LogStatus(const std::string& status);
 
-namespace {
-
-// TODO(oshima): Check if this special container is still necessary.
-class ContainerView : public View {
- public:
-  explicit ContainerView(ExampleBase* base)
-      : example_view_created_(false),
-        example_base_(base) {
-  }
-
- private:
-  // View:
-  void ViewHierarchyChanged(
-      const ViewHierarchyChangedDetails& details) override {
-    View::ViewHierarchyChanged(details);
-    // We're not using child == this because a Widget may not be
-    // available when this is added to the hierarchy.
-    if (details.is_add && GetWidget() && !example_view_created_) {
-      example_view_created_ = true;
-      example_base_->CreateExampleView(this);
-    }
-  }
-
-  // True if the example view has already been created, or false otherwise.
-  bool example_view_created_;
-
-  ExampleBase* example_base_;
-
-  DISALLOW_COPY_AND_ASSIGN(ContainerView);
-};
-
-}  // namespace
-
 ExampleBase::~ExampleBase() = default;
 
 ExampleBase::ExampleBase(const char* title) : example_title_(title) {
-  container_ = new ContainerView(this);
+  container_ = new View();
 }
 
 // Prints a message in the status area, at the bottom of the window.
diff --git a/ui/views/examples/example_base.h b/ui/views/examples/example_base.h
index 7a6b33d..e582096 100644
--- a/ui/views/examples/example_base.h
+++ b/ui/views/examples/example_base.h
@@ -28,8 +28,6 @@
  protected:
   explicit ExampleBase(const char* title);
 
-  View* container() { return container_; }
-
   // Prints a message in the status area, at the bottom of the window.
   void PrintStatus(const char* format, ...);
 
diff --git a/ui/views/examples/examples_window.cc b/ui/views/examples/examples_window.cc
index d95d143..f771a183 100644
--- a/ui/views/examples/examples_window.cc
+++ b/ui/views/examples/examples_window.cc
@@ -90,6 +90,9 @@
   examples.push_back(std::make_unique<TreeViewExample>());
   examples.push_back(std::make_unique<VectorExample>());
   examples.push_back(std::make_unique<WidgetExample>());
+
+  for (auto& example : examples)
+    example->CreateExampleView(example->example_view());
   return examples;
 }
 
@@ -150,7 +153,8 @@
     instance_ = this;
     combobox_->set_listener(this);
 
-    SetBackground(CreateStandardPanelBackground());
+    SetBackground(CreateThemedSolidBackground(
+        this, ui::NativeTheme::kColorId_DialogBackground));
     GridLayout* layout =
         SetLayoutManager(std::make_unique<views::GridLayout>(this));
     ColumnSet* column_set = layout->AddColumnSet(0);
@@ -197,16 +201,22 @@
       std::move(on_close_).Run();
   }
   gfx::Size CalculatePreferredSize() const override {
-    return gfx::Size(800, 300);
+    gfx::Size size(800, 300);
+    for (int i = 0; i < combobox_model_->GetItemCount(); i++) {
+      size.set_height(
+          std::max(size.height(),
+                   combobox_model_->GetItemViewAt(i)->GetHeightForWidth(800)));
+    }
+    return size;
   }
 
   // ComboboxListener:
   void OnPerformAction(Combobox* combobox) override {
     DCHECK_EQ(combobox, combobox_);
-    DCHECK(combobox->GetSelectedIndex());
+    int index = combobox->GetSelectedIndex();
+    DCHECK_LT(index, combobox_model_->GetItemCount());
     example_shown_->RemoveAllChildViews(false);
-    example_shown_->AddChildView(
-        combobox_model_->GetItemViewAt(combobox->GetSelectedIndex()));
+    example_shown_->AddChildView(combobox_model_->GetItemViewAt(index));
     example_shown_->RequestFocus();
     SetStatus(std::string());
     InvalidateLayout();
@@ -244,7 +254,8 @@
 }
 
 void LogStatus(const std::string& string) {
-  ExamplesWindowContents::instance()->SetStatus(string);
+  if (ExamplesWindowContents::instance())
+    ExamplesWindowContents::instance()->SetStatus(string);
 }
 
 }  // namespace examples
diff --git a/ui/views/examples/label_example.cc b/ui/views/examples/label_example.cc
index 82a9bd87..4e48df0 100644
--- a/ui/views/examples/label_example.cc
+++ b/ui/views/examples/label_example.cc
@@ -64,58 +64,60 @@
   // A very simple label example, followed by additional helpful examples.
   container->SetLayoutManager(
       std::make_unique<BoxLayout>(BoxLayout::kVertical, gfx::Insets(), 10));
-  Label* label = new Label(ASCIIToUTF16("Hello world!"));
-  container->AddChildView(label);
+  container->AddChildView(
+      std::make_unique<Label>(ASCIIToUTF16("Hello world!")));
 
   const wchar_t hello_world_hebrew[] =
       L"\x5e9\x5dc\x5d5\x5dd \x5d4\x5e2\x5d5\x5dc\x5dd!";
-  label = new Label(WideToUTF16(hello_world_hebrew));
+  auto label = std::make_unique<Label>(WideToUTF16(hello_world_hebrew));
   label->SetHorizontalAlignment(gfx::ALIGN_RIGHT);
-  container->AddChildView(label);
+  container->AddChildView(std::move(label));
 
-  label = new Label(WideToUTF16(L"A UTF16 surrogate pair: \x5d0\x5b0"));
+  label = std::make_unique<Label>(
+      WideToUTF16(L"A UTF16 surrogate pair: \x5d0\x5b0"));
   label->SetHorizontalAlignment(gfx::ALIGN_RIGHT);
-  container->AddChildView(label);
+  container->AddChildView(std::move(label));
 
-  label = new Label(ASCIIToUTF16("A left-aligned blue label."));
+  label = std::make_unique<Label>(ASCIIToUTF16("A left-aligned blue label."));
   label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
   label->SetEnabledColor(SK_ColorBLUE);
-  container->AddChildView(label);
+  container->AddChildView(std::move(label));
 
-  label = new Label(WideToUTF16(L"Password!"));
+  label = std::make_unique<Label>(WideToUTF16(L"Password!"));
   label->SetObscured(true);
-  container->AddChildView(label);
+  container->AddChildView(std::move(label));
 
-  label = new Label(ASCIIToUTF16("A Courier-18 label with shadows."));
+  label =
+      std::make_unique<Label>(ASCIIToUTF16("A Courier-18 label with shadows."));
   label->SetFontList(gfx::FontList("Courier, 18px"));
   gfx::ShadowValues shadows(1,
                             gfx::ShadowValue(gfx::Vector2d(), 1, SK_ColorRED));
   constexpr gfx::ShadowValue shadow(gfx::Vector2d(2, 2), 0, SK_ColorGRAY);
   shadows.push_back(shadow);
   label->SetShadows(shadows);
-  container->AddChildView(label);
+  container->AddChildView(std::move(label));
 
-  label = new ExamplePreferredSizeLabel();
+  label = std::make_unique<ExamplePreferredSizeLabel>();
   label->SetText(ASCIIToUTF16("A long label will elide toward its logical end "
       "if the text's width exceeds the label's available width."));
-  container->AddChildView(label);
+  container->AddChildView(std::move(label));
 
-  label = new ExamplePreferredSizeLabel();
+  label = std::make_unique<ExamplePreferredSizeLabel>();
   label->SetText(ASCIIToUTF16("A multi-line label will wrap onto subsequent "
     "lines if the text's width exceeds the label's available width, which is "
     "helpful for extemely long text used to demonstrate line wrapping."));
   label->SetMultiLine(true);
-  container->AddChildView(label);
+  container->AddChildView(std::move(label));
 
-  label = new Label(ASCIIToUTF16("Label with thick border"));
+  label = std::make_unique<Label>(ASCIIToUTF16("Label with thick border"));
   label->SetBorder(CreateSolidBorder(20, SK_ColorRED));
-  container->AddChildView(label);
+  container->AddChildView(std::move(label));
 
-  label = new Label(
+  label = std::make_unique<Label>(
       ASCIIToUTF16("A multiline label...\n\n...which supports text selection"));
   label->SetSelectable(true);
   label->SetMultiLine(true);
-  container->AddChildView(label);
+  container->AddChildView(std::move(label));
 
   AddCustomLabel(container);
 }
diff --git a/ui/views/examples/multiline_example.cc b/ui/views/examples/multiline_example.cc
index 7533fb2..e05d81d 100644
--- a/ui/views/examples/multiline_example.cc
+++ b/ui/views/examples/multiline_example.cc
@@ -180,8 +180,8 @@
   render_text_view_->SetText(new_contents);
   if (label_checkbox_->GetChecked())
     label_->SetText(new_contents);
-  container()->InvalidateLayout();
-  container()->SchedulePaint();
+  example_view()->InvalidateLayout();
+  example_view()->SchedulePaint();
 }
 
 void MultilineExample::ButtonPressed(Button* sender, const ui::Event& event) {
@@ -191,8 +191,8 @@
   } else if (sender == elision_checkbox_) {
     render_text_view_->SetMaxLines(elision_checkbox_->GetChecked() ? 3 : 0);
   }
-  container()->InvalidateLayout();
-  container()->SchedulePaint();
+  example_view()->InvalidateLayout();
+  example_view()->SchedulePaint();
 }
 
 }  // namespace examples
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_config.html b/ui/webui/resources/cr_components/chromeos/network/network_config.html
index fcab8984..6461170 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_config.html
+++ b/ui/webui/resources/cr_components/chromeos/network/network_config.html
@@ -9,6 +9,7 @@
 <link rel="import" href="chrome://resources/html/i18n_behavior.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-spinner/paper-spinner-lite.html">
 <link rel="import" href="network_config_input.html">
 <link rel="import" href="network_config_select.html">
 <link rel="import" href="network_config_toggle.html">
@@ -18,192 +19,202 @@
 <dom-module id="network-config">
   <template>
     <style include="network-shared settings-shared action-link iron-flex">
+      #spinner-container {
+        height: 200px;
+      }
     </style>
 
-    <!-- SSID (WiFi) -->
-    <template is="dom-if" if="[[isType_(NetworkType_.WI_FI, type)]]" restamp>
-      <network-config-input id="ssid" label="[[i18n('OncWiFi-SSID')]]"
-          value="{{configProperties_.WiFi.SSID}}" readonly="[[hasGuid_(guid)]]">
-      </network-config-input>
+    <template is="dom-if" if="[[waitingForProperties_]]" restamp>
+      <div id="spinner-container" class="layout vertical center-center">
+        <paper-spinner-lite active></paper-spinner-lite>
+      </div>
     </template>
 
-    <!-- Security (WiFi and Ethernet) -->
-    <template is="dom-if" if="[[securityIsVisible_(type)]]" restamp>
-      <network-config-select id="security" label="[[i18n('OncWiFi-Security')]]"
-          value="{{security_}}"
-          disabled="[[!securityIsEnabled_(guid, type)]]"
-          items="[[getSecurityItems_(type)]]"
-          onc-prefix="WiFi.Security"
-          property="[[getManagedSecurity_(managedProperties)]]">
-      </network-config-select>
-    </template>
-
-    <!-- Passphrase (WiFi) -->
-    <template is="dom-if" if="[[configRequiresPassphrase_(type, security_)]]"
-        restamp>
-      <network-password-input label="[[i18n('OncWiFi-Passphrase')]]"
-          value="{{configProperties_.WiFi.Passphrase}}"
-          on-enter="connectIfConfigured_"
-          property="[[managedProperties.WiFi.Passphrase]]">
-      </network-password-input>
-    </template>
-
-    <!-- VPN -->
-    <template is="dom-if" if="[[showVpn_]]" restamp>
-      <network-config-input label="[[i18n('OncVPN-Host')]]"
-          value="{{configProperties_.VPN.Host}}"
-          property="[[managedProperties.VPN.Host]]">
-      </network-config-input>
-      <network-config-input label="[[i18n('OncName')]]"
-          value="{{configProperties_.Name}}"
-          readonly="[[hasGuid_(guid)]]">
-      </network-config-input>
-      <network-config-select id="outer" label="[[i18n('OncVPN-Type')]]"
-          value="{{vpnType_}}" items="[[vpnTypeItems_]]"
-          onc-prefix="VPN.Type" disabled="[[hasGuid_(guid)]]"
-          property="[[managedProperties.VPN.Type]]">
-      </network-config-select>
-      <template is="dom-if" if="[[!showVpn_.OpenVPN]]">
-        <network-config-input label="[[i18n('OncVPN-L2TP-Username')]]"
-            value="{{configProperties_.VPN.L2TP.Username}}"
-            property="[[managedProperties.VPN.L2TP.Username]]">
+    <template is="dom-if" if="[[!waitingForProperties_]]" restamp>
+      <!-- SSID (WiFi) -->
+      <template is="dom-if" if="[[isType_(NetworkType_.WI_FI, type)]]" restamp>
+        <network-config-input id="ssid" label="[[i18n('OncWiFi-SSID')]]"
+            value="{{configProperties_.WiFi.SSID}}" readonly="[[hasGuid_(guid)]]">
         </network-config-input>
-        <network-password-input label="[[i18n('OncVPN-L2TP-Password')]]"
-            value="{{configProperties_.VPN.L2TP.Password}}"
-            property="[[managedProperties.VPN.L2TP.Password]]">
+      </template>
+
+      <!-- Security (WiFi and Ethernet) -->
+      <template is="dom-if" if="[[securityIsVisible_(type)]]" restamp>
+        <network-config-select id="security" label="[[i18n('OncWiFi-Security')]]"
+            value="{{security_}}"
+            disabled="[[!securityIsEnabled_(guid, type)]]"
+            items="[[getSecurityItems_(type)]]"
+            onc-prefix="WiFi.Security"
+            property="[[getManagedSecurity_(managedProperties)]]">
+        </network-config-select>
+      </template>
+
+      <!-- Passphrase (WiFi) -->
+      <template is="dom-if" if="[[configRequiresPassphrase_(type, security_)]]"
+          restamp>
+        <network-password-input label="[[i18n('OncWiFi-Passphrase')]]"
+            value="{{configProperties_.WiFi.Passphrase}}"
+            on-enter="connectIfConfigured_"
+            property="[[managedProperties.WiFi.Passphrase]]">
         </network-password-input>
-        <network-config-input label="[[i18n('OncVPN-IPsec-Group')]]"
-            value="{{configProperties_.VPN.IPsec.Group}}"
-            property="[[managedProperties.VPN.IPsec.Group]]">
+      </template>
+
+      <!-- VPN -->
+      <template is="dom-if" if="[[showVpn_]]" restamp>
+        <network-config-input label="[[i18n('OncVPN-Host')]]"
+            value="{{configProperties_.VPN.Host}}"
+            property="[[managedProperties.VPN.Host]]">
         </network-config-input>
-        <template is="dom-if" if="[[!showVpn_.Cert]]">
-          <network-password-input label="[[i18n('OncVPN-IPsec-PSK')]]"
-              value="{{configProperties_.VPN.IPsec.PSK}}"
-              property="[[managedProperties.VPN.IPsec.PSK]]">
+        <network-config-input label="[[i18n('OncName')]]"
+            value="{{configProperties_.Name}}"
+            readonly="[[hasGuid_(guid)]]">
+        </network-config-input>
+        <network-config-select id="outer" label="[[i18n('OncVPN-Type')]]"
+            value="{{vpnType_}}" items="[[vpnTypeItems_]]"
+            onc-prefix="VPN.Type" disabled="[[hasGuid_(guid)]]"
+            property="[[managedProperties.VPN.Type]]">
+        </network-config-select>
+        <template is="dom-if" if="[[!showVpn_.OpenVPN]]">
+          <network-config-input label="[[i18n('OncVPN-L2TP-Username')]]"
+              value="{{configProperties_.VPN.L2TP.Username}}"
+              property="[[managedProperties.VPN.L2TP.Username]]">
+          </network-config-input>
+          <network-password-input label="[[i18n('OncVPN-L2TP-Password')]]"
+              value="{{configProperties_.VPN.L2TP.Password}}"
+              property="[[managedProperties.VPN.L2TP.Password]]">
           </network-password-input>
+          <network-config-input label="[[i18n('OncVPN-IPsec-Group')]]"
+              value="{{configProperties_.VPN.IPsec.Group}}"
+              property="[[managedProperties.VPN.IPsec.Group]]">
+          </network-config-input>
+          <template is="dom-if" if="[[!showVpn_.Cert]]">
+            <network-password-input label="[[i18n('OncVPN-IPsec-PSK')]]"
+                value="{{configProperties_.VPN.IPsec.PSK}}"
+                property="[[managedProperties.VPN.IPsec.PSK]]">
+            </network-password-input>
+          </template>
         </template>
+        <template is="dom-if" if="[[showVpn_.OpenVPN]]">
+          <network-config-input label="[[i18n('OncVPN-OpenVPN-Username')]]"
+              value="{{configProperties_.VPN.OpenVPN.Username}}"
+              property="[[managedProperties.VPN.OpenVPN.Username]]">
+          </network-config-input>
+          <network-password-input label="[[i18n('OncVPN-OpenVPN-Password')]]"
+              value="{{configProperties_.VPN.OpenVPN.Password}}"
+              property="[[managedProperties.VPN.OpenVPN.Password]]">
+          </network-password-input>
+          <network-config-input label="[[i18n('OncVPN-OpenVPN-OTP')]]"
+              value="{{configProperties_.VPN.OpenVPN.OTP}}"
+              property="[[managedProperties.VPN.OpenVPN.OTP]]">
+          </network-config-input>
+        </template>
+        <template is="dom-if" if="[[showVpn_.Cert]]">
+          <network-config-select id="vpnServerCa"
+              label="[[i18n('OncEAP-ServerCA')]]"
+              value="{{selectedServerCaHash_}}" items="[[serverCaCerts_]]"
+              cert-list
+              property="[[getManagedVpnServerCaRefs_(managedProperties)]]">
+          </network-config-select>
+          <network-config-select id="vpnUserCert"
+              label="[[i18n('OncEAP-UserCert')]]"
+              value="{{selectedUserCertHash_}}" items="[[userCerts_]]"
+              cert-list
+              property="[[getManagedVpnClientCertType_(managedProperties)]]">
+          </network-config-select>
+        </template>
+        <network-config-toggle label="[[i18n('networkConfigSaveCredentials')]]"
+            checked="{{vpnSaveCredentials_}}"
+            property="[[getManagedVpnSaveCredentials_(managedProperties)]]">
+        </network-config-toggle>
       </template>
-      <template is="dom-if" if="[[showVpn_.OpenVPN]]">
-        <network-config-input label="[[i18n('OncVPN-OpenVPN-Username')]]"
-            value="{{configProperties_.VPN.OpenVPN.Username}}"
-            property="[[managedProperties.VPN.OpenVPN.Username]]">
-        </network-config-input>
-        <network-password-input label="[[i18n('OncVPN-OpenVPN-Password')]]"
-            value="{{configProperties_.VPN.OpenVPN.Password}}"
-            property="[[managedProperties.VPN.OpenVPN.Password]]">
-        </network-password-input>
-        <network-config-input label="[[i18n('OncVPN-OpenVPN-OTP')]]"
-            value="{{configProperties_.VPN.OpenVPN.OTP}}"
-            property="[[managedProperties.VPN.OpenVPN.OTP]]">
-        </network-config-input>
-      </template>
-      <template is="dom-if" if="[[showVpn_.Cert]]">
-        <network-config-select id="vpnServerCa"
-            label="[[i18n('OncEAP-ServerCA')]]"
+
+      <!-- EAP (WiFi, WiMAX, Ethernet) -->
+      <template is="dom-if" if="[[showEap_]]" restamp>
+        <network-config-select id="outer" label="[[i18n('OncEAP-Outer')]]"
+            value="{{eapProperties_.Outer}}" items="[[eapOuterItems_]]"
+            onc-prefix="EAP.Outer" hidden="[[!showEap_.Outer]]"
+            property="[[managedEapProperties_.Outer]]">
+        </network-config-select>
+        <network-config-select id="inner" label="[[i18n('OncEAP-Inner')]]"
+            value="{{eapProperties_.Inner}}"
+            items="[[getEapInnerItems_(eapProperties_.Outer)]]"
+            onc-prefix="EAP.Inner" hidden="[[!showEap_.Inner]]"
+            property="[[managedEapProperties_.Inner]]">
+        </network-config-select>
+        <network-config-select id="serverCa" label="[[i18n('OncEAP-ServerCA')]]"
             value="{{selectedServerCaHash_}}" items="[[serverCaCerts_]]"
-            cert-list
-            property="[[getManagedVpnServerCaRefs_(managedProperties)]]">
+            hidden="[[!showEap_.ServerCA]]" cert-list
+            property="[[managedEapProperties_.UseSystemCAs]]"
+            device-certs-only="[[deviceCertsOnly_]]">
         </network-config-select>
-        <network-config-select id="vpnUserCert"
-            label="[[i18n('OncEAP-UserCert')]]"
+        <network-config-input label="[[i18n('OncEAP-SubjectMatch')]]"
+            value="{{eapProperties_.SubjectMatch}}"
+            hidden="[[!showEap_.SubjectMatch]]"
+            property="[[managedEapProperties_.SubjectMatch]]">
+        </network-config-input>
+        <network-config-select id="userCert" label="[[i18n('OncEAP-UserCert')]]"
             value="{{selectedUserCertHash_}}" items="[[userCerts_]]"
-            cert-list
-            property="[[getManagedVpnClientCertType_(managedProperties)]]">
+            hidden="[[!showEap_.UserCert]]" cert-list
+            property="[[managedEapProperties_.ClientCertType]]"
+            device-certs-only="[[deviceCertsOnly_]]">
         </network-config-select>
+        <network-config-input label="[[i18n('OncEAP-Identity')]]"
+            value="{{eapProperties_.Identity}}" hidden="[[!showEap_.Identity]]"
+            property="[[managedEapProperties_.Identity]]">
+        </network-config-input>
+        <network-password-input label="[[i18n('OncEAP-Password')]]"
+            value="{{eapProperties_.Password}}" hidden="[[!showEap_.Password]]"
+            property="[[managedEapProperties_.Password]]">
+        </network-password-input>
+        <network-config-input label="[[i18n('OncEAP-AnonymousIdentity')]]"
+            value="{{eapProperties_.AnonymousIdentity}}"
+            hidden="[[!showEap_.AnonymousIdentity]]"
+            property="[[managedEapProperties_.AnonymousIdentity]]">
+        </network-config-input>
+        <network-config-toggle label="[[i18n('networkConfigSaveCredentials')]]"
+            checked="{{eapProperties_.SaveCredentials}}"
+            property="[[managedEapProperties_.SaveCredentials]]">
+        </network-config-toggle>
       </template>
-      <network-config-toggle label="[[i18n('networkConfigSaveCredentials')]]"
-          checked="{{vpnSaveCredentials_}}"
-          property="[[getManagedVpnSaveCredentials_(managedProperties)]]">
-      </network-config-toggle>
-    </template>
 
-    <!-- EAP (WiFi, WiMAX, Ethernet) -->
-    <template is="dom-if" if="[[showEap_]]" restamp>
-      <network-config-select id="outer" label="[[i18n('OncEAP-Outer')]]"
-          value="{{eapProperties_.Outer}}" items="[[eapOuterItems_]]"
-          onc-prefix="EAP.Outer" hidden="[[!showEap_.Outer]]"
-          property="[[managedEapProperties_.Outer]]">
-      </network-config-select>
-      <network-config-select id="inner" label="[[i18n('OncEAP-Inner')]]"
-          value="{{eapProperties_.Inner}}"
-          items="[[getEapInnerItems_(eapProperties_.Outer)]]"
-          onc-prefix="EAP.Inner" hidden="[[!showEap_.Inner]]"
-          property="[[managedEapProperties_.Inner]]">
-      </network-config-select>
-      <network-config-select id="serverCa" label="[[i18n('OncEAP-ServerCA')]]"
-          value="{{selectedServerCaHash_}}" items="[[serverCaCerts_]]"
-          hidden="[[!showEap_.ServerCA]]" cert-list
-          property="[[managedEapProperties_.UseSystemCAs]]"
-          device-certs-only="[[deviceCertsOnly_]]">
-      </network-config-select>
-      <network-config-input label="[[i18n('OncEAP-SubjectMatch')]]"
-          value="{{eapProperties_.SubjectMatch}}"
-          hidden="[[!showEap_.SubjectMatch]]"
-          property="[[managedEapProperties_.SubjectMatch]]">
-      </network-config-input>
-      <network-config-select id="userCert" label="[[i18n('OncEAP-UserCert')]]"
-          value="{{selectedUserCertHash_}}" items="[[userCerts_]]"
-          hidden="[[!showEap_.UserCert]]" cert-list
-          property="[[managedEapProperties_.ClientCertType]]"
-          device-certs-only="[[deviceCertsOnly_]]">
-      </network-config-select>
-      <network-config-input label="[[i18n('OncEAP-Identity')]]"
-          value="{{eapProperties_.Identity}}" hidden="[[!showEap_.Identity]]"
-          property="[[managedEapProperties_.Identity]]">
-      </network-config-input>
-      <network-password-input label="[[i18n('OncEAP-Password')]]"
-          value="{{eapProperties_.Password}}" hidden="[[!showEap_.Password]]"
-          property="[[managedEapProperties_.Password]]">
-      </network-password-input>
-      <network-config-input label="[[i18n('OncEAP-AnonymousIdentity')]]"
-          value="{{eapProperties_.AnonymousIdentity}}"
-          hidden="[[!showEap_.AnonymousIdentity]]"
-          property="[[managedEapProperties_.AnonymousIdentity]]">
-      </network-config-input>
-      <network-config-toggle label="[[i18n('networkConfigSaveCredentials')]]"
-          checked="{{eapProperties_.SaveCredentials}}"
-          property="[[managedEapProperties_.SaveCredentials]]">
-      </network-config-toggle>
-    </template>
+      <!-- Share (WiFi and WiMAX) -->
+      <template is="dom-if"
+          if="[[shareIsVisible_(guid, type, managedProperties)]]" restamp>
+        <div class="property-box">
+          <div id="shareLabel" class="start">[[i18n('networkConfigShare')]]</div>
+          <cr-toggle id="share" checked="{{shareNetwork_}}"
+              disabled="[[!shareIsEnabled_(guid, configProperties_.*,
+                        security_, eapProperties_.*, shareAllowEnable)]]"
+              aria-labeledby="shareLabel" on-change="onShareChanged_">
+          </cr-toggle>
+        </div>
+      </template>
 
-    <!-- Share (WiFi and WiMAX) -->
-    <template is="dom-if"
-        if="[[shareIsVisible_(guid, type, managedProperties)]]" restamp>
-      <div class="property-box">
-        <div id="shareLabel" class="start">[[i18n('networkConfigShare')]]</div>
-        <cr-toggle id="share" checked="{{shareNetwork_}}"
-            disabled="[[!shareIsEnabled_(guid, configProperties_.*,
-                      security_, eapProperties_.*, shareAllowEnable)]]"
-            aria-labeledby="shareLabel" on-change="onShareChanged_">
-        </cr-toggle>
-      </div>
-    </template>
+      <!-- AutoConnect (WiFi) -->
+      <template is="dom-if" if="[[configCanAutoConnect_(type)]]" restamp>
+        <div class="property-box">
+          <div id="autoConnectLabel"
+              class="start">[[i18n('networkAutoConnect')]]</div>
+          <template is="dom-if"
+              if="[[isAutoConnectEnforcedByPolicy_(globalPolicy)]]" restamp>
+            <cr-policy-indicator indicator-type="devicePolicy">
+            </cr-policy-indicator>
+          </template>
+          <cr-toggle id="autoConnect" checked="{{autoConnect_}}"
+              disabled="[[autoConnectDisabled_(globalPolicy)]]"
+              aria-labeledby="autoConnectLabel">
+          </cr-toggle>
+        </div>
+      </template>
 
-    <!-- AutoConnect (WiFi) -->
-    <template is="dom-if" if="[[configCanAutoConnect_(type)]]" restamp>
-      <div class="property-box">
-        <div id="autoConnectLabel"
-            class="start">[[i18n('networkAutoConnect')]]</div>
-        <template is="dom-if"
-            if="[[isAutoConnectEnforcedByPolicy_(globalPolicy)]]" restamp>
-          <cr-policy-indicator indicator-type="devicePolicy">
-          </cr-policy-indicator>
-        </template>
-        <cr-toggle id="autoConnect" checked="{{autoConnect_}}"
-            disabled="[[autoConnectDisabled_(globalPolicy)]]"
-            aria-labeledby="autoConnectLabel">
-        </cr-toggle>
-      </div>
+      <!-- Hidden Network Warning -->
+      <template is="dom-if" if="{{autoConnect_}}" restamp>
+        <div>
+          <iron-icon icon="cr:warning"></iron-icon>
+          [[i18nAdvanced('hiddenNetworkWarning')]]
+        </div>
+      </template>
     </template>
-
-    <!-- Hidden Network Warning -->
-    <template is="dom-if" if="{{autoConnect_}}" restamp>
-      <div>
-        <iron-icon icon="cr:warning"></iron-icon>
-        [[i18nAdvanced('hiddenNetworkWarning')]]
-      </div>
-    </template>
-
   </template>
   <script src="network_config.js"></script>
 </dom-module>
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_config.js b/ui/webui/resources/cr_components/chromeos/network/network_config.js
index 14b525e..83396a86 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_config.js
+++ b/ui/webui/resources/cr_components/chromeos/network/network_config.js
@@ -46,13 +46,12 @@
     /**
      * The GUID when an existing network is being configured. This will be
      * empty when configuring a new network.
-     * @private
      */
     guid: String,
 
     /**
      * The type of network being configured.
-     * @private {!chrome.networkingPrivate.NetworkType}
+     * @type {!chrome.networkingPrivate.NetworkType}
      */
     type: String,
 
@@ -62,14 +61,12 @@
     /** The default shared state. */
     shareDefault: Boolean,
 
-    /** @private */
     enableConnect: {
       type: Boolean,
       notify: true,
       value: false,
     },
 
-    /** @private */
     enableSave: {
       type: Boolean,
       notify: true,
@@ -86,7 +83,7 @@
      * The managed properties of an existing network.
      * This is used for determination of managed fields.
      * This will be undefined when configuring a new network.
-     * @private {!chrome.networkingPrivate.ManagedProperties|undefined}
+     * @type {!chrome.networkingPrivate.ManagedProperties|undefined}
      */
     managedProperties: {
       type: Object,
@@ -102,8 +99,17 @@
       value: null,
     },
 
-    /** Set if |guid| is not empty once managedProperties are received. */
-    propertiesReceived_: Boolean,
+    /**
+     * Whether this element is waiting for additional properties of the network
+     * to configure. If a network GUID is supplied, an asynchronous request is
+     * required to fetch these properties; otherwise, there is no need to wait
+     * for more properties to arrive.
+     * @private
+     */
+    waitingForProperties_: {
+      type: Boolean,
+      value: true,
+    },
 
     /** Set once managedProperties have been sent; prevents multiple saves. */
     propertiesSent_: Boolean,
@@ -326,7 +332,7 @@
 
   observers: [
     'setEnableConnect_(isConfigured_, propertiesSent_)',
-    'setEnableSave_(isConfigured_, propertiesReceived_)',
+    'setEnableSave_(isConfigured_, waitingForProperties_)',
     'updateConfigProperties_(managedProperties)',
     'updateSecurity_(configProperties_, security_)',
     'updateEapOuter_(eapProperties_.Outer)',
@@ -375,17 +381,17 @@
     this.selectedUserCertHash_ = undefined;
     this.guid = this.managedProperties.GUID;
     this.type = this.managedProperties.Type;
+    this.waitingForProperties_ = !!this.guid;
+
     if (this.guid) {
       this.networkingPrivate.getManagedProperties(
           this.guid, (managedProperties) => {
             this.getManagedPropertiesCallback_(managedProperties);
-            this.focusFirstInput_();
           });
     } else {
-      this.async(() => {
-        this.focusFirstInput_();
-      });
+      this.focusFirstInput_();
     }
+
     if (this.type == CrOnc.Type.VPN ||
         (this.globalPolicy &&
          this.globalPolicy.AllowOnlyPolicyNetworksToConnect)) {
@@ -393,6 +399,7 @@
     } else {
       this.autoConnect_ = true;
     }
+
     this.onCertificateListsChanged_();
     this.updateIsConfigured_();
     this.setShareNetwork_();
@@ -587,12 +594,14 @@
    * @private
    */
   setManagedProperties_: function(managedProperties) {
-    this.propertiesReceived_ = true;
     this.managedProperties = managedProperties;
     this.managedEapProperties_ = this.getManagedEap_(managedProperties);
     this.setError_(managedProperties.ErrorState);
     this.updateCertError_();
 
+    this.waitingForProperties_ = false;
+    this.focusFirstInput_();
+
     // Set the current shareNetwork_ value when properties are received.
     this.setShareNetwork_();
   },
@@ -1230,7 +1239,7 @@
 
   /** @private */
   setEnableSave_: function() {
-    this.enableSave = this.isConfigured_ && this.propertiesReceived_;
+    this.enableSave = this.isConfigured_ && !this.waitingForProperties_;
   },
 
   /** @private */