diff --git a/AUTHORS b/AUTHORS
index 4449900a..383f4ce 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -607,6 +607,7 @@
 Mohammed Wajahat Ali Siddiqui <wajahat.s@samsung.com>
 Mohan Reddy <mohan.reddy@samsung.com>
 Mohit Bhalla <bhallam@amazon.com>
+Momoko Hattori <momohatt10@gmail.com>
 Mrunal Kapade <mrunal.kapade@intel.com>
 Myeongjin Cho <myeongjin.cho@navercorp.com>
 Myles C. Maxfield <mymax@amazon.com>
diff --git a/DEPS b/DEPS
index bf8d6ab..a7760251 100644
--- a/DEPS
+++ b/DEPS
@@ -117,7 +117,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'c4533eae082ffd41da27963932345ebf344a66e0',
+  'angle_revision': '671809e62e807eb0f890a5d53eee1f8cb5fb825e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling build tools
   # and whatever else without interference from each other.
@@ -600,7 +600,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'd23bbdb87b57cdb8183c38e2900abd5ccdc3ae37',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '03ec3c699be2279b5583c6d5e75d0933cd8baaf4',
       'condition': 'checkout_linux',
   },
 
@@ -1106,7 +1106,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '6d2f3f4cb8bac1f7c4a945c73d07a33df74f22f9',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '1cd39fa9ea0c29acd67008919f5b524cf071a3ae',
+    Var('webrtc_git') + '/src.git' + '@' + '55d1af14751ad10e0cae741240f013ea67039f45',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1137,7 +1137,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@616feb1a7c2cf11f587543d415b7f18c9a8099f6',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@2cecd6e79600cfc4fc51c6109f5c2000b0fd684a',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/browser/aw_contents_statics.cc b/android_webview/browser/aw_contents_statics.cc
index 4061d7e..47952182 100644
--- a/android_webview/browser/aw_contents_statics.cc
+++ b/android_webview/browser/aw_contents_statics.cc
@@ -10,6 +10,7 @@
 #include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
 #include "base/android/scoped_java_ref.h"
+#include "base/bind.h"
 #include "base/callback.h"
 #include "base/task/post_task.h"
 #include "components/google/core/common/google_util.h"
@@ -52,6 +53,14 @@
   Java_AwContentsStatics_safeBrowsingWhitelistAssigned(env, callback, success);
 }
 
+void ProxyOverrideChanged(const JavaRef<jobject>& callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  if (callback.is_null())
+    return;
+  JNIEnv* env = AttachCurrentThread();
+  Java_AwContentsStatics_proxyOverrideChanged(env, callback);
+}
+
 }  // namespace
 
 // static
@@ -138,23 +147,27 @@
     const JavaParamRef<jclass>&,
     const base::android::JavaParamRef<jstring>& jhost,
     jint port,
-    const base::android::JavaParamRef<jobjectArray>& jexclusion_list) {
+    const base::android::JavaParamRef<jobjectArray>& jexclusion_list,
+    const JavaParamRef<jobject>& callback) {
   std::string host;
   base::android::ConvertJavaStringToUTF8(env, jhost, &host);
   std::vector<std::string> exclusion_list;
   base::android::AppendJavaStringArrayToStringVector(env, jexclusion_list,
                                                      &exclusion_list);
-
   AwBrowserContext::GetDefault()->GetAwURLRequestContext()->SetProxyOverride(
-      host, port, exclusion_list);
+      host, port, exclusion_list,
+      base::BindOnce(&ProxyOverrideChanged,
+                     ScopedJavaGlobalRef<jobject>(env, callback)));
 }
 
 // static
-void JNI_AwContentsStatics_ClearProxyOverride(JNIEnv* env,
-                                              const JavaParamRef<jclass>&) {
-  AwBrowserContext::GetDefault()
-      ->GetAwURLRequestContext()
-      ->ClearProxyOverride();
+void JNI_AwContentsStatics_ClearProxyOverride(
+    JNIEnv* env,
+    const JavaParamRef<jclass>&,
+    const JavaParamRef<jobject>& callback) {
+  AwBrowserContext::GetDefault()->GetAwURLRequestContext()->ClearProxyOverride(
+      base::BindOnce(&ProxyOverrideChanged,
+                     ScopedJavaGlobalRef<jobject>(env, callback)));
 }
 
 }  // namespace android_webview
diff --git a/android_webview/browser/net/aw_url_request_context_getter.cc b/android_webview/browser/net/aw_url_request_context_getter.cc
index 34403eb..12b5ec0 100644
--- a/android_webview/browser/net/aw_url_request_context_getter.cc
+++ b/android_webview/browser/net/aw_url_request_context_getter.cc
@@ -415,15 +415,17 @@
 void AwURLRequestContextGetter::SetProxyOverride(
     const std::string& host,
     int port,
-    const std::vector<std::string>& exclusion_list) {
+    const std::vector<std::string>& exclusion_list,
+    base::OnceClosure callback) {
   if (proxy_config_service_android_ != NULL) {
-    proxy_config_service_android_->SetProxyOverride(host, port, exclusion_list);
+    proxy_config_service_android_->SetProxyOverride(host, port, exclusion_list,
+                                                    std::move(callback));
   }
 }
 
-void AwURLRequestContextGetter::ClearProxyOverride() {
+void AwURLRequestContextGetter::ClearProxyOverride(base::OnceClosure callback) {
   if (proxy_config_service_android_ != NULL) {
-    proxy_config_service_android_->ClearProxyOverride();
+    proxy_config_service_android_->ClearProxyOverride(std::move(callback));
   }
 }
 
diff --git a/android_webview/browser/net/aw_url_request_context_getter.h b/android_webview/browser/net/aw_url_request_context_getter.h
index d9ece21b..117bdba 100644
--- a/android_webview/browser/net/aw_url_request_context_getter.h
+++ b/android_webview/browser/net/aw_url_request_context_getter.h
@@ -54,8 +54,9 @@
   // Methods to set and clear proxy override
   void SetProxyOverride(const std::string& host,
                         int port,
-                        const std::vector<std::string>& exclusion_list);
-  void ClearProxyOverride();
+                        const std::vector<std::string>& exclusion_list,
+                        base::OnceClosure callback);
+  void ClearProxyOverride(base::OnceClosure callback);
 
  private:
   friend class AwBrowserContext;
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/SharedStatics.java b/android_webview/glue/java/src/com/android/webview/chromium/SharedStatics.java
index 7fe5fbb..8f0c2f2 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/SharedStatics.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/SharedStatics.java
@@ -118,12 +118,12 @@
                 () -> AwContentsStatics.getSafeBrowsingPrivacyPolicyUrl());
     }
 
-    public void setProxyOverride(String host, int port, String[] exclusionList) {
+    public void setProxyOverride(String host, int port, String[] exclusionList, Runnable callback) {
         ThreadUtils.runOnUiThread(
-                () -> AwContentsStatics.setProxyOverride(host, port, exclusionList));
+                () -> AwContentsStatics.setProxyOverride(host, port, exclusionList, callback));
     }
 
-    public void clearProxyOverride() {
-        ThreadUtils.runOnUiThread(() -> AwContentsStatics.clearProxyOverride());
+    public void clearProxyOverride(Runnable callback) {
+        ThreadUtils.runOnUiThread(() -> AwContentsStatics.clearProxyOverride(callback));
     }
 }
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContentsStatics.java b/android_webview/java/src/org/chromium/android_webview/AwContentsStatics.java
index 6a12a9c..8237019 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContentsStatics.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContentsStatics.java
@@ -120,12 +120,19 @@
         nativeSetCheckClearTextPermitted(permitted);
     }
 
-    public static void setProxyOverride(String host, int port, String[] exclusionList) {
-        nativeSetProxyOverride(host, port, exclusionList);
+    @CalledByNative
+    private static void proxyOverrideChanged(Runnable callback) {
+        if (callback == null) return;
+        callback.run();
     }
 
-    public static void clearProxyOverride() {
-        nativeClearProxyOverride();
+    public static void setProxyOverride(
+            String host, int port, String[] exclusionList, Runnable callback) {
+        nativeSetProxyOverride(host, port, exclusionList, callback);
+    }
+
+    public static void clearProxyOverride(Runnable callback) {
+        nativeClearProxyOverride(callback);
     }
 
     /**
@@ -155,6 +162,6 @@
             String[] urls, Callback<Boolean> callback);
     private static native void nativeSetCheckClearTextPermitted(boolean permitted);
     private static native void nativeSetProxyOverride(
-            String host, int port, String[] exclusionList);
-    private static native void nativeClearProxyOverride();
+            String host, int port, String[] exclusionList, Runnable callback);
+    private static native void nativeClearProxyOverride(Runnable callback);
 }
diff --git a/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/StaticsBoundaryInterface.java b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/StaticsBoundaryInterface.java
index b3b5aac..5511b39 100644
--- a/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/StaticsBoundaryInterface.java
+++ b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/StaticsBoundaryInterface.java
@@ -17,6 +17,6 @@
     void initSafeBrowsing(Context context, ValueCallback<Boolean> callback);
     void setSafeBrowsingWhitelist(List<String> hosts, ValueCallback<Boolean> callback);
     Uri getSafeBrowsingPrivacyPolicyUrl();
-    void setProxyOverride(String host, int port, String[] exclusionList);
-    void clearProxyOverride();
+    void setProxyOverride(String host, int port, String[] exclusionList, Runnable callback);
+    void clearProxyOverride(Runnable callback);
 }
diff --git a/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/util/Features.java b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/util/Features.java
index 705e0685..59ce1df 100644
--- a/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/util/Features.java
+++ b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/util/Features.java
@@ -127,7 +127,7 @@
 
     // WebViewCompat.setProxyOverride
     // WebViewCompat.clearProxyOverride
-    public static final String PROXY_OVERRIDE = "PROXY_OVERRIDE";
+    public static final String PROXY_OVERRIDE = "PROXY_OVERRIDE:2";
 
     // WebViewCompat.getWebViewRenderer
     public static final String GET_WEB_VIEW_RENDERER = "GET_WEB_VIEW_RENDERER";
diff --git a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebViewChromiumFactory.java b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebViewChromiumFactory.java
index aada5cd7..83793ffa 100644
--- a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebViewChromiumFactory.java
+++ b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebViewChromiumFactory.java
@@ -118,13 +118,14 @@
         }
 
         @Override
-        public void setProxyOverride(String host, int port, String[] exclusionList) {
-            mSharedStatics.setProxyOverride(host, port, exclusionList);
+        public void setProxyOverride(
+                String host, int port, String[] exclusionList, Runnable callback) {
+            mSharedStatics.setProxyOverride(host, port, exclusionList, callback);
         }
 
         @Override
-        public void clearProxyOverride() {
-            mSharedStatics.clearProxyOverride();
+        public void clearProxyOverride(Runnable callback) {
+            mSharedStatics.clearProxyOverride(callback);
         }
     }
 
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index d91626d..6adae32 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1190,6 +1190,8 @@
     "wm/overview/window_selector_delegate.h",
     "wm/overview/window_selector_item.cc",
     "wm/overview/window_selector_item.h",
+    "wm/pip/pip_window_resizer.cc",
+    "wm/pip/pip_window_resizer.h",
     "wm/property_util.cc",
     "wm/property_util.h",
     "wm/resize_shadow.cc",
@@ -1966,6 +1968,7 @@
     "wm/overlay_layout_manager_unittest.cc",
     "wm/overview/cleanup_animation_observer_unittest.cc",
     "wm/overview/window_selector_unittest.cc",
+    "wm/pip/pip_window_resizer_unittest.cc",
     "wm/resize_shadow_and_cursor_unittest.cc",
     "wm/root_window_layout_manager_unittest.cc",
     "wm/screen_dimmer_unittest.cc",
diff --git a/ash/wm/client_controlled_state.cc b/ash/wm/client_controlled_state.cc
index 8bfc26c..725b97d 100644
--- a/ash/wm/client_controlled_state.cc
+++ b/ash/wm/client_controlled_state.cc
@@ -68,7 +68,8 @@
     bool was_pinned = window_state->IsPinned();
     bool was_trusted_pinned = window_state->IsTrustedPinned();
 
-    EnterNextState(window_state, next_state_type, kAnimationCrossFade);
+    set_next_bounds_change_animation_type(kAnimationCrossFade);
+    EnterNextState(window_state, next_state_type);
 
     VLOG(1) << "Processing Pinned Transtion: event=" << event->type()
             << ", state=" << old_state_type << "=>" << next_state_type
@@ -193,20 +194,25 @@
     return;
   switch (event->type()) {
     case WM_EVENT_SET_BOUNDS: {
-      const gfx::Rect& bounds =
-          static_cast<const SetBoundsEvent*>(event)->requested_bounds();
+      const auto* set_bounds_event = static_cast<const SetBoundsEvent*>(event);
+      const gfx::Rect& bounds = set_bounds_event->requested_bounds();
       if (set_bounds_locally_) {
-        switch (bounds_change_animation_type_) {
+        switch (next_bounds_change_animation_type_) {
           case kAnimationNone:
             window_state->SetBoundsDirect(bounds);
             break;
           case kAnimationCrossFade:
             window_state->SetBoundsDirectCrossFade(bounds);
             break;
+          case kAnimationAnimated:
+            window_state->SetBoundsDirectAnimated(
+                bounds, bounds_change_animation_duration_);
+            break;
         }
-        bounds_change_animation_type_ = kAnimationNone;
+        next_bounds_change_animation_type_ = kAnimationNone;
       } else if (!window_state->IsPinned()) {
         // TODO(oshima): Define behavior for pinned app.
+        bounds_change_animation_duration_ = set_bounds_event->duration();
         delegate_->HandleBoundsRequest(window_state,
                                        window_state->GetStateType(), bounds);
       }
@@ -226,13 +232,11 @@
 
 bool ClientControlledState::EnterNextState(
     WindowState* window_state,
-    mojom::WindowStateType next_state_type,
-    BoundsChangeAnimationType animation_type) {
+    mojom::WindowStateType next_state_type) {
   // Do nothing if  we're already in the same state, or delegate has already
   // been deleted.
   if (state_type_ == next_state_type || !delegate_)
     return false;
-  bounds_change_animation_type_ = animation_type;
   mojom::WindowStateType previous_state_type = state_type_;
   state_type_ = next_state_type;
 
diff --git a/ash/wm/client_controlled_state.h b/ash/wm/client_controlled_state.h
index f298239..d1bf73c4 100644
--- a/ash/wm/client_controlled_state.h
+++ b/ash/wm/client_controlled_state.h
@@ -65,8 +65,16 @@
   enum BoundsChangeAnimationType {
     kAnimationNone,
     kAnimationCrossFade,
+    kAnimationAnimated,
   };
 
+  // Sets the type of animation for the next bounds change
+  // applied locally.
+  void set_next_bounds_change_animation_type(
+      BoundsChangeAnimationType animation_type) {
+    next_bounds_change_animation_type_ = animation_type;
+  }
+
   // WindowState::State:
   void AttachState(WindowState* window_state,
                    WindowState::State* previous_state) override;
@@ -86,18 +94,17 @@
   // Enters next state. This is used when the state moves from one to another
   // within the same desktop mode. Returns true if the state has changed, or
   // false otherwise.
-  // |animation_type| specifies the type of animation to be applied when
-  // bounds changes.
   bool EnterNextState(wm::WindowState* window_state,
-                      mojom::WindowStateType next_state_type,
-                      BoundsChangeAnimationType animation_type);
+                      mojom::WindowStateType next_state_type);
 
  private:
   std::unique_ptr<Delegate> delegate_;
 
   bool set_bounds_locally_ = false;
+  base::TimeDelta bounds_change_animation_duration_ =
+      WindowState::kBoundsChangeSlideDuration;
 
-  BoundsChangeAnimationType bounds_change_animation_type_ = kAnimationNone;
+  BoundsChangeAnimationType next_bounds_change_animation_type_ = kAnimationNone;
 
   DISALLOW_COPY_AND_ASSIGN(ClientControlledState);
 };
diff --git a/ash/wm/client_controlled_state_unittest.cc b/ash/wm/client_controlled_state_unittest.cc
index 808515a..43c52297 100644
--- a/ash/wm/client_controlled_state_unittest.cc
+++ b/ash/wm/client_controlled_state_unittest.cc
@@ -160,8 +160,7 @@
   EXPECT_EQ(mojom::WindowStateType::DEFAULT, delegate()->old_state());
   EXPECT_EQ(mojom::WindowStateType::MAXIMIZED, delegate()->new_state());
   // Now enters the new state.
-  state()->EnterNextState(window_state(), delegate()->new_state(),
-                          ClientControlledState::kAnimationNone);
+  state()->EnterNextState(window_state(), delegate()->new_state());
   EXPECT_TRUE(widget()->IsMaximized());
   // Bounds is controlled by client.
   EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
@@ -176,8 +175,7 @@
   EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
   EXPECT_EQ(mojom::WindowStateType::MAXIMIZED, delegate()->old_state());
   EXPECT_EQ(mojom::WindowStateType::NORMAL, delegate()->new_state());
-  state()->EnterNextState(window_state(), delegate()->new_state(),
-                          ClientControlledState::kAnimationNone);
+  state()->EnterNextState(window_state(), delegate()->new_state());
   EXPECT_FALSE(widget()->IsMaximized());
   EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
 }
@@ -188,8 +186,7 @@
   EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
   EXPECT_EQ(mojom::WindowStateType::DEFAULT, delegate()->old_state());
   EXPECT_EQ(mojom::WindowStateType::MINIMIZED, delegate()->new_state());
-  state()->EnterNextState(window_state(), delegate()->new_state(),
-                          ClientControlledState::kAnimationNone);
+  state()->EnterNextState(window_state(), delegate()->new_state());
   EXPECT_TRUE(widget()->IsMinimized());
   EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
 
@@ -198,8 +195,7 @@
   EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
   EXPECT_EQ(mojom::WindowStateType::MINIMIZED, delegate()->old_state());
   EXPECT_EQ(mojom::WindowStateType::NORMAL, delegate()->new_state());
-  state()->EnterNextState(window_state(), delegate()->new_state(),
-                          ClientControlledState::kAnimationNone);
+  state()->EnterNextState(window_state(), delegate()->new_state());
   EXPECT_FALSE(widget()->IsMinimized());
   EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
 
@@ -209,8 +205,7 @@
   EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
   EXPECT_EQ(mojom::WindowStateType::NORMAL, delegate()->old_state());
   EXPECT_EQ(mojom::WindowStateType::MINIMIZED, delegate()->new_state());
-  state()->EnterNextState(window_state(), delegate()->new_state(),
-                          ClientControlledState::kAnimationNone);
+  state()->EnterNextState(window_state(), delegate()->new_state());
   EXPECT_TRUE(widget()->IsMinimized());
   EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
 
@@ -222,8 +217,7 @@
   EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
   EXPECT_EQ(mojom::WindowStateType::MINIMIZED, delegate()->old_state());
   EXPECT_EQ(mojom::WindowStateType::NORMAL, delegate()->new_state());
-  state()->EnterNextState(window_state(), delegate()->new_state(),
-                          ClientControlledState::kAnimationNone);
+  state()->EnterNextState(window_state(), delegate()->new_state());
   EXPECT_FALSE(widget()->IsMinimized());
   EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
 }
@@ -234,8 +228,7 @@
   EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
   EXPECT_EQ(mojom::WindowStateType::DEFAULT, delegate()->old_state());
   EXPECT_EQ(mojom::WindowStateType::FULLSCREEN, delegate()->new_state());
-  state()->EnterNextState(window_state(), delegate()->new_state(),
-                          ClientControlledState::kAnimationNone);
+  state()->EnterNextState(window_state(), delegate()->new_state());
   EXPECT_TRUE(widget()->IsFullscreen());
   EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
 
@@ -243,8 +236,7 @@
   EXPECT_TRUE(widget()->IsFullscreen());
   EXPECT_EQ(mojom::WindowStateType::FULLSCREEN, delegate()->old_state());
   EXPECT_EQ(mojom::WindowStateType::NORMAL, delegate()->new_state());
-  state()->EnterNextState(window_state(), delegate()->new_state(),
-                          ClientControlledState::kAnimationNone);
+  state()->EnterNextState(window_state(), delegate()->new_state());
   EXPECT_FALSE(widget()->IsFullscreen());
   EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
 }
@@ -257,8 +249,7 @@
   EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
   EXPECT_EQ(mojom::WindowStateType::DEFAULT, delegate()->old_state());
   EXPECT_EQ(mojom::WindowStateType::MAXIMIZED, delegate()->new_state());
-  state()->EnterNextState(window_state(), delegate()->new_state(),
-                          ClientControlledState::kAnimationNone);
+  state()->EnterNextState(window_state(), delegate()->new_state());
   EXPECT_TRUE(widget()->IsMaximized());
   EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
 
@@ -267,8 +258,7 @@
   EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
   EXPECT_EQ(mojom::WindowStateType::MAXIMIZED, delegate()->old_state());
   EXPECT_EQ(mojom::WindowStateType::FULLSCREEN, delegate()->new_state());
-  state()->EnterNextState(window_state(), delegate()->new_state(),
-                          ClientControlledState::kAnimationNone);
+  state()->EnterNextState(window_state(), delegate()->new_state());
   EXPECT_TRUE(widget()->IsFullscreen());
   EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
 
@@ -276,8 +266,7 @@
   EXPECT_TRUE(widget()->IsFullscreen());
   EXPECT_EQ(mojom::WindowStateType::FULLSCREEN, delegate()->old_state());
   EXPECT_EQ(mojom::WindowStateType::MAXIMIZED, delegate()->new_state());
-  state()->EnterNextState(window_state(), delegate()->new_state(),
-                          ClientControlledState::kAnimationNone);
+  state()->EnterNextState(window_state(), delegate()->new_state());
   EXPECT_TRUE(widget()->IsMaximized());
   EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
 
@@ -285,16 +274,14 @@
   EXPECT_TRUE(widget()->IsMaximized());
   EXPECT_EQ(mojom::WindowStateType::MAXIMIZED, delegate()->old_state());
   EXPECT_EQ(mojom::WindowStateType::NORMAL, delegate()->new_state());
-  state()->EnterNextState(window_state(), delegate()->new_state(),
-                          ClientControlledState::kAnimationNone);
+  state()->EnterNextState(window_state(), delegate()->new_state());
   EXPECT_FALSE(widget()->IsMaximized());
   EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
 }
 
 TEST_F(ClientControlledStateTest, IgnoreWorkspace) {
   widget()->Maximize();
-  state()->EnterNextState(window_state(), delegate()->new_state(),
-                          ClientControlledState::kAnimationNone);
+  state()->EnterNextState(window_state(), delegate()->new_state());
   EXPECT_TRUE(widget()->IsMaximized());
   delegate()->Reset();
 
diff --git a/ash/wm/default_state.cc b/ash/wm/default_state.cc
index 757c4bd2..2a9bb471 100644
--- a/ash/wm/default_state.cc
+++ b/ash/wm/default_state.cc
@@ -392,7 +392,8 @@
     window_state->SetBoundsDirect(event->requested_bounds());
   } else if (!SetMaximizedOrFullscreenBounds(window_state)) {
     if (event->animate()) {
-      window_state->SetBoundsDirectAnimated(event->requested_bounds());
+      window_state->SetBoundsDirectAnimated(event->requested_bounds(),
+                                            event->duration());
     } else {
       window_state->SetBoundsConstrained(event->requested_bounds());
     }
diff --git a/ash/wm/pip/pip_window_resizer.cc b/ash/wm/pip/pip_window_resizer.cc
new file mode 100644
index 0000000..6967ddda
--- /dev/null
+++ b/ash/wm/pip/pip_window_resizer.cc
@@ -0,0 +1,56 @@
+// 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 "ash/wm/pip/pip_window_resizer.h"
+
+#include "ash/wm/window_util.h"
+#include "ui/aura/window.h"
+#include "ui/display/screen.h"
+#include "ui/wm/core/coordinate_conversion.h"
+
+namespace ash {
+
+PipWindowResizer::PipWindowResizer(wm::WindowState* window_state)
+    : WindowResizer(window_state) {
+  window_state->OnDragStarted(details().window_component);
+}
+
+PipWindowResizer::~PipWindowResizer() {}
+
+void PipWindowResizer::Drag(const gfx::Point& location_in_parent,
+                            int event_flags) {
+  last_location_in_screen_ = location_in_parent;
+  ::wm::ConvertPointToScreen(GetTarget()->parent(), &last_location_in_screen_);
+
+  gfx::Rect bounds = CalculateBoundsForDrag(location_in_parent);
+  display::Display display =
+      display::Screen::GetScreen()->GetDisplayNearestWindow(GetTarget());
+  gfx::Rect work_area = display.work_area();
+  bounds.AdjustToFit(work_area);
+
+  if (bounds != GetTarget()->bounds()) {
+    moved_or_resized_ = true;
+    GetTarget()->SetBounds(bounds);
+  }
+}
+
+void PipWindowResizer::CompleteDrag() {
+  window_state()->OnCompleteDrag(last_location_in_screen_);
+  window_state()->DeleteDragDetails();
+  window_state()->ClearRestoreBounds();
+  window_state()->set_bounds_changed_by_user(moved_or_resized_);
+}
+
+void PipWindowResizer::RevertDrag() {
+  // Handle cancel as a complete drag for pip. Having the PIP window
+  // go back to where it was on cancel looks strange, so instead just
+  // will just stop it where it is and animate to the edge of the screen.
+  CompleteDrag();
+}
+
+void PipWindowResizer::FlingOrSwipe(ui::GestureEvent* event) {
+  CompleteDrag();
+}
+
+}  // namespace ash
diff --git a/ash/wm/pip/pip_window_resizer.h b/ash/wm/pip/pip_window_resizer.h
new file mode 100644
index 0000000..6254289
--- /dev/null
+++ b/ash/wm/pip/pip_window_resizer.h
@@ -0,0 +1,47 @@
+// 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_WM_PIP_PIP_WINDOW_RESIZER_H_
+#define ASH_WM_PIP_PIP_WINDOW_RESIZER_H_
+
+#include <memory>
+
+#include "ash/ash_export.h"
+#include "ash/wm/window_resizer.h"
+#include "base/macros.h"
+#include "ui/display/display.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace ash {
+
+namespace wm {
+class WindowState;
+}  // namespace wm
+
+// Controls resizing for windows with the PIP window state type. This
+// includes things like snapping the PIP window to the edges of the work area
+// and handling swipe-to-dismiss.
+class ASH_EXPORT PipWindowResizer : public WindowResizer {
+ public:
+  explicit PipWindowResizer(wm::WindowState* window_state);
+  ~PipWindowResizer() override;
+
+  // WindowResizer:
+  void Drag(const gfx::Point& location_in_parent, int event_flags) override;
+  void CompleteDrag() override;
+  void RevertDrag() override;
+  void FlingOrSwipe(ui::GestureEvent* event) override;
+
+ private:
+  wm::WindowState* window_state() { return window_state_; }
+
+  gfx::Point last_location_in_screen_;
+  bool moved_or_resized_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(PipWindowResizer);
+};
+
+}  // namespace ash
+
+#endif  // ASH_WM_PIP_PIP_WINDOW_RESIZER_H_
diff --git a/ash/wm/pip/pip_window_resizer_unittest.cc b/ash/wm/pip/pip_window_resizer_unittest.cc
new file mode 100644
index 0000000..521365f
--- /dev/null
+++ b/ash/wm/pip/pip_window_resizer_unittest.cc
@@ -0,0 +1,148 @@
+// 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 "ash/wm/pip/pip_window_resizer.h"
+
+#include <string>
+
+#include "ash/shelf/shelf.h"
+#include "ash/shell.h"
+#include "ash/test/ash_test_base.h"
+#include "ash/wm/window_state.h"
+#include "ash/wm/wm_event.h"
+#include "ui/aura/window.h"
+#include "ui/base/hit_test.h"
+#include "ui/gfx/geometry/insets.h"
+
+namespace ash {
+namespace wm {
+
+namespace {
+
+// WindowState based on a given initial state. Records the last resize bounds.
+class FakeWindowState : public wm::WindowState::State {
+ public:
+  explicit FakeWindowState(mojom::WindowStateType initial_state_type)
+      : state_type_(initial_state_type) {}
+  ~FakeWindowState() override = default;
+
+  // WindowState::State overrides:
+  void OnWMEvent(wm::WindowState* window_state,
+                 const wm::WMEvent* event) override {
+    if (event->IsBoundsEvent()) {
+      if (event->type() == wm::WM_EVENT_SET_BOUNDS) {
+        const auto* set_bounds_event =
+            static_cast<const wm::SetBoundsEvent*>(event);
+        last_bounds_ = set_bounds_event->requested_bounds();
+      }
+    }
+  }
+  mojom::WindowStateType GetType() const override { return state_type_; }
+  void AttachState(wm::WindowState* window_state,
+                   wm::WindowState::State* previous_state) override {}
+  void DetachState(wm::WindowState* window_state) override {}
+
+  const gfx::Rect& last_bounds() { return last_bounds_; }
+
+ private:
+  mojom::WindowStateType state_type_;
+  gfx::Rect last_bounds_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeWindowState);
+};
+
+}  // namespace
+
+class PipWindowResizerTest : public AshTestBase {
+ public:
+  PipWindowResizerTest() = default;
+  ~PipWindowResizerTest() override = default;
+
+  void SetUp() override {
+    AshTestBase::SetUp();
+
+    window_ = CreateTestWindowInShellWithBounds(gfx::Rect(200, 200, 100, 100));
+    wm::WindowState* window_state = wm::GetWindowState(window_);
+    test_state_ = new FakeWindowState(mojom::WindowStateType::PIP);
+    window_state->SetStateObject(
+        std::unique_ptr<wm::WindowState::State>(test_state_));
+  }
+
+  void TearDown() override { AshTestBase::TearDown(); }
+
+ protected:
+  aura::Window* window() { return window_; }
+  FakeWindowState* test_state() { return test_state_; }
+
+  PipWindowResizer* CreateResizerForTest(int window_component) {
+    wm::WindowState* window_state = wm::GetWindowState(window());
+    window_state->CreateDragDetails(gfx::Point(), window_component,
+                                    ::wm::WINDOW_MOVE_SOURCE_MOUSE);
+    return new PipWindowResizer(window_state);
+  }
+
+  gfx::Point CalculateDragPoint(const WindowResizer& resizer,
+                                int delta_x,
+                                int delta_y) const {
+    gfx::Point location = resizer.GetInitialLocation();
+    location.set_x(location.x() + delta_x);
+    location.set_y(location.y() + delta_y);
+    return location;
+  }
+
+  void UpdateWorkArea(const std::string& bounds) {
+    UpdateDisplay(bounds);
+    aura::Window* root = Shell::GetPrimaryRootWindow();
+    Shell::Get()->SetDisplayWorkAreaInsets(root, gfx::Insets());
+  }
+
+ private:
+  aura::Window* window_;
+  FakeWindowState* test_state_;
+
+  DISALLOW_COPY_AND_ASSIGN(PipWindowResizerTest);
+};
+
+TEST_F(PipWindowResizerTest, PipWindowCanDrag) {
+  UpdateWorkArea("400x800");
+  std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
+  ASSERT_TRUE(resizer.get());
+
+  resizer->Drag(CalculateDragPoint(*resizer, 0, 10), 0);
+  EXPECT_EQ("200,210 100x100", test_state()->last_bounds().ToString());
+}
+
+TEST_F(PipWindowResizerTest, PipWindowCanResize) {
+  UpdateWorkArea("400x800");
+  std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTBOTTOM));
+  ASSERT_TRUE(resizer.get());
+
+  resizer->Drag(CalculateDragPoint(*resizer, 0, 10), 0);
+  EXPECT_EQ("200,200 100x110", test_state()->last_bounds().ToString());
+}
+
+TEST_F(PipWindowResizerTest, PipWindowDragIsRestrictedToWorkArea) {
+  UpdateWorkArea("400x400");
+  std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
+  ASSERT_TRUE(resizer.get());
+
+  // Drag to the right.
+  resizer->Drag(CalculateDragPoint(*resizer, 800, 0), 0);
+  EXPECT_EQ("300,200 100x100", test_state()->last_bounds().ToString());
+
+  // Drag down.
+  resizer->Drag(CalculateDragPoint(*resizer, 0, 800), 0);
+  EXPECT_EQ("200,300 100x100", test_state()->last_bounds().ToString());
+
+  // Drag to the left.
+  resizer->Drag(CalculateDragPoint(*resizer, -800, 0), 0);
+  EXPECT_EQ("0,200 100x100", test_state()->last_bounds().ToString());
+
+  // Drag up.
+  resizer->Drag(CalculateDragPoint(*resizer, 0, -800), 0);
+  EXPECT_EQ("200,0 100x100", test_state()->last_bounds().ToString());
+}
+
+}  // namespace wm
+}  // namespace ash
diff --git a/ash/wm/window_state.cc b/ash/wm/window_state.cc
index d31eec0..4144f49 100644
--- a/ash/wm/window_state.cc
+++ b/ash/wm/window_state.cc
@@ -146,6 +146,8 @@
 
 }  // namespace
 
+constexpr base::TimeDelta WindowState::kBoundsChangeSlideDuration;
+
 WindowState::~WindowState() {
   // WindowState is registered as an owned property of |window_|, and window
   // unregisters all of its observers in its d'tor before destroying its
@@ -621,15 +623,13 @@
   SetBoundsDirect(child_bounds);
 }
 
-void WindowState::SetBoundsDirectAnimated(const gfx::Rect& bounds) {
-  const int kBoundsChangeSlideDurationMs = 120;
-
+void WindowState::SetBoundsDirectAnimated(const gfx::Rect& bounds,
+                                          base::TimeDelta duration) {
   ui::Layer* layer = window_->layer();
   ui::ScopedLayerAnimationSettings slide_settings(layer->GetAnimator());
   slide_settings.SetPreemptionStrategy(
       ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
-  slide_settings.SetTransitionDuration(
-      base::TimeDelta::FromMilliseconds(kBoundsChangeSlideDurationMs));
+  slide_settings.SetTransitionDuration(duration);
   SetBoundsDirect(bounds);
 }
 
diff --git a/ash/wm/window_state.h b/ash/wm/window_state.h
index c471434..b709edc 100644
--- a/ash/wm/window_state.h
+++ b/ash/wm/window_state.h
@@ -61,6 +61,10 @@
 // accessing the window using |window()| is cheap.
 class ASH_EXPORT WindowState : public aura::WindowObserver {
  public:
+  // The default duration for an animation between two sets of bounds.
+  static constexpr base::TimeDelta kBoundsChangeSlideDuration =
+      base::TimeDelta::FromMilliseconds(120);
+
   // A subclass of State class represents one of the window's states
   // that corresponds to WindowStateType in Ash environment, e.g.
   // maximized, minimized or side snapped, as subclass.
@@ -391,8 +395,10 @@
   void SetBoundsConstrained(const gfx::Rect& bounds);
 
   // Sets the wndow's |bounds| and transitions to the new bounds with
-  // a scale animation.
-  void SetBoundsDirectAnimated(const gfx::Rect& bounds);
+  // a scale animation, with duration specified by |duration|.
+  void SetBoundsDirectAnimated(
+      const gfx::Rect& bounds,
+      base::TimeDelta duration = kBoundsChangeSlideDuration);
 
   // Sets the window's |bounds| and transition to the new bounds with
   // a cross fade animation.
diff --git a/ash/wm/wm_event.cc b/ash/wm/wm_event.cc
index 8df1a23..8ad7737 100644
--- a/ash/wm/wm_event.cc
+++ b/ash/wm/wm_event.cc
@@ -84,8 +84,12 @@
 
 SetBoundsEvent::SetBoundsEvent(WMEventType type,
                                const gfx::Rect& bounds,
-                               bool animate)
-    : WMEvent(type), requested_bounds_(bounds), animate_(animate) {}
+                               bool animate,
+                               base::TimeDelta duration)
+    : WMEvent(type),
+      requested_bounds_(bounds),
+      animate_(animate),
+      duration_(duration) {}
 
 SetBoundsEvent::~SetBoundsEvent() = default;
 
diff --git a/ash/wm/wm_event.h b/ash/wm/wm_event.h
index 7ed5d7c1..3b87ecc 100644
--- a/ash/wm/wm_event.h
+++ b/ash/wm/wm_event.h
@@ -6,7 +6,9 @@
 #define ASH_WM_WM_EVENT_H_
 
 #include "ash/ash_export.h"
+#include "ash/wm/window_state.h"
 #include "base/macros.h"
+#include "base/time/time.h"
 #include "ui/gfx/geometry/rect.h"
 
 namespace ash {
@@ -131,18 +133,23 @@
 // An WMEvent to request new bounds for the window.
 class ASH_EXPORT SetBoundsEvent : public WMEvent {
  public:
-  SetBoundsEvent(WMEventType type,
-                 const gfx::Rect& requested_bounds,
-                 bool animate = false);
+  SetBoundsEvent(
+      WMEventType type,
+      const gfx::Rect& requested_bounds,
+      bool animate = false,
+      base::TimeDelta duration = WindowState::kBoundsChangeSlideDuration);
   ~SetBoundsEvent() override;
 
   const gfx::Rect& requested_bounds() const { return requested_bounds_; }
 
   bool animate() const { return animate_; }
 
+  base::TimeDelta duration() const { return duration_; }
+
  private:
   gfx::Rect requested_bounds_;
   bool animate_;
+  base::TimeDelta duration_;
 
   DISALLOW_COPY_AND_ASSIGN(SetBoundsEvent);
 };
diff --git a/ash/wm/workspace/workspace_window_resizer.cc b/ash/wm/workspace/workspace_window_resizer.cc
index 215910c..a9bf314 100644
--- a/ash/wm/workspace/workspace_window_resizer.cc
+++ b/ash/wm/workspace/workspace_window_resizer.cc
@@ -17,6 +17,7 @@
 #include "ash/shell.h"
 #include "ash/wm/default_window_resizer.h"
 #include "ash/wm/drag_window_resizer.h"
+#include "ash/wm/pip/pip_window_resizer.h"
 #include "ash/wm/tablet_mode/tablet_mode_browser_window_drag_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "ash/wm/window_positioning_utils.h"
@@ -117,10 +118,15 @@
   // refactor and eliminate chaining.
   std::unique_ptr<WindowResizer> window_resizer;
 
+  if (window_state->IsPip()) {
+    window_state->CreateDragDetails(point_in_parent, window_component, source);
+    window_resizer = std::make_unique<PipWindowResizer>(window_state);
+    return window_resizer;
+  }
+
   if (Shell::Get()
           ->tablet_mode_controller()
-          ->IsTabletModeWindowManagerEnabled() &&
-      !window_state->IsPip()) {
+          ->IsTabletModeWindowManagerEnabled()) {
     if (!CanDragInTabletMode(window, window_component))
       return nullptr;
 
@@ -132,7 +138,7 @@
     return window_resizer;
   }
 
-  if (!window_state->IsNormalOrSnapped() && !window_state->IsPip())
+  if (!window_state->IsNormalOrSnapped())
     return nullptr;
 
   int bounds_change =
diff --git a/base/synchronization/atomic_flag.cc b/base/synchronization/atomic_flag.cc
index 5aed67f..a7ff0e7 100644
--- a/base/synchronization/atomic_flag.cc
+++ b/base/synchronization/atomic_flag.cc
@@ -16,17 +16,15 @@
   DETACH_FROM_SEQUENCE(set_sequence_checker_);
 }
 
+AtomicFlag::~AtomicFlag() = default;
+
 void AtomicFlag::Set() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(set_sequence_checker_);
-  base::subtle::Release_Store(&flag_, 1);
-}
-
-bool AtomicFlag::IsSet() const {
-  return base::subtle::Acquire_Load(&flag_) != 0;
+  flag_.store(1, std::memory_order_release);
 }
 
 void AtomicFlag::UnsafeResetForTesting() {
-  base::subtle::Release_Store(&flag_, 0);
+  flag_.store(0, std::memory_order_release);
 }
 
 }  // namespace base
diff --git a/base/synchronization/atomic_flag.h b/base/synchronization/atomic_flag.h
index a98a35c..f386a16 100644
--- a/base/synchronization/atomic_flag.h
+++ b/base/synchronization/atomic_flag.h
@@ -5,7 +5,10 @@
 #ifndef BASE_SYNCHRONIZATION_ATOMIC_FLAG_H_
 #define BASE_SYNCHRONIZATION_ATOMIC_FLAG_H_
 
-#include "base/atomicops.h"
+#include <stdint.h>
+
+#include <atomic>
+
 #include "base/base_export.h"
 #include "base/macros.h"
 #include "base/sequence_checker.h"
@@ -18,7 +21,7 @@
 class BASE_EXPORT AtomicFlag {
  public:
   AtomicFlag();
-  ~AtomicFlag() = default;
+  ~AtomicFlag();
 
   // Set the flag. Must always be called from the same sequence.
   void Set();
@@ -26,14 +29,17 @@
   // Returns true iff the flag was set. If this returns true, the current thread
   // is guaranteed to be synchronized with all memory operations on the sequence
   // which invoked Set() up until at least the first call to Set() on it.
-  bool IsSet() const;
+  bool IsSet() const {
+    // Inline here: this has a measurable performance impact on base::WeakPtr.
+    return flag_.load(std::memory_order_acquire) != 0;
+  }
 
   // Resets the flag. Be careful when using this: callers might not expect
   // IsSet() to return false after returning true once.
   void UnsafeResetForTesting();
 
  private:
-  base::subtle::Atomic32 flag_ = 0;
+  std::atomic<uint_fast8_t> flag_{0};
   SEQUENCE_CHECKER(set_sequence_checker_);
 
   DISALLOW_COPY_AND_ASSIGN(AtomicFlag);
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index d1c30fdd..d61e686b 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -154,6 +154,14 @@
   custom_package = "org.chromium.chrome.download"
 }
 
+android_resources("chrome_autofill_assistant_java_resources") {
+  resource_dirs = [ "//chrome/android/java/res_autofill_assistant" ]
+  deps = [
+    ":chrome_app_java_resources",
+  ]
+  custom_package = "org.chromium.chrome.autofill_assistant"
+}
+
 java_strings_grd("chrome_strings_grd") {
   defines = chrome_grit_defines
   grd_file = "java/strings/android_chrome_strings.grd"
@@ -223,6 +231,7 @@
 android_library("chrome_java") {
   deps = [
     ":chrome_app_java_resources",
+    ":chrome_autofill_assistant_java_resources",
     ":chrome_download_java_resources",
     ":chrome_public_android_manifest",
     ":chrome_public_apk_template_resources",
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedLoggingBridge.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedLoggingBridge.java
index 24d6cf4a..35bd1f7b 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedLoggingBridge.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedLoggingBridge.java
@@ -4,11 +4,15 @@
 
 package org.chromium.chrome.browser.feed;
 
+import com.google.android.libraries.feed.host.logging.ActionType;
 import com.google.android.libraries.feed.host.logging.BasicLoggingApi;
 import com.google.android.libraries.feed.host.logging.ContentLoggingData;
 
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.ui.mojom.WindowOpenDisposition;
+
+import java.util.concurrent.TimeUnit;
 
 /**
  * Implementation of {@link BasicLoggingApi} that log actions performed on the Feed,
@@ -39,34 +43,36 @@
     public void onContentViewed(ContentLoggingData data) {
         assert mNativeFeedLoggingBridge != 0;
         nativeOnContentViewed(mNativeFeedLoggingBridge, data.getPositionInStream(),
-                data.getPublishedTimeSeconds(), data.getTimeContentBecameAvailable(),
-                data.getScore());
+                TimeUnit.SECONDS.toMillis(data.getPublishedTimeSeconds()),
+                TimeUnit.SECONDS.toMillis(data.getTimeContentBecameAvailable()), data.getScore());
     }
 
     @Override
     public void onContentDismissed(ContentLoggingData data) {
         assert mNativeFeedLoggingBridge != 0;
-        nativeOnContentDismissed(mNativeFeedLoggingBridge, data.getRepresentationUri());
+        nativeOnContentDismissed(
+                mNativeFeedLoggingBridge, data.getPositionInStream(), data.getRepresentationUri());
     }
 
     @Override
     public void onContentClicked(ContentLoggingData data) {
         assert mNativeFeedLoggingBridge != 0;
         nativeOnContentClicked(mNativeFeedLoggingBridge, data.getPositionInStream(),
-                data.getPublishedTimeSeconds(), data.getScore());
+                TimeUnit.SECONDS.toMillis(data.getPublishedTimeSeconds()), data.getScore());
     }
 
     @Override
-    public void onClientAction(ContentLoggingData data, int actionType) {
+    public void onClientAction(ContentLoggingData data, @ActionType int actionType) {
         assert mNativeFeedLoggingBridge != 0;
-        nativeOnClientAction(mNativeFeedLoggingBridge, actionType);
+        nativeOnClientAction(
+                mNativeFeedLoggingBridge, feedActionToWindowOpenDisposition(actionType));
     }
 
     @Override
     public void onContentContextMenuOpened(ContentLoggingData data) {
         assert mNativeFeedLoggingBridge != 0;
         nativeOnContentContextMenuOpened(mNativeFeedLoggingBridge, data.getPositionInStream(),
-                data.getPublishedTimeSeconds(), data.getScore());
+                TimeUnit.SECONDS.toMillis(data.getPublishedTimeSeconds()), data.getScore());
     }
 
     @Override
@@ -119,16 +125,37 @@
         nativeOnOfflinePageVisited(mNativeFeedLoggingBridge, visitTimeMs);
     }
 
+    private int feedActionToWindowOpenDisposition(@ActionType int actionType) {
+        switch (actionType) {
+            case ActionType.OPEN_URL:
+                return WindowOpenDisposition.CURRENT_TAB;
+            case ActionType.OPEN_URL_INCOGNITO:
+                return WindowOpenDisposition.IGNORE_ACTION;
+            case ActionType.OPEN_URL_NEW_TAB:
+                return WindowOpenDisposition.NEW_BACKGROUND_TAB;
+            case ActionType.OPEN_URL_NEW_WINDOW:
+                return WindowOpenDisposition.NEW_WINDOW;
+            case ActionType.DOWNLOAD:
+                return WindowOpenDisposition.SAVE_TO_DISK;
+            case ActionType.LEARN_MORE:
+            case ActionType.UNKNOWN:
+            default:
+                return WindowOpenDisposition.UNKNOWN;
+        }
+    }
+
     private native long nativeInit(Profile profile);
     private native void nativeDestroy(long nativeFeedLoggingBridge);
     private native void nativeOnContentViewed(long nativeFeedLoggingBridge, int position,
-            long publishedTimeSeconds, long timeContentBecameAvailable, float score);
-    private native void nativeOnContentDismissed(long nativeFeedLoggingBridge, String uri);
+            long publishedTimeMs, long timeContentBecameAvailableMs, float score);
+    private native void nativeOnContentDismissed(
+            long nativeFeedLoggingBridge, int position, String uri);
     private native void nativeOnContentClicked(
-            long nativeFeedLoggingBridge, int position, long publishedTimeSeconds, float score);
-    private native void nativeOnClientAction(long nativeFeedLoggingBridge, int actionType);
+            long nativeFeedLoggingBridge, int position, long publishedTimeMs, float score);
+    private native void nativeOnClientAction(
+            long nativeFeedLoggingBridge, int windowOpenDisposition);
     private native void nativeOnContentContextMenuOpened(
-            long nativeFeedLoggingBridge, int position, long publishedTimeSeconds, float score);
+            long nativeFeedLoggingBridge, int position, long publishedTimeMs, float score);
     private native void nativeOnMoreButtonViewed(long nativeFeedLoggingBridge, int position);
     private native void nativeOnMoreButtonClicked(long nativeFeedLoggingBridge, int position);
     private native void nativeOnOpenedWithContent(
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java
index a07113ab..5faf2c6 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java
@@ -71,7 +71,6 @@
     // Used when Feed is enabled.
     private @Nullable Stream mStream;
     private @Nullable FeedImageLoader mImageLoader;
-    private @Nullable FeedLoggingBridge mLoggingBridge;
     private @Nullable StreamLifecycleManager mStreamLifecycleManager;
     private @Nullable SectionHeaderView mSectionHeaderView;
     private @Nullable MarginResizer mSectionHeaderViewMarginResizer;
@@ -256,7 +255,6 @@
         mMediator.destroy();
         if (mStreamLifecycleManager != null) mStreamLifecycleManager.destroy();
         if (mImageLoader != null) mImageLoader.destroy();
-        if (mLoggingBridge != null) mLoggingBridge.destroy();
         mTab.getWindowAndroid().removeContextMenuCloseListener(mContextMenuManager);
     }
 
@@ -306,13 +304,13 @@
         Profile profile = mTab.getProfile();
 
         mImageLoader = new FeedImageLoader(profile, activity);
-        mLoggingBridge = new FeedLoggingBridge(profile);
+        FeedLoggingBridge loggingBridge = FeedProcessScopeFactory.getFeedLoggingBridge();
         FeedOfflineIndicator offlineIndicator = FeedProcessScopeFactory.getFeedOfflineIndicator();
         Runnable consumptionObserver =
                 () -> FeedProcessScopeFactory.getFeedScheduler().onSuggestionConsumed();
         ActionApi actionApi = new FeedActionHandler(mNewTabPageManager.getNavigationDelegate(),
                 consumptionObserver, offlineIndicator, OfflinePageBridge.getForProfile(profile),
-                mLoggingBridge);
+                loggingBridge);
 
         FeedStreamScope streamScope =
                 feedProcessScope
@@ -320,7 +318,7 @@
                                 new BasicStreamConfiguration(),
                                 new BasicCardConfiguration(activity.getResources(), mUiConfig),
                                 new BasicSnackbarApi(mNewTabPageManager.getSnackbarManager()),
-                                mLoggingBridge, offlineIndicator,
+                                loggingBridge, offlineIndicator,
                                 (FeedAppLifecycleListener)
                                         feedProcessScope.getAppLifecycleListener())
                         .build();
@@ -368,8 +366,6 @@
             mStream = null;
             mImageLoader.destroy();
             mImageLoader = null;
-            mLoggingBridge.destroy();
-            mLoggingBridge = null;
             mSectionHeaderView = null;
             mSectionHeaderViewMarginResizer.detach();
             mSectionHeaderViewMarginResizer = null;
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedProcessScopeFactory.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedProcessScopeFactory.java
index 36038f0..8111f6d 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedProcessScopeFactory.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedProcessScopeFactory.java
@@ -34,6 +34,7 @@
     private static FeedScheduler sFeedScheduler;
     private static FeedOfflineIndicator sFeedOfflineIndicator;
     private static NetworkClient sTestNetworkClient;
+    private static FeedLoggingBridge sFeedLoggingBridge;
 
     /** @return The shared {@link FeedProcessScope} instance. Null if the Feed is disabled. */
     public static @Nullable FeedProcessScope getFeedProcessScope() {
@@ -72,6 +73,15 @@
         return sFeedAppLifecycle;
     }
 
+    /** @return The {@link FeedLoggingBridge} that was given to the {@link FeedStreamScope}. Null if
+     * the Feed is disabled. */
+    public static @Nullable FeedLoggingBridge getFeedLoggingBridge() {
+        if (sFeedLoggingBridge == null) {
+            initialize();
+        }
+        return sFeedLoggingBridge;
+    }
+
     /**
      * @return Whether the dependencies provided by this class are allowed to be created. The feed
      *         process is disabled if supervised user or enterprise policy has once been added
@@ -84,7 +94,7 @@
 
     private static void initialize() {
         assert sFeedProcessScope == null && sFeedScheduler == null && sFeedOfflineIndicator == null
-                && sFeedAppLifecycle == null;
+                && sFeedAppLifecycle == null && sFeedLoggingBridge == null;
         if (!isFeedProcessEnabled()) return;
 
         sPrefChangeRegistrar = new PrefChangeRegistrar();
@@ -107,6 +117,7 @@
         FeedJournalStorage journalStorage = new FeedJournalStorage(profile);
         NetworkClient networkClient = sTestNetworkClient == null ?
             new FeedNetworkBridge(profile) : sTestNetworkClient;
+        sFeedLoggingBridge = new FeedLoggingBridge(profile);
         sFeedProcessScope = new FeedProcessScope
                                     .Builder(configHostApi, Executors.newSingleThreadExecutor(),
                                             new LoggingApiImpl(), networkClient, schedulerBridge,
@@ -202,5 +213,9 @@
             sFeedAppLifecycle.destroy();
             sFeedAppLifecycle = null;
         }
+        if (sFeedLoggingBridge != null) {
+            sFeedLoggingBridge.destroy();
+            sFeedLoggingBridge = null;
+        }
     }
 }
diff --git a/chrome/android/java/res_autofill_assistant/drawable-hdpi/autofill_assistant_bottombar_bg.9.png b/chrome/android/java/res_autofill_assistant/drawable-hdpi/autofill_assistant_bottombar_bg.9.png
new file mode 100644
index 0000000..606060d7
--- /dev/null
+++ b/chrome/android/java/res_autofill_assistant/drawable-hdpi/autofill_assistant_bottombar_bg.9.png
Binary files differ
diff --git a/chrome/android/java/res_autofill_assistant/drawable-mdpi/autofill_assistant_bottombar_bg.9.png b/chrome/android/java/res_autofill_assistant/drawable-mdpi/autofill_assistant_bottombar_bg.9.png
new file mode 100644
index 0000000..6d12e19
--- /dev/null
+++ b/chrome/android/java/res_autofill_assistant/drawable-mdpi/autofill_assistant_bottombar_bg.9.png
Binary files differ
diff --git a/chrome/android/java/res_autofill_assistant/drawable-xhdpi/autofill_assistant_bottombar_bg.9.png b/chrome/android/java/res_autofill_assistant/drawable-xhdpi/autofill_assistant_bottombar_bg.9.png
new file mode 100644
index 0000000..cbba344
--- /dev/null
+++ b/chrome/android/java/res_autofill_assistant/drawable-xhdpi/autofill_assistant_bottombar_bg.9.png
Binary files differ
diff --git a/chrome/android/java/res_autofill_assistant/drawable-xxhdpi/autofill_assistant_bottombar_bg.9.png b/chrome/android/java/res_autofill_assistant/drawable-xxhdpi/autofill_assistant_bottombar_bg.9.png
new file mode 100644
index 0000000..f0ccbfea
--- /dev/null
+++ b/chrome/android/java/res_autofill_assistant/drawable-xxhdpi/autofill_assistant_bottombar_bg.9.png
Binary files differ
diff --git a/chrome/android/java/res_autofill_assistant/drawable-xxxhdpi/autofill_assistant_bottombar_bg.9.png b/chrome/android/java/res_autofill_assistant/drawable-xxxhdpi/autofill_assistant_bottombar_bg.9.png
new file mode 100644
index 0000000..a286cb21
--- /dev/null
+++ b/chrome/android/java/res_autofill_assistant/drawable-xxxhdpi/autofill_assistant_bottombar_bg.9.png
Binary files differ
diff --git a/chrome/android/java/res_autofill_assistant/drawable/autofill_assistant_chip_bg.xml b/chrome/android/java/res_autofill_assistant/drawable/autofill_assistant_chip_bg.xml
new file mode 100644
index 0000000..86aa4759
--- /dev/null
+++ b/chrome/android/java/res_autofill_assistant/drawable/autofill_assistant_chip_bg.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2018 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+  <item
+      android:state_pressed="true"
+      android:drawable="@drawable/autofill_assistant_chip_bg_pressed" />
+  <item
+      android:drawable="@drawable/autofill_assistant_chip_bg_normal" />
+</selector>
diff --git a/chrome/android/java/res_autofill_assistant/drawable/autofill_assistant_chip_bg_normal.xml b/chrome/android/java/res_autofill_assistant/drawable/autofill_assistant_chip_bg_normal.xml
new file mode 100644
index 0000000..b3c0ba1
--- /dev/null
+++ b/chrome/android/java/res_autofill_assistant/drawable/autofill_assistant_chip_bg_normal.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2018 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+<shape
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+  <corners
+      android:radius="20dp" />
+  <solid
+      android:color="@color/white_mode_tint" />
+  <stroke
+      android:width="1dp"
+      android:color="@color/modern_grey_300" />
+</shape>
diff --git a/chrome/android/java/res_autofill_assistant/drawable/autofill_assistant_chip_bg_pressed.xml b/chrome/android/java/res_autofill_assistant/drawable/autofill_assistant_chip_bg_pressed.xml
new file mode 100644
index 0000000..1bdf15ea
--- /dev/null
+++ b/chrome/android/java/res_autofill_assistant/drawable/autofill_assistant_chip_bg_pressed.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2018 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+<shape
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+  <corners
+      android:radius="20dp" />
+  <solid
+      android:color="@color/modern_grey_300" />
+</shape>
diff --git a/chrome/android/java/res/layout/autofill_assistant_chip.xml b/chrome/android/java/res_autofill_assistant/layout/autofill_assistant_chip.xml
similarity index 81%
rename from chrome/android/java/res/layout/autofill_assistant_chip.xml
rename to chrome/android/java/res_autofill_assistant/layout/autofill_assistant_chip.xml
index 1ae075380..ea7c4f9 100644
--- a/chrome/android/java/res/layout/autofill_assistant_chip.xml
+++ b/chrome/android/java/res_autofill_assistant/layout/autofill_assistant_chip.xml
@@ -8,7 +8,8 @@
     android:layout_height="wrap_content"
     android:paddingStart="16dp"
     android:paddingEnd="16dp"
+    android:textAppearance="@style/BlackBodyDefault"
     android:gravity="center_vertical"
     android:minHeight="40dp"
     android:singleLine="true"
-    android:background="@drawable/chip_bg" />
+    android:background="@drawable/autofill_assistant_chip_bg" />
diff --git a/chrome/android/java/res/layout/autofill_assistant_sheet.xml b/chrome/android/java/res_autofill_assistant/layout/autofill_assistant_sheet.xml
similarity index 76%
rename from chrome/android/java/res/layout/autofill_assistant_sheet.xml
rename to chrome/android/java/res_autofill_assistant/layout/autofill_assistant_sheet.xml
index c81f1134..535f5ace 100644
--- a/chrome/android/java/res/layout/autofill_assistant_sheet.xml
+++ b/chrome/android/java/res_autofill_assistant/layout/autofill_assistant_sheet.xml
@@ -15,24 +15,12 @@
         android:id="@+id/overlay"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
+        android:background="@color/white_alpha_90"
         android:gravity="center"
         android:clickable="true"
         android:visibility="gone"
         android:focusable="false"
         android:orientation="vertical">
-
-        <ProgressBar
-            style="@style/Widget.AppCompat.ProgressBar.Horizontal"
-            android:layout_width="match_parent"
-            android:layout_height="16dp"
-            android:indeterminate="true" />
-
-        <TextView
-            android:id="@+id/overlay_text"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="8dp"
-            android:gravity="center_horizontal" />
     </LinearLayout>
 
     <LinearLayout
@@ -40,16 +28,17 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_gravity="bottom"
-        android:background="@drawable/popup_bg_bottom"
+        android:background="@drawable/autofill_assistant_bottombar_bg"
         android:orientation="vertical">
 
         <LinearLayout
             android:layout_width="match_parent"
             android:layout_height="56dp"
-            android:layout_marginTop="8dp"
+            android:layout_marginTop="16dp"
             android:layout_marginStart="24dp"
             android:layout_marginEnd="24dp"
-            android:orientation="horizontal">
+            android:orientation="horizontal"
+            android:gravity="center_vertical">
             <android.support.v7.widget.AppCompatImageView
                 android:layout_width="24dp"
                 android:layout_height="24dp"
@@ -59,10 +48,13 @@
                 android:id="@+id/status_message"
                 android:layout_width="0dp"
                 android:layout_height="match_parent"
-                android:layout_gravity="center_vertical"
+                android:gravity="center_vertical"
                 android:paddingStart="24dp"
-                android:maxLines="1"
-                android:layout_weight="1.0"/>
+                android:paddingEnd="24dp"
+                android:textAppearance="@style/BlackTitle2"
+                android:layout_weight="1.0"
+                android:maxLines="2"
+                android:ellipsize="end"/>
 
             <ImageButton
                 android:id="@+id/feedback_button"
@@ -87,7 +79,9 @@
         <RelativeLayout
             android:id="@+id/details"
             android:layout_width="match_parent"
-            android:layout_height="60dp"
+            android:layout_height="@dimen/autofill_assistant_details_image_size"
+            android:layout_marginStart="24dp"
+            android:layout_marginEnd="24dp"
             android:layout_marginBottom="8dp"
             android:visibility="gone">
             <TextView
@@ -96,7 +90,11 @@
                 android:layout_height="wrap_content"
                 android:layout_alignParentTop="true"
                 android:layout_alignParentStart="true"
-                android:layout_toStartOf="@+id/details_image"/>
+                android:layout_toStartOf="@+id/details_image"
+                android:gravity="center_vertical"
+                android:textAppearance="@style/BlackTitle2"
+                android:maxLines="1"
+                android:ellipsize="end"/>
             <TextView
                 android:id="@+id/details_text"
                 android:layout_width="wrap_content"
@@ -105,14 +103,20 @@
                 android:layout_above="@+id/details_time"
                 android:layout_alignParentStart="true"
                 android:layout_toStartOf="@+id/details_image"
-                android:gravity="center_vertical" />
+                android:gravity="center_vertical"
+                android:textAppearance="@style/BlackBody"
+                android:maxLines="1"
+                android:ellipsize="end"/>
             <TextView
                 android:id="@+id/details_time"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_alignParentBottom="true"
                 android:layout_alignParentStart="true"
-                android:layout_toStartOf="@+id/details_image"/>
+                android:gravity="center_vertical"
+                android:textAppearance="@style/TextAppearance.AutofillAssistantDetailsTime"
+                android:maxLines="1"
+                android:ellipsize="end"/>
             <android.support.v7.widget.AppCompatImageView
                 android:id="@+id/details_image"
                 android:layout_width="@dimen/autofill_assistant_details_image_size"
@@ -132,10 +136,10 @@
                 android:id="@+id/carousel"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:layout_marginTop="8dp"
-                android:layout_marginBottom="8dp"
-                android:layout_marginStart="24dp"
-                android:layout_marginEnd="24dp"
+                android:paddingTop="8dp"
+                android:paddingBottom="8dp"
+                android:paddingStart="24dp"
+                android:paddingEnd="24dp"
                 android:gravity="center_vertical"
                 android:orientation="horizontal">
             </LinearLayout>
diff --git a/chrome/android/java/res_autofill_assistant/values-v17/styles.xml b/chrome/android/java/res_autofill_assistant/values-v17/styles.xml
new file mode 100644
index 0000000..41c86a2b
--- /dev/null
+++ b/chrome/android/java/res_autofill_assistant/values-v17/styles.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2018 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<resources xmlns:tools="http://schemas.android.com/tools">
+    <!-- Q: Why put style resources under values-v17/ ?
+         A: Problem:
+            1. paddingStart causes a crash on Galaxy Tab&Note b/8351339.
+            2. So we wrote a build script(generate_v14_compatible_resources.py) to convert
+               paddingStart to paddingLeft for pre-v17 (crbug.com/235118).
+            3. However, style files are not overridden by the corresponding generated style files,
+               but merged when we pass them to aapt unlike layout files.
+
+            So we decided to keep style resources under values-v17/ so that it is not merged with
+            the generated style resources under res_v14_compatibility/values/ (crbug.com/243952).
+    -->
+
+    <style
+        name="TextAppearance.AutofillAssistantDetailsTime"
+        parent="@style/BlackBodyDefault">
+        <item name="android:textColor">@color/google_green_700</item>
+    </style>
+</resources>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiDelegate.java
index f9e7432..c996577 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiDelegate.java
@@ -14,6 +14,7 @@
 import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
 import android.support.v7.widget.AppCompatImageView;
 import android.text.TextUtils;
+import android.util.TypedValue;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -21,7 +22,7 @@
 import android.widget.TextView;
 
 import org.chromium.base.Promise;
-import org.chromium.chrome.R;
+import org.chromium.chrome.autofill_assistant.R;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
@@ -223,7 +224,7 @@
      * @param scriptHandles List of scripts to show.
      */
     public void updateScripts(ArrayList<ScriptHandle> scriptHandles) {
-        mChipsViewContainer.removeAllViews();
+        clearChipsViewContainer();
 
         if (scriptHandles.isEmpty()) {
             return;
@@ -233,18 +234,33 @@
             ScriptHandle scriptHandle = scriptHandles.get(i);
             TextView chipView = createChipView(scriptHandle.getName());
             chipView.setOnClickListener((unusedView) -> {
-                mChipsViewContainer.removeAllViews();
+                clearChipsViewContainer();
                 mClient.onScriptSelected(scriptHandle.getPath());
             });
-            mChipsViewContainer.addView(chipView);
+            addChipViewToContainer(chipView);
         }
 
         ensureFullContainerIsShown();
     }
 
+    private void addChipViewToContainer(TextView newChild) {
+        // Add a left margin if it's not the first child.
+        if (mChipsViewContainer.getChildCount() > 0) {
+            LinearLayout.LayoutParams layoutParams =
+                    new LinearLayout.LayoutParams(newChild.getLayoutParams());
+            int leftMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8,
+                    newChild.getContext().getResources().getDisplayMetrics());
+            layoutParams.setMargins(leftMargin, 0, 0, 0);
+            newChild.setLayoutParams(layoutParams);
+        }
+
+        mChipsViewContainer.addView(newChild);
+        mChipsViewContainer.setVisibility(View.VISIBLE);
+    }
+
     private TextView createChipView(String text) {
         TextView chipView = (TextView) (LayoutInflater.from(mActivity).inflate(
-                R.layout.autofill_assistant_chip, null /* root */));
+                R.layout.autofill_assistant_chip, mChipsViewContainer, false));
         chipView.setText(text);
         return chipView;
     }
@@ -269,7 +285,7 @@
         mDetailsText.setText(getDetailsText(details));
         mDetailsTime.setText(getDetailsTime(details.getDate()));
 
-        mDetailsImage.setVisibility(View.GONE);
+        mDetailsImage.setVisibility(View.INVISIBLE);
         mDetails.setVisibility(View.VISIBLE);
         ensureFullContainerIsShown();
 
@@ -332,22 +348,27 @@
             return;
         }
 
-        mChipsViewContainer.removeAllViews();
+        clearChipsViewContainer();
 
         for (int i = 0; i < profiles.size(); i++) {
             AutofillProfile profile = profiles.get(i);
             // TODO(crbug.com/806868): Show more information than the street.
             TextView chipView = createChipView(profile.getStreetAddress());
             chipView.setOnClickListener((unusedView) -> {
-                mChipsViewContainer.removeAllViews();
+                clearChipsViewContainer();
                 mClient.onAddressSelected(profile.getGUID());
             });
-            mChipsViewContainer.addView(chipView);
+            addChipViewToContainer(chipView);
         }
 
         ensureFullContainerIsShown();
     }
 
+    private void clearChipsViewContainer() {
+        mChipsViewContainer.removeAllViews();
+        mChipsViewContainer.setVisibility(View.GONE);
+    }
+
     /**
      * Show credit cards in the bar.
      *
@@ -359,17 +380,17 @@
             return;
         }
 
-        mChipsViewContainer.removeAllViews();
+        clearChipsViewContainer();
 
         for (int i = 0; i < cards.size(); i++) {
             CreditCard card = cards.get(i);
             // TODO(crbug.com/806868): Show more information than the card number.
             TextView chipView = createChipView(card.getObfuscatedNumber());
             chipView.setOnClickListener((unusedView) -> {
-                mChipsViewContainer.removeAllViews();
+                clearChipsViewContainer();
                 mClient.onCardSelected(card.getGUID());
             });
-            mChipsViewContainer.addView(chipView);
+            addChipViewToContainer(chipView);
         }
 
         ensureFullContainerIsShown();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImpl.java
index 8122c9d..1b06bfe 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImpl.java
@@ -28,6 +28,7 @@
 import org.chromium.base.ApplicationStatus;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.PathUtils;
+import org.chromium.base.StrictModeContext;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.metrics.RecordUserAction;
@@ -342,7 +343,9 @@
      */
     public static boolean isPackageSpecializedHandler(String packageName, Intent intent) {
         Context context = ContextUtils.getApplicationContext();
-        try {
+        // On certain Samsung devices, queryIntentActivities can trigger a
+        // StrictModeDiskReadViolation (https://crbug.com/894160).
+        try (StrictModeContext unused = StrictModeContext.allowDiskReads()){
             List<ResolveInfo> handlers = context.getPackageManager().queryIntentActivities(
                     intent, PackageManager.GET_RESOLVED_FILTER);
             return getSpecializedHandlersWithFilter(handlers, packageName, intent).size() > 0;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataPreferences.java
index da99bfc..5af33e4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataPreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataPreferences.java
@@ -22,12 +22,14 @@
 import android.widget.Button;
 import android.widget.ListView;
 
+import org.chromium.base.CollectionUtil;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.browsing_data.BrowsingDataType;
 import org.chromium.chrome.browser.browsing_data.ClearBrowsingDataTab;
+import org.chromium.chrome.browser.browsing_data.CookieOrCacheDeletionChoice;
 import org.chromium.chrome.browser.browsing_data.TimePeriod;
 import org.chromium.chrome.browser.historyreport.AppIndexingReporter;
 import org.chromium.chrome.browser.multiwindow.MultiWindowUtils;
@@ -42,6 +44,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -280,8 +283,8 @@
     /**
      * @return All available {@link DialogOption} entries.
      */
-    protected final static ArraySet<Integer> getAllOptions() {
-        ArraySet<Integer> all = new ArraySet<>();
+    protected final static Set<Integer> getAllOptions() {
+        Set<Integer> all = new ArraySet<>();
         for (@DialogOption int i = DialogOption.CLEAR_HISTORY; i < DialogOption.NUM_ENTRIES; i++) {
             all.add(i);
         }
@@ -293,11 +296,10 @@
      * @param options The set of selected {@link DialogOption} entries.
      * @return int[] List of {@link BrowsingDataType} that should be deleted.
      */
-    protected int[] getDataTypesFromOptions(ArraySet<Integer> options) {
-        int[] dataTypes = new int[options.size()];
-        int i = 0;
+    protected Set<Integer> getDataTypesFromOptions(Set<Integer> options) {
+        Set<Integer> dataTypes = new ArraySet<>();
         for (@DialogOption Integer option : options) {
-            dataTypes[i++] = getDataType(option);
+            dataTypes.add(getDataType(option));
         }
         return dataTypes;
     }
@@ -305,8 +307,8 @@
     /**
      * @return The currently selected {@link DialogOption} entries.
      */
-    protected final ArraySet<Integer> getSelectedOptions() {
-        ArraySet<Integer> selected = new ArraySet<>();
+    protected final Set<Integer> getSelectedOptions() {
+        Set<Integer> selected = new ArraySet<>();
         for (Item item : mItems) {
             if (item.isSelected()) selected.add(item.getOption());
         }
@@ -334,26 +336,41 @@
      * Requests the browsing data corresponding to the given dialog options to be deleted.
      * @param options The dialog options whose corresponding data should be deleted.
      */
-    private void clearBrowsingData(ArraySet<Integer> options, @Nullable String[] blacklistedDomains,
+    private void clearBrowsingData(Set<Integer> options, @Nullable String[] blacklistedDomains,
             @Nullable int[] blacklistedDomainReasons, @Nullable String[] ignoredDomains,
             @Nullable int[] ignoredDomainReasons) {
         onClearBrowsingData();
         showProgressDialog();
+        Set<Integer> dataTypes = getDataTypesFromOptions(options);
 
         RecordHistogram.recordMediumTimesHistogram("History.ClearBrowsingData.TimeSpentInDialog",
                 SystemClock.elapsedRealtime() - mDialogOpened, TimeUnit.MILLISECONDS);
 
-        int[] dataTypes = getDataTypesFromOptions(options);
+        final @CookieOrCacheDeletionChoice int choice;
+        if (dataTypes.contains(BrowsingDataType.COOKIES)) {
+            choice = dataTypes.contains(BrowsingDataType.CACHE)
+                    ? CookieOrCacheDeletionChoice.BOTH_COOKIES_AND_CACHE
+                    : CookieOrCacheDeletionChoice.ONLY_COOKIES;
+        } else {
+            choice = dataTypes.contains(BrowsingDataType.CACHE)
+                    ? CookieOrCacheDeletionChoice.ONLY_CACHE
+                    : CookieOrCacheDeletionChoice.NEITHER_COOKIES_NOR_CACHE;
+        }
+        RecordHistogram.recordEnumeratedHistogram(
+                "History.ClearBrowsingData.UserDeletedCookieOrCacheFromDialog", choice,
+                CookieOrCacheDeletionChoice.MAX_CHOICE_VALUE);
 
         Object spinnerSelection =
                 ((SpinnerPreference) findPreference(PREF_TIME_RANGE)).getSelectedOption();
         int timePeriod = ((TimePeriodSpinnerOption) spinnerSelection).getTimePeriod();
+        // TODO(bsazonov): Change integerListToIntArray to handle Collection<Integer>.
+        int[] dataTypesArray = CollectionUtil.integerListToIntArray(new ArrayList<>(dataTypes));
         if (blacklistedDomains != null && blacklistedDomains.length != 0) {
-            BrowsingDataBridge.getInstance().clearBrowsingDataExcludingDomains(this, dataTypes,
+            BrowsingDataBridge.getInstance().clearBrowsingDataExcludingDomains(this, dataTypesArray,
                     timePeriod, blacklistedDomains, blacklistedDomainReasons, ignoredDomains,
                     ignoredDomainReasons);
         } else {
-            BrowsingDataBridge.getInstance().clearBrowsingData(this, dataTypes, timePeriod);
+            BrowsingDataBridge.getInstance().clearBrowsingData(this, dataTypesArray, timePeriod);
         }
 
         // Clear all reported entities.
@@ -454,7 +471,7 @@
      * </ol>
      */
     private boolean shouldShowImportantSitesDialog() {
-        ArraySet<Integer> selectedOptions = getSelectedOptions();
+        Set<Integer> selectedOptions = getSelectedOptions();
         if (!selectedOptions.contains(DialogOption.CLEAR_CACHE)
                 && !selectedOptions.contains(DialogOption.CLEAR_COOKIES_AND_SITE_DATA)) {
             return false;
@@ -555,7 +572,7 @@
 
         // Not all checkboxes defined in the layout are necessarily handled by this class
         // or a particular subclass. Hide those that are not.
-        ArraySet<Integer> unboundOptions = getAllOptions();
+        Set<Integer> unboundOptions = getAllOptions();
         unboundOptions.removeAll(options);
         for (@DialogOption Integer option : unboundOptions) {
             getPreferenceScreen().removePreference(findPreference(getPreferenceKey(option)));
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataPreferencesBasic.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataPreferencesBasic.java
index 66446b9d..4082ad1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataPreferencesBasic.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataPreferencesBasic.java
@@ -5,7 +5,6 @@
 package org.chromium.chrome.browser.preferences.privacy;
 
 import android.os.Bundle;
-import android.support.v4.util.ArraySet;
 
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordUserAction;
@@ -23,6 +22,7 @@
 
 import java.util.Arrays;
 import java.util.List;
+import java.util.Set;
 
 /**
  * A simpler version of {@link ClearBrowsingDataPreferences} with fewer dialog options and more
@@ -76,20 +76,13 @@
     }
 
     @Override
-    protected int[] getDataTypesFromOptions(ArraySet<Integer> options) {
-        int[] dataTypes;
-        int i = 0;
+    protected Set<Integer> getDataTypesFromOptions(Set<Integer> options) {
+        Set<Integer> dataTypes = super.getDataTypesFromOptions(options);
         if (options.contains(DialogOption.CLEAR_COOKIES_AND_SITE_DATA)) {
             // COOKIES checkbox includes MEDIA_LICENSES, which need to be
             // specified separately. This is only done for the basic tab.
             // On the advanced tab Media Licenses has its own checkbox.
-            dataTypes = new int[options.size() + 1];
-            dataTypes[i++] = BrowsingDataType.MEDIA_LICENSES;
-        } else {
-            dataTypes = new int[options.size()];
-        }
-        for (Integer option : options) {
-            dataTypes[i++] = getDataType(option);
+            dataTypes.add(BrowsingDataType.MEDIA_LICENSES);
         }
         return dataTypes;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsObserver.java
index b689894..257b746 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsObserver.java
@@ -69,7 +69,10 @@
     private WebContentsObserver mObserver;
 
     public static void from(Tab tab) {
-        tab.getUserDataHost().setUserData(USER_DATA_KEY, new TabWebContentsObserver(tab));
+        TabWebContentsObserver observer = get(tab);
+        if (observer == null) {
+            tab.getUserDataHost().setUserData(USER_DATA_KEY, new TabWebContentsObserver(tab));
+        }
     }
 
     @VisibleForTesting
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataPreferencesTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataPreferencesTest.java
index 09c7d481..1db9f72 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataPreferencesTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataPreferencesTest.java
@@ -66,6 +66,7 @@
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Integration tests for ClearBrowsingDataPreferences.
@@ -624,7 +625,7 @@
         }
     }
 
-    private void setDataTypesToClear(final ArraySet<Integer> typesToClear) {
+    private void setDataTypesToClear(final Set<Integer> typesToClear) {
         ThreadUtils.runOnUiThreadBlocking(() -> {
             for (@DialogOption Integer option : ClearBrowsingDataPreferences.getAllOptions()) {
                 boolean enabled = typesToClear.contains(option);
diff --git a/chrome/android/webapk/shell_apk/current_version/current_version.gni b/chrome/android/webapk/shell_apk/current_version/current_version.gni
index 0b56bd0..066e59bc 100644
--- a/chrome/android/webapk/shell_apk/current_version/current_version.gni
+++ b/chrome/android/webapk/shell_apk/current_version/current_version.gni
@@ -12,4 +12,4 @@
 # //chrome/android/webapk/shell_apk:webapk is changed. This includes
 # Java files, Android resource files and AndroidManifest.xml. Does not affect
 # Chrome.apk
-current_shell_apk_version = 63
+current_shell_apk_version = 64
diff --git a/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/HostBrowserUtils.java b/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/HostBrowserUtils.java
index fcfa6e11..c76a74b 100644
--- a/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/HostBrowserUtils.java
+++ b/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/HostBrowserUtils.java
@@ -32,12 +32,12 @@
     private static final String TAG = "cr_HostBrowserUtils";
 
     /**
-     * The package names of the channels of Chrome that support WebAPKs. The most preferred one
-     * comes first.
+     * The package names of the browsers that support WebAPKs. The most preferred one comes first.
      */
-    private static List<String> sBrowsersSupportingWebApk = new ArrayList<String>(
-            Arrays.asList("com.google.android.apps.chrome", "com.android.chrome", "com.chrome.beta",
-                    "com.chrome.dev", "com.chrome.canary", "org.chromium.chrome"));
+    private static List<String> sBrowsersSupportingWebApk =
+            new ArrayList<String>(Arrays.asList("com.google.android.apps.chrome",
+                    "com.android.chrome", "com.chrome.beta", "com.chrome.dev", "com.chrome.canary",
+                    "org.chromium.chrome", "org.chromium.arc.intent_helper"));
 
     /** Caches the package name of the host browser. */
     private static String sHostPackage;
diff --git a/chrome/app/chrome_command_ids.h b/chrome/app/chrome_command_ids.h
index 713ac2d0..101b0f89 100644
--- a/chrome/app/chrome_command_ids.h
+++ b/chrome/app/chrome_command_ids.h
@@ -151,9 +151,6 @@
 #define IDC_SHOW_KEYBOARD_OVERLAY       40027
 #define IDC_PROFILING_ENABLED           40028
 #define IDC_BOOKMARKS_MENU              40029
-// TODO(atwilson): Remove IDC_SHOW_SYNC_SETUP when we officially allow signin
-// when sync is disabled.
-#define IDC_SHOW_SYNC_SETUP             40030
 #define IDC_SHOW_SIGNIN                 40030
 #define IDC_EXTENSION_ERRORS            40031
 #define IDC_SHOW_SIGNIN_ERROR           40032
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index 9881509..d43d0ddb 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -691,9 +691,6 @@
   <message name="IDS_DISCOVER_PIN_SETUP_SUBTITLE1" desc="A sub-title of the dialog that starts 'PIN-unlock' setup process. Subtitle explains to the user how this feature can be configured.">
     Use a number (PIN) instead of a password to unlock your device. To set your PIN later, go to Settings.
   </message>
-  <message name="IDS_DISCOVER_PIN_SETUP_LEARN_MORE" desc="A label on the link that leads to the help page.">
-    Learn more
-  </message>
   <message name="IDS_DISCOVER_PIN_SETUP_SKIP" desc="A label on the button that interrupts current setup flow.">
     Skip
   </message>
@@ -704,7 +701,19 @@
     Your PIN is added
   </message>
   <message name="IDS_DISCOVER_PIN_SETUP_SUBTITLE3" desc="A sub-title of the dialog that finishes 'PIN-unlock' setup process successfully suggesting how user can use the PIN that they have just set up.">
-    You can use PIN to unlock your device when signed out
+    You can use PIN to unlock your device when you signed out.
+  </message>
+  <message name="IDS_DISCOVER_PIN_SETUP" desc="A label on the button that leads to Pin Unlock feature set up.">
+    Pin setup
+  </message>
+  <message name="IDS_DISCOVER_PIN_SETUP_PASSWORD_TITLE" desc="A title of the dialog that starts 'PIN-unlock' setup process, and asks user to type their password first.">
+    Confirm your password
+  </message>
+  <message name="IDS_DISCOVER_PIN_SETUP_PASSWORD_SUBTITLE" desc="A sub-title of the dialog that starts 'PIN-unlock' setup process, and asks user to type their password first.">
+    Enter your password to configure screen lock and sign-in
+  </message>
+  <message name="IDS_DISCOVER_PIN_SETUP_DONE" desc="Label for the done button on the success screen in 'PIN-unlock' setup dialog.">
+    Done
   </message>
   <message name="IDS_APP_START_NETWORK_WAIT_MESSAGE" desc="Message displayed while installing and/or launching web application in kiosk mode.">
     Waiting for network connection...
diff --git a/chrome/app/chromeos_strings_grdp/IDS_DISCOVER_PIN_SETUP.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_DISCOVER_PIN_SETUP.png.sha1
new file mode 100644
index 0000000..d1ceca3
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_DISCOVER_PIN_SETUP.png.sha1
@@ -0,0 +1 @@
+0bcf70651e715c2a6ee25f6087dd5ca7d06d0f36
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_DISCOVER_PIN_SETUP_DONE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_DISCOVER_PIN_SETUP_DONE.png.sha1
new file mode 100644
index 0000000..6e9a3fb
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_DISCOVER_PIN_SETUP_DONE.png.sha1
@@ -0,0 +1 @@
+b9f29ad0e5b2f9da1b79b53b881ccf9ff1b2ba81
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_DISCOVER_PIN_SETUP_PASSWORD_SUBTITLE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_DISCOVER_PIN_SETUP_PASSWORD_SUBTITLE.png.sha1
new file mode 100644
index 0000000..d499c53
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_DISCOVER_PIN_SETUP_PASSWORD_SUBTITLE.png.sha1
@@ -0,0 +1 @@
+f2f12b480baa8b7015852593f61af93711541436
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_DISCOVER_PIN_SETUP_PASSWORD_TITLE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_DISCOVER_PIN_SETUP_PASSWORD_TITLE.png.sha1
new file mode 100644
index 0000000..d499c53
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_DISCOVER_PIN_SETUP_PASSWORD_TITLE.png.sha1
@@ -0,0 +1 @@
+f2f12b480baa8b7015852593f61af93711541436
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_DISCOVER_PIN_SETUP_SUBTITLE3.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_DISCOVER_PIN_SETUP_SUBTITLE3.png.sha1
index 05dc333..6e9a3fb 100644
--- a/chrome/app/chromeos_strings_grdp/IDS_DISCOVER_PIN_SETUP_SUBTITLE3.png.sha1
+++ b/chrome/app/chromeos_strings_grdp/IDS_DISCOVER_PIN_SETUP_SUBTITLE3.png.sha1
@@ -1 +1 @@
-71a9d2779ab9039cccc94f1791ea4141b6f16387
\ No newline at end of file
+b9f29ad0e5b2f9da1b79b53b881ccf9ff1b2ba81
\ No newline at end of file
diff --git a/chrome/app/file_manager_strings.grdp b/chrome/app/file_manager_strings.grdp
index 649da9d..f4848de 100644
--- a/chrome/app/file_manager_strings.grdp
+++ b/chrome/app/file_manager_strings.grdp
@@ -513,6 +513,9 @@
   <message name="IDS_FILE_BROWSER_SHARE_WITH_LINUX_BUTTON_LABEL" desc="Label for menu that will share a folder with the crostini Linux container.">
     Share with Linux
   </message>
+  <message name="IDS_FILE_BROWSER_MANAGE_LINUX_SHARING_BUTTON_LABEL" desc="Label for menu that will take users to the settings page where they can manage which files and folders are shared with the crostini Linux container.">
+    Manage Linux sharing
+  </message>
   <message name="IDS_FILE_BROWSER_TOGGLE_HIDDEN_FILES_COMMAND_LABEL" desc="Label for menu or button with checkmark that toggles visibility of hidden files.">
     Show hidden files
   </message>
diff --git a/chrome/app/file_manager_strings_grdp/IDS_FILE_BROWSER_MANAGE_LINUX_SHARING_BUTTON_LABEL.png.sha1 b/chrome/app/file_manager_strings_grdp/IDS_FILE_BROWSER_MANAGE_LINUX_SHARING_BUTTON_LABEL.png.sha1
new file mode 100644
index 0000000..caf2eb1
--- /dev/null
+++ b/chrome/app/file_manager_strings_grdp/IDS_FILE_BROWSER_MANAGE_LINUX_SHARING_BUTTON_LABEL.png.sha1
@@ -0,0 +1 @@
+129ee36bc8d092098adb2dd669278d7010b109a7
\ No newline at end of file
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index bb7bbbd..e1aa088 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -5563,6 +5563,9 @@
       <message name="IDS_PICTURE_IN_PICTURE_CLOSE_CONTROL_TEXT" desc="Text label of the close control button. The button appears when the user hovers over the Picture-in-Picture window.">
         Close
       </message>
+      <message name="IDS_PICTURE_IN_PICTURE_RESIZE_HANDLE_TEXT" desc="Text label of the resize handle. The button appears when the user hovers over the Picture-in-Picture window.">
+        Resize
+      </message>
       <message name="IDS_PICTURE_IN_PICTURE_PLAY_PAUSE_CONTROL_ACCESSIBLE_TEXT" desc="Accessible text label used for the controls button in the Picture-in-Picture window. The button toggles between play and pause controls.">
         Toggle video to play or pause
       </message>
diff --git a/chrome/app/vector_icons/BUILD.gn b/chrome/app/vector_icons/BUILD.gn
index 5fc11590..cb203c62 100644
--- a/chrome/app/vector_icons/BUILD.gn
+++ b/chrome/app/vector_icons/BUILD.gn
@@ -79,6 +79,7 @@
     "reload_touch.icon",
     "remove.icon",
     "remove_box.icon",
+    "resize_handle.icon",
     "sad_tab.icon",
     "security.icon",
     "sensors.icon",
diff --git a/chrome/app/vector_icons/resize_handle.icon b/chrome/app/vector_icons/resize_handle.icon
new file mode 100644
index 0000000..3865d96
--- /dev/null
+++ b/chrome/app/vector_icons/resize_handle.icon
@@ -0,0 +1,11 @@
+// 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.
+
+CANVAS_DIMENSIONS, 15,
+STROKE, 2.f,
+MOVE_TO, 2, 2,
+R_LINE_TO, 11.31f, 11.31f,
+MOVE_TO, 9, 2,
+R_LINE_TO, 4.24f, 4.24f,
+CLOSE
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 8e95359..a0d0dd38 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -367,6 +367,8 @@
     "data_use_measurement/chrome_data_use_ascriber_service.h",
     "data_use_measurement/chrome_data_use_ascriber_service_factory.cc",
     "data_use_measurement/chrome_data_use_ascriber_service_factory.h",
+    "data_use_measurement/chrome_data_use_measurement.cc",
+    "data_use_measurement/chrome_data_use_measurement.h",
     "data_use_measurement/chrome_data_use_recorder.cc",
     "data_use_measurement/chrome_data_use_recorder.h",
     "data_use_measurement/data_use_web_contents_observer.cc",
diff --git a/chrome/browser/android/feed/feed_logging_bridge.cc b/chrome/browser/android/feed/feed_logging_bridge.cc
index 5937cfb7..22f8430 100644
--- a/chrome/browser/android/feed/feed_logging_bridge.cc
+++ b/chrome/browser/android/feed/feed_logging_bridge.cc
@@ -6,6 +6,8 @@
 
 #include <jni.h>
 
+#include "base/android/jni_string.h"
+#include "base/time/time.h"
 #include "chrome/browser/android/feed/feed_host_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_android.h"
@@ -38,54 +40,77 @@
 
 FeedLoggingBridge::~FeedLoggingBridge() = default;
 
-void FeedLoggingBridge::Destroy(JNIEnv* env, const JavaRef<jobject>& j_this) {
+void FeedLoggingBridge::Destroy(JNIEnv* j_env, const JavaRef<jobject>& j_this) {
   delete this;
 }
 
 void FeedLoggingBridge::OnContentViewed(
     JNIEnv* j_env,
     const base::android::JavaRef<jobject>& j_this,
-    const jint position,
-    const jlong publishedTimeSeconds,
-    const jlong timeContentBecameAvailable,
-    const jfloat score) {}
+    const jint j_position,
+    const jlong j_publishedTimeSeconds,
+    const jlong j_timeContentBecameAvailableSeconds,
+    const jfloat j_score) {
+  feed_logging_metrics_->OnSuggestionShown(
+      j_position, base::Time::FromJavaTime(j_publishedTimeSeconds), j_score,
+      base::Time::FromJavaTime(j_timeContentBecameAvailableSeconds));
+}
 
 void FeedLoggingBridge::OnContentDismissed(
     JNIEnv* j_env,
     const base::android::JavaRef<jobject>& j_this,
-    const base::android::JavaRef<jstring>& j_url) {}
+    const jint j_position,
+    const base::android::JavaRef<jstring>& j_url) {
+  feed_logging_metrics_->OnSuggestionDismissed(
+      j_position, GURL(ConvertJavaStringToUTF8(j_env, j_url)));
+}
 
 void FeedLoggingBridge::OnContentClicked(
     JNIEnv* j_env,
     const base::android::JavaRef<jobject>& j_this,
-    const jint position,
-    const jlong publishedTimeSeconds,
-    const jfloat score) {}
+    const jint j_position,
+    const jlong j_publishedTimeSeconds,
+    const jfloat j_score) {
+  feed_logging_metrics_->OnSuggestionOpened(
+      j_position, base::Time::FromJavaTime(j_publishedTimeSeconds), j_score);
+}
 
 void FeedLoggingBridge::OnClientAction(
     JNIEnv* j_env,
     const base::android::JavaRef<jobject>& j_this,
-    const jint j_action_type) {}
+    const jint j_window_open_disposition) {
+  feed_logging_metrics_->OnSuggestionWindowOpened(
+      static_cast<WindowOpenDisposition>(j_window_open_disposition));
+}
 
 void FeedLoggingBridge::OnContentContextMenuOpened(
     JNIEnv* j_env,
     const base::android::JavaRef<jobject>& j_this,
-    const jint position,
-    const jlong publishedTimeSeconds,
-    const jfloat score) {}
+    const jint j_position,
+    const jlong j_publishedTimeSeconds,
+    const jfloat j_score) {
+  feed_logging_metrics_->OnSuggestionMenuOpened(
+      j_position, base::Time::FromJavaTime(j_publishedTimeSeconds), j_score);
+}
 
 void FeedLoggingBridge::OnMoreButtonViewed(JNIEnv* j_env,
                                            const JavaRef<jobject>& j_this,
-                                           const jint j_position) {}
+                                           const jint j_position) {
+  feed_logging_metrics_->OnMoreButtonShown(j_position);
+}
 
 void FeedLoggingBridge::OnMoreButtonClicked(JNIEnv* j_env,
                                             const JavaRef<jobject>& j_this,
-                                            const jint j_position) {}
+                                            const jint j_position) {
+  feed_logging_metrics_->OnMoreButtonClicked(j_position);
+}
 
 void FeedLoggingBridge::OnOpenedWithContent(JNIEnv* j_env,
                                             const JavaRef<jobject>& j_this,
                                             const jint j_time_to_Populate,
-                                            const jint j_content_count) {}
+                                            const jint j_content_count) {
+  feed_logging_metrics_->OnPageShown(j_content_count);
+}
 
 void FeedLoggingBridge::OnOpenedWithNoImmediateContent(
     JNIEnv* j_env,
@@ -98,11 +123,17 @@
 void FeedLoggingBridge::OnContentTargetVisited(
     JNIEnv* j_env,
     const base::android::JavaRef<jobject>& j_this,
-    const jlong visit_time) {}
+    const jlong visit_time_ms) {
+  feed_logging_metrics_->OnSuggestionArticleVisited(
+      base::TimeDelta::FromMilliseconds(visit_time_ms));
+}
 
 void FeedLoggingBridge::OnOfflinePageVisited(
     JNIEnv* j_env,
     const base::android::JavaRef<jobject>& j_this,
-    const jlong visit_time) {}
+    const jlong visit_time_ms) {
+  feed_logging_metrics_->OnSuggestionOfflinePageVisited(
+      base::TimeDelta::FromMilliseconds(visit_time_ms));
+}
 
 }  // namespace feed
diff --git a/chrome/browser/android/feed/feed_logging_bridge.h b/chrome/browser/android/feed/feed_logging_bridge.h
index e06ea226..f0ee72f4 100644
--- a/chrome/browser/android/feed/feed_logging_bridge.h
+++ b/chrome/browser/android/feed/feed_logging_bridge.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_ANDROID_FEED_FEED_LOGGING_BRIDGE_H_
 #define CHROME_BROWSER_ANDROID_FEED_FEED_LOGGING_BRIDGE_H_
 
+#include <utility>
+
 #include "base/android/scoped_java_ref.h"
 
 namespace feed {
@@ -23,30 +25,31 @@
 
   void OnContentViewed(JNIEnv* j_env,
                        const base::android::JavaRef<jobject>& j_this,
-                       const jint position,
-                       const jlong publishedTimeSeconds,
-                       const jlong timeContentBecameAvailable,
-                       const jfloat score);
+                       const jint j_position,
+                       const jlong j_publishedTimeMs,
+                       const jlong j_timeContentBecameAvailableMs,
+                       const jfloat j_score);
 
   void OnContentDismissed(JNIEnv* j_env,
                           const base::android::JavaRef<jobject>& j_this,
+                          const jint j_position,
                           const base::android::JavaRef<jstring>& j_url);
 
   void OnContentClicked(JNIEnv* j_env,
                         const base::android::JavaRef<jobject>& j_this,
-                        const jint position,
-                        const jlong publishedTimeSeconds,
-                        const jfloat score);
+                        const jint j_position,
+                        const jlong j_publishedTimeMs,
+                        const jfloat j_score);
 
   void OnClientAction(JNIEnv* j_env,
                       const base::android::JavaRef<jobject>& j_this,
-                      const jint j_action_type);
+                      const jint j_window_open_disposition);
 
   void OnContentContextMenuOpened(JNIEnv* j_env,
                                   const base::android::JavaRef<jobject>& j_this,
-                                  const jint position,
-                                  const jlong publishedTimeSeconds,
-                                  const jfloat score);
+                                  const jint j_position,
+                                  const jlong j_publishedTimeMs,
+                                  const jfloat j_score);
 
   void OnMoreButtonViewed(JNIEnv* j_env,
                           const base::android::JavaRef<jobject>& j_this,
@@ -70,11 +73,11 @@
 
   void OnContentTargetVisited(JNIEnv* j_env,
                               const base::android::JavaRef<jobject>& j_this,
-                              const jlong visit_time);
+                              const jlong visit_time_ms);
 
   void OnOfflinePageVisited(JNIEnv* j_env,
                             const base::android::JavaRef<jobject>& j_this,
-                            const jlong visit_time);
+                            const jlong visit_time_ms);
 
  private:
   FeedLoggingMetrics* feed_logging_metrics_;
diff --git a/chrome/browser/browser_process_platform_part_chromeos.cc b/chrome/browser/browser_process_platform_part_chromeos.cc
index 57e325b..1a557bb 100644
--- a/chrome/browser/browser_process_platform_part_chromeos.cc
+++ b/chrome/browser/browser_process_platform_part_chromeos.cc
@@ -27,7 +27,6 @@
 #include "chrome/browser/chromeos/system/timezone_util.h"
 #include "chrome/browser/component_updater/cros_component_installer_chromeos.h"
 #include "chrome/browser/component_updater/metadata_table_chromeos.h"
-#include "chrome/browser/ui/webui/chromeos/login/discover/discover_manager.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_switches.h"
 #include "chromeos/account_manager/account_manager_factory.h"
@@ -176,13 +175,6 @@
   return timezone_resolver_.get();
 }
 
-chromeos::DiscoverManager* BrowserProcessPlatformPart::GetDiscoverManager() {
-  if (!discover_manager_.get())
-    discover_manager_ = std::make_unique<chromeos::DiscoverManager>();
-
-  return discover_manager_.get();
-}
-
 void BrowserProcessPlatformPart::StartTearDown() {
   // interactive_ui_tests check for memory leaks before this object is
   // destroyed.  So we need to destroy |timezone_resolver_| here.
diff --git a/chrome/browser/browser_process_platform_part_chromeos.h b/chrome/browser/browser_process_platform_part_chromeos.h
index b2f616d..77c2d5c9 100644
--- a/chrome/browser/browser_process_platform_part_chromeos.h
+++ b/chrome/browser/browser_process_platform_part_chromeos.h
@@ -19,7 +19,6 @@
 class AccountManagerFactory;
 class ChromeSessionManager;
 class ChromeUserManager;
-class DiscoverManager;
 class ProfileHelper;
 class TimeZoneResolver;
 
@@ -105,8 +104,6 @@
 
   chromeos::TimeZoneResolver* GetTimezoneResolver();
 
-  chromeos::DiscoverManager* GetDiscoverManager();
-
   // Overridden from BrowserProcessPlatformPartBase:
   void StartTearDown() override;
   std::unique_ptr<policy::ChromeBrowserPolicyConnector>
@@ -163,8 +160,6 @@
       input_device_controller_client_;
 #endif
 
-  std::unique_ptr<chromeos::DiscoverManager> discover_manager_;
-
   SEQUENCE_CHECKER(sequence_checker_);
 
   DISALLOW_COPY_AND_ASSIGN(BrowserProcessPlatformPart);
diff --git a/chrome/browser/browsing_data/counters/browsing_data_counter_utils_browsertest.cc b/chrome/browser/browsing_data/counters/browsing_data_counter_utils_browsertest.cc
new file mode 100644
index 0000000..eb4983e
--- /dev/null
+++ b/chrome/browser/browsing_data/counters/browsing_data_counter_utils_browsertest.cc
@@ -0,0 +1,126 @@
+// 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/browsing_data/counters/browsing_data_counter_utils.h"
+
+#include "build/buildflag.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
+#include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
+#include "chrome/browser/sync/test/integration/sync_test.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/unified_consent/unified_consent_service_factory.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "components/browser_sync/profile_sync_service.h"
+#include "components/signin/core/browser/signin_buildflags.h"
+#include "components/sync/test/fake_server/fake_server_network_resources.h"
+#include "components/unified_consent/feature.h"
+#include "components/unified_consent/scoped_unified_consent.h"
+
+#if BUILDFLAG(ENABLE_DICE_SUPPORT) || defined(OS_CHROMEOS)
+#include "chrome/browser/signin/scoped_account_consistency.h"
+#endif
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/signin/signin_manager_factory.h"
+#include "components/signin/core/browser/signin_manager_base.h"
+#endif
+
+namespace {
+
+class BrowsingDataCounterUtilsBrowserTest
+    : public SyncTest,
+      public testing::WithParamInterface<bool> {
+ public:
+  BrowsingDataCounterUtilsBrowserTest()
+      : SyncTest(SINGLE_CLIENT),
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+        scoped_dice_(GetParam()
+                         ? std::make_unique<ScopedAccountConsistencyDice>()
+                         : nullptr),
+#elif defined(OS_CHROMEOS)
+        scoped_mirror_(std::make_unique<ScopedAccountConsistencyMirror>()),
+#endif
+        scoped_unified_consent_(
+            GetParam()
+                ? unified_consent::UnifiedConsentFeatureState::kEnabledNoBump
+                : unified_consent::UnifiedConsentFeatureState::kDisabled) {
+  }
+  ~BrowsingDataCounterUtilsBrowserTest() override = default;
+
+ private:
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+  // ScopedAccountConsistencyDice is required for unified consent to be enabled.
+  const std::unique_ptr<ScopedAccountConsistencyDice> scoped_dice_;
+#elif defined(OS_CHROMEOS)
+  // Need to manually turn on mirror for now.
+  const std::unique_ptr<ScopedAccountConsistencyMirror> scoped_mirror_;
+#endif
+  const unified_consent::ScopedUnifiedConsent scoped_unified_consent_;
+
+  DISALLOW_COPY_AND_ASSIGN(BrowsingDataCounterUtilsBrowserTest);
+};
+
+// Instantiate test for unified consent disabled & enabled.
+INSTANTIATE_TEST_CASE_P(,
+                        BrowsingDataCounterUtilsBrowserTest,
+                        ::testing::Bool());
+
+IN_PROC_BROWSER_TEST_P(BrowsingDataCounterUtilsBrowserTest,
+                       ShouldShowCookieException) {
+  Profile* profile = browser()->profile();
+
+  browser_sync::ProfileSyncService* sync_service =
+      ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile);
+
+  sync_service->OverrideNetworkResourcesForTest(
+      std::make_unique<fake_server::FakeServerNetworkResources>(
+          GetFakeServer()->AsWeakPtr()));
+
+  std::string username;
+#if defined(OS_CHROMEOS)
+  // In browser tests, the profile may already by authenticated with stub
+  // account |user_manager::kStubUserEmail|.
+  AccountInfo info = SigninManagerFactory::GetForProfile(profile)
+                         ->GetAuthenticatedAccountInfo();
+  username = info.email;
+#endif
+  if (username.empty())
+    username = "user@gmail.com";
+
+  std::unique_ptr<ProfileSyncServiceHarness> harness =
+      ProfileSyncServiceHarness::Create(
+          profile, username, "unused" /* password */,
+          ProfileSyncServiceHarness::SigninType::FAKE_SIGNIN);
+
+#if defined(OS_CHROMEOS)
+  // On Chrome OS, the profile is always authenticated.
+  EXPECT_TRUE(ShouldShowCookieException(profile));
+#else
+  // By default, a fresh profile is not signed in, nor syncing, so no cookie
+  // exception should be shown.
+  EXPECT_FALSE(ShouldShowCookieException(profile));
+
+  // Sign the profile in.
+  EXPECT_TRUE(harness->SignInPrimaryAccount());
+
+  // Sign-in alone shouldn't lead to a cookie exception.
+  EXPECT_FALSE(ShouldShowCookieException(profile));
+#endif
+
+  // Enable sync.
+  EXPECT_TRUE(harness->SetupSync());
+
+  // Now that we're syncing, we should offer to retain the cookie.
+  EXPECT_TRUE(ShouldShowCookieException(profile));
+
+#if !defined(OS_CHROMEOS)
+  // Pause sync.
+  harness->SignOutPrimaryAccount();
+
+  // There's no point in showing the cookie exception.
+  EXPECT_FALSE(ShouldShowCookieException(profile));
+#endif  // !defined(OS_CHROMEOS)
+}
+
+}  // namespace
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
index 5b2417a..005eae6 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
@@ -107,6 +107,7 @@
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/task_manager/task_manager_interface.h"
 #include "chrome/browser/ui/ash/chrome_keyboard_controller_client.h"
+#include "chrome/browser/ui/webui/chromeos/login/discover/discover_manager.h"
 #include "chrome/browser/upgrade_detector/upgrade_detector_chromeos.h"
 #include "chrome/common/channel_info.h"
 #include "chrome/common/chrome_constants.h"
@@ -118,7 +119,6 @@
 #include "chromeos/accelerometer/accelerometer_reader.h"
 #include "chromeos/audio/audio_devices_pref_handler_impl.h"
 #include "chromeos/audio/cras_audio_handler.h"
-#include "chromeos/chromeos_paths.h"
 #include "chromeos/chromeos_switches.h"
 #include "chromeos/components/drivefs/fake_drivefs_launcher_client.h"
 #include "chromeos/cryptohome/async_method_caller.h"
@@ -267,22 +267,6 @@
 #endif
 }
 
-void RegisterStubPathOverridesIfNecessary() {
-  // These overrides need to occur before BrowserPolicyConnectorChromeOS
-  // (for one) is created. The DCHECK ensures that is the case.
-  DCHECK(!g_browser_process);
-
-  base::FilePath user_data_dir;
-  if (base::SysInfo::IsRunningOnChromeOS() ||
-      !base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir)) {
-    return;
-  }
-
-  // Override some paths with stub locations so that cloud policy and enterprise
-  // enrollment work on desktop builds, for ease of development.
-  chromeos::RegisterStubPathOverrides(user_data_dir);
-}
-
 }  // namespace
 
 namespace internal {
@@ -566,8 +550,6 @@
                         .value();
   }
 
-  RegisterStubPathOverridesIfNecessary();
-
 #if defined(GOOGLE_CHROME_BUILD)
   const char kChromeOSReleaseTrack[] = "CHROMEOS_RELEASE_TRACK";
   std::string channel;
@@ -680,6 +662,8 @@
   chromeos::ResourceReporter::GetInstance()->StartMonitoring(
       task_manager::TaskManagerInterface::GetTaskManager());
 
+  discover_manager_ = std::make_unique<DiscoverManager>();
+
   ChromeBrowserMainPartsLinux::PreMainMessageLoopRun();
 }
 
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.h b/chrome/browser/chromeos/chrome_browser_main_chromeos.h
index 5940c06..3d290ce 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.h
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.h
@@ -35,6 +35,7 @@
 
 class ArcKioskAppManager;
 class DemoModeResourcesRemover;
+class DiscoverManager;
 class EventRewriterDelegateImpl;
 class IdleActionWarningObserver;
 class LowDiskNotification;
@@ -143,6 +144,7 @@
 
   std::unique_ptr<DemoModeResourcesRemover> demo_mode_resources_remover_;
   std::unique_ptr<crostini::CrosvmMetrics> crosvm_metrics_;
+  std::unique_ptr<DiscoverManager> discover_manager_;
 
   DISALLOW_COPY_AND_ASSIGN(ChromeBrowserMainPartsChromeos);
 };
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc
index 75a0736..a5fad95 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc
@@ -724,6 +724,8 @@
              IDS_FILE_BROWSER_MANAGE_IN_DRIVE_BUTTON_LABEL);
   SET_STRING("SHARE_WITH_LINUX_BUTTON_LABEL",
              IDS_FILE_BROWSER_SHARE_WITH_LINUX_BUTTON_LABEL);
+  SET_STRING("MANAGE_LINUX_SHARING_BUTTON_LABEL",
+             IDS_FILE_BROWSER_MANAGE_LINUX_SHARING_BUTTON_LABEL);
   SET_STRING("CHANGE_TO_LISTVIEW_BUTTON_LABEL",
              IDS_FILE_BROWSER_CHANGE_TO_LISTVIEW_BUTTON_LABEL);
   SET_STRING("CHANGE_TO_THUMBNAILVIEW_BUTTON_LABEL",
diff --git a/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api.cc b/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api.cc
index 1ccc228..7c69ca680 100644
--- a/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api.cc
+++ b/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api.cc
@@ -79,6 +79,9 @@
 constexpr const char* kMostCommonPins[] = {"1212", "1004", "2000", "6969",
                                            "1122", "1313", "2001", "1010"};
 
+// QuickUnlockPrivateGetAuthTokenFunction test observer.
+QuickUnlockPrivateGetAuthTokenFunction::TestObserver* test_observer;
+
 // Returns the active set of quick unlock modes.
 void ComputeActiveModes(Profile* profile, ActiveModeCallback result) {
   user_manager::User* user =
@@ -221,6 +224,12 @@
   authenticator_allocator_ = allocator;
 }
 
+// static
+void QuickUnlockPrivateGetAuthTokenFunction::SetTestObserver(
+    QuickUnlockPrivateGetAuthTokenFunction::TestObserver* observer) {
+  test_observer = observer;
+}
+
 ExtensionFunction::ResponseAction
 QuickUnlockPrivateGetAuthTokenFunction::Run() {
   std::unique_ptr<quick_unlock_private::GetAuthToken::Params> params =
@@ -233,6 +242,9 @@
   chromeos::UserContext user_context(*user);
   user_context.SetKey(chromeos::Key(params->account_password));
 
+  if (test_observer)
+    test_observer->OnGetAuthTokenCalled(params->account_password);
+
   // Alter |user_context| if the user is supervised.
   if (user->GetType() == user_manager::USER_TYPE_SUPERVISED) {
     user_context = chromeos::ChromeUserManager::Get()
diff --git a/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api.h b/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api.h
index 1d8b90bc..ffc7bd7 100644
--- a/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api.h
+++ b/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api.h
@@ -29,6 +29,11 @@
       base::Callback<chromeos::ExtendedAuthenticator*(
           chromeos::AuthStatusConsumer* auth_status_consumer)>;
 
+  class TestObserver {
+   public:
+    virtual void OnGetAuthTokenCalled(const std::string&) = 0;
+  };
+
   QuickUnlockPrivateGetAuthTokenFunction();
 
   // Use the given |allocator| to create an ExtendedAuthenticator instance. This
@@ -36,6 +41,10 @@
   void SetAuthenticatorAllocatorForTesting(
       const AuthenticatorAllocator& allocator);
 
+  // Test API.
+  static void SetTestObserver(
+      QuickUnlockPrivateGetAuthTokenFunction::TestObserver* observer);
+
   DECLARE_EXTENSION_FUNCTION("quickUnlockPrivate.getAuthToken",
                              QUICKUNLOCKPRIVATE_GETAUTHTOKEN);
 
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
index c7cdf72..8b31f902 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -390,6 +390,7 @@
     ::testing::Values(TestCase("openQuickView"),
                       TestCase("openQuickView").InGuestMode(),
                       TestCase("openQuickView").TabletMode(),
+                      TestCase("openQuickViewAudio"),
                       TestCase("openQuickViewImage"),
                       TestCase("openQuickViewVideo"),
 // QuickView PDF test fails on MSAN, crbug.com/768070
@@ -666,6 +667,7 @@
                       TestCase("hideSearchButton"),
                       TestCase("myFilesDisplaysAndOpensEntries"),
                       TestCase("directoryTreeRefresh"),
+                      TestCase("myFilesFolderRename"),
                       TestCase("myFilesUpdatesChildren")));
 
 WRAPPED_INSTANTIATE_TEST_CASE_P(
diff --git a/chrome/browser/chromeos/file_manager/file_manager_uitest.cc b/chrome/browser/chromeos/file_manager/file_manager_uitest.cc
index bfec5f1..d742644 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_uitest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_uitest.cc
@@ -61,8 +61,16 @@
   RunTest("checkselect");
 }
 
-IN_PROC_BROWSER_TEST_F(FileManagerUITest, Crostini) {
-  RunTest("crostini");
+IN_PROC_BROWSER_TEST_F(FileManagerUITest, CrostiniMount) {
+  RunTest("crostiniMount");
+}
+
+IN_PROC_BROWSER_TEST_F(FileManagerUITest, CrostiniShare) {
+  RunTest("crostiniShare");
+}
+
+IN_PROC_BROWSER_TEST_F(FileManagerUITest, CrostiniTasks) {
+  RunTest("crostiniTasks");
 }
 
 IN_PROC_BROWSER_TEST_F(FileManagerUITest, QuickView) {
diff --git a/chrome/browser/chromeos/login/demo_mode/demo_setup_controller.cc b/chrome/browser/chromeos/login/demo_mode/demo_setup_controller.cc
index 2b826081..d366900 100644
--- a/chrome/browser/chromeos/login/demo_mode/demo_setup_controller.cc
+++ b/chrome/browser/chromeos/login/demo_mode/demo_setup_controller.cc
@@ -186,7 +186,7 @@
   on_setup_success_ = std::move(on_setup_success);
   on_setup_error_ = std::move(on_setup_error);
 
-  VLOG(1) << "Starting demo mode enrollment "
+  VLOG(1) << "Starting demo setup "
           << DemoSession::DemoConfigToString(demo_config_);
 
   switch (demo_config_) {
@@ -206,6 +206,7 @@
 }
 
 void DemoSetupController::LoadDemoResourcesCrOSComponent() {
+  VLOG(1) << "Loading demo resources component";
   component_updater::CrOSComponentManager* cros_component_manager =
       g_browser_process->platform_part()->cros_component_manager();
   // In tests, use the desired error code.
@@ -238,6 +239,7 @@
     return;
   }
 
+  VLOG(1) << "Starting online enrollment";
   policy::DeviceCloudPolicyManagerChromeOS* policy_manager =
       g_browser_process->platform_part()
           ->browser_policy_connector_chromeos()
@@ -266,6 +268,7 @@
     return;
   }
 
+  VLOG(1) << "Checking if offline policy exists";
   std::string* message = new std::string();
   base::PostTaskWithTraitsAndReplyWithResult(
       FROM_HERE,
@@ -285,6 +288,7 @@
     return;
   }
 
+  VLOG(1) << "Starting offline enrollment";
   policy::EnrollmentConfig config;
   config.mode = policy::EnrollmentConfig::MODE_OFFLINE_DEMO;
   config.management_domain = DemoSetupController::kDemoModeDomain;
@@ -323,6 +327,7 @@
 
   // Try to load the policy for the device local account.
   if (demo_config_ == DemoSession::DemoModeConfig::kOffline) {
+    VLOG(1) << "Loading offline policy";
     DCHECK(!policy_dir_.empty());
     const base::FilePath file_path =
         policy_dir_.AppendASCII(kOfflineDeviceLocalAccountPolicyFileName);
@@ -334,7 +339,7 @@
                        weak_ptr_factory_.GetWeakPtr()));
     return;
   }
-
+  VLOG(1) << "Marking device registered";
   StartupUtils::MarkDeviceRegistered(
       base::BindOnce(&DemoSetupController::OnDeviceRegistered,
                      weak_ptr_factory_.GetWeakPtr()));
@@ -394,6 +399,7 @@
     return;
   }
 
+  VLOG(1) << "Storing offline policy";
   // On the unittest, the device_local_account_policy_store_ is already
   // initialized. Otherwise attempts to get the store.
   if (!device_local_account_policy_store_) {
@@ -449,6 +455,7 @@
 
 void DemoSetupController::OnStoreLoaded(policy::CloudPolicyStore* store) {
   DCHECK_EQ(store, device_local_account_policy_store_);
+  VLOG(1) << "Marking device registered";
   StartupUtils::MarkDeviceRegistered(
       base::BindOnce(&DemoSetupController::OnDeviceRegistered,
                      weak_ptr_factory_.GetWeakPtr()));
diff --git a/chrome/browser/chromeos/login/oobe_interactive_ui_test.cc b/chrome/browser/chromeos/login/oobe_interactive_ui_test.cc
index 35d6e0b..2f34bba 100644
--- a/chrome/browser/chromeos/login/oobe_interactive_ui_test.cc
+++ b/chrome/browser/chromeos/login/oobe_interactive_ui_test.cc
@@ -4,7 +4,9 @@
 
 #include "base/command_line.h"
 #include "base/macros.h"
+#include "base/optional.h"
 #include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api.h"
 #include "chrome/browser/chromeos/login/screens/gaia_view.h"
 #include "chrome/browser/chromeos/login/screens/sync_consent_screen.h"
 #include "chrome/browser/chromeos/login/screens/update_screen.h"
@@ -61,7 +63,27 @@
   DISALLOW_COPY_AND_ASSIGN(JsConditionWaiter);
 };
 
-class OobeInteractiveUITest : public OobeBaseTest {
+class ScopedQuickUnlockPrivateGetAuthTokenFunctionObserver {
+ public:
+  explicit ScopedQuickUnlockPrivateGetAuthTokenFunctionObserver(
+      extensions::QuickUnlockPrivateGetAuthTokenFunction::TestObserver*
+          observer) {
+    extensions::QuickUnlockPrivateGetAuthTokenFunction::SetTestObserver(
+        observer);
+  }
+  ~ScopedQuickUnlockPrivateGetAuthTokenFunctionObserver() {
+    extensions::QuickUnlockPrivateGetAuthTokenFunction::SetTestObserver(
+        nullptr);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(
+      ScopedQuickUnlockPrivateGetAuthTokenFunctionObserver);
+};
+
+class OobeInteractiveUITest
+    : public OobeBaseTest,
+      public extensions::QuickUnlockPrivateGetAuthTokenFunction::TestObserver {
  public:
   OobeInteractiveUITest() = default;
   ~OobeInteractiveUITest() override = default;
@@ -72,6 +94,11 @@
     OobeBaseTest::SetUpCommandLine(command_line);
   }
 
+  // QuickUnlockPrivateGetAuthTokenFunction::TestObserver:
+  void OnGetAuthTokenCalled(const std::string& password) override {
+    quick_unlock_private_get_auth_token_password_ = password;
+  }
+
   void TearDownOnMainThread() override {
     // If the login display is still showing, exit gracefully.
     if (LoginDisplayHost::default_host()) {
@@ -219,6 +246,41 @@
 
     screen->SetProfileSyncDisabledByPolicyForTesting(true);
     screen->OnStateChanged(nullptr);
+    JsConditionWaiter(js_checker_,
+                      "Oobe.getInstance().currentScreen.id != 'sync-consent'")
+        .Wait();
+    LOG(INFO) << "OobeInteractiveUITest: 'sync-consent' screen done.";
+  }
+
+  void WaitForDiscoverScreen() {
+    JsConditionWaiter(js_checker_,
+                      "Oobe.getInstance().currentScreen.id == 'discover'")
+        .Wait();
+    LOG(INFO) << "OobeInteractiveUITest: Switched to 'discover' screen.";
+  }
+
+  void RunDiscoverScreenChecks() {
+    js_checker_.ExpectTrue("!$('discover').hidden");
+    js_checker_.ExpectTrue("!$('discover-impl').hidden");
+    js_checker_.ExpectTrue(
+        "!$('discover-impl').root.querySelector('discover-pin-setup-module')."
+        "hidden");
+    js_checker_.ExpectTrue(
+        "!$('discover-impl').root.querySelector('discover-pin-setup-module').$."
+        "setup.hidden");
+    EXPECT_TRUE(quick_unlock_private_get_auth_token_password_.has_value());
+    EXPECT_EQ(quick_unlock_private_get_auth_token_password_,
+              OobeBaseTest::kFakeUserPassword);
+  }
+
+  void ExitDiscoverPinSetupScreen() {
+    js_checker_.Evaluate(
+        "$('discover-impl').root.querySelector('discover-pin-setup-module')."
+        "$.setupSkipButton.click()");
+    JsConditionWaiter(js_checker_,
+                      "Oobe.getInstance().currentScreen.id != 'discover'")
+        .Wait();
+    LOG(INFO) << "OobeInteractiveUITest: 'discover' screen done.";
   }
 
   void WaitForMarketingOptInScreen() {
@@ -260,12 +322,16 @@
     WaitForLoginDisplayHostShutdown();
   }
 
+  base::Optional<std::string> quick_unlock_private_get_auth_token_password_;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(OobeInteractiveUITest);
 };
 
 // Flakily times out: crbug.com/891484.
 IN_PROC_BROWSER_TEST_F(OobeInteractiveUITest, DISABLED_SimpleEndToEnd) {
+  ScopedQuickUnlockPrivateGetAuthTokenFunctionObserver scoped_observer(this);
+
   WaitForOobeWelcomeScreen();
   RunWelcomeScreenChecks();
   TapWelcomeNext();
@@ -291,6 +357,10 @@
   ExitScreenSyncConsent();
 #endif
 
+  WaitForDiscoverScreen();
+  RunDiscoverScreenChecks();
+  ExitDiscoverPinSetupScreen();
+
   WaitForMarketingOptInScreen();
   RunMarketingOptInScreenChecks();
   ExitMarketingOptInScreen();
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.cc b/chrome/browser/chromeos/login/session/user_session_manager.cc
index 119bf3b..0f20487 100644
--- a/chrome/browser/chromeos/login/session/user_session_manager.cc
+++ b/chrome/browser/chromeos/login/session/user_session_manager.cc
@@ -72,6 +72,7 @@
 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
+#include "chrome/browser/chromeos/settings/install_attributes.h"
 #include "chrome/browser/chromeos/tether/tether_service.h"
 #include "chrome/browser/chromeos/tpm_firmware_update_notification.h"
 #include "chrome/browser/component_updater/crl_set_component_installer.h"
@@ -93,6 +94,8 @@
 #include "chrome/browser/supervised_user/child_accounts/child_account_service_factory.h"
 #include "chrome/browser/ui/app_list/app_list_client_impl.h"
 #include "chrome/browser/ui/startup/startup_browser_creator.h"
+#include "chrome/browser/ui/webui/chromeos/login/discover/discover_manager.h"
+#include "chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_pin_setup.h"
 #include "chrome/common/channel_info.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/logging_chrome.h"
@@ -1185,25 +1188,31 @@
         input_method::InputMethodManager::Get();
     manager->SetState(GetDefaultIMEState(profile));
   }
+  user_manager::UserManager* user_manager = user_manager::UserManager::Get();
   // Set initial prefs if the user is new, or if the user was already present on
   // the device and the profile was re-created. This can happen e.g. in ext4
   // migration in wipe mode.
-  if (user_manager::UserManager::Get()->IsCurrentUserNew() ||
-      profile->IsNewProfile()) {
+  if (user_manager->IsCurrentUserNew() || profile->IsNewProfile()) {
     SetFirstLoginPrefs(profile, user_context.GetPublicSessionLocale(),
                        user_context.GetPublicSessionInputMethod());
+
+    if (user_manager->GetPrimaryUser() == user &&
+        user->GetType() == user_manager::USER_TYPE_REGULAR &&
+        !user_manager->IsUserNonCryptohomeDataEphemeral(user->GetAccountId())) {
+      chromeos::DiscoverManager::Get()
+          ->GetModule<chromeos::DiscoverModulePinSetup>()
+          ->SetPrimaryUserPassword(user_context.GetPasswordKey()->GetSecret());
+    }
   }
 
-  if (user_manager::UserManager::Get()->IsLoggedInAsSupervisedUser()) {
-    user_manager::User* active_user =
-        user_manager::UserManager::Get()->GetActiveUser();
+  if (user_manager->IsLoggedInAsSupervisedUser()) {
+    user_manager::User* active_user = user_manager->GetActiveUser();
     std::string supervised_user_sync_id =
         ChromeUserManager::Get()->GetSupervisedUserManager()->GetUserSyncId(
             active_user->GetAccountId().GetUserEmail());
     profile->GetPrefs()->SetString(prefs::kSupervisedUserId,
                                    supervised_user_sync_id);
-  } else if (user_manager::UserManager::Get()
-                 ->IsLoggedInAsUserWithGaiaAccount()) {
+  } else if (user_manager->IsLoggedInAsUserWithGaiaAccount()) {
     // Get the Gaia ID from the user context.  If it's not available, this may
     // not be available when unlocking a previously opened profile, or when
     // creating a supervised users.  However, in these cases the gaia_id should
@@ -1237,7 +1246,7 @@
         /*refresh_token=*/std::string());
     std::string account_id = identity_manager->GetPrimaryAccountId();
     const user_manager::User* user =
-        user_manager::UserManager::Get()->FindUser(user_context.GetAccountId());
+        user_manager->FindUser(user_context.GetAccountId());
     bool is_child = user->GetType() == user_manager::USER_TYPE_CHILD;
     DCHECK(is_child ==
            (user_context.GetUserType() == user_manager::USER_TYPE_CHILD));
diff --git a/chrome/browser/chromeos/login/wizard_controller.cc b/chrome/browser/chromeos/login/wizard_controller.cc
index cdab13c..c139a658 100644
--- a/chrome/browser/chromeos/login/wizard_controller.cc
+++ b/chrome/browser/chromeos/login/wizard_controller.cc
@@ -1124,6 +1124,10 @@
 }
 
 void WizardController::OnSyncConsentFinished() {
+  ShowDiscoverScreen();
+}
+
+void WizardController::OnDiscoverScreenFinished() {
   ShowMarketingOptInScreen();
 }
 
@@ -1742,7 +1746,7 @@
       OnDemoPreferencesCanceled();
       break;
     case ScreenExitCode::DISCOVER_FINISHED:
-      OnOobeFlowFinished();
+      OnDiscoverScreenFinished();
       break;
     case ScreenExitCode::FINGERPRINT_SETUP_FINISHED:
       OnFingerprintSetupFinished();
diff --git a/chrome/browser/chromeos/login/wizard_controller.h b/chrome/browser/chromeos/login/wizard_controller.h
index 9df4097..d94eefc 100644
--- a/chrome/browser/chromeos/login/wizard_controller.h
+++ b/chrome/browser/chromeos/login/wizard_controller.h
@@ -231,6 +231,7 @@
   void OnTermsOfServiceDeclined();
   void OnTermsOfServiceAccepted();
   void OnSyncConsentFinished();
+  void OnDiscoverScreenFinished();
   void OnFingerprintSetupFinished();
   void OnArcTermsOfServiceSkipped();
   void OnArcTermsOfServiceAccepted();
diff --git a/chrome/browser/data_use_measurement/chrome_data_use_ascriber.cc b/chrome/browser/data_use_measurement/chrome_data_use_ascriber.cc
index aa24356..a1c6da0 100644
--- a/chrome/browser/data_use_measurement/chrome_data_use_ascriber.cc
+++ b/chrome/browser/data_use_measurement/chrome_data_use_ascriber.cc
@@ -10,8 +10,10 @@
 
 #include "base/feature_list.h"
 #include "build/build_config.h"
+#include "chrome/browser/data_use_measurement/chrome_data_use_measurement.h"
 #include "chrome/browser/data_use_measurement/chrome_data_use_recorder.h"
 #include "components/data_use_measurement/content/content_url_request_classifier.h"
+#include "components/data_use_measurement/core/data_use_network_delegate.h"
 #include "components/data_use_measurement/core/data_use_recorder.h"
 #include "components/data_use_measurement/core/data_use_user_data.h"
 #include "components/data_use_measurement/core/url_request_classifier.h"
@@ -112,8 +114,7 @@
   if (service) {
     auto entry =
         CreateNewDataUseRecorder(request, DataUse::TrafficType::SERVICES);
-    entry->data_use().set_description(
-        DataUseUserData::GetServiceNameAsString(service->service_name()));
+    entry->data_use().set_description("");
     return entry;
   }
 
@@ -577,6 +578,15 @@
     observer.OnPageLoadConcluded(&entry->data_use());
 }
 
+std::unique_ptr<net::NetworkDelegate>
+ChromeDataUseAscriber::CreateNetworkDelegate(
+    std::unique_ptr<net::NetworkDelegate> wrapped_network_delegate) {
+  return std::make_unique<data_use_measurement::DataUseNetworkDelegate>(
+      std::move(wrapped_network_delegate), this,
+      std::make_unique<ChromeDataUseMeasurement>(CreateURLRequestClassifier(),
+                                                 this));
+}
+
 std::unique_ptr<URLRequestClassifier>
 ChromeDataUseAscriber::CreateURLRequestClassifier() const {
   return std::make_unique<ContentURLRequestClassifier>();
diff --git a/chrome/browser/data_use_measurement/chrome_data_use_ascriber.h b/chrome/browser/data_use_measurement/chrome_data_use_ascriber.h
index ca04f16a..039c8a4 100644
--- a/chrome/browser/data_use_measurement/chrome_data_use_ascriber.h
+++ b/chrome/browser/data_use_measurement/chrome_data_use_ascriber.h
@@ -69,6 +69,8 @@
   void OnBeforeUrlRequest(net::URLRequest* request) override;
   void OnUrlRequestCompleted(net::URLRequest* request, bool started) override;
   void OnUrlRequestDestroyed(net::URLRequest* request) override;
+  std::unique_ptr<net::NetworkDelegate> CreateNetworkDelegate(
+      std::unique_ptr<net::NetworkDelegate> wrapped_network_delegate) override;
   std::unique_ptr<URLRequestClassifier> CreateURLRequestClassifier()
       const override;
 
diff --git a/chrome/browser/data_use_measurement/chrome_data_use_measurement.cc b/chrome/browser/data_use_measurement/chrome_data_use_measurement.cc
new file mode 100644
index 0000000..f73d7f3b
--- /dev/null
+++ b/chrome/browser/data_use_measurement/chrome_data_use_measurement.cc
@@ -0,0 +1,65 @@
+// 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/data_use_measurement/chrome_data_use_measurement.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/macros.h"
+#include "base/task/post_task.h"
+#include "base/task/task_traits.h"
+#include "chrome/browser/browser_process.h"
+#include "components/data_use_measurement/core/data_use_ascriber.h"
+#include "components/data_use_measurement/core/url_request_classifier.h"
+#include "components/metrics/metrics_service.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace data_use_measurement {
+
+namespace {
+void UpdateMetricsUsagePrefs(int64_t total_bytes,
+                             bool is_cellular,
+                             bool is_metrics_service_usage) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  // Some unit tests use IOThread but do not initialize MetricsService. In that
+  // case it's fine to skip the update.
+  auto* metrics_service = g_browser_process->metrics_service();
+  if (metrics_service) {
+    metrics_service->UpdateMetricsUsagePrefs(total_bytes, is_cellular,
+                                             is_metrics_service_usage);
+  }
+}
+
+// This function is for forwarding metrics usage pref changes to the metrics
+// service on the appropriate thread.
+// TODO(gayane): Reduce the frequency of posting tasks from IO to UI thread.
+void UpdateMetricsUsagePrefsOnUIThread(int64_t total_bytes,
+                                       bool is_cellular,
+                                       bool is_metrics_service_usage) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+  base::PostTaskWithTraits(
+      FROM_HERE, content::BrowserThread::UI,
+      base::BindOnce(UpdateMetricsUsagePrefs, total_bytes, is_cellular,
+                     is_metrics_service_usage));
+}
+}  // namespace
+
+ChromeDataUseMeasurement::ChromeDataUseMeasurement(
+    std::unique_ptr<URLRequestClassifier> url_request_classifier,
+    DataUseAscriber* ascriber)
+    : DataUseMeasurement(std::move(url_request_classifier), ascriber) {}
+
+void ChromeDataUseMeasurement::UpdateDataUseToMetricsService(
+    int64_t total_bytes,
+    bool is_cellular,
+    bool is_metrics_service_usage) {
+  // Update data use of user traffic and services distinguishing cellular and
+  // metrics services data use.
+  UpdateMetricsUsagePrefsOnUIThread(total_bytes, is_cellular,
+                                    is_metrics_service_usage);
+}
+
+}  // namespace data_use_measurement
diff --git a/chrome/browser/data_use_measurement/chrome_data_use_measurement.h b/chrome/browser/data_use_measurement/chrome_data_use_measurement.h
new file mode 100644
index 0000000..d21d639
--- /dev/null
+++ b/chrome/browser/data_use_measurement/chrome_data_use_measurement.h
@@ -0,0 +1,34 @@
+// 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_DATA_USE_MEASUREMENT_CHROME_DATA_USE_MEASUREMENT_H_
+#define CHROME_BROWSER_DATA_USE_MEASUREMENT_CHROME_DATA_USE_MEASUREMENT_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "components/data_use_measurement/core/data_use_measurement.h"
+#include "components/data_use_measurement/core/url_request_classifier.h"
+
+namespace data_use_measurement {
+
+class DataUseAscriber;
+
+class ChromeDataUseMeasurement : public DataUseMeasurement {
+ public:
+  ChromeDataUseMeasurement(
+      std::unique_ptr<URLRequestClassifier> url_request_classifier,
+      DataUseAscriber* ascriber);
+
+  void UpdateDataUseToMetricsService(int64_t total_bytes,
+                                     bool is_cellular,
+                                     bool is_metrics_service_usage) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ChromeDataUseMeasurement);
+};
+
+}  // namespace data_use_measurement
+
+#endif  // CHROME_BROWSER_DATA_USE_MEASUREMENT_CHROME_DATA_USE_MEASUREMENT_H_
diff --git a/chrome/browser/io_thread.cc b/chrome/browser/io_thread.cc
index cefb6c3d..e38572c 100644
--- a/chrome/browser/io_thread.cc
+++ b/chrome/browser/io_thread.cc
@@ -173,29 +173,6 @@
   return std::move(remapped_resolver);
 }
 
-// This function is for forwarding metrics usage pref changes to the metrics
-// service on the appropriate thread.
-// TODO(gayane): Reduce the frequency of posting tasks from IO to UI thread.
-void UpdateMetricsUsagePrefsOnUIThread(const std::string& service_name,
-                                       int message_size,
-                                       bool is_cellular) {
-  base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI},
-                           base::BindOnce(
-                               [](const std::string& service_name,
-                                  int message_size, bool is_cellular) {
-                                 // Some unit tests use IOThread but do not
-                                 // initialize MetricsService. In that case it's
-                                 // fine to skip the update.
-                                 auto* metrics_service =
-                                     g_browser_process->metrics_service();
-                                 if (metrics_service) {
-                                   metrics_service->UpdateMetricsUsagePrefs(
-                                       service_name, message_size, is_cellular);
-                                 }
-                               },
-                               service_name, message_size, is_cellular));
-}
-
 }  // namespace
 
 class SystemURLRequestContextGetter : public net::URLRequestContextGetter {
@@ -407,7 +384,7 @@
         extension_event_router_forwarder());
     builder->set_network_delegate(
         globals_->data_use_ascriber->CreateNetworkDelegate(
-            std::move(chrome_network_delegate), GetMetricsDataUseForwarder()));
+            std::move(chrome_network_delegate)));
 
     std::unique_ptr<net::CertVerifier> cert_verifier;
     if (g_cert_verifier_for_io_thread_testing) {
@@ -454,7 +431,3 @@
         stub_resolver_enabled_, std::move(dns_over_https_servers_));
   }
 }
-
-metrics::UpdateUsagePrefCallbackType IOThread::GetMetricsDataUseForwarder() {
-  return base::Bind(&UpdateMetricsUsagePrefsOnUIThread);
-}
diff --git a/chrome/browser/io_thread.h b/chrome/browser/io_thread.h
index 8abc5632..95e97e8 100644
--- a/chrome/browser/io_thread.h
+++ b/chrome/browser/io_thread.h
@@ -25,7 +25,6 @@
 #include "chrome/browser/net/chrome_network_delegate.h"
 #include "chrome/browser/net/system_network_context_manager.h"
 #include "chrome/common/buildflags.h"
-#include "components/metrics/data_use_tracker.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/browser_thread_delegate.h"
 #include "extensions/buildflags/buildflags.h"
@@ -150,9 +149,6 @@
   // thread.
   void DisableQuic();
 
-  // Returns the callback for updating data use prefs.
-  metrics::UpdateUsagePrefCallbackType GetMetricsDataUseForwarder();
-
   // Configures |builder|'s ProxyResolutionService based on prefs and policies.
   void SetUpProxyService(network::URLRequestContextBuilderMojo* builder) const;
 
diff --git a/chrome/browser/metrics/chrome_feature_list_creator.cc b/chrome/browser/metrics/chrome_feature_list_creator.cc
index 06eb1fe..1508ba71 100644
--- a/chrome/browser/metrics/chrome_feature_list_creator.cc
+++ b/chrome/browser/metrics/chrome_feature_list_creator.cc
@@ -12,10 +12,12 @@
 #include "base/files/file_util.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/path_service.h"
+#include "base/sys_info.h"
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
 #include "cc/base/switches.h"
 #include "chrome/browser/about_flags.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
 #include "chrome/browser/metrics/chrome_metrics_services_manager_client.h"
 #include "chrome/browser/prefs/browser_prefs.h"
@@ -41,7 +43,30 @@
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/chromeos/dbus/dbus_helper.h"
 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
-#endif
+#include "chromeos/chromeos_paths.h"
+#endif  // defined(OS_CHROMEOS)
+
+namespace {
+
+#if defined(OS_CHROMEOS)
+void RegisterStubPathOverridesIfNecessary() {
+  // These overrides need to occur before BrowserPolicyConnectorChromeOS
+  // (for one) is created. The DCHECK ensures that is the case.
+  DCHECK(!g_browser_process);
+
+  base::FilePath user_data_dir;
+  if (base::SysInfo::IsRunningOnChromeOS() ||
+      !base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir)) {
+    return;
+  }
+
+  // Override some paths with stub locations so that cloud policy and enterprise
+  // enrollment work on desktop builds, for ease of development.
+  chromeos::RegisterStubPathOverrides(user_data_dir);
+}
+#endif  // defined(OS_CHROMEOS)
+
+}  // namespace
 
 ChromeFeatureListCreator::ChromeFeatureListCreator() = default;
 
@@ -88,6 +113,7 @@
   RegisterLocalState(pref_registry.get());
 
 #if defined(OS_CHROMEOS)
+  RegisterStubPathOverridesIfNecessary();
   chromeos::PreEarlyInitDBus();
   browser_policy_connector_ =
       std::make_unique<policy::BrowserPolicyConnectorChromeOS>();
diff --git a/chrome/browser/net/system_network_context_manager.cc b/chrome/browser/net/system_network_context_manager.cc
index 8a4ef8f..074bbdf5 100644
--- a/chrome/browser/net/system_network_context_manager.cc
+++ b/chrome/browser/net/system_network_context_manager.cc
@@ -37,6 +37,7 @@
 #include "components/policy/core/common/policy_service.h"
 #include "components/policy/policy_constants.h"
 #include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
 #include "components/variations/variations_associated_data.h"
 #include "components/version_info/version_info.h"
 #include "content/public/browser/browser_task_traits.h"
diff --git a/chrome/browser/net/system_network_context_manager.h b/chrome/browser/net/system_network_context_manager.h
index e0b48d4..78224376 100644
--- a/chrome/browser/net/system_network_context_manager.h
+++ b/chrome/browser/net/system_network_context_manager.h
@@ -19,6 +19,7 @@
 #include "services/network/public/mojom/ssl_config.mojom.h"
 
 class PrefRegistrySimple;
+class PrefService;
 class SSLConfigServiceManager;
 
 namespace network {
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 ee048cf..e056598 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
@@ -1564,4 +1564,103 @@
                                           "isPaused();", &is_paused));
   EXPECT_FALSE(is_paused);
 }
+
+// Tests that the close and resize icons move properly as the window changes
+// quadrants.
+IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
+                       MovingQuadrantsMovesResizeControl) {
+  GURL test_page_url = ui_test_utils::GetTestUrl(
+      base::FilePath(base::FilePath::kCurrentDirectory),
+      base::FilePath(
+          FILE_PATH_LITERAL("media/picture-in-picture/window-size.html")));
+  ui_test_utils::NavigateToURL(browser(), test_page_url);
+
+  content::WebContents* active_web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  ASSERT_TRUE(active_web_contents);
+
+  SetUpWindowController(active_web_contents);
+  ASSERT_TRUE(window_controller());
+
+  content::OverlayWindow* overlay_window =
+      window_controller()->GetWindowForTesting();
+  ASSERT_TRUE(overlay_window);
+  ASSERT_FALSE(overlay_window->IsVisible());
+
+  bool result = false;
+  ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
+      active_web_contents, "enterPictureInPicture();", &result));
+  ASSERT_TRUE(result);
+
+  OverlayWindowViews* overlay_window_views =
+      static_cast<OverlayWindowViews*>(overlay_window);
+
+  // The PiP window starts in the bottom-right quadrant of the screen.
+  gfx::Rect bottom_right_bounds = overlay_window_views->GetBounds();
+  // The relative center point of the window.
+  gfx::Point center(bottom_right_bounds.width() / 2,
+                    bottom_right_bounds.height() / 2);
+  gfx::Point close_button_position =
+      overlay_window_views->close_image_position_for_testing();
+  gfx::Point resize_button_position =
+      overlay_window_views->resize_handle_position_for_testing();
+
+  // The close button should be in the top right corner.
+  EXPECT_LT(center.x(), close_button_position.x());
+  EXPECT_GT(center.y(), close_button_position.y());
+  // The resize button should be in the top left corner.
+  EXPECT_GT(center.x(), resize_button_position.x());
+  EXPECT_GT(center.y(), resize_button_position.y());
+
+  // Move the window to the bottom left corner.
+  gfx::Rect bottom_left_bounds(0, bottom_right_bounds.y(),
+                               bottom_right_bounds.width(),
+                               bottom_right_bounds.height());
+  overlay_window_views->SetBounds(bottom_left_bounds);
+  close_button_position =
+      overlay_window_views->close_image_position_for_testing();
+  resize_button_position =
+      overlay_window_views->resize_handle_position_for_testing();
+
+  // The close button should be in the top left corner.
+  EXPECT_GT(center.x(), close_button_position.x());
+  EXPECT_GT(center.y(), close_button_position.y());
+  // The resize button should be in the top right corner.
+  EXPECT_LT(center.x(), resize_button_position.x());
+  EXPECT_GT(center.y(), resize_button_position.y());
+
+  // Move the window to the top right corner.
+  gfx::Rect top_right_bounds(bottom_right_bounds.x(), 0,
+                             bottom_right_bounds.width(),
+                             bottom_right_bounds.height());
+  overlay_window_views->SetBounds(top_right_bounds);
+  close_button_position =
+      overlay_window_views->close_image_position_for_testing();
+  resize_button_position =
+      overlay_window_views->resize_handle_position_for_testing();
+
+  // The close button should be in the top right corner.
+  EXPECT_LT(center.x(), close_button_position.x());
+  EXPECT_GT(center.y(), close_button_position.y());
+  // The resize button should be in the bottom left corner.
+  EXPECT_GT(center.x(), resize_button_position.x());
+  EXPECT_LT(center.y(), resize_button_position.y());
+
+  // Move the window to the top left corner.
+  gfx::Rect top_left_bounds(0, 0, bottom_right_bounds.width(),
+                            bottom_right_bounds.height());
+  overlay_window_views->SetBounds(top_left_bounds);
+  close_button_position =
+      overlay_window_views->close_image_position_for_testing();
+  resize_button_position =
+      overlay_window_views->resize_handle_position_for_testing();
+
+  // The close button should be in the top right corner.
+  EXPECT_LT(center.x(), close_button_position.x());
+  EXPECT_GT(center.y(), close_button_position.y());
+  // The resize button should be in the bottom right corner.
+  EXPECT_LT(center.x(), resize_button_position.x());
+  EXPECT_LT(center.y(), resize_button_position.y());
+}
+
 #endif  // defined(OS_CHROMEOS)
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 60de178..77f4bb2 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -86,7 +86,6 @@
 #include "components/flags_ui/pref_service_flags_storage.h"
 #include "components/gcm_driver/gcm_channel_status_syncer.h"
 #include "components/image_fetcher/core/storage/image_cache.h"
-#include "components/invalidation/impl/invalidator_registrar_with_memory.h"
 #include "components/invalidation/impl/per_user_topic_registration_manager.h"
 #include "components/language/content/browser/geo_language_provider.h"
 #include "components/metrics/metrics_pref_names.h"
@@ -594,7 +593,6 @@
   sync_sessions::SessionSyncPrefs::RegisterProfilePrefs(registry);
   syncer::SyncPrefs::RegisterProfilePrefs(registry);
   syncer::PerUserTopicRegistrationManager::RegisterProfilePrefs(registry);
-  syncer::InvalidatorRegistrarWithMemory::RegisterProfilePrefs(registry);
   TemplateURLPrepopulateData::RegisterProfilePrefs(registry);
   translate::TranslatePrefs::RegisterProfilePrefs(registry);
   UINetworkQualityEstimatorService::RegisterProfilePrefs(registry);
diff --git a/chrome/browser/profiles/profile_impl_io_data.cc b/chrome/browser/profiles/profile_impl_io_data.cc
index dede943a..9f765b5 100644
--- a/chrome/browser/profiles/profile_impl_io_data.cc
+++ b/chrome/browser/profiles/profile_impl_io_data.cc
@@ -428,8 +428,7 @@
 
   return data_reduction_proxy_io_data()->CreateNetworkDelegate(
       io_thread->globals()->data_use_ascriber->CreateNetworkDelegate(
-          std::move(chrome_network_delegate),
-          io_thread->GetMetricsDataUseForwarder()),
+          std::move(chrome_network_delegate)),
       true);
 }
 
diff --git a/chrome/browser/resources/chromeos/login/discover/discover_app.html b/chrome/browser/resources/chromeos/login/discover/discover_app.html
index 552f3bb..0a32765 100644
--- a/chrome/browser/resources/chromeos/login/discover/discover_app.html
+++ b/chrome/browser/resources/chromeos/login/discover/discover_app.html
@@ -16,6 +16,7 @@
     <script src="chrome://resources/js/util.js"></script>
     <script src="chrome://oobe/strings.js"></script>
     <script src="chrome://resources/js/i18n_template.js"></script>
+    <link rel="import" href="chrome://resources/html/i18n_behavior.html">
   </head>
   <body>
     <include src="../hd-iron-icon.html">
diff --git a/chrome/browser/resources/chromeos/login/discover/discover_app.js b/chrome/browser/resources/chromeos/login/discover/discover_app.js
index 2ebfc312..7e634d3 100644
--- a/chrome/browser/resources/chromeos/login/discover/discover_app.js
+++ b/chrome/browser/resources/chromeos/login/discover/discover_app.js
@@ -56,3 +56,9 @@
 document.addEventListener('DOMContentLoaded', function() {
   Oobe.initialize();
 });
+
+// In App mode Discover should not exit once last module is completed.
+// It should go back to "Welcome" screen instead.
+$('discoverUI').addEventListener('discover-done', function() {
+  $('discoverUI').onBeforeShow();
+});
diff --git a/chrome/browser/resources/chromeos/login/discover/discover_components.html b/chrome/browser/resources/chromeos/login/discover/discover_components.html
index c45c4da1..e596167 100644
--- a/chrome/browser/resources/chromeos/login/discover/discover_components.html
+++ b/chrome/browser/resources/chromeos/login/discover/discover_components.html
@@ -3,6 +3,7 @@
 <include src="modules/discover_module_redeem_offers.html">
 <include src="modules/discover_module_launch_help_app.html">
 <include src="modules/discover_module_sync_files.html">
+<include src="modules/discover_module_pin_setup.html">
 
 <include src="discover_welcome.html">
 <include src="discover_ui.html">
diff --git a/chrome/browser/resources/chromeos/login/discover/discover_components.js b/chrome/browser/resources/chromeos/login/discover/discover_components.js
index c3cc935c..031b8630 100644
--- a/chrome/browser/resources/chromeos/login/discover/discover_components.js
+++ b/chrome/browser/resources/chromeos/login/discover/discover_components.js
@@ -10,6 +10,7 @@
 // <include src="modules/discover_module_redeem_offers.js">
 // <include src="modules/discover_module_launch_help_app.js">
 // <include src="modules/discover_module_sync_files.js">
+// <include src="modules/discover_module_pin_setup.js">
 
 // --- These depend on modules.
 // <include src="discover_welcome.js">
diff --git a/chrome/browser/resources/chromeos/login/discover/discover_module_behavior.js b/chrome/browser/resources/chromeos/login/discover/discover_module_behavior.js
index f527b09..cdac3fc 100644
--- a/chrome/browser/resources/chromeos/login/discover/discover_module_behavior.js
+++ b/chrome/browser/resources/chromeos/login/discover/discover_module_behavior.js
@@ -18,16 +18,37 @@
     /**
      * Discover module name. Must be set explicitly.
      */
-    module: {
-      type: String,
-      readOnly: true,
-      value: '',
-    },
+    module: String,
   },
 
-  sendMessage: function(message, parameters) {
+  /**
+   * Sends one-way message to Discover API.
+   * @param {string} message Message to send.  Must start with
+   *     'discover.moduleName.'.
+   * @param {Array<*>} parameters
+   */
+  discoverCall: function(message, parameters) {
     assert(this.module.length > 0);
-    chrome.send('discover.' + this.module + '.' + message, parameters);
+    assert(message.startsWith('discover.' + this.module + '.'));
+    window.discoverSendImpl(message, null, parameters);
+  },
+
+  /**
+   * Sends Discover API message with callback to be invoked on reply.
+   * @param {string} message Message to send.  Must start with
+   *     'discover.moduleName.'.
+   * @param {Array<*>} parameters Message parameters.
+   * @param {!function(*)} callback Callback to be invoked on reply.
+   */
+  discoverCallWithReply: function(message, parameters, callback) {
+    assert(this.module.length > 0);
+    assert(message.startsWith('discover.' + this.module + '.'));
+    assert(callback instanceof Function);
+    window.discoverSendImpl(message, callback, parameters);
+  },
+
+  updateLocalizedContent: function() {
+    this.i18nUpdateLocale();
   },
 
   show: function() {},
diff --git a/chrome/browser/resources/chromeos/login/discover/discover_ui.html b/chrome/browser/resources/chromeos/login/discover/discover_ui.html
index 9c25ee0..53cc2cd 100644
--- a/chrome/browser/resources/chromeos/login/discover/discover_ui.html
+++ b/chrome/browser/resources/chromeos/login/discover/discover_ui.html
@@ -29,6 +29,7 @@
 <dom-module id="discover-ui">
   <template>
     <link rel="stylesheet" href="discover_ui.css">
+    <link rel="stylesheet" href="../oobe_fonts.css">
     <discover-welcome id="discoverWelcome"
         on-module-continue="startLinearFlow_" on-module-skip="end_">
       <discover-redeem-offers-card class="card" slot="cards"
@@ -40,7 +41,12 @@
       <discover-sync-files-card class="card" slot="cards"
           module="sync-files">
       </discover-sync-files-card>
+      <discover-pin-setup-card class="card" slot="cards"
+          module="pinSetup" on-click="onCardClick_">
+      </discover-pin-setup-card>
     </discover-welcome>
-    <!-- TODO (alemate): add modules -->
+    <discover-pin-setup-module class="module"
+        module="pinSetup" first-run="[[firstRun]]" hidden>
+    </discover-pin-setup-module>
   </template>
 </dom-module>
diff --git a/chrome/browser/resources/chromeos/login/discover/discover_ui.js b/chrome/browser/resources/chromeos/login/discover/discover_ui.js
index f08472c..d00393e 100644
--- a/chrome/browser/resources/chromeos/login/discover/discover_ui.js
+++ b/chrome/browser/resources/chromeos/login/discover/discover_ui.js
@@ -5,6 +5,35 @@
 /**
  * @fileoverview Polymer element for displaying Discover UI.
  */
+function initializeDiscoverAPI() {
+  let discoverCallbacks = {};
+
+  window.discoverSendImpl = (message, callback, parameters) => {
+    assert(message.startsWith('discover.'));
+    // Callback Id should be random to prevent triggering of incorrect
+    // callbacks if Id get screwed.
+    let callbackId;
+    if (callback) {
+      for (let i = 0; i < 10; ++i) {
+        callbackId =
+            String(Math.floor(Math.random() * 2147483647));  // 2^31 - 1
+        if (callbackId && !(callbackId in discoverCallbacks))
+          break;
+      }
+      assert(!(callbackId in discoverCallbacks));
+      discoverCallbacks[callbackId] = callback;
+    }
+    chrome.send(message, [callbackId].concat(parameters));
+  };
+
+  window.discoverReturn = (callbackId, value) => {
+    assert(callbackId in discoverCallbacks);
+    let callback = discoverCallbacks[callbackId];
+    assert(delete (discoverCallbacks[callbackId]));
+    callback.call(null, value);
+  };
+}
+
 {
   const DISCOVER_WELCOME_MODULE = 'discoverWelcome';
 
@@ -13,6 +42,17 @@
 
     behaviors: [I18nBehavior, OobeDialogHostBehavior],
 
+    properties: {
+      /**
+       * When this flag is true, Discover UI is displayed as part of FirstRun
+       * UI.
+       */
+      firstRun: {
+        type: Boolean,
+        value: false,
+      },
+    },
+
     updateLocalizedContent: function() {
       this.i18nUpdateLocale();
       this.propagateUpdateLocalizedContent('.card');
@@ -25,6 +65,7 @@
      * @override
      */
     attached: function() {
+      initializeDiscoverAPI();
       // Initialize modules event handlers.
       let modules = Polymer.dom(this.root).querySelectorAll('.module');
       for (let i = 0; i < modules.length; ++i) {
@@ -54,7 +95,11 @@
       this.propagateFullScreenMode('#discoverWelcome');
       this.propagateFullScreenMode('.module');
 
-      this.$.discoverWelcome.show();
+      if (this.firstRun) {
+        this.showModule_('pinSetup');
+      } else {
+        this.showModule_(DISCOVER_WELCOME_MODULE);
+      }
     },
 
     /*
diff --git a/chrome/browser/resources/chromeos/login/discover/discover_welcome.css b/chrome/browser/resources/chromeos/login/discover/discover_welcome.css
index e0cb7d7..de3508d 100644
--- a/chrome/browser/resources/chromeos/login/discover/discover_welcome.css
+++ b/chrome/browser/resources/chromeos/login/discover/discover_welcome.css
@@ -5,7 +5,7 @@
 :host {
   --card-box-height: 208px;
   --card-box-width: 312px;
-  --card-rows: 2;
+  --card-rows: 3;
   --card-spacing: 16px;  /* Spacing between cards. */
   --dialog-height: 640px;
   --highlight-card-box-width: 640px;
diff --git a/chrome/browser/resources/chromeos/login/discover/modules/discover_module_pin_setup.css b/chrome/browser/resources/chromeos/login/discover/modules/discover_module_pin_setup.css
new file mode 100644
index 0000000..e4cc587
--- /dev/null
+++ b/chrome/browser/resources/chromeos/login/discover/modules/discover_module_pin_setup.css
@@ -0,0 +1,28 @@
+/* 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. */
+
+#passwordInput {
+  margin-top: 32px;
+  width: 560px;
+}
+
+setup-pin-keyboard {
+  --cr-input-input: {
+    font-size: 28px;
+    letter-spacing: 28px;
+  };
+  --pin-keyboard-digit-button-letter: {
+    display: none;
+  };
+  --pin-keyboard-pin-input-style: {
+    width: 244px;
+  };
+  margin-top: 32px;
+}
+
+#done-illustration {
+  height: 264px;
+  margin-top: 38px; /* 64px from subtitle baseline. */
+  width: 264px;
+}
diff --git a/chrome/browser/resources/chromeos/login/discover/modules/discover_module_pin_setup.html b/chrome/browser/resources/chromeos/login/discover/modules/discover_module_pin_setup.html
new file mode 100644
index 0000000..7883666
--- /dev/null
+++ b/chrome/browser/resources/chromeos/login/discover/modules/discover_module_pin_setup.html
@@ -0,0 +1,160 @@
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
+<link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
+
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
+
+<link rel="import" href="chrome://resources/html/assert.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
+<link rel="import" href="chrome://resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.html">
+<link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html">
+
+<dom-module id="discover-pin-setup-card">
+  <template>
+    <discover-card on-click="onClick_">
+      <if expr="chromeos and _google_chrome">
+        <div slot=background>
+          <div style="background: lightgreen; width: 100%; height: 100%;"></div>
+        </div>
+      </if>
+      <div slot="title">
+        [[i18nDynamic(locale, 'discoverPinSetup')]]
+      </div>
+    </discover-card>
+  </template>
+</dom-module>
+
+<iron-iconset-svg name="discover-pin-setup-32" size="32">
+  <svg>
+    <defs>
+      <g id="lock">
+        <defs>
+          <path id="a" d="M24 12h-1.333V9.333A6.67 6.67 0 0 0 16 2.667a6.67 6.67 0 0 0-6.667 6.666V12H8a2.675 2.675 0 0 0-2.667 2.667V28c0 1.467 1.2 2.667 2.667 2.667h16c1.467 0 2.667-1.2 2.667-2.667V14.667C26.667 13.2 25.467 12 24 12zM12 9.333c0-2.213 1.787-4 4-4s4 1.787 4 4V12h-8V9.333zM24 28H8V14.667h16V28zm-8-4c1.467 0 2.667-1.2 2.667-2.667 0-1.466-1.2-2.666-2.667-2.666a2.675 2.675 0 0 0-2.667 2.666C13.333 22.8 14.533 24 16 24z">
+          </path>
+        </defs>
+        <g fill="none" fill-rule="evenodd">
+          <use fill="#1A73E8" fill-rule="nonzero" xlink:href="#a"></use>
+          <path d="M0 0h32v32H0z"></path>
+        </g>
+      </g>
+    </defs>
+  </svg>
+</iron-iconset-svg>
+
+<iron-iconset-svg name="discover-pin-setup-64" size="64">
+  <svg>
+    <defs>
+      <g id="lock">
+        <defs>
+          <path id="a" d="M48 24h-2.667v-5.333c0-7.36-5.973-13.334-13.333-13.334s-13.333 5.974-13.333 13.334V24H16c-2.933 0-5.333 2.4-5.333 5.333V56c0 2.933 2.4 5.333 5.333 5.333h32c2.933 0 5.333-2.4 5.333-5.333V29.333C53.333 26.4 50.933 24 48 24zm-24-5.333c0-4.427 3.573-8 8-8s8 3.573 8 8V24H24v-5.333zM48 56H16V29.333h32V56zm-16-8c2.933 0 5.333-2.4 5.333-5.333 0-2.934-2.4-5.334-5.333-5.334-2.933 0-5.333 2.4-5.333 5.334C26.667 45.6 29.067 48 32 48z"></path>
+        </defs>
+        <g fill="none" fill-rule="evenodd">
+          <use fill="#1A73E8" fill-rule="nonzero" xlink:href="#a"></use>
+          <path d="M0 0h64v64H0z"></path>
+        </g>
+      </g>
+    </defs>
+  </svg>
+</iron-iconset-svg>
+
+<dom-module id="discover-pin-setup-module">
+  <template>
+    <link rel="stylesheet" href="../../oobe_dialog_host.css">
+    <style include="iron-flex iron-flex-alignment iron-positioning
+                    cr-shared-style">
+    </style>
+    <style include="settings-shared"></style>
+    <link rel="stylesheet" href="discover_module_pin_setup.css">
+    <oobe-dialog id="loading" role="dialog" no-header no-footer-padding
+        hidden="[[isStepHidden_(step_, 'loading')]]">
+      <div slot="footer" class="flex layout vertical center center-justified">
+        <throbber-notice text="Please wait">
+        </throbber-notice>
+      </div>
+    </oobe-dialog>
+    <oobe-dialog role="dialog" has-buttons on-keypress="onKeypress_"
+        hidden="[[isStepHidden_(step_, 'password')]]">
+      <hd-iron-icon slot="oobe-icon" icon1x="discover-pin-setup-32:lock"
+          icon2x="discover-pin-setup-64:lock">
+      </hd-iron-icon>
+      <h1 slot="title">
+        [[i18nDynamic(locale, 'discoverPinSetupPasswordTitle')]]
+      </h1>
+      <div slot="subtitle">
+        [[i18nDynamic(locale, 'discoverPinSetupPasswordSubTitle')]]
+      </div>
+      <div slot="footer" class="flex layout horizontal">
+        <cr-input id="passwordInput" type="password"
+            placeholder="[[i18nDynamic(locale, 'passwordPromptPasswordLabel')]]"
+            error-message="[[i18nDynamic(locale,
+                                         'passwordPromptInvalidPassword')]]"
+            invalid="[[passwordInvalid_]]"
+            value="{{password_}}"
+            aria-disabled="false"
+            class="focus-on-show">
+        </cr-input>
+      </div>
+      <div slot="bottom-buttons" class="flex layout horizontal end-justified">
+        <oobe-text-button on-tap="onSkipButton_">
+          <div>[[i18nDynamic(locale, 'discoverPinSetupSkip')]]</div>
+        </oobe-text-button>
+        <oobe-next-button inverse disabled="[[!password_]]"
+            on-tap="onPasswordSubmitButton_">
+          <div>[[i18nDynamic(locale, 'next')]]</div>
+        </oobe-next-button>
+      </div>
+    </oobe-dialog>
+    <oobe-dialog id="setup" role="dialog" has-buttons
+        hidden="[[isPinSetupHidden_(step_)]]">
+      <hd-iron-icon slot="oobe-icon" icon1x="discover-pin-setup-32:lock"
+          icon2x="discover-pin-setup-64:lock">
+      </hd-iron-icon>
+      <h1 slot="title" hidden="[[isStepHidden_(step_, 'start')]]">
+        [[i18nDynamic(locale, 'discoverPinSetupTitle1')]]
+      </h1>
+      <h1 slot="title" hidden="[[isStepHidden_(step_, 'confirm')]]">
+        [[i18nDynamic(locale, 'discoverPinSetupTitle2')]]
+      </h1>
+      <h1 slot="title" hidden="[[isStepHidden_(step_, 'done')]]">
+        [[i18nDynamic(locale, 'discoverPinSetupTitle3')]]
+      </h1>
+      <div slot="subtitle" hidden="[[isStepHidden_(step_, 'start')]]">
+        [[i18nDynamic(locale, 'discoverPinSetupSubtitle1')]]
+      </div>
+      <div slot="subtitle" hidden="[[isStepHidden_(step_, 'done')]]">
+        [[i18nDynamic(locale, 'discoverPinSetupSubtitle3')]]<BR>
+      </div>
+      <div slot="footer" class="flex layout horizontal center-justified">
+          <setup-pin-keyboard id="pinKeyboard"
+              hidden="[[!isStepHidden_(step_, 'done')]]"
+              enable-submit="{{enableSubmit_}}"
+              is-confirm-step="{{isConfirmStep_}}"
+              on-pin-submit="onPinSubmit_"
+              on-set-pin-done="onSetPinDone_"
+              set-modes="{{setModes}}"
+              quick-unlock-private="[[quickUnlockPrivate_]]"
+              write-uma="[[writeUma_]]"
+              class="focus-on-show">
+          </setup-pin-keyboard>
+          <img id="done-illustration" hidden="[[isStepHidden_(step_, 'done')]]"
+              srcset="images/pin_illustration_1x.svg 1x,
+                      images/pin_illustration_2x.svg 2x">
+      </div>
+      <div slot="bottom-buttons" class="flex layout horizontal end-justified">
+        <oobe-text-button id="setupSkipButton" on-tap="onSkipButton_"
+            hidden="[[!isStepHidden_(step_, 'done')]]">
+          <div>[[i18nDynamic(locale, 'discoverPinSetupSkip')]]</div>
+        </oobe-text-button>
+        <oobe-next-button inverse
+            disabled="[[isNextDisabled_(step_, enableSubmit_)]]"
+            on-tap="onNextButton_" class="focus-on-show"
+            hidden="[[!isStepHidden_(step_, 'done')]]">
+          <div>[[i18nDynamic(locale, 'next')]]</div>
+        </oobe-next-button>
+        <oobe-text-button inverse on-tap="onDoneButton_" class="focus-on-show"
+            hidden="[[isStepHidden_(step_, 'done')]]">
+          <div>[[i18nDynamic(locale, 'discoverPinSetupDone')]]</div>
+        </oobe-next-button>
+      </div>
+    </oobe-dialog>
+  </template>
+</dom-module>
diff --git a/chrome/browser/resources/chromeos/login/discover/modules/discover_module_pin_setup.js b/chrome/browser/resources/chromeos/login/discover/modules/discover_module_pin_setup.js
new file mode 100644
index 0000000..7a66fedb
--- /dev/null
+++ b/chrome/browser/resources/chromeos/login/discover/modules/discover_module_pin_setup.js
@@ -0,0 +1,377 @@
+// 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.
+
+Polymer({
+  is: 'discover-pin-setup-card',
+
+  behaviors: [DiscoverModuleBehavior],
+});
+
+{
+  /** @const */
+  let PIN_SETUP_STEPS = {
+    LOADING: 'loading',
+    PASSWORD: 'password',
+    START: 'start',
+    CONFIRM: 'confirm',
+    DONE: 'done',
+  };
+
+  // Time out for quickUnlock API.
+  let kApiTimeout = 10000;
+
+  Polymer({
+    is: 'discover-pin-setup-module',
+
+    behaviors: [DiscoverModuleBehavior],
+
+    properties: {
+      /**
+       * True when "Learn more" link should be hidden.
+       */
+      learnMoreLinkHidden: {
+        type: Boolean,
+        value: false,
+      },
+
+      /**
+       * Flag from <setup-pin-keyboard>.
+       * @private
+       */
+      enableSubmit_: {
+        type: Boolean,
+        value: false,
+      },
+
+      /**
+       * Flag from <setup-pin-keyboard>.
+       * @private
+       */
+      isConfirmStep_: {
+        type: Boolean,
+        value: false,
+        observer: 'onIsConfirmStepChanged_',
+      },
+
+      /** QuickUnlockPrivate API token. */
+      authToken_: {
+        type: String,
+        observer: 'onAuthTokenChanged_',
+      },
+
+      setModes: Object,
+
+      /** @type{PIN_SETUP_STEPS} */
+      step_: {
+        type: Number,
+        value: PIN_SETUP_STEPS.LOADING,
+        observer: 'onStepChanged_',
+      },
+
+      /**
+       * Interface for chrome.quickUnlockPrivate calls. May be overridden by
+       * tests.
+       * @type {QuickUnlockPrivate}
+       * @private
+       */
+      quickUnlockPrivate_: {type: Object, value: chrome.quickUnlockPrivate},
+
+      /**
+       * writeUma is a function that handles writing uma stats. It may be
+       * overridden for tests.
+       *
+       * @type {Function}
+       * @private
+       */
+      writeUma_: {
+        type: Object,
+        value: function() {
+          return settings.recordLockScreenProgress;
+        }
+      },
+
+      /**
+       * When this flag is true, Discover UI is displayed as part of FirstRun
+       * UI.
+       */
+      firstRun: {
+        type: Boolean,
+        value: false,
+      },
+
+      /**
+       * Value of user password input field.
+       * @private
+       */
+      password_: {
+        type: String,
+        value: '',
+        observer: 'onTypedPasswordChange_',
+      },
+
+      /**
+       * Helper property which marks password as valid/invalid.
+       * @private
+       */
+      passwordInvalid_: {
+        type: Boolean,
+        value: false,
+      },
+    },
+
+    /**
+     * True when in first run mode and primary user password has been requested.
+     * @private
+     */
+    firstRunUserPasswordRequested_: false,
+
+    /**
+     * Timeout ID for automatic skip timer.
+     * @private
+     */
+    autoSkipTimer_: undefined,
+
+    /**
+     * Starts automatic skip timer.
+     * @private
+     */
+    startAutoSkipTimer_: function() {
+      if (this.autoSkipTimer_ !== undefined)
+        return;
+
+      this.autoSkipTimer_ = setTimeout(() => {
+        console.error('autoSkipTimer triggered!');
+        this.onSkipButton_();
+      }, kApiTimeout);
+    },
+
+    /**
+     * Kills automatic skip timer.
+     * @private
+     */
+    stopAutoSkipTimer_: function() {
+      if (this.autoSkipTimer_)
+        clearTimeout(this.autoSkipTimer_);
+
+      this.autoSkipTimer_ = undefined;
+    },
+
+    /**
+     * step_ observer. Makes sure default focus follows step change, and kills
+     * autoskip timer if spinner is hidden.
+     * @private
+     */
+    onStepChanged_: function() {
+      let dialogs = this.root.querySelectorAll('oobe-dialog');
+      for (let dialog of dialogs) {
+        if (dialog.hidden)
+          continue;
+
+        dialog.focus();
+        break;
+      }
+      if (this.step_ == PIN_SETUP_STEPS.LOADING)
+        return;
+
+      this.stopAutoSkipTimer_();
+    },
+
+    /** @override */
+    show: function() {
+      if (this.firstRun) {
+        this.getFirstRunUserPassword_();
+      } else {
+        this.step_ = PIN_SETUP_STEPS.PASSWORD;
+      }
+    },
+
+    /**
+     * Starts fetching QuickUnlock token.
+     * @private
+     */
+    getToken_: function(password) {
+      this.step_ = PIN_SETUP_STEPS.LOADING;
+      this.startAutoSkipTimer_();
+
+      this.quickUnlockPrivate_.getAuthToken(password, (tokenInfo) => {
+        if (chrome.runtime.lastError) {
+          console.error(
+              'getAuthToken(): Failed: ' + chrome.runtime.lastError.message);
+          if (this.firstRun) {
+            // Trigger 'token expired'
+            this.authToken_ = '';
+          } else {
+            this.step_ = PIN_SETUP_STEPS.PASSWORD;
+            this.passwordInvalid_ = true;
+            // Select the whole password if user entered an incorrect password.
+            this.$.passwordInput.select();
+          }
+          return;
+        }
+        this.setToken(tokenInfo);
+      });
+    },
+
+    /**
+     * Starts fetching primary user password.
+     * @private
+     */
+    getFirstRunUserPassword_: function() {
+      this.startAutoSkipTimer_();
+      this.discoverCallWithReply(
+          'discover.pinSetup.getUserPassword', [], (password) => {
+            if (chrome.runtime.lastError) {
+              console.error(
+                  'getUserPassword() failed: ' +
+                  chrome.runtime.lastError.message);
+              // Trigger 'token expired'
+              this.authToken_ = '';
+              return;
+            }
+            this.getToken_(password);
+          });
+    },
+
+    /**
+     * Receives new AuthToken.
+     * @private
+     */
+    setToken: function(tokenInfo) {
+      this.authToken_ = tokenInfo.token;
+      // Subtract time from the expiration time to account for IPC delays.
+      // Treat values less than the minimum as 0 for testing.
+      const IPC_SECONDS = 2;
+      const lifetimeMs = tokenInfo.lifetimeSeconds > IPC_SECONDS ?
+          (tokenInfo.lifetimeSeconds - IPC_SECONDS) * 1000 :
+          0;
+      this.clearAuthTokenTimeoutId_ = setTimeout(() => {
+        this.authToken_ = '';
+      }, lifetimeMs);
+
+      this.step_ = PIN_SETUP_STEPS.START;
+    },
+
+    /**
+     * Called when the authToken_ changes. If the authToken_ is NOT valid,
+     * skips module.
+     * @private
+     */
+    onAuthTokenChanged_: function() {
+      this.password_ = '';
+      if (!this.authToken_) {
+        this.setModes = null;
+        this.step_ = PIN_SETUP_STEPS.LOADING;
+        this.onSkipButton_();
+        return;
+      }
+      this.setModes = (modes, credentials, onComplete) => {
+        this.quickUnlockPrivate_.setModes(
+            this.authToken_, modes, credentials, () => {
+              let result = true;
+              if (chrome.runtime.lastError) {
+                console.error(
+                    'setModes failed: ' + chrome.runtime.lastError.message);
+                result = false;
+              }
+              onComplete(result);
+            });
+      };
+    },
+
+    /** @private */
+    onIsConfirmStepChanged_: function() {
+      if (this.isConfirmStep_)
+        this.step_ = PIN_SETUP_STEPS.CONFIRM;
+    },
+
+    /** @private */
+    onPinSubmit_: function() {
+      this.$.pinKeyboard.doSubmit();
+    },
+
+    /** @private */
+    onSetPinDone_: function() {
+      this.step_ = PIN_SETUP_STEPS.DONE;
+    },
+
+    /** @private */
+    onSkipButton_: function() {
+      this.password_ = '';
+      this.authToken_ = '';
+
+      this.stopAutoSkipTimer_();
+      this.$.pinKeyboard.resetState();
+      this.fire('module-continue');
+    },
+
+    /** @private */
+    onNextButton_: function() {
+      this.onPinSubmit_();
+    },
+
+    /** @private */
+    onDoneButton_: function() {
+      this.password_ = '';
+      this.authToken_ = '';
+      this.$.pinKeyboard.resetState();
+      this.fire('module-continue');
+    },
+
+    /**
+     * Returns true if current UI step is different from expected.
+     * @param {PIN_SETUP_STEPS} current_step
+     * @param {PIN_SETUP_STEPS} expected_step
+     * @private
+     */
+    isStepHidden_: function(current_step, expected_step) {
+      return current_step != expected_step;
+    },
+
+    /**
+     * Returns true if "Pin setup" dialog is hidden.
+     * @param {PIN_SETUP_STEPS} current_step
+     * @private
+     */
+    isPinSetupHidden_: function(current_step) {
+      return !['start', 'confirm', 'done'].includes(current_step);
+    },
+
+    /**
+     * Returns true if "Next" button is disabled.
+     * @param {PIN_SETUP_STEPS} step this.step_
+     * @param {Boolean} enableSubmit this.enableSubmit_
+     * @private
+     */
+    isNextDisabled_: function(step, enableSubmit) {
+      return step != PIN_SETUP_STEPS.DONE && !enableSubmit;
+    },
+
+    /**
+     * Triggers "Next" button if "Enter" key is pressed when entering password.
+     * @param {Event} e Keyboard event.
+     * @private
+     */
+    onKeypress_: function(e) {
+      if (e.key != 'Enter')
+        return;
+
+      this.onPasswordSubmitButton_();
+    },
+
+    /** @private */
+    onPasswordSubmitButton_: function() {
+      if (!this.password_)
+        return;
+
+      let password = this.password_;
+      this.password_ = '';
+      this.getToken_(password);
+    },
+
+    /** @private */
+    onTypedPasswordChange_: function() {
+      this.passwordInvalid_ = false;
+    },
+  });
+}
diff --git a/chrome/browser/resources/chromeos/login/discover/modules/images/pin_illustration_1x.svg b/chrome/browser/resources/chromeos/login/discover/modules/images/pin_illustration_1x.svg
new file mode 100644
index 0000000..0d59105
--- /dev/null
+++ b/chrome/browser/resources/chromeos/login/discover/modules/images/pin_illustration_1x.svg
@@ -0,0 +1,21 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="264" height="264">
+  <defs>
+    <path id="a" d="M0 0h228v228H0z"/>
+  </defs>
+  <g fill="none" fill-rule="evenodd">
+    <g transform="translate(18 18)">
+      <mask id="b" fill="#fff">
+        <use xlink:href="#a"/>
+      </mask>
+      <path fill="#DADCE0" d="M114 0C51.04 0 0 51.04 0 114s51.04 114 114 114 114-51.04 114-114S176.96 0 114 0m0 4c29.382 0 57.005 11.442 77.781 32.218 20.777 20.777 32.22 48.4 32.22 77.782 0 29.382-11.443 57.005-32.22 77.782C171.005 212.558 143.382 224 114.001 224c-29.384 0-57.007-11.442-77.783-32.218C15.441 171.005 4 143.382 4 114c0-29.382 11.441-57.005 32.218-77.782C56.994 15.442 84.618 4 114 4" mask="url(#b)"/>
+    </g>
+    <path fill="#D2E3FC" d="M171.62 108H93.38C85.99 108 80 114.005 80 121.412v58.177C80 186.996 85.99 193 93.38 193h78.24c7.39 0 13.38-6.004 13.38-13.41v-58.178c0-7.407-5.99-13.412-13.38-13.412m0 8.031c2.96 0 5.367 2.414 5.367 5.38v58.178c0 2.967-2.408 5.38-5.367 5.38H93.38c-2.96 0-5.367-2.413-5.367-5.38v-58.177c0-2.967 2.408-5.38 5.367-5.38h78.24"/>
+    <path stroke="#4285F4" stroke-width="4" d="M88.826 193h87.348c4.875 0 8.826-3.961 8.826-8.847v-67.306c0-4.887-3.951-8.847-8.826-8.847H88.826c-4.874 0-8.826 3.96-8.826 8.847v67.306c0 4.886 3.952 8.847 8.826 8.847z"/>
+    <path stroke="#34A853" stroke-width="4" d="M123 55c-8.815 3.705-15 12.38-15 22.49V91"/>
+    <path stroke="#FBBC05" stroke-width="4" d="M153 64c-4.331-6.616-11.902-11-20.518-11-3.364 0-6.568.668-9.482 1.876"/>
+    <path stroke="#EA4335" stroke-width="4" d="M157 94V77.964c0-4.774-1.47-9.224-4-12.964"/>
+    <path stroke="#4285F4" stroke-width="4" d="M157 107V93"/>
+    <path stroke="#34A853" stroke-width="4" d="M120 151.996l9.002 9.004L149 141"/>
+    <path d="M0 0h264v264H0z"/>
+  </g>
+</svg>
diff --git a/chrome/browser/resources/chromeos/login/discover/modules/images/pin_illustration_2x.svg b/chrome/browser/resources/chromeos/login/discover/modules/images/pin_illustration_2x.svg
new file mode 100644
index 0000000..301a912
--- /dev/null
+++ b/chrome/browser/resources/chromeos/login/discover/modules/images/pin_illustration_2x.svg
@@ -0,0 +1,21 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="528" height="528">
+  <defs>
+    <path id="a" d="M.001 0H456v456H.001z"/>
+  </defs>
+  <g fill="none" fill-rule="evenodd">
+    <g transform="translate(36 36)">
+      <mask id="b" fill="#fff">
+        <use xlink:href="#a"/>
+      </mask>
+      <path fill="#DADCE0" d="M228 0C102.08 0 0 102.08 0 228S102.08 456 228 456c125.92 0 228-102.08 228-228S353.92 0 228 0m0 8c58.765 0 114.01 22.884 155.563 64.436C425.117 113.99 448 169.236 448 228c0 58.764-22.884 114.01-64.438 155.564C342.01 425.116 286.765 448 228 448c-58.766 0-114.012-22.884-155.564-64.436C30.883 342.01 8 286.764 8 228c0-58.764 22.882-114.01 64.436-155.564C113.989 30.884 169.235 8 228 8" mask="url(#b)"/>
+    </g>
+    <path fill="#D2E3FC" d="M343.24 216H186.76c-14.779 0-26.76 12.01-26.76 26.823v116.355C160 373.991 171.981 386 186.76 386h156.48c14.78 0 26.76-12.009 26.76-26.822V242.823C370 228.01 358.02 216 343.24 216m0 16.063c5.918 0 10.734 4.827 10.734 10.76v116.355c0 5.934-4.816 10.76-10.734 10.76H186.76c-5.918 0-10.734-4.826-10.734-10.76V242.823c0-5.933 4.816-10.76 10.734-10.76h156.48"/>
+    <path stroke="#4285F4" stroke-width="8" d="M177.653 386h174.696c9.749 0 17.651-7.922 17.651-17.693V233.693c0-9.772-7.902-17.693-17.651-17.693H177.653c-9.749 0-17.653 7.92-17.653 17.693v134.614c0 9.77 7.904 17.693 17.653 17.693z"/>
+    <path stroke="#34A853" stroke-width="8" d="M246 110c-17.63 7.41-30 24.759-30 44.98V182"/>
+    <path stroke="#FBBC05" stroke-width="8" d="M306 128c-8.663-13.232-23.804-22-41.036-22A49.417 49.417 0 0 0 246 109.752"/>
+    <path stroke="#EA4335" stroke-width="8" d="M314 188v-32.072c0-9.548-2.938-18.447-8-25.928"/>
+    <path stroke="#4285F4" stroke-width="8" d="M314 214v-28"/>
+    <path stroke="#34A853" stroke-width="8" d="M240 303.993L258.005 322 298 282"/>
+    <path d="M0 0h528v528H0z"/>
+  </g>
+</svg>
diff --git a/chrome/browser/resources/chromeos/login/images/1x/reset_illustration_1x.svg b/chrome/browser/resources/chromeos/login/images/1x/reset_illustration_1x.svg
new file mode 100644
index 0000000..51d4fab
--- /dev/null
+++ b/chrome/browser/resources/chromeos/login/images/1x/reset_illustration_1x.svg
@@ -0,0 +1,14 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="264" height="264">
+  <g fill="none" fill-rule="evenodd">
+    <path fill="#EA4335" d="M96.554 122.415l6.413-6.413a2.269 2.269 0 0 1 3.207 0l6.412 6.413a2.267 2.267 0 0 1 0 3.207l-6.412 6.414a2.269 2.269 0 0 1-3.207 0l-6.413-6.413a2.269 2.269 0 0 1 0-3.208"/>
+    <path fill="#FBBC04" d="M133.766 101.358a5.667 5.667 0 0 1 0-8.016l4.81-4.81a5.67 5.67 0 0 1 8.016 8.016l-4.81 4.81a5.67 5.67 0 0 1-8.016 0"/>
+    <path fill="#34A853" d="M151.384 117.925l11.947 3.2c1.686.453 2.25 2.56 1.016 3.795l-8.745 8.745c-1.235 1.234-3.342.67-3.794-1.016l-3.2-11.947c-.453-1.686 1.09-3.23 2.776-2.777"/>
+    <path stroke="#4285F4" stroke-linecap="round" stroke-linejoin="round" stroke-width="4" d="M212 174c0 44.183-35.817 80-80 80s-80-35.817-80-80 35.817-80 80-80 80 35.817 80 80z"/>
+    <path fill="#4285F4" stroke="#4285F4" stroke-width="2" d="M135.125 164.625v6.25H157V149h-6.25v8.563C146.156 152.343 139.5 149 132 149c-6.906 0-13.156 2.813-17.688 7.313-4.53 4.5-7.312 10.78-7.312 17.687 0 13.813 11.188 25 25 25 6.906 0 13.156-2.813 17.688-7.313l-4.438-4.437c-3.406 3.406-8.094 5.5-13.25 5.5-10.344 0-18.75-8.406-18.75-18.75 0-5.156 2.094-9.844 5.5-13.25 3.406-3.406 8.094-5.5 13.25-5.5 6.906 0 12.969 3.781 16.219 9.375h-13.094z"/>
+    <path fill="#EA4335" d="M171.605 81.336l-6.413-6.413a2.268 2.268 0 0 1 0-3.206l6.413-6.413a2.269 2.269 0 0 1 3.207 0l6.412 6.413a2.266 2.266 0 0 1 0 3.206l-6.412 6.413a2.269 2.269 0 0 1-3.207 0"/>
+    <path fill="#FBBC04" d="M105.869 47.101l-6.413-6.413a2.268 2.268 0 0 1 0-3.206l6.413-6.413a2.269 2.269 0 0 1 3.207 0l6.413 6.413a2.268 2.268 0 0 1 0 3.206l-6.413 6.413a2.267 2.267 0 0 1-3.207 0"/>
+    <path fill="#4285F4" d="M129.012 64.68a5.667 5.667 0 0 1 0-8.017l4.81-4.81a5.67 5.67 0 0 1 8.016 8.016l-4.81 4.81a5.667 5.667 0 0 1-8.016 0"/>
+    <path fill="#34A853" d="M87.07 74.282l11.947 3.201c1.686.452 2.25 2.56 1.016 3.794l-8.745 8.745c-1.235 1.234-3.342.67-3.794-1.016L84.293 77.06c-.452-1.686 1.09-3.229 2.777-2.777M159 16a7 7 0 1 1-14 0 7 7 0 0 1 14 0"/>
+    <path d="M0 0h264v264H0z"/>
+  </g>
+</svg>
diff --git a/chrome/browser/resources/chromeos/login/images/2x/reset_illustration_2x.svg b/chrome/browser/resources/chromeos/login/images/2x/reset_illustration_2x.svg
new file mode 100644
index 0000000..326c80e5
--- /dev/null
+++ b/chrome/browser/resources/chromeos/login/images/2x/reset_illustration_2x.svg
@@ -0,0 +1,14 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="528" height="528">
+  <g fill="none" fill-rule="evenodd">
+    <path fill="#EA4335" d="M193.108 244.831l12.826-12.826a4.538 4.538 0 0 1 6.414 0l12.824 12.826a4.534 4.534 0 0 1 0 6.414l-12.824 12.826a4.538 4.538 0 0 1-6.414 0l-12.826-12.826a4.538 4.538 0 0 1 0-6.414"/>
+    <path fill="#FBBC04" d="M267.532 202.716c-4.428-4.428-4.428-11.606 0-16.032l9.62-9.62c4.426-4.428 11.604-4.428 16.032 0 4.426 4.428 4.426 11.606 0 16.032l-9.62 9.62c-4.428 4.426-11.606 4.426-16.032 0"/>
+    <path fill="#34A853" d="M302.769 235.85l23.894 6.401c3.372.904 4.5 5.118 2.032 7.588l-17.49 17.49c-2.47 2.468-6.684 1.34-7.588-2.032l-6.402-23.894c-.904-3.372 2.182-6.458 5.554-5.554"/>
+    <path stroke="#4285F4" stroke-linecap="round" stroke-linejoin="round" stroke-width="8" d="M424 348c0 88.366-71.634 160-160 160s-160-71.634-160-160 71.634-160 160-160 160 71.634 160 160z"/>
+    <path fill="#4285F4" stroke="#4285F4" stroke-width="4" d="M270.25 329.25v12.5H314V298h-12.5v17.125C292.312 304.687 279 298 264 298c-13.813 0-26.313 5.625-35.375 14.625-9.063 9-14.625 21.563-14.625 35.375 0 27.625 22.375 50 50 50 13.813 0 26.313-5.625 35.375-14.625L290.5 374.5c-6.813 6.813-16.188 11-26.5 11-20.688 0-37.5-16.813-37.5-37.5 0-10.313 4.188-19.688 11-26.5 6.813-6.813 16.188-11 26.5-11 13.813 0 25.938 7.563 32.438 18.75H270.25z"/>
+    <path fill="#EA4335" d="M343.21 162.672l-12.826-12.826a4.535 4.535 0 0 1 0-6.412l12.826-12.826a4.538 4.538 0 0 1 6.414 0l12.824 12.826a4.531 4.531 0 0 1 0 6.412l-12.824 12.826a4.538 4.538 0 0 1-6.414 0"/>
+    <path fill="#FBBC04" d="M211.737 94.202l-12.826-12.826a4.535 4.535 0 0 1 0-6.412l12.826-12.826a4.538 4.538 0 0 1 6.414 0l12.826 12.826a4.535 4.535 0 0 1 0 6.412l-12.826 12.826a4.534 4.534 0 0 1-6.414 0"/>
+    <path fill="#4285F4" d="M258.024 129.358c-4.428-4.426-4.428-11.604 0-16.032l9.62-9.62c4.426-4.426 11.604-4.426 16.032 0 4.426 4.428 4.426 11.606 0 16.032l-9.62 9.62c-4.428 4.428-11.606 4.428-16.032 0"/>
+    <path fill="#34A853" d="M174.14 148.564l23.894 6.402c3.372.904 4.5 5.118 2.032 7.588l-17.49 17.49c-2.47 2.468-6.684 1.34-7.588-2.032l-6.402-23.894c-.904-3.372 2.182-6.458 5.554-5.554M318 32c0 7.732-6.268 14-14 14s-14-6.268-14-14 6.268-14 14-14 14 6.268 14 14"/>
+    <path d="M0 0h528v528H0z"/>
+  </g>
+</svg>
diff --git a/chrome/browser/resources/chromeos/login/images/reset_illustration_1x.png b/chrome/browser/resources/chromeos/login/images/reset_illustration_1x.png
deleted file mode 100644
index 6966c1e..0000000
--- a/chrome/browser/resources/chromeos/login/images/reset_illustration_1x.png
+++ /dev/null
Binary files differ
diff --git a/chrome/browser/resources/chromeos/login/images/reset_illustration_2x.png b/chrome/browser/resources/chromeos/login/images/reset_illustration_2x.png
deleted file mode 100644
index 3b2c39b..0000000
--- a/chrome/browser/resources/chromeos/login/images/reset_illustration_2x.png
+++ /dev/null
Binary files differ
diff --git a/chrome/browser/resources/chromeos/login/multidevice_setup_first_run.html b/chrome/browser/resources/chromeos/login/multidevice_setup_first_run.html
index 8f8eeb8..edde59f 100644
--- a/chrome/browser/resources/chromeos/login/multidevice_setup_first_run.html
+++ b/chrome/browser/resources/chromeos/login/multidevice_setup_first_run.html
@@ -7,6 +7,7 @@
 <link rel="import" href="chrome://oobe/custom_elements.html">
 <link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/multidevice_setup_shared_css.html">
 <link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/multidevice_setup.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
 <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
 <!--
   UI for the MultiDevice setup flow when displayed after OOBE or during the
@@ -24,7 +25,63 @@
 -->
 <dom-module id="multidevice-setup-first-run">
   <template>
-    <style include="shared-style multidevice-setup-shared"></style>
+    <link rel="stylesheet" href="oobe_popup_overlay.css">
+    <style include="shared-style multidevice-setup-shared ">
+      #multidevice-help-overlay-container {
+        width: 768px;
+      }
+
+      #multidevice-help-overlay-webview {
+        border: 1px solid #d9d9d9;
+        display: block;
+        height: 450px;
+        width: 100%;
+      }
+
+      #multidevice-help-overlay-webview-container.overlay-loading > webview,
+      #multidevice-help-overlay-webview-container:not(.overlay-loading) > div {
+        display: none;
+      }
+
+      #multidevice-help-overlay-webview-container {
+        box-sizing: border-box;
+        height: 482px;
+        margin: auto;
+        padding: 24px 8px 8px 8px;
+        width: 100%;
+      }
+
+      .multidevice-help-overlay-close-top {
+        background-image: url(chrome://theme/IDR_CLOSE_DIALOG);
+        background-position: center;
+        background-repeat: no-repeat;
+        height: 14px;
+        position: absolute;
+        right: 7px;
+        top: 7px;
+        width: 14px;
+        z-index: 1;
+      }
+
+      html[dir='rtl'] .multidevice-help-overlay-close-top {
+        left: 10px;
+        right: auto;
+      }
+
+      .multidevice-help-overlay-close-top:hover {
+        background-image: url(chrome://theme/IDR_CLOSE_DIALOG_H);
+      }
+
+      .multidevice-help-overlay-close-top:active {
+        background-image: url(chrome://theme/IDR_CLOSE_DIALOG_P);
+      }
+
+      .multidevice-help-button-strip {
+        display: flex;
+        justify-content: flex-end;
+        margin: 8px;
+      }
+    </style>
     <multidevice-setup delegate="[[delegate_]]"
         on-setup-exited="onExitRequested_"
         forward-button-text="{{forwardButtonText_}}"
@@ -38,5 +95,31 @@
         <div>[[forwardButtonText_]]</div>
       </oobe-next-button>
     </multidevice-setup>
+
+    <div id="multidevice-help-overlay" class="popup-overlay"
+        hidden$="[[webviewOverlayHidden_]]">
+      <div id="multidevice-help-overlay-container"
+          class="oobe-popup not-resizable">
+        <div id="multidevice-help-overlay-close-top"
+            class="multidevice-help-overlay-close-top
+                multidevice-help-overlay-close-button"
+            on-click="hideWebviewOverlay_"
+            title="[[getOverlayCloseTopTitle_()]]">
+        </div>
+        <div id="multidevice-help-overlay-webview-container">
+          <webview id="multidevice-help-overlay-webview"
+              src="[[webviewSrc_]]"></webview>
+        </div>
+        <div class="multidevice-help-button-strip">
+          <oobe-text-button inverse id="multidevice-help-overlay-close-bottom"
+              class="multidevice-help-overlay-close-button"
+              on-click="hideWebviewOverlay_">
+            <!-- TODO(crbug.com/894537): Use string that is specific to
+                    MultiDevice. -->
+            <div i18n-content="arcOverlayClose"></div>
+          </oobe-text-button>
+        </div>
+      </div>
+    </div>
   </template>
 </dom-module>
diff --git a/chrome/browser/resources/chromeos/login/multidevice_setup_first_run.js b/chrome/browser/resources/chromeos/login/multidevice_setup_first_run.js
index 27ba0fd..ddce54e 100644
--- a/chrome/browser/resources/chromeos/login/multidevice_setup_first_run.js
+++ b/chrome/browser/resources/chromeos/login/multidevice_setup_first_run.js
@@ -50,7 +50,7 @@
   const MultiDeviceSetupFirstRun = Polymer({
     is: 'multidevice-setup-first-run',
 
-    behaviors: [WebUIListenerBehavior],
+    behaviors: [I18nBehavior, WebUIListenerBehavior],
 
     properties: {
       /** @private {!multidevice_setup.MultiDeviceSetupDelegate} */
@@ -82,6 +82,25 @@
         type: String,
         value: '',
       },
+
+      /** Whether the webview overlay should be hidden. */
+      webviewOverlayHidden_: {
+        type: Boolean,
+        value: true,
+      },
+
+      /**
+       * URL for the webview to display.
+       * @private {string|undefined}
+       */
+      webviewSrc_: {
+        type: String,
+        value: '',
+      },
+    },
+
+    listeners: {
+      'open-learn-more-webview-requested': 'onOpenLearnMoreWebviewRequested_',
     },
 
     /** @override */
@@ -99,7 +118,38 @@
     /** @private */
     onExitRequested_: function() {
       chrome.send('login.MultiDeviceSetupScreen.userActed', ['setup-finished']);
-    }
+    },
+
+    /**
+     * @param {boolean} shouldShow
+     * @param {string=} opt_url
+     * @private
+     */
+    setWebviewOverlayVisibility_: function(shouldShow, opt_url) {
+      if (opt_url) {
+        this.webviewSrc_ = opt_url;
+      }
+      this.webviewOverlayHidden_ = !shouldShow;
+    },
+
+    /** @private */
+    hideWebviewOverlay_: function() {
+      this.setWebviewOverlayVisibility_(false /* shouldShow */);
+    },
+
+    /**
+     * @param {!{detail: string}} event
+     * @private
+     */
+    onOpenLearnMoreWebviewRequested_: function(event) {
+      this.setWebviewOverlayVisibility_(
+          true /* shouldShow */, event.detail /* url */);
+    },
+
+    /** @private */
+    getOverlayCloseTopTitle_: function() {
+      return this.i18n('arcOverlayClose');
+    },
   });
 
   return {
diff --git a/chrome/browser/resources/chromeos/login/oobe_reset.css b/chrome/browser/resources/chromeos/login/oobe_reset.css
index fe2f471..99415e93 100644
--- a/chrome/browser/resources/chromeos/login/oobe_reset.css
+++ b/chrome/browser/resources/chromeos/login/oobe_reset.css
@@ -39,3 +39,8 @@
   cursor: pointer;
   pointer-events: auto;
 }
+
+#illustration {
+  height: 264px;
+  width: 264px;
+}
diff --git a/chrome/browser/resources/chromeos/login/oobe_reset.html b/chrome/browser/resources/chromeos/login/oobe_reset.html
index bd9e9dd..188c109 100644
--- a/chrome/browser/resources/chromeos/login/oobe_reset.html
+++ b/chrome/browser/resources/chromeos/login/oobe_reset.html
@@ -10,7 +10,13 @@
   <svg>
     <defs>
       <g id="alert" fill-rule="evenodd">
-        <path d="M16,2.66666667 C8.64,2.66666667 2.66666667,8.64 2.66666667,16 C2.66666667,23.36 8.64,29.3333333 16,29.3333333 C23.36,29.3333333 29.3333333,23.36 29.3333333,16 C29.3333333,8.64 23.36,2.66666667 16,2.66666667 L16,2.66666667 Z M17.3333333,22.6666667 L14.6666667,22.6666667 L14.6666667,20 L17.3333333,20 L17.3333333,22.6666667 L17.3333333,22.6666667 Z M17.3333333,17.3333333 L14.6666667,17.3333333 L14.6666667,9.33333333 L17.3333333,9.33333333 L17.3333333,17.3333333 L17.3333333,17.3333333 Z"></path>
+        <defs>
+          <path id="a" d="M14.667 20h2.666v2.667h-2.666V20zm0-10.667h2.666v8h-2.666v-8zm1.32-6.666C8.627 2.667 2.667 8.64 2.667 16s5.96 13.333 13.32 13.333c7.373 0 13.346-5.973 13.346-13.333S23.36 2.667 15.987 2.667zm.013 24c-5.893 0-10.667-4.774-10.667-10.667S10.107 5.333 16 5.333 26.667 10.107 26.667 16 21.893 26.667 16 26.667z"></path>
+        </defs>
+        <g fill="none" fill-rule="evenodd">
+          <path d="M0 0h32v32H0z"></path>
+          <use fill="#1A73E8" fill-rule="nonzero" xlink:href="#a"></use>
+        </g>
       </g>
     </defs>
   </svg>
@@ -20,7 +26,13 @@
   <svg>
     <defs>
       <g id="alert" fill-rule="evenodd">
-        <path d="M32,5.33333333 C17.28,5.33333333 5.33333333,17.28 5.33333333,32 C5.33333333,46.72 17.28,58.6666667 32,58.6666667 C46.72,58.6666667 58.6666667,46.72 58.6666667,32 C58.6666667,17.28 46.72,5.33333333 32,5.33333333 L32,5.33333333 Z M34.6666667,45.3333333 L29.3333333,45.3333333 L29.3333333,40 L34.6666667,40 L34.6666667,45.3333333 L34.6666667,45.3333333 Z M34.6666667,34.6666667 L29.3333333,34.6666667 L29.3333333,18.6666667 L34.6666667,18.6666667 L34.6666667,34.6666667 L34.6666667,34.6666667 Z"></path>
+        <defs>
+          <path id="a" d="M29.333 40h5.334v5.333h-5.334V40zm0-21.333h5.334v16h-5.334v-16zm2.64-13.334C17.253 5.333 5.333 17.28 5.333 32c0 14.72 11.92 26.667 26.64 26.667C46.72 58.667 58.667 46.72 58.667 32c0-14.72-11.947-26.667-26.694-26.667zm.027 48c-11.787 0-21.333-9.546-21.333-21.333 0-11.787 9.546-21.333 21.333-21.333 11.787 0 21.333 9.546 21.333 21.333 0 11.787-9.546 21.333-21.333 21.333z"></path>
+        </defs>
+        <g fill="none" fill-rule="evenodd">
+          <path d="M0 0h64v64H0z"></path>
+          <use fill="#1A73E8" fill-rule="nonzero" xlink:href="#a"></use>
+        </g>
       </g>
     </defs>
   </svg>
@@ -60,8 +72,8 @@
         </div>
       </div>
       <div slot="footer" class="flex layout vertical center">
-        <img srcset="images/reset_illustration_1x.png 1x,
-                     images/reset_illustration_2x.png 2x"
+        <img id="illustration" srcset="images/1x/reset_illustration_1x.svg 1x,
+                                       images/2x/reset_illustration_2x.svg 2x"
             i18n-values="alt:resetScreenIllustrationTitle">
         <div id="tpmFirmwareUpdate" class="layout horizontal"
             hidden="[[!tpmFirmwareUpdateAvailable_]]">
diff --git a/chrome/browser/resources/chromeos/login/screen_discover.html b/chrome/browser/resources/chromeos/login/screen_discover.html
index 8239f8d..a436903 100644
--- a/chrome/browser/resources/chromeos/login/screen_discover.html
+++ b/chrome/browser/resources/chromeos/login/screen_discover.html
@@ -1,3 +1,3 @@
 <div class="step right hidden" id="discover" hidden>
-  <discover-ui id="discover-impl"></discover>
+  <discover-ui id="discover-impl" first-run></discover>
 </div>
diff --git a/chrome/browser/resources/chromeos/login/screen_multidevice_setup.html b/chrome/browser/resources/chromeos/login/screen_multidevice_setup.html
index 7293931..31a4b95 100644
--- a/chrome/browser/resources/chromeos/login/screen_multidevice_setup.html
+++ b/chrome/browser/resources/chromeos/login/screen_multidevice_setup.html
@@ -2,6 +2,7 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 <link rel="import" href="chrome://oobe/custom_elements.html">
+<link rel="stylesheet" href="chrome://resources/css/overlay.css">
 
 <div class="step right hidden" id="multidevice-setup" hidden>
   <multidevice-setup-first-run id="multidevice-setup-impl">
diff --git a/chrome/browser/resources/settings/people_page/sync_page.html b/chrome/browser/resources/settings/people_page/sync_page.html
index d1e99ea..07ea937 100644
--- a/chrome/browser/resources/settings/people_page/sync_page.html
+++ b/chrome/browser/resources/settings/people_page/sync_page.html
@@ -455,25 +455,16 @@
                   aria-label="$i18n{encryptWithGoogleCredentialsLabel}">
                 $i18n{encryptWithGoogleCredentialsLabel}
               </cr-radio-button>
-              <template is="dom-if" if="[[syncPrefs.fullEncryptionBody]]">
-                <cr-radio-button name="encrypt-with-passphrase"
-                    class="list-item" disabled="[[syncPrefs.encryptAllData]]"
-                    aria-labelledby="fullEncryptionBody">
-                  <span id="fullEncryptionBody">
-                    [[syncPrefs.fullEncryptionBody]]
-                  </span>
-                </cr-radio-button>
-              </template>
-              <template is="dom-if" if="[[!syncPrefs.fullEncryptionBody]]">
-                <cr-radio-button name="encrypt-with-passphrase"
-                    class="list-item" disabled="[[syncPrefs.encryptAllData]]"
-                    aria-labelledby="encryptWithSyncPassphraseLabel">
-                  <span id="encryptWithSyncPassphraseLabel"
-                      on-click="onLearnMoreTap_">
-                    $i18nRaw{encryptWithSyncPassphraseLabel}
-                  </span>
-                </cr-radio-button>
-              </template>
+              <cr-radio-button name="encrypt-with-passphrase"
+                  class="list-item" disabled="[[syncPrefs.encryptAllData]]">
+                <span hidden="[[!syncPrefs.fullEncryptionBody]]">
+                  [[syncPrefs.fullEncryptionBody]]
+                </span>
+                <span on-click="onLearnMoreTap_"
+                    hidden="[[syncPrefs.fullEncryptionBody]]">
+                  $i18nRaw{encryptWithSyncPassphraseLabel}
+                </span>
+              </cr-radio-button>
             </paper-radio-group>
           </div>
 
diff --git a/chrome/browser/signin/signin_global_error.cc b/chrome/browser/signin/signin_global_error.cc
index e1b7b53..8e03c89 100644
--- a/chrome/browser/signin/signin_global_error.cc
+++ b/chrome/browser/signin/signin_global_error.cc
@@ -9,7 +9,7 @@
 #include "build/build_config.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/chrome_pages.h"
@@ -23,9 +23,9 @@
 #include "chrome/grit/generated_resources.h"
 #include "components/signin/core/browser/profile_management_switches.h"
 #include "components/signin/core/browser/signin_header_helper.h"
-#include "components/signin/core/browser/signin_manager.h"
 #include "components/signin/core/browser/signin_metrics.h"
 #include "net/base/url_util.h"
+#include "services/identity/public/cpp/identity_manager.h"
 #include "ui/base/l10n/l10n_util.h"
 
 #if !defined(OS_ANDROID)
@@ -116,10 +116,10 @@
   std::vector<base::string16> messages;
 
   // If the user isn't signed in, no need to display an error bubble.
-  SigninManagerBase* signin_manager =
-      SigninManagerFactory::GetForProfileIfExists(profile_);
-  if (signin_manager && !signin_manager->IsAuthenticated())
-      return messages;
+  auto* identity_manager =
+      IdentityManagerFactory::GetForProfileIfExists(profile_);
+  if (identity_manager && !identity_manager->HasPrimaryAccount())
+    return messages;
 
   if (!error_controller_->HasError())
     return messages;
diff --git a/chrome/browser/sync/test/integration/bookmarks_helper.cc b/chrome/browser/sync/test/integration/bookmarks_helper.cc
index 23547e2..b633d2c 100644
--- a/chrome/browser/sync/test/integration/bookmarks_helper.cc
+++ b/chrome/browser/sync/test/integration/bookmarks_helper.cc
@@ -45,6 +45,7 @@
 #include "components/history/core/browser/history_db_task.h"
 #include "components/history/core/browser/history_service.h"
 #include "components/history/core/browser/history_types.h"
+#include "components/sync/test/fake_server/entity_builder_factory.h"
 #include "components/sync_bookmarks/bookmark_change_processor.h"
 #include "content/public/test/test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -957,6 +958,15 @@
   return base::StringPrintf("Subsubfolder Name %d", i);
 }
 
+std::unique_ptr<syncer::LoopbackServerEntity> CreateBookmarkServerEntity(
+    const std::string& title,
+    const GURL& url) {
+  fake_server::EntityBuilderFactory entity_builder_factory;
+  fake_server::BookmarkEntityBuilder bookmark_builder =
+      entity_builder_factory.NewBookmarkEntityBuilder(title);
+  return bookmark_builder.BuildBookmark(url);
+}
+
 }  // namespace bookmarks_helper
 
 BookmarksMatchChecker::BookmarksMatchChecker()
@@ -1004,6 +1014,64 @@
   return "Waiting for bookmark count to match";
 }
 
+ServerBookmarksEqualityChecker::ServerBookmarksEqualityChecker(
+    browser_sync::ProfileSyncService* service,
+    fake_server::FakeServer* fake_server,
+    const std::vector<ExpectedBookmark>& expected_bookmarks,
+    syncer::Cryptographer* cryptographer)
+    : SingleClientStatusChangeChecker(service),
+      fake_server_(fake_server),
+      cryptographer_(cryptographer),
+      expected_bookmarks_(expected_bookmarks) {}
+
+bool ServerBookmarksEqualityChecker::IsExitConditionSatisfied() {
+  std::vector<sync_pb::SyncEntity> entities =
+      fake_server_->GetSyncEntitiesByModelType(syncer::BOOKMARKS);
+  if (expected_bookmarks_.size() != entities.size()) {
+    return false;
+  }
+
+  // Make a copy so we can remove bookmarks that were found.
+  std::vector<ExpectedBookmark> expected = expected_bookmarks_;
+  for (const sync_pb::SyncEntity& entity : entities) {
+    // If the cryptographer was provided, we expect the specifics to have
+    // encrypted data.
+    EXPECT_EQ(entity.specifics().has_encrypted(), cryptographer_ != nullptr);
+
+    sync_pb::BookmarkSpecifics actual_specifics;
+    if (entity.specifics().has_encrypted()) {
+      sync_pb::EntitySpecifics entity_specifics;
+      EXPECT_TRUE(cryptographer_->Decrypt(entity.specifics().encrypted(),
+                                          &entity_specifics));
+      actual_specifics = entity_specifics.bookmark();
+    } else {
+      actual_specifics = entity.specifics().bookmark();
+    }
+
+    auto it =
+        std::find_if(expected.begin(), expected.end(),
+                     [actual_specifics](const ExpectedBookmark& bookmark) {
+                       return actual_specifics.title() == bookmark.title &&
+                              actual_specifics.url() == bookmark.url;
+                     });
+    if (it != expected.end()) {
+      expected.erase(it);
+    } else {
+      ADD_FAILURE() << "Could not find expected bookmark with title '"
+                    << actual_specifics.title() << "' and URL '"
+                    << actual_specifics.url() << "'";
+    }
+  }
+
+  return true;
+}
+
+std::string ServerBookmarksEqualityChecker::GetDebugMessage() const {
+  return "Waiting for server-side bookmarks to match expected.";
+}
+
+ServerBookmarksEqualityChecker::~ServerBookmarksEqualityChecker() {}
+
 namespace {
 
 bool BookmarkCountsByUrlMatch(int profile,
diff --git a/chrome/browser/sync/test/integration/bookmarks_helper.h b/chrome/browser/sync/test/integration/bookmarks_helper.h
index a149ed80..ab030fee 100644
--- a/chrome/browser/sync/test/integration/bookmarks_helper.h
+++ b/chrome/browser/sync/test/integration/bookmarks_helper.h
@@ -5,13 +5,19 @@
 #ifndef CHROME_BROWSER_SYNC_TEST_INTEGRATION_BOOKMARKS_HELPER_H_
 #define CHROME_BROWSER_SYNC_TEST_INTEGRATION_BOOKMARKS_HELPER_H_
 
+#include <memory>
 #include <string>
+#include <vector>
 
 #include "base/compiler_specific.h"
 #include "chrome/browser/sync/test/integration/await_match_status_change_checker.h"
 #include "chrome/browser/sync/test/integration/multi_client_status_change_checker.h"
 #include "chrome/browser/sync/test/integration/single_client_status_change_checker.h"
+#include "components/sync/base/cryptographer.h"
+#include "components/sync/engine_impl/loopback_server/loopback_server_entity.h"
+#include "components/sync/test/fake_server/fake_server.h"
 #include "third_party/skia/include/core/SkColor.h"
+#include "url/gurl.h"
 
 class GURL;
 
@@ -223,6 +229,12 @@
 // Returns a subsubfolder name identifiable by |i|.
 std::string IndexedSubsubfolderName(int i);
 
+// Creates a server-side entity representing a bookmark with the given title and
+// URL.
+std::unique_ptr<syncer::LoopbackServerEntity> CreateBookmarkServerEntity(
+    const std::string& title,
+    const GURL& url);
+
 }  // namespace bookmarks_helper
 
 // Checker used to block until bookmarks match on all clients.
@@ -265,6 +277,37 @@
   const int expected_count_;
 };
 
+// Checker used to block until the bookmarks on the server match a given set of
+// expected bookmarks.
+class ServerBookmarksEqualityChecker : public SingleClientStatusChangeChecker {
+ public:
+  struct ExpectedBookmark {
+    std::string title;
+    GURL url;
+  };
+
+  // If a |cryptographer| is provided (i.e. is not nullptr), it is assumed that
+  // the server-side data should be encrypted, and the provided cryptographer
+  // will be used to decrypt the data prior to checking for equality.
+  ServerBookmarksEqualityChecker(
+      browser_sync::ProfileSyncService* service,
+      fake_server::FakeServer* fake_server,
+      const std::vector<ExpectedBookmark>& expected_bookmarks,
+      syncer::Cryptographer* cryptographer);
+
+  bool IsExitConditionSatisfied() override;
+  std::string GetDebugMessage() const override;
+
+  ~ServerBookmarksEqualityChecker() override;
+
+ private:
+  fake_server::FakeServer* fake_server_;
+  syncer::Cryptographer* cryptographer_;
+  const std::vector<ExpectedBookmark> expected_bookmarks_;
+
+  DISALLOW_COPY_AND_ASSIGN(ServerBookmarksEqualityChecker);
+};
+
 // Checker used to block until the actual number of bookmarks with the given url
 // match the expected count.
 class BookmarksUrlChecker : public AwaitMatchStatusChangeChecker {
diff --git a/chrome/browser/sync/test/integration/encryption_helper.cc b/chrome/browser/sync/test/integration/encryption_helper.cc
new file mode 100644
index 0000000..66a1096
--- /dev/null
+++ b/chrome/browser/sync/test/integration/encryption_helper.cc
@@ -0,0 +1,176 @@
+// 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 <string>
+#include <vector>
+
+#include "base/base64.h"
+#include "chrome/browser/sync/test/integration/encryption_helper.h"
+#include "components/browser_sync/profile_sync_service.h"
+#include "components/sync/base/passphrase_enums.h"
+#include "components/sync/base/system_encryptor.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace encryption_helper {
+
+bool GetServerNigori(fake_server::FakeServer* fake_server,
+                     sync_pb::NigoriSpecifics* nigori) {
+  std::vector<sync_pb::SyncEntity> entity_list =
+      fake_server->GetPermanentSyncEntitiesByModelType(syncer::NIGORI);
+  if (entity_list.size() != 1U) {
+    return false;
+  }
+
+  *nigori = entity_list[0].specifics().nigori();
+  return true;
+}
+
+void InitCustomPassphraseCryptographerFromNigori(
+    const sync_pb::NigoriSpecifics& nigori,
+    syncer::Cryptographer* cryptographer,
+    const std::string& passphrase) {
+  sync_pb::EncryptedData keybag = nigori.encryption_keybag();
+  cryptographer->SetPendingKeys(keybag);
+
+  std::string decoded_salt;
+  switch (syncer::ProtoKeyDerivationMethodToEnum(
+      nigori.custom_passphrase_key_derivation_method())) {
+    case syncer::KeyDerivationMethod::PBKDF2_HMAC_SHA1_1003:
+      ASSERT_TRUE(cryptographer->DecryptPendingKeys(
+          {syncer::KeyDerivationParams::CreateForPbkdf2(), passphrase}));
+      break;
+    case syncer::KeyDerivationMethod::SCRYPT_8192_8_11:
+      ASSERT_TRUE(base::Base64Decode(
+          nigori.custom_passphrase_key_derivation_salt(), &decoded_salt));
+      ASSERT_TRUE(cryptographer->DecryptPendingKeys(
+          {syncer::KeyDerivationParams::CreateForScrypt(decoded_salt),
+           passphrase}));
+      break;
+    case syncer::KeyDerivationMethod::UNSUPPORTED:
+      // This test cannot pass since we wouldn't know how to decrypt data
+      // encrypted using an unsupported method.
+      FAIL() << "Unsupported key derivation method encountered: "
+             << nigori.custom_passphrase_key_derivation_method();
+  }
+}
+
+sync_pb::NigoriSpecifics CreateCustomPassphraseNigori(
+    const syncer::KeyParams& params) {
+  syncer::KeyDerivationMethod method = params.derivation_params.method();
+
+  sync_pb::NigoriSpecifics nigori;
+  nigori.set_keybag_is_frozen(true);
+  nigori.set_keystore_migration_time(1U);
+  nigori.set_encrypt_everything(true);
+  nigori.set_passphrase_type(sync_pb::NigoriSpecifics::CUSTOM_PASSPHRASE);
+  nigori.set_custom_passphrase_key_derivation_method(
+      EnumKeyDerivationMethodToProto(method));
+
+  std::string encoded_salt;
+  switch (method) {
+    case syncer::KeyDerivationMethod::PBKDF2_HMAC_SHA1_1003:
+      // Nothing to do; no further information needs to be extracted from
+      // Nigori.
+      break;
+    case syncer::KeyDerivationMethod::SCRYPT_8192_8_11:
+      base::Base64Encode(params.derivation_params.scrypt_salt(), &encoded_salt);
+      nigori.set_custom_passphrase_key_derivation_salt(encoded_salt);
+      break;
+    case syncer::KeyDerivationMethod::UNSUPPORTED:
+      ADD_FAILURE()
+          << "Unsupported method in KeyParams, cannot construct Nigori.";
+      break;
+  }
+
+  // Nigori also contains a keybag, which is an encrypted collection of all keys
+  // that the data might be encrypted with. To create it, we construct a
+  // cryptographer, add our key to it, and use GetKeys() to dump it to the
+  // keybag (in encrypted form). So, in our case, the keybag is simply the
+  // passphrase-derived key encrypted with itself.  Note that this is usually
+  // also the case during normal Sync operation, and so the keybag from Nigori
+  // only helps the encryption machinery to know if a given key is correct (e.g.
+  // checking if a user's passphrase is correct is done by trying to decrypt the
+  // keybag using a key derived from that passphrase). However, in some migrated
+  // states, the keybag might also additionally contain an old, pre-migration
+  // key.
+  syncer::SystemEncryptor encryptor;
+  syncer::Cryptographer cryptographer(&encryptor);
+  bool add_key_result = cryptographer.AddKey(params);
+  DCHECK(add_key_result);
+  bool get_keys_result =
+      cryptographer.GetKeys(nigori.mutable_encryption_keybag());
+  DCHECK(get_keys_result);
+
+  return nigori;
+}
+
+sync_pb::EntitySpecifics GetEncryptedBookmarkEntitySpecifics(
+    const sync_pb::BookmarkSpecifics& bookmark_specifics,
+    const syncer::KeyParams& key_params) {
+  sync_pb::EntitySpecifics new_specifics;
+
+  sync_pb::EntitySpecifics wrapped_entity_specifics;
+  *wrapped_entity_specifics.mutable_bookmark() = bookmark_specifics;
+  syncer::SystemEncryptor encryptor;
+  syncer::Cryptographer cryptographer(&encryptor);
+  bool add_key_result = cryptographer.AddKey(key_params);
+  DCHECK(add_key_result);
+  bool encrypt_result = cryptographer.Encrypt(
+      wrapped_entity_specifics, new_specifics.mutable_encrypted());
+  DCHECK(encrypt_result);
+
+  new_specifics.mutable_bookmark()->set_title("encrypted");
+  new_specifics.mutable_bookmark()->set_url("encrypted");
+
+  return new_specifics;
+}
+
+void SetNigoriInFakeServer(fake_server::FakeServer* fake_server,
+                           const sync_pb::NigoriSpecifics& nigori) {
+  std::string nigori_entity_id =
+      fake_server->GetTopLevelPermanentItemId(syncer::NIGORI);
+  ASSERT_NE(nigori_entity_id, "");
+  sync_pb::EntitySpecifics nigori_entity_specifics;
+  *nigori_entity_specifics.mutable_nigori() = nigori;
+  fake_server->ModifyEntitySpecifics(nigori_entity_id, nigori_entity_specifics);
+}
+
+}  // namespace encryption_helper
+
+ServerNigoriChecker::ServerNigoriChecker(
+    browser_sync::ProfileSyncService* service,
+    fake_server::FakeServer* fake_server,
+    syncer::PassphraseType expected_passphrase_type)
+    : SingleClientStatusChangeChecker(service),
+      fake_server_(fake_server),
+      expected_passphrase_type_(expected_passphrase_type) {}
+
+bool ServerNigoriChecker::IsExitConditionSatisfied() {
+  std::vector<sync_pb::SyncEntity> nigori_entities =
+      fake_server_->GetPermanentSyncEntitiesByModelType(syncer::NIGORI);
+  EXPECT_LE(nigori_entities.size(), 1U);
+  return !nigori_entities.empty() &&
+         syncer::ProtoPassphraseTypeToEnum(
+             nigori_entities[0].specifics().nigori().passphrase_type()) ==
+             expected_passphrase_type_;
+}
+
+std::string ServerNigoriChecker::GetDebugMessage() const {
+  return "Waiting for a Nigori node with the proper passphrase type to become "
+         "available on the server.";
+}
+
+PassphraseRequiredStateChecker::PassphraseRequiredStateChecker(
+    browser_sync::ProfileSyncService* service,
+    bool desired_state)
+    : SingleClientStatusChangeChecker(service), desired_state_(desired_state) {}
+
+bool PassphraseRequiredStateChecker::IsExitConditionSatisfied() {
+  return service()->IsPassphraseRequiredForDecryption() == desired_state_;
+}
+
+std::string PassphraseRequiredStateChecker::GetDebugMessage() const {
+  return "Waiting until decryption passphrase is " +
+         std::string(desired_state_ ? "required" : "not required");
+}
diff --git a/chrome/browser/sync/test/integration/encryption_helper.h b/chrome/browser/sync/test/integration/encryption_helper.h
new file mode 100644
index 0000000..5b560c1
--- /dev/null
+++ b/chrome/browser/sync/test/integration/encryption_helper.h
@@ -0,0 +1,79 @@
+// 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_SYNC_TEST_INTEGRATION_ENCRYPTION_HELPER_H_
+#define CHROME_BROWSER_SYNC_TEST_INTEGRATION_ENCRYPTION_HELPER_H_
+
+#include <string>
+
+#include "chrome/browser/sync/test/integration/single_client_status_change_checker.h"
+#include "components/sync/base/cryptographer.h"
+#include "components/sync/protocol/nigori_specifics.pb.h"
+#include "components/sync/test/fake_server/fake_server.h"
+
+namespace encryption_helper {
+
+// Given a |fake_server|, fetches its Nigori node and writes it to the
+// proto pointed to by |nigori|. Returns false if the server does not contain
+// exactly one Nigori node.
+bool GetServerNigori(fake_server::FakeServer* fake_server,
+                     sync_pb::NigoriSpecifics* nigori);
+
+// Given a |fake_server|, sets the Nigori instance stored in it to |nigori|.
+void SetNigoriInFakeServer(fake_server::FakeServer* fake_server,
+                           const sync_pb::NigoriSpecifics& nigori);
+
+// Given a |nigori| with CUSTOM_PASSPHRASE passphrase type, initializes the
+// given |cryptographer| with the key described in it. Since the key inside the
+// Nigori is encrypted (by design), the provided |passphrase| will be used to
+// decrypt it. This function will fail the test (using ASSERT) if the Nigori is
+// not a custom passphrase one, or if the key cannot be decrypted.
+void InitCustomPassphraseCryptographerFromNigori(
+    const sync_pb::NigoriSpecifics& nigori,
+    syncer::Cryptographer* cryptographer,
+    const std::string& passphrase);
+
+// Returns an EntitySpecifics containing encrypted data corresponding to the
+// provided BookmarkSpecifics and encrypted using the given |key_params|.
+sync_pb::EntitySpecifics GetEncryptedBookmarkEntitySpecifics(
+    const sync_pb::BookmarkSpecifics& specifics,
+    const syncer::KeyParams& key_params);
+
+// Creates a NigoriSpecifics that describes encryption using a custom passphrase
+// with the given key parameters.
+sync_pb::NigoriSpecifics CreateCustomPassphraseNigori(
+    const syncer::KeyParams& params);
+
+}  // namespace encryption_helper
+
+// Checker used to block until a Nigori with a given passphrase type is
+// available on the server.
+class ServerNigoriChecker : public SingleClientStatusChangeChecker {
+ public:
+  ServerNigoriChecker(browser_sync::ProfileSyncService* service,
+                      fake_server::FakeServer* fake_server,
+                      syncer::PassphraseType expected_passphrase_type);
+
+  bool IsExitConditionSatisfied() override;
+  std::string GetDebugMessage() const override;
+
+ private:
+  fake_server::FakeServer* fake_server_;
+  syncer::PassphraseType expected_passphrase_type_;
+};
+
+// Checker used to block until Sync requires or stops requiring a passphrase.
+class PassphraseRequiredStateChecker : public SingleClientStatusChangeChecker {
+ public:
+  PassphraseRequiredStateChecker(browser_sync::ProfileSyncService* service,
+                                 bool desired_state);
+
+  bool IsExitConditionSatisfied() override;
+  std::string GetDebugMessage() const override;
+
+ private:
+  bool desired_state_;
+};
+
+#endif  // CHROME_BROWSER_SYNC_TEST_INTEGRATION_ENCRYPTION_HELPER_H_
diff --git a/chrome/browser/sync/test/integration/profile_sync_service_harness.cc b/chrome/browser/sync/test/integration/profile_sync_service_harness.cc
index 5814613..5e99091f 100644
--- a/chrome/browser/sync/test/integration/profile_sync_service_harness.cc
+++ b/chrome/browser/sync/test/integration/profile_sync_service_harness.cc
@@ -182,7 +182,7 @@
 #endif  // !OS_CHROMEOS
 
 bool ProfileSyncServiceHarness::SetupSync() {
-  bool result = SetupSync(syncer::UserSelectableTypes(), false);
+  bool result = SetupSync(syncer::UserSelectableTypes());
   if (!result) {
     LOG(ERROR) << profile_debug_name_ << ": SetupSync failed. Syncer status:\n"
                << GetServiceStatus();
@@ -193,7 +193,9 @@
 }
 
 bool ProfileSyncServiceHarness::SetupSyncForClearingServerData() {
-  bool result = SetupSync(syncer::UserSelectableTypes(), true);
+  bool result = SetupSyncImpl(syncer::UserSelectableTypes(),
+                              /*skip_passphrase_verification=*/true,
+                              /*encryption_passphrase=*/base::nullopt);
   if (!result) {
     LOG(ERROR) << profile_debug_name_
                << ": SetupSyncForClear failed. Syncer status:\n"
@@ -204,8 +206,47 @@
   return result;
 }
 
-bool ProfileSyncServiceHarness::SetupSync(syncer::ModelTypeSet synced_datatypes,
-                                          bool skip_passphrase_verification) {
+bool ProfileSyncServiceHarness::SetupSync(
+    syncer::ModelTypeSet synced_datatypes) {
+  return SetupSyncImpl(synced_datatypes, /*skip_passphrase_verification=*/false,
+                       /*encryption_passphrase=*/base::nullopt);
+}
+
+bool ProfileSyncServiceHarness::SetupSyncWithEncryptionPassphrase(
+    syncer::ModelTypeSet synced_datatypes,
+    const std::string& passphrase) {
+  return SetupSyncImpl(synced_datatypes, /*skip_passphrase_verification=*/false,
+                       passphrase);
+}
+
+bool ProfileSyncServiceHarness::SetupSyncWithDecryptionPassphrase(
+    syncer::ModelTypeSet synced_datatypes,
+    const std::string& passphrase) {
+  if (!SetupSyncImpl(synced_datatypes, /*skip_passphrase_verification=*/true,
+                     /*encryption_passphrase=*/base::nullopt)) {
+    return false;
+  }
+
+  DVLOG(1) << "Setting decryption passphrase.";
+  if (!service_->SetDecryptionPassphrase(passphrase)) {
+    // This is not a fatal failure, as some tests intentionally pass an
+    // incorrect passphrase. If this happens, Sync will be set up but will have
+    // encountered cryptographer errors for the passphrase-encrypted datatypes.
+    LOG(INFO) << "SetDecryptionPassphrase() failed.";
+  }
+  // Since SetupSyncImpl() was called with skip_passphrase_verification == true,
+  // it will not have called FinishSyncSetup(). FinishSyncSetup() is in charge
+  // of calling ProfileSyncService::SetFirstSetupComplete(), and without that,
+  // Sync will still be in setup mode and Sync-the-feature will be disabled.
+  // Therefore, we call FinishSyncSetup() here explicitly.
+  FinishSyncSetup();
+  return true;
+}
+
+bool ProfileSyncServiceHarness::SetupSyncImpl(
+    syncer::ModelTypeSet synced_datatypes,
+    bool skip_passphrase_verification,
+    const base::Optional<std::string>& encryption_passphrase) {
   DCHECK(!profile_->IsLegacySupervised())
       << "SetupSync should not be used for legacy supervised users.";
 
@@ -243,6 +284,12 @@
     service()->OnUserChoseDatatypes(sync_everything, synced_datatypes);
   }
 
+  if (encryption_passphrase.has_value()) {
+    service()->SetEncryptionPassphrase(
+        encryption_passphrase.value(),
+        syncer::SyncService::PassphraseType::EXPLICIT);
+  }
+
   // Notify ProfileSyncService that we are done with configuration.
   if (skip_passphrase_verification) {
     sync_blocker_.reset();
diff --git a/chrome/browser/sync/test/integration/profile_sync_service_harness.h b/chrome/browser/sync/test/integration/profile_sync_service_harness.h
index dff401d..f38d1d5 100644
--- a/chrome/browser/sync/test/integration/profile_sync_service_harness.h
+++ b/chrome/browser/sync/test/integration/profile_sync_service_harness.h
@@ -11,6 +11,7 @@
 
 #include "base/compiler_specific.h"
 #include "base/macros.h"
+#include "base/optional.h"
 #include "components/browser_sync/profile_sync_service.h"
 #include "components/sync/base/model_type.h"
 #include "components/sync/engine/cycle/sync_cycle_snapshot.h"
@@ -62,11 +63,21 @@
   // StopSyncService(), StartSyncService() directly after.
   bool SetupSyncForClearingServerData();
 
-  // Both SetupSync and SetupSyncForClearingServerData call into this method.
-  // Same as the above method, but enables sync only for the datatypes contained
-  // in |synced_datatypes|.
-  bool SetupSync(syncer::ModelTypeSet synced_datatypes,
-                 bool skip_passphrase_verification = false);
+  // Enables and configures sync only for the given |synced_datatypes|. Returns
+  // true only after sync has been fully initialized and authenticated, and we
+  // are ready to process changes.
+  bool SetupSync(syncer::ModelTypeSet synced_datatypes);
+
+  // Same as SetupSync(), but also sets the given encryption passphrase during
+  // setup.
+  bool SetupSyncWithEncryptionPassphrase(syncer::ModelTypeSet synced_datatypes,
+                                         const std::string& passphrase);
+
+  // Same as SetupSync(), but also sets the given decryption passphrase during
+  // setup. If the passphrase is incorrect, this method will still return true
+  // and Sync will be operational but with undecryptable datatypes disabled.
+  bool SetupSyncWithDecryptionPassphrase(syncer::ModelTypeSet synced_datatypes,
+                                         const std::string& passphrase);
 
   // Signals that sync setup is complete, and that PSS may begin syncing.
   // Typically SetupSync does this automatically, but if that returned false,
@@ -146,6 +157,16 @@
                             const std::string& password,
                             SigninType signin_type);
 
+  // If |encryption_passphrase| has a value, it will be set during setup. If
+  // not, no custom passphrase will be set. If |skip_passphrase_verification| is
+  // true and Sync requires a passphrase, FinishSyncSetup() will not be called,
+  // in order to give the caller a chance to provide the passphrase using
+  // SetDecryptionPassphrase(). After that, the caller needs to call
+  // FinishSyncSetup() manually.
+  bool SetupSyncImpl(syncer::ModelTypeSet synced_datatypes,
+                     bool skip_passphrase_verification,
+                     const base::Optional<std::string>& encryption_passphrase);
+
   // Gets detailed status from |service_| in pretty-printable form.
   std::string GetServiceStatus();
 
diff --git a/chrome/browser/sync/test/integration/single_client_custom_passphrase_sync_test.cc b/chrome/browser/sync/test/integration/single_client_custom_passphrase_sync_test.cc
new file mode 100644
index 0000000..d9d59a95
--- /dev/null
+++ b/chrome/browser/sync/test/integration/single_client_custom_passphrase_sync_test.cc
@@ -0,0 +1,364 @@
+// 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/sync/test/integration/bookmarks_helper.h"
+#include "chrome/browser/sync/test/integration/encryption_helper.h"
+#include "chrome/browser/sync/test/integration/sync_test.h"
+#include "components/browser_sync/profile_sync_service.h"
+#include "components/sync/base/cryptographer.h"
+#include "components/sync/base/passphrase_enums.h"
+#include "components/sync/base/sync_base_switches.h"
+#include "components/sync/base/system_encryptor.h"
+#include "components/sync/engine/sync_engine_switches.h"
+
+namespace {
+
+using bookmarks_helper::AddURL;
+using bookmarks_helper::CreateBookmarkServerEntity;
+using encryption_helper::CreateCustomPassphraseNigori;
+using encryption_helper::GetEncryptedBookmarkEntitySpecifics;
+using encryption_helper::GetServerNigori;
+using encryption_helper::InitCustomPassphraseCryptographerFromNigori;
+using encryption_helper::SetNigoriInFakeServer;
+using fake_server::FakeServer;
+using sync_pb::EncryptedData;
+using sync_pb::NigoriSpecifics;
+using sync_pb::SyncEntity;
+using syncer::Cryptographer;
+using syncer::KeyDerivationParams;
+using syncer::KeyParams;
+using syncer::LoopbackServerEntity;
+using syncer::ModelType;
+using syncer::ModelTypeSet;
+using syncer::PassphraseType;
+using syncer::ProtoPassphraseTypeToEnum;
+using syncer::SyncService;
+using syncer::SystemEncryptor;
+
+class DatatypeCommitCountingFakeServerObserver : public FakeServer::Observer {
+ public:
+  explicit DatatypeCommitCountingFakeServerObserver(FakeServer* fake_server)
+      : fake_server_(fake_server) {
+    fake_server->AddObserver(this);
+  }
+
+  void OnCommit(const std::string& committer_id,
+                ModelTypeSet committed_model_types) override {
+    for (ModelType type : committed_model_types) {
+      ++datatype_commit_counts_[type];
+    }
+  }
+
+  int GetCommitCountForDatatype(ModelType type) {
+    return datatype_commit_counts_[type];
+  }
+
+  ~DatatypeCommitCountingFakeServerObserver() override {
+    fake_server_->RemoveObserver(this);
+  }
+
+ private:
+  FakeServer* fake_server_;
+  std::map<syncer::ModelType, int> datatype_commit_counts_;
+};
+
+// These tests use a gray-box testing approach to verify that the data committed
+// to the server is encrypted properly, and that properly-encrypted data from
+// the server is successfully decrypted by the client. They also verify that the
+// key derivation methods are set, read and handled properly. They do not,
+// however, directly ensure that two clients syncing through the same account
+// will be able to access each others' data in the presence of a custom
+// passphrase. For this, a separate two-client test will be used.
+//
+// TODO(davidovic): Add two-client tests and update the above comment.
+class SingleClientCustomPassphraseSyncTest : public SyncTest {
+ public:
+  SingleClientCustomPassphraseSyncTest() : SyncTest(SINGLE_CLIENT) {}
+  ~SingleClientCustomPassphraseSyncTest() override {}
+
+  // Waits until the given set of bookmarks appears on the server, encrypted
+  // according to the server-side Nigori and with the given passphrase.
+  bool WaitForEncryptedServerBookmarks(
+      const std::vector<ServerBookmarksEqualityChecker::ExpectedBookmark>&
+          expected_bookmarks,
+      const std::string& passphrase) {
+    auto cryptographer = CreateCryptographerFromServerNigori(passphrase);
+    return ServerBookmarksEqualityChecker(GetSyncService(), GetFakeServer(),
+                                          expected_bookmarks,
+                                          cryptographer.get())
+        .Wait();
+  }
+
+  // Waits until the given set of bookmarks appears on the server, encrypted
+  // with the precise KeyParams given.
+  bool WaitForEncryptedServerBookmarks(
+      const std::vector<ServerBookmarksEqualityChecker::ExpectedBookmark>&
+          expected_bookmarks,
+      const KeyParams& key_params) {
+    auto cryptographer = CreateCryptographerWithKeyParams(key_params);
+    return ServerBookmarksEqualityChecker(GetSyncService(), GetFakeServer(),
+                                          expected_bookmarks,
+                                          cryptographer.get())
+        .Wait();
+  }
+
+  bool WaitForUnencryptedServerBookmarks(
+      const std::vector<ServerBookmarksEqualityChecker::ExpectedBookmark>&
+          expected_bookmarks) {
+    return ServerBookmarksEqualityChecker(GetSyncService(), GetFakeServer(),
+                                          expected_bookmarks,
+                                          /*cryptographer=*/nullptr)
+        .Wait();
+  }
+
+  bool WaitForNigori(PassphraseType expected_passphrase_type) {
+    return ServerNigoriChecker(GetSyncService(), GetFakeServer(),
+                               expected_passphrase_type)
+        .Wait();
+  }
+
+  bool WaitForPassphraseRequiredState(bool desired_state) {
+    return PassphraseRequiredStateChecker(GetSyncService(), desired_state)
+        .Wait();
+  }
+
+  bool WaitForClientBookmarkWithTitle(std::string title) {
+    return BookmarksTitleChecker(/*profile_index=*/0, title,
+                                 /*expected_count=*/1)
+        .Wait();
+  }
+
+  browser_sync::ProfileSyncService* GetSyncService() {
+    return SyncTest::GetSyncService(0);
+  }
+
+  // When the cryptographer is initialized with a passphrase, it uses the key
+  // derivation method and other parameters from the server-side Nigori. Thus,
+  // checking that the server-side Nigori contains the desired key derivation
+  // method and checking that the server-side encrypted bookmarks can be
+  // decrypted using a cryptographer initialized with this function is
+  // sufficient to determine that a given key derivation method is being
+  // correctly used for encryption.
+  std::unique_ptr<Cryptographer> CreateCryptographerFromServerNigori(
+      const std::string& passphrase) {
+    NigoriSpecifics nigori;
+    EXPECT_TRUE(GetServerNigori(GetFakeServer(), &nigori));
+    EXPECT_EQ(ProtoPassphraseTypeToEnum(nigori.passphrase_type()),
+              PassphraseType::CUSTOM_PASSPHRASE);
+    auto cryptographer = std::make_unique<Cryptographer>(&system_encryptor_);
+    InitCustomPassphraseCryptographerFromNigori(nigori, cryptographer.get(),
+                                                passphrase);
+    return cryptographer;
+  }
+
+  // A cryptographer initialized with the given KeyParams has not "seen" the
+  // server-side Nigori, and so any data decryptable by such a cryptographer
+  // does not depend on external info.
+  std::unique_ptr<Cryptographer> CreateCryptographerWithKeyParams(
+      const KeyParams& key_params) {
+    auto cryptographer = std::make_unique<Cryptographer>(&system_encryptor_);
+    cryptographer->AddKey(key_params);
+    return cryptographer;
+  }
+
+  void SetScryptFeaturesState(bool force_disabled,
+                              bool use_for_new_passphrases) {
+    std::vector<base::Feature> enabled_features;
+    std::vector<base::Feature> disabled_features;
+    if (force_disabled) {
+      enabled_features.push_back(
+          switches::kSyncForceDisableScryptForCustomPassphrase);
+    } else {
+      disabled_features.push_back(
+          switches::kSyncForceDisableScryptForCustomPassphrase);
+    }
+    if (use_for_new_passphrases) {
+      enabled_features.push_back(
+          switches::kSyncUseScryptForNewCustomPassphrases);
+    } else {
+      disabled_features.push_back(
+          switches::kSyncUseScryptForNewCustomPassphrases);
+    }
+    feature_list_.InitWithFeatures(enabled_features, disabled_features);
+  }
+
+  void InjectEncryptedServerBookmark(const std::string& title,
+                                     const GURL& url,
+                                     const KeyParams& key_params) {
+    std::unique_ptr<LoopbackServerEntity> server_entity =
+        CreateBookmarkServerEntity(title, url);
+    server_entity->SetSpecifics(GetEncryptedBookmarkEntitySpecifics(
+        server_entity->GetSpecifics().bookmark(), key_params));
+    GetFakeServer()->InjectEntity(std::move(server_entity));
+  }
+
+ private:
+  SystemEncryptor system_encryptor_;
+  base::test::ScopedFeatureList feature_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(SingleClientCustomPassphraseSyncTest);
+};
+
+IN_PROC_BROWSER_TEST_F(SingleClientCustomPassphraseSyncTest,
+                       CommitsEncryptedData) {
+  SetEncryptionPassphraseForClient(/*index=*/0, "hunter2");
+  ASSERT_TRUE(SetupSync());
+
+  ASSERT_TRUE(
+      AddURL(/*profile=*/0, "Hello world", GURL("https://google.com/")));
+  ASSERT_TRUE(
+      AddURL(/*profile=*/0, "Bookmark #2", GURL("https://example.com/")));
+  ASSERT_TRUE(WaitForNigori(PassphraseType::CUSTOM_PASSPHRASE));
+
+  EXPECT_TRUE(WaitForEncryptedServerBookmarks(
+      {{"Hello world", GURL("https://google.com/")},
+       {"Bookmark #2", GURL("https://example.com/")}},
+      /*passphrase=*/"hunter2"));
+}
+
+IN_PROC_BROWSER_TEST_F(SingleClientCustomPassphraseSyncTest,
+                       CommitsEncryptedDataUsingPbkdf2WhenScryptDisabled) {
+  SetScryptFeaturesState(/*force_disabled=*/false,
+                         /*use_for_new_passphrases=*/false);
+  SetEncryptionPassphraseForClient(/*index=*/0, "hunter2");
+  ASSERT_TRUE(SetupSync());
+  ASSERT_TRUE(AddURL(/*profile=*/0, "PBKDF2 encrypted",
+                     GURL("https://google.com/pbkdf2-encrypted")));
+
+  ASSERT_TRUE(WaitForNigori(PassphraseType::CUSTOM_PASSPHRASE));
+  NigoriSpecifics nigori;
+  EXPECT_TRUE(GetServerNigori(GetFakeServer(), &nigori));
+  EXPECT_EQ(nigori.custom_passphrase_key_derivation_method(),
+            sync_pb::NigoriSpecifics::PBKDF2_HMAC_SHA1_1003);
+  EXPECT_TRUE(WaitForEncryptedServerBookmarks(
+      {{"PBKDF2 encrypted", GURL("https://google.com/pbkdf2-encrypted")}},
+      /*passphrase=*/"hunter2"));
+}
+
+IN_PROC_BROWSER_TEST_F(SingleClientCustomPassphraseSyncTest,
+                       CommitsEncryptedDataUsingScryptWhenScryptEnabled) {
+  SetScryptFeaturesState(/*force_disabled=*/false,
+                         /*use_for_new_passphrases=*/true);
+  SetEncryptionPassphraseForClient(/*index=*/0, "hunter2");
+  ASSERT_TRUE(SetupSync());
+
+  ASSERT_TRUE(AddURL(/*profile=*/0, "scrypt encrypted",
+                     GURL("https://google.com/scrypt-encrypted")));
+
+  ASSERT_TRUE(WaitForNigori(PassphraseType::CUSTOM_PASSPHRASE));
+  NigoriSpecifics nigori;
+  EXPECT_TRUE(GetServerNigori(GetFakeServer(), &nigori));
+  EXPECT_EQ(nigori.custom_passphrase_key_derivation_method(),
+            sync_pb::NigoriSpecifics::SCRYPT_8192_8_11);
+  EXPECT_TRUE(WaitForEncryptedServerBookmarks(
+      {{"scrypt encrypted", GURL("https://google.com/scrypt-encrypted")}},
+      /*passphrase=*/"hunter2"));
+}
+
+IN_PROC_BROWSER_TEST_F(SingleClientCustomPassphraseSyncTest,
+                       CanDecryptPbkdf2KeyEncryptedData) {
+  KeyParams key_params = {KeyDerivationParams::CreateForPbkdf2(), "hunter2"};
+  InjectEncryptedServerBookmark("PBKDF2-encrypted bookmark",
+                                GURL("http://example.com/doesnt-matter"),
+                                key_params);
+  SetNigoriInFakeServer(GetFakeServer(),
+                        CreateCustomPassphraseNigori(key_params));
+  SetDecryptionPassphraseForClient(/*index=*/0, "hunter2");
+
+  ASSERT_TRUE(SetupSync());
+  EXPECT_TRUE(WaitForPassphraseRequiredState(/*desired_state=*/false));
+
+  EXPECT_TRUE(WaitForClientBookmarkWithTitle("PBKDF2-encrypted bookmark"));
+}
+
+IN_PROC_BROWSER_TEST_F(SingleClientCustomPassphraseSyncTest,
+                       CanDecryptScryptKeyEncryptedDataWhenScryptNotDisabled) {
+  SetScryptFeaturesState(/*force_disabled=*/false,
+                         /*used_for_new_passphrases_=*/false);
+  KeyParams key_params = {
+      KeyDerivationParams::CreateForScrypt("someConstantSalt"), "hunter2"};
+  InjectEncryptedServerBookmark("scypt-encrypted bookmark",
+                                GURL("http://example.com/doesnt-matter"),
+                                key_params);
+  SetNigoriInFakeServer(GetFakeServer(),
+                        CreateCustomPassphraseNigori(key_params));
+  SetDecryptionPassphraseForClient(/*index=*/0, "hunter2");
+
+  ASSERT_TRUE(SetupSync());
+  EXPECT_TRUE(WaitForPassphraseRequiredState(/*desired_state=*/false));
+
+  EXPECT_TRUE(WaitForClientBookmarkWithTitle("scypt-encrypted bookmark"));
+}
+
+IN_PROC_BROWSER_TEST_F(SingleClientCustomPassphraseSyncTest,
+                       CannotDecryptScryptKeyEncryptedDataWhenScryptDisabled) {
+  KeyParams key_params = {
+      KeyDerivationParams::CreateForScrypt("someConstantSalt"), "hunter2"};
+  sync_pb::NigoriSpecifics nigori = CreateCustomPassphraseNigori(key_params);
+  InjectEncryptedServerBookmark("scypt-encrypted bookmark",
+                                GURL("http://example.com/doesnt-matter"),
+                                key_params);
+  // Can only set feature state now because creating a Nigori and injecting an
+  // encrypted bookmark both require key derivation using scrypt.
+  SetScryptFeaturesState(/*force_disabled=*/true,
+                         /*used_for_new_passphrases_=*/false);
+  SetNigoriInFakeServer(GetFakeServer(), nigori);
+  SetDecryptionPassphraseForClient(/*index=*/0, "hunter2");
+
+  ASSERT_TRUE(SetupSync());
+
+  EXPECT_TRUE(WaitForPassphraseRequiredState(/*desired_state=*/true));
+}
+
+IN_PROC_BROWSER_TEST_F(SingleClientCustomPassphraseSyncTest,
+                       DoesNotLeakUnencryptedData) {
+  SetScryptFeaturesState(/*force_disabled=*/false,
+                         /*use_for_new_passphrases=*/false);
+  SetEncryptionPassphraseForClient(/*index=*/0, "hunter2");
+  DatatypeCommitCountingFakeServerObserver observer(GetFakeServer());
+  ASSERT_TRUE(SetupSync());
+
+  ASSERT_TRUE(AddURL(/*profile=*/0, "Should be encrypted",
+                     GURL("https://google.com/encrypted")));
+
+  ASSERT_TRUE(WaitForNigori(PassphraseType::CUSTOM_PASSPHRASE));
+  // If WaitForEncryptedServerBookmarks() succeeds, that means that a
+  // cryptographer initialized with only the key params was able to decrypt the
+  // data, so the data must be encrypted using a passphrase-derived key (and not
+  // e.g. a keystore key), because that cryptographer has never seen the
+  // server-side Nigori. Furthermore, if a bookmark commit has happened only
+  // once, we are certain that no bookmarks other than those we've verified to
+  // be encrypted have been committed.
+  EXPECT_TRUE(WaitForEncryptedServerBookmarks(
+      {{"Should be encrypted", GURL("https://google.com/encrypted")}},
+      {KeyDerivationParams::CreateForPbkdf2(), "hunter2"}));
+  EXPECT_EQ(observer.GetCommitCountForDatatype(syncer::BOOKMARKS), 1);
+}
+
+IN_PROC_BROWSER_TEST_F(SingleClientCustomPassphraseSyncTest,
+                       ReencryptsDataWhenPassphraseIsSet) {
+  SetScryptFeaturesState(/*force_disabled=*/false,
+                         /*use_for_new_passphrases=*/false);
+  ASSERT_TRUE(SetupSync());
+  ASSERT_TRUE(WaitForNigori(PassphraseType::KEYSTORE_PASSPHRASE));
+  ASSERT_TRUE(AddURL(/*profile=*/0, "Re-encryption is great",
+                     GURL("https://google.com/re-encrypted")));
+  std::vector<ServerBookmarksEqualityChecker::ExpectedBookmark> expected = {
+      {"Re-encryption is great", GURL("https://google.com/re-encrypted")}};
+  ASSERT_TRUE(WaitForUnencryptedServerBookmarks(expected));
+
+  GetSyncService()->SetEncryptionPassphrase(
+      "hunter2", SyncService::PassphraseType::EXPLICIT);
+  ASSERT_TRUE(WaitForNigori(PassphraseType::CUSTOM_PASSPHRASE));
+
+  // If WaitForEncryptedServerBookmarks() succeeds, that means that a
+  // cryptographer initialized with only the key params was able to decrypt the
+  // data, so the data must be encrypted using a passphrase-derived key (and not
+  // e.g. the previous keystore key which was stored in the Nigori keybag),
+  // because that cryptographer has never seen the server-side Nigori.
+  EXPECT_TRUE(WaitForEncryptedServerBookmarks(
+      expected, {KeyDerivationParams::CreateForPbkdf2(), "hunter2"}));
+}
+
+}  // namespace
diff --git a/chrome/browser/sync/test/integration/sync_test.cc b/chrome/browser/sync/test/integration/sync_test.cc
index 328a8ef8..cc0e9f0 100644
--- a/chrome/browser/sync/test/integration/sync_test.cc
+++ b/chrome/browser/sync/test/integration/sync_test.cc
@@ -643,6 +643,20 @@
   fake_server_->RemoveObserver(fake_server_invalidation_services_[index]);
 }
 
+void SyncTest::SetEncryptionPassphraseForClient(int index,
+                                                const std::string& passphrase) {
+  // Must be called before client initialization.
+  DCHECK(clients_.empty());
+  client_encryption_passphrases_[index] = passphrase;
+}
+
+void SyncTest::SetDecryptionPassphraseForClient(int index,
+                                                const std::string& passphrase) {
+  // Must be called before client initialization.
+  DCHECK(clients_.empty());
+  client_decryption_passphrases_[index] = passphrase;
+}
+
 void SyncTest::SetupMockGaiaResponsesForProfile(Profile* profile) {
   ChromeSigninClient* signin_client = static_cast<ChromeSigninClient*>(
       ChromeSigninClientFactory::GetForProfile(profile));
@@ -782,8 +796,35 @@
 
   // Sync each of the profiles.
   for (; clientIndex < num_clients_; clientIndex++) {
+    ProfileSyncServiceHarness* client = GetClient(clientIndex);
     DVLOG(1) << "Setting up " << clientIndex << " client";
-    if (!GetClient(clientIndex)->SetupSync()) {
+
+    auto decryption_passphrase_it =
+        client_decryption_passphrases_.find(clientIndex);
+    auto encryption_passphrase_it =
+        client_encryption_passphrases_.find(clientIndex);
+    bool decryption_passphrase_provided =
+        (decryption_passphrase_it != client_decryption_passphrases_.end());
+    bool encryption_passphrase_provided =
+        (encryption_passphrase_it != client_encryption_passphrases_.end());
+    if (decryption_passphrase_provided && encryption_passphrase_provided) {
+      LOG(FATAL) << "Both an encryption and decryption passphrase were "
+                    "provided for the client. This is disallowed.";
+      return false;
+    }
+
+    bool setup_succeeded;
+    if (encryption_passphrase_provided) {
+      setup_succeeded = client->SetupSyncWithEncryptionPassphrase(
+          syncer::UserSelectableTypes(), encryption_passphrase_it->second);
+    } else if (decryption_passphrase_provided) {
+      setup_succeeded = client->SetupSyncWithDecryptionPassphrase(
+          syncer::UserSelectableTypes(), decryption_passphrase_it->second);
+    } else {
+      setup_succeeded = client->SetupSync(syncer::UserSelectableTypes());
+    }
+
+    if (!setup_succeeded) {
       LOG(FATAL) << "SetupSync() failed.";
       return false;
     }
diff --git a/chrome/browser/sync/test/integration/sync_test.h b/chrome/browser/sync/test/integration/sync_test.h
index ec474802..41e8ea51 100644
--- a/chrome/browser/sync/test/integration/sync_test.h
+++ b/chrome/browser/sync/test/integration/sync_test.h
@@ -292,6 +292,23 @@
   // Stops notificatinos being sent to a client.
   void DisableNotificationsForClient(int index);
 
+  // Sets a decryption passphrase to be used for a client. The passphrase will
+  // be provided to the client during initialization, before Sync starts. It is
+  // an error to provide both a decryption and encryption passphrases for one
+  // client.
+  void SetDecryptionPassphraseForClient(int index,
+                                        const std::string& passphrase);
+
+  // Sets an explicit encryption passphrase to be used for a client. The
+  // passphrase will be set for the client during initialization, before Sync
+  // starts. An encryption passphrase can be also enabled after initialization,
+  // but using this method ensures that Sync is never enabled when there is no
+  // passphrase, which allows tests to check for unencrypted data leaks. It is
+  // an error to provide both a decryption and encryption passphrases for one
+  // client.
+  void SetEncryptionPassphraseForClient(int index,
+                                        const std::string& passphrase);
+
   // Sets up fake responses for kClientLoginUrl, kIssueAuthTokenUrl,
   // kGetUserInfoUrl and kSearchDomainCheckUrl in order to mock out calls to
   // GAIA servers.
@@ -443,6 +460,12 @@
   // profile with the server.
   std::vector<std::unique_ptr<ProfileSyncServiceHarness>> clients_;
 
+  // Mapping from client indexes to encryption passphrases to use for them.
+  std::map<int, std::string> client_encryption_passphrases_;
+
+  // Mapping from client indexes to decryption passphrases to use for them.
+  std::map<int, std::string> client_decryption_passphrases_;
+
   // A set of objects to listen for commit activity and broadcast notifications
   // of this activity to its peer sync clients.
   std::vector<std::unique_ptr<P2PInvalidationForwarder>>
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 57bfb5a2..e9f7ebd 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1532,6 +1532,8 @@
       "webui/chromeos/login/discover/discover_window_manager_observer.h",
       "webui/chromeos/login/discover/modules/discover_module_launch_help_app.cc",
       "webui/chromeos/login/discover/modules/discover_module_launch_help_app.h",
+      "webui/chromeos/login/discover/modules/discover_module_pin_setup.cc",
+      "webui/chromeos/login/discover/modules/discover_module_pin_setup.h",
       "webui/chromeos/login/discover/modules/discover_module_redeem_offers.cc",
       "webui/chromeos/login/discover/modules/discover_module_redeem_offers.h",
       "webui/chromeos/login/discover/modules/discover_module_sync_files.cc",
@@ -2634,6 +2636,8 @@
       "views/overlay/control_image_button.h",
       "views/overlay/overlay_window_views.cc",
       "views/overlay/overlay_window_views.h",
+      "views/overlay/resize_handle_button.cc",
+      "views/overlay/resize_handle_button.h",
       "views/page_action/page_action_icon_container_view.cc",
       "views/page_action/page_action_icon_container_view.h",
       "views/page_action/page_action_icon_view.cc",
diff --git a/chrome/browser/ui/browser_command_controller.cc b/chrome/browser/ui/browser_command_controller.cc
index ae98c5f..0649a4c5 100644
--- a/chrome/browser/ui/browser_command_controller.cc
+++ b/chrome/browser/ui/browser_command_controller.cc
@@ -1297,7 +1297,7 @@
     return;
 
   command_updater_.UpdateCommandEnabled(
-      IDC_SHOW_SYNC_SETUP, show_main_ui && pref_signin_allowed_.GetValue());
+      IDC_SHOW_SIGNIN, show_main_ui && pref_signin_allowed_.GetValue());
 }
 
 // static
diff --git a/chrome/browser/ui/browser_command_controller_unittest.cc b/chrome/browser/ui/browser_command_controller_unittest.cc
index 14c5ed3..edb839e 100644
--- a/chrome/browser/ui/browser_command_controller_unittest.cc
+++ b/chrome/browser/ui/browser_command_controller_unittest.cc
@@ -342,6 +342,7 @@
 
     //         Command ID        |      tab mode      |      fullscreen     |
     //                           | enabled | reserved | enabled  | reserved |
+    // clang-format off
     { IDC_OPEN_CURRENT_URL,        true,     false,     false,     false    },
     { IDC_FOCUS_TOOLBAR,           true,     false,     false,     false    },
     { IDC_FOCUS_LOCATION,          true,     false,     false,     false    },
@@ -370,6 +371,8 @@
     { IDC_SELECT_PREVIOUS_TAB,     true,     true,      true,      false    },
     { IDC_EXIT,                    true,     true,      true,      true     },
     { IDC_SHOW_AS_TAB,             false,    false,     false,     false    },
+    { IDC_SHOW_SIGNIN,             true,     false,      true,      false   },
+    // clang-format on
   };
   const content::NativeWebKeyboardEvent key_event(
       blink::WebInputEvent::kTypeFirst, 0,
@@ -488,9 +491,9 @@
   const CommandUpdater* command_updater = &command_controller;
 
   // Check that the SYNC_SETUP command is updated on preference change.
-  EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_SHOW_SYNC_SETUP));
+  EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_SHOW_SIGNIN));
   profile1->GetPrefs()->SetBoolean(prefs::kSigninAllowed, false);
-  EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_SHOW_SYNC_SETUP));
+  EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_SHOW_SIGNIN));
 }
 
 TEST_F(BrowserCommandControllerTest, OnSigninAllowedPrefChange) {
@@ -498,7 +501,7 @@
   const CommandUpdater* command_updater = &command_controller;
 
   // Check that the SYNC_SETUP command is updated on preference change.
-  EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_SHOW_SYNC_SETUP));
+  EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_SHOW_SIGNIN));
   profile()->GetPrefs()->SetBoolean(prefs::kSigninAllowed, false);
-  EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_SHOW_SYNC_SETUP));
+  EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_SHOW_SIGNIN));
 }
diff --git a/chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_controller_views.mm b/chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_controller_views.mm
index 33bc698..9a596d05 100644
--- a/chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_controller_views.mm
+++ b/chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_controller_views.mm
@@ -28,15 +28,18 @@
 - (BOOL)isFullscreenTransitionInProgress {
   views::BridgedNativeWidgetImpl* bridge_widget =
       views::BridgedNativeWidgetImpl::GetFromNativeWindow([self window]);
-  return bridge_widget->in_fullscreen_transition();
+  if (bridge_widget)
+    return bridge_widget->in_fullscreen_transition();
+  return NO;
 }
 
 - (NSWindow*)window {
   NSWindow* ns_window = browserView_->GetNativeWindow();
   if (!ns_view_) {
-    ns_view_.reset(
-        [views::BridgedNativeWidgetImpl::GetFromNativeWindow(ns_window)
-                ->ns_view() retain]);
+    views::BridgedNativeWidgetImpl* bridge_widget =
+        views::BridgedNativeWidgetImpl::GetFromNativeWindow(ns_window);
+    if (bridge_widget)
+      ns_view_.reset([bridge_widget->ns_view() retain]);
   }
   return ns_window;
 }
diff --git a/chrome/browser/ui/toolbar/app_menu_model.cc b/chrome/browser/ui/toolbar/app_menu_model.cc
index 5db023f3..aa93308 100644
--- a/chrome/browser/ui/toolbar/app_menu_model.cc
+++ b/chrome/browser/ui/toolbar/app_menu_model.cc
@@ -534,7 +534,7 @@
       }
       LogMenuAction(MENU_ACTION_SHOW_DOWNLOADS);
       break;
-    case IDC_SHOW_SYNC_SETUP:
+    case IDC_SHOW_SIGNIN:
       if (!uma_action_recorded_) {
         UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.ShowSyncSetup",
                                    delta);
diff --git a/chrome/browser/ui/views/overlay/close_image_button.cc b/chrome/browser/ui/views/overlay/close_image_button.cc
index cb898b1..65e0eb97 100644
--- a/chrome/browser/ui/views/overlay/close_image_button.cc
+++ b/chrome/browser/ui/views/overlay/close_image_button.cc
@@ -68,7 +68,17 @@
   SetBackgroundImage(kCloseBgColor, nullptr, nullptr);
 }
 
-void CloseImageButton::SetPosition(const gfx::Size& size) {
+void CloseImageButton::SetPosition(
+    const gfx::Size& size,
+    OverlayWindowViews::WindowQuadrant quadrant) {
+#if defined(OS_CHROMEOS)
+  if (quadrant == OverlayWindowViews::WindowQuadrant::kBottomLeft) {
+    ImageButton::SetPosition(
+        gfx::Point(kCloseButtonMargin, kCloseButtonMargin));
+    return;
+  }
+#endif
+
   ImageButton::SetPosition(
       gfx::Point(size.width() - kCloseButtonSize - kCloseButtonMargin,
                  kCloseButtonMargin));
diff --git a/chrome/browser/ui/views/overlay/close_image_button.h b/chrome/browser/ui/views/overlay/close_image_button.h
index 5f14ac8..6ff8e9a 100644
--- a/chrome/browser/ui/views/overlay/close_image_button.h
+++ b/chrome/browser/ui/views/overlay/close_image_button.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_UI_VIEWS_OVERLAY_CLOSE_IMAGE_BUTTON_H_
 #define CHROME_BROWSER_UI_VIEWS_OVERLAY_CLOSE_IMAGE_BUTTON_H_
 
+#include "chrome/browser/ui/views/overlay/overlay_window_views.h"
 #include "ui/views/controls/button/image_button.h"
 
 namespace views {
@@ -24,7 +25,8 @@
   void OnBlur() override;
 
   // Sets the position of itself with an offset from the given window size.
-  void SetPosition(const gfx::Size& size);
+  void SetPosition(const gfx::Size& size,
+                   OverlayWindowViews::WindowQuadrant quadrant);
 
  private:
   const gfx::ImageSkia close_background_;
diff --git a/chrome/browser/ui/views/overlay/overlay_window_views.cc b/chrome/browser/ui/views/overlay/overlay_window_views.cc
index 01fa670b4..a149b038 100644
--- a/chrome/browser/ui/views/overlay/overlay_window_views.cc
+++ b/chrome/browser/ui/views/overlay/overlay_window_views.cc
@@ -14,6 +14,7 @@
 #include "chrome/app/vector_icons/vector_icons.h"
 #include "chrome/browser/ui/views/overlay/close_image_button.h"
 #include "chrome/browser/ui/views/overlay/control_image_button.h"
+#include "chrome/browser/ui/views/overlay/resize_handle_button.h"
 #include "chrome/grit/generated_resources.h"
 #include "content/public/browser/picture_in_picture_window_controller.h"
 #include "content/public/browser/web_contents.h"
@@ -52,6 +53,30 @@
 // Colors for the control buttons.
 SkColor kBgColor = SK_ColorWHITE;
 SkColor kControlIconColor = SK_ColorBLACK;
+
+// Returns the quadrant the OverlayWindowViews is primarily in on the current
+// work area.
+OverlayWindowViews::WindowQuadrant GetCurrentWindowQuadrant(
+    const gfx::Rect window_bounds,
+    content::PictureInPictureWindowController* controller) {
+  gfx::Rect work_area =
+      display::Screen::GetScreen()
+          ->GetDisplayNearestWindow(
+              controller->GetInitiatorWebContents()->GetTopLevelNativeWindow())
+          .work_area();
+  gfx::Point window_center = window_bounds.CenterPoint();
+
+  // Check which quadrant the center of the window appears in.
+  if (window_center.x() < work_area.width() / 2) {
+    return (window_center.y() < work_area.height() / 2)
+               ? OverlayWindowViews::WindowQuadrant::kTopLeft
+               : OverlayWindowViews::WindowQuadrant::kBottomLeft;
+  }
+  return (window_center.y() < work_area.height() / 2)
+             ? OverlayWindowViews::WindowQuadrant::kTopRight
+             : OverlayWindowViews::WindowQuadrant::kBottomRight;
+}
+
 }  // namespace
 
 // OverlayWindow implementation of NonClientFrameView.
@@ -142,6 +167,9 @@
       controls_scrim_view_(new views::View()),
       controls_parent_view_(new views::View()),
       close_controls_view_(new views::CloseImageButton(this)),
+#if defined(OS_CHROMEOS)
+      resize_handle_view_(new views::ResizeHandleButton(this)),
+#endif
       play_pause_controls_view_(new views::ToggleImageButton(this)),
       hide_controls_timer_(
           FROM_HERE,
@@ -266,6 +294,13 @@
   play_pause_controls_view_->SetToggled(controller_->IsPlayerActive());
   play_pause_controls_view_->set_owned_by_client();
 
+#if defined(OS_CHROMEOS)
+  // views::View that shows the affordance that the window can be resized. ----
+  resize_handle_view_->SetPaintToLayer(ui::LAYER_TEXTURED);
+  resize_handle_view_->layer()->SetFillsBoundsOpaquely(false);
+  resize_handle_view_->set_owned_by_client();
+#endif
+
   // Accessibility.
   play_pause_controls_view_->SetFocusForPlatform();  // Make button focusable.
   const base::string16 play_pause_accessible_button_label(
@@ -286,6 +321,9 @@
   GetContentsView()->AddChildView(controls_scrim_view_.get());
   GetContentsView()->AddChildView(controls_parent_view_.get());
   GetContentsView()->AddChildView(close_controls_view_.get());
+#if defined(OS_CHROMEOS)
+  GetContentsView()->AddChildView(resize_handle_view_.get());
+#endif
 
   UpdatePlayPauseControlsSize();
   UpdateControlsVisibility(false);
@@ -323,6 +361,11 @@
     play_pause_controls_view_->SetVisible(false);
 
   GetCloseControlsLayer()->SetVisible(is_visible);
+
+#if defined(OS_CHROMEOS)
+  GetResizeHandleLayer()->SetVisible(is_visible);
+#endif
+
   GetControlsScrimLayer()->SetVisible(
       (playback_state_ == kNoVideo) ? false : is_visible);
   GetControlsParentLayer()->SetVisible(
@@ -337,7 +380,11 @@
   controls_scrim_view_->SetBoundsRect(
       gfx::Rect(gfx::Point(0, 0), larger_window_bounds.size()));
 
-  close_controls_view_->SetPosition(GetBounds().size());
+  WindowQuadrant quadrant = GetCurrentWindowQuadrant(GetBounds(), controller_);
+  close_controls_view_->SetPosition(GetBounds().size(), quadrant);
+#if defined(OS_CHROMEOS)
+  resize_handle_view_->SetPosition(GetBounds().size(), quadrant);
+#endif
 
   controls_parent_view_->SetBoundsRect(
       gfx::Rect(gfx::Point(0, 0), GetBounds().size()));
@@ -609,6 +656,13 @@
   // the window to reappear with the same origin point when a new video is
   // shown.
   window_bounds_ = GetBounds();
+
+#if defined(OS_CHROMEOS)
+  // Update the positioning of some icons when the window is moved.
+  WindowQuadrant quadrant = GetCurrentWindowQuadrant(GetBounds(), controller_);
+  close_controls_view_->SetPosition(GetBounds().size(), quadrant);
+  resize_handle_view_->SetPosition(GetBounds().size(), quadrant);
+#endif
 }
 
 void OverlayWindowViews::OnNativeWidgetSizeChanged(const gfx::Size& new_size) {
@@ -771,6 +825,10 @@
   return close_controls_view_->layer();
 }
 
+ui::Layer* OverlayWindowViews::GetResizeHandleLayer() {
+  return resize_handle_view_->layer();
+}
+
 ui::Layer* OverlayWindowViews::GetControlsParentLayer() {
   return controls_parent_view_->layer();
 }
@@ -788,6 +846,14 @@
   return play_pause_controls_view_.get();
 }
 
+gfx::Point OverlayWindowViews::close_image_position_for_testing() const {
+  return close_controls_view_->origin();
+}
+
+gfx::Point OverlayWindowViews::resize_handle_position_for_testing() const {
+  return resize_handle_view_->origin();
+}
+
 views::View* OverlayWindowViews::controls_parent_view_for_testing() const {
   return controls_parent_view_.get();
 }
diff --git a/chrome/browser/ui/views/overlay/overlay_window_views.h b/chrome/browser/ui/views/overlay/overlay_window_views.h
index 7431f5f..b1f714d 100644
--- a/chrome/browser/ui/views/overlay/overlay_window_views.h
+++ b/chrome/browser/ui/views/overlay/overlay_window_views.h
@@ -14,6 +14,7 @@
 namespace views {
 class ControlImageButton;
 class CloseImageButton;
+class ResizeHandleButton;
 class ToggleImageButton;
 }  // namespace views
 
@@ -27,6 +28,8 @@
       content::PictureInPictureWindowController* controller);
   ~OverlayWindowViews() override;
 
+  enum class WindowQuadrant { kBottomLeft, kBottomRight, kTopLeft, kTopRight };
+
   // OverlayWindow:
   bool IsActive() const override;
   void Close() override;
@@ -67,6 +70,8 @@
   gfx::Rect GetSecondCustomControlsBounds();
 
   views::ToggleImageButton* play_pause_controls_view_for_testing() const;
+  gfx::Point close_image_position_for_testing() const;
+  gfx::Point resize_handle_position_for_testing() const;
   views::View* controls_parent_view_for_testing() const;
   OverlayWindowViews::PlaybackState playback_state_for_testing() const;
 
@@ -118,6 +123,7 @@
 
   ui::Layer* GetControlsScrimLayer();
   ui::Layer* GetCloseControlsLayer();
+  ui::Layer* GetResizeHandleLayer();
   ui::Layer* GetControlsParentLayer();
 
   // Toggles the play/pause control through the |controller_| and updates the
@@ -174,6 +180,7 @@
   // the close view.
   std::unique_ptr<views::View> controls_parent_view_;
   std::unique_ptr<views::CloseImageButton> close_controls_view_;
+  std::unique_ptr<views::ResizeHandleButton> resize_handle_view_;
   std::unique_ptr<views::ToggleImageButton> play_pause_controls_view_;
   std::unique_ptr<views::ControlImageButton> first_custom_controls_view_;
   std::unique_ptr<views::ControlImageButton> second_custom_controls_view_;
diff --git a/chrome/browser/ui/views/overlay/resize_handle_button.cc b/chrome/browser/ui/views/overlay/resize_handle_button.cc
new file mode 100644
index 0000000..9fca4a8
--- /dev/null
+++ b/chrome/browser/ui/views/overlay/resize_handle_button.cc
@@ -0,0 +1,101 @@
+// 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/ui/views/overlay/resize_handle_button.h"
+
+#include "chrome/app/vector_icons/vector_icons.h"
+#include "chrome/grit/generated_resources.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/color_palette.h"
+#include "ui/gfx/image/image_skia_operations.h"
+#include "ui/gfx/paint_vector_icon.h"
+#include "ui/gfx/skbitmap_operations.h"
+#include "ui/views/vector_icons.h"
+
+namespace {
+
+const int kResizeHandleButtonSize = 16;
+
+constexpr SkColor kResizeHandleIconColor = SK_ColorWHITE;
+
+}  // namespace
+
+namespace views {
+
+ResizeHandleButton::ResizeHandleButton(ButtonListener* listener)
+    : ImageButton(listener) {
+  SetImageAlignment(views::ImageButton::ALIGN_CENTER,
+                    views::ImageButton::ALIGN_MIDDLE);
+  SetSize(gfx::Size(kResizeHandleButtonSize, kResizeHandleButtonSize));
+  SetImageForQuadrant(OverlayWindowViews::WindowQuadrant::kBottomRight);
+
+  // Accessibility.
+  SetFocusForPlatform();
+  const base::string16 resize_button_label(
+      l10n_util::GetStringUTF16(IDS_PICTURE_IN_PICTURE_RESIZE_HANDLE_TEXT));
+  SetAccessibleName(resize_button_label);
+  SetTooltipText(resize_button_label);
+  SetInstallFocusRingOnFocus(true);
+}
+
+ResizeHandleButton::~ResizeHandleButton() = default;
+
+void ResizeHandleButton::SetPosition(
+    const gfx::Size& size,
+    OverlayWindowViews::WindowQuadrant quadrant) {
+  // The resize handle should appear towards the center of the working area.
+  // This is determined as the opposite quadrant on the window.
+  switch (quadrant) {
+    case OverlayWindowViews::WindowQuadrant::kBottomLeft:
+      ImageButton::SetPosition(
+          gfx::Point(size.width() - kResizeHandleButtonSize, 0));
+      break;
+    case OverlayWindowViews::WindowQuadrant::kBottomRight:
+      ImageButton::SetPosition(gfx::Point(0, 0));
+      break;
+    case OverlayWindowViews::WindowQuadrant::kTopLeft:
+      ImageButton::SetPosition(
+          gfx::Point(size.width() - kResizeHandleButtonSize,
+                     size.height() - kResizeHandleButtonSize));
+      break;
+    case OverlayWindowViews::WindowQuadrant::kTopRight:
+      ImageButton::SetPosition(
+          gfx::Point(0, size.height() - kResizeHandleButtonSize));
+      break;
+  }
+
+  // Also rotate the icon to match the new corner.
+  SetImageForQuadrant(quadrant);
+}
+
+void ResizeHandleButton::SetImageForQuadrant(
+    OverlayWindowViews::WindowQuadrant quadrant) {
+  if (current_quadrant_ == quadrant)
+    return;
+  current_quadrant_ = quadrant;
+
+  gfx::ImageSkia icon = gfx::CreateVectorIcon(
+      kResizeHandleIcon, kResizeHandleButtonSize, kResizeHandleIconColor);
+  switch (quadrant) {
+    case OverlayWindowViews::WindowQuadrant::kBottomLeft:
+      break;
+    case OverlayWindowViews::WindowQuadrant::kBottomRight:
+      icon = gfx::ImageSkiaOperations::CreateRotatedImage(
+          icon, SkBitmapOperations::ROTATION_270_CW);
+      break;
+    case OverlayWindowViews::WindowQuadrant::kTopLeft:
+      icon = gfx::ImageSkiaOperations::CreateRotatedImage(
+          icon, SkBitmapOperations::ROTATION_90_CW);
+      break;
+    case OverlayWindowViews::WindowQuadrant::kTopRight:
+      icon = gfx::ImageSkiaOperations::CreateRotatedImage(
+          icon, SkBitmapOperations::ROTATION_180_CW);
+      break;
+  }
+
+  SetImage(views::Button::STATE_NORMAL, icon);
+}
+
+}  // namespace views
diff --git a/chrome/browser/ui/views/overlay/resize_handle_button.h b/chrome/browser/ui/views/overlay/resize_handle_button.h
new file mode 100644
index 0000000..1b3646f
--- /dev/null
+++ b/chrome/browser/ui/views/overlay/resize_handle_button.h
@@ -0,0 +1,32 @@
+// 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_UI_VIEWS_OVERLAY_RESIZE_HANDLE_BUTTON_H_
+#define CHROME_BROWSER_UI_VIEWS_OVERLAY_RESIZE_HANDLE_BUTTON_H_
+
+#include "chrome/browser/ui/views/overlay/overlay_window_views.h"
+#include "ui/views/controls/button/image_button.h"
+
+namespace views {
+
+// An image button representing a white resize handle affordance.
+class ResizeHandleButton : public views::ImageButton {
+ public:
+  explicit ResizeHandleButton(ButtonListener* listener);
+  ~ResizeHandleButton() override;
+
+  void SetPosition(const gfx::Size& size,
+                   OverlayWindowViews::WindowQuadrant quadrant);
+
+ private:
+  void SetImageForQuadrant(OverlayWindowViews::WindowQuadrant quadrant);
+
+  base::Optional<OverlayWindowViews::WindowQuadrant> current_quadrant_;
+
+  DISALLOW_COPY_AND_ASSIGN(ResizeHandleButton);
+};
+
+}  // namespace views
+
+#endif  // CHROME_BROWSER_UI_VIEWS_OVERLAY_RESIZE_HANDLE_BUTTON_H_
diff --git a/chrome/browser/ui/webui/chromeos/login/discover/discover_manager.cc b/chrome/browser/ui/webui/chromeos/login/discover/discover_manager.cc
index 0a205aa253..389e69a 100644
--- a/chrome/browser/ui/webui/chromeos/login/discover/discover_manager.cc
+++ b/chrome/browser/ui/webui/chromeos/login/discover/discover_manager.cc
@@ -9,17 +9,35 @@
 #include "base/logging.h"
 #include "chrome/browser/ui/webui/chromeos/login/discover/discover_handler.h"
 #include "chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_launch_help_app.h"
+#include "chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_pin_setup.h"
 #include "chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_redeem_offers.h"
 #include "chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_sync_files.h"
 #include "chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_welcome.h"
 
 namespace chromeos {
+namespace {
+
+// Owned by ChromeBrowserMainPartsChromeos.
+DiscoverManager* g_discover_manager = nullptr;
+
+}  // namespace
 
 DiscoverManager::DiscoverManager() {
+  DCHECK(!g_discover_manager);
+  g_discover_manager = this;
+
   CreateModules();
 }
 
-DiscoverManager::~DiscoverManager() = default;
+DiscoverManager::~DiscoverManager() {
+  DCHECK_EQ(g_discover_manager, this);
+  g_discover_manager = nullptr;
+}
+
+// static
+DiscoverManager* DiscoverManager::Get() {
+  return g_discover_manager;
+}
 
 bool DiscoverManager::IsCompleted() const {
   // Returns true if all of the modules are completed.
@@ -38,6 +56,8 @@
       std::make_unique<DiscoverModuleSyncFiles>();
   modules_[DiscoverModuleWelcome::kModuleName] =
       std::make_unique<DiscoverModuleWelcome>();
+  modules_[DiscoverModulePinSetup::kModuleName] =
+      std::make_unique<DiscoverModulePinSetup>();
 }
 
 std::vector<std::unique_ptr<DiscoverHandler>>
@@ -49,4 +69,10 @@
   return handlers;
 }
 
+DiscoverModule* DiscoverManager::GetModuleByName(
+    const std::string& module_name) const {
+  const auto it = modules_.find(module_name);
+  return it == modules_.end() ? nullptr : it->second.get();
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/login/discover/discover_manager.h b/chrome/browser/ui/webui/chromeos/login/discover/discover_manager.h
index bd0fa04..8ac51be 100644
--- a/chrome/browser/ui/webui/chromeos/login/discover/discover_manager.h
+++ b/chrome/browser/ui/webui/chromeos/login/discover/discover_manager.h
@@ -25,17 +25,29 @@
   DiscoverManager();
   ~DiscoverManager();
 
+  // Returns object instance from platform_parts.
+  static DiscoverManager* Get();
+
   // Returns true if there are no modules to be displayed.
   bool IsCompleted() const;
 
   // Returns vector of WebUI message handlers for visible modules.
   std::vector<std::unique_ptr<DiscoverHandler>> CreateWebUIHandlers() const;
 
+  template <typename T>
+  T* GetModule() {
+    return static_cast<T*>(GetModuleByName(T::kModuleName));
+  }
+
   const ModulesMap& get_modules() const { return modules_; }
 
  private:
   // Creates all needed modules.
   void CreateModules();
+
+  // Returns module by name.
+  DiscoverModule* GetModuleByName(const std::string& module_name) const;
+
   ModulesMap modules_;
 
   DISALLOW_COPY_AND_ASSIGN(DiscoverManager);
diff --git a/chrome/browser/ui/webui/chromeos/login/discover/discover_module.h b/chrome/browser/ui/webui/chromeos/login/discover/discover_module.h
index add727f..e97a5ad 100644
--- a/chrome/browser/ui/webui/chromeos/login/discover/discover_module.h
+++ b/chrome/browser/ui/webui/chromeos/login/discover/discover_module.h
@@ -24,7 +24,7 @@
   virtual bool IsCompleted() const = 0;
 
   // Creates and returns WebUI handler for the module.
-  virtual std::unique_ptr<DiscoverHandler> CreateWebUIHandler() const = 0;
+  virtual std::unique_ptr<DiscoverHandler> CreateWebUIHandler() = 0;
 
   // Module is also expected to provide static method:
   // static const char* kModuleName;
diff --git a/chrome/browser/ui/webui/chromeos/login/discover/discover_ui.cc b/chrome/browser/ui/webui/chromeos/login/discover/discover_ui.cc
index 553853e..426e678 100644
--- a/chrome/browser/ui/webui/chromeos/login/discover/discover_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/login/discover/discover_ui.cc
@@ -4,7 +4,6 @@
 
 #include "chrome/browser/ui/webui/chromeos/login/discover/discover_ui.h"
 
-#include "chrome/browser/browser_process.h"
 #include "chrome/browser/ui/webui/chromeos/login/discover/discover_handler.h"
 #include "chrome/browser/ui/webui/chromeos/login/discover/discover_manager.h"
 #include "content/public/browser/web_ui.h"
@@ -17,9 +16,7 @@
 
 void DiscoverUI::RegisterMessages(content::WebUI* web_ui) {
   std::vector<std::unique_ptr<DiscoverHandler>> handlers =
-      g_browser_process->platform_part()
-          ->GetDiscoverManager()
-          ->CreateWebUIHandlers();
+      DiscoverManager::Get()->CreateWebUIHandlers();
   for (auto& handler : handlers) {
     handlers_.push_back(handler.get());
     web_ui->AddMessageHandler(std::move(handler));
diff --git a/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_launch_help_app.cc b/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_launch_help_app.cc
index cd3e89c..37ea54d 100644
--- a/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_launch_help_app.cc
+++ b/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_launch_help_app.cc
@@ -70,7 +70,7 @@
 }
 
 std::unique_ptr<DiscoverHandler>
-DiscoverModuleLaunchHelpApp::CreateWebUIHandler() const {
+DiscoverModuleLaunchHelpApp::CreateWebUIHandler() {
   return std::make_unique<DiscoverModuleLaunchHelpAppHandler>();
 }
 
diff --git a/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_launch_help_app.h b/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_launch_help_app.h
index 7a386ab..78774b3 100644
--- a/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_launch_help_app.h
+++ b/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_launch_help_app.h
@@ -19,7 +19,7 @@
 
   // DiscoverModule:
   bool IsCompleted() const override;
-  std::unique_ptr<DiscoverHandler> CreateWebUIHandler() const override;
+  std::unique_ptr<DiscoverHandler> CreateWebUIHandler() override;
 
   // Module name.
   static const char kModuleName[];
diff --git a/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_pin_setup.cc b/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_pin_setup.cc
new file mode 100644
index 0000000..3586320
--- /dev/null
+++ b/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_pin_setup.cc
@@ -0,0 +1,137 @@
+// 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/ui/webui/chromeos/login/discover/modules/discover_module_pin_setup.h"
+
+#include <vector>
+
+#include "base/i18n/number_formatting.h"
+#include "base/values.h"
+#include "chrome/browser/ui/webui/chromeos/login/discover/discover_handler.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/login/localized_values_builder.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+
+namespace chromeos {
+
+namespace {
+
+class DiscoverModulePinSetupHandler : public DiscoverHandler {
+ public:
+  explicit DiscoverModulePinSetupHandler(
+      base::WeakPtr<DiscoverModulePinSetup> module);
+  ~DiscoverModulePinSetupHandler() override = default;
+
+  // BaseWebUIHandler
+  void DeclareLocalizedValues(
+      ::login::LocalizedValuesBuilder* builder) override;
+  void Initialize() override;
+  void RegisterMessages() override;
+
+ private:
+  // Message handlers.
+  void HandleGetUserPassword(const std::string& callbackId);
+
+  base::WeakPtr<DiscoverModulePinSetup> module_;
+
+  DISALLOW_COPY_AND_ASSIGN(DiscoverModulePinSetupHandler);
+};
+
+DiscoverModulePinSetupHandler::DiscoverModulePinSetupHandler(
+    base::WeakPtr<DiscoverModulePinSetup> module)
+    : DiscoverHandler(DiscoverModulePinSetup::kModuleName), module_(module) {}
+
+void DiscoverModulePinSetupHandler::DeclareLocalizedValues(
+    ::login::LocalizedValuesBuilder* builder) {
+  builder->Add("discoverPinSetup", IDS_DISCOVER_PIN_SETUP);
+
+  builder->Add("back", IDS_EULA_BACK_BUTTON);
+  builder->Add("next", IDS_EULA_NEXT_BUTTON);
+  builder->Add("discoverPinSetupDone", IDS_DISCOVER_PIN_SETUP_DONE);
+
+  builder->Add("discoverPinSetupTitle1", IDS_DISCOVER_PIN_SETUP_TITLE1);
+  builder->Add("discoverPinSetupSubtitle1", IDS_DISCOVER_PIN_SETUP_SUBTITLE1);
+  builder->Add("discoverPinSetupSkip", IDS_DISCOVER_PIN_SETUP_SKIP);
+  builder->Add("discoverPinSetupTitle2", IDS_DISCOVER_PIN_SETUP_TITLE2);
+  builder->Add("discoverPinSetupTitle3", IDS_DISCOVER_PIN_SETUP_TITLE3);
+  builder->Add("discoverPinSetupSubtitle3", IDS_DISCOVER_PIN_SETUP_SUBTITLE3);
+  builder->Add("discoverPinSetupPasswordTitle",
+               IDS_DISCOVER_PIN_SETUP_PASSWORD_TITLE);
+  builder->Add("discoverPinSetupPasswordSubTitle",
+               IDS_DISCOVER_PIN_SETUP_PASSWORD_SUBTITLE);
+
+  builder->Add("passwordPromptInvalidPassword",
+               IDS_SETTINGS_PEOPLE_PASSWORD_PROMPT_INVALID_PASSWORD);
+  builder->Add("passwordPromptPasswordLabel",
+               IDS_SETTINGS_PEOPLE_PASSWORD_PROMPT_PASSWORD_LABEL);
+
+  // Format numbers to be used on the pin keyboard.
+  for (int j = 0; j <= 9; j++) {
+    builder->Add("pinKeyboard" + base::IntToString(j),
+                 base::FormatNumber(int64_t{j}));
+  }
+  builder->Add("pinKeyboardPlaceholderPin", IDS_PIN_KEYBOARD_HINT_TEXT_PIN);
+  builder->Add("pinKeyboardPlaceholderPinPassword",
+               IDS_PIN_KEYBOARD_HINT_TEXT_PIN_PASSWORD);
+  builder->Add("pinKeyboardDeleteAccessibleName",
+               IDS_PIN_KEYBOARD_DELETE_ACCESSIBLE_NAME);
+  builder->Add("configurePinMismatched",
+               IDS_SETTINGS_PEOPLE_CONFIGURE_PIN_MISMATCHED);
+  builder->Add("configurePinTooShort",
+               IDS_SETTINGS_PEOPLE_CONFIGURE_PIN_TOO_SHORT);
+  builder->Add("configurePinTooLong",
+               IDS_SETTINGS_PEOPLE_CONFIGURE_PIN_TOO_LONG);
+  builder->Add("configurePinWeakPin",
+               IDS_SETTINGS_PEOPLE_CONFIGURE_PIN_WEAK_PIN);
+}
+
+void DiscoverModulePinSetupHandler::Initialize() {}
+
+void DiscoverModulePinSetupHandler::RegisterMessages() {
+  AddCallback("discover.pinSetup.getUserPassword",
+              &DiscoverModulePinSetupHandler::HandleGetUserPassword);
+}
+
+void DiscoverModulePinSetupHandler::HandleGetUserPassword(
+    const std::string& callbackId) {
+  web_ui()->CallJavascriptFunctionUnsafe(
+      "window.discoverReturn", base::Value(callbackId),
+      base::Value(module_->ConsumePrimaryUserPassword()));
+  return;
+}
+
+}  // anonymous namespace
+
+/* ***************************************************************** */
+/* Discover PinSetup module implementation below.               */
+
+const char DiscoverModulePinSetup::kModuleName[] = "pinSetup";
+
+DiscoverModulePinSetup::DiscoverModulePinSetup() {}
+
+DiscoverModulePinSetup::~DiscoverModulePinSetup() = default;
+
+bool DiscoverModulePinSetup::IsCompleted() const {
+  return false;
+}
+
+std::unique_ptr<DiscoverHandler> DiscoverModulePinSetup::CreateWebUIHandler() {
+  return std::make_unique<DiscoverModulePinSetupHandler>(
+      weak_ptr_factory_.GetWeakPtr());
+}
+
+std::string DiscoverModulePinSetup::ConsumePrimaryUserPassword() {
+  std::string result;
+  std::swap(primary_user_password_, result);
+  return result;
+}
+
+void DiscoverModulePinSetup::SetPrimaryUserPassword(
+    const std::string& password) {
+  primary_user_password_ = password;
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_pin_setup.h b/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_pin_setup.h
new file mode 100644
index 0000000..e0e9421
--- /dev/null
+++ b/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_pin_setup.h
@@ -0,0 +1,43 @@
+// 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_UI_WEBUI_CHROMEOS_LOGIN_DISCOVER_MODULES_DISCOVER_MODULE_PIN_SETUP_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_DISCOVER_MODULES_DISCOVER_MODULE_PIN_SETUP_H_
+
+#include <memory>
+
+#include "base/containers/flat_map.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/ui/webui/chromeos/login/discover/discover_module.h"
+
+namespace chromeos {
+
+class DiscoverModulePinSetup : public DiscoverModule {
+ public:
+  // Module name.
+  static const char kModuleName[];
+
+  DiscoverModulePinSetup();
+  ~DiscoverModulePinSetup() override;
+
+  // Returns primary user password, or empty string if not known.
+  // Password is kept for newly created user only, and is returned only once.
+  // (Empty string will be returned for subsequent calls.)
+  std::string ConsumePrimaryUserPassword();
+  void SetPrimaryUserPassword(const std::string& password);
+
+  // DiscoverModule:
+  bool IsCompleted() const override;
+  std::unique_ptr<DiscoverHandler> CreateWebUIHandler() override;
+
+ private:
+  std::string primary_user_password_;
+
+  base::WeakPtrFactory<DiscoverModulePinSetup> weak_ptr_factory_{this};
+  DISALLOW_COPY_AND_ASSIGN(DiscoverModulePinSetup);
+};
+
+}  // namespace chromeos
+#endif  // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_DISCOVER_MODULES_DISCOVER_MODULE_PIN_SETUP_H_
diff --git a/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_redeem_offers.cc b/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_redeem_offers.cc
index 300586e..cf44c276 100644
--- a/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_redeem_offers.cc
+++ b/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_redeem_offers.cc
@@ -56,7 +56,7 @@
 }
 
 std::unique_ptr<DiscoverHandler>
-DiscoverModuleRedeemOffers::CreateWebUIHandler() const {
+DiscoverModuleRedeemOffers::CreateWebUIHandler() {
   return std::make_unique<DiscoverModuleRedeemOffersHandler>();
 }
 
diff --git a/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_redeem_offers.h b/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_redeem_offers.h
index ac4d91b..7060b55 100644
--- a/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_redeem_offers.h
+++ b/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_redeem_offers.h
@@ -19,7 +19,7 @@
 
   // DiscoverModule:
   bool IsCompleted() const override;
-  std::unique_ptr<DiscoverHandler> CreateWebUIHandler() const override;
+  std::unique_ptr<DiscoverHandler> CreateWebUIHandler() override;
 
   // Module name.
   static const char kModuleName[];
diff --git a/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_sync_files.cc b/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_sync_files.cc
index cb910b7..00e8d236 100644
--- a/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_sync_files.cc
+++ b/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_sync_files.cc
@@ -55,8 +55,7 @@
   return false;
 }
 
-std::unique_ptr<DiscoverHandler> DiscoverModuleSyncFiles::CreateWebUIHandler()
-    const {
+std::unique_ptr<DiscoverHandler> DiscoverModuleSyncFiles::CreateWebUIHandler() {
   return std::make_unique<DiscoverModuleSyncFilesHandler>();
 }
 
diff --git a/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_sync_files.h b/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_sync_files.h
index 2eccb23..d1efcc5 100644
--- a/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_sync_files.h
+++ b/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_sync_files.h
@@ -19,7 +19,7 @@
 
   // DiscoverModule:
   bool IsCompleted() const override;
-  std::unique_ptr<DiscoverHandler> CreateWebUIHandler() const override;
+  std::unique_ptr<DiscoverHandler> CreateWebUIHandler() override;
 
   // Module name.
   static const char kModuleName[];
diff --git a/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_welcome.cc b/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_welcome.cc
index c5c8cb6e..b5f75cd 100644
--- a/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_welcome.cc
+++ b/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_welcome.cc
@@ -57,8 +57,7 @@
   return false;
 }
 
-std::unique_ptr<DiscoverHandler> DiscoverModuleWelcome::CreateWebUIHandler()
-    const {
+std::unique_ptr<DiscoverHandler> DiscoverModuleWelcome::CreateWebUIHandler() {
   return std::make_unique<DiscoverModuleWelcomeHandler>();
 }
 
diff --git a/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_welcome.h b/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_welcome.h
index bdde6246..dcb1d66 100644
--- a/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_welcome.h
+++ b/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_welcome.h
@@ -19,7 +19,7 @@
 
   // DiscoverModule:
   bool IsCompleted() const override;
-  std::unique_ptr<DiscoverHandler> CreateWebUIHandler() const override;
+  std::unique_ptr<DiscoverHandler> CreateWebUIHandler() override;
 
   // Module name.
   static const char kModuleName[];
diff --git a/chrome/common/extensions/api/_api_features.json b/chrome/common/extensions/api/_api_features.json
index 27535a2..edcbdbb7 100644
--- a/chrome/common/extensions/api/_api_features.json
+++ b/chrome/common/extensions/api/_api_features.json
@@ -658,7 +658,8 @@
     "contexts": ["webui"],
     "matches": [
       "chrome://multidevice-setup/*",
-      "chrome://settings/*"
+      "chrome://settings/*",
+      "chrome://oobe/*"
     ],
     "platforms": ["chromeos"]
   },
diff --git a/chrome/credential_provider/gaiacp/BUILD.gn b/chrome/credential_provider/gaiacp/BUILD.gn
index 46f6393..4d8d07f 100644
--- a/chrome/credential_provider/gaiacp/BUILD.gn
+++ b/chrome/credential_provider/gaiacp/BUILD.gn
@@ -38,9 +38,7 @@
     # Needed in order to include the win32 header security.h.
     "SECURITY_WIN32",
   ]
-  libs = [
-    "secur32.lib",  # For LsaXXX functions
-  ]
+  libs = [ "secur32.lib" ]  # For LsaXXX functions
 }
 
 # This static library is shared with the test code.
@@ -74,7 +72,9 @@
     "win_http_url_fetcher.h",
   ]
   public_configs = [ ":gaiacp_config" ]
-  public_deps = [ ":common" ]
+  public_deps = [
+    ":common",
+  ]
   deps = [
     ":gaia_credential_provider_idl",
     ":resources",
@@ -129,6 +129,60 @@
     "google_logo_small.bmp",
   ]
   outputs = [
+    "gaia_resources_am.pak",
+    "gaia_resources_ar.pak",
+    "gaia_resources_bg.pak",
+    "gaia_resources_bn.pak",
+    "gaia_resources_ca.pak",
+    "gaia_resources_cs.pak",
+    "gaia_resources_da.pak",
+    "gaia_resources_de.pak",
+    "gaia_resources_el.pak",
+    "gaia_resources_en-GB.pak",
+    "gaia_resources_en-US.pak",
+    "gaia_resources_es-419.pak",
+    "gaia_resources_es.pak",
+    "gaia_resources_et.pak",
+    "gaia_resources_fa.pak",
+    "gaia_resources_fake-bidi.pak",
+    "gaia_resources_fi.pak",
+    "gaia_resources_fil.pak",
+    "gaia_resources_fr.pak",
+    "gaia_resources_gu.pak",
+    "gaia_resources_he.pak",
+    "gaia_resources_hi.pak",
+    "gaia_resources_hr.pak",
+    "gaia_resources_hu.pak",
+    "gaia_resources_id.pak",
+    "gaia_resources_it.pak",
+    "gaia_resources_ja.pak",
+    "gaia_resources_kn.pak",
+    "gaia_resources_ko.pak",
+    "gaia_resources_lt.pak",
+    "gaia_resources_lv.pak",
+    "gaia_resources_ml.pak",
+    "gaia_resources_mr.pak",
+    "gaia_resources_ms.pak",
+    "gaia_resources_nb.pak",
+    "gaia_resources_nl.pak",
+    "gaia_resources_pl.pak",
+    "gaia_resources_pt-BR.pak",
+    "gaia_resources_pt-PT.pak",
+    "gaia_resources_ro.pak",
+    "gaia_resources_ru.pak",
+    "gaia_resources_sk.pak",
+    "gaia_resources_sl.pak",
+    "gaia_resources_sr.pak",
+    "gaia_resources_sv.pak",
+    "gaia_resources_sw.pak",
+    "gaia_resources_ta.pak",
+    "gaia_resources_te.pak",
+    "gaia_resources_th.pak",
+    "gaia_resources_tr.pak",
+    "gaia_resources_uk.pak",
+    "gaia_resources_vi.pak",
+    "gaia_resources_zh-CN.pak",
+    "gaia_resources_zh-TW.pak",
     "grit/gaia_resources.h",
     "grit/gaia_resources.rc",
   ]
@@ -155,7 +209,5 @@
     ":version",
     "//chrome/common:version_header",
   ]
-  configs += [
-    "//build/config/win:windowed",
-  ]
+  configs += [ "//build/config/win:windowed" ]
 }
diff --git a/chrome/renderer/extensions/cast_streaming_native_handler.cc b/chrome/renderer/extensions/cast_streaming_native_handler.cc
index 07f10e12..c9f1eb82 100644
--- a/chrome/renderer/extensions/cast_streaming_native_handler.cc
+++ b/chrome/renderer/extensions/cast_streaming_native_handler.cc
@@ -43,7 +43,6 @@
 #include "third_party/blink/public/platform/web_media_stream_track.h"
 #include "third_party/blink/public/platform/web_url.h"
 #include "third_party/blink/public/web/web_dom_media_stream_track.h"
-#include "third_party/blink/public/web/web_media_stream_registry.h"
 #include "url/gurl.h"
 
 using content::V8ValueConverter;
@@ -61,10 +60,7 @@
 
 constexpr char kInvalidAesIvMask[] = "Invalid value for AES IV mask";
 constexpr char kInvalidAesKey[] = "Invalid value for AES key";
-constexpr char kInvalidAudioParams[] = "Invalid audio params";
 constexpr char kInvalidDestination[] = "Invalid destination";
-constexpr char kInvalidFPS[] = "Invalid FPS";
-constexpr char kInvalidMediaStreamURL[] = "Invalid MediaStream URL";
 constexpr char kInvalidRtpParams[] = "Invalid value for RTP params";
 constexpr char kInvalidLatency[] = "Invalid value for max_latency. (0-1000)";
 constexpr char kInvalidRtpTimebase[] = "Invalid rtp_timebase. (1000-1000000)";
@@ -377,10 +373,6 @@
   RouteHandlerFunction("GetStats", "cast.streaming.rtpStream",
                        base::Bind(&CastStreamingNativeHandler::GetStats,
                                   weak_factory_.GetWeakPtr()));
-  RouteHandlerFunction(
-      "StartCastRtpReceiver", "cast.streaming.receiverSession",
-      base::Bind(&CastStreamingNativeHandler::StartCastRtpReceiver,
-                 weak_factory_.GetWeakPtr()));
 }
 
 void CastStreamingNativeHandler::Invalidate() {
@@ -913,117 +905,6 @@
   return true;
 }
 
-void CastStreamingNativeHandler::StartCastRtpReceiver(
-    const v8::FunctionCallbackInfo<v8::Value>& args) {
-  if (args.Length() < 8 || args.Length() > 9 ||
-      !args[0]->IsObject() ||
-      !args[1]->IsObject() ||
-      !args[2]->IsObject() ||
-      !args[3]->IsInt32() ||
-      !args[4]->IsInt32() ||
-      !args[5]->IsNumber() ||
-      !args[6]->IsString()) {
-    args.GetIsolate()->ThrowException(v8::Exception::TypeError(
-        v8::String::NewFromUtf8(args.GetIsolate(), kUnableToConvertArgs,
-                                v8::NewStringType::kNormal)
-            .ToLocalChecked()));
-    return;
-  }
-
-  v8::Isolate* isolate = context()->v8_context()->GetIsolate();
-
-  scoped_refptr<CastReceiverSession> session(
-      new CastReceiverSession());
-  media::cast::FrameReceiverConfig audio_config;
-  media::cast::FrameReceiverConfig video_config;
-  net::IPEndPoint local_endpoint;
-  net::IPEndPoint remote_endpoint;
-
-  if (!FrameReceiverConfigFromArg(isolate, args[0], &audio_config) ||
-      !FrameReceiverConfigFromArg(isolate, args[1], &video_config) ||
-      !IPEndPointFromArg(isolate, args[2], &local_endpoint)) {
-    return;
-  }
-
-  const int max_width = args[3]->ToInt32(args.GetIsolate())->Value();
-  const int max_height = args[4]->ToInt32(args.GetIsolate())->Value();
-  const double fps = args[5].As<v8::Number>()->Value();
-
-  if (fps <= 1) {
-    args.GetIsolate()->ThrowException(v8::Exception::TypeError(
-        v8::String::NewFromUtf8(args.GetIsolate(), kInvalidFPS,
-                                v8::NewStringType::kNormal)
-            .ToLocalChecked()));
-    return;
-  }
-
-  const std::string url = *v8::String::Utf8Value(isolate, args[6]);
-  blink::WebMediaStream stream =
-      blink::WebMediaStreamRegistry::LookupMediaStreamDescriptor(GURL(url));
-
-  if (stream.IsNull()) {
-    args.GetIsolate()->ThrowException(v8::Exception::TypeError(
-        v8::String::NewFromUtf8(args.GetIsolate(), kInvalidMediaStreamURL,
-                                v8::NewStringType::kNormal)
-            .ToLocalChecked()));
-    return;
-  }
-
-  media::VideoCaptureFormat capture_format(gfx::Size(max_width, max_height),
-                                           fps, media::PIXEL_FORMAT_I420);
-
-  video_config.target_frame_rate = fps;
-  audio_config.target_frame_rate = 100;
-
-  media::AudioParameters params(
-      media::AudioParameters::AUDIO_PCM_LINEAR,
-      media::GuessChannelLayout(audio_config.channels),
-      audio_config.rtp_timebase,  // sampling rate
-      static_cast<int>(audio_config.rtp_timebase /
-                       audio_config.target_frame_rate));
-
-  if (!params.IsValid()) {
-    args.GetIsolate()->ThrowException(v8::Exception::TypeError(
-        v8::String::NewFromUtf8(args.GetIsolate(), kInvalidAudioParams,
-                                v8::NewStringType::kNormal)
-            .ToLocalChecked()));
-    return;
-  }
-
-  std::unique_ptr<base::DictionaryValue> options;
-  if (args.Length() >= 9) {
-    std::unique_ptr<base::Value> options_value =
-        V8ValueConverter::Create()->FromV8Value(args[8],
-                                                context()->v8_context());
-    if (!options_value->is_none()) {
-      options = base::DictionaryValue::From(std::move(options_value));
-      if (!options) {
-        args.GetIsolate()->ThrowException(v8::Exception::TypeError(
-            v8::String::NewFromUtf8(args.GetIsolate(), kUnableToConvertArgs,
-                                    v8::NewStringType::kNormal)
-                .ToLocalChecked()));
-        return;
-      }
-    }
-  }
-
-  if (!options) {
-    options.reset(new base::DictionaryValue());
-  }
-
-  v8::CopyablePersistentTraits<v8::Function>::CopyablePersistent error_callback;
-  error_callback.Reset(args.GetIsolate(),
-                       v8::Local<v8::Function>(args[7].As<v8::Function>()));
-
-  session->Start(
-      audio_config, video_config, local_endpoint, remote_endpoint,
-      std::move(options), capture_format,
-      base::Bind(&CastStreamingNativeHandler::AddTracksToMediaStream,
-                 weak_factory_.GetWeakPtr(), url, params),
-      base::Bind(&CastStreamingNativeHandler::CallReceiverErrorCallback,
-                 weak_factory_.GetWeakPtr(), error_callback));
-}
-
 void CastStreamingNativeHandler::CallReceiverErrorCallback(
     v8::CopyablePersistentTraits<v8::Function>::CopyablePersistent function,
     const std::string& error_message) {
@@ -1036,27 +917,4 @@
                               1, &arg);
 }
 
-void CastStreamingNativeHandler::AddTracksToMediaStream(
-    const std::string& url,
-    const media::AudioParameters& params,
-    scoped_refptr<media::AudioCapturerSource> audio,
-    std::unique_ptr<media::VideoCapturerSource> video) {
-  blink::WebMediaStream web_stream =
-      blink::WebMediaStreamRegistry::LookupMediaStreamDescriptor(GURL(url));
-  if (web_stream.IsNull()) {
-    LOG(DFATAL) << "Stream not found.";
-    return;
-  }
-  if (!content::AddAudioTrackToMediaStream(
-          audio, params.sample_rate(), params.channel_layout(),
-          params.frames_per_buffer(), true,  // is_remote
-          &web_stream)) {
-    LOG(ERROR) << "Failed to add Cast audio track to media stream.";
-  }
-  if (!content::AddVideoTrackToMediaStream(std::move(video), true,  // is_remote
-                                           &web_stream)) {
-    LOG(ERROR) << "Failed to add Cast video track to media stream.";
-  }
-}
-
 }  // namespace extensions
diff --git a/chrome/renderer/extensions/cast_streaming_native_handler.h b/chrome/renderer/extensions/cast_streaming_native_handler.h
index 4d4c41fb..6ba698b 100644
--- a/chrome/renderer/extensions/cast_streaming_native_handler.h
+++ b/chrome/renderer/extensions/cast_streaming_native_handler.h
@@ -26,9 +26,6 @@
 }
 
 namespace media {
-class AudioCapturerSource;
-class AudioParameters;
-class VideoCapturerSource;
 namespace cast {
 struct FrameReceiverConfig;
 }
@@ -76,9 +73,6 @@
   void StopCastUdpTransport(
       const v8::FunctionCallbackInfo<v8::Value>& args);
 
-  void StartCastRtpReceiver(
-      const v8::FunctionCallbackInfo<v8::Value>& args);
-
   void ToggleLogging(const v8::FunctionCallbackInfo<v8::Value>& args);
   void GetRawEvents(const v8::FunctionCallbackInfo<v8::Value>& args);
   void GetStats(const v8::FunctionCallbackInfo<v8::Value>& args);
@@ -93,14 +87,6 @@
   void CallStopCallback(int stream_id) const;
   void CallErrorCallback(int stream_id, const std::string& message) const;
 
-  // Callback called after a cast receiver has been started. Adds the
-  // output audio/video streams to the MediaStream specified by |url|.
-  void AddTracksToMediaStream(
-      const std::string& url,
-      const media::AudioParameters& params,
-      scoped_refptr<media::AudioCapturerSource> audio,
-      std::unique_ptr<media::VideoCapturerSource> video);
-
   // |function| is a javascript function that will take |error_message| as
   // an argument. Called when something goes wrong in a cast receiver.
   void CallReceiverErrorCallback(
diff --git a/chrome/renderer/resources/extensions/cast_streaming_receiver_session_custom_bindings.js b/chrome/renderer/resources/extensions/cast_streaming_receiver_session_custom_bindings.js
index 7510ace..3dadf0e 100644
--- a/chrome/renderer/resources/extensions/cast_streaming_receiver_session_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/cast_streaming_receiver_session_custom_bindings.js
@@ -14,8 +14,6 @@
   apiFunctions.setHandleRequest(
       'createAndBind',
       function(ap, vp, local, weidgth, height, fr, url, cb, op) {
-        natives.StartCastRtpReceiver(
-            ap, vp, local, weidgth, height, fr, url, cb, op);
   });
 });
 
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index bfe2180..2b76bbc 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -525,6 +525,7 @@
       "../browser/browsing_data/browsing_data_local_storage_helper_browsertest.cc",
       "../browser/browsing_data/browsing_data_remover_browsertest.cc",
       "../browser/browsing_data/counters/autofill_counter_browsertest.cc",
+      "../browser/browsing_data/counters/browsing_data_counter_utils_browsertest.cc",
       "../browser/browsing_data/counters/cache_counter_browsertest.cc",
       "../browser/browsing_data/counters/conditional_cache_counting_helper_browsertest.cc",
       "../browser/browsing_data/counters/downloads_counter_browsertest.cc",
@@ -5187,6 +5188,8 @@
       "../browser/sync/test/integration/dictionary_helper.h",
       "../browser/sync/test/integration/dictionary_load_observer.cc",
       "../browser/sync/test/integration/dictionary_load_observer.h",
+      "../browser/sync/test/integration/encryption_helper.cc",
+      "../browser/sync/test/integration/encryption_helper.h",
       "../browser/sync/test/integration/extension_settings_helper.cc",
       "../browser/sync/test/integration/extension_settings_helper.h",
       "../browser/sync/test/integration/extensions_helper.cc",
@@ -5322,6 +5325,7 @@
       "../browser/sync/test/integration/single_client_apps_sync_test.cc",
       "../browser/sync/test/integration/single_client_arc_package_sync_test.cc",
       "../browser/sync/test/integration/single_client_bookmarks_sync_test.cc",
+      "../browser/sync/test/integration/single_client_custom_passphrase_sync_test.cc",
       "../browser/sync/test/integration/single_client_dictionary_sync_test.cc",
       "../browser/sync/test/integration/single_client_directory_sync_test.cc",
       "../browser/sync/test/integration/single_client_extensions_sync_test.cc",
diff --git a/components/data_use_measurement/core/BUILD.gn b/components/data_use_measurement/core/BUILD.gn
index c4342d25..7e2c5b0 100644
--- a/components/data_use_measurement/core/BUILD.gn
+++ b/components/data_use_measurement/core/BUILD.gn
@@ -33,10 +33,6 @@
     "//google_apis",
     "//net",
   ]
-
-  if (!is_ios) {
-    deps += [ "//components/domain_reliability" ]
-  }
 }
 
 source_set("unit_tests") {
diff --git a/components/data_use_measurement/core/DEPS b/components/data_use_measurement/core/DEPS
index 0b56a36..de111298 100644
--- a/components/data_use_measurement/core/DEPS
+++ b/components/data_use_measurement/core/DEPS
@@ -1,6 +1,4 @@
 include_rules = [
   "+net",
   "+components/metrics",
-  "+components/domain_reliability",
-  "+google_apis/gaia",
 ]
diff --git a/components/data_use_measurement/core/data_use_ascriber.cc b/components/data_use_measurement/core/data_use_ascriber.cc
index 2f1b664..462d320 100644
--- a/components/data_use_measurement/core/data_use_ascriber.cc
+++ b/components/data_use_measurement/core/data_use_ascriber.cc
@@ -7,9 +7,7 @@
 #include <memory>
 #include <utility>
 
-#include "components/data_use_measurement/core/data_use_network_delegate.h"
 #include "components/data_use_measurement/core/data_use_recorder.h"
-#include "components/data_use_measurement/core/url_request_classifier.h"
 #include "net/http/http_response_headers.h"
 
 namespace data_use_measurement {
@@ -17,14 +15,6 @@
 DataUseAscriber::DataUseAscriber() {}
 DataUseAscriber::~DataUseAscriber() {}
 
-std::unique_ptr<net::NetworkDelegate> DataUseAscriber::CreateNetworkDelegate(
-    std::unique_ptr<net::NetworkDelegate> wrapped_network_delegate,
-    const metrics::UpdateUsagePrefCallbackType& metrics_data_use_forwarder) {
-  return std::make_unique<data_use_measurement::DataUseNetworkDelegate>(
-      std::move(wrapped_network_delegate), this, CreateURLRequestClassifier(),
-      metrics_data_use_forwarder);
-}
-
 void DataUseAscriber::OnBeforeUrlRequest(net::URLRequest* request) {
   DataUseRecorder* recorder = GetOrCreateDataUseRecorder(request);
   if (!recorder)
diff --git a/components/data_use_measurement/core/data_use_ascriber.h b/components/data_use_measurement/core/data_use_ascriber.h
index c20e927..984bec89 100644
--- a/components/data_use_measurement/core/data_use_ascriber.h
+++ b/components/data_use_measurement/core/data_use_ascriber.h
@@ -12,7 +12,6 @@
 #include "base/observer_list.h"
 #include "base/threading/thread_checker.h"
 #include "components/data_use_measurement/core/data_use_measurement.h"
-#include "components/metrics/data_use_tracker.h"
 #include "url/gurl.h"
 
 namespace net {
@@ -69,9 +68,8 @@
   virtual ~DataUseAscriber();
 
   // Creates a network delegate that will be used to track data use.
-  std::unique_ptr<net::NetworkDelegate> CreateNetworkDelegate(
-      std::unique_ptr<net::NetworkDelegate> wrapped_network_delegate,
-      const metrics::UpdateUsagePrefCallbackType& metrics_data_use_forwarder);
+  virtual std::unique_ptr<net::NetworkDelegate> CreateNetworkDelegate(
+      std::unique_ptr<net::NetworkDelegate> wrapped_network_delegate) = 0;
 
   // Returns the DataUseRecorder to which data usage for the given URL should
   // be ascribed. If no existing DataUseRecorder exists, a new one will be
diff --git a/components/data_use_measurement/core/data_use_measurement.cc b/components/data_use_measurement/core/data_use_measurement.cc
index 7005768..c786ced0 100644
--- a/components/data_use_measurement/core/data_use_measurement.cc
+++ b/components/data_use_measurement/core/data_use_measurement.cc
@@ -16,8 +16,6 @@
 #include "components/data_use_measurement/core/data_use_recorder.h"
 #include "components/data_use_measurement/core/data_use_user_data.h"
 #include "components/data_use_measurement/core/url_request_classifier.h"
-#include "components/domain_reliability/uploader.h"
-#include "google_apis/gaia/gaia_auth_util.h"
 #include "net/base/network_change_notifier.h"
 #include "net/base/upload_data_stream.h"
 #include "net/http/http_response_headers.h"
@@ -75,10 +73,8 @@
 
 DataUseMeasurement::DataUseMeasurement(
     std::unique_ptr<URLRequestClassifier> url_request_classifier,
-    const metrics::UpdateUsagePrefCallbackType& metrics_data_use_forwarder,
     DataUseAscriber* ascriber)
     : url_request_classifier_(std::move(url_request_classifier)),
-      metrics_data_use_forwarder_(metrics_data_use_forwarder),
       ascriber_(ascriber)
 #if defined(OS_ANDROID)
       ,
@@ -112,20 +108,7 @@
   DataUseUserData* data_use_user_data = reinterpret_cast<DataUseUserData*>(
       request->GetUserData(DataUseUserData::kUserDataKey));
   if (!data_use_user_data) {
-    DataUseUserData::ServiceName service_name =
-        DataUseUserData::ServiceName::NOT_TAGGED;
-    if (!IsUserRequest(*request) &&
-        domain_reliability::DomainReliabilityUploader::
-            OriginatedFromDomainReliability(*request)) {
-      // Detect if the request originated from DomainReliability.
-      // DataUseUserData::AttachToFetcher() cannot be called from domain
-      // reliability, since it sets userdata on URLFetcher for its purposes.
-      service_name = DataUseUserData::ServiceName::DOMAIN_RELIABILITY;
-    } else if (gaia::RequestOriginatedFromGaia(*request)) {
-      service_name = DataUseUserData::ServiceName::GAIA;
-    }
-
-    data_use_user_data = new DataUseUserData(service_name, CurrentAppState());
+    data_use_user_data = new DataUseUserData(CurrentAppState());
     request->SetUserData(DataUseUserData::kUserDataKey,
                          base::WrapUnique(data_use_user_data));
   } else {
@@ -137,7 +120,12 @@
                                           const GURL& new_location) {
   // Recording data use of request on redirects.
   // TODO(rajendrant): May not be needed when http://crbug/651957 is fixed.
-  UpdateDataUsePrefs(request);
+  UpdateDataUseToMetricsService(
+      request.GetTotalSentBytes() + request.GetTotalReceivedBytes(),
+      net::NetworkChangeNotifier::IsConnectionCellular(
+          net::NetworkChangeNotifier::GetConnectionType()),
+      IsMetricsServiceRequest(
+          request.traffic_annotation().unique_id_hash_code));
   ReportServicesMessageSizeUMA(request);
   if (url_request_classifier_->IsFavIconRequest(request))
     RecordFavIconDataUse(request);
@@ -176,7 +164,12 @@
                                      bool started) {
   // TODO(amohammadkhan): Verify that there is no double recording in data use
   // of redirected requests.
-  UpdateDataUsePrefs(request);
+  UpdateDataUseToMetricsService(
+      request.GetTotalSentBytes() + request.GetTotalReceivedBytes(),
+      net::NetworkChangeNotifier::IsConnectionCellular(
+          net::NetworkChangeNotifier::GetConnectionType()),
+      IsMetricsServiceRequest(
+          request.traffic_annotation().unique_id_hash_code));
   ReportServicesMessageSizeUMA(request);
   RecordPageTransitionUMA(request);
 #if defined(OS_ANDROID)
@@ -261,30 +254,6 @@
   }
 }
 
-void DataUseMeasurement::UpdateDataUsePrefs(
-    const net::URLRequest& request) const {
-  bool is_connection_cellular =
-      net::NetworkChangeNotifier::IsConnectionCellular(
-          net::NetworkChangeNotifier::GetConnectionType());
-
-  DataUseUserData* attached_service_data = static_cast<DataUseUserData*>(
-      request.GetUserData(DataUseUserData::kUserDataKey));
-  DataUseUserData::ServiceName service_name =
-      attached_service_data ? attached_service_data->service_name()
-                            : DataUseUserData::NOT_TAGGED;
-
-  // Update data use prefs for cellular connections.
-  // TODO(rajendrant): Change this to only report data use of user-initiated
-  // traffic and metrics services (UMA, UKM). This will help to remove the
-  // DataUseUserData::ServiceName enum.
-  if (!metrics_data_use_forwarder_.is_null()) {
-    metrics_data_use_forwarder_.Run(
-        DataUseUserData::GetServiceNameAsString(service_name),
-        request.GetTotalSentBytes() + request.GetTotalReceivedBytes(),
-        is_connection_cellular);
-  }
-}
-
 #if defined(OS_ANDROID)
 void DataUseMeasurement::OnApplicationStateChangeForTesting(
     base::android::ApplicationState application_state) {
@@ -490,4 +459,16 @@
          kUserInitiatedTrafficAnnotations.end();
 }
 
+// static
+bool DataUseMeasurement::IsMetricsServiceRequest(
+    int32_t network_traffic_annotation_hash_id) {
+  static const std::set<int32_t> kMetricsServiceTrafficAnnotations = {
+      COMPUTE_NETWORK_TRAFFIC_ANNOTATION_ID_HASH("metrics_report_uma"),
+      COMPUTE_NETWORK_TRAFFIC_ANNOTATION_ID_HASH("metrics_report_ukm"),
+  };
+  return kMetricsServiceTrafficAnnotations.find(
+             network_traffic_annotation_hash_id) !=
+         kMetricsServiceTrafficAnnotations.end();
+}
+
 }  // namespace data_use_measurement
diff --git a/components/data_use_measurement/core/data_use_measurement.h b/components/data_use_measurement/core/data_use_measurement.h
index 0dee70c..31951de8 100644
--- a/components/data_use_measurement/core/data_use_measurement.h
+++ b/components/data_use_measurement/core/data_use_measurement.h
@@ -44,6 +44,10 @@
   // Returns true if the |request| is initiated by user traffic.
   static bool IsUserRequest(const net::URLRequest& request);
 
+  // Returns true if the NTA hash is one used by metrics (UMA, UKM) component.
+  static bool IsMetricsServiceRequest(
+      int32_t network_traffic_annotation_hash_id);
+
   // Returns the content-type saved in the request userdata when the response
   // headers were received.
   static DataUseUserData::DataUseContentType GetContentTypeForRequest(
@@ -51,9 +55,8 @@
 
   DataUseMeasurement(
       std::unique_ptr<URLRequestClassifier> url_request_classifier,
-      const metrics::UpdateUsagePrefCallbackType& metrics_data_use_forwarder,
       DataUseAscriber* ascriber);
-  ~DataUseMeasurement();
+  virtual ~DataUseMeasurement();
 
   // Called before a request is sent.
   void OnBeforeURLRequest(net::URLRequest* request);
@@ -81,6 +84,12 @@
       base::android::ApplicationState application_state);
 #endif
 
+  // Updates the data use to metrics service. |is_metrics_service_usage|
+  // indicates if the data use is from metrics component.
+  virtual void UpdateDataUseToMetricsService(int64_t total_bytes,
+                                             bool is_cellular,
+                                             bool is_metrics_service_usage) = 0;
+
  private:
   friend class DataUseMeasurementTest;
   FRIEND_TEST_ALL_PREFIXES(DataUseMeasurementTest,
@@ -134,9 +143,6 @@
                         TrafficDirection dir,
                         int64_t bytes);
 
-  // Updates the data use of the |request|, thus |request| must be non-null.
-  void UpdateDataUsePrefs(const net::URLRequest& request) const;
-
   // Reports the message size of the service requests.
   void ReportServicesMessageSizeUMA(const net::URLRequest& request);
 
@@ -171,12 +177,6 @@
   // Classifier for identifying if an URL request is user initiated.
   std::unique_ptr<URLRequestClassifier> url_request_classifier_;
 
-  // Callback for updating metrics about data use of user traffic and services.
-  // TODO(rajendrant): Change this to not report data use service name. Instead
-  // pass a bool to distinguish if the data use is for metrics services
-  // (UMA, UKM).
-  metrics::UpdateUsagePrefCallbackType metrics_data_use_forwarder_;
-
   // DataUseAscriber used to get the attributes of data use.
   DataUseAscriber* ascriber_;
 
diff --git a/components/data_use_measurement/core/data_use_measurement_unittest.cc b/components/data_use_measurement/core/data_use_measurement_unittest.cc
index cf6c15ec..8101408 100644
--- a/components/data_use_measurement/core/data_use_measurement_unittest.cc
+++ b/components/data_use_measurement/core/data_use_measurement_unittest.cc
@@ -71,6 +71,11 @@
     return &recorder_;
   }
 
+  std::unique_ptr<net::NetworkDelegate> CreateNetworkDelegate(
+      std::unique_ptr<net::NetworkDelegate> wrapped_network_delegate) override {
+    return nullptr;
+  }
+
   std::unique_ptr<URLRequestClassifier> CreateURLRequestClassifier()
       const override {
     return nullptr;
@@ -82,6 +87,22 @@
   DataUseRecorder recorder_;
 };
 
+class TestDataUseMeasurement : public DataUseMeasurement {
+ public:
+  TestDataUseMeasurement(
+      std::unique_ptr<URLRequestClassifier> url_request_classifier,
+      DataUseAscriber* ascriber)
+      : DataUseMeasurement(std::move(url_request_classifier), ascriber) {}
+
+  void UpdateDataUseToMetricsService(int64_t total_bytes,
+                                     bool is_cellular,
+                                     bool is_metrics_service_usage) override {
+    is_data_use_forwarder_called_ = true;
+  }
+
+  bool is_data_use_forwarder_called_ = false;
+};
+
 // The more usual initialization of kUserDataKey would be along the lines of
 //     const void* const TestURLRequestClassifier::kUserDataKey =
 //         &TestURLRequestClassifier::kUserDataKey;
@@ -104,8 +125,6 @@
       : url_request_classifier_(new TestURLRequestClassifier()),
         data_use_measurement_(
             std::unique_ptr<URLRequestClassifier>(url_request_classifier_),
-            base::Bind(&DataUseMeasurementTest::FakeDataUseforwarder,
-                       base::Unretained(this)),
             &ascriber_) {
     // During the test it is expected to not have cellular connection.
     DCHECK(!net::NetworkChangeNotifier::IsConnectionCellular(
@@ -140,7 +159,6 @@
       request->SetUserData(
           data_use_measurement::DataUseUserData::kUserDataKey,
           std::make_unique<data_use_measurement::DataUseUserData>(
-              data_use_measurement::DataUseUserData::SUGGESTIONS,
               data_use_measurement_.CurrentAppState()));
     }
 
@@ -196,7 +214,9 @@
 
   DataUseMeasurement* data_use_measurement() { return &data_use_measurement_; }
 
-  bool IsDataUseForwarderCalled() { return is_data_use_forwarder_called_; }
+  bool IsDataUseForwarderCalled() {
+    return data_use_measurement_.is_data_use_forwarder_called_;
+  }
 
  protected:
   void InitializeContext() {
@@ -206,22 +226,15 @@
     context_->Init();
   }
 
-  void FakeDataUseforwarder(const std::string& service_name,
-                            int message_size,
-                            bool is_celllular) {
-    is_data_use_forwarder_called_ = true;
-  }
-
   base::MessageLoopForIO loop_;
 
   TestDataUseAscriber ascriber_;
   TestURLRequestClassifier* url_request_classifier_;
-  DataUseMeasurement data_use_measurement_;
+  TestDataUseMeasurement data_use_measurement_;
 
   std::unique_ptr<net::MockClientSocketFactory> socket_factory_;
   std::unique_ptr<net::TestURLRequestContext> context_;
   const std::string kConnectionType = "NotCellular";
-  bool is_data_use_forwarder_called_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(DataUseMeasurementTest);
 };
diff --git a/components/data_use_measurement/core/data_use_network_delegate.cc b/components/data_use_measurement/core/data_use_network_delegate.cc
index 0ab9e375..f78130b 100644
--- a/components/data_use_measurement/core/data_use_network_delegate.cc
+++ b/components/data_use_measurement/core/data_use_network_delegate.cc
@@ -15,13 +15,10 @@
 DataUseNetworkDelegate::DataUseNetworkDelegate(
     std::unique_ptr<NetworkDelegate> nested_network_delegate,
     DataUseAscriber* ascriber,
-    std::unique_ptr<URLRequestClassifier> url_request_classifier,
-    const metrics::UpdateUsagePrefCallbackType& metrics_data_use_forwarder)
+    std::unique_ptr<DataUseMeasurement> data_use_measurement)
     : net::LayeredNetworkDelegate(std::move(nested_network_delegate)),
       ascriber_(ascriber),
-      data_use_measurement_(std::move(url_request_classifier),
-                            metrics_data_use_forwarder,
-                            ascriber_) {
+      data_use_measurement_(std::move(data_use_measurement)) {
   DCHECK(ascriber);
 }
 
@@ -31,13 +28,13 @@
     net::URLRequest* request,
     GURL* new_url) {
   ascriber_->OnBeforeUrlRequest(request);
-  data_use_measurement_.OnBeforeURLRequest(request);
+  data_use_measurement_->OnBeforeURLRequest(request);
 }
 
 void DataUseNetworkDelegate::OnBeforeRedirectInternal(
     net::URLRequest* request,
     const GURL& new_location) {
-  data_use_measurement_.OnBeforeRedirect(*request, new_location);
+  data_use_measurement_->OnBeforeRedirect(*request, new_location);
 }
 
 void DataUseNetworkDelegate::OnHeadersReceivedInternal(
@@ -45,28 +42,28 @@
     const net::HttpResponseHeaders* original_response_headers,
     scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
     GURL* allowed_unsafe_redirect_url) {
-  data_use_measurement_.OnHeadersReceived(request, original_response_headers);
+  data_use_measurement_->OnHeadersReceived(request, original_response_headers);
 }
 
 void DataUseNetworkDelegate::OnNetworkBytesReceivedInternal(
     net::URLRequest* request,
     int64_t bytes_received) {
   ascriber_->OnNetworkBytesReceived(request, bytes_received);
-  data_use_measurement_.OnNetworkBytesReceived(*request, bytes_received);
+  data_use_measurement_->OnNetworkBytesReceived(*request, bytes_received);
 }
 
 void DataUseNetworkDelegate::OnNetworkBytesSentInternal(
     net::URLRequest* request,
     int64_t bytes_sent) {
   ascriber_->OnNetworkBytesSent(request, bytes_sent);
-  data_use_measurement_.OnNetworkBytesSent(*request, bytes_sent);
+  data_use_measurement_->OnNetworkBytesSent(*request, bytes_sent);
 }
 
 void DataUseNetworkDelegate::OnCompletedInternal(net::URLRequest* request,
                                                  bool started,
                                                  int net_error) {
   ascriber_->OnUrlRequestCompleted(request, started);
-  data_use_measurement_.OnCompleted(*request, started);
+  data_use_measurement_->OnCompleted(*request, started);
 }
 
 void DataUseNetworkDelegate::OnURLRequestDestroyedInternal(
diff --git a/components/data_use_measurement/core/data_use_network_delegate.h b/components/data_use_measurement/core/data_use_network_delegate.h
index 5a80d9aa..adf9366 100644
--- a/components/data_use_measurement/core/data_use_network_delegate.h
+++ b/components/data_use_measurement/core/data_use_network_delegate.h
@@ -11,7 +11,6 @@
 
 #include "base/macros.h"
 #include "components/data_use_measurement/core/data_use_measurement.h"
-#include "components/metrics/data_use_tracker.h"
 #include "net/base/completion_callback.h"
 #include "net/base/layered_network_delegate.h"
 
@@ -32,8 +31,7 @@
   DataUseNetworkDelegate(
       std::unique_ptr<net::NetworkDelegate> nested_network_delegate,
       DataUseAscriber* ascriber,
-      std::unique_ptr<URLRequestClassifier> url_request_classifier,
-      const metrics::UpdateUsagePrefCallbackType& metrics_data_use_forwarder);
+      std::unique_ptr<DataUseMeasurement> data_use_measurement);
 
   ~DataUseNetworkDelegate() override;
 
@@ -66,7 +64,8 @@
   DataUseAscriber* ascriber_;
 
   // Component to report data use UMA.
-  data_use_measurement::DataUseMeasurement data_use_measurement_;
+  std::unique_ptr<data_use_measurement::DataUseMeasurement>
+      data_use_measurement_;
 };
 
 }  // namespace data_use_measurement
diff --git a/components/data_use_measurement/core/data_use_network_delegate_unittest.cc b/components/data_use_measurement/core/data_use_network_delegate_unittest.cc
index 077f0cf..07bc4dc8 100644
--- a/components/data_use_measurement/core/data_use_network_delegate_unittest.cc
+++ b/components/data_use_measurement/core/data_use_network_delegate_unittest.cc
@@ -54,12 +54,29 @@
     return nullptr;
   }
 
+  std::unique_ptr<net::NetworkDelegate> CreateNetworkDelegate(
+      std::unique_ptr<net::NetworkDelegate> wrapped_network_delegate) override {
+    return nullptr;
+  }
+
   std::unique_ptr<URLRequestClassifier> CreateURLRequestClassifier()
       const override {
     return nullptr;
   }
 };
 
+class TestDataUseMeasurement : public DataUseMeasurement {
+ public:
+  TestDataUseMeasurement(
+      std::unique_ptr<URLRequestClassifier> url_request_classifier,
+      DataUseAscriber* ascriber)
+      : DataUseMeasurement(std::move(url_request_classifier), ascriber) {}
+
+  void UpdateDataUseToMetricsService(int64_t total_bytes,
+                                     bool is_cellular,
+                                     bool is_metrics_service_usage) override {}
+};
+
 // static
 const void* const TestURLRequestClassifier::kUserDataKey =
     &TestURLRequestClassifier::kUserDataKey;
@@ -108,7 +125,6 @@
     request->SetUserData(
         data_use_measurement::DataUseUserData::kUserDataKey,
         std::make_unique<data_use_measurement::DataUseUserData>(
-            data_use_measurement::DataUseUserData::SUGGESTIONS,
             data_use_measurement::DataUseUserData::FOREGROUND));
   }
   request->Start();
@@ -120,10 +136,12 @@
  public:
   DataUseNetworkDelegateTest()
       : context_(true),
-        data_use_network_delegate_(std::make_unique<net::TestNetworkDelegate>(),
-                                   &test_data_use_ascriber_,
-                                   std::make_unique<TestURLRequestClassifier>(),
-                                   metrics::UpdateUsagePrefCallbackType()) {
+        data_use_network_delegate_(
+            std::make_unique<net::TestNetworkDelegate>(),
+            &test_data_use_ascriber_,
+            std::make_unique<TestDataUseMeasurement>(
+                std::make_unique<TestURLRequestClassifier>(),
+                &test_data_use_ascriber_)) {
     context_.set_client_socket_factory(&mock_socket_factory_);
     context_.set_network_delegate(&data_use_network_delegate_);
     context_.Init();
diff --git a/components/data_use_measurement/core/data_use_user_data.cc b/components/data_use_measurement/core/data_use_user_data.cc
index ca3bb4f..9887faa 100644
--- a/components/data_use_measurement/core/data_use_user_data.cc
+++ b/components/data_use_measurement/core/data_use_user_data.cc
@@ -30,10 +30,8 @@
 
 }  // namespace
 
-DataUseUserData::DataUseUserData(ServiceName service_name, AppState app_state)
-    : service_name_(service_name),
-      app_state_(app_state),
-      content_type_(DataUseContentType::OTHER) {}
+DataUseUserData::DataUseUserData(AppState app_state)
+    : app_state_(app_state), content_type_(DataUseContentType::OTHER) {}
 
 DataUseUserData::~DataUseUserData() {}
 
@@ -44,105 +42,12 @@
 // static
 std::unique_ptr<base::SupportsUserData::Data> DataUseUserData::Create(
     ServiceName service_name) {
-  return std::make_unique<DataUseUserData>(service_name, GetCurrentAppState());
-}
-
-// static
-std::string DataUseUserData::GetServiceNameAsString(ServiceName service_name) {
-  switch (service_name) {
-    case SUGGESTIONS:
-      return "Suggestions";
-    case NOT_TAGGED:
-      return "NotTagged";
-    case TRANSLATE:
-      return "Translate";
-    case SYNC:
-      return "Sync";
-    case OMNIBOX:
-      return "Omnibox";
-    case INVALIDATION:
-      return "Invalidation";
-    case RAPPOR:
-      return "Rappor";
-    case VARIATIONS:
-      return "Variations";
-    case UMA:
-      return "UMA";
-    case DOMAIN_RELIABILITY:
-      return "DomainReliability";
-    case PROFILE_DOWNLOADER:
-      return "ProfileDownloader";
-    case GOOGLE_URL_TRACKER:
-      return "GoogleURLTracker";
-    case AUTOFILL:
-      return "Autofill";
-    case POLICY:
-      return "Policy";
-    case SPELL_CHECKER:
-      return "SpellChecker";
-    case NTP_SNIPPETS_OBSOLETE:
-      return "NTPSnippetsObsolete";
-    case SAFE_BROWSING:
-      return "SafeBrowsing";
-    case DATA_REDUCTION_PROXY:
-      return "DataReductionProxy";
-    case PRECACHE:
-      return "Precache";
-    case NTP_TILES:
-      return "NTPTiles";
-    case FEEDBACK_UPLOADER:
-      return "FeedbackUploader";
-    case TRACING_UPLOADER:
-      return "TracingUploader";
-    case DOM_DISTILLER:
-      return "DOMDistiller";
-    case CLOUD_PRINT:
-      return "CloudPrint";
-    case SEARCH_PROVIDER_LOGOS:
-      return "SearchProviderLogos";
-    case UPDATE_CLIENT:
-      return "UpdateClient";
-    case GCM_DRIVER:
-      return "GCMDriver";
-    case WEB_HISTORY_SERVICE:
-      return "WebHistoryService";
-    case NETWORK_TIME_TRACKER:
-      return "NetworkTimeTracker";
-    case SUPERVISED_USER:
-      return "SupervisedUser";
-    case IMAGE_FETCHER_UNTAGGED:
-      return "ImageFetcherUntagged";
-    case GAIA:
-      return "GAIA";
-    case CAPTIVE_PORTAL:
-      return "CaptivePortal";
-    case WEB_RESOURCE_SERVICE:
-      return "WebResourceService";
-    case SIGNIN:
-      return "Signin";
-    case NTP_SNIPPETS_SUGGESTIONS:
-      return "NTPSnippetsSuggestions";
-    case NTP_SNIPPETS_THUMBNAILS:
-      return "NTPSnippetsThumbnails";
-    case DOODLE:
-      return "Doodle";
-    case UKM:
-      return "UKM";
-    case PAYMENTS:
-      return "Payments";
-    case LARGE_ICON_SERVICE:
-      return "LargeIconService";
-    case MACHINE_INTELLIGENCE:
-      return "MachineIntelligence";
-  }
-  return "INVALID";
+  return std::make_unique<DataUseUserData>(GetCurrentAppState());
 }
 
 // static
 void DataUseUserData::AttachToFetcher(net::URLFetcher* fetcher,
                                       ServiceName service_name) {
-  fetcher->SetURLRequestUserData(kUserDataKey,
-                                 base::Bind(&Create, service_name));
 }
 
 }  // namespace data_use_measurement
diff --git a/components/data_use_measurement/core/data_use_user_data.h b/components/data_use_measurement/core/data_use_user_data.h
index a6e110d..522f84b 100644
--- a/components/data_use_measurement/core/data_use_user_data.h
+++ b/components/data_use_measurement/core/data_use_user_data.h
@@ -25,6 +25,8 @@
   // Please keep them synced after any updates. Also add service name to
   // GetServiceNameAsString function and the same service name to
   // DataUse.Service.Types histogram suffixes in histograms.xml
+  // TODO(rajendrant): Remove this once all AttachToFetcher() callsites are
+  // removed.
   enum ServiceName {
     NOT_TAGGED,
     SUGGESTIONS,
@@ -95,7 +97,7 @@
   // platforms it is always FOREGROUND.
   enum AppState { UNKNOWN, BACKGROUND, FOREGROUND };
 
-  DataUseUserData(ServiceName service_name, AppState app_state);
+  explicit DataUseUserData(AppState app_state);
   ~DataUseUserData() override;
 
   // Helper function to create DataUseUserData.
@@ -107,11 +109,10 @@
 
   // Services should use this function to attach their |service_name| to the
   // URLFetcher serving them.
+  // TODO(rajendrant): Remove this once all callsites are removed.
   static void AttachToFetcher(net::URLFetcher* fetcher,
                               ServiceName service_name);
 
-  ServiceName service_name() const { return service_name_; }
-
   AppState app_state() const { return app_state_; }
 
   void set_app_state(AppState app_state) { app_state_ = app_state; }
@@ -126,8 +127,6 @@
   static const void* const kUserDataKey;
 
  private:
-  const ServiceName service_name_;
-
   // App state when network access was performed for the request previously.
   AppState app_state_;
 
diff --git a/components/domain_reliability/uploader.cc b/components/domain_reliability/uploader.cc
index 1bda01b..1f109a5 100644
--- a/components/domain_reliability/uploader.cc
+++ b/components/domain_reliability/uploader.cc
@@ -215,12 +215,6 @@
 }
 
 // static
-bool DomainReliabilityUploader::OriginatedFromDomainReliability(
-    const net::URLRequest& request) {
-  return request.GetUserData(UploadUserData::kUserDataKey) != nullptr;
-}
-
-// static
 int DomainReliabilityUploader::GetURLRequestUploadDepth(
     const net::URLRequest& request) {
   UploadUserData* data = static_cast<UploadUserData*>(
diff --git a/components/domain_reliability/uploader.h b/components/domain_reliability/uploader.h
index 90c9d1e13..51ff64c 100644
--- a/components/domain_reliability/uploader.h
+++ b/components/domain_reliability/uploader.h
@@ -55,9 +55,6 @@
       const scoped_refptr<net::URLRequestContextGetter>&
           url_request_context_getter);
 
-  // Returns true if the request originated from domain reliability uploader.
-  static bool OriginatedFromDomainReliability(const net::URLRequest& request);
-
   // Uploads |report_json| to |upload_url| and calls |callback| when the upload
   // has either completed or failed.
   virtual void UploadReport(const std::string& report_json,
diff --git a/components/exo/client_controlled_shell_surface.cc b/components/exo/client_controlled_shell_surface.cc
index 2f4d9d7..17a5d0f 100644
--- a/components/exo/client_controlled_shell_surface.cc
+++ b/components/exo/client_controlled_shell_surface.cc
@@ -770,13 +770,7 @@
                             GetWindowState()->is_dragged() &&
                             !is_display_move_pending;
 
-  // Android PIP windows can be dismissed by swiping off the screen. Let them be
-  // positioned off-screen. Apart from swipe to dismiss, the PIP window will be
-  // kept on screen.
-  // TODO(edcourtney): This should be done as a client controlled move, not a
-  // special case.
-  if (set_bounds_locally || client_controlled_state_->set_bounds_locally() ||
-      GetWindowState()->IsPip()) {
+  if (set_bounds_locally || client_controlled_state_->set_bounds_locally()) {
     // Convert from screen to display coordinates.
     gfx::Point origin = bounds.origin();
     wm::ConvertPointFromScreen(window->parent(), &origin);
@@ -881,8 +875,15 @@
   }
 
   ash::wm::WindowState* window_state = GetWindowState();
-  if (window_state->GetStateType() == pending_window_state_)
+  if (window_state->GetStateType() == pending_window_state_) {
+    // Animate PIP window movement unless it is being dragged.
+    if (window_state->IsPip() && !window_state->is_dragged()) {
+      client_controlled_state_->set_next_bounds_change_animation_type(
+          ash::wm::ClientControlledState::kAnimationAnimated);
+    }
+
     return true;
+  }
 
   if (IsPinned(window_state)) {
     VLOG(1) << "State change was requested while pinned";
@@ -920,8 +921,12 @@
     widget_->widget_delegate()->set_can_activate(true);
   }
 
-  client_controlled_state_->EnterNextState(window_state, pending_window_state_,
-                                           animation_type);
+  if (client_controlled_state_->EnterNextState(window_state,
+                                               pending_window_state_)) {
+    client_controlled_state_->set_next_bounds_change_animation_type(
+        animation_type);
+  }
+
   return true;
 }
 
diff --git a/components/exo/client_controlled_shell_surface_unittest.cc b/components/exo/client_controlled_shell_surface_unittest.cc
index 0c32c268..a627f0a 100644
--- a/components/exo/client_controlled_shell_surface_unittest.cc
+++ b/components/exo/client_controlled_shell_surface_unittest.cc
@@ -47,6 +47,8 @@
 #include "ui/aura/window_event_dispatcher.h"
 #include "ui/aura/window_targeter.h"
 #include "ui/aura/window_tree_host.h"
+#include "ui/compositor/scoped_animation_duration_scale_mode.h"
+#include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/compositor_extra/shadow.h"
 #include "ui/display/display.h"
 #include "ui/display/test/display_manager_test_api.h"
@@ -1826,31 +1828,6 @@
   EXPECT_TRUE(shell_surface->GetWidget()->CanActivate());
 }
 
-TEST_F(ClientControlledShellSurfaceTest, MovingPipWindowOffDisplayIsAllowed) {
-  UpdateDisplay("500x500");
-
-  gfx::Size buffer_size(256, 256);
-  std::unique_ptr<Buffer> buffer(
-      new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size)));
-  std::unique_ptr<Surface> surface(new Surface);
-  auto shell_surface(
-      exo_test_helper()->CreateClientControlledShellSurface(surface.get()));
-  surface->Attach(buffer.get());
-  surface->Commit();
-
-  // Use the PIP window state type.
-  shell_surface->SetPip();
-  shell_surface->GetWidget()->Show();
-
-  // Set an off-screen geometry.
-  gfx::Rect bounds(-200, 0, 100, 100);
-  shell_surface->SetGeometry(bounds);
-  surface->Commit();
-
-  EXPECT_EQ(gfx::Rect(-200, 0, 100, 100),
-            shell_surface->GetWidget()->GetWindowBoundsInScreen());
-}
-
 TEST_F(ClientControlledShellSurfaceDisplayTest,
        NoBoundsChangeEventInMinimized) {
   gfx::Size buffer_size(100, 100);
@@ -1885,4 +1862,51 @@
   ASSERT_EQ(1, bounds_change_count());
 }
 
+TEST_F(ClientControlledShellSurfaceTest, SetPipWindowBoundsAnimates) {
+  const gfx::Size buffer_size(256, 256);
+  std::unique_ptr<Buffer> buffer(
+      new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size)));
+  std::unique_ptr<Surface> surface(new Surface());
+  auto shell_surface =
+      exo_test_helper()->CreateClientControlledShellSurface(surface.get());
+
+  surface->Attach(buffer.get());
+  surface->Commit();
+  shell_surface->SetPip();
+  surface->Commit();
+  shell_surface->GetWidget()->Show();
+
+  ui::ScopedAnimationDurationScaleMode animation_scale_mode(
+      ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
+  aura::Window* window = shell_surface->GetWidget()->GetNativeWindow();
+  window->SetBounds(gfx::Rect(10, 10, 256, 256));
+  EXPECT_EQ(gfx::Rect(10, 10, 256, 256), window->layer()->GetTargetBounds());
+  EXPECT_EQ(gfx::Rect(0, 0, 256, 256), window->layer()->bounds());
+}
+
+TEST_F(ClientControlledShellSurfaceTest, PipWindowDragDoesNotAnimate) {
+  const gfx::Size buffer_size(256, 256);
+  std::unique_ptr<Buffer> buffer(
+      new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size)));
+  std::unique_ptr<Surface> surface(new Surface());
+  auto shell_surface =
+      exo_test_helper()->CreateClientControlledShellSurface(surface.get());
+
+  surface->Attach(buffer.get());
+  surface->Commit();
+  shell_surface->SetPip();
+  surface->Commit();
+  shell_surface->GetWidget()->Show();
+
+  aura::Window* window = shell_surface->GetWidget()->GetNativeWindow();
+  ui::ScopedAnimationDurationScaleMode animation_scale_mode(
+      ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
+  std::unique_ptr<ash::WindowResizer> resizer(ash::CreateWindowResizer(
+      window, gfx::Point(), HTCAPTION, ::wm::WINDOW_MOVE_SOURCE_MOUSE));
+  resizer->Drag(gfx::Point(10, 0), 0);
+  EXPECT_EQ(gfx::Rect(10, 0, 256, 256), window->layer()->GetTargetBounds());
+  EXPECT_EQ(gfx::Rect(10, 0, 256, 256), window->layer()->bounds());
+  resizer->CompleteDrag();
+}
+
 }  // namespace exo
diff --git a/components/feed/core/feed_logging_metrics.cc b/components/feed/core/feed_logging_metrics.cc
index cd1bb4c..10ec926 100644
--- a/components/feed/core/feed_logging_metrics.cc
+++ b/components/feed/core/feed_logging_metrics.cc
@@ -53,9 +53,6 @@
   base::UmaHistogramExactLinear(histogram_name, bucket, kNumBuckets);
   UMA_HISTOGRAM_EXACT_LINEAR(kHistogramArticlesUsageTimeLocal, bucket,
                              kNumBuckets);
-
-  base::RecordAction(
-      base::UserMetricsAction("NewTabPage_ContentSuggestions_ArticlesUsage"));
 }
 
 int ToUMAScore(float score) {
@@ -114,8 +111,7 @@
 
 void FeedLoggingMetrics::OnSuggestionOpened(int position,
                                             base::Time publish_date,
-                                            float score,
-                                            WindowOpenDisposition disposition) {
+                                            float score) {
   UMA_HISTOGRAM_EXACT_LINEAR("NewTabPage.ContentSuggestions.Opened", position,
                              kMaxSuggestionsTotal);
 
@@ -128,16 +124,17 @@
       "NewTabPage.ContentSuggestions.OpenedScoreNormalized.Articles",
       ToUMAScore(score), 11);
 
+  RecordContentSuggestionsUsage();
+}
+
+void FeedLoggingMetrics::OnSuggestionWindowOpened(
+    WindowOpenDisposition disposition) {
   // We use WindowOpenDisposition::MAX_VALUE + 1 for |value_max| since MAX_VALUE
   // itself is a valid (and used) enum value.
   UMA_HISTOGRAM_EXACT_LINEAR(
       "NewTabPage.ContentSuggestions.OpenDisposition.Articles",
       static_cast<int>(disposition),
       static_cast<int>(WindowOpenDisposition::MAX_VALUE) + 1);
-
-  RecordContentSuggestionsUsage();
-
-  base::RecordAction(base::UserMetricsAction("Suggestions.Content.Opened"));
 }
 
 void FeedLoggingMetrics::OnSuggestionMenuOpened(int position,
@@ -168,6 +165,12 @@
       "NewTabPage.ContentSuggestions.VisitDuration.Articles", visit_time);
 }
 
+void FeedLoggingMetrics::OnSuggestionOfflinePageVisited(
+    base::TimeDelta visit_time) {
+  base::UmaHistogramLongTimes(
+      "NewTabPage.ContentSuggestions.VisitDuration.Downloads", visit_time);
+}
+
 void FeedLoggingMetrics::OnMoreButtonShown(int position) {
   // The "more" card can appear in addition to the actual suggestions, so add
   // one extra bucket to this histogram.
diff --git a/components/feed/core/feed_logging_metrics.h b/components/feed/core/feed_logging_metrics.h
index b714aed2..0033b55 100644
--- a/components/feed/core/feed_logging_metrics.h
+++ b/components/feed/core/feed_logging_metrics.h
@@ -5,6 +5,9 @@
 #ifndef COMPONENTS_FEED_CORE_FEED_LOGGING_METRICS_H_
 #define COMPONENTS_FEED_CORE_FEED_LOGGING_METRICS_H_
 
+#include <memory>
+#include <utility>
+
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
@@ -43,10 +46,9 @@
                          float score,
                          base::Time fetch_date);
 
-  void OnSuggestionOpened(int position,
-                          base::Time publish_date,
-                          float score,
-                          WindowOpenDisposition disposition);
+  void OnSuggestionOpened(int position, base::Time publish_date, float score);
+
+  void OnSuggestionWindowOpened(WindowOpenDisposition disposition);
 
   void OnSuggestionMenuOpened(int position,
                               base::Time publish_date,
@@ -56,6 +58,8 @@
 
   void OnSuggestionArticleVisited(base::TimeDelta visit_time);
 
+  void OnSuggestionOfflinePageVisited(base::TimeDelta visit_time);
+
   // Should only be called once per NTP for each "more" button.
   void OnMoreButtonShown(int position);
 
diff --git a/components/feed/core/feed_logging_metrics_unittest.cc b/components/feed/core/feed_logging_metrics_unittest.cc
index 2b37250..cf8c7f6 100644
--- a/components/feed/core/feed_logging_metrics_unittest.cc
+++ b/components/feed/core/feed_logging_metrics_unittest.cc
@@ -96,20 +96,20 @@
               ElementsAre(base::Bucket(/*min=*/10, /*count=*/1)));
 }
 
-TEST_F(FeedLoggingMetricsTest, ShouldLogPrefetchedSuggestionsWhenOpened) {
+TEST_F(FeedLoggingMetricsTest, ShouldLogOnSuggestionOpened) {
   base::HistogramTester histogram_tester;
   feed_logging_metrics()->OnSuggestionOpened(
       /*position=*/11, base::Time::Now(),
-      /*score=*/1.0f, WindowOpenDisposition::CURRENT_TAB);
+      /*score=*/1.0f);
   feed_logging_metrics()->OnSuggestionOpened(
       /*position=*/13, base::Time::Now(),
-      /*score=*/1.0f, WindowOpenDisposition::CURRENT_TAB);
+      /*score=*/1.0f);
   feed_logging_metrics()->OnSuggestionOpened(
       /*position=*/15, base::Time::Now(),
-      /*score=*/1.0f, WindowOpenDisposition::CURRENT_TAB);
+      /*score=*/1.0f);
   feed_logging_metrics()->OnSuggestionOpened(
       /*position=*/23, base::Time::Now(),
-      /*score=*/1.0f, WindowOpenDisposition::CURRENT_TAB);
+      /*score=*/1.0f);
 
   EXPECT_THAT(
       histogram_tester.GetAllSamples("NewTabPage.ContentSuggestions.Opened"),
@@ -119,6 +119,25 @@
                   base::Bucket(/*min=*/23, /*count=*/1)));
 }
 
+TEST_F(FeedLoggingMetricsTest, ShouldLogOnSuggestionWindowOpened) {
+  base::HistogramTester histogram_tester;
+  feed_logging_metrics()->OnSuggestionWindowOpened(
+      WindowOpenDisposition::CURRENT_TAB);
+  feed_logging_metrics()->OnSuggestionWindowOpened(
+      WindowOpenDisposition::CURRENT_TAB);
+  feed_logging_metrics()->OnSuggestionWindowOpened(
+      WindowOpenDisposition::CURRENT_TAB);
+  feed_logging_metrics()->OnSuggestionWindowOpened(
+      WindowOpenDisposition::CURRENT_TAB);
+
+  EXPECT_THAT(histogram_tester.GetAllSamples(
+                  "NewTabPage.ContentSuggestions.OpenDisposition.Articles"),
+              ElementsAre(base::Bucket(
+                  /*WindowOpenDisposition::CURRENT_TAB=*/static_cast<int>(
+                      WindowOpenDisposition::CURRENT_TAB),
+                  /*count=*/4)));
+}
+
 TEST_F(FeedLoggingMetricsTest, ShouldLogOnSuggestionDismissedIfVisited) {
   base::HistogramTester histogram_tester;
   feed_logging_metrics()->OnSuggestionDismissed(/*position=*/10, VISITED_URL);
diff --git a/components/invalidation/impl/BUILD.gn b/components/invalidation/impl/BUILD.gn
index 5da2b03..32b90362 100644
--- a/components/invalidation/impl/BUILD.gn
+++ b/components/invalidation/impl/BUILD.gn
@@ -25,8 +25,6 @@
     "invalidator.h",
     "invalidator_registrar.cc",
     "invalidator_registrar.h",
-    "invalidator_registrar_with_memory.cc",
-    "invalidator_registrar_with_memory.h",
     "invalidator_storage.cc",
     "invalidator_storage.h",
     "mock_ack_handler.cc",
diff --git a/components/invalidation/impl/fcm_fake_invalidator.cc b/components/invalidation/impl/fcm_fake_invalidator.cc
index 63d6fc7..6e99a0b 100644
--- a/components/invalidation/impl/fcm_fake_invalidator.cc
+++ b/components/invalidation/impl/fcm_fake_invalidator.cc
@@ -16,7 +16,7 @@
 
 bool FCMFakeInvalidator::IsHandlerRegistered(
     InvalidationHandler* handler) const {
-  return registrar_.IsHandlerRegistered(handler);
+  return registrar_.IsHandlerRegisteredForTest(handler);
 }
 
 ObjectIdSet FCMFakeInvalidator::GetRegisteredIds(
diff --git a/components/invalidation/impl/fcm_invalidation_listener.cc b/components/invalidation/impl/fcm_invalidation_listener.cc
index a36b663..dc466a01 100644
--- a/components/invalidation/impl/fcm_invalidation_listener.cc
+++ b/components/invalidation/impl/fcm_invalidation_listener.cc
@@ -52,9 +52,11 @@
 }
 
 void FCMInvalidationListener::UpdateRegisteredTopics(const TopicSet& topics) {
-  ids_update_requested_ = true;
   registered_topics_ = topics;
-  DoRegistrationUpdate();
+  if (ticl_state_ == INVALIDATIONS_ENABLED &&
+      per_user_topic_registration_manager_ && !token_.empty()) {
+    DoRegistrationUpdate();
+  }
 }
 
 void FCMInvalidationListener::Ready(InvalidationClient* client) {
@@ -139,11 +141,6 @@
 }
 
 void FCMInvalidationListener::DoRegistrationUpdate() {
-  if (ticl_state_ != INVALIDATIONS_ENABLED ||
-      !per_user_topic_registration_manager_ || token_.empty() ||
-      !ids_update_requested_) {
-    return;
-  }
   per_user_topic_registration_manager_->UpdateRegisteredTopics(
       registered_topics_, token_);
 
diff --git a/components/invalidation/impl/fcm_invalidation_listener.h b/components/invalidation/impl/fcm_invalidation_listener.h
index 1ae08b4..e8febad0 100644
--- a/components/invalidation/impl/fcm_invalidation_listener.h
+++ b/components/invalidation/impl/fcm_invalidation_listener.h
@@ -129,18 +129,13 @@
   // Stored to pass to |per_user_topic_registration_manager_| on start.
   TopicSet registered_topics_;
 
-  // The states of the ticl and FCM channel.
+  // The states of the ticl and FCN channel.
   InvalidatorState ticl_state_;
   InvalidatorState fcm_network_state_;
 
   std::unique_ptr<PerUserTopicRegistrationManager>
       per_user_topic_registration_manager_;
   std::string token_;
-  // Prevents call to DoRegistrationUpdate in cases when
-  // UpdateRegisteredTopics wasn't called. For example, InformTokenRecieved
-  // can trigger DoRegistrationUpdate before any invalidation handler has
-  // requested registration for topics.
-  bool ids_update_requested_ = false;
 
   base::WeakPtrFactory<FCMInvalidationListener> weak_factory_;
 
diff --git a/components/invalidation/impl/fcm_invalidation_service.cc b/components/invalidation/impl/fcm_invalidation_service.cc
index 78236363..cbf8435e 100644
--- a/components/invalidation/impl/fcm_invalidation_service.cc
+++ b/components/invalidation/impl/fcm_invalidation_service.cc
@@ -29,14 +29,12 @@
     PrefService* pref_service,
     const syncer::ParseJSONCallback& parse_json,
     network::mojom::URLLoaderFactory* loader_factory)
-    : invalidator_registrar_(pref_service),
-      gcm_driver_(gcm_driver),
+    : gcm_driver_(gcm_driver),
       instance_id_driver_(instance_id_driver),
       identity_provider_(identity_provider),
       pref_service_(pref_service),
       parse_json_(parse_json),
-      loader_factory_(loader_factory),
-      update_was_requested_(false) {}
+      loader_factory_(loader_factory) {}
 
 FCMInvalidationService::~FCMInvalidationService() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -63,7 +61,8 @@
   invalidator_.reset(invalidator);
 
   invalidator_->RegisterHandler(this);
-  DoUpdateRegisteredIdsIfNeeded();
+  CHECK(invalidator_->UpdateRegisteredIds(
+      this, invalidator_registrar_.GetAllRegisteredIds()));
 }
 
 void FCMInvalidationService::RegisterInvalidationHandler(
@@ -78,12 +77,14 @@
     syncer::InvalidationHandler* handler,
     const syncer::ObjectIdSet& ids) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  update_was_requested_ = true;
   DVLOG(2) << "Registering ids: " << ids.size();
   syncer::TopicSet topics = ConvertIdsToTopics(ids);
   if (!invalidator_registrar_.UpdateRegisteredTopics(handler, topics))
     return false;
-  DoUpdateRegisteredIdsIfNeeded();
+  if (invalidator_) {
+    CHECK(invalidator_->UpdateRegisteredIds(
+        this, invalidator_registrar_.GetAllRegisteredIds()));
+  }
   logger_.OnUpdateTopics(invalidator_registrar_.GetSanitizedHandlersIdsMap());
   return true;
 }
@@ -93,6 +94,10 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DVLOG(2) << "Unregistering";
   invalidator_registrar_.UnregisterHandler(handler);
+  if (invalidator_) {
+    CHECK(invalidator_->UpdateRegisteredIds(
+        this, invalidator_registrar_.GetAllRegisteredIds()));
+  }
   logger_.OnUnregistration(handler->GetOwnerName());
 }
 
@@ -185,8 +190,10 @@
   invalidator_ = std::make_unique<syncer::FCMInvalidator>(
       std::move(network), identity_provider_, pref_service_, loader_factory_,
       parse_json_);
+
   invalidator_->RegisterHandler(this);
-  DoUpdateRegisteredIdsIfNeeded();
+  CHECK(invalidator_->UpdateRegisteredIds(
+      this, invalidator_registrar_.GetAllRegisteredIds()));
 }
 
 void FCMInvalidationService::StopInvalidator() {
@@ -226,12 +233,4 @@
   // TODO(meandory): report metric in case of unsucesfull deletion.
 }
 
-void FCMInvalidationService::DoUpdateRegisteredIdsIfNeeded() {
-  if (!invalidator_ || !update_was_requested_)
-    return;
-  auto registered_ids = invalidator_registrar_.GetAllRegisteredIds();
-  CHECK(invalidator_->UpdateRegisteredIds(this, registered_ids));
-  update_was_requested_ = false;
-}
-
 }  // namespace invalidation
diff --git a/components/invalidation/impl/fcm_invalidation_service.h b/components/invalidation/impl/fcm_invalidation_service.h
index 65b1314..67dcb15 100644
--- a/components/invalidation/impl/fcm_invalidation_service.h
+++ b/components/invalidation/impl/fcm_invalidation_service.h
@@ -9,7 +9,7 @@
 #include "base/timer/timer.h"
 #include "components/gcm_driver/instance_id/instance_id.h"
 #include "components/invalidation/impl/invalidation_logger.h"
-#include "components/invalidation/impl/invalidator_registrar_with_memory.h"
+#include "components/invalidation/impl/invalidator_registrar.h"
 #include "components/invalidation/public/identity_provider.h"
 #include "components/invalidation/public/invalidation_handler.h"
 #include "components/invalidation/public/invalidation_service.h"
@@ -93,9 +93,7 @@
   void OnInstanceIdRecieved(const std::string& id);
   void OnDeleteIDCompleted(instance_id::InstanceID::Result);
 
-  void DoUpdateRegisteredIdsIfNeeded();
-
-  syncer::InvalidatorRegistrarWithMemory invalidator_registrar_;
+  syncer::InvalidatorRegistrar invalidator_registrar_;
   std::unique_ptr<syncer::Invalidator> invalidator_;
 
   // The invalidation logger object we use to record state changes
@@ -110,7 +108,6 @@
   PrefService* pref_service_;
   syncer::ParseJSONCallback parse_json_;
   network::mojom::URLLoaderFactory* loader_factory_;
-  bool update_was_requested_ = false;
 
   SEQUENCE_CHECKER(sequence_checker_);
 
diff --git a/components/invalidation/impl/fcm_invalidation_service_unittest.cc b/components/invalidation/impl/fcm_invalidation_service_unittest.cc
index edcf1abd..6cb50c9 100644
--- a/components/invalidation/impl/fcm_invalidation_service_unittest.cc
+++ b/components/invalidation/impl/fcm_invalidation_service_unittest.cc
@@ -90,8 +90,6 @@
     pref_service_.registry()->RegisterStringPref(
         prefs::kFCMInvalidationClientIDCache,
         /*default_value=*/std::string());
-    syncer::InvalidatorRegistrarWithMemory::RegisterProfilePrefs(
-        pref_service_.registry());
   }
 
   ~FCMInvalidationServiceTestDelegate() {}
diff --git a/components/invalidation/impl/invalidator_registrar.cc b/components/invalidation/impl/invalidator_registrar.cc
index 502c7a60..45bc920 100644
--- a/components/invalidation/impl/invalidator_registrar.cc
+++ b/components/invalidation/impl/invalidator_registrar.cc
@@ -134,7 +134,7 @@
   return clean_handlers_to_topics;
 }
 
-bool InvalidatorRegistrar::IsHandlerRegistered(
+bool InvalidatorRegistrar::IsHandlerRegisteredForTest(
     const InvalidationHandler* handler) const {
   DCHECK(thread_checker_.CalledOnValidThread());
   return handlers_.HasObserver(handler);
diff --git a/components/invalidation/impl/invalidator_registrar.h b/components/invalidation/impl/invalidator_registrar.h
index 5e816f26..d1f28b7 100644
--- a/components/invalidation/impl/invalidator_registrar.h
+++ b/components/invalidation/impl/invalidator_registrar.h
@@ -33,24 +33,23 @@
   // and it must already be registered.
   void RegisterHandler(InvalidationHandler* handler);
 
+  // Updates the set of topics associated with |handler|.  |handler| must
+  // not be NULL, and must already be registered.  A topic must be registered
+  // for at most one handler. If topic is already registered function returns
+  // false.
+  bool UpdateRegisteredTopics(InvalidationHandler* handler,
+                              const TopicSet& topics) WARN_UNUSED_RESULT;
+
   // Stops sending notifications to |handler|.  |handler| must not be NULL, and
   // it must already be registered.  Note that this doesn't unregister the IDs
   // associated with |handler|.
   void UnregisterHandler(InvalidationHandler* handler);
 
-  // Updates the set of topics associated with |handler|.  |handler| must
-  // not be NULL, and must already be registered.  A topic must be registered
-  // for at most one handler. If topic is already registered function returns
-  // false.
-  virtual bool UpdateRegisteredTopics(InvalidationHandler* handler,
-                                      const TopicSet& topics)
-      WARN_UNUSED_RESULT;
-
-  virtual TopicSet GetRegisteredTopics(InvalidationHandler* handler) const;
+  TopicSet GetRegisteredTopics(InvalidationHandler* handler) const;
 
   // Returns the set of all IDs that are registered to some handler (even
   // handlers that have been unregistered).
-  virtual TopicSet GetAllRegisteredIds() const;
+  TopicSet GetAllRegisteredIds() const;
 
   // Sorts incoming invalidations into a bucket for each handler and then
   // dispatches the batched invalidations to the corresponding handler.
@@ -74,7 +73,7 @@
   // to display every registered handlers and its objectsIds.
   std::map<std::string, TopicSet> GetSanitizedHandlersIdsMap();
 
-  bool IsHandlerRegistered(const InvalidationHandler* handler) const;
+  bool IsHandlerRegisteredForTest(const InvalidationHandler* handler) const;
 
   // Needed for death tests.
   void DetachFromThreadForTest();
diff --git a/components/invalidation/impl/invalidator_registrar_with_memory.cc b/components/invalidation/impl/invalidator_registrar_with_memory.cc
deleted file mode 100644
index 3bd24df5..0000000
--- a/components/invalidation/impl/invalidator_registrar_with_memory.cc
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/invalidation/impl/invalidator_registrar_with_memory.h"
-
-#include <cstddef>
-#include <iterator>
-#include <utility>
-
-#include "base/logging.h"
-#include "components/invalidation/public/object_id_invalidation_map.h"
-#include "components/invalidation/public/topic_invalidation_map.h"
-#include "components/prefs/pref_registry_simple.h"
-#include "components/prefs/scoped_user_pref_update.h"
-
-namespace syncer {
-
-namespace {
-
-const char kTopicsToHandler[] = "invalidation.topics_to_handler";
-
-}  // namespace
-
-// static
-void InvalidatorRegistrarWithMemory::RegisterProfilePrefs(
-    PrefRegistrySimple* registry) {
-  registry->RegisterDictionaryPref(kTopicsToHandler);
-}
-
-InvalidatorRegistrarWithMemory::InvalidatorRegistrarWithMemory(
-    PrefService* local_state)
-    : InvalidatorRegistrar(), local_state_(local_state) {
-  const base::Value* pref_data = local_state_->Get(kTopicsToHandler);
-  for (const auto& it : pref_data->DictItems()) {
-    Topic topic = it.first;
-    std::string handler_name;
-    it.second.GetAsString(&handler_name);
-    handler_name_to_topics_map_[handler_name].insert(topic);
-  }
-}
-
-InvalidatorRegistrarWithMemory::~InvalidatorRegistrarWithMemory() {}
-
-bool InvalidatorRegistrarWithMemory::UpdateRegisteredTopics(
-    InvalidationHandler* handler,
-    const TopicSet& topics) {
-  TopicSet old_topics = GetRegisteredTopics(handler);
-  bool success = InvalidatorRegistrar::UpdateRegisteredTopics(handler, topics);
-  if (!InvalidatorRegistrar::IsHandlerRegistered(handler)) {
-    return success;
-  }
-
-  TopicSet to_unregister;
-  DictionaryPrefUpdate update(local_state_, kTopicsToHandler);
-  std::set_difference(old_topics.begin(), old_topics.end(), topics.begin(),
-                      topics.end(),
-                      std::inserter(to_unregister, to_unregister.begin()));
-  if (!to_unregister.empty()) {
-    for (const auto& topic : to_unregister) {
-      update->RemoveKey(topic);
-      handler_name_to_topics_map_[handler->GetOwnerName()].erase(topic);
-    }
-  }
-
-  for (const auto& topic : topics) {
-    handler_name_to_topics_map_[handler->GetOwnerName()].insert(topic);
-    update->SetKey(topic, base::Value(handler->GetOwnerName()));
-  }
-  return success;
-}
-
-TopicSet InvalidatorRegistrarWithMemory::GetAllRegisteredIds() const {
-  TopicSet registered_topics;
-  for (const auto& handler_to_topic : handler_name_to_topics_map_) {
-    registered_topics.insert(handler_to_topic.second.begin(),
-                             handler_to_topic.second.end());
-  }
-  return registered_topics;
-}
-
-}  // namespace syncer
diff --git a/components/invalidation/impl/invalidator_registrar_with_memory.h b/components/invalidation/impl/invalidator_registrar_with_memory.h
deleted file mode 100644
index a00e421f..0000000
--- a/components/invalidation/impl/invalidator_registrar_with_memory.h
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_INVALIDATION_IMPL_INVALIDATOR_REGISTRAR_WITH_MEMORY_H_
-#define COMPONENTS_INVALIDATION_IMPL_INVALIDATOR_REGISTRAR_WITH_MEMORY_H_
-
-#include <map>
-
-#include "base/macros.h"
-#include "base/observer_list.h"
-#include "base/threading/thread_checker.h"
-#include "components/invalidation/impl/invalidator_registrar.h"
-#include "components/invalidation/public/invalidation_export.h"
-#include "components/invalidation/public/invalidation_handler.h"
-#include "components/invalidation/public/invalidation_util.h"
-#include "components/prefs/pref_registry_simple.h"
-#include "components/prefs/pref_service.h"
-
-class PrefRegistrySimple;
-class PrefService;
-
-namespace syncer {
-
-using HandlerNameTopicsMap = std::map<std::string, TopicSet>;
-
-// A helper class for implementations of the Invalidator interface.  It helps
-// keep track of registered handlers and which object ID registrations are
-// associated with which handlers, so implementors can just reuse the logic
-// here to dispatch invalidations and other interesting notifications.
-class INVALIDATION_EXPORT InvalidatorRegistrarWithMemory
-    : public InvalidatorRegistrar {
- public:
-  InvalidatorRegistrarWithMemory(PrefService* local_state);
-
-  // It is an error to have registered handlers on destruction.
-  ~InvalidatorRegistrarWithMemory();
-
-  static void RegisterProfilePrefs(PrefRegistrySimple* registry);
-
-  // Updates the set of topics associated with |handler|.  |handler| must
-  // not be NULL, and must already be registered.  A topic must be registered
-  // for at most one handler. If topic is already registered function returns
-  // false.
-  bool UpdateRegisteredTopics(InvalidationHandler* handler,
-                              const TopicSet& topics) override
-      WARN_UNUSED_RESULT;
-
-  // void UnregisterHandler(InvalidationHandler* handler) override;
-  // void RegisterHandler(InvalidationHandler* handler) override;
-
-  // Returns the set of all IDs that are registered to some handler (even
-  // handlers that have been unregistered).
-  TopicSet GetAllRegisteredIds() const override;
-
- private:
-  std::unordered_map<std::string, InvalidationHandler*>
-      handler_name_to_handler_;
-  HandlerNameTopicsMap handler_name_to_topics_map_;
-  PrefService* local_state_;
-
-  DISALLOW_COPY_AND_ASSIGN(InvalidatorRegistrarWithMemory);
-};
-
-}  // namespace syncer
-
-#endif  // COMPONENTS_INVALIDATION_IMPL_INVALIDATOR_REGISTRAR_WITH_MEMORY_H_
diff --git a/components/metrics/data_use_tracker.cc b/components/metrics/data_use_tracker.cc
index 61be02f..0cb37b0 100644
--- a/components/metrics/data_use_tracker.cc
+++ b/components/metrics/data_use_tracker.cc
@@ -45,9 +45,9 @@
   registry->RegisterDictionaryPref(metrics::prefs::kUmaCellDataUse);
 }
 
-void DataUseTracker::UpdateMetricsUsagePrefs(const std::string& service_name,
-                                             int message_size,
-                                             bool is_cellular) {
+void DataUseTracker::UpdateMetricsUsagePrefs(int message_size,
+                                             bool is_cellular,
+                                             bool is_metrics_service_usage) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   if (!is_cellular)
@@ -55,7 +55,7 @@
 
   UpdateUsagePref(prefs::kUserCellDataUse, message_size);
   // TODO(holte): Consider adding seperate tracking for UKM.
-  if (service_name == "UMA" || service_name == "UKM")
+  if (is_metrics_service_usage)
     UpdateUsagePref(prefs::kUmaCellDataUse, message_size);
 }
 
diff --git a/components/metrics/data_use_tracker.h b/components/metrics/data_use_tracker.h
index 104223e..457193e 100644
--- a/components/metrics/data_use_tracker.h
+++ b/components/metrics/data_use_tracker.h
@@ -36,9 +36,9 @@
   static void RegisterPrefs(PrefRegistrySimple* registry);
 
   // Updates data usage tracking prefs with the specified values.
-  void UpdateMetricsUsagePrefs(const std::string& service_name,
-                               int message_size,
-                               bool is_cellular);
+  void UpdateMetricsUsagePrefs(int message_size,
+                               bool is_cellular,
+                               bool is_metrics_service_usage);
 
   // Returns whether a log with provided |log_bytes| can be uploaded according
   // to data use ratio and UMA quota provided by variations.
diff --git a/components/metrics/data_use_tracker_unittest.cc b/components/metrics/data_use_tracker_unittest.cc
index a271b5d..b571dc4f2 100644
--- a/components/metrics/data_use_tracker_unittest.cc
+++ b/components/metrics/data_use_tracker_unittest.cc
@@ -106,7 +106,7 @@
   int user_pref_value = 0;
   int uma_pref_value = 0;
 
-  data_use_tracker.UpdateMetricsUsagePrefs("", 2 * 100, true);
+  data_use_tracker.UpdateMetricsUsagePrefs(2 * 100, true, false);
   local_state.GetDictionary(prefs::kUserCellDataUse)
       ->GetInteger(kTodayStr, &user_pref_value);
   EXPECT_EQ(2 * 100, user_pref_value);
@@ -114,7 +114,7 @@
       ->GetInteger(kTodayStr, &uma_pref_value);
   EXPECT_EQ(0, uma_pref_value);
 
-  data_use_tracker.UpdateMetricsUsagePrefs("UMA", 100, true);
+  data_use_tracker.UpdateMetricsUsagePrefs(100, true, true);
   local_state.GetDictionary(prefs::kUserCellDataUse)
       ->GetInteger(kTodayStr, &user_pref_value);
   EXPECT_EQ(3 * 100, user_pref_value);
diff --git a/components/metrics/metrics_service.cc b/components/metrics/metrics_service.cc
index bfa17fe..01ed8be1e 100644
--- a/components/metrics/metrics_service.cc
+++ b/components/metrics/metrics_service.cc
@@ -452,12 +452,12 @@
   log_store()->StoreLog(log, MetricsLog::ONGOING_LOG);
 }
 
-void MetricsService::UpdateMetricsUsagePrefs(const std::string& service_name,
-                                             int message_size,
-                                             bool is_cellular) {
+void MetricsService::UpdateMetricsUsagePrefs(int message_size,
+                                             bool is_cellular,
+                                             bool is_metrics_service_usage) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  reporting_service_.UpdateMetricsUsagePrefs(service_name, message_size,
-                                             is_cellular);
+  reporting_service_.UpdateMetricsUsagePrefs(message_size, is_cellular,
+                                             is_metrics_service_usage);
 }
 
 //------------------------------------------------------------------------------
diff --git a/components/metrics/metrics_service.h b/components/metrics/metrics_service.h
index 6471314..8a26561b 100644
--- a/components/metrics/metrics_service.h
+++ b/components/metrics/metrics_service.h
@@ -171,9 +171,9 @@
   void PushExternalLog(const std::string& log);
 
   // Updates data usage tracking prefs with the specified values.
-  void UpdateMetricsUsagePrefs(const std::string& service_name,
-                               int message_size,
-                               bool is_cellular);
+  void UpdateMetricsUsagePrefs(int message_size,
+                               bool is_cellular,
+                               bool is_metrics_service_usage);
 
   variations::SyntheticTrialRegistry* synthetic_trial_registry() {
     return &synthetic_trial_registry_;
diff --git a/components/metrics/reporting_service.cc b/components/metrics/reporting_service.cc
index 6382804..9e18f4d 100644
--- a/components/metrics/reporting_service.cc
+++ b/components/metrics/reporting_service.cc
@@ -81,13 +81,13 @@
   return reporting_active_;
 }
 
-void ReportingService::UpdateMetricsUsagePrefs(const std::string& service_name,
-                                               int message_size,
-                                               bool is_cellular) {
+void ReportingService::UpdateMetricsUsagePrefs(int message_size,
+                                               bool is_cellular,
+                                               bool is_metrics_service_usage) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (data_use_tracker_) {
-    data_use_tracker_->UpdateMetricsUsagePrefs(service_name, message_size,
-                                               is_cellular);
+    data_use_tracker_->UpdateMetricsUsagePrefs(message_size, is_cellular,
+                                               is_metrics_service_usage);
   }
 }
 
diff --git a/components/metrics/reporting_service.h b/components/metrics/reporting_service.h
index d724430..a913d4e5 100644
--- a/components/metrics/reporting_service.h
+++ b/components/metrics/reporting_service.h
@@ -67,9 +67,9 @@
   bool reporting_active() const;
 
   // Updates data usage tracking prefs with the specified values.
-  void UpdateMetricsUsagePrefs(const std::string& service_name,
-                               int message_size,
-                               bool is_cellular);
+  void UpdateMetricsUsagePrefs(int message_size,
+                               bool is_cellular,
+                               bool is_metrics_service_usage);
 
   // Registers local state prefs used by this class. This should only be called
   // once.
diff --git a/components/password_manager/ios/BUILD.gn b/components/password_manager/ios/BUILD.gn
index c10662a..2254462 100644
--- a/components/password_manager/ios/BUILD.gn
+++ b/components/password_manager/ios/BUILD.gn
@@ -23,8 +23,10 @@
     "account_select_fill_data.h",
     "js_password_manager.h",
     "js_password_manager.mm",
-    "password_controller_helper.h",
-    "password_controller_helper.mm",
+    "password_form_helper.h",
+    "password_form_helper.mm",
+    "password_suggestion_helper.h",
+    "password_suggestion_helper.mm",
   ]
 }
 
@@ -48,7 +50,7 @@
   testonly = true
   sources = [
     "account_select_fill_data_unittest.cc",
-    "password_controller_helper_unittest.mm",
+    "password_form_helper_unittest.mm",
   ]
   deps = [
     ":ios",
diff --git a/components/password_manager/ios/password_controller_helper.h b/components/password_manager/ios/password_form_helper.h
similarity index 78%
rename from components/password_manager/ios/password_controller_helper.h
rename to components/password_manager/ios/password_form_helper.h
index 781feb7f..6e18d56 100644
--- a/components/password_manager/ios/password_controller_helper.h
+++ b/components/password_manager/ios/password_form_helper.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_PASSWORD_MANAGER_IOS_PASSWORD_CONTROLLER_HELPER_H_
-#define COMPONENTS_PASSWORD_MANAGER_IOS_PASSWORD_CONTROLLER_HELPER_H_
+#ifndef COMPONENTS_PASSWORD_MANAGER_IOS_PASSWORD_FORM_HELPER_H_
+#define COMPONENTS_PASSWORD_MANAGER_IOS_PASSWORD_FORM_HELPER_H_
 
 #import <Foundation/Foundation.h>
 
@@ -14,7 +14,7 @@
 NS_ASSUME_NONNULL_BEGIN
 
 @class JsPasswordManager;
-@class PasswordControllerHelper;
+@class PasswordFormHelper;
 
 namespace autofill {
 struct PasswordForm;
@@ -33,18 +33,17 @@
 class WebState;
 }  // namespace web
 
-// A protocol implemented by a delegate of PasswordControllerHelper.
-@protocol PasswordControllerHelperDelegate
+// A protocol implemented by a delegate of PasswordFormHelper.
+@protocol PasswordFormHelperDelegate
 // Called when the password form is submitted.
-- (void)helper:(PasswordControllerHelper*)helper
-    didSubmitForm:(const autofill::PasswordForm&)form
-      inMainFrame:(BOOL)inMainFrame;
+- (void)formHelper:(PasswordFormHelper*)formHelper
+     didSubmitForm:(const autofill::PasswordForm&)form
+       inMainFrame:(BOOL)inMainFrame;
 @end
 
-// Handles common logic of password controller for both ios/chrome and
-// ios/web_view.
-// TODO(crbug.com/865114): Rename to PasswordFormHelper.
-@interface PasswordControllerHelper
+// Handles common form processing logic of password controller for both
+// ios/chrome and ios/web_view.
+@interface PasswordFormHelper
     : NSObject<FormActivityObserver, CRWWebStateObserver>
 
 // The JsPasswordManager processing password form via javascript.
@@ -82,8 +81,9 @@
 
 // Creates a instance with the given WebState, observer and delegate.
 - (instancetype)initWithWebState:(web::WebState*)webState
-                        delegate:(nullable id<PasswordControllerHelperDelegate>)
-                                     delegate NS_DESIGNATED_INITIALIZER;
+                        delegate:
+                            (nullable id<PasswordFormHelperDelegate>)delegate
+    NS_DESIGNATED_INITIALIZER;
 
 - (instancetype)init NS_UNAVAILABLE;
 
@@ -91,4 +91,4 @@
 
 NS_ASSUME_NONNULL_END
 
-#endif  // COMPONENTS_PASSWORD_MANAGER_IOS_PASSWORD_CONTROLLER_HELPER_H_
+#endif  // COMPONENTS_PASSWORD_MANAGER_IOS_PASSWORD_FORM_HELPER_H_
diff --git a/components/password_manager/ios/password_controller_helper.mm b/components/password_manager/ios/password_form_helper.mm
similarity index 93%
rename from components/password_manager/ios/password_controller_helper.mm
rename to components/password_manager/ios/password_form_helper.mm
index 691653e7d..1a0f1754 100644
--- a/components/password_manager/ios/password_controller_helper.mm
+++ b/components/password_manager/ios/password_form_helper.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "components/password_manager/ios/password_controller_helper.h"
+#import "components/password_manager/ios/password_form_helper.h"
 
 #include <stddef.h>
 
@@ -48,9 +48,9 @@
 constexpr char kCommandPrefix[] = "passwordForm";
 }  // namespace
 
-@interface PasswordControllerHelper ()
+@interface PasswordFormHelper ()
 
-@property(nonatomic, weak) id<PasswordControllerHelperDelegate> delegate;
+@property(nonatomic, weak) id<PasswordFormHelperDelegate> delegate;
 
 // Handler for injected JavaScript callbacks.
 - (BOOL)handleScriptCommand:(const base::DictionaryValue&)JSONCommand;
@@ -80,14 +80,14 @@
 @end
 
 // Category for test only.
-@interface PasswordControllerHelper (Testing)
+@interface PasswordFormHelper (Testing)
 
 // Replaces JsPasswordManager for test.
 - (void)setJsPasswordManager:(JsPasswordManager*)jsPasswordManager;
 
 @end
 
-@implementation PasswordControllerHelper {
+@implementation PasswordFormHelper {
   // The WebState this instance is observing. Will be null after
   // -webStateDestroyed: has been called.
   web::WebState* _webState;
@@ -112,8 +112,7 @@
 #pragma mark - Initialization
 
 - (instancetype)initWithWebState:(web::WebState*)webState
-                        delegate:
-                            (id<PasswordControllerHelperDelegate>)delegate {
+                        delegate:(id<PasswordFormHelperDelegate>)delegate {
   self = [super init];
   if (self) {
     DCHECK(webState);
@@ -127,7 +126,7 @@
     _jsPasswordManager = [[JsPasswordManager alloc]
         initWithReceiver:_webState->GetJSInjectionReceiver()];
 
-    __weak PasswordControllerHelper* weakSelf = self;
+    __weak PasswordFormHelper* weakSelf = self;
     auto callback = base::BindRepeating(
         ^bool(const base::DictionaryValue& JSON, const GURL& originURL,
               bool interacting, bool isMainFrame, web::WebFrame* senderFrame) {
@@ -181,20 +180,20 @@
     // origin.
     return;
   }
-  __weak PasswordControllerHelper* weakSelf = self;
+  __weak PasswordFormHelper* weakSelf = self;
   // This code is racing against the new page loading and will not get the
   // password form data if the page has changed. In most cases this code wins
   // the race.
   // TODO(crbug.com/418827): Fix this by passing in more data from the JS side.
   id completionHandler = ^(BOOL found, const autofill::PasswordForm& form) {
-    PasswordControllerHelper* strongSelf = weakSelf;
-    id<PasswordControllerHelperDelegate> strongDelegate = strongSelf.delegate;
+    PasswordFormHelper* strongSelf = weakSelf;
+    id<PasswordFormHelperDelegate> strongDelegate = strongSelf.delegate;
     if (!strongSelf || !strongSelf->_webState || !strongDelegate) {
       return;
     }
-    [strongDelegate helper:strongSelf
-             didSubmitForm:form
-               inMainFrame:formInMainFrame];
+    [strongDelegate formHelper:strongSelf
+                 didSubmitForm:form
+                   inMainFrame:formInMainFrame];
   };
   // TODO(crbug.com/418827): Use |formData| instead of extracting form again.
   [self extractSubmittedPasswordForm:formName
@@ -231,7 +230,7 @@
   }
 
   if (_webState && self.delegate) {
-    [self.delegate helper:self didSubmitForm:*form inMainFrame:YES];
+    [self.delegate formHelper:self didSubmitForm:*form inMainFrame:YES];
     return YES;
   }
 
@@ -343,7 +342,7 @@
     return;
   }
 
-  __weak PasswordControllerHelper* weakSelf = self;
+  __weak PasswordFormHelper* weakSelf = self;
   [self.jsPasswordManager findPasswordFormsWithCompletionHandler:^(
                               NSString* jsonString) {
     std::vector<autofill::PasswordForm> forms;
@@ -386,10 +385,10 @@
                                     password:(NSString*)password
                            completionHandler:
                                (nullable void (^)(BOOL))completionHandler {
-  __weak PasswordControllerHelper* weakSelf = self;
+  __weak PasswordFormHelper* weakSelf = self;
   [self findPasswordFormsWithCompletionHandler:^(
             const std::vector<autofill::PasswordForm>& forms) {
-    PasswordControllerHelper* strongSelf = weakSelf;
+    PasswordFormHelper* strongSelf = weakSelf;
     for (const auto& form : forms) {
       autofill::PasswordFormFillData formData;
       std::map<base::string16, const autofill::PasswordForm*> matches;
diff --git a/components/password_manager/ios/password_controller_helper_unittest.mm b/components/password_manager/ios/password_form_helper_unittest.mm
similarity index 95%
rename from components/password_manager/ios/password_controller_helper_unittest.mm
rename to components/password_manager/ios/password_form_helper_unittest.mm
index eaa6d895..cec663f 100644
--- a/components/password_manager/ios/password_controller_helper_unittest.mm
+++ b/components/password_manager/ios/password_form_helper_unittest.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "components/password_manager/ios/password_controller_helper.h"
+#import "components/password_manager/ios/password_form_helper.h"
 
 #include <stddef.h>
 
@@ -16,7 +16,7 @@
 #include "components/password_manager/core/browser/stub_password_manager_driver.h"
 #include "components/password_manager/ios/account_select_fill_data.h"
 #import "components/password_manager/ios/js_password_manager.h"
-#import "components/password_manager/ios/password_controller_helper.h"
+#import "components/password_manager/ios/password_form_helper.h"
 #include "components/password_manager/ios/test_helpers.h"
 #include "ios/web/public/test/fakes/test_web_client.h"
 #import "ios/web/public/test/web_test_with_web_state.h"
@@ -38,7 +38,7 @@
 using test_helpers::SetPasswordFormFillData;
 using test_helpers::SetFillData;
 
-@interface PasswordControllerHelper (Testing)
+@interface PasswordFormHelper (Testing)
 
 // Provides access to the method below for testing with mocks.
 - (void)extractSubmittedPasswordForm:(const std::string&)formName
@@ -126,17 +126,17 @@
   }
 };
 
-class PasswordControllerHelperTest : public web::WebTestWithWebState {
+class PasswordFormHelperTest : public web::WebTestWithWebState {
  public:
-  PasswordControllerHelperTest()
+  PasswordFormHelperTest()
       : web::WebTestWithWebState(std::make_unique<TestWebClientWithScript>()) {}
 
-  ~PasswordControllerHelperTest() override = default;
+  ~PasswordFormHelperTest() override = default;
 
   void SetUp() override {
     WebTestWithWebState::SetUp();
-    helper_ = [[PasswordControllerHelper alloc] initWithWebState:web_state()
-                                                        delegate:nil];
+    helper_ =
+        [[PasswordFormHelper alloc] initWithWebState:web_state() delegate:nil];
   }
 
   void TearDown() override {
@@ -155,10 +155,10 @@
         [NSString stringWithFormat:kGetFormIdScript, form_index]));
   }
 
-  // PasswordControllerHelper for testing.
-  PasswordControllerHelper* helper_;
+  // PasswordFormHelper for testing.
+  PasswordFormHelper* helper_;
 
-  DISALLOW_COPY_AND_ASSIGN(PasswordControllerHelperTest);
+  DISALLOW_COPY_AND_ASSIGN(PasswordFormHelperTest);
 };
 
 struct GetSubmittedPasswordFormTestData {
@@ -176,7 +176,7 @@
 
 // Check that HTML forms are captured and converted correctly into
 // PasswordForms on submission.
-TEST_F(PasswordControllerHelperTest, GetSubmittedPasswordForm) {
+TEST_F(PasswordFormHelperTest, GetSubmittedPasswordForm) {
   // clang-format off
   const GetSubmittedPasswordFormTestData test_data[] = {
     // Two forms with no explicit names.
@@ -267,7 +267,7 @@
 };
 
 // Check that HTML forms are converted correctly into PasswordForms.
-TEST_F(PasswordControllerHelperTest, FindPasswordFormsInView) {
+TEST_F(PasswordFormHelperTest, FindPasswordFormsInView) {
   // clang-format off
   const FindPasswordFormTestData test_data[] = {
     // Normal form: a username and a password element.
@@ -500,7 +500,7 @@
 };
 
 // Tests that filling password forms works correctly.
-TEST_F(PasswordControllerHelperTest, FillPasswordForm) {
+TEST_F(PasswordFormHelperTest, FillPasswordForm) {
   LoadHtml(kHtmlWithMultiplePasswordForms);
 
   const std::string base_url = BaseUrl();
@@ -649,7 +649,7 @@
 }
 
 // Tests that filling password forms with fill data works correctly.
-TEST_F(PasswordControllerHelperTest, FillPasswordFormWithFillData) {
+TEST_F(PasswordFormHelperTest, FillPasswordFormWithFillData) {
   LoadHtml(
       @"<form><input id='u1' type='text' name='un1'>"
        "<input id='p1' type='password' name='pw1'></form>");
@@ -673,7 +673,7 @@
 
 // Tests that a form is found and the found form is filled in with the given
 // username and password.
-TEST_F(PasswordControllerHelperTest, FindAndFillOnePasswordForm) {
+TEST_F(PasswordFormHelperTest, FindAndFillOnePasswordForm) {
   LoadHtml(
       @"<form><input id='u1' type='text' name='un1'>"
        "<input id='p1' type='password' name='pw1'></form>");
@@ -698,7 +698,7 @@
 // Tests that multiple forms on the same page are found and filled.
 // This test includes an mock injected failure on form filling to verify
 // that completion handler is called with the proper values.
-TEST_F(PasswordControllerHelperTest, FindAndFillMultiplePasswordForms) {
+TEST_F(PasswordFormHelperTest, FindAndFillMultiplePasswordForms) {
   // Fails the first call to fill password form.
   MockJsPasswordManager* mockJsPasswordManager = [[MockJsPasswordManager alloc]
       initWithReceiver:web_state()->GetJSInjectionReceiver()];
diff --git a/components/password_manager/ios/password_suggestion_helper.h b/components/password_manager/ios/password_suggestion_helper.h
new file mode 100644
index 0000000..cbcca46
--- /dev/null
+++ b/components/password_manager/ios/password_suggestion_helper.h
@@ -0,0 +1,109 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_PASSWORD_MANAGER_IOS_PASSWORD_SUGGESTION_HELPER_H_
+#define COMPONENTS_PASSWORD_MANAGER_IOS_PASSWORD_SUGGESTION_HELPER_H_
+
+#import <Foundation/Foundation.h>
+#include <memory>
+
+#import "components/autofill/ios/browser/form_suggestion_provider.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@class FormSuggestion;
+@class PasswordSuggestionHelper;
+
+namespace autofill {
+struct PasswordFormFillData;
+}  // namespace autofill
+
+namespace password_manager {
+struct FillData;
+}  // namespace password_manager
+
+namespace web {
+class WebState;
+}  // namespace web
+
+// A protocol implemented by a delegate of PasswordSuggestionHelper.
+@protocol PasswordSuggestionHelperDelegate<NSObject>
+
+// Called when form extraction is required for checking suggestion availability.
+// The caller must trigger the form extraction in this method.
+- (void)suggestionHelperShouldTriggerFormExtraction:
+    (PasswordSuggestionHelper*)suggestionHelper;
+
+@end
+
+// Provides common logic of password autofill suggestions for both ios/chrome
+// and ios/web_view.
+@interface PasswordSuggestionHelper : NSObject
+
+// Creates an instance with the given delegate.
+- (instancetype)initWithDelegate:(id<PasswordSuggestionHelperDelegate>)delegate
+    NS_DESIGNATED_INITIALIZER;
+
+- (instancetype)init NS_UNAVAILABLE;
+
+// Retrieves suggestions as username and realm pairs
+// (defined in |password_manager::UsernameAndRealm|) and converts
+// them into objective C representations. In the returned |FormSuggestion|
+// items, |value| field will be the username and |displayDescription| will be
+// the realm.
+- (NSArray<FormSuggestion*>*)
+retrieveSuggestionsWithFormName:(NSString*)formName
+                fieldIdentifier:(NSString*)fieldIdentifier
+                      fieldType:(NSString*)fieldType;
+
+// Checks if suggestions are available for the field.
+// |completion| will be called when the check is completed, with boolean
+// parameter indicating whether suggestions are available or not.
+// See //components/autofill/ios/form_util/form_activity_params.h for definition
+// of other parameters.
+- (void)checkIfSuggestionsAvailableForForm:(NSString*)formName
+                           fieldIdentifier:(NSString*)fieldIdentifier
+                                      type:(NSString*)type
+                                   frameID:(NSString*)frameID
+                               isMainFrame:(BOOL)isMainFrame
+                                  webState:(web::WebState*)webState
+                         completionHandler:
+                             (SuggestionsAvailableCompletion)completion;
+
+// Retrieves password form fill data for |username| for use in
+// |PasswordFormHelper|'s
+// -fillPasswordFormWithFillData:completionHandler:.
+- (std::unique_ptr<password_manager::FillData>)getFillDataForUsername:
+    (NSString*)username;
+
+// The following methods should be called to maintain the correct state along
+// with password forms.
+
+// Resets fill data, callbacks and state flags for new page. This method should
+// be called in password controller's -webState:didLoadPageWithSuccess:.
+- (void)resetForNewPage;
+
+// Prepares fill data with given password form data. Triggers callback for
+// -checkIfSuggestionsAvailableForForm... if needed.
+// This method should be called in password controller's
+// -fillPasswordForm:completionHandler:.
+- (void)processWithPasswordFormFillData:
+    (const autofill::PasswordFormFillData&)formData;
+
+// Processes field for which no saved credentials are available.
+// Triggers callback for -checkIfSuggestionsAvailableForForm... if needed.
+// This method should be called in password controller's
+// -onNoSavedCredentials.
+- (void)processWithNoSavedCredentials;
+
+// Updates the state for password form extraction state.
+// This method should be called in password controller's
+// -didFinishPasswordFormExtraction:, when the extracted forms are not empty.
+- (void)updateStateOnPasswordFormExtracted;
+
+@end
+
+NS_ASSUME_NONNULL_END
+
+#endif  // COMPONENTS_PASSWORD_MANAGER_IOS_PASSWORD_SUGGESTION_HELPER_H_
diff --git a/components/password_manager/ios/password_suggestion_helper.mm b/components/password_manager/ios/password_suggestion_helper.mm
new file mode 100644
index 0000000..f46f1853
--- /dev/null
+++ b/components/password_manager/ios/password_suggestion_helper.mm
@@ -0,0 +1,175 @@
+// 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 "components/password_manager/ios/password_suggestion_helper.h"
+
+#include "base/strings/sys_string_conversions.h"
+#include "components/autofill/core/common/form_data.h"
+#import "components/autofill/ios/browser/form_suggestion.h"
+#include "components/password_manager/ios/account_select_fill_data.h"
+#include "ios/web/public/web_state/web_frame.h"
+#include "ios/web/public/web_state/web_frame_util.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
+
+using autofill::FormData;
+using autofill::PasswordFormFillData;
+using base::SysNSStringToUTF16;
+using base::SysNSStringToUTF8;
+using base::SysUTF16ToNSString;
+using base::SysUTF8ToNSString;
+using password_manager::AccountSelectFillData;
+using password_manager::FillData;
+
+typedef void (^PasswordSuggestionsAvailableCompletion)(
+    const password_manager::AccountSelectFillData* __nullable);
+
+namespace {
+NSString* const kPasswordFieldType = @"password";
+}  // namespace
+
+@interface PasswordSuggestionHelper ()
+// Delegate to receive callbacks.
+@property(nonatomic, weak, readonly) id<PasswordSuggestionHelperDelegate>
+    delegate;
+
+@end
+
+@implementation PasswordSuggestionHelper {
+  // The C++ interface to cache and retrieve password suggestions.
+  AccountSelectFillData _fillData;
+
+  // YES indicates that extracted password form has been sent to the password
+  // manager.
+  BOOL _sentPasswordFormToPasswordManager;
+
+  // The completion to inform the caller of -checkIfSuggestionsAvailableForForm:
+  // that suggestions are available for a given form and field.
+  PasswordSuggestionsAvailableCompletion _suggestionsAvailableCompletion;
+}
+
+@synthesize delegate = _delegate;
+
+- (instancetype)initWithDelegate:
+    (id<PasswordSuggestionHelperDelegate>)delegate {
+  self = [super init];
+  if (self) {
+    _sentPasswordFormToPasswordManager = NO;
+    _delegate = delegate;
+  }
+  return self;
+}
+
+#pragma mark - Public methods
+
+- (NSArray<FormSuggestion*>*)
+retrieveSuggestionsWithFormName:(NSString*)formName
+                fieldIdentifier:(NSString*)fieldIdentifier
+                      fieldType:(NSString*)fieldType {
+  base::string16 utfFormName = SysNSStringToUTF16(formName);
+  base::string16 utfFieldIdentifier = SysNSStringToUTF16(fieldIdentifier);
+  BOOL isPasswordField = [fieldType isEqual:kPasswordFieldType];
+
+  NSMutableArray<FormSuggestion*>* results = [NSMutableArray array];
+
+  if (_fillData.IsSuggestionsAvailable(utfFormName, utfFieldIdentifier,
+                                       isPasswordField)) {
+    std::vector<password_manager::UsernameAndRealm> usernameAndRealms =
+        _fillData.RetrieveSuggestions(utfFormName, utfFieldIdentifier,
+                                      isPasswordField);
+
+    for (const auto& usernameAndRealm : usernameAndRealms) {
+      NSString* username = SysUTF16ToNSString(usernameAndRealm.username);
+      NSString* realm = usernameAndRealm.realm.empty()
+                            ? nil
+                            : SysUTF8ToNSString(usernameAndRealm.realm);
+      [results addObject:[FormSuggestion suggestionWithValue:username
+                                          displayDescription:realm
+                                                        icon:nil
+                                                  identifier:0]];
+    }
+  }
+
+  return [results copy];
+}
+
+- (void)checkIfSuggestionsAvailableForForm:(NSString*)formName
+                           fieldIdentifier:(NSString*)fieldIdentifier
+                                      type:(NSString*)type
+                                   frameID:(NSString*)frameID
+                               isMainFrame:(BOOL)isMainFrame
+                                  webState:(web::WebState*)webState
+                         completionHandler:
+                             (SuggestionsAvailableCompletion)completion {
+  // When password controller's -processWithPasswordFormFillData: is already
+  // called, |completion| will be called immediately and |triggerFormExtraction|
+  // will be skipped.
+  // Otherwise, -suggestionHelperShouldTriggerFormExtraction: will be called
+  // and |completion| will not be called until
+  // -processWithPasswordFormFillData: is called.
+  // For unsupported form, |completion| will be called immediately and
+  // -suggestionHelperShouldTriggerFormExtraction: will be skipped.
+  if (!isMainFrame) {
+    web::WebFrame* frame =
+        web::GetWebFrameWithId(webState, SysNSStringToUTF8(frameID));
+    if (!frame || webState->GetLastCommittedURL().GetOrigin() !=
+                      frame->GetSecurityOrigin()) {
+      // Passwords is only supported on main frame and iframes with the same
+      // origin.
+      completion(NO);
+    }
+  }
+
+  if (!_sentPasswordFormToPasswordManager && [type isEqual:@"focus"]) {
+    // Save the callback until fill data is ready.
+    _suggestionsAvailableCompletion = ^(const AccountSelectFillData* fillData) {
+      completion(!fillData ? NO
+                           : fillData->IsSuggestionsAvailable(
+                                 SysNSStringToUTF16(formName),
+                                 SysNSStringToUTF16(fieldIdentifier), false));
+    };
+    // Form extraction is required for this check.
+    [self.delegate suggestionHelperShouldTriggerFormExtraction:self];
+    return;
+  }
+
+  completion(_fillData.IsSuggestionsAvailable(
+      SysNSStringToUTF16(formName), SysNSStringToUTF16(fieldIdentifier),
+      false));
+}
+
+- (std::unique_ptr<password_manager::FillData>)getFillDataForUsername:
+    (NSString*)username {
+  return _fillData.GetFillData(SysNSStringToUTF16(username));
+}
+
+- (void)resetForNewPage {
+  _fillData.Reset();
+  _sentPasswordFormToPasswordManager = NO;
+  _suggestionsAvailableCompletion = nil;
+}
+
+- (void)processWithPasswordFormFillData:(const PasswordFormFillData&)formData {
+  _fillData.Add(formData);
+
+  if (_suggestionsAvailableCompletion) {
+    _suggestionsAvailableCompletion(&_fillData);
+    _suggestionsAvailableCompletion = nil;
+  }
+}
+- (void)processWithNoSavedCredentials {
+  if (_suggestionsAvailableCompletion) {
+    _suggestionsAvailableCompletion(nullptr);
+  }
+  _suggestionsAvailableCompletion = nil;
+}
+
+- (void)updateStateOnPasswordFormExtracted {
+  _sentPasswordFormToPasswordManager = YES;
+}
+
+@end
diff --git a/components/sync/driver/glue/sync_backend_host_impl.cc b/components/sync/driver/glue/sync_backend_host_impl.cc
index 8d85557..42af545 100644
--- a/components/sync/driver/glue/sync_backend_host_impl.cc
+++ b/components/sync/driver/glue/sync_backend_host_impl.cc
@@ -149,11 +149,9 @@
   DCHECK(!host_);
 
   if (invalidation_handler_registered_) {
-    if (reason != BROWSER_SHUTDOWN) {
-      bool success =
-          invalidator_->UpdateRegisteredInvalidationIds(this, ObjectIdSet());
-      DCHECK(success);
-    }
+    bool success =
+        invalidator_->UpdateRegisteredInvalidationIds(this, ObjectIdSet());
+    DCHECK(success);
     invalidator_->UnregisterInvalidationHandler(this);
     invalidator_ = nullptr;
   }
diff --git a/components/sync/engine_impl/loopback_server/loopback_server.cc b/components/sync/engine_impl/loopback_server/loopback_server.cc
index 29c3977..5a0be40 100644
--- a/components/sync/engine_impl/loopback_server/loopback_server.cc
+++ b/components/sync/engine_impl/loopback_server/loopback_server.cc
@@ -185,6 +185,7 @@
     if (!top_level_entity) {
       return false;
     }
+    top_level_permanent_item_ids_[model_type] = top_level_entity->GetId();
     SaveEntity(std::move(top_level_entity));
 
     if (model_type == syncer::BOOKMARKS) {
@@ -200,6 +201,15 @@
   return true;
 }
 
+std::string LoopbackServer::GetTopLevelPermanentItemId(
+    syncer::ModelType model_type) {
+  auto it = top_level_permanent_item_ids_.find(model_type);
+  if (it == top_level_permanent_item_ids_.end()) {
+    return std::string();
+  }
+  return it->second;
+}
+
 void LoopbackServer::UpdateEntityVersion(LoopbackServerEntity* entity) {
   entity->SetVersion(++version_);
 }
@@ -506,6 +516,22 @@
   return sync_entities;
 }
 
+std::vector<sync_pb::SyncEntity>
+LoopbackServer::GetPermanentSyncEntitiesByModelType(ModelType model_type) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  std::vector<sync_pb::SyncEntity> sync_entities;
+  for (const auto& kv : entities_) {
+    const LoopbackServerEntity& entity = *kv.second;
+    if (!entity.IsDeleted() && entity.IsPermanent() &&
+        entity.GetModelType() == model_type) {
+      sync_pb::SyncEntity sync_entity;
+      entity.SerializeAsProto(&sync_entity);
+      sync_entities.push_back(sync_entity);
+    }
+  }
+  return sync_entities;
+}
+
 std::unique_ptr<base::DictionaryValue>
 LoopbackServer::GetEntitiesAsDictionaryValue() {
   DCHECK(thread_checker_.CalledOnValidThread());
diff --git a/components/sync/engine_impl/loopback_server/loopback_server.h b/components/sync/engine_impl/loopback_server/loopback_server.h
index 407f2c6e..680e9a1 100644
--- a/components/sync/engine_impl/loopback_server/loopback_server.h
+++ b/components/sync/engine_impl/loopback_server/loopback_server.h
@@ -88,6 +88,10 @@
   // Inserts the default permanent items in |entities_|.
   bool CreateDefaultPermanentItems();
 
+  // Returns an empty string if no top-level permanent item of the given type
+  // was created.
+  std::string GetTopLevelPermanentItemId(syncer::ModelType model_type);
+
   std::string GenerateNewKeystoreKey() const;
 
   // Saves a |entity| to |entities_|.
@@ -127,14 +131,20 @@
   std::string GetStoreBirthday() const;
 
   // Returns all entities stored by the server of the given |model_type|.
-  // This method is only used in tests.
+  // Permanent entities are excluded. This method is only used in tests.
   std::vector<sync_pb::SyncEntity> GetSyncEntitiesByModelType(
       syncer::ModelType model_type);
 
+  // Returns a list of permanent entities of the given |model_type|. This method
+  // is only used in tests.
+  std::vector<sync_pb::SyncEntity> GetPermanentSyncEntitiesByModelType(
+      syncer::ModelType model_type);
+
   // Creates a DicionaryValue representation of all entities present in the
   // server. The dictionary keys are the strings generated by ModelTypeToString
   // and the values are ListValues containing StringValue versions of entity
-  // names. Used by test to verify the contents of the server state.
+  // names. Permanent entities are excluded. Used by test to verify the contents
+  // of the server state.
   std::unique_ptr<base::DictionaryValue> GetEntitiesAsDictionaryValue();
 
   // Modifies the entity on the server with the given |id|. The entity's
@@ -189,6 +199,7 @@
   int64_t store_birthday_;
 
   EntityMap entities_;
+  std::map<ModelType, std::string> top_level_permanent_item_ids_;
   std::vector<std::string> keystore_keys_;
 
   // The file used to store the local sync data.
diff --git a/components/sync/test/fake_server/fake_server.cc b/components/sync/test/fake_server/fake_server.cc
index c2b55fc..e2f9c67c 100644
--- a/components/sync/test/fake_server/fake_server.cc
+++ b/components/sync/test/fake_server/fake_server.cc
@@ -306,6 +306,18 @@
   return loopback_server_->GetSyncEntitiesByModelType(model_type);
 }
 
+std::vector<sync_pb::SyncEntity>
+FakeServer::GetPermanentSyncEntitiesByModelType(ModelType model_type) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  return loopback_server_->GetPermanentSyncEntitiesByModelType(model_type);
+}
+
+std::string FakeServer::GetTopLevelPermanentItemId(
+    syncer::ModelType model_type) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  return loopback_server_->GetTopLevelPermanentItemId(model_type);
+}
+
 void FakeServer::InjectEntity(std::unique_ptr<LoopbackServerEntity> entity) {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(entity->GetModelType() != syncer::AUTOFILL_WALLET_DATA)
diff --git a/components/sync/test/fake_server/fake_server.h b/components/sync/test/fake_server/fake_server.h
index fdede785..6a8c2928 100644
--- a/components/sync/test/fake_server/fake_server.h
+++ b/components/sync/test/fake_server/fake_server.h
@@ -81,10 +81,21 @@
   // Returns all entities stored by the server of the given |model_type|.
   // This method returns SyncEntity protocol buffer objects (instead of
   // LoopbackServerEntity) so that callers can inspect datatype-specific data
-  // (e.g., the URL of a session tab).
+  // (e.g., the URL of a session tab). Permanent entities are excluded.
   std::vector<sync_pb::SyncEntity> GetSyncEntitiesByModelType(
       syncer::ModelType model_type);
 
+  // Returns all permanent entities stored by the server of the given
+  // |model_type|. This method returns SyncEntity protocol buffer objects
+  // (instead of LoopbackServerEntity) so that callers can inspect
+  // datatype-specific data (e.g., the URL of a session tab).
+  std::vector<sync_pb::SyncEntity> GetPermanentSyncEntitiesByModelType(
+      syncer::ModelType model_type);
+
+  // Returns an empty string if no top-level permanent item of the given type
+  // was created.
+  std::string GetTopLevelPermanentItemId(syncer::ModelType model_type);
+
   // Adds |entity| to the server's collection of entities. This method makes no
   // guarantees that the added entity will result in successful server
   // operations.
diff --git a/components/sync_bookmarks/BUILD.gn b/components/sync_bookmarks/BUILD.gn
index e264a6f..3b36cc4 100644
--- a/components/sync_bookmarks/BUILD.gn
+++ b/components/sync_bookmarks/BUILD.gn
@@ -38,6 +38,7 @@
     "//components/keyed_service/core:core",
     "//components/sync",
     "//components/undo",
+    "//ui/base",
     "//ui/gfx",
   ]
 }
diff --git a/components/sync_bookmarks/DEPS b/components/sync_bookmarks/DEPS
index eb67095..bc162d7 100644
--- a/components/sync_bookmarks/DEPS
+++ b/components/sync_bookmarks/DEPS
@@ -7,5 +7,6 @@
   "+components/prefs",
   "+components/sync",
   "+components/undo",
+  "+ui/base",
   "+ui/gfx",
 ]
diff --git a/components/sync_bookmarks/bookmark_model_type_processor.cc b/components/sync_bookmarks/bookmark_model_type_processor.cc
index 9a69445..cc7ae9c 100644
--- a/components/sync_bookmarks/bookmark_model_type_processor.cc
+++ b/components/sync_bookmarks/bookmark_model_type_processor.cc
@@ -216,10 +216,9 @@
   sync_pb::BookmarkModelMetadata model_metadata;
   model_metadata.ParseFromString(metadata_str);
 
-  auto model_type_state = std::make_unique<sync_pb::ModelTypeState>();
-  model_type_state->Swap(model_metadata.mutable_model_type_state());
-
-  if (model_type_state->initial_sync_done()) {
+  if (model_metadata.model_type_state().initial_sync_done() &&
+      SyncedBookmarkTracker::BookmarkModelMatchesMetadata(model,
+                                                          model_metadata)) {
     std::vector<NodeMetadataPair> nodes_metadata;
     for (sync_pb::BookmarkMetadata& bookmark_metadata :
          *model_metadata.mutable_bookmarks_metadata()) {
@@ -227,35 +226,20 @@
       // all nodes and store in a map keyed by id instead of doing a lookup for
       // every id.
       const bookmarks::BookmarkNode* node = nullptr;
-      if (bookmark_metadata.metadata().is_deleted()) {
-        if (bookmark_metadata.has_id()) {
-          DLOG(ERROR) << "Error when decoding sync metadata: Tombstones "
-                         "shouldn't have a bookmark id.";
-          continue;
-        }
-      } else {
-        if (!bookmark_metadata.has_id()) {
-          DLOG(ERROR)
-              << "Error when decoding sync metadata: Bookmark id is missing.";
-          continue;
-        }
+      if (!bookmark_metadata.metadata().is_deleted()) {
         node = GetBookmarkNodeByID(bookmark_model_, bookmark_metadata.id());
-        if (node == nullptr) {
-          DLOG(ERROR) << "Error when decoding sync metadata: Cannot find the "
-                         "bookmark node.";
-          continue;
-        }
+        DCHECK(node);
       }
       auto metadata = std::make_unique<sync_pb::EntityMetadata>();
       metadata->Swap(bookmark_metadata.mutable_metadata());
       nodes_metadata.emplace_back(node, std::move(metadata));
     }
-    // TODO(crbug.com/516866): Handle local nodes that don't have a
-    // corresponding
-    // metadata.
+    auto model_type_state = std::make_unique<sync_pb::ModelTypeState>();
+    model_type_state->Swap(model_metadata.mutable_model_type_state());
     StartTrackingMetadata(std::move(nodes_metadata),
                           std::move(model_type_state));
-  } else if (!model_metadata.bookmarks_metadata().empty()) {
+  } else if (!model_metadata.model_type_state().initial_sync_done() &&
+             !model_metadata.bookmarks_metadata().empty()) {
     DLOG(ERROR)
         << "Persisted Metadata not empty while initial sync is not done.";
   }
diff --git a/components/sync_bookmarks/bookmark_model_type_processor.h b/components/sync_bookmarks/bookmark_model_type_processor.h
index 66adbba..2a221df 100644
--- a/components/sync_bookmarks/bookmark_model_type_processor.h
+++ b/components/sync_bookmarks/bookmark_model_type_processor.h
@@ -63,7 +63,7 @@
   // restored via ModelReadyToSync() below.
   std::string EncodeSyncMetadata() const;
 
-  // It mainly decodes a BookmarkModelMetadata proto seralized in
+  // It mainly decodes a BookmarkModelMetadata proto serialized in
   // |metadata_str|, and uses it to fill in the tracker and the model type state
   // objects. |model| must not be null and must outlive this object. It is used
   // to the retrieve the local node ids, and is stored in the processor to be
diff --git a/components/sync_bookmarks/bookmark_model_type_processor_unittest.cc b/components/sync_bookmarks/bookmark_model_type_processor_unittest.cc
index 49a0312..33f299fe 100644
--- a/components/sync_bookmarks/bookmark_model_type_processor_unittest.cc
+++ b/components/sync_bookmarks/bookmark_model_type_processor_unittest.cc
@@ -30,7 +30,11 @@
 namespace {
 
 const char kBookmarkBarTag[] = "bookmark_bar";
+const char kOtherBookmarksTag[] = "other_bookmarks";
+const char kMobileBookmarksTag[] = "synced_bookmarks";
 const char kBookmarkBarId[] = "bookmark_bar_id";
+const char kOtherBookmarksId[] = "other_bookmarks_id";
+const char kMobileBookmarksId[] = "mobile_bookmarks_id";
 const char kBookmarksRootId[] = "root_id";
 const char kCacheGuid[] = "generated_id";
 
@@ -79,8 +83,8 @@
   const SyncedBookmarkTracker* tracker = processor->GetTrackerForTest();
 
   // Make sure the tracker contains all bookmarks in |bookmarks| + the
-  // bookmark bar node.
-  ASSERT_THAT(tracker->TrackedEntitiesCountForTest(), Eq(bookmarks.size() + 1));
+  // 3 permanent nodes.
+  ASSERT_THAT(tracker->TrackedEntitiesCountForTest(), Eq(bookmarks.size() + 3));
 
   for (BookmarkInfo bookmark : bookmarks) {
     const SyncedBookmarkTracker::Entity* entity =
@@ -103,22 +107,26 @@
   syncer::UpdateResponseDataList updates;
   syncer::UniquePosition pos = syncer::UniquePosition::InitialPosition(
       syncer::UniquePosition::RandomSuffix());
-  // Add update for the permanent folder "Bookmarks bar".
+  // Add update for the permanent folders "Bookmarks bar", "Other Bookmarks" and
+  // "Mobile Bookmarks".
   updates.push_back(
       CreateUpdateResponseData({kBookmarkBarId, std::string(), std::string(),
                                 kBookmarksRootId, kBookmarkBarTag},
                                pos, /*response_version=*/0));
+  updates.push_back(
+      CreateUpdateResponseData({kOtherBookmarksId, std::string(), std::string(),
+                                kBookmarksRootId, kOtherBookmarksTag},
+                               pos, /*response_version=*/0));
+  updates.push_back(CreateUpdateResponseData(
+      {kMobileBookmarksId, std::string(), std::string(), kBookmarksRootId,
+       kMobileBookmarksTag},
+      pos, /*response_version=*/0));
   for (BookmarkInfo bookmark : bookmarks) {
     pos = syncer::UniquePosition::After(pos,
                                         syncer::UniquePosition::RandomSuffix());
     updates.push_back(
         CreateUpdateResponseData(bookmark, pos, /*response_version=*/0));
   }
-  // TODO(crbug.com/516866): Remove after a proper positioning for remote
-  // updates is implemented. Reversing the updates because the sorting algorithm
-  // isn't stable. This is OK for now because once proper positioning is
-  // implemented, the siblings update requests order would be irrelvant.
-  std::reverse(updates.begin(), updates.end());
   processor->OnUpdateReceived(CreateDummyModelTypeState(), updates);
   AssertState(processor, bookmarks);
 }
@@ -278,11 +286,20 @@
   // within the processor.
   sync_pb::BookmarkModelMetadata model_metadata;
   model_metadata.mutable_model_type_state()->set_initial_sync_done(true);
-  // Add an entry for bookmark bar.
+  // Add entries for the permanent nodes. TestBookmarkClient adds all of them.
   sync_pb::BookmarkMetadata* bookmark_metadata =
       model_metadata.add_bookmarks_metadata();
   bookmark_metadata->set_id(bookmark_bar_node->id());
   bookmark_metadata->mutable_metadata()->set_server_id(kBookmarkBarId);
+
+  bookmark_metadata = model_metadata.add_bookmarks_metadata();
+  bookmark_metadata->set_id(bookmark_model()->other_node()->id());
+  bookmark_metadata->mutable_metadata()->set_server_id(kOtherBookmarksId);
+
+  bookmark_metadata = model_metadata.add_bookmarks_metadata();
+  bookmark_metadata->set_id(bookmark_model()->mobile_node()->id());
+  bookmark_metadata->mutable_metadata()->set_server_id(kMobileBookmarksId);
+
   // Add an entry for the bookmark node.
   bookmark_metadata = model_metadata.add_bookmarks_metadata();
   bookmark_metadata->set_id(bookmarknode->id());
@@ -331,6 +348,50 @@
   AssertState(&new_processor, bookmarks);
 }
 
+TEST_F(BookmarkModelTypeProcessorTest,
+       ShouldIgnoreNonEmptyMetadataWhileSyncNotDone) {
+  sync_pb::BookmarkModelMetadata model_metadata;
+  model_metadata.mutable_model_type_state()->set_initial_sync_done(false);
+  // Add entries to the metadata.
+  sync_pb::BookmarkMetadata* bookmark_metadata =
+      model_metadata.add_bookmarks_metadata();
+  bookmark_metadata->set_id(bookmark_model()->bookmark_bar_node()->id());
+  bookmark_metadata->mutable_metadata()->set_server_id(kBookmarkBarId);
+
+  // Create a new processor and init it with the metadata str.
+  BookmarkModelTypeProcessor new_processor(bookmark_undo_service());
+
+  std::string metadata_str;
+  model_metadata.SerializeToString(&metadata_str);
+  new_processor.ModelReadyToSync(metadata_str, base::DoNothing(),
+                                 bookmark_model());
+  // Metadata are corrupted, so no tracker should have been created.
+  EXPECT_THAT(new_processor.GetTrackerForTest(), IsNull());
+}
+
+TEST_F(BookmarkModelTypeProcessorTest,
+       ShouldIgnoreMetadataNotMatchingTheModel) {
+  sync_pb::BookmarkModelMetadata model_metadata;
+  model_metadata.mutable_model_type_state()->set_initial_sync_done(true);
+  // Add entries for only the bookmark bar. However, the TestBookmarkClient will
+  // create all the 3 permanent nodes.
+  sync_pb::BookmarkMetadata* bookmark_metadata =
+      model_metadata.add_bookmarks_metadata();
+  bookmark_metadata->set_id(bookmark_model()->bookmark_bar_node()->id());
+  bookmark_metadata->mutable_metadata()->set_server_id(kBookmarkBarId);
+
+  // Create a new processor and init it with the metadata str.
+  BookmarkModelTypeProcessor new_processor(bookmark_undo_service());
+
+  std::string metadata_str;
+  model_metadata.SerializeToString(&metadata_str);
+  new_processor.ModelReadyToSync(metadata_str, base::DoNothing(),
+                                 bookmark_model());
+
+  // Metadata are corrupted, so no tracker should have been created.
+  EXPECT_THAT(new_processor.GetTrackerForTest(), IsNull());
+}
+
 // Verifies that the model type state stored in the tracker gets
 // updated upon handling remote updates by assigning a new encryption
 // key name.
diff --git a/components/sync_bookmarks/synced_bookmark_tracker.cc b/components/sync_bookmarks/synced_bookmark_tracker.cc
index 14c3c35..c49854f 100644
--- a/components/sync_bookmarks/synced_bookmark_tracker.cc
+++ b/components/sync_bookmarks/synced_bookmark_tracker.cc
@@ -4,6 +4,7 @@
 
 #include "components/sync_bookmarks/synced_bookmark_tracker.h"
 
+#include <algorithm>
 #include <set>
 #include <utility>
 
@@ -11,11 +12,13 @@
 #include "base/sha1.h"
 #include "base/stl_util.h"
 #include "base/trace_event/memory_usage_estimator.h"
+#include "components/bookmarks/browser/bookmark_model.h"
 #include "components/bookmarks/browser/bookmark_node.h"
 #include "components/sync/base/time.h"
 #include "components/sync/base/unique_position.h"
 #include "components/sync/model/entity_data.h"
 #include "components/sync/protocol/proto_memory_estimations.h"
+#include "ui/base/models/tree_node_iterator.h"
 
 namespace sync_bookmarks {
 
@@ -107,6 +110,58 @@
 
 SyncedBookmarkTracker::~SyncedBookmarkTracker() = default;
 
+// static
+bool SyncedBookmarkTracker::BookmarkModelMatchesMetadata(
+    const bookmarks::BookmarkModel* model,
+    const sync_pb::BookmarkModelMetadata& model_metadata) {
+  DCHECK(model_metadata.model_type_state().initial_sync_done());
+
+  // Collect ids of non-deletion entries in the metadata.
+  std::vector<int> metadata_node_ids;
+  for (const sync_pb::BookmarkMetadata& bookmark_metadata :
+       model_metadata.bookmarks_metadata()) {
+    if (!bookmark_metadata.metadata().has_server_id()) {
+      DLOG(ERROR) << "Error when decoding sync metadata: Entities must contain "
+                     "server id.";
+      return false;
+    }
+    if (bookmark_metadata.metadata().is_deleted() &&
+        bookmark_metadata.has_id()) {
+      DLOG(ERROR) << "Error when decoding sync metadata: Tombstones "
+                     "shouldn't have a bookmark id.";
+      return false;
+    }
+    if (!bookmark_metadata.metadata().is_deleted() &&
+        !bookmark_metadata.has_id()) {
+      DLOG(ERROR)
+          << "Error when decoding sync metadata: Bookmark id is missing.";
+      return false;
+    }
+    // The entry is valid. If it's not a tombstone, collect its node id to
+    // compare it later with the ids in the model.
+    if (!bookmark_metadata.metadata().is_deleted()) {
+      metadata_node_ids.push_back(bookmark_metadata.id());
+    }
+  }
+
+  // Collect ids of nodes in the model.
+  std::vector<int> model_node_ids;
+  ui::TreeNodeIterator<const bookmarks::BookmarkNode> iterator(
+      model->root_node());
+  while (iterator.has_next()) {
+    const bookmarks::BookmarkNode* node = iterator.Next();
+    model_node_ids.push_back(node->id());
+  }
+
+  if (model_node_ids.size() != metadata_node_ids.size()) {
+    return false;
+  }
+  std::sort(model_node_ids.begin(), model_node_ids.end());
+  std::sort(metadata_node_ids.begin(), metadata_node_ids.end());
+  return std::equal(model_node_ids.begin(), model_node_ids.end(),
+                    metadata_node_ids.begin());
+}
+
 const SyncedBookmarkTracker::Entity* SyncedBookmarkTracker::GetEntityForSyncId(
     const std::string& sync_id) const {
   auto it = sync_id_to_entities_map_.find(sync_id);
diff --git a/components/sync_bookmarks/synced_bookmark_tracker.h b/components/sync_bookmarks/synced_bookmark_tracker.h
index 5857786..8384924e 100644
--- a/components/sync_bookmarks/synced_bookmark_tracker.h
+++ b/components/sync_bookmarks/synced_bookmark_tracker.h
@@ -18,6 +18,7 @@
 #include "components/sync/protocol/unique_position.pb.h"
 
 namespace bookmarks {
+class BookmarkModel;
 class BookmarkNode;
 }
 
@@ -95,6 +96,13 @@
       std::unique_ptr<sync_pb::ModelTypeState> model_type_state);
   ~SyncedBookmarkTracker();
 
+  // Checks the integrity of the |model_metadata|. It also verifies that the
+  // contents of the |model_metadata| match the contents of |model|. It should
+  // only be called if the initial sync has completed.
+  static bool BookmarkModelMatchesMetadata(
+      const bookmarks::BookmarkModel* model,
+      const sync_pb::BookmarkModelMetadata& model_metadata);
+
   // Returns null if no entity is found.
   const Entity* GetEntityForSyncId(const std::string& sync_id) const;
 
diff --git a/components/sync_bookmarks/synced_bookmark_tracker_unittest.cc b/components/sync_bookmarks/synced_bookmark_tracker_unittest.cc
index f368bc25..10955cf1 100644
--- a/components/sync_bookmarks/synced_bookmark_tracker_unittest.cc
+++ b/components/sync_bookmarks/synced_bookmark_tracker_unittest.cc
@@ -376,6 +376,94 @@
   EXPECT_THAT(entities_with_local_change[3]->metadata()->server_id(), Eq(kId3));
 }
 
+TEST(SyncedBookmarkTrackerTest, ShouldMatchModelAndMetadata) {
+  std::unique_ptr<bookmarks::BookmarkModel> model =
+      bookmarks::TestBookmarkClient::CreateModel();
+
+  const bookmarks::BookmarkNode* bookmark_bar_node = model->bookmark_bar_node();
+  const bookmarks::BookmarkNode* node = model->AddFolder(
+      /*parent=*/bookmark_bar_node, /*index=*/0, base::UTF8ToUTF16("node0"));
+
+  sync_pb::BookmarkModelMetadata model_metadata;
+  model_metadata.mutable_model_type_state()->set_initial_sync_done(true);
+  // Add entries for all the permanent nodes. TestBookmarkClient creates all the
+  // 3 permanent nodes.
+  sync_pb::BookmarkMetadata* bookmark_metadata =
+      model_metadata.add_bookmarks_metadata();
+  bookmark_metadata->set_id(model->bookmark_bar_node()->id());
+  bookmark_metadata->mutable_metadata()->set_server_id("BookmarkBarId");
+
+  bookmark_metadata = model_metadata.add_bookmarks_metadata();
+  bookmark_metadata->set_id(model->other_node()->id());
+  bookmark_metadata->mutable_metadata()->set_server_id("OtherBookmarksId");
+
+  bookmark_metadata = model_metadata.add_bookmarks_metadata();
+  bookmark_metadata->set_id(model->mobile_node()->id());
+  bookmark_metadata->mutable_metadata()->set_server_id("MobileBookmarksId");
+
+  // Add entry for the extra node.
+  bookmark_metadata = model_metadata.add_bookmarks_metadata();
+  bookmark_metadata->set_id(node->id());
+  bookmark_metadata->mutable_metadata()->set_server_id("NodeId");
+
+  // Add a tombstone entry.
+  sync_pb::BookmarkMetadata* tombstone =
+      model_metadata.add_bookmarks_metadata();
+  tombstone->mutable_metadata()->set_server_id("tombstoneId");
+  tombstone->mutable_metadata()->set_is_deleted(true);
+
+  EXPECT_TRUE(SyncedBookmarkTracker::BookmarkModelMatchesMetadata(
+      model.get(), model_metadata));
+}
+
+TEST(SyncedBookmarkTrackerTest, ShouldNotMatchModelAndCorruptedMetadata) {
+  std::unique_ptr<bookmarks::BookmarkModel> model =
+      bookmarks::TestBookmarkClient::CreateModel();
+
+  sync_pb::BookmarkModelMetadata model_metadata;
+  model_metadata.mutable_model_type_state()->set_initial_sync_done(true);
+  // Add entries for 2 permanent nodes only. TestBookmarkClient creates all the
+  // 3 permanent nodes.
+  sync_pb::BookmarkMetadata* bookmark_metadata =
+      model_metadata.add_bookmarks_metadata();
+  bookmark_metadata->set_id(model->bookmark_bar_node()->id());
+  bookmark_metadata->mutable_metadata()->set_server_id("BookmarkBarId");
+
+  bookmark_metadata = model_metadata.add_bookmarks_metadata();
+  bookmark_metadata->set_id(model->other_node()->id());
+  bookmark_metadata->mutable_metadata()->set_server_id("OtherBookmarksId");
+
+  // The entry for the Mobile bookmarks is missing.
+  EXPECT_FALSE(SyncedBookmarkTracker::BookmarkModelMatchesMetadata(
+      model.get(), model_metadata));
+
+  // The entry for the Mobile bookmarks is missing a server id.
+  bookmark_metadata = model_metadata.add_bookmarks_metadata();
+  bookmark_metadata->set_id(model->mobile_node()->id());
+  EXPECT_FALSE(SyncedBookmarkTracker::BookmarkModelMatchesMetadata(
+      model.get(), model_metadata));
+
+  // The entry for the Mobile bookmarks is missing a node id.
+  bookmark_metadata->clear_id();
+  bookmark_metadata->mutable_metadata()->set_server_id("OtherBookmarksId");
+  EXPECT_FALSE(SyncedBookmarkTracker::BookmarkModelMatchesMetadata(
+      model.get(), model_metadata));
+
+  // The entry for the Mobile bookmarks is having a wrong node id.
+  bookmark_metadata->set_id(model->mobile_node()->id() + 1);
+  EXPECT_FALSE(SyncedBookmarkTracker::BookmarkModelMatchesMetadata(
+      model.get(), model_metadata));
+
+  // A tombstone shouldn't have a node id.
+  sync_pb::BookmarkMetadata* tombstone =
+      model_metadata.add_bookmarks_metadata();
+  tombstone->mutable_metadata()->set_server_id("tombstoneId");
+  tombstone->mutable_metadata()->set_is_deleted(true);
+  tombstone->set_id(10);
+  EXPECT_FALSE(SyncedBookmarkTracker::BookmarkModelMatchesMetadata(
+      model.get(), model_metadata));
+}
+
 }  // namespace
 
 }  // namespace sync_bookmarks
diff --git a/content/browser/frame_host/cross_process_frame_connector.cc b/content/browser/frame_host/cross_process_frame_connector.cc
index 5cf6462..b1d54ae 100644
--- a/content/browser/frame_host/cross_process_frame_connector.cc
+++ b/content/browser/frame_host/cross_process_frame_connector.cc
@@ -279,8 +279,7 @@
   DCHECK(event.GetType() == blink::WebInputEvent::kGestureScrollBegin ||
          event.GetType() == blink::WebInputEvent::kGestureScrollUpdate ||
          event.GetType() == blink::WebInputEvent::kGestureScrollEnd ||
-         event.GetType() == blink::WebInputEvent::kGestureFlingStart ||
-         event.GetType() == blink::WebInputEvent::kGestureFlingCancel);
+         event.GetType() == blink::WebInputEvent::kGestureFlingStart);
   auto* parent_view = GetParentRenderWidgetHostView();
 
   if (!parent_view)
@@ -303,9 +302,7 @@
   if (event.GetType() == blink::WebInputEvent::kGestureScrollBegin) {
     event_router->BubbleScrollEvent(parent_view, resent_gesture_event, view_);
     is_scroll_bubbling_ = true;
-  } else if (is_scroll_bubbling_ ||
-             event.GetType() == blink::WebInputEvent::kGestureFlingCancel) {
-    // For GFC events the router decides whether to bubble them or not.
+  } else if (is_scroll_bubbling_) {
     event_router->BubbleScrollEvent(parent_view, resent_gesture_event, view_);
   }
   if (event.GetType() == blink::WebInputEvent::kGestureScrollEnd ||
diff --git a/content/browser/frame_host/navigation_controller_impl.cc b/content/browser/frame_host/navigation_controller_impl.cc
index bd5221c..1be647e 100644
--- a/content/browser/frame_host/navigation_controller_impl.cc
+++ b/content/browser/frame_host/navigation_controller_impl.cc
@@ -62,6 +62,7 @@
 #include "content/browser/frame_host/navigator.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"  // Temporary
 #include "content/browser/site_instance_impl.h"
+#include "content/common/content_constants_internal.h"
 #include "content/common/frame_messages.h"
 #include "content/common/view_messages.h"
 #include "content/public/browser/browser_context.h"
@@ -133,6 +134,24 @@
   return last_entry && last_entry->GetIsOverridingUserAgent();
 }
 
+// Determines whether to override user agent for a navigation.
+bool ShouldOverrideUserAgent(
+    NavigationController::UserAgentOverrideOption override_user_agent,
+    const NavigationEntry* last_committed_entry) {
+  switch (override_user_agent) {
+    case NavigationController::UA_OVERRIDE_INHERIT:
+      return ShouldKeepOverride(last_committed_entry);
+    case NavigationController::UA_OVERRIDE_TRUE:
+      return true;
+    case NavigationController::UA_OVERRIDE_FALSE:
+      return false;
+    default:
+      break;
+  }
+  NOTREACHED();
+  return false;
+}
+
 // Returns true this navigation should be treated as a reload. For e.g.
 // navigating to the last committed url via the address bar or clicking on a
 // link which results in a navigation to the last committed or pending
@@ -215,6 +234,34 @@
   return true;
 }
 
+bool IsValidURLForNavigation(bool is_main_frame,
+                             const GURL& virtual_url,
+                             const GURL& dest_url) {
+  // Don't attempt to navigate if the virtual URL is non-empty and invalid.
+  if (is_main_frame && !virtual_url.is_valid() && !virtual_url.is_empty()) {
+    LOG(WARNING) << "Refusing to load for invalid virtual URL: "
+                 << virtual_url.possibly_invalid_spec();
+    return false;
+  }
+
+  // Don't attempt to navigate to non-empty invalid URLs.
+  if (!dest_url.is_valid() && !dest_url.is_empty()) {
+    LOG(WARNING) << "Refusing to load invalid URL: "
+                 << dest_url.possibly_invalid_spec();
+    return false;
+  }
+
+  // The renderer will reject IPC messages with URLs longer than
+  // this limit, so don't attempt to navigate with a longer URL.
+  if (dest_url.spec().size() > url::kMaxURLChars) {
+    LOG(WARNING) << "Refusing to load URL as it exceeds " << url::kMaxURLChars
+                 << " characters.";
+    return false;
+  }
+
+  return true;
+}
+
 // See replaced_navigation_entry_data.h for details: this information is meant
 // to ensure |*output_entry| keeps track of its original URL (landing page in
 // case of server redirects) as it gets replaced (e.g. history.replaceState()),
@@ -286,6 +333,72 @@
   }
 }
 
+// Adjusts the original input URL if needed, to get the URL to actually load and
+// the virtual URL, which may differ.
+void RewriteUrlForNavigation(const GURL& original_url,
+                             BrowserContext* browser_context,
+                             GURL* url_to_load,
+                             GURL* virtual_url,
+                             bool* reverse_on_redirect) {
+  // Fix up the given URL before letting it be rewritten, so that any minor
+  // cleanup (e.g., removing leading dots) will not lead to a virtual URL.
+  *virtual_url = original_url;
+  BrowserURLHandlerImpl::GetInstance()->FixupURLBeforeRewrite(virtual_url,
+                                                              browser_context);
+
+  // Allow the browser URL handler to rewrite the URL. This will, for example,
+  // remove "view-source:" from the beginning of the URL to get the URL that
+  // will actually be loaded. This real URL won't be shown to the user, just
+  // used internally.
+  *url_to_load = *virtual_url;
+  BrowserURLHandlerImpl::GetInstance()->RewriteURLIfNecessary(
+      url_to_load, browser_context, reverse_on_redirect);
+}
+
+#if DCHECK_IS_ON()
+// Helper sanity check function used in debug mode.
+void ValidateRequestMatchesEntry(NavigationRequest* request,
+                                 NavigationEntryImpl* entry) {
+  if (request->frame_tree_node()->IsMainFrame()) {
+    DCHECK_EQ(request->browser_initiated(), !entry->is_renderer_initiated());
+    DCHECK(ui::PageTransitionTypeIncludingQualifiersIs(
+        request->common_params().transition, entry->GetTransitionType()));
+  }
+  DCHECK_EQ(request->common_params().should_replace_current_entry,
+            entry->should_replace_entry());
+  DCHECK_EQ(request->request_params().should_clear_history_list,
+            entry->should_clear_history_list());
+  DCHECK_EQ(request->common_params().has_user_gesture,
+            entry->has_user_gesture());
+  DCHECK_EQ(request->common_params().base_url_for_data_url,
+            entry->GetBaseURLForDataURL());
+  DCHECK_EQ(request->request_params().can_load_local_resources,
+            entry->GetCanLoadLocalResources());
+  DCHECK_EQ(request->common_params().started_from_context_menu,
+            entry->has_started_from_context_menu());
+
+  FrameNavigationEntry* frame_entry =
+      entry->GetFrameEntry(request->frame_tree_node());
+  if (!frame_entry) {
+    NOTREACHED();
+    return;
+  }
+
+  DCHECK_EQ(request->common_params().url, frame_entry->url());
+  DCHECK_EQ(request->common_params().method, frame_entry->method());
+
+  size_t redirect_size = request->request_params().redirects.size();
+  if (redirect_size == frame_entry->redirect_chain().size()) {
+    for (size_t i = 0; i < redirect_size; ++i) {
+      DCHECK_EQ(request->request_params().redirects[i],
+                frame_entry->redirect_chain()[i]);
+    }
+  } else {
+    NOTREACHED();
+  }
+}
+#endif  // DCHECK_IS_ON()
+
 }  // namespace
 
 // NavigationControllerImpl ----------------------------------------------------
@@ -309,28 +422,19 @@
     const std::string& extra_headers,
     BrowserContext* browser_context,
     scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory) {
-  // Fix up the given URL before letting it be rewritten, so that any minor
-  // cleanup (e.g., removing leading dots) will not lead to a virtual URL.
-  GURL dest_url(url);
-  BrowserURLHandlerImpl::GetInstance()->FixupURLBeforeRewrite(&dest_url,
-                                                              browser_context);
-
-  // Allow the browser URL handler to rewrite the URL. This will, for example,
-  // remove "view-source:" from the beginning of the URL to get the URL that
-  // will actually be loaded. This real URL won't be shown to the user, just
-  // used internally.
-  GURL loaded_url(dest_url);
+  GURL url_to_load;
+  GURL virtual_url;
   bool reverse_on_redirect = false;
-  BrowserURLHandlerImpl::GetInstance()->RewriteURLIfNecessary(
-      &loaded_url, browser_context, &reverse_on_redirect);
+  RewriteUrlForNavigation(url, browser_context, &url_to_load, &virtual_url,
+                          &reverse_on_redirect);
 
   NavigationEntryImpl* entry = new NavigationEntryImpl(
       nullptr,  // The site instance for tabs is sent on navigation
                 // (WebContents::GetSiteInstance).
-      loaded_url, referrer, base::string16(), transition, is_renderer_initiated,
-      blob_url_loader_factory);
-  entry->SetVirtualURL(dest_url);
-  entry->set_user_typed_url(dest_url);
+      url_to_load, referrer, base::string16(), transition,
+      is_renderer_initiated, blob_url_loader_factory);
+  entry->SetVirtualURL(virtual_url);
+  entry->set_user_typed_url(virtual_url);
   entry->set_update_virtual_url_with_url(reverse_on_redirect);
   entry->set_extra_headers(extra_headers);
   return base::WrapUnique(entry);
@@ -1810,6 +1914,31 @@
     pending_entry_->set_ssl_error(error);
 }
 
+#if defined(OS_ANDROID)
+// static
+bool NavigationControllerImpl::ValidateDataURLAsString(
+    const scoped_refptr<const base::RefCountedString>& data_url_as_string) {
+  if (!data_url_as_string)
+    return false;
+
+  if (data_url_as_string->size() > kMaxLengthOfDataURLString)
+    return false;
+
+  // The number of characters that is enough for validating a data: URI.
+  // From the GURL's POV, the only important part here is scheme, it doesn't
+  // check the actual content. Thus we can take only the prefix of the url, to
+  // avoid unneeded copying of a potentially long string.
+  const size_t kDataUriPrefixMaxLen = 64;
+  GURL data_url(
+      std::string(data_url_as_string->front_as<char>(),
+                  std::min(data_url_as_string->size(), kDataUriPrefixMaxLen)));
+  if (!data_url.is_valid() || !data_url.SchemeIs(url::kDataScheme))
+    return false;
+
+  return true;
+}
+#endif
+
 bool NavigationControllerImpl::StartHistoryNavigationInNewSubframe(
     RenderFrameHostImpl* render_frame_host,
     const GURL& default_url) {
@@ -1843,11 +1972,10 @@
     }
   }
 
-  std::unique_ptr<NavigationRequest> request = CreateNavigationRequest(
+  std::unique_ptr<NavigationRequest> request = CreateNavigationRequestFromEntry(
       render_frame_host->frame_tree_node(), *entry, frame_entry,
       ReloadType::NONE, false /* is_same_document_history_load */,
-      true /* is_history_navigation_in_new_child */, nullptr, nullptr,
-      base::TimeTicks() /* input_start */, WasActivatedOption::kUnknown);
+      true /* is_history_navigation_in_new_child */);
 
   if (!request)
     return false;
@@ -1915,9 +2043,12 @@
   // http://crbug.com/457149
   if (should_replace_current_entry && GetEntryCount() > 0)
     entry->set_should_replace_entry(true);
+
+  bool override_user_agent = false;
   if (GetLastCommittedEntry() &&
       GetLastCommittedEntry()->GetIsOverridingUserAgent()) {
     entry->SetIsOverridingUserAgent(true);
+    override_user_agent = true;
   }
   // TODO(creis): Set user gesture and intent received timestamp on Android.
 
@@ -1934,11 +2065,37 @@
         std::vector<GURL>(), PageState(), method, -1, blob_url_loader_factory);
   }
 
-  std::unique_ptr<NavigationRequest> request = CreateNavigationRequest(
-      render_frame_host->frame_tree_node(), *entry, frame_entry.get(),
-      ReloadType::NONE, false /* is_same_document_history_load */,
-      false /* is_history_navigation_in_new_child */, post_body, nullptr,
-      base::TimeTicks() /* input_start */, WasActivatedOption::kUnknown);
+  LoadURLParams params(url);
+  params.source_site_instance = source_site_instance;
+  params.load_type = method == "POST" ? LOAD_TYPE_HTTP_POST : LOAD_TYPE_DEFAULT;
+  params.transition_type = page_transition;
+  params.frame_tree_node_id =
+      render_frame_host->frame_tree_node()->frame_tree_node_id();
+  params.referrer = referrer;
+  /* params.redirect_chain: skip */
+  params.extra_headers = extra_headers;
+  params.is_renderer_initiated = is_renderer_initiated;
+  params.override_user_agent = UA_OVERRIDE_INHERIT;
+  /* params.base_url_for_data_url: skip */
+  /* params.virtual_url_for_data_url: skip */
+  /* params.data_url_as_string: skip */
+  params.post_data = post_body;
+  params.can_load_local_resources = false;
+  params.should_replace_current_entry = false;
+  /* params.frame_name: skip */
+  // TODO(clamy): See if user gesture should be propagated to this function.
+  params.has_user_gesture = false;
+  params.should_clear_history_list = false;
+  params.started_from_context_menu = false;
+  /* params.navigation_ui_data: skip */
+  /* params.input_start: skip */
+  params.was_activated = WasActivatedOption::kUnknown;
+
+  std::unique_ptr<NavigationRequest> request =
+      CreateNavigationRequestFromLoadParams(
+          render_frame_host->frame_tree_node(), params, override_user_agent,
+          should_replace_current_entry, false /* has_user_gesture */,
+          ReloadType::NONE, *entry, frame_entry.get());
 
   if (!request)
     return;
@@ -2192,11 +2349,10 @@
     // frame to navigate based on names if this were a same document
     // navigation, so we can safely assume this is the different document case.
     std::unique_ptr<NavigationRequest> navigation_request =
-        CreateNavigationRequest(
+        CreateNavigationRequestFromEntry(
             root, *pending_entry_, pending_entry_->GetFrameEntry(root),
             reload_type, false /* is_same_document_history_load */,
-            false /* is_history_navigation_in_new_child */, nullptr, nullptr,
-            base::TimeTicks() /* input_start */, WasActivatedOption::kUnknown);
+            false /* is_history_navigation_in_new_child */);
     if (!navigation_request) {
       // This navigation cannot start (e.g. the URL is invalid), delete the
       // pending NavigationEntry.
@@ -2269,12 +2425,10 @@
             old_item->document_sequence_number() &&
         !frame->current_frame_host()->GetLastCommittedURL().is_empty()) {
       std::unique_ptr<NavigationRequest> navigation_request =
-          CreateNavigationRequest(
+          CreateNavigationRequestFromEntry(
               frame, *pending_entry_, new_item, reload_type,
               true /* is_same_document_history_load */,
-              false /* is_history_navigation_in_new_child */, nullptr, nullptr,
-              base::TimeTicks() /* input_start */,
-              WasActivatedOption::kUnknown);
+              false /* is_history_navigation_in_new_child */);
       if (navigation_request) {
         // Only add the request if was properly created. It's possible for the
         // creation to fail in certain cases, e.g. when the URL is invalid.
@@ -2297,12 +2451,10 @@
       return;
     } else {
       std::unique_ptr<NavigationRequest> navigation_request =
-          CreateNavigationRequest(
+          CreateNavigationRequestFromEntry(
               frame, *pending_entry_, new_item, reload_type,
               false /* is_same_document_history_load */,
-              false /* is_history_navigation_in_new_child */, nullptr, nullptr,
-              base::TimeTicks() /* input_start */,
-              WasActivatedOption::kUnknown);
+              false /* is_history_navigation_in_new_child */);
       if (navigation_request) {
         // Only add the request if was properly created. It's possible for the
         // creation to fail in certain cases, e.g. when the URL is invalid.
@@ -2335,11 +2487,40 @@
   if (!node)
     node = delegate_->GetFrameTree()->root();
 
+  // Compute overrides to the LoadURLParams for |override_user_agent|,
+  // |should_replace_current_entry| and |has_user_gesture| that will be used
+  // both in the creation of the NavigationEntry and the NavigationRequest.
+  // Ideally, the LoadURLParams themselves would be updated, but since they are
+  // passed as a const reference, this is not possible.
+  // TODO(clamy): When we only create a NavigationRequest, move this to
+  // CreateNavigationRequestFromLoadURLParams.
+  bool override_user_agent = ShouldOverrideUserAgent(params.override_user_agent,
+                                                     GetLastCommittedEntry());
+
+  // Don't allow an entry replacement if there is no entry to replace.
+  // http://crbug.com/457149
+  bool should_replace_current_entry =
+      params.should_replace_current_entry && entries_.size();
+
+  // Always propagate `has_user_gesture` on Android but only when the request
+  // was originated by the renderer on other platforms. This is merely for
+  // backward compatibility as browser process user gestures create confusion in
+  // many tests.
+  bool has_user_gesture = false;
+#if defined(OS_ANDROID)
+  has_user_gesture = params.has_user_gesture;
+#else
+  if (params.is_renderer_initiated)
+    has_user_gesture = params.has_user_gesture;
+#endif
+
   // Javascript URLs should not create NavigationEntries. All other navigations
   // do, including navigations to chrome renderer debug URLs.
   std::unique_ptr<NavigationEntryImpl> entry;
   if (!params.url.SchemeIs(url::kJavaScriptScheme)) {
-    entry = CreateNavigationEntryFromLoadParams(node, params);
+    entry = CreateNavigationEntryFromLoadParams(
+        node, params, override_user_agent, should_replace_current_entry,
+        has_user_gesture);
     DiscardPendingEntry(false);
     SetPendingEntry(std::move(entry));
   }
@@ -2379,15 +2560,12 @@
   // navigation_ui_data should only be present for main frame navigations.
   DCHECK(node->IsMainFrame() || !params.navigation_ui_data);
 
-  // TODO(clamy): Create the NavigationRequest directly from the LoadURLParams
-  // instead of relying on the NavigationEntry.
   DCHECK(pending_entry_);
-  std::unique_ptr<NavigationRequest> request = CreateNavigationRequest(
-      node, *pending_entry_, pending_entry_->GetFrameEntry(node), reload_type,
-      false /* is_same_document_history_load */,
-      false /* is_history_navigation_in_new_child */, nullptr,
-      params.navigation_ui_data ? params.navigation_ui_data->Clone() : nullptr,
-      params.input_start, params.was_activated);
+  std::unique_ptr<NavigationRequest> request =
+      CreateNavigationRequestFromLoadParams(
+          node, params, override_user_agent, should_replace_current_entry,
+          has_user_gesture, reload_type, *pending_entry_,
+          pending_entry_->GetFrameEntry(node));
 
   // If the navigation couldn't start, return immediately and discard the
   // pending NavigationEntry.
@@ -2396,6 +2574,11 @@
     return;
   }
 
+#if DCHECK_IS_ON()
+  // Safety check that NavigationRequest and NavigationEntry match.
+  ValidateRequestMatchesEntry(request.get(), pending_entry_);
+#endif
+
   // If an interstitial page is showing, the previous renderer is blocked and
   // cannot make new requests.  Unblock (and disable) it to allow this
   // navigation to succeed.  The interstitial will stay visible until the
@@ -2435,7 +2618,10 @@
 std::unique_ptr<NavigationEntryImpl>
 NavigationControllerImpl::CreateNavigationEntryFromLoadParams(
     FrameTreeNode* node,
-    const LoadURLParams& params) {
+    const LoadURLParams& params,
+    bool override_user_agent,
+    bool should_replace_current_entry,
+    bool has_user_gesture) {
   // Browser initiated navigations might not have a blob_url_loader_factory set
   // in params even if the navigation is to a blob URL. If that happens, lookup
   // the correct url loader factory to use here.
@@ -2477,39 +2663,10 @@
 
   // Set the FTN ID (only used in non-site-per-process, for tests).
   entry->set_frame_tree_node_id(node->frame_tree_node_id());
-  // Don't allow an entry replacement if there is no entry to replace.
-  // http://crbug.com/457149
-  if (params.should_replace_current_entry && entries_.size() > 0)
-    entry->set_should_replace_entry(true);
+  entry->set_should_replace_entry(should_replace_current_entry);
   entry->set_should_clear_history_list(params.should_clear_history_list);
-
-  bool override = false;
-  switch (params.override_user_agent) {
-    case UA_OVERRIDE_INHERIT:
-      override = ShouldKeepOverride(GetLastCommittedEntry());
-      break;
-    case UA_OVERRIDE_TRUE:
-      override = true;
-      break;
-    case UA_OVERRIDE_FALSE:
-      override = false;
-      break;
-    default:
-      NOTREACHED();
-      break;
-  }
-  entry->SetIsOverridingUserAgent(override);
-
-// Always propagate `has_user_gesture` on Android but only when the request
-// was originated by the renderer on other platforms. This is merely for
-// backward compatibility as browser process user gestures create confusion in
-// many tests.
-#if defined(OS_ANDROID)
-  entry->set_has_user_gesture(params.has_user_gesture);
-#else
-  if (params.is_renderer_initiated)
-    entry->set_has_user_gesture(params.has_user_gesture);
-#endif
+  entry->SetIsOverridingUserAgent(override_user_agent);
+  entry->set_has_user_gesture(has_user_gesture);
 
   switch (params.load_type) {
     case LOAD_TYPE_DEFAULT:
@@ -2541,17 +2698,121 @@
 }
 
 std::unique_ptr<NavigationRequest>
-NavigationControllerImpl::CreateNavigationRequest(
+NavigationControllerImpl::CreateNavigationRequestFromLoadParams(
+    FrameTreeNode* node,
+    const LoadURLParams& params,
+    bool override_user_agent,
+    bool should_replace_current_entry,
+    bool has_user_gesture,
+    ReloadType reload_type,
+    const NavigationEntryImpl& entry,
+    FrameNavigationEntry* frame_entry) {
+  DCHECK_EQ(-1, GetIndexOfEntry(&entry));
+  GURL url_to_load;
+  GURL virtual_url;
+  bool reverse_on_redirect = false;
+  RewriteUrlForNavigation(params.url, browser_context_, &url_to_load,
+                          &virtual_url, &reverse_on_redirect);
+
+  // For DATA loads, override the virtual URL.
+  if (params.load_type == LOAD_TYPE_DATA)
+    virtual_url = params.virtual_url_for_data_url;
+
+  if (virtual_url.is_empty())
+    virtual_url = url_to_load;
+
+  CHECK(!node->IsMainFrame() || virtual_url == entry.GetVirtualURL());
+  CHECK_EQ(url_to_load, frame_entry->url());
+
+  // TODO(clamy): In order to remove the pending NavigationEntry, |virtual_url|
+  // and |reverse_on_redirect| should be stored in the NavigationRequest.
+
+  if (!IsValidURLForNavigation(node->IsMainFrame(), virtual_url, url_to_load))
+    return nullptr;
+
+  // Determine if Previews should be used for the navigation.
+  PreviewsState previews_state = PREVIEWS_UNSPECIFIED;
+  if (!node->IsMainFrame()) {
+    // For subframes, use the state of the top-level frame.
+    previews_state = node->frame_tree()
+                         ->root()
+                         ->current_frame_host()
+                         ->last_navigation_previews_state();
+  }
+
+  // Give the delegate an opportunity to adjust the previews state.
+  if (delegate_)
+    delegate_->AdjustPreviewsStateForNavigation(&previews_state);
+
+  // This will be used to set the Navigation Timing API navigationStart
+  // parameter for browser navigations in new tabs (intents, tabs opened through
+  // "Open link in new tab"). If the navigation must wait on the current
+  // RenderFrameHost to execute its BeforeUnload event, the navigation start
+  // will be updated when the BeforeUnload ack is received.
+  base::TimeTicks navigation_start = base::TimeTicks::Now();
+
+  FrameMsg_Navigate_Type::Value navigation_type =
+      GetNavigationType(node->current_url(),  // old_url
+                        url_to_load,          // new_url
+                        reload_type,          // reload_type
+                        entry,                // entry
+                        *frame_entry,         // frame_entry
+                        false);               // is_same_document_history_load
+
+  // Create the NavigationParams based on |params|.
+
+  bool is_view_source_mode = virtual_url.SchemeIs(kViewSourceScheme);
+  const GURL& history_url_for_data_url =
+      params.base_url_for_data_url.is_empty() ? GURL() : virtual_url;
+  CommonNavigationParams common_params(
+      url_to_load, params.referrer, params.transition_type, navigation_type,
+      !is_view_source_mode, should_replace_current_entry,
+      params.base_url_for_data_url, history_url_for_data_url, previews_state,
+      navigation_start,
+      params.load_type == LOAD_TYPE_HTTP_POST ? "POST" : "GET",
+      params.post_data, base::Optional<SourceLocation>(),
+      params.started_from_context_menu, has_user_gesture, InitiatorCSPInfo(),
+      params.input_start);
+
+  RequestNavigationParams request_params(
+      override_user_agent, params.redirect_chain, common_params.url,
+      common_params.method, params.can_load_local_resources,
+      frame_entry->page_state(), entry.GetUniqueID(),
+      false /* is_history_navigation_in_new_child */,
+      entry.GetSubframeUniqueNames(node), true /* intended_as_new_entry */,
+      -1 /* pending_history_list_offset */,
+      params.should_clear_history_list ? -1 : GetLastCommittedEntryIndex(),
+      params.should_clear_history_list ? 0 : GetEntryCount(),
+      is_view_source_mode, params.should_clear_history_list);
+#if defined(OS_ANDROID)
+  if (ValidateDataURLAsString(params.data_url_as_string)) {
+    request_params.data_url_as_string = params.data_url_as_string->data();
+  }
+#endif
+
+  request_params.was_activated = params.was_activated;
+
+  // A form submission may happen here if the navigation is a renderer-initiated
+  // form submission that took the OpenURL path.
+  scoped_refptr<network::ResourceRequestBody> request_body = params.post_data;
+
+  // extra_headers in params are \n separated; NavigationRequests want \r\n.
+  std::string extra_headers_crlf;
+  base::ReplaceChars(params.extra_headers, "\n", "\r\n", &extra_headers_crlf);
+  return NavigationRequest::CreateBrowserInitiated(
+      node, common_params, request_params, !params.is_renderer_initiated,
+      extra_headers_crlf, *frame_entry, entry, request_body,
+      params.navigation_ui_data ? params.navigation_ui_data->Clone() : nullptr);
+}
+
+std::unique_ptr<NavigationRequest>
+NavigationControllerImpl::CreateNavigationRequestFromEntry(
     FrameTreeNode* frame_tree_node,
     const NavigationEntryImpl& entry,
     FrameNavigationEntry* frame_entry,
     ReloadType reload_type,
     bool is_same_document_history_load,
-    bool is_history_navigation_in_new_child,
-    const scoped_refptr<network::ResourceRequestBody>& post_body,
-    std::unique_ptr<NavigationUIData> navigation_ui_data,
-    base::TimeTicks input_start,
-    WasActivatedOption was_activated) {
+    bool is_history_navigation_in_new_child) {
   GURL dest_url = frame_entry->url();
   Referrer dest_referrer = frame_entry->referrer();
   if (reload_type == ReloadType::ORIGINAL_REQUEST_URL &&
@@ -2564,28 +2825,8 @@
     dest_referrer = Referrer();
   }
 
-  // Don't attempt to navigate if the virtual URL is non-empty and invalid.
-  if (frame_tree_node->IsMainFrame()) {
-    const GURL& virtual_url = entry.GetVirtualURL();
-    if (!virtual_url.is_valid() && !virtual_url.is_empty()) {
-      LOG(WARNING) << "Refusing to load for invalid virtual URL: "
-                   << virtual_url.possibly_invalid_spec();
-      return nullptr;
-    }
-  }
-
-  // Don't attempt to navigate to non-empty invalid URLs.
-  if (!dest_url.is_valid() && !dest_url.is_empty()) {
-    LOG(WARNING) << "Refusing to load invalid URL: "
-                 << dest_url.possibly_invalid_spec();
-    return nullptr;
-  }
-
-  // The renderer will reject IPC messages with URLs longer than
-  // this limit, so don't attempt to navigate with a longer URL.
-  if (dest_url.spec().size() > url::kMaxURLChars) {
-    LOG(WARNING) << "Refusing to load URL as it exceeds " << url::kMaxURLChars
-                 << " characters.";
+  if (!IsValidURLForNavigation(frame_tree_node->IsMainFrame(),
+                               entry.GetVirtualURL(), dest_url)) {
     return nullptr;
   }
 
@@ -2609,9 +2850,6 @@
   // RenderFrameHost to execute its BeforeUnload event, the navigation start
   // will be updated when the BeforeUnload ack is received.
   base::TimeTicks navigation_start = base::TimeTicks::Now();
-  TRACE_EVENT_INSTANT_WITH_TIMESTAMP0(
-      "navigation,rail", "NavigationTiming navigationStart",
-      TRACE_EVENT_SCOPE_GLOBAL, navigation_start);
 
   FrameMsg_Navigate_Type::Value navigation_type = GetNavigationType(
       frame_tree_node->current_url(),  // old_url
@@ -2620,11 +2858,41 @@
       entry,                           // entry
       *frame_entry,                    // frame_entry
       is_same_document_history_load);  // is_same_document_history_load
+
+  // A form submission may happen here if the navigation is a
+  // back/forward/reload navigation that does a form resubmission.
+  scoped_refptr<network::ResourceRequestBody> request_body;
+  std::string post_content_type;
+  if (frame_entry->method() == "POST") {
+    request_body = frame_entry->GetPostData(&post_content_type);
+    // Might have a LF at end.
+    post_content_type =
+        base::TrimWhitespaceASCII(post_content_type, base::TRIM_ALL)
+            .as_string();
+  }
+
+  // Create the NavigationParams based on |entry| and |frame_entry|.
+  CommonNavigationParams common_params = entry.ConstructCommonNavigationParams(
+      *frame_entry, request_body, dest_url, dest_referrer, navigation_type,
+      previews_state, navigation_start, base::TimeTicks() /* input_start */);
+
+  // TODO(clamy): |intended_as_new_entry| below should always be false once
+  // Reload no longer leads to this being called for a pending NavigationEntry
+  // of index -1.
+  RequestNavigationParams request_params =
+      entry.ConstructRequestNavigationParams(
+          *frame_entry, common_params.url, common_params.method,
+          is_history_navigation_in_new_child,
+          entry.GetSubframeUniqueNames(frame_tree_node),
+          GetPendingEntryIndex() == -1 /* intended_as_new_entry */,
+          GetIndexOfEntry(&entry), GetLastCommittedEntryIndex(),
+          GetEntryCount());
+  request_params.post_content_type = post_content_type;
+
   return NavigationRequest::CreateBrowserInitiated(
-      frame_tree_node, dest_url, dest_referrer, *frame_entry, entry,
-      navigation_type, previews_state, is_same_document_history_load,
-      is_history_navigation_in_new_child, post_body, navigation_start, this,
-      std::move(navigation_ui_data), input_start, was_activated);
+      frame_tree_node, common_params, request_params,
+      !entry.is_renderer_initiated(), entry.extra_headers(), *frame_entry,
+      entry, request_body, nullptr /* navigation_ui_data */);
 }
 
 void NavigationControllerImpl::NotifyNavigationEntryCommitted(
diff --git a/content/browser/frame_host/navigation_controller_impl.h b/content/browser/frame_host/navigation_controller_impl.h
index b4ef12e..afaa65e 100644
--- a/content/browser/frame_host/navigation_controller_impl.h
+++ b/content/browser/frame_host/navigation_controller_impl.h
@@ -232,6 +232,13 @@
   // navigation failed due to an SSL error.
   void SetPendingNavigationSSLError(bool error);
 
+// Returns true if the string corresponds to a valid data URL, false
+// otherwise.
+#if defined(OS_ANDROID)
+  static bool ValidateDataURLAsString(
+      const scoped_refptr<const base::RefCountedString>& data_url_as_string);
+#endif
+
  private:
   friend class RestoreHelper;
 
@@ -279,24 +286,47 @@
 
   // Creates and returns a NavigationEntry based on |load_params| for a
   // navigation in |node|.
+  // |override_user_agent|, |should_replace_current_entry| and
+  // |has_user_gesture| will override the values from |load_params|. The same
+  // values should be passed to CreateNavigationRequestFromLoadParams.
   std::unique_ptr<NavigationEntryImpl> CreateNavigationEntryFromLoadParams(
       FrameTreeNode* node,
-      const LoadURLParams& load_params);
+      const LoadURLParams& load_params,
+      bool override_user_agent,
+      bool should_replace_current_entry,
+      bool has_user_gesture);
 
-  // Creates and returns a NavigationRequest based on the provided parameters.
+  // Creates and returns a NavigationRequest based on |load_params| for a
+  // new navigation in |node|.
   // Will return nullptr if the parameters are invalid and the navigation cannot
   // start.
-  std::unique_ptr<NavigationRequest> CreateNavigationRequest(
+  // |override_user_agent|, |should_replace_current_entry| and
+  // |has_user_gesture| will override the values from |load_params|. The same
+  // values should be passed to CreateNavigationEntryFromLoadParams.
+  // TODO(clamy): Remove the dependency on NavigationEntry and
+  // FrameNavigationEntry.
+  std::unique_ptr<NavigationRequest> CreateNavigationRequestFromLoadParams(
+      FrameTreeNode* node,
+      const LoadURLParams& load_params,
+      bool override_user_agent,
+      bool should_replace_current_entry,
+      bool has_user_gesture,
+      ReloadType reload_type,
+      const NavigationEntryImpl& entry,
+      FrameNavigationEntry* frame_entry);
+
+  // Creates and returns a NavigationRequest for a navigation to |entry|. Will
+  // return nullptr if the parameters are invalid and the navigation cannot
+  // start.
+  // TODO(clamy): Ensure this is only called for navigations to existing
+  // NavigationEntries.
+  std::unique_ptr<NavigationRequest> CreateNavigationRequestFromEntry(
       FrameTreeNode* frame_tree_node,
       const NavigationEntryImpl& entry,
       FrameNavigationEntry* frame_entry,
       ReloadType reload_type,
       bool is_same_document_history_load,
-      bool is_history_navigation_in_new_child,
-      const scoped_refptr<network::ResourceRequestBody>& post_body,
-      std::unique_ptr<NavigationUIData> navigation_ui_data,
-      base::TimeTicks input_start,
-      WasActivatedOption was_activated);
+      bool is_history_navigation_in_new_child);
 
   // Returns whether there is a pending NavigationEntry whose unique ID matches
   // the given NavigationHandle's pending_nav_entry_id.
diff --git a/content/browser/frame_host/navigation_entry_impl.cc b/content/browser/frame_host/navigation_entry_impl.cc
index bece944..47db0eb 100644
--- a/content/browser/frame_host/navigation_entry_impl.cc
+++ b/content/browser/frame_host/navigation_entry_impl.cc
@@ -20,6 +20,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "components/url_formatter/url_formatter.h"
+#include "content/browser/frame_host/navigation_controller_impl.h"
 #include "content/common/content_constants_internal.h"
 #include "content/common/navigation_params.h"
 #include "content/common/page_state_serialization.h"
@@ -732,18 +733,8 @@
       intended_as_new_entry, pending_offset_to_send, current_offset_to_send,
       current_length_to_send, IsViewSourceMode(), should_clear_history_list());
 #if defined(OS_ANDROID)
-  if (GetDataURLAsString() &&
-      GetDataURLAsString()->size() <= kMaxLengthOfDataURLString) {
-    // The number of characters that is enough for validating a data: URI.  From
-    // the GURL's POV, the only important part here is scheme, it doesn't check
-    // the actual content. Thus we can take only the prefix of the url, to avoid
-    // unneeded copying of a potentially long string.
-    const size_t kDataUriPrefixMaxLen = 64;
-    GURL data_url(std::string(
-        GetDataURLAsString()->front_as<char>(),
-        std::min(GetDataURLAsString()->size(), kDataUriPrefixMaxLen)));
-    if (data_url.is_valid() && data_url.SchemeIs(url::kDataScheme))
-      request_params.data_url_as_string = GetDataURLAsString()->data();
+  if (NavigationControllerImpl::ValidateDataURLAsString(GetDataURLAsString())) {
+    request_params.data_url_as_string = GetDataURLAsString()->data();
   }
 #endif
   return request_params;
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index b86fa9fc..d920ead 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -281,39 +281,17 @@
 // static
 std::unique_ptr<NavigationRequest> NavigationRequest::CreateBrowserInitiated(
     FrameTreeNode* frame_tree_node,
-    const GURL& dest_url,
-    const Referrer& dest_referrer,
+    const CommonNavigationParams& common_params,
+    const RequestNavigationParams& request_params,
+    bool browser_initiated,
+    const std::string& extra_headers,
     const FrameNavigationEntry& frame_entry,
     const NavigationEntryImpl& entry,
-    FrameMsg_Navigate_Type::Value navigation_type,
-    PreviewsState previews_state,
-    bool is_same_document_history_load,
-    bool is_history_navigation_in_new_child,
     const scoped_refptr<network::ResourceRequestBody>& post_body,
-    base::TimeTicks navigation_start,
-    NavigationControllerImpl* controller,
-    std::unique_ptr<NavigationUIData> navigation_ui_data,
-    base::TimeTicks input_start,
-    WasActivatedOption was_activated) {
-  // A form submission happens either because the navigation is a
-  // renderer-initiated form submission that took the OpenURL path or a
-  // back/forward/reload navigation the does a form resubmission.
-  scoped_refptr<network::ResourceRequestBody> request_body;
-  std::string post_content_type;
-  if (post_body) {
-    // Standard form submission from the renderer.
-    request_body = post_body;
-  } else if (frame_entry.method() == "POST") {
-    // Form resubmission during a back/forward/reload navigation.
-    request_body = frame_entry.GetPostData(&post_content_type);
-    // Might have a LF at end.
-    post_content_type =
-        base::TrimWhitespaceASCII(post_content_type, base::TRIM_ALL)
-            .as_string();
-  }
+    std::unique_ptr<NavigationUIData> navigation_ui_data) {
   // TODO(arthursonzogni): Form submission with the "GET" method is possible.
   // This is not currently handled here.
-  bool is_form_submission = !!request_body;
+  bool is_form_submission = !!post_body;
 
   base::Optional<url::Origin> initiator =
       frame_tree_node->IsMainFrame()
@@ -321,31 +299,10 @@
           : base::Optional<url::Origin>(
                 frame_tree_node->frame_tree()->root()->current_origin());
 
-  // While the navigation was started via the LoadURL path it may have come from
-  // the renderer in the first place as part of OpenURL.
-  bool browser_initiated = !entry.is_renderer_initiated();
-
-  CommonNavigationParams common_params = entry.ConstructCommonNavigationParams(
-      frame_entry, request_body, dest_url, dest_referrer, navigation_type,
-      previews_state, navigation_start, input_start);
-
-  RequestNavigationParams request_params =
-      entry.ConstructRequestNavigationParams(
-          frame_entry, common_params.url, common_params.method,
-          is_history_navigation_in_new_child,
-          entry.GetSubframeUniqueNames(frame_tree_node),
-          controller->GetPendingEntryIndex() == -1,
-          controller->GetIndexOfEntry(&entry),
-          controller->GetLastCommittedEntryIndex(),
-          controller->GetEntryCount());
-  request_params.post_content_type = post_content_type;
-  request_params.was_activated = was_activated;
-
   std::unique_ptr<NavigationRequest> navigation_request(new NavigationRequest(
       frame_tree_node, common_params,
       mojom::BeginNavigationParams::New(
-          entry.extra_headers(), net::LOAD_NORMAL,
-          false /* skip_service_worker */,
+          extra_headers, net::LOAD_NORMAL, false /* skip_service_worker */,
           blink::mojom::RequestContextType::LOCATION,
           blink::WebMixedContentContextType::kBlockable, is_form_submission,
           GURL() /* searchable_form_url */,
diff --git a/content/browser/frame_host/navigation_request.h b/content/browser/frame_host/navigation_request.h
index 7d37652..233111e 100644
--- a/content/browser/frame_host/navigation_request.h
+++ b/content/browser/frame_host/navigation_request.h
@@ -32,7 +32,6 @@
 
 class FrameNavigationEntry;
 class FrameTreeNode;
-class NavigationControllerImpl;
 class NavigationHandleImpl;
 class NavigationURLLoader;
 class NavigationData;
@@ -80,22 +79,20 @@
   };
 
   // Creates a request for a browser-intiated navigation.
+  // Note: this is sometimes called for renderer-initiated navigations going
+  // through the OpenURL path. |browser_initiated| should be false in that case.
+  // TODO(clamy): Rename this function and consider merging it with
+  // CreateRendererInitiated.
   static std::unique_ptr<NavigationRequest> CreateBrowserInitiated(
       FrameTreeNode* frame_tree_node,
-      const GURL& dest_url,
-      const Referrer& dest_referrer,
+      const CommonNavigationParams& common_params,
+      const RequestNavigationParams& request_params,
+      bool browser_initiated,
+      const std::string& extra_headers,
       const FrameNavigationEntry& frame_entry,
       const NavigationEntryImpl& entry,
-      FrameMsg_Navigate_Type::Value navigation_type,
-      PreviewsState previews_state,
-      bool is_same_document_history_load,
-      bool is_history_navigation_in_new_child,
       const scoped_refptr<network::ResourceRequestBody>& post_body,
-      base::TimeTicks navigation_start,
-      NavigationControllerImpl* controller,
-      std::unique_ptr<NavigationUIData> navigation_ui_data,
-      base::TimeTicks input_start,
-      WasActivatedOption was_activated);
+      std::unique_ptr<NavigationUIData> navigation_ui_data);
 
   // Creates a request for a renderer-intiated navigation.
   // Note: |body| is sent to the IO thread when calling BeginNavigation, and
diff --git a/content/browser/frame_host/navigator_impl.cc b/content/browser/frame_host/navigator_impl.cc
index 7d7544d..52771f1 100644
--- a/content/browser/frame_host/navigator_impl.cc
+++ b/content/browser/frame_host/navigator_impl.cc
@@ -324,6 +324,9 @@
                              ReloadType reload_type,
                              RestoreType restore_type) {
   TRACE_EVENT0("browser,navigation", "NavigatorImpl::Navigate");
+  TRACE_EVENT_INSTANT_WITH_TIMESTAMP0(
+      "navigation,rail", "NavigationTiming navigationStart",
+      TRACE_EVENT_SCOPE_GLOBAL, request->common_params().navigation_start);
 
   const GURL& dest_url = request->common_params().url;
   FrameTreeNode* frame_tree_node = request->frame_tree_node();
diff --git a/content/browser/frame_host/render_frame_host_manager_unittest.cc b/content/browser/frame_host/render_frame_host_manager_unittest.cc
index a5e33ca..9dbcbb9 100644
--- a/content/browser/frame_host/render_frame_host_manager_unittest.cc
+++ b/content/browser/frame_host/render_frame_host_manager_unittest.cc
@@ -436,6 +436,8 @@
                                        const NavigationEntryImpl& entry) {
     // Tests currently only navigate using main frame FrameNavigationEntries.
     FrameNavigationEntry* frame_entry = entry.root_node()->frame_entry.get();
+    FrameTreeNode* frame_tree_node =
+        manager->current_frame_host()->frame_tree_node();
     NavigationControllerImpl* controller =
         static_cast<NavigationControllerImpl*>(manager->current_frame_host()
                                                    ->frame_tree_node()
@@ -445,13 +447,37 @@
         entry.restore_type() == RestoreType::NONE
             ? FrameMsg_Navigate_Type::DIFFERENT_DOCUMENT
             : FrameMsg_Navigate_Type::RESTORE;
+    scoped_refptr<network::ResourceRequestBody> request_body;
+    std::string post_content_type;
+    if (frame_entry->method() == "POST") {
+      request_body = frame_entry->GetPostData(&post_content_type);
+      // Might have a LF at end.
+      post_content_type =
+          base::TrimWhitespaceASCII(post_content_type, base::TRIM_ALL)
+              .as_string();
+    }
+
+    CommonNavigationParams common_params =
+        entry.ConstructCommonNavigationParams(
+            *frame_entry, request_body, frame_entry->url(),
+            frame_entry->referrer(), navigate_type, PREVIEWS_UNSPECIFIED,
+            base::TimeTicks::Now(), base::TimeTicks::Now());
+    RequestNavigationParams request_params =
+        entry.ConstructRequestNavigationParams(
+            *frame_entry, common_params.url, common_params.method, false,
+            entry.GetSubframeUniqueNames(frame_tree_node),
+            controller->GetPendingEntryIndex() ==
+                -1 /* intended_as_new_entry */,
+            controller->GetIndexOfEntry(&entry),
+            controller->GetLastCommittedEntryIndex(),
+            controller->GetEntryCount());
+    request_params.post_content_type = post_content_type;
+
     std::unique_ptr<NavigationRequest> navigation_request =
         NavigationRequest::CreateBrowserInitiated(
-            manager->frame_tree_node_, frame_entry->url(),
-            frame_entry->referrer(), *frame_entry, entry, navigate_type,
-            PREVIEWS_UNSPECIFIED, false, false, nullptr, base::TimeTicks::Now(),
-            controller, nullptr, base::TimeTicks(),
-            WasActivatedOption::kUnknown);
+            frame_tree_node, common_params, request_params,
+            !entry.is_renderer_initiated(), entry.extra_headers(), *frame_entry,
+            entry, request_body, nullptr /* navigation_ui_data */);
 
     // Simulates request creation that triggers the 1st internal call to
     // GetFrameHostForNavigation.
@@ -2818,14 +2844,27 @@
       ui::PAGE_TRANSITION_TYPED, false /* is_renderer_init */,
       nullptr /* blob_url_loader_factory */);
   FrameNavigationEntry* frame_entry = entry.root_node()->frame_entry.get();
+  FrameTreeNode* frame_tree_node =
+      manager->current_frame_host()->frame_tree_node();
+  CommonNavigationParams common_params = entry.ConstructCommonNavigationParams(
+      *frame_entry, nullptr, frame_entry->url(), frame_entry->referrer(),
+      FrameMsg_Navigate_Type::DIFFERENT_DOCUMENT, PREVIEWS_UNSPECIFIED,
+      base::TimeTicks::Now(), base::TimeTicks::Now());
+  RequestNavigationParams request_params =
+      entry.ConstructRequestNavigationParams(
+          *frame_entry, common_params.url, common_params.method, false,
+          entry.GetSubframeUniqueNames(frame_tree_node),
+          controller().GetPendingEntryIndex() == -1 /* intended_as_new_entry */,
+          static_cast<NavigationControllerImpl&>(controller())
+              .GetIndexOfEntry(&entry),
+          controller().GetLastCommittedEntryIndex(),
+          controller().GetEntryCount());
+
   std::unique_ptr<NavigationRequest> navigation_request =
       NavigationRequest::CreateBrowserInitiated(
-          contents()->GetFrameTree()->root(), frame_entry->url(),
-          frame_entry->referrer(), *frame_entry, entry,
-          FrameMsg_Navigate_Type::DIFFERENT_DOCUMENT, PREVIEWS_UNSPECIFIED,
-          false, false, nullptr, base::TimeTicks::Now(),
-          static_cast<NavigationControllerImpl*>(&controller()), nullptr,
-          base::TimeTicks(), WasActivatedOption::kUnknown);
+          frame_tree_node, common_params, request_params,
+          !entry.is_renderer_initiated(), entry.extra_headers(), *frame_entry,
+          entry, nullptr /* request_body */, nullptr /* navigation_ui_data */);
   manager->DidCreateNavigationRequest(navigation_request.get());
 
   // As the initial RenderFrame was not live, the new RenderFrameHost should be
@@ -2881,14 +2920,27 @@
       ui::PAGE_TRANSITION_TYPED, false /* is_renderer_init */,
       nullptr /* blob_url_loader_factory */);
   FrameNavigationEntry* frame_entry = entry.root_node()->frame_entry.get();
+  FrameTreeNode* frame_tree_node =
+      manager->current_frame_host()->frame_tree_node();
+  CommonNavigationParams common_params = entry.ConstructCommonNavigationParams(
+      *frame_entry, nullptr, frame_entry->url(), frame_entry->referrer(),
+      FrameMsg_Navigate_Type::DIFFERENT_DOCUMENT, PREVIEWS_UNSPECIFIED,
+      base::TimeTicks::Now(), base::TimeTicks::Now());
+  RequestNavigationParams request_params =
+      entry.ConstructRequestNavigationParams(
+          *frame_entry, common_params.url, common_params.method, false,
+          entry.GetSubframeUniqueNames(frame_tree_node),
+          controller().GetPendingEntryIndex() == -1 /* intended_as_new_entry */,
+          static_cast<NavigationControllerImpl&>(controller())
+              .GetIndexOfEntry(&entry),
+          controller().GetLastCommittedEntryIndex(),
+          controller().GetEntryCount());
+
   std::unique_ptr<NavigationRequest> navigation_request =
       NavigationRequest::CreateBrowserInitiated(
-          contents()->GetFrameTree()->root(), frame_entry->url(),
-          frame_entry->referrer(), *frame_entry, entry,
-          FrameMsg_Navigate_Type::DIFFERENT_DOCUMENT, PREVIEWS_UNSPECIFIED,
-          false, false, nullptr, base::TimeTicks::Now(),
-          static_cast<NavigationControllerImpl*>(&controller()), nullptr,
-          base::TimeTicks(), WasActivatedOption::kUnknown);
+          frame_tree_node, common_params, request_params,
+          !entry.is_renderer_initiated(), entry.extra_headers(), *frame_entry,
+          entry, nullptr /* request_body */, nullptr /* navigation_ui_data */);
   manager->DidCreateNavigationRequest(navigation_request.get());
 
   // The current WebUI should still be in place and the pending WebUI should be
@@ -2941,14 +2993,27 @@
       ui::PAGE_TRANSITION_TYPED, false /* is_renderer_init */,
       nullptr /* blob_url_loader_factory */);
   FrameNavigationEntry* frame_entry = entry.root_node()->frame_entry.get();
+  FrameTreeNode* frame_tree_node =
+      manager->current_frame_host()->frame_tree_node();
+  CommonNavigationParams common_params = entry.ConstructCommonNavigationParams(
+      *frame_entry, nullptr, frame_entry->url(), frame_entry->referrer(),
+      FrameMsg_Navigate_Type::DIFFERENT_DOCUMENT, PREVIEWS_UNSPECIFIED,
+      base::TimeTicks::Now(), base::TimeTicks::Now());
+  RequestNavigationParams request_params =
+      entry.ConstructRequestNavigationParams(
+          *frame_entry, common_params.url, common_params.method, false,
+          entry.GetSubframeUniqueNames(frame_tree_node),
+          controller().GetPendingEntryIndex() == -1 /* intended_as_new_entry */,
+          static_cast<NavigationControllerImpl&>(controller())
+              .GetIndexOfEntry(&entry),
+          controller().GetLastCommittedEntryIndex(),
+          controller().GetEntryCount());
+
   std::unique_ptr<NavigationRequest> navigation_request =
       NavigationRequest::CreateBrowserInitiated(
-          contents()->GetFrameTree()->root(), frame_entry->url(),
-          frame_entry->referrer(), *frame_entry, entry,
-          FrameMsg_Navigate_Type::DIFFERENT_DOCUMENT, PREVIEWS_UNSPECIFIED,
-          false, false, nullptr, base::TimeTicks::Now(),
-          static_cast<NavigationControllerImpl*>(&controller()), nullptr,
-          base::TimeTicks(), WasActivatedOption::kUnknown);
+          frame_tree_node, common_params, request_params,
+          !entry.is_renderer_initiated(), entry.extra_headers(), *frame_entry,
+          entry, nullptr /* request_body */, nullptr /* navigation_ui_data */);
   manager->DidCreateNavigationRequest(navigation_request.get());
 
   // The current WebUI should still be in place and there should be a new
diff --git a/content/browser/loader/prefetch_url_loader_service.cc b/content/browser/loader/prefetch_url_loader_service.cc
index 497ee22..2a29c367 100644
--- a/content/browser/loader/prefetch_url_loader_service.cc
+++ b/content/browser/loader/prefetch_url_loader_service.cc
@@ -5,6 +5,7 @@
 #include "content/browser/loader/prefetch_url_loader_service.h"
 
 #include "base/feature_list.h"
+#include "base/time/default_tick_clock.h"
 #include "content/browser/loader/prefetch_url_loader.h"
 #include "content/browser/url_loader_factory_getter.h"
 #include "content/public/browser/content_browser_client.h"
@@ -38,7 +39,8 @@
 
 PrefetchURLLoaderService::PrefetchURLLoaderService()
     : signed_exchange_prefetch_metric_recorder_(
-          base::MakeRefCounted<SignedExchangePrefetchMetricRecorder>()) {}
+          base::MakeRefCounted<SignedExchangePrefetchMetricRecorder>(
+              base::DefaultTickClock::GetInstance())) {}
 
 void PrefetchURLLoaderService::InitializeResourceContext(
     ResourceContext* resource_context,
diff --git a/content/browser/renderer_host/input/fling_browsertest.cc b/content/browser/renderer_host/input/fling_browsertest.cc
index 343ae569..14afc422 100644
--- a/content/browser/renderer_host/input/fling_browsertest.cc
+++ b/content/browser/renderer_host/input/fling_browsertest.cc
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "build/build_config.h"
 #include "content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/public/test/browser_test_utils.h"
@@ -93,7 +92,7 @@
   void LoadPageWithOOPIF() {
     // navigate main frame to URL.
     GURL main_url(embedded_test_server()->GetURL(
-        "a.com", "/frame_tree/scrollable_page_with_positioned_frame.html"));
+        "a.com", "/frame_tree/page_with_positioned_frame.html"));
     EXPECT_TRUE(NavigateToURL(shell(), main_url));
 
     // Navigate oopif to URL.
@@ -120,21 +119,17 @@
         iframe_node->current_frame_host()->GetRenderWidgetHost()->GetView());
   }
 
-  void SimulateTouchscreenFling(
-      RenderWidgetHostImpl* render_widget_host,
-      const gfx::Vector2dF& fling_velocity = gfx::Vector2dF(0.f, -2000.f)) {
+  void SimulateTouchscreenFling(RenderWidgetHostImpl* render_widget_host) {
     DCHECK(render_widget_host);
     // Send a GSB to start scrolling sequence.
-    auto input_msg_watcher = std::make_unique<InputMsgWatcher>(
-        render_widget_host, blink::WebInputEvent::kGestureScrollBegin);
     blink::WebGestureEvent gesture_scroll_begin(
         blink::WebGestureEvent::kGestureScrollBegin,
         blink::WebInputEvent::kNoModifiers, ui::EventTimeForNow());
     gesture_scroll_begin.SetSourceDevice(blink::kWebGestureDeviceTouchscreen);
     gesture_scroll_begin.data.scroll_begin.delta_hint_units =
         blink::WebGestureEvent::ScrollUnits::kPrecisePixels;
-    gesture_scroll_begin.data.scroll_begin.delta_x_hint = fling_velocity.x();
-    gesture_scroll_begin.data.scroll_begin.delta_y_hint = fling_velocity.y();
+    gesture_scroll_begin.data.scroll_begin.delta_x_hint = 0.f;
+    gesture_scroll_begin.data.scroll_begin.delta_y_hint = -5.f;
     const gfx::PointF scroll_location_in_widget(1, 1);
     const gfx::PointF scroll_location_in_root =
         child_view_ ? child_view_->TransformPointToRootCoordSpaceF(
@@ -147,31 +142,26 @@
     gesture_scroll_begin.SetPositionInWidget(scroll_location_in_widget);
     gesture_scroll_begin.SetPositionInScreen(scroll_location_in_screen);
     render_widget_host->ForwardGestureEvent(gesture_scroll_begin);
-    input_msg_watcher->GetAckStateWaitIfNecessary();
 
     //  Send a GFS.
     blink::WebGestureEvent gesture_fling_start(
         blink::WebGestureEvent::kGestureFlingStart,
         blink::WebInputEvent::kNoModifiers, ui::EventTimeForNow());
     gesture_fling_start.SetSourceDevice(blink::kWebGestureDeviceTouchscreen);
-    gesture_fling_start.data.fling_start.velocity_x = fling_velocity.x();
-    gesture_fling_start.data.fling_start.velocity_y = fling_velocity.y();
+    gesture_fling_start.data.fling_start.velocity_x = 0.f;
+    gesture_fling_start.data.fling_start.velocity_y = -2000.f;
     gesture_fling_start.SetPositionInWidget(scroll_location_in_widget);
     gesture_fling_start.SetPositionInScreen(scroll_location_in_screen);
     render_widget_host->ForwardGestureEvent(gesture_fling_start);
   }
 
-  void SimulateTouchpadFling(
-      RenderWidgetHostImpl* render_widget_host,
-      const gfx::Vector2dF& fling_velocity = gfx::Vector2dF(0.f, -2000.f)) {
+  void SimulateTouchpadFling(RenderWidgetHostImpl* render_widget_host) {
     DCHECK(render_widget_host);
     // Send a wheel event to start scrolling sequence.
     auto input_msg_watcher = std::make_unique<InputMsgWatcher>(
-        render_widget_host, blink::WebInputEvent::kGestureScrollBegin);
+        GetWidgetHost(), blink::WebInputEvent::kMouseWheel);
     blink::WebMouseWheelEvent wheel_event =
-        SyntheticWebMouseWheelEventBuilder::Build(
-            10, 10, fling_velocity.x() / 1000, fling_velocity.y() / 1000, 0,
-            true);
+        SyntheticWebMouseWheelEventBuilder::Build(10, 10, 0, -53, 0, true);
     wheel_event.phase = blink::WebMouseWheelEvent::kPhaseBegan;
     const gfx::PointF position_in_widget(1, 1);
     const gfx::PointF position_in_root =
@@ -185,15 +175,15 @@
     wheel_event.SetPositionInWidget(position_in_widget);
     wheel_event.SetPositionInScreen(position_in_screen);
     render_widget_host->ForwardWheelEvent(wheel_event);
-    input_msg_watcher->GetAckStateWaitIfNecessary();
+    input_msg_watcher->WaitForAck();
 
     //  Send a GFS.
     blink::WebGestureEvent gesture_fling_start(
         blink::WebGestureEvent::kGestureFlingStart,
         blink::WebInputEvent::kNoModifiers, ui::EventTimeForNow());
     gesture_fling_start.SetSourceDevice(blink::kWebGestureDeviceTouchpad);
-    gesture_fling_start.data.fling_start.velocity_x = fling_velocity.x();
-    gesture_fling_start.data.fling_start.velocity_y = fling_velocity.y();
+    gesture_fling_start.data.fling_start.velocity_x = 0.f;
+    gesture_fling_start.data.fling_start.velocity_y = -2000.f;
     gesture_fling_start.SetPositionInWidget(position_in_widget);
     gesture_fling_start.SetPositionInScreen(position_in_screen);
     render_widget_host->ForwardGestureEvent(gesture_fling_start);
@@ -222,36 +212,25 @@
     run_loop.Run();
   }
 
-  void WaitForOOPIFScroll(FrameTreeNode* iframe_node,
-                          int target_scroll_offset = 100,
-                          bool upward = false) {
-    DCHECK(iframe_node);
-    double scroll_top =
-        EvalJs(iframe_node->current_frame_host(), "window.scrollY")
-            .ExtractDouble();
+  void WaitForChildScroll() {
+    FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
+                              ->GetFrameTree()
+                              ->root();
+    ASSERT_EQ(1U, root->child_count());
+    FrameTreeNode* iframe_node = root->child_at(0);
+    int scroll_top = EvalJs(iframe_node->current_frame_host(), "window.scrollY")
+                         .ExtractDouble();
     // scrollTop > 0 is not enough since the first progressFling is called from
-    // FlingController::ProcessGestureFlingStart. Wait for scrollTop to reach
-    // target_scroll_offset to make sure that ProgressFling has been called
-    // through FlingScheduler at least once.
-    while ((upward && scroll_top > target_scroll_offset) ||
-           (!upward && scroll_top < target_scroll_offset)) {
+    // FlingController::ProcessGestureFlingStart. Wait for scrollTop to exceed
+    // 100 pixels to make sure that ProgressFling has been called through
+    // FlingScheduler at least once.
+    while (scroll_top < 100) {
       GiveItSomeTime();
       scroll_top = EvalJs(iframe_node->current_frame_host(), "window.scrollY")
                        .ExtractDouble();
     }
   }
 
-  FrameTreeNode* GetRootNode() {
-    return static_cast<WebContentsImpl*>(shell()->web_contents())
-        ->GetFrameTree()
-        ->root();
-  }
-
-  FrameTreeNode* GetChildNode() {
-    FrameTreeNode* root = GetRootNode();
-    return root->child_at(0);
-  }
-
   std::unique_ptr<base::RunLoop> run_loop_;
   RenderWidgetHostViewBase* child_view_ = nullptr;
   RenderWidgetHostViewBase* root_view_ = nullptr;
@@ -260,10 +239,6 @@
   DISALLOW_COPY_AND_ASSIGN(BrowserSideFlingBrowserTest);
 };
 
-// On Mac we don't have any touchscreen/touchpad fling events (GFS/GFC).
-// Instead, the OS keeps sending wheel events when the user lifts their fingers
-// from touchpad.
-#if !defined(OS_MACOSX)
 IN_PROC_BROWSER_TEST_F(BrowserSideFlingBrowserTest, TouchscreenFling) {
   LoadURL(kBrowserFlingDataURL);
   SimulateTouchscreenFling(GetWidgetHost());
@@ -306,86 +281,23 @@
 IN_PROC_BROWSER_TEST_F(BrowserSideFlingBrowserTest, TouchscreenFlingInOOPIF) {
   LoadPageWithOOPIF();
   SimulateTouchscreenFling(child_view_->host());
-  WaitForOOPIFScroll(GetChildNode());
+  WaitForChildScroll();
 }
 IN_PROC_BROWSER_TEST_F(BrowserSideFlingBrowserTest, TouchpadFlingInOOPIF) {
   LoadPageWithOOPIF();
-  SimulateTouchpadFling(child_view_->host());
-  WaitForOOPIFScroll(GetChildNode());
-}
-IN_PROC_BROWSER_TEST_F(BrowserSideFlingBrowserTest,
-                       TouchscreenFlingBubblesFromOOPIF) {
-  LoadPageWithOOPIF();
-  // Scroll the parent down so that it is scrollable upward.
-  EXPECT_TRUE(
-      ExecJs(GetRootNode()->current_frame_host(), "window.scrollTo(0, 20)"));
-  // We expect to have window.scrollY == 20 after scrolling but with zoom for
-  // dsf enabled on android we get window.scrollY == 19 (see
-  // https://crbug.com/891860).
-  WaitForOOPIFScroll(GetRootNode(), 19);
-
-  // Fling and wait for the parent to scroll upward.
-  gfx::Vector2d fling_velocity(0, 2000);
-  SimulateTouchscreenFling(child_view_->host(), fling_velocity);
-  WaitForOOPIFScroll(GetRootNode(), 15, true /* upward */);
+  SimulateTouchscreenFling(child_view_->host());
+  WaitForChildScroll();
 }
 
-// Flaky, probably because of https://crbug.com/892656
-#define MAYBE_TouchpadFlingBubblesFromOOPIF \
-  DISABLED_TouchpadFlingBubblesFromOOPIF
-IN_PROC_BROWSER_TEST_F(BrowserSideFlingBrowserTest,
-                       MAYBE_TouchpadFlingBubblesFromOOPIF) {
-  LoadPageWithOOPIF();
-  // Scroll the parent down so that it is scrollable upward.
-  EXPECT_TRUE(
-      ExecJs(GetRootNode()->current_frame_host(), "window.scrollTo(0, 20)"));
-  // We expect to have window.scrollY == 20 after scrolling but with zoom for
-  // dsf enabled on android we get window.scrollY == 19 (see
-  // https://crbug.com/891860).
-  WaitForOOPIFScroll(GetRootNode(), 19);
-
-  // Fling and wait for the parent to scroll upward.
-  gfx::Vector2d fling_velocity(0, 2000);
-  SimulateTouchpadFling(child_view_->host(), fling_velocity);
-  WaitForOOPIFScroll(GetRootNode(), 15, true /* upward */);
-}
-IN_PROC_BROWSER_TEST_F(BrowserSideFlingBrowserTest, GFCGetsBubbledFromOOPIF) {
-  LoadPageWithOOPIF();
-  // Scroll the parent down so that it is scrollable upward.
-  EXPECT_TRUE(
-      ExecJs(GetRootNode()->current_frame_host(), "window.scrollTo(0, 20)"));
-  // We expect to have window.scrollY == 20 after scrolling but with zoom for
-  // dsf enabled on android we get window.scrollY == 19 (see
-  // https://crbug.com/891860).
-  WaitForOOPIFScroll(GetRootNode(), 19);
-
-  // Fling and wait for the parent to scroll upward.
-  gfx::Vector2d fling_velocity(0, 2000);
-  SimulateTouchscreenFling(child_view_->host(), fling_velocity);
-  WaitForOOPIFScroll(GetRootNode(), 15, true /* upward */);
-
-  // Send a GFC to the child and wait for it to get bubbled.
-  auto input_msg_watcher = std::make_unique<InputMsgWatcher>(
-      GetWidgetHost(), blink::WebInputEvent::kGestureFlingCancel);
-  blink::WebGestureEvent gesture_fling_cancel(
-      blink::WebGestureEvent::kGestureFlingCancel,
-      blink::WebInputEvent::kNoModifiers, ui::EventTimeForNow());
-  gesture_fling_cancel.SetSourceDevice(blink::kWebGestureDeviceTouchscreen);
-
-  const gfx::PointF location_in_widget(1, 1);
-  const gfx::PointF location_in_root =
-      child_view_->TransformPointToRootCoordSpaceF(location_in_widget);
-  const gfx::PointF location_in_screen =
-      location_in_root + root_view_->GetViewBounds().OffsetFromOrigin();
-  gesture_fling_cancel.SetPositionInWidget(location_in_widget);
-  gesture_fling_cancel.SetPositionInScreen(location_in_screen);
-  child_view_->host()->ForwardGestureEvent(gesture_fling_cancel);
-  input_msg_watcher->GetAckStateWaitIfNecessary();
-}
-
+// Disabled on MacOS because it doesn't support touchscreen scroll.
+#if defined(OS_MACOSX)
+#define MAYBE_ScrollEndGeneratedForFilteredFling \
+  DISABLED_ScrollEndGeneratedForFilteredFling
+#else
 // Flaky, see https://crbug.com/850455
 #define MAYBE_ScrollEndGeneratedForFilteredFling \
   DISABLED_ScrollEndGeneratedForFilteredFling
+#endif
 IN_PROC_BROWSER_TEST_F(BrowserSideFlingBrowserTest,
                        MAYBE_ScrollEndGeneratedForFilteredFling) {
   LoadURL(kTouchActionFilterDataURL);
@@ -436,6 +348,5 @@
   EXPECT_EQ(InputEventAckSource::BROWSER,
             scroll_end_watcher->last_event_ack_source());
 }
-#endif  // !defined(OS_MACOSX)
 
 }  // namespace content
diff --git a/content/browser/renderer_host/input/input_router_impl.cc b/content/browser/renderer_host/input/input_router_impl.cc
index f72a8f5..899b712 100644
--- a/content/browser/renderer_host/input/input_router_impl.cc
+++ b/content/browser/renderer_host/input/input_router_impl.cc
@@ -590,9 +590,8 @@
   touch_action_filter_.OnSetTouchAction(cc::kTouchActionAuto);
 }
 
-void InputRouterImpl::OnHasTouchEventHandlersForTest(bool has_handlers) {
-  touch_action_filter_.OnHasTouchEventHandlers(has_handlers);
-  // TODO(ajwong): Why doesn't this change |touch_event_queue_|?
+void InputRouterImpl::ForceResetTouchActionForTest() {
+  touch_action_filter_.ForceResetTouchActionForTest();
 }
 
 void InputRouterImpl::OnSetTouchAction(cc::TouchAction touch_action) {
diff --git a/content/browser/renderer_host/input/input_router_impl.h b/content/browser/renderer_host/input/input_router_impl.h
index 3edcf79..0582b35 100644
--- a/content/browser/renderer_host/input/input_router_impl.h
+++ b/content/browser/renderer_host/input/input_router_impl.h
@@ -107,7 +107,7 @@
     return frame_host_binding_;
   }
 
-  void OnHasTouchEventHandlersForTest(bool has_handlers);
+  void ForceResetTouchActionForTest();
 
  private:
   friend class InputRouterImplTest;
diff --git a/content/browser/renderer_host/input/touch_action_filter.cc b/content/browser/renderer_host/input/touch_action_filter.cc
index 249640d..da66a5d 100644
--- a/content/browser/renderer_host/input/touch_action_filter.cc
+++ b/content/browser/renderer_host/input/touch_action_filter.cc
@@ -231,6 +231,11 @@
   return false;
 }
 
+void TouchActionFilter::ForceResetTouchActionForTest() {
+  allowed_touch_action_.reset();
+  scrolling_touch_action_.reset();
+}
+
 void TouchActionFilter::OnSetTouchAction(cc::TouchAction touch_action) {
   // TODO(https://crbug.com/849819): add a DCHECK for
   // |has_touch_event_handler_|.
diff --git a/content/browser/renderer_host/input/touch_action_filter.h b/content/browser/renderer_host/input/touch_action_filter.h
index 0def2d9..231a026 100644
--- a/content/browser/renderer_host/input/touch_action_filter.h
+++ b/content/browser/renderer_host/input/touch_action_filter.h
@@ -69,6 +69,8 @@
   void IncreaseActiveTouches();
   void DecreaseActiveTouches();
 
+  void ForceResetTouchActionForTest();
+
   // Debugging only.
   void AppendToGestureSequenceForDebugging(const char* str);
 
diff --git a/content/browser/renderer_host/render_widget_host_input_event_router.cc b/content/browser/renderer_host/render_widget_host_input_event_router.cc
index 4b730f7..39b30d84 100644
--- a/content/browser/renderer_host/render_widget_host_input_event_router.cc
+++ b/content/browser/renderer_host/render_widget_host_input_event_router.cc
@@ -293,9 +293,6 @@
   if (view == last_fling_start_target_)
     last_fling_start_target_ = nullptr;
 
-  if (view == last_fling_start_bubbled_target_)
-    last_fling_start_bubbled_target_ = nullptr;
-
   event_targeter_->ViewWillBeDestroyed(view);
 }
 
@@ -1004,8 +1001,7 @@
   DCHECK(event.GetType() == blink::WebInputEvent::kGestureScrollBegin ||
          event.GetType() == blink::WebInputEvent::kGestureScrollUpdate ||
          event.GetType() == blink::WebInputEvent::kGestureScrollEnd ||
-         event.GetType() == blink::WebInputEvent::kGestureFlingStart ||
-         event.GetType() == blink::WebInputEvent::kGestureFlingCancel);
+         event.GetType() == blink::WebInputEvent::kGestureFlingStart);
 
   ui::LatencyInfo latency_info =
       ui::WebInputEventTraits::CreateLatencyInfoForWebGestureEvent(event);
@@ -1034,20 +1030,7 @@
     }
 
     bubbling_gesture_scroll_target_.target = target_view;
-  } else if (event.GetType() == blink::WebInputEvent::kGestureFlingCancel) {
-    // GFC event must get bubbled to the same target view that the last GFS has
-    // been bubbled.
-    if (last_fling_start_bubbled_target_) {
-      last_fling_start_bubbled_target_->ProcessGestureEvent(
-          GestureEventInTarget(event, last_fling_start_bubbled_target_),
-          latency_info);
-      last_fling_start_bubbled_target_ = nullptr;
-    }
-    return;
   } else {  // !(event.GetType() == blink::WebInputEvent::kGestureScrollBegin)
-            // && !(event.GetType() ==
-            // blink::WebInputEvent::kGestureFlingCancel)
-
     if (!bubbling_gesture_scroll_target_.target) {
       // The GestureScrollBegin event is not bubbled, don't bubble the rest of
       // the scroll events.
@@ -1079,12 +1062,6 @@
   bubbling_gesture_scroll_target_.target->ProcessGestureEvent(
       GestureEventInTarget(event, bubbling_gesture_scroll_target_.target),
       latency_info);
-
-  // The GFC should be sent to the view that handles the GFS.
-  if (event.GetType() == blink::WebInputEvent::kGestureFlingStart) {
-    last_fling_start_bubbled_target_ = bubbling_gesture_scroll_target_.target;
-  }
-
   if (event.GetType() == blink::WebInputEvent::kGestureScrollEnd ||
       event.GetType() == blink::WebInputEvent::kGestureFlingStart) {
     first_bubbling_scroll_target_.target = nullptr;
diff --git a/content/browser/renderer_host/render_widget_host_input_event_router.h b/content/browser/renderer_host/render_widget_host_input_event_router.h
index 7bcc75b..e1bf8094 100644
--- a/content/browser/renderer_host/render_widget_host_input_event_router.h
+++ b/content/browser/renderer_host/render_widget_host_input_event_router.h
@@ -308,12 +308,6 @@
   // Tracked for the purpose of targeting subsequent fling cancel events.
   RenderWidgetHostViewBase* last_fling_start_target_ = nullptr;
 
-  // During scroll bubbling we bubble the GFS to the target view so that its
-  // fling controller takes care of flinging. In this case we should also send
-  // the GFC to the bubbling target so that the fling controller currently in
-  // charge of the fling progress could handle the fling cancellelation as well.
-  RenderWidgetHostViewBase* last_fling_start_bubbled_target_ = nullptr;
-
   // Tracked for the purpose of providing a root_view when dispatching emulated
   // touch/gesture events.
   RenderWidgetHostViewBase* last_emulated_event_root_view_;
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 0ba4a66..10b41c9 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
@@ -527,24 +527,6 @@
       ack_result == INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS ||
       ack_result == INPUT_EVENT_ACK_STATE_CONSUMED_SHOULD_BUBBLE;
 
-// The inertial events on Mac should still get bubbled since there is no GFS to
-// bubble and the inertial events are received from the OS.
-#if !defined(OS_MACOSX)
-  // When a GFS is bubbled, we still send it to the fling controller of the
-  // child view to finish the scroll sequence. However the GSU and GSE events
-  // that are generated by the child view's fling controller do not need to get
-  // bubbled since the GFS event itself is bubbled and the target's fling
-  // controller will take care of flinging.
-  if ((event.GetType() == blink::WebInputEvent::kGestureScrollEnd &&
-       event.data.scroll_end.inertial_phase ==
-           blink::WebGestureEvent::kMomentumPhase) ||
-      (event.GetType() == blink::WebInputEvent::kGestureScrollUpdate &&
-       event.data.scroll_update.inertial_phase ==
-           blink::WebGestureEvent::kMomentumPhase)) {
-    return;
-  }
-#endif  // defined(OS_MACOSX)
-
   if ((event.GetType() == blink::WebInputEvent::kGestureScrollBegin) &&
       should_bubble) {
     DCHECK(!is_scroll_sequence_bubbling_);
@@ -563,8 +545,7 @@
        should_bubble) ||
       event.GetType() == blink::WebInputEvent::kGestureScrollUpdate ||
       event.GetType() == blink::WebInputEvent::kGestureScrollEnd ||
-      event.GetType() == blink::WebInputEvent::kGestureFlingStart ||
-      event.GetType() == blink::WebInputEvent::kGestureFlingCancel) {
+      event.GetType() == blink::WebInputEvent::kGestureFlingStart) {
     frame_connector_->BubbleScrollEvent(event);
   }
 }
diff --git a/content/browser/site_per_process_hit_test_browsertest.cc b/content/browser/site_per_process_hit_test_browsertest.cc
index 71f632e6..8ff295b6 100644
--- a/content/browser/site_per_process_hit_test_browsertest.cc
+++ b/content/browser/site_per_process_hit_test_browsertest.cc
@@ -4788,7 +4788,7 @@
       static_cast<InputRouterImpl*>(
           static_cast<RenderWidgetHostImpl*>(rwhva_root_->GetRenderWidgetHost())
               ->input_router())
-          ->OnHasTouchEventHandlersForTest(true);
+          ->ForceResetTouchActionForTest();
     }
     SendPinchBeginEndSequence(rwhva_root_, bounds.CenterPoint(), rwhi_child_);
 
diff --git a/content/browser/web_contents/web_contents_ns_view_bridge.mm b/content/browser/web_contents/web_contents_ns_view_bridge.mm
index 90181ae..68516c32 100644
--- a/content/browser/web_contents/web_contents_ns_view_bridge.mm
+++ b/content/browser/web_contents/web_contents_ns_view_bridge.mm
@@ -50,6 +50,10 @@
   NSRect ns_bounds_in_superview =
       [[cocoa_view_ superview] convertRect:ns_bounds_in_window fromView:nil];
   [cocoa_view_ setFrame:ns_bounds_in_superview];
+  // Ensure that the child RenderWidgetHostViews have the same frame as the
+  // WebContentsView.
+  for (NSView* child in [cocoa_view_ subviews])
+    [child setFrame:[cocoa_view_ bounds]];
   [cocoa_view_ setHidden:NO];
 }
 
diff --git a/content/browser/web_package/signed_exchange_loader.cc b/content/browser/web_package/signed_exchange_loader.cc
index 85a3ced..f4324a6d 100644
--- a/content/browser/web_package/signed_exchange_loader.cc
+++ b/content/browser/web_package/signed_exchange_loader.cc
@@ -33,6 +33,8 @@
 namespace {
 
 constexpr char kLoadResultHistogram[] = "SignedExchange.LoadResult";
+constexpr char kPrefetchLoadResultHistogram[] =
+    "SignedExchange.Prefetch.LoadResult";
 
 net::RedirectInfo CreateRedirectInfo(const GURL& new_url,
                                      const GURL& outer_request_url) {
@@ -129,6 +131,11 @@
   DCHECK(signed_exchange_utils::IsSignedExchangeHandlingEnabled());
   DCHECK(outer_request_url_.is_valid());
 
+  if (!(load_flags_ & net::LOAD_PREFETCH)) {
+    metric_recorder_->OnSignedExchangeNonPrefetch(
+        outer_request_url_, outer_response_.response_time);
+  }
+
   // https://wicg.github.io/webpackage/draft-yasskin-http-origin-signed-responses.html#privacy-considerations
   // This can be difficult to determine when the exchange is being loaded from
   // local disk, but when the client itself requested the exchange over a
@@ -136,8 +143,15 @@
   // transport layer, and MUST NOT accept exchanges transferred over plain HTTP
   // without TLS. [spec text]
   if (!IsOriginSecure(outer_request_url)) {
-    UMA_HISTOGRAM_ENUMERATION(kLoadResultHistogram,
-                              SignedExchangeLoadResult::kSXGServedFromNonHTTPS);
+    const SignedExchangeLoadResult result =
+        SignedExchangeLoadResult::kSXGServedFromNonHTTPS;
+    UMA_HISTOGRAM_ENUMERATION(kLoadResultHistogram, result);
+    if (load_flags_ & net::LOAD_PREFETCH) {
+      UMA_HISTOGRAM_ENUMERATION(kPrefetchLoadResultHistogram, result);
+      metric_recorder_->OnSignedExchangePrefetchFinished(
+          outer_request_url_, outer_response_.response_time);
+    }
+
     devtools_proxy_->ReportError(
         "Signed exchange response from non secure origin is not supported.",
         base::nullopt /* error_field */);
@@ -282,9 +296,10 @@
     const network::ResourceResponseHead& resource_response,
     std::unique_ptr<net::SourceStream> payload_stream) {
   UMA_HISTOGRAM_ENUMERATION(kLoadResultHistogram, result);
-
   if (load_flags_ & net::LOAD_PREFETCH) {
-    metric_recorder_->OnSignedExchangePrefetchFinished(request_url, error);
+    UMA_HISTOGRAM_ENUMERATION(kPrefetchLoadResultHistogram, result);
+    metric_recorder_->OnSignedExchangePrefetchFinished(
+        outer_request_url_, outer_response_.response_time);
   }
 
   if (error) {
@@ -358,6 +373,7 @@
   // TODO(https://crbug.com/803774): Fill the data length information too.
   network::URLLoaderCompletionStatus status;
   status.error_code = result;
+  status.completion_time = base::TimeTicks::Now();
 
   if (ssl_info_) {
     DCHECK((url_loader_options_ &
diff --git a/content/browser/web_package/signed_exchange_prefetch_metric_recorder.cc b/content/browser/web_package/signed_exchange_prefetch_metric_recorder.cc
index e83902c..605c3f2 100644
--- a/content/browser/web_package/signed_exchange_prefetch_metric_recorder.cc
+++ b/content/browser/web_package/signed_exchange_prefetch_metric_recorder.cc
@@ -4,20 +4,113 @@
 
 #include "content/browser/web_package/signed_exchange_prefetch_metric_recorder.h"
 
-#include "content/public/browser/browser_thread.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/time/tick_clock.h"
 
 namespace content {
 
-SignedExchangePrefetchMetricRecorder::SignedExchangePrefetchMetricRecorder() =
-    default;
+constexpr size_t kMaxEntries = 256u;
+
+// SignedExchangePrefetchMetricRecorder will not associate prefetch
+// to a navigation if they are older than |kPrefetchExpireTimeDelta|.
+constexpr base::TimeDelta kPrefetchExpireTimeDelta =
+    base::TimeDelta::FromMilliseconds(30000);
+// SignedExchangePrefetchMetricRecorder flushes expired prefetch entries once
+// per |kFlushTimeoutTimeDelta|.
+constexpr base::TimeDelta kFlushTimeoutTimeDelta =
+    base::TimeDelta::FromMilliseconds(30100);
+
+constexpr char kPrecisionHistogram[] =
+    "SignedExchange.Prefetch.Precision.30Seconds";
+constexpr char kRecallHistogram[] = "SignedExchange.Prefetch.Recall.30Seconds";
+
+SignedExchangePrefetchMetricRecorder::SignedExchangePrefetchMetricRecorder(
+    const base::TickClock* tick_clock)
+    : tick_clock_(tick_clock),
+      flush_timer_(std::make_unique<base::OneShotTimer>(tick_clock)) {
+  DETACH_FROM_SEQUENCE(sequence_checker_);
+}
+
 SignedExchangePrefetchMetricRecorder::~SignedExchangePrefetchMetricRecorder() =
     default;
 
 void SignedExchangePrefetchMetricRecorder::OnSignedExchangePrefetchFinished(
     const GURL& outer_url,
-    net::Error error) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  // TODO(crbug.com/890180): Actually Report UMA.
+    base::Time response_time) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (disabled_)
+    return;
+
+  if (recent_prefetch_entries_.size() > kMaxEntries) {
+    // Avoid DoS. Turn off further metric logging to avoid skew too.
+    recent_prefetch_entries_.clear();
+    disabled_ = true;
+    return;
+  }
+
+  // Update |prefetch_time| for an existing entry.
+  recent_prefetch_entries_.insert_or_assign(
+      std::make_pair(outer_url, response_time), tick_clock_->NowTicks());
+
+  if (!flush_timer_->IsRunning())
+    ScheduleFlushTimer();
+}
+
+void SignedExchangePrefetchMetricRecorder::OnSignedExchangeNonPrefetch(
+    const GURL& outer_url,
+    base::Time response_time) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (disabled_)
+    return;
+
+  auto it =
+      recent_prefetch_entries_.find(std::make_pair(outer_url, response_time));
+  bool prefetch_found = false;
+  if (it != recent_prefetch_entries_.end()) {
+    prefetch_found = true;
+    recent_prefetch_entries_.erase(it);
+  }
+
+  UMA_HISTOGRAM_BOOLEAN(kRecallHistogram, prefetch_found);
+  if (prefetch_found)
+    UMA_HISTOGRAM_BOOLEAN(kPrecisionHistogram, true);
+}
+
+void SignedExchangePrefetchMetricRecorder::ScheduleFlushTimer() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  flush_timer_->Start(
+      FROM_HERE, kFlushTimeoutTimeDelta,
+      base::BindOnce(&SignedExchangePrefetchMetricRecorder::OnFlushTimer,
+                     base::Unretained(this)));
+}
+
+void SignedExchangePrefetchMetricRecorder::OnFlushTimer() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (disabled_)
+    return;
+
+  const base::TimeTicks expire_before =
+      tick_clock_->NowTicks() - kPrefetchExpireTimeDelta;
+
+  PrefetchEntries entries;
+  std::swap(entries, recent_prefetch_entries_);
+  for (const auto& it : entries) {
+    const base::TimeTicks prefetch_time = it.second;
+
+    if (prefetch_time < expire_before) {
+      UMA_HISTOGRAM_BOOLEAN(kPrecisionHistogram, false);
+      continue;
+    }
+
+    recent_prefetch_entries_.insert(it);
+  }
+
+  if (!recent_prefetch_entries_.empty())
+    ScheduleFlushTimer();
 }
 
 }  // namespace content
diff --git a/content/browser/web_package/signed_exchange_prefetch_metric_recorder.h b/content/browser/web_package/signed_exchange_prefetch_metric_recorder.h
index 00566617..fa300da5 100644
--- a/content/browser/web_package/signed_exchange_prefetch_metric_recorder.h
+++ b/content/browser/web_package/signed_exchange_prefetch_metric_recorder.h
@@ -5,9 +5,16 @@
 #ifndef CONTENT_BROWSER_WEB_PACKAGE_SIGNED_EXCHANGE_PREFETCH_METRIC_RECORDER_H_
 #define CONTENT_BROWSER_WEB_PACKAGE_SIGNED_EXCHANGE_PREFETCH_METRIC_RECORDER_H_
 
+#include <utility>
+#include "base/containers/flat_map.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "net/base/net_errors.h"
+#include "base/sequence_checker.h"
+#include "base/time/tick_clock.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "content/browser/web_package/signed_exchange_loader.h"
+#include "content/common/content_export.h"
 
 class GURL;
 
@@ -15,18 +22,35 @@
 
 // SignedExchangePrefetchMetricRecorder records signed exchange prefetch and
 // its usage metrics.
-class SignedExchangePrefetchMetricRecorder final
+class CONTENT_EXPORT SignedExchangePrefetchMetricRecorder final
     : public base::RefCountedThreadSafe<SignedExchangePrefetchMetricRecorder> {
  public:
-  SignedExchangePrefetchMetricRecorder();
+  explicit SignedExchangePrefetchMetricRecorder(
+      const base::TickClock* tick_clock);
 
+  void OnSignedExchangeNonPrefetch(const GURL& outer_url,
+                                   base::Time response_time);
   void OnSignedExchangePrefetchFinished(const GURL& outer_url,
-                                        net::Error error);
+                                        base::Time response_time);
 
  private:
   friend class base::RefCountedThreadSafe<SignedExchangePrefetchMetricRecorder>;
   ~SignedExchangePrefetchMetricRecorder();
 
+  void ScheduleFlushTimer();
+  void OnFlushTimer();
+
+  bool disabled_ = false;
+  const base::TickClock* tick_clock_;
+
+  using PrefetchEntries =
+      base::flat_map<std::pair<GURL, base::Time /* response_time */>,
+                     base::TimeTicks /* prefetch_time */>;
+  PrefetchEntries recent_prefetch_entries_;
+
+  std::unique_ptr<base::OneShotTimer> flush_timer_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
   DISALLOW_COPY_AND_ASSIGN(SignedExchangePrefetchMetricRecorder);
 };
 
diff --git a/content/browser/web_package/signed_exchange_prefetch_metric_recorder_unittest.cc b/content/browser/web_package/signed_exchange_prefetch_metric_recorder_unittest.cc
new file mode 100644
index 0000000..5cae66b
--- /dev/null
+++ b/content/browser/web_package/signed_exchange_prefetch_metric_recorder_unittest.cc
@@ -0,0 +1,106 @@
+// 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/web_package/signed_exchange_prefetch_metric_recorder.h"
+
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/test/simple_test_clock.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+class SignedExchangePrefetchMetricRecorderTest : public ::testing::Test {
+ public:
+  SignedExchangePrefetchMetricRecorderTest()
+      : scoped_task_environment_(
+            base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME) {}
+
+  void SetUp() override {
+    metric_recorder_ =
+        base::MakeRefCounted<SignedExchangePrefetchMetricRecorder>(
+            &test_tick_clock_);
+  }
+
+  void FastForwardBy(const base::TimeDelta fast_forward_delta) {
+    test_clock_.Advance(fast_forward_delta);
+    test_tick_clock_.Advance(fast_forward_delta);
+    scoped_task_environment_.FastForwardBy(fast_forward_delta);
+  }
+
+ protected:
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  const base::HistogramTester histogram_tester_;
+  base::SimpleTestClock test_clock_;
+  base::SimpleTestTickClock test_tick_clock_;
+  scoped_refptr<SignedExchangePrefetchMetricRecorder> metric_recorder_;
+};
+
+TEST_F(SignedExchangePrefetchMetricRecorderTest, PrecisionRecall) {
+  const base::Time response_time = test_clock_.Now();
+  metric_recorder_->OnSignedExchangePrefetchFinished(
+      GURL("https://example.org/success.sxg"), response_time);
+
+  FastForwardBy(base::TimeDelta::FromMilliseconds(100));
+
+  const base::Time response_time2 = test_clock_.Now();
+  metric_recorder_->OnSignedExchangePrefetchFinished(
+      GURL("https://example.org/prefetch_unused.sxg"), response_time2);
+
+  FastForwardBy(base::TimeDelta::FromMilliseconds(100));
+
+  histogram_tester_.ExpectTotalCount(
+      "SignedExchange.Prefetch.Precision.30Seconds", 0);
+  histogram_tester_.ExpectTotalCount("SignedExchange.Prefetch.Recall.30Seconds",
+                                     0);
+
+  metric_recorder_->OnSignedExchangeNonPrefetch(
+      GURL("https://example.org/success.sxg"), response_time);
+
+  histogram_tester_.ExpectUniqueSample(
+      "SignedExchange.Prefetch.Precision.30Seconds", true, 1);
+  histogram_tester_.ExpectUniqueSample(
+      "SignedExchange.Prefetch.Recall.30Seconds", true, 1);
+
+  const base::Time response_time3 = test_clock_.Now();
+  metric_recorder_->OnSignedExchangeNonPrefetch(
+      GURL("https://example.org/not_prefetched.sxg"), response_time3);
+
+  histogram_tester_.ExpectBucketCount(
+      "SignedExchange.Prefetch.Recall.30Seconds", true, 1);
+  histogram_tester_.ExpectBucketCount(
+      "SignedExchange.Prefetch.Recall.30Seconds", false, 1);
+
+  FastForwardBy(base::TimeDelta::FromMilliseconds(35000));
+
+  histogram_tester_.ExpectBucketCount(
+      "SignedExchange.Prefetch.Precision.30Seconds", true, 1);
+  histogram_tester_.ExpectBucketCount(
+      "SignedExchange.Prefetch.Precision.30Seconds", false, 1);
+}
+
+TEST_F(SignedExchangePrefetchMetricRecorderTest, DuplicatePrefetch) {
+  GURL url("https://example.org/foo.sxg");
+  const base::Time response_time = test_clock_.Now();
+
+  metric_recorder_->OnSignedExchangePrefetchFinished(url, response_time);
+
+  FastForwardBy(base::TimeDelta::FromMilliseconds(100));
+
+  metric_recorder_->OnSignedExchangePrefetchFinished(url, response_time);
+
+  FastForwardBy(base::TimeDelta::FromMilliseconds(100));
+
+  metric_recorder_->OnSignedExchangeNonPrefetch(url, response_time);
+
+  FastForwardBy(base::TimeDelta::FromMilliseconds(35000));
+
+  histogram_tester_.ExpectUniqueSample(
+      "SignedExchange.Prefetch.Precision.30Seconds", true, 1);
+  histogram_tester_.ExpectUniqueSample(
+      "SignedExchange.Prefetch.Recall.30Seconds", true, 1);
+}
+
+}  // namespace content
diff --git a/content/browser/web_package/signed_exchange_request_handler_browsertest.cc b/content/browser/web_package/signed_exchange_request_handler_browsertest.cc
index 491651da..f1d6a76 100644
--- a/content/browser/web_package/signed_exchange_request_handler_browsertest.cc
+++ b/content/browser/web_package/signed_exchange_request_handler_browsertest.cc
@@ -254,6 +254,17 @@
   histogram_tester_.ExpectTotalCount(
       "SignedExchange.Time.CertificateFetch.Success",
       PrefetchIsEnabled() ? 2 : 1);
+  if (PrefetchIsEnabled()) {
+    histogram_tester_.ExpectUniqueSample("SignedExchange.Prefetch.LoadResult",
+                                         SignedExchangeLoadResult::kSuccess, 1);
+    histogram_tester_.ExpectUniqueSample(
+        "SignedExchange.Prefetch.Recall.30Seconds", true, 1);
+    histogram_tester_.ExpectUniqueSample(
+        "SignedExchange.Prefetch.Precision.30Seconds", true, 1);
+  } else {
+    histogram_tester_.ExpectUniqueSample(
+        "SignedExchange.Prefetch.Recall.30Seconds", false, 1);
+  }
 }
 
 IN_PROC_BROWSER_TEST_P(SignedExchangeRequestHandlerBrowserTest,
diff --git a/content/public/android/BUILD.gn b/content/public/android/BUILD.gn
index 86fb816..0420191 100644
--- a/content/public/android/BUILD.gn
+++ b/content/public/android/BUILD.gn
@@ -330,6 +330,7 @@
     "//content/browser/android/select_popup.cc",
     "//content/public/browser/android/child_process_importance.h",
     "//content/public/browser/android/motion_event_action.h",
+    "//content/public/browser/browsing_data_remover.h",
     "//content/public/browser/invalidate_type.h",
     "//content/public/browser/navigation_controller.h",
     "//content/public/common/browser_controls_state.h",
diff --git a/content/public/browser/browsing_data_remover.h b/content/public/browser/browsing_data_remover.h
index 67596f7..60055a79 100644
--- a/content/public/browser/browsing_data_remover.h
+++ b/content/public/browser/browsing_data_remover.h
@@ -121,6 +121,9 @@
 
   // A helper enum to report the deletion of cookies and/or cache. Do not
   // reorder the entries, as this enum is passed to UMA.
+  // A Java counterpart will be generated for this enum so that it can be
+  // logged on Android.
+  // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.browsing_data
   enum CookieOrCacheDeletionChoice {
     NEITHER_COOKIES_NOR_CACHE,
     ONLY_COOKIES,
diff --git a/content/public/renderer/media_stream_utils.cc b/content/public/renderer/media_stream_utils.cc
index 15d6f12c..f6ad39d 100644
--- a/content/public/renderer/media_stream_utils.cc
+++ b/content/public/renderer/media_stream_utils.cc
@@ -20,7 +20,6 @@
 #include "media/capture/video_capturer_source.h"
 #include "third_party/blink/public/platform/web_media_stream.h"
 #include "third_party/blink/public/platform/web_media_stream_source.h"
-#include "third_party/blink/public/web/web_media_stream_registry.h"
 
 namespace content {
 
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc
index 932bb243..6860439 100644
--- a/content/public/test/browser_test_utils.cc
+++ b/content/public/test/browser_test_utils.cc
@@ -653,7 +653,7 @@
 void ResetTouchAction(RenderWidgetHost* host) {
   static_cast<InputRouterImpl*>(
       static_cast<RenderWidgetHostImpl*>(host)->input_router())
-      ->OnHasTouchEventHandlersForTest(true);
+      ->ForceResetTouchActionForTest();
 }
 
 void ResendGestureScrollUpdateToEmbedder(WebContents* guest_web_contents,
diff --git a/content/renderer/media/web_media_element_source_utils.cc b/content/renderer/media/web_media_element_source_utils.cc
index 94502c5..9fc4893 100644
--- a/content/renderer/media/web_media_element_source_utils.cc
+++ b/content/renderer/media/web_media_element_source_utils.cc
@@ -6,7 +6,6 @@
 
 #include "third_party/blink/public/platform/web_media_player_source.h"
 #include "third_party/blink/public/platform/web_media_stream.h"
-#include "third_party/blink/public/web/web_media_stream_registry.h"
 
 namespace content {
 
@@ -15,11 +14,6 @@
   if (source.IsMediaStream())
     return source.GetAsMediaStream();
 
-  if (source.IsURL()) {
-    return blink::WebMediaStreamRegistry::LookupMediaStreamDescriptor(
-        source.GetAsURL());
-  }
-
   return blink::WebMediaStream();
 }
 
diff --git a/content/renderer/push_messaging/push_messaging_client.cc b/content/renderer/push_messaging/push_messaging_client.cc
index 512d3e2..06ae6c7 100644
--- a/content/renderer/push_messaging/push_messaging_client.cc
+++ b/content/renderer/push_messaging/push_messaging_client.cc
@@ -15,13 +15,11 @@
 #include "content/public/common/service_names.mojom.h"
 #include "content/renderer/push_messaging/push_provider.h"
 #include "content/renderer/render_frame_impl.h"
-#include "content/renderer/service_worker/web_service_worker_registration_impl.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "third_party/blink/public/mojom/manifest/manifest_manager.mojom.h"
 #include "third_party/blink/public/platform/modules/push_messaging/web_push_error.h"
 #include "third_party/blink/public/platform/modules/push_messaging/web_push_subscription.h"
 #include "third_party/blink/public/platform/modules/push_messaging/web_push_subscription_options.h"
-#include "third_party/blink/public/platform/modules/service_worker/web_service_worker_registration.h"
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/web/web_console_message.h"
 #include "third_party/blink/public/web/web_local_frame.h"
@@ -45,11 +43,10 @@
 }
 
 void PushMessagingClient::Subscribe(
-    blink::WebServiceWorkerRegistration* service_worker_registration,
+    int64_t service_worker_registration_id,
     const blink::WebPushSubscriptionOptions& options,
     bool user_gesture,
     std::unique_ptr<blink::WebPushSubscriptionCallbacks> callbacks) {
-  DCHECK(service_worker_registration);
   DCHECK(callbacks);
 
   // If a developer provided an application server key in |options|, skip
@@ -59,7 +56,7 @@
         ->GetManifestManager()
         .RequestManifest(base::BindOnce(&PushMessagingClient::DidGetManifest,
                                         base::Unretained(this),
-                                        service_worker_registration, options,
+                                        service_worker_registration_id, options,
                                         user_gesture, std::move(callbacks)));
   } else {
     PushSubscriptionOptions content_options;
@@ -67,13 +64,13 @@
     // Just treat the server key as a string of bytes and pass it to the push
     // service.
     content_options.sender_info = options.application_server_key.Latin1();
-    DoSubscribe(service_worker_registration, content_options, user_gesture,
+    DoSubscribe(service_worker_registration_id, content_options, user_gesture,
                 std::move(callbacks));
   }
 }
 
 void PushMessagingClient::DidGetManifest(
-    blink::WebServiceWorkerRegistration* service_worker_registration,
+    int64_t service_worker_registration_id,
     const blink::WebPushSubscriptionOptions& options,
     bool user_gesture,
     std::unique_ptr<blink::WebPushSubscriptionCallbacks> callbacks,
@@ -95,20 +92,15 @@
         base::UTF16ToUTF8(manifest.gcm_sender_id.string());
   }
 
-  DoSubscribe(service_worker_registration, content_options, user_gesture,
+  DoSubscribe(service_worker_registration_id, content_options, user_gesture,
               std::move(callbacks));
 }
 
 void PushMessagingClient::DoSubscribe(
-    blink::WebServiceWorkerRegistration* service_worker_registration,
+    int64_t service_worker_registration_id,
     const PushSubscriptionOptions& options,
     bool user_gesture,
     std::unique_ptr<blink::WebPushSubscriptionCallbacks> callbacks) {
-  int64_t service_worker_registration_id =
-      static_cast<WebServiceWorkerRegistrationImpl*>(
-          service_worker_registration)
-          ->RegistrationId();
-
   if (options.sender_info.empty()) {
     DidSubscribe(std::move(callbacks),
                  mojom::PushRegistrationStatus::NO_SENDER_ID, base::nullopt,
diff --git a/content/renderer/push_messaging/push_messaging_client.h b/content/renderer/push_messaging/push_messaging_client.h
index c8d6e8e3..b8ef9bc 100644
--- a/content/renderer/push_messaging/push_messaging_client.h
+++ b/content/renderer/push_messaging/push_messaging_client.h
@@ -44,13 +44,13 @@
 
   // WebPushClient implementation.
   void Subscribe(
-      blink::WebServiceWorkerRegistration* service_worker_registration,
+      int64_t service_worker_registration_id,
       const blink::WebPushSubscriptionOptions& options,
       bool user_gesture,
       std::unique_ptr<blink::WebPushSubscriptionCallbacks> callbacks) override;
 
   void DidGetManifest(
-      blink::WebServiceWorkerRegistration* service_worker_registration,
+      int64_t service_worker_registration_id,
       const blink::WebPushSubscriptionOptions& options,
       bool user_gesture,
       std::unique_ptr<blink::WebPushSubscriptionCallbacks> callbacks,
@@ -58,7 +58,7 @@
       const blink::Manifest& manifest);
 
   void DoSubscribe(
-      blink::WebServiceWorkerRegistration* service_worker_registration,
+      int64_t service_worker_registration_id,
       const PushSubscriptionOptions& options,
       bool user_gesture,
       std::unique_ptr<blink::WebPushSubscriptionCallbacks> callbacks);
diff --git a/content/renderer/push_messaging/push_provider.cc b/content/renderer/push_messaging/push_provider.cc
index 3f3e6e6..605a4b0 100644
--- a/content/renderer/push_messaging/push_provider.cc
+++ b/content/renderer/push_messaging/push_provider.cc
@@ -15,7 +15,6 @@
 #include "content/public/common/push_messaging_status.mojom.h"
 #include "content/public/common/push_subscription_options.h"
 #include "content/public/common/service_names.mojom.h"
-#include "content/renderer/service_worker/web_service_worker_registration_impl.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "third_party/blink/public/platform/modules/push_messaging/web_push_subscription.h"
 #include "third_party/blink/public/platform/modules/push_messaging/web_push_subscription_options.h"
@@ -87,15 +86,6 @@
   return "";
 }
 
-// Returns the id of the given |service_worker_registration|, which
-// is only available on the implementation of the interface.
-int64_t GetServiceWorkerRegistrationId(
-    blink::WebServiceWorkerRegistration* service_worker_registration) {
-  return static_cast<WebServiceWorkerRegistrationImpl*>(
-             service_worker_registration)
-      ->RegistrationId();
-}
-
 }  // namespace
 
 blink::WebPushError PushRegistrationStatusToWebPushError(
@@ -180,15 +170,12 @@
 }
 
 void PushProvider::Subscribe(
-    blink::WebServiceWorkerRegistration* service_worker_registration,
+    int64_t service_worker_registration_id,
     const blink::WebPushSubscriptionOptions& options,
     bool user_gesture,
     std::unique_ptr<blink::WebPushSubscriptionCallbacks> callbacks) {
-  DCHECK(service_worker_registration);
   DCHECK(callbacks);
 
-  int64_t service_worker_registration_id =
-      GetServiceWorkerRegistrationId(service_worker_registration);
   PushSubscriptionOptions content_options;
   content_options.user_visible_only = options.user_visible_only;
 
@@ -233,14 +220,10 @@
 }
 
 void PushProvider::Unsubscribe(
-    blink::WebServiceWorkerRegistration* service_worker_registration,
+    int64_t service_worker_registration_id,
     std::unique_ptr<blink::WebPushUnsubscribeCallbacks> callbacks) {
-  DCHECK(service_worker_registration);
   DCHECK(callbacks);
 
-  int64_t service_worker_registration_id =
-      GetServiceWorkerRegistrationId(service_worker_registration);
-
   push_messaging_manager_->Unsubscribe(
       service_worker_registration_id,
       // Safe to use base::Unretained because |push_messaging_manager_ |is owned
@@ -266,14 +249,10 @@
 }
 
 void PushProvider::GetSubscription(
-    blink::WebServiceWorkerRegistration* service_worker_registration,
+    int64_t service_worker_registration_id,
     std::unique_ptr<blink::WebPushSubscriptionCallbacks> callbacks) {
-  DCHECK(service_worker_registration);
   DCHECK(callbacks);
 
-  int64_t service_worker_registration_id =
-      GetServiceWorkerRegistrationId(service_worker_registration);
-
   push_messaging_manager_->GetSubscription(
       service_worker_registration_id,
       // Safe to use base::Unretained because |push_messaging_manager_ |is owned
diff --git a/content/renderer/push_messaging/push_provider.h b/content/renderer/push_messaging/push_provider.h
index 223d4cbc..ad6b1fc4 100644
--- a/content/renderer/push_messaging/push_provider.h
+++ b/content/renderer/push_messaging/push_provider.h
@@ -50,15 +50,15 @@
 
   // blink::WebPushProvider implementation.
   void Subscribe(
-      blink::WebServiceWorkerRegistration* service_worker_registration,
+      int64_t service_worker_registration_id,
       const blink::WebPushSubscriptionOptions& options,
       bool user_gesture,
       std::unique_ptr<blink::WebPushSubscriptionCallbacks> callbacks) override;
   void Unsubscribe(
-      blink::WebServiceWorkerRegistration* service_worker_registration,
+      int64_t service_worker_registration_id,
       std::unique_ptr<blink::WebPushUnsubscribeCallbacks> callbacks) override;
   void GetSubscription(
-      blink::WebServiceWorkerRegistration* service_worker_registration,
+      int64_t service_worker_registration_id,
       std::unique_ptr<blink::WebPushSubscriptionCallbacks> callbacks) override;
 
  private:
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index cf3e520..08cf1c5a 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -204,7 +204,6 @@
 #include "third_party/blink/public/web/web_frame_widget.h"
 #include "third_party/blink/public/web/web_input_method_controller.h"
 #include "third_party/blink/public/web/web_local_frame.h"
-#include "third_party/blink/public/web/web_media_stream_registry.h"
 #include "third_party/blink/public/web/web_navigation_policy.h"
 #include "third_party/blink/public/web/web_navigation_timings.h"
 #include "third_party/blink/public/web/web_plugin.h"
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index b82d9757..0362df8 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -1160,7 +1160,7 @@
 }
 
 void RenderThreadImpl::InitializeCompositorThread() {
-  blink_platform_impl_->InitializeCompositorThread();
+  blink_platform_impl_->CreateAndSetCompositorThread();
   compositor_task_runner_ = blink_platform_impl_->CompositorThreadTaskRunner();
   compositor_task_runner_->PostTask(
       FROM_HERE,
diff --git a/content/shell/renderer/layout_test/test_media_stream_renderer_factory.cc b/content/shell/renderer/layout_test/test_media_stream_renderer_factory.cc
index ffef851..f17dc37 100644
--- a/content/shell/renderer/layout_test/test_media_stream_renderer_factory.cc
+++ b/content/shell/renderer/layout_test/test_media_stream_renderer_factory.cc
@@ -8,7 +8,6 @@
 #include "media/media_buildflags.h"
 #include "third_party/blink/public/platform/web_media_stream.h"
 #include "third_party/blink/public/platform/web_media_stream_track.h"
-#include "third_party/blink/public/web/web_media_stream_registry.h"
 
 namespace {
 
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 1725379..f5529db 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1622,6 +1622,7 @@
     "../browser/web_package/signed_exchange_certificate_chain_unittest.cc",
     "../browser/web_package/signed_exchange_envelope_unittest.cc",
     "../browser/web_package/signed_exchange_handler_unittest.cc",
+    "../browser/web_package/signed_exchange_prefetch_metric_recorder_unittest.cc",
     "../browser/web_package/signed_exchange_prologue_unittest.cc",
     "../browser/web_package/signed_exchange_signature_header_field_unittest.cc",
     "../browser/web_package/signed_exchange_signature_verifier_unittest.cc",
diff --git a/content/test/gpu/gpu_tests/pixel_expectations.py b/content/test/gpu/gpu_tests/pixel_expectations.py
index d7af48c..abc9869 100644
--- a/content/test/gpu/gpu_tests/pixel_expectations.py
+++ b/content/test/gpu/gpu_tests/pixel_expectations.py
@@ -138,6 +138,3 @@
         ['android', ('qualcomm', 'Adreno (TM) 420')], bug=883500)
     self.Fail('Pixel_BackgroundImage',
         ['android', ('qualcomm', 'Adreno (TM) 430')], bug=883500)
-
-    # TODO(yiyix): remove expectation after rebaseline.
-    self.Fail("Pixel_CSS3DBlueBox", bug=879379)
diff --git a/docs/adding_to_third_party.md b/docs/adding_to_third_party.md
index 0d2e025..eda7e18 100644
--- a/docs/adding_to_third_party.md
+++ b/docs/adding_to_third_party.md
@@ -127,12 +127,11 @@
   When adding a new package that could potentially carry security risk, make
   sure to highlight risk to security@chromium.org. You may be asked to add
   a README.security or, in dangerous cases, README.SECURITY.URGENTLY file.
-* Add chromium-third-party@google.com as a reviewer on your change. This
-  will trigger an automatic round-robin assignment of the review to an
-  appropriate reviewer. This list does not receive or deliver email, so only
-  use it as a reviewer, not for other communication.
+* Get licensing approval. Email opensource-licensing@google.com with relevant
+  details and a link to the CL.
 
-Please send separate emails to the eng review and security lists.
+Please send separate emails to the eng review, opensource licensing, and
+security lists.
 
 Subsequent changes don't require third-party-owners approval; you can modify the
 code as much as you want. When you update code, be mindful of security-related
diff --git a/google_apis/gaia/gaia_auth_util.cc b/google_apis/gaia/gaia_auth_util.cc
index e2bb6147..75d69c8 100644
--- a/google_apis/gaia/gaia_auth_util.cc
+++ b/google_apis/gaia/gaia_auth_util.cc
@@ -182,14 +182,4 @@
   return true;
 }
 
-bool RequestOriginatedFromGaia(const net::URLRequest& request) {
-  return request.GetUserData(kURLRequestUserDataKey) != nullptr;
-}
-
-void MarkURLFetcherAsGaia(net::URLFetcher* fetcher) {
-  DCHECK(fetcher);
-  fetcher->SetURLRequestUserData(kURLRequestUserDataKey,
-                                 base::Bind(&GaiaURLRequestUserData::Create));
-}
-
 }  // namespace gaia
diff --git a/google_apis/gaia/gaia_auth_util.h b/google_apis/gaia/gaia_auth_util.h
index 175a41d..c6af6322 100644
--- a/google_apis/gaia/gaia_auth_util.h
+++ b/google_apis/gaia/gaia_auth_util.h
@@ -11,11 +11,6 @@
 
 class GURL;
 
-namespace net {
-class URLFetcher;
-class URLRequest;
-} // namespace net
-
 namespace gaia {
 
 struct ListedAccount {
@@ -66,12 +61,6 @@
                            std::vector<ListedAccount>* accounts,
                            std::vector<ListedAccount>* signed_out_accounts);
 
-// Returns true if the URL request originated from GAIA.
-bool RequestOriginatedFromGaia(const net::URLRequest& request);
-
-// Marks the URL request as a Gaia request.
-void MarkURLFetcherAsGaia(net::URLFetcher* request);
-
 }  // namespace gaia
 
 #endif  // GOOGLE_APIS_GAIA_GAIA_AUTH_UTIL_H_
diff --git a/google_apis/gaia/gaia_oauth_client.cc b/google_apis/gaia/gaia_oauth_client.cc
index c6c0221..3e14812 100644
--- a/google_apis/gaia/gaia_oauth_client.cc
+++ b/google_apis/gaia/gaia_oauth_client.cc
@@ -403,10 +403,6 @@
   // Retry is implemented internally.
   request_->SetRetryOptions(0, network::SimpleURLLoader::RETRY_NEVER);
 
-  // TODO(https://crbug.com/808498) re-add data use measurement once
-  // SimpleURLLoader supports it. Previous way of setting it was:
-  // MarkURLFetcherAsGaia(request_.get());
-
   request_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
       url_loader_factory_.get(),
       // Unretained(this) is safe since |this| owns |request_|, and its deletion
diff --git a/google_apis/gaia/oauth2_api_call_flow.cc b/google_apis/gaia/oauth2_api_call_flow.cc
index 86b8b75..2a4c73f4 100644
--- a/google_apis/gaia/oauth2_api_call_flow.cc
+++ b/google_apis/gaia/oauth2_api_call_flow.cc
@@ -95,10 +95,6 @@
   std::unique_ptr<network::SimpleURLLoader> result =
       network::SimpleURLLoader::Create(std::move(request), traffic_annotation);
 
-  // TODO(https://crbug.com/808498) re-add data use measurement once
-  // SimpleURLLoader supports it. Previously:
-  //     gaia::MarkURLFetcherAsGaia(result.get());
-
   // Fetchers are sometimes cancelled because a network change was detected,
   // especially at startup and after sign-in on ChromeOS. Retrying once should
   // be enough in those cases; let the fetcher retry up to 3 times just in case.
diff --git a/headless/lib/browser/headless_request_context_manager.cc b/headless/lib/browser/headless_request_context_manager.cc
index 318f26c..f8dcb0ae 100644
--- a/headless/lib/browser/headless_request_context_manager.cc
+++ b/headless/lib/browser/headless_request_context_manager.cc
@@ -128,14 +128,14 @@
 
   void SetURLRequestContext(net::URLRequestContext* context) {
     DCHECK(!context_ && !context_owner_);
-    DCHECK(!base::FeatureList::IsEnabled(network::features::kNetworkService));
+    DCHECK(!base::FeatureList::IsEnabled(::network::features::kNetworkService));
 
     context_ = context;
   }
 
   void SetURLRequestContext(std::unique_ptr<net::URLRequestContext> context) {
     DCHECK(!context_ && !context_owner_);
-    DCHECK(base::FeatureList::IsEnabled(network::features::kNetworkService));
+    DCHECK(base::FeatureList::IsEnabled(::network::features::kNetworkService));
 
     context_owner_ = std::move(context);
     context_ = context_owner_.get();
@@ -160,7 +160,7 @@
 // ProxyConfigClient.
 class HeadlessProxyConfigMonitor
     : public net::ProxyConfigService::Observer,
-      public network::mojom::ProxyConfigPollerClient {
+      public ::network::mojom::ProxyConfigPollerClient {
  public:
   static void DeleteSoon(std::unique_ptr<HeadlessProxyConfigMonitor> instance) {
     instance->task_runner_->DeleteSoon(FROM_HERE, instance.release());
@@ -191,7 +191,7 @@
   // whenever the configuration changes. Can be called more than once to inform
   // multiple NetworkContexts of proxy changes.
   void AddToNetworkContextParams(
-      network::mojom::NetworkContextParams* network_context_params) {
+      ::network::mojom::NetworkContextParams* network_context_params) {
     DCHECK(task_runner_->RunsTasksInCurrentSequence());
     DCHECK(!proxy_config_client_);
     network_context_params->proxy_config_client_request =
@@ -231,8 +231,8 @@
 
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
   std::unique_ptr<net::ProxyConfigService> proxy_config_service_;
-  mojo::Binding<network::mojom::ProxyConfigPollerClient> poller_binding_;
-  network::mojom::ProxyConfigClientPtr proxy_config_client_;
+  mojo::Binding<::network::mojom::ProxyConfigPollerClient> poller_binding_;
+  ::network::mojom::ProxyConfigClientPtr proxy_config_client_;
 
   DISALLOW_COPY_AND_ASSIGN(HeadlessProxyConfigMonitor);
 };
@@ -254,7 +254,8 @@
 
 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
   if (manager->user_data_path_.empty()) {
-    network::mojom::CryptConfigPtr config = network::mojom::CryptConfig::New();
+    ::network::mojom::CryptConfigPtr config =
+        ::network::mojom::CryptConfig::New();
     config->store = command_line->GetSwitchValueASCII(switches::kPasswordStore);
     config->product_name = kProductName;
     config->should_use_preference = false;
@@ -277,7 +278,7 @@
     const HeadlessBrowserContextOptions* options,
     base::FilePath user_data_path)
     : network_service_enabled_(
-          base::FeatureList::IsEnabled(network::features::kNetworkService)),
+          base::FeatureList::IsEnabled(::network::features::kNetworkService)),
       io_task_runner_(base::CreateSingleThreadTaskRunnerWithTraits(
           {content::BrowserThread::IO})),
       user_data_path_(std::move(user_data_path)),
@@ -359,7 +360,7 @@
   if (!network_service_enabled_) {
     DCHECK(network_context_request_);
 
-    auto builder = std::make_unique<network::URLRequestContextBuilderMojo>();
+    auto builder = std::make_unique<::network::URLRequestContextBuilderMojo>();
     builder->set_network_delegate(std::make_unique<DelegateImpl>());
     builder->SetCreateHttpTransactionFactoryCallback(
         base::BindOnce(&content::CreateDevToolsNetworkTransactionFactory));
@@ -381,9 +382,9 @@
   url_request_context_getter_->SetURLRequestContext(builder.Build());
 }
 
-network::mojom::NetworkContextParamsPtr
+::network::mojom::NetworkContextParamsPtr
 HeadlessRequestContextManager::CreateNetworkContextParams() {
-  auto context_params = network::mojom::NetworkContextParams::New();
+  auto context_params = ::network::mojom::NetworkContextParams::New();
 
   context_params->user_agent = user_agent_;
   context_params->accept_language = accept_language_;
diff --git a/headless/public/headless_browser.cc b/headless/public/headless_browser.cc
index 9ad4199..2f66f1f 100644
--- a/headless/public/headless_browser.cc
+++ b/headless/public/headless_browser.cc
@@ -20,14 +20,14 @@
 
 namespace {
 // Product name for building the default user agent string.
-const char kProductName[] = "HeadlessChrome";
+const char kHeadlessProductName[] = "HeadlessChrome";
 constexpr gfx::Size kDefaultWindowSize(800, 600);
 
 constexpr gfx::FontRenderParams::Hinting kDefaultFontRenderHinting =
     gfx::FontRenderParams::Hinting::HINTING_FULL;
 
 std::string GetProductNameAndVersion() {
-  return std::string(kProductName) + "/" + PRODUCT_VERSION;
+  return std::string(kHeadlessProductName) + "/" + PRODUCT_VERSION;
 }
 }  // namespace
 
diff --git a/ios/build/bots/chromium.fyi/ios12-sdk-xcode-clang.json b/ios/build/bots/chromium.fyi/ios12-sdk-xcode-clang.json
index 61acdf96..2f8510bf0 100644
--- a/ios/build/bots/chromium.fyi/ios12-sdk-xcode-clang.json
+++ b/ios/build/bots/chromium.fyi/ios12-sdk-xcode-clang.json
@@ -4,7 +4,7 @@
     "the final GM, but disabled for the other betas to reduce swirl for the",
     "goma team. Build is performed with gn+ninja."
   ],
-  "xcode build version": "10o23u",
+  "xcode build version": "10a254a",
   "use xcode build version": true,
   "gn_args": [
     "additional_target_cpus=[\"x86\"]",
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm
index ea225ce..0a1d18e 100644
--- a/ios/chrome/app/main_controller.mm
+++ b/ios/chrome/app/main_controller.mm
@@ -956,6 +956,10 @@
   ios::GetChromeBrowserProvider()
       ->GetMailtoHandlerProvider()
       ->RemoveMailtoHandling();
+  // _localStatePrefChangeRegistrar is observing the PrefService, which is owned
+  // indirectly by _chromeMain (through the ChromeBrowserState).
+  // Unregister the observer before the service is destroyed.
+  _localStatePrefChangeRegistrar.RemoveAll();
 
   _chromeMain.reset();
 }
diff --git a/ios/chrome/browser/passwords/password_controller.h b/ios/chrome/browser/passwords/password_controller.h
index 0d368507..389c6f0 100644
--- a/ios/chrome/browser/passwords/password_controller.h
+++ b/ios/chrome/browser/passwords/password_controller.h
@@ -9,7 +9,7 @@
 #include <memory>
 
 #import "components/autofill/ios/browser/form_suggestion_provider.h"
-#import "components/password_manager/ios/password_controller_helper.h"
+#import "components/password_manager/ios/password_form_helper.h"
 #import "ios/chrome/browser/passwords/ios_chrome_password_manager_client.h"
 #import "ios/chrome/browser/passwords/ios_chrome_password_manager_driver.h"
 #import "ios/web/public/web_state/web_state_observer_bridge.h"
@@ -42,7 +42,7 @@
 @interface PasswordController : NSObject<CRWWebStateObserver,
                                          PasswordManagerClientDelegate,
                                          PasswordManagerDriverDelegate,
-                                         PasswordControllerHelperDelegate>
+                                         PasswordFormHelperDelegate>
 
 // An object that can provide suggestions from this PasswordController.
 @property(nonatomic, readonly) id<FormSuggestionProvider> suggestionProvider;
diff --git a/ios/chrome/browser/passwords/password_controller.mm b/ios/chrome/browser/passwords/password_controller.mm
index 7ee3ff97..eee8586e 100644
--- a/ios/chrome/browser/passwords/password_controller.mm
+++ b/ios/chrome/browser/passwords/password_controller.mm
@@ -36,6 +36,7 @@
 #include "components/password_manager/core/browser/password_manager_driver.h"
 #include "components/password_manager/ios/account_select_fill_data.h"
 #import "components/password_manager/ios/js_password_manager.h"
+#import "components/password_manager/ios/password_suggestion_helper.h"
 #include "components/sync/driver/sync_service.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/infobars/infobar_manager_impl.h"
@@ -77,9 +78,6 @@
 using password_manager::SerializeFillData;
 using password_manager::SerializePasswordFormFillData;
 
-typedef void (^PasswordSuggestionsAvailableCompletion)(
-    const AccountSelectFillData*);
-
 namespace {
 // Types of password infobars to display.
 enum class PasswordInfoBarType { SAVE, UPDATE };
@@ -111,15 +109,18 @@
 }
 }  // namespace
 
-@interface PasswordController ()
+@interface PasswordController ()<PasswordSuggestionHelperDelegate>
 
 // View controller for auto sign-in notification, owned by this
 // PasswordController.
 @property(nonatomic, strong)
     NotifyUserAutoSigninViewController* notifyAutoSigninViewController;
 
-// Helper contains common password controller logic.
-@property(nonatomic, readonly) PasswordControllerHelper* helper;
+// Helper contains common password form processing logic.
+@property(nonatomic, readonly) PasswordFormHelper* formHelper;
+
+// Helper contains common password suggestion logic.
+@property(nonatomic, readonly) PasswordSuggestionHelper* suggestionHelper;
 
 @end
 
@@ -147,69 +148,12 @@
 
 @end
 
-namespace {
-
-// Constructs an array of FormSuggestions, each corresponding to a username/
-// password pair in |AccountSelectFillData|. "Show all" item is appended.
-NSArray* BuildSuggestions(const AccountSelectFillData& fillData,
-                          NSString* formName,
-                          NSString* fieldIdentifier,
-                          NSString* fieldType) {
-  base::string16 form_name = base::SysNSStringToUTF16(formName);
-  base::string16 field_identifier = base::SysNSStringToUTF16(fieldIdentifier);
-  bool is_password_field = [fieldType isEqualToString:@"password"];
-
-  NSMutableArray* suggestions = [NSMutableArray array];
-  PasswordSuggestionType suggestion_type = PasswordSuggestionType::SHOW_ALL;
-  if (fillData.IsSuggestionsAvailable(form_name, field_identifier,
-                                      is_password_field)) {
-    std::vector<password_manager::UsernameAndRealm> username_and_realms_ =
-        fillData.RetrieveSuggestions(form_name, field_identifier,
-                                     is_password_field);
-
-    // Add credentials.
-    for (const auto& username_and_realm : username_and_realms_) {
-      NSString* value = [base::SysUTF16ToNSString(username_and_realm.username)
-          stringByAppendingString:kSuggestionSuffix];
-      NSString* origin =
-          username_and_realm.realm.empty()
-              ? nil
-              : base::SysUTF8ToNSString(username_and_realm.realm);
-
-      [suggestions addObject:[FormSuggestion suggestionWithValue:value
-                                              displayDescription:origin
-                                                            icon:nil
-                                                      identifier:0]];
-      suggestion_type = PasswordSuggestionType::CREDENTIALS;
-    }
-  }
-
-  // Once Manual Fallback is enabled the access to settings will exist as an
-  // option in the new passwords UI.
-  if (!autofill::features::IsPasswordManualFallbackEnabled()) {
-    // Add "Show all".
-    NSString* showAll = l10n_util::GetNSString(IDS_IOS_SHOW_ALL_PASSWORDS);
-    [suggestions addObject:[FormSuggestion suggestionWithValue:showAll
-                                            displayDescription:nil
-                                                          icon:nil
-                                                    identifier:1]];
-  }
-  if (suggestions.count) {
-    LogSuggestionShown(suggestion_type);
-  }
-  return [suggestions copy];
-}
-
-}  // namespace
-
 @implementation PasswordController {
   std::unique_ptr<PasswordManager> _passwordManager;
   std::unique_ptr<PasswordManagerClient> _passwordManagerClient;
   std::unique_ptr<PasswordManagerDriver> _passwordManagerDriver;
   std::unique_ptr<CredentialManager> _credentialManager;
 
-  AccountSelectFillData _fillData;
-
   // The WebState this instance is observing. Will be null after
   // -webStateDestroyed: has been called.
   web::WebState* _webState;
@@ -220,14 +164,6 @@
   // Timer for hiding "Signing in as ..." notification.
   base::OneShotTimer _notifyAutoSigninTimer;
 
-  // True indicates that a request for credentials has been sent to the password
-  // store.
-  BOOL _sentRequestToStore;
-
-  // The completion to inform FormSuggestionController that suggestions are
-  // available for a given form and field.
-  PasswordSuggestionsAvailableCompletion _suggestionsAvailableCompletion;
-
   // User credential waiting to be displayed in autosign-in snackbar, once tab
   // becomes active.
   std::unique_ptr<autofill::PasswordForm> _pendingAutoSigninPasswordForm;
@@ -241,7 +177,9 @@
 
 @synthesize notifyAutoSigninViewController = _notifyAutoSigninViewController;
 
-@synthesize helper = _helper;
+@synthesize formHelper = _formHelper;
+
+@synthesize suggestionHelper = _suggestionHelper;
 
 - (instancetype)initWithWebState:(web::WebState*)webState {
   self = [self initWithWebState:webState
@@ -259,8 +197,10 @@
     _webStateObserverBridge =
         std::make_unique<web::WebStateObserverBridge>(self);
     _webState->AddObserver(_webStateObserverBridge.get());
-    _helper = [[PasswordControllerHelper alloc] initWithWebState:webState
-                                                        delegate:self];
+    _formHelper =
+        [[PasswordFormHelper alloc] initWithWebState:webState delegate:self];
+    _suggestionHelper =
+        [[PasswordSuggestionHelper alloc] initWithDelegate:self];
     if (passwordManagerClient)
       _passwordManagerClient = std::move(passwordManagerClient);
     else
@@ -268,8 +208,6 @@
     _passwordManager.reset(new PasswordManager(_passwordManagerClient.get()));
     _passwordManagerDriver.reset(new IOSChromePasswordManagerDriver(self));
 
-    _sentRequestToStore = NO;
-
     if (base::FeatureList::IsEnabled(features::kCredentialManager)) {
       _credentialManager = std::make_unique<CredentialManager>(
           _passwordManagerClient.get(), _webState);
@@ -308,9 +246,9 @@
 - (void)findAndFillPasswordForms:(NSString*)username
                         password:(NSString*)password
                completionHandler:(void (^)(BOOL))completionHandler {
-  [self.helper findAndFillPasswordFormsWithUserName:username
-                                           password:password
-                                  completionHandler:completionHandler];
+  [self.formHelper findAndFillPasswordFormsWithUserName:username
+                                               password:password
+                                      completionHandler:completionHandler];
 }
 
 #pragma mark - CRWWebStateObserver
@@ -334,9 +272,7 @@
 - (void)webState:(web::WebState*)webState didLoadPageWithSuccess:(BOOL)success {
   DCHECK_EQ(_webState, webState);
   // Clear per-page state.
-  _fillData.Reset();
-  _sentRequestToStore = NO;
-  _suggestionsAvailableCompletion = nil;
+  [self.suggestionHelper resetForNewPage];
 
   // Retrieve the identity of the page. In case the page might be malicous,
   // returns early.
@@ -391,45 +327,18 @@
                                   webState:(web::WebState*)webState
                          completionHandler:
                              (SuggestionsAvailableCompletion)completion {
-  if (!isMainFrame) {
-    web::WebFrame* frame =
-        web::GetWebFrameWithId(webState, base::SysNSStringToUTF8(frameID));
-    if (!frame || webState->GetLastCommittedURL().GetOrigin() !=
-                      frame->GetSecurityOrigin()) {
-      // Passwords is only supported on main frame and iframes with the same
-      // origin.
-      completion(NO);
-      return;
-    }
-  }
-
-  bool should_send_request_to_store =
-      !_sentRequestToStore && [type isEqual:@"focus"];
-  bool is_password_field = [fieldType isEqual:@"password"];
-
-  if (should_send_request_to_store) {
-    // Save the completion and go look for suggestions.
-    _suggestionsAvailableCompletion =
-        [^(const AccountSelectFillData* fill_data) {
-          if (is_password_field) {
-            // Always display "Show all" on the password field.
-            completion(YES);
-          } else if (!fill_data) {
-            completion(NO);
-          } else {
-            completion(fill_data->IsSuggestionsAvailable(
-                base::SysNSStringToUTF16(formName),
-                base::SysNSStringToUTF16(fieldIdentifier), false));
-          }
-        } copy];
-    [self findPasswordFormsAndSendThemToPasswordStore];
-    return;
-  }
-
-  completion(is_password_field ||
-             _fillData.IsSuggestionsAvailable(
-                 base::SysNSStringToUTF16(formName),
-                 base::SysNSStringToUTF16(fieldIdentifier), false));
+  [self.suggestionHelper
+      checkIfSuggestionsAvailableForForm:formName
+                         fieldIdentifier:fieldIdentifier
+                                    type:type
+                                 frameID:frameID
+                             isMainFrame:isMainFrame
+                                webState:webState
+                       completionHandler:^(BOOL suggestionsAvailable) {
+                         // Always display "Show All..." for password fields.
+                         completion([fieldType isEqualToString:@"password"] ||
+                                    suggestionsAvailable);
+                       }];
 }
 
 - (void)retrieveSuggestionsForForm:(NSString*)formName
@@ -442,8 +351,41 @@
                           webState:(web::WebState*)webState
                  completionHandler:(SuggestionsReadyCompletion)completion {
   DCHECK(GetPageURLAndCheckTrustLevel(webState, nullptr));
-  completion(BuildSuggestions(_fillData, formName, fieldIdentifier, fieldType),
-             self);
+  NSArray<FormSuggestion*>* rawSuggestions =
+      [self.suggestionHelper retrieveSuggestionsWithFormName:formName
+                                             fieldIdentifier:fieldIdentifier
+                                                   fieldType:fieldType];
+  PasswordSuggestionType suggestion_type =
+      rawSuggestions.count > 0 ? PasswordSuggestionType::CREDENTIALS
+                               : PasswordSuggestionType::SHOW_ALL;
+
+  NSMutableArray<FormSuggestion*>* suggestions = [NSMutableArray array];
+  for (FormSuggestion* rawSuggestion in rawSuggestions) {
+    [suggestions
+        addObject:[FormSuggestion
+                      suggestionWithValue:
+                          [rawSuggestion.value
+                              stringByAppendingString:kSuggestionSuffix]
+                       displayDescription:rawSuggestion.displayDescription
+                                     icon:nil
+                               identifier:0]];
+  }
+
+  // Once Manual Fallback is enabled the access to settings will exist as an
+  // option in the new passwords UI.
+  if (!autofill::features::IsPasswordManualFallbackEnabled()) {
+    // Add "Show all".
+    NSString* showAll = l10n_util::GetNSString(IDS_IOS_SHOW_ALL_PASSWORDS);
+    [suggestions addObject:[FormSuggestion suggestionWithValue:showAll
+                                            displayDescription:nil
+                                                          icon:nil
+                                                    identifier:1]];
+  }
+  if (suggestions.count) {
+    LogSuggestionShown(suggestion_type);
+  }
+
+  completion([suggestions copy], self);
 }
 
 - (void)didSelectSuggestion:(FormSuggestion*)suggestion
@@ -460,18 +402,19 @@
     return;
   }
   LogSuggestionClicked(PasswordSuggestionType::CREDENTIALS);
-  base::string16 value = base::SysNSStringToUTF16(suggestion.value);
   DCHECK([suggestion.value hasSuffix:kSuggestionSuffix]);
-  value.erase(value.length() - kSuggestionSuffix.length);
-  std::unique_ptr<FillData> fillData = _fillData.GetFillData(value);
+  NSString* username = [suggestion.value
+      substringToIndex:suggestion.value.length - kSuggestionSuffix.length];
+  std::unique_ptr<password_manager::FillData> fillData =
+      [self.suggestionHelper getFillDataForUsername:username];
 
   if (!fillData)
     completion();
 
-  [self.helper fillPasswordFormWithFillData:*fillData
-                          completionHandler:^(BOOL success) {
-                            completion();
-                          }];
+  [self.formHelper fillPasswordFormWithFillData:*fillData
+                              completionHandler:^(BOOL success) {
+                                completion();
+                              }];
 }
 
 #pragma mark - PasswordManagerClientDelegate
@@ -487,7 +430,7 @@
 }
 
 - (const GURL&)lastCommittedURL {
-  return self.helper.lastCommittedURL;
+  return self.formHelper.lastCommittedURL;
 }
 
 - (void)showSavePasswordInfoBar:
@@ -546,27 +489,20 @@
 
 - (void)fillPasswordForm:(const autofill::PasswordFormFillData&)formData
        completionHandler:(void (^)(BOOL))completionHandler {
-  _fillData.Add(formData);
-
-  if (_suggestionsAvailableCompletion) {
-    _suggestionsAvailableCompletion(&_fillData);
-    _suggestionsAvailableCompletion = nil;
-  }
-
-  [self.helper fillPasswordForm:formData completionHandler:completionHandler];
+  [self.suggestionHelper processWithPasswordFormFillData:formData];
+  [self.formHelper fillPasswordForm:formData
+                  completionHandler:completionHandler];
 }
 
 - (void)onNoSavedCredentials {
-  if (_suggestionsAvailableCompletion)
-    _suggestionsAvailableCompletion(nullptr);
-  _suggestionsAvailableCompletion = nil;
+  [self.suggestionHelper processWithNoSavedCredentials];
 }
 
-#pragma mark - PasswordControllerHelperDelegate
+#pragma mark - PasswordFormHelperDelegate
 
-- (void)helper:(PasswordControllerHelper*)helper
-    didSubmitForm:(const PasswordForm&)form
-      inMainFrame:(BOOL)inMainFrame {
+- (void)formHelper:(PasswordFormHelper*)formHelper
+     didSubmitForm:(const PasswordForm&)form
+       inMainFrame:(BOOL)inMainFrame {
   if (inMainFrame) {
     self.passwordManager->OnPasswordFormSubmitted(self.passwordManagerDriver,
                                                   form);
@@ -578,6 +514,13 @@
   }
 }
 
+#pragma mark - PasswordSuggestionHelperDelegate
+
+- (void)suggestionHelperShouldTriggerFormExtraction:
+    (PasswordSuggestionHelper*)suggestionHelper {
+  [self findPasswordFormsAndSendThemToPasswordStore];
+}
+
 #pragma mark - Private methods
 
 - (void)didFinishPasswordFormExtraction:
@@ -594,7 +537,8 @@
           ->DidShowPasswordFieldInInsecureContext();
     }
 
-    _sentRequestToStore = YES;
+    [self.suggestionHelper updateStateOnPasswordFormExtracted];
+
     // Invoke the password manager callback to autofill password forms
     // on the loaded page.
     self.passwordManager->OnPasswordFormsParsed(self.passwordManagerDriver,
@@ -616,8 +560,8 @@
   // Read all password forms from the page and send them to the password
   // manager.
   __weak PasswordController* weakSelf = self;
-  [self.helper findPasswordFormsWithCompletionHandler:^(
-                   const std::vector<autofill::PasswordForm>& forms) {
+  [self.formHelper findPasswordFormsWithCompletionHandler:^(
+                       const std::vector<autofill::PasswordForm>& forms) {
     [weakSelf didFinishPasswordFormExtraction:forms];
   }];
 }
diff --git a/ios/chrome/browser/passwords/password_controller_unittest.mm b/ios/chrome/browser/passwords/password_controller_unittest.mm
index 7de9876..97872e3 100644
--- a/ios/chrome/browser/passwords/password_controller_unittest.mm
+++ b/ios/chrome/browser/passwords/password_controller_unittest.mm
@@ -23,7 +23,7 @@
 #include "components/password_manager/core/browser/stub_password_manager_client.h"
 #include "components/password_manager/core/common/password_manager_pref_names.h"
 #import "components/password_manager/ios/js_password_manager.h"
-#import "components/password_manager/ios/password_controller_helper.h"
+#import "components/password_manager/ios/password_form_helper.h"
 #include "components/password_manager/ios/test_helpers.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/testing_pref_service.h"
@@ -150,8 +150,8 @@
 @interface PasswordController (
     Testing)<CRWWebStateObserver, FormSuggestionProvider>
 
-// Provides access to common helper logic for testing with mocks.
-@property(readonly) PasswordControllerHelper* helper;
+// Provides access to common form helper logic for testing with mocks.
+@property(readonly) PasswordFormHelper* formHelper;
 
 - (void)fillPasswordForm:(const PasswordFormFillData&)formData
        completionHandler:(void (^)(BOOL))completionHandler;
@@ -160,7 +160,7 @@
 
 @end
 
-@interface PasswordControllerHelper (Testing)
+@interface PasswordFormHelper (Testing)
 
 // Provides access to JavaScript Manager for testing with mocks.
 @property(readonly) JsPasswordManager* jsPasswordManager;
@@ -254,7 +254,7 @@
   // |failure_count| reaches |target_failure_count|, stop the partial mock
   // and let the original JavaScript manager execute.
   void SetFillPasswordFormFailureCount(int target_failure_count) {
-    id original_manager = passwordController_.helper.jsPasswordManager;
+    id original_manager = passwordController_.formHelper.jsPasswordManager;
     OCPartialMockObject* failing_manager =
         [OCMockObject partialMockForObject:original_manager];
     __block int failure_count = 0;
@@ -388,11 +388,12 @@
     LoadHtml(data.html_string);
     __block std::vector<PasswordForm> forms;
     __block BOOL block_was_called = NO;
-    [passwordController_.helper findPasswordFormsWithCompletionHandler:^(
-                                    const std::vector<PasswordForm>& result) {
-      block_was_called = YES;
-      forms = result;
-    }];
+    [passwordController_.formHelper
+        findPasswordFormsWithCompletionHandler:^(
+            const std::vector<PasswordForm>& result) {
+          block_was_called = YES;
+          forms = result;
+        }];
     EXPECT_TRUE(
         WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool() {
           return block_was_called;
@@ -489,7 +490,7 @@
                   form.username_element);
       }
     };
-    [passwordController_.helper
+    [passwordController_.formHelper
         extractSubmittedPasswordForm:FormName(data.number_of_forms_to_submit)
                    completionHandler:completion_handler];
     EXPECT_TRUE(
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm
index 6ae837c..0384f0f 100644
--- a/ios/chrome/browser/ui/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -3057,8 +3057,10 @@
 
 - (BOOL)isTabScrolledToTopForBubblePresenter:(BubblePresenter*)bubblePresenter {
   DCHECK(bubblePresenter == self.bubblePresenter);
-  CGPoint scrollOffset =
-      self.currentWebState->GetWebViewProxy().scrollViewProxy.contentOffset;
+  CRWWebViewScrollViewProxy* scrollProxy =
+      self.currentWebState->GetWebViewProxy().scrollViewProxy;
+  CGPoint scrollOffset = scrollProxy.contentOffset;
+  UIEdgeInsets contentInset = scrollProxy.contentInset;
 
   // If there is a native controller, use the native controller's scroll offset.
   id nativeController =
@@ -3068,7 +3070,7 @@
     scrollOffset = [nativeController scrollOffset];
   }
 
-  return CGPointEqualToPoint(scrollOffset, CGPointZero);
+  return AreCGFloatsEqual(scrollOffset.y, -contentInset.top);
 }
 
 #pragma mark - SnapshotGeneratorDelegate methods
@@ -4740,6 +4742,29 @@
   [nativeController focusFakebox];
 }
 
+- (void)unfocusOmniboxAndSwitchToTabWithURL:(const GURL&)URL {
+  // TODO(crbug.com/893121): Add animations.
+
+  // Cancelling the omnibox edit makes |URL| unsafe as it is not longer
+  // retained.
+  GURL retainedURL = URL;
+  [self.dispatcher cancelOmniboxEdit];
+
+  // TODO(crbug.com/893121): This should probably live in the WebState.
+  WebStateList* webStateList = self.tabModel.webStateList;
+  web::WebState* currentWebState = webStateList->GetActiveWebState();
+
+  for (NSInteger index = 0; index < webStateList->count(); index++) {
+    web::WebState* webState = webStateList->GetWebStateAt(index);
+
+    if (webState != currentWebState &&
+        retainedURL == webState->GetVisibleURL()) {
+      self.tabModel.webStateList->ActivateWebStateAt(index);
+      return;
+    }
+  }
+}
+
 #pragma mark - TabModelObserver methods
 
 // Observer method, tab inserted.
diff --git a/ios/chrome/browser/ui/browser_view_controller_unittest.mm b/ios/chrome/browser/ui/browser_view_controller_unittest.mm
index 3b080bf..dc30152 100644
--- a/ios/chrome/browser/ui/browser_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/browser_view_controller_unittest.mm
@@ -25,6 +25,7 @@
 #include "ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.h"
 #import "ios/chrome/browser/snapshots/snapshot_tab_helper.h"
 #import "ios/chrome/browser/tabs/tab.h"
+#import "ios/chrome/browser/tabs/tab_helper_util.h"
 #import "ios/chrome/browser/tabs/tab_model.h"
 #import "ios/chrome/browser/tabs/tab_model_observer.h"
 #import "ios/chrome/browser/tabs/tab_private.h"
@@ -42,6 +43,7 @@
 #import "ios/chrome/browser/web/error_page_content.h"
 #include "ios/chrome/browser/web_state_list/fake_web_state_list_delegate.h"
 #include "ios/chrome/browser/web_state_list/web_state_list.h"
+#import "ios/chrome/browser/web_state_list/web_state_opener.h"
 #import "ios/chrome/browser/web_state_list/web_usage_enabler/web_state_list_web_usage_enabler.h"
 #import "ios/chrome/browser/web_state_list/web_usage_enabler/web_state_list_web_usage_enabler_factory.h"
 #include "ios/chrome/grit/ios_strings.h"
@@ -51,7 +53,11 @@
 #import "ios/net/protocol_handler_util.h"
 #import "ios/testing/ocmock_complex_type_helper.h"
 #include "ios/web/public/referrer.h"
+#import "ios/web/public/test/fakes/fake_navigation_context.h"
+#import "ios/web/public/test/fakes/test_navigation_manager.h"
+#import "ios/web/public/test/fakes/test_web_state.h"
 #include "ios/web/public/test/test_web_thread_bundle.h"
+#import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
 #import "ios/web/public/web_state/ui/crw_native_content_provider.h"
 #import "ios/web/web_state/ui/crw_web_controller.h"
 #import "ios/web/web_state/web_state_impl.h"
@@ -241,6 +247,18 @@
     BlockCleanupTest::TearDown();
   }
 
+  // Returns a new unique_ptr containing a test webstate.
+  std::unique_ptr<web::TestWebState> CreateTestWebState() {
+    auto web_state = std::make_unique<web::TestWebState>();
+    web_state->SetBrowserState(chrome_browser_state_.get());
+    web_state->SetNavigationManager(
+        std::make_unique<web::TestNavigationManager>());
+    id mockJsInjectionReceiver = OCMClassMock([CRWJSInjectionReceiver class]);
+    web_state->SetJSInjectionReceiver(mockJsInjectionReceiver);
+    AttachTabHelpers(web_state.get(), true);
+    return web_state;
+  }
+
   MOCK_METHOD0(OnCompletionCalled, void());
 
   web::TestWebThreadBundle thread_bundle_;
@@ -256,6 +274,33 @@
   UIWindow* window_;
 };
 
+TEST_F(BrowserViewControllerTest, TestSwitchToTab) {
+  WebStateList* web_state_list = tabModel_.webStateList;
+  ASSERT_EQ(0, web_state_list->count());
+
+  std::unique_ptr<web::TestWebState> web_state = CreateTestWebState();
+  web::WebState* web_state_ptr = web_state.get();
+  web_state->SetCurrentURL(GURL("http://test/1"));
+  web_state_list->InsertWebState(0, std::move(web_state),
+                                 WebStateList::INSERT_FORCE_INDEX,
+                                 WebStateOpener());
+
+  std::unique_ptr<web::TestWebState> web_state_2 = CreateTestWebState();
+  web::WebState* web_state_ptr_2 = web_state_2.get();
+  GURL url("http://test/2");
+  web_state_2->SetCurrentURL(url);
+  web_state_list->InsertWebState(1, std::move(web_state_2),
+                                 WebStateList::INSERT_FORCE_INDEX,
+                                 WebStateOpener());
+
+  web_state_list->ActivateWebStateAt(0);
+
+  ASSERT_EQ(web_state_ptr, web_state_list->GetActiveWebState());
+
+  [bvc_.dispatcher unfocusOmniboxAndSwitchToTabWithURL:url];
+  EXPECT_EQ(web_state_ptr_2, web_state_list->GetActiveWebState());
+}
+
 TEST_F(BrowserViewControllerTest, TestTabSelected) {
   [bvc_ tabSelected:tab_ notifyToolbar:YES];
   EXPECT_EQ([[tab_ view] superview], [bvc_ contentArea]);
diff --git a/ios/chrome/browser/ui/commands/browser_commands.h b/ios/chrome/browser/ui/commands/browser_commands.h
index 8e91139..8acc5372 100644
--- a/ios/chrome/browser/ui/commands/browser_commands.h
+++ b/ios/chrome/browser/ui/commands/browser_commands.h
@@ -14,6 +14,7 @@
 #import "ios/chrome/browser/ui/commands/qr_scanner_commands.h"
 #import "ios/chrome/browser/ui/commands/snackbar_commands.h"
 
+class GURL;
 @class OpenNewTabCommand;
 @class ReadingListAddCommand;
 
@@ -116,6 +117,9 @@
 // omnibox.
 - (void)focusFakebox;
 
+// Unfocus omnibox then switch to the first tab displaying |URL|.
+- (void)unfocusOmniboxAndSwitchToTabWithURL:(const GURL&)URL;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_COMMANDS_BROWSER_COMMANDS_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_articles_header_item.mm b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_articles_header_item.mm
index f7e932f2..582681b 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_articles_header_item.mm
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_articles_header_item.mm
@@ -70,6 +70,14 @@
 
 #pragma mark - ContentSuggestionsArticlesHeaderCell
 
+@interface ContentSuggestionsArticlesHeaderCell ()
+
+@property(nonatomic, strong) NSArray<NSLayoutConstraint*>* standardConstraints;
+@property(nonatomic, strong)
+    NSArray<NSLayoutConstraint*>* accessibilityConstraints;
+
+@end
+
 @implementation ContentSuggestionsArticlesHeaderCell
 
 @synthesize button = _button;
@@ -83,34 +91,62 @@
     _label.translatesAutoresizingMaskIntoConstraints = NO;
     _label.font = [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
     _label.textColor = UIColorFromRGB(kLabelColorRGB, 1.0);
+    _label.adjustsFontForContentSizeCategory = YES;
     _label.adjustsFontSizeToFitWidth = YES;
 
     _button = [UIButton buttonWithType:UIButtonTypeSystem];
     _button.translatesAutoresizingMaskIntoConstraints = NO;
     _button.titleLabel.font =
         [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
+    _button.titleLabel.adjustsFontForContentSizeCategory = YES;
     [_button addTarget:self
                   action:@selector(buttonTapped)
         forControlEvents:UIControlEventTouchUpInside];
 
     [self.contentView addSubview:_button];
     [self.contentView addSubview:_label];
+
+    _standardConstraints = @[
+      [_label.bottomAnchor
+          constraintEqualToAnchor:self.contentView.bottomAnchor],
+      [_button.topAnchor constraintEqualToAnchor:self.contentView.topAnchor],
+      [_label.trailingAnchor
+          constraintLessThanOrEqualToAnchor:_button.leadingAnchor],
+      [_button.trailingAnchor
+          constraintEqualToAnchor:self.contentView.trailingAnchor
+                         constant:-kTextMargin],
+    ];
+    _accessibilityConstraints = @[
+      [_label.bottomAnchor
+          constraintEqualToAnchor:self.contentView.centerYAnchor],
+      [_button.topAnchor
+          constraintEqualToAnchor:self.contentView.centerYAnchor],
+      [_label.trailingAnchor
+          constraintLessThanOrEqualToAnchor:self.contentView.trailingAnchor
+                                   constant:-kTextMargin],
+      [_button.leadingAnchor
+          constraintEqualToAnchor:self.contentView.leadingAnchor
+                         constant:kTextMargin],
+      [_button.trailingAnchor
+          constraintLessThanOrEqualToAnchor:self.contentView.trailingAnchor
+                                   constant:-kTextMargin],
+    ];
+
     [NSLayoutConstraint activateConstraints:@[
       [_label.leadingAnchor
           constraintEqualToAnchor:self.contentView.leadingAnchor
                          constant:kTextMargin],
       [_label.topAnchor constraintEqualToAnchor:self.contentView.topAnchor],
-      [_label.bottomAnchor
-          constraintEqualToAnchor:self.contentView.bottomAnchor],
-      [_button.trailingAnchor
-          constraintEqualToAnchor:self.contentView.trailingAnchor
-                         constant:-kTextMargin],
-      [_button.topAnchor constraintEqualToAnchor:self.contentView.topAnchor],
       [_button.bottomAnchor
           constraintEqualToAnchor:self.contentView.bottomAnchor],
-      [_label.trailingAnchor
-          constraintLessThanOrEqualToAnchor:_button.leadingAnchor]
     ]];
+
+    if (ContentSizeCategoryIsAccessibilityCategory(
+            self.traitCollection.preferredContentSizeCategory)) {
+      [NSLayoutConstraint activateConstraints:_accessibilityConstraints];
+    } else {
+      [NSLayoutConstraint activateConstraints:_standardConstraints];
+    }
   }
   return self;
 }
@@ -120,6 +156,27 @@
   self.delegate = nil;
 }
 
+#pragma mark - UIView
+
+- (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection {
+  [super traitCollectionDidChange:previousTraitCollection];
+  BOOL isCurrentCategoryAccessibility =
+      ContentSizeCategoryIsAccessibilityCategory(
+          self.traitCollection.preferredContentSizeCategory);
+  BOOL isPreviousCategoryAccessibility =
+      ContentSizeCategoryIsAccessibilityCategory(
+          previousTraitCollection.preferredContentSizeCategory);
+  if (isCurrentCategoryAccessibility != isPreviousCategoryAccessibility) {
+    if (isCurrentCategoryAccessibility) {
+      [NSLayoutConstraint deactivateConstraints:self.standardConstraints];
+      [NSLayoutConstraint activateConstraints:self.accessibilityConstraints];
+    } else {
+      [NSLayoutConstraint deactivateConstraints:self.accessibilityConstraints];
+      [NSLayoutConstraint activateConstraints:self.standardConstraints];
+    }
+  }
+}
+
 #pragma mark Private
 
 // Callback for the button action.
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_cell.h b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_cell.h
index 3fc5d52..04c269f1 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_cell.h
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_cell.h
@@ -7,14 +7,14 @@
 
 #import "ios/third_party/material_components_ios/src/components/CollectionCells/src/MaterialCollectionCells.h"
 
-@class FaviconViewNew;
+@class FaviconView;
 
 // Corresponding cell for an article in the suggestions.
 @interface ContentSuggestionsCell : MDCCollectionViewCell
 
 @property(nonatomic, readonly, strong) UILabel* titleLabel;
 // View for displaying the favicon.
-@property(nonatomic, readonly, strong) FaviconViewNew* faviconView;
+@property(nonatomic, readonly, strong) FaviconView* faviconView;
 // Whether the image should be displayed.
 @property(nonatomic, assign) BOOL displayImage;
 
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_cell.mm b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_cell.mm
index 1f434a7..73c06350 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_cell.mm
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_cell.mm
@@ -90,7 +90,7 @@
     _noImageIcon = [[UIImageView alloc] initWithFrame:CGRectZero];
     _additionalInformationLabel = [[UILabel alloc] initWithFrame:CGRectZero];
     _contentImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
-    _faviconView = [[FaviconViewNew alloc] init];
+    _faviconView = [[FaviconView alloc] init];
 
     _contentImageView.contentMode = UIViewContentModeScaleAspectFill;
     _contentImageView.clipsToBounds = YES;
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_cell.h b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_cell.h
index c368321..e6fd6f5 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_cell.h
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_cell.h
@@ -7,14 +7,14 @@
 
 #import "ios/third_party/material_components_ios/src/components/CollectionCells/src/MaterialCollectionCells.h"
 
-@class FaviconViewNew;
+@class FaviconView;
 
 // Associated cell to display a Most Visited tile based on the suggestion.
 // It displays the favicon for this Most Visited suggestion and its title.
 @interface ContentSuggestionsMostVisitedCell : MDCCollectionViewCell
 
 // FaviconView displaying the favicon.
-@property(nonatomic, strong, readonly, nonnull) FaviconViewNew* faviconView;
+@property(nonatomic, strong, readonly, nonnull) FaviconView* faviconView;
 
 // Title of the Most Visited.
 @property(nonatomic, strong, readonly, nonnull) UILabel* titleLabel;
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_cell.mm b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_cell.mm
index c0c11286..09a8bc3 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_cell.mm
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_cell.mm
@@ -31,7 +31,7 @@
     _titleLabel.preferredMaxLayoutWidth = [[self class] defaultSize].width;
     _titleLabel.numberOfLines = kLabelNumLines;
 
-    _faviconView = [[FaviconViewNew alloc] init];
+    _faviconView = [[FaviconView alloc] init];
     _faviconView.font = [UIFont systemFontOfSize:22];
     _titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
     _faviconView.translatesAutoresizingMaskIntoConstraints = NO;
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_text_item.mm b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_text_item.mm
index e95c525..6730895 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_text_item.mm
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_text_item.mm
@@ -6,8 +6,6 @@
 
 #import "ios/chrome/browser/ui/collection_view/cells/collection_view_text_cell.h"
 #import "ios/chrome/browser/ui/content_suggestions/identifier/content_suggestion_identifier.h"
-#import "ios/third_party/material_components_ios/src/components/Palettes/src/MaterialPalettes.h"
-#import "ios/third_party/material_components_ios/src/components/Typography/src/MaterialTypography.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -59,16 +57,20 @@
 // Configures the |textLabel|.
 - (void)configureTextLabel:(UILabel*)textLabel {
   textLabel.text = self.text;
-  textLabel.textColor = [[MDCPalette greyPalette] tint900];
-  textLabel.font = [MDCTypography body2Font];
+  textLabel.textColor = [UIColor colorWithWhite:0.13 alpha:1];
+  textLabel.font =
+      [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline];
+  textLabel.adjustsFontForContentSizeCategory = YES;
   textLabel.numberOfLines = 0;
 }
 
 // Configures the |detailTextLabel|.
 - (void)configureDetailTextLabel:(UILabel*)detailTextLabel {
   detailTextLabel.text = self.detailText;
-  detailTextLabel.textColor = [[MDCPalette greyPalette] tint500];
-  detailTextLabel.font = [MDCTypography body1Font];
+  detailTextLabel.textColor = [UIColor colorWithWhite:0.62 alpha:1];
+  detailTextLabel.font =
+      [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
+  detailTextLabel.adjustsFontForContentSizeCategory = YES;
   detailTextLabel.numberOfLines = 0;
 }
 
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
index 1221f87f..b7c36cc 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
@@ -439,9 +439,16 @@
   if ([self.collectionUpdater isHeaderSection:section]) {
     return CGSizeMake(0, [self.headerSynchronizer headerHeight]);
   }
-  return [super collectionView:collectionView
-                               layout:collectionViewLayout
-      referenceSizeForHeaderInSection:section];
+  CGSize defaultSize = [super collectionView:collectionView
+                                      layout:collectionViewLayout
+             referenceSizeForHeaderInSection:section];
+  if (ContentSizeCategoryIsAccessibilityCategory(
+          self.traitCollection.preferredContentSizeCategory) &&
+      [self.collectionUpdater isContentSuggestionsSection:section]) {
+    // Double the size of the header as it is now on two lines.
+    defaultSize.height *= 2;
+  }
+  return defaultSize;
 }
 
 - (BOOL)collectionView:(nonnull UICollectionView*)collectionView
diff --git a/ios/chrome/browser/ui/infobars/confirm_infobar_view.mm b/ios/chrome/browser/ui/infobars/confirm_infobar_view.mm
index d620558c..d59fdf85 100644
--- a/ios/chrome/browser/ui/infobars/confirm_infobar_view.mm
+++ b/ios/chrome/browser/ui/infobars/confirm_infobar_view.mm
@@ -201,32 +201,32 @@
 @end
 
 @implementation SwitchView {
-  UISwitch* switch_;
-  CGFloat preferredTotalWidth_;
+  UISwitch* _switch;
+  CGFloat _preferredTotalWidth;
   // Layout metrics for calculating item placement.
-  const LayoutMetrics* metrics_;
+  const LayoutMetrics* _metrics;
 }
 
 - (instancetype)initWithText:(NSString*)labelText isOn:(BOOL)isOn {
-  metrics_ = &kLayoutMetrics;
+  _metrics = &kLayoutMetrics;
 
   self = [super initWithText:labelText];
   if (!self)
     return nil;
 
   self.label.textColor = [UIColor blackColor];
-  switch_ = [[UISwitch alloc] initWithFrame:CGRectZero];
-  switch_.exclusiveTouch = YES;
-  switch_.accessibilityLabel = labelText;
-  switch_.onTintColor = [[MDCPalette cr_bluePalette] tint500];
-  switch_.on = isOn;
+  _switch = [[UISwitch alloc] initWithFrame:CGRectZero];
+  _switch.exclusiveTouch = YES;
+  _switch.accessibilityLabel = labelText;
+  _switch.onTintColor = [[MDCPalette cr_bluePalette] tint500];
+  _switch.on = isOn;
 
   // Computes the size and initializes the view.
   CGSize labelSize = self.label.frame.size;
-  CGSize switchSize = switch_.frame.size;
+  CGSize switchSize = _switch.frame.size;
   CGRect frameRect = CGRectMake(
       0, 0,
-      labelSize.width + metrics_->space_between_widgets + switchSize.width,
+      labelSize.width + _metrics->space_between_widgets + switchSize.width,
       std::max(labelSize.height, switchSize.height));
   [self setFrame:frameRect];
 
@@ -244,30 +244,30 @@
   switchFrame = AlignRectOriginAndSizeToPixels(switchFrame);
 
   [self.label setFrame:labelFrame];
-  [switch_ setFrame:switchFrame];
-  preferredTotalWidth_ = CGRectGetMaxX(switchFrame);
+  [_switch setFrame:switchFrame];
+  _preferredTotalWidth = CGRectGetMaxX(switchFrame);
   self.preferredLabelWidth = CGRectGetMaxX(labelFrame);
 
   [self addSubview:self.label];
-  [self addSubview:switch_];
+  [self addSubview:_switch];
   return self;
 }
 
 - (void)setTag:(NSInteger)tag target:(id)target action:(SEL)action {
-  [switch_ setTag:tag];
-  [switch_ addTarget:target
+  [_switch setTag:tag];
+  [_switch addTarget:target
                 action:action
       forControlEvents:UIControlEventValueChanged];
 }
 
 - (CGFloat)heightRequiredForFooterWithWidth:(CGFloat)width layout:(BOOL)layout {
   CGFloat widthLeftForLabel =
-      width - [switch_ frame].size.width - metrics_->space_between_widgets;
+      width - [_switch frame].size.width - _metrics->space_between_widgets;
   CGSize maxSize = CGSizeMake(widthLeftForLabel, CGFLOAT_MAX);
   CGSize labelSize =
       [[self.label text] cr_boundingSizeWithSize:maxSize
                                             font:[self.label font]];
-  CGFloat viewHeight = std::max(labelSize.height, [switch_ frame].size.height);
+  CGFloat viewHeight = std::max(labelSize.height, [_switch frame].size.height);
   if (layout) {
     // Lays out the label and the switch to fit in {width, viewHeight}.
     CGRect newLabelFrame;
@@ -278,17 +278,17 @@
     [self.label setFrame:newLabelFrame];
     CGRect newSwitchFrame;
     newSwitchFrame.origin.x =
-        CGRectGetMaxX(newLabelFrame) + metrics_->space_between_widgets;
-    newSwitchFrame.origin.y = (viewHeight - [switch_ frame].size.height) / 2;
-    newSwitchFrame.size = [switch_ frame].size;
+        CGRectGetMaxX(newLabelFrame) + _metrics->space_between_widgets;
+    newSwitchFrame.origin.y = (viewHeight - [_switch frame].size.height) / 2;
+    newSwitchFrame.size = [_switch frame].size;
     newSwitchFrame = AlignRectOriginAndSizeToPixels(newSwitchFrame);
-    [switch_ setFrame:newSwitchFrame];
+    [_switch setFrame:newSwitchFrame];
   }
   return viewHeight;
 }
 
 - (CGFloat)preferredWidth {
-  return preferredTotalWidth_;
+  return _preferredTotalWidth;
 }
 
 @end
diff --git a/ios/chrome/browser/ui/omnibox/autocomplete_result_consumer.h b/ios/chrome/browser/ui/omnibox/autocomplete_result_consumer.h
index 06a4ee45..9658e21 100644
--- a/ios/chrome/browser/ui/omnibox/autocomplete_result_consumer.h
+++ b/ios/chrome/browser/ui/omnibox/autocomplete_result_consumer.h
@@ -22,7 +22,7 @@
 // Tells the delegate when a suggestion in|row| was chosen for appending to
 // omnibox.
 - (void)autocompleteResultConsumer:(id<AutocompleteResultConsumer>)sender
-          didSelectRowForAppending:(NSUInteger)row;
+        didTapTrailingButtonForRow:(NSUInteger)row;
 // Tells the delegate when a suggestion in |row| was removed.
 - (void)autocompleteResultConsumer:(id<AutocompleteResultConsumer>)sender
            didSelectRowForDeletion:(NSUInteger)row;
diff --git a/ios/chrome/browser/ui/omnibox/popup/BUILD.gn b/ios/chrome/browser/ui/omnibox/popup/BUILD.gn
index d92c3c5f..374dcdc5 100644
--- a/ios/chrome/browser/ui/omnibox/popup/BUILD.gn
+++ b/ios/chrome/browser/ui/omnibox/popup/BUILD.gn
@@ -52,6 +52,7 @@
     "self_sizing_table_view.mm",
   ]
   deps = [
+    "resources:omnibox_popup_tab_match",
     "//base",
     "//components/image_fetcher/ios",
     "//components/omnibox/browser",
@@ -73,9 +74,12 @@
   ]
   deps = [
     ":popup",
+    ":popup_internal",
     "//base",
+    "//components/omnibox/browser",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser",
+    "//ios/chrome/browser/ui/omnibox:omnibox_internal",
     "//testing/gtest",
     "//ui/base",
   ]
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_coordinator.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_coordinator.mm
index 189072e3..20d31d8 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_coordinator.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_coordinator.mm
@@ -61,6 +61,7 @@
   self.mediator =
       [[OmniboxPopupMediator alloc] initWithFetcher:std::move(imageFetcher)
                                            delegate:_popupView.get()];
+  self.mediator.dispatcher = (id<BrowserCommands>)self.dispatcher;
   self.popupViewController = [[OmniboxPopupViewController alloc] init];
   self.popupViewController.incognito = self.browserState->IsOffTheRecord();
 
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_mediator.h b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_mediator.h
index 625210e9..16997a9 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_mediator.h
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_mediator.h
@@ -11,6 +11,7 @@
 #import "ios/chrome/browser/ui/omnibox/autocomplete_result_consumer.h"
 #import "ios/chrome/browser/ui/omnibox/image_retriever.h"
 
+@protocol BrowserCommands;
 @class OmniboxPopupPresenter;
 
 namespace image_fetcher {
@@ -48,6 +49,7 @@
 // Updates the popup with the |results|.
 - (void)updateWithResults:(const AutocompleteResult&)results;
 
+@property(nonatomic, weak) id<BrowserCommands> dispatcher;
 @property(nonatomic, weak) id<AutocompleteResultConsumer> consumer;
 @property(nonatomic, assign, getter=isIncognito) BOOL incognito;
 // Whether the popup is open.
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_mediator.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_mediator.mm
index ded70cc..e7a0859 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_mediator.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_mediator.mm
@@ -10,6 +10,7 @@
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/autocomplete_result.h"
+#import "ios/chrome/browser/ui/commands/browser_commands.h"
 #import "ios/chrome/browser/ui/omnibox/autocomplete_match_formatter.h"
 #import "ios/chrome/browser/ui/omnibox/popup/omnibox_popup_presenter.h"
 
@@ -113,19 +114,22 @@
 }
 
 - (void)autocompleteResultConsumer:(id<AutocompleteResultConsumer>)sender
-          didSelectRowForAppending:(NSUInteger)row {
+        didTapTrailingButtonForRow:(NSUInteger)row {
   const AutocompleteMatch& match =
       ((const AutocompleteResult&)_currentResult).match_at(row);
 
-  if (AutocompleteMatch::IsSearchType(match.type)) {
-    base::RecordAction(
-        base::UserMetricsAction("MobileOmniboxRefineSuggestion.Search"));
+  if (match.has_tab_match) {
+    [self.dispatcher unfocusOmniboxAndSwitchToTabWithURL:match.destination_url];
   } else {
-    base::RecordAction(
-        base::UserMetricsAction("MobileOmniboxRefineSuggestion.Url"));
+    if (AutocompleteMatch::IsSearchType(match.type)) {
+      base::RecordAction(
+          base::UserMetricsAction("MobileOmniboxRefineSuggestion.Search"));
+    } else {
+      base::RecordAction(
+          base::UserMetricsAction("MobileOmniboxRefineSuggestion.Url"));
+    }
+    _delegate->OnMatchSelectedForAppending(match);
   }
-
-  _delegate->OnMatchSelectedForAppending(match);
 }
 
 - (void)autocompleteResultConsumer:(id<AutocompleteResultConsumer>)sender
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_row.h b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_row.h
index d0e5196..74a208efd 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_row.h
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_row.h
@@ -24,8 +24,11 @@
 
 @property(nonatomic, readonly, strong) UIImageView* imageView;
 @property(nonatomic, readonly, strong) UIImageView* answerImageView;
-@property(nonatomic, readonly, strong) UIButton* appendButton;
+@property(nonatomic, readonly, strong) UIButton* trailingButton;
 @property(nonatomic, assign) CGFloat rowHeight;
+// Whether this row is displaying a TabMatch. If YES, the trailing icon is
+// updated to reflect that.
+@property(nonatomic, assign, getter=isTabMatch) BOOL tabMatch;
 
 // Initialize the row with the given incognito state. The colors and styling are
 // dependent on whether or not the row is displayed in incognito mode.
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_row.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_row.mm
index 44e31b0..ea0f8a3 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_row.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_row.mm
@@ -20,8 +20,8 @@
 // Side (w or h) length for the leading image view.
 const CGFloat kImageViewSizeUIRefresh = 28.0;
 const CGFloat kImageViewCornerRadiusUIRefresh = 7.0;
-const CGFloat kAppendButtonTrailingMargin = 4;
-const CGFloat kAppendButtonSize = 48.0;
+const CGFloat kTrailingButtonTrailingMargin = 4;
+const CGFloat kTrailingButtonSize = 48.0;
 }
 
 @interface OmniboxPopupRow () {
@@ -29,7 +29,7 @@
 }
 
 // Set the append button normal and highlighted images.
-- (void)updateAppendButtonImages;
+- (void)updateTrailingButtonImages;
 
 @end
 
@@ -38,7 +38,7 @@
 @synthesize textTruncatingLabel = _textTruncatingLabel;
 @synthesize detailTruncatingLabel = _detailTruncatingLabel;
 @synthesize detailAnswerLabel = _detailAnswerLabel;
-@synthesize appendButton = _appendButton;
+@synthesize trailingButton = _trailingButton;
 @synthesize answerImageView = _answerImageView;
 @synthesize imageView = _imageView;
 @synthesize rowHeight = _rowHeight;
@@ -73,12 +73,12 @@
     _detailAnswerLabel.lineBreakMode = NSLineBreakByTruncatingTail;
     [self.contentView addSubview:_detailAnswerLabel];
 
-    _appendButton = [UIButton buttonWithType:UIButtonTypeCustom];
-    [_appendButton setContentMode:UIViewContentModeRight];
-    [self updateAppendButtonImages];
+    _trailingButton = [UIButton buttonWithType:UIButtonTypeCustom];
+    [_trailingButton setContentMode:UIViewContentModeRight];
+    [self updateTrailingButtonImages];
     // TODO(justincohen): Consider using the UITableViewCell's accessory view.
     // The current implementation is from before using a UITableViewCell.
-    [self.contentView addSubview:_appendButton];
+    [self.contentView addSubview:_trailingButton];
 
     // Before UI Refresh, the leading icon is only displayed on iPad. In UI
     // Refresh, it's only in Regular x Regular size class.
@@ -134,11 +134,11 @@
 
   LayoutRect trailingAccessoryLayout =
       LayoutRectMake(CGRectGetWidth(self.contentView.bounds) -
-                         kAppendButtonSize - kAppendButtonTrailingMargin,
+                         kTrailingButtonSize - kTrailingButtonTrailingMargin,
                      CGRectGetWidth(self.contentView.bounds),
-                     floor((_rowHeight - kAppendButtonSize) / 2),
-                     kAppendButtonSize, kAppendButtonSize);
-  _appendButton.frame = LayoutRectGetRect(trailingAccessoryLayout);
+                     floor((_rowHeight - kTrailingButtonSize) / 2),
+                     kTrailingButtonSize, kTrailingButtonSize);
+  _trailingButton.frame = LayoutRectGetRect(trailingAccessoryLayout);
 }
 
 - (void)updateLeadingImage:(UIImage*)image {
@@ -173,27 +173,38 @@
   [self updateHighlightBackground:highlighted];
 }
 
-- (void)updateAppendButtonImages {
-  int appendResourceID = _incognito
-                             ? IDR_IOS_OMNIBOX_KEYBOARD_VIEW_APPEND_INCOGNITO
-                             : IDR_IOS_OMNIBOX_KEYBOARD_VIEW_APPEND;
-  UIImage* appendImage = NativeReversableImage(appendResourceID, YES);
+- (void)setTabMatch:(BOOL)tabMatch {
+  _tabMatch = tabMatch;
+  [self updateTrailingButtonImages];
+}
+
+- (void)updateTrailingButtonImages {
+  UIImage* appendImage = nil;
+  if (self.tabMatch) {
+    appendImage = [UIImage imageNamed:@"omnibox_popup_tab_match"];
+  } else {
+    int appendResourceID = _incognito
+                               ? IDR_IOS_OMNIBOX_KEYBOARD_VIEW_APPEND_INCOGNITO
+                               : IDR_IOS_OMNIBOX_KEYBOARD_VIEW_APPEND;
+    appendImage = NativeReversableImage(appendResourceID, YES);
+  }
   if (IsUIRefreshPhase1Enabled()) {
     appendImage =
         [appendImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
-    _appendButton.tintColor = _incognito ? [UIColor colorWithWhite:1 alpha:0.5]
-                                         : [UIColor colorWithWhite:0 alpha:0.3];
+    _trailingButton.tintColor = _incognito
+                                    ? [UIColor colorWithWhite:1 alpha:0.5]
+                                    : [UIColor colorWithWhite:0 alpha:0.3];
   } else {
     int appendSelectedResourceID =
         _incognito ? IDR_IOS_OMNIBOX_KEYBOARD_VIEW_APPEND_INCOGNITO_HIGHLIGHTED
                    : IDR_IOS_OMNIBOX_KEYBOARD_VIEW_APPEND_HIGHLIGHTED;
     UIImage* appendImageSelected =
         NativeReversableImage(appendSelectedResourceID, YES);
-    [_appendButton setImage:appendImageSelected
-                   forState:UIControlStateHighlighted];
+    [_trailingButton setImage:appendImageSelected
+                     forState:UIControlStateHighlighted];
   }
 
-  [_appendButton setImage:appendImage forState:UIControlStateNormal];
+  [_trailingButton setImage:appendImage forState:UIControlStateNormal];
 }
 
 - (NSString*)accessibilityLabel {
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller.mm
index 16bb342..05ebb66 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller.mm
@@ -153,10 +153,10 @@
         [NSString stringWithFormat:@"omnibox suggestion %i", i];
     row.autoresizingMask = UIViewAutoresizingFlexibleWidth;
     [rowsBuilder addObject:row];
-    [row.appendButton addTarget:self
-                         action:@selector(appendButtonTapped:)
-               forControlEvents:UIControlEventTouchUpInside];
-    [row.appendButton setTag:i];
+    [row.trailingButton addTarget:self
+                           action:@selector(trailingButtonTapped:)
+                 forControlEvents:UIControlEventTouchUpInside];
+    [row.trailingButton setTag:i];
     row.rowHeight = kRowHeight;
   }
   _rows = [rowsBuilder copy];
@@ -284,7 +284,7 @@
   const CGFloat kDetailCellTopPadding = 26;
   const CGFloat kTextLabelHeight = 24;
   const CGFloat kTextDetailLabelHeight = 22;
-  const CGFloat kAppendButtonWidth = 40;
+  const CGFloat kTrailingButtonWidth = 40;
   const CGFloat kAnswerLabelHeight = 36;
   const CGFloat kAnswerImageWidth = 30;
   const CGFloat kAnswerImageLeftPadding = -1;
@@ -308,7 +308,7 @@
         kTextCellLeadingPadding + kAnswerImageLeftPadding;
     if (alignmentRight) {
       imageLeftPadding =
-          row.frame.size.width - (kAnswerImageWidth + kAppendButtonWidth);
+          row.frame.size.width - (kAnswerImageWidth + kTrailingButtonWidth);
     }
     CGFloat imageTopPadding = kDetailCellTopPadding + kAnswerImageTopPadding;
     row.answerImageView.frame =
@@ -396,22 +396,24 @@
     [row updateLeadingImage:image];
   }
 
+  row.tabMatch = match.isTabMatch;
+
   // Show append button for search history/search suggestions as the right
   // control element (aka an accessory element of a table view cell).
-  row.appendButton.hidden = !match.isAppendable;
-  [row.appendButton cancelTrackingWithEvent:nil];
+  row.trailingButton.hidden = !match.isAppendable && !match.isTabMatch;
+  [row.trailingButton cancelTrackingWithEvent:nil];
 
   // If a right accessory element is present or the text alignment is right
   // aligned, adjust the width to align with the accessory element.
   if (match.isAppendable || alignmentRight) {
     LayoutRect layout =
         LayoutRectForRectInBoundingRect(textLabel.frame, self.view.frame);
-    layout.size.width -= kAppendButtonWidth;
+    layout.size.width -= kTrailingButtonWidth;
     textLabel.frame = LayoutRectGetRect(layout);
     layout =
         LayoutRectForRectInBoundingRect(detailTextLabel.frame, self.view.frame);
     layout.size.width -=
-        kAppendButtonWidth + (match.hasImage ? answerImagePadding : 0);
+        kTrailingButtonWidth + (match.hasImage ? answerImagePadding : 0);
     detailTextLabel.frame = LayoutRectGetRect(layout);
   }
 
@@ -524,9 +526,10 @@
 #pragma mark -
 #pragma mark Action for append UIButton
 
-- (void)appendButtonTapped:(id)sender {
+- (void)trailingButtonTapped:(id)sender {
   NSUInteger row = [sender tag];
-  [self.delegate autocompleteResultConsumer:self didSelectRowForAppending:row];
+  [self.delegate autocompleteResultConsumer:self
+                 didTapTrailingButtonForRow:row];
 }
 
 #pragma mark -
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller_unittest.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller_unittest.mm
index 3117c0e..e864000 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller_unittest.mm
@@ -4,6 +4,9 @@
 
 #import "ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller.h"
 
+#include "components/omnibox/browser/autocomplete_match.h"
+#import "ios/chrome/browser/ui/omnibox/autocomplete_match_formatter.h"
+#import "ios/chrome/browser/ui/omnibox/popup/omnibox_popup_row.h"
 #include "testing/gtest_mac.h"
 #include "testing/platform_test.h"
 
@@ -23,14 +26,14 @@
   OmniboxPopupViewController* popup_view_controller_;
 };
 
-TEST_F(OmniboxPopupViewControllerTest, hasCellsWhenShortcutsEnabled) {
+TEST_F(OmniboxPopupViewControllerTest, HasCellsWhenShortcutsEnabled) {
   // This test makes an assumption that this view controller is a datasource for
   // a table view. Rewrite this test if this is not the case anymore.
   EXPECT_TRUE([popup_view_controller_
       conformsToProtocol:@protocol(UITableViewDataSource)]);
   id<UITableViewDataSource> datasource =
       (id<UITableViewDataSource>)popup_view_controller_;
-  UITableView* tableView = [[UITableView alloc] init];
+  UITableView* table_view = [[UITableView alloc] init];
 
   // A stub view controller.
   UIViewController* shortcutsViewController = [[UIViewController alloc] init];
@@ -40,17 +43,52 @@
 
   // Check that the shorcuts row doesn't appear.
   [popup_view_controller_ updateMatches:@[] withAnimation:NO];
-  EXPECT_EQ([datasource tableView:tableView numberOfRowsInSection:0], 0);
+  EXPECT_EQ([datasource tableView:table_view numberOfRowsInSection:0], 0);
 
   // Enable shortcuts and verify they appear. When enabling, the view controller
   // has to be non-nil.
   popup_view_controller_.shortcutsViewController = shortcutsViewController;
   popup_view_controller_.shortcutsEnabled = YES;
-  EXPECT_EQ([datasource tableView:tableView numberOfRowsInSection:0], 1);
+  EXPECT_EQ([datasource tableView:table_view numberOfRowsInSection:0], 1);
 
   // Disable and verify it disappears again.
   popup_view_controller_.shortcutsEnabled = NO;
-  EXPECT_EQ([datasource tableView:tableView numberOfRowsInSection:0], 0);
+  EXPECT_EQ([datasource tableView:table_view numberOfRowsInSection:0], 0);
+}
+
+TEST_F(OmniboxPopupViewControllerTest, HasTabMatch) {
+  EXPECT_TRUE([popup_view_controller_
+      conformsToProtocol:@protocol(UITableViewDataSource)]);
+  id<UITableViewDataSource> datasource =
+      (id<UITableViewDataSource>)popup_view_controller_;
+  UITableView* table_view = [[UITableView alloc] init];
+
+  // Check that if the match has a tab match, the cell's trailing button is
+  // visible.
+  AutocompleteMatch match;
+  match.has_tab_match = true;
+  AutocompleteMatchFormatter* formatter =
+      [[AutocompleteMatchFormatter alloc] initWithMatch:match];
+  [popup_view_controller_ updateMatches:@[ formatter ] withAnimation:NO];
+
+  EXPECT_EQ([datasource tableView:table_view numberOfRowsInSection:0], 1);
+  OmniboxPopupRow* cell = static_cast<OmniboxPopupRow*>([datasource
+                  tableView:table_view
+      cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]]);
+
+  EXPECT_FALSE(cell.trailingButton.hidden);
+
+  // Check that it is not the case if the tab match isn't visible.
+  match.has_tab_match = false;
+  formatter = [[AutocompleteMatchFormatter alloc] initWithMatch:match];
+  [popup_view_controller_ updateMatches:@[ formatter ] withAnimation:NO];
+
+  EXPECT_EQ([datasource tableView:table_view numberOfRowsInSection:0], 1);
+  cell = static_cast<OmniboxPopupRow*>([datasource
+                  tableView:table_view
+      cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]]);
+
+  EXPECT_TRUE(cell.trailingButton.hidden);
 }
 
 }  // namespace
diff --git a/ios/chrome/browser/ui/omnibox/popup/resources/BUILD.gn b/ios/chrome/browser/ui/omnibox/popup/resources/BUILD.gn
new file mode 100644
index 0000000..c7ac8e48e
--- /dev/null
+++ b/ios/chrome/browser/ui/omnibox/popup/resources/BUILD.gn
@@ -0,0 +1,14 @@
+# 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.
+
+import("//build/config/ios/asset_catalog.gni")
+
+imageset("omnibox_popup_tab_match") {
+  sources = [
+    "omnibox_popup_tab_match.imageset/Contents.json",
+    "omnibox_popup_tab_match.imageset/omnibox_popup_tab_match.png",
+    "omnibox_popup_tab_match.imageset/omnibox_popup_tab_match@2x.png",
+    "omnibox_popup_tab_match.imageset/omnibox_popup_tab_match@3x.png",
+  ]
+}
diff --git a/ios/chrome/browser/ui/omnibox/popup/resources/omnibox_popup_tab_match.imageset/Contents.json b/ios/chrome/browser/ui/omnibox/popup/resources/omnibox_popup_tab_match.imageset/Contents.json
new file mode 100644
index 0000000..3b1fa9d
--- /dev/null
+++ b/ios/chrome/browser/ui/omnibox/popup/resources/omnibox_popup_tab_match.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+    "images": [
+        {
+            "idiom": "universal",
+            "scale": "1x",
+            "filename": "omnibox_popup_tab_match.png"
+        },
+        {
+            "idiom": "universal",
+            "scale": "2x",
+            "filename": "omnibox_popup_tab_match@2x.png"
+        },
+        {
+            "idiom": "universal",
+            "scale": "3x",
+            "filename": "omnibox_popup_tab_match@3x.png"
+        }
+    ],
+    "info": {
+        "version": 1,
+        "author": "xcode"
+    }
+}
diff --git a/ios/chrome/browser/ui/omnibox/popup/resources/omnibox_popup_tab_match.imageset/omnibox_popup_tab_match.png b/ios/chrome/browser/ui/omnibox/popup/resources/omnibox_popup_tab_match.imageset/omnibox_popup_tab_match.png
new file mode 100644
index 0000000..0ee350d
--- /dev/null
+++ b/ios/chrome/browser/ui/omnibox/popup/resources/omnibox_popup_tab_match.imageset/omnibox_popup_tab_match.png
Binary files differ
diff --git a/ios/chrome/browser/ui/omnibox/popup/resources/omnibox_popup_tab_match.imageset/omnibox_popup_tab_match@2x.png b/ios/chrome/browser/ui/omnibox/popup/resources/omnibox_popup_tab_match.imageset/omnibox_popup_tab_match@2x.png
new file mode 100644
index 0000000..3c5140b
--- /dev/null
+++ b/ios/chrome/browser/ui/omnibox/popup/resources/omnibox_popup_tab_match.imageset/omnibox_popup_tab_match@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/omnibox/popup/resources/omnibox_popup_tab_match.imageset/omnibox_popup_tab_match@3x.png b/ios/chrome/browser/ui/omnibox/popup/resources/omnibox_popup_tab_match.imageset/omnibox_popup_tab_match@3x.png
new file mode 100644
index 0000000..9bf6189
--- /dev/null
+++ b/ios/chrome/browser/ui/omnibox/popup/resources/omnibox_popup_tab_match.imageset/omnibox_popup_tab_match@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/reading_list/BUILD.gn b/ios/chrome/browser/ui/reading_list/BUILD.gn
index f56c4d5..1c2ccc7a 100644
--- a/ios/chrome/browser/ui/reading_list/BUILD.gn
+++ b/ios/chrome/browser/ui/reading_list/BUILD.gn
@@ -73,8 +73,6 @@
 source_set("reading_list_ui") {
   configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
-    "empty_reading_list_background_view.h",
-    "empty_reading_list_background_view.mm",
     "empty_reading_list_message_util.h",
     "empty_reading_list_message_util.mm",
     "number_badge_view.h",
diff --git a/ios/chrome/browser/ui/reading_list/empty_reading_list_background_view.h b/ios/chrome/browser/ui/reading_list/empty_reading_list_background_view.h
deleted file mode 100644
index 869735f6..0000000
--- a/ios/chrome/browser/ui/reading_list/empty_reading_list_background_view.h
+++ /dev/null
@@ -1,22 +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 IOS_CHROME_BROWSER_UI_READING_LIST_EMPTY_READING_LIST_BACKGROUND_VIEW_H_
-#define IOS_CHROME_BROWSER_UI_READING_LIST_EMPTY_READING_LIST_BACKGROUND_VIEW_H_
-
-#import <UIKit/UIKit.h>
-
-// The view to use as the background view for an empty reading list list view.
-@interface EmptyReadingListBackgroundView : UIView
-
-- (instancetype)init NS_DESIGNATED_INITIALIZER;
-
-- (instancetype)initWithCoder:(NSCoder*)aDecoder NS_UNAVAILABLE;
-- (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE;
-
-+ (NSString*)accessibilityIdentifier;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_READING_LIST_EMPTY_READING_LIST_BACKGROUND_VIEW_H_
diff --git a/ios/chrome/browser/ui/reading_list/empty_reading_list_background_view.mm b/ios/chrome/browser/ui/reading_list/empty_reading_list_background_view.mm
deleted file mode 100644
index ca0ba69..0000000
--- a/ios/chrome/browser/ui/reading_list/empty_reading_list_background_view.mm
+++ /dev/null
@@ -1,105 +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.
-
-#import "ios/chrome/browser/ui/reading_list/empty_reading_list_background_view.h"
-
-#include "base/logging.h"
-#import "ios/chrome/browser/ui/reading_list/empty_reading_list_message_util.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace {
-// Images name.
-NSString* const kEmptyReadingListBackgroundIcon = @"reading_list_empty_state";
-
-// Background view constants.
-const CGFloat kImageHeight = 44;
-const CGFloat kImageWidth = 60;
-const CGFloat kPercentageFromTopForPosition = 0.4;
-const CGFloat kTextHorizontalMinimumMargin = 32;
-const CGFloat kTextImageSpacing = 10;
-const CGFloat kTextMaximalWidth = 255;
-}  // namespace
-
-@interface EmptyReadingListBackgroundView ()
-
-// Sets the constraints for this view, positionning the |imageView| in the X
-// center and at 40% of the top. The |label| is positionned below.
-- (void)setConstraintsToImageView:(UIImageView*)imageView
-                         andLabel:(UILabel*)label;
-
-@end
-
-@implementation EmptyReadingListBackgroundView
-
-#pragma mark - Public
-
-- (instancetype)init {
-  self = [super initWithFrame:CGRectZero];
-  if (self) {
-
-    // Attach to the label.
-    UILabel* label = [[UILabel alloc] initWithFrame:CGRectZero];
-    label.attributedText = GetReadingListEmptyMessage();
-    label.numberOfLines = 0;
-    label.textAlignment = NSTextAlignmentCenter;
-    label.accessibilityLabel = GetReadingListEmptyMessageA11yLabel();
-    label.accessibilityIdentifier =
-        [EmptyReadingListBackgroundView accessibilityIdentifier];
-    [label setTranslatesAutoresizingMaskIntoConstraints:NO];
-    [self addSubview:label];
-
-    UIImageView* imageView = [[UIImageView alloc] init];
-    imageView.image = [UIImage imageNamed:kEmptyReadingListBackgroundIcon];
-    [imageView setTranslatesAutoresizingMaskIntoConstraints:NO];
-    [self addSubview:imageView];
-
-    [self setConstraintsToImageView:imageView andLabel:label];
-  }
-  return self;
-}
-
-+ (NSString*)accessibilityIdentifier {
-  return @"ReadingListBackgroundViewIdentifier";
-}
-
-#pragma mark - Private
-
-- (void)setConstraintsToImageView:(UIImageView*)imageView
-                         andLabel:(UILabel*)label {
-  [NSLayoutConstraint activateConstraints:@[
-    [imageView.heightAnchor constraintEqualToConstant:kImageHeight],
-    [imageView.widthAnchor constraintEqualToConstant:kImageWidth],
-    [self.centerXAnchor constraintEqualToAnchor:label.centerXAnchor],
-    [self.centerXAnchor constraintEqualToAnchor:imageView.centerXAnchor],
-    [label.topAnchor constraintEqualToAnchor:imageView.bottomAnchor
-                                    constant:kTextImageSpacing],
-    [label.trailingAnchor
-        constraintLessThanOrEqualToAnchor:self.trailingAnchor
-                                 constant:-kTextHorizontalMinimumMargin],
-    [label.leadingAnchor
-        constraintGreaterThanOrEqualToAnchor:self.leadingAnchor
-                                    constant:kTextHorizontalMinimumMargin]
-  ]];
-
-  NSLayoutConstraint* widthConstraint =
-      [label.widthAnchor constraintEqualToConstant:kTextMaximalWidth];
-  widthConstraint.priority = UILayoutPriorityDefaultHigh;
-  widthConstraint.active = YES;
-
-  // Position the top of the image at 40% from the top.
-  NSLayoutConstraint* verticalAlignment =
-      [NSLayoutConstraint constraintWithItem:imageView
-                                   attribute:NSLayoutAttributeTop
-                                   relatedBy:NSLayoutRelationEqual
-                                      toItem:self
-                                   attribute:NSLayoutAttributeBottom
-                                  multiplier:kPercentageFromTopForPosition
-                                    constant:0];
-  [self addConstraints:@[ verticalAlignment ]];
-}
-
-@end
diff --git a/ios/chrome/browser/ui/reading_list/empty_reading_list_message_util.mm b/ios/chrome/browser/ui/reading_list/empty_reading_list_message_util.mm
index de93e4f..430cad8c 100644
--- a/ios/chrome/browser/ui/reading_list/empty_reading_list_message_util.mm
+++ b/ios/chrome/browser/ui/reading_list/empty_reading_list_message_util.mm
@@ -27,9 +27,30 @@
 // Background view constants.
 const CGFloat kLineSpacing = 4;
 
+UIFont* FontWithMaximumForCategory(UIContentSizeCategory category,
+                                   UIFontTextStyle font_style) {
+  if (ContentSizeCategoryIsAccessibilityCategory(category)) {
+    return [UIFont
+            preferredFontForTextStyle:font_style
+        compatibleWithTraitCollection:
+            [UITraitCollection traitCollectionWithPreferredContentSizeCategory:
+                                   UIContentSizeCategoryAccessibilityLarge]];
+  }
+  return [UIFont preferredFontForTextStyle:font_style];
+}
+
 // Returns the font to use for the message text.
 UIFont* GetMessageFont() {
-  return [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
+  return FontWithMaximumForCategory(
+      [UIApplication sharedApplication].preferredContentSizeCategory,
+      UIFontTextStyleBody);
+}
+
+// Returns the font to use for the message text.
+UIFont* GetInstructionFont() {
+  return FontWithMaximumForCategory(
+      [UIApplication sharedApplication].preferredContentSizeCategory,
+      UIFontTextStyleHeadline);
 }
 
 // Returns the attributes to use for the message text.
@@ -54,8 +75,7 @@
 // Later" option.
 NSMutableDictionary* GetInstructionAttributes() {
   NSMutableDictionary* attributes = GetMessageAttributes();
-  attributes[NSFontAttributeName] =
-      [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline];
+  attributes[NSFontAttributeName] = GetInstructionFont();
   return attributes;
 }
 
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm b/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm
index 8654318..b31f1fe 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm
+++ b/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm
@@ -17,7 +17,6 @@
 #include "ios/chrome/browser/reading_list/reading_list_model_factory.h"
 #import "ios/chrome/browser/ui/commands/reading_list_add_command.h"
 #import "ios/chrome/browser/ui/popup_menu/popup_menu_constants.h"
-#import "ios/chrome/browser/ui/reading_list/empty_reading_list_background_view.h"
 #import "ios/chrome/browser/ui/reading_list/reading_list_table_view_controller.h"
 #import "ios/chrome/browser/ui/reading_list/reading_list_toolbar_button_identifiers.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_url_cell_favicon_badge_view.h"
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_table_view_controller.mm b/ios/chrome/browser/ui/reading_list/reading_list_table_view_controller.mm
index e1701ea..04ef2f13 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_table_view_controller.mm
+++ b/ios/chrome/browser/ui/reading_list/reading_list_table_view_controller.mm
@@ -22,6 +22,7 @@
 #import "ios/chrome/browser/ui/reading_list/reading_list_toolbar_button_commands.h"
 #import "ios/chrome/browser/ui/reading_list/reading_list_toolbar_button_manager.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_text_header_footer_item.h"
+#import "ios/chrome/browser/ui/uikit_ui_util.h"
 #include "ios/chrome/grit/ios_strings.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 
@@ -224,6 +225,15 @@
     [self exitEditingModeAnimated:YES];
 }
 
+- (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection {
+  [super traitCollectionDidChange:previousTraitCollection];
+  if (!self.dataSource.hasElements &&
+      self.traitCollection.preferredContentSizeCategory !=
+          previousTraitCollection.preferredContentSizeCategory) {
+    [self tableIsEmpty];
+  }
+}
+
 #pragma mark - UITableViewDataSource
 
 - (void)tableView:(UITableView*)tableView
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_url_item.h b/ios/chrome/browser/ui/table_view/cells/table_view_url_item.h
index 20b7fce7..d44e41a53 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_url_item.h
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_url_item.h
@@ -10,7 +10,7 @@
 #import "ios/chrome/browser/ui/table_view/cells/table_view_item.h"
 
 class GURL;
-@class FaviconViewNew;
+@class FaviconView;
 @class TableViewURLCellFaviconBadgeView;
 
 // TableViewURLItem contains the model data for a TableViewURLCell.
@@ -41,7 +41,7 @@
 
 // The imageview that is displayed on the leading edge of the cell.  This
 // contains a favicon composited on top of an off-white background.
-@property(nonatomic, readonly, strong) FaviconViewNew* faviconView;
+@property(nonatomic, readonly, strong) FaviconView* faviconView;
 
 // Container View for the faviconView.
 @property(nonatomic, readonly, strong) UIImageView* faviconContainerView;
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_url_item.mm b/ios/chrome/browser/ui/table_view/cells/table_view_url_item.mm
index 55e5b8c..36e7bd3 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_url_item.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_url_item.mm
@@ -152,7 +152,7 @@
     _faviconContainerView = [[UIImageView alloc]
         initWithImage:[UIImage
                           imageNamed:@"table_view_cell_favicon_background"]];
-    _faviconView = [[FaviconViewNew alloc] init];
+    _faviconView = [[FaviconView alloc] init];
     _faviconView.contentMode = UIViewContentModeScaleAspectFit;
     _faviconView.clipsToBounds = YES;
     [_faviconContainerView addSubview:_faviconView];
diff --git a/ios/chrome/common/favicon/favicon_view.h b/ios/chrome/common/favicon/favicon_view.h
index c0a05e2..41cd295 100644
--- a/ios/chrome/common/favicon/favicon_view.h
+++ b/ios/chrome/common/favicon/favicon_view.h
@@ -16,7 +16,7 @@
 const CGFloat kFaviconPreferredSize = 24.0f;
 }  // namespace
 
-@interface FaviconViewNew : UIView
+@interface FaviconView : UIView
 
 // Configures this view with given attributes.
 - (void)configureWithAttributes:(nullable FaviconAttributes*)attributes;
diff --git a/ios/chrome/common/favicon/favicon_view.mm b/ios/chrome/common/favicon/favicon_view.mm
index 947d7ee..c0f823c 100644
--- a/ios/chrome/common/favicon/favicon_view.mm
+++ b/ios/chrome/common/favicon/favicon_view.mm
@@ -15,8 +15,8 @@
 const CGFloat kDefaultCornerRadius = 3;
 }
 
-@interface FaviconViewNew () {
-  // Property releaser for FaviconViewNew.
+@interface FaviconView () {
+  // Property releaser for FaviconView.
 }
 
 // Image view for the favicon.
@@ -26,7 +26,7 @@
 
 @end
 
-@implementation FaviconViewNew
+@implementation FaviconView
 @synthesize faviconImageView = _faviconImageView;
 @synthesize faviconFallbackLabel = _faviconFallbackLabel;
 
diff --git a/ios/chrome/content_widget_extension/most_visited_tile_view.h b/ios/chrome/content_widget_extension/most_visited_tile_view.h
index 1ea15fa4..90b6bbc4 100644
--- a/ios/chrome/content_widget_extension/most_visited_tile_view.h
+++ b/ios/chrome/content_widget_extension/most_visited_tile_view.h
@@ -9,7 +9,7 @@
 
 #import "ios/chrome/common/highlight_button.h"
 
-@class FaviconViewNew;
+@class FaviconView;
 
 // View to display a Most Visited tile based on the suggestion.
 // It displays the favicon for this Most Visited suggestion and its title.
@@ -24,7 +24,7 @@
 - (nonnull instancetype)initWithCoder:(nonnull NSCoder*)aDecoder NS_UNAVAILABLE;
 
 // FaviconView displaying the favicon.
-@property(nonatomic, strong, readonly, nonnull) FaviconViewNew* faviconView;
+@property(nonatomic, strong, readonly, nonnull) FaviconView* faviconView;
 
 // Title of the Most Visited.
 @property(nonatomic, strong, readonly, nonnull) UILabel* titleLabel;
diff --git a/ios/chrome/content_widget_extension/most_visited_tile_view.mm b/ios/chrome/content_widget_extension/most_visited_tile_view.mm
index 8623ab02..dbfe663 100644
--- a/ios/chrome/content_widget_extension/most_visited_tile_view.mm
+++ b/ios/chrome/content_widget_extension/most_visited_tile_view.mm
@@ -56,7 +56,7 @@
     [titleLabelEffectView.contentView addSubview:_titleLabel];
     AddSameConstraints(titleLabelEffectView, _titleLabel);
 
-    _faviconView = [[FaviconViewNew alloc] init];
+    _faviconView = [[FaviconView alloc] init];
     _faviconView.isAccessibilityElement = NO;
     _faviconView.font =
         [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline];
diff --git a/ios/web_view/BUILD.gn b/ios/web_view/BUILD.gn
index 59b6e434..b616e028 100644
--- a/ios/web_view/BUILD.gn
+++ b/ios/web_view/BUILD.gn
@@ -402,6 +402,7 @@
     "//components/browser_sync:test_support",
     "//components/prefs:test_support",
     "//components/signin/core/browser:test_support",
+    "//components/signin/ios/browser:test_support",
     "//components/sync:test_support_driver",
     "//ios/web/public/test",
     "//ios/web/public/test/fakes",
diff --git a/ios/web_view/internal/passwords/cwv_password_controller.mm b/ios/web_view/internal/passwords/cwv_password_controller.mm
index 6d310a7..90603a7 100644
--- a/ios/web_view/internal/passwords/cwv_password_controller.mm
+++ b/ios/web_view/internal/passwords/cwv_password_controller.mm
@@ -12,7 +12,7 @@
 #include "components/autofill/ios/browser/autofill_util.h"
 #import "components/password_manager/core/browser/form_parsing/ios_form_parser.h"
 #include "components/password_manager/core/browser/password_manager.h"
-#import "components/password_manager/ios/password_controller_helper.h"
+#import "components/password_manager/ios/password_form_helper.h"
 #import "ios/web/public/origin_util.h"
 #include "ios/web/public/url_scheme_util.h"
 #import "ios/web/public/web_state/web_state_observer_bridge.h"
@@ -36,14 +36,14 @@
 @interface CWVPasswordController ()<CRWWebStateObserver,
                                     CWVPasswordManagerClientDelegate,
                                     CWVPasswordManagerDriverDelegate,
-                                    PasswordControllerHelperDelegate>
+                                    PasswordFormHelperDelegate>
 
 // The PasswordManagerDriver owned by this PasswordController.
 @property(nonatomic, readonly)
     password_manager::PasswordManagerDriver* passwordManagerDriver;
 
-// Helper contains common password controller logic.
-@property(nonatomic, readonly) PasswordControllerHelper* helper;
+// Helper contains common password form processing logic.
+@property(nonatomic, readonly) PasswordFormHelper* formHelper;
 
 // Delegate to receive password autofill suggestion callbacks.
 @property(nonatomic, weak, nullable) id<CWVPasswordControllerDelegate> delegate;
@@ -83,7 +83,7 @@
 
 #pragma mark - Properties
 
-@synthesize helper = _helper;
+@synthesize formHelper = _formHelper;
 @synthesize delegate = _delegate;
 
 - (password_manager::PasswordManagerDriver*)passwordManagerDriver {
@@ -102,8 +102,8 @@
     _webStateObserverBridge =
         std::make_unique<web::WebStateObserverBridge>(self);
     _webState->AddObserver(_webStateObserverBridge.get());
-    _helper = [[PasswordControllerHelper alloc] initWithWebState:webState
-                                                        delegate:self];
+    _formHelper =
+        [[PasswordFormHelper alloc] initWithWebState:webState delegate:self];
 
     _passwordManagerClient =
         std::make_unique<WebViewPasswordManagerClient>(self);
@@ -186,7 +186,7 @@
 }
 
 - (const GURL&)lastCommittedURL {
-  return self.helper.lastCommittedURL;
+  return self.formHelper.lastCommittedURL;
 }
 
 - (void)showSavePasswordInfoBar:
@@ -259,7 +259,7 @@
 
 - (void)fillPasswordForm:(const autofill::PasswordFormFillData&)formData {
   // TODO(crbug.com/865114): Add suggestion related logic.
-  [self.helper fillPasswordForm:formData completionHandler:nil];
+  [self.formHelper fillPasswordForm:formData completionHandler:nil];
 }
 
 // Informs delegate that there are no saved credentials for the current page.
@@ -267,11 +267,11 @@
   // TODO(crbug.com/865114): Implement remaining logic.
 }
 
-#pragma mark - PasswordControllerHelperDelegate
+#pragma mark - PasswordFormHelperDelegate
 
-- (void)helper:(PasswordControllerHelper*)helper
-    didSubmitForm:(const PasswordForm&)form
-      inMainFrame:(BOOL)inMainFrame {
+- (void)formHelper:(PasswordFormHelper*)formHelper
+     didSubmitForm:(const PasswordForm&)form
+       inMainFrame:(BOOL)inMainFrame {
   if (inMainFrame) {
     self.passwordManager->OnPasswordFormSubmitted(self.passwordManagerDriver,
                                                   form);
@@ -314,8 +314,8 @@
   // Read all password forms from the page and send them to the password
   // manager.
   __weak CWVPasswordController* weakSelf = self;
-  [self.helper findPasswordFormsWithCompletionHandler:^(
-                   const std::vector<autofill::PasswordForm>& forms) {
+  [self.formHelper findPasswordFormsWithCompletionHandler:^(
+                       const std::vector<autofill::PasswordForm>& forms) {
     [weakSelf didFinishPasswordFormExtraction:forms];
   }];
 }
diff --git a/ios/web_view/internal/sync/cwv_sync_controller.mm b/ios/web_view/internal/sync/cwv_sync_controller.mm
index 1266bb85..a13c08e1 100644
--- a/ios/web_view/internal/sync/cwv_sync_controller.mm
+++ b/ios/web_view/internal/sync/cwv_sync_controller.mm
@@ -219,7 +219,7 @@
     ProfileOAuth2TokenServiceIOSDelegate* tokenDelegate =
         static_cast<ProfileOAuth2TokenServiceIOSDelegate*>(
             _tokenService->GetDelegate());
-    tokenDelegate->LoadCredentials(authenticatedID);
+    tokenDelegate->ReloadCredentials(authenticatedID);
   }
 }
 
diff --git a/ios/web_view/internal/sync/cwv_sync_controller_unittest.mm b/ios/web_view/internal/sync/cwv_sync_controller_unittest.mm
index a392318..e4bada1 100644
--- a/ios/web_view/internal/sync/cwv_sync_controller_unittest.mm
+++ b/ios/web_view/internal/sync/cwv_sync_controller_unittest.mm
@@ -15,7 +15,9 @@
 #include "components/signin/core/browser/fake_gaia_cookie_manager_service.h"
 #include "components/signin/core/browser/fake_profile_oauth2_token_service.h"
 #include "components/signin/core/browser/fake_signin_manager.h"
+#include "components/signin/core/browser/signin_error_controller.h"
 #include "components/signin/core/browser/test_signin_client.h"
+#include "components/signin/ios/browser/fake_profile_oauth2_token_service_ios_provider.h"
 #include "components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.h"
 #include "components/signin/ios/browser/profile_oauth2_token_service_ios_provider.h"
 #include "components/sync/driver/fake_sync_client.h"
@@ -47,7 +49,15 @@
   CWVSyncControllerTest()
       : browser_state_(/*off_the_record=*/false),
         signin_client_(browser_state_.GetPrefs()),
-        token_service_(browser_state_.GetPrefs()),
+        sigin_error_controller_(
+            SigninErrorController::AccountMode::ANY_ACCOUNT),
+        token_service_(
+            browser_state_.GetPrefs(),
+            std::make_unique<ProfileOAuth2TokenServiceIOSDelegate>(
+                &signin_client_,
+                std::make_unique<FakeProfileOAuth2TokenServiceIOSProvider>(),
+                &account_tracker_service_,
+                &sigin_error_controller_)),
         gaia_cookie_manager_service_(&token_service_,
                                      "cookie-source",
                                      &signin_client_),
@@ -99,6 +109,9 @@
   std::unique_ptr<browser_sync::ProfileSyncServiceMock> profile_sync_service_;
   AccountTrackerService account_tracker_service_;
   TestSigninClient signin_client_;
+  SigninErrorController sigin_error_controller_;
+  //  FakeProfileOAuth2TokenServiceIOSProvider token_service_provider_;
+  //  ProfileOAuth2TokenServiceIOSDelegate token_service_delegate_;
   FakeProfileOAuth2TokenService token_service_;
   FakeGaiaCookieManagerService gaia_cookie_manager_service_;
   FakeSigninManager signin_manager_;
diff --git a/ios/web_view/internal/web_view_web_main_parts.mm b/ios/web_view/internal/web_view_web_main_parts.mm
index c4ead59..888b8b67 100644
--- a/ios/web_view/internal/web_view_web_main_parts.mm
+++ b/ios/web_view/internal/web_view_web_main_parts.mm
@@ -5,6 +5,8 @@
 #import "ios/web_view/internal/web_view_web_main_parts.h"
 
 #include "base/base_paths.h"
+#include "base/feature_list.h"
+#include "base/logging.h"
 #include "base/path_service.h"
 #include "components/content_settings/core/common/content_settings_pattern.h"
 #include "ios/web_view/cwv_web_view_features.h"
@@ -38,6 +40,15 @@
   DCHECK(local_state);
 
   ApplicationContext::GetInstance()->PreCreateThreads();
+
+#if BUILDFLAG(IOS_WEB_VIEW_ENABLE_SYNC)
+  std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
+  feature_list->InitializeFromCommandLine(
+      /*enable_features=*/"",
+      // TODO(crbug.com/873790): Figure out if USER_CONSENTS need to be enabled.
+      /*disable_features=*/"SyncUserConsentSeparateType");
+  base::FeatureList::SetInstance(std::move(feature_list));
+#endif  // BUILDFLAG(IOS_WEB_VIEW_ENABLE_SYNC)
 }
 
 void WebViewWebMainParts::PreMainMessageLoopRun() {
diff --git a/ios/web_view/public/cwv_sync_controller.h b/ios/web_view/public/cwv_sync_controller.h
index cd342e3..815127b 100644
--- a/ios/web_view/public/cwv_sync_controller.h
+++ b/ios/web_view/public/cwv_sync_controller.h
@@ -70,7 +70,7 @@
                    dataSource:
                        (__weak id<CWVSyncControllerDataSource>)dataSource;
 
-// Stops syncs and nils out |currentIdentity|.
+// Stops syncs and nils out |currentIdentity|. This method is idempotent.
 - (void)stopSyncAndClearIdentity;
 
 // If |passphraseNeeded| is |YES|. Call this to unlock the sync data.
diff --git a/media/gpu/v4l2/tegra_v4l2_device.cc b/media/gpu/v4l2/tegra_v4l2_device.cc
index c48988b..15dd086 100644
--- a/media/gpu/v4l2/tegra_v4l2_device.cc
+++ b/media/gpu/v4l2/tegra_v4l2_device.cc
@@ -61,7 +61,25 @@
 }
 
 int TegraV4L2Device::Ioctl(int flags, void* arg) {
-  return HANDLE_EINTR(TegraV4L2_Ioctl(device_fd_, flags, arg));
+  int ret = HANDLE_EINTR(TegraV4L2_Ioctl(device_fd_, flags, arg));
+  if (ret)
+    return ret;
+
+  // Workarounds for Tegra's broken closed-source V4L2 interface.
+  struct v4l2_format* format;
+  switch (flags) {
+    // VIDIOC_G_FMT returns 0 planes for multiplanar formats with 1 plane.
+    case static_cast<int>(VIDIOC_G_FMT):
+      format = static_cast<struct v4l2_format*>(arg);
+      if (V4L2_TYPE_IS_MULTIPLANAR(format->type) &&
+          format->fmt.pix_mp.num_planes == 0)
+        format->fmt.pix_mp.num_planes = 1;
+      break;
+    default:
+      break;
+  }
+
+  return 0;
 }
 
 bool TegraV4L2Device::Poll(bool poll_device, bool* event_pending) {
diff --git a/media/gpu/v4l2/v4l2_device.cc b/media/gpu/v4l2/v4l2_device.cc
index 024570f..eb8411a 100644
--- a/media/gpu/v4l2/v4l2_device.cc
+++ b/media/gpu/v4l2/v4l2_device.cc
@@ -38,6 +38,7 @@
   static std::unique_ptr<V4L2Buffer> Create(scoped_refptr<V4L2Device> device,
                                             enum v4l2_buf_type type,
                                             enum v4l2_memory memory,
+                                            size_t planes_count,
                                             size_t buffer_id);
   ~V4L2Buffer();
 
@@ -48,6 +49,7 @@
   V4L2Buffer(scoped_refptr<V4L2Device> device,
              enum v4l2_buf_type type,
              enum v4l2_memory memory,
+             size_t planes_count,
              size_t buffer_id);
   bool Query();
 
@@ -56,6 +58,9 @@
 
   // V4L2 data as queried by QUERYBUF.
   struct v4l2_buffer v4l2_buffer_ = {};
+  // WARNING: do not change this to a vector or something smaller than
+  // VIDEO_MAX_PLANES, otherwise the Tegra libv4l2 will write data beyond
+  // the number of allocated planes, resulting in memory corruption.
   struct v4l2_plane v4l2_planes_[VIDEO_MAX_PLANES] = {{}};
 
   DISALLOW_COPY_AND_ASSIGN(V4L2Buffer);
@@ -64,10 +69,11 @@
 std::unique_ptr<V4L2Buffer> V4L2Buffer::Create(scoped_refptr<V4L2Device> device,
                                                enum v4l2_buf_type type,
                                                enum v4l2_memory memory,
+                                               size_t planes_count,
                                                size_t buffer_id) {
   // Not using std::make_unique because constructor is private.
   std::unique_ptr<V4L2Buffer> buffer(
-      new V4L2Buffer(device, type, memory, buffer_id));
+      new V4L2Buffer(device, type, memory, planes_count, buffer_id));
 
   if (!buffer->Query())
     return nullptr;
@@ -78,14 +84,19 @@
 V4L2Buffer::V4L2Buffer(scoped_refptr<V4L2Device> device,
                        enum v4l2_buf_type type,
                        enum v4l2_memory memory,
+                       size_t planes_count,
                        size_t buffer_id)
-    : device_(device), plane_mappings_(VIDEO_MAX_PLANES) {
+    : device_(device) {
   DCHECK(V4L2_TYPE_IS_MULTIPLANAR(type));
+  DCHECK_LE(planes_count, base::size(v4l2_planes_));
   v4l2_buffer_.m.planes = v4l2_planes_;
-  v4l2_buffer_.length = VIDEO_MAX_PLANES;
+  // Just in case we got more planes than we want.
+  v4l2_buffer_.length = std::min(planes_count, base::size(v4l2_planes_));
   v4l2_buffer_.index = buffer_id;
   v4l2_buffer_.type = type;
   v4l2_buffer_.memory = memory;
+
+  plane_mappings_.resize(v4l2_buffer_.length);
 }
 
 V4L2Buffer::~V4L2Buffer() {
@@ -103,7 +114,7 @@
     return false;
   }
 
-  plane_mappings_.resize(v4l2_buffer_.length);
+  DCHECK(plane_mappings_.size() == v4l2_buffer_.length);
 
   return true;
 }
@@ -154,6 +165,9 @@
 
   // Data from the buffer, that users can query and/or write.
   struct v4l2_buffer v4l2_buffer_;
+  // WARNING: do not change this to a vector or something smaller than
+  // VIDEO_MAX_PLANES, otherwise the Tegra libv4l2 will write data beyond
+  // the number of allocated planes, resulting in memory corruption.
   struct v4l2_plane v4l2_planes_[VIDEO_MAX_PLANES];
 
  private:
@@ -170,7 +184,7 @@
     scoped_refptr<V4L2Queue> queue)
     : queue_(std::move(queue)) {
   DCHECK(V4L2_TYPE_IS_MULTIPLANAR(v4l2_buffer->type));
-  DCHECK_LE(v4l2_buffer->length, static_cast<uint32_t>(VIDEO_MAX_PLANES));
+  DCHECK_LE(v4l2_buffer->length, base::size(v4l2_planes_));
 
   memcpy(&v4l2_buffer_, v4l2_buffer, sizeof(v4l2_buffer_));
   memcpy(v4l2_planes_, v4l2_buffer->m.planes,
@@ -526,13 +540,26 @@
     return 0;
   }
 
+  // First query the number of planes in the buffers we are about to request.
+  // This should not be required, but Tegra's VIDIOC_QUERYBUF will fail on
+  // output buffers if the number of specified planes does not exactly match the
+  // format.
+  struct v4l2_format format = {.type = type_};
+  int ret = device_->Ioctl(VIDIOC_G_FMT, &format);
+  if (ret) {
+    VPLOGF(1) << "VIDIOC_G_FMT failed: ";
+    return 0;
+  }
+  planes_count_ = format.fmt.pix_mp.num_planes;
+  DCHECK_LE(planes_count_, static_cast<size_t>(VIDEO_MAX_PLANES));
+
   struct v4l2_requestbuffers reqbufs = {};
   reqbufs.count = count;
   reqbufs.type = type_;
   reqbufs.memory = memory;
   DVLOGF(3) << "queue " << type_ << ": requesting " << count << " buffers.";
 
-  int ret = device_->Ioctl(VIDIOC_REQBUFS, &reqbufs);
+  ret = device_->Ioctl(VIDIOC_REQBUFS, &reqbufs);
   if (ret) {
     VPLOGF(1) << "VIDIOC_REQBUFS failed: ";
     return 0;
@@ -543,7 +570,7 @@
 
   // Now query all buffer information.
   for (size_t i = 0; i < reqbufs.count; i++) {
-    auto buffer = V4L2Buffer::Create(device_, type_, memory_, i);
+    auto buffer = V4L2Buffer::Create(device_, type_, memory_, planes_count_, i);
 
     if (!buffer) {
       DeallocateBuffers();
@@ -649,12 +676,14 @@
   }
 
   struct v4l2_buffer v4l2_buffer = {};
+  // WARNING: do not change this to a vector or something smaller than
+  // VIDEO_MAX_PLANES, otherwise the Tegra libv4l2 will write data beyond
+  // the number of allocated planes, resulting in memory corruption.
   struct v4l2_plane planes[VIDEO_MAX_PLANES] = {{}};
   v4l2_buffer.type = type_;
   v4l2_buffer.memory = memory_;
   v4l2_buffer.m.planes = planes;
-  // TODO(acourbot): use actual number of planes.
-  v4l2_buffer.length = VIDEO_MAX_PLANES;
+  v4l2_buffer.length = planes_count_;
   int ret = device_->Ioctl(VIDIOC_DQBUF, &v4l2_buffer);
   if (ret) {
     // TODO(acourbot): we should not have to check for EPIPE as codec clients
diff --git a/media/gpu/v4l2/v4l2_device.h b/media/gpu/v4l2/v4l2_device.h
index d17657c9..7134fe3a2 100644
--- a/media/gpu/v4l2/v4l2_device.h
+++ b/media/gpu/v4l2/v4l2_device.h
@@ -251,6 +251,7 @@
   const enum v4l2_buf_type type_;
   enum v4l2_memory memory_ = V4L2_MEMORY_MMAP;
   bool is_streaming_ = false;
+  size_t planes_count_ = 0;
 
   std::vector<std::unique_ptr<V4L2Buffer>> buffers_;
 
diff --git a/media/gpu/video_encode_accelerator_unittest.cc b/media/gpu/video_encode_accelerator_unittest.cc
index cb6adb2..de3fab9 100644
--- a/media/gpu/video_encode_accelerator_unittest.cc
+++ b/media/gpu/video_encode_accelerator_unittest.cc
@@ -70,8 +70,6 @@
 namespace media {
 namespace {
 
-const VideoPixelFormat kInputFormat = PIXEL_FORMAT_I420;
-
 // The absolute differences between original frame and decoded frame usually
 // ranges aroud 1 ~ 7. So we pick 10 as an extreme value to detect abnormal
 // decoded frames.
@@ -116,10 +114,10 @@
 // The syntax of each test stream is:
 // "in_filename:width:height:profile:out_filename:requested_bitrate
 //  :requested_framerate:requested_subsequent_bitrate
-//  :requested_subsequent_framerate"
+//  :requested_subsequent_framerate:pixel_format"
 // Instead of ":", "," can be used as a seperator as well. Note that ":" does
 // not work on Windows as it interferes with file paths.
-// - |in_filename| must be an I420 (YUV planar) raw stream
+// - |in_filename| is YUV raw stream. Its format must be |pixel_format|
 //   (see http://www.fourcc.org/yuv.php#IYUV).
 // - |width| and |height| are in pixels.
 // - |profile| to encode into (values of VideoCodecProfile).
@@ -132,12 +130,15 @@
 // Further parameters are optional (need to provide preceding positional
 // parameters if a specific subsequent parameter is required):
 // - |requested_bitrate| requested bitrate in bits per second.
+//   Bitrate is only forced for tests that test bitrate.
 // - |requested_framerate| requested initial framerate.
 // - |requested_subsequent_bitrate| bitrate to switch to in the middle of the
 //                                  stream.
 // - |requested_subsequent_framerate| framerate to switch to in the middle
 //                                    of the stream.
-//   Bitrate is only forced for tests that test bitrate.
+// - |pixel_format| is the VideoPixelFormat of |in_filename|. Users needs to
+//   set the value corresponding to the desired format. If it is not specified,
+//   this would be PIXEL_FORMAT_I420.
 
 #if defined(OS_CHROMEOS) || defined(OS_LINUX)
 const char* g_default_in_filename = "bear_320x192_40frames.yuv";
@@ -237,12 +238,12 @@
         requested_subsequent_framerate(0) {}
   ~TestStream() {}
 
+  VideoPixelFormat pixel_format;
   gfx::Size visible_size;
   gfx::Size coded_size;
   unsigned int num_frames;
 
-  // Original unaligned input file name provided as an argument to the test.
-  // And the file must be an I420 (YUV planar) raw stream.
+  // Original unaligned YUV input file name provided as an argument to the test.
   std::string in_filename;
 
   // A vector used to prepare aligned input buffers of |in_filename|. This
@@ -323,7 +324,9 @@
               coded_size == test_stream->coded_size);
   test_stream->coded_size = coded_size;
 
-  size_t num_planes = VideoFrame::NumPlanes(kInputFormat);
+  ASSERT_NE(test_stream->pixel_format, PIXEL_FORMAT_UNKNOWN);
+  const VideoPixelFormat pixel_format = test_stream->pixel_format;
+  size_t num_planes = VideoFrame::NumPlanes(pixel_format);
   std::vector<size_t> padding_sizes(num_planes);
   std::vector<size_t> coded_bpl(num_planes);
   std::vector<size_t> visible_bpl(num_planes);
@@ -338,18 +341,18 @@
   // copied into a row of coded_bpl bytes in the aligned file.
   for (size_t i = 0; i < num_planes; i++) {
     const size_t size =
-        VideoFrame::PlaneSize(kInputFormat, i, coded_size).GetArea();
+        VideoFrame::PlaneSize(pixel_format, i, coded_size).GetArea();
     test_stream->aligned_plane_size.push_back(
         AlignToPlatformRequirements(size));
     test_stream->aligned_buffer_size += test_stream->aligned_plane_size.back();
 
-    coded_bpl[i] = VideoFrame::RowBytes(i, kInputFormat, coded_size.width());
-    visible_bpl[i] = VideoFrame::RowBytes(i, kInputFormat,
+    coded_bpl[i] = VideoFrame::RowBytes(i, pixel_format, coded_size.width());
+    visible_bpl[i] = VideoFrame::RowBytes(i, pixel_format,
                                           test_stream->visible_size.width());
     visible_plane_rows[i] =
-        VideoFrame::Rows(i, kInputFormat, test_stream->visible_size.height());
+        VideoFrame::Rows(i, pixel_format, test_stream->visible_size.height());
     const size_t padding_rows =
-        VideoFrame::Rows(i, kInputFormat, coded_size.height()) -
+        VideoFrame::Rows(i, pixel_format, coded_size.height()) -
         visible_plane_rows[i];
     padding_sizes[i] =
         padding_rows * coded_bpl[i] + AlignToPlatformRequirements(size) - size;
@@ -360,7 +363,7 @@
   LOG_ASSERT(base::GetFileSize(src_file, &src_file_size));
 
   size_t visible_buffer_size =
-      VideoFrame::AllocationSize(kInputFormat, test_stream->visible_size);
+      VideoFrame::AllocationSize(pixel_format, test_stream->visible_size);
   LOG_ASSERT(src_file_size % visible_buffer_size == 0U)
       << "Stream byte size is not a product of calculated frame byte size";
 
@@ -421,7 +424,7 @@
                                  base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
     }
     LOG_ASSERT(fields.size() >= 4U) << data;
-    LOG_ASSERT(fields.size() <= 9U) << data;
+    LOG_ASSERT(fields.size() <= 10U) << data;
     auto test_stream = std::make_unique<TestStream>();
 
     test_stream->in_filename = FilePathStringTypeToString(fields[0]);
@@ -438,6 +441,7 @@
     LOG_ASSERT(profile > VIDEO_CODEC_PROFILE_UNKNOWN);
     LOG_ASSERT(profile <= VIDEO_CODEC_PROFILE_MAX);
     test_stream->requested_profile = static_cast<VideoCodecProfile>(profile);
+    test_stream->pixel_format = PIXEL_FORMAT_I420;
 
     if (fields.size() >= 5 && !fields[4].empty())
       test_stream->out_filename = FilePathStringTypeToString(fields[4]);
@@ -459,6 +463,12 @@
       LOG_ASSERT(base::StringToUint(
           fields[8], &test_stream->requested_subsequent_framerate));
     }
+
+    if (fields.size() >= 10 && !fields[9].empty()) {
+      unsigned int format = 0;
+      LOG_ASSERT(base::StringToUint(fields[9], &format));
+      test_stream->pixel_format = static_cast<VideoPixelFormat>(format);
+    }
     test_streams->push_back(std::move(test_stream));
   }
 }
@@ -748,6 +758,7 @@
     : public base::SupportsWeakPtr<VideoFrameQualityValidator> {
  public:
   VideoFrameQualityValidator(const VideoCodecProfile profile,
+                             const VideoPixelFormat pixel_format,
                              bool verify_quality,
                              const base::Closure& flush_complete_cb,
                              const base::Closure& decode_error_cb);
@@ -774,10 +785,11 @@
     uint64_t mse[VideoFrame::kMaxPlanes];
   };
 
-  static FrameStats CompareFrames(const VideoFrame& original_frame,
-                                  const VideoFrame& output_frame);
+  FrameStats CompareFrames(const VideoFrame& original_frame,
+                           const VideoFrame& output_frame);
   MediaLog media_log_;
   const VideoCodecProfile profile_;
+  const VideoPixelFormat pixel_format_;
   const bool verify_quality_;
   std::unique_ptr<FFmpegVideoDecoder> decoder_;
   VideoDecoder::DecodeCB decode_cb_;
@@ -795,10 +807,12 @@
 
 VideoFrameQualityValidator::VideoFrameQualityValidator(
     const VideoCodecProfile profile,
+    const VideoPixelFormat pixel_format,
     const bool verify_quality,
     const base::Closure& flush_complete_cb,
     const base::Closure& decode_error_cb)
     : profile_(profile),
+      pixel_format_(pixel_format),
       verify_quality_(verify_quality),
       decoder_(new FFmpegVideoDecoder(&media_log_)),
       decode_cb_(base::BindRepeating(&VideoFrameQualityValidator::DecodeDone,
@@ -820,12 +834,12 @@
   // The default output format of ffmpeg video decoder is YV12.
   VideoDecoderConfig config;
   if (IsVP8(profile_))
-    config.Initialize(kCodecVP8, VP8PROFILE_ANY, kInputFormat,
+    config.Initialize(kCodecVP8, VP8PROFILE_ANY, pixel_format_,
                       COLOR_SPACE_UNSPECIFIED, VIDEO_ROTATION_0, coded_size,
                       visible_size, natural_size, EmptyExtraData(),
                       Unencrypted());
   else if (IsH264(profile_))
-    config.Initialize(kCodecH264, H264PROFILE_MAIN, kInputFormat,
+    config.Initialize(kCodecH264, H264PROFILE_MAIN, pixel_format_,
                       COLOR_SPACE_UNSPECIFIED, VIDEO_ROTATION_0, coded_size,
                       visible_size, natural_size, EmptyExtraData(),
                       Unencrypted());
@@ -1077,12 +1091,6 @@
 VideoFrameQualityValidator::FrameStats
 VideoFrameQualityValidator::CompareFrames(const VideoFrame& original_frame,
                                           const VideoFrame& output_frame) {
-  // This code assumes I420/NV12 (e.g. 12bpp YUV planar) and needs to be updated
-  // to support anything else.
-  CHECK(original_frame.format() == PIXEL_FORMAT_I420 ||
-        original_frame.format() == PIXEL_FORMAT_YV12);
-  CHECK(output_frame.format() == PIXEL_FORMAT_I420 ||
-        output_frame.format() == PIXEL_FORMAT_YV12);
   CHECK(original_frame.visible_rect().size() ==
         output_frame.visible_rect().size());
 
@@ -1096,8 +1104,8 @@
         &frame_stats.ssim[plane], &frame_stats.mse[plane],
         original_frame.data(plane), original_frame.stride(plane),
         output_frame.data(plane), output_frame.stride(plane),
-        VideoFrame::Columns(plane, kInputFormat, frame_stats.width),
-        VideoFrame::Rows(plane, kInputFormat, frame_stats.height));
+        VideoFrame::Columns(plane, pixel_format_, frame_stats.width),
+        VideoFrame::Rows(plane, pixel_format_, frame_stats.height));
   }
   return frame_stats;
 }
@@ -1125,9 +1133,9 @@
       uint8_t* output_plane = output_frame->data(plane);
 
       size_t rows =
-          VideoFrame::Rows(plane, kInputFormat, visible_size.height());
+          VideoFrame::Rows(plane, pixel_format_, visible_size.height());
       size_t columns =
-          VideoFrame::Columns(plane, kInputFormat, visible_size.width());
+          VideoFrame::Columns(plane, pixel_format_, visible_size.width());
       size_t stride = original_frame->stride(plane);
 
       for (size_t i = 0; i < rows; i++) {
@@ -1139,7 +1147,7 @@
     }
 
     // Divide the difference by the size of frame.
-    difference /= VideoFrame::AllocationSize(kInputFormat, visible_size);
+    difference /= VideoFrame::AllocationSize(pixel_format_, visible_size);
     EXPECT_TRUE(difference <= kDecodeSimilarityThreshold)
         << "difference = " << difference << "  > decode similarity threshold";
   }
@@ -1435,7 +1443,8 @@
     // validating encoder quality.
     if (verify_output_ || !g_env->frame_stats_path().empty()) {
       quality_validator_.reset(new VideoFrameQualityValidator(
-          test_stream_->requested_profile, verify_output_,
+          test_stream_->requested_profile, test_stream_->pixel_format,
+          verify_output_,
           base::BindRepeating(&VEAClient::DecodeCompleted,
                               base::Unretained(this)),
           base::BindRepeating(&VEAClient::DecodeFailed,
@@ -1489,8 +1498,9 @@
            << ", initial bitrate: " << requested_bitrate_;
 
   const VideoEncodeAccelerator::Config config(
-      kInputFormat, test_stream_->visible_size, test_stream_->requested_profile,
-      requested_bitrate_, requested_framerate_);
+      test_stream_->pixel_format, test_stream_->visible_size,
+      test_stream_->requested_profile, requested_bitrate_,
+      requested_framerate_);
   encoder_ = CreateVideoEncodeAccelerator(config, this, gpu::GpuPreferences());
   if (!encoder_) {
     LOG(ERROR) << "Failed creating a VideoEncodeAccelerator.";
@@ -1742,10 +1752,10 @@
   CHECK_GT(current_framerate_, 0U);
 
   scoped_refptr<VideoFrame> video_frame = VideoFrame::WrapExternalYuvData(
-      kInputFormat, input_coded_size_, gfx::Rect(test_stream_->visible_size),
-      test_stream_->visible_size, input_coded_size_.width(),
-      input_coded_size_.width() / 2, input_coded_size_.width() / 2,
-      frame_data_y, frame_data_u, frame_data_v,
+      test_stream_->pixel_format, input_coded_size_,
+      gfx::Rect(test_stream_->visible_size), test_stream_->visible_size,
+      input_coded_size_.width(), input_coded_size_.width() / 2,
+      input_coded_size_.width() / 2, frame_data_y, frame_data_u, frame_data_v,
       // Timestamp needs to avoid starting from 0.
       base::TimeDelta().FromMilliseconds((next_input_id_ + 1) *
                                          base::Time::kMillisecondsPerSecond /
@@ -2108,8 +2118,8 @@
 
   gfx::Size visible_size(width_, height_);
   const VideoEncodeAccelerator::Config config(
-      kInputFormat, visible_size, g_env->test_streams_[0]->requested_profile,
-      bitrate_, fps_);
+      g_env->test_streams_[0]->pixel_format, visible_size,
+      g_env->test_streams_[0]->requested_profile, bitrate_, fps_);
   encoder_ = CreateVideoEncodeAccelerator(config, this, gpu::GpuPreferences());
   if (!encoder_) {
     LOG(ERROR) << "Failed creating a VideoEncodeAccelerator.";
@@ -2268,20 +2278,21 @@
   if (!has_encoder())
     return;
 
+  const VideoPixelFormat pixel_format = g_env->test_streams_[0]->pixel_format;
   std::vector<char, AlignedAllocator<char, kPlatformBufferAlignment>>
       aligned_data_y, aligned_data_u, aligned_data_v;
   aligned_data_y.resize(
-      VideoFrame::PlaneSize(kInputFormat, 0, input_coded_size).GetArea());
+      VideoFrame::PlaneSize(pixel_format, 0, input_coded_size).GetArea());
   aligned_data_u.resize(
-      VideoFrame::PlaneSize(kInputFormat, 1, input_coded_size).GetArea());
+      VideoFrame::PlaneSize(pixel_format, 1, input_coded_size).GetArea());
   aligned_data_v.resize(
-      VideoFrame::PlaneSize(kInputFormat, 2, input_coded_size).GetArea());
+      VideoFrame::PlaneSize(pixel_format, 2, input_coded_size).GetArea());
   uint8_t* frame_data_y = reinterpret_cast<uint8_t*>(&aligned_data_y[0]);
   uint8_t* frame_data_u = reinterpret_cast<uint8_t*>(&aligned_data_u[0]);
   uint8_t* frame_data_v = reinterpret_cast<uint8_t*>(&aligned_data_v[0]);
 
   scoped_refptr<VideoFrame> video_frame = VideoFrame::WrapExternalYuvData(
-      kInputFormat, input_coded_size, gfx::Rect(input_coded_size),
+      pixel_format, input_coded_size, gfx::Rect(input_coded_size),
       input_coded_size, input_coded_size.width(), input_coded_size.width() / 2,
       input_coded_size.width() / 2, frame_data_y, frame_data_u, frame_data_v,
       base::TimeDelta().FromMilliseconds(base::Time::kMillisecondsPerSecond /
diff --git a/media/test/data/README.md b/media/test/data/README.md
index f1d3aa1a..fa2d147 100644
--- a/media/test/data/README.md
+++ b/media/test/data/README.md
@@ -574,6 +574,18 @@
 First 40 raw i420 frames of bear-1280x720.mp4 scaled down to 320x192 for
 video_encode_accelerator_unittest.
 
+#### bear_320x192_40frames.nv12.yuv
+First 40 raw nv12 frames of bear-1280x720.mp4 scaled down to 320x192 for
+video_encode_accelerator_unittest.
+
+#### bear_320x192_40frames.nv21.yuv
+First 40 raw nv21 frames of bear-1280x720.mp4 scaled down to 320x192 for
+video_encode_accelerator_unittest.
+
+#### bear_320x192_40frames.yv12.yuv
+First 40 raw yv12 frames of bear-1280x720.mp4 scaled down to 320x192 for
+video_encode_accelerator_unittest.
+
 ###  VP9 parser test files:
 
 #### bear-vp9.ivf
diff --git a/media/test/data/bear_320x192_40frames.nv12.yuv b/media/test/data/bear_320x192_40frames.nv12.yuv
new file mode 100644
index 0000000..3bff3a4d
--- /dev/null
+++ b/media/test/data/bear_320x192_40frames.nv12.yuv
Binary files differ
diff --git a/media/test/data/bear_320x192_40frames.nv21.yuv b/media/test/data/bear_320x192_40frames.nv21.yuv
new file mode 100644
index 0000000..9ef82dd
--- /dev/null
+++ b/media/test/data/bear_320x192_40frames.nv21.yuv
Binary files differ
diff --git a/media/test/data/bear_320x192_40frames.yv12.yuv b/media/test/data/bear_320x192_40frames.yv12.yuv
new file mode 100644
index 0000000..65eca63
--- /dev/null
+++ b/media/test/data/bear_320x192_40frames.yv12.yuv
Binary files differ
diff --git a/net/proxy_resolution/proxy_config_service_android.cc b/net/proxy_resolution/proxy_config_service_android.cc
index 1661b2c..3dcbfab 100644
--- a/net/proxy_resolution/proxy_config_service_android.cc
+++ b/net/proxy_resolution/proxy_config_service_android.cc
@@ -231,8 +231,8 @@
     ProxyConfigWithAnnotation proxy_config;
     GetLatestProxyConfigInternal(get_property_callback_, &proxy_config);
     network_task_runner_->PostTask(
-        FROM_HERE, base::Bind(&Delegate::SetNewConfigInNetworkSequence, this,
-                              proxy_config));
+        FROM_HERE, base::BindOnce(&Delegate::SetNewConfigInNetworkSequence,
+                                  this, proxy_config));
   }
 
   void Shutdown() {
@@ -272,8 +272,8 @@
     ProxyConfigWithAnnotation proxy_config;
     GetLatestProxyConfigInternal(get_property_callback_, &proxy_config);
     network_task_runner_->PostTask(
-        FROM_HERE, base::Bind(&Delegate::SetNewConfigInNetworkSequence, this,
-                              proxy_config));
+        FROM_HERE, base::BindOnce(&Delegate::SetNewConfigInNetworkSequence,
+                                  this, proxy_config));
   }
 
   // Called in the JNI sequence.
@@ -293,8 +293,8 @@
           &proxy_config);
     }
     network_task_runner_->PostTask(
-        FROM_HERE, base::Bind(&Delegate::SetNewConfigInNetworkSequence, this,
-                              proxy_config));
+        FROM_HERE, base::BindOnce(&Delegate::SetNewConfigInNetworkSequence,
+                                  this, proxy_config));
   }
 
   void set_exclude_pac_url(bool enabled) {
@@ -304,27 +304,34 @@
   // Called in the JNI sequence.
   void SetProxyOverride(const std::string& host,
                         int port,
-                        const std::vector<std::string>& exclusion_list) {
+                        const std::vector<std::string>& exclusion_list,
+                        base::OnceClosure callback) {
     DCHECK(InJNISequence());
     has_proxy_override_ = true;
     ProxyConfigWithAnnotation proxy_config;
     CreateStaticProxyConfig(host, port, "", exclusion_list, &proxy_config);
-    network_task_runner_->PostTask(
-        FROM_HERE, base::Bind(&Delegate::SetNewConfigInNetworkSequence, this,
-                              proxy_config));
+    network_task_runner_->PostTaskAndReply(
+        FROM_HERE,
+        base::BindOnce(&Delegate::SetNewConfigInNetworkSequence, this,
+                       proxy_config),
+        std::move(callback));
   }
 
   // Called in the JNI sequence.
-  void ClearProxyOverride() {
+  void ClearProxyOverride(base::OnceClosure callback) {
     DCHECK(InJNISequence());
-    if (!has_proxy_override_)
+    if (!has_proxy_override_) {
+      std::move(callback).Run();
       return;
+    }
 
     ProxyConfigWithAnnotation proxy_config;
     GetLatestProxyConfigInternal(get_property_callback_, &proxy_config);
-    network_task_runner_->PostTask(
-        FROM_HERE, base::Bind(&Delegate::SetNewConfigInNetworkSequence, this,
-                              proxy_config));
+    network_task_runner_->PostTaskAndReply(
+        FROM_HERE,
+        base::BindOnce(&Delegate::SetNewConfigInNetworkSequence, this,
+                       proxy_config),
+        std::move(callback));
     has_proxy_override_ = false;
   }
 
@@ -453,12 +460,13 @@
 void ProxyConfigServiceAndroid::SetProxyOverride(
     const std::string& host,
     int port,
-    const std::vector<std::string>& exclusion_list) {
-  delegate_->SetProxyOverride(host, port, exclusion_list);
+    const std::vector<std::string>& exclusion_list,
+    base::OnceClosure callback) {
+  delegate_->SetProxyOverride(host, port, exclusion_list, std::move(callback));
 }
 
-void ProxyConfigServiceAndroid::ClearProxyOverride() {
-  delegate_->ClearProxyOverride();
+void ProxyConfigServiceAndroid::ClearProxyOverride(base::OnceClosure callback) {
+  delegate_->ClearProxyOverride(std::move(callback));
 }
 
 } // namespace net
diff --git a/net/proxy_resolution/proxy_config_service_android.h b/net/proxy_resolution/proxy_config_service_android.h
index ed70a5df..55394552 100644
--- a/net/proxy_resolution/proxy_config_service_android.h
+++ b/net/proxy_resolution/proxy_config_service_android.h
@@ -78,8 +78,9 @@
 
   void SetProxyOverride(const std::string& host,
                         int port,
-                        const std::vector<std::string>& exclusion_list);
-  void ClearProxyOverride();
+                        const std::vector<std::string>& exclusion_list,
+                        base::OnceClosure callback);
+  void ClearProxyOverride(base::OnceClosure callback);
 
  private:
   friend class ProxyConfigServiceAndroidTestBase;
diff --git a/net/proxy_resolution/proxy_config_service_android_unittest.cc b/net/proxy_resolution/proxy_config_service_android_unittest.cc
index 8c89180..61ece7dd 100644
--- a/net/proxy_resolution/proxy_config_service_android_unittest.cc
+++ b/net/proxy_resolution/proxy_config_service_android_unittest.cc
@@ -112,13 +112,14 @@
 
   void SetProxyOverride(const std::string& host,
                         int port,
-                        const std::vector<std::string>& exclusion_list) {
-    service_.SetProxyOverride(host, port, exclusion_list);
+                        const std::vector<std::string>& exclusion_list,
+                        base::OnceClosure callback) {
+    service_.SetProxyOverride(host, port, exclusion_list, std::move(callback));
     base::RunLoop().RunUntilIdle();
   }
 
-  void ClearProxyOverride() {
-    service_.ClearProxyOverride();
+  void ClearProxyOverride(base::OnceClosure callback) {
+    service_.ClearProxyOverride(std::move(callback));
     base::RunLoop().RunUntilIdle();
   }
 
@@ -178,6 +179,26 @@
   TestMapping("http://example.com/", "PROXY httpproxy.com:80");
 }
 
+struct ProxyCallback {
+  ProxyCallback()
+      : called(false),
+        callback(base::BindOnce(&ProxyCallback::Call, base::Unretained(this))) {
+  }
+
+  void Call() { called = true; }
+
+  bool called;
+  base::OnceClosure callback;
+};
+
+TEST_F(ProxyConfigServiceAndroidTest, TestProxyOverrideCallback) {
+  ProxyCallback proxyCallback;
+  ASSERT_FALSE(proxyCallback.called);
+  ClearProxyOverride(std::move(proxyCallback.callback));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(proxyCallback.called);
+}
+
 TEST_F(ProxyConfigServiceAndroidTest, TestOverrideNoProxy) {
   std::vector<std::string> exclusion_list;
 
@@ -185,11 +206,12 @@
   TestMapping("http://example.com/", "DIRECT");
 
   // Check that webview uses the custom proxy
-  SetProxyOverride("httpoverrideproxy.com", 200, exclusion_list);
+  SetProxyOverride("httpoverrideproxy.com", 200, exclusion_list,
+                   base::DoNothing());
   TestMapping("http://example.com/", "PROXY httpoverrideproxy.com:200");
 
   // Check that webview uses the default proxy
-  ClearProxyOverride();
+  ClearProxyOverride(base::DoNothing());
   TestMapping("http://example.com/", "DIRECT");
 }
 
@@ -200,7 +222,8 @@
   TestMapping("http://example.com/", "DIRECT");
 
   // Check that webview uses the custom proxy
-  SetProxyOverride("httpoverrideproxy.com", 200, exclusion_list);
+  SetProxyOverride("httpoverrideproxy.com", 200, exclusion_list,
+                   base::DoNothing());
   TestMapping("http://example.com/", "PROXY httpoverrideproxy.com:200");
 
   // Check that webview continues to use the custom proxy
@@ -209,7 +232,7 @@
   TestMapping("http://example.com/", "PROXY httpoverrideproxy.com:200");
 
   // Check that webview uses the non default proxy
-  ClearProxyOverride();
+  ClearProxyOverride(base::DoNothing());
   TestMapping("http://example.com/", "PROXY httpsomeproxy.com:80");
 }
 
@@ -225,11 +248,12 @@
   TestMapping("http://example.com/", "PROXY httpsomeproxy.com:80");
 
   // Check that webview uses the custom proxy
-  SetProxyOverride("httpoverrideproxy.com", 200, exclusion_list);
+  SetProxyOverride("httpoverrideproxy.com", 200, exclusion_list,
+                   base::DoNothing());
   TestMapping("http://example.com/", "PROXY httpoverrideproxy.com:200");
 
   // Check that webview uses the non default proxy
-  ClearProxyOverride();
+  ClearProxyOverride(base::DoNothing());
   TestMapping("http://example.com/", "PROXY httpsomeproxy.com:80");
 }
 
@@ -240,11 +264,12 @@
   TestMapping("http://example.com/", "DIRECT");
 
   // Check that webview uses the custom proxy
-  SetProxyOverride("httpoverrideproxy.com", 200, exclusion_list);
+  SetProxyOverride("httpoverrideproxy.com", 200, exclusion_list,
+                   base::DoNothing());
   TestMapping("http://example.com/", "PROXY httpoverrideproxy.com:200");
 
   // Check that webview uses the default proxy
-  ClearProxyOverride();
+  ClearProxyOverride(base::DoNothing());
   TestMapping("http://example.com/", "DIRECT");
 
   // Check that webview uses the non default proxy
@@ -260,7 +285,7 @@
   TestMapping("http://example.com/", "DIRECT");
 
   // Check that webview uses the default proxy
-  ClearProxyOverride();
+  ClearProxyOverride(base::DoNothing());
   TestMapping("http://example.com/", "DIRECT");
 }
 
@@ -273,7 +298,7 @@
   TestMapping("http://example.com/", "PROXY httpsomeproxy.com:80");
 
   // Check that webview uses the non default proxy
-  ClearProxyOverride();
+  ClearProxyOverride(base::DoNothing());
   TestMapping("http://example.com/", "PROXY httpsomeproxy.com:80");
 }
 
@@ -286,12 +311,13 @@
   TestMapping("http://example.com/", "DIRECT");
 
   // Check that webview handles the exclusion list correctly
-  SetProxyOverride("httpoverrideproxy.com", 200, exclusion_list);
+  SetProxyOverride("httpoverrideproxy.com", 200, exclusion_list,
+                   base::DoNothing());
   TestMapping("http://excluded.com/", "DIRECT");
   TestMapping("http://example.com/", "PROXY httpoverrideproxy.com:200");
 
   // Check that webview uses the default proxy
-  ClearProxyOverride();
+  ClearProxyOverride(base::DoNothing());
   TestMapping("http://excluded.com/", "DIRECT");
   TestMapping("http://example.com/", "DIRECT");
 }
diff --git a/net/quic/quic_chromium_client_session.cc b/net/quic/quic_chromium_client_session.cc
index f0eb6249..1dc14e465 100644
--- a/net/quic/quic_chromium_client_session.cc
+++ b/net/quic/quic_chromium_client_session.cc
@@ -1030,7 +1030,7 @@
   }
 }
 
-bool QuicChromiumClientSession::ShouldCreateOutgoingDynamicStream() {
+bool QuicChromiumClientSession::ShouldCreateOutgoingStream() {
   if (!crypto_stream_->encryption_established()) {
     DVLOG(1) << "Encryption not active so no outgoing stream created.";
     return false;
@@ -1213,10 +1213,10 @@
                               hostname);
 }
 
-bool QuicChromiumClientSession::ShouldCreateIncomingDynamicStream(
+bool QuicChromiumClientSession::ShouldCreateIncomingStream(
     quic::QuicStreamId id) {
   if (!connection()->connected()) {
-    LOG(DFATAL) << "ShouldCreateIncomingDynamicStream called when disconnected";
+    LOG(DFATAL) << "ShouldCreateIncomingStream called when disconnected";
     return false;
   }
   if (goaway_received()) {
@@ -1237,9 +1237,9 @@
   return true;
 }
 
-QuicChromiumClientStream*
-QuicChromiumClientSession::CreateIncomingDynamicStream(quic::QuicStreamId id) {
-  if (!ShouldCreateIncomingDynamicStream(id)) {
+QuicChromiumClientStream* QuicChromiumClientSession::CreateIncomingStream(
+    quic::QuicStreamId id) {
+  if (!ShouldCreateIncomingStream(id)) {
     return nullptr;
   }
   net::NetworkTrafficAnnotationTag traffic_annotation =
diff --git a/net/quic/quic_chromium_client_session.h b/net/quic/quic_chromium_client_session.h
index dfcc477..d6591b0 100644
--- a/net/quic/quic_chromium_client_session.h
+++ b/net/quic/quic_chromium_client_session.h
@@ -644,10 +644,10 @@
 
  protected:
   // quic::QuicSession methods:
-  bool ShouldCreateIncomingDynamicStream(quic::QuicStreamId id) override;
-  bool ShouldCreateOutgoingDynamicStream() override;
+  bool ShouldCreateIncomingStream(quic::QuicStreamId id) override;
+  bool ShouldCreateOutgoingStream() override;
 
-  QuicChromiumClientStream* CreateIncomingDynamicStream(
+  QuicChromiumClientStream* CreateIncomingStream(
       quic::QuicStreamId id) override;
 
  private:
diff --git a/net/quic/quic_chromium_client_session_peer.cc b/net/quic/quic_chromium_client_session_peer.cc
index d6e4fb81..b37a2f4 100644
--- a/net/quic/quic_chromium_client_session_peer.cc
+++ b/net/quic/quic_chromium_client_session_peer.cc
@@ -32,10 +32,9 @@
 }
 
 // static
-QuicChromiumClientStream*
-QuicChromiumClientSessionPeer::CreateOutgoingDynamicStream(
+QuicChromiumClientStream* QuicChromiumClientSessionPeer::CreateOutgoingStream(
     QuicChromiumClientSession* session) {
-  return session->ShouldCreateOutgoingDynamicStream()
+  return session->ShouldCreateOutgoingStream()
              ? session->CreateOutgoingReliableStreamImpl(
                    TRAFFIC_ANNOTATION_FOR_TESTS)
              : nullptr;
diff --git a/net/quic/quic_chromium_client_session_peer.h b/net/quic/quic_chromium_client_session_peer.h
index 8bdf717..23cdd51 100644
--- a/net/quic/quic_chromium_client_session_peer.h
+++ b/net/quic/quic_chromium_client_session_peer.h
@@ -29,7 +29,7 @@
   static uint64_t GetPushedAndUnclaimedBytesCount(
       QuicChromiumClientSession* session);
 
-  static QuicChromiumClientStream* CreateOutgoingDynamicStream(
+  static QuicChromiumClientStream* CreateOutgoingStream(
       QuicChromiumClientSession* session);
 
  private:
diff --git a/net/quic/quic_chromium_client_session_test.cc b/net/quic/quic_chromium_client_session_test.cc
index 4c46ee0..030ab19 100644
--- a/net/quic/quic_chromium_client_session_test.cc
+++ b/net/quic/quic_chromium_client_session_test.cc
@@ -510,7 +510,7 @@
   // can not proceed immediately.
   const size_t kMaxOpenStreams = session_->max_open_outgoing_streams();
   for (size_t i = 0; i < kMaxOpenStreams; i++) {
-    QuicChromiumClientSessionPeer::CreateOutgoingDynamicStream(session_.get());
+    QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get());
   }
   EXPECT_EQ(kMaxOpenStreams, session_->GetNumOpenOutgoingStreams());
 
@@ -552,7 +552,7 @@
   // can not proceed immediately.
   const size_t kMaxOpenStreams = session_->max_open_outgoing_streams();
   for (size_t i = 0; i < kMaxOpenStreams; i++) {
-    QuicChromiumClientSessionPeer::CreateOutgoingDynamicStream(session_.get());
+    QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get());
   }
   EXPECT_EQ(kMaxOpenStreams, session_->GetNumOpenOutgoingStreams());
 
@@ -605,7 +605,7 @@
   // can not proceed immediately.
   const size_t kMaxOpenStreams = session_->max_open_outgoing_streams();
   for (size_t i = 0; i < kMaxOpenStreams; i++) {
-    QuicChromiumClientSessionPeer::CreateOutgoingDynamicStream(session_.get());
+    QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get());
   }
   EXPECT_EQ(kMaxOpenStreams, session_->GetNumOpenOutgoingStreams());
 
@@ -711,7 +711,7 @@
   // can not proceed immediately.
   const size_t kMaxOpenStreams = session_->max_open_outgoing_streams();
   for (size_t i = 0; i < kMaxOpenStreams; i++) {
-    QuicChromiumClientSessionPeer::CreateOutgoingDynamicStream(session_.get());
+    QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get());
   }
   EXPECT_EQ(kMaxOpenStreams, session_->GetNumOpenOutgoingStreams());
 
@@ -754,13 +754,12 @@
   std::vector<QuicChromiumClientStream*> streams;
   for (size_t i = 0; i < kMaxOpenStreams; i++) {
     QuicChromiumClientStream* stream =
-        QuicChromiumClientSessionPeer::CreateOutgoingDynamicStream(
-            session_.get());
+        QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get());
     EXPECT_TRUE(stream);
     streams.push_back(stream);
   }
-  EXPECT_FALSE(QuicChromiumClientSessionPeer::CreateOutgoingDynamicStream(
-      session_.get()));
+  EXPECT_FALSE(
+      QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get()));
 
   EXPECT_EQ(kMaxOpenStreams, session_->GetNumOpenOutgoingStreams());
 
@@ -768,14 +767,14 @@
   quic::QuicStreamId stream_id = streams[0]->id();
   session_->CloseStream(stream_id);
 
-  EXPECT_FALSE(QuicChromiumClientSessionPeer::CreateOutgoingDynamicStream(
-      session_.get()));
+  EXPECT_FALSE(
+      QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get()));
   quic::QuicRstStreamFrame rst1(quic::kInvalidControlFrameId, stream_id,
                                 quic::QUIC_STREAM_NO_ERROR, 0);
   session_->OnRstStream(rst1);
   EXPECT_EQ(kMaxOpenStreams - 1, session_->GetNumOpenOutgoingStreams());
-  EXPECT_TRUE(QuicChromiumClientSessionPeer::CreateOutgoingDynamicStream(
-      session_.get()));
+  EXPECT_TRUE(
+      QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get()));
 }
 
 TEST_P(QuicChromiumClientSessionTest, PushStreamTimedOutNoResponse) {
@@ -801,8 +800,7 @@
   session_->OnProofVerifyDetailsAvailable(details);
 
   QuicChromiumClientStream* stream =
-      QuicChromiumClientSessionPeer::CreateOutgoingDynamicStream(
-          session_.get());
+      QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get());
   EXPECT_TRUE(stream);
 
   spdy::SpdyHeaderBlock promise_headers;
@@ -852,8 +850,7 @@
   session_->OnProofVerifyDetailsAvailable(details);
 
   QuicChromiumClientStream* stream =
-      QuicChromiumClientSessionPeer::CreateOutgoingDynamicStream(
-          session_.get());
+      QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get());
   EXPECT_TRUE(stream);
 
   spdy::SpdyHeaderBlock promise_headers;
@@ -908,8 +905,7 @@
   session_->OnProofVerifyDetailsAvailable(details);
 
   QuicChromiumClientStream* stream =
-      QuicChromiumClientSessionPeer::CreateOutgoingDynamicStream(
-          session_.get());
+      QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get());
   EXPECT_TRUE(stream);
 
   spdy::SpdyHeaderBlock promise_headers;
@@ -963,8 +959,7 @@
   session_->OnProofVerifyDetailsAvailable(details);
 
   QuicChromiumClientStream* stream =
-      QuicChromiumClientSessionPeer::CreateOutgoingDynamicStream(
-          session_.get());
+      QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get());
   EXPECT_TRUE(stream);
 
   spdy::SpdyHeaderBlock promise_headers;
@@ -1014,8 +1009,7 @@
   session_->OnProofVerifyDetailsAvailable(details);
 
   QuicChromiumClientStream* stream =
-      QuicChromiumClientSessionPeer::CreateOutgoingDynamicStream(
-          session_.get());
+      QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get());
   EXPECT_TRUE(stream);
 
   spdy::SpdyHeaderBlock promise_headers;
@@ -1068,8 +1062,7 @@
   std::vector<QuicChromiumClientStream*> streams;
   for (size_t i = 0; i < kMaxOpenStreams; i++) {
     QuicChromiumClientStream* stream =
-        QuicChromiumClientSessionPeer::CreateOutgoingDynamicStream(
-            session_.get());
+        QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get());
     EXPECT_TRUE(stream);
     streams.push_back(stream);
   }
@@ -1108,7 +1101,7 @@
   session_->connection()->OnGoAwayFrame(
       quic::QuicGoAwayFrame(quic::kInvalidControlFrameId,
                             quic::QUIC_PEER_GOING_AWAY, 1u, "Going away."));
-  EXPECT_EQ(nullptr, QuicChromiumClientSessionPeer::CreateOutgoingDynamicStream(
+  EXPECT_EQ(nullptr, QuicChromiumClientSessionPeer::CreateOutgoingStream(
                          session_.get()));
 }
 
@@ -1303,8 +1296,7 @@
 
   // Write data to session.
   QuicChromiumClientStream* stream =
-      QuicChromiumClientSessionPeer::CreateOutgoingDynamicStream(
-          session_.get());
+      QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get());
   struct iovec iov[1];
   iov[0].iov_base = data;
   iov[0].iov_len = 4;
@@ -1519,8 +1511,8 @@
 
   // Open a stream since the connection only sends PINGs to keep a
   // retransmittable packet on the wire if there's an open stream.
-  EXPECT_TRUE(QuicChromiumClientSessionPeer::CreateOutgoingDynamicStream(
-      session_.get()));
+  EXPECT_TRUE(
+      QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get()));
 
   quic::QuicAlarm* alarm =
       quic::test::QuicConnectionPeer::GetRetransmittableOnWireAlarm(
diff --git a/net/quic/quic_chromium_client_stream_test.cc b/net/quic/quic_chromium_client_stream_test.cc
index 99924ba..869042c 100644
--- a/net/quic/quic_chromium_client_stream_test.cc
+++ b/net/quic/quic_chromium_client_stream_test.cc
@@ -60,7 +60,7 @@
                void(quic::QuicErrorCode error,
                     const std::string& error_details,
                     quic::ConnectionCloseSource source));
-  MOCK_METHOD1(CreateIncomingDynamicStream,
+  MOCK_METHOD1(CreateIncomingStream,
                quic::QuicSpdyStream*(quic::QuicStreamId id));
   MOCK_METHOD0(CreateOutgoingBidirectionalStream, QuicChromiumClientStream*());
   MOCK_METHOD0(CreateOutgoingUnidirectionalStream, QuicChromiumClientStream*());
@@ -129,8 +129,8 @@
   bool IsAuthorized(const std::string& hostname) override { return true; }
 
  protected:
-  MOCK_METHOD1(ShouldCreateIncomingDynamicStream, bool(quic::QuicStreamId id));
-  MOCK_METHOD0(ShouldCreateOutgoingDynamicStream, bool());
+  MOCK_METHOD1(ShouldCreateIncomingStream, bool(quic::QuicStreamId id));
+  MOCK_METHOD0(ShouldCreateOutgoingStream, bool());
 
  private:
   std::unique_ptr<quic::QuicCryptoStream> crypto_stream_;
diff --git a/net/third_party/quic/core/http/quic_server_session_base.cc b/net/third_party/quic/core/http/quic_server_session_base.cc
index 1326676..fd73f2c7 100644
--- a/net/third_party/quic/core/http/quic_server_session_base.cc
+++ b/net/third_party/quic/core/http/quic_server_session_base.cc
@@ -192,9 +192,9 @@
       connection()->sent_packet_manager().GetLargestSentPacket();
 }
 
-bool QuicServerSessionBase::ShouldCreateIncomingDynamicStream(QuicStreamId id) {
+bool QuicServerSessionBase::ShouldCreateIncomingStream(QuicStreamId id) {
   if (!connection()->connected()) {
-    QUIC_BUG << "ShouldCreateIncomingDynamicStream called when disconnected";
+    QUIC_BUG << "ShouldCreateIncomingStream called when disconnected";
     return false;
   }
 
@@ -208,9 +208,9 @@
   return true;
 }
 
-bool QuicServerSessionBase::ShouldCreateOutgoingDynamicStream() {
+bool QuicServerSessionBase::ShouldCreateOutgoingStream() {
   if (!connection()->connected()) {
-    QUIC_BUG << "ShouldCreateOutgoingDynamicStream called when disconnected";
+    QUIC_BUG << "ShouldCreateOutgoingStream called when disconnected";
     return false;
   }
   if (!crypto_stream_->encryption_established()) {
diff --git a/net/third_party/quic/core/http/quic_server_session_base.h b/net/third_party/quic/core/http/quic_server_session_base.h
index 1ff4691..67f39b0 100644
--- a/net/third_party/quic/core/http/quic_server_session_base.h
+++ b/net/third_party/quic/core/http/quic_server_session_base.h
@@ -79,12 +79,12 @@
   // Return false when connection is closed or forward secure encryption hasn't
   // established yet or number of server initiated streams already reaches the
   // upper limit.
-  bool ShouldCreateOutgoingDynamicStream() override;
+  bool ShouldCreateOutgoingStream() override;
 
   // If we should create an incoming stream, returns true. Otherwise
   // does error handling, including communicating the error to the client and
   // possibly closing the connection, and returns false.
-  bool ShouldCreateIncomingDynamicStream(QuicStreamId id) override;
+  bool ShouldCreateIncomingStream(QuicStreamId id) override;
 
   virtual QuicCryptoServerStreamBase* CreateQuicCryptoServerStream(
       const QuicCryptoServerConfig* crypto_config,
diff --git a/net/third_party/quic/core/http/quic_server_session_base_test.cc b/net/third_party/quic/core/http/quic_server_session_base_test.cc
index 01d134c..f6816a46 100644
--- a/net/third_party/quic/core/http/quic_server_session_base_test.cc
+++ b/net/third_party/quic/core/http/quic_server_session_base_test.cc
@@ -63,8 +63,8 @@
   ~TestServerSession() override { delete connection(); };
 
  protected:
-  QuicSpdyStream* CreateIncomingDynamicStream(QuicStreamId id) override {
-    if (!ShouldCreateIncomingDynamicStream(id)) {
+  QuicSpdyStream* CreateIncomingStream(QuicStreamId id) override {
+    if (!ShouldCreateIncomingStream(id)) {
       return nullptr;
     }
     QuicSpdyStream* stream = new QuicSimpleServerStream(
@@ -79,7 +79,7 @@
   }
 
   QuicSpdyStream* CreateOutgoingUnidirectionalStream() override {
-    if (!ShouldCreateOutgoingDynamicStream()) {
+    if (!ShouldCreateOutgoingStream()) {
       return nullptr;
     }
 
@@ -355,7 +355,7 @@
   QuicConnectionPeer::TearDownLocalConnectionState(connection_);
   EXPECT_QUIC_BUG(QuicServerSessionBasePeer::GetOrCreateDynamicStream(
                       session_.get(), GetNthClientInitiatedId(0)),
-                  "ShouldCreateIncomingDynamicStream called when disconnected");
+                  "ShouldCreateIncomingStream called when disconnected");
 }
 
 class MockQuicCryptoServerStream : public QuicCryptoServerStream {
diff --git a/net/third_party/quic/core/http/quic_spdy_client_session.cc b/net/third_party/quic/core/http/quic_spdy_client_session.cc
index 24ff19b6..b756662 100644
--- a/net/third_party/quic/core/http/quic_spdy_client_session.cc
+++ b/net/third_party/quic/core/http/quic_spdy_client_session.cc
@@ -41,7 +41,7 @@
 void QuicSpdyClientSession::OnProofVerifyDetailsAvailable(
     const ProofVerifyDetails& /*verify_details*/) {}
 
-bool QuicSpdyClientSession::ShouldCreateOutgoingDynamicStream() {
+bool QuicSpdyClientSession::ShouldCreateOutgoingStream() {
   if (!crypto_stream_->encryption_established()) {
     QUIC_DLOG(INFO) << "Encryption not active so no outgoing stream created.";
     return false;
@@ -70,7 +70,7 @@
 
 QuicSpdyClientStream*
 QuicSpdyClientSession::CreateOutgoingBidirectionalStream() {
-  if (!ShouldCreateOutgoingDynamicStream()) {
+  if (!ShouldCreateOutgoingStream()) {
     return nullptr;
   }
   std::unique_ptr<QuicSpdyClientStream> stream = CreateClientStream();
@@ -113,9 +113,9 @@
   return crypto_stream_->num_scup_messages_received();
 }
 
-bool QuicSpdyClientSession::ShouldCreateIncomingDynamicStream(QuicStreamId id) {
+bool QuicSpdyClientSession::ShouldCreateIncomingStream(QuicStreamId id) {
   if (!connection()->connected()) {
-    QUIC_BUG << "ShouldCreateIncomingDynamicStream called when disconnected";
+    QUIC_BUG << "ShouldCreateIncomingStream called when disconnected";
     return false;
   }
   if (goaway_received() && respect_goaway_) {
@@ -133,9 +133,8 @@
   return true;
 }
 
-QuicSpdyStream* QuicSpdyClientSession::CreateIncomingDynamicStream(
-    QuicStreamId id) {
-  if (!ShouldCreateIncomingDynamicStream(id)) {
+QuicSpdyStream* QuicSpdyClientSession::CreateIncomingStream(QuicStreamId id) {
+  if (!ShouldCreateIncomingStream(id)) {
     return nullptr;
   }
   QuicSpdyStream* stream =
diff --git a/net/third_party/quic/core/http/quic_spdy_client_session.h b/net/third_party/quic/core/http/quic_spdy_client_session.h
index 9e1bc11..a3ca1669 100644
--- a/net/third_party/quic/core/http/quic_spdy_client_session.h
+++ b/net/third_party/quic/core/http/quic_spdy_client_session.h
@@ -65,12 +65,12 @@
 
  protected:
   // QuicSession methods:
-  QuicSpdyStream* CreateIncomingDynamicStream(QuicStreamId id) override;
+  QuicSpdyStream* CreateIncomingStream(QuicStreamId id) override;
   // If an outgoing stream can be created, return true.
-  bool ShouldCreateOutgoingDynamicStream() override;
+  bool ShouldCreateOutgoingStream() override;
 
   // If an incoming stream can be created, return true.
-  bool ShouldCreateIncomingDynamicStream(QuicStreamId id) override;
+  bool ShouldCreateIncomingStream(QuicStreamId id) override;
 
   // Create the crypto stream. Called by Initialize().
   virtual std::unique_ptr<QuicCryptoClientStreamBase> CreateQuicCryptoStream();
diff --git a/net/third_party/quic/core/http/quic_spdy_client_session_test.cc b/net/third_party/quic/core/http/quic_spdy_client_session_test.cc
index a9058c2..dcb4567 100644
--- a/net/third_party/quic/core/http/quic_spdy_client_session_test.cc
+++ b/net/third_party/quic/core/http/quic_spdy_client_session_test.cc
@@ -58,8 +58,7 @@
                                                     this, BIDIRECTIONAL);
   }
 
-  MockQuicSpdyClientStream* CreateIncomingDynamicStream(
-      QuicStreamId id) override {
+  MockQuicSpdyClientStream* CreateIncomingStream(QuicStreamId id) override {
     MockQuicSpdyClientStream* stream =
         new MockQuicSpdyClientStream(id, this, READ_UNIDIRECTIONAL);
     ActivateStream(QuicWrapUnique(stream));
diff --git a/net/third_party/quic/core/http/quic_spdy_session.h b/net/third_party/quic/core/http/quic_spdy_session.h
index 937a765..1bea32e9 100644
--- a/net/third_party/quic/core/http/quic_spdy_session.h
+++ b/net/third_party/quic/core/http/quic_spdy_session.h
@@ -135,21 +135,20 @@
   }
 
  protected:
-  // Override CreateIncomingDynamicStream(),
-  // CreateOutgoingBidirectionalStream() and
+  // Override CreateIncomingStream(), CreateOutgoingBidirectionalStream() and
   // CreateOutgoingUnidirectionalStream() with QuicSpdyStream return type to
   // make sure that all data streams are QuicSpdyStreams.
-  QuicSpdyStream* CreateIncomingDynamicStream(QuicStreamId id) override = 0;
+  QuicSpdyStream* CreateIncomingStream(QuicStreamId id) override = 0;
   QuicSpdyStream* CreateOutgoingBidirectionalStream() override = 0;
   QuicSpdyStream* CreateOutgoingUnidirectionalStream() override = 0;
 
   QuicSpdyStream* GetSpdyDataStream(const QuicStreamId stream_id);
 
   // If an incoming stream can be created, return true.
-  virtual bool ShouldCreateIncomingDynamicStream(QuicStreamId id) = 0;
+  virtual bool ShouldCreateIncomingStream(QuicStreamId id) = 0;
 
   // If an outgoing stream can be created, return true.
-  virtual bool ShouldCreateOutgoingDynamicStream() = 0;
+  virtual bool ShouldCreateOutgoingStream() = 0;
 
   // This was formerly QuicHeadersStream::WriteHeaders.  Needs to be
   // separate from QuicSpdySession::WriteHeaders because tests call
diff --git a/net/third_party/quic/core/http/quic_spdy_session_test.cc b/net/third_party/quic/core/http/quic_spdy_session_test.cc
index c1f9400c..206b47397 100644
--- a/net/third_party/quic/core/http/quic_spdy_session_test.cc
+++ b/net/third_party/quic/core/http/quic_spdy_session_test.cc
@@ -168,7 +168,7 @@
     return stream;
   }
 
-  TestStream* CreateIncomingDynamicStream(QuicStreamId id) override {
+  TestStream* CreateIncomingStream(QuicStreamId id) override {
     // Enforce the limit on the number of open streams.
     if (GetNumOpenIncomingStreams() + 1 > max_open_incoming_streams()) {
       connection()->CloseConnection(
@@ -182,11 +182,9 @@
     }
   }
 
-  bool ShouldCreateIncomingDynamicStream(QuicStreamId /*id*/) override {
-    return true;
-  }
+  bool ShouldCreateIncomingStream(QuicStreamId /*id*/) override { return true; }
 
-  bool ShouldCreateOutgoingDynamicStream() override { return true; }
+  bool ShouldCreateOutgoingStream() override { return true; }
 
   bool IsClosedStream(QuicStreamId id) {
     return QuicSession::IsClosedStream(id);
@@ -1605,7 +1603,7 @@
 
 TEST_P(QuicSpdySessionTestServer, OnPriorityFrame) {
   QuicStreamId stream_id = GetNthClientInitiatedId(0);
-  TestStream* stream = session_.CreateIncomingDynamicStream(stream_id);
+  TestStream* stream = session_.CreateIncomingStream(stream_id);
   session_.OnPriorityFrame(stream_id, kV3HighestPriority);
   EXPECT_EQ(kV3HighestPriority, stream->priority());
 }
diff --git a/net/third_party/quic/core/quic_dispatcher_test.cc b/net/third_party/quic/core/quic_dispatcher_test.cc
index ee06318..8c5ab72 100644
--- a/net/third_party/quic/core/quic_dispatcher_test.cc
+++ b/net/third_party/quic/core/quic_dispatcher_test.cc
@@ -75,7 +75,7 @@
                void(QuicErrorCode error,
                     const QuicString& error_details,
                     ConnectionCloseSource source));
-  MOCK_METHOD1(CreateIncomingDynamicStream, QuicSpdyStream*(QuicStreamId id));
+  MOCK_METHOD1(CreateIncomingStream, QuicSpdyStream*(QuicStreamId id));
   MOCK_METHOD0(CreateOutgoingBidirectionalStream, QuicSpdyStream*());
   MOCK_METHOD0(CreateOutgoingUnidirectionalStream, QuicSpdyStream*());
 
diff --git a/net/third_party/quic/core/quic_session.cc b/net/third_party/quic/core/quic_session.cc
index 69587ef3..e4d7450 100644
--- a/net/third_party/quic/core/quic_session.cc
+++ b/net/third_party/quic/core/quic_session.cc
@@ -931,7 +931,7 @@
     return nullptr;
   }
 
-  return CreateIncomingDynamicStream(stream_id);
+  return CreateIncomingStream(stream_id);
 }
 
 void QuicSession::set_max_open_incoming_streams(
diff --git a/net/third_party/quic/core/quic_session.h b/net/third_party/quic/core/quic_session.h
index b4d757d..da9bd76 100644
--- a/net/third_party/quic/core/quic_session.h
+++ b/net/third_party/quic/core/quic_session.h
@@ -356,7 +356,7 @@
   // Creates a new stream to handle a peer-initiated stream.
   // Caller does not own the returned stream.
   // Returns nullptr and does error handling if the stream can not be created.
-  virtual QuicStream* CreateIncomingDynamicStream(QuicStreamId id) = 0;
+  virtual QuicStream* CreateIncomingStream(QuicStreamId id) = 0;
 
   // Create a new stream to handle a locally-initiated bidirectional stream.
   // Caller does not own the returned stream.
diff --git a/net/third_party/quic/core/quic_session_test.cc b/net/third_party/quic/core/quic_session_test.cc
index d3b255c..d0d61f7 100644
--- a/net/third_party/quic/core/quic_session_test.cc
+++ b/net/third_party/quic/core/quic_session_test.cc
@@ -158,7 +158,7 @@
     return stream;
   }
 
-  TestStream* CreateIncomingDynamicStream(QuicStreamId id) override {
+  TestStream* CreateIncomingStream(QuicStreamId id) override {
     // Enforce the limit on the number of open streams.
     if (GetNumOpenIncomingStreams() + 1 > max_open_incoming_streams()) {
       connection()->CloseConnection(
diff --git a/net/third_party/quic/quartc/quartc_session.cc b/net/third_party/quic/quartc/quartc_session.cc
index 93de609..c08e0c9 100644
--- a/net/third_party/quic/quartc/quartc_session.cc
+++ b/net/third_party/quic/quartc/quartc_session.cc
@@ -309,7 +309,7 @@
   // TODO(zhihuang): Handle the proof verification.
 }
 
-QuicStream* QuartcSession::CreateIncomingDynamicStream(QuicStreamId id) {
+QuicStream* QuartcSession::CreateIncomingStream(QuicStreamId id) {
   return ActivateDataStream(CreateDataStream(id, QuicStream::kDefaultPriority));
 }
 
diff --git a/net/third_party/quic/quartc/quartc_session.h b/net/third_party/quic/quartc/quartc_session.h
index d0dd8af..742d9da 100644
--- a/net/third_party/quic/quartc/quartc_session.h
+++ b/net/third_party/quic/quartc/quartc_session.h
@@ -137,7 +137,7 @@
 
  protected:
   // QuicSession override.
-  QuicStream* CreateIncomingDynamicStream(QuicStreamId id) override;
+  QuicStream* CreateIncomingStream(QuicStreamId id) override;
 
   std::unique_ptr<QuartcStream> CreateDataStream(QuicStreamId id,
                                                  spdy::SpdyPriority priority);
diff --git a/net/third_party/quic/quartc/quartc_stream_test.cc b/net/third_party/quic/quartc/quartc_stream_test.cc
index cc8858a..f2ff1c4 100644
--- a/net/third_party/quic/quartc/quartc_stream_test.cc
+++ b/net/third_party/quic/quartc/quartc_stream_test.cc
@@ -58,7 +58,7 @@
     return QuicConsumedData(write_length, state != StreamSendingState::NO_FIN);
   }
 
-  QuartcStream* CreateIncomingDynamicStream(QuicStreamId id) override {
+  QuartcStream* CreateIncomingStream(QuicStreamId id) override {
     return nullptr;
   }
 
diff --git a/net/third_party/quic/test_tools/quic_test_server.cc b/net/third_party/quic/test_tools/quic_test_server.cc
index edcef09..6bbfe0b 100644
--- a/net/third_party/quic/test_tools/quic_test_server.cc
+++ b/net/third_party/quic/test_tools/quic_test_server.cc
@@ -37,8 +37,8 @@
         stream_factory_(stream_factory),
         crypto_stream_factory_(crypto_stream_factory) {}
 
-  QuicSpdyStream* CreateIncomingDynamicStream(QuicStreamId id) override {
-    if (!ShouldCreateIncomingDynamicStream(id)) {
+  QuicSpdyStream* CreateIncomingStream(QuicStreamId id) override {
+    if (!ShouldCreateIncomingStream(id)) {
       return nullptr;
     }
     if (stream_factory_) {
@@ -47,7 +47,7 @@
       ActivateStream(QuicWrapUnique(stream));
       return stream;
     }
-    return QuicSimpleServerSession::CreateIncomingDynamicStream(id);
+    return QuicSimpleServerSession::CreateIncomingStream(id);
   }
 
   QuicCryptoServerStreamBase* CreateQuicCryptoServerStream(
diff --git a/net/third_party/quic/test_tools/quic_test_utils.h b/net/third_party/quic/test_tools/quic_test_utils.h
index 78273700..30a2d6c6 100644
--- a/net/third_party/quic/test_tools/quic_test_utils.h
+++ b/net/third_party/quic/test_tools/quic_test_utils.h
@@ -557,11 +557,11 @@
                void(QuicErrorCode error,
                     const QuicString& error_details,
                     ConnectionCloseSource source));
-  MOCK_METHOD1(CreateIncomingDynamicStream, QuicStream*(QuicStreamId id));
+  MOCK_METHOD1(CreateIncomingStream, QuicStream*(QuicStreamId id));
   MOCK_METHOD0(CreateOutgoingBidirectionalStream, QuicStream*());
   MOCK_METHOD0(CreateOutgoingUnidirectionalStream, QuicStream*());
-  MOCK_METHOD1(ShouldCreateIncomingDynamicStream2, bool(QuicStreamId id));
-  MOCK_METHOD0(ShouldCreateOutgoingDynamicStream2, bool());
+  MOCK_METHOD1(ShouldCreateIncomingStream2, bool(QuicStreamId id));
+  MOCK_METHOD0(ShouldCreateOutgoingStream2, bool());
   MOCK_METHOD5(WritevData,
                QuicConsumedData(QuicStream* stream,
                                 QuicStreamId id,
@@ -635,11 +635,11 @@
                void(QuicErrorCode error,
                     const QuicString& error_details,
                     ConnectionCloseSource source));
-  MOCK_METHOD1(CreateIncomingDynamicStream, QuicSpdyStream*(QuicStreamId id));
+  MOCK_METHOD1(CreateIncomingStream, QuicSpdyStream*(QuicStreamId id));
   MOCK_METHOD0(CreateOutgoingBidirectionalStream, QuicSpdyStream*());
   MOCK_METHOD0(CreateOutgoingUnidirectionalStream, QuicSpdyStream*());
-  MOCK_METHOD1(ShouldCreateIncomingDynamicStream, bool(QuicStreamId id));
-  MOCK_METHOD0(ShouldCreateOutgoingDynamicStream, bool());
+  MOCK_METHOD1(ShouldCreateIncomingStream, bool(QuicStreamId id));
+  MOCK_METHOD0(ShouldCreateOutgoingStream, bool());
   MOCK_METHOD5(WritevData,
                QuicConsumedData(QuicStream* stream,
                                 QuicStreamId id,
@@ -718,7 +718,7 @@
       delete;
   ~TestQuicSpdyServerSession() override;
 
-  MOCK_METHOD1(CreateIncomingDynamicStream, QuicSpdyStream*(QuicStreamId id));
+  MOCK_METHOD1(CreateIncomingStream, QuicSpdyStream*(QuicStreamId id));
   MOCK_METHOD0(CreateOutgoingBidirectionalStream, QuicSpdyStream*());
   MOCK_METHOD0(CreateOutgoingUnidirectionalStream, QuicSpdyStream*());
   QuicCryptoServerStreamBase* CreateQuicCryptoServerStream(
@@ -781,11 +781,11 @@
                void(const ProofVerifyDetails& verify_details));
 
   // TestQuicSpdyClientSession
-  MOCK_METHOD1(CreateIncomingDynamicStream, QuicSpdyStream*(QuicStreamId id));
+  MOCK_METHOD1(CreateIncomingStream, QuicSpdyStream*(QuicStreamId id));
   MOCK_METHOD0(CreateOutgoingBidirectionalStream, QuicSpdyStream*());
   MOCK_METHOD0(CreateOutgoingUnidirectionalStream, QuicSpdyStream*());
-  MOCK_METHOD1(ShouldCreateIncomingDynamicStream, bool(QuicStreamId id));
-  MOCK_METHOD0(ShouldCreateOutgoingDynamicStream, bool());
+  MOCK_METHOD1(ShouldCreateIncomingStream, bool(QuicStreamId id));
+  MOCK_METHOD0(ShouldCreateOutgoingStream, bool());
 
   // Override to not send max header list size.
   void OnCryptoHandshakeEvent(CryptoHandshakeEvent event) override;
diff --git a/net/third_party/quic/tools/quic_simple_server_session.cc b/net/third_party/quic/tools/quic_simple_server_session.cc
index 64d081f..2288691 100644
--- a/net/third_party/quic/tools/quic_simple_server_session.cc
+++ b/net/third_party/quic/tools/quic_simple_server_session.cc
@@ -86,9 +86,8 @@
   HandlePromisedPushRequests();
 }
 
-QuicSpdyStream* QuicSimpleServerSession::CreateIncomingDynamicStream(
-    QuicStreamId id) {
-  if (!ShouldCreateIncomingDynamicStream(id)) {
+QuicSpdyStream* QuicSimpleServerSession::CreateIncomingStream(QuicStreamId id) {
+  if (!ShouldCreateIncomingStream(id)) {
     return nullptr;
   }
 
@@ -106,7 +105,7 @@
 
 QuicSimpleServerStream*
 QuicSimpleServerSession::CreateOutgoingUnidirectionalStream() {
-  if (!ShouldCreateOutgoingDynamicStream()) {
+  if (!ShouldCreateOutgoingStream()) {
     return nullptr;
   }
 
@@ -187,7 +186,7 @@
 }
 
 void QuicSimpleServerSession::HandlePromisedPushRequests() {
-  while (!promised_streams_.empty() && ShouldCreateOutgoingDynamicStream()) {
+  while (!promised_streams_.empty() && ShouldCreateOutgoingStream()) {
     PromisedStreamInfo& promised_info = promised_streams_.front();
     DCHECK_EQ(next_outgoing_stream_id(), promised_info.stream_id);
 
diff --git a/net/third_party/quic/tools/quic_simple_server_session.h b/net/third_party/quic/tools/quic_simple_server_session.h
index bc105b9..6e5160a 100644
--- a/net/third_party/quic/tools/quic_simple_server_session.h
+++ b/net/third_party/quic/tools/quic_simple_server_session.h
@@ -85,7 +85,7 @@
 
  protected:
   // QuicSession methods:
-  QuicSpdyStream* CreateIncomingDynamicStream(QuicStreamId id) override;
+  QuicSpdyStream* CreateIncomingStream(QuicStreamId id) override;
   QuicSimpleServerStream* CreateOutgoingBidirectionalStream() override;
   QuicSimpleServerStream* CreateOutgoingUnidirectionalStream() override;
   // Closing an outgoing stream can reduce open outgoing stream count, try
diff --git a/net/third_party/quic/tools/quic_simple_server_session_test.cc b/net/third_party/quic/tools/quic_simple_server_session_test.cc
index 4f555619..e24d917 100644
--- a/net/third_party/quic/tools/quic_simple_server_session_test.cc
+++ b/net/third_party/quic/tools/quic_simple_server_session_test.cc
@@ -58,9 +58,9 @@
     s->RegisterStaticStream(kCryptoStreamId, crypto_stream);
   }
 
-  static QuicSpdyStream* CreateIncomingDynamicStream(QuicSimpleServerSession* s,
-                                                     QuicStreamId id) {
-    return s->CreateIncomingDynamicStream(id);
+  static QuicSpdyStream* CreateIncomingStream(QuicSimpleServerSession* s,
+                                              QuicStreamId id) {
+    return s->CreateIncomingStream(id);
   }
 
   static QuicSimpleServerStream* CreateOutgoingUnidirectionalStream(
@@ -326,7 +326,7 @@
   EXPECT_TRUE(connection_->connected());
 }
 
-TEST_P(QuicSimpleServerSessionTest, CreateIncomingDynamicStreamDisconnected) {
+TEST_P(QuicSimpleServerSessionTest, CreateIncomingStreamDisconnected) {
   // EXPECT_QUIC_BUG tests are expensive so only run one instance of them.
   if (GetParam() != AllSupportedVersions()[0]) {
     return;
@@ -335,9 +335,9 @@
   // Tests that incoming stream creation fails when connection is not connected.
   size_t initial_num_open_stream = session_->GetNumOpenIncomingStreams();
   QuicConnectionPeer::TearDownLocalConnectionState(connection_);
-  EXPECT_QUIC_BUG(QuicSimpleServerSessionPeer::CreateIncomingDynamicStream(
+  EXPECT_QUIC_BUG(QuicSimpleServerSessionPeer::CreateIncomingStream(
                       session_.get(), GetNthClientInitiatedId(0)),
-                  "ShouldCreateIncomingDynamicStream called when disconnected");
+                  "ShouldCreateIncomingStream called when disconnected");
   EXPECT_EQ(initial_num_open_stream, session_->GetNumOpenIncomingStreams());
 }
 
@@ -347,14 +347,13 @@
   EXPECT_CALL(*connection_,
               CloseConnection(QUIC_INVALID_STREAM_ID,
                               "Client created even numbered stream", _));
-  QuicSimpleServerSessionPeer::CreateIncomingDynamicStream(session_.get(), 2);
+  QuicSimpleServerSessionPeer::CreateIncomingStream(session_.get(), 2);
   EXPECT_EQ(initial_num_open_stream, session_->GetNumOpenIncomingStreams());
 }
 
-TEST_P(QuicSimpleServerSessionTest, CreateIncomingDynamicStream) {
-  QuicSpdyStream* stream =
-      QuicSimpleServerSessionPeer::CreateIncomingDynamicStream(
-          session_.get(), GetNthClientInitiatedId(0));
+TEST_P(QuicSimpleServerSessionTest, CreateIncomingStream) {
+  QuicSpdyStream* stream = QuicSimpleServerSessionPeer::CreateIncomingStream(
+      session_.get(), GetNthClientInitiatedId(0));
   EXPECT_NE(nullptr, stream);
   EXPECT_EQ(GetNthClientInitiatedId(0), stream->id());
 }
@@ -371,7 +370,7 @@
   EXPECT_QUIC_BUG(
       QuicSimpleServerSessionPeer::CreateOutgoingUnidirectionalStream(
           session_.get()),
-      "ShouldCreateOutgoingDynamicStream called when disconnected");
+      "ShouldCreateOutgoingStream called when disconnected");
 
   EXPECT_EQ(initial_num_open_stream, session_->GetNumOpenOutgoingStreams());
 }
diff --git a/net/third_party/quic/tools/quic_simple_server_stream_test.cc b/net/third_party/quic/tools/quic_simple_server_stream_test.cc
index 026b8a1..23324ca0 100644
--- a/net/third_party/quic/tools/quic_simple_server_stream_test.cc
+++ b/net/third_party/quic/tools/quic_simple_server_stream_test.cc
@@ -115,7 +115,7 @@
                void(QuicErrorCode error,
                     const QuicString& error_details,
                     ConnectionCloseSource source));
-  MOCK_METHOD1(CreateIncomingDynamicStream, QuicSpdyStream*(QuicStreamId id));
+  MOCK_METHOD1(CreateIncomingStream, QuicSpdyStream*(QuicStreamId id));
   MOCK_METHOD5(WritevData,
                QuicConsumedData(QuicStream* stream,
                                 QuicStreamId id,
diff --git a/services/identity/public/cpp/identity_test_environment.cc b/services/identity/public/cpp/identity_test_environment.cc
index 686229f..46e8eb7 100644
--- a/services/identity/public/cpp/identity_test_environment.cc
+++ b/services/identity/public/cpp/identity_test_environment.cc
@@ -5,33 +5,19 @@
 #include "services/identity/public/cpp/identity_test_environment.h"
 
 #include "base/run_loop.h"
-#include "components/signin/core/browser/account_tracker_service.h"
-#include "components/signin/core/browser/fake_gaia_cookie_manager_service.h"
-#include "components/signin/core/browser/fake_profile_oauth2_token_service.h"
-#include "components/signin/core/browser/fake_signin_manager.h"
 #include "components/signin/core/browser/profile_management_switches.h"
 #include "components/signin/core/browser/test_signin_client.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "google_apis/gaia/oauth2_access_token_consumer.h"
 #include "services/identity/public/cpp/identity_test_utils.h"
 
-#if defined(OS_CHROMEOS)
-using SigninManagerForTest = FakeSigninManagerBase;
-#else
-using SigninManagerForTest = FakeSigninManager;
-#endif  // OS_CHROMEOS
-
 namespace identity {
 
-// Internal class that abstracts the dependencies out of the public interface.
-class IdentityTestEnvironmentInternal {
+class IdentityManagerDependenciesOwner {
  public:
-  IdentityTestEnvironmentInternal(
+  IdentityManagerDependenciesOwner(
       bool use_fake_url_loader_for_gaia_cookie_manager);
-  ~IdentityTestEnvironmentInternal();
-
-  // The IdentityManager instance created and owned by this instance.
-  IdentityManager* identity_manager();
+  ~IdentityManagerDependenciesOwner();
 
   AccountTrackerService* account_tracker_service();
 
@@ -48,12 +34,11 @@
   FakeProfileOAuth2TokenService token_service_;
   SigninManagerForTest signin_manager_;
   FakeGaiaCookieManagerService gaia_cookie_manager_service_;
-  std::unique_ptr<IdentityManager> identity_manager_;
 
-  DISALLOW_COPY_AND_ASSIGN(IdentityTestEnvironmentInternal);
+  DISALLOW_COPY_AND_ASSIGN(IdentityManagerDependenciesOwner);
 };
 
-IdentityTestEnvironmentInternal::IdentityTestEnvironmentInternal(
+IdentityManagerDependenciesOwner::IdentityManagerDependenciesOwner(
     bool use_fake_url_loader_for_gaia_cookie_manager)
     : signin_client_(&pref_service_),
       token_service_(&pref_service_),
@@ -86,119 +71,157 @@
 
   account_tracker_.Initialize(&pref_service_, base::FilePath());
 
-  identity_manager_.reset(new IdentityManager(&signin_manager_, &token_service_,
-                                              &account_tracker_,
-                                              &gaia_cookie_manager_service_));
 }
 
-IdentityTestEnvironmentInternal::~IdentityTestEnvironmentInternal() {}
-
-IdentityManager* IdentityTestEnvironmentInternal::identity_manager() {
-  return identity_manager_.get();
-}
+IdentityManagerDependenciesOwner::~IdentityManagerDependenciesOwner() {}
 
 AccountTrackerService*
-IdentityTestEnvironmentInternal::account_tracker_service() {
+IdentityManagerDependenciesOwner::account_tracker_service() {
   return &account_tracker_;
 }
 
-SigninManagerForTest* IdentityTestEnvironmentInternal::signin_manager() {
+SigninManagerForTest* IdentityManagerDependenciesOwner::signin_manager() {
   return &signin_manager_;
 }
 
 FakeProfileOAuth2TokenService*
-IdentityTestEnvironmentInternal::token_service() {
+IdentityManagerDependenciesOwner::token_service() {
   return &token_service_;
 }
 
 FakeGaiaCookieManagerService*
-IdentityTestEnvironmentInternal::gaia_cookie_manager_service() {
+IdentityManagerDependenciesOwner::gaia_cookie_manager_service() {
   return &gaia_cookie_manager_service_;
 }
 
 IdentityTestEnvironment::IdentityTestEnvironment(
     bool use_fake_url_loader_for_gaia_cookie_manager)
-    : internals_(std::make_unique<IdentityTestEnvironmentInternal>(
-          use_fake_url_loader_for_gaia_cookie_manager)) {
-  internals_->identity_manager()->AddDiagnosticsObserver(this);
+    : IdentityTestEnvironment(
+          /*account_tracker_service=*/nullptr,
+          /*token_service=*/nullptr,
+          /*signin_manager=*/nullptr,
+          /*gaia_cookie_manager_service=*/nullptr,
+          std::make_unique<IdentityManagerDependenciesOwner>(
+              use_fake_url_loader_for_gaia_cookie_manager)) {}
+
+IdentityTestEnvironment::IdentityTestEnvironment(
+    AccountTrackerService* account_tracker_service,
+    FakeProfileOAuth2TokenService* token_service,
+    SigninManagerForTest* signin_manager,
+    FakeGaiaCookieManagerService* gaia_cookie_manager_service)
+    : IdentityTestEnvironment(account_tracker_service,
+                              token_service,
+                              signin_manager,
+                              gaia_cookie_manager_service,
+                              /*dependency_owner=*/nullptr) {}
+
+IdentityTestEnvironment::IdentityTestEnvironment(
+    AccountTrackerService* account_tracker_service,
+    FakeProfileOAuth2TokenService* token_service,
+    SigninManagerForTest* signin_manager,
+    FakeGaiaCookieManagerService* gaia_cookie_manager_service,
+    std::unique_ptr<IdentityManagerDependenciesOwner> dependencies_owner) {
+  if (dependencies_owner) {
+    DCHECK(!(account_tracker_service || token_service || signin_manager ||
+             gaia_cookie_manager_service));
+
+    dependencies_owner_ = std::move(dependencies_owner);
+
+    account_tracker_service_ = dependencies_owner_->account_tracker_service();
+    token_service_ = dependencies_owner_->token_service();
+    signin_manager_ = dependencies_owner_->signin_manager();
+    gaia_cookie_manager_service_ =
+        dependencies_owner_->gaia_cookie_manager_service();
+
+  } else {
+    DCHECK(account_tracker_service && token_service && signin_manager &&
+           gaia_cookie_manager_service);
+
+    account_tracker_service_ = account_tracker_service;
+    token_service_ = token_service;
+    signin_manager_ = signin_manager;
+    gaia_cookie_manager_service_ = gaia_cookie_manager_service;
+  }
+
+  identity_manager_ = std::make_unique<IdentityManager>(
+      signin_manager_, token_service_, account_tracker_service_,
+      gaia_cookie_manager_service_);
+
+  identity_manager_->AddDiagnosticsObserver(this);
 }
 
 IdentityTestEnvironment::~IdentityTestEnvironment() {
-  internals_->identity_manager()->RemoveDiagnosticsObserver(this);
+  identity_manager_->RemoveDiagnosticsObserver(this);
 }
 
 IdentityManager* IdentityTestEnvironment::identity_manager() {
-  return internals_->identity_manager();
+  return identity_manager_.get();
 }
 
 AccountInfo IdentityTestEnvironment::SetPrimaryAccount(
     const std::string& email) {
-  return identity::SetPrimaryAccount(internals_->signin_manager(),
-                                     internals_->identity_manager(), email);
+  return identity::SetPrimaryAccount(signin_manager_, identity_manager(),
+                                     email);
 }
 
 void IdentityTestEnvironment::SetRefreshTokenForPrimaryAccount() {
-  identity::SetRefreshTokenForPrimaryAccount(internals_->token_service(),
-                                             internals_->identity_manager());
+  identity::SetRefreshTokenForPrimaryAccount(token_service_,
+                                             identity_manager());
 }
 
 void IdentityTestEnvironment::SetInvalidRefreshTokenForPrimaryAccount() {
-  identity::SetInvalidRefreshTokenForPrimaryAccount(
-      internals_->token_service(), internals_->identity_manager());
+  identity::SetInvalidRefreshTokenForPrimaryAccount(token_service_,
+                                                    identity_manager());
 }
 
 void IdentityTestEnvironment::RemoveRefreshTokenForPrimaryAccount() {
-  identity::RemoveRefreshTokenForPrimaryAccount(internals_->token_service(),
-                                                internals_->identity_manager());
+  identity::RemoveRefreshTokenForPrimaryAccount(token_service_,
+                                                identity_manager());
 }
 
 AccountInfo IdentityTestEnvironment::MakePrimaryAccountAvailable(
     const std::string& email) {
-  return identity::MakePrimaryAccountAvailable(
-      internals_->signin_manager(), internals_->token_service(),
-      internals_->identity_manager(), email);
+  return identity::MakePrimaryAccountAvailable(signin_manager_, token_service_,
+                                               identity_manager(), email);
 }
 
 void IdentityTestEnvironment::ClearPrimaryAccount(
     ClearPrimaryAccountPolicy policy) {
-  identity::ClearPrimaryAccount(internals_->signin_manager(),
-                                internals_->identity_manager(), policy);
+  identity::ClearPrimaryAccount(signin_manager_, identity_manager(), policy);
 }
 
 AccountInfo IdentityTestEnvironment::MakeAccountAvailable(
     const std::string& email) {
-  return identity::MakeAccountAvailable(internals_->account_tracker_service(),
-                                        internals_->token_service(),
-                                        internals_->identity_manager(), email);
+  return identity::MakeAccountAvailable(
+      account_tracker_service_, token_service_, identity_manager(), email);
 }
 
 void IdentityTestEnvironment::SetRefreshTokenForAccount(
     const std::string& account_id) {
-  return identity::SetRefreshTokenForAccount(
-      internals_->token_service(), internals_->identity_manager(), account_id);
+  return identity::SetRefreshTokenForAccount(token_service_, identity_manager(),
+                                             account_id);
 }
 
 void IdentityTestEnvironment::SetInvalidRefreshTokenForAccount(
     const std::string& account_id) {
   return identity::SetInvalidRefreshTokenForAccount(
-      internals_->token_service(), internals_->identity_manager(), account_id);
+      token_service_, identity_manager(), account_id);
 }
 
 void IdentityTestEnvironment::RemoveRefreshTokenForAccount(
     const std::string& account_id) {
-  return identity::RemoveRefreshTokenForAccount(
-      internals_->token_service(), internals_->identity_manager(), account_id);
+  return identity::RemoveRefreshTokenForAccount(token_service_,
+                                                identity_manager(), account_id);
 }
 
 void IdentityTestEnvironment::SetCookieAccounts(
     const std::vector<CookieParams>& cookie_accounts) {
-  identity::SetCookieAccounts(internals_->gaia_cookie_manager_service(),
-                              internals_->identity_manager(), cookie_accounts);
+  identity::SetCookieAccounts(gaia_cookie_manager_service_, identity_manager(),
+                              cookie_accounts);
 }
 
 void IdentityTestEnvironment::SetAutomaticIssueOfAccessTokens(bool grant) {
-  internals_->token_service()->set_auto_post_fetch_response_on_message_loop(
-      grant);
+  token_service_->set_auto_post_fetch_response_on_message_loop(grant);
 }
 
 void IdentityTestEnvironment::
@@ -206,8 +229,7 @@
         const std::string& token,
         const base::Time& expiration) {
   WaitForAccessTokenRequestIfNecessary(base::nullopt);
-  internals_->token_service()->IssueTokenForAllPendingRequests(token,
-                                                               expiration);
+  token_service_->IssueTokenForAllPendingRequests(token, expiration);
 }
 
 void IdentityTestEnvironment::
@@ -216,7 +238,7 @@
         const base::Time& expiration,
         const std::string& id_token) {
   WaitForAccessTokenRequestIfNecessary(base::nullopt);
-  internals_->token_service()->IssueTokenForAllPendingRequests(
+  token_service_->IssueTokenForAllPendingRequests(
       OAuth2AccessTokenConsumer::TokenResponse(token, expiration, id_token));
 }
 
@@ -226,15 +248,14 @@
         const std::string& token,
         const base::Time& expiration) {
   WaitForAccessTokenRequestIfNecessary(account_id);
-  internals_->token_service()->IssueAllTokensForAccount(account_id, token,
-                                                        expiration);
+  token_service_->IssueAllTokensForAccount(account_id, token, expiration);
 }
 
 void IdentityTestEnvironment::
     WaitForAccessTokenRequestIfNecessaryAndRespondWithError(
         const GoogleServiceAuthError& error) {
   WaitForAccessTokenRequestIfNecessary(base::nullopt);
-  internals_->token_service()->IssueErrorForAllPendingRequests(error);
+  token_service_->IssueErrorForAllPendingRequests(error);
 }
 
 void IdentityTestEnvironment::
@@ -242,8 +263,7 @@
         const std::string& account_id,
         const GoogleServiceAuthError& error) {
   WaitForAccessTokenRequestIfNecessary(account_id);
-  internals_->token_service()->IssueErrorForAllPendingRequestsForAccount(
-      account_id, error);
+  token_service_->IssueErrorForAllPendingRequestsForAccount(account_id, error);
 }
 
 void IdentityTestEnvironment::SetCallbackForNextAccessTokenRequest(
diff --git a/services/identity/public/cpp/identity_test_environment.h b/services/identity/public/cpp/identity_test_environment.h
index e473eec..9c1700a 100644
--- a/services/identity/public/cpp/identity_test_environment.h
+++ b/services/identity/public/cpp/identity_test_environment.h
@@ -6,21 +6,52 @@
 #define SERVICES_IDENTITY_PUBLIC_CPP_IDENTITY_TEST_ENVIRONMENT_H_
 
 #include "base/optional.h"
+#include "components/signin/core/browser/account_tracker_service.h"
+#include "components/signin/core/browser/fake_gaia_cookie_manager_service.h"
+#include "components/signin/core/browser/fake_profile_oauth2_token_service.h"
+#include "components/signin/core/browser/fake_signin_manager.h"
 #include "services/identity/public/cpp/identity_manager.h"
 #include "services/identity/public/cpp/identity_test_utils.h"
 
 namespace identity {
 
-class IdentityTestEnvironmentInternal;
+// Internal class that creates and owns dependencies of IdentityManager
+// when those dependencies are not passed in externally.
+class IdentityManagerDependenciesOwner;
 
 // Class that creates an IdentityManager for use in testing contexts and
 // provides facilities for driving that IdentityManager. The IdentityManager
 // instance is brought up in an environment where the primary account is
 // not available; call MakePrimaryAccountAvailable() as needed.
 class IdentityTestEnvironment : public IdentityManager::DiagnosticsObserver {
+#if defined(OS_CHROMEOS)
+  using SigninManagerForTest = FakeSigninManagerBase;
+#else
+  using SigninManagerForTest = FakeSigninManager;
+#endif  // OS_CHROMEOS
+
  public:
+  // Preferred constructor: constructs an IdentityManager object and its
+  // dependencies internally. Cannot be used if the client of this class
+  // is still interacting directly with those dependencies (e.g., if
+  // IdentityTestEnvironment is being introduced to incrementally convert
+  // a test). In that case, use the below constructor and switch to this
+  // constructor once the conversion is complete.
   IdentityTestEnvironment(
       bool use_fake_url_loader_for_gaia_cookie_manager = false);
+
+  // Constructor that takes in instances of the dependencies of
+  // IdentityManager and constructs an IdentityManager instance from those
+  // dependencies. For use in contexts where those dependencies are still
+  // being used directly by the creator of this object (i.e., while a test is
+  // being incrementally converted). Prefer the above constructor, and switch to
+  // that constructor once possible (e.g., when an incremental conversion is
+  // completed). NOTE: The passed-in objects must all outlive this object.
+  IdentityTestEnvironment(
+      AccountTrackerService* account_tracker_service,
+      FakeProfileOAuth2TokenService* token_service,
+      SigninManagerForTest* signin_manager,
+      FakeGaiaCookieManagerService* gaia_cookie_manager_service);
   ~IdentityTestEnvironment() override;
 
   // The IdentityManager instance created and owned by this instance.
@@ -180,6 +211,17 @@
     base::OnceClosure on_available;
   };
 
+  // Constructs this object and its IdentityManager instance from the supplied
+  // dependencies of IdentityManager, which must be either:
+  // (1) non-null instances of the backing classes, OR
+  // (2) a non-null instance of |dependencies_owner|.
+  IdentityTestEnvironment(
+      AccountTrackerService* account_tracker_service,
+      FakeProfileOAuth2TokenService* token_service,
+      SigninManagerForTest* signin_manager,
+      FakeGaiaCookieManagerService* gaia_cookie_manager_service,
+      std::unique_ptr<IdentityManagerDependenciesOwner> dependencies_owner);
+
   // IdentityManager::DiagnosticsObserver:
   void OnAccessTokenRequested(
       const std::string& account_id,
@@ -199,7 +241,16 @@
   void WaitForAccessTokenRequestIfNecessary(
       base::Optional<std::string> account_id);
 
-  std::unique_ptr<IdentityTestEnvironmentInternal> internals_;
+  // NOTE: This object must be first in the list, as it owns the objects
+  // pointed to below in the case where those objects are not passed in via
+  // the IdentityTestEnvironment constructor.
+  std::unique_ptr<IdentityManagerDependenciesOwner> dependencies_owner_;
+  AccountTrackerService* account_tracker_service_ = nullptr;
+  FakeProfileOAuth2TokenService* token_service_ = nullptr;
+  SigninManagerForTest* signin_manager_ = nullptr;
+  FakeGaiaCookieManagerService* gaia_cookie_manager_service_ = nullptr;
+  std::unique_ptr<IdentityManager> identity_manager_;
+
   base::OnceClosure on_access_token_requested_callback_;
   std::vector<AccessTokenRequestState> requesters_;
 
diff --git a/third_party/OWNERS b/third_party/OWNERS
index 6447bcb..7b36d33 100644
--- a/third_party/OWNERS
+++ b/third_party/OWNERS
@@ -14,10 +14,6 @@
 # Eng reviewer
 file://ENG_REVIEW_OWNERS
 
-# Automatic round-robin assignment of reviewer for third-party licenses.
-# No one receives email to this list, just use it as a reviewer.
-chromium-third-party@google.com
-
 thakis@chromium.org
 
 per-file .gitignore=*
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
index b6ad3cd..92389fa9 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
@@ -25,7 +25,7 @@
 crbug.com/636993 external/wpt/css/css-text-decor/text-decoration-color.html [ Failure ]
 
 # text-overflow:ellipsis and paint fragment
-crbug.com/873957 accessibility/ellipsis-text.html [ Failure ]
+crbug.com/873957 accessibility/ellipsis-text.html [ Failure Pass ]
 crbug.com/873957 http/tests/devtools/network/network-cookies-pane.js [ Failure ]
 
 # rightsizing-grid.html is truly flaky, show flakiness on reload
@@ -84,6 +84,8 @@
 crbug.com/591099 external/wpt/css/css-text/line-breaking/line-breaking-ic-003.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-text/overflow-wrap/overflow-wrap-min-content-size-002.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-text/overflow-wrap/overflow-wrap-min-content-size-003.html [ Pass ]
+crbug.com/591099 external/wpt/css/css-text/white-space/line-edge-white-space-collapse-001.html [ Pass ]
+crbug.com/591099 external/wpt/css/css-text/white-space/line-edge-white-space-collapse-002.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-text/white-space/pre-wrap-002.html [ Pass ]
 crbug.com/40634 external/wpt/css/css-text/white-space/trailing-space-before-br-001.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-text/word-break/word-break-break-all-004.html [ Pass ]
@@ -194,6 +196,11 @@
 crbug.com/591099 external/wpt/css/selectors/selector-placeholder-shown-type-change-001.html [ Pass ]
 crbug.com/591099 external/wpt/css/selectors/selector-read-write-type-change-002.html [ Pass ]
 crbug.com/591099 external/wpt/css/selectors/selector-required-type-change-002.html [ Pass ]
+crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-size-block-001.html [ Pass ]
+crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-size-fieldset-001.html [ Pass ]
+crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-size-fieldset-002.html [ Pass ]
+crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-size-inline-block-001.html [ Pass ]
+crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-size-inline-flex-001.html [ Pass ]
 crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-002.xhtml [ Pass ]
 crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-011.html [ Pass ]
 crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-012.html [ Pass ]
@@ -249,9 +256,9 @@
 crbug.com/591099 external/wpt/html/semantics/embedded-content/media-elements/interfaces/TextTrackCue/track.html [ Pass ]
 crbug.com/591099 external/wpt/html/semantics/embedded-content/media-elements/track/track-element/cloneNode.html [ Pass ]
 crbug.com/591099 external/wpt/html/semantics/scripting-1/the-script-element/async_010.htm [ Pass ]
-crbug.com/591099 external/wpt/picture-in-picture/request-picture-in-picture-twice.html [ Pass ]
+crbug.com/591099 external/wpt/picture-in-picture/request-picture-in-picture-twice.html [ Failure Pass Timeout ]
 crbug.com/591099 external/wpt/quirks/line-height-calculation.html [ Failure ]
-crbug.com/591099 external/wpt/requestidlecallback/callback-iframe.html [ Pass ]
+crbug.com/591099 external/wpt/requestidlecallback/callback-iframe.html [ Pass Timeout ]
 crbug.com/591099 external/wpt/webrtc/RTCDTMFSender-ontonechange.https.html [ Pass ]
 crbug.com/591099 external/wpt/webrtc/RTCPeerConnection-setLocalDescription-answer.html [ Pass ]
 crbug.com/591099 external/wpt/workers/Worker_ErrorEvent_error.htm [ Pass ]
@@ -287,7 +294,7 @@
 crbug.com/835484 fast/inline/outline-offset.html [ Failure ]
 crbug.com/591099 fast/overflow/overflow-update-transform.html [ Failure ]
 crbug.com/591099 fast/replaced/table-replaced-element.html [ Failure ]
-crbug.com/591099 fast/scrolling/scrollbar-tickmarks-hittest.html [ Failure ]
+crbug.com/591099 fast/scrolling/scrollbar-tickmarks-hittest.html [ Failure Pass ]
 crbug.com/591099 fast/sub-pixel/sub-pixel-border-2.html [ Failure ]
 crbug.com/591099 fast/table/border-collapsing/004-vertical.html [ Failure ]
 crbug.com/591099 fast/table/border-collapsing/composited-cell-collapsed-border.html [ Failure ]
@@ -327,7 +334,7 @@
 crbug.com/591099 http/tests/security/cors-rfc1918/addressspace-document-appcache.https.html [ Crash Failure ]
 crbug.com/591099 http/tests/security/cors-rfc1918/addressspace-document-csp-appcache.https.html [ Crash Failure Pass ]
 crbug.com/591099 http/tests/security/setDomainRelaxationForbiddenForURLScheme.html [ Crash ]
-crbug.com/591099 idle-callback/test-runner-run-idle-tasks.html [ Pass Timeout ]
+crbug.com/591099 idle-callback/test-runner-run-idle-tasks.html [ Crash Pass ]
 crbug.com/591099 images/color-profile-image-filter-all.html [ Failure ]
 crbug.com/591099 inspector-protocol/dom-snapshot/dom-snapshot-getSnapshot-pseudo-element.js [ Failure ]
 crbug.com/714962 inspector-protocol/layout-fonts/languages-emoji-rare-glyphs.js [ Failure ]
@@ -385,9 +392,10 @@
 crbug.com/591099 tables/mozilla/bugs/bug2973.html [ Failure ]
 crbug.com/591099 tables/mozilla/bugs/bug50695-2.html [ Failure ]
 crbug.com/591099 tables/mozilla/bugs/bug55527.html [ Failure ]
+crbug.com/591099 transforms/3d/general/perspective-units.html [ Failure ]
 crbug.com/591099 transforms/svg-vs-css.xhtml [ Failure ]
-crbug.com/870008 virtual/android/rootscroller/position-fixed-in-unscrollable-document-iframe.html [ Failure ]
-crbug.com/870008 virtual/android/rootscroller/position-fixed-in-unscrollable-document.html [ Failure Pass ]
+crbug.com/870008 virtual/android/rootscroller/position-fixed-in-unscrollable-document-iframe.html [ Failure Pass ]
+crbug.com/870008 virtual/android/rootscroller/position-fixed-in-unscrollable-document.html [ Failure ]
 crbug.com/591099 virtual/exotic-color-space/ [ Skip ]
 crbug.com/591099 virtual/feature-policy-vibrate/ [ Skip ]
 crbug.com/591099 virtual/gpu-rasterization/images/color-profile-image-filter-all.html [ Failure ]
@@ -408,7 +416,6 @@
 crbug.com/591099 virtual/outofblink-cors-ns/http/tests/fetch/chromium/release-handle-crash.html [ Crash Pass ]
 crbug.com/591099 virtual/outofblink-cors-ns/http/tests/security/cors-rfc1918/addressspace-document-appcache.https.html [ Crash Failure ]
 crbug.com/591099 virtual/outofblink-cors-ns/http/tests/security/cors-rfc1918/addressspace-document-csp-appcache.https.html [ Crash Failure Pass ]
-crbug.com/591099 virtual/outofblink-cors-ns/http/tests/security/document-domain-canonicalizes.html [ Pass ]
 crbug.com/591099 virtual/outofblink-cors-ns/http/tests/security/setDomainRelaxationForbiddenForURLScheme.html [ Crash ]
 crbug.com/591099 virtual/outofblink-cors-ns/http/tests/security/video-poster-cross-origin-crash2.html [ Pass ]
 crbug.com/591099 virtual/outofblink-cors/ [ Skip ]
diff --git a/third_party/WebKit/LayoutTests/css-shadow-parts/double-forward.html b/third_party/WebKit/LayoutTests/css-shadow-parts/double-forward.html
index 6e2f26f..ad052bd 100644
--- a/third_party/WebKit/LayoutTests/css-shadow-parts/double-forward.html
+++ b/third_party/WebKit/LayoutTests/css-shadow-parts/double-forward.html
@@ -17,9 +17,9 @@
       <span id="green_part" part="partp">This text</span>
     </template>
     <script>installCustomElement("custom-element-middle", "custom-element-middle-template");</script>
-    <template id="custom-element-middle-template"><custom-element-inner id="c-e-inner" partmap="partp part-forwarded1"></custom-element-inner></template>
+    <template id="custom-element-middle-template"><custom-element-inner id="c-e-inner" partmap="partp: part-forwarded1"></custom-element-inner></template>
     <script>installCustomElement("custom-element-outer", "custom-element-outer-template");</script>
-    <template id="custom-element-outer-template"><custom-element-middle id="c-e-middle" partmap="part-forwarded1 part-forwarded2"></custom-element-middle></template>
+    <template id="custom-element-outer-template"><custom-element-middle id="c-e-middle" partmap="part-forwarded1: part-forwarded2"></custom-element-middle></template>
     The following text should be green:
     <custom-element-outer id="c-e-outer"></custom-element-outer>
     <script type="text/javascript">
diff --git a/third_party/WebKit/LayoutTests/css-shadow-parts/invalidation-change-part-name-forward.html b/third_party/WebKit/LayoutTests/css-shadow-parts/invalidation-change-part-name-forward.html
index 02973e29..452567b0 100644
--- a/third_party/WebKit/LayoutTests/css-shadow-parts/invalidation-change-part-name-forward.html
+++ b/third_party/WebKit/LayoutTests/css-shadow-parts/invalidation-change-part-name-forward.html
@@ -17,7 +17,7 @@
       <span id="part" part="partp">This text</span>
     </template>
     <script>installCustomElement("custom-element-outer", "custom-element-outer-template");</script>
-    <template id="custom-element-outer-template"><custom-element-inner id="c-e-inner" partmap="partp part-forwarded"></custom-element-inner></template>
+    <template id="custom-element-outer-template"><custom-element-inner id="c-e-inner" partmap="partp: part-forwarded"></custom-element-inner></template>
     The following text should be green:
     <custom-element-outer id="c-e-outer"></custom-element-outer>
     <script type="text/javascript">
diff --git a/third_party/WebKit/LayoutTests/css-shadow-parts/invalidation-change-partmap-forward.html b/third_party/WebKit/LayoutTests/css-shadow-parts/invalidation-change-partmap-forward.html
index c67f656..dc70430 100644
--- a/third_party/WebKit/LayoutTests/css-shadow-parts/invalidation-change-partmap-forward.html
+++ b/third_party/WebKit/LayoutTests/css-shadow-parts/invalidation-change-partmap-forward.html
@@ -17,7 +17,7 @@
       <span id="part" part="partp">This text</span>
     </template>
     <script>installCustomElement("custom-element-outer", "custom-element-outer-template");</script>
-    <template id="custom-element-outer-template"><custom-element-inner id="c-e-inner" partmap="partp part-forwarded"></custom-element-inner></template>
+    <template id="custom-element-outer-template"><custom-element-inner id="c-e-inner" partmap="partp: part-forwarded"></custom-element-inner></template>
     The following text should be green:
     <custom-element-outer id="c-e-outer"></custom-element-outer>
     <script type="text/javascript">
diff --git a/third_party/WebKit/LayoutTests/css-shadow-parts/invalidation-complex-selector-forward.html b/third_party/WebKit/LayoutTests/css-shadow-parts/invalidation-complex-selector-forward.html
index fbd2a2b..25fc710 100644
--- a/third_party/WebKit/LayoutTests/css-shadow-parts/invalidation-complex-selector-forward.html
+++ b/third_party/WebKit/LayoutTests/css-shadow-parts/invalidation-complex-selector-forward.html
@@ -17,7 +17,7 @@
       <span id="part" part="partp">This text</span>
     </template>
     <script>installCustomElement("custom-element-outer", "custom-element-outer-template");</script>
-    <template id="custom-element-outer-template"><custom-element-inner id="c-e-inner" partmap="partp part-forwarded"></custom-element-inner></template>
+    <template id="custom-element-outer-template"><custom-element-inner id="c-e-inner" partmap="partp: part-forwarded"></custom-element-inner></template>
     The following text should be green:
     <div id="elem"><custom-element-outer id="c-e-outer"></custom-element-outer></div>
     <script type="text/javascript">
diff --git a/third_party/WebKit/LayoutTests/css-shadow-parts/precedence-part-vs-part.html b/third_party/WebKit/LayoutTests/css-shadow-parts/precedence-part-vs-part.html
index e87a1c8..8a46fa3 100644
--- a/third_party/WebKit/LayoutTests/css-shadow-parts/precedence-part-vs-part.html
+++ b/third_party/WebKit/LayoutTests/css-shadow-parts/precedence-part-vs-part.html
@@ -19,7 +19,7 @@
     <script>installCustomElement("custom-element-outer", "custom-element-outer-template");</script>
     <template id="custom-element-outer-template">
       <style>#c-e-inner::part(partp) { color: red; }</style>
-      <custom-element-inner id="c-e-inner" partmap="partp part-forwarded"></custom-element-inner>
+      <custom-element-inner id="c-e-inner" partmap="partp: part-forwarded"></custom-element-inner>
     </template>
     The following text should be green:
     <custom-element-outer id="c-e-outer"></custom-element-outer>
diff --git a/third_party/WebKit/LayoutTests/css-shadow-parts/simple-forward.html b/third_party/WebKit/LayoutTests/css-shadow-parts/simple-forward.html
index 1bf4557..1cc3faa 100644
--- a/third_party/WebKit/LayoutTests/css-shadow-parts/simple-forward.html
+++ b/third_party/WebKit/LayoutTests/css-shadow-parts/simple-forward.html
@@ -17,7 +17,7 @@
       <span id="green_part" part="partp">This text</span>
     </template>
     <script>installCustomElement("custom-element-outer", "custom-element-outer-template");</script>
-    <template id="custom-element-outer-template"><custom-element-inner id="c-e-inner" partmap="partp part-forwarded"></custom-element-inner></template>
+    <template id="custom-element-outer-template"><custom-element-inner id="c-e-inner" partmap="partp: part-forwarded"></custom-element-inner></template>
     The following text should be green:
     <custom-element-outer id="c-e-outer"></custom-element-outer>
     <script type="text/javascript">
diff --git a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST_5.json b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST_5.json
index 1195a15..d5c6dcf 100644
--- a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST_5.json
+++ b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST_5.json
@@ -151483,6 +151483,11 @@
      {}
     ]
    ],
+   "feature-policy/reporting/generic-sensor-reporting.https.html.headers": [
+    [
+     {}
+    ]
+   ],
    "feature-policy/reporting/geolocation-reporting.https.html.headers": [
     [
      {}
@@ -177893,6 +177898,16 @@
      {}
     ]
    ],
+   "wasm/jsapi/idlharness.any-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "wasm/jsapi/idlharness.any.worker-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "wasm/jsapi/instanceTestFactory.js": [
     [
      {}
@@ -221495,6 +221510,12 @@
      {}
     ]
    ],
+   "feature-policy/reporting/generic-sensor-reporting.https.html": [
+    [
+     "/feature-policy/reporting/generic-sensor-reporting.https.html",
+     {}
+    ]
+   ],
    "feature-policy/reporting/geolocation-reporting.https.html": [
     [
      "/feature-policy/reporting/geolocation-reporting.https.html",
@@ -266171,16 +266192,6 @@
      {}
     ]
    ],
-   "wasm/idlharness.any.js": [
-    [
-     "/wasm/idlharness.any.html",
-     {}
-    ],
-    [
-     "/wasm/idlharness.any.worker.html",
-     {}
-    ]
-   ],
    "wasm/jsapi/constructor/compile.any.js": [
     [
      "/wasm/jsapi/constructor/compile.any.html",
@@ -266309,6 +266320,16 @@
      {}
     ]
    ],
+   "wasm/jsapi/idlharness.any.js": [
+    [
+     "/wasm/jsapi/idlharness.any.html",
+     {}
+    ],
+    [
+     "/wasm/jsapi/idlharness.any.worker.html",
+     {}
+    ]
+   ],
    "wasm/jsapi/instance/constructor-bad-imports.any.js": [
     [
      "/wasm/jsapi/instance/constructor-bad-imports.any.html",
@@ -267569,6 +267590,12 @@
      {}
     ]
    ],
+   "webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-reuse.html": [
+    [
+     "/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-reuse.html",
+     {}
+    ]
+   ],
    "webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer.html": [
     [
      "/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer.html",
@@ -335985,7 +336012,7 @@
    "support"
   ],
   "css/css-properties-values-api/typedom.tentative.html": [
-   "6d0623bba420dfe5becafb9eb59cbdf4ad115954",
+   "51e45e887721a3e32325eefe62d984d3c1966922",
    "testharness"
   ],
   "css/css-properties-values-api/unit-cycles.html": [
@@ -362285,7 +362312,7 @@
    "testharness"
   ],
   "css/geometry/interfaces-expected.txt": [
-   "757d30bebc32a7a7be1eff7f501a9006f4bfe8ff",
+   "0e6decf1cc9af129823094dcc5cb444d8b76783e",
    "support"
   ],
   "css/geometry/interfaces.html": [
@@ -370005,7 +370032,7 @@
    "testharness"
   ],
   "dom/interfaces_exclude=Node-expected.txt": [
-   "cc031315525997f8247cbfbfa749f48a7626978f",
+   "33ba6302ac10570d37fa9b0f229142761ab21151",
    "support"
   ],
   "dom/lists/DOMTokenList-Iterable.html": [
@@ -375212,6 +375239,14 @@
    "d35e48ba40dc65a3b043a3e41a11332c42bfdba9",
    "support"
   ],
+  "feature-policy/reporting/generic-sensor-reporting.https.html": [
+   "c60e3e81a1dddbdc000d799c19719c48ad55376c",
+   "testharness"
+  ],
+  "feature-policy/reporting/generic-sensor-reporting.https.html.headers": [
+   "80cc02753044a3730695bce65bc2b4c22d7a8a6b",
+   "support"
+  ],
   "feature-policy/reporting/geolocation-reporting.https.html": [
    "22e258563b799c7d48db2452f15eb6124d1f2d0e",
    "testharness"
@@ -398241,7 +398276,7 @@
    "support"
   ],
   "interfaces/webrtc.idl": [
-   "cdeb5204e6a0b5735948b94d5e30931bc2fd062e",
+   "7496fff41dcbbeda6759977d56e12c21568b49f5",
    "support"
   ],
   "interfaces/webusb.idl": [
@@ -420097,7 +420132,7 @@
    "support"
   ],
   "resources/idlharness.js": [
-   "950df6fa34b82a3f4afe2ab925d90a72ddcf3872",
+   "0f49a725c33736d6ff4cb395234d2bf0c78970f9",
    "support"
   ],
   "resources/idlharness.js.headers": [
@@ -423477,7 +423512,7 @@
    "support"
   ],
   "service-workers/service-worker/resources/service-worker-header.py": [
-   "2e82e78107ad4f70b3e88d700139195f8a5b029e",
+   "74f57a72a9041762f9f50732d2ce890d889810c5",
    "support"
   ],
   "service-workers/service-worker/resources/service-worker-interception-dynamic-import-worker.js": [
@@ -423689,7 +423724,7 @@
    "testharness"
   ],
   "service-workers/service-worker/service-worker-header.https.html": [
-   "2584485c65ab60e8b429c3db66fc43a8157a8ed5",
+   "fb902cd1b455d130c899cf06bf05c32184d2b543",
    "testharness"
   ],
   "service-workers/service-worker/serviceworker-message-event-historical.https.html": [
@@ -424541,7 +424576,7 @@
    "support"
   ],
   "shape-detection/idlharness.any-expected.txt": [
-   "24c619327dbc1917215950282a7a727ba63ecea3",
+   "d6bc9aa971cd4103ee67a99ca4f8854ae2d3a325",
    "support"
   ],
   "shape-detection/idlharness.any.js": [
@@ -428468,10 +428503,6 @@
    "b55d60b65e2754e19f3464ca34ba67125f5ac2b1",
    "support"
   ],
-  "wasm/idlharness.any.js": [
-   "9c29ad14559382eba1d4c10cf5782e3e04682f2c",
-   "testharness"
-  ],
   "wasm/idlharness.any.worker-expected.txt": [
    "4aabd115ec68f2dbe9427f0c1569114ad3f33ca0",
    "support"
@@ -428544,6 +428575,18 @@
    "d4a84b254f76ea50284619967ab6dc98c99bfea2",
    "testharness"
   ],
+  "wasm/jsapi/idlharness.any-expected.txt": [
+   "d120c0a8f6dac128cc75921b4f4220210b4ccf74",
+   "support"
+  ],
+  "wasm/jsapi/idlharness.any.js": [
+   "25298d3dabfbbce7490fa040258f3e06cd94b582",
+   "testharness"
+  ],
+  "wasm/jsapi/idlharness.any.worker-expected.txt": [
+   "d120c0a8f6dac128cc75921b4f4220210b4ccf74",
+   "support"
+  ],
   "wasm/jsapi/instance/constructor-bad-imports.any.js": [
    "24c51c10dc5df9d52c06bfb0715e435b17f24f7a",
    "testharness"
@@ -428693,7 +428736,7 @@
    "support"
   ],
   "wasm/resources/load_wasm.js": [
-   "512324639059da8a9d76e9d740d97fc56ebdebc4",
+   "8316dcfbc89bd02073e4e08db1bee7f65d37e86c",
    "support"
   ],
   "wasm/serialization/broadcastchannel-success-and-failure.html": [
@@ -429816,6 +429859,10 @@
    "612a91cf4ef60f6f1d441d27e2ab86f32b94f0fd",
    "testharness"
   ],
+  "webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-reuse.html": [
+   "dabe323cbe2226d32c63576199eda61c1aecb168",
+   "testharness"
+  ],
   "webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer.html": [
    "a2c4581c4e80069f227fe29078bc3eb6409c8b4e",
    "testharness"
@@ -431945,7 +431992,7 @@
    "support"
   ],
   "webrtc/idlharness.https.window-expected.txt": [
-   "2730fbb3b2606b4599af0b6c317ca87948a71e90",
+   "794763f85fa85e75d061590307bdae997e742071",
    "support"
   ],
   "webrtc/idlharness.https.window.js": [
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-properties-values-api/typedom.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-properties-values-api/typedom.tentative.html
index 6d0623b..0255b8f 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/css/css-properties-values-api/typedom.tentative.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-properties-values-api/typedom.tentative.html
@@ -221,86 +221,86 @@
     let name2 = gen_prop('<length>', '0px');
     styleDecl.setProperty(name2, `var(${name1})`);
     assert_true(propertyMap.get(name2) instanceof CSSUnparsedValue);
-}, name => `${name}.get returns CSSUnparsedValue for value with var references`);
+}, name => `StylePropertyMap.get returns CSSUnparsedValue for value with var references (${name})`);
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     let name1 = gen_prop('<length>', '100px');
     let name2 = gen_prop('<length>#', '0px');
     styleDecl.setProperty(name2, `1px, var(${name1}), 3px`);
     assert_true(propertyMap.get(name2) instanceof CSSUnparsedValue);
-}, name => `${name}.get returns CSSUnparsedValue for value with var references in list`);
+}, name => `StylePropertyMap.get returns CSSUnparsedValue for value with var references in list (${name})`);
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     assert_attribute_get_type(styleDecl, propertyMap, '*', 'if(){}', CSSUnparsedValue);
-}, name => `${name}.get returns CSSUnparsedValue for *`);
+}, name => `StylePropertyMap.get returns CSSUnparsedValue for * (${name})`);
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     assert_attribute_get_type(styleDecl, propertyMap, '<angle>', '42deg', CSSUnitValue);
-}, name => `${name}.get returns CSSUnitValue for <angle>`);
+}, name => `StylePropertyMap.get returns CSSUnitValue for <angle> (${name})`);
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     assert_attribute_get_type(styleDecl, propertyMap, '<color>', '#fefefe', CSSStyleValue);
-}, name => `${name}.get returns CSSStyleValue for <color>`);
+}, name => `StylePropertyMap.get returns CSSStyleValue for <color> (${name})`);
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     assert_attribute_get_type(styleDecl, propertyMap, '<custom-ident>', 'none', CSSKeywordValue);
-}, name => `${name}.get returns CSSKeywordValue for <custom-ident>`);
+}, name => `StylePropertyMap.get returns CSSKeywordValue for <custom-ident> (${name})`);
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     assert_attribute_get_type(styleDecl, propertyMap, '<image>', 'url(thing.png)', CSSImageValue);
-}, name => `${name}.get returns CSSImageValue for <image>`);
+}, name => `StylePropertyMap.get returns CSSImageValue for <image> (${name})`);
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     assert_attribute_get_type(styleDecl, propertyMap, '<integer>', '100', CSSUnitValue);
-}, name => `${name}.get returns CSSUnitValue for <integer>`);
+}, name => `StylePropertyMap.get returns CSSUnitValue for <integer> (${name})`);
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     assert_attribute_get_type(styleDecl, propertyMap, '<length-percentage>', '10%', CSSUnitValue);
-}, name => `${name}.get returns CSSUnitValue for <length-percentage> [10%]`);
+}, name => `StylePropertyMap.get returns CSSUnitValue for <length-percentage> [10%] (${name})`);
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     assert_attribute_get_type(styleDecl, propertyMap, '<length-percentage>', '10px', CSSUnitValue);
-}, name => `${name}.get returns CSSUnitValue for <length-percentage> [10px]`);
+}, name => `StylePropertyMap.get returns CSSUnitValue for <length-percentage> [10px] (${name})`);
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     assert_attribute_get_type(styleDecl, propertyMap, '<length-percentage>', 'calc(10px + 10%)', CSSMathSum);
-}, name => `${name}.get returns CSSMathSum for <length-percentage> [calc(10px + 10%)]`);
+}, name => `StylePropertyMap.get returns CSSMathSum for <length-percentage> [calc(10px + 10%)] (${name})`);
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     assert_attribute_get_type(styleDecl, propertyMap, '<length>', '10px', CSSUnitValue);
-}, name => `${name}.get returns CSSUnitValue for <length>`);
+}, name => `StylePropertyMap.get returns CSSUnitValue for <length> (${name})`);
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     assert_attribute_get_type(styleDecl, propertyMap, '<number>', '42', CSSUnitValue);
-}, name => `${name}.get returns CSSUnitValue for <number>`);
+}, name => `StylePropertyMap.get returns CSSUnitValue for <number> (${name})`);
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     assert_attribute_get_type(styleDecl, propertyMap, '<percentage>', '10%', CSSUnitValue);
-}, name => `${name}.get returns CSSUnitValue for <percentage>`);
+}, name => `StylePropertyMap.get returns CSSUnitValue for <percentage> (${name})`);
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     assert_attribute_get_type(styleDecl, propertyMap, '<resolution>', '300dpi', CSSUnitValue);
-}, name => `${name}.get returns CSSUnitValue for <resolution>`);
+}, name => `StylePropertyMap.get returns CSSUnitValue for <resolution> (${name})`);
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     assert_attribute_get_type(styleDecl, propertyMap, '<time>', '42s', CSSUnitValue);
-}, name => `${name}.get returns CSSUnitValue for <time>`);
+}, name => `StylePropertyMap.get returns CSSUnitValue for <time> (${name})`);
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     assert_attribute_get_type(styleDecl, propertyMap, '<url>', 'url(a)', CSSStyleValue);
-}, name => `${name}.get returns CSSStyleValue for <url>`);
+}, name => `StylePropertyMap.get returns CSSStyleValue for <url> (${name})`);
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     assert_attribute_get_type(styleDecl, propertyMap, 'thing1 | THING2', 'thing1', CSSKeywordValue);
-}, name => `${name}.get returns CSSKeywordValue for thing1 | THING2`);
+}, name => `StylePropertyMap.get returns CSSKeywordValue for thing1 | THING2 (${name})`);
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     assert_attribute_get_type(styleDecl, propertyMap, '<length>+', '10px 20px', CSSUnitValue);
-}, name => `${name}.get returns CSSUnitValue for <length>+`);
+}, name => `StylePropertyMap.get returns CSSUnitValue for <length>+ (${name})`);
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     assert_attribute_get_type(styleDecl, propertyMap, '<length>#', '10px 20px', CSSUnitValue);
-}, name => `${name}.get returns CSSUnitValue for <length>#`);
+}, name => `StylePropertyMap.get returns CSSUnitValue for <length># (${name})`);
 
 // attributeStyleMap.getAll
 
@@ -309,14 +309,14 @@
     styleDecl.setProperty(name, '10px 20px 30px');
     assert_equals(propertyMap.getAll(name).length, 3);
     assert_true(propertyMap.getAll(name).every(x => x instanceof CSSUnitValue));
-}, name => `${name}.getAll returns a list of CSSUnitValues for <length>+`);
+}, name => `StylePropertyMap.getAll returns a list of CSSUnitValues for <length>+ (${name})`);
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     let name = gen_prop('<length>#', '0px');
     styleDecl.setProperty(name, '10px, 20px, 30px');
     assert_equals(propertyMap.getAll(name).length, 3);
     assert_true(propertyMap.getAll(name).every(x => x instanceof CSSUnitValue));
-}, name => `${name}.getAll returns a list of CSSUnitValues for <length>#`);
+}, name => `StylePropertyMap.getAll returns a list of CSSUnitValues for <length># (${name})`);
 
 // StylePropertyMap.set
 
@@ -325,13 +325,15 @@
         let name = gen_prop(options.syntax, options.initialValue);
         propertyMap.clear();
 
+        let ensureArray = v => v.constructor === Array ? v : [v];
+
         for (let value of options.shouldAccept)
-            propertyMap.set(name, value);
+            propertyMap.set(name, ...ensureArray(value));
 
         for (let value of options.shouldReject) {
-            assert_throws(new TypeError(), () => propertyMap.set(name, value));
+            assert_throws(new TypeError(), () => propertyMap.set(name, ...ensureArray(value)));
         }
-    }, `${propertyMapName}.set accepts correct CSSUnitValues for ${options.syntax}`);
+    }, `StylePropertyMap.set accepts correct CSSStyleValues for ${options.syntax} (${propertyMapName})`);
 }
 
 // Verify that the correct CSSStyleValues are accepted/rejected for a registered
@@ -360,77 +362,77 @@
     syntax: '<angle>',
     initialValue: '0deg',
     shouldAccept: [CSS.deg(42), CSS.turn(2), '42deg'],
-    shouldReject: [unparsed('42deg'), CSS.px(15), '50px'],
+    shouldReject: [unparsed('42deg'), CSS.px(15), '50px', [CSS.deg(15), '10deg']],
 });
 
 test_style_property_map_set({
     syntax: '<custom-ident>',
     initialValue: 'none',
     shouldAccept: [keyword('foo'), 'foo'],
-    shouldReject: [unparsed('foo'), CSS.px(15), '15px'],
+    shouldReject: [unparsed('foo'), CSS.px(15), '15px', [keyword('foo'), 'foo']],
 });
 
 test_style_property_map_set({
     syntax: '<image>',
     initialValue: 'url(a)',
     shouldAccept: [url_image('url(b)'), 'url(b)'],
-    shouldReject: [unparsed('url(b)'), CSS.px(100), '50px'],
+    shouldReject: [unparsed('url(b)'), CSS.px(100), '50px', [url_image('url(1)'), 'url(2)']],
 });
 
 test_style_property_map_set({
     syntax: '<integer>',
     initialValue: '0',
     shouldAccept: [CSS.number(1), CSS.number(-42), '1', '-42'],
-    shouldReject: [unparsed('42'), CSS.px(100), '50px'],
+    shouldReject: [unparsed('42'), CSS.px(100), '50px', [CSS.number(42), '42']],
 });
 
 test_style_property_map_set({
     syntax: '<length-percentage>',
     initialValue: '0px',
     shouldAccept: [CSS.percent(10), CSS.px(1), CSS.em(1), '10px', '10%'],
-    shouldReject: [unparsed('10%'), unparsed('10px'), CSS.dpi(1), 'url(b)'],
+    shouldReject: [unparsed('10%'), unparsed('10px'), CSS.dpi(1), 'url(b)', [CSS.percent(10), '10%']],
 });
 
 test_style_property_map_set({
     syntax: '<length>',
     initialValue: '0px',
     shouldAccept: [CSS.px(10), CSS.em(10), CSS.vh(200), sum(CSS.px(10), CSS.em(20)), '10em', 'calc(10px + 10em)'],
-    shouldReject: [unparsed('10px'), CSS.percent(1), 'url(b)'],
+    shouldReject: [unparsed('10px'), CSS.percent(1), 'url(b)', [CSS.em(10), '10px']],
 });
 
 test_style_property_map_set({
     syntax: '<number>',
     initialValue: '0',
     shouldAccept: [CSS.number(1337), CSS.number(-42.5), '1337', '-42.5'],
-    shouldReject: [unparsed('42'), CSS.px(15), '#fef'],
+    shouldReject: [unparsed('42'), CSS.px(15), '#fef', [CSS.number(-42.5), '42.5']],
 });
 
 test_style_property_map_set({
     syntax: '<percentage>',
     initialValue: '0%',
     shouldAccept: [CSS.percent(10), '10%'],
-    shouldReject: [unparsed('10%'), CSS.px(1), '#fef'],
+    shouldReject: [unparsed('10%'), CSS.px(1), '#fef', [CSS.percent(10), '1%']],
 });
 
 test_style_property_map_set({
     syntax: '<resolution>',
     initialValue: '0dpi',
     shouldAccept: [CSS.dpi(100), CSS.dpcm(10), CSS.dppx(50), '100dpi'],
-    shouldReject: [unparsed('42'), CSS.px(15), '#fef'],
+    shouldReject: [unparsed('42'), CSS.px(15), '#fef', [CSS.dpi(1), '2dpi']],
 });
 
 test_style_property_map_set({
     syntax: '<time>',
     initialValue: '0s',
     shouldAccept: [CSS.s(42), CSS.ms(16), '16ms'],
-    shouldReject: [unparsed('42s'), CSS.px(15), '#fef'],
+    shouldReject: [unparsed('42s'), CSS.px(15), '#fef', [CSS.s(5), '6s']],
 });
 
 test_style_property_map_set({
     syntax: '<url>',
     initialValue: 'url(a)',
     shouldAccept: [url_image('url(b)')],
-    shouldReject: [unparsed('url(b)'), CSS.px(100), '#fef'],
+    shouldReject: [unparsed('url(b)'), CSS.px(100), '#fef', [url_image('url(1)'), 'url(2)']],
 });
 
 test_style_property_map_set({
@@ -444,14 +446,308 @@
     syntax: 'none | thing | THING',
     initialValue: 'none',
     shouldAccept: [keyword('thing'), keyword('THING'), 'thing'],
-    shouldReject: [unparsed('thing'), CSS.px(15), keyword('notathing'), 'notathing'],
+    shouldReject: [unparsed('thing'), CSS.px(15), keyword('notathing'), 'notathing', [keyword('thing'), keyword('thing')]],
 });
 
 test_style_property_map_set({
     syntax: '<angle> | <length>',
     initialValue: '0deg',
     shouldAccept: [CSS.deg(42), CSS.turn(2), CSS.px(10), CSS.em(10), '10deg', '10px'],
-    shouldReject: [unparsed('42deg'), unparsed('20px'), CSS.s(1), '#fef'],
+    shouldReject: [unparsed('42deg'), unparsed('20px'), CSS.s(1), '#fef', [CSS.deg(42), '21deg']],
+});
+
+// StylePropertyMap.set for list-valued properties:
+
+test_style_property_map_set({
+    syntax: '<angle>+',
+    initialValue: '0deg',
+    shouldAccept: [CSS.deg(15), [CSS.deg(15), '10deg'], '15deg 10deg'],
+    shouldReject: [[CSS.deg(15), CSS.px(10)], '15deg 10px'],
+});
+
+test_style_property_map_set({
+    syntax: '<custom-ident>+',
+    initialValue: 'none',
+    shouldAccept: [keyword('foo'), [keyword('foo'), 'bar'], 'foo bar'],
+    shouldReject: [[keyword('foo'), CSS.px(10)], 'foo 10px'],
+});
+
+test_style_property_map_set({
+    syntax: '<image>+',
+    initialValue: 'url(a)',
+    shouldAccept: [url_image('url(1)'), [url_image('url(1)'), 'url(2)'], 'url(1) url(2)'],
+    shouldReject: [[url_image('url(1)'), CSS.px(10)], 'url(1) 10px'],
+});
+
+test_style_property_map_set({
+    syntax: '<integer>+',
+    initialValue: '0',
+    shouldAccept: [CSS.number(42), [CSS.number(42), '42'], '42 42'],
+    shouldReject: [[CSS.number(42), keyword('noint')], '42 noint'],
+});
+
+test_style_property_map_set({
+    syntax: '<length-percentage>+',
+    initialValue: '0px',
+    shouldAccept: [CSS.percent(10), [CSS.percent(10), '10%']],
+    shouldReject: [[CSS.percent(10), keyword('nolength')], '10% nolength'],
+});
+
+test_style_property_map_set({
+    syntax: '<length>+',
+    initialValue: '0px',
+    shouldAccept: [CSS.em(10), [CSS.em(10), '10px']],
+    shouldReject: [[CSS.em(10), keyword('nolength'), '10em nolength']],
+});
+
+test_style_property_map_set({
+    syntax: '<number>+',
+    initialValue: '0',
+    shouldAccept: [CSS.number(-42.5), [CSS.number(-42.5), '42.5'], '-42.5 42.5'],
+    shouldReject: [[CSS.number(-42.5), CSS.px(10)], '-42.5 10px'],
+});
+
+test_style_property_map_set({
+    syntax: '<percentage>+',
+    initialValue: '0%',
+    shouldAccept: [CSS.percent(10), [CSS.percent(10), '1%'], '10% 1%'],
+    shouldReject: [[CSS.percent(10), keyword('foo')], '10% foo'],
+});
+
+test_style_property_map_set({
+    syntax: '<resolution>+',
+    initialValue: '0dpi',
+    shouldAccept: [CSS.dpi(1), [CSS.dpi(1), '2dpi'], '1dpi 2dpi'],
+    shouldReject: [[CSS.dpi(1), keyword('foo')], '1dpi foo'],
+});
+
+test_style_property_map_set({
+    syntax: '<time>+',
+    initialValue: '0s',
+    shouldAccept: [CSS.s(5), [CSS.s(5), '6s'], '5s 6s'],
+    shouldReject: [[CSS.s(5), keyword('foo')], '5s foo'],
+});
+
+test_style_property_map_set({
+    syntax: '<url>+',
+    initialValue: 'url(a)',
+    shouldAccept: [url_image('url(1)'), [url_image('url(1)'), 'url(2)'], 'url(1) url(2)'],
+    shouldReject: [[url_image('url(1)'), CSS.px(10)], 'url(1) 10px'],
+});
+
+test_style_property_map_set({
+    syntax: 'thing+',
+    initialValue: 'thing',
+    shouldAccept: [keyword('thing'), [keyword('thing'), 'thing'], 'thing thing'],
+    shouldReject: [[keyword('thing'), CSS.px(10)], 'thing 10px'],
+});
+
+test_style_property_map_set({
+    syntax: '<length>#',
+    initialValue: '0px',
+    shouldAccept: [CSS.em(10), [CSS.em(10), '10px']],
+    shouldReject: [[CSS.em(10), keyword('nolength'), '10em nolength']],
+});
+
+function test_append_for_property_map(propertyMapName, propertyMap, options) {
+    test(function(){
+        let name = gen_prop(options.syntax, options.initialValue);
+
+        let ensureArray = v => v.constructor === Array ? v : [v];
+
+        for (let value of options.values) {
+            propertyMap.clear();
+
+            if (value.base !== null)
+                propertyMap.set(name, ...ensureArray(value.base));
+
+            // If 'null' is expected, it means we expect the append to fail.
+            if (value.expect !== null) {
+                propertyMap.append(name, ...ensureArray(value.append));
+                let actual = Array.from(propertyMap.getAll(name)).join(' ');
+                assert_equals(actual, value.expect);
+            } else {
+                assert_throws(new TypeError(), () => propertyMap.append(name, ...ensureArray(value.append)));
+            }
+        }
+    }, `StylePropertyMap.append accepts correct CSSStyleValues for ${options.syntax} (${propertyMapName})`);
+}
+
+// Verify that the correct CSSStyleValues are accepted/rejected when
+// appending values to list-valued properties.
+//
+// The same test is performed twice: once for attributeStyleMap, and once
+// for styleMap.
+function test_append(options) {
+    test_append_for_property_map('attributeStyleMap', target.attributeStyleMap, options);
+    test_append_for_property_map('styleMap', style.sheet.rules[0].styleMap, options);
+}
+
+test_append({
+    syntax: '<angle>+',
+    initialValue: '0deg',
+    values: [
+        { base: [CSS.deg(1)], append: [CSS.px(1)], expect: null },
+        { base: [CSS.deg(1)], append: [CSS.deg(2), CSS.px(1)], expect: null },
+        { base: [CSS.deg(1)], append: [CSS.deg(2), '1px'], expect: null },
+        { base: [CSS.deg(1)], append: [CSS.turn(2), CSS.deg(3)], expect: '1deg 2turn 3deg' },
+        { base: [CSS.deg(1), CSS.deg(2)], append: [CSS.deg(3)], expect: '1deg 2deg 3deg' },
+        { base: [CSS.deg(1)], append: [CSS.deg(2), '3deg'], expect: '1deg 2deg 3deg' },
+        { base: [CSS.deg(1)], append: [CSS.deg(2), '3turn 4deg'], expect: '1deg 2deg 3turn 4deg' },
+        { base: null, append: [CSS.deg(1), '2deg'], expect: '1deg 2deg' },
+    ],
+});
+
+test_append({
+    syntax: '<custom-ident>+',
+    initialValue: 'none',
+    values: [
+        { base: [keyword('foo')], append: [CSS.px(1)], expect: null },
+        { base: [keyword('foo')], append: [keyword('bar'), CSS.px(1)], expect: null },
+        { base: [keyword('foo')], append: [keyword('bar'), '1px'], expect: null },
+        { base: [keyword('foo')], append: [keyword('bar'), keyword('baz')], expect: 'foo bar baz' },
+        { base: [keyword('foo'), keyword('bar')], append: [keyword('baz')], expect: 'foo bar baz' },
+        { base: [keyword('foo')], append: [keyword('bar'), 'baz'], expect: 'foo bar baz' },
+        { base: [keyword('foo')], append: [keyword('bar'), 'baz zim'], expect: 'foo bar baz zim' },
+        { base: null, append: [keyword('foo'), 'bar'], expect: 'foo bar' },
+    ],
+});
+
+['<image>+', '<url>+'].forEach((syntax) => {
+    test_append({
+        syntax: syntax,
+        initialValue: 'url(0)',
+        values: [
+            { base: [url_image('url("1")')], append: [CSS.px(1)], expect: null },
+            { base: [url_image('url("1")')], append: [url_image('url("2")'), CSS.px(1)], expect: null },
+            { base: [url_image('url("1")')], append: [url_image('url("2")'), '1px'], expect: null },
+            { base: [url_image('url("1")')], append: [url_image('url("2")'), url_image('url("3")')], expect: 'url("1") url("2") url("3")' },
+            { base: [url_image('url("1")'), url_image('url("2")')], append: [url_image('url("3")')], expect: 'url("1") url("2") url("3")' },
+            { base: [url_image('url("1")')], append: [url_image('url("2")'), 'url("3")'], expect: 'url("1") url("2") url("3")' },
+            { base: [url_image('url("1")')], append: [url_image('url("2")'), 'url("3") url("4")'], expect: 'url("1") url("2") url("3") url("4")' },
+            { base: null, append: [url_image('url("1")'), 'url("2")'], expect: 'url("1") url("2")' },
+        ],
+    });
+});
+
+test_append({
+    syntax: '<integer>+',
+    initialValue: '0',
+    values: [
+        { base: [CSS.number(1)], append: [CSS.px(1)], expect: null },
+        { base: [CSS.number(1)], append: [CSS.number(2), CSS.px(1)], expect: null },
+        { base: [CSS.number(1)], append: [CSS.number(2), 'noint'], expect: null },
+        { base: [CSS.number(1)], append: [CSS.number(2), CSS.number(3)], expect: '1 2 3' },
+        { base: [CSS.number(1), CSS.number(2)], append: [CSS.number(3)], expect: '1 2 3' },
+        { base: [CSS.number(1)], append: [CSS.number(2), '3'], expect: '1 2 3' },
+        { base: [CSS.number(1)], append: [CSS.number(2), '3 4'], expect: '1 2 3 4' },
+        { base: null, append: [CSS.number(1), '2'], expect: '1 2' },
+    ],
+});
+
+test_append({
+    syntax: '<length-percentage>+',
+    initialValue: '0px',
+    values: [
+        { base: [CSS.px(1)], append: [keyword('nolength')], expect: null },
+        { base: [CSS.px(1)], append: [CSS.px(2), keyword('nolength')], expect: null },
+        { base: [CSS.px(1)], append: [CSS.px(2), 'nolength'], expect: null },
+        { base: [CSS.px(1)], append: [CSS.px(2), CSS.percent(3)], expect: '1px 2px 3%' },
+        { base: [CSS.px(1), CSS.px(2)], append: [CSS.percent(3)], expect: '1px 2px 3%' },
+        { base: [CSS.px(1)], append: [CSS.percent(2), '3px'], expect: '1px 2% 3px' },
+        { base: [CSS.px(1)], append: [CSS.px(2), '3% 4px'], expect: '1px 2px 3% 4px' },
+        { base: null, append: [CSS.px(1), '2%'], expect: '1px 2%' },
+    ],
+});
+
+test_append({
+    syntax: '<length>+',
+    initialValue: '0',
+    values: [
+        { base: [CSS.px(1)], append: [keyword('nolength')], expect: null },
+        { base: [CSS.px(1)], append: [CSS.px(2), keyword('nolength')], expect: null },
+        { base: [CSS.px(1)], append: [CSS.px(2), 'nolength'], expect: null },
+        { base: [CSS.px(1)], append: [CSS.em(2), CSS.px(3)], expect: '1px 2em 3px' },
+        { base: [CSS.px(1), CSS.em(2)], append: [CSS.vh(3)], expect: '1px 2em 3vh' },
+        { base: [CSS.px(1)], append: [CSS.em(2), '3px'], expect: '1px 2em 3px' },
+        { base: [CSS.px(1)], append: [CSS.px(2), '3em 4cm'], expect: '1px 2px 3em 4cm' },
+        { base: null, append: [CSS.vh(1), '2px'], expect: '1vh 2px' },
+    ],
+});
+
+test_append({
+    syntax: '<number>+',
+    initialValue: '0',
+    values: [
+        { base: [CSS.number(-1)], append: [keyword('NaN')], expect: null },
+        { base: [CSS.number(-1)], append: [CSS.number(2.5), keyword('NaN')], expect: null },
+        { base: [CSS.number(-1)], append: [CSS.number(2.5), '1px'], expect: null },
+        { base: [CSS.number(-1)], append: [CSS.number(2.5), CSS.number(3.2)], expect: '-1 2.5 3.2' },
+        { base: [CSS.number(-1), CSS.number(2.5)], append: [CSS.number(3.2)], expect: '-1 2.5 3.2' },
+        { base: [CSS.number(-1)], append: [CSS.number(2.5), '3.2'], expect: '-1 2.5 3.2' },
+        { base: [CSS.number(-1)], append: [CSS.number(2.5), '3.2 4'], expect: '-1 2.5 3.2 4' },
+        { base: null, append: [CSS.number(-1), '2.5'], expect: '-1 2.5' },
+    ],
+});
+
+test_append({
+    syntax: '<percentage>+',
+    initialValue: '0%',
+    values: [
+        { base: [CSS.percent(1)], append: [CSS.px(1)], expect: null },
+        { base: [CSS.percent(1)], append: [CSS.percent(2), CSS.px(1)], expect: null },
+        { base: [CSS.percent(1)], append: [CSS.percent(2), '1px'], expect: null },
+        { base: [CSS.percent(1)], append: [CSS.percent(2), CSS.percent(3)], expect: '1% 2% 3%' },
+        { base: [CSS.percent(1), CSS.percent(2)], append: [CSS.percent(3)], expect: '1% 2% 3%' },
+        { base: [CSS.percent(1)], append: [CSS.percent(2), '3%'], expect: '1% 2% 3%' },
+        { base: [CSS.percent(1)], append: [CSS.percent(2), '3% 4%'], expect: '1% 2% 3% 4%' },
+        { base: null, append: [CSS.percent(1), '2%'], expect: '1% 2%' },
+    ],
+});
+
+test_append({
+    syntax: '<resolution>+',
+    initialValue: '0dpi',
+    values: [
+        { base: [CSS.dpi(1)], append: [CSS.px(1)], expect: null },
+        { base: [CSS.dpi(1)], append: [CSS.dpi(2), CSS.px(1)], expect: null },
+        { base: [CSS.dpi(1)], append: [CSS.dpi(2), '1px'], expect: null },
+        { base: [CSS.dpi(1)], append: [CSS.dpi(2), CSS.dpi(3)], expect: '1dpi 2dpi 3dpi' },
+        { base: [CSS.dpi(1), CSS.dpi(2)], append: [CSS.dpi(3)], expect: '1dpi 2dpi 3dpi' },
+        { base: [CSS.dpi(1)], append: [CSS.dpi(2), '3dpi'], expect: '1dpi 2dpi 3dpi' },
+        { base: [CSS.dpi(1)], append: [CSS.dpi(2), '3dpi 4dpi'], expect: '1dpi 2dpi 3dpi 4dpi' },
+        { base: null, append: [CSS.dpi(1), '2dpi'], expect: '1dpi 2dpi' },
+    ],
+});
+
+test_append({
+    syntax: '<time>+',
+    initialValue: '0s',
+    values: [
+        { base: [CSS.s(1)], append: [CSS.px(1)], expect: null },
+        { base: [CSS.s(1)], append: [CSS.s(2), CSS.px(1)], expect: null },
+        { base: [CSS.s(1)], append: [CSS.ms(2), '1px'], expect: null },
+        { base: [CSS.s(1)], append: [CSS.ms(2), CSS.s(3)], expect: '1s 2ms 3s' },
+        { base: [CSS.s(1), CSS.s(2)], append: [CSS.s(3)], expect: '1s 2s 3s' },
+        { base: [CSS.s(1)], append: [CSS.s(2), '3s'], expect: '1s 2s 3s' },
+        { base: [CSS.s(1)], append: [CSS.s(2), '3ms 4s'], expect: '1s 2s 3ms 4s' },
+        { base: null, append: [CSS.s(1), '2s'], expect: '1s 2s' },
+    ],
+});
+
+test_append({
+    syntax: 'foo+',
+    initialValue: 'foo',
+    values: [
+        { base: [keyword('foo')], append: [CSS.px(1)], expect: null },
+        { base: [keyword('foo')], append: [keyword('foo'), CSS.px(1)], expect: null },
+        { base: [keyword('foo')], append: [keyword('foo'), '1px'], expect: null },
+        { base: [keyword('foo')], append: [keyword('foo'), keyword('foo')], expect: 'foo foo foo' },
+        { base: [keyword('foo'), keyword('foo')], append: [keyword('foo')], expect: 'foo foo foo' },
+        { base: [keyword('foo')], append: [keyword('foo'), 'foo'], expect: 'foo foo foo' },
+        { base: [keyword('foo')], append: [keyword('foo'), 'foo foo'], expect: 'foo foo foo foo' },
+        { base: null, append: [keyword('foo'), keyword('foo')], expect: 'foo foo' },
+    ],
 });
 
 // CSSStyleValue.parse/parseAll
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/geometry/interfaces-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/css/geometry/interfaces-expected.txt
index 757d30b..0e6decf 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/css/geometry/interfaces-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/geometry/interfaces-expected.txt
@@ -29,7 +29,7 @@
 PASS DOMPoint interface: existence and properties of interface object
 PASS DOMPoint interface object length
 PASS DOMPoint interface object name
-FAIL DOMPoint interface: legacy window alias assert_equals: self.SVGPoint should be the same value as self.DOMPoint expected function "function DOMPoint() { [native code] }" but got function "function SVGPoint() { [native code] }"
+FAIL DOMPoint interface: legacy window alias assert_equals: self.SVGPoint should be the same value as the interface object expected function "function DOMPoint() { [native code] }" but got function "function SVGPoint() { [native code] }"
 PASS DOMPoint interface: existence and properties of interface prototype object
 PASS DOMPoint interface: existence and properties of interface prototype object's "constructor" property
 PASS DOMPoint interface: existence and properties of interface prototype object's @@unscopables property
@@ -89,7 +89,7 @@
 PASS DOMRect interface: existence and properties of interface object
 PASS DOMRect interface object length
 PASS DOMRect interface object name
-FAIL DOMRect interface: legacy window alias assert_equals: self.SVGRect should be the same value as self.DOMRect expected function "function DOMRect() { [native code] }" but got function "function SVGRect() { [native code] }"
+FAIL DOMRect interface: legacy window alias assert_equals: self.SVGRect should be the same value as the interface object expected function "function DOMRect() { [native code] }" but got function "function SVGRect() { [native code] }"
 PASS DOMRect interface: existence and properties of interface prototype object
 PASS DOMRect interface: existence and properties of interface prototype object's "constructor" property
 PASS DOMRect interface: existence and properties of interface prototype object's @@unscopables property
@@ -272,7 +272,7 @@
 PASS DOMMatrix interface: existence and properties of interface object
 PASS DOMMatrix interface object length
 PASS DOMMatrix interface object name
-FAIL DOMMatrix interface: legacy window alias assert_equals: self.SVGMatrix should be the same value as self.DOMMatrix expected function "function DOMMatrix() { [native code] }" but got function "function SVGMatrix() { [native code] }"
+FAIL DOMMatrix interface: legacy window alias assert_equals: self.SVGMatrix should be the same value as the interface object expected function "function DOMMatrix() { [native code] }" but got function "function SVGMatrix() { [native code] }"
 PASS DOMMatrix interface: existence and properties of interface prototype object
 PASS DOMMatrix interface: existence and properties of interface prototype object's "constructor" property
 PASS DOMMatrix interface: existence and properties of interface prototype object's @@unscopables property
diff --git a/third_party/WebKit/LayoutTests/external/wpt/dom/interfaces_exclude=Node-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/dom/interfaces_exclude=Node-expected.txt
index cc031315..33ba630 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/dom/interfaces_exclude=Node-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/dom/interfaces_exclude=Node-expected.txt
@@ -839,16 +839,16 @@
 FAIL AbstractRange interface: attribute endContainer assert_own_property: self does not have own property "AbstractRange" expected property "AbstractRange" missing
 FAIL AbstractRange interface: attribute endOffset assert_own_property: self does not have own property "AbstractRange" expected property "AbstractRange" missing
 FAIL AbstractRange interface: attribute collapsed assert_own_property: self does not have own property "AbstractRange" expected property "AbstractRange" missing
-FAIL StaticRange interface: existence and properties of interface object assert_own_property: should inherit from AbstractRange, but self has no such property expected property "AbstractRange" missing
+FAIL StaticRange interface: existence and properties of interface object assert_own_property: self does not have own property "AbstractRange" expected property "AbstractRange" missing
 PASS StaticRange interface object length
 PASS StaticRange interface object name
-FAIL StaticRange interface: existence and properties of interface prototype object assert_own_property: should inherit from AbstractRange, but self has no such property expected property "AbstractRange" missing
+FAIL StaticRange interface: existence and properties of interface prototype object assert_own_property: self does not have own property "AbstractRange" expected property "AbstractRange" missing
 PASS StaticRange interface: existence and properties of interface prototype object's "constructor" property
 PASS StaticRange interface: existence and properties of interface prototype object's @@unscopables property
-FAIL Range interface: existence and properties of interface object assert_own_property: should inherit from AbstractRange, but self has no such property expected property "AbstractRange" missing
+FAIL Range interface: existence and properties of interface object assert_own_property: self does not have own property "AbstractRange" expected property "AbstractRange" missing
 PASS Range interface object length
 PASS Range interface object name
-FAIL Range interface: existence and properties of interface prototype object assert_own_property: should inherit from AbstractRange, but self has no such property expected property "AbstractRange" missing
+FAIL Range interface: existence and properties of interface prototype object assert_own_property: self does not have own property "AbstractRange" expected property "AbstractRange" missing
 PASS Range interface: existence and properties of interface prototype object's "constructor" property
 PASS Range interface: existence and properties of interface prototype object's @@unscopables property
 PASS Range interface: attribute commonAncestorContainer
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/generic-sensor-reporting.https.html b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/generic-sensor-reporting.https.html
new file mode 100644
index 0000000..c60e3e8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/generic-sensor-reporting.https.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <script src='/resources/testharness.js'></script>
+    <script src='/resources/testharnessreport.js'></script>
+  </head>
+  <body>
+    <script>
+var sensor_features_verified = {
+  "accelerometer": false,
+  "ambient-light-sensor": false,
+  "magnetometer": false,
+  "gyroscope": false
+};
+
+var check_report_format = function(reports, observer) {
+  // Check each report in this batch. This observer callback may be called
+  // multiple times before all reports have been processed.
+  for (const report of reports) {
+
+    // Validate that the reported feature is one of the sensor features, and that
+    // we have not seen a report for this feature before.
+    assert_true(sensor_features_verified.hasOwnProperty(report.body.feature));
+    assert_false(sensor_features_verified[report.body.feature]);
+
+    // Validate the remainder of the report
+    assert_equals(report.type, "feature-policy");
+    assert_equals(report.url, document.location.href);
+    assert_equals(report.body.sourceFile, document.location.href);
+    assert_equals(typeof report.body.message, "string");
+    assert_equals(typeof report.body.lineNumber, "number");
+    assert_equals(typeof report.body.columnNumber, "number");
+
+    sensor_features_verified[report.body.feature] = true;
+  }
+
+  // Test is only done when reports for all features have been seen
+  for (let result of Object.values(sensor_features_verified)) {
+    if (!result)
+      return;
+  }
+  this.done();
+};
+
+async_test(t => {
+  new ReportingObserver(t.step_func(check_report_format),
+                        {types: ['feature-policy']}).observe();
+  assert_throws("SecurityError", () => new Accelerometer(), "Constructing sensors should be blocked by policy");
+  assert_throws("SecurityError", () => new AmbientLightSensor(), "Constructing sensors should be blocked by policy");
+  assert_throws("SecurityError", () => new Gyroscope(), "Constructing sensors should be blocked by policy");
+  assert_throws("SecurityError", () => new Magnetometer(), "Constructing sensors should be blocked by policy");
+}, "Generic Sensor Report Format");
+    </script>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/generic-sensor-reporting.https.html.headers b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/generic-sensor-reporting.https.html.headers
new file mode 100644
index 0000000..80cc027
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/reporting/generic-sensor-reporting.https.html.headers
@@ -0,0 +1 @@
+Feature-Policy: ambient-light-sensor 'none'; accelerometer 'none'; gyroscope 'none'; magnetometer 'none'
diff --git a/third_party/WebKit/LayoutTests/external/wpt/interfaces/webrtc.idl b/third_party/WebKit/LayoutTests/external/wpt/interfaces/webrtc.idl
index cdeb520..7496fff4 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/interfaces/webrtc.idl
+++ b/third_party/WebKit/LayoutTests/external/wpt/interfaces/webrtc.idl
@@ -297,6 +297,7 @@
              required DOMString transactionId;
              required sequence<RTCRtpEncodingParameters> encodings;
              RTCDegradationPreference degradationPreference = "balanced";
+             RTCPriorityType priority = "low";
 };
 
 dictionary RTCRtpReceiveParameters : RTCRtpParameters {
@@ -314,7 +315,6 @@
              octet codecPayloadType;
              RTCDtxStatus dtx;
              boolean active = true;
-             RTCPriorityType priority = "low";
              unsigned long ptime;
              unsigned long maxBitrate;
              double maxFramerate;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/resources/idlharness.js b/third_party/WebKit/LayoutTests/external/wpt/resources/idlharness.js
index 950df6fa..0f49a725 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/resources/idlharness.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/resources/idlharness.js
@@ -1256,6 +1256,48 @@
     });
 };
 
+/**
+ * Value of the LegacyNamespace extended attribute, if any.
+ *
+ * https://heycam.github.io/webidl/#LegacyNamespace
+ */
+IdlInterface.prototype.get_legacy_namespace = function()
+{
+    var legacyNamespace = this.extAttrs.find(function(attribute) {
+        return attribute.name === "LegacyNamespace";
+    });
+    return legacyNamespace ? legacyNamespace.rhs.value : undefined;
+};
+
+IdlInterface.prototype.get_interface_object_owner = function()
+{
+    var legacyNamespace = this.get_legacy_namespace();
+    return legacyNamespace ? self[legacyNamespace] : self;
+};
+
+IdlInterface.prototype.assert_interface_object_exists = function()
+{
+    var owner = this.get_legacy_namespace() || "self";
+    assert_own_property(self[owner], this.name, owner + " does not have own property " + format_value(this.name));
+};
+
+IdlInterface.prototype.get_interface_object = function() {
+    if (this.has_extended_attribute("NoInterfaceObject")) {
+        throw new IdlHarnessError(this.name + " has no interface object due to NoInterfaceObject");
+    }
+
+    return this.get_interface_object_owner()[this.name];
+};
+
+IdlInterface.prototype.get_qualified_name = function() {
+    // https://heycam.github.io/webidl/#qualified-name
+    var legacyNamespace = this.get_legacy_namespace();
+    if (legacyNamespace) {
+        return legacyNamespace + "." + this.name;
+    }
+    return this.name;
+};
+
 IdlInterface.prototype.has_to_json_regular_operation = function() {
     return this.members.some(function(m) {
         return m.is_to_json_regular_operation();
@@ -1436,9 +1478,8 @@
 
         // TODO: Should we test here that the property is actually writable
         // etc., or trust getOwnPropertyDescriptor?
-        assert_own_property(self, this.name,
-                            "self does not have own property " + format_value(this.name));
-        var desc = Object.getOwnPropertyDescriptor(self, this.name);
+        this.assert_interface_object_exists();
+        var desc = Object.getOwnPropertyDescriptor(this.get_interface_object_owner(), this.name);
         assert_false("get" in desc, "self's property " + format_value(this.name) + " should not have a getter");
         assert_false("set" in desc, "self's property " + format_value(this.name) + " should not have a setter");
         assert_true(desc.writable, "self's property " + format_value(this.name) + " should be writable");
@@ -1448,7 +1489,7 @@
         if (this.is_callback()) {
             // "The internal [[Prototype]] property of an interface object for
             // a callback interface must be the Function.prototype object."
-            assert_equals(Object.getPrototypeOf(self[this.name]), Function.prototype,
+            assert_equals(Object.getPrototypeOf(this.get_interface_object()), Function.prototype,
                           "prototype of self's property " + format_value(this.name) + " is not Object.prototype");
 
             return;
@@ -1478,24 +1519,19 @@
         // ES6 (rev 30) 19.1.3.6:
         // "Else, if O has a [[Call]] internal method, then let builtinTag be
         // "Function"."
-        assert_class_string(self[this.name], "Function", "class string of " + this.name);
+        assert_class_string(this.get_interface_object(), "Function", "class string of " + this.name);
 
         // "The [[Prototype]] internal property of an interface object for a
         // non-callback interface is determined as follows:"
-        var prototype = Object.getPrototypeOf(self[this.name]);
+        var prototype = Object.getPrototypeOf(this.get_interface_object());
         if (this.base) {
             // "* If the interface inherits from some other interface, the
             //    value of [[Prototype]] is the interface object for that other
             //    interface."
-            var has_interface_object =
-                !this.array
-                     .members[this.base]
-                     .has_extended_attribute("NoInterfaceObject");
-            if (has_interface_object) {
-                assert_own_property(self, this.base,
-                                    'should inherit from ' + this.base +
-                                    ', but self has no such property');
-                assert_equals(prototype, self[this.base],
+            var inherited_interface = this.array.members[this.base];
+            if (!inherited_interface.has_extended_attribute("NoInterfaceObject")) {
+                inherited_interface.assert_interface_object_exists();
+                assert_equals(prototype, inherited_interface.get_interface_object(),
                               'prototype of ' + this.name + ' is not ' +
                               this.base);
             }
@@ -1513,12 +1549,13 @@
             //
             // "If I was not declared with a [Constructor] extended attribute,
             // then throw a TypeError."
+            var interface_object = this.get_interface_object();
             assert_throws(new TypeError(), function() {
-                self[this.name]();
-            }.bind(this), "interface object didn't throw TypeError when called as a function");
+                interface_object();
+            }, "interface object didn't throw TypeError when called as a function");
             assert_throws(new TypeError(), function() {
-                new self[this.name]();
-            }.bind(this), "interface object didn't throw TypeError when called as a constructor");
+                new interface_object();
+            }, "interface object didn't throw TypeError when called as a constructor");
         }
     }.bind(this), this.name + " interface: existence and properties of interface object");
 
@@ -1527,15 +1564,14 @@
             // This function tests WebIDL as of 2014-10-25.
             // https://heycam.github.io/webidl/#es-interface-call
 
-            assert_own_property(self, this.name,
-                                "self does not have own property " + format_value(this.name));
+            this.assert_interface_object_exists();
 
             // "Interface objects for non-callback interfaces MUST have a
             // property named “length” with attributes { [[Writable]]: false,
             // [[Enumerable]]: false, [[Configurable]]: true } whose value is
             // a Number."
-            assert_own_property(self[this.name], "length");
-            var desc = Object.getOwnPropertyDescriptor(self[this.name], "length");
+            assert_own_property(this.get_interface_object(), "length");
+            var desc = Object.getOwnPropertyDescriptor(this.get_interface_object(), "length");
             assert_false("get" in desc, this.name + ".length should not have a getter");
             assert_false("set" in desc, this.name + ".length should not have a setter");
             assert_false(desc.writable, this.name + ".length should not be writable");
@@ -1545,7 +1581,7 @@
             var constructors = this.extAttrs
                 .filter(function(attr) { return attr.name == "Constructor"; });
             var expected_length = minOverloadLength(constructors);
-            assert_equals(self[this.name].length, expected_length, "wrong value for " + this.name + ".length");
+            assert_equals(this.get_interface_object().length, expected_length, "wrong value for " + this.name + ".length");
         }.bind(this), this.name + " interface object length");
     }
 
@@ -1554,22 +1590,21 @@
             // This function tests WebIDL as of 2015-11-17.
             // https://heycam.github.io/webidl/#interface-object
 
-            assert_own_property(self, this.name,
-                                "self does not have own property " + format_value(this.name));
+            this.assert_interface_object_exists();
 
             // "All interface objects must have a property named “name” with
             // attributes { [[Writable]]: false, [[Enumerable]]: false,
             // [[Configurable]]: true } whose value is the identifier of the
             // corresponding interface."
 
-            assert_own_property(self[this.name], "name");
-            var desc = Object.getOwnPropertyDescriptor(self[this.name], "name");
+            assert_own_property(this.get_interface_object(), "name");
+            var desc = Object.getOwnPropertyDescriptor(this.get_interface_object(), "name");
             assert_false("get" in desc, this.name + ".name should not have a getter");
             assert_false("set" in desc, this.name + ".name should not have a setter");
             assert_false(desc.writable, this.name + ".name should not be writable");
             assert_false(desc.enumerable, this.name + ".name should not be enumerable");
             assert_true(desc.configurable, this.name + ".name should be configurable");
-            assert_equals(self[this.name].name, this.name, "wrong value for " + this.name + ".name");
+            assert_equals(this.get_interface_object().name, this.name, "wrong value for " + this.name + ".name");
         }.bind(this), this.name + " interface object name");
     }
 
@@ -1608,9 +1643,9 @@
             if (exposed_in(exposure_set(this, this.exposureSet)) && 'document' in self) {
                 for (alias of aliases) {
                     assert_true(alias in self, alias + " should exist");
-                    assert_equals(self[alias], self[this.name], "self." + alias + " should be the same value as self." + this.name);
+                    assert_equals(self[alias], this.get_interface_object(), "self." + alias + " should be the same value as the interface object");
                     var desc = Object.getOwnPropertyDescriptor(self, alias);
-                    assert_equals(desc.value, self[this.name], "wrong value in " + alias + " property descriptor");
+                    assert_equals(desc.value, this.get_interface_object(), "wrong value in " + alias + " property descriptor");
                     assert_true(desc.writable, alias + " should be writable");
                     assert_false(desc.enumerable, alias + " should not be enumerable");
                     assert_true(desc.configurable, alias + " should be configurable");
@@ -1636,11 +1671,10 @@
             return;
         }
 
-        assert_own_property(self, this.name,
-                            "self does not have own property " + format_value(this.name));
+        this.assert_interface_object_exists();
 
         if (this.is_callback()) {
-            assert_false("prototype" in self[this.name],
+            assert_false("prototype" in this.get_interface_object(),
                          this.name + ' should not have a "prototype" property');
             return;
         }
@@ -1652,9 +1686,9 @@
         // properties that correspond to the regular attributes and regular
         // operations defined on the interface, and is described in more detail
         // in section 4.5.4 below."
-        assert_own_property(self[this.name], "prototype",
+        assert_own_property(this.get_interface_object(), "prototype",
                             'interface "' + this.name + '" does not have own property "prototype"');
-        var desc = Object.getOwnPropertyDescriptor(self[this.name], "prototype");
+        var desc = Object.getOwnPropertyDescriptor(this.get_interface_object(), "prototype");
         assert_false("get" in desc, this.name + ".prototype should not have a getter");
         assert_false("set" in desc, this.name + ".prototype should not have a setter");
         assert_false(desc.writable, this.name + ".prototype should not be writable");
@@ -1687,38 +1721,39 @@
         //     object %ErrorPrototype%."
         //
         if (this.name === "Window") {
-            assert_class_string(Object.getPrototypeOf(self[this.name].prototype),
+            assert_class_string(Object.getPrototypeOf(this.get_interface_object().prototype),
                                 'WindowProperties',
                                 'Class name for prototype of Window' +
                                 '.prototype is not "WindowProperties"');
         } else {
-            var inherit_interface, inherit_interface_has_interface_object;
+            var inherit_interface, inherit_interface_interface_object;
             if (this.base) {
                 inherit_interface = this.base;
-                inherit_interface_has_interface_object =
-                    !this.array
-                         .members[inherit_interface]
-                         .has_extended_attribute("NoInterfaceObject");
+                var parent = this.array.members[inherit_interface];
+                if (!parent.has_extended_attribute("NoInterfaceObject")) {
+                    parent.assert_interface_object_exists();
+                    inherit_interface_interface_object = parent.get_interface_object();
+                }
             } else if (this.name === "DOMException") {
                 inherit_interface = 'Error';
-                inherit_interface_has_interface_object = true;
+                inherit_interface_interface_object = self.Error;
             } else {
                 inherit_interface = 'Object';
-                inherit_interface_has_interface_object = true;
+                inherit_interface_interface_object = self.Object;
             }
-            if (inherit_interface_has_interface_object) {
-                assert_own_property(self, inherit_interface,
-                                    'should inherit from ' + inherit_interface + ', but self has no such property');
-                assert_own_property(self[inherit_interface], 'prototype',
+            if (inherit_interface_interface_object) {
+                assert_not_equals(inherit_interface_interface_object, undefined,
+                                  'should inherit from ' + inherit_interface + ', but there is no such property');
+                assert_own_property(inherit_interface_interface_object, 'prototype',
                                     'should inherit from ' + inherit_interface + ', but that object has no "prototype" property');
-                assert_equals(Object.getPrototypeOf(self[this.name].prototype),
-                              self[inherit_interface].prototype,
+                assert_equals(Object.getPrototypeOf(this.get_interface_object().prototype),
+                              inherit_interface_interface_object.prototype,
                               'prototype of ' + this.name + '.prototype is not ' + inherit_interface + '.prototype');
             } else {
                 // We can't test that we get the correct object, because this is the
                 // only way to get our hands on it. We only test that its class
                 // string, at least, is correct.
-                assert_class_string(Object.getPrototypeOf(self[this.name].prototype),
+                assert_class_string(Object.getPrototypeOf(this.get_interface_object().prototype),
                                     inherit_interface + 'Prototype',
                                     'Class name for prototype of ' + this.name +
                                     '.prototype is not "' + inherit_interface + 'Prototype"');
@@ -1726,20 +1761,20 @@
         }
 
         // "The class string of an interface prototype object is the
-        // concatenation of the interface’s identifier and the string
+        // concatenation of the interface’s qualified identifier and the string
         // “Prototype”."
 
         // Skip these tests for now due to a specification issue about
         // prototype name.
         // https://www.w3.org/Bugs/Public/show_bug.cgi?id=28244
 
-        // assert_class_string(self[this.name].prototype, this.name + "Prototype",
+        // assert_class_string(this.get_interface_object().prototype, this.get_qualified_name() + "Prototype",
         //                     "class string of " + this.name + ".prototype");
 
         // String() should end up calling {}.toString if nothing defines a
         // stringifier.
         if (!this.has_stringifier()) {
-            // assert_equals(String(self[this.name].prototype), "[object " + this.name + "Prototype]",
+            // assert_equals(String(this.get_interface_object().prototype), "[object " + this.get_qualified_name() + "Prototype]",
             //         "String(" + this.name + ".prototype)");
         }
     }.bind(this), this.name + " interface: existence and properties of interface prototype object");
@@ -1751,7 +1786,7 @@
     // prototype exotic object."
     // https://heycam.github.io/webidl/#interface-prototype-object
     if (this.is_global()) {
-        this.test_immutable_prototype("interface prototype object", self[this.name].prototype);
+        this.test_immutable_prototype("interface prototype object", this.get_interface_object().prototype);
     }
 
     subsetTestByKey(this.name, test, function()
@@ -1760,16 +1795,15 @@
             return;
         }
 
-        assert_own_property(self, this.name,
-                            "self does not have own property " + format_value(this.name));
+        this.assert_interface_object_exists();
 
         if (this.is_callback()) {
-            assert_false("prototype" in self[this.name],
+            assert_false("prototype" in this.get_interface_object(),
                          this.name + ' should not have a "prototype" property');
             return;
         }
 
-        assert_own_property(self[this.name], "prototype",
+        assert_own_property(this.get_interface_object(), "prototype",
                             'interface "' + this.name + '" does not have own property "prototype"');
 
         // "If the [NoInterfaceObject] extended attribute was not specified on
@@ -1777,15 +1811,15 @@
         // property named “constructor” with attributes { [[Writable]]: true,
         // [[Enumerable]]: false, [[Configurable]]: true } whose value is a
         // reference to the interface object for the interface."
-        assert_own_property(self[this.name].prototype, "constructor",
+        assert_own_property(this.get_interface_object().prototype, "constructor",
                             this.name + '.prototype does not have own property "constructor"');
-        var desc = Object.getOwnPropertyDescriptor(self[this.name].prototype, "constructor");
+        var desc = Object.getOwnPropertyDescriptor(this.get_interface_object().prototype, "constructor");
         assert_false("get" in desc, this.name + ".prototype.constructor should not have a getter");
         assert_false("set" in desc, this.name + ".prototype.constructor should not have a setter");
         assert_true(desc.writable, this.name + ".prototype.constructor should be writable");
         assert_false(desc.enumerable, this.name + ".prototype.constructor should not be enumerable");
         assert_true(desc.configurable, this.name + ".prototype.constructor should be configurable");
-        assert_equals(self[this.name].prototype.constructor, self[this.name],
+        assert_equals(this.get_interface_object().prototype.constructor, this.get_interface_object(),
                       this.name + '.prototype.constructor is not the same object as ' + this.name);
     }.bind(this), this.name + ' interface: existence and properties of interface prototype object\'s "constructor" property');
 
@@ -1796,16 +1830,15 @@
             return;
         }
 
-        assert_own_property(self, this.name,
-                            "self does not have own property " + format_value(this.name));
+        this.assert_interface_object_exists();
 
         if (this.is_callback()) {
-            assert_false("prototype" in self[this.name],
+            assert_false("prototype" in this.get_interface_object(),
                          this.name + ' should not have a "prototype" property');
             return;
         }
 
-        assert_own_property(self[this.name], "prototype",
+        assert_own_property(this.get_interface_object(), "prototype",
                             'interface "' + this.name + '" does not have own property "prototype"');
 
         // If the interface has any member declared with the [Unscopable] extended
@@ -1814,7 +1847,7 @@
         // { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true },
         // and whose value is an object created as follows...
         var unscopables = this.get_unscopables().map(m => m.name);
-        var proto = self[this.name].prototype;
+        var proto = this.get_interface_object().prototype;
         if (unscopables.length != 0) {
             assert_own_property(
                 proto, Symbol.unscopables,
@@ -1845,7 +1878,7 @@
                                   this.name + '.prototype[Symbol.unscopables] has unexpected property "' + prop + '"');
             }
         } else {
-            assert_equals(Object.getOwnPropertyDescriptor(self[this.name].prototype, Symbol.unscopables),
+            assert_equals(Object.getOwnPropertyDescriptor(this.get_interface_object().prototype, Symbol.unscopables),
                           undefined,
                           this.name + '.prototype should not have @@unscopables');
         }
@@ -1962,21 +1995,20 @@
 
     subsetTestByKey(this.name, test, function()
     {
-        assert_own_property(self, this.name,
-                            "self does not have own property " + format_value(this.name));
+        this.assert_interface_object_exists();
 
         // "For each constant defined on an interface A, there must be
         // a corresponding property on the interface object, if it
         // exists."
-        assert_own_property(self[this.name], member.name);
+        assert_own_property(this.get_interface_object(), member.name);
         // "The value of the property is that which is obtained by
         // converting the constant’s IDL value to an ECMAScript
         // value."
-        assert_equals(self[this.name][member.name], constValue(member.value),
+        assert_equals(this.get_interface_object()[member.name], constValue(member.value),
                       "property has wrong value");
         // "The property has attributes { [[Writable]]: false,
         // [[Enumerable]]: true, [[Configurable]]: false }."
-        var desc = Object.getOwnPropertyDescriptor(self[this.name], member.name);
+        var desc = Object.getOwnPropertyDescriptor(this.get_interface_object(), member.name);
         assert_false("get" in desc, "property should not have a getter");
         assert_false("set" in desc, "property should not have a setter");
         assert_false(desc.writable, "property should not be writable");
@@ -1988,22 +2020,21 @@
     // exist on the interface prototype object."
     subsetTestByKey(this.name, test, function()
     {
-        assert_own_property(self, this.name,
-                            "self does not have own property " + format_value(this.name));
+        this.assert_interface_object_exists();
 
         if (this.is_callback()) {
-            assert_false("prototype" in self[this.name],
+            assert_false("prototype" in this.get_interface_object(),
                          this.name + ' should not have a "prototype" property');
             return;
         }
 
-        assert_own_property(self[this.name], "prototype",
+        assert_own_property(this.get_interface_object(), "prototype",
                             'interface "' + this.name + '" does not have own property "prototype"');
 
-        assert_own_property(self[this.name].prototype, member.name);
-        assert_equals(self[this.name].prototype[member.name], constValue(member.value),
+        assert_own_property(this.get_interface_object().prototype, member.name);
+        assert_equals(this.get_interface_object().prototype[member.name], constValue(member.value),
                       "property has wrong value");
-        var desc = Object.getOwnPropertyDescriptor(self[this.name], member.name);
+        var desc = Object.getOwnPropertyDescriptor(this.get_interface_object(), member.name);
         assert_false("get" in desc, "property should not have a getter");
         assert_false("set" in desc, "property should not have a setter");
         assert_false(desc.writable, "property should not be writable");
@@ -2026,13 +2057,12 @@
             return;
         }
 
-        assert_own_property(self, this.name,
-                            "self does not have own property " + format_value(this.name));
-        assert_own_property(self[this.name], "prototype",
+        this.assert_interface_object_exists();
+        assert_own_property(this.get_interface_object(), "prototype",
                             'interface "' + this.name + '" does not have own property "prototype"');
 
         if (member["static"]) {
-            assert_own_property(self[this.name], member.name,
+            assert_own_property(this.get_interface_object(), member.name,
                 "The interface object must have a property " +
                 format_value(member.name));
             a_test.done();
@@ -2045,7 +2075,7 @@
             assert_own_property(self, member.name,
                 "The global object must have a property " +
                 format_value(member.name));
-            assert_false(member.name in self[this.name].prototype,
+            assert_false(member.name in this.get_interface_object().prototype,
                 "The prototype object should not have a property " +
                 format_value(member.name));
 
@@ -2073,34 +2103,34 @@
             // since it will call done() on a_test.
             this.do_interface_attribute_asserts(self, member, a_test);
         } else {
-            assert_true(member.name in self[this.name].prototype,
+            assert_true(member.name in this.get_interface_object().prototype,
                 "The prototype object must have a property " +
                 format_value(member.name));
 
             if (!member.has_extended_attribute("LenientThis")) {
                 if (member.idlType.generic !== "Promise") {
                     assert_throws(new TypeError(), function() {
-                        self[this.name].prototype[member.name];
+                        this.get_interface_object().prototype[member.name];
                     }.bind(this), "getting property on prototype object must throw TypeError");
                     // do_interface_attribute_asserts must be the last thing we
                     // do, since it will call done() on a_test.
-                    this.do_interface_attribute_asserts(self[this.name].prototype, member, a_test);
+                    this.do_interface_attribute_asserts(this.get_interface_object().prototype, member, a_test);
                 } else {
                     promise_rejects(a_test, new TypeError(),
-                                    self[this.name].prototype[member.name])
+                                    this.get_interface_object().prototype[member.name])
                         .then(function() {
                             // do_interface_attribute_asserts must be the last
                             // thing we do, since it will call done() on a_test.
-                            this.do_interface_attribute_asserts(self[this.name].prototype,
+                            this.do_interface_attribute_asserts(this.get_interface_object().prototype,
                                                                 member, a_test);
                         }.bind(this));
                 }
             } else {
-                assert_equals(self[this.name].prototype[member.name], undefined,
+                assert_equals(this.get_interface_object().prototype[member.name], undefined,
                               "getting property on prototype object must return undefined");
               // do_interface_attribute_asserts must be the last thing we do,
               // since it will call done() on a_test.
-              this.do_interface_attribute_asserts(self[this.name].prototype, member, a_test);
+              this.do_interface_attribute_asserts(this.get_interface_object().prototype, member, a_test);
             }
         }
     }.bind(this));
@@ -2125,17 +2155,16 @@
             return;
         }
 
-        assert_own_property(self, this.name,
-                            "self does not have own property " + format_value(this.name));
+        this.assert_interface_object_exists();
 
         if (this.is_callback()) {
-            assert_false("prototype" in self[this.name],
+            assert_false("prototype" in this.get_interface_object(),
                          this.name + ' should not have a "prototype" property');
             a_test.done();
             return;
         }
 
-        assert_own_property(self[this.name], "prototype",
+        assert_own_property(this.get_interface_object(), "prototype",
                             'interface "' + this.name + '" does not have own property "prototype"');
 
         // "For each unique identifier of an exposed operation defined on the
@@ -2150,9 +2179,9 @@
         // "* If the operation is static, then the property exists on the
         //    interface object."
         if (member["static"]) {
-            assert_own_property(self[this.name], member.name,
+            assert_own_property(this.get_interface_object(), member.name,
                     "interface object missing static operation");
-            memberHolderObject = self[this.name];
+            memberHolderObject = this.get_interface_object();
         // "* Otherwise, [...] if the interface was declared with the [Global]
         //    extended attribute, then the property exists
         //    on every object that implements the interface."
@@ -2163,9 +2192,9 @@
         // "* Otherwise, the property exists solely on the interface’s
         //    interface prototype object."
         } else {
-            assert_own_property(self[this.name].prototype, member.name,
+            assert_own_property(this.get_interface_object().prototype, member.name,
                     "interface prototype object missing non-static operation");
-            memberHolderObject = self[this.name].prototype;
+            memberHolderObject = this.get_interface_object().prototype;
         }
         this.do_member_unscopable_asserts(member);
         this.do_member_operation_asserts(memberHolderObject, member, a_test);
@@ -2180,7 +2209,7 @@
         return;
     }
 
-    var unscopables = self[this.name].prototype[Symbol.unscopables];
+    var unscopables = this.get_interface_object().prototype[Symbol.unscopables];
     var prop = member.name;
     var propDesc = Object.getOwnPropertyDescriptor(unscopables, prop);
     assert_equals(typeof propDesc, "object",
@@ -2308,27 +2337,26 @@
 
 IdlInterface.prototype.test_member_iterable = function(member)
 {
-    var interfaceName = this.name;
     var isPairIterator = member.idlType.length === 2;
     subsetTestByKey(this.name, test, function()
     {
-        var descriptor = Object.getOwnPropertyDescriptor(self[interfaceName].prototype, Symbol.iterator);
+        var descriptor = Object.getOwnPropertyDescriptor(this.get_interface_object().prototype, Symbol.iterator);
         assert_true(descriptor.writable, "property should be writable");
         assert_true(descriptor.configurable, "property should be configurable");
         assert_false(descriptor.enumerable, "property should not be enumerable");
-        assert_equals(self[interfaceName].prototype[Symbol.iterator].name, isPairIterator ? "entries" : "values", "@@iterator function does not have the right name");
-    }, "Testing Symbol.iterator property of iterable interface " + interfaceName);
+        assert_equals(this.get_interface_object().prototype[Symbol.iterator].name, isPairIterator ? "entries" : "values", "@@iterator function does not have the right name");
+    }.bind(this), "Testing Symbol.iterator property of iterable interface " + this.name);
 
     if (isPairIterator) {
         subsetTestByKey(this.name, test, function() {
-            assert_equals(self[interfaceName].prototype[Symbol.iterator], self[interfaceName].prototype["entries"], "entries method is not the same as @@iterator");
-        }, "Testing pair iterable interface " + interfaceName);
+            assert_equals(this.get_interface_object().prototype[Symbol.iterator], this.get_interface_object().prototype["entries"], "entries method is not the same as @@iterator");
+        }.bind(this), "Testing pair iterable interface " + this.name);
     } else {
         subsetTestByKey(this.name, test, function() {
             ["entries", "keys", "values", "forEach", Symbol.Iterator].forEach(function(property) {
-                assert_equals(self[interfaceName].prototype[property], Array.prototype[property], property + " function is not the same as Array one");
-            });
-        }, "Testing value iterable interface " + interfaceName);
+                assert_equals(this.get_interface_object().prototype[property], Array.prototype[property], property + " function is not the same as Array one");
+            }.bind(this));
+        }.bind(this), "Testing value iterable interface " + this.name);
     }
 };
 
@@ -2340,21 +2368,20 @@
             return;
         }
 
-        assert_own_property(self, this.name,
-                            "self does not have own property " + format_value(this.name));
+        this.assert_interface_object_exists();
 
         if (this.is_callback()) {
-            assert_false("prototype" in self[this.name],
+            assert_false("prototype" in this.get_interface_object(),
                          this.name + ' should not have a "prototype" property');
             return;
         }
 
-        assert_own_property(self[this.name], "prototype",
+        assert_own_property(this.get_interface_object(), "prototype",
                             'interface "' + this.name + '" does not have own property "prototype"');
 
         // ". . . the property exists on the interface prototype object."
-        var interfacePrototypeObject = self[this.name].prototype;
-        assert_own_property(self[this.name].prototype, "toString",
+        var interfacePrototypeObject = this.get_interface_object().prototype;
+        assert_own_property(interfacePrototypeObject, "toString",
                 "interface prototype object missing non-static operation");
 
         var stringifierUnforgeable = member.isUnforgeable;
@@ -2380,7 +2407,7 @@
 
         // "Let O be the result of calling ToObject on the this value."
         assert_throws(new TypeError(), function() {
-            self[this.name].prototype.toString.apply(null, []);
+            interfacePrototypeObject.toString.apply(null, []);
         }, "calling stringifier with this = null didn't throw TypeError");
 
         // "If O is not an object that implements the interface on which the
@@ -2389,7 +2416,7 @@
         // TODO: Test a platform object that implements some other
         // interface.  (Have to be sure to get inheritance right.)
         assert_throws(new TypeError(), function() {
-            self[this.name].prototype.toString.apply({}, []);
+            interfacePrototypeObject.toString.apply({}, []);
         }, "calling stringifier with this = {} didn't throw TypeError");
     }.bind(this), this.name + " interface: stringifier");
 };
@@ -2419,10 +2446,10 @@
         if (!exposed_in(exposure_set(member, this.exposureSet))) {
             subsetTestByKey(this.name, test, function() {
                 // It's not exposed, so we shouldn't find it anywhere.
-                assert_false(member.name in self[this.name],
+                assert_false(member.name in this.get_interface_object(),
                              "The interface object must not have a property " +
                              format_value(member.name));
-                assert_false(member.name in self[this.name].prototype,
+                assert_false(member.name in this.get_interface_object().prototype,
                              "The prototype object must not have a property " +
                              format_value(member.name));
             }.bind(this), this.name + " interface: member " + member.name);
@@ -2538,9 +2565,8 @@
         {
             assert_equals(exception, null, "Unexpected exception when evaluating object");
             assert_equals(typeof obj, expected_typeof, "wrong typeof object");
-            assert_own_property(self, this.name,
-                                "self does not have own property " + format_value(this.name));
-            assert_own_property(self[this.name], "prototype",
+            this.assert_interface_object_exists();
+            assert_own_property(this.get_interface_object(), "prototype",
                                 'interface "' + this.name + '" does not have own property "prototype"');
 
             // "The value of the internal [[Prototype]] property of the
@@ -2548,22 +2574,22 @@
             // interface from the platform object’s associated global
             // environment."
             assert_equals(Object.getPrototypeOf(obj),
-                          self[this.name].prototype,
+                          this.get_interface_object().prototype,
                           desc + "'s prototype is not " + this.name + ".prototype");
         }.bind(this), this.name + " must be primary interface of " + desc);
     }
 
     // "The class string of a platform object that implements one or more
-    // interfaces must be the identifier of the primary interface of the
+    // interfaces must be the qualified name of the primary interface of the
     // platform object."
     subsetTestByKey(this.name, test, function()
     {
         assert_equals(exception, null, "Unexpected exception when evaluating object");
         assert_equals(typeof obj, expected_typeof, "wrong typeof object");
-        assert_class_string(obj, this.name, "class string of " + desc);
+        assert_class_string(obj, this.get_qualified_name(), "class string of " + desc);
         if (!this.has_stringifier())
         {
-            assert_equals(String(obj), "[object " + this.name + "]", "String(" + desc + ")");
+            assert_equals(String(obj), "[object " + this.get_qualified_name() + "]", "String(" + desc + ")");
         }
     }.bind(this), "Stringification of " + desc);
 };
diff --git a/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/resources/service-worker-header.py b/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/resources/service-worker-header.py
index 2e82e78..74f57a72a 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/resources/service-worker-header.py
+++ b/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/resources/service-worker-header.py
@@ -1,7 +1,20 @@
 def main(request, response):
   service_worker_header = request.headers.get('service-worker')
-  if service_worker_header == 'script':
-    body = '// Request has `Service-Worker: script` header'
-    return 200, [('Content-Type', 'application/javascript')], body
-  else:
+
+  if 'header' in request.GET and service_worker_header != 'script':
     return 400, [('Content-Type', 'text/plain')], 'Bad Request'
+
+  if 'no-header' in request.GET and service_worker_header == 'script':
+    return 400, [('Content-Type', 'text/plain')], 'Bad Request'
+
+  # no-cache itself to ensure the user agent finds a new version for each
+  # update.
+  headers = [('Cache-Control', 'no-cache, must-revalidate'),
+             ('Pragma', 'no-cache'),
+             ('Content-Type', 'application/javascript')]
+  body = '/* This is a service worker script */\n'
+
+  if 'import' in request.GET:
+    body += "importScripts('%s');" % request.GET['import']
+
+  return 200, headers, body
diff --git a/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/service-worker-header.https.html b/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/service-worker-header.https.html
index 2584485..fb902cd1 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/service-worker-header.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/service-worker-header.https.html
@@ -5,14 +5,19 @@
 <script src="resources/test-helpers.sub.js"></script>
 <script>
 
-promise_test(t => {
-  const script = 'resources/service-worker-header.py';
+promise_test(async t => {
+  const script = 'resources/service-worker-header.py'
+    + '?header&import=service-worker-header.py?no-header';
   const scope = 'resources/service-worker-header';
-  return service_worker_unregister_and_register(t, script, scope)
-    .then(registration => {
-      assert_true(registration instanceof ServiceWorkerRegistration);
-      return registration.unregister();
-    });
-}, 'A request to fetch service worker script should have Service-Worker header');
+  const expected_url = normalizeURL(script);
+  const registration =
+    await service_worker_unregister_and_register(t, script, scope);
+  t.add_cleanup(() => registration.unregister());
+  assert_true(registration instanceof ServiceWorkerRegistration);
+
+  await wait_for_state(t, registration.installing, 'activated');
+  await registration.update();
+}, 'A request to fetch service worker main script should have Service-Worker '
+  + 'header and imported scripts should not have one');
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/shape-detection/idlharness.any-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/shape-detection/idlharness.any-expected.txt
index 24c61932..d6bc9aa 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/shape-detection/idlharness.any-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/shape-detection/idlharness.any-expected.txt
@@ -7,7 +7,9 @@
 PASS FaceDetector interface: existence and properties of interface prototype object's "constructor" property
 PASS FaceDetector interface: existence and properties of interface prototype object's @@unscopables property
 PASS FaceDetector interface: operation detect(ImageBitmapSource)
-FAIL DetectedFace interface: existence and properties of interface object assert_throws: interface object didn't throw TypeError when called as a constructor function "function () { [native code] }" did not throw
+FAIL DetectedFace interface: existence and properties of interface object assert_throws: interface object didn't throw TypeError when called as a constructor function "function() {
+                new interface_object();
+            }" did not throw
 PASS DetectedFace interface object length
 PASS DetectedFace interface object name
 PASS DetectedFace interface: existence and properties of interface prototype object
@@ -23,7 +25,9 @@
 PASS BarcodeDetector interface: existence and properties of interface prototype object's @@unscopables property
 FAIL BarcodeDetector interface: attribute supportedFormats assert_true: The prototype object must have a property "supportedFormats" expected true got false
 PASS BarcodeDetector interface: operation detect(ImageBitmapSource)
-FAIL DetectedBarcode interface: existence and properties of interface object assert_throws: interface object didn't throw TypeError when called as a constructor function "function () { [native code] }" did not throw
+FAIL DetectedBarcode interface: existence and properties of interface object assert_throws: interface object didn't throw TypeError when called as a constructor function "function() {
+                new interface_object();
+            }" did not throw
 PASS DetectedBarcode interface object length
 PASS DetectedBarcode interface object name
 PASS DetectedBarcode interface: existence and properties of interface prototype object
diff --git a/third_party/WebKit/LayoutTests/external/wpt/wasm/idlharness.any.js b/third_party/WebKit/LayoutTests/external/wpt/wasm/idlharness.any.js
deleted file mode 100644
index 9c29ad1..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/wasm/idlharness.any.js
+++ /dev/null
@@ -1,33 +0,0 @@
-// META: script=/resources/WebIDLParser.js
-// META: script=/resources/idlharness.js
-// META: script=resources/load_wasm.js
-
-'use strict';
-
-// https://webassembly.github.io/spec/js-api/
-
-promise_test(async () => {
-  const srcs = ['wasm-js-api'];
-  const [wasm] = await Promise.all(
-    srcs.map(i => fetch(`/interfaces/${i}.idl`).then(r => r.text())));
-
-  const idl_array = new IdlArray();
-  idl_array.add_idls(wasm);
-  // Ignored errors are surfaced in idlharness.js's test_object below.
-  try {
-    self.memory = new Memory({initial: 1024});
-  } catch (e) { }
-
-  try {
-    self.mod = await createWasmModule();
-    self.instance = new Instance(self.mod);
-  } catch (e) { }
-
-  idl_array.add_objects({
-    Memory: ['memory'],
-    Module: ['mod'],
-    Instance: ['instance'],
-  });
-  idl_array.test();
-}, 'wasm-js-api interfaces.');
-
diff --git a/third_party/WebKit/LayoutTests/external/wpt/wasm/jsapi/idlharness.any-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/wasm/jsapi/idlharness.any-expected.txt
new file mode 100644
index 0000000..d120c0a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/wasm/jsapi/idlharness.any-expected.txt
@@ -0,0 +1,69 @@
+This is a testharness.js-based test.
+Found 63 tests; 46 PASS, 17 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS wasm-js-api interfaces.
+FAIL WebAssembly namespace: operation validate(BufferSource) assert_true: property should be enumerable expected true got false
+FAIL WebAssembly namespace: operation compile(BufferSource) assert_true: property should be enumerable expected true got false
+FAIL WebAssembly namespace: operation instantiate(BufferSource, object) assert_true: property should be enumerable expected true got false
+FAIL WebAssembly namespace: operation instantiate(Module, object) assert_true: property should be enumerable expected true got false
+PASS Module interface: existence and properties of interface object
+PASS Module interface object length
+PASS Module interface object name
+PASS Module interface: existence and properties of interface prototype object
+PASS Module interface: existence and properties of interface prototype object's "constructor" property
+PASS Module interface: existence and properties of interface prototype object's @@unscopables property
+FAIL Module interface: operation exports(Module) assert_true: property should be enumerable expected true got false
+FAIL Module interface: operation imports(Module) assert_true: property should be enumerable expected true got false
+FAIL Module interface: operation customSections(Module, USVString) assert_true: property should be enumerable expected true got false
+PASS Module must be primary interface of mod
+PASS Stringification of mod
+PASS Module interface: mod must inherit property "exports(Module)" with the proper type
+PASS Module interface: calling exports(Module) on mod with too few arguments must throw TypeError
+PASS Module interface: mod must inherit property "imports(Module)" with the proper type
+PASS Module interface: calling imports(Module) on mod with too few arguments must throw TypeError
+PASS Module interface: mod must inherit property "customSections(Module, USVString)" with the proper type
+PASS Module interface: calling customSections(Module, USVString) on mod with too few arguments must throw TypeError
+PASS Instance interface: existence and properties of interface object
+PASS Instance interface object length
+PASS Instance interface object name
+PASS Instance interface: existence and properties of interface prototype object
+PASS Instance interface: existence and properties of interface prototype object's "constructor" property
+PASS Instance interface: existence and properties of interface prototype object's @@unscopables property
+FAIL Instance interface: attribute exports assert_true: property should be enumerable expected true got false
+PASS Instance must be primary interface of instance
+PASS Stringification of instance
+PASS Instance interface: instance must inherit property "exports" with the proper type
+PASS Memory interface: existence and properties of interface object
+PASS Memory interface object length
+PASS Memory interface object name
+PASS Memory interface: existence and properties of interface prototype object
+PASS Memory interface: existence and properties of interface prototype object's "constructor" property
+PASS Memory interface: existence and properties of interface prototype object's @@unscopables property
+FAIL Memory interface: operation grow(unsigned long) assert_true: property should be enumerable expected true got false
+FAIL Memory interface: attribute buffer assert_true: property should be enumerable expected true got false
+PASS Memory must be primary interface of memory
+PASS Stringification of memory
+PASS Memory interface: memory must inherit property "grow(unsigned long)" with the proper type
+FAIL Memory interface: calling grow(unsigned long) on memory with too few arguments must throw TypeError assert_throws: Called with 0 arguments function "function() {
+            fn.apply(obj, args);
+        }" did not throw
+PASS Memory interface: memory must inherit property "buffer" with the proper type
+PASS Table interface: existence and properties of interface object
+PASS Table interface object length
+PASS Table interface object name
+PASS Table interface: existence and properties of interface prototype object
+PASS Table interface: existence and properties of interface prototype object's "constructor" property
+PASS Table interface: existence and properties of interface prototype object's @@unscopables property
+FAIL Table interface: operation grow(unsigned long) assert_true: property should be enumerable expected true got false
+FAIL Table interface: operation get(unsigned long) assert_true: property should be enumerable expected true got false
+FAIL Table interface: operation set(unsigned long, Function) assert_true: property should be enumerable expected true got false
+FAIL Table interface: attribute length assert_true: property should be enumerable expected true got false
+PASS Global interface: existence and properties of interface object
+PASS Global interface object length
+PASS Global interface object name
+PASS Global interface: existence and properties of interface prototype object
+PASS Global interface: existence and properties of interface prototype object's "constructor" property
+PASS Global interface: existence and properties of interface prototype object's @@unscopables property
+FAIL Global interface: operation valueOf() assert_true: property should be enumerable expected true got false
+FAIL Global interface: attribute value assert_true: property should be enumerable expected true got false
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/wasm/jsapi/idlharness.any.js b/third_party/WebKit/LayoutTests/external/wpt/wasm/jsapi/idlharness.any.js
new file mode 100644
index 0000000..25298d3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/wasm/jsapi/idlharness.any.js
@@ -0,0 +1,40 @@
+// META: script=/resources/WebIDLParser.js
+// META: script=/resources/idlharness.js
+// META: script=../resources/load_wasm.js
+
+'use strict';
+
+// https://webassembly.github.io/spec/js-api/
+
+promise_test(async () => {
+  const srcs = ['wasm-js-api'];
+  const [wasm] = await Promise.all(
+    srcs.map(i => fetch(`/interfaces/${i}.idl`).then(r => r.text())));
+
+  const idl_array = new IdlArray();
+  idl_array.add_idls(wasm, {
+    // Note the prose requirements in the specification.
+    except: ['CompileError', 'LinkError', 'RuntimeError']
+  });
+
+  // https://github.com/web-platform-tests/wpt/issues/12850
+  idl_array.add_untested_idls('[Exposed=(Window,Worker)] interface ArrayBuffer {};');
+
+  // Ignored errors are surfaced in idlharness.js's test_object below.
+  try {
+    self.memory = new WebAssembly.Memory({initial: 1024});
+  } catch (e) { }
+
+  try {
+    self.mod = await createWasmModule();
+    self.instance = new WebAssembly.Instance(self.mod);
+  } catch (e) { }
+
+  idl_array.add_objects({
+    Memory: ['memory'],
+    Module: ['mod'],
+    Instance: ['instance'],
+  });
+  idl_array.test();
+}, 'wasm-js-api interfaces.');
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/wasm/jsapi/idlharness.any.worker-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/wasm/jsapi/idlharness.any.worker-expected.txt
new file mode 100644
index 0000000..d120c0a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/wasm/jsapi/idlharness.any.worker-expected.txt
@@ -0,0 +1,69 @@
+This is a testharness.js-based test.
+Found 63 tests; 46 PASS, 17 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS wasm-js-api interfaces.
+FAIL WebAssembly namespace: operation validate(BufferSource) assert_true: property should be enumerable expected true got false
+FAIL WebAssembly namespace: operation compile(BufferSource) assert_true: property should be enumerable expected true got false
+FAIL WebAssembly namespace: operation instantiate(BufferSource, object) assert_true: property should be enumerable expected true got false
+FAIL WebAssembly namespace: operation instantiate(Module, object) assert_true: property should be enumerable expected true got false
+PASS Module interface: existence and properties of interface object
+PASS Module interface object length
+PASS Module interface object name
+PASS Module interface: existence and properties of interface prototype object
+PASS Module interface: existence and properties of interface prototype object's "constructor" property
+PASS Module interface: existence and properties of interface prototype object's @@unscopables property
+FAIL Module interface: operation exports(Module) assert_true: property should be enumerable expected true got false
+FAIL Module interface: operation imports(Module) assert_true: property should be enumerable expected true got false
+FAIL Module interface: operation customSections(Module, USVString) assert_true: property should be enumerable expected true got false
+PASS Module must be primary interface of mod
+PASS Stringification of mod
+PASS Module interface: mod must inherit property "exports(Module)" with the proper type
+PASS Module interface: calling exports(Module) on mod with too few arguments must throw TypeError
+PASS Module interface: mod must inherit property "imports(Module)" with the proper type
+PASS Module interface: calling imports(Module) on mod with too few arguments must throw TypeError
+PASS Module interface: mod must inherit property "customSections(Module, USVString)" with the proper type
+PASS Module interface: calling customSections(Module, USVString) on mod with too few arguments must throw TypeError
+PASS Instance interface: existence and properties of interface object
+PASS Instance interface object length
+PASS Instance interface object name
+PASS Instance interface: existence and properties of interface prototype object
+PASS Instance interface: existence and properties of interface prototype object's "constructor" property
+PASS Instance interface: existence and properties of interface prototype object's @@unscopables property
+FAIL Instance interface: attribute exports assert_true: property should be enumerable expected true got false
+PASS Instance must be primary interface of instance
+PASS Stringification of instance
+PASS Instance interface: instance must inherit property "exports" with the proper type
+PASS Memory interface: existence and properties of interface object
+PASS Memory interface object length
+PASS Memory interface object name
+PASS Memory interface: existence and properties of interface prototype object
+PASS Memory interface: existence and properties of interface prototype object's "constructor" property
+PASS Memory interface: existence and properties of interface prototype object's @@unscopables property
+FAIL Memory interface: operation grow(unsigned long) assert_true: property should be enumerable expected true got false
+FAIL Memory interface: attribute buffer assert_true: property should be enumerable expected true got false
+PASS Memory must be primary interface of memory
+PASS Stringification of memory
+PASS Memory interface: memory must inherit property "grow(unsigned long)" with the proper type
+FAIL Memory interface: calling grow(unsigned long) on memory with too few arguments must throw TypeError assert_throws: Called with 0 arguments function "function() {
+            fn.apply(obj, args);
+        }" did not throw
+PASS Memory interface: memory must inherit property "buffer" with the proper type
+PASS Table interface: existence and properties of interface object
+PASS Table interface object length
+PASS Table interface object name
+PASS Table interface: existence and properties of interface prototype object
+PASS Table interface: existence and properties of interface prototype object's "constructor" property
+PASS Table interface: existence and properties of interface prototype object's @@unscopables property
+FAIL Table interface: operation grow(unsigned long) assert_true: property should be enumerable expected true got false
+FAIL Table interface: operation get(unsigned long) assert_true: property should be enumerable expected true got false
+FAIL Table interface: operation set(unsigned long, Function) assert_true: property should be enumerable expected true got false
+FAIL Table interface: attribute length assert_true: property should be enumerable expected true got false
+PASS Global interface: existence and properties of interface object
+PASS Global interface object length
+PASS Global interface object name
+PASS Global interface: existence and properties of interface prototype object
+PASS Global interface: existence and properties of interface prototype object's "constructor" property
+PASS Global interface: existence and properties of interface prototype object's @@unscopables property
+FAIL Global interface: operation valueOf() assert_true: property should be enumerable expected true got false
+FAIL Global interface: attribute value assert_true: property should be enumerable expected true got false
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/wasm/resources/load_wasm.js b/third_party/WebKit/LayoutTests/external/wpt/wasm/resources/load_wasm.js
index 5123246..8316dcfb 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/wasm/resources/load_wasm.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/wasm/resources/load_wasm.js
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 function createWasmModule() {
-    return fetch('incrementer.wasm')
+    return fetch('/wasm/incrementer.wasm')
         .then(response => {
             if (!response.ok) throw new Error(response.statusText);
             return response.arrayBuffer();
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-reuse.html b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-reuse.html
new file mode 100644
index 0000000..dabe323
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-reuse.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>AudioBuffer can be reused between AudioBufferSourceNodes</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+function render_audio_context() {
+  let sampleRate = 44100;
+  let context = new OfflineAudioContext(
+      2, sampleRate *  0.1, sampleRate);
+  let buf = context.createBuffer(1, 0.1 * sampleRate, context.sampleRate);
+  let data = buf.getChannelData(0);
+  data[0] = 0.5;
+  data[1] = 0.25;
+  let b1 = context.createBufferSource();
+  b1.buffer = buf;
+  b1.start();
+  let b2 = context.createBufferSource();
+  b2.buffer = buf;
+  b2.start();
+  let merger = context.createChannelMerger(2);
+  b1.connect(merger, 0, 0);
+  b2.connect(merger, 0, 1);
+  merger.connect(context.destination);
+  return context.startRendering();
+}
+promise_test(function() {
+  return render_audio_context()
+    .then(function(buffer) {
+      assert_equals(buffer.getChannelData(0)[0], 0.5);
+      assert_equals(buffer.getChannelData(1)[0], 0.5);
+      assert_equals(buffer.getChannelData(0)[1], 0.25);
+      assert_equals(buffer.getChannelData(1)[1], 0.25);
+    });
+}, "AudioBuffer can be reused between AudioBufferSourceNodes");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/idlharness.https.window-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/webrtc/idlharness.https.window-expected.txt
index 2730fbb3..794763f8 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/idlharness.https.window-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/idlharness.https.window-expected.txt
@@ -321,7 +321,9 @@
 FAIL RTCDtlsTransport interface: idlTestObjects.dtlsTransport must inherit property "getRemoteCertificates()" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
 FAIL RTCDtlsTransport interface: idlTestObjects.dtlsTransport must inherit property "onstatechange" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
 FAIL RTCDtlsTransport interface: idlTestObjects.dtlsTransport must inherit property "onerror" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCIceTransport interface: existence and properties of interface object assert_throws: interface object didn't throw TypeError when called as a constructor function "function () { [native code] }" did not throw
+FAIL RTCIceTransport interface: existence and properties of interface object assert_throws: interface object didn't throw TypeError when called as a constructor function "function() {
+                new interface_object();
+            }" did not throw
 PASS RTCIceTransport interface object length
 PASS RTCIceTransport interface object name
 PASS RTCIceTransport interface: existence and properties of interface prototype object
diff --git a/third_party/WebKit/LayoutTests/html/dialog/dialog-show-modal-inert-crash.html b/third_party/WebKit/LayoutTests/html/dialog/dialog-show-modal-inert-crash.html
new file mode 100644
index 0000000..6e10a36
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/html/dialog/dialog-show-modal-inert-crash.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<iframe id="frame"></iframe>
+<script>
+  async_test(function(t) {
+    onload = t.step_func(() => {
+      const host = document.createElement("div");
+      frame.appendChild(host);
+      frame.contentDocument.body.innerHTML = "<dialog></dialog>";
+      document.body.offsetTop;
+      const root = host.createShadowRoot();
+      root.innerHTML = "<content>";
+      const dialog = frame.contentDocument.querySelector("dialog");
+      dialog.showModal();
+      t.done();
+    });
+  }, "Dialog.showModal() called when we have a dirty shadow distribution should not crash.");
+</script>
diff --git a/third_party/WebKit/LayoutTests/html/dialog/showmodal-shadow-sibling-frame-crash-expected.txt b/third_party/WebKit/LayoutTests/html/dialog/showmodal-shadow-sibling-frame-crash-expected.txt
new file mode 100644
index 0000000..2b0564d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/html/dialog/showmodal-shadow-sibling-frame-crash-expected.txt
@@ -0,0 +1,4 @@
+CONSOLE WARNING: line 24: document.registerElement is deprecated and will be removed in M73, around March 2019. Please use window.customElements.define instead. See https://www.chromestatus.com/features/4642138092470272 for more details.
+CONSOLE WARNING: line 16: Element.createShadowRoot is deprecated and will be removed in M73, around March 2019. Please use Element.attachShadow instead. See https://www.chromestatus.com/features/4507242028072960 for more details.
+Test for crash crbug.com/804047 Show custom dialog element
+
diff --git a/third_party/WebKit/LayoutTests/html/dialog/showmodal-shadow-sibling-frame-crash.html b/third_party/WebKit/LayoutTests/html/dialog/showmodal-shadow-sibling-frame-crash.html
new file mode 100644
index 0000000..134634a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/html/dialog/showmodal-shadow-sibling-frame-crash.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html>
+<body>
+Test for crash crbug.com/804047 <button>Show custom dialog element</button>
+
+<template id="dialog">
+ <custom-dialog></custom-dialog>
+</template>
+<div></div>
+<iframe></iframe>
+
+<script>
+var customDialogProto = Object.create(HTMLElement.prototype);
+
+customDialogProto.createdCallback = function() {
+  const shadowRoot = this.createShadowRoot();
+  shadowRoot.innerHTML = '<dialog></dialog>';
+};
+
+customDialogProto.show = function() {
+  this.shadowRoot.querySelector('dialog').showModal();
+};
+
+document.registerElement('custom-dialog', { prototype: customDialogProto });
+
+document.body.querySelector('button').addEventListener('click', function() {
+  const template = document.querySelector('#dialog');
+  const custom = document.importNode(template.content, true);
+  const dialog = custom.querySelector('custom-dialog');
+  document.querySelector('div').appendChild(dialog);
+  dialog.show();
+});
+
+if (window.testRunner) {
+  testRunner.dumpAsText();
+
+  let absoluteTop = 0;
+  let absoluteLeft = 0;
+  const element = document.body.querySelector('button');
+  for (let parent = element; parent; parent = parent.offsetParent) {
+    absoluteLeft += parent.offsetLeft;
+    absoluteTop += parent.offsetTop;
+  }
+
+  const x = absoluteLeft + element.offsetWidth / 2;
+  const y = absoluteTop + element.offsetHeight / 2;
+  eventSender.mouseMoveTo(x, y);
+  eventSender.mouseDown();
+  eventSender.mouseUp();
+}
+</script>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/loading/sxg/sxg-prefetch-resource-timing.html b/third_party/WebKit/LayoutTests/http/tests/loading/sxg/sxg-prefetch-resource-timing.html
new file mode 100644
index 0000000..c218c1b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/loading/sxg/sxg-prefetch-resource-timing.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<title>Resource Timing for prefetched SignedHTTPExchange</title>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="./resources/sxg-util.js"></script>
+<body>
+<script>
+function addPrefetch(url) {
+  let link = document.createElement('link');
+  link.rel = 'prefetch';
+  link.href = url;
+  document.body.appendChild(link);
+}
+async function wait_for_performance_entries(url) {
+  let entries = performance.getEntriesByName(url);
+  if (entries.length > 0) {
+    return entries;
+  }
+  return new Promise((resolve) => {
+    new PerformanceObserver((list) => {
+      const entries = list.getEntriesByName(url);
+      if (entries.length > 0) {
+        resolve(entries);
+      }
+    }).observe({ entryTypes: ['resource'] });
+  });
+}
+
+promise_test(async (t) => {
+  const url = 'http://127.0.0.1:8000/loading/sxg/resources/sxg-location.sxg';
+  addPrefetch(url);
+  return wait_for_performance_entries(url).then((entries) => {
+    assert_equals(entries.length, 1, "Should have only one resource timing entry");
+    const e = entries[0];
+    assert_greater_than(e.duration, 0, "duration should be greater than 0");
+    assert_greater_than(e.fetchStart, 0, "fetchStart should be greater than 0");
+    assert_greater_than(e.responseEnd, e.fetchStart,
+                        "responseEnd should be greater than fetchStart");
+  });
+}, 'Resource Timing for prefetched SignedHTTPExchange');
+</script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/block/float/nopaint-after-layer-destruction-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/block/float/nopaint-after-layer-destruction-expected.txt
deleted file mode 100644
index 2d78c09..0000000
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/block/float/nopaint-after-layer-destruction-expected.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x584
-      LayoutBlockFlow {P} at (0,0) size 784x36
-        LayoutText {#text} at (0,0) size 144x18
-          text run at (0,0) width 144: "This is a pixel test for "
-        LayoutInline {I} at (0,0) size 774x36
-          LayoutInline {A} at (0,0) size 353x18 [color=#0000EE]
-            LayoutText {#text} at (143,0) size 353x18
-              text run at (143,0) width 353: "http://bugzilla.opendarwin.org/show_bug.cgi?id=4334"
-          LayoutText {#text} at (495,0) size 774x36
-            text run at (495,0) width 5: " "
-            text run at (499,0) width 275: "REGRESSION: Flickering when css-hover"
-            text run at (0,18) width 277: "should change opacity on floating elements"
-        LayoutText {#text} at (276,18) size 5x18
-          text run at (276,18) width 5: "."
-      LayoutBlockFlow {P} at (0,52) size 784x18
-        LayoutText {#text} at (0,0) size 281x18
-          text run at (0,0) width 281: "There should be a solid green square below."
-      LayoutBlockFlow {HR} at (0,86) size 784x2 [border: (1px inset #EEEEEE)]
-      LayoutBlockFlow (floating) {DIV} at (0,96) size 100x100 [bgcolor=#008000]
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/layout_ng/fast/block/float/nopaint-after-layer-destruction-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/layout_ng/fast/block/float/nopaint-after-layer-destruction-expected.png
new file mode 100644
index 0000000..3d120b3b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/layout_ng/fast/block/float/nopaint-after-layer-destruction-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/idlharness.https.window-expected.txt b/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/idlharness.https.window-expected.txt
index 3f29529e..b53778f 100644
--- a/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/idlharness.https.window-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/idlharness.https.window-expected.txt
@@ -321,7 +321,9 @@
 FAIL RTCDtlsTransport interface: idlTestObjects.dtlsTransport must inherit property "getRemoteCertificates()" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
 FAIL RTCDtlsTransport interface: idlTestObjects.dtlsTransport must inherit property "onstatechange" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
 FAIL RTCDtlsTransport interface: idlTestObjects.dtlsTransport must inherit property "onerror" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL RTCIceTransport interface: existence and properties of interface object assert_throws: interface object didn't throw TypeError when called as a constructor function "function () { [native code] }" did not throw
+FAIL RTCIceTransport interface: existence and properties of interface object assert_throws: interface object didn't throw TypeError when called as a constructor function "function() {
+                new interface_object();
+            }" did not throw
 PASS RTCIceTransport interface object length
 PASS RTCIceTransport interface object name
 PASS RTCIceTransport interface: existence and properties of interface prototype object
diff --git a/third_party/blink/public/BUILD.gn b/third_party/blink/public/BUILD.gn
index d7e0253..4724a9f 100644
--- a/third_party/blink/public/BUILD.gn
+++ b/third_party/blink/public/BUILD.gn
@@ -461,7 +461,6 @@
     "web/web_local_frame_client.h",
     "web/web_meaningful_layout.h",
     "web/web_media_player_action.h",
-    "web/web_media_stream_registry.h",
     "web/web_memory_statistics.h",
     "web/web_menu_item_info.h",
     "web/web_meta_element.h",
diff --git a/third_party/blink/public/platform/modules/push_messaging/web_push_client.h b/third_party/blink/public/platform/modules/push_messaging/web_push_client.h
index ccf5c05e7..539ee80 100644
--- a/third_party/blink/public/platform/modules/push_messaging/web_push_client.h
+++ b/third_party/blink/public/platform/modules/push_messaging/web_push_client.h
@@ -12,16 +12,14 @@
 
 namespace blink {
 
-class WebServiceWorkerRegistration;
 struct WebPushSubscriptionOptions;
 
 class WebPushClient {
  public:
   virtual ~WebPushClient() = default;
 
-  // Ownership of the WebServiceWorkerRegistration is not transferred.
   // Ownership of the callbacks is transferred to the client.
-  virtual void Subscribe(WebServiceWorkerRegistration*,
+  virtual void Subscribe(int64_t service_worker_registration_id,
                          const WebPushSubscriptionOptions&,
                          bool user_gesture,
                          std::unique_ptr<WebPushSubscriptionCallbacks>) = 0;
diff --git a/third_party/blink/public/platform/modules/push_messaging/web_push_provider.h b/third_party/blink/public/platform/modules/push_messaging/web_push_provider.h
index 041402e..da3b8b46 100644
--- a/third_party/blink/public/platform/modules/push_messaging/web_push_provider.h
+++ b/third_party/blink/public/platform/modules/push_messaging/web_push_provider.h
@@ -12,7 +12,6 @@
 
 namespace blink {
 
-class WebServiceWorkerRegistration;
 struct WebPushError;
 struct WebPushSubscriptionOptions;
 
@@ -25,21 +24,18 @@
   virtual ~WebPushProvider() = default;
 
   // Takes ownership of the WebPushSubscriptionCallbacks.
-  // Does not take ownership of the WebServiceWorkerRegistration.
-  virtual void Subscribe(WebServiceWorkerRegistration*,
+  virtual void Subscribe(int64_t service_worker_registration_id,
                          const WebPushSubscriptionOptions&,
                          bool user_gesture,
                          std::unique_ptr<WebPushSubscriptionCallbacks>) = 0;
 
   // Takes ownership of the WebPushSubscriptionCallbacks.
-  // Does not take ownership of the WebServiceWorkerRegistration.
   virtual void GetSubscription(
-      WebServiceWorkerRegistration*,
+      int64_t service_worker_registration_id,
       std::unique_ptr<WebPushSubscriptionCallbacks>) = 0;
 
   // Takes ownership if the WebPushUnsubscribeCallbacks.
-  // Does not take ownership of the WebServiceWorkerRegistration.
-  virtual void Unsubscribe(WebServiceWorkerRegistration*,
+  virtual void Unsubscribe(int64_t service_worker_registration_id,
                            std::unique_ptr<WebPushUnsubscribeCallbacks>) = 0;
 };
 
diff --git a/third_party/blink/public/platform/platform.h b/third_party/blink/public/platform/platform.h
index 2f57bdd1..71666c92 100644
--- a/third_party/blink/public/platform/platform.h
+++ b/third_party/blink/public/platform/platform.h
@@ -167,6 +167,12 @@
   // You should not pass in a Platform object that is not fully instantiated.
   static void SetCurrentPlatformForTesting(Platform*);
 
+  // This sets up a minimally viable implementation of blink::Thread without
+  // changing the current Platform. This is essentially a workaround for the
+  // initialization order in ScopedUnittestsEnvironmentSetup, and nobody else
+  // should use this.
+  static void CreateMainThreadForTesting();
+
   // These are dirty workaround for tests requiring the main thread task runner
   // from a non-main thread. If your test needs base::ScopedTaskEnvironment
   // and a non-main thread may call MainThread()->GetTaskRunner(), call
@@ -428,38 +434,34 @@
 
   // Threads -------------------------------------------------------
 
-  // blink::Thread creation is no longer customizable in Platform.
-  // CreateThread() always creates a new physical thread for Blink.
-  // Platform maintains the thread-local storage containing each blink::Thread
-  // object, so that CurrentThread() could return the correct thread object.
-  //
-  // TODO(yutak): These non-virtual functions should be moved to somewhere
-  // else, because they no longer require embedder's implementation.
+  // Most of threading functionality has moved to blink::Thread. The functions
+  // in Platform are deprecated; use the counterpart in blink::Thread as noted
+  // below.
 
-  // Creates a new thread. This may be called from a non-main thread (e.g.
-  // nested Web workers).
+  // DEPRECATED: Use Thread::CreateThread() instead.
   std::unique_ptr<Thread> CreateThread(const ThreadCreationParams&);
 
-  // Creates a WebAudio-specific thread with the elevated priority. Do NOT use
-  // for any other purpose.
+  // DEPRECATED: Use Thread::CreateWebAudioThread() instead.
   std::unique_ptr<Thread> CreateWebAudioThread();
 
-  // Create and initialize the compositor thread. The thread is saved in
-  // Platform, and will be accessible through CompositorThread().
-  void InitializeCompositorThread();
-
-  // Returns an interface to the current thread.
+  // DEPRECATED: Use Thread::Current() instead.
   Thread* CurrentThread();
 
-  // Returns an interface to the main thread.
+  // DEPRECATED: Use Thread::MainThread() instead.
   Thread* MainThread();
 
-  // Returns an interface to the compositor thread. This can be null if the
-  // renderer was created with threaded rendering disabled.
+  // DEPRECATED: Use Thread::CompositorThread() instead.
   Thread* CompositorThread();
 
+  // The two compositor-related functions below are called by the embedder.
+  // TODO(yutak): Perhaps we should move these to somewhere else?
+
+  // Create and initialize the compositor thread. After this function
+  // completes, you can access CompositorThreadTaskRunner().
+  void CreateAndSetCompositorThread();
+
   // Returns the task runner of the compositor thread. This is available
-  // once InitializeCompositorThread() is called.
+  // once CreateAndSetCompositorThread() is called.
   scoped_refptr<base::SingleThreadTaskRunner> CompositorThreadTaskRunner();
 
   // This is called after the compositor thread is created, so the embedder
@@ -766,28 +768,9 @@
   // runs during Chromium's build step).
   virtual bool IsTakingV8ContextSnapshot() { return false; }
 
- protected:
-  Thread* main_thread_;
-
  private:
-  static void InitializeCommon(Platform* platform);
-
-  void WaitUntilThreadTLSUpdate(Thread*);
-  void UpdateThreadTLS(Thread* thread, base::WaitableEvent* event);
-
-  // Platform owns the main thread in most cases. The pointer value is the same
-  // as main_thread_ if this variable is non-null.
-  //
-  // This variable is null if (and only if) ScopedTestingPlatformSupport<>
-  // overrides the old Platform. In this case, main_thread_ points to the old
-  // Platform's main thread. See testing_platform_support.h for this.
-  std::unique_ptr<Thread> owned_main_thread_;
-
-  std::unique_ptr<Thread> compositor_thread_;
-
-  // We can't use WTF stuff here. Ultimately these should go away (see comments
-  // near CreateThread()), though.
-  base::ThreadLocalStorage::Slot current_thread_slot_;
+  static void InitializeCommon(Platform* platform,
+                               std::unique_ptr<Thread> main_thread);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/public/web/web_media_stream_registry.h b/third_party/blink/public/web/web_media_stream_registry.h
deleted file mode 100644
index d0b7565..0000000
--- a/third_party/blink/public/web/web_media_stream_registry.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2011 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_MEDIA_STREAM_REGISTRY_H_
-#define THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_MEDIA_STREAM_REGISTRY_H_
-
-#include "third_party/blink/public/platform/web_string.h"
-
-namespace blink {
-
-class WebMediaStream;
-class WebURL;
-
-class WebMediaStreamRegistry {
- public:
-  BLINK_EXPORT static WebMediaStream LookupMediaStreamDescriptor(const WebURL&);
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_MEDIA_STREAM_REGISTRY_H_
diff --git a/third_party/blink/renderer/bindings/core/v8/js_based_event_listener.cc b/third_party/blink/renderer/bindings/core/v8/js_based_event_listener.cc
index 789022b..5394a38 100644
--- a/third_party/blink/renderer/bindings/core/v8/js_based_event_listener.cc
+++ b/third_party/blink/renderer/bindings/core/v8/js_based_event_listener.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/bindings/core/v8/js_based_event_listener.h"
 
+#include "third_party/blink/renderer/bindings/core/v8/binding_security.h"
 #include "third_party/blink/renderer/bindings/core/v8/source_location.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
 #include "third_party/blink/renderer/core/dom/document.h"
@@ -73,7 +74,7 @@
     return;
 
   {
-    v8::HandleScope scope(isolate);
+    v8::HandleScope handle_scope(isolate);
 
     // Calling |GetListenerObject()| here may cause compilation of the
     // uncompiled script body in eventHandler's value earlier than standard's
@@ -92,7 +93,7 @@
   if (!script_state_of_listener->ContextIsValid())
     return;
 
-  ScriptState::Scope scope(script_state_of_listener);
+  ScriptState::Scope listener_script_state_scope(script_state_of_listener);
 
   // https://dom.spec.whatwg.org/#firing-events
   // Step 2. of firing events: Let event be the result of creating an event
@@ -101,11 +102,28 @@
   // |js_event|, a V8 wrapper object for |event|, must be created in the
   // relevant realm of the event target. The world must match the event
   // listener's world.
-  v8::Local<v8::Context> v8_context =
+  v8::Local<v8::Context> v8_context_of_event_target =
       ToV8Context(execution_context_of_event_target, GetWorld());
-  if (v8_context.IsEmpty())
+  if (v8_context_of_event_target.IsEmpty())
     return;
-  v8::Local<v8::Value> js_event = ToV8(event, v8_context->Global(), isolate);
+
+  if (v8_context_of_event_target != script_state_of_listener->GetContext()) {
+    // Catch exceptions thrown in the event listener if any and report them to
+    // DevTools console.
+    v8::TryCatch try_catch(isolate);
+    try_catch.SetVerbose(true);
+
+    // Check if the current context, which is set to the listener's relevant
+    // context by creating |listener_script_state_scope|, has access to the
+    // event target's relevant context before creating |js_event|. SecurityError
+    // is thrown if it doesn't have access.
+    if (!BindingSecurity::ShouldAllowAccessToCreationContext(
+            v8_context_of_event_target, event->GetWrapperTypeInfo()))
+      return;
+  }
+
+  v8::Local<v8::Value> js_event =
+      ToV8(event, v8_context_of_event_target->Global(), isolate);
   if (js_event.IsEmpty())
     return;
 
@@ -161,7 +179,7 @@
 
 std::unique_ptr<SourceLocation> JSBasedEventListener::GetSourceLocation(
     EventTarget& target) {
-  v8::HandleScope(GetIsolate());
+  v8::HandleScope handle_scope(GetIsolate());
   v8::Local<v8::Value> effective_function = GetEffectiveFunction(target);
   if (effective_function->IsFunction())
     return SourceLocation::FromFunction(effective_function.As<v8::Function>());
diff --git a/third_party/blink/renderer/bindings/core/v8/js_event_handler.cc b/third_party/blink/renderer/bindings/core/v8/js_event_handler.cc
index 42b029d..16d8f73 100644
--- a/third_party/blink/renderer/bindings/core/v8/js_event_handler.cc
+++ b/third_party/blink/renderer/bindings/core/v8/js_event_handler.cc
@@ -25,6 +25,16 @@
     v8::Local<v8::Function> listener,
     const V8PrivateProperty::Symbol& property) {
   DCHECK(!HasCompiledHandler());
+
+  // https://html.spec.whatwg.org/multipage/webappapis.html#getting-the-current-value-of-the-event-handler
+  // Step 12: Set eventHandler's value to the result of creating a Web IDL
+  // EventHandler callback function object whose object reference is function
+  // and whose callback context is settings object.
+  //
+  // Push |script_state|'s context onto the backup incumbent settings object
+  // stack because appropriate incumbent realm does not always exist when
+  // content attribute gets lazily compiled. This context is the same one of the
+  // relevant realm of |listener| and its event target.
   v8::Context::BackupIncumbentScope backup_incumbent_scope(
       script_state->GetContext());
   event_handler_ = V8EventHandlerNonNull::Create(listener);
diff --git a/third_party/blink/renderer/bindings/scripts/code_generator_v8.py b/third_party/blink/renderer/bindings/scripts/code_generator_v8.py
index 455a76e..9d321f9 100644
--- a/third_party/blink/renderer/bindings/scripts/code_generator_v8.py
+++ b/third_party/blink/renderer/bindings/scripts/code_generator_v8.py
@@ -187,19 +187,19 @@
         # Select appropriate Jinja template and contents function
         if interface.is_callback:
             header_template_filename = 'callback_interface.h.tmpl'
-            cpp_template_filename = 'callback_interface.cpp.tmpl'
+            cpp_template_filename = 'callback_interface.cc.tmpl'
             interface_context = v8_callback_interface.callback_interface_context
         elif interface.is_partial:
             interface_context = v8_interface.interface_context
             header_template_filename = 'partial_interface.h.tmpl'
-            cpp_template_filename = 'partial_interface.cpp.tmpl'
+            cpp_template_filename = 'partial_interface.cc.tmpl'
             interface_name += 'Partial'
             assert component == 'core'
             component = 'modules'
             include_paths = interface_info.get('dependencies_other_component_include_paths')
         else:
             header_template_filename = 'interface.h.tmpl'
-            cpp_template_filename = 'interface.cpp.tmpl'
+            cpp_template_filename = 'interface.cc.tmpl'
             interface_context = v8_interface.interface_context
 
         template_context = interface_context(interface, definitions.interfaces)
@@ -233,7 +233,7 @@
         # pylint: disable=unused-argument
         interfaces_info = self.info_provider.interfaces_info
         header_template = self.jinja_env.get_template('dictionary_v8.h.tmpl')
-        cpp_template = self.jinja_env.get_template('dictionary_v8.cpp.tmpl')
+        cpp_template = self.jinja_env.get_template('dictionary_v8.cc.tmpl')
         interface_info = interfaces_info[dictionary_name]
         template_context = v8_dictionary.dictionary_context(
             dictionary, interfaces_info)
@@ -273,7 +273,7 @@
         dictionary = definitions.dictionaries[definition_name]
         interface_info = interfaces_info[definition_name]
         header_template = self.jinja_env.get_template('dictionary_impl.h.tmpl')
-        cpp_template = self.jinja_env.get_template('dictionary_impl.cpp.tmpl')
+        cpp_template = self.jinja_env.get_template('dictionary_impl.cc.tmpl')
         template_context = v8_dictionary.dictionary_impl_context(
             dictionary, interfaces_info)
         include_paths = interface_info.get('dependencies_include_paths')
@@ -313,7 +313,7 @@
         includes.clear()
         union_type = union_type.resolve_typedefs(self.typedefs)
         header_template = self.jinja_env.get_template('union_container.h.tmpl')
-        cpp_template = self.jinja_env.get_template('union_container.cpp.tmpl')
+        cpp_template = self.jinja_env.get_template('union_container.cc.tmpl')
         template_context = v8_union.container_context(union_type, self.info_provider)
         template_context['header_includes'].append(
             self.info_provider.include_path_for_export)
@@ -365,7 +365,7 @@
     def generate_code_internal(self, callback_function, path):
         self.typedef_resolver.resolve(callback_function, callback_function.name)
         header_template = self.jinja_env.get_template('callback_function.h.tmpl')
-        cpp_template = self.jinja_env.get_template('callback_function.cpp.tmpl')
+        cpp_template = self.jinja_env.get_template('callback_function.cc.tmpl')
         template_context = v8_callback_function.callback_function_context(
             callback_function)
         if not is_testing_target(path):
diff --git a/third_party/blink/renderer/bindings/scripts/generate_v8_context_snapshot_external_references.py b/third_party/blink/renderer/bindings/scripts/generate_v8_context_snapshot_external_references.py
index 5eaa524..c55a0fe 100644
--- a/third_party/blink/renderer/bindings/scripts/generate_v8_context_snapshot_external_references.py
+++ b/third_party/blink/renderer/bindings/scripts/generate_v8_context_snapshot_external_references.py
@@ -29,7 +29,7 @@
     'third_party/blink/renderer/platform/bindings/v8_private_property.h',
     'v8/include/v8.h'])
 
-TEMPLATE_FILE = 'external_reference_table.cpp.tmpl'
+TEMPLATE_FILE = 'external_reference_table.cc.tmpl'
 
 WHITE_LIST_INTERFACES = frozenset([
     'DOMMatrix',  # crbug.com/733481
diff --git a/third_party/blink/renderer/bindings/templates/attributes.cpp.tmpl b/third_party/blink/renderer/bindings/templates/attributes.cc.tmpl
similarity index 98%
rename from third_party/blink/renderer/bindings/templates/attributes.cpp.tmpl
rename to third_party/blink/renderer/bindings/templates/attributes.cc.tmpl
index f569e78a..e211518 100644
--- a/third_party/blink/renderer/bindings/templates/attributes.cpp.tmpl
+++ b/third_party/blink/renderer/bindings/templates/attributes.cc.tmpl
@@ -1,5 +1,5 @@
-{% from 'utilities.cpp.tmpl' import declare_enum_validation_variable, v8_value_to_local_cpp_value %}
-{% from 'methods.cpp.tmpl' import runtime_timer_scope, runtime_timer_scope_disabled_by_default %}
+{% from 'utilities.cc.tmpl' import declare_enum_validation_variable, v8_value_to_local_cpp_value %}
+{% from 'methods.cc.tmpl' import runtime_timer_scope, runtime_timer_scope_disabled_by_default %}
 
 {##############################################################################}
 {% macro attribute_getter(attribute, world_suffix) %}
@@ -514,7 +514,7 @@
 
 {##############################################################################}
 {% macro build_attribute_or_accessor_configuration(attribute, config_type) %}
-{% from 'utilities.cpp.tmpl' import property_location %}
+{% from 'utilities.cc.tmpl' import property_location %}
 {% if attribute.constructor_type %}
   {% set getter_callback = '%s::%sConstructorGetterCallback' % (v8_class_or_partial, attribute.name) %}
   {% set setter_callback = 'nullptr' %}
diff --git a/third_party/blink/renderer/bindings/templates/callback_function.cpp.tmpl b/third_party/blink/renderer/bindings/templates/callback_function.cc.tmpl
similarity index 100%
rename from third_party/blink/renderer/bindings/templates/callback_function.cpp.tmpl
rename to third_party/blink/renderer/bindings/templates/callback_function.cc.tmpl
diff --git a/third_party/blink/renderer/bindings/templates/callback_interface.cpp.tmpl b/third_party/blink/renderer/bindings/templates/callback_interface.cc.tmpl
similarity index 98%
rename from third_party/blink/renderer/bindings/templates/callback_interface.cpp.tmpl
rename to third_party/blink/renderer/bindings/templates/callback_interface.cc.tmpl
index f68d464..2ac0b7b 100644
--- a/third_party/blink/renderer/bindings/templates/callback_interface.cpp.tmpl
+++ b/third_party/blink/renderer/bindings/templates/callback_interface.cc.tmpl
@@ -34,7 +34,7 @@
 #pragma clang diagnostic pop
 #endif
 
-{% from 'constants.cpp.tmpl' import install_constants with context %}
+{% from 'constants.cc.tmpl' import install_constants with context %}
 static void Install{{v8_class}}Template(v8::Isolate* isolate, const DOMWrapperWorld& world, v8::Local<v8::FunctionTemplate> interface_template) {
   // Legacy callback interface must not have a prototype object.
   interface_template->RemovePrototype();
diff --git a/third_party/blink/renderer/bindings/templates/callback_invoke.cc.tmpl b/third_party/blink/renderer/bindings/templates/callback_invoke.cc.tmpl
index 25f514b..97bf37f 100644
--- a/third_party/blink/renderer/bindings/templates/callback_invoke.cc.tmpl
+++ b/third_party/blink/renderer/bindings/templates/callback_invoke.cc.tmpl
@@ -1,4 +1,4 @@
-{% from 'utilities.cpp.tmpl' import declare_enum_validation_variable %}
+{% from 'utilities.cc.tmpl' import declare_enum_validation_variable %}
 
 {# Implements callback interface\'s "call a user object's operation", or
    callback function\'s "invoke" and/or "construct".
diff --git a/third_party/blink/renderer/bindings/templates/constants.cpp.tmpl b/third_party/blink/renderer/bindings/templates/constants.cc.tmpl
similarity index 96%
rename from third_party/blink/renderer/bindings/templates/constants.cpp.tmpl
rename to third_party/blink/renderer/bindings/templates/constants.cc.tmpl
index 1b8d27c..798329d7 100644
--- a/third_party/blink/renderer/bindings/templates/constants.cpp.tmpl
+++ b/third_party/blink/renderer/bindings/templates/constants.cc.tmpl
@@ -1,4 +1,4 @@
-{% from 'methods.cpp.tmpl' import runtime_timer_scope_disabled_by_default %}
+{% from 'methods.cc.tmpl' import runtime_timer_scope_disabled_by_default %}
 
 {##############################################################################}
 {% macro constant_getter_callback(constant) %}
diff --git a/third_party/blink/renderer/bindings/templates/dictionary_impl.cpp.tmpl b/third_party/blink/renderer/bindings/templates/dictionary_impl.cc.tmpl
similarity index 92%
rename from third_party/blink/renderer/bindings/templates/dictionary_impl.cpp.tmpl
rename to third_party/blink/renderer/bindings/templates/dictionary_impl.cc.tmpl
index a08c3ad..546a970 100644
--- a/third_party/blink/renderer/bindings/templates/dictionary_impl.cpp.tmpl
+++ b/third_party/blink/renderer/bindings/templates/dictionary_impl.cc.tmpl
@@ -1,4 +1,4 @@
-{% from 'dictionary_impl_common.cpp.tmpl' import dictionary_setter_impl with context %}
+{% from 'dictionary_impl_common.cc.tmpl' import dictionary_setter_impl with context %}
 {% filter format_blink_cpp_source_code %}
 
 {% include 'copyright_block.txt' %}
diff --git a/third_party/blink/renderer/bindings/templates/dictionary_impl.h.tmpl b/third_party/blink/renderer/bindings/templates/dictionary_impl.h.tmpl
index 8e485d3..8055c54 100644
--- a/third_party/blink/renderer/bindings/templates/dictionary_impl.h.tmpl
+++ b/third_party/blink/renderer/bindings/templates/dictionary_impl.h.tmpl
@@ -1,4 +1,4 @@
-{% from 'dictionary_impl_common.cpp.tmpl' import dictionary_setter_impl with context %}
+{% from 'dictionary_impl_common.cc.tmpl' import dictionary_setter_impl with context %}
 {% filter format_blink_cpp_source_code %}
 
 {% include 'copyright_block.txt' %}
diff --git a/third_party/blink/renderer/bindings/templates/dictionary_impl_common.cpp.tmpl b/third_party/blink/renderer/bindings/templates/dictionary_impl_common.cc.tmpl
similarity index 100%
rename from third_party/blink/renderer/bindings/templates/dictionary_impl_common.cpp.tmpl
rename to third_party/blink/renderer/bindings/templates/dictionary_impl_common.cc.tmpl
diff --git a/third_party/blink/renderer/bindings/templates/dictionary_v8.cpp.tmpl b/third_party/blink/renderer/bindings/templates/dictionary_v8.cc.tmpl
similarity index 97%
rename from third_party/blink/renderer/bindings/templates/dictionary_v8.cpp.tmpl
rename to third_party/blink/renderer/bindings/templates/dictionary_v8.cc.tmpl
index 1573a02..42ef29d 100644
--- a/third_party/blink/renderer/bindings/templates/dictionary_v8.cpp.tmpl
+++ b/third_party/blink/renderer/bindings/templates/dictionary_v8.cc.tmpl
@@ -1,6 +1,6 @@
 {% filter format_blink_cpp_source_code %}
 
-{% from 'utilities.cpp.tmpl' import declare_enum_validation_variable %}
+{% from 'utilities.cc.tmpl' import declare_enum_validation_variable %}
 {% include 'copyright_block.txt' %}
 #include "{{this_include_header_path}}"
 
@@ -22,7 +22,7 @@
 }
 {% endif %}
 
-{% from 'utilities.cpp.tmpl' import v8_value_to_local_cpp_value %}
+{% from 'utilities.cc.tmpl' import v8_value_to_local_cpp_value %}
 void {{v8_class}}::ToImpl(v8::Isolate* isolate, v8::Local<v8::Value> v8Value, {{cpp_class}}& impl, ExceptionState& exceptionState) {
   if (IsUndefinedOrNull(v8Value)) {
     {% if required_member_names %}
diff --git a/third_party/blink/renderer/bindings/templates/external_reference_table.cpp.tmpl b/third_party/blink/renderer/bindings/templates/external_reference_table.cc.tmpl
similarity index 100%
rename from third_party/blink/renderer/bindings/templates/external_reference_table.cpp.tmpl
rename to third_party/blink/renderer/bindings/templates/external_reference_table.cc.tmpl
diff --git a/third_party/blink/renderer/bindings/templates/interface.cpp.tmpl b/third_party/blink/renderer/bindings/templates/interface.cc.tmpl
similarity index 98%
rename from third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
rename to third_party/blink/renderer/bindings/templates/interface.cc.tmpl
index 1d7c57a4..02d1f47 100644
--- a/third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+++ b/third_party/blink/renderer/bindings/templates/interface.cc.tmpl
@@ -1,6 +1,6 @@
-{% extends 'interface_base.cpp.tmpl' %}
+{% extends 'interface_base.cc.tmpl' %}
 
-{% from 'methods.cpp.tmpl' import runtime_timer_scope, runtime_timer_scope_disabled_by_default %}
+{% from 'methods.cc.tmpl' import runtime_timer_scope, runtime_timer_scope_disabled_by_default %}
 
 {##############################################################################}
 {% block indexed_property_getter %}
@@ -70,7 +70,7 @@
 
 {##############################################################################}
 {% block indexed_property_setter %}
-{% from 'utilities.cpp.tmpl' import v8_value_to_local_cpp_value %}
+{% from 'utilities.cc.tmpl' import v8_value_to_local_cpp_value %}
 {% if indexed_property_setter and not indexed_property_setter.is_custom %}
 {% set setter = indexed_property_setter %}
 static void indexedPropertySetter(uint32_t index, v8::Local<v8::Value> v8Value, const v8::PropertyCallbackInfo<v8::Value>& info) {
@@ -427,7 +427,7 @@
 
 {##############################################################################}
 {% block named_property_setter %}
-{% from 'utilities.cpp.tmpl' import v8_value_to_local_cpp_value %}
+{% from 'utilities.cc.tmpl' import v8_value_to_local_cpp_value %}
 {% if named_property_setter and not named_property_setter.is_custom %}
 {% set setter = named_property_setter %}
 static void namedPropertySetter(const AtomicString& name, v8::Local<v8::Value> v8Value, const v8::PropertyCallbackInfo<v8::Value>& info) {
@@ -691,7 +691,7 @@
   {% raw %}
   // |methodName| must be same with {{method.name}} in
   // {{method.name}}OriginSafeMethodGetter{{world_suffix}} defined in
-  // methods.cpp.tmpl
+  // methods.cc.tmpl
   {% endraw %}
   V8PrivateProperty::GetSymbol(info.GetIsolate(), *methodName)
       .Set(v8::Local<v8::Object>::Cast(info.Holder()), v8Value);
@@ -710,7 +710,7 @@
 
 {##############################################################################}
 {% block named_constructor %}
-{% from 'methods.cpp.tmpl' import generate_constructor with context %}
+{% from 'methods.cc.tmpl' import generate_constructor with context %}
 {% if named_constructor %}
 {% set active_scriptwrappable_inheritance =
     'kInheritFromActiveScriptWrappable'
@@ -867,7 +867,7 @@
 
 {##############################################################################}
 {% macro install_origin_safe_method(method, instance_template, prototype_template) %}
-{% from 'utilities.cpp.tmpl' import property_location %}
+{% from 'utilities.cc.tmpl' import property_location %}
 {# TODO(dcheng): Currently, bindings must create a function object for each
    realm as a hack to support the incumbent realm. Clean this up when Blink
    properly supports the incumbent realm. #}
diff --git a/third_party/blink/renderer/bindings/templates/interface_base.cpp.tmpl b/third_party/blink/renderer/bindings/templates/interface_base.cc.tmpl
similarity index 96%
rename from third_party/blink/renderer/bindings/templates/interface_base.cpp.tmpl
rename to third_party/blink/renderer/bindings/templates/interface_base.cc.tmpl
index f78737d..bb5fa16d 100644
--- a/third_party/blink/renderer/bindings/templates/interface_base.cpp.tmpl
+++ b/third_party/blink/renderer/bindings/templates/interface_base.cc.tmpl
@@ -84,7 +84,7 @@
 
 {##############################################################################}
 {# Attributes #}
-{% from 'attributes.cpp.tmpl' import attribute_getter,
+{% from 'attributes.cc.tmpl' import attribute_getter,
        attribute_setter,
        with context %}
 {% for attribute in attributes %}
@@ -99,7 +99,7 @@
 {% endfor %}
 {##############################################################################}
 {# Methods #}
-{% from 'methods.cpp.tmpl' import generate_method, overload_resolution_method,
+{% from 'methods.cc.tmpl' import generate_method, overload_resolution_method,
       origin_safe_method_getter, generate_constructor,
       runtime_determined_length_method, runtime_determined_maxarg_method
       with context %}
@@ -180,13 +180,13 @@
 } // namespace {{cpp_class_or_partial}}V8Internal
 
 {# Constants #}
-{% from 'constants.cpp.tmpl' import constant_getter_callback with context %}
+{% from 'constants.cc.tmpl' import constant_getter_callback with context %}
 {% for constant in constants | has_special_getter %}
 {{constant_getter_callback(constant)}}
 {% endfor %}
 
 {# Attributes #}
-{% from 'attributes.cpp.tmpl' import constructor_getter_callback,
+{% from 'attributes.cc.tmpl' import constructor_getter_callback,
        attribute_getter_callback, attribute_setter_callback with context %}
 {% for attribute in attributes %}
 {% for world_suffix in attribute.world_suffixes %}
@@ -202,7 +202,7 @@
 {% endfor %}
 
 {# Methods #}
-{% from 'methods.cpp.tmpl' import origin_safe_method_getter_callback,
+{% from 'methods.cc.tmpl' import origin_safe_method_getter_callback,
       method_callback with context %}
 {% for method in methods %}
 {% for world_suffix in method.world_suffixes %}
@@ -350,7 +350,7 @@
 {% block visit_dom_wrapper %}{% endblock %}
 {##############################################################################}
 {% block install_attributes %}
-{% from 'attributes.cpp.tmpl' import attribute_configuration with context %}
+{% from 'attributes.cc.tmpl' import attribute_configuration with context %}
 {% if data_attributes %}
 // Suppress warning: global constructors, because AttributeConfiguration is trivial
 // and does not depend on another global objects.
@@ -371,7 +371,7 @@
 {% endblock %}
 {##############################################################################}
 {% block install_accessors %}
-{% from 'attributes.cpp.tmpl' import accessor_configuration with context %}
+{% from 'attributes.cc.tmpl' import accessor_configuration with context %}
 {% if accessors %}
 static const V8DOMConfiguration::AccessorConfiguration {{v8_class}}Accessors[] = {
     {% for accessor in accessors %}
@@ -383,7 +383,7 @@
 {% endblock %}
 {##############################################################################}
 {% block install_methods %}
-{% from 'methods.cpp.tmpl' import method_configuration with context %}
+{% from 'methods.cc.tmpl' import method_configuration with context %}
 {% if methods | has_method_configuration(is_partial) %}
 static const V8DOMConfiguration::MethodConfiguration {{v8_class}}Methods[] = {
     {% for method in methods | has_method_configuration(is_partial) %}
@@ -400,12 +400,12 @@
 {##############################################################################}
 {% block install_dom_template %}
 {% if not is_array_buffer_or_view %}
-{% from 'methods.cpp.tmpl' import install_custom_signature,
+{% from 'methods.cc.tmpl' import install_custom_signature,
         method_configuration with context %}
-{% from 'attributes.cpp.tmpl' import accessor_configuration,
+{% from 'attributes.cc.tmpl' import accessor_configuration,
         attribute_configuration,
         with context %}
-{% from 'constants.cpp.tmpl' import install_constants,
+{% from 'constants.cc.tmpl' import install_constants,
         constant_configuration with context %}
 {% if has_partial_interface or is_partial %}
 void {{v8_class_or_partial}}::install{{v8_class}}Template(
@@ -760,8 +760,8 @@
 {##############################################################################}
 {% block install_runtime_enabled %}
 {% if needs_runtime_enabled_installer %}
-{% from 'attributes.cpp.tmpl' import accessor_configuration, attribute_configuration with context %}
-{% from 'methods.cpp.tmpl' import install_custom_signature with context %}
+{% from 'attributes.cc.tmpl' import accessor_configuration, attribute_configuration with context %}
+{% from 'methods.cc.tmpl' import install_custom_signature with context %}
 {% if not is_partial %}
 void {{v8_class}}::InstallRuntimeEnabledFeatures(
     v8::Isolate* isolate,
@@ -851,11 +851,11 @@
 {% endblock %}
 {##############################################################################}
 {% block origin_trials %}
-{% from 'attributes.cpp.tmpl' import accessor_configuration,
+{% from 'attributes.cc.tmpl' import accessor_configuration,
         attribute_configuration,
         with context %}
-{% from 'constants.cpp.tmpl' import constant_configuration with context %}
-{% from 'methods.cpp.tmpl' import method_configuration with context %}
+{% from 'constants.cc.tmpl' import constant_configuration with context %}
+{% from 'methods.cc.tmpl' import method_configuration with context %}
 {% for feature in optional_features %}
 void {{v8_class_or_partial}}::install{{feature.name}}(v8::Isolate* isolate, const DOMWrapperWorld& world, v8::Local<v8::Object> instance, v8::Local<v8::Object> prototype, v8::Local<v8::Function> interface) {
   {% if feature.attributes or feature.methods %}
@@ -929,9 +929,9 @@
 {% block native_value_traits %}{% endblock %}
 {##############################################################################}
 {% block install_conditional_features %}
-{% from 'attributes.cpp.tmpl' import install_conditional_attributes,
+{% from 'attributes.cc.tmpl' import install_conditional_attributes,
       install_conditional_interface_objects with context %}
-{% from 'methods.cpp.tmpl' import install_conditional_methods with context %}
+{% from 'methods.cc.tmpl' import install_conditional_methods with context %}
 {% if install_conditional_features_func %}
 void {{v8_class_or_partial}}::InstallConditionalFeatures(
     v8::Local<v8::Context> context,
diff --git a/third_party/blink/renderer/bindings/templates/methods.cpp.tmpl b/third_party/blink/renderer/bindings/templates/methods.cc.tmpl
similarity index 98%
rename from third_party/blink/renderer/bindings/templates/methods.cpp.tmpl
rename to third_party/blink/renderer/bindings/templates/methods.cc.tmpl
index b0e5543..98e1038 100644
--- a/third_party/blink/renderer/bindings/templates/methods.cpp.tmpl
+++ b/third_party/blink/renderer/bindings/templates/methods.cc.tmpl
@@ -1,4 +1,4 @@
-{% from 'utilities.cpp.tmpl' import declare_enum_validation_variable, v8_value_to_local_cpp_value %}
+{% from 'utilities.cc.tmpl' import declare_enum_validation_variable, v8_value_to_local_cpp_value %}
 
 {##############################################################################}
 {% macro runtime_timer_scope(counter) %}
@@ -546,7 +546,7 @@
 
   {% raw %}
   // {{method.name}} must be same with |methodName| (=name) in
-  // {{cpp_class}}OriginSafeMethodSetter defined in interface.cpp.tmpl.
+  // {{cpp_class}}OriginSafeMethodSetter defined in interface.cc.tmpl.
   {% endraw %}
   V8PrivateProperty::Symbol propertySymbol =
       V8PrivateProperty::GetSymbol(info.GetIsolate(), "{{method.name}}");
@@ -612,7 +612,7 @@
 
 {##############################################################################}
 {% macro method_configuration(method) %}
-{% from 'utilities.cpp.tmpl' import property_location %}
+{% from 'utilities.cc.tmpl' import property_location %}
 {% set method_callback =
        '%s::%sMethodCallback' % (v8_class_or_partial, method.name) %}
 {% set method_callback_for_main_world =
diff --git a/third_party/blink/renderer/bindings/templates/partial_interface.cpp.tmpl b/third_party/blink/renderer/bindings/templates/partial_interface.cc.tmpl
similarity index 95%
rename from third_party/blink/renderer/bindings/templates/partial_interface.cpp.tmpl
rename to third_party/blink/renderer/bindings/templates/partial_interface.cc.tmpl
index e318f612..91087a0 100644
--- a/third_party/blink/renderer/bindings/templates/partial_interface.cpp.tmpl
+++ b/third_party/blink/renderer/bindings/templates/partial_interface.cc.tmpl
@@ -1,4 +1,4 @@
-{% extends 'interface_base.cpp.tmpl' %}
+{% extends 'interface_base.cc.tmpl' %}
 
 {##############################################################################}
 {% block partial_interface %}
diff --git a/third_party/blink/renderer/bindings/templates/templates.gni b/third_party/blink/renderer/bindings/templates/templates.gni
index b6c270e..585a300 100644
--- a/third_party/blink/renderer/bindings/templates/templates.gni
+++ b/third_party/blink/renderer/bindings/templates/templates.gni
@@ -5,29 +5,29 @@
 # Paths should be absolute so this file can be imported from anywhere.
 code_generator_template_files =
     get_path_info([
-                    "attributes.cpp.tmpl",
-                    "callback_function.cpp.tmpl",
+                    "attributes.cc.tmpl",
+                    "callback_function.cc.tmpl",
                     "callback_function.h.tmpl",
-                    "callback_interface.cpp.tmpl",
+                    "callback_interface.cc.tmpl",
                     "callback_interface.h.tmpl",
                     "callback_invoke.cc.tmpl",
-                    "constants.cpp.tmpl",
+                    "constants.cc.tmpl",
                     "copyright_block.txt",
-                    "dictionary_impl.cpp.tmpl",
+                    "dictionary_impl.cc.tmpl",
                     "dictionary_impl.h.tmpl",
-                    "dictionary_impl_common.cpp.tmpl",
-                    "dictionary_v8.cpp.tmpl",
+                    "dictionary_impl_common.cc.tmpl",
+                    "dictionary_v8.cc.tmpl",
                     "dictionary_v8.h.tmpl",
-                    "external_reference_table.cpp.tmpl",
-                    "interface.cpp.tmpl",
+                    "external_reference_table.cc.tmpl",
+                    "interface.cc.tmpl",
                     "interface.h.tmpl",
-                    "interface_base.cpp.tmpl",
-                    "methods.cpp.tmpl",
-                    "partial_interface.cpp.tmpl",
+                    "interface_base.cc.tmpl",
+                    "methods.cc.tmpl",
+                    "partial_interface.cc.tmpl",
                     "partial_interface.h.tmpl",
-                    "union_container.cpp.tmpl",
+                    "union_container.cc.tmpl",
                     "union_container.h.tmpl",
-                    "utilities.cpp.tmpl",
+                    "utilities.cc.tmpl",
                     "web_agent_api_interface.cc.tmpl",
                     "web_agent_api_interface.h.tmpl",
                   ],
diff --git a/third_party/blink/renderer/bindings/templates/union_container.cpp.tmpl b/third_party/blink/renderer/bindings/templates/union_container.cc.tmpl
similarity index 97%
rename from third_party/blink/renderer/bindings/templates/union_container.cpp.tmpl
rename to third_party/blink/renderer/bindings/templates/union_container.cc.tmpl
index 2b6b64c..884fd43 100644
--- a/third_party/blink/renderer/bindings/templates/union_container.cpp.tmpl
+++ b/third_party/blink/renderer/bindings/templates/union_container.cc.tmpl
@@ -1,5 +1,5 @@
-{% from 'utilities.cpp.tmpl' import declare_enum_validation_variable %}
-{% from 'utilities.cpp.tmpl' import v8_value_to_local_cpp_value %}
+{% from 'utilities.cc.tmpl' import declare_enum_validation_variable %}
+{% from 'utilities.cc.tmpl' import v8_value_to_local_cpp_value %}
 {#############################################################################}
 {% macro assign_and_return_if_hasinstance(member) %}
 {% if member.is_array_buffer_or_view_type %}
diff --git a/third_party/blink/renderer/bindings/templates/utilities.cpp.tmpl b/third_party/blink/renderer/bindings/templates/utilities.cc.tmpl
similarity index 100%
rename from third_party/blink/renderer/bindings/templates/utilities.cpp.tmpl
rename to third_party/blink/renderer/bindings/templates/utilities.cc.tmpl
diff --git a/third_party/blink/renderer/bindings/tests/results/core/array_buffer_or_array_buffer_view_or_dictionary.cc b/third_party/blink/renderer/bindings/tests/results/core/array_buffer_or_array_buffer_view_or_dictionary.cc
index fc0077f..647dca48 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/array_buffer_or_array_buffer_view_or_dictionary.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/array_buffer_or_array_buffer_view_or_dictionary.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/union_container.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/union_container.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/boolean_or_element_sequence.cc b/third_party/blink/renderer/bindings/tests/results/core/boolean_or_element_sequence.cc
index 44b11d81..f710fd4a 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/boolean_or_element_sequence.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/boolean_or_element_sequence.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/union_container.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/union_container.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/boolean_or_string_or_unrestricted_double.cc b/third_party/blink/renderer/bindings/tests/results/core/boolean_or_string_or_unrestricted_double.cc
index 3173af6..59c35a1a 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/boolean_or_string_or_unrestricted_double.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/boolean_or_string_or_unrestricted_double.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/union_container.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/union_container.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/boolean_or_test_callback_interface.cc b/third_party/blink/renderer/bindings/tests/results/core/boolean_or_test_callback_interface.cc
index 74e4ae1..577f00b 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/boolean_or_test_callback_interface.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/boolean_or_test_callback_interface.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/union_container.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/union_container.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/byte_string_or_node_list.cc b/third_party/blink/renderer/bindings/tests/results/core/byte_string_or_node_list.cc
index 1ca3e9119..9a0724ad 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/byte_string_or_node_list.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/byte_string_or_node_list.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/union_container.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/union_container.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/byte_string_sequence_sequence_or_byte_string_byte_string_record.cc b/third_party/blink/renderer/bindings/tests/results/core/byte_string_sequence_sequence_or_byte_string_byte_string_record.cc
index 2827871..2e7d3367 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/byte_string_sequence_sequence_or_byte_string_byte_string_record.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/byte_string_sequence_sequence_or_byte_string_byte_string_record.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/union_container.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/union_container.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/double_or_double_or_null_sequence.cc b/third_party/blink/renderer/bindings/tests/results/core/double_or_double_or_null_sequence.cc
index 216c319..c44d6f1 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/double_or_double_or_null_sequence.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/double_or_double_or_null_sequence.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/union_container.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/union_container.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/double_or_double_sequence.cc b/third_party/blink/renderer/bindings/tests/results/core/double_or_double_sequence.cc
index e0dc677..d1de8e7 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/double_or_double_sequence.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/double_or_double_sequence.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/union_container.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/union_container.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/double_or_long_or_boolean_sequence.cc b/third_party/blink/renderer/bindings/tests/results/core/double_or_long_or_boolean_sequence.cc
index a528934..52f7b601 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/double_or_long_or_boolean_sequence.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/double_or_long_or_boolean_sequence.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/union_container.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/union_container.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/double_or_string.cc b/third_party/blink/renderer/bindings/tests/results/core/double_or_string.cc
index 5ea464c..00fb975 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/double_or_string.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/double_or_string.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/union_container.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/union_container.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/double_or_string_or_double_or_string_sequence.cc b/third_party/blink/renderer/bindings/tests/results/core/double_or_string_or_double_or_string_sequence.cc
index 2298371e..e8ee4fe 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/double_or_string_or_double_or_string_sequence.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/double_or_string_or_double_or_string_sequence.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/union_container.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/union_container.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/element_sequence_or_byte_string_double_or_string_record.cc b/third_party/blink/renderer/bindings/tests/results/core/element_sequence_or_byte_string_double_or_string_record.cc
index 5567c047..f9f251a 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/element_sequence_or_byte_string_double_or_string_record.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/element_sequence_or_byte_string_double_or_string_record.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/union_container.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/union_container.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/float_or_boolean.cc b/third_party/blink/renderer/bindings/tests/results/core/float_or_boolean.cc
index 712688dd..72a0a33d 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/float_or_boolean.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/float_or_boolean.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/union_container.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/union_container.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/long_or_boolean.cc b/third_party/blink/renderer/bindings/tests/results/core/long_or_boolean.cc
index 141dd8ba5..de48707 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/long_or_boolean.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/long_or_boolean.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/union_container.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/union_container.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/long_or_test_dictionary.cc b/third_party/blink/renderer/bindings/tests/results/core/long_or_test_dictionary.cc
index 54a355d..42ff4ebb 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/long_or_test_dictionary.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/long_or_test_dictionary.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/union_container.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/union_container.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/long_sequence_or_event.cc b/third_party/blink/renderer/bindings/tests/results/core/long_sequence_or_event.cc
index 9621a9f..c1df376 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/long_sequence_or_event.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/long_sequence_or_event.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/union_container.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/union_container.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/nested_union_type.cc b/third_party/blink/renderer/bindings/tests/results/core/nested_union_type.cc
index 69585f5..6dc0496 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/nested_union_type.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/nested_union_type.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/union_container.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/union_container.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/node_or_node_list.cc b/third_party/blink/renderer/bindings/tests/results/core/node_or_node_list.cc
index 5357cbe7..8bc3505c 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/node_or_node_list.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/node_or_node_list.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/union_container.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/union_container.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/string_or_array_buffer_or_array_buffer_view.cc b/third_party/blink/renderer/bindings/tests/results/core/string_or_array_buffer_or_array_buffer_view.cc
index 17cec5cd..6c09a1a 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/string_or_array_buffer_or_array_buffer_view.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/string_or_array_buffer_or_array_buffer_view.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/union_container.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/union_container.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/string_or_double.cc b/third_party/blink/renderer/bindings/tests/results/core/string_or_double.cc
index 444b84b..e4b9ebe6 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/string_or_double.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/string_or_double.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/union_container.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/union_container.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/string_or_string_sequence.cc b/third_party/blink/renderer/bindings/tests/results/core/string_or_string_sequence.cc
index d26c42b..b9f839c 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/string_or_string_sequence.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/string_or_string_sequence.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/union_container.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/union_container.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/test_dictionary.cc b/third_party/blink/renderer/bindings/tests/results/core/test_dictionary.cc
index bd498bc..eee33b3 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/test_dictionary.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/test_dictionary.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/dictionary_impl.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/dictionary_impl.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/test_dictionary_derived.cc b/third_party/blink/renderer/bindings/tests/results/core/test_dictionary_derived.cc
index ec382a0d..a034934 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/test_dictionary_derived.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/test_dictionary_derived.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/dictionary_impl.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/dictionary_impl.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/test_enum_or_double.cc b/third_party/blink/renderer/bindings/tests/results/core/test_enum_or_double.cc
index 19a721f..6114972 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/test_enum_or_double.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/test_enum_or_double.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/union_container.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/union_container.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/test_enum_or_test_enum_or_null_sequence.cc b/third_party/blink/renderer/bindings/tests/results/core/test_enum_or_test_enum_or_null_sequence.cc
index 7ce2917f..7a4f425 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/test_enum_or_test_enum_or_null_sequence.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/test_enum_or_test_enum_or_null_sequence.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/union_container.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/union_container.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/test_enum_or_test_enum_sequence.cc b/third_party/blink/renderer/bindings/tests/results/core/test_enum_or_test_enum_sequence.cc
index a96c8ca3..be1c3bc 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/test_enum_or_test_enum_sequence.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/test_enum_or_test_enum_sequence.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/union_container.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/union_container.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/test_interface_2_or_uint8_array.cc b/third_party/blink/renderer/bindings/tests/results/core/test_interface_2_or_uint8_array.cc
index 0a44d2c..1f58f39 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/test_interface_2_or_uint8_array.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/test_interface_2_or_uint8_array.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/union_container.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/union_container.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/test_interface_event_init.cc b/third_party/blink/renderer/bindings/tests/results/core/test_interface_event_init.cc
index c5927f7..bdf5287 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/test_interface_event_init.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/test_interface_event_init.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/dictionary_impl.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/dictionary_impl.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/test_interface_or_long.cc b/third_party/blink/renderer/bindings/tests/results/core/test_interface_or_long.cc
index 72ddd7b..b4b39d1a 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/test_interface_or_long.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/test_interface_or_long.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/union_container.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/union_container.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/test_interface_or_test_interface_empty.cc b/third_party/blink/renderer/bindings/tests/results/core/test_interface_or_test_interface_empty.cc
index b4f738a..a5b9aee 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/test_interface_or_test_interface_empty.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/test_interface_or_test_interface_empty.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/union_container.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/union_container.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/test_permissive_dictionary.cc b/third_party/blink/renderer/bindings/tests/results/core/test_permissive_dictionary.cc
index 339d400..29d75af 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/test_permissive_dictionary.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/test_permissive_dictionary.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/dictionary_impl.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/dictionary_impl.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/unrestricted_double_or_string.cc b/third_party/blink/renderer/bindings/tests/results/core/unrestricted_double_or_string.cc
index ac0d42a..ee89235 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/unrestricted_double_or_string.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/unrestricted_double_or_string.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/union_container.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/union_container.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/unsigned_long_long_or_boolean_or_test_callback_interface.cc b/third_party/blink/renderer/bindings/tests/results/core/unsigned_long_long_or_boolean_or_test_callback_interface.cc
index 3c5a2d7..2ef955a 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/unsigned_long_long_or_boolean_or_test_callback_interface.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/unsigned_long_long_or_boolean_or_test_callback_interface.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/union_container.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/union_container.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_any_callback_function_optional_any_arg.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_any_callback_function_optional_any_arg.cc
index 9c94e586..81378ef 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_any_callback_function_optional_any_arg.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_any_callback_function_optional_any_arg.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/callback_function.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/callback_function.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_any_callback_function_variadic_any_args.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_any_callback_function_variadic_any_args.cc
index d44c397d..c44f741 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_any_callback_function_variadic_any_args.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_any_callback_function_variadic_any_args.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/callback_function.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/callback_function.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_array_buffer.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_array_buffer.cc
index ac29a395..2eb4e12 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_array_buffer.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_array_buffer.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_array_buffer_view.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_array_buffer_view.cc
index 6ee1342..2be1008 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_array_buffer_view.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_array_buffer_view.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_data_view.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_data_view.cc
index 9be5a1e3..6d9d76d 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_data_view.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_data_view.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_long_callback_function.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_long_callback_function.cc
index 194975f..f42b1a0 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_long_callback_function.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_long_callback_function.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/callback_function.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/callback_function.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_string_sequence_callback_function_long_sequence_arg.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_string_sequence_callback_function_long_sequence_arg.cc
index 02fabbb..ebe09c58 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_string_sequence_callback_function_long_sequence_arg.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_string_sequence_callback_function_long_sequence_arg.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/callback_function.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/callback_function.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_svg_test_interface.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_svg_test_interface.cc
index ddc78e0..5a9aaa9 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_svg_test_interface.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_svg_test_interface.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_attributes.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_attributes.cc
index 68079506..859d0cc 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_attributes.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_attributes.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_callback_functions.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_callback_functions.cc
index 915b9ea..6f43037 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_callback_functions.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_callback_functions.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_callback_interface.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_callback_interface.cc
index e078d5d..9dcb6a4 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_callback_interface.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_callback_interface.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/callback_interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/callback_interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_constants.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_constants.cc
index f686d4b82..4fbad7e 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_constants.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_constants.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_dictionary.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_dictionary.cc
index d605d0a..6394187d 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_dictionary.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_dictionary.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/dictionary_v8.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/dictionary_v8.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_dictionary_derived.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_dictionary_derived.cc
index c2ad6910..2adc1be 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_dictionary_derived.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_dictionary_derived.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/dictionary_v8.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/dictionary_v8.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_integer_indexed.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_integer_indexed.cc
index 28d9d4e..62d09050 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_integer_indexed.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_integer_indexed.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_integer_indexed_global.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_integer_indexed_global.cc
index b8d720e..dfa883f 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_integer_indexed_global.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_integer_indexed_global.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_integer_indexed_primary_global.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_integer_indexed_primary_global.cc
index 8ee0752..708b062 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_integer_indexed_primary_global.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_integer_indexed_primary_global.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface.cc
index fe533d8d..ea83e55 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_2.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_2.cc
index 9f720f0..d06ad87c 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_2.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_2.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_3.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_3.cc
index a8f61da..5a15cce 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_3.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_3.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_check_security.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_check_security.cc
index ecaa590..bd5a7fbd 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_check_security.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_check_security.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
@@ -201,7 +201,7 @@
   }
 
   // {{method.name}} must be same with |methodName| (=name) in
-  // {{cpp_class}}OriginSafeMethodSetter defined in interface.cpp.tmpl.
+  // {{cpp_class}}OriginSafeMethodSetter defined in interface.cc.tmpl.
   V8PrivateProperty::Symbol propertySymbol =
       V8PrivateProperty::GetSymbol(info.GetIsolate(), "doNotCheckSecurityVoidMethod");
   v8::Local<v8::Object> holder = v8::Local<v8::Object>::Cast(info.Holder());
@@ -233,7 +233,7 @@
   }
 
   // {{method.name}} must be same with |methodName| (=name) in
-  // {{cpp_class}}OriginSafeMethodSetter defined in interface.cpp.tmpl.
+  // {{cpp_class}}OriginSafeMethodSetter defined in interface.cc.tmpl.
   V8PrivateProperty::Symbol propertySymbol =
       V8PrivateProperty::GetSymbol(info.GetIsolate(), "doNotCheckSecurityPerWorldBindingsVoidMethod");
   v8::Local<v8::Object> holder = v8::Local<v8::Object>::Cast(info.Holder());
@@ -265,7 +265,7 @@
   }
 
   // {{method.name}} must be same with |methodName| (=name) in
-  // {{cpp_class}}OriginSafeMethodSetter defined in interface.cpp.tmpl.
+  // {{cpp_class}}OriginSafeMethodSetter defined in interface.cc.tmpl.
   V8PrivateProperty::Symbol propertySymbol =
       V8PrivateProperty::GetSymbol(info.GetIsolate(), "doNotCheckSecurityPerWorldBindingsVoidMethod");
   v8::Local<v8::Object> holder = v8::Local<v8::Object>::Cast(info.Holder());
@@ -297,7 +297,7 @@
   }
 
   // {{method.name}} must be same with |methodName| (=name) in
-  // {{cpp_class}}OriginSafeMethodSetter defined in interface.cpp.tmpl.
+  // {{cpp_class}}OriginSafeMethodSetter defined in interface.cc.tmpl.
   V8PrivateProperty::Symbol propertySymbol =
       V8PrivateProperty::GetSymbol(info.GetIsolate(), "doNotCheckSecurityUnforgeableVoidMethod");
   v8::Local<v8::Object> holder = v8::Local<v8::Object>::Cast(info.Holder());
@@ -424,7 +424,7 @@
   }
 
   // {{method.name}} must be same with |methodName| (=name) in
-  // {{cpp_class}}OriginSafeMethodSetter defined in interface.cpp.tmpl.
+  // {{cpp_class}}OriginSafeMethodSetter defined in interface.cc.tmpl.
   V8PrivateProperty::Symbol propertySymbol =
       V8PrivateProperty::GetSymbol(info.GetIsolate(), "doNotCheckSecurityVoidOverloadMethod");
   v8::Local<v8::Object> holder = v8::Local<v8::Object>::Cast(info.Holder());
@@ -466,7 +466,7 @@
 
   // |methodName| must be same with {{method.name}} in
   // {{method.name}}OriginSafeMethodGetter{{world_suffix}} defined in
-  // methods.cpp.tmpl
+  // methods.cc.tmpl
   V8PrivateProperty::GetSymbol(info.GetIsolate(), *methodName)
       .Set(v8::Local<v8::Object>::Cast(info.Holder()), v8Value);
 }
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_conditional_secure_context.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_conditional_secure_context.cc
index dfe293a..03f27263 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_conditional_secure_context.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_conditional_secure_context.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor.cc
index 1250cf8..e2007e9 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor_2.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor_2.cc
index 04b79a2..beaba95c 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor_2.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor_2.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor_3.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor_3.cc
index 0e30e15..98f3a9a2 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor_3.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor_3.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor_4.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor_4.cc
index 1340fb7b..22bb602 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor_4.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_constructor_4.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_custom_constructor.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_custom_constructor.cc
index b014630..82b9b143 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_custom_constructor.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_custom_constructor.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_document.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_document.cc
index 2903f9c..1dec31c 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_document.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_document.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_empty.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_empty.cc
index 51e9c8f..85fb95a9 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_empty.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_empty.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_event_init.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_event_init.cc
index f1a9a10..34df4fc 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_event_init.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_event_init.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/dictionary_v8.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/dictionary_v8.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_event_init_constructor.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_event_init_constructor.cc
index d99e4b0..47c9731 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_event_init_constructor.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_event_init_constructor.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_event_target.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_event_target.cc
index 80d4390..c1c536ff 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_event_target.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_event_target.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_named_constructor.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_named_constructor.cc
index f966d7b7..ab22b84 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_named_constructor.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_named_constructor.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_named_constructor_2.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_named_constructor_2.cc
index 998dd91..0184da4 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_named_constructor_2.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_named_constructor_2.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_node.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_node.cc
index 37b2d68..35a89e8c 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_node.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_node.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_origin_trial_enabled.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_origin_trial_enabled.cc
index 63cb40e..03d37bc3 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_origin_trial_enabled.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_origin_trial_enabled.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_secure_context.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_secure_context.cc
index 0f9910b..3d6b1eaf 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_secure_context.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_secure_context.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_legacy_callback_interface.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_legacy_callback_interface.cc
index eb3b1c43..d2aa85e 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_legacy_callback_interface.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_legacy_callback_interface.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/callback_interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/callback_interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_node.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_node.cc
index 27388218..89b20df 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_node.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_node.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.cc
index 548edfe..fce89aa 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_permissive_dictionary.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_permissive_dictionary.cc
index 508bcc8..d41bebf 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_permissive_dictionary.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_permissive_dictionary.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/dictionary_v8.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/dictionary_v8.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_special_operations.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_special_operations.cc
index 916de0ff..36216416 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_special_operations.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_special_operations.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_special_operations_not_enumerable.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_special_operations_not_enumerable.cc
index ea7e8d1a..9275f5f 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_special_operations_not_enumerable.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_special_operations_not_enumerable.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_typedefs.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_typedefs.cc
index d5a59bd..d3b2636 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_typedefs.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_typedefs.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_variadic_constructor_arguments.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_variadic_constructor_arguments.cc
index 27f875a..123d80c 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_variadic_constructor_arguments.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_variadic_constructor_arguments.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_treat_non_object_as_null_boolean_function.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_treat_non_object_as_null_boolean_function.cc
index 7868a24..3f745af0 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_treat_non_object_as_null_boolean_function.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_treat_non_object_as_null_boolean_function.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/callback_function.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/callback_function.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_treat_non_object_as_null_void_function.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_treat_non_object_as_null_void_function.cc
index b6d59ba..ae23a87 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_treat_non_object_as_null_void_function.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_treat_non_object_as_null_void_function.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/callback_function.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/callback_function.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_uint8_clamped_array.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_uint8_clamped_array.cc
index a9cf170..f838ab1 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_uint8_clamped_array.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_uint8_clamped_array.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function.cc
index 4604b860..8e9b8564 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/callback_function.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/callback_function.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function_dictionary_arg.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function_dictionary_arg.cc
index f11cbda..d6ea6737 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function_dictionary_arg.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function_dictionary_arg.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/callback_function.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/callback_function.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function_enum_arg.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function_enum_arg.cc
index 42e1729..eae3aba1 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function_enum_arg.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function_enum_arg.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/callback_function.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/callback_function.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function_interface_arg.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function_interface_arg.cc
index 3e66d4f..e13d389 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function_interface_arg.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function_interface_arg.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/callback_function.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/callback_function.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function_test_interface_sequence_arg.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function_test_interface_sequence_arg.cc
index 2b970bf..3e25936 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function_test_interface_sequence_arg.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function_test_interface_sequence_arg.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/callback_function.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/callback_function.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function_typedef.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function_typedef.cc
index 9952e59..8d05ea4 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function_typedef.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_void_callback_function_typedef.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/callback_function.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/callback_function.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/xml_http_request_or_string.cc b/third_party/blink/renderer/bindings/tests/results/core/xml_http_request_or_string.cc
index 914717ff..82d67c8 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/xml_http_request_or_string.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/xml_http_request_or_string.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/union_container.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/union_container.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/modules/boolean_or_string.cc b/third_party/blink/renderer/bindings/tests/results/modules/boolean_or_string.cc
index a4b9035..595e5f1 100644
--- a/third_party/blink/renderer/bindings/tests/results/modules/boolean_or_string.cc
+++ b/third_party/blink/renderer/bindings/tests/results/modules/boolean_or_string.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/union_container.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/union_container.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/modules/v8_test_inherited_legacy_unenumerable_named_properties.cc b/third_party/blink/renderer/bindings/tests/results/modules/v8_test_inherited_legacy_unenumerable_named_properties.cc
index 0ce7006c..9bb6443 100644
--- a/third_party/blink/renderer/bindings/tests/results/modules/v8_test_inherited_legacy_unenumerable_named_properties.cc
+++ b/third_party/blink/renderer/bindings/tests/results/modules/v8_test_inherited_legacy_unenumerable_named_properties.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/modules/v8_test_interface_2_partial.cc b/third_party/blink/renderer/bindings/tests/results/modules/v8_test_interface_2_partial.cc
index c11af8c..5d4d625d 100644
--- a/third_party/blink/renderer/bindings/tests/results/modules/v8_test_interface_2_partial.cc
+++ b/third_party/blink/renderer/bindings/tests/results/modules/v8_test_interface_2_partial.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/partial_interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/partial_interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/modules/v8_test_interface_5.cc b/third_party/blink/renderer/bindings/tests/results/modules/v8_test_interface_5.cc
index 4c80b44..d7660ff 100644
--- a/third_party/blink/renderer/bindings/tests/results/modules/v8_test_interface_5.cc
+++ b/third_party/blink/renderer/bindings/tests/results/modules/v8_test_interface_5.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/modules/v8_test_interface_partial.cc b/third_party/blink/renderer/bindings/tests/results/modules/v8_test_interface_partial.cc
index 239a538e..8492843 100644
--- a/third_party/blink/renderer/bindings/tests/results/modules/v8_test_interface_partial.cc
+++ b/third_party/blink/renderer/bindings/tests/results/modules/v8_test_interface_partial.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/partial_interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/partial_interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/modules/v8_test_not_enumerable_named_getter.cc b/third_party/blink/renderer/bindings/tests/results/modules/v8_test_not_enumerable_named_getter.cc
index 58fe85e8..e37234d 100644
--- a/third_party/blink/renderer/bindings/tests/results/modules/v8_test_not_enumerable_named_getter.cc
+++ b/third_party/blink/renderer/bindings/tests/results/modules/v8_test_not_enumerable_named_getter.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/modules/v8_test_sub_object.cc b/third_party/blink/renderer/bindings/tests/results/modules/v8_test_sub_object.cc
index 95e53da..54e12d7 100644
--- a/third_party/blink/renderer/bindings/tests/results/modules/v8_test_sub_object.cc
+++ b/third_party/blink/renderer/bindings/tests/results/modules/v8_test_sub_object.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/interface.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/bindings/tests/results/modules/v8_void_callback_function_modules.cc b/third_party/blink/renderer/bindings/tests/results/modules/v8_void_callback_function_modules.cc
index 64078dd..f0e35e9 100644
--- a/third_party/blink/renderer/bindings/tests/results/modules/v8_void_callback_function_modules.cc
+++ b/third_party/blink/renderer/bindings/tests/results/modules/v8_void_callback_function_modules.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This file has been auto-generated from the Jinja2 template
-// third_party/blink/renderer/bindings/templates/callback_function.cpp.tmpl
+// third_party/blink/renderer/bindings/templates/callback_function.cc.tmpl
 // by the script code_generator_v8.py.
 // DO NOT MODIFY!
 
diff --git a/third_party/blink/renderer/core/css/css_primitive_value.h b/third_party/blink/renderer/core/css/css_primitive_value.h
index a4b4f3a3..a00ce66f 100644
--- a/third_party/blink/renderer/core/css/css_primitive_value.h
+++ b/third_party/blink/renderer/core/css/css_primitive_value.h
@@ -297,7 +297,7 @@
   template <typename T>
   operator T*();  // compile-time guard
 
-  // Code generated by CSSPrimitiveValueUnitTrie.cpp.tmpl
+  // Code generated by css_primitive_value_unit_trie.cc.tmpl
   static UnitType StringToUnitType(const LChar*, unsigned length);
   static UnitType StringToUnitType(const UChar*, unsigned length);
 
diff --git a/third_party/blink/renderer/core/css/css_syntax_descriptor.h b/third_party/blink/renderer/core/css/css_syntax_descriptor.h
index ec88ced..7b3642a 100644
--- a/third_party/blink/renderer/core/css/css_syntax_descriptor.h
+++ b/third_party/blink/renderer/core/css/css_syntax_descriptor.h
@@ -45,6 +45,10 @@
   }
 
   bool IsRepeatable() const { return repeat_ != CSSSyntaxRepeat::kNone; }
+  char Separator() const {
+    DCHECK(IsRepeatable());
+    return repeat_ == CSSSyntaxRepeat::kSpaceSeparated ? ' ' : ',';
+  }
 
   bool CanTake(const CSSStyleValue&) const;
 
diff --git a/third_party/blink/renderer/core/css/cssom/style_property_map.cc b/third_party/blink/renderer/core/css/cssom/style_property_map.cc
index adc08f0..111f9fb 100644
--- a/third_party/blink/renderer/core/css/cssom/style_property_map.cc
+++ b/third_party/blink/renderer/core/css/cssom/style_property_map.cc
@@ -22,6 +22,21 @@
 
 namespace {
 
+bool IsListValuedProperty(const CSSProperty& property,
+                          const PropertyRegistration* registration) {
+  if (property.IsRepeated())
+    return true;
+  // TODO(andruud): The concept of "list-valued properties" doesn't fully work
+  // in all cases. See https://github.com/w3c/css-houdini-drafts/issues/823
+  // For now we only consider a custom property list-valued if it has a single
+  // syntax component that is repeatable (e.g. <length>+).
+  if (property.IDEquals(CSSPropertyVariable) && registration) {
+    const auto& components = registration->Syntax().Components();
+    return components.size() == 1 && components[0].IsRepeatable();
+  }
+  return false;
+}
+
 CSSValueList* CssValueListForPropertyID(CSSPropertyID property_id) {
   DCHECK(CSSProperty::Get(property_id).IsRepeated());
   char separator = CSSProperty::Get(property_id).RepetitionSeparator();
@@ -38,6 +53,44 @@
   }
 }
 
+const CSSVariableReferenceValue* CreateVariableReferenceValue(
+    const String& value,
+    const CSSParserContext& context) {
+  CSSTokenizer tokenizer(value);
+  const auto tokens = tokenizer.TokenizeToEOF();
+  CSSParserTokenRange range(tokens);
+  scoped_refptr<CSSVariableData> variable_data = CSSVariableData::Create(
+      range, false, false, context.BaseURL(), context.Charset());
+  return CSSVariableReferenceValue::Create(variable_data, context);
+}
+
+const CSSVariableReferenceValue* CreateVariableReferenceValue(
+    const CSSProperty& property,
+    const AtomicString& custom_property_name,
+    const PropertyRegistration& registration,
+    const CSSStyleValueVector& values,
+    const CSSParserContext& context) {
+  DCHECK(IsListValuedProperty(property, &registration));
+  DCHECK_EQ(registration.Syntax().Components().size(), 1U);
+
+  char separator = registration.Syntax().Components()[0].Separator();
+
+  StringBuilder builder;
+
+  for (const auto& value : values) {
+    if (!CSSOMTypes::PropertyCanTake(property.PropertyID(),
+                                     custom_property_name, &registration,
+                                     *value)) {
+      return nullptr;
+    }
+    if (!builder.IsEmpty())
+      builder.Append(separator);
+    builder.Append(value->toString());
+  }
+
+  return CreateVariableReferenceValue(builder.ToString(), context);
+}
+
 const CSSValue* StyleValueToCSSValue(
     const CSSProperty& property,
     const AtomicString& custom_property_name,
@@ -69,13 +122,8 @@
     case CSSPropertyVariable:
       if (registration &&
           style_value.GetType() != CSSStyleValue::kUnparsedType) {
-        CSSTokenizer tokenizer(style_value.toString());
-        const auto tokens = tokenizer.TokenizeToEOF();
-        CSSParserTokenRange range(tokens);
         CSSParserContext* context = CSSParserContext::Create(execution_context);
-        scoped_refptr<CSSVariableData> variable_data = CSSVariableData::Create(
-            range, false, false, context->BaseURL(), context->Charset());
-        return CSSVariableReferenceValue::Create(variable_data, *context);
+        return CreateVariableReferenceValue(style_value.toString(), *context);
       }
       break;
     case CSSPropertyBorderBottomLeftRadius:
@@ -207,7 +255,7 @@
     const PropertyRegistration* registration,
     const CSSStyleValueOrString& value,
     const ExecutionContext& execution_context) {
-  DCHECK(!property.IsRepeated());
+  DCHECK(!IsListValuedProperty(property, registration));
   DCHECK_EQ(property.IDEquals(CSSPropertyVariable),
             !custom_property_name.IsNull());
 
@@ -233,9 +281,10 @@
 const CSSValue* CoerceStyleValuesOrStrings(
     const CSSProperty& property,
     const AtomicString& custom_property_name,
+    const PropertyRegistration* registration,
     const HeapVector<CSSStyleValueOrString>& values,
     const ExecutionContext& execution_context) {
-  DCHECK(property.IsRepeated());
+  DCHECK(IsListValuedProperty(property, registration));
   DCHECK_EQ(property.IDEquals(CSSPropertyVariable),
             !custom_property_name.IsNull());
   if (values.IsEmpty())
@@ -243,11 +292,18 @@
 
   CSSStyleValueVector style_values =
       StyleValueFactory::CoerceStyleValuesOrStrings(
-          property, custom_property_name, nullptr, values, execution_context);
+          property, custom_property_name, registration, values,
+          execution_context);
 
   if (style_values.IsEmpty())
     return nullptr;
 
+  if (property.IDEquals(CSSPropertyVariable) && registration) {
+    CSSParserContext* context = CSSParserContext::Create(execution_context);
+    return CreateVariableReferenceValue(property, custom_property_name,
+                                        *registration, style_values, *context);
+  }
+
   CSSValueList* result = CssValueListForPropertyID(property.PropertyID());
   for (const auto& style_value : style_values) {
     const CSSValue* css_value =
@@ -317,9 +373,10 @@
   }
 
   const CSSValue* result = nullptr;
-  if (property.IsRepeated()) {
-    result = CoerceStyleValuesOrStrings(property, custom_property_name, values,
-                                        *execution_context);
+  if (IsListValuedProperty(property, registration)) {
+    result =
+        CoerceStyleValuesOrStrings(property, custom_property_name, registration,
+                                   values, *execution_context);
   } else if (values.size() == 1U) {
     result =
         CoerceStyleValueOrString(property, custom_property_name, registration,
@@ -351,13 +408,55 @@
     return;
   }
 
+  const CSSProperty& property = CSSProperty::Get(property_id);
+
   if (property_id == CSSPropertyVariable) {
+    AtomicString custom_property_name(property_name);
+
+    const PropertyRegistration* registration =
+        PropertyRegistration::From(execution_context, custom_property_name);
+
+    if (registration && IsListValuedProperty(property, registration)) {
+      CSSStyleValueVector style_values;
+
+      // Add existing CSSStyleValues:
+      if (const CSSValue* css_value =
+              GetCustomProperty(*execution_context, custom_property_name)) {
+        DCHECK(css_value->IsValueList());
+        style_values = StyleValueFactory::CssValueToStyleValueVector(
+            property_id, custom_property_name, *css_value);
+      }
+
+      // Append incoming CSSStyleValues:
+      CSSStyleValueVector incoming_style_values =
+          StyleValueFactory::CoerceStyleValuesOrStrings(
+              property, custom_property_name, registration, values,
+              *execution_context);
+
+      const CSSValue* result = nullptr;
+
+      if (!incoming_style_values.IsEmpty()) {
+        style_values.AppendVector(incoming_style_values);
+        CSSParserContext* context =
+            CSSParserContext::Create(*execution_context);
+        result =
+            CreateVariableReferenceValue(property, custom_property_name,
+                                         *registration, style_values, *context);
+      }
+
+      if (!result) {
+        exception_state.ThrowTypeError("Invalid type for property");
+        return;
+      }
+
+      SetCustomProperty(custom_property_name, *result);
+      return;
+    }
     exception_state.ThrowTypeError(
         "Appending to custom properties is not supported");
     return;
   }
 
-  const CSSProperty& property = CSSProperty::Get(property_id);
   if (!property.IsRepeated()) {
     exception_state.ThrowTypeError("Property does not support multiple values");
     return;
@@ -371,10 +470,8 @@
     current_value = CssValueListForPropertyID(property_id);
   }
 
-  // TODO(andruud): Don't pass g_null_atom as custom property name
-  // once appending to custom properties is supported.
   const CSSValue* result = CoerceStyleValuesOrStrings(
-      property, g_null_atom, values, *execution_context);
+      property, g_null_atom, nullptr, values, *execution_context);
   if (!result || !result->IsValueList()) {
     exception_state.ThrowTypeError("Invalid type for property");
     return;
diff --git a/third_party/blink/renderer/core/css/cssom/style_property_map_read_only.cc b/third_party/blink/renderer/core/css/cssom/style_property_map_read_only.cc
index 2a8d480..fa822e5 100644
--- a/third_party/blink/renderer/core/css/cssom/style_property_map_read_only.cc
+++ b/third_party/blink/renderer/core/css/cssom/style_property_map_read_only.cc
@@ -133,6 +133,18 @@
   return !getAll(execution_context, property_name, exception_state).IsEmpty();
 }
 
+const CSSValue* StylePropertyMapReadOnly::GetCustomProperty(
+    const ExecutionContext& execution_context,
+    const AtomicString& property_name) {
+  const CSSValue* value = GetCustomProperty(property_name);
+
+  const auto* document = DynamicTo<Document>(execution_context);
+  if (!document)
+    return value;
+
+  return PropertyRegistry::ParseIfRegistered(*document, property_name, value);
+}
+
 StylePropertyMapReadOnly::IterationSource*
 StylePropertyMapReadOnly::StartIteration(ScriptState* script_state,
                                          ExceptionState&) {
@@ -176,16 +188,4 @@
                                           serialization);
 }
 
-const CSSValue* StylePropertyMapReadOnly::GetCustomProperty(
-    const ExecutionContext& execution_context,
-    const AtomicString& property_name) {
-  const CSSValue* value = GetCustomProperty(property_name);
-
-  const auto* document = DynamicTo<Document>(execution_context);
-  if (!document)
-    return value;
-
-  return PropertyRegistry::ParseIfRegistered(*document, property_name, value);
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/cssom/style_property_map_read_only.h b/third_party/blink/renderer/core/css/cssom/style_property_map_read_only.h
index 49415543..acf6c42 100644
--- a/third_party/blink/renderer/core/css/cssom/style_property_map_read_only.h
+++ b/third_party/blink/renderer/core/css/cssom/style_property_map_read_only.h
@@ -50,12 +50,13 @@
 
   virtual String SerializationForShorthand(const CSSProperty&) = 0;
 
+  const CSSValue* GetCustomProperty(const ExecutionContext&,
+                                    const AtomicString&);
+
  private:
   IterationSource* StartIteration(ScriptState*, ExceptionState&) override;
 
   CSSStyleValue* GetShorthandProperty(const CSSProperty&);
-  const CSSValue* GetCustomProperty(const ExecutionContext&,
-                                    const AtomicString&);
 
  private:
   DISALLOW_COPY_AND_ASSIGN(StylePropertyMapReadOnly);
diff --git a/third_party/blink/renderer/core/dom/element_test.cc b/third_party/blink/renderer/core/dom/element_test.cc
index 74d0347..07b9e767 100644
--- a/third_party/blink/renderer/core/dom/element_test.cc
+++ b/third_party/blink/renderer/core/dom/element_test.cc
@@ -394,8 +394,8 @@
 TEST_F(ElementTest, PartmapAttribute) {
   Document& document = GetDocument();
   SetBodyContent(R"HTML(
-    <span id='has_one_mapping' partmap='partname1 partname2'></span>
-    <span id='has_two_mappings' partmap='partname1 partname2, partname3 partname4'></span>
+    <span id='has_one_mapping' partmap='partname1: partname2'></span>
+    <span id='has_two_mappings' partmap='partname1: partname2, partname3: partname4'></span>
     <span id='has_no_mapping'></span>
   )HTML");
 
@@ -432,7 +432,7 @@
     EXPECT_FALSE(has_no_mapping->PartNamesMap());
 
     // Now update the attribute value and make sure it's reflected.
-    has_no_mapping->setAttribute("partmap", "partname1 partname2");
+    has_no_mapping->setAttribute("partmap", "partname1: partname2");
     const NamesMap* part_names_map = has_no_mapping->PartNamesMap();
     ASSERT_TRUE(part_names_map);
     ASSERT_EQ(1UL, part_names_map->size());
diff --git a/third_party/blink/renderer/core/dom/idle_deadline_test.cc b/third_party/blink/renderer/core/dom/idle_deadline_test.cc
index aebde6e9..9e5073b8 100644
--- a/third_party/blink/renderer/core/dom/idle_deadline_test.cc
+++ b/third_party/blink/renderer/core/dom/idle_deadline_test.cc
@@ -8,7 +8,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
-#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_custom_scheduler.h"
+#include "third_party/blink/renderer/platform/testing/scoped_scheduler_overrider.h"
 #include "third_party/blink/renderer/platform/testing/wtf/scoped_mock_clock.h"
 #include "third_party/blink/renderer/platform/wtf/time.h"
 
@@ -74,7 +74,7 @@
   WTF::ScopedMockClock clock_;
 };
 
-TEST_F(IdleDeadlineTest, deadlineInFuture) {
+TEST_F(IdleDeadlineTest, DeadlineInFuture) {
   IdleDeadline* deadline =
       IdleDeadline::Create(TimeTicks() + TimeDelta::FromSecondsD(1.25),
                            IdleDeadline::CallbackType::kCalledWhenIdle);
@@ -82,18 +82,16 @@
   EXPECT_FLOAT_EQ(250.0, deadline->timeRemaining());
 }
 
-TEST_F(IdleDeadlineTest, deadlineInPast) {
+TEST_F(IdleDeadlineTest, DeadlineInPast) {
   IdleDeadline* deadline =
       IdleDeadline::Create(TimeTicks() + TimeDelta::FromSecondsD(0.75),
                            IdleDeadline::CallbackType::kCalledWhenIdle);
   EXPECT_FLOAT_EQ(0, deadline->timeRemaining());
 }
 
-TEST_F(IdleDeadlineTest, yieldForHighPriorityWork) {
+TEST_F(IdleDeadlineTest, YieldForHighPriorityWork) {
   MockIdleDeadlineScheduler scheduler;
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithCustomScheduler,
-                               ThreadScheduler*>
-      platform(&scheduler);
+  ScopedSchedulerOverrider scheduler_overrider(&scheduler);
 
   IdleDeadline* deadline =
       IdleDeadline::Create(TimeTicks() + TimeDelta::FromSecondsD(1.25),
diff --git a/third_party/blink/renderer/core/dom/names_map.cc b/third_party/blink/renderer/core/dom/names_map.cc
index 8acb1e5..ad7dbc3 100644
--- a/third_party/blink/renderer/core/dom/names_map.cc
+++ b/third_party/blink/renderer/core/dom/names_map.cc
@@ -45,15 +45,34 @@
 // second and => is not used to separate key and value. It also allows an ident
 // token on its own as a short-hand for forwarding with the same name.
 
-// The states that can occur while parsing the part map. {...} denotes the new
-// states that can be reached from this state.
+// The states that can occur while parsing the part map and their transitions.
+// A "+" indicates that this transition should consume the current character.
+// A "*" indicates that this is invalid input, usually the decision here is
+// to just do our best and recover gracefully.
 enum State {
-  kPreKey,    // Searching for the start of a key. {kPreKey, kKey}
-  kKey,       // Searching for the end of a key. {kKey, kPreValue}
-  kPreValue,  // Searching for the start of a value. {kPreValue, kPreKey,
-              // kValue}
-  kValue,     // Searching for the end of a value. {kValue, kPreKey, kPostValue}
-  kPostValue,  // Searching for the comma after the value. {kPostValue, kPreKey}
+  kPreKey,     // Searching for the start of a key:
+               //   space, comma, colon* -> kPreKey+
+               //   else -> kKey
+  kKey,        // Searching for the end of a key:
+               //   comma -> kPreKey+
+               //   colon -> kPreValue+
+               //   space -> kPostKey+
+               //   else -> kKey+
+  kPostKey,    // Searching for a delimiter:
+               //   comma -> kPreKey+
+               //   colon -> kPreValue+
+               //   space, else* -> kPostKey+
+  kPreValue,   // Searching for the start of a value:
+               //   comma -> kPreKey+
+               //   colon*, space -> kPreValue+
+               //   else -> kValue+
+  kValue,      // Searching for the end of a value:
+               //   comma -> kPreKey+
+               //   colon*, space -> kPostValue+
+               //   else -> kValue+
+  kPostValue,  // Searching for the comma after the value:
+               //   comma -> kPreKey+
+               //   colon*, else -> kPostValue+
 };
 
 template <typename CharacterType>
@@ -69,24 +88,55 @@
   State state = kPreKey;
   AtomicString key;
   while (cur < length) {
+    // Almost all cases break, ensuring that some input is consumed and we avoid
+    // an infinite loop. For the few transitions which should happen without
+    // consuming input we fall through into the new state's case. This makes it
+    // easy to see the cases which don't consume input and check that they lead
+    // to a case which does.
+    //
+    // The only state which should set a value for key is kKey, as we leave the
+    // state.
     switch (state) {
       case kPreKey:
-        // Skip any number of spaces and commas. When we find something else, it
-        // is the start of a key.
-        if (!IsHTMLSpaceOrComma<CharacterType>(characters[cur])) {
-          start = cur;
-          state = kKey;
-        }
-        break;
-      case kKey:
-        // At a space or comma, we have found the end of the key.
-        if (IsHTMLSpaceOrComma<CharacterType>(characters[cur])) {
-          key = AtomicString(characters + start, cur - start);
-          state = kPreValue;
-        } else {
+        // Skip any number of spaces, commas and colons. When we find something
+        // else, it is the start of a key.
+        if ((IsHTMLSpaceOrComma<CharacterType>(characters[cur]) ||
+             IsColon<CharacterType>(characters[cur]))) {
           break;
         }
+        start = cur;
+        state = kKey;
         FALLTHROUGH;
+      case kKey:
+        // At a comma this was a key without a value, the implicit value is the
+        // same as the key.
+        if (IsComma<CharacterType>(characters[cur])) {
+          key = AtomicString(characters + start, cur - start);
+          Add(key, key);
+          state = kPreKey;
+          // At a colon, we have found the end of the key and we expect a value.
+        } else if (IsColon<CharacterType>(characters[cur])) {
+          key = AtomicString(characters + start, cur - start);
+          state = kPreValue;
+          // At a space, we have found the end of the key.
+        } else if (IsHTMLSpace<CharacterType>(characters[cur])) {
+          key = AtomicString(characters + start, cur - start);
+          state = kPostKey;
+        }
+        break;
+      case kPostKey:
+        // At a comma this was a key without a value, the implicit value is the
+        // same as the key.
+        if (IsComma<CharacterType>(characters[cur])) {
+          Add(key, key);
+          state = kPreKey;
+          // At a colon this was a key with a value, we expect a value.
+        } else if (IsColon<CharacterType>(characters[cur])) {
+          state = kPreValue;
+        }
+        // Spaces should be consumed. We consume other characters too
+        // although the input is invalid
+        break;
       case kPreValue:
         // At a comma this was a key without a value, the implicit value is the
         // same as the key.
@@ -95,20 +145,26 @@
           state = kPreKey;
           // If we reach a non-space character, we have found the start of the
           // value.
-        } else if (IsNotHTMLSpace<CharacterType>(characters[cur])) {
+        } else if (IsColon<CharacterType>(characters[cur]) ||
+                   IsHTMLSpace<CharacterType>(characters[cur])) {
+          break;
+        } else {
           start = cur;
           state = kValue;
         }
         break;
       case kValue:
-        // At a comma or space, we have found the end of the value.
-        if (IsHTMLSpaceOrComma<CharacterType>(characters[cur])) {
+        // At a comma, we have found the end of the value and expect
+        // the next key.
+        if (IsComma<CharacterType>(characters[cur])) {
           Add(key, AtomicString(characters + start, cur - start));
-          if (IsComma<CharacterType>(characters[cur])) {
-            state = kPreKey;
-          } else {
-            state = kPostValue;
-          }
+          state = kPreKey;
+          // At a space or colon, we have found the end of the value,
+          // although a colon is invalid here.
+        } else if (IsHTMLSpace<CharacterType>(characters[cur]) ||
+                   IsColon<CharacterType>(characters[cur])) {
+          Add(key, AtomicString(characters + start, cur - start));
+          state = kPostValue;
         }
         break;
       case kPostValue:
@@ -130,6 +186,7 @@
       // The string ends with a key.
       key = AtomicString(characters + start, cur - start);
       FALLTHROUGH;
+    case kPostKey:
     case kPreValue:
       // The string ends after a key but with nothing else useful.
       Add(key, key);
diff --git a/third_party/blink/renderer/core/dom/names_map_test.cc b/third_party/blink/renderer/core/dom/names_map_test.cc
index db30cc8..995fade 100644
--- a/third_party/blink/renderer/core/dom/names_map_test.cc
+++ b/third_party/blink/renderer/core/dom/names_map_test.cc
@@ -29,26 +29,38 @@
   Vector<std::pair<String, ExpectedMap>> test_cases({
       // Various valid values.
       {"foo", {{"foo", "foo"}}},
-      {"foo bar", {{"foo", "bar"}}},
-      {"foo bar, foo buz", {{"foo", "bar buz"}}},
-      {"foo bar, buz", {{"foo", "bar"}, {"buz", "buz"}}},
-      {"foo bar, buz bar", {{"foo", "bar"}, {"buz", "bar"}}},
-      {"foo, buz bar", {{"foo", "foo"}, {"buz", "bar"}}},
+      {"foo: bar", {{"foo", "bar"}}},
+      {"foo : bar", {{"foo", "bar"}}},
+      {"foo: bar, foo: buz", {{"foo", "bar buz"}}},
+      {"foo: bar, buz", {{"foo", "bar"}, {"buz", "buz"}}},
+      {"foo: bar, buz: bar", {{"foo", "bar"}, {"buz", "bar"}}},
+      {"foo, buz: bar", {{"foo", "foo"}, {"buz", "bar"}}},
 
       // This is an error but qux should be ignored.
-      {"foo bar qux, buz bar", {{"foo", "bar"}, {"buz", "bar"}}},
-      {"foo bar, buz bar qux", {{"foo", "bar"}, {"buz", "bar"}}},
+      {"foo: bar qux, buz: bar", {{"foo", "bar"}, {"buz", "bar"}}},
+      {"foo: bar, buz: bar qux", {{"foo", "bar"}, {"buz", "bar"}}},
 
-      // This is an error but the extra comma should be ignored.
-      {",foo bar, buz bar", {{"foo", "bar"}, {"buz", "bar"}}},
-      {"foo bar,, buz bar", {{"foo", "bar"}, {"buz", "bar"}}},
-      {"foo bar, buz bar,", {{"foo", "bar"}, {"buz", "bar"}}},
-      {"foo bar, buz bar,,", {{"foo", "bar"}, {"buz", "bar"}}},
+      // This is an error but the extra commas and colons should be ignored.
+      {"foo:", {{"foo", "foo"}}},
+      {"foo:,", {{"foo", "foo"}}},
+      {"foo :", {{"foo", "foo"}}},
+      {"foo :,", {{"foo", "foo"}}},
+      {"foo: bar, buz:", {{"foo", "bar"}, {"buz", "buz"}}},
+      {"foo: bar, buz :", {{"foo", "bar"}, {"buz", "buz"}}},
+      {",foo: bar, buz: bar", {{"foo", "bar"}, {"buz", "bar"}}},
+      {"foo: bar,, buz: bar", {{"foo", "bar"}, {"buz", "bar"}}},
+      {"foo: bar, buz: bar,", {{"foo", "bar"}, {"buz", "bar"}}},
+      {"foo: bar, buz: bar,,", {{"foo", "bar"}, {"buz", "bar"}}},
+      {":foo: bar, buz: bar", {{"foo", "bar"}, {"buz", "bar"}}},
+      {"foo: bar:, buz: bar", {{"foo", "bar"}, {"buz", "bar"}}},
+      {"foo: :bar, buz: bar", {{"foo", "bar"}, {"buz", "bar"}}},
+      {"foo: bar, buz: bar:", {{"foo", "bar"}, {"buz", "bar"}}},
+      {"foo: bar, buz: bar::", {{"foo", "bar"}, {"buz", "bar"}}},
 
       // Spaces in odd places.
-      {" foo bar, buz bar", {{"foo", "bar"}, {"buz", "bar"}}},
-      {"foo bar,  buz bar", {{"foo", "bar"}, {"buz", "bar"}}},
-      {"foo bar, buz bar ", {{"foo", "bar"}, {"buz", "bar"}}},
+      {" foo: bar, buz: bar", {{"foo", "bar"}, {"buz", "bar"}}},
+      {"foo: bar,  buz: bar", {{"foo", "bar"}, {"buz", "bar"}}},
+      {"foo: bar, buz: bar ", {{"foo", "bar"}, {"buz", "bar"}}},
   });
 
   NamesMap map;
diff --git a/third_party/blink/renderer/core/dom/scripted_idle_task_controller_test.cc b/third_party/blink/renderer/core/dom/scripted_idle_task_controller_test.cc
index 3da0c695..52bb88766 100644
--- a/third_party/blink/renderer/core/dom/scripted_idle_task_controller_test.cc
+++ b/third_party/blink/renderer/core/dom/scripted_idle_task_controller_test.cc
@@ -6,12 +6,11 @@
 
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_idle_request_callback.h"
 #include "third_party/blink/renderer/core/dom/idle_request_options.h"
 #include "third_party/blink/renderer/core/testing/null_execution_context.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
-#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_custom_scheduler.h"
+#include "third_party/blink/renderer/platform/testing/scoped_scheduler_overrider.h"
 #include "third_party/blink/renderer/platform/wtf/time.h"
 
 namespace blink {
@@ -94,9 +93,7 @@
 
 TEST_F(ScriptedIdleTaskControllerTest, RunCallback) {
   MockScriptedIdleTaskControllerScheduler scheduler(ShouldYield::DONT_YIELD);
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithCustomScheduler,
-                               ThreadScheduler*>
-      platform(&scheduler);
+  ScopedSchedulerOverrider scheduler_overrider(&scheduler);
 
   NullExecutionContext execution_context;
   ScriptedIdleTaskController* controller =
@@ -117,9 +114,7 @@
 
 TEST_F(ScriptedIdleTaskControllerTest, DontRunCallbackWhenAskedToYield) {
   MockScriptedIdleTaskControllerScheduler scheduler(ShouldYield::YIELD);
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithCustomScheduler,
-                               ThreadScheduler*>
-      platform(&scheduler);
+  ScopedSchedulerOverrider scheduler_overrider(&scheduler);
 
   NullExecutionContext execution_context;
   ScriptedIdleTaskController* controller =
diff --git a/third_party/blink/renderer/core/exported/web_node.cc b/third_party/blink/renderer/core/exported/web_node.cc
index 06ba400..55843e70 100644
--- a/third_party/blink/renderer/core/exported/web_node.cc
+++ b/third_party/blink/renderer/core/exported/web_node.cc
@@ -133,6 +133,8 @@
 bool WebNode::IsFocusable() const {
   if (!private_->IsElementNode())
     return false;
+  if (!private_->GetDocument().IsRenderingReady())
+    return false;
   private_->GetDocument().UpdateStyleAndLayoutTreeForNode(private_.Get());
   return ToElement(private_.Get())->IsFocusable();
 }
diff --git a/third_party/blink/renderer/core/exported/web_node_test.cc b/third_party/blink/renderer/core/exported/web_node_test.cc
index bb1d34dc..d16be0d 100644
--- a/third_party/blink/renderer/core/exported/web_node_test.cc
+++ b/third_party/blink/renderer/core/exported/web_node_test.cc
@@ -67,7 +67,8 @@
   LoadURL("https://example.com/test.html");
   WebView().Resize(WebSize(800, 600));
 
-  main_resource.Complete(R"HTML(
+  main_resource.Start();
+  main_resource.Write(R"HTML(
     <!DOCTYPE html>
     <link rel=stylesheet href=style.css>
     <input id=focusable>
@@ -79,7 +80,9 @@
 
   WebNode input_node(GetDocument().getElementById("focusable"));
   EXPECT_FALSE(input_node.IsFocusable());
+  EXPECT_FALSE(GetDocument().HasNodesWithPlaceholderStyle());
 
+  main_resource.Finish();
   css_resource.Complete("dummy {}");
   EXPECT_TRUE(input_node.IsFocusable());
 }
diff --git a/third_party/blink/renderer/core/frame/device_single_window_event_controller.cc b/third_party/blink/renderer/core/frame/device_single_window_event_controller.cc
index 75ede52..44f49c5 100644
--- a/third_party/blink/renderer/core/frame/device_single_window_event_controller.cc
+++ b/third_party/blink/renderer/core/frame/device_single_window_event_controller.cc
@@ -96,7 +96,8 @@
   const Document& document = GetDocument();
   return std::all_of(features.begin(), features.end(),
                      [&document](mojom::FeaturePolicyFeature feature) {
-                       return document.IsFeatureEnabled(feature);
+                       return document.IsFeatureEnabled(
+                           feature, ReportOptions::kReportOnFailure);
                      });
 }
 
diff --git a/third_party/blink/renderer/core/html/html_dialog_element.cc b/third_party/blink/renderer/core/html/html_dialog_element.cc
index e3143d8..ce6a094 100644
--- a/third_party/blink/renderer/core/html/html_dialog_element.cc
+++ b/third_party/blink/renderer/core/html/html_dialog_element.cc
@@ -81,10 +81,13 @@
   dialog->GetDocument().ClearFocusedElement();
 }
 
-static void InertSubtreesChanged(Document& document) {
+static void InertSubtreesChanged(Document& document,
+                                 HTMLDialogElement& dialog_element) {
+  // SetIsInert recurses through subframes to propagate the inert bit and that
+  // needs an up-to-date dialog flat tree distribution: see crbug.com/789094 &
+  // crbug.com/804047 crash reports.
   if (document.GetFrame()) {
-    // SetIsInert recurses through subframes to propagate the inert bit as
-    // needed.
+    dialog_element.UpdateDistributionForFlatTreeTraversal();
     document.GetFrame()->SetIsInert(document.LocalOwner() &&
                                     document.LocalOwner()->IsInert());
   }
@@ -116,7 +119,7 @@
   HTMLDialogElement* active_modal_dialog = GetDocument().ActiveModalDialog();
   GetDocument().RemoveFromTopLayer(this);
   if (active_modal_dialog == this)
-    InertSubtreesChanged(GetDocument());
+    InertSubtreesChanged(GetDocument(), *this);
 
   if (!return_value.IsNull())
     return_value_ = return_value;
@@ -172,19 +175,20 @@
   GetDocument().AddToTopLayer(this);
   SetBooleanAttribute(openAttr, true);
 
+  ForceLayoutForCentering();
+
   // Throw away the AX cache first, so the subsequent steps don't have a chance
   // of queuing up AX events on objects that would be invalidated when the cache
   // is thrown away.
-  InertSubtreesChanged(GetDocument());
+  InertSubtreesChanged(GetDocument(), *this);
 
-  ForceLayoutForCentering();
   SetFocusForDialog(this);
 }
 
 void HTMLDialogElement::RemovedFrom(ContainerNode& insertion_point) {
   HTMLElement::RemovedFrom(insertion_point);
   SetNotCentered();
-  InertSubtreesChanged(GetDocument());
+  InertSubtreesChanged(GetDocument(), *this);
 }
 
 void HTMLDialogElement::SetCentered(LayoutUnit centered_position) {
diff --git a/third_party/blink/renderer/core/html/media/html_media_element.cc b/third_party/blink/renderer/core/html/media/html_media_element.cc
index 3ad04be..bee87b974 100644
--- a/third_party/blink/renderer/core/html/media/html_media_element.cc
+++ b/third_party/blink/renderer/core/html/media/html_media_element.cc
@@ -410,23 +410,12 @@
   return result;
 }
 
-URLRegistry* HTMLMediaElement::media_stream_registry_ = nullptr;
-
 const HashSet<AtomicString>& HTMLMediaElement::GetCheckedAttributeNames()
     const {
   DEFINE_STATIC_LOCAL(HashSet<AtomicString>, attribute_set, ({"src"}));
   return attribute_set;
 }
 
-void HTMLMediaElement::SetMediaStreamRegistry(URLRegistry* registry) {
-  DCHECK(!media_stream_registry_);
-  media_stream_registry_ = registry;
-}
-
-bool HTMLMediaElement::IsMediaStreamURL(const String& url) {
-  return media_stream_registry_ ? media_stream_registry_->Contains(url) : false;
-}
-
 bool HTMLMediaElement::IsHLSURL(const KURL& url) {
   // Keep the same logic as in media_codec_util.h.
   if (url.IsNull() || url.IsEmpty())
@@ -1410,8 +1399,7 @@
   if (media_source_)
     return WebMediaPlayer::kLoadTypeMediaSource;
 
-  if (src_object_ ||
-      (!current_src_.IsNull() && IsMediaStreamURL(current_src_.GetString())))
+  if (src_object_)
     return WebMediaPlayer::kLoadTypeMediaStream;
 
   return WebMediaPlayer::kLoadTypeURL;
@@ -1990,7 +1978,7 @@
     return false;
 
   // MediaStream can't be downloaded.
-  if (IsMediaStreamURL(current_src_.GetString()))
+  if (GetLoadType() == WebMediaPlayer::kLoadTypeMediaStream)
     return false;
 
   // MediaSource can't be downloaded.
diff --git a/third_party/blink/renderer/core/html/media/html_media_element.h b/third_party/blink/renderer/core/html/media/html_media_element.h
index f2d50264..7e060cf 100644
--- a/third_party/blink/renderer/core/html/media/html_media_element.h
+++ b/third_party/blink/renderer/core/html/media/html_media_element.h
@@ -76,7 +76,6 @@
 class TextTrackContainer;
 class TextTrackList;
 class TimeRanges;
-class URLRegistry;
 class VideoTrack;
 class VideoTrackList;
 class WebAudioSourceProvider;
@@ -101,8 +100,6 @@
 
   enum class RecordMetricsBehavior { kDoNotRecord, kDoRecord };
 
-  static void SetMediaStreamRegistry(URLRegistry*);
-  static bool IsMediaStreamURL(const String& url);
   static bool IsHLSURL(const KURL&);
 
   // If HTMLMediaElement is using MediaTracks (either placeholder or provided
@@ -753,8 +750,6 @@
   Member<HTMLMediaElementControlsList> controls_list_;
 
   Member<ElementVisibilityObserver> lazy_load_visibility_observer_;
-
-  static URLRegistry* media_stream_registry_;
 };
 
 inline bool IsHTMLMediaElement(const HTMLElement& element) {
diff --git a/third_party/blink/renderer/core/html/parser/html_parser_idioms.h b/third_party/blink/renderer/core/html/parser/html_parser_idioms.h
index ac928bb..1048d15 100644
--- a/third_party/blink/renderer/core/html/parser/html_parser_idioms.h
+++ b/third_party/blink/renderer/core/html/parser/html_parser_idioms.h
@@ -103,6 +103,11 @@
 }
 
 template <typename CharType>
+inline bool IsColon(CharType character) {
+  return character == ':';
+}
+
+template <typename CharType>
 inline bool IsHTMLSpaceOrComma(CharType character) {
   return IsComma(character) || IsHTMLSpace(character);
 }
diff --git a/third_party/blink/renderer/modules/background_fetch/background_fetch_bridge.cc b/third_party/blink/renderer/modules/background_fetch/background_fetch_bridge.cc
index 022104ce..dd222f4 100644
--- a/third_party/blink/renderer/modules/background_fetch/background_fetch_bridge.cc
+++ b/third_party/blink/renderer/modules/background_fetch/background_fetch_bridge.cc
@@ -6,7 +6,6 @@
 
 #include <utility>
 #include "services/service_manager/public/cpp/interface_provider.h"
-#include "third_party/blink/public/platform/modules/service_worker/web_service_worker_registration.h"
 #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_request.h"
 #include "third_party/blink/renderer/modules/background_fetch/background_fetch_options.h"
 #include "third_party/blink/renderer/modules/background_fetch/background_fetch_registration.h"
@@ -54,9 +53,9 @@
     bool match_all,
     mojom::blink::BackgroundFetchService::MatchRequestsCallback callback) {
   GetService()->MatchRequests(
-      GetSupplementable()->WebRegistration()->RegistrationId(), developer_id,
-      unique_id, std::move(request_to_match), std::move(cache_query_params),
-      match_all, std::move(callback));
+      GetSupplementable()->RegistrationId(), developer_id, unique_id,
+      std::move(request_to_match), std::move(cache_query_params), match_all,
+      std::move(callback));
 }
 
 void BackgroundFetchBridge::Fetch(
@@ -67,8 +66,8 @@
     mojom::blink::BackgroundFetchUkmDataPtr ukm_data,
     RegistrationCallback callback) {
   GetService()->Fetch(
-      GetSupplementable()->WebRegistration()->RegistrationId(), developer_id,
-      std::move(requests), std::move(options), icon, std::move(ukm_data),
+      GetSupplementable()->RegistrationId(), developer_id, std::move(requests),
+      std::move(options), icon, std::move(ukm_data),
       WTF::Bind(&BackgroundFetchBridge::DidGetRegistration,
                 WrapPersistent(this), WTF::Passed(std::move(callback))));
 }
@@ -76,8 +75,8 @@
 void BackgroundFetchBridge::Abort(const String& developer_id,
                                   const String& unique_id,
                                   AbortCallback callback) {
-  GetService()->Abort(GetSupplementable()->WebRegistration()->RegistrationId(),
-                      developer_id, unique_id, std::move(callback));
+  GetService()->Abort(GetSupplementable()->RegistrationId(), developer_id,
+                      unique_id, std::move(callback));
 }
 
 void BackgroundFetchBridge::UpdateUI(const String& developer_id,
@@ -91,15 +90,14 @@
     return;
   }
 
-  GetService()->UpdateUI(
-      GetSupplementable()->WebRegistration()->RegistrationId(), developer_id,
-      unique_id, title, icon, std::move(callback));
+  GetService()->UpdateUI(GetSupplementable()->RegistrationId(), developer_id,
+                         unique_id, title, icon, std::move(callback));
 }
 
 void BackgroundFetchBridge::GetRegistration(const String& developer_id,
                                             RegistrationCallback callback) {
   GetService()->GetRegistration(
-      GetSupplementable()->WebRegistration()->RegistrationId(), developer_id,
+      GetSupplementable()->RegistrationId(), developer_id,
       WTF::Bind(&BackgroundFetchBridge::DidGetRegistration,
                 WrapPersistent(this), WTF::Passed(std::move(callback))));
 }
@@ -121,9 +119,8 @@
 }
 
 void BackgroundFetchBridge::GetDeveloperIds(GetDeveloperIdsCallback callback) {
-  GetService()->GetDeveloperIds(
-      GetSupplementable()->WebRegistration()->RegistrationId(),
-      std::move(callback));
+  GetService()->GetDeveloperIds(GetSupplementable()->RegistrationId(),
+                                std::move(callback));
 }
 
 void BackgroundFetchBridge::AddRegistrationObserver(
diff --git a/third_party/blink/renderer/modules/background_sync/sync_manager.cc b/third_party/blink/renderer/modules/background_sync/sync_manager.cc
index 18ce043..47de737d 100644
--- a/third_party/blink/renderer/modules/background_sync/sync_manager.cc
+++ b/third_party/blink/renderer/modules/background_sync/sync_manager.cc
@@ -44,8 +44,7 @@
       blink::mojom::BackgroundSyncNetworkState::ONLINE;
 
   GetBackgroundSyncServicePtr()->Register(
-      std::move(sync_registration),
-      registration_->WebRegistration()->RegistrationId(),
+      std::move(sync_registration), registration_->RegistrationId(),
       WTF::Bind(SyncManager::RegisterCallback, WrapPersistent(resolver)));
 
   return promise;
@@ -56,7 +55,7 @@
   ScriptPromise promise = resolver->Promise();
 
   GetBackgroundSyncServicePtr()->GetRegistrations(
-      registration_->WebRegistration()->RegistrationId(),
+      registration_->RegistrationId(),
       WTF::Bind(&SyncManager::GetRegistrationsCallback,
                 WrapPersistent(resolver)));
 
diff --git a/third_party/blink/renderer/modules/cookie_store/cookie_store.cc b/third_party/blink/renderer/modules/cookie_store/cookie_store.cc
index d3a45bab..0248f4b9 100644
--- a/third_party/blink/renderer/modules/cookie_store/cookie_store.cc
+++ b/third_party/blink/renderer/modules/cookie_store/cookie_store.cc
@@ -348,7 +348,7 @@
 
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
   int64_t service_worker_registration_id =
-      scope->registration()->WebRegistration()->RegistrationId();
+      scope->registration()->RegistrationId();
   subscription_backend_->AppendSubscriptions(
       service_worker_registration_id, std::move(backend_subscriptions),
       WTF::Bind(&CookieStore::OnSubscribeToCookieChangesResult,
@@ -374,7 +374,7 @@
   ServiceWorkerGlobalScope* scope =
       ToServiceWorkerGlobalScope(GetExecutionContext());
   int64_t service_worker_registration_id =
-      scope->registration()->WebRegistration()->RegistrationId();
+      scope->registration()->RegistrationId();
   subscription_backend_->GetSubscriptions(
       service_worker_registration_id,
       WTF::Bind(&CookieStore::OnGetCookieChangeSubscriptionResult,
diff --git a/third_party/blink/renderer/modules/exported/BUILD.gn b/third_party/blink/renderer/modules/exported/BUILD.gn
index e3030b0..f79e1d1 100644
--- a/third_party/blink/renderer/modules/exported/BUILD.gn
+++ b/third_party/blink/renderer/modules/exported/BUILD.gn
@@ -16,7 +16,6 @@
     "web_idb_key.cc",
     "web_idb_key_range.cc",
     "web_idb_value.cc",
-    "web_media_stream_registry.cc",
     "web_storage_event_dispatcher_impl.cc",
     "web_user_media_request.cc",
   ]
diff --git a/third_party/blink/renderer/modules/exported/web_media_stream_registry.cc b/third_party/blink/renderer/modules/exported/web_media_stream_registry.cc
deleted file mode 100644
index d463018..0000000
--- a/third_party/blink/renderer/modules/exported/web_media_stream_registry.cc
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2011 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "third_party/blink/public/web/web_media_stream_registry.h"
-
-#include "third_party/blink/public/platform/web_media_stream.h"
-#include "third_party/blink/public/platform/web_string.h"
-#include "third_party/blink/public/platform/web_url.h"
-#include "third_party/blink/renderer/modules/mediastream/media_stream_registry.h"
-#include "third_party/blink/renderer/platform/mediastream/media_stream_descriptor.h"
-#include "third_party/blink/renderer/platform/weborigin/kurl.h"
-
-namespace blink {
-
-WebMediaStream WebMediaStreamRegistry::LookupMediaStreamDescriptor(
-    const WebURL& url) {
-  return WebMediaStream(
-      MediaStreamRegistry::Registry().LookupMediaStreamDescriptor(
-          KURL(url).GetString()));
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_overlay_play_button_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_overlay_play_button_element.cc
index aa454a8..b69ad79 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_overlay_play_button_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_overlay_play_button_element.cc
@@ -76,8 +76,7 @@
   // state. This allows potential recovery for transient network and decoder
   // resource issues.
   const String& url = MediaElement().currentSrc().GetString();
-  if (MediaElement().error() && !HTMLMediaElement::IsMediaStreamURL(url) &&
-      !HTMLMediaSource::Lookup(url)) {
+  if (MediaElement().error() && !HTMLMediaSource::Lookup(url)) {
     MediaElement().load();
   }
 
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_play_button_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_play_button_element.cc
index e358f000..81bb8d7 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_play_button_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_play_button_element.cc
@@ -62,8 +62,7 @@
     // state. This allows potential recovery for transient network and decoder
     // resource issues.
     const String& url = MediaElement().currentSrc().GetString();
-    if (MediaElement().error() && !HTMLMediaElement::IsMediaStreamURL(url) &&
-        !HTMLMediaSource::Lookup(url))
+    if (MediaElement().error() && !HTMLMediaSource::Lookup(url))
       MediaElement().load();
 
     MediaElement().TogglePlayState();
diff --git a/third_party/blink/renderer/modules/mediacapturefromelement/html_media_element_capture.cc b/third_party/blink/renderer/modules/mediacapturefromelement/html_media_element_capture.cc
index c396e06..fb74690 100644
--- a/third_party/blink/renderer/modules/mediacapturefromelement/html_media_element_capture.cc
+++ b/third_party/blink/renderer/modules/mediacapturefromelement/html_media_element_capture.cc
@@ -15,7 +15,6 @@
 #include "third_party/blink/renderer/modules/encryptedmedia/html_media_element_encrypted_media.h"
 #include "third_party/blink/renderer/modules/encryptedmedia/media_keys.h"
 #include "third_party/blink/renderer/modules/mediastream/media_stream.h"
-#include "third_party/blink/renderer/modules/mediastream/media_stream_registry.h"
 #include "third_party/blink/renderer/platform/mediastream/media_stream_center.h"
 
 namespace blink {
@@ -78,11 +77,7 @@
       track->stopTrack(context);
       media_stream_->RemoveTrackByComponentAndFireEvents(track->Component());
     }
-    MediaStreamDescriptor* const descriptor =
-        media_element_->currentSrc().IsEmpty()
-            ? media_element_->GetSrcObject()
-            : MediaStreamRegistry::Registry().LookupMediaStreamDescriptor(
-                  media_element_->currentSrc().GetString());
+    MediaStreamDescriptor* const descriptor = media_element_->GetSrcObject();
     DCHECK(descriptor);
     for (unsigned i = 0; i < descriptor->NumberOfAudioComponents(); i++) {
       media_stream_->AddTrackByComponentAndFireEvents(
@@ -182,11 +177,7 @@
 
   // If |element| is actually playing a MediaStream, just clone it.
   if (element.GetLoadType() == WebMediaPlayer::kLoadTypeMediaStream) {
-    MediaStreamDescriptor* const descriptor =
-        element.currentSrc().IsEmpty()
-            ? element.GetSrcObject()
-            : MediaStreamRegistry::Registry().LookupMediaStreamDescriptor(
-                  element.currentSrc().GetString());
+    MediaStreamDescriptor* const descriptor = element.GetSrcObject();
     DCHECK(descriptor);
     return MediaStream::Create(context, descriptor);
   }
diff --git a/third_party/blink/renderer/modules/mediastream/BUILD.gn b/third_party/blink/renderer/modules/mediastream/BUILD.gn
index e77eb497..fb4d8b6 100644
--- a/third_party/blink/renderer/modules/mediastream/BUILD.gn
+++ b/third_party/blink/renderer/modules/mediastream/BUILD.gn
@@ -23,8 +23,6 @@
     "media_stream.h",
     "media_stream_event.cc",
     "media_stream_event.h",
-    "media_stream_registry.cc",
-    "media_stream_registry.h",
     "media_stream_track.cc",
     "media_stream_track.h",
     "media_stream_track_content_hint.h",
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream.cc b/third_party/blink/renderer/modules/mediastream/media_stream.cc
index feef966d..f435bb5 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream.cc
@@ -28,7 +28,6 @@
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/frame/deprecation.h"
-#include "third_party/blink/renderer/modules/mediastream/media_stream_registry.h"
 #include "third_party/blink/renderer/modules/mediastream/media_stream_track_event.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
@@ -494,10 +493,6 @@
   events.clear();
 }
 
-URLRegistry& MediaStream::Registry() const {
-  return MediaStreamRegistry::Registry();
-}
-
 void MediaStream::Trace(blink::Visitor* visitor) {
   visitor->Trace(audio_tracks_);
   visitor->Trace(video_tracks_);
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream.h b/third_party/blink/renderer/modules/mediastream/media_stream.h
index 8b815ef7..7f119d1 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream.h
+++ b/third_party/blink/renderer/modules/mediastream/media_stream.h
@@ -26,7 +26,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASTREAM_MEDIA_STREAM_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASTREAM_MEDIA_STREAM_H_
 
-#include "third_party/blink/renderer/core/fileapi/url_registry.h"
 #include "third_party/blink/renderer/modules/event_target_modules.h"
 #include "third_party/blink/renderer/modules/mediastream/media_stream_track.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
@@ -53,7 +52,6 @@
 
 class MODULES_EXPORT MediaStream final : public EventTargetWithInlineData,
                                          public ContextClient,
-                                         public URLRegistrable,
                                          public MediaStreamDescriptorClient {
   USING_GARBAGE_COLLECTED_MIXIN(MediaStream);
   DEFINE_WRAPPERTYPEINFO();
@@ -127,9 +125,6 @@
     return ContextClient::GetExecutionContext();
   }
 
-  // URLRegistrable
-  URLRegistry& Registry() const override;
-
   void Trace(blink::Visitor*) override;
 
  protected:
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_registry.cc b/third_party/blink/renderer/modules/mediastream/media_stream_registry.cc
deleted file mode 100644
index db5b71d3..0000000
--- a/third_party/blink/renderer/modules/mediastream/media_stream_registry.cc
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2011 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1.  Redistributions of source code must retain the above copyright
- *     notice, this list of conditions and the following disclaimer.
- * 2.  Redistributions in binary form must reproduce the above copyright
- *     notice, this list of conditions and the following disclaimer in the
- *     documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "third_party/blink/renderer/modules/mediastream/media_stream_registry.h"
-
-#include "third_party/blink/renderer/core/html/media/html_media_element.h"
-#include "third_party/blink/renderer/modules/mediastream/media_stream.h"
-#include "third_party/blink/renderer/platform/weborigin/kurl.h"
-
-namespace blink {
-
-MediaStreamRegistry& MediaStreamRegistry::Registry() {
-  // Since WebWorkers cannot obtain MediaStream objects, we should be on the
-  // main thread.
-  DCHECK(IsMainThread());
-  DEFINE_STATIC_LOCAL(MediaStreamRegistry, instance, ());
-  return instance;
-}
-
-void MediaStreamRegistry::RegisterURL(SecurityOrigin*,
-                                      const KURL& url,
-                                      URLRegistrable* stream) {
-  DCHECK(&stream->Registry() == this);
-  DCHECK(IsMainThread());
-  stream_descriptors_->Set(url.GetString(),
-                           static_cast<MediaStream*>(stream)->Descriptor());
-}
-
-void MediaStreamRegistry::UnregisterURL(const KURL& url) {
-  DCHECK(IsMainThread());
-  stream_descriptors_->erase(url.GetString());
-}
-
-bool MediaStreamRegistry::Contains(const String& url) {
-  DCHECK(IsMainThread());
-  return stream_descriptors_->Contains(url);
-}
-
-MediaStreamDescriptor* MediaStreamRegistry::LookupMediaStreamDescriptor(
-    const String& url) {
-  DCHECK(IsMainThread());
-  return stream_descriptors_->at(url);
-}
-
-MediaStreamRegistry::MediaStreamRegistry()
-    : stream_descriptors_(
-          new HeapHashMap<String, Member<MediaStreamDescriptor>>) {
-  HTMLMediaElement::SetMediaStreamRegistry(this);
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_registry.h b/third_party/blink/renderer/modules/mediastream/media_stream_registry.h
deleted file mode 100644
index 8f34641d..0000000
--- a/third_party/blink/renderer/modules/mediastream/media_stream_registry.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2011 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1.  Redistributions of source code must retain the above copyright
- *     notice, this list of conditions and the following disclaimer.
- * 2.  Redistributions in binary form must reproduce the above copyright
- *     notice, this list of conditions and the following disclaimer in the
- *     documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASTREAM_MEDIA_STREAM_REGISTRY_H_
-#define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASTREAM_MEDIA_STREAM_REGISTRY_H_
-
-#include "third_party/blink/renderer/core/fileapi/url_registry.h"
-#include "third_party/blink/renderer/modules/modules_export.h"
-#include "third_party/blink/renderer/platform/heap/handle.h"
-#include "third_party/blink/renderer/platform/heap/persistent.h"
-#include "third_party/blink/renderer/platform/wtf/hash_map.h"
-#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
-
-namespace blink {
-
-class KURL;
-class MediaStreamDescriptor;
-
-class MODULES_EXPORT MediaStreamRegistry final : public URLRegistry {
- public:
-  // Returns a single instance of MediaStreamRegistry.
-  static MediaStreamRegistry& Registry();
-
-  // Registers a blob URL referring to the specified stream data.
-  void RegisterURL(SecurityOrigin*, const KURL&, URLRegistrable*) override;
-  void UnregisterURL(const KURL&) override;
-  bool Contains(const String&) override;
-
-  MediaStreamDescriptor* LookupMediaStreamDescriptor(const String& url);
-
- private:
-  MediaStreamRegistry();
-  Persistent<HeapHashMap<String, Member<MediaStreamDescriptor>>>
-      stream_descriptors_;
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASTREAM_MEDIA_STREAM_REGISTRY_H_
diff --git a/third_party/blink/renderer/modules/notifications/notification_manager.cc b/third_party/blink/renderer/modules/notifications/notification_manager.cc
index 36bc76a3..d4d6d33 100644
--- a/third_party/blink/renderer/modules/notifications/notification_manager.cc
+++ b/third_party/blink/renderer/modules/notifications/notification_manager.cc
@@ -129,7 +129,7 @@
 }
 
 void NotificationManager::DisplayPersistentNotification(
-    blink::WebServiceWorkerRegistration* service_worker_registration,
+    int64_t service_worker_registration_id,
     mojom::blink::NotificationDataPtr notification_data,
     mojom::blink::NotificationResourcesPtr notification_resources,
     ScriptPromiseResolver* resolver) {
@@ -165,8 +165,8 @@
   }
 
   GetNotificationService()->DisplayPersistentNotification(
-      service_worker_registration->RegistrationId(),
-      std::move(notification_data), std::move(notification_resources),
+      service_worker_registration_id, std::move(notification_data),
+      std::move(notification_resources),
       WTF::Bind(&NotificationManager::DidDisplayPersistentNotification,
                 WrapPersistent(this), WrapPersistent(resolver)));
 }
@@ -193,11 +193,11 @@
 }
 
 void NotificationManager::GetNotifications(
-    WebServiceWorkerRegistration* service_worker_registration,
+    int64_t service_worker_registration_id,
     const WebString& filter_tag,
     ScriptPromiseResolver* resolver) {
   GetNotificationService()->GetNotifications(
-      service_worker_registration->RegistrationId(), filter_tag,
+      service_worker_registration_id, filter_tag,
       WTF::Bind(&NotificationManager::DidGetNotifications, WrapPersistent(this),
                 WrapPersistent(resolver)));
 }
diff --git a/third_party/blink/renderer/modules/notifications/notification_manager.h b/third_party/blink/renderer/modules/notifications/notification_manager.h
index 98871553..7a8148d 100644
--- a/third_party/blink/renderer/modules/notifications/notification_manager.h
+++ b/third_party/blink/renderer/modules/notifications/notification_manager.h
@@ -18,7 +18,6 @@
 class ScriptPromise;
 class ScriptPromiseResolver;
 class ScriptState;
-class WebServiceWorkerRegistration;
 
 // The notification manager, unique to the execution context, is responsible for
 // connecting and communicating with the Mojo notification service.
@@ -61,7 +60,7 @@
 
   // Shows a notification from a service worker.
   void DisplayPersistentNotification(
-      blink::WebServiceWorkerRegistration* service_worker_registration,
+      int64_t service_worker_registration_id,
       mojom::blink::NotificationDataPtr notification_data,
       mojom::blink::NotificationResourcesPtr notification_resources,
       ScriptPromiseResolver* resolver);
@@ -72,10 +71,9 @@
   // Asynchronously gets the persistent notifications belonging to the Service
   // Worker Registration. If |filter_tag| is not an empty string, only the
   // notification with the given tag will be considered.
-  void GetNotifications(
-      WebServiceWorkerRegistration* service_worker_registration,
-      const WebString& filter_tag,
-      ScriptPromiseResolver* resolver);
+  void GetNotifications(int64_t service_worker_registration_id,
+                        const WebString& filter_tag,
+                        ScriptPromiseResolver* resolver);
 
   void Trace(blink::Visitor* visitor) override;
 
diff --git a/third_party/blink/renderer/modules/notifications/service_worker_registration_notifications.cc b/third_party/blink/renderer/modules/notifications/service_worker_registration_notifications.cc
index 72dba71..33d08e5c 100644
--- a/third_party/blink/renderer/modules/notifications/service_worker_registration_notifications.cc
+++ b/third_party/blink/renderer/modules/notifications/service_worker_registration_notifications.cc
@@ -87,7 +87,7 @@
 
   ExecutionContext* execution_context = ExecutionContext::From(script_state);
   NotificationManager::From(execution_context)
-      ->GetNotifications(registration.WebRegistration(), options.tag(),
+      ->GetNotifications(registration.RegistrationId(), options.tag(),
                          WrapPersistent(resolver));
   return promise;
 }
@@ -144,7 +144,7 @@
   DCHECK(loaders_.Contains(loader));
 
   NotificationManager::From(GetExecutionContext())
-      ->DisplayPersistentNotification(registration_->WebRegistration(),
+      ->DisplayPersistentNotification(registration_->RegistrationId(),
                                       std::move(data), loader->GetResources(),
                                       WrapPersistent(resolver));
   loaders_.erase(loader);
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.cc b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.cc
index fe216c25..e1895c44 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.cc
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.cc
@@ -237,7 +237,7 @@
   return nullptr;
 }
 
-P2PQuicStreamImpl* P2PQuicTransportImpl::CreateIncomingDynamicStream(
+P2PQuicStreamImpl* P2PQuicTransportImpl::CreateIncomingStream(
     quic::QuicStreamId id) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   P2PQuicStreamImpl* stream = CreateStreamInternal(id);
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.h b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.h
index 609f223c..60c85ef 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.h
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.h
@@ -104,7 +104,7 @@
   // Creates a new stream initiated from the remote side. The caller does not
   // own the stream, so the stream is activated and ownership is moved to the
   // quic::QuicSession.
-  P2PQuicStreamImpl* CreateIncomingDynamicStream(
+  P2PQuicStreamImpl* CreateIncomingStream(
       quic::QuicStreamId id) override;
 
   // Creates a new outgoing stream. The caller does not own the
diff --git a/third_party/blink/renderer/modules/push_messaging/push_manager.cc b/third_party/blink/renderer/modules/push_messaging/push_manager.cc
index a4760fb..44fb9ac5 100644
--- a/third_party/blink/renderer/modules/push_messaging/push_manager.cc
+++ b/third_party/blink/renderer/modules/push_messaging/push_manager.cc
@@ -79,13 +79,13 @@
           DOMException::Create(DOMExceptionCode::kInvalidStateError,
                                "Document is detached from window."));
     PushController::ClientFrom(frame).Subscribe(
-        registration_->WebRegistration(), web_options,
+        registration_->RegistrationId(), web_options,
         LocalFrame::HasTransientUserActivation(frame,
                                                true /* check_if_main_thread */),
         std::make_unique<PushSubscriptionCallbacks>(resolver, registration_));
   } else {
     PushProvider()->Subscribe(
-        registration_->WebRegistration(), web_options,
+        registration_->RegistrationId(), web_options,
         LocalFrame::HasTransientUserActivation(nullptr,
                                                true /* check_if_main_thread */),
         std::make_unique<PushSubscriptionCallbacks>(resolver, registration_));
@@ -99,7 +99,7 @@
   ScriptPromise promise = resolver->Promise();
 
   PushProvider()->GetSubscription(
-      registration_->WebRegistration(),
+      registration_->RegistrationId(),
       std::make_unique<PushSubscriptionCallbacks>(resolver, registration_));
   return promise;
 }
diff --git a/third_party/blink/renderer/modules/push_messaging/push_subscription.cc b/third_party/blink/renderer/modules/push_messaging/push_subscription.cc
index 2e2032d..625124a 100644
--- a/third_party/blink/renderer/modules/push_messaging/push_subscription.cc
+++ b/third_party/blink/renderer/modules/push_messaging/push_subscription.cc
@@ -98,7 +98,7 @@
   DCHECK(web_push_provider);
 
   web_push_provider->Unsubscribe(
-      service_worker_registration_->WebRegistration(),
+      service_worker_registration_->RegistrationId(),
       std::make_unique<CallbackPromiseAdapter<bool, PushError>>(resolver));
   return promise;
 }
diff --git a/third_party/blink/renderer/modules/sensor/sensor.cc b/third_party/blink/renderer/modules/sensor/sensor.cc
index 36fc023..a3680cf 100644
--- a/third_party/blink/renderer/modules/sensor/sensor.cc
+++ b/third_party/blink/renderer/modules/sensor/sensor.cc
@@ -26,7 +26,8 @@
                         const Vector<mojom::FeaturePolicyFeature>& features) {
   return std::all_of(features.begin(), features.end(),
                      [document](mojom::FeaturePolicyFeature feature) {
-                       return document->IsFeatureEnabled(feature);
+                       return document->IsFeatureEnabled(
+                           feature, ReportOptions::kReportOnFailure);
                      });
 }
 
diff --git a/third_party/blink/renderer/modules/service_worker/navigation_preload_manager.cc b/third_party/blink/renderer/modules/service_worker/navigation_preload_manager.cc
index e64157b..21078ebab 100644
--- a/third_party/blink/renderer/modules/service_worker/navigation_preload_manager.cc
+++ b/third_party/blink/renderer/modules/service_worker/navigation_preload_manager.cc
@@ -36,7 +36,7 @@
 
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
   ScriptPromise promise = resolver->Promise();
-  registration_->WebRegistration()->SetNavigationPreloadHeader(
+  registration_->SetNavigationPreloadHeader(
       value, std::make_unique<SetNavigationPreloadHeaderCallbacks>(resolver));
   return promise;
 }
@@ -44,7 +44,7 @@
 ScriptPromise NavigationPreloadManager::getState(ScriptState* script_state) {
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
   ScriptPromise promise = resolver->Promise();
-  registration_->WebRegistration()->GetNavigationPreloadState(
+  registration_->GetNavigationPreloadState(
       std::make_unique<GetNavigationPreloadStateCallbacks>(resolver));
   return promise;
 }
@@ -57,7 +57,7 @@
                                                    ScriptState* script_state) {
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
   ScriptPromise promise = resolver->Promise();
-  registration_->WebRegistration()->EnableNavigationPreload(
+  registration_->EnableNavigationPreload(
       enable, std::make_unique<EnableNavigationPreloadCallbacks>(resolver));
   return promise;
 }
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_registration.cc b/third_party/blink/renderer/modules/service_worker/service_worker_registration.cc
index bb51581..06ff633b 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_registration.cc
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_registration.cc
@@ -79,6 +79,10 @@
   return navigation_preload_;
 }
 
+int64_t ServiceWorkerRegistration::RegistrationId() const {
+  return handle_->Registration()->RegistrationId();
+}
+
 String ServiceWorkerRegistration::scope() const {
   return handle_->Registration()->Scope().GetString();
 }
@@ -96,6 +100,31 @@
   return "";
 }
 
+void ServiceWorkerRegistration::EnableNavigationPreload(
+    bool enable,
+    std::unique_ptr<
+        WebServiceWorkerRegistration::WebEnableNavigationPreloadCallbacks>
+        callbacks) {
+  handle_->Registration()->EnableNavigationPreload(enable,
+                                                   std::move(callbacks));
+}
+
+void ServiceWorkerRegistration::GetNavigationPreloadState(
+    std::unique_ptr<
+        WebServiceWorkerRegistration::WebGetNavigationPreloadStateCallbacks>
+        callbacks) {
+  handle_->Registration()->GetNavigationPreloadState(std::move(callbacks));
+}
+
+void ServiceWorkerRegistration::SetNavigationPreloadHeader(
+    const WebString& value,
+    std::unique_ptr<
+        WebServiceWorkerRegistration::WebSetNavigationPreloadHeaderCallbacks>
+        callbacks) {
+  handle_->Registration()->SetNavigationPreloadHeader(value,
+                                                      std::move(callbacks));
+}
+
 ScriptPromise ServiceWorkerRegistration::update(ScriptState* script_state) {
   if (!GetExecutionContext()) {
     return ScriptPromise::RejectWithDOMException(
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_registration.h b/third_party/blink/renderer/modules/service_worker/service_worker_registration.h
index 70d5bbfe..d472cb53 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_registration.h
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_registration.h
@@ -73,9 +73,19 @@
   String scope() const;
   String updateViaCache() const;
 
-  WebServiceWorkerRegistration* WebRegistration() {
-    return handle_->Registration();
-  }
+  int64_t RegistrationId() const;
+
+  void EnableNavigationPreload(
+      bool enable,
+      std::unique_ptr<
+          WebServiceWorkerRegistration::WebEnableNavigationPreloadCallbacks>);
+  void GetNavigationPreloadState(
+      std::unique_ptr<
+          WebServiceWorkerRegistration::WebGetNavigationPreloadStateCallbacks>);
+  void SetNavigationPreloadHeader(
+      const WebString& value,
+      std::unique_ptr<WebServiceWorkerRegistration::
+                          WebSetNavigationPreloadHeaderCallbacks>);
 
   ScriptPromise update(ScriptState*);
   ScriptPromise unregister(ScriptState*);
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index 1f0518e9cc..1e4de767 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -1574,15 +1574,17 @@
     "testing/picture_matchers.h",
     "testing/scoped_fake_plugin_registry.cc",
     "testing/scoped_fake_plugin_registry.h",
+    "testing/scoped_main_thread_overrider.cc",
+    "testing/scoped_main_thread_overrider.h",
     "testing/scoped_mocked_url.cc",
     "testing/scoped_mocked_url.h",
+    "testing/scoped_scheduler_overrider.cc",
+    "testing/scoped_scheduler_overrider.h",
     "testing/stub_graphics_layer_client.h",
     "testing/test_paint_artifact.cc",
     "testing/test_paint_artifact.h",
     "testing/testing_platform_support.cc",
     "testing/testing_platform_support.h",
-    "testing/testing_platform_support_with_custom_scheduler.cc",
-    "testing/testing_platform_support_with_custom_scheduler.h",
     "testing/testing_platform_support_with_mock_scheduler.cc",
     "testing/testing_platform_support_with_mock_scheduler.h",
     "testing/testing_platform_support_with_web_rtc.cc",
diff --git a/third_party/blink/renderer/platform/exported/platform.cc b/third_party/blink/renderer/platform/exported/platform.cc
index 13a3cf4..1ee5e5dd 100644
--- a/third_party/blink/renderer/platform/exported/platform.cc
+++ b/third_party/blink/renderer/platform/exported/platform.cc
@@ -33,7 +33,6 @@
 #include <memory>
 
 #include "base/single_thread_task_runner.h"
-#include "base/synchronization/waitable_event.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/memory_dump_manager.h"
 #include "build/build_config.h"
@@ -113,7 +112,7 @@
       CrossThreadBind(function, CrossThreadUnretained(context)));
 }
 
-Platform::Platform() : main_thread_(nullptr) {
+Platform::Platform() {
   WTF::Partitions::Initialize(MaxObservedSizeFunction);
 }
 
@@ -170,27 +169,22 @@
   DCHECK(!g_platform);
   DCHECK(platform);
   g_platform = platform;
-  g_platform->owned_main_thread_ = main_thread_scheduler->CreateMainThread();
-  g_platform->main_thread_ = g_platform->owned_main_thread_.get();
-  DCHECK(!g_platform->current_thread_slot_.Get());
-  g_platform->current_thread_slot_.Set(g_platform->main_thread_);
-  InitializeCommon(platform);
+  InitializeCommon(platform, main_thread_scheduler->CreateMainThread());
 }
 
 void Platform::CreateMainThreadAndInitialize(Platform* platform) {
   DCHECK(!g_platform);
   DCHECK(platform);
   g_platform = platform;
-  g_platform->owned_main_thread_ = std::make_unique<SimpleMainThread>();
-  g_platform->main_thread_ = g_platform->owned_main_thread_.get();
-  DCHECK(!g_platform->current_thread_slot_.Get());
-  g_platform->current_thread_slot_.Set(g_platform->main_thread_);
-  InitializeCommon(platform);
+  InitializeCommon(platform, std::make_unique<SimpleMainThread>());
 }
 
-void Platform::InitializeCommon(Platform* platform) {
+void Platform::InitializeCommon(Platform* platform,
+                                std::unique_ptr<Thread> main_thread) {
   WTF::Initialize(CallOnMainThreadFunction);
 
+  Thread::SetMainThread(std::move(main_thread));
+
   ProcessHeap::Init();
   MemoryCoordinator::Initialize();
   if (base::ThreadTaskRunnerHandle::IsSet()) {
@@ -208,63 +202,45 @@
   FontFamilyNames::init();
   InitializePlatformLanguage();
 
-  // TODO(ssid): remove this check after fixing crbug.com/486782.
-  if (g_platform->main_thread_) {
-    DCHECK(!g_gc_task_runner);
-    g_gc_task_runner = new GCTaskRunner(g_platform->main_thread_);
-    base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
-        PartitionAllocMemoryDumpProvider::Instance(), "PartitionAlloc",
-        base::ThreadTaskRunnerHandle::Get());
-    base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
-        FontCacheMemoryDumpProvider::Instance(), "FontCaches",
-        base::ThreadTaskRunnerHandle::Get());
-    base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
-        MemoryCacheDumpProvider::Instance(), "MemoryCache",
-        base::ThreadTaskRunnerHandle::Get());
-    base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
-        InstanceCountersMemoryDumpProvider::Instance(), "BlinkObjectCounters",
-        base::ThreadTaskRunnerHandle::Get());
-  }
+  DCHECK(!g_gc_task_runner);
+  g_gc_task_runner = new GCTaskRunner(Thread::MainThread());
+  base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
+      PartitionAllocMemoryDumpProvider::Instance(), "PartitionAlloc",
+      base::ThreadTaskRunnerHandle::Get());
+  base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
+      FontCacheMemoryDumpProvider::Instance(), "FontCaches",
+      base::ThreadTaskRunnerHandle::Get());
+  base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
+      MemoryCacheDumpProvider::Instance(), "MemoryCache",
+      base::ThreadTaskRunnerHandle::Get());
+  base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
+      InstanceCountersMemoryDumpProvider::Instance(), "BlinkObjectCounters",
+      base::ThreadTaskRunnerHandle::Get());
 
   RendererResourceCoordinator::Initialize();
 }
 
 void Platform::SetCurrentPlatformForTesting(Platform* platform) {
   DCHECK(platform);
-
-  // The overriding platform does not necessarily own the main thread
-  // (owned_main_thread_ may be null), but must have a pointer to a valid
-  // main thread object (which may be from the overridden platform).
-  //
-  // If the new platform's main_thread_ is null, that means we need to
-  // create a new main thread for it. This happens only in
-  // ScopedUnittestsEnvironmentSetup's constructor, which bypasses
-  // Platform::Initialize().
-  if (!platform->main_thread_) {
-    platform->owned_main_thread_ = std::make_unique<SimpleMainThread>();
-    platform->main_thread_ = platform->owned_main_thread_.get();
-  }
-
-  // Set only the main thread to TLS for the new platform. This is OK for the
-  // testing purposes. The TLS slot may already be set when
-  // ScopedTestingPlatformSupport tries to revert to the old platform.
-  if (!platform->current_thread_slot_.Get())
-    platform->current_thread_slot_.Set(platform->main_thread_);
-
   g_platform = platform;
 }
 
+void Platform::CreateMainThreadForTesting() {
+  DCHECK(!Thread::MainThread());
+  Thread::SetMainThread(std::make_unique<SimpleMainThread>());
+}
+
 void Platform::SetMainThreadTaskRunnerForTesting() {
   DCHECK(WTF::IsMainThread());
-  DCHECK(g_platform->main_thread_->IsSimpleMainThread());
-  static_cast<SimpleMainThread*>(g_platform->main_thread_)
+  DCHECK(Thread::MainThread()->IsSimpleMainThread());
+  static_cast<SimpleMainThread*>(Thread::MainThread())
       ->SetMainThreadTaskRunnerForTesting(base::ThreadTaskRunnerHandle::Get());
 }
 
 void Platform::UnsetMainThreadTaskRunnerForTesting() {
   DCHECK(WTF::IsMainThread());
-  DCHECK(g_platform->main_thread_->IsSimpleMainThread());
-  static_cast<SimpleMainThread*>(g_platform->main_thread_)
+  DCHECK(Thread::MainThread()->IsSimpleMainThread());
+  static_cast<SimpleMainThread*>(Thread::MainThread())
       ->SetMainThreadTaskRunnerForTesting(nullptr);
 }
 
@@ -273,11 +249,11 @@
 }
 
 Thread* Platform::MainThread() {
-  return main_thread_;
+  return Thread::MainThread();
 }
 
 Thread* Platform::CurrentThread() {
-  return static_cast<Thread*>(current_thread_slot_.Get());
+  return Thread::Current();
 }
 
 service_manager::Connector* Platform::GetConnector() {
@@ -305,57 +281,19 @@
 
 std::unique_ptr<Thread> Platform::CreateThread(
     const ThreadCreationParams& params) {
-  std::unique_ptr<scheduler::WebThreadBase> thread =
-      scheduler::WebThreadBase::CreateWorkerThread(params);
-  thread->Init();
-  WaitUntilThreadTLSUpdate(thread.get());
-  return std::move(thread);
+  return Thread::CreateThread(params);
 }
 
 std::unique_ptr<Thread> Platform::CreateWebAudioThread() {
-  ThreadCreationParams params(WebThreadType::kWebAudioThread);
-  // WebAudio uses a thread with |DISPLAY| priority to avoid glitch when the
-  // system is under the high pressure. Note that the main browser thread also
-  // runs with same priority. (see: crbug.com/734539)
-  params.thread_options.priority = base::ThreadPriority::DISPLAY;
-  return CreateThread(params);
+  return Thread::CreateWebAudioThread();
 }
 
-void Platform::WaitUntilThreadTLSUpdate(Thread* thread) {
-  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC,
-                            base::WaitableEvent::InitialState::NOT_SIGNALED);
-  // This cross-thread posting is guaranteed to be safe.
-  PostCrossThreadTask(*thread->GetTaskRunner(), FROM_HERE,
-                      CrossThreadBind(&Platform::UpdateThreadTLS,
-                                      WTF::CrossThreadUnretained(this),
-                                      WTF::CrossThreadUnretained(thread),
-                                      WTF::CrossThreadUnretained(&event)));
-  event.Wait();
-}
-
-void Platform::UpdateThreadTLS(Thread* thread, base::WaitableEvent* event) {
-  DCHECK(!current_thread_slot_.Get());
-  current_thread_slot_.Set(thread);
-  event->Signal();
-}
-
-void Platform::InitializeCompositorThread() {
-  DCHECK(!compositor_thread_);
-
-  ThreadCreationParams params(WebThreadType::kCompositorThread);
-#if defined(OS_ANDROID)
-  params.thread_options.priority = base::ThreadPriority::DISPLAY;
-#endif
-  std::unique_ptr<scheduler::WebThreadBase> compositor_thread =
-      scheduler::WebThreadBase::CreateCompositorThread(params);
-  compositor_thread->Init();
-  WaitUntilThreadTLSUpdate(compositor_thread.get());
-  compositor_thread_ = std::move(compositor_thread);
-  SetDisplayThreadPriority(compositor_thread_->ThreadId());
+void Platform::CreateAndSetCompositorThread() {
+  Thread::CreateAndSetCompositorThread();
 }
 
 Thread* Platform::CompositorThread() {
-  return compositor_thread_.get();
+  return Thread::CompositorThread();
 }
 
 scoped_refptr<base::SingleThreadTaskRunner>
diff --git a/third_party/blink/renderer/platform/scheduler/common/thread.cc b/third_party/blink/renderer/platform/scheduler/common/thread.cc
index bb3b7ad..7875408a 100644
--- a/third_party/blink/renderer/platform/scheduler/common/thread.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/thread.cc
@@ -5,8 +5,15 @@
 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
 
 #include "base/single_thread_task_runner.h"
+#include "base/synchronization/waitable_event.h"
 #include "build/build_config.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/scheduler/child/webthread_base.h"
+#include "third_party/blink/renderer/platform/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/web_task_runner.h"
 #include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/thread_specific.h"
 
 #if defined(OS_WIN)
 #include <windows.h>
@@ -16,6 +23,46 @@
 
 namespace blink {
 
+namespace {
+
+// Thread-local storage for "blink::Thread"s.
+Thread*& ThreadTLSSlot() {
+  DEFINE_THREAD_SAFE_STATIC_LOCAL(WTF::ThreadSpecific<Thread*>, thread_tls_slot,
+                                  ());
+  return *thread_tls_slot;
+}
+
+// Update the threads TLS on the newly created thread.
+void UpdateThreadTLS(Thread* thread, base::WaitableEvent* event) {
+  ThreadTLSSlot() = thread;
+  event->Signal();
+}
+
+// Post a task to register |thread| to the TLS, and wait until it gets actually
+// registered. This is called on a thread that created |thread| (not on
+// |thread|.)
+void UpdateThreadTLSAndWait(Thread* thread) {
+  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC,
+                            base::WaitableEvent::InitialState::NOT_SIGNALED);
+  PostCrossThreadTask(
+      *thread->GetTaskRunner(), FROM_HERE,
+      CrossThreadBind(&UpdateThreadTLS, WTF::CrossThreadUnretained(thread),
+                      WTF::CrossThreadUnretained(&event)));
+  event.Wait();
+}
+
+std::unique_ptr<Thread>& GetMainThread() {
+  DEFINE_STATIC_LOCAL(std::unique_ptr<Thread>, main_thread, ());
+  return main_thread;
+}
+
+std::unique_ptr<Thread>& GetCompositorThread() {
+  DEFINE_STATIC_LOCAL(std::unique_ptr<Thread>, compositor_thread, ());
+  return compositor_thread;
+}
+
+}  // namespace
+
 ThreadCreationParams::ThreadCreationParams(WebThreadType thread_type)
     : thread_type(thread_type),
       name(GetNameForThreadType(thread_type)),
@@ -33,6 +80,59 @@
   return *this;
 }
 
+std::unique_ptr<Thread> Thread::CreateThread(
+    const ThreadCreationParams& params) {
+  std::unique_ptr<scheduler::WebThreadBase> thread =
+      scheduler::WebThreadBase::CreateWorkerThread(params);
+  thread->Init();
+  UpdateThreadTLSAndWait(thread.get());
+  return std::move(thread);
+}
+
+std::unique_ptr<Thread> Thread::CreateWebAudioThread() {
+  ThreadCreationParams params(WebThreadType::kWebAudioThread);
+  // WebAudio uses a thread with |DISPLAY| priority to avoid glitch when the
+  // system is under the high pressure. Note that the main browser thread also
+  // runs with same priority. (see: crbug.com/734539)
+  params.thread_options.priority = base::ThreadPriority::DISPLAY;
+  return CreateThread(params);
+}
+
+void Thread::CreateAndSetCompositorThread() {
+  DCHECK(!GetCompositorThread());
+
+  ThreadCreationParams params(WebThreadType::kCompositorThread);
+#if defined(OS_ANDROID)
+  params.thread_options.priority = base::ThreadPriority::DISPLAY;
+#endif
+  std::unique_ptr<scheduler::WebThreadBase> compositor_thread =
+      scheduler::WebThreadBase::CreateCompositorThread(params);
+  compositor_thread->Init();
+  UpdateThreadTLSAndWait(compositor_thread.get());
+  GetCompositorThread() = std::move(compositor_thread);
+  Platform::Current()->SetDisplayThreadPriority(
+      GetCompositorThread()->ThreadId());
+}
+
+Thread* Thread::Current() {
+  return ThreadTLSSlot();
+}
+
+Thread* Thread::MainThread() {
+  return GetMainThread().get();
+}
+
+Thread* Thread::CompositorThread() {
+  return GetCompositorThread().get();
+}
+
+std::unique_ptr<Thread> Thread::SetMainThread(
+    std::unique_ptr<Thread> main_thread) {
+  ThreadTLSSlot() = main_thread.get();
+  std::swap(GetMainThread(), main_thread);
+  return main_thread;
+}
+
 #if defined(OS_WIN)
 static_assert(sizeof(blink::PlatformThreadId) >= sizeof(DWORD),
               "size of platform thread id is too small");
diff --git a/third_party/blink/renderer/platform/scheduler/public/thread.h b/third_party/blink/renderer/platform/scheduler/public/thread.h
index 61f99d1..8322e98 100644
--- a/third_party/blink/renderer/platform/scheduler/public/thread.h
+++ b/third_party/blink/renderer/platform/scheduler/public/thread.h
@@ -70,7 +70,8 @@
 // run.
 class PLATFORM_EXPORT Thread {
  public:
-  friend class Platform;  // For IsSimpleMainThread().
+  friend class Platform;  // For SetMainThread() and IsSimpleMainThread().
+  friend class ScopedMainThreadOverrider;  // For SetMainThread().
 
   // An IdleTask is expected to complete before the deadline it is passed.
   using IdleTask = base::OnceCallback<void(base::TimeTicks deadline)>;
@@ -82,6 +83,28 @@
     virtual void DidProcessTask() = 0;
   };
 
+  // Creates a new thread. This may be called from a non-main thread (e.g.
+  // nested Web workers).
+  static std::unique_ptr<Thread> CreateThread(const ThreadCreationParams&);
+
+  // Creates a WebAudio-specific thread with the elevated priority. Do NOT use
+  // for any other purpose.
+  static std::unique_ptr<Thread> CreateWebAudioThread();
+
+  // Create and save (as a global variable) the compositor thread. The thread
+  // will be accessible through CompositorThread().
+  static void CreateAndSetCompositorThread();
+
+  // Return an interface to the current thread.
+  static Thread* Current();
+
+  // Return an interface to the main thread.
+  static Thread* MainThread();
+
+  // Return an interface to the compositor thread (if initialized). This can be
+  // null if the renderer was created with threaded rendering disabled.
+  static Thread* CompositorThread();
+
   // DEPRECATED: Returns a task runner bound to the underlying scheduler's
   // default task queue.
   //
@@ -118,6 +141,13 @@
   virtual ~Thread() = default;
 
  private:
+  // For Platform and ScopedMainThreadOverrider. Return the thread object
+  // previously set (if any).
+  //
+  // This is done this way because we need to be able to "override" the main
+  // thread temporarily for ScopedTestingPlatformSupport.
+  static std::unique_ptr<Thread> SetMainThread(std::unique_ptr<Thread>);
+
   // This is used to identify the actual Thread instance. This should be
   // used only in Platform, and other users should ignore this.
   virtual bool IsSimpleMainThread() const { return false; }
diff --git a/third_party/blink/renderer/platform/testing/scoped_main_thread_overrider.cc b/third_party/blink/renderer/platform/testing/scoped_main_thread_overrider.cc
new file mode 100644
index 0000000..cc7d56f
--- /dev/null
+++ b/third_party/blink/renderer/platform/testing/scoped_main_thread_overrider.cc
@@ -0,0 +1,17 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/testing/scoped_main_thread_overrider.h"
+
+namespace blink {
+
+ScopedMainThreadOverrider::ScopedMainThreadOverrider(
+    std::unique_ptr<Thread> main_thread)
+    : original_main_thread_(Thread::SetMainThread(std::move(main_thread))) {}
+
+ScopedMainThreadOverrider::~ScopedMainThreadOverrider() {
+  Thread::SetMainThread(std::move(original_main_thread_));
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/testing/scoped_main_thread_overrider.h b/third_party/blink/renderer/platform/testing/scoped_main_thread_overrider.h
new file mode 100644
index 0000000..dbe1719
--- /dev/null
+++ b/third_party/blink/renderer/platform/testing/scoped_main_thread_overrider.h
@@ -0,0 +1,32 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_SCOPED_MAIN_THREAD_OVERRIDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_SCOPED_MAIN_THREAD_OVERRIDER_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/scheduler/public/thread.h"
+
+namespace blink {
+
+// This class instance lets you override the main thread (of type blink::Thread)
+// temporarily. This is useful when you want to change the main thread scheduler
+// during a test (see ScopedSchedulerOverrider).
+//
+// When ScopedMainThreadOverrider goes out of scope, the main thread is
+// reverted back to the original one, and the main thread object passed in
+// the constructor gets destructed.
+
+class ScopedMainThreadOverrider final {
+ public:
+  explicit ScopedMainThreadOverrider(std::unique_ptr<Thread> main_thread);
+  ~ScopedMainThreadOverrider();
+
+ private:
+  std::unique_ptr<Thread> original_main_thread_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_SCOPED_MAIN_THREAD_OVERRIDER_H_
diff --git a/third_party/blink/renderer/platform/testing/scoped_scheduler_overrider.cc b/third_party/blink/renderer/platform/testing/scoped_scheduler_overrider.cc
new file mode 100644
index 0000000..82ff3d3
--- /dev/null
+++ b/third_party/blink/renderer/platform/testing/scoped_scheduler_overrider.cc
@@ -0,0 +1,37 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/testing/scoped_scheduler_overrider.h"
+
+#include "third_party/blink/renderer/platform/wtf/wtf.h"
+
+namespace blink {
+
+namespace {
+
+class ThreadWithCustomScheduler : public Thread {
+ public:
+  explicit ThreadWithCustomScheduler(ThreadScheduler* scheduler)
+      : scheduler_(scheduler) {}
+  ~ThreadWithCustomScheduler() override {}
+
+  bool IsCurrentThread() const override {
+    DCHECK(WTF::IsMainThread());
+    return true;
+  }
+  ThreadScheduler* Scheduler() override { return scheduler_; }
+
+ private:
+  ThreadScheduler* scheduler_;
+};
+
+}  // namespace
+
+ScopedSchedulerOverrider::ScopedSchedulerOverrider(ThreadScheduler* scheduler)
+    : main_thread_overrider_(
+          std::make_unique<ThreadWithCustomScheduler>(scheduler)) {}
+
+ScopedSchedulerOverrider::~ScopedSchedulerOverrider() {}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/testing/scoped_scheduler_overrider.h b/third_party/blink/renderer/platform/testing/scoped_scheduler_overrider.h
new file mode 100644
index 0000000..7f46ea3
--- /dev/null
+++ b/third_party/blink/renderer/platform/testing/scoped_scheduler_overrider.h
@@ -0,0 +1,30 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_SCOPED_SCHEDULER_OVERRIDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_SCOPED_SCHEDULER_OVERRIDER_H_
+
+#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
+#include "third_party/blink/renderer/platform/testing/scoped_main_thread_overrider.h"
+
+namespace blink {
+
+// A utility which lets you inject your custom implementation of ThreadScheduler
+// on the main thread. This class sets up a very simple instance of
+// blink::Thread and overrides the global main thread using ScopedMainThread-
+// Overrider. Multi-thread is not supported.
+
+class ScopedSchedulerOverrider final {
+ public:
+  // |scheduler| must be owned by the caller.
+  explicit ScopedSchedulerOverrider(ThreadScheduler* scheduler);
+  ~ScopedSchedulerOverrider();
+
+ private:
+  ScopedMainThreadOverrider main_thread_overrider_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_SCOPED_SCHEDULER_OVERRIDER_H_
diff --git a/third_party/blink/renderer/platform/testing/testing_platform_support.cc b/third_party/blink/renderer/platform/testing/testing_platform_support.cc
index 3eea503..ebceac70 100644
--- a/third_party/blink/renderer/platform/testing/testing_platform_support.cc
+++ b/third_party/blink/renderer/platform/testing/testing_platform_support.cc
@@ -96,7 +96,6 @@
       interface_provider_(new TestingInterfaceProvider) {
   DCHECK(old_platform_);
   DCHECK(WTF::IsMainThread());
-  main_thread_ = old_platform_->CurrentThread();
 }
 
 TestingPlatformSupport::~TestingPlatformSupport() {
@@ -159,12 +158,22 @@
   base::DiscardableMemoryAllocator::SetInstance(
       discardable_memory_allocator_.get());
 
+  // TODO(yutak): The initialization steps below are essentially a subset of
+  // Platform::Initialize() steps with a few modifications for tests.
+  // We really shouldn't have those initialization steps in two places,
+  // because they are a very fragile piece of code (the initialization order
+  // is so sensitive) and we want it to be consistent between tests and
+  // production. Fix this someday.
   dummy_platform_ = std::make_unique<Platform>();
   Platform::SetCurrentPlatformForTesting(dummy_platform_.get());
 
   WTF::Partitions::Initialize(nullptr);
   WTF::Initialize(nullptr);
 
+  // This must be called after WTF::Initialize(), because ThreadSpecific<>
+  // used in this function depends on WTF::IsMainThread().
+  Platform::CreateMainThreadForTesting();
+
   testing_platform_support_ = std::make_unique<TestingPlatformSupport>();
   Platform::SetCurrentPlatformForTesting(testing_platform_support_.get());
 
diff --git a/third_party/blink/renderer/platform/testing/testing_platform_support_with_custom_scheduler.cc b/third_party/blink/renderer/platform/testing/testing_platform_support_with_custom_scheduler.cc
deleted file mode 100644
index 1a32b34..0000000
--- a/third_party/blink/renderer/platform/testing/testing_platform_support_with_custom_scheduler.cc
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_custom_scheduler.h"
-
-#include "third_party/blink/renderer/platform/scheduler/public/thread.h"
-#include "third_party/blink/renderer/platform/wtf/wtf.h"
-
-namespace blink {
-
-namespace {
-
-class ThreadWithCustomScheduler : public Thread {
- public:
-  explicit ThreadWithCustomScheduler(ThreadScheduler* scheduler)
-      : scheduler_(scheduler) {}
-  ~ThreadWithCustomScheduler() override {}
-
-  bool IsCurrentThread() const override {
-    DCHECK(WTF::IsMainThread());
-    return true;
-  }
-  ThreadScheduler* Scheduler() override { return scheduler_; }
-
- private:
-  ThreadScheduler* scheduler_;
-};
-
-}  // namespace
-
-TestingPlatformSupportWithCustomScheduler::
-    TestingPlatformSupportWithCustomScheduler(ThreadScheduler* scheduler)
-    : thread_(std::make_unique<ThreadWithCustomScheduler>(scheduler)) {
-  // If main_thread_ is set, Platform::SetCurrentPlatformForTesting() properly
-  // sets up the platform so Platform::CurrentThread() would return the
-  // thread specified here.
-  main_thread_ = thread_.get();
-}
-
-TestingPlatformSupportWithCustomScheduler::
-    ~TestingPlatformSupportWithCustomScheduler() {}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/platform/testing/testing_platform_support_with_custom_scheduler.h b/third_party/blink/renderer/platform/testing/testing_platform_support_with_custom_scheduler.h
deleted file mode 100644
index 604b8a3..0000000
--- a/third_party/blink/renderer/platform/testing/testing_platform_support_with_custom_scheduler.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_TESTING_PLATFORM_SUPPORT_WITH_CUSTOM_SCHEDULER_H_
-#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_TESTING_PLATFORM_SUPPORT_WITH_CUSTOM_SCHEDULER_H_
-
-#include <memory>
-#include "base/macros.h"
-#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
-#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
-
-// Test environment where you can inject your custom implementation of
-// ThreadScheduler on the main thread. Multi-thread is not supported.
-//
-// You would probably like to use this with ScopedTestingPlatformSupport
-// class template. See testing_platform_support.h for details.
-
-namespace blink {
-
-class TestingPlatformSupportWithCustomScheduler
-    : public TestingPlatformSupport {
- public:
-  // |scheduler| must be owned by the caller.
-  explicit TestingPlatformSupportWithCustomScheduler(
-      ThreadScheduler* scheduler);
-  ~TestingPlatformSupportWithCustomScheduler() override;
-
- private:
-  std::unique_ptr<Thread> thread_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestingPlatformSupportWithCustomScheduler);
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_TESTING_PLATFORM_SUPPORT_WITH_CUSTOM_SCHEDULER_H_
diff --git a/third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.cc b/third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.cc
index 0fdccdce..7f0f68e7 100644
--- a/third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.cc
+++ b/third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.cc
@@ -27,8 +27,8 @@
 
   scheduler_ = std::make_unique<scheduler::MainThreadSchedulerImpl>(
       std::move(sequence_manager), base::nullopt);
-  thread_ = scheduler_->CreateMainThread();
-  main_thread_ = thread_.get();
+  main_thread_overrider_ = std::make_unique<ScopedMainThreadOverrider>(
+      scheduler_->CreateMainThread());
   // Set the work batch size to one so TakePendingTasks behaves as expected.
   scheduler_->GetSchedulerHelperForTesting()->SetWorkBatchSizeForTesting(1);
 
diff --git a/third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h b/third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h
index 46027c9..f2b22e5c1 100644
--- a/third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h
+++ b/third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h
@@ -8,6 +8,7 @@
 #include <memory>
 #include "base/test/test_mock_time_task_runner.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
+#include "third_party/blink/renderer/platform/testing/scoped_main_thread_overrider.h"
 #include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
 #include "third_party/blink/renderer/platform/wtf/noncopyable.h"
 
@@ -36,6 +37,8 @@
   TestingPlatformSupportWithMockScheduler();
   ~TestingPlatformSupportWithMockScheduler() override;
 
+  std::unique_ptr<Thread> CreateMainThread();
+
   scoped_refptr<base::TestMockTimeTaskRunner> test_task_runner() {
     return test_task_runner_;
   }
@@ -77,7 +80,7 @@
   std::unique_ptr<scheduler::MainThreadSchedulerImpl> scheduler_;
   base::sequence_manager::SequenceManager*
       sequence_manager_;  // Owned by scheduler_.
-  std::unique_ptr<Thread> thread_;
+  std::unique_ptr<ScopedMainThreadOverrider> main_thread_overrider_;
 };
 
 }  // namespace blink
diff --git a/tools/determinism/deterministic_build_whitelist.pyl b/tools/determinism/deterministic_build_whitelist.pyl
index 804fbcb..cebec61 100644
--- a/tools/determinism/deterministic_build_whitelist.pyl
+++ b/tools/determinism/deterministic_build_whitelist.pyl
@@ -207,13 +207,9 @@
 
   # https://crbug.com/330260
   'win': [
-    # These two targets force symbol_level to be at least 1, they will work
-    # once we've figured out the PDB aging field.
-    'crashpad_snapshot_test_image_reader.exe',
-    'crashpad_snapshot_test_image_reader_module.dll',
-
-    # TODO(thakis): Figure out what's up with these two.
+    # TODO(thakis): Figure out what's up with these three.
     'mini_installer.exe',
+    'mini_installer.exe.pdb',
     'previous_version_mini_installer.exe',
 
     # These probably have mtimes in the zip headers and the scripts creating
@@ -245,7 +241,6 @@
     'content_browsertests.isolated',
     'content_shell_crash_test.isolated',
     'content_unittests.isolated',
-    'crashpad_tests.isolated',
     'extensions_browsertests.isolated',
     'extensions_unittests.isolated',
     'gin_unittests.isolated',
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 86138113..00998ee 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -21401,6 +21401,7 @@
   <int value="10" label="Toggle show hidden Android folders (on)"/>
   <int value="11" label="Toggle show hidden Android folders (off)"/>
   <int value="12" label="Share with Linux"/>
+  <int value="13" label="Manage Linux sharing"/>
 </enum>
 
 <enum name="FileManagerQuickViewWayToOpen">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index cf23b6cb..0eb1847 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -98535,6 +98535,39 @@
   </summary>
 </histogram>
 
+<histogram name="SignedExchange.Prefetch.LoadResult"
+    enum="SignedExchangeLoadResult" expires_after="2019-10-11">
+  <owner>kinuko@chromium.org</owner>
+  <owner>kouhei@chromium.org</owner>
+  <summary>
+    Records if the prefetched Signed Exchange was properly formatted and passed
+    verification steps. Reported for each completed SignedExchange prefetch.
+  </summary>
+</histogram>
+
+<histogram name="SignedExchange.Prefetch.Precision.30Seconds"
+    enum="BooleanUsage" expires_after="2019-10-11">
+  <owner>kinuko@chromium.org</owner>
+  <owner>kouhei@chromium.org</owner>
+  <summary>
+    Records if the prefetched Signed Exchange was actually the target of a
+    navigation which happened within 30 seconds. Reported when a corresponding
+    navigation is observed, or from an periodic timer event which cleans up the
+    unmatched prefetch entries.
+  </summary>
+</histogram>
+
+<histogram name="SignedExchange.Prefetch.Recall.30Seconds" enum="BooleanUsage"
+    expires_after="2019-10-11">
+  <owner>kinuko@chromium.org</owner>
+  <owner>kouhei@chromium.org</owner>
+  <summary>
+    Records how much Signed Exchange navigations were prefetched and not
+    prefetched. Matched against 30 seconds window. Reported for each Signed
+    Exchange navigations.
+  </summary>
+</histogram>
+
 <histogram name="SignedExchange.SignatureVerificationError.Expired"
     units="seconds" expires_after="2019-09-20">
   <owner>kinuko@chromium.org</owner>
@@ -124061,6 +124094,9 @@
 </histogram_suffixes>
 
 <histogram_suffixes name="DataUse.Service.Types" separator=".">
+  <obsolete>
+    Deprecated Oct 2018
+  </obsolete>
   <suffix name="Autofill"/>
   <suffix name="DomainReliability"/>
   <suffix name="GAIA"/>
diff --git a/ui/file_manager/base/js/BUILD.gn b/ui/file_manager/base/js/BUILD.gn
index b717981..db61614 100644
--- a/ui/file_manager/base/js/BUILD.gn
+++ b/ui/file_manager/base/js/BUILD.gn
@@ -6,12 +6,25 @@
 
 visibility = [ "//ui/file_manager/*" ]
 
-js_type_check("closure_compile") {
+group("closure_compile") {
+  deps = [
+    ":closure_compile_module",
+    ":test_support_type_check",
+  ]
+}
+
+js_type_check("closure_compile_module") {
   deps = [
     ":filtered_volume_manager",
   ]
 }
 
+js_type_check("test_support_type_check") {
+  deps = [
+    ":test_error_reporting",
+  ]
+}
+
 js_library("filtered_volume_manager") {
   deps = [
     "//ui/file_manager/externs:file_manager_private",
@@ -25,3 +38,11 @@
   externs_list =
       [ "//ui/file_manager/externs/background/volume_manager_factory.js" ]
 }
+
+js_library("test_error_reporting") {
+  deps = [
+    # Note we allow targets depending on test_error_reporting to access
+    # webui_resource_test transitively.
+    "//ui/webui/resources/js:webui_resource_test",
+  ]
+}
diff --git a/ui/file_manager/base/js/test_error_reporting.js b/ui/file_manager/base/js/test_error_reporting.js
new file mode 100644
index 0000000..a2798f3
--- /dev/null
+++ b/ui/file_manager/base/js/test_error_reporting.js
@@ -0,0 +1,42 @@
+// 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.
+
+/**
+ * Invokes a callback function depending on the result of promise.
+ *
+ * @param {Promise} promise Promise.
+ * @param {function(boolean)} callback Callback function. True is passed if the
+ *     test failed.
+ */
+function reportPromise(promise, callback) {
+  promise.then(
+      () => {
+        callback(/* error */ false);
+      },
+      (/** Error */ error) => {
+        console.error(error.stack || error);
+        callback(/* error */ true);
+      });
+}
+
+/**
+ * Waits until testFunction becomes true.
+ * @param {function(): boolean} testFunction A function which is tested.
+ * @return {!Promise} A promise which is fulfilled when the testFunction
+ *     becomes true.
+ */
+function waitUntil(testFunction) {
+  const INTERVAL_FOR_WAIT_UNTIL = 100;  // ms
+
+  return new Promise((resolve) => {
+    let tryTestFunction = () => {
+      if (testFunction())
+        resolve();
+      else
+        setTimeout(tryTestFunction, INTERVAL_FOR_WAIT_UNTIL);
+    };
+
+    tryTestFunction();
+  });
+}
diff --git a/ui/file_manager/file_manager/background/js/device_handler_unittest.html b/ui/file_manager/file_manager/background/js/device_handler_unittest.html
index b3027af1..ae81772c 100644
--- a/ui/file_manager/file_manager/background/js/device_handler_unittest.html
+++ b/ui/file_manager/file_manager/background/js/device_handler_unittest.html
@@ -15,6 +15,7 @@
 
 <!-- Need to load async_util.js before device_handler.js -->
 <script src="../../common/js/async_util.js"></script>
+<script src="../../../base/js/test_error_reporting.js"></script>
 <script src="../../common/js/unittest_util.js"></script>
 <script src="../../common/js/mock_entry.js"></script>
 <script src="../../common/js/volume_manager_common.js"></script>
diff --git a/ui/file_manager/file_manager/background/js/duplicate_finder_unittest.html b/ui/file_manager/file_manager/background/js/duplicate_finder_unittest.html
index ca77aa9..14fac76 100644
--- a/ui/file_manager/file_manager/background/js/duplicate_finder_unittest.html
+++ b/ui/file_manager/file_manager/background/js/duplicate_finder_unittest.html
@@ -16,6 +16,7 @@
   <script src="../../common/js/lru_cache.js"></script>
   <script src="../../common/js/mock_entry.js"></script>
   <script src="../../common/js/test_tracker.js"></script>
+  <script src="../../../base/js/test_error_reporting.js"></script>
   <script src="../../common/js/unittest_util.js"></script>
   <script src="../../common/js/files_app_entry_types.js"></script>
   <script src="../../common/js/util.js"></script>
diff --git a/ui/file_manager/file_manager/background/js/file_operation_manager_unittest.html b/ui/file_manager/file_manager/background/js/file_operation_manager_unittest.html
index 3c04a2d..7dd9c2b 100644
--- a/ui/file_manager/file_manager/background/js/file_operation_manager_unittest.html
+++ b/ui/file_manager/file_manager/background/js/file_operation_manager_unittest.html
@@ -11,7 +11,7 @@
 <script src="../../../../../ui/webui/resources/js/cr/event_target.js"></script>
 
 <script src="../../common/js/async_util.js"></script>
-<script src="../../common/js/unittest_util.js"></script>
+<script src="../../../base/js/test_error_reporting.js"></script>
 <script src="../../common/js/util.js"></script>
 <script src="../../common/js/lru_cache.js"></script>
 <script src="../../common/js/mock_entry.js"></script>
diff --git a/ui/file_manager/file_manager/background/js/import_history_unittest.html b/ui/file_manager/file_manager/background/js/import_history_unittest.html
index 2e7b6c0..2204a6a 100644
--- a/ui/file_manager/file_manager/background/js/import_history_unittest.html
+++ b/ui/file_manager/file_manager/background/js/import_history_unittest.html
@@ -17,6 +17,7 @@
 <script src="../../common/js/metrics_base.js"></script>
 <script src="../../common/js/metrics_events.js"></script>
 <script src="../../common/js/mock_entry.js"></script>
+<script src="../../../base/js/test_error_reporting.js"></script>
 <script src="../../common/js/unittest_util.js"></script>
 <script src="../../common/js/util.js"></script>
 
diff --git a/ui/file_manager/file_manager/background/js/media_import_handler_unittest.html b/ui/file_manager/file_manager/background/js/media_import_handler_unittest.html
index 993c317..7469576 100644
--- a/ui/file_manager/file_manager/background/js/media_import_handler_unittest.html
+++ b/ui/file_manager/file_manager/background/js/media_import_handler_unittest.html
@@ -17,6 +17,7 @@
   <script src="../../common/js/metrics_base.js"></script>
   <script src="../../common/js/metrics_events.js"></script>
   <script src="../../common/js/mock_entry.js"></script>
+  <script src="../../../base/js/test_error_reporting.js"></script>
   <script src="../../common/js/unittest_util.js"></script>
   <script src="../../common/js/files_app_entry_types.js"></script>
   <script src="../../common/js/util.js"></script>
diff --git a/ui/file_manager/file_manager/background/js/media_scanner_unittest.html b/ui/file_manager/file_manager/background/js/media_scanner_unittest.html
index 529c8bc..6746b6c 100644
--- a/ui/file_manager/file_manager/background/js/media_scanner_unittest.html
+++ b/ui/file_manager/file_manager/background/js/media_scanner_unittest.html
@@ -17,6 +17,7 @@
 <script src="../../common/js/lru_cache.js"></script>
 <script src="../../common/js/mock_entry.js"></script>
 <script src="../../common/js/mock_file_system.js"></script>
+<script src="../../../base/js/test_error_reporting.js"></script>
 <script src="../../common/js/unittest_util.js"></script>
 <script src="../../common/js/util.js"></script>
 <script src="metadata_proxy.js"></script>
diff --git a/ui/file_manager/file_manager/background/js/task_queue_unittest.html b/ui/file_manager/file_manager/background/js/task_queue_unittest.html
index 8e92ecdc..f609d0c 100644
--- a/ui/file_manager/file_manager/background/js/task_queue_unittest.html
+++ b/ui/file_manager/file_manager/background/js/task_queue_unittest.html
@@ -12,7 +12,7 @@
 
   <script src="../../common/js/volume_manager_common.js"></script>
   <script src="../../common/js/importer_common.js"></script>
-  <script src="../../common/js/unittest_util.js"></script>
+  <script src="../../../base/js/test_error_reporting.js"></script>
   <script src="task_queue.js"></script>
 
   <script src="task_queue_unittest.js"></script>
diff --git a/ui/file_manager/file_manager/background/js/volume_info_impl.js b/ui/file_manager/file_manager/background/js/volume_info_impl.js
index d1ecdc8..3dc28d3 100644
--- a/ui/file_manager/file_manager/background/js/volume_info_impl.js
+++ b/ui/file_manager/file_manager/background/js/volume_info_impl.js
@@ -65,12 +65,12 @@
     this.fakeEntries_[VolumeManagerCommon.RootType.DRIVE_OFFLINE] =
         new FakeEntry(
             str('DRIVE_OFFLINE_COLLECTION_LABEL'),
-            VolumeManagerCommon.RootType.DRIVE_OFFLINE, true);
+            VolumeManagerCommon.RootType.DRIVE_OFFLINE);
 
     this.fakeEntries_[VolumeManagerCommon.RootType.DRIVE_SHARED_WITH_ME] =
         new FakeEntry(
             str('DRIVE_SHARED_WITH_ME_COLLECTION_LABEL'),
-            VolumeManagerCommon.RootType.DRIVE_SHARED_WITH_ME, true);
+            VolumeManagerCommon.RootType.DRIVE_SHARED_WITH_ME);
   }
 
   // Note: This represents if the mounting of the volume is successfully done
diff --git a/ui/file_manager/file_manager/background/js/volume_manager_unittest.html b/ui/file_manager/file_manager/background/js/volume_manager_unittest.html
index 4e3aff7..b584ecc 100644
--- a/ui/file_manager/file_manager/background/js/volume_manager_unittest.html
+++ b/ui/file_manager/file_manager/background/js/volume_manager_unittest.html
@@ -13,6 +13,7 @@
 
   <script src="../../common/js/async_util.js"></script>
   <script src="../../common/js/mock_entry.js"></script>
+  <script src="../../../base/js/test_error_reporting.js"></script>
   <script src="../../common/js/unittest_util.js"></script>
   <script src="../../common/js/files_app_entry_types.js"></script>
   <script src="../../common/js/util.js"></script>
diff --git a/ui/file_manager/file_manager/common/js/BUILD.gn b/ui/file_manager/file_manager/common/js/BUILD.gn
index f4b2fdf..ee9eb4b 100644
--- a/ui/file_manager/file_manager/common/js/BUILD.gn
+++ b/ui/file_manager/file_manager/common/js/BUILD.gn
@@ -8,6 +8,14 @@
 # TODO(tapted): This entire folder should move to //ui/file_manager/base.
 visibility = [ "//ui/file_manager/*" ]
 
+group("closure_compile") {
+  deps = [
+    ":closure_compile_module",
+    ":test_support_type_check",
+    ":unit_tests_type_check",
+  ]
+}
+
 js_type_check("closure_compile_module") {
   deps = [
     ":async_util",
@@ -21,19 +29,25 @@
     ":metrics_events",
     ":mock_entry",
     ":progress_center_common",
-    ":unittest_util",
     ":util",
     ":volume_manager_common",
   ]
 }
 
+js_type_check("test_support_type_check") {
+  deps = [
+    ":test_importer_common",
+    ":unittest_util",
+  ]
+}
+
 js_library("async_util") {
 }
 
 js_library("async_util_unittest") {
   deps = [
     ":async_util",
-    ":unittest_util",
+    "//ui/file_manager/base/js:test_error_reporting",
   ]
 }
 
@@ -50,8 +64,8 @@
 js_library("files_app_entry_types_unittest") {
   deps = [
     ":files_app_entry_types",
-    ":unittest_util",
     ":volume_manager_common",
+    "//ui/file_manager/base/js:test_error_reporting",
   ]
 }
 
@@ -85,6 +99,7 @@
   deps = [
     ":importer_common",
     ":unittest_util",
+    "//ui/file_manager/base/js:test_error_reporting",
   ]
   visibility = []
   visibility = [ "//ui/file_manager/file_manager/*" ]
@@ -141,6 +156,9 @@
 }
 
 js_library("unittest_util") {
+  # Only files app tests use this util file.
+  visibility = []
+  visibility = [ "//ui/file_manager/file_manager/*" ]
   deps = [
     "//ui/webui/resources/js:webui_resource_test",
   ]
@@ -168,8 +186,8 @@
 js_library("util_unittest") {
   deps = [
     ":mock_entry",
-    ":unittest_util",
     ":util",
+    "//ui/file_manager/base/js:test_error_reporting",
   ]
 }
 
@@ -188,17 +206,3 @@
     ":util_unittest",
   ]
 }
-
-js_type_check("test_support_type_check") {
-  deps = [
-    ":test_importer_common",
-  ]
-}
-
-group("closure_compile") {
-  deps = [
-    ":closure_compile_module",
-    ":test_support_type_check",
-    ":unit_tests_type_check",
-  ]
-}
diff --git a/ui/file_manager/file_manager/common/js/files_app_entry_types.js b/ui/file_manager/file_manager/common/js/files_app_entry_types.js
index faad490c..c02b0ff2 100644
--- a/ui/file_manager/file_manager/common/js/files_app_entry_types.js
+++ b/ui/file_manager/file_manager/common/js/files_app_entry_types.js
@@ -487,17 +487,16 @@
  * FakeEntry is used for entries that used only for UI, that weren't generated
  * by FileSystem API, like Drive, Downloads or Provided.
  *
- * @implements FilesAppEntry
+ * @implements FilesAppDirEntry
  */
 class FakeEntry {
   /**
    * @param {string} label Translated text to be displayed to user.
    * @param {!VolumeManagerCommon.RootType} rootType Root type of this entry.
-   * @param {boolean} isDirectory Is this entry a directory-like entry?
    * @param {chrome.fileManagerPrivate.SourceRestriction=} opt_sourceRestriction
    *    used on Recents to filter the source of recent files/directories.
    */
-  constructor(label, rootType, isDirectory, opt_sourceRestriction) {
+  constructor(label, rootType, opt_sourceRestriction) {
     /**
      * @public {string} label: Label to be used when displaying to user, it
      *      should be already translated. */
@@ -509,13 +508,11 @@
     /** @public {!VolumeManagerCommon.RootType} */
     this.rootType = rootType;
 
-    /**
-     * @public {boolean} true if this entry represents a Directory-like entry.
-     */
-    this.isDirectory = isDirectory;
+    /** @public {boolean} true FakeEntry are always directory-like. */
+    this.isDirectory = true;
 
-    /** @public {boolean} true if this entry represents a File-like entry. */
-    this.isFile = !this.isDirectory;
+    /** @public {boolean} false FakeEntry are always directory-like. */
+    this.isFile = false;
 
     /**
      * @public {chrome.fileManagerPrivate.SourceRestriction|undefined} It's used
@@ -564,4 +561,13 @@
   get isNativeType() {
     return false;
   }
+
+  /**
+   * @return {!StaticReader} Returns a reader compatible with
+   * DirectoryEntry.createReader (from Web Standards) that reads 0 entries.
+   * @override
+   */
+  createReader() {
+    return new StaticReader([]);
+  }
 }
diff --git a/ui/file_manager/file_manager/common/js/files_app_entry_types_unittest.js b/ui/file_manager/file_manager/common/js/files_app_entry_types_unittest.js
index ad138317..698dc43 100644
--- a/ui/file_manager/file_manager/common/js/files_app_entry_types_unittest.js
+++ b/ui/file_manager/file_manager/common/js/files_app_entry_types_unittest.js
@@ -328,8 +328,7 @@
  * Test FakeEntry, which is only static data.
  */
 function testFakeEntry(testReportCallback) {
-  let fakeEntry =
-      new FakeEntry('label', VolumeManagerCommon.RootType.CROSTINI, true);
+  let fakeEntry = new FakeEntry('label', VolumeManagerCommon.RootType.CROSTINI);
 
   assertEquals(undefined, fakeEntry.sourceRestriction);
   assertEquals('FakeEntry', fakeEntry.type_name);
@@ -342,15 +341,12 @@
   assertTrue(fakeEntry.isDirectory);
   assertFalse(fakeEntry.isFile);
 
-  // Check the isDirectory and sourceRestriction constructor args.
+  // Check sourceRestriction constructor args.
   const kSourceRestriction =
       /** @type{chrome.fileManagerPrivate.SourceRestriction} */ ('fake');
   fakeEntry = new FakeEntry(
-      'label', VolumeManagerCommon.RootType.CROSTINI, false,
-      kSourceRestriction);
+      'label', VolumeManagerCommon.RootType.CROSTINI, kSourceRestriction);
   assertEquals(kSourceRestriction, fakeEntry.sourceRestriction);
-  assertFalse(fakeEntry.isDirectory);
-  assertTrue(fakeEntry.isFile);
 
   let callCounter = 0;
 
diff --git a/ui/file_manager/file_manager/common/js/metrics_unittest.html b/ui/file_manager/file_manager/common/js/metrics_unittest.html
index 9d98392..8e0ab645 100644
--- a/ui/file_manager/file_manager/common/js/metrics_unittest.html
+++ b/ui/file_manager/file_manager/common/js/metrics_unittest.html
@@ -11,15 +11,11 @@
   <script src="../../../../../ui/webui/resources/js/cr/event_target.js"></script>
   <script src="../../../../../ui/webui/resources/js/cr/ui/array_data_model.js"></script>
   <script src="../../../../../ui/webui/resources/js/load_time_data.js"></script>
-  
   <script src="../../../../../third_party/analytics/google-analytics-bundle.js"></script>
-  
-  <script src="unittest_util.js"></script>
+  <script src="../../../../file_manager/base/js/test_error_reporting.js"></script>
   <script src="util.js"></script>
   <script src="metrics_base.js"></script>
   <script src="metrics.js"></script>
-  
-  
   <script src="metrics_unittest.js"></script>
 </body>
 </html>
diff --git a/ui/file_manager/file_manager/common/js/unittest_util.js b/ui/file_manager/file_manager/common/js/unittest_util.js
index 413a05b0..b0c4388b 100644
--- a/ui/file_manager/file_manager/common/js/unittest_util.js
+++ b/ui/file_manager/file_manager/common/js/unittest_util.js
@@ -2,45 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-var INTERVAL_FOR_WAIT_UNTIL = 100; // ms
-
-/**
- * Invokes a callback function depending on the result of promise.
- *
- * @param {Promise} promise Promise.
- * @param {function(boolean)} callback Callback function. True is passed if the
- *     test failed.
- */
-function reportPromise(promise, callback) {
-  promise.then(
-      function() {
-        callback(/* error */ false);
-      },
-      function(/** @type {Error} */ error) {
-        console.error(error.stack || error);
-        callback(/* error */ true);
-      });
-}
-
-/**
- * Waits until testFunction becomes true.
- * @param {function(): boolean} testFunction A function which is tested.
- * @return {!Promise} A promise which is fullfilled when the testFunction
- *     becomes true.
- */
-function waitUntil(testFunction) {
-  return new Promise(function(resolve) {
-    var tryTestFunction = function() {
-      if (testFunction())
-        resolve();
-      else
-        setTimeout(tryTestFunction, INTERVAL_FOR_WAIT_UNTIL);
-    };
-
-    tryTestFunction();
-  });
-}
-
 /**
  * Asserts that two lists contain the same set of Entries.  Entries are deemed
  * to be the same if they point to the same full path.
@@ -49,7 +10,6 @@
  * @param {!Array<!FileEntry>} actual
  */
 function assertFileEntryListEquals(expected, actual) {
-
   var entryToPath = function(entry) {
     assertTrue(entry.isFile);
     return entry.fullPath;
@@ -77,13 +37,11 @@
   expectedPaths = expectedPaths.slice();
   expectedPaths.sort();
 
-  assertEquals(
-      JSON.stringify(expectedPaths),
-      JSON.stringify(actualPaths));
+  assertEquals(JSON.stringify(expectedPaths), JSON.stringify(actualPaths));
 }
 
 /**
- * A class that captures calls to a funtion so that values can be validated.
+ * A class that captures calls to a function so that values can be validated.
  * For use in tests only.
  *
  * <p>Example:
@@ -134,9 +92,8 @@
  *    or null if the recorder hasn't been called.
  */
 TestCallRecorder.prototype.getLastArguments = function() {
-  return (this.calls_.length === 0) ?
-      null :
-      this.calls_[this.calls_.length - 1];
+  return (this.calls_.length === 0) ? null :
+                                      this.calls_[this.calls_.length - 1];
 };
 
 /**
@@ -145,47 +102,7 @@
  *    by indexd.
  */
 TestCallRecorder.prototype.getArguments = function(index) {
-  return (index < this.calls_.length) ?
-      this.calls_[index] :
-      null;
-};
-
-/**
- * @constructor
- * @struct
- */
-function MockAPIEvent() {
-  /**
-   * @type {!Array<!Function>}
-   * @const
-   */
-  this.listeners_ = [];
-}
-
-/**
- * @param {!Function} callback
- */
-MockAPIEvent.prototype.addListener = function(callback) {
-  this.listeners_.push(callback);
-};
-
-/**
- * @param {!Function} callback
- */
-MockAPIEvent.prototype.removeListener = function(callback) {
-  var index = this.listeners_.indexOf(callback);
-  if (index < 0)
-    throw new Error('Tried to remove an unregistered listener.');
-  this.listeners_.splice(index, 1);
-};
-
-/**
- * @param {...*} var_args
- */
-MockAPIEvent.prototype.dispatch = function(var_args) {
-  for (var i = 0; i < this.listeners_.length; i++) {
-    this.listeners_[i].apply(null, arguments);
-  }
+  return (index < this.calls_.length) ? this.calls_[index] : null;
 };
 
 /**
@@ -204,7 +121,7 @@
   window.chrome.storage = {
     local: {
       get: this.get_.bind(this),
-      set: this.set_.bind(this)
+      set: this.set_.bind(this),
     }
   };
 }
@@ -217,11 +134,10 @@
 MockChromeStorageAPI.prototype.get_ = function(keys, callback) {
   var keys = keys instanceof Array ? keys : [keys];
   var result = {};
-  keys.forEach(
-      function(key) {
-        if (key in this.state)
-          result[key] = this.state[key];
-      }.bind(this));
+  keys.forEach((key) => {
+    if (key in this.state)
+      result[key] = this.state[key];
+  });
   callback(result);
 };
 
diff --git a/ui/file_manager/file_manager/foreground/elements/files_tooltip_unittest.html b/ui/file_manager/file_manager/foreground/elements/files_tooltip_unittest.html
index 35eab5a..07e5bcf 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_tooltip_unittest.html
+++ b/ui/file_manager/file_manager/foreground/elements/files_tooltip_unittest.html
@@ -43,5 +43,5 @@
 
 <script src="../../../../webui/resources/js/assert.js"></script>
 <script src="../../../../webui/resources/js/util.js"></script>
-<script src="../../common/js/unittest_util.js"></script>
+<script src="../../../base/js/test_error_reporting.js"></script>
 <script src="files_tooltip_unittest.js"></script>
diff --git a/ui/file_manager/file_manager/foreground/js/BUILD.gn b/ui/file_manager/file_manager/foreground/js/BUILD.gn
index 2823093..7f8dffa 100644
--- a/ui/file_manager/file_manager/foreground/js/BUILD.gn
+++ b/ui/file_manager/file_manager/foreground/js/BUILD.gn
@@ -580,8 +580,7 @@
   deps = [
     ":thumbnail_loader",
     "../../common/js:mock_entry",
-    "../../common/js:unittest_util",
-    "//ui/webui/resources/js:webui_resource_test",
+    "//ui/file_manager/base/js:test_error_reporting",
   ]
 }
 
diff --git a/ui/file_manager/file_manager/foreground/js/actions_model_unittest.html b/ui/file_manager/file_manager/foreground/js/actions_model_unittest.html
index 4e16461..73c607e1 100644
--- a/ui/file_manager/file_manager/foreground/js/actions_model_unittest.html
+++ b/ui/file_manager/file_manager/foreground/js/actions_model_unittest.html
@@ -13,6 +13,7 @@
 <script src="../../foreground/js/metadata/mock_metadata.js"></script>
 
 <script src="../../common/js/mock_entry.js"></script>
+<script src="../../../base/js/test_error_reporting.js"></script>
 <script src="../../common/js/unittest_util.js"></script>
 <script src="../../common/js/files_app_entry_types.js"></script>
 <script src="../../common/js/util.js"></script>
diff --git a/ui/file_manager/file_manager/foreground/js/directory_model.js b/ui/file_manager/file_manager/foreground/js/directory_model.js
index 559e59ea..c8180c9e 100644
--- a/ui/file_manager/file_manager/foreground/js/directory_model.js
+++ b/ui/file_manager/file_manager/foreground/js/directory_model.js
@@ -875,7 +875,7 @@
     // new one.
     if (util.isSameEntry(oldEntry, this.getCurrentDirEntry())) {
       this.changeDirectoryEntry(
-          /** @type {!DirectoryEntry|!FakeEntry} */ (newEntry));
+          /** @type {!DirectoryEntry|!FilesAppDirEntry} */ (newEntry));
     }
 
     // Replace the old item with the new item. oldEntry instance itself may
@@ -960,13 +960,18 @@
  * activateDirectoryEntry instead of this, which is higher-level function and
  * cares about the selection.
  *
- * @param {!DirectoryEntry|!FakeEntry} dirEntry The entry of the new directory
- *     to be opened.
+ * @param {!DirectoryEntry|!FilesAppDirEntry} dirEntry The entry of the new
+ *     directory to be opened.
  * @param {function()=} opt_callback Executed if the directory loads
  *     successfully.
  */
 DirectoryModel.prototype.changeDirectoryEntry = function(
     dirEntry, opt_callback) {
+  // If it's a VolumeEntry which wraps an actual entry, we should use the
+  // unwrapped entry.
+  if (dirEntry instanceof VolumeEntry)
+    dirEntry = assert(dirEntry.rootEntry);
+
   // Increment the sequence value.
   this.changeDirectorySequence_++;
   this.clearSearch_();
@@ -1082,8 +1087,8 @@
  *    directory.
  *  - Clears the selection, if the given directory is the current directory.
  *
- * @param {!DirectoryEntry|!FakeEntry} dirEntry The entry of the new directory
- *     to be opened.
+ * @param {!DirectoryEntry|!FilesAppDirEntry} dirEntry The entry of the new
+ *     directory to be opened.
  * @param {function()=} opt_callback Executed if the directory loads
  *     successfully.
  */
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager.js b/ui/file_manager/file_manager/foreground/js/file_manager.js
index 0bca2f7..f73e00b 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager.js
@@ -1207,7 +1207,7 @@
                 str('RECENT_ROOT_LABEL'), NavigationModelItemType.RECENT,
                 new FakeEntry(
                     str('RECENT_ROOT_LABEL'),
-                    VolumeManagerCommon.RootType.RECENT, true,
+                    VolumeManagerCommon.RootType.RECENT,
                     this.getSourceRestriction_())) :
             null,
         this.commandLineFlags_['disable-my-files-navigation']);
@@ -1220,25 +1220,25 @@
    * @private
    */
   FileManager.prototype.setupCrostini_ = function() {
-    chrome.fileManagerPrivate.isCrostiniEnabled((enabled) => {
+    chrome.fileManagerPrivate.isCrostiniEnabled((crostiniEnabled) => {
       // Check for 'crostini-files' cmd line flag.
       chrome.commandLinePrivate.hasSwitch('crostini-files', (filesEnabled) => {
-        Crostini.IS_CROSTINI_FILES_ENABLED = filesEnabled;
+        Crostini.IS_CROSTINI_FILES_ENABLED = crostiniEnabled && filesEnabled;
       });
 
       // Setup Linux files fake root.
-      this.directoryTree.dataModel.linuxFilesItem = enabled ?
+      this.directoryTree.dataModel.linuxFilesItem = crostiniEnabled ?
           new NavigationModelFakeItem(
               str('LINUX_FILES_ROOT_LABEL'), NavigationModelItemType.CROSTINI,
               new FakeEntry(
                   str('LINUX_FILES_ROOT_LABEL'),
-                  VolumeManagerCommon.RootType.CROSTINI, true)) :
+                  VolumeManagerCommon.RootType.CROSTINI)) :
           null;
 
       // Redraw the tree even if not enabled.  This is required for testing.
       this.directoryTree.redraw(false);
 
-      if (!enabled)
+      if (!crostiniEnabled)
         return;
 
       // Load any existing shared paths.
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
index f8ddc3a..589a5a3 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
@@ -395,6 +395,7 @@
   HIDDEN_ANDROID_FOLDERS_SHOW: 'toggle-hidden-android-folders-on',
   HIDDEN_ANDROID_FOLDERS_HIDE: 'toggle-hidden-android-folders-off',
   SHARE_WITH_LINUX: 'share-with-linux',
+  MANAGE_LINUX_SHARING: 'manage-linux-sharing',
 };
 
 /**
@@ -420,6 +421,7 @@
   CommandHandler.MenuCommandsForUMA.HIDDEN_ANDROID_FOLDERS_SHOW,
   CommandHandler.MenuCommandsForUMA.HIDDEN_ANDROID_FOLDERS_HIDE,
   CommandHandler.MenuCommandsForUMA.SHARE_WITH_LINUX,
+  CommandHandler.MenuCommandsForUMA.MANAGE_LINUX_SHARING,
 ];
 console.assert(
     Object.keys(CommandHandler.MenuCommandsForUMA).length ===
@@ -1674,6 +1676,31 @@
 });
 
 /**
+ * Link to settings page to manage files and folders shared with crostini
+ * container.
+ * @type {Command}
+ */
+CommandHandler.COMMANDS_['manage-linux-sharing'] = /** @type {Command} */ ({
+  /**
+   * @param {!Event} event Command event.
+   * @param {!CommandHandlerDeps} fileManager CommandHandlerDeps to use.
+   */
+  execute: function(event, fileManager) {
+    chrome.fileManagerPrivate.openSettingsSubpage('crostini/sharedPaths');
+    CommandHandler.recordMenuItemSelected_(
+        CommandHandler.MenuCommandsForUMA.MANAGE_LINUX_SHARING);
+  },
+  /**
+   * @param {!Event} event Command event.
+   * @param {!CommandHandlerDeps} fileManager CommandHandlerDeps to use.
+   */
+  canExecute: function(event, fileManager) {
+    event.canExecute = Crostini.IS_CROSTINI_FILES_ENABLED;
+    event.command.setHidden(!event.canExecute);
+  }
+});
+
+/**
  * Creates a shortcut of the selected folder (single only).
  * @type {Command}
  */
diff --git a/ui/file_manager/file_manager/foreground/js/file_tasks_unittest.html b/ui/file_manager/file_manager/foreground/js/file_tasks_unittest.html
index 2adea43..05aa0ee 100644
--- a/ui/file_manager/file_manager/foreground/js/file_tasks_unittest.html
+++ b/ui/file_manager/file_manager/foreground/js/file_tasks_unittest.html
@@ -10,7 +10,7 @@
 <script src="../../common/js/volume_manager_common.js"></script>
 <script src="../../common/js/mock_entry.js"></script>
 <script src="../../common/js/file_type.js"></script>
-<script src="../../common/js/unittest_util.js"></script>
+<script src="../../../base/js/test_error_reporting.js"></script>
 <script src="../../common/js/util.js"></script>
 
 <script src="constants.js"></script>
diff --git a/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js b/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js
index 696a254..d904d435 100644
--- a/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js
@@ -167,7 +167,7 @@
       queryRequiredElement('command#cut', this.document_));
 
   /**
-   * @private {DirectoryEntry|FakeEntry}
+   * @private {DirectoryEntry|FilesAppDirEntry}
    */
   this.destinationEntry_ = null;
 
diff --git a/ui/file_manager/file_manager/foreground/js/file_watcher.js b/ui/file_manager/file_manager/foreground/js/file_watcher.js
index 59c48d7..fd3949b 100644
--- a/ui/file_manager/file_manager/foreground/js/file_watcher.js
+++ b/ui/file_manager/file_manager/foreground/js/file_watcher.js
@@ -69,8 +69,8 @@
  * Changes the watched directory. In case of a fake entry, the watch is
  * just released, since there is no reason to track a fake directory.
  *
- * @param {!DirectoryEntry|!FakeEntry} entry Directory entry to be tracked, or
- *     the fake entry.
+ * @param {!DirectoryEntry|!FilesAppEntry} entry Directory entry to be tracked,
+ *     or the fake entry.
  * @return {!Promise}
  */
 FileWatcher.prototype.changeWatchedDirectory = function(entry) {
diff --git a/ui/file_manager/file_manager/foreground/js/import_controller_unittest.html b/ui/file_manager/file_manager/foreground/js/import_controller_unittest.html
index 048b501..869b35c4 100644
--- a/ui/file_manager/file_manager/foreground/js/import_controller_unittest.html
+++ b/ui/file_manager/file_manager/foreground/js/import_controller_unittest.html
@@ -16,6 +16,7 @@
 
   <script src="../../common/js/async_util.js"></script>
   <script src="../../common/js/mock_entry.js"></script>
+  <script src="../../../base/js/test_error_reporting.js"></script>
   <script src="../../common/js/unittest_util.js"></script>
   <script src="../../common/js/files_app_entry_types.js"></script>
   <script src="../../common/js/util.js"></script>
diff --git a/ui/file_manager/file_manager/foreground/js/list_thumbnail_loader_unittest.html b/ui/file_manager/file_manager/foreground/js/list_thumbnail_loader_unittest.html
index 21f19e0..4cb178e 100644
--- a/ui/file_manager/file_manager/foreground/js/list_thumbnail_loader_unittest.html
+++ b/ui/file_manager/file_manager/foreground/js/list_thumbnail_loader_unittest.html
@@ -13,7 +13,7 @@
 <script src="../../common/js/lru_cache.js"></script>
 <script src="../../common/js/util.js"></script>
 <script src="../../common/js/mock_entry.js"></script>
-<script src="../../common/js/unittest_util.js"></script>
+<script src="../../../base/js/test_error_reporting.js"></script>
 <script src="../../common/js/volume_manager_common.js"></script>
 <script src="directory_contents.js"></script>
 <script src="file_list_model.js"></script>
diff --git a/ui/file_manager/file_manager/foreground/js/list_thumbnail_loader_unittest.js b/ui/file_manager/file_manager/foreground/js/list_thumbnail_loader_unittest.js
index bb35917..1482b0e0 100644
--- a/ui/file_manager/file_manager/foreground/js/list_thumbnail_loader_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/list_thumbnail_loader_unittest.js
@@ -3,24 +3,6 @@
 // found in the LICENSE file.
 
 /**
- * Wait until the condition is met.
- *
- * @param {function()} condition Condition.
- * @return {Promise} A promise which is resolved when condition is met.
- */
-function waitUntil(condition) {
-  return new Promise(function(resolve, reject) {
-    var checkCondition = function() {
-      if (condition())
-        resolve();
-      else
-        setTimeout(checkCondition, 100);
-    };
-    checkCondition();
-  });
-}
-
-/**
  * Generates a data url of a sample image for testing.
  * TODO(yawano) Consider to share image generation logic with
  *     gallery/js/image_editor/test_util.js.
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/content_metadata_provider_unittest.html b/ui/file_manager/file_manager/foreground/js/metadata/content_metadata_provider_unittest.html
index 6a576aa7..66638da 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/content_metadata_provider_unittest.html
+++ b/ui/file_manager/file_manager/foreground/js/metadata/content_metadata_provider_unittest.html
@@ -13,7 +13,7 @@
 <!-- Others -->
 <script src="../../../../../webui/resources/js/assert.js"></script>
 <script src="../../../common/js/lru_cache.js"></script>
-<script src="../../../common/js/unittest_util.js"></script>
+<script src="../../../../base/js/test_error_reporting.js"></script>
 <script src="../../../common/js/file_type.js"></script>
 <script src="content_metadata_provider.js"></script>
 <script src="metadata_cache_item.js"></script>
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/external_metadata_provider_unittest.html b/ui/file_manager/file_manager/foreground/js/metadata/external_metadata_provider_unittest.html
index 682930f..e2edc1af1 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/external_metadata_provider_unittest.html
+++ b/ui/file_manager/file_manager/foreground/js/metadata/external_metadata_provider_unittest.html
@@ -13,7 +13,7 @@
 <!-- Others -->
 <script src="../../../../../webui/resources/js/assert.js"></script>
 <script src="../../../common/js/lru_cache.js"></script>
-<script src="../../../common/js/unittest_util.js"></script>
+<script src="../../../../base/js/test_error_reporting.js"></script>
 <script src="external_metadata_provider.js"></script>
 <script src="metadata_cache_item.js"></script>
 <script src="metadata_item.js"></script>
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/file_system_metadata_provider_unittest.html b/ui/file_manager/file_manager/foreground/js/metadata/file_system_metadata_provider_unittest.html
index 8d123ce..7136ed0 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/file_system_metadata_provider_unittest.html
+++ b/ui/file_manager/file_manager/foreground/js/metadata/file_system_metadata_provider_unittest.html
@@ -12,7 +12,7 @@
 <!-- Others -->
 <script src="../../../../../webui/resources/js/assert.js"></script>
 <script src="../../../common/js/lru_cache.js"></script>
-<script src="../../../common/js/unittest_util.js"></script>
+<script src="../../../../base/js/test_error_reporting.js"></script>
 <script src="file_system_metadata_provider.js"></script>
 <script src="metadata_cache_item.js"></script>
 <script src="metadata_item.js"></script>
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/metadata_cache_item_unittest.html b/ui/file_manager/file_manager/foreground/js/metadata/metadata_cache_item_unittest.html
index da1346c..2f9c70b 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/metadata_cache_item_unittest.html
+++ b/ui/file_manager/file_manager/foreground/js/metadata/metadata_cache_item_unittest.html
@@ -4,7 +4,7 @@
   -- found in the LICENSE file.
   -->
 <script src="../../../../../webui/resources/js/assert.js"></script>
-<script src="../../../common/js/unittest_util.js"></script>
+<script src="../../../../base/js/test_error_reporting.js"></script>
 <script src="metadata_cache_item.js"></script>
 <script src="metadata_item.js"></script>
 <script src="metadata_request.js"></script>
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/metadata_cache_set_unittest.html b/ui/file_manager/file_manager/foreground/js/metadata/metadata_cache_set_unittest.html
index a783501..b697c34 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/metadata_cache_set_unittest.html
+++ b/ui/file_manager/file_manager/foreground/js/metadata/metadata_cache_set_unittest.html
@@ -10,7 +10,7 @@
 
 <!-- Others -->
 <script src="../../../../../webui/resources/js/assert.js"></script>
-<script src="../../../common/js/unittest_util.js"></script>
+<script src="../../../../base/js/test_error_reporting.js"></script>
 <script src="../../../common/js/util.js"></script>
 <script src="metadata_cache_item.js"></script>
 <script src="metadata_cache_set.js"></script>
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/metadata_model_unittest.html b/ui/file_manager/file_manager/foreground/js/metadata/metadata_model_unittest.html
index f19aeba..3906cb1 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/metadata_model_unittest.html
+++ b/ui/file_manager/file_manager/foreground/js/metadata/metadata_model_unittest.html
@@ -11,7 +11,7 @@
 <!-- Others -->
 <script src="../../../../../webui/resources/js/assert.js"></script>
 <script src="../../../common/js/lru_cache.js"></script>
-<script src="../../../common/js/unittest_util.js"></script>
+<script src="../../../../base/js/test_error_reporting.js"></script>
 <script src="../../../common/js/util.js"></script>
 <script src="metadata_cache_item.js"></script>
 <script src="metadata_item.js"></script>
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/multi_metadata_provider_unittest.html b/ui/file_manager/file_manager/foreground/js/metadata/multi_metadata_provider_unittest.html
index 446bc5f0..40e15ca 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/multi_metadata_provider_unittest.html
+++ b/ui/file_manager/file_manager/foreground/js/metadata/multi_metadata_provider_unittest.html
@@ -13,7 +13,7 @@
 <!-- Others -->
 <script src="../../../../../webui/resources/js/assert.js"></script>
 <script src="../../../common/js/lru_cache.js"></script>
-<script src="../../../common/js/unittest_util.js"></script>
+<script src="../../../../base/js/test_error_reporting.js"></script>
 <script src="../../../common/js/volume_manager_common.js"></script>
 <script src="content_metadata_provider.js"></script>
 <script src="external_metadata_provider.js"></script>
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/thumbnail_model_unittest.html b/ui/file_manager/file_manager/foreground/js/metadata/thumbnail_model_unittest.html
index bb10cdcb..e655b9a4 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/thumbnail_model_unittest.html
+++ b/ui/file_manager/file_manager/foreground/js/metadata/thumbnail_model_unittest.html
@@ -4,7 +4,7 @@
   -- found in the LICENSE file.
   -->
 <script src="../../../common/js/file_type.js"></script>
-<script src="../../../common/js/unittest_util.js"></script>
+<script src="../../../../base/js/test_error_reporting.js"></script>
 <script src="metadata_item.js"></script>
 <script src="thumbnail_model.js"></script>
 
diff --git a/ui/file_manager/file_manager/foreground/js/navigation_list_model_unittest.html b/ui/file_manager/file_manager/foreground/js/navigation_list_model_unittest.html
index abcf28d..01953396 100644
--- a/ui/file_manager/file_manager/foreground/js/navigation_list_model_unittest.html
+++ b/ui/file_manager/file_manager/foreground/js/navigation_list_model_unittest.html
@@ -23,6 +23,7 @@
 <script src="../../background/js/mock_volume_manager.js"></script>
 <script src="../../common/js/async_util.js"></script>
 <script src="../../common/js/mock_entry.js"></script>
+<script src="../../../base/js/test_error_reporting.js"></script>
 <script src="../../common/js/unittest_util.js"></script>
 <script src="../../common/js/util.js"></script>
 <script src="mock_folder_shortcut_data_model.js"></script>
diff --git a/ui/file_manager/file_manager/foreground/js/providers_model_unittest.html b/ui/file_manager/file_manager/foreground/js/providers_model_unittest.html
index 31c71e723..5a865c1 100644
--- a/ui/file_manager/file_manager/foreground/js/providers_model_unittest.html
+++ b/ui/file_manager/file_manager/foreground/js/providers_model_unittest.html
@@ -14,6 +14,7 @@
 <script src="../../../../../ui/webui/resources/js/load_time_data.js"></script>
 
 <script src="../../common/js/mock_entry.js"></script>
+<script src="../../../base/js/test_error_reporting.js"></script>
 <script src="../../common/js/unittest_util.js"></script>
 <script src="../../common/js/files_app_entry_types.js"></script>
 <script src="../../common/js/util.js"></script>
diff --git a/ui/file_manager/file_manager/foreground/js/spinner_controller_unittest.html b/ui/file_manager/file_manager/foreground/js/spinner_controller_unittest.html
index de28607..f6783e5 100644
--- a/ui/file_manager/file_manager/foreground/js/spinner_controller_unittest.html
+++ b/ui/file_manager/file_manager/foreground/js/spinner_controller_unittest.html
@@ -8,6 +8,6 @@
 
 <script src="../../../../webui/resources/js/assert.js"></script>
 <script src="../../../../webui/resources/js/util.js"></script>
-<script src="../../common/js/unittest_util.js"></script>
+<script src="../../../base/js/test_error_reporting.js"></script>
 <script src="spinner_controller.js"></script>
 <script src="spinner_controller_unittest.js"></script>
diff --git a/ui/file_manager/file_manager/foreground/js/task_controller_unittest.html b/ui/file_manager/file_manager/foreground/js/task_controller_unittest.html
index f23b8e0..d6949ef 100644
--- a/ui/file_manager/file_manager/foreground/js/task_controller_unittest.html
+++ b/ui/file_manager/file_manager/foreground/js/task_controller_unittest.html
@@ -17,7 +17,7 @@
 
 <script src="../../common/js/file_type.js"></script>
 <script src="../../common/js/mock_entry.js"></script>
-<script src="../../common/js/unittest_util.js"></script>
+<script src="../../../base/js/test_error_reporting.js"></script>
 <script src="../../common/js/util.js"></script>
 <script src="../../common/js/volume_manager_common.js"></script>
 <script src="crostini.js"></script>
diff --git a/ui/file_manager/file_manager/foreground/js/ui/directory_tree_unittest.html b/ui/file_manager/file_manager/foreground/js/ui/directory_tree_unittest.html
index 0e7d0d18..e72ad12 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/directory_tree_unittest.html
+++ b/ui/file_manager/file_manager/foreground/js/ui/directory_tree_unittest.html
@@ -17,6 +17,7 @@
 
   <script src="../../../common/js/async_util.js"></script>
   <script src="../../../common/js/mock_entry.js"></script>
+  <script src="../../../../base/js/test_error_reporting.js"></script>
   <script src="../../../common/js/unittest_util.js"></script>
   <script src="../../../common/js/files_app_entry_types.js"></script>
   <script src="../../../common/js/util.js"></script>
diff --git a/ui/file_manager/file_manager/foreground/js/ui/file_tap_handler_unittest.html b/ui/file_manager/file_manager/foreground/js/ui/file_tap_handler_unittest.html
index 146270b8..596df36f 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/file_tap_handler_unittest.html
+++ b/ui/file_manager/file_manager/foreground/js/ui/file_tap_handler_unittest.html
@@ -6,7 +6,7 @@
 
 <script src="../../../../../../ui/webui/resources/js/assert.js"></script>
 
-<script src="../../../common/js/unittest_util.js"></script>
+<script src="../../../../base/js/test_error_reporting.js"></script>
 <script src="../../../common/js/util.js"></script>
 
 <script src="file_tap_handler.js"></script>
diff --git a/ui/file_manager/file_manager/main.html b/ui/file_manager/file_manager/main.html
index 10defcc..2acb28db 100644
--- a/ui/file_manager/file_manager/main.html
+++ b/ui/file_manager/file_manager/main.html
@@ -170,6 +170,7 @@
       <command id="manage-in-drive"
                i18n-values="label:MANAGE_IN_DRIVE_BUTTON_LABEL" disabled hidden>
       <command id="share-with-linux" i18n-values="label:SHARE_WITH_LINUX_BUTTON_LABEL" disabled hidden>
+      <command id="manage-linux-sharing" i18n-values="label:MANAGE_LINUX_SHARING_BUTTON_LABEL" disabled hidden>
 
       <command id="zoom-in" shortcut="=|Ctrl">
       <command id="zoom-out" shortcut="-|Ctrl">
@@ -268,6 +269,8 @@
                     command="#toggle-hidden-files"></cr-menu-item>
       <cr-menu-item id="gear-menu-toggle-hidden-android-folders"
                     command="#toggle-hidden-android-folders"></cr-menu-item>
+      <cr-menu-item id="gear-menu-manage-linux-sharing"
+                    command="#manage-linux-sharing"></cr-menu-item>
       <cr-menu-item id="gear-menu-drive-sync-settings"
                     command="#drive-sync-settings"></cr-menu-item>
       <cr-menu-item id="gear-menu-drive-hosted-settings"
diff --git a/ui/file_manager/file_manager/test/BUILD.gn b/ui/file_manager/file_manager/test/BUILD.gn
index 7c5254a8c..e3ec2a5 100644
--- a/ui/file_manager/file_manager/test/BUILD.gn
+++ b/ui/file_manager/file_manager/test/BUILD.gn
@@ -21,7 +21,9 @@
     "//chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc",
     "//ui/webui/resources/css/text_defaults.css",
     "check_select.js",
-    "crostini.js",
+    "crostini_mount.js",
+    "crostini_share.js",
+    "crostini_tasks.js",
     "js/strings.js",
     "quick_view.js",
     "uma.js",
@@ -36,7 +38,9 @@
   deps = [
     ":check_select",
     ":closure_compile_externs",
-    ":crostini",
+    ":crostini_mount",
+    ":crostini_share",
+    ":crostini_tasks",
     ":quick_view",
     ":uma",
   ]
@@ -68,7 +72,23 @@
   ]
 }
 
-js_library("crostini") {
+js_library("crostini_mount") {
+  deps = [
+    "../foreground/js:crostini",
+    "js:test_util",
+    "//ui/webui/resources/js:webui_resource_test",
+  ]
+}
+
+js_library("crostini_share") {
+  deps = [
+    "../foreground/js:crostini",
+    "js:test_util",
+    "//ui/webui/resources/js:webui_resource_test",
+  ]
+}
+
+js_library("crostini_tasks") {
   deps = [
     "../foreground/js:crostini",
     "js:test_util",
diff --git a/ui/file_manager/file_manager/test/crostini.js b/ui/file_manager/file_manager/test/crostini.js
deleted file mode 100644
index 086946a..0000000
--- a/ui/file_manager/file_manager/test/crostini.js
+++ /dev/null
@@ -1,436 +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.
-
-const crostini = {};
-
-crostini.testCrostiniNotEnabled = (done) => {
-  chrome.fileManagerPrivate.crostiniEnabled_ = false;
-  test.setupAndWaitUntilReady()
-      .then(() => {
-        fileManager.setupCrostini_();
-        return test.waitForElementLost(
-            '#directory-tree .tree-item [root-type-icon="crostini"]');
-      })
-      .then(() => {
-        // Reset crostini back to default enabled=true.
-        chrome.fileManagerPrivate.crostiniEnabled_ = true;
-        done();
-      });
-};
-
-crostini.testMountCrostiniSuccess = (done) => {
-  const oldMount = chrome.fileManagerPrivate.mountCrostini;
-  let mountCallback = null;
-  chrome.fileManagerPrivate.mountCrostini = (callback) => {
-    mountCallback = callback;
-  };
-  test.setupAndWaitUntilReady()
-      .then(() => {
-        // Linux files fake root is shown.
-        return test.waitForElement(
-            '#directory-tree .tree-item [root-type-icon="crostini"]');
-      })
-      .then(() => {
-        // Click on Linux files.
-        assertTrue(
-            test.fakeMouseClick(
-                '#directory-tree .tree-item [root-type-icon="crostini"]'),
-            'click linux files');
-        return test.waitForElement('paper-progress:not([hidden])');
-      })
-      .then(() => {
-        // Ensure mountCrostini is called.
-        return test.repeatUntil(() => {
-          if (!mountCallback)
-            return test.pending('Waiting for mountCrostini');
-          return mountCallback;
-        });
-      })
-      .then(() => {
-        // Intercept the fileManagerPrivate.mountCrostini call
-        // and add crostini disk mount.
-        test.mountCrostini();
-        // Continue from fileManagerPrivate.mountCrostini callback
-        // and ensure expected files are shown.
-        mountCallback();
-        return test.waitForFiles(
-            test.TestEntryInfo.getExpectedRows(test.BASIC_CROSTINI_ENTRY_SET));
-      })
-      .then(() => {
-        // Reset fileManagerPrivate.mountCrostini and remove mount.
-        chrome.fileManagerPrivate.mountCrostini = oldMount;
-        chrome.fileManagerPrivate.removeMount('crostini');
-        // Linux Files fake root is shown.
-        return test.waitForElement(
-            '#directory-tree .tree-item [root-type-icon="crostini"]');
-      })
-      .then(() => {
-        // Downloads folder should be shown when crostini goes away.
-        return test.waitForFiles(
-            test.TestEntryInfo.getExpectedRows(test.BASIC_LOCAL_ENTRY_SET));
-      })
-      .then(() => {
-        done();
-      });
-};
-
-crostini.testMountCrostiniError = (done) => {
-  const oldMount = chrome.fileManagerPrivate.mountCrostini;
-  // Override fileManagerPrivate.mountCrostini to return error.
-  chrome.fileManagerPrivate.mountCrostini = (callback) => {
-    chrome.runtime.lastError = {message: 'test message'};
-    callback();
-    delete chrome.runtime.lastError;
-  };
-  test.setupAndWaitUntilReady()
-      .then(() => {
-        return test.waitForElement(
-            '#directory-tree .tree-item [root-type-icon="crostini"]');
-      })
-      .then(() => {
-        // Click on Linux Files, ensure error dialog is shown.
-        assertTrue(test.fakeMouseClick(
-            '#directory-tree .tree-item [root-type-icon="crostini"]'));
-        return test.waitForElement('.cr-dialog-container.shown');
-      })
-      .then(() => {
-        // Click OK button to close.
-        assertTrue(test.fakeMouseClick('button.cr-dialog-ok'));
-        return test.waitForElementLost('.cr-dialog-container');
-      })
-      .then(() => {
-        // Reset chrome.fileManagerPrivate.mountCrostini.
-        chrome.fileManagerPrivate.mountCrostini = oldMount;
-        done();
-      });
-};
-
-crostini.testCrostiniMountOnDrag = (done) => {
-  chrome.fileManagerPrivate.mountCrostiniDelay_ = 0;
-  test.setupAndWaitUntilReady()
-      .then(() => {
-        return test.waitForElement(
-            '#directory-tree .tree-item [root-type-icon="crostini"]');
-      })
-      .then(() => {
-        assertTrue(test.sendEvent(
-            '#directory-tree .tree-item [root-type-icon="crostini"]',
-            new Event('dragenter', {bubbles: true})));
-        assertTrue(test.sendEvent(
-            '#directory-tree .tree-item [root-type-icon="crostini"]',
-            new Event('dragleave', {bubbles: true})));
-        return test.waitForFiles(
-            test.TestEntryInfo.getExpectedRows(test.BASIC_CROSTINI_ENTRY_SET));
-      })
-      .then(() => {
-        chrome.fileManagerPrivate.removeMount('crostini');
-        return test.waitForElement(
-            '#directory-tree .tree-item [root-type-icon="crostini"]');
-      })
-      .then(() => {
-        done();
-      });
-};
-
-crostini.testShareBeforeOpeningDownloadsWithCrostiniApp = (done) => {
-  // Save old fmp.getFileTasks and replace with version that returns
-  // crostini app and chrome Text app.
-  let oldGetFileTasks = chrome.fileManagerPrivate.getFileTasks;
-  chrome.fileManagerPrivate.getFileTasks = (entries, callback) => {
-    setTimeout(callback, 0, [
-      {
-        taskId: 'text-app-id|app|text',
-        title: 'Text',
-        verb: 'open_with',
-      },
-      {
-        taskId: 'crostini-app-id|crostini|open-with',
-        title: 'Crostini App',
-        verb: 'open_with',
-      }
-    ]);
-  };
-
-  // Save old fmp.sharePathWitCrostini.
-  const oldSharePath = chrome.fileManagerPrivate.sharePathWithCrostini;
-  let sharePathCalled = false;
-  chrome.fileManagerPrivate.sharePathWithCrostini = (entry, callback) => {
-    sharePathCalled = true;
-    oldSharePath(entry, callback);
-  };
-
-  // Save old fmp.executeTask.
-  const oldExecuteTask = chrome.fileManagerPrivate.executeTask;
-  let executeTaskCalled = false;
-  chrome.fileManagerPrivate.executeTask = (taskId, entries, callback) => {
-    executeTaskCalled = true;
-    oldExecuteTask(taskId, entries, callback);
-  };
-  chrome.metricsPrivate.values_ = [];
-
-  test.setupAndWaitUntilReady([], [], [])
-      .then(() => {
-        // Add '/A', and '/A/hello.txt', refresh, 'A' is shown.
-        test.addEntries(
-            [test.ENTRIES.directoryA, test.ENTRIES.helloInA], [], []);
-        assertTrue(test.fakeMouseClick('#refresh-button'), 'click refresh');
-        return test.waitForFiles(
-            test.TestEntryInfo.getExpectedRows([test.ENTRIES.directoryA]));
-      })
-      .then(() => {
-        // Change to 'A' directory, hello.txt is shown.
-        assertTrue(test.fakeMouseDoubleClick('[file-name="A"]'));
-        return test.waitForFiles(
-            test.TestEntryInfo.getExpectedRows([test.ENTRIES.hello]));
-      })
-      .then(() => {
-        // Right click on 'hello.txt' file, wait for dialog with 'Open with'.
-        assertTrue(test.fakeMouseRightClick('[file-name="hello.txt"]'));
-        return test.waitForElement(
-            'cr-menu-item[command="#open-with"]:not([hidden]');
-      })
-      .then(() => {
-        // Click 'Open with', wait for picker.
-        assertTrue(test.fakeMouseClick('cr-menu-item[command="#open-with"]'));
-        return test.waitForElement('#default-tasks-list');
-      })
-      .then(() => {
-        // Ensure picker shows both options.  Click on 'Crostini App'.  Ensure
-        // share path dialog is shown.
-        const list = document.querySelectorAll('#default-tasks-list li div');
-        assertEquals(2, list.length);
-        assertEquals('Open with Crostini App', list[0].innerText);
-        assertEquals('Open with Text', list[1].innerText);
-        assertTrue(test.fakeMouseClick('#default-tasks-list li'));
-        return test.repeatUntil(() => {
-          return document.querySelector('.cr-dialog-title').innerText ===
-              'Share files with Linux' ||
-              test.pending('Waiting for share before open dialog');
-        });
-      })
-      .then(() => {
-        // Validate dialog messages, click 'OK' to share and open.  Ensure
-        // dialog closes.
-        assertEquals(
-            'Let Linux apps open <b>hello.txt</b>.',
-            document.querySelector('.cr-dialog-text').innerHTML);
-        assertTrue(test.fakeMouseClick('button.cr-dialog-ok'));
-        return test.waitForElementLost('.cr-dialog-container');
-      })
-      .then(() => {
-        // Ensure fmp.sharePathWithCrostini, fmp.executeTask called.
-        return test.repeatUntil(() => {
-          return sharePathCalled && executeTaskCalled ||
-              test.pending('Waiting to share and open');
-        });
-      })
-      .then(() => {
-        // Validate UMAs.
-        const lastEnumUma = chrome.metricsPrivate.values_.pop();
-        assertEquals(
-            'FileBrowser.CrostiniShareDialog', lastEnumUma[0].metricName);
-        assertEquals(1 /* ShareBeforeOpen */, lastEnumUma[1]);
-
-        // Restore fmp.*.
-        chrome.fileManagerPrivate.getFileTasks = oldGetFileTasks;
-        chrome.fileManagerPrivate.sharePathWithCrostini = oldSharePath;
-        chrome.fileManagerPrivate.executeTask = oldExecuteTask;
-        done();
-      });
-};
-
-crostini.testErrorOpeningDownloadsRootWithDefaultCrostiniApp = (done) => {
-  // Save old fmp.getFileTasks and replace with version that returns
-  // crostini app and chrome Text app.
-  let oldGetFileTasks = chrome.fileManagerPrivate.getFileTasks;
-  chrome.fileManagerPrivate.getFileTasks = (entries, callback) => {
-    setTimeout(callback, 0, [{
-                 taskId: 'crostini-app-id|crostini|open-with',
-                 title: 'Crostini App',
-                 verb: 'open_with',
-                 isDefault: true,
-               }]);
-  };
-
-  test.setupAndWaitUntilReady()
-      .then(() => {
-        // Right click on 'world.ogv' file, wait for dialog with the default
-        // task action.
-        assertTrue(test.fakeMouseRightClick('[file-name="world.ogv"]'));
-        return test.repeatUntil(() => {
-          return document
-                     .querySelector(
-                         'cr-menu-item[command="#default-task"]:not([hidden])')
-                     .label === 'Crostini App' ||
-              test.pending('Waiting for default task menu item');
-        });
-      })
-      .then(() => {
-        // Click 'Open with', wait for picker.
-        assertTrue(
-            test.fakeMouseClick('cr-menu-item[command="#default-task"]'));
-        return test.waitForElement('.cr-dialog-container');
-      })
-      .then(() => {
-        // Validate error messages, click 'OK' to close.  Ensure dialog closes.
-        assertEquals(
-            'Unable to open with Crostini App',
-            document.querySelector('.cr-dialog-title').innerText);
-        assertEquals(
-            'To open files with Crostini App, ' +
-                'first copy to Linux files folder.',
-            document.querySelector('.cr-dialog-text').innerText);
-        assertTrue(test.fakeMouseClick('button.cr-dialog-ok'));
-        return test.waitForElementLost('.cr-dialog-container');
-      })
-      .then(() => {
-        // Restore fmp.getFileTasks.
-        chrome.fileManagerPrivate.getFileTasks = oldGetFileTasks;
-        done();
-      });
-};
-
-crostini.testSharePathCrostiniSuccess = (done) => {
-  const oldSharePath = chrome.fileManagerPrivate.sharePathWithCrostini;
-  let sharePathCalled = false;
-  chrome.fileManagerPrivate.sharePathWithCrostini = (entry, callback) => {
-    oldSharePath(entry, () => {
-      sharePathCalled = true;
-      callback();
-    });
-  };
-  chrome.metricsPrivate.smallCounts_ = [];
-  chrome.metricsPrivate.values_ = [];
-
-  test.setupAndWaitUntilReady()
-      .then(() => {
-        // Right-click 'photos' directory.
-        // Check 'Share with Linux' is shown in menu.
-        assertTrue(
-            test.fakeMouseRightClick('#file-list [file-name="photos"]'),
-            'right-click photos');
-        return test.waitForElement(
-            '#file-context-menu:not([hidden]) ' +
-            '[command="#share-with-linux"]:not([hidden]):not([disabled])');
-      })
-      .then(() => {
-        // Click on 'Share with Linux'.
-        assertTrue(
-            test.fakeMouseClick(
-                '#file-context-menu [command="#share-with-linux"]'),
-            'Share with Linux');
-        // Check sharePathWithCrostini is called.
-        return test.repeatUntil(() => {
-          return sharePathCalled || test.pending('wait for sharePathCalled');
-        });
-      })
-      .then(() => {
-        // Validate UMAs.
-        assertEquals(1, chrome.metricsPrivate.smallCounts_.length);
-        assertArrayEquals(
-            ['FileBrowser.CrostiniSharedPaths.Depth.downloads', 1],
-            chrome.metricsPrivate.smallCounts_[0]);
-        const lastEnumUma = chrome.metricsPrivate.values_.pop();
-        assertEquals('FileBrowser.MenuItemSelected', lastEnumUma[0].metricName);
-        assertEquals(12 /* Share with Linux */, lastEnumUma[1]);
-
-        // Restore fmp.*.
-        chrome.fileManagerPrivate.sharePathWithCrostini = oldSharePath;
-        done();
-      });
-};
-
-// Verify right-click menu with 'Share with Linux' is not shown for:
-// * Files (not directory)
-// * Any folder already shared
-// * Root Downloads folder
-// * Any folder outside of downloads (e.g. crostini or orive)
-crostini.testSharePathNotShown = (done) => {
-  const myFiles = '#directory-tree .tree-item [root-type-icon="my_files"]';
-  const downloads = '#file-list li [file-type-icon="downloads"]';
-  const linuxFiles = '#directory-tree .tree-item [root-type-icon="crostini"]';
-  const googleDrive = '#directory-tree .tree-item [volume-type-icon="drive"]';
-  const menuNoShareWithLinux = '#file-context-menu:not([hidden]) ' +
-      '[command="#share-with-linux"][hidden][disabled="disabled"]';
-  let alreadySharedPhotosDir;
-
-  test.setupAndWaitUntilReady()
-      .then(() => {
-        // Right-click 'hello.txt' file.
-        // Check 'Share with Linux' is not shown in menu.
-        assertTrue(
-            test.fakeMouseRightClick('#file-list [file-name="hello.txt"]'),
-            'right-click hello.txt');
-        return test.waitForElement(menuNoShareWithLinux);
-      })
-      .then(() => {
-        // Set a folder as already shared.
-        alreadySharedPhotosDir =
-            mockVolumeManager
-                .getCurrentProfileVolumeInfo(
-                    VolumeManagerCommon.VolumeType.DOWNLOADS)
-                .fileSystem.entries['/photos'];
-        Crostini.registerSharedPath(alreadySharedPhotosDir, mockVolumeManager);
-        assertTrue(
-            test.fakeMouseRightClick('#file-list [file-name="photos"]'),
-            'right-click hello.txt');
-        return test.waitForElement(menuNoShareWithLinux);
-      })
-      .then(() => {
-        // Select 'My files' in directory tree to show Downloads in file list.
-        assertTrue(test.fakeMouseClick(myFiles), 'click My files');
-        return test.waitForElement(downloads);
-      })
-      .then(() => {
-        // Right-click 'Downloads' directory.
-        // Check 'Share with Linux' is not shown in menu.
-        assertTrue(
-            test.fakeMouseRightClick(downloads), 'right-click downloads');
-        return test.waitForElement(menuNoShareWithLinux);
-      })
-      .then(() => {
-        // Select 'Linux files' in directory tree to show dir A in file list.
-        return test.waitForElement(linuxFiles);
-      })
-      .then(() => {
-        assertTrue(test.fakeMouseClick(linuxFiles), 'click Linux files');
-        return test.waitForFiles(
-            test.TestEntryInfo.getExpectedRows(test.BASIC_CROSTINI_ENTRY_SET));
-      })
-      .then(() => {
-        // Check 'Share with Linux' is not shown in menu.
-        assertTrue(
-            test.fakeMouseRightClick('#file-list [file-name="A"]'),
-            'right-click directory A');
-        return test.waitForElement(menuNoShareWithLinux);
-      })
-      .then(() => {
-        // Select 'Google Drive' to show dir photos in file list.
-        return test.waitForElement(googleDrive);
-      })
-      .then(() => {
-        assertTrue(test.fakeMouseClick(googleDrive), 'click Google Drive');
-        return test.waitForFiles(
-            test.TestEntryInfo.getExpectedRows(test.BASIC_DRIVE_ENTRY_SET));
-      })
-      .then(() => {
-        // Check 'Share with Linux' is not shown in menu.
-        assertTrue(
-            test.fakeMouseRightClick('#file-list [file-name="photos"]'),
-            'right-click photos');
-        return test.waitForElement(menuNoShareWithLinux);
-      })
-      .then(() => {
-        // Reset Linux files back to unmounted.
-        chrome.fileManagerPrivate.removeMount('crostini');
-        return test.waitForElement(
-            '#directory-tree .tree-item [root-type-icon="crostini"]');
-      })
-      .then(() => {
-        // Clear Crostini shared folders.
-        Crostini.unregisterSharedPath(
-            alreadySharedPhotosDir, mockVolumeManager);
-        done();
-      });
-};
diff --git a/ui/file_manager/file_manager/test/crostini_mount.js b/ui/file_manager/file_manager/test/crostini_mount.js
new file mode 100644
index 0000000..89138d9
--- /dev/null
+++ b/ui/file_manager/file_manager/test/crostini_mount.js
@@ -0,0 +1,134 @@
+// 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.
+
+const crostiniMount = {};
+
+crostiniMount.testCrostiniNotEnabled = (done) => {
+  chrome.fileManagerPrivate.crostiniEnabled_ = false;
+  test.setupAndWaitUntilReady()
+      .then(() => {
+        fileManager.setupCrostini_();
+        return test.waitForElementLost(
+            '#directory-tree .tree-item [root-type-icon="crostini"]');
+      })
+      .then(() => {
+        // Reset crostini back to default enabled=true.
+        chrome.fileManagerPrivate.crostiniEnabled_ = true;
+        done();
+      });
+};
+
+crostiniMount.testMountCrostiniSuccess = (done) => {
+  const oldMount = chrome.fileManagerPrivate.mountCrostini;
+  let mountCallback = null;
+  chrome.fileManagerPrivate.mountCrostini = (callback) => {
+    mountCallback = callback;
+  };
+  test.setupAndWaitUntilReady()
+      .then(() => {
+        // Linux files fake root is shown.
+        return test.waitForElement(
+            '#directory-tree .tree-item [root-type-icon="crostini"]');
+      })
+      .then(() => {
+        // Click on Linux files.
+        assertTrue(
+            test.fakeMouseClick(
+                '#directory-tree .tree-item [root-type-icon="crostini"]'),
+            'click linux files');
+        return test.waitForElement('paper-progress:not([hidden])');
+      })
+      .then(() => {
+        // Ensure mountCrostini is called.
+        return test.repeatUntil(() => {
+          if (!mountCallback)
+            return test.pending('Waiting for mountCrostini');
+          return mountCallback;
+        });
+      })
+      .then(() => {
+        // Intercept the fileManagerPrivate.mountCrostini call
+        // and add crostini disk mount.
+        test.mountCrostini();
+        // Continue from fileManagerPrivate.mountCrostini callback
+        // and ensure expected files are shown.
+        mountCallback();
+        return test.waitForFiles(
+            test.TestEntryInfo.getExpectedRows(test.BASIC_CROSTINI_ENTRY_SET));
+      })
+      .then(() => {
+        // Reset fileManagerPrivate.mountCrostini and remove mount.
+        chrome.fileManagerPrivate.mountCrostini = oldMount;
+        chrome.fileManagerPrivate.removeMount('crostini');
+        // Linux Files fake root is shown.
+        return test.waitForElement(
+            '#directory-tree .tree-item [root-type-icon="crostini"]');
+      })
+      .then(() => {
+        // Downloads folder should be shown when crostini goes away.
+        return test.waitForFiles(
+            test.TestEntryInfo.getExpectedRows(test.BASIC_LOCAL_ENTRY_SET));
+      })
+      .then(() => {
+        done();
+      });
+};
+
+crostiniMount.testMountCrostiniError = (done) => {
+  const oldMount = chrome.fileManagerPrivate.mountCrostini;
+  // Override fileManagerPrivate.mountCrostini to return error.
+  chrome.fileManagerPrivate.mountCrostini = (callback) => {
+    chrome.runtime.lastError = {message: 'test message'};
+    callback();
+    delete chrome.runtime.lastError;
+  };
+  test.setupAndWaitUntilReady()
+      .then(() => {
+        return test.waitForElement(
+            '#directory-tree .tree-item [root-type-icon="crostini"]');
+      })
+      .then(() => {
+        // Click on Linux Files, ensure error dialog is shown.
+        assertTrue(test.fakeMouseClick(
+            '#directory-tree .tree-item [root-type-icon="crostini"]'));
+        return test.waitForElement('.cr-dialog-container.shown');
+      })
+      .then(() => {
+        // Click OK button to close.
+        assertTrue(test.fakeMouseClick('button.cr-dialog-ok'));
+        return test.waitForElementLost('.cr-dialog-container');
+      })
+      .then(() => {
+        // Reset chrome.fileManagerPrivate.mountCrostini.
+        chrome.fileManagerPrivate.mountCrostini = oldMount;
+        done();
+      });
+};
+
+crostiniMount.testCrostiniMountOnDrag = (done) => {
+  chrome.fileManagerPrivate.mountCrostiniDelay_ = 0;
+  test.setupAndWaitUntilReady()
+      .then(() => {
+        return test.waitForElement(
+            '#directory-tree .tree-item [root-type-icon="crostini"]');
+      })
+      .then(() => {
+        assertTrue(test.sendEvent(
+            '#directory-tree .tree-item [root-type-icon="crostini"]',
+            new Event('dragenter', {bubbles: true})));
+        assertTrue(test.sendEvent(
+            '#directory-tree .tree-item [root-type-icon="crostini"]',
+            new Event('dragleave', {bubbles: true})));
+        return test.waitForFiles(
+            test.TestEntryInfo.getExpectedRows(test.BASIC_CROSTINI_ENTRY_SET));
+      })
+      .then(() => {
+        chrome.fileManagerPrivate.removeMount('crostini');
+        return test.waitForElement(
+            '#directory-tree .tree-item [root-type-icon="crostini"]');
+      })
+      .then(() => {
+        done();
+      });
+};
diff --git a/ui/file_manager/file_manager/test/crostini_share.js b/ui/file_manager/file_manager/test/crostini_share.js
new file mode 100644
index 0000000..c52f657
--- /dev/null
+++ b/ui/file_manager/file_manager/test/crostini_share.js
@@ -0,0 +1,204 @@
+// 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.
+
+const crostiniShare = {};
+
+crostiniShare.testSharePathCrostiniSuccess = (done) => {
+  const oldSharePath = chrome.fileManagerPrivate.sharePathWithCrostini;
+  let sharePathCalled = false;
+  chrome.fileManagerPrivate.sharePathWithCrostini = (entry, callback) => {
+    oldSharePath(entry, () => {
+      sharePathCalled = true;
+      callback();
+    });
+  };
+  chrome.metricsPrivate.smallCounts_ = [];
+  chrome.metricsPrivate.values_ = [];
+
+  test.setupAndWaitUntilReady()
+      .then(() => {
+        // Right-click 'photos' directory.
+        // Check 'Share with Linux' is shown in menu.
+        assertTrue(
+            test.fakeMouseRightClick('#file-list [file-name="photos"]'),
+            'right-click photos');
+        return test.waitForElement(
+            '#file-context-menu:not([hidden]) ' +
+            '[command="#share-with-linux"]:not([hidden]):not([disabled])');
+      })
+      .then(() => {
+        // Click on 'Share with Linux'.
+        assertTrue(
+            test.fakeMouseClick(
+                '#file-context-menu [command="#share-with-linux"]'),
+            'Share with Linux');
+        // Check sharePathWithCrostini is called.
+        return test.repeatUntil(() => {
+          return sharePathCalled || test.pending('wait for sharePathCalled');
+        });
+      })
+      .then(() => {
+        // Validate UMAs.
+        assertEquals(1, chrome.metricsPrivate.smallCounts_.length);
+        assertArrayEquals(
+            ['FileBrowser.CrostiniSharedPaths.Depth.downloads', 1],
+            chrome.metricsPrivate.smallCounts_[0]);
+        const lastEnumUma = chrome.metricsPrivate.values_.pop();
+        assertEquals('FileBrowser.MenuItemSelected', lastEnumUma[0].metricName);
+        assertEquals(12 /* Share with Linux */, lastEnumUma[1]);
+
+        // Restore fmp.*.
+        chrome.fileManagerPrivate.sharePathWithCrostini = oldSharePath;
+        done();
+      });
+};
+
+// Verify right-click menu with 'Share with Linux' is not shown for:
+// * Files (not directory)
+// * Any folder already shared
+// * Root Downloads folder
+// * Any folder outside of downloads (e.g. crostini or orive)
+crostiniShare.testSharePathNotShown = (done) => {
+  const myFiles = '#directory-tree .tree-item [root-type-icon="my_files"]';
+  const downloads = '#file-list li [file-type-icon="downloads"]';
+  const linuxFiles = '#directory-tree .tree-item [root-type-icon="crostini"]';
+  const googleDrive = '#directory-tree .tree-item [volume-type-icon="drive"]';
+  const menuNoShareWithLinux = '#file-context-menu:not([hidden]) ' +
+      '[command="#share-with-linux"][hidden][disabled="disabled"]';
+  let alreadySharedPhotosDir;
+
+  test.setupAndWaitUntilReady()
+      .then(() => {
+        // Right-click 'hello.txt' file.
+        // Check 'Share with Linux' is not shown in menu.
+        assertTrue(
+            test.fakeMouseRightClick('#file-list [file-name="hello.txt"]'),
+            'right-click hello.txt');
+        return test.waitForElement(menuNoShareWithLinux);
+      })
+      .then(() => {
+        // Set a folder as already shared.
+        alreadySharedPhotosDir =
+            mockVolumeManager
+                .getCurrentProfileVolumeInfo(
+                    VolumeManagerCommon.VolumeType.DOWNLOADS)
+                .fileSystem.entries['/photos'];
+        Crostini.registerSharedPath(alreadySharedPhotosDir, mockVolumeManager);
+        assertTrue(
+            test.fakeMouseRightClick('#file-list [file-name="photos"]'),
+            'right-click hello.txt');
+        return test.waitForElement(menuNoShareWithLinux);
+      })
+      .then(() => {
+        // Select 'My files' in directory tree to show Downloads in file list.
+        assertTrue(test.fakeMouseClick(myFiles), 'click My files');
+        return test.waitForElement(downloads);
+      })
+      .then(() => {
+        // Right-click 'Downloads' directory.
+        // Check 'Share with Linux' is not shown in menu.
+        assertTrue(
+            test.fakeMouseRightClick(downloads), 'right-click downloads');
+        return test.waitForElement(menuNoShareWithLinux);
+      })
+      .then(() => {
+        // Select 'Linux files' in directory tree to show dir A in file list.
+        return test.waitForElement(linuxFiles);
+      })
+      .then(() => {
+        assertTrue(test.fakeMouseClick(linuxFiles), 'click Linux files');
+        return test.waitForFiles(
+            test.TestEntryInfo.getExpectedRows(test.BASIC_CROSTINI_ENTRY_SET));
+      })
+      .then(() => {
+        // Check 'Share with Linux' is not shown in menu.
+        assertTrue(
+            test.fakeMouseRightClick('#file-list [file-name="A"]'),
+            'right-click directory A');
+        return test.waitForElement(menuNoShareWithLinux);
+      })
+      .then(() => {
+        // Select 'Google Drive' to show dir photos in file list.
+        return test.waitForElement(googleDrive);
+      })
+      .then(() => {
+        assertTrue(test.fakeMouseClick(googleDrive), 'click Google Drive');
+        return test.waitForFiles(
+            test.TestEntryInfo.getExpectedRows(test.BASIC_DRIVE_ENTRY_SET));
+      })
+      .then(() => {
+        // Check 'Share with Linux' is not shown in menu.
+        assertTrue(
+            test.fakeMouseRightClick('#file-list [file-name="photos"]'),
+            'right-click photos');
+        return test.waitForElement(menuNoShareWithLinux);
+      })
+      .then(() => {
+        // Reset Linux files back to unmounted.
+        chrome.fileManagerPrivate.removeMount('crostini');
+        return test.waitForElement(
+            '#directory-tree .tree-item [root-type-icon="crostini"]');
+      })
+      .then(() => {
+        // Clear Crostini shared folders.
+        Crostini.unregisterSharedPath(
+            alreadySharedPhotosDir, mockVolumeManager);
+        done();
+      });
+};
+
+// Verify gear menu 'Manage Linux sharing'.
+crostiniShare.testGearMenuManageLinuxSharing = (done) => {
+  const gearMenuClosed = '#gear-menu[hidden]';
+  const manageLinuxSharingOptionHidden =
+      '#gear-menu:not([hidden]) #gear-menu-manage-linux-sharing[hidden]';
+  const manageLinuxSharingOptionShown =
+      '#gear-menu:not([hidden]) #gear-menu-manage-linux-sharing:not([hidden])';
+  chrome.metricsPrivate.values_ = [];
+
+  test.setupAndWaitUntilReady()
+      .then(() => {
+        // Setup with crostini disabled.
+        chrome.fileManagerPrivate.crostiniEnabled_ = false;
+        fileManager.setupCrostini_();
+        return test.repeatUntil(
+            () => !Crostini.IS_CROSTINI_FILES_ENABLED ||
+                test.pending('crostini setup'));
+      })
+      .then(() => {
+        // Click gear menu, ensure 'Manage Linux sharing' is hidden.
+        assertTrue(test.fakeMouseClick('#gear-button'));
+        return test.waitForElement(manageLinuxSharingOptionHidden);
+      })
+      .then(() => {
+        // Close gear menu.
+        assertTrue(test.fakeMouseClick('#gear-button'));
+        return test.waitForElement(gearMenuClosed);
+      })
+      .then(() => {
+        // Setup with crostini enabled.
+        chrome.fileManagerPrivate.crostiniEnabled_ = true;
+        fileManager.setupCrostini_();
+        return test.repeatUntil(
+            () => Crostini.IS_CROSTINI_FILES_ENABLED ||
+                test.pending('crostini setup'));
+      })
+      .then(() => {
+        // Click gear menu, ensure 'Manage Linux sharing' is shown.
+        assertTrue(test.fakeMouseClick('#gear-button'));
+        return test.waitForElement(manageLinuxSharingOptionShown);
+      })
+      .then(() => {
+        // Click 'Manage Linux sharing'.
+        assertTrue(test.fakeMouseClick('#gear-menu-manage-linux-sharing'));
+        return test.waitForElement(gearMenuClosed);
+      })
+      .then(() => {
+        // Verify UMA.
+        const lastEnumUma = chrome.metricsPrivate.values_.pop();
+        assertEquals('FileBrowser.MenuItemSelected', lastEnumUma[0].metricName);
+        assertEquals(13 /* Manage Linux sharing */, lastEnumUma[1]);
+        done();
+      });
+};
diff --git a/ui/file_manager/file_manager/test/crostini_tasks.js b/ui/file_manager/file_manager/test/crostini_tasks.js
new file mode 100644
index 0000000..36366e5
--- /dev/null
+++ b/ui/file_manager/file_manager/test/crostini_tasks.js
@@ -0,0 +1,163 @@
+// 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.
+
+const crostiniTasks = {};
+
+crostiniTasks.testShareBeforeOpeningDownloadsWithCrostiniApp = (done) => {
+  // Save old fmp.getFileTasks and replace with version that returns
+  // crostini app and chrome Text app.
+  let oldGetFileTasks = chrome.fileManagerPrivate.getFileTasks;
+  chrome.fileManagerPrivate.getFileTasks = (entries, callback) => {
+    setTimeout(callback, 0, [
+      {
+        taskId: 'text-app-id|app|text',
+        title: 'Text',
+        verb: 'open_with',
+      },
+      {
+        taskId: 'crostini-app-id|crostini|open-with',
+        title: 'Crostini App',
+        verb: 'open_with',
+      }
+    ]);
+  };
+
+  // Save old fmp.sharePathWitCrostini.
+  const oldSharePath = chrome.fileManagerPrivate.sharePathWithCrostini;
+  let sharePathCalled = false;
+  chrome.fileManagerPrivate.sharePathWithCrostini = (entry, callback) => {
+    sharePathCalled = true;
+    oldSharePath(entry, callback);
+  };
+
+  // Save old fmp.executeTask.
+  const oldExecuteTask = chrome.fileManagerPrivate.executeTask;
+  let executeTaskCalled = false;
+  chrome.fileManagerPrivate.executeTask = (taskId, entries, callback) => {
+    executeTaskCalled = true;
+    oldExecuteTask(taskId, entries, callback);
+  };
+  chrome.metricsPrivate.values_ = [];
+
+  test.setupAndWaitUntilReady([], [], [])
+      .then(() => {
+        // Add '/A', and '/A/hello.txt', refresh, 'A' is shown.
+        test.addEntries(
+            [test.ENTRIES.directoryA, test.ENTRIES.helloInA], [], []);
+        assertTrue(test.fakeMouseClick('#refresh-button'), 'click refresh');
+        return test.waitForFiles(
+            test.TestEntryInfo.getExpectedRows([test.ENTRIES.directoryA]));
+      })
+      .then(() => {
+        // Change to 'A' directory, hello.txt is shown.
+        assertTrue(test.fakeMouseDoubleClick('[file-name="A"]'));
+        return test.waitForFiles(
+            test.TestEntryInfo.getExpectedRows([test.ENTRIES.hello]));
+      })
+      .then(() => {
+        // Right click on 'hello.txt' file, wait for dialog with 'Open with'.
+        assertTrue(test.fakeMouseRightClick('[file-name="hello.txt"]'));
+        return test.waitForElement(
+            'cr-menu-item[command="#open-with"]:not([hidden]');
+      })
+      .then(() => {
+        // Click 'Open with', wait for picker.
+        assertTrue(test.fakeMouseClick('cr-menu-item[command="#open-with"]'));
+        return test.waitForElement('#default-tasks-list');
+      })
+      .then(() => {
+        // Ensure picker shows both options.  Click on 'Crostini App'.  Ensure
+        // share path dialog is shown.
+        const list = document.querySelectorAll('#default-tasks-list li div');
+        assertEquals(2, list.length);
+        assertEquals('Open with Crostini App', list[0].innerText);
+        assertEquals('Open with Text', list[1].innerText);
+        assertTrue(test.fakeMouseClick('#default-tasks-list li'));
+        return test.repeatUntil(() => {
+          return document.querySelector('.cr-dialog-title').innerText ===
+              'Share files with Linux' ||
+              test.pending('Waiting for share before open dialog');
+        });
+      })
+      .then(() => {
+        // Validate dialog messages, click 'OK' to share and open.  Ensure
+        // dialog closes.
+        assertEquals(
+            'Let Linux apps open <b>hello.txt</b>.',
+            document.querySelector('.cr-dialog-text').innerHTML);
+        assertTrue(test.fakeMouseClick('button.cr-dialog-ok'));
+        return test.waitForElementLost('.cr-dialog-container');
+      })
+      .then(() => {
+        // Ensure fmp.sharePathWithCrostini, fmp.executeTask called.
+        return test.repeatUntil(() => {
+          return sharePathCalled && executeTaskCalled ||
+              test.pending('Waiting to share and open');
+        });
+      })
+      .then(() => {
+        // Validate UMAs.
+        const lastEnumUma = chrome.metricsPrivate.values_.pop();
+        assertEquals(
+            'FileBrowser.CrostiniShareDialog', lastEnumUma[0].metricName);
+        assertEquals(1 /* ShareBeforeOpen */, lastEnumUma[1]);
+
+        // Restore fmp.*.
+        chrome.fileManagerPrivate.getFileTasks = oldGetFileTasks;
+        chrome.fileManagerPrivate.sharePathWithCrostini = oldSharePath;
+        chrome.fileManagerPrivate.executeTask = oldExecuteTask;
+        done();
+      });
+};
+
+crostiniTasks.testErrorOpeningDownloadsRootWithDefaultCrostiniApp = (done) => {
+  // Save old fmp.getFileTasks and replace with version that returns
+  // crostini app and chrome Text app.
+  let oldGetFileTasks = chrome.fileManagerPrivate.getFileTasks;
+  chrome.fileManagerPrivate.getFileTasks = (entries, callback) => {
+    setTimeout(callback, 0, [{
+                 taskId: 'crostini-app-id|crostini|open-with',
+                 title: 'Crostini App',
+                 verb: 'open_with',
+                 isDefault: true,
+               }]);
+  };
+
+  test.setupAndWaitUntilReady()
+      .then(() => {
+        // Right click on 'world.ogv' file, wait for dialog with the default
+        // task action.
+        assertTrue(test.fakeMouseRightClick('[file-name="world.ogv"]'));
+        return test.repeatUntil(() => {
+          return document
+                     .querySelector(
+                         'cr-menu-item[command="#default-task"]:not([hidden])')
+                     .label === 'Crostini App' ||
+              test.pending('Waiting for default task menu item');
+        });
+      })
+      .then(() => {
+        // Click 'Open with', wait for picker.
+        assertTrue(
+            test.fakeMouseClick('cr-menu-item[command="#default-task"]'));
+        return test.waitForElement('.cr-dialog-container');
+      })
+      .then(() => {
+        // Validate error messages, click 'OK' to close.  Ensure dialog closes.
+        assertEquals(
+            'Unable to open with Crostini App',
+            document.querySelector('.cr-dialog-title').innerText);
+        assertEquals(
+            'To open files with Crostini App, ' +
+                'first copy to Linux files folder.',
+            document.querySelector('.cr-dialog-text').innerText);
+        assertTrue(test.fakeMouseClick('button.cr-dialog-ok'));
+        return test.waitForElementLost('.cr-dialog-container');
+      })
+      .then(() => {
+        // Restore fmp.getFileTasks.
+        chrome.fileManagerPrivate.getFileTasks = oldGetFileTasks;
+        done();
+      });
+};
diff --git a/ui/file_manager/gallery/js/BUILD.gn b/ui/file_manager/gallery/js/BUILD.gn
index 17e71d7..502bb88 100644
--- a/ui/file_manager/gallery/js/BUILD.gn
+++ b/ui/file_manager/gallery/js/BUILD.gn
@@ -62,8 +62,7 @@
   deps = [
     ":entry_list_watcher",
     "../../file_manager/common/js:mock_entry",
-    "../../file_manager/common/js:unittest_util",
-    "//ui/webui/resources/js:webui_resource_test",
+    "//ui/file_manager/base/js:test_error_reporting",
   ]
 }
 
@@ -104,7 +103,7 @@
   deps = [
     ":gallery_data_model",
     ":mock_gallery_item",
-    "../../file_manager/common/js:unittest_util",
+    "//ui/file_manager/base/js:test_error_reporting",
   ]
 }
 
@@ -125,8 +124,7 @@
   deps = [
     ":gallery_item",
     ":mock_gallery_item",
-    "../../file_manager/common/js:unittest_util",
-    "//ui/webui/resources/js:webui_resource_test",
+    "//ui/file_manager/base/js:test_error_reporting",
   ]
 }
 
@@ -143,8 +141,7 @@
   deps = [
     ":gallery_util",
     "../../file_manager/common/js:mock_entry",
-    "../../file_manager/common/js:unittest_util",
-    "//ui/webui/resources/js:webui_resource_test",
+    "//ui/file_manager/base/js:test_error_reporting",
   ]
 }
 
@@ -202,8 +199,7 @@
 js_library("slide_mode_unittest") {
   deps = [
     ":slide_mode",
-    "../../file_manager/common/js:unittest_util",
-    "//ui/webui/resources/js:webui_resource_test",
+    "//ui/file_manager/base/js:test_error_reporting",
   ]
 }
 
diff --git a/ui/file_manager/gallery/js/entry_list_watcher_unittest.js b/ui/file_manager/gallery/js/entry_list_watcher_unittest.js
index 633afe4f..d148fc9 100644
--- a/ui/file_manager/gallery/js/entry_list_watcher_unittest.js
+++ b/ui/file_manager/gallery/js/entry_list_watcher_unittest.js
@@ -5,6 +5,44 @@
 var chrome;
 var mockFileSystem;
 
+/**
+ * @constructor
+ * @struct
+ */
+function MockAPIEvent() {
+  /**
+   * @type {!Array<!Function>}
+   * @const
+   */
+  this.listeners_ = [];
+}
+
+/**
+ * @param {!Function} callback
+ */
+MockAPIEvent.prototype.addListener = function(callback) {
+  this.listeners_.push(callback);
+};
+
+/**
+ * @param {!Function} callback
+ */
+MockAPIEvent.prototype.removeListener = function(callback) {
+  var index = this.listeners_.indexOf(callback);
+  if (index < 0)
+    throw new Error('Tried to remove an unregistered listener.');
+  this.listeners_.splice(index, 1);
+};
+
+/**
+ * @param {...*} var_args
+ */
+MockAPIEvent.prototype.dispatch = function(var_args) {
+  for (var i = 0; i < this.listeners_.length; i++) {
+    this.listeners_[i].apply(null, arguments);
+  }
+};
+
 function webkitResolveLocalFileSystemURL(url, callback) {
   var paths = Object.keys(mockFileSystem.entries);
   for (var i = 0; i < paths.length; i++) {
diff --git a/ui/file_manager/gallery/js/image_editor/BUILD.gn b/ui/file_manager/gallery/js/image_editor/BUILD.gn
index e7a6675..1ef2fd38 100644
--- a/ui/file_manager/gallery/js/image_editor/BUILD.gn
+++ b/ui/file_manager/gallery/js/image_editor/BUILD.gn
@@ -124,8 +124,8 @@
   deps = [
     ":image_encoder",
     ":test_util",
-    "../../../file_manager/common/js:unittest_util",
     "../../../file_manager/foreground/js/metadata:metadata_parser",
+    "//ui/file_manager/base/js:test_error_reporting",
   ]
   externs_list = [ "../../../externs/metadata_worker_window.js" ]
 }
diff --git a/ui/file_manager/image_loader/BUILD.gn b/ui/file_manager/image_loader/BUILD.gn
index f883211..b0f9ceb 100644
--- a/ui/file_manager/image_loader/BUILD.gn
+++ b/ui/file_manager/image_loader/BUILD.gn
@@ -85,8 +85,7 @@
 js_library("image_loader_client_unittest") {
   deps = [
     ":image_loader_client",
-    "../file_manager/common/js:unittest_util",
-    "//ui/webui/resources/js:webui_resource_test",
+    "//ui/file_manager/base/js:test_error_reporting",
   ]
 }
 
@@ -100,8 +99,7 @@
 js_library("piex_loader_unittest") {
   deps = [
     ":piex_loader",
-    "../file_manager/common/js:unittest_util",
-    "//ui/webui/resources/js:webui_resource_test",
+    "//ui/file_manager/base/js:test_error_reporting",
   ]
 }
 
diff --git a/ui/file_manager/integration_tests/file_manager/my_files.js b/ui/file_manager/integration_tests/file_manager/my_files.js
index 4ad283f..fc7bc54 100644
--- a/ui/file_manager/integration_tests/file_manager/my_files.js
+++ b/ui/file_manager/integration_tests/file_manager/my_files.js
@@ -325,3 +325,107 @@
     },
   ]);
 };
+
+/**
+ * Check naming a folder after navigating inside MyFiles using file list (RHS).
+ * crbug.com/889636.
+ */
+testcase.myFilesFolderRename = function() {
+  let appId;
+  const textInput = '#file-list .table-row[renaming] input.rename';
+  StepsRunner.run([
+    // Open Files app on local Downloads.
+    function() {
+      setupAndWaitUntilReady(
+          null, RootPath.DOWNLOADS, this.next, [ENTRIES.photos], []);
+    },
+    // Select "My files" folder via directory tree.
+    function(result) {
+      appId = result.windowId;
+      const myFilesQuery = '#directory-tree [entry-label="My files"]';
+      const isDriveQuery = false;
+      remoteCall.callRemoteTestUtil(
+          'selectInDirectoryTree', appId, [myFilesQuery, isDriveQuery],
+          this.next);
+    },
+    // Wait for Downloads to load.
+    function(result) {
+      chrome.test.assertTrue(!!result, 'selectInDirectoryTree failed');
+      const expectedRows = [
+        ['Downloads', '--', 'Folder'],
+        ['Play files', '--', 'Folder'],
+        ['Linux files', '--', 'Folder'],
+      ];
+      remoteCall
+          .waitForFiles(
+              appId, expectedRows,
+              {ignoreFileSize: true, ignoreLastModifiedTime: true})
+          .then(this.next);
+    },
+    // Select Downloads via file list.
+    function() {
+      const downloads = ['Downloads'];
+      remoteCall.callRemoteTestUtil('selectFile', appId, downloads)
+          .then(result => {
+            chrome.test.assertTrue(!!result, 'selectFile failed');
+            this.next();
+          });
+    },
+    // Open Downloads via file list.
+    function() {
+      const fileListItem = '#file-list .table-row';
+      const key = [fileListItem, 'Enter', false, false, false];
+      remoteCall.callRemoteTestUtil('fakeKeyDown', appId, key).then(this.next);
+    },
+    // Wait for Downloads to load.
+    function(result) {
+      chrome.test.assertTrue(!!result, 'fakeKeyDown failed');
+      remoteCall.waitForFiles(appId, [ENTRIES.photos.getExpectedRow()])
+          .then(this.next);
+    },
+    // Select photos via file list.
+    function() {
+      const folder = ['photos'];
+      remoteCall.callRemoteTestUtil('selectFile', appId, folder)
+          .then(result => {
+            chrome.test.assertTrue(!!result, 'selectFile failed');
+            this.next();
+          });
+    },
+    // Press Ctrl+Enter for start renaming.
+    function() {
+      const fileListItem = '#file-list .table-row';
+      const key = [fileListItem, 'Enter', true, false, false];
+      remoteCall.callRemoteTestUtil('fakeKeyDown', appId, key).then(this.next);
+    },
+    // Wait for input for renaming to appear.
+    function(result) {
+      chrome.test.assertTrue(result, 'fakeKeyDown ctrl+Enter failed');
+      // Check: the renaming text input should be shown in the file list.
+      remoteCall.waitForElement(appId, textInput).then(this.next);
+    },
+    // Type new name.
+    function() {
+      remoteCall.callRemoteTestUtil('inputText', appId, [textInput, 'new name'])
+          .then(this.next);
+    },
+    // Send Enter key to the text input.
+    function() {
+      const key = [textInput, 'Enter', false, false, false];
+      remoteCall.callRemoteTestUtil('fakeKeyDown', appId, key).then(this.next);
+    },
+    // Wait for new name to appear on the file list.
+    function(result) {
+      chrome.test.assertTrue(result, 'fakeKeyDown failed');
+      const expectedRows = [['new name', '--', 'Folder', '']];
+      remoteCall
+          .waitForFiles(
+              appId, expectedRows,
+              {ignoreFileSize: true, ignoreLastModifiedTime: true})
+          .then(this.next);
+    },
+    function() {
+      checkIfNoErrorsOccured(this.next);
+    },
+  ]);
+};
diff --git a/ui/file_manager/integration_tests/file_manager/quick_view.js b/ui/file_manager/integration_tests/file_manager/quick_view.js
index 1f15419..e351fffb 100644
--- a/ui/file_manager/integration_tests/file_manager/quick_view.js
+++ b/ui/file_manager/integration_tests/file_manager/quick_view.js
@@ -723,6 +723,68 @@
 };
 
 /**
+ * Tests opening Quick View containing an audio file.
+ */
+testcase.openQuickViewAudio = function() {
+  const caller = getCaller();
+  let appId;
+
+  /**
+   * The <webview> resides in the <files-safe-media type="audio"> shadow DOM,
+   * which is a child of the #quick-view shadow DOM.
+   */
+  const webView = ['#quick-view', 'files-safe-media[type="audio"]', 'webview'];
+
+  StepsRunner.run([
+    // Open Files app on Downloads containing ENTRIES.beautiful song.
+    function() {
+      setupAndWaitUntilReady(
+          null, RootPath.DOWNLOADS, this.next, [ENTRIES.beautiful], []);
+    },
+    // Open the file in Quick View.
+    function(results) {
+      appId = results.windowId;
+      const openSteps = openQuickViewSteps(appId, ENTRIES.beautiful.nameText);
+      StepsRunner.run(openSteps).then(this.next);
+    },
+    // Wait for the Quick View <webview> to load and display its content.
+    function() {
+     function checkWebViewAudioLoaded(elements) {
+        let haveElements = Array.isArray(elements) && elements.length === 1;
+        if (haveElements)
+          haveElements = elements[0].styles.display.includes('block');
+        if (!haveElements || elements[0].attributes.loaded !== '')
+          return pending(caller, 'Waiting for <webview> to load.');
+        return;
+      }
+      repeatUntil(function() {
+        return remoteCall
+            .callRemoteTestUtil(
+                'deepQueryAllElements', appId, [webView, ['display']])
+            .then(checkWebViewAudioLoaded);
+      }).then(this.next);
+    },
+    // Get the <webview> document.body backgroundColor style.
+    function() {
+      const getBackgroundStyle =
+          'window.getComputedStyle(document.body).backgroundColor';
+      remoteCall
+        .callRemoteTestUtil(
+            'deepExecuteScriptInWebView', appId, [webView, getBackgroundStyle])
+        .then(this.next);
+    },
+    // Check: the <webview> body backgroundColor should be transparent black.
+    function(backgroundColor) {
+      chrome.test.assertEq('rgba(0, 0, 0, 0)', backgroundColor[0]);
+      this.next();
+    },
+    function() {
+      checkIfNoErrorsOccured(this.next);
+    },
+  ]);
+};
+
+/**
  * Tests opening Quick View containing an image.
  */
 testcase.openQuickViewImage = function() {
diff --git a/ui/ozone/platform/wayland/wayland_window.cc b/ui/ozone/platform/wayland/wayland_window.cc
index 27ddd18..0d02721 100644
--- a/ui/ozone/platform/wayland/wayland_window.cc
+++ b/ui/ozone/platform/wayland/wayland_window.cc
@@ -74,7 +74,8 @@
     : delegate_(delegate),
       connection_(connection),
       xdg_shell_objects_factory_(new XDGShellObjectFactory()),
-      state_(PlatformWindowState::PLATFORM_WINDOW_STATE_NORMAL) {
+      state_(PlatformWindowState::PLATFORM_WINDOW_STATE_NORMAL),
+      pending_state_(PlatformWindowState::PLATFORM_WINDOW_STATE_UNKNOWN) {
   // Set a class property key, which allows |this| to be used for interactive
   // events, e.g. move or resize.
   SetWmMoveResizeHandler(this, AsWmMoveResizeHandler());
@@ -296,6 +297,17 @@
 void WaylandWindow::ToggleFullscreen() {
   DCHECK(xdg_surface_);
 
+  // There are some cases, when Chromium triggers a fullscreen state change
+  // before the surface is activated. In such cases, Wayland may ignore state
+  // changes and such flags as --kiosk or --start-fullscreen will be ignored.
+  // To overcome this, set a pending state, and once the surface is activated,
+  // trigger the change.
+  if (!is_active_) {
+    DCHECK(!IsFullscreen());
+    pending_state_ = PlatformWindowState::PLATFORM_WINDOW_STATE_FULLSCREEN;
+    return;
+  }
+
   // TODO(msisov, tonikitoo): add multiscreen support. As the documentation says
   // if xdg_surface_set_fullscreen() is not provided with wl_output, it's up to
   // the compositor to choose which display will be used to map this surface.
@@ -468,7 +480,7 @@
 
   // Ensure that manually handled state changes to fullscreen correspond to the
   // configuration events from a compositor.
-  DCHECK(is_fullscreen == IsFullscreen());
+  DCHECK_EQ(is_fullscreen, IsFullscreen());
 
   // There are two cases, which must be handled for the minimized state.
   // The first one is the case, when the surface goes into the minimized state
@@ -525,6 +537,8 @@
 
   if (did_active_change)
     delegate_->OnActivationChanged(is_active_);
+
+  MaybeTriggerPendingStateChange();
 }
 
 void WaylandWindow::OnCloseRequest() {
@@ -571,6 +585,16 @@
   return state_ == PlatformWindowState::PLATFORM_WINDOW_STATE_FULLSCREEN;
 }
 
+void WaylandWindow::MaybeTriggerPendingStateChange() {
+  if (pending_state_ == PlatformWindowState::PLATFORM_WINDOW_STATE_UNKNOWN ||
+      !is_active_)
+    return;
+  DCHECK_EQ(pending_state_,
+            PlatformWindowState::PLATFORM_WINDOW_STATE_FULLSCREEN);
+  pending_state_ = PlatformWindowState::PLATFORM_WINDOW_STATE_UNKNOWN;
+  ToggleFullscreen();
+}
+
 WaylandWindow* WaylandWindow::GetParentWindow(
     gfx::AcceleratedWidget parent_widget) {
   WaylandWindow* parent_window = connection_->GetWindow(parent_widget);
diff --git a/ui/ozone/platform/wayland/wayland_window.h b/ui/ozone/platform/wayland/wayland_window.h
index e1cb0f6..ad9eb0c 100644
--- a/ui/ozone/platform/wayland/wayland_window.h
+++ b/ui/ozone/platform/wayland/wayland_window.h
@@ -129,6 +129,8 @@
   bool IsMaximized() const;
   bool IsFullscreen() const;
 
+  void MaybeTriggerPendingStateChange();
+
   // Creates a popup window, which is visible as a menu window.
   void CreateXdgPopup();
   // Creates a surface window, which is visible as a main window.
@@ -171,6 +173,9 @@
 
   // Stores current states of the window.
   ui::PlatformWindowState state_;
+  // Stores a pending state of the window, which is used before the surface is
+  // activated.
+  ui::PlatformWindowState pending_state_;
 
   bool is_active_ = false;
   bool is_minimizing_ = false;
diff --git a/ui/ozone/platform/wayland/wayland_window_unittest.cc b/ui/ozone/platform/wayland/wayland_window_unittest.cc
index a97b92eb..abc14e6 100644
--- a/ui/ozone/platform/wayland/wayland_window_unittest.cc
+++ b/ui/ozone/platform/wayland/wayland_window_unittest.cc
@@ -219,6 +219,8 @@
   EXPECT_EQ(PLATFORM_WINDOW_STATE_NORMAL, window_->GetPlatformWindowState());
 
   ScopedWlArray states = InitializeWlArrayWithActivatedState();
+  SendConfigureEvent(0, 0, 1, states.get());
+  Sync();
 
   AddStateToWlArray(XDG_SURFACE_STATE_FULLSCREEN, states.get());
 
@@ -230,7 +232,7 @@
   // comment in the WaylandWindow::ToggleFullscreen.
   EXPECT_EQ(window_->GetPlatformWindowState(),
             PLATFORM_WINDOW_STATE_FULLSCREEN);
-  SendConfigureEvent(0, 0, 1, states.get());
+  SendConfigureEvent(0, 0, 2, states.get());
   Sync();
 
   EXPECT_CALL(*GetXdgSurface(), UnsetFullscreen());
@@ -240,7 +242,45 @@
   EXPECT_EQ(window_->GetPlatformWindowState(), PLATFORM_WINDOW_STATE_UNKNOWN);
   // Reinitialize wl_array, which removes previous old states.
   states = InitializeWlArrayWithActivatedState();
+  SendConfigureEvent(0, 0, 3, states.get());
+  Sync();
+}
+
+TEST_P(WaylandWindowTest, StartWithFullscreen) {
+  // Make sure the window is initialized to normal state from the beginning.
+  EXPECT_EQ(PLATFORM_WINDOW_STATE_NORMAL, window_->GetPlatformWindowState());
+
+  // The state must not be changed to the fullscreen before the surface is
+  // activated.
+  EXPECT_CALL(*GetXdgSurface(), SetFullscreen()).Times(0);
+  EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0);
+  window_->ToggleFullscreen();
+  // The state of the window must still be a normal one.
+  EXPECT_EQ(window_->GetPlatformWindowState(), PLATFORM_WINDOW_STATE_NORMAL);
+
+  Sync();
+
+  // Once the surface will be activated, the window will automatically trigger
+  // the state change.
+  EXPECT_CALL(*GetXdgSurface(), SetFullscreen());
+  EXPECT_CALL(delegate_,
+              OnWindowStateChanged(Eq(PLATFORM_WINDOW_STATE_FULLSCREEN)));
+
+  // Activate the surface.
+  ScopedWlArray states = InitializeWlArrayWithActivatedState();
+  SendConfigureEvent(0, 0, 1, states.get());
+
+  Sync();
+
+  // The wayland window manually handles the fullscreen state changes, and it
+  // must change to a fullscreen before the state change is confirmed by the
+  // wayland. See comment in the WaylandWindow::ToggleFullscreen.
+  EXPECT_EQ(window_->GetPlatformWindowState(),
+            PLATFORM_WINDOW_STATE_FULLSCREEN);
+
+  AddStateToWlArray(XDG_SURFACE_STATE_FULLSCREEN, states.get());
   SendConfigureEvent(0, 0, 2, states.get());
+
   Sync();
 }
 
@@ -314,6 +354,8 @@
   const gfx::Rect current_bounds = window_->GetBounds();
 
   ScopedWlArray states = InitializeWlArrayWithActivatedState();
+  SendConfigureEvent(0, 0, 1, states.get());
+  Sync();
 
   gfx::Rect restored_bounds = window_->GetRestoredBoundsInPixels();
   EXPECT_EQ(restored_bounds, gfx::Rect());
@@ -323,7 +365,7 @@
   EXPECT_CALL(delegate_, OnBoundsChanged(Eq(fullscreen_bounds)));
   window_->ToggleFullscreen();
   AddStateToWlArray(XDG_SURFACE_STATE_FULLSCREEN, states.get());
-  SendConfigureEvent(fullscreen_bounds.width(), fullscreen_bounds.height(), 1,
+  SendConfigureEvent(fullscreen_bounds.width(), fullscreen_bounds.height(), 2,
                      states.get());
   Sync();
   restored_bounds = window_->GetRestoredBoundsInPixels();
@@ -338,7 +380,7 @@
   window_->Restore();
   // Reinitialize wl_array, which removes previous old states.
   states = InitializeWlArrayWithActivatedState();
-  SendConfigureEvent(0, 0, 2, states.get());
+  SendConfigureEvent(0, 0, 3, states.get());
   Sync();
   bounds = window_->GetBounds();
   EXPECT_EQ(bounds, restored_bounds);
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/BUILD.gn b/ui/webui/resources/cr_components/chromeos/multidevice_setup/BUILD.gn
index 86fda9f..616ad6e 100644
--- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/BUILD.gn
+++ b/ui/webui/resources/cr_components/chromeos/multidevice_setup/BUILD.gn
@@ -18,6 +18,7 @@
     ":setup_succeeded_page",
     ":start_setup_page",
     ":ui_page_container_behavior",
+    "//ui/webui/resources/js:web_ui_listener_behavior",
   ]
 }
 
@@ -116,6 +117,7 @@
 js_library("start_setup_page") {
   deps = [
     ":ui_page_container_behavior",
+    "//ui/webui/resources/js:web_ui_listener_behavior",
   ]
 }
 
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.html b/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.html
index 7a3325d24..a410816b 100644
--- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.html
+++ b/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.html
@@ -5,6 +5,8 @@
 <link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/ui_page.html">
 <link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/ui_page_container_behavior.html">
 <link rel="import" href="chrome://resources/html/cr.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
+<link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 
 <dom-module id="start-setup-page">
@@ -78,7 +80,7 @@
     </style>
 
     <ui-page header-text="[[headerText]]" icon-name="google-g">
-      <span slot="message" inner-h-t-m-l="[[messageHtml]]"></span>
+      <span slot="message" id="multidevice-summary-message" inner-h-t-m-l="[[messageHtml]]"></span>
       <div slot="additional-content">
         <div id="selector-and-details-container">
           <div id="deviceSelectionContainer" class="flex">
@@ -109,7 +111,7 @@
             </div>
             <div class="feature-detail">
               <iron-icon icon="multidevice-setup-icons-20:messages"></iron-icon>
-              <span inner-h-t-m-l="
+              <span id="awm-summary-message" inner-h-t-m-l="
                   [[i18nAdvanced('startSetupPageFeatureListAwm')]]">
               </span>
             </div>
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.js b/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.js
index 949fc8e..85437ce 100644
--- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.js
+++ b/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.js
@@ -58,8 +58,31 @@
   behaviors: [
     UiPageContainerBehavior,
     I18nBehavior,
+    WebUIListenerBehavior,
   ],
 
+  /** @override */
+  attached: function() {
+    this.addWebUIListener(
+        'multidevice_setup.initializeSetupFlow',
+        this.initializeSetupFlow_.bind(this));
+  },
+
+  /** @private */
+  initializeSetupFlow_: function() {
+    // The "Learn More" links are inside a grdp string, so we cannot actually
+    // add an onclick handler directly to the html. Instead, grab the two and
+    // manaully add onclick handlers.
+    let helpArticleLinks = [
+      this.$$('#multidevice-summary-message a'),
+      this.$$('#awm-summary-message a')
+    ];
+    for (let i = 0; i < helpArticleLinks.length; i++) {
+      helpArticleLinks[i].onclick = this.fire.bind(
+          this, 'open-learn-more-webview-requested', helpArticleLinks[i].href);
+    }
+  },
+
   /**
    * @param {!Array<!chromeos.deviceSync.mojom.RemoteDevice>} devices
    * @return {string} Label for devices selection content.