diff --git a/DEPS b/DEPS
index 8b4e9df..1b30258 100644
--- a/DEPS
+++ b/DEPS
@@ -121,11 +121,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '5f1692c60a43bfb9f9039e288862c82a9f7cbc8e',
+  'skia_revision': 'd4c7458f64270d605c04aa24e2171209c00a2d32',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '5df21f21f1704932e73a91c8935f68126d408092',
+  'v8_revision': 'd4d90563417c7f8dfd2fffddf5e1f50ad4ad5d63',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -169,7 +169,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling NaCl
   # and whatever else without interference from each other.
-  'nacl_revision': '202ccbd707ded4002aa402b9da0ea4e4026f3a1d',
+  'nacl_revision': '1ea07c56ac9b57c30eb784ab2af582af0cdd4b08',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
@@ -181,7 +181,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '51771a7cad87fdf00c949740c06205e71de8830a',
+  'catapult_revision': '865a64df6576494643d2eec97f9abbd89b37ed9c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -197,7 +197,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'feed_revision': '84617ef81ded68396acf495e8392266950972223',
+  'feed_revision': 'd3514e6cb9ac00229a76e2aedfa367fa71fa4bd2',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling android_sdk_build-tools_version
   # and whatever else without interference from each other.
@@ -245,7 +245,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '93158ebede898d3f243c375d30fe287c069a69e7',
+  'dawn_revision': '110bc7918fc269862bbab2dd9249e80338e15f7d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -679,7 +679,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '8743424b5d6d73e0c62cc87a21f9ac17f151495e',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '599dad1eadafee83bd2380af69d2a0d290d854c0',
       'condition': 'checkout_linux',
   },
 
@@ -704,7 +704,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '936a994608348285d6120127c906893ae1d37c24',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'f3d4ab3430828d2be748ba2f164b04cc115ba38c',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -845,7 +845,7 @@
     Var('chromium_git') + '/chromium/deps/hunspell_dictionaries.git' + '@' + 'a9bac57ce6c9d390a52ebaad3259f5fdb871210e',
 
   'src/third_party/icu':
-    Var('chromium_git') + '/chromium/deps/icu.git' + '@' + '23de01679d298bf9fb964ebede32f2157729ba96',
+    Var('chromium_git') + '/chromium/deps/icu.git' + '@' + 'd65301491c513d49163ad29c853eb85c02c8d5b4',
 
   'src/third_party/icu4j': {
       'packages': [
@@ -1036,7 +1036,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '43f09d829d87e595baeb9c27bf4a859e079a675f',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  'e1e1e01281079848dc884f272b5a12a49ed56422',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78',
@@ -1230,7 +1230,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@dd0edf7144157d89448238b6c1ea828e6507147d',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@aae753f5a641dd15216b9fc9f3e63d7828c264c5',
     'condition': 'checkout_src_internal',
   },
 
@@ -2543,7 +2543,7 @@
     ],
   },
   {
-    'name': 'gvr_static_shim_android_arm',
+    'name': 'gvr_static_shim_android_arm_1',
     'pattern': '\\.sha1',
     'condition': 'checkout_android',
     'action': [ 'python',
@@ -2551,11 +2551,11 @@
                 '--no_resume',
                 '--no_auth',
                 '--bucket', 'chromium-gvr-static-shim',
-                '-s', 'src/third_party/gvr-android-sdk/libgvr_shim_static_arm.a.sha1',
+                '-s', 'src/third_party/gvr-android-sdk/libgvr_shim_static_arm_1.a.sha1',
     ],
   },
   {
-    'name': 'gvr_static_shim_android_arm64',
+    'name': 'gvr_static_shim_android_arm_ndk1',
     'pattern': '\\.sha1',
     'condition': 'checkout_android',
     'action': [ 'python',
@@ -2563,23 +2563,11 @@
                 '--no_resume',
                 '--no_auth',
                 '--bucket', 'chromium-gvr-static-shim',
-                '-s', 'src/third_party/gvr-android-sdk/libgvr_shim_static_arm64.a.sha1',
-      ],
-  },
-  {
-    'name': 'gvr_static_shim_custom_libcxx_android_arm',
-    'pattern': '\\.sha1',
-    'condition': 'checkout_android',
-    'action': [ 'python',
-                'src/third_party/depot_tools/download_from_google_storage.py',
-                '--no_resume',
-                '--no_auth',
-                '--bucket', 'chromium-gvr-static-shim',
-                '-s', 'src/third_party/gvr-android-sdk/libgvr_shim_static_custom_libcxx_arm.a.sha1',
+                '-s', 'src/third_party/gvr-android-sdk/libgvr_shim_static_arm_ndk1.a.sha1',
     ],
   },
   {
-    'name': 'gvr_static_shim_custom_libcxx_android_arm64',
+    'name': 'gvr_static_shim_android_arm_Cr',
     'pattern': '\\.sha1',
     'condition': 'checkout_android',
     'action': [ 'python',
@@ -2587,8 +2575,44 @@
                 '--no_resume',
                 '--no_auth',
                 '--bucket', 'chromium-gvr-static-shim',
-                '-s', 'src/third_party/gvr-android-sdk/libgvr_shim_static_custom_libcxx_arm64.a.sha1',
-      ],
+                '-s', 'src/third_party/gvr-android-sdk/libgvr_shim_static_arm_Cr.a.sha1',
+    ],
+  },
+  {
+    'name': 'gvr_static_shim_android_arm64_1',
+    'pattern': '\\.sha1',
+    'condition': 'checkout_android',
+    'action': [ 'python',
+                'src/third_party/depot_tools/download_from_google_storage.py',
+                '--no_resume',
+                '--no_auth',
+                '--bucket', 'chromium-gvr-static-shim',
+                '-s', 'src/third_party/gvr-android-sdk/libgvr_shim_static_arm64_1.a.sha1',
+    ],
+  },
+  {
+    'name': 'gvr_static_shim_android_arm64_ndk1',
+    'pattern': '\\.sha1',
+    'condition': 'checkout_android',
+    'action': [ 'python',
+                'src/third_party/depot_tools/download_from_google_storage.py',
+                '--no_resume',
+                '--no_auth',
+                '--bucket', 'chromium-gvr-static-shim',
+                '-s', 'src/third_party/gvr-android-sdk/libgvr_shim_static_arm64_ndk1.a.sha1',
+    ],
+  },
+  {
+    'name': 'gvr_static_shim_android_arm64_Cr',
+    'pattern': '\\.sha1',
+    'condition': 'checkout_android',
+    'action': [ 'python',
+                'src/third_party/depot_tools/download_from_google_storage.py',
+                '--no_resume',
+                '--no_auth',
+                '--bucket', 'chromium-gvr-static-shim',
+                '-s', 'src/third_party/gvr-android-sdk/libgvr_shim_static_arm64_Cr.a.sha1',
+    ],
   },
   {
     'name': 'vr_assets',
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index 3eb5dee0..05bad9eb 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -644,7 +644,6 @@
     "browser/popup_touch_handle_drawable.h",
     "browser/render_thread_manager.cc",
     "browser/render_thread_manager.h",
-    "browser/render_thread_manager_client.h",
     "browser/renderer_host/auto_login_parser.cc",
     "browser/renderer_host/auto_login_parser.h",
     "browser/renderer_host/aw_render_view_host_ext.cc",
diff --git a/android_webview/browser/aw_content_browser_client.cc b/android_webview/browser/aw_content_browser_client.cc
index d8004476..a2aadee 100644
--- a/android_webview/browser/aw_content_browser_client.cc
+++ b/android_webview/browser/aw_content_browser_client.cc
@@ -522,7 +522,7 @@
     content::RenderFrameHost* opener,
     const GURL& opener_url,
     const GURL& opener_top_level_frame_url,
-    const GURL& source_origin,
+    const url::Origin& source_origin,
     content::mojom::WindowContainerType container_type,
     const GURL& target_url,
     const content::Referrer& referrer,
diff --git a/android_webview/browser/aw_content_browser_client.h b/android_webview/browser/aw_content_browser_client.h
index ac570e2..08ef1d7 100644
--- a/android_webview/browser/aw_content_browser_client.h
+++ b/android_webview/browser/aw_content_browser_client.h
@@ -123,7 +123,7 @@
   bool CanCreateWindow(content::RenderFrameHost* opener,
                        const GURL& opener_url,
                        const GURL& opener_top_level_frame_url,
-                       const GURL& source_origin,
+                       const url::Origin& source_origin,
                        content::mojom::WindowContainerType container_type,
                        const GURL& target_url,
                        const content::Referrer& referrer,
diff --git a/android_webview/browser/aw_gl_functor.cc b/android_webview/browser/aw_gl_functor.cc
index 19bbe23..ecc2b68 100644
--- a/android_webview/browser/aw_gl_functor.cc
+++ b/android_webview/browser/aw_gl_functor.cc
@@ -5,6 +5,7 @@
 #include "android_webview/browser/aw_gl_functor.h"
 
 #include "android_webview/public/browser/draw_gl.h"
+#include "base/stl_util.h"
 #include "base/task/post_task.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
@@ -22,7 +23,7 @@
                            void* spare) {
   // |view_context| is the value that was returned from the java
   // AwContents.onPrepareDrawGL; this cast must match the code there.
-  reinterpret_cast<android_webview::RenderThreadManager*>(view_context)
+  reinterpret_cast<android_webview::AwGLFunctor*>(view_context)
       ->DrawGL(draw_info);
 }
 }
@@ -36,7 +37,6 @@
 AwGLFunctor::AwGLFunctor(const JavaObjectWeakGlobalRef& java_ref)
     : java_ref_(java_ref),
       render_thread_manager_(
-          this,
           base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::UI})) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   ++g_instance_count;
@@ -75,14 +75,59 @@
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& obj) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  render_thread_manager_.DeleteHardwareRendererOnUI();
+  RenderThreadManager::InsideHardwareReleaseReset release_reset(
+      &render_thread_manager_);
+  DetachFunctorFromView();
+
+  // Receiving at least one frame is a precondition for
+  // initialization (such as looing up GL bindings and constructing
+  // hardware_renderer_).
+  bool draw_functor_succeeded = RequestInvokeGL(true);
+  if (!draw_functor_succeeded) {
+    LOG(ERROR) << "Unable to free GL resources. Has the Window leaked?";
+    // Calling release on wrong thread intentionally.
+    render_thread_manager_.DestroyHardwareRendererOnRT(true /* save_restore */);
+  }
 }
 
-jlong AwGLFunctor::GetAwDrawGLViewContext(
+void AwGLFunctor::DrawGL(AwDrawGLInfo* draw_info) {
+  TRACE_EVENT0("android_webview,toplevel", "DrawFunctor");
+  bool save_restore = draw_info->version < 3;
+  switch (draw_info->mode) {
+    case AwDrawGLInfo::kModeSync:
+      TRACE_EVENT_INSTANT0("android_webview", "kModeSync",
+                           TRACE_EVENT_SCOPE_THREAD);
+      render_thread_manager_.CommitFrameOnRT();
+      break;
+    case AwDrawGLInfo::kModeProcessNoContext:
+      LOG(ERROR) << "Received unexpected kModeProcessNoContext";
+      FALLTHROUGH;
+    case AwDrawGLInfo::kModeProcess:
+      render_thread_manager_.DestroyHardwareRendererOnRT(save_restore);
+      break;
+    case AwDrawGLInfo::kModeDraw: {
+      HardwareRendererDrawParams params{
+          draw_info->clip_left,   draw_info->clip_top, draw_info->clip_right,
+          draw_info->clip_bottom, draw_info->width,    draw_info->height,
+          draw_info->is_layer,
+      };
+      static_assert(base::size(decltype(draw_info->transform){}) ==
+                        base::size(params.transform),
+                    "transform size mismatch");
+      for (unsigned int i = 0; i < base::size(params.transform); ++i) {
+        params.transform[i] = draw_info->transform[i];
+      }
+      render_thread_manager_.DrawOnRT(save_restore, &params);
+      break;
+    }
+  }
+}
+
+void AwGLFunctor::RemoveFromCompositorFrameProducer(
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& obj) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  return reinterpret_cast<intptr_t>(&render_thread_manager_);
+  render_thread_manager_.RemoveFromCompositorFrameProducerOnUI();
 }
 
 jlong AwGLFunctor::GetCompositorFrameConsumer(
diff --git a/android_webview/browser/aw_gl_functor.h b/android_webview/browser/aw_gl_functor.h
index 12816fd0..b54d5134 100644
--- a/android_webview/browser/aw_gl_functor.h
+++ b/android_webview/browser/aw_gl_functor.h
@@ -7,31 +7,34 @@
 
 #include "android_webview/browser/compositor_frame_consumer.h"
 #include "android_webview/browser/render_thread_manager.h"
-#include "android_webview/browser/render_thread_manager_client.h"
 #include "base/android/jni_weak_ref.h"
 
+struct AwDrawGLInfo;
+
 namespace android_webview {
 
-class AwGLFunctor : public RenderThreadManagerClient {
+class AwGLFunctor {
  public:
-  bool RequestInvokeGL(bool wait_for_completion) override;
-  void DetachFunctorFromView() override;
-
   AwGLFunctor(const JavaObjectWeakGlobalRef& java_ref);
-  ~AwGLFunctor() override;
+  ~AwGLFunctor();
 
   void Destroy(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj);
   void DeleteHardwareRenderer(JNIEnv* env,
                               const base::android::JavaParamRef<jobject>& obj);
-  jlong GetAwDrawGLViewContext(JNIEnv* env,
-                               const base::android::JavaParamRef<jobject>& obj);
+  void RemoveFromCompositorFrameProducer(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>& obj);
   jlong GetCompositorFrameConsumer(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj);
   jlong GetAwDrawGLFunction(JNIEnv* env,
                             const base::android::JavaParamRef<jobject>& obj);
 
+  void DrawGL(AwDrawGLInfo* draw_info);
+
  private:
+  bool RequestInvokeGL(bool wait_for_completion);
+  void DetachFunctorFromView();
   CompositorFrameConsumer* GetCompositorFrameConsumer() {
     return &render_thread_manager_;
   }
diff --git a/android_webview/browser/browser_view_renderer.cc b/android_webview/browser/browser_view_renderer.cc
index 182c67e..489c7fa 100644
--- a/android_webview/browser/browser_view_renderer.cc
+++ b/android_webview/browser/browser_view_renderer.cc
@@ -107,14 +107,16 @@
       max_page_scale_factor_(0.f),
       on_new_picture_enable_(false),
       clear_view_(false),
-      offscreen_pre_raster_(false) {}
+      offscreen_pre_raster_(false),
+      weak_ptr_factory_(this) {}
 
 BrowserViewRenderer::~BrowserViewRenderer() {
   DCHECK(compositor_map_.empty());
-  SetCurrentCompositorFrameConsumer(nullptr);
-  while (compositor_frame_consumers_.size()) {
-    RemoveCompositorFrameConsumer(*compositor_frame_consumers_.begin());
-  }
+  DCHECK(!current_compositor_frame_consumer_);
+}
+
+base::WeakPtr<CompositorFrameProducer> BrowserViewRenderer::GetWeakPtr() {
+  return weak_ptr_factory_.GetWeakPtr();
 }
 
 void BrowserViewRenderer::SetCurrentCompositorFrameConsumer(
@@ -124,7 +126,6 @@
   }
   current_compositor_frame_consumer_ = compositor_frame_consumer;
   if (current_compositor_frame_consumer_) {
-    compositor_frame_consumers_.insert(current_compositor_frame_consumer_);
     current_compositor_frame_consumer_->SetCompositorFrameProducer(this);
     OnParentDrawConstraintsUpdated(current_compositor_frame_consumer_);
   }
@@ -220,7 +221,6 @@
   external_draw_constraints_ =
       current_compositor_frame_consumer_->GetParentDrawConstraintsOnUI();
 
-  ReturnResourceFromParent(current_compositor_frame_consumer_);
   UpdateMemoryPolicy();
 
   gfx::Transform transform_for_tile_priority =
@@ -255,12 +255,6 @@
   return viewport_rect_for_tile_priority;
 }
 
-void BrowserViewRenderer::ReturnedResourceAvailable(
-    CompositorFrameConsumer* compositor_frame_consumer) {
-  DCHECK(compositor_frame_consumers_.count(compositor_frame_consumer));
-  ReturnResourceFromParent(compositor_frame_consumer);
-}
-
 void BrowserViewRenderer::OnParentDrawConstraintsUpdated(
     CompositorFrameConsumer* compositor_frame_consumer) {
   DCHECK(compositor_frame_consumer);
@@ -276,20 +270,10 @@
 }
 
 void BrowserViewRenderer::RemoveCompositorFrameConsumer(
-    CompositorFrameConsumer* compositor_frame_consumer) {
-  DCHECK(compositor_frame_consumers_.count(compositor_frame_consumer));
-  compositor_frame_consumers_.erase(compositor_frame_consumer);
-  if (current_compositor_frame_consumer_ == compositor_frame_consumer) {
+    CompositorFrameConsumer* consumer) {
+  ReturnUncommittedFrames(consumer->PassUncommittedFrameOnUI());
+  if (current_compositor_frame_consumer_ == consumer)
     SetCurrentCompositorFrameConsumer(nullptr);
-  }
-
-  // At this point the compositor frame consumer has to hand back all resources
-  // to the child compositor.
-  compositor_frame_consumer->DeleteHardwareRendererOnUI();
-  ReturnUncommittedFrames(
-      compositor_frame_consumer->PassUncommittedFrameOnUI());
-  ReturnResourceFromParent(compositor_frame_consumer);
-  compositor_frame_consumer->SetCompositorFrameProducer(nullptr);
 }
 
 void BrowserViewRenderer::ReturnUncommittedFrames(
@@ -313,23 +297,14 @@
                                 std::move(resources));
 }
 
-void BrowserViewRenderer::ReturnResourceFromParent(
-    CompositorFrameConsumer* compositor_frame_consumer) {
-  CompositorFrameConsumer::ReturnedResourcesMap returned_resource_map;
-  compositor_frame_consumer->SwapReturnedResourcesOnUI(&returned_resource_map);
-  for (auto& pair : returned_resource_map) {
-    CompositorID compositor_id = pair.first;
-    content::SynchronousCompositor* compositor = FindCompositor(compositor_id);
-    std::vector<viz::ReturnedResource> resources;
-    resources.swap(pair.second.resources);
-
-    if (compositor && !resources.empty()) {
-      compositor->ReturnResources(pair.second.layer_tree_frame_sink_id,
-                                  resources);
-    }
-
-    has_rendered_frame_ = true;
-  }
+void BrowserViewRenderer::ReturnUsedResources(
+    const std::vector<viz::ReturnedResource>& resources,
+    const CompositorID& compositor_id,
+    uint32_t layer_tree_frame_sink_id) {
+  content::SynchronousCompositor* compositor = FindCompositor(compositor_id);
+  if (compositor && !resources.empty())
+    compositor->ReturnResources(layer_tree_frame_sink_id, resources);
+  has_rendered_frame_ = true;
 }
 
 bool BrowserViewRenderer::OnDrawSoftware(SkCanvas* canvas) {
@@ -468,11 +443,9 @@
 }
 
 void BrowserViewRenderer::ReleaseHardware() {
-  for (auto* compositor_frame_consumer : compositor_frame_consumers_) {
+  if (current_compositor_frame_consumer_) {
     ReturnUncommittedFrames(
-        compositor_frame_consumer->PassUncommittedFrameOnUI());
-    ReturnResourceFromParent(compositor_frame_consumer);
-    DCHECK(compositor_frame_consumer->ReturnedResourcesEmptyOnUI());
+        current_compositor_frame_consumer_->PassUncommittedFrameOnUI());
   }
   hardware_enabled_ = false;
   has_rendered_frame_ = false;
diff --git a/android_webview/browser/browser_view_renderer.h b/android_webview/browser/browser_view_renderer.h
index ebd1893..a825f965 100644
--- a/android_webview/browser/browser_view_renderer.h
+++ b/android_webview/browser/browser_view_renderer.h
@@ -141,11 +141,13 @@
   ui::TouchHandleDrawable* CreateDrawable() override;
 
   // CompositorFrameProducer overrides
-  void ReturnedResourceAvailable(
-      CompositorFrameConsumer* compositor_frame_consumer) override;
-  void OnParentDrawConstraintsUpdated(
-      CompositorFrameConsumer* compositor_frame_consumer) override;
+  base::WeakPtr<CompositorFrameProducer> GetWeakPtr() override;
   void RemoveCompositorFrameConsumer(
+      CompositorFrameConsumer* consumer) override;
+  void ReturnUsedResources(const std::vector<viz::ReturnedResource>& resources,
+                           const CompositorID& compositor_id,
+                           uint32_t layer_tree_frame_sink_id) override;
+  void OnParentDrawConstraintsUpdated(
       CompositorFrameConsumer* compositor_frame_consumer) override;
 
   void SetActiveCompositorID(const CompositorID& compositor_id);
@@ -186,7 +188,6 @@
   BrowserViewRendererClient* const client_;
   const scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
   CompositorFrameConsumer* current_compositor_frame_consumer_;
-  std::set<CompositorFrameConsumer*> compositor_frame_consumers_;
 
   // The current compositor that's owned by the current RVH.
   content::SynchronousCompositor* compositor_;
@@ -243,6 +244,8 @@
 
   ParentCompositorDrawConstraints external_draw_constraints_;
 
+  base::WeakPtrFactory<CompositorFrameProducer> weak_ptr_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(BrowserViewRenderer);
 };
 
diff --git a/android_webview/browser/browser_view_renderer_unittest.cc b/android_webview/browser/browser_view_renderer_unittest.cc
index f44b0ea..bfdecf7 100644
--- a/android_webview/browser/browser_view_renderer_unittest.cc
+++ b/android_webview/browser/browser_view_renderer_unittest.cc
@@ -185,7 +185,7 @@
     on_draw_count_++;
   }
 
-  bool WillDrawOnRT(AwDrawGLInfo* draw_info) override {
+  bool WillDrawOnRT(HardwareRendererDrawParams* params) override {
     if (draw_gl_count_on_rt_ == 1) {
       draw_gl_count_on_rt_++;
       ui_task_runner_->PostTask(FROM_HERE,
@@ -194,15 +194,15 @@
       return false;
     }
 
-    draw_info->width = window_->surface_size().width();
-    draw_info->height = window_->surface_size().height();
-    draw_info->is_layer = false;
+    params->width = window_->surface_size().width();
+    params->height = window_->surface_size().height();
+    params->is_layer = false;
 
     gfx::Transform transform;
     if (draw_gl_count_on_rt_ == 0)
       transform = new_constraints_.transform;
 
-    transform.matrix().asColMajorf(draw_info->transform);
+    transform.matrix().asColMajorf(params->transform);
     return true;
   }
 
@@ -268,15 +268,15 @@
     on_draw_count_++;
   }
 
-  bool WillDrawOnRT(AwDrawGLInfo* draw_info) override {
+  bool WillDrawOnRT(HardwareRendererDrawParams* params) override {
     will_draw_on_rt_count_++;
     // What happens in practice is draw functor is skipped initially since
     // it is offscreen so entirely clipped. Then later, the webview is moved
     // onscreen without another OnDrawon UI thread, and draw functor is called
     // with non-empty clip. Here in the test we pretend this second draw happens
     // immediately.
-    bool result = RenderingTest::WillDrawOnRT(draw_info);
-    assertNonEmptyClip(draw_info);
+    bool result = RenderingTest::WillDrawOnRT(params);
+    assertNonEmptyClip(params);
     return result;
   }
 
@@ -302,10 +302,10 @@
   }
 
  private:
-  void assertNonEmptyClip(AwDrawGLInfo* draw_info) {
-    gfx::Rect clip(draw_info->clip_left, draw_info->clip_top,
-                   draw_info->clip_right - draw_info->clip_left,
-                   draw_info->clip_bottom - draw_info->clip_top);
+  void assertNonEmptyClip(HardwareRendererDrawParams* params) {
+    gfx::Rect clip(params->clip_left, params->clip_top,
+                   params->clip_right - params->clip_left,
+                   params->clip_bottom - params->clip_top);
     ASSERT_FALSE(clip.IsEmpty());
   }
 
@@ -500,10 +500,15 @@
   }
 
   void CheckResults() override {
-    GetCompositorFrameConsumer()->DeleteHardwareRendererOnUI();
     window_->Detach();
-    window_.reset();
+    functor_->OnWindowDetached();
+    ui_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&SwitchLayerTreeFrameSinkIdTest::PostedCheckResults,
+                       base::Unretained(this)));
+  }
 
+  void PostedCheckResults() {
     // Make sure resources for the last output surface are returned.
     EXPECT_EQ(expected_return_count_,
               GetReturnedResourceCounts()[last_layer_tree_frame_sink_id_]);
@@ -537,8 +542,14 @@
   }
 
   void CheckResults() override {
-    LayerTreeFrameSinkResourceCountMap resource_counts;
     functor_.reset();
+    ui_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&RenderThreadManagerDeletionTest::PostedCheckResults,
+                       base::Unretained(this)));
+  }
+
+  void PostedCheckResults() {
     // Make sure resources for the last frame are returned.
     EXPECT_EQ(expected_return_count_, GetReturnedResourceCounts());
     EndTest();
@@ -561,9 +572,8 @@
       case 1: {
         // Switch to new RTM.
         std::unique_ptr<FakeFunctor> functor(new FakeFunctor);
-        functor->Init(window_.get(),
-                      std::make_unique<RenderThreadManager>(
-                          functor.get(), base::ThreadTaskRunnerHandle::Get()));
+        functor->Init(window_.get(), std::make_unique<RenderThreadManager>(
+                                         base::ThreadTaskRunnerHandle::Get()));
         browser_view_renderer_->SetCurrentCompositorFrameConsumer(
             functor->GetCompositorFrameConsumer());
         saved_functor_ = std::move(functor_);
@@ -599,8 +609,14 @@
   }
 
   void CheckResults() override {
-    LayerTreeFrameSinkResourceCountMap resource_counts;
     functor_.reset();
+    ui_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&RenderThreadManagerSwitchTest::PostedCheckResults,
+                       base::Unretained(this)));
+  }
+
+  void PostedCheckResults() {
     // Make sure resources for all frames are returned.
     EXPECT_EQ(expected_return_count_, GetReturnedResourceCounts());
     EndTest();
diff --git a/android_webview/browser/compositor_frame_consumer.h b/android_webview/browser/compositor_frame_consumer.h
index 2a65329a..9022083 100644
--- a/android_webview/browser/compositor_frame_consumer.h
+++ b/android_webview/browser/compositor_frame_consumer.h
@@ -5,11 +5,8 @@
 #ifndef ANDROID_WEBVIEW_BROWSER_COMPOSITOR_FRAME_CONSUMER_H_
 #define ANDROID_WEBVIEW_BROWSER_COMPOSITOR_FRAME_CONSUMER_H_
 
-#include <map>
-
 #include "android_webview/browser/child_frame.h"
 #include "android_webview/browser/parent_compositor_draw_constraints.h"
-#include "components/viz/common/resources/returned_resource.h"
 #include "ui/gfx/geometry/vector2d.h"
 
 namespace android_webview {
@@ -19,16 +16,6 @@
 
 class CompositorFrameConsumer {
  public:
-  struct ReturnedResources {
-    ReturnedResources();
-    ~ReturnedResources();
-
-    uint32_t layer_tree_frame_sink_id;
-    std::vector<viz::ReturnedResource> resources;
-  };
-  using ReturnedResourcesMap =
-      std::map<CompositorID, ReturnedResources, CompositorIDComparator>;
-
   // A CompositorFrameConsumer may be registered with at most one
   // CompositorFrameProducer.
   // The producer is responsible for managing the relationship with its
@@ -44,11 +31,7 @@
       std::unique_ptr<ChildFrame> frame) = 0;
   virtual ParentCompositorDrawConstraints GetParentDrawConstraintsOnUI()
       const = 0;
-  virtual void SwapReturnedResourcesOnUI(
-      ReturnedResourcesMap* returned_resource_map) = 0;
-  virtual bool ReturnedResourcesEmptyOnUI() const = 0;
   virtual ChildFrameQueue PassUncommittedFrameOnUI() = 0;
-  virtual void DeleteHardwareRendererOnUI() = 0;
 
  protected:
   virtual ~CompositorFrameConsumer() {}
diff --git a/android_webview/browser/compositor_frame_producer.h b/android_webview/browser/compositor_frame_producer.h
index 1e33b91..c0c6f52 100644
--- a/android_webview/browser/compositor_frame_producer.h
+++ b/android_webview/browser/compositor_frame_producer.h
@@ -5,18 +5,27 @@
 #ifndef ANDROID_WEBVIEW_BROWSER_COMPOSITOR_FRAME_PRODUCER_H_
 #define ANDROID_WEBVIEW_BROWSER_COMPOSITOR_FRAME_PRODUCER_H_
 
+#include <vector>
+
+#include "android_webview/browser/compositor_id.h"
+#include "base/memory/weak_ptr.h"
+#include "components/viz/common/resources/returned_resource.h"
+
 namespace android_webview {
 
 class CompositorFrameConsumer;
 
 class CompositorFrameProducer {
  public:
-  virtual void ReturnedResourceAvailable(
-      CompositorFrameConsumer* compositor_frame_consumer) = 0;
+  virtual base::WeakPtr<CompositorFrameProducer> GetWeakPtr();
+  virtual void ReturnUsedResources(
+      const std::vector<viz::ReturnedResource>& resources,
+      const CompositorID& compositor_id,
+      uint32_t layer_tree_frame_sink_id) = 0;
   virtual void OnParentDrawConstraintsUpdated(
       CompositorFrameConsumer* compositor_frame_consumer) = 0;
   virtual void RemoveCompositorFrameConsumer(
-      CompositorFrameConsumer* compositor_frame_consumer) = 0;
+      CompositorFrameConsumer* consumer) = 0;
 
  protected:
   virtual ~CompositorFrameProducer() {}
diff --git a/android_webview/browser/hardware_renderer.cc b/android_webview/browser/hardware_renderer.cc
index 410c03d0..10b51a1 100644
--- a/android_webview/browser/hardware_renderer.cc
+++ b/android_webview/browser/hardware_renderer.cc
@@ -12,7 +12,6 @@
 #include "android_webview/browser/parent_compositor_draw_constraints.h"
 #include "android_webview/browser/render_thread_manager.h"
 #include "android_webview/browser/surfaces_instance.h"
-#include "android_webview/public/browser/draw_gl.h"
 #include "base/trace_event/trace_event.h"
 #include "components/viz/common/quads/compositor_frame.h"
 #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
@@ -77,7 +76,7 @@
   child_frame_queue_.emplace_back(std::move(child_frames.front()));
 }
 
-void HardwareRenderer::DrawGL(AwDrawGLInfo* draw_info) {
+void HardwareRenderer::DrawGL(HardwareRendererDrawParams* params) {
   TRACE_EVENT0("android_webview", "HardwareRenderer::DrawGL");
 
   for (auto& pruned_frame : WaitAndPruneFrameQueue(&child_frame_queue_))
@@ -137,15 +136,15 @@
   }
 
   gfx::Transform transform(gfx::Transform::kSkipInitialization);
-  transform.matrix().setColMajorf(draw_info->transform);
+  transform.matrix().setColMajorf(params->transform);
   transform.Translate(scroll_offset_.x(), scroll_offset_.y());
 
-  gfx::Size viewport(draw_info->width, draw_info->height);
+  gfx::Size viewport(params->width, params->height);
   // Need to post the new transform matrix back to child compositor
   // because there is no onDraw during a Render Thread animation, and child
   // compositor might not have the tiles rasterized as the animation goes on.
-  ParentCompositorDrawConstraints draw_constraints(
-      draw_info->is_layer, transform, viewport.IsEmpty());
+  ParentCompositorDrawConstraints draw_constraints(params->is_layer, transform,
+                                                   viewport.IsEmpty());
   if (!child_frame_.get() || draw_constraints.NeedUpdate(*child_frame_)) {
     render_thread_manager_->PostExternalDrawConstraintsToChildCompositorOnRT(
         draw_constraints);
@@ -154,9 +153,9 @@
   if (!child_id_.is_valid())
     return;
 
-  gfx::Rect clip(draw_info->clip_left, draw_info->clip_top,
-                 draw_info->clip_right - draw_info->clip_left,
-                 draw_info->clip_bottom - draw_info->clip_top);
+  gfx::Rect clip(params->clip_left, params->clip_top,
+                 params->clip_right - params->clip_left,
+                 params->clip_bottom - params->clip_top);
   surfaces_->DrawAndSwap(viewport, clip, transform, surface_size_,
                          viz::SurfaceId(frame_sink_id_, child_id_),
                          device_scale_factor_);
diff --git a/android_webview/browser/hardware_renderer.h b/android_webview/browser/hardware_renderer.h
index 42710458..1fdcd6b 100644
--- a/android_webview/browser/hardware_renderer.h
+++ b/android_webview/browser/hardware_renderer.h
@@ -14,8 +14,6 @@
 #include "components/viz/common/surfaces/frame_sink_id.h"
 #include "services/viz/public/interfaces/compositing/compositor_frame_sink.mojom.h"
 
-struct AwDrawGLInfo;
-
 namespace viz {
 class CompositorFrameSinkSupport;
 class ParentLocalSurfaceIdAllocator;
@@ -27,6 +25,17 @@
 class RenderThreadManager;
 class SurfacesInstance;
 
+struct HardwareRendererDrawParams {
+  int clip_left;
+  int clip_top;
+  int clip_right;
+  int clip_bottom;
+  int width;
+  int height;
+  bool is_layer;
+  float transform[16];
+};
+
 class HardwareRenderer : public viz::mojom::CompositorFrameSinkClient {
  public:
   // Two rules:
@@ -44,7 +53,7 @@
   explicit HardwareRenderer(RenderThreadManager* state);
   ~HardwareRenderer() override;
 
-  void DrawGL(AwDrawGLInfo* draw_info);
+  void DrawGL(HardwareRendererDrawParams* params);
   void CommitFrame();
 
  private:
diff --git a/android_webview/browser/render_thread_manager.cc b/android_webview/browser/render_thread_manager.cc
index 51e070f5..3eb05b9 100644
--- a/android_webview/browser/render_thread_manager.cc
+++ b/android_webview/browser/render_thread_manager.cc
@@ -9,8 +9,6 @@
 #include "android_webview/browser/compositor_frame_producer.h"
 #include "android_webview/browser/compositor_id.h"
 #include "android_webview/browser/deferred_gpu_command_service.h"
-#include "android_webview/browser/hardware_renderer.h"
-#include "android_webview/browser/render_thread_manager_client.h"
 #include "android_webview/browser/scoped_app_gl_state_restore.h"
 #include "android_webview/public/browser/draw_gl.h"
 #include "base/bind.h"
@@ -24,37 +22,25 @@
 
 namespace android_webview {
 
-namespace {
-constexpr base::TimeDelta kSlightlyMoreThanOneFrame =
-    base::TimeDelta::FromMilliseconds(17);
-}
-
 RenderThreadManager::RenderThreadManager(
-    RenderThreadManagerClient* client,
     const scoped_refptr<base::SingleThreadTaskRunner>& ui_loop)
     : ui_loop_(ui_loop),
-      client_(client),
-      compositor_frame_producer_(nullptr),
-      has_received_frame_(false),
-      inside_hardware_release_(false),
+      mark_hardware_release_(false),
       weak_factory_on_ui_thread_(this) {
   DCHECK(ui_loop_->BelongsToCurrentThread());
-  DCHECK(client_);
   ui_thread_weak_ptr_ = weak_factory_on_ui_thread_.GetWeakPtr();
 }
 
 RenderThreadManager::~RenderThreadManager() {
   DCHECK(ui_loop_->BelongsToCurrentThread());
-  if (compositor_frame_producer_) {
-    compositor_frame_producer_->RemoveCompositorFrameConsumer(this);
-  }
   DCHECK(!hardware_renderer_.get());
+  DCHECK(child_frames_.empty());
 }
 
 void RenderThreadManager::UpdateParentDrawConstraintsOnUI() {
   DCHECK(ui_loop_->BelongsToCurrentThread());
-  if (compositor_frame_producer_) {
-    compositor_frame_producer_->OnParentDrawConstraintsUpdated(this);
+  if (producer_weak_ptr_) {
+    producer_weak_ptr_->OnParentDrawConstraintsUpdated(this);
   }
 }
 
@@ -71,7 +57,6 @@
 std::unique_ptr<ChildFrame> RenderThreadManager::SetFrameOnUI(
     std::unique_ptr<ChildFrame> new_frame) {
   DCHECK(new_frame);
-  has_received_frame_ = true;
 
   base::AutoLock lock(lock_);
   if (child_frames_.empty()) {
@@ -127,143 +112,66 @@
 
 void RenderThreadManager::SetInsideHardwareRelease(bool inside) {
   base::AutoLock lock(lock_);
-  inside_hardware_release_ = inside;
+  mark_hardware_release_ = inside;
 }
 
 bool RenderThreadManager::IsInsideHardwareRelease() const {
   base::AutoLock lock(lock_);
-  return inside_hardware_release_;
+  return mark_hardware_release_;
 }
 
-RenderThreadManager::ReturnedResources::ReturnedResources()
-    : layer_tree_frame_sink_id(0u) {}
-
-RenderThreadManager::ReturnedResources::~ReturnedResources() {}
-
 void RenderThreadManager::InsertReturnedResourcesOnRT(
     const std::vector<viz::ReturnedResource>& resources,
     const CompositorID& compositor_id,
     uint32_t layer_tree_frame_sink_id) {
   if (resources.empty())
     return;
-
-  base::AutoLock lock(lock_);
-  ReturnedResources& returned_resources =
-      returned_resources_map_[compositor_id];
-  if (returned_resources.layer_tree_frame_sink_id != layer_tree_frame_sink_id) {
-    returned_resources.resources.clear();
-  }
-  returned_resources.resources.insert(returned_resources.resources.end(),
-                                      resources.begin(), resources.end());
-  returned_resources.layer_tree_frame_sink_id = layer_tree_frame_sink_id;
-
-  if (!returned_resource_available_pending_) {
-    returned_resource_available_pending_ = true;
-    ui_loop_->PostDelayedTask(
-        FROM_HERE,
-        base::BindOnce(&RenderThreadManager::ReturnedResourceAvailableOnUI,
-                       ui_thread_weak_ptr_),
-        kSlightlyMoreThanOneFrame * 2);
-  }
+  ui_loop_->PostTask(
+      FROM_HERE, base::BindOnce(&CompositorFrameProducer::ReturnUsedResources,
+                                producer_weak_ptr_, resources, compositor_id,
+                                layer_tree_frame_sink_id));
 }
 
-void RenderThreadManager::ReturnedResourceAvailableOnUI() {
-  bool empty = false;
-  {
-    base::AutoLock lock(lock_);
-    DCHECK(returned_resource_available_pending_);
-    returned_resource_available_pending_ = false;
-    empty = returned_resources_map_.empty();
-  }
-  if (!empty && compositor_frame_producer_) {
-    compositor_frame_producer_->ReturnedResourceAvailable(this);
-  }
+void RenderThreadManager::CommitFrameOnRT() {
+  if (hardware_renderer_)
+    hardware_renderer_->CommitFrame();
 }
 
-void RenderThreadManager::SwapReturnedResourcesOnUI(
-    ReturnedResourcesMap* returned_resource_map) {
-  DCHECK(returned_resource_map->empty());
-  base::AutoLock lock(lock_);
-  returned_resource_map->swap(returned_resources_map_);
-}
-
-bool RenderThreadManager::ReturnedResourcesEmptyOnUI() const {
-  base::AutoLock lock(lock_);
-  return returned_resources_map_.empty();
-}
-
-void RenderThreadManager::DrawGL(AwDrawGLInfo* draw_info) {
-  TRACE_EVENT0("android_webview,toplevel", "DrawFunctor");
-  if (draw_info->mode == AwDrawGLInfo::kModeSync) {
-    TRACE_EVENT_INSTANT0("android_webview", "kModeSync",
-                         TRACE_EVENT_SCOPE_THREAD);
-    if (hardware_renderer_)
-      hardware_renderer_->CommitFrame();
-    return;
-  }
-
+void RenderThreadManager::DrawOnRT(bool save_restore,
+                                   HardwareRendererDrawParams* params) {
   // Force GL binding init if it's not yet initialized.
   DeferredGpuCommandService::GetInstance();
-
-  ScopedAppGLStateRestore state_restore(
-      draw_info->mode == AwDrawGLInfo::kModeDraw
-          ? ScopedAppGLStateRestore::MODE_DRAW
-          : ScopedAppGLStateRestore::MODE_RESOURCE_MANAGEMENT,
-      draw_info->version < 3 /* save_restore */);
+  ScopedAppGLStateRestore state_restore(ScopedAppGLStateRestore::MODE_DRAW,
+                                        save_restore);
   ScopedAllowGL allow_gl;
-  if (!hardware_renderer_ && draw_info->mode == AwDrawGLInfo::kModeDraw &&
-      !IsInsideHardwareRelease() && HasFrameForHardwareRendererOnRT()) {
+  if (!hardware_renderer_ && !IsInsideHardwareRelease() &&
+      HasFrameForHardwareRendererOnRT()) {
     hardware_renderer_.reset(new HardwareRenderer(this));
     hardware_renderer_->CommitFrame();
   }
 
-  if (draw_info->mode == AwDrawGLInfo::kModeProcessNoContext) {
-    LOG(ERROR) << "Received unexpected kModeProcessNoContext";
-  }
-
-  if (IsInsideHardwareRelease()) {
-    hardware_renderer_.reset();
-    return;
-  }
-
-  if (draw_info->mode != AwDrawGLInfo::kModeDraw)
-    return;
   if (hardware_renderer_)
-    hardware_renderer_->DrawGL(draw_info);
+    hardware_renderer_->DrawGL(params);
 }
 
-void RenderThreadManager::DeleteHardwareRendererOnUI() {
+void RenderThreadManager::DestroyHardwareRendererOnRT(bool save_restore) {
+  DeferredGpuCommandService::GetInstance();
+  ScopedAppGLStateRestore state_restore(
+      ScopedAppGLStateRestore::MODE_RESOURCE_MANAGEMENT, save_restore);
+  ScopedAllowGL allow_gl;
+  hardware_renderer_.reset();
+}
+
+void RenderThreadManager::RemoveFromCompositorFrameProducerOnUI() {
   DCHECK(ui_loop_->BelongsToCurrentThread());
-
-  InsideHardwareReleaseReset auto_inside_hardware_release_reset(this);
-
-  client_->DetachFunctorFromView();
-
-  // If the WebView gets onTrimMemory >= MODERATE twice in a row, the 2nd
-  // onTrimMemory will result in an unnecessary Render Thread InvokeGL call.
-  if (has_received_frame_) {
-    // Receiving at least one frame is a precondition for
-    // initialization (such as looing up GL bindings and constructing
-    // hardware_renderer_).
-    bool draw_functor_succeeded = client_->RequestInvokeGL(true);
-    if (!draw_functor_succeeded) {
-      LOG(ERROR) << "Unable to free GL resources. Has the Window leaked?";
-      // Calling release on wrong thread intentionally.
-      AwDrawGLInfo info;
-      info.mode = AwDrawGLInfo::kModeProcess;
-      DrawGL(&info);
-    }
-  }
-
-  has_received_frame_ = false;
+  if (producer_weak_ptr_)
+    producer_weak_ptr_->RemoveCompositorFrameConsumer(this);
 }
 
 void RenderThreadManager::SetCompositorFrameProducer(
     CompositorFrameProducer* compositor_frame_producer) {
-  DCHECK(compositor_frame_producer == compositor_frame_producer_ ||
-         compositor_frame_producer_ == nullptr ||
-         compositor_frame_producer == nullptr);
-  compositor_frame_producer_ = compositor_frame_producer;
+  DCHECK(ui_loop_->BelongsToCurrentThread());
+  producer_weak_ptr_ = compositor_frame_producer->GetWeakPtr();
 }
 
 bool RenderThreadManager::HasFrameForHardwareRendererOnRT() const {
diff --git a/android_webview/browser/render_thread_manager.h b/android_webview/browser/render_thread_manager.h
index 16d145b..2e79d1a8 100644
--- a/android_webview/browser/render_thread_manager.h
+++ b/android_webview/browser/render_thread_manager.h
@@ -8,6 +8,7 @@
 #include <map>
 
 #include "android_webview/browser/compositor_frame_consumer.h"
+#include "android_webview/browser/hardware_renderer.h"
 #include "android_webview/browser/parent_compositor_draw_constraints.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
@@ -15,21 +16,17 @@
 #include "base/synchronization/lock.h"
 #include "ui/gfx/geometry/vector2d.h"
 
-struct AwDrawGLInfo;
 namespace android_webview {
 
-class RenderThreadManagerClient;
 class ChildFrame;
 class CompositorFrameProducer;
 class HardwareRenderer;
-class InsideHardwareReleaseReset;
 struct CompositorID;
 
 // This class is used to pass data between UI thread and RenderThread.
 class RenderThreadManager : public CompositorFrameConsumer {
  public:
-  RenderThreadManager(
-      RenderThreadManagerClient* client,
+  explicit RenderThreadManager(
       const scoped_refptr<base::SingleThreadTaskRunner>& ui_loop);
   ~RenderThreadManager() override;
 
@@ -40,16 +37,13 @@
   std::unique_ptr<ChildFrame> SetFrameOnUI(
       std::unique_ptr<ChildFrame> frame) override;
   ParentCompositorDrawConstraints GetParentDrawConstraintsOnUI() const override;
-  void SwapReturnedResourcesOnUI(
-      ReturnedResourcesMap* returned_resource_map) override;
-  bool ReturnedResourcesEmptyOnUI() const override;
   ChildFrameQueue PassUncommittedFrameOnUI() override;
-  void DeleteHardwareRendererOnUI() override;
+
+  void RemoveFromCompositorFrameProducerOnUI();
 
   // Render thread methods.
   gfx::Vector2d GetScrollOffsetOnRT();
   ChildFrameQueue PassFramesOnRT();
-  void DrawGL(AwDrawGLInfo* draw_info);
   void PostExternalDrawConstraintsToChildCompositorOnRT(
       const ParentCompositorDrawConstraints& parent_draw_constraints);
   void InsertReturnedResourcesOnRT(
@@ -57,7 +51,10 @@
       const CompositorID& compositor_id,
       uint32_t layer_tree_frame_sink_id);
 
- private:
+  void CommitFrameOnRT();
+  void DrawOnRT(bool save_restore, HardwareRendererDrawParams* params);
+  void DestroyHardwareRendererOnRT(bool save_restore);
+
   class InsideHardwareReleaseReset {
    public:
     explicit InsideHardwareReleaseReset(
@@ -67,6 +64,8 @@
    private:
     RenderThreadManager* render_thread_manager_;
   };
+
+ private:
   static std::unique_ptr<ChildFrame> GetSynchronousCompositorFrame(
       scoped_refptr<content::SynchronousCompositor::FrameFuture> frame_future,
       std::unique_ptr<ChildFrame> child_frame);
@@ -76,18 +75,13 @@
 
   // UI thread methods.
   void UpdateParentDrawConstraintsOnUI();
-  void ReturnedResourceAvailableOnUI();
   bool IsInsideHardwareRelease() const;
   void SetInsideHardwareRelease(bool inside);
 
   // Accessed by UI thread.
   scoped_refptr<base::SingleThreadTaskRunner> ui_loop_;
-  RenderThreadManagerClient* const client_;
-  CompositorFrameProducer* compositor_frame_producer_;
+  base::WeakPtr<CompositorFrameProducer> producer_weak_ptr_;
   base::WeakPtr<RenderThreadManager> ui_thread_weak_ptr_;
-  // Whether any frame has been received on the UI thread by
-  // RenderThreadManager.
-  bool has_received_frame_;
 
   // Accessed by RT thread.
   std::unique_ptr<HardwareRenderer> hardware_renderer_;
@@ -96,10 +90,8 @@
   mutable base::Lock lock_;
   gfx::Vector2d scroll_offset_;
   ChildFrameQueue child_frames_;
-  bool inside_hardware_release_;
+  bool mark_hardware_release_;
   ParentCompositorDrawConstraints parent_draw_constraints_;
-  bool returned_resource_available_pending_ = false;
-  ReturnedResourcesMap returned_resources_map_;
 
   base::WeakPtrFactory<RenderThreadManager> weak_factory_on_ui_thread_;
 
diff --git a/android_webview/browser/render_thread_manager_client.h b/android_webview/browser/render_thread_manager_client.h
deleted file mode 100644
index 1d05fd1..0000000
--- a/android_webview/browser/render_thread_manager_client.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ANDROID_WEBVIEW_BROWSER_RENDER_THREAD_MANAGER_CLIENT_H_
-#define ANDROID_WEBVIEW_BROWSER_RENDER_THREAD_MANAGER_CLIENT_H_
-
-namespace android_webview {
-
-class RenderThreadManagerClient {
- public:
-  // Request DrawGL to be in called AwDrawGLInfo::kModeProcess type.
-  // |wait_for_completion| will cause the call to block until DrawGL has
-  // happened. The callback may never be made, and the mode may be promoted to
-  // kModeDraw.
-  virtual bool RequestInvokeGL(bool wait_for_completion) = 0;
-
-  // Call postInvalidateOnAnimation for invalidations. This is only used to
-  // synchronize draw functor destruction.
-  virtual void DetachFunctorFromView() = 0;
-
- protected:
-  virtual ~RenderThreadManagerClient() {}
-};
-
-}  // namespace android_webview
-
-#endif  // ANDROID_WEBVIEW_BROWSER_RENDER_THREAD_MANAGER_CLIENT_H_
diff --git a/android_webview/browser/test/fake_window.cc b/android_webview/browser/test/fake_window.cc
index 943bbb230..0ec1dd3 100644
--- a/android_webview/browser/test/fake_window.cc
+++ b/android_webview/browser/test/fake_window.cc
@@ -75,11 +75,13 @@
 
 void FakeWindow::Detach() {
   CheckCurrentlyOnUIThread();
+  view_->SetCurrentCompositorFrameConsumer(nullptr);
   view_->OnDetachedFromWindow();
 }
 
 void FakeWindow::RequestInvokeGL(FakeFunctor* functor,
                                  bool wait_for_completion) {
+  CreateRenderThreadIfNeeded();
   CheckCurrentlyOnUIThread();
   base::WaitableEvent completion(
       base::WaitableEvent::ResetPolicy::MANUAL,
@@ -213,6 +215,12 @@
 FakeFunctor::FakeFunctor() : window_(nullptr) {}
 
 FakeFunctor::~FakeFunctor() {
+  if (render_thread_manager_) {
+    render_thread_manager_->RemoveFromCompositorFrameProducerOnUI();
+    RenderThreadManager::InsideHardwareReleaseReset release_reset(
+        render_thread_manager_.get());
+    RequestInvokeGL(true);
+  }
   render_thread_manager_.reset();
 }
 
@@ -221,36 +229,29 @@
     std::unique_ptr<RenderThreadManager> render_thread_manager) {
   window_ = window;
   render_thread_manager_ = std::move(render_thread_manager);
-  callback_ =
-      base::BindRepeating(&RenderThreadManager::DrawGL,
-                          base::Unretained(render_thread_manager_.get()));
 }
 
 void FakeFunctor::Sync(const gfx::Rect& location,
                        WindowHooks* hooks) {
-  DCHECK(!callback_.is_null());
+  DCHECK(render_thread_manager_);
   committed_location_ = location;
-  AwDrawGLInfo sync_info;
-  sync_info.version = kAwDrawGLInfoVersion;
-  sync_info.mode = AwDrawGLInfo::kModeSync;
   hooks->WillSyncOnRT();
-  callback_.Run(&sync_info);
+  render_thread_manager_->CommitFrameOnRT();
   hooks->DidSyncOnRT();
 }
 
 void FakeFunctor::Draw(WindowHooks* hooks) {
-  DCHECK(!callback_.is_null());
-  AwDrawGLInfo draw_info;
-  draw_info.version = kAwDrawGLInfoVersion;
-  draw_info.mode = AwDrawGLInfo::kModeDraw;
-  draw_info.clip_left = committed_location_.x();
-  draw_info.clip_top = committed_location_.y();
-  draw_info.clip_right = committed_location_.x() + committed_location_.width();
-  draw_info.clip_bottom =
-      committed_location_.y() + committed_location_.height();
-  if (!hooks->WillDrawOnRT(&draw_info))
+  DCHECK(render_thread_manager_);
+  HardwareRendererDrawParams params{};
+  params.clip_left = committed_location_.x();
+  params.clip_top = committed_location_.y();
+  params.clip_right = committed_location_.x() + committed_location_.width();
+  params.clip_bottom = committed_location_.y() + committed_location_.height();
+  params.width = committed_location_.width();
+  params.height = committed_location_.height();
+  if (!hooks->WillDrawOnRT(&params))
     return;
-  callback_.Run(&draw_info);
+  render_thread_manager_->DrawOnRT(false /* save_restore */, &params);
   hooks->DidDrawOnRT();
 }
 
@@ -258,13 +259,19 @@
   return render_thread_manager_.get();
 }
 
+void FakeFunctor::OnWindowDetached() {
+  if (!render_thread_manager_)
+    return;
+  render_thread_manager_->RemoveFromCompositorFrameProducerOnUI();
+  RenderThreadManager::InsideHardwareReleaseReset release_reset(
+      render_thread_manager_.get());
+  RequestInvokeGL(true);
+}
+
 void FakeFunctor::Invoke(WindowHooks* hooks) {
-  DCHECK(!callback_.is_null());
-  AwDrawGLInfo invoke_info;
-  invoke_info.version = kAwDrawGLInfoVersion;
-  invoke_info.mode = AwDrawGLInfo::kModeProcess;
+  DCHECK(render_thread_manager_);
   hooks->WillProcessOnRT();
-  callback_.Run(&invoke_info);
+  render_thread_manager_->DestroyHardwareRendererOnRT(false /* save_restore */);
   hooks->DidProcessOnRT();
 }
 
@@ -274,6 +281,4 @@
   return true;
 }
 
-void FakeFunctor::DetachFunctorFromView() {}
-
 }  // namespace android_webview
diff --git a/android_webview/browser/test/fake_window.h b/android_webview/browser/test/fake_window.h
index 3c1576a..66852b36 100644
--- a/android_webview/browser/test/fake_window.h
+++ b/android_webview/browser/test/fake_window.h
@@ -7,8 +7,7 @@
 
 #include <map>
 
-#include "android_webview/browser/render_thread_manager_client.h"
-#include "android_webview/public/browser/draw_gl.h"
+#include "android_webview/browser/hardware_renderer.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
@@ -41,7 +40,7 @@
   virtual void DidSyncOnRT() = 0;
   virtual void WillProcessOnRT() = 0;
   virtual void DidProcessOnRT() = 0;
-  virtual bool WillDrawOnRT(AwDrawGLInfo* draw_info) = 0;
+  virtual bool WillDrawOnRT(HardwareRendererDrawParams* params) = 0;
   virtual void DidDrawOnRT() = 0;
 };
 
@@ -98,12 +97,10 @@
   DISALLOW_COPY_AND_ASSIGN(FakeWindow);
 };
 
-class FakeFunctor : public RenderThreadManagerClient {
+class FakeFunctor {
  public:
-  using DrawGLCallback = base::RepeatingCallback<void(AwDrawGLInfo*)>;
-
   FakeFunctor();
-  ~FakeFunctor() override;
+  ~FakeFunctor();
 
   void Init(FakeWindow* window,
             std::unique_ptr<RenderThreadManager> render_thread_manager);
@@ -112,14 +109,12 @@
   void Invoke(WindowHooks* hooks);
 
   CompositorFrameConsumer* GetCompositorFrameConsumer();
-
-  // RenderThreadManagerClient overrides
-  bool RequestInvokeGL(bool wait_for_completion) override;
-  void DetachFunctorFromView() override;
+  void OnWindowDetached();
 
  private:
+  bool RequestInvokeGL(bool wait_for_completion);
+
   FakeWindow* window_;
-  DrawGLCallback callback_;
   std::unique_ptr<RenderThreadManager> render_thread_manager_;
   gfx::Rect committed_location_;
 };
diff --git a/android_webview/browser/test/rendering_test.cc b/android_webview/browser/test/rendering_test.cc
index 23eeb78a..09ed0f8 100644
--- a/android_webview/browser/test/rendering_test.cc
+++ b/android_webview/browser/test/rendering_test.cc
@@ -70,9 +70,8 @@
   std::unique_ptr<FakeWindow> window(
       new FakeWindow(browser_view_renderer_.get(), this, gfx::Rect(100, 100)));
   functor_.reset(new FakeFunctor);
-  functor_->Init(window.get(),
-                 std::make_unique<RenderThreadManager>(
-                     functor_.get(), base::ThreadTaskRunnerHandle::Get()));
+  functor_->Init(window.get(), std::make_unique<RenderThreadManager>(
+                                   base::ThreadTaskRunnerHandle::Get()));
   browser_view_renderer_->SetCurrentCompositorFrameConsumer(
       functor_->GetCompositorFrameConsumer());
   window_ = std::move(window);
@@ -137,12 +136,12 @@
   compositor_->SetHardwareFrame(0u, ConstructEmptyFrame());
 }
 
-bool RenderingTest::WillDrawOnRT(AwDrawGLInfo* draw_info) {
-  draw_info->width = window_->surface_size().width();
-  draw_info->height = window_->surface_size().height();
-  draw_info->is_layer = false;
+bool RenderingTest::WillDrawOnRT(HardwareRendererDrawParams* params) {
+  params->width = window_->surface_size().width();
+  params->height = window_->surface_size().height();
+  params->is_layer = false;
   gfx::Transform transform;
-  transform.matrix().asColMajorf(draw_info->transform);
+  transform.matrix().asColMajorf(params->transform);
   return true;
 }
 
diff --git a/android_webview/browser/test/rendering_test.h b/android_webview/browser/test/rendering_test.h
index 9708059..5d9f728b 100644
--- a/android_webview/browser/test/rendering_test.h
+++ b/android_webview/browser/test/rendering_test.h
@@ -8,7 +8,6 @@
 #include <memory>
 
 #include "android_webview/browser/browser_view_renderer_client.h"
-#include "android_webview/browser/render_thread_manager_client.h"
 #include "android_webview/browser/test/fake_window.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
@@ -66,7 +65,7 @@
   void DidSyncOnRT() override {}
   void WillProcessOnRT() override {}
   void DidProcessOnRT() override {}
-  bool WillDrawOnRT(AwDrawGLInfo* draw_info) override;
+  bool WillDrawOnRT(HardwareRendererDrawParams* params) override;
   void DidDrawOnRT() override {}
 
   virtual void OnParentDrawConstraintsUpdated() {}
@@ -96,8 +95,6 @@
   std::unique_ptr<content::TestSynchronousCompositor> compositor_;
 
  private:
-  void DrawGL(AwDrawGLInfo* aw_draw_gl_info);
-
   const std::unique_ptr<base::MessageLoop> message_loop_;
   base::RunLoop run_loop_;
 
diff --git a/android_webview/java/src/org/chromium/android_webview/AwGLFunctor.java b/android_webview/java/src/org/chromium/android_webview/AwGLFunctor.java
index ec1bd80..7f71386 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwGLFunctor.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwGLFunctor.java
@@ -30,8 +30,7 @@
     public AwGLFunctor(
             AwContents.NativeDrawFunctorFactory nativeDrawFunctorFactory, ViewGroup containerView) {
         mNativeAwGLFunctor = nativeCreate(this);
-        mNativeDrawGLFunctor = nativeDrawFunctorFactory.createGLFunctor(
-                nativeGetAwDrawGLViewContext(mNativeAwGLFunctor));
+        mNativeDrawGLFunctor = nativeDrawFunctorFactory.createGLFunctor(mNativeAwGLFunctor);
         mContainerView = containerView;
         if (mNativeDrawGLFunctor.supportsDrawGLFunctorReleasedCallback()) {
             mFunctorReleasedCallback = () -> removeReference();
@@ -44,6 +43,7 @@
     @Override
     public void destroy() {
         assert mRefCount > 0;
+        nativeRemoveFromCompositorFrameProducer(mNativeAwGLFunctor);
         removeReference();
     }
 
@@ -110,7 +110,7 @@
     }
 
     private native void nativeDeleteHardwareRenderer(long nativeAwGLFunctor);
-    private native long nativeGetAwDrawGLViewContext(long nativeAwGLFunctor);
+    private native void nativeRemoveFromCompositorFrameProducer(long nativeAwGLFunctor);
     private native long nativeGetCompositorFrameConsumer(long nativeAwGLFunctor);
 
     private static native long nativeGetAwDrawGLFunction();
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 6de80608..ca342ff 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1359,6 +1359,9 @@
     "//chromeos:power_manager_proto",
     "//chromeos/assistant:buildflags",
     "//chromeos/components/multidevice/logging",
+
+    # TODO(stevenjb): Remove cryptohome dependency. htps://crbug.com/918922.
+    "//chromeos/cryptohome",
     "//chromeos/services/assistant/public:feature_flags",
     "//chromeos/services/assistant/public/mojom",
     "//chromeos/services/multidevice_setup/public/mojom",
@@ -2181,6 +2184,9 @@
     "//cc:test_support",
     "//chromeos",
     "//chromeos:test_support_without_gmock",
+
+    # TODO(stevenjb): Remove cryptohome dependency. htps://crbug.com/918922.
+    "//chromeos/cryptohome",
     "//components/account_id",
     "//components/prefs:test_support",
     "//components/user_manager:user_manager",
diff --git a/ash/ash_service.cc b/ash/ash_service.cc
index 4bb72ea..8d9d43c 100644
--- a/ash/ash_service.cc
+++ b/ash/ash_service.cc
@@ -16,7 +16,6 @@
 #include "base/threading/thread.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chromeos/audio/cras_audio_handler.h"
-#include "chromeos/cryptohome/system_salt_getter.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/power_policy_controller.h"
 #include "chromeos/network/network_connect.h"
@@ -81,7 +80,6 @@
 
   statistics_provider_.reset();
   // NOTE: PowerStatus is shutdown by Shell.
-  chromeos::SystemSaltGetter::Shutdown();
   chromeos::CrasAudioHandler::Shutdown();
   chromeos::NetworkConnect::Shutdown();
   network_connect_delegate_.reset();
@@ -146,7 +144,6 @@
   chromeos::NetworkConnect::Initialize(network_connect_delegate_.get());
   // TODO(jamescook): Initialize real audio handler.
   chromeos::CrasAudioHandler::InitializeForTesting();
-  chromeos::SystemSaltGetter::Initialize();
 
   // TODO(jamescook): Refactor StatisticsProvider so we can get just the data
   // we need in ash. Right now StatisticsProviderImpl launches the crossystem
diff --git a/ash/public/cpp/ash_features.cc b/ash/public/cpp/ash_features.cc
index 99b4277..905fefa7 100644
--- a/ash/public/cpp/ash_features.cc
+++ b/ash/public/cpp/ash_features.cc
@@ -19,9 +19,6 @@
 const base::Feature kDragTabsInTabletMode{"DragTabsInTabletMode",
                                           base::FEATURE_ENABLED_BY_DEFAULT};
 
-const base::Feature kKeyboardShortcutViewerApp{
-    "KeyboardShortcutViewerApp", base::FEATURE_ENABLED_BY_DEFAULT};
-
 const base::Feature kLockScreenNotifications{"LockScreenNotifications",
                                              base::FEATURE_ENABLED_BY_DEFAULT};
 
@@ -73,10 +70,6 @@
   return base::FeatureList::IsEnabled(kDockedMagnifier);
 }
 
-bool IsKeyboardShortcutViewerAppEnabled() {
-  return base::FeatureList::IsEnabled(kKeyboardShortcutViewerApp);
-}
-
 bool IsLockScreenNotificationsEnabled() {
   return base::FeatureList::IsEnabled(kLockScreenNotifications);
 }
diff --git a/ash/public/cpp/ash_features.h b/ash/public/cpp/ash_features.h
index 52e5f084..8c5bc34 100644
--- a/ash/public/cpp/ash_features.h
+++ b/ash/public/cpp/ash_features.h
@@ -26,11 +26,6 @@
 // https://crbug.com/823769.
 ASH_PUBLIC_EXPORT extern const base::Feature kDragTabsInTabletMode;
 
-// Enables the keyboard shortcut viewer mojo app.
-// TODO(msw): Remove this after the feature is fully launched.
-// https://crbug.com/841020.
-ASH_PUBLIC_EXPORT extern const base::Feature kKeyboardShortcutViewerApp;
-
 // Enables notifications on the lock screen.
 ASH_PUBLIC_EXPORT extern const base::Feature kLockScreenNotifications;
 
diff --git a/ash/shelf/app_list_button.cc b/ash/shelf/app_list_button.cc
index 33cf23a..e65a17f 100644
--- a/ash/shelf/app_list_button.cc
+++ b/ash/shelf/app_list_button.cc
@@ -124,10 +124,10 @@
       if (!Shell::Get()->app_list_controller()->IsVisible() || IsTabletMode())
         AnimateInkDrop(views::InkDropState::ACTION_TRIGGERED, event);
 
-      ImageButton::OnGestureEvent(event);
+      Button::OnGestureEvent(event);
       return;
     case ui::ET_GESTURE_TAP_DOWN:
-      // If |!ShouldEnterPushedState|, ImageButton::OnGestureEvent will not set
+      // If |!ShouldEnterPushedState|, Button::OnGestureEvent will not set
       // the event to be handled. This will cause the |ET_GESTURE_TAP| or
       // |ET_GESTURE_TAP_CANCEL| not to be sent to |app_list_button|, therefore
       // leaving the assistant overlay ripple stays visible.
@@ -145,7 +145,7 @@
       if (!Shell::Get()->app_list_controller()->IsVisible() || IsTabletMode())
         AnimateInkDrop(views::InkDropState::ACTION_PENDING, event);
 
-      ImageButton::OnGestureEvent(event);
+      Button::OnGestureEvent(event);
       // If assistant overlay animation starts, we need to make sure the event
       // is handled in order to end the animation in |ET_GESTURE_TAP| or
       // |ET_GESTURE_TAP_CANCEL|.
@@ -166,7 +166,7 @@
           Shell::Get()->app_list_controller()->StartVoiceInteractionSession();
         }
       } else {
-        ImageButton::OnGestureEvent(event);
+        Button::OnGestureEvent(event);
       }
       return;
     case ui::ET_GESTURE_LONG_TAP:
@@ -177,33 +177,33 @@
         AnimateInkDrop(views::InkDropState::HIDDEN, event);
         event->SetHandled();
       } else {
-        ImageButton::OnGestureEvent(event);
+        Button::OnGestureEvent(event);
       }
       return;
     default:
-      ImageButton::OnGestureEvent(event);
+      Button::OnGestureEvent(event);
       return;
   }
 }
 
 bool AppListButton::OnMousePressed(const ui::MouseEvent& event) {
-  ImageButton::OnMousePressed(event);
+  Button::OnMousePressed(event);
   shelf_view_->PointerPressedOnButton(this, ShelfView::MOUSE, event);
   return true;
 }
 
 void AppListButton::OnMouseReleased(const ui::MouseEvent& event) {
-  ImageButton::OnMouseReleased(event);
+  Button::OnMouseReleased(event);
   shelf_view_->PointerReleasedOnButton(this, ShelfView::MOUSE, false);
 }
 
 void AppListButton::OnMouseCaptureLost() {
   shelf_view_->PointerReleasedOnButton(this, ShelfView::MOUSE, true);
-  ImageButton::OnMouseCaptureLost();
+  Button::OnMouseCaptureLost();
 }
 
 bool AppListButton::OnMouseDragged(const ui::MouseEvent& event) {
-  ImageButton::OnMouseDragged(event);
+  Button::OnMouseDragged(event);
   shelf_view_->PointerDraggedOnButton(this, ShelfView::MOUSE, event);
   return true;
 }
@@ -227,7 +227,7 @@
 }
 
 void AppListButton::NotifyClick(const ui::Event& event) {
-  ImageButton::NotifyClick(event);
+  Button::NotifyClick(event);
   if (listener_)
     listener_->ButtonPressed(this, event, GetInkDrop());
 }
@@ -235,7 +235,7 @@
 bool AppListButton::ShouldEnterPushedState(const ui::Event& event) {
   if (!shelf_view_->ShouldEventActivateButton(this, event))
     return false;
-  return views::ImageButton::ShouldEnterPushedState(event);
+  return views::Button::ShouldEnterPushedState(event);
 }
 
 std::unique_ptr<views::InkDrop> AppListButton::CreateInkDrop() {
diff --git a/ash/shelf/app_list_button.h b/ash/shelf/app_list_button.h
index 9a0bcd2..8d50849 100644
--- a/ash/shelf/app_list_button.h
+++ b/ash/shelf/app_list_button.h
@@ -15,7 +15,6 @@
 #include "ash/shell_observer.h"
 #include "base/macros.h"
 #include "third_party/skia/include/core/SkColor.h"
-#include "ui/views/controls/button/image_button.h"
 
 namespace base {
 class OneShotTimer;
diff --git a/ash/shelf/back_button.cc b/ash/shelf/back_button.cc
index ebf4ec9..50b1c46 100644
--- a/ash/shelf/back_button.cc
+++ b/ash/shelf/back_button.cc
@@ -34,7 +34,7 @@
 BackButton::~BackButton() = default;
 
 void BackButton::OnGestureEvent(ui::GestureEvent* event) {
-  ImageButton::OnGestureEvent(event);
+  Button::OnGestureEvent(event);
   if (event->type() == ui::ET_GESTURE_TAP ||
       event->type() == ui::ET_GESTURE_TAP_DOWN) {
     GenerateAndSendBackEvent(event->type());
@@ -42,13 +42,13 @@
 }
 
 bool BackButton::OnMousePressed(const ui::MouseEvent& event) {
-  ImageButton::OnMousePressed(event);
+  Button::OnMousePressed(event);
   GenerateAndSendBackEvent(event.type());
   return true;
 }
 
 void BackButton::OnMouseReleased(const ui::MouseEvent& event) {
-  ImageButton::OnMouseReleased(event);
+  Button::OnMouseReleased(event);
   GenerateAndSendBackEvent(event.type());
 }
 
diff --git a/ash/shelf/back_button.h b/ash/shelf/back_button.h
index a8869a74..09acc47 100644
--- a/ash/shelf/back_button.h
+++ b/ash/shelf/back_button.h
@@ -10,7 +10,6 @@
 #include "ash/ash_export.h"
 #include "ash/shelf/shelf_control_button.h"
 #include "base/macros.h"
-#include "ui/views/controls/button/image_button.h"
 
 namespace ash {
 
@@ -23,7 +22,7 @@
   ~BackButton() override;
 
  protected:
-  // views::ImageButton:
+  // views::Button:
   void OnGestureEvent(ui::GestureEvent* event) override;
   bool OnMousePressed(const ui::MouseEvent& event) override;
   void OnMouseReleased(const ui::MouseEvent& event) override;
diff --git a/ash/shelf/shelf_control_button.cc b/ash/shelf/shelf_control_button.cc
index afeebba..36393e5 100644
--- a/ash/shelf/shelf_control_button.cc
+++ b/ash/shelf/shelf_control_button.cc
@@ -17,7 +17,7 @@
 
 namespace ash {
 
-ShelfControlButton::ShelfControlButton() : views::ImageButton(nullptr) {
+ShelfControlButton::ShelfControlButton() : views::Button(nullptr) {
   SetInkDropMode(InkDropMode::ON_NO_GESTURE_HANDLER);
   set_has_ink_drop_action_on_click(true);
   set_ink_drop_base_color(kShelfInkDropBaseColor);
diff --git a/ash/shelf/shelf_control_button.h b/ash/shelf/shelf_control_button.h
index 58f609f..4fb3638 100644
--- a/ash/shelf/shelf_control_button.h
+++ b/ash/shelf/shelf_control_button.h
@@ -9,13 +9,13 @@
 
 #include "ash/ash_export.h"
 #include "base/macros.h"
-#include "ui/views/controls/button/image_button.h"
+#include "ui/views/controls/button/button.h"
 
 namespace ash {
 
 // Base class for controls shown on the shelf that are not app shortcuts, such
 // as the app list, back, and overflow buttons.
-class ASH_EXPORT ShelfControlButton : public views::ImageButton {
+class ASH_EXPORT ShelfControlButton : public views::Button {
  public:
   ShelfControlButton();
   ~ShelfControlButton() override;
@@ -25,7 +25,7 @@
   gfx::Point GetCenterPoint() const;
 
  protected:
-  // views::ImageButton:
+  // views::Button:
   std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override;
   std::unique_ptr<views::InkDrop> CreateInkDrop() override;
   std::unique_ptr<views::InkDropMask> CreateInkDropMask() const override;
diff --git a/ash/test/ash_test_helper.cc b/ash/test/ash_test_helper.cc
index a0c6c43..b2204278 100644
--- a/ash/test/ash_test_helper.cc
+++ b/ash/test/ash_test_helper.cc
@@ -29,7 +29,6 @@
 #include "base/strings/string_split.h"
 #include "base/token.h"
 #include "chromeos/audio/cras_audio_handler.h"
-#include "chromeos/cryptohome/system_salt_getter.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/power_policy_controller.h"
 #include "chromeos/network/network_handler.h"
@@ -177,7 +176,6 @@
   // Create CrasAudioHandler for testing since g_browser_process is not
   // created in AshTestBase tests.
   chromeos::CrasAudioHandler::InitializeForTesting();
-  chromeos::SystemSaltGetter::Initialize();
 
   // Reset the global state for the cursor manager. This includes the
   // last cursor visibility state, etc.
@@ -255,7 +253,6 @@
   // CompositorFrameSinkClient::ReclaimResources()
   base::RunLoop().RunUntilIdle();
 
-  chromeos::SystemSaltGetter::Shutdown();
   chromeos::CrasAudioHandler::Shutdown();
 
   if (power_policy_controller_initialized_) {
diff --git a/base/allocator/allocator_shim_override_ucrt_symbols_win.h b/base/allocator/allocator_shim_override_ucrt_symbols_win.h
index 0cc52f2..c6cc306e 100644
--- a/base/allocator/allocator_shim_override_ucrt_symbols_win.h
+++ b/base/allocator/allocator_shim_override_ucrt_symbols_win.h
@@ -84,6 +84,29 @@
   ShimAlignedFree(address, nullptr);
 }
 
+// _recalloc_base is called by CRT internally.
+__declspec(restrict) void* _recalloc_base(void* block,
+                                          size_t count,
+                                          size_t size) {
+  const size_t old_block_size = (block != nullptr) ? _msize(block) : 0;
+  base::CheckedNumeric<size_t> new_block_size_checked = count;
+  new_block_size_checked *= size;
+  const size_t new_block_size = new_block_size_checked.ValueOrDie();
+
+  void* const new_block = realloc(block, new_block_size);
+
+  if (new_block != nullptr && old_block_size < new_block_size) {
+    memset(static_cast<char*>(new_block) + old_block_size, 0,
+           new_block_size - old_block_size);
+  }
+
+  return new_block;
+}
+
+__declspec(restrict) void* _recalloc(void* block, size_t count, size_t size) {
+  return _recalloc_base(block, count, size);
+}
+
 // The following uncommon _aligned_* routines are not used in Chromium and have
 // been shimmed to immediately crash to ensure that implementations are added if
 // uses are introduced.
diff --git a/base/android/jni_array_unittest.cc b/base/android/jni_array_unittest.cc
index 5c5eb058..2ddf0ff 100644
--- a/base/android/jni_array_unittest.cc
+++ b/base/android/jni_array_unittest.cc
@@ -11,7 +11,7 @@
 
 #include "base/android/jni_android.h"
 #include "base/android/scoped_java_ref.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace base {
@@ -19,7 +19,7 @@
 
 TEST(JniArray, BasicConversions) {
   const uint8_t kBytes[] = {0, 1, 2, 3};
-  const size_t kLen = arraysize(kBytes);
+  const size_t kLen = base::size(kBytes);
   JNIEnv* env = AttachCurrentThread();
   ScopedJavaLocalRef<jbyteArray> bytes = ToJavaByteArray(env, kBytes, kLen);
   ASSERT_TRUE(bytes);
@@ -75,7 +75,7 @@
 
 TEST(JniArray, BoolConversions) {
   const bool kBools[] = {false, true, false};
-  const size_t kLen = arraysize(kBools);
+  const size_t kLen = base::size(kBools);
 
   JNIEnv* env = AttachCurrentThread();
   CheckBoolConversion(env, kBools, kLen, ToJavaBooleanArray(env, kBools, kLen));
@@ -101,7 +101,7 @@
 TEST(JniArray, IntConversions) {
   const int kInts[] = {0, 1, -1, std::numeric_limits<int32_t>::min(),
                        std::numeric_limits<int32_t>::max()};
-  const size_t kLen = arraysize(kInts);
+  const size_t kLen = base::size(kInts);
 
   JNIEnv* env = AttachCurrentThread();
   CheckIntConversion(env, kInts, kLen, ToJavaIntArray(env, kInts, kLen));
@@ -129,7 +129,7 @@
 TEST(JniArray, LongConversions) {
   const int64_t kLongs[] = {0, 1, -1, std::numeric_limits<int64_t>::min(),
                             std::numeric_limits<int64_t>::max()};
-  const size_t kLen = arraysize(kLongs);
+  const size_t kLen = base::size(kLongs);
 
   JNIEnv* env = AttachCurrentThread();
   CheckLongConversion(env, kLongs, kLen, ToJavaLongArray(env, kLongs, kLen));
@@ -179,7 +179,7 @@
 
 TEST(JniArray, FloatConversions) {
   const float kFloats[] = { 0.0f, 1.0f, -10.0f};
-  const size_t kLen = arraysize(kFloats);
+  const size_t kLen = base::size(kFloats);
 
   JNIEnv* env = AttachCurrentThread();
   CheckFloatConversion(env, kFloats, kLen,
@@ -191,7 +191,7 @@
 
 TEST(JniArray, JavaBooleanArrayToBoolVector) {
   const bool kBools[] = {false, true, false};
-  const size_t kLen = arraysize(kBools);
+  const size_t kLen = base::size(kBools);
 
   JNIEnv* env = AttachCurrentThread();
   ScopedJavaLocalRef<jbooleanArray> jbooleans(env, env->NewBooleanArray(kLen));
@@ -214,7 +214,7 @@
 
 TEST(JniArray, JavaIntArrayToIntVector) {
   const int kInts[] = {0, 1, -1};
-  const size_t kLen = arraysize(kInts);
+  const size_t kLen = base::size(kInts);
 
   JNIEnv* env = AttachCurrentThread();
   ScopedJavaLocalRef<jintArray> jints(env, env->NewIntArray(kLen));
@@ -236,7 +236,7 @@
 
 TEST(JniArray, JavaLongArrayToInt64Vector) {
   const int64_t kInt64s[] = {0LL, 1LL, -1LL};
-  const size_t kLen = arraysize(kInt64s);
+  const size_t kLen = base::size(kInt64s);
 
   JNIEnv* env = AttachCurrentThread();
   ScopedJavaLocalRef<jlongArray> jlongs(env, env->NewLongArray(kLen));
@@ -264,7 +264,7 @@
 
 TEST(JniArray, JavaLongArrayToLongVector) {
   const int64_t kInt64s[] = {0LL, 1LL, -1LL};
-  const size_t kLen = arraysize(kInt64s);
+  const size_t kLen = base::size(kInt64s);
 
   JNIEnv* env = AttachCurrentThread();
   ScopedJavaLocalRef<jlongArray> jlongs(env, env->NewLongArray(kLen));
@@ -291,7 +291,7 @@
 
 TEST(JniArray, JavaFloatArrayToFloatVector) {
   const float kFloats[] = {0.0, 0.5, -0.5};
-  const size_t kLen = arraysize(kFloats);
+  const size_t kLen = base::size(kFloats);
 
   JNIEnv* env = AttachCurrentThread();
   ScopedJavaLocalRef<jfloatArray> jfloats(env, env->NewFloatArray(kLen));
@@ -367,12 +367,12 @@
   // Populate int[][] object.
   const int kInts0[] = {0, 1, -1, std::numeric_limits<int32_t>::min(),
                         std::numeric_limits<int32_t>::max()};
-  const size_t kLen0 = arraysize(kInts0);
+  const size_t kLen0 = base::size(kInts0);
   ScopedJavaLocalRef<jintArray> int_array0 = ToJavaIntArray(env, kInts0, kLen0);
   env->SetObjectArrayElement(array.obj(), 0, int_array0.obj());
 
   const int kInts1[] = {3, 4, 5};
-  const size_t kLen1 = arraysize(kInts1);
+  const size_t kLen1 = base::size(kInts1);
   ScopedJavaLocalRef<jintArray> int_array1 = ToJavaIntArray(env, kInts1, kLen1);
   env->SetObjectArrayElement(array.obj(), 1, int_array1.obj());
 
@@ -382,7 +382,7 @@
   env->SetObjectArrayElement(array.obj(), 2, int_array2.obj());
 
   const int kInts3[] = {16};
-  const size_t kLen3 = arraysize(kInts3);
+  const size_t kLen3 = base::size(kInts3);
   ScopedJavaLocalRef<jintArray> int_array3 = ToJavaIntArray(env, kInts3, kLen3);
   env->SetObjectArrayElement(array.obj(), 3, int_array3.obj());
 
diff --git a/base/command_line.cc b/base/command_line.cc
index 7825952..ba7acc29 100644
--- a/base/command_line.cc
+++ b/base/command_line.cc
@@ -9,7 +9,6 @@
 
 #include "base/files/file_path.h"
 #include "base/logging.h"
-#include "base/macros.h"
 #include "base/stl_util.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_tokenizer.h"
@@ -42,7 +41,7 @@
 // Unixes don't use slash as a switch.
 const CommandLine::CharType* const kSwitchPrefixes[] = {"--", "-"};
 #endif
-size_t switch_prefix_count = arraysize(kSwitchPrefixes);
+size_t switch_prefix_count = base::size(kSwitchPrefixes);
 
 size_t GetSwitchPrefixLength(const CommandLine::StringType& string) {
   for (size_t i = 0; i < switch_prefix_count; ++i) {
@@ -186,8 +185,8 @@
 // static
 void CommandLine::set_slash_is_not_a_switch() {
   // The last switch prefix should be slash, so adjust the size to skip it.
-  DCHECK_EQ(wcscmp(kSwitchPrefixes[arraysize(kSwitchPrefixes) - 1], L"/"), 0);
-  switch_prefix_count = arraysize(kSwitchPrefixes) - 1;
+  DCHECK_EQ(wcscmp(kSwitchPrefixes[base::size(kSwitchPrefixes) - 1], L"/"), 0);
+  switch_prefix_count = base::size(kSwitchPrefixes) - 1;
 }
 
 // static
diff --git a/base/command_line_unittest.cc b/base/command_line_unittest.cc
index 93ccd7c..46d04086 100644
--- a/base/command_line_unittest.cc
+++ b/base/command_line_unittest.cc
@@ -9,7 +9,7 @@
 #include <vector>
 
 #include "base/files/file_path.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -47,7 +47,7 @@
       FILE_PATH_LITERAL("--not-a-switch"),
       FILE_PATH_LITERAL("\"in the time of submarines...\""),
       FILE_PATH_LITERAL("unquoted arg-with-space")};
-  CommandLine cl(arraysize(argv), argv);
+  CommandLine cl(base::size(argv), argv);
 
   EXPECT_FALSE(cl.GetCommandLineString().empty());
   EXPECT_FALSE(cl.HasSwitch("cruller"));
@@ -307,22 +307,22 @@
  const CommandLine::CharType* raw_argv[] = { FILE_PATH_LITERAL("prog"),
                                              FILE_PATH_LITERAL("--"),
                                              FILE_PATH_LITERAL("--arg1") };
-  CommandLine cl(arraysize(raw_argv), raw_argv);
+ CommandLine cl(base::size(raw_argv), raw_argv);
 
-  cl.AppendSwitch("switch1");
-  cl.AppendSwitchASCII("switch2", "foo");
+ cl.AppendSwitch("switch1");
+ cl.AppendSwitchASCII("switch2", "foo");
 
-  cl.AppendArg("--arg2");
+ cl.AppendArg("--arg2");
 
-  EXPECT_EQ(FILE_PATH_LITERAL("prog --switch1 --switch2=foo -- --arg1 --arg2"),
-            cl.GetCommandLineString());
-  CommandLine::StringVector cl_argv = cl.argv();
-  EXPECT_EQ(FILE_PATH_LITERAL("prog"), cl_argv[0]);
-  EXPECT_EQ(FILE_PATH_LITERAL("--switch1"), cl_argv[1]);
-  EXPECT_EQ(FILE_PATH_LITERAL("--switch2=foo"), cl_argv[2]);
-  EXPECT_EQ(FILE_PATH_LITERAL("--"), cl_argv[3]);
-  EXPECT_EQ(FILE_PATH_LITERAL("--arg1"), cl_argv[4]);
-  EXPECT_EQ(FILE_PATH_LITERAL("--arg2"), cl_argv[5]);
+ EXPECT_EQ(FILE_PATH_LITERAL("prog --switch1 --switch2=foo -- --arg1 --arg2"),
+           cl.GetCommandLineString());
+ CommandLine::StringVector cl_argv = cl.argv();
+ EXPECT_EQ(FILE_PATH_LITERAL("prog"), cl_argv[0]);
+ EXPECT_EQ(FILE_PATH_LITERAL("--switch1"), cl_argv[1]);
+ EXPECT_EQ(FILE_PATH_LITERAL("--switch2=foo"), cl_argv[2]);
+ EXPECT_EQ(FILE_PATH_LITERAL("--"), cl_argv[3]);
+ EXPECT_EQ(FILE_PATH_LITERAL("--arg1"), cl_argv[4]);
+ EXPECT_EQ(FILE_PATH_LITERAL("--arg2"), cl_argv[5]);
 }
 
 // Tests that when AppendArguments is called that the program is set correctly
diff --git a/base/containers/linked_list_unittest.cc b/base/containers/linked_list_unittest.cc
index 8e547ba..fd09237 100644
--- a/base/containers/linked_list_unittest.cc
+++ b/base/containers/linked_list_unittest.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/containers/linked_list.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace base {
@@ -91,7 +91,7 @@
   EXPECT_EQ(&n1, list.tail());
   {
     const int expected[] = {1};
-    ExpectListContents(list, arraysize(expected), expected);
+    ExpectListContents(list, base::size(expected), expected);
   }
 
   Node n2(2);
@@ -101,7 +101,7 @@
   EXPECT_EQ(&n2, list.tail());
   {
     const int expected[] = {1, 2};
-    ExpectListContents(list, arraysize(expected), expected);
+    ExpectListContents(list, base::size(expected), expected);
   }
 
   Node n3(3);
@@ -111,7 +111,7 @@
   EXPECT_EQ(&n3, list.tail());
   {
     const int expected[] = {1, 2, 3};
-    ExpectListContents(list, arraysize(expected), expected);
+    ExpectListContents(list, base::size(expected), expected);
   }
 }
 
@@ -134,7 +134,7 @@
   EXPECT_EQ(&n5, list.tail());
   {
     const int expected[] = {1, 2, 3, 4, 5};
-    ExpectListContents(list, arraysize(expected), expected);
+    ExpectListContents(list, base::size(expected), expected);
   }
 
   // Remove from the middle.
@@ -144,7 +144,7 @@
   EXPECT_EQ(&n5, list.tail());
   {
     const int expected[] = {1, 2, 4, 5};
-    ExpectListContents(list, arraysize(expected), expected);
+    ExpectListContents(list, base::size(expected), expected);
   }
 
   // Remove from the tail.
@@ -154,7 +154,7 @@
   EXPECT_EQ(&n4, list.tail());
   {
     const int expected[] = {1, 2, 4};
-    ExpectListContents(list, arraysize(expected), expected);
+    ExpectListContents(list, base::size(expected), expected);
   }
 
   // Remove from the head.
@@ -164,7 +164,7 @@
   EXPECT_EQ(&n4, list.tail());
   {
     const int expected[] = {2, 4};
-    ExpectListContents(list, arraysize(expected), expected);
+    ExpectListContents(list, base::size(expected), expected);
   }
 
   // Empty the list.
@@ -186,7 +186,7 @@
   EXPECT_EQ(&n5, list.tail());
   {
     const int expected[] = {1, 2, 3, 4, 5};
-    ExpectListContents(list, arraysize(expected), expected);
+    ExpectListContents(list, base::size(expected), expected);
   }
 }
 
@@ -205,7 +205,7 @@
   EXPECT_EQ(&n2, list.tail());
   {
     const int expected[] = {1, 2};
-    ExpectListContents(list, arraysize(expected), expected);
+    ExpectListContents(list, base::size(expected), expected);
   }
 
   n3.InsertBefore(&n2);
@@ -214,7 +214,7 @@
   EXPECT_EQ(&n2, list.tail());
   {
     const int expected[] = {1, 3, 2};
-    ExpectListContents(list, arraysize(expected), expected);
+    ExpectListContents(list, base::size(expected), expected);
   }
 
   n4.InsertBefore(&n1);
@@ -223,7 +223,7 @@
   EXPECT_EQ(&n2, list.tail());
   {
     const int expected[] = {4, 1, 3, 2};
-    ExpectListContents(list, arraysize(expected), expected);
+    ExpectListContents(list, base::size(expected), expected);
   }
 }
 
@@ -242,7 +242,7 @@
   EXPECT_EQ(&n2, list.tail());
   {
     const int expected[] = {1, 2};
-    ExpectListContents(list, arraysize(expected), expected);
+    ExpectListContents(list, base::size(expected), expected);
   }
 
   n3.InsertAfter(&n2);
@@ -251,7 +251,7 @@
   EXPECT_EQ(&n3, list.tail());
   {
     const int expected[] = {1, 2, 3};
-    ExpectListContents(list, arraysize(expected), expected);
+    ExpectListContents(list, base::size(expected), expected);
   }
 
   n4.InsertAfter(&n1);
@@ -260,7 +260,7 @@
   EXPECT_EQ(&n3, list.tail());
   {
     const int expected[] = {1, 4, 2, 3};
-    ExpectListContents(list, arraysize(expected), expected);
+    ExpectListContents(list, base::size(expected), expected);
   }
 }
 
diff --git a/base/containers/span_unittest.cc b/base/containers/span_unittest.cc
index 05429a5..40b1afb4 100644
--- a/base/containers/span_unittest.cc
+++ b/base/containers/span_unittest.cc
@@ -12,7 +12,7 @@
 #include <vector>
 
 #include "base/containers/checked_iterators.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -105,7 +105,7 @@
 
   span<const int> const_span(array);
   EXPECT_EQ(array, const_span.data());
-  EXPECT_EQ(arraysize(array), const_span.size());
+  EXPECT_EQ(base::size(array), const_span.size());
   for (size_t i = 0; i < const_span.size(); ++i)
     EXPECT_EQ(array[i], const_span[i]);
 
@@ -1070,7 +1070,7 @@
 
   constexpr span<const int> lasts = constexpr_span.last(size);
   for (size_t i = 0; i < lasts.size(); ++i) {
-    const size_t j = (arraysize(kArray) - size) + i;
+    const size_t j = (base::size(kArray) - size) + i;
     EXPECT_EQ(kArray[j], lasts[i]);
   }
 
diff --git a/base/cpu.cc b/base/cpu.cc
index cd9066f5..aac2e957 100644
--- a/base/cpu.cc
+++ b/base/cpu.cc
@@ -12,7 +12,7 @@
 #include <algorithm>
 #include <utility>
 
-#include "base/macros.h"
+#include "base/stl_util.h"
 #include "build/build_config.h"
 
 #if defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX))
@@ -147,7 +147,7 @@
   int num_ids = cpu_info[0];
   std::swap(cpu_info[2], cpu_info[3]);
   static constexpr size_t kVendorNameSize = 3 * sizeof(cpu_info[1]);
-  static_assert(kVendorNameSize < arraysize(cpu_string),
+  static_assert(kVendorNameSize < base::size(cpu_string),
                 "cpu_string too small");
   memcpy(cpu_string, &cpu_info[1], kVendorNameSize);
   cpu_string[kVendorNameSize] = '\0';
@@ -202,7 +202,7 @@
   static constexpr int kParameterStart = 0x80000002;
   static constexpr int kParameterEnd = 0x80000004;
   static constexpr int kParameterSize = kParameterEnd - kParameterStart + 1;
-  static_assert(kParameterSize * sizeof(cpu_info) + 1 == arraysize(cpu_string),
+  static_assert(kParameterSize * sizeof(cpu_info) + 1 == base::size(cpu_string),
                 "cpu_string has wrong size");
 
   if (max_parameter >= kParameterEnd) {
diff --git a/base/debug/alias.h b/base/debug/alias.h
index 128fdaa0..35af2c23 100644
--- a/base/debug/alias.h
+++ b/base/debug/alias.h
@@ -6,6 +6,7 @@
 #define BASE_DEBUG_ALIAS_H_
 
 #include "base/base_export.h"
+#include "base/stl_util.h"
 #include "base/strings/string_util.h"
 
 namespace base {
@@ -35,9 +36,9 @@
 // Convenience macro that copies the null-terminated string from |c_str| into a
 // stack-allocated char array named |var_name| that holds up to |char_count|
 // characters and should be preserved in memory dumps.
-#define DEBUG_ALIAS_FOR_CSTR(var_name, c_str, char_count)  \
-  char var_name[char_count];                               \
-  ::base::strlcpy(var_name, (c_str), arraysize(var_name)); \
+#define DEBUG_ALIAS_FOR_CSTR(var_name, c_str, char_count)   \
+  char var_name[char_count];                                \
+  ::base::strlcpy(var_name, (c_str), base::size(var_name)); \
   ::base::debug::Alias(var_name);
 
 #endif  // BASE_DEBUG_ALIAS_H_
diff --git a/base/debug/debugger_posix.cc b/base/debug/debugger_posix.cc
index 150a7d8..01ccdb8 100644
--- a/base/debug/debugger_posix.cc
+++ b/base/debug/debugger_posix.cc
@@ -17,7 +17,7 @@
 #include <memory>
 #include <vector>
 
-#include "base/macros.h"
+#include "base/stl_util.h"
 #include "base/test/clang_coverage.h"
 #include "base/threading/platform_thread.h"
 #include "base/time/time.h"
@@ -97,13 +97,13 @@
   size_t info_size = sizeof(info);
 
 #if defined(OS_OPENBSD)
-  if (sysctl(mib, arraysize(mib), NULL, &info_size, NULL, 0) < 0)
+  if (sysctl(mib, base::size(mib), NULL, &info_size, NULL, 0) < 0)
     return -1;
 
   mib[5] = (info_size / sizeof(struct kinfo_proc));
 #endif
 
-  int sysctl_result = sysctl(mib, arraysize(mib), &info, &info_size, NULL, 0);
+  int sysctl_result = sysctl(mib, base::size(mib), &info, &info_size, NULL, 0);
   DCHECK_EQ(sysctl_result, 0);
   if (sysctl_result != 0) {
     is_set = true;
diff --git a/base/debug/proc_maps_linux_unittest.cc b/base/debug/proc_maps_linux_unittest.cc
index 639e046..8192548 100644
--- a/base/debug/proc_maps_linux_unittest.cc
+++ b/base/debug/proc_maps_linux_unittest.cc
@@ -7,8 +7,8 @@
 
 #include "base/debug/proc_maps_linux.h"
 #include "base/files/file_path.h"
-#include "base/macros.h"
 #include "base/path_service.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/threading/platform_thread.h"
 #include "build/build_config.h"
@@ -172,7 +172,7 @@
          MappedMemoryRegion::EXECUTE | MappedMemoryRegion::PRIVATE},
   };
 
-  for (size_t i = 0; i < arraysize(kTestCases); ++i) {
+  for (size_t i = 0; i < base::size(kTestCases); ++i) {
     SCOPED_TRACE(
         base::StringPrintf("kTestCases[%zu] = %s", i, kTestCases[i].input));
 
@@ -268,7 +268,7 @@
     "00400000-0040b000 r-xp 00000000 794418 /bin/cat\n",   // Missing device.
   };
 
-  for (size_t i = 0; i < arraysize(kTestCases); ++i) {
+  for (size_t i = 0; i < base::size(kTestCases); ++i) {
     SCOPED_TRACE(base::StringPrintf("kTestCases[%zu] = %s", i, kTestCases[i]));
     std::vector<MappedMemoryRegion> regions;
     EXPECT_FALSE(ParseProcMaps(kTestCases[i], &regions));
@@ -285,7 +285,7 @@
     "00400000-0040b000 rwxp 00000000 fc:00 parse! /bin/cat\n",
   };
 
-  for (size_t i = 0; i < arraysize(kTestCases); ++i) {
+  for (size_t i = 0; i < base::size(kTestCases); ++i) {
     SCOPED_TRACE(base::StringPrintf("kTestCases[%zu] = %s", i, kTestCases[i]));
     std::vector<MappedMemoryRegion> regions;
     EXPECT_FALSE(ParseProcMaps(kTestCases[i], &regions));
diff --git a/base/feature_list_unittest.cc b/base/feature_list_unittest.cc
index 3a870ac..df99487 100644
--- a/base/feature_list_unittest.cc
+++ b/base/feature_list_unittest.cc
@@ -10,9 +10,9 @@
 #include <utility>
 
 #include "base/format_macros.h"
-#include "base/macros.h"
 #include "base/metrics/field_trial.h"
 #include "base/metrics/persistent_memory_allocator.h"
+#include "base/stl_util.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
@@ -88,7 +88,7 @@
       {"OnByDefault", "OnByDefault,OffByDefault", false, false},
   };
 
-  for (size_t i = 0; i < arraysize(test_cases); ++i) {
+  for (size_t i = 0; i < base::size(test_cases); ++i) {
     const auto& test_case = test_cases[i];
     SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]: [%s] [%s]", i,
                                     test_case.enable_features,
@@ -144,7 +144,7 @@
   };
 
   FieldTrial::ActiveGroup active_group;
-  for (size_t i = 0; i < arraysize(test_cases); ++i) {
+  for (size_t i = 0; i < base::size(test_cases); ++i) {
     const auto& test_case = test_cases[i];
     SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]", i));
 
@@ -304,7 +304,7 @@
   const char kForcedOnGroupName[] = "ForcedOn";
   const char kForcedOffGroupName[] = "ForcedOff";
 
-  for (size_t i = 0; i < arraysize(test_cases); ++i) {
+  for (size_t i = 0; i < base::size(test_cases); ++i) {
     const auto& test_case = test_cases[i];
     SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]: [%s] [%s]", i,
                                     test_case.enable_features,
diff --git a/base/file_version_info_win.cc b/base/file_version_info_win.cc
index c2221b8..2c1a7436 100644
--- a/base/file_version_info_win.cc
+++ b/base/file_version_info_win.cc
@@ -9,6 +9,7 @@
 
 #include "base/files/file_path.h"
 #include "base/logging.h"
+#include "base/stl_util.h"
 #include "base/threading/scoped_blocking_call.h"
 #include "base/win/resource_util.h"
 
@@ -171,7 +172,7 @@
   lang_codepage[i++] = 1252;
 
   i = 0;
-  while (i < arraysize(lang_codepage)) {
+  while (i < base::size(lang_codepage)) {
     wchar_t sub_block[MAX_PATH];
     WORD language = lang_codepage[i++];
     WORD code_page = lang_codepage[i++];
diff --git a/base/files/file_path.h b/base/files/file_path.h
index 22ec09de..e0cec90 100644
--- a/base/files/file_path.h
+++ b/base/files/file_path.h
@@ -110,7 +110,7 @@
 
 #include "base/base_export.h"
 #include "base/compiler_specific.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
 #include "base/strings/string16.h"
 #include "base/strings/string_piece.h"
 #include "build/build_config.h"
@@ -169,7 +169,7 @@
   // when composing pathnames.
   static const CharType kSeparators[];
 
-  // arraysize(kSeparators).
+  // base::size(kSeparators).
   static const size_t kSeparatorsLength;
 
   // A special path component meaning "this directory."
diff --git a/base/files/file_path_constants.cc b/base/files/file_path_constants.cc
index 0b74846..d2d8d31 100644
--- a/base/files/file_path_constants.cc
+++ b/base/files/file_path_constants.cc
@@ -5,7 +5,7 @@
 #include <stddef.h>
 
 #include "base/files/file_path.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
 
 namespace base {
 
@@ -15,7 +15,7 @@
 const FilePath::CharType FilePath::kSeparators[] = FILE_PATH_LITERAL("/");
 #endif  // FILE_PATH_USES_WIN_SEPARATORS
 
-const size_t FilePath::kSeparatorsLength = arraysize(kSeparators);
+const size_t FilePath::kSeparatorsLength = base::size(kSeparators);
 
 const FilePath::CharType FilePath::kCurrentDirectory[] = FILE_PATH_LITERAL(".");
 const FilePath::CharType FilePath::kParentDirectory[] = FILE_PATH_LITERAL("..");
diff --git a/base/files/file_path_unittest.cc b/base/files/file_path_unittest.cc
index 5096310..34aa91c 100644
--- a/base/files/file_path_unittest.cc
+++ b/base/files/file_path_unittest.cc
@@ -7,7 +7,7 @@
 #include <sstream>
 
 #include "base/files/file_path.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -21,7 +21,7 @@
 #define FPL(x) FILE_PATH_LITERAL(x)
 
 // This macro constructs strings which can contain NULs.
-#define FPS(x) FilePath::StringType(FPL(x), arraysize(FPL(x)) - 1)
+#define FPS(x) FilePath::StringType(FPL(x), base::size(FPL(x)) - 1)
 
 namespace base {
 
@@ -142,7 +142,7 @@
 #endif  // FILE_PATH_USES_WIN_SEPARATORS
   };
 
-  for (size_t i = 0; i < arraysize(cases); ++i) {
+  for (size_t i = 0; i < base::size(cases); ++i) {
     FilePath input(cases[i].input);
     FilePath observed = input.DirName();
     EXPECT_EQ(FilePath::StringType(cases[i].expected), observed.value()) <<
@@ -229,7 +229,7 @@
 #endif  // FILE_PATH_USES_WIN_SEPARATORS
   };
 
-  for (size_t i = 0; i < arraysize(cases); ++i) {
+  for (size_t i = 0; i < base::size(cases); ++i) {
     FilePath input(cases[i].input);
     FilePath observed = input.BaseName();
     EXPECT_EQ(FilePath::StringType(cases[i].expected), observed.value()) <<
@@ -307,7 +307,7 @@
 #endif  // FILE_PATH_USES_WIN_SEPARATORS
   };
 
-  for (size_t i = 0; i < arraysize(cases); ++i) {
+  for (size_t i = 0; i < base::size(cases); ++i) {
     FilePath root(cases[i].inputs[0]);
     FilePath::StringType leaf(cases[i].inputs[1]);
     FilePath observed_str = root.Append(leaf);
@@ -386,7 +386,7 @@
 #endif  // FILE_PATH_USES_WIN_SEPARATORS
   };
 
-  for (size_t i = 0; i < arraysize(cases); ++i) {
+  for (size_t i = 0; i < base::size(cases); ++i) {
     FilePath input(cases[i].input);
     FilePath observed = input.StripTrailingSeparators();
     EXPECT_EQ(FilePath::StringType(cases[i].expected), observed.value()) <<
@@ -454,7 +454,7 @@
 #endif  // FILE_PATH_USES_WIN_SEPARATORS
   };
 
-  for (size_t i = 0; i < arraysize(cases); ++i) {
+  for (size_t i = 0; i < base::size(cases); ++i) {
     FilePath input(cases[i].input);
     bool observed = input.IsAbsolute();
     EXPECT_EQ(cases[i].expected, observed) <<
@@ -500,7 +500,7 @@
 #endif  // FILE_PATH_USES_WIN_SEPARATORS
   };
 
-  for (size_t i = 0; i < arraysize(cases); ++i) {
+  for (size_t i = 0; i < base::size(cases); ++i) {
     FilePath input(cases[i].input);
     std::vector<FilePath::StringType> comps;
     input.GetComponents(&comps);
@@ -559,7 +559,7 @@
 #endif  // FILE_PATH_USES_WIN_SEPARATORS
   };
 
-  for (size_t i = 0; i < arraysize(cases); ++i) {
+  for (size_t i = 0; i < base::size(cases); ++i) {
     FilePath parent(cases[i].inputs[0]);
     FilePath child(cases[i].inputs[1]);
 
@@ -623,7 +623,7 @@
 
   const FilePath base(FPL("blah"));
 
-  for (size_t i = 0; i < arraysize(cases); ++i) {
+  for (size_t i = 0; i < base::size(cases); ++i) {
     FilePath parent(cases[i].inputs[0]);
     FilePath child(cases[i].inputs[1]);
     {
@@ -690,7 +690,7 @@
 #endif  // FILE_PATH_USES_WIN_SEPARATORS
   };
 
-  for (size_t i = 0; i < arraysize(cases); ++i) {
+  for (size_t i = 0; i < base::size(cases); ++i) {
     FilePath a(cases[i].inputs[0]);
     FilePath b(cases[i].inputs[1]);
 
@@ -699,7 +699,7 @@
       b.value();
   }
 
-  for (size_t i = 0; i < arraysize(cases); ++i) {
+  for (size_t i = 0; i < base::size(cases); ++i) {
     FilePath a(cases[i].inputs[0]);
     FilePath b(cases[i].inputs[1]);
 
@@ -773,7 +773,7 @@
     { FPL("foo.user.js"),            FPL(".user.js") },
     { FPL("/foo.tar.bz"),            FPL(".tar.bz") },
   };
-  for (unsigned int i = 0; i < arraysize(cases); ++i) {
+  for (unsigned int i = 0; i < base::size(cases); ++i) {
     FilePath path(cases[i].input);
     FilePath::StringType extension = path.Extension();
     FilePath::StringType final_extension = path.FinalExtension();
@@ -782,7 +782,7 @@
     EXPECT_STREQ(cases[i].expected, final_extension.c_str())
         << "i: " << i << ", path: " << path.value();
   }
-  for (unsigned int i = 0; i < arraysize(double_extension_cases); ++i) {
+  for (unsigned int i = 0; i < base::size(double_extension_cases); ++i) {
     FilePath path(double_extension_cases[i].input);
     FilePath::StringType extension = path.Extension();
     EXPECT_STREQ(double_extension_cases[i].expected, extension.c_str())
@@ -850,7 +850,7 @@
     { { FPL("/bar/baz/foo.exe"), FPL(" (1)") },   FPL("/bar/baz/foo (1).exe") },
     { { FPL("/bar/baz/..////"), FPL(" (1)") },    FPL("") },
   };
-  for (unsigned int i = 0; i < arraysize(cases); ++i) {
+  for (unsigned int i = 0; i < base::size(cases); ++i) {
     FilePath path(cases[i].inputs[0]);
     FilePath result = path.InsertBeforeExtension(cases[i].inputs[1]);
     EXPECT_EQ(cases[i].expected, result.value()) << "i: " << i <<
@@ -877,7 +877,7 @@
     { FPL("/foo.bar/foo"),        FPL("/foo.bar/foo") },
     { FPL("/foo.bar/..////"),     FPL("/foo.bar/..////") },
   };
-  for (unsigned int i = 0; i < arraysize(cases); ++i) {
+  for (unsigned int i = 0; i < base::size(cases); ++i) {
     FilePath path(cases[i].input);
     FilePath removed = path.RemoveExtension();
     FilePath removed_final = path.RemoveFinalExtension();
@@ -926,7 +926,7 @@
     { { FPL("/foo.bar/foo"),        FPL("baz") }, FPL("/foo.bar/foo.baz") },
     { { FPL("/foo.bar/..////"),     FPL("baz") }, FPL("") },
   };
-  for (unsigned int i = 0; i < arraysize(cases); ++i) {
+  for (unsigned int i = 0; i < base::size(cases); ++i) {
     FilePath path(cases[i].inputs[0]);
     FilePath replaced = path.ReplaceExtension(cases[i].inputs[1]);
     EXPECT_EQ(cases[i].expected, replaced.value()) << "i: " << i <<
@@ -964,7 +964,7 @@
     { { FPL("/foo.bar/foo"),        FPL("baz") }, FPL("/foo.bar/foo.baz") },
     { { FPL("/foo.bar/..////"),     FPL("baz") }, FPL("") },
   };
-  for (unsigned int i = 0; i < arraysize(cases); ++i) {
+  for (unsigned int i = 0; i < base::size(cases); ++i) {
     FilePath path(cases[i].inputs[0]);
     FilePath added = path.AddExtension(cases[i].inputs[1]);
     EXPECT_EQ(cases[i].expected, added.value()) << "i: " << i <<
@@ -1008,7 +1008,7 @@
 #endif
   };
 
-  for (size_t i = 0; i < arraysize(cases); ++i) {
+  for (size_t i = 0; i < base::size(cases); ++i) {
     FilePath path(cases[i].inputs[0]);
     FilePath::StringType ext(cases[i].inputs[1]);
 
@@ -1085,7 +1085,7 @@
 #endif
   };
 
-  for (size_t i = 0; i < arraysize(cases); ++i) {
+  for (size_t i = 0; i < base::size(cases); ++i) {
     FilePath::StringType s1(cases[i].inputs[0]);
     FilePath::StringType s2(cases[i].inputs[1]);
     int result = FilePath::CompareIgnoreCase(s1, s2);
@@ -1120,7 +1120,7 @@
     { FPL("a/b/c"),    false },
   };
 
-  for (size_t i = 0; i < arraysize(cases); ++i) {
+  for (size_t i = 0; i < base::size(cases); ++i) {
     FilePath input(cases[i].input);
     bool observed = input.ReferencesParent();
     EXPECT_EQ(cases[i].expected, observed) <<
@@ -1142,7 +1142,7 @@
   ScopedLocale locale("en_US.UTF-8");
 #endif
 
-  for (size_t i = 0; i < arraysize(cases); ++i) {
+  for (size_t i = 0; i < base::size(cases); ++i) {
     // Test FromUTF8Unsafe() works.
     FilePath from_utf8 = FilePath::FromUTF8Unsafe(cases[i].utf8);
     EXPECT_EQ(cases[i].native, from_utf8.value())
@@ -1225,7 +1225,7 @@
     { FPL("foo/\\bar/\\"), FPL("foo\\\\bar\\\\") },
     { FPL("/\\foo\\/bar"), FPL("\\\\foo\\\\bar") },
   };
-  for (size_t i = 0; i < arraysize(cases); ++i) {
+  for (size_t i = 0; i < base::size(cases); ++i) {
     FilePath input(cases[i].input);
     FilePath observed = input.NormalizePathSeparators();
     EXPECT_EQ(FilePath::StringType(cases[i].expected), observed.value()) <<
@@ -1282,7 +1282,7 @@
     { FPL("content%2a%2f%2f"),     false },
   };
 
-  for (size_t i = 0; i < arraysize(cases); ++i) {
+  for (size_t i = 0; i < base::size(cases); ++i) {
     FilePath input(cases[i].input);
     bool observed = input.IsContentUri();
     EXPECT_EQ(cases[i].expected, observed) <<
diff --git a/base/files/file_path_watcher_fsevents.cc b/base/files/file_path_watcher_fsevents.cc
index 4441f08e..fe2a8af 100644
--- a/base/files/file_path_watcher_fsevents.cc
+++ b/base/files/file_path_watcher_fsevents.cc
@@ -13,6 +13,7 @@
 #include "base/lazy_instance.h"
 #include "base/logging.h"
 #include "base/mac/scoped_cftyperef.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/threading/scoped_blocking_call.h"
 #include "base/threading/sequenced_task_runner_handle.h"
@@ -221,9 +222,9 @@
       NULL, resolved_target_.DirName().value().c_str(),
       kCFStringEncodingMacHFS));
   CFStringRef paths_array[] = { cf_path.get(), cf_dir_path.get() };
-  ScopedCFTypeRef<CFArrayRef> watched_paths(CFArrayCreate(
-      NULL, reinterpret_cast<const void**>(paths_array), arraysize(paths_array),
-      &kCFTypeArrayCallBacks));
+  ScopedCFTypeRef<CFArrayRef> watched_paths(
+      CFArrayCreate(NULL, reinterpret_cast<const void**>(paths_array),
+                    base::size(paths_array), &kCFTypeArrayCallBacks));
 
   FSEventStreamContext context;
   context.version = 0;
diff --git a/base/files/file_proxy_unittest.cc b/base/files/file_proxy_unittest.cc
index 27db2f6f..7139bac 100644
--- a/base/files/file_proxy_unittest.cc
+++ b/base/files/file_proxy_unittest.cc
@@ -13,9 +13,9 @@
 #include "base/files/file.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
-#include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/run_loop.h"
+#include "base/stl_util.h"
 #include "base/test/scoped_task_environment.h"
 #include "base/threading/thread.h"
 #include "base/threading/thread_restrictions.h"
@@ -266,7 +266,7 @@
 TEST_F(FileProxyTest, Read) {
   // Setup.
   const char expected_data[] = "bleh";
-  int expected_bytes = arraysize(expected_data);
+  int expected_bytes = base::size(expected_data);
   ASSERT_EQ(expected_bytes,
             base::WriteFile(TestPath(), expected_data, expected_bytes));
 
@@ -291,7 +291,7 @@
   CreateProxy(File::FLAG_CREATE | File::FLAG_WRITE, &proxy);
 
   const char data[] = "foo!";
-  int data_bytes = arraysize(data);
+  int data_bytes = base::size(data);
   proxy.Write(0, data, data_bytes,
               BindOnce(&FileProxyTest::DidWrite, weak_factory_.GetWeakPtr()));
   RunLoop().Run();
diff --git a/base/files/file_util_posix.cc b/base/files/file_util_posix.cc
index eabdaf3..e56ac0a1 100644
--- a/base/files/file_util_posix.cc
+++ b/base/files/file_util_posix.cc
@@ -29,7 +29,6 @@
 #include "base/files/file_path.h"
 #include "base/files/scoped_file.h"
 #include "base/logging.h"
-#include "base/macros.h"
 #include "base/memory/singleton.h"
 #include "base/path_service.h"
 #include "base/posix/eintr_wrapper.h"
@@ -520,7 +519,8 @@
   DCHECK(!symlink_path.empty());
   DCHECK(target_path);
   char buf[PATH_MAX];
-  ssize_t count = ::readlink(symlink_path.value().c_str(), buf, arraysize(buf));
+  ssize_t count =
+      ::readlink(symlink_path.value().c_str(), buf, base::size(buf));
 
   if (count <= 0) {
     target_path->clear();
@@ -955,7 +955,7 @@
   ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
 
   std::set<gid_t> allowed_group_ids;
-  for (int i = 0, ie = arraysize(kAdminGroupNames); i < ie; ++i) {
+  for (int i = 0, ie = base::size(kAdminGroupNames); i < ie; ++i) {
     struct group *group_record = getgrnam(kAdminGroupNames[i]);
     if (!group_record) {
       DPLOG(ERROR) << "Could not get the group ID of group \""
diff --git a/base/files/file_util_unittest.cc b/base/files/file_util_unittest.cc
index 7ae651c..1670e320 100644
--- a/base/files/file_util_unittest.cc
+++ b/base/files/file_util_unittest.cc
@@ -27,8 +27,8 @@
 #include "base/files/scoped_file.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/guid.h"
-#include "base/macros.h"
 #include "base/path_service.h"
+#include "base/stl_util.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/multiprocess_test.h"
@@ -309,7 +309,7 @@
   std::wifstream file;
   file.open(filename.value().c_str());
   EXPECT_TRUE(file.is_open());
-  file.getline(contents, arraysize(contents));
+  file.getline(contents, base::size(contents));
   file.close();
   return std::wstring(contents);
 }
@@ -2347,7 +2347,7 @@
   ASSERT_EQ(0, ::_tdupenv_s(&original_tmp, &original_tmp_size, kTmpKey));
   // original_tmp may be NULL.
 
-  for (unsigned int i = 0; i < arraysize(kTmpValues); ++i) {
+  for (unsigned int i = 0; i < base::size(kTmpValues); ++i) {
     FilePath path;
     ::_tputenv_s(kTmpKey, kTmpValues[i]);
     GetTempDir(&path);
diff --git a/base/files/file_util_win.cc b/base/files/file_util_win.cc
index 71e5b7e..e4544698 100644
--- a/base/files/file_util_win.cc
+++ b/base/files/file_util_win.cc
@@ -22,10 +22,10 @@
 #include "base/files/file_path.h"
 #include "base/guid.h"
 #include "base/logging.h"
-#include "base/macros.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/process/process_handle.h"
 #include "base/rand_util.h"
+#include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
@@ -918,8 +918,7 @@
 
   wchar_t volume_path[MAX_PATH];
   if (!GetVolumePathNameW(path.NormalizePathSeparators().value().c_str(),
-                          volume_path,
-                          arraysize(volume_path))) {
+                          volume_path, base::size(volume_path))) {
     return -1;
   }
 
diff --git a/base/files/important_file_writer.cc b/base/files/important_file_writer.cc
index 9c887c9..de9be0ff 100644
--- a/base/files/important_file_writer.cc
+++ b/base/files/important_file_writer.cc
@@ -18,10 +18,10 @@
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/logging.h"
-#include "base/macros.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/numerics/safe_conversions.h"
+#include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/task_runner.h"
@@ -145,7 +145,7 @@
     char path[128];
   } file_info;
   file_info.data_size = data.size();
-  strlcpy(file_info.path, path.value().c_str(), arraysize(file_info.path));
+  strlcpy(file_info.path, path.value().c_str(), base::size(file_info.path));
   debug::Alias(&file_info);
 #endif
 
diff --git a/base/i18n/bidi_line_iterator_unittest.cc b/base/i18n/bidi_line_iterator_unittest.cc
index d531313..79d1a6d 100644
--- a/base/i18n/bidi_line_iterator_unittest.cc
+++ b/base/i18n/bidi_line_iterator_unittest.cc
@@ -4,7 +4,7 @@
 
 #include "base/i18n/bidi_line_iterator.h"
 
-#include "base/macros.h"
+#include "base/stl_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -167,17 +167,17 @@
       {44, UBIDI_RTL},
   };
 
-  ASSERT_EQ(arraysize(expected_runs),
+  ASSERT_EQ(base::size(expected_runs),
             static_cast<size_t>(iterator()->CountRuns()));
 
-  for (size_t i = 0; i < arraysize(expected_runs); ++i) {
+  for (size_t i = 0; i < base::size(expected_runs); ++i) {
     const auto& expected_run = expected_runs[i];
-    int expected_run_end = i >= arraysize(expected_runs) - 1
+    int expected_run_end = i >= base::size(expected_runs) - 1
                                ? kStringSize
                                : expected_runs[i + 1].start;
 
     size_t visual_index = GetParam() == TextDirection::RIGHT_TO_LEFT
-                              ? arraysize(expected_runs) - 1 - i
+                              ? base::size(expected_runs) - 1 - i
                               : i;
     int start, length;
     EXPECT_EQ(expected_run.dir,
diff --git a/base/i18n/break_iterator_unittest.cc b/base/i18n/break_iterator_unittest.cc
index 6e8632e..defc927 100644
--- a/base/i18n/break_iterator_unittest.cc
+++ b/base/i18n/break_iterator_unittest.cc
@@ -6,7 +6,7 @@
 
 #include <stddef.h>
 
-#include "base/macros.h"
+#include "base/stl_util.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
@@ -454,7 +454,7 @@
   }
   BreakIterator iter(text, BreakIterator::BREAK_CHARACTER);
   ASSERT_TRUE(iter.Init());
-  for (size_t i = 0; i < arraysize(kCharacters); ++i) {
+  for (size_t i = 0; i < base::size(kCharacters); ++i) {
     EXPECT_TRUE(iter.Advance());
     EXPECT_EQ(characters[i], iter.GetString());
   }
diff --git a/base/i18n/build_utf8_validator_tables.cc b/base/i18n/build_utf8_validator_tables.cc
index ab897d4..e096dec 100644
--- a/base/i18n/build_utf8_validator_tables.cc
+++ b/base/i18n/build_utf8_validator_tables.cc
@@ -41,8 +41,8 @@
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/logging.h"
-#include "base/macros.h"
 #include "base/numerics/safe_conversions.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "third_party/icu/source/common/unicode/utf8.h"
 
@@ -70,7 +70,8 @@
 const char kEpilog[] =
     "};\n"
     "\n"
-    "const size_t kUtf8ValidatorTablesSize = arraysize(kUtf8ValidatorTables);\n"
+    "const size_t kUtf8ValidatorTablesSize = "
+    "base::size(kUtf8ValidatorTables);\n"
     "\n"
     "}  // namespace internal\n"
     "}  // namespace base\n";
@@ -180,10 +181,10 @@
     uint8_t bytes[4];
     unsigned int offset = 0;
     UBool is_error = false;
-    U8_APPEND(bytes, offset, arraysize(bytes), i, is_error);
+    U8_APPEND(bytes, offset, base::size(bytes), i, is_error);
     DCHECK(!is_error);
     DCHECK_GT(offset, 0u);
-    DCHECK_LE(offset, arraysize(bytes));
+    DCHECK_LE(offset, base::size(bytes));
     Pair pair = {Character(bytes, bytes + offset), StringSet()};
     vector.push_back(pair);
   }
@@ -318,7 +319,7 @@
       {static_cast<uint8_t>(range.to() + 1), 1}};
   states->push_back(
       State(new_state_initializer,
-            new_state_initializer + arraysize(new_state_initializer)));
+            new_state_initializer + base::size(new_state_initializer)));
   const uint8_t new_state_number =
       base::checked_cast<uint8_t>(states->size() - 1);
   CHECK(state_map->insert(std::make_pair(set, new_state_number)).second);
@@ -350,10 +351,9 @@
       const StateRange new_range_initializer[] = {
           {range.from(), target_state},
           {static_cast<uint8_t>(range.to() + 1), 1}};
-      states[0]
-          .insert(states[0].end(),
-                  new_range_initializer,
-                  new_range_initializer + arraysize(new_range_initializer));
+      states[0].insert(
+          states[0].end(), new_range_initializer,
+          new_range_initializer + base::size(new_range_initializer));
     }
   }
   return states;
@@ -428,7 +428,7 @@
   settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
   logging::InitLogging(settings);
   if (base::CommandLine::ForCurrentProcess()->HasSwitch("help")) {
-    fwrite(kHelpText, 1, arraysize(kHelpText), stdout);
+    fwrite(kHelpText, 1, base::size(kHelpText), stdout);
     exit(EXIT_SUCCESS);
   }
   base::FilePath filename =
diff --git a/base/i18n/file_util_icu_unittest.cc b/base/i18n/file_util_icu_unittest.cc
index f2ec0b0..9d46434 100644
--- a/base/i18n/file_util_icu_unittest.cc
+++ b/base/i18n/file_util_icu_unittest.cc
@@ -7,7 +7,7 @@
 #include <stddef.h>
 
 #include "base/files/file_util.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -126,7 +126,7 @@
 };
 
 TEST_F(FileUtilICUTest, NormalizeFileNameEncoding) {
-  for (size_t i = 0; i < arraysize(kNormalizeFileNameEncodingTestCases); i++) {
+  for (size_t i = 0; i < base::size(kNormalizeFileNameEncodingTestCases); i++) {
     FilePath path(kNormalizeFileNameEncodingTestCases[i].original_path);
     NormalizeFileNameEncoding(&path);
     EXPECT_EQ(FilePath(kNormalizeFileNameEncodingTestCases[i].normalized_path),
diff --git a/base/i18n/icu_string_conversions_unittest.cc b/base/i18n/icu_string_conversions_unittest.cc
index d155986..3b38743 100644
--- a/base/i18n/icu_string_conversions_unittest.cc
+++ b/base/i18n/icu_string_conversions_unittest.cc
@@ -12,7 +12,7 @@
 #include "base/format_macros.h"
 #include "base/i18n/icu_string_conversions.h"
 #include "base/logging.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
@@ -163,7 +163,7 @@
 };
 
 TEST(ICUStringConversionsTest, ConvertBetweenCodepageAndUTF16) {
-  for (size_t i = 0; i < arraysize(kConvertCodepageCases); ++i) {
+  for (size_t i = 0; i < base::size(kConvertCodepageCases); ++i) {
     SCOPED_TRACE(base::StringPrintf(
                      "Test[%" PRIuS "]: <encoded: %s> <codepage: %s>", i,
                      kConvertCodepageCases[i].encoded,
@@ -218,7 +218,7 @@
 };
 TEST(ICUStringConversionsTest, ConvertToUtf8AndNormalize) {
   std::string result;
-  for (size_t i = 0; i < arraysize(kConvertAndNormalizeCases); ++i) {
+  for (size_t i = 0; i < base::size(kConvertAndNormalizeCases); ++i) {
     SCOPED_TRACE(base::StringPrintf(
                      "Test[%" PRIuS "]: <encoded: %s> <codepage: %s>", i,
                      kConvertAndNormalizeCases[i].encoded,
diff --git a/base/i18n/rtl.cc b/base/i18n/rtl.cc
index 8951b328..5db763e 100644
--- a/base/i18n/rtl.cc
+++ b/base/i18n/rtl.cc
@@ -13,7 +13,7 @@
 #include "base/files/file_path.h"
 #include "base/i18n/base_i18n_switches.h"
 #include "base/logging.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/strings/sys_string_conversions.h"
@@ -203,7 +203,7 @@
       SplitStringPiece(locale_name, "-_", KEEP_WHITESPACE, SPLIT_WANT_ALL);
   const StringPiece& language_code = locale_split[0];
   if (std::binary_search(kRTLLanguageCodes,
-                         kRTLLanguageCodes + arraysize(kRTLLanguageCodes),
+                         kRTLLanguageCodes + base::size(kRTLLanguageCodes),
                          language_code))
     return RIGHT_TO_LEFT;
   return LEFT_TO_RIGHT;
diff --git a/base/i18n/streaming_utf8_validator_unittest.cc b/base/i18n/streaming_utf8_validator_unittest.cc
index 8193a026..0742accb 100644
--- a/base/i18n/streaming_utf8_validator_unittest.cc
+++ b/base/i18n/streaming_utf8_validator_unittest.cc
@@ -11,7 +11,7 @@
 
 #include <string>
 
-#include "base/macros.h"
+#include "base/stl_util.h"
 #include "base/strings/string_piece.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -143,7 +143,7 @@
                              "\xef\xbb\xbf",  // UTF-8 BOM
 };
 
-const char* const* const valid_end = valid + arraysize(valid);
+const char* const* const valid_end = valid + base::size(valid);
 
 const char* const invalid[] = {
     // always invalid bytes
@@ -176,7 +176,7 @@
     "\xfe\xff", "\xff\xfe",
 };
 
-const char* const* const invalid_end = invalid + arraysize(invalid);
+const char* const* const invalid_end = invalid + base::size(invalid);
 
 // A ForwardIterator which returns all the non-empty prefixes of the elements of
 // "valid".
@@ -189,7 +189,7 @@
   // This is a value type; the default copy constructor and assignment operator
   // generated by the compiler are used.
 
-  static PartialIterator end() { return PartialIterator(arraysize(valid), 1); }
+  static PartialIterator end() { return PartialIterator(base::size(valid), 1); }
 
   PartialIterator& operator++() {
     Advance();
@@ -212,9 +212,9 @@
       : index_(index), prefix_length_(prefix_length) {}
 
   void Advance() {
-    if (index_ < arraysize(valid) && prefix_length_ < strlen(valid[index_]))
+    if (index_ < base::size(valid) && prefix_length_ < strlen(valid[index_]))
       ++prefix_length_;
-    while (index_ < arraysize(valid) &&
+    while (index_ < base::size(valid) &&
            prefix_length_ == strlen(valid[index_])) {
       ++index_;
       prefix_length_ = 1;
diff --git a/base/ios/ios_util.mm b/base/ios/ios_util.mm
index 08cce7e..608a8830 100644
--- a/base/ios/ios_util.mm
+++ b/base/ios/ios_util.mm
@@ -7,7 +7,7 @@
 #import <Foundation/Foundation.h>
 #include <stddef.h>
 
-#include "base/macros.h"
+#include "base/stl_util.h"
 #include "base/system/sys_info.h"
 
 namespace {
@@ -46,7 +46,7 @@
 bool IsRunningOnOrLater(int32_t major, int32_t minor, int32_t bug_fix) {
   static const int32_t* current_version = OSVersionAsArray();
   int32_t version[] = {major, minor, bug_fix};
-  for (size_t i = 0; i < arraysize(version); i++) {
+  for (size_t i = 0; i < base::size(version); i++) {
     if (current_version[i] != version[i])
       return current_version[i] > version[i];
   }
diff --git a/base/json/json_parser_unittest.cc b/base/json/json_parser_unittest.cc
index 0da3db84..c8ecc21 100644
--- a/base/json/json_parser_unittest.cc
+++ b/base/json/json_parser_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/json/json_reader.h"
 #include "base/memory/ptr_util.h"
 #include "base/optional.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/values.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -401,7 +402,7 @@
       // clang-format on
   };
 
-  for (unsigned int i = 0; i < arraysize(kCases); ++i) {
+  for (unsigned int i = 0; i < base::size(kCases); ++i) {
     auto test_case = kCases[i];
     SCOPED_TRACE(StringPrintf("case %u: \"%s\"", i, test_case.input));
 
@@ -447,7 +448,7 @@
       // clang-format on
   };
 
-  for (unsigned int i = 0; i < arraysize(kCases); ++i) {
+  for (unsigned int i = 0; i < base::size(kCases); ++i) {
     auto* test_case = kCases[i];
     SCOPED_TRACE(StringPrintf("case %u: \"%s\"", i, test_case));
 
diff --git a/base/json/json_reader_unittest.cc b/base/json/json_reader_unittest.cc
index 036ea78..c74aa0b9 100644
--- a/base/json/json_reader_unittest.cc
+++ b/base/json/json_reader_unittest.cc
@@ -11,8 +11,8 @@
 #include "base/base_paths.h"
 #include "base/files/file_util.h"
 #include "base/logging.h"
-#include "base/macros.h"
 #include "base/path_service.h"
+#include "base/stl_util.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
@@ -636,7 +636,7 @@
       "/* test *", "{\"foo\"", "{\"foo\":", "  [", "\"\\u123g\"", "{\n\"eh:\n}",
   };
 
-  for (size_t i = 0; i < arraysize(kInvalidJson); ++i) {
+  for (size_t i = 0; i < base::size(kInvalidJson); ++i) {
     JSONReader reader;
     LOG(INFO) << "Sanity test " << i << ": <" << kInvalidJson[i] << ">";
     EXPECT_FALSE(reader.ReadToValue(kInvalidJson[i]));
diff --git a/base/json/string_escape_unittest.cc b/base/json/string_escape_unittest.cc
index 0c44339..cf43af1 100644
--- a/base/json/string_escape_unittest.cc
+++ b/base/json/string_escape_unittest.cc
@@ -6,7 +6,7 @@
 
 #include <stddef.h>
 
-#include "base/macros.h"
+#include "base/stl_util.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -180,7 +180,7 @@
   }
 
   const char kEmbedNull[] = { '\xab', '\x39', '\0', '\x9f', '\xab' };
-  std::string in(kEmbedNull, arraysize(kEmbedNull));
+  std::string in(kEmbedNull, base::size(kEmbedNull));
   EXPECT_FALSE(IsStringUTF8(in));
   EXPECT_EQ(std::string("\\u00AB9\\u0000\\u009F\\u00AB"),
             EscapeBytesAsInvalidJSONString(in, false));
diff --git a/base/logging.cc b/base/logging.cc
index d649dc5..d473555 100644
--- a/base/logging.cc
+++ b/base/logging.cc
@@ -7,7 +7,6 @@
 #include <limits.h>
 #include <stdint.h>
 
-#include "base/macros.h"
 #include "base/stl_util.h"
 #include "build/build_config.h"
 
@@ -117,7 +116,7 @@
 VlogInfo* g_vlog_info_prev = nullptr;
 
 const char* const log_severity_names[] = {"INFO", "WARNING", "ERROR", "FATAL"};
-static_assert(LOG_NUM_SEVERITIES == arraysize(log_severity_names),
+static_assert(LOG_NUM_SEVERITIES == base::size(log_severity_names),
               "Incorrect number of log_severity_names");
 
 const char* log_severity_name(int severity) {
@@ -336,9 +335,9 @@
       // try the current directory
       wchar_t system_buffer[MAX_PATH];
       system_buffer[0] = 0;
-      DWORD len = ::GetCurrentDirectory(arraysize(system_buffer),
-                                        system_buffer);
-      if (len == 0 || len > arraysize(system_buffer))
+      DWORD len =
+          ::GetCurrentDirectory(base::size(system_buffer), system_buffer);
+      if (len == 0 || len > base::size(system_buffer))
         return false;
 
       *g_log_file_name = system_buffer;
@@ -689,7 +688,7 @@
       // By default, messages are only readable by the admin group. Explicitly
       // make them readable by the user generating the messages.
       char euid_string[12];
-      snprintf(euid_string, arraysize(euid_string), "%d", geteuid());
+      snprintf(euid_string, base::size(euid_string), "%d", geteuid());
       asl_set(asl_message.get(), ASL_KEY_READ_UID, euid_string);
 
       // Map Chrome log severities to ASL log levels.
@@ -964,7 +963,7 @@
   char msgbuf[kErrorMessageBufferSize];
   DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
   DWORD len = FormatMessageA(flags, nullptr, error_code, 0, msgbuf,
-                             arraysize(msgbuf), nullptr);
+                             base::size(msgbuf), nullptr);
   if (len) {
     // Messages returned by system end with line breaks.
     return base::CollapseWhitespaceASCII(msgbuf, true) +
diff --git a/base/mac/authorization_util.mm b/base/mac/authorization_util.mm
index a3bc4f9..cc5487b2 100644
--- a/base/mac/authorization_util.mm
+++ b/base/mac/authorization_util.mm
@@ -15,8 +15,8 @@
 #include "base/mac/foundation_util.h"
 #include "base/mac/mac_logging.h"
 #include "base/mac/scoped_authorizationref.h"
-#include "base/macros.h"
 #include "base/posix/eintr_wrapper.h"
+#include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 
@@ -62,7 +62,7 @@
     {kAuthorizationEnvironmentPrompt, prompt_length, (void*)prompt_c, 0}
   };
 
-  AuthorizationEnvironment environment = {arraysize(environment_items),
+  AuthorizationEnvironment environment = {base::size(environment_items),
                                           environment_items};
 
   status = AuthorizationCopyRights(authorization,
@@ -87,7 +87,7 @@
   AuthorizationItem right_items[] = {
     {kAuthorizationRightExecute, 0, NULL, 0}
   };
-  AuthorizationRights rights = {arraysize(right_items), right_items};
+  AuthorizationRights rights = {base::size(right_items), right_items};
 
   return GetAuthorizationRightsWithPrompt(&rights, prompt, 0);
 }
diff --git a/base/mac/foundation_util.mm b/base/mac/foundation_util.mm
index 15fc15b..38954c895 100644
--- a/base/mac/foundation_util.mm
+++ b/base/mac/foundation_util.mm
@@ -12,8 +12,8 @@
 #include "base/logging.h"
 #include "base/mac/bundle_locations.h"
 #include "base/mac/mac_logging.h"
-#include "base/macros.h"
 #include "base/numerics/safe_conversions.h"
+#include "base/stl_util.h"
 #include "base/strings/sys_string_conversions.h"
 #include "build/build_config.h"
 
@@ -149,7 +149,7 @@
 //   returns - path to the application bundle, or empty on error
 FilePath GetAppBundlePath(const FilePath& exec_name) {
   const char kExt[] = ".app";
-  const size_t kExtLength = arraysize(kExt) - 1;
+  const size_t kExtLength = base::size(kExt) - 1;
 
   // Split the path into components.
   std::vector<std::string> components;
diff --git a/base/mac/foundation_util_unittest.mm b/base/mac/foundation_util_unittest.mm
index a584094..5214354 100644
--- a/base/mac/foundation_util_unittest.mm
+++ b/base/mac/foundation_util_unittest.mm
@@ -12,7 +12,7 @@
 #include "base/format_macros.h"
 #include "base/mac/scoped_cftyperef.h"
 #include "base/mac/scoped_nsautorelease_pool.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -283,16 +283,13 @@
   CFStringRef keys[] = { CFSTR("one"), CFSTR("two"), CFSTR("three") };
   CFNumberRef values[] = { cf_one, cf_two, cf_three };
 
-  static_assert(arraysize(keys) == arraysize(values),
+  static_assert(base::size(keys) == base::size(values),
                 "keys and values arrays must have the same size");
 
-  ScopedCFTypeRef<CFDictionaryRef> test_dict(
-      CFDictionaryCreate(kCFAllocatorDefault,
-                         reinterpret_cast<const void**>(keys),
-                         reinterpret_cast<const void**>(values),
-                         arraysize(values),
-                         &kCFCopyStringDictionaryKeyCallBacks,
-                         &kCFTypeDictionaryValueCallBacks));
+  ScopedCFTypeRef<CFDictionaryRef> test_dict(CFDictionaryCreate(
+      kCFAllocatorDefault, reinterpret_cast<const void**>(keys),
+      reinterpret_cast<const void**>(values), base::size(values),
+      &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
 
   // GetValueFromDictionary<>(_, _) should produce the correct
   // expected output.
@@ -369,7 +366,7 @@
 #endif  // !defined(ARCH_CPU_64_BITS)
   };
 
-  for (size_t i = 0; i < arraysize(nsinteger_cases); ++i) {
+  for (size_t i = 0; i < base::size(nsinteger_cases); ++i) {
     EXPECT_EQ(nsinteger_cases[i].expected,
               StringPrintf("%" PRIdNS, nsinteger_cases[i].value));
     EXPECT_EQ(nsinteger_cases[i].expected_hex,
@@ -393,7 +390,7 @@
 #endif  // !defined(ARCH_CPU_64_BITS)
   };
 
-  for (size_t i = 0; i < arraysize(nsuinteger_cases); ++i) {
+  for (size_t i = 0; i < base::size(nsuinteger_cases); ++i) {
     EXPECT_EQ(nsuinteger_cases[i].expected,
               StringPrintf("%" PRIuNS, nsuinteger_cases[i].value));
     EXPECT_EQ(nsuinteger_cases[i].expected_hex,
diff --git a/base/mac/mac_util_unittest.mm b/base/mac/mac_util_unittest.mm
index 19faa09..13229c62 100644
--- a/base/mac/mac_util_unittest.mm
+++ b/base/mac/mac_util_unittest.mm
@@ -14,7 +14,7 @@
 #include "base/mac/foundation_util.h"
 #include "base/mac/scoped_cftyperef.h"
 #include "base/mac/scoped_nsobject.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
 #include "base/system/sys_info.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
@@ -63,7 +63,7 @@
     "/", "/foo", "foo", "/foo/bar.", "foo/bar.", "/foo/bar./bazquux",
     "foo/bar./bazquux", "foo/.app", "//foo",
   };
-  for (size_t i = 0; i < arraysize(invalid_inputs); i++) {
+  for (size_t i = 0; i < base::size(invalid_inputs); i++) {
     out = GetAppBundlePath(FilePath(invalid_inputs[i]));
     EXPECT_TRUE(out.empty()) << "loop: " << i;
   }
@@ -87,7 +87,7 @@
     { "/Applications/Google Foo.app/bar/Foo Helper.app/quux/Foo Helper",
         "/Applications/Google Foo.app" },
   };
-  for (size_t i = 0; i < arraysize(valid_inputs); i++) {
+  for (size_t i = 0; i < base::size(valid_inputs); i++) {
     out = GetAppBundlePath(FilePath(valid_inputs[i].in));
     EXPECT_FALSE(out.empty()) << "loop: " << i;
     EXPECT_STREQ(valid_inputs[i].expected_out,
@@ -103,8 +103,8 @@
   FilePath dummy_file_path = temp_dir_.GetPath().Append("DummyFile");
   const char dummy_data[] = "All your base are belong to us!";
   // Dump something real into the file.
-  ASSERT_EQ(static_cast<int>(arraysize(dummy_data)),
-            WriteFile(dummy_file_path, dummy_data, arraysize(dummy_data)));
+  ASSERT_EQ(static_cast<int>(base::size(dummy_data)),
+            WriteFile(dummy_file_path, dummy_data, base::size(dummy_data)));
   NSString* fileURLString = base::mac::FilePathToNSString(dummy_file_path);
   NSURL* fileURL = [NSURL URLWithString:fileURLString];
   // Initial state should be non-excluded.
diff --git a/base/mac/os_crash_dumps.cc b/base/mac/os_crash_dumps.cc
index 95af009..182d5d9 100644
--- a/base/mac/os_crash_dumps.cc
+++ b/base/mac/os_crash_dumps.cc
@@ -9,7 +9,7 @@
 #include <unistd.h>
 
 #include "base/logging.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
 
 namespace base {
 namespace mac {
@@ -42,7 +42,7 @@
   };
 
   // For all these signals, just wire things up so we exit immediately.
-  for (size_t i = 0; i < arraysize(signals_to_intercept); ++i) {
+  for (size_t i = 0; i < base::size(signals_to_intercept); ++i) {
     struct sigaction act = {};
     act.sa_handler = ExitSignalHandler;
 
diff --git a/base/memory/ref_counted_memory_unittest.cc b/base/memory/ref_counted_memory_unittest.cc
index b7498f9..d799517 100644
--- a/base/memory/ref_counted_memory_unittest.cc
+++ b/base/memory/ref_counted_memory_unittest.cc
@@ -9,6 +9,7 @@
 #include <utility>
 
 #include "base/memory/read_only_shared_memory_region.h"
+#include "base/stl_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -39,7 +40,7 @@
   scoped_refptr<RefCountedMemory> mem2;
   {
     const unsigned char kData[] = {12, 11, 99};
-    mem2 = MakeRefCounted<RefCountedBytes>(kData, arraysize(kData));
+    mem2 = MakeRefCounted<RefCountedBytes>(kData, base::size(kData));
   }
   ASSERT_EQ(3U, mem2->size());
   EXPECT_EQ(12U, mem2->front()[0]);
diff --git a/base/memory/shared_memory_helper.cc b/base/memory/shared_memory_helper.cc
index 363a6dc..47dfeb0 100644
--- a/base/memory/shared_memory_helper.cc
+++ b/base/memory/shared_memory_helper.cc
@@ -11,6 +11,7 @@
 #include "base/debug/alias.h"
 #endif  // defined(OS_CHROMEOS)
 
+#include "base/stl_util.h"
 #include "base/threading/thread_restrictions.h"
 
 namespace base {
@@ -129,16 +130,16 @@
       // begins.
       crash_ptr = strncpy(crash_ptr, kFileDataMarker, strlen(kFileDataMarker));
       for (int i = original_fd_limit; i >= 0; --i) {
-        memset(buf, 0, arraysize(buf));
-        memset(fd_path, 0, arraysize(fd_path));
-        snprintf(fd_path, arraysize(fd_path) - 1, "/proc/self/fd/%d", i);
-        ssize_t count = readlink(fd_path, buf, arraysize(buf) - 1);
+        memset(buf, 0, base::size(buf));
+        memset(fd_path, 0, base::size(fd_path));
+        snprintf(fd_path, base::size(fd_path) - 1, "/proc/self/fd/%d", i);
+        ssize_t count = readlink(fd_path, buf, base::size(buf) - 1);
         if (count < 0) {
           PLOG(ERROR) << "readlink failed for: " << fd_path;
           continue;
         }
 
-        if (crash_ptr + count + 1 < crash_buffer + arraysize(crash_buffer)) {
+        if (crash_ptr + count + 1 < crash_buffer + base::size(crash_buffer)) {
           crash_ptr = strncpy(crash_ptr, buf, count + 1);
         }
         LOG(ERROR) << i << ": " << buf;
diff --git a/base/memory/shared_memory_unittest.cc b/base/memory/shared_memory_unittest.cc
index fc941dd..b8ad161a 100644
--- a/base/memory/shared_memory_unittest.cc
+++ b/base/memory/shared_memory_unittest.cc
@@ -14,10 +14,10 @@
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/logging.h"
-#include "base/macros.h"
 #include "base/memory/shared_memory_handle.h"
 #include "base/process/kill.h"
 #include "base/rand_util.h"
+#include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
@@ -723,7 +723,7 @@
 TEST_P(SharedMemoryTest, UnsafeImageSection) {
   const char kTestSectionName[] = "UnsafeImageSection";
   wchar_t path[MAX_PATH];
-  EXPECT_GT(::GetModuleFileName(nullptr, path, arraysize(path)), 0U);
+  EXPECT_GT(::GetModuleFileName(nullptr, path, base::size(path)), 0U);
 
   // Map the current executable image to save us creating a new PE file on disk.
   base::win::ScopedHandle file_handle(::CreateFile(
diff --git a/base/message_loop/message_pump_mac.mm b/base/message_loop/message_pump_mac.mm
index 384533dd..2f53a79c 100644
--- a/base/message_loop/message_pump_mac.mm
+++ b/base/message_loop/message_pump_mac.mm
@@ -13,9 +13,9 @@
 #include "base/logging.h"
 #include "base/mac/call_with_eh_frame.h"
 #include "base/mac/scoped_cftyperef.h"
-#include "base/macros.h"
 #include "base/message_loop/timer_slack.h"
 #include "base/run_loop.h"
+#include "base/stl_util.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
 
@@ -157,7 +157,7 @@
         // Process work when AppKit is highlighting an item on the main menubar.
         CFSTR("NSUnhighlightMenuRunLoopMode"),
     };
-    static_assert(arraysize(modes) == kNumModes, "mode size mismatch");
+    static_assert(base::size(modes) == kNumModes, "mode size mismatch");
     static_assert((1 << kNumModes) - 1 == kAllModesMask,
                   "kAllModesMask not large enough");
 
diff --git a/base/metrics/field_trial_unittest.cc b/base/metrics/field_trial_unittest.cc
index dce2dd6..1240c65 100644
--- a/base/metrics/field_trial_unittest.cc
+++ b/base/metrics/field_trial_unittest.cc
@@ -9,11 +9,11 @@
 #include "base/base_switches.h"
 #include "base/build_time.h"
 #include "base/feature_list.h"
-#include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/field_trial_param_associator.h"
 #include "base/rand_util.h"
 #include "base/run_loop.h"
+#include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/gtest_util.h"
@@ -1102,7 +1102,7 @@
     { 0.95, kDefaultGroupName },
   };
 
-  for (size_t i = 0; i < arraysize(test_cases); ++i) {
+  for (size_t i = 0; i < base::size(test_cases); ++i) {
     TestFieldTrialObserver observer(TestFieldTrialObserver::ASYNCHRONOUS);
     scoped_refptr<FieldTrial> trial(
        FieldTrial::CreateSimulatedFieldTrial(kTrialName, 100, kDefaultGroupName,
diff --git a/base/metrics/metrics_hashes_unittest.cc b/base/metrics/metrics_hashes_unittest.cc
index aea254e..ec3446f2 100644
--- a/base/metrics/metrics_hashes_unittest.cc
+++ b/base/metrics/metrics_hashes_unittest.cc
@@ -8,7 +8,7 @@
 #include <stdint.h>
 
 #include "base/format_macros.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -25,7 +25,7 @@
     {"NewTab", "0x290eb683f96572f1"},
   };
 
-  for (size_t i = 0; i < arraysize(cases); ++i) {
+  for (size_t i = 0; i < base::size(cases); ++i) {
     uint64_t hash = HashMetricName(cases[i].input);
     std::string hash_hex = base::StringPrintf("0x%016" PRIx64, hash);
     EXPECT_EQ(cases[i].output, hash_hex);
diff --git a/base/metrics/sparse_histogram_unittest.cc b/base/metrics/sparse_histogram_unittest.cc
index 72dd9054..7271cf4 100644
--- a/base/metrics/sparse_histogram_unittest.cc
+++ b/base/metrics/sparse_histogram_unittest.cc
@@ -16,6 +16,7 @@
 #include "base/metrics/sample_map.h"
 #include "base/metrics/statistics_recorder.h"
 #include "base/pickle.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -354,7 +355,7 @@
       {2147483647, 2147483648LL},
   };
 
-  for (size_t i = 0; i < arraysize(cases); ++i) {
+  for (size_t i = 0; i < base::size(cases); ++i) {
     HistogramBase* histogram =
         SparseHistogram::FactoryGet(StringPrintf("ExtremeValues_%zu", i),
                                     HistogramBase::kUmaTargetedHistogramFlag);
diff --git a/base/pickle_unittest.cc b/base/pickle_unittest.cc
index 4563047..4bf5483 100644
--- a/base/pickle_unittest.cc
+++ b/base/pickle_unittest.cc
@@ -11,7 +11,7 @@
 #include <memory>
 #include <string>
 
-#include "base/macros.h"
+#include "base/stl_util.h"
 #include "base/strings/string16.h"
 #include "base/strings/utf_string_conversions.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -37,7 +37,7 @@
 // Test raw char16 writing, assumes UTF16 encoding is ANSI for alpha chars.
 const char16 testrawstring16[] = {'A', 'l', 'o', 'h', 'a', 0};
 const char testdata[] = "AAA\0BBB\0";
-const int testdatalen = arraysize(testdata) - 1;
+const int testdatalen = base::size(testdata) - 1;
 
 // checks that the results can be read correctly from the Pickle
 void VerifyResult(const Pickle& pickle) {
diff --git a/base/process/memory_win.cc b/base/process/memory_win.cc
index 06bf073c..4fff911 100644
--- a/base/process/memory_win.cc
+++ b/base/process/memory_win.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/process/memory.h"
+#include "base/stl_util.h"
 
 #include <windows.h>  // Must be in front of other Windows header files.
 
@@ -49,7 +50,7 @@
   // Pass the size of the failed request in an exception argument.
   ULONG_PTR exception_args[] = {size};
   ::RaiseException(win::kOomExceptionCode, EXCEPTION_NONCONTINUABLE,
-                   arraysize(exception_args), exception_args);
+                   base::size(exception_args), exception_args);
 
   // Safety check, make sure process exits here.
   _exit(win::kOomExceptionCode);
diff --git a/base/process/process_handle_freebsd.cc b/base/process/process_handle_freebsd.cc
index 192d72b..b56bf89 100644
--- a/base/process/process_handle_freebsd.cc
+++ b/base/process/process_handle_freebsd.cc
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/macros.h"
 #include "base/process/process_handle.h"
+#include "base/stl_util.h"
 
 #include <limits.h>
 #include <stddef.h>
@@ -19,7 +19,7 @@
   size_t length;
   int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process };
 
-  if (sysctl(mib, arraysize(mib), &info, &length, NULL, 0) < 0)
+  if (sysctl(mib, base::size(mib), &info, &length, NULL, 0) < 0)
     return -1;
 
   return info.ki_ppid;
@@ -32,7 +32,7 @@
 
   length = sizeof(pathname);
 
-  if (sysctl(mib, arraysize(mib), pathname, &length, NULL, 0) < 0 ||
+  if (sysctl(mib, base::size(mib), pathname, &length, NULL, 0) < 0 ||
       length == 0) {
     return FilePath();
   }
diff --git a/base/process/process_handle_openbsd.cc b/base/process/process_handle_openbsd.cc
index 045e720..e14acca 100644
--- a/base/process/process_handle_openbsd.cc
+++ b/base/process/process_handle_openbsd.cc
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/macros.h"
 #include "base/process/process_handle.h"
+#include "base/stl_util.h"
 
 #include <stddef.h>
 #include <sys/sysctl.h>
@@ -18,12 +18,12 @@
   int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process,
                 sizeof(struct kinfo_proc), 0 };
 
-  if (sysctl(mib, arraysize(mib), NULL, &length, NULL, 0) < 0)
+  if (sysctl(mib, base::size(mib), NULL, &length, NULL, 0) < 0)
     return -1;
 
   mib[5] = (length / sizeof(struct kinfo_proc));
 
-  if (sysctl(mib, arraysize(mib), &info, &length, NULL, 0) < 0)
+  if (sysctl(mib, base::size(mib), &info, &length, NULL, 0) < 0)
     return -1;
 
   return info.p_ppid;
@@ -35,10 +35,10 @@
   int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process,
                 sizeof(struct kinfo_proc), 0 };
 
-  if (sysctl(mib, arraysize(mib), NULL, &len, NULL, 0) == -1)
+  if (sysctl(mib, base::size(mib), NULL, &len, NULL, 0) == -1)
     return FilePath();
   mib[5] = (len / sizeof(struct kinfo_proc));
-  if (sysctl(mib, arraysize(mib), &kp, &len, NULL, 0) < 0)
+  if (sysctl(mib, base::size(mib), &kp, &len, NULL, 0) < 0)
     return FilePath();
   if ((kp.p_flag & P_SYSTEM) != 0)
     return FilePath();
diff --git a/base/process/process_iterator_freebsd.cc b/base/process/process_iterator_freebsd.cc
index 4df0d90e..e6300e1d 100644
--- a/base/process/process_iterator_freebsd.cc
+++ b/base/process/process_iterator_freebsd.cc
@@ -11,7 +11,7 @@
 #include <unistd.h>
 
 #include "base/logging.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 
@@ -29,7 +29,7 @@
 
   do {
     size_t len = 0;
-    if (sysctl(mib, arraysize(mib), NULL, &len, NULL, 0) < 0) {
+    if (sysctl(mib, base::size(mib), NULL, &len, NULL, 0) < 0) {
       LOG(ERROR) << "failed to get the size needed for the process list";
       kinfo_procs_.resize(0);
       done = true;
@@ -40,7 +40,7 @@
       num_of_kinfo_proc += 16;
       kinfo_procs_.resize(num_of_kinfo_proc);
       len = num_of_kinfo_proc * sizeof(struct kinfo_proc);
-      if (sysctl(mib, arraysize(mib), &kinfo_procs_[0], &len, NULL, 0) <0) {
+      if (sysctl(mib, base::size(mib), &kinfo_procs_[0], &len, NULL, 0) < 0) {
         // If we get a mem error, it just means we need a bigger buffer, so
         // loop around again.  Anything else is a real error and give up.
         if (errno != ENOMEM) {
@@ -78,14 +78,14 @@
       continue;
 
     length = 0;
-    if (sysctl(mib, arraysize(mib), NULL, &length, NULL, 0) < 0) {
+    if (sysctl(mib, base::size(mib), NULL, &length, NULL, 0) < 0) {
       LOG(ERROR) << "failed to figure out the buffer size for a command line";
       continue;
     }
 
     data.resize(length);
 
-    if (sysctl(mib, arraysize(mib), &data[0], &length, NULL, 0) < 0) {
+    if (sysctl(mib, base::size(mib), &data[0], &length, NULL, 0) < 0) {
       LOG(ERROR) << "failed to fetch a commandline";
       continue;
     }
diff --git a/base/process/process_iterator_mac.cc b/base/process/process_iterator_mac.cc
index f33121a..c57797c 100644
--- a/base/process/process_iterator_mac.cc
+++ b/base/process/process_iterator_mac.cc
@@ -11,7 +11,7 @@
 #include <unistd.h>
 
 #include "base/logging.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 
@@ -35,7 +35,7 @@
   do {
     // Get the size of the buffer
     size_t len = 0;
-    if (sysctl(mib, arraysize(mib), NULL, &len, NULL, 0) < 0) {
+    if (sysctl(mib, base::size(mib), NULL, &len, NULL, 0) < 0) {
       DLOG(ERROR) << "failed to get the size needed for the process list";
       kinfo_procs_.resize(0);
       done = true;
@@ -47,7 +47,7 @@
       kinfo_procs_.resize(num_of_kinfo_proc);
       len = num_of_kinfo_proc * sizeof(struct kinfo_proc);
       // Load the list of processes
-      if (sysctl(mib, arraysize(mib), &kinfo_procs_[0], &len, NULL, 0) < 0) {
+      if (sysctl(mib, base::size(mib), &kinfo_procs_[0], &len, NULL, 0) < 0) {
         // If we get a mem error, it just means we need a bigger buffer, so
         // loop around again.  Anything else is a real error and give up.
         if (errno != ENOMEM) {
@@ -85,13 +85,13 @@
 
     // Find out what size buffer we need.
     size_t data_len = 0;
-    if (sysctl(mib, arraysize(mib), NULL, &data_len, NULL, 0) < 0) {
+    if (sysctl(mib, base::size(mib), NULL, &data_len, NULL, 0) < 0) {
       DVPLOG(1) << "failed to figure out the buffer size for a commandline";
       continue;
     }
 
     data.resize(data_len);
-    if (sysctl(mib, arraysize(mib), &data[0], &data_len, NULL, 0) < 0) {
+    if (sysctl(mib, base::size(mib), &data[0], &data_len, NULL, 0) < 0) {
       DVPLOG(1) << "failed to fetch a commandline";
       continue;
     }
diff --git a/base/process/process_iterator_openbsd.cc b/base/process/process_iterator_openbsd.cc
index 74306c0..ad2168d 100644
--- a/base/process/process_iterator_openbsd.cc
+++ b/base/process/process_iterator_openbsd.cc
@@ -9,7 +9,7 @@
 #include <sys/sysctl.h>
 
 #include "base/logging.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 
@@ -28,7 +28,7 @@
 
   do {
     size_t len = 0;
-    if (sysctl(mib, arraysize(mib), NULL, &len, NULL, 0) < 0) {
+    if (sysctl(mib, base::size(mib), NULL, &len, NULL, 0) < 0) {
       DLOG(ERROR) << "failed to get the size needed for the process list";
       kinfo_procs_.resize(0);
       done = true;
@@ -39,7 +39,7 @@
       num_of_kinfo_proc += 16;
       kinfo_procs_.resize(num_of_kinfo_proc);
       len = num_of_kinfo_proc * sizeof(struct kinfo_proc);
-      if (sysctl(mib, arraysize(mib), &kinfo_procs_[0], &len, NULL, 0) < 0) {
+      if (sysctl(mib, base::size(mib), &kinfo_procs_[0], &len, NULL, 0) < 0) {
         // If we get a mem error, it just means we need a bigger buffer, so
         // loop around again.  Anything else is a real error and give up.
         if (errno != ENOMEM) {
@@ -78,13 +78,13 @@
 
     // Find out what size buffer we need.
     size_t data_len = 0;
-    if (sysctl(mib, arraysize(mib), NULL, &data_len, NULL, 0) < 0) {
+    if (sysctl(mib, base::size(mib), NULL, &data_len, NULL, 0) < 0) {
       DVPLOG(1) << "failed to figure out the buffer size for a commandline";
       continue;
     }
 
     data.resize(data_len);
-    if (sysctl(mib, arraysize(mib), &data[0], &data_len, NULL, 0) < 0) {
+    if (sysctl(mib, base::size(mib), &data[0], &data_len, NULL, 0) < 0) {
       DVPLOG(1) << "failed to fetch a commandline";
       continue;
     }
diff --git a/base/process/process_metrics_openbsd.cc b/base/process/process_metrics_openbsd.cc
index 509ed0b2..0fb41b6 100644
--- a/base/process/process_metrics_openbsd.cc
+++ b/base/process/process_metrics_openbsd.cc
@@ -9,9 +9,9 @@
 #include <sys/param.h>
 #include <sys/sysctl.h>
 
-#include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/process/process_metrics_iocounters.h"
+#include "base/stl_util.h"
 
 namespace base {
 
@@ -31,12 +31,12 @@
   int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid,
                 sizeof(struct kinfo_proc), 0 };
 
-  if (sysctl(mib, arraysize(mib), NULL, &length, NULL, 0) < 0)
+  if (sysctl(mib, base::size(mib), NULL, &length, NULL, 0) < 0)
     return -1;
 
   mib[5] = (length / sizeof(struct kinfo_proc));
 
-  if (sysctl(mib, arraysize(mib), &info, &length, NULL, 0) < 0)
+  if (sysctl(mib, base::size(mib), &info, &length, NULL, 0) < 0)
     return 0;
 
   return info.p_pctcpu;
@@ -75,7 +75,7 @@
   unsigned long mem_total, mem_free, mem_inactive;
   size_t len = sizeof(vmtotal);
 
-  if (sysctl(mib, arraysize(mib), &vmtotal, &len, NULL, 0) < 0)
+  if (sysctl(mib, base::size(mib), &vmtotal, &len, NULL, 0) < 0)
     return 0;
 
   mem_total = vmtotal.t_vm;
diff --git a/base/strings/string_number_conversions_unittest.cc b/base/strings/string_number_conversions_unittest.cc
index c014dce..95ef9d5 100644
--- a/base/strings/string_number_conversions_unittest.cc
+++ b/base/strings/string_number_conversions_unittest.cc
@@ -15,7 +15,7 @@
 
 #include "base/bit_cast.h"
 #include "base/format_macros.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -152,7 +152,7 @@
   // embedded NUL characters.  The NUL and extra data after it should be
   // interpreted as junk after the number.
   const char input[] = "6\06";
-  std::string input_string(input, arraysize(input) - 1);
+  std::string input_string(input, base::size(input) - 1);
   int output;
   EXPECT_FALSE(StringToInt(input_string, &output));
   EXPECT_EQ(6, output);
@@ -216,7 +216,7 @@
   // embedded NUL characters.  The NUL and extra data after it should be
   // interpreted as junk after the number.
   const char input[] = "6\06";
-  std::string input_string(input, arraysize(input) - 1);
+  std::string input_string(input, base::size(input) - 1);
   unsigned output;
   EXPECT_FALSE(StringToUint(input_string, &output));
   EXPECT_EQ(6U, output);
@@ -286,7 +286,7 @@
   // embedded NUL characters.  The NUL and extra data after it should be
   // interpreted as junk after the number.
   const char input[] = "6\06";
-  std::string input_string(input, arraysize(input) - 1);
+  std::string input_string(input, base::size(input) - 1);
   int64_t output;
   EXPECT_FALSE(StringToInt64(input_string, &output));
   EXPECT_EQ(6, output);
@@ -353,7 +353,7 @@
   // embedded NUL characters.  The NUL and extra data after it should be
   // interpreted as junk after the number.
   const char input[] = "6\06";
-  std::string input_string(input, arraysize(input) - 1);
+  std::string input_string(input, base::size(input) - 1);
   uint64_t output;
   EXPECT_FALSE(StringToUint64(input_string, &output));
   EXPECT_EQ(6U, output);
@@ -422,7 +422,7 @@
   // embedded NUL characters.  The NUL and extra data after it should be
   // interpreted as junk after the number.
   const char input[] = "6\06";
-  std::string input_string(input, arraysize(input) - 1);
+  std::string input_string(input, base::size(input) - 1);
   size_t output;
   EXPECT_FALSE(StringToSizeT(input_string, &output));
   EXPECT_EQ(6U, output);
@@ -479,7 +479,7 @@
   // embedded NUL characters.  The NUL and extra data after it should be
   // interpreted as junk after the number.
   const char input[] = "0xc0ffee\0" "9";
-  std::string input_string(input, arraysize(input) - 1);
+  std::string input_string(input, base::size(input) - 1);
   int output;
   EXPECT_FALSE(HexStringToInt(input_string, &output));
   EXPECT_EQ(0xc0ffee, output);
@@ -544,7 +544,7 @@
   // embedded NUL characters.  The NUL and extra data after it should be
   // interpreted as junk after the number.
   const char input[] = "0xc0ffee\0" "9";
-  std::string input_string(input, arraysize(input) - 1);
+  std::string input_string(input, base::size(input) - 1);
   uint32_t output;
   EXPECT_FALSE(HexStringToUInt(input_string, &output));
   EXPECT_EQ(0xc0ffeeU, output);
@@ -603,7 +603,7 @@
   // embedded NUL characters.  The NUL and extra data after it should be
   // interpreted as junk after the number.
   const char input[] = "0xc0ffee\0" "9";
-  std::string input_string(input, arraysize(input) - 1);
+  std::string input_string(input, base::size(input) - 1);
   int64_t output;
   EXPECT_FALSE(HexStringToInt64(input_string, &output));
   EXPECT_EQ(0xc0ffee, output);
@@ -666,7 +666,7 @@
   // embedded NUL characters.  The NUL and extra data after it should be
   // interpreted as junk after the number.
   const char input[] = "0xc0ffee\0" "9";
-  std::string input_string(input, arraysize(input) - 1);
+  std::string input_string(input, base::size(input) - 1);
   uint64_t output;
   EXPECT_FALSE(HexStringToUInt64(input_string, &output));
   EXPECT_EQ(0xc0ffeeU, output);
@@ -698,8 +698,7 @@
      "\x01\x23\x45\x67\x89\xAB\xCD\xEF\x01\x23\x45", 11, true},
   };
 
-
-  for (size_t i = 0; i < arraysize(cases); ++i) {
+  for (size_t i = 0; i < base::size(cases); ++i) {
     std::vector<uint8_t> output;
     std::vector<uint8_t> compare;
     EXPECT_EQ(cases[i].success, HexStringToBytes(cases[i].input, &output)) <<
@@ -791,7 +790,7 @@
      -1.0000000000000001e-259, true},
   };
 
-  for (size_t i = 0; i < arraysize(cases); ++i) {
+  for (size_t i = 0; i < base::size(cases); ++i) {
     double output;
     errno = 1;
     EXPECT_EQ(cases[i].success, StringToDouble(cases[i].input, &output));
@@ -804,7 +803,7 @@
   // embedded NUL characters.  The NUL and extra data after it should be
   // interpreted as junk after the number.
   const char input[] = "3.14\0" "159";
-  std::string input_string(input, arraysize(input) - 1);
+  std::string input_string(input, base::size(input) - 1);
   double output;
   EXPECT_FALSE(StringToDouble(input_string, &output));
   EXPECT_DOUBLE_EQ(3.14, output);
@@ -832,12 +831,12 @@
   // The following two values were seen in crashes in the wild.
   const char input_bytes[8] = {0, 0, 0, 0, '\xee', '\x6d', '\x73', '\x42'};
   double input = 0;
-  memcpy(&input, input_bytes, arraysize(input_bytes));
+  memcpy(&input, input_bytes, base::size(input_bytes));
   EXPECT_EQ("1335179083776", NumberToString(input));
   const char input_bytes2[8] =
       {0, 0, 0, '\xa0', '\xda', '\x6c', '\x73', '\x42'};
   input = 0;
-  memcpy(&input, input_bytes2, arraysize(input_bytes2));
+  memcpy(&input, input_bytes2, base::size(input_bytes2));
   EXPECT_EQ("1334890332160", NumberToString(input));
 }
 
diff --git a/base/strings/string_util.cc b/base/strings/string_util.cc
index 5e2d0ee..a8fcb8d 100644
--- a/base/strings/string_util.cc
+++ b/base/strings/string_util.cc
@@ -21,8 +21,8 @@
 #include <vector>
 
 #include "base/logging.h"
-#include "base/macros.h"
 #include "base/no_destructor.h"
+#include "base/stl_util.h"
 #include "base/strings/utf_string_conversion_utils.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/third_party/icu/icu_utf.h"
@@ -670,17 +670,17 @@
   size_t dimension = 0;
   const int kKilo = 1024;
   while (unit_amount >= kKilo &&
-         dimension < arraysize(kByteStringsUnlocalized) - 1) {
+         dimension < base::size(kByteStringsUnlocalized) - 1) {
     unit_amount /= kKilo;
     dimension++;
   }
 
   char buf[64];
   if (bytes != 0 && dimension > 0 && unit_amount < 100) {
-    base::snprintf(buf, arraysize(buf), "%.1lf%s", unit_amount,
+    base::snprintf(buf, base::size(buf), "%.1lf%s", unit_amount,
                    kByteStringsUnlocalized[dimension]);
   } else {
-    base::snprintf(buf, arraysize(buf), "%.0lf%s", unit_amount,
+    base::snprintf(buf, base::size(buf), "%.0lf%s", unit_amount,
                    kByteStringsUnlocalized[dimension]);
   }
 
diff --git a/base/strings/string_util_unittest.cc b/base/strings/string_util_unittest.cc
index 6951da7..fe9f21f6 100644
--- a/base/strings/string_util_unittest.cc
+++ b/base/strings/string_util_unittest.cc
@@ -12,7 +12,7 @@
 #include <algorithm>
 #include <type_traits>
 
-#include "base/macros.h"
+#include "base/stl_util.h"
 #include "base/strings/string16.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
@@ -94,14 +94,14 @@
 
   {
     const char array[] = "\x00\x00\xc2\x81\xc2\x81";
-    const std::string array_string(array, arraysize(array));
+    const std::string array_string(array, base::size(array));
     EXPECT_TRUE(Truncated(array_string, 4, &output));
     EXPECT_EQ(output.compare(std::string("\x00\x00\xc2\x81", 4)), 0);
   }
 
   {
     const char array[] = "\x00\xc2\x81\xc2\x81";
-    const std::string array_string(array, arraysize(array));
+    const std::string array_string(array, base::size(array));
     EXPECT_TRUE(Truncated(array_string, 4, &output));
     EXPECT_EQ(output.compare(std::string("\x00\xc2\x81", 3)), 0);
   }
@@ -168,7 +168,7 @@
 
   {
     const char array[] = "\x00\x00\xfe\xff";
-    const std::string array_string(array, arraysize(array));
+    const std::string array_string(array, base::size(array));
     EXPECT_TRUE(Truncated(array_string, 4, &output));
     EXPECT_EQ(output.compare(std::string("\x00\x00", 2)), 0);
   }
@@ -182,7 +182,7 @@
   }
   {
     const char array[] = "\xff\x00\x00\xfe";
-    const std::string array_string(array, arraysize(array));
+    const std::string array_string(array, base::size(array));
     EXPECT_TRUE(Truncated(array_string, 4, &output));
     EXPECT_EQ(output.compare(std::string("\xff\x00\x00", 3)), 0);
   }
@@ -428,7 +428,7 @@
   // Also, test that a non-ASCII character will be detected regardless of its
   // position inside the string.
   {
-    const size_t string_length = arraysize(char_ascii) - 1;
+    const size_t string_length = base::size(char_ascii) - 1;
     for (size_t offset = 0; offset < 8; ++offset) {
       for (size_t len = 0, max_len = string_length - offset; len < max_len;
            ++len) {
@@ -443,7 +443,7 @@
   }
 
   {
-    const size_t string_length = arraysize(char16_ascii) - 1;
+    const size_t string_length = base::size(char16_ascii) - 1;
     for (size_t offset = 0; offset < 4; ++offset) {
       for (size_t len = 0, max_len = string_length - offset; len < max_len;
            ++len) {
@@ -500,7 +500,7 @@
     L"0123ABCDwxyz \a\b\t\r\n!+,.~"
   };
 
-  for (size_t i = 0; i < arraysize(char_cases); ++i) {
+  for (size_t i = 0; i < base::size(char_cases); ++i) {
     EXPECT_TRUE(IsStringASCII(char_cases[i]));
     string16 utf16 = ASCIIToUTF16(char_cases[i]);
     EXPECT_EQ(WideToUTF16(wchar_cases[i]), utf16);
@@ -519,7 +519,7 @@
 
   // Convert strings with an embedded NUL character.
   const char chars_with_nul[] = "test\0string";
-  const int length_with_nul = arraysize(chars_with_nul) - 1;
+  const int length_with_nul = base::size(chars_with_nul) - 1;
   std::string string_with_nul(chars_with_nul, length_with_nul);
   string16 string16_with_nul = ASCIIToUTF16(string_with_nul);
   EXPECT_EQ(static_cast<string16::size_type>(length_with_nul),
@@ -1127,9 +1127,9 @@
   {
     char dst[10];
     wchar_t wdst[10];
-    EXPECT_EQ(7U, strlcpy(dst, "abcdefg", arraysize(dst)));
+    EXPECT_EQ(7U, strlcpy(dst, "abcdefg", base::size(dst)));
     EXPECT_EQ(0, memcmp(dst, "abcdefg", 8));
-    EXPECT_EQ(7U, wcslcpy(wdst, L"abcdefg", arraysize(wdst)));
+    EXPECT_EQ(7U, wcslcpy(wdst, L"abcdefg", base::size(wdst)));
     EXPECT_EQ(0, memcmp(wdst, L"abcdefg", sizeof(wchar_t) * 8));
   }
 
@@ -1150,9 +1150,9 @@
   {
     char dst[8];
     wchar_t wdst[8];
-    EXPECT_EQ(7U, strlcpy(dst, "abcdefg", arraysize(dst)));
+    EXPECT_EQ(7U, strlcpy(dst, "abcdefg", base::size(dst)));
     EXPECT_EQ(0, memcmp(dst, "abcdefg", 8));
-    EXPECT_EQ(7U, wcslcpy(wdst, L"abcdefg", arraysize(wdst)));
+    EXPECT_EQ(7U, wcslcpy(wdst, L"abcdefg", base::size(wdst)));
     EXPECT_EQ(0, memcmp(wdst, L"abcdefg", sizeof(wchar_t) * 8));
   }
 
@@ -1160,9 +1160,9 @@
   {
     char dst[7];
     wchar_t wdst[7];
-    EXPECT_EQ(7U, strlcpy(dst, "abcdefg", arraysize(dst)));
+    EXPECT_EQ(7U, strlcpy(dst, "abcdefg", base::size(dst)));
     EXPECT_EQ(0, memcmp(dst, "abcdef", 7));
-    EXPECT_EQ(7U, wcslcpy(wdst, L"abcdefg", arraysize(wdst)));
+    EXPECT_EQ(7U, wcslcpy(wdst, L"abcdefg", base::size(wdst)));
     EXPECT_EQ(0, memcmp(wdst, L"abcdef", sizeof(wchar_t) * 7));
   }
 
@@ -1170,9 +1170,9 @@
   {
     char dst[3];
     wchar_t wdst[3];
-    EXPECT_EQ(7U, strlcpy(dst, "abcdefg", arraysize(dst)));
+    EXPECT_EQ(7U, strlcpy(dst, "abcdefg", base::size(dst)));
     EXPECT_EQ(0, memcmp(dst, "ab", 3));
-    EXPECT_EQ(7U, wcslcpy(wdst, L"abcdefg", arraysize(wdst)));
+    EXPECT_EQ(7U, wcslcpy(wdst, L"abcdefg", base::size(wdst)));
     EXPECT_EQ(0, memcmp(wdst, L"ab", sizeof(wchar_t) * 3));
   }
 }
@@ -1371,9 +1371,9 @@
     strncpy(WriteInto(&buffer, num_chars + 1), kOriginal, num_chars);
     // Using std::string(buffer.c_str()) instead of |buffer| truncates the
     // string at the first \0.
-    EXPECT_EQ(std::string(kOriginal,
-                          std::min(num_chars, arraysize(kOriginal) - 1)),
-              std::string(buffer.c_str()));
+    EXPECT_EQ(
+        std::string(kOriginal, std::min(num_chars, base::size(kOriginal) - 1)),
+        std::string(buffer.c_str()));
     EXPECT_EQ(num_chars, buffer.size());
   }
 };
diff --git a/base/strings/stringprintf.cc b/base/strings/stringprintf.cc
index 109735ae..72160699 100644
--- a/base/strings/stringprintf.cc
+++ b/base/strings/stringprintf.cc
@@ -9,8 +9,8 @@
 
 #include <vector>
 
-#include "base/macros.h"
 #include "base/scoped_clear_last_error.h"
+#include "base/stl_util.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
@@ -56,17 +56,17 @@
   va_copy(ap_copy, ap);
 
   base::internal::ScopedClearLastError last_error;
-  int result = vsnprintfT(stack_buf, arraysize(stack_buf), format, ap_copy);
+  int result = vsnprintfT(stack_buf, base::size(stack_buf), format, ap_copy);
   va_end(ap_copy);
 
-  if (result >= 0 && result < static_cast<int>(arraysize(stack_buf))) {
+  if (result >= 0 && result < static_cast<int>(base::size(stack_buf))) {
     // It fit.
     dst->append(stack_buf, result);
     return;
   }
 
   // Repeatedly increase buffer size until it fits.
-  int mem_length = arraysize(stack_buf);
+  int mem_length = base::size(stack_buf);
   while (true) {
     if (result < 0) {
 #if defined(OS_WIN)
diff --git a/base/strings/utf_offset_string_conversions_unittest.cc b/base/strings/utf_offset_string_conversions_unittest.cc
index d6f540c..7753363 100644
--- a/base/strings/utf_offset_string_conversions_unittest.cc
+++ b/base/strings/utf_offset_string_conversions_unittest.cc
@@ -7,7 +7,7 @@
 #include <algorithm>
 
 #include "base/logging.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/utf_offset_string_conversions.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -67,7 +67,7 @@
       {{'A', 0xd800, 0xdf00, 'z'}, 3, 5},
       {{'A', 0xd800, 0xdf00, 'z'}, 4, 6},
   };
-  for (size_t i = 0; i < arraysize(utf16_to_utf8_cases); ++i) {
+  for (size_t i = 0; i < base::size(utf16_to_utf8_cases); ++i) {
     size_t offset = utf16_to_utf8_cases[i].input_offset;
     std::vector<size_t> offsets;
     offsets.push_back(offset);
@@ -118,8 +118,8 @@
     adjustments.push_back(OffsetAdjuster::Adjustment(3, 3, 1));
     OffsetAdjuster::AdjustOffsets(adjustments, &offsets);
     size_t expected_1[] = {0, 1, 2, 3, kNpos, kNpos, 4, 5, 6, 7};
-    EXPECT_EQ(offsets.size(), arraysize(expected_1));
-    for (size_t i = 0; i < arraysize(expected_1); ++i)
+    EXPECT_EQ(offsets.size(), base::size(expected_1));
+    for (size_t i = 0; i < base::size(expected_1); ++i)
       EXPECT_EQ(expected_1[i], offsets[i]);
   }
 
@@ -138,8 +138,8 @@
       0, kNpos, kNpos, 1, 2, kNpos, kNpos, kNpos, 4, 5, 6, kNpos, kNpos, kNpos,
       kNpos, kNpos, kNpos, 10, 11, 12, 13, kNpos, kNpos, 14
     };
-    EXPECT_EQ(offsets.size(), arraysize(expected_2));
-    for (size_t i = 0; i < arraysize(expected_2); ++i)
+    EXPECT_EQ(offsets.size(), base::size(expected_2));
+    for (size_t i = 0; i < base::size(expected_2); ++i)
       EXPECT_EQ(expected_2[i], offsets[i]);
   }
 
@@ -158,8 +158,8 @@
       0, kNpos, kNpos, 0, 1, kNpos, kNpos, kNpos, 5, 6, 7, 8, kNpos, kNpos, 11,
       12, kNpos, 12
     };
-    EXPECT_EQ(offsets.size(), arraysize(expected_3));
-    for (size_t i = 0; i < arraysize(expected_3); ++i)
+    EXPECT_EQ(offsets.size(), base::size(expected_3));
+    for (size_t i = 0; i < base::size(expected_3); ++i)
       EXPECT_EQ(expected_3[i], offsets[i]);
   }
 }
@@ -176,8 +176,8 @@
     adjustments.push_back(OffsetAdjuster::Adjustment(3, 3, 1));
     OffsetAdjuster::UnadjustOffsets(adjustments, &offsets);
     size_t expected_1[] = {0, 1, 2, 3, 6, 7, 8, 9};
-    EXPECT_EQ(offsets.size(), arraysize(expected_1));
-    for (size_t i = 0; i < arraysize(expected_1); ++i)
+    EXPECT_EQ(offsets.size(), base::size(expected_1));
+    for (size_t i = 0; i < base::size(expected_1); ++i)
       EXPECT_EQ(expected_1[i], offsets[i]);
   }
 
@@ -195,8 +195,8 @@
     size_t expected_2[] = {
       0, 3, 4, kNpos, 8, 9, 10, kNpos, kNpos, kNpos, 17, 18, 19, 20, 23
     };
-    EXPECT_EQ(offsets.size(), arraysize(expected_2));
-    for (size_t i = 0; i < arraysize(expected_2); ++i)
+    EXPECT_EQ(offsets.size(), base::size(expected_2));
+    for (size_t i = 0; i < base::size(expected_2); ++i)
       EXPECT_EQ(expected_2[i], offsets[i]);
   }
 
@@ -216,8 +216,8 @@
       4, kNpos, kNpos, kNpos, 8, 9, 10, 11, kNpos, kNpos, 14,
       15  // this could just as easily be 17
     };
-    EXPECT_EQ(offsets.size(), arraysize(expected_3));
-    for (size_t i = 0; i < arraysize(expected_3); ++i)
+    EXPECT_EQ(offsets.size(), base::size(expected_3));
+    for (size_t i = 0; i < base::size(expected_3); ++i)
       EXPECT_EQ(expected_3[i], offsets[i]);
   }
 }
diff --git a/base/strings/utf_string_conversions_unittest.cc b/base/strings/utf_string_conversions_unittest.cc
index 9b5cb23..cd799d28 100644
--- a/base/strings/utf_string_conversions_unittest.cc
+++ b/base/strings/utf_string_conversions_unittest.cc
@@ -5,7 +5,7 @@
 #include <stddef.h>
 
 #include "base/logging.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
@@ -194,14 +194,14 @@
     '\0'
   };
   string16 multistring16;
-  memcpy(WriteInto(&multistring16, arraysize(multi16)), multi16,
-                   sizeof(multi16));
-  EXPECT_EQ(arraysize(multi16) - 1, multistring16.length());
+  memcpy(WriteInto(&multistring16, base::size(multi16)), multi16,
+         sizeof(multi16));
+  EXPECT_EQ(base::size(multi16) - 1, multistring16.length());
   std::string expected;
-  memcpy(WriteInto(&expected, arraysize(multi)), multi, sizeof(multi));
-  EXPECT_EQ(arraysize(multi) - 1, expected.length());
+  memcpy(WriteInto(&expected, base::size(multi)), multi, sizeof(multi));
+  EXPECT_EQ(base::size(multi) - 1, expected.length());
   const std::string& converted = UTF16ToUTF8(multistring16);
-  EXPECT_EQ(arraysize(multi) - 1, converted.length());
+  EXPECT_EQ(base::size(multi) - 1, converted.length());
   EXPECT_EQ(expected, converted);
 }
 
diff --git a/base/sync_socket_win.cc b/base/sync_socket_win.cc
index 77b14d11..2521b2f9 100644
--- a/base/sync_socket_win.cc
+++ b/base/sync_socket_win.cc
@@ -8,8 +8,8 @@
 #include <stddef.h>
 
 #include "base/logging.h"
-#include "base/macros.h"
 #include "base/rand_util.h"
+#include "base/stl_util.h"
 #include "base/threading/scoped_blocking_call.h"
 #include "base/win/scoped_handle.h"
 
@@ -22,7 +22,7 @@
 // in sandboxed scenarios as we might have by-name policies that allow pipe
 // creation. Also keep the secure random number generation.
 const wchar_t kPipeNameFormat[] = L"\\\\.\\pipe\\chrome.sync.%u.%u.%lu";
-const size_t kPipePathMax =  arraysize(kPipeNameFormat) + (3 * 10) + 1;
+const size_t kPipePathMax = base::size(kPipeNameFormat) + (3 * 10) + 1;
 
 // To avoid users sending negative message lengths to Send/Receive
 // we clamp message lengths, which are size_t, to no more than INT_MAX.
@@ -154,11 +154,11 @@
       if (::GetLastError() == ERROR_IO_PENDING) {
         HANDLE events[] = { io_event->handle(), cancel_event->handle() };
         const int wait_result = WaitForMultipleObjects(
-            arraysize(events), events, FALSE,
-            timeout_in_ms == INFINITE ?
-                timeout_in_ms :
-                static_cast<DWORD>(
-                    (finish_time - current_time).InMilliseconds()));
+            base::size(events), events, FALSE,
+            timeout_in_ms == INFINITE
+                ? timeout_in_ms
+                : static_cast<DWORD>(
+                      (finish_time - current_time).InMilliseconds()));
         if (wait_result != WAIT_OBJECT_0 + 0) {
           // CancelIo() doesn't synchronously cancel outstanding IO, only marks
           // outstanding IO for cancellation. We must call GetOverlappedResult()
diff --git a/base/system/sys_info_chromeos.cc b/base/system/sys_info_chromeos.cc
index b6fd088..00a3fc8 100644
--- a/base/system/sys_info_chromeos.cc
+++ b/base/system/sys_info_chromeos.cc
@@ -13,7 +13,7 @@
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/lazy_instance.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
@@ -118,7 +118,7 @@
     }
     // Parse the version from the first matching recognized version key.
     std::string version;
-    for (size_t i = 0; i < arraysize(kLinuxStandardBaseVersionKeys); ++i) {
+    for (size_t i = 0; i < base::size(kLinuxStandardBaseVersionKeys); ++i) {
       std::string key = kLinuxStandardBaseVersionKeys[i];
       if (GetLsbReleaseValue(key, &version) && !version.empty())
         break;
@@ -137,7 +137,7 @@
     // Check release name for Chrome OS.
     std::string release_name;
     if (GetLsbReleaseValue(kChromeOsReleaseNameKey, &release_name)) {
-      for (size_t i = 0; i < arraysize(kChromeOsReleaseNames); ++i) {
+      for (size_t i = 0; i < base::size(kChromeOsReleaseNames); ++i) {
         if (release_name == kChromeOsReleaseNames[i]) {
           is_running_on_chromeos_ = true;
           break;
diff --git a/base/system/sys_info_ios.mm b/base/system/sys_info_ios.mm
index 49fdc6e..c1723f737 100644
--- a/base/system/sys_info_ios.mm
+++ b/base/system/sys_info_ios.mm
@@ -14,8 +14,8 @@
 #include "base/logging.h"
 #include "base/mac/scoped_mach_port.h"
 #include "base/mac/scoped_nsautorelease_pool.h"
-#include "base/macros.h"
 #include "base/process/process_metrics.h"
+#include "base/stl_util.h"
 #include "base/strings/string_util.h"
 #include "base/strings/sys_string_conversions.h"
 
@@ -27,7 +27,7 @@
 // system or the empty string on failure.
 std::string GetSysctlValue(const char* key_name) {
   char value[256];
-  size_t len = arraysize(value);
+  size_t len = base::size(value);
   if (sysctlbyname(key_name, &value, &len, nullptr, 0) == 0) {
     DCHECK_GE(len, 1u);
     DCHECK_EQ('\0', value[len - 1]);
diff --git a/base/system/sys_info_mac.mm b/base/system/sys_info_mac.mm
index 58bd34e..7bbc385 100644
--- a/base/system/sys_info_mac.mm
+++ b/base/system/sys_info_mac.mm
@@ -18,8 +18,8 @@
 #include "base/mac/mac_util.h"
 #include "base/mac/scoped_mach_port.h"
 #import "base/mac/sdk_forward_declarations.h"
-#include "base/macros.h"
 #include "base/process/process_metrics.h"
+#include "base/stl_util.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 
@@ -31,7 +31,7 @@
 // system or the empty string on failure.
 std::string GetSysctlValue(const char* key_name) {
   char value[256];
-  size_t len = arraysize(value);
+  size_t len = base::size(value);
   if (sysctlbyname(key_name, &value, &len, nullptr, 0) == 0) {
     DCHECK_GE(len, 1u);
     DCHECK_EQ('\0', value[len - 1]);
diff --git a/base/system/sys_info_openbsd.cc b/base/system/sys_info_openbsd.cc
index 58cb4c6..c6bd917 100644
--- a/base/system/sys_info_openbsd.cc
+++ b/base/system/sys_info_openbsd.cc
@@ -11,7 +11,7 @@
 #include <sys/sysctl.h>
 
 #include "base/logging.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
 
 namespace {
 
@@ -34,7 +34,7 @@
   int mib[] = {CTL_HW, HW_NCPU};
   int ncpu;
   size_t size = sizeof(ncpu);
-  if (sysctl(mib, arraysize(mib), &ncpu, &size, NULL, 0) < 0) {
+  if (sysctl(mib, base::size(mib), &ncpu, &size, NULL, 0) < 0) {
     NOTREACHED();
     return 1;
   }
@@ -58,7 +58,7 @@
   int mib[] = {CTL_KERN, KERN_SHMINFO, KERN_SHMINFO_SHMMAX};
   size_t limit;
   size_t size = sizeof(limit);
-  if (sysctl(mib, arraysize(mib), &limit, &size, NULL, 0) < 0) {
+  if (sysctl(mib, base::size(mib), &limit, &size, NULL, 0) < 0) {
     NOTREACHED();
     return 0;
   }
@@ -69,8 +69,8 @@
 std::string SysInfo::CPUModelName() {
   int mib[] = {CTL_HW, HW_MODEL};
   char name[256];
-  size_t len = arraysize(name);
-  if (sysctl(mib, arraysize(mib), name, &len, NULL, 0) < 0) {
+  size_t len = base::size(name);
+  if (sysctl(mib, base::size(mib), name, &len, NULL, 0) < 0) {
     NOTREACHED();
     return std::string();
   }
diff --git a/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc b/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc
index 99e1efbd..486a30f 100644
--- a/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc
+++ b/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc
@@ -4,6 +4,7 @@
 
 #include "base/task/sequence_manager/thread_controller_with_message_pump_impl.h"
 
+#include "base/auto_reset.h"
 #include "base/message_loop/message_pump.h"
 #include "base/time/tick_clock.h"
 #include "base/trace_event/trace_event.h"
@@ -121,9 +122,13 @@
   if (RunsTasksInCurrentSequence()) {
     // Don't post a DoWork if there's an immediate DoWork in flight or if we're
     // inside a top level DoWork. We can rely on a continuation being posted as
-    // needed.
-    if (main_thread_only().immediate_do_work_posted || InTopLevelDoWork())
+    // needed. We need to avoid this inside DoDelayedWork, however, because
+    // returning true there doesn't guarantee work to get scheduled.
+    // TODO(skyostil@): Simplify this once DoWork/DoDelayedWork get merged.
+    if (main_thread_only().immediate_do_work_posted ||
+        (InTopLevelDoWork() && !main_thread_only().doing_delayed_work)) {
       return;
+    }
     main_thread_only().immediate_do_work_posted = true;
   }
   pump_->ScheduleWork();
@@ -216,6 +221,7 @@
 
 bool ThreadControllerWithMessagePumpImpl::DoDelayedWork(
     TimeTicks* next_run_time) {
+  AutoReset<bool> delayed_scope(&main_thread_only().doing_delayed_work, true);
   return DoWorkImpl(next_run_time);
 }
 
diff --git a/base/task/sequence_manager/thread_controller_with_message_pump_impl.h b/base/task/sequence_manager/thread_controller_with_message_pump_impl.h
index 95e4cde6..c916ca1f 100644
--- a/base/task/sequence_manager/thread_controller_with_message_pump_impl.h
+++ b/base/task/sequence_manager/thread_controller_with_message_pump_impl.h
@@ -112,6 +112,10 @@
     // Used to prevent redundant calls to ScheduleWork / ScheduleDelayedWork.
     bool immediate_do_work_posted = false;
 
+    // Whether we're currently executing delayed work (as opposed to immediate
+    // work).
+    bool doing_delayed_work = false;
+
     // Number of tasks processed in a single DoWork invocation.
     int work_batch_size = 1;
 
diff --git a/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc b/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc
index 40aae04..28a2d409 100644
--- a/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc
+++ b/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc
@@ -350,6 +350,25 @@
   testing::Mock::VerifyAndClearExpectations(message_pump_);
 }
 
+TEST_F(ThreadControllerWithMessagePumpTest, ScheduleWorkFromDelayedTask) {
+  ThreadTaskRunnerHandle handle(MakeRefCounted<FakeTaskRunner>());
+
+  EXPECT_CALL(*message_pump_, Run(_))
+      .WillOnce(Invoke([](MessagePump::Delegate* delegate) {
+        base::TimeTicks run_time;
+        delegate->DoDelayedWork(&run_time);
+      }));
+  EXPECT_CALL(*message_pump_, ScheduleWork());
+
+  task_source_.AddTask(PendingTask(FROM_HERE, base::BindLambdaForTesting([&]() {
+                                     thread_controller_.ScheduleWork();
+                                   }),
+                                   TimeTicks()));
+  RunLoop().Run();
+
+  testing::Mock::VerifyAndClearExpectations(message_pump_);
+}
+
 TEST_F(ThreadControllerWithMessagePumpTest, SetDefaultTaskRunner) {
   scoped_refptr<SingleThreadTaskRunner> task_runner1 =
       MakeRefCounted<FakeTaskRunner>();
diff --git a/base/threading/platform_thread_unittest.cc b/base/threading/platform_thread_unittest.cc
index aca0fc2..96ea662 100644
--- a/base/threading/platform_thread_unittest.cc
+++ b/base/threading/platform_thread_unittest.cc
@@ -5,7 +5,7 @@
 #include <stddef.h>
 
 #include "base/compiler_specific.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/threading/platform_thread.h"
@@ -54,11 +54,11 @@
 
 TEST(PlatformThreadTest, TrivialJoinTimesTen) {
   TrivialThread thread[10];
-  PlatformThreadHandle handle[arraysize(thread)];
+  PlatformThreadHandle handle[base::size(thread)];
 
   for (auto& n : thread)
     ASSERT_FALSE(n.run_event().IsSignaled());
-  for (size_t n = 0; n < arraysize(thread); n++)
+  for (size_t n = 0; n < base::size(thread); n++)
     ASSERT_TRUE(PlatformThread::Create(0, &thread[n], &handle[n]));
   for (auto n : handle)
     PlatformThread::Join(n);
@@ -81,11 +81,11 @@
 
 TEST(PlatformThreadTest, TrivialDetachTimesTen) {
   TrivialThread thread[10];
-  PlatformThreadHandle handle[arraysize(thread)];
+  PlatformThreadHandle handle[base::size(thread)];
 
   for (auto& n : thread)
     ASSERT_FALSE(n.run_event().IsSignaled());
-  for (size_t n = 0; n < arraysize(thread); n++) {
+  for (size_t n = 0; n < base::size(thread); n++) {
     ASSERT_TRUE(PlatformThread::Create(0, &thread[n], &handle[n]));
     PlatformThread::Detach(handle[n]);
   }
@@ -187,17 +187,17 @@
   PlatformThreadId main_thread_id = PlatformThread::CurrentId();
 
   FunctionTestThread thread[10];
-  PlatformThreadHandle handle[arraysize(thread)];
+  PlatformThreadHandle handle[base::size(thread)];
 
   for (const auto& n : thread)
     ASSERT_FALSE(n.IsRunning());
 
-  for (size_t n = 0; n < arraysize(thread); n++)
+  for (size_t n = 0; n < base::size(thread); n++)
     ASSERT_TRUE(PlatformThread::Create(0, &thread[n], &handle[n]));
   for (auto& n : thread)
     n.WaitForTerminationReady();
 
-  for (size_t n = 0; n < arraysize(thread); n++) {
+  for (size_t n = 0; n < base::size(thread); n++) {
     ASSERT_TRUE(thread[n].IsRunning());
     EXPECT_NE(thread[n].thread_id(), main_thread_id);
 
diff --git a/base/threading/sequence_local_storage_slot_unittest.cc b/base/threading/sequence_local_storage_slot_unittest.cc
index 4a9f6a91..4e4eb20 100644
--- a/base/threading/sequence_local_storage_slot_unittest.cc
+++ b/base/threading/sequence_local_storage_slot_unittest.cc
@@ -6,8 +6,8 @@
 
 #include <utility>
 
-#include "base/macros.h"
 #include "base/memory/ptr_util.h"
+#include "base/stl_util.h"
 #include "base/threading/sequence_local_storage_map.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -125,14 +125,14 @@
 
   // Set the value of the slot to be the index of the current
   // SequenceLocalStorageMaps in the vector
-  for (unsigned int i = 0; i < arraysize(sequence_local_storage_maps); ++i) {
+  for (unsigned int i = 0; i < base::size(sequence_local_storage_maps); ++i) {
     internal::ScopedSetSequenceLocalStorageMapForCurrentThread
         scoped_sequence_local_storage(&sequence_local_storage_maps[i]);
 
     slot.Set(i);
   }
 
-  for (unsigned int i = 0; i < arraysize(sequence_local_storage_maps); ++i) {
+  for (unsigned int i = 0; i < base::size(sequence_local_storage_maps); ++i) {
     internal::ScopedSetSequenceLocalStorageMapForCurrentThread
         scoped_sequence_local_storage(&sequence_local_storage_maps[i]);
 
diff --git a/base/time/pr_time_unittest.cc b/base/time/pr_time_unittest.cc
index 6fce4ab1a..3170e57 100644
--- a/base/time/pr_time_unittest.cc
+++ b/base/time/pr_time_unittest.cc
@@ -6,7 +6,7 @@
 #include <time.h>
 
 #include "base/compiler_specific.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
 #include "base/third_party/nspr/prtime.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
@@ -79,7 +79,7 @@
   char time_buf[64] = {};
 #if defined(OS_WIN)
   localtime_s(&local_time, &current_time);
-  asctime_s(time_buf, arraysize(time_buf), &local_time);
+  asctime_s(time_buf, base::size(time_buf), &local_time);
 #elif defined(OS_POSIX) || defined(OS_FUCHSIA)
   localtime_r(&current_time, &local_time);
   asctime_r(&local_time, time_buf);
diff --git a/base/time/time_mac.cc b/base/time/time_mac.cc
index 7ae7459..faee634 100644
--- a/base/time/time_mac.cc
+++ b/base/time/time_mac.cc
@@ -19,8 +19,8 @@
 #include "base/mac/mach_logging.h"
 #include "base/mac/scoped_cftyperef.h"
 #include "base/mac/scoped_mach_port.h"
-#include "base/macros.h"
 #include "base/numerics/safe_conversions.h"
+#include "base/stl_util.h"
 #include "base/time/time_override.h"
 #include "build/build_config.h"
 
@@ -79,7 +79,7 @@
   struct timeval boottime;
   int mib[2] = {CTL_KERN, KERN_BOOTTIME};
   size_t size = sizeof(boottime);
-  int kr = sysctl(mib, arraysize(mib), &boottime, &size, nullptr, 0);
+  int kr = sysctl(mib, base::size(mib), &boottime, &size, nullptr, 0);
   DCHECK_EQ(KERN_SUCCESS, kr);
   base::TimeDelta time_difference =
       base::subtle::TimeNowIgnoringOverride() -
diff --git a/base/time/time_unittest.cc b/base/time/time_unittest.cc
index ce8cc39..7b51d15 100644
--- a/base/time/time_unittest.cc
+++ b/base/time/time_unittest.cc
@@ -12,7 +12,7 @@
 #include "base/build_time.h"
 #include "base/compiler_specific.h"
 #include "base/logging.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/threading/platform_thread.h"
 #include "base/time/time_override.h"
@@ -319,7 +319,7 @@
   char time_buf[64] = {};
 #if defined(OS_WIN)
   localtime_s(&local_time, &current_time);
-  asctime_s(time_buf, arraysize(time_buf), &local_time);
+  asctime_s(time_buf, base::size(time_buf), &local_time);
 #elif defined(OS_POSIX) || defined(OS_FUCHSIA)
   localtime_r(&current_time, &local_time);
   asctime_r(&local_time, time_buf);
diff --git a/base/timer/timer_unittest.cc b/base/timer/timer_unittest.cc
index a2cc476..b137a6657 100644
--- a/base/timer/timer_unittest.cc
+++ b/base/timer/timer_unittest.cc
@@ -17,6 +17,7 @@
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "base/sequenced_task_runner.h"
+#include "base/stl_util.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/task/post_task.h"
 #include "base/test/scoped_task_environment.h"
@@ -353,7 +354,7 @@
   ResetHelper reset_helper(&timer, &target);
 
   OneShotTimer timers[20];
-  for (size_t i = 0; i < arraysize(timers); ++i) {
+  for (size_t i = 0; i < base::size(timers); ++i) {
     timers[i].Start(FROM_HERE, TimeDelta::FromMilliseconds(i * 10),
                     &reset_helper, &ResetHelper::Reset);
   }
diff --git a/base/trace_event/heap_profiler_allocation_context_tracker.cc b/base/trace_event/heap_profiler_allocation_context_tracker.cc
index 98f5d11..7da727a 100644
--- a/base/trace_event/heap_profiler_allocation_context_tracker.cc
+++ b/base/trace_event/heap_profiler_allocation_context_tracker.cc
@@ -12,6 +12,7 @@
 #include "base/debug/leak_annotations.h"
 #include "base/debug/stack_trace.h"
 #include "base/no_destructor.h"
+#include "base/stl_util.h"
 #include "base/threading/platform_thread.h"
 #include "base/threading/thread_local_storage.h"
 #include "base/trace_event/heap_profiler_allocation_context.h"
@@ -221,17 +222,17 @@
 #if !defined(OS_NACL)  // We don't build base/debug/stack_trace.cc for NaCl.
 #if defined(OS_ANDROID) && BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE)
         const void* frames[Backtrace::kMaxFrameCount + 1];
-        static_assert(arraysize(frames) >= Backtrace::kMaxFrameCount,
+        static_assert(base::size(frames) >= Backtrace::kMaxFrameCount,
                       "not requesting enough frames to fill Backtrace");
         size_t frame_count =
             CFIBacktraceAndroid::GetInitializedInstance()->Unwind(
-                frames, arraysize(frames));
+                frames, base::size(frames));
 #elif BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
         const void* frames[Backtrace::kMaxFrameCount + 1];
-        static_assert(arraysize(frames) >= Backtrace::kMaxFrameCount,
+        static_assert(base::size(frames) >= Backtrace::kMaxFrameCount,
                       "not requesting enough frames to fill Backtrace");
         size_t frame_count = debug::TraceStackFramePointers(
-            frames, arraysize(frames),
+            frames, base::size(frames),
             1 /* exclude this function from the trace */);
 #else
         // Fall-back to capturing the stack with base::debug::StackTrace,
diff --git a/base/values.cc b/base/values.cc
index 3349f74..fbf6d83ae 100644
--- a/base/values.cc
+++ b/base/values.cc
@@ -26,7 +26,7 @@
 
 const char* const kTypeNames[] = {"null",   "boolean", "integer",    "double",
                                   "string", "binary",  "dictionary", "list"};
-static_assert(arraysize(kTypeNames) ==
+static_assert(base::size(kTypeNames) ==
                   static_cast<size_t>(Value::Type::LIST) + 1,
               "kTypeNames Has Wrong Size");
 
@@ -223,7 +223,7 @@
 // static
 const char* Value::GetTypeName(Value::Type type) {
   DCHECK_GE(static_cast<int>(type), 0);
-  DCHECK_LT(static_cast<size_t>(type), arraysize(kTypeNames));
+  DCHECK_LT(static_cast<size_t>(type), base::size(kTypeNames));
   return kTypeNames[static_cast<size_t>(type)];
 }
 
@@ -1404,7 +1404,7 @@
 
 std::ostream& operator<<(std::ostream& out, const Value::Type& type) {
   if (static_cast<int>(type) < 0 ||
-      static_cast<size_t>(type) >= arraysize(kTypeNames))
+      static_cast<size_t>(type) >= base::size(kTypeNames))
     return out << "Invalid Type (index = " << static_cast<int>(type) << ")";
   return out << Value::GetTypeName(type);
 }
diff --git a/base/vlog.cc b/base/vlog.cc
index 8fcc085..255e361b 100644
--- a/base/vlog.cc
+++ b/base/vlog.cc
@@ -10,7 +10,7 @@
 #include <utility>
 
 #include "base/logging.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
 
@@ -94,7 +94,7 @@
   base::StringPiece::size_type extension_start = module.rfind('.');
   module = module.substr(0, extension_start);
   static const char kInlSuffix[] = "-inl";
-  static const int kInlSuffixLen = arraysize(kInlSuffix) - 1;
+  static const int kInlSuffixLen = base::size(kInlSuffix) - 1;
   if (module.ends_with(kInlSuffix))
     module.remove_suffix(kInlSuffixLen);
   return module;
diff --git a/base/win/event_trace_consumer_unittest.cc b/base/win/event_trace_consumer_unittest.cc
index 9c4c242..ae79d88 100644
--- a/base/win/event_trace_consumer_unittest.cc
+++ b/base/win/event_trace_consumer_unittest.cc
@@ -13,8 +13,8 @@
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/logging.h"
-#include "base/macros.h"
 #include "base/process/process_handle.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/win/event_trace_controller.h"
 #include "base/win/event_trace_provider.h"
@@ -155,8 +155,8 @@
       return HRESULT_FROM_WIN32(::GetLastError());
 
     HANDLE events[] = { consumer_ready_.Get(), consumer_thread_.Get() };
-    DWORD result = ::WaitForMultipleObjects(arraysize(events), events,
-                                            FALSE, INFINITE);
+    DWORD result =
+        ::WaitForMultipleObjects(base::size(events), events, FALSE, INFINITE);
     switch (result) {
       case WAIT_OBJECT_0:
         // The event was set, the consumer_ is ready.
diff --git a/base/win/i18n.cc b/base/win/i18n.cc
index d7017e31..9c25e62 100644
--- a/base/win/i18n.cc
+++ b/base/win/i18n.cc
@@ -7,7 +7,7 @@
 #include <windows.h>
 
 #include "base/logging.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
 
 namespace {
 
@@ -33,7 +33,7 @@
   &kThreadLanguagesFunctionName[0]
 };
 
-static_assert(NUM_FUNCTIONS == arraysize(kLanguageFunctionNames),
+static_assert(NUM_FUNCTIONS == base::size(kLanguageFunctionNames),
               "LanguageFunction enum and kLanguageFunctionNames array must be "
               "kept in sync");
 
@@ -96,7 +96,7 @@
     wchar_t result_buffer[9];
     int result_length =
         GetLocaleInfo(locale_id, LOCALE_SISO639LANGNAME, &result_buffer[0],
-                      arraysize(result_buffer));
+                      base::size(result_buffer));
     DPCHECK(0 != result_length) << "Failed getting language id";
     if (1 < result_length) {
       language->assign(&result_buffer[0], result_length - 1);
@@ -104,7 +104,7 @@
       if (SUBLANG_NEUTRAL != SUBLANGID(lang_id)) {
         result_length =
             GetLocaleInfo(locale_id, LOCALE_SISO3166CTRYNAME, &result_buffer[0],
-                          arraysize(result_buffer));
+                          base::size(result_buffer));
         DPCHECK(0 != result_length) << "Failed getting region id";
         if (1 < result_length)
           region->assign(&result_buffer[0], result_length - 1);
diff --git a/base/win/registry.cc b/base/win/registry.cc
index 259df12..872f4f2 100644
--- a/base/win/registry.cc
+++ b/base/win/registry.cc
@@ -8,7 +8,7 @@
 #include <algorithm>
 
 #include "base/logging.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
 #include "base/strings/string_util.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/win/shlwapi.h"
@@ -230,7 +230,7 @@
 
 LONG RegKey::GetValueNameAt(int index, std::wstring* name) const {
   wchar_t buf[256];
-  DWORD bufsize = arraysize(buf);
+  DWORD bufsize = base::size(buf);
   LONG r = ::RegEnumValue(key_, index, buf, &bufsize, NULL, NULL, NULL, NULL);
   if (r == ERROR_SUCCESS)
     *name = buf;
@@ -641,7 +641,7 @@
 
 bool RegistryKeyIterator::Read() {
   if (Valid()) {
-    DWORD ncount = arraysize(name_);
+    DWORD ncount = base::size(name_);
     FILETIME written;
     LONG r = ::RegEnumKeyEx(key_, index_, name_, &ncount, NULL, NULL,
                             NULL, &written);
diff --git a/base/win/registry_unittest.cc b/base/win/registry_unittest.cc
index ee960cf9..f5bf600 100644
--- a/base/win/registry_unittest.cc
+++ b/base/win/registry_unittest.cc
@@ -12,7 +12,6 @@
 
 #include "base/bind.h"
 #include "base/compiler_specific.h"
-#include "base/macros.h"
 #include "base/run_loop.h"
 #include "base/stl_util.h"
 #include "base/test/scoped_task_environment.h"
@@ -171,20 +170,20 @@
   const wchar_t kName[] = L"name";
   // kData size is not a multiple of sizeof(wchar_t).
   const uint8_t kData[] = {1, 2, 3, 4, 5};
-  EXPECT_EQ(5u, arraysize(kData));
-  ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(kName, kData,
-                                          arraysize(kData), REG_BINARY));
+  EXPECT_EQ(5u, base::size(kData));
+  ASSERT_EQ(ERROR_SUCCESS,
+            key.WriteValue(kName, kData, base::size(kData), REG_BINARY));
 
   RegistryValueIterator iterator(HKEY_CURRENT_USER, foo_key.c_str());
   ASSERT_TRUE(iterator.Valid());
   EXPECT_STREQ(kName, iterator.Name());
   // ValueSize() is in bytes.
-  ASSERT_EQ(arraysize(kData), iterator.ValueSize());
+  ASSERT_EQ(base::size(kData), iterator.ValueSize());
   // Value() is NUL terminated.
   int end = (iterator.ValueSize() + sizeof(wchar_t) - 1) / sizeof(wchar_t);
   EXPECT_NE(L'\0', iterator.Value()[end-1]);
   EXPECT_EQ(L'\0', iterator.Value()[end]);
-  EXPECT_EQ(0, std::memcmp(kData, iterator.Value(), arraysize(kData)));
+  EXPECT_EQ(0, std::memcmp(kData, iterator.Value(), base::size(kData)));
   ++iterator;
   EXPECT_FALSE(iterator.Valid());
 }
diff --git a/base/win/scoped_bstr_unittest.cc b/base/win/scoped_bstr_unittest.cc
index d305e5a2..54ad47e3 100644
--- a/base/win/scoped_bstr_unittest.cc
+++ b/base/win/scoped_bstr_unittest.cc
@@ -4,7 +4,7 @@
 
 #include <stddef.h>
 
-#include "base/macros.h"
+#include "base/stl_util.h"
 #include "base/win/scoped_bstr.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -15,8 +15,8 @@
 
 static const wchar_t kTestString1[] = L"123";
 static const wchar_t kTestString2[] = L"456789";
-size_t test1_len = arraysize(kTestString1) - 1;
-size_t test2_len = arraysize(kTestString2) - 1;
+size_t test1_len = base::size(kTestString1) - 1;
+size_t test2_len = base::size(kTestString2) - 1;
 
 void DumbBstrTests() {
   ScopedBstr b;
diff --git a/base/win/shortcut_unittest.cc b/base/win/shortcut_unittest.cc
index 3c1c26f..e4543f32 100644
--- a/base/win/shortcut_unittest.cc
+++ b/base/win/shortcut_unittest.cc
@@ -11,7 +11,7 @@
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
 #include "base/test/test_file_util.h"
 #include "base/test/test_shortcut_win.h"
 #include "base/win/scoped_com_initializer.h"
@@ -36,7 +36,7 @@
     // Shortcut 1's properties
     {
       const FilePath target_file(temp_dir_.GetPath().Append(L"Target 1.txt"));
-      WriteFile(target_file, kFileContents, arraysize(kFileContents));
+      WriteFile(target_file, kFileContents, base::size(kFileContents));
 
       link_properties_.set_target(target_file);
       link_properties_.set_working_dir(temp_dir_.GetPath());
@@ -58,7 +58,7 @@
     // Shortcut 2's properties (all different from properties of shortcut 1).
     {
       const FilePath target_file_2(temp_dir_.GetPath().Append(L"Target 2.txt"));
-      WriteFile(target_file_2, kFileContents2, arraysize(kFileContents2));
+      WriteFile(target_file_2, kFileContents2, base::size(kFileContents2));
 
       FilePath icon_path_2;
       CreateTemporaryFileInDir(temp_dir_.GetPath(), &icon_path_2);
@@ -147,8 +147,8 @@
   FilePath resolved_name;
   EXPECT_TRUE(ResolveShortcut(link_file_, &resolved_name, NULL));
 
-  char read_contents[arraysize(kFileContents)];
-  base::ReadFile(resolved_name, read_contents, arraysize(read_contents));
+  char read_contents[base::size(kFileContents)];
+  base::ReadFile(resolved_name, read_contents, base::size(read_contents));
   EXPECT_STREQ(kFileContents, read_contents);
 }
 
@@ -160,8 +160,8 @@
   string16 args;
   EXPECT_TRUE(ResolveShortcut(link_file_, &resolved_name, &args));
 
-  char read_contents[arraysize(kFileContents)];
-  base::ReadFile(resolved_name, read_contents, arraysize(read_contents));
+  char read_contents[base::size(kFileContents)];
+  base::ReadFile(resolved_name, read_contents, base::size(read_contents));
   EXPECT_STREQ(kFileContents, read_contents);
   EXPECT_EQ(link_properties_.arguments, args);
 }
@@ -213,8 +213,8 @@
   FilePath resolved_name;
   EXPECT_TRUE(ResolveShortcut(link_file_, &resolved_name, NULL));
 
-  char read_contents[arraysize(kFileContents2)];
-  base::ReadFile(resolved_name, read_contents, arraysize(read_contents));
+  char read_contents[base::size(kFileContents2)];
+  base::ReadFile(resolved_name, read_contents, base::size(read_contents));
   EXPECT_STREQ(kFileContents2, read_contents);
 }
 
diff --git a/build/android/lint/suppressions.xml b/build/android/lint/suppressions.xml
index 3b4d641..2c3649f 100644
--- a/build/android/lint/suppressions.xml
+++ b/build/android/lint/suppressions.xml
@@ -167,6 +167,7 @@
   <issue id="InconsistentArrays" severity="Error">
     <ignore regexp="android_webview/locale_paks.resources.zip/values/locale-paks.xml"/>
     <ignore regexp="chrome/android/chrome_locale_paks.resources.zip/values/locale-paks.xml"/>
+    <ignore regexp="preloaded_fonts.xml"/>
   </issue>
   <issue id="InconsistentLayout" severity="ignore"/>
   <issue id="InefficientWeight" severity="Error">
diff --git a/build/config/compiler/compiler.gni b/build/config/compiler/compiler.gni
index 4bb8e0e..18a7f08 100644
--- a/build/config/compiler/compiler.gni
+++ b/build/config/compiler/compiler.gni
@@ -57,7 +57,6 @@
 
   # Enables support for ThinLTO, which links 3x-10x faster than full LTO. See
   # also http://blog.llvm.org/2016/06/thinlto-scalable-and-incremental-lto.html
-  # TODO(https://crbug.com/887272): Reenable on is_android && is_official_build
   use_thin_lto = is_cfi || (is_android && is_official_build)
 
   # Tell VS to create a PDB that references information in .obj files rather
diff --git a/build/config/mac/mac_sdk_overrides.gni b/build/config/mac/mac_sdk_overrides.gni
index 3632678..b05bc6a 100644
--- a/build/config/mac/mac_sdk_overrides.gni
+++ b/build/config/mac/mac_sdk_overrides.gni
@@ -9,14 +9,8 @@
 declare_args() {
   # Minimum supported version of the Mac SDK.
   if (_sdk_min_from_env == "") {
-    mac_sdk_min = "10.12"
+    mac_sdk_min = "10.13"
   } else {
     mac_sdk_min = _sdk_min_from_env
   }
-}
-
-# Always assert that mac_sdk_min is used on non-macOS platforms to prevent
-# unused args warnings.
-if (!is_mac) {
-  assert(mac_sdk_min == "10.12" || true)
-}
+}
\ No newline at end of file
diff --git a/build/mac_toolchain.py b/build/mac_toolchain.py
index 9f9d274..9d393c67 100755
--- a/build/mac_toolchain.py
+++ b/build/mac_toolchain.py
@@ -25,18 +25,12 @@
 
 # This can be changed after running:
 #    mac_toolchain upload -xcode-path path/to/Xcode.app
-MAC_TOOLCHAIN_VERSION = '8E2002'
+MAC_TOOLCHAIN_VERSION = '9E145'
 
 # The toolchain will not be downloaded if the minimum OS version is not met.
-# 16 is the major version number for macOS 10.12.
-MAC_MINIMUM_OS_VERSION = 16
-
-# The toolchain will not be downloaded if the maximum OS version is exceeded.
-# 17 is the major version number for macOS 10.13. Xcode 8 does not run on macOS
-# 10.14.
-# TODO(https://crbug.com/780980): Once we build with 10.13 SDK, Xcode 9, we
-# should be able to remove this upper bound.
-MAC_MAXIMUM_OS_VERSION = 17
+# 17 is the major version number for macOS 10.13.
+# 9E145 (Xcode 9.3) only runs on 10.13.2 and newer.
+MAC_MINIMUM_OS_VERSION = 17
 
 MAC_TOOLCHAIN_INSTALLER = 'mac_toolchain'
 
@@ -54,8 +48,7 @@
 
 def PlatformMeetsHermeticXcodeRequirements():
   major_version = int(platform.release().split('.')[0])
-  return (major_version >= MAC_MINIMUM_OS_VERSION and
-          major_version <= MAC_MAXIMUM_OS_VERSION)
+  return major_version >= MAC_MINIMUM_OS_VERSION
 
 
 def _UseHermeticToolchain():
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc
index 15c8bb5..cc56651 100644
--- a/cc/layers/layer.cc
+++ b/cc/layers/layer.cc
@@ -1190,6 +1190,19 @@
   SetSubtreePropertyChanged();
 }
 
+std::string Layer::ToString() const {
+  return base::StringPrintf(
+      "layer_id: %d\n"
+      "  element_id: %s\n"
+      "  bounds: %s\n"
+      "  position: %s\n"
+      "  scrollable: %d\n"
+      "  property tree indices: transform(%d) clip(%d) effect(%d) scroll(%d)\n",
+      id(), element_id().ToString().c_str(), bounds().ToString().c_str(),
+      position().ToString().c_str(), scrollable(), transform_tree_index(),
+      clip_tree_index(), effect_tree_index(), scroll_tree_index());
+}
+
 void Layer::SetUseParentBackfaceVisibility(bool use) {
   DCHECK(IsPropertyChangeAllowed());
   if (inputs_.use_parent_backface_visibility == use)
diff --git a/cc/layers/layer.h b/cc/layers/layer.h
index 82ffa3ea..2eed5e9e53 100644
--- a/cc/layers/layer.h
+++ b/cc/layers/layer.h
@@ -763,6 +763,8 @@
     return should_flatten_screen_space_transform_from_property_tree_;
   }
 
+  std::string ToString() const;
+
  protected:
   friend class LayerImpl;
   friend class TreeSynchronizer;
diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc
index 8a84df9..43c529e 100644
--- a/cc/layers/layer_impl.cc
+++ b/cc/layers/layer_impl.cc
@@ -11,6 +11,7 @@
 #include <utility>
 
 #include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/trace_event/trace_event.h"
@@ -376,7 +377,7 @@
   is_resized_by_browser_controls_ = resized;
 }
 
-std::unique_ptr<base::DictionaryValue> LayerImpl::LayerAsJson() {
+std::unique_ptr<base::DictionaryValue> LayerImpl::LayerAsJson() const {
   std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue);
   result->SetInteger("LayerId", id());
   result->SetString("LayerType", LayerTypeAsString());
@@ -391,7 +392,8 @@
   list->AppendDouble(position_.y());
   result->Set("Position", std::move(list));
 
-  const gfx::Transform& gfx_transform = test_properties()->transform;
+  const gfx::Transform& gfx_transform =
+      const_cast<LayerImpl*>(this)->test_properties()->transform;
   double transform[16];
   gfx_transform.matrix().asColMajord(transform);
   list = std::make_unique<base::ListValue>();
@@ -780,6 +782,16 @@
     state->SetValue("debug_info", debug_info_);
 }
 
+std::string LayerImpl::ToString() const {
+  std::string str;
+  base::JSONWriter::WriteWithOptions(
+      *LayerAsJson(),
+      base::JSONWriter::OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION |
+          base::JSONWriter::OPTIONS_PRETTY_PRINT,
+      &str);
+  return str;
+}
+
 size_t LayerImpl::GPUMemoryUsageInBytes() const { return 0; }
 
 void LayerImpl::RunMicroBenchmark(MicroBenchmarkImpl* benchmark) {
diff --git a/cc/layers/layer_impl.h b/cc/layers/layer_impl.h
index eb1461a..3002f25 100644
--- a/cc/layers/layer_impl.h
+++ b/cc/layers/layer_impl.h
@@ -340,7 +340,7 @@
   void AddDamageRect(const gfx::Rect& damage_rect);
   const gfx::Rect& damage_rect() const { return damage_rect_; }
 
-  virtual std::unique_ptr<base::DictionaryValue> LayerAsJson();
+  virtual std::unique_ptr<base::DictionaryValue> LayerAsJson() const;
   // TODO(pdr): This should be removed because there is no longer a tree
   // of layers, only a list.
   std::unique_ptr<base::DictionaryValue> LayerTreeAsJson();
@@ -390,6 +390,7 @@
   virtual void GetAllPrioritizedTilesForTracing(
       std::vector<PrioritizedTile>* prioritized_tiles) const;
   virtual void AsValueInto(base::trace_event::TracedValue* dict) const;
+  std::string ToString() const;
 
   virtual size_t GPUMemoryUsageInBytes() const;
 
diff --git a/cc/layers/nine_patch_layer_impl.cc b/cc/layers/nine_patch_layer_impl.cc
index 9e6c800..28a9241 100644
--- a/cc/layers/nine_patch_layer_impl.cc
+++ b/cc/layers/nine_patch_layer_impl.cc
@@ -85,7 +85,7 @@
   return "cc::NinePatchLayerImpl";
 }
 
-std::unique_ptr<base::DictionaryValue> NinePatchLayerImpl::LayerAsJson() {
+std::unique_ptr<base::DictionaryValue> NinePatchLayerImpl::LayerAsJson() const {
   std::unique_ptr<base::DictionaryValue> result = LayerImpl::LayerAsJson();
   quad_generator_.AsJson(result.get());
   return result;
diff --git a/cc/layers/nine_patch_layer_impl.h b/cc/layers/nine_patch_layer_impl.h
index 4841059..5e01501d 100644
--- a/cc/layers/nine_patch_layer_impl.h
+++ b/cc/layers/nine_patch_layer_impl.h
@@ -44,7 +44,7 @@
   void AppendQuads(viz::RenderPass* render_pass,
                    AppendQuadsData* append_quads_data) override;
 
-  std::unique_ptr<base::DictionaryValue> LayerAsJson() override;
+  std::unique_ptr<base::DictionaryValue> LayerAsJson() const override;
 
  protected:
   NinePatchLayerImpl(LayerTreeImpl* tree_impl, int id);
diff --git a/cc/layers/ui_resource_layer_impl.cc b/cc/layers/ui_resource_layer_impl.cc
index c3a94fc..7eae103 100644
--- a/cc/layers/ui_resource_layer_impl.cc
+++ b/cc/layers/ui_resource_layer_impl.cc
@@ -138,7 +138,8 @@
   return "cc::UIResourceLayerImpl";
 }
 
-std::unique_ptr<base::DictionaryValue> UIResourceLayerImpl::LayerAsJson() {
+std::unique_ptr<base::DictionaryValue> UIResourceLayerImpl::LayerAsJson()
+    const {
   std::unique_ptr<base::DictionaryValue> result = LayerImpl::LayerAsJson();
 
   result->Set("ImageBounds", MathUtil::AsValue(image_bounds_));
diff --git a/cc/layers/ui_resource_layer_impl.h b/cc/layers/ui_resource_layer_impl.h
index bd94720..1283a23 100644
--- a/cc/layers/ui_resource_layer_impl.h
+++ b/cc/layers/ui_resource_layer_impl.h
@@ -52,7 +52,7 @@
   void AppendQuads(viz::RenderPass* render_pass,
                    AppendQuadsData* append_quads_data) override;
 
-  std::unique_ptr<base::DictionaryValue> LayerAsJson() override;
+  std::unique_ptr<base::DictionaryValue> LayerAsJson() const override;
 
  protected:
   UIResourceLayerImpl(LayerTreeImpl* tree_impl, int id);
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index 10c74f8..605b3723 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -16,7 +16,6 @@
 #include "base/auto_reset.h"
 #include "base/bind.h"
 #include "base/command_line.h"
-#include "base/json/json_writer.h"
 #include "base/location.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
@@ -375,15 +374,10 @@
   // Dump property trees and layers if run with:
   //   --vmodule=layer_tree_host=3
   if (VLOG_IS_ON(3)) {
-    std::string property_trees;
-    base::JSONWriter::WriteWithOptions(
-        *sync_tree->property_trees()->AsTracedValue()->ToBaseValue(),
-        base::JSONWriter::OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION |
-            base::JSONWriter::OPTIONS_PRETTY_PRINT,
-        &property_trees);
     VLOG(3) << "After finishing commit on impl, the sync tree:"
             << "\nproperty_trees:\n"
-            << property_trees << "\ncc::LayerImpls:\n"
+            << sync_tree->property_trees()->ToString() << "\n"
+            << "cc::LayerImpls:\n"
             << sync_tree->LayerListAsJson();
   }
 }
@@ -806,30 +800,14 @@
   // This only prints output in unit test or for the renderer.
   if (VLOG_IS_ON(3) && (!GetClientNameForMetrics() ||
                         GetClientNameForMetrics() == std::string("Renderer"))) {
-    std::string property_trees;
-    base::JSONWriter::WriteWithOptions(
-        *property_trees_.AsTracedValue()->ToBaseValue(),
-        base::JSONWriter::OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION |
-            base::JSONWriter::OPTIONS_PRETTY_PRINT,
-        &property_trees);
     std::ostringstream layers;
-    for (auto* layer : *this) {
-      layers << "\n  layer id " << layer->id();
-      layers << "\n    element_id: " << layer->element_id();
-      layers << "\n    bounds: " << layer->bounds().ToString();
-      layers << "\n    opacity: " << layer->opacity();
-      layers << "\n    position: " << layer->position().ToString();
-      layers << "\n    draws_content: " << layer->DrawsContent();
-      layers << "\n    scrollable: " << layer->scrollable();
-      layers << "\n    contents_opaque: " << layer->contents_opaque();
-      layers << "\n    property tree indices: ";
-      layers << "transform(" << layer->transform_tree_index() << "), ";
-      layers << "clip(" << layer->clip_tree_index() << "), ";
-      layers << "effect(" << layer->effect_tree_index() << "), ";
-      layers << "scroll(" << layer->scroll_tree_index() << ")";
-    }
-    VLOG(3) << "After updating layers on the main thread:\nproperty trees:\n"
-            << property_trees << "\ncc:Layers:" << layers.str();
+    for (auto* layer : *this)
+      layers << layer->ToString() << "\n";
+    VLOG(3) << "After updating layers on the main thread:\n"
+            << "property trees:\n"
+            << property_trees_.ToString() << "\n"
+            << "cc::Layers:\n"
+            << layers.str();
   }
 
   bool painted_content_has_slow_paths = false;
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 6d4925ea..318b967 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -2903,15 +2903,10 @@
   // Dump property trees and layers if run with:
   //   --vmodule=layer_tree_host_impl=3
   if (VLOG_IS_ON(3)) {
-    std::string property_trees;
-    base::JSONWriter::WriteWithOptions(
-        *active_tree_->property_trees()->AsTracedValue()->ToBaseValue(),
-        base::JSONWriter::OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION |
-            base::JSONWriter::OPTIONS_PRETTY_PRINT,
-        &property_trees);
     VLOG(3) << "After activating sync tree, the active tree:"
             << "\nproperty_trees:\n"
-            << property_trees << "\ncc::LayerImpls:\n"
+            << active_tree_->property_trees()->ToString() << "\n"
+            << "cc::LayerImpls:\n"
             << active_tree_->LayerListAsJson();
   }
 }
diff --git a/cc/trees/property_tree.cc b/cc/trees/property_tree.cc
index b3fff07..efcce44 100644
--- a/cc/trees/property_tree.cc
+++ b/cc/trees/property_tree.cc
@@ -7,6 +7,7 @@
 #include <set>
 #include <vector>
 
+#include "base/json/json_writer.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/numerics/checked_math.h"
@@ -1996,6 +1997,16 @@
   return value;
 }
 
+std::string PropertyTrees::ToString() const {
+  std::string str;
+  base::JSONWriter::WriteWithOptions(
+      *AsTracedValue()->ToBaseValue(),
+      base::JSONWriter::OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION |
+          base::JSONWriter::OPTIONS_PRETTY_PRINT,
+      &str);
+  return str;
+}
+
 CombinedAnimationScale PropertyTrees::GetAnimationScales(
     int transform_node_id,
     LayerTreeImpl* layer_tree_impl) {
diff --git a/cc/trees/property_tree.h b/cc/trees/property_tree.h
index 6c01aa1..d5ae6e7 100644
--- a/cc/trees/property_tree.h
+++ b/cc/trees/property_tree.h
@@ -683,6 +683,7 @@
   }
 
   std::unique_ptr<base::trace_event::TracedValue> AsTracedValue() const;
+  std::string ToString() const;
 
   CombinedAnimationScale GetAnimationScales(int transform_node_id,
                                             LayerTreeImpl* layer_tree_impl);
diff --git a/chrome/VERSION b/chrome/VERSION
index d1b845eb..0215fc7 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=73
 MINOR=0
-BUILD=3661
+BUILD=3662
 PATCH=0
diff --git a/chrome/android/java/res/layout/autofill_name_fixflow.xml b/chrome/android/java/res/layout/autofill_name_fixflow.xml
index 96ebfcec..45cce8b2 100644
--- a/chrome/android/java/res/layout/autofill_name_fixflow.xml
+++ b/chrome/android/java/res/layout/autofill_name_fixflow.xml
@@ -10,6 +10,8 @@
     android:layout_height="wrap_content"
     style="@style/AlertDialogContent"
     android:minHeight="36dp"
+    android:focusable="true"
+    android:focusableInTouchMode="true"
     android:layout_marginBottom="32dp"
     android:paddingBottom="16dp"
     android:layout_marginTop="6dp"
@@ -19,7 +21,6 @@
     <org.chromium.chrome.browser.widget.CompatibilityTextInputLayout
         android:id="@+id/cc_name"
         android:labelFor="@+id/cc_name_edit"
-        android:focusableInTouchMode="true"
         android:layout_width="0dp"
         android:layout_weight="1"
         android:layout_height="wrap_content">
diff --git a/chrome/android/java/res/layout/keyboard_accessory_sheet_tab_password_info.xml b/chrome/android/java/res/layout/keyboard_accessory_sheet_tab_password_info.xml
new file mode 100644
index 0000000..6b0dfe6d
--- /dev/null
+++ b/chrome/android/java/res/layout/keyboard_accessory_sheet_tab_password_info.xml
@@ -0,0 +1,62 @@
+<?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. -->
+
+<org.chromium.chrome.browser.autofill.keyboard_accessory.PasswordAccessoryInfoView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:gravity="center_vertical|start"
+    android:fillViewport="true"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent"
+    android:paddingStart="@dimen/keyboard_accessory_suggestion_padding"
+    android:paddingEnd="@dimen/keyboard_accessory_suggestion_padding"
+    android:orientation="vertical">
+
+
+    <LinearLayout
+        android:gravity="center_vertical|start"
+        android:fillViewport="true"
+        android:layout_height="@dimen/keyboard_accessory_suggestion_height"
+        android:paddingTop="@dimen/keyboard_accessory_sheet_padding"
+        android:paddingBottom="@dimen/keyboard_accessory_sheet_padding"
+        android:layout_width="match_parent"
+        android:orientation="horizontal">
+
+        <org.chromium.ui.widget.ChipView
+            android:id="@+id/suggestion_text"
+            android:gravity="center_vertical|start"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            style="@style/InputChip" />
+
+        <Space
+            android:layout_weight="1"
+            android:layout_height="match_parent"
+            android:layout_width="0dp" />
+
+        <ImageView
+            android:id="@+id/favicon"
+            android:layout_width="@dimen/keyboard_accessory_suggestion_icon_size"
+            android:layout_height="@dimen/keyboard_accessory_suggestion_icon_size"
+            android:importantForAccessibility="no"
+            android:layout_gravity="center"/>
+
+    </LinearLayout>
+
+    <org.chromium.ui.widget.ChipView
+        android:id="@+id/password_text"
+        android:gravity="center_vertical|start"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/keyboard_accessory_sheet_padding"
+        android:layout_marginBottom="@dimen/keyboard_accessory_sheet_padding"
+        style="@style/InputChip" />
+
+    <View style="@style/HorizontalDivider"
+        android:layout_marginTop="@dimen/keyboard_accessory_sheet_padding"
+        android:layout_marginBottom="@dimen/keyboard_accessory_sheet_padding"
+        android:paddingStart="@dimen/keyboard_accessory_suggestion_padding"
+        android:paddingEnd="@dimen/keyboard_accessory_suggestion_padding" />
+
+</org.chromium.chrome.browser.autofill.keyboard_accessory.PasswordAccessoryInfoView>
diff --git a/chrome/android/java/res/layout/keyboard_accessory_sheet_tab_title.xml b/chrome/android/java/res/layout/keyboard_accessory_sheet_tab_title.xml
new file mode 100644
index 0000000..8ad2dcb
--- /dev/null
+++ b/chrome/android/java/res/layout/keyboard_accessory_sheet_tab_title.xml
@@ -0,0 +1,40 @@
+<?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. -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:gravity="center_vertical|start"
+    android:fillViewport="true"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent"
+    android:padding="0dp"
+    android:orientation="vertical">
+
+    <View style="@style/HorizontalDivider"
+        android:layout_marginTop="0dp"
+        android:layout_marginBottom="@dimen/keyboard_accessory_sheet_padding" />
+
+    <org.chromium.ui.widget.TextViewWithLeading
+        android:id="@+id/tab_title"
+        android:paddingStart="@dimen/keyboard_accessory_suggestion_padding"
+        android:paddingEnd="@dimen/keyboard_accessory_suggestion_padding"
+        android:paddingTop="@dimen/keyboard_accessory_suggestion_offset"
+        android:paddingBottom="@dimen/keyboard_accessory_suggestion_offset"
+        android:gravity="center_vertical|start"
+        android:textAppearance="@style/TextAppearance.BlackHint1"
+        android:minHeight="@dimen/keyboard_accessory_height"
+        app:leading="@dimen/text_size_large_leading"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"/>
+
+    <View style="@style/HorizontalDivider"
+        android:id="@+id/title_divider"
+        android:layout_marginTop="@dimen/keyboard_accessory_sheet_padding"
+        android:layout_marginBottom="@dimen/keyboard_accessory_sheet_padding"
+        android:layout_marginStart="@dimen/keyboard_accessory_suggestion_padding"
+        android:layout_marginEnd="@dimen/keyboard_accessory_suggestion_padding" />
+
+</LinearLayout>
diff --git a/chrome/android/java/res/layout/modal_dialog_title.xml b/chrome/android/java/res/layout/modal_dialog_title.xml
index 51202bd4..0b65398 100644
--- a/chrome/android/java/res/layout/modal_dialog_title.xml
+++ b/chrome/android/java/res/layout/modal_dialog_title.xml
@@ -20,5 +20,5 @@
         android:id="@+id/title"
         android:layout_width="wrap_content"
         android:layout_height="match_parent"
-        android:textAppearance="@style/TextAppearance.BlackHeadline" />
+        android:textAppearance="@style/TextAppearance.AlertDialogTitleStyle" />
 </LinearLayout>
\ No newline at end of file
diff --git a/chrome/android/java/res/layout/password_accessory_sheet_option.xml b/chrome/android/java/res/layout/password_accessory_sheet_option.xml
new file mode 100644
index 0000000..0f6f641
--- /dev/null
+++ b/chrome/android/java/res/layout/password_accessory_sheet_option.xml
@@ -0,0 +1,16 @@
+<?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. -->
+
+<TextView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:paddingStart="16dp"
+    android:paddingEnd="16dp"
+    android:fillViewport="true"
+    android:minHeight="48dp"
+    android:gravity="center_vertical|start"
+    android:textAppearance="@style/TextAppearance.BlackTitle1"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent"
+    android:background="?android:attr/selectableItemBackground"/>
diff --git a/chrome/android/java/res/values-v17/styles.xml b/chrome/android/java/res/values-v17/styles.xml
index 12df8d7..6599f5a 100644
--- a/chrome/android/java/res/values-v17/styles.xml
+++ b/chrome/android/java/res/values-v17/styles.xml
@@ -106,7 +106,7 @@
 
     <style name="AlertDialogTheme" parent="Theme.AppCompat.Light.Dialog.Alert">
         <item name="android:windowBackground">@drawable/popup_bg</item>
-        <item name="android:windowTitleStyle">@style/AlertDialogTitleStyle</item>
+        <item name="android:windowTitleStyle">@style/TextAppearance.AlertDialogTitleStyle</item>
         <item name="android:textColorHighlight">@color/text_highlight_color</item>
 
         <!--  Overriding AppCompat values -->
@@ -118,8 +118,15 @@
         <item name="spinnerStyle">@style/SpinnerStyle</item>
     </style>
 
-    <style name="AlertDialogTitleStyle" parent="RtlOverlay.DialogWindowTitle.AppCompat">
-        <item name="android:textAppearance">@style/TextAppearance.BlackHeadline</item>
+    <!-- Styled to match BlackHeadline but setup to override values in the app compat parent theme.
+         Note that the fontFamily doesn't get applied on older versions of Android.
+         See https://crbug.com/918697.-->
+    <style name="TextAppearance.AlertDialogTitleStyle"
+           parent="RtlOverlay.DialogWindowTitle.AppCompat">
+        <item name="android:textColor">@color/default_text_color_list</item>
+        <item name="android:textSize">@dimen/headline_size</item>
+        <item name="android:fontFamily">@font/accent_font</item>
+        <item name="android:textStyle">normal</item>
     </style>
 
     <!-- The dim amount should match the alpha of modal_dialog_scrim_color. -->
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
index 06fe4b1a..67a0fc4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -103,7 +103,6 @@
 import org.chromium.chrome.browser.metrics.LaunchMetrics;
 import org.chromium.chrome.browser.metrics.UmaSessionStats;
 import org.chromium.chrome.browser.modaldialog.AppModalPresenter;
-import org.chromium.chrome.browser.modaldialog.ModalDialogManager;
 import org.chromium.chrome.browser.multiwindow.MultiWindowUtils;
 import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
 import org.chromium.chrome.browser.nfc.BeamController;
@@ -173,6 +172,7 @@
 import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.display.DisplayAndroid;
 import org.chromium.ui.display.DisplayUtil;
+import org.chromium.ui.modaldialog.ModalDialogManager;
 import org.chromium.ui.widget.Toast;
 import org.chromium.webapk.lib.client.WebApkNavigationClient;
 import org.chromium.webapk.lib.client.WebApkValidator;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index 211654c..b87a061 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -94,7 +94,6 @@
 import org.chromium.chrome.browser.metrics.ActivityStopMetrics;
 import org.chromium.chrome.browser.metrics.LaunchMetrics;
 import org.chromium.chrome.browser.metrics.MainIntentBehaviorMetrics;
-import org.chromium.chrome.browser.modaldialog.ModalDialogManager;
 import org.chromium.chrome.browser.modaldialog.TabModalLifetimeHandler;
 import org.chromium.chrome.browser.multiwindow.MultiInstanceChromeTabbedActivity;
 import org.chromium.chrome.browser.multiwindow.MultiWindowUtils;
@@ -154,6 +153,7 @@
 import org.chromium.ui.UiUtils;
 import org.chromium.ui.base.PageTransition;
 import org.chromium.ui.base.WindowAndroid;
+import org.chromium.ui.modaldialog.ModalDialogManager;
 import org.chromium.ui.widget.Toast;
 
 import java.lang.annotation.Retention;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillNameFixFlowBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillNameFixFlowBridge.java
index 6dc25cb..6bc5f7c0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillNameFixFlowBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillNameFixFlowBridge.java
@@ -12,8 +12,8 @@
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ResourceId;
 import org.chromium.chrome.browser.autofill.AutofillNameFixFlowPrompt.AutofillNameFixFlowPromptDelegate;
-import org.chromium.chrome.browser.modaldialog.DialogDismissalCause;
 import org.chromium.ui.base.WindowAndroid;
+import org.chromium.ui.modaldialog.DialogDismissalCause;
 
 /**
  * JNI call glue for AutofillNameFixFlowPrompt C++ and Java objects.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillNameFixFlowPrompt.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillNameFixFlowPrompt.java
index 736bb1d..3b549e4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillNameFixFlowPrompt.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillNameFixFlowPrompt.java
@@ -20,16 +20,15 @@
 
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
-import org.chromium.chrome.browser.modaldialog.DialogDismissalCause;
-import org.chromium.chrome.browser.modaldialog.ModalDialogManager;
-import org.chromium.chrome.browser.modaldialog.ModalDialogProperties;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
+import org.chromium.ui.modaldialog.DialogDismissalCause;
+import org.chromium.ui.modaldialog.ModalDialogManager;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * Prompt that asks users to confirm user's name before saving card to Google.
  */
-public class AutofillNameFixFlowPrompt implements TextWatcher, ModalDialogView.Controller {
+public class AutofillNameFixFlowPrompt implements TextWatcher, ModalDialogProperties.Controller {
     /**
      * An interface to handle the interaction with
      * an AutofillNameFixFlowPrompt object.
@@ -87,7 +86,7 @@
         // Hitting the "submit" button on the software keyboard should submit.
         mUserNameInput.setOnEditorActionListener((view, actionId, event) -> {
             if (actionId == EditorInfo.IME_ACTION_DONE) {
-                onClick(mDialogModel, ModalDialogView.ButtonType.POSITIVE);
+                onClick(mDialogModel, ModalDialogProperties.ButtonType.POSITIVE);
                 return true;
             }
             return false;
@@ -143,9 +142,9 @@
 
     @Override
     public void onClick(PropertyModel model, int buttonType) {
-        if (buttonType == ModalDialogView.ButtonType.POSITIVE) {
+        if (buttonType == ModalDialogProperties.ButtonType.POSITIVE) {
             mDelegate.onUserAccept(mUserNameInput.getText().toString());
-        } else if (buttonType == ModalDialogView.ButtonType.NEGATIVE) {
+        } else if (buttonType == ModalDialogProperties.ButtonType.NEGATIVE) {
             mModalDialogManager.dismissDialog(model, DialogDismissalCause.NEGATIVE_BUTTON_CLICKED);
         }
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskBridge.java
index 02d3989..a1e57951 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskBridge.java
@@ -12,8 +12,8 @@
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ResourceId;
 import org.chromium.chrome.browser.autofill.CardUnmaskPrompt.CardUnmaskPromptDelegate;
-import org.chromium.chrome.browser.modaldialog.DialogDismissalCause;
 import org.chromium.ui.base.WindowAndroid;
+import org.chromium.ui.modaldialog.DialogDismissalCause;
 
 /**
 * JNI call glue for CardUnmaskPrompt C++ and Java objects.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java
index 3076555..b9b4c2d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java
@@ -38,11 +38,10 @@
 import org.chromium.base.task.AsyncTask;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
-import org.chromium.chrome.browser.modaldialog.DialogDismissalCause;
-import org.chromium.chrome.browser.modaldialog.ModalDialogManager;
-import org.chromium.chrome.browser.modaldialog.ModalDialogProperties;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
+import org.chromium.ui.modaldialog.DialogDismissalCause;
+import org.chromium.ui.modaldialog.ModalDialogManager;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
+import org.chromium.ui.modelutil.PropertyModel;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -51,7 +50,8 @@
 /**
  * A prompt that bugs users to enter their CVC when unmasking a Wallet instrument (credit card).
  */
-public class CardUnmaskPrompt implements TextWatcher, OnClickListener, ModalDialogView.Controller {
+public class CardUnmaskPrompt
+        implements TextWatcher, OnClickListener, ModalDialogProperties.Controller {
     private static CardUnmaskObserverForTest sObserverForTest;
 
     private final CardUnmaskPromptDelegate mDelegate;
@@ -218,7 +218,7 @@
         // Hitting the "submit" button on the software keyboard should submit the form if valid.
         mCardUnmaskInput.setOnEditorActionListener((v14, actionId, event) -> {
             if (actionId == EditorInfo.IME_ACTION_DONE) {
-                onClick(mDialogModel, ModalDialogView.ButtonType.POSITIVE);
+                onClick(mDialogModel, ModalDialogProperties.ButtonType.POSITIVE);
                 return true;
             }
             return false;
@@ -742,11 +742,11 @@
 
     @Override
     public void onClick(PropertyModel model, int buttonType) {
-        if (buttonType == ModalDialogView.ButtonType.POSITIVE) {
+        if (buttonType == ModalDialogProperties.ButtonType.POSITIVE) {
             mDelegate.onUserInput(mCardUnmaskInput.getText().toString(),
                     mMonthInput.getText().toString(), Integer.toString(getFourDigitYear()),
                     mStoreLocallyCheckbox != null && mStoreLocallyCheckbox.isChecked());
-        } else if (buttonType == ModalDialogView.ButtonType.NEGATIVE) {
+        } else if (buttonType == ModalDialogProperties.ButtonType.NEGATIVE) {
             mModalDialogManager.dismissDialog(model, DialogDismissalCause.NEGATIVE_BUTTON_CLICKED);
         }
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetCoordinator.java
index 8b2ee78..979f58c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetCoordinator.java
@@ -22,8 +22,8 @@
 import org.chromium.chrome.browser.modelutil.LazyConstructionPropertyMcp;
 import org.chromium.chrome.browser.modelutil.ListModel;
 import org.chromium.chrome.browser.modelutil.ListModelChangeProcessor;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.ui.ViewProvider;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * Creates and owns all elements which are part of the accessory sheet component.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetMediator.java
index c30da75..0b70441 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetMediator.java
@@ -17,9 +17,9 @@
 import android.support.v7.widget.RecyclerView;
 
 import org.chromium.base.VisibleForTesting;
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
-import org.chromium.chrome.browser.modelutil.PropertyObservable;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyObservable;
 
 /**
  * Contains the controller logic of the AccessorySheet component.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetProperties.java
index 5639a37..b8f870b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetProperties.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetProperties.java
@@ -8,10 +8,10 @@
 
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Tab;
 import org.chromium.chrome.browser.modelutil.ListModel;
-import org.chromium.chrome.browser.modelutil.PropertyModel.ReadableObjectPropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableBooleanPropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableIntPropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableObjectPropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.ReadableObjectPropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableBooleanPropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableIntPropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
 
 /**
  * This model holds all view state of the accessory sheet.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetTabViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetTabViewBinder.java
index 2bb5225..662edcd8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetTabViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetTabViewBinder.java
@@ -10,7 +10,10 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.TextView;
 
+import org.chromium.chrome.R;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetTabModel.AccessorySheetDataPiece;
 import org.chromium.chrome.browser.modelutil.ListModel;
 
@@ -35,6 +38,56 @@
         void bind(T t, V view) {}
     }
 
+    /**
+     * Creates an {@link ElementViewHolder} for the given |viewType|.
+     * @param parent A {@link android.view.ViewParent} to attach this view to.
+     * @param viewType A {@link AccessorySheetDataPiece.Type} describing the view to be created.
+     * @return A {@link ElementViewHolder}.
+     */
+    static ElementViewHolder create(ViewGroup parent, @AccessorySheetDataPiece.Type int viewType) {
+        switch (viewType) {
+            case AccessorySheetDataPiece.Type.TITLE:
+                return new TitleViewHolder(parent);
+            case AccessorySheetDataPiece.Type.FOOTER_COMMAND:
+                return new FooterCommandViewHolder(parent);
+        }
+        assert false : "Unhandled type of data piece: " + viewType;
+        return null;
+    }
+
+    /**
+     * Holds a Title consisting of a top divider, a text view and a bottom divider.
+     */
+    static class TitleViewHolder extends ElementViewHolder<String, LinearLayout> {
+        TitleViewHolder(ViewGroup parent) {
+            super(parent, R.layout.keyboard_accessory_sheet_tab_title);
+        }
+
+        @Override
+        protected void bind(String displayText, LinearLayout view) {
+            TextView titleView = view.findViewById(R.id.tab_title);
+            titleView.setText(displayText);
+            titleView.setContentDescription(displayText);
+        }
+    }
+
+    /**
+     * Holds a clickable {@link TextView} that represents a footer command.
+     */
+    static class FooterCommandViewHolder
+            extends ElementViewHolder<KeyboardAccessoryData.FooterCommand, TextView> {
+        FooterCommandViewHolder(ViewGroup parent) {
+            super(parent, R.layout.password_accessory_sheet_option);
+        }
+
+        @Override
+        protected void bind(KeyboardAccessoryData.FooterCommand footerCommand, TextView view) {
+            view.setText(footerCommand.getDisplayText());
+            view.setContentDescription(footerCommand.getDisplayText());
+            view.setOnClickListener(v -> footerCommand.execute());
+        }
+    }
+
     static void initializeView(
             RecyclerView view, @Nullable RecyclerView.OnScrollListener scrollListener) {
         view.setLayoutManager(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetViewBinder.java
index 36634a5..6533b8e7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetViewBinder.java
@@ -17,8 +17,8 @@
 import android.view.ViewGroup;
 import android.view.ViewParent;
 
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * Observes {@link AccessorySheetProperties} changes (like a newly available tab) and triggers the
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryCoordinator.java
index 02335fc..f270cbb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryCoordinator.java
@@ -20,12 +20,12 @@
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryViewBinder.ActionViewHolder;
 import org.chromium.chrome.browser.modelutil.LazyConstructionPropertyMcp;
 import org.chromium.chrome.browser.modelutil.ListModel;
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
 import org.chromium.chrome.browser.modelutil.RecyclerViewAdapter;
 import org.chromium.chrome.browser.modelutil.SimpleRecyclerViewMcp;
 import org.chromium.ui.ViewProvider;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * Creates and owns all elements which are part of the keyboard accessory component.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryMediator.java
index 7caab6aa0..cbd97d4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryMediator.java
@@ -19,9 +19,9 @@
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryCoordinator.VisibilityDelegate;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Action;
 import org.chromium.chrome.browser.modelutil.ListObservable;
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
-import org.chromium.chrome.browser.modelutil.PropertyObservable;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyObservable;
 
 import java.util.ArrayList;
 import java.util.Arrays;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryMetricsRecorder.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryMetricsRecorder.java
index 3e02394..3341d36 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryMetricsRecorder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryMetricsRecorder.java
@@ -16,9 +16,9 @@
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.UserInfo;
 import org.chromium.chrome.browser.modelutil.ListModel;
 import org.chromium.chrome.browser.modelutil.ListObservable;
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
-import org.chromium.chrome.browser.modelutil.PropertyObservable;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyObservable;
 
 import java.util.HashSet;
 import java.util.Set;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryModernViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryModernViewBinder.java
index c99a7ad..c55f71ba 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryModernViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryModernViewBinder.java
@@ -12,8 +12,8 @@
 import android.view.ViewGroup;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * Observes {@link KeyboardAccessoryProperties} changes (like a newly available tab) and triggers
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryProperties.java
index 01247c0..2719872 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryProperties.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryProperties.java
@@ -5,10 +5,10 @@
 package org.chromium.chrome.browser.autofill.keyboard_accessory;
 
 import org.chromium.chrome.browser.modelutil.ListModel;
-import org.chromium.chrome.browser.modelutil.PropertyModel.ReadableObjectPropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableBooleanPropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableIntPropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableObjectPropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.ReadableObjectPropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableBooleanPropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableIntPropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
 
 /**
  * As model of the keyboard accessory component, this class holds the data relevant to the visual
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutCoordinator.java
index 7dbc5ef0..59d4db4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutCoordinator.java
@@ -14,8 +14,8 @@
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.browser.modelutil.ListModel;
 import org.chromium.chrome.browser.modelutil.ListModelChangeProcessor;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * This component reflects the state of selected tabs in the keyboard accessory. It can be assigned
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutMediator.java
index 28eb65d..6ed6194 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutMediator.java
@@ -14,9 +14,9 @@
 
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryTabLayoutCoordinator.AccessoryTabObserver;
 import org.chromium.chrome.browser.modelutil.ListObservable;
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
-import org.chromium.chrome.browser.modelutil.PropertyObservable;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyObservable;
 
 /**
  * This mediator observes and changes a {@link PropertyModel} that contains the visual appearance of
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutProperties.java
index 824bbc8..9bd3994e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutProperties.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutProperties.java
@@ -7,8 +7,8 @@
 import android.support.design.widget.TabLayout;
 
 import org.chromium.chrome.browser.modelutil.ListModel;
-import org.chromium.chrome.browser.modelutil.PropertyModel.ReadableObjectPropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableObjectPropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.ReadableObjectPropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
 
 /**
  * These properties are used to describe a model for the tab layout component as used in the
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutViewBinder.java
index 0d1dbf1..02b1c67 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutViewBinder.java
@@ -13,8 +13,8 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.modelutil.ListModel;
 import org.chromium.chrome.browser.modelutil.ListModelChangeProcessor;
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * Stateless {@link ListModelChangeProcessor.ViewBinder} that binds a {@link ListModel}'s data to
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryViewBinder.java
index 8245b25..1fdfda6d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryViewBinder.java
@@ -20,8 +20,8 @@
 
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Action;
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * Observes {@link KeyboardAccessoryProperties} changes (like a newly available tab) and triggers
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessoryInfoView.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessoryInfoView.java
new file mode 100644
index 0000000..9b213a4
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessoryInfoView.java
@@ -0,0 +1,64 @@
+// 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.
+
+package org.chromium.chrome.browser.autofill.keyboard_accessory;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.Nullable;
+import android.support.v7.content.res.AppCompatResources;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+
+import org.chromium.chrome.R;
+import org.chromium.ui.widget.ChipView;
+
+/**
+ * This view represents a section of user credentials in the password tab of the keyboard accessory.
+ */
+class PasswordAccessoryInfoView extends LinearLayout {
+    private ImageView mIcon;
+    private ChipView mUsername;
+    private ChipView mPassword;
+
+    /**
+     * Constructor for inflating from XML.
+     */
+    public PasswordAccessoryInfoView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        mIcon = findViewById(R.id.favicon);
+        mUsername = findViewById(R.id.suggestion_text);
+        mPassword = findViewById(R.id.password_text);
+    }
+
+    void setIconForBitmap(@Nullable Bitmap favicon) {
+        Drawable icon;
+        if (favicon == null) {
+            icon = AppCompatResources.getDrawable(getContext(), R.drawable.ic_globe_36dp);
+        } else {
+            icon = new BitmapDrawable(getContext().getResources(), favicon);
+        }
+        final int kIconSize = getContext().getResources().getDimensionPixelSize(
+                R.dimen.keyboard_accessory_suggestion_icon_size);
+        icon.setBounds(0, 0, kIconSize, kIconSize);
+        mIcon.setImageDrawable(icon);
+    }
+
+    ChipView getUsername() {
+        return mUsername;
+    }
+
+    ChipView getPassword() {
+        return mPassword;
+    }
+}
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetCoordinator.java
index 2e8678e..696b0c5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetCoordinator.java
@@ -13,6 +13,7 @@
 
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetTabModel.AccessorySheetDataPiece;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.AccessorySheetData;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Provider;
@@ -72,7 +73,11 @@
     @Override
     public void onTabCreated(ViewGroup view) {
         super.onTabCreated(view);
-        PasswordAccessorySheetViewBinder.initializeView((RecyclerView) view, mModel);
+        if (ChromeFeatureList.isEnabled(ChromeFeatureList.AUTOFILL_KEYBOARD_ACCESSORY)) {
+            PasswordAccessorySheetModernViewBinder.initializeView((RecyclerView) view, mModel);
+        } else {
+            PasswordAccessorySheetViewBinder.initializeView((RecyclerView) view, mModel);
+        }
     }
 
     @Override
@@ -91,8 +96,8 @@
 
     /**
      * Creates an adapter to an {@link PasswordAccessorySheetViewBinder} that is wired
-     * up to a model change processor listening to the {@link ListModel <AccessorySheetDataPiece>}.
-     * @param model the {@link ListModel <Item>} the adapter gets its data from.
+     * up to a model change processor listening to the {@link AccessorySheetTabModel}.
+     * @param model the {@link AccessorySheetTabModel} the adapter gets its data from.
      * @return Returns a fully initialized and wired adapter to a PasswordAccessorySheetViewBinder.
      */
     static RecyclerViewAdapter<AccessorySheetTabViewBinder.ElementViewHolder, Void> createAdapter(
@@ -103,6 +108,20 @@
                 PasswordAccessorySheetViewBinder::create);
     }
 
+    /**
+     * Creates an adapter to an {@link PasswordAccessorySheetModernViewBinder} that is wired up to
+     * the model change processor which listens to the {@link AccessorySheetTabModel}.
+     * @param model the {@link AccessorySheetTabModel} the adapter gets its data from.
+     * @return Returns an {@link PasswordAccessorySheetModernViewBinder} wired to a MCP.
+     */
+    static RecyclerViewAdapter<AccessorySheetTabViewBinder.ElementViewHolder, Void>
+    createModernAdapter(ListModel<AccessorySheetDataPiece> model) {
+        return new RecyclerViewAdapter<>(
+                new SimpleRecyclerViewMcp<>(model, AccessorySheetDataPiece::getType,
+                        AccessorySheetTabViewBinder.ElementViewHolder::bind),
+                PasswordAccessorySheetModernViewBinder::create);
+    }
+
     @VisibleForTesting
     AccessorySheetTabModel getSheetDataPiecesForTesting() {
         return mModel;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetMediator.java
index 40d7f69..005ca05 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetMediator.java
@@ -9,8 +9,8 @@
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.AccessorySheetData;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.FooterCommand;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.UserInfo;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
+import org.chromium.ui.modelutil.PropertyModel;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetModernViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetModernViewBinder.java
new file mode 100644
index 0000000..6e66c88b
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetModernViewBinder.java
@@ -0,0 +1,71 @@
+// 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.
+
+package org.chromium.chrome.browser.autofill.keyboard_accessory;
+
+import android.support.v7.widget.RecyclerView;
+import android.text.method.PasswordTransformationMethod;
+import android.view.ViewGroup;
+
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetTabModel.AccessorySheetDataPiece;
+import org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetTabViewBinder.ElementViewHolder;
+import org.chromium.chrome.browser.modelutil.ListModel;
+import org.chromium.ui.widget.ChipView;
+
+/**
+ * This stateless class provides methods to bind a {@link ListModel<AccessorySheetDataPiece>}
+ * to the {@link RecyclerView} used as view of a tab for the accessory sheet component.
+ */
+class PasswordAccessorySheetModernViewBinder {
+    static ElementViewHolder create(ViewGroup parent, @AccessorySheetDataPiece.Type int viewType) {
+        switch (viewType) {
+            case AccessorySheetDataPiece.Type.PASSWORD_INFO:
+                return new PasswordInfoViewHolder(parent);
+            case AccessorySheetDataPiece.Type.TITLE: // Intentional fallthrough.
+            case AccessorySheetDataPiece.Type.FOOTER_COMMAND: // Intentional fallthrough.
+                return AccessorySheetTabViewBinder.create(parent, viewType);
+        }
+        assert false : "Unhandled type of data piece: " + viewType;
+        return null;
+    }
+
+    /**
+     * Holds a TextView that represents a list entry.
+     */
+    static class PasswordInfoViewHolder
+            extends ElementViewHolder<KeyboardAccessoryData.UserInfo, PasswordAccessoryInfoView> {
+        PasswordInfoViewHolder(ViewGroup parent) {
+            super(parent, R.layout.keyboard_accessory_sheet_tab_password_info);
+        }
+
+        @Override
+        protected void bind(KeyboardAccessoryData.UserInfo info, PasswordAccessoryInfoView view) {
+            bindChipView(view.getUsername(), info.getFields().get(0));
+            bindChipView(view.getPassword(), info.getFields().get(1));
+
+            view.setIconForBitmap(null); // Set the default icon, then try to get a better one.
+            if (info.getFaviconProvider() != null) {
+                info.getFaviconProvider().fetchFavicon(
+                        itemView.getContext().getResources().getDimensionPixelSize(
+                                R.dimen.keyboard_accessory_suggestion_icon_size),
+                        view::setIconForBitmap);
+            }
+        }
+
+        void bindChipView(ChipView chip, KeyboardAccessoryData.UserInfo.Field field) {
+            chip.getInnerTextView().setTransformationMethod(
+                    field.isObfuscated() ? new PasswordTransformationMethod() : null);
+            chip.getInnerTextView().setText(field.getDisplayText());
+            chip.getInnerTextView().setContentDescription(field.getA11yDescription());
+            chip.setOnClickListener(!field.isSelectable() ? null : src -> field.triggerSelection());
+            chip.setClickable(field.isSelectable());
+            chip.setEnabled(field.isSelectable());
+        }
+    }
+
+    static void initializeView(RecyclerView view, AccessorySheetTabModel model) {
+        view.setAdapter(PasswordAccessorySheetCoordinator.createModernAdapter(model));
+    }
+}
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManager.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManager.java
index 24fab23f..059497f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManager.java
@@ -152,8 +152,7 @@
 
         mToolbar = (BookmarkActionBar) mSelectableListLayout.initializeToolbar(
                 R.layout.bookmark_action_bar, mSelectionDelegate, 0, null, R.id.normal_menu_group,
-                R.id.selection_mode_menu_group, R.color.modern_primary_color, null, true,
-                isDialogUi);
+                R.id.selection_mode_menu_group, null, true, isDialogUi);
         mToolbar.initializeSearchView(
                 this, R.string.bookmark_action_bar_search, R.id.search_menu_id);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/TrustedWebActivityModel.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/TrustedWebActivityModel.java
index e5ad441..742dc3aa 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/TrustedWebActivityModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/TrustedWebActivityModel.java
@@ -8,7 +8,7 @@
 
 import org.chromium.chrome.browser.browserservices.Origin;
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModel;
 
 import javax.inject.Inject;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/view/PersistentNotificationView.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/view/PersistentNotificationView.java
index 49b7654d..6c3058a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/view/PersistentNotificationView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/view/PersistentNotificationView.java
@@ -32,13 +32,13 @@
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
 import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.Destroyable;
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyObservable;
-import org.chromium.chrome.browser.modelutil.PropertyObservable.PropertyObserver;
 import org.chromium.chrome.browser.notifications.NotificationBuilderFactory;
 import org.chromium.chrome.browser.notifications.channels.ChannelDefinitions;
 import org.chromium.chrome.browser.preferences.PreferencesLauncher;
 import org.chromium.chrome.browser.preferences.website.SettingsNavigationSource;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyObservable;
+import org.chromium.ui.modelutil.PropertyObservable.PropertyObserver;
 
 import javax.inject.Inject;
 import javax.inject.Named;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/view/TrustedWebActivityDisclosureView.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/view/TrustedWebActivityDisclosureView.java
index 1441f4e..f609819 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/view/TrustedWebActivityDisclosureView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/view/TrustedWebActivityDisclosureView.java
@@ -15,10 +15,10 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.browserservices.trustedwebactivityui.TrustedWebActivityModel;
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyObservable;
 import org.chromium.chrome.browser.snackbar.Snackbar;
 import org.chromium.chrome.browser.snackbar.SnackbarManager;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyObservable;
 
 import javax.inject.Inject;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/view/TrustedWebActivityToolbarView.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/view/TrustedWebActivityToolbarView.java
index 46e92fc..12db1f0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/view/TrustedWebActivityToolbarView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/view/TrustedWebActivityToolbarView.java
@@ -11,8 +11,8 @@
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
 import org.chromium.chrome.browser.fullscreen.FullscreenManager;
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyObservable;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyObservable;
 
 import javax.inject.Inject;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerCategoryView.java b/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerCategoryView.java
index e7b3d70..b3ae7003 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerCategoryView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerCategoryView.java
@@ -139,8 +139,8 @@
         int titleId = multiSelectionAllowed ? R.string.contacts_picker_select_contacts
                                             : R.string.contacts_picker_select_contact;
         mToolbar = (ContactsPickerToolbar) mSelectableListLayout.initializeToolbar(
-                R.layout.contacts_picker_toolbar, mSelectionDelegate, titleId, null, 0, 0,
-                R.color.modern_primary_color, null, false, false);
+                R.layout.contacts_picker_toolbar, mSelectionDelegate, titleId, null, 0, 0, null,
+                false, false);
         mToolbar.setNavigationOnClickListener(this);
         mToolbar.initializeSearchView(this, R.string.contacts_picker_search, 0);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsModel.java b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsModel.java
index ef9240c7..0d7b674 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsModel.java
@@ -7,8 +7,8 @@
 import android.view.View.OnClickListener;
 
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
-import org.chromium.chrome.browser.modelutil.PropertyObservable;
 import org.chromium.chrome.browser.widget.ListMenuButton;
+import org.chromium.ui.modelutil.PropertyObservable;
 
 import java.util.Arrays;
 import java.util.Collection;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadLocationDialogBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadLocationDialogBridge.java
index 381a6570..64823b2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadLocationDialogBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadLocationDialogBridge.java
@@ -12,14 +12,13 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.browser.ChromeActivity;
-import org.chromium.chrome.browser.modaldialog.DialogDismissalCause;
-import org.chromium.chrome.browser.modaldialog.ModalDialogManager;
-import org.chromium.chrome.browser.modaldialog.ModalDialogProperties;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
 import org.chromium.chrome.download.R;
 import org.chromium.ui.base.WindowAndroid;
+import org.chromium.ui.modaldialog.DialogDismissalCause;
+import org.chromium.ui.modaldialog.ModalDialogManager;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
+import org.chromium.ui.modelutil.PropertyModel;
 
 import java.io.File;
 import java.util.ArrayList;
@@ -27,7 +26,7 @@
 /**
  * Helper class to handle communication between download location dialog and native.
  */
-public class DownloadLocationDialogBridge implements ModalDialogView.Controller {
+public class DownloadLocationDialogBridge implements ModalDialogProperties.Controller {
     private long mNativeDownloadLocationDialogBridge;
     private PropertyModel mDialogModel;
     private DownloadLocationCustomView mCustomView;
@@ -78,11 +77,11 @@
     @Override
     public void onClick(PropertyModel model, int buttonType) {
         switch (buttonType) {
-            case ModalDialogView.ButtonType.POSITIVE:
+            case ModalDialogProperties.ButtonType.POSITIVE:
                 mModalDialogManager.dismissDialog(
                         model, DialogDismissalCause.POSITIVE_BUTTON_CLICKED);
                 break;
-            case ModalDialogView.ButtonType.NEGATIVE:
+            case ModalDialogProperties.ButtonType.NEGATIVE:
                 mModalDialogManager.dismissDialog(
                         model, DialogDismissalCause.NEGATIVE_BUTTON_CLICKED);
                 break;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/empty/EmptyCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/empty/EmptyCoordinator.java
index 86c2cbb..1dcfb46 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/empty/EmptyCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/empty/EmptyCoordinator.java
@@ -15,10 +15,10 @@
 import org.chromium.chrome.browser.download.home.filter.Filters.FilterType;
 import org.chromium.chrome.browser.download.home.filter.OfflineItemFilterObserver;
 import org.chromium.chrome.browser.download.home.filter.OfflineItemFilterSource;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
 import org.chromium.chrome.browser.offlinepages.prefetch.PrefetchConfiguration;
 import org.chromium.components.offline_items_collection.OfflineItem;
+import org.chromium.ui.modelutil.PropertyModel;
 
 import java.util.Collection;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/empty/EmptyProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/empty/EmptyProperties.java
index d5acc02c..53195c2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/empty/EmptyProperties.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/empty/EmptyProperties.java
@@ -6,8 +6,8 @@
 
 import android.support.annotation.IntDef;
 
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableIntPropertyKey;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableIntPropertyKey;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/empty/EmptyViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/empty/EmptyViewBinder.java
index 8c14b29b..6c349a0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/empty/EmptyViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/empty/EmptyViewBinder.java
@@ -4,9 +4,9 @@
 
 package org.chromium.chrome.browser.download.home.empty;
 
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor.ViewBinder;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * A helper {@link ViewBinder} responsible for gluing {@link EmptyProperties} to
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterCoordinator.java
index d380c9261..8526a6b6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterCoordinator.java
@@ -11,9 +11,9 @@
 import org.chromium.base.ObserverList;
 import org.chromium.chrome.browser.download.home.filter.Filters.FilterType;
 import org.chromium.chrome.browser.download.home.filter.chips.ChipsCoordinator;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
 import org.chromium.chrome.browser.offlinepages.prefetch.PrefetchConfiguration;
+import org.chromium.ui.modelutil.PropertyModel;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterProperties.java
index a963458..6dfbbd9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterProperties.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterProperties.java
@@ -7,10 +7,10 @@
 import android.view.View;
 
 import org.chromium.base.Callback;
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableBooleanPropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableIntPropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableObjectPropertyKey;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableBooleanPropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableIntPropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
 
 /** The properties needed to render the download home filter view. */
 public interface FilterProperties {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterViewBinder.java
index db6cd9d..011afcf5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterViewBinder.java
@@ -4,9 +4,9 @@
 
 package org.chromium.chrome.browser.download.home.filter;
 
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor.ViewBinder;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * A helper {@link ViewBinder} responsible for gluing {@link FilterProperties} to
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DecoratedListItemModel.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DecoratedListItemModel.java
index 9f1e884..b1e8e49 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DecoratedListItemModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DecoratedListItemModel.java
@@ -10,8 +10,8 @@
 import org.chromium.chrome.browser.modelutil.ListObservable;
 import org.chromium.chrome.browser.modelutil.ListObservable.ListObserver;
 import org.chromium.chrome.browser.modelutil.ListObservableImpl;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.modelutil.SimpleList;
+import org.chromium.ui.modelutil.PropertyModel;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListItemModel.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListItemModel.java
index 96db81d3..afe31ca 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListItemModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListItemModel.java
@@ -4,7 +4,7 @@
 
 package org.chromium.chrome.browser.download.home.list;
 
-import org.chromium.chrome.browser.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * This model represents the data required to build a list UI around a set of {@link ListItem}s.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListProperties.java
index 543c85b9..5b38d72 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListProperties.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListProperties.java
@@ -5,12 +5,12 @@
 package org.chromium.chrome.browser.download.home.list;
 
 import org.chromium.base.Callback;
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableBooleanPropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableObjectPropertyKey;
 import org.chromium.components.offline_items_collection.OfflineItem;
 import org.chromium.components.offline_items_collection.OfflineItemVisuals;
 import org.chromium.components.offline_items_collection.VisualsCallback;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableBooleanPropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
 
 import java.util.List;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListPropertyViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListPropertyViewBinder.java
index 71eb179..2d0947ff 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListPropertyViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListPropertyViewBinder.java
@@ -8,9 +8,9 @@
 import android.support.v7.widget.RecyclerView.ItemAnimator;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor.ViewBinder;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
 
 class ListPropertyViewBinder implements ViewBinder<PropertyModel, RecyclerView, PropertyKey> {
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/CustomViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/CustomViewHolder.java
index 70f84cb..d59dd4f2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/CustomViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/CustomViewHolder.java
@@ -10,7 +10,7 @@
 import android.widget.FrameLayout;
 
 import org.chromium.chrome.browser.download.home.list.ListItem;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /** A {@link RecyclerView.ViewHolder} that holds a {@link View} that is opaque to the holder. */
 public class CustomViewHolder extends ListItemViewHolder {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/GenericViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/GenericViewHolder.java
index 83cc8e7c..7a5163a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/GenericViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/GenericViewHolder.java
@@ -20,9 +20,9 @@
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.browser.download.home.list.ListItem;
 import org.chromium.chrome.browser.download.home.list.UiUtils;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.download.R;
 import org.chromium.components.offline_items_collection.OfflineItemVisuals;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /** A {@link RecyclerView.ViewHolder} specifically meant to display a generic {@code OfflineItem}.
  */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/ImageViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/ImageViewHolder.java
index b35d4443..03f995d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/ImageViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/ImageViewHolder.java
@@ -9,8 +9,8 @@
 import android.view.ViewGroup;
 
 import org.chromium.chrome.browser.download.home.list.ListItem;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.download.R;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /** A {@link RecyclerView.ViewHolder} specifically meant to display an image {@code OfflineItem}. */
 public class ImageViewHolder extends OfflineItemViewHolder {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/InProgressGenericViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/InProgressGenericViewHolder.java
index d5c6f2b..5571d7c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/InProgressGenericViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/InProgressGenericViewHolder.java
@@ -10,8 +10,8 @@
 import android.widget.TextView;
 
 import org.chromium.chrome.browser.download.home.list.ListItem;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.download.R;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * A {@link RecyclerView.ViewHolder} specifically meant to display an in-progress generic {@code
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/InProgressImageViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/InProgressImageViewHolder.java
index 76875eb..ea861ec 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/InProgressImageViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/InProgressImageViewHolder.java
@@ -11,8 +11,8 @@
 
 import org.chromium.chrome.browser.download.home.list.ListItem;
 import org.chromium.chrome.browser.download.home.list.view.AutoAnimatorDrawable;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.download.R;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * A {@link RecyclerView.ViewHolder} specifically meant to display an in-progress image {@code
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/InProgressVideoViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/InProgressVideoViewHolder.java
index 0a4e7b0..b73ae22 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/InProgressVideoViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/InProgressVideoViewHolder.java
@@ -10,8 +10,8 @@
 import android.widget.TextView;
 
 import org.chromium.chrome.browser.download.home.list.ListItem;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.download.R;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * A {@link RecyclerView.ViewHolder} specifically meant to display an in-progress video {@code
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/InProgressViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/InProgressViewHolder.java
index 976643c..2295ccb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/InProgressViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/InProgressViewHolder.java
@@ -13,10 +13,10 @@
 import org.chromium.chrome.browser.download.home.list.ListProperties;
 import org.chromium.chrome.browser.download.home.list.UiUtils;
 import org.chromium.chrome.browser.download.home.list.view.CircularProgressView;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.download.R;
 import org.chromium.components.offline_items_collection.OfflineItem;
 import org.chromium.components.offline_items_collection.OfflineItemState;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * A {@link RecyclerView.ViewHolder} specifically meant to display an in-progress {@code
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/ListItemViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/ListItemViewHolder.java
index 76dfd2d..823370a8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/ListItemViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/ListItemViewHolder.java
@@ -10,7 +10,7 @@
 
 import org.chromium.chrome.browser.download.home.list.ListItem;
 import org.chromium.chrome.browser.download.home.list.ListUtils;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * A {@link ViewHolder} responsible for building and setting properties on the underlying Android
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/OfflineItemViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/OfflineItemViewHolder.java
index 53e75fd..47d8b8c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/OfflineItemViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/OfflineItemViewHolder.java
@@ -16,11 +16,11 @@
 import org.chromium.chrome.browser.download.home.list.ListProperties;
 import org.chromium.chrome.browser.download.home.list.view.AsyncImageView;
 import org.chromium.chrome.browser.download.home.view.SelectionView;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.widget.ListMenuButton;
 import org.chromium.chrome.download.R;
 import org.chromium.components.offline_items_collection.OfflineItem;
 import org.chromium.components.offline_items_collection.OfflineItemVisuals;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * Helper that supports all typical actions for OfflineItems.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/PrefetchViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/PrefetchViewHolder.java
index 2ae68877..8d2dd3b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/PrefetchViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/PrefetchViewHolder.java
@@ -11,8 +11,8 @@
 
 import org.chromium.chrome.browser.download.home.list.ListItem;
 import org.chromium.chrome.browser.download.home.list.UiUtils;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.download.R;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * A {@link RecyclerView.ViewHolder} specifically meant to display a prefetch item.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/SectionTitleViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/SectionTitleViewHolder.java
index 66904e61..a7eafc35 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/SectionTitleViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/SectionTitleViewHolder.java
@@ -16,11 +16,11 @@
 import org.chromium.chrome.browser.download.home.list.ListProperties;
 import org.chromium.chrome.browser.download.home.list.ListUtils;
 import org.chromium.chrome.browser.download.home.list.UiUtils;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.widget.ListMenuButton;
 import org.chromium.chrome.download.R;
 import org.chromium.components.offline_items_collection.OfflineItem;
 import org.chromium.components.offline_items_collection.OfflineItemState;
+import org.chromium.ui.modelutil.PropertyModel;
 
 import java.util.ArrayList;
 import java.util.Collection;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/VideoViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/VideoViewHolder.java
index a1511fd..49315dd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/VideoViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/VideoViewHolder.java
@@ -11,9 +11,9 @@
 
 import org.chromium.chrome.browser.download.home.list.ListItem;
 import org.chromium.chrome.browser.download.home.list.UiUtils;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.download.R;
 import org.chromium.components.offline_items_collection.OfflineItem;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * A {@link RecyclerView.ViewHolder} specifically meant to display a video {@code OfflineItem}.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/storage/StorageCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/storage/StorageCoordinator.java
index fd440f6..e5c2f03ee 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/storage/StorageCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/storage/StorageCoordinator.java
@@ -10,11 +10,11 @@
 import android.widget.TextView;
 
 import org.chromium.chrome.browser.download.home.filter.OfflineItemFilterSource;
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableObjectPropertyKey;
 import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
 import org.chromium.chrome.download.R;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
 
 /**
  * The coordinator responsible for creating the storage summary view in download home.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/toolbar/ToolbarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/toolbar/ToolbarCoordinator.java
index b8543b05..23854e8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/toolbar/ToolbarCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/toolbar/ToolbarCoordinator.java
@@ -112,8 +112,7 @@
         mShadow = mView.findViewById(R.id.shadow);
 
         mToolbar.initialize(selectionDelegate, 0 /* titleResId */, null /* drawerLayout */,
-                normalMenuGroupId, R.id.selection_mode_menu_group, R.color.modern_primary_color,
-                hasCloseButton);
+                normalMenuGroupId, R.id.selection_mode_menu_group, hasCloseButton);
         mToolbar.setOnMenuItemClickListener(this ::onMenuItemClick);
 
         // TODO(crbug.com/881037): Pass the visible group to the toolbar during initialization.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java
index c5461e8d..3105bca 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java
@@ -236,8 +236,7 @@
 
         mToolbar = (DownloadManagerToolbar) mSelectableListLayout.initializeToolbar(
                 R.layout.download_manager_toolbar, mBackendProvider.getSelectionDelegate(), 0, null,
-                normalGroupId, R.id.selection_mode_menu_group, R.color.modern_primary_color, this,
-                true, isSeparateActivity);
+                normalGroupId, R.id.selection_mode_menu_group, this, true, isSeparateActivity);
         mToolbar.getMenu().setGroupVisible(normalGroupId, true);
         mToolbar.setManager(this);
         mToolbar.initialize(mFilterAdapter);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/CategoryCardAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/CategoryCardAdapter.java
index 6760e81..fc877165 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/CategoryCardAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/CategoryCardAdapter.java
@@ -14,15 +14,15 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.modelutil.ForwardingListObservable;
 import org.chromium.chrome.browser.modelutil.ListObservable.ListObserver;
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
-import org.chromium.chrome.browser.modelutil.PropertyObservable;
 import org.chromium.chrome.browser.modelutil.RecyclerViewAdapter;
 import org.chromium.chrome.browser.native_page.ContextMenuManager;
 import org.chromium.chrome.browser.native_page.NativePageNavigationDelegate;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.widget.LoadingView;
 import org.chromium.chrome.browser.widget.RoundedIconGenerator;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyObservable;
 import org.chromium.ui.widget.ChromeBulletSpan;
 import org.chromium.ui.widget.TextViewWithLeading;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryCardView.java b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryCardView.java
index 85d75fb..3236fab 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryCardView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryCardView.java
@@ -10,15 +10,11 @@
 import android.view.ContextMenu;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.View.OnCreateContextMenuListener;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
 import org.chromium.chrome.browser.native_page.ContextMenuManager;
 import org.chromium.chrome.browser.native_page.NativePageNavigationDelegate;
@@ -27,6 +23,8 @@
 import org.chromium.chrome.browser.widget.RoundedIconGenerator;
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.ui.base.PageTransition;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.mojom.WindowOpenDisposition;
 
 import java.util.ArrayList;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPage.java b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPage.java
index 27e4784..0cd8831 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPage.java
@@ -23,7 +23,6 @@
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.UrlConstants;
 import org.chromium.chrome.browser.modelutil.ListModel;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.modelutil.RecyclerViewAdapter;
 import org.chromium.chrome.browser.native_page.BasicNativePage;
 import org.chromium.chrome.browser.native_page.ContextMenuManager;
@@ -36,6 +35,7 @@
 import org.chromium.chrome.browser.widget.RoundedIconGenerator;
 import org.chromium.content_public.browser.NavigationController;
 import org.chromium.content_public.browser.NavigationEntry;
+import org.chromium.ui.modelutil.PropertyModel;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesSite.java b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesSite.java
index 0165271..2f740ef 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesSite.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesSite.java
@@ -7,7 +7,7 @@
 import android.graphics.Bitmap;
 
 import org.chromium.base.annotations.CalledByNative;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * An object encapsulating info for a website.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java
index 02e33c3..5068045 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java
@@ -125,8 +125,8 @@
         // 3. Initialize toolbar.
         mToolbar = (HistoryManagerToolbar) mSelectableListLayout.initializeToolbar(
                 R.layout.history_toolbar, mSelectionDelegate, R.string.menu_history, null,
-                R.id.normal_menu_group, R.id.selection_mode_menu_group,
-                R.color.modern_primary_color, this, true, isSeparateActivity);
+                R.id.normal_menu_group, R.id.selection_mode_menu_group, this, true,
+                isSeparateActivity);
         mToolbar.setManager(this);
         mToolbar.initializeSearchView(this, R.string.history_manager_search, R.id.search_menu_id);
         mToolbar.setInfoMenuItem(R.id.info_menu_id);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoDisclosureActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoDisclosureActivity.java
index 41b2ade..e4a675f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoDisclosureActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoDisclosureActivity.java
@@ -16,13 +16,11 @@
 import org.chromium.base.task.AsyncTask;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.modaldialog.AppModalPresenter;
-import org.chromium.chrome.browser.modaldialog.ModalDialogManager;
-import org.chromium.chrome.browser.modaldialog.ModalDialogManager.ModalDialogType;
-import org.chromium.chrome.browser.modaldialog.ModalDialogProperties;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView.ButtonType;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.ui.modaldialog.ModalDialogManager;
+import org.chromium.ui.modaldialog.ModalDialogManager.ModalDialogType;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * Activity that shows a dialog with a warning before opening an incognito Custom Tab when
@@ -78,25 +76,27 @@
         finish();
     }
 
-    private final ModalDialogView.Controller mDialogController = new ModalDialogView.Controller() {
-        @Override
-        public void onClick(PropertyModel model, int buttonType) {
-            if (buttonType == ButtonType.NEGATIVE) {
-                finish();
-                return;
-            }
-            if (mCloseIncognitoTabs) {
-                mOpenCustomTabAfterCleanUpTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
-            } else {
-                openCustomTabActivity();
-            }
-        }
+    private final ModalDialogProperties.Controller mDialogController =
+            new ModalDialogProperties.Controller() {
+                @Override
+                public void onClick(PropertyModel model, int buttonType) {
+                    if (buttonType == ModalDialogProperties.ButtonType.NEGATIVE) {
+                        finish();
+                        return;
+                    }
+                    if (mCloseIncognitoTabs) {
+                        mOpenCustomTabAfterCleanUpTask.executeOnExecutor(
+                                AsyncTask.THREAD_POOL_EXECUTOR);
+                    } else {
+                        openCustomTabActivity();
+                    }
+                }
 
-        @Override
-        public void onDismiss(PropertyModel model, int dismissalCause) {
-            finish();
-        }
-    };
+                @Override
+                public void onDismiss(PropertyModel model, int dismissalCause) {
+                    finish();
+                }
+            };
 
     private AsyncTask<Void> mOpenCustomTabAfterCleanUpTask = new AsyncTask<Void>() {
         @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java
index 0b6cf52..0ebe578 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java
@@ -40,7 +40,6 @@
 import org.chromium.chrome.browser.LaunchIntentDispatcher;
 import org.chromium.chrome.browser.WarmupManager;
 import org.chromium.chrome.browser.firstrun.FirstRunFlowSequencer;
-import org.chromium.chrome.browser.modaldialog.ModalDialogManager;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tabmodel.DocumentModeAssassin;
 import org.chromium.chrome.browser.upgrade.UpgradeActivity;
@@ -49,6 +48,7 @@
 import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.display.DisplayAndroid;
 import org.chromium.ui.display.DisplayUtil;
+import org.chromium.ui.modaldialog.ModalDialogManager;
 
 import java.lang.reflect.Field;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/jsdialog/JavascriptAppModalDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/jsdialog/JavascriptAppModalDialog.java
index 133eaeb2..7c6dd751 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/jsdialog/JavascriptAppModalDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/jsdialog/JavascriptAppModalDialog.java
@@ -8,9 +8,9 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
-import org.chromium.chrome.browser.modaldialog.DialogDismissalCause;
-import org.chromium.chrome.browser.modaldialog.ModalDialogManager;
 import org.chromium.ui.base.WindowAndroid;
+import org.chromium.ui.modaldialog.DialogDismissalCause;
+import org.chromium.ui.modaldialog.ModalDialogManager;
 
 /**
  * A dialog shown via JavaScript. This can be an alert dialog, a prompt dialog, a confirm dialog,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/jsdialog/JavascriptModalDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/jsdialog/JavascriptModalDialog.java
index e9c1230..8bdb60b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/jsdialog/JavascriptModalDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/jsdialog/JavascriptModalDialog.java
@@ -11,16 +11,15 @@
 import org.chromium.base.Log;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
-import org.chromium.chrome.browser.modaldialog.DialogDismissalCause;
-import org.chromium.chrome.browser.modaldialog.ModalDialogManager;
-import org.chromium.chrome.browser.modaldialog.ModalDialogProperties;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
+import org.chromium.ui.modaldialog.DialogDismissalCause;
+import org.chromium.ui.modaldialog.ModalDialogManager;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * A base class for creating, showing and dismissing a modal dialog for a JavaScript popup.
  */
-public abstract class JavascriptModalDialog implements ModalDialogView.Controller {
+public abstract class JavascriptModalDialog implements ModalDialogProperties.Controller {
     private static final String TAG = "JSModalDialog";
 
     private final String mTitle;
@@ -87,11 +86,11 @@
     public void onClick(PropertyModel model, int buttonType) {
         if (mModalDialogManager == null) return;
         switch (buttonType) {
-            case ModalDialogView.ButtonType.POSITIVE:
+            case ModalDialogProperties.ButtonType.POSITIVE:
                 mModalDialogManager.dismissDialog(
                         model, DialogDismissalCause.POSITIVE_BUTTON_CLICKED);
                 break;
-            case ModalDialogView.ButtonType.NEGATIVE:
+            case ModalDialogProperties.ButtonType.NEGATIVE:
                 mModalDialogManager.dismissDialog(
                         model, DialogDismissalCause.NEGATIVE_BUTTON_CLICKED);
                 break;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/jsdialog/JavascriptTabModalDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/jsdialog/JavascriptTabModalDialog.java
index 37b776a..ef0a7db 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/jsdialog/JavascriptTabModalDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/jsdialog/JavascriptTabModalDialog.java
@@ -7,9 +7,9 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
-import org.chromium.chrome.browser.modaldialog.DialogDismissalCause;
-import org.chromium.chrome.browser.modaldialog.ModalDialogManager;
 import org.chromium.ui.base.WindowAndroid;
+import org.chromium.ui.modaldialog.DialogDismissalCause;
+import org.chromium.ui.modaldialog.ModalDialogManager;
 
 /**
  * The controller to communicate with native JavaScriptDialogAndroid for a tab modal JavaScript
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/language/LanguageAskPrompt.java b/chrome/android/java/src/org/chromium/chrome/browser/language/LanguageAskPrompt.java
index e04aba0a..e28a11b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/language/LanguageAskPrompt.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/language/LanguageAskPrompt.java
@@ -22,15 +22,14 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeFeatureList;
-import org.chromium.chrome.browser.modaldialog.DialogDismissalCause;
-import org.chromium.chrome.browser.modaldialog.ModalDialogManager;
-import org.chromium.chrome.browser.modaldialog.ModalDialogProperties;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
 import org.chromium.chrome.browser.preferences.languages.LanguageItem;
 import org.chromium.components.language.AndroidLanguageMetricsBridge;
 import org.chromium.components.language.GeoLanguageProviderBridge;
+import org.chromium.ui.modaldialog.DialogDismissalCause;
+import org.chromium.ui.modaldialog.ModalDialogManager;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
+import org.chromium.ui.modelutil.PropertyModel;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -43,7 +42,7 @@
  * Implements a modal dialog that prompts the user about the languages they can read. Displayed
  * once at browser startup when no other promo or modals are shown.
  */
-public class LanguageAskPrompt implements ModalDialogView.Controller {
+public class LanguageAskPrompt implements ModalDialogProperties.Controller {
     // Enum values for the Translate.ExplicitLanguageAsk.Event histogram.
     private static final int PROMPT_EVENT_SHOWN = 0;
     private static final int PROMPT_EVENT_SAVED = 1;
@@ -361,7 +360,7 @@
 
     @Override
     public void onClick(PropertyModel model, int buttonType) {
-        if (buttonType == ModalDialogView.ButtonType.NEGATIVE) {
+        if (buttonType == ModalDialogProperties.ButtonType.NEGATIVE) {
             mModalDialogManager.dismissDialog(model, DialogDismissalCause.NEGATIVE_BUTTON_CLICKED);
         } else {
             saveLanguages();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleManager.java b/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleManager.java
index ae847f1c..cb89696 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleManager.java
@@ -147,13 +147,7 @@
      * @return Whether the Chrome instance is running in a special locale.
      */
     public boolean isSpecialLocaleEnabled() {
-        // If there is a kill switch sent from the server, disable the feature.
-        if (!ChromeFeatureList.isEnabled("SpecialLocaleWrapper")) {
-            return false;
-        }
-        boolean inSpecialLocale = ChromeFeatureList.isEnabled("SpecialLocale");
-        inSpecialLocale = isReallyInSpecialLocale(inSpecialLocale);
-        return inSpecialLocale;
+        return false;
     }
 
     /**
@@ -379,15 +373,6 @@
     }
 
     /**
-     * Does some extra checking about whether the user is in special locale.
-     * @param inSpecialLocale Whether the variation service thinks the client is in special locale.
-     * @return The result after extra confirmation.
-     */
-    protected boolean isReallyInSpecialLocale(boolean inSpecialLocale) {
-        return inSpecialLocale;
-    }
-
-    /**
      * @return Whether and which search engine promo should be shown.
      */
     @SearchEnginePromoType
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/AppModalPresenter.java b/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/AppModalPresenter.java
index 15a61074..df6a990 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/AppModalPresenter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/AppModalPresenter.java
@@ -9,9 +9,12 @@
 import android.view.LayoutInflater;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
+import org.chromium.ui.modaldialog.DialogDismissalCause;
+import org.chromium.ui.modaldialog.ModalDialogManager;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /** The presenter that shows a {@link ModalDialogView} in an Android dialog. */
 public class AppModalPresenter extends ModalDialogManager.Presenter {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/ModalDialogProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/ModalDialogProperties.java
deleted file mode 100644
index 55447552..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/ModalDialogProperties.java
+++ /dev/null
@@ -1,70 +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.
-
-package org.chromium.chrome.browser.modaldialog;
-
-import android.graphics.drawable.Drawable;
-import android.view.View;
-
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel.ReadableObjectPropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableBooleanPropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableObjectPropertyKey;
-
-/**
- * The model properties for a modal dialog.
- */
-public class ModalDialogProperties {
-    /** The {@link ModalDialogView.Controller} that handles events on user actions. */
-    public static final ReadableObjectPropertyKey<ModalDialogView.Controller> CONTROLLER =
-            new ReadableObjectPropertyKey<>();
-
-    /** The content description of the dialog for accessibility. */
-    public static final ReadableObjectPropertyKey<String> CONTENT_DESCRIPTION =
-            new ReadableObjectPropertyKey<>();
-
-    /** The title of the dialog. */
-    public static final WritableObjectPropertyKey<String> TITLE = new WritableObjectPropertyKey<>();
-
-    /** The title icon of the dialog. */
-    public static final WritableObjectPropertyKey<Drawable> TITLE_ICON =
-            new WritableObjectPropertyKey<>();
-
-    /** The message of the dialog. */
-    public static final WritableObjectPropertyKey<String> MESSAGE =
-            new WritableObjectPropertyKey<>();
-
-    /** The customized content view of the dialog. */
-    public static final WritableObjectPropertyKey<View> CUSTOM_VIEW =
-            new WritableObjectPropertyKey<>();
-
-    /** The text on the positive button. */
-    public static final WritableObjectPropertyKey<String> POSITIVE_BUTTON_TEXT =
-            new WritableObjectPropertyKey<>();
-
-    /** The enabled state on the positive button. */
-    public static final WritableBooleanPropertyKey POSITIVE_BUTTON_DISABLED =
-            new WritableBooleanPropertyKey();
-
-    /** The text on the negative button. */
-    public static final WritableObjectPropertyKey<String> NEGATIVE_BUTTON_TEXT =
-            new WritableObjectPropertyKey<>();
-
-    /** The enabled state on the negative button. */
-    public static final WritableBooleanPropertyKey NEGATIVE_BUTTON_DISABLED =
-            new WritableBooleanPropertyKey();
-
-    /** Whether the dialog should be dismissed on user tapping the scrim. */
-    public static final WritableBooleanPropertyKey CANCEL_ON_TOUCH_OUTSIDE =
-            new WritableBooleanPropertyKey();
-
-    /** Whether the title is scrollable with the message. */
-    public static final WritableBooleanPropertyKey TITLE_SCROLLABLE =
-            new WritableBooleanPropertyKey();
-
-    public static final PropertyKey[] ALL_KEYS = new PropertyKey[] {CONTROLLER, CONTENT_DESCRIPTION,
-            TITLE, TITLE_ICON, MESSAGE, CUSTOM_VIEW, POSITIVE_BUTTON_TEXT, POSITIVE_BUTTON_DISABLED,
-            NEGATIVE_BUTTON_TEXT, NEGATIVE_BUTTON_DISABLED, CANCEL_ON_TOUCH_OUTSIDE,
-            TITLE_SCROLLABLE};
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/ModalDialogView.java b/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/ModalDialogView.java
index 8e1ceab..c32c12a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/ModalDialogView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/ModalDialogView.java
@@ -6,7 +6,6 @@
 
 import android.content.Context;
 import android.graphics.drawable.Drawable;
-import android.support.annotation.IntDef;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.View;
@@ -17,51 +16,16 @@
 
 import org.chromium.base.Callback;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.widget.BoundedLinearLayout;
 import org.chromium.chrome.browser.widget.FadingEdgeScrollView;
 import org.chromium.ui.UiUtils;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
 
 /**
  * Generic dialog view for app modal or tab modal alert dialogs.
  */
 public class ModalDialogView extends BoundedLinearLayout implements View.OnClickListener {
-    /**
-     * Interface that controls the actions on the modal dialog.
-     */
-    public interface Controller {
-        /**
-         * Handle click event of the buttons on the dialog.
-         * @param model The dialog model that is associated with this click event.
-         * @param buttonType The type of the button.
-         */
-        void onClick(PropertyModel model, @ButtonType int buttonType);
-
-        /**
-         * Handle dismiss event when the dialog is dismissed by actions on the dialog. Note that it
-         * can be dangerous to the {@code dismissalCause} for business logic other than metrics
-         * recording, unless the dismissal cause is fully controlled by the client (e.g. button
-         * clicked), because the dismissal cause can be different values depending on modal dialog
-         * type and mode of presentation (e.g. it could be unknown on VR but a specific value on
-         * non-VR).
-         * @param model The dialog model that is associated with this dismiss event.
-         * @param dismissalCause The reason of the dialog being dismissed.
-         * @see DialogDismissalCause
-         */
-        void onDismiss(PropertyModel model, @DialogDismissalCause int dismissalCause);
-    }
-
-    @IntDef({ButtonType.POSITIVE, ButtonType.NEGATIVE})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ButtonType {
-        int POSITIVE = 0;
-        int NEGATIVE = 1;
-    }
-
-    private Controller mController;
+    private ModalDialogProperties.Controller mController;
 
     private FadingEdgeScrollView mScrollView;
     private ViewGroup mTitleContainer;
@@ -116,23 +80,24 @@
     @Override
     public void onClick(View view) {
         if (view == mPositiveButton) {
-            mOnButtonClickedCallback.onResult(ButtonType.POSITIVE);
+            mOnButtonClickedCallback.onResult(ModalDialogProperties.ButtonType.POSITIVE);
         } else if (view == mNegativeButton) {
-            mOnButtonClickedCallback.onResult(ButtonType.NEGATIVE);
+            mOnButtonClickedCallback.onResult(ModalDialogProperties.ButtonType.NEGATIVE);
         }
     }
 
     /**
      * @return The controller that controls the actions on the dialogs.
      */
-    public Controller getController() {
+    public ModalDialogProperties.Controller getController() {
         return mController;
     }
 
     /**
-     * @param controller The {@link Controller} that handles events on user actions.
+     * @param controller The {@link ModalDialogProperties.Controller} that handles events on user
+     *         actions.
      */
-    void setController(Controller controller) {
+    void setController(ModalDialogProperties.Controller controller) {
         mController = controller;
     }
 
@@ -214,11 +179,11 @@
     /**
      * @param buttonType Indicates which button should be returned.
      */
-    private Button getButton(@ButtonType int buttonType) {
+    private Button getButton(@ModalDialogProperties.ButtonType int buttonType) {
         switch (buttonType) {
-            case ButtonType.POSITIVE:
+            case ModalDialogProperties.ButtonType.POSITIVE:
                 return mPositiveButton;
-            case ButtonType.NEGATIVE:
+            case ModalDialogProperties.ButtonType.NEGATIVE:
                 return mNegativeButton;
             default:
                 assert false;
@@ -229,19 +194,19 @@
     /**
      * Sets button text for the specified button. If {@code buttonText} is empty or null, the
      * specified button will not be visible.
-     * @param buttonType The {@link ButtonType} of the button.
+     * @param buttonType The {@link ModalDialogProperties.ButtonType} of the button.
      * @param buttonText The text to be set on the specified button.
      */
-    void setButtonText(@ButtonType int buttonType, String buttonText) {
+    void setButtonText(@ModalDialogProperties.ButtonType int buttonType, String buttonText) {
         getButton(buttonType).setText(buttonText);
         updateButtonVisibility();
     }
 
     /**
-     * @param buttonType The {@link ButtonType} of the button.
+     * @param buttonType The {@link ModalDialogProperties.ButtonType} of the button.
      * @param enabled Whether the specified button should be enabled.
      */
-    void setButtonEnabled(@ButtonType int buttonType, boolean enabled) {
+    void setButtonEnabled(@ModalDialogProperties.ButtonType int buttonType, boolean enabled) {
         getButton(buttonType).setEnabled(enabled);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/ModalDialogViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/ModalDialogViewBinder.java
index 950afcf..0d4f45d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/ModalDialogViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/ModalDialogViewBinder.java
@@ -4,9 +4,10 @@
 
 package org.chromium.chrome.browser.modaldialog;
 
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * This class is responsible for binding view properties from {@link ModalDialogProperties} to a
@@ -26,16 +27,16 @@
         } else if (ModalDialogProperties.CUSTOM_VIEW == propertyKey) {
             view.setCustomView(model.get(ModalDialogProperties.CUSTOM_VIEW));
         } else if (ModalDialogProperties.POSITIVE_BUTTON_TEXT == propertyKey) {
-            view.setButtonText(ModalDialogView.ButtonType.POSITIVE,
+            view.setButtonText(ModalDialogProperties.ButtonType.POSITIVE,
                     model.get(ModalDialogProperties.POSITIVE_BUTTON_TEXT));
         } else if (ModalDialogProperties.POSITIVE_BUTTON_DISABLED == propertyKey) {
-            view.setButtonEnabled(ModalDialogView.ButtonType.POSITIVE,
+            view.setButtonEnabled(ModalDialogProperties.ButtonType.POSITIVE,
                     !model.get(ModalDialogProperties.POSITIVE_BUTTON_DISABLED));
         } else if (ModalDialogProperties.NEGATIVE_BUTTON_TEXT == propertyKey) {
-            view.setButtonText(ModalDialogView.ButtonType.NEGATIVE,
+            view.setButtonText(ModalDialogProperties.ButtonType.NEGATIVE,
                     model.get(ModalDialogProperties.NEGATIVE_BUTTON_TEXT));
         } else if (ModalDialogProperties.NEGATIVE_BUTTON_DISABLED == propertyKey) {
-            view.setButtonEnabled(ModalDialogView.ButtonType.NEGATIVE,
+            view.setButtonEnabled(ModalDialogProperties.ButtonType.NEGATIVE,
                     !model.get(ModalDialogProperties.NEGATIVE_BUTTON_DISABLED));
         } else if (ModalDialogProperties.TITLE_SCROLLABLE == propertyKey) {
             view.setTitleScrollable(model.get(ModalDialogProperties.TITLE_SCROLLABLE));
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/OWNERS
index 295ff6ad..d6ccfec 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/OWNERS
+++ b/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/OWNERS
@@ -1,4 +1,4 @@
-huayinz@chromium.org
+file://ui/android/java/src/org/chromium/ui/modaldialog/OWNERS
 
 # COMPONENT: UI>Browser>Mobile
 # OS: Android
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/TabModalLifetimeHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/TabModalLifetimeHandler.java
index c2b1566..3e779ea 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/TabModalLifetimeHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/TabModalLifetimeHandler.java
@@ -7,13 +7,15 @@
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.lifecycle.Destroyable;
 import org.chromium.chrome.browser.lifecycle.NativeInitObserver;
-import org.chromium.chrome.browser.modaldialog.ModalDialogManager.ModalDialogType;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabObserver;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabModelObserver;
+import org.chromium.ui.modaldialog.DialogDismissalCause;
+import org.chromium.ui.modaldialog.ModalDialogManager;
+import org.chromium.ui.modaldialog.ModalDialogManager.ModalDialogType;
 
 /**
  * Class responsible for handling dismissal of a tab modal dialog on user actions outside the tab
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/TabModalPresenter.java b/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/TabModalPresenter.java
index 29dc9b70..f90c28b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/TabModalPresenter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/TabModalPresenter.java
@@ -21,8 +21,6 @@
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel;
 import org.chromium.chrome.browser.contextualsearch.ContextualSearchManager;
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabBrowserControlsOffsetHelper;
@@ -34,6 +32,11 @@
 import org.chromium.content_public.common.BrowserControlsState;
 import org.chromium.ui.UiUtils;
 import org.chromium.ui.interpolators.BakedBezierInterpolator;
+import org.chromium.ui.modaldialog.DialogDismissalCause;
+import org.chromium.ui.modaldialog.ModalDialogManager;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * The presenter that displays a single tab modal dialog.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/LazyConstructionPropertyMcp.java b/chrome/android/java/src/org/chromium/chrome/browser/modelutil/LazyConstructionPropertyMcp.java
index 0b19398..5180423 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/LazyConstructionPropertyMcp.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/modelutil/LazyConstructionPropertyMcp.java
@@ -7,6 +7,9 @@
 import android.support.annotation.Nullable;
 
 import org.chromium.ui.ViewProvider;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyObservable;
 
 import java.util.HashSet;
 import java.util.Set;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/ModelListAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/modelutil/ModelListAdapter.java
index 200085d..d34b3b2a5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/ModelListAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/modelutil/ModelListAdapter.java
@@ -12,11 +12,13 @@
 import android.widget.BaseAdapter;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableBooleanPropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableFloatPropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableIntPropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableObjectPropertyKey;
 import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor.ViewBinder;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModel.WritableBooleanPropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableFloatPropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableIntPropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/modelutil/OWNERS
index 0fa757c..b5edb89d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/OWNERS
+++ b/chrome/android/java/src/org/chromium/chrome/browser/modelutil/OWNERS
@@ -1 +1,4 @@
-twellington@chromium.org
+file://ui/android/java/src/org/chromium/ui/modelutil/OWNERS
+
+# COMPONENT: UI>Browser>Mobile
+# OS: Android
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/PropertyListModel.java b/chrome/android/java/src/org/chromium/chrome/browser/modelutil/PropertyListModel.java
index bffbd7e..9e98b7ec 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/PropertyListModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/modelutil/PropertyListModel.java
@@ -6,6 +6,8 @@
 
 import android.support.annotation.Nullable;
 
+import org.chromium.ui.modelutil.PropertyObservable;
+
 import java.util.Collection;
 
 /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/PropertyModelChangeProcessor.java b/chrome/android/java/src/org/chromium/chrome/browser/modelutil/PropertyModelChangeProcessor.java
index 0fa72c8..854aaad 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/PropertyModelChangeProcessor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/modelutil/PropertyModelChangeProcessor.java
@@ -4,7 +4,8 @@
 
 package org.chromium.chrome.browser.modelutil;
 
-import org.chromium.chrome.browser.modelutil.PropertyObservable.PropertyObserver;
+import org.chromium.ui.modelutil.PropertyObservable;
+import org.chromium.ui.modelutil.PropertyObservable.PropertyObserver;
 
 /**
  * A model change processor for use with a {@link PropertyObservable} model. The
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticle.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticle.java
index 3d367713..8071496 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticle.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticle.java
@@ -9,9 +9,9 @@
 import android.support.annotation.Nullable;
 
 import org.chromium.base.DiscardableReferencePool.DiscardableReference;
-import org.chromium.chrome.browser.modelutil.PropertyObservable;
 import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder.PartialBindCallback;
 import org.chromium.chrome.browser.suggestions.OfflinableSuggestion;
+import org.chromium.ui.modelutil.PropertyObservable;
 
 import java.io.File;
 import java.util.Collection;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarCoordinator.java
index 53e42b60..0388838c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarCoordinator.java
@@ -9,12 +9,12 @@
 
 import org.chromium.base.Callback;
 import org.chromium.chrome.browser.WindowDelegate;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
 import org.chromium.chrome.browser.omnibox.UrlBar.ScrollType;
 import org.chromium.chrome.browser.omnibox.UrlBar.UrlBarDelegate;
 import org.chromium.chrome.browser.omnibox.UrlBar.UrlDirectionListener;
 import org.chromium.chrome.browser.omnibox.UrlBar.UrlTextChangeListener;
+import org.chromium.ui.modelutil.PropertyModel;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarMediator.java
index 1087c17..ce29481 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarMediator.java
@@ -16,7 +16,6 @@
 import org.chromium.base.Callback;
 import org.chromium.base.ContextUtils;
 import org.chromium.chrome.browser.WindowDelegate;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.omnibox.OmniboxUrlEmphasizer.UrlEmphasisSpan;
 import org.chromium.chrome.browser.omnibox.UrlBar.ScrollType;
 import org.chromium.chrome.browser.omnibox.UrlBar.UrlBarDelegate;
@@ -25,6 +24,7 @@
 import org.chromium.chrome.browser.omnibox.UrlBarCoordinator.SelectionState;
 import org.chromium.chrome.browser.omnibox.UrlBarProperties.AutocompleteText;
 import org.chromium.chrome.browser.omnibox.UrlBarProperties.UrlBarTextState;
+import org.chromium.ui.modelutil.PropertyModel;
 
 import java.net.MalformedURLException;
 import java.net.URL;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarProperties.java
index ca2a3db..e1f9d10 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarProperties.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarProperties.java
@@ -8,15 +8,15 @@
 
 import org.chromium.base.Callback;
 import org.chromium.chrome.browser.WindowDelegate;
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableBooleanPropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableObjectPropertyKey;
 import org.chromium.chrome.browser.omnibox.UrlBar.ScrollType;
 import org.chromium.chrome.browser.omnibox.UrlBar.UrlBarDelegate;
 import org.chromium.chrome.browser.omnibox.UrlBar.UrlBarTextContextMenuDelegate;
 import org.chromium.chrome.browser.omnibox.UrlBar.UrlDirectionListener;
 import org.chromium.chrome.browser.omnibox.UrlBar.UrlTextChangeListener;
 import org.chromium.chrome.browser.omnibox.UrlBarCoordinator.SelectionState;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableBooleanPropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
 
 import java.util.Locale;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarViewBinder.java
index 9a1f8df..b57bd7b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarViewBinder.java
@@ -13,10 +13,10 @@
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.Callback;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.omnibox.UrlBarProperties.AutocompleteText;
 import org.chromium.chrome.browser.omnibox.UrlBarProperties.UrlBarTextState;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * Handles translating the UrlBar model data to the view state.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
index bfae132..1d69784 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
@@ -10,7 +10,7 @@
 import android.view.View;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * Contains the controller logic of the Status component.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusProperties.java
index e86d7a18..23fc4ba 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusProperties.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusProperties.java
@@ -6,10 +6,10 @@
 
 import android.view.View;
 
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableBooleanPropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableIntPropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableObjectPropertyKey;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableBooleanPropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableIntPropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
 
 /**
  * Model for the Status view.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusViewBinder.java
index df7a8db00..61eec0f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusViewBinder.java
@@ -4,9 +4,9 @@
 
 package org.chromium.chrome.browser.omnibox.status;
 
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor.ViewBinder;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * StatusViewBinder observes StatusModel changes and triggers StatusView updates.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusViewCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusViewCoordinator.java
index dcecd81..610d14c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusViewCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusViewCoordinator.java
@@ -13,10 +13,10 @@
 
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
 import org.chromium.chrome.browser.page_info.PageInfoController;
 import org.chromium.chrome.browser.toolbar.ToolbarDataProvider;
+import org.chromium.ui.modelutil.PropertyModel;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java
index 7f8702f..d20c155 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java
@@ -19,7 +19,6 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.modelutil.LazyConstructionPropertyMcp;
 import org.chromium.chrome.browser.modelutil.ModelListAdapter;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.omnibox.LocationBarVoiceRecognitionHandler;
 import org.chromium.chrome.browser.omnibox.UrlBar.UrlTextChangeListener;
 import org.chromium.chrome.browser.omnibox.UrlBarEditingTextStateProvider;
@@ -33,6 +32,7 @@
 import org.chromium.ui.ViewProvider;
 import org.chromium.ui.base.PageTransition;
 import org.chromium.ui.base.WindowAndroid;
+import org.chromium.ui.modelutil.PropertyModel;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
index f72763f2..fe515f9b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
@@ -27,11 +27,6 @@
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.init.AsyncInitializationActivity;
-import org.chromium.chrome.browser.modaldialog.DialogDismissalCause;
-import org.chromium.chrome.browser.modaldialog.ModalDialogManager;
-import org.chromium.chrome.browser.modaldialog.ModalDialogProperties;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.omnibox.LocationBarVoiceRecognitionHandler;
 import org.chromium.chrome.browser.omnibox.MatchClassificationStyle;
 import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
@@ -49,6 +44,10 @@
 import org.chromium.ui.base.DeviceFormFactor;
 import org.chromium.ui.base.PageTransition;
 import org.chromium.ui.base.WindowAndroid;
+import org.chromium.ui.modaldialog.DialogDismissalCause;
+import org.chromium.ui.modaldialog.ModalDialogManager;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
+import org.chromium.ui.modelutil.PropertyModel;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -683,14 +682,14 @@
             return;
         }
 
-        ModalDialogView.Controller dialogController = new ModalDialogView.Controller() {
+        ModalDialogProperties.Controller dialogController = new ModalDialogProperties.Controller() {
             @Override
             public void onClick(PropertyModel model, int buttonType) {
-                if (buttonType == ModalDialogView.ButtonType.POSITIVE) {
+                if (buttonType == ModalDialogProperties.ButtonType.POSITIVE) {
                     RecordUserAction.record("MobileOmniboxDeleteRequested");
                     mAutocomplete.deleteSuggestion(position, suggestion.hashCode());
                     manager.dismissDialog(model, DialogDismissalCause.POSITIVE_BUTTON_CLICKED);
-                } else if (buttonType == ModalDialogView.ButtonType.NEGATIVE) {
+                } else if (buttonType == ModalDialogProperties.ButtonType.NEGATIVE) {
                     manager.dismissDialog(model, DialogDismissalCause.NEGATIVE_BUTTON_CLICKED);
                 }
             }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionListProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionListProperties.java
index a6f7236..ca80a402 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionListProperties.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionListProperties.java
@@ -6,11 +6,11 @@
 
 import android.util.Pair;
 
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableBooleanPropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableObjectPropertyKey;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionsList.OmniboxSuggestionListEmbedder;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModel.WritableBooleanPropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
 
 import java.util.List;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionListViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionListViewBinder.java
index c6a111e..2b3f176a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionListViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionListViewBinder.java
@@ -8,9 +8,9 @@
 import android.view.ViewGroup;
 
 import org.chromium.chrome.browser.modelutil.ModelListAdapter;
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.ui.UiUtils;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * Handles property updates to the suggestion list component.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionViewProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionViewProperties.java
index c9dfafd4..b3a4467 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionViewProperties.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionViewProperties.java
@@ -11,11 +11,11 @@
 import android.text.style.UpdateAppearance;
 import android.util.Pair;
 
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableBooleanPropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableIntPropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableObjectPropertyKey;
 import org.chromium.chrome.browser.omnibox.suggestions.SuggestionView.SuggestionViewDelegate;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableBooleanPropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableIntPropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionViewViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionViewViewBinder.java
index 66941da..25ce944 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionViewViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionViewViewBinder.java
@@ -15,10 +15,10 @@
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.omnibox.suggestions.SuggestionViewProperties.SuggestionIcon;
 import org.chromium.ui.base.DeviceFormFactor;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
 
 class SuggestionViewViewBinder {
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/page_info/ConnectionInfoPopup.java b/chrome/android/java/src/org/chromium/chrome/browser/page_info/ConnectionInfoPopup.java
index 1bababd..f82fb47 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/page_info/ConnectionInfoPopup.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/page_info/ConnectionInfoPopup.java
@@ -24,21 +24,20 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ResourceId;
-import org.chromium.chrome.browser.modaldialog.DialogDismissalCause;
-import org.chromium.chrome.browser.modaldialog.ModalDialogManager;
-import org.chromium.chrome.browser.modaldialog.ModalDialogProperties;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.vr.UiUnsupportedMode;
 import org.chromium.chrome.browser.vr.VrModuleProvider;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.content_public.browser.WebContentsObserver;
+import org.chromium.ui.modaldialog.DialogDismissalCause;
+import org.chromium.ui.modaldialog.ModalDialogManager;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * Java side of Android implementation of the page info UI.
  */
-public class ConnectionInfoPopup implements OnClickListener, ModalDialogView.Controller {
+public class ConnectionInfoPopup implements OnClickListener, ModalDialogProperties.Controller {
     private static final String TAG = "ConnectionInfoPopup";
 
     private static final String HELP_URL =
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoController.java b/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoController.java
index 4214905..93ad8f2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoController.java
@@ -31,10 +31,6 @@
 import org.chromium.chrome.browser.UrlConstants;
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.instantapps.InstantAppsHandler;
-import org.chromium.chrome.browser.modaldialog.DialogDismissalCause;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView.ButtonType;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.offlinepages.OfflinePageItem;
 import org.chromium.chrome.browser.offlinepages.OfflinePageUtils;
 import org.chromium.chrome.browser.omnibox.OmniboxUrlEmphasizer;
@@ -59,6 +55,10 @@
 import org.chromium.net.GURLUtils;
 import org.chromium.ui.base.DeviceFormFactor;
 import org.chromium.ui.base.WindowAndroid;
+import org.chromium.ui.modaldialog.DialogDismissalCause;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
+import org.chromium.ui.modaldialog.ModalDialogProperties.ButtonType;
+import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.text.NoUnderlineClickableSpan;
 import org.chromium.ui.text.SpanApplier;
 import org.chromium.ui.text.SpanApplier.SpanInfo;
@@ -75,7 +75,7 @@
  * Java side of Android implementation of the page info UI.
  */
 public class PageInfoController
-        implements ModalDialogView.Controller, SystemSettingsActivityRequiredListener {
+        implements ModalDialogProperties.Controller, SystemSettingsActivityRequiredListener {
     @IntDef({OpenedFromSource.MENU, OpenedFromSource.TOOLBAR, OpenedFromSource.VR})
     @Retention(RetentionPolicy.SOURCE)
     public @interface OpenedFromSource {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoDialog.java
index 14e0bab..248e16d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoDialog.java
@@ -21,12 +21,11 @@
 import android.widget.LinearLayout;
 import android.widget.ScrollView;
 
-import org.chromium.chrome.browser.modaldialog.DialogDismissalCause;
-import org.chromium.chrome.browser.modaldialog.ModalDialogManager;
-import org.chromium.chrome.browser.modaldialog.ModalDialogProperties;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.ui.interpolators.BakedBezierInterpolator;
+import org.chromium.ui.modaldialog.DialogDismissalCause;
+import org.chromium.ui.modaldialog.ModalDialogManager;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * Represents the dialog containing the page info view.
@@ -46,7 +45,7 @@
     @NonNull
     private final ModalDialogManager mManager;
     @NonNull
-    private final ModalDialogView.Controller mController;
+    private final ModalDialogProperties.Controller mController;
 
     // Animation which is currently running, if there is one.
     private Animator mCurrentAnimation;
@@ -67,7 +66,7 @@
      */
     public PageInfoDialog(Context context, @NonNull PageInfoView view, View tabView,
             boolean isSheet, @NonNull ModalDialogManager manager,
-            @NonNull ModalDialogView.Controller controller) {
+            @NonNull ModalDialogProperties.Controller controller) {
         mView = view;
         mIsSheet = isSheet;
         mManager = manager;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordGenerationDialogBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordGenerationDialogBridge.java
index 30c5a65..c5ae883 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordGenerationDialogBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordGenerationDialogBridge.java
@@ -6,8 +6,8 @@
 
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.chrome.browser.ChromeActivity;
-import org.chromium.chrome.browser.modaldialog.DialogDismissalCause;
 import org.chromium.ui.base.WindowAndroid;
+import org.chromium.ui.modaldialog.DialogDismissalCause;
 
 /**
  * JNI call glue between native password generation and Java objects.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordGenerationDialogCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordGenerationDialogCoordinator.java
index 87f3d26..573a8f6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordGenerationDialogCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordGenerationDialogCoordinator.java
@@ -10,9 +10,9 @@
 import org.chromium.base.Callback;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
-import org.chromium.chrome.browser.modaldialog.DialogDismissalCause;
-import org.chromium.chrome.browser.modaldialog.ModalDialogManager;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
+import org.chromium.ui.modaldialog.DialogDismissalCause;
+import org.chromium.ui.modaldialog.ModalDialogManager;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * The coordinator for the password generation modal dialog. Manages the sub-component objects
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordGenerationDialogMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordGenerationDialogMediator.java
index 64f7c2a..46a2dc1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordGenerationDialogMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordGenerationDialogMediator.java
@@ -9,13 +9,12 @@
 
 import org.chromium.base.Callback;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.modaldialog.ModalDialogProperties;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /** Mediator class responsible for initializing the model state. */
 public class PasswordGenerationDialogMediator {
-    private static class DialogController implements ModalDialogView.Controller {
+    private static class DialogController implements ModalDialogProperties.Controller {
         private final Callback<Boolean> mPasswordActionCallback;
 
         public DialogController(Callback<Boolean> passwordActionCallback) {
@@ -25,10 +24,10 @@
         @Override
         public void onClick(PropertyModel model, int buttonType) {
             switch (buttonType) {
-                case ModalDialogView.ButtonType.POSITIVE:
+                case ModalDialogProperties.ButtonType.POSITIVE:
                     mPasswordActionCallback.onResult(true);
                     break;
-                case ModalDialogView.ButtonType.NEGATIVE:
+                case ModalDialogProperties.ButtonType.NEGATIVE:
                     mPasswordActionCallback.onResult(false);
                     break;
                 default:
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordGenerationDialogModel.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordGenerationDialogModel.java
index a5efa50..091458af 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordGenerationDialogModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordGenerationDialogModel.java
@@ -4,7 +4,7 @@
 
 package org.chromium.chrome.browser.password_manager;
 
-import org.chromium.chrome.browser.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * Data model for the password generation modal dialog.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordGenerationDialogViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordGenerationDialogViewBinder.java
index 8e47763..697bcd350 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordGenerationDialogViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordGenerationDialogViewBinder.java
@@ -5,14 +5,15 @@
 package org.chromium.chrome.browser.password_manager;
 
 import org.chromium.base.Callback;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /** Class responsible for binding the model and the view. On bind, it lazily initializes the view
  * since all the needed data was made available at this point.
  */
 public class PasswordGenerationDialogViewBinder {
-    private static class PasswordGenerationDialogController implements ModalDialogView.Controller {
+    private static class PasswordGenerationDialogController
+            implements ModalDialogProperties.Controller {
         private final Callback<Boolean> mPasswordActionCallback;
 
         public PasswordGenerationDialogController(Callback<Boolean> passwordActionCallback) {
@@ -22,10 +23,10 @@
         @Override
         public void onClick(PropertyModel model, int buttonType) {
             switch (buttonType) {
-                case ModalDialogView.ButtonType.POSITIVE:
+                case ModalDialogProperties.ButtonType.POSITIVE:
                     mPasswordActionCallback.onResult(true);
                     break;
-                case ModalDialogView.ButtonType.NEGATIVE:
+                case ModalDialogProperties.ButtonType.NEGATIVE:
                     mPasswordActionCallback.onResult(false);
                     break;
                 default:
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/permissions/PermissionAppModalDialogView.java b/chrome/android/java/src/org/chromium/chrome/browser/permissions/PermissionAppModalDialogView.java
index b8187cb..f312877 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/permissions/PermissionAppModalDialogView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/permissions/PermissionAppModalDialogView.java
@@ -12,9 +12,8 @@
 import android.widget.TextView;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.modaldialog.ModalDialogProperties;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * The Permission dialog that is app modal.
@@ -23,7 +22,7 @@
     private final PropertyModel mDialogModel;
 
     PermissionAppModalDialogView(
-            ModalDialogView.Controller controller, PermissionDialogDelegate delegate) {
+            ModalDialogProperties.Controller controller, PermissionDialogDelegate delegate) {
         Context context = delegate.getTab().getActivity();
         LayoutInflater inflater = LayoutInflater.from(context);
         View customView = inflater.inflate(R.layout.permission_dialog, null);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/permissions/PermissionDialogController.java b/chrome/android/java/src/org/chromium/chrome/browser/permissions/PermissionDialogController.java
index 5b5990c..6153427 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/permissions/PermissionDialogController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/permissions/PermissionDialogController.java
@@ -12,13 +12,13 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeFeatureList;
-import org.chromium.chrome.browser.modaldialog.DialogDismissalCause;
-import org.chromium.chrome.browser.modaldialog.ModalDialogManager;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.vr.VrModuleProvider;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet;
 import org.chromium.chrome.browser.widget.bottomsheet.EmptyBottomSheetObserver;
+import org.chromium.ui.modaldialog.DialogDismissalCause;
+import org.chromium.ui.modaldialog.ModalDialogManager;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
+import org.chromium.ui.modelutil.PropertyModel;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -34,7 +34,7 @@
  * and will be displayed once the user responds to the current dialog.
  */
 public class PermissionDialogController
-        implements AndroidPermissionRequester.RequestDelegate, ModalDialogView.Controller {
+        implements AndroidPermissionRequester.RequestDelegate, ModalDialogProperties.Controller {
     @IntDef({State.NOT_SHOWING, State.PROMPT_PENDING, State.PROMPT_OPEN, State.PROMPT_ACCEPTED,
             State.PROMPT_DENIED, State.REQUEST_ANDROID_PERMISSIONS})
     @Retention(RetentionPolicy.SOURCE)
@@ -306,14 +306,14 @@
     }
 
     @Override
-    public void onClick(PropertyModel model, @ModalDialogView.ButtonType int buttonType) {
+    public void onClick(PropertyModel model, @ModalDialogProperties.ButtonType int buttonType) {
         switch (buttonType) {
-            case ModalDialogView.ButtonType.POSITIVE:
+            case ModalDialogProperties.ButtonType.POSITIVE:
                 mPositiveClickListener.onClick(null, 0);
                 mModalDialogManager.dismissDialog(
                         model, DialogDismissalCause.POSITIVE_BUTTON_CLICKED);
                 break;
-            case ModalDialogView.ButtonType.NEGATIVE:
+            case ModalDialogProperties.ButtonType.NEGATIVE:
                 mNegativeClickListener.onClick(null, 0);
                 mModalDialogManager.dismissDialog(
                         model, DialogDismissalCause.NEGATIVE_BUTTON_CLICKED);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerCategoryView.java b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerCategoryView.java
index 539f28c..15b1cda6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerCategoryView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerCategoryView.java
@@ -154,8 +154,8 @@
         int titleId = multiSelectionAllowed ? R.string.photo_picker_select_images
                                             : R.string.photo_picker_select_image;
         PhotoPickerToolbar toolbar = (PhotoPickerToolbar) mSelectableListLayout.initializeToolbar(
-                R.layout.photo_picker_toolbar, mSelectionDelegate, titleId, null, 0, 0,
-                R.color.default_primary_color, null, false, false);
+                R.layout.photo_picker_toolbar, mSelectionDelegate, titleId, null, 0, 0, null, false,
+                false);
         toolbar.setNavigationOnClickListener(this);
         Button doneButton = (Button) toolbar.findViewById(R.id.done);
         doneButton.setOnClickListener(this);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
index e3a2a41..b61883a8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
@@ -28,7 +28,6 @@
 import org.chromium.chrome.browser.init.SingleWindowKeyboardVisibilityDelegate;
 import org.chromium.chrome.browser.locale.LocaleManager;
 import org.chromium.chrome.browser.modaldialog.AppModalPresenter;
-import org.chromium.chrome.browser.modaldialog.ModalDialogManager;
 import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteController;
 import org.chromium.chrome.browser.snackbar.SnackbarManager;
 import org.chromium.chrome.browser.snackbar.SnackbarManager.SnackbarManageable;
@@ -42,6 +41,7 @@
 import org.chromium.content_public.common.ContentUrlConstants;
 import org.chromium.ui.base.ActivityKeyboardVisibilityDelegate;
 import org.chromium.ui.base.ActivityWindowAndroid;
+import org.chromium.ui.modaldialog.ModalDialogManager;
 
 /** Queries the user's default search engine and shows autocomplete suggestions. */
 public class SearchActivity extends AsyncInitializationActivity
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonCoordinator.java
index 98d0235..b190ab5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonCoordinator.java
@@ -9,7 +9,6 @@
 import android.view.ViewGroup;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver;
@@ -17,6 +16,7 @@
 import org.chromium.chrome.browser.toolbar.TabCountProvider.TabCountObserver;
 import org.chromium.chrome.browser.toolbar.ThemeColorProvider.ThemeColorObserver;
 import org.chromium.chrome.browser.util.AccessibilityUtil;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * The controller for the tab switcher button. This class handles all interactions that the tab
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonProperties.java
index 73fa6df..4f140ed 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonProperties.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonProperties.java
@@ -8,9 +8,9 @@
 import android.view.View.OnClickListener;
 import android.view.View.OnLongClickListener;
 
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableIntPropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableObjectPropertyKey;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableIntPropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
 
 /**
  * The properties needed to render the tab switcher button.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonViewBinder.java
index 208b744b..49d9450 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonViewBinder.java
@@ -4,9 +4,9 @@
 
 package org.chromium.chrome.browser.toolbar;
 
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * This class is responsible for pushing updates to the Android view of the tab switcher. These
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarModel.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarModel.java
index 4424c782..6b30f63 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarModel.java
@@ -7,7 +7,7 @@
 import org.chromium.chrome.browser.compositor.layouts.LayoutManager;
 import org.chromium.chrome.browser.compositor.layouts.ToolbarSwipeLayout;
 import org.chromium.chrome.browser.compositor.layouts.eventfilter.EdgeSwipeHandler;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.resources.ResourceManager;
 
 /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarViewBinder.java
index d06bfc28..a7a8731a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarViewBinder.java
@@ -9,8 +9,8 @@
 
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.compositor.scene_layer.ScrollingBottomViewSceneLayer;
-import org.chromium.chrome.browser.modelutil.PropertyKey;
 import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
+import org.chromium.ui.modelutil.PropertyKey;
 
 /**
  * This class is responsible for pushing updates to both the Android view and the compositor
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarModel.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarModel.java
index 7897f53..3eb5413 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarModel.java
@@ -4,7 +4,7 @@
 
 package org.chromium.chrome.browser.toolbar.bottom;
 
-import org.chromium.chrome.browser.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * All of the state for the tab switcher bottom toolbar, updated by the
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarViewBinder.java
index cf5afeb..7696f96 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarViewBinder.java
@@ -7,8 +7,8 @@
 import android.view.View;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.modelutil.PropertyKey;
 import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
+import org.chromium.ui.modelutil.PropertyKey;
 
 /**
  * This class is responsible for pushing updates the view of the tab switcher bottom toolbar. These
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr/VrAlertDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/vr/VrAlertDialog.java
index 594b7b92..03de5a8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr/VrAlertDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr/VrAlertDialog.java
@@ -9,11 +9,10 @@
 import android.content.DialogInterface;
 import android.view.View;
 
-import org.chromium.chrome.browser.modaldialog.DialogDismissalCause;
-import org.chromium.chrome.browser.modaldialog.ModalDialogManager;
-import org.chromium.chrome.browser.modaldialog.ModalDialogProperties;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
+import org.chromium.ui.modaldialog.DialogDismissalCause;
+import org.chromium.ui.modaldialog.ModalDialogManager;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * This class implements a VrAlertDialog which is similar to Android AlertDialog in VR.
@@ -89,10 +88,10 @@
                 || whichButton == DialogInterface.BUTTON_NEGATIVE);
         if (whichButton == DialogInterface.BUTTON_POSITIVE) {
             mButtonPositive = new DialogButton(
-                    ModalDialogView.ButtonType.POSITIVE, text.toString(), listener);
+                    ModalDialogProperties.ButtonType.POSITIVE, text.toString(), listener);
         } else if (whichButton == DialogInterface.BUTTON_NEGATIVE) {
             mButtonNegative = new DialogButton(
-                    ModalDialogView.ButtonType.NEGATIVE, text.toString(), listener);
+                    ModalDialogProperties.ButtonType.NEGATIVE, text.toString(), listener);
         }
     }
 
@@ -105,12 +104,12 @@
     }
 
     private PropertyModel createDialogModel() {
-        ModalDialogView.Controller controller = new ModalDialogView.Controller() {
+        ModalDialogProperties.Controller controller = new ModalDialogProperties.Controller() {
             @Override
             public void onClick(PropertyModel model, int buttonType) {
-                if (buttonType == ModalDialogView.ButtonType.POSITIVE) {
+                if (buttonType == ModalDialogProperties.ButtonType.POSITIVE) {
                     mButtonPositive.getListener().onClick(null, mButtonPositive.getId());
-                } else if (buttonType == ModalDialogView.ButtonType.NEGATIVE) {
+                } else if (buttonType == ModalDialogProperties.ButtonType.NEGATIVE) {
                     mButtonNegative.getListener().onClick(null, mButtonNegative.getId());
                 }
                 dismiss();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr/VrModalPresenter.java b/chrome/android/java/src/org/chromium/chrome/browser/vr/VrModalPresenter.java
index 28fd481..ec5d2a2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr/VrModalPresenter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr/VrModalPresenter.java
@@ -12,13 +12,13 @@
 import android.widget.FrameLayout;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.modaldialog.DialogDismissalCause;
-import org.chromium.chrome.browser.modaldialog.ModalDialogManager;
 import org.chromium.chrome.browser.modaldialog.ModalDialogView;
 import org.chromium.chrome.browser.modaldialog.ModalDialogViewBinder;
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
+import org.chromium.ui.modaldialog.DialogDismissalCause;
+import org.chromium.ui.modaldialog.ModalDialogManager;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /** The presenter that shows a {@link ModalDialogView} in an Android dialog. */
 public class VrModalPresenter extends ModalDialogManager.Presenter {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr/VrShell.java b/chrome/android/java/src/org/chromium/chrome/browser/vr/VrShell.java
index dfcb66b..9045e1e4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr/VrShell.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr/VrShell.java
@@ -35,8 +35,6 @@
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.compositor.CompositorView;
-import org.chromium.chrome.browser.modaldialog.DialogDismissalCause;
-import org.chromium.chrome.browser.modaldialog.ModalDialogManager;
 import org.chromium.chrome.browser.page_info.PageInfoController;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
@@ -62,6 +60,8 @@
 import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.display.DisplayAndroid;
 import org.chromium.ui.display.VirtualDisplayAndroid;
+import org.chromium.ui.modaldialog.DialogDismissalCause;
+import org.chromium.ui.modaldialog.ModalDialogManager;
 import org.chromium.ui.widget.UiWidgetFactory;
 
 import java.util.ArrayList;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr/VrUiWidgetFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/vr/VrUiWidgetFactory.java
index 5b56053..7eadc5a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr/VrUiWidgetFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr/VrUiWidgetFactory.java
@@ -9,7 +9,7 @@
 import android.content.Context;
 import android.widget.PopupWindow;
 
-import org.chromium.chrome.browser.modaldialog.ModalDialogManager;
+import org.chromium.ui.modaldialog.ModalDialogManager;
 import org.chromium.ui.widget.UiWidgetFactory;
 
 /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListLayout.java
index b7d60be..d073c8fe 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListLayout.java
@@ -192,9 +192,6 @@
      *                         established.
      * @param selectedGroupResId The resource id of the menu item to show when a selection is
      *                           established.
-     * @param normalBackgroundColorResId The resource id of the color to use as the background color
-     *                                   when selection is not enabled. If null the default appbar
-     *                                   background color will be used.
      * @param listener The OnMenuItemClickListener to set on the toolbar.
      * @param showShadowOnSelection Whether to show the toolbar shadow on selection.
      * @param updateStatusBarColor Whether the status bar color should be updated to match the
@@ -205,7 +202,6 @@
     public SelectableListToolbar<E> initializeToolbar(int toolbarLayoutId,
             SelectionDelegate<E> delegate, int titleResId, @Nullable DrawerLayout drawerLayout,
             int normalGroupResId, int selectedGroupResId,
-            @Nullable Integer normalBackgroundColorResId,
             @Nullable OnMenuItemClickListener listener, boolean showShadowOnSelection,
             boolean updateStatusBarColor) {
         mToolbarStub.setLayoutResource(toolbarLayoutId);
@@ -213,7 +209,7 @@
         SelectableListToolbar<E> toolbar = (SelectableListToolbar<E>) mToolbarStub.inflate();
         mToolbar = toolbar;
         mToolbar.initialize(delegate, titleResId, drawerLayout, normalGroupResId,
-                selectedGroupResId, normalBackgroundColorResId, updateStatusBarColor);
+                selectedGroupResId, updateStatusBarColor);
 
         if (listener != null) {
             mToolbar.setOnMenuItemClickListener(listener);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListToolbar.java
index 3112338..8e1e99f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListToolbar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListToolbar.java
@@ -167,16 +167,13 @@
      *                         established.
      * @param selectedGroupResId The resource id of the menu item to show when a selection is
      *                           established.
-     * @param normalBackgroundColorResId The resource id of the color to use as the background color
-     *                                   when selection is not enabled. If null the default appbar
-     *                                   background color will be used.
      * @param updateStatusBarColor Whether the status bar color should be updated to match the
      *                             toolbar color. If true, the status bar will only be updated if
      *                             the current device fully supports theming and is on Android M+.
      */
     public void initialize(SelectionDelegate<E> delegate, int titleResId,
             @Nullable DrawerLayout drawerLayout, int normalGroupResId, int selectedGroupResId,
-            @Nullable Integer normalBackgroundColorResId, boolean updateStatusBarColor) {
+            boolean updateStatusBarColor) {
         mTitleResId = titleResId;
         mDrawerLayout = drawerLayout;
         mNormalGroupResId = normalGroupResId;
@@ -198,11 +195,8 @@
 
         if (mDrawerLayout != null) initActionBarDrawerToggle();
 
-        normalBackgroundColorResId = normalBackgroundColorResId != null
-                ? normalBackgroundColorResId
-                : ColorUtils.getDefaultThemeColor(getResources(), false);
         mNormalBackgroundColor =
-                ApiCompatibilityUtils.getColor(getResources(), normalBackgroundColorResId);
+                ApiCompatibilityUtils.getColor(getResources(), R.color.modern_primary_color);
         setBackgroundColor(mNormalBackgroundColor);
 
         mSelectionBackgroundColor = ApiCompatibilityUtils.getColor(
@@ -213,6 +207,7 @@
         mLightIconColorList =
                 AppCompatResources.getColorStateList(getContext(), R.color.white_mode_tint);
 
+        setTitleTextAppearance(getContext(), R.style.TextAppearance_BlackHeadline);
         if (mTitleResId != 0) setTitle(mTitleResId);
 
         // TODO(twellington): add the concept of normal & selected tint to apply to all toolbar
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 530b0ad2..74c8120 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -127,9 +127,11 @@
   "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingBridge.java",
   "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingCoordinator.java",
   "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingMediator.java",
+  "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessoryInfoView.java",
   "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetCoordinator.java",
   "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetMediator.java",
   "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetViewBinder.java",
+  "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetModernViewBinder.java",
   "java/src/org/chromium/chrome/browser/autofill_assistant/AbstractAutofillAssistantUiController.java",
   "java/src/org/chromium/chrome/browser/autofill_assistant/AnimatedProgressBar.java",
   "java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFacade.java",
@@ -950,9 +952,6 @@
   "java/src/org/chromium/chrome/browser/metrics/WebApkUma.java",
   "java/src/org/chromium/chrome/browser/metrics/WebappUma.java",
   "java/src/org/chromium/chrome/browser/modaldialog/AppModalPresenter.java",
-  "java/src/org/chromium/chrome/browser/modaldialog/DialogDismissalCause.java",
-  "java/src/org/chromium/chrome/browser/modaldialog/ModalDialogManager.java",
-  "java/src/org/chromium/chrome/browser/modaldialog/ModalDialogProperties.java",
   "java/src/org/chromium/chrome/browser/modaldialog/ModalDialogView.java",
   "java/src/org/chromium/chrome/browser/modaldialog/ModalDialogViewBinder.java",
   "java/src/org/chromium/chrome/browser/modaldialog/TabModalLifetimeHandler.java",
@@ -965,11 +964,8 @@
   "java/src/org/chromium/chrome/browser/modelutil/ListObservable.java",
   "java/src/org/chromium/chrome/browser/modelutil/ListObservableImpl.java",
   "java/src/org/chromium/chrome/browser/modelutil/ModelListAdapter.java",
-  "java/src/org/chromium/chrome/browser/modelutil/PropertyKey.java",
   "java/src/org/chromium/chrome/browser/modelutil/PropertyListModel.java",
-  "java/src/org/chromium/chrome/browser/modelutil/PropertyModel.java",
   "java/src/org/chromium/chrome/browser/modelutil/PropertyModelChangeProcessor.java",
-  "java/src/org/chromium/chrome/browser/modelutil/PropertyObservable.java",
   "java/src/org/chromium/chrome/browser/modelutil/RecyclerViewAdapter.java",
   "java/src/org/chromium/chrome/browser/modelutil/SimpleList.java",
   "java/src/org/chromium/chrome/browser/modelutil/SimpleRecyclerViewMcp.java",
@@ -1910,6 +1906,7 @@
   "javatests/src/org/chromium/chrome/browser/autofill/AutofillTestHelper.java",
   "javatests/src/org/chromium/chrome/browser/autofill/PersonalDataManagerTest.java",
   "javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetViewTest.java",
+  "javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetTabViewTest.java",
   "javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/FakeKeyboard.java",
   "javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutViewTest.java",
   "javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryViewTest.java",
@@ -1918,6 +1915,7 @@
   "javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingUiCaptureTest.java",
   "javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessoryIntegrationTest.java",
   "javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetViewTest.java",
+  "javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetModernViewTest.java",
   "javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java",
   "javatests/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java",
   "javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkBridgeTest.java",
@@ -2072,8 +2070,10 @@
   "javatests/src/org/chromium/chrome/browser/metrics/PageLoadMetricsTest.java",
   "javatests/src/org/chromium/chrome/browser/metrics/StartupLoadingMetricsTest.java",
   "javatests/src/org/chromium/chrome/browser/metrics/UkmTest.java",
-  "javatests/src/org/chromium/chrome/browser/modaldialog/ModalDialogManagerTest.java",
+  "javatests/src/org/chromium/chrome/browser/modaldialog/AppModalPresenterTest.java",
+  "javatests/src/org/chromium/chrome/browser/modaldialog/ModalDialogTestUtils.java",
   "javatests/src/org/chromium/chrome/browser/modaldialog/ModalDialogViewTest.java",
+  "javatests/src/org/chromium/chrome/browser/modaldialog/TabModalPresenterTest.java",
   "javatests/src/org/chromium/chrome/browser/multiwindow/MultiWindowIntegrationTest.java",
   "javatests/src/org/chromium/chrome/browser/multiwindow/MultiWindowTestHelper.java",
   "javatests/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtilsTest.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/TabsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/TabsTest.java
index aa0d5d4..4050942 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/TabsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/TabsTest.java
@@ -54,8 +54,6 @@
 import org.chromium.chrome.browser.compositor.layouts.phone.stack.Stack;
 import org.chromium.chrome.browser.compositor.layouts.phone.stack.StackTab;
 import org.chromium.chrome.browser.jsdialog.JavascriptTabModalDialog;
-import org.chromium.chrome.browser.modaldialog.ModalDialogProperties;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabState;
@@ -87,6 +85,8 @@
 import org.chromium.content_public.browser.test.util.UiUtils;
 import org.chromium.content_public.common.ContentSwitches;
 import org.chromium.net.test.EmbeddedTestServer;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
+import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.test.util.UiRestriction;
 
 import java.io.File;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetTabViewTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetTabViewTest.java
new file mode 100644
index 0000000..0cf07e4
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetTabViewTest.java
@@ -0,0 +1,149 @@
+// 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.
+
+package org.chromium.chrome.browser.autofill.keyboard_accessory;
+
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+import android.support.annotation.LayoutRes;
+import android.support.test.filters.MediumTest;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.ChromeTabbedActivity;
+import org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetTabModel.AccessorySheetDataPiece;
+import org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetTabModel.AccessorySheetDataPiece.Type;
+import org.chromium.chrome.browser.modelutil.RecyclerViewAdapter;
+import org.chromium.chrome.browser.modelutil.SimpleRecyclerViewMcp;
+import org.chromium.chrome.test.ChromeActivityTestRule;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.content_public.browser.test.util.Criteria;
+import org.chromium.content_public.browser.test.util.CriteriaHelper;
+import org.chromium.ui.DeferredViewStubInflationProvider;
+import org.chromium.ui.widget.TextViewWithLeading;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * View tests for the password accessory sheet.
+ */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+public class AccessorySheetTabViewTest {
+    private final AccessorySheetTabModel mModel = new AccessorySheetTabModel();
+    private AtomicReference<RecyclerView> mView = new AtomicReference<>();
+
+    @Rule
+    public ChromeActivityTestRule<ChromeTabbedActivity> mActivityTestRule =
+            new ChromeActivityTestRule<>(ChromeTabbedActivity.class);
+
+    /**
+     * This helper method inflates the accessory sheet and loads the given layout as minimalistic
+     * Tab. The passed callback then allows access to the inflated layout.
+     * @param layout The layout to be inflated.
+     * @param listener Is called with the inflated layout when the Accessory Sheet initializes it.
+     */
+    private void openLayoutInAccessorySheet(
+            @LayoutRes int layout, KeyboardAccessoryData.Tab.Listener listener) {
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            AccessorySheetCoordinator accessorySheet =
+                    new AccessorySheetCoordinator(new DeferredViewStubInflationProvider<>(
+                            mActivityTestRule.getActivity().findViewById(
+                                    R.id.keyboard_accessory_sheet_stub)));
+            accessorySheet.addTab(new KeyboardAccessoryData.Tab(
+                    null, null, layout, AccessoryTabType.ALL, listener));
+            accessorySheet.setHeight(
+                    mActivityTestRule.getActivity().getResources().getDimensionPixelSize(
+                            R.dimen.keyboard_accessory_sheet_height));
+            accessorySheet.show();
+        });
+    }
+
+    @Before
+    public void setUp() throws InterruptedException {
+        mActivityTestRule.startMainActivityOnBlankPage();
+        openLayoutInAccessorySheet(
+                R.layout.password_accessory_sheet, new KeyboardAccessoryData.Tab.Listener() {
+                    @Override
+                    public void onTabCreated(ViewGroup view) {
+                        mView.set((RecyclerView) view);
+                        AccessorySheetTabViewBinder.initializeView(mView.get(), null);
+                        ((RecyclerView) view)
+                                .setAdapter(new RecyclerViewAdapter<>(
+                                        new SimpleRecyclerViewMcp<>(mModel,
+                                                AccessorySheetDataPiece::getType,
+                                                AccessorySheetTabViewBinder
+                                                        .ElementViewHolder::bind),
+                                        AccessorySheetTabViewBinder::create));
+                    }
+
+                    @Override
+                    public void onTabShown() {}
+                });
+        CriteriaHelper.pollUiThread(Criteria.equals(true, () -> mView.get() != null));
+    }
+
+    @After
+    public void tearDown() {
+        mView.set(null);
+    }
+
+    @Test
+    @MediumTest
+    public void testAddingATitleToTheModelRendersIt() {
+        assertThat(mView.get().getChildCount(), is(0));
+
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> { mModel.add(new AccessorySheetDataPiece("Passwords", Type.TITLE)); });
+
+        CriteriaHelper.pollUiThread(Criteria.equals(1, () -> mView.get().getChildCount()));
+        assertThat(mView.get().getChildAt(0), instanceOf(LinearLayout.class));
+        LinearLayout layout = (LinearLayout) mView.get().getChildAt(0);
+        assertThat(layout.getChildCount(), is(3));
+        assertThat(layout.getChildAt(0), instanceOf(View.class)); // The top divider.
+        assertThat(layout.getChildAt(1), instanceOf(TextViewWithLeading.class));
+        assertThat(layout.getChildAt(2), instanceOf(View.class)); // Divider to commands.
+        assertThat(((TextView) layout.getChildAt(1)).getText(), is("Passwords"));
+    }
+
+    @Test
+    @MediumTest
+    public void testAddingFooterCommandToTheModelRendersButton() throws ExecutionException {
+        final AtomicReference<Boolean> clicked = new AtomicReference<>(false);
+        assertThat(mView.get().getChildCount(), is(0));
+
+        ManualFillingTestHelper.createTestCredentials();
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            mModel.add(new AccessorySheetDataPiece(
+                    new KeyboardAccessoryData.FooterCommand(
+                            "Manage passwords", item -> clicked.set(true)),
+                    Type.FOOTER_COMMAND));
+        });
+
+        CriteriaHelper.pollUiThread(Criteria.equals(1, () -> mView.get().getChildCount()));
+        assertThat(mView.get().getChildAt(0), instanceOf(TextView.class));
+        TextView btn = (TextView) mView.get().getChildAt(0);
+
+        assertThat(btn.getText(), is("Manage passwords"));
+
+        ThreadUtils.runOnUiThreadBlocking(btn::performClick);
+        assertThat(clicked.get(), is(true));
+    }
+}
\ No newline at end of file
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetViewTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetViewTest.java
index 6f5cc8b..1b203e4 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetViewTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetViewTest.java
@@ -46,12 +46,12 @@
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Tab;
 import org.chromium.chrome.browser.modelutil.LazyConstructionPropertyMcp;
 import org.chromium.chrome.browser.modelutil.ListModel;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.ViewUtils;
 import org.chromium.ui.DeferredViewStubInflationProvider;
 import org.chromium.ui.ViewProvider;
+import org.chromium.ui.modelutil.PropertyModel;
 
 import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.BlockingQueue;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutViewTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutViewTest.java
index b54ab3d4..bbab0c2 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutViewTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutViewTest.java
@@ -40,9 +40,9 @@
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.modelutil.ListModel;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * View tests for the keyboard accessory tab layout component.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryViewTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryViewTest.java
index 7493fb0..e862cef3 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryViewTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryViewTest.java
@@ -44,11 +44,11 @@
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.modelutil.LazyConstructionPropertyMcp;
 import org.chromium.chrome.browser.modelutil.ListModel;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.ui.DeferredViewStubInflationProvider;
 import org.chromium.ui.ViewProvider;
+import org.chromium.ui.modelutil.PropertyModel;
 
 import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.BlockingQueue;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetModernViewTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetModernViewTest.java
new file mode 100644
index 0000000..980b5d3
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetModernViewTest.java
@@ -0,0 +1,156 @@
+// 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.
+
+package org.chromium.chrome.browser.autofill.keyboard_accessory;
+
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertThat;
+
+import android.support.test.filters.MediumTest;
+import android.support.v7.widget.RecyclerView;
+import android.text.method.PasswordTransformationMethod;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.ChromeTabbedActivity;
+import org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetTabModel.AccessorySheetDataPiece;
+import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.UserInfo;
+import org.chromium.chrome.test.ChromeActivityTestRule;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.content_public.browser.test.util.Criteria;
+import org.chromium.content_public.browser.test.util.CriteriaHelper;
+import org.chromium.ui.DeferredViewStubInflationProvider;
+import org.chromium.ui.widget.ChipView;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * View tests for the password accessory sheet.
+ */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+public class PasswordAccessorySheetModernViewTest {
+    private final AccessorySheetTabModel mModel = new AccessorySheetTabModel();
+    private AtomicReference<RecyclerView> mView = new AtomicReference<>();
+
+    @Rule
+    public ChromeActivityTestRule<ChromeTabbedActivity> mActivityTestRule =
+            new ChromeActivityTestRule<>(ChromeTabbedActivity.class);
+
+    @Before
+    public void setUp() throws InterruptedException {
+        mActivityTestRule.startMainActivityOnBlankPage();
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            AccessorySheetCoordinator accessorySheet =
+                    new AccessorySheetCoordinator(new DeferredViewStubInflationProvider<>(
+                            mActivityTestRule.getActivity().findViewById(
+                                    R.id.keyboard_accessory_sheet_stub)));
+            accessorySheet.addTab(
+                    new KeyboardAccessoryData.Tab(null, null, R.layout.password_accessory_sheet,
+                            AccessoryTabType.ALL, new KeyboardAccessoryData.Tab.Listener() {
+                                @Override
+                                public void onTabCreated(ViewGroup view) {
+                                    mView.set((RecyclerView) view);
+                                    AccessorySheetTabViewBinder.initializeView(mView.get(), null);
+                                    PasswordAccessorySheetModernViewBinder.initializeView(
+                                            mView.get(), mModel);
+                                }
+
+                                @Override
+                                public void onTabShown() {}
+                            }));
+            accessorySheet.setHeight(
+                    mActivityTestRule.getActivity().getResources().getDimensionPixelSize(
+                            R.dimen.keyboard_accessory_sheet_height));
+            accessorySheet.show();
+        });
+        CriteriaHelper.pollUiThread(Criteria.equals(true, () -> mView.get() != null));
+    }
+
+    @After
+    public void tearDown() {
+        mView.set(null);
+    }
+
+    @Test
+    @MediumTest
+    public void testAddingCaptionsToTheModelRendersThem() {
+        assertThat(mView.get().getChildCount(), is(0));
+
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            mModel.add(
+                    new AccessorySheetDataPiece("Passwords", AccessorySheetDataPiece.Type.TITLE));
+        });
+
+        CriteriaHelper.pollUiThread(Criteria.equals(1, () -> mView.get().getChildCount()));
+        View title = mView.get().findViewById(R.id.tab_title);
+        assertThat(title, is(not(nullValue())));
+        assertThat(title, instanceOf(TextView.class));
+        assertThat(((TextView) title).getText(), is("Passwords"));
+    }
+
+    @Test
+    @MediumTest
+    public void testAddingUserInfoToTheModelRendersClickableActions() throws ExecutionException {
+        final AtomicReference<Boolean> clicked = new AtomicReference<>(false);
+        assertThat(mView.get().getChildCount(), is(0));
+
+        UserInfo testInfo = new UserInfo(null);
+        testInfo.addField(new UserInfo.Field(
+                "Name Suggestion", "Name Suggestion", false, item -> clicked.set(true)));
+        testInfo.addField(new UserInfo.Field(
+                "Password Suggestion", "Password Suggestion", true, item -> clicked.set(true)));
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            mModel.add(new AccessorySheetDataPiece(
+                    testInfo, AccessorySheetDataPiece.Type.PASSWORD_INFO));
+        });
+
+        CriteriaHelper.pollUiThread(Criteria.equals(1, () -> mView.get().getChildCount()));
+
+        assertThat(getNameSuggestion().getInnerTextView().getText(), is("Name Suggestion"));
+        assertThat(getPasswordSuggestion().getInnerTextView().getText(), is("Password Suggestion"));
+        assertThat(getPasswordSuggestion().getInnerTextView().getTransformationMethod(),
+                instanceOf(PasswordTransformationMethod.class));
+
+        ThreadUtils.runOnUiThreadBlocking(getNameSuggestion()::performClick);
+        assertThat(clicked.get(), is(true));
+        clicked.set(false);
+        ThreadUtils.runOnUiThreadBlocking(getPasswordSuggestion()::performClick);
+        assertThat(clicked.get(), is(true));
+    }
+
+    private ChipView getNameSuggestion() {
+        assertThat(mView.get().getChildAt(0), instanceOf(LinearLayout.class));
+        LinearLayout layout = (LinearLayout) mView.get().getChildAt(0);
+        View view = layout.findViewById(R.id.suggestion_text);
+        assertThat(view, is(not(nullValue())));
+        assertThat(view, instanceOf(ChipView.class));
+        return (ChipView) view;
+    }
+
+    private ChipView getPasswordSuggestion() {
+        assertThat(mView.get().getChildAt(0), instanceOf(LinearLayout.class));
+        LinearLayout layout = (LinearLayout) mView.get().getChildAt(0);
+        View view = layout.findViewById(R.id.password_text);
+        assertThat(view, is(not(nullValue())));
+        assertThat(view, instanceOf(ChipView.class));
+        return (ChipView) view;
+    }
+}
\ No newline at end of file
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/jsdialog/JavascriptTabModalDialogTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/jsdialog/JavascriptTabModalDialogTest.java
index 1067ce5..1f82ae4 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/jsdialog/JavascriptTabModalDialogTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/jsdialog/JavascriptTabModalDialogTest.java
@@ -33,9 +33,6 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
-import org.chromium.chrome.browser.modaldialog.ModalDialogProperties;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.content_public.browser.LoadUrlParams;
@@ -43,6 +40,8 @@
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.content_public.browser.test.util.TestCallbackHelperContainer.OnEvaluateJavaScriptResultHelper;
 import org.chromium.ui.base.PageTransition;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
+import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.test.util.UiRestriction;
 
 import java.util.concurrent.ExecutionException;
@@ -105,8 +104,8 @@
 
         ThreadUtils.runOnUiThreadBlocking(() -> {
             PropertyModel model = mActivity.getModalDialogManager().getCurrentDialogForTest();
-            jsDialog.onClick(model, ModalDialogView.ButtonType.POSITIVE);
-            jsDialog.onClick(model, ModalDialogView.ButtonType.POSITIVE);
+            jsDialog.onClick(model, ModalDialogProperties.ButtonType.POSITIVE);
+            jsDialog.onClick(model, ModalDialogProperties.ButtonType.POSITIVE);
         });
 
         Assert.assertTrue("JavaScript execution should continue after closing prompt.",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/modaldialog/AppModalPresenterTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/modaldialog/AppModalPresenterTest.java
new file mode 100644
index 0000000..2f3620b
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/modaldialog/AppModalPresenterTest.java
@@ -0,0 +1,120 @@
+// 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.
+
+package org.chromium.chrome.browser.modaldialog;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
+import static org.chromium.chrome.browser.modaldialog.ModalDialogTestUtils.checkCurrentPresenter;
+import static org.chromium.chrome.browser.modaldialog.ModalDialogTestUtils.checkDialogDismissalCause;
+import static org.chromium.chrome.browser.modaldialog.ModalDialogTestUtils.checkPendingSize;
+import static org.chromium.chrome.browser.modaldialog.ModalDialogTestUtils.createDialog;
+import static org.chromium.chrome.browser.modaldialog.ModalDialogTestUtils.showDialog;
+
+import android.support.test.espresso.Espresso;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.util.CallbackHelper;
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.ChromeTabbedActivity;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
+import org.chromium.ui.modaldialog.DialogDismissalCause;
+import org.chromium.ui.modaldialog.ModalDialogManager;
+import org.chromium.ui.modaldialog.ModalDialogManager.ModalDialogType;
+import org.chromium.ui.modelutil.PropertyModel;
+
+/**
+ * Tests for {@link AppModalPresenter}.
+ */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+public class AppModalPresenterTest {
+    private class TestObserver implements ModalDialogTestUtils.TestDialogDismissedObserver {
+        public final CallbackHelper onDialogDismissedCallback = new CallbackHelper();
+
+        @Override
+        public void onDialogDismissed(int dismissalCause) {
+            onDialogDismissedCallback.notifyCalled();
+            checkDialogDismissalCause(mExpectedDismissalCause, dismissalCause);
+        }
+    }
+
+    @Rule
+    public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
+
+    private ChromeTabbedActivity mActivity;
+    private ModalDialogManager mManager;
+    private TestObserver mTestObserver;
+    private Integer mExpectedDismissalCause;
+
+    @Before
+    public void setUp() throws InterruptedException {
+        mActivityTestRule.startMainActivityOnBlankPage();
+        mActivity = mActivityTestRule.getActivity();
+        mManager = mActivity.getModalDialogManager();
+        mTestObserver = new TestObserver();
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"ModalDialog"})
+    public void testDismiss_BackPressed() throws Exception {
+        PropertyModel dialog1 = createDialog(mActivity, "1", null);
+        PropertyModel dialog2 = createDialog(mActivity, "2", null);
+
+        // Initially there are no dialogs in the pending list. Browser controls are not restricted.
+        checkPendingSize(mManager, ModalDialogType.APP, 0);
+        checkCurrentPresenter(mManager, null);
+
+        // Add three dialogs available for showing. The app modal dialog should be shown first.
+        showDialog(mManager, dialog1, ModalDialogType.APP);
+        showDialog(mManager, dialog2, ModalDialogType.APP);
+        checkPendingSize(mManager, ModalDialogType.APP, 1);
+        onView(withText("1")).check(matches(isDisplayed()));
+        checkCurrentPresenter(mManager, ModalDialogType.APP);
+
+        // Perform back press. The first app modal dialog should be dismissed, and the second one
+        // should be shown.
+        Espresso.pressBack();
+        checkPendingSize(mManager, ModalDialogType.APP, 0);
+        onView(withText("1")).check(doesNotExist());
+        onView(withText("2")).check(matches(isDisplayed()));
+        checkCurrentPresenter(mManager, ModalDialogType.APP);
+
+        // Perform a second back press. The second app modal dialog should be dismissed.
+        Espresso.pressBack();
+        checkPendingSize(mManager, ModalDialogType.APP, 0);
+        onView(withText("2")).check(doesNotExist());
+        checkCurrentPresenter(mManager, null);
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"ModalDialog"})
+    public void testDismiss_DismissalCause_BackPressed() throws Exception {
+        PropertyModel dialog1 = createDialog(mActivity, "1", mTestObserver);
+        mExpectedDismissalCause = DialogDismissalCause.NAVIGATE_BACK_OR_TOUCH_OUTSIDE;
+
+        showDialog(mManager, dialog1, ModalDialogType.APP);
+
+        // Dismiss the tab modal dialog and verify dismissal cause.
+        int callCount = mTestObserver.onDialogDismissedCallback.getCallCount();
+        Espresso.pressBack();
+        mTestObserver.onDialogDismissedCallback.waitForCallback(callCount);
+
+        mExpectedDismissalCause = null;
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/modaldialog/ModalDialogManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/modaldialog/ModalDialogManagerTest.java
deleted file mode 100644
index 52bbb74f0..0000000
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/modaldialog/ModalDialogManagerTest.java
+++ /dev/null
@@ -1,837 +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.
-
-package org.chromium.chrome.browser.modaldialog;
-
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.action.ViewActions.click;
-import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
-import static android.support.test.espresso.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
-import static android.support.test.espresso.matcher.ViewMatchers.isDescendantOfA;
-import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static android.support.test.espresso.matcher.ViewMatchers.isEnabled;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static android.support.test.espresso.matcher.ViewMatchers.withText;
-
-import static org.hamcrest.Matchers.allOf;
-import static org.hamcrest.Matchers.not;
-
-import android.content.res.Resources;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.espresso.Espresso;
-import android.support.test.filters.SmallTest;
-import android.view.View;
-import android.view.ViewGroup;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import org.chromium.base.ThreadUtils;
-import org.chromium.base.test.util.CallbackHelper;
-import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.DisabledTest;
-import org.chromium.base.test.util.Restriction;
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ChromeSwitches;
-import org.chromium.chrome.browser.ChromeTabbedActivity;
-import org.chromium.chrome.browser.modaldialog.ModalDialogManager.ModalDialogType;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
-import org.chromium.chrome.browser.omnibox.UrlFocusChangeListener;
-import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
-import org.chromium.chrome.test.util.ChromeTabUtils;
-import org.chromium.ui.test.util.UiRestriction;
-
-import java.util.List;
-
-/**
- * Tests for displaying and functioning of modal dialogs on tabs.
- */
-@RunWith(ChromeJUnit4ClassRunner.class)
-@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
-public class ModalDialogManagerTest {
-    private static class TestObserver implements UrlFocusChangeListener {
-        public final CallbackHelper onUrlFocusChangedCallback = new CallbackHelper();
-        public final CallbackHelper onDialogDismissedCallback = new CallbackHelper();
-
-        @Override
-        public void onUrlFocusChange(boolean hasFocus) {
-            onUrlFocusChangedCallback.notifyCalled();
-        }
-
-        public void onDialogDismissed() {
-            onDialogDismissedCallback.notifyCalled();
-        }
-    }
-
-    @Rule
-    public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
-
-    private static final int MAX_DIALOGS = 4;
-    private ChromeTabbedActivity mActivity;
-    private ModalDialogManager mManager;
-    private PropertyModel[] mDialogModels;
-    private TestObserver mTestObserver;
-    private Integer mExpectedDismissalCause;
-
-    @Before
-    public void setUp() throws Exception {
-        mActivityTestRule.startMainActivityOnBlankPage();
-        mActivity = mActivityTestRule.getActivity();
-        mManager = mActivity.getModalDialogManager();
-        mDialogModels = new PropertyModel[MAX_DIALOGS];
-        for (int i = 0; i < MAX_DIALOGS; i++) mDialogModels[i] = createDialog(i);
-        mTestObserver = new TestObserver();
-        mActivity.getToolbarManager()
-                .getToolbarLayoutForTesting()
-                .getLocationBar()
-                .addUrlFocusChangeListener(mTestObserver);
-        TabModalPresenter presenter =
-                (TabModalPresenter) mManager.getPresenterForTest(ModalDialogType.TAB);
-        presenter.disableAnimationForTest();
-    }
-
-    @Test
-    @SmallTest
-    public void testOneDialog() throws Exception {
-        // Initially there are no dialogs in the pending list. Browser controls are not restricted.
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 0);
-        checkBrowserControls(false);
-        checkCurrentPresenter(null);
-
-        // Show a dialog. The pending list should be empty, and the dialog should be showing.
-        // Browser controls should be restricted.
-        showDialog(0, ModalDialogType.TAB);
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 0);
-        onView(withId(R.id.tab_modal_dialog_container))
-                .check(matches(hasDescendant(withText("0"))));
-        checkBrowserControls(true);
-        checkCurrentPresenter(ModalDialogType.TAB);
-
-        // Dismiss the dialog by clicking positive button.
-        onView(withText(R.string.ok)).perform(click());
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 0);
-        onView(withId(R.id.tab_modal_dialog_container))
-                .check(matches(not(hasDescendant(withText("0")))));
-        checkBrowserControls(false);
-        checkCurrentPresenter(null);
-    }
-
-    @Test
-    @SmallTest
-    public void testTwoDialogs() throws Exception {
-        // Initially there are no dialogs in the pending list. Browser controls are not restricted.
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 0);
-        checkBrowserControls(false);
-        checkCurrentPresenter(null);
-
-        // Show the first dialog.
-        // The pending list should be empty, and the dialog should be showing.
-        // The tab modal container shouldn't be in the window hierarchy when an app modal dialog is
-        // showing.
-        showDialog(0, ModalDialogType.APP);
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 0);
-        onView(withText("0")).check(matches(isDisplayed()));
-        onView(withId(R.id.tab_modal_dialog_container)).check(doesNotExist());
-        checkCurrentPresenter(ModalDialogType.APP);
-
-        // Show the second dialog. It should be added to the pending list, and the first dialog
-        // should still be shown.
-        showDialog(1, ModalDialogType.TAB);
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 1);
-        onView(withText("0")).check(matches(isDisplayed()));
-        onView(withId(R.id.tab_modal_dialog_container)).check(doesNotExist());
-        checkCurrentPresenter(ModalDialogType.APP);
-
-        // Dismiss the first dialog by clicking cancel. The second dialog should be removed from
-        // pending list and shown immediately after.
-        onView(withText(R.string.cancel)).perform(click());
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 0);
-        onView(withText("0")).check(doesNotExist());
-        onView(withId(R.id.tab_modal_dialog_container))
-                .check(matches(
-                        allOf(not(hasDescendant(withText("0"))), hasDescendant(withText("1")))));
-        checkBrowserControls(true);
-        checkCurrentPresenter(ModalDialogType.TAB);
-
-        // Dismiss the second dialog by clicking ok. Browser controls should no longer be
-        // restricted.
-        onView(withText(R.string.ok)).perform(click());
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 0);
-        onView(withText("0")).check(doesNotExist());
-        onView(withId(R.id.tab_modal_dialog_container))
-                .check(matches(allOf(
-                        not(hasDescendant(withText("0"))), not(hasDescendant(withText("1"))))));
-        checkBrowserControls(false);
-        checkCurrentPresenter(null);
-    }
-
-    @Test
-    @SmallTest
-    public void testThreeDialogs() throws Exception {
-        // Initially there are no dialogs in the pending list. Browser controls are not restricted.
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 0);
-        checkBrowserControls(false);
-        checkCurrentPresenter(null);
-
-        // Show the first dialog.
-        // The pending list should be empty, and the dialog should be showing.
-        // Browser controls should be restricted.
-        showDialog(0, ModalDialogType.TAB);
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 0);
-        onView(withId(R.id.tab_modal_dialog_container))
-                .check(matches(hasDescendant(withText("0"))));
-        checkBrowserControls(true);
-        checkCurrentPresenter(ModalDialogType.TAB);
-
-        // Show the second dialog. It should be added to the pending list, and the first dialog
-        // should still be shown.
-        showDialog(1, ModalDialogType.TAB);
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 1);
-        onView(withId(R.id.tab_modal_dialog_container))
-                .check(matches(
-                        allOf(hasDescendant(withText("0")), not(hasDescendant(withText("1"))))));
-        checkBrowserControls(true);
-        checkCurrentPresenter(ModalDialogType.TAB);
-
-        // Show the third dialog (app modal). The first tab modal dialog should be back to the
-        // pending list and this app modal dialog should be shown right after.
-        showDialog(2, ModalDialogType.APP);
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 2);
-        onView(withText("2")).check(matches(isDisplayed()));
-        onView(withId(R.id.tab_modal_dialog_container)).check(doesNotExist());
-        checkCurrentPresenter(ModalDialogType.APP);
-
-        // Simulate dismissing the dialog by non-user action. The second dialog should be removed
-        // from pending list without showing.
-        dismissDialog(1);
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 1);
-        onView(withText("2")).check(matches(isDisplayed()));
-        onView(withId(R.id.tab_modal_dialog_container)).check(doesNotExist());
-        checkCurrentPresenter(ModalDialogType.APP);
-
-        // Dismiss the second dialog twice and verify nothing breaks.
-        dismissDialog(1);
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 1);
-        onView(withText("2")).check(matches(isDisplayed()));
-        onView(withId(R.id.tab_modal_dialog_container)).check(doesNotExist());
-        checkCurrentPresenter(ModalDialogType.APP);
-
-        // Dismiss the third dialog. The first dialog should be removed from pending list and
-        // shown immediately after. The tab modal container shouldn't be in the window hierarchy
-        // when an app modal dialog is showing.
-        dismissDialog(2);
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 0);
-        onView(withText("2")).check(doesNotExist());
-        onView(withId(R.id.tab_modal_dialog_container))
-                .check(matches(allOf(hasDescendant(withText("0")),
-                        not(hasDescendant(withText("1"))), not(hasDescendant(withText("2"))))));
-        checkBrowserControls(true);
-        checkCurrentPresenter(ModalDialogType.TAB);
-
-        // Dismiss the first dialog by clicking OK. Browser controls should no longer be restricted.
-        onView(withText(R.string.ok)).perform(click());
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 0);
-        onView(withText("2")).check(doesNotExist());
-        onView(withId(R.id.tab_modal_dialog_container))
-                .check(matches(allOf(not(hasDescendant(withText("0"))),
-                        not(hasDescendant(withText("1"))), not(hasDescendant(withText("2"))))));
-        checkBrowserControls(false);
-        checkCurrentPresenter(null);
-    }
-
-    @Test
-    @SmallTest
-    public void testShow_ShowNext() throws Exception {
-        // Initially there are no dialogs in the pending list. Browser controls are not restricted.
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 0);
-        checkBrowserControls(false);
-        checkCurrentPresenter(null);
-
-        // Insert two tab modal dialogs and two app modal dialogs for showing.
-        showDialog(0, ModalDialogType.TAB);
-        showDialog(1, ModalDialogType.TAB);
-        showDialog(2, ModalDialogType.APP);
-        showDialog(3, ModalDialogType.APP);
-
-        // The first inserted app modal dialog is shown.
-        checkPendingSize(ModalDialogType.APP, 1);
-        checkPendingSize(ModalDialogType.TAB, 2);
-        onView(withText("2")).check(matches(isDisplayed()));
-        onView(withId(R.id.tab_modal_dialog_container)).check(doesNotExist());
-        checkCurrentPresenter(ModalDialogType.APP);
-
-        // Dismiss the dialog by clicking OK. The second inserted app modal dialog should be shown.
-        onView(withText(R.string.ok)).perform(click());
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 2);
-        onView(withText("3")).check(matches(isDisplayed()));
-        onView(withId(R.id.tab_modal_dialog_container)).check(doesNotExist());
-        checkCurrentPresenter(ModalDialogType.APP);
-
-        // Dismiss the dialog by clicking OK. The first tab modal dialog should be shown.
-        onView(withText(R.string.ok)).perform(click());
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 1);
-        onView(withId(R.id.tab_modal_dialog_container))
-                .check(matches(allOf(hasDescendant(withText("0")),
-                        not(hasDescendant(withText("1"))), not(hasDescendant(withText("2"))),
-                        not(hasDescendant(withText("3"))))));
-        checkBrowserControls(true);
-        checkCurrentPresenter(ModalDialogType.TAB);
-
-        // Dismiss the dialog by clicking OK. The second tab modal dialog should be shown.
-        onView(withText(R.string.ok)).perform(click());
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 0);
-        onView(withId(R.id.tab_modal_dialog_container))
-                .check(matches(allOf(hasDescendant(withText("1")),
-                        not(hasDescendant(withText("0"))), not(hasDescendant(withText("2"))),
-                        not(hasDescendant(withText("3"))))));
-        checkBrowserControls(true);
-        checkCurrentPresenter(ModalDialogType.TAB);
-    }
-
-    @Test
-    @SmallTest
-    public void testShow_ShowDialogAsNext() throws Exception {
-        // Initially there are no dialogs in the pending list. Browser controls are not restricted.
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 0);
-        checkBrowserControls(false);
-        checkCurrentPresenter(null);
-
-        // Insert a tab modal dialog and a app modal dialog for showing.
-        showDialog(0, ModalDialogType.TAB);
-        showDialog(1, ModalDialogType.TAB);
-        showDialog(2, ModalDialogType.APP);
-
-        // The first inserted app modal dialog is shown.
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 2);
-        onView(withText("2")).check(matches(isDisplayed()));
-        onView(withId(R.id.tab_modal_dialog_container)).check(doesNotExist());
-        checkCurrentPresenter(ModalDialogType.APP);
-
-        // Show a tab modal dialog as the next dialog to be shown. Verify that the app modal dialog
-        // is still showing.
-        showDialogAsNext(3, ModalDialogType.TAB);
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 3);
-        onView(withText("2")).check(matches(isDisplayed()));
-        onView(withId(R.id.tab_modal_dialog_container)).check(doesNotExist());
-        checkCurrentPresenter(ModalDialogType.APP);
-
-        // Dismiss the dialog by clicking OK. The third tab modal dialog should be shown.
-        onView(withText(R.string.ok)).perform(click());
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 2);
-        onView(withId(R.id.tab_modal_dialog_container))
-                .check(matches(allOf(hasDescendant(withText("3")),
-                        not(hasDescendant(withText("0"))), not(hasDescendant(withText("1"))),
-                        not(hasDescendant(withText("2"))))));
-        checkBrowserControls(true);
-        checkCurrentPresenter(ModalDialogType.TAB);
-
-        // Dismiss the dialog by clicking OK. The first tab modal dialog should be shown.
-        onView(withText(R.string.ok)).perform(click());
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 1);
-        onView(withId(R.id.tab_modal_dialog_container))
-                .check(matches(allOf(hasDescendant(withText("0")),
-                        not(hasDescendant(withText("1"))), not(hasDescendant(withText("2"))),
-                        not(hasDescendant(withText("3"))))));
-        checkBrowserControls(true);
-        checkCurrentPresenter(ModalDialogType.TAB);
-    }
-
-    @Test
-    @SmallTest
-    @DisabledTest(message = "crbug.com/812066")
-    public void testShow_UrlBarFocused() throws Exception {
-        // Show a dialog. The dialog should be shown on top of the toolbar.
-        showDialog(0, ModalDialogType.TAB);
-
-        TabModalPresenter presenter =
-                (TabModalPresenter) mManager.getPresenterForTest(ModalDialogType.TAB);
-        final View dialogContainer = presenter.getDialogContainerForTest();
-        final View controlContainer = mActivity.findViewById(R.id.control_container);
-        final ViewGroup containerParent = presenter.getContainerParentForTest();
-
-        ThreadUtils.runOnUiThreadBlocking(() -> {
-            Assert.assertTrue(containerParent.indexOfChild(dialogContainer)
-                    > containerParent.indexOfChild(controlContainer));
-        });
-
-        // When editing URL, it should be shown on top of the dialog.
-        int callCount = mTestObserver.onUrlFocusChangedCallback.getCallCount();
-        onView(withId(R.id.url_bar)).perform(click());
-        mTestObserver.onUrlFocusChangedCallback.waitForCallback(callCount);
-        ThreadUtils.runOnUiThreadBlocking(() -> {
-            Assert.assertTrue(containerParent.indexOfChild(dialogContainer)
-                    < containerParent.indexOfChild(controlContainer));
-        });
-
-        // When URL bar is not focused, the dialog should be shown on top of the toolbar again.
-        callCount = mTestObserver.onUrlFocusChangedCallback.getCallCount();
-        Espresso.pressBack();
-        mTestObserver.onUrlFocusChangedCallback.waitForCallback(callCount);
-        ThreadUtils.runOnUiThreadBlocking(() -> {
-            Assert.assertTrue(containerParent.indexOfChild(dialogContainer)
-                    > containerParent.indexOfChild(controlContainer));
-        });
-
-        // Dismiss the dialog by clicking OK.
-        onView(withText(R.string.ok)).perform(click());
-    }
-
-    @Test
-    @SmallTest
-    @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
-    @DisabledTest(message = "crbug.com/804858")
-    public void testSuspend_ToggleOverview() throws Exception {
-        // Initially there are no dialogs in the pending list. Browser controls are not restricted.
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 0);
-        checkBrowserControls(false);
-        checkCurrentPresenter(null);
-
-        // Add two dialogs available for showing.
-        showDialog(0, ModalDialogType.TAB);
-        showDialog(1, ModalDialogType.TAB);
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 1);
-        onView(withId(R.id.tab_modal_dialog_container))
-                .check(matches(
-                        allOf(hasDescendant(withText("0")), not(hasDescendant(withText("1"))))));
-        checkBrowserControls(true);
-        checkCurrentPresenter(ModalDialogType.TAB);
-
-        //  Tab modal dialogs should be suspended on entering tab switcher.
-        onView(withId(R.id.tab_switcher_button)).perform(click());
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 2);
-        onView(withId(R.id.tab_modal_dialog_container))
-                .check(matches(allOf(
-                        not(hasDescendant(withText("0"))), not(hasDescendant(withText("1"))))));
-        checkBrowserControls(false);
-        checkCurrentPresenter(null);
-
-        // Exit overview mode. The first dialog should be showing again.
-        onView(withId(R.id.tab_switcher_button)).perform(click());
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 1);
-        onView(withId(R.id.tab_modal_dialog_container))
-                .check(matches(
-                        allOf(hasDescendant(withText("0")), not(hasDescendant(withText("1"))))));
-        checkBrowserControls(true);
-        checkCurrentPresenter(ModalDialogType.TAB);
-
-        // Dismiss the first dialog. The second dialog should be shown.
-        onView(withText(R.string.ok)).perform(click());
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 0);
-        onView(withId(R.id.tab_modal_dialog_container))
-                .check(matches(
-                        allOf(not(hasDescendant(withText("0"))), hasDescendant(withText("1")))));
-        checkBrowserControls(true);
-        checkCurrentPresenter(ModalDialogType.TAB);
-    }
-
-    @Test
-    @SmallTest
-    @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
-    @DisabledTest(message = "crbug.com/804858")
-    public void testSuspend_ShowNext() throws Exception {
-        // Initially there are no dialogs in the pending list. Browser controls are not restricted.
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 0);
-        checkBrowserControls(false);
-        checkCurrentPresenter(null);
-
-        // Add a tab modal dialog available for showing.
-        showDialog(0, ModalDialogType.TAB);
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 1);
-        onView(withId(R.id.tab_modal_dialog_container))
-                .check(matches(hasDescendant(withText("0"))));
-        checkBrowserControls(true);
-        checkCurrentPresenter(ModalDialogType.TAB);
-
-        // Tab modal dialogs should be suspended on entering tab switcher. App modal dialog should
-        // be showing.
-        onView(withId(R.id.tab_switcher_button)).perform(click());
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 0);
-
-        // An app modal dialog can be shown in tab switcher.
-        showDialog(1, ModalDialogType.APP);
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 0);
-        onView(withId(R.id.tab_modal_dialog_container)).check(doesNotExist());
-        onView(withText("1")).check(matches(isDisplayed()));
-        checkCurrentPresenter(ModalDialogType.APP);
-
-        // Close the app modal dialog and exit overview mode. The first dialog should be showing
-        // again.
-        onView(withText(R.string.ok)).perform(click());
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 1);
-        onView(withId(R.id.tab_switcher_button)).perform(click());
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 0);
-        onView(withId(R.id.tab_modal_dialog_container))
-                .check(matches(hasDescendant(withText("0"))));
-        checkBrowserControls(true);
-        checkCurrentPresenter(ModalDialogType.TAB);
-    }
-
-    @Test
-    @SmallTest
-    @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
-    public void testSuspend_LastTabClosed() throws Exception {
-        // Make sure there is only one opened tab.
-        while (mActivity.getCurrentTabModel().getCount() > 1) {
-            ChromeTabUtils.closeCurrentTab(InstrumentationRegistry.getInstrumentation(), mActivity);
-        }
-
-        // Initially there are no dialogs in the pending list. Browser controls are not restricted.
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 0);
-        checkBrowserControls(false);
-        checkCurrentPresenter(null);
-
-        // Add a tab modal dialog available for showing.
-        showDialog(0, ModalDialogType.TAB);
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 0);
-        onView(withId(R.id.tab_modal_dialog_container))
-                .check(matches(hasDescendant(withText("0"))));
-        checkBrowserControls(true);
-        checkCurrentPresenter(ModalDialogType.TAB);
-
-        // Tab modal dialogs should be suspended on entering tab switcher.
-        onView(withId(R.id.tab_switcher_button)).perform(click());
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 1);
-        checkBrowserControls(false);
-        checkCurrentPresenter(null);
-
-        // Close the only tab in the tab switcher.
-        ChromeTabUtils.closeCurrentTab(InstrumentationRegistry.getInstrumentation(), mActivity);
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 0);
-        checkBrowserControls(false);
-        checkCurrentPresenter(null);
-    }
-
-    @Test
-    @SmallTest
-    @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
-    public void testSuspend_TabClosed() throws Exception {
-        mActivityTestRule.loadUrlInNewTab("about:blank");
-
-        // Initially there are no dialogs in the pending list. Browser controls are not restricted.
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 0);
-        checkBrowserControls(false);
-        checkCurrentPresenter(null);
-
-        // Add a tab modal dialog available for showing.
-        showDialog(0, ModalDialogType.TAB);
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 0);
-        onView(withId(R.id.tab_modal_dialog_container))
-                .check(matches(hasDescendant(withText("0"))));
-        checkBrowserControls(true);
-        checkCurrentPresenter(ModalDialogType.TAB);
-
-        // Tab modal dialogs should be suspended on entering tab switcher.
-        onView(withId(R.id.tab_switcher_button)).perform(click());
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 1);
-        checkBrowserControls(false);
-        checkCurrentPresenter(null);
-
-        // Close current tab in the tab switcher.
-        ChromeTabUtils.closeCurrentTab(InstrumentationRegistry.getInstrumentation(), mActivity);
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 0);
-        checkBrowserControls(false);
-        checkCurrentPresenter(null);
-
-        // Show a new tab modal dialog, and it should be suspended in tab switcher.
-        showDialog(1, ModalDialogType.TAB);
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 1);
-        checkBrowserControls(false);
-        checkCurrentPresenter(null);
-
-        // Show an app modal dialog. The app modal dialog should be shown.
-        showDialog(2, ModalDialogType.APP);
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 1);
-        onView(withText("2")).check(matches(isDisplayed()));
-        checkCurrentPresenter(ModalDialogType.APP);
-    }
-
-    @Test
-    @SmallTest
-    public void testDismiss_SwitchTab() throws Exception {
-        // Open a new tab and make sure that the current tab is at index 0.
-        mActivityTestRule.loadUrlInNewTab("about:blank");
-        ChromeTabUtils.switchTabInCurrentTabModel(mActivity, 0);
-
-        // Initially there are no dialogs in the pending list. Browser controls are not restricted.
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 0);
-        checkBrowserControls(false);
-        checkCurrentPresenter(null);
-
-        // Add a tab modal dialog available for showing.
-        showDialog(0, ModalDialogType.TAB);
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 0);
-        onView(withId(R.id.tab_modal_dialog_container))
-                .check(matches(hasDescendant(withText("0"))));
-        checkBrowserControls(true);
-        checkCurrentPresenter(ModalDialogType.TAB);
-
-        // Dialog should be dismissed after switching to a different tab.
-        ChromeTabUtils.switchTabInCurrentTabModel(mActivity, 1);
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 0);
-        checkBrowserControls(false);
-        checkCurrentPresenter(null);
-
-        // Open a tab modal dialog in the current tab. The dialog should be shown.
-        showDialog(1, ModalDialogType.TAB);
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 0);
-        onView(withId(R.id.tab_modal_dialog_container))
-                .check(matches(hasDescendant(withText("1"))));
-        checkBrowserControls(true);
-        checkCurrentPresenter(ModalDialogType.TAB);
-    }
-
-    @Test
-    @SmallTest
-    public void testDismiss_BackPressed() throws Exception {
-        // Initially there are no dialogs in the pending list. Browser controls are not restricted.
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 0);
-        checkBrowserControls(false);
-        checkCurrentPresenter(null);
-
-        // Add three dialogs available for showing. The app modal dialog should be shown first.
-        showDialog(0, ModalDialogType.TAB);
-        showDialog(1, ModalDialogType.TAB);
-        showDialog(2, ModalDialogType.APP);
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 2);
-        onView(withText("2")).check(matches(isDisplayed()));
-        onView(withId(R.id.tab_modal_dialog_container)).check(doesNotExist());
-        checkCurrentPresenter(ModalDialogType.APP);
-
-        // Perform back press. The app modal dialog should be dismissed.
-        // The first tab modal dialog should be shown. The tab modal container shouldn't be in the
-        // window hierarchy when an app modal dialog is showing.
-        Espresso.pressBack();
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 1);
-        onView(withText("2")).check(doesNotExist());
-        onView(withId(R.id.tab_modal_dialog_container))
-                .check(matches(allOf(hasDescendant(withText("0")),
-                        not(hasDescendant(withText("1"))), not(hasDescendant(withText("2"))))));
-        checkBrowserControls(true);
-        checkCurrentPresenter(ModalDialogType.TAB);
-
-        // Perform a second back press. The first tab modal dialog should be dismissed.
-        Espresso.pressBack();
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 0);
-        onView(withText("2")).check(doesNotExist());
-        onView(withId(R.id.tab_modal_dialog_container))
-                .check(matches(allOf(not(hasDescendant(withText("0"))),
-                        hasDescendant(withText("1")), not(hasDescendant(withText("2"))))));
-        checkBrowserControls(true);
-        checkCurrentPresenter(ModalDialogType.TAB);
-
-        // Perform a third back press. The second tab modal dialog should be dismissed.
-        Espresso.pressBack();
-        checkPendingSize(ModalDialogType.APP, 0);
-        checkPendingSize(ModalDialogType.TAB, 0);
-        onView(withText("2")).check(doesNotExist());
-        onView(withId(R.id.tab_modal_dialog_container))
-                .check(matches(allOf(not(hasDescendant(withText("0"))),
-                        not(hasDescendant(withText("1"))), not(hasDescendant(withText("2"))))));
-        checkBrowserControls(false);
-        checkCurrentPresenter(null);
-    }
-
-    @Test
-    @SmallTest
-    public void testDismiss_DismissalCause_BackPressed() throws Exception {
-        mExpectedDismissalCause = DialogDismissalCause.NAVIGATE_BACK_OR_TOUCH_OUTSIDE;
-        int callCount = mTestObserver.onDialogDismissedCallback.getCallCount();
-
-        showDialog(0, ModalDialogType.APP);
-        showDialog(1, ModalDialogType.TAB);
-
-        // Dismiss the app modal dialog and veirify dimissal cause.
-        Espresso.pressBack();
-        mTestObserver.onDialogDismissedCallback.waitForCallback(callCount);
-
-        // Dismiss the tab modal dialog and veirify dimissal cause.
-        callCount = mTestObserver.onDialogDismissedCallback.getCallCount();
-        Espresso.pressBack();
-        mTestObserver.onDialogDismissedCallback.waitForCallback(callCount);
-
-        mExpectedDismissalCause = null;
-    }
-
-    @Test
-    @SmallTest
-    public void testDismiss_DismissalCause_TabSwitched() throws Exception {
-        mExpectedDismissalCause = DialogDismissalCause.TAB_SWITCHED;
-        int callCount = mTestObserver.onDialogDismissedCallback.getCallCount();
-
-        // Open a new tab and make sure that the current tab is at index 0.
-        mActivityTestRule.loadUrlInNewTab("about:blank");
-        ChromeTabUtils.switchTabInCurrentTabModel(mActivity, 0);
-
-        // Show a tab modal dialog and then switch tab.
-        showDialog(0, ModalDialogType.TAB);
-        ChromeTabUtils.switchTabInCurrentTabModel(mActivity, 1);
-        mTestObserver.onDialogDismissedCallback.waitForCallback(callCount);
-
-        mExpectedDismissalCause = null;
-    }
-
-    @Test
-    @SmallTest
-    public void testDismiss_DismissalCause_TabDestroyed() throws Exception {
-        mExpectedDismissalCause = DialogDismissalCause.TAB_DESTROYED;
-        int callCount = mTestObserver.onDialogDismissedCallback.getCallCount();
-
-        // Show a tab modal dialog and then close tab.
-        showDialog(0, ModalDialogType.TAB);
-        ChromeTabUtils.closeCurrentTab(InstrumentationRegistry.getInstrumentation(), mActivity);
-        mTestObserver.onDialogDismissedCallback.waitForCallback(callCount);
-
-        mExpectedDismissalCause = null;
-    }
-
-    private PropertyModel createDialog(final int index) throws Exception {
-        return ThreadUtils.runOnUiThreadBlocking(() -> {
-            ModalDialogView.Controller controller = new ModalDialogView.Controller() {
-                @Override
-                public void onDismiss(
-                        PropertyModel model, @DialogDismissalCause int dismissalCause) {
-                    mTestObserver.onDialogDismissed();
-                    checkDialogDismissalCause(dismissalCause);
-                }
-
-                @Override
-                public void onClick(PropertyModel model, int buttonType) {
-                    switch (buttonType) {
-                        case ModalDialogView.ButtonType.POSITIVE:
-                        case ModalDialogView.ButtonType.NEGATIVE:
-                            dismissDialog(index);
-                            break;
-                        default:
-                            Assert.fail("Unknown button type: " + buttonType);
-                    }
-                }
-            };
-            Resources resources = mActivity.getResources();
-            final PropertyModel model =
-                    new PropertyModel.Builder(ModalDialogProperties.ALL_KEYS)
-                            .with(ModalDialogProperties.CONTROLLER, controller)
-                            .with(ModalDialogProperties.TITLE, Integer.toString(index))
-                            .with(ModalDialogProperties.POSITIVE_BUTTON_TEXT, resources,
-                                    R.string.ok)
-                            .with(ModalDialogProperties.NEGATIVE_BUTTON_TEXT, resources,
-                                    R.string.cancel)
-                            .build();
-            return model;
-        });
-    }
-
-    private void showDialog(
-            final int index, final @ModalDialogManager.ModalDialogType int dialogType) {
-        ThreadUtils.runOnUiThreadBlocking(
-                () -> mManager.showDialog(mDialogModels[index], dialogType));
-    }
-
-    private void showDialogAsNext(
-            final int index, final @ModalDialogManager.ModalDialogType int dialogType) {
-        ThreadUtils.runOnUiThreadBlocking(
-                () -> mManager.showDialog(mDialogModels[index], dialogType, true));
-    }
-
-    private void dismissDialog(final int index) {
-        ThreadUtils.runOnUiThreadBlocking(
-                () -> mManager.dismissDialog(mDialogModels[index], DialogDismissalCause.UNKNOWN));
-    }
-
-    private void checkPendingSize(
-            final @ModalDialogManager.ModalDialogType int dialogType, final int expected) {
-        ThreadUtils.runOnUiThreadBlocking(() -> {
-            List list = mManager.getPendingDialogsForTest(dialogType);
-            Assert.assertEquals(expected, list != null ? list.size() : 0);
-        });
-    }
-
-    private void checkCurrentPresenter(final Integer dialogType) {
-        ThreadUtils.runOnUiThreadBlocking(() -> {
-            if (dialogType == null) {
-                Assert.assertFalse(mManager.isShowing());
-                Assert.assertNull(mManager.getCurrentPresenterForTest());
-            } else {
-                Assert.assertTrue(mManager.isShowing());
-                Assert.assertEquals(mManager.getPresenterForTest(dialogType),
-                        mManager.getCurrentPresenterForTest());
-            }
-        });
-    }
-
-    private void checkBrowserControls(boolean restricted) {
-        if (restricted) {
-            Assert.assertTrue("All tabs should be obscured", mActivity.isViewObscuringAllTabs());
-            onView(allOf(withId(R.id.menu_button), isDescendantOfA(withId(R.id.toolbar))))
-                    .check(matches(not(isEnabled())));
-        } else {
-            Assert.assertFalse("Tabs shouldn't be obscured", mActivity.isViewObscuringAllTabs());
-            onView(allOf(withId(R.id.menu_button), isDescendantOfA(withId(R.id.toolbar))))
-                    .check(matches(isEnabled()));
-        }
-    }
-
-    private void checkDialogDismissalCause(int dismissalCause) {
-        if (mExpectedDismissalCause == null) return;
-        Assert.assertEquals(mExpectedDismissalCause.intValue(), dismissalCause);
-    }
-}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/modaldialog/ModalDialogTestUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/modaldialog/ModalDialogTestUtils.java
new file mode 100644
index 0000000..543ee7cf
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/modaldialog/ModalDialogTestUtils.java
@@ -0,0 +1,121 @@
+// 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.
+
+package org.chromium.chrome.browser.modaldialog;
+
+import android.content.res.Resources;
+import android.support.annotation.Nullable;
+
+import org.junit.Assert;
+
+import org.chromium.base.ThreadUtils;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.ui.modaldialog.DialogDismissalCause;
+import org.chromium.ui.modaldialog.ModalDialogManager;
+import org.chromium.ui.modaldialog.ModalDialogManager.ModalDialogType;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
+import org.chromium.ui.modelutil.PropertyModel;
+
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+
+/**
+ * Utility methods and classes for testing modal dialogs.
+ */
+public class ModalDialogTestUtils {
+    /**
+     * Test observer that notifies dialog dismissal.
+     */
+    public interface TestDialogDismissedObserver {
+        /**
+         * Called when dialog is dismissed.
+         * @param dismissalCause The dismissal cause.
+         */
+        void onDialogDismissed(@DialogDismissalCause int dismissalCause);
+    }
+
+    /**
+     * @return A {@link PropertyModel} of a modal dialog that is used for testing.
+     */
+    public static PropertyModel createDialog(ChromeActivity activity, String title,
+            @Nullable TestDialogDismissedObserver observer) throws ExecutionException {
+        return ThreadUtils.runOnUiThreadBlocking(() -> {
+            ModalDialogProperties.Controller controller = new ModalDialogProperties.Controller() {
+                @Override
+                public void onDismiss(
+                        PropertyModel model, @DialogDismissalCause int dismissalCause) {
+                    if (observer != null) observer.onDialogDismissed(dismissalCause);
+                }
+
+                @Override
+                public void onClick(PropertyModel model, int buttonType) {
+                    switch (buttonType) {
+                        case ModalDialogProperties.ButtonType.POSITIVE:
+                        case ModalDialogProperties.ButtonType.NEGATIVE:
+                            activity.getModalDialogManager().dismissDialog(
+                                    model, DialogDismissalCause.UNKNOWN);
+                            break;
+                        default:
+                            Assert.fail("Unknown button type: " + buttonType);
+                    }
+                }
+            };
+            Resources resources = activity.getResources();
+            return new PropertyModel.Builder(ModalDialogProperties.ALL_KEYS)
+                    .with(ModalDialogProperties.CONTROLLER, controller)
+                    .with(ModalDialogProperties.TITLE, title)
+                    .with(ModalDialogProperties.POSITIVE_BUTTON_TEXT, resources, R.string.ok)
+                    .with(ModalDialogProperties.NEGATIVE_BUTTON_TEXT, resources, R.string.cancel)
+                    .build();
+        });
+    }
+
+    /**
+     * Shows a dialog on the specified {@link ModalDialogManager} on the UI thread.
+     */
+    public static void showDialog(
+            ModalDialogManager manager, PropertyModel model, @ModalDialogType int dialogType) {
+        ThreadUtils.runOnUiThreadBlocking(() -> manager.showDialog(model, dialogType));
+    }
+
+    /**
+     * Checks whether the number of pending dialogs of a specified type is as expected.
+     */
+    public static void checkPendingSize(
+            ModalDialogManager manager, @ModalDialogType int dialogType, int expected) {
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            List list = manager.getPendingDialogsForTest(dialogType);
+            Assert.assertEquals(expected, list != null ? list.size() : 0);
+        });
+    }
+
+    /**
+     * Checks whether the current presenter of the {@link ModalDialogManager} is as expected. If
+     * {@code dialogType} is null, then the expected current presenter should be null.
+     */
+    public static void checkCurrentPresenter(
+            ModalDialogManager manager, @Nullable Integer dialogType) {
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            if (dialogType == null) {
+                Assert.assertFalse(manager.isShowing());
+                Assert.assertNull(manager.getCurrentPresenterForTest());
+            } else {
+                Assert.assertTrue(manager.isShowing());
+                Assert.assertEquals(manager.getPresenterForTest(dialogType),
+                        manager.getCurrentPresenterForTest());
+            }
+        });
+    }
+
+    /**
+     * Checks whether the dialog dismissal cause is as expected. If {@code expectedDismissalCause}
+     * is null, then the check is skipped.
+     */
+    public static void checkDialogDismissalCause(
+            @Nullable Integer expectedDismissalCause, @DialogDismissalCause int dismissalCause) {
+        if (expectedDismissalCause == null) return;
+        Assert.assertEquals(expectedDismissalCause.intValue(), dismissalCause);
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/modaldialog/ModalDialogViewTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/modaldialog/ModalDialogViewTest.java
index 2c761ab2..74d9b21 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/modaldialog/ModalDialogViewTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/modaldialog/ModalDialogViewTest.java
@@ -38,11 +38,12 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeSwitches;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.chrome.test.util.RenderTestRule;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
+import org.chromium.ui.modelutil.PropertyModel;
 
 import java.io.IOException;
 import java.util.Collections;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/modaldialog/TabModalPresenterTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/modaldialog/TabModalPresenterTest.java
new file mode 100644
index 0000000..7d2e903
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/modaldialog/TabModalPresenterTest.java
@@ -0,0 +1,462 @@
+// 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.
+
+package org.chromium.chrome.browser.modaldialog;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.isEnabled;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.not;
+
+import static org.chromium.chrome.browser.modaldialog.ModalDialogTestUtils.checkCurrentPresenter;
+import static org.chromium.chrome.browser.modaldialog.ModalDialogTestUtils.checkDialogDismissalCause;
+import static org.chromium.chrome.browser.modaldialog.ModalDialogTestUtils.checkPendingSize;
+import static org.chromium.chrome.browser.modaldialog.ModalDialogTestUtils.createDialog;
+import static org.chromium.chrome.browser.modaldialog.ModalDialogTestUtils.showDialog;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.espresso.Espresso;
+import android.support.test.filters.SmallTest;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.test.util.CallbackHelper;
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.DisabledTest;
+import org.chromium.base.test.util.Feature;
+import org.chromium.base.test.util.Restriction;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.ChromeTabbedActivity;
+import org.chromium.chrome.browser.omnibox.UrlBar;
+import org.chromium.chrome.browser.omnibox.UrlFocusChangeListener;
+import org.chromium.chrome.browser.tab.EmptyTabObserver;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
+import org.chromium.chrome.test.util.ChromeTabUtils;
+import org.chromium.chrome.test.util.OmniboxTestUtils;
+import org.chromium.ui.modaldialog.DialogDismissalCause;
+import org.chromium.ui.modaldialog.ModalDialogManager;
+import org.chromium.ui.modaldialog.ModalDialogManager.ModalDialogType;
+import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.test.util.UiRestriction;
+
+/**
+ * Tests for {@link TabModalPresenter}.
+ */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+public class TabModalPresenterTest {
+    private class TestObserver extends EmptyTabObserver
+            implements UrlFocusChangeListener, ModalDialogTestUtils.TestDialogDismissedObserver {
+        public final CallbackHelper onUrlFocusChangedCallback = new CallbackHelper();
+        public final CallbackHelper onDialogDismissedCallback = new CallbackHelper();
+        public final CallbackHelper onTabInteractabilityChangedCallback = new CallbackHelper();
+
+        @Override
+        public void onUrlFocusChange(boolean hasFocus) {
+            onUrlFocusChangedCallback.notifyCalled();
+        }
+
+        @Override
+        public void onInteractabilityChanged(boolean isInteractable) {
+            onTabInteractabilityChangedCallback.notifyCalled();
+        }
+
+        @Override
+        public void onDialogDismissed(int dismissalCause) {
+            onDialogDismissedCallback.notifyCalled();
+            checkDialogDismissalCause(mExpectedDismissalCause, dismissalCause);
+        }
+    }
+
+    @Rule
+    public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
+
+    private ChromeTabbedActivity mActivity;
+    private ModalDialogManager mManager;
+    private TestObserver mTestObserver;
+    private Integer mExpectedDismissalCause;
+
+    @Before
+    public void setUp() throws InterruptedException {
+        mActivityTestRule.startMainActivityOnBlankPage();
+        mActivity = mActivityTestRule.getActivity();
+        mManager = mActivity.getModalDialogManager();
+        mTestObserver = new TestObserver();
+        mActivity.getToolbarManager()
+                .getToolbarLayoutForTesting()
+                .getLocationBar()
+                .addUrlFocusChangeListener(mTestObserver);
+        TabModalPresenter presenter =
+                (TabModalPresenter) mManager.getPresenterForTest(ModalDialogType.TAB);
+        presenter.disableAnimationForTest();
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"ModalDialog"})
+    @DisabledTest(message = "crbug.com/812066")
+    public void testShow_UrlBarFocused() throws Exception {
+        // Show a tab modal dialog. The dialog should be shown on top of the toolbar.
+        PropertyModel dialog1 = createDialog(mActivity, "1", null);
+        showDialog(mManager, dialog1, ModalDialogType.TAB);
+
+        TabModalPresenter presenter =
+                (TabModalPresenter) mManager.getPresenterForTest(ModalDialogType.TAB);
+        final View dialogContainer = presenter.getDialogContainerForTest();
+        final View controlContainer = mActivity.findViewById(R.id.control_container);
+        final ViewGroup containerParent = presenter.getContainerParentForTest();
+
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            Assert.assertTrue(containerParent.indexOfChild(dialogContainer)
+                    > containerParent.indexOfChild(controlContainer));
+        });
+
+        // When editing URL, it should be shown on top of the dialog.
+        UrlBar urlBar = mActivity.findViewById(R.id.url_bar);
+        int callCount = mTestObserver.onUrlFocusChangedCallback.getCallCount();
+        OmniboxTestUtils.toggleUrlBarFocus(urlBar, true);
+        mTestObserver.onUrlFocusChangedCallback.waitForCallback(callCount);
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            Assert.assertTrue(containerParent.indexOfChild(dialogContainer)
+                    < containerParent.indexOfChild(controlContainer));
+        });
+
+        // When URL bar is not focused, the dialog should be shown on top of the toolbar again.
+        callCount = mTestObserver.onUrlFocusChangedCallback.getCallCount();
+        OmniboxTestUtils.toggleUrlBarFocus(urlBar, false);
+        mTestObserver.onUrlFocusChangedCallback.waitForCallback(callCount);
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            Assert.assertTrue(containerParent.indexOfChild(dialogContainer)
+                    > containerParent.indexOfChild(controlContainer));
+        });
+
+        // Dismiss the dialog by clicking OK.
+        onView(withText(R.string.ok)).perform(click());
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"ModalDialog"})
+    @DisabledTest(message = "crbug.com/804858")
+    @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
+    public void testSuspend_ToggleOverview() throws Exception {
+        mActivity.getActivityTab().addObserver(mTestObserver);
+        PropertyModel dialog1 = createDialog(mActivity, "1", null);
+        PropertyModel dialog2 = createDialog(mActivity, "2", null);
+        PropertyModel dialog3 = createDialog(mActivity, "3", null);
+
+        // Initially there are no dialogs in the pending list. Browser controls are not restricted.
+        checkPendingSize(mManager, ModalDialogType.TAB, 0);
+        checkBrowserControls(false);
+        checkCurrentPresenter(mManager, null);
+
+        // Add two tab modal dialogs available for showing.
+        showDialog(mManager, dialog1, ModalDialogType.TAB);
+        showDialog(mManager, dialog2, ModalDialogType.TAB);
+        checkPendingSize(mManager, ModalDialogType.TAB, 1);
+        onView(withId(R.id.tab_modal_dialog_container))
+                .check(matches(
+                        allOf(hasDescendant(withText("1")), not(hasDescendant(withText("2"))))));
+        checkBrowserControls(true);
+        checkCurrentPresenter(mManager, ModalDialogType.TAB);
+
+        //  Tab modal dialogs should be suspended on entering tab switcher.
+        onView(withId(R.id.tab_switcher_button)).perform(click());
+        checkPendingSize(mManager, ModalDialogType.TAB, 2);
+        onView(withId(R.id.tab_modal_dialog_container))
+                .check(matches(allOf(
+                        not(hasDescendant(withText("1"))), not(hasDescendant(withText("2"))))));
+        checkBrowserControls(false);
+        checkCurrentPresenter(mManager, null);
+
+        // An app modal dialog can be shown in tab switcher.
+        showDialog(mManager, dialog3, ModalDialogType.APP);
+        checkPendingSize(mManager, ModalDialogType.APP, 0);
+        checkPendingSize(mManager, ModalDialogType.TAB, 2);
+        onView(withId(R.id.tab_modal_dialog_container)).check(doesNotExist());
+        onView(withText("3")).check(matches(isDisplayed()));
+        checkCurrentPresenter(mManager, ModalDialogType.APP);
+
+        // Close the app modal dialog and verify that the tab modal dialog is still queued.
+        onView(withText(R.string.ok)).perform(click());
+        checkPendingSize(mManager, ModalDialogType.APP, 0);
+        checkPendingSize(mManager, ModalDialogType.TAB, 2);
+
+        // Exit overview mode. The first dialog should be showing again.
+        int callCount = mTestObserver.onTabInteractabilityChangedCallback.getCallCount();
+        onView(withId(R.id.tab_switcher_button)).perform(click());
+        mTestObserver.onTabInteractabilityChangedCallback.waitForCallback(callCount);
+
+        checkPendingSize(mManager, ModalDialogType.TAB, 1);
+        onView(withId(R.id.tab_modal_dialog_container))
+                .check(matches(
+                        allOf(hasDescendant(withText("1")), not(hasDescendant(withText("2"))))));
+        checkBrowserControls(true);
+        checkCurrentPresenter(mManager, ModalDialogType.TAB);
+
+        // Dismiss the first dialog. The second dialog should be shown.
+        onView(withText(R.string.ok)).perform(click());
+        checkPendingSize(mManager, ModalDialogType.TAB, 0);
+        onView(withId(R.id.tab_modal_dialog_container))
+                .check(matches(
+                        allOf(not(hasDescendant(withText("1"))), hasDescendant(withText("2")))));
+        checkBrowserControls(true);
+        checkCurrentPresenter(mManager, ModalDialogType.TAB);
+
+        // Reset states.
+        mActivity.getActivityTab().removeObserver(mTestObserver);
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"ModalDialog"})
+    @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
+    public void testSuspend_LastTabClosed() throws Exception {
+        PropertyModel dialog1 = createDialog(mActivity, "1", null);
+
+        // Make sure there is only one opened tab.
+        while (mActivity.getCurrentTabModel().getCount() > 1) {
+            ChromeTabUtils.closeCurrentTab(InstrumentationRegistry.getInstrumentation(), mActivity);
+        }
+
+        // Initially there are no dialogs in the pending list. Browser controls are not restricted.
+        checkPendingSize(mManager, ModalDialogType.TAB, 0);
+        checkBrowserControls(false);
+        checkCurrentPresenter(mManager, null);
+
+        // Add a tab modal dialog available for showing.
+        showDialog(mManager, dialog1, ModalDialogType.TAB);
+        checkPendingSize(mManager, ModalDialogType.TAB, 0);
+        onView(withId(R.id.tab_modal_dialog_container))
+                .check(matches(hasDescendant(withText("1"))));
+        checkBrowserControls(true);
+        checkCurrentPresenter(mManager, ModalDialogType.TAB);
+
+        // Tab modal dialogs should be suspended on entering tab switcher.
+        onView(withId(R.id.tab_switcher_button)).perform(click());
+        checkPendingSize(mManager, ModalDialogType.TAB, 1);
+        checkBrowserControls(false);
+        checkCurrentPresenter(mManager, null);
+
+        // Close the only tab in the tab switcher. Verify that the queued tab modal dialogs are
+        // cleared.
+        ChromeTabUtils.closeCurrentTab(InstrumentationRegistry.getInstrumentation(), mActivity);
+        checkPendingSize(mManager, ModalDialogType.TAB, 0);
+        checkBrowserControls(false);
+        checkCurrentPresenter(mManager, null);
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"ModalDialog"})
+    @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
+    public void testSuspend_TabClosed() throws Exception {
+        PropertyModel dialog1 = createDialog(mActivity, "1", null);
+        PropertyModel dialog2 = createDialog(mActivity, "2", null);
+        PropertyModel dialog3 = createDialog(mActivity, "3", null);
+        mActivityTestRule.loadUrlInNewTab("about:blank");
+
+        // Initially there are no dialogs in the pending list. Browser controls are not restricted.
+        checkPendingSize(mManager, ModalDialogType.APP, 0);
+        checkPendingSize(mManager, ModalDialogType.TAB, 0);
+        checkBrowserControls(false);
+        checkCurrentPresenter(mManager, null);
+
+        // Add a tab modal dialog available for showing.
+        showDialog(mManager, dialog1, ModalDialogType.TAB);
+        checkPendingSize(mManager, ModalDialogType.APP, 0);
+        checkPendingSize(mManager, ModalDialogType.TAB, 0);
+        onView(withId(R.id.tab_modal_dialog_container))
+                .check(matches(hasDescendant(withText("1"))));
+        checkBrowserControls(true);
+        checkCurrentPresenter(mManager, ModalDialogType.TAB);
+
+        // Tab modal dialogs should be suspended on entering tab switcher.
+        onView(withId(R.id.tab_switcher_button)).perform(click());
+        checkPendingSize(mManager, ModalDialogType.APP, 0);
+        checkPendingSize(mManager, ModalDialogType.TAB, 1);
+        checkBrowserControls(false);
+        checkCurrentPresenter(mManager, null);
+
+        // Close current tab in the tab switcher.
+        ChromeTabUtils.closeCurrentTab(InstrumentationRegistry.getInstrumentation(), mActivity);
+        checkPendingSize(mManager, ModalDialogType.APP, 0);
+        checkPendingSize(mManager, ModalDialogType.TAB, 0);
+        checkBrowserControls(false);
+        checkCurrentPresenter(mManager, null);
+
+        // Show a new tab modal dialog, and it should be suspended in tab switcher.
+        showDialog(mManager, dialog2, ModalDialogType.TAB);
+        checkPendingSize(mManager, ModalDialogType.APP, 0);
+        checkPendingSize(mManager, ModalDialogType.TAB, 1);
+        checkBrowserControls(false);
+        checkCurrentPresenter(mManager, null);
+
+        // Show an app modal dialog. The app modal dialog should be shown.
+        showDialog(mManager, dialog3, ModalDialogType.APP);
+        checkPendingSize(mManager, ModalDialogType.APP, 0);
+        checkPendingSize(mManager, ModalDialogType.TAB, 1);
+        onView(withText("3")).check(matches(isDisplayed()));
+        checkCurrentPresenter(mManager, ModalDialogType.APP);
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"ModalDialog"})
+    public void testDismiss_SwitchTab() throws Exception {
+        PropertyModel dialog1 = createDialog(mActivity, "1", null);
+        PropertyModel dialog2 = createDialog(mActivity, "2", null);
+
+        // Open a new tab and make sure that the current tab is at index 0.
+        mActivityTestRule.loadUrlInNewTab("about:blank");
+        ChromeTabUtils.switchTabInCurrentTabModel(mActivity, 0);
+
+        // Initially there are no dialogs in the pending list. Browser controls are not restricted.
+        checkPendingSize(mManager, ModalDialogType.TAB, 0);
+        checkBrowserControls(false);
+        checkCurrentPresenter(mManager, null);
+
+        // Add a tab modal dialog available for showing.
+        showDialog(mManager, dialog1, ModalDialogType.TAB);
+        checkPendingSize(mManager, ModalDialogType.TAB, 0);
+        onView(withId(R.id.tab_modal_dialog_container))
+                .check(matches(hasDescendant(withText("1"))));
+        checkBrowserControls(true);
+        checkCurrentPresenter(mManager, ModalDialogType.TAB);
+
+        // Dialog should be dismissed after switching to a different tab.
+        ChromeTabUtils.switchTabInCurrentTabModel(mActivity, 1);
+        checkPendingSize(mManager, ModalDialogType.TAB, 0);
+        checkBrowserControls(false);
+        checkCurrentPresenter(mManager, null);
+
+        // Open a tab modal dialog in the current tab. The dialog should be shown.
+        showDialog(mManager, dialog2, ModalDialogType.TAB);
+        checkPendingSize(mManager, ModalDialogType.TAB, 0);
+        onView(withId(R.id.tab_modal_dialog_container))
+                .check(matches(hasDescendant(withText("2"))));
+        checkBrowserControls(true);
+        checkCurrentPresenter(mManager, ModalDialogType.TAB);
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"ModalDialog"})
+    public void testDismiss_BackPressed() throws Exception {
+        PropertyModel dialog1 = createDialog(mActivity, "1", null);
+        PropertyModel dialog2 = createDialog(mActivity, "2", null);
+
+        // Initially there are no dialogs in the pending list. Browser controls are not restricted.
+        checkPendingSize(mManager, ModalDialogType.TAB, 0);
+        checkBrowserControls(false);
+        checkCurrentPresenter(mManager, null);
+
+        // Add two tab modal dialogs available for showing. The first dialog should be shown first.
+        showDialog(mManager, dialog1, ModalDialogType.TAB);
+        showDialog(mManager, dialog2, ModalDialogType.TAB);
+        checkPendingSize(mManager, ModalDialogType.TAB, 1);
+        onView(withId(R.id.tab_modal_dialog_container))
+                .check(matches(hasDescendant(withText("1"))));
+        checkCurrentPresenter(mManager, ModalDialogType.TAB);
+
+        // Perform a back press. The first tab modal dialog should be dismissed.
+        Espresso.pressBack();
+        checkPendingSize(mManager, ModalDialogType.TAB, 0);
+        onView(withId(R.id.tab_modal_dialog_container))
+                .check(matches(
+                        allOf(not(hasDescendant(withText("1"))), hasDescendant(withText("2")))));
+        checkBrowserControls(true);
+        checkCurrentPresenter(mManager, ModalDialogType.TAB);
+
+        // Perform a second back press. The second tab modal dialog should be dismissed.
+        Espresso.pressBack();
+        checkPendingSize(mManager, ModalDialogType.APP, 0);
+        checkPendingSize(mManager, ModalDialogType.TAB, 0);
+        onView(withId(R.id.tab_modal_dialog_container))
+                .check(matches(allOf(
+                        not(hasDescendant(withText("1"))), not(hasDescendant(withText("2"))))));
+        checkBrowserControls(false);
+        checkCurrentPresenter(mManager, null);
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"ModalDialog"})
+    public void testDismiss_DismissalCause_BackPressed() throws Exception {
+        PropertyModel dialog1 = createDialog(mActivity, "1", mTestObserver);
+        mExpectedDismissalCause = DialogDismissalCause.NAVIGATE_BACK_OR_TOUCH_OUTSIDE;
+
+        showDialog(mManager, dialog1, ModalDialogType.TAB);
+
+        // Dismiss the tab modal dialog and verify dismissal cause.
+        int callCount = mTestObserver.onDialogDismissedCallback.getCallCount();
+        Espresso.pressBack();
+        mTestObserver.onDialogDismissedCallback.waitForCallback(callCount);
+
+        mExpectedDismissalCause = null;
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"ModalDialog"})
+    public void testDismiss_DismissalCause_TabSwitched() throws Exception {
+        PropertyModel dialog1 = createDialog(mActivity, "1", mTestObserver);
+        mExpectedDismissalCause = DialogDismissalCause.TAB_SWITCHED;
+        int callCount = mTestObserver.onDialogDismissedCallback.getCallCount();
+
+        // Open a new tab and make sure that the current tab is at index 0.
+        mActivityTestRule.loadUrlInNewTab("about:blank");
+        ChromeTabUtils.switchTabInCurrentTabModel(mActivity, 0);
+
+        // Show a tab modal dialog and then switch tab.
+        showDialog(mManager, dialog1, ModalDialogType.TAB);
+        ChromeTabUtils.switchTabInCurrentTabModel(mActivity, 1);
+        mTestObserver.onDialogDismissedCallback.waitForCallback(callCount);
+
+        mExpectedDismissalCause = null;
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"ModalDialog"})
+    public void testDismiss_DismissalCause_TabDestroyed() throws Exception {
+        PropertyModel dialog1 = createDialog(mActivity, "1", mTestObserver);
+        mExpectedDismissalCause = DialogDismissalCause.TAB_DESTROYED;
+        int callCount = mTestObserver.onDialogDismissedCallback.getCallCount();
+
+        // Show a tab modal dialog and then close tab.
+        showDialog(mManager, dialog1, ModalDialogType.TAB);
+        ChromeTabUtils.closeCurrentTab(InstrumentationRegistry.getInstrumentation(), mActivity);
+        mTestObserver.onDialogDismissedCallback.waitForCallback(callCount);
+
+        mExpectedDismissalCause = null;
+    }
+
+    private void checkBrowserControls(boolean restricted) {
+        if (restricted) {
+            Assert.assertTrue("All tabs should be obscured", mActivity.isViewObscuringAllTabs());
+            onView(allOf(isDisplayed(), withId(R.id.menu_button))).check(matches(not(isEnabled())));
+        } else {
+            Assert.assertFalse("Tabs shouldn't be obscured", mActivity.isViewObscuringAllTabs());
+            onView(allOf(isDisplayed(), withId(R.id.menu_button))).check(matches(isEnabled()));
+        }
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestBillingAddressWithoutPhoneTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestBillingAddressWithoutPhoneTest.java
index c345e7d5..84d6890 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestBillingAddressWithoutPhoneTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestBillingAddressWithoutPhoneTest.java
@@ -23,9 +23,9 @@
 import org.chromium.chrome.browser.autofill.CardType;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView;
 import org.chromium.chrome.browser.payments.PaymentRequestTestRule.MainActivityStartCallback;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
 
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
@@ -79,7 +79,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
         mPaymentRequestTestRule.expectResultContains(new String[] {"Jon NoPhone"});
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCanMakePaymentMetricsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCanMakePaymentMetricsTest.java
index 12302d89..defb54a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCanMakePaymentMetricsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCanMakePaymentMetricsTest.java
@@ -25,11 +25,11 @@
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.autofill.AutofillTestHelper;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView;
 import org.chromium.chrome.browser.payments.PaymentRequestTestRule.MainActivityStartCallback;
 import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
 
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
@@ -114,7 +114,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
 
         // Make sure the canMakePayment events were logged correctly.
         int expectedSample = Event.SHOWN | Event.PAY_CLICKED | Event.RECEIVED_INSTRUMENT_DETAILS
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestContactDetailsAndFreeShippingTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestContactDetailsAndFreeShippingTest.java
index 40b87cf..bcd66d24 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestContactDetailsAndFreeShippingTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestContactDetailsAndFreeShippingTest.java
@@ -20,9 +20,9 @@
 import org.chromium.chrome.browser.autofill.CardType;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView;
 import org.chromium.chrome.browser.payments.PaymentRequestTestRule.MainActivityStartCallback;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
 
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
@@ -66,7 +66,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
         mPaymentRequestTestRule.expectResultContains(new String[] {"Jon Doe", "jon.doe@google.com",
                 "+15555555555", "Jon Doe", "4111111111111111", "12", "2050", "basic-card", "123",
                 "Google", "340 Main St", "CA", "Los Angeles", "90291", "US", "en",
@@ -90,7 +90,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
         mPaymentRequestTestRule.expectResultContains(new String[] {"Jon Doe", "jon.doe@google.com",
                 "+15555555555", "Jon Doe", "4111111111111111", "12", "2050", "basic-card", "123",
                 "Google", "340 Main St", "CA", "Los Angeles", "90291", "US", "en",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestDynamicShippingSingleAddressTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestDynamicShippingSingleAddressTest.java
index c64ccae..4b228a72 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestDynamicShippingSingleAddressTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestDynamicShippingSingleAddressTest.java
@@ -21,10 +21,10 @@
 import org.chromium.chrome.browser.autofill.CardType;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView;
 import org.chromium.chrome.browser.payments.PaymentRequestTestRule.MainActivityStartCallback;
 import org.chromium.chrome.browser.payments.ui.PaymentRequestSection;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
 
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
@@ -83,7 +83,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
         mPaymentRequestTestRule.expectResultContains(new String[] {"Jon Doe", "4111111111111111",
                 "12", "2050", "basic-card", "123", "Google", "340 Main St", "CA", "Los Angeles",
                 "90291", "+16502530000", "US", "en", "californiaShippingOption"});
@@ -117,7 +117,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
         mPaymentRequestTestRule.expectResultContains(new String[] {"Jane Doe", "4111111111111111",
                 "12", "2050", "basic-card", "123", "Google", "340 Main St", "CA", "Los Angeles",
                 "90291", "+16502530000", "US", "en", "californiaShippingOption"});
@@ -152,7 +152,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
         mPaymentRequestTestRule.expectResultContains(new String[] {"Jon Doe", "4111111111111111",
                 "12", "2050", "basic-card", "123", "Google", "340 Main St", "CA", "Los Angeles",
                 "90291", "+16502530000", "US", "en", "californiaShippingOption"});
@@ -208,7 +208,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
         mPaymentRequestTestRule.expectResultContains(new String[] {"Bob", "Google",
                 "1600 Amphitheatre Pkwy", "Mountain View", "CA", "94043", "+16502530000"});
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestEmailAndFreeShippingTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestEmailAndFreeShippingTest.java
index 4bfbc3f..b797edd 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestEmailAndFreeShippingTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestEmailAndFreeShippingTest.java
@@ -20,9 +20,9 @@
 import org.chromium.chrome.browser.autofill.CardType;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView;
 import org.chromium.chrome.browser.payments.PaymentRequestTestRule.MainActivityStartCallback;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
 
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
@@ -62,7 +62,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
         mPaymentRequestTestRule.expectResultContains(new String[] {"jon.doe@google.com", "Jon Doe",
                 "4111111111111111", "12", "2050", "basic-card", "123", "Google", "340 Main St",
                 "CA", "Los Angeles", "90291", "US", "en", "freeShippingOption"});
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestExpiredLocalCardTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestExpiredLocalCardTest.java
index 55c16cae..bf8a58b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestExpiredLocalCardTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestExpiredLocalCardTest.java
@@ -21,9 +21,9 @@
 import org.chromium.chrome.browser.autofill.CardType;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView;
 import org.chromium.chrome.browser.payments.PaymentRequestTestRule.MainActivityStartCallback;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
 
 import java.util.Calendar;
 import java.util.concurrent.ExecutionException;
@@ -72,7 +72,7 @@
                 new int[] {R.id.expiration_month, R.id.expiration_year, R.id.card_unmask_input},
                 new String[] {"11", "26", "123"}, mRule.getReadyToUnmask());
         mRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mRule.getDismissed());
         mRule.expectResultContains(new String[] {"Jon Doe", "4111111111111111", "11", "2026",
                 "basic-card", "123", "Google", "340 Main St", "CA", "Los Angeles", "90291", "US",
                 "en", "freeShippingOption"});
@@ -93,7 +93,7 @@
                 new int[] {R.id.expiration_month, R.id.expiration_year, R.id.card_unmask_input},
                 new String[] {"11", "26", "123"}, mRule.getReadyToUnmask());
         mRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mRule.getDismissed());
 
         // Make sure the new expiration date was saved.
         CreditCard storedCard = mHelper.getCreditCard(mCreditCardId);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestExtraShippingOptionsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestExtraShippingOptionsTest.java
index 11d373a..97e2360 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestExtraShippingOptionsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestExtraShippingOptionsTest.java
@@ -18,9 +18,9 @@
 import org.chromium.chrome.browser.autofill.CardType;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView;
 import org.chromium.chrome.browser.payments.PaymentRequestTestRule.MainActivityStartCallback;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
 
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
@@ -62,7 +62,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
         mPaymentRequestTestRule.expectResultContains(
                 new String[] {"Jon Doe", "4111111111111111", "12", "2050", "basic-card", "123",
                         "Google", "340 Main St", "CA", "Los Angeles", "90291", "US", "en"});
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestFailCompleteTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestFailCompleteTest.java
index bb1e060..bb10772 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestFailCompleteTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestFailCompleteTest.java
@@ -18,9 +18,9 @@
 import org.chromium.chrome.browser.autofill.CardType;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView;
 import org.chromium.chrome.browser.payments.PaymentRequestTestRule.MainActivityStartCallback;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
 
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
@@ -57,7 +57,8 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getResultReady());
+                ModalDialogProperties.ButtonType.POSITIVE,
+                mPaymentRequestTestRule.getResultReady());
         mPaymentRequestTestRule.clickAndWait(
                 R.id.ok_button, mPaymentRequestTestRule.getDismissed());
         mPaymentRequestTestRule.expectResultContains(new String[] {"Transaction failed"});
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestFreeShippingTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestFreeShippingTest.java
index 6fa32ab..2307a02 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestFreeShippingTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestFreeShippingTest.java
@@ -22,10 +22,10 @@
 import org.chromium.chrome.browser.autofill.CardType;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView;
 import org.chromium.chrome.browser.payments.PaymentRequestTestRule.MainActivityStartCallback;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.RenderTestRule;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
 
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
@@ -70,7 +70,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
         mPaymentRequestTestRule.expectResultContains(new String[] {"Jon Doe", "4111111111111111",
                 "12", "2050", "basic-card", "123", "Google", "340 Main St", "CA", "Los Angeles",
                 "90291", "US", "en", "freeShippingOption"});
@@ -119,7 +119,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
         mPaymentRequestTestRule.expectResultContains(new String[] {"Bob", "Google",
                 "1600 Amphitheatre Pkwy", "Mountain View", "CA", "94043", "+16502530000"});
     }
@@ -148,7 +148,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
         mPaymentRequestTestRule.expectResultContains(new String[] {
                 "Alice", "Supreme Court", "Airport Road", "Kabul", "1043", "+93202530000"});
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestIdTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestIdTest.java
index ae4ae33c..22b61ef 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestIdTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestIdTest.java
@@ -18,9 +18,9 @@
 import org.chromium.chrome.browser.autofill.CardType;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView;
 import org.chromium.chrome.browser.payments.PaymentRequestTestRule.MainActivityStartCallback;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
 
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
@@ -63,7 +63,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
         mPaymentRequestTestRule.expectResultContains(new String[] {"my_payment_id"});
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestIncompleteServerCardTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestIncompleteServerCardTest.java
index 6a71692..ecec5db 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestIncompleteServerCardTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestIncompleteServerCardTest.java
@@ -21,9 +21,9 @@
 import org.chromium.chrome.browser.autofill.CardType;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView;
 import org.chromium.chrome.browser.payments.PaymentRequestTestRule.MainActivityStartCallback;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
 
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
@@ -71,7 +71,7 @@
         mPaymentRequestTestRule.clickAndWait(
                 R.id.button_primary, mPaymentRequestTestRule.getReadyForUnmaskInput());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.NEGATIVE, mPaymentRequestTestRule.getReadyToPay());
+                ModalDialogProperties.ButtonType.NEGATIVE, mPaymentRequestTestRule.getReadyToPay());
         mPaymentRequestTestRule.clickAndWait(
                 R.id.button_secondary, mPaymentRequestTestRule.getDismissed());
         mPaymentRequestTestRule.expectResultContains(new String[] {"Request cancelled"});
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestJourneyLoggerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestJourneyLoggerTest.java
index cb21588..36f2e5a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestJourneyLoggerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestJourneyLoggerTest.java
@@ -29,9 +29,9 @@
 import org.chromium.chrome.browser.autofill.CardType;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView;
 import org.chromium.chrome.browser.payments.PaymentRequestTestRule.MainActivityStartCallback;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
 
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
@@ -86,7 +86,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
 
         // Make sure the right number of suggestions were logged.
         Assert.assertEquals(1,
@@ -168,7 +168,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
 
         // Make sure the edit was logged.
         Assert.assertEquals(1,
@@ -219,7 +219,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
 
         // Make sure the add was logged.
         Assert.assertEquals(1,
@@ -375,7 +375,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
 
         // Make sure the add was logged.
         Assert.assertEquals(1,
@@ -409,7 +409,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
 
         // Make sure the right number of suggestions were logged.
         Assert.assertEquals(1,
@@ -493,7 +493,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
 
         // Make sure the edit was logged.
         Assert.assertEquals(1,
@@ -542,7 +542,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
 
         // Make sure the add was logged.
         Assert.assertEquals(1,
@@ -963,7 +963,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
 
         // Make sure nothing was logged for contact info.
         Assert.assertEquals(0,
@@ -997,7 +997,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
 
         // Make sure the right number of suggestions were logged.
         Assert.assertEquals(1,
@@ -1023,7 +1023,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
 
         // Make sure the right number of suggestions were logged.
         Assert.assertEquals(2,
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestMetricsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestMetricsTest.java
index 1ead8a9e..314319d7 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestMetricsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestMetricsTest.java
@@ -30,10 +30,10 @@
 import org.chromium.chrome.browser.autofill.CardType;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView;
 import org.chromium.chrome.browser.payments.PaymentRequestTestRule.MainActivityStartCallback;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.ChromeTabUtils;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
 
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
@@ -81,7 +81,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
 
         // Make sure the events were logged correctly.
         int expectedSample = Event.SHOWN | Event.PAY_CLICKED | Event.RECEIVED_INSTRUMENT_DETAILS
@@ -330,7 +330,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
 
         // Make sure the events were logged correctly.
         int expectedSample = Event.SHOWN | Event.PAY_CLICKED | Event.RECEIVED_INSTRUMENT_DETAILS
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestNameAndFreeShippingTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestNameAndFreeShippingTest.java
index e24bc053..cfba731 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestNameAndFreeShippingTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestNameAndFreeShippingTest.java
@@ -20,9 +20,9 @@
 import org.chromium.chrome.browser.autofill.CardType;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView;
 import org.chromium.chrome.browser.payments.PaymentRequestTestRule.MainActivityStartCallback;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
 
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
@@ -62,7 +62,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
         mPaymentRequestTestRule.expectResultContains(new String[] {"Jon Doe", "Jon Doe",
                 "4111111111111111", "12", "2050", "basic-card", "123", "Google", "340 Main St",
                 "CA", "Los Angeles", "90291", "US", "en", "freeShippingOption"});
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestNameTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestNameTest.java
index c5a78fad..feb7ce5 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestNameTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestNameTest.java
@@ -20,9 +20,9 @@
 import org.chromium.chrome.browser.autofill.CardType;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView;
 import org.chromium.chrome.browser.payments.PaymentRequestTestRule.MainActivityStartCallback;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
 
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
@@ -81,7 +81,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
         mPaymentRequestTestRule.expectResultContains(new String[] {"Jon Doe"});
     }
 
@@ -127,7 +127,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
         mPaymentRequestTestRule.expectResultContains(new String[] {"Jane Jones"});
     }
 
@@ -162,7 +162,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
         mPaymentRequestTestRule.expectResultContains(new String[] {"Jon Doe"});
 
         int expectedSample = Event.SHOWN | Event.PAY_CLICKED | Event.RECEIVED_INSTRUMENT_DETAILS
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestNoShippingTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestNoShippingTest.java
index 43073ca..351d5df 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestNoShippingTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestNoShippingTest.java
@@ -27,9 +27,9 @@
 import org.chromium.chrome.browser.autofill.CardType;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView;
 import org.chromium.chrome.browser.payments.PaymentRequestTestRule.MainActivityStartCallback;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
 
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
@@ -107,7 +107,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
         mPaymentRequestTestRule.expectResultContains(
                 new String[] {"Jon Doe", "4111111111111111", "12", "2050", "basic-card", "123"});
     }
@@ -122,13 +122,13 @@
         mPaymentRequestTestRule.clickAndWait(
                 R.id.button_primary, mPaymentRequestTestRule.getReadyForUnmaskInput());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.NEGATIVE, mPaymentRequestTestRule.getReadyToPay());
+                ModalDialogProperties.ButtonType.NEGATIVE, mPaymentRequestTestRule.getReadyToPay());
         mPaymentRequestTestRule.clickAndWait(
                 R.id.button_primary, mPaymentRequestTestRule.getReadyForUnmaskInput());
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
         mPaymentRequestTestRule.expectResultContains(
                 new String[] {"Jon Doe", "4111111111111111", "12", "2050", "basic-card", "123"});
     }
@@ -182,7 +182,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
         mPaymentRequestTestRule.expectResultContains(
                 new String[] {"5454545454545454", "12", "Bob"});
     }
@@ -206,7 +206,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
         mPaymentRequestTestRule.expectResultContains(
                 new String[] {"5454545454545454", "12", "Bob"});
     }
@@ -261,7 +261,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
         mPaymentRequestTestRule.expectResultContains(
                 new String[] {"5454545454545454", "12", "Bob", "Google", "1600 Amphitheatre Pkwy",
                         "Mountain View", "CA", "94043", "+16502530000"});
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestNoUpdateWithTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestNoUpdateWithTest.java
index be2e746c..646255c5 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestNoUpdateWithTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestNoUpdateWithTest.java
@@ -19,9 +19,9 @@
 import org.chromium.chrome.browser.autofill.CardType;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView;
 import org.chromium.chrome.browser.payments.PaymentRequestTestRule.MainActivityStartCallback;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
 
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
@@ -69,7 +69,7 @@
         mRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mRule.getReadyToUnmask());
         mRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mRule.getDismissed());
         mRule.expectResultContains(new String[] {"freeShipping"});
     }
 
@@ -88,7 +88,7 @@
         mRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mRule.getReadyToUnmask());
         mRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mRule.getDismissed());
         mRule.expectResultContains(new String[] {"freeShipping"});
     }
 
@@ -106,7 +106,7 @@
         mRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mRule.getReadyToUnmask());
         mRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mRule.getDismissed());
         mRule.expectResultContains(new String[] {"updatedShipping"});
     }
 }
\ No newline at end of file
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPaymentAppAndCardsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPaymentAppAndCardsTest.java
index cd0a720e..710cbd4 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPaymentAppAndCardsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPaymentAppAndCardsTest.java
@@ -24,9 +24,9 @@
 import org.chromium.chrome.browser.autofill.CardType;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView;
 import org.chromium.chrome.browser.payments.PaymentRequestTestRule.MainActivityStartCallback;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
 
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
@@ -193,7 +193,8 @@
             mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                     R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
             mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                    ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                    ModalDialogProperties.ButtonType.POSITIVE,
+                    mPaymentRequestTestRule.getDismissed());
             mPaymentRequestTestRule.expectResultContains(new String[] {
                     "Jon Doe", "4111111111111111", "12", "2050", "basic-card", "123"});
         }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPaymentMethodIdentifierTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPaymentMethodIdentifierTest.java
index a143cefe..da0a6e02 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPaymentMethodIdentifierTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPaymentMethodIdentifierTest.java
@@ -23,9 +23,9 @@
 import org.chromium.chrome.browser.autofill.CardType;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView;
 import org.chromium.chrome.browser.payments.PaymentRequestTestRule.MainActivityStartCallback;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
 
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
@@ -155,7 +155,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
         mPaymentRequestTestRule.expectResultContains(
                 new String[] {"Jon Doe", "4111111111111111", "12", "2050", "basic-card", "123"});
     }
@@ -188,7 +188,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
         mPaymentRequestTestRule.expectResultContains(
                 new String[] {"5555555555554444", "12", "Jane Jones", "123", "mastercard"});
     }
@@ -221,7 +221,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
         mPaymentRequestTestRule.expectResultContains(
                 new String[] {"4242424242424242", "12", "Jane Jones", "123", "basic-card"});
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPhoneAndFreeShippingTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPhoneAndFreeShippingTest.java
index d97ec7b..b237f15 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPhoneAndFreeShippingTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPhoneAndFreeShippingTest.java
@@ -20,9 +20,9 @@
 import org.chromium.chrome.browser.autofill.CardType;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView;
 import org.chromium.chrome.browser.payments.PaymentRequestTestRule.MainActivityStartCallback;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
 
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
@@ -62,7 +62,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
         mPaymentRequestTestRule.expectResultContains(new String[] {"+15555555555", "Jon Doe",
                 "4111111111111111", "12", "2050", "basic-card", "123", "Google", "340 Main St",
                 "CA", "Los Angeles", "90291", "US", "en", "freeShippingOption"});
@@ -84,7 +84,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
         mPaymentRequestTestRule.expectResultContains(new String[] {"+15555555555", "Jon Doe",
                 "4111111111111111", "12", "2050", "basic-card", "123", "Google", "340 Main St",
                 "CA", "Los Angeles", "90291", "US", "en", "freeShippingOption"});
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestRemoveBillingAddressTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestRemoveBillingAddressTest.java
index eae1cb7..db0f8e1 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestRemoveBillingAddressTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestRemoveBillingAddressTest.java
@@ -22,9 +22,9 @@
 import org.chromium.chrome.browser.autofill.CardType;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView;
 import org.chromium.chrome.browser.payments.PaymentRequestTestRule.MainActivityStartCallback;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
 
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
@@ -94,7 +94,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
         mPaymentRequestTestRule.expectResultContains(new String[] {"4111111111111111", "Alice",
                 "12", "123", "Jane Smith", "Google", "1600 Amphitheatre Pkwy", "CA",
                 "Mountain View", "94043", "US", "+15155435555", "en-US"});
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestServerCardTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestServerCardTest.java
index fbb0a29..b6149a2 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestServerCardTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestServerCardTest.java
@@ -19,9 +19,9 @@
 import org.chromium.chrome.browser.autofill.CardType;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView;
 import org.chromium.chrome.browser.payments.PaymentRequestTestRule.MainActivityStartCallback;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
 
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
@@ -60,7 +60,7 @@
         mPaymentRequestTestRule.clickAndWait(
                 R.id.button_primary, mPaymentRequestTestRule.getReadyForUnmaskInput());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.NEGATIVE, mPaymentRequestTestRule.getReadyToPay());
+                ModalDialogProperties.ButtonType.NEGATIVE, mPaymentRequestTestRule.getReadyToPay());
         mPaymentRequestTestRule.clickAndWait(
                 R.id.close_button, mPaymentRequestTestRule.getDismissed());
         mPaymentRequestTestRule.expectResultContains(new String[] {"Request cancelled"});
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTestRule.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTestRule.java
index a6b0b60..2447fee 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTestRule.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTestRule.java
@@ -26,8 +26,6 @@
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.autofill.CardUnmaskPrompt;
 import org.chromium.chrome.browser.autofill.CardUnmaskPrompt.CardUnmaskObserverForTest;
-import org.chromium.chrome.browser.modaldialog.ModalDialogProperties;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.payments.PaymentRequestImpl.PaymentRequestServiceObserverForTest;
 import org.chromium.chrome.browser.payments.ui.PaymentRequestSection.OptionSection;
 import org.chromium.chrome.browser.payments.ui.PaymentRequestSection.OptionSection.OptionRow;
@@ -43,6 +41,8 @@
 import org.chromium.payments.mojom.PaymentDetailsModifier;
 import org.chromium.payments.mojom.PaymentItem;
 import org.chromium.payments.mojom.PaymentMethodData;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
+import org.chromium.ui.modelutil.PropertyModel;
 
 import java.util.ArrayList;
 import java.util.HashSet;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestUseStatsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestUseStatsTest.java
index 615ea972..79e3406 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestUseStatsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestUseStatsTest.java
@@ -19,9 +19,9 @@
 import org.chromium.chrome.browser.autofill.CardType;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
-import org.chromium.chrome.browser.modaldialog.ModalDialogView;
 import org.chromium.chrome.browser.payments.PaymentRequestTestRule.MainActivityStartCallback;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
 
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
@@ -74,7 +74,7 @@
         mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
         mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogView.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
+                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
 
         // Get the current date value just after the end of the Payment Request.
         long timeAfterRecord = mHelper.getCurrentDateForTesting();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/jsdialog/VrBrowserJavaScriptModalDialogTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/jsdialog/VrBrowserJavaScriptModalDialogTest.java
index aa47630..462ccbd5 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/jsdialog/VrBrowserJavaScriptModalDialogTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/jsdialog/VrBrowserJavaScriptModalDialogTest.java
@@ -22,13 +22,13 @@
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.jsdialog.JavascriptAppModalDialog;
-import org.chromium.chrome.browser.modaldialog.ModalDialogProperties;
 import org.chromium.chrome.browser.vr.rules.ChromeTabbedActivityVrTestRule;
 import org.chromium.chrome.browser.vr.util.NativeUiUtils;
 import org.chromium.chrome.browser.vr.util.VrBrowserTransitionUtils;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.RenderTestRule;
 import org.chromium.content_public.browser.test.util.JavaScriptUtils;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
 
 import java.io.IOException;
 import java.util.concurrent.ExecutionException;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetControllerTest.java
index 926b9af..c61f0ae 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetControllerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetControllerTest.java
@@ -35,10 +35,10 @@
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Tab;
 import org.chromium.chrome.browser.modelutil.ListObservable;
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
-import org.chromium.chrome.browser.modelutil.PropertyObservable;
 import org.chromium.chrome.test.util.browser.modelutil.FakeViewProvider;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyObservable;
 
 /**
  * Controller tests for the keyboard accessory bottom sheet component.
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryControllerTest.java
index 54379a47f..84a0777 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryControllerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryControllerTest.java
@@ -33,10 +33,10 @@
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Action;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.PropertyProvider;
 import org.chromium.chrome.browser.modelutil.ListObservable;
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
-import org.chromium.chrome.browser.modelutil.PropertyObservable.PropertyObserver;
 import org.chromium.chrome.test.util.browser.modelutil.FakeViewProvider;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyObservable.PropertyObserver;
 
 import java.util.HashMap;
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutControllerTest.java
index 4008b14..fc8c314 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutControllerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutControllerTest.java
@@ -27,9 +27,9 @@
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.modelutil.ListObservable;
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
-import org.chromium.chrome.browser.modelutil.PropertyObservable.PropertyObserver;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyObservable.PropertyObserver;
 
 import java.util.HashMap;
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingControllerTest.java
index de23ea44..80d8428 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingControllerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingControllerTest.java
@@ -55,7 +55,6 @@
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
 import org.chromium.chrome.browser.modelutil.ListModel;
 import org.chromium.chrome.browser.modelutil.ListObservable;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.Tab.TabHidingType;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
@@ -66,6 +65,7 @@
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.KeyboardVisibilityDelegate;
 import org.chromium.ui.display.DisplayAndroid;
+import org.chromium.ui.modelutil.PropertyModel;
 
 import java.lang.ref.WeakReference;
 import java.util.Map;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/modelutil/LazyConstructionPropertyMcpTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/modelutil/LazyConstructionPropertyMcpTest.java
index 87f4cb2..d0b3082 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/modelutil/LazyConstructionPropertyMcpTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/modelutil/LazyConstructionPropertyMcpTest.java
@@ -27,11 +27,14 @@
 import org.robolectric.shadows.ShadowLooper;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableBooleanPropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableIntPropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableObjectPropertyKey;
 import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor.ViewBinder;
 import org.chromium.chrome.test.util.browser.modelutil.FakeViewProvider;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModel.WritableBooleanPropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableIntPropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
+import org.chromium.ui.modelutil.PropertyObservable;
 
 /**
  * Unit tests for LazyConstructionPropertyMcp.
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/modelutil/PropertyModelTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/modelutil/PropertyModelTest.java
index 601bb004..3747456 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/modelutil/PropertyModelTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/modelutil/PropertyModelTest.java
@@ -17,11 +17,13 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableBooleanPropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableFloatPropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableIntPropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel.WritableObjectPropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyObservable.PropertyObserver;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModel.WritableBooleanPropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableFloatPropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableIntPropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
+import org.chromium.ui.modelutil.PropertyObservable.PropertyObserver;
 
 import java.util.ArrayList;
 import java.util.Collection;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/UrlBarMediatorUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/UrlBarMediatorUnitTest.java
index 5c88aa3..0926060 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/UrlBarMediatorUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/UrlBarMediatorUnitTest.java
@@ -18,9 +18,9 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.chrome.browser.modelutil.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
-import org.chromium.chrome.browser.modelutil.PropertyObservable.PropertyObserver;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyObservable.PropertyObserver;
 
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 5843574d..21db56e 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -5833,6 +5833,9 @@
       <message name="IDS_MEDIA_TAB_CAPTURE_WITH_AUDIO_NOTIFICATION_TEXT_DELEGATED" desc="label used in screen capture notification UI to show the screen sharing status when the stream is consumed by a tab instead of the app initiating the window picker">
         <ph name="APP_NAME">$1<ex>html5rocks.com</ex></ph> is sharing a Chrome tab and audio with <ph name="TAB_NAME">$2<ex>https://google.com</ex></ph>.
       </message>
+      <message name="IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_SOURCE" desc="Text shown on the button that changes the screen capture source.">
+        Change source
+      </message>
       <message name="IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_STOP" desc="Text shown on the button that stops screen capture.">
         Stop sharing
       </message>
diff --git a/chrome/app/generated_resources_grd/IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_SOURCE.png.sha1 b/chrome/app/generated_resources_grd/IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_SOURCE.png.sha1
new file mode 100644
index 0000000..d1e924e9
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_SOURCE.png.sha1
@@ -0,0 +1 @@
+33f4d4a42b06f6de5402213f1ab8cd15efff19ee
\ No newline at end of file
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 86950e0..7d5a4f8 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1862,9 +1862,6 @@
      SINGLE_VALUE_TYPE(app_list::switches::kResetAppListInstallState)},
 #endif  // BUILDFLAG(ENABLE_APP_LIST)
 #if defined(OS_ANDROID)
-    {"enable-special-locale", flag_descriptions::kEnableSpecialLocaleName,
-     flag_descriptions::kEnableSpecialLocaleDescription, kOsAndroid,
-     FEATURE_VALUE_TYPE(chrome::android::kSpecialLocaleFeature)},
     {"enable-accessibility-tab-switcher",
      flag_descriptions::kAccessibilityTabSwitcherName,
      flag_descriptions::kAccessibilityTabSwitcherDescription, kOsAndroid,
@@ -3470,11 +3467,6 @@
      FEATURE_VALUE_TYPE(blink::features::kStopInBackground)},
 
 #if defined(OS_CHROMEOS)
-    {"ash-keyboard-shortcut-viewer-app",
-     flag_descriptions::kAshKeyboardShortcutViewerAppName,
-     flag_descriptions::kAshKeyboardShortcutViewerAppDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(ash::features::kKeyboardShortcutViewerApp)},
-
     {"ash-disable-login-dim-and-blur",
      flag_descriptions::kAshDisableLoginDimAndBlurName,
      flag_descriptions::kAshDisableLoginDimAndBlurDescription, kOsCrOS,
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc
index d223eca..d689f16 100644
--- a/chrome/browser/android/chrome_feature_list.cc
+++ b/chrome/browser/android/chrome_feature_list.cc
@@ -142,7 +142,6 @@
     &kServiceManagerForDownload,
     &kSoleIntegration,
     &kSpannableInlineAutocomplete,
-    &kSpecialLocaleFeature,
     &kSpecialLocaleWrapper,
     &kTabReparenting,
     &kTabSwitcherOnReturn,
@@ -398,9 +397,6 @@
 const base::Feature kSpannableInlineAutocomplete{
     "SpannableInlineAutocomplete", base::FEATURE_ENABLED_BY_DEFAULT};
 
-const base::Feature kSpecialLocaleFeature{"SpecialLocale",
-                                          base::FEATURE_DISABLED_BY_DEFAULT};
-
 const base::Feature kSpecialLocaleWrapper{"SpecialLocaleWrapper",
                                           base::FEATURE_ENABLED_BY_DEFAULT};
 
diff --git a/chrome/browser/android/chrome_feature_list.h b/chrome/browser/android/chrome_feature_list.h
index 2d02adee..1ed5ba5 100644
--- a/chrome/browser/android/chrome_feature_list.h
+++ b/chrome/browser/android/chrome_feature_list.h
@@ -78,7 +78,6 @@
 extern const base::Feature kServiceManagerForDownload;
 extern const base::Feature kSoleIntegration;
 extern const base::Feature kSpannableInlineAutocomplete;
-extern const base::Feature kSpecialLocaleFeature;
 extern const base::Feature kSpecialLocaleWrapper;
 extern const base::Feature kTabReparenting;
 extern const base::Feature kTabSwitcherOnReturn;
diff --git a/chrome/browser/apps/platform_apps/api/BUILD.gn b/chrome/browser/apps/platform_apps/api/BUILD.gn
index 792a997..f88b325 100644
--- a/chrome/browser/apps/platform_apps/api/BUILD.gn
+++ b/chrome/browser/apps/platform_apps/api/BUILD.gn
@@ -73,6 +73,7 @@
       "//chrome:strings",
       "//chromeos/components/multidevice/logging",
       "//chromeos/components/proximity_auth",
+      "//chromeos/cryptohome",
       "//components/account_id",
       "//components/strings:components_strings_grit",
       "//components/user_manager",
diff --git a/chrome/browser/apps/platform_apps/api/music_manager_private/BUILD.gn b/chrome/browser/apps/platform_apps/api/music_manager_private/BUILD.gn
index c543fd81..60ff11c 100644
--- a/chrome/browser/apps/platform_apps/api/music_manager_private/BUILD.gn
+++ b/chrome/browser/apps/platform_apps/api/music_manager_private/BUILD.gn
@@ -24,15 +24,15 @@
     "//base",
     "//chrome/common/apps/platform_apps/api",
     "//content/public/browser",
+    "//crypto",
     "//extensions/browser",
     "//rlz/buildflags",
-    "//crypto",
   ]
 
   if (is_chromeos) {
     sources -= [ "device_id_linux.cc" ]
 
-    deps += [ "//chromeos" ]
+    deps += [ "//chromeos/cryptohome" ]
   }
 
   if (enable_rlz_support) {
diff --git a/chrome/browser/browser_switcher/alternative_browser_driver.h b/chrome/browser/browser_switcher/alternative_browser_driver.h
index 7662f97..f9625ef5 100644
--- a/chrome/browser/browser_switcher/alternative_browser_driver.h
+++ b/chrome/browser/browser_switcher/alternative_browser_driver.h
@@ -26,6 +26,8 @@
  public:
   virtual ~AlternativeBrowserDriver();
 
+  // TODO(nicolaso): Remove SetBrowserPath() and SetBrowserParameters().
+
   // Updates the executable path that will be used for the browser when it is
   // launched. |path| is not necessarily a valid file path. It may be a
   // placeholder such as "${ie}".
@@ -35,13 +37,26 @@
   // launched.
   virtual void SetBrowserParameters(const base::ListValue* parameters) = 0;
 
+  // Expands environment variables and possibly tildes (~) in the given
+  // string. On Windows, this should expand %VAR% to the var's value. On POSIX
+  // systems, this should expand $VAR and ${VAR}, and expand ~ to the home
+  // directory.
+  //
+  // "${url}" is special and is left as is in the string.
+  virtual void ExpandEnvVars(std::string* str) const = 0;
+
+  // If the string is a preset browser placeholder (e.g. ${ie}, ${firefox}),
+  // expands it to an executable path. The list of preset browsers is
+  // platform-specific.
+  virtual void ExpandPresetBrowsers(std::string* str) const = 0;
+
   // Tries to launch |browser| at the specified URL, using whatever
   // method is most appropriate.
   virtual bool TryLaunch(const GURL& url) = 0;
 };
 
-// Default concrete implementation for |AlternativeBrowserDriver|. This uses
-// Windows primitives to access
+// Default concrete implementation for |AlternativeBrowserDriver|. Uses a
+// platform-specific method to locate and launch the appropriate browser.
 class AlternativeBrowserDriverImpl : public AlternativeBrowserDriver {
  public:
   AlternativeBrowserDriverImpl();
@@ -50,6 +65,8 @@
   // AlternativeBrowserDriver
   void SetBrowserPath(base::StringPiece path) override;
   void SetBrowserParameters(const base::ListValue* parameters) override;
+  void ExpandEnvVars(std::string* str) const override;
+  void ExpandPresetBrowsers(std::string* str) const override;
   bool TryLaunch(const GURL& url) override;
 
   // Create the CommandLine object that would be used to launch an external
diff --git a/chrome/browser/browser_switcher/alternative_browser_driver_posix.cc b/chrome/browser/browser_switcher/alternative_browser_driver_posix.cc
index 5d597fb..e1c1b97 100644
--- a/chrome/browser/browser_switcher/alternative_browser_driver_posix.cc
+++ b/chrome/browser/browser_switcher/alternative_browser_driver_posix.cc
@@ -131,18 +131,7 @@
 
 void AlternativeBrowserDriverImpl::SetBrowserPath(base::StringPiece path) {
   browser_path_ = path.as_string();
-#if defined(OS_MACOSX)
-  // Unlike most POSIX platforms, MacOS always has another browser than Chrome,
-  // so admins don't have to explicitly configure one.
-  if (browser_path_.empty()) {
-    browser_path_ = kSafariExecutableName;
-  }
-#endif
-  for (const auto& mapping : kBrowserVarMappings) {
-    if (!browser_path_.compare(mapping.var_name)) {
-      browser_path_ = mapping.executable_name;
-    }
-  }
+  ExpandPresetBrowsers(&browser_path_);
 }
 
 void AlternativeBrowserDriverImpl::SetBrowserParameters(
@@ -155,6 +144,29 @@
   }
 }
 
+void AlternativeBrowserDriverImpl::ExpandPresetBrowsers(
+    std::string* str) const {
+#if defined(OS_MACOSX)
+  // Unlike most POSIX platforms, MacOS always has another browser than Chrome,
+  // so admins don't have to explicitly configure one.
+  if (str->empty()) {
+    *str = kSafariExecutableName;
+    return;
+  }
+#endif
+  for (const auto& mapping : kBrowserVarMappings) {
+    if (!str->compare(mapping.var_name)) {
+      *str = mapping.executable_name;
+      return;
+    }
+  }
+}
+
+void AlternativeBrowserDriverImpl::ExpandEnvVars(std::string* str) const {
+  ExpandTilde(str);
+  ExpandEnvironmentVariables(str);
+}
+
 bool AlternativeBrowserDriverImpl::TryLaunch(const GURL& url) {
   if (browser_path_.empty()) {
     LOG(ERROR) << "Alternative browser not configured. "
diff --git a/chrome/browser/browser_switcher/alternative_browser_driver_win.cc b/chrome/browser/browser_switcher/alternative_browser_driver_win.cc
index 12b74ae5..c9c658b 100644
--- a/chrome/browser/browser_switcher/alternative_browser_driver_win.cc
+++ b/chrome/browser/browser_switcher/alternative_browser_driver_win.cc
@@ -171,6 +171,27 @@
   return (TryLaunchWithDde(url) || TryLaunchWithExec(url));
 }
 
+void AlternativeBrowserDriverImpl::ExpandEnvVars(std::string* str) const {
+  std::wstring wide = base::UTF8ToWide(*str);
+  ExpandEnvironmentVariables(&wide);
+  *str = base::WideToUTF8(wide);
+}
+
+void AlternativeBrowserDriverImpl::ExpandPresetBrowsers(
+    std::string* str) const {
+  if (str->empty()) {
+    *str = base::WideToUTF8(GetBrowserLocation(kIExploreKey));
+    return;
+  }
+  std::wstring wide = base::UTF8ToWide(*str);
+  for (const auto& mapping : kBrowserVarMappings) {
+    if (!wide.compare(mapping.var_name)) {
+      *str = base::WideToUTF8(GetBrowserLocation(mapping.registry_key));
+      return;
+    }
+  }
+}
+
 bool AlternativeBrowserDriverImpl::TryLaunchWithDde(const GURL& url) {
   if (dde_host_.empty())
     return false;
diff --git a/chrome/browser/browser_switcher/alternative_browser_launcher_unittest.cc b/chrome/browser/browser_switcher/alternative_browser_launcher_unittest.cc
index 1e9d0fc..20d28f27 100644
--- a/chrome/browser/browser_switcher/alternative_browser_launcher_unittest.cc
+++ b/chrome/browser/browser_switcher/alternative_browser_launcher_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "base/values.h"
 #include "chrome/browser/browser_switcher/browser_switcher_prefs.h"
+#include "chrome/browser/browser_switcher/mock_alternative_browser_driver.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/scoped_user_pref_update.h"
 #include "components/prefs/testing_pref_service.h"
@@ -23,15 +24,6 @@
 
 const char kExampleDotCom[] = "http://example.com/";
 
-class MockAlternativeBrowserDriver : public AlternativeBrowserDriver {
- public:
-  MockAlternativeBrowserDriver() = default;
-  ~MockAlternativeBrowserDriver() override = default;
-
-  MOCK_METHOD1(SetBrowserPath, void(base::StringPiece));
-  MOCK_METHOD1(SetBrowserParameters, void(const base::ListValue*));
-  MOCK_METHOD1(TryLaunch, bool(const GURL&));
-};
 
 }  // namespace
 
@@ -40,7 +32,8 @@
   void SetUp() override {
     prefs_.registry()->RegisterStringPref(prefs::kAlternativeBrowserPath, "");
     prefs_.registry()->RegisterListPref(prefs::kAlternativeBrowserParameters);
-    driver_ = new MockAlternativeBrowserDriver();
+    auto driver = std::make_unique<MockAlternativeBrowserDriver>();
+    driver_ = driver.get();
     EXPECT_CALL(*driver_, SetBrowserPath(_))
         .WillOnce(Invoke([](base::StringPiece path) { EXPECT_EQ("", path); }));
     EXPECT_CALL(*driver_, SetBrowserParameters(_))
@@ -48,7 +41,7 @@
           EXPECT_TRUE(parameters->GetList().empty());
         }));
     launcher_ = std::make_unique<AlternativeBrowserLauncherImpl>(
-        &prefs_, std::unique_ptr<AlternativeBrowserDriver>(driver_));
+        &prefs_, std::move(driver));
   }
 
   TestingPrefServiceSimple* prefs() { return &prefs_; }
diff --git a/chrome/browser/browser_switcher/browser_switcher_prefs.cc b/chrome/browser/browser_switcher/browser_switcher_prefs.cc
index 4d33e0b9..3775abc81 100644
--- a/chrome/browser/browser_switcher/browser_switcher_prefs.cc
+++ b/chrome/browser/browser_switcher/browser_switcher_prefs.cc
@@ -4,10 +4,157 @@
 
 #include "chrome/browser/browser_switcher/browser_switcher_prefs.h"
 
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/metrics/histogram_macros.h"
 #include "build/build_config.h"
+#include "chrome/browser/browser_switcher/alternative_browser_driver.h"
 #include "components/pref_registry/pref_registry_syncable.h"
+#include "components/prefs/pref_service.h"
 
 namespace browser_switcher {
+
+RuleSet::RuleSet() = default;
+RuleSet::~RuleSet() = default;
+
+BrowserSwitcherPrefs::BrowserSwitcherPrefs(PrefService* prefs,
+                                           AlternativeBrowserDriver* driver)
+    : prefs_(prefs), driver_(driver) {
+  DCHECK(prefs_);
+
+  change_registrar_.Init(prefs_);
+
+  const struct {
+    const char* pref_name;
+    void (BrowserSwitcherPrefs::*callback)();
+  } hooks[] = {
+      {prefs::kAlternativeBrowserPath,
+       &BrowserSwitcherPrefs::AlternativeBrowserPathChanged},
+      {prefs::kAlternativeBrowserParameters,
+       &BrowserSwitcherPrefs::AlternativeBrowserParametersChanged},
+      {prefs::kUrlList, &BrowserSwitcherPrefs::UrlListChanged},
+      {prefs::kUrlGreylist, &BrowserSwitcherPrefs::GreylistChanged},
+  };
+
+  // Listen for pref changes, and run all the hooks once to initialize state.
+  for (const auto& hook : hooks) {
+    change_registrar_.Add(
+        hook.pref_name,
+        base::BindRepeating(hook.callback, base::Unretained(this)));
+    (*this.*hook.callback)();
+  }
+}
+
+BrowserSwitcherPrefs::~BrowserSwitcherPrefs() = default;
+
+// static
+void BrowserSwitcherPrefs::RegisterProfilePrefs(
+    user_prefs::PrefRegistrySyncable* registry) {
+  registry->RegisterStringPref(prefs::kAlternativeBrowserPath, "");
+  registry->RegisterListPref(prefs::kAlternativeBrowserParameters);
+  registry->RegisterListPref(prefs::kUrlList);
+  registry->RegisterListPref(prefs::kUrlGreylist);
+  registry->RegisterStringPref(prefs::kExternalSitelistUrl, "");
+#if defined(OS_WIN)
+  registry->RegisterBooleanPref(prefs::kUseIeSitelist, false);
+#endif
+}
+
+const std::string& BrowserSwitcherPrefs::GetAlternativeBrowserPath() {
+  return alt_browser_path_;
+}
+
+std::string BrowserSwitcherPrefs::GetAlternativeBrowserPathRaw() {
+  if (!prefs_->IsManagedPreference(prefs::kAlternativeBrowserPath))
+    return std::string();
+  return prefs_->GetString(prefs::kAlternativeBrowserPath);
+}
+
+const std::vector<std::string>&
+BrowserSwitcherPrefs::GetAlternativeBrowserParameters() {
+  return alt_browser_params_;
+}
+
+const RuleSet& BrowserSwitcherPrefs::GetRules() {
+  return rules_;
+}
+
+GURL BrowserSwitcherPrefs::GetExternalSitelistUrl() {
+  if (!prefs_->IsManagedPreference(prefs::kExternalSitelistUrl))
+    return GURL();
+  return GURL(prefs_->GetString(prefs::kExternalSitelistUrl));
+}
+
+#if defined(OS_WIN)
+bool BrowserSwitcherPrefs::UseIeSitelist() {
+  if (!prefs_->IsManagedPreference(prefs::kUseIeSitelist))
+    return false;
+  return prefs_->GetBoolean(prefs::kUseIeSitelist);
+}
+#endif
+
+void BrowserSwitcherPrefs::AlternativeBrowserPathChanged() {
+  alt_browser_path_.clear();
+  if (prefs_->IsManagedPreference(prefs::kAlternativeBrowserPath))
+    alt_browser_path_ = prefs_->GetString(prefs::kAlternativeBrowserPath);
+  driver_->ExpandPresetBrowsers(&alt_browser_path_);
+  driver_->ExpandEnvVars(&alt_browser_path_);
+}
+
+void BrowserSwitcherPrefs::AlternativeBrowserParametersChanged() {
+  alt_browser_params_.clear();
+  if (!prefs_->IsManagedPreference(prefs::kAlternativeBrowserParameters))
+    return;
+  const base::ListValue* params =
+      prefs_->GetList(prefs::kAlternativeBrowserParameters);
+  for (const auto& param : *params) {
+    std::string param_string = param.GetString();
+    driver_->ExpandEnvVars(&param_string);
+    alt_browser_params_.push_back(param_string);
+  }
+}
+
+void BrowserSwitcherPrefs::UrlListChanged() {
+  rules_.sitelist.clear();
+
+  if (!prefs_->IsManagedPreference(prefs::kUrlList))
+    return;
+
+  UMA_HISTOGRAM_COUNTS_100000(
+      "BrowserSwitcher.UrlListSize",
+      prefs_->GetList(prefs::kUrlList)->GetList().size());
+
+  bool has_wildcard = false;
+  for (const auto& url : *prefs_->GetList(prefs::kUrlList)) {
+    rules_.sitelist.push_back(url.GetString());
+    if (url.GetString() == "*")
+      has_wildcard = true;
+  }
+
+  UMA_HISTOGRAM_BOOLEAN("BrowserSwitcher.UrlListWildcard", has_wildcard);
+}
+
+void BrowserSwitcherPrefs::GreylistChanged() {
+  rules_.greylist.clear();
+
+  // This pref is sensitive. Only set through policies.
+  if (!prefs_->IsManagedPreference(prefs::kUrlGreylist))
+    return;
+
+  UMA_HISTOGRAM_COUNTS_100000(
+      "BrowserSwitcher.GreylistSize",
+      prefs_->GetList(prefs::kUrlGreylist)->GetList().size());
+
+  bool has_wildcard = false;
+  for (const auto& url : *prefs_->GetList(prefs::kUrlGreylist)) {
+    rules_.greylist.push_back(url.GetString());
+    if (url.GetString() == "*")
+      has_wildcard = true;
+  }
+
+  UMA_HISTOGRAM_BOOLEAN("BrowserSwitcher.UrlListWildcard", has_wildcard);
+}
+
 namespace prefs {
 
 // Path to the executable of the alternative browser, or one of "${chrome}",
@@ -35,14 +182,6 @@
 #endif
 
 void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
-  registry->RegisterStringPref(prefs::kAlternativeBrowserPath, "");
-  registry->RegisterListPref(prefs::kAlternativeBrowserParameters);
-  registry->RegisterListPref(prefs::kUrlList);
-  registry->RegisterListPref(prefs::kUrlGreylist);
-  registry->RegisterStringPref(prefs::kExternalSitelistUrl, "");
-#if defined(OS_WIN)
-  registry->RegisterBooleanPref(prefs::kUseIeSitelist, false);
-#endif
 }
 
 }  // namespace prefs
diff --git a/chrome/browser/browser_switcher/browser_switcher_prefs.h b/chrome/browser/browser_switcher/browser_switcher_prefs.h
index 5b8456e..03991aa0 100644
--- a/chrome/browser/browser_switcher/browser_switcher_prefs.h
+++ b/chrome/browser/browser_switcher/browser_switcher_prefs.h
@@ -5,13 +5,93 @@
 #ifndef CHROME_BROWSER_BROWSER_SWITCHER_BROWSER_SWITCHER_PREFS_H_
 #define CHROME_BROWSER_BROWSER_SWITCHER_BROWSER_SWITCHER_PREFS_H_
 
+#include "base/macros.h"
 #include "build/build_config.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "url/gurl.h"
 
 namespace user_prefs {
 class PrefRegistrySyncable;
 }  // namespace user_prefs
 
+class PrefService;
+
 namespace browser_switcher {
+
+class AlternativeBrowserDriver;
+
+// A named pair type.
+struct RuleSet {
+  RuleSet();
+  ~RuleSet();
+
+  std::vector<std::string> sitelist;
+  std::vector<std::string> greylist;
+};
+
+// Contains the current state of the prefs related to LBS. For sensitive prefs,
+// only respects managed prefs. Also does some type conversions and
+// transformations on the prefs (e.g. expanding preset values for
+// AlternativeBrowserPath).
+class BrowserSwitcherPrefs {
+ public:
+  BrowserSwitcherPrefs(PrefService* prefs, AlternativeBrowserDriver* driver);
+  virtual ~BrowserSwitcherPrefs();
+
+  static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
+
+  // Returns the path to the alternative browser to launch, after substitutions
+  // (preset browsers, environment variables, tildes). If the pref is not
+  // managed, returns the empty string.
+  const std::string& GetAlternativeBrowserPath();
+
+  // Same as above, but returns the value before substitutions. Still checks
+  // that the pref is managed.
+  std::string GetAlternativeBrowserPathRaw();
+
+  // Returns the arguments to pass to the alternative browser, after
+  // substitutions (environment variables, tildes). If the pref is not managed,
+  // returns the empty string.
+  const std::vector<std::string>& GetAlternativeBrowserParameters();
+
+  // Returns the sitelist + greylist configured directly through Chrome
+  // policies. If the pref is not managed, returns an empty vector.
+  const RuleSet& GetRules();
+
+  // Returns the URL to download for an external XML sitelist. If the pref is
+  // not managed, returns an invalid URL.
+  GURL GetExternalSitelistUrl();
+
+#if defined(OS_WIN)
+  // Returns true if Chrome should download and apply the XML sitelist from
+  // IEEM's SiteList policy. If the pref is not managed, returns false.
+  bool UseIeSitelist();
+#endif
+
+ private:
+  // Hooks for PrefChangeRegistrar.
+  void AlternativeBrowserPathChanged();
+  void AlternativeBrowserParametersChanged();
+  void UrlListChanged();
+  void GreylistChanged();
+
+  PrefService* prefs_;
+  PrefChangeRegistrar change_registrar_;
+
+  // Used to expand environment variables and platform-specific presets
+  // (e.g. ${ie}) in AlternativeBrowserPath.
+  AlternativeBrowserDriver* driver_;
+
+  // Type-converted and/or expanded pref values, updated by the
+  // PrefChangeRegistrar hooks.
+  std::string alt_browser_path_;
+  std::vector<std::string> alt_browser_params_;
+
+  RuleSet rules_;
+
+  DISALLOW_COPY_AND_ASSIGN(BrowserSwitcherPrefs);
+};
+
 namespace prefs {
 
 extern const char kAlternativeBrowserPath[];
@@ -24,9 +104,8 @@
 extern const char kUseIeSitelist[];
 #endif
 
-void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
-
 }  // namespace prefs
+
 }  // namespace browser_switcher
 
 #endif  // CHROME_BROWSER_BROWSER_SWITCHER_BROWSER_SWITCHER_PREFS_H_
diff --git a/chrome/browser/browser_switcher/browser_switcher_prefs_unittest.cc b/chrome/browser/browser_switcher/browser_switcher_prefs_unittest.cc
new file mode 100644
index 0000000..dc1d8b0
--- /dev/null
+++ b/chrome/browser/browser_switcher/browser_switcher_prefs_unittest.cc
@@ -0,0 +1,108 @@
+// 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/browser_switcher/browser_switcher_sitelist.h"
+
+#include "base/values.h"
+#include "chrome/browser/browser_switcher/browser_switcher_prefs.h"
+#include "chrome/browser/browser_switcher/ieem_sitelist_parser.h"
+#include "chrome/browser/browser_switcher/mock_alternative_browser_driver.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+using testing::_;
+using testing::Invoke;
+
+namespace browser_switcher {
+
+namespace {
+
+std::unique_ptr<base::Value> StringArrayToValue(
+    const std::vector<const char*>& strings) {
+  std::vector<base::Value> values(strings.size());
+  std::transform(strings.begin(), strings.end(), values.begin(),
+                 [](const char* s) { return base::Value(s); });
+  return std::make_unique<base::Value>(values);
+}
+
+void SetStringToItWorked(std::string* str) {
+  *str = "it worked";
+}
+
+}  // namespace
+
+class BrowserSwitcherPrefsTest : public testing::Test {
+ public:
+  void SetUp() override {
+    BrowserSwitcherPrefs::RegisterProfilePrefs(prefs_backend_.registry());
+    prefs_ = std::make_unique<BrowserSwitcherPrefs>(&prefs_backend_, &driver_);
+  }
+
+  sync_preferences::TestingPrefServiceSyncable* prefs_backend() {
+    return &prefs_backend_;
+  }
+  MockAlternativeBrowserDriver& driver() { return driver_; }
+  BrowserSwitcherPrefs* prefs() { return prefs_.get(); }
+
+ private:
+  sync_preferences::TestingPrefServiceSyncable prefs_backend_;
+  MockAlternativeBrowserDriver driver_;
+  std::unique_ptr<BrowserSwitcherPrefs> prefs_;
+};
+
+TEST_F(BrowserSwitcherPrefsTest, ListensForPrefChanges) {
+  prefs_backend()->SetManagedPref(prefs::kAlternativeBrowserPath,
+                                  std::make_unique<base::Value>("notepad.exe"));
+  prefs_backend()->SetManagedPref(prefs::kAlternativeBrowserParameters,
+                                  StringArrayToValue({"a", "b", "c"}));
+  prefs_backend()->SetManagedPref(prefs::kUrlList,
+                                  StringArrayToValue({"example.com"}));
+  prefs_backend()->SetManagedPref(prefs::kUrlGreylist,
+                                  StringArrayToValue({"foo.example.com"}));
+
+  EXPECT_EQ("notepad.exe", prefs()->GetAlternativeBrowserPath());
+
+  EXPECT_EQ(3u, prefs()->GetAlternativeBrowserParameters().size());
+  EXPECT_EQ("a", prefs()->GetAlternativeBrowserParameters()[0]);
+  EXPECT_EQ("b", prefs()->GetAlternativeBrowserParameters()[1]);
+  EXPECT_EQ("c", prefs()->GetAlternativeBrowserParameters()[2]);
+
+  EXPECT_EQ(1u, prefs()->GetRules().sitelist.size());
+  EXPECT_EQ("example.com", prefs()->GetRules().sitelist[0]);
+
+  EXPECT_EQ(1u, prefs()->GetRules().greylist.size());
+  EXPECT_EQ("foo.example.com", prefs()->GetRules().greylist[0]);
+}
+
+TEST_F(BrowserSwitcherPrefsTest, ExpandsEnvironmentVariablesInPath) {
+  EXPECT_CALL(driver(), ExpandEnvVars(_))
+      .WillOnce(Invoke(&SetStringToItWorked));
+  prefs_backend()->SetManagedPref(
+      prefs::kAlternativeBrowserPath,
+      std::make_unique<base::Value>("it didn't work"));
+  EXPECT_EQ("it worked", prefs()->GetAlternativeBrowserPath());
+}
+
+TEST_F(BrowserSwitcherPrefsTest, ExpandsPresetBrowsersInPath) {
+  EXPECT_CALL(driver(), ExpandPresetBrowsers(_))
+      .WillOnce(Invoke(&SetStringToItWorked));
+  prefs_backend()->SetManagedPref(
+      prefs::kAlternativeBrowserPath,
+      std::make_unique<base::Value>("it didn't work"));
+  EXPECT_EQ("it worked", prefs()->GetAlternativeBrowserPath());
+}
+
+TEST_F(BrowserSwitcherPrefsTest, ExpandsEnvironmentVariablesInParameters) {
+  EXPECT_CALL(driver(), ExpandEnvVars(_))
+      .WillOnce(Invoke(&SetStringToItWorked));
+  prefs_backend()->SetManagedPref(prefs::kAlternativeBrowserParameters,
+                                  StringArrayToValue({"it didn't work"}));
+  EXPECT_EQ(1u, prefs()->GetAlternativeBrowserParameters().size());
+  EXPECT_EQ("it worked", prefs()->GetAlternativeBrowserParameters()[0]);
+}
+
+}  // namespace browser_switcher
diff --git a/chrome/browser/browser_switcher/browser_switcher_sitelist.cc b/chrome/browser/browser_switcher/browser_switcher_sitelist.cc
index faa735b..6a93e8d 100644
--- a/chrome/browser/browser_switcher/browser_switcher_sitelist.cc
+++ b/chrome/browser/browser_switcher/browser_switcher_sitelist.cc
@@ -95,9 +95,6 @@
 
 }  // namespace
 
-BrowserSwitcherSitelistImpl::RuleSet::RuleSet() = default;
-BrowserSwitcherSitelistImpl::RuleSet::~RuleSet() = default;
-
 BrowserSwitcherSitelist::~BrowserSwitcherSitelist() = default;
 
 BrowserSwitcherSitelistImpl::BrowserSwitcherSitelistImpl(PrefService* prefs)
diff --git a/chrome/browser/browser_switcher/browser_switcher_sitelist.h b/chrome/browser/browser_switcher/browser_switcher_sitelist.h
index 730232bc..97117d2 100644
--- a/chrome/browser/browser_switcher/browser_switcher_sitelist.h
+++ b/chrome/browser/browser_switcher/browser_switcher_sitelist.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_BROWSER_SWITCHER_BROWSER_SWITCHER_SITELIST_H_
 
 #include "base/macros.h"
+#include "chrome/browser/browser_switcher/browser_switcher_prefs.h"
 #include "components/prefs/pref_change_registrar.h"
 
 class PrefService;
@@ -57,14 +58,6 @@
 
   bool ShouldSwitchImpl(const GURL& url) const;
 
-  struct RuleSet {
-    RuleSet();
-    ~RuleSet();
-
-    std::vector<std::string> sitelist;
-    std::vector<std::string> greylist;
-  };
-
   RuleSet chrome_policies_;
   RuleSet ieem_sitelist_;
   RuleSet external_sitelist_;
diff --git a/chrome/browser/browser_switcher/mock_alternative_browser_driver.cc b/chrome/browser/browser_switcher/mock_alternative_browser_driver.cc
new file mode 100644
index 0000000..5b61a6f5
--- /dev/null
+++ b/chrome/browser/browser_switcher/mock_alternative_browser_driver.cc
@@ -0,0 +1,13 @@
+// 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/browser_switcher/mock_alternative_browser_driver.h"
+
+namespace browser_switcher {
+
+MockAlternativeBrowserDriver::MockAlternativeBrowserDriver() = default;
+
+MockAlternativeBrowserDriver::~MockAlternativeBrowserDriver() = default;
+
+}  // namespace browser_switcher
diff --git a/chrome/browser/browser_switcher/mock_alternative_browser_driver.h b/chrome/browser/browser_switcher/mock_alternative_browser_driver.h
new file mode 100644
index 0000000..dc98191
--- /dev/null
+++ b/chrome/browser/browser_switcher/mock_alternative_browser_driver.h
@@ -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.
+
+#ifndef CHROME_BROWSER_BROWSER_SWITCHER_MOCK_ALTERNATIVE_BROWSER_DRIVER_H_
+#define CHROME_BROWSER_BROWSER_SWITCHER_MOCK_ALTERNATIVE_BROWSER_DRIVER_H_
+
+#include "chrome/browser/browser_switcher/alternative_browser_driver.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "url/gurl.h"
+
+namespace browser_switcher {
+
+class MockAlternativeBrowserDriver : public AlternativeBrowserDriver {
+ public:
+  MockAlternativeBrowserDriver();
+  ~MockAlternativeBrowserDriver() override;
+
+  MOCK_METHOD1(SetBrowserPath, void(base::StringPiece));
+  MOCK_METHOD1(SetBrowserParameters, void(const base::ListValue*));
+  MOCK_CONST_METHOD1(ExpandEnvVars, void(std::string*));
+  MOCK_CONST_METHOD1(ExpandPresetBrowsers, void(std::string*));
+  MOCK_METHOD1(TryLaunch, bool(const GURL&));
+};
+
+}  // namespace browser_switcher
+
+#endif  // CHROME_BROWSER_BROWSER_SWITCHER_MOCK_ALTERNATIVE_BROWSER_DRIVER_H_
diff --git a/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc b/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc
index 9872276..f5d08d2 100644
--- a/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc
+++ b/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc
@@ -1233,6 +1233,53 @@
   EXPECT_EQ(0, GetMediaLicenseCount());
   ExpectCookieTreeModelCount(0);
 }
+
+IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest,
+                       MediaLicenseDeletionWithFilter) {
+  const std::string kMediaLicenseType = "MediaLicense";
+
+  GURL url =
+      embedded_test_server()->GetURL("/browsing_data/media_license.html");
+  ui_test_utils::NavigateToURL(browser(), url);
+
+  EXPECT_EQ(0, GetMediaLicenseCount());
+  EXPECT_FALSE(HasDataForType(kMediaLicenseType));
+
+  SetDataForType(kMediaLicenseType);
+  EXPECT_EQ(1, GetMediaLicenseCount());
+  EXPECT_TRUE(HasDataForType(kMediaLicenseType));
+
+  // Try to remove the Media Licenses using a whitelist that doesn't include
+  // the current URL. Media License should not be deleted.
+  std::unique_ptr<BrowsingDataFilterBuilder> filter_builder =
+      BrowsingDataFilterBuilder::Create(BrowsingDataFilterBuilder::WHITELIST);
+  filter_builder->AddOrigin(
+      url::Origin::CreateFromNormalizedTuple("https", "test-origin", 443));
+  RemoveWithFilterAndWait(
+      content::BrowsingDataRemover::DATA_TYPE_MEDIA_LICENSES,
+      std::move(filter_builder));
+  EXPECT_EQ(1, GetMediaLicenseCount());
+
+  // Now try with a blacklist that includes the current URL. Media License
+  // should not be deleted.
+  filter_builder =
+      BrowsingDataFilterBuilder::Create(BrowsingDataFilterBuilder::BLACKLIST);
+  filter_builder->AddOrigin(url::Origin::Create(url));
+  RemoveWithFilterAndWait(
+      content::BrowsingDataRemover::DATA_TYPE_MEDIA_LICENSES,
+      std::move(filter_builder));
+  EXPECT_EQ(1, GetMediaLicenseCount());
+
+  // Now try with a whitelist that includes the current URL. Media License
+  // should be deleted this time.
+  filter_builder =
+      BrowsingDataFilterBuilder::Create(BrowsingDataFilterBuilder::WHITELIST);
+  filter_builder->AddOrigin(url::Origin::Create(url));
+  RemoveWithFilterAndWait(
+      content::BrowsingDataRemover::DATA_TYPE_MEDIA_LICENSES,
+      std::move(filter_builder));
+  EXPECT_EQ(0, GetMediaLicenseCount());
+}
 #endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)
 
 const std::vector<std::string> kStorageTypes{
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
index 30ef0405..f47d2c37 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
@@ -977,34 +977,42 @@
     base::RecordAction(UserMetricsAction("ClearBrowsingData_ContentLicenses"));
 
 #if BUILDFLAG(ENABLE_PLUGINS)
-    // Will be completed in OnDeauthorizeFlashContentLicensesCompleted()
-    num_pending_tasks_ += 1;
-    if (!pepper_flash_settings_manager_.get()) {
-      pepper_flash_settings_manager_.reset(
-          new PepperFlashSettingsManager(this, profile_));
+    // Flash does not support filtering by domain, so skip this if clearing only
+    // a specified set of sites.
+    if (filter_builder.GetMode() != BrowsingDataFilterBuilder::WHITELIST) {
+      // Will be completed in OnDeauthorizeFlashContentLicensesCompleted()
+      num_pending_tasks_ += 1;
+      if (!pepper_flash_settings_manager_.get()) {
+        pepper_flash_settings_manager_.reset(
+            new PepperFlashSettingsManager(this, profile_));
+      }
+      deauthorize_flash_content_licenses_request_id_ =
+          pepper_flash_settings_manager_->DeauthorizeContentLicenses(prefs);
     }
-    deauthorize_flash_content_licenses_request_id_ =
-        pepper_flash_settings_manager_->DeauthorizeContentLicenses(prefs);
 #endif  // BUILDFLAG(ENABLE_PLUGINS)
 
 #if defined(OS_CHROMEOS)
     // On Chrome OS, delete any content protection platform keys.
-    const user_manager::User* user =
-        chromeos::ProfileHelper::Get()->GetUserByProfile(profile_);
-    if (!user) {
-      LOG(WARNING) << "Failed to find user for current profile.";
-    } else {
-      chromeos::DBusThreadManager::Get()
-          ->GetCryptohomeClient()
-          ->TpmAttestationDeleteKeys(
-              chromeos::attestation::KEY_USER,
-              cryptohome::CreateAccountIdentifierFromAccountId(
-                  user->GetAccountId()),
-              chromeos::attestation::kContentProtectionKeyPrefix,
-              base::BindOnce(
-                  &ChromeBrowsingDataRemoverDelegate::OnClearPlatformKeys,
-                  weak_ptr_factory_.GetWeakPtr(),
-                  CreatePendingTaskCompletionClosure()));
+    // Platform keys do not support filtering by domain, so skip this if
+    // clearing only a specified set of sites.
+    if (filter_builder.GetMode() != BrowsingDataFilterBuilder::WHITELIST) {
+      const user_manager::User* user =
+          chromeos::ProfileHelper::Get()->GetUserByProfile(profile_);
+      if (!user) {
+        LOG(WARNING) << "Failed to find user for current profile.";
+      } else {
+        chromeos::DBusThreadManager::Get()
+            ->GetCryptohomeClient()
+            ->TpmAttestationDeleteKeys(
+                chromeos::attestation::KEY_USER,
+                cryptohome::CreateAccountIdentifierFromAccountId(
+                    user->GetAccountId()),
+                chromeos::attestation::kContentProtectionKeyPrefix,
+                base::BindOnce(
+                    &ChromeBrowsingDataRemoverDelegate::OnClearPlatformKeys,
+                    weak_ptr_factory_.GetWeakPtr(),
+                    CreatePendingTaskCompletionClosure()));
+      }
     }
 #endif  // defined(OS_CHROMEOS)
 
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.h b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.h
index cd7415f..54b86ed 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.h
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.h
@@ -99,9 +99,10 @@
 
     // Datatypes that can be deleted partially per URL / origin / domain,
     // whichever makes sense.
-    FILTERABLE_DATA_TYPES = DATA_TYPE_SITE_DATA |
-                            content::BrowsingDataRemover::DATA_TYPE_CACHE |
-                            content::BrowsingDataRemover::DATA_TYPE_DOWNLOADS,
+    FILTERABLE_DATA_TYPES =
+        DATA_TYPE_SITE_DATA | content::BrowsingDataRemover::DATA_TYPE_CACHE |
+        content::BrowsingDataRemover::DATA_TYPE_DOWNLOADS |
+        content::BrowsingDataRemover::DATA_TYPE_MEDIA_LICENSES,
 
     // Includes all the available remove options. Meant to be used by clients
     // that wish to wipe as much data as possible from a Profile, to make it
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index ee3d340..1fe7877 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -2301,7 +2301,7 @@
 void ChromeContentBrowserClient::NavigationRequestRedirected(
     int frame_tree_node_id,
     const GURL& url,
-    base::Optional<net::HttpRequestHeaders>* modified_request_headers) {
+    base::Optional<net::HttpRequestHeaders>* modified_headers) {
   WebContents* web_contents =
       WebContents::FromFrameTreeNodeId(frame_tree_node_id);
   content::BrowserContext* browser_context = web_contents->GetBrowserContext();
@@ -2315,7 +2315,7 @@
       std::unique_ptr<net::HttpRequestHeaders> extra_headers;
       policy_header_service->AddPolicyHeaders(url, &extra_headers);
       if (extra_headers)
-        *modified_request_headers = std::move(*extra_headers);
+        *modified_headers = std::move(*extra_headers);
     }
   }
 
@@ -2323,9 +2323,9 @@
       ClientHintsFactory::GetForBrowserContext(browser_context)
           ->GetAdditionalNavigationRequestClientHintsHeaders(url);
   if (client_hints_extra_headers) {
-    if (!modified_request_headers->has_value())
-      *modified_request_headers = net::HttpRequestHeaders();
-    modified_request_headers->value().MergeFrom(*client_hints_extra_headers);
+    if (!modified_headers->has_value())
+      *modified_headers = net::HttpRequestHeaders();
+    modified_headers->value().MergeFrom(*client_hints_extra_headers);
   }
 }
 
@@ -2906,7 +2906,7 @@
     RenderFrameHost* opener,
     const GURL& opener_url,
     const GURL& opener_top_level_frame_url,
-    const GURL& source_origin,
+    const url::Origin& source_origin,
     content::mojom::WindowContainerType container_type,
     const GURL& target_url,
     const content::Referrer& referrer,
@@ -2995,9 +2995,9 @@
     return false;
   }
 
-  BlockedWindowParams blocked_params(target_url, referrer, frame_name,
-                                     disposition, features, user_gesture,
-                                     opener_suppressed);
+  BlockedWindowParams blocked_params(target_url, source_origin, referrer,
+                                     frame_name, disposition, features,
+                                     user_gesture, opener_suppressed);
   NavigateParams nav_params = blocked_params.CreateNavigateParams(web_contents);
   if (MaybeBlockPopup(web_contents, opener_top_level_frame_url, &nav_params,
                       nullptr /*=open_url_params*/,
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index d8aaa26..9e62e44 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -205,10 +205,10 @@
       const GURL& url,
       std::unique_ptr<net::HttpRequestHeaders>* extra_headers,
       int* extra_load_flags) override;
-  void NavigationRequestRedirected(int frame_tree_node_id,
-                                   const GURL& url,
-                                   base::Optional<net::HttpRequestHeaders>*
-                                       modified_request_headers) override;
+  void NavigationRequestRedirected(
+      int frame_tree_node_id,
+      const GURL& url,
+      base::Optional<net::HttpRequestHeaders>* modified_headers) override;
   bool AllowAppCache(const GURL& manifest_url,
                      const GURL& first_party,
                      content::ResourceContext* context) override;
@@ -305,7 +305,7 @@
   bool CanCreateWindow(content::RenderFrameHost* opener,
                        const GURL& opener_url,
                        const GURL& opener_top_level_frame_url,
-                       const GURL& source_origin,
+                       const url::Origin& source_origin,
                        content::mojom::WindowContainerType container_type,
                        const GURL& target_url,
                        const content::Referrer& referrer,
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 52317c4d..bf38efd 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -95,6 +95,9 @@
     "//chromeos/components/multidevice/logging",
     "//chromeos/components/proximity_auth",
     "//chromeos/components/tether",
+    "//chromeos/cryptohome",
+    "//chromeos/login/auth",
+    "//chromeos/login/login_state",
     "//chromeos/network",
     "//chromeos/services/device_sync/public/cpp",
     "//chromeos/services/machine_learning/public/cpp",
diff --git a/chrome/browser/chromeos/arc/screen_capture/arc_screen_capture_session.cc b/chrome/browser/chromeos/arc/screen_capture/arc_screen_capture_session.cc
index 3ca19804..96c4d3c 100644
--- a/chrome/browser/chromeos/arc/screen_capture/arc_screen_capture_session.cc
+++ b/chrome/browser/chromeos/arc/screen_capture/arc_screen_capture_session.cc
@@ -138,8 +138,9 @@
                                    base::UTF8ToUTF16(display_name));
     notification_ui_ = ScreenCaptureNotificationUI::Create(notification_text);
     notification_ui_->OnStarted(
-        base::BindRepeating(&ArcScreenCaptureSession::NotificationStop,
-                            weak_ptr_factory_.GetWeakPtr()));
+        base::BindOnce(&ArcScreenCaptureSession::NotificationStop,
+                       weak_ptr_factory_.GetWeakPtr()),
+        base::RepeatingClosure());
   }
 
   ash::Shell::Get()->display_manager()->inc_screen_capture_active_counter();
diff --git a/chrome/browser/chromeos/extensions/echo_private_apitest.cc b/chrome/browser/chromeos/extensions/echo_private_apitest.cc
index 1d3c897..e693bc9 100644
--- a/chrome/browser/chromeos/extensions/echo_private_apitest.cc
+++ b/chrome/browser/chromeos/extensions/echo_private_apitest.cc
@@ -9,7 +9,8 @@
 #include "base/strings/stringprintf.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
-#include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h"
+#include "chrome/browser/chromeos/settings/scoped_testing_cros_settings.h"
+#include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h"
 #include "chrome/browser/chromeos/ui/echo_dialog_view.h"
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/extensions/extension_function_test_utils.h"
@@ -39,14 +40,6 @@
 
   ~ExtensionEchoPrivateApiTest() override {}
 
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    extensions::ExtensionApiTest::SetUpCommandLine(command_line);
-
-    // Force usage of stub cros settings provider instead of device settings
-    // provider.
-    command_line->AppendSwitch(switches::kStubCrosSettings);
-  }
-
   void RunDefaultGetUserFunctionAndExpectResultEquals(int tab_id,
                                                       bool expected_result) {
     scoped_refptr<EchoPrivateGetUserConsentFunction> function(
@@ -130,8 +123,7 @@
  protected:
   int expected_dialog_buttons_;
   DialogTestAction dialog_action_;
-  chromeos::ScopedCrosSettingsTestHelper settings_helper_{
-      /* create_settings_service= */ false};
+  ScopedTestingCrosSettings scoped_testing_cros_settings_;
 
  private:
   int dialog_invocation_count_;
@@ -242,8 +234,8 @@
 IN_PROC_BROWSER_TEST_F(ExtensionEchoPrivateApiTest,
                        GetUserConsent_AllowRedeemPrefTrue) {
   const int tab_id = OpenAndActivateTab();
-  settings_helper_.SetBoolean(chromeos::kAllowRedeemChromeOsRegistrationOffers,
-                              true);
+  scoped_testing_cros_settings_.device_settings()->Set(
+      kAllowRedeemChromeOsRegistrationOffers, base::Value(true));
 
   expected_dialog_buttons_ = ui::DIALOG_BUTTON_CANCEL | ui::DIALOG_BUTTON_OK;
   dialog_action_ = DIALOG_TEST_ACTION_ACCEPT;
@@ -256,8 +248,8 @@
 IN_PROC_BROWSER_TEST_F(ExtensionEchoPrivateApiTest,
                        GetUserConsent_ConsentDenied) {
   const int tab_id = OpenAndActivateTab();
-  settings_helper_.SetBoolean(chromeos::kAllowRedeemChromeOsRegistrationOffers,
-                              true);
+  scoped_testing_cros_settings_.device_settings()->Set(
+      kAllowRedeemChromeOsRegistrationOffers, base::Value(true));
 
   expected_dialog_buttons_ = ui::DIALOG_BUTTON_CANCEL | ui::DIALOG_BUTTON_OK;
   dialog_action_ = DIALOG_TEST_ACTION_CANCEL;
@@ -270,8 +262,8 @@
 IN_PROC_BROWSER_TEST_F(ExtensionEchoPrivateApiTest,
                        GetUserConsent_AllowRedeemPrefFalse) {
   const int tab_id = OpenAndActivateTab();
-  settings_helper_.SetBoolean(chromeos::kAllowRedeemChromeOsRegistrationOffers,
-                              false);
+  scoped_testing_cros_settings_.device_settings()->Set(
+      kAllowRedeemChromeOsRegistrationOffers, base::Value(false));
 
   expected_dialog_buttons_ = ui::DIALOG_BUTTON_CANCEL;
   dialog_action_ = DIALOG_TEST_ACTION_CANCEL;
diff --git a/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api_unittest.cc b/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api_unittest.cc
index 28e0015..b1b8028 100644
--- a/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api_unittest.cc
+++ b/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api_unittest.cc
@@ -149,6 +149,7 @@
     }
     DBusThreadManager::GetSetterForTesting()->SetCryptohomeClient(
         std::move(cryptohome_client));
+    SystemSaltGetter::Initialize();
 
     ExtensionApiUnittest::SetUp();
 
@@ -191,6 +192,7 @@
     fake_user_manager_ = nullptr;
 
     ExtensionApiUnittest::TearDown();
+    SystemSaltGetter::Shutdown();
     cryptohome::HomedirMethods::Shutdown();
   }
 
diff --git a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service.cc b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service.cc
index e96e5a12..8462d3d 100644
--- a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service.cc
+++ b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service.cc
@@ -645,6 +645,9 @@
       case ScreenlockState::PASSWORD_REAUTH:
         return SmartLockMetricsRecorder::SmartLockAuthEventPasswordState::
             kForcedReauth;
+      case ScreenlockState::PHONE_NOT_LOCKABLE:
+        return SmartLockMetricsRecorder::SmartLockAuthEventPasswordState::
+            kPhoneNotLockable;
       default:
         NOTREACHED();
         return SmartLockMetricsRecorder::SmartLockAuthEventPasswordState::
diff --git a/chrome/browser/chromeos/login/login_ui_keyboard_browsertest.cc b/chrome/browser/chromeos/login/login_ui_keyboard_browsertest.cc
index 177a5a6..eb79ad1 100644
--- a/chrome/browser/chromeos/login/login_ui_keyboard_browsertest.cc
+++ b/chrome/browser/chromeos/login/login_ui_keyboard_browsertest.cc
@@ -14,7 +14,8 @@
 #include "chrome/browser/chromeos/login/startup_utils.h"
 #include "chrome/browser/chromeos/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/chromeos/login/ui/login_display_host.h"
-#include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h"
+#include "chrome/browser/chromeos/settings/scoped_testing_cros_settings.h"
+#include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h"
 #include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/constants/chromeos_switches.h"
@@ -184,13 +185,6 @@
       : LoginManagerTest(false, true /* should_initialize_webui */) {}
   ~LoginUIKeyboardTestWithUsersAndOwner() override {}
 
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    LoginManagerTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitch(switches::kStubCrosSettings);
-
-    LoginManagerTest::SetUpCommandLine(command_line);
-  }
-
   void SetUpOnMainThread() override {
     user_input_methods.push_back("xkb:fr::fra");
     user_input_methods.push_back("xkb:de::ger");
@@ -199,7 +193,8 @@
     chromeos::input_method::InputMethodManager::Get()->MigrateInputMethods(
         &user_input_methods);
 
-    settings_helper_.SetString(kDeviceOwner, kTestUser3);
+    scoped_testing_cros_settings_.device_settings()->Set(
+        kDeviceOwner, base::Value(kTestUser3));
 
     LoginManagerTest::SetUpOnMainThread();
   }
@@ -224,8 +219,7 @@
 
  protected:
   std::vector<std::string> user_input_methods;
-  ScopedCrosSettingsTestHelper settings_helper_{
-      /* create_settings_service= */ false};
+  ScopedTestingCrosSettings scoped_testing_cros_settings_;
 };
 
 void LoginUIKeyboardTestWithUsersAndOwner::CheckGaiaKeyboard() {
diff --git a/chrome/browser/chromeos/login/users/user_manager_hide_supervised_users_browsertest.cc b/chrome/browser/chromeos/login/users/user_manager_hide_supervised_users_browsertest.cc
index 0fb1ec82..7a4b588 100644
--- a/chrome/browser/chromeos/login/users/user_manager_hide_supervised_users_browsertest.cc
+++ b/chrome/browser/chromeos/login/users/user_manager_hide_supervised_users_browsertest.cc
@@ -11,7 +11,7 @@
 #include "chrome/browser/chromeos/login/users/chrome_user_manager.h"
 #include "chrome/browser/chromeos/login/users/chrome_user_manager_impl.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
-#include "chromeos/constants/chromeos_switches.h"
+#include "chrome/browser/chromeos/settings/scoped_testing_cros_settings.h"
 #include "components/account_id/account_id.h"
 #include "components/user_manager/scoped_user_manager.h"
 #include "components/user_manager/user_manager.h"
@@ -53,13 +53,9 @@
     scoped_user_manager_.reset();
   }
 
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    LoginManagerTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitch(chromeos::switches::kStubCrosSettings);
-  }
-
  protected:
   std::vector<AccountId> test_users_;
+  ScopedTestingCrosSettings scoped_testing_cros_settings_;
 
  private:
   void InitializeTestUsers() {
diff --git a/chrome/browser/chromeos/login/webview_login_browsertest.cc b/chrome/browser/chromeos/login/webview_login_browsertest.cc
index e944d84..dc12814 100644
--- a/chrome/browser/chromeos/login/webview_login_browsertest.cc
+++ b/chrome/browser/chromeos/login/webview_login_browsertest.cc
@@ -23,7 +23,8 @@
 #include "chrome/browser/chromeos/policy/device_policy_builder.h"
 #include "chrome/browser/chromeos/policy/device_policy_cros_browser_test.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
-#include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h"
+#include "chrome/browser/chromeos/settings/scoped_testing_cros_settings.h"
+#include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h"
 #include "chrome/browser/policy/test/local_policy_test_server.h"
 #include "chrome/browser/ui/login/login_handler.h"
 #include "chrome/browser/ui/webui/signin/signin_utils.h"
@@ -166,7 +167,6 @@
   void SetUpCommandLine(base::CommandLine* command_line) override {
     command_line->AppendSwitch(switches::kOobeSkipPostLogin);
     command_line->AppendSwitch(::switches::kUseFakeDeviceForMediaStream);
-    command_line->AppendSwitch(switches::kStubCrosSettings);
     OobeBaseTest::SetUpCommandLine(command_line);
   }
 
@@ -230,8 +230,7 @@
   }
 
  protected:
-  ScopedCrosSettingsTestHelper settings_helper_{
-      /* create_settings_service= */ false};
+  chromeos::ScopedTestingCrosSettings scoped_testing_cros_settings_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(WebviewLoginTest);
@@ -303,8 +302,10 @@
   test::OobeJS().ExpectTrue(frame_url + ".search('flow=nosignup') == -1");
 
   // Disallow new users - we also need to set a whitelist due to weird logic.
-  settings_helper_.Set(kAccountsPrefUsers, base::ListValue());
-  settings_helper_.SetBoolean(kAccountsPrefAllowNewUser, false);
+  scoped_testing_cros_settings_.device_settings()->Set(kAccountsPrefUsers,
+                                                       base::ListValue());
+  scoped_testing_cros_settings_.device_settings()->Set(
+      kAccountsPrefAllowNewUser, base::Value(false));
   WaitForGaiaPageReload();
 
   // flow=nosignup indicates that user creation is not allowed.
diff --git a/chrome/browser/chromeos/policy/force_maximize_on_first_run_chromeos_browsertest.cc b/chrome/browser/chromeos/policy/force_maximize_on_first_run_chromeos_browsertest.cc
index 63f4005..5fd4b0a9 100644
--- a/chrome/browser/chromeos/policy/force_maximize_on_first_run_chromeos_browsertest.cc
+++ b/chrome/browser/chromeos/policy/force_maximize_on_first_run_chromeos_browsertest.cc
@@ -53,7 +53,8 @@
   DISALLOW_COPY_AND_ASSIGN(ForceMaximizeOnFirstRunTest);
 };
 
-IN_PROC_BROWSER_TEST_F(ForceMaximizeOnFirstRunTest, PRE_TwoRuns) {
+// crbug.com/919100: disabled because of flakiness.
+IN_PROC_BROWSER_TEST_F(ForceMaximizeOnFirstRunTest, DISABLED_PRE_TwoRuns) {
   SetUpResolution();
   SkipToLoginScreen();
   LogIn(kAccountId, kAccountPassword, kEmptyServices);
@@ -76,7 +77,8 @@
   EXPECT_FALSE(browser1->window()->IsMaximized());
 }
 
-IN_PROC_BROWSER_TEST_F(ForceMaximizeOnFirstRunTest, TwoRuns) {
+// crbug.com/919100: disabled because of flakiness.
+IN_PROC_BROWSER_TEST_F(ForceMaximizeOnFirstRunTest, DISABLED_TwoRuns) {
   SetUpResolution();
   content::WindowedNotificationObserver(
       chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
diff --git a/chrome/browser/chromeos/preferences_chromeos_browsertest.cc b/chrome/browser/chromeos/preferences_chromeos_browsertest.cc
index c470f29..e8da9f8 100644
--- a/chrome/browser/chromeos/preferences_chromeos_browsertest.cc
+++ b/chrome/browser/chromeos/preferences_chromeos_browsertest.cc
@@ -15,11 +15,10 @@
 #include "chrome/browser/chromeos/login/ui/user_adding_screen.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
-#include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h"
+#include "chrome/browser/chromeos/settings/scoped_testing_cros_settings.h"
 #include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h"
 #include "chrome/browser/chromeos/system/fake_input_device_settings.h"
 #include "chrome/common/pref_names.h"
-#include "chromeos/constants/chromeos_switches.h"
 #include "components/feedback/tracing_manager.h"
 #include "components/prefs/pref_service.h"
 #include "components/user_manager/user_manager.h"
@@ -45,11 +44,9 @@
       test_users_.push_back(AccountId::FromUserEmailGaiaId(
           kTestUsers[i].email, kTestUsers[i].gaia_id));
     }
-  }
 
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    LoginManagerTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitch(switches::kStubCrosSettings);
+    scoped_testing_cros_settings_.device_settings()->Set(
+        kDeviceOwner, base::Value(test_users_[0].GetUserEmail()));
   }
 
   void SetUpOnMainThread() override {
@@ -60,7 +57,6 @@
     static_cast<input_method::InputMethodManagerImpl*>(
         input_method::InputMethodManager::Get())
         ->SetImeKeyboardForTesting(keyboard_);
-    settings_helper_.SetString(kDeviceOwner, test_users_[0].GetUserEmail());
   }
 
   // Sets set of preferences in given |prefs|. Value of prefernece depends of
@@ -120,8 +116,7 @@
   }
 
   std::vector<AccountId> test_users_;
-  ScopedCrosSettingsTestHelper settings_helper_{
-      /* create_settings_service= */ false};
+  ScopedTestingCrosSettings scoped_testing_cros_settings_;
 
  private:
   system::InputDeviceSettings::FakeInterface* input_settings_;
@@ -148,7 +143,7 @@
 IN_PROC_BROWSER_TEST_F(PreferencesTestForceWebUiLogin, PRE_MultiProfiles) {
   RegisterUser(test_users_[0]);
   RegisterUser(test_users_[1]);
-  chromeos::StartupUtils::MarkOobeCompleted();
+  StartupUtils::MarkOobeCompleted();
 }
 
 IN_PROC_BROWSER_TEST_F(PreferencesTestForceWebUiLogin, MultiProfiles) {
diff --git a/chrome/browser/chromeos/ui/screen_capture_notification_ui_chromeos.cc b/chrome/browser/chromeos/ui/screen_capture_notification_ui_chromeos.cc
index 4675325..3ffd839f 100644
--- a/chrome/browser/chromeos/ui/screen_capture_notification_ui_chromeos.cc
+++ b/chrome/browser/chromeos/ui/screen_capture_notification_ui_chromeos.cc
@@ -21,12 +21,23 @@
 }
 
 gfx::NativeViewId ScreenCaptureNotificationUIChromeOS::OnStarted(
-    const base::Closure& stop_callback) {
+    base::OnceClosure stop_callback,
+    base::RepeatingClosure source_callback) {
+  stop_callback_ = std::move(stop_callback);
   ash::Shell::Get()->system_tray_notifier()->NotifyScreenCaptureStart(
-      stop_callback, text_);
+      base::BindRepeating(
+          &ScreenCaptureNotificationUIChromeOS::ProcessStopRequestFromUI,
+          base::Unretained(this)),
+      text_);
   return 0;
 }
 
+void ScreenCaptureNotificationUIChromeOS::ProcessStopRequestFromUI() {
+  if (!stop_callback_.is_null()) {
+    std::move(stop_callback_).Run();
+  }
+}
+
 }  // namespace chromeos
 
 // static
diff --git a/chrome/browser/chromeos/ui/screen_capture_notification_ui_chromeos.h b/chrome/browser/chromeos/ui/screen_capture_notification_ui_chromeos.h
index fb3632a..f56afac 100644
--- a/chrome/browser/chromeos/ui/screen_capture_notification_ui_chromeos.h
+++ b/chrome/browser/chromeos/ui/screen_capture_notification_ui_chromeos.h
@@ -19,10 +19,14 @@
   ~ScreenCaptureNotificationUIChromeOS() override;
 
   // ScreenCaptureNotificationUI overrides.
-  gfx::NativeViewId OnStarted(const base::Closure& stop_callback) override;
+  gfx::NativeViewId OnStarted(base::OnceClosure stop_callback,
+                              base::RepeatingClosure source_callback) override;
 
  private:
+  void ProcessStopRequestFromUI();
+
   const base::string16 text_;
+  base::OnceClosure stop_callback_;
 
   DISALLOW_COPY_AND_ASSIGN(ScreenCaptureNotificationUIChromeOS);
 };
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index c16f074..3563234b 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -956,6 +956,8 @@
       "//ash",
       "//ash/public/cpp",
       "//chromeos/components/proximity_auth",
+      "//chromeos/cryptohome",
+      "//chromeos/login/login_state",
       "//chromeos/services/ime/public/mojom",
       "//chromeos/services/machine_learning/public/cpp",
       "//chromeos/services/machine_learning/public/mojom",
diff --git a/chrome/browser/extensions/activity_log/activity_log.cc b/chrome/browser/extensions/activity_log/activity_log.cc
index 691a907..d0ca2748 100644
--- a/chrome/browser/extensions/activity_log/activity_log.cc
+++ b/chrome/browser/extensions/activity_log/activity_log.cc
@@ -812,6 +812,12 @@
   database_policy_->RemoveActions(action_ids);
 }
 
+void ActivityLog::RemoveExtensionData(const std::string& extension_id) {
+  if (!database_policy_)
+    return;
+  database_policy_->RemoveExtensionData(extension_id);
+}
+
 void ActivityLog::RemoveURLs(const std::vector<GURL>& restrict_urls) {
   if (!database_policy_)
     return;
diff --git a/chrome/browser/extensions/activity_log/activity_log.h b/chrome/browser/extensions/activity_log/activity_log.h
index 1012ed7..3115923a 100644
--- a/chrome/browser/extensions/activity_log/activity_log.h
+++ b/chrome/browser/extensions/activity_log/activity_log.h
@@ -116,6 +116,10 @@
   // action_ids array.
   void RemoveActions(const std::vector<int64_t>& action_ids);
 
+  // Remove all actions from the activity log database with the specified
+  // extension_id.
+  void RemoveExtensionData(const std::string& extension_id);
+
   // Clean up URLs from the activity log database.
   // If restrict_urls is empty then all URLs in the activity log database are
   // removed, otherwise only those in restrict_urls are removed.
diff --git a/chrome/browser/extensions/activity_log/activity_log_unittest.cc b/chrome/browser/extensions/activity_log/activity_log_unittest.cc
index cd27c75..3e350b9 100644
--- a/chrome/browser/extensions/activity_log/activity_log_unittest.cc
+++ b/chrome/browser/extensions/activity_log/activity_log_unittest.cc
@@ -35,7 +35,7 @@
 
 namespace {
 
-const char kExtensionId[] = "abc";
+const char kExtensionId[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
 
 const char* const kUrlApiCalls[] = {
     "HTMLButtonElement.formAction", "HTMLEmbedElement.src",
@@ -87,6 +87,11 @@
     ASSERT_EQ(0, static_cast<int>(i->size()));
   }
 
+  static void RetrieveActions_LogAndFetchActions1(
+      std::unique_ptr<std::vector<scoped_refptr<Action>>> i) {
+    ASSERT_EQ(1, static_cast<int>(i->size()));
+  }
+
   static void RetrieveActions_LogAndFetchActions2(
       std::unique_ptr<std::vector<scoped_refptr<Action>>> i) {
     ASSERT_EQ(2, static_cast<int>(i->size()));
@@ -200,7 +205,6 @@
 
 TEST_F(ActivityLogTest, LogAndFetchActions) {
   ActivityLog* activity_log = ActivityLog::GetInstance(profile());
-  std::unique_ptr<base::ListValue> args(new base::ListValue());
   ASSERT_TRUE(GetDatabaseEnabled());
 
   // Write some API calls
@@ -267,8 +271,6 @@
 
 TEST_F(ActivityLogTest, ArgUrlExtraction) {
   ActivityLog* activity_log = ActivityLog::GetInstance(profile());
-  std::unique_ptr<base::ListValue> args(new base::ListValue());
-
   base::Time now = base::Time::Now();
 
   // Submit a DOM API call which should have its URL extracted into the arg_url
@@ -338,7 +340,6 @@
           .Build();
 
   ActivityLog* activity_log = ActivityLog::GetInstance(profile());
-  std::unique_ptr<base::ListValue> args(new base::ListValue());
   ASSERT_TRUE(GetDatabaseEnabled());
 
   // Write some API calls
@@ -362,7 +363,6 @@
 
 TEST_F(ActivityLogTest, ArgUrlApiCalls) {
   ActivityLog* activity_log = ActivityLog::GetInstance(profile());
-  std::unique_ptr<base::ListValue> args(new base::ListValue());
   base::Time now = base::Time::Now();
   int api_calls_size = base::size(kUrlApiCalls);
   scoped_refptr<Action> action;
@@ -383,6 +383,32 @@
       base::BindOnce(ActivityLogTest::RetrieveActions_ArgUrlApiCalls));
 }
 
+TEST_F(ActivityLogTest, DeleteActivitiesByExtension) {
+  const std::string kOtherExtensionId = std::string(32, 'c');
+
+  ActivityLog* activity_log = ActivityLog::GetInstance(profile());
+  ASSERT_TRUE(GetDatabaseEnabled());
+
+  scoped_refptr<Action> action =
+      base::MakeRefCounted<Action>(kExtensionId, base::Time::Now(),
+                                   Action::ACTION_API_CALL, "tabs.testMethod");
+  activity_log->LogAction(action);
+
+  action =
+      base::MakeRefCounted<Action>(kOtherExtensionId, base::Time::Now(),
+                                   Action::ACTION_DOM_ACCESS, "document.write");
+  action->set_page_url(GURL("http://www.google.com"));
+  activity_log->LogAction(action);
+
+  activity_log->RemoveExtensionData(kExtensionId);
+  activity_log->GetFilteredActions(
+      kExtensionId, Action::ACTION_ANY, "", "", "", 0,
+      base::BindOnce(ActivityLogTest::RetrieveActions_LogAndFetchActions0));
+  activity_log->GetFilteredActions(
+      kOtherExtensionId, Action::ACTION_ANY, "", "", "", 0,
+      base::BindOnce(ActivityLogTest::RetrieveActions_LogAndFetchActions1));
+}
+
 class ActivityLogTestWithoutSwitch : public ActivityLogTest {
  public:
   ActivityLogTestWithoutSwitch() {}
diff --git a/chrome/browser/extensions/api/activity_log_private/activity_log_private_api.cc b/chrome/browser/extensions/api/activity_log_private/activity_log_private_api.cc
index a2f3244..fc01719 100644
--- a/chrome/browser/extensions/api/activity_log_private/activity_log_private_api.cc
+++ b/chrome/browser/extensions/api/activity_log_private/activity_log_private_api.cc
@@ -106,7 +106,7 @@
 bool ActivityLogPrivateGetExtensionActivitiesFunction::RunAsync() {
   std::unique_ptr<activity_log_private::GetExtensionActivities::Params> params(
       activity_log_private::GetExtensionActivities::Params::Create(*args_));
-  EXTENSION_FUNCTION_VALIDATE(params.get());
+  EXTENSION_FUNCTION_VALIDATE(params);
 
   // Get the arguments in the right format.
   Filter filter = std::move(params->filter);
@@ -180,7 +180,7 @@
 ActivityLogPrivateDeleteActivitiesFunction::Run() {
   std::unique_ptr<activity_log_private::DeleteActivities::Params> params(
       activity_log_private::DeleteActivities::Params::Create(*args_));
-  EXTENSION_FUNCTION_VALIDATE(params.get());
+  EXTENSION_FUNCTION_VALIDATE(params);
 
   // Put the arguments in the right format.
   std::vector<int64_t> action_ids;
@@ -197,6 +197,19 @@
 }
 
 ExtensionFunction::ResponseAction
+ActivityLogPrivateDeleteActivitiesByExtensionFunction::Run() {
+  std::unique_ptr<activity_log_private::DeleteActivitiesByExtension::Params>
+      params(activity_log_private::DeleteActivitiesByExtension::Params::Create(
+          *args_));
+  EXTENSION_FUNCTION_VALIDATE(params);
+
+  ActivityLog* activity_log = ActivityLog::GetInstance(browser_context());
+  DCHECK(activity_log);
+  activity_log->RemoveExtensionData(params->extension_id);
+  return RespondNow(NoArguments());
+}
+
+ExtensionFunction::ResponseAction
 ActivityLogPrivateDeleteDatabaseFunction::Run() {
   ActivityLog* activity_log = ActivityLog::GetInstance(browser_context());
   DCHECK(activity_log);
@@ -207,7 +220,7 @@
 ExtensionFunction::ResponseAction ActivityLogPrivateDeleteUrlsFunction::Run() {
   std::unique_ptr<activity_log_private::DeleteUrls::Params> params(
       activity_log_private::DeleteUrls::Params::Create(*args_));
-  EXTENSION_FUNCTION_VALIDATE(params.get());
+  EXTENSION_FUNCTION_VALIDATE(params);
 
   // Put the arguments in the right format.
   std::vector<GURL> gurls;
diff --git a/chrome/browser/extensions/api/activity_log_private/activity_log_private_api.h b/chrome/browser/extensions/api/activity_log_private/activity_log_private_api.h
index 25467ff..c162da48 100644
--- a/chrome/browser/extensions/api/activity_log_private/activity_log_private_api.h
+++ b/chrome/browser/extensions/api/activity_log_private/activity_log_private_api.h
@@ -93,6 +93,20 @@
   ResponseAction Run() override;
 };
 
+// The implementation of activityLogPrivate.deleteActivitiesByExtension
+class ActivityLogPrivateDeleteActivitiesByExtensionFunction
+    : public UIThreadExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("activityLogPrivate.deleteActivitiesByExtension",
+                             ACTIVITYLOGPRIVATE_DELETEACTIVITIESBYEXTENSION)
+
+ protected:
+  ~ActivityLogPrivateDeleteActivitiesByExtensionFunction() override {}
+
+  // ExtensionFunction:
+  ResponseAction Run() override;
+};
+
 // The implementation of activityLogPrivate.deleteDatabase
 class ActivityLogPrivateDeleteDatabaseFunction
     : public UIThreadExtensionFunction {
diff --git a/chrome/browser/extensions/api/settings_private/settings_private_apitest.cc b/chrome/browser/extensions/api/settings_private/settings_private_apitest.cc
index fb876e2..df85ee8 100644
--- a/chrome/browser/extensions/api/settings_private/settings_private_apitest.cc
+++ b/chrome/browser/extensions/api/settings_private/settings_private_apitest.cc
@@ -27,7 +27,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 #if defined(OS_CHROMEOS)
-#include "chromeos/constants/chromeos_switches.h"
+#include "chrome/browser/chromeos/settings/scoped_testing_cros_settings.h"
 #endif
 
 using testing::Mock;
@@ -43,13 +43,6 @@
   SettingsPrivateApiTest() {}
   ~SettingsPrivateApiTest() override {}
 
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    ExtensionApiTest::SetUpCommandLine(command_line);
-#if defined(OS_CHROMEOS)
-    command_line->AppendSwitch(chromeos::switches::kStubCrosSettings);
-#endif
-  }
-
   void SetUpInProcessBrowserTestFixture() override {
     EXPECT_CALL(provider_, IsInitializationComplete(_))
         .WillRepeatedly(Return(true));
@@ -77,6 +70,10 @@
  private:
   policy::MockConfigurationPolicyProvider provider_;
 
+#if defined(OS_CHROMEOS)
+  chromeos::ScopedTestingCrosSettings scoped_testing_cros_settings_;
+#endif
+
   DISALLOW_COPY_AND_ASSIGN(SettingsPrivateApiTest);
 };
 
diff --git a/chrome/browser/extensions/bookmark_app_navigation_throttle_utils.cc b/chrome/browser/extensions/bookmark_app_navigation_throttle_utils.cc
index d5f6e5e3..822bd74 100644
--- a/chrome/browser/extensions/bookmark_app_navigation_throttle_utils.cc
+++ b/chrome/browser/extensions/bookmark_app_navigation_throttle_utils.cc
@@ -106,6 +106,7 @@
     url_params.extra_headers = content_type_header.ToString();
   }
 
+  url_params.initiator_origin = navigation_handle->GetInitiatorOrigin();
   url_params.uses_post = navigation_handle->IsPost();
   url_params.post_data = navigation_handle->GetResourceRequestBody();
   url_params.redirect_chain = navigation_handle->GetRedirectChain();
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index c515ca8..e19c905 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -212,11 +212,6 @@
     "expiry_milestone": 76
   },
   {
-    "name": "ash-keyboard-shortcut-viewer-app",
-    "owners": [ "msw" ],
-    "expiry_milestone": 73
-  },
-  {
     "name": "ash-shelf-color",
     // "owners": [ "your-team" ],
     "expiry_milestone": 76
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index e108f37..7433b88 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -2399,12 +2399,6 @@
     "Enable OSK overscroll support. With this flag on, the OSK will only "
     "resize the visual viewport.";
 
-const char kEnableSpecialLocaleName[] =
-    "Enable custom logic for special locales.";
-const char kEnableSpecialLocaleDescription[] =
-    "Enable custom logic for special locales. In this mode, Chrome might "
-    "behave differently in some locales.";
-
 const char kEnableWebNfcName[] = "WebNFC";
 const char kEnableWebNfcDescription[] = "Enable WebNFC support.";
 
@@ -3084,11 +3078,6 @@
     "Enable unified desktop mode which allows a window to span multiple "
     "displays.";
 
-const char kAshKeyboardShortcutViewerAppName[] =
-    "Keyboard Shortcut Viewer mojo app";
-const char kAshKeyboardShortcutViewerAppDescription[] =
-    "Use an out-of-process mojo app to show the Keyboard Shortcut Viewer.";
-
 const char kAshShelfColorName[] = "Shelf color in Chrome OS system UI";
 const char kAshShelfColorDescription[] =
     "Enables/disables the shelf color to be a derived from the wallpaper. The "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index f6d436e5..3e6ac68 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1430,9 +1430,6 @@
 extern const char kEnableOskOverscrollName[];
 extern const char kEnableOskOverscrollDescription[];
 
-extern const char kEnableSpecialLocaleName[];
-extern const char kEnableSpecialLocaleDescription[];
-
 extern const char kEnableWebNfcName[];
 extern const char kEnableWebNfcDescription[];
 
@@ -1850,9 +1847,6 @@
 extern const char kAshEnableUnifiedDesktopName[];
 extern const char kAshEnableUnifiedDesktopDescription[];
 
-extern const char kAshKeyboardShortcutViewerAppName[];
-extern const char kAshKeyboardShortcutViewerAppDescription[];
-
 extern const char kAshShelfColorName[];
 extern const char kAshShelfColorDescription[];
 
diff --git a/chrome/browser/media/webrtc/desktop_capture_access_handler.cc b/chrome/browser/media/webrtc/desktop_capture_access_handler.cc
index 74f5eb5..4dd0a27 100644
--- a/chrome/browser/media/webrtc/desktop_capture_access_handler.cc
+++ b/chrome/browser/media/webrtc/desktop_capture_access_handler.cc
@@ -313,8 +313,6 @@
     return;
   }
 
-  // The extension name that the stream is registered with.
-  std::string original_extension_name;
   // Resolve DesktopMediaID for the specified device id.
   content::DesktopMediaID media_id;
   // TODO(miu): Replace "main RenderFrame" IDs with the request's actual
@@ -331,7 +329,7 @@
         content::DesktopStreamsRegistry::GetInstance()->RequestMediaForStreamId(
             request.requested_video_device_id,
             main_frame->GetProcess()->GetID(), main_frame->GetRoutingID(),
-            request.security_origin, &original_extension_name,
+            request.security_origin, nullptr,
             content::kRegistryStreamTypeDesktop);
   }
 
@@ -375,13 +373,13 @@
   const bool display_notification =
       display_notification_ && ShouldDisplayNotification(extension);
 
-  ui = GetDevicesForDesktopCapture(web_contents, &devices, media_id,
-                                   content::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE,
-                                   content::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE,
-                                   capture_audio, request.disable_local_echo,
-                                   display_notification,
-                                   GetApplicationTitle(web_contents, extension),
-                                   base::UTF8ToUTF16(original_extension_name));
+  ui = GetDevicesForDesktopCapture(
+      web_contents, &devices, media_id,
+      content::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE,
+      content::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE, capture_audio,
+      request.disable_local_echo, display_notification,
+      GetApplicationTitle(web_contents, extension),
+      GetApplicationTitle(web_contents, extension));
   UpdateExtensionTrusted(request, extension);
   std::move(callback).Run(devices, content::MEDIA_DEVICE_OK, std::move(ui));
 }
@@ -441,8 +439,6 @@
   const PendingAccessRequest& pending_request = *queue.front();
 
   std::vector<content::DesktopMediaID::Type> media_types = {
-      content::DesktopMediaID::TYPE_SCREEN,
-      content::DesktopMediaID::TYPE_WINDOW,
       content::DesktopMediaID::TYPE_WEB_CONTENTS};
   auto source_lists = picker_factory_->CreateMediaList(media_types);
 
diff --git a/chrome/browser/media/webrtc/desktop_capture_access_handler_unittest.cc b/chrome/browser/media/webrtc/desktop_capture_access_handler_unittest.cc
index e56810f..07f61010 100644
--- a/chrome/browser/media/webrtc/desktop_capture_access_handler_unittest.cc
+++ b/chrome/browser/media/webrtc/desktop_capture_access_handler_unittest.cc
@@ -40,7 +40,7 @@
       content::MediaStreamRequestType request_type,
       bool request_audio) {
     FakeDesktopMediaPickerFactory::TestFlags test_flags[] = {
-        {true /* expect_screens */, true /* expect_windows*/,
+        {false /* expect_screens */, false /* expect_windows*/,
          true /* expect_tabs */, request_audio /* expect_audio */,
          fake_desktop_media_id_response /* selected_source */}};
     picker_factory_->SetTestFlags(test_flags, base::size(test_flags));
@@ -135,7 +135,7 @@
   const content::MediaStreamType stream_type =
       content::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE;
   FakeDesktopMediaPickerFactory::TestFlags test_flags[] = {
-      {true /* expect_screens */, true /* expect_windows*/,
+      {false /* expect_screens */, false /* expect_windows*/,
        true /* expect_tabs */, false /* expect_audio */,
        content::DesktopMediaID(), true /* cancelled */}};
   picker_factory_->SetTestFlags(test_flags, base::size(test_flags));
@@ -166,7 +166,7 @@
 
 TEST_F(DesktopCaptureAccessHandlerTest, ChangeSourceWebContentsDestroyed) {
   FakeDesktopMediaPickerFactory::TestFlags test_flags[] = {
-      {true /* expect_screens */, true /* expect_windows*/,
+      {false /* expect_screens */, false /* expect_windows*/,
        true /* expect_tabs */, false /* expect_audio */,
        content::DesktopMediaID(), true /* cancelled */}};
   picker_factory_->SetTestFlags(test_flags, base::size(test_flags));
@@ -190,12 +190,12 @@
 
 TEST_F(DesktopCaptureAccessHandlerTest, ChangeSourceMultipleRequests) {
   FakeDesktopMediaPickerFactory::TestFlags test_flags[] = {
-      {true /* expect_screens */, true /* expect_windows*/,
+      {false /* expect_screens */, false /* expect_windows*/,
        true /* expect_tabs */, false /* expect_audio */,
        content::DesktopMediaID(
            content::DesktopMediaID::TYPE_SCREEN,
            content::DesktopMediaID::kFakeId) /* selected_source */},
-      {true /* expect_screens */, true /* expect_windows*/,
+      {false /* expect_screens */, false /* expect_windows*/,
        true /* expect_tabs */, false /* expect_audio */,
        content::DesktopMediaID(
            content::DesktopMediaID::TYPE_WINDOW,
diff --git a/chrome/browser/media/webrtc/media_stream_capture_indicator.cc b/chrome/browser/media/webrtc/media_stream_capture_indicator.cc
index 390527a..5e260d2 100644
--- a/chrome/browser/media/webrtc/media_stream_capture_indicator.cc
+++ b/chrome/browser/media/webrtc/media_stream_capture_indicator.cc
@@ -155,20 +155,22 @@
 
  private:
   // content::MediaStreamUI interface.
-  gfx::NativeViewId OnStarted(const base::Closure& stop_callback) override {
+  gfx::NativeViewId OnStarted(base::OnceClosure stop_callback,
+                              base::RepeatingClosure source_callback) override {
     DCHECK(!started_);
     started_ = true;
 
     if (device_usage_) {
       // |device_usage_| handles |stop_callback| when |ui_| is unspecified.
-      device_usage_->AddDevices(devices_,
-                                ui_ ? base::Closure() : stop_callback);
+      device_usage_->AddDevices(
+          devices_, ui_ ? base::OnceClosure() : std::move(stop_callback));
     }
 
     // If a custom |ui_| is specified, notify it that the stream started and let
-    // it handle the |stop_callback|.
+    // it handle the |stop_callback| and |source_callback|.
     if (ui_)
-      return ui_->OnStarted(stop_callback);
+      return ui_->OnStarted(std::move(stop_callback),
+                            std::move(source_callback));
 
     return 0;
   }
diff --git a/chrome/browser/media/webrtc/media_stream_capture_indicator.h b/chrome/browser/media/webrtc/media_stream_capture_indicator.h
index 510fc24..2f41fd50 100644
--- a/chrome/browser/media/webrtc/media_stream_capture_indicator.h
+++ b/chrome/browser/media/webrtc/media_stream_capture_indicator.h
@@ -31,10 +31,14 @@
   // Called when stream capture is stopped.
   virtual ~MediaStreamUI() = default;
 
-  // Called when stream capture starts. |stop_callback| is a callback to stop
-  // the stream. Returns the platform-dependent window ID for the UI, or 0 if
-  // not applicable.
-  virtual gfx::NativeViewId OnStarted(const base::Closure& stop) = 0;
+  // Called when stream capture starts.
+  // |stop_callback| is a callback to stop the stream.
+  // |source_callback| is a callback to change the desktop capture source.
+  // Returns the platform-dependent window ID for the UI, or 0 if not
+  // applicable.
+  virtual gfx::NativeViewId OnStarted(
+      base::OnceClosure stop_callback,
+      base::RepeatingClosure source_callback) = 0;
 };
 
 // Keeps track of which WebContents are capturing media streams. Used to display
diff --git a/chrome/browser/media/webrtc/media_stream_devices_controller_browsertest.cc b/chrome/browser/media/webrtc/media_stream_devices_controller_browsertest.cc
index cae8cb5..29de704 100644
--- a/chrome/browser/media/webrtc/media_stream_devices_controller_browsertest.cc
+++ b/chrome/browser/media/webrtc/media_stream_devices_controller_browsertest.cc
@@ -563,7 +563,7 @@
   std::unique_ptr<content::MediaStreamUI> video_stream_ui =
       dispatcher->GetMediaStreamCaptureIndicator()->RegisterMediaStream(
           GetWebContents(), video_devices);
-  video_stream_ui->OnStarted(base::Closure());
+  video_stream_ui->OnStarted(base::OnceClosure(), base::RepeatingClosure());
 
   // Request mic and deny.
   SetDevicePolicy(DEVICE_TYPE_AUDIO, ACCESS_DENIED);
diff --git a/chrome/browser/media/webrtc/webrtc_browsertest_base.cc b/chrome/browser/media/webrtc/webrtc_browsertest_base.cc
index b1b4490..3b1db29 100644
--- a/chrome/browser/media/webrtc/webrtc_browsertest_base.cc
+++ b/chrome/browser/media/webrtc/webrtc_browsertest_base.cc
@@ -89,6 +89,10 @@
   if (file == NULL || std::string("CONSOLE") != file)
     return false;
 
+  // TODO(crbug.com/918871): Fix AppRTC and stop ignoring this error.
+  if (str.find("Synchronous XHR in page dismissal") != std::string::npos)
+    return false;
+
   bool contains_uncaught = str.find("\"Uncaught ") != std::string::npos;
   if (severity == logging::LOG_ERROR ||
       (severity == logging::LOG_INFO && contains_uncaught)) {
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 2a8001cf..58dfb84 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -757,7 +757,7 @@
 
 #if defined(OS_WIN) || defined(OS_MACOSX) || \
     (defined(OS_LINUX) && !defined(OS_CHROMEOS))
-  browser_switcher::prefs::RegisterProfilePrefs(registry);
+  browser_switcher::BrowserSwitcherPrefs::RegisterProfilePrefs(registry);
 #endif
 
 #if defined(TOOLKIT_VIEWS)
diff --git a/chrome/browser/previews/previews_lite_page_navigation_throttle.cc b/chrome/browser/previews/previews_lite_page_navigation_throttle.cc
index 9e5fe4b..11044fc 100644
--- a/chrome/browser/previews/previews_lite_page_navigation_throttle.cc
+++ b/chrome/browser/previews/previews_lite_page_navigation_throttle.cc
@@ -541,7 +541,7 @@
     // it is pointing towards the original page, it is considered a bypass.
     // Otherwise it is just a forwarded bypass.
     if (GURL(original_url) == navigation_handle()->GetURL()) {
-      GetServerLitePageInfo()->status = previews::ServerLitePageStatus::kBypass;
+      SetServerLitePageInfoStatus(previews::ServerLitePageStatus::kBypass);
       manager_->AddSingleBypass(navigation_handle()->GetURL().spec());
       UMA_HISTOGRAM_MEDIUM_TIMES(
           "Previews.ServerLitePage.HttpOnlyFallbackPenalty",
@@ -574,7 +574,7 @@
     // Otherwise fall out of this if and potentially trigger again.
     UMA_HISTOGRAM_ENUMERATION("Previews.ServerLitePage.ServerResponse",
                               ServerResponse::kRedirect);
-    GetServerLitePageInfo()->status = previews::ServerLitePageStatus::kRedirect;
+    SetServerLitePageInfoStatus(previews::ServerLitePageStatus::kRedirect);
   }
 
   return MaybeNavigateToPreview();
@@ -590,7 +590,7 @@
 
   UMA_HISTOGRAM_ENUMERATION("Previews.ServerLitePage.ServerResponse",
                             ServerResponse::kFailed);
-  GetServerLitePageInfo()->status = previews::ServerLitePageStatus::kFailure;
+  SetServerLitePageInfoStatus(previews::ServerLitePageStatus::kFailure);
 
   // The Preview was triggered but there was some irrecoverable issue (like
   // there is no network connection). Load the original page and let it go
@@ -598,7 +598,8 @@
   LoadAndBypass(
       navigation_handle()->GetWebContents(), manager_,
       MakeOpenURLParams(navigation_handle(), GURL(original_url), std::string()),
-      GetServerLitePageInfo()->Clone(), true);
+      GetServerLitePageInfo() ? GetServerLitePageInfo()->Clone() : nullptr,
+      true);
   return content::NavigationThrottle::CANCEL;
 }
 
@@ -690,6 +691,15 @@
   return previews_data->server_lite_page_info();
 }
 
+void PreviewsLitePageNavigationThrottle::SetServerLitePageInfoStatus(
+    previews::ServerLitePageStatus status) {
+  previews::PreviewsUserData::ServerLitePageInfo* info =
+      GetServerLitePageInfo();
+  if (!info)
+    return;
+  info->status = status;
+}
+
 previews::PreviewsUserData::ServerLitePageInfo*
 PreviewsLitePageNavigationThrottle::GetOrCreateServerLitePageInfo() const {
   PreviewsUITabHelper* ui_tab_helper = PreviewsUITabHelper::FromWebContents(
diff --git a/chrome/browser/previews/previews_lite_page_navigation_throttle.h b/chrome/browser/previews/previews_lite_page_navigation_throttle.h
index a0d073ef..715ea349 100644
--- a/chrome/browser/previews/previews_lite_page_navigation_throttle.h
+++ b/chrome/browser/previews/previews_lite_page_navigation_throttle.h
@@ -142,6 +142,10 @@
   // navigation, if there is one. If not, returns nullptr.
   previews::PreviewsUserData::ServerLitePageInfo* GetServerLitePageInfo() const;
 
+  // Safely sets the status of the ServerLitePageInfo struct from an existing
+  // attempted lite page navigation, if there is one. If not, does nothing.
+  void SetServerLitePageInfoStatus(previews::ServerLitePageStatus status);
+
   // Gets the ServerLitePageInfo struct from an existing attempted lite page
   // navigation, if there is one. If not, returns a new ServerLitePageInfo
   // initialized with metadata from navigation_handle() and |this| that is owned
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
index 9c0910d..a5fe67e 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
@@ -398,7 +398,7 @@
       MediaCaptureDevicesDispatcher::GetInstance()
           ->GetMediaStreamCaptureIndicator()
           ->RegisterMediaStream(web_contents_, video_devices);
-  ui->OnStarted(base::RepeatingClosure());
+  ui->OnStarted(base::OnceClosure(), base::RepeatingClosure());
   ExpectCanDiscardFalseAllReasons(&tab_lifecycle_unit,
                                   DecisionFailureReason::LIVE_STATE_CAPTURING);
 
@@ -421,7 +421,7 @@
       MediaCaptureDevicesDispatcher::GetInstance()
           ->GetMediaStreamCaptureIndicator()
           ->RegisterMediaStream(web_contents_, desktop_capture_devices);
-  ui->OnStarted(base::RepeatingClosure());
+  ui->OnStarted(base::OnceClosure(), base::RepeatingClosure());
   ExpectCanDiscardFalseAllReasons(
       &tab_lifecycle_unit, DecisionFailureReason::LIVE_STATE_DESKTOP_CAPTURE);
 
diff --git a/chrome/browser/resource_coordinator/tab_manager_browsertest.cc b/chrome/browser/resource_coordinator/tab_manager_browsertest.cc
index 16306b42..a70ac3e 100644
--- a/chrome/browser/resource_coordinator/tab_manager_browsertest.cc
+++ b/chrome/browser/resource_coordinator/tab_manager_browsertest.cc
@@ -661,7 +661,7 @@
   std::unique_ptr<content::MediaStreamUI> video_stream_ui =
       dispatcher->GetMediaStreamCaptureIndicator()->RegisterMediaStream(
           tab, video_devices);
-  video_stream_ui->OnStarted(base::Closure());
+  video_stream_ui->OnStarted(base::OnceClosure(), base::RepeatingClosure());
 
   // Should not be able to discard a tab.
   ASSERT_FALSE(
@@ -734,7 +734,7 @@
   std::unique_ptr<content::MediaStreamUI> video_stream_ui =
       dispatcher->GetMediaStreamCaptureIndicator()->RegisterMediaStream(
           tab, video_devices);
-  video_stream_ui->OnStarted(base::Closure());
+  video_stream_ui->OnStarted(base::OnceClosure(), base::RepeatingClosure());
 
   // Should not be able to suspend a tab which plays a video.
   int render_process_id = tab->GetMainFrame()->GetProcess()->GetID();
diff --git a/chrome/browser/resources/safe_browsing/PRESUBMIT.py b/chrome/browser/resources/safe_browsing/PRESUBMIT.py
index 6dd31065..d617bd5 100644
--- a/chrome/browser/resources/safe_browsing/PRESUBMIT.py
+++ b/chrome/browser/resources/safe_browsing/PRESUBMIT.py
@@ -28,7 +28,7 @@
   # At worst, the submitter can skip the presubmit check on upload if it isn't
   # correct.
   return [output_api.PresubmitError(
-      'Increment |version_id| in download_file_types.ascipb if you are '
+      'Increment |version_id| in download_file_types.asciipb if you are '
       'updating the file types proto.')]
 
 def CheckChangeOnUpload(input_api, output_api):
diff --git a/chrome/browser/resources/ssl/ssl_error_assistant/PRESUBMIT.py b/chrome/browser/resources/ssl/ssl_error_assistant/PRESUBMIT.py
index 6000ab2..16a0f32 100644
--- a/chrome/browser/resources/ssl/ssl_error_assistant/PRESUBMIT.py
+++ b/chrome/browser/resources/ssl/ssl_error_assistant/PRESUBMIT.py
@@ -32,7 +32,7 @@
   # At worst, the submitter can skip the presubmit check on upload if it isn't
   # correct.
   return [output_api.PresubmitError(
-      'Increment |version_id| in ssl_error_assistant.ascipb if you are '
+      'Increment |version_id| in ssl_error_assistant.asciipb if you are '
       'updating the SSL Error Assistant proto.')]
 
 def CheckChangeOnUpload(input_api, output_api):
diff --git a/chrome/browser/sync/test/integration/two_client_typed_urls_sync_test.cc b/chrome/browser/sync/test/integration/two_client_typed_urls_sync_test.cc
index 63596e2..8185a97 100644
--- a/chrome/browser/sync/test/integration/two_client_typed_urls_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_typed_urls_sync_test.cc
@@ -492,7 +492,14 @@
   ASSERT_TRUE(ProfilesHaveSameTypedURLsChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest, AddOneDeleteOtherAddAgain) {
+// crbug.com/919090
+#if defined(THREAD_SANITIZER)
+#define MAYBE_AddOneDeleteOtherAddAgain DISABLED_AddOneDeleteOtherAddAgain
+#else
+#define MAYBE_AddOneDeleteOtherAddAgain AddOneDeleteOtherAddAgain
+#endif
+IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest,
+                       MAYBE_AddOneDeleteOtherAddAgain) {
   const base::string16 kHistoryUrl(
       ASCIIToUTF16("http://www.add-delete-add-history.google.com/"));
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
diff --git a/chrome/browser/tab_contents/view_source_browsertest.cc b/chrome/browser/tab_contents/view_source_browsertest.cc
index 1f713b3..c92695a7 100644
--- a/chrome/browser/tab_contents/view_source_browsertest.cc
+++ b/chrome/browser/tab_contents/view_source_browsertest.cc
@@ -542,7 +542,7 @@
                        ViewSourceNotAffectedByHeaderPolicy) {
   ASSERT_TRUE(embedded_test_server()->Start());
   const std::string k_verify_feature = R"(
-      var all_features = document.policy.allowedFeatures();
+      var all_features = document.featurePolicy.allowedFeatures();
       var vs = all_features.find((f) => f === 'vertical-scroll');
       console.log(vs);
       domAutomationController.send("" + vs);)";
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index dcf5c6b..0aea5de 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1742,6 +1742,9 @@
       "//chromeos/components/multidevice/logging",
       "//chromeos/components/proximity_auth",
       "//chromeos/components/tether",
+      "//chromeos/cryptohome",
+      "//chromeos/login/auth",
+      "//chromeos/login/login_state",
       "//chromeos/resources:resources_grit",
       "//chromeos/services/assistant/public:feature_flags",
       "//chromeos/services/assistant/public/mojom",
diff --git a/chrome/browser/ui/ash/ksv/DEPS b/chrome/browser/ui/ash/ksv/DEPS
index f361719..17b17d3 100644
--- a/chrome/browser/ui/ash/ksv/DEPS
+++ b/chrome/browser/ui/ash/ksv/DEPS
@@ -1,16 +1,10 @@
 include_rules = [
   "-chrome",
   "+ash/components/shortcut_viewer/public",
-  "+chrome/browser/chromeos/accessibility",
   "+chrome/browser/ui/ash/ksv",
 ]
 
 specific_include_rules = {
-  # mash-ok. https://crbug.com/841020
-  "keyboard_shortcut_viewer_util\.cc": [
-    "+ash/components/shortcut_viewer/views/keyboard_shortcut_view.h",
-    "+ash/shell.h",
-  ],
   # Tests.
   "keyboard_shortcut_viewer_metadata_unittest\.cc": [
     "+ash/components/shortcut_viewer",
diff --git a/chrome/browser/ui/ash/ksv/keyboard_shortcut_viewer_util.cc b/chrome/browser/ui/ash/ksv/keyboard_shortcut_viewer_util.cc
index c74a63c..abcace9 100644
--- a/chrome/browser/ui/ash/ksv/keyboard_shortcut_viewer_util.cc
+++ b/chrome/browser/ui/ash/ksv/keyboard_shortcut_viewer_util.cc
@@ -5,36 +5,19 @@
 #include "chrome/browser/ui/ash/ksv/keyboard_shortcut_viewer_util.h"
 
 #include "ash/components/shortcut_viewer/public/mojom/shortcut_viewer.mojom.h"
-#include "ash/components/shortcut_viewer/views/keyboard_shortcut_view.h"
-#include "ash/public/cpp/ash_features.h"
-#include "ash/shell.h"
 #include "base/time/time.h"
 #include "content/public/common/service_manager_connection.h"
 #include "services/service_manager/public/cpp/connector.h"
-#include "ui/base/ui_base_features.h"
 
 namespace keyboard_shortcut_viewer_util {
 
 void ToggleKeyboardShortcutViewer() {
-  base::TimeTicks user_gesture_time = base::TimeTicks::Now();
-  if (ash::features::IsKeyboardShortcutViewerAppEnabled()) {
-    shortcut_viewer::mojom::ShortcutViewerPtr shortcut_viewer_ptr;
-    service_manager::Connector* connector =
-        content::ServiceManagerConnection::GetForProcess()->GetConnector();
-    connector->BindInterface(shortcut_viewer::mojom::kServiceName,
-                             &shortcut_viewer_ptr);
-    shortcut_viewer_ptr->Toggle(user_gesture_time);
-  } else {
-    // A value of |null| while IsSingleProcessMash() results in the keyboard
-    // shortcut viewer using DesktopNativeWidgetAura, just as all other non-ash
-    // codes does in single-process-mash.
-    aura::Window* context =
-        features::IsSingleProcessMash()
-            ? nullptr
-            : ash::Shell::Get()->GetRootWindowForNewWindows();
-    keyboard_shortcut_viewer::KeyboardShortcutView::Toggle(user_gesture_time,
-                                                           context);
-  }
+  shortcut_viewer::mojom::ShortcutViewerPtr shortcut_viewer_ptr;
+  service_manager::Connector* connector =
+      content::ServiceManagerConnection::GetForProcess()->GetConnector();
+  connector->BindInterface(shortcut_viewer::mojom::kServiceName,
+                           &shortcut_viewer_ptr);
+  shortcut_viewer_ptr->Toggle(base::TimeTicks::Now());
 }
 
 }  // namespace keyboard_shortcut_viewer_util
diff --git a/chrome/browser/ui/blocked_content/blocked_window_params.cc b/chrome/browser/ui/blocked_content/blocked_window_params.cc
index 5097b91..dfb946ea 100644
--- a/chrome/browser/ui/blocked_content/blocked_window_params.cc
+++ b/chrome/browser/ui/blocked_content/blocked_window_params.cc
@@ -14,6 +14,7 @@
 
 BlockedWindowParams::BlockedWindowParams(
     const GURL& target_url,
+    const url::Origin& initiator_origin,
     const content::Referrer& referrer,
     const std::string& frame_name,
     WindowOpenDisposition disposition,
@@ -21,6 +22,7 @@
     bool user_gesture,
     bool opener_suppressed)
     : target_url_(target_url),
+      initiator_origin_(initiator_origin),
       referrer_(referrer),
       frame_name_(frame_name),
       disposition_(disposition),
@@ -40,6 +42,7 @@
   NavigateParams nav_params(
       Profile::FromBrowserContext(web_contents->GetBrowserContext()), popup_url,
       ui::PAGE_TRANSITION_LINK);
+  nav_params.initiator_origin = initiator_origin_;
   nav_params.referrer = referrer_;
   nav_params.frame_name = frame_name_;
   nav_params.source_contents = web_contents;
diff --git a/chrome/browser/ui/blocked_content/blocked_window_params.h b/chrome/browser/ui/blocked_content/blocked_window_params.h
index b2e8ff3..26857e6 100644
--- a/chrome/browser/ui/blocked_content/blocked_window_params.h
+++ b/chrome/browser/ui/blocked_content/blocked_window_params.h
@@ -10,6 +10,7 @@
 #include "third_party/blink/public/web/window_features.mojom.h"
 #include "ui/base/window_open_disposition.h"
 #include "url/gurl.h"
+#include "url/origin.h"
 
 namespace content {
 class WebContents;
@@ -18,6 +19,7 @@
 class BlockedWindowParams {
  public:
   BlockedWindowParams(const GURL& target_url,
+                      const url::Origin& initiator_origin,
                       const content::Referrer& referrer,
                       const std::string& frame_name_,
                       WindowOpenDisposition disposition,
@@ -33,6 +35,7 @@
 
  private:
   GURL target_url_;
+  url::Origin initiator_origin_;
   content::Referrer referrer_;
   std::string frame_name_;
   WindowOpenDisposition disposition_;
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc b/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc
index d368de17..552b5a5 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc
@@ -431,7 +431,7 @@
         GetMediaStreamCaptureIndicator();
   std::unique_ptr<content::MediaStreamUI> media_stream_ui =
       indicator->RegisterMediaStream(web_contents(), audio_devices);
-  media_stream_ui->OnStarted(base::Closure());
+  media_stream_ui->OnStarted(base::OnceClosure(), base::RepeatingClosure());
   microphone_camera_state &= ~TabSpecificContentSettings::MICROPHONE_BLOCKED;
   content_settings->OnMediaStreamPermissionSet(url,
                                                microphone_camera_state,
@@ -1006,8 +1006,9 @@
       content_setting_bubble_model->bubble_content().list_items;
   EXPECT_EQ(0U, list_items.size());
 
-  BlockedWindowParams params(GURL("about:blank"), content::Referrer(),
-                             std::string(), WindowOpenDisposition::NEW_POPUP,
+  BlockedWindowParams params(GURL("about:blank"), url::Origin(),
+                             content::Referrer(), std::string(),
+                             WindowOpenDisposition::NEW_POPUP,
                              blink::mojom::WindowFeatures(), false, true);
   constexpr size_t kItemCount = 3;
   for (size_t i = 1; i <= kItemCount; i++) {
diff --git a/chrome/browser/ui/layout_constants.cc b/chrome/browser/ui/layout_constants.cc
index 841a6b0..e387f55 100644
--- a/chrome/browser/ui/layout_constants.cc
+++ b/chrome/browser/ui/layout_constants.cc
@@ -99,7 +99,7 @@
     case TOOLBAR_ACTION_VIEW: {
       // TODO(afakhry): Unify all toolbar button sizes on all platforms.
       // https://crbug.com/822967.
-      return gfx::Insets(touch_ui ? 10 : 2);
+      return gfx::Insets(touch_ui ? 10 : 0);
     }
   }
   NOTREACHED();
diff --git a/chrome/browser/ui/managed_ui.cc b/chrome/browser/ui/managed_ui.cc
index f49ba49..b74fbfac 100644
--- a/chrome/browser/ui/managed_ui.cc
+++ b/chrome/browser/ui/managed_ui.cc
@@ -25,11 +25,6 @@
   if (!base::FeatureList::IsEnabled(features::kShowManagedUi))
     return false;
 
-  // Most policies don't apply to incognito mode, and incognito already
-  // discloses that you may be tracked/MITM'd by your admin.
-  if (profile->IsOffTheRecord())
-    return false;
-
   // This profile may have policies configured.
   auto* profile_connector =
       policy::ProfilePolicyConnectorFactory::GetForBrowserContext(profile);
diff --git a/chrome/browser/ui/screen_capture_notification_ui_browsertest.cc b/chrome/browser/ui/screen_capture_notification_ui_browsertest.cc
index 97bb0271..a162129 100644
--- a/chrome/browser/ui/screen_capture_notification_ui_browsertest.cc
+++ b/chrome/browser/ui/screen_capture_notification_ui_browsertest.cc
@@ -25,13 +25,14 @@
     screen_capture_notification_ui_ =
         ScreenCaptureNotificationUI::Create(base::string16(
             base::ASCIIToUTF16("ScreenCaptureNotificationUI Browser Test")));
-    on_started_result_ =
-        screen_capture_notification_ui_->OnStarted(base::BindRepeating(
+    on_started_result_ = screen_capture_notification_ui_->OnStarted(
+        base::BindRepeating(
             [](ScreenCaptureNotificationUiBrowserTest* test) {
               if (test->run_loop_)
                 test->run_loop_->QuitWhenIdle();
             },
-            base::Unretained(this)));
+            base::Unretained(this)),
+        base::RepeatingClosure());
   }
 
   bool VerifyUi() override {
diff --git a/chrome/browser/ui/screen_capture_notification_ui_stub.cc b/chrome/browser/ui/screen_capture_notification_ui_stub.cc
index 980989a..fcef998 100644
--- a/chrome/browser/ui/screen_capture_notification_ui_stub.cc
+++ b/chrome/browser/ui/screen_capture_notification_ui_stub.cc
@@ -12,7 +12,8 @@
   ScreenCaptureNotificationUIStub() {}
   ~ScreenCaptureNotificationUIStub() override {}
 
-  gfx::NativeViewId OnStarted(const base::Closure& stop_callback) override {
+  gfx::NativeViewId OnStarted(base::OnceClosure stop_callback,
+                              base::RepeatingClosure source_callback) override {
     NOTIMPLEMENTED();
     return 0;
   }
diff --git a/chrome/browser/ui/toolbar/toolbar_actions_bar.cc b/chrome/browser/ui/toolbar/toolbar_actions_bar.cc
index 1c7d262c..f362d3eb 100644
--- a/chrome/browser/ui/toolbar/toolbar_actions_bar.cc
+++ b/chrome/browser/ui/toolbar/toolbar_actions_bar.cc
@@ -166,7 +166,7 @@
     num_rows += (std::max(0, icon_count - 1) / num_icons);
   }
 
-  return gfx::ScaleToFlooredSize(GetViewSize(), num_icons, num_rows);
+  return gfx::Size(IconCountToWidth(num_icons), IconCountToWidth(num_rows));
 }
 
 int ToolbarActionsBar::GetMinimumWidth() const {
@@ -178,12 +178,17 @@
 }
 
 int ToolbarActionsBar::IconCountToWidth(size_t icons) const {
-  return icons * GetViewSize().width();
+  if (icons == 0)
+    return 0;
+  return icons * GetViewSize().width() +
+         (icons - 1) * GetLayoutConstant(TOOLBAR_ELEMENT_PADDING);
 }
 
 size_t ToolbarActionsBar::WidthToIconCount(int pixels) const {
-  return base::ClampToRange(pixels / GetViewSize().width(), 0,
-                            static_cast<int>(toolbar_actions_.size()));
+  const int element_padding = GetLayoutConstant(TOOLBAR_ELEMENT_PADDING);
+  return base::ClampToRange(
+      (pixels + element_padding) / (GetViewSize().width() + element_padding), 0,
+      static_cast<int>(toolbar_actions_.size()));
 }
 
 size_t ToolbarActionsBar::GetIconCount() const {
@@ -274,8 +279,10 @@
                                   : relative_index;
 
   const auto size = GetViewSize();
-  return gfx::Rect(
-      gfx::Point(index_in_row * size.width(), row_index * size.height()), size);
+  const int element_padding = GetLayoutConstant(TOOLBAR_ELEMENT_PADDING);
+  return gfx::Rect(gfx::Point(index_in_row * (size.width() + element_padding),
+                              row_index * (size.height() + element_padding)),
+                   size);
 }
 
 std::vector<ToolbarActionViewController*>
@@ -372,7 +379,7 @@
 void ToolbarActionsBar::SetOverflowRowWidth(int width) {
   DCHECK(in_overflow_mode());
   platform_settings_.icons_per_overflow_menu_row =
-      std::max(width / GetViewSize().width(), 1);
+      std::max(WidthToIconCount(width), static_cast<size_t>(1));
 }
 
 void ToolbarActionsBar::OnResizeComplete(int width) {
diff --git a/chrome/browser/ui/toolbar/toolbar_actions_bar_unittest.cc b/chrome/browser/ui/toolbar/toolbar_actions_bar_unittest.cc
index 0c72f08..5f846086 100644
--- a/chrome/browser/ui/toolbar/toolbar_actions_bar_unittest.cc
+++ b/chrome/browser/ui/toolbar/toolbar_actions_bar_unittest.cc
@@ -255,7 +255,8 @@
   EXPECT_EQ(3u, toolbar_actions_bar()->GetIconCount());
   const gfx::Size view_size = toolbar_actions_bar()->GetViewSize();
   // Check the widths.
-  int expected_width = 3 * view_size.width();
+  int expected_width =
+      3 * view_size.width() + 2 * GetLayoutConstant(TOOLBAR_ELEMENT_PADDING);
   EXPECT_EQ(expected_width, toolbar_actions_bar()->GetFullSize().width());
   // Since all icons are showing, the current width should be the max width.
   int maximum_width = expected_width;
@@ -271,7 +272,8 @@
   EXPECT_EQ(2u, toolbar_actions_bar()->GetIconCount());
 
   // The current width should now be enough for two icons.
-  expected_width = 2 * view_size.width();
+  expected_width =
+      2 * view_size.width() + GetLayoutConstant(TOOLBAR_ELEMENT_PADDING);
   EXPECT_EQ(expected_width, toolbar_actions_bar()->GetFullSize().width());
   // The maximum and minimum widths should have remained constant (since we have
   // the same number of actions).
@@ -328,7 +330,7 @@
 
   // If we resize by enough to include a new icon, width and icon count should
   // both increase.
-  width += view_size.width();
+  width += view_size.width() + GetLayoutConstant(TOOLBAR_ELEMENT_PADDING);
   toolbar_actions_bar()->OnResizeComplete(width);
   EXPECT_EQ(width, toolbar_actions_bar()->GetFullSize().width());
   EXPECT_EQ(2u, toolbar_actions_bar()->GetIconCount());
@@ -336,7 +338,7 @@
   // If we shrink the bar so that a full icon can't fit, it should resize to
   // hide that icon.
   toolbar_actions_bar()->OnResizeComplete(width - 1);
-  width -= view_size.width();
+  width -= view_size.width() + GetLayoutConstant(TOOLBAR_ELEMENT_PADDING);
   EXPECT_EQ(width, toolbar_actions_bar()->GetFullSize().width());
   EXPECT_EQ(1u, toolbar_actions_bar()->GetIconCount());
 }
@@ -442,8 +444,11 @@
 // Test the bounds calculation for different indices.
 TEST_P(ToolbarActionsBarUnitTest, TestActionFrameBounds) {
   const auto size = toolbar_actions_bar()->GetViewSize();
-  const auto icon_rect = [size](int x, int y) {
-    return gfx::Rect(gfx::Point(x * size.width(), y * size.height()), size);
+  const int element_padding = GetLayoutConstant(TOOLBAR_ELEMENT_PADDING);
+  const auto icon_rect = [size, element_padding](int x, int y) {
+    return gfx::Rect(gfx::Point(x * (element_padding + size.width()),
+                                y * (element_padding + size.height())),
+                     size);
   };
 
   constexpr int kIconsPerOverflowRow = 3;
@@ -456,8 +461,11 @@
                           ActionType::BROWSER_ACTION);
   }
   toolbar_model()->SetVisibleIconCount(kNumExtensions);
-  const int icon_width = toolbar_actions_bar()->GetViewSize().width();
-  overflow_bar()->SetOverflowRowWidth(icon_width * kIconsPerOverflowRow);
+  const int overflow_width =
+      kIconsPerOverflowRow *
+          (toolbar_actions_bar()->GetViewSize().width() + element_padding) -
+      element_padding;
+  overflow_bar()->SetOverflowRowWidth(overflow_width);
   EXPECT_EQ(kIconsPerOverflowRow,
             overflow_bar()->platform_settings().icons_per_overflow_menu_row);
 
diff --git a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc
index fd244b8e..976b24bc 100644
--- a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc
+++ b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc
@@ -197,7 +197,7 @@
   if (params.request_audio) {
     audio_share_checkbox_ = new views::Checkbox(
         l10n_util::GetStringUTF16(IDS_DESKTOP_MEDIA_PICKER_AUDIO_SHARE));
-    audio_share_checkbox_->SetChecked(false);
+    audio_share_checkbox_->SetChecked(true);
   }
 
   // Focus on the first non-null media_list.
diff --git a/chrome/browser/ui/views/screen_capture_notification_ui_views.cc b/chrome/browser/ui/views/screen_capture_notification_ui_views.cc
index 47be9d9..9bcf4eb 100644
--- a/chrome/browser/ui/views/screen_capture_notification_ui_views.cc
+++ b/chrome/browser/ui/views/screen_capture_notification_ui_views.cc
@@ -20,6 +20,7 @@
 #include "ui/views/controls/image_view.h"
 #include "ui/views/controls/link.h"
 #include "ui/views/controls/link_listener.h"
+#include "ui/views/layout/box_layout.h"
 #include "ui/views/view.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_delegate.h"
@@ -40,8 +41,6 @@
 
 namespace {
 
-const int kMinimumWidth = 460;
-const int kMaximumWidth = 1000;
 const int kHorizontalMargin = 10;
 const float kWindowAlphaValue = 0.85f;
 const int kPaddingVertical = 5;
@@ -92,10 +91,10 @@
   ~ScreenCaptureNotificationUIViews() override;
 
   // ScreenCaptureNotificationUI interface.
-  gfx::NativeViewId OnStarted(const base::Closure& stop_callback) override;
+  gfx::NativeViewId OnStarted(base::OnceClosure stop_callback,
+                              base::RepeatingClosure source_callback) override;
 
   // views::View overrides.
-  gfx::Size CalculatePreferredSize() const override;
   void Layout() override;
 
   // views::WidgetDelegateView overrides.
@@ -117,12 +116,16 @@
  private:
   // Helper to call |stop_callback_|.
   void NotifyStopped();
+  // Helper to call |source_callback_|.
+  void NotifySourceChange();
 
   const base::string16 text_;
-  base::Closure stop_callback_;
+  base::OnceClosure stop_callback_;
+  base::RepeatingClosure source_callback_;
   NotificationBarClientView* client_view_;
   views::ImageView* gripper_;
   views::Label* label_;
+  views::Button* source_button_;
   views::Button* stop_button_;
   views::Link* hide_link_;
 
@@ -135,10 +138,14 @@
       client_view_(nullptr),
       gripper_(nullptr),
       label_(nullptr),
+      source_button_(nullptr),
       stop_button_(nullptr),
       hide_link_(nullptr) {
   set_owned_by_client();
 
+  SetLayoutManager(std::make_unique<views::BoxLayout>(
+      views::BoxLayout::kHorizontal, gfx::Insets(), kHorizontalMargin));
+
   gripper_ = new views::ImageView();
   gripper_->SetImage(
       ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
@@ -148,6 +155,12 @@
   label_ = new views::Label();
   AddChildView(label_);
 
+  base::string16 source_text =
+      l10n_util::GetStringUTF16(IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_SOURCE);
+  source_button_ =
+      views::MdTextButton::CreateSecondaryUiButton(this, source_text);
+  AddChildView(source_button_);
+
   base::string16 stop_text =
       l10n_util::GetStringUTF16(IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_STOP);
   stop_button_ =
@@ -164,13 +177,19 @@
 }
 
 ScreenCaptureNotificationUIViews::~ScreenCaptureNotificationUIViews() {
+  source_callback_.Reset();
   stop_callback_.Reset();
   delete GetWidget();
 }
 
 gfx::NativeViewId ScreenCaptureNotificationUIViews::OnStarted(
-    const base::Closure& stop_callback) {
-  stop_callback_ = stop_callback;
+    base::OnceClosure stop_callback,
+    base::RepeatingClosure source_callback) {
+  stop_callback_ = std::move(stop_callback);
+  source_callback_ = std::move(source_callback);
+
+  if (source_callback_.is_null())
+    source_button_->SetVisible(false);
 
   label_->SetElideBehavior(gfx::ELIDE_MIDDLE);
   label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
@@ -230,47 +249,13 @@
 #endif
 }
 
-gfx::Size ScreenCaptureNotificationUIViews::CalculatePreferredSize() const {
-  gfx::Size grip_size = gripper_->GetPreferredSize();
-  gfx::Size label_size = label_->GetPreferredSize();
-  gfx::Size stop_button_size = stop_button_->GetPreferredSize();
-  gfx::Size hide_link_size = hide_link_->GetPreferredSize();
-  int width = kHorizontalMargin * 3 + grip_size.width() + label_size.width() +
-      stop_button_size.width() + hide_link_size.width();
-  width = std::max(width, kMinimumWidth);
-  width = std::min(width, kMaximumWidth);
-  return gfx::Size(width, std::max(label_size.height(),
-                                   std::max(hide_link_size.height(),
-                                            stop_button_size.height())));
-}
-
 void ScreenCaptureNotificationUIViews::Layout() {
-  gfx::Rect grip_rect(gripper_->GetPreferredSize());
-  grip_rect.set_y((bounds().height() - grip_rect.height()) / 2);
-  gripper_->SetBoundsRect(grip_rect);
+  View::Layout();
 
-  gfx::Rect stop_button_rect(stop_button_->GetPreferredSize());
-  gfx::Rect hide_link_rect(hide_link_->GetPreferredSize());
-
-  hide_link_rect.set_x(bounds().width() - hide_link_rect.width());
-  hide_link_rect.set_y((bounds().height() - hide_link_rect.height()) / 2);
-  hide_link_->SetBoundsRect(hide_link_rect);
-
-  stop_button_rect.set_x(
-      hide_link_rect.x() - kHorizontalMargin - stop_button_rect.width());
-  stop_button_->SetBoundsRect(stop_button_rect);
-
-  gfx::Rect label_rect;
-  label_rect.set_x(grip_rect.right() + kHorizontalMargin);
-  label_rect.set_width(
-      stop_button_rect.x() - kHorizontalMargin - label_rect.x());
-  label_rect.set_height(bounds().height());
-  label_->SetBoundsRect(label_rect);
-
-  client_view_->set_client_rect(gfx::Rect(
-      stop_button_rect.x(), stop_button_rect.y(),
-      stop_button_rect.width() + kHorizontalMargin + hide_link_rect.width(),
-      std::max(stop_button_rect.height(), hide_link_rect.height())));
+  gfx::Rect client_rect = source_button_->bounds();
+  client_rect.Union(stop_button_->bounds());
+  client_rect.Union(hide_link_->bounds());
+  client_view_->set_client_rect(client_rect);
 }
 
 void ScreenCaptureNotificationUIViews::DeleteDelegate() {
@@ -319,7 +304,12 @@
 
 void ScreenCaptureNotificationUIViews::ButtonPressed(views::Button* sender,
                                                      const ui::Event& event) {
-  NotifyStopped();
+  if (sender == stop_button_) {
+    NotifyStopped();
+  } else {
+    DCHECK_EQ(source_button_, sender);
+    NotifySourceChange();
+  }
 }
 
 void ScreenCaptureNotificationUIViews::LinkClicked(views::Link* source,
@@ -327,12 +317,14 @@
   GetWidget()->Minimize();
 }
 
+void ScreenCaptureNotificationUIViews::NotifySourceChange() {
+  if (!source_callback_.is_null())
+    source_callback_.Run();
+}
+
 void ScreenCaptureNotificationUIViews::NotifyStopped() {
-  if (!stop_callback_.is_null()) {
-    base::Closure callback = stop_callback_;
-    stop_callback_.Reset();
-    callback.Run();
-  }
+  if (!stop_callback_.is_null())
+    std::move(stop_callback_).Run();
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/views/tabs/tab_icon.cc b/chrome/browser/ui/views/tabs/tab_icon.cc
index a0bdbbf4..aa28656 100644
--- a/chrome/browser/ui/views/tabs/tab_icon.cc
+++ b/chrome/browser/ui/views/tabs/tab_icon.cc
@@ -57,7 +57,7 @@
 // Returns a rect in which the throbber should be painted.
 gfx::RectF GetThrobberBounds(const gfx::Rect& bounds) {
   gfx::RectF throbber_bounds(bounds);
-  constexpr float kThrobberHeightDp = 2;
+  constexpr float kThrobberHeightDp = 3;
   // The throbber starts 1dp below the tab icon.
   throbber_bounds.set_y(bounds.bottom() + 1);
   throbber_bounds.set_height(kThrobberHeightDp);
@@ -359,15 +359,13 @@
   cc::PaintFlags flags;
   flags.setColor(color);
   flags.setStyle(cc::PaintFlags::kFill_Style);
-  // Disable anti-aliasing to effectively "pixel align" the rectangle.
-  flags.setAntiAlias(false);
   if (animation_state_.finished_loading_animation_fade_out) {
     flags.setAlpha(
         (1.0 - *animation_state_.finished_loading_animation_fade_out) *
         SK_AlphaOPAQUE);
   }
 
-  canvas->DrawRect(bounds, flags);
+  canvas->DrawRoundRect(bounds, bounds.height() / 2, flags);
 }
 
 void TabIcon::PaintLoadingAnimation(gfx::Canvas* canvas,
diff --git a/chrome/browser/ui/views/toolbar/browser_actions_container.cc b/chrome/browser/ui/views/toolbar/browser_actions_container.cc
index 1bc1bf7..d0577fc 100644
--- a/chrome/browser/ui/views/toolbar/browser_actions_container.cc
+++ b/chrome/browser/ui/views/toolbar/browser_actions_container.cc
@@ -519,20 +519,18 @@
     const auto size = toolbar_actions_bar_->GetViewSize();
     const int offset_into_icon_area = GetMirroredXInView(event.x()) -
                                       GetResizeAreaWidth() + (size.width() / 2);
-    const int before_icon_unclamped = offset_into_icon_area / size.width();
-
-    // Next, figure out what row we're on. This only matters for overflow mode,
-    // but the calculation is the same for both.
-    row_index = event.y() / size.height();
-
-    // Sanity check - we should never be on a different row in the main
-    // container.
-    DCHECK(ShownInsideMenu() || row_index == 0);
+    const int before_icon_unclamped =
+        toolbar_actions_bar_->WidthToIconCount(offset_into_icon_area);
 
     // We need to figure out how many icons are visible on the relevant row.
     // In the main container, this will just be the visible actions.
     int visible_icons_on_row = VisibleBrowserActionsAfterAnimation();
     if (ShownInsideMenu()) {
+      // Next, figure out what row we're on.
+      const int element_padding = GetLayoutConstant(TOOLBAR_ELEMENT_PADDING);
+      row_index =
+          (event.y() + element_padding) / (size.height() + element_padding);
+
       const int icons_per_row = platform_settings().icons_per_overflow_menu_row;
       // If this is the final row of the overflow, then this is the remainder of
       // visible icons. Otherwise, it's a full row (kIconsPerRow).
@@ -575,11 +573,8 @@
   // Make sure we have the same view as we started with.
   DCHECK_EQ(GetIdAt(data.index()), data.id());
 
-  size_t i = drop_position_->row *
-      platform_settings().icons_per_overflow_menu_row +
-      drop_position_->icon_in_row;
-  if (ShownInsideMenu())
-    i += main_container_->VisibleBrowserActionsAfterAnimation();
+  size_t i = GetDropPositionIndex();
+
   // |i| now points to the item to the right of the drop indicator*, which is
   // correct when dragging an icon to the left. When dragging to the right,
   // however, we want the icon being dragged to get the index of the item to
@@ -712,23 +707,18 @@
     // The two-pixel width drop indicator.
     constexpr int kDropIndicatorWidth = 2;
 
-    // Convert back to a pixel offset into the container.  First find the X
-    // coordinate of the drop icon.
-    const auto size = toolbar_actions_bar_->GetViewSize();
-    // TODO(pbos): The drag/drop separator and view placement should share code
-    // after ToolbarActionsBar and BrowserActionsContainer merge.
-    const int drop_icon_x = GetResizeAreaWidth() +
-                            drop_position_->icon_in_row * size.width() -
-                            (kDropIndicatorWidth / 2);
-
-    // Next, clamp so the indicator doesn't touch the adjoining toolbar items.
-    const int drop_indicator_x =
-        base::ClampToRange(drop_icon_x, 1, width() - kDropIndicatorWidth - 1);
-
-    const int row_height = size.height();
-    const int drop_indicator_y = row_height * drop_position_->row;
-    gfx::Rect indicator_bounds = GetMirroredRect(gfx::Rect(
-        drop_indicator_x, drop_indicator_y, kDropIndicatorWidth, row_height));
+    const size_t i = GetDropPositionIndex();
+    const gfx::Rect frame = toolbar_actions_bar_->GetFrameForIndex(i);
+    gfx::Rect indicator_bounds = GetMirroredRect(
+        gfx::Rect(GetResizeAreaWidth() + frame.x() -
+                      GetLayoutConstant(TOOLBAR_ELEMENT_PADDING) / 2 -
+                      kDropIndicatorWidth / 2,
+                  frame.y(), kDropIndicatorWidth, frame.height()));
+    // Clamp the indicator to the view bounds so that heading / trailing markers
+    // don't paint outside the controller. It's OK if they paint over the resize
+    // area or separator (but the in-menu container has neither).
+    indicator_bounds.set_x(base::ClampToRange(
+        indicator_bounds.x(), 0, width() - indicator_bounds.width()));
 
     // Color of the drop indicator.
     // Always get the theme provider of the browser widget, since if this view
@@ -787,7 +777,7 @@
   if (num_icons == 0)
     return 0;
   return GetResizeAreaWidth() + GetSeparatorAreaWidth() +
-         num_icons * toolbar_actions_bar_->GetViewSize().width();
+         toolbar_actions_bar_->IconCountToWidth(num_icons);
 }
 
 int BrowserActionsContainer::GetWidthWithAllActionsVisible() const {
@@ -795,6 +785,15 @@
       toolbar_actions_bar_->toolbar_actions_unordered().size());
 }
 
+size_t BrowserActionsContainer::GetDropPositionIndex() const {
+  size_t i =
+      drop_position_->row * platform_settings().icons_per_overflow_menu_row +
+      drop_position_->icon_in_row;
+  if (ShownInsideMenu())
+    i += main_container_->VisibleBrowserActionsAfterAnimation();
+  return i;
+}
+
 int BrowserActionsContainer::GetResizeAreaWidth() const {
   if (!resize_area_)
     return 0;
diff --git a/chrome/browser/ui/views/toolbar/browser_actions_container.h b/chrome/browser/ui/views/toolbar/browser_actions_container.h
index a80fee6d..b109d1a3 100644
--- a/chrome/browser/ui/views/toolbar/browser_actions_container.h
+++ b/chrome/browser/ui/views/toolbar/browser_actions_container.h
@@ -267,6 +267,9 @@
   int GetWidthForIconCount(size_t num_icons) const;
   int GetWidthWithAllActionsVisible() const;
 
+  // Get index of the drag-drop position.
+  size_t GetDropPositionIndex() const;
+
   // Returns the preferred width given the limit of |max_width|. (Unlike most
   // views, since we don't want to show part of an icon or a large space after
   // the omnibox, this is probably *not* |max_width|).
diff --git a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
index fdacdb0..876f01d 100644
--- a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
@@ -789,6 +789,9 @@
           Profile::FromWebUI(web_ui()));
   content::StoragePartition* partition =
       signin_partition_manager->GetCurrentStoragePartition();
+  if (!partition)
+    return;
+
   net::CookieOptions cookie_options;
   cookie_options.set_include_httponly();
 
diff --git a/chrome/chrome_watcher/BUILD.gn b/chrome/chrome_watcher/BUILD.gn
index 39e9e3d..bbbcfbd 100644
--- a/chrome/chrome_watcher/BUILD.gn
+++ b/chrome/chrome_watcher/BUILD.gn
@@ -49,10 +49,8 @@
 
 shared_library("chrome_watcher") {
   sources = [
-    "chrome_watcher_main.cc",
-  ]
-  inputs = [
     "chrome_watcher.def",
+    "chrome_watcher_main.cc",
   ]
   deps = [
     ":chrome_watcher_resources",
@@ -66,7 +64,6 @@
     "//components/browser_watcher",
     "//content/public/common:static_switches",
   ]
-  ldflags = [ "/DEF:" + rebase_path("chrome_watcher.def", root_build_dir) ]
   configs -= [ "//build/config/win:console" ]
   configs += [ "//build/config/win:windowed" ]
 }
diff --git a/chrome/common/extensions/api/activity_log_private.json b/chrome/common/extensions/api/activity_log_private.json
index 685ac82..9be4030 100644
--- a/chrome/common/extensions/api/activity_log_private.json
+++ b/chrome/common/extensions/api/activity_log_private.json
@@ -109,6 +109,23 @@
         ]
       },
       {
+        "name": "deleteActivitiesByExtension",
+        "type": "function",
+        "description": "Deletes activities in the ActivityLog database specified by the extension ID.",
+        "parameters": [
+          {
+            "name": "extensionId",
+            "type": "string",
+            "description": "The ID of the extension to delete activities for."
+          }, {
+            "type": "function",
+            "name": "callback",
+            "parameters": [],
+            "optional": true
+          }
+        ]
+      },
+      {
         "name": "deleteDatabase",
         "type": "function",
         "description": "Deletes the entire ActivityLog database.",
diff --git a/chrome/renderer/extensions/automation_internal_custom_bindings.cc b/chrome/renderer/extensions/automation_internal_custom_bindings.cc
index 2b5c175..dbae8d4 100644
--- a/chrome/renderer/extensions/automation_internal_custom_bindings.cc
+++ b/chrome/renderer/extensions/automation_internal_custom_bindings.cc
@@ -7,7 +7,11 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include <algorithm>
 #include <memory>
+#include <set>
+#include <string>
+#include <utility>
 
 #include "base/bind.h"
 #include "base/i18n/string_search.h"
@@ -700,10 +704,8 @@
       [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
          AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node, int start,
          int end) {
-        if (node->data().role != ax::mojom::Role::kInlineTextBox) {
-          gfx::Rect global_bounds = ComputeGlobalNodeBounds(tree_wrapper, node);
-          result.Set(RectToV8Object(isolate, global_bounds));
-        }
+        if (node->data().role != ax::mojom::Role::kInlineTextBox)
+          return;
 
         // Use character offsets to compute the local bounds of this subrange.
         gfx::RectF local_bounds(0, 0,
@@ -1154,7 +1156,7 @@
 // http://crbug.com/784266
 // clang-format off
 void AutomationInternalCustomBindings::OnMessageReceived(
-    const IPC::Message& message){
+    const IPC::Message& message) {
   IPC_BEGIN_MESSAGE_MAP(AutomationInternalCustomBindings, message)
     IPC_MESSAGE_HANDLER(ExtensionMsg_AccessibilityEventBundle,
                         OnAccessibilityEvents)
diff --git a/chrome/renderer/resources/extensions/automation/automation_node.js b/chrome/renderer/resources/extensions/automation/automation_node.js
index 9349b72..558cf49 100644
--- a/chrome/renderer/resources/extensions/automation/automation_node.js
+++ b/chrome/renderer/resources/extensions/automation/automation_node.js
@@ -438,11 +438,6 @@
       return;
 
     if (!GetBoolAttribute(this.treeID, this.id, 'supportsTextLocation')) {
-      // GetBoundsForRange should only be called on INLINE_TEXT_BOX.
-      if (this.role != RoleType.INLINE_TEXT_BOX) {
-        callback(null);
-        return;
-      }
       try {
         callback(
             GetBoundsForRange(this.treeID, this.id, startIndex, endIndex));
diff --git a/chrome/services/file_util/public/cpp/sandboxed_rar_analyzer_unittest.cc b/chrome/services/file_util/public/cpp/sandboxed_rar_analyzer_unittest.cc
index 6e98bcd..cf4432a9 100644
--- a/chrome/services/file_util/public/cpp/sandboxed_rar_analyzer_unittest.cc
+++ b/chrome/services/file_util/public/cpp/sandboxed_rar_analyzer_unittest.cc
@@ -272,5 +272,19 @@
 #endif
 }
 
+TEST_F(SandboxedRarAnalyzerTest, AnalyzeMultipartRarContainingExecutable) {
+  base::FilePath path;
+  // Contains one part of an exe file.
+  ASSERT_NO_FATAL_FAILURE(path = GetFilePath("multipart.part0001.rar"));
+
+  safe_browsing::ArchiveAnalyzerResults results;
+  AnalyzeFile(path, &results);
+
+  ASSERT_TRUE(results.success);
+  ASSERT_TRUE(results.has_executable);
+  EXPECT_EQ(1, results.archived_binary.size());
+  EXPECT_TRUE(results.archived_archive_filenames.empty());
+}
+
 }  // namespace
 }  // namespace safe_browsing
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 5bd6051..29b0a5c 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -4483,7 +4483,10 @@
       "../browser/browser_switcher/alternative_browser_driver_unittest.cc",
       "../browser/browser_switcher/alternative_browser_launcher_unittest.cc",
       "../browser/browser_switcher/browser_switcher_navigation_throttle_unittest.cc",
+      "../browser/browser_switcher/browser_switcher_prefs_unittest.cc",
       "../browser/browser_switcher/browser_switcher_sitelist_unittest.cc",
+      "../browser/browser_switcher/mock_alternative_browser_driver.cc",
+      "../browser/browser_switcher/mock_alternative_browser_driver.h",
       "../browser/password_manager/password_store_signin_notifier_impl_unittest.cc",
     ]
   }
diff --git a/chrome/test/chromedriver/js/call_function.js b/chrome/test/chromedriver/js/call_function.js
index 923ec31..4bdca10 100644
--- a/chrome/test/chromedriver/js/call_function.js
+++ b/chrome/test/chromedriver/js/call_function.js
@@ -260,6 +260,7 @@
   // frames[0].document.body instanceof Object == false even though
   // typeof(frames[0].document.body) == 'object'.
   if ((typeof(value) == 'object' && value != null) ||
+      (value instanceof HTMLAllCollection) ||
       (typeof(value) == 'function' && value.nodeName &&
        value.nodeType == NodeType.ELEMENT)) {
     var nodeType = value['nodeType'];
diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py
index 0e29f70..01edb0a 100755
--- a/chrome/test/chromedriver/test/run_py_tests.py
+++ b/chrome/test/chromedriver/test/run_py_tests.py
@@ -90,6 +90,9 @@
     'ChromeDriverTest.testHoverOverElement',
     # https://bugs.chromium.org/p/chromedriver/issues/detail?id=833
     'ChromeDriverTest.testAlertOnNewWindow',
+    # https://bugs.chromium.org/p/chromedriver/issues/detail?id=2579
+    'ChromeDriverTest.testTakeElementScreenshot',
+    'ChromeDriverTest.testTakeElementScreenshotInIframe',
 ]
 
 _VERSION_SPECIFIC_FILTER = {}
@@ -242,9 +245,6 @@
         'HeadlessInvalidCertificateTest.*',
         # Tests of the desktop Chrome launch process.
         'LaunchDesktopTest.*',
-        # https://bugs.chromium.org/p/chromedriver/issues/detail?id=2579
-        'ChromeDriverTest.testTakeElementScreenshot',
-        'ChromeDriverTest.testTakeElementScreenshotInIframe',
     ]
 )
 _ANDROID_NEGATIVE_FILTER['chrome_stable'] = (
diff --git a/chrome/test/data/safe_browsing/rar/multipart.part0001.rar b/chrome/test/data/safe_browsing/rar/multipart.part0001.rar
new file mode 100644
index 0000000..4b68869
--- /dev/null
+++ b/chrome/test/data/safe_browsing/rar/multipart.part0001.rar
Binary files differ
diff --git a/chrome/test/data/safe_browsing/rar/multipart.part0002.rar b/chrome/test/data/safe_browsing/rar/multipart.part0002.rar
new file mode 100644
index 0000000..fca49fd
--- /dev/null
+++ b/chrome/test/data/safe_browsing/rar/multipart.part0002.rar
Binary files differ
diff --git a/chrome/test/data/webui/.eslintrc.js b/chrome/test/data/webui/.eslintrc.js
index f28d0056c..32e4b5e 100644
--- a/chrome/test/data/webui/.eslintrc.js
+++ b/chrome/test/data/webui/.eslintrc.js
@@ -5,6 +5,8 @@
 module.exports = {
   'env': {'browser': true, 'es6': true},
   'rules': {
+    'brace-style': ['error', '1tbs'],
+    'curly': ['error', 'multi-line', 'consistent'],
     'no-restricted-properties': 'off',
   },
 };
diff --git a/chrome/test/data/webui/a11y/accessibility_test.js b/chrome/test/data/webui/a11y/accessibility_test.js
index 5678d4d..7a91f31a 100644
--- a/chrome/test/data/webui/a11y/accessibility_test.js
+++ b/chrome/test/data/webui/a11y/accessibility_test.js
@@ -61,8 +61,9 @@
 
   return new Promise((resolve, reject) => {
     axe.run(context, options, (err, results) => {
-      if (err)
+      if (err) {
         reject(err);
+      }
 
       let filteredViolations = AccessibilityTest.filterViolations_(
           results.violations, testDef.violationFilter || {});
diff --git a/chrome/test/data/webui/about_invalidations_browsertest.js b/chrome/test/data/webui/about_invalidations_browsertest.js
index 36524e10..8d133c7d 100644
--- a/chrome/test/data/webui/about_invalidations_browsertest.js
+++ b/chrome/test/data/webui/about_invalidations_browsertest.js
@@ -87,15 +87,18 @@
       pattern2Test = pattern2Test &&
           (pattern2[cell] == oidTable.rows[row].cells[cell].textContent);
     }
-    if (pattern1Test)
+    if (pattern1Test) {
       expectEquals('greyed', oidTable.rows[row].className);
-    if (pattern2Test)
+    }
+    if (pattern2Test) {
       expectEquals('content', oidTable.rows[row].className);
+    }
 
     foundPattern1 = foundPattern1 || pattern1Test;
     foundPattern2 = foundPattern2 || pattern2Test;
-    if (foundPattern2)
+    if (foundPattern2) {
       expectTrue(foundPattern1, 'The entries were not ordererd');
+    }
   }
   expectTrue(foundPattern1 && foundPattern2, 'couldn\'t find both objects ids');
 });
diff --git a/chrome/test/data/webui/accessibility_audit_browsertest.js b/chrome/test/data/webui/accessibility_audit_browsertest.js
index 68feeff..7f94a5f 100644
--- a/chrome/test/data/webui/accessibility_audit_browsertest.js
+++ b/chrome/test/data/webui/accessibility_audit_browsertest.js
@@ -53,14 +53,16 @@
     var numAccessibilityWarnings = 0;
     for (var i = 0; i < accessibilityResults.length; i++) {
       var result = accessibilityResults[i];
-      if (result.rule.severity == axs.constants.Severity.Warning)
+      if (result.rule.severity == axs.constants.Severity.Warning) {
         numAccessibilityWarnings++;
-      else
+      } else {
         numAccessibilityErrors++;
+      }
     }
 
-    if (this.expectedErrors != null)
+    if (this.expectedErrors != null) {
       expectEquals(this.expectedErrors, numAccessibilityErrors);
+    }
     if (this.expectedWarnings != null) {
       expectEquals(this.expectedWarnings, numAccessibilityWarnings);
     }
@@ -157,8 +159,9 @@
   var realAudit = axs.Audit;
   var expectedInvocation = audit.expects(exactly(times)).run(ANYTHING);
   var willArgs = [];
-  for (var i = 0; i < times; i++)
+  for (var i = 0; i < times; i++) {
     willArgs.push(callFunction(realAudit.run, auditConfig));
+  }
   expectedInvocation.will.apply(expectedInvocation, willArgs);
   axs.Audit = audit.proxy();
   axs.Audit.createReport = realAudit.createReport;
diff --git a/chrome/test/data/webui/async_gen.js b/chrome/test/data/webui/async_gen.js
index dc08b3c..fe4a9e6 100644
--- a/chrome/test/data/webui/async_gen.js
+++ b/chrome/test/data/webui/async_gen.js
@@ -47,8 +47,9 @@
 
   /** @inheritDoc */
   preLoad: function() {
-    if (window.preLoadCount === undefined)
+    if (window.preLoadCount === undefined) {
       window.preLoadCount = 0;
+    }
     assertEquals(0, Number(window.preLoadCount++));
   },
 };
diff --git a/chrome/test/data/webui/bidichecker_tests.js b/chrome/test/data/webui/bidichecker_tests.js
index 4b908fd..e173404 100644
--- a/chrome/test/data/webui/bidichecker_tests.js
+++ b/chrome/test/data/webui/bidichecker_tests.js
@@ -103,13 +103,15 @@
   };
 
   var dir = isRTL ? 'RTL' : 'LTR';
-  if (!filters.hasOwnProperty(pageName))
-    pageName += '/';
   if (!filters.hasOwnProperty(pageName)) {
-    if (pageName.charAt(pageName.length - 2) == '/')
+    pageName += '/';
+  }
+  if (!filters.hasOwnProperty(pageName)) {
+    if (pageName.charAt(pageName.length - 2) == '/') {
       pageName = pageName.substr(0, pageName.length - 2);
-    else
+    } else {
       return globalFilters[dir];
+    }
   }
   if (filters.hasOwnProperty(pageName) &&
       filters[pageName].hasOwnProperty(dir)) {
diff --git a/chrome/test/data/webui/bluetooth_internals_browsertest.js b/chrome/test/data/webui/bluetooth_internals_browsertest.js
index ca41490..9e832234 100644
--- a/chrome/test/data/webui/bluetooth_internals_browsertest.js
+++ b/chrome/test/data/webui/bluetooth_internals_browsertest.js
@@ -565,10 +565,11 @@
      */
     function whenSnackbarShows(pendingSnackbar) {
       return new Promise(function(resolve) {
-        if (pendingSnackbar.classList.contains('open'))
+        if (pendingSnackbar.classList.contains('open')) {
           resolve();
-        else
+        } else {
           pendingSnackbar.addEventListener('showed', resolve);
+        }
       });
     }
 
@@ -734,8 +735,9 @@
           value = value[part];
         }
 
-        if (propName == 'isGattConnected')
+        if (propName == 'isGattConnected') {
           value = value ? 'Connected' : 'Not Connected';
+        }
 
         if (typeof(value) === 'boolean') {
           expectEquals(value, valueCell.classList.contains('checked'));
diff --git a/chrome/test/data/webui/certificate_viewer_dialog_test.js b/chrome/test/data/webui/certificate_viewer_dialog_test.js
index afe1b53..833c31e 100644
--- a/chrome/test/data/webui/certificate_viewer_dialog_test.js
+++ b/chrome/test/data/webui/certificate_viewer_dialog_test.js
@@ -186,10 +186,13 @@
 function getElementWithValue(tree) {
   for (var i = 0; i < tree.childNodes.length; i++) {
     var element = tree.childNodes[i];
-    if (element.detail && element.detail.payload && element.detail.payload.val)
+    if (element.detail && element.detail.payload &&
+        element.detail.payload.val) {
       return element;
-    if (element = getElementWithValue(element))
+    }
+    if (element = getElementWithValue(element)) {
       return element;
+    }
   }
   return null;
 }
diff --git a/chrome/test/data/webui/chromeos/cr_onc_strings.js b/chrome/test/data/webui/chromeos/cr_onc_strings.js
index 9731d807..38b5c566 100644
--- a/chrome/test/data/webui/chromeos/cr_onc_strings.js
+++ b/chrome/test/data/webui/chromeos/cr_onc_strings.js
@@ -98,22 +98,25 @@
  */
 CrOncTest.convertToManagedProperties = function(properties) {
   'use strict';
-  if (!properties)
+  if (!properties) {
     return undefined;
+  }
   var result = {};
   var keys = Object.keys(properties);
-  if (typeof properties != 'object')
+  if (typeof properties != 'object') {
     return {Active: properties};
+  }
   for (var i = 0; i < keys.length; ++i) {
     var k = keys[i];
     const unmanagedProperties = [
       'ConnectionState', 'GUID',
       /* ManagedCellularProperties.SIMLockStatus */ 'LockType', 'Source', 'Type'
     ];
-    if (unmanagedProperties.includes(k))
+    if (unmanagedProperties.includes(k)) {
       result[k] = properties[k];
-    else
+    } else {
       result[k] = this.convertToManagedProperties(properties[k]);
+    }
   }
   return result;
 };
diff --git a/chrome/test/data/webui/chromeos/fake_networking_private.js b/chrome/test/data/webui/chromeos/fake_networking_private.js
index c4776cdb..08566e3 100644
--- a/chrome/test/data/webui/chromeos/fake_networking_private.js
+++ b/chrome/test/data/webui/chromeos/fake_networking_private.js
@@ -151,8 +151,9 @@
       var devices = [];
       Object.keys(this.deviceStates_).forEach(function(type) {
         var state = this.deviceStates_[type];
-        if (state.State != '')
+        if (state.State != '') {
           devices.push(state);
+        }
       }.bind(this));
       callback(devices);
       this.methodCalled('getDeviceStates');
diff --git a/chrome/test/data/webui/cr_elements/cr_action_menu_test.js b/chrome/test/data/webui/cr_elements/cr_action_menu_test.js
index 5ada2a5..76800e7 100644
--- a/chrome/test/data/webui/cr_elements/cr_action_menu_test.js
+++ b/chrome/test/data/webui/cr_elements/cr_action_menu_test.js
@@ -45,8 +45,9 @@
   teardown(function() {
     document.body.style.direction = 'ltr';
 
-    if (dialog.open)
+    if (dialog.open) {
       menu.close();
+    }
   });
 
   function down() {
@@ -126,8 +127,9 @@
   });
 
   test('pressing enter when no focus', function() {
-    if (cr.isWindows || cr.isMac)
+    if (cr.isWindows || cr.isMac) {
       return testFocusAfterClosing('Enter');
+    }
 
     // First item is selected
     menu.showAt(dots);
diff --git a/chrome/test/data/webui/cr_elements/cr_dialog_test.js b/chrome/test/data/webui/cr_elements/cr_dialog_test.js
index 38af387..9a51e248 100644
--- a/chrome/test/data/webui/cr_elements/cr_dialog_test.js
+++ b/chrome/test/data/webui/cr_elements/cr_dialog_test.js
@@ -313,8 +313,9 @@
     // calls callback before MutationObserver does.
     const observer = new MutationObserver(function(changes) {
       // Only care about class mutations.
-      if (changes[0].attributeName != 'class')
+      if (changes[0].attributeName != 'class') {
         return;
+      }
 
       observerCount++;
       switch (observerCount) {
diff --git a/chrome/test/data/webui/cr_elements/cr_fingerprint_progress_arc_tests.js b/chrome/test/data/webui/cr_elements/cr_fingerprint_progress_arc_tests.js
index dd8b2b6..5939512 100644
--- a/chrome/test/data/webui/cr_elements/cr_fingerprint_progress_arc_tests.js
+++ b/chrome/test/data/webui/cr_elements/cr_fingerprint_progress_arc_tests.js
@@ -89,8 +89,9 @@
    * @param {!Array<Point>} listOfPoints
    */
   function assertListOfColorsEqual(expectedColor, listOfPoints) {
-    for (let point of listOfPoints)
+    for (let point of listOfPoints) {
       assertColorEquals(expectedColor, getRGBData(point));
+    }
   }
 
   test('TestDrawArc', function() {
diff --git a/chrome/test/data/webui/cr_elements/cr_toggle_test.js b/chrome/test/data/webui/cr_elements/cr_toggle_test.js
index 99ae018..bae42ff 100644
--- a/chrome/test/data/webui/cr_elements/cr_toggle_test.js
+++ b/chrome/test/data/webui/cr_elements/cr_toggle_test.js
@@ -65,8 +65,9 @@
    *     moveDirection is non-zero.
    */
   function triggerPointerDownMoveUpTapSequence(moveDirection, diff) {
-    if (window.getComputedStyle(toggle)['pointer-events'] === 'none')
+    if (window.getComputedStyle(toggle)['pointer-events'] === 'none') {
       return;
+    }
 
     // Simulate events in the same order they are fired by the browser.
     // Need to provide a valid |pointerId| for setPointerCapture() to not throw
diff --git a/chrome/test/data/webui/extensions/a11y/extensions_a11y_test.js b/chrome/test/data/webui/extensions/a11y/extensions_a11y_test.js
index 91e297c..a7db9e4 100644
--- a/chrome/test/data/webui/extensions/a11y/extensions_a11y_test.js
+++ b/chrome/test/data/webui/extensions/a11y/extensions_a11y_test.js
@@ -78,8 +78,9 @@
    * @private
    */
   static hasAncestor_(node, type) {
-    if (!node.parentElement)
+    if (!node.parentElement) {
       return false;
+    }
 
     return (node.parentElement.tagName.toLocaleLowerCase() == type) ||
         CrExtensionsA11yTest.hasAncestor_(node.parentElement, type);
diff --git a/chrome/test/data/webui/extensions/code_section_test.js b/chrome/test/data/webui/extensions/code_section_test.js
index cdd0908..af35d3d 100644
--- a/chrome/test/data/webui/extensions/code_section_test.js
+++ b/chrome/test/data/webui/extensions/code_section_test.js
@@ -70,10 +70,12 @@
           afterHighlight: '',
           message: 'Highlight message',
         };
-        for (let i = 0; i < beforeLineCount; i++)
+        for (let i = 0; i < beforeLineCount; i++) {
           code.beforeHighlight += 'a\n';
-        for (let i = 0; i < afterLineCount; i++)
+        }
+        for (let i = 0; i < afterLineCount; i++) {
           code.afterHighlight += 'a\n';
+        }
       }
 
       setCodeContent(0, 2000);
diff --git a/chrome/test/data/webui/extensions/test_util.js b/chrome/test/data/webui/extensions/test_util.js
index e9fa92d..1ab7897 100644
--- a/chrome/test/data/webui/extensions/test_util.js
+++ b/chrome/test/data/webui/extensions/test_util.js
@@ -79,8 +79,9 @@
     verify: function() {
       const missingEvents = [];
       for (const key in this.listeners_) {
-        if (!this.listeners_[key].satisfied)
+        if (!this.listeners_[key].satisfied) {
           missingEvents.push(key);
+        }
       }
       expectEquals(0, missingEvents.length, JSON.stringify(missingEvents));
     },
@@ -261,8 +262,9 @@
     function doSearch(node) {
       if (node.nodeType == Node.ELEMENT_NODE) {
         const matches = node.querySelectorAll(query);
-        for (let match of matches)
+        for (let match of matches) {
           elements.add(match);
+        }
       }
       let child = node.firstChild;
       while (child !== null) {
@@ -270,8 +272,9 @@
         child = child.nextSibling;
       }
       const shadowRoot = node.shadowRoot;
-      if (shadowRoot)
+      if (shadowRoot) {
         doSearch(shadowRoot);
+      }
     }
     doSearch(root);
     return Array.from(elements);
diff --git a/chrome/test/data/webui/inspect_ui_test.js b/chrome/test/data/webui/inspect_ui_test.js
index 91f5e83e..6bf5425 100644
--- a/chrome/test/data/webui/inspect_ui_test.js
+++ b/chrome/test/data/webui/inspect_ui_test.js
@@ -25,8 +25,9 @@
 
 function findByContentSubstring(elements, content, childSelector) {
   return Array.prototype.filter.call(elements, function(element) {
-    if (childSelector)
+    if (childSelector) {
       element = element.querySelector(childSelector);
+    }
     return element && element.textContent.indexOf(content) >= 0;
   })[0];
 }
diff --git a/chrome/test/data/webui/md_bookmarks/dnd_manager_test.js b/chrome/test/data/webui/md_bookmarks/dnd_manager_test.js
index b31575ffc..6e0231ab 100644
--- a/chrome/test/data/webui/md_bookmarks/dnd_manager_test.js
+++ b/chrome/test/data/webui/md_bookmarks/dnd_manager_test.js
@@ -23,8 +23,9 @@
   function getListItem(id) {
     const items = list.root.querySelectorAll('bookmarks-item');
     for (let i = 0; i < items.length; i++) {
-      if (items[i].itemId == id)
+      if (items[i].itemId == id) {
         return items[i];
+      }
     }
   }
 
diff --git a/chrome/test/data/webui/md_bookmarks/store_test.js b/chrome/test/data/webui/md_bookmarks/store_test.js
index 6f3a8794..9d50c9f 100644
--- a/chrome/test/data/webui/md_bookmarks/store_test.js
+++ b/chrome/test/data/webui/md_bookmarks/store_test.js
@@ -88,8 +88,9 @@
       },
 
       itemsChanged_: function(newItems, oldItems) {
-        if (oldItems)
+        if (oldItems) {
           this.hasChanged = true;
+        }
       },
     });
   });
diff --git a/chrome/test/data/webui/md_bookmarks/test_command_manager.js b/chrome/test/data/webui/md_bookmarks/test_command_manager.js
index 5b956cb..e626d06 100644
--- a/chrome/test/data/webui/md_bookmarks/test_command_manager.js
+++ b/chrome/test/data/webui/md_bookmarks/test_command_manager.js
@@ -25,8 +25,9 @@
    */
   commandManager.assertLastCommand = function(command, ids) {
     assertEquals(command, lastCommand);
-    if (ids)
+    if (ids) {
       assertDeepEquals(ids, normalizeIterable(lastCommandIds));
+    }
     lastCommand = null;
     lastCommandIds = null;
   };
diff --git a/chrome/test/data/webui/md_bookmarks/test_timer_proxy.js b/chrome/test/data/webui/md_bookmarks/test_timer_proxy.js
index 7229fcc..71c5cde2 100644
--- a/chrome/test/data/webui/md_bookmarks/test_timer_proxy.js
+++ b/chrome/test/data/webui/md_bookmarks/test_timer_proxy.js
@@ -23,10 +23,11 @@
        * @override
        */
       setTimeout(fn, delay) {
-        if (this.immediatelyResolveTimeouts)
+        if (this.immediatelyResolveTimeouts) {
           fn();
-        else
+        } else {
           this.activeTimeouts_.set(this.nextTimeoutId_, fn);
+        }
 
         return this.nextTimeoutId_++;
       }
diff --git a/chrome/test/data/webui/md_bookmarks/test_util.js b/chrome/test/data/webui/md_bookmarks/test_util.js
index c7a55292..7bf6f20c 100644
--- a/chrome/test/data/webui/md_bookmarks/test_util.js
+++ b/chrome/test/data/webui/md_bookmarks/test_util.js
@@ -39,8 +39,9 @@
     title: '',
   };
   if (config) {
-    for (const key in config)
+    for (const key in config) {
       newFolder[key] = config[key];
+    }
   }
   if (children.length) {
     for (let i = 0; i < children.length; i++) {
@@ -59,8 +60,9 @@
  */
 function removeChild(tree, index) {
   tree.children.splice(index, 1);
-  for (let i = index; i < tree.children.length; i++)
+  for (let i = index; i < tree.children.length; i++) {
     tree.children[i].index = i;
+  }
 }
 
 /**
@@ -76,8 +78,9 @@
     url: 'http://www.google.com/',
   };
   if (config) {
-    for (const key in config)
+    for (const key in config) {
       newItem[key] = config[key];
+    }
   }
   return newItem;
 }
@@ -122,8 +125,9 @@
   };
 
   if (config) {
-    for (const key in config)
+    for (const key in config) {
       props[key] = config[key];
+    }
   }
 
   if (cr.isMac && props.ctrlKey) {
@@ -134,8 +138,9 @@
   element.dispatchEvent(new MouseEvent('mousedown', props));
   element.dispatchEvent(new MouseEvent('mouseup', props));
   element.dispatchEvent(new MouseEvent(eventName, props));
-  if (config && config.detail == 2)
+  if (config && config.detail == 2) {
     element.dispatchEvent(new MouseEvent('dblclick', props));
+  }
 }
 
 /**
@@ -149,8 +154,9 @@
   let node;
   while (nodes.length) {
     node = nodes.pop();
-    if (node.itemId == id)
+    if (node.itemId == id) {
       return node;
+    }
 
     node.root.querySelectorAll('bookmarks-folder-node').forEach((x) => {
       nodes.unshift(x);
diff --git a/chrome/test/data/webui/md_history/history_list_test.js b/chrome/test/data/webui/md_history/history_list_test.js
index df3acaa..be3fba5c 100644
--- a/chrome/test/data/webui/md_history/history_list_test.js
+++ b/chrome/test/data/webui/md_history/history_list_test.js
@@ -338,8 +338,9 @@
   // TODO(calamity): Reenable this test after fixing flakiness.
   // See http://crbug.com/640862.
   test.skip('scrolling history list causes toolbar shadow to appear', () => {
-    for (let i = 0; i < 10; i++)
+    for (let i = 0; i < 10; i++) {
       app.historyResult(createHistoryInfo(), TEST_HISTORY_RESULTS);
+    }
     return PolymerTest.flushTasks()
         .then(function() {
           assertFalse(app.toolbarShadow_);
diff --git a/chrome/test/data/webui/md_history/history_metrics_test.js b/chrome/test/data/webui/md_history/history_metrics_test.js
index 039a4cc..d28e9f8 100644
--- a/chrome/test/data/webui/md_history/history_metrics_test.js
+++ b/chrome/test/data/webui/md_history/history_metrics_test.js
@@ -27,19 +27,22 @@
       recordHistogram: function(histogram, value, max) {
         assertTrue(value < max);
 
-        if (!(histogram in this.histogramMap))
+        if (!(histogram in this.histogramMap)) {
           this.histogramMap[histogram] = {};
+        }
 
-        if (!(value in this.histogramMap[histogram]))
+        if (!(value in this.histogramMap[histogram])) {
           this.histogramMap[histogram][value] = 0;
+        }
 
         this.histogramMap[histogram][value]++;
       },
 
       /** @override */
       recordAction: function(action) {
-        if (!(action in this.actionMap))
+        if (!(action in this.actionMap)) {
           this.actionMap[action] = 0;
+        }
 
         this.actionMap[action]++;
       },
diff --git a/chrome/test/data/webui/md_history/md_history_focus_test.js b/chrome/test/data/webui/md_history/md_history_focus_test.js
index 76763d6e..7d8df150 100644
--- a/chrome/test/data/webui/md_history/md_history_focus_test.js
+++ b/chrome/test/data/webui/md_history/md_history_focus_test.js
@@ -90,8 +90,9 @@
       assertNotEquals(field.$.searchInput, field.root.activeElement);
 
       let modifier = 'ctrl';
-      if (cr.isMac)
+      if (cr.isMac) {
         modifier = 'meta';
+      }
 
       MockInteractions.pressAndReleaseKeyOn(document.body, 70, modifier, 'f');
       assertTrue(field.showingSearch);
diff --git a/chrome/test/data/webui/md_history/test_util.js b/chrome/test/data/webui/md_history/test_util.js
index da9ec44..61ee884 100644
--- a/chrome/test/data/webui/md_history/test_util.js
+++ b/chrome/test/data/webui/md_history/test_util.js
@@ -43,8 +43,9 @@
  * @return {!HistoryEntry} An object representing a history entry.
  */
 function createHistoryEntry(timestamp, urlStr) {
-  if (typeof timestamp === 'string')
+  if (typeof timestamp === 'string') {
     timestamp += ' UTC';
+  }
 
   const d = new Date(timestamp);
   const url = new URL(urlStr);
@@ -108,15 +109,17 @@
  * @return {Promise}
  */
 function waitForEvent(element, eventName, predicate) {
-  if (!predicate)
+  if (!predicate) {
     predicate = function() {
       return true;
     };
+  }
 
   return new Promise(function(resolve) {
     const listener = function(e) {
-      if (!predicate(e))
+      if (!predicate(e)) {
         return;
+      }
 
       resolve();
       element.removeEventListener(eventName, listener);
@@ -148,8 +151,9 @@
 
 function disableLinkClicks() {
   document.addEventListener('click', function(e) {
-    if (e.defaultPrevented)
+    if (e.defaultPrevented) {
       return;
+    }
 
     const eventPath = e.path;
     let anchor = null;
@@ -163,8 +167,9 @@
       }
     }
 
-    if (!anchor)
+    if (!anchor) {
       return;
+    }
 
     e.preventDefault();
   });
diff --git a/chrome/test/data/webui/md_user_manager/control_bar_tests.js b/chrome/test/data/webui/md_user_manager/control_bar_tests.js
index 8272e9a..a7fae10 100644
--- a/chrome/test/data/webui/md_user_manager/control_bar_tests.js
+++ b/chrome/test/data/webui/md_user_manager/control_bar_tests.js
@@ -48,8 +48,9 @@
         return new Promise(function(resolve, reject) {
           // We expect to go to the 'create-profile' page.
           listenOnce(controlBarElement, 'change-page', function(event) {
-            if (event.detail.page == 'create-user-page')
+            if (event.detail.page == 'create-user-page') {
               resolve();
+            }
           });
 
           // Simulate clicking 'Create Profile'.
@@ -82,8 +83,9 @@
 
       teardown(function(done) {
         controlBarElement.remove();
-        if (errorDialogElement.$.dialog.open)
+        if (errorDialogElement.$.dialog.open) {
           errorDialogElement.$.dialog.close();
+        }
 
         // Allow asynchronous tasks to finish.
         setTimeout(done);
@@ -121,8 +123,9 @@
         return new Promise(function(resolve, reject) {
           // We expect to go to the 'create-profile' page.
           listenOnce(controlBarElement, 'change-page', function(event) {
-            if (event.detail.page == 'create-user-page')
+            if (event.detail.page == 'create-user-page') {
               resolve();
+            }
           });
 
           // Simulate clicking 'Create Profile'.
diff --git a/chrome/test/data/webui/md_user_manager/create_profile_tests.js b/chrome/test/data/webui/md_user_manager/create_profile_tests.js
index b47af5f..acd1409 100644
--- a/chrome/test/data/webui/md_user_manager/create_profile_tests.js
+++ b/chrome/test/data/webui/md_user_manager/create_profile_tests.js
@@ -98,8 +98,9 @@
         return new Promise(function(resolve, reject) {
           // Create was successful. We expect to leave the page.
           createProfileElement.addEventListener('change-page', function(event) {
-            if (event.detail.page == 'user-pods-page')
+            if (event.detail.page == 'user-pods-page') {
               resolve();
+            }
           });
 
           // Simulate clicking 'Create'.
diff --git a/chrome/test/data/webui/media_router/issue_banner_tests.js b/chrome/test/data/webui/media_router/issue_banner_tests.js
index 80fb9cc..e720e03 100644
--- a/chrome/test/data/webui/media_router/issue_banner_tests.js
+++ b/chrome/test/data/webui/media_router/issue_banner_tests.js
@@ -44,10 +44,11 @@
       // expected data.
       var checkDataFromEventFiring = function(issue, data, isDefault) {
         assertEquals(issue.id, data.detail.id);
-        if (isDefault)
+        if (isDefault) {
           assertEquals(issue.defaultActionType, data.detail.actionType);
-        else
+        } else {
           assertEquals(issue.secondaryActionType, data.detail.actionType);
+        }
         assertEquals(issue.helpPageId, data.detail.helpPageId);
       };
 
diff --git a/chrome/test/data/webui/media_router/media_router_container_test_base.js b/chrome/test/data/webui/media_router/media_router_container_test_base.js
index 21be80e..2cf7266 100644
--- a/chrome/test/data/webui/media_router/media_router_container_test_base.js
+++ b/chrome/test/data/webui/media_router/media_router_container_test_base.js
@@ -26,8 +26,9 @@
      *     should be visible.
      */
     var checkElementsVisibleWithId = function(elementIdList) {
-      for (var id of elementIdList)
+      for (var id of elementIdList) {
         checkElementVisibleWithId(true, id);
+      }
 
       for (id of hiddenCheckElementIdList) {
         if (!elementIdList.includes(id)) {
diff --git a/chrome/test/data/webui/media_router/media_router_header_tests.js b/chrome/test/data/webui/media_router/media_router_header_tests.js
index 5287e70..b439d25 100644
--- a/chrome/test/data/webui/media_router/media_router_header_tests.js
+++ b/chrome/test/data/webui/media_router/media_router_header_tests.js
@@ -53,12 +53,14 @@
       // Checks whether the elements specified in |elementIdList| are visible.
       // Checks whether all other elements are hidden.
       var checkElementsVisibleWithId = function(elementIdList) {
-        for (var i = 0; i < elementIdList.length; i++)
+        for (var i = 0; i < elementIdList.length; i++) {
           checkElementHidden(false, elementIdList[i]);
+        }
 
         for (var j = 0; j < hiddenCheckElementIdList.length; j++) {
-          if (elementIdList.indexOf(hiddenCheckElementIdList[j]) == -1)
+          if (elementIdList.indexOf(hiddenCheckElementIdList[j]) == -1) {
             checkElementHidden(true, hiddenCheckElementIdList[j]);
+          }
         }
       };
 
diff --git a/chrome/test/data/webui/mocha_adapter.js b/chrome/test/data/webui/mocha_adapter.js
index 2f0632e..504e8ca 100644
--- a/chrome/test/data/webui/mocha_adapter.js
+++ b/chrome/test/data/webui/mocha_adapter.js
@@ -38,8 +38,9 @@
     if (err.stack) {
       var stack = err.stack.split('\n');
       for (var i = 0; i < stack.length; i++) {
-        if (stack[i].indexOf('mocha.js:') == -1)
+        if (stack[i].indexOf('mocha.js:') == -1) {
           message += stack[i] + '\n';
+        }
       }
     } else {
       message += err.toString();
@@ -51,10 +52,11 @@
   // Report the results to the test API.
   runner.on('end', function() {
     if (failures == 0) {
-      if (passes > 0)
+      if (passes > 0) {
         testDone();
-      else
+      } else {
         testDone([false, 'Failure: Mocha ran, but no mocha tests were run!']);
+      }
       return;
     }
     testDone([
diff --git a/chrome/test/data/webui/mock_controller.js b/chrome/test/data/webui/mock_controller.js
index d51bf19..6405a81 100644
--- a/chrome/test/data/webui/mock_controller.js
+++ b/chrome/test/data/webui/mock_controller.js
@@ -81,8 +81,9 @@
    */
   verifyMock: function() {
     var errorMessage = 'Number of method calls did not match expectation.';
-    if (this.functionName)
+    if (this.functionName) {
       errorMessage = 'Error in ' + this.functionName + ':\n' + errorMessage;
+    }
     assertEquals(this.expectations_.length, this.calls_.length, errorMessage);
     for (var i = 0; i < this.expectations_.length; i++) {
       this.validateCall(i, this.expectations_[i], this.calls_[i]);
diff --git a/chrome/test/data/webui/mock_timer.js b/chrome/test/data/webui/mock_timer.js
index 96a61fd..347d096 100644
--- a/chrome/test/data/webui/mock_timer.js
+++ b/chrome/test/data/webui/mock_timer.js
@@ -184,20 +184,23 @@
   fireElapsedCallbacks_: function() {
     while (this.schedule_.length > 0) {
       var when = this.schedule_[this.schedule_.length - 1].when;
-      if (when > this.until_)
+      if (when > this.until_) {
         break;
+      }
 
       var task = this.schedule_.pop();
       var details = this.timers_[task.key];
-      if (!details)
-        continue;  // Cancelled task.
+      if (!details) {
+        continue;
+      }  // Cancelled task.
 
       this.now_ = when;
       details.callback.apply(window);
-      if (details.repeats)
+      if (details.repeats) {
         this.scheduleTask_(details);
-      else
+      } else {
         this.clearTimeout_(details.key);
+      }
     }
     this.now_ = this.until_;
   },
diff --git a/chrome/test/data/webui/net_internals/domain_security_policy_view.js b/chrome/test/data/webui/net_internals/domain_security_policy_view.js
index 7c024fc..ac42d4f 100644
--- a/chrome/test/data/webui/net_internals/domain_security_policy_view.js
+++ b/chrome/test/data/webui/net_internals/domain_security_policy_view.js
@@ -42,8 +42,9 @@
    */
   onQueryResult_: function(result) {
     // Ignore results after |this| is finished.
-    if (this.isDone())
+    if (this.isDone()) {
       return;
+    }
 
     expectEquals(this.domain_, $(this.inputId_).value);
 
diff --git a/chrome/test/data/webui/net_internals/net_internals_test.js b/chrome/test/data/webui/net_internals/net_internals_test.js
index 6f75299c..fb29900 100644
--- a/chrome/test/data/webui/net_internals/net_internals_test.js
+++ b/chrome/test/data/webui/net_internals/net_internals_test.js
@@ -130,8 +130,9 @@
    * @return {node} The tbody node, or null.
    */
   NetInternalsTest.getTbodyDescendent = function(ancestorId) {
-    if ($(ancestorId).nodeName == 'TBODY')
+    if ($(ancestorId).nodeName == 'TBODY') {
       return $(ancestorId);
+    }
     // The tbody element of the first styled table in |parentId|.
     return document.querySelector('#' + ancestorId + ' tbody');
   };
@@ -146,12 +147,14 @@
   NetInternalsTest.getTbodyNumRows = function(ancestorId) {
     // The tbody element of the first styled table in |parentId|.
     var tbody = NetInternalsTest.getTbodyDescendent(ancestorId);
-    if (!tbody)
+    if (!tbody) {
       return -1;
+    }
     var visibleChildren = 0;
     for (var i = 0; i < tbody.children.length; ++i) {
-      if (NetInternalsTest.nodeIsVisible(tbody.children[i]))
+      if (NetInternalsTest.nodeIsVisible(tbody.children[i])) {
         ++visibleChildren;
+      }
     }
     return visibleChildren;
   };
@@ -182,8 +185,9 @@
     var currentChild = tbody.children[0];
     while (currentChild) {
       if (NetInternalsTest.nodeIsVisible(currentChild)) {
-        if (row == 0)
+        if (row == 0) {
           return currentChild.children[column].innerText;
+        }
         --row;
       }
       currentChild = currentChild.nextElementSibling;
@@ -331,8 +335,9 @@
       expectEquals(
           tabVisibilityState[hash], NetInternalsTest.tabLinkIsVisible(tabId),
           tabId + ' visibility state is unexpected.');
-      if (tourTabs && tabVisibilityState[hash])
+      if (tourTabs && tabVisibilityState[hash]) {
         NetInternalsTest.switchToView(hash);
+      }
       tabCount++;
     }
 
@@ -340,8 +345,9 @@
     var tabSwitcher = MainView.getInstance().tabSwitcher();
     var tabIdToView = tabSwitcher.getAllTabViews();
     var expectedTabCount = 0;
-    for (tabId in tabIdToView)
+    for (tabId in tabIdToView) {
       expectedTabCount++;
+    }
     expectEquals(tabCount, expectedTabCount);
   };
 
@@ -409,8 +415,9 @@
         nextTask.start.apply(nextTask, argArray);
       } else {
         this.isRunning_ = false;
-        if (this.endTestWhenDone_)
+        if (this.endTestWhenDone_) {
           testDone();
+        }
       }
     }
   };
diff --git a/chrome/test/data/webui/ntp4.js b/chrome/test/data/webui/ntp4.js
index 50c1322..e3bbfd06 100644
--- a/chrome/test/data/webui/ntp4.js
+++ b/chrome/test/data/webui/ntp4.js
@@ -37,18 +37,20 @@
   assertEquals(8, mostVisited.length, 'There should be 8 most visited tiles.');
 
   var apps = document.querySelectorAll('.app');
-  if (loadTimeData.getBoolean('showApps'))
+  if (loadTimeData.getBoolean('showApps')) {
     assertGE(apps.length, 1, 'There should be at least one app.');
-  else
+  } else {
     assertEquals(0, apps.length, 'There should be no apps.');
+  }
 });
 
 TEST_F('NTP4WebUITest', 'DISABLED_NTPHasNavDots', function() {
   var navDots = document.querySelectorAll('.dot');
-  if (loadTimeData.getBoolean('showApps'))
+  if (loadTimeData.getBoolean('showApps')) {
     assertGE(navDots.length, 2, 'There should be at least two navdots.');
-  else
+  } else {
     assertEquals(1, navDots.length, 'There should be exactly one navdot.');
+  }
 });
 
 // http://crbug.com/118514
diff --git a/chrome/test/data/webui/polymer_browser_test_base.js b/chrome/test/data/webui/polymer_browser_test_base.js
index 4c5a751..95b2baa 100644
--- a/chrome/test/data/webui/polymer_browser_test_base.js
+++ b/chrome/test/data/webui/polymer_browser_test_base.js
@@ -74,10 +74,12 @@
             'is already registered, perhaps you have loaded a script twice. ' +
             'Incorrect resource URLs can redirect to base WebUI pages; make ' +
             'sure the following URLs are correct and unique:\n';
-        for (var i = 0; i < PolymerTest.importUrls_.length; i++)
+        for (var i = 0; i < PolymerTest.importUrls_.length; i++) {
           msg += '  ' + PolymerTest.importUrls_[i] + '\n';
-        for (var i = 0; i < PolymerTest.scriptUrls_.length; i++)
+        }
+        for (var i = 0; i < PolymerTest.scriptUrls_.length; i++) {
           msg += '  ' + PolymerTest.scriptUrls_[i] + '\n';
+        }
         console.error(msg);
 
         // Mocha will handle the error.
@@ -112,8 +114,9 @@
   e.querySelectorAll('* /deep/ iron-icon').forEach(function(icon) {
     // Early return if the src is set instead of the icon, since the tests
     // below will not work correctly in this case.
-    if (icon.src && !icon.icon)
+    if (icon.src && !icon.icon) {
       return;
+    }
 
     // If the icon isn't set (or is set to ''), then don't test this. Having no
     // set icon is valid for cases when we don't want to display anything.
@@ -175,8 +178,9 @@
   var vulcanizeDiv =
       document.querySelector('body > div[hidden][by-polymer-bundler]');
   document.body.innerHTML = '';
-  if (vulcanizeDiv)
+  if (vulcanizeDiv) {
     document.body.appendChild(vulcanizeDiv);
+  }
 };
 
 /**
@@ -184,8 +188,9 @@
  */
 PolymerTest.getLibraries = function(basePath) {
   // Ensure basePath ends in '/'.
-  if (basePath.length && basePath[basePath.length - 1] != '/')
+  if (basePath.length && basePath[basePath.length - 1] != '/') {
     basePath += '/';
+  }
 
   return PolymerTest.prototype.extraLibraries.map(function(library) {
     return basePath + library;
diff --git a/chrome/test/data/webui/print_preview/custom_margins_test.js b/chrome/test/data/webui/print_preview/custom_margins_test.js
index b49be8334..16083049 100644
--- a/chrome/test/data/webui/print_preview/custom_margins_test.js
+++ b/chrome/test/data/webui/print_preview/custom_margins_test.js
@@ -144,8 +144,9 @@
      * @param {number} end The ending position for the control in pixels.
      */
     function dragControl(control, start, end) {
-      if (window.getComputedStyle(control)['pointer-events'] === 'none')
+      if (window.getComputedStyle(control)['pointer-events'] === 'none') {
         return;
+      }
 
       let xStart = 0;
       let yStart = 0;
diff --git a/chrome/test/data/webui/print_preview/destination_select_test.js b/chrome/test/data/webui/print_preview/destination_select_test.js
index d775850..77afefc 100644
--- a/chrome/test/data/webui/print_preview/destination_select_test.js
+++ b/chrome/test/data/webui/print_preview/destination_select_test.js
@@ -58,8 +58,9 @@
       document.body.appendChild(page);
 
       const promises = [nativeLayer.whenCalled('getInitialSettings')];
-      if (!opt_expectPrinterFailure)
+      if (!opt_expectPrinterFailure) {
         promises.push(nativeLayer.whenCalled('getPrinterCapabilities'));
+      }
       return Promise.all(promises);
     }
 
@@ -177,8 +178,9 @@
             const reportedPrinters = page.destinationStore_.destinations();
             assertEquals(2, reportedPrinters.length);
             destinations.forEach((destination, index) => {
-              if (destination.id == 'ID1')
+              if (destination.id == 'ID1') {
                 return;
+              }
 
               assertFalse(reportedPrinters.some(p => p.id == destination.id));
             });
diff --git a/chrome/test/data/webui/print_preview/link_container_test.js b/chrome/test/data/webui/print_preview/link_container_test.js
index 0d4f1649..afe4006 100644
--- a/chrome/test/data/webui/print_preview/link_container_test.js
+++ b/chrome/test/data/webui/print_preview/link_container_test.js
@@ -77,15 +77,17 @@
       };
 
       validateLinkState(systemDialogLink, false);
-      if (cr.isMac)
+      if (cr.isMac) {
         validateLinkState(openInPreviewLink, false);
+      }
 
       // Set disabled to true, indicating that there is a validation error or
       // printer error.
       linkContainer.disabled = true;
       validateLinkState(systemDialogLink, cr.isWindows);
-      if (cr.isMac)
+      if (cr.isMac) {
         validateLinkState(openInPreviewLink, true);
+      }
     });
 
     /**
diff --git a/chrome/test/data/webui/print_preview/native_layer_stub.js b/chrome/test/data/webui/print_preview/native_layer_stub.js
index c06ca7c..7e11649 100644
--- a/chrome/test/data/webui/print_preview/native_layer_stub.js
+++ b/chrome/test/data/webui/print_preview/native_layer_stub.js
@@ -105,15 +105,17 @@
       this.methodCalled(
           'getPreview', {printTicket: printTicket, pageCount: pageCount});
       const printTicketParsed = JSON.parse(printTicket);
-      if (printTicketParsed.deviceName == this.badPrinterId_)
+      if (printTicketParsed.deviceName == this.badPrinterId_) {
         return Promise.reject('SETTINGS_INVALID');
+      }
       const pageRanges = printTicketParsed.pageRange;
       const requestId = printTicketParsed.requestID;
       if (pageRanges.length == 0) {  // assume full length document, 1 page.
         cr.webUIListenerCallback(
             'page-count-ready', this.pageCount_, requestId, 100);
-        for (let i = 0; i < this.pageCount_; i++)
+        for (let i = 0; i < this.pageCount_; i++) {
           cr.webUIListenerCallback('page-preview-ready', i, 0, requestId);
+        }
       } else {
         const pages = pageRanges.reduce(function(soFar, range) {
           for (let page = range.from; page <= range.to; page++) {
@@ -142,8 +144,9 @@
       this.methodCalled(
           'getPrinterCapabilities',
           {destinationId: printerId, printerType: type});
-      if (type != print_preview.PrinterType.LOCAL_PRINTER)
+      if (type != print_preview.PrinterType.LOCAL_PRINTER) {
         return Promise.reject();
+      }
       return this.localDestinationCapabilities_.get(printerId) ||
           Promise.reject();
     }
diff --git a/chrome/test/data/webui/print_preview/plugin_stub.js b/chrome/test/data/webui/print_preview/plugin_stub.js
index 808bc11..e4044b2 100644
--- a/chrome/test/data/webui/print_preview/plugin_stub.js
+++ b/chrome/test/data/webui/print_preview/plugin_stub.js
@@ -83,8 +83,9 @@
      * @param {number} index The preview index.
      */
     loadPreviewPage(previewUid, pageIndex, index) {
-      if (this.loadCallback_)
+      if (this.loadCallback_) {
         this.loadCallback_(true);
+      }
     }
   }
 
diff --git a/chrome/test/data/webui/print_preview/preview_generation_test.js b/chrome/test/data/webui/print_preview/preview_generation_test.js
index 99dcd1f..c0b00ef 100644
--- a/chrome/test/data/webui/print_preview/preview_generation_test.js
+++ b/chrome/test/data/webui/print_preview/preview_generation_test.js
@@ -65,8 +65,9 @@
             nativeLayer.whenCalled('getPrinterCapabilities'),
           ])
           .then(function() {
-            if (!page.documentInfo_.isModifiable)
+            if (!page.documentInfo_.isModifiable) {
               page.documentInfo_.updateFitToPageScaling(98);
+            }
             page.documentInfo_.updatePageCount(3);
             return nativeLayer.whenCalled('getPreview');
           });
diff --git a/chrome/test/data/webui/print_preview/print_preview_test_utils.js b/chrome/test/data/webui/print_preview/print_preview_test_utils.js
index 11286e8..a6a03d53 100644
--- a/chrome/test/data/webui/print_preview/print_preview_test_utils.js
+++ b/chrome/test/data/webui/print_preview/print_preview_test_utils.js
@@ -99,8 +99,9 @@
       numSettings, printerId, opt_printerName) {
     const template =
         print_preview_test_utils.getCddTemplate(printerId, opt_printerName);
-    if (numSettings < 1)
+    if (numSettings < 1) {
       return template;
+    }
 
     template.capabilities.printer.vendor_capability = [{
       display_name: 'Print Area',
@@ -115,8 +116,9 @@
       },
     }];
 
-    if (numSettings < 2)
+    if (numSettings < 2) {
       return template;
+    }
 
     // Add new capability.
     template.capabilities.printer.vendor_capability.push({
@@ -132,8 +134,9 @@
       }
     });
 
-    if (numSettings < 3)
+    if (numSettings < 3) {
       return template;
+    }
 
     template.capabilities.printer.vendor_capability.push({
       display_name: 'Watermark',
diff --git a/chrome/test/data/webui/print_preview/restore_state_test.js b/chrome/test/data/webui/print_preview/restore_state_test.js
index f031fb2..4d0cce8 100644
--- a/chrome/test/data/webui/print_preview/restore_state_test.js
+++ b/chrome/test/data/webui/print_preview/restore_state_test.js
@@ -260,8 +260,9 @@
           .then(function() {
             // Set all the settings sections.
             testData.forEach((testValue, index) => {
-              if (index == testData.length - 1)
+              if (index == testData.length - 1) {
                 nativeLayer.resetResolver('saveAppState');
+              }
               page.$$(testValue.section)
                   .setSetting(testValue.settingName, testValue.value);
             });
diff --git a/chrome/test/data/webui/print_preview/settings_section_test.js b/chrome/test/data/webui/print_preview/settings_section_test.js
index 4175687..9a713d8 100644
--- a/chrome/test/data/webui/print_preview/settings_section_test.js
+++ b/chrome/test/data/webui/print_preview/settings_section_test.js
@@ -75,8 +75,9 @@
     function initDocumentInfo(isPdf, hasSelection) {
       const info = new print_preview.DocumentInfo();
       info.init(!isPdf, 'title', hasSelection);
-      if (isPdf)
+      if (isPdf) {
         info.updateFitToPageScaling(98);
+      }
       info.updatePageCount(3);
       page.set('documentInfo_', info);
       Polymer.dom.flush();
diff --git a/chrome/test/data/webui/settings/a11y/settings_accessibility_test.js b/chrome/test/data/webui/settings/a11y/settings_accessibility_test.js
index a9111e1..da11a7ab 100644
--- a/chrome/test/data/webui/settings/a11y/settings_accessibility_test.js
+++ b/chrome/test/data/webui/settings/a11y/settings_accessibility_test.js
@@ -38,8 +38,9 @@
     return nodeResult.element.hasAttribute('aria-active-attribute');
   },
   'button-name': function(nodeResult) {
-    if (nodeResult.element.classList.contains('icon-expand-more'))
+    if (nodeResult.element.classList.contains('icon-expand-more')) {
       return true;
+    }
 
     // Ignore the <button> residing within cr-toggle and cr-checkbox, which has
     // tabindex -1 anyway.
diff --git a/chrome/test/data/webui/settings/about_page_tests.js b/chrome/test/data/webui/settings/about_page_tests.js
index a9d2244f..8f8454d 100644
--- a/chrome/test/data/webui/settings/about_page_tests.js
+++ b/chrome/test/data/webui/settings/about_page_tests.js
@@ -19,8 +19,9 @@
             'getHasEndOfLife', 'refreshTPMFirmwareUpdateStatus', 'setChannel');
       }
 
-      if (cr.isMac)
+      if (cr.isMac) {
         methodNames.push('promoteUpdater');
+      }
 
       super(methodNames);
 
diff --git a/chrome/test/data/webui/settings/advanced_page_browsertest.js b/chrome/test/data/webui/settings/advanced_page_browsertest.js
index 4f61cd42..5bf9e361 100644
--- a/chrome/test/data/webui/settings/advanced_page_browsertest.js
+++ b/chrome/test/data/webui/settings/advanced_page_browsertest.js
@@ -43,8 +43,9 @@
     test('advanced pages', function() {
       const page = self.basicPage;
       let sections = ['privacy', 'languages', 'downloads', 'reset'];
-      if (cr.isChromeOS)
+      if (cr.isChromeOS) {
         sections = sections.concat(['dateTime', 'a11y']);
+      }
 
       for (let i = 0; i < sections.length; i++) {
         const section = self.getSection(page, sections[i]);
diff --git a/chrome/test/data/webui/settings/autofill_section_test.js b/chrome/test/data/webui/settings/autofill_section_test.js
index 74e70bb..1ef1c83 100644
--- a/chrome/test/data/webui/settings/autofill_section_test.js
+++ b/chrome/test/data/webui/settings/autofill_section_test.js
@@ -82,7 +82,9 @@
       section.address = address;
       document.body.appendChild(section);
       test_util.eventToPromise('on-update-address-wrapper', section)
-        .then(function() { resolve(section); });
+          .then(function() {
+            resolve(section);
+          });
     });
   }
 
@@ -124,10 +126,11 @@
 
         function loop() {
           const item = items[index++];
-          if (item)
+          if (item) {
             loopBody(item).then(loop);
-          else
+          } else {
             resolve();
+          }
         }
 
         loop();
diff --git a/chrome/test/data/webui/settings/basic_page_browsertest.js b/chrome/test/data/webui/settings/basic_page_browsertest.js
index 4dca1303..d77abd5 100644
--- a/chrome/test/data/webui/settings/basic_page_browsertest.js
+++ b/chrome/test/data/webui/settings/basic_page_browsertest.js
@@ -56,8 +56,9 @@
           // All the sections must be hidden by the TestSearchManager, just like
           // the real SearchManager. Otherwise, the bug doesn't manifest.
           const sections = page.parentNode.querySelectorAll('settings-section');
-          for (let section of sections)
+          for (let section of sections) {
             section.hiddenBySearch = !!text;
+          }
 
           this.searchRequest_.resolver.resolve(this.searchRequest_);
           this.methodCalled('search', text);
@@ -77,10 +78,11 @@
       const page = self.basicPage;
       let sections = ['appearance', 'onStartup', 'people', 'search'];
       expectTrue(!!self.getSection(page, 'appearance'));
-      if (!cr.isChromeOS)
+      if (!cr.isChromeOS) {
         sections.push('defaultBrowser');
-      else
+      } else {
         sections = sections.concat(['internet', 'bluetooth', 'device']);
+      }
 
       for (let i = 0; i < sections.length; i++) {
         const section = self.getSection(page, sections[i]);
diff --git a/chrome/test/data/webui/settings/date_time_page_tests.js b/chrome/test/data/webui/settings/date_time_page_tests.js
index bc3b5499..6c6e0cd 100644
--- a/chrome/test/data/webui/settings/date_time_page_tests.js
+++ b/chrome/test/data/webui/settings/date_time_page_tests.js
@@ -238,8 +238,9 @@
 
     const checkButton = getAutodetectOnButton(dateTime);
     const checkButtonChecked = checkButton ? checkButton.checked : false;
-    if (!managed)
+    if (!managed) {
       assertEquals(autoDetect, checkButtonChecked);
+    }
   }
 
   function verifyTimeZonesPopulated(populated) {
@@ -249,10 +250,11 @@
 
     const dropdown =
         userTimezoneDropdown ? userTimezoneDropdown : systemTimezoneDropdown;
-    if (populated)
+    if (populated) {
       assertEquals(fakeTimeZones.length, dropdown.menuOptions.length);
-    else
+    } else {
       assertEquals(1, dropdown.menuOptions.length);
+    }
   }
 
   function updatePolicy(dateTime, managed, valueFromPolicy) {
diff --git a/chrome/test/data/webui/settings/device_page_tests.js b/chrome/test/data/webui/settings/device_page_tests.js
index 94240e7..a800d400 100644
--- a/chrome/test/data/webui/settings/device_page_tests.js
+++ b/chrome/test/data/webui/settings/device_page_tests.js
@@ -97,8 +97,9 @@
         app.preferred = app.value == appId;
       });
 
-      if (changed)
+      if (changed) {
         this.scheduleLockScreenAppsUpdated_();
+      }
     },
 
     /** @override */
diff --git a/chrome/test/data/webui/settings/fake_bluetooth.js b/chrome/test/data/webui/settings/fake_bluetooth.js
index d65b09d..5497f17 100644
--- a/chrome/test/data/webui/settings/fake_bluetooth.js
+++ b/chrome/test/data/webui/settings/fake_bluetooth.js
@@ -44,11 +44,13 @@
 
     /** @param {!Array<!chrome.bluetooth.Device>} devices */
     setDevicesForTest: function(devices) {
-      for (const d of this.devices)
+      for (const d of this.devices) {
         this.onDeviceRemoved.callListeners(d);
+      }
       this.devices = devices.slice();
-      for (const d of this.devices)
+      for (const d of this.devices) {
         this.onDeviceAdded.callListeners(d);
+      }
     },
 
     /**
@@ -86,8 +88,9 @@
 
     /** @override */
     getDevices: function(opt_filter, opt_callback) {
-      if (opt_callback)
+      if (opt_callback) {
         opt_callback(this.devices.slice());
+      }
     },
 
     /** @override */
diff --git a/chrome/test/data/webui/settings/fake_bluetooth_private.js b/chrome/test/data/webui/settings/fake_bluetooth_private.js
index 6ed649ba..e72b439 100644
--- a/chrome/test/data/webui/settings/fake_bluetooth_private.js
+++ b/chrome/test/data/webui/settings/fake_bluetooth_private.js
@@ -25,15 +25,17 @@
     /** @override */
     setAdapterState: function(state, opt_callback) {
       this.bluetoothApi_.setAdapterState(state);
-      if (opt_callback)
+      if (opt_callback) {
         opt_callback();
+      }
     },
 
     /** @override */
     setPairingResponse: function(options, opt_callback) {
       this.pairingResponses_[options.device.address] = options;
-      if (opt_callback)
+      if (opt_callback) {
         opt_callback();
+      }
     },
 
     /** @override */
@@ -53,8 +55,9 @@
       device.paired = true;
       device.connecting = true;
       this.bluetoothApi_.updateDeviceForTest(device);
-      if (opt_callback)
+      if (opt_callback) {
         opt_callback(chrome.bluetoothPrivate.ConnectResultType.IN_PROGRESS);
+      }
     },
 
     /** @override */
diff --git a/chrome/test/data/webui/settings/fake_language_settings_private.js b/chrome/test/data/webui/settings/fake_language_settings_private.js
index 5ac7f087..e654b21f 100644
--- a/chrome/test/data/webui/settings/fake_language_settings_private.js
+++ b/chrome/test/data/webui/settings/fake_language_settings_private.js
@@ -171,8 +171,9 @@
     enableLanguage(languageCode) {
       let languageCodes = this.settingsPrefs_.prefs.intl.accept_languages.value;
       const languages = languageCodes.split(',');
-      if (languages.indexOf(languageCode) != -1)
+      if (languages.indexOf(languageCode) != -1) {
         return;
+      }
       languages.push(languageCode);
       languageCodes = languages.join(',');
       this.settingsPrefs_.set(
@@ -191,8 +192,9 @@
       let languageCodes = this.settingsPrefs_.prefs.intl.accept_languages.value;
       const languages = languageCodes.split(',');
       const index = languages.indexOf(languageCode);
-      if (index == -1)
+      if (index == -1) {
         return;
+      }
       languages.splice(index, 1);
       languageCodes = languages.join(',');
       this.settingsPrefs_.set(
@@ -215,13 +217,15 @@
           this.settingsPrefs_.prefs.translate_blocked_languages.value.indexOf(
               languageCode);
       if (enable) {
-        if (index == -1)
+        if (index == -1) {
           return;
+        }
         this.settingsPrefs_.splice(
             'prefs.translate_blocked_languages.value', index, 1);
       } else {
-        if (index != -1)
+        if (index != -1) {
           return;
+        }
         this.settingsPrefs_.push(
             'prefs.translate_blocked_languages.value', languageCode);
       }
@@ -239,21 +243,24 @@
       const index = languages.indexOf(languageCode);
 
       if (moveType == chrome.languageSettingsPrivate.MoveType.TOP) {
-        if (index < 1)
+        if (index < 1) {
           return;
+        }
 
         languages.splice(index, 1);
         languages.unshift(languageCode);
       } else if (moveType == chrome.languageSettingsPrivate.MoveType.UP) {
-        if (index < 1)
+        if (index < 1) {
           return;
+        }
 
         let temp = languages[index - 1];
         languages[index - 1] = languageCode;
         languages[index] = temp;
       } else if (moveType == chrome.languageSettingsPrivate.MoveType.DOWN) {
-        if (index == -1 || index == languages.length - 1)
+        if (index == -1 || index == languages.length - 1) {
           return;
+        }
 
         let temp = languages[index + 1];
         languages[index + 1] = languageCode;
@@ -319,8 +326,9 @@
      *     callback
      */
     getInputMethodLists(callback) {
-      if (!cr.isChromeOS)
+      if (!cr.isChromeOS) {
         assertNotReached();
+      }
       callback({
         componentExtensionImes:
             JSON.parse(JSON.stringify(this.componentExtensionImes)),
diff --git a/chrome/test/data/webui/settings/fake_quick_unlock_private.js b/chrome/test/data/webui/settings/fake_quick_unlock_private.js
index be53f27..f60553f 100644
--- a/chrome/test/data/webui/settings/fake_quick_unlock_private.js
+++ b/chrome/test/data/webui/settings/fake_quick_unlock_private.js
@@ -72,8 +72,9 @@
         this.lockScreenEnabled = enabled;
         clearError_();
       }
-      if (onComplete)
+      if (onComplete) {
         onComplete();
+      }
     },
 
     /**
@@ -137,8 +138,9 @@
         errors.push(chrome.quickUnlockPrivate.CredentialProblem.TOO_LONG);
       }
 
-      if (!!credential && TEST_WEAK_PINS.includes(credential))
+      if (!!credential && TEST_WEAK_PINS.includes(credential)) {
         warnings.push(chrome.quickUnlockPrivate.CredentialProblem.TOO_WEAK);
+      }
 
       message.errors = errors;
       message.warnings = warnings;
diff --git a/chrome/test/data/webui/settings/fake_quick_unlock_uma.js b/chrome/test/data/webui/settings/fake_quick_unlock_uma.js
index 1a4f49b..50846749 100644
--- a/chrome/test/data/webui/settings/fake_quick_unlock_uma.js
+++ b/chrome/test/data/webui/settings/fake_quick_unlock_uma.js
@@ -12,8 +12,9 @@
    */
   function FakeQuickUnlockUma() {
     this.histogram = {};
-    for (const key in LockScreenProgress)
+    for (const key in LockScreenProgress) {
       this.histogram[LockScreenProgress[key]] = 0;
+    }
   }
 
   FakeQuickUnlockUma.prototype = {
@@ -22,8 +23,9 @@
      * @param {LockScreenProgress} key
      */
     recordProgress: function(key) {
-      if (!(key in this.histogram))
+      if (!(key in this.histogram)) {
         this.histogram[key] = 0;
+      }
       this.histogram[key]++;
     },
 
diff --git a/chrome/test/data/webui/settings/fake_settings_private.js b/chrome/test/data/webui/settings/fake_settings_private.js
index def118c..d5bc7cf5 100644
--- a/chrome/test/data/webui/settings/fake_settings_private.js
+++ b/chrome/test/data/webui/settings/fake_settings_private.js
@@ -24,10 +24,12 @@
   function FakeSettingsPrivate(opt_initialPrefs) {
     this.prefs = {};
 
-    if (!opt_initialPrefs)
+    if (!opt_initialPrefs) {
       return;
-    for (const pref of opt_initialPrefs)
+    }
+    for (const pref of opt_initialPrefs) {
       this.addPref_(pref.type, pref.key, pref.value);
+    }
   }
 
   FakeSettingsPrivate.prototype = {
@@ -37,8 +39,9 @@
     getAllPrefs: function(callback) {
       // Send a copy of prefs to keep our internal state private.
       const prefs = [];
-      for (const key in this.prefs)
+      for (const key in this.prefs) {
         prefs.push(deepCopy(this.prefs[key]));
+      }
 
       // Run the callback asynchronously to test that the prefs aren't actually
       // used before they become available.
@@ -63,8 +66,9 @@
       callback(true);
 
       // Like chrome.settingsPrivate, send a notification when prefs change.
-      if (changed)
+      if (changed) {
         this.sendPrefChanges([{key: key, value: deepCopy(value)}]);
+      }
     },
 
     getPref: function(key, callback) {
diff --git a/chrome/test/data/webui/settings/fake_system_display.js b/chrome/test/data/webui/settings/fake_system_display.js
index 5b4b8cc..1e9f4e3 100644
--- a/chrome/test/data/webui/settings/fake_system_display.js
+++ b/chrome/test/data/webui/settings/fake_system_display.js
@@ -62,8 +62,9 @@
       }
 
       if (info.mirroringSourceId != undefined) {
-        for (let d of this.fakeDisplays)
+        for (let d of this.fakeDisplays) {
           d.mirroringSourceId = info.mirroringSourceId;
+        }
       }
 
       if (info.isPrimary != undefined) {
@@ -80,8 +81,9 @@
         }
         this.updateLayouts_();
       }
-      if (info.rotation != undefined)
+      if (info.rotation != undefined) {
         display.rotation = info.rotation;
+      }
     },
 
     /** @override */
@@ -113,8 +115,9 @@
           }
         }
       }
-      for (let d of this.fakeDisplays)
+      for (let d of this.fakeDisplays) {
         d.mirroringSourceId = mirroringSourceId;
+      }
       callback();
     },
 
@@ -126,8 +129,9 @@
       const idx = this.fakeDisplays.findIndex(function(display) {
         return display.id == id;
       });
-      if (idx >= 0)
+      if (idx >= 0) {
         return this.fakeDisplays[idx];
+      }
       return undefined;
     },
 
diff --git a/chrome/test/data/webui/settings/fingerprint_browsertest_chromeos.js b/chrome/test/data/webui/settings/fingerprint_browsertest_chromeos.js
index 87a507a1..37fe338 100644
--- a/chrome/test/data/webui/settings/fingerprint_browsertest_chromeos.js
+++ b/chrome/test/data/webui/settings/fingerprint_browsertest_chromeos.js
@@ -32,8 +32,9 @@
    * @param {number} percent
    */
   scanReceived(result, complete, percent) {
-    if (complete)
+    if (complete) {
       this.fingerprintsList_.push('New Label');
+    }
 
     cr.webUIListenerCallback(
         'on-fingerprint-scan-received',
diff --git a/chrome/test/data/webui/settings/languages_page_tests.js b/chrome/test/data/webui/settings/languages_page_tests.js
index 53b4653..be3de37 100644
--- a/chrome/test/data/webui/settings/languages_page_tests.js
+++ b/chrome/test/data/webui/settings/languages_page_tests.js
@@ -414,8 +414,9 @@
 
       test('move up/down buttons', function() {
         // Add several languages.
-        for (const language of ['en-CA', 'en-US', 'tk', 'no'])
+        for (const language of ['en-CA', 'en-US', 'tk', 'no']) {
           languageHelper.enableLanguage(language);
+        }
 
         Polymer.dom.flush();
 
@@ -557,8 +558,9 @@
       });
 
       test('error handling', function() {
-        if (cr.isMac)
+        if (cr.isMac) {
           return;
+        }
 
         const checkAllHidden = nodes => {
           assertTrue(nodes.every(node => node.hidden));
diff --git a/chrome/test/data/webui/settings/languages_tests.js b/chrome/test/data/webui/settings/languages_tests.js
index 82b67fac..0449a69 100644
--- a/chrome/test/data/webui/settings/languages_tests.js
+++ b/chrome/test/data/webui/settings/languages_tests.js
@@ -47,8 +47,9 @@
 
       document.body.appendChild(languageHelper);
       return languageHelper.whenReady().then(function() {
-        if (cr.isChromeOS || cr.isWindows)
+        if (cr.isChromeOS || cr.isWindows) {
           return browserProxy.whenCalled('getProspectiveUILanguage');
+        }
       });
     });
 
diff --git a/chrome/test/data/webui/settings/multidevice_smartlock_subpage_test.js b/chrome/test/data/webui/settings/multidevice_smartlock_subpage_test.js
index d76ff9f..c49eac7 100644
--- a/chrome/test/data/webui/settings/multidevice_smartlock_subpage_test.js
+++ b/chrome/test/data/webui/settings/multidevice_smartlock_subpage_test.js
@@ -140,8 +140,7 @@
     });
   });
 
-  test('Smart Lock enable feature toggle without authentication', function()
-  {
+  test('Smart Lock enable feature toggle without authentication', function() {
     smartLockSubPage = createSmartLockSubPage();
     setSuiteState(settings.MultiDeviceFeatureState.ENABLED_BY_USER);
     setSmartLockFeatureState(settings.MultiDeviceFeatureState.DISABLED_BY_USER);
diff --git a/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.js b/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.js
index 421800b..11f1ab5e 100644
--- a/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.js
+++ b/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.js
@@ -206,8 +206,9 @@
   removeSavedPassword: function(id) {
     this.actual_.removed.passwords++;
 
-    if (this.onRemoveSavedPassword)
+    if (this.onRemoveSavedPassword) {
       this.onRemoveSavedPassword(id);
+    }
   },
 
   /** @override */
@@ -231,8 +232,9 @@
   removeException: function(id) {
     this.actual_.removed.exceptions++;
 
-    if (this.onRemoveException)
+    if (this.onRemoveException) {
       this.onRemoveException(id);
+    }
   },
 
   /** @override */
diff --git a/chrome/test/data/webui/settings/prefs_tests.js b/chrome/test/data/webui/settings/prefs_tests.js
index 0caeb3f5..a7a2fca 100644
--- a/chrome/test/data/webui/settings/prefs_tests.js
+++ b/chrome/test/data/webui/settings/prefs_tests.js
@@ -33,8 +33,9 @@
       let pref = prefStore;
       for (const part of path) {
         pref = pref[part];
-        if (!pref)
+        if (!pref) {
           return undefined;
+        }
       }
       return pref;
     }
diff --git a/chrome/test/data/webui/settings/privacy_page_test.js b/chrome/test/data/webui/settings/privacy_page_test.js
index 13be27d..18ec3c57 100644
--- a/chrome/test/data/webui/settings/privacy_page_test.js
+++ b/chrome/test/data/webui/settings/privacy_page_test.js
@@ -742,11 +742,13 @@
     });
   }
 
-  if (cr.isMac || cr.isWindows)
+  if (cr.isMac || cr.isWindows) {
     registerNativeCertificateManagerTests();
+  }
 
-  if (!cr.isChromeOS)
+  if (!cr.isChromeOS) {
     registerClearBrowsingDataTestsDice();
+  }
 
   registerClearBrowsingDataTests();
   registerPrivacyPageTests();
diff --git a/chrome/test/data/webui/settings/quick_unlock_authenticate_browsertest_chromeos.js b/chrome/test/data/webui/settings/quick_unlock_authenticate_browsertest_chromeos.js
index 1438ef00..781f89d 100644
--- a/chrome/test/data/webui/settings/quick_unlock_authenticate_browsertest_chromeos.js
+++ b/chrome/test/data/webui/settings/quick_unlock_authenticate_browsertest_chromeos.js
@@ -26,10 +26,11 @@
         // cr-dialog itself will always be 0x0. It's the inner native <dialog>
         // that has actual dimensions.
         // (The same about PIN-KEYBOARD.)
-        if (element.tagName == 'CR-DIALOG')
+        if (element.tagName == 'CR-DIALOG') {
           element = element.getNative();
-        else if (element.tagName == 'PIN-KEYBOARD')
+        } else if (element.tagName == 'PIN-KEYBOARD') {
           element = element.$.root;
+        }
       }
     }
 
@@ -52,8 +53,9 @@
    */
   function getFromElement(selector) {
     let childElement = testElement.$$(selector);
-    if (!childElement && testElement.$.pinKeyboard)
+    if (!childElement && testElement.$.pinKeyboard) {
       childElement = testElement.$.pinKeyboard.$$(selector);
+    }
 
     assertTrue(!!childElement);
     return childElement;
@@ -214,11 +216,12 @@
        */
       function assertRadioButtonChecked(radioButton) {
         function doAssert(element, name) {
-          if (radioButton == element)
+          if (radioButton == element) {
             assertTrue(element.checked, 'Expected ' + name + ' to be checked');
-          else
+          } else {
             assertFalse(
                 element.checked, 'Expected ' + name + ' to be unchecked');
+          }
         }
 
         doAssert(passwordRadioButton, 'passwordButton');
diff --git a/chrome/test/data/webui/settings/settings_main_test.js b/chrome/test/data/webui/settings/settings_main_test.js
index 308611e..31b31e0 100644
--- a/chrome/test/data/webui/settings/settings_main_test.js
+++ b/chrome/test/data/webui/settings/settings_main_test.js
@@ -125,10 +125,11 @@
      */
     function assertToggleContainerVisible(expectedVisible) {
       const toggleContainer = getToggleContainer();
-      if (expectedVisible)
+      if (expectedVisible) {
         assertNotEquals('none', toggleContainer.style.display);
-      else
+      } else {
         assertEquals('none', toggleContainer.style.display);
+      }
     }
 
     test('no results page shows and hides', function() {
diff --git a/chrome/test/data/webui/settings/settings_menu_test.js b/chrome/test/data/webui/settings/settings_menu_test.js
index 9bcdd3d..3da71205 100644
--- a/chrome/test/data/webui/settings/settings_menu_test.js
+++ b/chrome/test/data/webui/settings/settings_menu_test.js
@@ -127,8 +127,9 @@
       assertFalse(settingsMenu.$$('#advancedButton').hidden);
       assertFalse(settingsMenu.$$('#advancedSubmenu').hidden);
       assertFalse(settingsMenu.$$('#reset').hidden);
-      if (!cr.isChromeOS)
+      if (!cr.isChromeOS) {
         assertFalse(settingsMenu.$$('#defaultBrowser').hidden);
+      }
     });
   });
 });
diff --git a/chrome/test/data/webui/settings/settings_page_browsertest.js b/chrome/test/data/webui/settings/settings_page_browsertest.js
index 8b7a6ef..ccc8616 100644
--- a/chrome/test/data/webui/settings/settings_page_browsertest.js
+++ b/chrome/test/data/webui/settings/settings_page_browsertest.js
@@ -71,8 +71,9 @@
     const page = settingsMain.$$(pageType);
 
     const idleRender = page && page.$$('settings-idle-load');
-    if (!idleRender)
+    if (!idleRender) {
       return Promise.resolve(page);
+    }
 
     return idleRender.get().then(function() {
       Polymer.dom.flush();
@@ -91,8 +92,9 @@
     assertTrue(!!sections);
     for (let i = 0; i < sections.length; ++i) {
       const s = sections[i];
-      if (s.section == section)
+      if (s.section == section) {
         return s;
+      }
     }
     return undefined;
   },
@@ -106,8 +108,9 @@
     // Check if there are sub-pages to verify.
     const pages = section.firstElementChild.shadowRoot.querySelector(
         'settings-animated-pages');
-    if (!pages)
+    if (!pages) {
       return;
+    }
 
     const children = pages.getContentChildren();
     const stampedChildren = children.filter(function(element) {
diff --git a/chrome/test/data/webui/settings/settings_passwords_section_browsertest.js b/chrome/test/data/webui/settings/settings_passwords_section_browsertest.js
index 7b5a7bb..6df7203 100644
--- a/chrome/test/data/webui/settings/settings_passwords_section_browsertest.js
+++ b/chrome/test/data/webui/settings/settings_passwords_section_browsertest.js
@@ -189,8 +189,9 @@
    */
   function listContainsUrl(passwordList, url) {
     for (let i = 0; i < passwordList.length; ++i) {
-      if (passwordList[i].loginPair.urls.origin == url)
+      if (passwordList[i].loginPair.urls.origin == url) {
         return true;
+      }
     }
     return false;
   }
@@ -202,8 +203,9 @@
    */
   function exceptionsListContainsUrl(exceptionList, url) {
     for (let i = 0; i < exceptionList.length; ++i) {
-      if (exceptionList[i].urls.orginUrl == url)
+      if (exceptionList[i].urls.orginUrl == url) {
         return true;
+      }
     }
     return false;
   }
diff --git a/chrome/test/data/webui/settings/site_details_tests.js b/chrome/test/data/webui/settings/site_details_tests.js
index a1b71bf..2e3dd78 100644
--- a/chrome/test/data/webui/settings/site_details_tests.js
+++ b/chrome/test/data/webui/settings/site_details_tests.js
@@ -111,9 +111,10 @@
       settings.ContentSettingsTypes.PROTOCOL_HANDLERS,
       settings.ContentSettingsTypes.ZOOM_LEVELS,
     ];
-    if (!cr.isChromeOS)
+    if (!cr.isChromeOS) {
       nonSiteDetailsContentSettingsTypes.push(
           settings.ContentSettingsTypes.PROTECTED_CONTENT);
+    }
 
     // A list of optionally shown content settings mapped to their loadTimeData
     // flag string.
@@ -259,8 +260,9 @@
               .forEach((siteDetailsPermission) => {
                 if (!cr.isChromeOS &&
                     siteDetailsPermission.category ==
-                        settings.ContentSettingsTypes.PROTECTED_CONTENT)
+                        settings.ContentSettingsTypes.PROTECTED_CONTENT) {
                   return;
+                }
 
                 // Verify settings match the values specified in |prefs|.
                 let expectedSetting = settings.ContentSetting.ALLOW;
diff --git a/chrome/test/data/webui/settings/site_list_tests.js b/chrome/test/data/webui/settings/site_list_tests.js
index 728e884..b69e73171 100644
--- a/chrome/test/data/webui/settings/site_list_tests.js
+++ b/chrome/test/data/webui/settings/site_list_tests.js
@@ -333,8 +333,9 @@
   /** Closes the action menu. */
   function closeActionMenu() {
     const menu = testElement.$$('cr-action-menu');
-    if (menu.open)
+    if (menu.open) {
       menu.close();
+    }
   }
 
   /**
@@ -346,8 +347,9 @@
     assertTrue(!!menu);
     const menuItems = menu.querySelectorAll('button:not([hidden])');
     assertEquals(items.length, menuItems.length);
-    for (let i = 0; i < items.length; i++)
+    for (let i = 0; i < items.length; i++) {
       assertEquals(items[i], menuItems[i].textContent.trim());
+    }
   }
 
   /**
diff --git a/chrome/test/data/webui/settings/sync_account_control_test.js b/chrome/test/data/webui/settings/sync_account_control_test.js
index af4ea08..44570a14e 100644
--- a/chrome/test/data/webui/settings/sync_account_control_test.js
+++ b/chrome/test/data/webui/settings/sync_account_control_test.js
@@ -95,10 +95,11 @@
       assertVisible(testElement.$$('#avatar-row'), false);
       // TODO (rbpotter): Remove this conditional when the migration to
       // Polymer 2 is completed.
-      if (Polymer.DomIf)
+      if (Polymer.DomIf) {
         assertEquals(null, testElement.$$('#menu'));
-      else
+      } else {
         assertVisible(testElement.$$('#menu'), false);
+      }
       assertVisible(testElement.$$('#sign-in'), true);
 
       testElement.$$('#sign-in').click();
diff --git a/chrome/test/data/webui/settings/test_languages_browser_proxy.js b/chrome/test/data/webui/settings/test_languages_browser_proxy.js
index 9ec78798..0fc56c0 100644
--- a/chrome/test/data/webui/settings/test_languages_browser_proxy.js
+++ b/chrome/test/data/webui/settings/test_languages_browser_proxy.js
@@ -7,8 +7,9 @@
   class TestLanguagesBrowserProxy extends TestBrowserProxy {
     constructor() {
       const methodNames = [];
-      if (cr.isChromeOS || cr.isWindows)
+      if (cr.isChromeOS || cr.isWindows) {
         methodNames.push('getProspectiveUILanguage');
+      }
 
       super(methodNames);
 
diff --git a/chrome/test/data/webui/settings/test_lifetime_browser_proxy.js b/chrome/test/data/webui/settings/test_lifetime_browser_proxy.js
index 2cc201e8..270ea1cb 100644
--- a/chrome/test/data/webui/settings/test_lifetime_browser_proxy.js
+++ b/chrome/test/data/webui/settings/test_lifetime_browser_proxy.js
@@ -11,8 +11,9 @@
   class TestLifetimeBrowserProxy extends TestBrowserProxy {
     constructor() {
       const methodNames = ['restart', 'relaunch'];
-      if (cr.isChromeOS)
+      if (cr.isChromeOS) {
         methodNames.push('signOutAndRestart', 'factoryReset');
+      }
 
       super(methodNames);
     }
diff --git a/chrome/test/data/webui/settings/test_local_data_browser_proxy.js b/chrome/test/data/webui/settings/test_local_data_browser_proxy.js
index ecf533f..52b98c6 100644
--- a/chrome/test/data/webui/settings/test_local_data_browser_proxy.js
+++ b/chrome/test/data/webui/settings/test_local_data_browser_proxy.js
@@ -49,8 +49,9 @@
 
   /** @override */
   getDisplayList(filter) {
-    if (filter === undefined)
+    if (filter === undefined) {
       filter = '';
+    }
     let output = [];
     for (let i = 0; i < this.cookieList_.length; ++i) {
       if (this.cookieList_[i].site.indexOf(filter) >= 0) {
diff --git a/chrome/test/data/webui/settings/test_site_settings_prefs_browser_proxy.js b/chrome/test/data/webui/settings/test_site_settings_prefs_browser_proxy.js
index e6d5d87..e71f0a1 100644
--- a/chrome/test/data/webui/settings/test_site_settings_prefs_browser_proxy.js
+++ b/chrome/test/data/webui/settings/test_site_settings_prefs_browser_proxy.js
@@ -114,8 +114,9 @@
     // Remove entries from the current prefs which have the same origin.
     const newPrefs = /** @type {!Array<RawSiteException>} */
         (this.prefs_.exceptions[category].filter((categoryException) => {
-          if (categoryException.origin != newException.origin)
+          if (categoryException.origin != newException.origin) {
             return true;
+          }
         }));
     newPrefs.push(newException);
     this.prefs_.exceptions[category] = newPrefs;
@@ -197,8 +198,9 @@
 
     contentTypes.forEach((contentType) => {
       this.prefs_.exceptions[contentType].forEach((exception) => {
-        if (exception.origin.includes('*'))
+        if (exception.origin.includes('*')) {
           return;
+        }
         origins_set.add(exception.origin);
       });
     });
diff --git a/chrome/test/data/webui/settings/test_util.js b/chrome/test/data/webui/settings/test_util.js
index 0df9504..c7dab2ac 100644
--- a/chrome/test/data/webui/settings/test_util.js
+++ b/chrome/test/data/webui/settings/test_util.js
@@ -59,10 +59,11 @@
    */
   function fakeDataBind(el1, el2, property) {
     const forwardChange = function(el, event) {
-      if (event.detail.hasOwnProperty('path'))
+      if (event.detail.hasOwnProperty('path')) {
         el.notifyPath(event.detail.path, event.detail.value);
-      else
+      } else {
         el.set(property, event.detail.value);
+      }
     };
     // Add the listeners symmetrically. Polymer will prevent recursion.
     el1.addEventListener(property + '-changed', forwardChange.bind(null, el2));
@@ -90,8 +91,9 @@
    * @return {DefaultContentSetting}
    */
   function createDefaultContentSetting(override) {
-    if (override === undefined)
+    if (override === undefined) {
       override = {};
+    }
     return Object.assign(
         {
           setting: settings.ContentSetting.ASK,
@@ -109,8 +111,9 @@
    * @return {RawSiteException}
    */
   function createRawSiteException(origin, override) {
-    if (override === undefined)
+    if (override === undefined) {
       override = {};
+    }
     return Object.assign(
         {
           embeddingOrigin: origin,
@@ -201,8 +204,9 @@
   }
 
   function createOriginInfo(origin, override) {
-    if (override === undefined)
+    if (override === undefined) {
       override = {};
+    }
     return Object.assign(
         {
           origin: origin,
diff --git a/chrome/test/data/webui/sys_internals/test_util.js b/chrome/test/data/webui/sys_internals/test_util.js
index 63db0bd0..73049cb 100644
--- a/chrome/test/data/webui/sys_internals/test_util.js
+++ b/chrome/test/data/webui/sys_internals/test_util.js
@@ -39,4 +39,4 @@
       origDataSize: 200 * GB,
     },
   };
-}
+};
diff --git a/chrome/test/data/webui/test_api.js b/chrome/test/data/webui/test_api.js
index 0d2b5535..f83d032d 100644
--- a/chrome/test/data/webui/test_api.js
+++ b/chrome/test/data/webui/test_api.js
@@ -75,10 +75,11 @@
 
   var realElementAnimate = Element.prototype.animate;
   Element.prototype.animate = function(keyframes, opt_options) {
-    if (typeof opt_options == 'object')
+    if (typeof opt_options == 'object') {
       opt_options.duration = 0;
-    else
+    } else {
       opt_options = 0;
+    }
     return realElementAnimate.call(this, keyframes, opt_options);
   };
   if (document.timeline && document.timeline.play) {
@@ -353,8 +354,9 @@
   tearDown: function() {
     if (typeof document != 'undefined') {
       var noAnimationStyle = document.getElementById('no-animation');
-      if (noAnimationStyle)
+      if (noAnimationStyle) {
         noAnimationStyle.parentNode.removeChild(noAnimationStyle);
+      }
     }
 
     Mock4JS.verifyAllMocks();
@@ -373,16 +375,18 @@
    * fixture.
    */
   runAccessibilityAudit: function() {
-    if (!this.runAccessibilityChecks || typeof document === 'undefined')
+    if (!this.runAccessibilityChecks || typeof document === 'undefined') {
       return;
+    }
 
     var auditConfig = this.accessibilityAuditConfig;
     if (!runAccessibilityAudit(this.a11yResults_, auditConfig)) {
       var report = accessibilityAuditReport(this.a11yResults_);
-      if (this.accessibilityIssuesAreErrors)
+      if (this.accessibilityIssuesAreErrors) {
         throw new Error(report);
-      else
+      } else {
         console.warn(report);
+      }
     }
   },
 
@@ -400,8 +404,9 @@
     var savedArgs = new SaveMockArguments();
     var completionAction = new CallFunctionAction(
         this, savedArgs, completion, Array.prototype.slice.call(arguments, 2));
-    if (whenTestDone === WhenTestDone.DEFAULT)
+    if (whenTestDone === WhenTestDone.DEFAULT) {
       whenTestDone = WhenTestDone.ASSERT;
+    }
     var runAll = new RunAllAction(true, whenTestDone, [completionAction]);
     return function() {
       savedArgs.arguments = Array.prototype.slice.call(arguments);
@@ -421,8 +426,9 @@
    *     the currentTestCase.
    */
   deferRunTest: function(whenTestDone) {
-    if (whenTestDone === WhenTestDone.DEFAULT)
+    if (whenTestDone === WhenTestDone.DEFAULT) {
       whenTestDone = WhenTestDone.ALWAYS;
+    }
 
     return currentTestCase.deferRunTest.apply(
         currentTestCase,
@@ -475,40 +481,45 @@
    * @type {Function}
    */
   preLoad: function(name) {
-    if (this.fixture)
+    if (this.fixture) {
       this.fixture.preLoad();
+    }
   },
 
   /**
    * Called before a test runs.
    */
   setUp: function() {
-    if (this.fixture)
+    if (this.fixture) {
       this.fixture.setUp();
+    }
   },
 
   /**
    * Called before a test is torn down (by testDone()).
    */
   tearDown: function() {
-    if (this.fixture)
+    if (this.fixture) {
       this.fixture.tearDown();
+    }
   },
 
   /**
    * Called to run this test's body.
    */
   runTest: function() {
-    if (this.body && this.fixture)
+    if (this.body && this.fixture) {
       this.fixture.runTest(this.body);
+    }
   },
 
   /**
    * Called after a test is run (in testDone) to test accessibility.
    */
   runAccessibilityAudit: function() {
-    if (this.fixture)
+    if (this.fixture) {
       this.fixture.runAccessibilityAudit();
+    }
   },
 
   /**
@@ -529,8 +540,9 @@
       console.error(e.stack || e.toString());
     }
 
-    if (!this.deferred_)
+    if (!this.deferred_) {
       this.runTest();
+    }
 
     // tearDown called by testDone().
   },
@@ -588,8 +600,9 @@
  * @see overrideChrome
  */
 function registerMockMessageCallbacks(mockObject, mockClass) {
-  if (!deferGlobalOverrides && !originalChrome)
+  if (!deferGlobalOverrides && !originalChrome) {
     overrideChrome();
+  }
   var mockProxy = mockObject.proxy();
   for (var func in mockClass.prototype) {
     if (typeof mockClass.prototype[func] === 'function') {
@@ -640,8 +653,9 @@
     callback: callback,
   };
 
-  if (!deferGlobalOverrides)
+  if (!deferGlobalOverrides) {
     overrideGlobal(name);
+  }
 }
 
 /**
@@ -655,8 +669,9 @@
   var namespace = this;
   for (var i = 0; i < path.length - 1; i++) {
     var fieldName = path[i];
-    if (!namespace[fieldName])
-    namespace[fieldName] = {};
+    if (!namespace[fieldName]) {
+      namespace[fieldName] = {};
+    }
 
     namespace = namespace[fieldName];
   }
@@ -679,8 +694,9 @@
  */
 function makeMockClass(methodNames) {
   function MockConstructor() {}
-  for (var i = 0; i < methodNames.length; i++)
+  for (var i = 0; i < methodNames.length; i++) {
     MockConstructor.prototype[methodNames[i]] = emptyFunction;
+  }
   return MockConstructor;
 }
 
@@ -698,8 +714,9 @@
   mockFunctions.functions_ = {};
 
   for (var func in MockClass.prototype) {
-    if (typeof MockClass.prototype[func] === 'function')
+    if (typeof MockClass.prototype[func] === 'function') {
       mockFunctions.functions_[func] = mockProxy[func].bind(mockProxy);
+    }
   }
 
   mockFunctions.functions = function() {
@@ -720,8 +737,9 @@
 function registerMockGlobals(mockObject, mockClass) {
   var mockProxy = mockObject.proxy();
   for (var func in mockClass.prototype) {
-    if (typeof mockClass.prototype[func] === 'function')
+    if (typeof mockClass.prototype[func] === 'function') {
       registerMockGlobal(func, mockProxy, mockProxy[func]);
+    }
   }
 }
 
@@ -733,8 +751,9 @@
 function registerMockApis(mockObject) {
   var functions = mockObject.functions();
   for (var func in functions) {
-    if (typeof functions[func] === 'function')
+    if (typeof functions[func] === 'function') {
       registerMockApi(func, functions[func]);
+    }
   }
 }
 
@@ -745,10 +764,11 @@
  */
 function send(messageName) {
   var callback = sendCallbacks[messageName];
-  if (callback != undefined)
+  if (callback != undefined) {
     callback[1].apply(callback[0], Array.prototype.slice.call(arguments, 1));
-  else
+  } else {
     this.__proto__.send.apply(this.__proto__, arguments);
+  }
 }
 
 /**
@@ -796,13 +816,15 @@
                .call(null) &&
           ok;
 
-      if (!ok && result)
+      if (!ok && result) {
         result = [false, errorsToMessage(errors, result[1])];
+      }
 
       currentTestCase = null;
     }
-    if (!result)
+    if (!result) {
       result = testResult();
+    }
 
     if (hasWindow && window.webUiTest) {
       let testRunner;
@@ -819,10 +841,11 @@
         assertNotReached(
             'Mojo bindings found, but no valid test interface loaded');
       }
-      if (result[0])
+      if (result[0]) {
         testRunner.testComplete();
-      else
+      } else {
         testRunner.testComplete(result[1]);
+      }
     } else if (chrome.send) {
       // For WebUI and v8 unit tests.
       chrome.send('testResult', result);
@@ -848,8 +871,9 @@
  */
 function errorsToMessage(errors, opt_message) {
   var message = '';
-  if (opt_message)
+  if (opt_message) {
     message += opt_message + '\n';
+  }
 
   for (var i = 0; i < errors.length; ++i) {
     var errorMessage = errors[i].stack || errors[i].message;
@@ -866,8 +890,9 @@
  */
 function testResult(errorsOk) {
   var result = [true, ''];
-  if (errors.length)
+  if (errors.length) {
     result = [!!errorsOk, errorsToMessage(errors)];
+  }
 
   return result;
 }
@@ -1029,8 +1054,9 @@
 function assertAccessibilityOk(opt_results) {
   var a11yResults = opt_results || [];
   var auditConfig = currentTestCase.fixture.accessibilityAuditConfig;
-  if (!runAccessibilityAudit(a11yResults, auditConfig))
+  if (!runAccessibilityAudit(a11yResults, auditConfig)) {
     throw new Error(accessibilityAuditReport(a11yResults));
+  }
 }
 
 /**
@@ -1085,8 +1111,9 @@
 
   // Depending on how we were called, |this| might not resolve to the global
   // context.
-  if (testName == 'RUN_TEST_F' && testBody === undefined)
+  if (testName == 'RUN_TEST_F' && testBody === undefined) {
     testBody = RUN_TEST_F;
+  }
 
   if (typeof testBody === 'undefined') {
     testBody = eval(testFunction);
@@ -1098,8 +1125,9 @@
 
   // Async allow expect errors, but not assert errors.
   var result = runTestFunction(testFunction, testBody, testArguments, isAsync);
-  if (!isAsync || !result[0])
+  if (!isAsync || !result[0]) {
     testDone(result);
+  }
   return true;
 }
 
@@ -1182,14 +1210,16 @@
   // events (and doesn't fire), whereas the window does not. Listening to the
   // capture phase allows this event to fire first.
   window.addEventListener('DOMContentLoaded', function() {
-    if (chrome.send)
+    if (chrome.send) {
       overrideChrome();
+    }
 
     // Override globals at load time so they will be defined.
     assertTrue(deferGlobalOverrides);
     deferGlobalOverrides = false;
-    for (var funcName in globalOverrides)
+    for (var funcName in globalOverrides) {
       overrideGlobal(funcName);
+    }
   }, true);
   currentTestCase = createTestCase(testFixture, testName);
   currentTestCase.preLoad();
@@ -1256,10 +1286,12 @@
  */
 function TEST_F(testFixture, testName, testBody) {
   var fixtureConstructor = this[testFixture];
-  if (!fixtureConstructor.prototype.name)
+  if (!fixtureConstructor.prototype.name) {
     fixtureConstructor.prototype.name = testFixture;
-  if (fixtureConstructor['testCaseBodies'] === undefined)
+  }
+  if (fixtureConstructor['testCaseBodies'] === undefined) {
     fixtureConstructor.testCaseBodies = {};
+  }
   fixtureConstructor.testCaseBodies[testName] = testBody;
 }
 
@@ -1274,8 +1306,9 @@
  * @see runTest
  */
 function RUN_TEST_F(testFixture, testName) {
-  if (!currentTestCase)
+  if (!currentTestCase) {
     currentTestCase = createTestCase(testFixture, testName);
+  }
   assertEquals(currentTestCase.name, testName);
   assertEquals(currentTestCase.fixture.name, testFixture);
   console.log('Running TestCase ' + testFixture + '.' + testName);
@@ -1318,8 +1351,9 @@
   argumentMatches: function(actualArgument) {
     this.arguments_.push(actualArgument);
     var match = this.realMatcher_.argumentMatches(actualArgument);
-    if (!match)
+    if (!match) {
       this.arguments_.splice(0, this.arguments_.length);
+    }
 
     return match;
   },
@@ -1587,24 +1621,29 @@
   invoke: function() {
     try {
       var result;
-      for (var i = 0; i < this.actions_.length; ++i)
+      for (var i = 0; i < this.actions_.length; ++i) {
         result = this.actions_[i].invoke();
+      }
 
       if ((this.whenTestDone_ == WhenTestDone.EXPECT && errors.length) ||
-          this.whenTestDone_ == WhenTestDone.ALWAYS)
+          this.whenTestDone_ == WhenTestDone.ALWAYS) {
         testDone();
+      }
 
       return result;
     } catch (e) {
-      if (!(e instanceof Error))
+      if (!(e instanceof Error)) {
         e = new Error(e.toString());
+      }
 
-      if (!this.isAsync_)
+      if (!this.isAsync_) {
         throw e;
+      }
 
       errors.push(e);
-      if (this.whenTestDone_ != WhenTestDone.NEVER)
+      if (this.whenTestDone_ != WhenTestDone.NEVER) {
         testDone();
+      }
     }
   },
 
diff --git a/chrome/test/data/webui/test_store.js b/chrome/test/data/webui/test_store.js
index e5f70b0..405b6d8 100644
--- a/chrome/test/data/webui/test_store.js
+++ b/chrome/test/data/webui/test_store.js
@@ -63,10 +63,12 @@
       /** @override */
       reduce_(action) {
         this.lastAction_ = action;
-        if (this.enableReducers_)
+        if (this.enableReducers_) {
           this.storeImplClass.prototype.reduce_.call(this, action);
-        if (this.resolverMap_.has(action.name))
+        }
+        if (this.resolverMap_.has(action.name)) {
           this.resolverMap_.get(action.name).resolve(action);
+        }
       }
 
       /**
diff --git a/chrome/test/data/webui/webui_resource_async_browsertest.js b/chrome/test/data/webui/webui_resource_async_browsertest.js
index e8bb3bf1..16fe6ae 100644
--- a/chrome/test/data/webui/webui_resource_async_browsertest.js
+++ b/chrome/test/data/webui/webui_resource_async_browsertest.js
@@ -129,10 +129,12 @@
     var EVENT_NAME = 'my-foo-event';
 
     teardown(function() {
-      if (listener1)
+      if (listener1) {
         cr.removeWebUIListener(listener1);
-      if (listener2)
+      }
+      if (listener2) {
         cr.removeWebUIListener(listener2);
+      }
     });
 
     test('removeWebUIListener', function() {
diff --git a/chrome/test/data/webui/webview_content_script_test.js b/chrome/test/data/webui/webview_content_script_test.js
index 65d5269..d443e66 100644
--- a/chrome/test/data/webui/webview_content_script_test.js
+++ b/chrome/test/data/webui/webview_content_script_test.js
@@ -77,8 +77,9 @@
   });
 
   window.addEventListener('message', function(e) {
-    if (e.source != webview.contentWindow)
+    if (e.source != webview.contentWindow) {
       return;
+    }
     var data = JSON.parse(e.data);
     if (data == RESPONSE_FROM_COMM_CHANNEL_1) {
       console.log(
@@ -128,22 +129,25 @@
   var response_1 = false;
   var response_2 = false;
   window.addEventListener('message', function(e) {
-    if (e.source != webview.contentWindow)
+    if (e.source != webview.contentWindow) {
       return;
+    }
     var data = JSON.parse(e.data);
     if (data == RESPONSE_FROM_COMM_CHANNEL_1) {
       console.log(
           'Step 4: A communication channel has been established with webview.');
       response_1 = true;
-      if (response_1 && response_2)
+      if (response_1 && response_2) {
         chrome.send('testResult', [true]);
+      }
       return;
     } else if (data == RESPONSE_FROM_COMM_CHANNEL_2) {
       console.log(
           'Step 5: A communication channel has been established with webview.');
       response_2 = true;
-      if (response_1 && response_2)
+      if (response_1 && response_2) {
         chrome.send('testResult', [true]);
+      }
       return;
     }
     console.log('Unexpected message: \'' + data[0] + '\'');
@@ -187,8 +191,9 @@
 
   var should_get_response_from_script_1 = true;
   window.addEventListener('message', function(e) {
-    if (e.source != webview.contentWindow)
+    if (e.source != webview.contentWindow) {
       return;
+    }
     var data = JSON.parse(e.data);
     if (data == RESPONSE_FROM_COMM_CHANNEL_1) {
       if (should_get_response_from_script_1) {
@@ -252,8 +257,9 @@
   });
 
   window.addEventListener('message', function(e) {
-    if (e.source != webview2.contentWindow)
+    if (e.source != webview2.contentWindow) {
       return;
+    }
     var data = JSON.parse(e.data);
     if (data == RESPONSE_FROM_COMM_CHANNEL_1) {
       chrome.send('testResult', [false]);
@@ -305,8 +311,9 @@
   });
 
   window.addEventListener('message', function(e) {
-    if (e.source != webview.contentWindow)
+    if (e.source != webview.contentWindow) {
       return;
+    }
     var data = JSON.parse(e.data);
     if (data[0] == RESPONSE_FROM_COMM_CHANNEL_1 &&
         should_get_response_from_script_1) {
@@ -360,8 +367,9 @@
   });
 
   window.addEventListener('message', function(e) {
-    if (!newwebview || e.source != newwebview.contentWindow)
+    if (!newwebview || e.source != newwebview.contentWindow) {
       return;
+    }
     var data = JSON.parse(e.data);
     if (data == RESPONSE_FROM_COMM_CHANNEL_1 &&
         e.source == newwebview.contentWindow) {
@@ -509,8 +517,9 @@
   });
 
   webview.addEventListener('loadstop', function(e) {
-    if (webview.src != 'about:blank')
+    if (webview.src != 'about:blank') {
       return;
+    }
     console.log('load stop of src = :' + webview.src);
     webview.executeScript(
         {file: 'test/draganddroptoinput.js'}, function(results) {
diff --git a/chromecast/browser/cast_content_browser_client.cc b/chromecast/browser/cast_content_browser_client.cc
index ce183fb..4bc6692 100644
--- a/chromecast/browser/cast_content_browser_client.cc
+++ b/chromecast/browser/cast_content_browser_client.cc
@@ -700,7 +700,7 @@
     content::RenderFrameHost* opener,
     const GURL& opener_url,
     const GURL& opener_top_level_frame_url,
-    const GURL& source_origin,
+    const url::Origin& source_origin,
     content::mojom::WindowContainerType container_type,
     const GURL& target_url,
     const content::Referrer& referrer,
diff --git a/chromecast/browser/cast_content_browser_client.h b/chromecast/browser/cast_content_browser_client.h
index 67fceb5..f1e8a52 100644
--- a/chromecast/browser/cast_content_browser_client.h
+++ b/chromecast/browser/cast_content_browser_client.h
@@ -165,7 +165,7 @@
   bool CanCreateWindow(content::RenderFrameHost* opener,
                        const GURL& opener_url,
                        const GURL& opener_top_level_frame_url,
-                       const GURL& source_origin,
+                       const url::Origin& source_origin,
                        content::mojom::WindowContainerType container_type,
                        const GURL& target_url,
                        const content::Referrer& referrer,
diff --git a/chromeos/BUILD.gn b/chromeos/BUILD.gn
index e881ff21..e2cec478 100644
--- a/chromeos/BUILD.gn
+++ b/chromeos/BUILD.gn
@@ -31,10 +31,7 @@
     ":chromeos_export",
     ":policy_certificate_provider",
     ":tools",
-    "//chromeos/cryptohome",
     "//chromeos/dbus",
-    "//chromeos/login/auth",
-    "//chromeos/login/login_state",
     "//chromeos/network",
     "//chromeos/settings",
     "//dbus",
@@ -58,6 +55,9 @@
     "//base",
     "//base:i18n",
     "//base/third_party/dynamic_annotations",
+    "//chromeos/cryptohome",
+    "//chromeos/login/auth",
+    "//chromeos/login/login_state",
     "//components/account_id",
     "//components/device_event_log",
     "//components/policy:cloud_policy_proto_generated_compile",
@@ -66,6 +66,7 @@
     "//components/prefs",
     "//components/user_manager",
     "//crypto:platform",
+    "//dbus",
     "//google_apis",
     "//media/base:video_facing",
     "//services/network/public/cpp:cpp",
@@ -246,10 +247,11 @@
     ":cryptohome_proto",
     ":power_manager_proto",
     "//chromeos/dbus:test_support",
-    "//chromeos/login/auth:test_support",
-    "//chromeos/login/login_state:test_support",
   ]
   deps = [
+    "//chromeos/cryptohome",
+    "//chromeos/login/auth:test_support",
+    "//chromeos/login/login_state:test_support",
     "//components/account_id",
     "//google_apis",
     "//net:test_support",
@@ -308,6 +310,7 @@
     ":test_support",
     ":test_support_without_gmock",
     "//base/test:test_support",
+    "//chromeos/cryptohome",
     "//chromeos/cryptohome:unit_tests",
     "//chromeos/dbus:unit_tests",
     "//chromeos/login/auth:unit_tests",
diff --git a/chromeos/components/proximity_auth/smart_lock_metrics_recorder.h b/chromeos/components/proximity_auth/smart_lock_metrics_recorder.h
index ca70f34..d31206c 100644
--- a/chromeos/components/proximity_auth/smart_lock_metrics_recorder.h
+++ b/chromeos/components/proximity_auth/smart_lock_metrics_recorder.h
@@ -72,7 +72,8 @@
     kPhoneLockedAndRssiTooLow = 15,
     kForcedReauth = 16,
     kRequiredForLogin = 17,
-    kMaxValue = kRequiredForLogin
+    kPhoneNotLockable = 18,
+    kMaxValue = kPhoneNotLockable
   };
 
   static void RecordSmartLockUnlockAuthMethodChoice(
diff --git a/chromeos/components/tether/BUILD.gn b/chromeos/components/tether/BUILD.gn
index 6c0d8a17..fe991000 100644
--- a/chromeos/components/tether/BUILD.gn
+++ b/chromeos/components/tether/BUILD.gn
@@ -119,6 +119,7 @@
     "//chromeos",
     "//chromeos/components/multidevice/logging",
     "//chromeos/components/tether/proto",
+    "//chromeos/login/login_state",
     "//chromeos/services/device_sync/public/cpp",
     "//chromeos/services/multidevice_setup/public/cpp:cpp",
     "//chromeos/services/multidevice_setup/public/mojom:mojom",
@@ -265,6 +266,7 @@
     "//chromeos/components/multidevice",
     "//chromeos/components/multidevice:test_support",
     "//chromeos/components/tether/proto",
+    "//chromeos/login/login_state",
     "//chromeos/services/device_sync:test_support",
     "//chromeos/services/device_sync/public/cpp",
     "//chromeos/services/device_sync/public/cpp:test_support",
diff --git a/components/arc/BUILD.gn b/components/arc/BUILD.gn
index b95a190..6c067bc 100644
--- a/components/arc/BUILD.gn
+++ b/components/arc/BUILD.gn
@@ -82,6 +82,8 @@
     "//chromeos",
     "//chromeos:login_manager_proto",
     "//chromeos:power_manager_proto",
+    "//chromeos/cryptohome",
+    "//chromeos/login/login_state",
     "//components/account_id",
     "//components/exo",
     "//components/google/core/browser",
@@ -182,6 +184,7 @@
     "//base",
     "//chromeos",
     "//chromeos:login_manager_proto",
+    "//chromeos/cryptohome",
     "//components/account_id",
     "//components/keyed_service/content",
     "//components/prefs",
@@ -323,6 +326,7 @@
     "//chromeos",
     "//chromeos:power_manager_proto",
     "//chromeos:test_support_without_gmock",
+    "//chromeos/cryptohome",
     "//components/account_id",
     "//components/keyed_service/content",
     "//components/prefs:test_support",
diff --git a/components/autofill/content/renderer/form_autofill_util.cc b/components/autofill/content/renderer/form_autofill_util.cc
index 8ec7ae7b..0beac09 100644
--- a/components/autofill/content/renderer/form_autofill_util.cc
+++ b/components/autofill/content/renderer/form_autofill_util.cc
@@ -1415,7 +1415,8 @@
     FormData* form,
     FormFieldData* field) {
   form->origin = GetCanonicalOriginForDocument(document);
-  form->button_titles = InferButtonTitlesForForm(document.Body());
+  if (!document.Body().IsNull())
+    form->button_titles = InferButtonTitlesForForm(document.Body());
   if (document.GetFrame() && document.GetFrame()->Top()) {
     form->main_frame_origin = document.GetFrame()->Top()->GetSecurityOrigin();
   } else {
diff --git a/components/autofill/core/browser/autofill_download_manager.cc b/components/autofill/core/browser/autofill_download_manager.cc
index ac679de..adeb0145 100644
--- a/components/autofill/core/browser/autofill_download_manager.cc
+++ b/components/autofill/core/browser/autofill_download_manager.cc
@@ -158,9 +158,6 @@
       break;
     default:
       NOTREACHED();
-      base::UmaHistogramSparse("Autofill.Unknown.HttpResponseOrErrorCode",
-                               response_or_error_code);
-      UMA_HISTOGRAM_TIMES("Autofill.Unknown.RequestDuration", request_duration);
   }
 }
 
@@ -179,8 +176,6 @@
       break;
     default:
       NOTREACHED();
-      UMA_HISTOGRAM_COUNTS_100000("Autofill.Unknown.FailingPayloadSize",
-                                  num_bytes);
   }
 }
 
@@ -197,7 +192,6 @@
       break;
     default:
       NOTREACHED();
-      UMA_HISTOGRAM_MEDIUM_TIMES("Autofill.Unknown.BackoffDelay", delay);
   }
 }
 
diff --git a/components/autofill/core/common/save_password_progress_logger.cc b/components/autofill/core/common/save_password_progress_logger.cc
index 8a8ba13..57c49b8 100644
--- a/components/autofill/core/common/save_password_progress_logger.cc
+++ b/components/autofill/core/common/save_password_progress_logger.cc
@@ -305,7 +305,7 @@
     case SavePasswordProgressLogger::STRING_ON_ASK_USER_OR_SAVE_PASSWORD:
       return "PasswordManager::AskUserOrSavePassword";
     case SavePasswordProgressLogger::STRING_CAN_PROVISIONAL_MANAGER_SAVE_METHOD:
-      return "PasswordManager::CanProvisionalManagerSave";
+      return "PasswordManager::IsReadyForAutomaticSaving";
     case SavePasswordProgressLogger::STRING_NO_PROVISIONAL_SAVE_MANAGER:
       return "No provisional save manager";
     case SavePasswordProgressLogger::STRING_NUMBER_OF_VISIBLE_FORMS:
diff --git a/components/cronet/cronet_url_request.cc b/components/cronet/cronet_url_request.cc
index 22134c3..b39eb0d 100644
--- a/components/cronet/cronet_url_request.cc
+++ b/components/cronet/cronet_url_request.cc
@@ -304,6 +304,7 @@
 void CronetURLRequest::NetworkTasks::FollowDeferredRedirect() {
   DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
   url_request_->FollowDeferredRedirect(
+      base::nullopt /* removed_request_headers */,
       base::nullopt /* modified_request_headers */);
 }
 
diff --git a/components/password_manager/core/browser/password_manager.cc b/components/password_manager/core/browser/password_manager.cc
index 871d9859..8717ee37 100644
--- a/components/password_manager/core/browser/password_manager.cc
+++ b/components/password_manager/core/browser/password_manager.cc
@@ -653,7 +653,7 @@
 
   ProvisionallySavePassword(password_form, driver);
 
-  if (CanProvisionalManagerSave())
+  if (IsReadyForAutomaticSaving())
     OnLoginSuccessful();
 }
 
@@ -957,7 +957,7 @@
   provisional_save_manager_.swap(manager);
 }
 
-bool PasswordManager::CanProvisionalManagerSave() {
+bool PasswordManager::IsReadyForAutomaticSaving() {
   std::unique_ptr<BrowserSavePasswordProgressLogger> logger;
   if (password_manager_util::IsLoggingActive(client_)) {
     logger.reset(
@@ -983,7 +983,8 @@
         submitted_manager->GetOrigin(), logger.get());
     return false;
   }
-  return true;
+
+  return !submitted_manager->GetPendingCredentials().only_for_fallback_saving;
 }
 
 bool PasswordManager::ShouldBlockPasswordForSameOriginButDifferentScheme(
@@ -1007,7 +1008,7 @@
     logger->LogMessage(Logger::STRING_ON_PASSWORD_FORMS_RENDERED_METHOD);
   }
 
-  if (!CanProvisionalManagerSave())
+  if (!IsReadyForAutomaticSaving())
     return;
 
   PasswordFormManagerInterface* submitted_manager = GetSubmittedManager();
diff --git a/components/password_manager/core/browser/password_manager.h b/components/password_manager/core/browser/password_manager.h
index d43ba98..e5bc56d 100644
--- a/components/password_manager/core/browser/password_manager.h
+++ b/components/password_manager/core/browser/password_manager.h
@@ -200,7 +200,7 @@
 
   // Returns true if |provisional_save_manager_| is ready for saving and
   // non-blacklisted.
-  bool CanProvisionalManagerSave();
+  bool IsReadyForAutomaticSaving();
 
   // Returns true if there already exists a provisionally saved password form
   // from the same origin as |form|, but with a different and secure scheme.
diff --git a/components/password_manager/core/browser/password_manager_unittest.cc b/components/password_manager/core/browser/password_manager_unittest.cc
index 38f0e77..24ae6a7 100644
--- a/components/password_manager/core/browser/password_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_manager_unittest.cc
@@ -2984,6 +2984,29 @@
   manager()->OnPasswordFormSubmittedNoChecks(&driver_, submitted_form);
 }
 
+TEST_F(PasswordManagerTest, NoSavePromptForNotPasswordForm) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  TurnOnNewParsingForSaving(&scoped_feature_list);
+
+  EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
+      .WillRepeatedly(Return(true));
+
+  EXPECT_CALL(*store_, GetLogins(_, _))
+      .WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
+  PasswordForm form(MakeSimpleForm());
+  // Make the form to be credit card form.
+  form.form_data.fields[1].autocomplete_attribute = "cc-csc";
+
+  manager()->OnPasswordFormsParsed(&driver_, {form});
+
+  auto submitted_form = form;
+  submitted_form.form_data.fields[0].value = ASCIIToUTF16("text");
+  submitted_form.form_data.fields[1].value = ASCIIToUTF16("1234");
+
+  EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
+  manager()->OnPasswordFormSubmittedNoChecks(&driver_, submitted_form);
+}
+
 // Check that when autofill predictions are received before a form is found then
 // server predictions are not ignored and used for filling.
 TEST_F(PasswordManagerTest, AutofillPredictionBeforeFormParsed) {
diff --git a/components/signin/core/browser/account_investigator_unittest.cc b/components/signin/core/browser/account_investigator_unittest.cc
index 10ad5406..8deaa43 100644
--- a/components/signin/core/browser/account_investigator_unittest.cc
+++ b/components/signin/core/browser/account_investigator_unittest.cc
@@ -62,7 +62,6 @@
 
   ~AccountInvestigatorTest() override { investigator_.Shutdown(); }
 
-  FakeSigninManager* signin_manager() { return &signin_manager_; }
   identity::IdentityTestEnvironment* identity_test_env() {
     return &identity_test_env_;
   }
diff --git a/content/browser/appcache/appcache_url_loader_request.cc b/content/browser/appcache/appcache_url_loader_request.cc
index 75bd7798..0bd5d69 100644
--- a/content/browser/appcache/appcache_url_loader_request.cc
+++ b/content/browser/appcache/appcache_url_loader_request.cc
@@ -79,6 +79,7 @@
   bool not_used_clear_body;
   net::RedirectUtil::UpdateHttpRequest(
       request_.url, request_.method, redirect_info,
+      base::nullopt /* removed_request_headers */,
       base::nullopt /* modified_request_headers */, &request_.headers,
       &not_used_clear_body);
   request_.url = redirect_info.new_url;
diff --git a/content/browser/devtools/devtools_url_loader_interceptor.cc b/content/browser/devtools/devtools_url_loader_interceptor.cc
index c908d3c5..0a9d90f 100644
--- a/content/browser/devtools/devtools_url_loader_interceptor.cc
+++ b/content/browser/devtools/devtools_url_loader_interceptor.cc
@@ -299,9 +299,8 @@
 
   // network::mojom::URLLoader methods
   void FollowRedirect(
-      const base::Optional<std::vector<std::string>>&
-          to_be_removed_request_headers,
-      const base::Optional<net::HttpRequestHeaders>& modified_request_headers,
+      const base::Optional<std::vector<std::string>>& removed_headers,
+      const base::Optional<net::HttpRequestHeaders>& modified_headers,
       const base::Optional<GURL>& new_url) override;
   void ProceedWithResponse() override;
   void SetPriority(net::RequestPriority priority,
@@ -1286,13 +1285,15 @@
 
 // URLLoader methods
 void InterceptionJob::FollowRedirect(
-    const base::Optional<std::vector<std::string>>&
-        to_be_removed_request_headers,
-    const base::Optional<net::HttpRequestHeaders>& modified_request_headers,
+    const base::Optional<std::vector<std::string>>& removed_headers,
+    const base::Optional<net::HttpRequestHeaders>& modified_headers,
     const base::Optional<GURL>& new_url) {
-  DCHECK(!modified_request_headers.has_value()) << "Redirect with modified "
-                                                   "headers was not supported "
-                                                   "yet. crbug.com/845683";
+  // TODO(arthursonzogni, juncai): This seems to be correctly implemented, but
+  // not used nor tested so far. Add tests and remove this DCHECK to support
+  // this feature if needed. See https://crbug.com/845683.
+  DCHECK(!removed_headers && !modified_headers)
+      << "Redirect with removed or modified headers is not supported yet. See "
+         "https://crbug.com/845683";
   DCHECK(!new_url.has_value()) << "Redirect with modified url was not "
                                   "supported yet. crbug.com/845683";
   DCHECK(!waiting_for_resolution_);
@@ -1301,10 +1302,9 @@
   const net::RedirectInfo& info = *response_metadata_->redirect_info;
 
   bool clear_body = false;
-  net::RedirectUtil::UpdateHttpRequest(
-      request->url, request->method, info,
-      base::nullopt /* modified_request_headers */, &request->headers,
-      &clear_body);
+  net::RedirectUtil::UpdateHttpRequest(request->url, request->method, info,
+                                       removed_headers, modified_headers,
+                                       &request->headers, &clear_body);
   if (clear_body)
     request->request_body = nullptr;
   request->method = info.new_method;
@@ -1334,7 +1334,8 @@
   }
   if (state_ == State::kRedirectReceived) {
     state_ = State::kRequestSent;
-    loader_->FollowRedirect(base::nullopt, base::nullopt, base::nullopt);
+    loader_->FollowRedirect(removed_headers, modified_headers,
+                            base::nullopt /* new_url */);
     return;
   }
 
diff --git a/content/browser/frame_host/form_submission_throttle_browsertest.cc b/content/browser/frame_host/form_submission_throttle_browsertest.cc
index d45fa855..60f3de7 100644
--- a/content/browser/frame_host/form_submission_throttle_browsertest.cc
+++ b/content/browser/frame_host/form_submission_throttle_browsertest.cc
@@ -63,6 +63,7 @@
                               ->root();
     std::unique_ptr<NavigationHandle> handle = NavigationHandleImpl::Create(
         test.form_page_url,      // url
+        base::nullopt,           // initiator_origin
         std::vector<GURL>(),     // redirect chain
         root,                    // frame_tree_node
         true,                    // is_renderer_initiated
@@ -100,6 +101,7 @@
                             ->root();
   std::unique_ptr<NavigationHandle> handle = NavigationHandleImpl::Create(
       form_url,                      // url
+      base::nullopt,                 // initiator_origin
       std::vector<GURL>(),           // redirect chain
       root,                          // frame_tree_node
       true,                          // is_renderer_initiated
diff --git a/content/browser/frame_host/navigation_handle_impl.cc b/content/browser/frame_host/navigation_handle_impl.cc
index b8e933d..395a89ce 100644
--- a/content/browser/frame_host/navigation_handle_impl.cc
+++ b/content/browser/frame_host/navigation_handle_impl.cc
@@ -145,6 +145,7 @@
 // static
 std::unique_ptr<NavigationHandleImpl> NavigationHandleImpl::Create(
     const GURL& url,
+    const base::Optional<url::Origin>& initiator_origin,
     const std::vector<GURL>& redirect_chain,
     FrameTreeNode* frame_tree_node,
     bool is_renderer_initiated,
@@ -167,17 +168,19 @@
     const std::string& href_translate,
     base::TimeTicks input_start) {
   return std::unique_ptr<NavigationHandleImpl>(new NavigationHandleImpl(
-      url, redirect_chain, frame_tree_node, is_renderer_initiated,
-      is_same_document, navigation_start, pending_nav_entry_id,
-      started_from_context_menu, should_check_main_world_csp,
-      is_form_submission, std::move(navigation_ui_data), method,
-      std::move(request_headers), resource_request_body, sanitized_referrer,
-      has_user_gesture, transition, is_external_protocol, request_context_type,
-      mixed_content_context_type, href_translate, input_start));
+      url, initiator_origin, redirect_chain, frame_tree_node,
+      is_renderer_initiated, is_same_document, navigation_start,
+      pending_nav_entry_id, started_from_context_menu,
+      should_check_main_world_csp, is_form_submission,
+      std::move(navigation_ui_data), method, std::move(request_headers),
+      resource_request_body, sanitized_referrer, has_user_gesture, transition,
+      is_external_protocol, request_context_type, mixed_content_context_type,
+      href_translate, input_start));
 }
 
 NavigationHandleImpl::NavigationHandleImpl(
     const GURL& url,
+    const base::Optional<url::Origin>& initiator_origin,
     const std::vector<GURL>& redirect_chain,
     FrameTreeNode* frame_tree_node,
     bool is_renderer_initiated,
@@ -200,6 +203,7 @@
     const std::string& href_translate,
     base::TimeTicks input_start)
     : url_(url),
+      initiator_origin_(initiator_origin),
       has_user_gesture_(has_user_gesture),
       transition_(transition),
       is_external_protocol_(is_external_protocol),
@@ -663,6 +667,10 @@
   return href_translate_;
 }
 
+const base::Optional<url::Origin>& NavigationHandleImpl::GetInitiatorOrigin() {
+  return initiator_origin_;
+}
+
 bool NavigationHandleImpl::IsSignedExchangeInnerResponse() {
   return is_signed_exchange_inner_response_;
 }
diff --git a/content/browser/frame_host/navigation_handle_impl.h b/content/browser/frame_host/navigation_handle_impl.h
index 2941010..d3de646 100644
--- a/content/browser/frame_host/navigation_handle_impl.h
+++ b/content/browser/frame_host/navigation_handle_impl.h
@@ -57,6 +57,7 @@
   // this navigation.
   static std::unique_ptr<NavigationHandleImpl> Create(
       const GURL& url,
+      const base::Optional<url::Origin>& initiator_origin,
       const std::vector<GURL>& redirect_chain,
       FrameTreeNode* frame_tree_node,
       bool is_renderer_initiated,
@@ -169,6 +170,7 @@
   bool WasResponseCached() override;
   const net::ProxyServer& GetProxyServer() override;
   const std::string& GetHrefTranslate() override;
+  const base::Optional<url::Origin>& GetInitiatorOrigin() override;
 
   const std::string& origin_policy() const { return origin_policy_; }
   void set_origin_policy(const std::string& origin_policy) {
@@ -393,6 +395,7 @@
 
   NavigationHandleImpl(
       const GURL& url,
+      const base::Optional<url::Origin>& initiator_origin,
       const std::vector<GURL>& redirect_chain,
       FrameTreeNode* frame_tree_node,
       bool is_renderer_initiated,
@@ -467,6 +470,7 @@
   // See NavigationHandle for a description of those member variables.
   GURL url_;
   scoped_refptr<SiteInstance> starting_site_instance_;
+  base::Optional<url::Origin> initiator_origin_;
   Referrer sanitized_referrer_;
   bool has_user_gesture_;
   ui::PageTransition transition_;
diff --git a/content/browser/frame_host/navigation_handle_impl_unittest.cc b/content/browser/frame_host/navigation_handle_impl_unittest.cc
index 277f4788..401646f 100644
--- a/content/browser/frame_host/navigation_handle_impl_unittest.cc
+++ b/content/browser/frame_host/navigation_handle_impl_unittest.cc
@@ -250,7 +250,8 @@
 
   void CreateNavigationHandle() {
     test_handle_ = NavigationHandleImpl::Create(
-        GURL(), std::vector<GURL>(), main_test_rfh()->frame_tree_node(),
+        GURL(), base::nullopt, std::vector<GURL>(),
+        main_test_rfh()->frame_tree_node(),
         true,   // is_renderer_initiated
         false,  // is_same_document
         base::TimeTicks::Now(), 0,
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index df434ea..357f1cc 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -738,8 +738,8 @@
 
   std::unique_ptr<NavigationHandleImpl> navigation_handle =
       NavigationHandleImpl::Create(
-          common_params_.url, redirect_chain, frame_tree_node_,
-          !browser_initiated_,
+          common_params_.url, common_params_.initiator_origin, redirect_chain,
+          frame_tree_node_, !browser_initiated_,
           FrameMsg_Navigate_Type::IsSameDocument(
               common_params_.navigation_type),
           common_params_.navigation_start, nav_entry_id_,
diff --git a/content/browser/frame_host/origin_policy_throttle_unittest.cc b/content/browser/frame_host/origin_policy_throttle_unittest.cc
index 067d00b7..deaa838 100644
--- a/content/browser/frame_host/origin_policy_throttle_unittest.cc
+++ b/content/browser/frame_host/origin_policy_throttle_unittest.cc
@@ -48,6 +48,7 @@
     // NavigationHandleImplTest::CreateNavigationHandle.
     nav_handle_ = NavigationHandleImpl::Create(
         url,                  // url, as requested by caller
+        base::nullopt,        // initiator_origin
         std::vector<GURL>(),  // redirect chain
         static_cast<WebContentsImpl*>(web_contents())
             ->GetFrameTree()
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 6bbc97b..40e3e2f 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -3624,7 +3624,7 @@
       GetContentClient()->browser()->CanCreateWindow(
           this, GetLastCommittedURL(),
           frame_tree_node_->frame_tree()->GetMainFrame()->GetLastCommittedURL(),
-          last_committed_origin_.GetURL(), params->window_container_type,
+          last_committed_origin_, params->window_container_type,
           params->target_url, params->referrer.To<Referrer>(),
           params->frame_name, params->disposition, *params->features,
           effective_transient_activation_state, params->opener_suppressed,
diff --git a/content/browser/loader/cross_site_document_resource_handler.cc b/content/browser/loader/cross_site_document_resource_handler.cc
index c1bc001..2bc9d8d 100644
--- a/content/browser/loader/cross_site_document_resource_handler.cc
+++ b/content/browser/loader/cross_site_document_resource_handler.cc
@@ -168,11 +168,13 @@
     }
   }
 
-  void ResumeForRedirect(const base::Optional<net::HttpRequestHeaders>&
-                             modified_request_headers) override {
-    DCHECK(!modified_request_headers.has_value())
-        << "Redirect with modified headers was not supported yet. "
-           "crbug.com/845683";
+  void ResumeForRedirect(
+      const base::Optional<std::vector<std::string>>& removed_request_headers,
+      const base::Optional<net::HttpRequestHeaders>& modified_request_headers)
+      override {
+    DCHECK(!modified_request_headers && !removed_request_headers)
+        << "Redirect with removed or modified headers is not supported yet. "
+           "See https://crbug.com/845683";
     Resume();
   }
 
diff --git a/content/browser/loader/detachable_resource_handler.cc b/content/browser/loader/detachable_resource_handler.cc
index fb8c1c16..b4ac539 100644
--- a/content/browser/loader/detachable_resource_handler.cc
+++ b/content/browser/loader/detachable_resource_handler.cc
@@ -38,10 +38,12 @@
     detachable_handler_->ResumeInternal();
   }
 
-  void ResumeForRedirect(const base::Optional<net::HttpRequestHeaders>&
-                             modified_request_headers) override {
+  void ResumeForRedirect(
+      const base::Optional<std::vector<std::string>>& removed_headers,
+      const base::Optional<net::HttpRequestHeaders>& modified_headers)
+      override {
     MarkAsUsed();
-    detachable_handler_->ResumeForRedirect(modified_request_headers);
+    detachable_handler_->ResumeForRedirect(removed_headers, modified_headers);
   }
 
   void Cancel() override {
diff --git a/content/browser/loader/intercepting_resource_handler.cc b/content/browser/loader/intercepting_resource_handler.cc
index 2037c70..f2b71a3 100644
--- a/content/browser/loader/intercepting_resource_handler.cc
+++ b/content/browser/loader/intercepting_resource_handler.cc
@@ -29,11 +29,13 @@
     intercepting_handler_->ResumeInternal();
   }
 
-  void ResumeForRedirect(const base::Optional<net::HttpRequestHeaders>&
-                             modified_request_headers) override {
-    DCHECK(!modified_request_headers.has_value())
-        << "Redirect with modified headers was not supported yet. "
-           "crbug.com/845683";
+  void ResumeForRedirect(
+      const base::Optional<std::vector<std::string>>& removed_headers,
+      const base::Optional<net::HttpRequestHeaders>& modified_headers)
+      override {
+    DCHECK(!removed_headers && !modified_headers)
+        << "Removing or modifying headers from the |new_handler| is not used "
+           "and not supported. See https://crbug.com/845683.";
     Resume();
   }
 
diff --git a/content/browser/loader/mime_sniffing_resource_handler.cc b/content/browser/loader/mime_sniffing_resource_handler.cc
index b228a7d..9c74e93 100644
--- a/content/browser/loader/mime_sniffing_resource_handler.cc
+++ b/content/browser/loader/mime_sniffing_resource_handler.cc
@@ -74,11 +74,13 @@
     mime_handler_->ResumeInternal();
   }
 
-  void ResumeForRedirect(const base::Optional<net::HttpRequestHeaders>&
-                             modified_request_headers) override {
-    DCHECK(!modified_request_headers.has_value())
-        << "Redirect with modified headers was not supported yet. "
-           "crbug.com/845683";
+  void ResumeForRedirect(
+      const base::Optional<std::vector<std::string>>& removed_headers,
+      const base::Optional<net::HttpRequestHeaders>& modified_headers)
+      override {
+    DCHECK(!removed_headers && !modified_headers)
+        << "Redirect with removed or modified headers is not used nor "
+           "supported. See https://crbug.com/845683.";
     Resume();
   }
 
diff --git a/content/browser/loader/mock_resource_loader.cc b/content/browser/loader/mock_resource_loader.cc
index e43c857..c6f627f 100644
--- a/content/browser/loader/mock_resource_loader.cc
+++ b/content/browser/loader/mock_resource_loader.cc
@@ -25,11 +25,13 @@
 
   void Resume() override { mock_loader_->OnResume(); }
 
-  void ResumeForRedirect(const base::Optional<net::HttpRequestHeaders>&
-                             modified_request_headers) override {
-    DCHECK(!modified_request_headers.has_value())
-        << "Redirect with modified headers was not supported yet. "
-           "crbug.com/845683";
+  void ResumeForRedirect(
+      const base::Optional<std::vector<std::string>>& removed_headers,
+      const base::Optional<net::HttpRequestHeaders>& modified_headers)
+      override {
+    DCHECK(!removed_headers && !modified_headers)
+        << "Redirect with removed or modified headers is not supported yet. "
+           "See https://crbug.com/845683";
     Resume();
   }
 
diff --git a/content/browser/loader/mojo_async_resource_handler.cc b/content/browser/loader/mojo_async_resource_handler.cc
index 97192a6..f50e583 100644
--- a/content/browser/loader/mojo_async_resource_handler.cc
+++ b/content/browser/loader/mojo_async_resource_handler.cc
@@ -385,9 +385,8 @@
 }
 
 void MojoAsyncResourceHandler::FollowRedirect(
-    const base::Optional<std::vector<std::string>>&
-        to_be_removed_request_headers,
-    const base::Optional<net::HttpRequestHeaders>& modified_request_headers,
+    const base::Optional<std::vector<std::string>>& removed_headers,
+    const base::Optional<net::HttpRequestHeaders>& modified_headers,
     const base::Optional<GURL>& new_url) {
   if (new_url) {
     ReportBadMessage(
@@ -410,7 +409,7 @@
   DCHECK(!did_defer_on_writing_);
   did_defer_on_redirect_ = false;
   request()->LogUnblocked();
-  ResumeForRedirect(modified_request_headers);
+  ResumeForRedirect(removed_headers, modified_headers);
 }
 
 void MojoAsyncResourceHandler::ProceedWithResponse() {
diff --git a/content/browser/loader/mojo_async_resource_handler.h b/content/browser/loader/mojo_async_resource_handler.h
index b1b01b6..38759f1 100644
--- a/content/browser/loader/mojo_async_resource_handler.h
+++ b/content/browser/loader/mojo_async_resource_handler.h
@@ -85,9 +85,8 @@
 
   // network::mojom::URLLoader implementation:
   void FollowRedirect(
-      const base::Optional<std::vector<std::string>>&
-          to_be_removed_request_headers,
-      const base::Optional<net::HttpRequestHeaders>& modified_request_headers,
+      const base::Optional<std::vector<std::string>>& removed_headers,
+      const base::Optional<net::HttpRequestHeaders>& modified_headers,
       const base::Optional<GURL>& new_url) override;
   void ProceedWithResponse() override;
   void SetPriority(net::RequestPriority priority,
diff --git a/content/browser/loader/navigation_url_loader_impl.cc b/content/browser/loader/navigation_url_loader_impl.cc
index bc1aa36..e27b972 100644
--- a/content/browser/loader/navigation_url_loader_impl.cc
+++ b/content/browser/loader/navigation_url_loader_impl.cc
@@ -884,8 +884,8 @@
     // let the loader just follow the redirect.
     if (url_loader_) {
       DCHECK(!redirect_info_.new_url.is_empty());
-      url_loader_->FollowRedirect(
-          std::move(url_loader_modified_request_headers_));
+      url_loader_->FollowRedirect(std::move(url_loader_removed_headers_),
+                                  std::move(url_loader_modified_headers_));
       return;
     }
 
@@ -1039,7 +1039,8 @@
   }
 
   void FollowRedirect(
-      const base::Optional<net::HttpRequestHeaders>& modified_request_headers) {
+      const base::Optional<std::vector<std::string>>& removed_headers,
+      const base::Optional<net::HttpRequestHeaders>& modified_headers) {
     DCHECK_CURRENTLY_ON(BrowserThread::IO);
     DCHECK(!redirect_info_.new_url.is_empty());
     if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) {
@@ -1051,7 +1052,7 @@
     }
 
     if (!IsLoaderInterceptionEnabled()) {
-      url_loader_->FollowRedirect(modified_request_headers);
+      url_loader_->FollowRedirect(removed_headers, modified_headers);
       return;
     }
 
@@ -1067,7 +1068,7 @@
     bool should_clear_upload = false;
     net::RedirectUtil::UpdateHttpRequest(
         resource_request_->url, resource_request_->method, redirect_info_,
-        modified_request_headers, &resource_request_->headers,
+        removed_headers, modified_headers, &resource_request_->headers,
         &should_clear_upload);
     if (should_clear_upload) {
       // The request body is no longer applicable.
@@ -1085,7 +1086,8 @@
 
     // Need to cache modified headers for |url_loader_| since it doesn't use
     // |resource_request_| during redirect.
-    url_loader_modified_request_headers_ = modified_request_headers;
+    url_loader_removed_headers_ = removed_headers;
+    url_loader_modified_headers_ = modified_headers;
 
     if (signed_exchange_utils::NeedToCheckRedirectedURLForAcceptHeader()) {
       // Currently we send the SignedExchange accept header only for the limited
@@ -1094,16 +1096,16 @@
       // header on when redirecting to the origins in the OriginList of
       // SignedHTTPExchangeAcceptHeader field trial, and need to remove it when
       // redirecting to out of the OriginList.
-      if (!url_loader_modified_request_headers_)
-        url_loader_modified_request_headers_ = net::HttpRequestHeaders();
+      if (!url_loader_modified_headers_)
+        url_loader_modified_headers_ = net::HttpRequestHeaders();
       std::string accept_value = network::kFrameAcceptHeader;
       if (signed_exchange_utils::ShouldAdvertiseAcceptHeader(
               url::Origin::Create(resource_request_->url))) {
         DCHECK(!accept_value.empty());
         accept_value.append(kAcceptHeaderSignedExchangeSuffix);
       }
-      url_loader_modified_request_headers_->SetHeader(network::kAcceptHeader,
-                                                      accept_value);
+      url_loader_modified_headers_->SetHeader(network::kAcceptHeader,
+                                              accept_value);
       resource_request_->headers.SetHeader(network::kAcceptHeader,
                                            accept_value);
     }
@@ -1489,7 +1491,8 @@
 
   // Caches the modified request headers provided by clients during redirect,
   // will be consumed by next |url_loader_->FollowRedirect()|.
-  base::Optional<net::HttpRequestHeaders> url_loader_modified_request_headers_;
+  base::Optional<std::vector<std::string>> url_loader_removed_headers_;
+  base::Optional<net::HttpRequestHeaders> url_loader_modified_headers_;
 
   BlobHandles blob_handles_;
   std::vector<GURL> url_chain_;
@@ -1758,14 +1761,13 @@
 }
 
 void NavigationURLLoaderImpl::FollowRedirect(
-    const base::Optional<std::vector<std::string>>&
-        to_be_removed_request_headers,
-    const base::Optional<net::HttpRequestHeaders>& modified_request_headers) {
+    const base::Optional<std::vector<std::string>>& removed_headers,
+    const base::Optional<net::HttpRequestHeaders>& modified_headers) {
   base::PostTaskWithTraits(
       FROM_HERE, {BrowserThread::IO},
       base::BindOnce(&URLLoaderRequestController::FollowRedirect,
                      base::Unretained(request_controller_.get()),
-                     modified_request_headers));
+                     removed_headers, modified_headers));
 }
 
 void NavigationURLLoaderImpl::ProceedWithResponse() {}
diff --git a/content/browser/loader/null_resource_controller.cc b/content/browser/loader/null_resource_controller.cc
index 8ac719d3..385864b 100644
--- a/content/browser/loader/null_resource_controller.cc
+++ b/content/browser/loader/null_resource_controller.cc
@@ -29,10 +29,11 @@
 }
 
 void NullResourceController::ResumeForRedirect(
-    const base::Optional<net::HttpRequestHeaders>& modified_request_headers) {
-  DCHECK(!modified_request_headers.has_value()) << "Redirect with modified "
-                                                   "headers was not supported "
-                                                   "yet. crbug.com/845683";
+    const base::Optional<std::vector<std::string>>& removed_headers,
+    const base::Optional<net::HttpRequestHeaders>& modified_headers) {
+  DCHECK(!removed_headers && !modified_headers)
+      << "Redirect with removed or modified headers is not supported yet. See "
+         "https://crbug.com/845683";
   Resume();
 }
 
diff --git a/content/browser/loader/null_resource_controller.h b/content/browser/loader/null_resource_controller.h
index 66c9e03..8df0468 100644
--- a/content/browser/loader/null_resource_controller.h
+++ b/content/browser/loader/null_resource_controller.h
@@ -23,8 +23,9 @@
   void Cancel() override;
   void CancelWithError(int error_code) override;
   void Resume() override;
-  void ResumeForRedirect(const base::Optional<net::HttpRequestHeaders>&
-                             modified_request_headers) override;
+  void ResumeForRedirect(
+      const base::Optional<std::vector<std::string>>& removed_headers,
+      const base::Optional<net::HttpRequestHeaders>& modified_headers) override;
 
  private:
   bool* was_resumed_;
diff --git a/content/browser/loader/resource_controller.h b/content/browser/loader/resource_controller.h
index 55ecd8c..0f4bbb8 100644
--- a/content/browser/loader/resource_controller.h
+++ b/content/browser/loader/resource_controller.h
@@ -32,10 +32,11 @@
   virtual void Resume() = 0;
 
   // Similar to |Resume()| but can only be called if the request was previously
-  // redirected. |modified_request_headers| are changes applied to the request
-  // headers after updating them for the redirect.
-  virtual void ResumeForRedirect(const base::Optional<net::HttpRequestHeaders>&
-                                     modified_request_headers) = 0;
+  // redirected. |removed_headers| and |modified_headers| are
+  // applied to the request header after updating them for the redirect.
+  virtual void ResumeForRedirect(
+      const base::Optional<std::vector<std::string>>& removed_headers,
+      const base::Optional<net::HttpRequestHeaders>& modified_headers) = 0;
 };
 
 }  // namespace content
diff --git a/content/browser/loader/resource_handler.cc b/content/browser/loader/resource_handler.cc
index c58d7641e..86455da 100644
--- a/content/browser/loader/resource_handler.cc
+++ b/content/browser/loader/resource_handler.cc
@@ -45,8 +45,9 @@
 }
 
 void ResourceHandler::ResumeForRedirect(
-    const base::Optional<net::HttpRequestHeaders>& modified_request_headers) {
-  ReleaseController()->ResumeForRedirect(modified_request_headers);
+    const base::Optional<std::vector<std::string>>& removed_headers,
+    const base::Optional<net::HttpRequestHeaders>& modified_headers) {
+  ReleaseController()->ResumeForRedirect(removed_headers, modified_headers);
 }
 
 void ResourceHandler::Cancel() {
diff --git a/content/browser/loader/resource_handler.h b/content/browser/loader/resource_handler.h
index bb60dbe0..23b4ee6e 100644
--- a/content/browser/loader/resource_handler.h
+++ b/content/browser/loader/resource_handler.h
@@ -160,7 +160,8 @@
   // passed to HoldController and then destroy it.
   void Resume();
   void ResumeForRedirect(
-      const base::Optional<net::HttpRequestHeaders>& modified_request_headers);
+      const base::Optional<std::vector<std::string>>& removed_headers,
+      const base::Optional<net::HttpRequestHeaders>& modified_headers);
   void Cancel();
   void CancelWithError(int error_code);
 
diff --git a/content/browser/loader/resource_loader.cc b/content/browser/loader/resource_loader.cc
index 12c0aea4..756a4a3 100644
--- a/content/browser/loader/resource_loader.cc
+++ b/content/browser/loader/resource_loader.cc
@@ -157,14 +157,16 @@
   void Resume() override {
     MarkAsUsed();
     resource_loader_->Resume(true /* called_from_resource_controller */,
-                             base::nullopt);
+                             base::nullopt, base::nullopt);
   }
 
-  void ResumeForRedirect(const base::Optional<net::HttpRequestHeaders>&
-                             modified_request_headers) override {
+  void ResumeForRedirect(
+      const base::Optional<std::vector<std::string>>& removed_headers,
+      const base::Optional<net::HttpRequestHeaders>& modified_headers)
+      override {
     MarkAsUsed();
     resource_loader_->Resume(true /* called_from_resource_controller */,
-                             modified_request_headers);
+                             removed_headers, modified_headers);
   }
 
   void Cancel() override {
@@ -222,7 +224,7 @@
     // anything. Go ahead and resume the request now.
     if (old_deferred_stage == DEFERRED_NONE)
       resource_loader_->Resume(false /* called_from_resource_controller */,
-                               base::nullopt);
+                               base::nullopt, base::nullopt);
   }
 
  private:
@@ -530,11 +532,12 @@
 
 void ResourceLoader::Resume(
     bool called_from_resource_controller,
-    const base::Optional<net::HttpRequestHeaders>& modified_request_headers) {
+    const base::Optional<std::vector<std::string>>& removed_headers,
+    const base::Optional<net::HttpRequestHeaders>& modified_headers) {
   DeferredStage stage = deferred_stage_;
   deferred_stage_ = DEFERRED_NONE;
-  DCHECK(!modified_request_headers.has_value() || stage == DEFERRED_REDIRECT)
-      << "modified_request_headers can only be used with redirects";
+  DCHECK((!modified_headers && !removed_headers) || stage == DEFERRED_REDIRECT)
+      << "Modifying or removing headers can only be used with redirects";
   switch (stage) {
     case DEFERRED_NONE:
       NOTREACHED();
@@ -553,7 +556,7 @@
       // URLRequest::Start completes asynchronously, so starting the request now
       // won't result in synchronously calling into a ResourceHandler, if this
       // was called from Resume().
-      FollowDeferredRedirectInternal(modified_request_headers);
+      FollowDeferredRedirectInternal(removed_headers, modified_headers);
       break;
     case DEFERRED_ON_WILL_READ:
       // Always post a task, as synchronous resumes don't go through this
@@ -680,17 +683,20 @@
 }
 
 void ResourceLoader::FollowDeferredRedirectInternal(
-    const base::Optional<net::HttpRequestHeaders>& modified_request_headers) {
+    const base::Optional<std::vector<std::string>>& removed_headers,
+    const base::Optional<net::HttpRequestHeaders>& modified_headers) {
   DCHECK(!deferred_redirect_url_.is_empty());
   GURL redirect_url = deferred_redirect_url_;
   deferred_redirect_url_ = GURL();
   if (delegate_->HandleExternalProtocol(this, redirect_url)) {
-    DCHECK(!modified_request_headers.has_value())
-        << "ResourceLoaderDelegate::HandleExternalProtocol() with modified "
-           "headers was not supported yet. crbug.com/845683";
+    // Chrome doesn't make use of the request's headers to handle external
+    // protocol. Modifying headers here would be useless.
+    DCHECK(!modified_headers && !removed_headers)
+        << "Modifying or removing headers after a redirect to an external "
+           "protocol is not supported yet. See https://crbug.com/845683.";
     Cancel();
   } else {
-    request_->FollowDeferredRedirect(modified_request_headers);
+    request_->FollowDeferredRedirect(removed_headers, modified_headers);
   }
 }
 
diff --git a/content/browser/loader/resource_loader.h b/content/browser/loader/resource_loader.h
index bbd241e2..dbed666 100644
--- a/content/browser/loader/resource_loader.h
+++ b/content/browser/loader/resource_loader.h
@@ -98,17 +98,19 @@
   // |called_from_resource_controller| is true if called directly from a
   // ResourceController, in which case |resource_handler_| must not be invoked
   // or destroyed synchronously to avoid re-entrancy issues, and false
-  // otherwise. |modified_request_headers| is used for redirects only.
-  void Resume(
-      bool called_from_resource_controller,
-      const base::Optional<net::HttpRequestHeaders>& modified_request_headers);
+  // otherwise. |modified_headers| and |removed_headers| are used
+  // for redirects only.
+  void Resume(bool called_from_resource_controller,
+              const base::Optional<std::vector<std::string>>& removed_headers,
+              const base::Optional<net::HttpRequestHeaders>& modified_headers);
   void Cancel();
   void CancelWithError(int error_code);
 
   void StartRequestInternal();
   void CancelRequestInternal(int error, bool from_renderer);
   void FollowDeferredRedirectInternal(
-      const base::Optional<net::HttpRequestHeaders>& modified_request_headers);
+      const base::Optional<std::vector<std::string>>& removed_request_headers,
+      const base::Optional<net::HttpRequestHeaders>& modified_headers);
   void CompleteResponseStarted();
   // If |handle_result_async| is true, the result of the following read will be
   // handled asynchronously if it completes synchronously, unless it's EOF or an
diff --git a/content/browser/plugin_private_storage_helper.cc b/content/browser/plugin_private_storage_helper.cc
index 57f5b7e..a75e02c6 100644
--- a/content/browser/plugin_private_storage_helper.cc
+++ b/content/browser/plugin_private_storage_helper.cc
@@ -30,6 +30,7 @@
 #include "storage/browser/fileapi/file_system_context.h"
 #include "storage/browser/fileapi/isolated_context.h"
 #include "storage/browser/fileapi/obfuscated_file_util.h"
+#include "storage/browser/quota/special_storage_policy.h"
 #include "storage/common/fileapi/file_system_util.h"
 
 namespace content {
@@ -387,6 +388,8 @@
 void ClearPluginPrivateDataOnFileTaskRunner(
     scoped_refptr<storage::FileSystemContext> filesystem_context,
     const GURL& storage_origin,
+    const StoragePartition::OriginMatcherFunction& origin_matcher,
+    const scoped_refptr<storage::SpecialStoragePolicy>& special_storage_policy,
     const base::Time begin,
     const base::Time end,
     const base::Closure& callback) {
@@ -413,6 +416,8 @@
   // If a specific origin is provided, then check that it is in the list
   // returned and remove all the other origins.
   if (!storage_origin.is_empty()) {
+    DCHECK(origin_matcher.is_null()) << "Only 1 of |storage_origin| and "
+                                        "|origin_matcher| should be specified.";
     if (!base::ContainsKey(origins, storage_origin)) {
       // Nothing matches, so nothing to do.
       callback.Run();
@@ -424,6 +429,25 @@
     origins.insert(storage_origin);
   }
 
+  // If a filter is provided, determine which origins match.
+  if (!origin_matcher.is_null()) {
+    DCHECK(storage_origin.is_empty())
+        << "Only 1 of |storage_origin| and |origin_matcher| should be "
+           "specified.";
+    std::set<GURL> origins_to_check;
+    origins_to_check.swap(origins);
+    for (const auto& origin : origins_to_check) {
+      if (origin_matcher.Run(origin, special_storage_policy.get()))
+        origins.insert(origin);
+    }
+
+    // If no origins matched, there is nothing to do.
+    if (origins.empty()) {
+      callback.Run();
+      return;
+    }
+  }
+
   PluginPrivateDataDeletionHelper* helper = new PluginPrivateDataDeletionHelper(
       std::move(filesystem_context), begin, end, callback);
   helper->CheckOriginsOnFileTaskRunner(origins);
diff --git a/content/browser/plugin_private_storage_helper.h b/content/browser/plugin_private_storage_helper.h
index 2f92bfa..c204fd5 100644
--- a/content/browser/plugin_private_storage_helper.h
+++ b/content/browser/plugin_private_storage_helper.h
@@ -12,12 +12,14 @@
 #endif
 
 #include "base/callback_forward.h"
-#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_refptr.h"
 #include "base/time/time.h"
+#include "content/public/browser/storage_partition.h"
 #include "url/gurl.h"
 
 namespace storage {
 class FileSystemContext;
+class SpecialStoragePolicy;
 }
 
 namespace content {
@@ -30,6 +32,8 @@
 void ClearPluginPrivateDataOnFileTaskRunner(
     scoped_refptr<storage::FileSystemContext> filesystem_context,
     const GURL& storage_origin,
+    const StoragePartition::OriginMatcherFunction& origin_matcher,
+    const scoped_refptr<storage::SpecialStoragePolicy>& special_storage_policy,
     const base::Time begin,
     const base::Time end,
     const base::Closure& callback);
diff --git a/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc b/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc
index 0a31c03e..59bccbc2 100644
--- a/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc
+++ b/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc
@@ -224,6 +224,7 @@
       : FakeMediaStreamUIProxy(/*tests_use_fake_render_frame_hosts=*/true) {}
   void OnStarted(
       base::OnceClosure stop,
+      base::RepeatingClosure source,
       MediaStreamUIProxy::WindowIdCallback window_id_callback) override {
     // gmock cannot handle move-only types:
     MockOnStarted(base::AdaptCallbackForRepeating(std::move(stop)));
diff --git a/content/browser/renderer_host/media/media_stream_manager.cc b/content/browser/renderer_host/media/media_stream_manager.cc
index 53d03af..553836de 100644
--- a/content/browser/renderer_host/media/media_stream_manager.cc
+++ b/content/browser/renderer_host/media/media_stream_manager.cc
@@ -419,6 +419,7 @@
   const MediaDeviceSaltAndOrigin salt_and_origin;
 
   MediaStreamDevices devices;
+  MediaStreamDevices old_devices;
 
   // Callback to the requester which audio/video devices have been selected.
   // It can be null if the requester has no interest to know the result.
@@ -1124,22 +1125,12 @@
     if (request->video_type() == MEDIA_DISPLAY_VIDEO_CAPTURE) {
       devices.push_back(MediaStreamDeviceFromFakeDeviceConfig());
     } else if (request->video_type() == MEDIA_GUM_DESKTOP_VIDEO_CAPTURE) {
-      MediaStreamDevice device;
-      // Set media type according to request type for explicit expectation in
-      // the unit test and cache the |label| in the device name field.
-      // These are for unit test purpose only.
-      if (request->request_type() == MEDIA_DEVICE_UPDATE) {
-        DesktopMediaID media_id(DesktopMediaID::TYPE_WINDOW,
-                                DesktopMediaID::kNullId);
-        device = MediaStreamDevice(MEDIA_GUM_DESKTOP_VIDEO_CAPTURE,
-                                   media_id.ToString(), label);
-      } else {
-        DesktopMediaID media_id(DesktopMediaID::TYPE_SCREEN,
-                                DesktopMediaID::kNullId);
-        device = MediaStreamDevice(MEDIA_GUM_DESKTOP_VIDEO_CAPTURE,
-                                   media_id.ToString(), label);
-      }
-      devices.push_back(device);
+      // Cache the |label| in the device name field, for unit test purpose only.
+      devices.push_back(MediaStreamDevice(
+          MEDIA_GUM_DESKTOP_VIDEO_CAPTURE,
+          DesktopMediaID(DesktopMediaID::TYPE_SCREEN, DesktopMediaID::kNullId)
+              .ToString(),
+          label));
     } else {
       MediaStreamDevices audio_devices = ConvertToMediaStreamDevices(
           request->audio_type(), enumeration[MEDIA_DEVICE_TYPE_AUDIO_INPUT]);
@@ -1508,6 +1499,32 @@
   }
 }
 
+void MediaStreamManager::FinalizeChangeDevice(const std::string& label,
+                                              DeviceRequest* request) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(request->device_changed_cb);
+
+  std::vector<std::vector<MediaStreamDevice>> old_devices_by_type(
+      NUM_MEDIA_TYPES);
+  for (const auto& old_device : request->old_devices)
+    old_devices_by_type[old_device.type].push_back(old_device);
+
+  for (const auto& new_device : request->devices) {
+    MediaStreamDevice old_device;
+    auto& old_devices = old_devices_by_type[new_device.type];
+    if (!old_devices.empty()) {
+      old_device = old_devices.back();
+      old_devices.pop_back();
+    }
+
+    request->device_changed_cb.Run(label, old_device, new_device);
+  }
+
+  for (const auto& old_devices : old_devices_by_type)
+    for (const auto& old_device : old_devices)
+      request->device_changed_cb.Run(label, old_device, MediaStreamDevice());
+}
+
 void MediaStreamManager::FinalizeMediaAccessRequest(
     const std::string& label,
     DeviceRequest* request,
@@ -1628,6 +1645,7 @@
       break;
     }
     case MEDIA_DEVICE_UPDATE:
+      FinalizeChangeDevice(label, request);
       OnStreamStarted(label);
       break;
     default:
@@ -1839,47 +1857,22 @@
     DeviceRequest* request,
     const MediaStreamDevices& devices) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK(request->device_changed_cb);
   DVLOG(1) << "HandleChangeSourceRequestResponse("
            << ", {label = " << label << "})";
 
-  MediaStreamDevices new_devices;
+  request->old_devices.clear();
+  request->old_devices.swap(request->devices);
+
   bool found_audio = false;
-  size_t device_count = std::max(devices.size(), request->devices.size());
-  for (size_t i = 0; i < device_count; i++) {
-    MediaStreamDevice old_device = MediaStreamDevice();
-    MediaStreamDevice new_device = MediaStreamDevice();
+  for (const MediaStreamDevice& media_stream_device : devices) {
+    MediaStreamDevice new_device = media_stream_device;
+    found_audio |= IsAudioInputMediaType(new_device.type);
 
-    if (i < devices.size()) {
-      new_device = devices[i];
-
-      if (IsAudioInputMediaType(new_device.type)) {
-        found_audio = true;
-      }
-    }
-
-    for (auto device_it = request->devices.begin();
-         device_it != request->devices.end(); ++device_it) {
-      if (new_device.type == MEDIA_NO_SERVICE ||
-          device_it->type == new_device.type) {
-        old_device = *device_it;
-        request->devices.erase(device_it);
-        break;
-      }
-    }
-
-    if (new_device.type != MEDIA_NO_SERVICE) {
-      new_device.session_id =
-          GetDeviceManager(new_device.type)->Open(new_device);
-      request->SetState(new_device.type, MEDIA_REQUEST_STATE_OPENING);
-      new_devices.push_back(new_device);
-    }
-
-    request->device_changed_cb.Run(label, old_device, new_device);
+    new_device.session_id = GetDeviceManager(new_device.type)->Open(new_device);
+    request->SetState(new_device.type, MEDIA_REQUEST_STATE_OPENING);
+    request->devices.push_back(new_device);
   }
 
-  request->devices.clear();
-  request->devices = new_devices;
   request->SetAudioType(found_audio ? request->controls.audio.stream_type
                                     : MEDIA_NO_SERVICE);
 }
@@ -2131,10 +2124,28 @@
   if (!request)
     return;
 
+  // Show "Change source" button on notification bar only for tab sharing by
+  // desktopCapture API.
+  bool enable_change_source = std::any_of(
+      request->devices.cbegin(), request->devices.cend(), [](auto device) {
+        DesktopMediaID media_id = DesktopMediaID::Parse(device.id);
+        return device.type == MEDIA_GUM_DESKTOP_VIDEO_CAPTURE &&
+               media_id.type == DesktopMediaID::TYPE_WEB_CONTENTS;
+      });
+
+  base::RepeatingClosure device_changed_cb;
+  if (enable_change_source &&
+      base::FeatureList::IsEnabled(features::kDesktopCaptureChangeSource)) {
+    device_changed_cb = base::BindRepeating(
+        &MediaStreamManager::ChangeMediaStreamSourceFromBrowser,
+        base::Unretained(this), label);
+  }
+
   if (request->ui_proxy) {
     request->ui_proxy->OnStarted(
         base::BindOnce(&MediaStreamManager::StopMediaStreamFromBrowser,
                        base::Unretained(this), label),
+        device_changed_cb,
         base::BindOnce(&MediaStreamManager::OnMediaStreamUIWindowId,
                        base::Unretained(this), request->video_type(),
                        request->devices));
diff --git a/content/browser/renderer_host/media/media_stream_manager.h b/content/browser/renderer_host/media/media_stream_manager.h
index 1f15318..a1033c4 100644
--- a/content/browser/renderer_host/media/media_stream_manager.h
+++ b/content/browser/renderer_host/media/media_stream_manager.h
@@ -430,6 +430,7 @@
                              DeviceRequest* request,
                              content::MediaStreamRequestResult result);
   void FinalizeOpenDevice(const std::string& label, DeviceRequest* request);
+  void FinalizeChangeDevice(const std::string& label, DeviceRequest* request);
   void FinalizeMediaAccessRequest(const std::string& label,
                                   DeviceRequest* request,
                                   const MediaStreamDevices& devices);
diff --git a/content/browser/renderer_host/media/media_stream_manager_unittest.cc b/content/browser/renderer_host/media/media_stream_manager_unittest.cc
index 8226fb9..8e2c9d6 100644
--- a/content/browser/renderer_host/media/media_stream_manager_unittest.cc
+++ b/content/browser/renderer_host/media/media_stream_manager_unittest.cc
@@ -249,6 +249,8 @@
 
 TEST_F(MediaStreamManagerTest, MakeMediaAccessRequest) {
   MakeMediaAccessRequest(0);
+  EXPECT_CALL(*media_observer_, OnMediaRequestStateChanged(_, _, _, _, _, _))
+      .Times(testing::AtLeast(1));
 
   // Expecting the callback will be triggered and quit the test.
   EXPECT_CALL(*this, Response(0));
@@ -522,7 +524,7 @@
       base::BindRepeating(
           [](const std::string& label, const MediaStreamDevice& device) {
             EXPECT_EQ(MEDIA_GUM_DESKTOP_VIDEO_CAPTURE, device.type);
-            EXPECT_EQ(DesktopMediaID::TYPE_SCREEN,
+            EXPECT_NE(DesktopMediaID::TYPE_NONE,
                       DesktopMediaID::Parse(device.id).type);
           });
   MediaStreamManager::DeviceChangedCallback changed_callback;
@@ -536,7 +538,7 @@
       std::move(changed_callback));
   run_loop_.Run();
   EXPECT_EQ(controls.video.stream_type, video_device.type);
-  EXPECT_EQ(DesktopMediaID::TYPE_SCREEN,
+  EXPECT_NE(DesktopMediaID::TYPE_NONE,
             DesktopMediaID::Parse(video_device.id).type);
 
   // |request_label| is cached in the |device.name| for testing purpose.
@@ -581,10 +583,10 @@
              const MediaStreamDevice& old_device,
              const MediaStreamDevice& new_device) {
             EXPECT_EQ(MEDIA_GUM_DESKTOP_VIDEO_CAPTURE, old_device.type);
-            EXPECT_EQ(DesktopMediaID::TYPE_SCREEN,
+            EXPECT_NE(DesktopMediaID::TYPE_NONE,
                       DesktopMediaID::Parse(old_device.id).type);
             EXPECT_EQ(MEDIA_GUM_DESKTOP_VIDEO_CAPTURE, new_device.type);
-            EXPECT_EQ(DesktopMediaID::TYPE_WINDOW,
+            EXPECT_NE(DesktopMediaID::TYPE_NONE,
                       DesktopMediaID::Parse(new_device.id).type);
             *video_device = new_device;
           },
@@ -599,7 +601,7 @@
       std::move(changed_callback));
   run_loop_.Run();
   EXPECT_EQ(controls.video.stream_type, video_device.type);
-  EXPECT_EQ(DesktopMediaID::TYPE_SCREEN,
+  EXPECT_NE(DesktopMediaID::TYPE_NONE,
             DesktopMediaID::Parse(video_device.id).type);
 
   // |request_label| is cached in the |device.name| for testing purpose.
diff --git a/content/browser/renderer_host/media/media_stream_ui_proxy.cc b/content/browser/renderer_host/media/media_stream_ui_proxy.cc
index 4db595a..0db7c26 100644
--- a/content/browser/renderer_host/media/media_stream_ui_proxy.cc
+++ b/content/browser/renderer_host/media/media_stream_ui_proxy.cc
@@ -65,7 +65,7 @@
   ~Core();
 
   void RequestAccess(std::unique_ptr<MediaStreamRequest> request);
-  void OnStarted(gfx::NativeViewId* window_id);
+  void OnStarted(gfx::NativeViewId* window_id, bool has_source_callback);
 
   void ProcessAccessRequestResponse(int render_process_id,
                                     int render_frame_id,
@@ -76,6 +76,7 @@
  private:
   friend class FakeMediaStreamUIProxy;
   void ProcessStopRequestFromUI();
+  void ProcessChangeSourceRequestFromUI();
   RenderFrameHostDelegate* GetRenderFrameHostDelegate(int render_process_id,
                                                       int render_frame_id);
 
@@ -127,11 +128,20 @@
                      request->render_frame_id));
 }
 
-void MediaStreamUIProxy::Core::OnStarted(gfx::NativeViewId* window_id) {
+void MediaStreamUIProxy::Core::OnStarted(gfx::NativeViewId* window_id,
+                                         bool has_source_callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  base::RepeatingClosure device_change_cb;
+  if (has_source_callback) {
+    device_change_cb = base::BindRepeating(
+        &Core::ProcessChangeSourceRequestFromUI, base::Unretained(this));
+  }
+
   if (ui_) {
     *window_id = ui_->OnStarted(
-        base::Bind(&Core::ProcessStopRequestFromUI, base::Unretained(this)));
+        base::BindOnce(&Core::ProcessStopRequestFromUI, base::Unretained(this)),
+        device_change_cb);
   }
 }
 
@@ -164,7 +174,9 @@
   if (filtered_devices.empty() && result == MEDIA_DEVICE_OK)
     result = MEDIA_DEVICE_PERMISSION_DENIED;
 
-  ui_ = std::move(stream_ui);
+  if (stream_ui)
+    ui_ = std::move(stream_ui);
+
   base::PostTaskWithTraits(
       FROM_HERE, {BrowserThread::IO},
       base::BindOnce(&MediaStreamUIProxy::ProcessAccessRequestResponse, proxy_,
@@ -179,6 +191,15 @@
       base::BindOnce(&MediaStreamUIProxy::ProcessStopRequestFromUI, proxy_));
 }
 
+void MediaStreamUIProxy::Core::ProcessChangeSourceRequestFromUI() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  base::PostTaskWithTraits(
+      FROM_HERE, {BrowserThread::IO},
+      base::BindOnce(&MediaStreamUIProxy::ProcessChangeSourceRequestFromUI,
+                     proxy_));
+}
+
 RenderFrameHostDelegate* MediaStreamUIProxy::Core::GetRenderFrameHostDelegate(
     int render_process_id,
     int render_frame_id) {
@@ -225,18 +246,20 @@
 }
 
 void MediaStreamUIProxy::OnStarted(base::OnceClosure stop_callback,
+                                   base::RepeatingClosure source_callback,
                                    WindowIdCallback window_id_callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   stop_callback_ = std::move(stop_callback);
+  source_callback_ = std::move(source_callback);
 
   // Owned by the PostTaskAndReply callback.
   gfx::NativeViewId* window_id = new gfx::NativeViewId(0);
 
   base::PostTaskWithTraitsAndReply(
       FROM_HERE, {BrowserThread::UI},
-      base::BindOnce(&Core::OnStarted, base::Unretained(core_.get()),
-                     window_id),
+      base::BindOnce(&Core::OnStarted, base::Unretained(core_.get()), window_id,
+                     !!source_callback_),
       base::BindOnce(&MediaStreamUIProxy::OnWindowId,
                      weak_factory_.GetWeakPtr(), std::move(window_id_callback),
                      base::Owned(window_id)));
@@ -258,6 +281,13 @@
   base::ResetAndReturn(&stop_callback_).Run();
 }
 
+void MediaStreamUIProxy::ProcessChangeSourceRequestFromUI() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  if (source_callback_)
+    source_callback_.Run();
+}
+
 void MediaStreamUIProxy::OnWindowId(WindowIdCallback window_id_callback,
                                     gfx::NativeViewId* window_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
@@ -347,6 +377,7 @@
 }
 
 void FakeMediaStreamUIProxy::OnStarted(base::OnceClosure stop_callback,
+                                       base::RepeatingClosure source_callback,
                                        WindowIdCallback window_id_callback) {}
 
 }  // namespace content
diff --git a/content/browser/renderer_host/media/media_stream_ui_proxy.h b/content/browser/renderer_host/media/media_stream_ui_proxy.h
index c1bafeba..6506ed1b 100644
--- a/content/browser/renderer_host/media/media_stream_ui_proxy.h
+++ b/content/browser/renderer_host/media/media_stream_ui_proxy.h
@@ -43,11 +43,15 @@
                              ResponseCallback response_callback);
 
   // Notifies the UI that the MediaStream has been started. Must be called after
-  // access has been approved using RequestAccess(). |stop_callback| is be
-  // called on the IO thread after the user has requests the stream to be
-  // stopped. |window_id_callback| is called on the IO thread with the platform-
+  // access has been approved using RequestAccess().
+  // |stop_callback| is be called on the IO thread after the user has requests
+  // the stream to be stopped.
+  // |source_callback| is be called on the IO thread after the user has requests
+  // the stream source to be changed.
+  // |window_id_callback| is called on the IO thread with the platform-
   // dependent window ID of the UI.
   virtual void OnStarted(base::OnceClosure stop_callback,
+                         base::RepeatingClosure source_callback,
                          WindowIdCallback window_id_callback);
 
  protected:
@@ -62,6 +66,7 @@
       const MediaStreamDevices& devices,
       content::MediaStreamRequestResult result);
   void ProcessStopRequestFromUI();
+  void ProcessChangeSourceRequestFromUI();
   void OnWindowId(WindowIdCallback window_id_callback,
                   gfx::NativeViewId* window_id);
   void OnCheckedAccess(base::Callback<void(bool)> callback, bool have_access);
@@ -69,6 +74,7 @@
   std::unique_ptr<Core, content::BrowserThread::DeleteOnUIThread> core_;
   ResponseCallback response_callback_;
   base::OnceClosure stop_callback_;
+  base::RepeatingClosure source_callback_;
 
   base::WeakPtrFactory<MediaStreamUIProxy> weak_factory_;
 
@@ -91,6 +97,7 @@
   void RequestAccess(std::unique_ptr<MediaStreamRequest> request,
                      ResponseCallback response_callback) override;
   void OnStarted(base::OnceClosure stop_callback,
+                 base::RepeatingClosure source_callback,
                  WindowIdCallback window_id_callback) override;
 
  private:
diff --git a/content/browser/renderer_host/media/media_stream_ui_proxy_unittest.cc b/content/browser/renderer_host/media/media_stream_ui_proxy_unittest.cc
index 73cdee4..d8d9313 100644
--- a/content/browser/renderer_host/media/media_stream_ui_proxy_unittest.cc
+++ b/content/browser/renderer_host/media/media_stream_ui_proxy_unittest.cc
@@ -52,7 +52,16 @@
 
 class MockMediaStreamUI : public MediaStreamUI {
  public:
-  MOCK_METHOD1(OnStarted, gfx::NativeViewId(const base::Closure& stop));
+  gfx::NativeViewId OnStarted(base::OnceClosure stop,
+                              base::RepeatingClosure source) override {
+    // gmock cannot handle move-only types:
+    return MockOnStarted(base::AdaptCallbackForRepeating(std::move(stop)),
+                         source);
+  }
+
+  MOCK_METHOD2(MockOnStarted,
+               gfx::NativeViewId(base::RepeatingClosure stop,
+                                 base::RepeatingClosure source));
 };
 
 class MockStopStreamHandler {
@@ -61,6 +70,10 @@
   MOCK_METHOD1(OnWindowId, void(gfx::NativeViewId window_id));
 };
 
+class MockChangeSourceStreamHandler {
+ public:
+  MOCK_METHOD0(OnChangeSource, void());
+};
 
 }  // namespace
 
@@ -95,26 +108,11 @@
     expected->video_type == arg.video_type;
 }
 
-// These tests are flaky on Linux. https://crbug.com/826483
-#if defined(OS_LINUX)
-#define MAYBE_DeleteBeforeAccepted DISABLED_DeleteBeforeAccepted
-#define MAYBE_Deny DISABLED_Deny
-#define MAYBE_AcceptAndStart DISABLED_AcceptAndStart
-#define MAYBE_StopFromUI DISABLED_StopFromUI
-#define MAYBE_WindowIdCallbackCalled DISABLED_WindowIdCallbackCalled
-#else
-#define MAYBE_DeleteBeforeAccepted DeleteBeforeAccepted
-#define MAYBE_Deny Deny
-#define MAYBE_AcceptAndStart AcceptAndStart
-#define MAYBE_StopFromUI StopFromUI
-#define MAYBE_WindowIdCallbackCalled WindowIdCallbackCalled
-#endif
-
-TEST_F(MediaStreamUIProxyTest, MAYBE_Deny) {
-  std::unique_ptr<MediaStreamRequest> request(new MediaStreamRequest(
+TEST_F(MediaStreamUIProxyTest, Deny) {
+  auto request = std::make_unique<MediaStreamRequest>(
       0, 0, 0, GURL("http://origin/"), false, MEDIA_GENERATE_STREAM,
       std::string(), std::string(), MEDIA_DEVICE_AUDIO_CAPTURE,
-      MEDIA_DEVICE_VIDEO_CAPTURE, false));
+      MEDIA_DEVICE_VIDEO_CAPTURE, false);
   MediaStreamRequest* request_ptr = request.get();
   proxy_->RequestAccess(
       std::move(request),
@@ -141,11 +139,11 @@
   EXPECT_TRUE(response.empty());
 }
 
-TEST_F(MediaStreamUIProxyTest, MAYBE_AcceptAndStart) {
-  std::unique_ptr<MediaStreamRequest> request(new MediaStreamRequest(
+TEST_F(MediaStreamUIProxyTest, AcceptAndStart) {
+  auto request = std::make_unique<MediaStreamRequest>(
       0, 0, 0, GURL("http://origin/"), false, MEDIA_GENERATE_STREAM,
       std::string(), std::string(), MEDIA_DEVICE_AUDIO_CAPTURE,
-      MEDIA_DEVICE_VIDEO_CAPTURE, false));
+      MEDIA_DEVICE_VIDEO_CAPTURE, false);
   MediaStreamRequest* request_ptr = request.get();
   proxy_->RequestAccess(
       std::move(request),
@@ -163,8 +161,8 @@
   MediaStreamDevices devices;
   devices.push_back(
       MediaStreamDevice(MEDIA_DEVICE_AUDIO_CAPTURE, "Mic", "Mic"));
-  std::unique_ptr<MockMediaStreamUI> ui(new MockMediaStreamUI());
-  EXPECT_CALL(*ui, OnStarted(_)).WillOnce(Return(0));
+  auto ui = std::make_unique<MockMediaStreamUI>();
+  EXPECT_CALL(*ui, MockOnStarted(_, _)).WillOnce(Return(0));
   std::move(callback).Run(devices, MEDIA_DEVICE_OK, std::move(ui));
 
   MediaStreamDevices response;
@@ -174,16 +172,17 @@
 
   EXPECT_FALSE(response.empty());
 
-  proxy_->OnStarted(base::Closure(), MediaStreamUIProxy::WindowIdCallback());
+  proxy_->OnStarted(base::OnceClosure(), base::RepeatingClosure(),
+                    MediaStreamUIProxy::WindowIdCallback());
   base::RunLoop().RunUntilIdle();
 }
 
 // Verify that the proxy can be deleted before the request is processed.
-TEST_F(MediaStreamUIProxyTest, MAYBE_DeleteBeforeAccepted) {
-  std::unique_ptr<MediaStreamRequest> request(new MediaStreamRequest(
+TEST_F(MediaStreamUIProxyTest, DeleteBeforeAccepted) {
+  auto request = std::make_unique<MediaStreamRequest>(
       0, 0, 0, GURL("http://origin/"), false, MEDIA_GENERATE_STREAM,
       std::string(), std::string(), MEDIA_DEVICE_AUDIO_CAPTURE,
-      MEDIA_DEVICE_VIDEO_CAPTURE, false));
+      MEDIA_DEVICE_VIDEO_CAPTURE, false);
   MediaStreamRequest* request_ptr = request.get();
   proxy_->RequestAccess(
       std::move(request),
@@ -205,11 +204,11 @@
   std::move(callback).Run(devices, MEDIA_DEVICE_OK, std::move(ui));
 }
 
-TEST_F(MediaStreamUIProxyTest, MAYBE_StopFromUI) {
-  std::unique_ptr<MediaStreamRequest> request(new MediaStreamRequest(
+TEST_F(MediaStreamUIProxyTest, StopFromUI) {
+  auto request = std::make_unique<MediaStreamRequest>(
       0, 0, 0, GURL("http://origin/"), false, MEDIA_GENERATE_STREAM,
       std::string(), std::string(), MEDIA_DEVICE_AUDIO_CAPTURE,
-      MEDIA_DEVICE_VIDEO_CAPTURE, false));
+      MEDIA_DEVICE_VIDEO_CAPTURE, false);
   MediaStreamRequest* request_ptr = request.get();
   proxy_->RequestAccess(
       std::move(request),
@@ -229,8 +228,8 @@
   MediaStreamDevices devices;
   devices.push_back(
       MediaStreamDevice(MEDIA_DEVICE_AUDIO_CAPTURE, "Mic", "Mic"));
-  std::unique_ptr<MockMediaStreamUI> ui(new MockMediaStreamUI());
-  EXPECT_CALL(*ui, OnStarted(_))
+  auto ui = std::make_unique<MockMediaStreamUI>();
+  EXPECT_CALL(*ui, MockOnStarted(_, _))
       .WillOnce(testing::DoAll(SaveArg<0>(&stop_callback), Return(0)));
   std::move(callback).Run(devices, MEDIA_DEVICE_OK, std::move(ui));
 
@@ -244,6 +243,7 @@
   MockStopStreamHandler stop_handler;
   proxy_->OnStarted(base::BindOnce(&MockStopStreamHandler::OnStop,
                                    base::Unretained(&stop_handler)),
+                    base::RepeatingClosure(),
                     MediaStreamUIProxy::WindowIdCallback());
   base::RunLoop().RunUntilIdle();
 
@@ -253,11 +253,11 @@
   base::RunLoop().RunUntilIdle();
 }
 
-TEST_F(MediaStreamUIProxyTest, MAYBE_WindowIdCallbackCalled) {
-  std::unique_ptr<MediaStreamRequest> request(new MediaStreamRequest(
+TEST_F(MediaStreamUIProxyTest, WindowIdCallbackCalled) {
+  auto request = std::make_unique<MediaStreamRequest>(
       0, 0, 0, GURL("http://origin/"), false, MEDIA_GENERATE_STREAM,
       std::string(), std::string(), MEDIA_NO_SERVICE,
-      MEDIA_GUM_DESKTOP_VIDEO_CAPTURE, false));
+      MEDIA_GUM_DESKTOP_VIDEO_CAPTURE, false);
   MediaStreamRequest* request_ptr = request.get();
 
   proxy_->RequestAccess(
@@ -273,8 +273,8 @@
   base::RunLoop().RunUntilIdle();
 
   const int kWindowId = 1;
-  std::unique_ptr<MockMediaStreamUI> ui(new MockMediaStreamUI());
-  EXPECT_CALL(*ui, OnStarted(_)).WillOnce(Return(kWindowId));
+  auto ui = std::make_unique<MockMediaStreamUI>();
+  EXPECT_CALL(*ui, MockOnStarted(_, _)).WillOnce(Return(kWindowId));
 
   std::move(callback).Run(MediaStreamDevices(), MEDIA_DEVICE_OK, std::move(ui));
   EXPECT_CALL(response_callback_, OnAccessRequestResponse(_, _));
@@ -284,11 +284,65 @@
 
   proxy_->OnStarted(base::BindOnce(&MockStopStreamHandler::OnStop,
                                    base::Unretained(&handler)),
+                    base::RepeatingClosure(),
                     base::BindOnce(&MockStopStreamHandler::OnWindowId,
                                    base::Unretained(&handler)));
   base::RunLoop().RunUntilIdle();
 }
 
+TEST_F(MediaStreamUIProxyTest, ChangeSourceFromUI) {
+  auto request = std::make_unique<MediaStreamRequest>(
+      0, 0, 0, GURL("http://origin/"), false, MEDIA_GENERATE_STREAM,
+      std::string(), std::string(), MEDIA_GUM_DESKTOP_AUDIO_CAPTURE,
+      MEDIA_GUM_DESKTOP_VIDEO_CAPTURE, false);
+  MediaStreamRequest* request_ptr = request.get();
+  proxy_->RequestAccess(
+      std::move(request),
+      base::BindOnce(&MockResponseCallback::OnAccessRequestResponse,
+                     base::Unretained(&response_callback_)));
+  MediaResponseCallback callback;
+  EXPECT_CALL(delegate_,
+              RequestMediaAccessPermission(SameRequest(request_ptr), _))
+      .WillOnce([&](testing::Unused, MediaResponseCallback* cb) {
+        callback = std::move(*cb);
+      });
+  base::RunLoop().RunUntilIdle();
+  ASSERT_FALSE(callback.is_null());
+
+  base::RepeatingClosure source_callback;
+
+  MediaStreamDevices devices;
+  devices.push_back(MediaStreamDevice(MEDIA_GUM_DESKTOP_VIDEO_CAPTURE,
+                                      "fake_desktop_video_device",
+                                      "Fake Desktop Video Device"));
+  auto ui = std::make_unique<MockMediaStreamUI>();
+  EXPECT_CALL(*ui, MockOnStarted(_, _))
+      .WillOnce(testing::DoAll(SaveArg<1>(&source_callback), Return(0)));
+  std::move(callback).Run(devices, MEDIA_DEVICE_OK, std::move(ui));
+
+  MediaStreamDevices response;
+  EXPECT_CALL(response_callback_, OnAccessRequestResponse(_, _))
+      .WillOnce(SaveArg<0>(&response));
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_FALSE(response.empty());
+
+  MockStopStreamHandler stop_handler;
+  MockChangeSourceStreamHandler source_handler;
+  proxy_->OnStarted(
+      base::BindOnce(&MockStopStreamHandler::OnStop,
+                     base::Unretained(&stop_handler)),
+      base::BindRepeating(&MockChangeSourceStreamHandler::OnChangeSource,
+                          base::Unretained(&source_handler)),
+      MediaStreamUIProxy::WindowIdCallback());
+  base::RunLoop().RunUntilIdle();
+
+  ASSERT_FALSE(source_callback.is_null());
+  EXPECT_CALL(source_handler, OnChangeSource());
+  source_callback.Run();
+  base::RunLoop().RunUntilIdle();
+}
+
 // Basic tests for feature policy checks through the MediaStreamUIProxy. These
 // tests are not meant to cover every edge case as the FeaturePolicy class
 // itself is tested thoroughly in feature_policy_unittest.cc and in
@@ -353,7 +407,7 @@
         devices.push_back(
             MediaStreamDevice(MEDIA_DEVICE_VIDEO_CAPTURE, "Camera", "Camera"));
       }
-      std::unique_ptr<MockMediaStreamUI> ui(new MockMediaStreamUI());
+      auto ui = std::make_unique<MockMediaStreamUI>();
       std::move(callback).Run(devices, MEDIA_DEVICE_OK, std::move(ui));
     }
   };
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index 1da0c85d..f98cf0e4 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -8702,8 +8702,9 @@
   // feature. If its parent frame's policy was replicated correctly to the
   // proxy, then this will be enabled. Otherwise, it will be disabled, as
   // geolocation is disabled by default in cross-origin frames.
-  EXPECT_EQ(true, EvalJs(root->child_at(1)->child_at(0),
-                         "document.policy.allowsFeature('geolocation')"));
+  EXPECT_EQ(true,
+            EvalJs(root->child_at(1)->child_at(0),
+                   "document.featurePolicy.allowsFeature('geolocation')"));
 
   // Now navigate the iframe to a page with no policy, and the same nested
   // cross-site iframe. The policy should be cleared in the proxy.
@@ -8716,8 +8717,9 @@
   // Ask the deepest iframe to report the enabled state of the geolocation
   // feature. If its parent frame's policy was replicated correctly to the
   // proxy, then this will now be disabled.
-  EXPECT_EQ(false, EvalJs(root->child_at(1)->child_at(0),
-                          "document.policy.allowsFeature('geolocation')"));
+  EXPECT_EQ(false,
+            EvalJs(root->child_at(1)->child_at(0),
+                   "document.featurePolicy.allowsFeature('geolocation')"));
 }
 
 // Test that the constructed feature policy is correct in sandboxed
@@ -8758,7 +8760,7 @@
   EXPECT_TRUE(ExecuteScriptAndExtractBool(
       root->child_at(0),
       "window.domAutomationController.send("
-      "document.policy.allowsFeature('geolocation'));",
+      "document.featurePolicy.allowsFeature('geolocation'));",
       &success));
   EXPECT_TRUE(success);
 
@@ -8778,7 +8780,7 @@
   EXPECT_TRUE(ExecuteScriptAndExtractBool(
       root->child_at(0),
       "window.domAutomationController.send("
-      "document.policy.allowsFeature('geolocation'));",
+      "document.featurePolicy.allowsFeature('geolocation'));",
       &success));
   EXPECT_TRUE(success);
 }
@@ -8814,7 +8816,7 @@
   EXPECT_TRUE(ExecuteScriptAndExtractBool(
       root->child_at(0),
       "window.domAutomationController.send("
-      "document.policy.allowsFeature('geolocation'));",
+      "document.featurePolicy.allowsFeature('geolocation'));",
       &success));
   EXPECT_TRUE(success);
 
@@ -8835,7 +8837,7 @@
   EXPECT_TRUE(ExecuteScriptAndExtractBool(
       root->child_at(0),
       "window.domAutomationController.send("
-      "document.policy.allowsFeature('geolocation'));",
+      "document.featurePolicy.allowsFeature('geolocation'));",
       &success));
   EXPECT_FALSE(success);
 }
@@ -9129,11 +9131,11 @@
   // cross-origin to frame 3, it will use the proxy's replicated policy as the
   // parent policy; otherwise we would just ask frame 3 to report its own state.
   bool success = false;
-  EXPECT_TRUE(
-      ExecuteScriptAndExtractBool(root->child_at(1)->child_at(0),
-                                  "window.domAutomationController.send("
-                                  "document.policy.allowsFeature('autoplay'));",
-                                  &success));
+  EXPECT_TRUE(ExecuteScriptAndExtractBool(
+      root->child_at(1)->child_at(0),
+      "window.domAutomationController.send("
+      "document.featurePolicy.allowsFeature('autoplay'));",
+      &success));
   EXPECT_TRUE(success);
 }
 
diff --git a/content/browser/speech/speech_recognition_manager_impl.cc b/content/browser/speech/speech_recognition_manager_impl.cc
index dd8beab..a40d3e7 100644
--- a/content/browser/speech/speech_recognition_manager_impl.cc
+++ b/content/browser/speech/speech_recognition_manager_impl.cc
@@ -474,7 +474,7 @@
   auto iter = sessions_.find(session_id);
   if (iter->second->ui) {
     // Notify the UI that the devices are being used.
-    iter->second->ui->OnStarted(base::OnceClosure(),
+    iter->second->ui->OnStarted(base::OnceClosure(), base::RepeatingClosure(),
                                 MediaStreamUIProxy::WindowIdCallback());
   }
 
diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc
index 8261046..b775221 100644
--- a/content/browser/storage_partition_impl.cc
+++ b/content/browser/storage_partition_impl.cc
@@ -1205,10 +1205,11 @@
   if (remove_mask_ & REMOVE_DATA_MASK_PLUGIN_PRIVATE_DATA) {
     IncrementTaskCountOnUI();
     filesystem_context->default_file_task_runner()->PostTask(
-        FROM_HERE,
-        base::BindOnce(&ClearPluginPrivateDataOnFileTaskRunner,
-                       base::WrapRefCounted(filesystem_context), storage_origin,
-                       begin, end, std::move(decrement_callback)));
+        FROM_HERE, base::BindOnce(&ClearPluginPrivateDataOnFileTaskRunner,
+                                  base::WrapRefCounted(filesystem_context),
+                                  storage_origin, origin_matcher,
+                                  base::WrapRefCounted(special_storage_policy),
+                                  begin, end, std::move(decrement_callback)));
   }
 #endif  // BUILDFLAG(ENABLE_PLUGINS)
 
diff --git a/content/browser/web_contents/web_contents_view_cocoa.mm b/content/browser/web_contents/web_contents_view_cocoa.mm
index 4a024934..53bd394 100644
--- a/content/browser/web_contents/web_contents_view_cocoa.mm
+++ b/content/browser/web_contents/web_contents_view_cocoa.mm
@@ -180,11 +180,16 @@
 // NSDraggingDestination methods
 
 - (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender {
+  // Fill out a DropData from pasteboard.
+  DropData dropData;
+  content::PopulateDropDataFromPasteboard(&dropData,
+                                          [sender draggingPasteboard]);
+  [dragDest_ setDropData:dropData];
   return [dragDest_ draggingEntered:sender view:self];
 }
 
 - (void)draggingExited:(id<NSDraggingInfo>)sender {
-  [dragDest_ draggingExited:sender];
+  [dragDest_ draggingExited];
 }
 
 - (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender {
diff --git a/content/browser/web_contents/web_drag_dest_mac.h b/content/browser/web_contents/web_drag_dest_mac.h
index 6d6ae69..990cd2cd5 100644
--- a/content/browser/web_contents/web_drag_dest_mac.h
+++ b/content/browser/web_contents/web_drag_dest_mac.h
@@ -25,6 +25,16 @@
 // A typedef for a RenderViewHost used for comparison purposes only.
 typedef content::RenderViewHost* RenderViewHostIdentifier;
 
+namespace content {
+
+// Given |data|, which should not be nil, fill it in using the contents of the
+// given pasteboard. The types handled by this method should be kept in sync
+// with [WebContentsViewCocoa registerDragTypes].
+void CONTENT_EXPORT PopulateDropDataFromPasteboard(content::DropData* data,
+                                                   NSPasteboard* pboard);
+
+}
+
 // A class that handles tracking and event processing for a drag and drop
 // over the content area. Assumes something else initiates the drag, this is
 // only for processing during a drag.
@@ -57,8 +67,12 @@
   int dragStartProcessID_;
   content::GlobalRoutingID dragStartViewID_;
 
-  // The data for the current drag, or NULL if none is in progress.
-  std::unique_ptr<content::DropData> dropData_;
+  // The unfiltered data for the current drag, or nullptr if none is in
+  // progress.
+  std::unique_ptr<content::DropData> dropDataUnfiltered_;
+
+  // The data for the current drag, filtered by |currentRWHForDrag_|.
+  std::unique_ptr<content::DropData> dropDataFiltered_;
 
   // True if the drag has been canceled.
   bool canceled_;
@@ -80,9 +94,10 @@
 
 // Messages to send during the tracking of a drag, ususally upon receiving
 // calls from the view system. Communicates the drag messages to WebCore.
+- (void)setDropData:(const content::DropData&)dropData;
 - (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)info
                               view:(NSView*)view;
-- (void)draggingExited:(id<NSDraggingInfo>)info;
+- (void)draggingExited;
 - (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)info
                               view:(NSView*)view;
 - (BOOL)performDragOperation:(id<NSDraggingInfo>)info view:(NSView*)view;
@@ -104,10 +119,6 @@
 
 // Public use only for unit tests.
 @interface WebDragDest(Testing)
-// Given |data|, which should not be nil, fill it in using the contents of the
-// given pasteboard.
-- (void)populateDropData:(content::DropData*)data
-             fromPasteboard:(NSPasteboard*)pboard;
 // Given a point in window coordinates and a view in that window, return a
 // flipped point in the coordinate system of |view|.
 - (NSPoint)flipWindowPointToView:(const NSPoint&)windowPoint
diff --git a/content/browser/web_contents/web_drag_dest_mac.mm b/content/browser/web_contents/web_drag_dest_mac.mm
index 5bb3b17f..e9d6a913 100644
--- a/content/browser/web_contents/web_drag_dest_mac.mm
+++ b/content/browser/web_contents/web_drag_dest_mac.mm
@@ -84,7 +84,7 @@
 }
 
 - (DropData*)currentDropData {
-  return dropData_.get();
+  return dropDataFiltered_.get();
 }
 
 - (void)setDragDelegate:(content::WebDragDestDelegate*)delegate {
@@ -133,6 +133,10 @@
 // Messages to send during the tracking of a drag, usually upon receiving
 // calls from the view system. Communicates the drag messages to WebCore.
 
+- (void)setDropData:(const DropData&)dropData {
+  dropDataUnfiltered_ = std::make_unique<DropData>(dropData);
+}
+
 - (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)info
                               view:(NSView*)view {
   // Save off the RVH so we can tell if it changes during a drag. If it does,
@@ -158,15 +162,12 @@
   if (![self isValidDragTarget:targetRWH])
     return NSDragOperationNone;
 
+  // Filter |dropDataUnfiltered_| by currentRWHForDrag_ to populate
+  // |dropDataFiltered_|.
+  DCHECK(dropDataUnfiltered_);
+  std::unique_ptr<DropData> dropData =
+      std::make_unique<DropData>(*dropDataUnfiltered_);
   currentRWHForDrag_ = targetRWH->GetWeakPtr();
-
-  // Fill out a DropData from pasteboard.
-  std::unique_ptr<DropData> dropData;
-  dropData.reset(new DropData());
-  [self populateDropData:dropData.get()
-             fromPasteboard:[info draggingPasteboard]];
-  // TODO(paulmeyer): Data may be pulled from the pasteboard multiple times per
-  // drag. Ideally, this should only be done once, and filtered as needed.
   currentRWHForDrag_->FilterDropData(dropData.get());
 
   NSDragOperation mask = [info draggingSourceOperationMask];
@@ -190,10 +191,11 @@
     delegate_->OnDragEnter();
   }
 
-  dropData_.swap(dropData);
+  dropDataFiltered_.swap(dropData);
 
   currentRWHForDrag_->DragTargetDragEnter(
-      *dropData_, transformedPt, gfx::PointF(screenPoint.x, screenPoint.y),
+      *dropDataFiltered_, transformedPt,
+      gfx::PointF(screenPoint.x, screenPoint.y),
       static_cast<WebDragOperationsMask>(mask), GetModifierFlags());
 
   // We won't know the true operation (whether the drag is allowed) until we
@@ -202,7 +204,7 @@
   return currentOperation_;
 }
 
-- (void)draggingExited:(id<NSDraggingInfo>)info {
+- (void)draggingExited {
   DCHECK(currentRVH_);
   if (currentRVH_ != webContents_->GetRenderViewHost())
     return;
@@ -220,7 +222,8 @@
     currentRWHForDrag_->DragTargetDragLeave(gfx::PointF(), gfx::PointF());
     currentRWHForDrag_.reset();
   }
-  dropData_.reset();
+  dropDataUnfiltered_.reset();
+  dropDataFiltered_.reset();
 }
 
 - (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)info view:(NSView*)view {
@@ -326,11 +329,12 @@
 
   currentRVH_ = NULL;
 
-  targetRWH->DragTargetDrop(*dropData_, transformedPt,
+  targetRWH->DragTargetDrop(*dropDataFiltered_, transformedPt,
                             gfx::PointF(screenPoint.x, screenPoint.y),
                             GetModifierFlags());
 
-  dropData_.reset();
+  dropDataUnfiltered_.reset();
+  dropDataFiltered_.reset();
 
   return YES;
 }
@@ -354,11 +358,12 @@
              dragStartViewID_;
 }
 
-// Given |data|, which should not be nil, fill it in using the contents of the
-// given pasteboard. The types handled by this method should be kept in sync
-// with [WebContentsViewCocoa registerDragTypes].
-- (void)populateDropData:(DropData*)data
-          fromPasteboard:(NSPasteboard*)pboard {
+@end
+
+namespace content {
+
+void PopulateDropDataFromPasteboard(content::DropData* data,
+                                    NSPasteboard* pboard) {
   DCHECK(data);
   DCHECK(pboard);
   NSArray* types = [pboard types];
@@ -420,4 +425,4 @@
   }
 }
 
-@end
+}  // namespace content
diff --git a/content/browser/web_contents/web_drag_dest_mac_unittest.mm b/content/browser/web_contents/web_drag_dest_mac_unittest.mm
index a18d7e7..3ecdcc8 100644
--- a/content/browser/web_contents/web_drag_dest_mac_unittest.mm
+++ b/content/browser/web_contents/web_drag_dest_mac_unittest.mm
@@ -157,7 +157,7 @@
   NSString* textString = @"hi there";
   [pboard->get() setString:htmlString forType:NSHTMLPboardType];
   [pboard->get() setString:textString forType:NSStringPboardType];
-  [drag_dest_ populateDropData:&data fromPasteboard:pboard->get()];
+  content::PopulateDropDataFromPasteboard(&data, pboard->get());
   EXPECT_EQ(data.url.spec(), "http://www.google.com/");
   EXPECT_EQ(base::SysNSStringToUTF16(textString), data.text.string());
   EXPECT_EQ(base::SysNSStringToUTF16(htmlString), data.html.string());
diff --git a/content/browser/worker_host/worker_script_fetcher.cc b/content/browser/worker_host/worker_script_fetcher.cc
index 1c686ee..5bbbf9b8 100644
--- a/content/browser/worker_host/worker_script_fetcher.cc
+++ b/content/browser/worker_host/worker_script_fetcher.cc
@@ -165,7 +165,8 @@
     const network::ResourceResponseHead& head) {
   redirect_infos_.push_back(redirect_info);
   redirect_response_heads_.push_back(head);
-  url_loader_->FollowRedirect(base::nullopt);
+  url_loader_->FollowRedirect(base::nullopt /* removed_headers */,
+                              base::nullopt /* modified_headers */);
 }
 
 void WorkerScriptFetcher::OnUploadProgress(int64_t current_position,
diff --git a/content/browser/worker_host/worker_script_loader.cc b/content/browser/worker_host/worker_script_loader.cc
index 8bb0837..dc7bd23 100644
--- a/content/browser/worker_host/worker_script_loader.cc
+++ b/content/browser/worker_host/worker_script_loader.cc
@@ -133,13 +133,9 @@
 // the new URL.
 
 void WorkerScriptLoader::FollowRedirect(
-    const base::Optional<std::vector<std::string>>&
-        to_be_removed_request_headers,
-    const base::Optional<net::HttpRequestHeaders>& modified_request_headers,
+    const base::Optional<std::vector<std::string>>& removed_headers,
+    const base::Optional<net::HttpRequestHeaders>& modified_headers,
     const base::Optional<GURL>& new_url) {
-  DCHECK(!modified_request_headers.has_value()) << "Redirect with modified "
-                                                   "headers was not supported "
-                                                   "yet. crbug.com/845683";
   DCHECK(!new_url.has_value()) << "Redirect with modified URL was not "
                                   "supported yet. crbug.com/845683";
   DCHECK(redirect_info_);
@@ -149,7 +145,7 @@
   bool should_clear_upload = false;
   net::RedirectUtil::UpdateHttpRequest(
       resource_request_.url, resource_request_.method, *redirect_info_,
-      modified_request_headers, &resource_request_.headers,
+      removed_headers, modified_headers, &resource_request_.headers,
       &should_clear_upload);
 
   resource_request_.url = redirect_info_->new_url;
diff --git a/content/common/throttling_url_loader.cc b/content/common/throttling_url_loader.cc
index 18292c5..ce10c13 100644
--- a/content/common/throttling_url_loader.cc
+++ b/content/common/throttling_url_loader.cc
@@ -13,6 +13,41 @@
 
 namespace content {
 
+namespace {
+
+// Merges |removed_headers_B| into |removed_headers_A|. Handles the cases where
+// any of them are base::nullopt.
+void MergeRemovedHeaders(
+    base::Optional<std::vector<std::string>>* removed_headers_A,
+    const base::Optional<std::vector<std::string>>& removed_headers_B) {
+  if (!removed_headers_B || removed_headers_B->empty())
+    return;
+  if (!*removed_headers_A) {
+    *removed_headers_A = removed_headers_B;
+    return;
+  }
+  for (auto& header : *removed_headers_B) {
+    if (!base::ContainsValue(**removed_headers_A, header))
+      (**removed_headers_A).emplace_back(std::move(header));
+  }
+}
+
+// Merges |modified_headers_B| into |modified_headers_A|. Handles the cases
+// where any of them are base::nullopt.
+void MergeModifiedHeaders(
+    base::Optional<net::HttpRequestHeaders>* modified_headers_A,
+    const base::Optional<net::HttpRequestHeaders>& modified_headers_B) {
+  if (!modified_headers_B || modified_headers_B->IsEmpty())
+    return;
+  if (!*modified_headers_A) {
+    *modified_headers_A = modified_headers_B;
+    return;
+  }
+  (**modified_headers_A).MergeFrom(*modified_headers_B);
+}
+
+}  // namespace
+
 class ThrottlingURLLoader::ForwardingThrottleDelegate
     : public URLLoaderThrottle::Delegate {
  public:
@@ -198,21 +233,14 @@
 }
 
 void ThrottlingURLLoader::FollowRedirect(
+    const base::Optional<std::vector<std::string>>& removed_headers,
     const base::Optional<net::HttpRequestHeaders>& modified_headers) {
-  const base::Optional<net::HttpRequestHeaders>* modified_headers_to_send =
-      &modified_headers;
-  if (modified_request_headers_) {
-    if (modified_headers)
-      modified_request_headers_->MergeFrom(*modified_headers);
-    modified_headers_to_send = &modified_request_headers_;
-  }
+  MergeRemovedHeaders(&removed_headers_, removed_headers);
+  MergeModifiedHeaders(&modified_headers_, modified_headers);
 
   if (!throttle_will_start_redirect_url_.is_empty()) {
     throttle_will_start_redirect_url_ = GURL();
     // This is a synthesized redirect, so no need to tell the URLLoader.
-    DCHECK(!modified_headers_to_send->has_value())
-        << "ThrottlingURLLoader doesn't support modifying headers for "
-           "synthesized requests.";
     StartNow();
     return;
   }
@@ -221,13 +249,12 @@
     base::Optional<GURL> new_url;
     if (!throttle_will_redirect_redirect_url_.is_empty())
       new_url = throttle_will_redirect_redirect_url_;
-    url_loader_->FollowRedirect(to_be_removed_request_headers_,
-                                *modified_headers_to_send, new_url);
+    url_loader_->FollowRedirect(removed_headers_, modified_headers_, new_url);
     throttle_will_redirect_redirect_url_ = GURL();
   }
 
-  to_be_removed_request_headers_.reset();
-  modified_request_headers_.reset();
+  removed_headers_.reset();
+  modified_headers_.reset();
 }
 
 void ThrottlingURLLoader::FollowRedirectForcingRestart() {
@@ -235,15 +262,15 @@
   client_binding_.Close();
   CHECK(throttle_will_redirect_redirect_url_.is_empty());
 
-  if (to_be_removed_request_headers_) {
-    for (const std::string& key : *to_be_removed_request_headers_)
+  if (removed_headers_) {
+    for (const std::string& key : *removed_headers_)
       start_info_->url_request.headers.RemoveHeader(key);
-    to_be_removed_request_headers_.reset();
+    removed_headers_.reset();
   }
 
-  if (modified_request_headers_) {
-    start_info_->url_request.headers.MergeFrom(*modified_request_headers_);
-    modified_request_headers_.reset();
+  if (modified_headers_) {
+    start_info_->url_request.headers.MergeFrom(*modified_headers_);
+    modified_headers_.reset();
   }
 
   StartNow();
@@ -498,11 +525,11 @@
       auto* throttle = entry.throttle.get();
       bool throttle_deferred = false;
       auto weak_ptr = weak_factory_.GetWeakPtr();
-      std::vector<std::string> to_be_removed_headers;
+      std::vector<std::string> removed_headers;
       net::HttpRequestHeaders modified_headers;
       net::RedirectInfo redirect_info_copy = redirect_info;
       throttle->WillRedirectRequest(&redirect_info_copy, response_head,
-                                    &throttle_deferred, &to_be_removed_headers,
+                                    &throttle_deferred, &removed_headers,
                                     &modified_headers);
       if (base::FeatureList::IsEnabled(network::features::kNetworkService) &&
           redirect_info_copy.new_url != redirect_info.new_url) {
@@ -525,23 +552,8 @@
       if (!HandleThrottleResult(throttle, throttle_deferred, &deferred))
         return;
 
-      if (!to_be_removed_headers.empty()) {
-        if (to_be_removed_request_headers_) {
-          for (auto& header : to_be_removed_headers) {
-            if (!base::ContainsValue(*to_be_removed_request_headers_, header))
-              to_be_removed_request_headers_->push_back(std::move(header));
-          }
-        } else {
-          to_be_removed_request_headers_ = std::move(to_be_removed_headers);
-        }
-      }
-
-      if (!modified_headers.IsEmpty()) {
-        if (modified_request_headers_)
-          modified_request_headers_->MergeFrom(modified_headers);
-        else
-          modified_request_headers_ = std::move(modified_headers);
-      }
+      MergeRemovedHeaders(&removed_headers_, removed_headers);
+      MergeModifiedHeaders(&modified_headers_, modified_headers);
     }
 
     if (deferred) {
diff --git a/content/common/throttling_url_loader.h b/content/common/throttling_url_loader.h
index f721d8e..91640ca 100644
--- a/content/common/throttling_url_loader.h
+++ b/content/common/throttling_url_loader.h
@@ -51,7 +51,8 @@
   ~ThrottlingURLLoader() override;
 
   void FollowRedirect(
-      const base::Optional<net::HttpRequestHeaders>& modified_request_headers);
+      const base::Optional<std::vector<std::string>>& removed_headers,
+      const base::Optional<net::HttpRequestHeaders>& modified_headers);
   // Follows a redirect, calling CreateLoaderAndStart() on the factory. This
   // is useful if the factory uses different loaders for different URLs.
   void FollowRedirectForcingRestart();
@@ -254,8 +255,8 @@
 
   bool response_intercepted_ = false;
 
-  base::Optional<std::vector<std::string>> to_be_removed_request_headers_;
-  base::Optional<net::HttpRequestHeaders> modified_request_headers_;
+  base::Optional<std::vector<std::string>> removed_headers_;
+  base::Optional<net::HttpRequestHeaders> modified_headers_;
 
   int pending_restart_flags_ = 0;
   bool has_pending_restart_ = false;
diff --git a/content/common/throttling_url_loader_unittest.cc b/content/common/throttling_url_loader_unittest.cc
index 1b51c65..05dce1c 100644
--- a/content/common/throttling_url_loader_unittest.cc
+++ b/content/common/throttling_url_loader_unittest.cc
@@ -236,11 +236,11 @@
   using ThrottleCallback =
       base::RepeatingCallback<void(URLLoaderThrottle::Delegate* delegate,
                                    bool* defer)>;
-  using ThrottleRedirectCallback = base::RepeatingCallback<void(
-      URLLoaderThrottle::Delegate* delegate,
-      bool* defer,
-      std::vector<std::string>* to_be_removed_headers,
-      net::HttpRequestHeaders* modified_headers)>;
+  using ThrottleRedirectCallback =
+      base::RepeatingCallback<void(URLLoaderThrottle::Delegate* delegate,
+                                   bool* defer,
+                                   std::vector<std::string>* removed_headers,
+                                   net::HttpRequestHeaders* modified_headers)>;
 
   size_t will_start_request_called() const {
     return will_start_request_called_;
@@ -486,7 +486,7 @@
 TEST_F(ThrottlingURLLoaderTest, CancelBeforeRedirect) {
   throttle_->set_will_redirect_request_callback(base::BindRepeating(
       [](URLLoaderThrottle::Delegate* delegate, bool* /* defer */,
-         std::vector<std::string>* /* to_be_removed_headers */,
+         std::vector<std::string>* /* removed_headers */,
          net::HttpRequestHeaders* /* modified_headers */) {
         delegate->CancelWithError(net::ERR_ACCESS_DENIED);
       }));
@@ -519,7 +519,7 @@
   throttle_->set_will_redirect_request_callback(base::Bind(
       [](const base::Closure& quit_closure,
          URLLoaderThrottle::Delegate* delegate, bool* defer,
-         std::vector<std::string>* /* to_be_removed_headers */,
+         std::vector<std::string>* /* removed_headers */,
          net::HttpRequestHeaders* /* modified_headers */) {
         *defer = true;
         quit_closure.Run();
@@ -570,9 +570,9 @@
 TEST_F(ThrottlingURLLoaderTest, ModifyHeadersBeforeRedirect) {
   throttle_->set_will_redirect_request_callback(base::BindRepeating(
       [](URLLoaderThrottle::Delegate* delegate, bool* /* defer */,
-         std::vector<std::string>* to_be_removed_headers,
+         std::vector<std::string>* removed_headers,
          net::HttpRequestHeaders* modified_headers) {
-        to_be_removed_headers->push_back("X-Test-Header-1");
+        removed_headers->push_back("X-Test-Header-1");
         modified_headers->SetHeader("X-Test-Header-2", "Foo");
         modified_headers->SetHeader("X-Test-Header-3", "Throttle Value");
       }));
@@ -581,7 +581,7 @@
     net::HttpRequestHeaders modified_headers;
     modified_headers.SetHeader("X-Test-Header-3", "Client Value");
     modified_headers.SetHeader("X-Test-Header-4", "Bar");
-    loader_->FollowRedirect(modified_headers);
+    loader_->FollowRedirect(base::nullopt, modified_headers);
   }));
 
   CreateLoaderAndStart();
@@ -605,25 +605,25 @@
 
   throttle_->set_will_redirect_request_callback(base::BindRepeating(
       [](URLLoaderThrottle::Delegate* delegate, bool* /* defer */,
-         std::vector<std::string>* to_be_removed_headers,
+         std::vector<std::string>* removed_headers,
          net::HttpRequestHeaders* modified_headers) {
-        to_be_removed_headers->push_back("X-Test-Header-0");
-        to_be_removed_headers->push_back("X-Test-Header-1");
+        removed_headers->push_back("X-Test-Header-0");
+        removed_headers->push_back("X-Test-Header-1");
         modified_headers->SetHeader("X-Test-Header-3", "Foo");
         modified_headers->SetHeader("X-Test-Header-4", "Throttle1");
       }));
 
   throttle2->set_will_redirect_request_callback(base::BindRepeating(
       [](URLLoaderThrottle::Delegate* delegate, bool* /* defer */,
-         std::vector<std::string>* to_be_removed_headers,
+         std::vector<std::string>* removed_headers,
          net::HttpRequestHeaders* modified_headers) {
-        to_be_removed_headers->push_back("X-Test-Header-1");
-        to_be_removed_headers->push_back("X-Test-Header-2");
+        removed_headers->push_back("X-Test-Header-1");
+        removed_headers->push_back("X-Test-Header-2");
         modified_headers->SetHeader("X-Test-Header-4", "Throttle2");
       }));
 
   client_.set_on_received_redirect_callback(base::BindLambdaForTesting(
-      [&]() { loader_->FollowRedirect(base::nullopt); }));
+      [&]() { loader_->FollowRedirect(base::nullopt, base::nullopt); }));
 
   CreateLoaderAndStart();
   factory_.NotifyClientOnReceiveRedirect();
@@ -765,7 +765,7 @@
   throttle_->set_will_process_response_callback(std::move(resume_callback));
   throttle_->set_will_redirect_request_callback(base::BindRepeating(
       [](URLLoaderThrottle::Delegate* delegate, bool* /* defer */,
-         std::vector<std::string>* /* to_be_removed_headers */,
+         std::vector<std::string>* /* removed_headers */,
          net::HttpRequestHeaders* /* modified_headers */) {
         delegate->Resume();
         delegate->Resume();
@@ -1129,7 +1129,7 @@
   throttle_->set_will_redirect_request_callback(base::BindRepeating(
       [](const base::RepeatingClosure& quit_closure,
          URLLoaderThrottle::Delegate* delegate, bool* defer,
-         std::vector<std::string>* /* to_be_removed_headers */,
+         std::vector<std::string>* /* removed_headers */,
          net::HttpRequestHeaders* /* modified_headers */) {
         *defer = true;
         quit_closure.Run();
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index 49aeb5f..ac70d5d 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -487,7 +487,7 @@
     RenderFrameHost* opener,
     const GURL& opener_url,
     const GURL& opener_top_level_frame_url,
-    const GURL& source_origin,
+    const url::Origin& source_origin,
     content::mojom::WindowContainerType container_type,
     const GURL& target_url,
     const Referrer& referrer,
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index dc15374..a018e24 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -559,12 +559,12 @@
       int* extra_load_flags) {}
 
   // Allow the embedder to modify headers for a redirect. If non-nullopt,
-  // |*modified_request_headers| are applied to the request headers after
-  // updating them for the redirect.
+  // |*modified_headers| is applied to the request headers after updating them
+  // for the redirect.
   virtual void NavigationRequestRedirected(
       int frame_tree_node_id,
       const GURL& url,
-      base::Optional<net::HttpRequestHeaders>* modified_request_headers) {}
+      base::Optional<net::HttpRequestHeaders>* modified_headers) {}
 
   // Allow the embedder to control if the given cookie can be read.
   // This is called on the IO thread.
@@ -776,7 +776,7 @@
       RenderFrameHost* opener,
       const GURL& opener_url,
       const GURL& opener_top_level_frame_url,
-      const GURL& source_origin,
+      const url::Origin& source_origin,
       content::mojom::WindowContainerType container_type,
       const GURL& target_url,
       const Referrer& referrer,
diff --git a/content/public/browser/desktop_media_id.h b/content/public/browser/desktop_media_id.h
index 9c970b6..cb93bc5 100644
--- a/content/public/browser/desktop_media_id.h
+++ b/content/public/browser/desktop_media_id.h
@@ -41,14 +41,16 @@
   static aura::Window* GetAuraWindowById(const DesktopMediaID& id);
 #endif  // defined(USE_AURA)
 
-  DesktopMediaID() = default;
+  constexpr DesktopMediaID() = default;
 
-  DesktopMediaID(Type type, Id id) : type(type), id(id) {}
+  constexpr DesktopMediaID(Type type, Id id) : type(type), id(id) {}
 
-  DesktopMediaID(Type type, Id id, WebContentsMediaCaptureId web_contents_id)
+  constexpr DesktopMediaID(Type type,
+                           Id id,
+                           WebContentsMediaCaptureId web_contents_id)
       : type(type), id(id), web_contents_id(web_contents_id) {}
 
-  DesktopMediaID(Type type, Id id, bool audio_share)
+  constexpr DesktopMediaID(Type type, Id id, bool audio_share)
       : type(type), id(id), audio_share(audio_share) {}
 
   // Operators so that DesktopMediaID can be used with STL containers.
diff --git a/content/public/browser/navigation_handle.cc b/content/public/browser/navigation_handle.cc
index f0770fdd..9f55b85 100644
--- a/content/public/browser/navigation_handle.cc
+++ b/content/public/browser/navigation_handle.cc
@@ -44,7 +44,7 @@
   }
   std::unique_ptr<NavigationHandleImpl> handle_impl =
       NavigationHandleImpl::Create(
-          url, std::vector<GURL>(), rfhi->frame_tree_node(),
+          url, base::nullopt, std::vector<GURL>(), rfhi->frame_tree_node(),
           true,  // is_renderer_initiated
           is_same_document, base::TimeTicks::Now(), 0,
           false,                  // started_from_context_menu
diff --git a/content/public/browser/navigation_handle.h b/content/public/browser/navigation_handle.h
index b1185b9..66c96ef8 100644
--- a/content/public/browser/navigation_handle.h
+++ b/content/public/browser/navigation_handle.h
@@ -285,6 +285,10 @@
   // initiated from a link that had that attribute set.
   virtual const std::string& GetHrefTranslate() = 0;
 
+  // Returns, if available, the origin of the document that has initiated the
+  // navigation for this NavigationHandle.
+  virtual const base::Optional<url::Origin>& GetInitiatorOrigin() = 0;
+
   // Testing methods ----------------------------------------------------------
   //
   // The following methods should be used exclusively for writing unit tests.
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 958afbdb..c90becd 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -139,6 +139,10 @@
 const base::Feature kDataSaverHoldback{"DataSaverHoldback",
                                        base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enable changing source dynamically for desktop capture.
+const base::Feature kDesktopCaptureChangeSource{
+    "DesktopCaptureChangeSource", base::FEATURE_ENABLED_BY_DEFAULT};
+
 // Throttle tasks in Blink background timer queues based on CPU budgets
 // for the background tab. Bug: https://crbug.com/639852.
 const base::Feature kExpensiveBackgroundTimerThrottling{
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 41d969d..d14d7d2 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -41,6 +41,7 @@
 CONTENT_EXPORT extern const base::Feature kCrashReporting;
 CONTENT_EXPORT extern const base::Feature kCSSFragmentIdentifiers;
 CONTENT_EXPORT extern const base::Feature kDataSaverHoldback;
+CONTENT_EXPORT extern const base::Feature kDesktopCaptureChangeSource;
 CONTENT_EXPORT extern const base::Feature kExperimentalProductivityFeatures;
 CONTENT_EXPORT extern const base::Feature kExpensiveBackgroundTimerThrottling;
 CONTENT_EXPORT extern const base::Feature kExtendedMouseButtons;
diff --git a/content/public/common/media_stream_request.h b/content/public/common/media_stream_request.h
index fe92295f..cdc2c63 100644
--- a/content/public/common/media_stream_request.h
+++ b/content/public/common/media_stream_request.h
@@ -233,9 +233,11 @@
   virtual ~MediaStreamUI() {}
 
   // Called when MediaStream capturing is started. Chrome layer can call |stop|
-  // to stop the stream. Returns the platform-dependent window ID for the UI, or
-  // 0 if not applicable.
-  virtual gfx::NativeViewId OnStarted(const base::Closure& stop) = 0;
+  // to stop the stream, or |source| to change the source of the stream.
+  // Returns the platform-dependent window ID for the UI, or 0 if not
+  // applicable.
+  virtual gfx::NativeViewId OnStarted(base::OnceClosure stop,
+                                      base::RepeatingClosure source) = 0;
 };
 
 // Callback used return results of media access requests.
diff --git a/content/public/test/url_loader_interceptor.cc b/content/public/test/url_loader_interceptor.cc
index 8bbeaf1f..12ee936 100644
--- a/content/public/test/url_loader_interceptor.cc
+++ b/content/public/test/url_loader_interceptor.cc
@@ -10,6 +10,7 @@
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/path_service.h"
+#include "base/synchronization/lock.h"
 #include "base/task/post_task.h"
 #include "base/test/bind_test_util.h"
 #include "base/threading/thread_restrictions.h"
@@ -48,6 +49,115 @@
 
 }  // namespace
 
+// Part of URLLoaderInterceptor which lives on the IO thread. Outlives
+// URLLoaderInterceptor.
+class URLLoaderInterceptor::IOState
+    : public base::RefCountedThreadSafe<URLLoaderInterceptor::IOState> {
+ public:
+  explicit IOState(URLLoaderInterceptor* parent) : parent_(parent) {}
+  void Initialize(base::OnceClosure closure);
+  // Called when a SubresourceWrapper's binding has an error.
+  void SubresourceWrapperBindingError(SubresourceWrapper* wrapper);
+
+  // Unsets the parent pointer. Prevents URLLoaderInterceptor::Intercept from
+  // being called.
+  void UnsetParent() {
+    base::AutoLock lock(intercept_lock_);
+    parent_ = nullptr;
+  }
+
+  void Shutdown(base::OnceClosure closure) {
+    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+    url_loader_factory_getter_wrappers_.clear();
+    subresource_wrappers_.clear();
+
+    if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
+      URLLoaderFactoryGetter::SetGetNetworkFactoryCallbackForTesting(
+          URLLoaderFactoryGetter::GetNetworkFactoryCallback());
+    } else {
+      NavigationURLLoaderImpl::SetBeginNavigationInterceptorForTesting(
+          NavigationURLLoaderImpl::BeginNavigationInterceptor());
+    }
+
+    if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) {
+      ResourceMessageFilter::SetNetworkFactoryForTesting(nullptr);
+    }
+
+    if (closure)
+      std::move(closure).Run();
+  }
+
+  // Callback on IO thread whenever a
+  // URLLoaderFactoryGetter::GetNetworkContext is called on an object that
+  // doesn't have a test factory set up.
+  void GetNetworkFactoryCallback(
+      URLLoaderFactoryGetter* url_loader_factory_getter);
+
+  void CreateURLLoaderFactoryForSubresources(
+      network::mojom::URLLoaderFactoryRequest request,
+      int process_id,
+      network::mojom::URLLoaderFactoryPtrInfo original_factory);
+
+  bool Intercept(RequestParams* params) {
+    // The lock ensures that |URLLoaderInterceptor| can't be deleted while it
+    // is processing an intercept. Before |URLLoaderInterceptor| is deleted,
+    // parent_ is set to null so that requests can't be intercepted after
+    // |URLLoaderInterceptor| is deleted.
+    base::AutoLock lock(intercept_lock_);
+    if (!parent_)
+      return false;
+    return parent_->Intercept(params);
+  }
+
+  bool BeginNavigationCallback(
+      network::mojom::URLLoaderRequest* request,
+      int32_t routing_id,
+      int32_t request_id,
+      uint32_t options,
+      const network::ResourceRequest& url_request,
+      network::mojom::URLLoaderClientPtr* client,
+      const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
+    RequestParams params;
+    params.process_id = 0;
+    params.request = std::move(*request);
+    params.routing_id = routing_id;
+    params.request_id = request_id;
+    params.options = options;
+    params.url_request = url_request;
+    params.client = std::move(*client);
+    params.traffic_annotation = traffic_annotation;
+
+    if (Intercept(&params))
+      return true;
+
+    *request = std::move(params.request);
+    *client = std::move(params.client);
+    return false;
+  }
+
+ private:
+  friend class base::RefCountedThreadSafe<IOState>;
+  ~IOState() {}
+
+  // This lock guarantees that when URLLoaderInterceptor is destroyed,
+  // no intercept callbacks will be called.
+  base::Lock intercept_lock_;
+  URLLoaderInterceptor* parent_ GUARDED_BY(intercept_lock_);
+
+  // For intercepting frame requests with network service. There is one per
+  // StoragePartition. Only accessed on IO thread.
+  std::set<std::unique_ptr<URLLoaderFactoryGetterWrapper>>
+      url_loader_factory_getter_wrappers_;
+  // For intercepting subresources without network service in
+  // ResourceMessageFilter.
+  std::unique_ptr<Interceptor> rmf_interceptor_;
+  // For intercepting subresources with network service. There is one per
+  // active render frame commit. Only accessed on IO thread.
+  std::set<std::unique_ptr<SubresourceWrapper>> subresource_wrappers_;
+
+  DISALLOW_COPY_AND_ASSIGN(IOState);
+};
+
 class URLLoaderInterceptor::Interceptor
     : public network::mojom::URLLoaderFactory {
  public:
@@ -55,7 +165,7 @@
   using OriginalFactoryGetter =
       base::Callback<network::mojom::URLLoaderFactory*()>;
 
-  Interceptor(URLLoaderInterceptor* parent,
+  Interceptor(URLLoaderInterceptor::IOState* parent,
               const ProcessIdGetter& process_id_getter,
               const OriginalFactoryGetter& original_factory_getter)
       : parent_(parent),
@@ -113,7 +223,7 @@
       std::move(error_handler_).Run();
   }
 
-  URLLoaderInterceptor* parent_;
+  URLLoaderInterceptor::IOState* parent_;
   ProcessIdGetter process_id_getter_;
   OriginalFactoryGetter original_factory_getter_;
   mojo::BindingSet<network::mojom::URLLoaderFactory> bindings_;
@@ -128,8 +238,9 @@
  public:
   URLLoaderFactoryGetterWrapper(
       URLLoaderFactoryGetter* url_loader_factory_getter,
-      URLLoaderInterceptor* parent)
+      URLLoaderInterceptor::IOState* parent)
       : url_loader_factory_getter_(url_loader_factory_getter) {
+    DCHECK_CURRENTLY_ON(BrowserThread::IO);
     frame_interceptor_ = std::make_unique<Interceptor>(
         parent, base::BindRepeating([]() { return 0; }),
         base::BindLambdaForTesting([=]() -> network::mojom::URLLoaderFactory* {
@@ -152,6 +263,7 @@
   }
 
   ~URLLoaderFactoryGetterWrapper() {
+    DCHECK_CURRENTLY_ON(BrowserThread::IO);
     url_loader_factory_getter_->SetNetworkFactoryForTesting(nullptr, false);
     url_loader_factory_getter_->SetNetworkFactoryForTesting(nullptr, true);
   }
@@ -167,7 +279,7 @@
   URLLoaderFactoryNavigationWrapper(
       network::mojom::URLLoaderFactoryRequest request,
       network::mojom::URLLoaderFactoryPtr target_factory,
-      URLLoaderInterceptor* parent)
+      URLLoaderInterceptor::IOState* parent)
       : target_factory_(std::move(target_factory)) {
     interceptor_ = std::make_unique<Interceptor>(
         parent, base::BindRepeating([]() { return 0; }),
@@ -187,7 +299,7 @@
 class URLLoaderInterceptor::BrowserProcessWrapper {
  public:
   BrowserProcessWrapper(network::mojom::URLLoaderFactoryRequest factory_request,
-                        URLLoaderInterceptor* parent,
+                        URLLoaderInterceptor::IOState* parent,
                         network::mojom::URLLoaderFactoryPtr original_factory)
       : interceptor_(
             parent,
@@ -217,7 +329,7 @@
  public:
   SubresourceWrapper(network::mojom::URLLoaderFactoryRequest factory_request,
                      int process_id,
-                     URLLoaderInterceptor* parent,
+                     URLLoaderInterceptor::IOState* parent,
                      network::mojom::URLLoaderFactoryPtrInfo original_factory)
       : interceptor_(
             parent,
@@ -227,9 +339,9 @@
                                 base::Unretained(this))),
         original_factory_(std::move(original_factory)) {
     interceptor_.BindRequest(std::move(factory_request));
-    interceptor_.SetConnectionErrorHandler(
-        base::BindOnce(&URLLoaderInterceptor::SubresourceWrapperBindingError,
-                       base::Unretained(parent), this));
+    interceptor_.SetConnectionErrorHandler(base::BindOnce(
+        &URLLoaderInterceptor::IOState::SubresourceWrapperBindingError,
+        base::Unretained(parent), this));
   }
 
   ~SubresourceWrapper() {}
@@ -252,11 +364,12 @@
 URLLoaderInterceptor::RequestParams& URLLoaderInterceptor::RequestParams::
 operator=(RequestParams&& other) = default;
 
-URLLoaderInterceptor::URLLoaderInterceptor(const InterceptCallback& callback)
-    : callback_(callback) {
+URLLoaderInterceptor::URLLoaderInterceptor(const InterceptCallback& callback,
+                                           base::OnceClosure ready_callback)
+    : callback_(callback), io_thread_(base::MakeRefCounted<IOState>(this)) {
   DCHECK(!BrowserThread::IsThreadInitialized(BrowserThread::UI) ||
          BrowserThread::CurrentlyOn(BrowserThread::UI));
-
+  use_runloop_ = !ready_callback;
   if (base::FeatureList::IsEnabled(
           blink::features::kServiceWorkerServicification) ||
       base::FeatureList::IsEnabled(network::features::kNetworkService)) {
@@ -285,19 +398,34 @@
   }
 
   if (BrowserThread::IsThreadInitialized(BrowserThread::IO)) {
-    base::RunLoop run_loop;
-    base::PostTaskWithTraits(
-        FROM_HERE, {BrowserThread::IO},
-        base::BindOnce(&URLLoaderInterceptor::InitializeOnIOThread,
-                       base::Unretained(this), run_loop.QuitClosure()));
-    run_loop.Run();
+    if (use_runloop_) {
+      base::RunLoop run_loop;
+      base::PostTaskWithTraits(
+          FROM_HERE, {BrowserThread::IO},
+          base::BindOnce(&URLLoaderInterceptor::IOState::Initialize, io_thread_,
+                         run_loop.QuitClosure()));
+      run_loop.Run();
+    } else {
+      base::OnceClosure wrapped_callback = base::BindOnce(
+          [](base::OnceClosure callback) {
+            base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI},
+                                     std::move(callback));
+          },
+          std::move(ready_callback));
+
+      base::PostTaskWithTraits(
+          FROM_HERE, {BrowserThread::IO},
+          base::BindOnce(&URLLoaderInterceptor::IOState::Initialize, io_thread_,
+                         std::move(wrapped_callback)));
+    }
   } else {
-    InitializeOnIOThread(base::OnceClosure());
+    io_thread_->Initialize(std::move(ready_callback));
   }
 }
 
 URLLoaderInterceptor::~URLLoaderInterceptor() {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  io_thread_->UnsetParent();
 
   if (base::FeatureList::IsEnabled(
           blink::features::kServiceWorkerServicification) ||
@@ -317,12 +445,19 @@
         NavigationURLLoaderImpl::URLLoaderFactoryInterceptor());
   }
 
-  base::RunLoop run_loop;
-  base::PostTaskWithTraits(
-      FROM_HERE, {BrowserThread::IO},
-      base::BindOnce(&URLLoaderInterceptor::ShutdownOnIOThread,
-                     base::Unretained(this), run_loop.QuitClosure()));
-  run_loop.Run();
+  if (use_runloop_) {
+    base::RunLoop run_loop;
+    base::PostTaskWithTraits(
+        FROM_HERE, {BrowserThread::IO},
+        base::BindOnce(&URLLoaderInterceptor::IOState::Shutdown, io_thread_,
+                       run_loop.QuitClosure()));
+    run_loop.Run();
+  } else {
+    base::PostTaskWithTraits(
+        FROM_HERE, {BrowserThread::IO},
+        base::BindOnce(&URLLoaderInterceptor::IOState::Shutdown, io_thread_,
+                       base::OnceClosure()));
+  }
 }
 
 void URLLoaderInterceptor::WriteResponse(
@@ -404,9 +539,8 @@
             std::move(original_factory)));
     return;
   }
-
-  subresource_wrappers_.emplace(std::make_unique<SubresourceWrapper>(
-      std::move(request), process_id, this, std::move(original_factory)));
+  io_thread_->CreateURLLoaderFactoryForSubresources(
+      std::move(request), process_id, std::move(original_factory));
 }
 
 network::mojom::URLLoaderFactoryPtr
@@ -415,18 +549,11 @@
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   network::mojom::URLLoaderFactoryPtr loader_factory;
   browser_process_interceptors_.emplace(std::make_unique<BrowserProcessWrapper>(
-      mojo::MakeRequest(&loader_factory), this, std::move(original_factory)));
+      mojo::MakeRequest(&loader_factory), io_thread_.get(),
+      std::move(original_factory)));
   return loader_factory;
 }
 
-void URLLoaderInterceptor::GetNetworkFactoryCallback(
-    URLLoaderFactoryGetter* url_loader_factory_getter) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-  url_loader_factory_getter_wrappers_.emplace(
-      std::make_unique<URLLoaderFactoryGetterWrapper>(url_loader_factory_getter,
-                                                      this));
-}
-
 void URLLoaderInterceptor::InterceptNavigationRequestCallback(
     network::mojom::URLLoaderFactoryRequest* request) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
@@ -437,33 +564,8 @@
 
   navigation_wrappers_.emplace(
       std::make_unique<URLLoaderFactoryNavigationWrapper>(
-          std::move(proxied_request), std::move(target_factory), this));
-}
-
-bool URLLoaderInterceptor::BeginNavigationCallback(
-    network::mojom::URLLoaderRequest* request,
-    int32_t routing_id,
-    int32_t request_id,
-    uint32_t options,
-    const network::ResourceRequest& url_request,
-    network::mojom::URLLoaderClientPtr* client,
-    const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
-  RequestParams params;
-  params.process_id = 0;
-  params.request = std::move(*request);
-  params.routing_id = routing_id;
-  params.request_id = request_id;
-  params.options = options;
-  params.url_request = url_request;
-  params.client = std::move(*client);
-  params.traffic_annotation = traffic_annotation;
-
-  if (Intercept(&params))
-    return true;
-
-  *request = std::move(params.request);
-  *client = std::move(params.client);
-  return false;
+          std::move(proxied_request), std::move(target_factory),
+          io_thread_.get()));
 }
 
 bool URLLoaderInterceptor::Intercept(RequestParams* params) {
@@ -487,7 +589,7 @@
   return false;
 }
 
-void URLLoaderInterceptor::SubresourceWrapperBindingError(
+void URLLoaderInterceptor::IOState::SubresourceWrapperBindingError(
     SubresourceWrapper* wrapper) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
   for (auto& it : subresource_wrappers_) {
@@ -500,15 +602,17 @@
   NOTREACHED();
 }
 
-void URLLoaderInterceptor::InitializeOnIOThread(base::OnceClosure closure) {
+void URLLoaderInterceptor::IOState::Initialize(base::OnceClosure closure) {
   if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
     URLLoaderFactoryGetter::SetGetNetworkFactoryCallbackForTesting(
-        base::BindRepeating(&URLLoaderInterceptor::GetNetworkFactoryCallback,
-                            base::Unretained(this)));
+        base::BindRepeating(
+            &URLLoaderInterceptor::IOState::GetNetworkFactoryCallback,
+            base::Unretained(this)));
   } else {
     NavigationURLLoaderImpl::SetBeginNavigationInterceptorForTesting(
-        base::BindRepeating(&URLLoaderInterceptor::BeginNavigationCallback,
-                            base::Unretained(this)));
+        base::BindRepeating(
+            &URLLoaderInterceptor::IOState::BeginNavigationCallback,
+            base::Unretained(this)));
   }
 
   if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) {
@@ -523,44 +627,44 @@
         }));
     ResourceMessageFilter::SetNetworkFactoryForTesting(rmf_interceptor_.get());
   }
-
   if (closure)
     std::move(closure).Run();
 }
 
-void URLLoaderInterceptor::ShutdownOnIOThread(base::OnceClosure closure) {
+void URLLoaderInterceptor::IOState::GetNetworkFactoryCallback(
+    URLLoaderFactoryGetter* url_loader_factory_getter) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-  url_loader_factory_getter_wrappers_.clear();
-  subresource_wrappers_.clear();
+  url_loader_factory_getter_wrappers_.emplace(
+      std::make_unique<URLLoaderFactoryGetterWrapper>(url_loader_factory_getter,
+                                                      this));
+}
 
-  if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
-    URLLoaderFactoryGetter::SetGetNetworkFactoryCallbackForTesting(
-        URLLoaderFactoryGetter::GetNetworkFactoryCallback());
-  } else {
-    NavigationURLLoaderImpl::SetBeginNavigationInterceptorForTesting(
-        NavigationURLLoaderImpl::BeginNavigationInterceptor());
-  }
-
-  if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) {
-    ResourceMessageFilter::SetNetworkFactoryForTesting(nullptr);
-  }
-
-  std::move(closure).Run();
+void URLLoaderInterceptor::IOState::CreateURLLoaderFactoryForSubresources(
+    network::mojom::URLLoaderFactoryRequest request,
+    int process_id,
+    network::mojom::URLLoaderFactoryPtrInfo original_factory) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  subresource_wrappers_.emplace(std::make_unique<SubresourceWrapper>(
+      std::move(request), process_id, this, std::move(original_factory)));
 }
 
 // static
 std::unique_ptr<content::URLLoaderInterceptor>
 URLLoaderInterceptor::SetupRequestFailForURL(const GURL& url,
-                                             net::Error error) {
-  return std::make_unique<content::URLLoaderInterceptor>(base::BindRepeating(
-      [](const GURL& url, net::Error error,
-         content::URLLoaderInterceptor::RequestParams* params) {
-        if (params->url_request.url != url)
-          return false;
-        params->client->OnComplete(network::URLLoaderCompletionStatus(error));
-        return true;
-      },
-      url, error));
+                                             net::Error error,
+                                             base::OnceClosure ready_callback) {
+  return std::make_unique<content::URLLoaderInterceptor>(
+      base::BindRepeating(
+          [](const GURL& url, net::Error error,
+             content::URLLoaderInterceptor::RequestParams* params) {
+            if (params->url_request.url != url)
+              return false;
+            params->client->OnComplete(
+                network::URLLoaderCompletionStatus(error));
+            return true;
+          },
+          url, error),
+      std::move(ready_callback));
 }
 
 }  // namespace content
diff --git a/content/public/test/url_loader_interceptor.h b/content/public/test/url_loader_interceptor.h
index 98cb6c3..3fb90ca 100644
--- a/content/public/test/url_loader_interceptor.h
+++ b/content/public/test/url_loader_interceptor.h
@@ -11,13 +11,13 @@
 
 #include "base/files/file_path.h"
 #include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "net/base/net_errors.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
 
 namespace content {
-class URLLoaderFactoryGetter;
 
 // Helper class to intercept URLLoaderFactory calls for tests.
 // This intercepts:
@@ -78,7 +78,12 @@
   // forward the request to the original URLLoaderFactory.
   using InterceptCallback = base::Callback<bool(RequestParams* params)>;
 
-  explicit URLLoaderInterceptor(const InterceptCallback& callback);
+  // Create an interceptor which calls |callback|. If |ready_callback| is not
+  // provided, a nested RunLoop is used to ensure the interceptor is ready
+  // before returning. If |ready_callback| is provided, no RunLoop is called,
+  // and instead |ready_callback| is called after the interceptor is installed.
+  URLLoaderInterceptor(const InterceptCallback& callback,
+                       base::OnceClosure ready_callback = {});
   ~URLLoaderInterceptor();
 
   // Helper methods for use when intercepting.
@@ -112,13 +117,17 @@
 
   // Returns an interceptor that (as long as it says alive) will intercept
   // requests to |url| and fail them using the provided |error|.
+  // |ready_callback| is optional and avoids the use of RunLoop, see
+  // the constructor for more detail.
   static std::unique_ptr<URLLoaderInterceptor> SetupRequestFailForURL(
       const GURL& url,
-      net::Error error);
+      net::Error error,
+      base::OnceClosure ready_callback = {});
 
  private:
   class BrowserProcessWrapper;
   class Interceptor;
+  class IOState;
   class SubresourceWrapper;
   class URLLoaderFactoryGetterWrapper;
   class URLLoaderFactoryNavigationWrapper;
@@ -135,11 +144,6 @@
   network::mojom::URLLoaderFactoryPtr GetURLLoaderFactoryForBrowserProcess(
       network::mojom::URLLoaderFactoryPtr original_factory);
 
-  // Callback on IO thread whenever a URLLoaderFactoryGetter::GetNetworkContext
-  // is called on an object that doesn't have a test factory set up.
-  void GetNetworkFactoryCallback(
-      URLLoaderFactoryGetter* url_loader_factory_getter);
-
   // Callback on UI thread whenever NavigationURLLoaderImpl needs a
   // URLLoaderFactory with a network::mojom::TrustedURLLoaderHeaderClient.
   void InterceptNavigationRequestCallback(
@@ -161,28 +165,17 @@
   // intercepted.
   bool Intercept(RequestParams* params);
 
-  // Called when a SubresourceWrapper's binding has an error.
-  void SubresourceWrapperBindingError(SubresourceWrapper* wrapper);
-
   // Called on IO thread at initialization and shutdown.
   void InitializeOnIOThread(base::OnceClosure closure);
-  void ShutdownOnIOThread(base::OnceClosure closure);
 
+  bool use_runloop_;
+  base::OnceClosure ready_callback_;
   InterceptCallback callback_;
-  // For intercepting frame requests with network service. There is one per
-  // StoragePartition. Only accessed on IO thread.
-  std::set<std::unique_ptr<URLLoaderFactoryGetterWrapper>>
-      url_loader_factory_getter_wrappers_;
+  scoped_refptr<IOState> io_thread_;
   // For intecepting non-frame requests from the browser process. There is one
   // per StoragePartition. Only accessed on UI thread.
   std::set<std::unique_ptr<BrowserProcessWrapper>>
       browser_process_interceptors_;
-  // For intercepting subresources without network service in
-  // ResourceMessageFilter.
-  std::unique_ptr<Interceptor> rmf_interceptor_;
-  // For intercepting subresources with network service. There is one per active
-  // render frame commit. Only accessed on IO thread.
-  std::set<std::unique_ptr<SubresourceWrapper>> subresource_wrappers_;
   std::set<std::unique_ptr<URLLoaderFactoryNavigationWrapper>>
       navigation_wrappers_;
 
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index fc0ab4e..5a874a7 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -48,8 +48,6 @@
     "android/synchronous_layer_tree_frame_sink.h",
     "appcache/appcache_backend_proxy.cc",
     "appcache/appcache_backend_proxy.h",
-    "appcache/appcache_dispatcher.cc",
-    "appcache/appcache_dispatcher.h",
     "appcache/appcache_frontend_impl.cc",
     "appcache/appcache_frontend_impl.h",
     "appcache/web_application_cache_host_impl.cc",
diff --git a/content/renderer/appcache/appcache_dispatcher.cc b/content/renderer/appcache/appcache_dispatcher.cc
deleted file mode 100644
index 50594cf..0000000
--- a/content/renderer/appcache/appcache_dispatcher.cc
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/renderer/appcache/appcache_dispatcher.h"
-
-#include "content/common/appcache.mojom.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
-
-namespace content {
-
-AppCacheDispatcher::AppCacheDispatcher(content::AppCacheFrontend* frontend)
-    : frontend_(frontend), binding_(this) {}
-
-AppCacheDispatcher::~AppCacheDispatcher() = default;
-
-void AppCacheDispatcher::Bind(mojom::AppCacheFrontendRequest request) {
-  binding_.Bind(std::move(request));
-}
-
-void AppCacheDispatcher::CacheSelected(int32_t host_id,
-                                       mojom::AppCacheInfoPtr info) {
-  frontend_->OnCacheSelected(host_id, *info);
-}
-
-void AppCacheDispatcher::StatusChanged(const std::vector<int32_t>& host_ids,
-                                       AppCacheStatus status) {
-  frontend_->OnStatusChanged(host_ids, status);
-}
-
-void AppCacheDispatcher::EventRaised(const std::vector<int32_t>& host_ids,
-                                     AppCacheEventID event_id) {
-  frontend_->OnEventRaised(host_ids, event_id);
-}
-
-void AppCacheDispatcher::ProgressEventRaised(
-    const std::vector<int32_t>& host_ids,
-    const GURL& url,
-    int32_t num_total,
-    int32_t num_complete) {
-  frontend_->OnProgressEventRaised(host_ids, url, num_total, num_complete);
-}
-
-void AppCacheDispatcher::ErrorEventRaised(
-    const std::vector<int32_t>& host_ids,
-    mojom::AppCacheErrorDetailsPtr details) {
-  frontend_->OnErrorEventRaised(host_ids, *details);
-}
-
-void AppCacheDispatcher::LogMessage(int32_t host_id,
-                                    int32_t log_level,
-                                    const std::string& message) {
-  frontend_->OnLogMessage(
-      host_id, static_cast<AppCacheLogLevel>(log_level), message);
-}
-
-void AppCacheDispatcher::ContentBlocked(int32_t host_id,
-                                        const GURL& manifest_url) {
-  frontend_->OnContentBlocked(host_id, manifest_url);
-}
-
-void AppCacheDispatcher::SetSubresourceFactory(
-    int32_t host_id,
-    network::mojom::URLLoaderFactoryPtr url_loader_factory) {
-  frontend_->OnSetSubresourceFactory(host_id, std::move(url_loader_factory));
-}
-
-}  // namespace content
diff --git a/content/renderer/appcache/appcache_dispatcher.h b/content/renderer/appcache/appcache_dispatcher.h
deleted file mode 100644
index d864f3f..0000000
--- a/content/renderer/appcache/appcache_dispatcher.h
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_RENDERER_APPCACHE_APPCACHE_DISPATCHER_H_
-#define CONTENT_RENDERER_APPCACHE_APPCACHE_DISPATCHER_H_
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "content/common/appcache.mojom.h"
-#include "content/common/appcache_interfaces.h"
-#include "content/renderer/appcache/appcache_backend_proxy.h"
-#include "ipc/ipc_listener.h"
-#include "mojo/public/cpp/bindings/binding.h"
-
-namespace content {
-
-// Dispatches appcache related messages sent to a child process from the
-// main browser process. There is one instance per child process. Messages
-// are dispatched on the main child thread. The ChildThread base class
-// creates an instance and delegates calls to it.
-class AppCacheDispatcher : public mojom::AppCacheFrontend {
- public:
-  explicit AppCacheDispatcher(content::AppCacheFrontend* frontend);
-  ~AppCacheDispatcher() override;
-
-  void Bind(mojom::AppCacheFrontendRequest request);
-
-  AppCacheBackendProxy* backend_proxy() { return &backend_proxy_; }
-
-
- private:
-  // mojom::AppCacheFrontend
-  void CacheSelected(int32_t host_id, mojom::AppCacheInfoPtr info) override;
-  void StatusChanged(const std::vector<int32_t>& host_ids,
-                     AppCacheStatus status) override;
-  void EventRaised(const std::vector<int32_t>& host_ids,
-                   AppCacheEventID event_id) override;
-  void ProgressEventRaised(const std::vector<int32_t>& host_ids,
-                           const GURL& url,
-                           int32_t num_total,
-                           int32_t num_complete) override;
-  void ErrorEventRaised(const std::vector<int32_t>& host_ids,
-                        mojom::AppCacheErrorDetailsPtr details) override;
-  void LogMessage(int32_t host_id,
-                  int32_t log_level,
-                  const std::string& message) override;
-  void ContentBlocked(int32_t host_id, const GURL& manifest_url) override;
-  void SetSubresourceFactory(
-      int32_t host_id,
-      network::mojom::URLLoaderFactoryPtr url_loader_factory) override;
-
-  AppCacheBackendProxy backend_proxy_;
-  std::unique_ptr<content::AppCacheFrontend> frontend_;
-  mojo::Binding<mojom::AppCacheFrontend> binding_;
-};
-
-}  // namespace content
-
-#endif  // CONTENT_RENDERER_APPCACHE_APPCACHE_DISPATCHER_H_
diff --git a/content/renderer/appcache/appcache_frontend_impl.cc b/content/renderer/appcache/appcache_frontend_impl.cc
index 75e398f8..810893e 100644
--- a/content/renderer/appcache/appcache_frontend_impl.cc
+++ b/content/renderer/appcache/appcache_frontend_impl.cc
@@ -4,7 +4,6 @@
 
 #include "content/renderer/appcache/appcache_frontend_impl.h"
 
-#include "base/logging.h"
 #include "content/renderer/appcache/web_application_cache_host_impl.h"
 #include "third_party/blink/public/web/web_console_message.h"
 
@@ -18,15 +17,22 @@
   return WebApplicationCacheHostImpl::FromId(id);
 }
 
-void AppCacheFrontendImpl::OnCacheSelected(int host_id,
-                                           const AppCacheInfo& info) {
-  WebApplicationCacheHostImpl* host = GetHost(host_id);
-  if (host)
-    host->OnCacheSelected(info);
+AppCacheFrontendImpl::AppCacheFrontendImpl() : binding_(this) {}
+AppCacheFrontendImpl::~AppCacheFrontendImpl() = default;
+
+void AppCacheFrontendImpl::Bind(mojom::AppCacheFrontendRequest request) {
+  binding_.Bind(std::move(request));
 }
 
-void AppCacheFrontendImpl::OnStatusChanged(const std::vector<int>& host_ids,
-                                           AppCacheStatus status) {
+void AppCacheFrontendImpl::CacheSelected(int32_t host_id,
+                                         mojom::AppCacheInfoPtr info) {
+  WebApplicationCacheHostImpl* host = GetHost(host_id);
+  if (host)
+    host->OnCacheSelected(*info);
+}
+
+void AppCacheFrontendImpl::StatusChanged(const std::vector<int32_t>& host_ids,
+                                         AppCacheStatus status) {
   for (auto i = host_ids.begin(); i != host_ids.end(); ++i) {
     WebApplicationCacheHostImpl* host = GetHost(*i);
     if (host)
@@ -34,8 +40,8 @@
   }
 }
 
-void AppCacheFrontendImpl::OnEventRaised(const std::vector<int>& host_ids,
-                                         AppCacheEventID event_id) {
+void AppCacheFrontendImpl::EventRaised(const std::vector<int32_t>& host_ids,
+                                       AppCacheEventID event_id) {
   DCHECK_NE(
       event_id,
       AppCacheEventID::APPCACHE_PROGRESS_EVENT);  // See OnProgressEventRaised.
@@ -48,11 +54,11 @@
   }
 }
 
-void AppCacheFrontendImpl::OnProgressEventRaised(
-    const std::vector<int>& host_ids,
+void AppCacheFrontendImpl::ProgressEventRaised(
+    const std::vector<int32_t>& host_ids,
     const GURL& url,
-    int num_total,
-    int num_complete) {
+    int32_t num_total,
+    int32_t num_complete) {
   for (auto i = host_ids.begin(); i != host_ids.end(); ++i) {
     WebApplicationCacheHostImpl* host = GetHost(*i);
     if (host)
@@ -60,33 +66,33 @@
   }
 }
 
-void AppCacheFrontendImpl::OnErrorEventRaised(
-    const std::vector<int>& host_ids,
-    const AppCacheErrorDetails& details) {
+void AppCacheFrontendImpl::ErrorEventRaised(
+    const std::vector<int32_t>& host_ids,
+    mojom::AppCacheErrorDetailsPtr details) {
   for (auto i = host_ids.begin(); i != host_ids.end(); ++i) {
     WebApplicationCacheHostImpl* host = GetHost(*i);
     if (host)
-      host->OnErrorEventRaised(details);
+      host->OnErrorEventRaised(*details);
   }
 }
 
-void AppCacheFrontendImpl::OnLogMessage(int host_id,
-                                        AppCacheLogLevel log_level,
-                                        const std::string& message) {
+void AppCacheFrontendImpl::LogMessage(int32_t host_id,
+                                      int32_t log_level,
+                                      const std::string& message) {
   WebApplicationCacheHostImpl* host = GetHost(host_id);
   if (host)
-    host->OnLogMessage(log_level, message);
+    host->OnLogMessage(static_cast<AppCacheLogLevel>(log_level), message);
 }
 
-void AppCacheFrontendImpl::OnContentBlocked(int host_id,
-                                            const GURL& manifest_url) {
+void AppCacheFrontendImpl::ContentBlocked(int32_t host_id,
+                                          const GURL& manifest_url) {
   WebApplicationCacheHostImpl* host = GetHost(host_id);
   if (host)
     host->OnContentBlocked(manifest_url);
 }
 
-void AppCacheFrontendImpl::OnSetSubresourceFactory(
-    int host_id,
+void AppCacheFrontendImpl::SetSubresourceFactory(
+    int32_t host_id,
     network::mojom::URLLoaderFactoryPtr url_loader_factory) {
   WebApplicationCacheHostImpl* host = GetHost(host_id);
   if (host)
diff --git a/content/renderer/appcache/appcache_frontend_impl.h b/content/renderer/appcache/appcache_frontend_impl.h
index e8adfc02..5d465a16 100644
--- a/content/renderer/appcache/appcache_frontend_impl.h
+++ b/content/renderer/appcache/appcache_frontend_impl.h
@@ -5,30 +5,51 @@
 #ifndef CONTENT_RENDERER_APPCACHE_APPCACHE_FRONTEND_IMPL_H_
 #define CONTENT_RENDERER_APPCACHE_APPCACHE_FRONTEND_IMPL_H_
 
+#include <cstdint>
+#include <string>
+#include <vector>
+
+#include "content/common/appcache.mojom.h"
 #include "content/common/appcache_interfaces.h"
+#include "content/renderer/appcache/appcache_backend_proxy.h"
+#include "mojo/public/cpp/bindings/binding.h"
 
 namespace content {
 
-class AppCacheFrontendImpl : public AppCacheFrontend {
+// Dispatches appcache related messages sent to a child process from the main
+// browser process. There is one instance per child process.
+class AppCacheFrontendImpl : public mojom::AppCacheFrontend {
  public:
-  void OnCacheSelected(int host_id, const AppCacheInfo& info) override;
-  void OnStatusChanged(const std::vector<int>& host_ids,
-                       AppCacheStatus status) override;
-  void OnEventRaised(const std::vector<int>& host_ids,
-                     AppCacheEventID event_id) override;
-  void OnProgressEventRaised(const std::vector<int>& host_ids,
-                             const GURL& url,
-                             int num_total,
-                             int num_complete) override;
-  void OnErrorEventRaised(const std::vector<int>& host_ids,
-                          const AppCacheErrorDetails& details) override;
-  void OnLogMessage(int host_id,
-                    AppCacheLogLevel log_level,
-                    const std::string& message) override;
-  void OnContentBlocked(int host_id, const GURL& manifest_url) override;
-  void OnSetSubresourceFactory(
-      int host_id,
+  AppCacheFrontendImpl();
+  ~AppCacheFrontendImpl() override;
+
+  void Bind(mojom::AppCacheFrontendRequest request);
+
+  AppCacheBackendProxy* backend_proxy() { return &backend_proxy_; }
+
+ private:
+  // mojom::AppCacheFrontend
+  void CacheSelected(int32_t host_id, mojom::AppCacheInfoPtr info) override;
+  void StatusChanged(const std::vector<int32_t>& host_ids,
+                     AppCacheStatus status) override;
+  void EventRaised(const std::vector<int32_t>& host_ids,
+                   AppCacheEventID event_id) override;
+  void ProgressEventRaised(const std::vector<int32_t>& host_ids,
+                           const GURL& url,
+                           int32_t num_total,
+                           int32_t num_complete) override;
+  void ErrorEventRaised(const std::vector<int32_t>& host_ids,
+                        mojom::AppCacheErrorDetailsPtr details) override;
+  void LogMessage(int32_t host_id,
+                  int32_t log_level,
+                  const std::string& message) override;
+  void ContentBlocked(int32_t host_id, const GURL& manifest_url) override;
+  void SetSubresourceFactory(
+      int32_t host_id,
       network::mojom::URLLoaderFactoryPtr url_loader_factory) override;
+
+  AppCacheBackendProxy backend_proxy_;
+  mojo::Binding<mojom::AppCacheFrontend> binding_;
 };
 
 }  // namespace content
diff --git a/content/renderer/loader/resource_dispatcher.cc b/content/renderer/loader/resource_dispatcher.cc
index 8a319765..09f05b19 100644
--- a/content/renderer/loader/resource_dispatcher.cc
+++ b/content/renderer/loader/resource_dispatcher.cc
@@ -462,7 +462,9 @@
     if (request_info->redirect_requires_loader_restart) {
       request_info->url_loader->FollowRedirectForcingRestart();
     } else {
-      request_info->url_loader->FollowRedirect(base::nullopt);
+      request_info->url_loader->FollowRedirect(
+          base::nullopt /* removed_headers */,
+          base::nullopt /* modified_headers */);
     }
   }
 }
diff --git a/content/renderer/media_recorder/media_recorder_handler.cc b/content/renderer/media_recorder/media_recorder_handler.cc
index 391d638..ba06d9a7 100644
--- a/content/renderer/media_recorder/media_recorder_handler.cc
+++ b/content/renderer/media_recorder/media_recorder_handler.cc
@@ -247,13 +247,12 @@
   }
 
   const bool use_video_tracks =
-      !video_tracks_.IsEmpty() && video_tracks_[0].IsEnabled() &&
+      !video_tracks_.IsEmpty() &&
       video_tracks_[0].Source().GetReadyState() !=
           blink::WebMediaStreamSource::kReadyStateEnded;
   const bool use_audio_tracks =
       !audio_tracks_.IsEmpty() &&
       MediaStreamAudioTrack::From(audio_tracks_[0]) &&
-      audio_tracks_[0].IsEnabled() &&
       audio_tracks_[0].Source().GetReadyState() !=
           blink::WebMediaStreamSource::kReadyStateEnded;
 
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 33b0e82..518f8b8 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -90,7 +90,7 @@
 #include "content/public/renderer/renderer_ppapi_host.h"
 #include "content/renderer/accessibility/aom_content_ax_tree.h"
 #include "content/renderer/accessibility/render_accessibility_impl.h"
-#include "content/renderer/appcache/appcache_dispatcher.h"
+#include "content/renderer/appcache/appcache_frontend_impl.h"
 #include "content/renderer/browser_plugin/browser_plugin.h"
 #include "content/renderer/browser_plugin/browser_plugin_manager.h"
 #include "content/renderer/compositor/layer_tree_view.h"
@@ -3706,7 +3706,7 @@
 
   return std::make_unique<RendererWebApplicationCacheHostImpl>(
       RenderViewImpl::FromWebView(frame_->View()), client,
-      RenderThreadImpl::current()->appcache_dispatcher()->backend_proxy(),
+      RenderThreadImpl::current()->appcache_frontend_impl()->backend_proxy(),
       navigation_state->commit_params().appcache_host_id, routing_id_);
 }
 
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index 5cf96a0..19e1317 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -79,7 +79,6 @@
 #include "content/public/renderer/content_renderer_client.h"
 #include "content/public/renderer/render_thread_observer.h"
 #include "content/public/renderer/render_view_visitor.h"
-#include "content/renderer/appcache/appcache_dispatcher.h"
 #include "content/renderer/appcache/appcache_frontend_impl.h"
 #include "content/renderer/browser_plugin/browser_plugin_manager.h"
 #include "content/renderer/categorized_worker_pool.h"
@@ -756,11 +755,10 @@
   widget_count_ = 0;
   hidden_widget_count_ = 0;
 
-  appcache_dispatcher_.reset(
-      new AppCacheDispatcher(new AppCacheFrontendImpl()));
+  appcache_frontend_impl_ = std::make_unique<AppCacheFrontendImpl>();
   registry->AddInterface(
-      base::BindRepeating(&AppCacheDispatcher::Bind,
-                          base::Unretained(appcache_dispatcher())),
+      base::BindRepeating(&AppCacheFrontendImpl::Bind,
+                          base::Unretained(appcache_frontend_impl())),
       GetWebMainThreadScheduler()->IPCTaskRunner());
   dom_storage_dispatcher_.reset(new DomStorageDispatcher());
 
diff --git a/content/renderer/render_thread_impl.h b/content/renderer/render_thread_impl.h
index ef2f1af..099a26df 100644
--- a/content/renderer/render_thread_impl.h
+++ b/content/renderer/render_thread_impl.h
@@ -117,7 +117,7 @@
 
 namespace content {
 
-class AppCacheDispatcher;
+class AppCacheFrontendImpl;
 class AecDumpMessageFilter;
 class AudioRendererMixerManager;
 class BrowserPluginManager;
@@ -315,8 +315,8 @@
     return compositor_task_runner_;
   }
 
-  AppCacheDispatcher* appcache_dispatcher() const {
-    return appcache_dispatcher_.get();
+  AppCacheFrontendImpl* appcache_frontend_impl() const {
+    return appcache_frontend_impl_.get();
   }
 
   DomStorageDispatcher* dom_storage_dispatcher() const {
@@ -588,7 +588,7 @@
       discardable_shared_memory_manager_;
 
   // These objects live solely on the render thread.
-  std::unique_ptr<AppCacheDispatcher> appcache_dispatcher_;
+  std::unique_ptr<AppCacheFrontendImpl> appcache_frontend_impl_;
   std::unique_ptr<DomStorageDispatcher> dom_storage_dispatcher_;
   std::unique_ptr<blink::scheduler::WebThreadScheduler> main_thread_scheduler_;
   std::unique_ptr<RendererBlinkPlatformImpl> blink_platform_impl_;
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index b3e7f4a..1f242ea 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -67,7 +67,6 @@
 #include "content/public/renderer/render_view_observer.h"
 #include "content/public/renderer/render_view_visitor.h"
 #include "content/public/renderer/window_features_converter.h"
-#include "content/renderer/appcache/appcache_dispatcher.h"
 #include "content/renderer/appcache/web_application_cache_host_impl.h"
 #include "content/renderer/browser_plugin/browser_plugin.h"
 #include "content/renderer/browser_plugin/browser_plugin_manager.h"
diff --git a/content/renderer/service_worker/service_worker_subresource_loader.cc b/content/renderer/service_worker/service_worker_subresource_loader.cc
index 68df0916a..2ab6ad2 100644
--- a/content/renderer/service_worker/service_worker_subresource_loader.cc
+++ b/content/renderer/service_worker/service_worker_subresource_loader.cc
@@ -653,9 +653,8 @@
 // ServiceWorkerSubresourceLoader: URLLoader implementation -----------------
 
 void ServiceWorkerSubresourceLoader::FollowRedirect(
-    const base::Optional<std::vector<std::string>>&
-        to_be_removed_request_headers,
-    const base::Optional<net::HttpRequestHeaders>& modified_request_headers,
+    const base::Optional<std::vector<std::string>>& removed_headers,
+    const base::Optional<net::HttpRequestHeaders>& modified_headers,
     const base::Optional<GURL>& new_url) {
   TRACE_EVENT_WITH_FLOW1(
       "ServiceWorker", "ServiceWorkerSubresourceLoader::FollowRedirect",
@@ -663,17 +662,20 @@
                           TRACE_ID_LOCAL(request_id_)),
       TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "new_url",
       redirect_info_->new_url.spec());
-  DCHECK(!modified_request_headers.has_value()) << "Redirect with modified "
-                                                   "headers was not supported "
-                                                   "yet. crbug.com/845683";
-  DCHECK(!new_url.has_value()) << "Redirect with modified URL was not "
+  // TODO(arthursonzogni, juncai): This seems to be correctly implemented, but
+  // not used so far. Add tests and remove this DCHECK to support this feature
+  // if needed. See https://crbug.com/845683.
+  DCHECK(!removed_headers && !modified_headers)
+      << "Redirect with removed or modified headers is not supported yet. See "
+         "https://crbug.com/845683";
+  DCHECK(!new_url.has_value()) << "Redirect with modified url was not "
                                   "supported yet. crbug.com/845683";
   DCHECK(redirect_info_);
 
   bool should_clear_upload = false;
   net::RedirectUtil::UpdateHttpRequest(
       resource_request_.url, resource_request_.method, *redirect_info_,
-      modified_request_headers, &resource_request_.headers,
+      removed_headers, modified_headers, &resource_request_.headers,
       &should_clear_upload);
   if (should_clear_upload)
     resource_request_.request_body = nullptr;
diff --git a/content/renderer/service_worker/service_worker_subresource_loader.h b/content/renderer/service_worker/service_worker_subresource_loader.h
index 8e2cb7e..0148819 100644
--- a/content/renderer/service_worker/service_worker_subresource_loader.h
+++ b/content/renderer/service_worker/service_worker_subresource_loader.h
@@ -107,9 +107,8 @@
 
   // network::mojom::URLLoader overrides:
   void FollowRedirect(
-      const base::Optional<std::vector<std::string>>&
-          to_be_removed_request_headers,
-      const base::Optional<net::HttpRequestHeaders>& modified_request_headers,
+      const base::Optional<std::vector<std::string>>& removed_headers,
+      const base::Optional<net::HttpRequestHeaders>& modified_headers,
       const base::Optional<GURL>& new_url) override;
   void ProceedWithResponse() override;
   void SetPriority(net::RequestPriority priority,
diff --git a/content/renderer/shared_worker/embedded_shared_worker_stub.cc b/content/renderer/shared_worker/embedded_shared_worker_stub.cc
index adf241f..6e7bd1fd 100644
--- a/content/renderer/shared_worker/embedded_shared_worker_stub.cc
+++ b/content/renderer/shared_worker/embedded_shared_worker_stub.cc
@@ -19,7 +19,7 @@
 #include "content/public/common/origin_util.h"
 #include "content/public/common/renderer_preferences.h"
 #include "content/public/renderer/content_renderer_client.h"
-#include "content/renderer/appcache/appcache_dispatcher.h"
+#include "content/renderer/appcache/appcache_frontend_impl.h"
 #include "content/renderer/appcache/web_application_cache_host_impl.h"
 #include "content/renderer/loader/child_url_loader_factory_bundle.h"
 #include "content/renderer/loader/request_extra_data.h"
@@ -57,10 +57,11 @@
   SharedWorkerWebApplicationCacheHostImpl(
       blink::WebApplicationCacheHostClient* client,
       int appcache_host_id)
-      : WebApplicationCacheHostImpl(
-            client,
-            RenderThreadImpl::current()->appcache_dispatcher()->backend_proxy(),
-            appcache_host_id) {}
+      : WebApplicationCacheHostImpl(client,
+                                    RenderThreadImpl::current()
+                                        ->appcache_frontend_impl()
+                                        ->backend_proxy(),
+                                    appcache_host_id) {}
 
   // Main resource loading is different for workers. The main resource is
   // loaded by the worker using WorkerClassicScriptLoader.
diff --git a/content/shell/browser/web_test/web_test_content_browser_client.cc b/content/shell/browser/web_test/web_test_content_browser_client.cc
index 9b34749..13627e8e 100644
--- a/content/shell/browser/web_test/web_test_content_browser_client.cc
+++ b/content/shell/browser/web_test/web_test_content_browser_client.cc
@@ -290,7 +290,7 @@
     content::RenderFrameHost* opener,
     const GURL& opener_url,
     const GURL& opener_top_level_frame_url,
-    const GURL& source_origin,
+    const url::Origin& source_origin,
     content::mojom::WindowContainerType container_type,
     const GURL& target_url,
     const content::Referrer& referrer,
diff --git a/content/shell/browser/web_test/web_test_content_browser_client.h b/content/shell/browser/web_test/web_test_content_browser_client.h
index e28c57f6..ad388b2 100644
--- a/content/shell/browser/web_test/web_test_content_browser_client.h
+++ b/content/shell/browser/web_test/web_test_content_browser_client.h
@@ -60,7 +60,7 @@
   bool CanCreateWindow(content::RenderFrameHost* opener,
                        const GURL& opener_url,
                        const GURL& opener_top_level_frame_url,
-                       const GURL& source_origin,
+                       const url::Origin& source_origin,
                        content::mojom::WindowContainerType container_type,
                        const GURL& target_url,
                        const content::Referrer& referrer,
diff --git a/content/test/data/page_with_touch_start_janking_main_thread.html b/content/test/data/page_with_touch_start_janking_main_thread.html
index 7480d1fd..1979c37 100644
--- a/content/test/data/page_with_touch_start_janking_main_thread.html
+++ b/content/test/data/page_with_touch_start_janking_main_thread.html
@@ -10,10 +10,10 @@
 </body>
 <script>
 function setup() {
-  janktest.ontouchstart = function() {
+  document.getElementById('janktest').addEventListener('touchstart', (e) => {
     var end = performance.now() + 1200;
-    while (performance.now() < end) ;
-  };
+    while (performance.now() < end);
+  }, {passive: false});
 }
 </script>
 </html>
diff --git a/content/test/gpu/gpu_tests/pixel_expectations.py b/content/test/gpu/gpu_tests/pixel_expectations.py
index 9e5705d..09da641c 100644
--- a/content/test/gpu/gpu_tests/pixel_expectations.py
+++ b/content/test/gpu/gpu_tests/pixel_expectations.py
@@ -46,13 +46,6 @@
     self.Fail('Pixel_ScissorTestWithPreserveDrawingBuffer',
         ['android'], bug=521588)
 
-    # TODO(kbr): re-enable after rebaseline to clear up Nexus 5 failures
-    self.Fail('Pixel_Canvas2DRedBox', bug=918952)
-    self.Fail('Pixel_CanvasDisplayLinearRGBAccelerated2D', bug=918952)
-    self.Fail('Pixel_CanvasDisplayLinearRGBUnaccelerated2DGPUCompositing',
-              bug=918952)
-    self.Fail('Pixel_CanvasLowLatency2D', bug=918952)
-
     # TODO(vmiura) check / generate reference images for Android devices
     self.Fail('Pixel_SolidColorBackground', ['mac', 'android'], bug=624256)
 
diff --git a/content/test/url_loader_interceptor_test.cc b/content/test/url_loader_interceptor_test.cc
index 666691c6..61004e24 100644
--- a/content/test/url_loader_interceptor_test.cc
+++ b/content/test/url_loader_interceptor_test.cc
@@ -81,6 +81,48 @@
   EXPECT_FALSE(NavigateToURL(shell(), GetPageURL()));
 }
 
+IN_PROC_BROWSER_TEST_F(URLLoaderInterceptorTest,
+                       AsynchronousInitializationInterceptFrame) {
+  GURL url = GetPageURL();
+  base::RunLoop run_loop;
+  URLLoaderInterceptor interceptor(
+      base::BindLambdaForTesting(
+          [&](URLLoaderInterceptor::RequestParams* params) {
+            EXPECT_EQ(params->url_request.url, url);
+            EXPECT_EQ(params->process_id, 0);
+            network::URLLoaderCompletionStatus status;
+            status.error_code = net::ERR_FAILED;
+            params->client->OnComplete(status);
+            return true;
+          }),
+      run_loop.QuitClosure());
+  run_loop.Run();
+  EXPECT_FALSE(NavigateToURL(shell(), GetPageURL()));
+}
+
+IN_PROC_BROWSER_TEST_F(URLLoaderInterceptorTest,
+                       AsynchronousDestructionIsAppliedImmediately) {
+  const GURL url = GetPageURL();
+  {
+    base::RunLoop run_loop;
+    URLLoaderInterceptor interceptor(
+        base::BindLambdaForTesting(
+            [&](URLLoaderInterceptor::RequestParams* params) {
+              EXPECT_EQ(params->url_request.url, url);
+              EXPECT_EQ(params->process_id, 0);
+              network::URLLoaderCompletionStatus status;
+              status.error_code = net::ERR_FAILED;
+              params->client->OnComplete(status);
+              return true;
+            }),
+        run_loop.QuitClosure());
+    run_loop.Run();
+
+    ASSERT_FALSE(NavigateToURL(shell(), url));
+  }
+  EXPECT_TRUE(NavigateToURL(shell(), url));
+}
+
 class TestBrowserClientWithHeaderClient
     : public ContentBrowserClient,
       public network::mojom::TrustedURLLoaderHeaderClient {
diff --git a/docs/chrome_os_logging.md b/docs/chrome_os_logging.md
index b04d593c0..07c2adba 100644
--- a/docs/chrome_os_logging.md
+++ b/docs/chrome_os_logging.md
@@ -27,14 +27,16 @@
 By default, only messages logged at severity `WARNING` or higher are written to
 disk. More concretely, `LOG(INFO)` messages are discarded.
 
-## Verbose Logging
-
-When actively debugging issues, Chrome's `--vmodule` flag is sometimes used to
-temporarily log messages at lower severities for particular modules. See the
+To enable `LOG(INFO)` message, pass `--log-level=0` to Chrome. See the
 [Passing Chrome flags from session_manager] document for more details, and
 specifically the `/etc/chrome_dev.conf` mechanism that can be used to
 change flags on development devices.
 
+## Verbose Logging
+
+When actively debugging issues, Chrome's `--vmodule` flag is sometimes used to
+temporarily log messages at lower severities for particular modules.
+
 [base/logging.h]: ../base/logging.h
 [session_manager]: https://chromium.googlesource.com/chromiumos/platform2/+/master/login_manager/
 [Passing Chrome flags from session_manager]: https://chromium.googlesource.com/chromiumos/platform2/+/master/login_manager/docs/flags.md
diff --git a/extensions/browser/BUILD.gn b/extensions/browser/BUILD.gn
index d776a49..a5f1861 100644
--- a/extensions/browser/BUILD.gn
+++ b/extensions/browser/BUILD.gn
@@ -505,6 +505,7 @@
       "//chromeos",
       "//chromeos:media_perception_proto",
       "//chromeos:power_manager_proto",
+      "//chromeos/login/login_state",
     ]
   }
 }
@@ -691,6 +692,7 @@
     deps += [
       "//chromeos:media_perception_proto",
       "//chromeos:test_support",
+      "//chromeos/login/login_state",
       "//components/feedback",
       "//extensions/shell:app_shell_lib",
     ]
diff --git a/extensions/browser/api/BUILD.gn b/extensions/browser/api/BUILD.gn
index 1adcdad..9f4b7182 100644
--- a/extensions/browser/api/BUILD.gn
+++ b/extensions/browser/api/BUILD.gn
@@ -2,8 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//extensions/common/api/schema.gni")
 import("//extensions/buildflags/buildflags.gni")
+import("//extensions/common/api/schema.gni")
 import("//tools/json_schema_compiler/json_schema_api.gni")
 
 assert(enable_extensions,
@@ -144,6 +144,7 @@
     deps += [
       "//chromeos",
       "//chromeos:media_perception_proto",
+      "//chromeos/login/login_state",
       "//chromeos/services/media_perception/public/mojom",
       "//chromeos/services/media_perception/public/mojom:mojom_js_data_deps",
     ]
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h
index 738dacf..bdea2ea 100644
--- a/extensions/browser/extension_function_histogram_value.h
+++ b/extensions/browser/extension_function_histogram_value.h
@@ -1367,6 +1367,7 @@
   ACCESSIBILITY_PRIVATE_SETSWITCHACCESSMENUSTATE = 1304,
   AUTOTESTPRIVATE_SENDASSISTANTTEXTQUERY = 1305,
   AUTOTESTPRIVATE_SETCROSTINIAPPSCALED = 1306,
+  ACTIVITYLOGPRIVATE_DELETEACTIVITIESBYEXTENSION = 1307,
   // Last entry: Add new entries above, then run:
   // python tools/metrics/histograms/update_extension_histograms.py
   ENUM_BOUNDARY
diff --git a/extensions/shell/BUILD.gn b/extensions/shell/BUILD.gn
index 40685d7..dfb4201 100644
--- a/extensions/shell/BUILD.gn
+++ b/extensions/shell/BUILD.gn
@@ -228,6 +228,7 @@
     ]
     deps += [
       "//chromeos",
+      "//chromeos/login/login_state",
       "//ui/chromeos",
       "//ui/display/manager",
     ]
diff --git a/gpu/command_buffer/tests/gl_oob_attrib_unittest.cc b/gpu/command_buffer/tests/gl_oob_attrib_unittest.cc
index d4c050f5..f0bd23ce 100644
--- a/gpu/command_buffer/tests/gl_oob_attrib_unittest.cc
+++ b/gpu/command_buffer/tests/gl_oob_attrib_unittest.cc
@@ -24,6 +24,15 @@
 // Tests that enabling a vertex array for a location that matches any column of
 // a matrix attribute correctly triggers out-of-bounds checks.
 TEST_F(GLOOBAttribTest, DrawUsingOOBMatrixAttrib) {
+  // The passthrough command decoder uses robust buffer access behaviour. This
+  // makes OOB error checks unimportant because OOB accesses will not cause
+  // errors. OOB accesses will also return implementation-dependent values.
+  // See the KHR_robust_buffer_access_behavior spec for more information.
+  if (gl_.gpu_preferences().use_passthrough_cmd_decoder) {
+    std::cout << "Test skipped, KHR_robust_buffer_access_behavior enabled.\n";
+    return;
+  }
+
   const char kVertexShader[] =
       "attribute mat3 attrib;\n"
       "varying vec4 color;\n"
diff --git a/gpu/ipc/service/image_transport_surface_fuchsia.cc b/gpu/ipc/service/image_transport_surface_fuchsia.cc
index f2feaed..636ded4e 100644
--- a/gpu/ipc/service/image_transport_surface_fuchsia.cc
+++ b/gpu/ipc/service/image_transport_surface_fuchsia.cc
@@ -5,6 +5,7 @@
 #include "gpu/ipc/service/image_transport_surface.h"
 
 #include "base/logging.h"
+#include "gpu/ipc/service/pass_through_image_transport_surface.h"
 #include "ui/gl/gl_surface.h"
 #include "ui/gl/gl_surface_stub.h"
 #include "ui/gl/init/gl_factory.h"
@@ -21,7 +22,13 @@
     return new gl::GLSurfaceStub;
   }
 
-  return gl::init::CreateViewGLSurface(surface_handle);
+  scoped_refptr<gl::GLSurface> surface =
+      gl::init::CreateViewGLSurface(surface_handle);
+
+  if (!surface)
+    return surface;
+  return base::MakeRefCounted<PassThroughImageTransportSurface>(
+      delegate, surface.get(), false);
 }
 
 }  // namespace gpu
diff --git a/gpu/vulkan/vulkan_swap_chain.cc b/gpu/vulkan/vulkan_swap_chain.cc
index cd21927a..84c9d38 100644
--- a/gpu/vulkan/vulkan_swap_chain.cc
+++ b/gpu/vulkan/vulkan_swap_chain.cc
@@ -210,7 +210,7 @@
   swap_chain_create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
   swap_chain_create_info.surface = surface;
   swap_chain_create_info.minImageCount =
-      std::max(2u, surface_caps.minImageCount);
+      std::max(3u, surface_caps.minImageCount);
   swap_chain_create_info.imageFormat = surface_format.format;
   swap_chain_create_info.imageColorSpace = surface_format.colorSpace;
   swap_chain_create_info.imageExtent = surface_caps.currentExtent;
diff --git a/ios/chrome/browser/snapshots/snapshot_generator.mm b/ios/chrome/browser/snapshots/snapshot_generator.mm
index 8bd42a2..1b38acd 100644
--- a/ios/chrome/browser/snapshots/snapshot_generator.mm
+++ b/ios/chrome/browser/snapshots/snapshot_generator.mm
@@ -116,9 +116,7 @@
 
 - (UIImage*)updateSnapshot {
   UIImage* snapshot = [self generateSnapshotWithOverlays:YES];
-  if (snapshot) {
-    [self.snapshotCache setImage:snapshot withSessionID:self.sessionID];
-  }
+  [self updateSnapshotCacheWithImage:snapshot];
   return snapshot;
 }
 
diff --git a/ios/chrome/browser/tabs/tab_model.mm b/ios/chrome/browser/tabs/tab_model.mm
index ef1dc830..962c7ae 100644
--- a/ios/chrome/browser/tabs/tab_model.mm
+++ b/ios/chrome/browser/tabs/tab_model.mm
@@ -912,6 +912,17 @@
     navigationItem = webState->GetNavigationManager()->GetLastCommittedItem();
   }
 
+  if (!navigationItem) {
+    // Pending item may not exist due to the bug in //ios/web layer.
+    // TODO(crbug.com/899827): remove this early return once GetPendingItem()
+    // always return valid object inside WebStateObserver::DidStartNavigation()
+    // callback.
+    //
+    // Note that GetLastCommittedItem() returns null if navigation manager does
+    // not have committed items (which is normal situation).
+    return;
+  }
+
   [[OmniboxGeolocationController sharedInstance]
       addLocationToNavigationItem:navigationItem
                      browserState:ios::ChromeBrowserState::FromBrowserState(
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm
index 0e3a8c54..2ba7c50 100644
--- a/ios/chrome/browser/ui/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -2513,6 +2513,10 @@
   NewTabPageTabHelper* NTPHelper = NewTabPageTabHelper::FromWebState(webState);
   if (!NTPHelper || !NTPHelper->IsActive())
     return CGRectZero;
+  if (base::FeatureList::IsEnabled(
+          web::features::kBrowserContainerFullscreen) &&
+      !IsRegularXRegularSizeClass())
+    return self.contentArea.bounds;
   // NTP expects to be laid out behind the bottom toolbar.  It uses
   // |contentInset| to push content above the toolbar.
   UIEdgeInsets viewportInsets = [self viewportInsetsForView:self.contentArea];
diff --git a/ios/chrome/browser/ui/settings/cells/BUILD.gn b/ios/chrome/browser/ui/settings/cells/BUILD.gn
index 1938fe4a..e6c01298 100644
--- a/ios/chrome/browser/ui/settings/cells/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/cells/BUILD.gn
@@ -4,6 +4,8 @@
 
 source_set("cells") {
   sources = [
+    "account_sign_in_item.h",
+    "account_sign_in_item.mm",
     "autofill_data_item.h",
     "autofill_data_item.mm",
     "byo_textfield_item.h",
@@ -61,6 +63,8 @@
     "//ios/chrome/browser/ui/icons",
     "//ios/chrome/browser/ui/table_view:styler",
     "//ios/chrome/browser/ui/table_view/cells",
+    "//ios/public/provider/chrome/browser",
+    "//ios/public/provider/chrome/browser/signin",
     "//ios/third_party/material_roboto_font_loader_ios",
     "//ui/base",
   ]
diff --git a/ios/chrome/browser/ui/settings/cells/account_sign_in_item.h b/ios/chrome/browser/ui/settings/cells/account_sign_in_item.h
new file mode 100644
index 0000000..a9db60b
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/cells/account_sign_in_item.h
@@ -0,0 +1,18 @@
+// 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 IOS_CHROME_BROWSER_UI_SETTINGS_CELLS_ACCOUNT_SIGN_IN_ITEM_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_CELLS_ACCOUNT_SIGN_IN_ITEM_H_
+
+#import "ios/chrome/browser/ui/table_view/cells/table_view_item.h"
+
+// Item to display a non-personnalized sign-in cell.
+@interface AccountSignInItem : TableViewItem
+
+// Subtitle for the sign-in cell (optional).
+@property(nonatomic, strong) NSString* detailText;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_CELLS_ACCOUNT_SIGN_IN_ITEM_H_
diff --git a/ios/chrome/browser/ui/settings/cells/account_sign_in_item.mm b/ios/chrome/browser/ui/settings/cells/account_sign_in_item.mm
new file mode 100644
index 0000000..45a2b97
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/cells/account_sign_in_item.mm
@@ -0,0 +1,50 @@
+// 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 "ios/chrome/browser/ui/settings/cells/account_sign_in_item.h"
+
+#import "ios/chrome/browser/ui/settings/cells/settings_image_detail_text_cell.h"
+#import "ios/chrome/browser/ui/util/uikit_ui_util.h"
+#include "ios/chrome/grit/ios_chromium_strings.h"
+#include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
+#include "ios/public/provider/chrome/browser/signin/signin_resources_provider.h"
+#include "ui/base/l10n/l10n_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+
+// Account profile picture size.
+const CGFloat kAccountProfilePhotoDimension = 40.0f;
+
+}  // namespace
+
+@implementation AccountSignInItem
+
+- (instancetype)initWithType:(NSInteger)type {
+  self = [super initWithType:type];
+  if (self) {
+    self.cellClass = [SettingsImageDetailTextCell class];
+    self.accessibilityTraits |= UIAccessibilityTraitButton;
+  }
+  return self;
+}
+
+#pragma mark - CollectionViewItem
+
+- (void)configureCell:(SettingsImageDetailTextCell*)cell
+           withStyler:(ChromeTableViewStyler*)styler {
+  [super configureCell:cell withStyler:styler];
+  cell.textLabel.text =
+      l10n_util::GetNSString(IDS_IOS_SIGN_IN_TO_CHROME_SETTING_TITLE);
+  cell.detailTextLabel.text = self.detailText;
+  cell.image = CircularImageFromImage(ios::GetChromeBrowserProvider()
+                                          ->GetSigninResourcesProvider()
+                                          ->GetDefaultAvatar(),
+                                      kAccountProfilePhotoDimension);
+}
+
+@end
diff --git a/ios/chrome/browser/ui/settings/table_cell_catalog_view_controller.mm b/ios/chrome/browser/ui/settings/table_cell_catalog_view_controller.mm
index 866ecff..3cdadcf 100644
--- a/ios/chrome/browser/ui/settings/table_cell_catalog_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/table_cell_catalog_view_controller.mm
@@ -10,6 +10,7 @@
 #import "ios/chrome/browser/ui/authentication/cells/table_view_signin_promo_item.h"
 #import "ios/chrome/browser/ui/autofill/cells/autofill_edit_item.h"
 #import "ios/chrome/browser/ui/icons/chrome_icon.h"
+#import "ios/chrome/browser/ui/settings/cells/account_sign_in_item.h"
 #import "ios/chrome/browser/ui/settings/cells/autofill_data_item.h"
 #import "ios/chrome/browser/ui/settings/cells/copied_to_chrome_item.h"
 #import "ios/chrome/browser/ui/settings/cells/encryption_item.h"
@@ -58,6 +59,7 @@
   ItemTypeEncryption,
   ItemTypeLinkFooter,
   ItemTypeDetailText,
+  ItemTypeAccountSignInItem,
   ItemTypeSettingsSwitch1,
   ItemTypeSettingsSwitch2,
   ItemTypeSyncSwitch,
@@ -181,6 +183,12 @@
   [model addItem:settingsDetailItemLong
       toSectionWithIdentifier:SectionIdentifierSettings];
 
+  AccountSignInItem* accountSignInItem =
+      [[AccountSignInItem alloc] initWithType:ItemTypeAccountSignInItem];
+  accountSignInItem.detailText = @"Get cool stuff on all your devices";
+  [model addItem:accountSignInItem
+      toSectionWithIdentifier:SectionIdentifierSettings];
+
   SettingsSwitchItem* settingsSwitchItem =
       [[SettingsSwitchItem alloc] initWithType:ItemTypeSettingsSwitch1];
   settingsSwitchItem.text = @"This is a settings switch item";
diff --git a/media/base/BUILD.gn b/media/base/BUILD.gn
index 726a228..c79d7af 100644
--- a/media/base/BUILD.gn
+++ b/media/base/BUILD.gn
@@ -212,6 +212,8 @@
     "provision_fetcher.h",
     "ranges.cc",
     "ranges.h",
+    "reentrancy_checker.cc",
+    "reentrancy_checker.h",
     "renderer.cc",
     "renderer.h",
     "renderer_client.h",
@@ -504,6 +506,7 @@
     "null_video_sink_unittest.cc",
     "pipeline_impl_unittest.cc",
     "ranges_unittest.cc",
+    "reentrancy_checker_unittest.cc",
     "renderer_factory_selector_unittest.cc",
     "seekable_buffer_unittest.cc",
     "serial_runner_unittest.cc",
diff --git a/media/base/reentrancy_checker.cc b/media/base/reentrancy_checker.cc
new file mode 100644
index 0000000..296503de
--- /dev/null
+++ b/media/base/reentrancy_checker.cc
@@ -0,0 +1,26 @@
+// 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 "media/base/reentrancy_checker.h"
+
+namespace media {
+
+NonReentrantScope::NonReentrantScope(base::Lock& lock) : lock_(lock) {
+  is_lock_holder_ = lock_.Try();
+
+  // TODO(sandersd): Allow the caller to provide the message? The macro knows
+  // the name of the scope.
+  if (!is_lock_holder_)
+    LOG(FATAL) << "Non-reentrant scope was reentered";
+}
+
+NonReentrantScope::~NonReentrantScope() {
+  if (!is_lock_holder_)
+    return;
+
+  lock_.AssertAcquired();
+  lock_.Release();
+}
+
+}  // namespace media
diff --git a/media/base/reentrancy_checker.h b/media/base/reentrancy_checker.h
new file mode 100644
index 0000000..4ac134b
--- /dev/null
+++ b/media/base/reentrancy_checker.h
@@ -0,0 +1,64 @@
+// 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 MEDIA_BASE_REENTRANCY_CHECKER_H_
+#define MEDIA_BASE_REENTRANCY_CHECKER_H_
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/synchronization/lock.h"
+#include "base/thread_annotations.h"
+#include "media/base/media_export.h"
+
+// Asserts that a non-reentrant scope (which can span multiple methods) is not
+// reentered.
+//
+// No-op and zero-sized when DCHECKs are disabled at build time.
+//
+// Failures are reported by LOG(FATAL):
+//   [...:FATAL:reentrancy_checker.cc(15)] Non-reentrant scope reentered
+//   #0 0x7f12ef2ee8dd base::debug::StackTrace::StackTrace()
+//   #1 0x7f12eefdffaa base::debug::StackTrace::StackTrace()
+//   #2 0x7f12ef051e0b logging::LogMessage::~LogMessage()
+//   #3 0x7f12edc8bfc6 media::NonReentrantScope::NonReentrantScope()
+//   #4 0x7f12edd93e41 MyClass::MyMethod()
+//
+// Usage:
+//   class MyClass {
+//    public:
+//     void MyMethod() {
+//       NON_REENTRANT_SCOPE(my_reentrancy_checker_);
+//       ...
+//      }
+//
+//    private:
+//     REENTRANCY_CHECKER(my_reentrancy_checker_);
+//   };
+
+#if DCHECK_IS_ON()
+#define REENTRANCY_CHECKER(name) ::base::Lock name
+#define NON_REENTRANT_SCOPE(name) ::media::NonReentrantScope name##scope(name)
+#else  // DCHECK_IS_ON()
+#define REENTRANCY_CHECKER(name)
+#define NON_REENTRANT_SCOPE(name)
+#endif  // DCHECK_IS_ON()
+
+namespace media {
+
+// Implementation of NON_REENTRANT_SCOPE(). Do not use directly.
+class SCOPED_LOCKABLE MEDIA_EXPORT NonReentrantScope {
+ public:
+  explicit NonReentrantScope(base::Lock& lock) EXCLUSIVE_LOCK_FUNCTION(lock);
+  ~NonReentrantScope() UNLOCK_FUNCTION();
+
+ private:
+  base::Lock& lock_;
+  bool is_lock_holder_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(NonReentrantScope);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_BASE_REENTRANCY_CHECKER_H_
diff --git a/media/base/reentrancy_checker_unittest.cc b/media/base/reentrancy_checker_unittest.cc
new file mode 100644
index 0000000..7eff2e7
--- /dev/null
+++ b/media/base/reentrancy_checker_unittest.cc
@@ -0,0 +1,38 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/base/reentrancy_checker.h"
+
+#include "base/logging.h"
+#include "base/test/gtest_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+class ReentrancyCheckerTest : public testing::Test {
+ public:
+  void UseNonReentrantScope() { NON_REENTRANT_SCOPE(reentrancy_checker_); }
+
+ protected:
+  REENTRANCY_CHECKER(reentrancy_checker_);
+};
+
+TEST_F(ReentrancyCheckerTest, Construct) {}
+
+TEST_F(ReentrancyCheckerTest, NonReentrantUse) {
+  NON_REENTRANT_SCOPE(reentrancy_checker_);
+}
+
+TEST_F(ReentrancyCheckerTest, ReentrantUse) {
+  NON_REENTRANT_SCOPE(reentrancy_checker_);
+
+#if DCHECK_IS_ON()
+  EXPECT_DEATH(UseNonReentrantScope(), "reentered");
+#else
+  // Does nothing if DCHECKs are off.
+  UseNonReentrantScope();
+#endif  // DCHECK_IS_ON()
+}
+
+}  // namespace media
diff --git a/media/gpu/accelerated_video_decoder.h b/media/gpu/accelerated_video_decoder.h
index ee9dc93..fb7acd82 100644
--- a/media/gpu/accelerated_video_decoder.h
+++ b/media/gpu/accelerated_video_decoder.h
@@ -66,11 +66,13 @@
   // we need a new set of them, or when an error occurs.
   virtual DecodeResult Decode() WARN_UNUSED_RESULT = 0;
 
-  // Return dimensions/required number of output surfaces that client should
-  // be ready to provide for the decoder to function properly.
-  // To be used after Decode() returns kAllocateNewSurfaces.
+  // Return dimensions/required number of pictures that client should be ready
+  // to provide for the decoder to function properly (of which up to
+  // GetNumReferenceFrames() might be needed for internal decoding). To be used
+  // after Decode() returns kAllocateNewSurfaces.
   virtual gfx::Size GetPicSize() const = 0;
   virtual size_t GetRequiredNumOfPictures() const = 0;
+  virtual size_t GetNumReferenceFrames() const = 0;
 
   // About 3 secs for 30 fps video. When the new sized keyframe is missed, the
   // decoder cannot decode the frame. The number of frames are skipped until
diff --git a/media/gpu/h264_decoder.cc b/media/gpu/h264_decoder.cc
index 43ee2867..dc2c76c 100644
--- a/media/gpu/h264_decoder.cc
+++ b/media/gpu/h264_decoder.cc
@@ -1444,7 +1444,17 @@
 }
 
 size_t H264Decoder::GetRequiredNumOfPictures() const {
-  return dpb_.max_num_pics() + kPicsInPipeline;
+  constexpr size_t kPicsInPipeline = limits::kMaxVideoFrames + 1;
+  return GetNumReferenceFrames() + kPicsInPipeline;
+}
+
+size_t H264Decoder::GetNumReferenceFrames() const {
+  // Use the maximum number of pictures in the Decoded Picture Buffer plus one
+  // for the one being currently egressed.
+  // Another +1 is experimentally needed for high-to-high resolution changes.
+  // TODO(mcasas): Figure out why +2 instead of +1, see crbug.com/909926 and
+  // http://crrev.com/c/1363807/9/media/gpu/h264_decoder.cc#1449.
+  return dpb_.max_num_pics() + 2;
 }
 
 // static
diff --git a/media/gpu/h264_decoder.h b/media/gpu/h264_decoder.h
index a7b9e4b..e77ede18 100644
--- a/media/gpu/h264_decoder.h
+++ b/media/gpu/h264_decoder.h
@@ -168,6 +168,7 @@
   DecodeResult Decode() override WARN_UNUSED_RESULT;
   gfx::Size GetPicSize() const override;
   size_t GetRequiredNumOfPictures() const override;
+  size_t GetNumReferenceFrames() const override;
 
   // Return true if we need to start a new picture.
   static bool IsNewPrimaryCodedPicture(const H264Picture* curr_pic,
@@ -182,17 +183,6 @@
                                              H264Picture* pic);
 
  private:
-  // We need to keep at most kDPBMaxSize pictures in DPB for
-  // reference/to display later and an additional one for the one currently
-  // being decoded. We also ask for some additional ones since VDA needs
-  // to accumulate a few ready-to-output pictures before it actually starts
-  // displaying and giving them back. +2 instead of +1 because of subjective
-  // smoothness improvement during testing.
-  enum {
-    kPicsInPipeline = limits::kMaxVideoFrames + 2,
-    kMaxNumReqPictures = H264DPB::kDPBMaxSize + kPicsInPipeline,
-  };
-
   // Internal state of the decoder.
   enum State {
     // After initialization, need an SPS.
diff --git a/media/gpu/vaapi/vaapi_video_decode_accelerator.cc b/media/gpu/vaapi/vaapi_video_decode_accelerator.cc
index bf12715..5853d190 100644
--- a/media/gpu/vaapi/vaapi_video_decode_accelerator.cc
+++ b/media/gpu/vaapi/vaapi_video_decode_accelerator.cc
@@ -16,6 +16,7 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/numerics/ranges.h"
 #include "base/stl_util.h"
 #include "base/strings/string_util.h"
 #include "base/synchronization/waitable_event.h"
@@ -174,11 +175,14 @@
       vaapi_picture_factory_(new VaapiPictureFactory()),
       surfaces_available_(&lock_),
       decode_using_client_picture_buffers_(false),
+      use_reduced_number_of_allocations_(false),
       task_runner_(base::ThreadTaskRunnerHandle::Get()),
       decoder_thread_("VaapiDecoderThread"),
       finish_flush_pending_(false),
       awaiting_va_surfaces_recycle_(false),
       requested_num_pics_(0),
+      requested_num_reference_frames_(0),
+      previously_requested_num_reference_frames_(0),
       profile_(VIDEO_CODEC_PROFILE_UNKNOWN),
       make_context_current_cb_(make_context_current_cb),
       bind_image_cb_(bind_image_cb),
@@ -250,6 +254,10 @@
   output_mode_ = config.output_mode;
   decode_using_client_picture_buffers_ =
       ShouldDecodeOnclientPictureBuffers(output_mode_, profile_);
+  use_reduced_number_of_allocations_ =
+      !decode_using_client_picture_buffers_ &&
+      output_mode_ == Config::OutputMode::ALLOCATE;
+  previously_requested_num_reference_frames_ = 0;
   return true;
 }
 
@@ -478,7 +486,8 @@
             FROM_HERE,
             base::Bind(&VaapiVideoDecodeAccelerator::InitiateSurfaceSetChange,
                        weak_this_, decoder_->GetRequiredNumOfPictures(),
-                       decoder_->GetPicSize()));
+                       decoder_->GetPicSize(),
+                       decoder_->GetNumReferenceFrames()));
         // We'll get rescheduled once ProvidePictureBuffers() finishes.
         return;
 
@@ -515,23 +524,37 @@
   }
 }
 
-void VaapiVideoDecodeAccelerator::InitiateSurfaceSetChange(size_t num_pics,
-                                                           gfx::Size size) {
+void VaapiVideoDecodeAccelerator::InitiateSurfaceSetChange(
+    size_t num_pics,
+    gfx::Size size,
+    size_t num_reference_frames) {
   DCHECK(task_runner_->BelongsToCurrentThread());
   DCHECK(!awaiting_va_surfaces_recycle_);
+  DCHECK_GT(num_pics, num_reference_frames);
 
   // At this point decoder has stopped running and has already posted onto our
   // loop any remaining output request callbacks, which executed before we got
-  // here. Some of them might have been pended though, because we might not
-  // have had enough TFPictures to output surfaces to. Initiate a wait cycle,
+  // here. Some of them might have been pended though, because we might not have
+  // had enough PictureBuffers to output surfaces to. Initiate a wait cycle,
   // which will wait for client to return enough PictureBuffers to us, so that
   // we can finish all pending output callbacks, releasing associated surfaces.
-  VLOGF(2) << "Initiating surface set change";
   awaiting_va_surfaces_recycle_ = true;
 
-  requested_num_pics_ = num_pics;
   requested_pic_size_ = size;
 
+  // If we can |use_reduced_number_of_allocations_|, split the requested
+  // |num_pics| between VA reference frames and client PictureBuffers proper.
+  if (use_reduced_number_of_allocations_)
+    requested_num_reference_frames_ = num_reference_frames;
+  else
+    requested_num_reference_frames_ = 0;
+
+  requested_num_pics_ = num_pics - requested_num_reference_frames_;
+
+  VLOGF(2) << " |requested_num_pics_| = " << requested_num_pics_
+           << "; |requested_num_reference_frames_| = "
+           << requested_num_reference_frames_;
+
   TryFinishSurfaceSetChange();
 }
 
@@ -542,14 +565,19 @@
     return;
 
   base::AutoLock auto_lock(lock_);
+  const size_t expected_max_available_va_surfaces =
+      use_reduced_number_of_allocations_
+          ? previously_requested_num_reference_frames_
+          : pictures_.size();
+
   if (!pending_output_cbs_.empty() ||
-      pictures_.size() != available_va_surfaces_.size()) {
-    // Either:
-    // 1. Not all pending pending output callbacks have been executed yet.
-    // Wait for the client to return enough pictures and retry later.
-    // 2. The above happened and all surface release callbacks have been posted
-    // as the result, but not all have executed yet. Post ourselves after them
-    // to let them release surfaces.
+      expected_max_available_va_surfaces != available_va_surfaces_.size()) {
+    // If we're here the stream resolution has changed; we need to wait until:
+    // - all |pending_output_cbs_| have been executed
+    // - all VASurfaces are back to |available_va_surfaces_|; we can't use
+    //   |requested_num_reference_frames_| for comparison, since it might have
+    //   changed in the previous call to InitiateSurfaceSetChange(), so we use
+    //   |previously_requested_num_reference_frames_| instead.
     DVLOGF(2) << "Awaiting pending output/surface release callbacks to finish";
     task_runner_->PostTask(
         FROM_HERE,
@@ -558,6 +586,8 @@
     return;
   }
 
+  previously_requested_num_reference_frames_ = requested_num_reference_frames_;
+
   // All surfaces released, destroy them and dismiss all PictureBuffers.
   awaiting_va_surfaces_recycle_ = false;
   available_va_surfaces_.clear();
@@ -581,6 +611,7 @@
       base::BindOnce(&Client::ProvidePictureBuffers, client_,
                      requested_num_pics_, format, 1, requested_pic_size_,
                      vaapi_picture_factory_->GetGLTextureTarget()));
+  // |client_| may respond via AssignPictureBuffers().
 }
 
 void VaapiVideoDecodeAccelerator::Decode(
@@ -675,14 +706,19 @@
     RETURN_AND_NOTIFY_ON_FAILURE(
         vaapi_wrapper_->CreateContext(va_format, requested_pic_size_),
         "Failed creating VA Context", PLATFORM_FAILURE, );
+    DCHECK_EQ(va_surface_ids.size(), buffers.size());
   } else {
+    const size_t requested_num_surfaces = use_reduced_number_of_allocations_
+                                              ? requested_num_reference_frames_
+                                              : pictures_.size();
+    CHECK_NE(requested_num_surfaces, 0u);
     va_surface_ids.clear();
-    RETURN_AND_NOTIFY_ON_FAILURE(
-        vaapi_wrapper_->CreateContextAndSurfaces(
-            va_format, requested_pic_size_, buffers.size(), &va_surface_ids),
-        "Failed creating VA Surfaces", PLATFORM_FAILURE, );
+    RETURN_AND_NOTIFY_ON_FAILURE(vaapi_wrapper_->CreateContextAndSurfaces(
+                                     va_format, requested_pic_size_,
+                                     requested_num_surfaces, &va_surface_ids),
+                                 "Failed creating VA Surfaces",
+                                 PLATFORM_FAILURE, );
   }
-  DCHECK_EQ(va_surface_ids.size(), buffers.size());
 
   available_va_surfaces_.assign(va_surface_ids.begin(), va_surface_ids.end());
 
@@ -995,9 +1031,12 @@
     const VASurfaceID id = available_va_surfaces_.front();
     available_va_surfaces_.pop_front();
 
-    TRACE_COUNTER_ID2("media,gpu", "Vaapi VASurfaceIDs", this, "used",
-                      pictures_.size() - available_va_surfaces_.size(),
-                      "available", available_va_surfaces_.size());
+    TRACE_COUNTER_ID2(
+        "media,gpu", "Vaapi VASurfaceIDs", this, "used",
+        (use_reduced_number_of_allocations_ ? requested_num_reference_frames_
+                                            : pictures_.size()) -
+            available_va_surfaces_.size(),
+        "available", available_va_surfaces_.size());
 
     return new VASurface(id, requested_pic_size_,
                          vaapi_wrapper_->va_surface_format(),
@@ -1027,13 +1066,17 @@
 void VaapiVideoDecodeAccelerator::RecycleVASurfaceID(
     VASurfaceID va_surface_id) {
   DCHECK(task_runner_->BelongsToCurrentThread());
+
   {
     base::AutoLock auto_lock(lock_);
     available_va_surfaces_.push_back(va_surface_id);
     if (!decode_using_client_picture_buffers_) {
-      TRACE_COUNTER_ID2("media,gpu", "Vaapi VASurfaceIDs", this, "used",
-                        pictures_.size() - available_va_surfaces_.size(),
-                        "available", available_va_surfaces_.size());
+      TRACE_COUNTER_ID2(
+          "media,gpu", "Vaapi VASurfaceIDs", this, "used",
+          (use_reduced_number_of_allocations_ ? requested_num_reference_frames_
+                                              : pictures_.size()) -
+              available_va_surfaces_.size(),
+          "available", available_va_surfaces_.size());
     }
     surfaces_available_.Signal();
   }
@@ -1045,7 +1088,7 @@
     base::trace_event::ProcessMemoryDump* pmd) {
   using base::trace_event::MemoryAllocatorDump;
   base::AutoLock auto_lock(lock_);
-  if (decode_using_client_picture_buffers_ || pictures_.empty())
+  if (decode_using_client_picture_buffers_ || !requested_num_reference_frames_)
     return false;
 
   auto dump_name = base::StringPrintf("gpu/vaapi/decoder/0x%" PRIxPTR,
@@ -1060,16 +1103,20 @@
   const float va_surface_bytes_per_pixel =
       va_surface_format == VA_RT_FORMAT_YUV420 ? kNumBytesPerPixelYUV420
                                                : kNumBytesPerPixelYUV420_10bpp;
-  // Report |pictures_.size()| and the associated memory size.
-  // The calculated size is an estimation since we don't know the internal VA
+  // Report |requested_num_surfaces| and the associated memory size. The
+  // calculated size is an estimation since we don't know the internal VA
   // strides, texture compression, headers, etc, but is a good lower boundary.
-  dump->AddScalar(
-      MemoryAllocatorDump::kNameSize, MemoryAllocatorDump::kUnitsBytes,
-      static_cast<uint64_t>(pictures_.size() * requested_pic_size_.GetArea() *
-                            va_surface_bytes_per_pixel));
+  const size_t requested_num_surfaces = use_reduced_number_of_allocations_
+                                            ? requested_num_reference_frames_
+                                            : pictures_.size();
+  dump->AddScalar(MemoryAllocatorDump::kNameSize,
+                  MemoryAllocatorDump::kUnitsBytes,
+                  static_cast<uint64_t>(requested_num_surfaces *
+                                        requested_pic_size_.GetArea() *
+                                        va_surface_bytes_per_pixel));
   dump->AddScalar(MemoryAllocatorDump::kNameObjectCount,
                   MemoryAllocatorDump::kUnitsObjects,
-                  static_cast<uint64_t>(pictures_.size()));
+                  static_cast<uint64_t>(requested_num_surfaces));
 
   return true;
 }
diff --git a/media/gpu/vaapi/vaapi_video_decode_accelerator.h b/media/gpu/vaapi/vaapi_video_decode_accelerator.h
index abc8c52..10c9da0 100644
--- a/media/gpu/vaapi/vaapi_video_decode_accelerator.h
+++ b/media/gpu/vaapi/vaapi_video_decode_accelerator.h
@@ -178,9 +178,12 @@
   // |available_va_surfaces_|
   void RecycleVASurfaceID(VASurfaceID va_surface_id);
 
-  // Initiate wait cycle for surfaces to be released before we release them
-  // and allocate new ones, as requested by the decoder.
-  void InitiateSurfaceSetChange(size_t num_pics, gfx::Size size);
+  // Request a new set of |num_pics| PictureBuffers to be allocated by
+  // |client_|. Up to |num_reference_frames| out of |num_pics_| might be needed
+  // by |decoder_|.
+  void InitiateSurfaceSetChange(size_t num_pics,
+                                gfx::Size size,
+                                size_t num_reference_frames);
 
   // Check if the surfaces have been released or post ourselves for later.
   void TryFinishSurfaceSetChange();
@@ -252,9 +255,13 @@
   // Only used on |task_runner_|.
   base::queue<base::OnceClosure> pending_output_cbs_;
 
+  // TODO(crbug.com/912295): Enable these two for IMPORT |output_mode_| as well.
   // Under some circumstances, we can pass to libva our own VASurfaceIDs to
-  // decode onto, which skips one copy. Only used on |task_runner_|.
+  // decode onto, which skips one copy. see https://crbug.com/822346.
   bool decode_using_client_picture_buffers_;
+  // When |decode_using_client_picture_buffers_| is false and under certain
+  // conditions, we can reduce the number of necessary allocated buffers.
+  bool use_reduced_number_of_allocations_;
 
   // WeakPtr<> pointing to |this| for use in posting tasks from the decoder
   // thread back to the ChildThread.  Because the decoder thread is a member of
@@ -289,10 +296,14 @@
   // to be returned before we can free them. Only used on |task_runner_|.
   bool awaiting_va_surfaces_recycle_;
 
-  // Last requested number/resolution of output picture buffers and their
-  // format.
+  // Last requested number/resolution of output PictureBuffers.
   size_t requested_num_pics_;
   gfx::Size requested_pic_size_;
+  // Max number of reference frames needed by |decoder_|. Only used on
+  // |task_runner_| and when |use_reduced_number_of_allocations_| is true.
+  size_t requested_num_reference_frames_;
+  size_t previously_requested_num_reference_frames_;
+
   VideoCodecProfile profile_;
 
   // Callback to make GL context current.
diff --git a/media/gpu/vaapi/vaapi_video_decode_accelerator_unittest.cc b/media/gpu/vaapi/vaapi_video_decode_accelerator_unittest.cc
index a329436..cdd47320 100644
--- a/media/gpu/vaapi/vaapi_video_decode_accelerator_unittest.cc
+++ b/media/gpu/vaapi/vaapi_video_decode_accelerator_unittest.cc
@@ -40,7 +40,7 @@
 constexpr int32_t kBitstreamId = 123;
 constexpr size_t kInputSize = 256;
 
-constexpr size_t kNumPictures = 2;
+constexpr size_t kNumPictures = 4;
 const gfx::Size kPictureSize(64, 48);
 
 constexpr size_t kNewNumPictures = 3;
@@ -61,6 +61,7 @@
   MOCK_METHOD0(Decode, DecodeResult());
   MOCK_CONST_METHOD0(GetPicSize, gfx::Size());
   MOCK_CONST_METHOD0(GetRequiredNumOfPictures, size_t());
+  MOCK_CONST_METHOD0(GetNumReferenceFrames, size_t());
 };
 
 class MockVaapiWrapper : public VaapiWrapper {
@@ -153,7 +154,8 @@
     decoder_thread_.Start();
 
     // Don't want to go through a vda_->Initialize() because it binds too many
-    // items of the environment. Instead, just start the decoder thread.
+    // items of the environment. Instead, do all the necessary steps here.
+
     vda_.decoder_thread_task_runner_ = decoder_thread_.task_runner();
 
     // Plug in all the mocks and ourselves as the |client_|.
@@ -163,10 +165,15 @@
     vda_.vpp_vaapi_wrapper_ = mock_vpp_vaapi_wrapper_;
     vda_.vaapi_picture_factory_.reset(mock_vaapi_picture_factory_);
 
+    // TODO(crbug.com/917999): add IMPORT mode to test variations.
     vda_.output_mode_ = VideoDecodeAccelerator::Config::OutputMode::ALLOCATE;
 
     vda_.decode_using_client_picture_buffers_ =
         GetParam().decode_using_client_picture_buffers;
+    vda_.use_reduced_number_of_allocations_ =
+        !vda_.decode_using_client_picture_buffers_ &&
+        vda_.output_mode_ ==
+            VideoDecodeAccelerator::Config::OutputMode::ALLOCATE;
 
     vda_.state_ = VaapiVideoDecodeAccelerator::kIdle;
   }
@@ -221,6 +228,9 @@
     EXPECT_CALL(*mock_decoder_, GetRequiredNumOfPictures())
         .WillOnce(Return(num_pictures));
     EXPECT_CALL(*mock_decoder_, GetPicSize()).WillOnce(Return(picture_size));
+    const size_t kNumReferenceFrames = num_pictures / 2;
+    EXPECT_CALL(*mock_decoder_, GetNumReferenceFrames())
+        .WillOnce(Return(kNumReferenceFrames));
     EXPECT_CALL(*mock_vaapi_wrapper_, DestroyContextAndSurfaces());
 
     if (expect_dismiss_picture_buffers) {
@@ -228,8 +238,14 @@
           .Times(num_picture_buffers_to_dismiss);
     }
 
+    const size_t expected_num_picture_buffers_requested =
+        vda_.use_reduced_number_of_allocations_
+            ? num_pictures - kNumReferenceFrames
+            : num_pictures;
+
     EXPECT_CALL(*this,
-                ProvidePictureBuffers(num_pictures, _, 1, picture_size, _))
+                ProvidePictureBuffers(expected_num_picture_buffers_requested, _,
+                                      1, picture_size, _))
         .WillOnce(RunClosure(quit_closure));
 
     base::SharedMemoryHandle handle;
@@ -263,17 +279,18 @@
           MockCreateVaapiPicture(mock_vaapi_wrapper_.get(), picture_size))
           .Times(num_pictures);
     } else {
-      EXPECT_CALL(*mock_vaapi_wrapper_,
-                  CreateContextAndSurfaces(_, picture_size, num_pictures, _))
-          .WillOnce(DoAll(
-              WithArg<3>(Invoke(
-                  [num_pictures](std::vector<VASurfaceID>* va_surface_ids) {
-                    va_surface_ids->resize(num_pictures);
-                  })),
-              Return(true)));
+      const size_t kNumReferenceFrames = num_pictures / 2;
       EXPECT_CALL(
-          *mock_vaapi_picture_factory_,
-          MockCreateVaapiPicture(mock_vpp_vaapi_wrapper_.get(), picture_size))
+          *mock_vaapi_wrapper_,
+          CreateContextAndSurfaces(_, picture_size, kNumReferenceFrames, _))
+          .WillOnce(DoAll(
+              WithArg<3>(Invoke([kNumReferenceFrames](
+                                    std::vector<VASurfaceID>* va_surface_ids) {
+                va_surface_ids->resize(kNumReferenceFrames);
+              })),
+              Return(true)));
+      EXPECT_CALL(*mock_vaapi_picture_factory_,
+                  MockCreateVaapiPicture(_, picture_size))
           .Times(num_pictures);
     }
 
diff --git a/media/gpu/vp8_decoder.cc b/media/gpu/vp8_decoder.cc
index 33a181d..c86f71825 100644
--- a/media/gpu/vp8_decoder.cc
+++ b/media/gpu/vp8_decoder.cc
@@ -7,6 +7,10 @@
 
 namespace media {
 
+namespace {
+constexpr size_t kVP8NumFramesActive = 4;
+};
+
 VP8Decoder::VP8Accelerator::VP8Accelerator() {}
 
 VP8Decoder::VP8Accelerator::~VP8Accelerator() {}
@@ -165,9 +169,14 @@
 }
 
 size_t VP8Decoder::GetRequiredNumOfPictures() const {
-  const size_t kVP8NumFramesActive = 4;
-  const size_t kPicsInPipeline = limits::kMaxVideoFrames + 2;
+  constexpr size_t kPicsInPipeline = limits::kMaxVideoFrames + 1;
   return kVP8NumFramesActive + kPicsInPipeline;
 }
 
+size_t VP8Decoder::GetNumReferenceFrames() const {
+  // Maximum number of reference frames needed plus one for the one being
+  // currently egressed.
+  return kVP8NumFramesActive + 1;
+}
+
 }  // namespace media
diff --git a/media/gpu/vp8_decoder.h b/media/gpu/vp8_decoder.h
index f08a8126..8ed398b89 100644
--- a/media/gpu/vp8_decoder.h
+++ b/media/gpu/vp8_decoder.h
@@ -72,6 +72,7 @@
   DecodeResult Decode() override WARN_UNUSED_RESULT;
   gfx::Size GetPicSize() const override;
   size_t GetRequiredNumOfPictures() const override;
+  size_t GetNumReferenceFrames() const override;
 
  private:
   bool DecodeAndOutputCurrentFrame(scoped_refptr<VP8Picture> pic);
diff --git a/media/gpu/vp9_decoder.cc b/media/gpu/vp9_decoder.cc
index a16c6a9..a8d2f2a 100644
--- a/media/gpu/vp9_decoder.cc
+++ b/media/gpu/vp9_decoder.cc
@@ -261,9 +261,14 @@
 }
 
 size_t VP9Decoder::GetRequiredNumOfPictures() const {
-  // kMaxVideoFrames to keep higher level media pipeline populated, +2 for the
-  // pictures being parsed and decoded currently.
-  return limits::kMaxVideoFrames + kVp9NumRefFrames + 2;
+  constexpr size_t kPicsInPipeline = limits::kMaxVideoFrames + 1;
+  return kPicsInPipeline + GetNumReferenceFrames();
+}
+
+size_t VP9Decoder::GetNumReferenceFrames() const {
+  // Maximum number of reference frames needed plus one for the one being
+  // currently egressed.
+  return kVp9NumRefFrames + 1;
 }
 
 }  // namespace media
diff --git a/media/gpu/vp9_decoder.h b/media/gpu/vp9_decoder.h
index 3bf20061..b4422f0 100644
--- a/media/gpu/vp9_decoder.h
+++ b/media/gpu/vp9_decoder.h
@@ -106,6 +106,7 @@
   DecodeResult Decode() override WARN_UNUSED_RESULT;
   gfx::Size GetPicSize() const override;
   size_t GetRequiredNumOfPictures() const override;
+  size_t GetNumReferenceFrames() const override;
 
  private:
   // Update ref_frames_ based on the information in current frame header.
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 3a9176b..f1936dca 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -1675,6 +1675,7 @@
       "third_party/spdy/core/write_scheduler.h",
       "third_party/spdy/core/zero_copy_output_buffer.h",
       "third_party/spdy/platform/api/spdy_arraysize.h",
+      "third_party/spdy/platform/api/spdy_containers.h",
       "third_party/spdy/platform/api/spdy_estimate_memory_usage.h",
       "third_party/spdy/platform/api/spdy_export.h",
       "third_party/spdy/platform/api/spdy_flags.h",
@@ -1684,6 +1685,7 @@
       "third_party/spdy/platform/api/spdy_string_piece.h",
       "third_party/spdy/platform/api/spdy_string_utils.h",
       "third_party/spdy/platform/impl/spdy_arraysize_impl.h",
+      "third_party/spdy/platform/impl/spdy_containers_impl.h",
       "third_party/spdy/platform/impl/spdy_estimate_memory_usage_impl.h",
       "third_party/spdy/platform/impl/spdy_export_impl.h",
       "third_party/spdy/platform/impl/spdy_flags_impl.cc",
diff --git a/net/spdy/spdy_network_transaction_unittest.cc b/net/spdy/spdy_network_transaction_unittest.cc
index 86aa4e7..e9047be 100644
--- a/net/spdy/spdy_network_transaction_unittest.cc
+++ b/net/spdy/spdy_network_transaction_unittest.cc
@@ -2592,7 +2592,8 @@
 
   EXPECT_EQ(1, delegate.received_redirect_count());
 
-  request->FollowDeferredRedirect(base::nullopt /* modified_request_headers */);
+  request->FollowDeferredRedirect(base::nullopt /* removed_headers */,
+                                  base::nullopt /* modified_headers */);
   delegate.RunUntilComplete();
 
   EXPECT_EQ(1, delegate.response_started_count());
@@ -2687,8 +2688,8 @@
   delegate1.RunUntilRedirect();
   EXPECT_EQ(1, delegate1.received_redirect_count());
 
-  request1->FollowDeferredRedirect(
-      base::nullopt /* modified_request_headers */);
+  request1->FollowDeferredRedirect(base::nullopt /* removed_headers */,
+                                   base::nullopt /* modified_headers */);
   delegate1.RunUntilComplete();
   EXPECT_EQ(1, delegate1.response_started_count());
   EXPECT_FALSE(delegate1.received_data_before_response());
diff --git a/net/third_party/spdy/core/hpack/hpack_header_table.h b/net/third_party/spdy/core/hpack/hpack_header_table.h
index 5119b25..e97a81b 100644
--- a/net/third_party/spdy/core/hpack/hpack_header_table.h
+++ b/net/third_party/spdy/core/hpack/hpack_header_table.h
@@ -8,12 +8,11 @@
 #include <cstddef>
 #include <deque>
 #include <memory>
-#include <unordered_map>
-#include <unordered_set>
 #include <utility>
 
 #include "base/macros.h"
 #include "net/third_party/spdy/core/hpack/hpack_entry.h"
+#include "net/third_party/spdy/platform/api/spdy_containers.h"
 #include "net/third_party/spdy/platform/api/spdy_export.h"
 #include "net/third_party/spdy/platform/api/spdy_string_piece.h"
 
@@ -67,11 +66,9 @@
   struct SPDY_EXPORT_PRIVATE EntriesEq {
     bool operator()(const HpackEntry* lhs, const HpackEntry* rhs) const;
   };
-
-  using UnorderedEntrySet =
-      std::unordered_set<HpackEntry*, EntryHasher, EntriesEq>;
-  using NameToEntryMap = std::
-      unordered_map<SpdyStringPiece, const HpackEntry*, base::StringPieceHash>;
+  using UnorderedEntrySet = SpdyHashSet<HpackEntry*, EntryHasher, EntriesEq>;
+  using NameToEntryMap =
+      SpdyHashMap<SpdyStringPiece, const HpackEntry*, base::StringPieceHash>;
 
   HpackHeaderTable();
   HpackHeaderTable(const HpackHeaderTable&) = delete;
diff --git a/net/third_party/spdy/platform/api/spdy_containers.h b/net/third_party/spdy/platform/api/spdy_containers.h
new file mode 100644
index 0000000..bf8075a0
--- /dev/null
+++ b/net/third_party/spdy/platform/api/spdy_containers.h
@@ -0,0 +1,27 @@
+// Copyright (c) 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 NET_THIRD_PARTY_SPDY_PLATFORM_API_SPDY_CONTAINERS_H_
+#define NET_THIRD_PARTY_SPDY_PLATFORM_API_SPDY_CONTAINERS_H_
+
+#include "net/third_party/spdy/platform/impl/spdy_containers_impl.h"
+
+namespace spdy {
+
+template <typename KeyType>
+using SpdyHash = SpdyHashImpl<KeyType>;
+
+// SpdyHashMap does not guarantee pointer stability.
+template <typename KeyType,
+          typename ValueType,
+          typename Hash = SpdyHash<KeyType>>
+using SpdyHashMap = SpdyHashMapImpl<KeyType, ValueType, Hash>;
+
+// SpdyHashSet does not guarantee pointer stability.
+template <typename ElementType, typename Hasher, typename Eq>
+using SpdyHashSet = SpdyHashSetImpl<ElementType, Hasher, Eq>;
+
+}  // namespace spdy
+
+#endif  // NET_THIRD_PARTY_SPDY_PLATFORM_API_SPDY_CONTAINERS_H_
diff --git a/net/third_party/spdy/platform/impl/spdy_containers_impl.h b/net/third_party/spdy/platform/impl/spdy_containers_impl.h
new file mode 100644
index 0000000..4c9325e
--- /dev/null
+++ b/net/third_party/spdy/platform/impl/spdy_containers_impl.h
@@ -0,0 +1,23 @@
+// Copyright (c) 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 NET_THIRD_PARTY_SPDY_PLATFORM_IMPL_SPDY_CONTAINERS_IMPL_H_
+#define NET_THIRD_PARTY_SPDY_PLATFORM_IMPL_SPDY_CONTAINERS_IMPL_H_
+
+#include <unordered_map>
+#include <unordered_set>
+
+namespace spdy {
+
+template <typename KeyType>
+using SpdyHashImpl = std::hash<KeyType>;
+
+template <typename KeyType, typename ValueType, typename Hash>
+using SpdyHashMapImpl = std::unordered_map<KeyType, ValueType, Hash>;
+
+template <typename ElementType, typename Hasher, typename Eq>
+using SpdyHashSetImpl = std::unordered_set<ElementType, Hasher, Eq>;
+
+}  // namespace spdy
+
+#endif  // NET_THIRD_PARTY_SPDY_PLATFORM_IMPL_SPDY_CONTAINERS_IMPL_H_
diff --git a/net/url_request/redirect_util.cc b/net/url_request/redirect_util.cc
index 540e25e..630a76d 100644
--- a/net/url_request/redirect_util.cc
+++ b/net/url_request/redirect_util.cc
@@ -16,7 +16,8 @@
     const GURL& original_url,
     const std::string& original_method,
     const RedirectInfo& redirect_info,
-    const base::Optional<net::HttpRequestHeaders>& modified_request_headers,
+    const base::Optional<std::vector<std::string>>& removed_headers,
+    const base::Optional<net::HttpRequestHeaders>& modified_headers,
     HttpRequestHeaders* request_headers,
     bool* should_clear_upload) {
   DCHECK(request_headers);
@@ -24,6 +25,11 @@
 
   *should_clear_upload = false;
 
+  if (removed_headers) {
+    for (const std::string& key : removed_headers.value())
+      request_headers->RemoveHeader(key);
+  }
+
   if (redirect_info.new_method != original_method) {
     // TODO(davidben): This logic still needs to be replicated at the consumers.
     //
@@ -66,8 +72,8 @@
                                url::Origin().Serialize());
   }
 
-  if (modified_request_headers)
-    request_headers->MergeFrom(modified_request_headers.value());
+  if (modified_headers)
+    request_headers->MergeFrom(modified_headers.value());
 }
 
 }  // namespace net
diff --git a/net/url_request/redirect_util.h b/net/url_request/redirect_util.h
index 9d1b3b9..9cc9153 100644
--- a/net/url_request/redirect_util.h
+++ b/net/url_request/redirect_util.h
@@ -20,15 +20,16 @@
 class RedirectUtil {
  public:
   // Updates HTTP headers in |request_headers| for a redirect.
-  // |modified_request_headers| are specified by clients to add or override
-  // existing headers for the redirect.
+  // |removed_headers| and |modified_headers| are specified by
+  // clients to add or override existing headers for the redirect.
   // |should_clear_upload| is set to true when the request body should be
   // cleared during the redirect.
   NET_EXPORT static void UpdateHttpRequest(
       const GURL& original_url,
       const std::string& original_method,
       const RedirectInfo& redirect_info,
-      const base::Optional<net::HttpRequestHeaders>& modified_request_headers,
+      const base::Optional<std::vector<std::string>>& removed_headers,
+      const base::Optional<net::HttpRequestHeaders>& modified_headers,
       HttpRequestHeaders* request_headers,
       bool* should_clear_upload);
 };
diff --git a/net/url_request/redirect_util_unittest.cc b/net/url_request/redirect_util_unittest.cc
index e83a55d..fb48d68 100644
--- a/net/url_request/redirect_util_unittest.cc
+++ b/net/url_request/redirect_util_unittest.cc
@@ -23,7 +23,7 @@
   struct TestCase {
     const char* new_method;
     const char* new_url;
-    const char* modified_request_headers;
+    const char* modified_headers;
     bool expected_should_clear_upload;
     // nullptr if the origin header should not exist
     const char* expected_origin_header;
@@ -32,28 +32,28 @@
       {
           "POST" /* new_method */,
           "https://www.example.com/redirected.php" /* new_url */,
-          "Header1: Value1\r\nHeader2: Value2" /* modified_request_headers */,
+          "Header1: Value1\r\nHeader2: Value2" /* modified_headers */,
           false /* expected_should_clear_upload */,
           "https://origin.example.com" /* expected_origin_header */
       },
       {
           "GET" /* new_method */,
           "https://www.example.com/redirected.php" /* new_url */,
-          "Header1: Value1\r\nHeader2: Value2" /* modified_request_headers */,
+          "Header1: Value1\r\nHeader2: Value2" /* modified_headers */,
           true /* expected_should_clear_upload */,
           nullptr /* expected_origin_header */
       },
       {
           "POST" /* new_method */,
           "https://other.example.com/redirected.php" /* new_url */,
-          "Header1: Value1\r\nHeader2: Value2" /* modified_request_headers */,
+          "Header1: Value1\r\nHeader2: Value2" /* modified_headers */,
           false /* expected_should_clear_upload */,
           "null" /* expected_origin_header */
       },
       {
           "GET" /* new_method */,
           "https://other.example.com/redirected.php" /* new_url */,
-          "Header1: Value1\r\nHeader2: Value2" /* modified_request_headers */,
+          "Header1: Value1\r\nHeader2: Value2" /* modified_headers */,
           true /* expected_should_clear_upload */,
           nullptr /* expected_origin_header */
       }};
@@ -65,12 +65,11 @@
     redirect_info.new_method = test.new_method;
     redirect_info.new_url = GURL(test.new_url);
 
-    net::HttpRequestHeaders modified_request_headers;
-    modified_request_headers.AddHeadersFromString(
-        test.modified_request_headers);
+    net::HttpRequestHeaders modified_headers;
+    modified_headers.AddHeadersFromString(test.modified_headers);
     std::string expected_modified_header1, expected_modified_header2;
-    modified_request_headers.GetHeader("Header1", &expected_modified_header1);
-    modified_request_headers.GetHeader("Header2", &expected_modified_header2);
+    modified_headers.GetHeader("Header1", &expected_modified_header1);
+    modified_headers.GetHeader("Header2", &expected_modified_header2);
 
     HttpRequestHeaders request_headers;
     request_headers.SetHeader(HttpRequestHeaders::kOrigin,
@@ -83,9 +82,10 @@
 
     bool should_clear_upload = !test.expected_should_clear_upload;
 
-    RedirectUtil::UpdateHttpRequest(original_url, kOriginalMethod,
-                                    redirect_info, modified_request_headers,
-                                    &request_headers, &should_clear_upload);
+    RedirectUtil::UpdateHttpRequest(
+        original_url, kOriginalMethod, redirect_info,
+        base::nullopt /* removed_headers */, modified_headers, &request_headers,
+        &should_clear_upload);
     EXPECT_EQ(test.expected_should_clear_upload, should_clear_upload);
     EXPECT_EQ(!test.expected_should_clear_upload,
               request_headers.HasHeader(HttpRequestHeaders::kContentLength));
@@ -109,5 +109,129 @@
   }
 }
 
+TEST(RedirectUtilTest, RemovedHeaders) {
+  struct TestCase {
+    std::vector<const char*> initial_headers;
+    std::vector<const char*> modified_headers;
+    std::vector<const char*> removed_headers;
+    std::vector<const char*> final_headers;
+  };
+  const TestCase kTests[] = {
+      // Remove no headers (empty vector).
+      {
+          {},  // Initial headers
+          {},  // Modified headers
+          {},  // Removed headers
+          {},  // Final headers
+      },
+      // Remove an existing header.
+      {
+          {"A:0"},  // Initial headers
+          {},       // Modified headers
+          {"A"},    // Removed headers
+          {},       // Final headers
+      },
+      // Remove a missing header.
+      {
+          {},     // Initial headers
+          {},     // Modified headers
+          {"A"},  // Removed headers
+          {},     // Final headers
+      },
+      // Remove two different headers.
+      {
+          {"A:0", "B:0"},  // Initial headers
+          {},              // Modified headers
+          {"A", "B"},      // Removed headers
+          {},              // Final headers
+      },
+      // Remove two times the same headers.
+      {
+          {"A:0"},     // Initial headers
+          {},          // Modified headers
+          {"A", "A"},  // Removed headers
+          {},          // Final headers
+      },
+      // Remove an existing header that is also modified.
+      {
+          {"A:0"},  // Initial headers
+          {"A:1"},  // Modified headers
+          {"A"},    // Removed headers
+          {"A:1"},  // Final headers
+      },
+      // Some headers are removed, some aren't.
+      {
+          {"A:0", "B:0"},  // Initial headers
+          {},              // Modified headers
+          {"A"},           // Removed headers
+          {"B:0"},         // Final headers
+      },
+  };
+
+  for (const auto& test : kTests) {
+    HttpRequestHeaders initial_headers, modified_headers, final_headers;
+    std::vector<std::string> removed_headers;
+    for (const char* header : test.initial_headers)
+      initial_headers.AddHeadersFromString(header);
+    for (const char* header : test.modified_headers)
+      modified_headers.AddHeadersFromString(header);
+    for (const char* header : test.removed_headers)
+      removed_headers.push_back(header);
+    for (const char* header : test.final_headers)
+      final_headers.AddHeadersFromString(header);
+    bool should_clear_upload(false);  // unused.
+
+    RedirectUtil::UpdateHttpRequest(GURL(),         // original_url
+                                    std::string(),  // original_method
+                                    RedirectInfo(), removed_headers,
+                                    modified_headers, &initial_headers,
+                                    &should_clear_upload);
+
+    // The initial_headers have been updated and should match the expected final
+    // headers.
+    EXPECT_EQ(initial_headers.ToString(), final_headers.ToString());
+  }
+}
+
+// Test with removed_headers = base::nullopt.
+TEST(RedirectUtilTest, RemovedHeadersNullOpt) {
+  HttpRequestHeaders initial_headers, final_headers;
+  initial_headers.AddHeadersFromString("A:0");
+  final_headers.AddHeadersFromString("A:0");
+  base::Optional<std::vector<std::string>> removed_headers(base::nullopt);
+  base::Optional<HttpRequestHeaders> modified_headers(base::in_place);
+  bool should_clear_upload(false);  // unused.
+
+  RedirectUtil::UpdateHttpRequest(GURL(),         // original_url
+                                  std::string(),  // original_method
+                                  RedirectInfo(), removed_headers,
+                                  modified_headers, &initial_headers,
+                                  &should_clear_upload);
+
+  // The initial_headers have been updated and should match the expected final
+  // headers.
+  EXPECT_EQ(initial_headers.ToString(), final_headers.ToString());
+}
+
+// Test with modified_headers = base::nullopt.
+TEST(RedirectUtilTest, ModifyHeadersNullopt) {
+  HttpRequestHeaders initial_headers, final_headers;
+  initial_headers.AddHeadersFromString("A:0");
+  final_headers.AddHeadersFromString("A:0");
+  base::Optional<std::vector<std::string>> removed_headers(base::in_place);
+  base::Optional<HttpRequestHeaders> modified_headers(base::nullopt);
+  bool should_clear_upload(false);  // unused.
+
+  RedirectUtil::UpdateHttpRequest(GURL(),         // original_url
+                                  std::string(),  // original_method
+                                  RedirectInfo(), removed_headers,
+                                  modified_headers, &initial_headers,
+                                  &should_clear_upload);
+
+  // The initial_headers have been updated and should match the expected final
+  // headers.
+  EXPECT_EQ(initial_headers.ToString(), final_headers.ToString());
+}
+
 }  // namespace
 }  // namespace net
diff --git a/net/url_request/url_request.cc b/net/url_request/url_request.cc
index 89ae7bd..6a8af3b 100644
--- a/net/url_request/url_request.cc
+++ b/net/url_request/url_request.cc
@@ -874,12 +874,13 @@
 }
 
 void URLRequest::FollowDeferredRedirect(
-    const base::Optional<net::HttpRequestHeaders>& modified_request_headers) {
+    const base::Optional<std::vector<std::string>>& removed_headers,
+    const base::Optional<net::HttpRequestHeaders>& modified_headers) {
   DCHECK(job_.get());
   DCHECK(status_.is_success());
 
   status_ = URLRequestStatus::FromError(ERR_IO_PENDING);
-  job_->FollowDeferredRedirect(modified_request_headers);
+  job_->FollowDeferredRedirect(removed_headers, modified_headers);
 }
 
 void URLRequest::SetAuth(const AuthCredentials& credentials) {
@@ -944,7 +945,8 @@
 
 void URLRequest::Redirect(
     const RedirectInfo& redirect_info,
-    const base::Optional<net::HttpRequestHeaders>& modified_request_headers) {
+    const base::Optional<std::vector<std::string>>& removed_headers,
+    const base::Optional<net::HttpRequestHeaders>& modified_headers) {
   // This method always succeeds. Whether |job_| is allowed to redirect to
   // |redirect_info| is checked in URLRequestJob::CanFollowRedirect, before
   // NotifyReceivedRedirect. This means the delegate can assume that, if it
@@ -967,7 +969,7 @@
 
   bool clear_body = false;
   net::RedirectUtil::UpdateHttpRequest(url(), method_, redirect_info,
-                                       modified_request_headers,
+                                       removed_headers, modified_headers,
                                        &extra_request_headers_, &clear_body);
   if (clear_body)
     upload_data_stream_.reset();
diff --git a/net/url_request/url_request.h b/net/url_request/url_request.h
index 804efd5a..9301dc4 100644
--- a/net/url_request/url_request.h
+++ b/net/url_request/url_request.h
@@ -637,10 +637,11 @@
 
   // This method may be called to follow a redirect that was deferred in
   // response to an OnReceivedRedirect call. If non-null,
-  // |modified_request_headers| are changes applied to the request headers after
+  // |modified_headers| are changes applied to the request headers after
   // updating them for the redirect.
   void FollowDeferredRedirect(
-      const base::Optional<net::HttpRequestHeaders>& modified_request_headers);
+      const base::Optional<std::vector<std::string>>& removed_headers,
+      const base::Optional<net::HttpRequestHeaders>& modified_headers);
 
   // One of the following two methods should be called in response to an
   // OnAuthRequired() callback (and only then).
@@ -760,11 +761,12 @@
   void set_status(URLRequestStatus status);
 
   // Allow the URLRequestJob to redirect this request. If non-null,
-  // |modified_request_headers| are changes applied to the request headers after
-  // updating them for the redirect.
+  // |removed_headers| and |modified_headers| are changes
+  // applied to the request headers after updating them for the redirect.
   void Redirect(
       const RedirectInfo& redirect_info,
-      const base::Optional<net::HttpRequestHeaders>& modified_request_headers);
+      const base::Optional<std::vector<std::string>>& removed_headers,
+      const base::Optional<net::HttpRequestHeaders>& modified_headers);
 
   // Called by URLRequestJob to allow interception when a redirect occurs.
   void NotifyReceivedRedirect(const RedirectInfo& redirect_info,
diff --git a/net/url_request/url_request_job.cc b/net/url_request/url_request_job.cc
index 2c55a0eb..efcd4d5a 100644
--- a/net/url_request/url_request_job.cc
+++ b/net/url_request/url_request_job.cc
@@ -256,7 +256,8 @@
 }
 
 void URLRequestJob::FollowDeferredRedirect(
-    const base::Optional<net::HttpRequestHeaders>& modified_request_headers) {
+    const base::Optional<std::vector<std::string>>& removed_headers,
+    const base::Optional<net::HttpRequestHeaders>& modified_headers) {
   // OnReceivedRedirect must have been called.
   DCHECK(deferred_redirect_info_);
 
@@ -264,7 +265,7 @@
   // pass along a reference to |deferred_redirect_info_|.
   base::Optional<RedirectInfo> redirect_info =
       std::move(deferred_redirect_info_);
-  FollowRedirect(*redirect_info, modified_request_headers);
+  FollowRedirect(*redirect_info, removed_headers, modified_headers);
 }
 
 int64_t URLRequestJob::prefilter_bytes_read() const {
@@ -439,8 +440,8 @@
     if (defer_redirect) {
       deferred_redirect_info_ = std::move(redirect_info);
     } else {
-      FollowRedirect(redirect_info,
-                     base::nullopt /* modified_request_headers */);
+      FollowRedirect(redirect_info, base::nullopt, /*  removed_headers */
+                     base::nullopt /* modified_headers */);
     }
     return;
   }
@@ -699,8 +700,9 @@
 
 void URLRequestJob::FollowRedirect(
     const RedirectInfo& redirect_info,
-    const base::Optional<net::HttpRequestHeaders>& modified_request_headers) {
-  request_->Redirect(redirect_info, modified_request_headers);
+    const base::Optional<std::vector<std::string>>& removed_headers,
+    const base::Optional<net::HttpRequestHeaders>& modified_headers) {
+  request_->Redirect(redirect_info, removed_headers, modified_headers);
 }
 
 void URLRequestJob::GatherRawReadStats(int bytes_read) {
diff --git a/net/url_request/url_request_job.h b/net/url_request/url_request_job.h
index c75471c5..f08e6e8 100644
--- a/net/url_request/url_request_job.h
+++ b/net/url_request/url_request_job.h
@@ -198,7 +198,8 @@
   virtual void ContinueDespiteLastError();
 
   void FollowDeferredRedirect(
-      const base::Optional<net::HttpRequestHeaders>& modified_request_headers);
+      const base::Optional<std::vector<std::string>>& removed_headers,
+      const base::Optional<net::HttpRequestHeaders>& modified_headers);
 
   // Returns true if the Job is done producing response data and has called
   // NotifyDone on the request.
@@ -372,7 +373,8 @@
   // given redirect destination.
   void FollowRedirect(
       const RedirectInfo& redirect_info,
-      const base::Optional<net::HttpRequestHeaders>& modified_request_headers);
+      const base::Optional<std::vector<std::string>>& removed_headers,
+      const base::Optional<net::HttpRequestHeaders>& modified_headers);
 
   // Called after every raw read. If |bytes_read| is > 0, this indicates
   // a successful read of |bytes_read| unfiltered bytes. If |bytes_read|
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index a3c511d6..37615dc 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -4396,7 +4396,8 @@
     EXPECT_EQ(redirect_url, GURL(location));
 
     // Let the request finish.
-    r->FollowDeferredRedirect(base::nullopt /* modified_request_headers */);
+    r->FollowDeferredRedirect(base::nullopt /* removed_headers */,
+                              base::nullopt /* modified_headers */);
     d.RunUntilComplete();
     EXPECT_EQ(OK, d.request_status());
     EXPECT_EQ(ProxyServer(ProxyServer::SCHEME_HTTP,
@@ -4450,7 +4451,8 @@
     EXPECT_EQ(redirect_url, GURL(location));
 
     // Let the request finish.
-    r->FollowDeferredRedirect(base::nullopt /* modified_request_headers */);
+    r->FollowDeferredRedirect(base::nullopt /* removed_headers */,
+                              base::nullopt /* modified_headers */);
     d.RunUntilComplete();
 
     EXPECT_EQ(OK, d.request_status());
@@ -4513,7 +4515,8 @@
     EXPECT_EQ(redirect_url, GURL(location));
 
     // Let the request finish.
-    r->FollowDeferredRedirect(base::nullopt /* modified_request_headers */);
+    r->FollowDeferredRedirect(base::nullopt /* removed_headers */,
+                              base::nullopt /* modified_headers */);
     d.RunUntilComplete();
 
     EXPECT_EQ(OK, d.request_status());
@@ -5632,9 +5635,10 @@
     // FollowDeferredRedirect should not be called after cancellation.
     if (cancel_stage_ == CANCEL_ON_RECEIVED_REDIRECT)
       return;
-    if (!defer_redirect)
-      request->FollowDeferredRedirect(
-          base::nullopt /* modified_request_headers */);
+    if (!defer_redirect) {
+      request->FollowDeferredRedirect(base::nullopt /* removed_headers */,
+                                      base::nullopt /* modified_headers */);
+    }
   }
 
   void OnResponseStartedLoggingComplete(URLRequest* request, int net_error) {
@@ -7169,7 +7173,8 @@
     EXPECT_EQ(0, d.response_started_count());
     EXPECT_TRUE(req->was_cached());
 
-    req->FollowDeferredRedirect(base::nullopt /* modified_request_headers */);
+    req->FollowDeferredRedirect(base::nullopt /* removed_headers */,
+                                base::nullopt /* modified_headers */);
     d.RunUntilComplete();
     EXPECT_EQ(1, d.received_redirect_count());
     EXPECT_EQ(1, d.response_started_count());
@@ -7471,7 +7476,8 @@
     CheckFullRequestHeaders(d.full_request_headers(), test_url);
     d.ClearFullRequestHeaders();
 
-    req->FollowDeferredRedirect(base::nullopt /* modified_request_headers */);
+    req->FollowDeferredRedirect(base::nullopt /* removed_headers */,
+                                base::nullopt /* modified_headers */);
     d.RunUntilComplete();
 
     EXPECT_EQ(1, d.response_started_count());
@@ -7505,7 +7511,8 @@
 
     EXPECT_EQ(1, d.received_redirect_count());
 
-    req->FollowDeferredRedirect(base::nullopt /* modified_request_headers */);
+    req->FollowDeferredRedirect(base::nullopt /* removed_headers */,
+                                base::nullopt /* modified_headers */);
     d.RunUntilComplete();
 
     GURL target_url(http_test_server()->GetURL("/with-headers.html"));
@@ -7526,7 +7533,7 @@
   }
 }
 
-TEST_F(URLRequestTestHTTP, DeferredRedirect_ModifiedRequestHeaders) {
+TEST_F(URLRequestTestHTTP, DeferredRedirect_ModifiedHeaders) {
   ASSERT_TRUE(http_test_server()->Start());
 
   TestDelegate d;
@@ -7555,11 +7562,12 @@
     d.ClearFullRequestHeaders();
 
     // Overwrite Header2 and add Header3.
-    net::HttpRequestHeaders modified_request_headers;
-    modified_request_headers.SetHeader("Header2", "");
-    modified_request_headers.SetHeader("Header3", "Value3");
+    net::HttpRequestHeaders modified_headers;
+    modified_headers.SetHeader("Header2", "");
+    modified_headers.SetHeader("Header3", "Value3");
 
-    req->FollowDeferredRedirect(modified_request_headers);
+    req->FollowDeferredRedirect(base::nullopt /* removed_headers */,
+                                modified_headers);
     d.RunUntilComplete();
 
     EXPECT_EQ(1, d.response_started_count());
@@ -7578,6 +7586,52 @@
   }
 }
 
+TEST_F(URLRequestTestHTTP, DeferredRedirect_RemovedHeaders) {
+  ASSERT_TRUE(http_test_server()->Start());
+
+  TestDelegate d;
+  {
+    GURL test_url(http_test_server()->GetURL("/redirect-test.html"));
+    std::unique_ptr<URLRequest> req(default_context().CreateRequest(
+        test_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
+
+    // Set initial headers for the request.
+    req->SetExtraRequestHeaderByName("Header1", "Value1", true /* overwrite */);
+    req->SetExtraRequestHeaderByName("Header2", "Value2", true /* overwrite */);
+
+    req->Start();
+    d.RunUntilRedirect();
+
+    // Initial request should have initial headers.
+    EXPECT_EQ(1, d.received_redirect_count());
+    EXPECT_TRUE(d.have_full_request_headers());
+    const HttpRequestHeaders& sent_headers1 = d.full_request_headers();
+    std::string sent_value;
+    EXPECT_TRUE(sent_headers1.GetHeader("Header1", &sent_value));
+    EXPECT_EQ("Value1", sent_value);
+    EXPECT_TRUE(sent_headers1.GetHeader("Header2", &sent_value));
+    EXPECT_EQ("Value2", sent_value);
+    d.ClearFullRequestHeaders();
+
+    // Keep Header1 and remove Header2.
+    std::vector<std::string> removed_headers({"Header2"});
+    req->FollowDeferredRedirect(removed_headers,
+                                base::nullopt /* modified_headers */);
+    d.RunUntilComplete();
+
+    EXPECT_EQ(1, d.response_started_count());
+    EXPECT_FALSE(d.received_data_before_response());
+    EXPECT_EQ(OK, d.request_status());
+
+    // Redirected request should also have
+    EXPECT_TRUE(d.have_full_request_headers());
+    const HttpRequestHeaders& sent_headers2 = d.full_request_headers();
+    EXPECT_TRUE(sent_headers2.GetHeader("Header1", &sent_value));
+    EXPECT_EQ("Value1", sent_value);
+    EXPECT_FALSE(sent_headers2.GetHeader("Header2", &sent_value));
+  }
+}
+
 TEST_F(URLRequestTestHTTP, CancelDeferredRedirect) {
   ASSERT_TRUE(http_test_server()->Start());
 
@@ -11720,7 +11774,8 @@
 
   raw_req_headers = HttpRawRequestHeaders();
   raw_resp_headers = nullptr;
-  r->FollowDeferredRedirect(base::nullopt /* modified_request_headers */);
+  r->FollowDeferredRedirect(base::nullopt /* removed_headers */,
+                            base::nullopt /* modified_headers */);
   delegate.RunUntilComplete();
   EXPECT_TRUE(raw_req_headers.FindHeaderForTest("X-Foo", &value));
   EXPECT_EQ("bar", value);
diff --git a/ppapi/tests/test_flash_fullscreen_for_browser_ui.cc b/ppapi/tests/test_flash_fullscreen_for_browser_ui.cc
index 07c1c33..4bb1004f 100644
--- a/ppapi/tests/test_flash_fullscreen_for_browser_ui.cc
+++ b/ppapi/tests/test_flash_fullscreen_for_browser_ui.cc
@@ -4,6 +4,9 @@
 
 #include "ppapi/tests/test_flash_fullscreen_for_browser_ui.h"
 
+#include <GLES2/gl2.h>
+
+#include "ppapi/c/ppb_opengles2.h"
 #include "ppapi/cpp/input_event.h"
 #include "ppapi/cpp/instance.h"
 #include "ppapi/cpp/rect.h"
@@ -16,8 +19,6 @@
     : TestCase(instance),
       screen_mode_(instance),
       view_change_event_(instance->pp_instance()),
-      num_trigger_events_(0),
-      request_fullscreen_(false),
       callback_factory_(this) {
   // This plugin should not be removed after this TestCase passes because
   // browser UI testing requires it to remain and to be interactive.
@@ -28,7 +29,9 @@
 }
 
 bool TestFlashFullscreenForBrowserUI::Init() {
-  return CheckTestingInterface();
+  opengl_es2_ = static_cast<const PPB_OpenGLES2*>(
+      pp::Module::Get()->GetBrowserInterface(PPB_OPENGLES2_INTERFACE));
+  return opengl_es2_ && CheckTestingInterface();
 }
 
 void TestFlashFullscreenForBrowserUI::RunTests(const std::string& filter) {
@@ -43,6 +46,18 @@
   if (screen_mode_.SetFullscreen(true))
     return ReportError("SetFullscreen(true) outside of user gesture", true);
 
+  int32_t attribs[] = {PP_GRAPHICS3DATTRIB_RED_SIZE,     8,
+                       PP_GRAPHICS3DATTRIB_GREEN_SIZE,   8,
+                       PP_GRAPHICS3DATTRIB_BLUE_SIZE,    8,
+                       PP_GRAPHICS3DATTRIB_ALPHA_SIZE,   8,
+                       PP_GRAPHICS3DATTRIB_DEPTH_SIZE,   0,
+                       PP_GRAPHICS3DATTRIB_STENCIL_SIZE, 0,
+                       PP_GRAPHICS3DATTRIB_WIDTH,        layer_size_.width(),
+                       PP_GRAPHICS3DATTRIB_HEIGHT,       layer_size_.height(),
+                       PP_GRAPHICS3DATTRIB_NONE};
+  graphics_3d_ = pp::Graphics3D(instance_, attribs);
+  instance_->BindGraphics(graphics_3d_);
+
   // Trigger another call to SetFullscreen(true) from HandleInputEvent().
   // The transition is asynchronous and ends at the next DidChangeView().
   view_change_event_.Reset();
@@ -57,10 +72,6 @@
   if (!screen_mode_.IsFullscreen())
     return ReportError("IsFullscreen() in fullscreen", false);
 
-  compositor_ = pp::Compositor(instance_);
-  instance_->BindGraphics(compositor_);
-  color_layer_ = compositor_.AddLayer();
-
   const int32_t result =
       instance_->RequestFilteringInputEvents(PP_INPUTEVENT_CLASS_MOUSE |
                                              PP_INPUTEVENT_CLASS_KEYBOARD);
@@ -75,8 +86,10 @@
   if (normal_position_.IsEmpty())
     normal_position_ = view.GetRect();
 
-  if (!compositor_.is_null())
-    Paint(PP_OK);
+  if (!graphics_3d_.is_null()) {
+    graphics_3d_.ResizeBuffers(layer_size_.width(), layer_size_.height());
+    RequestPaint();
+  }
 
   view_change_event_.Signal();
 }
@@ -142,18 +155,36 @@
   }
 
   ++num_trigger_events_;
+  RequestPaint();
 
   return true;
 }
 
-void TestFlashFullscreenForBrowserUI::Paint(int32_t last_compositor_result) {
-  if (num_trigger_events_ == 0)
-    color_layer_.SetColor(0.0f, 1.0f, 0.0f, 1.0f, layer_size_);
-  else if (num_trigger_events_ % 2)
-    color_layer_.SetColor(1.0f, 0.0f, 0.0f, 1.0f, layer_size_);
+void TestFlashFullscreenForBrowserUI::RequestPaint() {
+  if (swap_pending_)
+    needs_paint_ = true;
   else
-    color_layer_.SetColor(0.0f, 0.0f, 1.0f, 1.0f, layer_size_);
+    DoPaint();
+}
 
-  compositor_.CommitLayers(
-      callback_factory_.NewCallback(&TestFlashFullscreenForBrowserUI::Paint));
+void TestFlashFullscreenForBrowserUI::DoPaint() {
+  if (num_trigger_events_ == 0)
+    opengl_es2_->ClearColor(graphics_3d_.pp_resource(), 0.0f, 1.0f, 0.0f, 1.0f);
+  else if (num_trigger_events_ % 2)
+    opengl_es2_->ClearColor(graphics_3d_.pp_resource(), 1.0f, 0.0f, 0.0f, 1.0f);
+  else
+    opengl_es2_->ClearColor(graphics_3d_.pp_resource(), 0.0f, 0.0f, 1.0f, 1.0f);
+
+  opengl_es2_->Clear(graphics_3d_.pp_resource(), GL_COLOR_BUFFER_BIT);
+  swap_pending_ = true;
+  graphics_3d_.SwapBuffers(
+      callback_factory_.NewCallback(&TestFlashFullscreenForBrowserUI::DidSwap));
+}
+
+void TestFlashFullscreenForBrowserUI::DidSwap(int32_t last_compositor_result) {
+  swap_pending_ = false;
+  if (needs_paint_) {
+    needs_paint_ = false;
+    DoPaint();
+  }
 }
diff --git a/ppapi/tests/test_flash_fullscreen_for_browser_ui.h b/ppapi/tests/test_flash_fullscreen_for_browser_ui.h
index 9bb80a4..64ee4cb 100644
--- a/ppapi/tests/test_flash_fullscreen_for_browser_ui.h
+++ b/ppapi/tests/test_flash_fullscreen_for_browser_ui.h
@@ -9,14 +9,15 @@
 
 #include <string>
 
-#include "ppapi/cpp/compositor.h"
-#include "ppapi/cpp/compositor_layer.h"
+#include "ppapi/cpp/graphics_3d.h"
 #include "ppapi/cpp/private/flash_fullscreen.h"
 #include "ppapi/cpp/size.h"
 #include "ppapi/tests/test_case.h"
 #include "ppapi/tests/test_utils.h"
 #include "ppapi/utility/completion_callback_factory.h"
 
+struct PPB_OpenGLES2;
+
 // This is a special TestCase whose purpose is *not* to test the correctness of
 // the Pepper APIs.  Instead, this is a simulated Flash plugin, used to place
 // the browser window and other UI elements into Flash Fullscreen mode for
@@ -41,24 +42,30 @@
 
  private:
   std::string TestEnterFullscreen();
-  void Paint(int32_t last_compositor_result);
+  void RequestPaint();
+  void DoPaint();
+  void DidSwap(int32_t last_compositor_result);
   void SimulateUserGesture();
   bool GotError();
   std::string Error();
   void FailFullscreenTest(const std::string& error);
 
+  // OpenGL ES2 interface.
+  const PPB_OpenGLES2* opengl_es2_ = nullptr;
+
   std::string error_;
 
   pp::FlashFullscreen screen_mode_;
   NestedEvent view_change_event_;
 
-  pp::Compositor compositor_;
+  pp::Graphics3D graphics_3d_;
   pp::Size layer_size_;
-  pp::CompositorLayer color_layer_;
   pp::Rect normal_position_;
 
-  int num_trigger_events_;
-  bool request_fullscreen_;
+  int num_trigger_events_ = 0;
+  bool request_fullscreen_ = false;
+  bool swap_pending_ = false;
+  bool needs_paint_ = false;
 
   pp::CompletionCallbackFactory<TestFlashFullscreenForBrowserUI>
       callback_factory_;
diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc
index 3024367..db931610 100644
--- a/services/network/url_loader.cc
+++ b/services/network/url_loader.cc
@@ -557,9 +557,8 @@
 const void* const URLLoader::kUserDataKey = &URLLoader::kUserDataKey;
 
 void URLLoader::FollowRedirect(
-    const base::Optional<std::vector<std::string>>&
-        to_be_removed_request_headers,
-    const base::Optional<net::HttpRequestHeaders>& modified_request_headers,
+    const base::Optional<std::vector<std::string>>& removed_headers,
+    const base::Optional<net::HttpRequestHeaders>& modified_headers,
     const base::Optional<GURL>& new_url) {
   if (!url_request_) {
     NotifyCompleted(net::ERR_UNEXPECTED);
@@ -583,12 +582,7 @@
   deferred_redirect_url_.reset();
   new_redirect_url_ = new_url;
 
-  if (to_be_removed_request_headers.has_value()) {
-    for (const std::string& key : to_be_removed_request_headers.value())
-      url_request_->RemoveRequestHeaderByName(key);
-  }
-
-  url_request_->FollowDeferredRedirect(modified_request_headers);
+  url_request_->FollowDeferredRedirect(removed_headers, modified_headers);
   new_redirect_url_.reset();
 }
 
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index b9eaeed..0c16b3e 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -385,7 +385,7 @@
               "pool": "Chrome-CrOS-VM"
             }
           ],
-          "hard_timeout": 1200,
+          "hard_timeout": 2400,
           "idempotent": false,
           "shards": 12
         }
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 89cf426..966c4a75 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -495,7 +495,7 @@
           '--remote-ssh-port=9222',
         ],
         'swarming': {
-          'hard_timeout': 1200,
+          'hard_timeout': 2400,
           'idempotent': False,  # https://crbug.com/549140
           'shards': 12,
         },
diff --git a/third_party/.gitignore b/third_party/.gitignore
index edd94e33..3f4cc34f 100644
--- a/third_party/.gitignore
+++ b/third_party/.gitignore
@@ -90,10 +90,7 @@
 /gson/lib/
 /gvr-android-sdk/common_library.aar
 /gvr-android-sdk/test-libraries/controller_test_api.aar
-/gvr-android-sdk/libgvr_shim_static_arm.a
-/gvr-android-sdk/libgvr_shim_static_arm64.a
-/gvr-android-sdk/libgvr_shim_static_custom_libcxx_arm.a
-/gvr-android-sdk/libgvr_shim_static_custom_libcxx_arm64.a
+/gvr-android-sdk/libgvr_shim_static_*.a
 /gvr-android-sdk/src
 /gvr-android-sdk/test-apks/daydream_home/*.apk
 /gvr-android-sdk/test-apks/vr_services/*.apk
diff --git a/third_party/android_deps/BUILD.gn b/third_party/android_deps/BUILD.gn
index 6fb2c730..7b200a8a 100644
--- a/third_party/android_deps/BUILD.gn
+++ b/third_party/android_deps/BUILD.gn
@@ -574,7 +574,6 @@
 java_prebuilt("com_squareup_javapoet_java") {
   jar_path = "libs/com_squareup_javapoet/javapoet-1.11.0.jar"
   output_name = "com_squareup_javapoet"
-  supports_android = true
 }
 
 java_prebuilt("javax_inject_javax_inject_java") {
diff --git a/third_party/blink/renderer/core/core_idl_files.gni b/third_party/blink/renderer/core/core_idl_files.gni
index eeb7e174..ddf5b8a6 100644
--- a/third_party/blink/renderer/core/core_idl_files.gni
+++ b/third_party/blink/renderer/core/core_idl_files.gni
@@ -173,7 +173,7 @@
                     "events/transition_event.idl",
                     "events/ui_event.idl",
                     "events/wheel_event.idl",
-                    "feature_policy/policy.idl",
+                    "feature_policy/feature_policy.idl",
                     "fetch/body.idl",
                     "fetch/headers.idl",
                     "fetch/request.idl",
diff --git a/third_party/blink/renderer/core/css/properties/longhands/border_image_slice_custom.cc b/third_party/blink/renderer/core/css/properties/longhands/border_image_slice_custom.cc
index 62888d64..00b47d5 100644
--- a/third_party/blink/renderer/core/css/properties/longhands/border_image_slice_custom.cc
+++ b/third_party/blink/renderer/core/css/properties/longhands/border_image_slice_custom.cc
@@ -29,9 +29,16 @@
 }
 
 const CSSValue* BorderImageSlice::InitialValue() const {
-  DEFINE_STATIC_LOCAL(Persistent<CSSValue>, value,
+  DEFINE_STATIC_LOCAL(Persistent<CSSValue>, hundredPercent,
                       (CSSPrimitiveValue::Create(
                           100, CSSPrimitiveValue::UnitType::kPercentage)));
+  DEFINE_STATIC_LOCAL(
+      Persistent<CSSQuadValue>, slices,
+      (CSSQuadValue::Create(hundredPercent, hundredPercent, hundredPercent,
+                            hundredPercent, CSSQuadValue::kSerializeAsQuad)));
+  DEFINE_STATIC_LOCAL(
+      Persistent<CSSBorderImageSliceValue>, value,
+      (CSSBorderImageSliceValue::Create(slices, /* fill */ false)));
   return value;
 }
 
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index d1bd473..2349199 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -7539,7 +7539,7 @@
   return Thread::Current()->GetTaskRunner();
 }
 
-Policy* Document::policy() {
+DOMFeaturePolicy* Document::featurePolicy() {
   if (!policy_)
     policy_ = MakeGarbageCollected<DocumentPolicy>(this);
   return policy_.Get();
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index 4784c1da3..95bdebf 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -109,6 +109,7 @@
 class DocumentState;
 class DocumentTimeline;
 class DocumentType;
+class DOMFeaturePolicy;
 class Element;
 class ElementDataCache;
 class ElementRegistrationOptions;
@@ -151,7 +152,6 @@
 class OriginAccessEntry;
 class Page;
 class PendingAnimations;
-class Policy;
 class ProcessingInstruction;
 class PropertyRegistry;
 class QualifiedName;
@@ -260,7 +260,7 @@
                                                const Position&);
 
   // Support JS introspection of frame policy (e.g. feature policy).
-  Policy* policy();
+  DOMFeaturePolicy* featurePolicy();
 
   MediaQueryMatcher& GetMediaQueryMatcher();
 
@@ -1902,7 +1902,7 @@
 
   bool needs_to_record_ukm_outlive_time_;
 
-  Member<Policy> policy_;
+  Member<DOMFeaturePolicy> policy_;
 
   Member<SlotAssignmentEngine> slot_assignment_engine_;
 
diff --git a/third_party/blink/renderer/core/dom/document.idl b/third_party/blink/renderer/core/dom/document.idl
index d0fb671..9f744a4 100644
--- a/third_party/blink/renderer/core/dom/document.idl
+++ b/third_party/blink/renderer/core/dom/document.idl
@@ -188,11 +188,8 @@
     [MeasureAs=DocumentCaretRangeFromPoint] Range caretRangeFromPoint([Default=Undefined] optional long x, [Default=Undefined] optional long y);
 
 
-    // https://wicg.github.io/feature-policy
-    // TODO(iclelland): add spec for JS exposure in the spec for Feature Policy.
-    // Please refer to this doc for more details for now:
-    // https://docs.google.com/a/chromium.org/document/d/1wvk3cXkblNnbkMcsKayseK-k0SMGiP9b9fQFgfpqQpc/edit?usp=sharing
-    [OriginTrialEnabled=FeaturePolicyJavaScriptInterface] readonly attribute Policy policy;
+    // https://wicg.github.io/feature-policy/#the-policy-object
+    [OriginTrialEnabled=FeaturePolicyJavaScriptInterface] readonly attribute FeaturePolicy featurePolicy;
 
     // Deprecated prefixed page visibility API.
     // TODO(davidben): This is a property so attaching a deprecation warning results in false positives when outputting
diff --git a/third_party/blink/renderer/core/exported/web_frame_serializer.cc b/third_party/blink/renderer/core/exported/web_frame_serializer.cc
index 55cf81a..9e4bae89 100644
--- a/third_party/blink/renderer/core/exported/web_frame_serializer.cc
+++ b/third_party/blink/renderer/core/exported/web_frame_serializer.cc
@@ -403,14 +403,10 @@
 
 bool CacheControlNoStoreHeaderPresent(
     const WebLocalFrameImpl& web_local_frame) {
-  const ResourceResponse& response =
-      web_local_frame.GetDocumentLoader()->GetResponse().ToResourceResponse();
-  if (response.CacheControlContainsNoStore())
-    return true;
-
-  DocumentLoader* document_loader =
-      web_local_frame.GetFrame()->Loader().GetDocumentLoader();
-  return document_loader->CacheControlContainsNoStore();
+  return web_local_frame.GetDocumentLoader()
+      ->GetResponse()
+      .ToResourceResponse()
+      .CacheControlContainsNoStore();
 }
 
 bool FrameShouldBeSerializedAsMHTML(
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc
index f779ae2..d4660b7 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
@@ -139,6 +139,7 @@
 #include "third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h"
 #include "third_party/blink/renderer/core/paint/first_meaningful_paint_detector.h"
 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
+#include "third_party/blink/renderer/core/paint/paint_timing_detector.h"
 #include "third_party/blink/renderer/core/scroll/scrollbar_theme.h"
 #include "third_party/blink/renderer/core/timing/dom_window_performance.h"
 #include "third_party/blink/renderer/core/timing/window_performance.h"
@@ -1739,6 +1740,22 @@
     }
   }
 
+  if (RuntimeEnabledFeatures::FirstContentfulPaintPlusPlusEnabled()) {
+    // Notify the focus frame of the input. Note that the other frames are not
+    // notified as input is only handled by the focused frame.
+    Frame* frame = FocusedCoreFrame();
+    if (frame && frame->IsLocalFrame()) {
+      LocalFrame* local_frame = ToLocalFrame(frame);
+      if (local_frame && local_frame->View() &&
+          local_frame->View()
+              ->GetPaintTimingDetector()
+              .NeedToNotifyInputOrScroll()) {
+        local_frame->View()->GetPaintTimingDetector().NotifyInputEvent(
+            input_event.GetType());
+      }
+    }
+  }
+
   // Skip the pointerrawmove for mouse capture case.
   if (mouse_capture_node_ &&
       input_event.GetType() == WebInputEvent::kPointerRawMove)
diff --git a/third_party/blink/renderer/core/feature_policy/BUILD.gn b/third_party/blink/renderer/core/feature_policy/BUILD.gn
index c9aa37d..70fab1ab 100644
--- a/third_party/blink/renderer/core/feature_policy/BUILD.gn
+++ b/third_party/blink/renderer/core/feature_policy/BUILD.gn
@@ -7,12 +7,12 @@
 blink_core_sources("feature_policy") {
   sources = [
     "document_policy.h",
+    "dom_feature_policy.cc",
+    "dom_feature_policy.h",
     "feature_policy.cc",
     "feature_policy.h",
     "iframe_policy.h",
     "layout_animations_policy.cc",
     "layout_animations_policy.h",
-    "policy.cc",
-    "policy.h",
   ]
 }
diff --git a/third_party/blink/renderer/core/feature_policy/document_policy.h b/third_party/blink/renderer/core/feature_policy/document_policy.h
index 3743ef9..164b1e64 100644
--- a/third_party/blink/renderer/core/feature_policy/document_policy.h
+++ b/third_party/blink/renderer/core/feature_policy/document_policy.h
@@ -7,14 +7,14 @@
 
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/dom/document.h"
-#include "third_party/blink/renderer/core/feature_policy/policy.h"
+#include "third_party/blink/renderer/core/feature_policy/dom_feature_policy.h"
 #include "third_party/blink/renderer/platform/heap/member.h"
 
 namespace blink {
 
 // DocumentPolicy inherits Policy. It represents the feature policy
 // introspection of a document.
-class CORE_EXPORT DocumentPolicy final : public Policy {
+class CORE_EXPORT DocumentPolicy final : public DOMFeaturePolicy {
  public:
   // Create a new DocumentPolicy, which is associated with |document|.
   explicit DocumentPolicy(Document* document) : document_(document) {}
diff --git a/third_party/blink/renderer/core/feature_policy/policy.cc b/third_party/blink/renderer/core/feature_policy/dom_feature_policy.cc
similarity index 79%
rename from third_party/blink/renderer/core/feature_policy/policy.cc
rename to third_party/blink/renderer/core/feature_policy/dom_feature_policy.cc
index 474a477..73e2ba5 100644
--- a/third_party/blink/renderer/core/feature_policy/policy.cc
+++ b/third_party/blink/renderer/core/feature_policy/dom_feature_policy.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "third_party/blink/renderer/core/feature_policy/policy.h"
+#include "third_party/blink/renderer/core/feature_policy/dom_feature_policy.h"
 
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/feature_policy/feature_policy.h"
@@ -12,7 +12,7 @@
 
 namespace blink {
 
-bool Policy::allowsFeature(const String& feature) const {
+bool DOMFeaturePolicy::allowsFeature(const String& feature) const {
   if (GetDefaultFeatureNameMap().Contains(feature)) {
     return GetPolicy()->IsFeatureEnabled(
         GetDefaultFeatureNameMap().at(feature));
@@ -22,7 +22,8 @@
   return false;
 }
 
-bool Policy::allowsFeature(const String& feature, const String& url) const {
+bool DOMFeaturePolicy::allowsFeature(const String& feature,
+                                     const String& url) const {
   scoped_refptr<const SecurityOrigin> origin =
       SecurityOrigin::CreateFromString(url);
   if (!origin || origin->IsOpaque()) {
@@ -41,7 +42,7 @@
       GetDefaultFeatureNameMap().at(feature), origin->ToUrlOrigin());
 }
 
-Vector<String> Policy::allowedFeatures() const {
+Vector<String> DOMFeaturePolicy::allowedFeatures() const {
   Vector<String> allowed_features;
   for (const auto& entry : GetDefaultFeatureNameMap()) {
     if (GetPolicy()->IsFeatureEnabled(entry.value))
@@ -50,7 +51,8 @@
   return allowed_features;
 }
 
-Vector<String> Policy::getAllowlistForFeature(const String& feature) const {
+Vector<String> DOMFeaturePolicy::getAllowlistForFeature(
+    const String& feature) const {
   if (GetDefaultFeatureNameMap().Contains(feature)) {
     const FeaturePolicy::Allowlist allowlist =
         GetPolicy()->GetAllowlistForFeature(
@@ -68,17 +70,18 @@
   return Vector<String>();
 }
 
-void Policy::AddWarningForUnrecognizedFeature(const String& feature) const {
+void DOMFeaturePolicy::AddWarningForUnrecognizedFeature(
+    const String& feature) const {
   GetDocument()->AddConsoleMessage(
       ConsoleMessage::Create(kOtherMessageSource, kWarningMessageLevel,
                              "Unrecognized feature: '" + feature + "'."));
 }
 
-void Policy::Trace(blink::Visitor* visitor) {
+void DOMFeaturePolicy::Trace(blink::Visitor* visitor) {
   ScriptWrappable::Trace(visitor);
 }
 
-void Policy::UpdateContainerPolicy(
+void DOMFeaturePolicy::UpdateContainerPolicy(
     const ParsedFeaturePolicy& container_policy,
     scoped_refptr<const SecurityOrigin> src_origin) {}
 
diff --git a/third_party/blink/renderer/core/feature_policy/policy.h b/third_party/blink/renderer/core/feature_policy/dom_feature_policy.h
similarity index 74%
rename from third_party/blink/renderer/core/feature_policy/policy.h
rename to third_party/blink/renderer/core/feature_policy/dom_feature_policy.h
index 1f066f2..48ffe76 100644
--- a/third_party/blink/renderer/core/feature_policy/policy.h
+++ b/third_party/blink/renderer/core/feature_policy/dom_feature_policy.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 THIRD_PARTY_BLINK_RENDERER_CORE_FEATURE_POLICY_POLICY_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_FEATURE_POLICY_POLICY_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FEATURE_POLICY_DOM_FEATURE_POLICY_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_FEATURE_POLICY_DOM_FEATURE_POLICY_H_
 
 #include "third_party/blink/public/common/feature_policy/feature_policy.h"
 #include "third_party/blink/renderer/core/core_export.h"
@@ -15,13 +15,13 @@
 class Document;
 class SecurityOrigin;
 
-// Policy provides an interface for feature policy introspection of a document
-// (DocumentPolicy) or an iframe (IFramePolicy).
-class CORE_EXPORT Policy : public ScriptWrappable {
+// DOMFeaturePolicy provides an interface for feature policy introspection of a
+// document (DocumentPolicy) or an iframe (IFramePolicy).
+class CORE_EXPORT DOMFeaturePolicy : public ScriptWrappable {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  ~Policy() override = default;
+  ~DOMFeaturePolicy() override = default;
 
   // Implementation of methods of the policy interface:
   // Returns whether or not the given feature is allowed on the origin of the
@@ -36,8 +36,8 @@
   // URL.
   Vector<String> getAllowlistForFeature(const String& url) const;
 
-  // Inform the Policy object when the container policy on its frame element has
-  // changed.
+  // Inform the DOMFeaturePolicy object when the container policy on its frame
+  // element has changed.
   virtual void UpdateContainerPolicy(
       const ParsedFeaturePolicy& container_policy = {},
       scoped_refptr<const SecurityOrigin> src_origin = nullptr);
@@ -56,4 +56,4 @@
 
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_FEATURE_POLICY_POLICY_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_FEATURE_POLICY_DOM_FEATURE_POLICY_H_
diff --git a/third_party/blink/renderer/core/feature_policy/feature_policy.idl b/third_party/blink/renderer/core/feature_policy/feature_policy.idl
new file mode 100644
index 0000000..7cb7595
--- /dev/null
+++ b/third_party/blink/renderer/core/feature_policy/feature_policy.idl
@@ -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.
+
+// https://wicg.github.io/feature-policy/#the-policy-object
+[
+  NoInterfaceObject,
+  OriginTrialEnabled=FeaturePolicyJavaScriptInterface,
+  ImplementedAs=DOMFeaturePolicy
+] interface FeaturePolicy {
+  [MeasureAs=FeaturePolicyJSAPI] boolean allowsFeature(DOMString feature, optional DOMString url);
+  [MeasureAs=FeaturePolicyJSAPI] sequence<DOMString> allowedFeatures();
+  [MeasureAs=FeaturePolicyJSAPI] sequence<DOMString> getAllowlistForFeature(DOMString feature);
+};
diff --git a/third_party/blink/renderer/core/feature_policy/iframe_policy.h b/third_party/blink/renderer/core/feature_policy/iframe_policy.h
index e53ec64..c6adca5 100644
--- a/third_party/blink/renderer/core/feature_policy/iframe_policy.h
+++ b/third_party/blink/renderer/core/feature_policy/iframe_policy.h
@@ -6,16 +6,17 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FEATURE_POLICY_IFRAME_POLICY_H_
 
 #include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/core/feature_policy/policy.h"
+#include "third_party/blink/renderer/core/feature_policy/dom_feature_policy.h"
 #include "third_party/blink/renderer/platform/heap/member.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 
 namespace blink {
 
-// IFramePolicy inherits Policy. It represents the feature policy introspection
-// of an iframe contained in a document. It is tynthetic from the parent policy
-// and the iframe container policy (parsed from the allow attribute).
-class IFramePolicy final : public Policy {
+// IFramePolicy inherits Policy. It represents the feature policy of an iframe
+// contained in a document, as seen from that document (not including any
+// information private to that frame). It is synthesized from the parent
+// document's policy and the iframe's container policy.
+class IFramePolicy final : public DOMFeaturePolicy {
  public:
   ~IFramePolicy() override = default;
 
@@ -39,7 +40,7 @@
 
   void Trace(blink::Visitor* visitor) override {
     visitor->Trace(parent_document_);
-    Policy::Trace(visitor);
+    DOMFeaturePolicy::Trace(visitor);
   }
 
  protected:
diff --git a/third_party/blink/renderer/core/feature_policy/policy.idl b/third_party/blink/renderer/core/feature_policy/policy.idl
deleted file mode 100644
index d2d5ad31..0000000
--- a/third_party/blink/renderer/core/feature_policy/policy.idl
+++ /dev/null
@@ -1,16 +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.
-
-// https://wicg.github.io/feature-policy
-// TODO(iclelland): add spec for JS exposure in the spec for Feature Policy.
-// Please refer to this doc for more details for now:
-// https://docs.google.com/a/chromium.org/document/d/1wvk3cXkblNnbkMcsKayseK-k0SMGiP9b9fQFgfpqQpc/edit?usp=sharing
-[
-  NoInterfaceObject,
-  OriginTrialEnabled=FeaturePolicyJavaScriptInterface
-] interface Policy {
-  [MeasureAs=FeaturePolicyJSAPI] boolean allowsFeature(DOMString feature, optional DOMString url);
-  [MeasureAs=FeaturePolicyJSAPI] sequence<DOMString> allowedFeatures();
-  [MeasureAs=FeaturePolicyJSAPI] sequence<DOMString> getAllowlistForFeature(DOMString feature);
-};
diff --git a/third_party/blink/renderer/core/feature_policy/policy_test.cc b/third_party/blink/renderer/core/feature_policy/policy_test.cc
index 2d45104..3101b3d 100644
--- a/third_party/blink/renderer/core/feature_policy/policy_test.cc
+++ b/third_party/blink/renderer/core/feature_policy/policy_test.cc
@@ -31,11 +31,11 @@
         "https://example.com https://example.net");
   }
 
-  Policy* GetPolicy() const { return policy_; }
+  DOMFeaturePolicy* GetPolicy() const { return policy_; }
 
  protected:
   Persistent<Document> document_;
-  Persistent<Policy> policy_;
+  Persistent<DOMFeaturePolicy> policy_;
 };
 
 class DocumentPolicyTest : public PolicyTest {
diff --git a/third_party/blink/renderer/core/html/html_iframe_element.cc b/third_party/blink/renderer/core/html/html_iframe_element.cc
index 2301338..8756653 100644
--- a/third_party/blink/renderer/core/html/html_iframe_element.cc
+++ b/third_party/blink/renderer/core/html/html_iframe_element.cc
@@ -82,7 +82,7 @@
   return sandbox_.Get();
 }
 
-Policy* HTMLIFrameElement::policy() {
+DOMFeaturePolicy* HTMLIFrameElement::featurePolicy() {
   if (!policy_) {
     policy_ = MakeGarbageCollected<IFramePolicy>(
         &GetDocument(), ContainerPolicy(), GetOriginForFeaturePolicy());
diff --git a/third_party/blink/renderer/core/html/html_iframe_element.h b/third_party/blink/renderer/core/html/html_iframe_element.h
index e9267ef2..29c35ba 100644
--- a/third_party/blink/renderer/core/html/html_iframe_element.h
+++ b/third_party/blink/renderer/core/html/html_iframe_element.h
@@ -32,7 +32,7 @@
 #include "third_party/blink/renderer/platform/supplementable.h"
 
 namespace blink {
-class Policy;
+class DOMFeaturePolicy;
 
 class CORE_EXPORT HTMLIFrameElement final
     : public HTMLFrameElementBase,
@@ -49,7 +49,7 @@
 
   DOMTokenList* sandbox() const;
   // Support JS introspection of frame policy (e.g. feature policy)
-  Policy* policy();
+  DOMFeaturePolicy* featurePolicy();
 
   // Returns attributes that should be checked against Trusted Types
   const HashSet<AtomicString>& GetCheckedAttributeNames() const override;
@@ -93,7 +93,7 @@
   bool allow_payment_request_;
   bool collapsed_by_client_;
   Member<HTMLIFrameElementSandbox> sandbox_;
-  Member<Policy> policy_;
+  Member<DOMFeaturePolicy> policy_;
 
   network::mojom::ReferrerPolicy referrer_policy_;
 };
diff --git a/third_party/blink/renderer/core/html/html_iframe_element.idl b/third_party/blink/renderer/core/html/html_iframe_element.idl
index c45e0f7..167b402d 100644
--- a/third_party/blink/renderer/core/html/html_iframe_element.idl
+++ b/third_party/blink/renderer/core/html/html_iframe_element.idl
@@ -42,12 +42,9 @@
     [CEReactions, Reflect] attribute DOMString csp;
 
     // Feature Policy
-    // https://wicg.github.io/feature-policy/
     [CEReactions, Reflect] attribute DOMString allow;
-    // TODO(iclelland): add spec for JS exposure in the spec for Feature Policy.
-    // Please refer to this doc for more details for now:
-    // https://docs.google.com/a/chromium.org/document/d/1wvk3cXkblNnbkMcsKayseK-k0SMGiP9b9fQFgfpqQpc/edit?usp=sharing
-    [OriginTrialEnabled=FeaturePolicyJavaScriptInterface] readonly attribute Policy policy;
+    // https://wicg.github.io/feature-policy/#the-policy-object
+    [OriginTrialEnabled=FeaturePolicyJavaScriptInterface] readonly attribute FeaturePolicy featurePolicy;
 
     // obsolete members
     // https://html.spec.whatwg.org/#HTMLIFrameElement-partial
diff --git a/third_party/blink/renderer/core/layout/jank_region.cc b/third_party/blink/renderer/core/layout/jank_region.cc
index 8a0b580..dec163a6 100644
--- a/third_party/blink/renderer/core/layout/jank_region.cc
+++ b/third_party/blink/renderer/core/layout/jank_region.cc
@@ -43,7 +43,8 @@
 
  private:
   Vector<int> endpoints_;
-  std::map<int, unsigned> endpoint_to_index_;
+  // Avoid WTF::HashMap as key may be 0 or -1.
+  std::unordered_map<int, unsigned> endpoint_to_index_;
 
 #if DCHECK_IS_ON()
   bool has_index_ = false;
diff --git a/third_party/blink/renderer/core/layout/jank_tracker.cc b/third_party/blink/renderer/core/layout/jank_tracker.cc
index cbd97b74..4a6ccfe 100644
--- a/third_party/blink/renderer/core/layout/jank_tracker.cc
+++ b/third_party/blink/renderer/core/layout/jank_tracker.cc
@@ -42,6 +42,9 @@
 }
 
 static float RegionGranularityScale(const IntRect& viewport) {
+  if (RuntimeEnabledFeatures::JankTrackingSweepLineEnabled())
+    return 1;
+
   return kRegionGranularitySteps /
          std::min(viewport.Height(), viewport.Width());
 }
diff --git a/third_party/blink/renderer/core/layout/jank_tracker_test.cc b/third_party/blink/renderer/core/layout/jank_tracker_test.cc
index abe86ef..312d5165 100644
--- a/third_party/blink/renderer/core/layout/jank_tracker_test.cc
+++ b/third_party/blink/renderer/core/layout/jank_tracker_test.cc
@@ -51,6 +51,9 @@
 }
 
 TEST_F(JankTrackerTest, GranularitySnapping) {
+  if (RuntimeEnabledFeatures::JankTrackingSweepLineEnabled())
+    return;
+
   SetBodyInnerHTML(R"HTML(
     <style>
       #j { position: relative; width: 304px; height: 104px; }
diff --git a/third_party/blink/renderer/core/layout/line/breaking_context_inline_headers.h b/third_party/blink/renderer/core/layout/line/breaking_context_inline_headers.h
index be12bbc..bc06df1 100644
--- a/third_party/blink/renderer/core/layout/line/breaking_context_inline_headers.h
+++ b/third_party/blink/renderer/core/layout/line/breaking_context_inline_headers.h
@@ -87,9 +87,6 @@
         floats_fit_on_line_(true),
         collapse_white_space_(false),
         starting_new_paragraph_(line_info_.PreviousLineBrokeCleanly()),
-        allow_images_to_break_(
-            !block.GetDocument().InQuirksMode() || !block.IsTableCell() ||
-            !block_style_->LogicalWidth().IsIntrinsicOrAuto()),
         at_end_(false),
         line_midpoint_state_(resolver.GetMidpointState()) {
     line_info_.SetPreviousLineBrokeCleanly(false);
@@ -217,7 +214,6 @@
   bool floats_fit_on_line_;
   bool collapse_white_space_;
   bool starting_new_paragraph_;
-  bool allow_images_to_break_;
   bool at_end_;
 
   LineMidpointState& line_midpoint_state_;
@@ -643,7 +639,6 @@
   // Break on replaced elements if either has normal white-space,
   // or if the replaced element is ruby that can break before.
   if ((auto_wrap_ || ComputedStyle::AutoWrap(last_ws_)) &&
-      (!current_.GetLineLayoutItem().IsImage() || allow_images_to_break_) &&
       (!current_.GetLineLayoutItem().IsRubyRun() ||
        LineLayoutRubyRun(current_.GetLineLayoutItem())
            .CanBreakBefore(layout_text_info_.line_break_iterator_))) {
@@ -1602,7 +1597,6 @@
   if (!current_.GetLineLayoutItem().IsFloatingOrOutOfFlowPositioned()) {
     last_object_ = current_.GetLineLayoutItem();
     if (last_object_.IsAtomicInlineLevel() && auto_wrap_ &&
-        (!last_object_.IsImage() || allow_images_to_break_) &&
         (!last_object_.IsListMarker() ||
          LineLayoutListMarker(last_object_).IsInside()) &&
         !last_object_.IsRubyRun()) {
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc
index 52a2d33..5896d88 100644
--- a/third_party/blink/renderer/core/loader/document_loader.cc
+++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -373,10 +373,6 @@
   return request_.HttpBody();
 }
 
-bool DocumentLoader::CacheControlContainsNoStore() const {
-  return request_.CacheControlContainsNoStore();
-}
-
 void DocumentLoader::SetHistoryItemStateForCommit(
     HistoryItem* old_item,
     WebFrameLoadType load_type,
diff --git a/third_party/blink/renderer/core/loader/document_loader.h b/third_party/blink/renderer/core/loader/document_loader.h
index cf4cc48..a7202cd 100644
--- a/third_party/blink/renderer/core/loader/document_loader.h
+++ b/third_party/blink/renderer/core/loader/document_loader.h
@@ -132,7 +132,6 @@
   const KURL& UrlForHistory() const;
   const AtomicString& Referrer() const;
   EncodedFormData* HttpBody() const;
-  bool CacheControlContainsNoStore() const;
 
   void DidChangePerformanceTiming();
   void DidObserveLoadingBehavior(WebLoadingBehaviorFlag);
diff --git a/third_party/blink/renderer/core/paint/background_image_geometry.cc b/third_party/blink/renderer/core/paint/background_image_geometry.cc
index b568822..2117e23 100644
--- a/third_party/blink/renderer/core/paint/background_image_geometry.cc
+++ b/third_party/blink/renderer/core/paint/background_image_geometry.cc
@@ -796,14 +796,17 @@
     }
     case EFillSizeType::kContain:
     case EFillSizeType::kCover: {
+      // Always use the snapped positioning area size for this computation,
+      // so that we resize the image to completely fill the actual painted
+      // area.
       float horizontal_scale_factor =
           image_intrinsic_size.Width()
-              ? positioning_area_size.Width().ToFloat() /
+              ? snapped_positioning_area_size.Width().ToFloat() /
                     image_intrinsic_size.Width()
               : 1.0f;
       float vertical_scale_factor =
           image_intrinsic_size.Height()
-              ? positioning_area_size.Height().ToFloat() /
+              ? snapped_positioning_area_size.Height().ToFloat() /
                     image_intrinsic_size.Height()
               : 1.0f;
       // Force the dimension that determines the size to exactly match the
@@ -815,27 +818,27 @@
         // at the edge of the image when we paint it.
         if (horizontal_scale_factor < vertical_scale_factor) {
           tile_size_ = LayoutSize(
-              positioning_area_size.Width(),
+              snapped_positioning_area_size.Width(),
               LayoutUnit(std::max(1.0f, roundf(image_intrinsic_size.Height() *
                                                horizontal_scale_factor))));
         } else {
           tile_size_ = LayoutSize(
               LayoutUnit(std::max(1.0f, roundf(image_intrinsic_size.Width() *
                                                vertical_scale_factor))),
-              positioning_area_size.Height());
+              snapped_positioning_area_size.Height());
         }
         return;
       }
       if (horizontal_scale_factor > vertical_scale_factor) {
         tile_size_ =
-            LayoutSize(positioning_area_size.Width(),
+            LayoutSize(snapped_positioning_area_size.Width(),
                        LayoutUnit(std::max(1.0f, image_intrinsic_size.Height() *
                                                      horizontal_scale_factor)));
       } else {
         tile_size_ =
             LayoutSize(LayoutUnit(std::max(1.0f, image_intrinsic_size.Width() *
                                                      vertical_scale_factor)),
-                       positioning_area_size.Height());
+                       snapped_positioning_area_size.Height());
       }
       return;
     }
diff --git a/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc b/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc
index a96242f..46084492 100644
--- a/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc
+++ b/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc
@@ -96,7 +96,7 @@
 }  // namespace
 
 // Set a big enough limit for the number of nodes to ensure memory usage is
-// capped. Exceeding such limit will deactivate the algorithm.
+// capped. Exceeding such limit will make the detactor stops recording entries.
 constexpr size_t kImageNodeNumberLimit = 5000;
 
 static bool LargeImageFirst(const base::WeakPtr<ImageRecord>& a,
@@ -205,6 +205,8 @@
 }
 
 void ImagePaintTimingDetector::NotifyNodeRemoved(DOMNodeId node_id) {
+  if (!is_recording_)
+    return;
   if (id_record_map_.Contains(node_id)) {
     // We assume that the removed node's id wouldn't be recycled, so we don't
     // bother to remove these records from size_ordered_set_ or
@@ -352,8 +354,13 @@
     record->first_size = rect_size;
     size_ordered_set_.insert(record->AsWeakPtr());
     id_record_map_.insert(node_id, std::move(record));
-    if (id_record_map_.size() + size_zero_ids_.size() > kImageNodeNumberLimit)
-      Deactivate();
+    if (id_record_map_.size() + size_zero_ids_.size() > kImageNodeNumberLimit) {
+      TRACE_EVENT_INSTANT2("loading", "ImagePaintTimingDetector::OverNodeLimit",
+                           TRACE_EVENT_SCOPE_THREAD, "recorded_node_count",
+                           id_record_map_.size(), "size_zero_node_count",
+                           size_zero_ids_.size());
+      StopRecordEntries();
+    }
   }
 
   if (id_record_map_.Contains(node_id) && !id_record_map_.at(node_id)->loaded &&
@@ -374,11 +381,7 @@
   }
 }
 
-void ImagePaintTimingDetector::Deactivate() {
-  TRACE_EVENT_INSTANT2("loading", "ImagePaintTimingDetector::OverNodeLimit",
-                       TRACE_EVENT_SCOPE_THREAD, "recorded_node_count",
-                       id_record_map_.size(), "size_zero_node_count",
-                       size_zero_ids_.size());
+void ImagePaintTimingDetector::StopRecordEntries() {
   is_recording_ = false;
 }
 
diff --git a/third_party/blink/renderer/core/paint/image_paint_timing_detector.h b/third_party/blink/renderer/core/paint/image_paint_timing_detector.h
index f9a8128c..7a952c7 100644
--- a/third_party/blink/renderer/core/paint/image_paint_timing_detector.h
+++ b/third_party/blink/renderer/core/paint/image_paint_timing_detector.h
@@ -69,6 +69,13 @@
   void NotifyNodeRemoved(DOMNodeId);
   base::TimeTicks LargestImagePaint() const { return largest_image_paint_; }
   base::TimeTicks LastImagePaint() const { return last_image_paint_; }
+  // After the method being called, the detector stops to record new entries and
+  // node removal. But it still observe the loading status. In other words, if
+  // an image is recorded before stopping recording, and finish loading after
+  // stopping recording, the detector can still observe the loading being
+  // finished.
+  void StopRecordEntries();
+  bool IsRecording() const { return is_recording_; }
   void Trace(blink::Visitor*);
 
  private:
@@ -88,8 +95,6 @@
   void RegisterNotifySwapTime();
   void OnLargestImagePaintDetected(const ImageRecord&);
   void OnLastImagePaintDetected(const ImageRecord&);
-  void Deactivate();
-
   void Analyze();
 
   base::RepeatingCallback<void(WebLayerTreeView::ReportTimeCallback)>
@@ -122,6 +127,8 @@
   unsigned frame_index_ = 1;
 
   unsigned last_frame_index_queued_for_timing_ = 0;
+  // Used to control if we record new image entries and image removal, but has
+  // no effect on recording the loading status.
   bool is_recording_ = true;
 
   base::TimeTicks largest_image_paint_;
diff --git a/third_party/blink/renderer/core/paint/image_paint_timing_detector_test.cc b/third_party/blink/renderer/core/paint/image_paint_timing_detector_test.cc
index 4b1aafa..de30bcf 100644
--- a/third_party/blink/renderer/core/paint/image_paint_timing_detector_test.cc
+++ b/third_party/blink/renderer/core/paint/image_paint_timing_detector_test.cc
@@ -12,6 +12,7 @@
 #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
 #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/scroll/scroll_types.h"
 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
@@ -122,6 +123,8 @@
         WebString::FromUTF8(file_name));
   }
 
+  void SimulateScroll() { GetPaintTimingDetector().NotifyScroll(kUserScroll); }
+
  private:
   void FakeNotifySwapTime(WebLayerTreeView::ReportTimeCallback callback) {
     callback_queue_.push(std::move(callback));
@@ -692,4 +695,16 @@
   EXPECT_EQ(CountRecords(), 0u);
 }
 
+TEST_F(ImagePaintTimingDetectorTest, DeactivateAfterUserInput) {
+  SetBodyInnerHTML(R"HTML(
+    <div id="parent">
+      <img id="target"></img>
+    </div>
+  )HTML");
+  SimulateScroll();
+  SetImageAndPaint("target", 5, 5);
+  UpdateAllLifecyclePhasesAndInvokeCallbackIfAny();
+  EXPECT_EQ(CountRecords(), 0u);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/ng/ng_inline_box_fragment_painter.cc b/third_party/blink/renderer/core/paint/ng/ng_inline_box_fragment_painter.cc
index c884762..507082f 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_inline_box_fragment_painter.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_inline_box_fragment_painter.cc
@@ -82,7 +82,12 @@
       inline_box_fragment_.InlineFragmentsFor(
           inline_box_fragment_.GetLayoutObject());
   NGPaintFragment::FragmentRange::iterator iter = fragments.begin();
-  bool object_has_multiple_boxes = ++iter != fragments.end();
+  // TODO(layout-dev): We shouldn't get here if there are no fragments for the
+  // layout object but in some cases that appears to happen. Likely when the
+  // object isn't a LayoutNGInlineFormattingContext.
+  DCHECK(iter != fragments.end());
+  bool object_has_multiple_boxes =
+      iter != fragments.end() && ++iter != fragments.end();
 
   // TODO(eae): Switch to LayoutNG version of BackgroundImageGeometry.
   BackgroundImageGeometry geometry(*static_cast<const LayoutBoxModelObject*>(
diff --git a/third_party/blink/renderer/core/paint/paint_layer.cc b/third_party/blink/renderer/core/paint/paint_layer.cc
index 68e58825..bdce575 100644
--- a/third_party/blink/renderer/core/paint/paint_layer.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer.cc
@@ -2459,20 +2459,15 @@
   return FloatRect();
 }
 
-FloatRect PaintLayer::BackdropFilterReferenceBox() const {
+FloatRect PaintLayer::BackdropFilterBounds() const {
   FloatRect reference_box(GetLayoutObject().BorderBoundingBox());
   float zoom = GetLayoutObject().StyleRef().EffectiveZoom();
   if (zoom != 1)
     reference_box.Scale(1 / zoom);
+  reference_box.Move(-ToFloatSize(FilterReferenceBox().Location()));
   return reference_box;
 }
 
-FloatRect PaintLayer::BackdropFilterBounds() const {
-  FloatRect rect(BackdropFilterReferenceBox());
-  rect.Move(-ToFloatSize(FilterReferenceBox().Location()));
-  return rect;
-}
-
 bool PaintLayer::HitTestClippedOutByClipPath(
     PaintLayer* root_layer,
     const HitTestLocation& hit_test_location) const {
@@ -3249,8 +3244,13 @@
     CompositorFilterOperations& operations,
     gfx::RectF* backdrop_filter_bounds) const {
   DCHECK(backdrop_filter_bounds);
-  *backdrop_filter_bounds = BackdropFilterBounds();
+  const auto& style = GetLayoutObject().StyleRef();
+  if (style.BackdropFilter().IsEmpty()) {
+    operations.Clear();
+    return;
+  }
   FloatRect reference_box = BackdropFilterBounds();
+  *backdrop_filter_bounds = reference_box;
   if (operations.IsEmpty() || reference_box != operations.ReferenceBox())
     operations = CreateCompositorFilterOperationsForBackdropFilter();
 }
@@ -3258,11 +3258,15 @@
 CompositorFilterOperations
 PaintLayer::CreateCompositorFilterOperationsForBackdropFilter() const {
   const auto& style = GetLayoutObject().StyleRef();
+  CompositorFilterOperations return_value;
+  if (style.BackdropFilter().IsEmpty()) {
+    return return_value;
+  }
   float zoom = style.EffectiveZoom();
   FloatRect reference_box = BackdropFilterBounds();
-  CompositorFilterOperations return_value =
-      FilterEffectBuilder(reference_box, zoom)
-          .BuildFilterOperations(style.BackdropFilter());
+  return_value = FilterEffectBuilder(reference_box, zoom)
+                     .BuildFilterOperations(style.BackdropFilter());
+  DCHECK(!return_value.IsEmpty());
   return return_value;
 }
 
diff --git a/third_party/blink/renderer/core/paint/paint_layer.h b/third_party/blink/renderer/core/paint/paint_layer.h
index b2f4f9f6..2b403a8 100644
--- a/third_party/blink/renderer/core/paint/paint_layer.h
+++ b/third_party/blink/renderer/core/paint/paint_layer.h
@@ -638,7 +638,6 @@
   // coordinate system of the object with the filter. Filter bounds is the
   // reference box, offset by the object's location in the graphics layer.
   FloatRect FilterReferenceBox() const;
-  FloatRect BackdropFilterReferenceBox() const;
   FloatRect BackdropFilterBounds() const;
 
   void UpdateFilterReferenceBox();
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
index 342a288..aa7ceb16 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
@@ -317,13 +317,16 @@
     return false;
 
   // Paint containment establishes isolation.
-  if (object.ShouldApplyPaintContainment())
+  // Style & Layout containment also establish isolation.
+  if (object.ShouldApplyPaintContainment() ||
+      (object.ShouldApplyStyleContainment() &&
+       object.ShouldApplyLayoutContainment())) {
     return true;
+  }
 
   // Layout view establishes isolation with the exception of local roots (since
   // they are already essentially isolated).
-  if (RuntimeEnabledFeatures::LayoutViewIsolationNodesEnabled() &&
-      object.IsLayoutView()) {
+  if (object.IsLayoutView()) {
     const auto* parent_frame = object.GetFrame()->Tree().Parent();
     return parent_frame && parent_frame->IsLocalFrame();
   }
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
index cc4412ab..5130964 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
@@ -1857,9 +1857,6 @@
 }
 
 TEST_P(PaintPropertyTreeBuilderTest, FramesEstablishIsolation) {
-  if (!RuntimeEnabledFeatures::LayoutViewIsolationNodesEnabled())
-    return;
-
   SetBodyInnerHTML(R"HTML(
     <style>
       body { margin: 0; }
@@ -3447,106 +3444,121 @@
   CHECK_EXACT_VISUAL_RECT(LayoutRect(0, 0, 500, 600), child, clipper);
 }
 
-TEST_P(PaintPropertyTreeBuilderTest, ContainsPaintContentsTreeState) {
-  SetBodyInnerHTML(R"HTML(
-    <style>body { margin: 20px 30px; }</style>
-    <div id='clipper'
-        style='contain: paint; width: 300px; height: 200px;'>
-      <div id='child'
-          style='position: relative; width: 400px; height: 500px;'></div>
-    </div>
-  )HTML");
+TEST_P(PaintPropertyTreeBuilderTest, ContainPaintOrStyleLayoutTreeState) {
+  for (const char* containment : {"paint", "style layout"}) {
+    SCOPED_TRACE(containment);
+    SetBodyInnerHTML(String::Format(R"HTML(
+      <style>body { margin: 20px 30px; }</style>
+      <div id='clipper'
+          style='contain: %s; width: 300px; height: 200px;'>
+        <div id='child'
+            style='position: relative; width: 400px; height: 500px;'></div>
+      </div>
+    )HTML",
+                                    containment));
 
-  LayoutBoxModelObject* clipper =
-      ToLayoutBoxModelObject(GetLayoutObjectByElementId("clipper"));
-  const ObjectPaintProperties* clip_properties =
-      clipper->FirstFragment().PaintProperties();
-  LayoutObject* child = GetLayoutObjectByElementId("child");
-  const auto& clip_local_properties =
-      clipper->FirstFragment().LocalBorderBoxProperties();
+    LayoutBoxModelObject* clipper =
+        ToLayoutBoxModelObject(GetLayoutObjectByElementId("clipper"));
+    const ObjectPaintProperties* clip_properties =
+        clipper->FirstFragment().PaintProperties();
+    LayoutObject* child = GetLayoutObjectByElementId("child");
+    const auto& clip_local_properties =
+        clipper->FirstFragment().LocalBorderBoxProperties();
 
-  // Verify that we created isolation nodes.
-  EXPECT_TRUE(clip_properties->TransformIsolationNode());
-  EXPECT_TRUE(clip_properties->EffectIsolationNode());
-  EXPECT_TRUE(clip_properties->ClipIsolationNode());
+    // Verify that we created isolation nodes.
+    EXPECT_TRUE(clip_properties->TransformIsolationNode());
+    EXPECT_TRUE(clip_properties->EffectIsolationNode());
+    EXPECT_TRUE(clip_properties->ClipIsolationNode());
 
-  // Verify parenting:
+    // Verify parenting:
 
-  // Transform isolation node should be parented to the local border box
-  // properties transform, which should be the paint offset translation.
-  EXPECT_EQ(clip_properties->TransformIsolationNode()->Parent(),
-            clip_local_properties.Transform());
-  EXPECT_EQ(clip_properties->TransformIsolationNode()->Parent(),
-            clip_properties->PaintOffsetTranslation());
-  // Similarly, effect isolation node is parented to the local border box
-  // properties effect.
-  EXPECT_EQ(clip_properties->EffectIsolationNode()->Parent(),
-            clip_local_properties.Effect());
-  // Clip isolation node, however, is parented to the overflow clip, which is in
-  // turn parented to the local border box properties clip.
-  EXPECT_EQ(clip_properties->ClipIsolationNode()->Parent(),
-            clip_properties->OverflowClip());
-  EXPECT_EQ(clip_properties->OverflowClip()->Parent(),
-            clip_local_properties.Clip());
+    // Transform isolation node should be parented to the local border box
+    // properties transform, which should be the paint offset translation.
+    EXPECT_EQ(clip_properties->TransformIsolationNode()->Parent(),
+              clip_local_properties.Transform());
+    EXPECT_EQ(clip_properties->TransformIsolationNode()->Parent(),
+              clip_properties->PaintOffsetTranslation());
+    // Similarly, effect isolation node is parented to the local border box
+    // properties effect.
+    EXPECT_EQ(clip_properties->EffectIsolationNode()->Parent(),
+              clip_local_properties.Effect());
+    if (strcmp(containment, "paint") == 0) {
+      // If we contain paint, then clip isolation node is parented to the
+      // overflow clip, which is in turn parented to the local border box
+      // properties clip.
+      EXPECT_EQ(clip_properties->ClipIsolationNode()->Parent(),
+                clip_properties->OverflowClip());
+      EXPECT_EQ(clip_properties->OverflowClip()->Parent(),
+                clip_local_properties.Clip());
+    } else {
+      // Otherwise, the clip isolation node is parented to the local border box
+      // properties clip directly.
+      EXPECT_EQ(clip_properties->ClipIsolationNode()->Parent(),
+                clip_local_properties.Clip());
+    }
 
-  // Verify transform:
+    // Verify transform:
 
-  // Isolation transform node should be identity.
-  EXPECT_EQ(clip_properties->TransformIsolationNode()->Matrix(),
-            TransformationMatrix());
+    // Isolation transform node should be identity.
+    EXPECT_EQ(clip_properties->TransformIsolationNode()->Matrix(),
+              TransformationMatrix());
 
-  // TODO(crbug.com/732611): CAP invalidations are incorrect if there is
-  // scrolling.
-  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
-    EXPECT_FALSE(DocScrollTranslation());
-    EXPECT_TRUE(DocPreTranslation());
-    // Isolation induces paint offset translation, so the node should be
-    // different from the doc node, but its parent is the same as the doc node.
-    EXPECT_EQ(DocPreTranslation(), clipper->FirstFragment()
-                                       .LocalBorderBoxProperties()
-                                       .Transform()
-                                       ->Parent());
-  } else {
-    // Always create scroll translation for layout view even the document does
-    // not scroll (not enough content).
-    EXPECT_TRUE(DocScrollTranslation());
-    // Isolation induces paint offset translation, so the node should be
-    // different from the doc node, but its parent is the same as the doc node.
-    EXPECT_EQ(DocScrollTranslation(), clipper->FirstFragment()
-                                          .LocalBorderBoxProperties()
-                                          .Transform()
-                                          ->Parent());
+    // TODO(crbug.com/732611): CAP invalidations are incorrect if there is
+    // scrolling.
+    if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
+      EXPECT_FALSE(DocScrollTranslation());
+      EXPECT_TRUE(DocPreTranslation());
+      // Isolation induces paint offset translation, so the node should be
+      // different from the doc node, but its parent is the same as the doc
+      // node.
+      EXPECT_EQ(DocPreTranslation(), clipper->FirstFragment()
+                                         .LocalBorderBoxProperties()
+                                         .Transform()
+                                         ->Parent());
+    } else {
+      // Always create scroll translation for layout view even the document does
+      // not scroll (not enough content).
+      EXPECT_TRUE(DocScrollTranslation());
+      // Isolation induces paint offset translation, so the node should be
+      // different from the doc node, but its parent is the same as the doc
+      // node.
+      EXPECT_EQ(DocScrollTranslation(), clipper->FirstFragment()
+                                            .LocalBorderBoxProperties()
+                                            .Transform()
+                                            ->Parent());
+    }
+
+    // Verify clip:
+
+    EXPECT_EQ(DocContentClip(),
+              clipper->FirstFragment().LocalBorderBoxProperties().Clip());
+    // Clip isolation node should be big enough to encompass all other clips,
+    // including DocContentClip.
+    EXPECT_TRUE(
+        clip_properties->ClipIsolationNode()->ClipRect().Rect().Contains(
+            DocContentClip()->ClipRect().Rect()));
+
+    // Verify contents properties and child properties:
+
+    auto contents_properties = clipper->FirstFragment().ContentsProperties();
+    // Since the clipper is isolated, its paint offset should be 0, 0.
+    EXPECT_EQ(LayoutPoint(0, 0), clipper->FirstFragment().PaintOffset());
+    // Ensure that the contents properties match isolation nodes.
+    EXPECT_EQ(clip_properties->TransformIsolationNode(),
+              contents_properties.Transform());
+    EXPECT_EQ(clip_properties->ClipIsolationNode(), contents_properties.Clip());
+    EXPECT_EQ(clip_properties->EffectIsolationNode(),
+              contents_properties.Effect());
+
+    // Child should be using isolation nodes as its local border box properties.
+    EXPECT_EQ(contents_properties.Transform(),
+              child->FirstFragment().LocalBorderBoxProperties().Transform());
+    EXPECT_EQ(contents_properties.Clip(),
+              child->FirstFragment().LocalBorderBoxProperties().Clip());
+    EXPECT_EQ(contents_properties.Effect(),
+              child->FirstFragment().LocalBorderBoxProperties().Effect());
+    CHECK_EXACT_VISUAL_RECT(LayoutRect(0, 0, 400, 500), child, clipper);
   }
-
-  // Verify clip:
-
-  EXPECT_EQ(DocContentClip(),
-            clipper->FirstFragment().LocalBorderBoxProperties().Clip());
-  // Clip isolation node should be big enough to encompass all other clips,
-  // including DocContentClip.
-  EXPECT_TRUE(clip_properties->ClipIsolationNode()->ClipRect().Rect().Contains(
-      DocContentClip()->ClipRect().Rect()));
-
-  // Verify contents properties and child properties:
-
-  auto contents_properties = clipper->FirstFragment().ContentsProperties();
-  // Since the clipper is isolated, its paint offset should be 0, 0.
-  EXPECT_EQ(LayoutPoint(0, 0), clipper->FirstFragment().PaintOffset());
-  // Ensure that the contents properties match isolation nodes.
-  EXPECT_EQ(clip_properties->TransformIsolationNode(),
-            contents_properties.Transform());
-  EXPECT_EQ(clip_properties->ClipIsolationNode(), contents_properties.Clip());
-  EXPECT_EQ(clip_properties->EffectIsolationNode(),
-            contents_properties.Effect());
-
-  // Child should be using isolation nodes as its local border box properties.
-  EXPECT_EQ(contents_properties.Transform(),
-            child->FirstFragment().LocalBorderBoxProperties().Transform());
-  EXPECT_EQ(contents_properties.Clip(),
-            child->FirstFragment().LocalBorderBoxProperties().Clip());
-  EXPECT_EQ(contents_properties.Effect(),
-            child->FirstFragment().LocalBorderBoxProperties().Effect());
-  CHECK_EXACT_VISUAL_RECT(LayoutRect(0, 0, 400, 500), child, clipper);
 }
 
 TEST_P(PaintPropertyTreeBuilderTest, OverflowScrollContentsTreeState) {
diff --git a/third_party/blink/renderer/core/paint/paint_timing_detector.cc b/third_party/blink/renderer/core/paint/paint_timing_detector.cc
index c5c6608..23f48306 100644
--- a/third_party/blink/renderer/core/paint/paint_timing_detector.cc
+++ b/third_party/blink/renderer/core/paint/paint_timing_detector.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 #include "third_party/blink/renderer/core/paint/paint_timing_detector.h"
+#include "third_party/blink/public/platform/web_input_event.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
@@ -53,6 +54,28 @@
       DOMNodeIds::IdForNode(object.GetNode()));
 }
 
+void PaintTimingDetector::NotifyInputEvent(WebInputEvent::Type type) {
+  if (type == WebInputEvent::kMouseMove || type == WebInputEvent::kMouseEnter ||
+      type == WebInputEvent::kMouseLeave ||
+      WebInputEvent::IsPinchGestureEventType(type)) {
+    return;
+  }
+  text_paint_timing_detector_->StopRecordEntries();
+  image_paint_timing_detector_->StopRecordEntries();
+}
+
+void PaintTimingDetector::NotifyScroll(ScrollType scroll_type) {
+  if (scroll_type != kUserScroll && scroll_type != kCompositorScroll)
+    return;
+  text_paint_timing_detector_->StopRecordEntries();
+  image_paint_timing_detector_->StopRecordEntries();
+}
+
+bool PaintTimingDetector::NeedToNotifyInputOrScroll() {
+  return text_paint_timing_detector_->IsRecording() ||
+         image_paint_timing_detector_->IsRecording();
+}
+
 void PaintTimingDetector::DidChangePerformanceTiming() {
   Document* document = frame_view_->GetFrame().GetDocument();
   if (!document)
diff --git a/third_party/blink/renderer/core/paint/paint_timing_detector.h b/third_party/blink/renderer/core/paint/paint_timing_detector.h
index 4002f08..78e24f28 100644
--- a/third_party/blink/renderer/core/paint/paint_timing_detector.h
+++ b/third_party/blink/renderer/core/paint/paint_timing_detector.h
@@ -5,8 +5,10 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_TIMING_DETECTOR_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_TIMING_DETECTOR_H_
 
+#include "third_party/blink/public/platform/web_input_event.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/platform/heap/member.h"
+#include "third_party/blink/renderer/platform/scroll/scroll_types.h"
 
 namespace blink {
 
@@ -32,6 +34,9 @@
                             const PaintLayer& painting_layer);
   void NotifyNodeRemoved(const LayoutObject& object);
   void NotifyPrePaintFinished();
+  void NotifyInputEvent(WebInputEvent::Type);
+  bool NeedToNotifyInputOrScroll();
+  void NotifyScroll(ScrollType scroll_type);
   void DidChangePerformanceTiming();
   uint64_t CalculateVisualSize(const LayoutRect& invalidated_rect,
                                const PaintLayer& painting_layer) const;
diff --git a/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc b/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc
index c5692de..cb56ce4 100644
--- a/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc
+++ b/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc
@@ -117,6 +117,8 @@
 }
 
 void TextPaintTimingDetector::NotifyNodeRemoved(DOMNodeId node_id) {
+  if (!is_recording_)
+    return;
   for (TextRecord& record : texts_to_record_swap_time_) {
     if (record.node_id == node_id)
       record.node_id = kInvalidDOMNodeId;
@@ -215,15 +217,15 @@
   if (recorded_text_node_ids_.size() + size_zero_node_ids_.size() +
           texts_to_record_swap_time_.size() >=
       kTextNodeNumberLimit) {
-    Deactivate();
+    TRACE_EVENT_INSTANT2("loading", "TextPaintTimingDetector::OverNodeLimit",
+                         TRACE_EVENT_SCOPE_THREAD, "recorded_node_count",
+                         recorded_text_node_ids_.size(), "size_zero_node_count",
+                         size_zero_node_ids_.size());
+    StopRecordEntries();
   }
 }
 
-void TextPaintTimingDetector::Deactivate() {
-  TRACE_EVENT_INSTANT2("loading", "TextPaintTimingDetector::OverNodeLimit",
-                       TRACE_EVENT_SCOPE_THREAD, "recorded_node_count",
-                       recorded_text_node_ids_.size(), "size_zero_node_count",
-                       size_zero_node_ids_.size());
+void TextPaintTimingDetector::StopRecordEntries() {
   timer_.Stop();
   is_recording_ = false;
 }
diff --git a/third_party/blink/renderer/core/paint/text_paint_timing_detector.h b/third_party/blink/renderer/core/paint/text_paint_timing_detector.h
index d009f65..673ffc1d 100644
--- a/third_party/blink/renderer/core/paint/text_paint_timing_detector.h
+++ b/third_party/blink/renderer/core/paint/text_paint_timing_detector.h
@@ -63,6 +63,8 @@
   void Dispose() { timer_.Stop(); }
   base::TimeTicks LargestTextPaint() const { return largest_text_paint_; }
   base::TimeTicks LastTextPaint() const { return last_text_paint_; }
+  void StopRecordEntries();
+  bool IsRecording() const { return is_recording_; }
   void Trace(blink::Visitor*);
 
  private:
@@ -77,7 +79,6 @@
   void RegisterNotifySwapTime(ReportTimeCallback callback);
   void OnLargestTextDetected(const TextRecord&);
   void OnLastTextDetected(const TextRecord&);
-  void Deactivate();
 
   HashSet<DOMNodeId> recorded_text_node_ids_;
   HashSet<DOMNodeId> size_zero_node_ids_;
diff --git a/third_party/blink/renderer/core/scroll/scrollable_area.cc b/third_party/blink/renderer/core/scroll/scrollable_area.cc
index f5aaff4..260aff12 100644
--- a/third_party/blink/renderer/core/scroll/scrollable_area.cc
+++ b/third_party/blink/renderer/core/scroll/scrollable_area.cc
@@ -34,7 +34,10 @@
 #include "build/build_config.h"
 #include "cc/layers/picture_layer.h"
 #include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/core/frame/local_frame_view.h"
+#include "third_party/blink/renderer/core/layout/layout_box.h"
 #include "third_party/blink/renderer/core/page/chrome_client.h"
+#include "third_party/blink/renderer/core/paint/paint_timing_detector.h"
 #include "third_party/blink/renderer/core/scroll/programmatic_scroll_animator.h"
 #include "third_party/blink/renderer/core/scroll/scroll_animator_base.h"
 #include "third_party/blink/renderer/core/scroll/scrollbar_theme.h"
@@ -323,6 +326,18 @@
         GetScrollOffset() - old_offset, scroll_type);
   }
 
+  if (RuntimeEnabledFeatures::FirstContentfulPaintPlusPlusEnabled()) {
+    if (GetScrollOffset() != old_offset && GetLayoutBox() &&
+        GetLayoutBox()->GetFrameView() &&
+        GetLayoutBox()
+            ->GetFrameView()
+            ->GetPaintTimingDetector()
+            .NeedToNotifyInputOrScroll()) {
+      GetLayoutBox()->GetFrameView()->GetPaintTimingDetector().NotifyScroll(
+          scroll_type);
+    }
+  }
+
   GetScrollAnimator().SetCurrentOffset(offset);
 }
 
diff --git a/third_party/blink/renderer/devtools/front_end/elements/StylesSidebarPane.js b/third_party/blink/renderer/devtools/front_end/elements/StylesSidebarPane.js
index 8cf0257e..419ca3d 100644
--- a/third_party/blink/renderer/devtools/front_end/elements/StylesSidebarPane.js
+++ b/third_party/blink/renderer/devtools/front_end/elements/StylesSidebarPane.js
@@ -64,6 +64,7 @@
     this._isEditingStyle = false;
     /** @type {?RegExp} */
     this._filterRegex = null;
+    this._isActivePropertyHighlighted = false;
 
     this.contentElement.classList.add('styles-pane');
 
@@ -392,10 +393,13 @@
    * @param {?Elements.StylePropertyTreeElement} treeElement
    */
   _setActiveProperty(treeElement) {
+    if (this._isActivePropertyHighlighted)
+      SDK.OverlayModel.hideDOMNodeHighlight();
+    this._isActivePropertyHighlighted = false;
+
     if (!this.node())
       return;
 
-    SDK.OverlayModel.hideDOMNodeHighlight();
     if (!treeElement || treeElement.overloaded() || treeElement.inherited())
       return;
 
@@ -405,6 +409,7 @@
       if (!treeElement.name.startsWith(mode))
         continue;
       this.node().domModel().overlayModel().highlightDOMNodeWithConfig(this.node().id, {mode, selectors});
+      this._isActivePropertyHighlighted = true;
       break;
     }
   }
diff --git a/third_party/blink/renderer/platform/fonts/font_cache.cc b/third_party/blink/renderer/platform/fonts/font_cache.cc
index b72da9f..012098d 100644
--- a/third_party/blink/renderer/platform/fonts/font_cache.cc
+++ b/third_party/blink/renderer/platform/fonts/font_cache.cc
@@ -46,6 +46,7 @@
 #include "third_party/blink/renderer/platform/fonts/font_global_context.h"
 #include "third_party/blink/renderer/platform/fonts/font_platform_data.h"
 #include "third_party/blink/renderer/platform/fonts/font_smoothing_mode.h"
+#include "third_party/blink/renderer/platform/fonts/font_unique_name_lookup.h"
 #include "third_party/blink/renderer/platform/fonts/shaping/shape_cache.h"
 #include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
 #include "third_party/blink/renderer/platform/fonts/text_rendering_mode.h"
@@ -440,4 +441,18 @@
                                 WTF::Partitions::kAllocatedObjectPoolName);
 }
 
+sk_sp<SkTypeface> FontCache::CreateTypefaceFromUniqueName(
+    const FontFaceCreationParams& creation_params,
+    CString& name) {
+  FontUniqueNameLookup* unique_name_lookup =
+      FontGlobalContext::Get()->GetFontUniqueNameLookup();
+  DCHECK(unique_name_lookup);
+  sk_sp<SkTypeface> uniquely_identified_font =
+      unique_name_lookup->MatchUniqueName(creation_params.Family());
+  if (uniquely_identified_font) {
+    return uniquely_identified_font;
+  }
+  return nullptr;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/fonts/font_cache.h b/third_party/blink/renderer/platform/fonts/font_cache.h
index f32563d..589e0ba 100644
--- a/third_party/blink/renderer/platform/fonts/font_cache.h
+++ b/third_party/blink/renderer/platform/fonts/font_cache.h
@@ -253,6 +253,9 @@
       UChar32,
       const SimpleFontData* font_data_to_substitute,
       FontFallbackPriority = FontFallbackPriority::kText);
+  sk_sp<SkTypeface> CreateTypefaceFromUniqueName(
+      const FontFaceCreationParams& creation_params,
+      CString& name);
 
   friend class FontGlobalContext;
   FontCache();
diff --git a/third_party/blink/renderer/platform/fonts/skia/font_cache_skia.cc b/third_party/blink/renderer/platform/fonts/skia/font_cache_skia.cc
index 90898db..3eb56e5f 100644
--- a/third_party/blink/renderer/platform/fonts/skia/font_cache_skia.cc
+++ b/third_party/blink/renderer/platform/fonts/skia/font_cache_skia.cc
@@ -43,7 +43,6 @@
 #include "third_party/blink/renderer/platform/fonts/font_description.h"
 #include "third_party/blink/renderer/platform/fonts/font_face_creation_params.h"
 #include "third_party/blink/renderer/platform/fonts/font_global_context.h"
-#include "third_party/blink/renderer/platform/fonts/font_unique_name_lookup.h"
 #include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
 #include "third_party/blink/renderer/platform/fonts/skia/sktypeface_factory.h"
 #include "third_party/blink/renderer/platform/language.h"
@@ -57,25 +56,6 @@
 
 namespace blink {
 
-#if defined(OS_ANDROID) || defined(OS_LINUX)
-namespace {
-
-static sk_sp<SkTypeface> CreateTypefaceFromUniqueName(
-    const FontFaceCreationParams& creation_params,
-    CString& name) {
-  FontUniqueNameLookup* unique_name_lookup =
-      FontGlobalContext::Get()->GetFontUniqueNameLookup();
-  DCHECK(unique_name_lookup);
-  sk_sp<SkTypeface> uniquely_identified_font =
-      unique_name_lookup->MatchUniqueName(creation_params.Family());
-  if (uniquely_identified_font) {
-    return uniquely_identified_font;
-  }
-  return nullptr;
-}
-}  // namespace
-#endif
-
 AtomicString ToAtomicString(const SkString& str) {
   return AtomicString::FromUTF8(str.c_str(), str.size());
 }
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 5b94d2f..b179b4b 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -695,10 +695,6 @@
       implied_by: ["LayoutNG"],
     },
     {
-      name: "LayoutViewIsolationNodes",
-      status: "stable",
-    },
-    {
       name: "LazyFrameLoading",
     },
     {
diff --git a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
index c9d7ec2..14f5288 100644
--- a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
@@ -35,9 +35,13 @@
 crbug.com/591099 external/wpt/css/CSS2/floats-clear/no-clearance-adjoining-opposite-float.html [ Pass ]
 crbug.com/591099 external/wpt/css/CSS2/floats-clear/no-clearance-due-to-large-margin-after-left-right.html [ Pass ]
 crbug.com/877946 external/wpt/css/CSS2/linebox/anonymous-inline-inherit-001.html [ Pass ]
+crbug.com/591099 external/wpt/css/CSS2/text/white-space-bidirectionality-001.xht [ Pass ]
 crbug.com/859233 external/wpt/css/css-break/hit-test-inline-fragmentation-with-border-radius.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-sizing/intrinsic-percent-non-replaced-004.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-sizing/intrinsic-percent-non-replaced-005.html [ Pass ]
+crbug.com/591099 external/wpt/css/css-text/text-transform/text-transform-shaping-001.html [ Pass ]
+crbug.com/591099 external/wpt/css/css-text/text-transform/text-transform-shaping-002.html [ Pass ]
+crbug.com/591099 external/wpt/css/css-text/text-transform/text-transform-shaping-003.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-text/writing-system/writing-system-segment-break-001.html [ Pass ]
 crbug.com/626703 external/wpt/css/css-writing-modes/sizing-orthog-htb-in-vlr-004.xht [ Pass ]
 crbug.com/626703 external/wpt/css/css-writing-modes/sizing-orthog-prct-htb-in-vlr-001.xht [ Pass ]
@@ -50,10 +54,15 @@
 crbug.com/626703 external/wpt/css/css-writing-modes/sizing-orthog-vlr-in-htb-001.xht [ Pass ]
 crbug.com/626703 external/wpt/css/css-writing-modes/sizing-orthog-vlr-in-htb-004.xht [ Pass ]
 crbug.com/591099 external/wpt/css/css-writing-modes/two-levels-of-orthogonal-flows-percentage.html [ Pass ]
+crbug.com/591099 external/wpt/css/filter-effects/filtered-inline-applies-to-float.html [ Pass ]
+crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/sizing/block-size-with-min-or-max-content-1a.html [ Pass ]
+crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/sizing/block-size-with-min-or-max-content-1b.html [ Pass ]
+crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/sizing/hori-block-size-small-or-larger-than-container-with-min-or-max-content-2a.html [ Pass ]
+crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/sizing/hori-block-size-small-or-larger-than-container-with-min-or-max-content-2b.html [ Pass ]
+crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/sizing/vert-block-size-small-or-larger-than-container-with-min-or-max-content-2a.html [ Pass ]
 
 # New failures are appended below by the script.
 crbug.com/728378 compositing/culling/tile-occlusion-boundaries.html [ Failure ]
-crbug.com/864398 compositing/iframes/floating-self-painting-frame.html [ Failure ]
 crbug.com/591099 compositing/overflow/border-radius-above-composited-subframe.html [ Failure ]
 crbug.com/591099 css3/filters/backdrop-filter-rendering.html [ Pass ]
 crbug.com/591099 css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change.html [ Failure ]
@@ -89,6 +98,12 @@
 crbug.com/591099 external/wpt/css/css-rhythm/ [ Skip ]
 crbug.com/591099 external/wpt/css/css-shapes/shape-outside/supported-shapes/polygon/shape-outside-polygon-017.html [ Pass ]
 crbug.com/845902 external/wpt/css/css-sizing/whitespace-and-break.html [ Pass ]
+crbug.com/591099 external/wpt/css/css-text/boundary-shaping/boundary-shaping-001.html [ Pass ]
+crbug.com/591099 external/wpt/css/css-text/boundary-shaping/boundary-shaping-002.html [ Failure ]
+crbug.com/591099 external/wpt/css/css-text/boundary-shaping/boundary-shaping-006.html [ Failure ]
+crbug.com/591099 external/wpt/css/css-text/boundary-shaping/boundary-shaping-007.html [ Failure ]
+crbug.com/591099 external/wpt/css/css-text/boundary-shaping/boundary-shaping-009.html [ Failure ]
+crbug.com/591099 external/wpt/css/css-text/boundary-shaping/boundary-shaping-010.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-text/line-breaking/line-breaking-009.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-text/line-breaking/line-breaking-011.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-text/line-breaking/line-breaking-ic-002.html [ Pass ]
@@ -243,7 +258,6 @@
 crbug.com/591099 fast/overflow/overflow-update-transform.html [ Failure ]
 crbug.com/591099 fast/replaced/replaced-breaking.html [ Failure Pass ]
 crbug.com/591099 fast/scroll-snap/snaps-after-keyboard-scrolling-rtl.html [ Failure ]
-crbug.com/591099 fast/table/unbreakable-images-quirk.html [ Failure ]
 crbug.com/591099 fast/text/descent-clip-in-scaled-page.html [ Failure ]
 crbug.com/591099 fast/text/ellipsis-in-relative-inline-right.html [ Failure ]
 crbug.com/591099 fast/text/ellipsis-in-relative-inline.html [ Failure ]
@@ -287,7 +301,6 @@
 crbug.com/591099 printing/iframe-svg-in-object-print.html [ Failure ]
 crbug.com/591099 scrollbars/auto-scrollbar-fit-content.html [ Failure ]
 crbug.com/591099 svg/zoom/page/zoom-svg-float-border-padding.xml [ Pass ]
-crbug.com/591099 tables/mozilla/bugs/bug101674.html [ Failure ]
 crbug.com/591099 tables/mozilla/bugs/bug14159-1.html [ Pass ]
 crbug.com/591099 tables/mozilla/bugs/bug2973.html [ Pass ]
 crbug.com/591099 virtual/android/rootscroller/set-root-scroller.html [ Pass ]
diff --git a/third_party/blink/web_tests/LeakExpectations b/third_party/blink/web_tests/LeakExpectations
index 67eac970..92b18ee 100644
--- a/third_party/blink/web_tests/LeakExpectations
+++ b/third_party/blink/web_tests/LeakExpectations
@@ -95,6 +95,8 @@
 # Sheriff 2018-12-20
 crbug.com/916893 [ Linux ] virtual/bidi-caret-affinity/editing/pasteboard/drag-drop-iframe-refresh-crash.html [ Pass Leak ]
 
+# Sheriff 2019-01-04
+crbug.com/919117 [ Linux ] virtual/mouseevent_fractional/fast/events/middleClickAutoscroll-drag.html [ Pass Leak ]
 ###########################################################################
 # WARNING: Memory leaks must be fixed asap. Sheriff is expected to revert #
 # culprit CLs instead of suppressing the leaks. If you have any question, #
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 08e021f..5e613b4 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -2163,6 +2163,7 @@
 crbug.com/771233 [ Win10 ] http/tests/devtools/audits2/ [ Skip ]
 
 crbug.com/865477 [ Mac ] virtual/gpu/fast/canvas/OffscreenCanvas-copyImage.html [ Failure ]
+crbug.com/865477 [ Mac ] fast/canvas/OffscreenCanvas-copyImage.html [ Failure ]
 
 crbug.com/410974 fast/scroll-behavior/scroll-customization/scrollstate-basic.html [ Pass Failure ]
 crbug.com/410974 fast/scroll-behavior/scroll-customization/scrollstate-consume-deltas.html [ Pass Failure ]
@@ -2730,23 +2731,6 @@
 # Chrome touch-action computation ignores div transforms.
 crbug.com/715148 external/wpt/pointerevents/pointerevent_touch-action-rotated-divs_touch-manual.html  [ Skip ]
 
-crbug.com/606367 external/wpt/pointerevents/pointerevent_pointerout_pen-manual.html  [ Skip ]
-crbug.com/606367 external/wpt/pointerevents/pointerevent_boundary_events_at_implicit_release_hoverable_pointers-manual.html  [ Skip ]
-crbug.com/606367 external/wpt/pointerevents/pointerevent_lostpointercapture_for_disconnected_node-manual.html  [ Skip ]
-crbug.com/606367 external/wpt/pointerevents/pointerevent_lostpointercapture_is_first-manual.html  [ Skip ]
-crbug.com/606367 external/wpt/pointerevents/pointerevent_pointerenter_does_not_bubble-manual.html  [ Skip ]
-crbug.com/606367 external/wpt/pointerevents/pointerevent_pointerleave_descendant_over-manual.html  [ Skip ]
-crbug.com/606367 external/wpt/pointerevents/pointerevent_pointerleave_descendants-manual.html  [ Skip ]
-crbug.com/606367 external/wpt/pointerevents/pointerevent_pointerleave_does_not_bubble-manual.html  [ Skip ]
-crbug.com/606367 external/wpt/pointerevents/pointerevent_pointerout_received_once-manual.html  [ Skip ]
-crbug.com/606367 external/wpt/pointerevents/pointerevent_releasepointercapture_invalid_pointerid-manual.html  [ Skip ]
-crbug.com/606367 external/wpt/pointerevents/pointerevent_releasepointercapture_onpointerup_mouse-manual.html  [ Skip ]
-crbug.com/606367 external/wpt/pointerevents/pointerevent_setpointercapture_disconnected-manual.html  [ Skip ]
-crbug.com/606367 external/wpt/pointerevents/pointerevent_setpointercapture_invalid_pointerid-manual.html  [ Skip ]
-crbug.com/606367 external/wpt/pointerevents/pointerevent_setpointercapture_relatedtarget-manual.html  [ Skip ]
-crbug.com/606367 external/wpt/pointerevents/pointerevent_suppress_compat_events_on_click-manual.html  [ Skip ]
-crbug.com/606367 external/wpt/pointerevents/pointerevent_suppress_compat_events_on_drag_mouse-manual.html  [ Skip ]
-
 # Disabled until v8 roll.
 crbug.com/906847 inspector-protocol/runtime/runtime-getProperties.js [ Skip ]
 crbug.com/906847 inspector-protocol/debugger/debugger-scope-skip-variables-with-empty-name.js [ Skip ]
@@ -6035,3 +6019,6 @@
 
 # Sheriff 2019-01-03
 crbug.com/918905 [ Mac Linux ] external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/sizing/block-size-with-min-or-max-content-table-1b.html [ Pass Failure ]
+
+# Sheriff 2019-01-04
+crbug.com/919096 [ Linux ] virtual/display-lock/display-lock/lock-before-append/measure-updated-layout.html [ Timeout Pass ]
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
index c3ffc6a..7179920 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
@@ -30785,6 +30785,18 @@
      {}
     ]
    ],
+   "css/css-backgrounds/background-image-cover-zoomed-1.html": [
+    [
+     "/css/css-backgrounds/background-image-cover-zoomed-1.html",
+     [
+      [
+       "/css/css-backgrounds/reference/background-image-cover-zoomed-1-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-backgrounds/background-image-first-letter.html": [
     [
      "/css/css-backgrounds/background-image-first-letter.html",
@@ -115645,11 +115657,6 @@
      {}
     ]
    ],
-   "common/sleep.py": [
-    [
-     {}
-    ]
-   ],
    "common/slow.py": [
     [
      {}
@@ -125090,6 +125097,11 @@
      {}
     ]
    ],
+   "css/css-backgrounds/reference/background-image-cover-zoomed-1-ref.html": [
+    [
+     {}
+    ]
+   ],
    "css/css-backgrounds/reference/background-image-first-letter-ref.html": [
     [
      {}
@@ -179065,12 +179077,22 @@
      {}
     ]
    ],
+   "resource-timing/iframe-setdomain.sub.html": [
+    [
+     {}
+    ]
+   ],
    "resource-timing/no-entries-for-cross-origin-css-fetched.sub-expected.txt": [
     [
      {}
     ]
    ],
-   "resource-timing/resource-timing-level1.js": [
+   "resource-timing/resource-timing-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "resource-timing/resource-timing.js": [
     [
      {}
     ]
@@ -179140,11 +179162,6 @@
      {}
     ]
    ],
-   "resource-timing/resources/iframe-setdomain.sub.html": [
-    [
-     {}
-    ]
-   ],
    "resource-timing/resources/iframe_TAO_match_origin.html": [
     [
      {}
@@ -186520,6 +186537,11 @@
      {}
     ]
    ],
+   "webrtc/RTCPeerConnection-track-stats.https-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "webrtc/RTCPeerConnection-transceivers.https-expected.txt": [
     [
      {}
@@ -205827,6 +205849,12 @@
      {}
     ]
    ],
+   "css/css-backgrounds/border-image-slice-shorthand-reset.html": [
+    [
+     "/css/css-backgrounds/border-image-slice-shorthand-reset.html",
+     {}
+    ]
+   ],
    "css/css-backgrounds/parsing/background-attachment-invalid.html": [
     [
      "/css/css-backgrounds/parsing/background-attachment-invalid.html",
@@ -271745,20 +271773,20 @@
      {}
     ]
    ],
-   "resource-timing/resource-timing-level1.sub.html": [
-    [
-     "/resource-timing/resource-timing-level1.sub.html",
-     {
-      "timeout": "long"
-     }
-    ]
-   ],
    "resource-timing/resource-timing-tojson.html": [
     [
      "/resource-timing/resource-timing-tojson.html",
      {}
     ]
    ],
+   "resource-timing/resource-timing.html": [
+    [
+     "/resource-timing/resource-timing.html",
+     {
+      "timeout": "long"
+     }
+    ]
+   ],
    "resource-timing/resource_TAO_cross_origin_redirect_chain.html": [
     [
      "/resource-timing/resource_TAO_cross_origin_redirect_chain.html",
@@ -303521,7 +303549,7 @@
    "testharness"
   ],
   "bluetooth/resources/bluetooth-helpers.js": [
-   "64a20c2f994164abd16d77143b5e39da89927ed4",
+   "c01fc9fcf168056b319c18ca40a92df73665b12a",
    "support"
   ],
   "bluetooth/resources/bluetooth-scanning-helpers.js": [
@@ -304352,10 +304380,6 @@
    "6805c323df5a975231648b830e33ce183c3cbbd3",
    "support"
   ],
-  "common/sleep.py": [
-   "28065eff5fc907dbfb69f4c69020d049c7daa35d",
-   "support"
-  ],
   "common/slow.py": [
    "f3b1c7e2ea61b571bd56cc1c70c5f89bb8e7e4dc",
    "support"
@@ -325488,6 +325512,10 @@
    "7051aa359c8cdf427a6c1b63179baad26c5a1645",
    "reftest"
   ],
+  "css/css-backgrounds/background-image-cover-zoomed-1.html": [
+   "0d434abe23a50c39aadce1449fcd148a1bdffe6a",
+   "reftest"
+  ],
   "css/css-backgrounds/background-image-first-letter.html": [
    "f78460b90708223616a019aa48aaaa4fe8b2ce43",
    "reftest"
@@ -327300,6 +327328,10 @@
    "5ef9ff0d1df560a31a87df7438c9e435d6ad65e3",
    "reftest"
   ],
+  "css/css-backgrounds/border-image-slice-shorthand-reset.html": [
+   "7f178b428de3b3e2b185d3a02eb9baad80e63719",
+   "testharness"
+  ],
   "css/css-backgrounds/border-image-space-001.html": [
    "8794d37324c8284e6fd9b265842ed23574c68b09",
    "reftest"
@@ -328068,6 +328100,10 @@
    "f8b2ad25ccdc743e638d402590141296c142e602",
    "support"
   ],
+  "css/css-backgrounds/reference/background-image-cover-zoomed-1-ref.html": [
+   "d61bac9feddf1013aa2db92cba8d258a523de446",
+   "support"
+  ],
   "css/css-backgrounds/reference/background-image-first-letter-ref.html": [
    "304be71fbace6a964566e2a24dc8ab802cdec7e3",
    "support"
@@ -437013,7 +437049,7 @@
    "support"
   ],
   "resource-timing/SyntheticResponse.py": [
-   "26e0a8017d6c2c7b51c94998f34a79fa20eb1e12",
+   "528ee239c174e9bd7618288f88cc09f8c5a9b58b",
    "support"
   ],
   "resource-timing/buffer-full-add-after-full-event.html": [
@@ -437068,6 +437104,10 @@
    "a7542f191c10ea7b42110b5fc80e7abf43174bb5",
    "testharness"
   ],
+  "resource-timing/iframe-setdomain.sub.html": [
+   "944ee10c44259ac84281cd802144068583d539db",
+   "support"
+  ],
   "resource-timing/no-entries-for-cross-origin-css-fetched.sub-expected.txt": [
    "a256bb2dc766a40f21115049572246bf003499ba",
    "support"
@@ -437076,18 +437116,22 @@
    "92d94a17ec0df642b0a500dbd9fb5aa06eaacb64",
    "testharness"
   ],
-  "resource-timing/resource-timing-level1.js": [
-   "6c63eff7b297c8ec05b917b6bca55e3e622d3448",
+  "resource-timing/resource-timing-expected.txt": [
+   "e8973024c18b7b05f08107fd2ac414e3d6c4e1b2",
    "support"
   ],
-  "resource-timing/resource-timing-level1.sub.html": [
-   "1555ef4d0cfe55d9a4d732b7244ad213ab4c1969",
-   "testharness"
-  ],
   "resource-timing/resource-timing-tojson.html": [
    "77094f4b843a43fb30aeca48c505337b4322ca81",
    "testharness"
   ],
+  "resource-timing/resource-timing.html": [
+   "ad97044a6291912880046a4e784af13d28ab2f71",
+   "testharness"
+  ],
+  "resource-timing/resource-timing.js": [
+   "8d97b2f26871e3a492cc6d3d253808e0cf18b0f2",
+   "support"
+  ],
   "resource-timing/resource_TAO_cross_origin_redirect_chain.html": [
    "522188279c3ab81ec27cd7e83f014ecf747b21a8",
    "testharness"
@@ -437260,10 +437304,6 @@
    "31a769eb3666492a3804f1c3deca73df526c0a4f",
    "support"
   ],
-  "resource-timing/resources/iframe-setdomain.sub.html": [
-   "4a2f609aa469f14d7efe64baaeb739efdb2fdc75",
-   "support"
-  ],
   "resource-timing/resources/iframe_TAO_match_origin.html": [
    "cf68aade7954e609087d34f21e437c285eb73a58",
    "support"
@@ -449425,7 +449465,7 @@
    "support"
   ],
   "webrtc/RTCPeerConnection-remote-track-mute.https.html": [
-   "56fe761425096e963589309b828a8a7f7d36a9be",
+   "095fe50e305f853cb3c76030c0f0cb3cd86e4fff",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-removeTrack.https-expected.txt": [
@@ -449521,19 +449561,23 @@
    "testharness"
   ],
   "webrtc/RTCPeerConnection-setRemoteDescription-tracks.https-expected.txt": [
-   "c22013632bdda50e4e471a73361871b4a9cb4eb8",
+   "4eb476dd3111e14112aa5e2175b83d0f2b5f5869",
    "support"
   ],
   "webrtc/RTCPeerConnection-setRemoteDescription-tracks.https.html": [
-   "aa3f93e235e3408bba55548de226e76327ce40f5",
+   "7c565dd1c3baf8a379a5b89cb342e11413c3b032",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-setRemoteDescription.html": [
    "182abea403ed7bee6568aac402cbb6a8caac6159",
    "testharness"
   ],
+  "webrtc/RTCPeerConnection-track-stats.https-expected.txt": [
+   "bd8e8449e00bea6b07050accc890d99e8e61d4b2",
+   "support"
+  ],
   "webrtc/RTCPeerConnection-track-stats.https.html": [
-   "30c368f670d78154fd3ca3c67d20eb67d9a052e7",
+   "2d45c343d861f0b62a7d3d1f979ff7702862e5d7",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-transceivers.https-expected.txt": [
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/resources/bluetooth-helpers.js b/third_party/blink/web_tests/external/wpt/bluetooth/resources/bluetooth-helpers.js
index 64a20c2..c01fc9fc 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/resources/bluetooth-helpers.js
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/resources/bluetooth-helpers.js
@@ -18,8 +18,8 @@
 }
 
 function performChromiumSetup() {
-  // Make sure we are actually on Chromium.
-  if (!Mojo) {
+  // Make sure we are actually on Chromium with Mojo enabled.
+  if (typeof Mojo === 'undefined') {
     return;
   }
 
diff --git a/third_party/blink/web_tests/external/wpt/common/sleep.py b/third_party/blink/web_tests/external/wpt/common/sleep.py
deleted file mode 100644
index 28065eff..0000000
--- a/third_party/blink/web_tests/external/wpt/common/sleep.py
+++ /dev/null
@@ -1,8 +0,0 @@
-import time
-
-# sleep can be lower than requested value in some platforms: https://bugs.python.org/issue31539
-# We add padding here to compensate for that.
-sleep_padding = 15.0
-
-def sleep_at_least(sleep_in_ms):
-    time.sleep((sleep_in_ms + sleep_padding) / 1E3);
diff --git a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-image-cover-zoomed-1.html b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-image-cover-zoomed-1.html
new file mode 100644
index 0000000..0d434abe
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-image-cover-zoomed-1.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>CSS Background Test: background-size:cover should cover at zoom</title>
+    <link rel="author" title="schenney" href="mailto:schenney@chromium.org">
+    <link rel="help" href="http://www.w3.org/TR/css3-background">
+    <link rel="match" href="reference/background-image-cover-zoomed-1-ref.html">
+    <style>
+      body {
+        zoom: 80%;
+      }
+      body > div {
+        background: #f00;
+        width: 504px;
+        height: 252px;
+      }
+
+      div > div {
+        width: 504px;
+        height: 252px;
+        background-image: url(support/40px-wide-20px-tall-green-rect.png);
+        background-size: cover;
+        background-repeat: no-repeat;
+      }
+  </style>
+</head>
+
+<body>
+  <div>
+    <div>
+    </div>
+  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/border-image-slice-shorthand-reset.html b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/border-image-slice-shorthand-reset.html
new file mode 100644
index 0000000..7f178b4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/border-image-slice-shorthand-reset.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-backgrounds/#border-image-slice" />
+<link rel="help" href="https://drafts.csswg.org/css-backgrounds/#border-image" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+  div {
+    border: 1px solid;
+    border-image-slice: 1;
+  }
+  div {
+    /* Should reset border-image-slice */
+    border-image: linear-gradient(black, black);
+  }
+</style>
+<div>This text should not have a border, just corner dots</div>
+<script>
+  test(() => {
+    assert_equals(getComputedStyle(document.querySelector("div")).borderImageSlice, "100%");
+  }, "Check that the border-image shorthand resets border-image-slice to its initial value.");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/reference/background-image-cover-zoomed-1-ref.html b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/reference/background-image-cover-zoomed-1-ref.html
new file mode 100644
index 0000000..d61bac9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/reference/background-image-cover-zoomed-1-ref.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>CSS Background Test Reference</title>
+    <link rel="author" title="schenney" href="mailto:schenney@chromium.org">
+    <style>
+      body {
+        zoom: 80%;
+      }
+      body > div {
+        width: 504px;
+        height: 252px;
+        background-image: url(../support/40px-wide-20px-tall-green-rect.png);
+        background-size: cover;
+        background-repeat: no-repeat;
+      }
+  </style>
+</head>
+
+<body>
+  <div>
+  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-reference-filter.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-reference-filter.html
new file mode 100644
index 0000000..74c813c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-reference-filter.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>backdrop-filter: Correctly apply reference filter</title>
+<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
+<link rel="match"  href="backdrop-filter-clip-rect-ref.html">
+
+<div>
+  <p>Expected: A green box, color-inverted inside the short, wide box with a<br>
+  blue border, and not color-inverted anywhere else. In particular, there should be<br>
+  no color inversion inside the tall, narrow box, or anywhere outside that.</p>
+</div>
+<div class="box"></div>
+<div class="navbar">
+  <div class="menu"></div>
+  <div class="menu2"></div>
+</div>
+
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" style="display:none">
+  <defs>
+    <filter id="svgInvert">
+      <feColorMatrix in="SourceGraphic" type="matrix" values="-1  0  0 0 1
+                                                               0 -1  0 0 1
+                                                               0  0 -1 0 1
+                                                               0  0  0 1 0"/>
+    </filter>
+  </defs>
+</svg>
+
+<style>
+div {
+  position: absolute;
+}
+.box {
+  width: 200px;
+  height: 200px;
+  top: 100px;
+  left: 100px;
+  background: green;
+}
+.navbar {
+  width: 300px;
+  height: 50px;
+  top: 150px;
+  left: 50px;
+  border: 1px solid blue;
+  backdrop-filter: url(#svgInvert);
+}
+.menu {
+  width: 100px;
+  height: 150px;
+  top: 50px;
+  left: 95px;
+  border: 1px solid blue;
+}
+.menu2 {
+  width: 100px;
+  height: 30px;
+  top: -32px;
+  left: 95px;
+  border: 1px solid blue;
+}
+</style>
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/layout-animations-disabled-violation-report-js-tentative.html b/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/layout-animations-disabled-violation-report-js-tentative.html
index b45d8c3..e7150a9 100644
--- a/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/layout-animations-disabled-violation-report-js-tentative.html
+++ b/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/layout-animations-disabled-violation-report-js-tentative.html
@@ -7,7 +7,7 @@
 <body>
 <script>
   test(() => {
-    document.policy.allowedFeatures().forEach((enabled_feature) => {
+    document.featurePolicy.allowedFeatures().forEach((enabled_feature) => {
       assert_not_equals(enabled_feature, "layout-animations");
     });
   },
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/layout-animations-disabled-violation-report-keyframes-tentative.html b/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/layout-animations-disabled-violation-report-keyframes-tentative.html
index 0701c50..163ccae 100644
--- a/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/layout-animations-disabled-violation-report-keyframes-tentative.html
+++ b/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/layout-animations-disabled-violation-report-keyframes-tentative.html
@@ -7,7 +7,7 @@
 <body>
 <script>
   test(() => {
-    document.policy.allowedFeatures().forEach((enabled_feature) => {
+    document.featurePolicy.allowedFeatures().forEach((enabled_feature) => {
       assert_not_equals(enabled_feature, "layout-animations");
     });
   },
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/vertical-scroll-main-frame-manual.tentative.html b/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/vertical-scroll-main-frame-manual.tentative.html
index 6392ea5..34cf142 100644
--- a/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/vertical-scroll-main-frame-manual.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/vertical-scroll-main-frame-manual.tentative.html
@@ -25,7 +25,7 @@
 
   // Sanity check.
   test(() => {
-    assert_false(document.policy.allowsFeature("vertical-scroll"),
+    assert_false(document.featurePolicy.allowsFeature("vertical-scroll"),
                  "Expected 'vertical-scroll' to be disabled.");
   }, "'vertical-scroll' disabled in main document.");
 
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/feature-policy-header-policy-allowed-for-all.https.sub.html b/third_party/blink/web_tests/external/wpt/feature-policy/feature-policy-header-policy-allowed-for-all.https.sub.html
index 3334b97..6bf7ca65 100644
--- a/third_party/blink/web_tests/external/wpt/feature-policy/feature-policy-header-policy-allowed-for-all.https.sub.html
+++ b/third_party/blink/web_tests/external/wpt/feature-policy/feature-policy-header-policy-allowed-for-all.https.sub.html
@@ -15,7 +15,7 @@
   // Test that fullscreen's allowlist is ['*']
   test(function() {
     assert_array_equals(
-      document.policy.getAllowlistForFeature('fullscreen'),
+      document.featurePolicy.getAllowlistForFeature('fullscreen'),
       ['*']);
   }, header_policy + ' -- test allowlist is ['*']');
 
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/feature-policy-header-policy-allowed-for-self.https.sub.html b/third_party/blink/web_tests/external/wpt/feature-policy/feature-policy-header-policy-allowed-for-self.https.sub.html
index 60e22f4..8641746e 100644
--- a/third_party/blink/web_tests/external/wpt/feature-policy/feature-policy-header-policy-allowed-for-self.https.sub.html
+++ b/third_party/blink/web_tests/external/wpt/feature-policy/feature-policy-header-policy-allowed-for-self.https.sub.html
@@ -15,7 +15,7 @@
   // Test that fullscreen's allowlist is ['same_origin']
   test(function() {
     assert_array_equals(
-      document.policy.getAllowlistForFeature('fullscreen'),
+      document.featurePolicy.getAllowlistForFeature('fullscreen'),
       [same_origin]);
   }, header_policy + ' -- test allowlist is [same_origin]');
 
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/feature-policy-header-policy-allowed-for-some.https.sub.html b/third_party/blink/web_tests/external/wpt/feature-policy/feature-policy-header-policy-allowed-for-some.https.sub.html
index ade5dda..e07c51f 100644
--- a/third_party/blink/web_tests/external/wpt/feature-policy/feature-policy-header-policy-allowed-for-some.https.sub.html
+++ b/third_party/blink/web_tests/external/wpt/feature-policy/feature-policy-header-policy-allowed-for-some.https.sub.html
@@ -16,7 +16,7 @@
   // Test that fullscreen's allowlist is [same_origin, cross_origin, 'https://www.example.com']
   test(function() {
     assert_array_equals(
-      document.policy.getAllowlistForFeature('fullscreen').sort(),
+      document.featurePolicy.getAllowlistForFeature('fullscreen').sort(),
       [same_origin, cross_origin, 'https://www.example.com'].sort());
   }, header_policy + ' -- test allowlist is [same_origin, cross_origin, https://www.example.com]');
 
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/feature-policy-header-policy-declined.https.sub.html b/third_party/blink/web_tests/external/wpt/feature-policy/feature-policy-header-policy-declined.https.sub.html
index 531c919..d00d592 100644
--- a/third_party/blink/web_tests/external/wpt/feature-policy/feature-policy-header-policy-declined.https.sub.html
+++ b/third_party/blink/web_tests/external/wpt/feature-policy/feature-policy-header-policy-declined.https.sub.html
@@ -16,7 +16,7 @@
   // Test that fullscreen's allowlist is [same_origin, cross_origin, 'https://www.example.com']
   test(function() {
     assert_array_equals(
-      document.policy.getAllowlistForFeature('fullscreen'),
+      document.featurePolicy.getAllowlistForFeature('fullscreen'),
       [cross_origin, 'https://www.example.com'].sort());
   }, header_policy + ' -- test allowlist is [cross_origin, https://www.example.com]');
 
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/feature-policy-header-policy-disallowed-for-all.https.sub.html b/third_party/blink/web_tests/external/wpt/feature-policy/feature-policy-header-policy-disallowed-for-all.https.sub.html
index c025705a..9a0f483 100644
--- a/third_party/blink/web_tests/external/wpt/feature-policy/feature-policy-header-policy-disallowed-for-all.https.sub.html
+++ b/third_party/blink/web_tests/external/wpt/feature-policy/feature-policy-header-policy-disallowed-for-all.https.sub.html
@@ -15,7 +15,7 @@
   // Test that fullscreen's allowlist is []
   test(function() {
     assert_array_equals(
-      document.policy.getAllowlistForFeature('fullscreen'),
+      document.featurePolicy.getAllowlistForFeature('fullscreen'),
       []);
   }, header_policy + ' -- test allowlist is []');
 
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/idlharness.window-expected.txt b/third_party/blink/web_tests/external/wpt/feature-policy/idlharness.window-expected.txt
index 43914ea6..1d9622d 100644
--- a/third_party/blink/web_tests/external/wpt/feature-policy/idlharness.window-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/feature-policy/idlharness.window-expected.txt
@@ -2,13 +2,13 @@
 PASS idl_test setup
 PASS Partial interface Document: original interface defined
 PASS Partial interface HTMLIFrameElement: original interface defined
-FAIL Stringification of document.featurePolicy assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL FeaturePolicy interface: document.featurePolicy must inherit property "allowsFeature(DOMString, DOMString)" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL FeaturePolicy interface: calling allowsFeature(DOMString, DOMString) on document.featurePolicy with too few arguments must throw TypeError assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL FeaturePolicy interface: document.featurePolicy must inherit property "features()" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL FeaturePolicy interface: document.featurePolicy must inherit property "allowedFeatures()" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL FeaturePolicy interface: document.featurePolicy must inherit property "getAllowlistForFeature(DOMString)" with the proper type assert_equals: wrong typeof object expected "object" but got "undefined"
-FAIL FeaturePolicy interface: calling getAllowlistForFeature(DOMString) on document.featurePolicy with too few arguments must throw TypeError assert_equals: wrong typeof object expected "object" but got "undefined"
+PASS Stringification of document.featurePolicy
+PASS FeaturePolicy interface: document.featurePolicy must inherit property "allowsFeature(DOMString, DOMString)" with the proper type
+PASS FeaturePolicy interface: calling allowsFeature(DOMString, DOMString) on document.featurePolicy with too few arguments must throw TypeError
+FAIL FeaturePolicy interface: document.featurePolicy must inherit property "features()" with the proper type assert_inherits: property "features" not found in prototype chain
+PASS FeaturePolicy interface: document.featurePolicy must inherit property "allowedFeatures()" with the proper type
+PASS FeaturePolicy interface: document.featurePolicy must inherit property "getAllowlistForFeature(DOMString)" with the proper type
+PASS FeaturePolicy interface: calling getAllowlistForFeature(DOMString) on document.featurePolicy with too few arguments must throw TypeError
 FAIL FeaturePolicyViolationReportBody interface: existence and properties of interface object assert_own_property: self does not have own property "FeaturePolicyViolationReportBody" expected property "FeaturePolicyViolationReportBody" missing
 FAIL FeaturePolicyViolationReportBody interface object length assert_own_property: self does not have own property "FeaturePolicyViolationReportBody" expected property "FeaturePolicyViolationReportBody" missing
 FAIL FeaturePolicyViolationReportBody interface object name assert_own_property: self does not have own property "FeaturePolicyViolationReportBody" expected property "FeaturePolicyViolationReportBody" missing
@@ -20,8 +20,8 @@
 FAIL FeaturePolicyViolationReportBody interface: attribute lineNumber assert_own_property: self does not have own property "FeaturePolicyViolationReportBody" expected property "FeaturePolicyViolationReportBody" missing
 FAIL FeaturePolicyViolationReportBody interface: attribute columnNumber assert_own_property: self does not have own property "FeaturePolicyViolationReportBody" expected property "FeaturePolicyViolationReportBody" missing
 FAIL FeaturePolicyViolationReportBody interface: attribute disposition assert_own_property: self does not have own property "FeaturePolicyViolationReportBody" expected property "FeaturePolicyViolationReportBody" missing
-FAIL HTMLIFrameElement interface: attribute featurePolicy assert_true: The prototype object must have a property "featurePolicy" expected true got false
-FAIL Document interface: attribute featurePolicy assert_true: The prototype object must have a property "featurePolicy" expected true got false
-FAIL Document interface: document must inherit property "featurePolicy" with the proper type assert_inherits: property "featurePolicy" not found in prototype chain
+PASS HTMLIFrameElement interface: attribute featurePolicy
+PASS Document interface: attribute featurePolicy
+PASS Document interface: document must inherit property "featurePolicy" with the proper type
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/resources/feature-policy-allowedfeatures.html b/third_party/blink/web_tests/external/wpt/feature-policy/resources/feature-policy-allowedfeatures.html
index 9cc8e1e..f4b02027 100644
--- a/third_party/blink/web_tests/external/wpt/feature-policy/resources/feature-policy-allowedfeatures.html
+++ b/third_party/blink/web_tests/external/wpt/feature-policy/resources/feature-policy-allowedfeatures.html
@@ -2,6 +2,6 @@
 'use strict';
 
 window.onload = function() {
-  parent.postMessage(document.policy.allowedFeatures(), '*');
+  parent.postMessage(document.featurePolicy.allowedFeatures(), '*');
 }
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/resources/featurepolicy.js b/third_party/blink/web_tests/external/wpt/feature-policy/resources/featurepolicy.js
index 38ae418..a0756e3 100644
--- a/third_party/blink/web_tests/external/wpt/feature-policy/resources/featurepolicy.js
+++ b/third_party/blink/web_tests/external/wpt/feature-policy/resources/featurepolicy.js
@@ -1,6 +1,7 @@
 // Feature test to avoid timeouts
 function assert_feature_policy_supported() {
-  assert_not_equals(document.policy, undefined, 'Feature Policy is supported');
+  assert_not_equals(document.featurePolicy, undefined,
+                    'Feature Policy is supported');
 }
 // Tests whether a feature that is enabled/disabled by feature policy works
 // as expected.
@@ -257,7 +258,8 @@
 // Arguments:
 //     expected_policy: A list of {feature, allowlist} pairs where the feature is
 //         enabled for every origin in the allowlist, in the |policy|.
-//     policy: Either a document.policy or a iframe.policy to be tested.
+//     policy: Either a document.featurePolicy or an iframe.featurePolicy to be
+//         tested.
 //     message: A short description of what policy is being tested.
 function test_allowlists(expected_policy, policy, message) {
   for (var allowlist of allowlists) {
@@ -409,7 +411,7 @@
   document.body.appendChild(frame);
   // frame_policy should be dynamically updated as allow and allowfullscreen is
   // updated.
-  var frame_policy = frame.policy;
+  var frame_policy = frame.featurePolicy;
   if (typeof allow !== 'undefined') {
     frame.setAttribute('allow', allow);
   }
diff --git a/third_party/blink/web_tests/external/wpt/origin-policy/origin-policy-features.https.tentative.html b/third_party/blink/web_tests/external/wpt/origin-policy/origin-policy-features.https.tentative.html
index b5dbe3e..e83acf1d 100644
--- a/third_party/blink/web_tests/external/wpt/origin-policy/origin-policy-features.https.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/origin-policy/origin-policy-features.https.tentative.html
@@ -7,8 +7,8 @@
 <body>
   <script>
     async_test(t => {
-      assert_false(document.policy.allowsFeature('geolocation'));
-      assert_true(document.policy.allowsFeature('camera'));
+      assert_false(document.featurePolicy.allowsFeature('geolocation'));
+      assert_true(document.featurePolicy.allowsFeature('camera'));
       t.done();
     }, "Origin-Policy-based Feature policy");
   </script>
diff --git a/third_party/blink/web_tests/external/wpt/resource-timing/SyntheticResponse.py b/third_party/blink/web_tests/external/wpt/resource-timing/SyntheticResponse.py
index 26e0a80..528ee23 100644
--- a/third_party/blink/web_tests/external/wpt/resource-timing/SyntheticResponse.py
+++ b/third_party/blink/web_tests/external/wpt/resource-timing/SyntheticResponse.py
@@ -1,7 +1,5 @@
 import urllib
-import sys, os
-sys.path.append(os.path.join(os.path.dirname(__file__), "../common/"))
-import sleep
+import time
 
 def main(request, response):
     index = request.request_path.index("?")
@@ -13,7 +11,7 @@
         if arg.startswith("ignored"):
             continue
         elif arg.endswith("ms"):
-            sleep.sleep_at_least(float(arg[0:-2]))
+            time.sleep(float(arg[0:-2]) / 1E3);
         elif arg.startswith("redirect:"):
             return (302, "WEBPERF MARKETING"), [("Location", urllib.unquote(arg[9:]))], "TEST"
         elif arg.startswith("mime:"):
diff --git a/third_party/blink/web_tests/external/wpt/resource-timing/iframe-setdomain.sub.html b/third_party/blink/web_tests/external/wpt/resource-timing/iframe-setdomain.sub.html
new file mode 100644
index 0000000..944ee10
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/resource-timing/iframe-setdomain.sub.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>domain: {{domains[www]}}</title>
+</head>
+<body>
+  <script>
+    // The purpose of this IFrame is to change the 'document.domain'
+    document.domain = "{{domains[www]}}";
+  </script>
+  The resource-timings.html test loads this document into an IFrame to vet that setting
+  'document.domain' does not effect the timing allowed.
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/resource-timing/resource-timing-expected.txt b/third_party/blink/web_tests/external/wpt/resource-timing/resource-timing-expected.txt
new file mode 100644
index 0000000..e897302
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/resource-timing/resource-timing-expected.txt
@@ -0,0 +1,29 @@
+This is a testharness.js-based test.
+PASS No timeline entry for about:blank
+FAIL Setting 'document.domain' does not effect same-origin checks assert_unreached: At most, 1 entry should be added to the performance timeline during a fetch. Reached unreachable code
+PASS 'iframe (Populate cache): The initial request populates the cache (if appropriate).
+PASS 'iframe (Potentially Cached): Immediately fetch the same URL, exercising the cache hit path (if any).
+PASS 'xmlhttprequest (Populate cache): The initial request populates the cache (if appropriate).
+PASS 'xmlhttprequest (Potentially Cached): Immediately fetch the same URL, exercising the cache hit path (if any).
+PASS 'script (Populate cache): The initial request populates the cache (if appropriate).
+PASS 'script (Potentially Cached): Immediately fetch the same URL, exercising the cache hit path (if any).
+PASS 'link (Populate cache): The initial request populates the cache (if appropriate).
+PASS 'link (Potentially Cached): Immediately fetch the same URL, exercising the cache hit path (if any).
+PASS 'iframe: 250ms delay before 'responseStart', another 250ms delay before 'responseEnd'.
+PASS 'xmlhttprequest: 250ms delay before 'responseStart', another 250ms delay before 'responseEnd'.
+PASS 'script: 250ms delay before 'responseStart', another 250ms delay before 'responseEnd'.
+PASS 'link: 250ms delay before 'responseStart', another 250ms delay before 'responseEnd'.
+PASS 'iframe (Redirected): 250ms delay before 'redirectEnd', another 250ms delay before 'responseStart'.
+PASS 'xmlhttprequest (Redirected): 250ms delay before 'redirectEnd', another 250ms delay before 'responseStart'.
+PASS 'script (Redirected): 250ms delay before 'redirectEnd', another 250ms delay before 'responseStart'.
+PASS 'link (Redirected): 250ms delay before 'redirectEnd', another 250ms delay before 'responseStart'.
+PASS 'iframe 250ms delay in headers does not affect responseStart'
+PASS 'xmlhttprequest 250ms delay in headers does not affect responseStart'
+PASS 'script 250ms delay in headers does not affect responseStart'
+PASS 'link 250ms delay in headers does not affect responseStart'
+PASS 'iframe responseStart uses 1XX (first) response timings'
+PASS 'xmlhttprequest responseStart uses 1XX (first) response timings'
+PASS 'script responseStart uses 1XX (first) response timings'
+PASS 'link responseStart uses 1XX (first) response timings'
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/resource-timing/resource-timing-level1.sub.html b/third_party/blink/web_tests/external/wpt/resource-timing/resource-timing.html
similarity index 67%
rename from third_party/blink/web_tests/external/wpt/resource-timing/resource-timing-level1.sub.html
rename to third_party/blink/web_tests/external/wpt/resource-timing/resource-timing.html
index 1555ef4..ad97044 100644
--- a/third_party/blink/web_tests/external/wpt/resource-timing/resource-timing-level1.sub.html
+++ b/third_party/blink/web_tests/external/wpt/resource-timing/resource-timing.html
@@ -14,13 +14,6 @@
 <body>
   <div id="log"></div>
   <pre id="output"></pre>
-  <script>
-      if (window.location.hostname != "{{domains[www]}}") {
-          let url = new URL(window.location.href);
-          url.hostname = "{{domains[www]}}";
-          window.location = url.href;
-      }
-  </script>
-  <script src="resource-timing-level1.js"></script>
+  <script src="resource-timing.js"></script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/external/wpt/resource-timing/resource-timing-level1.js b/third_party/blink/web_tests/external/wpt/resource-timing/resource-timing.js
similarity index 98%
rename from third_party/blink/web_tests/external/wpt/resource-timing/resource-timing-level1.js
rename to third_party/blink/web_tests/external/wpt/resource-timing/resource-timing.js
index 6c63eff7..8d97b2f 100644
--- a/third_party/blink/web_tests/external/wpt/resource-timing/resource-timing-level1.js
+++ b/third_party/blink/web_tests/external/wpt/resource-timing/resource-timing.js
@@ -60,13 +60,13 @@
                         initiateFetch(
                             test,
                             "iframe",
-                            canonicalize("resources/iframe-setdomain.sub.html"),
+                            canonicalize("iframe-setdomain.sub.html"),
                             function (initiator, entry) {
                                 // Ensure that the script inside the IFrame has successfully changed the IFrame's domain.
                                 assert_throws(
-                                    "SecurityError",
+                                    null,
                                     function () {
-                                        assert_not_equals(initiator.contentWindow.document, null);
+                                        assert_not_equals(frame.contentWindow.document, null);
                                     },
                                     "Test Error: IFrame is not recognized as cross-domain.");
 
@@ -74,7 +74,7 @@
                                 // verify that the following non-zero properties return their value.
                                 ["domainLookupStart", "domainLookupEnd", "connectStart", "connectEnd"]
                                     .forEach(function(property) {
-                                        assert_greater_than(entry[property], 0,
+                                        assert_greater_than(entry.connectEnd, 0,
                                             "Property should be non-zero because timing allow check ignores 'document.domain'.");
                                     });
                                 test.done();
@@ -324,7 +324,7 @@
             // Multiple browsers seem to cheat a bit and race img.onLoad and setting responseEnd.  Microsoft https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/2379187
             // Yield for 100ms to workaround a suspected race where window.onload fires before
             //     script visible side-effects from the wininet/urlmon thread have finished.
-            test.step_timeout(
+            window.setTimeout(
                 test.step_func(
                     function () {
                         performance
@@ -468,7 +468,7 @@
             when invoked. */
         function createOnloadCallbackFn(test, initiator, url, onloadCallback) {
             // Remember the number of entries on the timeline prior to initiating the fetch:
-            var beforeEntryCount = performance.getEntriesByType("resource").length;
+            var beforeEntryCount = performance.getEntries().length;
 
             return test.step_func(
                 function() {
@@ -480,7 +480,7 @@
                         }
                     }
 
-                    var entries = performance.getEntriesByType("resource");
+                    var entries = performance.getEntries();
                     var candidateEntry = entries[entries.length - 1];
 
                     switch (entries.length - beforeEntryCount)
diff --git a/third_party/blink/web_tests/external/wpt/resource-timing/resources/iframe-setdomain.sub.html b/third_party/blink/web_tests/external/wpt/resource-timing/resources/iframe-setdomain.sub.html
deleted file mode 100644
index 4a2f609..0000000
--- a/third_party/blink/web_tests/external/wpt/resource-timing/resources/iframe-setdomain.sub.html
+++ /dev/null
@@ -1,14 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <title>domain: {{domains[]}}</title>
-</head>
-<body>
-  <script>
-    // The purpose of this IFrame is to change the 'document.domain'
-    document.domain = "{{domains[]}}";
-  </script>
-  The resource-timings-level1.sub.html test loads this document into an IFrame to vet that setting
-  'document.domain' does not effect the timing allowed.
-</body>
-</html>
diff --git a/third_party/blink/web_tests/fast/backgrounds/size/contain-and-cover-zoomed-expected.png b/third_party/blink/web_tests/fast/backgrounds/size/contain-and-cover-zoomed-expected.png
index 2b9b4dec..2d2afc5 100644
--- a/third_party/blink/web_tests/fast/backgrounds/size/contain-and-cover-zoomed-expected.png
+++ b/third_party/blink/web_tests/fast/backgrounds/size/contain-and-cover-zoomed-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/fast/mediarecorder/MediaRecorder-disabled-tracks.html b/third_party/blink/web_tests/fast/mediarecorder/MediaRecorder-disabled-tracks.html
new file mode 100644
index 0000000..c73597d
--- /dev/null
+++ b/third_party/blink/web_tests/fast/mediarecorder/MediaRecorder-disabled-tracks.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<script src=../../resources/testharness.js></script>
+<script src=../../resources/testharnessreport.js></script>
+<script>
+
+// This test verifies that MediaStream with disabled tracks can be correctly
+// recorded. See crbug.com/878255 for more context.
+var makeAsyncTest = function(value, expected) {
+  var recorder;
+
+  async_test(function(test) {
+    const recorderOnDataAvailable = this.step_func(function(event) {
+      if (recorder.state != "recording")
+        return;
+
+      recorder.onstop = recorderOnStopExpected;
+      recorder.stop();
+    });
+
+    const recorderOnStopExpected = this.step_func_done();
+    const recorderOnStopUnexpected = test.unreached_func('Recording stopped.');
+    const recorderOnError = test.unreached_func('Recording error.');
+
+    const gotStream = this.step_func(function(stream) {
+      for (track in stream.getTracks())
+        track.enabled = true;
+
+      recorder = new MediaRecorder(stream);
+
+      assert_equals(recorder.state, "inactive");
+      recorder.ondataavailable = recorderOnDataAvailable;
+      recorder.onstop = recorderOnStopUnexpected;
+      recorder.onerror = recorderOnError;
+      recorder.start();
+
+      assert_equals(recorder.state, "recording");
+      recorder.requestData();
+    });
+
+    const onError = test.unreached_func('Error creating MediaStream.');
+    navigator.webkitGetUserMedia(value, gotStream, onError);
+  });
+};
+
+generate_tests(makeAsyncTest,
+               [["video-only",  {video: true,  audio: false}],
+                ["audio-only",  {video: false, audio: true}],
+                ["audio-video", {video: true,  audio: true}]]);
+
+</script>
+
diff --git a/third_party/blink/web_tests/fast/table/unbreakable-images-quirk.html b/third_party/blink/web_tests/fast/table/unbreakable-images-quirk.html
index 6533f20..632080f 100644
--- a/third_party/blink/web_tests/fast/table/unbreakable-images-quirk.html
+++ b/third_party/blink/web_tests/fast/table/unbreakable-images-quirk.html
@@ -1,5 +1,5 @@
 <style>
-    img { background-color: lightblue; width: 20px; height: 50px; }
+    img { background-color: lightblue; width: 20px; height: 40px; }
     table {width: 15px; background: silver; }
 </style>
 No line break
@@ -20,7 +20,7 @@
     </TR>
 </TABLE>
 <hr>
-Line break after the "a".
+Line break after "loremipsum".
 <TABLE>
     <TR> 
         <TD>
@@ -29,7 +29,7 @@
     </TR>
 </TABLE>
 <hr>
-Line break after the "a".
+Line break after the "loremipsum".
 <TABLE>
     <TR> 
         <TD>
@@ -38,7 +38,7 @@
     </TR>
 </TABLE>
 <hr>
-Line break after "wideword".
+Line break after the first image.
 <TABLE>
     <TR> 
         <TD>
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/backgrounds/size/contain-and-cover-zoomed-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/backgrounds/size/contain-and-cover-zoomed-expected.png
index dd47295..2d2afc5 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/backgrounds/size/contain-and-cover-zoomed-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/fast/backgrounds/size/contain-and-cover-zoomed-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/filters/filter-repaint-accelerated-child-with-filter-child-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/filters/filter-repaint-accelerated-child-with-filter-child-expected.txt
index 6303ad5..c6d341b 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/filters/filter-repaint-accelerated-child-with-filter-child-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/filters/filter-repaint-accelerated-child-with-filter-child-expected.txt
@@ -15,7 +15,7 @@
         {
           "object": "LayoutBlockFlow DIV id='resize' class='drop-shadow'",
           "rect": [0, 0, 160, 260],
-          "reason": "paint property change"
+          "reason": "geometry"
         }
       ],
       "transform": 1
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/filters/filter-repaint-accelerated-on-accelerated-filter-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/filters/filter-repaint-accelerated-on-accelerated-filter-expected.txt
index dfe5c8d..6be7d4c4 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/filters/filter-repaint-accelerated-on-accelerated-filter-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/filters/filter-repaint-accelerated-on-accelerated-filter-expected.txt
@@ -15,7 +15,7 @@
         {
           "object": "LayoutBlockFlow DIV id='resize' class='drop-shadow accelerated'",
           "rect": [0, 0, 160, 260],
-          "reason": "paint property change"
+          "reason": "geometry"
         }
       ],
       "transform": 1
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/virtual/gpu/fast/canvas/image-object-in-canvas-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/virtual/gpu/fast/canvas/image-object-in-canvas-expected.png
index 468e98f..1c05c36 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/virtual/gpu/fast/canvas/image-object-in-canvas-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/virtual/gpu/fast/canvas/image-object-in-canvas-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/http/tests/feature-policy/policy_iframes.php b/third_party/blink/web_tests/http/tests/feature-policy/policy_iframes.php
index b2471da3..daa8bad 100644
--- a/third_party/blink/web_tests/http/tests/feature-policy/policy_iframes.php
+++ b/third_party/blink/web_tests/http/tests/feature-policy/policy_iframes.php
@@ -26,11 +26,11 @@
 <iframe id="f1" src="../resources/dummy.html"></iframe>
 <iframe id="f2" src="http://localhost:8000/../resources/dummy.html"></iframe>
 <script>
-var local_iframe_policy = document.getElementById("f1").policy;
-var remote_iframe_policy = document.getElementById("f2").policy;
+var local_iframe_policy = document.getElementById("f1").featurePolicy;
+var remote_iframe_policy = document.getElementById("f2").featurePolicy;
 var local_src = "http://127.0.0.1:8000";
 var remote_src = "http://localhost:8000";
-// Tests for policy.allowsFeature().
+// Tests for featurePolicy.allowsFeature().
 // fullscreen should be allowed in both iframes on any origin.
 test(function() {
   assert_true(local_iframe_policy.allowsFeature("fullscreen"));
@@ -41,7 +41,7 @@
   assert_false(remote_iframe_policy.allowsFeature("fullscreen", local_src));
   assert_false(local_iframe_policy.allowsFeature("fullscreen", "http://www.example.com"));
   assert_false(remote_iframe_policy.allowsFeature("fullscreen", "http://www.example.com"));
-}, 'Test policy.allowsFeature() on fullscreen');
+}, 'Test featurePolicy.allowsFeature() on fullscreen');
 
 // Camera should be allowed in both iframes on src origin but no
 test(function() {
@@ -53,7 +53,7 @@
   assert_false(remote_iframe_policy.allowsFeature("camera", local_src));
   assert_false(local_iframe_policy.allowsFeature("camera", "http://www.example.com"));
   assert_false(remote_iframe_policy.allowsFeature("camera", "http://www.example.com"));
-}, 'Test policy.allowsFeature() on camera');
+}, 'Test featurePolicy.allowsFeature() on camera');
 // payment should only be allowed in the local iframe on src origin:
 test(function() {
   assert_true(local_iframe_policy.allowsFeature("payment"));
@@ -62,7 +62,7 @@
   assert_false(remote_iframe_policy.allowsFeature("payment"));
   assert_false(remote_iframe_policy.allowsFeature("payment", local_src));
   assert_false(remote_iframe_policy.allowsFeature("payment", remote_src));
-}, 'Test policy.allowsFeature() on locally allowed feature payment');
+}, 'Test featurePolicy.allowsFeature() on locally allowed feature payment');
 // badfeature and midi should be disallowed in both iframes:
 for (var feature of ["badfeature", "midi"]) {
   test(function() {
@@ -72,58 +72,58 @@
     assert_false(remote_iframe_policy.allowsFeature(feature));
     assert_false(remote_iframe_policy.allowsFeature(feature, local_src));
     assert_false(remote_iframe_policy.allowsFeature(feature, remote_src));
-  }, 'Test policy.allowsFeature() on disallowed feature ' + feature);
+  }, 'Test featurePolicy.allowsFeature() on disallowed feature ' + feature);
 }
 
-// Tests for policy.allowedFeatures().
+// Tests for featurePolicy.allowedFeatures().
 var allowed_local_iframe_features = local_iframe_policy.allowedFeatures();
 var allowed_remote_iframe_features = remote_iframe_policy.allowedFeatures();
 for (var feature of ["fullscreen", "camera"]) {
   test(function() {
     assert_true(allowed_local_iframe_features.includes(feature));
     assert_true(allowed_remote_iframe_features.includes(feature));
-  }, 'Test policy.allowedFeatures() include feature ' + feature);
+  }, 'Test featurePolicy.allowedFeatures() include feature ' + feature);
 }
 for (var feature of ["badfeature", "midi"]) {
   test(function() {
     assert_false(allowed_local_iframe_features.includes(feature));
     assert_false(allowed_remote_iframe_features.includes(feature));
-  }, 'Test policy.allowedFeatures() does not include disallowed feature ' +
+  }, 'Test featurePolicy.allowedFeatures() does not include disallowed feature ' +
     feature);
 }
 for (var feature of ["payment", "geolocation"]) {
 test(function() {
   assert_true(allowed_local_iframe_features.includes(feature));
   assert_false(allowed_remote_iframe_features.includes(feature));
-}, 'Test policy.allowedFeatures() locally include feature ' + feature +
+}, 'Test featurePolicy.allowedFeatures() locally include feature ' + feature +
   '  but not remotely ');
 }
 
-// Tests for policy.getAllowlistForFeature().
+// Tests for featurePolicy.getAllowlistForFeature().
 test(function() {
   assert_array_equals(
     local_iframe_policy.getAllowlistForFeature("fullscreen"), [local_src]);
   assert_array_equals(
     remote_iframe_policy.getAllowlistForFeature("fullscreen"), [remote_src]);
-}, 'policy.getAllowlistForFeature(): fullscreen is allowed in both iframes');
+}, 'featurePolicy.getAllowlistForFeature(): fullscreen is allowed in both iframes');
 test(function() {
   assert_array_equals(
     local_iframe_policy.getAllowlistForFeature("payment"), [local_src]);
   assert_array_equals(
     remote_iframe_policy.getAllowlistForFeature("payment"), []);
-}, 'policy.getAllowlistForFeature(): payment is allowed only in local iframe');
+}, 'featurePolicy.getAllowlistForFeature(): payment is allowed only in local iframe');
 test(function() {
   assert_array_equals(
     local_iframe_policy.getAllowlistForFeature("geolocation"), [local_src]);
   assert_array_equals(
     remote_iframe_policy.getAllowlistForFeature("geolocation"), []);
-}, 'policy.getAllowlistForFeature(): geolocation is allowed only in local iframe');
+}, 'featurePolicy.getAllowlistForFeature(): geolocation is allowed only in local iframe');
 test(function() {
   assert_array_equals(
     local_iframe_policy.getAllowlistForFeature("midi"), []);
   assert_array_equals(
     remote_iframe_policy.getAllowlistForFeature("midi"), []);
-}, 'policy.getAllowlistForFeature(): midi is disallowed in both iframe');
+}, 'featurePolicy.getAllowlistForFeature(): midi is disallowed in both iframe');
 
 // Dynamically update iframes policy.
 document.getElementById("f1").allow = "fullscreen 'none'; payment 'src'; midi 'src'; geolocation 'none'; camera 'src' 'self' https://www.example.com https://www.example.net";
@@ -132,12 +132,12 @@
   assert_array_equals(
     local_iframe_policy.getAllowlistForFeature("fullscreen"), []);
   assert_array_equals(
-    document.getElementById("f1").policy.getAllowlistForFeature("fullscreen"),
+    document.getElementById("f1").featurePolicy.getAllowlistForFeature("fullscreen"),
     []);
   assert_array_equals(
     remote_iframe_policy.getAllowlistForFeature("fullscreen"), []);
   assert_array_equals(
-    document.getElementById("f2").policy.getAllowlistForFeature("fullscreen"),
+    document.getElementById("f2").featurePolicy.getAllowlistForFeature("fullscreen"),
     []);
 }, 'Dynamically redefine allow: fullscreen is disallowed in both iframes');
 
@@ -145,12 +145,12 @@
   assert_array_equals(
     local_iframe_policy.getAllowlistForFeature("payment"), [local_src]);
   assert_array_equals(
-    document.getElementById("f1").policy.getAllowlistForFeature("payment"),
+    document.getElementById("f1").featurePolicy.getAllowlistForFeature("payment"),
     [local_src]);
   assert_array_equals(
     remote_iframe_policy.getAllowlistForFeature("payment"), [remote_src]);
   assert_array_equals(
-    document.getElementById("f2").policy.getAllowlistForFeature("payment"),
+    document.getElementById("f2").featurePolicy.getAllowlistForFeature("payment"),
     [remote_src]);
 }, 'Dynamically redefine allow: payment is allowed in both iframes');
 
diff --git a/third_party/blink/web_tests/http/tests/feature-policy/policy_main_document.php b/third_party/blink/web_tests/http/tests/feature-policy/policy_main_document.php
index 35ff1f8..8698d23 100644
--- a/third_party/blink/web_tests/http/tests/feature-policy/policy_main_document.php
+++ b/third_party/blink/web_tests/http/tests/feature-policy/policy_main_document.php
@@ -24,45 +24,45 @@
 <script src="../../resources/testharness.js"></script>
 <script src="../../resources/testharnessreport.js"></script>
 <script>
-var policy_main = document.policy;
+var policy_main = document.featurePolicy;
 var allowed_features = ["fullscreen", "payment", "camera"];
 var disallowed_features = ["badfeature", "midi"];
 
-// Tests for policy.allowsFeature().
+// Tests for featurePolicy.allowsFeature().
 for (var feature of allowed_features) {
   test(function() {
     assert_true(policy_main.allowsFeature(feature));
     assert_true(policy_main.allowsFeature(feature, "http://127.0.0.1:8000"));
-  }, 'Test policy.allowsFeature() on feature ' + feature);
+  }, 'Test featurePolicy.allowsFeature() on feature ' + feature);
 }
 
 test(function() {
   assert_true(policy_main.allowsFeature("camera", "https://www.example.com"));
   assert_true(policy_main.allowsFeature("camera", "https://www.example.net"));
-}, 'Test policy.allowsFeature() for camera');
+}, 'Test featurePolicy.allowsFeature() for camera');
 
 for (var feature of disallowed_features) {
   test(function() {
     assert_false(policy_main.allowsFeature(feature));
     assert_false(policy_main.allowsFeature(feature, "http://127.0.0.1:8000"));
-  }, 'Test policy.allowsFeature() on disallowed feature ' + feature);
+  }, 'Test featurePolicy.allowsFeature() on disallowed feature ' + feature);
 }
 
-// Tests for policy.allowedFeatures().
+// Tests for featurePolicy.allowedFeatures().
 var allowed_features_main = policy_main.allowedFeatures();
 for (var feature of allowed_features) {
   test(function() {
     assert_true(allowed_features_main.includes(feature));
-  }, 'Test policy.allowedFeatures() include feature ' + feature);
+  }, 'Test featurePolicy.allowedFeatures() include feature ' + feature);
 }
 for (var feature of disallowed_features) {
   test(function() {
     assert_false(allowed_features_main.includes(feature));
-  }, 'Test policy.allowedFeatures() does not include disallowed feature ' +
+  }, 'Test featurePolicy.allowedFeatures() does not include disallowed feature ' +
     feature);
 }
 
-// Tests for policy.getAllowlistForFeature().
+// Tests for featurePolicy.getAllowlistForFeature().
 assert_array_equals(
   policy_main.getAllowlistForFeature("fullscreen"), ["*"],
   "fullscreen is allowed for all in main frame");
diff --git a/third_party/blink/web_tests/images/color-profile-background-image-cover-expected.png b/third_party/blink/web_tests/images/color-profile-background-image-cover-expected.png
index 248c3a2..d7f94dcf 100644
--- a/third_party/blink/web_tests/images/color-profile-background-image-cover-expected.png
+++ b/third_party/blink/web_tests/images/color-profile-background-image-cover-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/paint/invalidation/filters/filter-repaint-accelerated-child-with-filter-child-expected.txt b/third_party/blink/web_tests/paint/invalidation/filters/filter-repaint-accelerated-child-with-filter-child-expected.txt
index fd0f932..19f391b 100644
--- a/third_party/blink/web_tests/paint/invalidation/filters/filter-repaint-accelerated-child-with-filter-child-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/filters/filter-repaint-accelerated-child-with-filter-child-expected.txt
@@ -29,12 +29,7 @@
         {
           "object": "LayoutBlockFlow DIV id='resize' class='drop-shadow'",
           "rect": [0, 0, 212, 257],
-          "reason": "paint property change"
-        },
-        {
-          "object": "LayoutBlockFlow DIV id='resize' class='drop-shadow'",
-          "rect": [0, 0, 158, 257],
-          "reason": "paint property change"
+          "reason": "geometry"
         }
       ],
       "transform": 1
diff --git a/third_party/blink/web_tests/platform/linux/fast/table/unbreakable-images-quirk-expected.png b/third_party/blink/web_tests/platform/linux/fast/table/unbreakable-images-quirk-expected.png
index 59f9650f..48c8cbd8 100644
--- a/third_party/blink/web_tests/platform/linux/fast/table/unbreakable-images-quirk-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/table/unbreakable-images-quirk-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/tables/mozilla/bugs/bug101674-expected.png b/third_party/blink/web_tests/platform/linux/tables/mozilla/bugs/bug101674-expected.png
index ce1c8a9..7724f72 100644
--- a/third_party/blink/web_tests/platform/linux/tables/mozilla/bugs/bug101674-expected.png
+++ b/third_party/blink/web_tests/platform/linux/tables/mozilla/bugs/bug101674-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/table/unbreakable-images-quirk-expected.png b/third_party/blink/web_tests/platform/mac/fast/table/unbreakable-images-quirk-expected.png
index 1addbd19..00bb040 100644
--- a/third_party/blink/web_tests/platform/mac/fast/table/unbreakable-images-quirk-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/table/unbreakable-images-quirk-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/tables/mozilla/bugs/bug2973-expected.png b/third_party/blink/web_tests/platform/mac/tables/mozilla/bugs/bug2973-expected.png
index 43e43b4..6d87b358 100644
--- a/third_party/blink/web_tests/platform/mac/tables/mozilla/bugs/bug2973-expected.png
+++ b/third_party/blink/web_tests/platform/mac/tables/mozilla/bugs/bug2973-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/table/unbreakable-images-quirk-expected.png b/third_party/blink/web_tests/platform/win/fast/table/unbreakable-images-quirk-expected.png
index 988137f..f18ac54 100644
--- a/third_party/blink/web_tests/platform/win/fast/table/unbreakable-images-quirk-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/table/unbreakable-images-quirk-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/tables/mozilla/bugs/bug101674-expected.png b/third_party/blink/web_tests/platform/win/tables/mozilla/bugs/bug101674-expected.png
index c74d7b86..854046e 100644
--- a/third_party/blink/web_tests/platform/win/tables/mozilla/bugs/bug101674-expected.png
+++ b/third_party/blink/web_tests/platform/win/tables/mozilla/bugs/bug101674-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/tables/mozilla/bugs/bug2973-expected.png b/third_party/blink/web_tests/platform/win/tables/mozilla/bugs/bug2973-expected.png
index 200bf2b..a5f0b1b 100644
--- a/third_party/blink/web_tests/platform/win/tables/mozilla/bugs/bug2973-expected.png
+++ b/third_party/blink/web_tests/platform/win/tables/mozilla/bugs/bug2973-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/exotic-color-space/images/color-profile-background-image-cover-expected.png b/third_party/blink/web_tests/virtual/exotic-color-space/images/color-profile-background-image-cover-expected.png
index 08e5424..e96c44f 100644
--- a/third_party/blink/web_tests/virtual/exotic-color-space/images/color-profile-background-image-cover-expected.png
+++ b/third_party/blink/web_tests/virtual/exotic-color-space/images/color-profile-background-image-cover-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-background-image-cover-expected.png b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-background-image-cover-expected.png
index 015149c..bd4d0a92 100644
--- a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-background-image-cover-expected.png
+++ b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-background-image-cover-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt b/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt
index 8f62293..6ace489 100644
--- a/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt
@@ -625,6 +625,7 @@
     property contentDocument
     property contentWindow
     property csp
+    property featurePolicy
     property frameBorder
     property getSVGDocument
     property height
@@ -632,7 +633,6 @@
     property marginHeight
     property marginWidth
     property name
-    property policy
     property referrerPolicy
     property sandbox
     property scrolling
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index 65e6db0..f978ba8 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -1547,6 +1547,7 @@
     getter documentURI
     getter domain
     getter embeds
+    getter featurePolicy
     getter fgColor
     getter firstElementChild
     getter fonts
@@ -1664,7 +1665,6 @@
     getter pictureInPictureEnabled
     getter plugins
     getter pointerLockElement
-    getter policy
     getter readyState
     getter referrer
     getter rootElement
@@ -3011,13 +3011,13 @@
     getter contentDocument
     getter contentWindow
     getter csp
+    getter featurePolicy
     getter frameBorder
     getter height
     getter longDesc
     getter marginHeight
     getter marginWidth
     getter name
-    getter policy
     getter referrerPolicy
     getter sandbox
     getter scrolling
diff --git a/third_party/closure_compiler/externs/activity_log_private.js b/third_party/closure_compiler/externs/activity_log_private.js
index 27299bd..e591ec85 100644
--- a/third_party/closure_compiler/externs/activity_log_private.js
+++ b/third_party/closure_compiler/externs/activity_log_private.js
@@ -115,6 +115,14 @@
 chrome.activityLogPrivate.deleteActivities = function(activityIds) {};
 
 /**
+ * Deletes activities in the ActivityLog database specified by the extension ID.
+ * @param {string} extensionId Erases only the activities from the extension
+ *     specified.
+ * @param {function():void=} callback
+ */
+chrome.activityLogPrivate.deleteActivitiesByExtension = function(extensionId, callback) {};
+
+/**
  * Deletes the entire ActivityLog database.
  */
 chrome.activityLogPrivate.deleteDatabase = function() {};
diff --git a/third_party/feed/README.chromium b/third_party/feed/README.chromium
index 6bb040c..35bba1a 100644
--- a/third_party/feed/README.chromium
+++ b/third_party/feed/README.chromium
@@ -2,7 +2,7 @@
 Short name: feed
 URL: https://chromium.googlesource.com/feed
 Version: 0
-Revision: 84617ef81ded68396acf495e8392266950972223
+Revision: d3514e6cb9ac00229a76e2aedfa367fa71fa4bd2
 License: Apache 2.0
 License File: LICENSE
 Security Critical: yes
diff --git a/third_party/gvr-android-sdk/BUILD.gn b/third_party/gvr-android-sdk/BUILD.gn
index 1b7b72b..e1156c03 100644
--- a/third_party/gvr-android-sdk/BUILD.gn
+++ b/third_party/gvr-android-sdk/BUILD.gn
@@ -114,12 +114,14 @@
       "//clank/third_party/gvr_shim",
     ]
   } else {
-    if (use_custom_libcxx) {
-      library = "//third_party/gvr-android-sdk/libgvr_shim_static_custom_libcxx_${current_cpu}.a"
+    if (!use_custom_libcxx) {
+      cxx_abi_version = "ndk1"
+    } else if (is_component_build) {
+      cxx_abi_version = "Cr"
     } else {
-      library =
-          "//third_party/gvr-android-sdk/libgvr_shim_static_${current_cpu}.a"
+      cxx_abi_version = "1"
     }
+    library = "//third_party/gvr-android-sdk/libgvr_shim_static_${current_cpu}_${cxx_abi_version}.a"
     libs += [ library ]
 
     if (is_component_build) {
diff --git a/third_party/gvr-android-sdk/libgvr_shim_static_arm.a.sha1 b/third_party/gvr-android-sdk/libgvr_shim_static_arm.a.sha1
deleted file mode 100644
index 0fa1089e..0000000
--- a/third_party/gvr-android-sdk/libgvr_shim_static_arm.a.sha1
+++ /dev/null
@@ -1 +0,0 @@
-b93dc26168196ca0eb2cb7d294b3c0aab0965083
\ No newline at end of file
diff --git a/third_party/gvr-android-sdk/libgvr_shim_static_arm64.a.sha1 b/third_party/gvr-android-sdk/libgvr_shim_static_arm64.a.sha1
deleted file mode 100644
index 16696aaf..0000000
--- a/third_party/gvr-android-sdk/libgvr_shim_static_arm64.a.sha1
+++ /dev/null
@@ -1 +0,0 @@
-e8ab02f4481e65092cf5c032e036a6fe31c92071
\ No newline at end of file
diff --git a/third_party/gvr-android-sdk/libgvr_shim_static_arm64_1.a.sha1 b/third_party/gvr-android-sdk/libgvr_shim_static_arm64_1.a.sha1
new file mode 100644
index 0000000..ee5e0aa
--- /dev/null
+++ b/third_party/gvr-android-sdk/libgvr_shim_static_arm64_1.a.sha1
@@ -0,0 +1 @@
+e3a8233332ab7f448613c5d3c6c78e5e698505e5
\ No newline at end of file
diff --git a/third_party/gvr-android-sdk/libgvr_shim_static_arm64_Cr.a.sha1 b/third_party/gvr-android-sdk/libgvr_shim_static_arm64_Cr.a.sha1
new file mode 100644
index 0000000..6ee4f74
--- /dev/null
+++ b/third_party/gvr-android-sdk/libgvr_shim_static_arm64_Cr.a.sha1
@@ -0,0 +1 @@
+dceee76743308d071abb90cf0157b3a9620badfa
\ No newline at end of file
diff --git a/third_party/gvr-android-sdk/libgvr_shim_static_arm64_ndk1.a.sha1 b/third_party/gvr-android-sdk/libgvr_shim_static_arm64_ndk1.a.sha1
new file mode 100644
index 0000000..d4ad04c4
--- /dev/null
+++ b/third_party/gvr-android-sdk/libgvr_shim_static_arm64_ndk1.a.sha1
@@ -0,0 +1 @@
+3dde388b26d3659b4f99d84c9a358fc78f37f5b8
\ No newline at end of file
diff --git a/third_party/gvr-android-sdk/libgvr_shim_static_arm_1.a.sha1 b/third_party/gvr-android-sdk/libgvr_shim_static_arm_1.a.sha1
new file mode 100644
index 0000000..58d381d
--- /dev/null
+++ b/third_party/gvr-android-sdk/libgvr_shim_static_arm_1.a.sha1
@@ -0,0 +1 @@
+8d19ef4983bbfe1b9b679f418dee80678a7d7652
\ No newline at end of file
diff --git a/third_party/gvr-android-sdk/libgvr_shim_static_arm_Cr.a.sha1 b/third_party/gvr-android-sdk/libgvr_shim_static_arm_Cr.a.sha1
new file mode 100644
index 0000000..0c8b7e64
--- /dev/null
+++ b/third_party/gvr-android-sdk/libgvr_shim_static_arm_Cr.a.sha1
@@ -0,0 +1 @@
+ee194b9c00e14ae07fb0e39add6522a6637b742f
\ No newline at end of file
diff --git a/third_party/gvr-android-sdk/libgvr_shim_static_arm_ndk1.a.sha1 b/third_party/gvr-android-sdk/libgvr_shim_static_arm_ndk1.a.sha1
new file mode 100644
index 0000000..0b49459
--- /dev/null
+++ b/third_party/gvr-android-sdk/libgvr_shim_static_arm_ndk1.a.sha1
@@ -0,0 +1 @@
+136cb96d7936fe9ad491046fd7c0ddf541ad2765
\ No newline at end of file
diff --git a/third_party/gvr-android-sdk/libgvr_shim_static_custom_libcxx_arm.a.sha1 b/third_party/gvr-android-sdk/libgvr_shim_static_custom_libcxx_arm.a.sha1
deleted file mode 100644
index 39c18f4e..0000000
--- a/third_party/gvr-android-sdk/libgvr_shim_static_custom_libcxx_arm.a.sha1
+++ /dev/null
@@ -1 +0,0 @@
-e6343e9152930dcd4d9eb6eed2c993658a9e997d
\ No newline at end of file
diff --git a/third_party/gvr-android-sdk/libgvr_shim_static_custom_libcxx_arm64.a.sha1 b/third_party/gvr-android-sdk/libgvr_shim_static_custom_libcxx_arm64.a.sha1
deleted file mode 100644
index 6cd20c3f..0000000
--- a/third_party/gvr-android-sdk/libgvr_shim_static_custom_libcxx_arm64.a.sha1
+++ /dev/null
@@ -1 +0,0 @@
-9e1297a47b30e28d12113fb569f0272f43cb7607
\ No newline at end of file
diff --git a/third_party/libaddressinput/chromium/addressinput_util.cc b/third_party/libaddressinput/chromium/addressinput_util.cc
index ca10d41d..3b87787d 100644
--- a/third_party/libaddressinput/chromium/addressinput_util.cc
+++ b/third_party/libaddressinput/chromium/addressinput_util.cc
@@ -10,6 +10,7 @@
 
 #include "base/logging.h"
 #include "base/macros.h"
+#include "base/stl_util.h"
 #include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_data.h"
 #include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_metadata.h"
 
@@ -63,7 +64,7 @@
       ::i18n::addressinput::RECIPIENT
   };
 
-  for (size_t i = 0; i < arraysize(kFields); ++i) {
+  for (size_t i = 0; i < base::size(kFields); ++i) {
     AddressField field = kFields[i];
     if (address_to_check.IsFieldEmpty(field) &&
         IsFieldRequired(field, address_to_check.region_code) &&
diff --git a/third_party/unrar/BUILD.gn b/third_party/unrar/BUILD.gn
index c3594cf..65bda51 100644
--- a/third_party/unrar/BUILD.gn
+++ b/third_party/unrar/BUILD.gn
@@ -67,6 +67,7 @@
       "LARGEFILE_SOURCE",
       "RAR_SMP",
       "SILENT",
+      "NOVOLUME",
 
       # The following is set to disable certain macro definitions in the unrar
       # source code.
diff --git a/tools/android/roll/android_deps/build.gradle b/tools/android/roll/android_deps/build.gradle
index 24c14bd..89b2daab 100644
--- a/tools/android/roll/android_deps/build.gradle
+++ b/tools/android/roll/android_deps/build.gradle
@@ -70,8 +70,9 @@
     annotationProcessor "com.google.dagger:dagger-compiler:${daggerVersion}"
 
     // JavaPoet
-    // Matches version depended on by Dagger.
-    compile "com.squareup:javapoet:1.11.0"
+    // Matches version depended on by Dagger. Uses annotationProcessor so that
+    // it doesn't get supports_android, which is given by compile.
+    annotationProcessor "com.squareup:javapoet:1.11.0"
 
     compile "com.google.protobuf:protobuf-lite:3.0.1"
 
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index f2d34736..4f4c81a 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -17590,6 +17590,7 @@
   <int value="1304" label="ACCESSIBILITY_PRIVATE_SETSWITCHACCESSMENUSTATE"/>
   <int value="1305" label="AUTOTESTPRIVATE_SENDASSISTANTTEXTQUERY"/>
   <int value="1306" label="AUTOTESTPRIVATE_SETCROSTINIAPPSCALED"/>
+  <int value="1307" label="ACTIVITYLOGPRIVATE_DELETEACTIVITIESBYEXTENSION"/>
 </enum>
 
 <enum name="ExtensionIconState">
@@ -48256,9 +48257,10 @@
   <int value="12" label="Sign-in failed"/>
   <int value="13" label="Pairing added"/>
   <int value="14" label="No screenlock state handler"/>
-  <int value="15" label="RSSI too low"/>
+  <int value="15" label="Phone locked and RSSI too low"/>
   <int value="16" label="Forced Reauthentication"/>
   <int value="17" label="Required for login"/>
+  <int value="18" label="Phone not lockable"/>
 </enum>
 
 <enum name="SmartLockAuthMethodChoice">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index fc00713..2442d75 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -7937,7 +7937,7 @@
   </summary>
 </histogram>
 
-<histogram name="Autofill.Query.BackoffDelay" units="ms" expires_after="M73">
+<histogram name="Autofill.Query.BackoffDelay" units="ms">
   <owner>rogerm@chromium.org</owner>
   <summary>
     The delay of a network request for a query due to exponential backoff.
@@ -7963,7 +7963,7 @@
 </histogram>
 
 <histogram name="Autofill.Query.HttpResponseOrErrorCode"
-    enum="CombinedHttpResponseAndNetErrorCode" expires_after="M73">
+    enum="CombinedHttpResponseAndNetErrorCode">
   <owner>rogerm@chromium.org</owner>
   <summary>
     The http response code or net error code returned on a query.
@@ -7975,7 +7975,7 @@
   <summary>The HTTP method used to query the autofill server.</summary>
 </histogram>
 
-<histogram name="Autofill.Query.RequestDuration" units="ms" expires_after="M73">
+<histogram name="Autofill.Query.RequestDuration" units="ms">
   <owner>rogerm@chromium.org</owner>
   <summary>The duration of a network request for a query.</summary>
 </histogram>
@@ -8347,7 +8347,10 @@
   </summary>
 </histogram>
 
-<histogram name="Autofill.Unknown.BackoffDelay" units="ms" expires_after="M73">
+<histogram name="Autofill.Unknown.BackoffDelay" units="ms">
+  <obsolete>
+    Deprecated as of 01/2019, as it never occurs.
+  </obsolete>
   <owner>rogerm@chromium.org</owner>
   <summary>
     The delay of a network request for which the download manager seems to have
@@ -8357,6 +8360,9 @@
 </histogram>
 
 <histogram name="Autofill.Unknown.FailingPayloadSize" units="bytes">
+  <obsolete>
+    Deprecated as of 01/2019, as it never occurs.
+  </obsolete>
   <owner>rogerm@chromium.org</owner>
   <summary>
     The number of bytes that were sent in a request for which the download
@@ -8377,7 +8383,10 @@
 </histogram>
 
 <histogram name="Autofill.Unknown.HttpResponseOrErrorCode"
-    enum="CombinedHttpResponseAndNetErrorCode" expires_after="M73">
+    enum="CombinedHttpResponseAndNetErrorCode">
+  <obsolete>
+    Deprecated as of 01/2019, as it never occurs.
+  </obsolete>
   <owner>rogerm@chromium.org</owner>
   <summary>
     The http response code or net error code returned on a request for which the
@@ -8386,8 +8395,10 @@
   </summary>
 </histogram>
 
-<histogram name="Autofill.Unknown.RequestDuration" units="ms"
-    expires_after="M73">
+<histogram name="Autofill.Unknown.RequestDuration" units="ms">
+  <obsolete>
+    Deprecated as of 01/2019, as it never occurs.
+  </obsolete>
   <owner>rogerm@chromium.org</owner>
   <summary>
     The duration of a network request for which the download manager seems to
diff --git a/tools/perf/core/stacktrace_unittest.py b/tools/perf/core/stacktrace_unittest.py
index 48a30b85d..258e0b1 100644
--- a/tools/perf/core/stacktrace_unittest.py
+++ b/tools/perf/core/stacktrace_unittest.py
@@ -22,8 +22,8 @@
     self.assertTrue(c.exception.is_valid_dump)
 
   # Stack traces aren't working on Android yet.
-  @decorators.Enabled('mac', 'linux')
-  @decorators.Disabled('snowleopard')
+  # Disabled on mac, flaky: https://crbug.com/820282.
+  @decorators.Enabled('linux')
   def testCrashSymbols(self):
     with self.assertRaises(exceptions.DevtoolsTargetCrashException) as c:
       self._tab.Navigate('chrome://crash', timeout=5)
diff --git a/ui/accessibility/ax_node.cc b/ui/accessibility/ax_node.cc
index d238ddc7..13f5765 100644
--- a/ui/accessibility/ax_node.cc
+++ b/ui/accessibility/ax_node.cc
@@ -344,10 +344,17 @@
 }
 
 int32_t AXNode::GetTableRowRowIndex() const {
-  // TODO(dmazzoni): Compute from AXTableInfo. http://crbug.com/832289
-  int32_t row_index = 0;
-  GetIntAttribute(ax::mojom::IntAttribute::kTableRowIndex, &row_index);
-  return row_index;
+  if (!IsTableRow())
+    return 0;
+
+  AXTableInfo* table_info = GetAncestorTableInfo();
+  if (!table_info)
+    return 0;
+
+  const auto& iter = table_info->row_id_to_index.find(id());
+  if (iter != table_info->row_id_to_index.end())
+    return iter->second;
+  return 0;
 }
 
 //
diff --git a/ui/accessibility/ax_table_info.cc b/ui/accessibility/ax_table_info.cc
index 1986ac75..aa559a202 100644
--- a/ui/accessibility/ax_table_info.cc
+++ b/ui/accessibility/ax_table_info.cc
@@ -152,14 +152,22 @@
   // index in the accessibility tree if legal, but replacing it with
   // valid table coordinates otherwise.
   int32_t cell_index = 0;
-  int32_t current_row_index = 0;
   int32_t current_aria_row_index = 1;
+  int32_t previous_row_index = -1;
   for (size_t i = 0; i < cell_nodes_per_row.size(); i++) {
     auto& cell_nodes_in_this_row = cell_nodes_per_row[i];
     AXNode* row_node = row_nodes[i];
     bool is_first_cell_in_row = true;
     int32_t current_col_index = 0;
     int32_t current_aria_col_index = 1;
+
+    // Make sure the row index is always at least as high as the one reported by
+    // Blink.
+    row_id_to_index[row_node->id()] =
+        std::max(previous_row_index + 1,
+                 row_node->GetIntAttribute(IntAttribute::kTableRowIndex));
+    int32_t* current_row_index = &row_id_to_index[row_node->id()];
+
     for (AXNode* cell : cell_nodes_in_this_row) {
       // Fill in basic info in CellData.
       CellData cell_data;
@@ -194,10 +202,13 @@
         is_first_cell_in_row = false;
 
         // If it's the first cell in the row, ensure the row index is
-        // incrementing. The rest of the cells in this row will be force to
-        // have the same row index.
-        cell_data.row_index = std::max(cell_data.row_index, current_row_index);
-        current_row_index = cell_data.row_index;
+        // incrementing. The rest of the cells in this row are forced to have
+        // the same row index.
+        if (cell_data.row_index > *current_row_index) {
+          *current_row_index = cell_data.row_index;
+        } else {
+          cell_data.row_index = *current_row_index;
+        }
 
         // The starting ARIA row and column index might be specified in
         // the row node, we should check there.
@@ -215,7 +226,7 @@
       } else {
         // Don't allow the row index to change after the beginning
         // of a row.
-        cell_data.row_index = current_row_index;
+        cell_data.row_index = *current_row_index;
         cell_data.aria_row_index = current_aria_row_index;
       }
 
@@ -244,11 +255,11 @@
       cell_data_vector.push_back(cell_data);
     }
 
-    // At the end of each row, increment |current_row_index| to reflect the next
-    // available index after this row. The next row index must be at least this
-    // large. Same for the current ARIA row index.
-    current_row_index++;
+    // At the end of each row, increment |current_aria_row_index| to reflect the
+    // next available index after this row. The next row index must be at least
+    // this large. Also update |previous_row_index|.
     current_aria_row_index++;
+    previous_row_index = *current_row_index;
   }
 }
 
diff --git a/ui/accessibility/ax_table_info.h b/ui/accessibility/ax_table_info.h
index 39c912c..167c5818 100644
--- a/ui/accessibility/ax_table_info.h
+++ b/ui/accessibility/ax_table_info.h
@@ -82,6 +82,9 @@
   // Map from each cell's node ID to its index in unique_cell_ids.
   base::hash_map<int32_t, int32_t> cell_id_to_index;
 
+  // Map from each row's node ID to its row index.
+  base::hash_map<int32_t, int32_t> row_id_to_index;
+
   // The ARIA row count and column count, if any ARIA table or grid
   // attributes are used in the table at all.
   int32_t aria_row_count = 0;
diff --git a/ui/android/BUILD.gn b/ui/android/BUILD.gn
index ab96092..26921a0 100644
--- a/ui/android/BUILD.gn
+++ b/ui/android/BUILD.gn
@@ -289,6 +289,12 @@
     "java/src/org/chromium/ui/gl/SurfaceTextureListener.java",
     "java/src/org/chromium/ui/gl/SurfaceTexturePlatformWrapper.java",
     "java/src/org/chromium/ui/interpolators/BakedBezierInterpolator.java",
+    "java/src/org/chromium/ui/modaldialog/DialogDismissalCause.java",
+    "java/src/org/chromium/ui/modaldialog/ModalDialogManager.java",
+    "java/src/org/chromium/ui/modaldialog/ModalDialogProperties.java",
+    "java/src/org/chromium/ui/modelutil/PropertyKey.java",
+    "java/src/org/chromium/ui/modelutil/PropertyModel.java",
+    "java/src/org/chromium/ui/modelutil/PropertyObservable.java",
     "java/src/org/chromium/ui/resources/HandleViewResources.java",
     "java/src/org/chromium/ui/resources/LayoutResource.java",
     "java/src/org/chromium/ui/resources/Resource.java",
@@ -367,6 +373,7 @@
     "junit/src/org/chromium/ui/base/LocalizationUtilsTest.java",
     "junit/src/org/chromium/ui/base/SelectFileDialogTest.java",
     "junit/src/org/chromium/ui/drawable/StateListDrawableBuilderTest.java",
+    "junit/src/org/chromium/ui/modaldialog/ModalDialogManagerTest.java",
     "junit/src/org/chromium/ui/text/SpanApplierTest.java",
     "junit/src/org/chromium/ui/shadows/ShadowAsyncLayoutInflater.java",
     "junit/src/org/chromium/ui/shadows/ShadowAppCompatResources.java",
diff --git a/ui/android/java/res/font/accent_font.xml b/ui/android/java/res/font/accent_font.xml
new file mode 100644
index 0000000..dee6183
--- /dev/null
+++ b/ui/android/java/res/font/accent_font.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2019 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<font-family />
diff --git a/ui/android/java/res/values-v17/styles.xml b/ui/android/java/res/values-v17/styles.xml
index 4743f2ee8..361713e 100644
--- a/ui/android/java/res/values-v17/styles.xml
+++ b/ui/android/java/res/values-v17/styles.xml
@@ -88,12 +88,15 @@
         <item name="android:fontFamily">sans-serif</item>
         <item name="android:textStyle">bold</item>
     </style>
+    <!-- This style is overridden downstream to set accent_font_medium as the font family. -->
+    <style name="TextAppearance.ButtonMediumStyle" parent="TextAppearance.RobotoMediumStyle" />
 
     <!-- Black Text Styles -->
     <!-- TODO(huayinz): remove tools:ignore once these text styles are all used in ui/android -->
     <style name="TextAppearance.BlackHeadline" tools:ignore="UnusedResources">
         <item name="android:textColor">@color/default_text_color_list</item>
         <item name="android:textSize">@dimen/headline_size</item>
+        <item name="android:fontFamily">@font/accent_font</item>
     </style>
     <style name="TextAppearance.BlackTitle1" tools:ignore="UnusedResources">
         <item name="android:textColor">@color/default_text_color_list</item>
@@ -139,7 +142,7 @@
         <item name="android:textColor">@color/default_text_color_secondary_list</item>
         <item name="android:textSize">@dimen/text_size_small</item>
     </style>
-    <style name="TextAppearance.BlackButtonText" parent="TextAppearance.RobotoMediumStyle" tools:ignore="UnusedResources">
+    <style name="TextAppearance.BlackButtonText" parent="TextAppearance.ButtonMediumStyle" tools:ignore="UnusedResources">
         <item name="android:textColor">@color/default_text_color_secondary_list</item>
         <item name="android:textSize">@dimen/text_size_medium</item>
     </style>
@@ -153,6 +156,7 @@
     <style name="TextAppearance.WhiteHeadline" tools:ignore="UnusedResources">
         <item name="android:textColor">@android:color/white</item>
         <item name="android:textSize">@dimen/headline_size</item>
+        <item name="android:fontFamily">@font/accent_font</item>
     </style>
     <style name="TextAppearance.WhiteBodyIncognito" tools:ignore="UnusedResources">
         <item name="android:textColor">@color/white_alpha_70</item>
@@ -170,7 +174,10 @@
         <item name="android:textColor">@android:color/white</item>
         <item name="android:textSize">@dimen/text_size_medium</item>
     </style>
-    <style name="TextAppearance.WhiteButtonText" parent="TextAppearance.WhiteTitle2" />
+    <style name="TextAppearance.WhiteButtonText" parent="TextAppearance.ButtonMediumStyle">
+        <item name="android:textColor">@android:color/white</item>
+        <item name="android:textSize">@dimen/text_size_medium</item>
+    </style>
     <style name="TextAppearance.WhiteLink" tools:ignore="UnusedResources">
         <item name="android:textColor">@android:color/white</item>
         <item name="android:textSize">@dimen/text_size_medium</item>
@@ -178,15 +185,18 @@
     </style>
 
     <!-- Blue Text Styles -->
-    <style name="TextAppearance.BlueTitle2" parent="TextAppearance.RobotoMediumStyle">
+    <style name="TextAppearance.BlueTitle2" parent="TextAppearance.RobotoMediumStyle" tools:ignore="UnusedResources">
         <item name="android:textColor">@color/blue_when_enabled</item>
         <item name="android:textSize">@dimen/text_size_medium</item>
     </style>
-    <style name="TextAppearance.BlueButtonText1" parent="TextAppearance.RobotoMediumStyle" tools:ignore="UnusedResources">
+    <style name="TextAppearance.BlueButtonText1" parent="TextAppearance.ButtonMediumStyle" tools:ignore="UnusedResources">
         <item name="android:textColor">@color/modern_blue_300</item>
         <item name="android:textSize">@dimen/text_size_medium</item>
     </style>
-    <style name="TextAppearance.BlueButtonText2" parent="TextAppearance.BlueTitle2" />
+    <style name="TextAppearance.BlueButtonText2" parent="TextAppearance.ButtonMediumStyle">
+        <item name="android:textColor">@color/blue_when_enabled</item>
+        <item name="android:textSize">@dimen/text_size_medium</item>
+    </style>
     <style name="TextAppearance.BlueLink1" tools:ignore="UnusedResources">
         <item name="android:textColor">@color/blue_when_enabled</item>
         <item name="android:textSize">@dimen/text_size_large</item>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/DialogDismissalCause.java b/ui/android/java/src/org/chromium/ui/modaldialog/DialogDismissalCause.java
similarity index 97%
rename from chrome/android/java/src/org/chromium/chrome/browser/modaldialog/DialogDismissalCause.java
rename to ui/android/java/src/org/chromium/ui/modaldialog/DialogDismissalCause.java
index 4699c44..1058df1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/DialogDismissalCause.java
+++ b/ui/android/java/src/org/chromium/ui/modaldialog/DialogDismissalCause.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.modaldialog;
+package org.chromium.ui.modaldialog;
 
 import android.support.annotation.IntDef;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/ModalDialogManager.java b/ui/android/java/src/org/chromium/ui/modaldialog/ModalDialogManager.java
similarity index 94%
rename from chrome/android/java/src/org/chromium/chrome/browser/modaldialog/ModalDialogManager.java
rename to ui/android/java/src/org/chromium/ui/modaldialog/ModalDialogManager.java
index 449bf5a..2e4d8af 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/ModalDialogManager.java
+++ b/ui/android/java/src/org/chromium/ui/modaldialog/ModalDialogManager.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.modaldialog;
+package org.chromium.ui.modaldialog;
 
 import android.support.annotation.IntDef;
 import android.support.annotation.NonNull;
@@ -11,7 +11,7 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.VisibleForTesting;
-import org.chromium.chrome.browser.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModel;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -53,7 +53,7 @@
         /**
          * Run the cached cancel callback and reset the cached callback.
          */
-        protected final void dismissCurrentDialog(@DialogDismissalCause int dismissalCause) {
+        public final void dismissCurrentDialog(@DialogDismissalCause int dismissalCause) {
             if (mDismissCallback == null) return;
 
             // Set #mCancelCallback to null before calling the callback to avoid it being
@@ -66,7 +66,7 @@
         /**
          * @return The dialog model that this presenter is showing.
          */
-        protected final PropertyModel getDialogModel() {
+        public final PropertyModel getDialogModel() {
             return mDialogModel;
         }
 
@@ -264,7 +264,7 @@
      * @param dismissalCause The {@link DialogDismissalCause} that describes why the dialogs are
      *                       dismissed.
      */
-    protected void dismissDialogsOfType(
+    public void dismissDialogsOfType(
             @ModalDialogType int dialogType, @DialogDismissalCause int dismissalCause) {
         dismissPendingDialogsOfType(dialogType, dismissalCause);
         if (isShowing() && dialogType == mCurrentType) {
@@ -280,7 +280,8 @@
 
         while (!dialogs.isEmpty()) {
             PropertyModel model = dialogs.remove(0);
-            ModalDialogView.Controller controller = model.get(ModalDialogProperties.CONTROLLER);
+            ModalDialogProperties.Controller controller =
+                    model.get(ModalDialogProperties.CONTROLLER);
             controller.onDismiss(model, dismissalCause);
         }
     }
@@ -292,7 +293,7 @@
      * in the pending list. Any dialogs of the specified type in the pending list will be skipped.
      * @param dialogType The specified type of dialogs to be suspended.
      */
-    protected void suspendType(@ModalDialogType int dialogType) {
+    public void suspendType(@ModalDialogType int dialogType) {
         mSuspendedTypes.add(dialogType);
         if (isShowing() && dialogType == mCurrentType) {
             suspendCurrentDialog();
@@ -304,7 +305,7 @@
      * Resume the specified type of dialogs after suspension.
      * @param dialogType The specified type of dialogs to be resumed.
      */
-    protected void resumeType(@ModalDialogType int dialogType) {
+    public void resumeType(@ModalDialogType int dialogType) {
         mSuspendedTypes.remove(dialogType);
         if (!isShowing()) showNextDialog();
     }
@@ -340,17 +341,17 @@
     }
 
     @VisibleForTesting
-    List<PropertyModel> getPendingDialogsForTest(@ModalDialogType int dialogType) {
+    public List<PropertyModel> getPendingDialogsForTest(@ModalDialogType int dialogType) {
         return mPendingDialogs.get(dialogType);
     }
 
     @VisibleForTesting
-    Presenter getPresenterForTest(@ModalDialogType int dialogType) {
+    public Presenter getPresenterForTest(@ModalDialogType int dialogType) {
         return mPresenters.get(dialogType);
     }
 
     @VisibleForTesting
-    Presenter getCurrentPresenterForTest() {
+    public Presenter getCurrentPresenterForTest() {
         return mCurrentPresenter;
     }
 }
diff --git a/ui/android/java/src/org/chromium/ui/modaldialog/ModalDialogProperties.java b/ui/android/java/src/org/chromium/ui/modaldialog/ModalDialogProperties.java
new file mode 100644
index 0000000..a695523
--- /dev/null
+++ b/ui/android/java/src/org/chromium/ui/modaldialog/ModalDialogProperties.java
@@ -0,0 +1,107 @@
+// 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.
+
+package org.chromium.ui.modaldialog;
+
+import android.graphics.drawable.Drawable;
+import android.support.annotation.IntDef;
+import android.view.View;
+
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModel.ReadableObjectPropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableBooleanPropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * The model properties for a modal dialog.
+ */
+public class ModalDialogProperties {
+    /**
+     * Interface that controls the actions on the modal dialog.
+     */
+    public interface Controller {
+        /**
+         * Handle click event of the buttons on the dialog.
+         * @param model The dialog model that is associated with this click event.
+         * @param buttonType The type of the button.
+         */
+        void onClick(PropertyModel model, @ButtonType int buttonType);
+
+        /**
+         * Handle dismiss event when the dialog is dismissed by actions on the dialog. Note that it
+         * can be dangerous to the {@code dismissalCause} for business logic other than metrics
+         * recording, unless the dismissal cause is fully controlled by the client (e.g. button
+         * clicked), because the dismissal cause can be different values depending on modal dialog
+         * type and mode of presentation (e.g. it could be unknown on VR but a specific value on
+         * non-VR).
+         * @param model The dialog model that is associated with this dismiss event.
+         * @param dismissalCause The reason of the dialog being dismissed.
+         * @see DialogDismissalCause
+         */
+        void onDismiss(PropertyModel model, @DialogDismissalCause int dismissalCause);
+    }
+
+    @IntDef({ModalDialogProperties.ButtonType.POSITIVE, ModalDialogProperties.ButtonType.NEGATIVE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ButtonType {
+        int POSITIVE = 0;
+        int NEGATIVE = 1;
+    }
+
+    /** The {@link Controller} that handles events on user actions. */
+    public static final ReadableObjectPropertyKey<Controller> CONTROLLER =
+            new ReadableObjectPropertyKey<>();
+
+    /** The content description of the dialog for accessibility. */
+    public static final ReadableObjectPropertyKey<String> CONTENT_DESCRIPTION =
+            new ReadableObjectPropertyKey<>();
+
+    /** The title of the dialog. */
+    public static final WritableObjectPropertyKey<String> TITLE = new WritableObjectPropertyKey<>();
+
+    /** The title icon of the dialog. */
+    public static final WritableObjectPropertyKey<Drawable> TITLE_ICON =
+            new WritableObjectPropertyKey<>();
+
+    /** The message of the dialog. */
+    public static final WritableObjectPropertyKey<String> MESSAGE =
+            new WritableObjectPropertyKey<>();
+
+    /** The customized content view of the dialog. */
+    public static final WritableObjectPropertyKey<View> CUSTOM_VIEW =
+            new WritableObjectPropertyKey<>();
+
+    /** The text on the positive button. */
+    public static final WritableObjectPropertyKey<String> POSITIVE_BUTTON_TEXT =
+            new WritableObjectPropertyKey<>();
+
+    /** The enabled state on the positive button. */
+    public static final WritableBooleanPropertyKey POSITIVE_BUTTON_DISABLED =
+            new WritableBooleanPropertyKey();
+
+    /** The text on the negative button. */
+    public static final WritableObjectPropertyKey<String> NEGATIVE_BUTTON_TEXT =
+            new WritableObjectPropertyKey<>();
+
+    /** The enabled state on the negative button. */
+    public static final WritableBooleanPropertyKey NEGATIVE_BUTTON_DISABLED =
+            new WritableBooleanPropertyKey();
+
+    /** Whether the dialog should be dismissed on user tapping the scrim. */
+    public static final WritableBooleanPropertyKey CANCEL_ON_TOUCH_OUTSIDE =
+            new WritableBooleanPropertyKey();
+
+    /** Whether the title is scrollable with the message. */
+    public static final WritableBooleanPropertyKey TITLE_SCROLLABLE =
+            new WritableBooleanPropertyKey();
+
+    public static final PropertyKey[] ALL_KEYS = new PropertyKey[] {CONTROLLER, CONTENT_DESCRIPTION,
+            TITLE, TITLE_ICON, MESSAGE, CUSTOM_VIEW, POSITIVE_BUTTON_TEXT, POSITIVE_BUTTON_DISABLED,
+            NEGATIVE_BUTTON_TEXT, NEGATIVE_BUTTON_DISABLED, CANCEL_ON_TOUCH_OUTSIDE,
+            TITLE_SCROLLABLE};
+}
diff --git a/ui/android/java/src/org/chromium/ui/modaldialog/OWNERS b/ui/android/java/src/org/chromium/ui/modaldialog/OWNERS
new file mode 100644
index 0000000..4c1b84f
--- /dev/null
+++ b/ui/android/java/src/org/chromium/ui/modaldialog/OWNERS
@@ -0,0 +1,6 @@
+huayinz@chromium.org
+twellington@chromium.org
+tedchoc@chromium.org
+
+# COMPONENT: UI>Browser>Mobile
+# OS: Android
\ No newline at end of file
diff --git a/ui/android/java/src/org/chromium/ui/modelutil/OWNERS b/ui/android/java/src/org/chromium/ui/modelutil/OWNERS
new file mode 100644
index 0000000..38a5178
--- /dev/null
+++ b/ui/android/java/src/org/chromium/ui/modelutil/OWNERS
@@ -0,0 +1,5 @@
+twellington@chromium.org
+tedchoc@chromium.org
+
+# COMPONENT: UI>Browser>Mobile
+# OS: Android
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/PropertyKey.java b/ui/android/java/src/org/chromium/ui/modelutil/PropertyKey.java
similarity index 87%
rename from chrome/android/java/src/org/chromium/chrome/browser/modelutil/PropertyKey.java
rename to ui/android/java/src/org/chromium/ui/modelutil/PropertyKey.java
index 51d01cd0..6b22263 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/PropertyKey.java
+++ b/ui/android/java/src/org/chromium/ui/modelutil/PropertyKey.java
@@ -1,7 +1,7 @@
 // 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.
-package org.chromium.chrome.browser.modelutil;
+package org.chromium.ui.modelutil;
 
 /**
  * Place holder interface to allow key definitions.  Should not be used directly and only exposed
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/PropertyModel.java b/ui/android/java/src/org/chromium/ui/modelutil/PropertyModel.java
similarity index 99%
rename from chrome/android/java/src/org/chromium/chrome/browser/modelutil/PropertyModel.java
rename to ui/android/java/src/org/chromium/ui/modelutil/PropertyModel.java
index 1783ae3..0b27c32f5d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/PropertyModel.java
+++ b/ui/android/java/src/org/chromium/ui/modelutil/PropertyModel.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.modelutil;
+package org.chromium.ui.modelutil;
 
 import android.content.Context;
 import android.content.res.Resources;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/PropertyObservable.java b/ui/android/java/src/org/chromium/ui/modelutil/PropertyObservable.java
similarity index 97%
rename from chrome/android/java/src/org/chromium/chrome/browser/modelutil/PropertyObservable.java
rename to ui/android/java/src/org/chromium/ui/modelutil/PropertyObservable.java
index 2a320e89..74ba330 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/PropertyObservable.java
+++ b/ui/android/java/src/org/chromium/ui/modelutil/PropertyObservable.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.modelutil;
+package org.chromium.ui.modelutil;
 
 import android.support.annotation.Nullable;
 
diff --git a/ui/android/junit/src/org/chromium/ui/modaldialog/ModalDialogManagerTest.java b/ui/android/junit/src/org/chromium/ui/modaldialog/ModalDialogManagerTest.java
new file mode 100644
index 0000000..241bc14a
--- /dev/null
+++ b/ui/android/junit/src/org/chromium/ui/modaldialog/ModalDialogManagerTest.java
@@ -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.
+
+package org.chromium.ui.modaldialog;
+
+import static junit.framework.Assert.assertFalse;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.Feature;
+import org.chromium.ui.modaldialog.ModalDialogManager.ModalDialogType;
+import org.chromium.ui.modelutil.PropertyModel;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tests to validate actions within {@link ModalDialogManager}.
+ */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class ModalDialogManagerTest {
+    private static final int MAX_DIALOGS = 4;
+
+    @Mock
+    private ModalDialogManager.Presenter mAppModalPresenter;
+    @Mock
+    private ModalDialogManager.Presenter mTabModalPresenter;
+
+    private ModalDialogManager mModalDialogManager;
+    private List<PropertyModel> mDialogModels = new ArrayList<>();
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mModalDialogManager = new ModalDialogManager(mAppModalPresenter, ModalDialogType.APP);
+        mModalDialogManager.registerPresenter(mTabModalPresenter, ModalDialogType.TAB);
+
+        for (int i = 0; i < MAX_DIALOGS; ++i) {
+            ModalDialogProperties.Controller controller = new ModalDialogProperties.Controller() {
+                @Override
+                public void onClick(PropertyModel model, int buttonType) {}
+
+                @Override
+                public void onDismiss(PropertyModel model, int dismissalCause) {}
+            };
+
+            mDialogModels.add(new PropertyModel.Builder(ModalDialogProperties.ALL_KEYS)
+                                      .with(ModalDialogProperties.CONTROLLER, spy(controller))
+                                      .build());
+        }
+    }
+
+    /** Tests showing a dialog when no dialog is currently showing. */
+    @Test
+    @Feature({"ModalDialog"})
+    public void testShowDialog_NoDialogIsShowing() {
+        // Show an app modal dialog and verify that it is showing.
+        mModalDialogManager.showDialog(mDialogModels.get(0), ModalDialogType.APP);
+        assertTrue(mModalDialogManager.isShowing());
+        assertEquals(mDialogModels.get(0), mModalDialogManager.getCurrentDialogForTest());
+        assertEquals(0, mModalDialogManager.getPendingDialogsForTest(ModalDialogType.APP).size());
+        assertNull(mModalDialogManager.getPendingDialogsForTest(ModalDialogType.TAB));
+        verify(mAppModalPresenter, times(1)).addDialogView(mDialogModels.get(0));
+        verify(mTabModalPresenter, times(0)).addDialogView(any());
+    }
+
+    /**
+     * Tests showing a dialog when another dialog of same or higher priority is currently showing.
+     */
+    @Test
+    @Feature({"ModalDialog"})
+    public void testShowDialog_DialogOfSameOrHigherPriorityIsShowing() {
+        // Show an app modal dialog and verify that it is showing.
+        mModalDialogManager.showDialog(mDialogModels.get(0), ModalDialogType.APP);
+        assertEquals(mDialogModels.get(0), mModalDialogManager.getCurrentDialogForTest());
+        verify(mAppModalPresenter, times(1)).addDialogView(mDialogModels.get(0));
+        verify(mTabModalPresenter, times(0)).addDialogView(any());
+
+        // Show another app modal dialog and verify that it is queued.
+        mModalDialogManager.showDialog(mDialogModels.get(1), ModalDialogType.APP);
+        assertEquals(mDialogModels.get(0), mModalDialogManager.getCurrentDialogForTest());
+        assertEquals(1, mModalDialogManager.getPendingDialogsForTest(ModalDialogType.APP).size());
+        assertNull(mModalDialogManager.getPendingDialogsForTest(ModalDialogType.TAB));
+        verify(mAppModalPresenter, times(1)).addDialogView(mDialogModels.get(0));
+        verify(mAppModalPresenter, times(0)).addDialogView(mDialogModels.get(1));
+        verify(mTabModalPresenter, times(0)).addDialogView(any());
+
+        // Show a tab modal dialog and verify that it is queued.
+        mModalDialogManager.showDialog(mDialogModels.get(2), ModalDialogType.TAB);
+        assertEquals(mDialogModels.get(0), mModalDialogManager.getCurrentDialogForTest());
+        assertEquals(1, mModalDialogManager.getPendingDialogsForTest(ModalDialogType.APP).size());
+        assertEquals(1, mModalDialogManager.getPendingDialogsForTest(ModalDialogType.TAB).size());
+        verify(mAppModalPresenter, times(1)).addDialogView(mDialogModels.get(0));
+        verify(mAppModalPresenter, times(0)).addDialogView(mDialogModels.get(1));
+        verify(mTabModalPresenter, times(0)).addDialogView(any());
+    }
+
+    /** Tests showing a dialog when another dialog of lower priority is currently showing. */
+    @Test
+    @Feature({"ModalDialog"})
+    public void testShowDialog_DialogOfLowerPriorityIsShowing() {
+        // Show a tab modal dialog and verify that it is showing.
+        mModalDialogManager.showDialog(mDialogModels.get(0), ModalDialogType.TAB);
+        assertEquals(mDialogModels.get(0), mModalDialogManager.getCurrentDialogForTest());
+        verify(mAppModalPresenter, times(0)).addDialogView(any());
+        verify(mTabModalPresenter, times(1)).addDialogView(mDialogModels.get(0));
+
+        // Show an app modal dialog, and verify that the app modal dialog is shown, and the tab
+        // modal dialog is queued.
+        mModalDialogManager.showDialog(mDialogModels.get(1), ModalDialogType.APP);
+        assertEquals(mDialogModels.get(1), mModalDialogManager.getCurrentDialogForTest());
+        assertEquals(0, mModalDialogManager.getPendingDialogsForTest(ModalDialogType.APP).size());
+        assertEquals(1, mModalDialogManager.getPendingDialogsForTest(ModalDialogType.TAB).size());
+        verify(mAppModalPresenter, times(0)).addDialogView(mDialogModels.get(0));
+        verify(mAppModalPresenter, times(1)).addDialogView(mDialogModels.get(1));
+        verify(mTabModalPresenter, times(1)).addDialogView(any());
+    }
+
+    /**
+     * Tests whether the next dialog in the pending queue is correctly shown after the current
+     * dialog is dismissed.
+     */
+    @Test
+    @Feature({"ModalDialog"})
+    public void testShowDialog_ShowNextDialogAfterDismiss() {
+        // Show an app modal dialog and verify that it is showing.
+        mModalDialogManager.showDialog(mDialogModels.get(0), ModalDialogType.APP);
+        assertEquals(mDialogModels.get(0), mModalDialogManager.getCurrentDialogForTest());
+        verify(mAppModalPresenter, times(1)).addDialogView(mDialogModels.get(0));
+        verify(mTabModalPresenter, times(0)).addDialogView(any());
+
+        // Show a second and a third app modal dialog and verify that they are queued.
+        mModalDialogManager.showDialog(mDialogModels.get(1), ModalDialogType.APP);
+        mModalDialogManager.showDialog(mDialogModels.get(2), ModalDialogType.APP);
+        assertEquals(mDialogModels.get(0), mModalDialogManager.getCurrentDialogForTest());
+        assertEquals(2, mModalDialogManager.getPendingDialogsForTest(ModalDialogType.APP).size());
+        assertNull(mModalDialogManager.getPendingDialogsForTest(ModalDialogType.TAB));
+        verify(mAppModalPresenter, times(1)).addDialogView(any());
+        verify(mTabModalPresenter, times(0)).addDialogView(any());
+
+        // Dismiss the first dialog and verify that the second dialog is shown.
+        mModalDialogManager.dismissDialog(mDialogModels.get(0), ModalDialogType.APP);
+        assertOnDismissCalled(mDialogModels.get(0), 1);
+        assertEquals(mDialogModels.get(1), mModalDialogManager.getCurrentDialogForTest());
+        assertEquals(1, mModalDialogManager.getPendingDialogsForTest(ModalDialogType.APP).size());
+        assertNull(mModalDialogManager.getPendingDialogsForTest(ModalDialogType.TAB));
+        verify(mAppModalPresenter, times(1)).addDialogView(mDialogModels.get(0));
+        verify(mAppModalPresenter, times(1)).addDialogView(mDialogModels.get(1));
+        verify(mAppModalPresenter, times(0)).addDialogView(mDialogModels.get(2));
+        verify(mTabModalPresenter, times(0)).addDialogView(any());
+    }
+
+    /** Tests showing a dialog as the next available dialog in the pending queue. */
+    @Test
+    @Feature({"ModalDialog"})
+    public void testShowDialog_ShowAsNext() {
+        // Show an app modal dialog and verify that it is showing.
+        mModalDialogManager.showDialog(mDialogModels.get(0), ModalDialogType.APP);
+        assertEquals(mDialogModels.get(0), mModalDialogManager.getCurrentDialogForTest());
+        verify(mAppModalPresenter, times(1)).addDialogView(mDialogModels.get(0));
+        verify(mTabModalPresenter, times(0)).addDialogView(any());
+
+        // Show a second app modal dialog and verify that it is queued.
+        mModalDialogManager.showDialog(mDialogModels.get(1), ModalDialogType.APP);
+        assertEquals(mDialogModels.get(0), mModalDialogManager.getCurrentDialogForTest());
+        assertEquals(1, mModalDialogManager.getPendingDialogsForTest(ModalDialogType.APP).size());
+        assertNull(mModalDialogManager.getPendingDialogsForTest(ModalDialogType.TAB));
+        verify(mAppModalPresenter, times(1)).addDialogView(any());
+        verify(mTabModalPresenter, times(0)).addDialogView(any());
+
+        // Show a third app modal dialog as next and verify that it is queued.
+        mModalDialogManager.showDialog(mDialogModels.get(2), ModalDialogType.APP, true);
+        assertEquals(mDialogModels.get(0), mModalDialogManager.getCurrentDialogForTest());
+        assertEquals(2, mModalDialogManager.getPendingDialogsForTest(ModalDialogType.APP).size());
+        assertNull(mModalDialogManager.getPendingDialogsForTest(ModalDialogType.TAB));
+        verify(mAppModalPresenter, times(1)).addDialogView(any());
+        verify(mTabModalPresenter, times(0)).addDialogView(any());
+
+        // Dismiss the first dialog and verify that the third dialog is shown.
+        mModalDialogManager.dismissDialog(mDialogModels.get(0), ModalDialogType.APP);
+        assertOnDismissCalled(mDialogModels.get(0), 1);
+        assertEquals(mDialogModels.get(2), mModalDialogManager.getCurrentDialogForTest());
+        assertEquals(1, mModalDialogManager.getPendingDialogsForTest(ModalDialogType.APP).size());
+        assertNull(mModalDialogManager.getPendingDialogsForTest(ModalDialogType.TAB));
+        verify(mAppModalPresenter, times(1)).addDialogView(mDialogModels.get(0));
+        verify(mAppModalPresenter, times(0)).addDialogView(mDialogModels.get(1));
+        verify(mAppModalPresenter, times(1)).addDialogView(mDialogModels.get(2));
+        verify(mTabModalPresenter, times(0)).addDialogView(any());
+    }
+
+    /** Tests dismissing the current dialog. */
+    @Test
+    @Feature({"ModalDialog"})
+    public void testDismissDialog_CurrentDialog() {
+        // Show an app modal dialog and verify that it is showing.
+        mModalDialogManager.showDialog(mDialogModels.get(0), ModalDialogType.APP);
+        assertEquals(mDialogModels.get(0), mModalDialogManager.getCurrentDialogForTest());
+        verify(mAppModalPresenter, times(1)).addDialogView(mDialogModels.get(0));
+
+        // Show a tab modal dialog then a second app modal dialog and verify that they are queued.
+        mModalDialogManager.showDialog(mDialogModels.get(1), ModalDialogType.TAB);
+        mModalDialogManager.showDialog(mDialogModels.get(2), ModalDialogType.APP);
+        assertEquals(mDialogModels.get(0), mModalDialogManager.getCurrentDialogForTest());
+        assertEquals(1, mModalDialogManager.getPendingDialogsForTest(ModalDialogType.APP).size());
+        assertEquals(1, mModalDialogManager.getPendingDialogsForTest(ModalDialogType.TAB).size());
+
+        // Dismiss the current dialog and the second app modal dialog should be showing next.
+        mModalDialogManager.dismissDialog(mDialogModels.get(0), DialogDismissalCause.UNKNOWN);
+        assertOnDismissCalled(mDialogModels.get(0), 1);
+        assertEquals(mDialogModels.get(2), mModalDialogManager.getCurrentDialogForTest());
+        verify(mAppModalPresenter, times(1)).removeDialogView(mDialogModels.get(0));
+
+        assertTrue(mModalDialogManager.isShowing());
+        assertEquals(0, mModalDialogManager.getPendingDialogsForTest(ModalDialogType.APP).size());
+        assertEquals(1, mModalDialogManager.getPendingDialogsForTest(ModalDialogType.TAB).size());
+        verify(mAppModalPresenter, times(1)).addDialogView(mDialogModels.get(2));
+        verify(mTabModalPresenter, times(0)).addDialogView(any());
+
+        // Dismiss the first dialog again and verify nothing is changed.
+        mModalDialogManager.dismissDialog(mDialogModels.get(0), DialogDismissalCause.UNKNOWN);
+        assertOnDismissCalled(mDialogModels.get(0), 1);
+        assertEquals(mDialogModels.get(2), mModalDialogManager.getCurrentDialogForTest());
+        assertEquals(0, mModalDialogManager.getPendingDialogsForTest(ModalDialogType.APP).size());
+        assertEquals(1, mModalDialogManager.getPendingDialogsForTest(ModalDialogType.TAB).size());
+        Mockito.verifyNoMoreInteractions(mAppModalPresenter, mTabModalPresenter);
+    }
+
+    /** Tests dismissing a dialog in the pending queue. */
+    @Test
+    @Feature({"ModalDialog"})
+    public void testDismissDialog_DialogInQueue() {
+        // Show three dialogs.
+        mModalDialogManager.showDialog(mDialogModels.get(0), ModalDialogType.APP);
+        mModalDialogManager.showDialog(mDialogModels.get(1), ModalDialogType.APP);
+        mModalDialogManager.showDialog(mDialogModels.get(2), ModalDialogType.TAB);
+        assertEquals(mDialogModels.get(0), mModalDialogManager.getCurrentDialogForTest());
+        assertEquals(1, mModalDialogManager.getPendingDialogsForTest(ModalDialogType.APP).size());
+        assertEquals(1, mModalDialogManager.getPendingDialogsForTest(ModalDialogType.TAB).size());
+
+        // Dismiss the second dialog.
+        mModalDialogManager.dismissDialog(mDialogModels.get(1), DialogDismissalCause.UNKNOWN);
+        assertOnDismissCalled(mDialogModels.get(1), 1);
+        assertEquals(mDialogModels.get(0), mModalDialogManager.getCurrentDialogForTest());
+        assertEquals(0, mModalDialogManager.getPendingDialogsForTest(ModalDialogType.APP).size());
+        assertEquals(1, mModalDialogManager.getPendingDialogsForTest(ModalDialogType.TAB).size());
+
+        // Dismiss the third dialog.
+        mModalDialogManager.dismissDialog(mDialogModels.get(2), DialogDismissalCause.UNKNOWN);
+        assertOnDismissCalled(mDialogModels.get(2), 1);
+        assertEquals(mDialogModels.get(0), mModalDialogManager.getCurrentDialogForTest());
+        assertEquals(0, mModalDialogManager.getPendingDialogsForTest(ModalDialogType.APP).size());
+        assertEquals(0, mModalDialogManager.getPendingDialogsForTest(ModalDialogType.TAB).size());
+    }
+
+    /** Tests dismissing all dialogs. */
+    @Test
+    @Feature({"ModalDialog"})
+    public void testDismissAllDialogs() {
+        // Show three dialogs.
+        mModalDialogManager.showDialog(mDialogModels.get(0), ModalDialogType.APP);
+        mModalDialogManager.showDialog(mDialogModels.get(1), ModalDialogType.APP);
+        mModalDialogManager.showDialog(mDialogModels.get(2), ModalDialogType.TAB);
+
+        // Dismiss all dialog.
+        mModalDialogManager.dismissAllDialogs(DialogDismissalCause.UNKNOWN);
+        assertOnDismissCalled(mDialogModels.get(0), 1);
+        assertOnDismissCalled(mDialogModels.get(1), 1);
+        assertOnDismissCalled(mDialogModels.get(2), 1);
+        assertFalse(mModalDialogManager.isShowing());
+        assertEquals(0, mModalDialogManager.getPendingDialogsForTest(ModalDialogType.APP).size());
+        assertEquals(0, mModalDialogManager.getPendingDialogsForTest(ModalDialogType.TAB).size());
+    }
+
+    /** Tests dismissing dialogs of a certain type. */
+    @Test
+    @Feature({"ModalDialog"})
+    public void testDismissDialogsOfType() {
+        // Show three dialogs.
+        mModalDialogManager.showDialog(mDialogModels.get(0), ModalDialogType.APP);
+        mModalDialogManager.showDialog(mDialogModels.get(1), ModalDialogType.APP);
+        mModalDialogManager.showDialog(mDialogModels.get(2), ModalDialogType.TAB);
+
+        // Dismiss all app modal dialogs.
+        mModalDialogManager.dismissDialogsOfType(ModalDialogType.APP, DialogDismissalCause.UNKNOWN);
+        assertOnDismissCalled(mDialogModels.get(0), 1);
+        assertOnDismissCalled(mDialogModels.get(1), 1);
+        assertEquals(mDialogModels.get(2), mModalDialogManager.getCurrentDialogForTest());
+        assertEquals(0, mModalDialogManager.getPendingDialogsForTest(ModalDialogType.APP).size());
+        assertEquals(0, mModalDialogManager.getPendingDialogsForTest(ModalDialogType.TAB).size());
+    }
+
+    /** Tests suspending dialogs of a certain type. */
+    @Test
+    @Feature({"ModalDialog"})
+    public void testSuspendType() {
+        // Show two tab modal dialogs.
+        mModalDialogManager.showDialog(mDialogModels.get(0), ModalDialogType.TAB);
+        mModalDialogManager.showDialog(mDialogModels.get(1), ModalDialogType.TAB);
+
+        // Suspend all tab modal dialogs. onDismiss() should not be called.
+        mModalDialogManager.suspendType(ModalDialogType.TAB);
+        assertOnDismissCalled(mDialogModels.get(0), 0);
+        assertOnDismissCalled(mDialogModels.get(1), 0);
+        assertFalse(mModalDialogManager.isShowing());
+        assertNull(mModalDialogManager.getPendingDialogsForTest(ModalDialogType.APP));
+        assertEquals(2, mModalDialogManager.getPendingDialogsForTest(ModalDialogType.TAB).size());
+
+        // Show a third tab modal dialog, and verify that it is queued.
+        mModalDialogManager.showDialog(mDialogModels.get(2), ModalDialogType.TAB);
+        assertFalse(mModalDialogManager.isShowing());
+        assertNull(mModalDialogManager.getPendingDialogsForTest(ModalDialogType.APP));
+        assertEquals(3, mModalDialogManager.getPendingDialogsForTest(ModalDialogType.TAB).size());
+
+        // Show an app modal dialog, and verify that it is shown.
+        mModalDialogManager.showDialog(mDialogModels.get(3), ModalDialogType.APP);
+        assertEquals(mDialogModels.get(3), mModalDialogManager.getCurrentDialogForTest());
+        assertEquals(0, mModalDialogManager.getPendingDialogsForTest(ModalDialogType.APP).size());
+        assertEquals(3, mModalDialogManager.getPendingDialogsForTest(ModalDialogType.TAB).size());
+    }
+
+    /** Tests resuming dialogs of a certain type. */
+    @Test
+    @Feature({"ModalDialog"})
+    public void testResumeType() {
+        // Show three tab modal dialogs.
+        mModalDialogManager.showDialog(mDialogModels.get(0), ModalDialogType.TAB);
+        mModalDialogManager.showDialog(mDialogModels.get(1), ModalDialogType.TAB);
+        mModalDialogManager.showDialog(mDialogModels.get(2), ModalDialogType.TAB);
+
+        // Suspend all tab modal dialogs.
+        mModalDialogManager.suspendType(ModalDialogType.TAB);
+        assertFalse(mModalDialogManager.isShowing());
+        assertEquals(3, mModalDialogManager.getPendingDialogsForTest(ModalDialogType.TAB).size());
+
+        // Resume tab modal dialogs.
+        mModalDialogManager.resumeType(ModalDialogType.TAB);
+        assertEquals(mDialogModels.get(0), mModalDialogManager.getCurrentDialogForTest());
+        assertEquals(2, mModalDialogManager.getPendingDialogsForTest(ModalDialogType.TAB).size());
+    }
+
+    private static void assertOnDismissCalled(PropertyModel model, int numberOfInvocations) {
+        verify(model.get(ModalDialogProperties.CONTROLLER), times(numberOfInvocations))
+                .onDismiss(eq(model), anyInt());
+    }
+}
diff --git a/ui/gfx/paint_throbber.cc b/ui/gfx/paint_throbber.cc
index 17b2f13a..e281c13f 100644
--- a/ui/gfx/paint_throbber.cc
+++ b/ui/gfx/paint_throbber.cc
@@ -223,11 +223,9 @@
   cc::PaintFlags flags;
   flags.setColor(color);
   flags.setStyle(cc::PaintFlags::kFill_Style);
-  // Disable anti-aliasing to effectively "pixel align" the rectangle.
-  flags.setAntiAlias(false);
 
   // Draw with circular end caps.
-  canvas->DrawRect(bounds, flags);
+  canvas->DrawRoundRect(bounds, bounds.height() / 2, flags);
 }
 
 }  // namespace gfx
diff --git a/ui/gfx/paint_throbber.h b/ui/gfx/paint_throbber.h
index a3c0171..a373d20a1 100644
--- a/ui/gfx/paint_throbber.h
+++ b/ui/gfx/paint_throbber.h
@@ -60,7 +60,7 @@
 
 // Cycle time for the throbber above. Used to be able to smoothly transition
 // between the throbber and the determinite progress-bar animation.
-constexpr int kNewThrobberWaitingAnimationCycleMs = 850;
+constexpr int kNewThrobberWaitingAnimationCycleMs = 1000;
 
 }  // namespace gfx
 
diff --git a/ui/gfx/render_text.cc b/ui/gfx/render_text.cc
index 46dd6934..be2f26d 100644
--- a/ui/gfx/render_text.cc
+++ b/ui/gfx/render_text.cc
@@ -626,8 +626,10 @@
   return changed;
 }
 
-bool RenderText::MoveCursorToPoint(const gfx::Point& point, bool select) {
-  gfx::SelectionModel model = FindCursorPosition(point);
+bool RenderText::MoveCursorToPoint(const gfx::Point& point,
+                                   bool select,
+                                   const gfx::Point& drag_origin) {
+  gfx::SelectionModel model = FindCursorPosition(point, drag_origin);
   if (select)
     model.set_selection_start(selection().start());
   return SetSelection(model);
diff --git a/ui/gfx/render_text.h b/ui/gfx/render_text.h
index 50eaca5..6eaea6c5 100644
--- a/ui/gfx/render_text.h
+++ b/ui/gfx/render_text.h
@@ -344,8 +344,13 @@
   // Moves the cursor to the text index corresponding to |point|. If |select| is
   // true, a selection is made with the current selection start index. If the
   // resultant text indices do not lie on valid grapheme boundaries, it is a no-
-  // op and returns false.
-  bool MoveCursorToPoint(const gfx::Point& point, bool select);
+  // op and returns false. If this move is happening because of a drag causing a
+  // selection change, and |drag_origin| is not the zero point, then
+  // |drag_origin| overrides the default origin for a select-to-drag
+  // (usually the existing text insertion cursor).
+  bool MoveCursorToPoint(const gfx::Point& point,
+                         bool select,
+                         const gfx::Point& drag_origin = gfx::Point());
 
   // Set the selection_model_ based on |range|.
   // If the |range| start or end is greater than text length, it is modified
@@ -446,8 +451,13 @@
 
   void Draw(Canvas* canvas);
 
-  // Gets the SelectionModel from a visual point in local coordinates.
-  virtual SelectionModel FindCursorPosition(const Point& point) = 0;
+  // Gets the SelectionModel from a visual point in local coordinates. If
+  // |drag_origin| is nonzero, it is used as the baseline for
+  // out-of-vertical-bounds drags on platforms that have them, instead of the
+  // default origin (the insertion cursor's position).
+  virtual SelectionModel FindCursorPosition(
+      const Point& point,
+      const Point& drag_origin = gfx::Point()) = 0;
 
   // Returns true if the position is a valid logical index into text(), and is
   // also a valid grapheme boundary, which may be used as a cursor position.
diff --git a/ui/gfx/render_text_harfbuzz.cc b/ui/gfx/render_text_harfbuzz.cc
index 926b9e8c..64b482b0 100644
--- a/ui/gfx/render_text_harfbuzz.cc
+++ b/ui/gfx/render_text_harfbuzz.cc
@@ -1215,7 +1215,9 @@
   return total_size_;
 }
 
-SelectionModel RenderTextHarfBuzz::FindCursorPosition(const Point& view_point) {
+SelectionModel RenderTextHarfBuzz::FindCursorPosition(
+    const Point& view_point,
+    const Point& drag_origin) {
   EnsureLayout();
   DCHECK(!lines().empty());
 
@@ -1225,7 +1227,9 @@
   if (RenderText::kDragToEndIfOutsideVerticalBounds && !multiline() &&
       (line_index < 0 || line_index >= static_cast<int>(lines().size()))) {
     SelectionModel selection_start = GetSelectionModelForSelectionStart();
-    bool left = view_point.x() < GetCursorBounds(selection_start, true).x();
+    int edge = drag_origin.x() == 0 ? GetCursorBounds(selection_start, true).x()
+                                    : drag_origin.x();
+    bool left = view_point.x() < edge;
     return EdgeSelectionModel(left ? CURSOR_LEFT : CURSOR_RIGHT);
   }
   // Otherwise, clamp |line_index| to a valid value or drag to logical ends.
diff --git a/ui/gfx/render_text_harfbuzz.h b/ui/gfx/render_text_harfbuzz.h
index 38a9913..97bb64b 100644
--- a/ui/gfx/render_text_harfbuzz.h
+++ b/ui/gfx/render_text_harfbuzz.h
@@ -211,7 +211,8 @@
   const base::string16& GetDisplayText() override;
   Size GetStringSize() override;
   SizeF GetStringSizeF() override;
-  SelectionModel FindCursorPosition(const Point& point) override;
+  SelectionModel FindCursorPosition(const Point& point,
+                                    const Point& drag_origin) override;
   bool IsSelectionSupported() const override;
   std::vector<FontSpan> GetFontSpansForTesting() override;
   std::vector<Rect> GetSubstringBounds(const Range& range) override;
diff --git a/ui/gfx/render_text_mac.h b/ui/gfx/render_text_mac.h
index 9a952eb..1c7f2bb 100644
--- a/ui/gfx/render_text_mac.h
+++ b/ui/gfx/render_text_mac.h
@@ -36,7 +36,8 @@
   const base::string16& GetDisplayText() override;
   Size GetStringSize() override;
   SizeF GetStringSizeF() override;
-  SelectionModel FindCursorPosition(const Point& point) override;
+  SelectionModel FindCursorPosition(const Point& point,
+                                    const Point& drag_origin) override;
   bool IsSelectionSupported() const override;
   std::vector<FontSpan> GetFontSpansForTesting() override;
 
diff --git a/ui/gfx/render_text_mac.mm b/ui/gfx/render_text_mac.mm
index 198eecd..5d804727 100644
--- a/ui/gfx/render_text_mac.mm
+++ b/ui/gfx/render_text_mac.mm
@@ -137,7 +137,8 @@
   return string_size_;
 }
 
-SelectionModel RenderTextMac::FindCursorPosition(const Point& point) {
+SelectionModel RenderTextMac::FindCursorPosition(const Point& point,
+                                                 const Point& drag_origin) {
   // TODO(asvitkine): Implement this. http://crbug.com/131618
   return SelectionModel();
 }
diff --git a/ui/views/cocoa/bridged_native_widget_host_impl.h b/ui/views/cocoa/bridged_native_widget_host_impl.h
index 2bbec19..383e924 100644
--- a/ui/views/cocoa/bridged_native_widget_host_impl.h
+++ b/ui/views/cocoa/bridged_native_widget_host_impl.h
@@ -230,7 +230,6 @@
                  gfx::Point* baseline_point) override;
   views_bridge_mac::DragDropClient* GetDragDropClient() override;
   ui::TextInputClient* GetTextInputClient() override;
-  void GetHasInputContext(bool* has_text_input_context) override;
 
   // BridgeFactoryHost::Observer:
   void OnBridgeFactoryHostDestroying(BridgeFactoryHost* host) override;
diff --git a/ui/views/cocoa/bridged_native_widget_host_impl.mm b/ui/views/cocoa/bridged_native_widget_host_impl.mm
index d8e8e14..155bea3d 100644
--- a/ui/views/cocoa/bridged_native_widget_host_impl.mm
+++ b/ui/views/cocoa/bridged_native_widget_host_impl.mm
@@ -153,8 +153,8 @@
 void BridgedNativeWidgetHostImpl::CreateLocalBridge(
     base::scoped_nsobject<NativeWidgetMacNSWindow> window) {
   local_window_ = window;
-  bridge_impl_ =
-      std::make_unique<BridgedNativeWidgetImpl>(widget_id_, this, this);
+  bridge_impl_ = std::make_unique<BridgedNativeWidgetImpl>(
+      widget_id_, this, this, text_input_host_.get());
   bridge_impl_->SetWindow(window);
 }
 
@@ -180,8 +180,11 @@
   views_bridge_mac::mojom::BridgedNativeWidgetHostAssociatedPtr host_ptr;
   host_mojo_binding_.Bind(mojo::MakeRequest(&host_ptr),
                           ui::WindowResizeHelperMac::Get()->task_runner());
+  views_bridge_mac::mojom::TextInputHostAssociatedPtr text_input_host_ptr;
+  text_input_host_->BindRequest(mojo::MakeRequest(&text_input_host_ptr));
   bridge_factory_host_->GetFactory()->CreateBridgedNativeWidget(
-      widget_id_, mojo::MakeRequest(&bridge_ptr_), host_ptr.PassInterface());
+      widget_id_, mojo::MakeRequest(&bridge_ptr_), host_ptr.PassInterface(),
+      text_input_host_ptr.PassInterface());
 
   // Create the window in its process, and attach it to its parent window.
   bridge()->CreateWindow(std::move(window_create_params));
@@ -1154,10 +1157,6 @@
   text_input_host_->SetTextInputClient(new_text_input_client);
 }
 
-void BridgedNativeWidgetHostImpl::GetHasInputContext(bool* has_input_context) {
-  return text_input_host_->GetHasInputContext(has_input_context);
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // BridgedNativeWidgetImpl, internal::InputMethodDelegate:
 
diff --git a/ui/views/cocoa/text_input_host.h b/ui/views/cocoa/text_input_host.h
index 7af58f8a..d1aa622 100644
--- a/ui/views/cocoa/text_input_host.h
+++ b/ui/views/cocoa/text_input_host.h
@@ -6,7 +6,9 @@
 #define UI_VIEWS_COCOA_TEXT_INPUT_HOST_H_
 
 #include "base/macros.h"
+#include "mojo/public/cpp/bindings/associated_binding.h"
 #include "ui/views/views_export.h"
+#include "ui/views_bridge_mac/mojo/text_input_host.mojom.h"
 
 namespace ui {
 class TextInputClient;
@@ -16,22 +18,58 @@
 
 class BridgedNativeWidgetHostImpl;
 
-class VIEWS_EXPORT TextInputHost {
+class VIEWS_EXPORT TextInputHost
+    : public views_bridge_mac::mojom::TextInputHost {
  public:
   TextInputHost(BridgedNativeWidgetHostImpl* host_impl);
-  ~TextInputHost();
+  ~TextInputHost() override;
+  void BindRequest(
+      views_bridge_mac::mojom::TextInputHostAssociatedRequest request);
 
   // Set the current TextInputClient.
   void SetTextInputClient(ui::TextInputClient* new_text_input_client);
 
-  // Return true if -[NSView inputContext] should return a non-nil value.
-  void GetHasInputContext(bool* has_input_context);
-
   // Return a pointer to the host's ui::TextInputClient.
   // TODO(ccameron): Remove the need for this call.
   ui::TextInputClient* GetTextInputClient() const;
 
  private:
+  // views_bridge_mac::mojom::TextInputHost:
+  bool HasClient(bool* out_has_client) override;
+  bool HasInputContext(bool* out_has_input_context) override;
+  bool IsRTL(bool* out_is_rtl) override;
+  bool GetSelectionRange(gfx::Range* out_range) override;
+  bool GetSelectionText(bool* out_result, base::string16* out_text) override;
+  void InsertText(const base::string16& text, bool as_character) override;
+  void DeleteRange(const gfx::Range& range) override;
+  void SetCompositionText(const base::string16& text,
+                          const gfx::Range& selected_range,
+                          const gfx::Range& replacement_range) override;
+  void ConfirmCompositionText() override;
+  bool HasCompositionText(bool* out_has_composition_text) override;
+  bool GetCompositionTextRange(gfx::Range* out_composition_range) override;
+  bool GetAttributedSubstringForRange(const gfx::Range& requested_range,
+                                      base::string16* out_text,
+                                      gfx::Range* out_actual_range) override;
+  bool GetFirstRectForRange(const gfx::Range& requested_range,
+                            gfx::Rect* out_rect,
+                            gfx::Range* out_actual_range) override;
+
+  // views_bridge_mac::mojom::TextInputHost synchronous methods:
+  void HasClient(HasClientCallback callback) override;
+  void HasInputContext(HasInputContextCallback callback) override;
+  void IsRTL(IsRTLCallback callback) override;
+  void GetSelectionRange(GetSelectionRangeCallback callback) override;
+  void GetSelectionText(GetSelectionTextCallback callback) override;
+  void HasCompositionText(HasCompositionTextCallback callback) override;
+  void GetCompositionTextRange(
+      GetCompositionTextRangeCallback callback) override;
+  void GetAttributedSubstringForRange(
+      const gfx::Range& requested_range,
+      GetAttributedSubstringForRangeCallback callback) override;
+  void GetFirstRectForRange(const gfx::Range& requested_range,
+                            GetFirstRectForRangeCallback callback) override;
+
   // Weak. If non-null the TextInputClient of the currently focused views::View
   // in the hierarchy rooted at the root view of |host_impl_|. Owned by the
   // focused views::View.
@@ -44,6 +82,7 @@
 
   BridgedNativeWidgetHostImpl* const host_impl_;
 
+  mojo::AssociatedBinding<views_bridge_mac::mojom::TextInputHost> mojo_binding_;
   DISALLOW_COPY_AND_ASSIGN(TextInputHost);
 };
 
diff --git a/ui/views/cocoa/text_input_host.mm b/ui/views/cocoa/text_input_host.mm
index 11f7b68..9b7d9abd 100644
--- a/ui/views/cocoa/text_input_host.mm
+++ b/ui/views/cocoa/text_input_host.mm
@@ -4,17 +4,144 @@
 
 #include "ui/views/cocoa/text_input_host.h"
 
+#include "ui/accelerated_widget_mac/window_resize_helper_mac.h"
 #include "ui/base/ime/text_input_client.h"
+#include "ui/events/keycodes/dom/dom_code.h"
 #include "ui/views/cocoa/bridged_native_widget_host_impl.h"
 #include "ui/views_bridge_mac/bridged_native_widget_impl.h"
 
+namespace {
+
+// Returns the boundary rectangle for composition characters in the
+// |requested_range|. Sets |actual_range| corresponding to the returned
+// rectangle. For cases, where there is no composition text or the
+// |requested_range| lies outside the composition range, a zero width rectangle
+// corresponding to the caret bounds is returned. Logic used is similar to
+// RenderWidgetHostViewMac::GetCachedFirstRectForCharacterRange(...).
+gfx::Rect GetFirstRectForRangeHelper(const ui::TextInputClient* client,
+                                     const gfx::Range& requested_range,
+                                     gfx::Range* actual_range) {
+  // NSRange doesn't support reversed ranges.
+  DCHECK(!requested_range.is_reversed());
+  DCHECK(actual_range);
+
+  // Set up default return values, to be returned in case of unusual cases.
+  gfx::Rect default_rect;
+  *actual_range = gfx::Range::InvalidRange();
+  if (!client)
+    return default_rect;
+
+  default_rect = client->GetCaretBounds();
+  default_rect.set_width(0);
+
+  // If possible, modify actual_range to correspond to caret position.
+  gfx::Range selection_range;
+  if (client->GetEditableSelectionRange(&selection_range)) {
+    // Caret bounds correspond to end index of selection_range.
+    *actual_range = gfx::Range(selection_range.end());
+  }
+
+  gfx::Range composition_range;
+  if (!client->HasCompositionText() ||
+      !client->GetCompositionTextRange(&composition_range) ||
+      !composition_range.Contains(requested_range))
+    return default_rect;
+
+  DCHECK(!composition_range.is_reversed());
+
+  const size_t from = requested_range.start() - composition_range.start();
+  const size_t to = requested_range.end() - composition_range.start();
+
+  // Pick the first character's bounds as the initial rectangle, then grow it to
+  // the full |requested_range| if possible.
+  const bool request_is_composition_end = from == composition_range.length();
+  const size_t first_index = request_is_composition_end ? from - 1 : from;
+  gfx::Rect union_rect;
+  if (!client->GetCompositionCharacterBounds(first_index, &union_rect))
+    return default_rect;
+
+  // If requested_range is empty, return a zero width rectangle corresponding to
+  // it.
+  if (from == to) {
+    if (request_is_composition_end &&
+        client->GetTextDirection() != base::i18n::RIGHT_TO_LEFT) {
+      // In case of an empty requested range at end of composition, return the
+      // rectangle to the right of the last compositioned character.
+      union_rect.set_origin(union_rect.top_right());
+    }
+    union_rect.set_width(0);
+    *actual_range = requested_range;
+    return union_rect;
+  }
+
+  // Toolkit-views textfields are always single-line, so no need to check for
+  // line breaks.
+  for (size_t i = from + 1; i < to; i++) {
+    gfx::Rect current_rect;
+    if (client->GetCompositionCharacterBounds(i, &current_rect)) {
+      union_rect.Union(current_rect);
+    } else {
+      *actual_range =
+          gfx::Range(requested_range.start(), i + composition_range.start());
+      return union_rect;
+    }
+  }
+  *actual_range = requested_range;
+  return union_rect;
+}
+
+// Returns the string corresponding to |requested_range| for the given |client|.
+// If a gfx::Range::InvalidRange() is passed, the full string stored by |client|
+// is returned. Sets |actual_range| corresponding to the returned string.
+base::string16 AttributedSubstringForRangeHelper(
+    const ui::TextInputClient* client,
+    const gfx::Range& requested_range,
+    gfx::Range* actual_range) {
+  // NSRange doesn't support reversed ranges.
+  DCHECK(!requested_range.is_reversed());
+  DCHECK(actual_range);
+
+  base::string16 substring;
+  gfx::Range text_range;
+  *actual_range = gfx::Range::InvalidRange();
+  if (!client || !client->GetTextRange(&text_range))
+    return substring;
+
+  // gfx::Range::Intersect() behaves a bit weirdly. If B is an empty range
+  // contained inside a non-empty range A, B intersection A returns
+  // gfx::Range::InvalidRange(), instead of returning B.
+  *actual_range = text_range.Contains(requested_range)
+                      ? requested_range
+                      : text_range.Intersect(requested_range);
+
+  // This is a special case for which the complete string should should be
+  // returned. NSTextView also follows this, though the same is not mentioned in
+  // NSTextInputClient documentation.
+  if (!requested_range.IsValid())
+    *actual_range = text_range;
+
+  client->GetTextFromRange(*actual_range, &substring);
+  return substring;
+}
+
+}
+
 namespace views {
 
+////////////////////////////////////////////////////////////////////////////////
+// TextInputHost, public:
+
 TextInputHost::TextInputHost(BridgedNativeWidgetHostImpl* host_impl)
-    : host_impl_(host_impl) {}
+    : host_impl_(host_impl), mojo_binding_(this) {}
 
 TextInputHost::~TextInputHost() {}
 
+void TextInputHost::BindRequest(
+    views_bridge_mac::mojom::TextInputHostAssociatedRequest request) {
+  mojo_binding_.Bind(std::move(request),
+                     ui::WindowResizeHelperMac::Get()->task_runner());
+}
+
 ui::TextInputClient* TextInputHost::GetTextInputClient() const {
   return text_input_client_;
 }
@@ -58,13 +185,21 @@
   }
 }
 
-void TextInputHost::GetHasInputContext(bool* has_input_context) {
-  *has_input_context = false;
+////////////////////////////////////////////////////////////////////////////////
+// TextInputHost, views_bridge_mac::mojom::TextInputHost:
+
+bool TextInputHost::HasClient(bool* out_has_client) {
+  *out_has_client = text_input_client_ != nullptr;
+  return true;
+}
+
+bool TextInputHost::HasInputContext(bool* out_has_input_context) {
+  *out_has_input_context = false;
 
   // If the textInputClient_ does not exist, return nil since this view does not
   // conform to NSTextInputClient protocol.
   if (!pending_text_input_client_)
-    return;
+    return true;
 
   // If a menu is active, and -[NSView interpretKeyEvents:] asks for the
   // input context, return nil. This ensures the action message is sent to
@@ -72,7 +207,7 @@
   bool has_menu_controller = false;
   host_impl_->GetHasMenuController(&has_menu_controller);
   if (has_menu_controller)
-    return;
+    return true;
 
   // When not in an editable mode, or while entering passwords
   // (http://crbug.com/23219), we don't want to show IME candidate windows.
@@ -81,10 +216,193 @@
   switch (pending_text_input_client_->GetTextInputType()) {
     case ui::TEXT_INPUT_TYPE_NONE:
     case ui::TEXT_INPUT_TYPE_PASSWORD:
-      return;
+      return true;
     default:
-      *has_input_context = true;
+      *out_has_input_context = true;
   }
+  return true;
+}
+
+bool TextInputHost::IsRTL(bool* out_is_rtl) {
+  *out_is_rtl = text_input_client_ && text_input_client_->GetTextDirection() ==
+                                          base::i18n::RIGHT_TO_LEFT;
+  return true;
+}
+
+bool TextInputHost::GetSelectionRange(gfx::Range* out_range) {
+  if (!text_input_client_ ||
+      !text_input_client_->GetEditableSelectionRange(out_range)) {
+    *out_range = gfx::Range::InvalidRange();
+  }
+  return true;
+}
+
+bool TextInputHost::GetSelectionText(bool* out_result,
+                                     base::string16* out_text) {
+  *out_result = false;
+  if (!text_input_client_)
+    return true;
+  gfx::Range selection_range;
+  if (!text_input_client_->GetEditableSelectionRange(&selection_range))
+    return true;
+  base::string16 text;
+  *out_result = text_input_client_->GetTextFromRange(selection_range, &text);
+  return true;
+}
+
+void TextInputHost::InsertText(const base::string16& text, bool as_character) {
+  if (!text_input_client_)
+    return;
+  if (as_character) {
+    // If a single character is inserted by keyDown's call to
+    // interpretKeyEvents: then use InsertChar() to allow editing events to be
+    // merged. We use ui::VKEY_UNKNOWN as the key code since it's not feasible
+    // to determine the correct key code for each unicode character. Also a
+    // correct keycode is not needed in the current context. Send ui::EF_NONE as
+    // the key modifier since |text| already accounts for the pressed key
+    // modifiers.
+    text_input_client_->InsertChar(ui::KeyEvent(
+        text[0], ui::VKEY_UNKNOWN, ui::DomCode::NONE, ui::EF_NONE));
+  } else {
+    text_input_client_->InsertText(text);
+  }
+}
+
+void TextInputHost::DeleteRange(const gfx::Range& range) {
+  if (!text_input_client_)
+    return;
+  text_input_client_->DeleteRange(range);
+}
+
+void TextInputHost::SetCompositionText(const base::string16& text,
+                                       const gfx::Range& selected_range,
+                                       const gfx::Range& replacement_range) {
+  if (!text_input_client_)
+    return;
+
+  text_input_client_->DeleteRange(replacement_range);
+  ui::CompositionText composition;
+  composition.text = text;
+  composition.selection = selected_range;
+
+  // Add an underline with text color and a transparent background to the
+  // composition text. TODO(karandeepb): On Cocoa textfields, the target clause
+  // of the composition has a thick underlines. The composition text also has
+  // discontinous underlines for different clauses. This is also supported in
+  // the Chrome renderer. Add code to extract underlines from |text| once our
+  // render text implementation supports thick underlines and discontinous
+  // underlines for consecutive characters. See http://crbug.com/612675.
+  composition.ime_text_spans.push_back(
+      ui::ImeTextSpan(ui::ImeTextSpan::Type::kComposition, 0, text.length(),
+                      ui::ImeTextSpan::Thickness::kThin, SK_ColorTRANSPARENT));
+  text_input_client_->SetCompositionText(composition);
+}
+
+void TextInputHost::ConfirmCompositionText() {
+  if (!text_input_client_)
+    return;
+  text_input_client_->ConfirmCompositionText();
+}
+
+bool TextInputHost::HasCompositionText(bool* out_has_composition_text) {
+  *out_has_composition_text = false;
+  if (!text_input_client_)
+    return true;
+  *out_has_composition_text = text_input_client_->HasCompositionText();
+  return true;
+  return true;
+}
+
+bool TextInputHost::GetCompositionTextRange(gfx::Range* out_composition_range) {
+  *out_composition_range = gfx::Range::InvalidRange();
+  if (!text_input_client_)
+    return true;
+  if (!text_input_client_->HasCompositionText())
+    return true;
+  text_input_client_->GetCompositionTextRange(out_composition_range);
+  return true;
+}
+
+bool TextInputHost::GetAttributedSubstringForRange(
+    const gfx::Range& requested_range,
+    base::string16* out_text,
+    gfx::Range* out_actual_range) {
+  *out_text = AttributedSubstringForRangeHelper(
+      text_input_client_, requested_range, out_actual_range);
+  return true;
+}
+
+bool TextInputHost::GetFirstRectForRange(const gfx::Range& requested_range,
+                                         gfx::Rect* out_rect,
+                                         gfx::Range* out_actual_range) {
+  *out_rect = GetFirstRectForRangeHelper(text_input_client_, requested_range,
+                                         out_actual_range);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TextInputHost, views_bridge_mac::mojom::TextInputHost synchronous methods:
+
+void TextInputHost::HasClient(HasClientCallback callback) {
+  bool has_client = false;
+  HasClient(&has_client);
+  std::move(callback).Run(has_client);
+}
+
+void TextInputHost::HasInputContext(HasInputContextCallback callback) {
+  bool has_input_context = false;
+  HasClient(&has_input_context);
+  std::move(callback).Run(has_input_context);
+}
+
+void TextInputHost::IsRTL(IsRTLCallback callback) {
+  bool is_rtl = false;
+  IsRTL(&is_rtl);
+  std::move(callback).Run(is_rtl);
+}
+
+void TextInputHost::GetSelectionRange(GetSelectionRangeCallback callback) {
+  gfx::Range range = gfx::Range::InvalidRange();
+  GetSelectionRange(&range);
+  std::move(callback).Run(range);
+}
+
+void TextInputHost::GetSelectionText(GetSelectionTextCallback callback) {
+  bool result = false;
+  base::string16 text;
+  GetSelectionText(&result, &text);
+  std::move(callback).Run(result, text);
+}
+
+void TextInputHost::HasCompositionText(HasCompositionTextCallback callback) {
+  bool has_composition_text = false;
+  IsRTL(&has_composition_text);
+  std::move(callback).Run(has_composition_text);
+}
+
+void TextInputHost::GetCompositionTextRange(
+    GetCompositionTextRangeCallback callback) {
+  gfx::Range range = gfx::Range::InvalidRange();
+  GetCompositionTextRange(&range);
+  std::move(callback).Run(range);
+}
+
+void TextInputHost::GetAttributedSubstringForRange(
+    const gfx::Range& requested_range,
+    GetAttributedSubstringForRangeCallback callback) {
+  base::string16 text;
+  gfx::Range actual_range = gfx::Range::InvalidRange();
+  GetAttributedSubstringForRange(requested_range, &text, &actual_range);
+  std::move(callback).Run(text, actual_range);
+}
+
+void TextInputHost::GetFirstRectForRange(
+    const gfx::Range& requested_range,
+    GetFirstRectForRangeCallback callback) {
+  gfx::Rect rect;
+  gfx::Range actual_range;
+  GetFirstRectForRange(requested_range, &rect, &actual_range);
+  std::move(callback).Run(rect, actual_range);
 }
 
 }  // namespace views
diff --git a/ui/views/selection_controller.cc b/ui/views/selection_controller.cc
index b04a9930..8dac911f 100644
--- a/ui/views/selection_controller.cc
+++ b/ui/views/selection_controller.cc
@@ -39,6 +39,7 @@
     return true;
 
   if (event.IsOnlyLeftMouseButton()) {
+    first_drag_location_ = event.location();
     if (delegate_->SupportsDrag())
       delegate_->SetTextBeingDragged(false);
 
@@ -206,7 +207,10 @@
 
   delegate_->OnBeforePointerAction();
 
-  render_text->MoveCursorToPoint(last_drag_location_, true);
+  // Note that |first_drag_location_| is only used when
+  // RenderText::kDragToEndIfOutsideVerticalBounds, which is platform-specific.
+  render_text->MoveCursorToPoint(last_drag_location_, true,
+                                 first_drag_location_);
 
   if (aggregated_clicks_ == 1) {
     render_text->SelectWord();
diff --git a/ui/views/selection_controller.h b/ui/views/selection_controller.h
index c4382f1b..e8b15cde 100644
--- a/ui/views/selection_controller.h
+++ b/ui/views/selection_controller.h
@@ -85,9 +85,12 @@
   // Returns whether |point| is inside any substring of the text.
   bool IsInsideText(const gfx::Point& point);
 
-  // A timer and point used to modify the selection when dragging.
+  // A timer and point used to modify the selection when dragging. The
+  // |first_drag_location_| field is used to store where the drag-to-select
+  // started.
   base::RepeatingTimer drag_selection_timer_;
   gfx::Point last_drag_location_;
+  gfx::Point first_drag_location_;
 
   // State variables used to track the last click time and location.
   base::TimeTicks last_click_time_;
diff --git a/ui/views/selection_controller_unittest.cc b/ui/views/selection_controller_unittest.cc
index e28d530..626ff04 100644
--- a/ui/views/selection_controller_unittest.cc
+++ b/ui/views/selection_controller_unittest.cc
@@ -101,6 +101,10 @@
     return render_text_->GetSubstringBounds(gfx::Range(index, index + 1))[0];
   }
 
+  gfx::Point TranslatePointX(const gfx::Point& point, int delta) {
+    return point + gfx::Vector2d(delta, 0);
+  }
+
  private:
   void PressMouseButton(const gfx::Point& location, int button, bool focused) {
     DCHECK(!(mouse_flags_ & button));
@@ -185,5 +189,31 @@
   EXPECT_EQ("", GetSelectedText());
 }
 
+// Regression test for https://crbug.com/731252
+// This test validates that drags which are:
+//   a) Above or below the text, and
+//   b) Past one end of the text
+// behave properly with regard to RenderText::kDragToEndIfOutsideVerticalBounds.
+// When that option is true, drags outside the text that are horizontally
+// "towards" the text should select all of it; when that option is false, those
+// drags should have no effect.
+TEST_F(SelectionControllerTest, DragPastEndUsesProperOrigin) {
+  SetText("abc def");
+
+  gfx::Point point = BoundsOfChar(6).top_right() + gfx::Vector2d(100, -10);
+
+  LeftMouseDown(point);
+  EXPECT_EQ("", GetSelectedText());
+
+  DragMouse(TranslatePointX(point, -1));
+  if (gfx::RenderText::kDragToEndIfOutsideVerticalBounds)
+    EXPECT_EQ("abc def", GetSelectedText());
+  else
+    EXPECT_EQ("", GetSelectedText());
+
+  DragMouse(TranslatePointX(point, 1));
+  EXPECT_EQ("", GetSelectedText());
+}
+
 }  // namespace
 }  // namespace views
diff --git a/ui/views_bridge_mac/BUILD.gn b/ui/views_bridge_mac/BUILD.gn
index 67f95555..d4111ab 100644
--- a/ui/views_bridge_mac/BUILD.gn
+++ b/ui/views_bridge_mac/BUILD.gn
@@ -47,6 +47,7 @@
     "mojo/bridge_factory.mojom",
     "mojo/bridged_native_widget.mojom",
     "mojo/bridged_native_widget_host.mojom",
+    "mojo/text_input_host.mojom",
   ]
 
   public_deps = [
@@ -57,5 +58,6 @@
     "//ui/events/mojo:interfaces",
     "//ui/gfx/geometry/mojo",
     "//ui/gfx/mojo",
+    "//ui/gfx/range/mojo",
   ]
 }
diff --git a/ui/views_bridge_mac/bridge_factory_impl.h b/ui/views_bridge_mac/bridge_factory_impl.h
index 061be58..d42be356 100644
--- a/ui/views_bridge_mac/bridge_factory_impl.h
+++ b/ui/views_bridge_mac/bridge_factory_impl.h
@@ -28,7 +28,8 @@
   void CreateBridgedNativeWidget(
       uint64_t bridge_id,
       mojom::BridgedNativeWidgetAssociatedRequest bridge_request,
-      mojom::BridgedNativeWidgetHostAssociatedPtrInfo host) override;
+      mojom::BridgedNativeWidgetHostAssociatedPtrInfo host,
+      mojom::TextInputHostAssociatedPtrInfo text_input_host) override;
 
  private:
   friend class base::NoDestructor<BridgeFactoryImpl>;
diff --git a/ui/views_bridge_mac/bridge_factory_impl.mm b/ui/views_bridge_mac/bridge_factory_impl.mm
index 2d1f20e3..614379e2 100644
--- a/ui/views_bridge_mac/bridge_factory_impl.mm
+++ b/ui/views_bridge_mac/bridge_factory_impl.mm
@@ -21,12 +21,15 @@
 class Bridge : public BridgedNativeWidgetHostHelper {
  public:
   Bridge(uint64_t bridge_id,
+         mojom::BridgedNativeWidgetAssociatedRequest bridge_request,
          mojom::BridgedNativeWidgetHostAssociatedPtrInfo host_ptr,
-         mojom::BridgedNativeWidgetAssociatedRequest bridge_request) {
+         mojom::TextInputHostAssociatedPtrInfo text_input_host_ptr) {
     host_ptr_.Bind(std::move(host_ptr),
                    ui::WindowResizeHelperMac::Get()->task_runner());
+    text_input_host_ptr_.Bind(std::move(text_input_host_ptr),
+                              ui::WindowResizeHelperMac::Get()->task_runner());
     bridge_impl_ = std::make_unique<BridgedNativeWidgetImpl>(
-        bridge_id, host_ptr_.get(), this);
+        bridge_id, host_ptr_.get(), this, text_input_host_ptr_.get());
     bridge_impl_->BindRequest(
         std::move(bridge_request),
         base::BindOnce(&Bridge::OnConnectionError, base::Unretained(this)));
@@ -81,11 +84,10 @@
     // Text input doesn't work across mojo yet.
     return nullptr;
   }
-  void GetHasInputContext(bool* has_input_context) override {
-    *has_input_context = false;
-  }
 
   mojom::BridgedNativeWidgetHostAssociatedPtr host_ptr_;
+  mojom::TextInputHostAssociatedPtr text_input_host_ptr_;
+
   std::unique_ptr<BridgedNativeWidgetImpl> bridge_impl_;
   base::scoped_nsobject<NSAccessibilityRemoteUIElement>
       remote_accessibility_element_;
@@ -113,10 +115,11 @@
 void BridgeFactoryImpl::CreateBridgedNativeWidget(
     uint64_t bridge_id,
     mojom::BridgedNativeWidgetAssociatedRequest bridge_request,
-    mojom::BridgedNativeWidgetHostAssociatedPtrInfo host) {
+    mojom::BridgedNativeWidgetHostAssociatedPtrInfo host,
+    mojom::TextInputHostAssociatedPtrInfo text_input_host) {
   // The resulting object will be destroyed when its message pipe is closed.
-  ignore_result(
-      new Bridge(bridge_id, std::move(host), std::move(bridge_request)));
+  ignore_result(new Bridge(bridge_id, std::move(bridge_request),
+                           std::move(host), std::move(text_input_host)));
 }
 
 BridgeFactoryImpl::BridgeFactoryImpl() : binding_(this) {}
diff --git a/ui/views_bridge_mac/bridged_content_view.mm b/ui/views_bridge_mac/bridged_content_view.mm
index 694fbeb..2f4389d 100644
--- a/ui/views_bridge_mac/bridged_content_view.mm
+++ b/ui/views_bridge_mac/bridged_content_view.mm
@@ -60,11 +60,6 @@
                     NSHeight(content_rect) - point_in_window.y);
 }
 
-// Returns true if |client| has RTL text.
-bool IsTextRTL(const ui::TextInputClient* client) {
-  return client && client->GetTextDirection() == base::i18n::RIGHT_TO_LEFT;
-}
-
 // Returns true if |event| may have triggered dismissal of an IME and would
 // otherwise be ignored by a ui::TextInputClient when inserted.
 bool IsImeTriggerEvent(NSEvent* event) {
@@ -73,117 +68,6 @@
          key == ui::VKEY_ESCAPE;
 }
 
-// Returns the boundary rectangle for composition characters in the
-// |requested_range|. Sets |actual_range| corresponding to the returned
-// rectangle. For cases, where there is no composition text or the
-// |requested_range| lies outside the composition range, a zero width rectangle
-// corresponding to the caret bounds is returned. Logic used is similar to
-// RenderWidgetHostViewMac::GetCachedFirstRectForCharacterRange(...).
-gfx::Rect GetFirstRectForRangeHelper(const ui::TextInputClient* client,
-                                     const gfx::Range& requested_range,
-                                     gfx::Range* actual_range) {
-  // NSRange doesn't support reversed ranges.
-  DCHECK(!requested_range.is_reversed());
-  DCHECK(actual_range);
-
-  // Set up default return values, to be returned in case of unusual cases.
-  gfx::Rect default_rect;
-  *actual_range = gfx::Range::InvalidRange();
-  if (!client)
-    return default_rect;
-
-  default_rect = client->GetCaretBounds();
-  default_rect.set_width(0);
-
-  // If possible, modify actual_range to correspond to caret position.
-  gfx::Range selection_range;
-  if (client->GetEditableSelectionRange(&selection_range)) {
-    // Caret bounds correspond to end index of selection_range.
-    *actual_range = gfx::Range(selection_range.end());
-  }
-
-  gfx::Range composition_range;
-  if (!client->HasCompositionText() ||
-      !client->GetCompositionTextRange(&composition_range) ||
-      !composition_range.Contains(requested_range))
-    return default_rect;
-
-  DCHECK(!composition_range.is_reversed());
-
-  const size_t from = requested_range.start() - composition_range.start();
-  const size_t to = requested_range.end() - composition_range.start();
-
-  // Pick the first character's bounds as the initial rectangle, then grow it to
-  // the full |requested_range| if possible.
-  const bool request_is_composition_end = from == composition_range.length();
-  const size_t first_index = request_is_composition_end ? from - 1 : from;
-  gfx::Rect union_rect;
-  if (!client->GetCompositionCharacterBounds(first_index, &union_rect))
-    return default_rect;
-
-  // If requested_range is empty, return a zero width rectangle corresponding to
-  // it.
-  if (from == to) {
-    if (request_is_composition_end && !IsTextRTL(client)) {
-      // In case of an empty requested range at end of composition, return the
-      // rectangle to the right of the last compositioned character.
-      union_rect.set_origin(union_rect.top_right());
-    }
-    union_rect.set_width(0);
-    *actual_range = requested_range;
-    return union_rect;
-  }
-
-  // Toolkit-views textfields are always single-line, so no need to check for
-  // line breaks.
-  for (size_t i = from + 1; i < to; i++) {
-    gfx::Rect current_rect;
-    if (client->GetCompositionCharacterBounds(i, &current_rect)) {
-      union_rect.Union(current_rect);
-    } else {
-      *actual_range =
-          gfx::Range(requested_range.start(), i + composition_range.start());
-      return union_rect;
-    }
-  }
-  *actual_range = requested_range;
-  return union_rect;
-}
-
-// Returns the string corresponding to |requested_range| for the given |client|.
-// If a gfx::Range::InvalidRange() is passed, the full string stored by |client|
-// is returned. Sets |actual_range| corresponding to the returned string.
-base::string16 AttributedSubstringForRangeHelper(
-    const ui::TextInputClient* client,
-    const gfx::Range& requested_range,
-    gfx::Range* actual_range) {
-  // NSRange doesn't support reversed ranges.
-  DCHECK(!requested_range.is_reversed());
-  DCHECK(actual_range);
-
-  base::string16 substring;
-  gfx::Range text_range;
-  *actual_range = gfx::Range::InvalidRange();
-  if (!client || !client->GetTextRange(&text_range))
-    return substring;
-
-  // gfx::Range::Intersect() behaves a bit weirdly. If B is an empty range
-  // contained inside a non-empty range A, B intersection A returns
-  // gfx::Range::InvalidRange(), instead of returning B.
-  *actual_range = text_range.Contains(requested_range)
-                      ? requested_range
-                      : text_range.Intersect(requested_range);
-
-  // This is a special case for which the complete string should should be
-  // returned. NSTextView also follows this, though the same is not mentioned in
-  // NSTextInputClient documentation.
-  if (!requested_range.IsValid())
-    *actual_range = text_range;
-
-  client->GetTextFromRange(*actual_range, &substring);
-  return substring;
-}
-
 ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) {
   if (action == @selector(undo:))
     return ui::TextEditCommand::UNDO;
@@ -256,6 +140,14 @@
 // Returns the native Widget's drag drop client. Possibly null.
 - (views_bridge_mac::DragDropClient*)dragDropClient NS_RETURNS_INNER_POINTER;
 
+// Returns true if there exists a ui::TextInputClient for the currently focused
+// views::View.
+- (BOOL)hasTextInputClient;
+
+// Returns true if there exists a ui::TextInputClient for the currently focused
+// views::View and that client is right-to-left.
+- (BOOL)isTextRTL;
+
 // Menu action handlers.
 - (void)undo:(id)sender;
 - (void)redo:(id)sender;
@@ -312,6 +204,20 @@
   return bridge_ ? bridge_->host_helper()->GetTextInputClient() : nullptr;
 }
 
+- (BOOL)hasTextInputClient {
+  bool hasTextInputClient = NO;
+  if (bridge_)
+    bridge_->text_input_host()->HasClient(&hasTextInputClient);
+  return hasTextInputClient;
+}
+
+- (BOOL)isTextRTL {
+  bool isRTL = NO;
+  if (bridge_)
+    bridge_->text_input_host()->IsRTL(&isRTL);
+  return isRTL;
+}
+
 - (void)dealloc {
   // By the time |self| is dealloc'd, it should never be in an NSWindow, and it
   // should never be the current input context.
@@ -479,6 +385,7 @@
 
   // If there's an active TextInputClient, schedule the editing command to be
   // performed.
+  // TODO(https://crbug.com/901490): Add mojo support for ui::TextEditCommand.
   if ([self textInputClient] &&
           [self textInputClient] -> IsTextEditCommandEnabled(command)) {
     [self textInputClient] -> SetTextEditCommandForNextKeyEvent(command);
@@ -561,27 +468,14 @@
   }
 
   // Forward the |text| to |textInputClient_| if no menu is active.
-  if ([self textInputClient] && ![self hasActiveMenuController]) {
-    // If a single character is inserted by keyDown's call to
-    // interpretKeyEvents: then use InsertChar() to allow editing events to be
-    // merged. We use ui::VKEY_UNKNOWN as the key code since it's not feasible
-    // to determine the correct key code for each unicode character. Also a
-    // correct keycode is not needed in the current context. Send ui::EF_NONE as
-    // the key modifier since |text| already accounts for the pressed key
-    // modifiers.
-
-    // Also, note we don't check isFinalInsertForKeyEvent, nor use
-    // |keyDownEvent_| to generate the synthetic ui::KeyEvent since:  For
-    //  composed text, [keyDownEvent_ characters] might not be the same as
-    // |text|. This is because |keyDownEvent_| will correspond to the event that
-    // caused the composition text to be confirmed, say, Return key press.
-    if (isCharacterEvent) {
-      [self textInputClient] -> InsertChar(ui::KeyEvent(
-                                 [text characterAtIndex:0], ui::VKEY_UNKNOWN,
-                                 ui::DomCode::NONE, ui::EF_NONE));
-    } else {
-      [self textInputClient] -> InsertText(base::SysNSStringToUTF16(text));
-    }
+  if ([self hasTextInputClient] && ![self hasActiveMenuController]) {
+    // Note we don't check isFinalInsertForKeyEvent, nor use |keyDownEvent_|
+    // to generate the synthetic ui::KeyEvent since:  For composed text,
+    // [keyDownEvent_ characters] might not be the same as |text|. This is
+    // because |keyDownEvent_| will correspond to the event that caused the
+    // composition text to be confirmed, say, Return key press.
+    bridge_->text_input_host()->InsertText(base::SysNSStringToUTF16(text),
+                                           isCharacterEvent);
     // Suppress accelerators that may be bound to this key, since it inserted
     // text instead. But note that IME may follow with -insertNewLine:, which
     // will resurrect the keyEvent for accelerator handling.
@@ -763,9 +657,9 @@
 - (NSTextInputContext*)inputContext {
   if (!bridge_)
     return nil;
-  bool has_text_input_context = false;
-  bridge_->host_helper()->GetHasInputContext(&has_text_input_context);
-  return has_text_input_context ? [super inputContext] : nil;
+  bool hasTextInputContext = false;
+  bridge_->text_input_host()->HasInputContext(&hasTextInputContext);
+  return hasTextInputContext ? [super inputContext] : nil;
 }
 
 // NSResponder implementation.
@@ -1197,25 +1091,23 @@
 }
 
 - (void)moveToLeftEndOfLine:(id)sender {
-  IsTextRTL([self textInputClient]) ? [self moveToEndOfLine:sender]
-                                    : [self moveToBeginningOfLine:sender];
+  [self isTextRTL] ? [self moveToEndOfLine:sender]
+                   : [self moveToBeginningOfLine:sender];
 }
 
 - (void)moveToRightEndOfLine:(id)sender {
-  IsTextRTL([self textInputClient]) ? [self moveToBeginningOfLine:sender]
-                                    : [self moveToEndOfLine:sender];
+  [self isTextRTL] ? [self moveToBeginningOfLine:sender]
+                   : [self moveToEndOfLine:sender];
 }
 
 - (void)moveToLeftEndOfLineAndModifySelection:(id)sender {
-  IsTextRTL([self textInputClient])
-      ? [self moveToEndOfLineAndModifySelection:sender]
-      : [self moveToBeginningOfLineAndModifySelection:sender];
+  [self isTextRTL] ? [self moveToEndOfLineAndModifySelection:sender]
+                   : [self moveToBeginningOfLineAndModifySelection:sender];
 }
 
 - (void)moveToRightEndOfLineAndModifySelection:(id)sender {
-  IsTextRTL([self textInputClient])
-      ? [self moveToBeginningOfLineAndModifySelection:sender]
-      : [self moveToEndOfLineAndModifySelection:sender];
+  [self isTextRTL] ? [self moveToBeginningOfLineAndModifySelection:sender]
+                   : [self moveToEndOfLineAndModifySelection:sender];
 }
 
 // Graphical Element transposition
@@ -1312,8 +1204,8 @@
   // Valid if (sendType, returnType) is either (string, nil), (nil, string),
   // or (string, string).
   BOOL valid =
-      [self textInputClient] && ((canWrite && (canRead || !returnType)) ||
-                                 (canRead && (canWrite || !sendType)));
+      [self hasTextInputClient] && ((canWrite && (canRead || !returnType)) ||
+                                    (canRead && (canWrite || !sendType)));
   return valid
              ? self
              : [super validRequestorForSendType:sendType returnType:returnType];
@@ -1327,15 +1219,13 @@
   // either for when it is upgraded.
   DCHECK([types containsObject:NSStringPboardType] ||
          [types containsObject:base::mac::CFToNSCast(kUTTypeUTF8PlainText)]);
-  if (![self textInputClient])
-    return NO;
 
-  gfx::Range selectionRange;
-  if (![self textInputClient] -> GetEditableSelectionRange(&selectionRange))
-    return NO;
-
+  bool result = NO;
   base::string16 text;
-  [self textInputClient] -> GetTextFromRange(selectionRange, &text);
+  if (bridge_)
+    bridge_->text_input_host()->GetSelectionText(&result, &text);
+  if (!result)
+    return NO;
   return [pboard writeObjects:@[ base::SysUTF16ToNSString(text) ]];
 }
 
@@ -1365,10 +1255,12 @@
   // See https://crbug.com/888782.
   if (range.location == NSNotFound)
     range.length = 0;
-
-  gfx::Range actual_range;
-  base::string16 substring = AttributedSubstringForRangeHelper(
-      [self textInputClient], gfx::Range(range), &actual_range);
+  base::string16 substring;
+  gfx::Range actual_range = gfx::Range::InvalidRange();
+  if (bridge_) {
+    bridge_->text_input_host()->GetAttributedSubstringForRange(
+        gfx::Range(range), &substring, &actual_range);
+  }
   if (actualRange) {
     // To maintain consistency with NSTextView, return range {0,0} for an out of
     // bounds requested range.
@@ -1433,78 +1325,64 @@
 
 - (NSRect)firstRectForCharacterRange:(NSRange)range
                          actualRange:(NSRangePointer)actualNSRange {
-  gfx::Range actualRange;
-  gfx::Rect rect = GetFirstRectForRangeHelper([self textInputClient],
-                                              gfx::Range(range), &actualRange);
+  gfx::Rect rect;
+  gfx::Range actualRange = gfx::Range::InvalidRange();
+  if (bridge_) {
+    bridge_->text_input_host()->GetFirstRectForRange(gfx::Range(range), &rect,
+                                                     &actualRange);
+  }
   if (actualNSRange)
     *actualNSRange = actualRange.ToNSRange();
   return gfx::ScreenRectToNSRect(rect);
 }
 
 - (BOOL)hasMarkedText {
-  return
-      [self textInputClient] && [self textInputClient] -> HasCompositionText();
+  bool hasCompositionText = NO;
+  if (bridge_)
+    bridge_->text_input_host()->HasCompositionText(&hasCompositionText);
+  return hasCompositionText;
 }
 
 - (void)insertText:(id)text replacementRange:(NSRange)replacementRange {
-  if (!bridge_ || ![self textInputClient])
+  if (!bridge_)
     return;
-
-  [self textInputClient] -> DeleteRange(gfx::Range(replacementRange));
+  bridge_->text_input_host()->DeleteRange(gfx::Range(replacementRange));
   [self insertTextInternal:text];
 }
 
 - (NSRange)markedRange {
-  if (![self textInputClient])
-    return NSMakeRange(NSNotFound, 0);
-
-  gfx::Range range;
-  [self textInputClient] -> GetCompositionTextRange(&range);
+  gfx::Range range = gfx::Range::InvalidRange();
+  if (bridge_)
+    bridge_->text_input_host()->GetCompositionTextRange(&range);
   return range.ToNSRange();
 }
 
 - (NSRange)selectedRange {
-  if (![self textInputClient])
-    return NSMakeRange(NSNotFound, 0);
-
-  gfx::Range range;
-  [self textInputClient] -> GetEditableSelectionRange(&range);
+  gfx::Range range = gfx::Range::InvalidRange();
+  if (bridge_)
+    bridge_->text_input_host()->GetSelectionRange(&range);
   return range.ToNSRange();
 }
 
 - (void)setMarkedText:(id)text
         selectedRange:(NSRange)selectedRange
      replacementRange:(NSRange)replacementRange {
-  if (![self textInputClient])
+  if (![self hasTextInputClient])
     return;
 
   if ([text isKindOfClass:[NSAttributedString class]])
     text = [text string];
-
-  [self textInputClient] -> DeleteRange(gfx::Range(replacementRange));
-  ui::CompositionText composition;
-  composition.text = base::SysNSStringToUTF16(text);
-  composition.selection = gfx::Range(selectedRange);
-
-  // Add an underline with text color and a transparent background to the
-  // composition text. TODO(karandeepb): On Cocoa textfields, the target clause
-  // of the composition has a thick underlines. The composition text also has
-  // discontinous underlines for different clauses. This is also supported in
-  // the Chrome renderer. Add code to extract underlines from |text| once our
-  // render text implementation supports thick underlines and discontinous
-  // underlines for consecutive characters. See http://crbug.com/612675.
-  composition.ime_text_spans.push_back(
-      ui::ImeTextSpan(ui::ImeTextSpan::Type::kComposition, 0, [text length],
-                      ui::ImeTextSpan::Thickness::kThin, SK_ColorTRANSPARENT));
-  [self textInputClient] -> SetCompositionText(composition);
+  bridge_->text_input_host()->SetCompositionText(base::SysNSStringToUTF16(text),
+                                                 gfx::Range(selectedRange),
+                                                 gfx::Range(replacementRange));
   hasUnhandledKeyDownEvent_ = NO;
 }
 
 - (void)unmarkText {
-  if (![self textInputClient])
+  if (![self hasTextInputClient])
     return;
 
-  [self textInputClient] -> ConfirmCompositionText();
+  bridge_->text_input_host()->ConfirmCompositionText();
   hasUnhandledKeyDownEvent_ = NO;
 }
 
@@ -1520,6 +1398,7 @@
   if (command == ui::TextEditCommand::INVALID_COMMAND)
     return NO;
 
+  // TODO(https://crbug.com/901490): Add mojo support for ui::TextEditCommand.
   if ([self textInputClient])
     return [self textInputClient] -> IsTextEditCommandEnabled(command);
 
diff --git a/ui/views_bridge_mac/bridged_native_widget_host_helper.h b/ui/views_bridge_mac/bridged_native_widget_host_helper.h
index 2d8d15d..cfb144c 100644
--- a/ui/views_bridge_mac/bridged_native_widget_host_helper.h
+++ b/ui/views_bridge_mac/bridged_native_widget_host_helper.h
@@ -59,10 +59,6 @@
   // Return a pointer to the host's ui::TextInputClient.
   // TODO(ccameron): Remove the needs for this call.
   virtual ui::TextInputClient* GetTextInputClient() = 0;
-
-  // Return true if -[NSView inputContext] should return a non-nil value.
-  // TODO(ccameron): Move this function to the mojo interface.
-  virtual void GetHasInputContext(bool* has_input_context) = 0;
 };
 
 }  // namespace views_bridge_mac
diff --git a/ui/views_bridge_mac/bridged_native_widget_impl.h b/ui/views_bridge_mac/bridged_native_widget_impl.h
index d4b0f4fd..68afafaa 100644
--- a/ui/views_bridge_mac/bridged_native_widget_impl.h
+++ b/ui/views_bridge_mac/bridged_native_widget_impl.h
@@ -23,6 +23,7 @@
 #include "ui/views/views_export.h"
 #import "ui/views_bridge_mac/cocoa_mouse_capture_delegate.h"
 #include "ui/views_bridge_mac/mojo/bridged_native_widget.mojom.h"
+#include "ui/views_bridge_mac/mojo/text_input_host.mojom.h"
 
 @class BridgedContentView;
 @class ModalShowAnimationWithLayer;
@@ -33,6 +34,7 @@
 
 namespace mojom {
 class BridgedNativeWidgetHost;
+class TextInputHost;
 }  // namespace mojom
 
 class BridgedNativeWidgetHostHelper;
@@ -76,9 +78,11 @@
       const views_bridge_mac::mojom::CreateWindowParams* params);
 
   // Creates one side of the bridge. |host| and |parent| must not be NULL.
-  BridgedNativeWidgetImpl(uint64_t bridged_native_widget_id,
-                          BridgedNativeWidgetHost* host,
-                          BridgedNativeWidgetHostHelper* host_helper);
+  BridgedNativeWidgetImpl(
+      uint64_t bridged_native_widget_id,
+      BridgedNativeWidgetHost* host,
+      BridgedNativeWidgetHostHelper* host_helper,
+      views_bridge_mac::mojom::TextInputHost* text_input_host);
   ~BridgedNativeWidgetImpl() override;
 
   // Bind |bridge_mojo_binding_| to |request|, and set the connection error
@@ -155,6 +159,9 @@
   BridgedContentView* ns_view() { return bridged_view_; }
   BridgedNativeWidgetHost* host() { return host_; }
   BridgedNativeWidgetHostHelper* host_helper() { return host_helper_; }
+  views_bridge_mac::mojom::TextInputHost* text_input_host() const {
+    return text_input_host_;
+  }
   NSWindow* ns_window();
 
   views_bridge_mac::DragDropClient* drag_drop_client();
@@ -280,6 +287,9 @@
   const uint64_t id_;
   BridgedNativeWidgetHost* const host_;               // Weak. Owns this.
   BridgedNativeWidgetHostHelper* const host_helper_;  // Weak, owned by |host_|.
+  views_bridge_mac::mojom::TextInputHost* const
+      text_input_host_;  // Weak, owned by |host_|.
+
   base::scoped_nsobject<NativeWidgetMacNSWindow> window_;
   base::scoped_nsobject<ViewsNSWindowDelegate> window_delegate_;
   base::scoped_nsobject<NSObject<CommandDispatcherDelegate>>
diff --git a/ui/views_bridge_mac/bridged_native_widget_impl.mm b/ui/views_bridge_mac/bridged_native_widget_impl.mm
index fd5540a..83a49da0 100644
--- a/ui/views_bridge_mac/bridged_native_widget_impl.mm
+++ b/ui/views_bridge_mac/bridged_native_widget_impl.mm
@@ -299,10 +299,12 @@
 BridgedNativeWidgetImpl::BridgedNativeWidgetImpl(
     uint64_t bridged_native_widget_id,
     BridgedNativeWidgetHost* host,
-    BridgedNativeWidgetHostHelper* host_helper)
+    BridgedNativeWidgetHostHelper* host_helper,
+    views_bridge_mac::mojom::TextInputHost* text_input_host)
     : id_(bridged_native_widget_id),
       host_(host),
       host_helper_(host_helper),
+      text_input_host_(text_input_host),
       bridge_mojo_binding_(this) {
   DCHECK(GetIdToWidgetImplMap().find(id_) == GetIdToWidgetImplMap().end());
   GetIdToWidgetImplMap().insert(std::make_pair(id_, this));
diff --git a/ui/views_bridge_mac/mojo/bridge_factory.mojom b/ui/views_bridge_mac/mojo/bridge_factory.mojom
index d6a88f8..bbb596f9c6 100644
--- a/ui/views_bridge_mac/mojo/bridge_factory.mojom
+++ b/ui/views_bridge_mac/mojo/bridge_factory.mojom
@@ -7,6 +7,7 @@
 import "ui/views_bridge_mac/mojo/alert.mojom";
 import "ui/views_bridge_mac/mojo/bridged_native_widget.mojom";
 import "ui/views_bridge_mac/mojo/bridged_native_widget_host.mojom";
+import "ui/views_bridge_mac/mojo/text_input_host.mojom";
 
 // The interface through which a bridge is created and connected to its host.
 interface BridgeFactory {
@@ -19,6 +20,7 @@
   CreateBridgedNativeWidget(
       uint64 bridge_id,
       associated BridgedNativeWidget& bridge_request,
-      associated BridgedNativeWidgetHost host);
+      associated BridgedNativeWidgetHost host,
+      associated TextInputHost text_input_host);
 };
 
diff --git a/ui/views_bridge_mac/mojo/text_input_host.mojom b/ui/views_bridge_mac/mojo/text_input_host.mojom
new file mode 100644
index 0000000..b721d6e
--- /dev/null
+++ b/ui/views_bridge_mac/mojo/text_input_host.mojom
@@ -0,0 +1,81 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module views_bridge_mac.mojom;
+
+import "mojo/public/mojom/base/string16.mojom";
+import "ui/gfx/geometry/mojo/geometry.mojom";
+import "ui/gfx/range/mojo/range.mojom";
+
+// Interface exposing a views::Widget's currently-focused views::View's
+// ui::TextInputClient to the NSView corresponding to that views::Widget.
+interface TextInputHost {
+  // Returns true in |has_client| if there exists an active ui::TextInputClient
+  // for the focused views::View.
+  [Sync]
+  HasClient() => (bool has_client);
+
+  // Returns true in |has_input_context|| if -[NSView inputContext] should
+  // return a non-nil value.
+  [Sync]
+  HasInputContext() => (bool has_input_context);
+
+  // Returns true in |is_rtl| if there exists an active ui::TextInputClient and
+  // that ui::TextInputClient is RTL (right-to-left).
+  [Sync]
+  IsRTL() => (bool is_rtl);
+
+  // Retrieves the character range of current selection in the current text
+  // input. Returns an invalid range if the information cannot be retrieved
+  // right now or if the selected text is not in the focused views::View.
+  [Sync]
+  GetSelectionRange() => (gfx.mojom.Range range);
+
+  // Retrieves the currently selected text, and stores it in |text|. Returns
+  // false in |result| if there was no selection.
+  [Sync]
+  GetSelectionText() => (bool result, mojo_base.mojom.String16 text);
+
+  // Inserts a given text at the insertion point.
+  InsertText(mojo_base.mojom.String16 text, bool as_character);
+
+  // Deletes contents in the given character range.
+  DeleteRange(gfx.mojom.Range range);
+
+  // Sets composition text in the current ui::TextInputClient to |text| with
+  // selection range |selected_range|, and delete the content of
+  // |replacement_range|.
+  SetCompositionText(mojo_base.mojom.String16 text,
+                     gfx.mojom.Range selected_range,
+                     gfx.mojom.Range replacement_range);
+
+  // Converts current composition text into final content.
+  ConfirmCompositionText();
+
+  // Returns true in |has_composition_text| if there is composition text.
+  [Sync]
+  HasCompositionText() => (bool has_composition_text);
+
+  // Returns in |composition_range| the character range of current composition
+  // text.
+  [Sync]
+  GetCompositionTextRange() => (gfx.mojom.Range composition_range);
+
+  // Returns in |text| the string corresponding to |requested_range| for current
+  // ui::TextInputClient. If a gfx::Range::InvalidRange() is passed, the full
+  // string stored by for the current ui::TextInputClient is returned. Sets
+  // |actual_range| corresponding to the returned string.
+  [Sync]
+  GetAttributedSubstringForRange(gfx.mojom.Range requested_range) =>
+      (mojo_base.mojom.String16 text, gfx.mojom.Range actual_range);
+
+  // Returns in |rect| the boundary rectangle for composition characters in the
+  // |requested_range|. Sets |actual_range| corresponding to the returned
+  // rectangle. For cases where there is no composition text or the
+  // |requested_range| lies outside the composition range, a zero width
+  // rectangle corresponding to the caret bounds is returned.
+  [Sync]
+  GetFirstRectForRange(gfx.mojom.Range requested_range) =>
+      (gfx.mojom.Rect rect, gfx.mojom.Range actual_range);
+};
diff --git a/webrunner/net_http/url_loader_impl.cc b/webrunner/net_http/url_loader_impl.cc
index 646f71b..44200de 100644
--- a/webrunner/net_http/url_loader_impl.cc
+++ b/webrunner/net_http/url_loader_impl.cc
@@ -194,7 +194,8 @@
   }
 
   done_callback_ = std::move(callback);
-  net_request_->FollowDeferredRedirect(base::nullopt);
+  net_request_->FollowDeferredRedirect(base::nullopt /* removed_headers */,
+                                       base::nullopt /* modified_headers */);
 }
 
 void URLLoaderImpl::QueryStatus(QueryStatusCallback callback) {